From fb39e7c92e9160495c1d7a8be3c19db70bbe23c1 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 2 Jun 2023 09:18:36 +0800 Subject: [PATCH 001/231] refactor: change client manager to generic class --- .../src/Abstraction/BaseClass/ClientBase.cs | 9 +-- .../BaseClass/ClientManagerBase.cs | 20 +++--- .../src/Abstraction/BaseClass/ServerBase.cs | 6 +- .../Chat/src/Aggregate/Redis/RedisChannel.cs | 4 +- .../Chat/src/Application/ClientManager.cs | 72 ++----------------- .../Handler/CmdHandler/General/WhoHandler.cs | 5 ++ src/Servers/Chat/test/RedisChatChannelTest.cs | 4 +- .../src/Application/ClientManager.cs | 5 +- .../src/V2/Aggregate/Redis/NatNegChannel.cs | 3 +- .../src/V2/Application/ClientManager.cs | 3 +- 10 files changed, 41 insertions(+), 90 deletions(-) diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs index 80ca10bfe..e322b4836 100644 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs +++ b/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs @@ -4,6 +4,7 @@ using UniSpy.Server.Core.Logging; using UniSpy.Server.Core.Extension; using System.Threading.Tasks; +using System.Net; namespace UniSpy.Server.Core.Abstraction.BaseClass { @@ -26,7 +27,7 @@ public ClientBase(IConnection connection, IServer server) Connection = connection; Server = server; EventBinding(); - ClientManagerBase.AddClient(this); + ClientManagerBase.AddClient(this); } protected virtual void EventBinding() { @@ -56,7 +57,7 @@ protected virtual void EventBinding() /// /// Only work for tcp /// - protected virtual void OnConnected() => ClientManagerBase.AddClient(this); + protected virtual void OnConnected() => ClientManagerBase.AddClient(this); /// /// Only work for tcp @@ -130,12 +131,12 @@ public void Dispose() ((ITcpConnection)Connection).OnReceive -= OnReceived; ((ITcpConnection)Connection).OnConnect -= OnConnected; ((ITcpConnection)Connection).OnDisconnect -= OnDisconnected; - ClientManagerBase.RemoveClient(this); + ClientManagerBase.RemoveClient(this); break; case NetworkConnectionType.Udp: ((IUdpConnection)Connection).OnReceive -= OnReceived; _timer.Dispose(); - ClientManagerBase.RemoveClient(this); + ClientManagerBase.RemoveClient(this); break; case NetworkConnectionType.Http: ((IHttpConnection)Connection).OnReceive -= OnReceived; diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ClientManagerBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ClientManagerBase.cs index bf21e097d..8bb2c8e6d 100644 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ClientManagerBase.cs +++ b/src/Libraries/Core/src/Abstraction/BaseClass/ClientManagerBase.cs @@ -9,23 +9,25 @@ namespace UniSpy.Server.Core.Abstraction.BaseClass /// if you need search client in specific server, /// please inherit this class and create static method. /// - public abstract class ClientManagerBase + public abstract class ClientManagerBase + where TKey : IPEndPoint + where TValue : IClient { - public static readonly ConcurrentDictionary ClientPool = new ConcurrentDictionary(); - public static void AddClient(IClient client) + public static readonly ConcurrentDictionary ClientPool = new ConcurrentDictionary(); + public static void AddClient(TValue client) { - ClientPool.TryAdd(client.Connection.RemoteIPEndPoint, client); + ClientPool.TryAdd((TKey)client.Connection.RemoteIPEndPoint, client); } - public static IClient RemoveClient(IClient client) + public static TValue RemoveClient(TValue client) { - return RemoveClient(client.Connection.RemoteIPEndPoint); + return RemoveClient((TKey)client.Connection.RemoteIPEndPoint); } - public static IClient RemoveClient(IPEndPoint endPoint) + public static TValue RemoveClient(TKey endPoint) { ClientPool.TryRemove(endPoint, out var client); return client; } - public static IClient GetClient(IPEndPoint endPoint) + public static TValue GetClient(TKey endPoint) { if (ClientPool.TryGetValue(endPoint, out var client)) { @@ -33,7 +35,7 @@ public static IClient GetClient(IPEndPoint endPoint) } else { - return null; + return default; } } } diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ServerBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ServerBase.cs index 961f51dfb..75453bec5 100644 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ServerBase.cs +++ b/src/Libraries/Core/src/Abstraction/BaseClass/ServerBase.cs @@ -46,13 +46,13 @@ public void SetServerInfo() PublicIPEndPoint = _cfg.PublicIPEndPoint; } protected abstract IConnectionManager CreateConnectionManager(IPEndPoint endPoint); - private IClient HandleConnectionInitialization(IConnection connection) + protected virtual IClient HandleConnectionInitialization(IConnection connection) { - var client = ClientManagerBase.GetClient(connection.RemoteIPEndPoint); + var client = ClientManagerBase.GetClient(connection.RemoteIPEndPoint); if (client is null) { client = CreateClient(connection); - ClientManagerBase.AddClient(client); + ClientManagerBase.AddClient(client); } return client; } diff --git a/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs b/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs index 948822e49..b40963f3d 100644 --- a/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs +++ b/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs @@ -28,7 +28,7 @@ public override void ReceivedMessage(RemoteMessage message) ClientManager.RemoveClient(message.Client); return; } - IChatClient client = (IChatClient)ClientManager.GetClient(message.Client); + IChatClient client = (IChatClient)ClientManager.GetClient(message.Client.Connection.RemoteIPEndPoint); if (client is null) { ClientManager.AddClient(message.Client); @@ -67,7 +67,7 @@ public override void ReceivedMessage(RemoteMessage message) { return; } - IChatClient client = (IChatClient)ClientManager.GetClient(message.Client); + IChatClient client = (IChatClient)ClientManager.GetClient(message.Client.Connection.RemoteIPEndPoint); if (client is null) { throw new Chat.Exception($"There are no remote client found in RemoteClients pool, the client must be login on the remote server."); diff --git a/src/Servers/Chat/src/Application/ClientManager.cs b/src/Servers/Chat/src/Application/ClientManager.cs index 6411b9442..f5e23b304 100644 --- a/src/Servers/Chat/src/Application/ClientManager.cs +++ b/src/Servers/Chat/src/Application/ClientManager.cs @@ -1,88 +1,26 @@ -using System.Collections.Concurrent; +using System.Net; using System.Collections.Generic; using System.Linq; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate; using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; +using UniSpy.Server.Chat.Abstraction.Interface; namespace UniSpy.Server.Chat.Application { - public sealed class ClientManager : ClientManagerBase + public sealed class ClientManager : ClientManagerBase { - public static readonly ConcurrentDictionary RemoteClientPool = new ConcurrentDictionary(); - private static string RemoteClientDictionaryKey(RemoteClient client) - { - return $"{client.Server.Id} {client.Connection.RemoteIPEndPoint}"; - } - public static new void AddClient(IClient client) - { - if (((IChatClient)client).Info.IsRemoteClient) - { - var key = RemoteClientDictionaryKey((RemoteClient)client); - RemoteClientPool.TryAdd(key, (RemoteClient)client); - } - else - { - ClientManagerBase.AddClient(client); - } - } - public static new IClient RemoveClient(IClient client) - { - if (((IChatClient)client).Info.IsRemoteClient) - { - var key = RemoteClientDictionaryKey((RemoteClient)client); - RemoteClientPool.TryRemove(key, out _); - return client; - } - else - { - return ClientManagerBase.RemoveClient(client); - } - } - public static IClient GetClient(IClient client) - { - if (((IChatClient)client).Info.IsRemoteClient) - { - var key = RemoteClientDictionaryKey((RemoteClient)client); - if (RemoteClientPool.TryGetValue(key, out var c)) - { - return c; - - } - else - { - return null; - } - } - else - { - return ClientManagerBase.GetClient(client.Connection.RemoteIPEndPoint); - } - } /// /// We need to make sure client is get by nickname, otherwise we throw exception /// /// - /// public static IChatClient GetClientByNickName(string nickName) { IChatClient client; - client = (IChatClient)ClientPool.Values.Where(c => ((ClientInfo)(c.Info)).NickName == nickName).FirstOrDefault(); - if (client is null) - { - client = (IChatClient)RemoteClientPool.Values.Where(c => ((ClientInfo)(c.Info)).NickName == nickName).FirstOrDefault(); - } - if (client is null) - { - throw new Chat.Exception($"No client named {nickName} found."); - } + client = ClientPool.Values.Where(c => c.Info.NickName == nickName).FirstOrDefault(); return client; } public static List GetAllClientInfo() { - var infos = ClientPool.Values.Select(c => ((ClientInfo)(c.Info))).ToList(); - infos.AddRange(RemoteClientPool.Values.Select(c => ((ClientInfo)(c.Info))).ToList()); + var infos = ClientPool.Values.Select(c => (c.Info)).ToList(); return infos; } } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs index 01eaccc77..4288ff860 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs @@ -61,6 +61,11 @@ private void GetChannelUsersInfo() private void GetUserInfo() { var client = ClientManager.GetClientByNickName(_request.NickName); + + if (client is null) + { + throw new Chat.Exception($"Client not exist with nickname {_request.NickName}"); + } foreach (var channel in client.Info.JoinedChannels.Values) { diff --git a/src/Servers/Chat/test/RedisChatChannelTest.cs b/src/Servers/Chat/test/RedisChatChannelTest.cs index a34b0daac..7634a76ab 100644 --- a/src/Servers/Chat/test/RedisChatChannelTest.cs +++ b/src/Servers/Chat/test/RedisChatChannelTest.cs @@ -14,6 +14,7 @@ using System.Collections.Generic; using System.Net; using Moq; +using UniSpy.Server.Chat.Abstraction.Interface; namespace UniSpy.Server.Chat.Test { @@ -113,7 +114,7 @@ public void GeneralTest() // Client.ClientPool.Remove(client1.Connection.RemoteIPEndPoint, out _); ClientManager.RemoveClient(client1); var remoteClient = client1.GetRemoteClient() as ITestClient; - ClientManager.AddClient((IClient)remoteClient); + ClientManager.AddClient((IChatClient)remoteClient); foreach (var r in request1) { var count = ClientManager.ClientPool.Count; @@ -143,6 +144,7 @@ public void GeneralTest() // "PART #GSP!worms3!MJ0NJ4c3aM :Left Game\r\n" }; var client2 = MockObject.CreateClient(port: 1235) as ITestClient; + ClientManager.AddClient((IChatClient)client2); foreach (var r in request2) { var count = ClientManager.ClientPool.Count; diff --git a/src/Servers/PresenceConnectionManager/src/Application/ClientManager.cs b/src/Servers/PresenceConnectionManager/src/Application/ClientManager.cs index e6b49d016..71891dde3 100644 --- a/src/Servers/PresenceConnectionManager/src/Application/ClientManager.cs +++ b/src/Servers/PresenceConnectionManager/src/Application/ClientManager.cs @@ -1,13 +1,14 @@ +using System.Net; using System.Linq; using UniSpy.Server.Core.Abstraction.BaseClass; namespace UniSpy.Server.PresenceConnectionManager.Application { - public class ClientManager : ClientManagerBase + public class ClientManager : ClientManagerBase { public static Client GetClient(int profileid, int? productid = null, int? namespaceId = null) { - return (Client)ClientPool.Values.FirstOrDefault( + return ClientPool.Values.FirstOrDefault( c => ((ClientInfo)c.Info).SubProfileInfo.ProductId == productid && ((ClientInfo)c.Info).SubProfileInfo.ProfileId == profileid && ((ClientInfo)c.Info).SubProfileInfo.NamespaceId == namespaceId); diff --git a/src/Servers/QueryReport/src/V2/Aggregate/Redis/NatNegChannel.cs b/src/Servers/QueryReport/src/V2/Aggregate/Redis/NatNegChannel.cs index a079a10ef..095d82b9f 100644 --- a/src/Servers/QueryReport/src/V2/Aggregate/Redis/NatNegChannel.cs +++ b/src/Servers/QueryReport/src/V2/Aggregate/Redis/NatNegChannel.cs @@ -5,6 +5,7 @@ using UniSpy.Server.Core.Logging; using UniSpy.Server.Core.Abstraction.BaseClass; using UniSpy.Server.QueryReport.Application; +using System.Net; namespace UniSpy.Server.QueryReport.V2.Aggregate.Redis { @@ -15,7 +16,7 @@ public NatNegChannel() : base(RedisChannelName.NatNegCookieChannel) } public override void ReceivedMessage(ClientMessageRequest message) { - var client = (Client)ClientManagerBase.GetClient(message.TargetIPEndPoint); + var client = (Client)ClientManagerBase.GetClient(message.TargetIPEndPoint); if (client is null) { LogWriter.LogWarn($"Client:{message.TargetIPEndPoint} not found, we ignore natneg message from SB: {message.ServerBrowserSenderId}"); diff --git a/src/Servers/ServerBrowser/src/V2/Application/ClientManager.cs b/src/Servers/ServerBrowser/src/V2/Application/ClientManager.cs index 3ea99c7ca..630b013d5 100644 --- a/src/Servers/ServerBrowser/src/V2/Application/ClientManager.cs +++ b/src/Servers/ServerBrowser/src/V2/Application/ClientManager.cs @@ -1,10 +1,11 @@ +using System.Net; using UniSpy.Server.Core.Abstraction.BaseClass; using System.Linq; using System.Collections.Generic; namespace UniSpy.Server.ServerBrowser.V2.Application { - public class ClientManager : ClientManagerBase + public class ClientManager : ClientManagerBase { public static List GetClient(string gameName) { From e448b2314712e9eecfd221ad98820ec5cfaac7b4 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 3 Jun 2023 15:57:31 +0800 Subject: [PATCH 002/231] refactor(chat): chatcrypt serilizable --- .../Chat/src/Aggregate/Misc/BufferCache.cs | 3 +++ .../Chat/src/Aggregate/Misc/ChatCrypt.cs | 7 ++++++ .../Chat/src/Aggregate/RemoteObjects.cs | 22 +++++++++++-------- src/Servers/Chat/src/Application/Client.cs | 7 ++---- src/Servers/Chat/test/RedisChatChannelTest.cs | 2 ++ 5 files changed, 27 insertions(+), 14 deletions(-) diff --git a/src/Servers/Chat/src/Aggregate/Misc/BufferCache.cs b/src/Servers/Chat/src/Aggregate/Misc/BufferCache.cs index 90a82509b..dcebaa187 100644 --- a/src/Servers/Chat/src/Aggregate/Misc/BufferCache.cs +++ b/src/Servers/Chat/src/Aggregate/Misc/BufferCache.cs @@ -2,6 +2,9 @@ namespace UniSpy.Server.Chat.Aggregate.Misc { + /// + /// Tcp data will be received uncomplete, we need to combine it according to gamespy protocol + /// public class BufferCache : Core.Misc.BufferCacheBase { public BufferCache() diff --git a/src/Servers/Chat/src/Aggregate/Misc/ChatCrypt.cs b/src/Servers/Chat/src/Aggregate/Misc/ChatCrypt.cs index e2b5a258b..5c2581291 100755 --- a/src/Servers/Chat/src/Aggregate/Misc/ChatCrypt.cs +++ b/src/Servers/Chat/src/Aggregate/Misc/ChatCrypt.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Newtonsoft.Json; using UniSpy.Server.Core.Abstraction.Interface; using UniSpy.Server.Core.Encryption; @@ -96,8 +97,14 @@ public static bool EncodeIP() { return false; } + [JsonProperty] public PeerChatCTX ClientCtx { get; private set; } + [JsonProperty] public PeerChatCTX ServerCtx { get; private set; } + /// + /// using for json deserialization + /// + public ChatCrypt() { } public ChatCrypt(string gameSecretKey) { ClientCtx = new PeerChatCTX(); diff --git a/src/Servers/Chat/src/Aggregate/RemoteObjects.cs b/src/Servers/Chat/src/Aggregate/RemoteObjects.cs index 10793c713..a6a22b072 100644 --- a/src/Servers/Chat/src/Aggregate/RemoteObjects.cs +++ b/src/Servers/Chat/src/Aggregate/RemoteObjects.cs @@ -2,6 +2,7 @@ using System.Net; using Newtonsoft.Json; using UniSpy.Server.Chat.Abstraction.Interface; +using UniSpy.Server.Chat.Aggregate.Misc; using UniSpy.Server.Chat.Application; using UniSpy.Server.Chat.Handler; using UniSpy.Server.Core.Abstraction.BaseClass; @@ -58,21 +59,24 @@ public class RemoteClient : IChatClient, Core.Abstraction.Interface.ITestClient public bool IsLogRaw { get; set; } [JsonConverter(typeof(ConcreteTypeConverter))] public IConnection Connection { get; set; } - public ICryptography Crypto => null; + [JsonConverter(typeof(ConcreteTypeConverter))] + public ICryptography Crypto { get; set; } [JsonConverter(typeof(ConcreteTypeConverter))] public ClientInfoBase Info { get; set; } ClientInfo IChatClient.Info => (ClientInfo)Info; [JsonConverter(typeof(ConcreteTypeConverter))] public IServer Server { get; set; } - - // public RemoteClient() { } - public RemoteClient(RemoteTcpConnection conn, ClientInfo info, IServer server) + /// + /// using for json deserialization + /// + public RemoteClient() { } + public RemoteClient(Client client) { - Connection = conn; - // we need to copy the client info because it is a reference type - Info = info.DeepCopy(); - Server = server; - (Info as ClientInfo).IsRemoteClient = true; + Connection = new RemoteTcpConnection(client.Connection, new RemoteTcpConnectionManager()); + Server = new RemoteServer(client.Server); + Info = client.Info.DeepCopy(); + ((ClientInfo)Info).IsRemoteClient = true; + Crypto = client.Crypto; } public void Send(IResponse response) => this.LogDebug("Ignore remote client Send() operation"); public RemoteClient GetRemoteClient() => this; diff --git a/src/Servers/Chat/src/Application/Client.cs b/src/Servers/Chat/src/Application/Client.cs index c08ed18a8..4c103bd4b 100644 --- a/src/Servers/Chat/src/Application/Client.cs +++ b/src/Servers/Chat/src/Application/Client.cs @@ -62,11 +62,8 @@ private void PublishDisconnectMessage() protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, UniSpyEncoding.GetString((byte[])buffer)); public RemoteClient GetRemoteClient() { - var manager = new RemoteTcpConnectionManager(); - var conn = new RemoteTcpConnection(Connection, manager); - var server = new RemoteServer(Server); - var client = new RemoteClient(conn, Info, server); - return client; + var remoteClient = new RemoteClient(this); + return remoteClient; } } } \ No newline at end of file diff --git a/src/Servers/Chat/test/RedisChatChannelTest.cs b/src/Servers/Chat/test/RedisChatChannelTest.cs index 7634a76ab..6419e10ce 100644 --- a/src/Servers/Chat/test/RedisChatChannelTest.cs +++ b/src/Servers/Chat/test/RedisChatChannelTest.cs @@ -15,6 +15,7 @@ using System.Net; using Moq; using UniSpy.Server.Chat.Abstraction.Interface; +using UniSpy.Server.Chat.Aggregate.Misc; namespace UniSpy.Server.Chat.Test { @@ -58,6 +59,7 @@ public void RedisChannel() var remoteClient = client.GetRemoteClient(); var request = new JoinRequest(ChannelRequests.Join); request.Parse(); + remoteClient.Crypto = new ChatCrypt("hello"); var message = new RemoteMessage(request, remoteClient); var msgStr = JsonConvert.SerializeObject(message); var msgObj = JsonConvert.DeserializeObject(msgStr); From ab2898878311a5a376831abb14d50b2648122bc7 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 8 Jun 2023 08:09:49 +0800 Subject: [PATCH 003/231] refactor: added comments --- common/UniSpyServerConfig.json | 18 +-------- .../src/Abstraction/BaseClass/RedisClient.cs | 1 - .../BaseClass/ServerLauncherBase.cs | 16 ++++---- src/Libraries/Core/src/Config/UniSpyConfig.cs | 34 +---------------- .../src/Entity/RelayServerInfo.cs | 1 - .../src/Aggregate/Redis/NatAddressInfo.cs | 37 +++---------------- .../src/Aggregate/Redis/NatFailInfo.cs | 1 - .../src/Aggregate/Misc/LoginChallengeProof.cs | 9 +++-- .../CmdHandler/General/LoginHandler.cs | 2 +- .../src/V2/Aggregate/Redis/ChannelInfo.cs | 1 - .../src/V2/Aggregate/Redis/GameServerInfo.cs | 1 - 11 files changed, 25 insertions(+), 96 deletions(-) diff --git a/common/UniSpyServerConfig.json b/common/UniSpyServerConfig.json index 75e04d2fc..78926cc43 100644 --- a/common/UniSpyServerConfig.json +++ b/common/UniSpyServerConfig.json @@ -1,23 +1,9 @@ { "Database": { - "Server": "127.0.0.1", - "Port": "5432", - "Database": "unispy", - "Username": "root", - "Password": "0000", - "SSLMode": "Prefer", - "TrustServerCert": "false", - "SSLKey": "", - "SSLPassword": "", - "RootCert": "" + "ConnectionString": "reference to https://www.connectionstrings.com/postgresql/ example: User ID=root;Password=myPassword;Host=localhost;Port=5432;Database=myDataBase;Pooling=true;Min Pool Size=0;Max Pool Size=100;Connection Lifetime=0;" }, "Redis": { - "Server": "127.0.0.1", - "Port": "6379", - "User": "", - "Password": "", - "SSL": "false", - "SSLHost": "" + "ConnectionString": "reference to https://stackexchange.github.io/StackExchange.Redis/Configuration.html example: localhost:6379,user=,password=,ssl=,sslHost=" }, "Servers": [ { diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/RedisClient.cs b/src/Libraries/Core/src/Abstraction/BaseClass/RedisClient.cs index 46dc8caff..16ad67ac5 100644 --- a/src/Libraries/Core/src/Abstraction/BaseClass/RedisClient.cs +++ b/src/Libraries/Core/src/Abstraction/BaseClass/RedisClient.cs @@ -1,4 +1,3 @@ -using StackExchange.Redis; using UniSpy.Server.Core.Config; namespace UniSpy.Server.Core.Abstraction.BaseClass diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ServerLauncherBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ServerLauncherBase.cs index a2fad800c..db535dde8 100755 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ServerLauncherBase.cs +++ b/src/Libraries/Core/src/Abstraction/BaseClass/ServerLauncherBase.cs @@ -17,7 +17,9 @@ public abstract class ServerLauncherBase /// UniSpy server version /// public static readonly string Version = "0.8.1"; - + /// + /// The server instances which contains the server objects + /// public static List ServerInstances { get; protected set; } = new List(); public ServerLauncherBase() { @@ -51,28 +53,26 @@ protected virtual void LaunchServer() protected void ConnectRedis() { - var redisConfig = ConfigManager.Config.Redis; try { - var r = StackExchange.Redis.ConnectionMultiplexer.Connect(redisConfig.ConnectionString); - r.Dispose(); + // auto dispose + using (StackExchange.Redis.ConnectionMultiplexer.Connect(ConfigManager.Config.Redis.ConnectionString)) { } } catch (System.Exception e) { throw new Exception("Can not connect to Redis", e); } - Console.WriteLine($"Successfully connected to Redis at {redisConfig.Server}:{redisConfig.Port}"); + Console.WriteLine($"Successfully connected to Redis"); } protected void ConnectMySql() { //Determine which database is used and establish the database connection. - var dbConfig = ConfigManager.Config.Database; if (!new UniSpyContext().Database.CanConnect()) { - throw new Exception($"Can not connect to {dbConfig.Type}!"); + throw new Exception($"Can not connect to Postgresql."); } - Console.WriteLine($"Successfully connected to {dbConfig.Type} at {dbConfig.Server}:{dbConfig.Port}"); + Console.WriteLine($"Successfully connected to Postgresql."); } protected static void ShowUniSpyLogo() { diff --git a/src/Libraries/Core/src/Config/UniSpyConfig.cs b/src/Libraries/Core/src/Config/UniSpyConfig.cs index 6b384b643..30d376c8f 100755 --- a/src/Libraries/Core/src/Config/UniSpyConfig.cs +++ b/src/Libraries/Core/src/Config/UniSpyConfig.cs @@ -4,7 +4,6 @@ using Newtonsoft.Json; using Serilog.Events; using StackExchange.Redis; -using UniSpy.Server.Core.Database; namespace UniSpy.Server.Core.Config { @@ -17,40 +16,11 @@ public class UniSpyConfig } public class UniSpyDatabaseConfig { - public string ConnectionString => - $"Server={Server};" - + $"Port={Port};" - + $"Database={Database};" - + $"Username={Username};" - + $"Password={Password};" - + $"SSL Mode={SSLMode};" - + $"Trust Server Certificate={TrustServerCert};" - + $"SSL Certificate={SSLCert};" - + $"SSL Key={SSLKey};" - + $"SSL Password={SSLPassword};" - + $"Root Certificate={RootCert};"; - public DatabaseType Type; - public string Server; - public int Port; - public string Database; - public string Username; - public string Password; - public string SSLMode; - public bool TrustServerCert; - public string SSLCert; - public string SSLKey; - public string SSLPassword; - public string RootCert; + public string ConnectionString; } public class UniSpyRedisConfig { - public string ConnectionString => $"{Server}:{Port},user={User},password={Password},ssl={SSL},sslHost={SSLHost}"; - public string Server; - public int Port; - public string User; - public string Password; - public bool SSL; - public string SSLHost; + public string ConnectionString; [JsonIgnore] public IConnectionMultiplexer RedisConnection => ConnectionMultiplexer.Connect(ConnectionString); } diff --git a/src/Servers/GameTrafficRelay/src/Entity/RelayServerInfo.cs b/src/Servers/GameTrafficRelay/src/Entity/RelayServerInfo.cs index 74b82c0b6..1c03dffc9 100644 --- a/src/Servers/GameTrafficRelay/src/Entity/RelayServerInfo.cs +++ b/src/Servers/GameTrafficRelay/src/Entity/RelayServerInfo.cs @@ -3,7 +3,6 @@ using Newtonsoft.Json; using StackExchange.Redis; using UniSpy.LinqToRedis; -using UniSpy.Server.Core.Config; using UniSpy.Server.Core.Extension.Redis; using UniSpy.Server.Core.Misc; diff --git a/src/Servers/NatNegotiation/src/Aggregate/Redis/NatAddressInfo.cs b/src/Servers/NatNegotiation/src/Aggregate/Redis/NatAddressInfo.cs index a3ed2046d..fa1c02112 100644 --- a/src/Servers/NatNegotiation/src/Aggregate/Redis/NatAddressInfo.cs +++ b/src/Servers/NatNegotiation/src/Aggregate/Redis/NatAddressInfo.cs @@ -4,7 +4,6 @@ using Newtonsoft.Json; using UniSpy.LinqToRedis; using UniSpy.Server.NatNegotiation.Enumerate; -using UniSpy.Server.Core.Config; using UniSpy.Server.Core.Misc; using UniSpy.Server.NatNegotiation.Handler.CmdHandler; using System.Linq; @@ -42,6 +41,7 @@ public NatAddressInfo() : base(RedisDbNumber.NatAddressInfo, TimeSpan.FromMinute } public record NatInitInfo { + public Dictionary AddressInfos { get; private set; } public uint Cookie => (uint)AddressInfos.Values.Last().Cookie; public byte Version => (byte)AddressInfos.Values.Last().Version; public NatClientIndex ClientIndex => (NatClientIndex)AddressInfos.Values.Last().ClientIndex; @@ -54,37 +54,13 @@ public record NatInitInfo /// /// Some game will not send GP init packet, we use NN3 as default. /// - public IPEndPoint PublicIPEndPoint - { - get - { - if (Version == 2) - { - return AddressInfos[NatPortType.NN2].PublicIPEndPoint; - } - else - { - return AddressInfos[NatPortType.NN3].PublicIPEndPoint; - } - } - } + public IPEndPoint PublicIPEndPoint => AddressInfos.Values.Last().PublicIPEndPoint; + /// /// The private address will show in NN2 and NN3 packets, in here we use private ip in NN3 as default /// - public IPEndPoint PrivateIPEndPoint - { - get - { - if (Version == 2) - { - return AddressInfos[NatPortType.NN2].PrivateIPEndPoint; - } - else - { - return AddressInfos[NatPortType.NN3].PrivateIPEndPoint; - } - } - } + public IPEndPoint PrivateIPEndPoint => AddressInfos.Values.Last().PrivateIPEndPoint; + public NatType NatType { get @@ -100,12 +76,11 @@ public NatType NatType } } - public Dictionary AddressInfos { get; private set; } public NatInitInfo(List infos) { AddressInfos = infos.Select((i) => new { i }).ToDictionary(a => ((NatPortType)a.i.PortType), a => a.i); - if (infos.First().Version == 2) + if (Version == 2) { if (!(AddressInfos.ContainsKey(NatPortType.NN1) && AddressInfos.ContainsKey(NatPortType.NN2))) diff --git a/src/Servers/NatNegotiation/src/Aggregate/Redis/NatFailInfo.cs b/src/Servers/NatNegotiation/src/Aggregate/Redis/NatFailInfo.cs index 072ce030a..ac35a9074 100644 --- a/src/Servers/NatNegotiation/src/Aggregate/Redis/NatFailInfo.cs +++ b/src/Servers/NatNegotiation/src/Aggregate/Redis/NatFailInfo.cs @@ -2,7 +2,6 @@ using System.Net; using Newtonsoft.Json; using UniSpy.LinqToRedis; -using UniSpy.Server.Core.Config; using UniSpy.Server.Core.Extension.Redis; using UniSpy.Server.Core.Misc; using UniSpy.Server.NatNegotiation.Enumerate; diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/Misc/LoginChallengeProof.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/Misc/LoginChallengeProof.cs index d0bf1776d..fb1e3398c 100755 --- a/src/Servers/PresenceConnectionManager/src/Aggregate/Misc/LoginChallengeProof.cs +++ b/src/Servers/PresenceConnectionManager/src/Aggregate/Misc/LoginChallengeProof.cs @@ -13,7 +13,9 @@ public sealed class LoginChallengeProof public string Challenge1 { get; private set; } public string Challenge2 { get; private set; } public string PasswordHash { get; private set; } - + /// + /// The context using to build the challenge and response, the challenge1 can be server challenge or client challenge as same as challenge2. + /// public LoginChallengeProof(string userData, LoginType loginType, int? partnerID, string challenge1, string challenge2, string passwordHash) { UserData = userData; @@ -39,14 +41,15 @@ public static string GenerateProof(LoginChallengeProof data) if (data.PartnerID is not null) { if (data?.PartnerID != (int)GPPartnerID.Gamespy - && data?.LoginType != LoginType.AuthToken) + && data?.LoginType != LoginType.AuthToken) { tempUserData = $@"{data.PartnerID}@{data.UserData}"; } } // Generate our response string - StringBuilder responseString = new StringBuilder(data.PasswordHash); + var responseString = new StringBuilder(); + responseString.Append(data.PasswordHash); responseString.Append(' ', 48); // 48 spaces responseString.Append(tempUserData); responseString.Append(data.Challenge1); diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LoginHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LoginHandler.cs index 4bde6cb68..b38b0ff99 100755 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LoginHandler.cs +++ b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LoginHandler.cs @@ -76,7 +76,7 @@ protected override void DataOperation() LoginChallengeProof proofData = new LoginChallengeProof( _request.UserData, (LoginType)_request.Type, - (int?)_request.PartnerID, + _request.PartnerID, LoginChallengeProof.ServerChallenge, _request.UserChallenge, _result.DatabaseResults.PasswordHash); diff --git a/src/Servers/QueryReport/src/V2/Aggregate/Redis/ChannelInfo.cs b/src/Servers/QueryReport/src/V2/Aggregate/Redis/ChannelInfo.cs index 6cb6613d4..216775df4 100644 --- a/src/Servers/QueryReport/src/V2/Aggregate/Redis/ChannelInfo.cs +++ b/src/Servers/QueryReport/src/V2/Aggregate/Redis/ChannelInfo.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using UniSpy.LinqToRedis; -using UniSpy.Server.Core.Config; using UniSpy.Server.Core.Extension.Redis; namespace UniSpy.Server.QueryReport.Aggregate.Redis.Channel diff --git a/src/Servers/QueryReport/src/V2/Aggregate/Redis/GameServerInfo.cs b/src/Servers/QueryReport/src/V2/Aggregate/Redis/GameServerInfo.cs index 883f7553b..51dda7397 100644 --- a/src/Servers/QueryReport/src/V2/Aggregate/Redis/GameServerInfo.cs +++ b/src/Servers/QueryReport/src/V2/Aggregate/Redis/GameServerInfo.cs @@ -4,7 +4,6 @@ using Newtonsoft.Json; using UniSpy.LinqToRedis; using UniSpy.Server.QueryReport.V2.Enumerate; -using UniSpy.Server.Core.Config; using UniSpy.Server.Core.Misc; using UniSpy.Server.Core.Extension.Redis; using System.Linq; From 44ca2f2964803be3064ab5be0cc3ca7f9dab59f5 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 9 Jun 2023 18:37:23 +0800 Subject: [PATCH 004/231] refactor(sb): converted v1 encryption to c# code --- .../V1/Abstraction/BaseClass/EnctypeBase.cs | 180 +++++++++++++----- .../src/V1/Aggregate/Enctype2.cs | 46 +++-- 2 files changed, 164 insertions(+), 62 deletions(-) diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs index 6b22bb7e0..65dbced11 100644 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs +++ b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs @@ -1,3 +1,6 @@ +using System.Net.NetworkInformation; +using System; +using System.Linq; using UniSpy.Server.Core.Abstraction.Interface; namespace UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass @@ -6,11 +9,98 @@ public abstract class EnctypeBase : ICryptography { public byte[] Decrypt(byte[] data) => throw new UniSpy.Exception("Enctype only encrypt message on server side."); public abstract byte[] Encrypt(byte[] data); - protected void EncShare3(byte[] data, byte n1 = 0, byte n2 = 0) + protected void Encshare1(uint[] tbuff, byte[] datap, int len) { - byte t1, t2, t3, t4; + int pIndex, sIndex; + // convert uint array to byte array + var tbuffBytes = Array.ConvertAll(tbuff, Convert.ToByte); + var datapIndex = 0; + pIndex = sIndex = 309 * 4; + Encshare2(tbuff, tbuff.Skip(pIndex / 4).ToArray(), 16); + while (len-- > 0) + { + if ((pIndex - sIndex) == 63) + { + pIndex = sIndex; + //convert int array to byte array + var newtbuffBytes = Array.ConvertAll(tbuff, Convert.ToUInt32); + Encshare2(tbuff, newtbuffBytes, 16); + } + datap[datapIndex] ^= tbuffBytes[pIndex]; + datapIndex++; + pIndex++; + } + } + protected void Encshare2(uint[] tbuff, uint[] tbuffp, int len) + { + + uint t1, t2, t3, t4, t5; + uint limit; + uint p; + + t2 = tbuff[304]; + t1 = tbuff[305]; + t3 = tbuff[306]; + t5 = tbuff[307]; + limit = (uint)len; + // Array.Copy(tbuffp, limit, len); + // for (int i = 0; i < len; i++) + // { + + // } + uint tbuffpIndex = 0; + while (tbuffpIndex < limit) + { + p = t2 + 272; + while (t5 < (ushort.MaxValue + 1)) + { + t1 += t5; + p++; + t3 += t1; + t1 += t3; + tbuff[p - 17] = t1; + tbuff[p - 1] = t1; + t4 = (t3 << 24) | (t3 >> 8); + tbuff[p + 15] = t1; + t5 <<= 1; + t2++; + t1 ^= tbuff[t1 & 0xff]; + t4 ^= tbuff[t4 & 0xff]; + t3 = (t4 << 24) | (t4 >> 8); + t4 = (t1 >> 24) | (t1 << 8); + t4 ^= tbuff[t4 & 0xff]; + t3 ^= tbuff[t3 & 0xff]; + t1 = (t4 >> 24) | (t4 << 8); + } + t3 ^= t1; + tbuff[tbuffpIndex] = t3; + tbuffpIndex++; + t2--; + t1 = tbuff[t2 + 256]; + t5 = tbuff[t2 + 272]; + t1 = ~t1; + t3 = (t1 << 24) | (t1 >> 8); + t3 ^= tbuff[t3 & 0xff]; + t5 ^= tbuff[t5 & 0xff]; + t1 = (t3 << 24) | (t3 >> 8); + t4 = (t5 >> 24) | (t5 << 8); + t1 ^= tbuff[t1 & 0xff]; + t4 ^= tbuff[t4 & 0xff]; + t3 = (t4 >> 24) | (t4 << 8); + t5 = (tbuff[t2 + 288] << 1) + 1; + } + + tbuff[304] = t2; + tbuff[305] = t1; + tbuff[306] = t3; + tbuff[307] = t5; + } + protected void EncShare3(uint[] data, int n1 = 0, int n2 = 0) + { + uint t1, t2, t3, t4; int i; - t2 = n1; + + t2 = (uint)n1; t1 = 0; t4 = 1; data[304] = 0; @@ -23,16 +113,16 @@ protected void EncShare3(byte[] data, byte n1 = 0, byte n2 = 0) if ((n2 & i) != 0) { - t2 = (byte)~t2; - t4 = (byte)((t4 << 1) + 1); - t3 = (byte)((t2 << 24) | (t2 >> 8)); - t3 ^= data[t3 & 0xff]; - t1 ^= data[t1 & 0xff]; - t2 = (byte)((t3 << 24) | (t3 >> 8)); - t3 = (byte)((t1 >> 24) | (t1 << 8)); - t2 ^= data[t2 & 0xff]; - t3 ^= data[t3 & 0xff]; - t1 = (byte)((t3 >> 24) | (t3 << 8)); + t2 = ~t2; + t4 = (t4 << 1) + 1; + t3 = (t2 << 24) | (t2 >> 8); + t3 ^= data[t3 & 0xFF]; + t1 ^= data[t1 & 0xFF]; + t2 = (t3 << 24) | (t3 >> 8); + t3 = (t1 >> 24) | (t1 << 8); + t2 ^= data[t2 & 0xFF]; + t3 ^= data[t3 & 0xFF]; + t1 = (t3 >> 24) | (t3 << 8); } else { @@ -40,63 +130,55 @@ protected void EncShare3(byte[] data, byte n1 = 0, byte n2 = 0) data[data[304] + 272] = t1; data[data[304] + 288] = t4; data[304]++; - t3 = (byte)((t1 << 24) | (t1 >> 8)); - t2 ^= data[t2 & 0xff]; - t3 ^= data[t3 & 0xff]; - t1 = (byte)((t3 << 24) | (t3 >> 8)); - t3 = (byte)((t2 >> 24) | (t2 << 8)); - t3 ^= data[t3 & 0xff]; - t1 ^= data[t1 & 0xff]; - t2 = (byte)((t3 >> 24) | (t3 << 8)); + t3 = (t1 << 24) | (t1 >> 8); + t2 ^= data[t2 & 0xFF]; + t3 ^= data[t3 & 0xFF]; + t1 = (t3 << 24) | (t3 >> 8); + t3 = (t2 >> 24) | (t2 << 8); + t3 ^= data[t3 & 0xFF]; + t1 ^= data[t1 & 0xFF]; + t2 = (t3 >> 24) | (t3 << 8); t4 <<= 1; } } + data[305] = t2; data[306] = t1; data[307] = t4; - data[308] = n1; + data[308] = (uint)n1; } - protected byte[] EncShare4(byte[] data) + protected void EncShare4(byte[] src, int size, uint[] dest) { - byte[] encodeData = new byte[326]; - byte tmp; - byte i; + uint tmp; + int i; byte pos, x, y; - // encrypted data size - int size = data[0]; - for (y = 0; y < 4; y++) + for (i = 0; i < 256; i++) // Initialize all elements in dest array to 0 + dest[i] = 0; + + for (y = 0; y < 4; y++) // Loop 4 times, process one byte at a time { - for (i = 0; i <= 255; i++) + for (i = 0; i < 256; i++) // Perform operation on each element in dest array { - encodeData[i] = (byte)((encodeData[i] << 8) + i); - // fill 256 bytes of encodeData with 0,1,2,3...255 - // Array.Copy(Enumerable.Range(0,255).ToArray(),encodeData,256); + dest[i] = (dest[i] << 8) + (uint)i; // Left shift dest[i] by 8 bits and add i in the low 8 bits } - for (pos = y, x = 0; x < 2; x++) + for (pos = y, x = 0; x < 2; x++) // Loop 2 times, process 128 bytes each time { - for (i = 0; i <= 255; i++) + for (i = 0; i < 256; i++) // Perform operation on each element in dest array { - tmp = encodeData[i]; - pos += (byte)(tmp + data[i % size]); - encodeData[i] = encodeData[pos]; - encodeData[pos] = tmp; + tmp = dest[i]; // Save the value of dest[i] in tmp + pos += (byte)(tmp + src[i % size]); // Calculate the value of pos + dest[i] = dest[pos]; // Assign the value of dest[pos] to dest[i] + dest[pos] = tmp; // Assign the value of tmp to dest[pos] } } } - for (i = 0; i <= 255; i++) - { - encodeData[i] ^= i; - } - - EncShare3(encodeData); - return encodeData; - } - protected void EncShare2(byte[] buffer, byte[] bufferp) - { + for (i = 0; i < 256; i++) // Perform operation on each element in dest array + dest[i] ^= (uint)i; // Perform XOR between dest[i] and i to obtain the final key + EncShare3(dest); // Call the encshare3 function to further encrypt the key } } } \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs index 1680dc5c5..f59daff21 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System; using UniSpy.Server.Core.Encryption; using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; @@ -16,19 +16,39 @@ public Enctype2(string gameSecretKey) public override byte[] Encrypt(byte[] data) { - var encData = new byte[326]; - var buffer = new List(); - // add 8 bytes header - buffer.AddRange(new byte[8] { 0, 0, 0, 0, 0, 0, 0, 0 }); - // add 1 byte empty data - buffer.Add(0); - // add data after - buffer.AddRange(data); - - KeyXor(encData); - // at last - data[0] ^= 0xec; throw new System.NotImplementedException(); + //convert the byte array to unit array and parse into following functions + // Encoder(GameSecreteKey,data,?); + } + + public int Encoder(byte[] key, byte[] data, int size) + { + uint[] dest = new uint[326]; + int i; + int headerSize = 8; + + for (i = size - 1; i >= 0; i--) + { + data[1 + headerSize + i] = data[i]; + } + data[0] = (byte)headerSize; + + byte[] datap = new byte[data.Length - 1]; + Array.Copy(data, 1, datap, 0, datap.Length); + Array.Clear(datap, 0, data[0]); + + for (i = 256; i < 326; i++) + dest[i] = 0; + EncShare4(datap, data[0], dest); + + Array.Clear(data, 1 + data[0] + size, 6); + Encshare1(dest, datap, size + 6); + + for (i = 0; i < key.Length; i++) + datap[i] ^= key[i]; + size += 1 + data[0] + 6; + data[0] ^= 0xec; + return size; } private void KeyXor(byte[] data) From 5e7dddf61e736acaabe522f92a829d1661bdd96c Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 9 Jun 2023 19:16:53 +0800 Subject: [PATCH 005/231] update(sb): enctype1 c code convert --- .../V1/Abstraction/BaseClass/EnctypeBase.cs | 4 + .../src/V1/Aggregate/Enctype1.cs | 182 +++++++++++++++++- 2 files changed, 183 insertions(+), 3 deletions(-) diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs index 65dbced11..18e541a45 100644 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs +++ b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs @@ -5,6 +5,10 @@ namespace UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass { + /// + /// The encryption type 1 and 2 base class which contains the functions using in enctype1 and enctype2. + /// What confuses me the most is why gamespy doesn't use standard encryption schemes like DES, AES etc. + /// public abstract class EnctypeBase : ICryptography { public byte[] Decrypt(byte[] data) => throw new UniSpy.Exception("Enctype only encrypt message on server side."); diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs index 503bc919a..7303065cb 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs @@ -9,13 +9,14 @@ public class Enctype1 : EnctypeBase /// The server key /// 256 bytes /// - public static readonly byte[] ServerKey = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + public static readonly byte[] Enctype1Table = new byte[] { 0x01, 0xba, 0xfa, 0xb2, 0x51, 0x00, 0x54, 0x80, 0x75, 0x16, 0x8e, 0x8e, 0x02, 0x08, 0x36, 0xa5, 0x2d, 0x05, 0x0d, 0x16, 0x52, 0x07, 0xb4, 0x22, 0x8c, 0xe9, 0x09, 0xd6, 0xb9, 0x26, 0x00, 0x04, 0x06, 0x05, 0x00, 0x13, 0x18, 0xc4, 0x1e, 0x5b, 0x1d, 0x76, 0x74, 0xfc, 0x50, 0x51, 0x06, 0x16, 0x00, 0x51, 0x28, 0x00, 0x04, 0x0a, 0x29, 0x78, 0x51, 0x00, 0x01, 0x11, 0x52, 0x16, 0x06, 0x4a, 0x20, 0x84, 0x01, 0xa2, 0x1e, 0x16, 0x47, 0x16, 0x32, 0x51, 0x9a, 0xc4, 0x03, 0x2a, 0x73, 0xe1, 0x2d, 0x4f, 0x18, 0x4b, 0x93, 0x4c, 0x0f, 0x39, 0x0a, 0x00, 0x04, 0xc0, 0x12, 0x0c, 0x9a, 0x5e, 0x02, 0xb3, 0x18, 0xb8, 0x07, 0x0c, 0xcd, 0x21, 0x05, 0xc0, 0xa9, 0x41, 0x43, 0x04, 0x3c, 0x52, 0x75, 0xec, 0x98, 0x80, 0x1d, 0x08, 0x02, 0x1d, 0x58, 0x84, 0x01, 0x4e, 0x3b, 0x6a, 0x53, 0x7a, 0x55, 0x56, 0x57, 0x1e, 0x7f, 0xec, 0xb8, 0xad, 0x00, 0x70, 0x1f, 0x82, 0xd8, 0xfc, 0x97, 0x8b, 0xf0, 0x83, 0xfe, 0x0e, 0x76, 0x03, 0xbe, 0x39, 0x29, 0x77, 0x30, 0xe0, 0x2b, 0xff, 0xb7, 0x9e, 0x01, 0x04, 0xf8, 0x01, 0x0e, 0xe8, 0x53, 0xff, 0x94, 0x0c, 0xb2, 0x45, 0x9e, 0x0a, 0xc7, 0x06, 0x18, 0x01, 0x64, 0xb0, 0x03, 0x98, 0x01, 0xeb, 0x02, 0xb0, 0x01, 0xb4, 0x12, 0x49, 0x07, 0x1f, 0x5f, 0x5e, 0x5d, 0xa0, 0x4f, 0x5b, 0xa0, 0x5a, 0x59, 0x58, 0xcf, 0x52, 0x54, 0xd0, 0xb8, 0x34, 0x02, 0xfc, 0x0e, 0x42, 0x29, 0xb8, 0xda, 0x00, 0xba, 0xb1, 0xf0, 0x12, 0xfd, 0x23, 0xae, 0xb6, 0x45, 0xa9, 0xbb, 0x06, 0xb8, 0x88, 0x14, 0x24, 0xa9, 0x00, 0x14, 0xcb, 0x24, 0x12, 0xae, 0xcc, 0x57, 0x56, 0xee, 0xfd, 0x08, 0x30, 0xd9, 0xfd, 0x8b, 0x3e, 0x0a, 0x84, 0x46, 0xfa, 0x77, 0xb8 }; /// /// Client key /// 16 bytes /// public static readonly byte[] ClientKey = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; public byte[] Key { get; private set; } + private byte[] _enc1key = new byte[261]; public Enctype1(byte[] key) { Key = key; @@ -24,9 +25,184 @@ public Enctype1(byte[] key) public override byte[] Encrypt(byte[] data) { - byte[] lengthBytes = BitConverter.GetBytes(data.Length); - throw new System.NotImplementedException(); } + + private void Func1(byte[] id) + { + if (id is not null) + { + if (id.Length > 0) + { + Func4(id); + } + } + } + + void Func4(byte[] id) + { + // Declare variables i, n1, n2, t1, t2 + int i, + n1 = 0, + n2 = 0; + byte t1, + t2; + + // If idlen is less than 1, return directly + if (id.Length < 1) + return; + + // Initialize encryption array _enc1key + byte[] _enc1key = new byte[256]; + for (i = 0; i < 256; i++) + _enc1key[i] = (byte)i; + + // Shuffle the encryption array _enc1key + for (i = 255; i >= 0; i--) + { + t1 = (byte)Func5(i, id, ref n1, ref n2); + t2 = _enc1key[i]; + _enc1key[i] = _enc1key[t1]; + _enc1key[t1] = t2; + } + + // Set specific values to some elements of the encryption array _enc1key + _enc1key[256] = _enc1key[1]; + _enc1key[257] = _enc1key[3]; + _enc1key[258] = _enc1key[5]; + _enc1key[259] = _enc1key[7]; + _enc1key[260] = _enc1key[n1 & 0xff]; + } + int Func5(int cnt, byte[] id, ref int n1, ref int n2) + { + // Declare variables i, tmp, mask, and initialize mask as 1 + int i, + tmp, + mask = 1; + + // If cnt is 0, return 0 + if (cnt == 0) + return 0; + + // If cnt is greater than 1, increase the value of mask until it's greater than or equal to cnt + if (cnt > 1) + { + do + { + mask = (mask << 1) + 1; + } while (mask < cnt); + } + + // Initialize i as 0 + i = 0; + + // Iterate until finding a tmp value less than or equal to cnt + do + { + // Update the values of n1 and n2, and calculate the tmp value + n1 = _enc1key[n1 & 0xff] + id[n2]; + n2++; + if (n2 >= id.Length) + { + n2 = 0; + n1 += id.Length; + } + tmp = n1 & mask; + + // If the iteration exceeds 11 times, take tmp modulo cnt + if (++i > 11) + tmp %= cnt; + } while (tmp > cnt); + + // Return the tmp value + return tmp; + } + + private void Func6(byte[] data) + { + var len = data.Length; + int i = 0; + var dataInts = Array.ConvertAll(data, Convert.ToInt32); + while (len-- > 0) + { + dataInts[i] = Func7(dataInts[i]); + i++; + } + } + + int Func7(int len) + { + // Declare variables a, b, c as unsigned char + byte a, b, c; + + // Get certain elements from the encryption array _enc1key and calculate new values based on them + a = _enc1key[256]; + b = _enc1key[257]; + c = _enc1key[a]; + _enc1key[256] = (byte)(a + 1); + _enc1key[257] = (byte)(b + c); + + a = _enc1key[260]; + b = _enc1key[257]; + b = _enc1key[b]; + c = _enc1key[a]; + _enc1key[a] = b; + + a = _enc1key[259]; + b = _enc1key[257]; + a = _enc1key[a]; + _enc1key[b] = a; + + a = _enc1key[256]; + b = _enc1key[259]; + a = _enc1key[a]; + _enc1key[b] = a; + + a = _enc1key[256]; + _enc1key[a] = c; + b = _enc1key[258]; + a = _enc1key[c]; + c = _enc1key[259]; + b = (byte)(b + a); + _enc1key[258] = b; + + a = b; + c = _enc1key[c]; + b = _enc1key[257]; + b = _enc1key[b]; + a = _enc1key[a]; + c += b; + b = _enc1key[260]; + b = _enc1key[b]; + c += b; + b = _enc1key[c]; + c = _enc1key[256]; + c = _enc1key[c]; + a += c; + c = _enc1key[b]; + b = _enc1key[a]; + + // Store the len value in variable a and perform XOR on c and a + a = (byte)len; + c ^= b; + _enc1key[260] = a; + c ^= a; + _enc1key[259] = c; + + // Return c value + return c; + } + void Func8(byte[] data, int len, byte[] enctype1_data) + { + // Iterate over each byte in data + var dataIndex = 0; + while (len-- > 0) + { + // Encrypt the byte in data using the enctype1_data array + data[dataIndex] = enctype1_data[dataIndex]; + dataIndex++; + } + } + } } \ No newline at end of file From f21e3514ef71aa7999789a5c2b12785302c209e4 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 12 Jun 2023 16:22:04 +0800 Subject: [PATCH 006/231] refactor(sb): fix encshare func bugs, create encshare test --- .../V1/Abstraction/BaseClass/EnctypeBase.cs | 108 ++++++++++++------ .../src/V1/Aggregate/Enctype1.cs | 108 ++++++++++++++++-- .../src/V1/Aggregate/Enctype2.cs | 2 +- .../ServerBrowser/test/V1/EnctypeTest.cs | 55 +++++++++ 4 files changed, 229 insertions(+), 44 deletions(-) create mode 100644 src/Servers/ServerBrowser/test/V1/EnctypeTest.cs diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs index 18e541a45..1f8e07660 100644 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs +++ b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.Net.NetworkInformation; using System; using System.Linq; @@ -13,31 +14,45 @@ public abstract class EnctypeBase : ICryptography { public byte[] Decrypt(byte[] data) => throw new UniSpy.Exception("Enctype only encrypt message on server side."); public abstract byte[] Encrypt(byte[] data); - protected void Encshare1(uint[] tbuff, byte[] datap, int len) + /// + /// The encryption function 2 + /// + /// the input tbuff + /// tbuff start index + /// datap is a subset of tbuff + /// datap start index + /// unknown + protected void Encshare1(uint[] tbuff, int tbuffIndex, byte[] datap, int datapIndex, int len) { int pIndex, sIndex; // convert uint array to byte array - var tbuffBytes = Array.ConvertAll(tbuff, Convert.ToByte); - var datapIndex = 0; - pIndex = sIndex = 309 * 4; - Encshare2(tbuff, tbuff.Skip(pIndex / 4).ToArray(), 16); + pIndex = sIndex = 309 * sizeof(uint); + Encshare2(tbuff, 309, 16); + var tbuffBytes = ConvertUintToBytes(tbuff); + while (len-- > 0) { if ((pIndex - sIndex) == 63) { pIndex = sIndex; //convert int array to byte array - var newtbuffBytes = Array.ConvertAll(tbuff, Convert.ToUInt32); - Encshare2(tbuff, newtbuffBytes, 16); + Encshare2(tbuff, 0, 16); + tbuffBytes = ConvertUintToBytes(tbuff); } datap[datapIndex] ^= tbuffBytes[pIndex]; datapIndex++; pIndex++; } + tbuff = ConvertBytesToUint(tbuffBytes); } - protected void Encshare2(uint[] tbuff, uint[] tbuffp, int len) + /// + /// Encryption related function2 + /// + /// the input data + /// the another index of input data + /// unknown + protected void Encshare2(uint[] tbuff, uint tbuffp, int len) { - uint t1, t2, t3, t4, t5; uint limit; uint p; @@ -46,26 +61,20 @@ protected void Encshare2(uint[] tbuff, uint[] tbuffp, int len) t1 = tbuff[305]; t3 = tbuff[306]; t5 = tbuff[307]; - limit = (uint)len; - // Array.Copy(tbuffp, limit, len); - // for (int i = 0; i < len; i++) - // { - - // } - uint tbuffpIndex = 0; - while (tbuffpIndex < limit) + limit = (uint)(tbuffp + len); + while (tbuffp < limit) { p = t2 + 272; - while (t5 < (ushort.MaxValue + 1)) + while (t5 <= ushort.MaxValue) { t1 += t5; p++; t3 += t1; t1 += t3; tbuff[p - 17] = t1; - tbuff[p - 1] = t1; + tbuff[p - 1] = t3; t4 = (t3 << 24) | (t3 >> 8); - tbuff[p + 15] = t1; + tbuff[p + 15] = t5; t5 <<= 1; t2++; t1 ^= tbuff[t1 & 0xff]; @@ -77,8 +86,7 @@ protected void Encshare2(uint[] tbuff, uint[] tbuffp, int len) t1 = (t4 >> 24) | (t4 << 8); } t3 ^= t1; - tbuff[tbuffpIndex] = t3; - tbuffpIndex++; + tbuff[tbuffp++] = t3; t2--; t1 = tbuff[t2 + 256]; t5 = tbuff[t2 + 272]; @@ -151,38 +159,68 @@ protected void EncShare3(uint[] data, int n1 = 0, int n2 = 0) data[307] = t4; data[308] = (uint)n1; } + /// + /// Seems this function is used to initialize encryption parameter + /// + /// + /// + /// protected void EncShare4(byte[] src, int size, uint[] dest) { uint tmp; int i; byte pos, x, y; - for (i = 0; i < 256; i++) // Initialize all elements in dest array to 0 + for (i = 0; i < 256; i++) dest[i] = 0; - for (y = 0; y < 4; y++) // Loop 4 times, process one byte at a time + for (y = 0; y < 4; y++) { - for (i = 0; i < 256; i++) // Perform operation on each element in dest array + for (i = 0; i < 256; i++) { - dest[i] = (dest[i] << 8) + (uint)i; // Left shift dest[i] by 8 bits and add i in the low 8 bits + dest[i] = (dest[i] << 8) + (uint)i; } - for (pos = y, x = 0; x < 2; x++) // Loop 2 times, process 128 bytes each time + for (pos = y, x = 0; x < 2; x++) { - for (i = 0; i < 256; i++) // Perform operation on each element in dest array + for (i = 0; i < 256; i++) { - tmp = dest[i]; // Save the value of dest[i] in tmp - pos += (byte)(tmp + src[i % size]); // Calculate the value of pos - dest[i] = dest[pos]; // Assign the value of dest[pos] to dest[i] - dest[pos] = tmp; // Assign the value of tmp to dest[pos] + tmp = dest[i]; + pos += (byte)(tmp + src[i % size]); + dest[i] = dest[pos]; + dest[pos] = tmp; } } } - for (i = 0; i < 256; i++) // Perform operation on each element in dest array - dest[i] ^= (uint)i; // Perform XOR between dest[i] and i to obtain the final key + for (i = 0; i < 256; i++) + dest[i] ^= (uint)i; - EncShare3(dest); // Call the encshare3 function to further encrypt the key + EncShare3(dest); + } + protected static byte[] ConvertUintToBytes(uint[] input) + { + var ontputBytes = new byte[input.Length * sizeof(uint)]; + for (var i = 0; i < input.Length; i++) + { + Array.Copy(BitConverter.GetBytes(input[i]), 0, ontputBytes, i * sizeof(uint), sizeof(uint)); + } + return ontputBytes.ToArray(); + } + protected static uint[] ConvertBytesToUint(byte[] input) + { + if (input.Length % sizeof(uint) != 0) + { + throw new System.Exception("the input length is not correct"); + } + var onputInts = new uint[input.Length / sizeof(uint)]; + for (var i = 0; i < (input.Length / sizeof(uint)); i++) + { + var tempBytes = input.Skip(i * sizeof(uint)).Take(sizeof(uint)).ToArray(); + var tempUint = BitConverter.ToUInt32(tempBytes); + onputInts[i] = tempUint; + } + return onputInts; } } } \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs index 7303065cb..ed6141774 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; namespace UniSpy.Server.ServerBrowser.V1.Aggregate @@ -25,20 +26,112 @@ public Enctype1(byte[] key) public override byte[] Encrypt(byte[] data) { + // create a copy + var dataCopy = data.ToArray(); + throw new System.NotImplementedException(); } + private byte[] Decoder(byte[] data, int dataLen) + { + var tbuff = new byte[326]; + var tbuff2 = new byte[258]; - private void Func1(byte[] id) + var len = BitConverter.ToInt32(data.Take(4).ToArray()); + if (len <= 0) + { + throw new ServerBrowser.Exception("input data length must bigger than 0"); + } + if (len > dataLen) + { + throw new ServerBrowser.Exception("input data length can not bigger than dataLen"); + } + data[4] = (byte)((data[4] ^ 62) - 20); + data[5] = (byte)((data[5] ^ 205) - 5); + var tempData1 = data.Skip(19).ToArray(); + Func8(tempData1, 16, Enctype1Table); + data = data.Take(19).Concat(tempData1).ToArray(); + len -= data[4] + data[5] + 40; + var dataP = 0 + data[5] + 40; + var tempLen = (len >> 2) - 5; + if (tempLen >= 0) + { + Func4(Key); + var tempData2 = data.Skip(dataP).ToArray(); + Func6(tempData2, tempLen); + } + + + tempLen = (len >> 1) - 17; + if (tempLen >= 0) + { + var tempData3 = data.Skip(36).ToArray(); + uint[] tbuffInt = Array.ConvertAll(tbuff, Convert.ToUInt32); + EncShare4(tempData3, 4, tbuffInt); + data = data.Skip(36).Concat(tempData3).ToArray(); + var tempData4 = data.Skip(dataP).ToArray(); + Encshare1(tbuffInt, 0, data, dataP, tempLen); + data = data.Skip(dataP).Concat(tempData4).ToArray(); + tbuff = Array.ConvertAll(tbuffInt, Convert.ToByte); + } + + var tempData5 = data.Skip(19).ToArray(); + Func3(tempData5, 16, tbuff2); + data = data.Skip(19).Concat(tempData5).ToArray(); + var tempData6 = data.Skip(dataP).ToArray(); + Func2(tempData6, len, tbuff2); + data = data.Skip(dataP).Concat(tempData6).ToArray(); + return data; + } + private void Func1(byte[] data) { - if (id is not null) + if (data is not null) { - if (id.Length > 0) - { - Func4(id); - } + throw new ServerBrowser.Exception("the input data should not be null."); + } + if (data.Length <= 0) + { + throw new ServerBrowser.Exception("the input data length is not valid."); } + Func4(data); + } + void Func2(byte[] data, int size, byte[] crypt) + { + byte n1, n2, t; + n1 = crypt[256]; + n2 = crypt[257]; + int dataIndex = 0; + while (size-- > 0) + { + t = crypt[++n1]; + n2 += t; + crypt[n1] = crypt[n2]; + t += crypt[n1]; + data[dataIndex] ^= crypt[t]; + dataIndex++; + } + crypt[256] = n1; + crypt[257] = n2; + } + void Func3(byte[] data, int len, byte[] buff) + { + int i; + byte pos = 0, tmp, rev = 0xff; + for (i = 0; i <= byte.MaxValue; i++) + { + buff[i] = rev--; + } + buff[256] = 0; + buff[257] = 0; + for (i = 0; i <= byte.MaxValue; i++) + { + tmp = buff[i]; + pos += (byte)(data[i % len] + tmp); + buff[i] = buff[pos]; + buff[pos] = tmp; + } + } void Func4(byte[] id) { // Declare variables i, n1, n2, t1, t2 @@ -118,9 +211,8 @@ int Func5(int cnt, byte[] id, ref int n1, ref int n2) return tmp; } - private void Func6(byte[] data) + private void Func6(byte[] data, int len) { - var len = data.Length; int i = 0; var dataInts = Array.ConvertAll(data, Convert.ToInt32); while (len-- > 0) diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs index f59daff21..f0cc5b86e 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs @@ -42,7 +42,7 @@ public int Encoder(byte[] key, byte[] data, int size) EncShare4(datap, data[0], dest); Array.Clear(data, 1 + data[0] + size, 6); - Encshare1(dest, datap, size + 6); + Encshare1(dest, 0, datap, 0, size + 6); for (i = 0; i < key.Length; i++) datap[i] ^= key[i]; diff --git a/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs b/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs new file mode 100644 index 000000000..6b67c2d7a --- /dev/null +++ b/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs @@ -0,0 +1,55 @@ +using System; +using System.Linq; +using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; +using Xunit; + +public class EnctypeTest : EnctypeBase +{ + + public override byte[] Encrypt(byte[] data) => throw new System.NotImplementedException(); + [Fact] + public void EncShare2Test() + { + uint[] tbuff = Enumerable.Repeat(1, 326).ToArray(); + var tbuffUint = Array.ConvertAll(tbuff, Convert.ToUInt32); + var tbuffpUint = tbuffUint.ToArray(); + Encshare2(tbuffUint, 309, 6); + var correctResult = new uint[] + { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 17498629, 67830052, 39783468, 510855193, 366629229, 3551672311, 2479564971, 2781774699, 4118137534, 3094373108, 829124753, 3611234592, 1213911675, 3241236538, 3419395059, 3, 17236227, 34013461, 20580121, 306578602, 230830554, 2185634525, 1540642169, 2099507104, 1913332720, 3987761791, 3463819041, 860209457, 3948159327, 2584192363, 2377682576, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 32775, 32774, 32772, 12, 2389626517, 2938163061, 4097, 1, 1549944694, 2745022601, 3181633273, 1113334022, 2827919296, 1467047999, 1485013325, 2809953970, 4233124407, 61842888, 2999413561, 1295553734, 4062552591, 232414704, 3507545467, 787421828, 1 }; + Assert.True(correctResult.SequenceEqual(correctResult)); + } + + [Fact] + public void EncShare1Test() + { + uint[] tbuff = Enumerable.Repeat(1, 326).ToArray(); + byte[] datap = ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); + Encshare1(tbuff, 0, datap, 0, 16); + uint[] tbuffCorrect = new uint[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 17498629, 67830052, 39783468, 510855193, 366629229, 3551672311, 2479564971, 2781774699, 4118137534, 3094373108, 829124753, 3611234592, 1213911675, 3241236538, 3419395059, 3, 17236227, 34013461, 20580121, 306578602, 230830554, 2185634525, 1540642169, 2099507104, 1913332720, 3987761791, 3463819041, 860209457, 3948159327, 2584192363, 2377682576, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 32775, 32774, 32772, 12, 2389626517, 2938163061, 4097, 1, 1549944694, 2745022601, 3181633273, 1113334022, 2827919296, 1467047999, 1485013325, 2809953970, 4233124407, 61842888, 2999413561, 1295553734, 4062552591, 232414704, 3507545467, 787421828, 1 }; + uint[] datapCorrect = new uint[] { 1549944692, 2745022603, 3181633275, 1113334020, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; + uint[] datapUints = ConvertBytesToUint(datap); + Assert.True(datapCorrect.SequenceEqual(datapUints)); + Assert.True(tbuffCorrect.SequenceEqual(tbuff)); + } + + [Fact] + public void EncShare3Test() + { + uint[] data = Enumerable.Repeat(2, 326).ToArray(); + var dataCorrect = new uint[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 33620998, 135004685, 69605426, 1014894209, 659661192, 2751357934, 70666098, 903130807, 49046030, 3998375437, 390165736, 1818952629, 3823375448, 2963547126, 1207763347, 1, 33620484, 67633672, 35065374, 609093707, 416178922, 41172573, 2689180916, 3999573889, 1412900922, 2353420577, 3107029142, 4152417908, 3761014253, 2622030354, 604491503, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 16, 26297853, 3438224389, 65536, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; + EncShare3(data, 0, 0); + Assert.True(dataCorrect.SequenceEqual(data)); + } + + [Fact] + public void EncShare4Test() + { + byte[] datap = ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); + uint[] dest = Enumerable.Repeat(2, 326).ToArray(); + var destCorrect = new uint[] { 791505064, 2677899357, 1869417374, 1465234252, 2475789121, 740983918, 2189457908, 1330493599, 3469488310, 404138664, 3890561660, 2037844579, 3772710031, 3014765711, 1414723042, 3250538244, 2239997656, 3368442198, 2829463375, 3873709831, 1616809334, 2408416094, 2593714885, 3065269826, 2728411308, 3115826211, 623071236, 1987339440, 3503169148, 3149487561, 3216855940, 4277978053, 2694731547, 943095479, 2947390224, 3284255792, 892575532, 4092678516, 3301083488, 1701020929, 50269918, 1583105766, 1178877547, 3233684681, 100976904, 1279948990, 909431883, 2341052024, 656793440, 1229403759, 825219562, 4059029186, 1111521199, 3435794874, 3486359808, 2088400399, 1498911874, 2458935151, 2138956613, 2661076370, 4193770664, 875740486, 3789516497, 67269043, 3048411365, 3722181363, 3907409649, 842022855, 1785245261, 2997891633, 1027315374, 1448379989, 4244284893, 3856866799, 1397854789, 4294782009, 168303206, 2206334467, 3200035771, 976798254, 2054714062, 1667360351, 1852589230, 4042177498, 3823179561, 1599942934, 437785722, 2223115414, 151501413, 3317924486, 3031575332, 4143189866, 134631422, 4227418534, 2981074340, 3385320959, 117789415, 2711583408, 2879989466, 3991617966, 286206350, 1818888530, 2071585337, 3739025234, 1431558871, 2021051280, 3267424079, 2745248443, 2307337611, 589374105, 2795805112, 1734712546, 2492609632, 4126389475, 4176931104, 2374707415, 3621103376, 3924286366, 2004218049, 84087549, 3957934975, 1212547806, 1549461370, 3082111598, 4025327728, 3132657759, 202009759, 1953642301, 2290488196, 2273689622, 1532595819, 2576839429, 724152551, 2610524026, 1717830723, 1802078870, 2543150389, 2762113475, 538843459, 926252336, 1835774642, 2442121321, 1768364936, 1077865321, 707273192, 3941128409, 50449982, 218878597, 6393, 2778976161, 3183168775, 3520064586, 3974786543, 2863201253, 959919482, 4075851646, 3166321558, 3334784883, 690431172, 336772646, 2964244513, 3705314749, 420976600, 1566245846, 387311668, 1010479510, 2896847722, 2846308697, 319944579, 2930534087, 3351637619, 1195746181, 1650488629, 1751570372, 3418979436, 3570593063, 4008508924, 505193660, 2627368049, 572549046, 252515951, 673636639, 1482054814, 2913665291, 4109518194, 2812631805, 3654778850, 454681732, 33435212, 1296779424, 1364191760, 3536885831, 2644195397, 269380140, 1145189234, 1515732185, 757817052, 1128356723, 1347352576, 3637938136, 1684159756, 3840047978, 3671636355, 2155743464, 2509489735, 555702320, 2559995931, 185189712, 3587438174, 606263025, 2324213583, 2526306506, 488365343, 1094685107, 1313605119, 1886282224, 2105240036, 774640903, 808383289, 993638370, 3553739022, 1162027228, 639955374, 4210601392, 3452684985, 1381031990, 1246242604, 2172609167, 1970533518, 3688480660, 2391593397, 4261137494, 1263113115, 1919957700, 3604219589, 3806348272, 3402139713, 303092719, 1044122905, 370419094, 353606213, 1903121829, 2357873757, 235667635, 4160071907, 2425266738, 3098990011, 521996276, 858858457, 2122099102, 3755824166, 2256844040, 1061009100, 1936812966, 471501886, 1633679743, 2, 3648983511, 3019444315, 3087473871, 3504257679, 3636804204, 677370271, 1517421204, 319045361, 2414764241, 1428393162, 1627687985, 3434938190, 1735044768, 1113717230, 3367048133, 1, 1803453643, 3619135896, 151234568, 3605443923, 1593688450, 2992556862, 3389812064, 1790820139, 3447432721, 1637898815, 1432460662, 2272000269, 3310238632, 964792890, 2941880083, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 16, 2110503879, 2280433177, 65536, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; + + EncShare4(datap, 1, dest); + + Assert.True(destCorrect.SequenceEqual(dest)); + } +} \ No newline at end of file From 1bc399a773b50c403a4090eb67d0b3dbead00a3e Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 14 Jun 2023 10:38:19 +0800 Subject: [PATCH 007/231] update(sb): v1 enctype unittest --- .../V1/Abstraction/BaseClass/EnctypeBase.cs | 2 -- .../src/V1/Aggregate/Enctype2.cs | 22 +++++++++++-------- .../ServerBrowser/test/V1/EnctypeTest.cs | 22 ++++++++++++++++++- 3 files changed, 34 insertions(+), 12 deletions(-) diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs index 1f8e07660..0e7b0ac32 100644 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs +++ b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; -using System.Net.NetworkInformation; using System; using System.Linq; using UniSpy.Server.Core.Abstraction.Interface; diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs index f0cc5b86e..eb9ad98b8 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs @@ -1,3 +1,4 @@ +using System.Linq; using System; using UniSpy.Server.Core.Encryption; using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; @@ -33,19 +34,22 @@ public int Encoder(byte[] key, byte[] data, int size) } data[0] = (byte)headerSize; - byte[] datap = new byte[data.Length - 1]; - Array.Copy(data, 1, datap, 0, datap.Length); - Array.Clear(datap, 0, data[0]); + // set datap index + byte datap = 1; + // set 1-8 as 0 + Array.Clear(data, datap, 8); + Array.Clear(dest, 256, dest.Length - 256); - for (i = 256; i < 326; i++) - dest[i] = 0; - EncShare4(datap, data[0], dest); - Array.Clear(data, 1 + data[0] + size, 6); - Encshare1(dest, 0, datap, 0, size + 6); + var dataTemp = data.Skip(datap).ToArray(); + EncShare4(dataTemp, data[0], dest); + Array.Copy(dataTemp, 0, data, datap, dataTemp.Length); + Array.Clear(data, data[0] + size + 1, 6); + + Encshare1(dest, 0, data, datap + data[0], size + 6); for (i = 0; i < key.Length; i++) - datap[i] ^= key[i]; + data[datap + i] ^= key[i]; size += 1 + data[0] + 6; data[0] ^= 0xec; return size; diff --git a/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs b/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs index 6b67c2d7a..787cd9040 100644 --- a/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs +++ b/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs @@ -1,11 +1,12 @@ +using System.Data; using System; using System.Linq; using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; +using UniSpy.Server.ServerBrowser.V1.Aggregate; using Xunit; public class EnctypeTest : EnctypeBase { - public override byte[] Encrypt(byte[] data) => throw new System.NotImplementedException(); [Fact] public void EncShare2Test() @@ -52,4 +53,23 @@ public void EncShare4Test() Assert.True(destCorrect.SequenceEqual(dest)); } +} + + +public class Encrypt2Test : Enctype2 +{ + public Encrypt2Test() : base("abcdef") + { + } + + [Fact] + public void EncoderTest() + { + var data = Enumerable.Repeat(2, 326).ToArray(); + var dataBytes = ConvertUintToBytes(data); + Encoder(GameSecreteKey, dataBytes, 6); + var dataCorrect = new uint[] { 1667391972, 6710628, 3464277760, 3324666663, 1732499745, 230, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; + data = ConvertBytesToUint(dataBytes); + Assert.True(dataCorrect.SequenceEqual(data)); + } } \ No newline at end of file From bdf08fedd93ab0519bea94f9f77093a661a1ea66 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 16 Jun 2023 09:16:10 +0800 Subject: [PATCH 008/231] fix(psp): wrong request parse on locstring --- src/Libraries/Core/src/Misc/GameSpyUtils.cs | 2 +- .../PresenceConnectionManager/test/Game/GameTest.cs | 9 +++++++++ 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/Libraries/Core/src/Misc/GameSpyUtils.cs b/src/Libraries/Core/src/Misc/GameSpyUtils.cs index 054530d4d..0e12b725b 100755 --- a/src/Libraries/Core/src/Misc/GameSpyUtils.cs +++ b/src/Libraries/Core/src/Misc/GameSpyUtils.cs @@ -29,7 +29,7 @@ public static string GetRequestName(string request) /// public static Dictionary ConvertToKeyValue(string request) { - string[] commandParts = request.Replace(@"\final\", "").TrimStart('\\').TrimEnd('\\').Split('\\'); + string[] commandParts = request.Replace(@"\final\", "").TrimStart('\\').Split('\\'); return ConvertToKeyValue(commandParts); } diff --git a/src/Servers/PresenceConnectionManager/test/Game/GameTest.cs b/src/Servers/PresenceConnectionManager/test/Game/GameTest.cs index 04b6a87f2..843a63bea 100644 --- a/src/Servers/PresenceConnectionManager/test/Game/GameTest.cs +++ b/src/Servers/PresenceConnectionManager/test/Game/GameTest.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using UniSpy.Server.Core.Abstraction.Interface; using UniSpy.Server.Core.Encryption; +using UniSpy.Server.PresenceConnectionManager.Contract.Request; using Xunit; namespace UniSpy.Server.PresenceConnectionManager.Test @@ -34,5 +35,13 @@ public void ConflictGlobalStorm() ((ITestClient)MokeObject.client).TestReceived(UniSpyEncoding.GetBytes(raw)); } } + [Fact] + public void swbfrontps2Test() + { + var raw = @"\status\1\sesskey\1111\statstring\EN LIGNE\locstring\\final\"; + var req = new StatusRequest(raw); + req.Parse(); + + } } } \ No newline at end of file From 5dbd13725b21f852160a3702c504572c07da0dd0 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 16 Jun 2023 17:25:18 +0800 Subject: [PATCH 009/231] refactor(sb): verify enctype1 function --- .../V1/Abstraction/BaseClass/EnctypeBase.cs | 13 +- .../V1/Abstraction/Interface/IEnctypeTest.cs | 28 ++++ .../src/V1/Aggregate/Enctype1.cs | 127 ++++++++++-------- .../src/V1/Aggregate/Enctype2.cs | 6 +- .../ServerBrowser/test/V1/EnctypeTest.cs | 59 +++++--- 5 files changed, 157 insertions(+), 76 deletions(-) create mode 100644 src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs index 0e7b0ac32..e6262a8e9 100644 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs +++ b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs @@ -8,7 +8,7 @@ namespace UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass /// The encryption type 1 and 2 base class which contains the functions using in enctype1 and enctype2. /// What confuses me the most is why gamespy doesn't use standard encryption schemes like DES, AES etc. /// - public abstract class EnctypeBase : ICryptography + public abstract class EnctypeBase : ICryptography, IEnctypeShareTest { public byte[] Decrypt(byte[] data) => throw new UniSpy.Exception("Enctype only encrypt message on server side."); public abstract byte[] Encrypt(byte[] data); @@ -220,5 +220,16 @@ protected static uint[] ConvertBytesToUint(byte[] input) } return onputInts; } + + void IEnctypeShareTest.Encshare1(uint[] tbuff, int tbuffIndex, byte[] datap, int datapIndex, int len) => Encshare1(tbuff, tbuffIndex, datap, datapIndex, len); + + void IEnctypeShareTest.Encshare2(uint[] tbuff, uint tbuffp, int len) => Encshare2(tbuff, tbuffp, len); + + void IEnctypeShareTest.EncShare3(uint[] data, int n1, int n2) => EncShare3(data, n1, n2); + + void IEnctypeShareTest.EncShare4(byte[] src, int size, uint[] dest) => EncShare4(src, size, dest); + uint[] IEnctypeShareTest.ConvertBytesToUint(byte[] input) => ConvertBytesToUint(input); + byte[] IEnctypeShareTest.ConvertUintToBytes(uint[] input) => ConvertUintToBytes(input); + } } \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs new file mode 100644 index 000000000..d559c28ca --- /dev/null +++ b/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs @@ -0,0 +1,28 @@ +public interface IEnctype1Test : IEnctypeShareTest +{ + void Func1(byte[] data); + void Func2(byte[] data, int size, byte[] crypt); + void Func3(byte[] data, int len, byte[] buff); + void Func4(byte[] id); + int Func5(int cnt, byte[] id, ref int n1, ref int n2); + void Func6(byte[] data, int len); + int Func7(int len); + void Func8(byte[] data, int len, byte[] enctype1_data); + byte[] EncKey { get; } +} + +public interface IEnctypeShareTest +{ + void Encshare1(uint[] tbuff, int tbuffIndex, byte[] datap, int datapIndex, int len); + void Encshare2(uint[] tbuff, uint tbuffp, int len); + void EncShare3(uint[] data, int n1 = 0, int n2 = 0); + void EncShare4(byte[] src, int size, uint[] dest); + byte[] ConvertUintToBytes(uint[] input); + uint[] ConvertBytesToUint(byte[] input); +} + +public interface IEnctype2Test : IEnctypeShareTest +{ + int Encoder(byte[] key, byte[] data, int size); + +} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs index ed6141774..4cc18eda0 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs @@ -4,7 +4,7 @@ namespace UniSpy.Server.ServerBrowser.V1.Aggregate { - public class Enctype1 : EnctypeBase + public class Enctype1 : EnctypeBase, IEnctype1Test { /// /// The server key @@ -17,7 +17,10 @@ public class Enctype1 : EnctypeBase /// public static readonly byte[] ClientKey = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; public byte[] Key { get; private set; } - private byte[] _enc1key = new byte[261]; + + byte[] IEnctype1Test.EncKey => _enckey; + + private byte[] _enckey = new byte[261]; public Enctype1(byte[] key) { Key = key; @@ -146,25 +149,24 @@ void Func4(byte[] id) return; // Initialize encryption array _enc1key - byte[] _enc1key = new byte[256]; for (i = 0; i < 256; i++) - _enc1key[i] = (byte)i; + _enckey[i] = (byte)i; // Shuffle the encryption array _enc1key for (i = 255; i >= 0; i--) { t1 = (byte)Func5(i, id, ref n1, ref n2); - t2 = _enc1key[i]; - _enc1key[i] = _enc1key[t1]; - _enc1key[t1] = t2; + t2 = _enckey[i]; + _enckey[i] = _enckey[t1]; + _enckey[t1] = t2; } // Set specific values to some elements of the encryption array _enc1key - _enc1key[256] = _enc1key[1]; - _enc1key[257] = _enc1key[3]; - _enc1key[258] = _enc1key[5]; - _enc1key[259] = _enc1key[7]; - _enc1key[260] = _enc1key[n1 & 0xff]; + _enckey[256] = _enckey[1]; + _enckey[257] = _enckey[3]; + _enckey[258] = _enckey[5]; + _enckey[259] = _enckey[7]; + _enckey[260] = _enckey[n1 & 0xff]; } int Func5(int cnt, byte[] id, ref int n1, ref int n2) { @@ -193,7 +195,7 @@ int Func5(int cnt, byte[] id, ref int n1, ref int n2) do { // Update the values of n1 and n2, and calculate the tmp value - n1 = _enc1key[n1 & 0xff] + id[n2]; + n1 = _enckey[n1 & 0xff] + id[n2]; n2++; if (n2 >= id.Length) { @@ -211,7 +213,7 @@ int Func5(int cnt, byte[] id, ref int n1, ref int n2) return tmp; } - private void Func6(byte[] data, int len) + void Func6(byte[] data, int len) { int i = 0; var dataInts = Array.ConvertAll(data, Convert.ToInt32); @@ -228,58 +230,58 @@ int Func7(int len) byte a, b, c; // Get certain elements from the encryption array _enc1key and calculate new values based on them - a = _enc1key[256]; - b = _enc1key[257]; - c = _enc1key[a]; - _enc1key[256] = (byte)(a + 1); - _enc1key[257] = (byte)(b + c); - - a = _enc1key[260]; - b = _enc1key[257]; - b = _enc1key[b]; - c = _enc1key[a]; - _enc1key[a] = b; - - a = _enc1key[259]; - b = _enc1key[257]; - a = _enc1key[a]; - _enc1key[b] = a; - - a = _enc1key[256]; - b = _enc1key[259]; - a = _enc1key[a]; - _enc1key[b] = a; - - a = _enc1key[256]; - _enc1key[a] = c; - b = _enc1key[258]; - a = _enc1key[c]; - c = _enc1key[259]; + a = _enckey[256]; + b = _enckey[257]; + c = _enckey[a]; + _enckey[256] = (byte)(a + 1); + _enckey[257] = (byte)(b + c); + + a = _enckey[260]; + b = _enckey[257]; + b = _enckey[b]; + c = _enckey[a]; + _enckey[a] = b; + + a = _enckey[259]; + b = _enckey[257]; + a = _enckey[a]; + _enckey[b] = a; + + a = _enckey[256]; + b = _enckey[259]; + a = _enckey[a]; + _enckey[b] = a; + + a = _enckey[256]; + _enckey[a] = c; + b = _enckey[258]; + a = _enckey[c]; + c = _enckey[259]; b = (byte)(b + a); - _enc1key[258] = b; + _enckey[258] = b; a = b; - c = _enc1key[c]; - b = _enc1key[257]; - b = _enc1key[b]; - a = _enc1key[a]; + c = _enckey[c]; + b = _enckey[257]; + b = _enckey[b]; + a = _enckey[a]; c += b; - b = _enc1key[260]; - b = _enc1key[b]; + b = _enckey[260]; + b = _enckey[b]; c += b; - b = _enc1key[c]; - c = _enc1key[256]; - c = _enc1key[c]; + b = _enckey[c]; + c = _enckey[256]; + c = _enckey[c]; a += c; - c = _enc1key[b]; - b = _enc1key[a]; + c = _enckey[b]; + b = _enckey[a]; // Store the len value in variable a and perform XOR on c and a a = (byte)len; c ^= b; - _enc1key[260] = a; + _enckey[260] = a; c ^= a; - _enc1key[259] = c; + _enckey[259] = c; // Return c value return c; @@ -296,5 +298,20 @@ void Func8(byte[] data, int len, byte[] enctype1_data) } } + void IEnctype1Test.Func1(byte[] data) => Func1(data); + + void IEnctype1Test.Func2(byte[] data, int size, byte[] crypt) => Func2(data, size, crypt); + + void IEnctype1Test.Func3(byte[] data, int len, byte[] buff) => Func3(data, len, buff); + + void IEnctype1Test.Func4(byte[] id) => Func4(id); + + int IEnctype1Test.Func5(int cnt, byte[] id, ref int n1, ref int n2) => Func5(cnt, id, ref n1, ref n2); + + void IEnctype1Test.Func6(byte[] data, int len) => Func6(data, len); + + int IEnctype1Test.Func7(int len) => Func7(len); + + void IEnctype1Test.Func8(byte[] data, int len, byte[] enctype1_data) => Func8(data, len, enctype1_data); } } \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs index eb9ad98b8..d425cf371 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs @@ -6,7 +6,7 @@ namespace UniSpy.Server.ServerBrowser.V1.Aggregate { - public class Enctype2 : EnctypeBase + public class Enctype2 : EnctypeBase, IEnctype2Test { public const int HeaderSize = 8; public byte[] GameSecreteKey { get; private set; } @@ -22,7 +22,7 @@ public override byte[] Encrypt(byte[] data) // Encoder(GameSecreteKey,data,?); } - public int Encoder(byte[] key, byte[] data, int size) + private int Encoder(byte[] key, byte[] data, int size) { uint[] dest = new uint[326]; int i; @@ -62,5 +62,7 @@ private void KeyXor(byte[] data) data[i] ^= GameSecreteKey[i]; } } + + int IEnctype2Test.Encoder(byte[] key, byte[] data, int size) => Encoder(key, data, size); } } \ No newline at end of file diff --git a/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs b/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs index 787cd9040..132761d29 100644 --- a/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs +++ b/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs @@ -1,20 +1,20 @@ -using System.Data; using System; using System.Linq; using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; using UniSpy.Server.ServerBrowser.V1.Aggregate; using Xunit; +using UniSpy.Server.Core.Encryption; -public class EnctypeTest : EnctypeBase +public class EnctypeTest { - public override byte[] Encrypt(byte[] data) => throw new System.NotImplementedException(); + private IEnctypeShareTest encShare = new Enctype2("abcdef"); [Fact] public void EncShare2Test() { uint[] tbuff = Enumerable.Repeat(1, 326).ToArray(); var tbuffUint = Array.ConvertAll(tbuff, Convert.ToUInt32); var tbuffpUint = tbuffUint.ToArray(); - Encshare2(tbuffUint, 309, 6); + encShare.Encshare2(tbuffUint, 309, 6); var correctResult = new uint[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 17498629, 67830052, 39783468, 510855193, 366629229, 3551672311, 2479564971, 2781774699, 4118137534, 3094373108, 829124753, 3611234592, 1213911675, 3241236538, 3419395059, 3, 17236227, 34013461, 20580121, 306578602, 230830554, 2185634525, 1540642169, 2099507104, 1913332720, 3987761791, 3463819041, 860209457, 3948159327, 2584192363, 2377682576, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 32775, 32774, 32772, 12, 2389626517, 2938163061, 4097, 1, 1549944694, 2745022601, 3181633273, 1113334022, 2827919296, 1467047999, 1485013325, 2809953970, 4233124407, 61842888, 2999413561, 1295553734, 4062552591, 232414704, 3507545467, 787421828, 1 }; Assert.True(correctResult.SequenceEqual(correctResult)); @@ -24,11 +24,11 @@ public void EncShare2Test() public void EncShare1Test() { uint[] tbuff = Enumerable.Repeat(1, 326).ToArray(); - byte[] datap = ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); - Encshare1(tbuff, 0, datap, 0, 16); + byte[] datap = encShare.ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); + encShare.Encshare1(tbuff, 0, datap, 0, 16); uint[] tbuffCorrect = new uint[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 17498629, 67830052, 39783468, 510855193, 366629229, 3551672311, 2479564971, 2781774699, 4118137534, 3094373108, 829124753, 3611234592, 1213911675, 3241236538, 3419395059, 3, 17236227, 34013461, 20580121, 306578602, 230830554, 2185634525, 1540642169, 2099507104, 1913332720, 3987761791, 3463819041, 860209457, 3948159327, 2584192363, 2377682576, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 32775, 32774, 32772, 12, 2389626517, 2938163061, 4097, 1, 1549944694, 2745022601, 3181633273, 1113334022, 2827919296, 1467047999, 1485013325, 2809953970, 4233124407, 61842888, 2999413561, 1295553734, 4062552591, 232414704, 3507545467, 787421828, 1 }; uint[] datapCorrect = new uint[] { 1549944692, 2745022603, 3181633275, 1113334020, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; - uint[] datapUints = ConvertBytesToUint(datap); + uint[] datapUints = encShare.ConvertBytesToUint(datap); Assert.True(datapCorrect.SequenceEqual(datapUints)); Assert.True(tbuffCorrect.SequenceEqual(tbuff)); } @@ -38,38 +38,61 @@ public void EncShare3Test() { uint[] data = Enumerable.Repeat(2, 326).ToArray(); var dataCorrect = new uint[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 33620998, 135004685, 69605426, 1014894209, 659661192, 2751357934, 70666098, 903130807, 49046030, 3998375437, 390165736, 1818952629, 3823375448, 2963547126, 1207763347, 1, 33620484, 67633672, 35065374, 609093707, 416178922, 41172573, 2689180916, 3999573889, 1412900922, 2353420577, 3107029142, 4152417908, 3761014253, 2622030354, 604491503, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 16, 26297853, 3438224389, 65536, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; - EncShare3(data, 0, 0); + encShare.EncShare3(data, 0, 0); Assert.True(dataCorrect.SequenceEqual(data)); } [Fact] public void EncShare4Test() { - byte[] datap = ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); + byte[] datap = encShare.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); uint[] dest = Enumerable.Repeat(2, 326).ToArray(); var destCorrect = new uint[] { 791505064, 2677899357, 1869417374, 1465234252, 2475789121, 740983918, 2189457908, 1330493599, 3469488310, 404138664, 3890561660, 2037844579, 3772710031, 3014765711, 1414723042, 3250538244, 2239997656, 3368442198, 2829463375, 3873709831, 1616809334, 2408416094, 2593714885, 3065269826, 2728411308, 3115826211, 623071236, 1987339440, 3503169148, 3149487561, 3216855940, 4277978053, 2694731547, 943095479, 2947390224, 3284255792, 892575532, 4092678516, 3301083488, 1701020929, 50269918, 1583105766, 1178877547, 3233684681, 100976904, 1279948990, 909431883, 2341052024, 656793440, 1229403759, 825219562, 4059029186, 1111521199, 3435794874, 3486359808, 2088400399, 1498911874, 2458935151, 2138956613, 2661076370, 4193770664, 875740486, 3789516497, 67269043, 3048411365, 3722181363, 3907409649, 842022855, 1785245261, 2997891633, 1027315374, 1448379989, 4244284893, 3856866799, 1397854789, 4294782009, 168303206, 2206334467, 3200035771, 976798254, 2054714062, 1667360351, 1852589230, 4042177498, 3823179561, 1599942934, 437785722, 2223115414, 151501413, 3317924486, 3031575332, 4143189866, 134631422, 4227418534, 2981074340, 3385320959, 117789415, 2711583408, 2879989466, 3991617966, 286206350, 1818888530, 2071585337, 3739025234, 1431558871, 2021051280, 3267424079, 2745248443, 2307337611, 589374105, 2795805112, 1734712546, 2492609632, 4126389475, 4176931104, 2374707415, 3621103376, 3924286366, 2004218049, 84087549, 3957934975, 1212547806, 1549461370, 3082111598, 4025327728, 3132657759, 202009759, 1953642301, 2290488196, 2273689622, 1532595819, 2576839429, 724152551, 2610524026, 1717830723, 1802078870, 2543150389, 2762113475, 538843459, 926252336, 1835774642, 2442121321, 1768364936, 1077865321, 707273192, 3941128409, 50449982, 218878597, 6393, 2778976161, 3183168775, 3520064586, 3974786543, 2863201253, 959919482, 4075851646, 3166321558, 3334784883, 690431172, 336772646, 2964244513, 3705314749, 420976600, 1566245846, 387311668, 1010479510, 2896847722, 2846308697, 319944579, 2930534087, 3351637619, 1195746181, 1650488629, 1751570372, 3418979436, 3570593063, 4008508924, 505193660, 2627368049, 572549046, 252515951, 673636639, 1482054814, 2913665291, 4109518194, 2812631805, 3654778850, 454681732, 33435212, 1296779424, 1364191760, 3536885831, 2644195397, 269380140, 1145189234, 1515732185, 757817052, 1128356723, 1347352576, 3637938136, 1684159756, 3840047978, 3671636355, 2155743464, 2509489735, 555702320, 2559995931, 185189712, 3587438174, 606263025, 2324213583, 2526306506, 488365343, 1094685107, 1313605119, 1886282224, 2105240036, 774640903, 808383289, 993638370, 3553739022, 1162027228, 639955374, 4210601392, 3452684985, 1381031990, 1246242604, 2172609167, 1970533518, 3688480660, 2391593397, 4261137494, 1263113115, 1919957700, 3604219589, 3806348272, 3402139713, 303092719, 1044122905, 370419094, 353606213, 1903121829, 2357873757, 235667635, 4160071907, 2425266738, 3098990011, 521996276, 858858457, 2122099102, 3755824166, 2256844040, 1061009100, 1936812966, 471501886, 1633679743, 2, 3648983511, 3019444315, 3087473871, 3504257679, 3636804204, 677370271, 1517421204, 319045361, 2414764241, 1428393162, 1627687985, 3434938190, 1735044768, 1113717230, 3367048133, 1, 1803453643, 3619135896, 151234568, 3605443923, 1593688450, 2992556862, 3389812064, 1790820139, 3447432721, 1637898815, 1432460662, 2272000269, 3310238632, 964792890, 2941880083, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 16, 2110503879, 2280433177, 65536, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; - EncShare4(datap, 1, dest); + encShare.EncShare4(datap, 1, dest); Assert.True(destCorrect.SequenceEqual(dest)); } } -public class Encrypt2Test : Enctype2 +public class Encrypt2Test { - public Encrypt2Test() : base("abcdef") - { - } - + private IEnctype2Test _enctype2 = new Enctype2("abcdef"); [Fact] public void EncoderTest() { var data = Enumerable.Repeat(2, 326).ToArray(); - var dataBytes = ConvertUintToBytes(data); - Encoder(GameSecreteKey, dataBytes, 6); + var dataBytes = _enctype2.ConvertUintToBytes(data); + _enctype2.Encoder(((Enctype2)_enctype2).GameSecreteKey, dataBytes, 6); var dataCorrect = new uint[] { 1667391972, 6710628, 3464277760, 3324666663, 1732499745, 230, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; - data = ConvertBytesToUint(dataBytes); + data = _enctype2.ConvertBytesToUint(dataBytes); Assert.True(dataCorrect.SequenceEqual(data)); } +} + + +public class Enctype1Test +{ + [Fact] + public void Func5Test() + { + IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); + var key = UniSpyEncoding.GetBytes("abcdef"); + int n1 = 2, n2 = 2; + enc1.Func5(5, key, ref n1, ref n2); + Assert.True(n1 == 99); + Assert.True(n2 == 1); + Assert.True(key.SequenceEqual(UniSpyEncoding.GetBytes("abcdef"))); + } + [Fact] + public void Func4Test() + { + IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); + var key = UniSpyEncoding.GetBytes("abcdef"); + enc1.Func4(key); + Assert.True(key.SequenceEqual(UniSpyEncoding.GetBytes("abcdef"))); + var correctEnc1Key = new byte[] { 35, 53, 63, 48, 8, 15, 110, 160, 40, 34, 38, 172, 125, 28, 118, 9, 22, 107, 36, 16, 46, 104, 6, 136, 32, 50, 101, 132, 21, 119, 190, 20, 114, 154, 33, 42, 98, 39, 131, 70, 56, 144, 4, 120, 2, 26, 149, 18, 0, 153, 24, 44, 12, 152, 10, 138, 51, 133, 71, 17, 5, 202, 3, 208, 11, 128, 134, 83, 14, 41, 23, 52, 122, 151, 47, 116, 27, 108, 139, 214, 113, 75, 250, 78, 145, 86, 57, 124, 111, 45, 126, 59, 147, 65, 141, 137, 143, 69, 96, 148, 30, 105, 95, 196, 129, 146, 62, 109, 54, 81, 130, 102, 68, 115, 60, 117, 58, 140, 74, 121, 66, 123, 64, 142, 135, 127, 72, 99, 166, 76, 220, 82, 226, 112, 89, 80, 77, 92, 29, 90, 87, 84, 93, 178, 232, 100, 150, 88, 155, 184, 238, 106, 244, 94, 161, 156, 157, 158, 159, 1, 167, 162, 163, 164, 165, 7, 173, 168, 169, 170, 171, 13, 179, 174, 175, 176, 177, 19, 185, 180, 181, 182, 183, 25, 191, 186, 187, 188, 189, 31, 197, 192, 193, 194, 195, 37, 203, 198, 199, 200, 201, 43, 209, 204, 205, 206, 207, 49, 215, 210, 211, 212, 213, 55, 221, 216, 217, 218, 219, 61, 227, 222, 223, 224, 225, 67, 233, 228, 229, 230, 231, 73, 239, 234, 235, 236, 237, 79, 245, 240, 241, 242, 243, 85, 251, 246, 247, 248, 249, 91, 103, 252, 253, 254, 255, 97, 53, 48, 15, 160, 45 }; + Assert.True(enc1.EncKey.SequenceEqual(correctEnc1Key)); + } } \ No newline at end of file From a3c970a29ef51eb6730e4d86ad5e616dd67f54f4 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 16 Jun 2023 18:52:40 +0800 Subject: [PATCH 010/231] refactor(sb): correct enctype1 code --- .../src/V1/Aggregate/Enctype1.cs | 12 ++-- .../ServerBrowser/test/V1/EnctypeTest.cs | 56 +++++++++++++++++++ 2 files changed, 63 insertions(+), 5 deletions(-) diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs index 4cc18eda0..2dbb05a54 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs @@ -96,7 +96,6 @@ private void Func1(byte[] data) throw new ServerBrowser.Exception("the input data length is not valid."); } Func4(data); - } void Func2(byte[] data, int size, byte[] crypt) { @@ -109,6 +108,8 @@ void Func2(byte[] data, int size, byte[] crypt) t = crypt[++n1]; n2 += t; crypt[n1] = crypt[n2]; + crypt[n2] = t; + t += crypt[n1]; data[dataIndex] ^= crypt[t]; dataIndex++; @@ -212,14 +213,15 @@ int Func5(int cnt, byte[] id, ref int n1, ref int n2) // Return the tmp value return tmp; } - + /// + /// init the enckey, data is not touched + /// void Func6(byte[] data, int len) { int i = 0; - var dataInts = Array.ConvertAll(data, Convert.ToInt32); while (len-- > 0) { - dataInts[i] = Func7(dataInts[i]); + data[i] = (byte)Func7(data[i]); i++; } } @@ -293,7 +295,7 @@ void Func8(byte[] data, int len, byte[] enctype1_data) while (len-- > 0) { // Encrypt the byte in data using the enctype1_data array - data[dataIndex] = enctype1_data[dataIndex]; + data[dataIndex] = enctype1_data[data[dataIndex]]; dataIndex++; } } diff --git a/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs b/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs index 132761d29..d4fdd1433 100644 --- a/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs +++ b/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs @@ -95,4 +95,60 @@ public void Func4Test() var correctEnc1Key = new byte[] { 35, 53, 63, 48, 8, 15, 110, 160, 40, 34, 38, 172, 125, 28, 118, 9, 22, 107, 36, 16, 46, 104, 6, 136, 32, 50, 101, 132, 21, 119, 190, 20, 114, 154, 33, 42, 98, 39, 131, 70, 56, 144, 4, 120, 2, 26, 149, 18, 0, 153, 24, 44, 12, 152, 10, 138, 51, 133, 71, 17, 5, 202, 3, 208, 11, 128, 134, 83, 14, 41, 23, 52, 122, 151, 47, 116, 27, 108, 139, 214, 113, 75, 250, 78, 145, 86, 57, 124, 111, 45, 126, 59, 147, 65, 141, 137, 143, 69, 96, 148, 30, 105, 95, 196, 129, 146, 62, 109, 54, 81, 130, 102, 68, 115, 60, 117, 58, 140, 74, 121, 66, 123, 64, 142, 135, 127, 72, 99, 166, 76, 220, 82, 226, 112, 89, 80, 77, 92, 29, 90, 87, 84, 93, 178, 232, 100, 150, 88, 155, 184, 238, 106, 244, 94, 161, 156, 157, 158, 159, 1, 167, 162, 163, 164, 165, 7, 173, 168, 169, 170, 171, 13, 179, 174, 175, 176, 177, 19, 185, 180, 181, 182, 183, 25, 191, 186, 187, 188, 189, 31, 197, 192, 193, 194, 195, 37, 203, 198, 199, 200, 201, 43, 209, 204, 205, 206, 207, 49, 215, 210, 211, 212, 213, 55, 221, 216, 217, 218, 219, 61, 227, 222, 223, 224, 225, 67, 233, 228, 229, 230, 231, 73, 239, 234, 235, 236, 237, 79, 245, 240, 241, 242, 243, 85, 251, 246, 247, 248, 249, 91, 103, 252, 253, 254, 255, 97, 53, 48, 15, 160, 45 }; Assert.True(enc1.EncKey.SequenceEqual(correctEnc1Key)); } + + [Fact] + public void Func7Test() + { + IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); + var r = enc1.Func7(10); + var correctEnc1Key = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 10, 10 }; + Assert.True(enc1.EncKey.SequenceEqual(correctEnc1Key)); + } + + [Fact] + public void Func8Test() + { + IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); + var data = enc1.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); + enc1.Func8(data, 10, Enctype1.Enctype1Table); + var correctData = new uint[] { 16843194, 16843194, 442, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; + var dataUints = enc1.ConvertBytesToUint(data); + Assert.True(correctData.SequenceEqual(dataUints)); + } + + [Fact] + public void Func6Test() + { + IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); + var data = enc1.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); + enc1.Func6(data, 10); + var correctData = new byte[] { 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 }; + Assert.True(correctData.SequenceEqual(data)); + var encKeyCorrect = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0 }; + Assert.True(encKeyCorrect.SequenceEqual(enc1.EncKey)); + } + + [Fact] + public void Func3Test() + { + IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); + var data = enc1.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); + var buff = enc1.ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); + enc1.Func3(data, 10, buff); + var correctBuff = new uint[] { 2423783724, 2626217045, 2666177577, 3362939995, 3417775348, 3999423967, 1642197989, 91342666, 3246176910, 455134510, 3298402371, 2675676894, 4059931565, 2176375884, 1087007327, 3680970979, 2960389948, 3992074825, 449167537, 4019542232, 3920131915, 1672128080, 1953860086, 2635063843, 499266173, 995782236, 1863130809, 2899638540, 3943859670, 3611120871, 2705294232, 1747078419, 1389594391, 1704679349, 830416758, 2357868666, 556737645, 2486454320, 184857584, 1150109371, 1338197782, 1775337177, 3216789815, 2952129716, 713304926, 4209027549, 171131454, 2933556984, 2184588750, 896240384, 1895567724, 121182412, 4283344853, 2548565524, 242815122, 1444522835, 3062851200, 3486691985, 3499345562, 631422715, 149333344, 1096400159, 4261619269, 164173058, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; + var buffUints = enc1.ConvertBytesToUint(buff); + Assert.True(correctBuff.SequenceEqual(buffUints)); + } + + [Fact] + public void Func2Test() + { + IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); + var data = enc1.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); + var crypt = enc1.ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); + enc1.Func2(data, 10, crypt); + var correctBuff = new uint[] { 33554432, 131584, 512, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1548, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; + var buffUints = enc1.ConvertBytesToUint(crypt); + Assert.True(correctBuff.SequenceEqual(buffUints)); + } } \ No newline at end of file From dc0f5313f1e1f7f03c2c325107c95fced36fa865 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 17 Jun 2023 19:12:35 +0800 Subject: [PATCH 011/231] fix: increase try catch scope --- .../Abstraction/BaseClass/CmdHandlerBase.cs | 15 +----- .../Abstraction/BaseClass/CmdSwitcherBase.cs | 4 +- src/Libraries/Core/src/Exception/Exception.cs | 47 +++++++++++++++++++ src/Libraries/Core/src/Misc/GameSpyUtils.cs | 8 +--- .../ChannelProperty/ChannelUserRelated.cs | 3 +- .../ChannelProperty/ChannelValidation.cs | 2 +- src/Servers/Chat/src/Application/Program.cs | 2 +- .../GameStatus/src/Application/Program.cs | 2 +- .../{GSException.cs => Exception.cs} | 0 .../ConnectionForwardClient.cs | 0 .../ConnectionListener.cs | 0 .../{Entity => Aggregate}/ConnectionPairs.cs | 0 .../src/{Entity => Aggregate}/NetworkUtils.cs | 0 .../{Entity => Aggregate}/RelayServerInfo.cs | 0 .../ServerStatusReporter.cs | 0 .../src/Application/Program.cs | 2 +- .../NatNegotiation/src/Application/Program.cs | 2 +- .../src/Application/Program.cs | 2 +- .../src/Application/Program.cs | 2 +- .../QueryReport/src/Application/Program.cs | 2 +- .../ServerBrowser/src/Application/Program.cs | 3 +- .../SakeArrayObject.cs | 2 +- .../Structure => Aggregate}/SoapXElement.cs | 2 +- .../Constant => Aggregate}/WebEndpoints.cs | 2 +- .../WebServer/src/Application/Client.cs | 11 ++++- .../WebServer/src/Application/Program.cs | 2 +- .../WebServer/src/Exception/Exception.cs} | 11 ++--- .../WebServer/src/Handler/CmdSwitcher.cs | 13 +++-- .../Module/Sake/Abstraction/CmdHandlerBase.cs | 2 +- .../Contract/Request/CreateRecordRequest.cs | 2 +- .../Contract/Request/GetMyRecordsRequest.cs | 2 +- .../Request/GetRandomRecordsRequest.cs | 2 +- .../Request/GetSpecificRecordsRequest.cs | 2 +- .../Request/SearchForRecordsRequest.cs | 2 +- .../Contract/Request/UpdateRecordRequest.cs | 2 +- .../Contract/Result/GetMyRecordsResult.cs | 2 +- src/Servers/WebServer/test/GeneralTest.cs | 21 +++++++++ .../test/{TestClasses.cs => MokeObject.cs} | 0 38 files changed, 116 insertions(+), 60 deletions(-) create mode 100755 src/Libraries/Core/src/Exception/Exception.cs rename src/Servers/GameStatus/src/Exception/{GSException.cs => Exception.cs} (100%) rename src/Servers/GameTrafficRelay/src/{Entity => Aggregate}/ConnectionForwardClient.cs (100%) rename src/Servers/GameTrafficRelay/src/{Entity => Aggregate}/ConnectionListener.cs (100%) rename src/Servers/GameTrafficRelay/src/{Entity => Aggregate}/ConnectionPairs.cs (100%) rename src/Servers/GameTrafficRelay/src/{Entity => Aggregate}/NetworkUtils.cs (100%) rename src/Servers/GameTrafficRelay/src/{Entity => Aggregate}/RelayServerInfo.cs (100%) rename src/Servers/GameTrafficRelay/src/{Entity => Aggregate}/ServerStatusReporter.cs (100%) rename src/Servers/WebServer/src/{Entity/Structure => Aggregate}/SakeArrayObject.cs (93%) rename src/Servers/WebServer/src/{Entity/Structure => Aggregate}/SoapXElement.cs (98%) rename src/Servers/WebServer/src/{Entity/Constant => Aggregate}/WebEndpoints.cs (95%) rename src/{Libraries/Core/src/Exception/UniSpyException.cs => Servers/WebServer/src/Exception/Exception.cs} (65%) mode change 100755 => 100644 create mode 100644 src/Servers/WebServer/test/GeneralTest.cs rename src/Servers/WebServer/test/{TestClasses.cs => MokeObject.cs} (100%) diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/CmdHandlerBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/CmdHandlerBase.cs index a26e10bd7..f914831ea 100755 --- a/src/Libraries/Core/src/Abstraction/BaseClass/CmdHandlerBase.cs +++ b/src/Libraries/Core/src/Abstraction/BaseClass/CmdHandlerBase.cs @@ -35,7 +35,7 @@ public virtual void Handle() } Response(); } - catch (UniSpy.Exception ex) + catch (System.Exception ex) { HandleException(ex); } @@ -58,18 +58,7 @@ protected virtual void Response() _client.Send(_response); } - protected virtual void HandleException(System.Exception ex) - { - // we only log exception message when this message is UniSpy.Exception - if (ex is UniSpy.Exception) - { - _client.LogError(ex.Message); - } - else - { - _client.LogError(ex.ToString()); - } - } + protected virtual void HandleException(System.Exception ex) => UniSpy.Exception.HandleException(ex, _client); private void LogCurrentClass() { diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/CmdSwitcherBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/CmdSwitcherBase.cs index 2b4b88104..83e9fe121 100755 --- a/src/Libraries/Core/src/Abstraction/BaseClass/CmdSwitcherBase.cs +++ b/src/Libraries/Core/src/Abstraction/BaseClass/CmdSwitcherBase.cs @@ -52,9 +52,9 @@ public virtual void Handle() handler.Handle(); } } - catch (UniSpy.Exception e) + catch (System.Exception e) { - _client.LogError(e.Message); + UniSpy.Exception.HandleException(e, _client); } } /// diff --git a/src/Libraries/Core/src/Exception/Exception.cs b/src/Libraries/Core/src/Exception/Exception.cs new file mode 100755 index 000000000..d72994870 --- /dev/null +++ b/src/Libraries/Core/src/Exception/Exception.cs @@ -0,0 +1,47 @@ +using UniSpy.Server.Core.Abstraction.Interface; +using UniSpy.Server.Core.Logging; + +namespace UniSpy +{ + public class Exception : System.Exception + { + public Exception() + { + } + + public Exception(string message) : base(message) + { + } + + public Exception(string message, System.Exception innerException) : base(message, innerException) + { + } + public static void HandleException(System.Exception ex, IClient client = null) + { + // we only log exception message when this message is UniSpy.Exception + if (ex is UniSpy.Exception) + { + if (client is null) + { + LogWriter.LogError(ex.Message); + } + else + { + client.LogError(ex.Message); + } + } + else + { + if (client is null) + { + LogWriter.LogError(ex.ToString()); + } + else + { + client.LogError(ex.ToString()); + } + } + } + } + +} diff --git a/src/Libraries/Core/src/Misc/GameSpyUtils.cs b/src/Libraries/Core/src/Misc/GameSpyUtils.cs index 0e12b725b..b5a48e188 100755 --- a/src/Libraries/Core/src/Misc/GameSpyUtils.cs +++ b/src/Libraries/Core/src/Misc/GameSpyUtils.cs @@ -95,14 +95,8 @@ string DomainMapper(Match match) return match.Groups[1].Value + domainName; } } - catch (RegexMatchTimeoutException e) + catch { - LogWriter.LogError(e); - return false; - } - catch (ArgumentException e) - { - LogWriter.LogError(e); return false; } diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs index d5fbff554..3e5562397 100644 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs +++ b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs @@ -3,6 +3,7 @@ using System.Linq; using UniSpy.Server.Chat.Abstraction.Interface; using UniSpy.Server.Chat.Contract.Request.Channel; +using UniSpy.Server.Chat.Error.IRC.General; using UniSpy.Server.Core.Abstraction.Interface; using UniSpy.Server.Core.Logging; using UniSpy.Server.QueryReport.Aggregate.Redis.Channel; @@ -65,7 +66,7 @@ private void UnBanUser(ModeRequest request) } if (result.Count() > 1) { - LogWriter.LogError($"Multiple user with same nick name in channel {Name}"); + throw new ErrOneUSNickNameException("Multiple user with same nick name in channel {Name}"); } } diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelValidation.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelValidation.cs index 5b8cbdbd2..8b2cba74b 100644 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelValidation.cs +++ b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelValidation.cs @@ -18,7 +18,7 @@ private void Validation(IChatClient client, string password) if (Mode.IsInviteOnly) { //invited only - throw new IRCChannelException("This is an invited only channel.", IRCErrorCode.InviteOnlyChan, Name); + throw new InviteOnlyChanException("This is an invited only channel.", Name); } if (IsUserBanned(client)) { diff --git a/src/Servers/Chat/src/Application/Program.cs b/src/Servers/Chat/src/Application/Program.cs index 78112d0cd..83b4e9981 100755 --- a/src/Servers/Chat/src/Application/Program.cs +++ b/src/Servers/Chat/src/Application/Program.cs @@ -15,7 +15,7 @@ static void Main(string[] args) } catch (System.Exception e) { - LogWriter.LogError(e); + UniSpy.Exception.HandleException(e); } } } diff --git a/src/Servers/GameStatus/src/Application/Program.cs b/src/Servers/GameStatus/src/Application/Program.cs index 332f62cb1..fdef42c5c 100755 --- a/src/Servers/GameStatus/src/Application/Program.cs +++ b/src/Servers/GameStatus/src/Application/Program.cs @@ -15,7 +15,7 @@ static void Main(string[] args) } catch (System.Exception e) { - LogWriter.LogError(e); + UniSpy.Exception.HandleException(e); } } } diff --git a/src/Servers/GameStatus/src/Exception/GSException.cs b/src/Servers/GameStatus/src/Exception/Exception.cs similarity index 100% rename from src/Servers/GameStatus/src/Exception/GSException.cs rename to src/Servers/GameStatus/src/Exception/Exception.cs diff --git a/src/Servers/GameTrafficRelay/src/Entity/ConnectionForwardClient.cs b/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionForwardClient.cs similarity index 100% rename from src/Servers/GameTrafficRelay/src/Entity/ConnectionForwardClient.cs rename to src/Servers/GameTrafficRelay/src/Aggregate/ConnectionForwardClient.cs diff --git a/src/Servers/GameTrafficRelay/src/Entity/ConnectionListener.cs b/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionListener.cs similarity index 100% rename from src/Servers/GameTrafficRelay/src/Entity/ConnectionListener.cs rename to src/Servers/GameTrafficRelay/src/Aggregate/ConnectionListener.cs diff --git a/src/Servers/GameTrafficRelay/src/Entity/ConnectionPairs.cs b/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionPairs.cs similarity index 100% rename from src/Servers/GameTrafficRelay/src/Entity/ConnectionPairs.cs rename to src/Servers/GameTrafficRelay/src/Aggregate/ConnectionPairs.cs diff --git a/src/Servers/GameTrafficRelay/src/Entity/NetworkUtils.cs b/src/Servers/GameTrafficRelay/src/Aggregate/NetworkUtils.cs similarity index 100% rename from src/Servers/GameTrafficRelay/src/Entity/NetworkUtils.cs rename to src/Servers/GameTrafficRelay/src/Aggregate/NetworkUtils.cs diff --git a/src/Servers/GameTrafficRelay/src/Entity/RelayServerInfo.cs b/src/Servers/GameTrafficRelay/src/Aggregate/RelayServerInfo.cs similarity index 100% rename from src/Servers/GameTrafficRelay/src/Entity/RelayServerInfo.cs rename to src/Servers/GameTrafficRelay/src/Aggregate/RelayServerInfo.cs diff --git a/src/Servers/GameTrafficRelay/src/Entity/ServerStatusReporter.cs b/src/Servers/GameTrafficRelay/src/Aggregate/ServerStatusReporter.cs similarity index 100% rename from src/Servers/GameTrafficRelay/src/Entity/ServerStatusReporter.cs rename to src/Servers/GameTrafficRelay/src/Aggregate/ServerStatusReporter.cs diff --git a/src/Servers/GameTrafficRelay/src/Application/Program.cs b/src/Servers/GameTrafficRelay/src/Application/Program.cs index 98a92506a..48777da28 100755 --- a/src/Servers/GameTrafficRelay/src/Application/Program.cs +++ b/src/Servers/GameTrafficRelay/src/Application/Program.cs @@ -15,7 +15,7 @@ static void Main(string[] args) } catch (System.Exception e) { - LogWriter.LogError(e); + UniSpy.Exception.HandleException(e); } } } diff --git a/src/Servers/NatNegotiation/src/Application/Program.cs b/src/Servers/NatNegotiation/src/Application/Program.cs index ec22fc835..68606e927 100755 --- a/src/Servers/NatNegotiation/src/Application/Program.cs +++ b/src/Servers/NatNegotiation/src/Application/Program.cs @@ -15,7 +15,7 @@ static void Main(string[] args) } catch (System.Exception e) { - LogWriter.LogError(e); + UniSpy.Exception.HandleException(e); } } } diff --git a/src/Servers/PresenceConnectionManager/src/Application/Program.cs b/src/Servers/PresenceConnectionManager/src/Application/Program.cs index 93599634b..d53ea5128 100755 --- a/src/Servers/PresenceConnectionManager/src/Application/Program.cs +++ b/src/Servers/PresenceConnectionManager/src/Application/Program.cs @@ -15,7 +15,7 @@ static void Main(string[] args) } catch (System.Exception e) { - LogWriter.LogError(e); + UniSpy.Exception.HandleException(e); } } } diff --git a/src/Servers/PresenceSearchPlayer/src/Application/Program.cs b/src/Servers/PresenceSearchPlayer/src/Application/Program.cs index 302b8abb6..afc20913d 100755 --- a/src/Servers/PresenceSearchPlayer/src/Application/Program.cs +++ b/src/Servers/PresenceSearchPlayer/src/Application/Program.cs @@ -15,7 +15,7 @@ static void Main(string[] args) } catch (System.Exception e) { - LogWriter.LogError(e); + UniSpy.Exception.HandleException(e); } } } diff --git a/src/Servers/QueryReport/src/Application/Program.cs b/src/Servers/QueryReport/src/Application/Program.cs index 961d065a4..d24213b25 100755 --- a/src/Servers/QueryReport/src/Application/Program.cs +++ b/src/Servers/QueryReport/src/Application/Program.cs @@ -15,7 +15,7 @@ static void Main(string[] args) } catch (System.Exception e) { - LogWriter.LogError(e); + UniSpy.Exception.HandleException(e); } } } diff --git a/src/Servers/ServerBrowser/src/Application/Program.cs b/src/Servers/ServerBrowser/src/Application/Program.cs index 88ddc0a8b..5b0283a30 100755 --- a/src/Servers/ServerBrowser/src/Application/Program.cs +++ b/src/Servers/ServerBrowser/src/Application/Program.cs @@ -15,9 +15,8 @@ static void Main(string[] args) } catch (System.Exception e) { - LogWriter.LogError(e); + UniSpy.Exception.HandleException(e); } - } } } diff --git a/src/Servers/WebServer/src/Entity/Structure/SakeArrayObject.cs b/src/Servers/WebServer/src/Aggregate/SakeArrayObject.cs similarity index 93% rename from src/Servers/WebServer/src/Entity/Structure/SakeArrayObject.cs rename to src/Servers/WebServer/src/Aggregate/SakeArrayObject.cs index 6489385f7..e0cb000a1 100644 --- a/src/Servers/WebServer/src/Entity/Structure/SakeArrayObject.cs +++ b/src/Servers/WebServer/src/Aggregate/SakeArrayObject.cs @@ -1,4 +1,4 @@ -namespace UniSpy.Server.WebServer.Contract +namespace UniSpy.Server.WebServer.Aggregate { public record RecordFieldObject : FieldObject { diff --git a/src/Servers/WebServer/src/Entity/Structure/SoapXElement.cs b/src/Servers/WebServer/src/Aggregate/SoapXElement.cs similarity index 98% rename from src/Servers/WebServer/src/Entity/Structure/SoapXElement.cs rename to src/Servers/WebServer/src/Aggregate/SoapXElement.cs index 1976d6b2e..80edd0dc8 100644 --- a/src/Servers/WebServer/src/Entity/Structure/SoapXElement.cs +++ b/src/Servers/WebServer/src/Aggregate/SoapXElement.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Xml.Linq; -namespace UniSpy.Server.WebServer.Contract +namespace UniSpy.Server.WebServer.Aggregate { public class SoapXElement : XElement { diff --git a/src/Servers/WebServer/src/Entity/Constant/WebEndpoints.cs b/src/Servers/WebServer/src/Aggregate/WebEndpoints.cs similarity index 95% rename from src/Servers/WebServer/src/Entity/Constant/WebEndpoints.cs rename to src/Servers/WebServer/src/Aggregate/WebEndpoints.cs index 393a9236a..ac30593cd 100644 --- a/src/Servers/WebServer/src/Entity/Constant/WebEndpoints.cs +++ b/src/Servers/WebServer/src/Aggregate/WebEndpoints.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; -namespace UniSpy.Server.WebServer.Entity.Constant +namespace UniSpy.Server.WebServer.Aggregate { public class WebEndpoints { diff --git a/src/Servers/WebServer/src/Application/Client.cs b/src/Servers/WebServer/src/Application/Client.cs index ff117b990..986e79cdc 100644 --- a/src/Servers/WebServer/src/Application/Client.cs +++ b/src/Servers/WebServer/src/Application/Client.cs @@ -1,6 +1,7 @@ using UniSpy.Server.WebServer.Handler; using UniSpy.Server.Core.Abstraction.BaseClass; using UniSpy.Server.Core.Abstraction.Interface; +using UniSpy.Server.Core.Logging; namespace UniSpy.Server.WebServer.Application { @@ -15,10 +16,16 @@ public Client(IConnection connection, IServer server) : base(connection, server) protected override void OnReceived(object buffer) { - base.OnReceived(buffer); var rq = (IHttpRequest)buffer; + + if (rq.Body.Length == 0 || rq.Body == "") + { + this.LogWarn($"ignore empty message"); + return; + } + base.OnReceived(buffer); if (!rq.KeepAlive) - ((IHttpConnection)Connection).Disconnect(); + (Connection as IHttpConnection)?.Disconnect(); } } } \ No newline at end of file diff --git a/src/Servers/WebServer/src/Application/Program.cs b/src/Servers/WebServer/src/Application/Program.cs index 0332e7a1c..809fbb84f 100755 --- a/src/Servers/WebServer/src/Application/Program.cs +++ b/src/Servers/WebServer/src/Application/Program.cs @@ -15,7 +15,7 @@ static void Main(string[] args) } catch (System.Exception e) { - LogWriter.LogError(e); + UniSpy.Exception.HandleException(e); } } } diff --git a/src/Libraries/Core/src/Exception/UniSpyException.cs b/src/Servers/WebServer/src/Exception/Exception.cs old mode 100755 new mode 100644 similarity index 65% rename from src/Libraries/Core/src/Exception/UniSpyException.cs rename to src/Servers/WebServer/src/Exception/Exception.cs index a85cb700e..5e481c50a --- a/src/Libraries/Core/src/Exception/UniSpyException.cs +++ b/src/Servers/WebServer/src/Exception/Exception.cs @@ -1,11 +1,7 @@ -namespace UniSpy +namespace UniSpy.Server.WebServer { - public class Exception : System.Exception + public class Exception : UniSpy.Exception { - public Exception() - { - } - public Exception(string message) : base(message) { } @@ -14,5 +10,4 @@ public Exception(string message, System.Exception innerException) : base(message { } } - -} +} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Handler/CmdSwitcher.cs b/src/Servers/WebServer/src/Handler/CmdSwitcher.cs index 5b0196db2..52f35f99b 100755 --- a/src/Servers/WebServer/src/Handler/CmdSwitcher.cs +++ b/src/Servers/WebServer/src/Handler/CmdSwitcher.cs @@ -29,13 +29,16 @@ protected override void ProcessRawRequest() // { // throw new UniSpy.Exception($"Invalid http path access:{_rawRequest.Url}"); // } - if (_rawRequest.Body == "") + try { - return; + dynamic xelements = XElement.Parse(_rawRequest.Body); + var name = xelements.FirstNode.FirstNode.Name.LocalName; + _requests.Add(new KeyValuePair(name, _rawRequest.Body)); + } + catch (System.Exception ex) + { + throw new WebServer.Exception("xml parsing error", ex); } - dynamic xelements = XElement.Parse(_rawRequest.Body); - var name = xelements.FirstNode.FirstNode.Name.LocalName; - _requests.Add(new KeyValuePair(name, _rawRequest.Body)); } protected override IHandler CreateCmdHandlers(object name, object rawRequest) diff --git a/src/Servers/WebServer/src/Module/Sake/Abstraction/CmdHandlerBase.cs b/src/Servers/WebServer/src/Module/Sake/Abstraction/CmdHandlerBase.cs index 3d9ef2a14..967fea850 100644 --- a/src/Servers/WebServer/src/Module/Sake/Abstraction/CmdHandlerBase.cs +++ b/src/Servers/WebServer/src/Module/Sake/Abstraction/CmdHandlerBase.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.IO; using Newtonsoft.Json; -using UniSpy.Server.WebServer.Contract; +using UniSpy.Server.WebServer.Aggregate; using UniSpy.Server.Core.Abstraction.Interface; namespace UniSpy.Server.WebServer.Module.Sake.Abstraction diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/CreateRecordRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/CreateRecordRequest.cs index 0c7fafb3b..4b5391260 100644 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/CreateRecordRequest.cs +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Request/CreateRecordRequest.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Xml.Linq; -using UniSpy.Server.WebServer.Contract; +using UniSpy.Server.WebServer.Aggregate; using UniSpy.Server.WebServer.Module.Sake.Abstraction; namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetMyRecordsRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetMyRecordsRequest.cs index d4b73e907..0b2cb7b99 100644 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetMyRecordsRequest.cs +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetMyRecordsRequest.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Xml.Linq; -using UniSpy.Server.WebServer.Contract; +using UniSpy.Server.WebServer.Aggregate; using UniSpy.Server.WebServer.Module.Sake.Abstraction; namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetRandomRecordsRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetRandomRecordsRequest.cs index a8d2f8d34..13da34b8e 100644 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetRandomRecordsRequest.cs +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetRandomRecordsRequest.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Xml.Linq; -using UniSpy.Server.WebServer.Contract; +using UniSpy.Server.WebServer.Aggregate; using UniSpy.Server.WebServer.Module.Sake.Abstraction; namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetSpecificRecordsRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetSpecificRecordsRequest.cs index 1af9cfa02..22114dbae 100644 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetSpecificRecordsRequest.cs +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetSpecificRecordsRequest.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Xml.Linq; -using UniSpy.Server.WebServer.Contract; +using UniSpy.Server.WebServer.Aggregate; using UniSpy.Server.WebServer.Module.Sake.Abstraction; namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/SearchForRecordsRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/SearchForRecordsRequest.cs index 668bbb39c..5746e84eb 100644 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/SearchForRecordsRequest.cs +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Request/SearchForRecordsRequest.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Xml.Linq; -using UniSpy.Server.WebServer.Contract; +using UniSpy.Server.WebServer.Aggregate; using UniSpy.Server.WebServer.Module.Sake.Abstraction; namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/UpdateRecordRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/UpdateRecordRequest.cs index c01daba4b..362801bff 100644 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/UpdateRecordRequest.cs +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Request/UpdateRecordRequest.cs @@ -1,7 +1,7 @@ using System.Collections.Generic; using System.Linq; using System.Xml.Linq; -using UniSpy.Server.WebServer.Contract; +using UniSpy.Server.WebServer.Aggregate; using UniSpy.Server.WebServer.Module.Sake.Abstraction; namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Result/GetMyRecordsResult.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Result/GetMyRecordsResult.cs index 5d04d883a..aa5933ea0 100644 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Result/GetMyRecordsResult.cs +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Result/GetMyRecordsResult.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Contract; +using UniSpy.Server.WebServer.Aggregate; namespace UniSpy.Server.WebServer.Module.Sake.Contract.Result { diff --git a/src/Servers/WebServer/test/GeneralTest.cs b/src/Servers/WebServer/test/GeneralTest.cs new file mode 100644 index 000000000..eab796851 --- /dev/null +++ b/src/Servers/WebServer/test/GeneralTest.cs @@ -0,0 +1,21 @@ +using Moq; +using UniSpy.Server.Core.Abstraction.Interface; +using UniSpy.Server.WebServer.Handler; +using Xunit; + +namespace UniSpy.Server.WebServer.Test +{ + public class GeneralTest + { + [Fact] + public void InlegalMessageTest() + { + var client = MokeObject.CreateClient(); + var req = new Mock(); + req.Setup(s => s.Body).Returns("username=admin&psd=Feefifofum"); + req.Setup(s => s.Url).Returns("abcdefg"); + var switcher = new CmdSwitcher(client, req.Object); + switcher.Handle(); + } + } +} \ No newline at end of file diff --git a/src/Servers/WebServer/test/TestClasses.cs b/src/Servers/WebServer/test/MokeObject.cs similarity index 100% rename from src/Servers/WebServer/test/TestClasses.cs rename to src/Servers/WebServer/test/MokeObject.cs From 3e919f133c3b5ebc819379d998aca7475e131c8c Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 17 Jun 2023 20:25:50 +0800 Subject: [PATCH 012/231] refactor(lib): change async send to sync send to avoid wrong sequence --- .../Core/src/Network/Http/Server/HttpConnection.cs | 4 ++-- src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs | 2 +- .../Core/src/Network/Udp/Client/UniSpyUdpClient.cs | 6 +----- src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs | 6 ++++-- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs b/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs index 73ff585ab..90d760a83 100644 --- a/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs +++ b/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs @@ -32,7 +32,7 @@ void IConnection.Send(string response) // Response.MakeOkResponse(); Response.SetBegin(200); Response.SetBody(response); - base.SendResponseAsync(); + base.SendResponse(); } void IConnection.Send(byte[] response) @@ -40,7 +40,7 @@ void IConnection.Send(byte[] response) // Response.MakeOkResponse(); Response.SetBegin(200); Response.SetBody(response); - base.SendResponseAsync(); + base.SendResponse(); } void ITcpConnection.Disconnect() => Disconnect(); diff --git a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs index 64470d66d..b937c9807 100644 --- a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs +++ b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs @@ -52,7 +52,7 @@ protected override void OnReceived(byte[] buffer, long offset, long size) } void ITcpConnection.Disconnect() => Disconnect(); public new void Send(string response) => Send(UniSpyEncoding.GetBytes(response)); - public new void Send(byte[] response) => base.SendAsync(response); + public new void Send(byte[] response) => base.Send(response); } } diff --git a/src/Libraries/Core/src/Network/Udp/Client/UniSpyUdpClient.cs b/src/Libraries/Core/src/Network/Udp/Client/UniSpyUdpClient.cs index 4db56d320..4dde15139 100644 --- a/src/Libraries/Core/src/Network/Udp/Client/UniSpyUdpClient.cs +++ b/src/Libraries/Core/src/Network/Udp/Client/UniSpyUdpClient.cs @@ -24,11 +24,7 @@ protected override void OnDisconnected() base.OnConnected(); } - protected override void OnReceived(EndPoint endpoint, byte[] buffer, long offset, long size) - { - // Continue receive datagrams - ReceiveAsync(); - } + protected override void OnReceived(EndPoint endpoint, byte[] buffer, long offset, long size) => ReceiveAsync(); protected override void OnError(SocketError error) { diff --git a/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs b/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs index 6efa2b967..bcb026609 100644 --- a/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs +++ b/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs @@ -33,11 +33,13 @@ public bool Send(object response) { if (response.GetType() == typeof(string)) { - return Manager.SendAsync(RemoteIPEndPoint, UniSpyEncoding.GetBytes((string)response)); + Manager.Send(RemoteIPEndPoint, UniSpyEncoding.GetBytes((string)response)); + return true; } else if (response.GetType() == typeof(byte[])) { - return Manager.SendAsync(RemoteIPEndPoint, (byte[])response); + Manager.Send(RemoteIPEndPoint, (byte[])response); + return true; } else { From 5efc6c0e278016dd3d305e2aa5a6088268212138 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sun, 18 Jun 2023 11:06:12 +0800 Subject: [PATCH 013/231] fix(chat): nick * request handle logic --- .../BaseClass/Channel/ChannelHandlerBase.cs | 9 --- .../Abstraction/BaseClass/CmdHandlerBase.cs | 7 -- src/Servers/Chat/src/Aggregate/ChannelMode.cs | 2 +- .../ChannelProperty/ChannelUserRelated.cs | 1 - .../Chat/src/Application/ClientInfo.cs | 10 +-- .../Contract/Response/General/NickResponse.cs | 6 +- .../src/Contract/Result/General/NickResult.cs | 12 +++ src/Servers/Chat/src/Exception/Exception.cs | 5 -- .../Handler/CmdHandler/Channel/JoinHandler.cs | 9 --- .../CmdHandler/Channel/SetCKeyHandler.cs | 15 +--- .../CmdHandler/General/LoginHandler.cs | 4 + .../Handler/CmdHandler/General/NickHandler.cs | 75 ++++++++++++------- 12 files changed, 70 insertions(+), 85 deletions(-) create mode 100644 src/Servers/Chat/src/Contract/Result/General/NickResult.cs diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs index ca0c2364c..689e06c7f 100755 --- a/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs +++ b/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs @@ -21,15 +21,6 @@ public ChannelHandlerBase(IChatClient client, IRequest request) : base(client, r protected override void RequestCheck() { - // we handle join when get user nickname - if (!_client.Info.IsNickNameSet) - { - lock (_client.Info.HandlerStack) - { - _client.Info.HandlerStack.Add(this); - } - throw new Chat.HandleLaterException($"{this.GetType().Name} is saved until nickname is set."); - } if (_request.RawRequest is not null) { base.RequestCheck(); diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/CmdHandlerBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/CmdHandlerBase.cs index f8ad166d4..e23afa676 100755 --- a/src/Servers/Chat/src/Abstraction/BaseClass/CmdHandlerBase.cs +++ b/src/Servers/Chat/src/Abstraction/BaseClass/CmdHandlerBase.cs @@ -30,13 +30,6 @@ protected override void HandleException(System.Exception ex) { _client.Send(((IRCException)ex)); } - else if (ex is HandleLaterException) - { - // if the exception is HandleLaterException, we log it as warning - _client.LogWarn(ex.Message); - return; - } - base.HandleException(ex); } protected override void Response() diff --git a/src/Servers/Chat/src/Aggregate/ChannelMode.cs b/src/Servers/Chat/src/Aggregate/ChannelMode.cs index 3d5df82c2..7658769e2 100755 --- a/src/Servers/Chat/src/Aggregate/ChannelMode.cs +++ b/src/Servers/Chat/src/Aggregate/ChannelMode.cs @@ -24,7 +24,7 @@ public sealed class ChannelMode public bool IsAllowExternalMessage { get; private set; } = false; //t - toggle the topic settable by channel operator only flag; [JsonProperty] - public bool IsTopicOnlySetByChannelOperator { get; private set; } = true; + public bool IsTopicOnlySetByChannelOperator { get; private set; } = false; // e - toggle the operator allow channel limits flag; [JsonProperty] public bool IsOperatorAbeyChannelLimits { get; private set; } = true; diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs index 3e5562397..c50c2d6ee 100644 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs +++ b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs @@ -208,7 +208,6 @@ public static void RemoveBindOnUserAndChannel(ChannelUser leaver) /// Send message to all users in this channel /// except the sender /// - /// public void MultiCast(IClient sender, IResponse message, bool isSkipSender = false) { foreach (var user in Users.Values) diff --git a/src/Servers/Chat/src/Application/ClientInfo.cs b/src/Servers/Chat/src/Application/ClientInfo.cs index 5b9d23a6c..ede2fea9b 100755 --- a/src/Servers/Chat/src/Application/ClientInfo.cs +++ b/src/Servers/Chat/src/Application/ClientInfo.cs @@ -1,9 +1,7 @@ using System.Collections.Concurrent; -using System.Collections.Generic; using Newtonsoft.Json; using UniSpy.Server.Chat.Aggregate; using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; namespace UniSpy.Server.Chat.Application { @@ -36,13 +34,7 @@ public sealed class ClientInfo : ClientInfoBase /// /// public KeyValueManager KeyValues { get; private set; } = new KeyValueManager(); - /// - /// Store Handler here processing later. - /// Some game is using * as nickname, and nickname will send in SETCHANKEY request, so we need to store handlers and process it latter - /// - [JsonIgnore] - public List HandlerStack { get; private set; } = new List(); - public bool IsNickNameSet => NickName != "*"; + public ClientInfo() { } diff --git a/src/Servers/Chat/src/Contract/Response/General/NickResponse.cs b/src/Servers/Chat/src/Contract/Response/General/NickResponse.cs index b0acdc63d..ca5545986 100755 --- a/src/Servers/Chat/src/Contract/Response/General/NickResponse.cs +++ b/src/Servers/Chat/src/Contract/Response/General/NickResponse.cs @@ -1,16 +1,16 @@ using UniSpy.Server.Chat.Abstraction.BaseClass; using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.General; +using UniSpy.Server.Chat.Contract.Result.General; namespace UniSpy.Server.Chat.Contract.Response.General { public sealed class NickResponse : ResponseBase { - private new NickRequest _request => (NickRequest)base._request; + private new NickResult _result => (NickResult)base._result; public NickResponse(RequestBase request, ResultBase result) : base(request, result) { } public override void Build() { - SendingBuffer = $":{ServerDomain} {ResponseName.Welcome} {_request.NickName} :Welcome to UniSpy!\r\n"; + SendingBuffer = $":{ServerDomain} {ResponseName.Welcome} {_result.NickName} :Welcome to UniSpy!\r\n"; } } } diff --git a/src/Servers/Chat/src/Contract/Result/General/NickResult.cs b/src/Servers/Chat/src/Contract/Result/General/NickResult.cs new file mode 100644 index 000000000..7304bd3e3 --- /dev/null +++ b/src/Servers/Chat/src/Contract/Result/General/NickResult.cs @@ -0,0 +1,12 @@ +using UniSpy.Server.Chat.Abstraction.BaseClass; + +namespace UniSpy.Server.Chat.Contract.Result.General +{ + public sealed class NickResult : ResultBase + { + public string NickName { get; set; } + public NickResult() + { + } + } +} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/Exception.cs b/src/Servers/Chat/src/Exception/Exception.cs index e7cfc0cda..e2c093084 100755 --- a/src/Servers/Chat/src/Exception/Exception.cs +++ b/src/Servers/Chat/src/Exception/Exception.cs @@ -8,9 +8,4 @@ public Exception(string message) : base(message) { } public Exception(string message, System.Exception innerException) : base(message, innerException) { } } - - public class HandleLaterException : Exception - { - public HandleLaterException(string message) : base(message) { } - } } \ No newline at end of file diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs index 811877f6d..a09fe10c4 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs @@ -38,15 +38,6 @@ public JoinHandler(IChatClient client, JoinRequest request) : base(client, reque //发送频道用户列表给此用户 protected override void RequestCheck() { - // we handle this when get user nickname - if (!_client.Info.IsNickNameSet) - { - lock (_client.Info.HandlerStack) - { - _client.Info.HandlerStack.Add(this); - } - throw new Chat.HandleLaterException($"{this.GetType().Name} is saved until nickname is set."); - } base.RequestCheck(); //some GameSpy game only allow one player join one chat room //but GameSpy Arcade can join more than one channel diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetCKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetCKeyHandler.cs index c1b62650b..c8ea67e79 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetCKeyHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetCKeyHandler.cs @@ -24,24 +24,11 @@ public SetCKeyHandler(IChatClient client, SetCKeyRequest request) : base(client, protected override void RequestCheck() { _request.Parse(); - // if client nickname is *, we set the name and execute the stored handlers - if (!_client.Info.IsNickNameSet) - { - _client.Info.NickName = _request.NickName; - lock (_client.Info.HandlerStack) - { - foreach (var handler in _client.Info.HandlerStack) - { - handler.Handle(); - } - _client.Info.HandlerStack.Clear(); - } - } base.RequestCheck(); if (_request.NickName != _client.Info.NickName) { - if (!_user.IsChannelOperator) + if (_channel.Mode.IsTopicOnlySetByChannelOperator) { throw new Chat.Exception("SETCKEY failed because you are not channel operator."); } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/LoginHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/LoginHandler.cs index 9406a46ab..2bc594b17 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/LoginHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/LoginHandler.cs @@ -1,3 +1,4 @@ +using System.Linq; using UniSpy.Server.Chat.Abstraction.BaseClass; using UniSpy.Server.Chat.Contract.Request.General; using UniSpy.Server.Chat.Contract.Response.General; @@ -34,9 +35,12 @@ protected override void DataOperation() case LoginReqeustType.NickAndEmailLogin: // the ignored variables _ will used in future (_result.ProfileId, _result.UserID, _, _) = StorageOperation.Persistance.NickAndEmailLogin(_request.NickName, _request.Email, _request.PasswordHash); + //todo check whether NICK * will occur in this situation break; case LoginReqeustType.UniqueNickLogin: (_result.ProfileId, _result.UserID, _, _) = StorageOperation.Persistance.UniqueNickLogin(_request.UniqueNick, _request.NamespaceId); + // we set user's uniquenick + _client.Info.UniqueNickName = _request.UniqueNick; break; } } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs index b237b4ce3..b7ea82db3 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs @@ -5,6 +5,7 @@ using UniSpy.Server.Chat.Contract.Request.General; using UniSpy.Server.Chat.Contract.Response.General; using UniSpy.Server.Chat.Abstraction.Interface; +using UniSpy.Server.Chat.Contract.Result.General; namespace UniSpy.Server.Chat.Handler.CmdHandler.General { @@ -12,48 +13,68 @@ namespace UniSpy.Server.Chat.Handler.CmdHandler.General public sealed class NickHandler : CmdHandlerBase { private new NickRequest _request => (NickRequest)base._request; - public NickHandler(IChatClient client, NickRequest request) : base(client, request) { } - - protected override void RequestCheck() + private new NickResult _result { get => (NickResult)base._result; set => base._result = value; } + public NickHandler(IChatClient client, NickRequest request) : base(client, request) { - base.RequestCheck(); - int number = 0; - string validNickName; - var clientInfos = ClientManager.GetAllClientInfo(); + _result = new NickResult(); + } - if (_request.NickName == "*") + private void SetUniqueNickAsNickName() + { + if (_client.Info.UniqueNickName is null) { - //client is only authenticating with chat, we just sign a valid name to it. - _client.Info.NickName = System.Guid.NewGuid().ToString(); - return; + throw new Chat.Exception("uniquenick is not set."); } - - if (ClientManager.GetAllClientInfo().Where(i => i.NickName == _request.NickName).Count() == 0) + if (_client.Info.UniqueNickName == "") { - _client.Info.NickName = _request.NickName; + throw new Chat.Exception("uniquenick can not be empty string"); + } + var postFix = ""; + if (_client.Info.GameName.Length > 2) + { + postFix = _client.Info.GameName.Substring(0, 3); } else { - while (true) + postFix = _client.Info.GameName.Substring(0, 2); + } + _client.Info.NickName = $"{_client.Info.UniqueNickName}-{postFix}"; + } + private void SuggestNewNickName() + { + int number = 0; + string validNickName; + while (true) + { + string newNickName = _request.NickName + number; + if (ClientManager.ClientPool.Values.Count(c => c.Info.NickName == _request.NickName) == 0) { - string newNickName = _request.NickName + number; - if (ClientManager.GetAllClientInfo().Where(i => i.NickName == _request.NickName).Count() == 0) - { - validNickName = newNickName; - break; - } + validNickName = newNickName; + break; } - throw new NickNameInUseException( - $"The nick name: {_request.NickName} is already in use", - _request.NickName, - validNickName); } + throw new NickNameInUseException( + $"The nick name: {_request.NickName} is already in use", + _request.NickName, + validNickName); } - protected override void DataOperation() { - _client.Info.NickName = _request.NickName; + if (_request.NickName == "*") + { + //client using its - as his nickname in chat + SetUniqueNickAsNickName(); + } + else if (ClientManager.ClientPool.Values.Count(c => c.Info.NickName == _request.NickName) == 0) + { + _client.Info.NickName = _request.NickName; + } + else + { + SuggestNewNickName(); + } + _result.NickName = _client.Info.NickName; } protected override void ResponseConstruct() { From 6eb7ad1cbe2d5a5fa33881030871ae776ccef471 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sun, 18 Jun 2023 11:29:36 +0800 Subject: [PATCH 014/231] refactor(lib): server config --- common/UniSpyServerConfig.json | 18 ++++++++-- .../BaseClass/ServerLauncherBase.cs | 19 +++++----- src/Libraries/Core/src/Config/UniSpyConfig.cs | 36 +++++++++++++++++-- 3 files changed, 58 insertions(+), 15 deletions(-) diff --git a/common/UniSpyServerConfig.json b/common/UniSpyServerConfig.json index 78926cc43..75e04d2fc 100644 --- a/common/UniSpyServerConfig.json +++ b/common/UniSpyServerConfig.json @@ -1,9 +1,23 @@ { "Database": { - "ConnectionString": "reference to https://www.connectionstrings.com/postgresql/ example: User ID=root;Password=myPassword;Host=localhost;Port=5432;Database=myDataBase;Pooling=true;Min Pool Size=0;Max Pool Size=100;Connection Lifetime=0;" + "Server": "127.0.0.1", + "Port": "5432", + "Database": "unispy", + "Username": "root", + "Password": "0000", + "SSLMode": "Prefer", + "TrustServerCert": "false", + "SSLKey": "", + "SSLPassword": "", + "RootCert": "" }, "Redis": { - "ConnectionString": "reference to https://stackexchange.github.io/StackExchange.Redis/Configuration.html example: localhost:6379,user=,password=,ssl=,sslHost=" + "Server": "127.0.0.1", + "Port": "6379", + "User": "", + "Password": "", + "SSL": "false", + "SSLHost": "" }, "Servers": [ { diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ServerLauncherBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ServerLauncherBase.cs index db535dde8..1338eb1ad 100755 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ServerLauncherBase.cs +++ b/src/Libraries/Core/src/Abstraction/BaseClass/ServerLauncherBase.cs @@ -17,9 +17,7 @@ public abstract class ServerLauncherBase /// UniSpy server version /// public static readonly string Version = "0.8.1"; - /// - /// The server instances which contains the server objects - /// + public static List ServerInstances { get; protected set; } = new List(); public ServerLauncherBase() { @@ -53,26 +51,27 @@ protected virtual void LaunchServer() protected void ConnectRedis() { + var redisConfig = ConfigManager.Config.Redis; try { - // auto dispose - using (StackExchange.Redis.ConnectionMultiplexer.Connect(ConfigManager.Config.Redis.ConnectionString)) { } + using var r = StackExchange.Redis.ConnectionMultiplexer.Connect(redisConfig.ConnectionString); } catch (System.Exception e) { - throw new Exception("Can not connect to Redis", e); + throw new UniSpy.Exception("Can not connect to Redis", e); } - Console.WriteLine($"Successfully connected to Redis"); + Console.WriteLine($"Successfully connected to Redis at {redisConfig.Server}:{redisConfig.Port}"); } protected void ConnectMySql() { //Determine which database is used and establish the database connection. + var dbConfig = ConfigManager.Config.Database; if (!new UniSpyContext().Database.CanConnect()) { - throw new Exception($"Can not connect to Postgresql."); + throw new Exception($"Can not connect to {dbConfig.Type}!"); } - Console.WriteLine($"Successfully connected to Postgresql."); + Console.WriteLine($"Successfully connected to {dbConfig.Type} at {dbConfig.Server}:{dbConfig.Port}"); } protected static void ShowUniSpyLogo() { @@ -81,4 +80,4 @@ protected static void ShowUniSpyLogo() Console.WriteLine(@"Version: " + Version); } } -} +} \ No newline at end of file diff --git a/src/Libraries/Core/src/Config/UniSpyConfig.cs b/src/Libraries/Core/src/Config/UniSpyConfig.cs index 30d376c8f..4f0a56ffc 100755 --- a/src/Libraries/Core/src/Config/UniSpyConfig.cs +++ b/src/Libraries/Core/src/Config/UniSpyConfig.cs @@ -4,6 +4,7 @@ using Newtonsoft.Json; using Serilog.Events; using StackExchange.Redis; +using UniSpy.Server.Core.Database; namespace UniSpy.Server.Core.Config { @@ -16,11 +17,40 @@ public class UniSpyConfig } public class UniSpyDatabaseConfig { - public string ConnectionString; + public string ConnectionString => + $"Server={Server};" + + $"Port={Port};" + + $"Database={Database};" + + $"Username={Username};" + + $"Password={Password};" + + $"SSL Mode={SSLMode};" + + $"Trust Server Certificate={TrustServerCert};" + + $"SSL Certificate={SSLCert};" + + $"SSL Key={SSLKey};" + + $"SSL Password={SSLPassword};" + + $"Root Certificate={RootCert};"; + public DatabaseType Type; + public string Server; + public int Port; + public string Database; + public string Username; + public string Password; + public string SSLMode; + public bool TrustServerCert; + public string SSLCert; + public string SSLKey; + public string SSLPassword; + public string RootCert; } public class UniSpyRedisConfig { - public string ConnectionString; + public string ConnectionString => $"{Server}:{Port},user={User},password={Password},ssl={SSL},sslHost={SSLHost}"; + public string Server; + public int Port; + public string User; + public string Password; + public bool SSL; + public string SSLHost; [JsonIgnore] public IConnectionMultiplexer RedisConnection => ConnectionMultiplexer.Connect(ConnectionString); } @@ -33,4 +63,4 @@ public class UniSpyServerConfig public string PublicAddress; public int ListeningPort; } -} +} \ No newline at end of file From 6d3e41f205a6c2f37e4e997ce7dd850a3e2d61a6 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sun, 18 Jun 2023 11:47:04 +0800 Subject: [PATCH 015/231] refactor(gtr):change exception type --- src/Servers/GameTrafficRelay/src/Application/Program.cs | 1 - src/Servers/GameTrafficRelay/src/Application/ServerLauncher.cs | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Servers/GameTrafficRelay/src/Application/Program.cs b/src/Servers/GameTrafficRelay/src/Application/Program.cs index 48777da28..f65a8028d 100755 --- a/src/Servers/GameTrafficRelay/src/Application/Program.cs +++ b/src/Servers/GameTrafficRelay/src/Application/Program.cs @@ -1,5 +1,4 @@ using System; -using UniSpy.Server.Core.Logging; namespace UniSpy.Server.GameTrafficRelay.Application { diff --git a/src/Servers/GameTrafficRelay/src/Application/ServerLauncher.cs b/src/Servers/GameTrafficRelay/src/Application/ServerLauncher.cs index a845da67f..e06b68f5d 100644 --- a/src/Servers/GameTrafficRelay/src/Application/ServerLauncher.cs +++ b/src/Servers/GameTrafficRelay/src/Application/ServerLauncher.cs @@ -14,7 +14,7 @@ protected override List LaunchNetworkService() var server = new Server(); if (server.PublicIPEndPoint.Address.Equals(IPAddress.Any) || server.PublicIPEndPoint.Address.Equals(IPAddress.Loopback)) { - throw new System.Exception("Game traffic relay server public address can not set to 0.0.0.0 or 127.0.0.1 !"); + throw new UniSpy.Exception("Game traffic relay server public address can not set to 0.0.0.0 or 127.0.0.1 !"); } return new List { server }; } From 87d71b2b77c9f847e25fc6fcc76584ad70da7e5a Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sun, 18 Jun 2023 13:28:54 +0800 Subject: [PATCH 016/231] fix(lib) client manager wrong reference --- .../src/Abstraction/BaseClass/ClientBase.cs | 8 +++---- .../BaseClass/ClientManagerBase.cs | 18 +++++++-------- .../src/Abstraction/BaseClass/ServerBase.cs | 4 ++-- src/Servers/Chat/src/Application/Client.cs | 6 ++++- .../Chat/src/Application/ClientManager.cs | 7 +++--- src/Servers/Chat/src/Application/Program.cs | 4 ++++ .../Response/General/WhoIsResponse.cs | 4 ++-- .../Handler/CmdHandler/General/NickHandler.cs | 4 ++-- .../Handler/CmdHandler/General/WhoHandler.cs | 4 +++- .../CmdHandler/General/WhoIsHandler.cs | 2 +- .../CmdHandler/Message/PrivateHandler.cs | 11 +++++----- .../GameStatus/src/Application/Program.cs | 4 ++++ .../src/Application/Program.cs | 4 ++++ .../NatNegotiation/src/Application/Program.cs | 4 ++++ .../src/Application/ClientManager.cs | 4 ++-- .../src/Application/Program.cs | 4 ++++ .../src/Application/Program.cs | 4 ++++ .../QueryReport/src/Application/Program.cs | 4 ++++ .../src/V2/Aggregate/Redis/NatNegChannel.cs | 2 +- .../ServerBrowser/src/Application/Program.cs | 4 ++++ .../src/V2/Application/ClientManager.cs | 2 +- .../ServerList/ServerListHandler.cs | 22 +++++++++++++------ src/Servers/ServerBrowser/test/V2/GameTest.cs | 8 +++++++ .../WebServer/src/Application/Program.cs | 4 ++++ 24 files changed, 99 insertions(+), 43 deletions(-) diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs index e322b4836..c5b663df5 100644 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs +++ b/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs @@ -27,7 +27,7 @@ public ClientBase(IConnection connection, IServer server) Connection = connection; Server = server; EventBinding(); - ClientManagerBase.AddClient(this); + // ClientManagerBase.AddClient(this); } protected virtual void EventBinding() { @@ -57,7 +57,7 @@ protected virtual void EventBinding() /// /// Only work for tcp /// - protected virtual void OnConnected() => ClientManagerBase.AddClient(this); + protected virtual void OnConnected() => ClientManagerBase.AddClient(this); /// /// Only work for tcp @@ -131,12 +131,12 @@ public void Dispose() ((ITcpConnection)Connection).OnReceive -= OnReceived; ((ITcpConnection)Connection).OnConnect -= OnConnected; ((ITcpConnection)Connection).OnDisconnect -= OnDisconnected; - ClientManagerBase.RemoveClient(this); + ClientManagerBase.RemoveClient(this); break; case NetworkConnectionType.Udp: ((IUdpConnection)Connection).OnReceive -= OnReceived; _timer.Dispose(); - ClientManagerBase.RemoveClient(this); + ClientManagerBase.RemoveClient(this); break; case NetworkConnectionType.Http: ((IHttpConnection)Connection).OnReceive -= OnReceived; diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ClientManagerBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ClientManagerBase.cs index 8bb2c8e6d..adeb65ad3 100644 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ClientManagerBase.cs +++ b/src/Libraries/Core/src/Abstraction/BaseClass/ClientManagerBase.cs @@ -9,25 +9,23 @@ namespace UniSpy.Server.Core.Abstraction.BaseClass /// if you need search client in specific server, /// please inherit this class and create static method. /// - public abstract class ClientManagerBase - where TKey : IPEndPoint - where TValue : IClient + public abstract class ClientManagerBase { - public static readonly ConcurrentDictionary ClientPool = new ConcurrentDictionary(); - public static void AddClient(TValue client) + public static readonly ConcurrentDictionary ClientPool = new ConcurrentDictionary(); + public static void AddClient(IClient client) { - ClientPool.TryAdd((TKey)client.Connection.RemoteIPEndPoint, client); + ClientPool.TryAdd(client.Connection.RemoteIPEndPoint, client); } - public static TValue RemoveClient(TValue client) + public static IClient RemoveClient(IClient client) { - return RemoveClient((TKey)client.Connection.RemoteIPEndPoint); + return RemoveClient(client.Connection.RemoteIPEndPoint); } - public static TValue RemoveClient(TKey endPoint) + public static IClient RemoveClient(IPEndPoint endPoint) { ClientPool.TryRemove(endPoint, out var client); return client; } - public static TValue GetClient(TKey endPoint) + public static IClient GetClient(IPEndPoint endPoint) { if (ClientPool.TryGetValue(endPoint, out var client)) { diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ServerBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ServerBase.cs index 75453bec5..56b5e1682 100644 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ServerBase.cs +++ b/src/Libraries/Core/src/Abstraction/BaseClass/ServerBase.cs @@ -48,11 +48,11 @@ public void SetServerInfo() protected abstract IConnectionManager CreateConnectionManager(IPEndPoint endPoint); protected virtual IClient HandleConnectionInitialization(IConnection connection) { - var client = ClientManagerBase.GetClient(connection.RemoteIPEndPoint); + var client = ClientManagerBase.GetClient(connection.RemoteIPEndPoint); if (client is null) { client = CreateClient(connection); - ClientManagerBase.AddClient(client); + ClientManagerBase.AddClient(client); } return client; } diff --git a/src/Servers/Chat/src/Application/Client.cs b/src/Servers/Chat/src/Application/Client.cs index 4c103bd4b..831704aae 100644 --- a/src/Servers/Chat/src/Application/Client.cs +++ b/src/Servers/Chat/src/Application/Client.cs @@ -22,7 +22,11 @@ public Client(IConnection connection, IServer server) : base(connection, server) { Info = new ClientInfo(); } - + protected override void OnConnected() + { + Info.IsRemoteClient = false; + base.OnConnected(); + } protected override void OnReceived(object buffer) { var message = DecryptMessage((byte[])buffer); diff --git a/src/Servers/Chat/src/Application/ClientManager.cs b/src/Servers/Chat/src/Application/ClientManager.cs index f5e23b304..08a4d98e4 100644 --- a/src/Servers/Chat/src/Application/ClientManager.cs +++ b/src/Servers/Chat/src/Application/ClientManager.cs @@ -6,21 +6,20 @@ namespace UniSpy.Server.Chat.Application { - public sealed class ClientManager : ClientManagerBase + public sealed class ClientManager : ClientManagerBase { /// /// We need to make sure client is get by nickname, otherwise we throw exception /// - /// public static IChatClient GetClientByNickName(string nickName) { IChatClient client; - client = ClientPool.Values.Where(c => c.Info.NickName == nickName).FirstOrDefault(); + client = (IChatClient)ClientPool.Values.FirstOrDefault(c => ((IChatClient)c).Info.NickName == nickName); return client; } public static List GetAllClientInfo() { - var infos = ClientPool.Values.Select(c => (c.Info)).ToList(); + var infos = ClientPool.Values.Select(c => ((ClientInfo)(c.Info))).ToList(); return infos; } } diff --git a/src/Servers/Chat/src/Application/Program.cs b/src/Servers/Chat/src/Application/Program.cs index 83b4e9981..25ca2f409 100755 --- a/src/Servers/Chat/src/Application/Program.cs +++ b/src/Servers/Chat/src/Application/Program.cs @@ -17,6 +17,10 @@ static void Main(string[] args) { UniSpy.Exception.HandleException(e); } + finally + { + while (Console.ReadKey().Key != ConsoleKey.Q) { } + } } } } diff --git a/src/Servers/Chat/src/Contract/Response/General/WhoIsResponse.cs b/src/Servers/Chat/src/Contract/Response/General/WhoIsResponse.cs index 3efb098a9..d671090fc 100755 --- a/src/Servers/Chat/src/Contract/Response/General/WhoIsResponse.cs +++ b/src/Servers/Chat/src/Contract/Response/General/WhoIsResponse.cs @@ -23,9 +23,9 @@ public override void Build() } SendingBuffer += $":{ServerDomain} {ResponseName.WhoIsChannels} {_result.NickName} {_result.Name} :{channelNames}\r\n"; - - SendingBuffer += $":{ServerDomain} {ResponseName.EndOfWhoIs} {_result.NickName} {_result.Name} :End of WHOIS list.\r\n"; } + + SendingBuffer += $":{ServerDomain} {ResponseName.EndOfWhoIs} {_result.NickName} {_result.Name} :End of WHOIS list.\r\n"; } } } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs index b7ea82db3..161b5817b 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs @@ -48,7 +48,7 @@ private void SuggestNewNickName() while (true) { string newNickName = _request.NickName + number; - if (ClientManager.ClientPool.Values.Count(c => c.Info.NickName == _request.NickName) == 0) + if (ClientManager.ClientPool.Values.Count(c => ((ClientInfo)(c.Info)).NickName == _request.NickName) == 0) { validNickName = newNickName; break; @@ -66,7 +66,7 @@ protected override void DataOperation() //client using its - as his nickname in chat SetUniqueNickAsNickName(); } - else if (ClientManager.ClientPool.Values.Count(c => c.Info.NickName == _request.NickName) == 0) + else if (ClientManager.ClientPool.Values.Count(c => ((ClientInfo)(c.Info)).NickName == _request.NickName) == 0) { _client.Info.NickName = _request.NickName; } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs index 4288ff860..dca5ba2ae 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs @@ -1,3 +1,4 @@ +using System.Net; using UniSpy.Server.Chat.Abstraction.BaseClass; using UniSpy.Server.Chat.Application; using UniSpy.Server.Chat.Aggregate.Misc; @@ -6,6 +7,8 @@ using UniSpy.Server.Chat.Contract.Result.General; using UniSpy.Server.Chat.Aggregate; using UniSpy.Server.Chat.Abstraction.Interface; +using UniSpy.Server.Core.Abstraction.BaseClass; +using UniSpy.Server.Core.Abstraction.Interface; namespace UniSpy.Server.Chat.Handler.CmdHandler.General { @@ -61,7 +64,6 @@ private void GetChannelUsersInfo() private void GetUserInfo() { var client = ClientManager.GetClientByNickName(_request.NickName); - if (client is null) { throw new Chat.Exception($"Client not exist with nickname {_request.NickName}"); diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/WhoIsHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/WhoIsHandler.cs index 147ab7ad2..251c19734 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/WhoIsHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/WhoIsHandler.cs @@ -22,7 +22,7 @@ protected override void RequestCheck() // there only existed one nick name base.RequestCheck(); var client = ClientManager.GetClientByNickName(_request.NickName); - _clientInfo = client.Info; + _clientInfo = client?.Info; } protected override void DataOperation() { diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs index 494ab3ffd..23f8f8a47 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs @@ -1,5 +1,6 @@ using UniSpy.Server.Chat.Abstraction.BaseClass; using UniSpy.Server.Chat.Abstraction.Interface; +using UniSpy.Server.Chat.Application; using UniSpy.Server.Chat.Contract.Request.Message; using UniSpy.Server.Chat.Contract.Response.Message; using UniSpy.Server.Chat.Contract.Result.Message; @@ -17,11 +18,11 @@ public PrivateHandler(IChatClient client, PrivateRequest request) : base(client, } protected override void ChannelMessageDataOpration() { - - if (_channel.Mode.IsModeratedChannel) - { - return; - } + var data = ClientManager.ClientPool; + // if (_channel.Mode.IsModeratedChannel) + // { + // return; + // } if (_channel.IsUserBanned(_user)) { diff --git a/src/Servers/GameStatus/src/Application/Program.cs b/src/Servers/GameStatus/src/Application/Program.cs index fdef42c5c..5c41c26ca 100755 --- a/src/Servers/GameStatus/src/Application/Program.cs +++ b/src/Servers/GameStatus/src/Application/Program.cs @@ -17,6 +17,10 @@ static void Main(string[] args) { UniSpy.Exception.HandleException(e); } + finally + { + while (Console.ReadKey().Key != ConsoleKey.Q) { } + } } } } diff --git a/src/Servers/GameTrafficRelay/src/Application/Program.cs b/src/Servers/GameTrafficRelay/src/Application/Program.cs index f65a8028d..8a64790ef 100755 --- a/src/Servers/GameTrafficRelay/src/Application/Program.cs +++ b/src/Servers/GameTrafficRelay/src/Application/Program.cs @@ -16,6 +16,10 @@ static void Main(string[] args) { UniSpy.Exception.HandleException(e); } + finally + { + while (Console.ReadKey().Key != ConsoleKey.Q) { } + } } } } diff --git a/src/Servers/NatNegotiation/src/Application/Program.cs b/src/Servers/NatNegotiation/src/Application/Program.cs index 68606e927..edd148cbd 100755 --- a/src/Servers/NatNegotiation/src/Application/Program.cs +++ b/src/Servers/NatNegotiation/src/Application/Program.cs @@ -17,6 +17,10 @@ static void Main(string[] args) { UniSpy.Exception.HandleException(e); } + finally + { + while (Console.ReadKey().Key != ConsoleKey.Q) { } + } } } } diff --git a/src/Servers/PresenceConnectionManager/src/Application/ClientManager.cs b/src/Servers/PresenceConnectionManager/src/Application/ClientManager.cs index 71891dde3..13d33e6cc 100644 --- a/src/Servers/PresenceConnectionManager/src/Application/ClientManager.cs +++ b/src/Servers/PresenceConnectionManager/src/Application/ClientManager.cs @@ -4,11 +4,11 @@ namespace UniSpy.Server.PresenceConnectionManager.Application { - public class ClientManager : ClientManagerBase + public class ClientManager : ClientManagerBase { public static Client GetClient(int profileid, int? productid = null, int? namespaceId = null) { - return ClientPool.Values.FirstOrDefault( + return (Client)ClientPool.Values.FirstOrDefault( c => ((ClientInfo)c.Info).SubProfileInfo.ProductId == productid && ((ClientInfo)c.Info).SubProfileInfo.ProfileId == profileid && ((ClientInfo)c.Info).SubProfileInfo.NamespaceId == namespaceId); diff --git a/src/Servers/PresenceConnectionManager/src/Application/Program.cs b/src/Servers/PresenceConnectionManager/src/Application/Program.cs index d53ea5128..60c63a3c2 100755 --- a/src/Servers/PresenceConnectionManager/src/Application/Program.cs +++ b/src/Servers/PresenceConnectionManager/src/Application/Program.cs @@ -17,6 +17,10 @@ static void Main(string[] args) { UniSpy.Exception.HandleException(e); } + finally + { + while (Console.ReadKey().Key != ConsoleKey.Q) { } + } } } } \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Application/Program.cs b/src/Servers/PresenceSearchPlayer/src/Application/Program.cs index afc20913d..14aaf1ef9 100755 --- a/src/Servers/PresenceSearchPlayer/src/Application/Program.cs +++ b/src/Servers/PresenceSearchPlayer/src/Application/Program.cs @@ -17,6 +17,10 @@ static void Main(string[] args) { UniSpy.Exception.HandleException(e); } + finally + { + while (Console.ReadKey().Key != ConsoleKey.Q) { } + } } } } diff --git a/src/Servers/QueryReport/src/Application/Program.cs b/src/Servers/QueryReport/src/Application/Program.cs index d24213b25..4a53afb75 100755 --- a/src/Servers/QueryReport/src/Application/Program.cs +++ b/src/Servers/QueryReport/src/Application/Program.cs @@ -17,6 +17,10 @@ static void Main(string[] args) { UniSpy.Exception.HandleException(e); } + finally + { + while (Console.ReadKey().Key != ConsoleKey.Q) { } + } } } } diff --git a/src/Servers/QueryReport/src/V2/Aggregate/Redis/NatNegChannel.cs b/src/Servers/QueryReport/src/V2/Aggregate/Redis/NatNegChannel.cs index 095d82b9f..86ea60186 100644 --- a/src/Servers/QueryReport/src/V2/Aggregate/Redis/NatNegChannel.cs +++ b/src/Servers/QueryReport/src/V2/Aggregate/Redis/NatNegChannel.cs @@ -16,7 +16,7 @@ public NatNegChannel() : base(RedisChannelName.NatNegCookieChannel) } public override void ReceivedMessage(ClientMessageRequest message) { - var client = (Client)ClientManagerBase.GetClient(message.TargetIPEndPoint); + var client = (Client)ClientManagerBase.GetClient(message.TargetIPEndPoint); if (client is null) { LogWriter.LogWarn($"Client:{message.TargetIPEndPoint} not found, we ignore natneg message from SB: {message.ServerBrowserSenderId}"); diff --git a/src/Servers/ServerBrowser/src/Application/Program.cs b/src/Servers/ServerBrowser/src/Application/Program.cs index 5b0283a30..13cbc22c4 100755 --- a/src/Servers/ServerBrowser/src/Application/Program.cs +++ b/src/Servers/ServerBrowser/src/Application/Program.cs @@ -17,6 +17,10 @@ static void Main(string[] args) { UniSpy.Exception.HandleException(e); } + finally + { + while (Console.ReadKey().Key != ConsoleKey.Q) { } + } } } } diff --git a/src/Servers/ServerBrowser/src/V2/Application/ClientManager.cs b/src/Servers/ServerBrowser/src/V2/Application/ClientManager.cs index 630b013d5..04103b8ba 100644 --- a/src/Servers/ServerBrowser/src/V2/Application/ClientManager.cs +++ b/src/Servers/ServerBrowser/src/V2/Application/ClientManager.cs @@ -5,7 +5,7 @@ namespace UniSpy.Server.ServerBrowser.V2.Application { - public class ClientManager : ClientManagerBase + public class ClientManager : ClientManagerBase { public static List GetClient(string gameName) { diff --git a/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/ServerList/ServerListHandler.cs b/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/ServerList/ServerListHandler.cs index 5d07288ff..8f17c6e04 100644 --- a/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/ServerList/ServerListHandler.cs +++ b/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/ServerList/ServerListHandler.cs @@ -101,19 +101,27 @@ private void P2PServerMainList() { var serverInfos = QueryReport.V2.Application.StorageOperation.Persistance.GetGameServerInfos(_request.GameName); ((ServerMainListResult)_result).Flag = GameServerFlags.HasKeysFlag; - + var filteredGameServerInfos = new List(); //TODO do filter - if (_request.Filter.Contains("groupid=")) + if (_request.Filter is not null) { - var groupId = _request.Filter.Replace("groupid=", ""); - var filteredGameServerInfos = serverInfos.Where(s => s.ServerData.ContainsKey("groupid=")).Where(s => s.ServerData["groupid"] == groupId).ToList(); - ((ServerMainListResult)_result).GameServerInfos = filteredGameServerInfos; + if (_request.Filter.Contains("groupid=")) + { + var groupId = _request.Filter.Replace("groupid=", ""); + filteredGameServerInfos = serverInfos.Where(s => s.ServerData.ContainsKey("groupid=")).Where(s => s.ServerData["groupid"] == groupId).ToList(); + ((ServerMainListResult)_result).GameServerInfos = filteredGameServerInfos; + } + else + { + filteredGameServerInfos = serverInfos; + } } else { - ((ServerMainListResult)_result).GameServerInfos = serverInfos; + filteredGameServerInfos = serverInfos; } - + + ((ServerMainListResult)_result).GameServerInfos = filteredGameServerInfos; } private void ServerMainList() { diff --git a/src/Servers/ServerBrowser/test/V2/GameTest.cs b/src/Servers/ServerBrowser/test/V2/GameTest.cs index f3f697651..330a46bfe 100644 --- a/src/Servers/ServerBrowser/test/V2/GameTest.cs +++ b/src/Servers/ServerBrowser/test/V2/GameTest.cs @@ -135,5 +135,13 @@ public void Anno1701Date20221104() }; } + [Fact] + public void AartsTest20230618() + { + //aarts aarts F|Cy9!&w \hostname\gamemode\hostport\hostname\gamename\gametype\gamever\mapname\numplayers\maxplayers\gamemode\password\groupid\mapsessiontype\mapids\internet + var raw = new byte[] { 0x00, 0xB8, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x61, 0x61, 0x72, 0x74, 0x73, 0x00, 0x61, 0x61, 0x72, 0x74, 0x73, 0x00, 0x46, 0x7C, 0x43, 0x79, 0x39, 0x21, 0x26, 0x77, 0x00, 0x5C, 0x68, 0x6F, 0x73, 0x74, 0x6E, 0x61, 0x6D, 0x65, 0x5C, 0x67, 0x61, 0x6D, 0x65, 0x6D, 0x6F, 0x64, 0x65, 0x5C, 0x68, 0x6F, 0x73, 0x74, 0x70, 0x6F, 0x72, 0x74, 0x5C, 0x68, 0x6F, 0x73, 0x74, 0x6E, 0x61, 0x6D, 0x65, 0x5C, 0x67, 0x61, 0x6D, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x5C, 0x67, 0x61, 0x6D, 0x65, 0x74, 0x79, 0x70, 0x65, 0x5C, 0x67, 0x61, 0x6D, 0x65, 0x76, 0x65, 0x72, 0x5C, 0x6D, 0x61, 0x70, 0x6E, 0x61, 0x6D, 0x65, 0x5C, 0x6E, 0x75, 0x6D, 0x70, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x73, 0x5C, 0x6D, 0x61, 0x78, 0x70, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x73, 0x5C, 0x67, 0x61, 0x6D, 0x65, 0x6D, 0x6F, 0x64, 0x65, 0x5C, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, 0x5C, 0x67, 0x72, 0x6F, 0x75, 0x70, 0x69, 0x64, 0x5C, 0x6D, 0x61, 0x70, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6F, 0x6E, 0x74, 0x79, 0x70, 0x65, 0x5C, 0x6D, 0x61, 0x70, 0x69, 0x64, 0x73, 0x5C, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x65, 0x74, 0x00, 0x00, 0x00, 0x00, 0x04 }; + var client = MockObject.CreateClient() as ITestClient; + client.TestReceived(raw); + } } } diff --git a/src/Servers/WebServer/src/Application/Program.cs b/src/Servers/WebServer/src/Application/Program.cs index 809fbb84f..89f286fe1 100755 --- a/src/Servers/WebServer/src/Application/Program.cs +++ b/src/Servers/WebServer/src/Application/Program.cs @@ -17,6 +17,10 @@ static void Main(string[] args) { UniSpy.Exception.HandleException(e); } + finally + { + while (Console.ReadKey().Key != ConsoleKey.Q) { } + } } } } \ No newline at end of file From 68d65f5d4c18d5af55421651eb0f6ef1decd3bd6 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 19 Jun 2023 07:56:00 +0800 Subject: [PATCH 017/231] fix(web): splited http packet --- .../Network/Http/Server/HttpBufferCache.cs | 47 +++++++++++++++++++ .../src/Network/Http/Server/HttpConnection.cs | 13 +++++ .../src/Abstraction/SoapEnvelopBase.cs | 14 +++++- src/Servers/WebServer/test/GeneralTest.cs | 21 +++++++++ 4 files changed, 94 insertions(+), 1 deletion(-) create mode 100644 src/Libraries/Core/src/Network/Http/Server/HttpBufferCache.cs diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpBufferCache.cs b/src/Libraries/Core/src/Network/Http/Server/HttpBufferCache.cs new file mode 100644 index 000000000..12f47769e --- /dev/null +++ b/src/Libraries/Core/src/Network/Http/Server/HttpBufferCache.cs @@ -0,0 +1,47 @@ + +using System.Linq; +using UniSpy.Server.Core.Encryption; + +namespace UniSpy.Server.Core.Network.Http.Server +{ + public class HttpBufferCache : UniSpy.Server.Core.Misc.BufferCacheBase + { + private const string _endFlag = ""; + public HttpBufferCache() + { + } + + public override bool ProcessBuffer(string buffer, out string completeBuffer) + { + if (buffer.Contains(_endFlag)) + { + if (InCompleteBuffer is null) + { + completeBuffer = buffer; + return true; + } + else + { + completeBuffer = InCompleteBuffer + buffer; + InCompleteBuffer = null; + return true; + } + } + else + { + if (InCompleteBuffer is null) + { + InCompleteBuffer = buffer; + completeBuffer = null; + return false; + } + else + { + InCompleteBuffer += buffer; + completeBuffer = null; + return false; + } + } + } + } +} \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs b/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs index 90d760a83..a5f5517fa 100644 --- a/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs +++ b/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs @@ -1,5 +1,7 @@ +using System.Linq; using System.Net; using UniSpy.Server.Core.Abstraction.Interface; +using UniSpy.Server.Core.Encryption; using UniSpy.Server.Core.Events; namespace UniSpy.Server.Core.Network.Http.Server @@ -13,6 +15,7 @@ public class HttpConnection : NetCoreServer.HttpSession, IHttpConnection public event OnConnectedEventHandler OnConnect; public event OnDisconnectedEventHandler OnDisconnect; public event OnReceivedEventHandler OnReceive; + private HttpBufferCache _bufferCache = new HttpBufferCache(); public HttpConnection(HttpConnectionManager server) : base(server) { } @@ -26,6 +29,16 @@ protected override void OnConnecting() } protected override void OnConnected() => OnConnect(); protected override void OnDisconnected() => OnDisconnect(); + protected override void OnReceived(byte[] buffer, long offset, long size) + { + var req = UniSpyEncoding.GetString(buffer.Take((int)size).ToArray()); + string compeleteBuffer; + if (_bufferCache.ProcessBuffer(req, out compeleteBuffer)) + { + var completeBytes = UniSpyEncoding.GetBytes(compeleteBuffer); + base.OnReceived(completeBytes, offset, completeBytes.Length); + } + } protected override void OnReceivedRequest(NetCoreServer.HttpRequest request) => OnReceive(new HttpRequest(request)); void IConnection.Send(string response) { diff --git a/src/Servers/WebServer/src/Abstraction/SoapEnvelopBase.cs b/src/Servers/WebServer/src/Abstraction/SoapEnvelopBase.cs index 248fb1d30..53a0998af 100644 --- a/src/Servers/WebServer/src/Abstraction/SoapEnvelopBase.cs +++ b/src/Servers/WebServer/src/Abstraction/SoapEnvelopBase.cs @@ -1,5 +1,6 @@ using System.IO; using System.Linq; +using System.Text; using System.Xml.Linq; namespace UniSpy.Server.WebServer.Abstraction @@ -59,11 +60,22 @@ public virtual void Add(string name, object value) public override string ToString() { - using (var writer = new StringWriter()) + using (var writer = new MyStringWriter()) { Content.Save(writer); return writer.ToString(); } } + + private class MyStringWriter : StringWriter + { + public MyStringWriter() + { + Encoding = Encoding.UTF8; + } + + public override Encoding Encoding { get; } + } } + } \ No newline at end of file diff --git a/src/Servers/WebServer/test/GeneralTest.cs b/src/Servers/WebServer/test/GeneralTest.cs index eab796851..bb98946a7 100644 --- a/src/Servers/WebServer/test/GeneralTest.cs +++ b/src/Servers/WebServer/test/GeneralTest.cs @@ -1,5 +1,7 @@ using Moq; using UniSpy.Server.Core.Abstraction.Interface; +using UniSpy.Server.Core.Encryption; +using UniSpy.Server.Core.Network.Http.Server; using UniSpy.Server.WebServer.Handler; using Xunit; @@ -17,5 +19,24 @@ public void InlegalMessageTest() var switcher = new CmdSwitcher(client, req.Object); switcher.Handle(); } + + [Fact] + public void BufferCacheTest() + { + // Given + var req1 = "POST /AuthService/AuthService.asmx HTTP/1.1\r\nHost: capricorn.auth.pubsvs.gamespy.com\r\nUser-Agent: GameSpyHTTP/1.0\r\nConnection: close\r\nContent-Length: 861\r\nContent-Type: text/xml\r\nSOAPAction: \"http://gamespy.net/AuthService/LoginUniqueNick\"\r\n"; + var req2 = "\r\n19595asdf210002899d0429a3bc5c24934320e526bed7ad645fb56c7783e2c8afef77e0dd1005d3383b58eb6919baae85881183148a4b4ebe1169475c85e305a2ca4f2d30d9ecf2275dda0706a14d15c5eb755bbca034168b13997f9111ebe821d640f64622586f231479a8516d47c046a819b19e03ba4697c68e3879dafab9380032333233"; + // When + var cache = new HttpBufferCache(); + string completeBuffer; + Assert.False(cache.ProcessBuffer(req1, out completeBuffer)); + + Assert.True(cache.ProcessBuffer(req2, out completeBuffer)); + + Assert.True(cache.InCompleteBuffer == null); + var resultreq = "POST /AuthService/AuthService.asmx HTTP/1.1\r\nHost: capricorn.auth.pubsvs.gamespy.com\r\nUser-Agent: GameSpyHTTP/1.0\r\nConnection: close\r\nContent-Length: 861\r\nContent-Type: text/xml\r\nSOAPAction: \"http://gamespy.net/AuthService/LoginUniqueNick\"\r\n\r\n19595asdf210002899d0429a3bc5c24934320e526bed7ad645fb56c7783e2c8afef77e0dd1005d3383b58eb6919baae85881183148a4b4ebe1169475c85e305a2ca4f2d30d9ecf2275dda0706a14d15c5eb755bbca034168b13997f9111ebe821d640f64622586f231479a8516d47c046a819b19e03ba4697c68e3879dafab9380032333233"; + Assert.True(completeBuffer == resultreq); + // Then + } } } \ No newline at end of file From 7837cdc2f62e16591445d11719b302e51d471873 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 19 Jun 2023 07:57:17 +0800 Subject: [PATCH 018/231] refactor(web): remove const string in buffer cache --- .../Core/src/Network/Http/Server/HttpBufferCache.cs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpBufferCache.cs b/src/Libraries/Core/src/Network/Http/Server/HttpBufferCache.cs index 12f47769e..ebfe6d8d1 100644 --- a/src/Libraries/Core/src/Network/Http/Server/HttpBufferCache.cs +++ b/src/Libraries/Core/src/Network/Http/Server/HttpBufferCache.cs @@ -1,19 +1,14 @@ - -using System.Linq; -using UniSpy.Server.Core.Encryption; - namespace UniSpy.Server.Core.Network.Http.Server { public class HttpBufferCache : UniSpy.Server.Core.Misc.BufferCacheBase { - private const string _endFlag = ""; public HttpBufferCache() { } public override bool ProcessBuffer(string buffer, out string completeBuffer) { - if (buffer.Contains(_endFlag)) + if (buffer.Contains("")) { if (InCompleteBuffer is null) { From eb7e9a162300755de4c47d79b21639a86c8c2209 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 19 Jun 2023 09:18:40 +0800 Subject: [PATCH 019/231] fix(web): exception message parsing --- src/Servers/WebServer/src/Handler/CmdSwitcher.cs | 6 +++--- .../WebServer/src/Module/Auth/Exception/Exception.cs | 2 +- .../Module/Auth/Handler/LoginRemoteAuthWithGameIdHandler.cs | 2 +- .../Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Servers/WebServer/src/Handler/CmdSwitcher.cs b/src/Servers/WebServer/src/Handler/CmdSwitcher.cs index 52f35f99b..d6fd5c7ce 100755 --- a/src/Servers/WebServer/src/Handler/CmdSwitcher.cs +++ b/src/Servers/WebServer/src/Handler/CmdSwitcher.cs @@ -55,15 +55,15 @@ protected override IHandler CreateCmdHandlers(object name, object rawRequest) #region Auth case "LoginProfile": return new LoginProfileHandler(_client, new LoginProfileRequest((string)rawRequest)); - case "LoginProfileWithGameid": + case "LoginProfileWithGameId": return new LoginProfileWithGameIdHandler(_client, new LoginProfileWithGameIdRequest((string)rawRequest)); case "LoginRemoteAuth": return new LoginRemoteAuthHandler(_client, new LoginRemoteAuthRequest((string)rawRequest)); - case "LoginRemoteAuthWithGameid": + case "LoginRemoteAuthWithGameId": return new LoginRemoteAuthWithGameIdHandler(_client, new LoginRemoteAuthWithGameIdRequest((string)rawRequest)); case "LoginUniqueNick": return new LoginUniqueNickHandler(_client, new LoginUniqueNickRequest((string)rawRequest)); - case "LoginUniqueNickWithGameid": + case "LoginUniqueNickWithGameId": return new LoginUniqueNickWithGameIdHandler(_client, new LoginUniqueNickWithGameIdRequest((string)rawRequest)); #endregion #region Direct2Game diff --git a/src/Servers/WebServer/src/Module/Auth/Exception/Exception.cs b/src/Servers/WebServer/src/Module/Auth/Exception/Exception.cs index c273a2f19..45a847107 100644 --- a/src/Servers/WebServer/src/Module/Auth/Exception/Exception.cs +++ b/src/Servers/WebServer/src/Module/Auth/Exception/Exception.cs @@ -41,7 +41,7 @@ public Exception() : this("Unkown Error!", AuthErrorCode.UnknownError) public Exception(string message) : this(message, AuthErrorCode.UnknownError) { } - public Exception(string message, AuthErrorCode code) + public Exception(string message, AuthErrorCode code) : base(message) { ErrorCode = code; } diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginRemoteAuthWithGameIdHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginRemoteAuthWithGameIdHandler.cs index 1e5403dce..541228e24 100644 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginRemoteAuthWithGameIdHandler.cs +++ b/src/Servers/WebServer/src/Module/Auth/Handler/LoginRemoteAuthWithGameIdHandler.cs @@ -25,7 +25,7 @@ join sp in db.Subprofiles on p.ProfileId equals sp.ProfileId select new { u, p, sp }; if (result.Count() != 1) { - throw new System.Exception("No account exists with the provided email address."); + throw new Auth.Exception("No account exists with the provided email address."); } var data = result.First(); diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs index f0f49911d..c93241e3a 100644 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs +++ b/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs @@ -25,7 +25,7 @@ join sp in db.Subprofiles on p.ProfileId equals sp.ProfileId select new { u, p, sp }; if (result.Count() != 1) { - throw new System.Exception("No account exists with the provided email address."); + throw new Auth.Exception("No account exists with the provided email address."); } var data = result.First(); _result.UserId = data.u.UserId; From 74075fea24315c73907f9fe2dfd6d1b1d834a693 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 26 Jun 2023 11:10:27 +0800 Subject: [PATCH 020/231] update: redis channel related functions --- .../src/Abstraction/BaseClass/ClientBase.cs | 1 - .../src/Extension/Redis/RedisChannelName.cs | 1 + .../Core/src/Network/RemoteObject.cs} | 44 +---------------- .../BaseClass/Channel/ChannelHandlerBase.cs | 2 +- .../Abstraction/BaseClass/CmdHandlerBase.cs | 5 +- .../BaseClass/General/LogedInHandlerBase.cs | 2 +- .../BaseClass/Message/MessageHandlerBase.cs | 2 +- .../{IChatClient.cs => IShareClient.cs} | 2 +- .../Chat/src/Aggregate/ChannelManager.cs | 2 +- .../ChannelProperty/ChannelGeneral.cs | 2 +- .../ChannelProperty/ChannelUserRelated.cs | 8 ++-- .../ChannelProperty/ChannelValidation.cs | 2 +- src/Servers/Chat/src/Aggregate/ChannelUser.cs | 4 +- .../Redis/Contract/DisconnectRequest.cs | 12 ----- .../{ChannelMessage.cs => RemoteMessage.cs} | 0 .../Chat/src/Aggregate/Redis/RedisChannel.cs | 4 +- .../Chat/src/Aggregate/RemoteClient.cs | 48 +++++++++++++++++++ src/Servers/Chat/src/Application/Client.cs | 17 ++++--- .../Chat/src/Application/ClientManager.cs | 6 +-- src/Servers/Chat/src/Application/Server.cs | 6 +-- .../CmdHandler/Channel/GetCKeyHandler.cs | 2 +- .../Channel/GetChannelKeyHandler.cs | 2 +- .../Handler/CmdHandler/Channel/JoinHandler.cs | 2 +- .../Handler/CmdHandler/Channel/KickHandler.cs | 2 +- .../Handler/CmdHandler/Channel/ModeHandler.cs | 2 +- .../CmdHandler/Channel/NamesHandler.cs | 2 +- .../Handler/CmdHandler/Channel/PartHandler.cs | 2 +- .../CmdHandler/Channel/SetCKeyHandler.cs | 2 +- .../Channel/SetChannelKeyHandler.cs | 2 +- .../CmdHandler/Channel/TopicHandler.cs | 2 +- .../CmdHandler/General/CdKeyHandler.cs | 2 +- .../CmdHandler/General/CryptHandler.cs | 2 +- .../CmdHandler/General/GetKeyHandler.cs | 2 +- .../CmdHandler/General/GetUdpRelayHandler.cs | 2 +- .../CmdHandler/General/InviteHandler.cs | 2 +- .../Handler/CmdHandler/General/ListHandler.cs | 2 +- .../CmdHandler/General/LoginHandler.cs | 2 +- .../Handler/CmdHandler/General/NickHandler.cs | 2 +- .../Handler/CmdHandler/General/PingHandler.cs | 2 +- .../Handler/CmdHandler/General/QuitHandler.cs | 8 +++- .../CmdHandler/General/SetKeyHandler.cs | 2 +- .../Handler/CmdHandler/General/UserHandler.cs | 2 +- .../CmdHandler/General/UserIPHandler.cs | 2 +- .../Handler/CmdHandler/General/WhoHandler.cs | 2 +- .../CmdHandler/General/WhoIsHandler.cs | 2 +- .../Handler/CmdHandler/Message/AtmHandler.cs | 2 +- .../CmdHandler/Message/NoticeHandler.cs | 2 +- .../CmdHandler/Message/PrivateHandler.cs | 2 +- .../Handler/CmdHandler/Message/UtmHandler.cs | 2 +- src/Servers/Chat/src/Handler/CmdSwitcher.cs | 4 +- src/Servers/Chat/test/RedisChatChannelTest.cs | 6 +-- .../BaseClass/LoggedInCmdHandlerBase.cs | 26 +++++++++- .../src/Abstraction/Interface/IShareClient.cs | 12 +++++ .../src/Aggregate/Redis/UserInfoChannel.cs | 34 +++++++++++++ .../src/Aggregate/Redis/UserInfoMessage.cs | 18 +++++++ .../src/Aggregate/RemoteClient.cs | 47 ++++++++++++++++++ .../src/Application/Client.cs | 14 +++++- .../src/Application/ClientInfo.cs | 6 +++ .../src/Application/Server.cs | 9 +++- .../src/Application/ServerLauncher.cs | 1 + .../src/Handler/CmdSwitcher.cs | 3 +- 61 files changed, 287 insertions(+), 127 deletions(-) rename src/{Servers/Chat/src/Aggregate/RemoteObjects.cs => Libraries/Core/src/Network/RemoteObject.cs} (62%) rename src/Servers/Chat/src/Abstraction/Interface/{IChatClient.cs => IShareClient.cs} (83%) delete mode 100644 src/Servers/Chat/src/Aggregate/Redis/Contract/DisconnectRequest.cs rename src/Servers/Chat/src/Aggregate/Redis/Contract/{ChannelMessage.cs => RemoteMessage.cs} (100%) create mode 100644 src/Servers/Chat/src/Aggregate/RemoteClient.cs create mode 100644 src/Servers/PresenceConnectionManager/src/Abstraction/Interface/IShareClient.cs create mode 100644 src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoChannel.cs create mode 100644 src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoMessage.cs create mode 100644 src/Servers/PresenceConnectionManager/src/Aggregate/RemoteClient.cs diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs index c5b663df5..2b2f8ab17 100644 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs +++ b/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs @@ -4,7 +4,6 @@ using UniSpy.Server.Core.Logging; using UniSpy.Server.Core.Extension; using System.Threading.Tasks; -using System.Net; namespace UniSpy.Server.Core.Abstraction.BaseClass { diff --git a/src/Libraries/Core/src/Extension/Redis/RedisChannelName.cs b/src/Libraries/Core/src/Extension/Redis/RedisChannelName.cs index 8c7764b2d..9b3354329 100755 --- a/src/Libraries/Core/src/Extension/Redis/RedisChannelName.cs +++ b/src/Libraries/Core/src/Extension/Redis/RedisChannelName.cs @@ -6,5 +6,6 @@ public class RedisChannelName public const string HeartbeatChannel = "QueryReport:HeartbeatChannel"; public const string PresenceConnectionManagerBuddyMessageChannel = "PresenceConnectionManager:BuddyMessageChannel"; public const string ChatChannelPrefix = "Chat:Channel"; + public const string PresenceConnectionManagerUserInfoChannel = "PresenceConnectionManager:UserInfoChannel"; } } diff --git a/src/Servers/Chat/src/Aggregate/RemoteObjects.cs b/src/Libraries/Core/src/Network/RemoteObject.cs similarity index 62% rename from src/Servers/Chat/src/Aggregate/RemoteObjects.cs rename to src/Libraries/Core/src/Network/RemoteObject.cs index a6a22b072..150253892 100644 --- a/src/Servers/Chat/src/Aggregate/RemoteObjects.cs +++ b/src/Libraries/Core/src/Network/RemoteObject.cs @@ -1,18 +1,12 @@ using System; using System.Net; using Newtonsoft.Json; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Chat.Handler; -using UniSpy.Server.Core.Abstraction.BaseClass; using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; using UniSpy.Server.Core.Events; using UniSpy.Server.Core.Logging; using UniSpy.Server.Core.Misc; -namespace UniSpy.Server.Chat.Aggregate +namespace UniSpy.Server.Core.Network { public class ConcreteTypeConverter : JsonConverter { @@ -54,45 +48,11 @@ public RemoteServer(IServer server) } public void Start() { } } - public class RemoteClient : IChatClient, Core.Abstraction.Interface.ITestClient - { - public bool IsLogRaw { get; set; } - [JsonConverter(typeof(ConcreteTypeConverter))] - public IConnection Connection { get; set; } - [JsonConverter(typeof(ConcreteTypeConverter))] - public ICryptography Crypto { get; set; } - [JsonConverter(typeof(ConcreteTypeConverter))] - public ClientInfoBase Info { get; set; } - ClientInfo IChatClient.Info => (ClientInfo)Info; - [JsonConverter(typeof(ConcreteTypeConverter))] - public IServer Server { get; set; } - /// - /// using for json deserialization - /// - public RemoteClient() { } - public RemoteClient(Client client) - { - Connection = new RemoteTcpConnection(client.Connection, new RemoteTcpConnectionManager()); - Server = new RemoteServer(client.Server); - Info = client.Info.DeepCopy(); - ((ClientInfo)Info).IsRemoteClient = true; - Crypto = client.Crypto; - } - public void Send(IResponse response) => this.LogDebug("Ignore remote client Send() operation"); - public RemoteClient GetRemoteClient() => this; - - public void TestReceived(byte[] buffer) - { - this.LogNetworkReceiving(buffer); - var switcher = new CmdSwitcher(this, UniSpyEncoding.GetString(buffer)); - switcher.Handle(); - } - } public class RemoteTcpConnectionManager : IConnectionManager { #pragma warning disable CS0067 public event OnConnectingEventHandler OnInitialization; - public void Start() => throw new Chat.Exception("Remote tcp connection do not have this method."); + public void Start() => throw new UniSpy.Exception("Remote tcp connection do not have this method."); public RemoteTcpConnectionManager() { } } diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs index 689e06c7f..3dc6d38da 100755 --- a/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs +++ b/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs @@ -17,7 +17,7 @@ public abstract class ChannelHandlerBase : LogedInHandlerBase /// protected ChannelUser _user; private new ChannelRequestBase _request => (ChannelRequestBase)base._request; - public ChannelHandlerBase(IChatClient client, IRequest request) : base(client, request) { } + public ChannelHandlerBase(IShareClient client, IRequest request) : base(client, request) { } protected override void RequestCheck() { diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/CmdHandlerBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/CmdHandlerBase.cs index e23afa676..24e6dd093 100755 --- a/src/Servers/Chat/src/Abstraction/BaseClass/CmdHandlerBase.cs +++ b/src/Servers/Chat/src/Abstraction/BaseClass/CmdHandlerBase.cs @@ -2,7 +2,6 @@ using UniSpy.Server.Chat.Aggregate.Redis.Contract; using UniSpy.Server.Chat.Error.IRC.General; using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Logging; namespace UniSpy.Server.Chat.Abstraction.BaseClass { @@ -18,11 +17,11 @@ namespace UniSpy.Server.Chat.Abstraction.BaseClass /// public abstract class CmdHandlerBase : UniSpy.Server.Core.Abstraction.BaseClass.CmdHandlerBase { - protected new IChatClient _client => (IChatClient)base._client; + protected new IShareClient _client => (IShareClient)base._client; protected new RequestBase _request => (RequestBase)base._request; protected new ResultBase _result { get => (ResultBase)base._result; set => base._result = value; } protected new ResponseBase _response { get => (ResponseBase)base._response; set => base._response = value; } - public CmdHandlerBase(IChatClient client, IRequest request) : base(client, request) { } + public CmdHandlerBase(IShareClient client, IRequest request) : base(client, request) { } //if we use this structure the error response should also write to _sendingBuffer protected override void HandleException(System.Exception ex) { diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/General/LogedInHandlerBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/General/LogedInHandlerBase.cs index 1c6237992..0e1613c01 100755 --- a/src/Servers/Chat/src/Abstraction/BaseClass/General/LogedInHandlerBase.cs +++ b/src/Servers/Chat/src/Abstraction/BaseClass/General/LogedInHandlerBase.cs @@ -5,7 +5,7 @@ namespace UniSpy.Server.Chat.Abstraction.BaseClass { public abstract class LogedInHandlerBase : CmdHandlerBase { - public LogedInHandlerBase(IChatClient client, IRequest request) : base(client, request) { } + public LogedInHandlerBase(IShareClient client, IRequest request) : base(client, request) { } // public override void Handle() // { diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageHandlerBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageHandlerBase.cs index ebc05eedc..377adf3f1 100755 --- a/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageHandlerBase.cs +++ b/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageHandlerBase.cs @@ -13,7 +13,7 @@ public abstract class MessageHandlerBase : ChannelHandlerBase protected new MessageRequestBase _request => (MessageRequestBase)base._request; protected new MessageResultBase _result { get => (MessageResultBase)base._result; set => base._result = value; } protected ChannelUser _receiver; - public MessageHandlerBase(IChatClient client, IRequest request) : base(client, request) { } + public MessageHandlerBase(IShareClient client, IRequest request) : base(client, request) { } protected override void RequestCheck() { diff --git a/src/Servers/Chat/src/Abstraction/Interface/IChatClient.cs b/src/Servers/Chat/src/Abstraction/Interface/IShareClient.cs similarity index 83% rename from src/Servers/Chat/src/Abstraction/Interface/IChatClient.cs rename to src/Servers/Chat/src/Abstraction/Interface/IShareClient.cs index dca1ab89d..b3e109338 100644 --- a/src/Servers/Chat/src/Abstraction/Interface/IChatClient.cs +++ b/src/Servers/Chat/src/Abstraction/Interface/IShareClient.cs @@ -4,7 +4,7 @@ namespace UniSpy.Server.Chat.Abstraction.Interface { - public interface IChatClient : IClient + public interface IShareClient : IClient, ITestClient { public new ClientInfo Info { get; } public RemoteClient GetRemoteClient(); diff --git a/src/Servers/Chat/src/Aggregate/ChannelManager.cs b/src/Servers/Chat/src/Aggregate/ChannelManager.cs index 1c4e9349d..aea470dec 100644 --- a/src/Servers/Chat/src/Aggregate/ChannelManager.cs +++ b/src/Servers/Chat/src/Aggregate/ChannelManager.cs @@ -29,7 +29,7 @@ public static void RemoveChannel(string name) var chanInfo = chan.GetChannelCache(); QueryReport.Application.StorageOperation.RemoveChannel(chanInfo); } - public static Channel CreateChannel(string name, string password = null, IChatClient creator = null) + public static Channel CreateChannel(string name, string password = null, IShareClient creator = null) { var channel = new Channel(name, creator, password); diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelGeneral.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelGeneral.cs index a663dbebc..9e2d667ac 100755 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelGeneral.cs +++ b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelGeneral.cs @@ -30,7 +30,7 @@ public sealed partial class Channel public string RoomName { get; private set; } public bool IsValidPeerRoom => GroupId is not null && RoomName is not null; public string PreviousJoinedChannel { get; private set; } - public Channel(string name, IChatClient client, string password = null) + public Channel(string name, IShareClient client, string password = null) { ServerId = client.Server.Id; Name = name; diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs index c50c2d6ee..0d3db9f55 100644 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs +++ b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs @@ -134,8 +134,8 @@ private void DisableUserVoicePermission(ModeRequest request) } } public ChannelUser GetUser(string nickName) => Users.ContainsKey(nickName) == true ? Users[nickName] : null; - public ChannelUser GetUser(IChatClient client) => Users.Values.FirstOrDefault(u => u.Connection.RemoteIPEndPoint == client.Connection.RemoteIPEndPoint); - public ChannelUser AddUser(IChatClient client, string password = null, bool isChannelCreator = false, bool isChannelOperator = false) + public ChannelUser GetUser(IShareClient client) => Users.Values.FirstOrDefault(u => u.Connection.RemoteIPEndPoint == client.Connection.RemoteIPEndPoint); + public ChannelUser AddUser(IShareClient client, string password = null, bool isChannelCreator = false, bool isChannelOperator = false) { Validation(client, password); var user = new ChannelUser(client, this); @@ -159,9 +159,9 @@ public void RemoveUser(ChannelUser user) } public bool IsUserExisted(ChannelUser user) => IsUserExisted(user.Client); - public bool IsUserExisted(IChatClient client) => Users.ContainsKey(client.Info.NickName); + public bool IsUserExisted(IShareClient client) => Users.ContainsKey(client.Info.NickName); public bool IsUserBanned(ChannelUser user) => IsUserBanned(user.Client); - private bool IsUserBanned(IChatClient client) + private bool IsUserBanned(IShareClient client) { if (!BanList.ContainsKey(client.Info.NickName)) { diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelValidation.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelValidation.cs index 8b2cba74b..288dc8abe 100644 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelValidation.cs +++ b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelValidation.cs @@ -13,7 +13,7 @@ public void VerifyPassword(string pass) throw new Chat.Exception("Password is not correct"); } } - private void Validation(IChatClient client, string password) + private void Validation(IShareClient client, string password) { if (Mode.IsInviteOnly) { diff --git a/src/Servers/Chat/src/Aggregate/ChannelUser.cs b/src/Servers/Chat/src/Aggregate/ChannelUser.cs index 79be39ad2..66920b4e1 100755 --- a/src/Servers/Chat/src/Aggregate/ChannelUser.cs +++ b/src/Servers/Chat/src/Aggregate/ChannelUser.cs @@ -31,7 +31,7 @@ public sealed class ChannelUser /// The client reference /// [JsonIgnore] - public IChatClient Client { get; private set; } + public IShareClient Client { get; private set; } [JsonIgnore] public ClientInfo Info => Client.Info; [JsonIgnore] @@ -66,7 +66,7 @@ public string Modes } } public ChannelUser() { } - public ChannelUser(IChatClient client, Channel channel) + public ChannelUser(IShareClient client, Channel channel) { Client = client; Channel = channel; diff --git a/src/Servers/Chat/src/Aggregate/Redis/Contract/DisconnectRequest.cs b/src/Servers/Chat/src/Aggregate/Redis/Contract/DisconnectRequest.cs deleted file mode 100644 index f0efd7bd6..000000000 --- a/src/Servers/Chat/src/Aggregate/Redis/Contract/DisconnectRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Aggregate.Redis.Contract -{ - public class DisconnectRequest : RequestBase - { - public DisconnectRequest() : base("DISCONNECT") - { - Parse(); - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/Redis/Contract/ChannelMessage.cs b/src/Servers/Chat/src/Aggregate/Redis/Contract/RemoteMessage.cs similarity index 100% rename from src/Servers/Chat/src/Aggregate/Redis/Contract/ChannelMessage.cs rename to src/Servers/Chat/src/Aggregate/Redis/Contract/RemoteMessage.cs diff --git a/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs b/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs index b40963f3d..8b145b1e6 100644 --- a/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs +++ b/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs @@ -28,7 +28,7 @@ public override void ReceivedMessage(RemoteMessage message) ClientManager.RemoveClient(message.Client); return; } - IChatClient client = (IChatClient)ClientManager.GetClient(message.Client.Connection.RemoteIPEndPoint); + IShareClient client = (IShareClient)ClientManager.GetClient(message.Client.Connection.RemoteIPEndPoint); if (client is null) { ClientManager.AddClient(message.Client); @@ -67,7 +67,7 @@ public override void ReceivedMessage(RemoteMessage message) { return; } - IChatClient client = (IChatClient)ClientManager.GetClient(message.Client.Connection.RemoteIPEndPoint); + IShareClient client = (IShareClient)ClientManager.GetClient(message.Client.Connection.RemoteIPEndPoint); if (client is null) { throw new Chat.Exception($"There are no remote client found in RemoteClients pool, the client must be login on the remote server."); diff --git a/src/Servers/Chat/src/Aggregate/RemoteClient.cs b/src/Servers/Chat/src/Aggregate/RemoteClient.cs new file mode 100644 index 000000000..b57469906 --- /dev/null +++ b/src/Servers/Chat/src/Aggregate/RemoteClient.cs @@ -0,0 +1,48 @@ +using Newtonsoft.Json; +using UniSpy.Server.Chat.Abstraction.Interface; +using UniSpy.Server.Chat.Aggregate.Misc; +using UniSpy.Server.Chat.Application; +using UniSpy.Server.Chat.Handler; +using UniSpy.Server.Core.Abstraction.BaseClass; +using UniSpy.Server.Core.Abstraction.Interface; +using UniSpy.Server.Core.Encryption; +using UniSpy.Server.Core.Logging; +using UniSpy.Server.Core.Network; + +namespace UniSpy.Server.Chat.Aggregate +{ + public class RemoteClient : IShareClient + { + public bool IsLogRaw { get; set; } + [JsonConverter(typeof(ConcreteTypeConverter))] + public IConnection Connection { get; set; } + [JsonConverter(typeof(ConcreteTypeConverter))] + public ICryptography Crypto { get; set; } + [JsonConverter(typeof(ConcreteTypeConverter))] + public ClientInfoBase Info { get; set; } + ClientInfo IShareClient.Info => (ClientInfo)Info; + [JsonConverter(typeof(ConcreteTypeConverter))] + public IServer Server { get; set; } + /// + /// using for json deserialization + /// + public RemoteClient() { } + public RemoteClient(Client client) + { + Connection = new RemoteTcpConnection(client.Connection, new RemoteTcpConnectionManager()); + Server = new RemoteServer(client.Server); + Info = client.Info.DeepCopy(); + ((ClientInfo)Info).IsRemoteClient = true; + Crypto = client.Crypto; + } + public void Send(IResponse response) { } + public RemoteClient GetRemoteClient() => this; + + public void TestReceived(byte[] buffer) + { + this.LogNetworkReceiving(buffer); + var switcher = new CmdSwitcher(this, UniSpyEncoding.GetString(buffer)); + switcher.Handle(); + } + } +} \ No newline at end of file diff --git a/src/Servers/Chat/src/Application/Client.cs b/src/Servers/Chat/src/Application/Client.cs index 831704aae..67ad5158d 100644 --- a/src/Servers/Chat/src/Application/Client.cs +++ b/src/Servers/Chat/src/Application/Client.cs @@ -13,14 +13,16 @@ namespace UniSpy.Server.Chat.Application { - public class Client : ClientBase, IChatClient + public class Client : ClientBase, IShareClient { public new ClientInfo Info { get => (ClientInfo)base.Info; private set => base.Info = value; } public new ITcpConnection Connection => (ITcpConnection)base.Connection; private BufferCache _bufferCache = new BufferCache(); + private RemoteClient _remoteClient; public Client(IConnection connection, IServer server) : base(connection, server) { Info = new ClientInfo(); + _remoteClient = new RemoteClient(this); } protected override void OnConnected() { @@ -55,19 +57,16 @@ protected override void OnDisconnected() new QuitHandler(this, req).Handle(); Info.IsLoggedIn = false; } - Task.Run(() => PublishDisconnectMessage()); - base.OnDisconnected(); - } - private void PublishDisconnectMessage() - { - var message = new RemoteMessage(new DisconnectRequest(), GetRemoteClient()); + var request = new QuitRequest() { Reason = "Disconnect from server" }; + var message = new RemoteMessage(request, GetRemoteClient()); Chat.Application.Server.GeneralChannel.PublishMessage(message); + base.OnDisconnected(); } protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, UniSpyEncoding.GetString((byte[])buffer)); public RemoteClient GetRemoteClient() { - var remoteClient = new RemoteClient(this); - return remoteClient; + _remoteClient.Info = Info; + return _remoteClient; } } } \ No newline at end of file diff --git a/src/Servers/Chat/src/Application/ClientManager.cs b/src/Servers/Chat/src/Application/ClientManager.cs index 08a4d98e4..ce18d9b96 100644 --- a/src/Servers/Chat/src/Application/ClientManager.cs +++ b/src/Servers/Chat/src/Application/ClientManager.cs @@ -11,10 +11,10 @@ public sealed class ClientManager : ClientManagerBase /// /// We need to make sure client is get by nickname, otherwise we throw exception /// - public static IChatClient GetClientByNickName(string nickName) + public static IShareClient GetClientByNickName(string nickName) { - IChatClient client; - client = (IChatClient)ClientPool.Values.FirstOrDefault(c => ((IChatClient)c).Info.NickName == nickName); + IShareClient client; + client = (IShareClient)ClientPool.Values.FirstOrDefault(c => ((IShareClient)c).Info.NickName == nickName); return client; } public static List GetAllClientInfo() diff --git a/src/Servers/Chat/src/Application/Server.cs b/src/Servers/Chat/src/Application/Server.cs index 0279bacb8..171d3dea9 100644 --- a/src/Servers/Chat/src/Application/Server.cs +++ b/src/Servers/Chat/src/Application/Server.cs @@ -8,14 +8,14 @@ namespace UniSpy.Server.Chat.Application { public sealed class Server : ServerBase { - public static GeneralMessageChannel GeneralChannel = new GeneralMessageChannel(); + public static readonly GeneralMessageChannel GeneralChannel = new GeneralMessageChannel(); static Server() { _name = "Chat"; } - public Server(){ } + public Server() { } - public Server(IConnectionManager manager) : base(manager){} + public Server(IConnectionManager manager) : base(manager) { } public override void Start() { diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetCKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetCKeyHandler.cs index abe454d03..d2cdad551 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetCKeyHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetCKeyHandler.cs @@ -15,7 +15,7 @@ public sealed class GetCKeyHandler : ChannelHandlerBase private new GetCKeyRequest _request => (GetCKeyRequest)base._request; private new GetCKeyResult _result { get => (GetCKeyResult)base._result; set => base._result = value; } private TimeSpan _waitingTime = TimeSpan.FromSeconds(10); - public GetCKeyHandler(IChatClient client, GetCKeyRequest request) : base(client, request) + public GetCKeyHandler(IShareClient client, GetCKeyRequest request) : base(client, request) { _result = new GetCKeyResult(); } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetChannelKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetChannelKeyHandler.cs index 3eeb4ed4d..dc0166ba1 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetChannelKeyHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetChannelKeyHandler.cs @@ -16,7 +16,7 @@ public sealed class GetChannelKeyHandler : ChannelHandlerBase { private new GetChannelKeyRequest _request => (GetChannelKeyRequest)base._request; private new GetChannelKeyResult _result { get => (GetChannelKeyResult)base._result; set => base._result = value; } - public GetChannelKeyHandler(IChatClient client, GetChannelKeyRequest request) : base(client, request) + public GetChannelKeyHandler(IShareClient client, GetChannelKeyRequest request) : base(client, request) { _result = new GetChannelKeyResult(); } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs index a09fe10c4..c06732299 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs @@ -24,7 +24,7 @@ public sealed class JoinHandler : LogedInHandlerBase private static readonly GeneralMessageChannel GeneralMessageChannel = new GeneralMessageChannel(); private Aggregate.Channel _channel; private ChannelUser _user; - public JoinHandler(IChatClient client, JoinRequest request) : base(client, request) + public JoinHandler(IShareClient client, JoinRequest request) : base(client, request) { _result = new JoinResult(); } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/KickHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/KickHandler.cs index d7576c003..986fd109d 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/KickHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/KickHandler.cs @@ -14,7 +14,7 @@ public sealed class KickHandler : ChannelHandlerBase private new KickResponse _response { get => (KickResponse)base._response; set => base._response = value; } private new KickResult _result { get => (KickResult)base._result; set => base._result = value; } private ChannelUser _kickee; - public KickHandler(IChatClient client, KickRequest request) : base(client, request) + public KickHandler(IShareClient client, KickRequest request) : base(client, request) { _result = new KickResult(); } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/ModeHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/ModeHandler.cs index bde72edfd..b809419cd 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/ModeHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/ModeHandler.cs @@ -14,7 +14,7 @@ public sealed class ModeHandler : ChannelHandlerBase { private new ModeRequest _request => (ModeRequest)base._request; private new ModeResult _result { get => (ModeResult)base._result; set => base._result = value; } - public ModeHandler(IChatClient client, ModeRequest request) : base(client, request) { } + public ModeHandler(IShareClient client, ModeRequest request) : base(client, request) { } protected override void DataOperation() { _result = new ModeResult(); diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/NamesHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/NamesHandler.cs index df9410bdc..f0b8898a8 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/NamesHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/NamesHandler.cs @@ -13,7 +13,7 @@ public sealed class NamesHandler : ChannelHandlerBase { private new NamesRequest _request => (NamesRequest)base._request; private new NamesResult _result { get => (NamesResult)base._result; set => base._result = value; } - public NamesHandler(IChatClient client, NamesRequest request) : base(client, request) { } + public NamesHandler(IShareClient client, NamesRequest request) : base(client, request) { } protected override void RequestCheck() { if (_request.RawRequest is null) diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs index 1438af0e7..0a0188d57 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs @@ -16,7 +16,7 @@ public sealed class PartHandler : ChannelHandlerBase private new PartRequest _request => (PartRequest)base._request; private new PartResponse _response { get => (PartResponse)base._response; set => base._response = value; } private new PartResult _result { get => (PartResult)base._result; set => base._result = value; } - public PartHandler(IChatClient client, PartRequest request) : base(client, request) { } + public PartHandler(IShareClient client, PartRequest request) : base(client, request) { } static PartHandler() { } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetCKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetCKeyHandler.cs index c8ea67e79..8c46320f0 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetCKeyHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetCKeyHandler.cs @@ -17,7 +17,7 @@ public sealed class SetCKeyHandler : ChannelHandlerBase { private new SetCKeyRequest _request => (SetCKeyRequest)base._request; private ChannelUser _otherUser; - public SetCKeyHandler(IChatClient client, SetCKeyRequest request) : base(client, request) + public SetCKeyHandler(IShareClient client, SetCKeyRequest request) : base(client, request) { } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetChannelKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetChannelKeyHandler.cs index eb9b771a0..0280e4aba 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetChannelKeyHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetChannelKeyHandler.cs @@ -15,7 +15,7 @@ public sealed class SetChannelKeyHandler : ChannelHandlerBase { private new SetChannelKeyRequest _request => (SetChannelKeyRequest)base._request; private new SetChannelKeyResult _result { get => (SetChannelKeyResult)base._result; set => base._result = value; } - public SetChannelKeyHandler(IChatClient client, SetChannelKeyRequest request) : base(client, request) + public SetChannelKeyHandler(IShareClient client, SetChannelKeyRequest request) : base(client, request) { _result = new SetChannelKeyResult(); } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/TopicHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/TopicHandler.cs index 177cabee6..530a1ec22 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/TopicHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/TopicHandler.cs @@ -11,7 +11,7 @@ public sealed class TopicHandler : ChannelHandlerBase { private new TopicRequest _request => (TopicRequest)base._request; private new TopicResult _result { get => (TopicResult)base._result; set => base._result = value; } - public TopicHandler(IChatClient client, TopicRequest request) : base(client, request) + public TopicHandler(IShareClient client, TopicRequest request) : base(client, request) { _result = new TopicResult(); } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/CdKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/CdKeyHandler.cs index 7e25688d1..f87baa97f 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/CdKeyHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/CdKeyHandler.cs @@ -10,7 +10,7 @@ public sealed class CdKeyHandler : CmdHandlerBase { private new CdKeyRequest _request => (CdKeyRequest)base._request; - public CdKeyHandler(IChatClient client, CdKeyRequest request) : base(client, request){ } + public CdKeyHandler(IShareClient client, CdKeyRequest request) : base(client, request){ } protected override void ResponseConstruct() { diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/CryptHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/CryptHandler.cs index 8e1f49f55..b7d70bdd8 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/CryptHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/CryptHandler.cs @@ -17,7 +17,7 @@ public sealed class CryptHandler : CmdHandlerBase private new CryptResult _result { get => (CryptResult)base._result; set => base._result = value; } private ICryptography _crypto; // CRYPT des 1 gamename - public CryptHandler(IChatClient client, CryptRequest request) : base(client, request) + public CryptHandler(IShareClient client, CryptRequest request) : base(client, request) { _result = new CryptResult(); } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/GetKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/GetKeyHandler.cs index 2ace55763..6b1609ba3 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/GetKeyHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/GetKeyHandler.cs @@ -14,7 +14,7 @@ public sealed class GetKeyHandler : LogedInHandlerBase { private new GetKeyRequest _request => (GetKeyRequest)base._request; private new GetKeyResult _result { get => (GetKeyResult)base._result; set => base._result = value; } - public GetKeyHandler(IChatClient client, GetKeyRequest request) : base(client, request) + public GetKeyHandler(IShareClient client, GetKeyRequest request) : base(client, request) { _result = new GetKeyResult(); } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/GetUdpRelayHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/GetUdpRelayHandler.cs index 08a972601..a4ad86d1e 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/GetUdpRelayHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/GetUdpRelayHandler.cs @@ -12,6 +12,6 @@ namespace UniSpy.Server.Chat.Handler.CmdHandler.General public sealed class GetUdpRelayHandler : CmdHandlerBase { new GetUdpRelayRequest _request => (GetUdpRelayRequest)base._request; - public GetUdpRelayHandler(IChatClient client, GetUdpRelayRequest request) : base(client, request) { } + public GetUdpRelayHandler(IShareClient client, GetUdpRelayRequest request) : base(client, request) { } } } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/InviteHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/InviteHandler.cs index d759f8f7b..32cb6b554 100644 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/InviteHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/InviteHandler.cs @@ -8,7 +8,7 @@ namespace UniSpy.Server.Chat.Handler.CmdHandler.General public sealed class InviteHandler : CmdHandlerBase { private new InviteRequest _request => (InviteRequest)base._request; - public InviteHandler(IChatClient client, InviteRequest request) : base(client, request) + public InviteHandler(IShareClient client, InviteRequest request) : base(client, request) { } protected override void RequestCheck() diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/ListHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/ListHandler.cs index d09a47edd..dac847a66 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/ListHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/ListHandler.cs @@ -14,7 +14,7 @@ public sealed class ListHandler : LogedInHandlerBase private new ListRequest _request => (ListRequest)base._request; private new ListResult _result { get => (ListResult)base._result; set => base._result = value; } //:irc.foonet.com 321 Pants Channel :Users Name\r\n:irc.foonet.com 323 Pants :End of /LIST\r\n - public ListHandler(IChatClient client, ListRequest request) : base(client, request) + public ListHandler(IShareClient client, ListRequest request) : base(client, request) { _result = new ListResult(); } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/LoginHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/LoginHandler.cs index 2bc594b17..36f797167 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/LoginHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/LoginHandler.cs @@ -14,7 +14,7 @@ public sealed class LoginHandler : CmdHandlerBase private new LoginRequest _request => (LoginRequest)base._request; private new LoginResult _result { get => (LoginResult)base._result; set => base._result = value; } - public LoginHandler(IChatClient client, LoginRequest request) : base(client, request) + public LoginHandler(IShareClient client, LoginRequest request) : base(client, request) { _result = new LoginResult(); } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs index 161b5817b..caa7c56ac 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs @@ -14,7 +14,7 @@ public sealed class NickHandler : CmdHandlerBase { private new NickRequest _request => (NickRequest)base._request; private new NickResult _result { get => (NickResult)base._result; set => base._result = value; } - public NickHandler(IChatClient client, NickRequest request) : base(client, request) + public NickHandler(IShareClient client, NickRequest request) : base(client, request) { _result = new NickResult(); } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/PingHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/PingHandler.cs index c43f77edf..87a3a7464 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/PingHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/PingHandler.cs @@ -11,7 +11,7 @@ public sealed class PingHandler : LogedInHandlerBase { private new PingRequest _request => (PingRequest)base._request; private new PingResult _result { get => (PingResult)base._result; set => base._result = value; } - public PingHandler(IChatClient client, PingRequest request) : base(client, request){ } + public PingHandler(IShareClient client, PingRequest request) : base(client, request){ } protected override void DataOperation() { _result = new PingResult(); diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/QuitHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/QuitHandler.cs index 44af14a2b..ceb034d31 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/QuitHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/QuitHandler.cs @@ -4,6 +4,7 @@ using UniSpy.Server.Chat.Contract.Request.Channel; using UniSpy.Server.Chat.Contract.Request.General; using UniSpy.Server.Chat.Handler.CmdHandler.Channel; +using UniSpy.Server.Core.Abstraction.BaseClass; namespace UniSpy.Server.Chat.Handler.CmdHandler.General { @@ -12,7 +13,7 @@ public sealed class QuitHandler : LogedInHandlerBase { private new QuitRequest _request => (QuitRequest)base._request; // when a user disconnected with server we can call this function - public QuitHandler(IChatClient client, QuitRequest request) : base(client, request) { } + public QuitHandler(IShareClient client, QuitRequest request) : base(client, request) { } protected override void RequestCheck() { if (_request.RawRequest is null) @@ -41,6 +42,11 @@ protected override void DataOperation() // client is loged out _client.Info.IsLoggedIn = false; } + // we remove client from ClientManager + if (_client.Info.IsRemoteClient) + { + ClientManagerBase.RemoveClient(_client.Connection.RemoteIPEndPoint); + } } } } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/SetKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/SetKeyHandler.cs index 40bcdd53c..38021546c 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/SetKeyHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/SetKeyHandler.cs @@ -10,7 +10,7 @@ namespace UniSpy.Server.Chat.Handler.CmdHandler.General public sealed class SetKeyHandler : LogedInHandlerBase { private new SetKeyRequest _request => (SetKeyRequest)base._request; - public SetKeyHandler(IChatClient client, SetKeyRequest request) : base(client, request) { } + public SetKeyHandler(IShareClient client, SetKeyRequest request) : base(client, request) { } protected override void DataOperation() { diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/UserHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/UserHandler.cs index ece6a44f3..e00bfb1b8 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/UserHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/UserHandler.cs @@ -8,7 +8,7 @@ namespace UniSpy.Server.Chat.Handler.CmdHandler.General public sealed class UserHandler : CmdHandlerBase { private new UserRequest _request => (UserRequest)base._request; - public UserHandler(IChatClient client, UserRequest request) : base(client, request){ } + public UserHandler(IShareClient client, UserRequest request) : base(client, request){ } protected override void DataOperation() { diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/UserIPHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/UserIPHandler.cs index ca6aebcae..375918b2d 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/UserIPHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/UserIPHandler.cs @@ -12,7 +12,7 @@ public sealed class UserIPHandler : CmdHandlerBase private new UserIPRequest _request => (UserIPRequest)base._request; private new UserIPResult _result { get => (UserIPResult)base._result; set => base._result = value; } - public UserIPHandler(IChatClient client, UserIPRequest request) : base(client, request){ } + public UserIPHandler(IShareClient client, UserIPRequest request) : base(client, request){ } protected override void DataOperation() { diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs index dca5ba2ae..126754139 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs @@ -21,7 +21,7 @@ public sealed class WhoHandler : LogedInHandlerBase { private new WhoRequest _request => (WhoRequest)base._request; private new WhoResult _result { get => (WhoResult)base._result; set => base._result = value; } - public WhoHandler(IChatClient client, WhoRequest request) : base(client, request) { } + public WhoHandler(IShareClient client, WhoRequest request) : base(client, request) { } protected override void DataOperation() { diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/WhoIsHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/WhoIsHandler.cs index 251c19734..0e26dbdf0 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/WhoIsHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/WhoIsHandler.cs @@ -13,7 +13,7 @@ public sealed class WhoIsHandler : CmdHandlerBase private new WhoIsRequest _request => (WhoIsRequest)base._request; private new WhoIsResult _result { get => (WhoIsResult)base._result; set => base._result = value; } private ClientInfo _clientInfo; - public WhoIsHandler(IChatClient client, WhoIsRequest request) : base(client, request) { } + public WhoIsHandler(IShareClient client, WhoIsRequest request) : base(client, request) { } protected override void RequestCheck() { diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Message/AtmHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Message/AtmHandler.cs index dff5cceb5..bf7be6ad0 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Message/AtmHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Message/AtmHandler.cs @@ -13,7 +13,7 @@ public sealed class AtmHandler : MessageHandlerBase { new AtmRequest _request => (AtmRequest)base._request; new AtmResult _result{ get => (AtmResult)base._result; set => base._result = value; } - public AtmHandler(IChatClient client, AtmRequest request) : base(client, request) + public AtmHandler(IShareClient client, AtmRequest request) : base(client, request) { _result = new AtmResult(); } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Message/NoticeHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Message/NoticeHandler.cs index 774488a80..a79364c69 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Message/NoticeHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Message/NoticeHandler.cs @@ -11,7 +11,7 @@ public sealed class NoticeHandler : MessageHandlerBase { private new NoticeRequest _request => (NoticeRequest)base._request; private new NoticeResult _result{ get => (NoticeResult)base._result; set => base._result = value; } - public NoticeHandler(IChatClient client, NoticeRequest request) : base(client, request) + public NoticeHandler(IShareClient client, NoticeRequest request) : base(client, request) { _result = new NoticeResult(); } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs index 23f8f8a47..1ae99bfb6 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs @@ -12,7 +12,7 @@ public sealed class PrivateHandler : MessageHandlerBase { private new PrivateRequest _request => (PrivateRequest)base._request; private new PrivateResult _result { get => (PrivateResult)base._result; set => base._result = value; } - public PrivateHandler(IChatClient client, PrivateRequest request) : base(client, request) + public PrivateHandler(IShareClient client, PrivateRequest request) : base(client, request) { _result = new PrivateResult(); } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Message/UtmHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Message/UtmHandler.cs index cbf9026f1..d5cb499f1 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Message/UtmHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Message/UtmHandler.cs @@ -13,7 +13,7 @@ public sealed class UtmHandler : MessageHandlerBase { private new UtmRequest _request => (UtmRequest)base._request; private new UtmResult _result { get => (UtmResult)base._result; set => base._result = value; } - public UtmHandler(IChatClient client, UtmRequest request) : base(client, request) + public UtmHandler(IShareClient client, UtmRequest request) : base(client, request) { _result = new UtmResult(); } diff --git a/src/Servers/Chat/src/Handler/CmdSwitcher.cs b/src/Servers/Chat/src/Handler/CmdSwitcher.cs index 6f590a1b2..dec8bbbe2 100755 --- a/src/Servers/Chat/src/Handler/CmdSwitcher.cs +++ b/src/Servers/Chat/src/Handler/CmdSwitcher.cs @@ -19,8 +19,8 @@ namespace UniSpy.Server.Chat.Handler public sealed class CmdSwitcher : CmdSwitcherBase { private new string _rawRequest => (string)base._rawRequest; - private new IChatClient _client => (IChatClient)base._client; - public CmdSwitcher(IChatClient client, string rawRequest) : base(client, rawRequest) { } + private new IShareClient _client => (IShareClient)base._client; + public CmdSwitcher(IShareClient client, string rawRequest) : base(client, rawRequest) { } protected override void ProcessRawRequest() { diff --git a/src/Servers/Chat/test/RedisChatChannelTest.cs b/src/Servers/Chat/test/RedisChatChannelTest.cs index 6419e10ce..6772b1284 100644 --- a/src/Servers/Chat/test/RedisChatChannelTest.cs +++ b/src/Servers/Chat/test/RedisChatChannelTest.cs @@ -70,7 +70,7 @@ public void RedisChannel() public void Crypt() { - var request1 = new DisconnectRequest(); + var request1 = new QuitRequest() { Reason = "client disconnected." }; var client = MockObject.CreateClient() as Client; client.Info.IsLoggedIn = true; @@ -116,7 +116,7 @@ public void GeneralTest() // Client.ClientPool.Remove(client1.Connection.RemoteIPEndPoint, out _); ClientManager.RemoveClient(client1); var remoteClient = client1.GetRemoteClient() as ITestClient; - ClientManager.AddClient((IChatClient)remoteClient); + ClientManager.AddClient((IShareClient)remoteClient); foreach (var r in request1) { var count = ClientManager.ClientPool.Count; @@ -146,7 +146,7 @@ public void GeneralTest() // "PART #GSP!worms3!MJ0NJ4c3aM :Left Game\r\n" }; var client2 = MockObject.CreateClient(port: 1235) as ITestClient; - ClientManager.AddClient((IChatClient)client2); + ClientManager.AddClient((IShareClient)client2); foreach (var r in request2) { var count = ClientManager.ClientPool.Count; diff --git a/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/LoggedInCmdHandlerBase.cs b/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/LoggedInCmdHandlerBase.cs index ebb51db11..85cc116d5 100755 --- a/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/LoggedInCmdHandlerBase.cs +++ b/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/LoggedInCmdHandlerBase.cs @@ -1,5 +1,6 @@ using UniSpy.Server.PresenceSearchPlayer.Exception.General; using UniSpy.Server.Core.Abstraction.Interface; +using UniSpy.Server.PresenceConnectionManager.Aggregate.Redis; namespace UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass { @@ -10,11 +11,34 @@ public LoggedInCmdHandlerBase(IClient client, IRequest request) : base(client, r } protected override void RequestCheck() { - if(_client.Info.LoginStat!=Enumerate.LoginStatus.Completed) + if (_client.Info.LoginStat != Enumerate.LoginStatus.Completed) { throw new GPException("You are not logged in, please login first."); } base.RequestCheck(); } + public override void Handle() + { + base.Handle(); + try + { + // we publish this message to redis channel + PublishMessage(); + } + catch (Exception ex) + { + HandleException(ex); + } + } + protected virtual void PublishMessage() + { + // we do not publish message when the message is received from remote client + if (_client.Info.IsRemoteClient) + { + return; + } + var msg = new UserInfoMessage(_client.GetRemoteClient()); + Application.Server.UserInfoChannel.PublishMessage(msg); + } } } diff --git a/src/Servers/PresenceConnectionManager/src/Abstraction/Interface/IShareClient.cs b/src/Servers/PresenceConnectionManager/src/Abstraction/Interface/IShareClient.cs new file mode 100644 index 000000000..f0f078cdd --- /dev/null +++ b/src/Servers/PresenceConnectionManager/src/Abstraction/Interface/IShareClient.cs @@ -0,0 +1,12 @@ +using UniSpy.Server.Core.Abstraction.Interface; +using UniSpy.Server.PresenceConnectionManager.Aggregate; +using UniSpy.Server.PresenceConnectionManager.Application; + +namespace UniSpy.Server.PresenceConnectionManager.Abstraction.Interface +{ + public interface IShareClient : IClient, ITestClient + { + public new ClientInfo Info { get; } + public RemoteClient GetRemoteClient(); + } +} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoChannel.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoChannel.cs new file mode 100644 index 000000000..f548acb02 --- /dev/null +++ b/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoChannel.cs @@ -0,0 +1,34 @@ +using UniSpy.Server.Core.Abstraction.BaseClass; +using UniSpy.Server.Core.Extension.Redis; +using UniSpy.Server.PresenceConnectionManager.Abstraction.Interface; +using UniSpy.Server.PresenceConnectionManager.Application; + +namespace UniSpy.Server.PresenceConnectionManager.Aggregate.Redis +{ + public class UserInfoChannel : RedisChannelBase + { + public UserInfoChannel() : base(RedisChannelName.PresenceConnectionManagerUserInfoChannel) + { + } + public override void ReceivedMessage(UserInfoMessage message) + { + // base.ReceivedMessage(message); + if (message.Client.Server.Id == ServerLauncher.Server.Id) + { + return; + } + + IShareClient client = (IShareClient)ClientManager.GetClient(message.Client.Connection.RemoteIPEndPoint); + if (client is null) + { + ClientManager.AddClient(message.Client); + client = message.Client; + } + else + { + // we update the remote client info + ((RemoteClient)client).Info = message.Client.Info; + } + } + } +} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoMessage.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoMessage.cs new file mode 100644 index 000000000..ea0e5d35d --- /dev/null +++ b/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoMessage.cs @@ -0,0 +1,18 @@ +using System.Net; +using System; +using UniSpy.Server.PresenceConnectionManager.Application; +using UniSpy.Server.Core.Misc; +using Newtonsoft.Json; + +namespace UniSpy.Server.PresenceConnectionManager.Aggregate.Redis +{ + public class UserInfoMessage + { + public RemoteClient Client { get; private set; } + public UserInfoMessage() { } + public UserInfoMessage(RemoteClient client) + { + Client = client; + } + } +} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/RemoteClient.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/RemoteClient.cs new file mode 100644 index 000000000..bc9622a9c --- /dev/null +++ b/src/Servers/PresenceConnectionManager/src/Aggregate/RemoteClient.cs @@ -0,0 +1,47 @@ +using Newtonsoft.Json; +using UniSpy.Server.Core.Abstraction.BaseClass; +using UniSpy.Server.Core.Abstraction.Interface; +using UniSpy.Server.Core.Encryption; +using UniSpy.Server.Core.Logging; +using UniSpy.Server.Core.Network; +using UniSpy.Server.PresenceConnectionManager.Abstraction.Interface; +using UniSpy.Server.PresenceConnectionManager.Application; +using UniSpy.Server.PresenceConnectionManager.Handler; + +namespace UniSpy.Server.PresenceConnectionManager.Aggregate +{ + + public class RemoteClient : IShareClient + { + public bool IsLogRaw { get; set; } + [JsonConverter(typeof(ConcreteTypeConverter))] + public IConnection Connection { get; set; } + public ICryptography Crypto => null; + [JsonConverter(typeof(ConcreteTypeConverter))] + public ClientInfoBase Info { get; set; } + ClientInfo IShareClient.Info => (ClientInfo)Info; + [JsonConverter(typeof(ConcreteTypeConverter))] + public IServer Server { get; set; } + /// + /// using for json deserialization + /// + /// public RemoteClient() { } + public RemoteClient(Client client) + { + Connection = new RemoteTcpConnection(client.Connection, new RemoteTcpConnectionManager()); + Server = new RemoteServer(client.Server); + Info = client.Info.DeepCopy(); + ((ClientInfo)Info).IsRemoteClient = true; + } + + public RemoteClient GetRemoteClient() => this; + + public void Send(IResponse response) { } + public void TestReceived(byte[] buffer) + { + this.LogNetworkReceiving(buffer); + var switcher = new CmdSwitcher(this, UniSpyEncoding.GetString(buffer)); + switcher.Handle(); + } + } +} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Application/Client.cs b/src/Servers/PresenceConnectionManager/src/Application/Client.cs index 78daaff2d..c137a6bf9 100644 --- a/src/Servers/PresenceConnectionManager/src/Application/Client.cs +++ b/src/Servers/PresenceConnectionManager/src/Application/Client.cs @@ -5,16 +5,20 @@ using UniSpy.Server.Core.Abstraction.Interface; using UniSpy.Server.Core.Logging; using UniSpy.Server.Core.Encryption; +using UniSpy.Server.PresenceConnectionManager.Abstraction.Interface; +using UniSpy.Server.PresenceConnectionManager.Aggregate; namespace UniSpy.Server.PresenceConnectionManager.Application { - public sealed class Client : ClientBase + public sealed class Client : ClientBase, IShareClient { public new ITcpConnection Connection => (ITcpConnection)base.Connection; - public new ClientInfo Info { get => (ClientInfo)base.Info; private set => base.Info = value; } + public new ClientInfo Info { get => (ClientInfo)base.Info; set => base.Info = value; } + private RemoteClient _remoteClient; public Client(IConnection connection, IServer server) : base(connection, server) { Info = new ClientInfo(); + _remoteClient = new RemoteClient(this); } protected override void OnConnected() { @@ -34,5 +38,11 @@ protected override void OnConnected() } protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, UniSpyEncoding.GetString((byte[])buffer)); + + public RemoteClient GetRemoteClient() + { + _remoteClient.Info = Info; + return _remoteClient; + } } } \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs b/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs index e92f0652d..29c1aff5e 100755 --- a/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs +++ b/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs @@ -19,8 +19,14 @@ public sealed class ClientInfo : ClientInfoBase public User UserInfo { get; set; } public Subprofile SubProfileInfo { get; set; } public Profile ProfileInfo { get; set; } + public bool IsRemoteClient { get; set; } public ClientInfo() { } + public ClientInfo DeepCopy() + { + var infoCopy = (ClientInfo)this.MemberwiseClone(); + return infoCopy; + } } } diff --git a/src/Servers/PresenceConnectionManager/src/Application/Server.cs b/src/Servers/PresenceConnectionManager/src/Application/Server.cs index 0f21664e8..b7002ff9e 100644 --- a/src/Servers/PresenceConnectionManager/src/Application/Server.cs +++ b/src/Servers/PresenceConnectionManager/src/Application/Server.cs @@ -2,11 +2,13 @@ using UniSpy.Server.Core.Abstraction.BaseClass; using UniSpy.Server.Core.Network.Tcp.Server; using UniSpy.Server.Core.Abstraction.Interface; +using UniSpy.Server.PresenceConnectionManager.Aggregate.Redis; namespace UniSpy.Server.PresenceConnectionManager.Application { public sealed class Server : ServerBase { + public static readonly UserInfoChannel UserInfoChannel = new UserInfoChannel(); static Server() { _name = "PresenceConnectionManager"; @@ -14,6 +16,11 @@ static Server() public Server() { } public Server(IConnectionManager manager) : base(manager) { } protected override IClient CreateClient(IConnection connection) => new Client(connection, this); - protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new TcpConnectionManager(IPEndPoint.Parse("0.0.0.0:29900")); + protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new TcpConnectionManager(endPoint); + public override void Start() + { + base.Start(); + UserInfoChannel.Subscribe(); + } } } \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Application/ServerLauncher.cs b/src/Servers/PresenceConnectionManager/src/Application/ServerLauncher.cs index 333f93dc1..de9024218 100644 --- a/src/Servers/PresenceConnectionManager/src/Application/ServerLauncher.cs +++ b/src/Servers/PresenceConnectionManager/src/Application/ServerLauncher.cs @@ -6,6 +6,7 @@ namespace UniSpy.Server.PresenceConnectionManager.Application { public sealed class ServerLauncher : ServerLauncherBase { + public static IServer Server => ServerInstances[0]; protected override List LaunchNetworkService() => new List { new Server() }; } } \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdSwitcher.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdSwitcher.cs index 215b48a38..f5a183543 100755 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdSwitcher.cs +++ b/src/Servers/PresenceConnectionManager/src/Handler/CmdSwitcher.cs @@ -10,6 +10,7 @@ using UniSpy.Server.Core.Abstraction.Interface; using UniSpy.Server.PresenceSearchPlayer.Contract.Request; using UniSpy.Server.PresenceConnectionManager.Application; +using UniSpy.Server.PresenceConnectionManager.Abstraction.Interface; namespace UniSpy.Server.PresenceConnectionManager.Handler { @@ -17,7 +18,7 @@ public sealed class CmdSwitcher : CmdSwitcherBase { private new string _rawRequest => (string)base._rawRequest; private new Client _client => (Client)base._client; - public CmdSwitcher(Client client, string rawRequest) : base(client, rawRequest) + public CmdSwitcher(IShareClient client, string rawRequest) : base(client, rawRequest) { } protected override void ProcessRawRequest() From a6aa5a112bdf8a6362084d3bdbca474300acb358 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 27 Jun 2023 11:01:39 +0800 Subject: [PATCH 021/231] fix(pcm): object can not be jsonified --- src/Servers/Chat/src/Application/Client.cs | 5 ++ src/Servers/Chat/src/Application/Server.cs | 14 ++++ .../Interface/IStorageOperation.cs | 6 +- .../src/Aggregate/Redis/UserInfoMessage.cs | 1 + .../src/Aggregate/RemoteClient.cs | 2 +- .../src/Application/Client.cs | 5 ++ .../src/Application/ClientInfo.cs | 78 +++++++++++++++++-- .../src/Application/Server.cs | 13 ++++ .../src/Application/StorageOperation.cs | 12 +-- .../CmdHandler/General/LoginHandler.cs | 6 +- 10 files changed, 124 insertions(+), 18 deletions(-) diff --git a/src/Servers/Chat/src/Application/Client.cs b/src/Servers/Chat/src/Application/Client.cs index 67ad5158d..f64c10b85 100644 --- a/src/Servers/Chat/src/Application/Client.cs +++ b/src/Servers/Chat/src/Application/Client.cs @@ -24,6 +24,11 @@ public Client(IConnection connection, IServer server) : base(connection, server) Info = new ClientInfo(); _remoteClient = new RemoteClient(this); } + public Client(IConnection connection, IServer server, ClientInfo info) : this(connection, server) + { + Info = info; + _remoteClient = new RemoteClient(this); + } protected override void OnConnected() { Info.IsRemoteClient = false; diff --git a/src/Servers/Chat/src/Application/Server.cs b/src/Servers/Chat/src/Application/Server.cs index 171d3dea9..9bddc2987 100644 --- a/src/Servers/Chat/src/Application/Server.cs +++ b/src/Servers/Chat/src/Application/Server.cs @@ -3,6 +3,7 @@ using UniSpy.Server.Core.Network.Tcp.Server; using UniSpy.Server.Core.Abstraction.Interface; using System.Net; +using UniSpy.Server.Chat.Abstraction.Interface; namespace UniSpy.Server.Chat.Application { @@ -26,5 +27,18 @@ public override void Start() protected override IClient CreateClient(IConnection connection) => new Client(connection, this); protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new TcpConnectionManager(endPoint); + protected override IClient HandleConnectionInitialization(IConnection connection) + { + var client = (IShareClient)base.HandleConnectionInitialization(connection); + if (client.Info.IsRemoteClient) + { + var info = client.Info; + info.IsRemoteClient = false; + // we parse info to our client + client = new Client(connection, this, info); + + } + return client; + } } } \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Abstraction/Interface/IStorageOperation.cs b/src/Servers/PresenceConnectionManager/src/Abstraction/Interface/IStorageOperation.cs index 45fafebac..2ed717e1e 100644 --- a/src/Servers/PresenceConnectionManager/src/Abstraction/Interface/IStorageOperation.cs +++ b/src/Servers/PresenceConnectionManager/src/Abstraction/Interface/IStorageOperation.cs @@ -9,9 +9,9 @@ public interface IStorageOperation List GetBlockedProfileIds(int profileId, int namespaceId); List GetFriendProfileIds(int profileId, int namespaceId); void DeleteFriendByProfileId(int profileId, int targetId, int namespaceId); - (User, Profile, Subprofile) GetUsersInfos(string email, string nickName); - (User, Profile, Subprofile) GetUsersInfos(string uniqueNick, int namespaceId); - (User, Profile, Subprofile) GetUsersInfos(string authToken, int partnerId, int namespaceId); + (int, int, int) GetUsersInfos(string email, string nickName); + (int, int, int) GetUsersInfos(string uniqueNick, int namespaceId); + (int, int, int) GetUsersInfos(string authToken, int partnerId, int namespaceId); void UpdateBlockInfo(int targetId, int profileId, int namespaceId); void UpdateFriendInfo(int targetId, int profileId, int namespaceId); void UpdateProfileInfo(Profile profile); diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoMessage.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoMessage.cs index ea0e5d35d..cff60f120 100644 --- a/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoMessage.cs +++ b/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoMessage.cs @@ -8,6 +8,7 @@ namespace UniSpy.Server.PresenceConnectionManager.Aggregate.Redis { public class UserInfoMessage { + [JsonProperty] public RemoteClient Client { get; private set; } public UserInfoMessage() { } public UserInfoMessage(RemoteClient client) diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/RemoteClient.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/RemoteClient.cs index bc9622a9c..f15affa19 100644 --- a/src/Servers/PresenceConnectionManager/src/Aggregate/RemoteClient.cs +++ b/src/Servers/PresenceConnectionManager/src/Aggregate/RemoteClient.cs @@ -25,7 +25,7 @@ public class RemoteClient : IShareClient /// /// using for json deserialization /// - /// public RemoteClient() { } + public RemoteClient() { } public RemoteClient(Client client) { Connection = new RemoteTcpConnection(client.Connection, new RemoteTcpConnectionManager()); diff --git a/src/Servers/PresenceConnectionManager/src/Application/Client.cs b/src/Servers/PresenceConnectionManager/src/Application/Client.cs index c137a6bf9..35a8f9102 100644 --- a/src/Servers/PresenceConnectionManager/src/Application/Client.cs +++ b/src/Servers/PresenceConnectionManager/src/Application/Client.cs @@ -20,6 +20,11 @@ public Client(IConnection connection, IServer server) : base(connection, server) Info = new ClientInfo(); _remoteClient = new RemoteClient(this); } + public Client(IConnection connection, IServer server, ClientInfo info) : this(connection, server) + { + Info = info; + _remoteClient = new RemoteClient(this); + } protected override void OnConnected() { base.OnConnected(); diff --git a/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs b/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs index 29c1aff5e..529963f07 100755 --- a/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs +++ b/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs @@ -3,11 +3,16 @@ using UniSpy.Server.Core.Abstraction.BaseClass; using UniSpy.Server.Core.Database.DatabaseModel; using UniSpy.Server.PresenceConnectionManager.Aggregate.Misc; +using System.Linq; +using Newtonsoft.Json; namespace UniSpy.Server.PresenceConnectionManager.Application { public sealed class ClientInfo : ClientInfoBase { + public int? UserId { get; set; } + public int? ProfileId { get; set; } + public int? SubProfileId { get; set; } public const ushort SessionKey = 1111; public const string LoginTicket = "0000000000000000000000__"; public DateTime CreatedTime { get; private set; } = DateTime.Now; @@ -16,13 +21,76 @@ public sealed class ClientInfo : ClientInfoBase public UserStatus Status { get; set; } = new UserStatus(); public UserStatusInfo StatusInfo { get; set; } public LoginStatus LoginStat { get; set; } = LoginStatus.Connected; - public User UserInfo { get; set; } - public Subprofile SubProfileInfo { get; set; } - public Profile ProfileInfo { get; set; } - public bool IsRemoteClient { get; set; } - public ClientInfo() + + [JsonIgnore] + private User _userInfo; + [JsonIgnore] + private Profile _profileInfo; + [JsonIgnore] + private Subprofile _subProfileInfo; + [JsonIgnore] + public User UserInfo + { + get + { + if (UserId is null) + { + throw new UniSpy.Exception("UserId is not setted"); + } + if (_userInfo is null) + { + using (var db = new UniSpyContext()) + { + _userInfo = db.Users.Where(s => s.UserId == UserId).First(); + } + } + return _userInfo; + } + // set => _userInfo = value; + } + [JsonIgnore] + public Profile ProfileInfo { + get + { + if (ProfileId is null) + { + throw new UniSpy.Exception("ProfileId is not set"); + } + if (_profileInfo is null) + { + using (var db = new UniSpyContext()) + { + _profileInfo = db.Profiles.Where(s => s.ProfileId == ProfileId).First(); + } + } + return _profileInfo; + } + // set => _profileInfo = value; } + [JsonIgnore] + public Subprofile SubProfileInfo + { + get + { + if (SubProfileId is null) + { + throw new UniSpy.Exception("Subprofile is not set"); + } + if (_subProfileInfo is null) + { + using (var db = new UniSpyContext()) + { + _subProfileInfo = db.Subprofiles.Where(s => s.SubProfileId == SubProfileId).First(); + } + } + return _subProfileInfo; + } + // set => _subProfileInfo = value; + } + + public bool IsRemoteClient { get; set; } + public ClientInfo() { } public ClientInfo DeepCopy() { var infoCopy = (ClientInfo)this.MemberwiseClone(); diff --git a/src/Servers/PresenceConnectionManager/src/Application/Server.cs b/src/Servers/PresenceConnectionManager/src/Application/Server.cs index b7002ff9e..47cd47c7d 100644 --- a/src/Servers/PresenceConnectionManager/src/Application/Server.cs +++ b/src/Servers/PresenceConnectionManager/src/Application/Server.cs @@ -3,6 +3,7 @@ using UniSpy.Server.Core.Network.Tcp.Server; using UniSpy.Server.Core.Abstraction.Interface; using UniSpy.Server.PresenceConnectionManager.Aggregate.Redis; +using UniSpy.Server.PresenceConnectionManager.Abstraction.Interface; namespace UniSpy.Server.PresenceConnectionManager.Application { @@ -17,6 +18,18 @@ public Server() { } public Server(IConnectionManager manager) : base(manager) { } protected override IClient CreateClient(IConnection connection) => new Client(connection, this); protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new TcpConnectionManager(endPoint); + protected override IClient HandleConnectionInitialization(IConnection connection) + { + var client = (IShareClient)base.HandleConnectionInitialization(connection); + if (client.Info.IsRemoteClient) + { + var info = client.Info; + info.IsRemoteClient = false; + // we parse the info into our client + client = new Client(connection, this, info); + } + return client; + } public override void Start() { base.Start(); diff --git a/src/Servers/PresenceConnectionManager/src/Application/StorageOperation.cs b/src/Servers/PresenceConnectionManager/src/Application/StorageOperation.cs index fca286878..e15494957 100644 --- a/src/Servers/PresenceConnectionManager/src/Application/StorageOperation.cs +++ b/src/Servers/PresenceConnectionManager/src/Application/StorageOperation.cs @@ -111,7 +111,7 @@ join u in db.Users on p.Userid equals u.UserId } } - public (User, Profile, Subprofile) GetUsersInfos(string email, string nickName) + public (int, int, int) GetUsersInfos(string email, string nickName) { using (var db = new UniSpyContext()) { @@ -127,11 +127,11 @@ join n in db.Subprofiles on p.ProfileId equals n.ProfileId { throw new GPLoginBadProfileException("email and nick dose not exist"); } - return (info.u, info.p, info.n); + return (info.u.UserId, info.p.ProfileId, info.n.SubProfileId); } } - public (User, Profile, Subprofile) GetUsersInfos(string uniqueNick, int namespaceId) + public (int, int, int) GetUsersInfos(string uniqueNick, int namespaceId) { using (var db = new UniSpyContext()) @@ -148,11 +148,11 @@ join u in db.Users on p.Userid equals u.UserId { throw new GPLoginBadUniquenickException($"The uniquenick: {uniqueNick} is invalid."); } - return (info.u, info.p, info.n); + return (info.u.UserId, info.p.ProfileId, info.n.SubProfileId); } } - public (User, Profile, Subprofile) GetUsersInfos(string authToken, int partnerId, int namespaceId) + public (int, int, int) GetUsersInfos(string authToken, int partnerId, int namespaceId) { using (var db = new UniSpyContext()) { @@ -169,7 +169,7 @@ join n in db.Subprofiles on p.ProfileId equals n.ProfileId { throw new GPLoginBadPreAuthException("The pre-authentication token is invalid."); } - return (info.u, info.p, info.n); + return (info.u.UserId, info.p.ProfileId, info.n.SubProfileId); } } diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LoginHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LoginHandler.cs index b38b0ff99..2b3140527 100755 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LoginHandler.cs +++ b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LoginHandler.cs @@ -108,18 +108,18 @@ private void NickEmailLogin() throw new GPLoginBadEmailException($"email: {_request.Email} is invalid."); } - (_client.Info.UserInfo, _client.Info.ProfileInfo, _client.Info.SubProfileInfo) = PresenceConnectionManager.Application.StorageOperation.Persistance.GetUsersInfos(_request.Email, _request.Nick); + (_client.Info.UserId, _client.Info.ProfileId, _client.Info.SubProfileId) = PresenceConnectionManager.Application.StorageOperation.Persistance.GetUsersInfos(_request.Email, _request.Nick); } private void UniquenickLogin() { - (_client.Info.UserInfo, _client.Info.ProfileInfo, _client.Info.SubProfileInfo) = PresenceConnectionManager.Application.StorageOperation.Persistance.GetUsersInfos(_request.UniqueNick, (int)_request.NamespaceID); + (_client.Info.UserId, _client.Info.ProfileId, _client.Info.SubProfileId) = PresenceConnectionManager.Application.StorageOperation.Persistance.GetUsersInfos(_request.UniqueNick, (int)_request.NamespaceID); } private void AuthtokenLogin() { - (_client.Info.UserInfo, _client.Info.ProfileInfo, _client.Info.SubProfileInfo) = PresenceConnectionManager.Application.StorageOperation.Persistance.GetUsersInfos(_request.AuthToken, (int)_request.PartnerID, (int)_request.NamespaceID); + (_client.Info.UserId, _client.Info.ProfileId, _client.Info.SubProfileId) = PresenceConnectionManager.Application.StorageOperation.Persistance.GetUsersInfos(_request.AuthToken, (int)_request.PartnerID, (int)_request.NamespaceID); } private void IsChallengeCorrect() From 0bf05e0b203fedbf3638586dcc4fdda0fd1f1652 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 28 Jun 2023 20:57:11 +0800 Subject: [PATCH 022/231] fix(pcm): stack overflow when debugging clieninfo --- .../Core/src/Abstraction/BaseClass/ClientBase.cs | 9 +-------- src/Servers/Chat/src/Aggregate/RemoteClient.cs | 1 + .../Abstraction/BaseClass/LoggedInCmdHandlerBase.cs | 4 ++++ .../src/Aggregate/Redis/UserInfoChannel.cs | 13 ++++++++++--- .../src/Aggregate/RemoteClient.cs | 1 + .../src/Application/ClientInfo.cs | 3 --- 6 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs index 2b2f8ab17..7a9593c9f 100644 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs +++ b/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs @@ -88,14 +88,7 @@ protected virtual void OnReceived(object buffer) } // we let child class to create swicher for us var switcher = CreateSwitcher(buffer); - if (System.Diagnostics.Debugger.IsAttached) - { - switcher.Handle(); - } - else - { - Task.Run(() => switcher.Handle()); - } + switcher.Handle(); } protected virtual byte[] DecryptMessage(byte[] buffer) { diff --git a/src/Servers/Chat/src/Aggregate/RemoteClient.cs b/src/Servers/Chat/src/Aggregate/RemoteClient.cs index b57469906..5826b0966 100644 --- a/src/Servers/Chat/src/Aggregate/RemoteClient.cs +++ b/src/Servers/Chat/src/Aggregate/RemoteClient.cs @@ -20,6 +20,7 @@ public class RemoteClient : IShareClient public ICryptography Crypto { get; set; } [JsonConverter(typeof(ConcreteTypeConverter))] public ClientInfoBase Info { get; set; } + [JsonIgnore] ClientInfo IShareClient.Info => (ClientInfo)Info; [JsonConverter(typeof(ConcreteTypeConverter))] public IServer Server { get; set; } diff --git a/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/LoggedInCmdHandlerBase.cs b/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/LoggedInCmdHandlerBase.cs index 85cc116d5..3931ef350 100755 --- a/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/LoggedInCmdHandlerBase.cs +++ b/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/LoggedInCmdHandlerBase.cs @@ -37,6 +37,10 @@ protected virtual void PublishMessage() { return; } + if (_client.Info.LoginStat != Enumerate.LoginStatus.Completed) + { + return; + } var msg = new UserInfoMessage(_client.GetRemoteClient()); Application.Server.UserInfoChannel.PublishMessage(msg); } diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoChannel.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoChannel.cs index f548acb02..7b2bc9e4b 100644 --- a/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoChannel.cs +++ b/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoChannel.cs @@ -1,3 +1,4 @@ +using System; using UniSpy.Server.Core.Abstraction.BaseClass; using UniSpy.Server.Core.Extension.Redis; using UniSpy.Server.PresenceConnectionManager.Abstraction.Interface; @@ -12,13 +13,19 @@ public UserInfoChannel() : base(RedisChannelName.PresenceConnectionManagerUserIn } public override void ReceivedMessage(UserInfoMessage message) { - // base.ReceivedMessage(message); + // when we are in debug mode, the linq operation will not be executed, so we have to execute manually to make program do not crash + if (System.Diagnostics.Debugger.IsAttached) + { + _ = ((ClientInfo)(message.Client.Info)).UserInfo; + _ = ((ClientInfo)(message.Client.Info)).ProfileInfo; + _ = ((ClientInfo)(message.Client.Info)).SubProfileInfo; + } + if (message.Client.Server.Id == ServerLauncher.Server.Id) { return; } - - IShareClient client = (IShareClient)ClientManager.GetClient(message.Client.Connection.RemoteIPEndPoint); + var client = (IShareClient)ClientManager.GetClient(message.Client.Connection.RemoteIPEndPoint); if (client is null) { ClientManager.AddClient(message.Client); diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/RemoteClient.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/RemoteClient.cs index f15affa19..9fde82013 100644 --- a/src/Servers/PresenceConnectionManager/src/Aggregate/RemoteClient.cs +++ b/src/Servers/PresenceConnectionManager/src/Aggregate/RemoteClient.cs @@ -19,6 +19,7 @@ public class RemoteClient : IShareClient public ICryptography Crypto => null; [JsonConverter(typeof(ConcreteTypeConverter))] public ClientInfoBase Info { get; set; } + [JsonIgnore] ClientInfo IShareClient.Info => (ClientInfo)Info; [JsonConverter(typeof(ConcreteTypeConverter))] public IServer Server { get; set; } diff --git a/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs b/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs index 529963f07..db2132173 100755 --- a/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs +++ b/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs @@ -46,7 +46,6 @@ public User UserInfo } return _userInfo; } - // set => _userInfo = value; } [JsonIgnore] public Profile ProfileInfo @@ -66,7 +65,6 @@ public Profile ProfileInfo } return _profileInfo; } - // set => _profileInfo = value; } [JsonIgnore] public Subprofile SubProfileInfo @@ -86,7 +84,6 @@ public Subprofile SubProfileInfo } return _subProfileInfo; } - // set => _subProfileInfo = value; } public bool IsRemoteClient { get; set; } From 5715cf403a139508f58b2cc2cea03413423cacf9 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 6 Jul 2023 19:19:09 +0800 Subject: [PATCH 023/231] update(nn): natneg version2 protocol --- .../src/Aggregate/Redis/NatAddressInfo.cs | 162 ++++++++++-------- .../src/Handler/CmdHandler/InitHandler.cs | 31 ++-- src/Servers/NatNegotiation/test/GameTest.cs | 24 +++ 3 files changed, 129 insertions(+), 88 deletions(-) diff --git a/src/Servers/NatNegotiation/src/Aggregate/Redis/NatAddressInfo.cs b/src/Servers/NatNegotiation/src/Aggregate/Redis/NatAddressInfo.cs index fa1c02112..fdbac7f78 100644 --- a/src/Servers/NatNegotiation/src/Aggregate/Redis/NatAddressInfo.cs +++ b/src/Servers/NatNegotiation/src/Aggregate/Redis/NatAddressInfo.cs @@ -75,95 +75,105 @@ public NatType NatType } } } - - - public NatInitInfo(List infos) + private void ProcessVersion2(List infos) { - AddressInfos = infos.Select((i) => new { i }).ToDictionary(a => ((NatPortType)a.i.PortType), a => a.i); - if (Version == 2) - { - if (!(AddressInfos.ContainsKey(NatPortType.NN1) + if (!(AddressInfos.ContainsKey(NatPortType.NN1) && AddressInfos.ContainsKey(NatPortType.NN2))) - { - throw new NatNegotiation.Exception("Incomplete init packets"); - } + { + throw new NatNegotiation.Exception("Incomplete init packets"); + } - if (AddressInfos[NatPortType.NN1].Cookie != AddressInfos[NatPortType.NN2].Cookie) - { - throw new NatNegotiation.Exception("Broken cookie"); - } - if (AddressInfos[NatPortType.NN1].Version != AddressInfos[NatPortType.NN2].Version) - { - throw new NatNegotiation.Exception("Broken version"); - } - if (AddressInfos[NatPortType.NN1].ClientIndex != AddressInfos[NatPortType.NN2].ClientIndex) - { - throw new NatNegotiation.Exception("Broken client index"); - } - if (!AddressInfos[NatPortType.NN1].PrivateIPEndPoint.Equals(AddressInfos[NatPortType.NN2].PrivateIPEndPoint)) - { - throw new NatNegotiation.Exception("Client is sending wrong initpacket."); - } - if (AddressInfos.ContainsKey(NatPortType.GP)) - { - if (AddressInfos[NatPortType.GP].Cookie != AddressInfos[NatPortType.NN1].Cookie || - AddressInfos[NatPortType.GP].Version != AddressInfos[NatPortType.NN1].Version || - AddressInfos[NatPortType.GP].ClientIndex != AddressInfos[NatPortType.NN1].ClientIndex || - AddressInfos[NatPortType.GP].UseGamePort != AddressInfos[NatPortType.NN1].UseGamePort) - { - throw new NatNegotiation.Exception("GP packet info is not correct"); - } - } + if (AddressInfos[NatPortType.NN1].Cookie != AddressInfos[NatPortType.NN2].Cookie) + { + throw new NatNegotiation.Exception("Broken cookie"); + } + if (AddressInfos[NatPortType.NN1].Version != AddressInfos[NatPortType.NN2].Version) + { + throw new NatNegotiation.Exception("Broken version"); } - else + if (AddressInfos[NatPortType.NN1].ClientIndex != AddressInfos[NatPortType.NN2].ClientIndex) { - // todo - // some game will not send GP packet to natneg server, currently do not know the reason of it, need more game for analysis. - // this will happen in GameClient - if (!(AddressInfos.ContainsKey(NatPortType.NN1) - && AddressInfos.ContainsKey(NatPortType.NN2) - && AddressInfos.ContainsKey(NatPortType.NN3))) + throw new NatNegotiation.Exception("Broken client index"); + } + // if (!AddressInfos[NatPortType.NN1].PrivateIPEndPoint.Equals(AddressInfos[NatPortType.NN2].PrivateIPEndPoint)) + // { + // throw new NatNegotiation.Exception("Client is sending wrong initpacket."); + // } + if (AddressInfos.ContainsKey(NatPortType.GP)) + { + if (AddressInfos[NatPortType.GP].Cookie != AddressInfos[NatPortType.NN1].Cookie || + AddressInfos[NatPortType.GP].Version != AddressInfos[NatPortType.NN1].Version || + AddressInfos[NatPortType.GP].ClientIndex != AddressInfos[NatPortType.NN1].ClientIndex || + AddressInfos[NatPortType.GP].UseGamePort != AddressInfos[NatPortType.NN1].UseGamePort) { - throw new NatNegotiation.Exception("Incomplete init packets"); + throw new NatNegotiation.Exception("GP packet info is not correct"); } + } + } + private void ProcessVersion3(List infos) + { + // todo + // some game will not send GP packet to natneg server, currently do not know the reason of it, need more game for analysis. + // this will happen in GameClient + if (!(AddressInfos.ContainsKey(NatPortType.NN1) + && AddressInfos.ContainsKey(NatPortType.NN2) + && AddressInfos.ContainsKey(NatPortType.NN3))) + { + throw new NatNegotiation.Exception("Incomplete init packets"); + } - if (AddressInfos[NatPortType.NN1].Cookie != AddressInfos[NatPortType.NN2].Cookie - || AddressInfos[NatPortType.NN1].Cookie != AddressInfos[NatPortType.NN3].Cookie) - { - throw new NatNegotiation.Exception("Broken cookie"); - } - if (AddressInfos[NatPortType.NN1].Version != AddressInfos[NatPortType.NN2].Version - || AddressInfos[NatPortType.NN1].Version != AddressInfos[NatPortType.NN3].Version) - { - throw new NatNegotiation.Exception("Broken version"); - } + if (AddressInfos[NatPortType.NN1].Cookie != AddressInfos[NatPortType.NN2].Cookie + || AddressInfos[NatPortType.NN1].Cookie != AddressInfos[NatPortType.NN3].Cookie) + { + throw new NatNegotiation.Exception("Broken cookie"); + } + if (AddressInfos[NatPortType.NN1].Version != AddressInfos[NatPortType.NN2].Version + || AddressInfos[NatPortType.NN1].Version != AddressInfos[NatPortType.NN3].Version) + { + throw new NatNegotiation.Exception("Broken version"); + } - if (AddressInfos[NatPortType.NN1].ClientIndex != AddressInfos[NatPortType.NN2].ClientIndex - || AddressInfos[NatPortType.NN1].ClientIndex != AddressInfos[NatPortType.NN3].ClientIndex) - { - throw new NatNegotiation.Exception("Broken client index"); - } - if (AddressInfos[NatPortType.NN1].UseGamePort != AddressInfos[NatPortType.NN2].UseGamePort - || AddressInfos[NatPortType.NN1].UseGamePort != AddressInfos[NatPortType.NN3].UseGamePort) - { - throw new NatNegotiation.Exception("Broken use game port"); - } - if (!AddressInfos[NatPortType.NN2].PrivateIPEndPoint.Equals(AddressInfos[NatPortType.NN3].PrivateIPEndPoint)) - { - throw new NatNegotiation.Exception("Client is sending wrong initpacket."); - } - if (AddressInfos.ContainsKey(NatPortType.GP)) + if (AddressInfos[NatPortType.NN1].ClientIndex != AddressInfos[NatPortType.NN2].ClientIndex + || AddressInfos[NatPortType.NN1].ClientIndex != AddressInfos[NatPortType.NN3].ClientIndex) + { + throw new NatNegotiation.Exception("Broken client index"); + } + if (AddressInfos[NatPortType.NN1].UseGamePort != AddressInfos[NatPortType.NN2].UseGamePort + || AddressInfos[NatPortType.NN1].UseGamePort != AddressInfos[NatPortType.NN3].UseGamePort) + { + throw new NatNegotiation.Exception("Broken use game port"); + } + if (!AddressInfos[NatPortType.NN2].PrivateIPEndPoint.Equals(AddressInfos[NatPortType.NN3].PrivateIPEndPoint)) + { + throw new NatNegotiation.Exception("Client is sending wrong initpacket."); + } + if (AddressInfos.ContainsKey(NatPortType.GP)) + { + if (AddressInfos[NatPortType.GP].Cookie != AddressInfos[NatPortType.NN1].Cookie || + AddressInfos[NatPortType.GP].Version != AddressInfos[NatPortType.NN1].Version || + AddressInfos[NatPortType.GP].ClientIndex != AddressInfos[NatPortType.NN1].ClientIndex || + AddressInfos[NatPortType.GP].UseGamePort != AddressInfos[NatPortType.NN1].UseGamePort) { - if (AddressInfos[NatPortType.GP].Cookie != AddressInfos[NatPortType.NN1].Cookie || - AddressInfos[NatPortType.GP].Version != AddressInfos[NatPortType.NN1].Version || - AddressInfos[NatPortType.GP].ClientIndex != AddressInfos[NatPortType.NN1].ClientIndex || - AddressInfos[NatPortType.GP].UseGamePort != AddressInfos[NatPortType.NN1].UseGamePort) - { - throw new NatNegotiation.Exception("GP packet info is not correct"); - } + throw new NatNegotiation.Exception("GP packet info is not correct"); } } } + + public NatInitInfo(List infos) + { + AddressInfos = infos.Select((i) => new { i }).ToDictionary(a => ((NatPortType)a.i.PortType), a => a.i); + switch (this.Version) + { + case 1: + throw new NatNegotiation.Exception("version 1 do not implemented."); + case 2: + ProcessVersion2(infos); + break; + case 3: + ProcessVersion3(infos); + break; + } + } } public class RedisClient : UniSpy.Server.Core.Abstraction.BaseClass.RedisClient diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/InitHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/InitHandler.cs index c753311dd..ee6517e77 100755 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/InitHandler.cs +++ b/src/Servers/NatNegotiation/src/Handler/CmdHandler/InitHandler.cs @@ -65,21 +65,28 @@ protected override void Response() // Task.Run(() => StorageOperation.Persistance.UpdateInitInfo(_addressInfo)); StorageOperation.Persistance.UpdateInitInfo(_addressInfo); // init packet nn3 is the last one client send, although receiving nn3 does not mean we received other init packets, but we can use this as a flag to prevent start multiple connect handler - if (_request.Version == 2) - { - if (_request.PortType == NatPortType.NN2 && _client.Info.IsNeigotiating == false) - { - _client.Info.IsNeigotiating = true; - PrepareForConnectingAsync(); - } - } - else + switch (_request.Version) { - if (_request.PortType == NatPortType.NN3 && _client.Info.IsNeigotiating == false) - { + case 1: + throw new NatNegotiation.Exception("The natneg version 1 is not implemented"); + case 2: + if (_request.PortType == NatPortType.NN2 + && _client.Info.IsNeigotiating == false) + { + goto default; + } + break; + case 3: + if (_request.PortType == NatPortType.NN3 + && _client.Info.IsNeigotiating == false) + { + goto default; + } + break; + default: _client.Info.IsNeigotiating = true; PrepareForConnectingAsync(); - } + break; } } diff --git a/src/Servers/NatNegotiation/test/GameTest.cs b/src/Servers/NatNegotiation/test/GameTest.cs index dc1b3c725..0d107b80a 100644 --- a/src/Servers/NatNegotiation/test/GameTest.cs +++ b/src/Servers/NatNegotiation/test/GameTest.cs @@ -331,5 +331,29 @@ public void Flatout2pc20230519() } Thread.Sleep(10000); } + [Fact] + /// + /// NN1 packet's private port can not equal to NN2's port + /// + public void Gautletps2Test20230620() + { + var client1 = MockObject.CreateClient("192.168.9.13", 57430); + var client2 = MockObject.CreateClient("192.168.9.12", 59624); + + var requests = new List>() + { + new KeyValuePair(client1,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x0B,0x90,0x1C,0x24,0x02,0x00,0x01,0xC0,0xA8,0x09,0x0D,0x08,0x0C,0x67,0x61,0x75,0x6E,0x74,0x6C,0x65,0x74,0x70,0x73,0x32,0x00}), + new KeyValuePair(client1,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x0B,0x90,0x1C,0x24,0x00,0x00,0x01,0xC0,0xA8,0x09,0x0D,0x00,0x00,0x67,0x61,0x75,0x6E,0x74,0x6C,0x65,0x74,0x70,0x73,0x32,0x00}), + new KeyValuePair(client1,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x0B,0x90,0x1C,0x24,0x01,0x00,0x01,0xC0,0xA8,0x09,0x0D,0x00,0x00,0x67,0x61,0x75,0x6E,0x74,0x6C,0x65,0x74,0x70,0x73,0x32,0x00}), + new KeyValuePair(client2,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x0B,0x90,0x1C,0x24,0x00,0x01,0x01,0xC0,0xA8,0x09,0x0C,0x00,0x00,0x67,0x61,0x75,0x6E,0x74,0x6C,0x65,0x74,0x70,0x73,0x32,0x00}), + new KeyValuePair(client2,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x0B,0x90,0x1C,0x24,0x01,0x01,0x01,0xC0,0xA8,0x09,0x0C,0x00,0x00,0x67,0x61,0x75,0x6E,0x74,0x6C,0x65,0x74,0x70,0x73,0x32,0x00}), + new KeyValuePair(client2,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x0B,0x90,0x1C,0x24,0x02,0x01,0x01,0xC0,0xA8,0x09,0x0C,0x08,0x0C,0x67,0x61,0x75,0x6E,0x74,0x6C,0x65,0x74,0x70,0x73,0x32,0x00}) + }; + + foreach (var item in requests) + { + item.Key.TestReceived(item.Value); + } + } } } \ No newline at end of file From 035c97f34c23b93e29f3c3171b894c92e7ed9fdb Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 7 Jul 2023 22:54:15 +0800 Subject: [PATCH 024/231] refactor(nn): added game test --- src/Servers/NatNegotiation/test/GameTest.cs | 24 +++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/Servers/NatNegotiation/test/GameTest.cs b/src/Servers/NatNegotiation/test/GameTest.cs index 0d107b80a..5364fb07c 100644 --- a/src/Servers/NatNegotiation/test/GameTest.cs +++ b/src/Servers/NatNegotiation/test/GameTest.cs @@ -355,5 +355,29 @@ public void Gautletps2Test20230620() item.Key.TestReceived(item.Value); } } + + [Fact] + public void MarvelPs2_20230707() + { + var client1GP = MockObject.CreateClient("192.168.9.2", 5165); + var client1NN = MockObject.CreateClient("192.168.9.2", 59300); + var client2GP = MockObject.CreateClient("192.168.9.9", 5165); + var client2NN = MockObject.CreateClient("192.168.9.9", 62545); + + var requests = new List>() + { + new KeyValuePair(client1GP,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x7D,0x47,0xCF,0x14,0x00,0x01,0x01,0xC0,0xA8,0x09,0x02,0x00,0x00,0x6D,0x61,0x72,0x76,0x6C,0x65,0x67,0x70,0x73,0x32,0x70,0x00,0x00}), + new KeyValuePair(client1NN,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x7D,0x47,0xCF,0x14,0x01,0x01,0x01,0xC0,0xA8,0x09,0x02,0x00,0x00,0x6D,0x61,0x72,0x76,0x6C,0x65,0x67,0x70,0x73,0x32,0x70,0x00}), + new KeyValuePair(client1NN,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x7D,0x47,0xCF,0x14,0x02,0x01,0x01,0xC0,0xA8,0x09,0x02,0x14,0x2D,0x6D,0x61,0x72,0x76,0x6C,0x65,0x67,0x70,0x73,0x32,0x70,0x00}), + new KeyValuePair(client2GP,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x7D,0x47,0xCF,0x14,0x00,0x00,0x01,0xC0,0xA8,0x09,0x09,0x00,0x00,0x6D,0x61,0x72,0x76,0x6C,0x65,0x67,0x70,0x73,0x32,0x70,0x00}), + new KeyValuePair(client2NN,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x7D,0x47,0xCF,0x14,0x02,0x00,0x01,0xC0,0xA8,0x09,0x09,0x14,0x2D,0x6D,0x61,0x72,0x76,0x6C,0x65,0x67,0x70,0x73,0x32,0x70,0x00}), + new KeyValuePair(client2NN,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x7D,0x47,0xCF,0x14,0x01,0x00,0x01,0xC0,0xA8,0x09,0x09,0x00,0x00,0x6D,0x61,0x72,0x76,0x6C,0x65,0x67,0x70,0x73,0x32,0x70,0x00}) + }; + + foreach (var item in requests) + { + item.Key.TestReceived(item.Value); + } + } } } \ No newline at end of file From 98bf9bc62deab315b9e11dff2a926f101fc3ae62 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 10 Jul 2023 11:28:19 +0800 Subject: [PATCH 025/231] refactor(pcm): add unittest --- .../PresenceConnectionManager/test/Game/GameTest.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/Servers/PresenceConnectionManager/test/Game/GameTest.cs b/src/Servers/PresenceConnectionManager/test/Game/GameTest.cs index 843a63bea..81f1dfdad 100644 --- a/src/Servers/PresenceConnectionManager/test/Game/GameTest.cs +++ b/src/Servers/PresenceConnectionManager/test/Game/GameTest.cs @@ -38,10 +38,11 @@ public void ConflictGlobalStorm() [Fact] public void swbfrontps2Test() { - var raw = @"\status\1\sesskey\1111\statstring\EN LIGNE\locstring\\final\"; - var req = new StatusRequest(raw); - req.Parse(); - + var raw = @"\status\1\sesskey\1111\statstring\EN LIGNE\locstring\\final\"; + var req = new StatusRequest(raw); + req.Parse(); + Assert.True(req.Status.LocationString == ""); + Assert.True(req.Status.StatusString == "EN LIGNE"); } } } \ No newline at end of file From 9352b0959ee5915d72b19c043c8a3b653c45377f Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 10 Jul 2023 11:28:55 +0800 Subject: [PATCH 026/231] refactor: nn and gtr for relay logic --- .../src/Network/Udp/Server/UdpConnection.cs | 3 +- .../src/Aggregate/ConnectionForwardClient.cs | 19 --- .../src/Aggregate/ConnectionListener.cs | 114 ++++++++---------- .../src/Aggregate/ConnectionPairs.cs | 37 ------ .../src/Aggregate/NetworkUtils.cs | 18 +-- .../src/Aggregate/ServerStatusReporter.cs | 2 +- .../src/Contracts/NatNegotiation.cs | 15 ++- .../Controllers/NatNegotiationController.cs | 34 ++++-- .../src/Interface/IConnectionForwardClient.cs | 9 -- .../src/Interface/IConnectionListener.cs | 15 --- src/Servers/GameTrafficRelay/test/GameTest.cs | 69 +++++------ .../test/{TestClasses.cs => MockObject.cs} | 2 +- .../src/Handler/CmdHandler/ConnectHandler.cs | 18 ++- 13 files changed, 134 insertions(+), 221 deletions(-) delete mode 100644 src/Servers/GameTrafficRelay/src/Aggregate/ConnectionForwardClient.cs delete mode 100644 src/Servers/GameTrafficRelay/src/Aggregate/ConnectionPairs.cs delete mode 100644 src/Servers/GameTrafficRelay/src/Interface/IConnectionForwardClient.cs delete mode 100644 src/Servers/GameTrafficRelay/src/Interface/IConnectionListener.cs rename src/Servers/GameTrafficRelay/test/{TestClasses.cs => MockObject.cs} (95%) diff --git a/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs b/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs index bcb026609..c672a3984 100644 --- a/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs +++ b/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs @@ -1,3 +1,4 @@ +using System.Threading.Tasks; using System.Net; using System.Threading; using UniSpy.Server.Core.Abstraction.Interface; @@ -26,7 +27,7 @@ public virtual void OnReceived(byte[] message) { // Server.ReceiveAsync(); ThreadPool.QueueUserWorkItem(o => { try { Manager.ReceiveAsync(); } catch { } }); - OnReceive(message); + Task.Run(() => OnReceive(message)); } public bool Send(object response) diff --git a/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionForwardClient.cs b/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionForwardClient.cs deleted file mode 100644 index 734c97ba0..000000000 --- a/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionForwardClient.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.GameTrafficRelay.Interface; -using UniSpy.Server.Core.Extension; -using UniSpy.Server.Core.Logging; -namespace UniSpy.Server.GameTrafficRelay.Aggregate -{ - public sealed class ConnectionForwardClient : IConnectionForwardClient - { - public IConnectionListener GameSpyTrafficListener { get; set; } - public IConnectionForwardClient ForwardTargetClient { get; set; } - public ConnectionForwardClient() - { - } - public void ForwardMessage(byte[] data) - { - LogWriter.LogDebug($" [{GameSpyTrafficListener.ListeningEndPoint}] => [{ForwardTargetClient.GameSpyTrafficListener.ListeningEndPoint}] {StringExtensions.ConvertPrintableBytesToString(data)} [{StringExtensions.ConvertByteToHexString(data)}]"); - ForwardTargetClient.ForwardMessage(data); - } - } -} \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionListener.cs b/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionListener.cs index 0d29ccd52..5656c79fc 100644 --- a/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionListener.cs +++ b/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionListener.cs @@ -2,51 +2,42 @@ using System.Linq; using System.Net; using System.Threading; -using UniSpy.Server.GameTrafficRelay.Controller; -using UniSpy.Server.GameTrafficRelay.Interface; using UniSpy.Server.Core.Extension; using UniSpy.Server.Core.Logging; +using UniSpy.Server.GameTrafficRelay.Controller; namespace UniSpy.Server.GameTrafficRelay.Aggregate { - public class ConnectionListener : NetCoreServer.UdpServer, IConnectionListener + public class ConnectionListener : NetCoreServer.UdpServer { public uint Cookie { get; private set; } public IPEndPoint ListeningEndPoint => (IPEndPoint)Endpoint; - /// - /// The other gamespy client that message need forward to - /// - public IConnectionListener ForwardTargetListener { get; set; } - public IPEndPoint GameSpyClientIPEndPoint { get; private set; } - public DateTime LastPacketReceivedTime { get; private set; } - public TimeSpan ConnectionExistedTime => DateTime.Now - LastPacketReceivedTime; - private TimeSpan _expireTimeInterval; - private System.Timers.Timer _timer; - public ConnectionListener(IPEndPoint listeningEndPoint, uint cookie) : base(listeningEndPoint.Address, listeningEndPoint.Port) + private EasyTimer _timer; + private IPAddress _gameServerAddress; + private IPAddress _gameClientAddress; + private IPEndPoint _gameServerEndPoint; + private IPEndPoint _gameClientEndPoint; + public ConnectionListener(IPEndPoint listeningEndPoint, uint cookie, IPAddress gameServerAddr, IPAddress gameClientAddr) : base(listeningEndPoint) { - // after create listener we start it + _gameServerAddress = gameServerAddr; + _gameClientAddress = gameClientAddr; Cookie = cookie; + _timer = new EasyTimer(TimeSpan.FromMinutes(10), TimeSpan.FromSeconds(10), CheckExpiredClient); + // after create listener we start it Start(); - LastPacketReceivedTime = DateTime.Now; - // we set expire time to 2 minutes - _expireTimeInterval = new TimeSpan(0, 2, 0); - _timer = new System.Timers.Timer - { - Enabled = true, - Interval = 60000, - AutoReset = true - };//10000 - _timer.Start(); - _timer.Elapsed += (s, e) => CheckExpiredClient(); LogWriter.LogDebug($"[{ListeningEndPoint}] gamespy client listener started."); } protected override void OnStarted() => ReceiveAsync(); private void CheckExpiredClient() { - // we calculate the interval between last packe and current time - if (ConnectionExistedTime > _expireTimeInterval) + if (_gameServerEndPoint is null || _gameClientEndPoint is null) { - ((IConnectionListener)this).Dispose(); + if (!IsDisposed) + { + Dispose(); + NatNegotiationController.ConnectionListeners.TryRemove(this.Cookie, out _); + LogWriter.LogDebug($"[{ListeningEndPoint}] gamespy listener shutdown."); + } } } protected override void OnReceived(EndPoint endpoint, byte[] buffer, long offset, long size) @@ -56,53 +47,46 @@ protected override void OnReceived(EndPoint endpoint, byte[] buffer, long offset } protected void OnReceived(EndPoint endPoint, byte[] buffer) { - // we only accept the gamespy client message - - if (GameSpyClientIPEndPoint is null) - { - GameSpyClientIPEndPoint = (IPEndPoint)endPoint; - } - ThreadPool.QueueUserWorkItem(o => { try { ReceiveAsync(); } catch { } }); - LogWriter.LogDebug($"[{GameSpyClientIPEndPoint}] [recv] {StringExtensions.ConvertPrintableBytesToString(buffer)} [{StringExtensions.ConvertByteToHexString(buffer)}]"); - ForwardTargetListener.ForwardMessage(buffer); - LastPacketReceivedTime = DateTime.Now; - } - - public void ForwardMessage(byte[] data) - { - // we must send the every message to both client - var retryCount = 0; - // we wait for clients connect 5 sec - if (GameSpyClientIPEndPoint is null) + LogWriter.LogDebug($"[{endPoint}] [recv] {StringExtensions.ConvertPrintableBytesToString(buffer)} [{StringExtensions.ConvertByteToHexString(buffer)}]"); + // we only accept the gamespy client message + if (_gameClientEndPoint is null || _gameClientEndPoint is null) { - LogWriter.LogDebug($"[{ListeningEndPoint}] is waiting for gamespy client to connect."); - Thread.Sleep(5000); - retryCount++; + if (_gameServerAddress.Equals(((IPEndPoint)endPoint).Address) && _gameServerEndPoint is null) + { + _gameServerEndPoint = (IPEndPoint)endPoint; + } + else if (_gameClientAddress.Equals(((IPEndPoint)endPoint).Address) && _gameClientEndPoint is null && !_gameClientAddress.Equals((IPEndPoint)endPoint)) + { + _gameClientEndPoint = (IPEndPoint)endPoint; + } + else + { + //ignore + } } - - // after waiting for 10 sec client still not connecting we just dispose it - if (GameSpyClientIPEndPoint is null) + else { - ForwardTargetListener?.Dispose(); - if (NatNegotiationController.ConnectionPairs.TryGetValue(Cookie, out _)) + if (_gameServerEndPoint.Equals((IPEndPoint)endPoint)) + { + ForwardMessage(_gameServerEndPoint, _gameClientEndPoint, buffer); + } + else if (_gameClientEndPoint.Equals((IPEndPoint)endPoint)) { - NatNegotiationController.ConnectionPairs.TryRemove(Cookie, out _); + ForwardMessage(_gameClientEndPoint, _gameServerEndPoint, buffer); + } + else + { + //ignore } - ((IConnectionListener)this).Dispose(); - return; } - LogWriter.LogDebug($"[{ListeningEndPoint}] => [{GameSpyClientIPEndPoint}] {StringExtensions.ConvertPrintableBytesToString(data)} [{StringExtensions.ConvertByteToHexString(data)}]"); - SendAsync(GameSpyClientIPEndPoint, data); } - void IConnectionListener.Dispose() + public void ForwardMessage(IPEndPoint sender, IPEndPoint receiver, byte[] data) { - if (!IsDisposed) - { - LogWriter.LogDebug($"[{ListeningEndPoint}] gamespy client listener stoped"); - Dispose(); - } + _timer.RefreshLastActiveTime(); + SendAsync(receiver, data); + LogWriter.LogDebug($"[{sender}] => [{receiver}] {StringExtensions.ConvertPrintableBytesToString(data)} [{StringExtensions.ConvertByteToHexString(data)}]"); } } } \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionPairs.cs b/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionPairs.cs deleted file mode 100644 index 1631f7fb5..000000000 --- a/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionPairs.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Net; -using UniSpy.Server.GameTrafficRelay.Interface; - -namespace UniSpy.Server.GameTrafficRelay.Aggregate -{ - public class ConnectionForwardBridge - { - public IConnectionForwardClient Client { get; private set; } - public IConnectionListener Listener { get; private set; } - public ConnectionForwardBridge(IConnectionListener listener, IConnectionForwardClient client) - { - Listener = listener; - Client = client; - } - } - /// - /// This class represent a connection pair, - /// in this class 2 IConnectionForwardBridge instances is created and listen to 2 different avaliable udp port. - /// The message send from gamespy client is redirect by the client in IConnectionForwardBridge - /// - public sealed class ConnectionPair - { - public uint Cookie { get; private set; } - public IConnectionListener Listener1 { get; private set; } - public IConnectionListener Listener2 { get; private set; } - - public ConnectionPair(IPEndPoint gameSpyClient1ListeningEnd, IPEndPoint gameSpyClient2ListeningEnd, uint cookie) - { - Cookie = cookie; - Listener1 = new ConnectionListener(gameSpyClient1ListeningEnd, cookie); - Listener2 = new ConnectionListener(gameSpyClient2ListeningEnd, cookie); - // bind listener1 with client1 - Listener1.ForwardTargetListener = Listener2; - Listener2.ForwardTargetListener = Listener1; - } - } -} \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/src/Aggregate/NetworkUtils.cs b/src/Servers/GameTrafficRelay/src/Aggregate/NetworkUtils.cs index f9c85698a..2d2a170ff 100644 --- a/src/Servers/GameTrafficRelay/src/Aggregate/NetworkUtils.cs +++ b/src/Servers/GameTrafficRelay/src/Aggregate/NetworkUtils.cs @@ -1,3 +1,4 @@ +using System.Net; using System.Linq; using System.Net.NetworkInformation; @@ -6,11 +7,9 @@ namespace UniSpy.Server.GameTrafficRelay.Entity public sealed class NetworkUtils { /// - /// Find 2 ports is not using by other programs + /// Find 1 port is not using by other programs /// - /// - /// ports array - public static int[] GetAvailablePorts(int startingPort = 1025) + public static int GetAvailablePort(int startingPort) { var properties = IPGlobalProperties.GetIPGlobalProperties(); @@ -29,13 +28,18 @@ public static int[] GetAvailablePorts(int startingPort = 1025) .Where(n => n.Port >= startingPort) .Select(n => n.Port); - var ports = Enumerable.Range(startingPort, ushort.MaxValue) + var port = Enumerable.Range(startingPort, ushort.MaxValue) .Where(i => !tcpConnectionPorts.Contains(i)) .Where(i => !tcpListenerPorts.Contains(i)) .Where(i => !udpListenerPorts.Contains(i)) - .Take(2).ToArray(); + .First(); - return ports; + return port; + } + public static IPEndPoint GetAvaliableLocalEndPoint(int startingPort = 1025) + { + var port = GetAvailablePort(startingPort); + return new IPEndPoint(IPAddress.Any, port); } } } \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/src/Aggregate/ServerStatusReporter.cs b/src/Servers/GameTrafficRelay/src/Aggregate/ServerStatusReporter.cs index f94bb0728..0d8a9fe4d 100644 --- a/src/Servers/GameTrafficRelay/src/Aggregate/ServerStatusReporter.cs +++ b/src/Servers/GameTrafficRelay/src/Aggregate/ServerStatusReporter.cs @@ -24,7 +24,7 @@ private void UpdateServerInfo() { ServerID = _server.Id, PublicIPEndPoint = _server.PublicIPEndPoint, - ClientCount = NatNegotiationController.ConnectionPairs.Values.Count * 2 + ClientCount = NatNegotiationController.ConnectionListeners.Values.Count * 2 }; _ = _redisClient.SetValueAsync(info); var data = _redisClient.GetKeyValues(); diff --git a/src/Servers/GameTrafficRelay/src/Contracts/NatNegotiation.cs b/src/Servers/GameTrafficRelay/src/Contracts/NatNegotiation.cs index c3cd1e289..90589acd1 100644 --- a/src/Servers/GameTrafficRelay/src/Contracts/NatNegotiation.cs +++ b/src/Servers/GameTrafficRelay/src/Contracts/NatNegotiation.cs @@ -9,12 +9,21 @@ public record NatNegotiationRequest { public uint Cookie { get; set; } public Guid ServerId { get; set; } + /// + /// Gameserver public ip endpoint, used to validate the negotiator's ip + /// + [JsonConverter(typeof(IPEndPoint))] + public IPEndPoint GameServerEnd { get; set; } + /// + /// Gameclient public ip endpoint, used to validate the negotiator's ip + /// + [JsonConverter(typeof(IPEndPoint))] + public IPEndPoint GameClientEnd { get; set; } } public record NatNegotiationResponse { [JsonConverter(typeof(IPEndPointConverter))] - public IPEndPoint IPEndPoint1 { get; set; } - [JsonConverter(typeof(IPEndPointConverter))] - public IPEndPoint IPEndPoint2 { get; set; } + public int Port { get; set; } + public string Message { get; set; } } } \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/src/Controllers/NatNegotiationController.cs b/src/Servers/GameTrafficRelay/src/Controllers/NatNegotiationController.cs index 56dcf4fcb..ed87c68fa 100644 --- a/src/Servers/GameTrafficRelay/src/Controllers/NatNegotiationController.cs +++ b/src/Servers/GameTrafficRelay/src/Controllers/NatNegotiationController.cs @@ -14,7 +14,7 @@ namespace UniSpy.Server.GameTrafficRelay.Controller public class NatNegotiationController : ControllerBase { private readonly ILogger _logger; - public static ConcurrentDictionary ConnectionPairs = new ConcurrentDictionary(); + public static ConcurrentDictionary ConnectionListeners = new ConcurrentDictionary(); public NatNegotiationController(ILogger logger) { _logger = logger; @@ -22,21 +22,33 @@ public NatNegotiationController(ILogger logger) [HttpPost] public Task GetNatNegotiationInfo(NatNegotiationRequest request) { + NatNegotiationResponse response; + if (request.GameClientEnd is null || request.GameServerEnd is null) + { + response = new NatNegotiationResponse() + { + Port = -1, + Message = "game client/server's address is missing from request" + }; + return Task.FromResult(response); + } // natneg connecthandler will send 2 request to game traffic relay - ConnectionPair pair; - if (!ConnectionPairs.TryGetValue(request.Cookie, out pair)) + ConnectionListener listener; + if (!ConnectionListeners.TryGetValue(request.Cookie, out listener)) { - var ports = NetworkUtils.GetAvailablePorts(); - var ends = new IPEndPoint[]{new IPEndPoint(IPAddress.Any,ports[0]), - new IPEndPoint(IPAddress.Any,ports[1])}; - pair = new ConnectionPair(ends[0], ends[1], request.Cookie); - ConnectionPairs.TryAdd(request.Cookie, pair); + var relayEnd = NetworkUtils.GetAvaliableLocalEndPoint(); + listener = new ConnectionListener( + relayEnd, + request.Cookie, + request.GameServerEnd.Address, + request.GameClientEnd.Address); + + ConnectionListeners.TryAdd(request.Cookie, listener); } - var response = new NatNegotiationResponse() + response = new NatNegotiationResponse() { - IPEndPoint1 = new IPEndPoint(ServerLauncher.Server.PublicIPEndPoint.Address, pair.Listener1.ListeningEndPoint.Port), - IPEndPoint2 = new IPEndPoint(ServerLauncher.Server.PublicIPEndPoint.Address, pair.Listener2.ListeningEndPoint.Port) + Port = listener.ListeningEndPoint.Port }; return Task.FromResult(response); } diff --git a/src/Servers/GameTrafficRelay/src/Interface/IConnectionForwardClient.cs b/src/Servers/GameTrafficRelay/src/Interface/IConnectionForwardClient.cs deleted file mode 100644 index 51ca82c01..000000000 --- a/src/Servers/GameTrafficRelay/src/Interface/IConnectionForwardClient.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UniSpy.Server.GameTrafficRelay.Interface -{ - public interface IConnectionForwardClient - { - IConnectionListener GameSpyTrafficListener { get; } - IConnectionForwardClient ForwardTargetClient { get; } - void ForwardMessage(byte[] data); - } -} \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/src/Interface/IConnectionListener.cs b/src/Servers/GameTrafficRelay/src/Interface/IConnectionListener.cs deleted file mode 100644 index a909ae9a1..000000000 --- a/src/Servers/GameTrafficRelay/src/Interface/IConnectionListener.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Net; - -namespace UniSpy.Server.GameTrafficRelay.Interface -{ - public interface IConnectionListener - { - IPEndPoint ListeningEndPoint { get; } - // IConnectionForwardClient Client { get; } - IPEndPoint GameSpyClientIPEndPoint { get; } - public IConnectionListener ForwardTargetListener { get; set; } - - void ForwardMessage(byte[] data); - void Dispose(); - } -} \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/test/GameTest.cs b/src/Servers/GameTrafficRelay/test/GameTest.cs index 0492d7507..d149cc613 100644 --- a/src/Servers/GameTrafficRelay/test/GameTest.cs +++ b/src/Servers/GameTrafficRelay/test/GameTest.cs @@ -1,11 +1,8 @@ -using System.Collections.Generic; -using System.Linq; +using System.Threading; using System.Net; using Xunit; using UniSpy.Server.GameTrafficRelay.Controller; -using System.Net.NetworkInformation; -using UniSpy.Server.GameTrafficRelay.Application; -using Moq; +using System.Net.Sockets; namespace UniSpy.Server.GameTrafficRelay.Test { @@ -13,15 +10,15 @@ public class GameTest { public GameTest() { - TestClasses.CreateServer(); + MockObject.CreateServer(); } [Fact] public void GetNagNegotiationInfo() { - var mockServer = new Mock(); - mockServer.Setup(s => s.PublicIPEndPoint).Returns(IPEndPoint.Parse("202.91.0.1:123")); - ServerLauncher.ServerInstances.Add(mockServer.Object); + // var mockServer = new Mock(); + // mockServer.Setup(s => s.PublicIPEndPoint).Returns(IPEndPoint.Parse("202.91.0.1:123")); + // ServerLauncher.ServerInstances.Add(mockServer.Object); var httpContext = new Microsoft.AspNetCore.Http.DefaultHttpContext(); @@ -38,23 +35,31 @@ public void GetNagNegotiationInfo() var request = new NatNegotiationRequest() { Cookie = 123456, - ServerId = System.Guid.NewGuid() + ServerId = System.Guid.NewGuid(), + GameClientEnd = IPEndPoint.Parse("127.0.0.1:1234"), + GameServerEnd = IPEndPoint.Parse("127.0.0.1:1235") }; - controller.GetNatNegotiationInfo(request); + var resp = controller.GetNatNegotiationInfo(request).Result; + var relayEnd = new IPEndPoint(IPAddress.Loopback, resp.Port); - // Assert.ThrowsAsync(() => controller.GetNatNegotiationInfo(request)); + var sockClient = new Socket(AddressFamily.InterNetwork, + SocketType.Dgram, + ProtocolType.Udp); + sockClient.Bind(IPEndPoint.Parse("127.0.0.1:1234")); + // we only use one client to send message to check if the listener shutdown + var req = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x07, 0x00, 0x00, 0x02, 0x9A, 0xC0, 0xA8, 0x01, 0x67, 0x6C, 0xFD, 0x00, 0x00 }; + sockClient.SendTo(req, relayEnd); + var sockServer = new Socket(AddressFamily.InterNetwork, + SocketType.Dgram, + ProtocolType.Udp); + sockServer.Bind(IPEndPoint.Parse("127.0.0.1:1235")); + sockServer.SendTo(req, relayEnd); + Thread.Sleep(2000); - // var resp = controller.GetNatNegotiationInfo(request); - - // Assert.True(IsPortUsing(resp.IPEndPoint1.Port)); - // Assert.True(IsPortUsing(resp.IPEndPoint2.Port)); - // var sock = new Socket(AddressFamily.InterNetwork, - // SocketType.Dgram, - // ProtocolType.Udp); - // // we only use one client to send message to check if the listener shutdown - // var req = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x07, 0x00, 0x00, 0x02, 0x9A, 0xC0, 0xA8, 0x01, 0x67, 0x6C, 0xFD, 0x00, 0x00 }; - // sock.SendTo(req, resp.IPEndPoint1); + sockClient.SendTo(req, relayEnd); + Thread.Sleep(2000); + sockServer.SendTo(req, relayEnd); // // Thread.Sleep(10000); // // we check if listener is stoped // Assert.False(IsPortUsing(resp.IPEndPoint1.Port)); @@ -62,25 +67,7 @@ public void GetNagNegotiationInfo() // // because we are not runing server, so the server object is null // Assert.Null(resp.IPEndPoint1); // Assert.Null(resp.IPEndPoint2); - - } - internal static bool IsPortUsing(int port) - { - var tcpPorts = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners().Select(p => p.Port).ToArray(); - var udpPorts = IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners().Select(p => p.Port).ToArray(); - var usedPorts = new List(); - usedPorts.AddRange(tcpPorts); - usedPorts.AddRange(udpPorts); - usedPorts.Sort(); - usedPorts.Distinct(); - if (usedPorts.Contains(port)) - { - return true; - } - else - { - return false; - } + Thread.Sleep(20000); } } } \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/test/TestClasses.cs b/src/Servers/GameTrafficRelay/test/MockObject.cs similarity index 95% rename from src/Servers/GameTrafficRelay/test/TestClasses.cs rename to src/Servers/GameTrafficRelay/test/MockObject.cs index e9c39dd19..f8262b8fc 100644 --- a/src/Servers/GameTrafficRelay/test/TestClasses.cs +++ b/src/Servers/GameTrafficRelay/test/MockObject.cs @@ -4,7 +4,7 @@ namespace UniSpy.Server.GameTrafficRelay.Test { - public static class TestClasses + public static class MockObject { public static IServer Server = CreateServer(); diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs index 70334a222..378cb6aac 100755 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs +++ b/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs @@ -116,23 +116,19 @@ private void UsingGameRelayServerToNegotiate() var req = new NatNegotiationRequest() { Cookie = _myInitInfo.Cookie, - ServerId = _client.Server.Id + ServerId = _client.Server.Id, + GameClientEnd = _myInitInfo.ClientIndex == NatClientIndex.GameClient ? _myInitInfo.PublicIPEndPoint : _othersInitInfo.PublicIPEndPoint, + GameServerEnd = _myInitInfo.ClientIndex == NatClientIndex.GameServer ? _myInitInfo.PublicIPEndPoint : _othersInitInfo.PublicIPEndPoint }; var client = new RestClient($"http://{relayEndPoint}/NatNegotiation").UseNewtonsoftJson(); var request = new RestRequest().AddJsonBody(req); var resp = client.Post(request); - if (_client.Info.ClientIndex == NatClientIndex.GameClient) + if (resp.Port == -1) { - _guessedOthersIPEndPoint = resp.IPEndPoint1; - } - else if (_client.Info.ClientIndex == NatClientIndex.GameServer) - { - _guessedOthersIPEndPoint = resp.IPEndPoint2; - } - else - { - throw new NatNegotiation.Exception("The client index is not applied"); + throw new NatNegotiation.Exception(resp.Message); } + // we create endpoint by using the relay server address and the relay port + _guessedOthersIPEndPoint = new IPEndPoint(relayEndPoint.Address, resp.Port); } public static NatStrategyType DetermineNATStrategy(NatInitInfo info1, NatInitInfo info2) From 29567d58b4a08747621c30dbf1e6b92ecbc96f79 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 10 Jul 2023 17:29:23 +0800 Subject: [PATCH 027/231] fix(gtr): negotiation error --- .../src/Aggregate/ConnectionListener.cs | 24 ++++++++++++++++--- .../src/Contracts/NatNegotiation.cs | 13 ++++------ .../Controllers/NatNegotiationController.cs | 22 +++++++++-------- src/Servers/GameTrafficRelay/test/GameTest.cs | 4 ++-- .../src/Handler/CmdHandler/ConnectHandler.cs | 5 ++-- 5 files changed, 42 insertions(+), 26 deletions(-) diff --git a/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionListener.cs b/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionListener.cs index 5656c79fc..4ca101237 100644 --- a/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionListener.cs +++ b/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionListener.cs @@ -22,7 +22,7 @@ public ConnectionListener(IPEndPoint listeningEndPoint, uint cookie, IPAddress g _gameServerAddress = gameServerAddr; _gameClientAddress = gameClientAddr; Cookie = cookie; - _timer = new EasyTimer(TimeSpan.FromMinutes(10), TimeSpan.FromSeconds(10), CheckExpiredClient); + _timer = new EasyTimer(TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(10), CheckExpiredClient); // after create listener we start it Start(); LogWriter.LogDebug($"[{ListeningEndPoint}] gamespy client listener started."); @@ -30,13 +30,14 @@ public ConnectionListener(IPEndPoint listeningEndPoint, uint cookie, IPAddress g protected override void OnStarted() => ReceiveAsync(); private void CheckExpiredClient() { - if (_gameServerEndPoint is null || _gameClientEndPoint is null) + if (_gameClientEndPoint is null || _gameServerEndPoint is null || _timer.IsExpired) { if (!IsDisposed) { Dispose(); NatNegotiationController.ConnectionListeners.TryRemove(this.Cookie, out _); LogWriter.LogDebug($"[{ListeningEndPoint}] gamespy listener shutdown."); + _timer.Dispose(); } } } @@ -45,10 +46,26 @@ protected override void OnReceived(EndPoint endpoint, byte[] buffer, long offset var message = buffer.Skip((int)offset).Take((int)size).ToArray(); OnReceived(endpoint, message); } + private bool CheckValidation(byte[] buffer) + { + var magic = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2 }; + if (!buffer.Take(6).SequenceEqual(magic)) + { + return false; + } + var cookie = buffer.Skip(8).Take(4).ToArray(); + + if (Cookie != BitConverter.ToInt32(cookie)) + { + return false; + } + + return true; + } protected void OnReceived(EndPoint endPoint, byte[] buffer) { ThreadPool.QueueUserWorkItem(o => { try { ReceiveAsync(); } catch { } }); - LogWriter.LogDebug($"[{endPoint}] [recv] {StringExtensions.ConvertPrintableBytesToString(buffer)} [{StringExtensions.ConvertByteToHexString(buffer)}]"); + // we only accept the gamespy client message if (_gameClientEndPoint is null || _gameClientEndPoint is null) { @@ -64,6 +81,7 @@ protected void OnReceived(EndPoint endPoint, byte[] buffer) { //ignore } + LogWriter.LogDebug($"[{endPoint}] [recv] {StringExtensions.ConvertPrintableBytesToString(buffer)} [{StringExtensions.ConvertByteToHexString(buffer)}]"); } else { diff --git a/src/Servers/GameTrafficRelay/src/Contracts/NatNegotiation.cs b/src/Servers/GameTrafficRelay/src/Contracts/NatNegotiation.cs index 90589acd1..da50d625e 100644 --- a/src/Servers/GameTrafficRelay/src/Contracts/NatNegotiation.cs +++ b/src/Servers/GameTrafficRelay/src/Contracts/NatNegotiation.cs @@ -1,28 +1,25 @@ using System; -using System.Net; -using Newtonsoft.Json; -using UniSpy.Server.Core.Misc; namespace UniSpy.Server.GameTrafficRelay { public record NatNegotiationRequest { + public uint Cookie { get; set; } public Guid ServerId { get; set; } /// /// Gameserver public ip endpoint, used to validate the negotiator's ip /// - [JsonConverter(typeof(IPEndPoint))] - public IPEndPoint GameServerEnd { get; set; } + // [JsonConverter(typeof(IPEndPointConverter))] + public string GameServerIP { get; set; } /// /// Gameclient public ip endpoint, used to validate the negotiator's ip /// - [JsonConverter(typeof(IPEndPoint))] - public IPEndPoint GameClientEnd { get; set; } + // [JsonConverter(typeof(IPEndPointConverter))] + public string GameClientIP { get; set; } } public record NatNegotiationResponse { - [JsonConverter(typeof(IPEndPointConverter))] public int Port { get; set; } public string Message { get; set; } } diff --git a/src/Servers/GameTrafficRelay/src/Controllers/NatNegotiationController.cs b/src/Servers/GameTrafficRelay/src/Controllers/NatNegotiationController.cs index ed87c68fa..d1783eb27 100644 --- a/src/Servers/GameTrafficRelay/src/Controllers/NatNegotiationController.cs +++ b/src/Servers/GameTrafficRelay/src/Controllers/NatNegotiationController.cs @@ -23,7 +23,7 @@ public NatNegotiationController(ILogger logger) public Task GetNatNegotiationInfo(NatNegotiationRequest request) { NatNegotiationResponse response; - if (request.GameClientEnd is null || request.GameServerEnd is null) + if (request.GameClientIP is null || request.GameServerIP is null) { response = new NatNegotiationResponse() { @@ -34,18 +34,20 @@ public Task GetNatNegotiationInfo(NatNegotiationRequest } // natneg connecthandler will send 2 request to game traffic relay ConnectionListener listener; - if (!ConnectionListeners.TryGetValue(request.Cookie, out listener)) + lock (ConnectionListeners) { - var relayEnd = NetworkUtils.GetAvaliableLocalEndPoint(); - listener = new ConnectionListener( - relayEnd, - request.Cookie, - request.GameServerEnd.Address, - request.GameClientEnd.Address); + if (!ConnectionListeners.TryGetValue(request.Cookie, out listener)) + { + var relayEnd = NetworkUtils.GetAvaliableLocalEndPoint(); + listener = new ConnectionListener( + relayEnd, + request.Cookie, + IPEndPoint.Parse(request.GameServerIP).Address, + IPEndPoint.Parse(request.GameClientIP).Address); - ConnectionListeners.TryAdd(request.Cookie, listener); + ConnectionListeners.TryAdd(request.Cookie, listener); + } } - response = new NatNegotiationResponse() { Port = listener.ListeningEndPoint.Port diff --git a/src/Servers/GameTrafficRelay/test/GameTest.cs b/src/Servers/GameTrafficRelay/test/GameTest.cs index d149cc613..c0fb915c2 100644 --- a/src/Servers/GameTrafficRelay/test/GameTest.cs +++ b/src/Servers/GameTrafficRelay/test/GameTest.cs @@ -36,8 +36,8 @@ public void GetNagNegotiationInfo() { Cookie = 123456, ServerId = System.Guid.NewGuid(), - GameClientEnd = IPEndPoint.Parse("127.0.0.1:1234"), - GameServerEnd = IPEndPoint.Parse("127.0.0.1:1235") + GameClientIP = "127.0.0.1:1234", + GameServerIP = "127.0.0.1:1235" }; var resp = controller.GetNatNegotiationInfo(request).Result; diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs index 378cb6aac..1162d529c 100755 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs +++ b/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs @@ -60,7 +60,6 @@ protected override void DataOperation() _client.LogInfo("The public ip is in nat fail record database, we use game traffic relay."); strategy = NatStrategyType.UseGameTrafficRelay; } - switch (strategy) { case NatStrategyType.UsePublicIP: @@ -117,8 +116,8 @@ private void UsingGameRelayServerToNegotiate() { Cookie = _myInitInfo.Cookie, ServerId = _client.Server.Id, - GameClientEnd = _myInitInfo.ClientIndex == NatClientIndex.GameClient ? _myInitInfo.PublicIPEndPoint : _othersInitInfo.PublicIPEndPoint, - GameServerEnd = _myInitInfo.ClientIndex == NatClientIndex.GameServer ? _myInitInfo.PublicIPEndPoint : _othersInitInfo.PublicIPEndPoint + GameClientIP = _myInitInfo.ClientIndex == NatClientIndex.GameClient ? _myInitInfo.PublicIPEndPoint.ToString() : _othersInitInfo.PublicIPEndPoint.ToString(), + GameServerIP = _myInitInfo.ClientIndex == NatClientIndex.GameServer ? _myInitInfo.PublicIPEndPoint.ToString() : _othersInitInfo.PublicIPEndPoint.ToString() }; var client = new RestClient($"http://{relayEndPoint}/NatNegotiation").UseNewtonsoftJson(); var request = new RestRequest().AddJsonBody(req); From 9f21de553cc90e6c2000e873f601828a05164fe9 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 12 Jul 2023 17:12:42 +0800 Subject: [PATCH 028/231] update(gtr): limit relay ips --- .../src/Application/Server.cs | 2 +- .../src/Application/StorageOperation.cs | 21 ------- .../Controllers/NatNegotiationController.cs | 24 ++++---- .../src/Interface/IStorageOperation.cs | 10 ---- .../src/UniSpy.Server.GameTrafficRelay.csproj | 2 +- src/Servers/GameTrafficRelay/test/GameTest.cs | 6 +- .../Interface/IStorageOperation.cs | 7 ++- .../GameTrafficRelay}/ConnectionListener.cs | 36 +++++++---- .../GameTrafficRelay}/NatNegotiation.cs | 10 ++-- .../GameTrafficRelay}/NetworkUtils.cs | 2 +- .../GameTrafficRelay}/RelayServerInfo.cs | 2 +- .../GameTrafficRelay/RelaySwitcher.cs | 26 ++++++++ .../GameTrafficRelay}/ServerStatusReporter.cs | 7 +-- .../src/Application/StorageOperation.cs | 15 +++-- .../src/Contract/Request/PingRequest.cs | 60 +++++++++---------- .../src/Contract/Response/PingResponse.cs | 18 ++++++ .../src/Handler/CmdHandler/ConnectHandler.cs | 10 ++-- .../src/Handler/CmdHandler/PingHandler.cs | 20 +++++++ .../NatNegotiation/src/Handler/CmdSwitcher.cs | 2 +- .../src/UniSpy.Server.NatNegotiation.csproj | 2 +- .../QueryReport/src/Application/Server.cs | 1 - 21 files changed, 163 insertions(+), 120 deletions(-) delete mode 100644 src/Servers/GameTrafficRelay/src/Application/StorageOperation.cs delete mode 100644 src/Servers/GameTrafficRelay/src/Interface/IStorageOperation.cs rename src/Servers/{GameTrafficRelay/src/Aggregate => NatNegotiation/src/Aggregate/GameTrafficRelay}/ConnectionListener.cs (73%) rename src/Servers/{GameTrafficRelay/src/Contracts => NatNegotiation/src/Aggregate/GameTrafficRelay}/NatNegotiation.cs (68%) rename src/Servers/{GameTrafficRelay/src/Aggregate => NatNegotiation/src/Aggregate/GameTrafficRelay}/NetworkUtils.cs (96%) rename src/Servers/{GameTrafficRelay/src/Aggregate => NatNegotiation/src/Aggregate/GameTrafficRelay}/RelayServerInfo.cs (92%) create mode 100644 src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelaySwitcher.cs rename src/Servers/{GameTrafficRelay/src/Aggregate => NatNegotiation/src/Aggregate/GameTrafficRelay}/ServerStatusReporter.cs (79%) create mode 100644 src/Servers/NatNegotiation/src/Contract/Response/PingResponse.cs create mode 100644 src/Servers/NatNegotiation/src/Handler/CmdHandler/PingHandler.cs diff --git a/src/Servers/GameTrafficRelay/src/Application/Server.cs b/src/Servers/GameTrafficRelay/src/Application/Server.cs index a7d12dfdb..272a62c2a 100644 --- a/src/Servers/GameTrafficRelay/src/Application/Server.cs +++ b/src/Servers/GameTrafficRelay/src/Application/Server.cs @@ -4,9 +4,9 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; -using UniSpy.Server.GameTrafficRelay.Entity; using UniSpy.Server.Core.Abstraction.BaseClass; using UniSpy.Server.Core.Abstraction.Interface; +using UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay; namespace UniSpy.Server.GameTrafficRelay.Application { diff --git a/src/Servers/GameTrafficRelay/src/Application/StorageOperation.cs b/src/Servers/GameTrafficRelay/src/Application/StorageOperation.cs deleted file mode 100644 index 33b92a3d2..000000000 --- a/src/Servers/GameTrafficRelay/src/Application/StorageOperation.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.GameTrafficRelay.Entity; -using UniSpy.Server.GameTrafficRelay.Interface; - -namespace UniSpy.Server.GameTrafficRelay.Application -{ - public sealed class StorageOperation : IStorageOperation - { - /// - /// Game relay server information redis server. - /// - private static RedisClient _redisClient = new RedisClient(); - - public static IStorageOperation Persistance = new StorageOperation(); - public List GetAvaliableRelayServers() - { - return _redisClient.Context.ToList(); - } - } -} \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/src/Controllers/NatNegotiationController.cs b/src/Servers/GameTrafficRelay/src/Controllers/NatNegotiationController.cs index d1783eb27..37552c900 100644 --- a/src/Servers/GameTrafficRelay/src/Controllers/NatNegotiationController.cs +++ b/src/Servers/GameTrafficRelay/src/Controllers/NatNegotiationController.cs @@ -1,11 +1,9 @@ -using System.Collections.Concurrent; using System.Net; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; -using UniSpy.Server.GameTrafficRelay.Application; -using UniSpy.Server.GameTrafficRelay.Entity; -using UniSpy.Server.GameTrafficRelay.Aggregate; using System.Threading.Tasks; +using UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay; +using UniSpy.Server.NatNegotiation.Handler.CmdHandler; namespace UniSpy.Server.GameTrafficRelay.Controller { @@ -14,7 +12,6 @@ namespace UniSpy.Server.GameTrafficRelay.Controller public class NatNegotiationController : ControllerBase { private readonly ILogger _logger; - public static ConcurrentDictionary ConnectionListeners = new ConcurrentDictionary(); public NatNegotiationController(ILogger logger) { _logger = logger; @@ -23,7 +20,7 @@ public NatNegotiationController(ILogger logger) public Task GetNatNegotiationInfo(NatNegotiationRequest request) { NatNegotiationResponse response; - if (request.GameClientIP is null || request.GameServerIP is null) + if (request.GameClientIPs is null || request.GameServerIPs is null) { response = new NatNegotiationResponse() { @@ -34,18 +31,17 @@ public Task GetNatNegotiationInfo(NatNegotiationRequest } // natneg connecthandler will send 2 request to game traffic relay ConnectionListener listener; - lock (ConnectionListeners) + lock (PingHandler.ConnectionListeners) { - if (!ConnectionListeners.TryGetValue(request.Cookie, out listener)) + if (!PingHandler.ConnectionListeners.TryGetValue(request.Cookie, out listener)) { var relayEnd = NetworkUtils.GetAvaliableLocalEndPoint(); - listener = new ConnectionListener( - relayEnd, - request.Cookie, - IPEndPoint.Parse(request.GameServerIP).Address, - IPEndPoint.Parse(request.GameClientIP).Address); + listener = new ConnectionListener(relayEnd, + request.Cookie, + request.GameServerIPs, + request.GameClientIPs); - ConnectionListeners.TryAdd(request.Cookie, listener); + PingHandler.ConnectionListeners.TryAdd(request.Cookie, listener); } } response = new NatNegotiationResponse() diff --git a/src/Servers/GameTrafficRelay/src/Interface/IStorageOperation.cs b/src/Servers/GameTrafficRelay/src/Interface/IStorageOperation.cs deleted file mode 100644 index 006262927..000000000 --- a/src/Servers/GameTrafficRelay/src/Interface/IStorageOperation.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.GameTrafficRelay.Entity; - -namespace UniSpy.Server.GameTrafficRelay.Interface -{ - public interface IStorageOperation - { - List GetAvaliableRelayServers(); - } -} \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/src/UniSpy.Server.GameTrafficRelay.csproj b/src/Servers/GameTrafficRelay/src/UniSpy.Server.GameTrafficRelay.csproj index f7ad3587f..2aabf0df9 100644 --- a/src/Servers/GameTrafficRelay/src/UniSpy.Server.GameTrafficRelay.csproj +++ b/src/Servers/GameTrafficRelay/src/UniSpy.Server.GameTrafficRelay.csproj @@ -23,6 +23,6 @@ - + \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/test/GameTest.cs b/src/Servers/GameTrafficRelay/test/GameTest.cs index c0fb915c2..cde87d187 100644 --- a/src/Servers/GameTrafficRelay/test/GameTest.cs +++ b/src/Servers/GameTrafficRelay/test/GameTest.cs @@ -3,6 +3,7 @@ using Xunit; using UniSpy.Server.GameTrafficRelay.Controller; using System.Net.Sockets; +using UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay; namespace UniSpy.Server.GameTrafficRelay.Test { @@ -36,8 +37,8 @@ public void GetNagNegotiationInfo() { Cookie = 123456, ServerId = System.Guid.NewGuid(), - GameClientIP = "127.0.0.1:1234", - GameServerIP = "127.0.0.1:1235" + GameClientIPs = new System.Collections.Generic.List() { "127.0.0.1:1234" }, + GameServerIPs = new System.Collections.Generic.List() { "127.0.0.1:1235" } }; var resp = controller.GetNatNegotiationInfo(request).Result; @@ -67,7 +68,6 @@ public void GetNagNegotiationInfo() // // because we are not runing server, so the server object is null // Assert.Null(resp.IPEndPoint1); // Assert.Null(resp.IPEndPoint2); - Thread.Sleep(20000); } } } \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Abstraction/Interface/IStorageOperation.cs b/src/Servers/NatNegotiation/src/Abstraction/Interface/IStorageOperation.cs index 9a22c1584..6d9b4c42a 100644 --- a/src/Servers/NatNegotiation/src/Abstraction/Interface/IStorageOperation.cs +++ b/src/Servers/NatNegotiation/src/Abstraction/Interface/IStorageOperation.cs @@ -1,16 +1,19 @@ using System; using System.Collections.Generic; +using UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay; using UniSpy.Server.NatNegotiation.Aggregate.Redis; +using UniSpy.Server.NatNegotiation.Aggregate.Redis.Fail; namespace UniSpy.Server.NatNegotiation.Abstraction.Interface { public interface IStorageOperation { + List GetAvaliableRelayServers(); void UpdateInitInfo(NatAddressInfo info); int CountInitInfo(uint cookie, byte version); List GetInitInfos(Guid serverId, uint cookie); void RemoveInitInfo(NatAddressInfo info); - void UpdateNatFailInfo(NatNegotiation.Aggregate.Redis.Fail.NatFailInfo info); - int GetNatFailInfo(NatNegotiation.Aggregate.Redis.Fail.NatFailInfo info); + void UpdateNatFailInfo(NatFailInfo info); + int GetNatFailInfo(NatFailInfo info); } } \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionListener.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs similarity index 73% rename from src/Servers/GameTrafficRelay/src/Aggregate/ConnectionListener.cs rename to src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs index 4ca101237..a933afc08 100644 --- a/src/Servers/GameTrafficRelay/src/Aggregate/ConnectionListener.cs +++ b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs @@ -1,15 +1,19 @@ using System; +using System.Collections.Generic; using System.Linq; using System.Net; using System.Threading; +using UniSpy.Server.Core.Abstraction.Interface; using UniSpy.Server.Core.Extension; using UniSpy.Server.Core.Logging; -using UniSpy.Server.GameTrafficRelay.Controller; +using UniSpy.Server.Core.Network.Udp.Server; +using UniSpy.Server.NatNegotiation.Handler.CmdHandler; -namespace UniSpy.Server.GameTrafficRelay.Aggregate +namespace UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay { public class ConnectionListener : NetCoreServer.UdpServer { + private IConnectionManager _manager; public uint Cookie { get; private set; } public IPEndPoint ListeningEndPoint => (IPEndPoint)Endpoint; private EasyTimer _timer; @@ -17,15 +21,25 @@ public class ConnectionListener : NetCoreServer.UdpServer private IPAddress _gameClientAddress; private IPEndPoint _gameServerEndPoint; private IPEndPoint _gameClientEndPoint; - public ConnectionListener(IPEndPoint listeningEndPoint, uint cookie, IPAddress gameServerAddr, IPAddress gameClientAddr) : base(listeningEndPoint) + private List _gameServerValidIPs; + private List _gameClientValidIPs; + public ConnectionListener(IPEndPoint listeningEndPoint, uint cookie, List gameServerIPs, List gameClientIPs) : base(listeningEndPoint) { - _gameServerAddress = gameServerAddr; - _gameClientAddress = gameClientAddr; + _manager = new UdpConnectionManager(listeningEndPoint); + // _manager.OnInitialization + // _gameServerAddress = IPEndPoint.Parse(request.GameServerIP); + // _gameClientAddress = IPEndPoint.Parse(request.GameClientIP); + _gameServerValidIPs = gameServerIPs; + _gameClientValidIPs = gameClientIPs; Cookie = cookie; _timer = new EasyTimer(TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(10), CheckExpiredClient); // after create listener we start it Start(); LogWriter.LogDebug($"[{ListeningEndPoint}] gamespy client listener started."); + } + private void OnInit() + { + } protected override void OnStarted() => ReceiveAsync(); private void CheckExpiredClient() @@ -35,17 +49,13 @@ private void CheckExpiredClient() if (!IsDisposed) { Dispose(); - NatNegotiationController.ConnectionListeners.TryRemove(this.Cookie, out _); + PingHandler.ConnectionListeners.TryRemove(this.Cookie, out _); LogWriter.LogDebug($"[{ListeningEndPoint}] gamespy listener shutdown."); _timer.Dispose(); } } } - protected override void OnReceived(EndPoint endpoint, byte[] buffer, long offset, long size) - { - var message = buffer.Skip((int)offset).Take((int)size).ToArray(); - OnReceived(endpoint, message); - } + protected override void OnReceived(EndPoint endpoint, byte[] buffer, long offset, long size) => OnReceived(endpoint, buffer.Skip((int)offset).Take((int)size).ToArray()); private bool CheckValidation(byte[] buffer) { var magic = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2 }; @@ -69,11 +79,11 @@ protected void OnReceived(EndPoint endPoint, byte[] buffer) // we only accept the gamespy client message if (_gameClientEndPoint is null || _gameClientEndPoint is null) { - if (_gameServerAddress.Equals(((IPEndPoint)endPoint).Address) && _gameServerEndPoint is null) + if (_gameServerValidIPs.Contains(endPoint.ToString()) && _gameServerEndPoint is null) { _gameServerEndPoint = (IPEndPoint)endPoint; } - else if (_gameClientAddress.Equals(((IPEndPoint)endPoint).Address) && _gameClientEndPoint is null && !_gameClientAddress.Equals((IPEndPoint)endPoint)) + else if (_gameClientValidIPs.Contains(endPoint.ToString()) && _gameClientEndPoint is null && !_gameClientAddress.Equals((IPEndPoint)endPoint)) { _gameClientEndPoint = (IPEndPoint)endPoint; } diff --git a/src/Servers/GameTrafficRelay/src/Contracts/NatNegotiation.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/NatNegotiation.cs similarity index 68% rename from src/Servers/GameTrafficRelay/src/Contracts/NatNegotiation.cs rename to src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/NatNegotiation.cs index da50d625e..e49d7576e 100644 --- a/src/Servers/GameTrafficRelay/src/Contracts/NatNegotiation.cs +++ b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/NatNegotiation.cs @@ -1,22 +1,20 @@ +using System.Collections.Generic; using System; -namespace UniSpy.Server.GameTrafficRelay +namespace UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay { public record NatNegotiationRequest { - public uint Cookie { get; set; } public Guid ServerId { get; set; } /// /// Gameserver public ip endpoint, used to validate the negotiator's ip /// - // [JsonConverter(typeof(IPEndPointConverter))] - public string GameServerIP { get; set; } + public List GameServerIPs { get; set; } /// /// Gameclient public ip endpoint, used to validate the negotiator's ip /// - // [JsonConverter(typeof(IPEndPointConverter))] - public string GameClientIP { get; set; } + public List GameClientIPs { get; set; } } public record NatNegotiationResponse { diff --git a/src/Servers/GameTrafficRelay/src/Aggregate/NetworkUtils.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/NetworkUtils.cs similarity index 96% rename from src/Servers/GameTrafficRelay/src/Aggregate/NetworkUtils.cs rename to src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/NetworkUtils.cs index 2d2a170ff..af7e98cc8 100644 --- a/src/Servers/GameTrafficRelay/src/Aggregate/NetworkUtils.cs +++ b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/NetworkUtils.cs @@ -2,7 +2,7 @@ using System.Linq; using System.Net.NetworkInformation; -namespace UniSpy.Server.GameTrafficRelay.Entity +namespace UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay { public sealed class NetworkUtils { diff --git a/src/Servers/GameTrafficRelay/src/Aggregate/RelayServerInfo.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelayServerInfo.cs similarity index 92% rename from src/Servers/GameTrafficRelay/src/Aggregate/RelayServerInfo.cs rename to src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelayServerInfo.cs index 1c03dffc9..45111870c 100644 --- a/src/Servers/GameTrafficRelay/src/Aggregate/RelayServerInfo.cs +++ b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelayServerInfo.cs @@ -6,7 +6,7 @@ using UniSpy.Server.Core.Extension.Redis; using UniSpy.Server.Core.Misc; -namespace UniSpy.Server.GameTrafficRelay.Entity +namespace UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay { public record RelayServerInfo : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject { diff --git a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelaySwitcher.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelaySwitcher.cs new file mode 100644 index 000000000..7baa6b3bd --- /dev/null +++ b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelaySwitcher.cs @@ -0,0 +1,26 @@ +using UniSpy.Server.Core.Abstraction.Interface; +using UniSpy.Server.NatNegotiation.Application; +using UniSpy.Server.NatNegotiation.Contract.Request; +using UniSpy.Server.NatNegotiation.Enumerate; +using UniSpy.Server.NatNegotiation.Handler.CmdHandler; + +namespace UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay +{ + public class RelaySwitcher : Handler.CmdSwitcher + { + public RelaySwitcher(Client client, byte[] rawRequest) : base(client, rawRequest) + { + } + protected override IHandler CreateCmdHandlers(object name, object rawRequest) + { + var req = (byte[])rawRequest; + switch ((Enumerate.RequestType)name) + { + case RequestType.Ping: + return new PingHandler(_client, new PingRequest(req)); + default: + throw new NatNegotiation.Exception("GameTrafficRelay only process ping request"); + } + } + } +} \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/src/Aggregate/ServerStatusReporter.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ServerStatusReporter.cs similarity index 79% rename from src/Servers/GameTrafficRelay/src/Aggregate/ServerStatusReporter.cs rename to src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ServerStatusReporter.cs index 0d8a9fe4d..da0e482e1 100644 --- a/src/Servers/GameTrafficRelay/src/Aggregate/ServerStatusReporter.cs +++ b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ServerStatusReporter.cs @@ -1,10 +1,9 @@ using System; -using UniSpy.Server.GameTrafficRelay.Controller; using UniSpy.Server.Core.Extension; -namespace UniSpy.Server.GameTrafficRelay.Entity +namespace UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay { - internal class ServerStatusReporter + public class ServerStatusReporter { private EasyTimer _myTimer; private RedisClient _redisClient = new RedisClient(); @@ -24,7 +23,7 @@ private void UpdateServerInfo() { ServerID = _server.Id, PublicIPEndPoint = _server.PublicIPEndPoint, - ClientCount = NatNegotiationController.ConnectionListeners.Values.Count * 2 + ClientCount = Handler.CmdHandler.PingHandler.ConnectionListeners.Values.Count }; _ = _redisClient.SetValueAsync(info); var data = _redisClient.GetKeyValues(); diff --git a/src/Servers/NatNegotiation/src/Application/StorageOperation.cs b/src/Servers/NatNegotiation/src/Application/StorageOperation.cs index c37b21f60..3957daff5 100644 --- a/src/Servers/NatNegotiation/src/Application/StorageOperation.cs +++ b/src/Servers/NatNegotiation/src/Application/StorageOperation.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using UniSpy.Server.NatNegotiation.Abstraction.Interface; -using UniSpy.Server.NatNegotiation.Aggregate.Redis; +using UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay; namespace UniSpy.Server.NatNegotiation.Application { @@ -11,9 +11,14 @@ internal sealed class StorageOperation : IStorageOperation /// /// natneg init information redis server. /// - private static RedisClient _redisClient = new RedisClient(); + private static Aggregate.Redis.RedisClient _redisClient = new Aggregate.Redis.RedisClient(); + private static Aggregate.GameTrafficRelay.RedisClient _relayRedisClient = new Aggregate.GameTrafficRelay.RedisClient(); private static NatNegotiation.Aggregate.Redis.Fail.RedisClient _natFailRedisClient = new NatNegotiation.Aggregate.Redis.Fail.RedisClient(); public static IStorageOperation Persistance = new StorageOperation(); + public List GetAvaliableRelayServers() + { + return _relayRedisClient.Context.ToList(); + } public int CountInitInfo(uint cookie, byte version) { return _redisClient.Context.Count(k => @@ -21,19 +26,19 @@ public int CountInitInfo(uint cookie, byte version) && k.Version == version); } - public List GetInitInfos(Guid serverId, uint cookie) + public List GetInitInfos(Guid serverId, uint cookie) { return _redisClient.Context.Where(k => k.ServerID == serverId && k.Cookie == cookie).ToList(); } - public void UpdateInitInfo(NatAddressInfo info) + public void UpdateInitInfo(Aggregate.Redis.NatAddressInfo info) { _ = _redisClient.SetValueAsync(info); } - public void RemoveInitInfo(NatAddressInfo info) + public void RemoveInitInfo(Aggregate.Redis.NatAddressInfo info) { _redisClient.DeleteKeyValue(info); } diff --git a/src/Servers/NatNegotiation/src/Contract/Request/PingRequest.cs b/src/Servers/NatNegotiation/src/Contract/Request/PingRequest.cs index 31dea4953..1cc61040c 100644 --- a/src/Servers/NatNegotiation/src/Contract/Request/PingRequest.cs +++ b/src/Servers/NatNegotiation/src/Contract/Request/PingRequest.cs @@ -1,32 +1,30 @@ -// using System; -// using System.Linq; -// using System.Net; -// using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -// -// using UniSpy.Server.NatNegotiation.Enumerate; +using System; +using System.Linq; +using System.Net; +using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -// namespace UniSpy.Server.NatNegotiation.Contract.Request -// { -// /// -// /// When client can not start p2p game, it will use our server to redirect his game traffic -// /// -// -// public sealed class PingRequest : CommonRequestBase -// { -// public IPEndPoint GuessedTargetIPEndPoint { get; set; } -// public byte? GotYourData { get; private set; } -// public byte? IsFinished { get; private set; } -// public PingRequest(byte[] rawRequest) : base(rawRequest) -// { -// } -// public override void Parse() -// { -// base.Parse(); -// var _ipBytes = RawRequest.Skip(12).Take(4).ToArray(); -// var _portBytes = RawRequest.Skip(16).Take(2).ToArray(); -// GuessedTargetIPEndPoint = new IPEndPoint(new IPAddress(_ipBytes), BitConverter.ToUInt16(_portBytes)); -// GotYourData = RawRequest[18]; -// IsFinished = RawRequest[19]; -// } -// } -// } \ No newline at end of file +using UniSpy.Server.NatNegotiation.Enumerate; + +namespace UniSpy.Server.NatNegotiation.Contract.Request +{ + /// + /// When client can not start p2p game, it will use our server to redirect his game traffic + /// + + public sealed class PingRequest : CommonRequestBase + { + public IPEndPoint GuessedTargetIPEndPoint { get; set; } + public bool? GotYourData { get; private set; } + public bool? IsFinished { get; private set; } + public PingRequest(byte[] rawRequest) : base(rawRequest) { } + public override void Parse() + { + base.Parse(); + var _ipBytes = RawRequest.Skip(12).Take(4).ToArray(); + var _portBytes = RawRequest.Skip(16).Take(2).ToArray(); + GuessedTargetIPEndPoint = new IPEndPoint(new IPAddress(_ipBytes), BitConverter.ToUInt16(_portBytes)); + GotYourData = Convert.ToBoolean(RawRequest[18]); + IsFinished = Convert.ToBoolean(RawRequest[19]); + } + } +} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Contract/Response/PingResponse.cs b/src/Servers/NatNegotiation/src/Contract/Response/PingResponse.cs new file mode 100644 index 000000000..971b48afe --- /dev/null +++ b/src/Servers/NatNegotiation/src/Contract/Response/PingResponse.cs @@ -0,0 +1,18 @@ +using UniSpy.Server.Core.Abstraction.BaseClass; +using UniSpy.Server.NatNegotiation.Contract.Request; + +namespace UniSpy.Server.NatNegotiation.Contract.Response +{ + public class PingResponse : ResponseBase + { + private new PingRequest _request => (PingRequest)base._request; + public PingResponse(PingRequest request) : base(request, null) + { + } + + public override void Build() + { + SendingBuffer = _request.RawRequest; + } + } +} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs index 1162d529c..ca50542d1 100755 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs +++ b/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs @@ -7,10 +7,10 @@ using UniSpy.Server.NatNegotiation.Contract.Request; using UniSpy.Server.NatNegotiation.Contract.Response; using UniSpy.Server.NatNegotiation.Contract.Result; -using UniSpy.Server.GameTrafficRelay; using RestSharp; using RestSharp.Serializers.NewtonsoftJson; using UniSpy.Server.Core.Logging; +using UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay; namespace UniSpy.Server.NatNegotiation.Handler.CmdHandler { @@ -105,19 +105,21 @@ private void UsingPrivateAddressToNegotiate() } private void UsingGameRelayServerToNegotiate() { - var relayServers = GameTrafficRelay.Application.StorageOperation.Persistance.GetAvaliableRelayServers(); + var relayServers = StorageOperation.Persistance.GetAvaliableRelayServers(); if (relayServers.Count == 0) { throw new NatNegotiation.Exception("No GameRelayServer found, you must start a GameRelayServer!"); } //todo the optimized server will be selected var relayEndPoint = relayServers.OrderBy(x => x.ClientCount).First().PublicIPEndPoint; + var myIPs = _myInitInfo.AddressInfos.Select(x=>x.Value.PublicIPEndPoint.ToString()).ToList(); + var otherIPs = _othersInitInfo.AddressInfos.Select(x=>x.Value.PublicIPEndPoint.ToString()).ToList(); var req = new NatNegotiationRequest() { Cookie = _myInitInfo.Cookie, ServerId = _client.Server.Id, - GameClientIP = _myInitInfo.ClientIndex == NatClientIndex.GameClient ? _myInitInfo.PublicIPEndPoint.ToString() : _othersInitInfo.PublicIPEndPoint.ToString(), - GameServerIP = _myInitInfo.ClientIndex == NatClientIndex.GameServer ? _myInitInfo.PublicIPEndPoint.ToString() : _othersInitInfo.PublicIPEndPoint.ToString() + GameClientIPs = _myInitInfo.ClientIndex == NatClientIndex.GameClient ? myIPs : otherIPs, + GameServerIPs = _myInitInfo.ClientIndex == NatClientIndex.GameServer ? myIPs : otherIPs }; var client = new RestClient($"http://{relayEndPoint}/NatNegotiation").UseNewtonsoftJson(); var request = new RestRequest().AddJsonBody(req); diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/PingHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/PingHandler.cs new file mode 100644 index 000000000..1256f4271 --- /dev/null +++ b/src/Servers/NatNegotiation/src/Handler/CmdHandler/PingHandler.cs @@ -0,0 +1,20 @@ +using System.Collections.Concurrent; +using UniSpy.Server.Core.Abstraction.BaseClass; +using UniSpy.Server.Core.Abstraction.Interface; +using UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay; +using UniSpy.Server.NatNegotiation.Contract.Request; + +namespace UniSpy.Server.NatNegotiation.Handler.CmdHandler +{ + public class PingHandler : CmdHandlerBase + { + public static ConcurrentDictionary ConnectionListeners = new ConcurrentDictionary(); + public PingHandler(IClient client, PingRequest request) : base(client, request) + { + } + protected override void RequestCheck() + { + base.RequestCheck(); + } + } +} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Handler/CmdSwitcher.cs b/src/Servers/NatNegotiation/src/Handler/CmdSwitcher.cs index 87d69547b..400f4fd24 100755 --- a/src/Servers/NatNegotiation/src/Handler/CmdSwitcher.cs +++ b/src/Servers/NatNegotiation/src/Handler/CmdSwitcher.cs @@ -8,7 +8,7 @@ namespace UniSpy.Server.NatNegotiation.Handler { - public sealed class CmdSwitcher : CmdSwitcherBase + public class CmdSwitcher : CmdSwitcherBase { private new byte[] _rawRequest => (byte[])base._rawRequest; private new Client _client => (Client)base._client; diff --git a/src/Servers/NatNegotiation/src/UniSpy.Server.NatNegotiation.csproj b/src/Servers/NatNegotiation/src/UniSpy.Server.NatNegotiation.csproj index 711438cda..5932d9961 100755 --- a/src/Servers/NatNegotiation/src/UniSpy.Server.NatNegotiation.csproj +++ b/src/Servers/NatNegotiation/src/UniSpy.Server.NatNegotiation.csproj @@ -19,6 +19,6 @@ - + \ No newline at end of file diff --git a/src/Servers/QueryReport/src/Application/Server.cs b/src/Servers/QueryReport/src/Application/Server.cs index cd47fe407..90704065d 100644 --- a/src/Servers/QueryReport/src/Application/Server.cs +++ b/src/Servers/QueryReport/src/Application/Server.cs @@ -21,7 +21,6 @@ public override void Start() V2.Application.StorageOperation.NatNegChannel.Subscribe(); } protected override IClient CreateClient(IConnection connection) => new Client(connection, this); - protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new UdpConnectionManager(endPoint); } From c765a6fa13a539a3b9dec696fa539328bcd537c0 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 15 Jul 2023 20:07:12 +0800 Subject: [PATCH 029/231] fix(gtr): reference null error --- .../src/Aggregate/GameTrafficRelay/ConnectionListener.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs index a933afc08..222a22923 100644 --- a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs +++ b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs @@ -17,8 +17,6 @@ public class ConnectionListener : NetCoreServer.UdpServer public uint Cookie { get; private set; } public IPEndPoint ListeningEndPoint => (IPEndPoint)Endpoint; private EasyTimer _timer; - private IPAddress _gameServerAddress; - private IPAddress _gameClientAddress; private IPEndPoint _gameServerEndPoint; private IPEndPoint _gameClientEndPoint; private List _gameServerValidIPs; @@ -77,13 +75,14 @@ protected void OnReceived(EndPoint endPoint, byte[] buffer) ThreadPool.QueueUserWorkItem(o => { try { ReceiveAsync(); } catch { } }); // we only accept the gamespy client message - if (_gameClientEndPoint is null || _gameClientEndPoint is null) + if (_gameServerEndPoint is null || _gameClientEndPoint is null) { + if (_gameServerValidIPs.Contains(endPoint.ToString()) && _gameServerEndPoint is null) { _gameServerEndPoint = (IPEndPoint)endPoint; } - else if (_gameClientValidIPs.Contains(endPoint.ToString()) && _gameClientEndPoint is null && !_gameClientAddress.Equals((IPEndPoint)endPoint)) + else if (_gameClientValidIPs.Contains(endPoint.ToString()) && _gameClientEndPoint is null) { _gameClientEndPoint = (IPEndPoint)endPoint; } @@ -91,6 +90,7 @@ protected void OnReceived(EndPoint endPoint, byte[] buffer) { //ignore } + LogWriter.LogDebug($"[{endPoint}] [recv] {StringExtensions.ConvertPrintableBytesToString(buffer)} [{StringExtensions.ConvertByteToHexString(buffer)}]"); } else From 36a0ae26effa6542d28c42cfd4c23548f0c4c518 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 7 Aug 2023 21:05:05 +0800 Subject: [PATCH 030/231] refactor: update credits --- README.MD | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 README.MD diff --git a/README.MD b/README.MD new file mode 100644 index 000000000..23ca4513e --- /dev/null +++ b/README.MD @@ -0,0 +1,30 @@ +# UniSpy Server + +[![license](https://img.shields.io/github/license/GameProgressive/UniSpyServer.svg)](../LICENSE) +![CIPass](https://github.com/GameProgressive/UniSpyServer/workflows/CI/badge.svg)\ +![platforms](https://img.shields.io/badge/platform-win32%20%7C%20win64%20%7C%20linux%20%7C%20osx-brightgreen.svg)\ +[![discord-banner](https://discord.com/api/guilds/512314008079171615/widget.png?style=banner2)](https://discord.gg/NpggYaD) + +UniSpy is a GameSpy Project that aims to create GameSpy services. + +The server is written in C# and is inspired by the old OpenSpy project. + +## Information & Documentation +See the [wiki](https://github.com/GameProgressive/UniSpyServer/wiki) for more information about project and the UniSpy Server. +You can also use our Software Development Kit to create games or learn about the [UniSpySDK](https://github.com/GameProgressive/UniSpySDK). + +Technical papers and documentation about the GameSpy protocol and the games that use it, [GameSpyDocs](https://github.com/GameProgressive/GameSpyDocs). + +## Credits +* The [contributors](https://github.com/GameProgressive/UniSpyServer/graphs/contributors) of the project +* [Luigi Auriemma](https://aluigi.altervista.org/papers.htm#distrust) for his gamespy papers that were used as a reference +* [BattleSpy](https://github.com/BF2Statistics/BattleSpy) for their library, that we used as a base for UniSpy +* [NetCoreServer](https://github.com/chronoxor/NetCoreServer) for their TCP and UDP server +* [openspy-core-v2](https://github.com/chc/openspy-core-v2) for code reference that were used to understand GameSpy protocol in detail + +## Thanks +* [CHC](https://github.com/chc) for sharing his knowledge and give us much help + + +## License +This project is licensed under the [GNU Affero General Public License v3.0](../LICENSE). From e55d020640df6cd34cf89cc6336019c866341d7d Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 7 Aug 2023 21:06:03 +0800 Subject: [PATCH 031/231] Update README.MD --- .github/README.MD | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/README.MD b/.github/README.MD index 6824d0632..23ca4513e 100644 --- a/.github/README.MD +++ b/.github/README.MD @@ -20,6 +20,10 @@ Technical papers and documentation about the GameSpy protocol and the games that * [Luigi Auriemma](https://aluigi.altervista.org/papers.htm#distrust) for his gamespy papers that were used as a reference * [BattleSpy](https://github.com/BF2Statistics/BattleSpy) for their library, that we used as a base for UniSpy * [NetCoreServer](https://github.com/chronoxor/NetCoreServer) for their TCP and UDP server +* [openspy-core-v2](https://github.com/chc/openspy-core-v2) for code reference that were used to understand GameSpy protocol in detail + +## Thanks +* [CHC](https://github.com/chc) for sharing his knowledge and give us much help ## License From f8223fc2742e479f045322b87896974ab22ced51 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 7 Aug 2023 21:07:09 +0800 Subject: [PATCH 032/231] refactor: update credit --- README.MD | 30 ------------------------------ 1 file changed, 30 deletions(-) delete mode 100644 README.MD diff --git a/README.MD b/README.MD deleted file mode 100644 index 23ca4513e..000000000 --- a/README.MD +++ /dev/null @@ -1,30 +0,0 @@ -# UniSpy Server - -[![license](https://img.shields.io/github/license/GameProgressive/UniSpyServer.svg)](../LICENSE) -![CIPass](https://github.com/GameProgressive/UniSpyServer/workflows/CI/badge.svg)\ -![platforms](https://img.shields.io/badge/platform-win32%20%7C%20win64%20%7C%20linux%20%7C%20osx-brightgreen.svg)\ -[![discord-banner](https://discord.com/api/guilds/512314008079171615/widget.png?style=banner2)](https://discord.gg/NpggYaD) - -UniSpy is a GameSpy Project that aims to create GameSpy services. - -The server is written in C# and is inspired by the old OpenSpy project. - -## Information & Documentation -See the [wiki](https://github.com/GameProgressive/UniSpyServer/wiki) for more information about project and the UniSpy Server. -You can also use our Software Development Kit to create games or learn about the [UniSpySDK](https://github.com/GameProgressive/UniSpySDK). - -Technical papers and documentation about the GameSpy protocol and the games that use it, [GameSpyDocs](https://github.com/GameProgressive/GameSpyDocs). - -## Credits -* The [contributors](https://github.com/GameProgressive/UniSpyServer/graphs/contributors) of the project -* [Luigi Auriemma](https://aluigi.altervista.org/papers.htm#distrust) for his gamespy papers that were used as a reference -* [BattleSpy](https://github.com/BF2Statistics/BattleSpy) for their library, that we used as a base for UniSpy -* [NetCoreServer](https://github.com/chronoxor/NetCoreServer) for their TCP and UDP server -* [openspy-core-v2](https://github.com/chc/openspy-core-v2) for code reference that were used to understand GameSpy protocol in detail - -## Thanks -* [CHC](https://github.com/chc) for sharing his knowledge and give us much help - - -## License -This project is licensed under the [GNU Affero General Public License v3.0](../LICENSE). From 408b05e74aea8b9f9cbe2096236bd9e9d2d8d4c7 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 7 Aug 2023 21:09:03 +0800 Subject: [PATCH 033/231] Update README.MD Grammar correction --- .github/README.MD | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README.MD b/.github/README.MD index 23ca4513e..fd1ea7c6e 100644 --- a/.github/README.MD +++ b/.github/README.MD @@ -23,7 +23,7 @@ Technical papers and documentation about the GameSpy protocol and the games that * [openspy-core-v2](https://github.com/chc/openspy-core-v2) for code reference that were used to understand GameSpy protocol in detail ## Thanks -* [CHC](https://github.com/chc) for sharing his knowledge and give us much help +* [CHC](https://github.com/chc) for sharing his knowledge and giving us much help ## License From 1d1d768d5293a40fd364bf7a80fbe6191f018688 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 11 Aug 2023 07:59:57 +0800 Subject: [PATCH 034/231] refactor(web): correction response --- .../src/V1/Aggregate/Enctype1.cs | 49 +++++++++++++++++-- .../src/V1/Application/ClientInfo.cs | 4 +- .../src/V1/Contract/Request/ListRequest.cs | 6 ++- .../src/Abstraction/SoapEnvelopBase.cs | 6 +++ .../Request/GetPurchaseHistoryRequest.cs | 14 ++++-- .../Request/GetStoreAvailabilityRequest.cs | 8 +-- .../Response/GetPurchaseHistoryResponse.cs | 6 ++- .../Response/GetStoreAvailabilityResponse.cs | 5 +- .../Result/GetPurchaseHistoryResult.cs | 2 +- .../Result/GetStoreAvailabilityResult.cs | 11 ++++- .../Handler/GetPurchaseHistoryHandler.cs | 2 +- .../Handler/GetStoreAvailabilityHandler.cs | 14 ------ .../Sake/Handler/SearchForRecordsHandler.cs | 1 - .../WebServer/test/Auth/RawRequests.cs | 8 +-- 14 files changed, 93 insertions(+), 43 deletions(-) diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs index 2dbb05a54..117c08bea 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Linq; using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; @@ -10,16 +11,15 @@ public class Enctype1 : EnctypeBase, IEnctype1Test /// The server key /// 256 bytes /// - public static readonly byte[] Enctype1Table = new byte[] { 0x01, 0xba, 0xfa, 0xb2, 0x51, 0x00, 0x54, 0x80, 0x75, 0x16, 0x8e, 0x8e, 0x02, 0x08, 0x36, 0xa5, 0x2d, 0x05, 0x0d, 0x16, 0x52, 0x07, 0xb4, 0x22, 0x8c, 0xe9, 0x09, 0xd6, 0xb9, 0x26, 0x00, 0x04, 0x06, 0x05, 0x00, 0x13, 0x18, 0xc4, 0x1e, 0x5b, 0x1d, 0x76, 0x74, 0xfc, 0x50, 0x51, 0x06, 0x16, 0x00, 0x51, 0x28, 0x00, 0x04, 0x0a, 0x29, 0x78, 0x51, 0x00, 0x01, 0x11, 0x52, 0x16, 0x06, 0x4a, 0x20, 0x84, 0x01, 0xa2, 0x1e, 0x16, 0x47, 0x16, 0x32, 0x51, 0x9a, 0xc4, 0x03, 0x2a, 0x73, 0xe1, 0x2d, 0x4f, 0x18, 0x4b, 0x93, 0x4c, 0x0f, 0x39, 0x0a, 0x00, 0x04, 0xc0, 0x12, 0x0c, 0x9a, 0x5e, 0x02, 0xb3, 0x18, 0xb8, 0x07, 0x0c, 0xcd, 0x21, 0x05, 0xc0, 0xa9, 0x41, 0x43, 0x04, 0x3c, 0x52, 0x75, 0xec, 0x98, 0x80, 0x1d, 0x08, 0x02, 0x1d, 0x58, 0x84, 0x01, 0x4e, 0x3b, 0x6a, 0x53, 0x7a, 0x55, 0x56, 0x57, 0x1e, 0x7f, 0xec, 0xb8, 0xad, 0x00, 0x70, 0x1f, 0x82, 0xd8, 0xfc, 0x97, 0x8b, 0xf0, 0x83, 0xfe, 0x0e, 0x76, 0x03, 0xbe, 0x39, 0x29, 0x77, 0x30, 0xe0, 0x2b, 0xff, 0xb7, 0x9e, 0x01, 0x04, 0xf8, 0x01, 0x0e, 0xe8, 0x53, 0xff, 0x94, 0x0c, 0xb2, 0x45, 0x9e, 0x0a, 0xc7, 0x06, 0x18, 0x01, 0x64, 0xb0, 0x03, 0x98, 0x01, 0xeb, 0x02, 0xb0, 0x01, 0xb4, 0x12, 0x49, 0x07, 0x1f, 0x5f, 0x5e, 0x5d, 0xa0, 0x4f, 0x5b, 0xa0, 0x5a, 0x59, 0x58, 0xcf, 0x52, 0x54, 0xd0, 0xb8, 0x34, 0x02, 0xfc, 0x0e, 0x42, 0x29, 0xb8, 0xda, 0x00, 0xba, 0xb1, 0xf0, 0x12, 0xfd, 0x23, 0xae, 0xb6, 0x45, 0xa9, 0xbb, 0x06, 0xb8, 0x88, 0x14, 0x24, 0xa9, 0x00, 0x14, 0xcb, 0x24, 0x12, 0xae, 0xcc, 0x57, 0x56, 0xee, 0xfd, 0x08, 0x30, 0xd9, 0xfd, 0x8b, 0x3e, 0x0a, 0x84, 0x46, 0xfa, 0x77, 0xb8 }; + public static readonly byte[] Enctype1Table = new byte[256] { 0x01, 0xba, 0xfa, 0xb2, 0x51, 0x00, 0x54, 0x80, 0x75, 0x16, 0x8e, 0x8e, 0x02, 0x08, 0x36, 0xa5, 0x2d, 0x05, 0x0d, 0x16, 0x52, 0x07, 0xb4, 0x22, 0x8c, 0xe9, 0x09, 0xd6, 0xb9, 0x26, 0x00, 0x04, 0x06, 0x05, 0x00, 0x13, 0x18, 0xc4, 0x1e, 0x5b, 0x1d, 0x76, 0x74, 0xfc, 0x50, 0x51, 0x06, 0x16, 0x00, 0x51, 0x28, 0x00, 0x04, 0x0a, 0x29, 0x78, 0x51, 0x00, 0x01, 0x11, 0x52, 0x16, 0x06, 0x4a, 0x20, 0x84, 0x01, 0xa2, 0x1e, 0x16, 0x47, 0x16, 0x32, 0x51, 0x9a, 0xc4, 0x03, 0x2a, 0x73, 0xe1, 0x2d, 0x4f, 0x18, 0x4b, 0x93, 0x4c, 0x0f, 0x39, 0x0a, 0x00, 0x04, 0xc0, 0x12, 0x0c, 0x9a, 0x5e, 0x02, 0xb3, 0x18, 0xb8, 0x07, 0x0c, 0xcd, 0x21, 0x05, 0xc0, 0xa9, 0x41, 0x43, 0x04, 0x3c, 0x52, 0x75, 0xec, 0x98, 0x80, 0x1d, 0x08, 0x02, 0x1d, 0x58, 0x84, 0x01, 0x4e, 0x3b, 0x6a, 0x53, 0x7a, 0x55, 0x56, 0x57, 0x1e, 0x7f, 0xec, 0xb8, 0xad, 0x00, 0x70, 0x1f, 0x82, 0xd8, 0xfc, 0x97, 0x8b, 0xf0, 0x83, 0xfe, 0x0e, 0x76, 0x03, 0xbe, 0x39, 0x29, 0x77, 0x30, 0xe0, 0x2b, 0xff, 0xb7, 0x9e, 0x01, 0x04, 0xf8, 0x01, 0x0e, 0xe8, 0x53, 0xff, 0x94, 0x0c, 0xb2, 0x45, 0x9e, 0x0a, 0xc7, 0x06, 0x18, 0x01, 0x64, 0xb0, 0x03, 0x98, 0x01, 0xeb, 0x02, 0xb0, 0x01, 0xb4, 0x12, 0x49, 0x07, 0x1f, 0x5f, 0x5e, 0x5d, 0xa0, 0x4f, 0x5b, 0xa0, 0x5a, 0x59, 0x58, 0xcf, 0x52, 0x54, 0xd0, 0xb8, 0x34, 0x02, 0xfc, 0x0e, 0x42, 0x29, 0xb8, 0xda, 0x00, 0xba, 0xb1, 0xf0, 0x12, 0xfd, 0x23, 0xae, 0xb6, 0x45, 0xa9, 0xbb, 0x06, 0xb8, 0x88, 0x14, 0x24, 0xa9, 0x00, 0x14, 0xcb, 0x24, 0x12, 0xae, 0xcc, 0x57, 0x56, 0xee, 0xfd, 0x08, 0x30, 0xd9, 0xfd, 0x8b, 0x3e, 0x0a, 0x84, 0x46, 0xfa, 0x77, 0xb8 }; /// /// Client key /// 16 bytes /// - public static readonly byte[] ClientKey = new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + public static readonly byte[] ClientKey = new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; public byte[] Key { get; private set; } - byte[] IEnctype1Test.EncKey => _enckey; - + private uint[] _tbuff = new uint[326]; private byte[] _enckey = new byte[261]; public Enctype1(byte[] key) { @@ -31,6 +31,47 @@ public override byte[] Encrypt(byte[] data) { // create a copy var dataCopy = data.ToArray(); + if (data.Length! > 1) + { + throw new ServerBrowser.Exception("The encryption data can not be empty"); + } + int tempLen = (data.Length >> 1) - 17; + int encshare4Data = 0; + byte[] encshare4DataBytes = BitConverter.GetBytes(encshare4Data); + if (tempLen >= 0) + { + EncShare4(encshare4DataBytes, encshare4DataBytes.Length, _tbuff); + Encshare1(_tbuff, 0, data, data.Length, tempLen); + } + var scrambleData = Enumerable.Repeat(0x01, 16); + var encryption_key = new byte[258]; + // Func3() + var output = new uint[data.Length / sizeof(byte)]; + var outputBytes = output.SelectMany(BitConverter.GetBytes).ToArray(); + if (tempLen >= 0) + { + EncShare4(dataCopy, ClientKey.Length, output); + Func6(dataCopy, tempLen); + } + var newOutput = new List(); + // 4 bytes message length + // newOutput.AddRange(new byte[4] { 0x00, 0x00, 0x00, 0x00 }); + newOutput.Add(42); + newOutput.Add(218); + // 6 to 19 is unknown data, we do not use it + newOutput.AddRange(Enumerable.Repeat(0, 13)); + newOutput.AddRange(scrambleData); + // unused data + newOutput.AddRange(new byte[4] { 0x00, 0x00, 0x00, 0x00 }); + // encshare4data + newOutput.AddRange(new byte[4] { 0x00, 0x00, 0x00, 0x00 }); + // unused data + newOutput.AddRange(Enumerable.Repeat(0x00, 18)); + + newOutput.AddRange(dataCopy); + // insert encryption length to index 0 + newOutput.InsertRange(0, BitConverter.GetBytes(newOutput.Count).Reverse()); + throw new System.NotImplementedException(); } diff --git a/src/Servers/ServerBrowser/src/V1/Application/ClientInfo.cs b/src/Servers/ServerBrowser/src/V1/Application/ClientInfo.cs index 417c42927..781ffd9cf 100644 --- a/src/Servers/ServerBrowser/src/V1/Application/ClientInfo.cs +++ b/src/Servers/ServerBrowser/src/V1/Application/ClientInfo.cs @@ -4,8 +4,8 @@ namespace UniSpy.Server.ServerBrowser.V1.Application { public sealed class ClientInfo : ClientInfoBase { - public static string EncKey = @"000000"; - public static string ChallengeResponse = $@"\basic\\secure\{EncKey}\final\"; + public const string Challenge = @"000000"; + public const string ChallengeResponse = $@"\basic\\secure\{Challenge}\final\"; public string GameSecretKey { get; set; } public ClientInfo() { diff --git a/src/Servers/ServerBrowser/src/V1/Contract/Request/ListRequest.cs b/src/Servers/ServerBrowser/src/V1/Contract/Request/ListRequest.cs index a8c634e19..aa671df58 100644 --- a/src/Servers/ServerBrowser/src/V1/Contract/Request/ListRequest.cs +++ b/src/Servers/ServerBrowser/src/V1/Contract/Request/ListRequest.cs @@ -22,7 +22,11 @@ public ListRequest(string rawRequest) : base(rawRequest) } public override void Parse() { - base.Parse(); + // base.Parse(); + var firstGameIndex = RawRequest.IndexOf('\\', 10); + var gameNameHeader = RawRequest.Substring(0, firstGameIndex); + + if (KeyValues.ContainsKey("where")) { Filter = KeyValues["where"]; diff --git a/src/Servers/WebServer/src/Abstraction/SoapEnvelopBase.cs b/src/Servers/WebServer/src/Abstraction/SoapEnvelopBase.cs index 53a0998af..b2edeac4e 100644 --- a/src/Servers/WebServer/src/Abstraction/SoapEnvelopBase.cs +++ b/src/Servers/WebServer/src/Abstraction/SoapEnvelopBase.cs @@ -40,11 +40,17 @@ public void BackToParentElement() { CurrentElement = CurrentElement.Parent; } + /// + /// Add the element and go inside it + /// public virtual void Add(string name) { CurrentElement.Add(new XElement(_bodyNamespace + name)); ChangeToElement(name); } + /// + /// Add the element and stay current position + /// public virtual void Add(string name, object value) { CurrentElement.Add(new XElement(_bodyNamespace + name, value)); diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Request/GetPurchaseHistoryRequest.cs b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Request/GetPurchaseHistoryRequest.cs index 47ffb73c6..bc1742a2d 100644 --- a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Request/GetPurchaseHistoryRequest.cs +++ b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Request/GetPurchaseHistoryRequest.cs @@ -11,7 +11,7 @@ public class GetPurchaseHistoryRequest : RequestBase public string AccessToken { get; private set; } public string Proof { get; private set; } - + public string Certificate { get; private set; } public GetPurchaseHistoryRequest(string rawRequest) : base(rawRequest) { } @@ -21,20 +21,24 @@ public override void Parse() base.Parse(); if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "gameid")) { - throw new Exception("gameid is missing from the request"); + throw new WebServer.Exception("gameid is missing from the request"); } var gameid = _contentElement.Descendants().First(p => p.Name.LocalName == "gameid").Value; GameId = int.Parse(gameid); if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "proof")) { - throw new Exception("proof is missing from the request"); + throw new WebServer.Exception("proof is missing from the request"); } Proof = _contentElement.Descendants().First(p => p.Name.LocalName == "proof").Value; - + if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "certificate")) + { + throw new WebServer.Exception("certificate is missing from the request"); + } + Certificate = _contentElement.Descendants().First(p => p.Name.LocalName == "certificate").Value; if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "accesstoken")) { - throw new Exception("accesstoken is missing from the request"); + throw new WebServer.Exception("accesstoken is missing from the request"); } AccessToken = _contentElement.Descendants().First(p => p.Name.LocalName == "accesstoken").Value; } diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Request/GetStoreAvailabilityRequest.cs b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Request/GetStoreAvailabilityRequest.cs index 354d6a266..94997848e 100644 --- a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Request/GetStoreAvailabilityRequest.cs +++ b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Request/GetStoreAvailabilityRequest.cs @@ -20,27 +20,27 @@ public override void Parse() base.Parse(); if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "gameid")) { - throw new Exception("gameid is missing from the request"); + throw new WebServer.Exception("gameid is missing from the request"); } var gameid = _contentElement.Descendants().First(p => p.Name.LocalName == "gameid").Value; GameId = int.Parse(gameid); if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "version")) { - throw new Exception("version is missing from the request"); + throw new WebServer.Exception("version is missing from the request"); } var version = _contentElement.Descendants().First(p => p.Name.LocalName == "version").Value; Version = int.Parse(version); if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "region")) { - throw new Exception("region is missing from the request"); + throw new WebServer.Exception("region is missing from the request"); } Region = _contentElement.Descendants().First(p => p.Name.LocalName == "region").Value; if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "accesstoken")) { - throw new Exception("accesstoken is missing from the request"); + throw new WebServer.Exception("accesstoken is missing from the request"); } AccessToken = _contentElement.Descendants().First(p => p.Name.LocalName == "accesstoken").Value; } diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetPurchaseHistoryResponse.cs b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetPurchaseHistoryResponse.cs index e8258e710..965a1114b 100644 --- a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetPurchaseHistoryResponse.cs +++ b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetPurchaseHistoryResponse.cs @@ -16,10 +16,12 @@ public override void Build() { _content.Add("GetPurchaseHistoryResult"); _content.Add("status"); - _content.Add("code", _result.Status); - _content.ChangeToElement("GetPurchaseHistoryResult"); + _content.Add("code", _result.Code); _content.Add("orderpurchases"); + // we do not know the purchace content for each game, so currently we just make it 0 _content.Add("count", 0); + // _content.ChangeToElement("GetPurchaseHistoryResult"); + // _content.BackToParentElement(); base.Build(); } } diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetStoreAvailabilityResponse.cs b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetStoreAvailabilityResponse.cs index 000a19c3b..f3a5a5f40 100644 --- a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetStoreAvailabilityResponse.cs +++ b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetStoreAvailabilityResponse.cs @@ -15,9 +15,10 @@ public override void Build() { _content.Add("GetStoreAvailabilityResult"); _content.Add("status"); - _content.Add("code", _result.Status); + _content.Add("code", _result.Code); + _content.ChangeToElement("status"); + _content.Add("storestatusid", _result.StoreStatusId); _content.ChangeToElement("GetStoreAvailabilityResult"); - _content.Add("storestatusid", _result.StoreResult); base.Build(); } } diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Result/GetPurchaseHistoryResult.cs b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Result/GetPurchaseHistoryResult.cs index 9bc9060e4..63ea47346 100644 --- a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Result/GetPurchaseHistoryResult.cs +++ b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Result/GetPurchaseHistoryResult.cs @@ -4,6 +4,6 @@ namespace UniSpy.Server.WebServer.Module.Direct2Game.Contract.Result { public class GetPurchaseHistoryResult : ResultBase { - public int Status { get; set; } + public int Code { get; set; } = 0; } } diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Result/GetStoreAvailabilityResult.cs b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Result/GetStoreAvailabilityResult.cs index ea9659d92..a7f6f59d3 100644 --- a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Result/GetStoreAvailabilityResult.cs +++ b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Result/GetStoreAvailabilityResult.cs @@ -2,10 +2,17 @@ namespace UniSpy.Server.WebServer.Module.Direct2Game.Contract.Result { + public enum AvaliableCode + { + StoreOnline = 10, + StoreOfflineForMaintaince = 20, + StoreOfflineRetired = 50, + StoreNotYetLaunched = 100 + } public class GetStoreAvailabilityResult : ResultBase { - public int Status { get; set; } - public int StoreResult { get; set; } + public int Code { get; set; } = 0; + public AvaliableCode StoreStatusId { get; set; } = AvaliableCode.StoreOnline; public GetStoreAvailabilityResult() { diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Handler/GetPurchaseHistoryHandler.cs b/src/Servers/WebServer/src/Module/Direct2Game/Handler/GetPurchaseHistoryHandler.cs index 3d92db8d1..c48997f8f 100644 --- a/src/Servers/WebServer/src/Module/Direct2Game/Handler/GetPurchaseHistoryHandler.cs +++ b/src/Servers/WebServer/src/Module/Direct2Game/Handler/GetPurchaseHistoryHandler.cs @@ -24,7 +24,7 @@ protected override void DataOperation() protected override void ResponseConstruct() { - _result.Status = 0; + _result.Code = 0; _response = new GetPurchaseHistoryResponse(_request, _result); } diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Handler/GetStoreAvailabilityHandler.cs b/src/Servers/WebServer/src/Module/Direct2Game/Handler/GetStoreAvailabilityHandler.cs index cb7678ce9..25a9a484d 100644 --- a/src/Servers/WebServer/src/Module/Direct2Game/Handler/GetStoreAvailabilityHandler.cs +++ b/src/Servers/WebServer/src/Module/Direct2Game/Handler/GetStoreAvailabilityHandler.cs @@ -5,31 +5,17 @@ namespace UniSpy.Server.WebServer.Module.Direct2Game.Handler { - public class GetStoreAvailabilityHandler : CmdHandlerBase { protected new GetStoreAvailabilityRequest _request => (GetStoreAvailabilityRequest)base._request; protected new GetStoreAvailabilityResult _result = new GetStoreAvailabilityResult(); public GetStoreAvailabilityHandler(Client client, GetStoreAvailabilityRequest request) : base(client, request) { - - } - - protected override void DataOperation() - { - _result.Status = 0; - _result.StoreResult = 10; - /* - * 10 -> Store is available - * 50 -> ERROR - * 100 -> Cannot download DLC information from backend server - */ } protected override void ResponseConstruct() { _response = new GetStoreAvailabilityResponse(_request, _result); } - } } \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs b/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs index 8425a9ddf..0a35b086d 100644 --- a/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs +++ b/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs @@ -11,7 +11,6 @@ internal class SearchForRecordsHandler : CmdHandlerBase protected new SearchForRecordsRequest _request => (SearchForRecordsRequest)base._request; public SearchForRecordsHandler(Client client, SearchForRecordsRequest request) : base(client, request) { - } protected override void ResponseConstruct() diff --git a/src/Servers/WebServer/test/Auth/RawRequests.cs b/src/Servers/WebServer/test/Auth/RawRequests.cs index 80463d8fe..1c916a1f9 100644 --- a/src/Servers/WebServer/test/Auth/RawRequests.cs +++ b/src/Servers/WebServer/test/Auth/RawRequests.cs @@ -8,7 +8,7 @@ public class RawRequests xmlns:SOAP-ENC=""http://schemas.xmlsoap.org/soap/encoding/"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" - xmlns:ns1=""http://gamespy.net/sake""> + xmlns:ns1=""http://gamespy.net/AuthService""> 1 @@ -31,7 +31,7 @@ public class RawRequests xmlns:SOAP-ENC=""http://schemas.xmlsoap.org/soap/encoding/"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" - xmlns:ns1=""http://gamespy.net/sake""> + xmlns:ns1=""http://gamespy.net/AuthService""> 0 @@ -50,7 +50,7 @@ public class RawRequests xmlns:SOAP-ENC=""http://schemas.xmlsoap.org/soap/encoding/"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" - xmlns:ns1=""http://gamespy.net/sake""> + xmlns:ns1=""http://gamespy.net/AuthService""> 1 @@ -69,7 +69,7 @@ public class RawRequests xmlns:SOAP-ENC=""http://schemas.xmlsoap.org/soap/encoding/"" xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xmlns:xsd=""http://www.w3.org/2001/XMLSchema"" - xmlns:ns1=""http://gamespy.net/sake""> + xmlns:ns1=""http://gamespy.net/AuthService""> 1 From 5e00d066ad834009f6626d88a1fb0e510d5c14c3 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 16 Aug 2023 21:48:24 +0800 Subject: [PATCH 035/231] refactor: update ef core --- common/EFScript.txt | 2 + .../src/Database/DatabaseModel/Addrequest.cs | 2 +- .../src/Database/DatabaseModel/Blocked.cs | 2 +- .../Core/src/Database/DatabaseModel/Friend.cs | 2 +- .../src/Database/DatabaseModel/Profile.cs | 4 +- .../src/Database/DatabaseModel/Pstorage.cs | 42 +++++---- .../src/Database/DatabaseModel/Sakestorage.cs | 32 ++++--- .../src/Database/DatabaseModel/Subprofile.cs | 10 +- .../Database/DatabaseModel/UnispyContext.cs | 46 ++++++---- .../Core/src/Database/DatabaseModel/User.cs | 4 +- .../Chat/src/Application/StorageOperation.cs | 16 ++-- .../src/Application/StorageOperation.cs | 16 ++-- .../src/Application/ClientInfo.cs | 6 +- .../src/Application/ClientManager.cs | 6 +- .../src/Application/StorageOperation.cs | 58 ++++++------ .../Response/General/NewUserResponse.cs | 2 +- .../CmdHandler/Buddy/BlockListHandler.cs | 4 +- .../CmdHandler/Buddy/BuddyListHandler.cs | 6 +- .../CmdHandler/Buddy/DelBuddyHandler.cs | 4 +- .../CmdHandler/General/LoginHandler.cs | 8 +- .../CmdHandler/Profile/AddBlockHandler.cs | 6 +- .../CmdHandler/Profile/GetProfileHandler.cs | 2 +- .../CmdHandler/Profile/NewProfileHandler.cs | 8 +- .../CmdHandler/Profile/RegisterNickHandler.cs | 2 +- .../src/Application/StorageOperation.cs | 92 +++++++++---------- .../src/Contract/Response/NewUserResponse.cs | 2 +- .../src/Handler/CmdHandler/NewUserHandler.cs | 14 +-- .../Auth/Handler/LoginProfileHandler.cs | 12 +-- .../Handler/LoginProfileWithGameIdHandler.cs | 12 +-- .../LoginRemoteAuthWithGameIdHandler.cs | 10 +- .../Auth/Handler/LoginUniqueNickHandler.cs | 10 +- .../LoginUniqueNickWithGameIdHandler.cs | 10 +- .../Response/SearchForRecordResponse.cs | 50 ++++++++-- .../Contract/Result/SearchForRecordsResult.cs | 10 ++ .../Sake/Handler/SearchForRecordsHandler.cs | 13 ++- src/Servers/WebServer/test/MokeObject.cs | 2 +- src/Servers/WebServer/test/Sake/GameTest.cs | 48 ++++++++++ 37 files changed, 351 insertions(+), 224 deletions(-) create mode 100644 common/EFScript.txt create mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/Result/SearchForRecordsResult.cs create mode 100644 src/Servers/WebServer/test/Sake/GameTest.cs diff --git a/common/EFScript.txt b/common/EFScript.txt new file mode 100644 index 000000000..567598932 --- /dev/null +++ b/common/EFScript.txt @@ -0,0 +1,2 @@ + +dotnet ef dbcontext scaffold "ConnectionString" Npgsql.EntityFrameworkCore.PostgreSQL -o Models \ No newline at end of file diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Addrequest.cs b/src/Libraries/Core/src/Database/DatabaseModel/Addrequest.cs index e7f60066c..3c14bff67 100644 --- a/src/Libraries/Core/src/Database/DatabaseModel/Addrequest.cs +++ b/src/Libraries/Core/src/Database/DatabaseModel/Addrequest.cs @@ -6,7 +6,7 @@ namespace UniSpy.Server.Core.Database.DatabaseModel public partial class Addrequest { public int Addrequestid { get; set; } - public int ProfileId { get; set; } + public int Profileid { get; set; } public int Namespaceid { get; set; } public int Targetid { get; set; } public string Reason { get; set; } = null!; diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Blocked.cs b/src/Libraries/Core/src/Database/DatabaseModel/Blocked.cs index 82936e98d..0305931d1 100644 --- a/src/Libraries/Core/src/Database/DatabaseModel/Blocked.cs +++ b/src/Libraries/Core/src/Database/DatabaseModel/Blocked.cs @@ -6,7 +6,7 @@ namespace UniSpy.Server.Core.Database.DatabaseModel public partial class Blocked { public int Blockid { get; set; } - public int ProfileId { get; set; } + public int Profileid { get; set; } public int Namespaceid { get; set; } public int Targetid { get; set; } diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Friend.cs b/src/Libraries/Core/src/Database/DatabaseModel/Friend.cs index 325081a31..bb29c1015 100644 --- a/src/Libraries/Core/src/Database/DatabaseModel/Friend.cs +++ b/src/Libraries/Core/src/Database/DatabaseModel/Friend.cs @@ -6,7 +6,7 @@ namespace UniSpy.Server.Core.Database.DatabaseModel public partial class Friend { public int Friendid { get; set; } - public int ProfileId { get; set; } + public int Profileid { get; set; } public int Namespaceid { get; set; } public int Targetid { get; set; } diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Profile.cs b/src/Libraries/Core/src/Database/DatabaseModel/Profile.cs index 3aa6a0ef3..b1f97bc44 100644 --- a/src/Libraries/Core/src/Database/DatabaseModel/Profile.cs +++ b/src/Libraries/Core/src/Database/DatabaseModel/Profile.cs @@ -13,10 +13,11 @@ public Profile() Blockeds = new HashSet(); Friends = new HashSet(); Pstorages = new HashSet(); + Sakestorages = new HashSet(); Subprofiles = new HashSet(); } - public int ProfileId { get; set; } + public int Profileid { get; set; } public int Userid { get; set; } public string Nick { get; set; } = null!; public int Serverflag { get; set; } @@ -65,6 +66,7 @@ public Profile() public virtual ICollection Blockeds { get; set; } public virtual ICollection Friends { get; set; } public virtual ICollection Pstorages { get; set; } + public virtual ICollection Sakestorages { get; set; } public virtual ICollection Subprofiles { get; set; } } } diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Pstorage.cs b/src/Libraries/Core/src/Database/DatabaseModel/Pstorage.cs index b7ea8452b..5e077a99b 100644 --- a/src/Libraries/Core/src/Database/DatabaseModel/Pstorage.cs +++ b/src/Libraries/Core/src/Database/DatabaseModel/Pstorage.cs @@ -1,20 +1,22 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; - -namespace UniSpy.Server.Core.Database.DatabaseModel -{ - /// - /// Old games use pstorage to store game data. - /// - public partial class Pstorage - { - public int Pstorageid { get; set; } - public int ProfileId { get; set; } - public int Ptype { get; set; } - public int Dindex { get; set; } - [Column(TypeName = "jsonb")] - public Dictionary Data { get; set; } - // public string Data { get; set; } - public virtual Profile Profile { get; set; } = null!; - } -} +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; + +namespace UniSpy.Server.Core.Database.DatabaseModel; + +/// +/// Old games use pstorage to store game data. +/// +public partial class Pstorage +{ + public int Pstorageid { get; set; } + + public int Profileid { get; set; } + + public int Ptype { get; set; } + + public int Dindex { get; set; } + [Column(TypeName = "jsonb")] + public Dictionary Data { get; set; } + + public virtual Profile Profile { get; set; } = null!; +} diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Sakestorage.cs b/src/Libraries/Core/src/Database/DatabaseModel/Sakestorage.cs index 85d2001f7..6d2b9a60d 100644 --- a/src/Libraries/Core/src/Database/DatabaseModel/Sakestorage.cs +++ b/src/Libraries/Core/src/Database/DatabaseModel/Sakestorage.cs @@ -1,11 +1,21 @@ -namespace UniSpy.Server.Core.Database.DatabaseModel -{ - /// - /// Sake storage system. - /// - public partial class Sakestorage - { - public int Sakestorageid { get; set; } - public string Tableid { get; set; } = null!; - } -} +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations.Schema; + +namespace UniSpy.Server.Core.Database.DatabaseModel +{ + /// + /// Sake storage system. + /// + public partial class Sakestorage + { + public int Sakestorageid { get; set; } + public string Tableid { get; set; } = null!; + public int Profileid { get; set; } + [Column(TypeName = "jsonb")] + public Dictionary Userdata { get; set; } = null!; + + public virtual Profile Profile { get; set; } = null!; + } +} + + diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Subprofile.cs b/src/Libraries/Core/src/Database/DatabaseModel/Subprofile.cs index c84785e0d..dd7d3d413 100644 --- a/src/Libraries/Core/src/Database/DatabaseModel/Subprofile.cs +++ b/src/Libraries/Core/src/Database/DatabaseModel/Subprofile.cs @@ -5,12 +5,12 @@ namespace UniSpy.Server.Core.Database.DatabaseModel /// public partial class Subprofile { - public int SubProfileId { get; set; } - public int ProfileId { get; set; } + public int Subprofileid { get; set; } + public int Profileid { get; set; } public string Uniquenick { get; set; } - public int NamespaceId { get; set; } - public int PartnerId { get; set; } - public int? ProductId { get; set; } + public int Namespaceid { get; set; } + public int Partnerid { get; set; } + public int? Productid { get; set; } public string Gamename { get; set; } public string Cdkeyenc { get; set; } public short? Firewall { get; set; } diff --git a/src/Libraries/Core/src/Database/DatabaseModel/UnispyContext.cs b/src/Libraries/Core/src/Database/DatabaseModel/UnispyContext.cs index 9e511e471..d0c5f1448 100644 --- a/src/Libraries/Core/src/Database/DatabaseModel/UnispyContext.cs +++ b/src/Libraries/Core/src/Database/DatabaseModel/UnispyContext.cs @@ -49,7 +49,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.Namespaceid).HasColumnName("namespaceid"); - entity.Property(e => e.ProfileId).HasColumnName("profileid"); + entity.Property(e => e.Profileid).HasColumnName("profileid"); entity.Property(e => e.Reason) .HasColumnType("character varying") @@ -63,7 +63,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.HasOne(d => d.Profile) .WithMany(p => p.Addrequests) - .HasForeignKey(d => d.ProfileId) + .HasForeignKey(d => d.Profileid) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("addrequests_fk"); }); @@ -83,13 +83,13 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.Namespaceid).HasColumnName("namespaceid"); - entity.Property(e => e.ProfileId).HasColumnName("profileid"); + entity.Property(e => e.Profileid).HasColumnName("profileid"); entity.Property(e => e.Targetid).HasColumnName("targetid"); entity.HasOne(d => d.Profile) .WithMany(p => p.Blockeds) - .HasForeignKey(d => d.ProfileId) + .HasForeignKey(d => d.Profileid) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("blocked_fk"); }); @@ -106,13 +106,13 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.Namespaceid).HasColumnName("namespaceid"); - entity.Property(e => e.ProfileId).HasColumnName("profileid"); + entity.Property(e => e.Profileid).HasColumnName("profileid"); entity.Property(e => e.Targetid).HasColumnName("targetid"); entity.HasOne(d => d.Profile) .WithMany(p => p.Friends) - .HasForeignKey(d => d.ProfileId) + .HasForeignKey(d => d.Profileid) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("friends_fk"); }); @@ -215,7 +215,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.HasComment("User profiles."); - entity.Property(e => e.ProfileId) + entity.Property(e => e.Profileid) .HasColumnName("profileid") .HasDefaultValueSql("nextval('profiles_profileid_seq'::regclass)"); @@ -399,13 +399,13 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.Dindex).HasColumnName("dindex"); - entity.Property(e => e.ProfileId).HasColumnName("profileid"); + entity.Property(e => e.Profileid).HasColumnName("profileid"); entity.Property(e => e.Ptype).HasColumnName("ptype"); entity.HasOne(d => d.Profile) .WithMany(p => p.Pstorages) - .HasForeignKey(d => d.ProfileId) + .HasForeignKey(d => d.Profileid) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("pstorage_fk"); }); @@ -420,9 +420,21 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnName("sakestorageid") .HasDefaultValueSql("nextval('sakestorage_sakestorageid_seq'::regclass)"); + entity.Property(e => e.Profileid).HasColumnName("profileid"); + entity.Property(e => e.Tableid) .HasColumnType("character varying") .HasColumnName("tableid"); + + entity.Property(e => e.Userdata) + .HasColumnType("jsonb") + .HasColumnName("userdata"); + + entity.HasOne(d => d.Profile) + .WithMany(p => p.Sakestorages) + .HasForeignKey(d => d.Profileid) + .OnDelete(DeleteBehavior.ClientSetNull) + .HasConstraintName("profileid_fk"); }); modelBuilder.Entity(entity => @@ -431,7 +443,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.HasComment("User subprofiles."); - entity.Property(e => e.SubProfileId) + entity.Property(e => e.Subprofileid) .HasColumnName("subprofileid") .HasDefaultValueSql("nextval('subprofiles_subprofileid_seq'::regclass)"); @@ -449,17 +461,17 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.Property(e => e.Gamename).HasColumnName("gamename"); - entity.Property(e => e.NamespaceId).HasColumnName("namespaceid"); + entity.Property(e => e.Namespaceid).HasColumnName("namespaceid"); - entity.Property(e => e.PartnerId).HasColumnName("partnerid"); + entity.Property(e => e.Partnerid).HasColumnName("partnerid"); entity.Property(e => e.Port) .HasColumnName("port") .HasDefaultValueSql("0"); - entity.Property(e => e.ProductId).HasColumnName("productid"); + entity.Property(e => e.Productid).HasColumnName("productid"); - entity.Property(e => e.ProfileId).HasColumnName("profileid"); + entity.Property(e => e.Profileid).HasColumnName("profileid"); entity.Property(e => e.Uniquenick) .HasColumnType("character varying") @@ -467,7 +479,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.HasOne(d => d.Profile) .WithMany(p => p.Subprofiles) - .HasForeignKey(d => d.ProfileId) + .HasForeignKey(d => d.Profileid) .OnDelete(DeleteBehavior.ClientSetNull) .HasConstraintName("subprofiles_fk"); }); @@ -478,7 +490,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) entity.HasComment("User account information."); - entity.Property(e => e.UserId) + entity.Property(e => e.Userid) .HasColumnName("userid") .HasDefaultValueSql("nextval('users_userid_seq'::regclass)"); @@ -500,7 +512,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) .HasColumnName("emailverified") .HasDefaultValueSql("true"); - entity.Property(e => e.LastIp).HasColumnName("lastip"); + entity.Property(e => e.Lastip).HasColumnName("lastip"); entity.Property(e => e.Lastonline) .HasColumnType("timestamp without time zone") diff --git a/src/Libraries/Core/src/Database/DatabaseModel/User.cs b/src/Libraries/Core/src/Database/DatabaseModel/User.cs index fe9bb1645..ef47b81ef 100644 --- a/src/Libraries/Core/src/Database/DatabaseModel/User.cs +++ b/src/Libraries/Core/src/Database/DatabaseModel/User.cs @@ -14,11 +14,11 @@ public User() Profiles = new HashSet(); } - public int UserId { get; set; } + public int Userid { get; set; } public string Email { get; set; } = null!; public string Password { get; set; } = null!; public bool? Emailverified { get; set; } - public IPAddress LastIp { get; set; } + public IPAddress Lastip { get; set; } public DateTime Lastonline { get; set; } public DateTime Createddate { get; set; } public bool Banned { get; set; } diff --git a/src/Servers/Chat/src/Application/StorageOperation.cs b/src/Servers/Chat/src/Application/StorageOperation.cs index 846104592..ef3b92893 100644 --- a/src/Servers/Chat/src/Application/StorageOperation.cs +++ b/src/Servers/Chat/src/Application/StorageOperation.cs @@ -14,14 +14,14 @@ public sealed class StorageOperation : IStorageOperation using (var db = new UniSpyContext()) { var result = from u in db.Users - join p in db.Profiles on u.UserId equals p.Userid + join p in db.Profiles on u.Userid equals p.Userid where u.Email == email && p.Nick == nickName && u.Password == passwordHash select new { - userId = u.UserId, - profileId = p.ProfileId, + userId = u.Userid, + profileId = p.Profileid, emailVerified = u.Emailverified, banned = u.Banned }; @@ -41,14 +41,14 @@ join p in db.Profiles on u.UserId equals p.Userid using (var db = new UniSpyContext()) { var result = from n in db.Subprofiles - join p in db.Profiles on n.ProfileId equals p.ProfileId - join u in db.Users on p.Userid equals u.UserId + join p in db.Profiles on n.Profileid equals p.Profileid + join u in db.Users on p.Userid equals u.Userid where n.Uniquenick == uniqueNick - && n.NamespaceId == namespaceId + && n.Namespaceid == namespaceId select new { - userId = u.UserId, - profileId = p.ProfileId, + userId = u.Userid, + profileId = p.Profileid, uniquenick = n.Uniquenick, emailVerified = u.Emailverified, banned = u.Banned diff --git a/src/Servers/GameStatus/src/Application/StorageOperation.cs b/src/Servers/GameStatus/src/Application/StorageOperation.cs index 684834bc6..18e503b8e 100644 --- a/src/Servers/GameStatus/src/Application/StorageOperation.cs +++ b/src/Servers/GameStatus/src/Application/StorageOperation.cs @@ -25,7 +25,7 @@ public void UpdatePlayerData(int profileId, PersistStorageType storageType, int { var result = from p in db.Pstorages - where p.ProfileId == profileId + where p.Profileid == profileId && p.Dindex == dataIndex && p.Ptype == (int)storageType select p; @@ -36,7 +36,7 @@ public void UpdatePlayerData(int profileId, PersistStorageType storageType, int //insert a new record in database ps = new Pstorage(); ps.Dindex = dataIndex; - ps.ProfileId = profileId; + ps.Profileid = profileId; ps.Ptype = (int)storageType; ps.Data = data; db.Pstorages.Add(ps); @@ -58,7 +58,7 @@ public int GetProfileId(string token) { var result = from s in db.Subprofiles where s.Authtoken == token - select s.ProfileId; + select s.Profileid; if (result.Count() != 1) { throw new GameStatus.Exception("No records found in database by authtoken."); @@ -72,9 +72,9 @@ public int GetProfileId(string cdKey, string nickName) using (var db = new UniSpyContext()) { var result = from s in db.Subprofiles - join p in db.Profiles on s.ProfileId equals p.ProfileId + join p in db.Profiles on s.Profileid equals p.Profileid where s.Cdkeyenc == cdKey && p.Nick == nickName - select s.ProfileId; + select s.Profileid; if (result.Count() != 1) { throw new GameStatus.Exception("No records found in database by cdkey hash."); @@ -88,8 +88,8 @@ public int GetProfileId(int profileId) using (var db = new UniSpyContext()) { var result = from p in db.Profiles - where p.ProfileId == profileId - select p.ProfileId; + where p.Profileid == profileId + select p.Profileid; if (result.Count() != 1) { throw new GameStatus.Exception("No records found in database by profileid."); @@ -105,7 +105,7 @@ public Dictionary GetPlayerData(int profileId, PersistStorageTyp var result = from ps in db.Pstorages where ps.Ptype == (int)storageType && ps.Dindex == dataIndex - && ps.ProfileId == profileId + && ps.Profileid == profileId select ps.Data; if (result.Count() != 1) diff --git a/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs b/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs index db2132173..686025c0a 100755 --- a/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs +++ b/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs @@ -41,7 +41,7 @@ public User UserInfo { using (var db = new UniSpyContext()) { - _userInfo = db.Users.Where(s => s.UserId == UserId).First(); + _userInfo = db.Users.Where(s => s.Userid == UserId).First(); } } return _userInfo; @@ -60,7 +60,7 @@ public Profile ProfileInfo { using (var db = new UniSpyContext()) { - _profileInfo = db.Profiles.Where(s => s.ProfileId == ProfileId).First(); + _profileInfo = db.Profiles.Where(s => s.Profileid == ProfileId).First(); } } return _profileInfo; @@ -79,7 +79,7 @@ public Subprofile SubProfileInfo { using (var db = new UniSpyContext()) { - _subProfileInfo = db.Subprofiles.Where(s => s.SubProfileId == SubProfileId).First(); + _subProfileInfo = db.Subprofiles.Where(s => s.Subprofileid == SubProfileId).First(); } } return _subProfileInfo; diff --git a/src/Servers/PresenceConnectionManager/src/Application/ClientManager.cs b/src/Servers/PresenceConnectionManager/src/Application/ClientManager.cs index 13d33e6cc..55bc225f9 100644 --- a/src/Servers/PresenceConnectionManager/src/Application/ClientManager.cs +++ b/src/Servers/PresenceConnectionManager/src/Application/ClientManager.cs @@ -9,9 +9,9 @@ public class ClientManager : ClientManagerBase public static Client GetClient(int profileid, int? productid = null, int? namespaceId = null) { return (Client)ClientPool.Values.FirstOrDefault( - c => ((ClientInfo)c.Info).SubProfileInfo.ProductId == productid - && ((ClientInfo)c.Info).SubProfileInfo.ProfileId == profileid - && ((ClientInfo)c.Info).SubProfileInfo.NamespaceId == namespaceId); + c => ((ClientInfo)c.Info).SubProfileInfo.Productid == productid + && ((ClientInfo)c.Info).SubProfileInfo.Profileid == profileid + && ((ClientInfo)c.Info).SubProfileInfo.Namespaceid == namespaceId); } } } \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Application/StorageOperation.cs b/src/Servers/PresenceConnectionManager/src/Application/StorageOperation.cs index e15494957..ca322f607 100644 --- a/src/Servers/PresenceConnectionManager/src/Application/StorageOperation.cs +++ b/src/Servers/PresenceConnectionManager/src/Application/StorageOperation.cs @@ -19,7 +19,7 @@ public bool IsEmailExist(string email) var result = from u in db.Users //According to FSW partnerid is not nessesary where u.Email == email - select u.UserId; + select u.Userid; if (result.Count() == 0) { @@ -32,7 +32,7 @@ public void DeleteFriendByProfileId(int profileId, int targetId, int namespaceId { using (var db = new UniSpyContext()) { - var result = db.Friends.Where(f => f.ProfileId == profileId + var result = db.Friends.Where(f => f.Profileid == profileId && f.Targetid == targetId && f.Namespaceid == namespaceId).FirstOrDefault(); if (result is not null) @@ -50,7 +50,7 @@ public List GetBlockedProfileIds(int profileId, int namespaceId) { using (var db = new UniSpyContext()) { - return db.Blockeds.Where(f => f.ProfileId == profileId + return db.Blockeds.Where(f => f.Profileid == profileId && f.Namespaceid == namespaceId) .Select(f => f.Targetid).ToList(); } @@ -60,7 +60,7 @@ public List GetFriendProfileIds(int profileId, int namespaceId) { using (var db = new UniSpyContext()) { - return db.Friends.Where(f => f.ProfileId == profileId + return db.Friends.Where(f => f.Profileid == profileId && f.Namespaceid == namespaceId).Select(f => f.Targetid).ToList(); } } @@ -71,14 +71,14 @@ public GetProfileDataModel GetProfileInfos(int profileId, int namespaceId) { //we have to make sure the search target has the same namespaceID var result = from p in db.Profiles - join s in db.Subprofiles on p.ProfileId equals s.ProfileId - join u in db.Users on p.Userid equals u.UserId - where p.ProfileId == profileId - && s.NamespaceId == namespaceId + join s in db.Subprofiles on p.Profileid equals s.Profileid + join u in db.Users on p.Userid equals u.Userid + where p.Profileid == profileId + && s.Namespaceid == namespaceId select new GetProfileDataModel { Nick = p.Nick, - ProfileId = p.ProfileId, + ProfileId = p.Profileid, UniqueNick = s.Uniquenick, Email = u.Email, Firstname = p.Firstname, @@ -116,8 +116,8 @@ join u in db.Users on p.Userid equals u.UserId using (var db = new UniSpyContext()) { var result = from u in db.Users - join p in db.Profiles on u.UserId equals p.Userid - join n in db.Subprofiles on p.ProfileId equals n.ProfileId + join p in db.Profiles on u.Userid equals p.Userid + join n in db.Subprofiles on p.Profileid equals n.Profileid where u.Email == email && p.Nick == nickName select new { u, p, n }; @@ -127,7 +127,7 @@ join n in db.Subprofiles on p.ProfileId equals n.ProfileId { throw new GPLoginBadProfileException("email and nick dose not exist"); } - return (info.u.UserId, info.p.ProfileId, info.n.SubProfileId); + return (info.u.Userid, info.p.Profileid, info.n.Subprofileid); } } @@ -137,10 +137,10 @@ join n in db.Subprofiles on p.ProfileId equals n.ProfileId using (var db = new UniSpyContext()) { var result = from n in db.Subprofiles - join p in db.Profiles on n.ProfileId equals p.ProfileId - join u in db.Users on p.Userid equals u.UserId + join p in db.Profiles on n.Profileid equals p.Profileid + join u in db.Users on p.Userid equals u.Userid where n.Uniquenick == uniqueNick - && n.NamespaceId == namespaceId + && n.Namespaceid == namespaceId select new { u, p, n }; var info = result.FirstOrDefault(); @@ -148,7 +148,7 @@ join u in db.Users on p.Userid equals u.UserId { throw new GPLoginBadUniquenickException($"The uniquenick: {uniqueNick} is invalid."); } - return (info.u.UserId, info.p.ProfileId, info.n.SubProfileId); + return (info.u.Userid, info.p.Profileid, info.n.Subprofileid); } } @@ -157,11 +157,11 @@ join u in db.Users on p.Userid equals u.UserId using (var db = new UniSpyContext()) { var result = from u in db.Users - join p in db.Profiles on u.UserId equals p.Userid - join n in db.Subprofiles on p.ProfileId equals n.ProfileId + join p in db.Profiles on u.Userid equals p.Userid + join n in db.Subprofiles on p.Profileid equals n.Profileid where n.Authtoken == authToken - && n.PartnerId == partnerId - && n.NamespaceId == namespaceId + && n.Partnerid == partnerId + && n.Namespaceid == namespaceId select new { u, p, n }; var info = result.FirstOrDefault(); @@ -169,7 +169,7 @@ join n in db.Subprofiles on p.ProfileId equals n.ProfileId { throw new GPLoginBadPreAuthException("The pre-authentication token is invalid."); } - return (info.u.UserId, info.p.ProfileId, info.n.SubProfileId); + return (info.u.Userid, info.p.Profileid, info.n.Subprofileid); } } @@ -179,11 +179,11 @@ public void UpdateBlockInfo(int targetId, int profileId, int namespaceId) { if (db.Blockeds.Where(b => b.Targetid == targetId && b.Namespaceid == namespaceId - && b.ProfileId == profileId).Count() == 0) + && b.Profileid == profileId).Count() == 0) { Blocked blocked = new Blocked { - ProfileId = profileId, + Profileid = profileId, Targetid = targetId, Namespaceid = namespaceId }; @@ -198,11 +198,11 @@ public void UpdateFriendInfo(int targetId, int profileId, int namespaceId) { if (db.Friends.Where(b => b.Targetid == targetId && b.Namespaceid == namespaceId - && b.ProfileId == profileId).Count() == 0) + && b.Profileid == profileId).Count() == 0) { Friend friend = new Friend { - ProfileId = profileId, + Profileid = profileId, Targetid = targetId, Namespaceid = namespaceId }; @@ -217,7 +217,7 @@ public void AddNickName(int userId, int profileId, string newNick) var profiles = new UniSpy.Server.Core.Database.DatabaseModel.Profile { - ProfileId = profileId, + Profileid = profileId, Nick = newNick, Userid = userId }; @@ -231,7 +231,7 @@ public void UpdateNickName(int profileId, string oldNick, string newNick) using (var db = new UniSpyContext()) { var result = from p in db.Profiles - where p.ProfileId == profileId + where p.Profileid == profileId && p.Nick == oldNick select p; @@ -244,7 +244,7 @@ public void UpdateNickName(int profileId, string oldNick, string newNick) result.First().Nick = newNick; } - var profile = db.Profiles.Where(p => p.ProfileId == profileId + var profile = db.Profiles.Where(p => p.Profileid == profileId && p.Nick == oldNick).First(); profile.Nick = newNick; db.Profiles.Add(profile); @@ -265,7 +265,7 @@ public void UpdateUniqueNick(int subProfileId, string uniqueNick) { using (var db = new UniSpyContext()) { - var result = db.Subprofiles.FirstOrDefault(s => s.SubProfileId == subProfileId); + var result = db.Subprofiles.FirstOrDefault(s => s.Subprofileid == subProfileId); result.Uniquenick = uniqueNick; db.Subprofiles.Update(result); db.SaveChanges(); diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Response/General/NewUserResponse.cs b/src/Servers/PresenceConnectionManager/src/Contract/Response/General/NewUserResponse.cs index 88b35106d..16934744f 100755 --- a/src/Servers/PresenceConnectionManager/src/Contract/Response/General/NewUserResponse.cs +++ b/src/Servers/PresenceConnectionManager/src/Contract/Response/General/NewUserResponse.cs @@ -11,7 +11,7 @@ public NewUserResponse(NewUserRequest request, NewUserResult result) : base(requ public override void Build() { - SendingBuffer = $@"\nur\\userid\{_result.User.UserId}\profileid\{_result.SubProfile.ProfileId}\id\{_request.OperationID}\final\"; + SendingBuffer = $@"\nur\\userid\{_result.User.Userid}\profileid\{_result.SubProfile.Profileid}\id\{_request.OperationID}\final\"; } } } \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BlockListHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BlockListHandler.cs index 299856217..7f772baec 100755 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BlockListHandler.cs +++ b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BlockListHandler.cs @@ -18,8 +18,8 @@ protected override void RequestCheck() { } protected override void DataOperation() { - _result.ProfileIdList = StorageOperation.Persistance.GetBlockedProfileIds(_client.Info.ProfileInfo.ProfileId, - _client.Info.SubProfileInfo.NamespaceId); + _result.ProfileIdList = StorageOperation.Persistance.GetBlockedProfileIds(_client.Info.ProfileInfo.Profileid, + _client.Info.SubProfileInfo.Namespaceid); } protected override void ResponseConstruct() diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BuddyListHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BuddyListHandler.cs index bbc219db6..2cf3d87dc 100755 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BuddyListHandler.cs +++ b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BuddyListHandler.cs @@ -18,8 +18,8 @@ public BuddyListHandler(IClient client) : base(client, null) protected override void RequestCheck() { } protected override void DataOperation() { - var friendsId = StorageOperation.Persistance.GetFriendProfileIds(_client.Info.ProfileInfo.ProfileId, - _client.Info.SubProfileInfo.NamespaceId); + var friendsId = StorageOperation.Persistance.GetFriendProfileIds(_client.Info.ProfileInfo.Profileid, + _client.Info.SubProfileInfo.Namespaceid); _result.ProfileIDList = friendsId; } @@ -40,7 +40,7 @@ protected override void Response() var request = new StatusInfoRequest { ProfileId = profileID, - NamespaceID = (int)_client.Info.SubProfileInfo.NamespaceId, + NamespaceID = (int)_client.Info.SubProfileInfo.Namespaceid, IsGetStatusInfo = true }; new StatusInfoHandler(_client, request).Handle(); diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/DelBuddyHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/DelBuddyHandler.cs index b681705a7..2d528eb7d 100755 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/DelBuddyHandler.cs +++ b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/DelBuddyHandler.cs @@ -18,9 +18,9 @@ public DelBuddyHandler(Client client, DelBuddyRequest request) : base(client, re protected override void DataOperation() { - StorageOperation.Persistance.DeleteFriendByProfileId(_client.Info.ProfileInfo.ProfileId, + StorageOperation.Persistance.DeleteFriendByProfileId(_client.Info.ProfileInfo.Profileid, _request.TargetId, - _client.Info.SubProfileInfo.NamespaceId); + _client.Info.SubProfileInfo.Namespaceid); } } } diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LoginHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LoginHandler.cs index 2b3140527..383a44180 100755 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LoginHandler.cs +++ b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LoginHandler.cs @@ -42,15 +42,15 @@ protected override void DataOperation() _result.DatabaseResults = new LogInDataModel { Email = _client.Info.UserInfo.Email, - UserId = _client.Info.UserInfo.UserId, - ProfileId = _client.Info.ProfileInfo.ProfileId, - SubProfileId = _client.Info.SubProfileInfo.SubProfileId, + UserId = _client.Info.UserInfo.Userid, + ProfileId = _client.Info.ProfileInfo.Profileid, + SubProfileId = _client.Info.SubProfileInfo.Subprofileid, Nick = _client.Info.ProfileInfo.Nick, UniqueNick = _client.Info.SubProfileInfo.Uniquenick, PasswordHash = _client.Info.UserInfo.Password, EmailVerifiedFlag = (bool)_client.Info.UserInfo.Emailverified, BannedFlag = (bool)_client.Info.UserInfo.Banned, - NamespaceId = _client.Info.SubProfileInfo.NamespaceId + NamespaceId = _client.Info.SubProfileInfo.Namespaceid }; _client.Info.Status.CurrentStatus = GPStatusCode.Online; } diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/AddBlockHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/AddBlockHandler.cs index 37e85b721..d7d9476e9 100755 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/AddBlockHandler.cs +++ b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/AddBlockHandler.cs @@ -15,7 +15,7 @@ public AddBlockHandler(Client client, AddBlockRequest request) : base(client, re protected override void RequestCheck() { base.RequestCheck(); - if (_client.Info.ProfileInfo.ProfileId == _request.TargetId) + if (_client.Info.ProfileInfo.Profileid == _request.TargetId) { throw new GPException("You can not block your self."); } @@ -23,8 +23,8 @@ protected override void RequestCheck() protected override void DataOperation() { StorageOperation.Persistance.UpdateBlockInfo(_request.TargetId, - _client.Info.ProfileInfo.ProfileId, - _client.Info.SubProfileInfo.NamespaceId); + _client.Info.ProfileInfo.Profileid, + _client.Info.SubProfileInfo.Namespaceid); } } } diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/GetProfileHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/GetProfileHandler.cs index e4b5c0996..71be02d39 100755 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/GetProfileHandler.cs +++ b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/GetProfileHandler.cs @@ -20,7 +20,7 @@ public GetProfileHandler(Client client, GetProfileRequest request) : base(client } protected override void DataOperation() { - _result.UserProfile = StorageOperation.Persistance.GetProfileInfos(_request.ProfileId, _client.Info.SubProfileInfo.NamespaceId); + _result.UserProfile = StorageOperation.Persistance.GetProfileInfos(_request.ProfileId, _client.Info.SubProfileInfo.Namespaceid); if (_result.UserProfile is null) { diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/NewProfileHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/NewProfileHandler.cs index 801170879..d55be1814 100755 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/NewProfileHandler.cs +++ b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/NewProfileHandler.cs @@ -27,17 +27,17 @@ protected override void DataOperation() { if (_request.IsReplaceNickName) { - StorageOperation.Persistance.UpdateNickName(_client.Info.ProfileInfo.ProfileId, + StorageOperation.Persistance.UpdateNickName(_client.Info.ProfileInfo.Profileid, _request.OldNick, _request.NewNick); } else { - StorageOperation.Persistance.AddNickName(_client.Info.UserInfo.UserId, - _client.Info.ProfileInfo.ProfileId, + StorageOperation.Persistance.AddNickName(_client.Info.UserInfo.Userid, + _client.Info.ProfileInfo.Profileid, _request.NewNick); } - _result.ProfileId = _client.Info.ProfileInfo.ProfileId; + _result.ProfileId = _client.Info.ProfileInfo.Profileid; } protected override void ResponseConstruct() diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/RegisterNickHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/RegisterNickHandler.cs index b78f37137..f56026e19 100755 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/RegisterNickHandler.cs +++ b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/RegisterNickHandler.cs @@ -25,7 +25,7 @@ protected override void DataOperation() { try { - StorageOperation.Persistance.UpdateUniqueNick(_client.Info.SubProfileInfo.SubProfileId, + StorageOperation.Persistance.UpdateUniqueNick(_client.Info.SubProfileInfo.Subprofileid, _request.UniqueNick); } catch (System.Exception e) diff --git a/src/Servers/PresenceSearchPlayer/src/Application/StorageOperation.cs b/src/Servers/PresenceSearchPlayer/src/Application/StorageOperation.cs index 7534cb224..c31376b57 100644 --- a/src/Servers/PresenceSearchPlayer/src/Application/StorageOperation.cs +++ b/src/Servers/PresenceSearchPlayer/src/Application/StorageOperation.cs @@ -29,13 +29,13 @@ public bool VerifyEmailAndPassword(string email, string password) { // Not every game uses PartnerId; optional var result = from p in db.Profiles - join u in db.Users on p.Userid equals u.UserId - join sp in db.Subprofiles on p.ProfileId equals sp.ProfileId + join u in db.Users on p.Userid equals u.Userid + join sp in db.Subprofiles on p.Profileid equals sp.Profileid where u.Email.Equals(email) && u.Password.Equals(password) && p.Nick.Equals(nickName) - || sp.PartnerId.Equals(partnerId) - select p.ProfileId; + || sp.Partnerid.Equals(partnerId) + select p.Profileid; if (result.Count() == 1) { @@ -111,9 +111,9 @@ public Subprofile GetSubProfile(int profileId, int namespaceId, int productId) using (var db = new UniSpyContext()) { return db.Subprofiles.Where(s => - s.ProfileId == profileId && - s.NamespaceId == namespaceId && - s.ProductId == productId).FirstOrDefault(); + s.Profileid == profileId && + s.Namespaceid == namespaceId && + s.Productid == productId).FirstOrDefault(); } } @@ -122,11 +122,11 @@ public List GetAllNickAndUniqueNick(string email, string passwor using (var db = new UniSpyContext()) { var result = from u in db.Users - join p in db.Profiles on u.UserId equals p.Userid - join n in db.Subprofiles on p.ProfileId equals n.ProfileId + join p in db.Profiles on u.Userid equals p.Userid + join n in db.Subprofiles on p.Profileid equals n.Profileid where u.Email == email && u.Password == password - && n.NamespaceId == namespaceId + && n.Namespaceid == namespaceId select new NicksDataModel { NickName = p.Nick, @@ -142,24 +142,24 @@ public List GetFriendsInfo(int profileId, int namespaceId, using (var db = new UniSpyContext()) { var result = from b in db.Friends - where b.ProfileId == profileId && b.Namespaceid == namespaceId + where b.Profileid == profileId && b.Namespaceid == namespaceId select b.Targetid; foreach (var info in result) { var b = from p in db.Profiles - join n in db.Subprofiles on p.ProfileId equals n.ProfileId - join u in db.Users on p.Userid equals u.UserId - where n.NamespaceId == namespaceId - && n.ProfileId == info && n.Gamename == gameName + join n in db.Subprofiles on p.Profileid equals n.Profileid + join u in db.Users on p.Userid equals u.Userid + where n.Namespaceid == namespaceId + && n.Profileid == info && n.Gamename == gameName select new OthersDatabaseModel { - ProfileId = p.ProfileId, + ProfileId = p.Profileid, Nick = p.Nick, Uniquenick = n.Uniquenick, Lastname = p.Lastname, Firstname = p.Firstname, - Userid = u.UserId, + Userid = u.Userid, Email = u.Email }; @@ -177,12 +177,12 @@ public List GetMatchedProfileIdInfos(List profileI foreach (var pid in profileIds) { var result = from n in db.Subprofiles - where n.ProfileId == pid - && n.NamespaceId == namespaceId + where n.Profileid == pid + && n.Namespaceid == namespaceId //select new { uniquenick = n.Uniquenick }; select new OthersListDatabaseModel { - ProfileId = n.ProfileId, + ProfileId = n.Profileid, Uniquenick = n.Uniquenick }; @@ -197,19 +197,19 @@ public List GetMatchedInfosByNick(string nickName) using (var db = new UniSpyContext()) { var result = from p in db.Profiles - join n in db.Subprofiles on p.ProfileId equals n.ProfileId - join u in db.Users on p.Userid equals u.UserId + join n in db.Subprofiles on p.Profileid equals n.Profileid + join u in db.Users on p.Userid equals u.Userid where p.Nick == nickName //&& n.Namespaceid == _request.NamespaceID select new SearchDataBaseModel { - ProfileId = n.ProfileId, + ProfileId = n.Profileid, Nick = p.Nick, Uniquenick = n.Uniquenick, Email = u.Email, Firstname = p.Firstname, Lastname = p.Lastname, - NamespaceID = n.NamespaceId + NamespaceID = n.Namespaceid }; return result.ToList(); } @@ -223,18 +223,18 @@ public List GetMatchedInfosByEmail(string email) { var result = from p in db.Profiles - join n in db.Subprofiles on p.ProfileId equals n.ProfileId - join u in db.Users on p.Userid equals u.UserId + join n in db.Subprofiles on p.Profileid equals n.Profileid + join u in db.Users on p.Userid equals u.Userid where u.Email == email select new SearchDataBaseModel { - ProfileId = n.ProfileId, + ProfileId = n.Profileid, Nick = p.Nick, Uniquenick = n.Uniquenick, Email = u.Email, Firstname = p.Firstname, Lastname = p.Lastname, - NamespaceID = n.NamespaceId + NamespaceID = n.Namespaceid }; return result.ToList(); } @@ -246,21 +246,21 @@ public List GetMatchedInfosByNickAndEmail(string nickName, using (var db = new UniSpyContext()) { var result = from p in db.Profiles - join n in db.Subprofiles on p.ProfileId equals n.ProfileId - join u in db.Users on p.Userid equals u.UserId + join n in db.Subprofiles on p.Profileid equals n.Profileid + join u in db.Users on p.Userid equals u.Userid where p.Nick == nickName && u.Email == email //&& n.Namespaceid == _request.NamespaceID //&& n.Gamename == _request.GameName //&& n.Partnerid == _request.PartnerID select new SearchDataBaseModel { - ProfileId = n.ProfileId, + ProfileId = n.Profileid, Nick = p.Nick, Uniquenick = n.Uniquenick, Email = u.Email, Firstname = p.Firstname, Lastname = p.Lastname, - NamespaceID = n.NamespaceId + NamespaceID = n.Namespaceid }; return result.ToList(); } @@ -272,21 +272,21 @@ public List GetMatchedInfosByUniqueNickAndNamespaceId(strin using (var db = new UniSpyContext()) { var result = from p in db.Profiles - join n in db.Subprofiles on p.ProfileId equals n.ProfileId - join u in db.Users on p.Userid equals u.UserId + join n in db.Subprofiles on p.Profileid equals n.Profileid + join u in db.Users on p.Userid equals u.Userid where n.Uniquenick == uniqueNick - && n.NamespaceId == namespaceId + && n.Namespaceid == namespaceId //&& n.Gamename == _request.GameName //&& n.Partnerid == _request.PartnerID select new SearchDataBaseModel { - ProfileId = n.ProfileId, + ProfileId = n.Profileid, Nick = p.Nick, Uniquenick = n.Uniquenick, Email = u.Email, Firstname = p.Firstname, Lastname = p.Lastname, - NamespaceID = n.NamespaceId + NamespaceID = n.Namespaceid }; return result.ToList(); } @@ -301,19 +301,19 @@ public List GetMatchedInfosByNamespaceId(List na foreach (var nsId in namespaceIds) { var result = from p in db.Profiles - join n in db.Subprofiles on p.ProfileId equals n.ProfileId - join u in db.Users on p.Userid equals u.UserId + join n in db.Subprofiles on p.Profileid equals n.Profileid + join u in db.Users on p.Userid equals u.Userid where n.Uniquenick == uniqueNick - && n.NamespaceId == nsId + && n.Namespaceid == nsId select new SearchUniqueDatabaseModel { - ProfileId = n.ProfileId, + ProfileId = n.Profileid, Nick = p.Nick, Uniquenick = n.Uniquenick, Email = u.Email, Firstname = p.Firstname, Lastname = p.Lastname, - NamespaceID = n.NamespaceId + NamespaceID = n.Namespaceid }; dataList.Add(result.First()); } @@ -326,11 +326,11 @@ public bool IsUniqueNickExist(string uniqueNick, int namespaceId, string gameNam using (var db = new UniSpyContext()) { var result = from p in db.Profiles - join n in db.Subprofiles on p.ProfileId equals n.ProfileId + join n in db.Subprofiles on p.Profileid equals n.Profileid where n.Uniquenick == uniqueNick - && n.NamespaceId == namespaceId + && n.Namespaceid == namespaceId && n.Gamename == gameName - select p.ProfileId; + select p.Profileid; if (result.Count() != 0) { @@ -347,7 +347,7 @@ public bool IsEmailExist(string email) var result = from u in db.Users //According to FSW partnerid is not nessesary where u.Email == email - select u.UserId; + select u.Userid; if (result.Count() == 0) { diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Response/NewUserResponse.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Response/NewUserResponse.cs index 40bebee6f..07e7e0a6d 100755 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Response/NewUserResponse.cs +++ b/src/Servers/PresenceSearchPlayer/src/Contract/Response/NewUserResponse.cs @@ -13,7 +13,7 @@ public NewUserResponse(NewUserRequest request, NewUserResult result) : base(requ } public override void Build() { - SendingBuffer = $@"\nur\\pid\{_result.SubProfile.ProfileId}\final\"; + SendingBuffer = $@"\nur\\pid\{_result.SubProfile.Profileid}\final\"; } } } diff --git a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/NewUserHandler.cs b/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/NewUserHandler.cs index ee4910c2d..f18ecd844 100755 --- a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/NewUserHandler.cs +++ b/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/NewUserHandler.cs @@ -31,12 +31,12 @@ private void UpdateOtherInfo() { if (_request.HasPartnerIDFlag) { - _result.SubProfile.PartnerId = _request.PartnerID; + _result.SubProfile.Partnerid = _request.PartnerID; } if (_request.HasProductIDFlag) { - _result.SubProfile.ProductId = _request.ProductID; + _result.SubProfile.Productid = _request.ProductID; } if (_request.HasGameNameFlag) @@ -90,7 +90,7 @@ private void DatabaseOperationByType() } case NewUserStatus.CheckProfile: - var profile = StorageOperation.Persistance.GetProfile(_result.User.UserId, _request.Nick); + var profile = StorageOperation.Persistance.GetProfile(_result.User.Userid, _request.Nick); if (profile is null) { goto case NewUserStatus.ProfileNotExist; @@ -102,7 +102,7 @@ private void DatabaseOperationByType() } case NewUserStatus.ProfileNotExist: - _result.Profile = new Profile { Userid = _result.User.UserId, Nick = _request.Nick }; + _result.Profile = new Profile { Userid = _result.User.Userid, Nick = _request.Nick }; StorageOperation.Persistance.AddProfile(_result.Profile); goto case NewUserStatus.CheckSubProfile; @@ -110,7 +110,7 @@ private void DatabaseOperationByType() //we do nothing here case NewUserStatus.CheckSubProfile: - var subProfile = StorageOperation.Persistance.GetSubProfile(_result.Profile.ProfileId, _request.NamespaceID, _request.ProductID); + var subProfile = StorageOperation.Persistance.GetSubProfile(_result.Profile.Profileid, _request.NamespaceID, _request.ProductID); if (subProfile is null) { goto case NewUserStatus.SubProfileNotExist; @@ -125,9 +125,9 @@ private void DatabaseOperationByType() //we create subprofile and return _result.SubProfile = new Subprofile { - ProfileId = _result.Profile.ProfileId, + Profileid = _result.Profile.Profileid, Uniquenick = _request.Uniquenick, - NamespaceId = _request.NamespaceID + Namespaceid = _request.NamespaceID }; StorageOperation.Persistance.AddSubProfile(_result.SubProfile); break; diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginProfileHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginProfileHandler.cs index f5d11fdd8..e6bd53c45 100644 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginProfileHandler.cs +++ b/src/Servers/WebServer/src/Module/Auth/Handler/LoginProfileHandler.cs @@ -23,12 +23,12 @@ protected override void DataOperation() using (var db = new UniSpyContext()) { var result = from p in db.Profiles - join u in db.Users on p.Userid equals u.UserId - join sp in db.Subprofiles on p.ProfileId equals sp.ProfileId + join u in db.Users on p.Userid equals u.Userid + join sp in db.Subprofiles on p.Profileid equals sp.Profileid where sp.Uniquenick == _request.Uniquenick && sp.Cdkeyenc == _request.CDKey - && sp.PartnerId == _request.PartnerCode - && sp.NamespaceId == _request.NamespaceId + && sp.Partnerid == _request.PartnerCode + && sp.Namespaceid == _request.NamespaceId && u.Email == _request.Email select new { u, p, sp }; if (result.Count() != 1) @@ -36,8 +36,8 @@ join sp in db.Subprofiles on p.ProfileId equals sp.ProfileId throw new Auth.Exception("No account exists with the provided email address."); } var data = result.First(); - _result.UserId = data.u.UserId; - _result.ProfileId = data.p.ProfileId; + _result.UserId = data.u.Userid; + _result.ProfileId = data.p.Profileid; _result.CdKeyHash = data.sp.Cdkeyenc; // currently we set this to uniquenick _result.ProfileNick = data.sp.Uniquenick; diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginProfileWithGameIdHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginProfileWithGameIdHandler.cs index c5d07aad6..2c851cd61 100644 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginProfileWithGameIdHandler.cs +++ b/src/Servers/WebServer/src/Module/Auth/Handler/LoginProfileWithGameIdHandler.cs @@ -18,12 +18,12 @@ protected override void DataOperation() using (var db = new UniSpyContext()) { var result = from p in db.Profiles - join u in db.Users on p.Userid equals u.UserId - join sp in db.Subprofiles on p.ProfileId equals sp.ProfileId + join u in db.Users on p.Userid equals u.Userid + join sp in db.Subprofiles on p.Profileid equals sp.Profileid where sp.Uniquenick == _request.Uniquenick && sp.Cdkeyenc == _request.CDKey - && sp.PartnerId == _request.PartnerCode - && sp.NamespaceId == _request.NamespaceId + && sp.Partnerid == _request.PartnerCode + && sp.Namespaceid == _request.NamespaceId && u.Email == _request.Email // we do not care about game id now select new { u, p, sp }; @@ -32,8 +32,8 @@ join sp in db.Subprofiles on p.ProfileId equals sp.ProfileId throw new Auth.Exception("No account exists with the provided email address."); } var data = result.First(); - _result.UserId = data.u.UserId; - _result.ProfileId = data.p.ProfileId; + _result.UserId = data.u.Userid; + _result.ProfileId = data.p.Profileid; _result.CdKeyHash = data.sp.Cdkeyenc; // currently we set this to uniquenick _result.ProfileNick = data.sp.Uniquenick; diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginRemoteAuthWithGameIdHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginRemoteAuthWithGameIdHandler.cs index 541228e24..f3dce839a 100644 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginRemoteAuthWithGameIdHandler.cs +++ b/src/Servers/WebServer/src/Module/Auth/Handler/LoginRemoteAuthWithGameIdHandler.cs @@ -18,10 +18,10 @@ protected override void DataOperation() using (var db = new UniSpyContext()) { var result = from p in db.Profiles - join u in db.Users on p.Userid equals u.UserId - join sp in db.Subprofiles on p.ProfileId equals sp.ProfileId + join u in db.Users on p.Userid equals u.Userid + join sp in db.Subprofiles on p.Profileid equals sp.Profileid where sp.Authtoken == _request.AuthToken && - sp.PartnerId == _request.GameId + sp.Partnerid == _request.GameId select new { u, p, sp }; if (result.Count() != 1) { @@ -29,8 +29,8 @@ join sp in db.Subprofiles on p.ProfileId equals sp.ProfileId } var data = result.First(); - _result.UserId = data.u.UserId; - _result.ProfileId = data.p.ProfileId; + _result.UserId = data.u.Userid; + _result.ProfileId = data.p.Profileid; _result.CdKeyHash = data.sp.Cdkeyenc; // currently we set this to uniquenick _result.ProfileNick = data.sp.Uniquenick; diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickHandler.cs index 5d63120c6..08534ba45 100644 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickHandler.cs +++ b/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickHandler.cs @@ -23,10 +23,10 @@ protected override void DataOperation() using (var db = new UniSpyContext()) { var result = from p in db.Profiles - join u in db.Users on p.Userid equals u.UserId - join sp in db.Subprofiles on p.ProfileId equals sp.ProfileId + join u in db.Users on p.Userid equals u.Userid + join sp in db.Subprofiles on p.Profileid equals sp.Profileid where sp.Uniquenick == _request.Uniquenick - && sp.NamespaceId == _request.NamespaceId + && sp.Namespaceid == _request.NamespaceId // && u.Password == _request.Password select new { u, p, sp }; if (result.Count() != 1) @@ -34,8 +34,8 @@ join sp in db.Subprofiles on p.ProfileId equals sp.ProfileId throw new Auth.Exception("No account exists with the provided email address."); } var data = result.First(); - _result.UserId = data.u.UserId; - _result.ProfileId = data.p.ProfileId; + _result.UserId = data.u.Userid; + _result.ProfileId = data.p.Profileid; // _result.CdKeyHash = data.sp.Cdkeyenc; _result.CdKeyHash = "xxxxxxxxxxx"; // currently we set this to uniquenick diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs index c93241e3a..5b7073178 100644 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs +++ b/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs @@ -18,18 +18,18 @@ protected override void DataOperation() using (var db = new UniSpyContext()) { var result = from p in db.Profiles - join u in db.Users on p.Userid equals u.UserId - join sp in db.Subprofiles on p.ProfileId equals sp.ProfileId + join u in db.Users on p.Userid equals u.Userid + join sp in db.Subprofiles on p.Profileid equals sp.Profileid where sp.Uniquenick == _request.Uniquenick && - sp.NamespaceId == _request.NamespaceId + sp.Namespaceid == _request.NamespaceId select new { u, p, sp }; if (result.Count() != 1) { throw new Auth.Exception("No account exists with the provided email address."); } var data = result.First(); - _result.UserId = data.u.UserId; - _result.ProfileId = data.p.ProfileId; + _result.UserId = data.u.Userid; + _result.ProfileId = data.p.Profileid; // _result.CdKeyHash = data.sp.Cdkeyenc; _result.CdKeyHash = "xxxxxxxxxxx"; // currently we set this to uniquenick diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs index 1f49fdabc..85e97f351 100644 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs @@ -1,21 +1,55 @@ -using UniSpy.Server.WebServer.Abstraction; +using UniSpy.Server.WebServer.Module.Sake.Abstraction; +using UniSpy.Server.WebServer.Module.Sake.Contract.Request; +using UniSpy.Server.WebServer.Module.Sake.Contract.Result; namespace UniSpy.Server.WebServer.Module.Sake.Contract.Response { internal class SearchForRecordResponse : ResponseBase { - public SearchForRecordResponse(RequestBase request, ResultBase result) : base(request, result) + private new SearchForRecordsRequest _request => (SearchForRecordsRequest)base._request; + private new SearchForRecordsResult _result => (SearchForRecordsResult)base._result; + public SearchForRecordResponse(SearchForRecordsRequest request, SearchForRecordsResult result) : base(request, result) { } public override void Build() { - SendingBuffer = "Success" + - "" + - "ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg" + - "0" + - "" + - ""; + _content.Add("values"); + _content.Add("ArrayOfRecordValue"); + foreach (var item in _result.UserData) + { + if (item.Key == "RecordValue") + { + _content.Add("RecordValue"); + } + _content.Add(item.Key, item.Value); + } + SendingBuffer = @" + + + + Success + + + + + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + + + + + 0 + + + + + + + "; // base.Build(); } } diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Result/SearchForRecordsResult.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Result/SearchForRecordsResult.cs new file mode 100644 index 000000000..05cd2914d --- /dev/null +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Result/SearchForRecordsResult.cs @@ -0,0 +1,10 @@ +using System.Collections.Generic; +using UniSpy.Server.WebServer.Module.Sake.Abstraction; + +namespace UniSpy.Server.WebServer.Module.Sake.Contract.Result +{ + public class SearchForRecordsResult : ResultBase + { + public Dictionary UserData; + } +} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs b/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs index 0a35b086d..b7f39fabf 100644 --- a/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs +++ b/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs @@ -2,20 +2,27 @@ using UniSpy.Server.WebServer.Module.Sake.Contract.Response; using UniSpy.Server.WebServer.Module.Sake.Contract.Request; using UniSpy.Server.WebServer.Application; +using UniSpy.Server.WebServer.Module.Sake.Contract.Result; namespace UniSpy.Server.WebServer.Module.Sake.Handler { internal class SearchForRecordsHandler : CmdHandlerBase { - protected new SearchForRecordsRequest _request => (SearchForRecordsRequest)base._request; + private new SearchForRecordsRequest _request => (SearchForRecordsRequest)base._request; + private new SearchForRecordsResult _result { get => (SearchForRecordsResult)base._result; set => base._result = value; } public SearchForRecordsHandler(Client client, SearchForRecordsRequest request) : base(client, request) { + _result = new SearchForRecordsResult(); + } + protected override void DataOperation() + { + base.DataOperation(); + // search user data from database } - protected override void ResponseConstruct() { - _response = new SearchForRecordResponse(_request, null); + _response = new SearchForRecordResponse(_request, _result); } } } diff --git a/src/Servers/WebServer/test/MokeObject.cs b/src/Servers/WebServer/test/MokeObject.cs index a0aaed468..59db56916 100644 --- a/src/Servers/WebServer/test/MokeObject.cs +++ b/src/Servers/WebServer/test/MokeObject.cs @@ -7,7 +7,7 @@ namespace UniSpy.Server.WebServer.Test { public static class MokeObject { - public static IClient Client = CreateClient(); + public static Client Client = CreateClient(); public static Client CreateClient(string ipAddress = "192.168.1.2", int port = 9999) { diff --git a/src/Servers/WebServer/test/Sake/GameTest.cs b/src/Servers/WebServer/test/Sake/GameTest.cs new file mode 100644 index 000000000..b640607b7 --- /dev/null +++ b/src/Servers/WebServer/test/Sake/GameTest.cs @@ -0,0 +1,48 @@ +using Moq; +using UniSpy.Server.Core.Abstraction.Interface; +using Xunit; + +namespace UniSpy.Server.WebServer.Test.Sake +{ + public class GameTest + { + [Fact] + public void Crysis2SakeTest() + { + var raw = @" + + + + 3300 + 8TTq4M + 0000000000000000000000__ + DEDICATEDSTATS + PROFILE = 35 + recordid + 0 + 1 + 0 + + 0 + + DATA + recordid + + + + "; + + var req = new Mock(); + req.Setup(r => r.Body).Returns(raw); + + var switcher = new WebServer.Handler.CmdSwitcher(MokeObject.Client, req.Object); + switcher.Handle(); + + } + } +} \ No newline at end of file From 2f465bd9dc920c885535ab1b3e2a567f1dbc3c41 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 19 Aug 2023 18:05:25 +0800 Subject: [PATCH 036/231] refactor(sb): update v1 enc functions --- .../V1/Abstraction/BaseClass/EnctypeBase.cs | 110 ++--- .../V1/Abstraction/Interface/IEnctypeTest.cs | 17 +- .../src/V1/Aggregate/Enctype1.cs | 387 +++++++++--------- .../src/V1/Aggregate/Enctype2.cs | 12 +- .../ServerBrowser/test/V1/EnctypeTest.cs | 38 +- 5 files changed, 297 insertions(+), 267 deletions(-) diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs index e6262a8e9..d004d11d2 100644 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs +++ b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs @@ -15,18 +15,18 @@ public abstract class EnctypeBase : ICryptography, IEnctypeShareTest /// /// The encryption function 2 /// - /// the input tbuff - /// tbuff start index - /// datap is a subset of tbuff - /// datap start index + /// the input tbuff + /// tbuff start index + /// datap is a subset of tbuff + /// datap start index /// unknown - protected void Encshare1(uint[] tbuff, int tbuffIndex, byte[] datap, int datapIndex, int len) + protected void ChangeSboxEncryptPlaintext(uint[] sbox, int sboxIndex, byte[] data, int startIndex, int len) { int pIndex, sIndex; // convert uint array to byte array pIndex = sIndex = 309 * sizeof(uint); - Encshare2(tbuff, 309, 16); - var tbuffBytes = ConvertUintToBytes(tbuff); + SboxElementExchange(sbox, 309, 16); + var sboxBytes = ConvertUintToBytes(sbox); while (len-- > 0) { @@ -34,33 +34,33 @@ protected void Encshare1(uint[] tbuff, int tbuffIndex, byte[] datap, int datapIn { pIndex = sIndex; //convert int array to byte array - Encshare2(tbuff, 0, 16); - tbuffBytes = ConvertUintToBytes(tbuff); + SboxElementExchange(sbox, 0, 16); + sboxBytes = ConvertUintToBytes(sbox); } - datap[datapIndex] ^= tbuffBytes[pIndex]; - datapIndex++; + data[startIndex] ^= sboxBytes[pIndex]; + startIndex++; pIndex++; } - tbuff = ConvertBytesToUint(tbuffBytes); + sbox = ConvertBytesToUint(sboxBytes); } /// /// Encryption related function2 /// - /// the input data - /// the another index of input data + /// the input data + /// the another index of input data /// unknown - protected void Encshare2(uint[] tbuff, uint tbuffp, int len) + protected void SboxElementExchange(uint[] sbox, uint startIndex, int len) { uint t1, t2, t3, t4, t5; uint limit; uint p; - t2 = tbuff[304]; - t1 = tbuff[305]; - t3 = tbuff[306]; - t5 = tbuff[307]; - limit = (uint)(tbuffp + len); - while (tbuffp < limit) + t2 = sbox[304]; + t1 = sbox[305]; + t3 = sbox[306]; + t5 = sbox[307]; + limit = (uint)(startIndex + len); + while (startIndex < limit) { p = t2 + 272; while (t5 <= ushort.MaxValue) @@ -69,41 +69,41 @@ protected void Encshare2(uint[] tbuff, uint tbuffp, int len) p++; t3 += t1; t1 += t3; - tbuff[p - 17] = t1; - tbuff[p - 1] = t3; + sbox[p - 17] = t1; + sbox[p - 1] = t3; t4 = (t3 << 24) | (t3 >> 8); - tbuff[p + 15] = t5; + sbox[p + 15] = t5; t5 <<= 1; t2++; - t1 ^= tbuff[t1 & 0xff]; - t4 ^= tbuff[t4 & 0xff]; + t1 ^= sbox[t1 & 0xff]; + t4 ^= sbox[t4 & 0xff]; t3 = (t4 << 24) | (t4 >> 8); t4 = (t1 >> 24) | (t1 << 8); - t4 ^= tbuff[t4 & 0xff]; - t3 ^= tbuff[t3 & 0xff]; + t4 ^= sbox[t4 & 0xff]; + t3 ^= sbox[t3 & 0xff]; t1 = (t4 >> 24) | (t4 << 8); } t3 ^= t1; - tbuff[tbuffp++] = t3; + sbox[startIndex++] = t3; t2--; - t1 = tbuff[t2 + 256]; - t5 = tbuff[t2 + 272]; + t1 = sbox[t2 + 256]; + t5 = sbox[t2 + 272]; t1 = ~t1; t3 = (t1 << 24) | (t1 >> 8); - t3 ^= tbuff[t3 & 0xff]; - t5 ^= tbuff[t5 & 0xff]; + t3 ^= sbox[t3 & 0xff]; + t5 ^= sbox[t5 & 0xff]; t1 = (t3 << 24) | (t3 >> 8); t4 = (t5 >> 24) | (t5 << 8); - t1 ^= tbuff[t1 & 0xff]; - t4 ^= tbuff[t4 & 0xff]; + t1 ^= sbox[t1 & 0xff]; + t4 ^= sbox[t4 & 0xff]; t3 = (t4 >> 24) | (t4 << 8); - t5 = (tbuff[t2 + 288] << 1) + 1; + t5 = (sbox[t2 + 288] << 1) + 1; } - tbuff[304] = t2; - tbuff[305] = t1; - tbuff[306] = t3; - tbuff[307] = t5; + sbox[304] = t2; + sbox[305] = t1; + sbox[306] = t3; + sbox[307] = t5; } protected void EncShare3(uint[] data, int n1 = 0, int n2 = 0) { @@ -160,41 +160,41 @@ protected void EncShare3(uint[] data, int n1 = 0, int n2 = 0) /// /// Seems this function is used to initialize encryption parameter /// - /// - /// - /// - protected void EncShare4(byte[] src, int size, uint[] dest) + /// the sbox seed + /// seed size + /// sbox + protected void ConstructSbox(byte[] input, int size, uint[] output) { uint tmp; int i; byte pos, x, y; for (i = 0; i < 256; i++) - dest[i] = 0; + output[i] = 0; for (y = 0; y < 4; y++) { for (i = 0; i < 256; i++) { - dest[i] = (dest[i] << 8) + (uint)i; + output[i] = (output[i] << 8) + (uint)i; } for (pos = y, x = 0; x < 2; x++) { for (i = 0; i < 256; i++) { - tmp = dest[i]; - pos += (byte)(tmp + src[i % size]); - dest[i] = dest[pos]; - dest[pos] = tmp; + tmp = output[i]; + pos += (byte)(tmp + input[i % size]); + output[i] = output[pos]; + output[pos] = tmp; } } } for (i = 0; i < 256; i++) - dest[i] ^= (uint)i; + output[i] ^= (uint)i; - EncShare3(dest); + EncShare3(output); } protected static byte[] ConvertUintToBytes(uint[] input) { @@ -221,13 +221,13 @@ protected static uint[] ConvertBytesToUint(byte[] input) return onputInts; } - void IEnctypeShareTest.Encshare1(uint[] tbuff, int tbuffIndex, byte[] datap, int datapIndex, int len) => Encshare1(tbuff, tbuffIndex, datap, datapIndex, len); + void IEnctypeShareTest.Encshare1(uint[] tbuff, int tbuffIndex, byte[] datap, int datapIndex, int len) => ChangeSboxEncryptPlaintext(tbuff, tbuffIndex, datap, datapIndex, len); - void IEnctypeShareTest.Encshare2(uint[] tbuff, uint tbuffp, int len) => Encshare2(tbuff, tbuffp, len); + void IEnctypeShareTest.Encshare2(uint[] tbuff, uint tbuffp, int len) => SboxElementExchange(tbuff, tbuffp, len); void IEnctypeShareTest.EncShare3(uint[] data, int n1, int n2) => EncShare3(data, n1, n2); - void IEnctypeShareTest.EncShare4(byte[] src, int size, uint[] dest) => EncShare4(src, size, dest); + void IEnctypeShareTest.EncShare4(byte[] src, int size, uint[] dest) => ConstructSbox(src, size, dest); uint[] IEnctypeShareTest.ConvertBytesToUint(byte[] input) => ConvertBytesToUint(input); byte[] IEnctypeShareTest.ConvertUintToBytes(uint[] input) => ConvertUintToBytes(input); diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs index d559c28ca..a106ce2ac 100644 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs +++ b/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs @@ -1,14 +1,15 @@ public interface IEnctype1Test : IEnctypeShareTest { - void Func1(byte[] data); - void Func2(byte[] data, int size, byte[] crypt); - void Func3(byte[] data, int len, byte[] buff); - void Func4(byte[] id); - int Func5(int cnt, byte[] id, ref int n1, ref int n2); - void Func6(byte[] data, int len); - int Func7(int len); + void EncryptByEnc0Key(byte[] data, int size, byte[] crypt); + void CreateEnc0Key(byte[] data, int len, byte[] buff); + void CreateEnc1Key(byte[] validateKey, byte[] enc1Key); + int Func5(int cnt, byte[] id, ref int n1, ref int n2, byte[] encKey); + void EncryptByEnc1Key(byte[] data, int len, byte[] enc1Key); + int SubstituteEnc1key(int len, byte[] enc1Key); void Func8(byte[] data, int len, byte[] enctype1_data); - byte[] EncKey { get; } + byte[] Enc0Key { get; } + byte[] Enc1Key { get; } + byte[] ValidateKey { get; } } public interface IEnctypeShareTest diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs index 117c08bea..966d0adec 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs @@ -11,173 +11,172 @@ public class Enctype1 : EnctypeBase, IEnctype1Test /// The server key /// 256 bytes /// - public static readonly byte[] Enctype1Table = new byte[256] { 0x01, 0xba, 0xfa, 0xb2, 0x51, 0x00, 0x54, 0x80, 0x75, 0x16, 0x8e, 0x8e, 0x02, 0x08, 0x36, 0xa5, 0x2d, 0x05, 0x0d, 0x16, 0x52, 0x07, 0xb4, 0x22, 0x8c, 0xe9, 0x09, 0xd6, 0xb9, 0x26, 0x00, 0x04, 0x06, 0x05, 0x00, 0x13, 0x18, 0xc4, 0x1e, 0x5b, 0x1d, 0x76, 0x74, 0xfc, 0x50, 0x51, 0x06, 0x16, 0x00, 0x51, 0x28, 0x00, 0x04, 0x0a, 0x29, 0x78, 0x51, 0x00, 0x01, 0x11, 0x52, 0x16, 0x06, 0x4a, 0x20, 0x84, 0x01, 0xa2, 0x1e, 0x16, 0x47, 0x16, 0x32, 0x51, 0x9a, 0xc4, 0x03, 0x2a, 0x73, 0xe1, 0x2d, 0x4f, 0x18, 0x4b, 0x93, 0x4c, 0x0f, 0x39, 0x0a, 0x00, 0x04, 0xc0, 0x12, 0x0c, 0x9a, 0x5e, 0x02, 0xb3, 0x18, 0xb8, 0x07, 0x0c, 0xcd, 0x21, 0x05, 0xc0, 0xa9, 0x41, 0x43, 0x04, 0x3c, 0x52, 0x75, 0xec, 0x98, 0x80, 0x1d, 0x08, 0x02, 0x1d, 0x58, 0x84, 0x01, 0x4e, 0x3b, 0x6a, 0x53, 0x7a, 0x55, 0x56, 0x57, 0x1e, 0x7f, 0xec, 0xb8, 0xad, 0x00, 0x70, 0x1f, 0x82, 0xd8, 0xfc, 0x97, 0x8b, 0xf0, 0x83, 0xfe, 0x0e, 0x76, 0x03, 0xbe, 0x39, 0x29, 0x77, 0x30, 0xe0, 0x2b, 0xff, 0xb7, 0x9e, 0x01, 0x04, 0xf8, 0x01, 0x0e, 0xe8, 0x53, 0xff, 0x94, 0x0c, 0xb2, 0x45, 0x9e, 0x0a, 0xc7, 0x06, 0x18, 0x01, 0x64, 0xb0, 0x03, 0x98, 0x01, 0xeb, 0x02, 0xb0, 0x01, 0xb4, 0x12, 0x49, 0x07, 0x1f, 0x5f, 0x5e, 0x5d, 0xa0, 0x4f, 0x5b, 0xa0, 0x5a, 0x59, 0x58, 0xcf, 0x52, 0x54, 0xd0, 0xb8, 0x34, 0x02, 0xfc, 0x0e, 0x42, 0x29, 0xb8, 0xda, 0x00, 0xba, 0xb1, 0xf0, 0x12, 0xfd, 0x23, 0xae, 0xb6, 0x45, 0xa9, 0xbb, 0x06, 0xb8, 0x88, 0x14, 0x24, 0xa9, 0x00, 0x14, 0xcb, 0x24, 0x12, 0xae, 0xcc, 0x57, 0x56, 0xee, 0xfd, 0x08, 0x30, 0xd9, 0xfd, 0x8b, 0x3e, 0x0a, 0x84, 0x46, 0xfa, 0x77, 0xb8 }; + public static readonly byte[] MasterKey = new byte[256] { 0x01, 0xba, 0xfa, 0xb2, 0x51, 0x00, 0x54, 0x80, 0x75, 0x16, 0x8e, 0x8e, 0x02, 0x08, 0x36, 0xa5, 0x2d, 0x05, 0x0d, 0x16, 0x52, 0x07, 0xb4, 0x22, 0x8c, 0xe9, 0x09, 0xd6, 0xb9, 0x26, 0x00, 0x04, 0x06, 0x05, 0x00, 0x13, 0x18, 0xc4, 0x1e, 0x5b, 0x1d, 0x76, 0x74, 0xfc, 0x50, 0x51, 0x06, 0x16, 0x00, 0x51, 0x28, 0x00, 0x04, 0x0a, 0x29, 0x78, 0x51, 0x00, 0x01, 0x11, 0x52, 0x16, 0x06, 0x4a, 0x20, 0x84, 0x01, 0xa2, 0x1e, 0x16, 0x47, 0x16, 0x32, 0x51, 0x9a, 0xc4, 0x03, 0x2a, 0x73, 0xe1, 0x2d, 0x4f, 0x18, 0x4b, 0x93, 0x4c, 0x0f, 0x39, 0x0a, 0x00, 0x04, 0xc0, 0x12, 0x0c, 0x9a, 0x5e, 0x02, 0xb3, 0x18, 0xb8, 0x07, 0x0c, 0xcd, 0x21, 0x05, 0xc0, 0xa9, 0x41, 0x43, 0x04, 0x3c, 0x52, 0x75, 0xec, 0x98, 0x80, 0x1d, 0x08, 0x02, 0x1d, 0x58, 0x84, 0x01, 0x4e, 0x3b, 0x6a, 0x53, 0x7a, 0x55, 0x56, 0x57, 0x1e, 0x7f, 0xec, 0xb8, 0xad, 0x00, 0x70, 0x1f, 0x82, 0xd8, 0xfc, 0x97, 0x8b, 0xf0, 0x83, 0xfe, 0x0e, 0x76, 0x03, 0xbe, 0x39, 0x29, 0x77, 0x30, 0xe0, 0x2b, 0xff, 0xb7, 0x9e, 0x01, 0x04, 0xf8, 0x01, 0x0e, 0xe8, 0x53, 0xff, 0x94, 0x0c, 0xb2, 0x45, 0x9e, 0x0a, 0xc7, 0x06, 0x18, 0x01, 0x64, 0xb0, 0x03, 0x98, 0x01, 0xeb, 0x02, 0xb0, 0x01, 0xb4, 0x12, 0x49, 0x07, 0x1f, 0x5f, 0x5e, 0x5d, 0xa0, 0x4f, 0x5b, 0xa0, 0x5a, 0x59, 0x58, 0xcf, 0x52, 0x54, 0xd0, 0xb8, 0x34, 0x02, 0xfc, 0x0e, 0x42, 0x29, 0xb8, 0xda, 0x00, 0xba, 0xb1, 0xf0, 0x12, 0xfd, 0x23, 0xae, 0xb6, 0x45, 0xa9, 0xbb, 0x06, 0xb8, 0x88, 0x14, 0x24, 0xa9, 0x00, 0x14, 0xcb, 0x24, 0x12, 0xae, 0xcc, 0x57, 0x56, 0xee, 0xfd, 0x08, 0x30, 0xd9, 0xfd, 0x8b, 0x3e, 0x0a, 0x84, 0x46, 0xfa, 0x77, 0xb8 }; /// /// Client key /// 16 bytes /// public static readonly byte[] ClientKey = new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - public byte[] Key { get; private set; } - byte[] IEnctype1Test.EncKey => _enckey; - private uint[] _tbuff = new uint[326]; - private byte[] _enckey = new byte[261]; - public Enctype1(byte[] key) + public byte[] ValidateKey { get; private set; } + private byte[] _sboxInitSeed = BitConverter.GetBytes(0); + private uint[] _sbox = new uint[326]; + private byte[] _enc0Key = new byte[258]; + private byte[] _enc1Key = new byte[261]; + /// + /// The enc0seed is using to generate enc0key + /// + private byte[] _enc0Seed = Enumerable.Repeat(0x01, 16).ToArray(); + /// + /// _enc0seedIndex is the index of enc0seed, so we find the index in master key, + /// we only use the 0 index value in master key so we hard code the index + /// + private byte[] _enc0seedIndex = Enumerable.Repeat(0x00, 16).ToArray(); + public Enctype1(byte[] validateKey) { - Key = key; + ValidateKey = validateKey; } - public override byte[] Encrypt(byte[] data) { // create a copy - var dataCopy = data.ToArray(); - if (data.Length! > 1) + var plaintext = data.ToArray(); + + if (plaintext.Length <= 1) { - throw new ServerBrowser.Exception("The encryption data can not be empty"); + throw new ServerBrowser.Exception("The data can not be empty"); } - int tempLen = (data.Length >> 1) - 17; - int encshare4Data = 0; - byte[] encshare4DataBytes = BitConverter.GetBytes(encshare4Data); + int tempLen = (plaintext.Length >> 1) - 17; if (tempLen >= 0) { - EncShare4(encshare4DataBytes, encshare4DataBytes.Length, _tbuff); - Encshare1(_tbuff, 0, data, data.Length, tempLen); + ConstructSbox(_sboxInitSeed, _sboxInitSeed.Length, _sbox); + ChangeSboxEncryptPlaintext(_sbox, 0, plaintext, 0, tempLen); } - var scrambleData = Enumerable.Repeat(0x01, 16); - var encryption_key = new byte[258]; - // Func3() - var output = new uint[data.Length / sizeof(byte)]; - var outputBytes = output.SelectMany(BitConverter.GetBytes).ToArray(); + CreateEnc0Key(_enc0Seed, _enc0Key); + EncryptByEnc0Key(plaintext, plaintext.Length, _enc0Key); + tempLen = (plaintext.Length >> 2) - 5; if (tempLen >= 0) { - EncShare4(dataCopy, ClientKey.Length, output); - Func6(dataCopy, tempLen); + CreateEnc1Key(ValidateKey, _enc1Key); + EncryptByEnc1Key(plaintext, tempLen, _enc1Key); } - var newOutput = new List(); + var ciphertext = BuildOutput(ValidateKey, _enc0seedIndex, plaintext, _sboxInitSeed); + return ciphertext; + } + + private byte[] BuildOutput(byte[] validateKey, byte[] scrambleData, byte[] encryptedData, byte[] sboxInitSeed) + { + var output = new List(); // 4 bytes message length // newOutput.AddRange(new byte[4] { 0x00, 0x00, 0x00, 0x00 }); - newOutput.Add(42); - newOutput.Add(218); + output.Add(42); + output.Add(218); // 6 to 19 is unknown data, we do not use it - newOutput.AddRange(Enumerable.Repeat(0, 13)); - newOutput.AddRange(scrambleData); + output.AddRange(Enumerable.Repeat(0, 13).ToArray()); + output.AddRange(scrambleData); // unused data - newOutput.AddRange(new byte[4] { 0x00, 0x00, 0x00, 0x00 }); - // encshare4data - newOutput.AddRange(new byte[4] { 0x00, 0x00, 0x00, 0x00 }); + output.AddRange(new byte[4] { 0x00, 0x00, 0x00, 0x00 }); + // sboxInitSeed + output.AddRange(sboxInitSeed); // unused data - newOutput.AddRange(Enumerable.Repeat(0x00, 18)); + output.AddRange(Enumerable.Repeat(0x00, 18).ToArray()); - newOutput.AddRange(dataCopy); + output.AddRange(encryptedData); // insert encryption length to index 0 - newOutput.InsertRange(0, BitConverter.GetBytes(newOutput.Count).Reverse()); - - - throw new System.NotImplementedException(); + output.InsertRange(0, BitConverter.GetBytes(output.Count).Reverse()); + return output.ToArray(); } private byte[] Decoder(byte[] data, int dataLen) { - var tbuff = new byte[326]; - var tbuff2 = new byte[258]; - - var len = BitConverter.ToInt32(data.Take(4).ToArray()); - if (len <= 0) - { - throw new ServerBrowser.Exception("input data length must bigger than 0"); - } - if (len > dataLen) - { - throw new ServerBrowser.Exception("input data length can not bigger than dataLen"); - } - data[4] = (byte)((data[4] ^ 62) - 20); - data[5] = (byte)((data[5] ^ 205) - 5); - var tempData1 = data.Skip(19).ToArray(); - Func8(tempData1, 16, Enctype1Table); - data = data.Take(19).Concat(tempData1).ToArray(); - len -= data[4] + data[5] + 40; - var dataP = 0 + data[5] + 40; - var tempLen = (len >> 2) - 5; - if (tempLen >= 0) - { - Func4(Key); - var tempData2 = data.Skip(dataP).ToArray(); - Func6(tempData2, tempLen); - } - - - tempLen = (len >> 1) - 17; - if (tempLen >= 0) - { - var tempData3 = data.Skip(36).ToArray(); - uint[] tbuffInt = Array.ConvertAll(tbuff, Convert.ToUInt32); - EncShare4(tempData3, 4, tbuffInt); - data = data.Skip(36).Concat(tempData3).ToArray(); - var tempData4 = data.Skip(dataP).ToArray(); - Encshare1(tbuffInt, 0, data, dataP, tempLen); - data = data.Skip(dataP).Concat(tempData4).ToArray(); - tbuff = Array.ConvertAll(tbuffInt, Convert.ToByte); - } - - var tempData5 = data.Skip(19).ToArray(); - Func3(tempData5, 16, tbuff2); - data = data.Skip(19).Concat(tempData5).ToArray(); - var tempData6 = data.Skip(dataP).ToArray(); - Func2(tempData6, len, tbuff2); - data = data.Skip(dataP).Concat(tempData6).ToArray(); - return data; - } - private void Func1(byte[] data) - { - if (data is not null) - { - throw new ServerBrowser.Exception("the input data should not be null."); - } - if (data.Length <= 0) - { - throw new ServerBrowser.Exception("the input data length is not valid."); - } - Func4(data); + // var tbuff = new byte[326]; + // var tbuff2 = new byte[258]; + + // var len = BitConverter.ToInt32(data.Take(4).ToArray()); + // if (len <= 0) + // { + // throw new ServerBrowser.Exception("input data length must bigger than 0"); + // } + // if (len > dataLen) + // { + // throw new ServerBrowser.Exception("input data length can not bigger than dataLen"); + // } + // data[4] = (byte)((data[4] ^ 62) - 20); + // data[5] = (byte)((data[5] ^ 205) - 5); + // var tempData1 = data.Skip(19).ToArray(); + // Func8(tempData1, 16, Enctype1Table); + // data = data.Take(19).Concat(tempData1).ToArray(); + // len -= data[4] + data[5] + 40; + // var dataP = 0 + data[5] + 40; + // var tempLen = (len >> 2) - 5; + // if (tempLen >= 0) + // { + // ChangeEncKeyByValidateKey(Key,enckey); + // var tempData2 = data.Skip(dataP).ToArray(); + // Func6(tempData2, tempLen); + // } + + + // tempLen = (len >> 1) - 17; + // if (tempLen >= 0) + // { + // var tempData3 = data.Skip(36).ToArray(); + // uint[] tbuffInt = Array.ConvertAll(tbuff, Convert.ToUInt32); + // ConstructSbox(tempData3, 4, tbuffInt); + // data = data.Skip(36).Concat(tempData3).ToArray(); + // var tempData4 = data.Skip(dataP).ToArray(); + // ChangeSboxEncryptPlaintext(tbuffInt, 0, data, dataP, tempLen); + // data = data.Skip(dataP).Concat(tempData4).ToArray(); + // tbuff = Array.ConvertAll(tbuffInt, Convert.ToByte); + // } + + // var tempData5 = data.Skip(19).ToArray(); + // CreateEncryptionKey(tempData5, tbuff2); + // data = data.Skip(19).Concat(tempData5).ToArray(); + // var tempData6 = data.Skip(dataP).ToArray(); + // EncryptByEncKey(tempData6, len, tbuff2); + // data = data.Skip(dataP).Concat(tempData6).ToArray(); + // return data; + throw new System.NotImplementedException(); } - void Func2(byte[] data, int size, byte[] crypt) + void EncryptByEnc0Key(byte[] data, int size, byte[] enc0Key) { byte n1, n2, t; - n1 = crypt[256]; - n2 = crypt[257]; + n1 = enc0Key[256]; + n2 = enc0Key[257]; int dataIndex = 0; while (size-- > 0) { - t = crypt[++n1]; + t = enc0Key[++n1]; n2 += t; - crypt[n1] = crypt[n2]; - crypt[n2] = t; + enc0Key[n1] = enc0Key[n2]; + enc0Key[n2] = t; - t += crypt[n1]; - data[dataIndex] ^= crypt[t]; + t += enc0Key[n1]; + data[dataIndex] ^= enc0Key[t]; dataIndex++; } - crypt[256] = n1; - crypt[257] = n2; + enc0Key[256] = n1; + enc0Key[257] = n2; } - void Func3(byte[] data, int len, byte[] buff) + void CreateEnc0Key(byte[] scrambleData, byte[] enc0Key) { int i; byte pos = 0, tmp, rev = 0xff; for (i = 0; i <= byte.MaxValue; i++) { - buff[i] = rev--; + enc0Key[i] = rev--; } - buff[256] = 0; - buff[257] = 0; + enc0Key[256] = 0; + enc0Key[257] = 0; for (i = 0; i <= byte.MaxValue; i++) { - tmp = buff[i]; - pos += (byte)(data[i % len] + tmp); - buff[i] = buff[pos]; - buff[pos] = tmp; + tmp = enc0Key[i]; + pos += (byte)(scrambleData[i % scrambleData.Length] + tmp); + enc0Key[i] = enc0Key[pos]; + enc0Key[pos] = tmp; } } - void Func4(byte[] id) + void CreateEnc1Key(byte[] validateKey, byte[] enc1Key) { // Declare variables i, n1, n2, t1, t2 int i, @@ -187,30 +186,30 @@ void Func4(byte[] id) t2; // If idlen is less than 1, return directly - if (id.Length < 1) + if (validateKey.Length < 1) return; // Initialize encryption array _enc1key for (i = 0; i < 256; i++) - _enckey[i] = (byte)i; + enc1Key[i] = (byte)i; // Shuffle the encryption array _enc1key for (i = 255; i >= 0; i--) { - t1 = (byte)Func5(i, id, ref n1, ref n2); - t2 = _enckey[i]; - _enckey[i] = _enckey[t1]; - _enckey[t1] = t2; + t1 = (byte)Func5(i, validateKey, ref n1, ref n2, enc1Key); + t2 = enc1Key[i]; + enc1Key[i] = enc1Key[t1]; + enc1Key[t1] = t2; } // Set specific values to some elements of the encryption array _enc1key - _enckey[256] = _enckey[1]; - _enckey[257] = _enckey[3]; - _enckey[258] = _enckey[5]; - _enckey[259] = _enckey[7]; - _enckey[260] = _enckey[n1 & 0xff]; + enc1Key[256] = enc1Key[1]; + enc1Key[257] = enc1Key[3]; + enc1Key[258] = enc1Key[5]; + enc1Key[259] = enc1Key[7]; + enc1Key[260] = enc1Key[n1 & 0xff]; } - int Func5(int cnt, byte[] id, ref int n1, ref int n2) + int Func5(int cnt, byte[] id, ref int n1, ref int n2, byte[] encKey) { // Declare variables i, tmp, mask, and initialize mask as 1 int i, @@ -237,7 +236,7 @@ int Func5(int cnt, byte[] id, ref int n1, ref int n2) do { // Update the values of n1 and n2, and calculate the tmp value - n1 = _enckey[n1 & 0xff] + id[n2]; + n1 = encKey[n1 & 0xff] + id[n2]; n2++; if (n2 >= id.Length) { @@ -257,74 +256,73 @@ int Func5(int cnt, byte[] id, ref int n1, ref int n2) /// /// init the enckey, data is not touched /// - void Func6(byte[] data, int len) + void EncryptByEnc1Key(byte[] data, int len, byte[] enc1Key) { int i = 0; while (len-- > 0) { - data[i] = (byte)Func7(data[i]); + data[i] = (byte)SubstituteEnc1key(data[i], enc1Key); i++; } } - - int Func7(int len) + int SubstituteEnc1key(int len, byte[] enc1Key) { // Declare variables a, b, c as unsigned char byte a, b, c; // Get certain elements from the encryption array _enc1key and calculate new values based on them - a = _enckey[256]; - b = _enckey[257]; - c = _enckey[a]; - _enckey[256] = (byte)(a + 1); - _enckey[257] = (byte)(b + c); - - a = _enckey[260]; - b = _enckey[257]; - b = _enckey[b]; - c = _enckey[a]; - _enckey[a] = b; - - a = _enckey[259]; - b = _enckey[257]; - a = _enckey[a]; - _enckey[b] = a; - - a = _enckey[256]; - b = _enckey[259]; - a = _enckey[a]; - _enckey[b] = a; - - a = _enckey[256]; - _enckey[a] = c; - b = _enckey[258]; - a = _enckey[c]; - c = _enckey[259]; + a = enc1Key[256]; + b = enc1Key[257]; + c = enc1Key[a]; + enc1Key[256] = (byte)(a + 1); + enc1Key[257] = (byte)(b + c); + + a = enc1Key[260]; + b = enc1Key[257]; + b = enc1Key[b]; + c = enc1Key[a]; + enc1Key[a] = b; + + a = enc1Key[259]; + b = enc1Key[257]; + a = enc1Key[a]; + enc1Key[b] = a; + + a = enc1Key[256]; + b = enc1Key[259]; + a = enc1Key[a]; + enc1Key[b] = a; + + a = enc1Key[256]; + enc1Key[a] = c; + b = enc1Key[258]; + a = enc1Key[c]; + c = enc1Key[259]; b = (byte)(b + a); - _enckey[258] = b; + enc1Key[258] = b; a = b; - c = _enckey[c]; - b = _enckey[257]; - b = _enckey[b]; - a = _enckey[a]; + c = enc1Key[c]; + b = enc1Key[257]; + b = enc1Key[b]; + a = enc1Key[a]; c += b; - b = _enckey[260]; - b = _enckey[b]; + b = enc1Key[260]; + b = enc1Key[b]; c += b; - b = _enckey[c]; - c = _enckey[256]; - c = _enckey[c]; + b = enc1Key[c]; + c = enc1Key[256]; + c = enc1Key[c]; a += c; - c = _enckey[b]; - b = _enckey[a]; + c = enc1Key[b]; + b = enc1Key[a]; // Store the len value in variable a and perform XOR on c and a a = (byte)len; c ^= b; - _enckey[260] = a; + enc1Key[260] = a; c ^= a; - _enckey[259] = c; + enc1Key[259] = c; // Return c value return c; @@ -341,20 +339,35 @@ void Func8(byte[] data, int len, byte[] enctype1_data) } } - void IEnctype1Test.Func1(byte[] data) => Func1(data); - - void IEnctype1Test.Func2(byte[] data, int size, byte[] crypt) => Func2(data, size, crypt); - - void IEnctype1Test.Func3(byte[] data, int len, byte[] buff) => Func3(data, len, buff); - - void IEnctype1Test.Func4(byte[] id) => Func4(id); - - int IEnctype1Test.Func5(int cnt, byte[] id, ref int n1, ref int n2) => Func5(cnt, id, ref n1, ref n2); - - void IEnctype1Test.Func6(byte[] data, int len) => Func6(data, len); - - int IEnctype1Test.Func7(int len) => Func7(len); + void EncryptScrambleData(byte[] scrambleData, byte[] masterKey) + { + for (int i = 0; i < scrambleData.Length; i++) + { + byte offset = FindIndexInMasterKey(scrambleData[i], masterKey); + scrambleData[i] = offset; + } + } + byte FindIndexInMasterKey(byte data, byte[] masterKey) + { + for (byte i = 0; i < masterKey.Length; i++) + { + if (data == masterKey[i]) + { + return i; + } + } + throw new System.Exception("No index found for scramble data"); + } + void IEnctype1Test.EncryptByEnc0Key(byte[] data, int size, byte[] crypt) => EncryptByEnc0Key(data, size, crypt); + void IEnctype1Test.CreateEnc0Key(byte[] data, int len, byte[] buff) => CreateEnc0Key(data, buff); + void IEnctype1Test.CreateEnc1Key(byte[] validateKey, byte[] enc1Key) => CreateEnc1Key(validateKey, enc1Key); + int IEnctype1Test.Func5(int cnt, byte[] id, ref int n1, ref int n2, byte[] encKey) => Func5(cnt, id, ref n1, ref n2, encKey); + void IEnctype1Test.EncryptByEnc1Key(byte[] data, int len, byte[] enc1Key) => EncryptByEnc1Key(data, len, enc1Key); + int IEnctype1Test.SubstituteEnc1key(int len, byte[] enc1Key) => SubstituteEnc1key(len, enc1Key); void IEnctype1Test.Func8(byte[] data, int len, byte[] enctype1_data) => Func8(data, len, enctype1_data); + byte[] IEnctype1Test.Enc0Key => _enc0Key; + byte[] IEnctype1Test.Enc1Key => _enc1Key; + byte[] IEnctype1Test.ValidateKey => ValidateKey; } } \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs index d425cf371..5225cd22b 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs @@ -24,7 +24,7 @@ public override byte[] Encrypt(byte[] data) private int Encoder(byte[] key, byte[] data, int size) { - uint[] dest = new uint[326]; + uint[] sbox = new uint[326]; int i; int headerSize = 8; @@ -38,15 +38,15 @@ private int Encoder(byte[] key, byte[] data, int size) byte datap = 1; // set 1-8 as 0 Array.Clear(data, datap, 8); - Array.Clear(dest, 256, dest.Length - 256); + Array.Clear(sbox, 256, sbox.Length - 256); - var dataTemp = data.Skip(datap).ToArray(); - EncShare4(dataTemp, data[0], dest); - Array.Copy(dataTemp, 0, data, datap, dataTemp.Length); + var sboxSeed = data.Skip(datap).ToArray(); + ConstructSbox(sboxSeed, data[0], sbox); + Array.Copy(sboxSeed, 0, data, datap, sboxSeed.Length); Array.Clear(data, data[0] + size + 1, 6); - Encshare1(dest, 0, data, datap + data[0], size + 6); + ChangeSboxEncryptPlaintext(sbox, 0, data, datap + data[0], size + 6); for (i = 0; i < key.Length; i++) data[datap + i] ^= key[i]; diff --git a/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs b/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs index d4fdd1433..804bf7670 100644 --- a/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs +++ b/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs @@ -1,3 +1,4 @@ +using System.Text; using System; using System.Linq; using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; @@ -78,9 +79,10 @@ public class Enctype1Test public void Func5Test() { IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); + var encKey = UniSpyEncoding.GetBytes("abcdef"); var key = UniSpyEncoding.GetBytes("abcdef"); int n1 = 2, n2 = 2; - enc1.Func5(5, key, ref n1, ref n2); + enc1.Func5(5, key, ref n1, ref n2, encKey); Assert.True(n1 == 99); Assert.True(n2 == 1); Assert.True(key.SequenceEqual(UniSpyEncoding.GetBytes("abcdef"))); @@ -89,20 +91,21 @@ public void Func5Test() public void Func4Test() { IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); + var key = UniSpyEncoding.GetBytes("abcdef"); - enc1.Func4(key); + enc1.CreateEnc1Key(enc1.ValidateKey, key); Assert.True(key.SequenceEqual(UniSpyEncoding.GetBytes("abcdef"))); var correctEnc1Key = new byte[] { 35, 53, 63, 48, 8, 15, 110, 160, 40, 34, 38, 172, 125, 28, 118, 9, 22, 107, 36, 16, 46, 104, 6, 136, 32, 50, 101, 132, 21, 119, 190, 20, 114, 154, 33, 42, 98, 39, 131, 70, 56, 144, 4, 120, 2, 26, 149, 18, 0, 153, 24, 44, 12, 152, 10, 138, 51, 133, 71, 17, 5, 202, 3, 208, 11, 128, 134, 83, 14, 41, 23, 52, 122, 151, 47, 116, 27, 108, 139, 214, 113, 75, 250, 78, 145, 86, 57, 124, 111, 45, 126, 59, 147, 65, 141, 137, 143, 69, 96, 148, 30, 105, 95, 196, 129, 146, 62, 109, 54, 81, 130, 102, 68, 115, 60, 117, 58, 140, 74, 121, 66, 123, 64, 142, 135, 127, 72, 99, 166, 76, 220, 82, 226, 112, 89, 80, 77, 92, 29, 90, 87, 84, 93, 178, 232, 100, 150, 88, 155, 184, 238, 106, 244, 94, 161, 156, 157, 158, 159, 1, 167, 162, 163, 164, 165, 7, 173, 168, 169, 170, 171, 13, 179, 174, 175, 176, 177, 19, 185, 180, 181, 182, 183, 25, 191, 186, 187, 188, 189, 31, 197, 192, 193, 194, 195, 37, 203, 198, 199, 200, 201, 43, 209, 204, 205, 206, 207, 49, 215, 210, 211, 212, 213, 55, 221, 216, 217, 218, 219, 61, 227, 222, 223, 224, 225, 67, 233, 228, 229, 230, 231, 73, 239, 234, 235, 236, 237, 79, 245, 240, 241, 242, 243, 85, 251, 246, 247, 248, 249, 91, 103, 252, 253, 254, 255, 97, 53, 48, 15, 160, 45 }; - Assert.True(enc1.EncKey.SequenceEqual(correctEnc1Key)); + Assert.True(enc1.Enc1Key.SequenceEqual(correctEnc1Key)); } [Fact] public void Func7Test() { IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); - var r = enc1.Func7(10); + var r = enc1.SubstituteEnc1key(10, enc1.Enc1Key); var correctEnc1Key = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 10, 10 }; - Assert.True(enc1.EncKey.SequenceEqual(correctEnc1Key)); + Assert.True(enc1.Enc1Key.SequenceEqual(correctEnc1Key)); } [Fact] @@ -110,7 +113,7 @@ public void Func8Test() { IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); var data = enc1.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); - enc1.Func8(data, 10, Enctype1.Enctype1Table); + enc1.Func8(data, 10, Enctype1.MasterKey); var correctData = new uint[] { 16843194, 16843194, 442, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; var dataUints = enc1.ConvertBytesToUint(data); Assert.True(correctData.SequenceEqual(dataUints)); @@ -121,11 +124,11 @@ public void Func6Test() { IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); var data = enc1.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); - enc1.Func6(data, 10); + enc1.EncryptByEnc1Key(data, 10, enc1.Enc1Key); var correctData = new byte[] { 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 }; Assert.True(correctData.SequenceEqual(data)); - var encKeyCorrect = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0 }; - Assert.True(encKeyCorrect.SequenceEqual(enc1.EncKey)); + var enc1KeyCorrect = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0 }; + Assert.True(enc1KeyCorrect.SequenceEqual(enc1.Enc1Key)); } [Fact] @@ -134,7 +137,7 @@ public void Func3Test() IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); var data = enc1.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); var buff = enc1.ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); - enc1.Func3(data, 10, buff); + enc1.CreateEnc0Key(data, 10, buff); var correctBuff = new uint[] { 2423783724, 2626217045, 2666177577, 3362939995, 3417775348, 3999423967, 1642197989, 91342666, 3246176910, 455134510, 3298402371, 2675676894, 4059931565, 2176375884, 1087007327, 3680970979, 2960389948, 3992074825, 449167537, 4019542232, 3920131915, 1672128080, 1953860086, 2635063843, 499266173, 995782236, 1863130809, 2899638540, 3943859670, 3611120871, 2705294232, 1747078419, 1389594391, 1704679349, 830416758, 2357868666, 556737645, 2486454320, 184857584, 1150109371, 1338197782, 1775337177, 3216789815, 2952129716, 713304926, 4209027549, 171131454, 2933556984, 2184588750, 896240384, 1895567724, 121182412, 4283344853, 2548565524, 242815122, 1444522835, 3062851200, 3486691985, 3499345562, 631422715, 149333344, 1096400159, 4261619269, 164173058, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; var buffUints = enc1.ConvertBytesToUint(buff); Assert.True(correctBuff.SequenceEqual(buffUints)); @@ -146,9 +149,22 @@ public void Func2Test() IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); var data = enc1.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); var crypt = enc1.ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); - enc1.Func2(data, 10, crypt); + enc1.EncryptByEnc0Key(data, 10, crypt); var correctBuff = new uint[] { 33554432, 131584, 512, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1548, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; var buffUints = enc1.ConvertBytesToUint(crypt); Assert.True(correctBuff.SequenceEqual(buffUints)); } + + [Fact] + public void TotalEncTest() + { + // Given + var key = Encoding.ASCII.GetBytes("123456"); + var plainText = Encoding.ASCII.GetBytes("000000000000000000000000000000000000"); + var enc = new Enctype1(key); + var result = enc.Encrypt(plainText); + // When + + // Then + } } \ No newline at end of file From 8e5f94d9c0d772e6ba56b45f3645260be6074228 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 21 Aug 2023 22:49:21 +0800 Subject: [PATCH 037/231] refactor(sb): update enctype2 --- .../V1/Abstraction/BaseClass/EnctypeBase.cs | 4 +-- .../V1/Abstraction/Interface/IEnctypeTest.cs | 2 +- .../src/V1/Aggregate/Enctype1.cs | 2 +- .../src/V1/Aggregate/Enctype2.cs | 28 +++++++++++++------ .../ServerBrowser/test/V1/EnctypeTest.cs | 10 +++---- 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs index d004d11d2..e57e490ec 100644 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs +++ b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs @@ -20,7 +20,7 @@ public abstract class EnctypeBase : ICryptography, IEnctypeShareTest /// datap is a subset of tbuff /// datap start index /// unknown - protected void ChangeSboxEncryptPlaintext(uint[] sbox, int sboxIndex, byte[] data, int startIndex, int len) + protected void ChangeSboxEncryptPlaintext(uint[] sbox, byte[] data, int startIndex, int len) { int pIndex, sIndex; // convert uint array to byte array @@ -221,7 +221,7 @@ protected static uint[] ConvertBytesToUint(byte[] input) return onputInts; } - void IEnctypeShareTest.Encshare1(uint[] tbuff, int tbuffIndex, byte[] datap, int datapIndex, int len) => ChangeSboxEncryptPlaintext(tbuff, tbuffIndex, datap, datapIndex, len); + void IEnctypeShareTest.Encshare1(uint[] tbuff, byte[] datap, int datapIndex, int len) => ChangeSboxEncryptPlaintext(tbuff, datap, datapIndex, len); void IEnctypeShareTest.Encshare2(uint[] tbuff, uint tbuffp, int len) => SboxElementExchange(tbuff, tbuffp, len); diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs index a106ce2ac..8d3e6fd25 100644 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs +++ b/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs @@ -14,7 +14,7 @@ public interface IEnctype1Test : IEnctypeShareTest public interface IEnctypeShareTest { - void Encshare1(uint[] tbuff, int tbuffIndex, byte[] datap, int datapIndex, int len); + void Encshare1(uint[] tbuff, byte[] datap, int datapIndex, int len); void Encshare2(uint[] tbuff, uint tbuffp, int len); void EncShare3(uint[] data, int n1 = 0, int n2 = 0); void EncShare4(byte[] src, int size, uint[] dest); diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs index 966d0adec..e0f2aa61d 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs @@ -49,7 +49,7 @@ public override byte[] Encrypt(byte[] data) if (tempLen >= 0) { ConstructSbox(_sboxInitSeed, _sboxInitSeed.Length, _sbox); - ChangeSboxEncryptPlaintext(_sbox, 0, plaintext, 0, tempLen); + ChangeSboxEncryptPlaintext(_sbox, plaintext, 0, tempLen); } CreateEnc0Key(_enc0Seed, _enc0Key); EncryptByEnc0Key(plaintext, plaintext.Length, _enc0Key); diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs index 5225cd22b..8b02d6555 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs @@ -5,11 +5,23 @@ namespace UniSpy.Server.ServerBrowser.V1.Aggregate { - + public class Enc2Params + { + public const int CRYPT_HEIGHT = 16; + uint[] Sbox = new uint[256]; + uint[] XStack = new uint[CRYPT_HEIGHT]; + uint[] YStack = new uint[CRYPT_HEIGHT]; + uint[] ZStack = new uint[CRYPT_HEIGHT]; + int Index; + uint x, y, z; + uint TreeNum; + uint[] KeyData = new uint[16]; + } public class Enctype2 : EnctypeBase, IEnctype2Test { public const int HeaderSize = 8; public byte[] GameSecreteKey { get; private set; } + private Enc2Params _params = new Enc2Params(); public Enctype2(string gameSecretKey) { GameSecreteKey = UniSpyEncoding.GetBytes(gameSecretKey); @@ -26,27 +38,25 @@ private int Encoder(byte[] key, byte[] data, int size) { uint[] sbox = new uint[326]; int i; - int headerSize = 8; + // int headerSize = 8; for (i = size - 1; i >= 0; i--) { - data[1 + headerSize + i] = data[i]; + data[1 + HeaderSize + i] = data[i]; } - data[0] = (byte)headerSize; + data[0] = (byte)HeaderSize; // set datap index byte datap = 1; // set 1-8 as 0 - Array.Clear(data, datap, 8); - Array.Clear(sbox, 256, sbox.Length - 256); - - + // Array.Clear(data, datap, 8); + // Array.Clear(sbox, 256, sbox.Length - 256); var sboxSeed = data.Skip(datap).ToArray(); ConstructSbox(sboxSeed, data[0], sbox); Array.Copy(sboxSeed, 0, data, datap, sboxSeed.Length); Array.Clear(data, data[0] + size + 1, 6); - ChangeSboxEncryptPlaintext(sbox, 0, data, datap + data[0], size + 6); + ChangeSboxEncryptPlaintext(sbox, data, datap + data[0], size + 6); for (i = 0; i < key.Length; i++) data[datap + i] ^= key[i]; diff --git a/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs b/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs index 804bf7670..31d011c57 100644 --- a/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs +++ b/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs @@ -24,14 +24,14 @@ public void EncShare2Test() [Fact] public void EncShare1Test() { - uint[] tbuff = Enumerable.Repeat(1, 326).ToArray(); - byte[] datap = encShare.ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); - encShare.Encshare1(tbuff, 0, datap, 0, 16); + uint[] sbox = Enumerable.Repeat(1, 326).ToArray(); + byte[] data = encShare.ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); + encShare.Encshare1(sbox, data, 0, 16); uint[] tbuffCorrect = new uint[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 17498629, 67830052, 39783468, 510855193, 366629229, 3551672311, 2479564971, 2781774699, 4118137534, 3094373108, 829124753, 3611234592, 1213911675, 3241236538, 3419395059, 3, 17236227, 34013461, 20580121, 306578602, 230830554, 2185634525, 1540642169, 2099507104, 1913332720, 3987761791, 3463819041, 860209457, 3948159327, 2584192363, 2377682576, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 32775, 32774, 32772, 12, 2389626517, 2938163061, 4097, 1, 1549944694, 2745022601, 3181633273, 1113334022, 2827919296, 1467047999, 1485013325, 2809953970, 4233124407, 61842888, 2999413561, 1295553734, 4062552591, 232414704, 3507545467, 787421828, 1 }; uint[] datapCorrect = new uint[] { 1549944692, 2745022603, 3181633275, 1113334020, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; - uint[] datapUints = encShare.ConvertBytesToUint(datap); + uint[] datapUints = encShare.ConvertBytesToUint(data); Assert.True(datapCorrect.SequenceEqual(datapUints)); - Assert.True(tbuffCorrect.SequenceEqual(tbuff)); + Assert.True(tbuffCorrect.SequenceEqual(sbox)); } [Fact] From 762d8ee8b8480738b68bd04930db9e28b0d83b61 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 23 Aug 2023 08:47:44 +0800 Subject: [PATCH 038/231] refactor(sb): update enctype2 related functions --- .../V1/Abstraction/Interface/IEnctypeTest.cs | 2 +- .../src/V1/Aggregate/Enctype2.cs | 197 ++++++++++++++---- 2 files changed, 154 insertions(+), 45 deletions(-) diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs index 8d3e6fd25..dabb2af54 100644 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs +++ b/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs @@ -24,6 +24,6 @@ public interface IEnctypeShareTest public interface IEnctype2Test : IEnctypeShareTest { - int Encoder(byte[] key, byte[] data, int size); + void Encoder(byte[] key, byte[] data, int size); } \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs index 8b02d6555..04c0dbe29 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs @@ -2,29 +2,163 @@ using System; using UniSpy.Server.Core.Encryption; using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; +using System.Collections.Generic; namespace UniSpy.Server.ServerBrowser.V1.Aggregate { - public class Enc2Params + public class Enctype2Params { - public const int CRYPT_HEIGHT = 16; - uint[] Sbox = new uint[256]; - uint[] XStack = new uint[CRYPT_HEIGHT]; - uint[] YStack = new uint[CRYPT_HEIGHT]; - uint[] ZStack = new uint[CRYPT_HEIGHT]; - int Index; - uint x, y, z; - uint TreeNum; - uint[] KeyData = new uint[16]; + public const int CRYPT_HEADER_LENGTH = 16; + public const int SBOX_SIZE = 256; + public const int NUM_KEYSETUP_SKIP = 2; + public const int CRYPT_MIN_LEAF_NUM = (1 << (CRYPT_HEADER_LENGTH)); + public uint[] Sbox = new uint[SBOX_SIZE]; + public uint[] XStack = new uint[CRYPT_HEADER_LENGTH]; + public uint[] YStack = new uint[CRYPT_HEADER_LENGTH]; + public uint[] ZStack = new uint[CRYPT_HEADER_LENGTH]; + public int Index; + public uint X, Y, Z; + public uint TreeNum; + public uint[] KeyData = new uint[16]; + public byte[] EncryptKey { get; private set; } = new byte[13] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + public bool IsInitialized = false; + + public Enctype2Params(byte[] gameSecretKey) + { + Array.Copy(gameSecretKey, EncryptKey, gameSecretKey.Length); + InitCryptParam(); + } + + private void InitCryptParam() + { + int i, j, k, index; + uint tmp; + for (j = 0; j < 4; j++) + { + for (i = 0; i < Sbox.Length; i++) + { + Sbox[i] = (byte)(Sbox[i] * Enctype2Params.SBOX_SIZE + i); + } + index = j; + for (k = 0; k < Enctype2Params.NUM_KEYSETUP_SKIP; k++) + { + for (i = 0; i < Enctype2Params.SBOX_SIZE; i++) + { + index += (byte)(EncryptKey[i % EncryptKey.Length] + Sbox[i]); + index &= (Enctype2Params.SBOX_SIZE - 1); + tmp = Sbox[i]; + Sbox[i] = Sbox[index]; + Sbox[index] = tmp; + } + } + } + for (i = 0; i < Enctype2Params.SBOX_SIZE; i++) + { + Sbox[i] ^= (byte)i; + } + UpdateParams(); + IsInitialized = true; + } + private static void Accumlator(ref uint x, ref uint y, ref uint z) + { + x += z; + y += x; + x += y; + } + private static void ByteShift1(ref uint x, ref uint y, ref uint z, uint[] sbox) + { + x = ~x; + x = (((x) << 24) | ((x) >> 8)); + x ^= sbox[x & 0xFF]; + y ^= sbox[y & 0xFF]; + x = (((x) << 24) | ((x) >> 8)); + y = (((y) << 8) | ((y) >> 24)); + x ^= sbox[x & 0xFF]; + y ^= sbox[y & 0xFF]; + y = (((y) << 8) | ((y) >> 24)); + z += (z + 1); + } + private static void ByteShift2(ref uint x, ref uint y, ref uint z, uint[] sbox) + { + y = (((y) << 24) | ((y) >> 8)); + x ^= sbox[x & 0xFF]; + y ^= sbox[y & 0xFF]; + y = (((y) << 24) | ((y) >> 8)); + x = (((x) << 8) | ((x) >> 24)); + x ^= sbox[x & 0xFF]; + y ^= sbox[y & 0xFF]; + x = (((x) << 8) | ((x) >> 24)); + z += z; + } + private void UpdateParams(uint treeNum = 0, uint leafNum = 0) + { + int i; + uint x, y, z; + i = 1 << (CRYPT_HEADER_LENGTH - 1); + x = treeNum; + y = 0; + z = 1; + Index = 0; + while (i > 0) + { + Accumlator(ref x, ref y, ref z); + if ((i & leafNum) != 0) + { + ByteShift1(ref x, ref y, ref z, Sbox); + } + else + { + XStack[Index] = x; + YStack[Index] = y; + ZStack[Index] = z; + Index++; + + ByteShift2(ref x, ref y, ref z, Sbox); + } + i >>= 1; + } + X = x; + Y = y; + Z = z; + TreeNum = treeNum; + // return X ^ Y; + } + public void RollingEncryptKey(uint startIndex = CRYPT_HEADER_LENGTH) + { + for (int i = 0; i < CRYPT_HEADER_LENGTH; i++) + { + while (Z < CRYPT_MIN_LEAF_NUM) + { + Accumlator(ref X, ref Y, ref Z); + XStack[Index] = X; + YStack[Index] = Y; + ZStack[Index] = Z; + Index++; + ByteShift1(ref X, ref Y, ref Z, Sbox); + } + startIndex++; + startIndex = X ^ Y; + Index--; + if (Index < 0) + { + Index = 0; + } + X = XStack[Index]; + Y = YStack[Index]; + Z = ZStack[Index]; + ByteShift2(ref X, ref Y, ref Z, Sbox); + } + } } public class Enctype2 : EnctypeBase, IEnctype2Test { public const int HeaderSize = 8; public byte[] GameSecreteKey { get; private set; } - private Enc2Params _params = new Enc2Params(); + private Enctype2Params _params; public Enctype2(string gameSecretKey) { GameSecreteKey = UniSpyEncoding.GetBytes(gameSecretKey); + _params = new Enctype2Params(GameSecreteKey); } public override byte[] Encrypt(byte[] data) @@ -34,45 +168,20 @@ public override byte[] Encrypt(byte[] data) // Encoder(GameSecreteKey,data,?); } - private int Encoder(byte[] key, byte[] data, int size) + private void Encoder(byte[] key, byte[] data, int size) { - uint[] sbox = new uint[326]; int i; - // int headerSize = 8; - - for (i = size - 1; i >= 0; i--) + var plainText = data.ToArray(); + for (i = 0; i < plainText.Length; i++) { - data[1 + HeaderSize + i] = data[i]; - } - data[0] = (byte)HeaderSize; - - // set datap index - byte datap = 1; - // set 1-8 as 0 - // Array.Clear(data, datap, 8); - // Array.Clear(sbox, 256, sbox.Length - 256); - var sboxSeed = data.Skip(datap).ToArray(); - ConstructSbox(sboxSeed, data[0], sbox); - Array.Copy(sboxSeed, 0, data, datap, sboxSeed.Length); - Array.Clear(data, data[0] + size + 1, 6); - - ChangeSboxEncryptPlaintext(sbox, data, datap + data[0], size + 6); + if (i == 0 || i >= (_params.EncryptKey.Length - 1)) + { - for (i = 0; i < key.Length; i++) - data[datap + i] ^= key[i]; - size += 1 + data[0] + 6; - data[0] ^= 0xec; - return size; - } - - private void KeyXor(byte[] data) - { - for (int i = 0; i < GameSecreteKey.Length; i++) - { - data[i] ^= GameSecreteKey[i]; + } + plainText[i] ^= _params.EncryptKey[i % _params.EncryptKey.Length]; } } - int IEnctype2Test.Encoder(byte[] key, byte[] data, int size) => Encoder(key, data, size); + void IEnctype2Test.Encoder(byte[] key, byte[] data, int size) => Encoder(key, data, size); } } \ No newline at end of file From 2a231a19b7f9fdfd4c33c38e8f7260166db93ee9 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 26 Aug 2023 09:46:23 +0800 Subject: [PATCH 039/231] refactor(sb): Amend enctype1 and enctype2 algorithm correctness --- .../V1/Abstraction/BaseClass/EnctypeBase.cs | 7 +- .../V1/Abstraction/Interface/IEnctypeTest.cs | 4 +- .../src/V1/Aggregate/Enctype1.cs | 15 ++- .../src/V1/Aggregate/Enctype2.cs | 116 +++++++++--------- .../ServerBrowser/test/V1/EnctypeTest.cs | 70 ++++++----- 5 files changed, 110 insertions(+), 102 deletions(-) diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs index e57e490ec..8b28f71a3 100644 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs +++ b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs @@ -196,7 +196,7 @@ protected void ConstructSbox(byte[] input, int size, uint[] output) EncShare3(output); } - protected static byte[] ConvertUintToBytes(uint[] input) + public static byte[] ConvertUintToBytes(uint[] input) { var ontputBytes = new byte[input.Length * sizeof(uint)]; for (var i = 0; i < input.Length; i++) @@ -205,7 +205,7 @@ protected static byte[] ConvertUintToBytes(uint[] input) } return ontputBytes.ToArray(); } - protected static uint[] ConvertBytesToUint(byte[] input) + public static uint[] ConvertBytesToUint(byte[] input) { if (input.Length % sizeof(uint) != 0) { @@ -228,8 +228,5 @@ protected static uint[] ConvertBytesToUint(byte[] input) void IEnctypeShareTest.EncShare3(uint[] data, int n1, int n2) => EncShare3(data, n1, n2); void IEnctypeShareTest.EncShare4(byte[] src, int size, uint[] dest) => ConstructSbox(src, size, dest); - uint[] IEnctypeShareTest.ConvertBytesToUint(byte[] input) => ConvertBytesToUint(input); - byte[] IEnctypeShareTest.ConvertUintToBytes(uint[] input) => ConvertUintToBytes(input); - } } \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs index dabb2af54..d1e6d9ed7 100644 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs +++ b/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs @@ -18,12 +18,10 @@ public interface IEnctypeShareTest void Encshare2(uint[] tbuff, uint tbuffp, int len); void EncShare3(uint[] data, int n1 = 0, int n2 = 0); void EncShare4(byte[] src, int size, uint[] dest); - byte[] ConvertUintToBytes(uint[] input); - uint[] ConvertBytesToUint(byte[] input); } public interface IEnctype2Test : IEnctypeShareTest { - void Encoder(byte[] key, byte[] data, int size); + // void Encoder(uint[] data); } \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs index e0f2aa61d..23fd3f0b3 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; namespace UniSpy.Server.ServerBrowser.V1.Aggregate @@ -31,9 +32,9 @@ public class Enctype1 : EnctypeBase, IEnctype1Test /// we only use the 0 index value in master key so we hard code the index /// private byte[] _enc0seedIndex = Enumerable.Repeat(0x00, 16).ToArray(); - public Enctype1(byte[] validateKey) + public Enctype1(string validateKey) { - ValidateKey = validateKey; + ValidateKey = Encoding.ASCII.GetBytes(validateKey); } public override byte[] Encrypt(byte[] data) @@ -66,23 +67,21 @@ public override byte[] Encrypt(byte[] data) private byte[] BuildOutput(byte[] validateKey, byte[] scrambleData, byte[] encryptedData, byte[] sboxInitSeed) { var output = new List(); - // 4 bytes message length - // newOutput.AddRange(new byte[4] { 0x00, 0x00, 0x00, 0x00 }); output.Add(42); output.Add(218); // 6 to 19 is unknown data, we do not use it output.AddRange(Enumerable.Repeat(0, 13).ToArray()); output.AddRange(scrambleData); - // unused data - output.AddRange(new byte[4] { 0x00, 0x00, 0x00, 0x00 }); + output.Add(0); // sboxInitSeed output.AddRange(sboxInitSeed); // unused data output.AddRange(Enumerable.Repeat(0x00, 18).ToArray()); output.AddRange(encryptedData); - // insert encryption length to index 0 - output.InsertRange(0, BitConverter.GetBytes(output.Count).Reverse()); + // insert total message length to index 0 + // 4 bytes message length + output.InsertRange(0, BitConverter.GetBytes(output.Count + 4).Reverse()); return output.ToArray(); } private byte[] Decoder(byte[] data, int dataLen) diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs index 04c0dbe29..9ff4bf4c9 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs @@ -19,33 +19,33 @@ public class Enctype2Params public int Index; public uint X, Y, Z; public uint TreeNum; - public uint[] KeyData = new uint[16]; - public byte[] EncryptKey { get; private set; } = new byte[13] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }; + public uint[] EncryptKey { get; set; } = new uint[16]; + public byte[] Seed { get; private set; } = new byte[13]; public bool IsInitialized = false; public Enctype2Params(byte[] gameSecretKey) { - Array.Copy(gameSecretKey, EncryptKey, gameSecretKey.Length); InitCryptParam(); + Array.Copy(gameSecretKey, Seed, gameSecretKey.Length); } private void InitCryptParam() { - int i, j, k, index; + uint i, j, k, index; uint tmp; for (j = 0; j < 4; j++) { - for (i = 0; i < Sbox.Length; i++) + for (i = 0; i < Enctype2Params.SBOX_SIZE; i++) { - Sbox[i] = (byte)(Sbox[i] * Enctype2Params.SBOX_SIZE + i); + Sbox[i] = Sbox[i] * Enctype2Params.SBOX_SIZE + i; } index = j; for (k = 0; k < Enctype2Params.NUM_KEYSETUP_SKIP; k++) { for (i = 0; i < Enctype2Params.SBOX_SIZE; i++) { - index += (byte)(EncryptKey[i % EncryptKey.Length] + Sbox[i]); - index &= (Enctype2Params.SBOX_SIZE - 1); + index += (Seed[i % Seed.Length] + Sbox[i]); + index &= Enctype2Params.SBOX_SIZE - 1; tmp = Sbox[i]; Sbox[i] = Sbox[index]; Sbox[index] = tmp; @@ -59,13 +59,13 @@ private void InitCryptParam() UpdateParams(); IsInitialized = true; } - private static void Accumlator(ref uint x, ref uint y, ref uint z) + public static void Accumlator(ref uint x, ref uint y, ref uint z) { x += z; y += x; x += y; } - private static void ByteShift1(ref uint x, ref uint y, ref uint z, uint[] sbox) + public static void ByteShift1(ref uint x, ref uint y, ref uint z, uint[] sbox) { x = ~x; x = (((x) << 24) | ((x) >> 8)); @@ -78,7 +78,7 @@ private static void ByteShift1(ref uint x, ref uint y, ref uint z, uint[] sbox) y = (((y) << 8) | ((y) >> 24)); z += (z + 1); } - private static void ByteShift2(ref uint x, ref uint y, ref uint z, uint[] sbox) + public static void ByteShift2(ref uint x, ref uint y, ref uint z, uint[] sbox) { y = (((y) << 24) | ((y) >> 8)); x ^= sbox[x & 0xFF]; @@ -92,62 +92,65 @@ private static void ByteShift2(ref uint x, ref uint y, ref uint z, uint[] sbox) } private void UpdateParams(uint treeNum = 0, uint leafNum = 0) { - int i; - uint x, y, z; - i = 1 << (CRYPT_HEADER_LENGTH - 1); - x = treeNum; - y = 0; - z = 1; + int i = 1 << (CRYPT_HEADER_LENGTH - 1); + X = treeNum; + Y = 0; + Z = 1; Index = 0; while (i > 0) { - Accumlator(ref x, ref y, ref z); + Accumlator(ref X, ref Y, ref Z); if ((i & leafNum) != 0) { - ByteShift1(ref x, ref y, ref z, Sbox); + ByteShift1(ref X, ref Y, ref Z, Sbox); } else { - XStack[Index] = x; - YStack[Index] = y; - ZStack[Index] = z; + XStack[Index] = X; + YStack[Index] = Y; + ZStack[Index] = Z; Index++; - ByteShift2(ref x, ref y, ref z, Sbox); + ByteShift2(ref X, ref Y, ref Z, Sbox); } i >>= 1; } - X = x; - Y = y; - Z = z; TreeNum = treeNum; - // return X ^ Y; } - public void RollingEncryptKey(uint startIndex = CRYPT_HEADER_LENGTH) + public void InitKeyData() { - for (int i = 0; i < CRYPT_HEADER_LENGTH; i++) + uint x, y, z; + int index; + x = X; + y = Y; + z = Z; + index = Index; + for (uint i = 0; i < EncryptKey.Length; i++) { - while (Z < CRYPT_MIN_LEAF_NUM) + while (z < Enctype2Params.CRYPT_MIN_LEAF_NUM) { - Accumlator(ref X, ref Y, ref Z); - XStack[Index] = X; - YStack[Index] = Y; - ZStack[Index] = Z; - Index++; - ByteShift1(ref X, ref Y, ref Z, Sbox); + Enctype2Params.Accumlator(ref x, ref y, ref z); + XStack[index] = x; + YStack[index] = y; + ZStack[index] = z; + index++; + Enctype2Params.ByteShift2(ref x, ref y, ref z, Sbox); } - startIndex++; - startIndex = X ^ Y; - Index--; - if (Index < 0) + EncryptKey[i] = x ^ y; + index--; + if (index < 0) { - Index = 0; + index = 0; } - X = XStack[Index]; - Y = YStack[Index]; - Z = ZStack[Index]; - ByteShift2(ref X, ref Y, ref Z, Sbox); + x = XStack[index]; + y = YStack[index]; + z = ZStack[index]; + Enctype2Params.ByteShift1(ref x, ref y, ref z, Sbox); } + X = x; + Y = y; + Z = z; + Index = index; } } public class Enctype2 : EnctypeBase, IEnctype2Test @@ -155,33 +158,34 @@ public class Enctype2 : EnctypeBase, IEnctype2Test public const int HeaderSize = 8; public byte[] GameSecreteKey { get; private set; } private Enctype2Params _params; + private List _header = new List(); public Enctype2(string gameSecretKey) { GameSecreteKey = UniSpyEncoding.GetBytes(gameSecretKey); _params = new Enctype2Params(GameSecreteKey); + _header.Add((byte)(_params.Seed.Length ^ 0xEC)); + _header.AddRange(_params.Seed); } public override byte[] Encrypt(byte[] data) - { - throw new System.NotImplementedException(); - //convert the byte array to unit array and parse into following functions - // Encoder(GameSecreteKey,data,?); - } - - private void Encoder(byte[] key, byte[] data, int size) { int i; var plainText = data.ToArray(); + var encKeyBytes = EnctypeBase.ConvertUintToBytes(_params.EncryptKey); for (i = 0; i < plainText.Length; i++) { - if (i == 0 || i >= (_params.EncryptKey.Length - 1)) + var modIndex = i % (encKeyBytes.Length - 1); + if (modIndex == 0) { - + _params.InitKeyData(); + encKeyBytes = EnctypeBase.ConvertUintToBytes(_params.EncryptKey); } - plainText[i] ^= _params.EncryptKey[i % _params.EncryptKey.Length]; + plainText[i] ^= encKeyBytes[modIndex]; } + return plainText; } - void IEnctype2Test.Encoder(byte[] key, byte[] data, int size) => Encoder(key, data, size); + + // void IEnctype2Test.Encoder(uint[] data) => InitKeyData(data); } } \ No newline at end of file diff --git a/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs b/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs index 31d011c57..af1ce959b 100644 --- a/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs +++ b/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs @@ -25,11 +25,11 @@ public void EncShare2Test() public void EncShare1Test() { uint[] sbox = Enumerable.Repeat(1, 326).ToArray(); - byte[] data = encShare.ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); + byte[] data = EnctypeBase.ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); encShare.Encshare1(sbox, data, 0, 16); uint[] tbuffCorrect = new uint[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 17498629, 67830052, 39783468, 510855193, 366629229, 3551672311, 2479564971, 2781774699, 4118137534, 3094373108, 829124753, 3611234592, 1213911675, 3241236538, 3419395059, 3, 17236227, 34013461, 20580121, 306578602, 230830554, 2185634525, 1540642169, 2099507104, 1913332720, 3987761791, 3463819041, 860209457, 3948159327, 2584192363, 2377682576, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 32775, 32774, 32772, 12, 2389626517, 2938163061, 4097, 1, 1549944694, 2745022601, 3181633273, 1113334022, 2827919296, 1467047999, 1485013325, 2809953970, 4233124407, 61842888, 2999413561, 1295553734, 4062552591, 232414704, 3507545467, 787421828, 1 }; uint[] datapCorrect = new uint[] { 1549944692, 2745022603, 3181633275, 1113334020, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; - uint[] datapUints = encShare.ConvertBytesToUint(data); + uint[] datapUints = EnctypeBase.ConvertBytesToUint(data); Assert.True(datapCorrect.SequenceEqual(datapUints)); Assert.True(tbuffCorrect.SequenceEqual(sbox)); } @@ -46,7 +46,7 @@ public void EncShare3Test() [Fact] public void EncShare4Test() { - byte[] datap = encShare.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); + byte[] datap = EnctypeBase.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); uint[] dest = Enumerable.Repeat(2, 326).ToArray(); var destCorrect = new uint[] { 791505064, 2677899357, 1869417374, 1465234252, 2475789121, 740983918, 2189457908, 1330493599, 3469488310, 404138664, 3890561660, 2037844579, 3772710031, 3014765711, 1414723042, 3250538244, 2239997656, 3368442198, 2829463375, 3873709831, 1616809334, 2408416094, 2593714885, 3065269826, 2728411308, 3115826211, 623071236, 1987339440, 3503169148, 3149487561, 3216855940, 4277978053, 2694731547, 943095479, 2947390224, 3284255792, 892575532, 4092678516, 3301083488, 1701020929, 50269918, 1583105766, 1178877547, 3233684681, 100976904, 1279948990, 909431883, 2341052024, 656793440, 1229403759, 825219562, 4059029186, 1111521199, 3435794874, 3486359808, 2088400399, 1498911874, 2458935151, 2138956613, 2661076370, 4193770664, 875740486, 3789516497, 67269043, 3048411365, 3722181363, 3907409649, 842022855, 1785245261, 2997891633, 1027315374, 1448379989, 4244284893, 3856866799, 1397854789, 4294782009, 168303206, 2206334467, 3200035771, 976798254, 2054714062, 1667360351, 1852589230, 4042177498, 3823179561, 1599942934, 437785722, 2223115414, 151501413, 3317924486, 3031575332, 4143189866, 134631422, 4227418534, 2981074340, 3385320959, 117789415, 2711583408, 2879989466, 3991617966, 286206350, 1818888530, 2071585337, 3739025234, 1431558871, 2021051280, 3267424079, 2745248443, 2307337611, 589374105, 2795805112, 1734712546, 2492609632, 4126389475, 4176931104, 2374707415, 3621103376, 3924286366, 2004218049, 84087549, 3957934975, 1212547806, 1549461370, 3082111598, 4025327728, 3132657759, 202009759, 1953642301, 2290488196, 2273689622, 1532595819, 2576839429, 724152551, 2610524026, 1717830723, 1802078870, 2543150389, 2762113475, 538843459, 926252336, 1835774642, 2442121321, 1768364936, 1077865321, 707273192, 3941128409, 50449982, 218878597, 6393, 2778976161, 3183168775, 3520064586, 3974786543, 2863201253, 959919482, 4075851646, 3166321558, 3334784883, 690431172, 336772646, 2964244513, 3705314749, 420976600, 1566245846, 387311668, 1010479510, 2896847722, 2846308697, 319944579, 2930534087, 3351637619, 1195746181, 1650488629, 1751570372, 3418979436, 3570593063, 4008508924, 505193660, 2627368049, 572549046, 252515951, 673636639, 1482054814, 2913665291, 4109518194, 2812631805, 3654778850, 454681732, 33435212, 1296779424, 1364191760, 3536885831, 2644195397, 269380140, 1145189234, 1515732185, 757817052, 1128356723, 1347352576, 3637938136, 1684159756, 3840047978, 3671636355, 2155743464, 2509489735, 555702320, 2559995931, 185189712, 3587438174, 606263025, 2324213583, 2526306506, 488365343, 1094685107, 1313605119, 1886282224, 2105240036, 774640903, 808383289, 993638370, 3553739022, 1162027228, 639955374, 4210601392, 3452684985, 1381031990, 1246242604, 2172609167, 1970533518, 3688480660, 2391593397, 4261137494, 1263113115, 1919957700, 3604219589, 3806348272, 3402139713, 303092719, 1044122905, 370419094, 353606213, 1903121829, 2357873757, 235667635, 4160071907, 2425266738, 3098990011, 521996276, 858858457, 2122099102, 3755824166, 2256844040, 1061009100, 1936812966, 471501886, 1633679743, 2, 3648983511, 3019444315, 3087473871, 3504257679, 3636804204, 677370271, 1517421204, 319045361, 2414764241, 1428393162, 1627687985, 3434938190, 1735044768, 1113717230, 3367048133, 1, 1803453643, 3619135896, 151234568, 3605443923, 1593688450, 2992556862, 3389812064, 1790820139, 3447432721, 1637898815, 1432460662, 2272000269, 3310238632, 964792890, 2941880083, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 16, 2110503879, 2280433177, 65536, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; @@ -63,12 +63,13 @@ public class Encrypt2Test [Fact] public void EncoderTest() { - var data = Enumerable.Repeat(2, 326).ToArray(); - var dataBytes = _enctype2.ConvertUintToBytes(data); - _enctype2.Encoder(((Enctype2)_enctype2).GameSecreteKey, dataBytes, 6); - var dataCorrect = new uint[] { 1667391972, 6710628, 3464277760, 3324666663, 1732499745, 230, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; - data = _enctype2.ConvertBytesToUint(dataBytes); - Assert.True(dataCorrect.SequenceEqual(data)); + // var data = Enumerable.Repeat(2, 326).ToArray(); + // var dataBytes = EnctypeBase.ConvertUintToBytes(data); + // var secretKeyBytes = EnctypeBase.ConvertBytesToUint(((Enctype2)_enctype2).GameSecreteKey); + // _enctype2.Encoder(secretKeyBytes); + // var dataCorrect = new uint[] { 1667391972, 6710628, 3464277760, 3324666663, 1732499745, 230, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; + // data = EnctypeBase.ConvertBytesToUint(dataBytes); + // Assert.True(dataCorrect.SequenceEqual(data)); } } @@ -78,7 +79,7 @@ public class Enctype1Test [Fact] public void Func5Test() { - IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); + IEnctype1Test enc1 = (IEnctype1Test)new Enctype1("abcdef"); var encKey = UniSpyEncoding.GetBytes("abcdef"); var key = UniSpyEncoding.GetBytes("abcdef"); int n1 = 2, n2 = 2; @@ -90,7 +91,7 @@ public void Func5Test() [Fact] public void Func4Test() { - IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); + IEnctype1Test enc1 = (IEnctype1Test)new Enctype1("abcdef"); var key = UniSpyEncoding.GetBytes("abcdef"); enc1.CreateEnc1Key(enc1.ValidateKey, key); @@ -102,7 +103,7 @@ public void Func4Test() [Fact] public void Func7Test() { - IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); + IEnctype1Test enc1 = (IEnctype1Test)new Enctype1("abcdef"); var r = enc1.SubstituteEnc1key(10, enc1.Enc1Key); var correctEnc1Key = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 10, 10 }; Assert.True(enc1.Enc1Key.SequenceEqual(correctEnc1Key)); @@ -111,19 +112,19 @@ public void Func7Test() [Fact] public void Func8Test() { - IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); - var data = enc1.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); + IEnctype1Test enc1 = (IEnctype1Test)new Enctype1("abcdef"); + var data = EnctypeBase.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); enc1.Func8(data, 10, Enctype1.MasterKey); var correctData = new uint[] { 16843194, 16843194, 442, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; - var dataUints = enc1.ConvertBytesToUint(data); + var dataUints = EnctypeBase.ConvertBytesToUint(data); Assert.True(correctData.SequenceEqual(dataUints)); } [Fact] public void Func6Test() { - IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); - var data = enc1.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); + IEnctype1Test enc1 = (IEnctype1Test)new Enctype1("abcdef"); + var data = EnctypeBase.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); enc1.EncryptByEnc1Key(data, 10, enc1.Enc1Key); var correctData = new byte[] { 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 }; Assert.True(correctData.SequenceEqual(data)); @@ -134,37 +135,46 @@ public void Func6Test() [Fact] public void Func3Test() { - IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); - var data = enc1.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); - var buff = enc1.ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); + IEnctype1Test enc1 = (IEnctype1Test)new Enctype1("abcdef"); + var data = EnctypeBase.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); + var buff = EnctypeBase.ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); enc1.CreateEnc0Key(data, 10, buff); var correctBuff = new uint[] { 2423783724, 2626217045, 2666177577, 3362939995, 3417775348, 3999423967, 1642197989, 91342666, 3246176910, 455134510, 3298402371, 2675676894, 4059931565, 2176375884, 1087007327, 3680970979, 2960389948, 3992074825, 449167537, 4019542232, 3920131915, 1672128080, 1953860086, 2635063843, 499266173, 995782236, 1863130809, 2899638540, 3943859670, 3611120871, 2705294232, 1747078419, 1389594391, 1704679349, 830416758, 2357868666, 556737645, 2486454320, 184857584, 1150109371, 1338197782, 1775337177, 3216789815, 2952129716, 713304926, 4209027549, 171131454, 2933556984, 2184588750, 896240384, 1895567724, 121182412, 4283344853, 2548565524, 242815122, 1444522835, 3062851200, 3486691985, 3499345562, 631422715, 149333344, 1096400159, 4261619269, 164173058, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; - var buffUints = enc1.ConvertBytesToUint(buff); + var buffUints = EnctypeBase.ConvertBytesToUint(buff); Assert.True(correctBuff.SequenceEqual(buffUints)); } [Fact] public void Func2Test() { - IEnctype1Test enc1 = (IEnctype1Test)new Enctype1(UniSpyEncoding.GetBytes("abcdef")); - var data = enc1.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); - var crypt = enc1.ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); + IEnctype1Test enc1 = (IEnctype1Test)new Enctype1("abcdef"); + var data = EnctypeBase.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); + var crypt = EnctypeBase.ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); enc1.EncryptByEnc0Key(data, 10, crypt); var correctBuff = new uint[] { 33554432, 131584, 512, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1548, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; - var buffUints = enc1.ConvertBytesToUint(crypt); + var buffUints = EnctypeBase.ConvertBytesToUint(crypt); Assert.True(correctBuff.SequenceEqual(buffUints)); } [Fact] - public void TotalEncTest() + public void FinalEnctype1Test() { - // Given - var key = Encoding.ASCII.GetBytes("123456"); + var key = "123456"; var plainText = Encoding.ASCII.GetBytes("000000000000000000000000000000000000"); var enc = new Enctype1(key); var result = enc.Encrypt(plainText); - // When + var correct = new byte[] { 0, 0, 0, 94, 42, 218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 154, 163, 177, 4, 195, 33, 192, 235, 227, 121, 208, 204, 19, 11, 74, 63, 102, 184, 149, 11, 229, 45, 113, 154, 18, 116, 175, 222, 66, 32, 146, 129, 11, 22, 161 }; + Assert.True(result.SequenceEqual(correct)); + } - // Then + [Fact] + public void FinalEnctype2Test() + { + var key = "123456"; + var plainText = Encoding.ASCII.GetBytes("000000000000000000000000000000000000"); + var enc = new Enctype2(key); + var result = enc.Encrypt(plainText); + var correct = new byte[] { 157, 76, 254, 23, 81, 26, 246, 17, 233, 115, 87, 214, 24, 146, 105, 83, 161, 42, 182, 182, 54, 110, 0, 236, 163, 144, 200, 28, 156, 151, 57, 95, 42, 141, 219, 192 }; + Assert.True(result.SequenceEqual(correct)); } } \ No newline at end of file From d7a564861d7db930f479ad998958dd2fbfc78abd Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sun, 27 Aug 2023 13:30:58 +0800 Subject: [PATCH 040/231] feature(web): update sake logic --- .../src/Database/DatabaseModel/Sakestorage.cs | 7 +- .../src/Aggregate/SakeArrayObject.cs | 3 + .../Contract/Request/CreateRecordRequest.cs | 45 ++++++--- .../Request/SearchForRecordsRequest.cs | 4 +- .../Response/SearchForRecordResponse.cs | 47 +++------- .../Contract/Result/SearchForRecordsResult.cs | 4 +- .../Sake/Handler/SearchForRecordsHandler.cs | 14 +++ .../src/UniSpy.Server.WebServer.csproj | 3 + src/Servers/WebServer/test/Sake/GameTest.cs | 2 +- .../WebServer/test/Sake/HandlerTest.cs | 91 +++++++++++++++++++ .../WebServer/test/Sake/RequestsTest.cs | 2 +- 11 files changed, 164 insertions(+), 58 deletions(-) diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Sakestorage.cs b/src/Libraries/Core/src/Database/DatabaseModel/Sakestorage.cs index 6d2b9a60d..8d3928905 100644 --- a/src/Libraries/Core/src/Database/DatabaseModel/Sakestorage.cs +++ b/src/Libraries/Core/src/Database/DatabaseModel/Sakestorage.cs @@ -1,5 +1,5 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; +using System.ComponentModel.DataAnnotations.Schema; +using System.Text.Json; namespace UniSpy.Server.Core.Database.DatabaseModel { @@ -12,8 +12,7 @@ public partial class Sakestorage public string Tableid { get; set; } = null!; public int Profileid { get; set; } [Column(TypeName = "jsonb")] - public Dictionary Userdata { get; set; } = null!; - + public JsonDocument Userdata { get; set; } = null!; public virtual Profile Profile { get; set; } = null!; } } diff --git a/src/Servers/WebServer/src/Aggregate/SakeArrayObject.cs b/src/Servers/WebServer/src/Aggregate/SakeArrayObject.cs index e0cb000a1..b5b494a18 100644 --- a/src/Servers/WebServer/src/Aggregate/SakeArrayObject.cs +++ b/src/Servers/WebServer/src/Aggregate/SakeArrayObject.cs @@ -1,5 +1,7 @@ +using System.Collections.Generic; namespace UniSpy.Server.WebServer.Aggregate { + public record RecordFieldObject : FieldObject { public string FieldValue { get; private set; } @@ -11,6 +13,7 @@ public RecordFieldObject(string fieldValue, string fieldName, string filedType) public record FieldObject { + public string FieldName { get; private set; } public string FiledType { get; private set; } diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/CreateRecordRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/CreateRecordRequest.cs index 4b5391260..5357a97db 100644 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/CreateRecordRequest.cs +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Request/CreateRecordRequest.cs @@ -1,35 +1,50 @@ using System.Collections.Generic; using System.Linq; +using System.Text.Json; using System.Xml.Linq; using UniSpy.Server.WebServer.Aggregate; using UniSpy.Server.WebServer.Module.Sake.Abstraction; namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request { - + public class CreateRecordRequest : RequestBase { - public List Values { get; set; } + public static readonly List SakeTypes = new List() + { + "binaryDataValue", + "booleanValue", + "dateAndTimeValue", + "unicodeStringValue", + "asciiStringValue", + "floatValue", + "int64Value", + "intValue", + "shortValue", + "byteValue" + }; + public JsonDocument Values { get; set; } public CreateRecordRequest(string rawRequest) : base(rawRequest) { - Values = new List(); } public override void Parse() { base.Parse(); - var valuesNode = _contentElement.Descendants().Where(p => p.Name.LocalName == "values").First(); - foreach (XElement element in valuesNode.Nodes()) - { - // TODO move this code to RecordFieldObject as static method which takes XElement as input returns a List - // first we find the value name by string "name" - var name = element.Descendants().Where(p => p.Name.LocalName == "name").First().Value; - // then we get the value type by string "value" - var type = element.Descendants().Where(p => p.Name.LocalName == "value").First().Descendants().First().Name.LocalName; - // then we get the actual value by its type we get before - var value = element.Descendants().Where(p => p.Name.LocalName == type).First().Value; - Values.Add(new RecordFieldObject(value, name, type)); - } + var valuesNode = _contentElement.Descendants().Where(p => p.Name.LocalName == "ArrayOfRecordValue").First(); + var jStr = Newtonsoft.Json.JsonConvert.SerializeXNode(valuesNode).Replace("ns1:", ""); + Values = JsonDocument.Parse(jStr); + // foreach (XElement element in valuesNode.Nodes()) + // { + // // TODO move this code to RecordFieldObject as static method which takes XElement as input returns a List + // // first we find the value name by string "name" + // var name = element.Descendants().Where(p => p.Name.LocalName == "name").First().Value; + // // then we get the value type by string "value" + // var type = element.Descendants().Where(p => p.Name.LocalName == "value").First().Descendants().First().Name.LocalName; + // // then we get the actual value by its type we get before + // var value = element.Descendants().Where(p => p.Name.LocalName == type).First().Value; + // Values.Add(new RecordFieldObject(value, name, type)); + // } } } } \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/SearchForRecordsRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/SearchForRecordsRequest.cs index 5746e84eb..3ac6f6406 100644 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/SearchForRecordsRequest.cs +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Request/SearchForRecordsRequest.cs @@ -14,7 +14,7 @@ public class SearchForRecordsRequest : RequestBase public string Offset { get; set; } public string Max { get; set; } public string Surrounding { get; set; } - public string OwnerIds { get; set; } + public int OwnerIds { get; set; } public string CacheFlag { get; set; } public List Fields { get; set; } @@ -37,7 +37,7 @@ public override void Parse() var surrounding = _contentElement.Descendants().Where(p => p.Name.LocalName == "surrounding").First().Value; Surrounding = surrounding; var ownerids = _contentElement.Descendants().Where(p => p.Name.LocalName == "ownerids").First().Value; - OwnerIds = ownerids; + OwnerIds = int.Parse(ownerids); var cacheFlag = _contentElement.Descendants().Where(p => p.Name.LocalName == "cacheFlag").First().Value; CacheFlag = cacheFlag; var fieldsNode = _contentElement.Descendants().Where(p => p.Name.LocalName == "fields").First(); diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs index 85e97f351..2802aee0a 100644 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs @@ -1,3 +1,6 @@ +using System.Text; +using System.Xml.Linq; +using System.Text.Json; using UniSpy.Server.WebServer.Module.Sake.Abstraction; using UniSpy.Server.WebServer.Module.Sake.Contract.Request; using UniSpy.Server.WebServer.Module.Sake.Contract.Result; @@ -14,43 +17,21 @@ public SearchForRecordResponse(SearchForRecordsRequest request, SearchForRecords public override void Build() { - _content.Add("values"); - _content.Add("ArrayOfRecordValue"); - foreach (var item in _result.UserData) + + var newXele = Newtonsoft.Json.JsonConvert.DeserializeXNode(_result.UserData.RootElement.ToString()).Root; + // add namespace to root element + newXele.Name = SakeSoapEnvelope.SakeNamespace + newXele.Name.LocalName; + // Add the namespace to each selected node + foreach (var childNode in newXele.DescendantsAndSelf()) { - if (item.Key == "RecordValue") + foreach (var element in childNode.Elements()) { - _content.Add("RecordValue"); + element.Name = SakeSoapEnvelope.SakeNamespace + element.Name.LocalName; } - _content.Add(item.Key, item.Value); } - SendingBuffer = @" - - - - Success - - - - - ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg - - - - - 0 - - - - - - - "; - // base.Build(); + _content.Add("values", newXele); + + base.Build(); } } } diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Result/SearchForRecordsResult.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Result/SearchForRecordsResult.cs index 05cd2914d..f8771b552 100644 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Result/SearchForRecordsResult.cs +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Result/SearchForRecordsResult.cs @@ -1,10 +1,10 @@ -using System.Collections.Generic; +using System.Text.Json; using UniSpy.Server.WebServer.Module.Sake.Abstraction; namespace UniSpy.Server.WebServer.Module.Sake.Contract.Result { public class SearchForRecordsResult : ResultBase { - public Dictionary UserData; + public JsonDocument UserData; } } \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs b/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs index b7f39fabf..4595e7905 100644 --- a/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs +++ b/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs @@ -1,8 +1,11 @@ +using System.Security.Cryptography.X509Certificates; using UniSpy.Server.WebServer.Abstraction; using UniSpy.Server.WebServer.Module.Sake.Contract.Response; using UniSpy.Server.WebServer.Module.Sake.Contract.Request; using UniSpy.Server.WebServer.Application; using UniSpy.Server.WebServer.Module.Sake.Contract.Result; +using UniSpy.Server.Core.Database.DatabaseModel; +using System.Linq; namespace UniSpy.Server.WebServer.Module.Sake.Handler { @@ -19,6 +22,17 @@ protected override void DataOperation() { base.DataOperation(); // search user data from database + using (var db = new UniSpyContext()) + { + _result.UserData = db.Sakestorages.Where( + t => t.Profileid == _request.OwnerIds + && t.Tableid == _request.TableId) + .Select(s => s.Userdata).FirstOrDefault(); + } + if (_result.UserData is null) + { + throw new WebServer.Exception("There are no sake data found in the database, please manually add it in the database"); + } } protected override void ResponseConstruct() { diff --git a/src/Servers/WebServer/src/UniSpy.Server.WebServer.csproj b/src/Servers/WebServer/src/UniSpy.Server.WebServer.csproj index 8b9381e25..31ce9ceb6 100755 --- a/src/Servers/WebServer/src/UniSpy.Server.WebServer.csproj +++ b/src/Servers/WebServer/src/UniSpy.Server.WebServer.csproj @@ -14,6 +14,9 @@ ..\..\..\..\build\$(Configuration) + + + diff --git a/src/Servers/WebServer/test/Sake/GameTest.cs b/src/Servers/WebServer/test/Sake/GameTest.cs index b640607b7..5e26ed218 100644 --- a/src/Servers/WebServer/test/Sake/GameTest.cs +++ b/src/Servers/WebServer/test/Sake/GameTest.cs @@ -27,7 +27,7 @@ public void Crysis2SakeTest() 0 1 0 - + 2 0 DATA diff --git a/src/Servers/WebServer/test/Sake/HandlerTest.cs b/src/Servers/WebServer/test/Sake/HandlerTest.cs index 139648997..ff6a1e0e6 100644 --- a/src/Servers/WebServer/test/Sake/HandlerTest.cs +++ b/src/Servers/WebServer/test/Sake/HandlerTest.cs @@ -1,6 +1,10 @@ +using System.Linq; +using System.Xml.Linq; +using System.Xml; using UniSpy.Server.WebServer.Module.Sake.Handler; using UniSpy.Server.WebServer.Module.Sake.Contract.Request; using Xunit; +using ChoETL; namespace UniSpy.Server.WebServer.Test.Sake { @@ -14,5 +18,92 @@ public void CreateRecordTest() var handler = new CreateRecordHandler(client, request); Assert.Throws(() => handler.Handle()); } + [Fact] + public void JsonDocumentTest() + { + var data = "[{\"binaryDataValue\":\"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\"},{\"intValue\":\"0\"}]"; + + var jsonDoc = System.Text.Json.JsonDocument.Parse(data); + var dataList = jsonDoc.RootElement.EnumerateArray(); + foreach (var element in dataList) + { + var kvEmu = element.EnumerateObject(); + foreach (var kv in kvEmu) + { + var key = kv.Name; + var value = kv.Value.GetString(); + } + } + // Given + + // When + + // Then + } + [Fact] + public void XMLToJsonTest() + { + var data = @" + + + + Success + + + + + ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg + + + + + 0 + + + + + + + "; + var xele = XElement.Parse(data); + var array = xele.Descendants().Where(d => d.Name.LocalName == "ArrayOfRecordValue").First(); + var jsonText = Newtonsoft.Json.JsonConvert.SerializeXNode(array); + jsonText = jsonText.Replace("ns1:", ""); + // var newJsonText = jsonText.Replace("{","n.) + jsonDoc.RootElement.ToString(); + + var newXele = Newtonsoft.Json.JsonConvert.DeserializeXNode(jsonText).Root; + var ns = Module.Sake.Contract.SakeSoapEnvelope.SakeNamespace; + // Add a namespace declaration to the root element + newXele.Add(new XAttribute(XNamespace.Xmlns + "ns1", ns)); + + // Select the nodes that need the namespace + // var recordValueNodes = newXele.Descendants(Module.Sake.Contract.SakeSoapEnvelope.SakeNamespace + "RecordValue"); + + // Add the namespace to each selected node + foreach (var childNode in newXele.DescendantsAndSelf()) + { + foreach (var attribute in childNode.Attributes()) + { + attribute.Remove(); + childNode.Add(new XAttribute(ns + attribute.Name.LocalName, attribute.Value)); + } + foreach (var element in childNode.Elements()) + { + element.Name = ns + element.Name.LocalName; + } + } + + // Print the modified XML content + string modifiedXml = newXele.ToString(); + // var xeleStr = newXele.ToString().Replace("<", " Date: Wed, 20 Sep 2023 10:35:48 +0800 Subject: [PATCH 041/231] refactor(sb): added v1 group and server info build logic --- .../V1/Contract/Request/GameNameRequest.cs | 4 +- .../V1/Contract/Response/GameNameResponse.cs | 4 -- .../src/V1/Contract/Response/ListResponse.cs | 43 ++++++++++++++++--- .../V1/Handler/CmdHandler/GameNameHandler.cs | 6 +-- .../src/UniSpy.Server.WebServer.csproj | 3 -- .../WebServer/test/Sake/HandlerTest.cs | 2 - 6 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/Servers/ServerBrowser/src/V1/Contract/Request/GameNameRequest.cs b/src/Servers/ServerBrowser/src/V1/Contract/Request/GameNameRequest.cs index 2e0cd8d8f..80e5dead2 100644 --- a/src/Servers/ServerBrowser/src/V1/Contract/Request/GameNameRequest.cs +++ b/src/Servers/ServerBrowser/src/V1/Contract/Request/GameNameRequest.cs @@ -19,7 +19,7 @@ public sealed class GameNameRequest : RequestBase { public EncryptionType EncType { get; private set; } public string Version { get; private set; } - public string EncKey { get; private set; } + public string ValidateKey { get; private set; } public GameNameRequest(string rawRequest) : base(rawRequest) { } @@ -46,7 +46,7 @@ public override void Parse() // star trek armada 2 do not use encryption if (KeyValues.ContainsKey("secure")) { - EncKey = KeyValues["secure"]; + ValidateKey = KeyValues["secure"]; } } } diff --git a/src/Servers/ServerBrowser/src/V1/Contract/Response/GameNameResponse.cs b/src/Servers/ServerBrowser/src/V1/Contract/Response/GameNameResponse.cs index c623696c8..838dd0061 100644 --- a/src/Servers/ServerBrowser/src/V1/Contract/Response/GameNameResponse.cs +++ b/src/Servers/ServerBrowser/src/V1/Contract/Response/GameNameResponse.cs @@ -4,10 +4,6 @@ namespace UniSpy.Server.ServerBrowser.V1.Contract.Response { public sealed class GameNameResponse : ResponseBase { - /// - /// Crypt key is 14 char long string - /// - public static string CryptKey = "00000000000000"; public GameNameResponse(RequestBase request, ResultBase result) : base(request, result) { } diff --git a/src/Servers/ServerBrowser/src/V1/Contract/Response/ListResponse.cs b/src/Servers/ServerBrowser/src/V1/Contract/Response/ListResponse.cs index 409e99e59..aee141828 100644 --- a/src/Servers/ServerBrowser/src/V1/Contract/Response/ListResponse.cs +++ b/src/Servers/ServerBrowser/src/V1/Contract/Response/ListResponse.cs @@ -1,3 +1,5 @@ +using System.Text.RegularExpressions; +using System.Linq; using System; using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; using UniSpy.Server.ServerBrowser.V1.Contract.Request; @@ -31,25 +33,54 @@ public override void Build() SendingBuffer += @"\final\"; } - + /// + /// Build group infos + /// public void BuildGroupInfo() { - + SendingBuffer = @"\group" + SendingBuffer; + foreach (var group in _result.PeerRoomsInfo) + { + foreach (var kv in group.KeyValues) + { + SendingBuffer += @$"\{kv.Key}\{kv.Value}"; + } + } } + /// + /// Build servers detailed info (contain all keyvalues of all server) + /// public void BuildServerAllInfo() { - + SendingBuffer = @"\list2" + SendingBuffer; + foreach (var info in _result.ServersInfo) + { + foreach (var kv in info.KeyValues) + { + SendingBuffer += @$"\{kv.Key}\{kv.Value}"; + } + } } + + /// + /// Build general info for servers (ip and port) + /// public void BuildServerGeneralInfo() { + SendingBuffer = @"\list2" + SendingBuffer; + foreach (var info in _result.ServersInfo) { if (_request.IsSendingCompressFormat) { - var buffer = BitConverter.ToString(info.HostIPAddress.GetAddressBytes()) + BitConverter.ToString(BitConverter.GetBytes((short)info.HostPort)); - SendingBuffer += @$"\ip\{buffer}"; + var portBytes = BitConverter.GetBytes((short)info.HostPort); + var buffer = BitConverter.ToString(info.HostIPAddress.GetAddressBytes()) + BitConverter.ToString(portBytes); + SendingBuffer += buffer; + } + else + { + SendingBuffer += @$"\ip\{info.HostIPEndPoint}"; } - SendingBuffer += @$"\ip\{info.HostIPEndPoint}"; } } } diff --git a/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/GameNameHandler.cs b/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/GameNameHandler.cs index 2d6a16318..2fe641c6b 100644 --- a/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/GameNameHandler.cs +++ b/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/GameNameHandler.cs @@ -1,5 +1,6 @@ using UniSpy.Server.Core.Extension; using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; +using UniSpy.Server.ServerBrowser.V1.Aggregate; using UniSpy.Server.ServerBrowser.V1.Application; using UniSpy.Server.ServerBrowser.V1.Contract.Request; @@ -26,16 +27,15 @@ protected override void RequestCheck() } protected override void DataOperation() { - switch (_request.EncType) { case EncryptionType.Plaintext: break; case EncryptionType.Type1: - // _client.Crypto = xxx; + _client.Crypto = new Enctype1(_request.ValidateKey); break; case EncryptionType.Type2: - // _client.Crypto = xxx; + _client.Crypto = new Enctype2(_client.Info.GameSecretKey); break; } } diff --git a/src/Servers/WebServer/src/UniSpy.Server.WebServer.csproj b/src/Servers/WebServer/src/UniSpy.Server.WebServer.csproj index 31ce9ceb6..8b9381e25 100755 --- a/src/Servers/WebServer/src/UniSpy.Server.WebServer.csproj +++ b/src/Servers/WebServer/src/UniSpy.Server.WebServer.csproj @@ -14,9 +14,6 @@ ..\..\..\..\build\$(Configuration) - - - diff --git a/src/Servers/WebServer/test/Sake/HandlerTest.cs b/src/Servers/WebServer/test/Sake/HandlerTest.cs index ff6a1e0e6..c86bf3c41 100644 --- a/src/Servers/WebServer/test/Sake/HandlerTest.cs +++ b/src/Servers/WebServer/test/Sake/HandlerTest.cs @@ -1,10 +1,8 @@ using System.Linq; using System.Xml.Linq; -using System.Xml; using UniSpy.Server.WebServer.Module.Sake.Handler; using UniSpy.Server.WebServer.Module.Sake.Contract.Request; using Xunit; -using ChoETL; namespace UniSpy.Server.WebServer.Test.Sake { From 49d66b894588ef3637cc17b60de3aa7891559cae Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 26 Sep 2023 20:54:25 +0800 Subject: [PATCH 042/231] refactor(chat): added crysis2 test --- src/Servers/Chat/test/Game/GameTest.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/Servers/Chat/test/Game/GameTest.cs b/src/Servers/Chat/test/Game/GameTest.cs index 084595356..2633a188f 100644 --- a/src/Servers/Chat/test/Game/GameTest.cs +++ b/src/Servers/Chat/test/Game/GameTest.cs @@ -297,5 +297,23 @@ public void WormsFortsUnderSiege20230520() (client as ITestClient).TestReceived(UniSpyEncoding.GetBytes(req)); } } + + [Fact] + public void Crysis2_20230926() + { + var client = MockObject.CreateClient(); + + var requests = new List{ + "CRYPT des 1 capricorn\r\n", + "LOGIN 95 Sporesirius baec04ae6862e941b948c8a1a361c096\r\n", + "USRIP\r\n", + "USER XpaGflff1X|39 127.0.0.1 peerchat.unispy.dev :e51130924b08de265d9ae69113103f78\r\nNICK *\r\n", + "QUIT :Later!\r\n" + }; + foreach (var req in requests) + { + (client as ITestClient).TestReceived(UniSpyEncoding.GetBytes(req)); + } + } } } \ No newline at end of file From 03fb580114c83571daeae62fb2d18900d16ba3ce Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 27 Sep 2023 00:20:32 +0800 Subject: [PATCH 043/231] refactor: added game test --- .vscode/launch.json | 43 +++++++++--- .../V1/Abstraction/BaseClass/EnctypeBase.cs | 2 +- .../src/V1/Aggregate/Enctype1.cs | 51 -------------- .../src/V1/Aggregate/Enctype2.cs | 9 +-- .../src/V1/Application/ClientInfo.cs | 1 + .../V1/Contract/Request/GameNameRequest.cs | 4 +- .../V1/Handler/CmdHandler/GameNameHandler.cs | 16 +++-- .../src/V1/Handler/CmdHandler/ListHandler.cs | 2 +- .../WebServer/src/Application/ClientInfo.cs | 4 +- .../Auth/Abstraction/LoginResponseBase.cs | 6 +- .../Contract/Request/CreateRecordRequest.cs | 11 --- .../Response/SearchForRecordResponse.cs | 20 ++---- .../Sake/Handler/SearchForRecordsHandler.cs | 1 - src/Servers/WebServer/src/UniSpy_Logo.ico | Bin 51196 -> 0 bytes src/Servers/WebServer/test/Auth/GameTest.cs | 64 ++++++++++++++++++ 15 files changed, 130 insertions(+), 104 deletions(-) delete mode 100644 src/Servers/WebServer/src/UniSpy_Logo.ico create mode 100644 src/Servers/WebServer/test/Auth/GameTest.cs diff --git a/.vscode/launch.json b/.vscode/launch.json index 27e6c71eb..0ea7fd15c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -14,6 +14,9 @@ "stopAtEntry": false, "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", + "logging": { + "moduleLoad": false + } }, { "name": "PSP", @@ -24,7 +27,10 @@ "cwd": "${workspaceRoot}/build/Debug/net6.0/", "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", - "stopAtEntry": false + "stopAtEntry": false, + "logging": { + "moduleLoad": false + } }, { "name": "NatNeg", @@ -35,7 +41,10 @@ "cwd": "${workspaceRoot}/build/Debug/net6.0/", "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", - "stopAtEntry": false + "stopAtEntry": false, + "logging": { + "moduleLoad": false + } }, { "name": "GTR", @@ -49,7 +58,10 @@ "env": { "ASPNETCORE_ENVIRONMENT": "Development" }, - "stopAtEntry": false + "stopAtEntry": false, + "logging": { + "moduleLoad": false + } }, { "name": "Chat", @@ -60,7 +72,10 @@ "cwd": "${workspaceRoot}/build/Debug/net6.0/", "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", - "stopAtEntry": false + "stopAtEntry": false, + "logging": { + "moduleLoad": false + } }, { "name": "QR", @@ -71,7 +86,10 @@ "cwd": "${workspaceRoot}/build/Debug/net6.0/", "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", - "stopAtEntry": false + "stopAtEntry": false, + "logging": { + "moduleLoad": false + } }, { "name": "SB", @@ -82,7 +100,10 @@ "cwd": "${workspaceRoot}/build/Debug/net6.0/", "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", - "stopAtEntry": false + "stopAtEntry": false, + "logging": { + "moduleLoad": false + } }, { "name": "GS", @@ -93,7 +114,10 @@ "cwd": "${workspaceRoot}/build/Debug/net6.0/", "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", - "stopAtEntry": false + "stopAtEntry": false, + "logging": { + "moduleLoad": false + } }, { "name": "Web", @@ -104,7 +128,10 @@ "cwd": "${workspaceRoot}/build/Debug/net6.0/", "console": "integratedTerminal", "internalConsoleOptions": "neverOpen", - "stopAtEntry": false + "stopAtEntry": false, + "logging": { + "moduleLoad": false + } } ], "compounds": [ diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs index 8b28f71a3..d8cecc844 100644 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs +++ b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs @@ -10,7 +10,7 @@ namespace UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass /// public abstract class EnctypeBase : ICryptography, IEnctypeShareTest { - public byte[] Decrypt(byte[] data) => throw new UniSpy.Exception("Enctype only encrypt message on server side."); + public byte[] Decrypt(byte[] data) => data; public abstract byte[] Encrypt(byte[] data); /// /// The encryption function 2 diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs index 23fd3f0b3..4fc691c8e 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs @@ -84,58 +84,7 @@ private byte[] BuildOutput(byte[] validateKey, byte[] scrambleData, byte[] encry output.InsertRange(0, BitConverter.GetBytes(output.Count + 4).Reverse()); return output.ToArray(); } - private byte[] Decoder(byte[] data, int dataLen) - { - // var tbuff = new byte[326]; - // var tbuff2 = new byte[258]; - - // var len = BitConverter.ToInt32(data.Take(4).ToArray()); - // if (len <= 0) - // { - // throw new ServerBrowser.Exception("input data length must bigger than 0"); - // } - // if (len > dataLen) - // { - // throw new ServerBrowser.Exception("input data length can not bigger than dataLen"); - // } - // data[4] = (byte)((data[4] ^ 62) - 20); - // data[5] = (byte)((data[5] ^ 205) - 5); - // var tempData1 = data.Skip(19).ToArray(); - // Func8(tempData1, 16, Enctype1Table); - // data = data.Take(19).Concat(tempData1).ToArray(); - // len -= data[4] + data[5] + 40; - // var dataP = 0 + data[5] + 40; - // var tempLen = (len >> 2) - 5; - // if (tempLen >= 0) - // { - // ChangeEncKeyByValidateKey(Key,enckey); - // var tempData2 = data.Skip(dataP).ToArray(); - // Func6(tempData2, tempLen); - // } - - // tempLen = (len >> 1) - 17; - // if (tempLen >= 0) - // { - // var tempData3 = data.Skip(36).ToArray(); - // uint[] tbuffInt = Array.ConvertAll(tbuff, Convert.ToUInt32); - // ConstructSbox(tempData3, 4, tbuffInt); - // data = data.Skip(36).Concat(tempData3).ToArray(); - // var tempData4 = data.Skip(dataP).ToArray(); - // ChangeSboxEncryptPlaintext(tbuffInt, 0, data, dataP, tempLen); - // data = data.Skip(dataP).Concat(tempData4).ToArray(); - // tbuff = Array.ConvertAll(tbuffInt, Convert.ToByte); - // } - - // var tempData5 = data.Skip(19).ToArray(); - // CreateEncryptionKey(tempData5, tbuff2); - // data = data.Skip(19).Concat(tempData5).ToArray(); - // var tempData6 = data.Skip(dataP).ToArray(); - // EncryptByEncKey(tempData6, len, tbuff2); - // data = data.Skip(dataP).Concat(tempData6).ToArray(); - // return data; - throw new System.NotImplementedException(); - } void EncryptByEnc0Key(byte[] data, int size, byte[] enc0Key) { byte n1, n2, t; diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs index 9ff4bf4c9..42b91ea23 100644 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs +++ b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs @@ -158,13 +158,13 @@ public class Enctype2 : EnctypeBase, IEnctype2Test public const int HeaderSize = 8; public byte[] GameSecreteKey { get; private set; } private Enctype2Params _params; - private List _header = new List(); + private List _buffer = new List(); public Enctype2(string gameSecretKey) { GameSecreteKey = UniSpyEncoding.GetBytes(gameSecretKey); _params = new Enctype2Params(GameSecreteKey); - _header.Add((byte)(_params.Seed.Length ^ 0xEC)); - _header.AddRange(_params.Seed); + _buffer.Add((byte)(_params.Seed.Length ^ 0xEC)); + _buffer.AddRange(_params.Seed); } public override byte[] Encrypt(byte[] data) @@ -182,7 +182,8 @@ public override byte[] Encrypt(byte[] data) } plainText[i] ^= encKeyBytes[modIndex]; } - return plainText; + _buffer.AddRange(plainText); + return _buffer.ToArray(); } diff --git a/src/Servers/ServerBrowser/src/V1/Application/ClientInfo.cs b/src/Servers/ServerBrowser/src/V1/Application/ClientInfo.cs index 781ffd9cf..81ceef0b4 100644 --- a/src/Servers/ServerBrowser/src/V1/Application/ClientInfo.cs +++ b/src/Servers/ServerBrowser/src/V1/Application/ClientInfo.cs @@ -7,6 +7,7 @@ public sealed class ClientInfo : ClientInfoBase public const string Challenge = @"000000"; public const string ChallengeResponse = $@"\basic\\secure\{Challenge}\final\"; public string GameSecretKey { get; set; } + public string ValidateKey { get; set; } public ClientInfo() { } diff --git a/src/Servers/ServerBrowser/src/V1/Contract/Request/GameNameRequest.cs b/src/Servers/ServerBrowser/src/V1/Contract/Request/GameNameRequest.cs index 80e5dead2..718fe692b 100644 --- a/src/Servers/ServerBrowser/src/V1/Contract/Request/GameNameRequest.cs +++ b/src/Servers/ServerBrowser/src/V1/Contract/Request/GameNameRequest.cs @@ -6,9 +6,9 @@ public enum EncryptionType { // do not encrypt Plaintext, - // use encryption method 1 + // use enctype 1 Type1, - // use encryption method 2 + // use enctype 2 Type2 } /// diff --git a/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/GameNameHandler.cs b/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/GameNameHandler.cs index 2fe641c6b..58c2d30d6 100644 --- a/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/GameNameHandler.cs +++ b/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/GameNameHandler.cs @@ -9,7 +9,7 @@ namespace UniSpy.Server.ServerBrowser.V1.Handler.CmdHandler public sealed class GameNameHandler : CmdHandlerBase { private new GameNameRequest _request => (GameNameRequest)base._request; - public GameNameHandler(Client client, RequestBase request) : base(client, request) + public GameNameHandler(Client client, GameNameRequest request) : base(client, request) { } @@ -18,12 +18,7 @@ protected override void RequestCheck() // todo create ICryptographic implementation here based on the enctype base.RequestCheck(); _client.Info.GameSecretKey = DataOperationExtensions.GetSecretKey(_request.GameName); - // we currently do not care about this, assume client is received our challenge - // todo we need to decode the EncKey here, which is sended when client connect to us - // if (_request.EncKey != ClientInfo.EncKey) - // { - // throw new ServerBrowser.Exception("Game encryption key is not valid"); - // } + _client.Info.ValidateKey = _request.ValidateKey; } protected override void DataOperation() { @@ -39,5 +34,12 @@ protected override void DataOperation() break; } } + + protected override void Response() + { + base.Response(); + // reset the crypto object to null + _client.Crypto = null; + } } } \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/ListHandler.cs b/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/ListHandler.cs index 09175c167..180b43e56 100644 --- a/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/ListHandler.cs +++ b/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/ListHandler.cs @@ -11,7 +11,7 @@ public class ListHandler : CmdHandlerBase private new ListResult _result { get => (ListResult)base._result; set => base._result = value; } private new ListResponse _response { get => (ListResponse)base._response; set => base._response = value; } private new ListRequest _request => (ListRequest)base._request; - public ListHandler(Client client, RequestBase request) : base(client, request) + public ListHandler(Client client, ListRequest request) : base(client, request) { _result = new ListResult(); } diff --git a/src/Servers/WebServer/src/Application/ClientInfo.cs b/src/Servers/WebServer/src/Application/ClientInfo.cs index a8152188c..83993030b 100644 --- a/src/Servers/WebServer/src/Application/ClientInfo.cs +++ b/src/Servers/WebServer/src/Application/ClientInfo.cs @@ -15,8 +15,8 @@ public sealed class ClientInfo : ClientInfoBase /// /// RandomNumber /// - public const string PeerKeyPrivate = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; - public const string PeerKeyPublicModulus = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + public const string PeerKeyExponent = "010001"; + public const string PeerKeyModulus = "aefb5064bbd1eb632fa8d57aab1c49366ce0ee3161cbef19f2b7971b63b811790ecbf6a47b34c55f65a0766b40c261c5d69c394cd320842dd2bccba883d30eae8fdba5d03b21b09bfc600dcb30b1b2f3fbe8077630b006dcb54c4254f14891762f72e7bbfe743eb8baf65f9e8c8d11ebe46f6b59e986b4c394cfbc2c8606e29f"; /// /// should be 256 characters /// diff --git a/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResponseBase.cs b/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResponseBase.cs index a996676eb..0c2900b45 100644 --- a/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResponseBase.cs +++ b/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResponseBase.cs @@ -31,8 +31,8 @@ protected void BuildContext() _content.Add("profilenick", _result.ProfileNick); _content.Add("uniquenick", _result.UniqueNick); _content.Add("cdkeyhash", _result.CdKeyHash); - _content.Add("peerkeymodulus", ClientInfo.PeerKeyPublicModulus); - _content.Add("peerkeyexponent", ClientInfo.PeerKeyPrivate); + _content.Add("peerkeymodulus", ClientInfo.PeerKeyModulus); + _content.Add("peerkeyexponent", ClientInfo.PeerKeyExponent); _content.Add("serverdata", ClientInfo.ServerData); using (var md5 = MD5.Create()) @@ -60,7 +60,7 @@ protected void BuildContext() _content.Add("signature", ClientInfo.SignaturePreFix + hashString); } _content.BackToParentElement(); - _content.Add("peerkeyprivate", ClientInfo.PeerKeyPrivate); + _content.Add("peerkeyprivate", ClientInfo.PeerKeyExponent); } } } \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/CreateRecordRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/CreateRecordRequest.cs index 5357a97db..68838a0b0 100644 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/CreateRecordRequest.cs +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Request/CreateRecordRequest.cs @@ -34,17 +34,6 @@ public override void Parse() var valuesNode = _contentElement.Descendants().Where(p => p.Name.LocalName == "ArrayOfRecordValue").First(); var jStr = Newtonsoft.Json.JsonConvert.SerializeXNode(valuesNode).Replace("ns1:", ""); Values = JsonDocument.Parse(jStr); - // foreach (XElement element in valuesNode.Nodes()) - // { - // // TODO move this code to RecordFieldObject as static method which takes XElement as input returns a List - // // first we find the value name by string "name" - // var name = element.Descendants().Where(p => p.Name.LocalName == "name").First().Value; - // // then we get the value type by string "value" - // var type = element.Descendants().Where(p => p.Name.LocalName == "value").First().Descendants().First().Name.LocalName; - // // then we get the actual value by its type we get before - // var value = element.Descendants().Where(p => p.Name.LocalName == type).First().Value; - // Values.Add(new RecordFieldObject(value, name, type)); - // } } } } \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs index 2802aee0a..518d3299e 100644 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs @@ -1,6 +1,3 @@ -using System.Text; -using System.Xml.Linq; -using System.Text.Json; using UniSpy.Server.WebServer.Module.Sake.Abstraction; using UniSpy.Server.WebServer.Module.Sake.Contract.Request; using UniSpy.Server.WebServer.Module.Sake.Contract.Result; @@ -17,20 +14,17 @@ public SearchForRecordResponse(SearchForRecordsRequest request, SearchForRecords public override void Build() { - - var newXele = Newtonsoft.Json.JsonConvert.DeserializeXNode(_result.UserData.RootElement.ToString()).Root; + _content.Add("SearchForRecordsResponse"); + _content.Add("SearchForRecordsResult", "Success"); + var temp = Newtonsoft.Json.JsonConvert.DeserializeXNode(_result.UserData.RootElement.ToString()).Root; // add namespace to root element - newXele.Name = SakeSoapEnvelope.SakeNamespace + newXele.Name.LocalName; + temp.Name = SakeSoapEnvelope.SakeNamespace + temp.Name.LocalName; // Add the namespace to each selected node - foreach (var childNode in newXele.DescendantsAndSelf()) + foreach (var element in temp.DescendantsAndSelf()) { - foreach (var element in childNode.Elements()) - { - element.Name = SakeSoapEnvelope.SakeNamespace + element.Name.LocalName; - } + element.Name = SakeSoapEnvelope.SakeNamespace + element.Name.LocalName; } - _content.Add("values", newXele); - + _content.Add("values", temp); base.Build(); } } diff --git a/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs b/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs index 4595e7905..dd8e763d9 100644 --- a/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs +++ b/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs @@ -1,4 +1,3 @@ -using System.Security.Cryptography.X509Certificates; using UniSpy.Server.WebServer.Abstraction; using UniSpy.Server.WebServer.Module.Sake.Contract.Response; using UniSpy.Server.WebServer.Module.Sake.Contract.Request; diff --git a/src/Servers/WebServer/src/UniSpy_Logo.ico b/src/Servers/WebServer/src/UniSpy_Logo.ico deleted file mode 100644 index 0bdce3c8c30ea6ed8a2922109f11e024ff7238a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51196 zcmagEbyOTd*FM-Iz%UH%4ucb1f@|>L?(PIhkl+j&2rj{c69`FgLU4i;90CLg7Aypp zU_o}?@7-_D?~mQHeQLV;RzH2~-do*0_1rE1009U900_`E6tG4g4xpv~Y#;yz&`|;a zEAIcw48Wue04T7c(FFg+vx5MD#1Q}(82&5&1_OYk69Dk>{a1Fz1OR5&|KR_t&lv*% z61@I{|F2vL1Auei|KR^`84m#RL;n}g060U@{U!b{o&l&t>-m`azj(CF0s#6Y01&6G zsf-J!f}<_PRaH^Y{g1Z){GbqYRc+p6fUfwo)%6w8^(Zbbx{wePqmRpRaL@>1F!(D1 z!Hk6!jE!vugAF4PQdn63MdA?Z{znsmkFKU3aQ5)Y>EP%q=+<;u@G@won{?(T~r6 z_>(f**Y+|eayNwEL3AUG83*mXwI7RK)q+#SV1AcHq6z#61$)m3GKv_7qUqV?nHdX< zRnNcKA=S$U6ElBL=H~wyW~p)3+OKdta(E98!pCi!xH@{aKYRDDkhE83Ne*=DmT&YC zatp5_E9t($dF}N;w)dte7CB1JxaLQTP4YuxLu7Jd?9A+C`p=M7a1dK+Y}Kgl^uRFl z%S~Rr)82VXlWdvm1{G8%qC@!gS-D~J{eWd_pz!SyyT9ehaZtEH8v<{KM1+NJW8MMh z?-)UadvHEs8Xq5wV3(NHsy#SGu1j>FBYRYi;gxU*$kDXek6PjPk`mPC93$H$I@gcD z&N(9NLN5fQ^lUx4wG(J`mql$G)2y3eLT_=AYrYV0wzo88_HRs-rga(h4?fWGlr<)E zcXuDjcbqAhG}UBgIs6J@3{;ZmW|M>mGRj{T-x^}E7QsooO~}+h?T@oR^+HiEM|c2C zJ47wx7u*eH>F)%57fPET3nXL=_(^b!%?Skhc-8*3fZhF`3_meTjA<@kCL9l19&pHJ zgh7Qe+wD4Pjsi4OXjrYQ!`D6DXZ8G^bc>05aF&&2DE`FC$R5n5DGp$NS#V}1YMmxZ zl4{OjzNJ^`UP{Ey^;k-uh)BF%_AE;thu zn!^2<5wMT%x~{?)jcXvc0|>nI^6!jf0ee1wv-}Q>>PL)ZO& zf1w72IURwl{B^W?Pe^|JK&! zT`HTLH!^?8Af5HPUcFtEDQ=nc-ySuv zA2-+7>@W#bKF2B2U&Qbjd(9Wvz2g+{jxzGcPKy<9+A4X_(!k0BM)(f7bPTr}LWqyo zH@8#ASg`VR@E3INt?iXty?4EC7XgMa#>*pQYXm$@8|ONXl7MvgG`ZHsSWaspkNId6 zH$17dxoI$`h~sNbEGB??V;0Cp3QY=HoWi4apE}n}IEyar+B?nKuI383?4yz7y2$+$ z{u%f%+xjaIW!I3pWA55y*o2XxW%kLi;X}}imxnEh43Qshz-q>_A4s_bgiY;g^SygE zWoCDHm!By*b2X7J^~ld(o8(i}Yl+TNm9G1keCUuIC;z$qhItoYC<@NCpJ|=;S`@&c zkDB+)V%@Ed(B#nrd-30jR>D2R5_)FsSf>nNVbd^Hs)$x=-_bKm?NeLovZVa`Gd>}|IYTA?Gwo2GqZ*)69J0_8S>#V{5%r5V9VgQ_=Hg}?Wg4ntxHS>d67^+|19^-J6vPB-Rs}rCp=p&Th^K=lWhM zo@%`M7UPg$3jUwl@Yv5E+~#9ltx4p54pF{)?F{nsArsu>O&^)cy=vmA%P%FDGaoz; zRa1xF95@pjZthTfrd~H~CFsV#W2kG;56F4{LtZ4!vXje)7L)6zR#!s+m zQtN}jzt20n$YCmq6h;C&Pp@BV;tE|ORSaZXjzZGxIepGF=yR|B{;{`XTP4O!*S|() zXIMUvhl*>hbB6!OdC#g8zVDusnyH^91iYBn+jr9(AXfJvWt0{?^XrtjmufMR=-ZzmhaKJJ{G7x zOqdy2XNq>X_#1qnoTIY!RkZdvtNc#(vEa*+GKL-JaRf)D+M$Sy)8Xki4C>lmwJ%N& z*advo4v?-Fb9TCzwit%@YH85=^{f2f)wwEi*QhaGTF;(%ZX%Ag<5J%8e}ufXWdpcW z>lrfgkJ>XRM^BS#11ct(18yT0tTBH!j8F;sxx@tZ6%)DFTkdT2!B>A3p0YQA5I zp3n*^b{|E2Sv8e=H^FQ!E>d{(O5*t6Lom;=i(0aK$uFu-efGV^1ieHGIGMKnNYFjfnohLV?Du}Y%D|xOcxAIAs?`ra-hW*pHgYf2BMrXJ# z-E&%Jv7c8t*Py#oiRY~JAfk$cSdWCTb5gLUJ*#fD)Fwv8G|WA<`d?w;N8-6ADU*|w z$}q*At2EUqm7cH8W^LHkeGOC{bOc}#@DK**Q3@HwuW)C9Z_1)=n9Dx87?v}2;Ew(6 zEvL!z?hlr??%ho;6pvEO2KiQ?mbxe6o4-U7UUfg^{494_fRX*GMb1tS-sWQOyw+KU z#~v@v>}wA%&ieG5sesrxw;9#LU!V`4D~@K&Ny}(rjj~Q)C7z+y^$2{Ix^ifj&r9-p zl>654?#oun@^7M#M>s%TzpX|GZCc*jsa3*vud-U)du=!3RkVL4at-~|q{L_6J99Av zORc#DlI0K{8}JAXJ;I$^NR%qKNu~8V{ct066)-;wGIW_fZeb(ur&cHo3wCYBF(LoHJwQ)+~DOfE}89naUB`k}}gxNBW(hZCd%&goNU; zk%7E44ot2_MXac=c67=9Jzk2gb*tcuhknIKcYl5Dhq{CXm{-8Vn~aDgvCxh}>gN_F z8AZQ3(_-E~;y-`bYw%z`Hd^+z23K39aLN28cQT(~7YkkU?ZE0DOV|32u%?qd62)ex zvZj(%GFbrhNVV_0D+ckkWv?*`t0Qh6Z4FlAQo#|YSdjFbYOO1cj zbej0HNl5-15hp*5u&#LUGVea-jqy!mKQ^)i$>2HCo`qehT|M1+x*wyrcOS5pGIvY) zTgax%;W(?+w6=)ymj3TIZzh4XC2{n$A5c})RH&A>j`;uL7qI{E3%8HP`Tzg{{$KdT zf4K&lL5%zl>F~zFav~sb#l(z4AgbW-Cs1e-4E7WX?V03DI$Qi9paH5RC|g zB{p^}Hg*pJLHa*j3=OEm!C@vMiig1laB+!2AZ*}2)U+Rgz`(#jU-&;d{XDBItj>=wmd0qydA``pH0`G6W(33+n|YrWYnA0tDiR!z#DniEHBk_M3_dZbULr5ZCJ`?7(pN|32RejrSc{odjXwLQVm6}fhH0#0k z;$Qvc!zuQf;Pr*uUxMX?lw{j-{{8*dWtUR|@WVqD$B@|r@w@NWe5a58!pg28&~}^+ zw(BRqR=`UVL|f)xnhzcxKu2nbx4TmU`XBQBEyr zIke~ql8pT~Kd}7PLY;aicaWB?{wqVx_$bZsA`8_X!*gcnW9AoMC|rDl36*zBC^&qY zb=ZwQ2q{ffX;@IJDL#@ul`~c&)l5q*A}4LxRnzf-@sZ+wWKoW@=2gcUCS?r8hN;L*;R^wF8C&^NwAtD88~pUm4%JdT=xUuV3Z zi2DAdbtzY;y_Zjk;eS*;yTZRfaOSGbd&o^2nwwxdH)DF>LL5CU9WYYKKr2Ku^-h#X zDb|H>c1@0Uoo_|81M{-z!zsA|VKDUFjAQM-xMmGb(6H!1S*GA>gx(~vgBllah$epZ zm}B~jCt%m`qcH6y`|+pWdInj1+5b%LY)?+y221c{NcSI+?g^<$Z3GT)WllHCzM8p8 zEB*Zu`=j4W+CmY&Q*QCYm-%i&kE&&n+s70y7bpvq5u3Vo7hl63fAod3Q# zVTyfrO3=;MWIeGvGWvZuBJlxubPGg@)6sZxnTB6pZ`uo$(oSAPlLr^}^Mf;bdVUHc zPG`F-uLdApD`BSw4&JDT(nZz5J^3vlrojXSdna=1)+Qm%jA^q$a#Te;XqL?_W(5iMO2zfGwud)nP%-o7ceZ*45wXGf@87B45Kpbwm^#iX6#o~PJyqMVU~ zPsfD$J~?7oSD{M#0>jz1ZR3HQ6N2(B%46#9vo+kxw?)DgrLUsKK=wr)Wrk3x+S;51 z#7{XfGW~92fvQhdADT#gdEUb~`~}$FYLrB=KFPSbX);yb-Ov0~C62wwrYr6y|F=yx zvu@giI7aUGysJf#@6nUvbBE!vC0!RtQc1P7L$}W4w{(hd9}t4Vc?&yM3)tm-^LTA& zdB`wsgwOeat21|`2%)VMPBsC4zmKgl*a|Y0f~+1euv~p0ZVXIx>>qHF(Pp&#AvZGb z>^vSz@n(`TvgogyHp&8J=mzT2a0VXZSO>(Q=IB2T6BC!?d7|*6`b<)8nyySH%@k@$ zD?yE{lbWEQ)FRgM79(uKhXIsNqw4GXuMg&O#y@XX z96i9t0SJCFo!5s??~!xjHO__Fk6XViQ+;s$)9Q7+d_;ebISSBAOXXtQMdcW~eIPnu ztnLuLc)q$U=s`!vxm=3f z1z+j-qwv2G2KX}U=8YM&6i@gvv)`)zDDe#n3Jnc?{`|QR_SxX|^?|#RP-&~JqhsdO zve=Y?dcZfJDDdn0zI4hfmey8Qzn2n}-uAt>7CVQB3PwiMTFY3H&{>>N&hG&H#D^jU z)H$|_RrO6zyJHQ1H8QhcEmVf$uImnN8bGAo+ui*TZrF)mWV0qiN5-2g4p3pLIS`)2?%RF=lJ`fog zEau+dWBt$|YJ{&N<$jCaVD*-YCI5}}^O2Yl!HB`J(5O4mxsC1D({q>A)|Gp7#mH9g z;3m?c!TnRY*-nsHmbwS4O|b47BGOB!x4{JY6}6en@OR3iEbwsO(N9)?A=pikxHfaL ziOg;6xlCx2iHwu7%1vGHpMS6)n7YZ^eca zEFZfn5oNu zM_nm2k+#f7->Qbv;p5sQIo4~=Rbwguo`KOs}W*Ef1aSTlx#2>6*$~Zcun}OX%>75)o z)ACr);$L{yZ{+Fd=sj5^O=)I8b+k0t{AeF?$nYgB8_>yda`X@t*%^ESXragcpoiP{TD=sl-tKuJo zle&GA8Z8c|2&r&Rso?sMOcFaK1r>oKBv&eCx3V3y#4Rb;K|^>}XU7LLoPmV@v}fzL zKj$6tr_FhL)T!{5J0X}wcR%4WjYs;BzxFp5rgx1lWenX_9DB{?VE3dVO{3M6LJ%#r z*B5)ILH{4L`b=XCHddh!E;j|b?h$iVlPR;3vZB%3BV>fe-evI}WudZv~y-726Fa3HuD-$DwrSS0jHv zvwlq|UfLR(=y)|y2gPmR_iI*iM<)vE7Ge~z+vjDY;tWFer9Nx*9`f?5Dl8le zEv4K9|9+ezG{R(*R6a8)W;V6h`spL#H>Hgrgv&3J7$w=?^ZFqNQkjZRdKbBZoYk-& zF*i>5Jr=SLn+>{?V89%z24fli&UX-L+>{U}t2q;Fc-tnnxM2>`0}16GN#j2!V|R)E zLRsdE81|0%=;Lv~Rh$eM0FDe4-&~+3hR#0@%;P=JuxVL-F!H>Y>^c`+c|;#EU32|O zCX95b5#kn`V|)Tny&AUb`gl-5S2Vz2raL?neQ=Rm?yw(Yg3=G#wT$ke%g47 z+1Zos|7r!R!m9MzODh&bfGagVRs@s_r7P+h`#{M4>Jqv3fi?7Bd^RUT;k}d>TSYn| z))o4KP}c|EJ<%pa1a<-)4GO@A9o`fsN3R-J#@^?(C&hP=<$2-yo=Uv(qSYo{TL7J> z+BO{B$K!_d?+`_@SpjvZm&wWGva2|Dl`0U?2_*tc!g&L$h+3cq@0ef5 zKaATe_5ZWNsgwP!Zc1;+;;gaN@!}ooevn|_^?gVI=NAmp8Y{?bML%Q>q!*79eYzST z!5|wW(tzhJYJ4UW=+5l4W{{a?&VcN@2GHd(#2>B#ZSC{*lgp;exSNfD8!H+dEX0p za?6!f^KX@*RB5K;XM7m<^u_2Pe|2_tR{AB`mq!r=E@P7FUg>s3GTjIE6Vo#1jIZu} zVeiNaEPlPVp!88q$d=XeYJc+ycGdDWFw&c89@=l-k9ZhviRG}wYA;UqmRkGZk;UeP zH$mJ^l9C(RS<_){{C%l55_VyKp?cHvrhD&>eFEuGbZq}EuLJ9*Y|t}_YL$$Wp68C0 z?mV~{zjn2@b#1o1Q*T#I`kQA&d!ljgVKbTkcw5%t{r)j8|KCpMpDQ?Zdqt4oU>>3% zYNb)%4wu0IUOxU%iUC#NK8zON9aX>4EV$KvhhJ1@KNER*SESEEs}4O0 z9_;zxux0!apMPNU+R`yc$`3w{)s$5VhZgz{#-ms*v^H|@N3)g%6g7BSqM`>gMU0pn zb4o7`>6{ENd%$nBs5e`)K0mqQ276NDdR!Ad)XY?NSRB9cu(`%lzf$7BGxCcG+1Iq2vfrw#(k~qk1(*!-0APi9K%EI+?~@BuxuA~dq2gM zFXX(xxjmPClQ?1vJ|8r1{CFvloPK~*E1tDeVv?Y9^BCB|_j`PDw= z{6h5XhgR(G8zS{Z5&QWGW8l|0-pNM3@>=Wn143@A_$89opxBHTU%g(Q3m^Fvb?v34 zbUvA{{J|3CrO*@bzK(CT-B4P9D|p4bXm!I-t!w*vdH1eA<`!MXgv(r#%Ma%DN8;&e zS%h7WH%B-IapLWoes6w(2%vL+ugy3~J6<}QnmXoa7}cS0=ykZakZ$cu)yM1nv4M>J z;B@`$L@`|=WaRpuJC!s-xgG38$A!X? zF@M5pTyCd`7u+DsEMIG0J_ph{NKxS4sd-}7`oTM%B z0TDH_{~yEmUDtDZREfC6C=0aZ^6-0X{>TXre_%;5_REL^ul=RYv5L-p^RQhO%~@bFlCYdE zG_`J#I~rnE2(^j?FppZ`6C{SX%dX{dEg5yr(Gbp%3)kZF+z@Al4if$^Y@Y~a$NrS> zw74?2TyhRFW>qz_Yboh5$t6F%wq#4xfV`C;pXDvqrLjbIYj8}EzJJlv`JON5Aa}Qz zw#w-#&X=^(X-r?$Ev=2QA=d%*K4p~?`PUANyBta&`)5BJE3}gDm{}Khc*8ySPg)kz zgy`c{-m!bTI#vdrX+;z!8i6XX`Uac)69pFtp*DOhldeaKM%jOy@KM*|9_s$@{}H$Z zY_e!u_KuE>CKgH8d{U028iRxROx|J7<*P|#6pPa}Dl;K(6s6x)-JLU~h*3$^Vq)ep z)J>`1!%qJxTaO80)B|K!6IJ6~4c)jOM&?$7YV|5*qakT9qBg3FWiCv^$!x1%Xgf27y`sFM%W`AZouLCSzj!k`${J4zH! zg|6`x%UXqvwWhq{*TiFs(38c`H1WQ9x2@7819o}-bh^q@FN;Tgk&aPz%0j$1Mw%|` zDdp(v&v7I^3*sq?$Yq&IbLlY3gw+$BwUzUB7$2 z_$F&N#76Zc&$Qp((@CnbceEZsJIRg&58i@uRzM7C_PO|tVpm%|I4rV|r7u%5I_xg5~R zOln=#c~wgL+x3GE_I%>bAQt2L6F$0NqD+ne9IU5~$_+pAF*#4B^@ji1$5OmgHBzm? z&uku5%rDRlI8G;iw4nHl_O*W5iPLE|CYiAbg=9kY_QdrgwodUfXpMDJcjJDJ-Dw^m z0L1d}_SM4*lbH=+X1nJxR{`YANA1b@A>+rIs@ZEVL$5<7 zV^1U}zYs0Q`5C`!&q0Ql3^=X1esz>6k^dHJ%gRSlUM7_)`|_!N$I<{!t?lPXzOUDQ zq`YRlJ@A&CWPwxv_XODJ6s@7KuI-vKG=5hGR=&R zG>N|cbZaz=ld0lQ#!DK!z!F!kfc|<*WhHWwbIOhcg)tVcLWhp|{aQ{Tu|6i95`{-$ z;C0i_ZRWnZ|4FH74A|JjvOOruE97mrNOev^R_4 zh@C!H^CCf~$q+te2!T53Q)+4Fs+edeKuYL};rpz)wLZ<+vlfG&zo1_sxk41Ho2i~3 znfL75Xx`iJWA8^cyL+PS1_7O6Xi3CI&_C z8b{Dz<8yi_Fg&!ao_kOI%YHMx%Vcw2tu<#>hesL$E!Nj&(z>Tt%;e%|abF-rXPy85 zzmA|Y@PPS?N=NhpF5tge(1VVb{xpNB_Ya1?6I^6s5NNl0gxJ$EmDCb2%Olf;Ocu5U z>5+@K2Tx@xjuv*jznqB;dZ#snWYX9VUvRKJ%}_8QQq>t1i)0p%jatsxy}9GZ!G1>} zYhBl1BRwHF5izoyv$(w5-1gza2N+{%42H9YJ%3Sr-2U(=MhDU(oj=3^wE-$1d~VS# zjDos*a@wGTi%3&#Weo@vUE@xlAs5}$J=;`%Ld=c&o`ZOugBnQTi>PPJa0Rv4Z#9#F z*8-nv@~Xf8VxNKn;wNb{%)|*vvT^w6!uhX#HTOU^?ndo&O{XoWhJm2}cHO%P1h}-&Xw1!(Lm@ zuh6o;Ot1de_TZf_Lr1g_(wAL|my&wfj`5pnn?G?hy{6$~9vQo@T}Gu}c7=C=Uq1aU zJKe*!qn%$a;!blqQ5#5;rL#r{rvDW(bz)Ya@F|LFSL~lc?tIY75uI0lkO+0dHH!aL z2cl({TXvQ|`R%ym<%^Nlli*J^c18YWJEXFHfh(m}ulw&0943PJ^*WP%9Q#CEtBEl_ z;xu(`-lYza|H&q2Ej@vt6!rNsuk*JueNCji%OU>HizhJ4x-fvM2 zDW@stK)uAgmld|-GzOkYv%e6{*_jZQ2T_1_*o7aYQis@BANfi{E}Si10po0(c_rUV z7Sy;anX3p&jI&hCCJRnpl%$fU=y6@&HH(XF~py=OafmG$Ue9o-2&En^6 zj%`ewYpQ>%4a5wKZpXa*aq)`WIV2GiuKQdAtWT(OpkGd=spsk_rqhDs;{!f;??*yL zph)H-^$<7`IQaMTU8ka{m4%8BCP!-pMeVoPKc~RLv(ZJ%A{o;8vBzj@F?f&mT#CQ%X({#>v zOF@sKR_S`Wra79uCcmZHs4Sq3pL%(c!+$Oc?3L+^GIR|&8T)*Ai*T4f6>=vlxiF8s>b=W*_D{pJ?jX#v`tO+Us5c3L#zoWkz{lW{U+5sQ zrn>)26r(8Orc`AIoQ6rZRXyuJ;*ZYVq%j0`q|54*q=|12C~Zf3zsV(pHxW*kqN48Bs)u7Au!@IM>-5+jf;7&#*+?qb1Lz9M)WCslUEgs$tBQ278Yzhh9m%8tr-P=Rj4y)@DFK56FP%b5)E z8dN1wFX9CiH;J_opHVY~w^uxWjr~)o1LuGAW>|^!rW|6J*G*GwWZz z%{6nAt2&C2m$>i)t8aW;QZL(0h9WL_ll${sj*DpxJ8RX}Ca*VZB24Ew$yx|9FDaG= zXR$z?kp#u4tD*1L;jsMqXT68UP7EIkjOsg;Sme#Nw1mA5HuO>O&zE;Vu~!DJQxi`-HMb`+QoN`mrkvb_1MvS}Bs zAS}1!O_sv?nuK_ET<1@HxvAB6-f5r7?2tq3-c4KNzrc~!i;aMQRSQ=W&qpk-+E11~ zraxv@sIh*v<1Wemg26dRARK+a%1U{UnQ`sC_0DKzbvBE^b}3OIJ?q2a+Pf(?oj4*} z2>FrC1lJ~a>-aS@^}D&|^T#kAmfXtQqIZq?zw+aBOJWS|F5EAbK~BO4!-@i@%9<=x zYow+%x6dqBc+X=0M0{p55tZG0JQ4I0^!-`6p^55`kLf% z(SY*9sY?DdFJ90ls#y>2cR@DY-v|SxA zrV@(Wu_-C_GEBt_1FGMErt;+eb2b*7`iN`KFUouH8N-9xNGpe027M#rt_nNAW>c(t z#>&~m>?|Rl*88lyI$Ks>tGK8uNgL4YK|gqpp?SZN%y9~Ft~}pw;G~$H9sfB~lvL@{ zsVf3?S5|q^(o^rjP1E1^DMt;huJ&7fbu6MQP(vkGw_nmJRTn4nGt)BqZiiMke0<4} z|HzT);!Wn?cSVURBIp-PA9DM|cl1%jXC`|eV^JhM39f~kNi(Lqf;b^uHlkcjgPSckQX8nxby2g>A;-k% zKpTUd$>CeBm|+mdFK>wtVj*{aYcOn~B+m{U?=4Ks-QaSXvyBJB*1R(U?ff_XMXf?m z??*S#L;s6Umurs^MdlKsBc@|B`MC^IjQ%cP@>fxZp`Gys6jz^D|Ig2&0~M?nmcTO< zIeU`;uMc7iL6+(p`h^C@KmrKy89lx-@OXk{om2z_W$KcX#AvQ+1CD&W-`J{TzuaaC z5_Bsn5L4x_VYxq@+-wI%bzD_#$VmHPlvDw-U%}<$_=`zJ!!96p#@Fh)u5!%hupOEJ zg(+>|g*m{=uf@gWTEu5%K3o8kzCZ*i=NPU8zbjAB7&TDAq2WQGaDzLnXP&p5ru$xy>4ph~*}%S|jG zdXrC4p9@GmwxY?H8ql{AaPaTo2TFs z!qfvuVj18vg3e#W7$o?B8_@Yz&-10&+DsU)VCh>}!JeVNBke(Gt$$+-D(>4 z+nj)?T9k4|50hH4K2%VVZ-zphSdIfFPq@kSlQUZthN3{x0a6Oi7`1OHspD#MU{XF2 zjl^$Kb<@{di-AqxWA@;TDiV;0J?0`TaERfKh(l&`Mhqh>$>sP_NJ0`&EkP2&G)A~g z*%9WXMwBLi6<3=MlZdS?gpJ1~h7ydL52$IVrna~!j$;FYSp?ovs2NrujxFZ((pr#O zyds|_bb~l~7En{v&L@Zoj*3X(M4&7Ii@1RmP#AM_A?#nQ7YeqlkiuAy9IHlh21QOI ze7HpO}WI4n{V z*$5F;ieg35V0vwGQQxZ8#swgV4g8VH>9+ncDc<0s?7f_pK;|6^E|?k7gd(6HTciek zgRBH=XA(qD_pui%K%-%L_=T@2Zf$O5C!{BTEX7WOi=ts;3eGGM>6>nXIxK?8mkH>p zYMxEG5*NV6gt^8?h=-r+wuoepIcg#fyTh~M`c&>V{ z#^rFkAlRuGni64JWa|3G8B}{;4y}~$Y+V#Iu>nGU;>2PSMPMM#i{$?H+<+R`WKjY~ z#|=h=Gp$?GcnRUPvxZID(M=mMn_oS3?*Ibhyl6VfJE?2Lezy`o%tNeTNo!v9W=Seb!BVQduisJDi{+=1@I6`UV#L8EOGMVa3zU~>EoJwkzHRQzqKS~ zju*q{``2)u$PoUDJ7QR7DA8rDVnebZEhjI^Ugr}5j@4=~SySS6!+Z@s?{eE}{(;Q|VDq~p!>KYr$qg`; zVi6iln(z=hiHorSapeWtfV?JL(1_2Q9aw-7x}0u2^m2DIvPI`SGJcPKyAmQf9mDPm z1q;PCKXNdxYGqVE`lQO{Ldn`|<^w zkS~;h?5Coy8oFW-xU8IP3?!&$OoSK|aM>Xjo9!2lcol#NCZW#Ba2hHqDqogTo?iB% z8z3c6JiGh*FvAMM#{;09()iAVfhg4on5S!+l_EFgM${VWlNcEpA zbP%;q07!@wsbi`B21==R;fGqGptZZx|KgJrYJ z1^B-y)r2)|EPqsfzcnp+-!zK*M!`3(xLufP{s8k9nvTu?Lq5tvA*_v8hS`oo4Y60j z7IR}qG{_zpwXuvG8%z-Uj(l-k#J}{Cx*;@vr=xKpTgrnfPHyd`KXz(Czhp(~^(}@e z?|Wpf@qpyJ{7NNU8=kJJb zvKnvLXNoJaJxqZaIvODj(Y^%Z?u+PKZXEy@neB+6B91oyw#H`3ElYLoQ$x5%;^IOq zw&*%q-4(d9;bw7J$33v0fWd&t@z0VANY5GMHguguX9TL83UuW4DOotngQ4u zW$Q;C13|I1Z($NS&qY)Bc$j?^6!;$VmwA$WjZ)a&y6u~kCgv20lQD2UB6E?p9ep(M z<*=~ew4Ln&O<)WjC)~W$*VM1o-|HuL4H*&IChb=c)+v#HL)kT@%75&=-oNH_e}Gw= z1#1KCBhfeQ)NsnC4od{~fOS3W9YB%wd*L8zN7~Mlm7ZU=-KebX>ETpzl5N2Hdx9f+(l)A@x#S>JQYVIz`Sqs*)BDYnQM94@7?{$fb zE%GZuP@}Nps1p0JweDn3^_0j~RHT1_Prj&JdeZFR$RrPT3Fbb1T}p-RKbd{uWd3qY zxBNmWZaZJNtLLwK^g|ivs{h2QBd@J$NeF~bt9Y6=_44qj15L3BMwRDe# z!%lhN=7`j}l3FtcQqmu`F*~(sURgO*V1;hIHNx@k<{g;A3Zt6NB8Xq<`-i2(xJ@!c zAY6Ira22fz>$KBg(#e3XY7Nz<(52cXi;us>Z#nc-ZaaZ+EF!*h zO+U}txYc6R8CLtWO2!CRw5H&*e8glZ$Usy<5TBCpBv8?m-Y38CPL%G!!|b(uoL=T_ ztwmW-4SybItlC}vRcJ%yUr>9SZw^H!oy{EeZCE+0JvXV(sF2Z&?KZ{H#vLU_D)bk_qBfD>Y{S&I z_+sOip(M#+O$KFxvyY)^5A7CZPVxM_fT(4!xtRibN}2MOo!Oqiv3W--NK5kmuT|fM z%)SC&fGhYBY7(nY#DAq#Gxe^2^xAinICY!xDx;@uZf}dut^*Nf=0{?#qc{I(cG|jL4|ZLo?YJWXt-MS#WW_nB zTZgmBa}_SgK>CA{bST&s=*Y$hDDnXNvK`uxDn8X)tOIY}< zZ2u&WKgca-kVUBS+NAvnYQ)vYbMO_ynELa?oeWh8pEf|5lM(kU;f`Gs?ZBU1_`3z;xCB zjYCIUtTWN{P%&lN9dpUmW2rx)s}CI;KVK@83G)y=oiDzBKO_pGV5oC2Fd*Era5TxX zax*!iooI)zSIG_i{`Ksv{ojKK-NWHb^*?+3HG9)idi4Q#_povG_p*5Iy=BkZkIn2w z1)%=*AqM-aSWp7!r$_4BiPpo171HDfpLqUPk&n4d?B4*uJr*a%siAYoI)-VNOXpmc zsq80>GU57T$SY>ja4TU>R-%_ENS@@;`YW29LpF<-8uazN)WL?$SU5%eqdTLzf0jlv zOeHNiG6)#yWuw^JWP*oM_yJqwZ;VL~#N$A^IscVHtCM!E#@O~W!j*QLG=r_LT74k< zF8-iP>a)JcByUYp!U-ez4tw-_wc-evq5?YuoEA$g25apLtW|N6GlIL|6}bgDb%I=8 zEM(0D2#RcaaX#z{QkjHXCj#7q3RU!e7Lxm|>})+gj-xv8nH-(51R`Vx5hJhq^4mA( zuZ#4c2S`yz#AE76BT4+F1iAUucZN$iYYtZqObxl_1OUYP5o#9jpzyEPfKCL`75tS{ z0MzZ3N_Ddb66!|T+2l>+36RAM_RJy3-vn}bu*HP0(uB{ngswscx}~m{pxsngm+Eur zIOz4_{{TslHRH-!+Q5AdK5?|FO}E`~1?WRe5D}#4 zDbxb_0~;v7!ntQJ4z|wi-Z$z|6(#==tNrFGOyZm^Kl47E@wP}Qbo6qxE3lC@@KSGd zUAbrE_$YNnu!GNRhy?_o#sE5i4@*{~Uh@y&ta+{u027gz+Uik6ND{WBv?y79`~rCd zQH*&i=GUT_0_V-KIGf-PF!7VzAv_um<9JVuV^I`tY1sR7#(8KFd3Pa*3(JdJ>G>)O zr+v{5xBD290tZN#f8n1Jhh?$!6$$W1SE|G+24L36pX2^%GE0yf=?<2vBbi69YEb|* zwSFB@=xXEd{e|7GJXmgOAK`Bn06@0~(1pc^BWuUW`)p0*wKTvT85O=(@noDyo-Q~w z7IE`(n1P={a*SPrOOFh?7DWRq+vUSN02VbV-Ze8vJiA#}Uce3ACs!50HzP*LAYX0Y zLpDo*FTg#1w=Up&>}H1f8i<9&#uwrETXe|aS}|}TANWr$yUzj?*kgD}%Hs^;kO2B= zq51mzD>IFo)bXp$w{fLRHcnjp_%8v`I3=m8BLsG_FK|oR?YZ`V5L4uV18YG+VSu$X z%0cB!iqPZNZ)!9DQM~TL`{=TWK1pE<^2TvcEpCm5doNR4vL^5lD`Z!cSazDr zkq0&g{=}-u_0^rnH%KF72<*kCtqmCL^`EZ=-O9uXjcgeyWf+Z^|71+7SNqCA3_#Wd zMobM<^`E1YoG#gq1Bg+u+bLOQYjj!<3le5_)t2gbb}wJjewJY>8XoY6%+9auPb-Hn zc-+W!i<)bXiab_0UZ{Ls#TxgPU|kIM^oW^m4rBEY*fY4L}-U z?LHZUu!gwC=+3M-n>4pCf0miTSD^FDFQg?B%jBfi4`qxbQ^qJ1q73(c z?ZaK_QBP0L`@P@q`d{C?b7b?)Q1@`x16&1Dr$0#_HS$1fY7Xc-<* zKc8SDY=X(>Hjhj)pX{>aay^OZg*k4jk*Ozhc^;(DJ8{jQYjM?=j}6vie2Y^atq|1c zNcY+?=2ZCE_Rw4*=FO7Wgj&CH_zk%Kil&@@*(@+c`+mNe+y1$3g0jI0>g~rRZI)AJ z4qUv%iro`%(%`3Zc|Ug1y{!)GyBH+iF~`+zG)U?88}(#tRM~Zmap3x9a(Q>h@y~|F zM-yzado4=893~$$#g>a!OBzCLtPbhdudDEdZ<f@E-&TYt>u&IbP1? zXPDIH-)ul_gy|ASk74C4?F(h-UGEHNnoyl&Dr&cWdw_6~GKa6_l2HkV`j(?vwWZOC zk9Nky0|pOw=;^1EH@EdSE4P`mOK_-4B zGV!wGE4UwADw$+LH8@VysY5Hv zjD@vwZI&*3cYU`b!}t?h=dSSMHMi{uZiMH@J4@DtvuU~Q_PeV{*vD_UO8#hwenP~; zB&%rsQp<%)V*(;$`MSHA-ma~jzlYz>ns!`Za7%#Z#wYSE;;syYY9p7UD|IOef|+CCfc$m#OKd%WCb^q<=4G(MTIOQnRMq$s$Ph zSzq^+d2_PPzVjOJyViO6*4Q-TGUPYMvfR%$j_xT`FK!o89iHo6(Y{dr;OQ@r*Ks8tu4D zl{z*_nR8ydxj+C@p=)kcyv3$%-_ zWe*G;E}0vm6t2N~UL*X3|K?n0pTMqNBa=5UPF2R1@q@m#r9=_we)g>zENhNSesG=K z?bDW-^MTDQ8BS$|K_eMrDGLU5(u=H?G1`naxDlChZnwtF594ZfmBx64lyxaxR}hR8 z_0RTXm{egH-z8)Vu;%pX2fD`a=Mi=8!n(8v+(Htr8^&tfzr?oxl&ye_L{Ox-&X%IJ zhv%kvyc>&XYN7>AvLa1q;L_7Hu9V&VU0uy9weCNZaCiDZd6^XyKQ#0}I{1qw2A{AVp zEK1WplKT|tUUGY|<}I_JtL$W@Q=b0@>9oF3>Elgi(kGf;CAU-XNQ^!tH^v62D6+SV zs$)bd{`%Ii7nn0eOOJ5Sh&9t_(EEge^U(}K-NUS|h9OsvTsJ&Xpgon~ zJekj{RVCkt>4pu4o;tNIXgEX4(s{TWZvWfKd!CPvUyP7Ux~iWoSee!^SgYKT#t`!% z-QU9;zF5_`x0Jc^c!Z%3rD~F(dvJ-8Bvea%t9j~mFPExlzwKQ*W3^o05 zAg@)rVS?SYdCyQ?=DHTzob0E~N=mObZen+~`m_#HA$L(aqAl)fBe;{?M^+=hG;w#b zx8n=2onZBRJt8m29X!I4nc1bxWYW(Nb{f+lq{*ogb*rzZNm}isVhlD*?^^jepjYLK zLHW+ia(}Z^IUk&Tnno)I7py-Mbf}MW^hoz=Va52xR4!4IvY0V-Ebsl&I1|G5dB)v0 za<72#9OFDstK}^&(fv;tdX(C_60keE*ZD*w1D?KyRoOv%-EisF`yF$|bwa#^R`EIn z^gBPwCib6I5IzhN;-w^&{v(y$Fn*L(mK^}8``Liv!U<+)mE2&3O}pvB?yub0SN4C-EO@t%ecyhTx69S}nV%dQU5FV` zsJ3b^9dET47{6Wfw0V-3e!TUR^f*-$jjF~7<>=PS#;aYqj5Y=@aPA)1J8(d%Ml^8Y zx$OL9XR?Pm=9P7gwNMY(KjnL7eKFgh)poS^bHuw3tid3o9Plq(FM8|orrvN`-?XqB zjtW(=l?*L}xaPbzzA>FepN4~N2vypcDANmPMO9+!+)kxc4NK;82cHU(J)row?;32P zH1dLX`L)~V2RpVCY(Xme{LeKPf+&QN| z+FIp>738v?wHsY6BeTKnNwbaPyh+OjdexdQ;?HHuq{Sq;bCvG~T2%>#vjsgIF0m~e z^j66otB%hdgmKQwQsiOU$MA}c-wR%LJ(%)HvDwFOWaXU;n@?BU8innPkUaq8rAI0- zWzR*~owq%e+5_(ldbf;RC(G{Ay_Cng)`DMj^3j^bdTL8$u5gtr+rWhZXb??3^w(J% zYs+0|6Qs^!zSQWP-KNY`lSXt8^Tp;9J_ot4$xd9|yTbeCT8RuGt6A-i&vQDSiL3>lMQ|he$PQeW!3z~Q99!ttjJFpOJj~Vt-GjkmG!1yl9g-E&B8Lh zo&d+Pb(RuI=JEQMw)E7!y;icOJbfg4Y>em#_aoKf)D|a(4B7^VRjPr@9`dY=mDb@m1Wz4nprfI(XR7q4 z(Rp^~Zq3s*o%O6`tc{cUrhZ|1hZrdog73-dW~yy_W&P%!@zo)(SXc_o z1?ekJ8YoY8Tr?Uv9XPZj$B@ZRvMwq{-IL5cP4Ly-vb#_@O7H%8 zIe+$TGJeVK@;vNspD$&ta0PMf!-nKjV_?8?cducy%xT9xHRUunZ;W)vxT*FmQ_U*1 zb*@^p#IUtqNKh))4$%-X^A`h&>Tsa`x)Qf{W(Dv1 z318xO&6%&p;{Wha!utkrSEMSxyoxJJV5z@br9frAEaQ!%%cQ3Aq!$PPyq%L*^~9v$ zSj$rV(OM&u3mpj4L1*ON}&uECG)a%=$gB$2ZDR(syENhRg(SSBq>C-VqQn{Q62)$Ehyn(i4Yx28XQQeW31U zR2sN_e7qOL+sw?+fLqpKVM!RK#>c2C7@vM7@^HL9tq(zgftqX!uihS>!LmzWY?<1KQ1sZ@X5hK6&NPI|nlA~F zb?_MCUTUGc^2n)`gY(Q=y=^h7E$1SSU(ojh(;dI}OFGYQsr6VV3dT&DL1m@sPSLe> zU7sc55f_GNgEOy6g=Y`+PUda5p4oTi0-6YS(3W2Z?HoUejbCCMO zsKJTq<55I=^DwATy22&LpU%v`x7VL*>xF)fWn9)FXJGF~^FPpbQ``Aqg3M8^ojaWE zHIw^E` zk`9#dExgOum2uWhOdK7MHi=It@DRbV z!(}06$31Z3B%yo7V!I56?sqcxGHI8+vM#%OBIE)X4NdsCUQ(gZuukf28axYH6S#!9 zX&vNBHEI-YLEI($G3dxdplThb>YJPkPOxr97F5^g7#V19*{r?Au$xwmKwe1}qCH7N zRw2TCY`ms3Q_7iJt4q)9)(TB}zNMWxdOn$BUiEd{Y7b(s&rzeOFuTcRAyXa{QWGFs zw)vr3&jYvp9&VB!-Ane3?l4wn@9vqu?|?$*OY?Bi9nSf;0&*!&1RZN6PONqzid}Kz zURzs*5&Vmk`^;dYfI3a8%BtO8hufr?V0P=$X%C%FyU~3m7aA^nang9TT;o=Q=4FLT z+{>-YF53i-eB5ZJ@J_Mwu*=+&%R@ymww&RzrGvi9U8427u9yXG-B7csSh?U+_Tzxw zRr;r??VJ0THBVYB`t_lUPIgJa94vo7P?L&H{osLZ&z zp}}cwsZ0c!SD9J1ZxB(NvNF+}i`}kPo4XqpdRm>omNVBCmqDhwMwzN>m#yfIrRrC! za(FpUfaz;H7({lm_}bBHwudWic^4C{!FQzbV)>_MdTOU6I435Tb#~MTBylVL7ywxV$7fWws#&8(x6P8f8I}mF~`i; zu)?|wEzn~M{h2S~n~u8gJyjt%SL?_=D#9z#626$4Go?w+JEeS5Uidp?=DHCVe4Ovv zGBVd-XKhD{mSE6B!CPlzD=o?FD*Tg<4Sm{u*xv1ln4Eua7Pps*VXiXI-8O~yvaU`! z{_I(NmnM2cqtiRBmoPHiC+u`RXwEyP>*yE+X2UAG*2^=s#>#9CRt{m7sv3hs%8`4k zg*hvTVD@?9UM>4W?n@uax6EnL4~gsAH&Gy~;h64KUqwB{u-C79AEhfMDJ=Q8CVuf` zL`H_R^rjxs4O_gGtJU>SlRYvF7qvdxICgtug5!D2C~~-}cTum9h(?lxysR+IOASgd z9jV(9HK1=frKM?T)RI_wA!0s87kQ%d7TnCe?~i>?jNH51HMXvNOOc?5hFkZJx|MtP z_2`70Ii@8@RM>Do{H~m$+#WRpIo+EPHIAI!O4enkRf36IgtD*ARilWt6kj?4?7Q`V z^;qs_4YM_M@4PG55RR(o)o(Q0#y4NdF22ra-wr>}8TCudbt106IM?lgg+x-Ar_zZ$ z|IA$xMIxV<6Wo|Pq+WcaDv5r?i zo6=D=bE4}UaZDV0r&#}KXA`IU$&g!)8)K6%c#10MQMe|+mfFwymcn&MSpr;%HWZHb zP72i1_%d$*^7Q$!b~Ufrr{k^>f>`E@+7=Q4w6!T|mn#%TIFWR+qOd(bmwFlP) zj+k@k)o;BqoXnvkrT)0=Hp_L!7aKk;l;^8t)7vduIU4LWcP-Ccw~Z!&O6OB$`NX*j zm09lQDn>+F20qXT{={|U`0^vRJcFsLxLB_)UnF>txqe6f>%%^^iam#`br-ibv0Ne6 z?Yca;^9AjYjjTl*MW^g=S;E=mMZ!xp+~&$xz}2|is{0~y3{Kfm4nA%>Id<{#V3eqj z1trEKxU0d_(J@P(rSzt{mI=L@>jTs1XfOnxSYIYkf8~)wBcIC(UgAd;GkO~Sm;wUa zNxoCCXj~mG%FeQZrmDodCfJqiz0+iFlj@j{L-bfeOui{GDc3)feCGvYR*Vp~Bh2BV z>;ZPkrQzc9PaC;1Doh%w)u#7JXR%#6b?~8VI@O_hUv6h&RKRmO4V3%ysXA@V{JA7D zR;^yWgpH6v7dwdZn+6TAB50V05AJhdZ2vx}XeDW%IR8C!yqx|C2HJ1y5jLcW>&1rFP;~rHl{QSI}yXye8=RTFj z-g1AI*mYq}5ku5s6SIxj~+;O#|1fLVccTDfi3ByHj6CvgH{w0!#NDvPVK zpZO2V`qUbBei00LP$3QVsytRB(w94R)?FS9Pv`E6=H*woK0=o&c7@Ql%}gQr(z}J0 zi=)hWxh%ZsXlMeLRiqX;^pEXTo;**zYoszPf|4%>i!b$Wyy#W$-!I1DZ!)&yjC!`3 zwl+58!JX0U;4)NweEd?cSG|35KVPj;*4i*fKiLC3!3meGtOfLj4?kyNW?pjgmgnc* z#><0AU?I@FLN>o)6GyUKjCz=5x@@nZXZ_B6qb>Q$9IehDy}h?o5YO~ZezN>9>eqK9 zz{$yVvNh03qiK9W%ljo3W4vYx`&veqb-ukN?jqbZ5A4F0HVRTtzDsK!4{tl!t5oi^ zPyB_;#zHs0!FjHtKA|hT>VYHl6_>TmeY%_sYy{SPI^MKd)xD57s8aZOc()n7SXn?bh85Esoj5pyT7se721jcWS;Ww>aez>1n8aNR_eq zP}%9>z!7=EuC|+&FE!4GI%Il#Ti@Mm-p>bCIL(&|voneZlM7u(t#%#QV)lj3WVuFK z?TMya6R=`qYVzztPMixp?d^Sysd;Z%sIgbR%Fxcj)RbLe2f}tw%K2Q~u<~-Y(c@mn z^06Z{Oz{50PG`1PGG}CjDZo-Cl;Lq7yGnC2k!T}tQD-%8M0btOv0(@IA(h^=D?Cg) z6i++q3+U}N-K-~0A&}~pcRG*{uvP7ftYQie}p$y^Czn&s)zSF|7a z7iziBl?Y%v*Um`g23Nf<6}DMJpSQm=R^UvKp0k3ip{{vx)yVZ;l}gjHb#!G8pEdSw zTcEk%>I7IegaYXp>IG^0b;q)iVLHjWJNwCW-^Eq?Pw2vVR4=4yb;vE#5L5aTj*I33 z^cAV){=+VZ1EeR9xey~hxpC9Ix|aiI>n~WBQu)pVxH?|v{xL+&RzQ#D>AqFkY%PQj zp#|2bo;&nU!c?#@r1xFh=n-(zQc#M*#8pV}S$TS2TVu`>alJ6NNO|T0`g7)st0e3W zM|G_n0v%ULIZu}uwbwr07Y>;T!ci31fE)wd1@^L)@2r-t+Nyospu~u2Bkk%X?#n9T zUJM4NHrw`%8y9}=qzz12T)!uynEySL$#rkgSPs{@ecnYD?A;O~l2St>tmmrFcqitQ zzu5qHD35z5KSm!PZ@6-zN&T51&)b?_22m+W6O8&W1H6m!uq)~STm4?bK@PnQ3-r0K zUhv=ObGH0MQ-2?<_AcGagAH)YaCE}AKT#x#5*ujaze1kr@u&<^)(;8u6we$5+vPA;KpchZq%WSocJbAf~3!r zATi;D!>;#K^V^Q8bf%5i$XPfQGI|^i6XIwzk$Pdav9hf_ROUH}qQar> zQAZn%Cqapn2n#;NJ@H=tnMs*qE5d>tsx3--FTkiU3)Gss^H1)sP&jYS6W=(<`z5OF z+#P6rbQh(x*{t1JE&B0e%$pBH=L%ci zL+ty!s28LMS;y;ORI!AWH&Q=sYjAGfYnUyqvxO;fgTPI#?4r|SJY5Z{#{!JGW$)EL z3%Q`Gwhb(K6mkZy?>~7=_+zD}PUCttmH2VFi=GSKvcKJqEVCSyCzEUn-VLu&TBz&Y zu3=V0{?6A>`qV`mV(IblZLic;ZGWR|E<-i&M)1x~FUR&eI5G!0H2Qt1Qg##LXg`O| z+q8BInEb>%ejVSWdfL&MSI<_QBGrrV+S%taSa}@=1FZL2Mk8@|Z`3}gvIFEJMIK~S zCLc1qKWt;>X3@#ydx68r;ES!1`pBgUd(P-Xg@BWvB3Iei{q7*J!!=Brop2yDQWPc3ylV5_-Ob|ht4iVgZs3il1q)p zN-qjoU)wG?~Mw7Oo+Z;Cm~3b?+s=1E(C8 zRxUb6v$l$ip?if@*%wQrk#cC}+HJrtBzkud2fQ@1XcM-T#kYLZ{!gE?7>$fq$@93f zCfcOB5lrE&!qH2;laoqy6Q%TK$;s~a=jdFz;l-JG@@l>s9bP(114C=_+4oJ{xFsW} zSHhTMqhOI68tqgECRn4qW=GG{xf};*GI_Opjc_`RHtG0B3u6)=HI%xGp4U4cdI4UQ z$^e>kvc{Ho1;1UadYGK`Xs+0NS7ETpW@hygc#vKdADvkn-(;XIFJP4(?`~nX`*T9u zNuq7tsg_t|?cBB}mrv;~Cd@=89wcr^dA-@ZAtj?SzKKs%go7hWkjt6nic44d<-v%9 z!J)cg(nCFBSK#GK0+|V6o0Q($sN_=(@Sf1fXN}Egk{4dHV%gyW%QdQb#dE$D8q4p< zKYDPD4)8CL0kv5CfknN^@b2MRc;}GGxj$m{a*f(X_A4%Jiwjd@c7(}UosGXUux7nL zl7=swWH*4d*~k`|@)|YaI-Uz)!30-JgS%lPXE|l}PJWKm)~Yz#0ZW4-XIbY1u(o)` zYM32*)ym>}*vj z`5UrRQ3dZ*`Q!Tp3O~OviypJ`({_O}%b^TYJ{(~7ohlYFY*r3*bfz1;zP|Z%H(}RB z&-$d?!QCfI2AvaLIS{KPm`@BZy99UI2LHz*fn;9zx3cec$bQkg>r~Fid}kJPy9`tJF4d$5Ay0cR2;oS^_F?x zt3zd9=-^%Eq+XT8+Cc?#vsGT!@Y3PD9I8i3I?oJ4YR-d#vNEzJRYYT0J4i$$W|uip zp?v2cSXg8}84P^g?zmzsx8+VEtfP5?46$Nzy8bc4@#l4R$*jB2>N^>DR_zAkO8zFv z_ycEqCp$#SoxZS^I~|OXrT&u3t7Uq)JvJ&Z&sDpjxBfcpPm9y~at2nXfOo}^Z;}r)(K6!dk1$fW`rVFsk==>Jnom7fk}wsotR-My^uI{$3y8w$(FlDqQQza zOTB>Gnswii+|Q@Bp*BL{URE+v?7@!k;jMKqKdmTr>uep)&P;Fi?+Q8`5Y*HUf)>lZ zKBl)cT_g1y#4#l@f;k~AAksd(b|Jiex8k0p0|B#q*tPQ0io(y6FkB}u7THoC7O@^x zS_RNLD#4Z#OP^psSkv#sns3|qkw_lC$6>YpgRP<%Jfwd+7Kbo=Xa!!{M}OrCYA|?Q z@!!AxOioT>@XxQ9n_E8C)YOelOiWO z#R|G(O2^j99b+U|tVq3`YQadbEP3$Y(NRXaWsl0rO4=$3mg(?lFk)jTrcF|7LcS5u0?uuu(n?YoUf4clfJT=q%<6uJs~@JvobE0}S8tP?+RU zna@EOob#u~NDVJKHBK04vSxco*GFS0*Z{VH@Ni_J zTzI6bQ4!wEGk6S7XvGN1Q^0uI@O?&_m!w?ePxvgIib_w_A-%JFDlRo$%0t;0PZ*2G zz2RK?v_{AhDGq@6@au=I7)JoaJzHstAQj^z-y@r-7>K?|s^5_&! z$Ld=lO1RZL6%`cpy3YvR$D^Wn+60t;xAlyuoghkWINV^AGbc+C1?3#u>x z>V`+l{t1d<7`N6@3wSJ#1>;eS#%iho3z_M-=~P?_utg93^cam##|e>B`G!;37pediE7 za=B-T#I7zPTIVw0Ou&7m2h+OL>zsvUJ~lWwj2RddVo#pD#Md$cT)H#^)6=_* zv9M@h3lm_pS6!yNDe@JIOgL%_jAVSuS^P)QL8p$8wf zZK&@UHICkNHFf}FvBm=WPHkb1suD&Z7`48Lua><^--W{jtG;f_qt;iaDx%Iex1|p4 z>+7?mxZKy*bC|HkNDaYLKm`<#+~;u+{YF3xZ-5%6-3p~lJr|%$Ob8wx>dSrI=^610 zOVc5sh9}`aCwfz#BFqH^7o}&Vd`+j`Kb1I@lRVumjA`|ZWC#`jBJdEbzfXoW2Tj9x zPA4xa3wSym#PM{1VOw-NC|eGmvGkjeC!vSnsWtXWqz|H0{7Em{Vo7TG1St{uiU@e> z;hdTNfs_hO%gL#xr)JztMfC-LOQ-0~m_}vM?R2$o=`$9KpdhJH$Rg5RiLaOFCZVu5Xgrmn1g(fGhC5R_$E{3Ao30I7UTomI7Xg>$`f6wTc+ye z8%32*Vyfe){z*dlpey*}4CFZj05?8Pa8(6=jD#07%s_f6Iy663ANWuX zib03I`K0`>{cHJF9`7%y-k=Pt$W-7CP;kUTnf?tPFyi*bGgUoXHI7#!h?gtj1m6=3xA z;n=%(10bU=hOh@VFffQQF)aos4{}-@E~lcpX=rW%@5;w|dintFF1Tx0SXdc0H1r9p ztE+=K7{`i>Ut^=AqaeT4UHYW#zM2U*AW( z-ud%Ou|Vc@<-ci8c6meE2Yk<>kG?`(0Afh@UlH zzI=&&`t%9Ab&Dh?&$e3rFYW(#Bfvdh-Dy7ebhik0XZA?Lzs+l%u|xF2Zmk-f0se3< zf|RA=M^S`~4E=J7DlOx}qs+X>Gmy7d(A>dkz_9P+GF}D)={=^lnBD24Dr;y~i_z@b z%)B22U5bT*jysiw&*~poeB#hjI1SLRGMlqrPtkaehRs?#pi#O~n|S7Na?ayfx0@Ys znrotB$i~RT#K@*2M>Us#*|A$~U|qn$v4C}}sx81{gmc>j_*q!^1rFn%8G;f!aGba- zji!1;ahlBFxVC|T3$%_Nf~yG?Jw26{1?t{^_S`y6XQvy&TbuDiqO(YzM-PeCGBA|l zdlJ43^EGXXPSQ;8;bPoJX8=SS5uHY11Op$G!D#`akBIhWXTQYcaz0My85wPG+IjM1A$IH-qRstSb#)CUAW(?YqpYkl zI9r(E^cV4l9Xo=tRjbwk{r3VKv~XIirIm+WzkUllbEXihsdtZ8I=jz-fPcd=<_+=;=#wJm$^IhZRGOj|1XoXznrisxaaMAtBGP z-MjB%`uba7{Jf!$I+%`5J{BGx4m_;{=N~2}dDydO%~(fAFU$oC*52L?xTa$_Z$85= zUM#|*qpJYdo%q=1<>h0wwQYD?h>s$^kIs|~3`cMt%E(xPRa7)%LPCYm|9qT>Ffr+2 z^78U9_eGeWUm4V$iqAKE&j{Aga-8Ryn&tsdD};DH@bBB0wlFIZ`5Da`A8%-+5LLu-_gaSx1p3Gm)L{M_5pg697z*4WsE zk1r+VcFfQajcp`8{tOJ~ao+aeK_%APJAkh*RPVxth1jD zU3q|?CWiPU8gprBY5YEJ&Ky^~93!JKoXPWH9=G86baYR!=H_lpQ!^iMDu#7!i_1Ll zX)LtezdZyF^+2z0(eFb9%By=E$jG=JT@#u`UYjQ*uqT5Ie~8Ja`QUR?Gaq^(fn5w! zx#*X;@U+bg2!hR*g^R!CqEL85H{WzC1j@D&uJ3bEZ1tOKR0x7_gY(p1b0Nh$z(|M^ z!(tXT&zcR1x#>NWHb;AD^ZCpKvZn#xMI9cMa|XbtrAbfHqRdHBn2c=Q zr}(oN<)XE376wlxGH}ugbzR28ET;@2*IH3R4+ykE=YsIGz2~6MRGw3j&eII5$3zc#p0%(sJ#5>9VkINF|k}ZM^c-t?hzjjK(HFSS9x{6 zz6PBwod(t1Gst`BF7(A%DP}5vGr-YT7y=ccpfH^F&=;}GNizUAF49m_3q4?dURL(C z`GG7opPSv&vxBN2&@V6ylFYq)M@8FD|+!dMA%ZLyYA%-$& zPGqdMpGoP3zDC66+@VlEXc=5qv-M36KGO@0y zx%pZJrQY6|`AV<8=1d1{;ER0lH0GmU?^3EC^9lU$EkV(K8yip9D@p?2^$v%nJy`15 z0Gn=J3a7fjx6Swe*1hAjoI>+s@2292w;&mVB&STB^QQO^$|K1gq%e|6;3IZ8?L=va zR-QUlgvSwmrK5WcGD#uMClDV&=SLx-DOm^c78;tHKrc%{UdYExObS6JDFB#4puyLG z57dDCumt1bnY!QHzrO&xfB!!40B7JYZ-Ksx!Z|As$H~MbAMX77u#Aj4;J*iee@)=D znUAj+Q&3RArTK7XjRBtd9QaZm z$Oj+rHW(Pr0q@z0%U_|P6;Qqa@V^1_Mhnz`3+Gi&pT5F!bL%k9^$a{^rjEZC`Br9557lOP}f|osewg|tcxPAK>$fc#w9|n+FTk)}F zV$uda-w$Ko4l?m8z>x=+i#t1!?3RR&6B;WdccOa;G?wt%FpNzphVB#K^P2cv($n9; zWfvNnYcMW_AV-$tV}fK?28OeEJxE4US1*LM@(kuX3fr&&jf)+~m`iZk;?ku{ur^Au zq@*Nl+qOJ>?a|T2K-(TsR4L1XD77V`DdV z{P=NPwp+Qf0OR5+2Kl`N=2aHt;c6IfZ&*`CaKBN2nVJ@0`}U!|lmO{|_Xhs zMUO=w-85bLDKJLE^3nna#((Ss>9qbzfxnfe-V{Fi4H|`{H243sjd>m6-vdv@_*roF z_pR2c5P?N}1|~BLO0A=t_aijMweBa-sH8<;$9Dw?b9AI;*7(N^=za$NZt1LrZ@I;a&YJNk zYSv^aVJxd+mK+O~gKU3IwighdhtaU_TlSZ%Xj;teFAX<6IyW^3H2Kl(o$J39vLA%` z*W#O6)hOBCdZ_&&{3GX#hcWLPyeS$tj4>K^&Gb`wa0Vy}5=_6=W)U{M9_MWQn#OC& z_+5cHZ>B4fdT#9YZK2TazB)aZ_DSDiL#K|ztFPPX7FxHJWvWESk)Evw=~gu0SyG#3 zG(a24Ytfrxn#efku>z82>tIi%??fT`@;NLcrkZRPyuC9py1;6kG)YIm<~WQYAoxi0 z94u}s6d<`5zBg=;zkcq;Zw&6jOcM6;8!new4XyRstot{PB`C(--Q~6C4D7YjO(9M;RRSdTs?`lYSZ%m0ySHrXQ@XjJ_1q#LEm~kC!+05pvCpI zB5Di1o(;Yh5?PxkI@oQ+_#-!%^Y%yBukqk-Bl9gpedTNZ4*`X5UHt}*5hQT1<9?1+ zr30T%4ro`kHg@4jS7t!X2r|WZdY+u-MbqPDpL%lDTE8PRfMx{gzF!NZAr@4hT8=Xy zr-C%!4>$>)_r_l!rxUEcf_xW9`WZaacWaYW#$x}^;0d$xJKwCI!DBk4I%oRgGgs#K zMW@LMu-&U)>6g@uef~W(oS6crDrU>qV&67R4FhML-06~YD(!jgpV;6lk!JT7C=LJn z{i}!Z(S5hG(+CqfF58eu_}4Q3r?-TmfI~A8jR^ew@9Hi25f+%*EcfX|P;&n0K#w;k zJ-t|C`}WvJ1_rTFD2&p6gqsuBv_>+8mPkICda=K@veXWOy zsra+Id(!cYj1qMtqnOB*E0c;5ep9felL7+SUB`~aT{?L(?!r_Zr3e?|{P~5Sl$Dc8 zcJGe4mz`avhQ?uNXo&Xm<>VvW+_~@h`LlZD5hC@drMu$GT6 zy9eQPmHm}%>x1g%(ide6|riS zN%o#S*Y8?cJ-U7JWc-Eq@B5fZymTq)D3rn13M*^DFdJL`5UK-o;FF%7Ne(mTeP&q{ zMt!KQZIIZxGbR#^QBhH)B629aXHRTsRna1_Wp^Fa7DC7PcJ@k`}Vj;g@u*N{z>^ifrUFFN-)S`ij2(JuBMh;0D8f%v;ppd z#@DP#%#Mr8(?#nC?&m1}q>b6iAo_~vQE+gE$rLRq{`_t2OwmzN9O%qv4UMFy5fRxt zy1Kd-fPDwp1pSqc|4tez+tk!5fZAQXIx+Vf4g0=LQrk2A`SXiDtzDa#f$%_k_@Cf` z#txOae?P}y)28?&R@TDdnRWk@@PY;TAGd9bi;juO)kFN?SM!3_)WE<1!{f(AT9%fv z0ld6V-~E%eegzA3zU$z@IN!|7lGSLR{w3e^u9?=7FAbLP;s* zxwm(UZDV7LAfnBrb^x_Z~HxCq39{k8pxX&D!QWV6|D zN4x>?B-BrC?!3O4cp(0uuAcZ5$w#ENX7f>>5Kl(%P<|3?vhtcAMp)OPtPOF%*^jWSU^Xn?;HmQN9WJ-=Fgws>*3+yfa)dHhxXc=Hyu2`;sYdD zG`2A@F`LB1Bwu3K93t4)fMX$&ku4&UlWP*ewuA`wI=BN{2qFcA2INTs3^NXktWc$- zWU4?{vw<{Eq`H6QBUse$y1IICbMq4cVEaG>+ZK?UAA+ox|C2rajVFVy4qzjRU_${o z>QC(3=j_+g(#nVA>c7JSjZ1U$8~(7cEF++8DeUaIgEQChpM=r*YxCx~xX8%t?Px9h zZ0u1wS{sO8qchZB+bV*-=YUHEnL7zwJh)hJans=>$V&tF1RR3>HYO;Hcm?du(C5!z z$j*iXI%{3K_W0oP<;l;4g|phHc;VAYuv3Ei1K`}@2F?!79me1`!}&o6+!}CN;MU=3 zU^4?SN5G*oNg$3J*fCAAu@wx7h-9^`SdmnE_im;!qH{m40W>#=mZCF=pWoyChYm$u zS5WY%Vq)3=XA}uIqp*XU|C??BlvxOP=@FHbJj)#%V!UqOPP0I?4fXTKwr7hYTwGjS z&wbw}8`R4SP7ItBIGJgO!YGXw@MHaoBRl(oFXzvncb;v$XKQn28j=lsef_ODIOIN| zF$Oy?a5gZuPT($p^8)7$&SyG&9(*Sp2JEb$@0vs*q2)a{Z{D_=S?@myM@6M>UbxV! z37s#&mTT5^_2W95T6<|Q_Yp(|h09fmiJ9yFNt-ia(SFd@j?esS8{e<(pXG1b6qi&} zQ@?O#o&O}<+xu?*!Gp2BNM`t18$U}&dl<K<8!On6a8pqk%QBX*&T(l_T^^bW-cG$8dF1EF`o%c`LoLL6#9VELT`ax<( zL?ox((=*utos*C|b0*OR-4T)Ur~00?3-R=sb^U1=(JMc{)csqw#K)nwUcYV@nt7%} zXVm)oMo}lH_={V%B*fjhlWsOM|5xo!-2(jY{i1sc8E_`xzTQ)aK;(z#SAF<5d59jL zKAn8p&Mv{br>B?o-_-TrDvRbeJ-tNZ(4n~N{QOV9%ISQ3xxH3aako=a3fCh3@!!Jd zciTbxAtj|q8{OYb-3!l7*U;Gtok^pjayS18@A_SQei!cDyZ0IE`Ehai8=vh%wm7K~o$XP=8Ho-Hn(-I9<{u>Rlh#h>*Dops>cwsGs$_}G88PJU0G zAoUC0&HB84ePSxQqeEl$GkpF@Iy(2GI$2qZ{>Sg2Nc~`8DH>H*Psy#VeJ$}%a3#T_ z{e*bDhez@;e*WAZQoa9eAKj_BxFno?{klcypX_n8R+N+;KmVUxE5ADy2nTfciOxqe z?}yqQdduUI(^6UmvicW6W3q0wPHm| z8RGFX$2}>jP=|vf|HH4!{lAn4b1{DZ{!;_g2SjrP1fI^Sv;DU?BN+(Y=lzUzJ0=``o#tQ)mw0o#MS>Vp*-f zg8!#^NLEAZ65TERc<=g8>-qtHQb;Jf6X8xe%OJY+{CTbH;luIQxVZ9Wvn%+=``Oqy zKAa>AI6Eht`%la9-Lt`(6AuZ;p>g{PM@T5#2^}0(RrmHXFZ|jijYB>Pt&&Gi+nm!*^Y_Ql`&JGtS<`cWL|^Fa=rm3E_aB$EaQXPP{F zR=p%7By`u&qetDHoSe>OWMrt%)Gf@cJ1;M9g{!Npi=CaFm%sll%d)a6In)li8v)sQ z)^&wsYIIhbStS-E_+Xz=0U-c(Q zS7T&k6pHMwNbMjU)}B3k0^waF^z!RhSXx@zp(|Ie97eGH{R515?`9c8Ut-Zc6Qa4O zZG;2Tk)bw8_5Gb+Qc}H?oxNai)vDzD3l|cd($b1Gk^YBtZ`Ix1&5m?k@D9tTnfS~M zZ{EE55z;fEwhkOPa2@HNX6BQ^R8&-OW?7%m*w`pY!t3qZ9yX-+M0aUgT1naH&N3h% z-JF#FcfN~D@>z7B@U!*28@)6lVms1P z{uMtO(A`Oau5JV9?e>F?>@3)y-2iuU+TDP(^N?o)y0cB7&y#^YK>u|gkm^HgEHE%| z&(GTbH|a>Xtf{G!Mn=XBxXJ<^+X1HwU@sSgY`uQLkAKFeN^o1dIdj~hE+eRO3G|Bx z>S6sz+Bg3O-7{$B*yYs|9N9|KOopqIP|&JuK!NN2nR^rU=Wb)?_xS4sL^ zR$P|~?;HZWHk31m^5>u~q*G3YcK&Kdif|Vf&uZPYDIp2jIQ)q3Y;hzTAiFcTTh=*t zEdCO@D@Nxjdiqdka~t3w2TtHSo$l=TlJNeOkNU(9ZVA-A75Z_9$j)9ksI2_B1l{Gt z#^!B8GW@^Q8z7pduC7`5=Xn0A58vl?baZ^RRheyk|0E6RgA5F|$No>_&(AN=|0nYD z-}MR6F!;|FzuE9-W?lg6SqbLf0M_$iaA#p1c!To;=MN6)@{t}N*;yC@tTL<@o>}+G z#*MmZe}~JT=3l*f{V)@g9N@4SaIpuxd|}Te%)0Mp-}f_V0otp`Ud12kI)<-(W@ZKA z<;&joe~RDVmCMX5UOicZ`LZ~yH`10jzvA@IPPxBE!W@av##RljO zLL$5Q@9_Dj`EbVV`I!yl@8b5maCUa_U|0K}S@WcNRV3-xK?>ezd-k{{HOQMCWbW5@V3v UHnM&EH}?u~_W!@ + + + + 0 + 0529e96f6f937c4ccb05427782412d6febba9f8cd983f9245ce34a1446d711bceed0da10748a5420c290ffad9e959de906b2498698dc66ca2b63e2e6b02d344ba9f936b44571eed3f91bcec3154d3dbb258510d13b89ecea7be33a5690b51d6e35acc0fe04f1a41b3f0fd4dd35d9166d8c9d3921142fdc92f9967e927bc8c991 + + 0 + 1 + 95 + 95 + 67436 + 10040172 + 1687112622 + MyCrysis + asdf19 + D41D8CD98F00B204E9800998ECF8427E + aefb5064bbd1eb632fa8d57aab1c49366ce0ee3161cbef19f2b7971b63b811790ecbf6a47b34c55f65a0766b40c261c5d69c394cd320842dd2bccba883d30eae8fdba5d03b21b09bfc600dcb30b1b2f3fbe8077630b006dcb54c4254f14891762f72e7bbfe743eb8baf65f9e8c8d11ebe46f6b59e986b4c394cfbc2c8606e29f + 010001 + a75fbef2b50236c991e438efe81f0941a645a23d00e3f9bb409681e6c9b37e92a97c88afe65663081642c494c06860ee5dd45f9adab4d9caac3ed766717027eb76f8e6c6a30cbd32637abacfad0dcaf0e96948d20182766099a5133e89b92c498bc3470d1bab666d549e8af51edc2f009d63ecdff84551ed04138b6129adfd9f + 5F3E3EB96D4301D21E4F8375BB6FE4800F5CB27CD5B3D7DE49CB46B0B3E75A63A3D534AF7DA0FAB21B8C7A4EF56A316FACEECEAB153AD313876FC2A1895A151982897359328D06258B0125789FEBF928D117B7C8A342F8C40135708A17A65A3B189D3A0B7EBFEF21E1C435A409B1DA9449D6EF17D69BAAD844FADFFA30B227A8 + + + + "; + // Given + var raw = @" + + + + 1 + 95 + 95 + spyguy + + 0000 + + + + "; + // When + var request = new Module.Auth.Contract.Request.LoginUniqueNickRequest(raw); + request.Parse(); + var handler = new Module.Auth.Handler.LoginUniqueNickHandler(MokeObject.Client,request); + handler.Handle(); + // Then + } +} \ No newline at end of file From 2b4ff7fc8b2e7b0638096d14b947669f8201a625 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 5 Oct 2023 09:50:19 +0800 Subject: [PATCH 044/231] refactor(web): fixed exponent error, added expire time --- src/Servers/WebServer/src/Application/ClientInfo.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/Servers/WebServer/src/Application/ClientInfo.cs b/src/Servers/WebServer/src/Application/ClientInfo.cs index 83993030b..7b4f8ddfd 100644 --- a/src/Servers/WebServer/src/Application/ClientInfo.cs +++ b/src/Servers/WebServer/src/Application/ClientInfo.cs @@ -1,3 +1,4 @@ +using System; using UniSpy.Server.Core.Abstraction.BaseClass; namespace UniSpy.Server.WebServer.Application @@ -15,14 +16,14 @@ public sealed class ClientInfo : ClientInfoBase /// /// RandomNumber /// - public const string PeerKeyExponent = "010001"; + public const string PeerKeyExponent = "000001"; public const string PeerKeyModulus = "aefb5064bbd1eb632fa8d57aab1c49366ce0ee3161cbef19f2b7971b63b811790ecbf6a47b34c55f65a0766b40c261c5d69c394cd320842dd2bccba883d30eae8fdba5d03b21b09bfc600dcb30b1b2f3fbe8077630b006dcb54c4254f14891762f72e7bbfe743eb8baf65f9e8c8d11ebe46f6b59e986b4c394cfbc2c8606e29f"; /// /// should be 256 characters /// public const string ServerData = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; - public const int ExpireTime = 0; + public static int ExpireTime => (int)((DateTimeOffset)DateTime.UtcNow + TimeSpan.FromMinutes(5)).ToUnixTimeSeconds(); public const string SignaturePreFix = "0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003020300C06082A864886F70D020505000410"; public ClientInfo() { From 9f35b075b85001bd859e3d0f8fce528d3d2f3520 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 7 Oct 2023 07:35:09 +0800 Subject: [PATCH 045/231] fix(lib): add redis database missing check --- src/Libraries/LinqToRedis/src/Core/RedisClient.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/Libraries/LinqToRedis/src/Core/RedisClient.cs b/src/Libraries/LinqToRedis/src/Core/RedisClient.cs index 454e2342e..d62688071 100644 --- a/src/Libraries/LinqToRedis/src/Core/RedisClient.cs +++ b/src/Libraries/LinqToRedis/src/Core/RedisClient.cs @@ -30,14 +30,7 @@ public class RedisClient where TValue : RedisKeyValueObject /// The default keyvalue object, used to provide default information /// public readonly TValue DefaultKVObject = ((TValue)System.Activator.CreateInstance(typeof(TValue))); - public RedisClient(string connectionString) - { - CheckValidation(); - Multiplexer = ConnectionMultiplexer.Connect(connectionString); - Db = Multiplexer.GetDatabase((int)DefaultKVObject.Db); - _provider = new RedisQueryProvider(this); - Context = new QueryableObject(_provider); - } + public RedisClient(string connectionString) : this(ConnectionMultiplexer.Connect(connectionString)) { } /// /// Use existing multiplexer for performance /// @@ -51,6 +44,11 @@ public RedisClient(IConnectionMultiplexer multiplexer) } private void CheckValidation() { + // check if Db is set + if (DefaultKVObject.Db is null) + { + throw new ArgumentNullException($"The RedisKeyValueObject:{this.GetType().Name} must set database number in constructor"); + } var properties = typeof(TValue).GetProperties().Where(p => p.GetCustomAttributes(typeof(RedisKeyAttribute), true).Select(a => a is RedisKeyAttribute).Any()).ToList(); if (properties.Count() == 0) { From 2eb9b0aa020742984087d22c277e7f07abc5de49 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 10 Oct 2023 19:11:52 +0800 Subject: [PATCH 046/231] refactor(chat): re-design distribution chating logic --- .../src/Abstraction/BaseClass/ClientBase.cs | 5 +- src/Libraries/Core/src/Extension/EasyTimer.cs | 17 ++- .../Core/src/Extension/Redis/RedisDbNumber.cs | 13 +- .../LinqToRedis/src/Core/RedisClient.cs | 46 ++----- .../LinqToRedis/src/Core/RedisLock.cs | 51 ++++++++ .../BaseClass/Channel/ChannelHandlerBase.cs | 38 +++++- .../Abstraction/BaseClass/CmdHandlerBase.cs | 32 ----- .../BaseClass/General/LogedInHandlerBase.cs | 10 -- .../src/Abstraction/Interface/IShareClient.cs | 1 + .../Interface/IStorageOperation.cs | 16 +++ .../Chat/src/Aggregate/ChannelManager.cs | 36 +----- .../ChannelProperty/ChannelGeneral.cs | 47 ++++++- .../ChannelProperty/ChannelManage.cs | 83 ++++++++++++ .../ChannelProperty/ChannelOperation.cs | 36 ------ .../ChannelProperty/ChannelUserRelated.cs | 46 ++++--- src/Servers/Chat/src/Aggregate/ChannelUser.cs | 29 +++-- .../Chat/src/Aggregate/Redis/ChannelCache.cs | 25 ++++ .../src/Aggregate/Redis/ClientInfoCache.cs | 19 +++ .../Aggregate/Redis/Contract/RemoteMessage.cs | 2 +- .../Chat/src/Aggregate/Redis/PeerRoom.cs | 69 ++++++++++ .../Chat/src/Aggregate/Redis/RedisChannel.cs | 60 ++------- .../Chat/src/Aggregate/RemoteClient.cs | 5 +- src/Servers/Chat/src/Application/Client.cs | 18 +-- .../Chat/src/Application/ClientInfo.cs | 10 +- .../Chat/src/Application/ClientManager.cs | 6 +- src/Servers/Chat/src/Application/Server.cs | 17 +-- .../Chat/src/Application/ServerLauncher.cs | 6 + .../Chat/src/Application/StorageOperation.cs | 96 +++++++++++++- .../CmdHandler/Channel/GetCKeyHandler.cs | 2 +- .../Channel/GetChannelKeyHandler.cs | 2 +- .../Handler/CmdHandler/Channel/JoinHandler.cs | 81 +++++++----- .../Handler/CmdHandler/Channel/KickHandler.cs | 4 +- .../CmdHandler/Channel/NamesHandler.cs | 4 +- .../Handler/CmdHandler/Channel/PartHandler.cs | 29 ++--- .../Channel/SetChannelKeyHandler.cs | 2 +- .../CmdHandler/General/CryptHandler.cs | 4 +- .../CmdHandler/General/GetKeyHandler.cs | 5 +- .../CmdHandler/General/InviteHandler.cs | 2 +- .../Handler/CmdHandler/General/ListHandler.cs | 2 +- .../Handler/CmdHandler/General/NickHandler.cs | 49 ++++---- .../Handler/CmdHandler/General/QuitHandler.cs | 8 +- .../Handler/CmdHandler/General/WhoHandler.cs | 13 +- .../CmdHandler/Message/PrivateHandler.cs | 2 +- .../Chat/src/UniSpy.Server.Chat.csproj | 1 - .../Chat/test/Channel/ChannelHandlerTest.cs | 8 +- src/Servers/Chat/test/Game/GameTest.cs | 5 +- src/Servers/Chat/test/MockObject.cs | 4 + src/Servers/Chat/test/RedisChatChannelTest.cs | 15 ++- .../Interface/IStorageOperation.cs | 12 +- .../GameTrafficRelay/ConnectionListener.cs | 8 +- .../GameTrafficRelay/RelayServerInfo.cs | 13 +- .../GameTrafficRelay/ServerStatusReporter.cs | 8 +- .../{NatAddressInfo.cs => InitPacketCache.cs} | 25 ++-- .../{NatFailInfo.cs => NatFailInfoCache.cs} | 15 ++- .../NatNegotiation/src/Application/Client.cs | 3 +- .../src/Application/StorageOperation.cs | 20 +-- .../src/Handler/CmdHandler/ConnectHandler.cs | 2 +- .../src/Handler/CmdHandler/InitHandler.cs | 4 +- .../src/Handler/CmdHandler/ReportHandler.cs | 2 +- src/Servers/NatNegotiation/test/GameTest.cs | 12 +- .../NatNegotiation/test/NatDetectionTest.cs | 40 +++--- .../Interface/IStorageOperation.cs | 11 ++ .../QueryReport/src/Application/Server.cs | 1 + .../src/Application/StorageOperation.cs | 25 ++-- .../src/UniSpy.Server.QueryReport.csproj | 1 + .../Interface/IStorageOperation.cs | 6 +- .../{GameServerInfo.cs => GameServerCache.cs} | 12 +- .../src/V1/Application/StorageOperation.cs | 8 +- .../V1/Handler/CmdHandler/HeartbeatHandler.cs | 2 +- .../Interface/IStorageOperation.cs | 11 +- .../src/V2/Aggregate/Redis/ChannelInfo.cs | 118 ------------------ .../src/V2/Aggregate/Redis/GameServerInfo.cs | 12 +- .../V2/Aggregate/Redis/HeartBeatChannel.cs | 2 +- .../src/V2/Application/StorageOperation.cs | 33 +---- .../src/V2/Contract/Result/EchoResult.cs | 2 +- .../V2/Handler/CmdHandler/HeartBeatHandler.cs | 4 +- .../src/V1/Contract/Result/ListResult.cs | 2 +- .../src/V2/Aggregate/HeartbeatChannel.cs | 2 +- .../src/V2/Aggregate/ServerInfoBuilder.cs | 16 +-- .../src/V2/Application/Server.cs | 2 +- .../V2/Contract/Result/AdHoc/AdHocResult.cs | 2 +- .../Result/ServerList/ServerMainListResult.cs | 4 +- .../ServerList/ServerNetworkInfoListResult.cs | 4 +- .../Handler/CmdHandler/AdHoc/AdHocHandler.cs | 4 +- .../ServerList/ServerListHandler.cs | 12 +- .../UniSpy.Server.ServerBrowser.Test.csproj | 2 +- .../ServerBrowser/test/V2/PeerRoomTest.cs | 14 +-- src/Servers/WebServer/test/Auth/GameTest.cs | 54 ++++---- 88 files changed, 876 insertions(+), 735 deletions(-) create mode 100644 src/Libraries/LinqToRedis/src/Core/RedisLock.cs create mode 100644 src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelManage.cs delete mode 100644 src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelOperation.cs create mode 100644 src/Servers/Chat/src/Aggregate/Redis/ChannelCache.cs create mode 100644 src/Servers/Chat/src/Aggregate/Redis/ClientInfoCache.cs create mode 100644 src/Servers/Chat/src/Aggregate/Redis/PeerRoom.cs rename src/Servers/NatNegotiation/src/Aggregate/Redis/{NatAddressInfo.cs => InitPacketCache.cs} (92%) rename src/Servers/NatNegotiation/src/Aggregate/Redis/{NatFailInfo.cs => NatFailInfoCache.cs} (70%) create mode 100644 src/Servers/QueryReport/src/Abstraction/Interface/IStorageOperation.cs rename src/Servers/QueryReport/src/V1/Aggregation/Redis/{GameServerInfo.cs => GameServerCache.cs} (73%) diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs index 7a9593c9f..64653e9ad 100644 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs +++ b/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs @@ -26,7 +26,6 @@ public ClientBase(IConnection connection, IServer server) Connection = connection; Server = server; EventBinding(); - // ClientManagerBase.AddClient(this); } protected virtual void EventBinding() { @@ -39,7 +38,9 @@ protected virtual void EventBinding() break; case NetworkConnectionType.Udp: ((IUdpConnection)Connection).OnReceive += OnReceived; - _timer = new EasyTimer(TimeSpan.FromHours(1), TimeSpan.FromMinutes(1), CheckExpiredClient); + _timer = new EasyTimer(TimeSpan.FromHours(1), TimeSpan.FromMinutes(1)); + _timer.Elapsed += (s, e) => CheckExpiredClient(); + _timer.Start(); break; case NetworkConnectionType.Http: ((IHttpConnection)Connection).OnReceive += OnReceived; diff --git a/src/Libraries/Core/src/Extension/EasyTimer.cs b/src/Libraries/Core/src/Extension/EasyTimer.cs index d38e09e26..ac89ddf4e 100644 --- a/src/Libraries/Core/src/Extension/EasyTimer.cs +++ b/src/Libraries/Core/src/Extension/EasyTimer.cs @@ -13,7 +13,15 @@ public class EasyTimer /// private Timer _timer; public bool IsExpired => IdleTime > ExpireTime; - public EasyTimer(TimeSpan expireTimeSpan, TimeSpan intervalTimeSpan, Action invokingAction) + public event ElapsedEventHandler Elapsed + { + add { _timer.Elapsed += value; } + remove { _timer.Elapsed -= value; } + } + /// + /// Easy timer constructor, remember to call Start() + /// + public EasyTimer(TimeSpan expireTimeSpan, TimeSpan intervalTimeSpan) { _timer = new Timer { @@ -22,8 +30,13 @@ public EasyTimer(TimeSpan expireTimeSpan, TimeSpan intervalTimeSpan, Action invo AutoReset = true }; ExpireTime = expireTimeSpan; + } + /// + /// Start the timer + /// + public void Start() + { RefreshLastActiveTime(); - _timer.Elapsed += (s, e) => invokingAction(); _timer.Start(); } public void RefreshLastActiveTime() => LastActiveTime = DateTime.Now; diff --git a/src/Libraries/Core/src/Extension/Redis/RedisDbNumber.cs b/src/Libraries/Core/src/Extension/Redis/RedisDbNumber.cs index 0ccc239b7..b9626fa75 100755 --- a/src/Libraries/Core/src/Extension/Redis/RedisDbNumber.cs +++ b/src/Libraries/Core/src/Extension/Redis/RedisDbNumber.cs @@ -2,11 +2,12 @@ namespace UniSpy.Server.Core.Extension.Redis { public enum RedisDbNumber : int { - ChatChannel = 0, - GameServerV1 = 1, - GameServerV2 = 2, - NatAddressInfo = 3, - NatFailInfo = 4, - GameTrafficRelay = 5 + ChatChannel, + ChatUser, + GameServerV1, + GameServerV2, + NatAddressInfo, + NatFailInfo, + GameTrafficRelay } } diff --git a/src/Libraries/LinqToRedis/src/Core/RedisClient.cs b/src/Libraries/LinqToRedis/src/Core/RedisClient.cs index d62688071..ac4501e25 100644 --- a/src/Libraries/LinqToRedis/src/Core/RedisClient.cs +++ b/src/Libraries/LinqToRedis/src/Core/RedisClient.cs @@ -10,7 +10,7 @@ namespace UniSpy.LinqToRedis { /// - /// TODO we need to implement get AllKeys, AllValues + /// The redis linq client /// /// public class RedisClient where TValue : RedisKeyValueObject @@ -86,18 +86,7 @@ public async Task> GetValuesAsync(TValue key) } public List GetMatchedKeys(IRedisKey key = null) { - var matchedKeys = new List(); - var searchKey = key is null ? DefaultKVObject.SearchKey : key.SearchKey; - foreach (var end in _endPoints) - { - var server = Multiplexer.GetServer(end); - // we get matched key from database - foreach (var k in server.Keys(pattern: searchKey, database: Db.Database)) - { - matchedKeys.Add(k); - } - } - return matchedKeys; + return Task.Run(async () => await GetMatchedKeysAsync(key)).Result; } public async Task> GetMatchedKeysAsync(IRedisKey key = null) { @@ -119,17 +108,9 @@ public async Task> GetMatchedKeysAsync(IRedisKey key = null) /// if key is null this will get all key value from database /// /// - /// public Dictionary GetKeyValues(IRedisKey key = null) { - var dict = new Dictionary(); - var keys = GetMatchedKeys(key); - foreach (var k in keys) - { - var value = Db.StringGet(k.ToString()); - dict.Add(k, JsonConvert.DeserializeObject(value)); - } - return dict; + return Task.Run>(async () => await GetKeyValuesAsync(key)).Result; } public async Task> GetKeyValuesAsync(IRedisKey key = null) @@ -146,7 +127,7 @@ public async Task> GetKeyValuesAsync(IRedisKey key = public bool SetValue(TValue value) { - return Db.StringSet(value.FullKey, JsonConvert.SerializeObject((TValue)value), value.ExpireTime); + return Task.Run(async () => await SetValueAsync(value)).Result; } public async Task SetValueAsync(TValue value) { @@ -154,12 +135,7 @@ public async Task SetValueAsync(TValue value) } public TValue GetValue(IRedisKey key) { - var value = Db.StringGet(key.FullKey); - if (value.IsNull) - { - return default; - } - return JsonConvert.DeserializeObject(value); + return Task.Run(async () => await GetValueAsync(key)).Result; } public async Task GetValueAsync(IRedisKey key) { @@ -179,6 +155,10 @@ public TValue this[IRedisKey key] get => GetValue(key); set => SetValue(value); } + public void FlushDb() + { + Task.Run(async () => await FlushDbAsync()); + } public async Task FlushDbAsync() { var keys = GetMatchedKeysAsync(); @@ -187,13 +167,5 @@ public async Task FlushDbAsync() await Db.KeyDeleteAsync(key); } } - public void FlushDb() - { - var keys = GetMatchedKeys(); - foreach (var key in keys) - { - Db.KeyDelete(key); - } - } } } diff --git a/src/Libraries/LinqToRedis/src/Core/RedisLock.cs b/src/Libraries/LinqToRedis/src/Core/RedisLock.cs new file mode 100644 index 000000000..fb1948a5a --- /dev/null +++ b/src/Libraries/LinqToRedis/src/Core/RedisLock.cs @@ -0,0 +1,51 @@ +using System; +using StackExchange.Redis; +using System.Threading.Tasks; + +namespace UniSpy.LinqToRedis +{ + public class RedisLock : IDisposable + { + public TimeSpan LockTime { get; private set; } + public TimeSpan? RetryTime { get; private set; } + public TimeSpan? RetryInterval { get; private set; } + public IDatabase Db { get; private set; } + /// + /// The token used to lock the object + /// + /// + public RedisValue LockToken { get; private set; } = Environment.MachineName; + /// + /// The key using to lock the object, original key is ChannelName:myfriend the lock key will be Lock_ChannelName:myfriend + /// + /// + public string LockKey { get; private set; } + public RedisLock(TimeSpan lockTime, IDatabase db, RedisKeyValueObject key) : this(lockTime, null, null, db, key) { } + public RedisLock(TimeSpan lockTime, TimeSpan? retryTime, TimeSpan? retryInterval, IDatabase db, RedisKeyValueObject key) + { + LockTime = lockTime; + RetryTime = retryTime; + RetryInterval = retryInterval; + Db = db; + LockKey = "Lock_" + key.FullKey; + } + + public bool LockTake() + { + return Db.LockTake(LockKey, LockToken, LockTime); + } + public async Task LockTakeAsync() + { + return await Db.LockTakeAsync(LockKey, LockToken, LockTime); + } + public bool LockRelease() + { + return Db.LockRelease(LockKey, LockToken); + } + + public void Dispose() + { + Db.LockRelease(LockKey, LockToken); + } + } +} \ No newline at end of file diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs index 3dc6d38da..86d525abb 100755 --- a/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs +++ b/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs @@ -3,6 +3,7 @@ using UniSpy.Server.Chat.Aggregate; using UniSpy.Server.Core.Abstraction.Interface; using UniSpy.Server.Chat.Abstraction.Interface; +using UniSpy.Server.Chat.Aggregate.Redis.Contract; namespace UniSpy.Server.Chat.Abstraction.BaseClass { @@ -18,14 +19,16 @@ public abstract class ChannelHandlerBase : LogedInHandlerBase protected ChannelUser _user; private new ChannelRequestBase _request => (ChannelRequestBase)base._request; public ChannelHandlerBase(IShareClient client, IRequest request) : base(client, request) { } - protected override void RequestCheck() { if (_request.RawRequest is not null) { base.RequestCheck(); } - _channel = _client.Info.GetJoinedChannel(_request.ChannelName); + if (_channel is null) + { + _channel = _client.Info.GetLocalJoinedChannel(_request.ChannelName); + } if (_channel is null) { throw new NoSuchChannelException($"No such channel {_request.ChannelName}", _request.ChannelName); @@ -36,13 +39,40 @@ protected override void RequestCheck() throw new NoSuchNickException($"Can not find user with nickname: {_client.Info.NickName} username: {_client.Info.UserName}"); } } - protected override void PublishMessage() + + public override void Handle() { + base.Handle(); + try + { + PublishMessage(); + } + catch (Exception ex) + { + HandleException(ex); + } + } + /// + /// publish message to redis channel, only localclient can publish message + /// + protected void PublishMessage() + { + // we do not publish message when the message is received from remote client + if (_client.IsRemoteClient) + { + return; + } if (_channel is null) { return; } - base.PublishMessage(); + if (_request.RawRequest is null) + { + return; + } + Aggregate.Channel.UpdateChannelCache(_user, _channel); + var msg = new RemoteMessage(_request, _client.GetRemoteClient()); + _channel.Broker.PublishMessage(msg); } } } diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/CmdHandlerBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/CmdHandlerBase.cs index 24e6dd093..ecb5b2720 100755 --- a/src/Servers/Chat/src/Abstraction/BaseClass/CmdHandlerBase.cs +++ b/src/Servers/Chat/src/Abstraction/BaseClass/CmdHandlerBase.cs @@ -22,7 +22,6 @@ public abstract class CmdHandlerBase : UniSpy.Server.Core.Abstraction.BaseClass. protected new ResultBase _result { get => (ResultBase)base._result; set => base._result = value; } protected new ResponseBase _response { get => (ResponseBase)base._response; set => base._response = value; } public CmdHandlerBase(IShareClient client, IRequest request) : base(client, request) { } - //if we use this structure the error response should also write to _sendingBuffer protected override void HandleException(System.Exception ex) { if (ex is IRCException) @@ -39,36 +38,5 @@ protected override void Response() } base.Response(); } - /// - /// publish message to redis channel - /// - protected virtual void PublishMessage() - { - // we do not publish message when the message is received from remote client - if (_client.Info.IsRemoteClient) - { - return; - } - // if rawrequest is null means the request is generated by us, we do not publish custom requests - if (_request.RawRequest is null) - { - return; - } - var msg = new RemoteMessage(_request, _client.GetRemoteClient()); - Application.Server.GeneralChannel.PublishMessage(msg); - } - public override void Handle() - { - base.Handle(); - try - { - // we publish this message to redis channel - PublishMessage(); - } - catch (Exception ex) - { - HandleException(ex); - } - } } } diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/General/LogedInHandlerBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/General/LogedInHandlerBase.cs index 0e1613c01..88bc1d67f 100755 --- a/src/Servers/Chat/src/Abstraction/BaseClass/General/LogedInHandlerBase.cs +++ b/src/Servers/Chat/src/Abstraction/BaseClass/General/LogedInHandlerBase.cs @@ -7,16 +7,6 @@ public abstract class LogedInHandlerBase : CmdHandlerBase { public LogedInHandlerBase(IShareClient client, IRequest request) : base(client, request) { } - // public override void Handle() - // { - // if (!_client.Info.IsLoggedIn) - // { - // _client.LogInfo($"{_client.Info.NickName} Please login first!"); - // return; - // } - - // base.Handle(); - // } protected override void RequestCheck() { if (!_client.Info.IsLoggedIn) diff --git a/src/Servers/Chat/src/Abstraction/Interface/IShareClient.cs b/src/Servers/Chat/src/Abstraction/Interface/IShareClient.cs index b3e109338..05ad506cb 100644 --- a/src/Servers/Chat/src/Abstraction/Interface/IShareClient.cs +++ b/src/Servers/Chat/src/Abstraction/Interface/IShareClient.cs @@ -7,6 +7,7 @@ namespace UniSpy.Server.Chat.Abstraction.Interface public interface IShareClient : IClient, ITestClient { public new ClientInfo Info { get; } + public bool IsRemoteClient { get; } public RemoteClient GetRemoteClient(); } } \ No newline at end of file diff --git a/src/Servers/Chat/src/Abstraction/Interface/IStorageOperation.cs b/src/Servers/Chat/src/Abstraction/Interface/IStorageOperation.cs index 4a33f559e..18641edd1 100644 --- a/src/Servers/Chat/src/Abstraction/Interface/IStorageOperation.cs +++ b/src/Servers/Chat/src/Abstraction/Interface/IStorageOperation.cs @@ -1,8 +1,24 @@ +using System.Collections.Generic; +using UniSpy.Server.Chat.Aggregate; +using UniSpy.Server.Chat.Aggregate.Redis; +using UniSpy.Server.Chat.Application; +using UniSpy.Server.Core.Database.DatabaseModel; + namespace UniSpy.Server.Chat.Abstraction.Interface { public interface IStorageOperation { + public Dictionary> PeerGroupList { get; } + public ChannelCache.RedisClient ChannelCacheClient { get; } + public ClientInfoCache.RedisClient ClientCacheClient { get; } (int userId, int profileId, bool emailVerified, bool banned) NickAndEmailLogin(string nickName, string email, string passwordHash); (int userId, int profileId, bool emailVerified, bool banned) UniqueNickLogin(string uniqueNick, int namespaceId); + bool IsChannelExist(ChannelCache key); + Channel GetChannel(ChannelCache key); + void UpdateChannel(Channel channel); + void RemoveChannel(Channel channel); + void UpdateClient(Client client); + void RemoveClient(Client client); + bool IsClientExist(ClientInfoCache key); } } \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/ChannelManager.cs b/src/Servers/Chat/src/Aggregate/ChannelManager.cs index aea470dec..3323c381d 100644 --- a/src/Servers/Chat/src/Aggregate/ChannelManager.cs +++ b/src/Servers/Chat/src/Aggregate/ChannelManager.cs @@ -1,43 +1,11 @@ using System.Collections.Concurrent; using UniSpy.Server.Chat.Abstraction.Interface; +using UniSpy.Server.Chat.Aggregate.Redis; namespace UniSpy.Server.Chat.Aggregate { - public static class ChannelManager + public static class LocalChannelManager { - public static readonly ConcurrentDictionary Channels = new ConcurrentDictionary(); - public static bool IsChannelExist(string name) - { - return Channels.ContainsKey(name); - } - /// - /// You need to manually check channel existance then get channel - /// - /// - /// - public static Channel GetChannel(string name) - { - if (!Channels.TryGetValue(name, out var channel)) - { - throw new Chat.Exception("Channel do not exist!"); - } - return channel; - } - public static void RemoveChannel(string name) - { - Channels.TryRemove(name, out var chan); - var chanInfo = chan.GetChannelCache(); - QueryReport.Application.StorageOperation.RemoveChannel(chanInfo); - } - public static Channel CreateChannel(string name, string password = null, IShareClient creator = null) - { - var channel = new Channel(name, creator, password); - if (!Channels.TryAdd(name, channel)) - { - Channels.TryGetValue(name, out channel); - } - return channel; - } } } \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelGeneral.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelGeneral.cs index 9e2d667ac..7436749e2 100755 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelGeneral.cs +++ b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelGeneral.cs @@ -1,10 +1,30 @@ using System; using System.Linq; +using Newtonsoft.Json; using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.QueryReport.Aggregate.Redis.Channel; +using UniSpy.Server.Chat.Aggregate.Redis; namespace UniSpy.Server.Chat.Aggregate { + public enum PeerRoomType + { + /// + /// The main room for a game. + /// + Title, + /// + /// A room where players meet before starting a game. + /// + Staging, + /// + /// A room which is, in general, for a particular type of gameplay (team, dm, etc.). + /// + Group, + /// + /// The normal room + /// + Normal + } public sealed partial class Channel { public Guid ServerId { get; private set; } @@ -25,11 +45,28 @@ public sealed partial class Channel public PeerRoomType? RoomType { get; private set; } public string Password { get; private set; } public string Topic { get; set; } - public Redis.ChatMessageChannel MessageBroker { get; private set; } + /// + /// Join handler creates Broker and stored on local + /// + [JsonIgnore] + public ChannelMessageBroker Broker + { + get + { + if (_broker is null) + { + MessageBrokers.TryGetValue(Name, out _broker); + } + return _broker; + } + } + [JsonIgnore] + private ChannelMessageBroker _broker; public int? GroupId { get; private set; } public string RoomName { get; private set; } public bool IsValidPeerRoom => GroupId is not null && RoomName is not null; public string PreviousJoinedChannel { get; private set; } + public Channel() { } public Channel(string name, IShareClient client, string password = null) { ServerId = client.Server.Id; @@ -51,8 +88,6 @@ public Channel(string name, IShareClient client, string password = null) GetTitileRoomName(); break; } - MessageBroker = new Redis.ChatMessageChannel(Name); - MessageBroker.Subscribe(); } private void GetGroupId() { @@ -65,9 +100,9 @@ private void GetGroupId() } private void GetPeerRoomName() { - if (QueryReport.Application.StorageOperation.PeerGroupList.ContainsKey(GameName)) + if (Chat.Application.StorageOperation.Persistance.PeerGroupList.ContainsKey(GameName)) { - var grouplist = QueryReport.Application.StorageOperation.PeerGroupList[GameName]; + var grouplist = Chat.Application.StorageOperation.Persistance.PeerGroupList[GameName]; var room = grouplist.Where(g => g.Groupid == GroupId).FirstOrDefault(); if (room is null) { diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelManage.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelManage.cs new file mode 100644 index 000000000..d808ff27f --- /dev/null +++ b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelManage.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Concurrent; +using Newtonsoft.Json; +using UniSpy.Server.Chat.Abstraction.Interface; +using UniSpy.Server.Chat.Aggregate.Redis; + +namespace UniSpy.Server.Chat.Aggregate +{ + /// + /// The code manage local and remote channel + /// + public sealed partial class Channel + { + /// + /// The local channel manager + /// + [JsonIgnore] + public static readonly ConcurrentDictionary LocalChannels = new(); + public static readonly ConcurrentDictionary MessageBrokers = new(); + /// + /// You need to manually check channel existance then get channel + /// + /// + /// + public static Channel GetLocalChannel(string name) + { + LocalChannels.TryGetValue(name, out var channel); + return channel; + } + public static void RemoveLocalChannel(Channel channel) + { + LocalChannels.TryRemove(channel.Name, out var chan); + RemoveMessageBrocker(channel); + } + public static Channel CreateLocalChannel(string name, IShareClient creator = null, string password = null) + { + var channel = new Channel(name, creator, password); + LocalChannels.TryAdd(name, channel); + AddMessageBrocker(channel); + return channel; + } + public static void UpdateChannelCache(ChannelUser user, Channel channel) + { + if (user.Client.IsRemoteClient) + { + return; + } + Application.StorageOperation.Persistance.UpdateChannel(channel); + } + public static Channel GetChannelCache(ChannelCache key) + { + return Application.StorageOperation.Persistance.GetChannel(key); + } + public static void RemoveChannelCache(ChannelUser user, Channel channel) + { + if (user.Client.IsRemoteClient) + { + return; + } + StackExchange.Redis.RedisValue token = Environment.MachineName; + if (Application.StorageOperation.Persistance.ChannelCacheClient.Db.LockTake(channel.Name, token, TimeSpan.FromSeconds(10))) + { + Application.StorageOperation.Persistance.RemoveChannel(channel); + } + } + + public static ChannelMessageBroker AddMessageBrocker(Channel channel) + { + ChannelMessageBroker broker; + if (!MessageBrokers.TryGetValue(channel.Name, out broker)) + { + broker = new ChannelMessageBroker(channel.Name); + broker.Subscribe(); + MessageBrokers.TryAdd(channel.Name, broker); + } + return broker; + } + public static void RemoveMessageBrocker(Channel channel) + { + MessageBrokers.TryRemove(channel.Name, out var _); + } + } +} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelOperation.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelOperation.cs deleted file mode 100644 index cb92487bf..000000000 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelOperation.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Linq; -using UniSpy.Server.QueryReport.Aggregate.Redis.Channel; - -namespace UniSpy.Server.Chat.Aggregate -{ - public partial class Channel - { - public ChannelInfo GetChannelCache() - { - var chanInfo = new ChannelInfo() - { - ServerId = this.ServerId, - Name = this.Name, - MaxNumberUser = this.MaxNumberUser, - CreateTime = this.CreateTime, - RoomType = this.RoomType, - GameName = this.GameName, - PreviousJoinedChannel = this.PreviousJoinedChannel, - Users = this.Users.Where(u => u.Value.Info.IsRemoteClient == false).Select(u => u.Key).ToList() - }; - return chanInfo; - } - public static void UpdateChannelCache(ChannelUser user) - { - if (user.Client.Info.IsRemoteClient) - { - return; - } - var channelInfo = user.Channel.GetChannelCache(); - if (!QueryReport.Application.StorageOperation.UpdateChannel(channelInfo)) - { - throw new Error.IRC.Channel.NoSuchChannelException("Update channel on redis fail.", user.Channel.Name); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs index 0d3db9f55..d52ab41a3 100644 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs +++ b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs @@ -1,12 +1,11 @@ using Newtonsoft.Json; using System.Collections.Concurrent; +using System.Collections.Generic; using System.Linq; using UniSpy.Server.Chat.Abstraction.Interface; using UniSpy.Server.Chat.Contract.Request.Channel; using UniSpy.Server.Chat.Error.IRC.General; using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.QueryReport.Aggregate.Redis.Channel; namespace UniSpy.Server.Chat.Aggregate { @@ -16,12 +15,12 @@ public partial class Channel /// | key -> Nickname | value -> ChannelUser| /// [JsonProperty] - public ConcurrentDictionary BanList { get; private set; } = new ConcurrentDictionary(); + public ConcurrentDictionary BanList { get; private set; } = new(); /// /// | key -> Nickname | value -> ChannelUser| /// [JsonProperty] - public ConcurrentDictionary Users { get; private set; } = new ConcurrentDictionary(); + public ConcurrentDictionary Users { get; private set; } = new(); [JsonProperty] public string _creatorNickName { get; private set; } [JsonIgnore] @@ -29,7 +28,7 @@ public ChannelUser Creator { get { - if (Users.Values.Where(u => u.Info.NickName == _creatorNickName).Count() == 1) + if (Users.Values.Where(u => u.Client.Info.NickName == _creatorNickName).Count() == 1) { return Users[_creatorNickName]; } @@ -41,23 +40,23 @@ public ChannelUser Creator } private void BanUser(ModeRequest request) { - var result = Users.Values.Where(u => u.Info.NickName == request.NickName); + var result = Users.Values.Where(u => u.Client.Info.NickName == request.NickName); if (result.Count() != 1) { return; } ChannelUser user = result.First(); - if (BanList.Values.Where(u => u.Info.NickName == request.NickName).Count() == 1) + if (BanList.Values.Where(u => u.Client.Info.NickName == request.NickName).Count() == 1) { return; } - BanList.TryAdd(user.Info.NickName, user); + BanList.TryAdd(user.Client.Info.NickName, user); } private void UnBanUser(ModeRequest request) { - var result = BanList.Where(u => u.Value.Info.NickName == request.NickName); + var result = BanList.Where(u => u.Value.Client.Info.NickName == request.NickName); if (result.Count() == 1) { var keyValue = result.First(); @@ -73,7 +72,7 @@ private void UnBanUser(ModeRequest request) private void AddChannelOperator(ModeRequest request) { //check whether this user is in this channel - var result = Users.Where(u => u.Value.Info.UserName == request.UserName); + var result = Users.Where(u => u.Value.Client.Info.UserName == request.UserName); if (result.Count() != 1) { return; @@ -90,7 +89,7 @@ private void AddChannelOperator(ModeRequest request) private void RemoveChannelOperator(ModeRequest request) { - var result = Users.Where(u => u.Value.Info.UserName == request.UserName); + var result = Users.Where(u => u.Value.Client.Info.UserName == request.UserName); if (result.Count() != 1) { return; @@ -105,7 +104,7 @@ private void RemoveChannelOperator(ModeRequest request) private void EnableUserVoicePermission(ModeRequest request) { - var result = Users.Where(u => u.Value.Info.UserName == request.UserName); + var result = Users.Where(u => u.Value.Client.Info.UserName == request.UserName); if (result.Count() != 1) { return; @@ -121,7 +120,7 @@ private void EnableUserVoicePermission(ModeRequest request) } private void DisableUserVoicePermission(ModeRequest request) { - var result = Users.Where(u => u.Value.Info.UserName == request.UserName); + var result = Users.Where(u => u.Value.Client.Info.UserName == request.UserName); if (result.Count() != 1) { return; @@ -134,7 +133,7 @@ private void DisableUserVoicePermission(ModeRequest request) } } public ChannelUser GetUser(string nickName) => Users.ContainsKey(nickName) == true ? Users[nickName] : null; - public ChannelUser GetUser(IShareClient client) => Users.Values.FirstOrDefault(u => u.Connection.RemoteIPEndPoint == client.Connection.RemoteIPEndPoint); + public ChannelUser GetUser(IShareClient client) => Users.Values.FirstOrDefault(u => u.Client.Connection.RemoteIPEndPoint == client.Connection.RemoteIPEndPoint); public ChannelUser AddUser(IShareClient client, string password = null, bool isChannelCreator = false, bool isChannelOperator = false) { Validation(client, password); @@ -154,7 +153,7 @@ public ChannelUser AddUser(IShareClient client, string password = null, bool isC public void RemoveUser(ChannelUser user) { - user.Info.PreviousJoinedChannel = Name; + user.Client.Info.PreviousJoinedChannel = Name; RemoveBindOnUserAndChannel(user); } @@ -167,7 +166,7 @@ private bool IsUserBanned(IShareClient client) { return false; } - if (BanList[client.Info.NickName].Connection.RemoteIPEndPoint != client.Connection.RemoteIPEndPoint) + if (BanList[client.Info.NickName].Client.Connection.RemoteIPEndPoint != client.Connection.RemoteIPEndPoint) { return false; } @@ -196,13 +195,13 @@ public string GetAllUsersNickString() } public static void AddBindOnUserAndChannel(ChannelUser joiner) { - joiner.Channel.Users.TryAdd(joiner.Info.NickName, joiner); - joiner.Info.JoinedChannels.TryAdd(joiner.Channel.Name, joiner.Channel); + joiner.Channel.Users.TryAdd(joiner.Client.Info.NickName, joiner); + joiner.Client.Info.JoinedChannels.TryAdd(joiner.Channel.Name, joiner.Channel); } public static void RemoveBindOnUserAndChannel(ChannelUser leaver) { - leaver.Channel.Users.TryRemove(leaver.Info.NickName, out _); - leaver.Info.JoinedChannels.TryRemove(leaver.Channel.Name, out _); + leaver.Channel.Users.TryRemove(leaver.Client.Info.NickName, out _); + leaver.Client.Info.JoinedChannels.Remove(leaver.Channel.Name, out _); } /// /// Send message to all users in this channel @@ -210,11 +209,10 @@ public static void RemoveBindOnUserAndChannel(ChannelUser leaver) /// public void MultiCast(IClient sender, IResponse message, bool isSkipSender = false) { - foreach (var user in Users.Values) + var users = Users.Values.Where(u => !u.IsRemoteClient).ToList(); + foreach (var user in users) { - if (user.IsRemoteUser - || (isSkipSender - && user.RemoteIPEndPoint.Equals(sender.Connection.RemoteIPEndPoint))) + if (isSkipSender) { continue; } diff --git a/src/Servers/Chat/src/Aggregate/ChannelUser.cs b/src/Servers/Chat/src/Aggregate/ChannelUser.cs index 66920b4e1..008cc7b2c 100755 --- a/src/Servers/Chat/src/Aggregate/ChannelUser.cs +++ b/src/Servers/Chat/src/Aggregate/ChannelUser.cs @@ -16,11 +16,10 @@ public sealed class ChannelUser /// /// Indicate whether this client is shared from redis channel /// - [JsonProperty] - public bool IsRemoteUser => Info.IsRemoteClient; public bool IsVoiceable { get; set; } = true; public bool IsChannelCreator { get; set; } public bool IsChannelOperator { get; set; } + public bool IsRemoteClient => Client.IsRemoteClient; /// /// The remote ip end point of this user /// @@ -31,11 +30,27 @@ public sealed class ChannelUser /// The client reference /// [JsonIgnore] - public IShareClient Client { get; private set; } - [JsonIgnore] - public ClientInfo Info => Client.Info; + public IShareClient Client + { + get + { + if (_client is not null) + { + return _client; + } + else + { + var client = ClientManager.GetClient(RemoteIPEndPoint); + if (client is null) + { + throw new UniSpy.Exception("the client is not on local server, please check logic"); + } + return (IShareClient)client; + } + } + } [JsonIgnore] - public IConnection Connection => Client.Connection; + private IShareClient _client; /// /// The user key values storage /// @@ -68,7 +83,7 @@ public string Modes public ChannelUser() { } public ChannelUser(IShareClient client, Channel channel) { - Client = client; + _client = client; Channel = channel; ServerId = client.Server.Id; RemoteIPEndPoint = client.Connection.RemoteIPEndPoint; diff --git a/src/Servers/Chat/src/Aggregate/Redis/ChannelCache.cs b/src/Servers/Chat/src/Aggregate/Redis/ChannelCache.cs new file mode 100644 index 000000000..43eeafcb5 --- /dev/null +++ b/src/Servers/Chat/src/Aggregate/Redis/ChannelCache.cs @@ -0,0 +1,25 @@ +using System; +using UniSpy.LinqToRedis; +using UniSpy.Server.Core.Extension.Redis; + +namespace UniSpy.Server.Chat.Aggregate.Redis +{ + public record ChannelCache : Core.Abstraction.BaseClass.RedisKeyValueObject + { + [RedisKey] + public string GameName { get; set; } + [RedisKey] + public string ChannelName { get; set; } + // [RedisKey] + // public string PreviousJoinedChannel => Channel?.PreviousJoinedChannel; + public Channel Channel { get; set; } + public ChannelCache() : base(RedisDbNumber.ChatChannel, TimeSpan.FromMinutes(1)) + { + } + public class RedisClient : Core.Abstraction.BaseClass.RedisClient + { + public RedisClient() { } + } + } + +} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/Redis/ClientInfoCache.cs b/src/Servers/Chat/src/Aggregate/Redis/ClientInfoCache.cs new file mode 100644 index 000000000..c636ae3da --- /dev/null +++ b/src/Servers/Chat/src/Aggregate/Redis/ClientInfoCache.cs @@ -0,0 +1,19 @@ +using System; +using UniSpy.LinqToRedis; +using UniSpy.Server.Chat.Application; +using UniSpy.Server.Core.Extension.Redis; + +namespace UniSpy.Server.Chat.Aggregate.Redis +{ + public record ClientInfoCache : Core.Abstraction.BaseClass.RedisKeyValueObject + { + [RedisKey] + public string NickName { get; set; } + public ClientInfo Info { get; set; } + public ClientInfoCache() : base(RedisDbNumber.ChatChannel, TimeSpan.FromHours(1)) { } + public class RedisClient : Core.Abstraction.BaseClass.RedisClient + { + public RedisClient() { } + } + } +} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/Redis/Contract/RemoteMessage.cs b/src/Servers/Chat/src/Aggregate/Redis/Contract/RemoteMessage.cs index c3951868e..2bfed57c5 100644 --- a/src/Servers/Chat/src/Aggregate/Redis/Contract/RemoteMessage.cs +++ b/src/Servers/Chat/src/Aggregate/Redis/Contract/RemoteMessage.cs @@ -15,7 +15,7 @@ public class RemoteMessage /// /// Constructor for json deserialization /// - public RemoteMessage(){} + public RemoteMessage() { } public RemoteMessage(RequestBase request, RemoteClient client) { RawRequest = UniSpyEncoding.GetBytes(request.RawRequest); diff --git a/src/Servers/Chat/src/Aggregate/Redis/PeerRoom.cs b/src/Servers/Chat/src/Aggregate/Redis/PeerRoom.cs new file mode 100644 index 000000000..7f3b2ded7 --- /dev/null +++ b/src/Servers/Chat/src/Aggregate/Redis/PeerRoom.cs @@ -0,0 +1,69 @@ +using System; +using System.Linq; + +namespace UniSpy.Server.Chat.Aggregate.Redis +{ + public static class PeerRoom + { + /// + /// When game connects to the server, the player will enter the default channel for communicating with other players. + /// + public const string TitleRoomPrefix = "#GSP"; + /// + /// When a player creates their own game and is waiting for others to join they are placed in a separate chat room called the "staging room" + /// Staging rooms have two title seperator like #GSP!xxxx!xxxx + /// + public const string StagingRoomPrefix = "#GSP"; + /// + /// group rooms is used split the list of games into categories (by gametype, skill, region, etc.). In this case, when entering the title room, the user would get a list of group rooms instead of a list of games + /// Group room have one title seperator like #GPG!xxxxxx + /// + public const string GroupRoomPrefix = "#GPG"; + public const char TitleSeperator = '!'; + /// + /// Group room #GPG!622 + /// Staging room #GSP!worms3!Ml4lz344lM + /// Normal room #islanbul + /// + /// + /// + public static PeerRoomType GetRoomType(string channelName) + { + if (IsGroupRoom(channelName)) + { + return PeerRoomType.Group; + } + else if (IsStagingRoom(channelName)) + { + return PeerRoomType.Staging; + } + else if (IsTitleRoom(channelName)) + { + return PeerRoomType.Title; + } + else + { + return PeerRoomType.Normal; + } + } + private static bool IsStagingRoom(string channelName) + { + var a = channelName.Count(c => c == TitleSeperator) == 2 ? true : false; + var b = channelName.StartsWith(StagingRoomPrefix, StringComparison.CurrentCultureIgnoreCase) ? true : false; + return a && b; + } + private static bool IsTitleRoom(string channelName) + { + var a = channelName.Count(c => c == TitleSeperator) == 1 ? true : false; + var b = channelName.StartsWith(TitleRoomPrefix, StringComparison.CurrentCultureIgnoreCase) ? true : false; + return a && b; + } + private static bool IsGroupRoom(string channelName) + { + + var a = channelName.Count(c => c == TitleSeperator) == 1 ? true : false; + var b = channelName.StartsWith(GroupRoomPrefix, StringComparison.CurrentCultureIgnoreCase) ? true : false; + return a && b; + } + } +} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs b/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs index 8b145b1e6..8225232f7 100644 --- a/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs +++ b/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs @@ -4,76 +4,32 @@ using UniSpy.Server.Core.Extension.Redis; using System.Threading.Tasks; using UniSpy.Server.Chat.Application; -using UniSpy.Server.Chat.Abstraction.Interface; using UniSpy.Server.Core.Encryption; namespace UniSpy.Server.Chat.Aggregate.Redis { - /// - /// The general chat message will process here - /// - public class GeneralMessageChannel : RedisChannelBase - { - public GeneralMessageChannel() : base(RedisChannelName.ChatChannelPrefix) - { - } - public override void ReceivedMessage(RemoteMessage message) - { - if (message.Client.Server.Id == ServerLauncher.Server.Id) - { - return; - } - if (message.Type == "DISCONNECT") - { - ClientManager.RemoveClient(message.Client); - return; - } - IShareClient client = (IShareClient)ClientManager.GetClient(message.Client.Connection.RemoteIPEndPoint); - if (client is null) - { - ClientManager.AddClient(message.Client); - client = message.Client; - } - else - { - // we update the remote client info - ((RemoteClient)client).Info = message.Client.Info; - } - - var switcher = new CmdSwitcher(client, UniSpyEncoding.GetString(message.RawRequest)); - if (System.Diagnostics.Debugger.IsAttached) - { - switcher.Handle(); - } - else - { - Task.Run(() => switcher.Handle()); - } - } - } /// /// When a local channel is created the user message will send to redis channel /// redis channel is like a broadcast platform which will broadcast the message to all the user /// when user is connected to unispy chat server /// - public class ChatMessageChannel : RedisChannelBase + public class ChannelMessageBroker : RedisChannelBase { - public ChatMessageChannel(string chatChannelName) : base($"{RedisChannelName.ChatChannelPrefix}:{chatChannelName}") { } + public ChannelMessageBroker(string chatChannelName) : base($"{RedisChannelName.ChatChannelPrefix}:{chatChannelName}") { } public override void ReceivedMessage(RemoteMessage message) { - // base.ReceivedMessage(message); - if (message.Client.Server.Id == ServerLauncher.Server.Id) + // we are uint testing + if (ServerLauncher.Server is null) { return; } - IShareClient client = (IShareClient)ClientManager.GetClient(message.Client.Connection.RemoteIPEndPoint); - if (client is null) + // base.ReceivedMessage(message); + if (message.Client.Server.Id == ServerLauncher.Server.Id) { - throw new Chat.Exception($"There are no remote client found in RemoteClients pool, the client must be login on the remote server."); + return; } - - var switcher = new CmdSwitcher(client, UniSpyEncoding.GetString(message.RawRequest)); + var switcher = new CmdSwitcher(message.Client, UniSpyEncoding.GetString(message.RawRequest)); if (System.Diagnostics.Debugger.IsAttached) { switcher.Handle(); diff --git a/src/Servers/Chat/src/Aggregate/RemoteClient.cs b/src/Servers/Chat/src/Aggregate/RemoteClient.cs index 5826b0966..985c2e65b 100644 --- a/src/Servers/Chat/src/Aggregate/RemoteClient.cs +++ b/src/Servers/Chat/src/Aggregate/RemoteClient.cs @@ -13,6 +13,8 @@ namespace UniSpy.Server.Chat.Aggregate { public class RemoteClient : IShareClient { + [JsonProperty] + public bool IsRemoteClient => !ClientManager.ClientPool.ContainsKey(Connection.RemoteIPEndPoint); public bool IsLogRaw { get; set; } [JsonConverter(typeof(ConcreteTypeConverter))] public IConnection Connection { get; set; } @@ -32,8 +34,7 @@ public RemoteClient(Client client) { Connection = new RemoteTcpConnection(client.Connection, new RemoteTcpConnectionManager()); Server = new RemoteServer(client.Server); - Info = client.Info.DeepCopy(); - ((ClientInfo)Info).IsRemoteClient = true; + Info = client.Info; Crypto = client.Crypto; } public void Send(IResponse response) { } diff --git a/src/Servers/Chat/src/Application/Client.cs b/src/Servers/Chat/src/Application/Client.cs index f64c10b85..d01081e3d 100644 --- a/src/Servers/Chat/src/Application/Client.cs +++ b/src/Servers/Chat/src/Application/Client.cs @@ -2,7 +2,6 @@ using UniSpy.Server.Chat.Abstraction.Interface; using UniSpy.Server.Chat.Aggregate; using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Aggregate.Redis.Contract; using UniSpy.Server.Chat.Contract.Request.General; using UniSpy.Server.Chat.Handler; using UniSpy.Server.Chat.Handler.CmdHandler.General; @@ -17,6 +16,7 @@ public class Client : ClientBase, IShareClient { public new ClientInfo Info { get => (ClientInfo)base.Info; private set => base.Info = value; } public new ITcpConnection Connection => (ITcpConnection)base.Connection; + public bool IsRemoteClient => !ClientManager.ClientPool.ContainsKey(Connection.RemoteIPEndPoint); private BufferCache _bufferCache = new BufferCache(); private RemoteClient _remoteClient; public Client(IConnection connection, IServer server) : base(connection, server) @@ -29,11 +29,7 @@ public Client(IConnection connection, IServer server, ClientInfo info) : this(co Info = info; _remoteClient = new RemoteClient(this); } - protected override void OnConnected() - { - Info.IsRemoteClient = false; - base.OnConnected(); - } + protected override void OnReceived(object buffer) { var message = DecryptMessage((byte[])buffer); @@ -62,16 +58,14 @@ protected override void OnDisconnected() new QuitHandler(this, req).Handle(); Info.IsLoggedIn = false; } - var request = new QuitRequest() { Reason = "Disconnect from server" }; - var message = new RemoteMessage(request, GetRemoteClient()); - Chat.Application.Server.GeneralChannel.PublishMessage(message); base.OnDisconnected(); } protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, UniSpyEncoding.GetString((byte[])buffer)); - public RemoteClient GetRemoteClient() + public RemoteClient GetRemoteClient() => _remoteClient; + protected override void OnConnected() { - _remoteClient.Info = Info; - return _remoteClient; + StorageOperation.Persistance.RemoveClient(this); + base.OnConnected(); } } } \ No newline at end of file diff --git a/src/Servers/Chat/src/Application/ClientInfo.cs b/src/Servers/Chat/src/Application/ClientInfo.cs index ede2fea9b..7e1f43696 100755 --- a/src/Servers/Chat/src/Application/ClientInfo.cs +++ b/src/Servers/Chat/src/Application/ClientInfo.cs @@ -1,4 +1,5 @@ -using System.Collections.Concurrent; +using System.Collections.Generic; +using System; using Newtonsoft.Json; using UniSpy.Server.Chat.Aggregate; using UniSpy.Server.Core.Abstraction.BaseClass; @@ -12,7 +13,8 @@ public sealed class ClientInfo : ClientInfoBase /// (We do not send this information to our public channel) /// [JsonIgnore] - public ConcurrentDictionary JoinedChannels { get; private set; } = new ConcurrentDictionary(); + public Dictionary JoinedChannels { get; private set; } = new(); + // secure connection public string GameName { get; set; } public string NickName { get; set; } @@ -27,8 +29,8 @@ public sealed class ClientInfo : ClientInfoBase public bool IsQuietMode { get; set; } = false; [JsonIgnore] public string IRCPrefix => $"{NickName}!{UserName}@{Chat.Abstraction.BaseClass.ResponseBase.ServerDomain}"; - public bool IsRemoteClient { get; set; } public string PreviousJoinedChannel { get; set; } = ""; + public Guid ServerId { get; private set; } /// /// Global user key values /// @@ -41,7 +43,7 @@ public ClientInfo() public bool IsJoinedChannel(string channelName) => JoinedChannels.ContainsKey(channelName); - public Channel GetJoinedChannel(string channelName) + public Channel GetLocalJoinedChannel(string channelName) { if (JoinedChannels.ContainsKey(channelName)) { diff --git a/src/Servers/Chat/src/Application/ClientManager.cs b/src/Servers/Chat/src/Application/ClientManager.cs index ce18d9b96..393eda3f7 100644 --- a/src/Servers/Chat/src/Application/ClientManager.cs +++ b/src/Servers/Chat/src/Application/ClientManager.cs @@ -3,6 +3,7 @@ using System.Linq; using UniSpy.Server.Core.Abstraction.BaseClass; using UniSpy.Server.Chat.Abstraction.Interface; +using UniSpy.Server.Core.Abstraction.Interface; namespace UniSpy.Server.Chat.Application { @@ -11,10 +12,9 @@ public sealed class ClientManager : ClientManagerBase /// /// We need to make sure client is get by nickname, otherwise we throw exception /// - public static IShareClient GetClientByNickName(string nickName) + public static Client GetClientByNickName(string nickName) { - IShareClient client; - client = (IShareClient)ClientPool.Values.FirstOrDefault(c => ((IShareClient)c).Info.NickName == nickName); + var client = (Client)ClientPool.Values.FirstOrDefault(c => ((IShareClient)c).Info.NickName == nickName); return client; } public static List GetAllClientInfo() diff --git a/src/Servers/Chat/src/Application/Server.cs b/src/Servers/Chat/src/Application/Server.cs index 9bddc2987..579919444 100644 --- a/src/Servers/Chat/src/Application/Server.cs +++ b/src/Servers/Chat/src/Application/Server.cs @@ -3,13 +3,11 @@ using UniSpy.Server.Core.Network.Tcp.Server; using UniSpy.Server.Core.Abstraction.Interface; using System.Net; -using UniSpy.Server.Chat.Abstraction.Interface; namespace UniSpy.Server.Chat.Application { public sealed class Server : ServerBase { - public static readonly GeneralMessageChannel GeneralChannel = new GeneralMessageChannel(); static Server() { _name = "Chat"; @@ -21,24 +19,11 @@ public Server(IConnectionManager manager) : base(manager) { } public override void Start() { base.Start(); - GeneralChannel.Subscribe(); + _ = Chat.Application.StorageOperation.Persistance.PeerGroupList; } protected override IClient CreateClient(IConnection connection) => new Client(connection, this); protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new TcpConnectionManager(endPoint); - protected override IClient HandleConnectionInitialization(IConnection connection) - { - var client = (IShareClient)base.HandleConnectionInitialization(connection); - if (client.Info.IsRemoteClient) - { - var info = client.Info; - info.IsRemoteClient = false; - // we parse info to our client - client = new Client(connection, this, info); - - } - return client; - } } } \ No newline at end of file diff --git a/src/Servers/Chat/src/Application/ServerLauncher.cs b/src/Servers/Chat/src/Application/ServerLauncher.cs index 90e96853b..d65b58de0 100644 --- a/src/Servers/Chat/src/Application/ServerLauncher.cs +++ b/src/Servers/Chat/src/Application/ServerLauncher.cs @@ -8,5 +8,11 @@ public sealed class ServerLauncher : ServerLauncherBase { public static IServer Server => ServerInstances[0]; protected override List LaunchNetworkService() => new List { new Server() }; + public override void Start() + { + base.Start(); + // initialize peergrouplist + _ = Chat.Application.StorageOperation.Persistance.PeerGroupList; + } } } \ No newline at end of file diff --git a/src/Servers/Chat/src/Application/StorageOperation.cs b/src/Servers/Chat/src/Application/StorageOperation.cs index ef3b92893..8f2e9e623 100644 --- a/src/Servers/Chat/src/Application/StorageOperation.cs +++ b/src/Servers/Chat/src/Application/StorageOperation.cs @@ -1,14 +1,29 @@ +using System; +using System.Collections.Generic; using System.Linq; using UniSpy.Server.Chat.Abstraction.Interface; +using UniSpy.Server.Chat.Aggregate; +using UniSpy.Server.Chat.Aggregate.Redis; using UniSpy.Server.Core.Database.DatabaseModel; -using UniSpy.Server.QueryReport.Aggregate.Redis.Channel; namespace UniSpy.Server.Chat.Application { public sealed class StorageOperation : IStorageOperation { public static IStorageOperation Persistance = new StorageOperation(); - private static RedisClient _redisClient = new RedisClient(); + /// + /// The peer group list in memory + /// + Dictionary> IStorageOperation.PeerGroupList => _peerGroupList; + private readonly Dictionary> _peerGroupList = GetAllGroupList(); + public ChannelCache.RedisClient ChannelCacheClient { get; } = new(); + public ClientInfoCache.RedisClient ClientCacheClient { get; } = new(); + + + public StorageOperation() + { + } + public (int userId, int profileId, bool emailVerified, bool banned) NickAndEmailLogin(string nickName, string email, string passwordHash) { using (var db = new UniSpyContext()) @@ -66,5 +81,82 @@ join u in db.Users on p.Userid equals u.Userid banned: result.First().banned); } } + + public bool IsChannelExist(ChannelCache key) + { + return ChannelCacheClient.Context.Count(c => c.ChannelName == key.ChannelName && c.GameName == key.GameName) == 1 ? true : false; + } + + public Channel GetChannel(ChannelCache key) + { + var result = ChannelCacheClient.Context.Where(c => c.ChannelName == key.ChannelName && c.GameName == key.GameName).FirstOrDefault(); + return result?.Channel; + } + + public void UpdateChannel(Channel channel) + { + var data = new ChannelCache + { + ChannelName = channel.Name, + GameName = channel.GameName, + Channel = channel + }; + ChannelCacheClient.SetValue(data); + } + + public void RemoveChannel(Channel channel) + { + var data = new ChannelCache + { + ChannelName = channel.Name, + }; + ChannelCacheClient.DeleteKeyValue(data); + } + + public void UpdateClient(Client client) + { + var data = new ClientInfoCache + { + NickName = client.Info.NickName, + Info = client.Info + }; + ClientCacheClient.SetValue(data); + } + + public void RemoveClient(Client client) + { + var data = new ClientInfoCache + { + NickName = client.Info.NickName + }; + ClientCacheClient.DeleteKeyValue(data); + } + + public bool IsClientExist(ClientInfoCache key) + { + return ClientCacheClient.Context.Count(c => c.NickName == key.NickName) == 1 ? true : false; + } + + private static Dictionary> GetAllGroupList() + { + using (var db = new UniSpyContext()) + { + var result = from g in db.Games + join gl in db.Grouplists on g.Gameid equals gl.Gameid + select new Grouplist + { + Game = g, + Gameid = g.Gameid, + Groupid = gl.Groupid, + Roomname = gl.Roomname + }; + var result2 = from g in result + group g by g.Game.Gamename into dd + select new KeyValuePair>(dd.Key, dd.ToList()); + + var data = result2.ToDictionary(x => x.Key, x => x.Value); + return data; + } + } } } \ No newline at end of file diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetCKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetCKeyHandler.cs index d2cdad551..8159c2240 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetCKeyHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetCKeyHandler.cs @@ -62,7 +62,7 @@ private void GetUserKeyValue(ChannelUser user) string userValues = user.KeyValues.GetValueString(_request.Keys); var model = new GetCKeyDataModel { - NickName = user.Info.NickName, + NickName = user.Client.Info.NickName, UserValues = userValues }; _result.DataResults.Add(model); diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetChannelKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetChannelKeyHandler.cs index dc0166ba1..264b5dc47 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetChannelKeyHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetChannelKeyHandler.cs @@ -23,7 +23,7 @@ public GetChannelKeyHandler(IShareClient client, GetChannelKeyRequest request) : protected override void DataOperation() { - _result.ChannelUserIRCPrefix = _user.Info.IRCPrefix; + _result.ChannelUserIRCPrefix = _user.Client.Info.IRCPrefix; _result.Values = _channel.KeyValues.GetValueString(_request.Keys); _result.ChannelName = _channel.Name; } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs index c06732299..bfd5ca197 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs @@ -1,14 +1,13 @@ using UniSpy.Server.Chat.Abstraction.BaseClass; using UniSpy.Server.Chat.Error.IRC.General; -using UniSpy.Server.Chat.Aggregate; using UniSpy.Server.Chat.Contract.Request.Channel; using UniSpy.Server.Chat.Contract.Request.General; using UniSpy.Server.Chat.Contract.Response.Channel; using UniSpy.Server.Chat.Contract.Result.Channel; -using UniSpy.Server.Chat.Aggregate.Redis; using UniSpy.Server.Chat.Abstraction.Interface; using UniSpy.Server.Chat.Error.IRC.Channel; -using UniSpy.Server.QueryReport.Aggregate.Redis.Channel; +using System; +using UniSpy.Server.Chat.Aggregate; namespace UniSpy.Server.Chat.Handler.CmdHandler.Channel { @@ -16,14 +15,11 @@ namespace UniSpy.Server.Chat.Handler.CmdHandler.Channel /// Game will only join one channel at one time /// - public sealed class JoinHandler : LogedInHandlerBase + public sealed class JoinHandler : ChannelHandlerBase { private new JoinRequest _request => (JoinRequest)base._request; private new JoinResult _result { get => (JoinResult)base._result; set => base._result = value; } private new JoinResponse _response { get => (JoinResponse)base._response; set => base._response = value; } - private static readonly GeneralMessageChannel GeneralMessageChannel = new GeneralMessageChannel(); - private Aggregate.Channel _channel; - private ChannelUser _user; public JoinHandler(IShareClient client, JoinRequest request) : base(client, request) { _result = new JoinResult(); @@ -38,7 +34,11 @@ public JoinHandler(IShareClient client, JoinRequest request) : base(client, requ //发送频道用户列表给此用户 protected override void RequestCheck() { - base.RequestCheck(); + if (!_client.Info.IsLoggedIn) + { + new Chat.Exception($"{_client.Info.NickName} Please login first!"); + } + _request.Parse(); //some GameSpy game only allow one player join one chat room //but GameSpy Arcade can join more than one channel if (_client.Info.JoinedChannels.Count > 3) @@ -53,34 +53,53 @@ protected override void RequestCheck() protected override void DataOperation() { - lock (ChannelManager.Channels) + // we acquire redis lock + // redis lock + var key = new Aggregate.Redis.ChannelCache { - var isChannelExistOnLocal = ChannelManager.IsChannelExist(_request.ChannelName); - if (isChannelExistOnLocal) + ChannelName = _request.ChannelName, + GameName = _client.Info.GameName + }; + using (var locker = new LinqToRedis.RedisLock(TimeSpan.FromSeconds(10), Application.StorageOperation.Persistance.ChannelCacheClient.Db, key)) + { + if (locker.LockTake()) { - _channel = ChannelManager.GetChannel(_request.ChannelName); - _user = _channel.AddUser(_client, _request.Password ?? null); + _channel = Aggregate.Channel.GetLocalChannel(_request.ChannelName); + // if local channel is null + if (_channel is null) + { + _channel = Aggregate.Channel.GetChannelCache(key); + } + // if remote channel is null + if (_channel is null) + { + // create channel + _channel = Aggregate.Channel.CreateLocalChannel(_request.ChannelName, _client, _request.Password); + // we need to check whether this channel is gamespy official channel + switch (_channel.RoomType) + { + case PeerRoomType.Title: + case PeerRoomType.Group: + _user = _channel.AddUser(_client, _request.Password ?? null); + break; + case PeerRoomType.Normal: + case PeerRoomType.Staging: + _user = _channel.AddUser(_client, _request.Password ?? null, true, true); + break; + } + } + else + { + _user = _channel.AddUser(_client, _request.Password ?? null); + } + Aggregate.Channel.UpdateChannelCache(_user, _channel); } else { - // create channel - _channel = ChannelManager.CreateChannel(_request.ChannelName, _request.Password ?? null, _client); - // we need to check whether this channel is gamespy official channel - switch (_channel.RoomType) - { - case PeerRoomType.Title: - case PeerRoomType.Staging: - case PeerRoomType.Group: - _user = _channel.AddUser(_client, _request.Password ?? null); - break; - case PeerRoomType.Normal: - _user = _channel.AddUser(_client, _request.Password ?? null, true, true); - break; - } + throw new BadChannelKeyException("The channel is created by other person, try to re-join this channel", _request.ChannelName); } - Aggregate.Channel.UpdateChannelCache(_user); - // Aggregate.Channel.UpdatePeerRoomInfo(_user); } + _result.AllChannelUserNicks = _channel.GetAllUsersNickString(); _result.JoinerNickName = _client.Info.NickName; _result.ChannelModes = _channel.Mode.ToString(); @@ -113,8 +132,8 @@ protected override void Response() { RequestType = ModeRequestType.GetChannelAndUserModes, ChannelName = _request.ChannelName, - NickName = _user.Info.NickName, - UserName = _user.Info.UserName, + NickName = _user.Client.Info.NickName, + UserName = _user.Client.Info.UserName, Password = _request.Password is null ? null : _request.Password }; new ModeHandler(_client, userModeRequest).Handle(); diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/KickHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/KickHandler.cs index 986fd109d..984ebabdd 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/KickHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/KickHandler.cs @@ -38,7 +38,7 @@ protected override void DataOperation() _result.ChannelName = _channel.Name; _result.KickerNickName = _client.Info.NickName; _result.KickerIRCPrefix = _client.Info.IRCPrefix; - _result.KickeeNickName = _kickee.Info.NickName; + _result.KickeeNickName = _kickee.Client.Info.NickName; } protected override void ResponseConstruct() { @@ -49,7 +49,7 @@ protected override void Response() { _channel.MultiCast(_client, _response); _channel.RemoveUser(_kickee); - Aggregate.Channel.UpdateChannelCache(_user); + Aggregate.Channel.UpdateChannelCache(_user,_channel); // Aggregate.Channel.UpdatePeerRoomInfo(_user); } } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/NamesHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/NamesHandler.cs index f0b8898a8..ab37f4d0a 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/NamesHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/NamesHandler.cs @@ -18,7 +18,7 @@ protected override void RequestCheck() { if (_request.RawRequest is null) { - _channel = _client.Info.GetJoinedChannel(_request.ChannelName); + _channel = _client.Info.GetLocalJoinedChannel(_request.ChannelName); if (_channel is null) { throw new NoSuchChannelException($"No such channel {_request.ChannelName}", _request.ChannelName); @@ -37,7 +37,7 @@ protected override void DataOperation() _result = new NamesResult(); _result.AllChannelUserNicks = _channel.GetAllUsersNickString(); _result.ChannelName = _channel.Name; - _result.RequesterNickName = _user.Info.NickName; + _result.RequesterNickName = _user.Client.Info.NickName; } protected override void ResponseConstruct() { diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs index 0a0188d57..a64f5116b 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs @@ -6,7 +6,6 @@ using UniSpy.Server.Chat.Contract.Response.Channel; using UniSpy.Server.Chat.Contract.Result.Channel; using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.QueryReport.Aggregate.Redis.Channel; namespace UniSpy.Server.Chat.Handler.CmdHandler.Channel { @@ -24,7 +23,7 @@ protected override void RequestCheck() { if (_request.RawRequest is null) { - _channel = _client.Info.GetJoinedChannel(_request.ChannelName); + _channel = _client.Info.GetLocalJoinedChannel(_request.ChannelName); if (_channel is null) { throw new NoSuchChannelException($"No such channel {_request.ChannelName}", _request.ChannelName); @@ -43,7 +42,7 @@ protected override void RequestCheck() protected override void DataOperation() { _result = new PartResult(); - _result.LeaverIRCPrefix = _user.Info.IRCPrefix; + _result.LeaverIRCPrefix = _user.Client.Info.IRCPrefix; _result.ChannelName = _channel.Name; switch (_channel.RoomType) { @@ -51,38 +50,36 @@ protected override void DataOperation() case PeerRoomType.Staging: if (_user.IsChannelCreator) { + switch (_channel.RoomType) + { + case PeerRoomType.Normal: + case PeerRoomType.Staging: + Aggregate.Channel.RemoveLocalChannel(_channel); + Aggregate.Channel.RemoveChannelCache(_user, _channel); + break; + } foreach (var user in _channel.Users.Values) { // we do not need to send part message to leaver - if (user.Info.NickName == _user.Info.NickName) + if (user.Client.Info.NickName == _user.Client.Info.NickName) { continue; } // We create a new KICKHandler to handle KICK operation for us var kickRequest = new KickRequest { - KickeeNickName = user.Info.NickName, + KickeeNickName = user.Client.Info.NickName, ChannelName = _channel.Name, Reason = _request.Reason, }; new KickHandler(_client, kickRequest).Handle(); } - ChannelManager.RemoveChannel(_channel.Name); - _channel.MessageBroker.Unsubscribe(); - switch (_channel.RoomType) - { - case PeerRoomType.Normal: - case PeerRoomType.Staging: - var chanInfo = _channel.GetChannelCache(); - QueryReport.Application.StorageOperation.RemoveChannel(chanInfo); - break; - } } goto default; default: // we need always remove the connection in leaver and channel _channel.RemoveUser(_user); - Aggregate.Channel.UpdateChannelCache(_user); + Aggregate.Channel.UpdateChannelCache(_user, _channel); // Aggregate.Channel.UpdatePeerRoomInfo(_user); break; } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetChannelKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetChannelKeyHandler.cs index 0280e4aba..4056adeb9 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetChannelKeyHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetChannelKeyHandler.cs @@ -28,7 +28,7 @@ protected override void DataOperation() _channel.KeyValues.Update(_request.KeyValues); _result.ChannelName = _result.ChannelName; - _result.ChannelUserIRCPrefix = _user.Info.IRCPrefix; + _result.ChannelUserIRCPrefix = _user.Client.Info.IRCPrefix; } protected override void ResponseConstruct() { diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/CryptHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/CryptHandler.cs index b7d70bdd8..6638135f1 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/CryptHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/CryptHandler.cs @@ -24,7 +24,7 @@ public CryptHandler(IShareClient client, CryptRequest request) : base(client, re protected override void DataOperation() { // we do not use crypto for remote client - if (!_client.Info.IsRemoteClient) + if (!_client.IsRemoteClient) { string secretKey = DataOperationExtensions.GetSecretKey(_request.GameName); if (secretKey is null) @@ -46,7 +46,7 @@ protected override void ResponseConstruct() protected override void Response() { base.Response(); - if (!_client.Info.IsRemoteClient) + if (!_client.IsRemoteClient) { ((Client)_client).Crypto = _crypto; } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/GetKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/GetKeyHandler.cs index 6b1609ba3..89621937d 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/GetKeyHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/GetKeyHandler.cs @@ -33,9 +33,10 @@ protected override void DataOperation() else { _result.NickName = _request.NickName; - var target = ClientManager.GetClientByNickName(_request.NickName); - _result.Values.Add(_client.Info.KeyValues.GetValueString(_request.Keys)); + } + var target = ClientManager.GetClientByNickName(_request.NickName); + _result.Values.Add(target.Info.KeyValues.GetValueString(_request.Keys)); } protected override void ResponseConstruct() diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/InviteHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/InviteHandler.cs index 32cb6b554..2b7e92add 100644 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/InviteHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/InviteHandler.cs @@ -18,7 +18,7 @@ protected override void RequestCheck() { throw new Chat.Exception("To invite user, you must in the channel."); } - var chan = ChannelManager.GetChannel(_request.ChannelName); + var chan = Aggregate.Channel.GetLocalChannel(_request.ChannelName); chan.Mode.InviteNickNames.Add(_request.NickName); } } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/ListHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/ListHandler.cs index dac847a66..8873dd5d8 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/ListHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/ListHandler.cs @@ -21,7 +21,7 @@ public ListHandler(IShareClient client, ListRequest request) : base(client, requ protected override void DataOperation() { //add list response header - foreach (var channel in ChannelManager.Channels.Values) + foreach (var channel in Aggregate.Channel.LocalChannels.Values) { //TODO //add channel information here diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs index caa7c56ac..d56e81858 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs @@ -1,3 +1,4 @@ +using System; using System.Linq; using UniSpy.Server.Chat.Abstraction.BaseClass; using UniSpy.Server.Chat.Application; @@ -6,6 +7,7 @@ using UniSpy.Server.Chat.Contract.Response.General; using UniSpy.Server.Chat.Abstraction.Interface; using UniSpy.Server.Chat.Contract.Result.General; +using UniSpy.Server.Chat.Aggregate.Redis; namespace UniSpy.Server.Chat.Handler.CmdHandler.General { @@ -14,6 +16,7 @@ public sealed class NickHandler : CmdHandlerBase { private new NickRequest _request => (NickRequest)base._request; private new NickResult _result { get => (NickResult)base._result; set => base._result = value; } + private string _tempNickName; public NickHandler(IShareClient client, NickRequest request) : base(client, request) { _result = new NickResult(); @@ -39,25 +42,7 @@ private void SetUniqueNickAsNickName() { postFix = _client.Info.GameName.Substring(0, 2); } - _client.Info.NickName = $"{_client.Info.UniqueNickName}-{postFix}"; - } - private void SuggestNewNickName() - { - int number = 0; - string validNickName; - while (true) - { - string newNickName = _request.NickName + number; - if (ClientManager.ClientPool.Values.Count(c => ((ClientInfo)(c.Info)).NickName == _request.NickName) == 0) - { - validNickName = newNickName; - break; - } - } - throw new NickNameInUseException( - $"The nick name: {_request.NickName} is already in use", - _request.NickName, - validNickName); + _tempNickName = $"{_client.Info.UniqueNickName}-{postFix}"; } protected override void DataOperation() { @@ -66,14 +51,32 @@ protected override void DataOperation() //client using its - as his nickname in chat SetUniqueNickAsNickName(); } - else if (ClientManager.ClientPool.Values.Count(c => ((ClientInfo)(c.Info)).NickName == _request.NickName) == 0) + else { - _client.Info.NickName = _request.NickName; + _tempNickName = _request.NickName; } - else + var key = new ClientInfoCache { NickName = _tempNickName }; + using (var locker = new LinqToRedis.RedisLock(TimeSpan.FromSeconds(10), Application.StorageOperation.Persistance.ClientCacheClient.Db, key)) { - SuggestNewNickName(); + if (locker.LockTake()) + { + if (!Application.StorageOperation.Persistance.IsClientExist(key)) + { + _client.Info.NickName = _request.NickName; + Application.StorageOperation.Persistance.UpdateClient((Client)_client); + } + else + { + throw new NickNameInUseException(); + } + } + else + { + throw new NickNameInUseException(); + } + } + _result.NickName = _client.Info.NickName; } protected override void ResponseConstruct() diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/QuitHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/QuitHandler.cs index ceb034d31..82d4ef714 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/QuitHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/QuitHandler.cs @@ -4,7 +4,6 @@ using UniSpy.Server.Chat.Contract.Request.Channel; using UniSpy.Server.Chat.Contract.Request.General; using UniSpy.Server.Chat.Handler.CmdHandler.Channel; -using UniSpy.Server.Core.Abstraction.BaseClass; namespace UniSpy.Server.Chat.Handler.CmdHandler.General { @@ -27,7 +26,7 @@ protected override void DataOperation() { foreach (var channel in _client.Info.JoinedChannels.Values) { - ChannelUser user = channel.GetUser(_client); + var user = channel.GetUser(_client); if (user is null) { continue; @@ -42,11 +41,6 @@ protected override void DataOperation() // client is loged out _client.Info.IsLoggedIn = false; } - // we remove client from ClientManager - if (_client.Info.IsRemoteClient) - { - ClientManagerBase.RemoveClient(_client.Connection.RemoteIPEndPoint); - } } } } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs index 126754139..602c10ae7 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs @@ -40,19 +40,20 @@ protected override void DataOperation() private void GetChannelUsersInfo() { - if (!ChannelManager.IsChannelExist(_request.ChannelName)) + + var channel = Aggregate.Channel.GetLocalChannel(_request.ChannelName); + if (channel is null) { - throw new IRCChannelException($"The channel is not exist.", IRCErrorCode.NoSuchChannel, _request.ChannelName); + throw new IRCChannelException($"Channel do not exist.", IRCErrorCode.NoSuchChannel, _request.ChannelName); } - var channel = ChannelManager.GetChannel(_request.ChannelName); foreach (var user in channel.Users.Values) { var data = new WhoDataModel { ChannelName = channel.Name, - UserName = user.Info.UserName, - NickName = user.Info.NickName, - PublicIPAddress = user.Connection.RemoteIPEndPoint.Address.ToString(), + UserName = user.Client.Info.UserName, + NickName = user.Client.Info.NickName, + PublicIPAddress = user.Client.Connection.RemoteIPEndPoint.Address.ToString(), Modes = user.Modes }; _result.DataModels.Add(data); diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs index 1ae99bfb6..5c4b96016 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs @@ -33,7 +33,7 @@ protected override void ChannelMessageDataOpration() { return; } - // if (_user.Info.IsQuietMode) + // if (_user.Client.Info.IsQuietMode) // { // return; // } diff --git a/src/Servers/Chat/src/UniSpy.Server.Chat.csproj b/src/Servers/Chat/src/UniSpy.Server.Chat.csproj index d42e6c35f..8b9381e25 100755 --- a/src/Servers/Chat/src/UniSpy.Server.Chat.csproj +++ b/src/Servers/Chat/src/UniSpy.Server.Chat.csproj @@ -17,7 +17,6 @@ - \ No newline at end of file diff --git a/src/Servers/Chat/test/Channel/ChannelHandlerTest.cs b/src/Servers/Chat/test/Channel/ChannelHandlerTest.cs index 19d41d74a..a171ce893 100644 --- a/src/Servers/Chat/test/Channel/ChannelHandlerTest.cs +++ b/src/Servers/Chat/test/Channel/ChannelHandlerTest.cs @@ -1,3 +1,4 @@ +using UniSpy.Server.Chat.Aggregate.Redis; using UniSpy.Server.Chat.Application; using UniSpy.Server.Chat.Contract.Request.Channel; using UniSpy.Server.Chat.Contract.Request.General; @@ -5,7 +6,6 @@ using UniSpy.Server.Chat.Handler.CmdHandler.Channel; using UniSpy.Server.Chat.Handler.CmdHandler.General; using UniSpy.Server.Chat.Handler.CmdHandler.Message; -using UniSpy.Server.QueryReport.Aggregate.Redis.Channel; using Xunit; namespace UniSpy.Server.Chat.Test.Channel @@ -15,9 +15,9 @@ public class ChannelHandlerTest [Fact] public void RoomTypeTest() { - Assert.True(PeerRoom.GetRoomType("#GPG!622") == QueryReport.Aggregate.Redis.Channel.PeerRoomType.Group); - Assert.True(PeerRoom.GetRoomType("#GSP!worms3!Ml4lz344lM") == QueryReport.Aggregate.Redis.Channel.PeerRoomType.Staging); - Assert.True(PeerRoom.GetRoomType("#islanbul") == QueryReport.Aggregate.Redis.Channel.PeerRoomType.Normal); + Assert.True(PeerRoom.GetRoomType("#GPG!622") == Chat.Aggregate.PeerRoomType.Group); + Assert.True(PeerRoom.GetRoomType("#GSP!worms3!Ml4lz344lM") == Chat.Aggregate.PeerRoomType.Staging); + Assert.True(PeerRoom.GetRoomType("#islanbul") == Chat.Aggregate.PeerRoomType.Normal); } [Fact] public void JoinHandleTest() diff --git a/src/Servers/Chat/test/Game/GameTest.cs b/src/Servers/Chat/test/Game/GameTest.cs index 2633a188f..d4bb70790 100644 --- a/src/Servers/Chat/test/Game/GameTest.cs +++ b/src/Servers/Chat/test/Game/GameTest.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using System.Linq; -using UniSpy.Server.Chat.Aggregate; using UniSpy.Server.Chat.Application; using UniSpy.Server.Chat.Handler; using UniSpy.Server.Core.Abstraction.Interface; @@ -186,8 +185,8 @@ public void Worm3dTest20220613() { ((ITestClient)client2).TestReceived(UniSpyEncoding.GetBytes(raw)); } - var userCount = ChannelManager.Channels.First().Value.Users.Count; - Assert.Equal(2, userCount); + // var userCount = ChannelManager.Channels.First().Value.Users.Count; + // Assert.Equal(2, userCount); int count1 = ((Client)client1).Info.JoinedChannels.Count(); Assert.Equal(2, count1); int count2 = ((Client)client2).Info.JoinedChannels.Count(); diff --git a/src/Servers/Chat/test/MockObject.cs b/src/Servers/Chat/test/MockObject.cs index 3c38a8b16..5db332c77 100644 --- a/src/Servers/Chat/test/MockObject.cs +++ b/src/Servers/Chat/test/MockObject.cs @@ -1,3 +1,4 @@ +using System.Linq; using System.Net; using Moq; using UniSpy.Server.Chat.Application; @@ -17,6 +18,9 @@ public static IClient CreateClient(string ipAddress = "79.209.235.252", int port var serverMock = new Chat.Application.Server(managerMock.Object); var client = new Client(connectionMock.Object, serverMock); client.Info.GameName = "gmtest"; + ClientManager.ClientPool.TryAdd(client.Connection.RemoteIPEndPoint, client); + // if(ServerLauncher.ServerInstances.Count(s=>s.id)) + // ServerLauncher.ServerInstances.Add(serverMock); return client; } } diff --git a/src/Servers/Chat/test/RedisChatChannelTest.cs b/src/Servers/Chat/test/RedisChatChannelTest.cs index 6772b1284..3323c1524 100644 --- a/src/Servers/Chat/test/RedisChatChannelTest.cs +++ b/src/Servers/Chat/test/RedisChatChannelTest.cs @@ -6,7 +6,6 @@ using UniSpy.Server.Chat.Test.Channel; using UniSpy.Server.Chat.Handler.CmdHandler.Channel; using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Chat.Aggregate.Redis; using UniSpy.Server.Core.Encryption; using UniSpy.Server.Chat.Contract.Request.General; using UniSpy.Server.Chat.Test.General; @@ -63,8 +62,8 @@ public void RedisChannel() var message = new RemoteMessage(request, remoteClient); var msgStr = JsonConvert.SerializeObject(message); var msgObj = JsonConvert.DeserializeObject(msgStr); - var chan = new GeneralMessageChannel(); - chan.ReceivedMessage(msgObj); + // var chan = new GeneralMessageChannel(); + // chan.ReceivedMessage(msgObj); } [Fact] public void Crypt() @@ -79,8 +78,8 @@ public void Crypt() var request = new CryptRequest(GeneralRequests.Crypt); request.Parse(); var message = new RemoteMessage(request, remoteClient); - var chan = new GeneralMessageChannel(); - chan.ReceivedMessage(message); + // var chan = new GeneralMessageChannel(); + // chan.ReceivedMessage(message); } [Fact] public void GeneralTest() @@ -160,11 +159,11 @@ public void ChannelInfoTest() var client = MockObject.CreateClient() as Client; client.Info.IsLoggedIn = true; client.Info.NickName = "xiaojiuwo"; - var channel = ChannelManager.CreateChannel("xiaojiuwo", creator: client); + var channel = Aggregate.Channel.CreateLocalChannel("xiaojiuwo", creator: client); var user = channel.GetUser(client); - var channelStr = JsonConvert.SerializeObject(channel.GetChannelCache()); - var channelObj = JsonConvert.DeserializeObject(channelStr); + var channelStr = JsonConvert.SerializeObject(channel); + var channelObj = JsonConvert.DeserializeObject(channelStr); } } } diff --git a/src/Servers/NatNegotiation/src/Abstraction/Interface/IStorageOperation.cs b/src/Servers/NatNegotiation/src/Abstraction/Interface/IStorageOperation.cs index 6d9b4c42a..d6deade8b 100644 --- a/src/Servers/NatNegotiation/src/Abstraction/Interface/IStorageOperation.cs +++ b/src/Servers/NatNegotiation/src/Abstraction/Interface/IStorageOperation.cs @@ -8,12 +8,12 @@ namespace UniSpy.Server.NatNegotiation.Abstraction.Interface { public interface IStorageOperation { - List GetAvaliableRelayServers(); - void UpdateInitInfo(NatAddressInfo info); + List GetAvaliableRelayServers(); + void UpdateInitInfo(InitPacketCache info); int CountInitInfo(uint cookie, byte version); - List GetInitInfos(Guid serverId, uint cookie); - void RemoveInitInfo(NatAddressInfo info); - void UpdateNatFailInfo(NatFailInfo info); - int GetNatFailInfo(NatFailInfo info); + List GetInitInfos(Guid serverId, uint cookie); + void RemoveInitInfo(InitPacketCache info); + void UpdateNatFailInfo(NatFailInfoCache info); + int GetNatFailInfo(NatFailInfoCache info); } } \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs index 222a22923..c536607f3 100644 --- a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs +++ b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs @@ -30,14 +30,16 @@ public ConnectionListener(IPEndPoint listeningEndPoint, uint cookie, List CheckExpiredClient(); // after create listener we start it Start(); LogWriter.LogDebug($"[{ListeningEndPoint}] gamespy client listener started."); } - private void OnInit() + public override bool Start() { - + _timer.Start(); + return base.Start(); } protected override void OnStarted() => ReceiveAsync(); private void CheckExpiredClient() diff --git a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelayServerInfo.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelayServerInfo.cs index 45111870c..22202265d 100644 --- a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelayServerInfo.cs +++ b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelayServerInfo.cs @@ -8,7 +8,7 @@ namespace UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay { - public record RelayServerInfo : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject + public record RelayServerCache : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject { [RedisKey] public Guid? ServerID { get; init; } @@ -17,13 +17,12 @@ public record RelayServerInfo : UniSpy.Server.Core.Abstraction.BaseClass.RedisKe public IPEndPoint PublicIPEndPoint { get; init; } public int ClientCount { get; set; } - public RelayServerInfo() : base(RedisDbNumber.GameTrafficRelay, TimeSpan.FromMinutes(1)) + public RelayServerCache() : base(RedisDbNumber.GameTrafficRelay, TimeSpan.FromMinutes(1)) { } - } - - public class RedisClient : UniSpy.Server.Core.Abstraction.BaseClass.RedisClient - { - public RedisClient() { } + public class RedisClient : UniSpy.Server.Core.Abstraction.BaseClass.RedisClient + { + public RedisClient() { } + } } } \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ServerStatusReporter.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ServerStatusReporter.cs index da0e482e1..abda6ac4a 100644 --- a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ServerStatusReporter.cs +++ b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ServerStatusReporter.cs @@ -6,20 +6,22 @@ namespace UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay public class ServerStatusReporter { private EasyTimer _myTimer; - private RedisClient _redisClient = new RedisClient(); + private RelayServerCache.RedisClient _redisClient = new(); private Core.Abstraction.Interface.IServer _server; public ServerStatusReporter(Core.Abstraction.Interface.IServer server) { _server = server; + _myTimer = new EasyTimer(TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(10)); + _myTimer.Elapsed += (s, e) => UpdateServerInfo(); UpdateServerInfo(); } public void Start() { - _myTimer = new EasyTimer(TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(10), UpdateServerInfo); + _myTimer.Start(); } private void UpdateServerInfo() { - var info = new RelayServerInfo() + var info = new RelayServerCache() { ServerID = _server.Id, PublicIPEndPoint = _server.PublicIPEndPoint, diff --git a/src/Servers/NatNegotiation/src/Aggregate/Redis/NatAddressInfo.cs b/src/Servers/NatNegotiation/src/Aggregate/Redis/InitPacketCache.cs similarity index 92% rename from src/Servers/NatNegotiation/src/Aggregate/Redis/NatAddressInfo.cs rename to src/Servers/NatNegotiation/src/Aggregate/Redis/InitPacketCache.cs index fdbac7f78..5f22b3742 100644 --- a/src/Servers/NatNegotiation/src/Aggregate/Redis/NatAddressInfo.cs +++ b/src/Servers/NatNegotiation/src/Aggregate/Redis/InitPacketCache.cs @@ -11,7 +11,7 @@ namespace UniSpy.Server.NatNegotiation.Aggregate.Redis { - public record NatAddressInfo : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject + public record InitPacketCache : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject { [RedisKey] public Guid? ServerID { get; init; } @@ -35,13 +35,19 @@ public record NatAddressInfo : UniSpy.Server.Core.Abstraction.BaseClass.RedisKey [JsonConverter(typeof(IPEndPointConverter))] public IPEndPoint PrivateIPEndPoint { get; init; } - public NatAddressInfo() : base(RedisDbNumber.NatAddressInfo, TimeSpan.FromMinutes(3)) + public InitPacketCache() : base(RedisDbNumber.NatAddressInfo, TimeSpan.FromMinutes(3)) { } + public class RedisClient : UniSpy.Server.Core.Abstraction.BaseClass.RedisClient + { + public RedisClient() + { + } + } } public record NatInitInfo { - public Dictionary AddressInfos { get; private set; } + public Dictionary AddressInfos { get; private set; } public uint Cookie => (uint)AddressInfos.Values.Last().Cookie; public byte Version => (byte)AddressInfos.Values.Last().Version; public NatClientIndex ClientIndex => (NatClientIndex)AddressInfos.Values.Last().ClientIndex; @@ -75,7 +81,7 @@ public NatType NatType } } } - private void ProcessVersion2(List infos) + private void ProcessVersion2(List infos) { if (!(AddressInfos.ContainsKey(NatPortType.NN1) && AddressInfos.ContainsKey(NatPortType.NN2))) @@ -110,7 +116,7 @@ private void ProcessVersion2(List infos) } } } - private void ProcessVersion3(List infos) + private void ProcessVersion3(List infos) { // todo // some game will not send GP packet to natneg server, currently do not know the reason of it, need more game for analysis. @@ -159,7 +165,7 @@ private void ProcessVersion3(List infos) } } - public NatInitInfo(List infos) + public NatInitInfo(List infos) { AddressInfos = infos.Select((i) => new { i }).ToDictionary(a => ((NatPortType)a.i.PortType), a => a.i); switch (this.Version) @@ -175,11 +181,4 @@ public NatInitInfo(List infos) } } } - - public class RedisClient : UniSpy.Server.Core.Abstraction.BaseClass.RedisClient - { - public RedisClient() - { - } - } } \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Aggregate/Redis/NatFailInfo.cs b/src/Servers/NatNegotiation/src/Aggregate/Redis/NatFailInfoCache.cs similarity index 70% rename from src/Servers/NatNegotiation/src/Aggregate/Redis/NatFailInfo.cs rename to src/Servers/NatNegotiation/src/Aggregate/Redis/NatFailInfoCache.cs index ac35a9074..723d1c464 100644 --- a/src/Servers/NatNegotiation/src/Aggregate/Redis/NatFailInfo.cs +++ b/src/Servers/NatNegotiation/src/Aggregate/Redis/NatFailInfoCache.cs @@ -12,7 +12,7 @@ namespace UniSpy.Server.NatNegotiation.Aggregate.Redis.Fail /// The information pair using to switch strategy /// /// - public record NatFailInfo : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject + public record NatFailInfoCache : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject { [RedisKey] [JsonConverter(typeof(IPAddresConverter))] @@ -20,8 +20,8 @@ public record NatFailInfo : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyVal [RedisKey] [JsonConverter(typeof(IPAddresConverter))] public IPAddress PublicIPAddress2 { get; init; } - public NatFailInfo() : base(RedisDbNumber.NatFailInfo, TimeSpan.FromDays(1)) { } - public NatFailInfo(NatInitInfo info1, NatInitInfo info2) : base(RedisDbNumber.NatFailInfo, TimeSpan.FromDays(1)) + public NatFailInfoCache() : base(RedisDbNumber.NatFailInfo, TimeSpan.FromDays(1)) { } + public NatFailInfoCache(NatInitInfo info1, NatInitInfo info2) : base(RedisDbNumber.NatFailInfo, TimeSpan.FromDays(1)) { // we need to store in sequence to make consistancy and reduce duplications if (info1.ClientIndex == NatClientIndex.GameClient) @@ -35,10 +35,9 @@ public NatFailInfo(NatInitInfo info1, NatInitInfo info2) : base(RedisDbNumber.Na PublicIPAddress2 = info1.PublicIPEndPoint.Address; } } - } - - public class RedisClient : UniSpy.Server.Core.Abstraction.BaseClass.RedisClient - { - public RedisClient() { } + public class RedisClient : UniSpy.Server.Core.Abstraction.BaseClass.RedisClient + { + public RedisClient() { } + } } } \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Application/Client.cs b/src/Servers/NatNegotiation/src/Application/Client.cs index 6a69047c3..60d775716 100644 --- a/src/Servers/NatNegotiation/src/Application/Client.cs +++ b/src/Servers/NatNegotiation/src/Application/Client.cs @@ -13,13 +13,14 @@ public class Client : ClientBase public Client(IConnection connection, IServer server) : base(connection, server) { Info = new ClientInfo(); + _timer = new EasyTimer(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(1)); IsLogRaw = true; } protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, (byte[])buffer); protected override void EventBinding() { ((IUdpConnection)Connection).OnReceive += OnReceived; - _timer = new EasyTimer(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(1), CheckExpiredClient); + _timer.Elapsed += (s, e) => CheckExpiredClient(); } } } \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Application/StorageOperation.cs b/src/Servers/NatNegotiation/src/Application/StorageOperation.cs index 3957daff5..470cfb813 100644 --- a/src/Servers/NatNegotiation/src/Application/StorageOperation.cs +++ b/src/Servers/NatNegotiation/src/Application/StorageOperation.cs @@ -3,6 +3,8 @@ using System.Linq; using UniSpy.Server.NatNegotiation.Abstraction.Interface; using UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay; +using UniSpy.Server.NatNegotiation.Aggregate.Redis; +using UniSpy.Server.NatNegotiation.Aggregate.Redis.Fail; namespace UniSpy.Server.NatNegotiation.Application { @@ -11,11 +13,11 @@ internal sealed class StorageOperation : IStorageOperation /// /// natneg init information redis server. /// - private static Aggregate.Redis.RedisClient _redisClient = new Aggregate.Redis.RedisClient(); - private static Aggregate.GameTrafficRelay.RedisClient _relayRedisClient = new Aggregate.GameTrafficRelay.RedisClient(); - private static NatNegotiation.Aggregate.Redis.Fail.RedisClient _natFailRedisClient = new NatNegotiation.Aggregate.Redis.Fail.RedisClient(); + private InitPacketCache.RedisClient _redisClient = new(); + private RelayServerCache.RedisClient _relayRedisClient = new(); + private NatFailInfoCache.RedisClient _natFailRedisClient = new(); public static IStorageOperation Persistance = new StorageOperation(); - public List GetAvaliableRelayServers() + public List GetAvaliableRelayServers() { return _relayRedisClient.Context.ToList(); } @@ -26,29 +28,29 @@ public int CountInitInfo(uint cookie, byte version) && k.Version == version); } - public List GetInitInfos(Guid serverId, uint cookie) + public List GetInitInfos(Guid serverId, uint cookie) { return _redisClient.Context.Where(k => k.ServerID == serverId && k.Cookie == cookie).ToList(); } - public void UpdateInitInfo(Aggregate.Redis.NatAddressInfo info) + public void UpdateInitInfo(Aggregate.Redis.InitPacketCache info) { _ = _redisClient.SetValueAsync(info); } - public void RemoveInitInfo(Aggregate.Redis.NatAddressInfo info) + public void RemoveInitInfo(Aggregate.Redis.InitPacketCache info) { _redisClient.DeleteKeyValue(info); } - public void UpdateNatFailInfo(Aggregate.Redis.Fail.NatFailInfo info) + public void UpdateNatFailInfo(Aggregate.Redis.Fail.NatFailInfoCache info) { _ = _natFailRedisClient.SetValueAsync(info); } - public int GetNatFailInfo(Aggregate.Redis.Fail.NatFailInfo info) + public int GetNatFailInfo(Aggregate.Redis.Fail.NatFailInfoCache info) { return _natFailRedisClient.Context.Where(i => i.PublicIPAddress1.Equals(info.PublicIPAddress1) && i.PublicIPAddress2.Equals(info.PublicIPAddress2)).Count(); } diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs index ca50542d1..7b6c63744 100755 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs +++ b/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs @@ -48,7 +48,7 @@ protected override void RequestCheck() // NOTE: If GTR is not used and both clients are on the same LAN, we need to use PrivateIPEndPoint. protected override void DataOperation() { - var info = new NatNegotiation.Aggregate.Redis.Fail.NatFailInfo(_myInitInfo, _othersInitInfo); + var info = new NatNegotiation.Aggregate.Redis.Fail.NatFailInfoCache(_myInitInfo, _othersInitInfo); NatStrategyType strategy; if (StorageOperation.Persistance.GetNatFailInfo(info) == 0) diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/InitHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/InitHandler.cs index ee6517e77..d4696d4b0 100755 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/InitHandler.cs +++ b/src/Servers/NatNegotiation/src/Handler/CmdHandler/InitHandler.cs @@ -19,14 +19,14 @@ public sealed class InitHandler : CmdHandlerBase /// /// Local NatInitInfo storage, after all init packets are received we send all into redis database /// - private NatAddressInfo _addressInfo; + private InitPacketCache _addressInfo; public InitHandler(Client client, InitRequest request) : base(client, request) { _result = new InitResult(); } protected override void DataOperation() { - _addressInfo = new NatAddressInfo() + _addressInfo = new InitPacketCache() { ServerID = _client.Server.Id, Cookie = (uint)_request.Cookie, diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ReportHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/ReportHandler.cs index 428536933..9be68c947 100755 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ReportHandler.cs +++ b/src/Servers/NatNegotiation/src/Handler/CmdHandler/ReportHandler.cs @@ -62,7 +62,7 @@ protected override void Response() if (!(bool)_request.IsNatSuccess) { //todo we save the fail information some where else. - var info = new NatNegotiation.Aggregate.Redis.Fail.NatFailInfo(_myInitInfo, _othersInitInfo); + var info = new NatNegotiation.Aggregate.Redis.Fail.NatFailInfoCache(_myInitInfo, _othersInitInfo); if (StorageOperation.Persistance.GetNatFailInfo(info) == 0) { StorageOperation.Persistance.UpdateNatFailInfo(info); diff --git a/src/Servers/NatNegotiation/test/GameTest.cs b/src/Servers/NatNegotiation/test/GameTest.cs index 5364fb07c..bbcab1c20 100644 --- a/src/Servers/NatNegotiation/test/GameTest.cs +++ b/src/Servers/NatNegotiation/test/GameTest.cs @@ -12,7 +12,7 @@ public class GameTest [Fact] public void Anno1701_20230209() { - new RedisClient().Db.Execute("FLUSHALL"); + new InitPacketCache.RedisClient().Db.Execute("FLUSHALL"); var clients = new Dictionary() { {"client1gp",MockObject.CreateClient("192.168.0.109",1111)}, @@ -64,7 +64,7 @@ public void Anno1701_20230127() /// public void GameSpySDK20221116() { - new RedisClient().Db.Execute("FLUSHDB"); + new InitPacketCache.RedisClient().Db.Execute("FLUSHDB"); var clients = new Dictionary() { {"client1gp",MockObject.CreateClient("192.168.0.109",1111)}, @@ -112,7 +112,7 @@ public void GameSpySDK20221116() /// public void NatPunchStrategy20221117() { - new RedisClient().Db.Execute("FLUSHDB"); + new InitPacketCache.RedisClient().Db.Execute("FLUSHDB"); var clients1 = new Dictionary() { {"client1gp",MockObject.CreateClient("192.168.0.109",1111)}, @@ -201,7 +201,7 @@ public void NatPunchStrategy20221117() public void NatFullConeTest() { // clean all stuff in database - new RedisClient().Db.Execute("FLUSHDB"); + new InitPacketCache.RedisClient().Db.Execute("FLUSHDB"); var client = MockObject.CreateClient("192.168.1.2", 9999); var server = MockObject.CreateClient("192.168.1.3", 9999); @@ -235,7 +235,7 @@ public void NatFullConeTest() public void NatConePortIncrement() { // clean all stuff in database - new RedisClient().Db.Execute("FLUSHDB"); + new InitPacketCache.RedisClient().Db.Execute("FLUSHDB"); var client = MockObject.CreateClient("192.168.1.2", 53935); var server = MockObject.CreateClient("192.168.1.3", 53935); @@ -307,7 +307,7 @@ public void Anno1701() [Fact] public void Flatout2pc20230519() { - new RedisClient().FlushDb(); + new InitPacketCache.RedisClient().FlushDb(); var client1GP = MockObject.CreateClient("91.43.63.201", 23756); var client1NN1 = MockObject.CreateClient("91.43.63.201", 64871); var client1NN2 = MockObject.CreateClient("91.43.63.201", 64871); diff --git a/src/Servers/NatNegotiation/test/NatDetectionTest.cs b/src/Servers/NatNegotiation/test/NatDetectionTest.cs index 6dc31b252..b01e30dbd 100644 --- a/src/Servers/NatNegotiation/test/NatDetectionTest.cs +++ b/src/Servers/NatNegotiation/test/NatDetectionTest.cs @@ -12,9 +12,9 @@ public class NatDetectionTest [Fact] public void PublicIPTest() { - var list = new List() + var list = new List() { - new NatAddressInfo() + new InitPacketCache() { PortType = NatPortType.GP, ClientIndex = NatClientIndex.GameClient, @@ -24,7 +24,7 @@ public void PublicIPTest() PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:1"), PrivateIPEndPoint = IPEndPoint.Parse("10.0.0.1:0") }, - new NatAddressInfo() + new InitPacketCache() { PortType = NatPortType.NN1, ClientIndex = NatClientIndex.GameClient, @@ -34,7 +34,7 @@ public void PublicIPTest() PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:2"), PrivateIPEndPoint = IPEndPoint.Parse("10.0.0.1:0") }, - new NatAddressInfo() + new InitPacketCache() { PortType = NatPortType.NN2, ClientIndex = NatClientIndex.GameClient, @@ -44,7 +44,7 @@ public void PublicIPTest() PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:2"), PrivateIPEndPoint = IPEndPoint.Parse("10.0.0.1:2") }, - new NatAddressInfo() + new InitPacketCache() { PortType = NatPortType.NN3, ClientIndex = NatClientIndex.GameClient, @@ -63,9 +63,9 @@ public void PublicIPTest() [Fact] public void FullConeTest() { - var list = new List() + var list = new List() { - new NatAddressInfo() + new InitPacketCache() { PortType = NatPortType.GP, ClientIndex = NatClientIndex.GameClient, @@ -75,7 +75,7 @@ public void FullConeTest() PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:1"), PrivateIPEndPoint = IPEndPoint.Parse("192.168.1.1:0") }, - new NatAddressInfo() + new InitPacketCache() { PortType = NatPortType.NN1, ClientIndex = NatClientIndex.GameClient, @@ -85,7 +85,7 @@ public void FullConeTest() PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:2"), PrivateIPEndPoint = IPEndPoint.Parse("192.168.1.1:0") }, - new NatAddressInfo() + new InitPacketCache() { PortType = NatPortType.NN2, ClientIndex = NatClientIndex.GameClient, @@ -95,7 +95,7 @@ public void FullConeTest() PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:2"), PrivateIPEndPoint = IPEndPoint.Parse("192.168.1.1:2") }, - new NatAddressInfo() + new InitPacketCache() { PortType = NatPortType.NN3, ClientIndex = NatClientIndex.GameClient, @@ -115,9 +115,9 @@ public void FullConeTest() [Fact] public void SymetricTest() { - var list = new List() + var list = new List() { - new NatAddressInfo() + new InitPacketCache() { PortType = NatPortType.GP, ClientIndex = NatClientIndex.GameClient, @@ -127,7 +127,7 @@ public void SymetricTest() PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:1"), PrivateIPEndPoint = IPEndPoint.Parse("192.168.1.1:1") }, - new NatAddressInfo() + new InitPacketCache() { PortType = NatPortType.NN1, ClientIndex = NatClientIndex.GameClient, @@ -137,7 +137,7 @@ public void SymetricTest() PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:2"), PrivateIPEndPoint = IPEndPoint.Parse("192.168.1.1:2") }, - new NatAddressInfo() + new InitPacketCache() { PortType = NatPortType.NN2, ClientIndex = NatClientIndex.GameClient, @@ -147,7 +147,7 @@ public void SymetricTest() PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:3"), PrivateIPEndPoint = IPEndPoint.Parse("192.168.1.1:2") }, - new NatAddressInfo() + new InitPacketCache() { PortType = NatPortType.NN3, ClientIndex = NatClientIndex.GameClient, @@ -171,9 +171,9 @@ public void NatStrategyTest() [Fact] public void SposiriusNetworkTest() { - var list = new List() + var list = new List() { - new NatAddressInfo() + new InitPacketCache() { PortType = NatPortType.GP, ClientIndex = NatClientIndex.GameClient, @@ -183,7 +183,7 @@ public void SposiriusNetworkTest() PublicIPEndPoint = IPEndPoint.Parse("91.52.105.210:51520"), PrivateIPEndPoint = IPEndPoint.Parse("192.168.0.60:0") }, - new NatAddressInfo() + new InitPacketCache() { PortType = NatPortType.NN1, ClientIndex = NatClientIndex.GameClient, @@ -192,7 +192,7 @@ public void SposiriusNetworkTest() GameName ="gmtest", PublicIPEndPoint = IPEndPoint.Parse("91.52.105.210:51521"), PrivateIPEndPoint = IPEndPoint.Parse("192.168.0.60:0") - },new NatAddressInfo() + },new InitPacketCache() { PortType = NatPortType.NN2, ClientIndex = NatClientIndex.GameClient, @@ -201,7 +201,7 @@ public void SposiriusNetworkTest() GameName ="gmtest", PublicIPEndPoint = IPEndPoint.Parse("91.52.105.210:49832"), PrivateIPEndPoint = IPEndPoint.Parse("192.168.0.60:49832") - },new NatAddressInfo() + },new InitPacketCache() { PortType = NatPortType.NN3, ClientIndex = NatClientIndex.GameClient, diff --git a/src/Servers/QueryReport/src/Abstraction/Interface/IStorageOperation.cs b/src/Servers/QueryReport/src/Abstraction/Interface/IStorageOperation.cs new file mode 100644 index 000000000..ec3f0b7fb --- /dev/null +++ b/src/Servers/QueryReport/src/Abstraction/Interface/IStorageOperation.cs @@ -0,0 +1,11 @@ +using System.Collections.Generic; +using UniSpy.Server.Chat.Aggregate; + +namespace UniSpy.Server.QueryReport.Abstraction.Interface +{ + public interface IStorageOperation + { + List GetPeerStagingChannel(string gameName, int groupId); + List GetPeerGroupChannel(int groupId); + } +} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/Application/Server.cs b/src/Servers/QueryReport/src/Application/Server.cs index 90704065d..fc6869c88 100644 --- a/src/Servers/QueryReport/src/Application/Server.cs +++ b/src/Servers/QueryReport/src/Application/Server.cs @@ -18,6 +18,7 @@ public Server(IConnectionManager manager) : base(manager) { } public override void Start() { base.Start(); + _ = Chat.Application.StorageOperation.Persistance.PeerGroupList; V2.Application.StorageOperation.NatNegChannel.Subscribe(); } protected override IClient CreateClient(IConnection connection) => new Client(connection, this); diff --git a/src/Servers/QueryReport/src/Application/StorageOperation.cs b/src/Servers/QueryReport/src/Application/StorageOperation.cs index 39b264810..e50132c45 100644 --- a/src/Servers/QueryReport/src/Application/StorageOperation.cs +++ b/src/Servers/QueryReport/src/Application/StorageOperation.cs @@ -1,30 +1,27 @@ using System.Collections.Generic; using System.Linq; -using UniSpy.Server.Core.Database.DatabaseModel; -using UniSpy.Server.QueryReport.Aggregate.Redis.Channel; +using UniSpy.Server.Chat.Aggregate; +using UniSpy.Server.Chat.Aggregate.Redis; +using UniSpy.Server.QueryReport.Abstraction.Interface; namespace UniSpy.Server.QueryReport.Application { - public class StorageOperation + public class StorageOperation : IStorageOperation { - /// - /// The peer group list in memory - /// - public static readonly Dictionary> PeerGroupList = QueryReport.V2.Application.StorageOperation.Persistance.GetAllGroupList(); - public static QueryReport.Aggregate.Redis.Channel.RedisClient _chatChannelRedisClient = new QueryReport.Aggregate.Redis.Channel.RedisClient(); - public static bool UpdateChannel(ChannelInfo channel) => _chatChannelRedisClient.SetValue(channel); - public static void RemoveChannel(ChannelInfo channel) => _chatChannelRedisClient.DeleteKeyValue(channel); - public static List GetPeerStagingChannel(string gameName, int groupId) + public static IStorageOperation Persistance = new StorageOperation(); + public List GetPeerStagingChannel(string gameName, int groupId) { var stagingName = $"{PeerRoom.StagingRoomPrefix}!{gameName}!*"; var groupName = $"{PeerRoom.GroupRoomPrefix}!{groupId}"; - var stagingRooms = QueryReport.Application.StorageOperation._chatChannelRedisClient.Context.Where(c => c.Name == stagingName && c.PreviousJoinedChannel == groupName).ToList(); + var stagingRooms = Chat.Application.StorageOperation.Persistance.ChannelCacheClient.Context + .Where(c => c.ChannelName == stagingName).ToList() + .Where(c => c.Channel.PreviousJoinedChannel == groupName).Select(c => c.Channel).ToList(); return stagingRooms; } - public static List GetPeerGroupChannel(int groupId) + public List GetPeerGroupChannel(int groupId) { var groupName = $"{PeerRoom.GroupRoomPrefix}!{groupId}"; - var groupRooms = QueryReport.Application.StorageOperation._chatChannelRedisClient.Context.Where(c => c.Name == groupName).ToList(); + var groupRooms = Chat.Application.StorageOperation.Persistance.ChannelCacheClient.Context.Where(c => c.ChannelName == groupName).ToList().Select(c => c.Channel).ToList(); return groupRooms; } } diff --git a/src/Servers/QueryReport/src/UniSpy.Server.QueryReport.csproj b/src/Servers/QueryReport/src/UniSpy.Server.QueryReport.csproj index 8b9381e25..958ec9895 100755 --- a/src/Servers/QueryReport/src/UniSpy.Server.QueryReport.csproj +++ b/src/Servers/QueryReport/src/UniSpy.Server.QueryReport.csproj @@ -18,5 +18,6 @@ + \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Abstraction/Interface/IStorageOperation.cs b/src/Servers/QueryReport/src/V1/Abstraction/Interface/IStorageOperation.cs index 6ba73d1c9..3fc7fa6f0 100644 --- a/src/Servers/QueryReport/src/V1/Abstraction/Interface/IStorageOperation.cs +++ b/src/Servers/QueryReport/src/V1/Abstraction/Interface/IStorageOperation.cs @@ -6,10 +6,10 @@ namespace UniSpy.Server.QueryReport.V1.Abstraction.Interface { public interface IStorageOperation { - List GetServersInfo(string gameName); + List GetServersInfo(string gameName); string GetGameSecretKey(string gameName); - GameServerInfo GetServerInfo(IPEndPoint endPoint); - void UpdateServerInfo(GameServerInfo info); + GameServerCache GetServerInfo(IPEndPoint endPoint); + void UpdateServerInfo(GameServerCache info); void RemoveServerInfo(IPEndPoint endPoint); } } \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Aggregation/Redis/GameServerInfo.cs b/src/Servers/QueryReport/src/V1/Aggregation/Redis/GameServerCache.cs similarity index 73% rename from src/Servers/QueryReport/src/V1/Aggregation/Redis/GameServerInfo.cs rename to src/Servers/QueryReport/src/V1/Aggregation/Redis/GameServerCache.cs index 5ba020292..90592c7dc 100644 --- a/src/Servers/QueryReport/src/V1/Aggregation/Redis/GameServerInfo.cs +++ b/src/Servers/QueryReport/src/V1/Aggregation/Redis/GameServerCache.cs @@ -9,7 +9,7 @@ namespace UniSpy.Server.QueryReport.V1.Aggregation.Redis { - public record GameServerInfo : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject + public record GameServerCache : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject { [RedisKey] public Guid? ServerID { get; set; } @@ -27,14 +27,14 @@ public record GameServerInfo : UniSpy.Server.Core.Abstraction.BaseClass.RedisKey /// The key values that contians all the information about this game server /// public Dictionary KeyValues { get; set; } - public GameServerInfo() : base(RedisDbNumber.GameServerV1, TimeSpan.FromSeconds(30)) + public GameServerCache() : base(RedisDbNumber.GameServerV1, TimeSpan.FromSeconds(30)) { } - } - public class RedisClient : RedisClient - { - public RedisClient() : base(ConfigManager.Config.Redis.RedisConnection) + public class RedisClient : RedisClient { + public RedisClient() : base(ConfigManager.Config.Redis.RedisConnection) + { + } } } } \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Application/StorageOperation.cs b/src/Servers/QueryReport/src/V1/Application/StorageOperation.cs index db46e31a6..32816863e 100644 --- a/src/Servers/QueryReport/src/V1/Application/StorageOperation.cs +++ b/src/Servers/QueryReport/src/V1/Application/StorageOperation.cs @@ -9,7 +9,7 @@ namespace UniSpy.Server.QueryReport.V1.Application { public class StorageOperation : IStorageOperation { - private static RedisClient _redisClient = new RedisClient(); + private static GameServerCache.RedisClient _redisClient = new(); public static IStorageOperation Persistance = new StorageOperation(); public string GetGameSecretKey(string gameName) { @@ -26,12 +26,12 @@ public string GetGameSecretKey(string gameName) return result.First().Secretkey; } } - public List GetServersInfo(string gameName) + public List GetServersInfo(string gameName) { var result = _redisClient.Context.Where(s => s.GameName == gameName).ToList(); return result; } - public GameServerInfo GetServerInfo(IPEndPoint endPoint) + public GameServerCache GetServerInfo(IPEndPoint endPoint) { var result = _redisClient.Context.Where(s => s.HostIPAddress == endPoint.Address && s.HostPort == endPoint.Port); if (result.Count() != 1) @@ -40,7 +40,7 @@ public GameServerInfo GetServerInfo(IPEndPoint endPoint) } return result.First(); } - public void UpdateServerInfo(GameServerInfo info) + public void UpdateServerInfo(GameServerCache info) { _ = _redisClient.SetValueAsync(info); } diff --git a/src/Servers/QueryReport/src/V1/Handler/CmdHandler/HeartbeatHandler.cs b/src/Servers/QueryReport/src/V1/Handler/CmdHandler/HeartbeatHandler.cs index fc4f44df4..699770969 100644 --- a/src/Servers/QueryReport/src/V1/Handler/CmdHandler/HeartbeatHandler.cs +++ b/src/Servers/QueryReport/src/V1/Handler/CmdHandler/HeartbeatHandler.cs @@ -19,7 +19,7 @@ protected override void DataOperation() var gameServerEnd = new IPEndPoint(_client.Connection.RemoteIPEndPoint.Address, _request.QueryReportPort); _client.Info.GameSecretKey = QueryReport.V1.Application.StorageOperation.Persistance.GetGameSecretKey(_request.GameName); // _result.Challenge = Challenge; - var info = new GameServerInfo() + var info = new GameServerCache() { ServerID = _client.Server.Id, HostIPAddress = _client.Connection.RemoteIPEndPoint.Address, diff --git a/src/Servers/QueryReport/src/V2/Abstraction/Interface/IStorageOperation.cs b/src/Servers/QueryReport/src/V2/Abstraction/Interface/IStorageOperation.cs index 926da73de..766903fc3 100644 --- a/src/Servers/QueryReport/src/V2/Abstraction/Interface/IStorageOperation.cs +++ b/src/Servers/QueryReport/src/V2/Abstraction/Interface/IStorageOperation.cs @@ -7,12 +7,11 @@ namespace UniSpy.Server.QueryReport.V2.Abstraction.Interface { public interface IStorageOperation { - List GetServerInfos(uint instantKey); - void UpdateGameServer(GameServerInfo info); - void RemoveGameServer(GameServerInfo info); - GameServerInfo GetGameServerInfo(IPEndPoint end); - List GetGameServerInfos(string gameName); - Dictionary> GetAllGroupList(); + List GetServerInfos(uint instantKey); + void UpdateGameServer(GameServerCache info); + void RemoveGameServer(GameServerCache info); + GameServerCache GetGameServerInfo(IPEndPoint end); + List GetGameServerInfos(string gameName); public void PublishClientMessage(ClientMessageRequest message); } diff --git a/src/Servers/QueryReport/src/V2/Aggregate/Redis/ChannelInfo.cs b/src/Servers/QueryReport/src/V2/Aggregate/Redis/ChannelInfo.cs index 216775df4..e69de29bb 100644 --- a/src/Servers/QueryReport/src/V2/Aggregate/Redis/ChannelInfo.cs +++ b/src/Servers/QueryReport/src/V2/Aggregate/Redis/ChannelInfo.cs @@ -1,118 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using UniSpy.LinqToRedis; -using UniSpy.Server.Core.Extension.Redis; - -namespace UniSpy.Server.QueryReport.Aggregate.Redis.Channel -{ - - public static class PeerRoom - { - /// - /// When game connects to the server, the player will enter the default channel for communicating with other players. - /// - public const string TitleRoomPrefix = "#GSP"; - /// - /// When a player creates their own game and is waiting for others to join they are placed in a separate chat room called the "staging room" - /// Staging rooms have two title seperator like #GSP!xxxx!xxxx - /// - public const string StagingRoomPrefix = "#GSP"; - /// - /// group rooms is used split the list of games into categories (by gametype, skill, region, etc.). In this case, when entering the title room, the user would get a list of group rooms instead of a list of games - /// Group room have one title seperator like #GPG!xxxxxx - /// - public const string GroupRoomPrefix = "#GPG"; - public const char TitleSeperator = '!'; - /// - /// Group room #GPG!622 - /// Staging room #GSP!worms3!Ml4lz344lM - /// Normal room #islanbul - /// - /// - /// - public static PeerRoomType GetRoomType(string channelName) - { - if (IsGroupRoom(channelName)) - { - // var c = StorageOperation.Persistance.IsPeerLobby(channelName); - - return PeerRoomType.Group; - } - else if (IsStagingRoom(channelName)) - { - return PeerRoomType.Staging; - } - else if (IsTitleRoom(channelName)) - { - return PeerRoomType.Title; - } - else - { - return PeerRoomType.Normal; - } - } - private static bool IsStagingRoom(string channelName) - { - var a = channelName.Count(c => c == TitleSeperator) == 2 ? true : false; - var b = channelName.StartsWith(StagingRoomPrefix, StringComparison.CurrentCultureIgnoreCase) ? true : false; - return a && b; - } - private static bool IsTitleRoom(string channelName) - { - var a = channelName.Count(c => c == TitleSeperator) == 1 ? true : false; - var b = channelName.StartsWith(TitleRoomPrefix, StringComparison.CurrentCultureIgnoreCase) ? true : false; - return a && b; - } - private static bool IsGroupRoom(string channelName) - { - - var a = channelName.Count(c => c == TitleSeperator) == 1 ? true : false; - var b = channelName.StartsWith(GroupRoomPrefix, StringComparison.CurrentCultureIgnoreCase) ? true : false; - return a && b; - } - } - public enum PeerRoomType - { - /// - /// The main room for a game. - /// - Title, - /// - /// A room where players meet before starting a game. - /// - Staging, - /// - /// A room which is, in general, for a particular type of gameplay (team, dm, etc.). - /// - Group, - /// - /// The normal room - /// - Normal - } - /// - /// The channel info that stored in redis - /// - public record ChannelInfo : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject - { - [RedisKey] - public Guid? ServerId { get; set; } - [RedisKey] - public string Name { get; set; } - [RedisKey] - public string PreviousJoinedChannel { get; set; } - public PeerRoomType? RoomType { get; set; } - public int? GroupId { get; set; } - public string RoomName { get; set; } - public string GameName { get; set; } - public int MaxNumberUser { get; set; } - public DateTime CreateTime { get; set; } - public List Users { get; set; } - public ChannelInfo() : base(RedisDbNumber.ChatChannel, TimeSpan.FromHours(1)) { } - } - public class RedisClient : UniSpy.Server.Core.Abstraction.BaseClass.RedisClient - { - public RedisClient() { } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V2/Aggregate/Redis/GameServerInfo.cs b/src/Servers/QueryReport/src/V2/Aggregate/Redis/GameServerInfo.cs index 51dda7397..130c46092 100644 --- a/src/Servers/QueryReport/src/V2/Aggregate/Redis/GameServerInfo.cs +++ b/src/Servers/QueryReport/src/V2/Aggregate/Redis/GameServerInfo.cs @@ -10,7 +10,7 @@ namespace UniSpy.Server.QueryReport.V2.Aggregate.Redis.GameServer { - public record GameServerInfo : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject + public record GameServerCache : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject { [RedisKey] public Guid? ServerID { get; set; } @@ -41,12 +41,12 @@ public record GameServerInfo : UniSpy.Server.Core.Abstraction.BaseClass.RedisKey public Dictionary ServerData { get; set; } public List> PlayerData { get; set; } public List> TeamData { get; set; } - public GameServerInfo() : base(RedisDbNumber.GameServerV2, TimeSpan.FromSeconds(30)) + public GameServerCache() : base(RedisDbNumber.GameServerV2, TimeSpan.FromSeconds(30)) { } - } - internal class RedisClient : UniSpy.Server.Core.Abstraction.BaseClass.RedisClient - { - public RedisClient() { } + internal class RedisClient : UniSpy.Server.Core.Abstraction.BaseClass.RedisClient + { + public RedisClient() { } + } } } \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V2/Aggregate/Redis/HeartBeatChannel.cs b/src/Servers/QueryReport/src/V2/Aggregate/Redis/HeartBeatChannel.cs index c33f0fc49..578c01835 100644 --- a/src/Servers/QueryReport/src/V2/Aggregate/Redis/HeartBeatChannel.cs +++ b/src/Servers/QueryReport/src/V2/Aggregate/Redis/HeartBeatChannel.cs @@ -4,7 +4,7 @@ namespace UniSpy.Server.QueryReport.V2.Aggregate.Redis { - public class HeartbeatChannel : RedisChannelBase + public class HeartbeatChannel : RedisChannelBase { public HeartbeatChannel() : base(RedisChannelName.HeartbeatChannel) { diff --git a/src/Servers/QueryReport/src/V2/Application/StorageOperation.cs b/src/Servers/QueryReport/src/V2/Application/StorageOperation.cs index 7ac37b6af..4489a6d1f 100644 --- a/src/Servers/QueryReport/src/V2/Application/StorageOperation.cs +++ b/src/Servers/QueryReport/src/V2/Application/StorageOperation.cs @@ -11,7 +11,7 @@ namespace UniSpy.Server.QueryReport.V2.Application { public sealed class StorageOperation : IStorageOperation { - private static QueryReport.V2.Aggregate.Redis.GameServer.RedisClient _gameServerRedisClient = new QueryReport.V2.Aggregate.Redis.GameServer.RedisClient(); + private static GameServerCache.RedisClient _gameServerRedisClient = new(); public static IStorageOperation Persistance = new StorageOperation(); public static NatNegChannel NatNegChannel = new NatNegChannel(); /// @@ -19,37 +19,16 @@ public sealed class StorageOperation : IStorageOperation /// We run subscribe() in SB, because SB need to receive message. /// public static HeartbeatChannel HeartbeatChannel = new HeartbeatChannel(); - public List GetServerInfos(uint instantKey) => _gameServerRedisClient.Context.Where(x => x.InstantKey == instantKey).ToList(); + public List GetServerInfos(uint instantKey) => _gameServerRedisClient.Context.Where(x => x.InstantKey == instantKey).ToList(); - public void RemoveGameServer(GameServerInfo info) => _gameServerRedisClient.DeleteKeyValue(info); - public void UpdateGameServer(GameServerInfo info) => _ = _gameServerRedisClient.SetValueAsync(info); - public List GetGameServerInfos(string gameName) + public void RemoveGameServer(GameServerCache info) => _gameServerRedisClient.DeleteKeyValue(info); + public void UpdateGameServer(GameServerCache info) => _ = _gameServerRedisClient.SetValueAsync(info); + public List GetGameServerInfos(string gameName) { return _gameServerRedisClient.Context.Where(x => x.GameName == gameName).ToList(); } - public Dictionary> GetAllGroupList() - { - using (var db = new UniSpyContext()) - { - var result = from g in db.Games - join gl in db.Grouplists on g.Gameid equals gl.Gameid - select new Grouplist - { - Game = g, - Gameid = g.Gameid, - Groupid = gl.Groupid, - Roomname = gl.Roomname - }; - var result2 = from g in result - group g by g.Game.Gamename into dd - select new KeyValuePair>(dd.Key, dd.ToList()); - - var data = result2.ToDictionary(x => x.Key, x => x.Value); - return data; - } - } - public GameServerInfo GetGameServerInfo(IPEndPoint end) + public GameServerCache GetGameServerInfo(IPEndPoint end) { return _gameServerRedisClient.Context.FirstOrDefault(x => x.HostIPAddress == end.Address & diff --git a/src/Servers/QueryReport/src/V2/Contract/Result/EchoResult.cs b/src/Servers/QueryReport/src/V2/Contract/Result/EchoResult.cs index ecabf9139..a314343da 100755 --- a/src/Servers/QueryReport/src/V2/Contract/Result/EchoResult.cs +++ b/src/Servers/QueryReport/src/V2/Contract/Result/EchoResult.cs @@ -5,7 +5,7 @@ namespace UniSpy.Server.QueryReport.V2.Contract.Result { public sealed class EchoResult : ResultBase { - public GameServerInfo Info { get; set; } + public GameServerCache Info { get; set; } public EchoResult() { } diff --git a/src/Servers/QueryReport/src/V2/Handler/CmdHandler/HeartBeatHandler.cs b/src/Servers/QueryReport/src/V2/Handler/CmdHandler/HeartBeatHandler.cs index d21e3811d..fa13f4e8e 100755 --- a/src/Servers/QueryReport/src/V2/Handler/CmdHandler/HeartBeatHandler.cs +++ b/src/Servers/QueryReport/src/V2/Handler/CmdHandler/HeartBeatHandler.cs @@ -14,7 +14,7 @@ public sealed class HeartBeatHandler : CmdHandlerBase { private new HeartBeatRequest _request => (HeartBeatRequest)base._request; private new HeartBeatResult _result { get => (HeartBeatResult)base._result; set => base._result = value; } - private GameServerInfo _gameServerInfo; + private GameServerCache _gameServerInfo; public HeartBeatHandler(Client client, HeartBeatRequest request) : base(client, request) { _result = new HeartBeatResult(); @@ -56,7 +56,7 @@ protected override void ResponseConstruct() private void CheckSpamGameServer() { // Ensures that an IP address creates a server for each game, we check if redis has multiple game servers - _gameServerInfo = new GameServerInfo() + _gameServerInfo = new GameServerCache() { ServerID = _client.Server.Id, HostIPAddress = _client.Connection.RemoteIPEndPoint.Address, diff --git a/src/Servers/ServerBrowser/src/V1/Contract/Result/ListResult.cs b/src/Servers/ServerBrowser/src/V1/Contract/Result/ListResult.cs index 3069c835f..a935594e9 100644 --- a/src/Servers/ServerBrowser/src/V1/Contract/Result/ListResult.cs +++ b/src/Servers/ServerBrowser/src/V1/Contract/Result/ListResult.cs @@ -7,7 +7,7 @@ namespace UniSpy.Server.ServerBrowser.V1.Contract.Result { public sealed class ListResult : ResultBase { - public List ServersInfo { get; set; } + public List ServersInfo { get; set; } public List PeerRoomsInfo { get; set; } public ListResult() { diff --git a/src/Servers/ServerBrowser/src/V2/Aggregate/HeartbeatChannel.cs b/src/Servers/ServerBrowser/src/V2/Aggregate/HeartbeatChannel.cs index c238af607..662bc6e64 100644 --- a/src/Servers/ServerBrowser/src/V2/Aggregate/HeartbeatChannel.cs +++ b/src/Servers/ServerBrowser/src/V2/Aggregate/HeartbeatChannel.cs @@ -10,7 +10,7 @@ public sealed class HeartbeatChannel : QueryReport.V2.Aggregate.Redis.HeartbeatC /// we do not run subscribe() in QR because QR only need to push /// We run subscribe() in SB, because SB need to receive message /// - public override void ReceivedMessage(GameServerInfo message) + public override void ReceivedMessage(GameServerCache message) { LogWriter.LogInfo($"Received game server message from QR:{message.ServerID}"); var handler = new AdHocHandler(message); diff --git a/src/Servers/ServerBrowser/src/V2/Aggregate/ServerInfoBuilder.cs b/src/Servers/ServerBrowser/src/V2/Aggregate/ServerInfoBuilder.cs index aef6f43b2..40582fff5 100644 --- a/src/Servers/ServerBrowser/src/V2/Aggregate/ServerInfoBuilder.cs +++ b/src/Servers/ServerBrowser/src/V2/Aggregate/ServerInfoBuilder.cs @@ -19,7 +19,7 @@ public static class ServerInfoBuilder /// /// /// - public static List BuildServerInfoHeader(GameServerFlags flag, GameServerInfo serverInfo) + public static List BuildServerInfoHeader(GameServerFlags flag, GameServerCache serverInfo) { List header = new List(); // add key flag @@ -42,7 +42,7 @@ public static List BuildServerInfoHeader(GameServerFlags flag, GameServerI // _serverListData.AddRange(header); return header; } - public static void CheckNatNegFlag(List header, GameServerInfo serverInfo) + public static void CheckNatNegFlag(List header, GameServerCache serverInfo) { if (serverInfo.ServerData.ContainsKey("natneg")) { @@ -54,7 +54,7 @@ public static void CheckNatNegFlag(List header, GameServerInfo serverInfo) } } } - public static void CheckUnsolicitedUdp(List header, GameServerInfo serverInfo) + public static void CheckUnsolicitedUdp(List header, GameServerCache serverInfo) { if (serverInfo.ServerData.ContainsKey("allow_unsolicited_udp")) { @@ -69,9 +69,9 @@ public static void CheckUnsolicitedUdp(List header, GameServerInfo serverI /// !when game create a channel chat, it will use both the public ip and private ip to build the name. /// !Known game: Worm3d /// - public static void CheckPrivateIP(List header, GameServerInfo serverInfo) + public static void CheckPrivateIP(List header, GameServerCache serverInfo) { - if (QueryReport.Application.StorageOperation.PeerGroupList.ContainsKey(serverInfo.GameName)) + if (Chat.Application.StorageOperation.Persistance.PeerGroupList.ContainsKey(serverInfo.GameName)) { // We already have the localip. Bytes are worng. if (serverInfo.ServerData.ContainsKey("localip0")) @@ -84,7 +84,7 @@ public static void CheckPrivateIP(List header, GameServerInfo serverInfo) } } } - public static void CheckNonStandardPort(List header, GameServerInfo serverInfo) + public static void CheckNonStandardPort(List header, GameServerCache serverInfo) { // !! only dedicated server have different query report port and host port // !! but peer server have same query report port and host port @@ -99,7 +99,7 @@ public static void CheckNonStandardPort(List header, GameServerInfo server /// /// !disabled because crysiswars localport is 64100 which is larger than short integer, need to find whether this function is needed. /// - public static void CheckNonStandardPrivatePort(List header, GameServerInfo serverInfo) + public static void CheckNonStandardPrivatePort(List header, GameServerCache serverInfo) { // we check private port here // if (serverInfo.ServerData.ContainsKey("localport")) @@ -113,7 +113,7 @@ public static void CheckNonStandardPrivatePort(List header, GameServerInfo // } // } } - public static void CheckICMPSupport(List header, GameServerInfo serverInfo) + public static void CheckICMPSupport(List header, GameServerCache serverInfo) { if (serverInfo.ServerData.ContainsKey("icmp_address")) { diff --git a/src/Servers/ServerBrowser/src/V2/Application/Server.cs b/src/Servers/ServerBrowser/src/V2/Application/Server.cs index 24038e8e9..d4e2dc53a 100644 --- a/src/Servers/ServerBrowser/src/V2/Application/Server.cs +++ b/src/Servers/ServerBrowser/src/V2/Application/Server.cs @@ -21,7 +21,7 @@ public override void Start() QueryReport.V2.Application.StorageOperation.HeartbeatChannel.OnReceived += ReceivedMessage; base.Start(); } - public void ReceivedMessage(QueryReport.V2.Aggregate.Redis.GameServer.GameServerInfo message) + public void ReceivedMessage(QueryReport.V2.Aggregate.Redis.GameServer.GameServerCache message) { LogWriter.LogInfo($"Received game server message from QR:{message.ServerID}"); var handler = new Handler.CmdHandler.AdHoc.AdHocHandler(message); diff --git a/src/Servers/ServerBrowser/src/V2/Contract/Result/AdHoc/AdHocResult.cs b/src/Servers/ServerBrowser/src/V2/Contract/Result/AdHoc/AdHocResult.cs index dcfc2730f..1bf9ed50c 100755 --- a/src/Servers/ServerBrowser/src/V2/Contract/Result/AdHoc/AdHocResult.cs +++ b/src/Servers/ServerBrowser/src/V2/Contract/Result/AdHoc/AdHocResult.cs @@ -5,7 +5,7 @@ namespace UniSpy.Server.ServerBrowser.V2.Contract.Result { public sealed class AdHocResult : ResultBase { - public GameServerInfo GameServerInfo { get; set; } + public GameServerCache GameServerInfo { get; set; } public AdHocResult() { } diff --git a/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/ServerMainListResult.cs b/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/ServerMainListResult.cs index d5e3790dd..a084f922b 100755 --- a/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/ServerMainListResult.cs +++ b/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/ServerMainListResult.cs @@ -6,10 +6,10 @@ namespace UniSpy.Server.ServerBrowser.V2.Contract.Result { public class ServerMainListResult : ServerListUpdateOptionResultBase { - public List GameServerInfos { get; set; } + public List GameServerInfos { get; set; } public ServerMainListResult() { - GameServerInfos = new List(); + GameServerInfos = new List(); } } } diff --git a/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/ServerNetworkInfoListResult.cs b/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/ServerNetworkInfoListResult.cs index 9c63feac3..31b4d5c00 100755 --- a/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/ServerNetworkInfoListResult.cs +++ b/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/ServerNetworkInfoListResult.cs @@ -6,10 +6,10 @@ namespace UniSpy.Server.ServerBrowser.V2.Contract.Result.ServerList public sealed class ServerNetworkInfoListResult : ServerMainListResult { - public List ServersInfo { get; private set; } + public List ServersInfo { get; private set; } public ServerNetworkInfoListResult() { - ServersInfo = new List(); + ServersInfo = new List(); } } } diff --git a/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/AdHoc/AdHocHandler.cs b/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/AdHoc/AdHocHandler.cs index 291c9986e..184d448b8 100644 --- a/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/AdHoc/AdHocHandler.cs +++ b/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/AdHoc/AdHocHandler.cs @@ -12,8 +12,8 @@ namespace UniSpy.Server.ServerBrowser.V2.Handler.CmdHandler.AdHoc { public class AdHocHandler : IHandler { - public GameServerInfo _message; - public AdHocHandler(GameServerInfo message) + public GameServerCache _message; + public AdHocHandler(GameServerCache message) { LogWriter.LogCurrentClass(this); _message = message; diff --git a/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/ServerList/ServerListHandler.cs b/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/ServerList/ServerListHandler.cs index 8f17c6e04..be6541cae 100644 --- a/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/ServerList/ServerListHandler.cs +++ b/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/ServerList/ServerListHandler.cs @@ -65,13 +65,13 @@ private void P2PGroupRoomList() { // first get the peer room in memory, if there is no such game we do not continue - if (!QueryReport.Application.StorageOperation.PeerGroupList.ContainsKey(_request.GameName)) + if (!Chat.Application.StorageOperation.Persistance.PeerGroupList.ContainsKey(_request.GameName)) { throw new ServerBrowser.Exception($"Invalid game name: {_request.GameName}."); } // Game name is unique in redis database - var grouplist = QueryReport.Application.StorageOperation.PeerGroupList[_request.GameName]; + var grouplist = Chat.Application.StorageOperation.Persistance.PeerGroupList[_request.GameName]; // we do not create peer room cache on redis, we just send peer room info to client var tempInfos = new List(); foreach (var group in grouplist) @@ -80,9 +80,9 @@ private void P2PGroupRoomList() var roomInfo = new PeerRoomInfo(group.Game.Gamename, group.Groupid, group.Roomname); tempInfos.Add(roomInfo); // get the channels info from redis where groupid equals above - var groupRooms = QueryReport.Application.StorageOperation.GetPeerGroupChannel(group.Groupid); + var groupRooms = QueryReport.Application.StorageOperation.Persistance.GetPeerGroupChannel(group.Groupid); // get the channels info from redis where created under gamename and groupid above - var stagingRooms = QueryReport.Application.StorageOperation.GetPeerStagingChannel(group.Game.Gamename, group.Groupid); + var stagingRooms = QueryReport.Application.StorageOperation.Persistance.GetPeerStagingChannel(group.Game.Gamename, group.Groupid); if (groupRooms.Count != 0) { roomInfo.NumberOfWaitingPlayers = groupRooms.Sum(r => r.Users.Count); @@ -101,7 +101,7 @@ private void P2PServerMainList() { var serverInfos = QueryReport.V2.Application.StorageOperation.Persistance.GetGameServerInfos(_request.GameName); ((ServerMainListResult)_result).Flag = GameServerFlags.HasKeysFlag; - var filteredGameServerInfos = new List(); + var filteredGameServerInfos = new List(); //TODO do filter if (_request.Filter is not null) { @@ -120,7 +120,7 @@ private void P2PServerMainList() { filteredGameServerInfos = serverInfos; } - + ((ServerMainListResult)_result).GameServerInfos = filteredGameServerInfos; } private void ServerMainList() diff --git a/src/Servers/ServerBrowser/test/UniSpy.Server.ServerBrowser.Test.csproj b/src/Servers/ServerBrowser/test/UniSpy.Server.ServerBrowser.Test.csproj index dd6f9415f..b69d73c28 100644 --- a/src/Servers/ServerBrowser/test/UniSpy.Server.ServerBrowser.Test.csproj +++ b/src/Servers/ServerBrowser/test/UniSpy.Server.ServerBrowser.Test.csproj @@ -28,6 +28,6 @@ - + \ No newline at end of file diff --git a/src/Servers/ServerBrowser/test/V2/PeerRoomTest.cs b/src/Servers/ServerBrowser/test/V2/PeerRoomTest.cs index d4f4c04b7..829396781 100644 --- a/src/Servers/ServerBrowser/test/V2/PeerRoomTest.cs +++ b/src/Servers/ServerBrowser/test/V2/PeerRoomTest.cs @@ -1,5 +1,5 @@ -using System; -using UniSpy.Server.QueryReport.Aggregate.Redis.Channel; +using UniSpy.Server.Chat.Abstraction.Interface; +using UniSpy.Server.Chat.Aggregate; using Xunit; namespace UniSpy.Server.ServerBrowser.Test.V2 @@ -9,13 +9,9 @@ public class PeerRoomTest [Fact] public void TestName() { - var info = new ChannelInfo() - { - PreviousJoinedChannel = "", - ServerId = Guid.NewGuid(), - Name = "#GPG!622" - }; - QueryReport.Application.StorageOperation.UpdateChannel(info); + var client = Chat.Test.MockObject.CreateClient(); + var chan = new Channel("Experts", (IShareClient)client); + Chat.Application.StorageOperation.Persistance.UpdateChannel(chan); } } } \ No newline at end of file diff --git a/src/Servers/WebServer/test/Auth/GameTest.cs b/src/Servers/WebServer/test/Auth/GameTest.cs index 18ac6355f..6e373ed54 100644 --- a/src/Servers/WebServer/test/Auth/GameTest.cs +++ b/src/Servers/WebServer/test/Auth/GameTest.cs @@ -7,33 +7,33 @@ public class GameTest [Fact] public void Crysis2Auth() { - var correct = @" - - - - 0 - 0529e96f6f937c4ccb05427782412d6febba9f8cd983f9245ce34a1446d711bceed0da10748a5420c290ffad9e959de906b2498698dc66ca2b63e2e6b02d344ba9f936b44571eed3f91bcec3154d3dbb258510d13b89ecea7be33a5690b51d6e35acc0fe04f1a41b3f0fd4dd35d9166d8c9d3921142fdc92f9967e927bc8c991 - - 0 - 1 - 95 - 95 - 67436 - 10040172 - 1687112622 - MyCrysis - asdf19 - D41D8CD98F00B204E9800998ECF8427E - aefb5064bbd1eb632fa8d57aab1c49366ce0ee3161cbef19f2b7971b63b811790ecbf6a47b34c55f65a0766b40c261c5d69c394cd320842dd2bccba883d30eae8fdba5d03b21b09bfc600dcb30b1b2f3fbe8077630b006dcb54c4254f14891762f72e7bbfe743eb8baf65f9e8c8d11ebe46f6b59e986b4c394cfbc2c8606e29f - 010001 - a75fbef2b50236c991e438efe81f0941a645a23d00e3f9bb409681e6c9b37e92a97c88afe65663081642c494c06860ee5dd45f9adab4d9caac3ed766717027eb76f8e6c6a30cbd32637abacfad0dcaf0e96948d20182766099a5133e89b92c498bc3470d1bab666d549e8af51edc2f009d63ecdff84551ed04138b6129adfd9f - 5F3E3EB96D4301D21E4F8375BB6FE4800F5CB27CD5B3D7DE49CB46B0B3E75A63A3D534AF7DA0FAB21B8C7A4EF56A316FACEECEAB153AD313876FC2A1895A151982897359328D06258B0125789FEBF928D117B7C8A342F8C40135708A17A65A3B189D3A0B7EBFEF21E1C435A409B1DA9449D6EF17D69BAAD844FADFFA30B227A8 - - - - "; + // var correct = @" + // + // + // + // 0 + // 0529e96f6f937c4ccb05427782412d6febba9f8cd983f9245ce34a1446d711bceed0da10748a5420c290ffad9e959de906b2498698dc66ca2b63e2e6b02d344ba9f936b44571eed3f91bcec3154d3dbb258510d13b89ecea7be33a5690b51d6e35acc0fe04f1a41b3f0fd4dd35d9166d8c9d3921142fdc92f9967e927bc8c991 + // + // 0 + // 1 + // 95 + // 95 + // 67436 + // 10040172 + // 1687112622 + // MyCrysis + // asdf19 + // D41D8CD98F00B204E9800998ECF8427E + // aefb5064bbd1eb632fa8d57aab1c49366ce0ee3161cbef19f2b7971b63b811790ecbf6a47b34c55f65a0766b40c261c5d69c394cd320842dd2bccba883d30eae8fdba5d03b21b09bfc600dcb30b1b2f3fbe8077630b006dcb54c4254f14891762f72e7bbfe743eb8baf65f9e8c8d11ebe46f6b59e986b4c394cfbc2c8606e29f + // 010001 + // a75fbef2b50236c991e438efe81f0941a645a23d00e3f9bb409681e6c9b37e92a97c88afe65663081642c494c06860ee5dd45f9adab4d9caac3ed766717027eb76f8e6c6a30cbd32637abacfad0dcaf0e96948d20182766099a5133e89b92c498bc3470d1bab666d549e8af51edc2f009d63ecdff84551ed04138b6129adfd9f + // 5F3E3EB96D4301D21E4F8375BB6FE4800F5CB27CD5B3D7DE49CB46B0B3E75A63A3D534AF7DA0FAB21B8C7A4EF56A316FACEECEAB153AD313876FC2A1895A151982897359328D06258B0125789FEBF928D117B7C8A342F8C40135708A17A65A3B189D3A0B7EBFEF21E1C435A409B1DA9449D6EF17D69BAAD844FADFFA30B227A8 + // + // + // + // "; // Given var raw = @" Date: Wed, 11 Oct 2023 21:14:31 +0800 Subject: [PATCH 047/231] fix(chat): message publish logic --- .../BaseClass/Channel/ChannelHandlerBase.cs | 24 ++++++++++++++--- .../ChannelProperty/ChannelUserRelated.cs | 4 +-- .../Chat/src/Aggregate/Redis/RedisChannel.cs | 4 +-- .../CmdHandler/Channel/GetCKeyHandler.cs | 5 +++- .../Channel/GetChannelKeyHandler.cs | 5 +++- .../Handler/CmdHandler/Channel/JoinHandler.cs | 7 ++--- .../Handler/CmdHandler/Channel/KickHandler.cs | 2 -- .../Handler/CmdHandler/Channel/ModeHandler.cs | 13 ++++++++- .../CmdHandler/Channel/NamesHandler.cs | 27 +++++++------------ .../Handler/CmdHandler/Channel/PartHandler.cs | 17 +++++++++--- src/Servers/Chat/test/Game/GameTest.cs | 5 ---- .../src/Handler/CmdHandler/InitHandler.cs | 9 +++---- 12 files changed, 75 insertions(+), 47 deletions(-) diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs index 86d525abb..939603a60 100755 --- a/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs +++ b/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs @@ -4,6 +4,8 @@ using UniSpy.Server.Core.Abstraction.Interface; using UniSpy.Server.Chat.Abstraction.Interface; using UniSpy.Server.Chat.Aggregate.Redis.Contract; +using System; +using UniSpy.Server.Chat.Aggregate.Redis; namespace UniSpy.Server.Chat.Abstraction.BaseClass { @@ -33,7 +35,10 @@ protected override void RequestCheck() { throw new NoSuchChannelException($"No such channel {_request.ChannelName}", _request.ChannelName); } - _user = _channel.GetUser(_client); + if (_user is null) + { + _user = _channel.GetUser(_client); + } if (_user is null) { throw new NoSuchNickException($"Can not find user with nickname: {_client.Info.NickName} username: {_client.Info.UserName}"); @@ -55,7 +60,7 @@ public override void Handle() /// /// publish message to redis channel, only localclient can publish message /// - protected void PublishMessage() + protected virtual void PublishMessage() { // we do not publish message when the message is received from remote client if (_client.IsRemoteClient) @@ -70,7 +75,20 @@ protected void PublishMessage() { return; } - Aggregate.Channel.UpdateChannelCache(_user, _channel); + + var key = new ChannelCache + { + ChannelName = _channel.Name, + GameName = _channel.GameName + }; + using (var locker = new LinqToRedis.RedisLock(TimeSpan.FromSeconds(10), Application.StorageOperation.Persistance.ChannelCacheClient.Db, key)) + { + if (locker.LockTake()) + { + Aggregate.Channel.UpdateChannelCache(_user, _channel); + } + } + var msg = new RemoteMessage(_request, _client.GetRemoteClient()); _channel.Broker.PublishMessage(msg); } diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs index d52ab41a3..bafb2df96 100644 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs +++ b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs @@ -1,6 +1,5 @@ using Newtonsoft.Json; using System.Collections.Concurrent; -using System.Collections.Generic; using System.Linq; using UniSpy.Server.Chat.Abstraction.Interface; using UniSpy.Server.Chat.Contract.Request.Channel; @@ -212,7 +211,8 @@ public void MultiCast(IClient sender, IResponse message, bool isSkipSender = fal var users = Users.Values.Where(u => !u.IsRemoteClient).ToList(); foreach (var user in users) { - if (isSkipSender) + if (isSkipSender + && sender.Connection.RemoteIPEndPoint.Equals(user.RemoteIPEndPoint)) { continue; } diff --git a/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs b/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs index 8225232f7..84cbde050 100644 --- a/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs +++ b/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs @@ -19,8 +19,8 @@ public ChannelMessageBroker(string chatChannelName) : base($"{RedisChannelName.C public override void ReceivedMessage(RemoteMessage message) { - // we are uint testing - if (ServerLauncher.Server is null) + // we are uint testing so we skip publish message here + if (ServerLauncher.ServerInstances.Count == 0) { return; } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetCKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetCKeyHandler.cs index 8159c2240..008676514 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetCKeyHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetCKeyHandler.cs @@ -72,7 +72,10 @@ protected override void ResponseConstruct() { _response = new GetCKeyResponse(_request, _result); } - + /// + /// We do not publish message in GetCKey + /// + protected override void PublishMessage() { } private void WaittingForKey(ChannelUser user) { // if user did not contains all key and value we wait for it diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetChannelKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetChannelKeyHandler.cs index 264b5dc47..b3890f3d1 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetChannelKeyHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetChannelKeyHandler.cs @@ -27,7 +27,10 @@ protected override void DataOperation() _result.Values = _channel.KeyValues.GetValueString(_request.Keys); _result.ChannelName = _channel.Name; } - + /// + /// We do not publish message in GetChanKey + /// + protected override void PublishMessage() { } protected override void ResponseConstruct() { _response = new GetChannelKeyResponse(_request, _result); diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs index bfd5ca197..8035df977 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs @@ -126,8 +126,8 @@ protected override void Response() { ChannelName = _request.ChannelName }; - new NamesHandler(_client, namesRequest).Handle(); - + var nameHandler = new NamesHandler(_client, namesRequest, _channel, _user); + nameHandler.Handle(); var userModeRequest = new ModeRequest { RequestType = ModeRequestType.GetChannelAndUserModes, @@ -136,7 +136,8 @@ protected override void Response() UserName = _user.Client.Info.UserName, Password = _request.Password is null ? null : _request.Password }; - new ModeHandler(_client, userModeRequest).Handle(); + var modeHandler = new ModeHandler(_client, userModeRequest, _channel, _user); + modeHandler.Handle(); } } } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/KickHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/KickHandler.cs index 984ebabdd..10c1374d5 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/KickHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/KickHandler.cs @@ -49,8 +49,6 @@ protected override void Response() { _channel.MultiCast(_client, _response); _channel.RemoveUser(_kickee); - Aggregate.Channel.UpdateChannelCache(_user,_channel); - // Aggregate.Channel.UpdatePeerRoomInfo(_user); } } } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/ModeHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/ModeHandler.cs index b809419cd..2b8625720 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/ModeHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/ModeHandler.cs @@ -15,6 +15,11 @@ public sealed class ModeHandler : ChannelHandlerBase private new ModeRequest _request => (ModeRequest)base._request; private new ModeResult _result { get => (ModeResult)base._result; set => base._result = value; } public ModeHandler(IShareClient client, ModeRequest request) : base(client, request) { } + public ModeHandler(IShareClient client, ModeRequest request, Aggregate.Channel channel, Aggregate.ChannelUser user) : base(client, request) + { + _user = user; + _channel = channel; + } protected override void DataOperation() { _result = new ModeResult(); @@ -33,7 +38,13 @@ protected override void DataOperation() break; } } - + protected override void PublishMessage() + { + if (_request.RequestType == ModeRequestType.SetChannelModes) + { + base.PublishMessage(); + } + } protected override void ResponseConstruct() { // we only response to get channel modes diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/NamesHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/NamesHandler.cs index ab37f4d0a..de0e17bfa 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/NamesHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/NamesHandler.cs @@ -1,6 +1,4 @@ using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Error.IRC.Channel; -using UniSpy.Server.Chat.Error.IRC.General; using UniSpy.Server.Chat.Contract.Request.General; using UniSpy.Server.Chat.Contract.Response.Channel; using UniSpy.Server.Chat.Contract.Result.Channel; @@ -14,24 +12,12 @@ public sealed class NamesHandler : ChannelHandlerBase private new NamesRequest _request => (NamesRequest)base._request; private new NamesResult _result { get => (NamesResult)base._result; set => base._result = value; } public NamesHandler(IShareClient client, NamesRequest request) : base(client, request) { } - protected override void RequestCheck() + public NamesHandler(IShareClient client, NamesRequest request, Aggregate.Channel channel, Aggregate.ChannelUser user) : base(client, request) { - if (_request.RawRequest is null) - { - _channel = _client.Info.GetLocalJoinedChannel(_request.ChannelName); - if (_channel is null) - { - throw new NoSuchChannelException($"No such channel {_request.ChannelName}", _request.ChannelName); - } - _user = _channel.GetUser(_client); - if (_user is null) - { - throw new NoSuchNickException($"Can not find user with nickname: {_client.Info.NickName} username: {_client.Info.UserName}"); - } - return; - } - base.RequestCheck(); + _user = user; + _channel = channel; } + protected override void DataOperation() { _result = new NamesResult(); @@ -39,6 +25,11 @@ protected override void DataOperation() _result.ChannelName = _channel.Name; _result.RequesterNickName = _user.Client.Info.NickName; } + + /// + /// We do not publish message in names handler + /// + protected override void PublishMessage() { } protected override void ResponseConstruct() { _response = new NamesResponse(_request, _result); diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs index a64f5116b..23cefa209 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs @@ -55,7 +55,6 @@ protected override void DataOperation() case PeerRoomType.Normal: case PeerRoomType.Staging: Aggregate.Channel.RemoveLocalChannel(_channel); - Aggregate.Channel.RemoveChannelCache(_user, _channel); break; } foreach (var user in _channel.Users.Values) @@ -79,12 +78,22 @@ protected override void DataOperation() default: // we need always remove the connection in leaver and channel _channel.RemoveUser(_user); - Aggregate.Channel.UpdateChannelCache(_user, _channel); - // Aggregate.Channel.UpdatePeerRoomInfo(_user); break; } } - + protected override void PublishMessage() + { + switch (_channel.RoomType) + { + case PeerRoomType.Normal: + case PeerRoomType.Staging: + Aggregate.Channel.RemoveChannelCache(_user, _channel); + break; + default: + base.PublishMessage(); + break; + } + } protected override void ResponseConstruct() { _response = new PartResponse(_request, _result); diff --git a/src/Servers/Chat/test/Game/GameTest.cs b/src/Servers/Chat/test/Game/GameTest.cs index d4bb70790..17ae2dadf 100644 --- a/src/Servers/Chat/test/Game/GameTest.cs +++ b/src/Servers/Chat/test/Game/GameTest.cs @@ -52,11 +52,6 @@ public void TcpMessageSplitingTest() { ((ITestClient)client).TestReceived(raw); } - // Given - - // When - - // Then } [Fact] public void Worms3dTest() diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/InitHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/InitHandler.cs index d4696d4b0..888f2fe8c 100755 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/InitHandler.cs +++ b/src/Servers/NatNegotiation/src/Handler/CmdHandler/InitHandler.cs @@ -19,14 +19,14 @@ public sealed class InitHandler : CmdHandlerBase /// /// Local NatInitInfo storage, after all init packets are received we send all into redis database /// - private InitPacketCache _addressInfo; + private InitPacketCache _cache; public InitHandler(Client client, InitRequest request) : base(client, request) { _result = new InitResult(); } protected override void DataOperation() { - _addressInfo = new InitPacketCache() + _cache = new InitPacketCache() { ServerID = _client.Server.Id, Cookie = (uint)_request.Cookie, @@ -38,7 +38,7 @@ protected override void DataOperation() PublicIPEndPoint = _client.Connection.RemoteIPEndPoint, PrivateIPEndPoint = _request.PrivateIPEndPoint }; - _client.LogInfo($"Received init request with private ip: [{_addressInfo.PrivateIPEndPoint}], cookie: {_addressInfo.Cookie}, client index: {_addressInfo.ClientIndex}."); + _client.LogInfo($"Received init request with private ip: [{_cache.PrivateIPEndPoint}], cookie: {_cache.Cookie}, client index: {_cache.ClientIndex}."); _result.RemoteIPEndPoint = _client.Connection.RemoteIPEndPoint; } protected override void ResponseConstruct() @@ -62,8 +62,7 @@ protected override void Response() } // todo make the code do not block and redis do not have thread theaf problem - // Task.Run(() => StorageOperation.Persistance.UpdateInitInfo(_addressInfo)); - StorageOperation.Persistance.UpdateInitInfo(_addressInfo); + StorageOperation.Persistance.UpdateInitInfo(_cache); // init packet nn3 is the last one client send, although receiving nn3 does not mean we received other init packets, but we can use this as a flag to prevent start multiple connect handler switch (_request.Version) { From fb18ae7173b2f439b1faadf2b778dd7c89c2d3a4 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 18 Oct 2023 22:03:46 +0800 Subject: [PATCH 048/231] fix(web): int parse error --- .../Sake/Contract/Request/SearchForRecordsRequest.cs | 7 +++++-- src/Servers/WebServer/test/Sake/GameTest.cs | 12 ++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/SearchForRecordsRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/SearchForRecordsRequest.cs index 3ac6f6406..8b6fd312c 100644 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/SearchForRecordsRequest.cs +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Request/SearchForRecordsRequest.cs @@ -6,7 +6,7 @@ namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request { - + public class SearchForRecordsRequest : RequestBase { public string Filter { get; set; } @@ -37,7 +37,10 @@ public override void Parse() var surrounding = _contentElement.Descendants().Where(p => p.Name.LocalName == "surrounding").First().Value; Surrounding = surrounding; var ownerids = _contentElement.Descendants().Where(p => p.Name.LocalName == "ownerids").First().Value; - OwnerIds = int.Parse(ownerids); + if (int.TryParse(ownerids, out var ids)) + { + OwnerIds = ids; + } var cacheFlag = _contentElement.Descendants().Where(p => p.Name.LocalName == "cacheFlag").First().Value; CacheFlag = cacheFlag; var fieldsNode = _contentElement.Descendants().Where(p => p.Name.LocalName == "fields").First(); diff --git a/src/Servers/WebServer/test/Sake/GameTest.cs b/src/Servers/WebServer/test/Sake/GameTest.cs index 5e26ed218..cfdc368d2 100644 --- a/src/Servers/WebServer/test/Sake/GameTest.cs +++ b/src/Servers/WebServer/test/Sake/GameTest.cs @@ -44,5 +44,17 @@ public void Crysis2SakeTest() switcher.Handle(); } + + [Fact] + public void Gunbrosand20231018() + { + var raw = "33008TTq4M0000000000000000000000__DEDICATEDSTATSPROFILE = 7recordid0100DATArecordid"; + var req = new Mock(); + req.Setup(r => r.Body).Returns(raw); + + var switcher = new WebServer.Handler.CmdSwitcher(MokeObject.Client, req.Object); + switcher.Handle(); + + } } } \ No newline at end of file From e267ef7718438bba6ae2bbfdc5d529732ed051fc Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 18 Oct 2023 23:12:43 +0800 Subject: [PATCH 049/231] fix(pcm): gunbrosand login fail --- .../src/Contract/Response/General/LoginResponse.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Response/General/LoginResponse.cs b/src/Servers/PresenceConnectionManager/src/Contract/Response/General/LoginResponse.cs index c2d00174c..7605b85cb 100755 --- a/src/Servers/PresenceConnectionManager/src/Contract/Response/General/LoginResponse.cs +++ b/src/Servers/PresenceConnectionManager/src/Contract/Response/General/LoginResponse.cs @@ -25,7 +25,7 @@ public override void Build() SendingBuffer += @"\userid\" + _result.DatabaseResults.UserId; SendingBuffer += @"\profileid\" + _result.DatabaseResults.ProfileId; - if (_request.Type != LoginType.NickEmail) + if (_result.DatabaseResults.UniqueNick is not null) { SendingBuffer += @"\uniquenick\" + _result.DatabaseResults.UniqueNick; } From 0b7a17a2faaaccd7af64d07c3bc5fc537f8b6702 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 18 Oct 2023 23:53:24 +0800 Subject: [PATCH 050/231] refactor(chat): distribution logic --- .../BaseClass/Channel/ChannelHandlerBase.cs | 37 +++++++++++-------- .../BaseClass/Message/MessageHandlerBase.cs | 4 ++ .../Interface/IStorageOperation.cs | 4 +- .../ChannelProperty/ChannelManage.cs | 3 +- src/Servers/Chat/src/Application/Client.cs | 20 +++++----- .../Chat/src/Application/ClientManager.cs | 2 - .../Chat/src/Application/StorageOperation.cs | 4 +- .../CmdHandler/General/CryptHandler.cs | 2 +- .../Handler/CmdHandler/General/NickHandler.cs | 2 +- 9 files changed, 42 insertions(+), 36 deletions(-) diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs index 939603a60..cb1f38762 100755 --- a/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs +++ b/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs @@ -50,7 +50,21 @@ public override void Handle() base.Handle(); try { + // we do not publish message when the message is received from remote client + if (_client.IsRemoteClient) + { + return; + } + if (_channel is null) + { + return; + } + if (_request.RawRequest is null) + { + return; + } PublishMessage(); + UpdateChannelCache(); } catch (Exception ex) { @@ -62,25 +76,19 @@ public override void Handle() /// protected virtual void PublishMessage() { - // we do not publish message when the message is received from remote client - if (_client.IsRemoteClient) - { - return; - } - if (_channel is null) - { - return; - } - if (_request.RawRequest is null) - { - return; - } + var msg = new RemoteMessage(_request, _client.GetRemoteClient()); + _channel.Broker.PublishMessage(msg); + } + + protected virtual void UpdateChannelCache() + { var key = new ChannelCache { ChannelName = _channel.Name, GameName = _channel.GameName }; + using (var locker = new LinqToRedis.RedisLock(TimeSpan.FromSeconds(10), Application.StorageOperation.Persistance.ChannelCacheClient.Db, key)) { if (locker.LockTake()) @@ -88,9 +96,6 @@ protected virtual void PublishMessage() Aggregate.Channel.UpdateChannelCache(_user, _channel); } } - - var msg = new RemoteMessage(_request, _client.GetRemoteClient()); - _channel.Broker.PublishMessage(msg); } } } diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageHandlerBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageHandlerBase.cs index 377adf3f1..9b584d2d6 100755 --- a/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageHandlerBase.cs +++ b/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageHandlerBase.cs @@ -77,5 +77,9 @@ protected override void Response() break; } } + protected override void UpdateChannelCache() + { + // we do nothing here, when there is a channel message + } } } diff --git a/src/Servers/Chat/src/Abstraction/Interface/IStorageOperation.cs b/src/Servers/Chat/src/Abstraction/Interface/IStorageOperation.cs index 18641edd1..38662b441 100644 --- a/src/Servers/Chat/src/Abstraction/Interface/IStorageOperation.cs +++ b/src/Servers/Chat/src/Abstraction/Interface/IStorageOperation.cs @@ -17,8 +17,8 @@ public interface IStorageOperation Channel GetChannel(ChannelCache key); void UpdateChannel(Channel channel); void RemoveChannel(Channel channel); - void UpdateClient(Client client); - void RemoveClient(Client client); + void UpdateClient(IShareClient client); + void RemoveClient(IShareClient client); bool IsClientExist(ClientInfoCache key); } } \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelManage.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelManage.cs index d808ff27f..1cb82987d 100644 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelManage.cs +++ b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelManage.cs @@ -16,12 +16,11 @@ public sealed partial class Channel /// [JsonIgnore] public static readonly ConcurrentDictionary LocalChannels = new(); + [JsonIgnore] public static readonly ConcurrentDictionary MessageBrokers = new(); /// /// You need to manually check channel existance then get channel /// - /// - /// public static Channel GetLocalChannel(string name) { LocalChannels.TryGetValue(name, out var channel); diff --git a/src/Servers/Chat/src/Application/Client.cs b/src/Servers/Chat/src/Application/Client.cs index d01081e3d..a4a29ed2e 100644 --- a/src/Servers/Chat/src/Application/Client.cs +++ b/src/Servers/Chat/src/Application/Client.cs @@ -1,4 +1,4 @@ -using System.Threading.Tasks; +using System; using UniSpy.Server.Chat.Abstraction.Interface; using UniSpy.Server.Chat.Aggregate; using UniSpy.Server.Chat.Aggregate.Misc; @@ -8,6 +8,7 @@ using UniSpy.Server.Core.Abstraction.BaseClass; using UniSpy.Server.Core.Abstraction.Interface; using UniSpy.Server.Core.Encryption; +using UniSpy.Server.Core.Extension; using UniSpy.Server.Core.Logging; namespace UniSpy.Server.Chat.Application @@ -29,7 +30,13 @@ public Client(IConnection connection, IServer server, ClientInfo info) : this(co Info = info; _remoteClient = new RemoteClient(this); } - + protected override void EventBinding() + { + base.EventBinding(); + _timer = new EasyTimer(TimeSpan.FromMinutes(1)); + _timer.Elapsed += (s, e) => Application.StorageOperation.Persistance.UpdateClient(this); + _timer.Start(); + } protected override void OnReceived(object buffer) { var message = DecryptMessage((byte[])buffer); @@ -37,14 +44,7 @@ protected override void OnReceived(object buffer) { this.LogNetworkReceiving(completeBuffer); var switcher = CreateSwitcher(completeBuffer); - if (System.Diagnostics.Debugger.IsAttached) - { - switcher.Handle(); - } - else - { - Task.Run(() => switcher.Handle()); - } + switcher.Handle(); } } protected override void OnDisconnected() diff --git a/src/Servers/Chat/src/Application/ClientManager.cs b/src/Servers/Chat/src/Application/ClientManager.cs index 393eda3f7..c866fd5f2 100644 --- a/src/Servers/Chat/src/Application/ClientManager.cs +++ b/src/Servers/Chat/src/Application/ClientManager.cs @@ -1,9 +1,7 @@ -using System.Net; using System.Collections.Generic; using System.Linq; using UniSpy.Server.Core.Abstraction.BaseClass; using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Core.Abstraction.Interface; namespace UniSpy.Server.Chat.Application { diff --git a/src/Servers/Chat/src/Application/StorageOperation.cs b/src/Servers/Chat/src/Application/StorageOperation.cs index 8f2e9e623..53d8e37ac 100644 --- a/src/Servers/Chat/src/Application/StorageOperation.cs +++ b/src/Servers/Chat/src/Application/StorageOperation.cs @@ -113,7 +113,7 @@ public void RemoveChannel(Channel channel) ChannelCacheClient.DeleteKeyValue(data); } - public void UpdateClient(Client client) + public void UpdateClient(IShareClient client) { var data = new ClientInfoCache { @@ -123,7 +123,7 @@ public void UpdateClient(Client client) ClientCacheClient.SetValue(data); } - public void RemoveClient(Client client) + public void RemoveClient(IShareClient client) { var data = new ClientInfoCache { diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/CryptHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/CryptHandler.cs index 6638135f1..fca598df9 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/CryptHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/CryptHandler.cs @@ -46,7 +46,7 @@ protected override void ResponseConstruct() protected override void Response() { base.Response(); - if (!_client.IsRemoteClient) + if (!_client.IsRemoteClient && _client.GetType() == typeof(Client)) { ((Client)_client).Crypto = _crypto; } diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs index d56e81858..c7f6c2560 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs @@ -63,7 +63,7 @@ protected override void DataOperation() if (!Application.StorageOperation.Persistance.IsClientExist(key)) { _client.Info.NickName = _request.NickName; - Application.StorageOperation.Persistance.UpdateClient((Client)_client); + Application.StorageOperation.Persistance.UpdateClient(_client); } else { From e15c1a368b18f3d538f23345793d9de4a1a3f6f4 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 18 Oct 2023 23:58:08 +0800 Subject: [PATCH 051/231] refactor(lib): update functions --- src/Libraries/Core/src/Extension/EasyTimer.cs | 20 ++++++++++++++++--- .../Core/src/Misc/BufferCacheBase.cs | 2 +- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/Libraries/Core/src/Extension/EasyTimer.cs b/src/Libraries/Core/src/Extension/EasyTimer.cs index ac89ddf4e..79d193264 100644 --- a/src/Libraries/Core/src/Extension/EasyTimer.cs +++ b/src/Libraries/Core/src/Extension/EasyTimer.cs @@ -5,14 +5,27 @@ namespace UniSpy.Server.Core.Extension { public class EasyTimer { - public TimeSpan ExpireTime { get; private set; } + public TimeSpan? ExpireTime { get; private set; } public DateTime LastActiveTime { get; private set; } public TimeSpan IdleTime => DateTime.Now - LastActiveTime; /// /// The timer to count and invoke some event /// private Timer _timer; - public bool IsExpired => IdleTime > ExpireTime; + public bool IsExpired + { + get + { + if (ExpireTime is null) + { + return false; + } + else + { + return IdleTime > ExpireTime; + } + } + } public event ElapsedEventHandler Elapsed { add { _timer.Elapsed += value; } @@ -21,7 +34,7 @@ public event ElapsedEventHandler Elapsed /// /// Easy timer constructor, remember to call Start() /// - public EasyTimer(TimeSpan expireTimeSpan, TimeSpan intervalTimeSpan) + public EasyTimer(TimeSpan? expireTimeSpan, TimeSpan intervalTimeSpan) { _timer = new Timer { @@ -31,6 +44,7 @@ public EasyTimer(TimeSpan expireTimeSpan, TimeSpan intervalTimeSpan) }; ExpireTime = expireTimeSpan; } + public EasyTimer(TimeSpan intervalTimeSpan) : this(null, intervalTimeSpan) { } /// /// Start the timer /// diff --git a/src/Libraries/Core/src/Misc/BufferCacheBase.cs b/src/Libraries/Core/src/Misc/BufferCacheBase.cs index 443229d88..dffcb8b6a 100644 --- a/src/Libraries/Core/src/Misc/BufferCacheBase.cs +++ b/src/Libraries/Core/src/Misc/BufferCacheBase.cs @@ -9,7 +9,7 @@ protected BufferCacheBase() } /// - /// Process the incoming buffer, if buffer is complete return complete buffer, other wise return null + /// Process the incoming buffer, if buffer is complete return true, otherwise return false /// /// public abstract bool ProcessBuffer(T buffer, out T completeBuffer); From 10ba4639ba85fc5e64679024c829fbb0c8f4a89a Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 19 Oct 2023 11:00:05 +0800 Subject: [PATCH 052/231] refactor(chat): remove duplicated redis operation --- .../ChannelProperty/ChannelManage.cs | 3 +- .../src/Aggregate/Redis/ClientInfoCache.cs | 2 +- src/Servers/Chat/src/Application/Client.cs | 1 + .../Handler/CmdHandler/Channel/JoinHandler.cs | 6 +- .../Handler/CmdHandler/Channel/PartHandler.cs | 65 ++++++++++--------- 5 files changed, 43 insertions(+), 34 deletions(-) diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelManage.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelManage.cs index 1cb82987d..96facec65 100644 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelManage.cs +++ b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelManage.cs @@ -76,7 +76,8 @@ public static ChannelMessageBroker AddMessageBrocker(Channel channel) } public static void RemoveMessageBrocker(Channel channel) { - MessageBrokers.TryRemove(channel.Name, out var _); + MessageBrokers.TryRemove(channel.Name, out var broker); + broker.Dispose(); } } } \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/Redis/ClientInfoCache.cs b/src/Servers/Chat/src/Aggregate/Redis/ClientInfoCache.cs index c636ae3da..6df2115ea 100644 --- a/src/Servers/Chat/src/Aggregate/Redis/ClientInfoCache.cs +++ b/src/Servers/Chat/src/Aggregate/Redis/ClientInfoCache.cs @@ -10,7 +10,7 @@ public record ClientInfoCache : Core.Abstraction.BaseClass.RedisKeyValueObject [RedisKey] public string NickName { get; set; } public ClientInfo Info { get; set; } - public ClientInfoCache() : base(RedisDbNumber.ChatChannel, TimeSpan.FromHours(1)) { } + public ClientInfoCache() : base(RedisDbNumber.ChatChannel, TimeSpan.FromMinutes(2)) { } public class RedisClient : Core.Abstraction.BaseClass.RedisClient { public RedisClient() { } diff --git a/src/Servers/Chat/src/Application/Client.cs b/src/Servers/Chat/src/Application/Client.cs index a4a29ed2e..999072d09 100644 --- a/src/Servers/Chat/src/Application/Client.cs +++ b/src/Servers/Chat/src/Application/Client.cs @@ -33,6 +33,7 @@ public Client(IConnection connection, IServer server, ClientInfo info) : this(co protected override void EventBinding() { base.EventBinding(); + // bind a event that can update the client info to redis _timer = new EasyTimer(TimeSpan.FromMinutes(1)); _timer.Elapsed += (s, e) => Application.StorageOperation.Persistance.UpdateClient(this); _timer.Start(); diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs index 8035df977..12a645482 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs @@ -92,7 +92,7 @@ protected override void DataOperation() { _user = _channel.AddUser(_client, _request.Password ?? null); } - Aggregate.Channel.UpdateChannelCache(_user, _channel); + base.UpdateChannelCache(); // <= we update the channel cache here } else { @@ -106,6 +106,10 @@ protected override void DataOperation() _result.JoinerPrefix = _client.Info.IRCPrefix; } + protected override void UpdateChannelCache() + { + // we do not update channel cache again in base class because we update it when channel is created + } protected override void ResponseConstruct() { _response = new JoinResponse(_request, _result); diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs index 23cefa209..a03680abb 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs @@ -16,9 +16,6 @@ public sealed class PartHandler : ChannelHandlerBase private new PartResponse _response { get => (PartResponse)base._response; set => base._response = value; } private new PartResult _result { get => (PartResult)base._result; set => base._result = value; } public PartHandler(IShareClient client, PartRequest request) : base(client, request) { } - static PartHandler() - { - } protected override void RequestCheck() { if (_request.RawRequest is null) @@ -48,32 +45,7 @@ protected override void DataOperation() { case PeerRoomType.Normal: case PeerRoomType.Staging: - if (_user.IsChannelCreator) - { - switch (_channel.RoomType) - { - case PeerRoomType.Normal: - case PeerRoomType.Staging: - Aggregate.Channel.RemoveLocalChannel(_channel); - break; - } - foreach (var user in _channel.Users.Values) - { - // we do not need to send part message to leaver - if (user.Client.Info.NickName == _user.Client.Info.NickName) - { - continue; - } - // We create a new KICKHandler to handle KICK operation for us - var kickRequest = new KickRequest - { - KickeeNickName = user.Client.Info.NickName, - ChannelName = _channel.Name, - Reason = _request.Reason, - }; - new KickHandler(_client, kickRequest).Handle(); - } - } + KickAllUser(); goto default; default: // we need always remove the connection in leaver and channel @@ -81,7 +53,38 @@ protected override void DataOperation() break; } } - protected override void PublishMessage() + private void KickAllUser() + { + if (_user.IsChannelCreator) + { + // we first send all user message to let them known the creator is leaving + foreach (var user in _channel.Users.Values) + { + // we do not need to send part message to leaver + if (user.Client.Info.NickName == _user.Client.Info.NickName) + { + continue; + } + // We create a new KICKHandler to handle KICK operation for us + var kickRequest = new KickRequest + { + KickeeNickName = user.Client.Info.NickName, + ChannelName = _channel.Name, + Reason = _request.Reason, + }; + new KickHandler(_client, kickRequest).Handle(); + } + // we remove the local channel and unbind events + switch (_channel.RoomType) + { + case PeerRoomType.Normal: + case PeerRoomType.Staging: + Aggregate.Channel.RemoveLocalChannel(_channel); + break; + } + } + } + protected override void UpdateChannelCache() { switch (_channel.RoomType) { @@ -90,7 +93,7 @@ protected override void PublishMessage() Aggregate.Channel.RemoveChannelCache(_user, _channel); break; default: - base.PublishMessage(); + base.UpdateChannelCache(); break; } } From 60cf7cf87888eb22e8ffbfae518ad16f24ee02d0 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 20 Oct 2023 10:43:13 +0800 Subject: [PATCH 053/231] fix(web): pass authtest --- src/Libraries/Core/src/Extension/StringExtensions.cs | 6 +++--- src/Servers/WebServer/src/Application/ClientInfo.cs | 12 +++++++++--- .../src/Module/Auth/Abstraction/LoginResponseBase.cs | 5 ++--- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Libraries/Core/src/Extension/StringExtensions.cs b/src/Libraries/Core/src/Extension/StringExtensions.cs index 14915bdc2..d3210365e 100755 --- a/src/Libraries/Core/src/Extension/StringExtensions.cs +++ b/src/Libraries/Core/src/Extension/StringExtensions.cs @@ -1,3 +1,4 @@ +using System.Numerics; using System; using System.Collections.Generic; using System.IO; @@ -210,12 +211,11 @@ public static bool CheckResponseValidation(this byte[] buffer) /// public static byte[] FromHexStringToBytes(this string hex) { - return Enumerable.Range(0, hex.Length) + var data = Enumerable.Range(0, hex.Length) .Where(x => x % 2 == 0) .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) .ToArray(); + return data; } - - } } diff --git a/src/Servers/WebServer/src/Application/ClientInfo.cs b/src/Servers/WebServer/src/Application/ClientInfo.cs index 7b4f8ddfd..cc34657e0 100644 --- a/src/Servers/WebServer/src/Application/ClientInfo.cs +++ b/src/Servers/WebServer/src/Application/ClientInfo.cs @@ -13,12 +13,18 @@ namespace UniSpy.Server.WebServer.Application /// public sealed class ClientInfo : ClientInfoBase { - /// - /// RandomNumber - /// public const string PeerKeyExponent = "000001"; public const string PeerKeyModulus = "aefb5064bbd1eb632fa8d57aab1c49366ce0ee3161cbef19f2b7971b63b811790ecbf6a47b34c55f65a0766b40c261c5d69c394cd320842dd2bccba883d30eae8fdba5d03b21b09bfc600dcb30b1b2f3fbe8077630b006dcb54c4254f14891762f72e7bbfe743eb8baf65f9e8c8d11ebe46f6b59e986b4c394cfbc2c8606e29f"; /// + /// The exponent generated for user + /// exponent is 000001 + /// + public static readonly byte PeerKeyExponentByte = 1; + /// + /// The modulus generated for user aefb5064bbd1eb632fa8d57aab1c49366ce0ee3161cbef19f2b7971b63b811790ecbf6a47b34c55f65a0766b40c261c5d69c394cd320842dd2bccba883d30eae8fdba5d03b21b09bfc600dcb30b1b2f3fbe8077630b006dcb54c4254f14891762f72e7bbfe743eb8baf65f9e8c8d11ebe46f6b59e986b4c394cfbc2c8606e29f + /// + public static readonly byte[] PeerKeyModulusBytes = { 0xAE, 0xFB, 0x50, 0x64, 0xBB, 0xD1, 0xEB, 0x63, 0x2F, 0xA8, 0xD5, 0x7A, 0xAB, 0x1C, 0x49, 0x36, 0x6C, 0xE0, 0xEE, 0x31, 0x61, 0xCB, 0xEF, 0x19, 0xF2, 0xB7, 0x97, 0x1B, 0x63, 0xB8, 0x11, 0x79, 0x0E, 0xCB, 0xF6, 0xA4, 0x7B, 0x34, 0xC5, 0x5F, 0x65, 0xA0, 0x76, 0x6B, 0x40, 0xC2, 0x61, 0xC5, 0xD6, 0x9C, 0x39, 0x4C, 0xD3, 0x20, 0x84, 0x2D, 0xD2, 0xBC, 0xCB, 0xA8, 0x83, 0xD3, 0x0E, 0xAE, 0x8F, 0xDB, 0xA5, 0xD0, 0x3B, 0x21, 0xB0, 0x9B, 0xFC, 0x60, 0x0D, 0xCB, 0x30, 0xB1, 0xB2, 0xF3, 0xFB, 0xE8, 0x07, 0x76, 0x30, 0xB0, 0x06, 0xDC, 0xB5, 0x4C, 0x42, 0x54, 0xF1, 0x48, 0x91, 0x76, 0x2F, 0x72, 0xE7, 0xBB, 0xFE, 0x74, 0x3E, 0xB8, 0xBA, 0xF6, 0x5F, 0x9E, 0x8C, 0x8D, 0x11, 0xEB, 0xE4, 0x6F, 0x6B, 0x59, 0xE9, 0x86, 0xB4, 0xC3, 0x94, 0xCF, 0xBC, 0x2C, 0x86, 0x06, 0xE2, 0x9F }; + /// /// should be 256 characters /// public const string ServerData = diff --git a/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResponseBase.cs b/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResponseBase.cs index 0c2900b45..30f4df63e 100644 --- a/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResponseBase.cs +++ b/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResponseBase.cs @@ -49,9 +49,8 @@ protected void BuildContext() dataToHash.AddRange(Encoding.ASCII.GetBytes(_result.UniqueNick)); dataToHash.AddRange(Encoding.ASCII.GetBytes(_result.CdKeyHash)); - // if these 2 value be 0 we do not need to add them to the list - // dataToHash.AddRange(ClientInfo.PeerKeyPublicModulus.FromHexStringToBytes()); - // dataToHash.AddRange(ClientInfo.PeerKeyPrivate.FromHexStringToBytes()); + dataToHash.AddRange(ClientInfo.PeerKeyModulusBytes); + dataToHash.Add(ClientInfo.PeerKeyExponentByte); // server data should be convert to bytes[128] then added to list dataToHash.AddRange(ClientInfo.ServerData.FromHexStringToBytes()); From b386910b79c6d0dd1008687fd711d3bb02e06995 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 24 Oct 2023 08:06:15 +0800 Subject: [PATCH 054/231] fix(chat): client cache update check --- src/Servers/Chat/src/Application/StorageOperation.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Servers/Chat/src/Application/StorageOperation.cs b/src/Servers/Chat/src/Application/StorageOperation.cs index 53d8e37ac..2238dc4ed 100644 --- a/src/Servers/Chat/src/Application/StorageOperation.cs +++ b/src/Servers/Chat/src/Application/StorageOperation.cs @@ -115,6 +115,11 @@ public void RemoveChannel(Channel channel) public void UpdateClient(IShareClient client) { + // we do not update client info when its nickname is not registered + if (client.Info.NickName is null) + { + return; + } var data = new ClientInfoCache { NickName = client.Info.NickName, From 548d94768d71742a4a4ae06e9195d508c45763d1 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 24 Oct 2023 09:33:15 +0800 Subject: [PATCH 055/231] fix(natneg): server launch crash --- src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs | 2 +- src/Servers/NatNegotiation/src/Application/Client.cs | 6 ------ .../NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs | 6 +++--- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs index 64653e9ad..ab510c257 100644 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs +++ b/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs @@ -38,7 +38,7 @@ protected virtual void EventBinding() break; case NetworkConnectionType.Udp: ((IUdpConnection)Connection).OnReceive += OnReceived; - _timer = new EasyTimer(TimeSpan.FromHours(1), TimeSpan.FromMinutes(1)); + _timer = new EasyTimer(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(1)); _timer.Elapsed += (s, e) => CheckExpiredClient(); _timer.Start(); break; diff --git a/src/Servers/NatNegotiation/src/Application/Client.cs b/src/Servers/NatNegotiation/src/Application/Client.cs index 60d775716..c2a2d5643 100644 --- a/src/Servers/NatNegotiation/src/Application/Client.cs +++ b/src/Servers/NatNegotiation/src/Application/Client.cs @@ -13,14 +13,8 @@ public class Client : ClientBase public Client(IConnection connection, IServer server) : base(connection, server) { Info = new ClientInfo(); - _timer = new EasyTimer(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(1)); IsLogRaw = true; } protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, (byte[])buffer); - protected override void EventBinding() - { - ((IUdpConnection)Connection).OnReceive += OnReceived; - _timer.Elapsed += (s, e) => CheckExpiredClient(); - } } } \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs index 7b6c63744..e925569fb 100755 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs +++ b/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs @@ -34,7 +34,7 @@ public ConnectHandler(Client client, ConnectRequest request) : base(client, requ protected override void RequestCheck() { // detecting nat - var addressInfos = StorageOperation.Persistance.GetInitInfos(_client.Server.Id, (uint)_client.Info.Cookie); + var addressInfos = StorageOperation.Persistance.GetInitInfos(_client.Server.Id, _request.Cookie); // if (addressInfos.Count < InitHandler.InitPacketCount) // { // throw new NatNegotiation.Exception($"The number of init info in redis with cookie: {_client.Info.Cookie} is not bigger than 7."); @@ -112,8 +112,8 @@ private void UsingGameRelayServerToNegotiate() } //todo the optimized server will be selected var relayEndPoint = relayServers.OrderBy(x => x.ClientCount).First().PublicIPEndPoint; - var myIPs = _myInitInfo.AddressInfos.Select(x=>x.Value.PublicIPEndPoint.ToString()).ToList(); - var otherIPs = _othersInitInfo.AddressInfos.Select(x=>x.Value.PublicIPEndPoint.ToString()).ToList(); + var myIPs = _myInitInfo.AddressInfos.Select(x => x.Value.PublicIPEndPoint.ToString()).ToList(); + var otherIPs = _othersInitInfo.AddressInfos.Select(x => x.Value.PublicIPEndPoint.ToString()).ToList(); var req = new NatNegotiationRequest() { Cookie = _myInitInfo.Cookie, From 36daaafe56922d88ce5f627ae1aa454c8d441dd0 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 24 Oct 2023 10:08:43 +0800 Subject: [PATCH 056/231] fix(lib): redis connection fail with retry --- src/Libraries/Core/src/Config/UniSpyConfig.cs | 2 +- .../QueryReport/src/Application/Client.cs | 21 +++++++++++++------ .../QueryReport/src/V1/Handler/CmdSwitcher.cs | 2 +- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/Libraries/Core/src/Config/UniSpyConfig.cs b/src/Libraries/Core/src/Config/UniSpyConfig.cs index 4f0a56ffc..fbc431b2b 100755 --- a/src/Libraries/Core/src/Config/UniSpyConfig.cs +++ b/src/Libraries/Core/src/Config/UniSpyConfig.cs @@ -44,7 +44,7 @@ public class UniSpyDatabaseConfig } public class UniSpyRedisConfig { - public string ConnectionString => $"{Server}:{Port},user={User},password={Password},ssl={SSL},sslHost={SSLHost}"; + public string ConnectionString => $"{Server}:{Port},user={User},password={Password},ssl={SSL},sslHost={SSLHost},abortConnect=false"; public string Server; public int Port; public string User; diff --git a/src/Servers/QueryReport/src/Application/Client.cs b/src/Servers/QueryReport/src/Application/Client.cs index d33a6acbd..f02b504a0 100644 --- a/src/Servers/QueryReport/src/Application/Client.cs +++ b/src/Servers/QueryReport/src/Application/Client.cs @@ -1,6 +1,8 @@ +using System.Text; +using System; using UniSpy.Server.Core.Abstraction.BaseClass; using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.QueryReport.V2.Handler; +using UniSpy.Server.Core.Encryption; namespace UniSpy.Server.QueryReport.Application { @@ -9,13 +11,20 @@ public sealed class Client : ClientBase public Client(IConnection connection, IServer server) : base(connection, server) { IsLogRaw = true; - // launch redis channel Info = new ClientInfo(); } - public new ClientInfo Info { get => (ClientInfo)base.Info; private set => base.Info = value; } - - - protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, (byte[])buffer); + protected override ISwitcher CreateSwitcher(object buffer) + { + var data = (byte[])buffer; + if (data[0] == Convert.ToInt32('\\')) + { + return new V1.Handler.CmdSwitcher(this, UniSpyEncoding.GetString(data)); + } + else + { + return new V2.Handler.CmdSwitcher(this, data); + } + } } } \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Handler/CmdSwitcher.cs b/src/Servers/QueryReport/src/V1/Handler/CmdSwitcher.cs index 6f61a8bc8..94864744b 100644 --- a/src/Servers/QueryReport/src/V1/Handler/CmdSwitcher.cs +++ b/src/Servers/QueryReport/src/V1/Handler/CmdSwitcher.cs @@ -11,7 +11,7 @@ public sealed class CmdSwitcher : CmdSwitcherBase { private new string _rawRequest => (string)base._rawRequest; private new Client _client => (Client)base._client; - public CmdSwitcher(Client client, object rawRequest) : base(client, rawRequest) + public CmdSwitcher(Client client, string rawRequest) : base(client, rawRequest) { } From a480ebdd4e7d2cf6a0e8e95646f13755a36ab1f7 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 24 Oct 2023 22:17:43 +0800 Subject: [PATCH 057/231] fix(chat): wrong remove client logic --- src/Servers/Chat/src/Application/Client.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/Servers/Chat/src/Application/Client.cs b/src/Servers/Chat/src/Application/Client.cs index 999072d09..580d17a82 100644 --- a/src/Servers/Chat/src/Application/Client.cs +++ b/src/Servers/Chat/src/Application/Client.cs @@ -59,14 +59,10 @@ protected override void OnDisconnected() new QuitHandler(this, req).Handle(); Info.IsLoggedIn = false; } + StorageOperation.Persistance.RemoveClient(this); base.OnDisconnected(); } protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, UniSpyEncoding.GetString((byte[])buffer)); public RemoteClient GetRemoteClient() => _remoteClient; - protected override void OnConnected() - { - StorageOperation.Persistance.RemoveClient(this); - base.OnConnected(); - } } } \ No newline at end of file From 14a926c4b123986fe39593a85da366701cef30c4 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 24 Oct 2023 23:18:12 +0800 Subject: [PATCH 058/231] refactor(web): add empty value when no sake data found --- .../Response/SearchForRecordResponse.cs | 22 +++++++++++++------ .../Sake/Handler/SearchForRecordsHandler.cs | 8 +++---- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs index 518d3299e..347901480 100644 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs +++ b/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs @@ -16,15 +16,23 @@ public override void Build() { _content.Add("SearchForRecordsResponse"); _content.Add("SearchForRecordsResult", "Success"); - var temp = Newtonsoft.Json.JsonConvert.DeserializeXNode(_result.UserData.RootElement.ToString()).Root; - // add namespace to root element - temp.Name = SakeSoapEnvelope.SakeNamespace + temp.Name.LocalName; - // Add the namespace to each selected node - foreach (var element in temp.DescendantsAndSelf()) + if (_result.UserData is not null) { - element.Name = SakeSoapEnvelope.SakeNamespace + element.Name.LocalName; + var temp = Newtonsoft.Json.JsonConvert.DeserializeXNode(_result.UserData.RootElement.ToString()).Root; + // add namespace to root element + temp.Name = SakeSoapEnvelope.SakeNamespace + temp.Name.LocalName; + // Add the namespace to each selected node + foreach (var element in temp.DescendantsAndSelf()) + { + element.Name = SakeSoapEnvelope.SakeNamespace + element.Name.LocalName; + } + _content.Add("values", temp); } - _content.Add("values", temp); + else + { + _content.Add("values"); + } + base.Build(); } } diff --git a/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs b/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs index dd8e763d9..522b9debf 100644 --- a/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs +++ b/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs @@ -28,10 +28,10 @@ protected override void DataOperation() && t.Tableid == _request.TableId) .Select(s => s.Userdata).FirstOrDefault(); } - if (_result.UserData is null) - { - throw new WebServer.Exception("There are no sake data found in the database, please manually add it in the database"); - } + // if (_result.UserData is null) + // { + // throw new WebServer.Exception("There are no sake data found in the database, please manually add it in the database"); + // } } protected override void ResponseConstruct() { From 20fd9bdee36230564cb0962a54fbc32685f5318a Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 31 Oct 2023 22:55:50 +0800 Subject: [PATCH 059/231] refactor: update vscode config --- .vscode/launch.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 0ea7fd15c..3a000d416 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -136,21 +136,21 @@ ], "compounds": [ { - "name": "PCM, PSP", + "name": "PCM PSP", "configurations": [ "PCM", "PSP" ] }, { - "name": "NatNeg, GTR", + "name": "NatNeg GTR", "configurations": [ "NatNeg", "GTR" ] }, { - "name": "QR, SB, NatNeg, GTR", + "name": "QR SB NatNeg GTR", "configurations": [ "QR", "SB", @@ -159,14 +159,14 @@ ] }, { - "name": "GS, PSP", + "name": "GS PSP", "configurations": [ "PSP", "GS" ] }, { - "name": "SAKE, PCM", + "name": "Web PCM", "configurations": [ "Web", "PCM" @@ -181,7 +181,7 @@ ] }, { - "name": "Legacy servers", + "name": "PCM PSP NN Chat QR SB GS", "configurations": [ "PCM", "PSP", @@ -193,7 +193,7 @@ ] }, { - "name": "New servers", + "name": "PCM PSP NatNeg Chat QR SB Web", "configurations": [ "PCM", "PSP", @@ -205,7 +205,7 @@ ] }, { - "name": "All servers", + "name": "PCM PSP NN GTR Chat QR SB GS Web", "configurations": [ "PCM", "PSP", From 32c5b3fb17680f8ef828311e3906d3463304a6aa Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 2 Nov 2023 21:00:48 +0800 Subject: [PATCH 060/231] refactor(lib): change network components --- .../src/Abstraction/BaseClass/ClientBase.cs | 7 +- .../src/Abstraction/Interface/IConnection.cs | 6 +- .../src/Abstraction/Interface/IHttpRequest.cs | 9 +- .../Core/src/Misc/BufferCacheBase.cs | 17 --- .../Network/Http/Server/HttpBufferCache.cs | 42 -------- .../src/Network/Http/Server/HttpConnection.cs | 80 +++++--------- .../Http/Server/HttpConnectionManager.cs | 56 +++++++--- .../src/Network/Http/Server/HttpRequest.cs | 51 +++------ .../Core/src/Network/RemoteObject.cs | 3 +- .../src/Network/Tcp/Server/TcpConnection.cs | 91 ++++++++++------ .../Tcp/Server/TcpConnectionManager.cs | 55 ++++++---- .../src/Network/Udp/Client/UniSpyUdpClient.cs | 35 ------ .../src/Network/Udp/Server/UdpConnection.cs | 97 +++++++++-------- .../Udp/Server/UdpConnectionManager.cs | 64 +++++------ .../Chat/src/Aggregate/Misc/BufferCache.cs | 46 -------- src/Servers/Chat/src/Application/Client.cs | 10 +- .../CmdHandler/Message/PrivateHandler.cs | 2 +- .../GameTrafficRelay/Conneciton/Client.cs | 0 .../GameTrafficRelay/Conneciton/Server.cs | 0 .../GameTrafficRelay/ConnectionListener.cs | 101 +++++++++--------- .../src/Application/Server.cs | 2 +- .../src/V2/Aggregate/Misc/BufferCache.cs | 47 -------- .../src/V2/Application/Client.cs | 9 -- .../WebServer/src/Application/Client.cs | 2 - .../WebServer/src/Application/Server.cs | 11 +- src/Servers/WebServer/test/GeneralTest.cs | 34 +++--- 26 files changed, 346 insertions(+), 531 deletions(-) delete mode 100644 src/Libraries/Core/src/Misc/BufferCacheBase.cs delete mode 100644 src/Libraries/Core/src/Network/Http/Server/HttpBufferCache.cs delete mode 100644 src/Libraries/Core/src/Network/Udp/Client/UniSpyUdpClient.cs delete mode 100644 src/Servers/Chat/src/Aggregate/Misc/BufferCache.cs create mode 100644 src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/Conneciton/Client.cs create mode 100644 src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/Conneciton/Server.cs delete mode 100644 src/Servers/ServerBrowser/src/V2/Aggregate/Misc/BufferCache.cs diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs index ab510c257..4921a64f5 100644 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs +++ b/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs @@ -3,7 +3,6 @@ using UniSpy.Server.Core.Encryption; using UniSpy.Server.Core.Logging; using UniSpy.Server.Core.Extension; -using System.Threading.Tasks; namespace UniSpy.Server.Core.Abstraction.BaseClass { @@ -44,8 +43,6 @@ protected virtual void EventBinding() break; case NetworkConnectionType.Http: ((IHttpConnection)Connection).OnReceive += OnReceived; - ((IHttpConnection)Connection).OnConnect += OnConnected; - ((IHttpConnection)Connection).OnDisconnect += OnDisconnected; break; case NetworkConnectionType.Test: this.LogVerbose("Using unit-test mock connection."); @@ -57,7 +54,8 @@ protected virtual void EventBinding() /// /// Only work for tcp /// - protected virtual void OnConnected() => ClientManagerBase.AddClient(this); + // protected virtual void OnConnected() => ClientManagerBase.AddClient(this); + protected virtual void OnConnected() { } /// /// Only work for tcp @@ -139,7 +137,6 @@ public void Dispose() /// /// Sending IResponse to client(ciphertext or plaintext) /// - /// public void Send(IResponse response) { byte[] buffer = null; diff --git a/src/Libraries/Core/src/Abstraction/Interface/IConnection.cs b/src/Libraries/Core/src/Abstraction/Interface/IConnection.cs index 58170bc98..03460ddbd 100644 --- a/src/Libraries/Core/src/Abstraction/Interface/IConnection.cs +++ b/src/Libraries/Core/src/Abstraction/Interface/IConnection.cs @@ -1,3 +1,4 @@ +using System; using System.Net; using UniSpy.Server.Core.Events; @@ -29,7 +30,6 @@ public interface IConnection /// public interface IUdpConnection : IConnection { - // public TimeSpan ConnectionExistedTime { get; } void Send(IPEndPoint endPoint, byte[] response); void Send(IPEndPoint endPoint, string response); } @@ -42,11 +42,11 @@ public interface ITcpConnection : IConnection event OnDisconnectedEventHandler OnDisconnect; void Disconnect(); } - public interface IHttpConnection : ITcpConnection + public interface IHttpConnection : IConnection { } - public interface IConnectionManager + public interface IConnectionManager : IDisposable { event OnConnectingEventHandler OnInitialization; void Start(); diff --git a/src/Libraries/Core/src/Abstraction/Interface/IHttpRequest.cs b/src/Libraries/Core/src/Abstraction/Interface/IHttpRequest.cs index 8c1db0864..17a00e1d7 100644 --- a/src/Libraries/Core/src/Abstraction/Interface/IHttpRequest.cs +++ b/src/Libraries/Core/src/Abstraction/Interface/IHttpRequest.cs @@ -1,14 +1,11 @@ +using System; + namespace UniSpy.Server.Core.Abstraction.Interface { public interface IHttpRequest { - byte[] BodyBytes { get; } string Body { get; } - long Cookies { get; } - long Headers { get; } - string Protocol { get; } - string Url { get; } + Uri Url { get; } string Method { get; } - bool KeepAlive { get; } } } \ No newline at end of file diff --git a/src/Libraries/Core/src/Misc/BufferCacheBase.cs b/src/Libraries/Core/src/Misc/BufferCacheBase.cs deleted file mode 100644 index dffcb8b6a..000000000 --- a/src/Libraries/Core/src/Misc/BufferCacheBase.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace UniSpy.Server.Core.Misc -{ - public abstract class BufferCacheBase - { - public T InCompleteBuffer { get; protected set; } - - protected BufferCacheBase() - { - } - - /// - /// Process the incoming buffer, if buffer is complete return true, otherwise return false - /// - /// - public abstract bool ProcessBuffer(T buffer, out T completeBuffer); - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpBufferCache.cs b/src/Libraries/Core/src/Network/Http/Server/HttpBufferCache.cs deleted file mode 100644 index ebfe6d8d1..000000000 --- a/src/Libraries/Core/src/Network/Http/Server/HttpBufferCache.cs +++ /dev/null @@ -1,42 +0,0 @@ -namespace UniSpy.Server.Core.Network.Http.Server -{ - public class HttpBufferCache : UniSpy.Server.Core.Misc.BufferCacheBase - { - public HttpBufferCache() - { - } - - public override bool ProcessBuffer(string buffer, out string completeBuffer) - { - if (buffer.Contains("")) - { - if (InCompleteBuffer is null) - { - completeBuffer = buffer; - return true; - } - else - { - completeBuffer = InCompleteBuffer + buffer; - InCompleteBuffer = null; - return true; - } - } - else - { - if (InCompleteBuffer is null) - { - InCompleteBuffer = buffer; - completeBuffer = null; - return false; - } - else - { - InCompleteBuffer += buffer; - completeBuffer = null; - return false; - } - } - } - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs b/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs index a5f5517fa..7b4dfba2e 100644 --- a/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs +++ b/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs @@ -1,62 +1,38 @@ -using System.Linq; using System.Net; +using System.Text; using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; using UniSpy.Server.Core.Events; -namespace UniSpy.Server.Core.Network.Http.Server -{ - public class HttpConnection : NetCoreServer.HttpSession, IHttpConnection - { - public IPEndPoint RemoteIPEndPoint { get; private set; } - public NetworkConnectionType ConnectionType => NetworkConnectionType.Http; - public IConnectionManager Manager => (IConnectionManager)Server; +namespace UniSpy.Server.Core.Network.Http.Server; - public event OnConnectedEventHandler OnConnect; - public event OnDisconnectedEventHandler OnDisconnect; - public event OnReceivedEventHandler OnReceive; - private HttpBufferCache _bufferCache = new HttpBufferCache(); - public HttpConnection(HttpConnectionManager server) : base(server) - { - } - protected override void OnConnecting() - { - if (RemoteIPEndPoint is null) - { - RemoteIPEndPoint = (IPEndPoint)Socket.RemoteEndPoint; - } - base.OnConnecting(); - } - protected override void OnConnected() => OnConnect(); - protected override void OnDisconnected() => OnDisconnect(); - protected override void OnReceived(byte[] buffer, long offset, long size) - { - var req = UniSpyEncoding.GetString(buffer.Take((int)size).ToArray()); - string compeleteBuffer; - if (_bufferCache.ProcessBuffer(req, out compeleteBuffer)) - { - var completeBytes = UniSpyEncoding.GetBytes(compeleteBuffer); - base.OnReceived(completeBytes, offset, completeBytes.Length); - } - } - protected override void OnReceivedRequest(NetCoreServer.HttpRequest request) => OnReceive(new HttpRequest(request)); - void IConnection.Send(string response) - { - // Response.MakeOkResponse(); - Response.SetBegin(200); - Response.SetBody(response); - base.SendResponse(); - } +public class HttpConnection : IHttpConnection +{ + public IConnectionManager Manager { get; private set; } + public HttpListenerContext Context { get; private set; } + public IPEndPoint RemoteIPEndPoint { get; private set; } - void IConnection.Send(byte[] response) - { - // Response.MakeOkResponse(); - Response.SetBegin(200); - Response.SetBody(response); - base.SendResponse(); - } + public NetworkConnectionType ConnectionType { get; } = NetworkConnectionType.Http; + public event OnReceivedEventHandler OnReceive; + public HttpConnection(HttpListenerContext context, IConnectionManager manager) + { + Manager = manager; + Context = context; + RemoteIPEndPoint = context.Request.RemoteEndPoint; + } + public void OnReceived(IHttpRequest request) + { + OnReceive(request); + } - void ITcpConnection.Disconnect() => Disconnect(); + public void Send(string response) + { + Send(Encoding.UTF8.GetBytes(response)); + } + public void Send(byte[] response) + { + Context.Response.StatusCode = (int)HttpStatusCode.OK; + Context.Response.ContentType = "application/xml?"; + Context.Response.OutputStream.Write(response); } } \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs b/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs index bf82505a4..22844d1da 100644 --- a/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs +++ b/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs @@ -1,25 +1,51 @@ +using System; using System.Net; -using NetCoreServer; +using System.Threading.Tasks; using UniSpy.Server.Core.Abstraction.Interface; using UniSpy.Server.Core.Events; +using UniSpy.Server.Core.Logging; -namespace UniSpy.Server.Core.Network.Http.Server +namespace UniSpy.Server.Core.Network.Http.Server; + +public class HttpConnectionManager : IConnectionManager, IDisposable { - public class HttpConnectionManager : NetCoreServer.HttpServer, IConnectionManager + public event OnConnectingEventHandler OnInitialization; + public HttpListener Listener { get; private set; } + public HttpConnectionManager(IPEndPoint endPoint) { - public event OnConnectingEventHandler OnInitialization; - public HttpConnectionManager(IPEndPoint endpoint) : base(endpoint) - { - } - - protected override NetCoreServer.TcpSession CreateSession() => new UniSpy.Server.Core.Network.Http.Server.HttpConnection(this); - protected override void OnConnecting(TcpSession connection) + Listener = new HttpListener(); + Listener.Prefixes.Add($"http://localhost:{endPoint.Port}/"); + } + public void Start() + { + Listener.Start(); + Task.Run(() => { - OnInitialization((HttpConnection)connection); - base.OnConnecting(connection); - } - - public new void Start() => base.Start(); + while (true) + { + try + { + var context = Listener.GetContext(); + var raw = context.Request; + var request = new HttpRequest(raw); + var conn = new HttpConnection(context, this); + OnConnecting(conn); + conn.OnReceived(request); + } + catch (Exception ex) + { + LogWriter.LogError(ex); + } + } + }); + } + public void OnConnecting(IHttpConnection connection) + { + OnInitialization((IConnection)connection); + } + public void Dispose() + { + Listener.Stop(); } } \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpRequest.cs b/src/Libraries/Core/src/Network/Http/Server/HttpRequest.cs index 0638c0e73..35e7707a6 100644 --- a/src/Libraries/Core/src/Network/Http/Server/HttpRequest.cs +++ b/src/Libraries/Core/src/Network/Http/Server/HttpRequest.cs @@ -1,45 +1,26 @@ using System; +using System.IO; +using System.Net; +using System.Text; using UniSpy.Server.Core.Abstraction.Interface; -namespace UniSpy.Server.Core.Network.Http.Server +namespace UniSpy.Server.Core.Network.Http.Server; +public class HttpRequest : IHttpRequest { - public class HttpRequest : IHttpRequest + public string Body { get; private set; } + public Uri Url { get; private set; } + public string Method { get; private set; } + public HttpListenerRequest RawRequest { get; private set; } + public HttpRequest(HttpListenerRequest rawRequest) { - public Uri Uri => new Uri(Url); - - public byte[] BodyBytes { get; private set; } - - public string Body { get; private set; } - - public long Cookies { get; private set; } - - public long Headers { get; private set; } - - public string Protocol { get; private set; } - - public string Url { get; private set; } - - public string Method { get; private set; } - - public bool KeepAlive { get; private set; } - - - public HttpRequest(NetCoreServer.HttpRequest request) + RawRequest = rawRequest; + Method = RawRequest.HttpMethod; + Url = RawRequest.Url; + using (var stream = rawRequest.InputStream) { - Url = request.Url; - BodyBytes = request.BodyBytes; - Body = request.Body; - Cookies = request.Cookies; - Headers = request.Headers; - Protocol = request.Protocol; - Method = request.Method; - KeepAlive = false; - - for (var m = 0; m < request.Headers; m++) + using (var reader = new StreamReader(stream, rawRequest.ContentEncoding)) { - var k = request.Header(m); - if (k.Item1 == "Connection" && k.Item2.ToLower() == "keep-alive") - KeepAlive = true; + Body = reader.ReadToEnd(); } } } diff --git a/src/Libraries/Core/src/Network/RemoteObject.cs b/src/Libraries/Core/src/Network/RemoteObject.cs index 150253892..98656820f 100644 --- a/src/Libraries/Core/src/Network/RemoteObject.cs +++ b/src/Libraries/Core/src/Network/RemoteObject.cs @@ -52,8 +52,9 @@ public class RemoteTcpConnectionManager : IConnectionManager { #pragma warning disable CS0067 public event OnConnectingEventHandler OnInitialization; - public void Start() => throw new UniSpy.Exception("Remote tcp connection do not have this method."); public RemoteTcpConnectionManager() { } + public void Start() { } + public void Dispose() { } } public class RemoteTcpConnection : ITcpConnection diff --git a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs index b937c9807..6eb9ea300 100644 --- a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs +++ b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs @@ -1,59 +1,80 @@ +using System; using System.Linq; using System.Net; +using System.Net.Sockets; +using System.Threading.Tasks; using UniSpy.Server.Core.Abstraction.Interface; using UniSpy.Server.Core.Encryption; using UniSpy.Server.Core.Events; +using UniSpy.Server.Core.Logging; namespace UniSpy.Server.Core.Network.Tcp.Server { - /// - /// This is a template class that helps creating a TCP Session (formerly TCP stream) - /// with logging functionality and ServerName, as required in the old network stack. - /// - public class TcpConnection : NetCoreServer.TcpSession, ITcpConnection + public class TcpConnection : ITcpConnection, IDisposable { - /// - /// remote endpoint will dispose when disconnecting, however we need that in some situation, - /// we must save it here for further use - /// - /// + public IConnectionManager Manager { get; private set; } + public IPEndPoint RemoteIPEndPoint { get; private set; } - IConnectionManager IConnection.Manager => (IConnectionManager)base.Server; - public NetworkConnectionType ConnectionType => NetworkConnectionType.Tcp; + + public NetworkConnectionType ConnectionType { get; } = NetworkConnectionType.Tcp; + public event OnConnectedEventHandler OnConnect; public event OnDisconnectedEventHandler OnDisconnect; public event OnReceivedEventHandler OnReceive; - public TcpConnection(TcpConnectionManager server) : base(server) + + public TcpClient Client { get; private set; } + public TcpConnection(TcpClient client, IConnectionManager manager) { + RemoteIPEndPoint = (IPEndPoint)client.Client.RemoteEndPoint; + Client = client; + Manager = manager; } - protected override void OnConnecting() + public void Disconnect() { - // we set ipendpoint here - if (RemoteIPEndPoint is null) - { - RemoteIPEndPoint = (IPEndPoint)Socket.RemoteEndPoint; - } - base.OnConnecting(); + OnDisconnected(); + Client.Client.Disconnect(true); } - protected override void OnConnected() + + public void Send(string response) => Send(UniSpyEncoding.GetBytes(response)); + + public void Send(byte[] response) => Client.Client.Send(response); + + public void OnConnected() { OnConnect(); - base.OnConnected(); - } - protected override void OnDisconnected() - { - OnDisconnect(); - base.OnDisconnected(); + Task.Run(() => StartReceiving()); } - protected override void OnReceived(byte[] buffer, long offset, long size) + + private void StartReceiving() { - OnReceive(buffer.Skip((int)offset).Take((int)size).ToArray()); - base.OnReceived(buffer, offset, size); + var stream = Client.GetStream(); + byte[] buffer = new byte[2048]; + int bytesRead; + while (true) + { + try + { + bytesRead = stream.Read(buffer, 0, buffer.Length); + if (bytesRead == 0) + { + OnDisconnected(); + break; // Connection closed by the client + } + + var receivedData = buffer.Take(bytesRead).ToArray(); + OnReceived(receivedData); + } + catch (Exception ex) + { + LogWriter.LogError(ex); + OnDisconnected(); + break; + } + } } - void ITcpConnection.Disconnect() => Disconnect(); - public new void Send(string response) => Send(UniSpyEncoding.GetBytes(response)); - public new void Send(byte[] response) => base.Send(response); + public void OnDisconnected() => OnDisconnect(); + public void OnReceived(object buffer) => OnReceive(buffer); + public void Dispose() => Client.Dispose(); } -} - +} \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnectionManager.cs b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnectionManager.cs index 4eefd03e4..6374a05f4 100644 --- a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnectionManager.cs +++ b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnectionManager.cs @@ -1,35 +1,48 @@ using System; +using System.Collections.Concurrent; using System.Net; +using System.Net.Sockets; +using System.Threading.Tasks; using UniSpy.Server.Core.Abstraction.Interface; using UniSpy.Server.Core.Events; -namespace UniSpy.Server.Core.Network.Tcp.Server +namespace UniSpy.Server.Core.Network.Tcp.Server; + +public class TcpConnectionManager : IConnectionManager, IDisposable { - /// - /// This is a template class that helps creating a TCP Server with logging functionality and ServerName, as required in the old network stack. - /// - public class TcpConnectionManager : NetCoreServer.TcpServer, IConnectionManager + public event OnConnectingEventHandler OnInitialization; + public TcpListener Listener; + public ConcurrentDictionary ConnectionPool = new(); + + public TcpConnectionManager(IPEndPoint endPoint) { - public TcpConnectionManager(IPEndPoint endpoint) : base(endpoint) - { - } + Listener = new TcpListener(endPoint); + } - public event OnConnectingEventHandler OnInitialization; - public new virtual void Start() + public void Start() + { + Listener.Start(); + Task.Run(() => { - if (OptionSendBufferSize > int.MaxValue || OptionReceiveBufferSize > int.MaxValue) + while (true) { - throw new ArgumentException("Buffer size can not big than length of integer!"); + var client = Listener.AcceptTcpClient(); + var conn = new TcpConnection(client, this); + Task.Run(() => OnConnecting(conn)); } - base.Start(); - } - protected override NetCoreServer.TcpSession CreateSession() => new TcpConnection(this); + }); + } - protected override void OnConnecting(NetCoreServer.TcpSession connection) - { - OnInitialization((IConnection)connection); - base.OnConnecting(connection); - } + + public void OnConnecting(ITcpConnection connection) + { + OnInitialization((IConnection)connection); + (connection as TcpConnection).OnConnected(); + } + + public void Dispose() + { + Listener.Stop(); } -} +} \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/Udp/Client/UniSpyUdpClient.cs b/src/Libraries/Core/src/Network/Udp/Client/UniSpyUdpClient.cs deleted file mode 100644 index 4dde15139..000000000 --- a/src/Libraries/Core/src/Network/Udp/Client/UniSpyUdpClient.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Net; -using System.Net.Sockets; -using UdpClient = NetCoreServer.UdpClient; - -namespace UniSpy.Server.Core.Network.Udp.Client -{ - public class UniSpyUdpClient : UdpClient - { - - public UniSpyUdpClient(IPAddress address, int port) : base(address, port) - { - } - protected override void OnConnected() - { - Console.WriteLine($"Echo UDP client connected a new connection with Id {Id}"); - - // Start receive datagrams - ReceiveAsync(); - } - protected override void OnDisconnected() - { - Console.WriteLine($"Echo UDP client disconnected a connection with Id {Id}"); - base.OnConnected(); - } - - protected override void OnReceived(EndPoint endpoint, byte[] buffer, long offset, long size) => ReceiveAsync(); - - protected override void OnError(SocketError error) - { - Console.WriteLine($"Echo UDP client caught an error with code {error}"); - } - - } -} diff --git a/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs b/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs index c672a3984..75c863e50 100644 --- a/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs +++ b/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs @@ -1,55 +1,54 @@ -using System.Threading.Tasks; using System.Net; -using System.Threading; using UniSpy.Server.Core.Abstraction.Interface; using UniSpy.Server.Core.Encryption; using UniSpy.Server.Core.Events; -namespace UniSpy.Server.Core.Network.Udp.Server +namespace UniSpy.Server.Core.Network.Udp.Server; + +public class UdpConnection : IUdpConnection { - /// - /// A remote endpoint wrapper for UDP server which unifies the interface for - /// - public class UdpConnection : IUdpConnection - { - public UdpConnectionManager Manager { get; private set; } - public IPEndPoint RemoteIPEndPoint { get; private set; } - IConnectionManager IConnection.Manager => Manager; - public NetworkConnectionType ConnectionType => NetworkConnectionType.Udp; - public event OnReceivedEventHandler OnReceive; - - public UdpConnection(UdpConnectionManager server, IPEndPoint endPoint) - { - Manager = server; - RemoteIPEndPoint = endPoint; - } - public virtual void OnReceived(byte[] message) - { - // Server.ReceiveAsync(); - ThreadPool.QueueUserWorkItem(o => { try { Manager.ReceiveAsync(); } catch { } }); - Task.Run(() => OnReceive(message)); - } - - public bool Send(object response) - { - if (response.GetType() == typeof(string)) - { - Manager.Send(RemoteIPEndPoint, UniSpyEncoding.GetBytes((string)response)); - return true; - } - else if (response.GetType() == typeof(byte[])) - { - Manager.Send(RemoteIPEndPoint, (byte[])response); - return true; - } - else - { - throw new UniSpy.Exception("IUdpConnection.Send: response must be string or byte[]"); - } - } - public void Send(string response) => Send(RemoteIPEndPoint, UniSpyEncoding.GetBytes(response)); - public void Send(byte[] response) => Manager.Send(RemoteIPEndPoint, response); - public void Send(IPEndPoint endPoint, string response) => Send(endPoint, UniSpyEncoding.GetBytes(response)); - public void Send(IPEndPoint endPoint, byte[] response) => Manager.Send(endPoint, response); - } -} + public IConnectionManager Manager { get; private set; } + + public IPEndPoint RemoteIPEndPoint { get; private set; } + + public NetworkConnectionType ConnectionType { get; } = NetworkConnectionType.Udp; + + public event OnReceivedEventHandler OnReceive; + public UdpConnection(IPEndPoint endPoint, IConnectionManager manager) + { + RemoteIPEndPoint = endPoint; + Manager = manager; + } + public void OnReceived(byte[] buffer) + { + OnReceive(buffer); + } + public void Send(IPEndPoint endPoint, byte[] response) + { + (Manager as UdpConnectionManager).Listener.Send(response, response.Length, endPoint); + } + + public void Send(IPEndPoint endPoint, string response) + { + Send(endPoint, UniSpyEncoding.GetBytes(response)); + } + + public void Send(string response) + { + Send(UniSpyEncoding.GetBytes(response)); + } + + public void Send(byte[] response) + { + (Manager as UdpConnectionManager).Listener.Send(response, response.Length, RemoteIPEndPoint); + } + + public override bool Equals(object obj) + { + return RemoteIPEndPoint.Equals((obj as UdpConnection).RemoteIPEndPoint); + } + public override int GetHashCode() + { + return RemoteIPEndPoint.GetHashCode(); + } +} \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/Udp/Server/UdpConnectionManager.cs b/src/Libraries/Core/src/Network/Udp/Server/UdpConnectionManager.cs index ee96f7de9..4587c64b7 100644 --- a/src/Libraries/Core/src/Network/Udp/Server/UdpConnectionManager.cs +++ b/src/Libraries/Core/src/Network/Udp/Server/UdpConnectionManager.cs @@ -1,48 +1,42 @@ using System; -using System.Linq; using System.Net; +using System.Net.Sockets; +using System.Threading.Tasks; using UniSpy.Server.Core.Abstraction.Interface; using UniSpy.Server.Core.Events; -namespace UniSpy.Server.Core.Network.Udp.Server +namespace UniSpy.Server.Core.Network.Udp.Server; + +public class UdpConnectionManager : IConnectionManager, IDisposable { - /// - /// This is a template class that helps creating a UDP Server with - /// logging functionality and ServerName, as required in the old network stack. - /// - public class UdpConnectionManager : NetCoreServer.UdpServer, IConnectionManager + public event OnConnectingEventHandler OnInitialization; + public UdpClient Listener { get; private set; } + public UdpConnectionManager(IPEndPoint endPoint) { - public UdpConnectionManager(IPEndPoint endpoint) : base(endpoint) - { - } - public event OnConnectingEventHandler OnInitialization; + Listener = new UdpClient(endPoint); + } - public new virtual void Start() + public void Start() + { + Task.Run(() => { - if (OptionSendBufferSize > int.MaxValue || OptionReceiveBufferSize > int.MaxValue) + while (true) { - throw new ArgumentException("Buffer size can not big than length of integer!"); + var clientEndPoint = new IPEndPoint(IPAddress.Any, (Listener.Client.RemoteEndPoint as IPEndPoint).Port); + var data = Listener.Receive(ref clientEndPoint); + var conn = new UdpConnection(clientEndPoint, this); + OnConnecting(conn); + Task.Run(() => conn.OnReceived(data)); } - base.Start(); - } - protected override void OnStarted() => ReceiveAsync(); - - /// - /// Send unencrypted data - /// - /// plaintext - /// is sending succeed - protected override void OnReceived(EndPoint endPoint, byte[] buffer, long offset, long size) - { - var connection = CreateConnection((IPEndPoint)endPoint); - (connection as UdpConnection).OnReceived(buffer.Skip((int)offset).Take((int)size).ToArray()); - } - protected virtual IUdpConnection CreateConnection(IPEndPoint endPoint) - { - var connection = new UdpConnection(this, endPoint); - var client = OnInitialization(connection); - return client.Connection as IUdpConnection; - } + }); + } + public void OnConnecting(IUdpConnection connection) + { + OnInitialization?.Invoke(connection); } -} + public void Dispose() + { + Listener.Dispose(); + } +} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/Misc/BufferCache.cs b/src/Servers/Chat/src/Aggregate/Misc/BufferCache.cs deleted file mode 100644 index dcebaa187..000000000 --- a/src/Servers/Chat/src/Aggregate/Misc/BufferCache.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Linq; - -namespace UniSpy.Server.Chat.Aggregate.Misc -{ - /// - /// Tcp data will be received uncomplete, we need to combine it according to gamespy protocol - /// - public class BufferCache : Core.Misc.BufferCacheBase - { - public BufferCache() - { - } - - public override bool ProcessBuffer(byte[] buffer, out byte[] completeBuffer) - { - if (buffer[buffer.Length - 1] == 0x0A) - { - // check last _incomplteBuffer if it has incomplete message, then combine them - if (InCompleteBuffer is not null) - { - completeBuffer = InCompleteBuffer.Concat(buffer).ToArray(); - InCompleteBuffer = null; - } - else - { - completeBuffer = buffer; - } - return true; - } - else - { - // message is not finished, we add it in _completeBuffer - if (InCompleteBuffer is null) - { - InCompleteBuffer = buffer; - } - else - { - InCompleteBuffer = InCompleteBuffer.Concat(buffer).ToArray(); - } - completeBuffer = null; - return false; - } - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Application/Client.cs b/src/Servers/Chat/src/Application/Client.cs index 580d17a82..a521d32fa 100644 --- a/src/Servers/Chat/src/Application/Client.cs +++ b/src/Servers/Chat/src/Application/Client.cs @@ -18,7 +18,6 @@ public class Client : ClientBase, IShareClient public new ClientInfo Info { get => (ClientInfo)base.Info; private set => base.Info = value; } public new ITcpConnection Connection => (ITcpConnection)base.Connection; public bool IsRemoteClient => !ClientManager.ClientPool.ContainsKey(Connection.RemoteIPEndPoint); - private BufferCache _bufferCache = new BufferCache(); private RemoteClient _remoteClient; public Client(IConnection connection, IServer server) : base(connection, server) { @@ -41,12 +40,9 @@ protected override void EventBinding() protected override void OnReceived(object buffer) { var message = DecryptMessage((byte[])buffer); - if (_bufferCache.ProcessBuffer(message, out var completeBuffer)) - { - this.LogNetworkReceiving(completeBuffer); - var switcher = CreateSwitcher(completeBuffer); - switcher.Handle(); - } + this.LogNetworkReceiving(message); + var switcher = CreateSwitcher(message); + switcher.Handle(); } protected override void OnDisconnected() { diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs index 5c4b96016..25aa7174f 100755 --- a/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs +++ b/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs @@ -18,7 +18,7 @@ public PrivateHandler(IShareClient client, PrivateRequest request) : base(client } protected override void ChannelMessageDataOpration() { - var data = ClientManager.ClientPool; + // var data = ClientManager.ClientPool; // if (_channel.Mode.IsModeratedChannel) // { // return; diff --git a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/Conneciton/Client.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/Conneciton/Client.cs new file mode 100644 index 000000000..e69de29bb diff --git a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/Conneciton/Server.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/Conneciton/Server.cs new file mode 100644 index 000000000..e69de29bb diff --git a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs index c536607f3..8d658c76d 100644 --- a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs +++ b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Linq; using System.Net; -using System.Threading; using UniSpy.Server.Core.Abstraction.Interface; using UniSpy.Server.Core.Extension; using UniSpy.Server.Core.Logging; @@ -11,22 +10,22 @@ namespace UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay { - public class ConnectionListener : NetCoreServer.UdpServer + public class ConnectionListener : IDisposable { private IConnectionManager _manager; public uint Cookie { get; private set; } - public IPEndPoint ListeningEndPoint => (IPEndPoint)Endpoint; + public IPEndPoint ListeningEndPoint { get; private set; } + public bool IsDisposed { get; private set; } private EasyTimer _timer; - private IPEndPoint _gameServerEndPoint; - private IPEndPoint _gameClientEndPoint; + private IUdpConnection _clientConnection; + private IUdpConnection _serverConnection; private List _gameServerValidIPs; private List _gameClientValidIPs; - public ConnectionListener(IPEndPoint listeningEndPoint, uint cookie, List gameServerIPs, List gameClientIPs) : base(listeningEndPoint) + public ConnectionListener(IPEndPoint listeningEndPoint, uint cookie, List gameServerIPs, List gameClientIPs) { + ListeningEndPoint = listeningEndPoint; _manager = new UdpConnectionManager(listeningEndPoint); - // _manager.OnInitialization - // _gameServerAddress = IPEndPoint.Parse(request.GameServerIP); - // _gameClientAddress = IPEndPoint.Parse(request.GameClientIP); + _manager.OnInitialization += OnIntialization; _gameServerValidIPs = gameServerIPs; _gameClientValidIPs = gameClientIPs; Cookie = cookie; @@ -36,15 +35,40 @@ public ConnectionListener(IPEndPoint listeningEndPoint, uint cookie, List OnReceived((IUdpConnection)connection, (byte[])buffer); + return default; + } + } + lock (_serverConnection) + { + if (_serverConnection is null) + { + if (_gameClientValidIPs.Contains(connection.RemoteIPEndPoint.ToString()) && _serverConnection is null) + { + _serverConnection = connection as IUdpConnection; + connection.OnReceive += (buffer) => OnReceived((IUdpConnection)connection, (byte[])buffer); + return default; + } + } + } + return default; } - protected override void OnStarted() => ReceiveAsync(); private void CheckExpiredClient() { - if (_gameClientEndPoint is null || _gameServerEndPoint is null || _timer.IsExpired) + if (_serverConnection is null || _clientConnection is null || _timer.IsExpired) { if (!IsDisposed) { @@ -52,10 +76,10 @@ private void CheckExpiredClient() PingHandler.ConnectionListeners.TryRemove(this.Cookie, out _); LogWriter.LogDebug($"[{ListeningEndPoint}] gamespy listener shutdown."); _timer.Dispose(); + } } } - protected override void OnReceived(EndPoint endpoint, byte[] buffer, long offset, long size) => OnReceived(endpoint, buffer.Skip((int)offset).Take((int)size).ToArray()); private bool CheckValidation(byte[] buffer) { var magic = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2 }; @@ -72,51 +96,32 @@ private bool CheckValidation(byte[] buffer) return true; } - protected void OnReceived(EndPoint endPoint, byte[] buffer) + protected void OnReceived(IUdpConnection connection, byte[] buffer) { - ThreadPool.QueueUserWorkItem(o => { try { ReceiveAsync(); } catch { } }); - // we only accept the gamespy client message - if (_gameServerEndPoint is null || _gameClientEndPoint is null) + if (_clientConnection.Equals(connection)) { - - if (_gameServerValidIPs.Contains(endPoint.ToString()) && _gameServerEndPoint is null) - { - _gameServerEndPoint = (IPEndPoint)endPoint; - } - else if (_gameClientValidIPs.Contains(endPoint.ToString()) && _gameClientEndPoint is null) - { - _gameClientEndPoint = (IPEndPoint)endPoint; - } - else - { - //ignore - } - - LogWriter.LogDebug($"[{endPoint}] [recv] {StringExtensions.ConvertPrintableBytesToString(buffer)} [{StringExtensions.ConvertByteToHexString(buffer)}]"); + ForwardMessage(_clientConnection, _serverConnection, buffer); + return; } - else + + if (_serverConnection.Equals(connection)) { - if (_gameServerEndPoint.Equals((IPEndPoint)endPoint)) - { - ForwardMessage(_gameServerEndPoint, _gameClientEndPoint, buffer); - } - else if (_gameClientEndPoint.Equals((IPEndPoint)endPoint)) - { - ForwardMessage(_gameClientEndPoint, _gameServerEndPoint, buffer); - } - else - { - //ignore - } + ForwardMessage(_serverConnection, _clientConnection, buffer); + return; } } - public void ForwardMessage(IPEndPoint sender, IPEndPoint receiver, byte[] data) + public void ForwardMessage(IUdpConnection sender, IUdpConnection receiver, byte[] data) { _timer.RefreshLastActiveTime(); - SendAsync(receiver, data); + receiver.Send(data); LogWriter.LogDebug($"[{sender}] => [{receiver}] {StringExtensions.ConvertPrintableBytesToString(data)} [{StringExtensions.ConvertByteToHexString(data)}]"); } + + public void Dispose() + { + _manager.Dispose(); + } } } \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Application/Server.cs b/src/Servers/PresenceSearchPlayer/src/Application/Server.cs index 51a79de7c..745ae7231 100644 --- a/src/Servers/PresenceSearchPlayer/src/Application/Server.cs +++ b/src/Servers/PresenceSearchPlayer/src/Application/Server.cs @@ -16,6 +16,6 @@ public Server() { } public Server(IConnectionManager manager) : base(manager) { } protected override IClient CreateClient(IConnection connection) => new Client(connection, this); - protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new TcpConnectionManager(IPEndPoint.Parse("0.0.0.0:29901")); + protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new TcpConnectionManager(endPoint); } } \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Aggregate/Misc/BufferCache.cs b/src/Servers/ServerBrowser/src/V2/Aggregate/Misc/BufferCache.cs deleted file mode 100644 index 85241ef62..000000000 --- a/src/Servers/ServerBrowser/src/V2/Aggregate/Misc/BufferCache.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Linq; -using UniSpy.Server.ServerBrowser.V2.Enumerate; - -namespace UniSpy.Server.ServerBrowser.V2.Aggregate.Misc -{ - public class BufferCache : Core.Misc.BufferCacheBase - { - public override bool ProcessBuffer(byte[] buffer, out byte[] completeBuffer) - { - if (((RequestType)buffer[2]) == RequestType.SendMessageRequest) - { - if (buffer.Length > 9) - { - // complete sendmessage request received - completeBuffer = buffer; - return true; - } - else - { - InCompleteBuffer = buffer; - completeBuffer = null; - return false; - } - } - else if (buffer.Take(6).SequenceEqual(NatNegotiation.Abstraction.BaseClass.RequestBase.MagicData)) - { - if (InCompleteBuffer is not null) - { - completeBuffer = InCompleteBuffer.Concat(buffer).ToArray(); - InCompleteBuffer = null; - return true; - } - else - { - // we ignore natneg message when _incompleteBuffer is null - completeBuffer = null; - return false; - } - } - else - { - completeBuffer = buffer; - return true; - } - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Application/Client.cs b/src/Servers/ServerBrowser/src/V2/Application/Client.cs index d08582f55..7b19f9930 100644 --- a/src/Servers/ServerBrowser/src/V2/Application/Client.cs +++ b/src/Servers/ServerBrowser/src/V2/Application/Client.cs @@ -8,7 +8,6 @@ namespace UniSpy.Server.ServerBrowser.V2.Application public sealed class Client : ClientBase { public new ClientInfo Info { get => (ClientInfo)base.Info; set => base.Info = value; } - private BufferCache _bufferCache = new BufferCache(); public Client(IConnection connection, IServer server) : base(connection, server) { // Crypto is init in ServerListHandler @@ -17,13 +16,5 @@ public Client(IConnection connection, IServer server) : base(connection, server) } protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, (byte[])buffer); - - protected override void OnReceived(object buffer) - { - if (_bufferCache.ProcessBuffer((byte[])buffer, out var completeBuffer)) - { - base.OnReceived(completeBuffer); - } - } } } \ No newline at end of file diff --git a/src/Servers/WebServer/src/Application/Client.cs b/src/Servers/WebServer/src/Application/Client.cs index 986e79cdc..299552a4d 100644 --- a/src/Servers/WebServer/src/Application/Client.cs +++ b/src/Servers/WebServer/src/Application/Client.cs @@ -24,8 +24,6 @@ protected override void OnReceived(object buffer) return; } base.OnReceived(buffer); - if (!rq.KeepAlive) - (Connection as IHttpConnection)?.Disconnect(); } } } \ No newline at end of file diff --git a/src/Servers/WebServer/src/Application/Server.cs b/src/Servers/WebServer/src/Application/Server.cs index c13d9522e..a4c0f3865 100644 --- a/src/Servers/WebServer/src/Application/Server.cs +++ b/src/Servers/WebServer/src/Application/Server.cs @@ -11,12 +11,19 @@ static Server() { _name = "WebServer"; } - public Server(){ } + public Server() { } - public Server(IConnectionManager manager) : base(manager){} + public Server(IConnectionManager manager) : base(manager) { } protected override IClient CreateClient(IConnection connection) => new Client(connection, this); protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new HttpConnectionManager(endPoint); + + protected override IClient HandleConnectionInitialization(IConnection connection) + { + var client = CreateClient(connection); + return client; + } + } } \ No newline at end of file diff --git a/src/Servers/WebServer/test/GeneralTest.cs b/src/Servers/WebServer/test/GeneralTest.cs index bb98946a7..88f28a44e 100644 --- a/src/Servers/WebServer/test/GeneralTest.cs +++ b/src/Servers/WebServer/test/GeneralTest.cs @@ -15,28 +15,28 @@ public void InlegalMessageTest() var client = MokeObject.CreateClient(); var req = new Mock(); req.Setup(s => s.Body).Returns("username=admin&psd=Feefifofum"); - req.Setup(s => s.Url).Returns("abcdefg"); + req.Setup(s => s.Url).Returns(new System.Uri("abcdefg")); var switcher = new CmdSwitcher(client, req.Object); switcher.Handle(); } - [Fact] - public void BufferCacheTest() - { - // Given - var req1 = "POST /AuthService/AuthService.asmx HTTP/1.1\r\nHost: capricorn.auth.pubsvs.gamespy.com\r\nUser-Agent: GameSpyHTTP/1.0\r\nConnection: close\r\nContent-Length: 861\r\nContent-Type: text/xml\r\nSOAPAction: \"http://gamespy.net/AuthService/LoginUniqueNick\"\r\n"; - var req2 = "\r\n19595asdf210002899d0429a3bc5c24934320e526bed7ad645fb56c7783e2c8afef77e0dd1005d3383b58eb6919baae85881183148a4b4ebe1169475c85e305a2ca4f2d30d9ecf2275dda0706a14d15c5eb755bbca034168b13997f9111ebe821d640f64622586f231479a8516d47c046a819b19e03ba4697c68e3879dafab9380032333233"; - // When - var cache = new HttpBufferCache(); - string completeBuffer; - Assert.False(cache.ProcessBuffer(req1, out completeBuffer)); + // [Fact] + // public void BufferCacheTest() + // { + // // Given + // var req1 = "POST /AuthService/AuthService.asmx HTTP/1.1\r\nHost: capricorn.auth.pubsvs.gamespy.com\r\nUser-Agent: GameSpyHTTP/1.0\r\nConnection: close\r\nContent-Length: 861\r\nContent-Type: text/xml\r\nSOAPAction: \"http://gamespy.net/AuthService/LoginUniqueNick\"\r\n"; + // var req2 = "\r\n19595asdf210002899d0429a3bc5c24934320e526bed7ad645fb56c7783e2c8afef77e0dd1005d3383b58eb6919baae85881183148a4b4ebe1169475c85e305a2ca4f2d30d9ecf2275dda0706a14d15c5eb755bbca034168b13997f9111ebe821d640f64622586f231479a8516d47c046a819b19e03ba4697c68e3879dafab9380032333233"; + // // When + // var cache = new HttpBufferCache(); + // string completeBuffer; + // Assert.False(cache.ProcessBuffer(req1, out completeBuffer)); - Assert.True(cache.ProcessBuffer(req2, out completeBuffer)); + // Assert.True(cache.ProcessBuffer(req2, out completeBuffer)); - Assert.True(cache.InCompleteBuffer == null); - var resultreq = "POST /AuthService/AuthService.asmx HTTP/1.1\r\nHost: capricorn.auth.pubsvs.gamespy.com\r\nUser-Agent: GameSpyHTTP/1.0\r\nConnection: close\r\nContent-Length: 861\r\nContent-Type: text/xml\r\nSOAPAction: \"http://gamespy.net/AuthService/LoginUniqueNick\"\r\n\r\n19595asdf210002899d0429a3bc5c24934320e526bed7ad645fb56c7783e2c8afef77e0dd1005d3383b58eb6919baae85881183148a4b4ebe1169475c85e305a2ca4f2d30d9ecf2275dda0706a14d15c5eb755bbca034168b13997f9111ebe821d640f64622586f231479a8516d47c046a819b19e03ba4697c68e3879dafab9380032333233"; - Assert.True(completeBuffer == resultreq); - // Then - } + // Assert.True(cache.InCompleteBuffer == null); + // var resultreq = "POST /AuthService/AuthService.asmx HTTP/1.1\r\nHost: capricorn.auth.pubsvs.gamespy.com\r\nUser-Agent: GameSpyHTTP/1.0\r\nConnection: close\r\nContent-Length: 861\r\nContent-Type: text/xml\r\nSOAPAction: \"http://gamespy.net/AuthService/LoginUniqueNick\"\r\n\r\n19595asdf210002899d0429a3bc5c24934320e526bed7ad645fb56c7783e2c8afef77e0dd1005d3383b58eb6919baae85881183148a4b4ebe1169475c85e305a2ca4f2d30d9ecf2275dda0706a14d15c5eb755bbca034168b13997f9111ebe821d640f64622586f231479a8516d47c046a819b19e03ba4697c68e3879dafab9380032333233"; + // Assert.True(completeBuffer == resultreq); + // // Then + // } } } \ No newline at end of file From 188b7e16516d67d5a85bbb9b37c92e8a3ba21c4c Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 2 Nov 2023 21:10:35 +0800 Subject: [PATCH 061/231] refactor(web): disconnect client by header --- src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs b/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs index 7b4dfba2e..69ff959f0 100644 --- a/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs +++ b/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs @@ -34,5 +34,9 @@ public void Send(byte[] response) Context.Response.StatusCode = (int)HttpStatusCode.OK; Context.Response.ContentType = "application/xml?"; Context.Response.OutputStream.Write(response); + if (Context.Request.Headers["Connection"] == "close") + { + Context.Response.Close(); + } } } \ No newline at end of file From b255ac45681bb78b65f7e7bc746a469c7b6f6920 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 2 Nov 2023 22:59:13 +0800 Subject: [PATCH 062/231] refactor(lib): clean network related code --- .../src/Abstraction/Interface/IConnection.cs | 2 +- .../src/Network/Http/Server/HttpConnection.cs | 1 + .../Http/Server/HttpConnectionManager.cs | 8 +--- .../Core/src/Network/RemoteObject.cs | 2 + .../src/Network/Tcp/Client/UniSpyTcpClient.cs | 47 ------------------- .../src/Network/Tcp/Server/TcpConnection.cs | 11 +++-- .../Tcp/Server/TcpConnectionManager.cs | 13 ++--- .../Udp/Server/UdpConnectionManager.cs | 9 ++-- 8 files changed, 21 insertions(+), 72 deletions(-) delete mode 100644 src/Libraries/Core/src/Network/Tcp/Client/UniSpyTcpClient.cs diff --git a/src/Libraries/Core/src/Abstraction/Interface/IConnection.cs b/src/Libraries/Core/src/Abstraction/Interface/IConnection.cs index 03460ddbd..e5c8b2fa8 100644 --- a/src/Libraries/Core/src/Abstraction/Interface/IConnection.cs +++ b/src/Libraries/Core/src/Abstraction/Interface/IConnection.cs @@ -36,7 +36,7 @@ public interface IUdpConnection : IConnection /// /// Represent a tcp connection /// - public interface ITcpConnection : IConnection + public interface ITcpConnection : IConnection, IDisposable { event OnConnectedEventHandler OnConnect; event OnDisconnectedEventHandler OnDisconnect; diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs b/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs index 69ff959f0..cb56307a3 100644 --- a/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs +++ b/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs @@ -1,3 +1,4 @@ +using System; using System.Net; using System.Text; using UniSpy.Server.Core.Abstraction.Interface; diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs b/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs index 22844d1da..63f0659e8 100644 --- a/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs +++ b/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs @@ -7,7 +7,7 @@ namespace UniSpy.Server.Core.Network.Http.Server; -public class HttpConnectionManager : IConnectionManager, IDisposable +public class HttpConnectionManager : IConnectionManager { public event OnConnectingEventHandler OnInitialization; public HttpListener Listener { get; private set; } @@ -29,7 +29,7 @@ public void Start() var raw = context.Request; var request = new HttpRequest(raw); var conn = new HttpConnection(context, this); - OnConnecting(conn); + OnInitialization(conn); conn.OnReceived(request); } catch (Exception ex) @@ -39,10 +39,6 @@ public void Start() } }); } - public void OnConnecting(IHttpConnection connection) - { - OnInitialization((IConnection)connection); - } public void Dispose() { diff --git a/src/Libraries/Core/src/Network/RemoteObject.cs b/src/Libraries/Core/src/Network/RemoteObject.cs index 98656820f..e73b3dd07 100644 --- a/src/Libraries/Core/src/Network/RemoteObject.cs +++ b/src/Libraries/Core/src/Network/RemoteObject.cs @@ -81,5 +81,7 @@ public RemoteTcpConnection(ITcpConnection conn, IConnectionManager manager) public void Disconnect() => LogWriter.LogDebug("Remote tcp connection do not have this method."); public void Send(string response) => LogWriter.LogDebug("Remote tcp connection do not have this method."); public void Send(byte[] response) => LogWriter.LogDebug("Remote tcp connection do not have this method."); + + public void Dispose() { } } } \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/Tcp/Client/UniSpyTcpClient.cs b/src/Libraries/Core/src/Network/Tcp/Client/UniSpyTcpClient.cs deleted file mode 100644 index ea9695994..000000000 --- a/src/Libraries/Core/src/Network/Tcp/Client/UniSpyTcpClient.cs +++ /dev/null @@ -1,47 +0,0 @@ -// using System; -// using System.Net; -// using UniSpy.Server.Core.Encryption; -// using UniSpy.Server.Core.Logging; -// using TcpClient = NetCoreServer.TcpClient; - -// namespace UniSpy.Server.Core.Network.Tcp.Client -// { - -// public abstract class UniSpyTcpClient : TcpClient -// { -// protected UniSpyTcpClient(IPEndPoint endpoint) : base(endpoint) -// { -// } - -// /// -// /// We automatic connect to remote server address -// /// - -// protected override void OnReceived(byte[] buffer, long offset, long size) -// { -// LogWriter.LogNetworkTraffic("Recv", Endpoint, buffer, size); -// byte[] tempBuffer = new byte[size]; -// Array.Copy(buffer, 0, tempBuffer, 0, size); -// OnReceived(tempBuffer); -// } - -// protected virtual void OnReceived(byte[] buffer) -// { -// OnReceived(UniSpyEncoding.GetString(buffer)); -// } - -// protected virtual void OnReceived(string buffer) { } - -// public override bool SendAsync(byte[] buffer, long offset, long size) -// { -// LogWriter.LogNetworkTraffic("Send", Endpoint, buffer, size); -// return base.SendAsync(buffer, offset, size); -// } - -// public override long Send(byte[] buffer, long offset, long size) -// { -// LogWriter.LogNetworkTraffic("Send", Endpoint, buffer, size); -// return base.Send(buffer, offset, size); -// } -// } -// } diff --git a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs index 6eb9ea300..c2139462f 100644 --- a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs +++ b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs @@ -10,7 +10,7 @@ namespace UniSpy.Server.Core.Network.Tcp.Server { - public class TcpConnection : ITcpConnection, IDisposable + public class TcpConnection : ITcpConnection { public IConnectionManager Manager { get; private set; } @@ -62,7 +62,7 @@ private void StartReceiving() } var receivedData = buffer.Take(bytesRead).ToArray(); - OnReceived(receivedData); + OnReceive(receivedData); } catch (Exception ex) { @@ -72,8 +72,11 @@ private void StartReceiving() } } } - public void OnDisconnected() => OnDisconnect(); - public void OnReceived(object buffer) => OnReceive(buffer); + public void OnDisconnected() + { + OnDisconnect(); + Client.Close(); + } public void Dispose() => Client.Dispose(); } diff --git a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnectionManager.cs b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnectionManager.cs index 6374a05f4..f6233cae5 100644 --- a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnectionManager.cs +++ b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnectionManager.cs @@ -29,18 +29,15 @@ public void Start() { var client = Listener.AcceptTcpClient(); var conn = new TcpConnection(client, this); - Task.Run(() => OnConnecting(conn)); + Task.Run(() => + { + OnInitialization(conn); + conn.OnConnected(); + }); } }); } - - public void OnConnecting(ITcpConnection connection) - { - OnInitialization((IConnection)connection); - (connection as TcpConnection).OnConnected(); - } - public void Dispose() { Listener.Stop(); diff --git a/src/Libraries/Core/src/Network/Udp/Server/UdpConnectionManager.cs b/src/Libraries/Core/src/Network/Udp/Server/UdpConnectionManager.cs index 4587c64b7..215a7456d 100644 --- a/src/Libraries/Core/src/Network/Udp/Server/UdpConnectionManager.cs +++ b/src/Libraries/Core/src/Network/Udp/Server/UdpConnectionManager.cs @@ -22,18 +22,15 @@ public void Start() { while (true) { - var clientEndPoint = new IPEndPoint(IPAddress.Any, (Listener.Client.RemoteEndPoint as IPEndPoint).Port); + var clientEndPoint = new IPEndPoint(IPAddress.Any, 0); var data = Listener.Receive(ref clientEndPoint); var conn = new UdpConnection(clientEndPoint, this); - OnConnecting(conn); + // OnInitialization?.Invoke(conn); Task.Run(() => conn.OnReceived(data)); } }); } - public void OnConnecting(IUdpConnection connection) - { - OnInitialization?.Invoke(connection); - } + public void Dispose() { From 8887cf1e4b8bd9546f2d47dd216fd874bfa19a3e Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 3 Nov 2023 10:45:33 +0800 Subject: [PATCH 063/231] fix(lib): network error --- .../src/Network/Http/Server/HttpConnection.cs | 4 ++++ .../Http/Server/HttpConnectionManager.cs | 24 +++++++++---------- .../src/Network/Tcp/Server/TcpConnection.cs | 2 +- .../Tcp/Server/TcpConnectionManager.cs | 3 --- .../src/Network/Udp/Server/UdpConnection.cs | 1 + .../Udp/Server/UdpConnectionManager.cs | 17 ++++++++++--- src/Servers/WebServer/test/Auth/RSATest.cs | 20 ++++++++++++++++ 7 files changed, 51 insertions(+), 20 deletions(-) create mode 100644 src/Servers/WebServer/test/Auth/RSATest.cs diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs b/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs index cb56307a3..6cb69c90f 100644 --- a/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs +++ b/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs @@ -23,6 +23,10 @@ public HttpConnection(HttpListenerContext context, IConnectionManager manager) public void OnReceived(IHttpRequest request) { OnReceive(request); + if (Context.Request.Headers["Connection"] == "close") + { + Context.Response.Close(); + } } public void Send(string response) diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs b/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs index 63f0659e8..738423d01 100644 --- a/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs +++ b/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Concurrent; using System.Net; using System.Threading.Tasks; using UniSpy.Server.Core.Abstraction.Interface; @@ -23,19 +24,16 @@ public void Start() { while (true) { - try - { - var context = Listener.GetContext(); - var raw = context.Request; - var request = new HttpRequest(raw); - var conn = new HttpConnection(context, this); - OnInitialization(conn); - conn.OnReceived(request); - } - catch (Exception ex) - { - LogWriter.LogError(ex); - } + var context = Listener.GetContext(); + Task.Run(() => + { + + var conn = new HttpConnection(context, this); + OnInitialization(conn); + var raw = context.Request; + var request = new HttpRequest(raw); + conn.OnReceived(request); + }); } }); } diff --git a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs index c2139462f..76a741e2d 100644 --- a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs +++ b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs @@ -31,8 +31,8 @@ public TcpConnection(TcpClient client, IConnectionManager manager) } public void Disconnect() { - OnDisconnected(); Client.Client.Disconnect(true); + OnDisconnected(); } public void Send(string response) => Send(UniSpyEncoding.GetBytes(response)); diff --git a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnectionManager.cs b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnectionManager.cs index f6233cae5..63c032003 100644 --- a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnectionManager.cs +++ b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnectionManager.cs @@ -12,14 +12,11 @@ public class TcpConnectionManager : IConnectionManager, IDisposable { public event OnConnectingEventHandler OnInitialization; public TcpListener Listener; - public ConcurrentDictionary ConnectionPool = new(); - public TcpConnectionManager(IPEndPoint endPoint) { Listener = new TcpListener(endPoint); } - public void Start() { Listener.Start(); diff --git a/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs b/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs index 75c863e50..59b924988 100644 --- a/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs +++ b/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs @@ -14,6 +14,7 @@ public class UdpConnection : IUdpConnection public NetworkConnectionType ConnectionType { get; } = NetworkConnectionType.Udp; public event OnReceivedEventHandler OnReceive; + public UdpConnection(IPEndPoint endPoint, IConnectionManager manager) { RemoteIPEndPoint = endPoint; diff --git a/src/Libraries/Core/src/Network/Udp/Server/UdpConnectionManager.cs b/src/Libraries/Core/src/Network/Udp/Server/UdpConnectionManager.cs index 215a7456d..aaa23b246 100644 --- a/src/Libraries/Core/src/Network/Udp/Server/UdpConnectionManager.cs +++ b/src/Libraries/Core/src/Network/Udp/Server/UdpConnectionManager.cs @@ -1,3 +1,4 @@ +using System.Collections.Concurrent; using System; using System.Net; using System.Net.Sockets; @@ -11,6 +12,7 @@ public class UdpConnectionManager : IConnectionManager, IDisposable { public event OnConnectingEventHandler OnInitialization; public UdpClient Listener { get; private set; } + public ConcurrentDictionary Pool = new(); public UdpConnectionManager(IPEndPoint endPoint) { Listener = new UdpClient(endPoint); @@ -24,9 +26,18 @@ public void Start() { var clientEndPoint = new IPEndPoint(IPAddress.Any, 0); var data = Listener.Receive(ref clientEndPoint); - var conn = new UdpConnection(clientEndPoint, this); - // OnInitialization?.Invoke(conn); - Task.Run(() => conn.OnReceived(data)); + + Task.Run(() => + { + UdpConnection conn; + if (!Pool.TryGetValue(clientEndPoint, out conn)) + { + conn = new UdpConnection(clientEndPoint, this); + Pool.TryAdd(clientEndPoint, conn); + } + OnInitialization(conn); + conn.OnReceived(data); + }); } }); } diff --git a/src/Servers/WebServer/test/Auth/RSATest.cs b/src/Servers/WebServer/test/Auth/RSATest.cs new file mode 100644 index 000000000..ce5b54aa0 --- /dev/null +++ b/src/Servers/WebServer/test/Auth/RSATest.cs @@ -0,0 +1,20 @@ +using System.Security.Cryptography; +using Xunit; + +namespace UniSpy.Server.WebServer.Test.Auth +{ + public class RSATest + { + [Fact] + public void KeyGen() + { + using (var rsa = new RSACryptoServiceProvider(1024)) + { + var privateKey = rsa.ExportParameters(true); + + // The public key is used for verifying the signature + var publicKey = rsa.ExportParameters(false); + } + } + } +} \ No newline at end of file From d7eb831a0b87266bdd7bafd4b6e22cfb01eeb46d Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 3 Nov 2023 22:01:35 +0800 Subject: [PATCH 064/231] fix(lib): network exception --- .../Network/Http/Server/HttpConnectionManager.cs | 16 +++++++++++++++- .../Core/src/Network/Tcp/Server/TcpConnection.cs | 10 +++++----- 2 files changed, 20 insertions(+), 6 deletions(-) diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs b/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs index 738423d01..d98b900ba 100644 --- a/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs +++ b/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs @@ -15,7 +15,21 @@ public class HttpConnectionManager : IConnectionManager public HttpConnectionManager(IPEndPoint endPoint) { Listener = new HttpListener(); - Listener.Prefixes.Add($"http://localhost:{endPoint.Port}/"); + string host; + if (endPoint.Address.ToString() == "0.0.0.0") + { + host = "+"; + } + else if (endPoint.Address.ToString() == "127.0.0.1") + { + host = "localhost"; + } + else + { + host = endPoint.Address.ToString(); + } + + Listener.Prefixes.Add($"http://{host}:{endPoint.Port}/"); } public void Start() { diff --git a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs index 76a741e2d..fb0f0172a 100644 --- a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs +++ b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs @@ -1,4 +1,3 @@ -using System; using System.Linq; using System.Net; using System.Net.Sockets; @@ -47,13 +46,14 @@ public void OnConnected() private void StartReceiving() { - var stream = Client.GetStream(); - byte[] buffer = new byte[2048]; - int bytesRead; + while (true) { try { + var stream = Client.GetStream(); + byte[] buffer = new byte[2048]; + int bytesRead; bytesRead = stream.Read(buffer, 0, buffer.Length); if (bytesRead == 0) { @@ -64,7 +64,7 @@ private void StartReceiving() var receivedData = buffer.Take(bytesRead).ToArray(); OnReceive(receivedData); } - catch (Exception ex) + catch (SocketException ex) { LogWriter.LogError(ex); OnDisconnected(); From 1c37b78eee2027ea14cb53fa220c43aff5665389 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 24 Nov 2023 20:32:39 +0800 Subject: [PATCH 065/231] fix(web): wrong d2g response --- .../Core/src/Config/ConfigManager.cs | 10 +++++++- .../src/Network/Http/Server/HttpConnection.cs | 4 ++- .../src/Network/Tcp/Server/TcpConnection.cs | 16 +++++++++--- .../src/Abstraction/SoapEnvelopBase.cs | 10 +------- .../WebServer/src/Application/ClientInfo.cs | 13 +++++++--- .../Auth/Abstraction/LoginResponseBase.cs | 9 ++++--- .../Auth/Handler/LoginUniqueNickHandler.cs | 2 +- .../LoginUniqueNickWithGameIdHandler.cs | 2 +- .../Contract/Direct2GameSoapEnvelope.cs | 4 +-- .../Response/GetPurchaseHistoryResponse.cs | 4 +-- .../Response/GetStoreAvailabilityResponse.cs | 4 +-- src/Servers/WebServer/test/Auth/RSATest.cs | 25 +++++++++++++++++-- 12 files changed, 71 insertions(+), 32 deletions(-) diff --git a/src/Libraries/Core/src/Config/ConfigManager.cs b/src/Libraries/Core/src/Config/ConfigManager.cs index e6368aac1..9d74dd281 100755 --- a/src/Libraries/Core/src/Config/ConfigManager.cs +++ b/src/Libraries/Core/src/Config/ConfigManager.cs @@ -1,3 +1,4 @@ +using System; using Newtonsoft.Json; using System.IO; @@ -7,9 +8,16 @@ public class ConfigManager { public static bool IsConfigFileExist => File.Exists(ConfigPath); public static UniSpyConfig Config = LoadConfigFile(); - public const string ConfigPath = @"UniSpyServerConfig.json"; + public const string ConfigPath = "UniSpyServerConfig.json"; + public const string EnvVarName = "UNISPYCONFIG"; private static UniSpyConfig LoadConfigFile() { + var config = Environment.GetEnvironmentVariable(EnvVarName); + if (config is not null && config !="") + { + return JsonConvert.DeserializeObject(config); + } + if (!IsConfigFileExist) { throw new UniSpy.Exception("UniSpy server config file not found"); diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs b/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs index 6cb69c90f..95fa48284 100644 --- a/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs +++ b/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs @@ -37,7 +37,9 @@ public void Send(string response) public void Send(byte[] response) { Context.Response.StatusCode = (int)HttpStatusCode.OK; - Context.Response.ContentType = "application/xml?"; + Context.Response.ContentType = "application/xml"; + Context.Response.SendChunked = false; + Context.Response.ContentLength64 = response.Length; Context.Response.OutputStream.Write(response); if (Context.Request.Headers["Connection"] == "close") { diff --git a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs index fb0f0172a..34f3fe8f8 100644 --- a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs +++ b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs @@ -46,14 +46,22 @@ public void OnConnected() private void StartReceiving() { - + if (Client.Connected == false) + { + OnDisconnected(); + return; + } + var stream = Client.GetStream(); + byte[] buffer = new byte[2048]; + int bytesRead; while (true) { try { - var stream = Client.GetStream(); - byte[] buffer = new byte[2048]; - int bytesRead; + if (!stream.CanRead || !stream.CanWrite) + { + break; + } bytesRead = stream.Read(buffer, 0, buffer.Length); if (bytesRead == 0) { diff --git a/src/Servers/WebServer/src/Abstraction/SoapEnvelopBase.cs b/src/Servers/WebServer/src/Abstraction/SoapEnvelopBase.cs index b2edeac4e..2f7a062c8 100644 --- a/src/Servers/WebServer/src/Abstraction/SoapEnvelopBase.cs +++ b/src/Servers/WebServer/src/Abstraction/SoapEnvelopBase.cs @@ -55,20 +55,12 @@ public virtual void Add(string name, object value) { CurrentElement.Add(new XElement(_bodyNamespace + name, value)); } - // public virtual void Add(string name, List values) - // { - // throw new NotImplementedException(); - // } - // public virtual void Add(string name, List values) - // { - // throw new NotImplementedException(); - // } public override string ToString() { using (var writer = new MyStringWriter()) { - Content.Save(writer); + Content.Save(writer, SaveOptions.DisableFormatting); return writer.ToString(); } } diff --git a/src/Servers/WebServer/src/Application/ClientInfo.cs b/src/Servers/WebServer/src/Application/ClientInfo.cs index cc34657e0..3d0fe8c81 100644 --- a/src/Servers/WebServer/src/Application/ClientInfo.cs +++ b/src/Servers/WebServer/src/Application/ClientInfo.cs @@ -1,5 +1,6 @@ using System; using UniSpy.Server.Core.Abstraction.BaseClass; +using UniSpy.Server.Core.Extension; namespace UniSpy.Server.WebServer.Application { @@ -13,24 +14,28 @@ namespace UniSpy.Server.WebServer.Application /// public sealed class ClientInfo : ClientInfoBase { - public const string PeerKeyExponent = "000001"; + public const string PeerKeyExponent = "010001"; public const string PeerKeyModulus = "aefb5064bbd1eb632fa8d57aab1c49366ce0ee3161cbef19f2b7971b63b811790ecbf6a47b34c55f65a0766b40c261c5d69c394cd320842dd2bccba883d30eae8fdba5d03b21b09bfc600dcb30b1b2f3fbe8077630b006dcb54c4254f14891762f72e7bbfe743eb8baf65f9e8c8d11ebe46f6b59e986b4c394cfbc2c8606e29f"; /// /// The exponent generated for user /// exponent is 000001 /// - public static readonly byte PeerKeyExponentByte = 1; + // public static byte[] PeerKeyExponentByte => BitConverter.GetBytes(int.Parse(PeerKeyExponent)); /// /// The modulus generated for user aefb5064bbd1eb632fa8d57aab1c49366ce0ee3161cbef19f2b7971b63b811790ecbf6a47b34c55f65a0766b40c261c5d69c394cd320842dd2bccba883d30eae8fdba5d03b21b09bfc600dcb30b1b2f3fbe8077630b006dcb54c4254f14891762f72e7bbfe743eb8baf65f9e8c8d11ebe46f6b59e986b4c394cfbc2c8606e29f /// - public static readonly byte[] PeerKeyModulusBytes = { 0xAE, 0xFB, 0x50, 0x64, 0xBB, 0xD1, 0xEB, 0x63, 0x2F, 0xA8, 0xD5, 0x7A, 0xAB, 0x1C, 0x49, 0x36, 0x6C, 0xE0, 0xEE, 0x31, 0x61, 0xCB, 0xEF, 0x19, 0xF2, 0xB7, 0x97, 0x1B, 0x63, 0xB8, 0x11, 0x79, 0x0E, 0xCB, 0xF6, 0xA4, 0x7B, 0x34, 0xC5, 0x5F, 0x65, 0xA0, 0x76, 0x6B, 0x40, 0xC2, 0x61, 0xC5, 0xD6, 0x9C, 0x39, 0x4C, 0xD3, 0x20, 0x84, 0x2D, 0xD2, 0xBC, 0xCB, 0xA8, 0x83, 0xD3, 0x0E, 0xAE, 0x8F, 0xDB, 0xA5, 0xD0, 0x3B, 0x21, 0xB0, 0x9B, 0xFC, 0x60, 0x0D, 0xCB, 0x30, 0xB1, 0xB2, 0xF3, 0xFB, 0xE8, 0x07, 0x76, 0x30, 0xB0, 0x06, 0xDC, 0xB5, 0x4C, 0x42, 0x54, 0xF1, 0x48, 0x91, 0x76, 0x2F, 0x72, 0xE7, 0xBB, 0xFE, 0x74, 0x3E, 0xB8, 0xBA, 0xF6, 0x5F, 0x9E, 0x8C, 0x8D, 0x11, 0xEB, 0xE4, 0x6F, 0x6B, 0x59, 0xE9, 0x86, 0xB4, 0xC3, 0x94, 0xCF, 0xBC, 0x2C, 0x86, 0x06, 0xE2, 0x9F }; + // public static byte[] PeerKeyModulusBytes => PeerKeyModulus.FromHexStringToBytes(); /// /// should be 256 characters /// public const string ServerData = - "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + "95980bf5011ce73f2866b995a272420c36f1e8b4ac946f0b5bfe87c9fef0811036da00cfa85e77e00af11c924d425ec06b1dd052feab1250376155272904cbf9da831b0ce3d52964424c0a426b869e2c0ad11ffa3e70496e27ea250adb707a96b3496bff190eafc0b6b9c99db75b02c2a822bb1b5b3d954e7b2c0f9b1487e3e1"; public static int ExpireTime => (int)((DateTimeOffset)DateTime.UtcNow + TimeSpan.FromMinutes(5)).ToUnixTimeSeconds(); public const string SignaturePreFix = "0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003020300C06082A864886F70D020505000410"; + + public readonly byte[] PrivateKeyD = new byte[128] { 0x96, 0xE2, 0xF4, 0xF9, 0x7C, 0x78, 0x81, 0x0C, 0x13, 0xE2, 0xA4, 0xCA, 0xC1, 0x12, 0x44, 0xBB, 0x5B, 0x37, 0xC5, 0x4E, 0x98, 0x20, 0xF1, 0x3F, 0xE9, 0xB5, 0x53, 0xDA, 0x10, 0xB1, 0xE8, 0xEF, 0x8E, 0xB0, 0x3F, 0x87, 0x68, 0x1B, 0x0E, 0x62, 0x4D, 0x1A, 0x8D, 0xE9, 0x17, 0x4C, 0xBE, 0xE5, 0xB8, 0xED, 0x92, 0xE4, 0xBE, 0x74, 0xF8, 0x6C, 0x30, 0x38, 0xCD, 0x7C, 0x1A, 0x20, 0xB9, 0xA3, 0xDB, 0x1D, 0x49, 0x22, 0x62, 0x87, 0x38, 0x68, 0xFB, 0xA0, 0x8E, 0x1E, 0xAB, 0x5C, 0xBA, 0x86, 0x3F, 0x8F, 0xDB, 0xF4, 0x5E, 0xEA, 0x61, 0x4B, 0xBF, 0x6C, 0xFC, 0x47, 0x00, 0x81, 0x44, 0x2A, 0x97, 0x78, 0x7E, 0xB6, 0xEC, 0xA7, 0x1C, 0x48, 0x96, 0x81, 0x6C, 0x2A, 0x62, 0x72, 0x4C, 0x0E, 0x8C, 0xAA, 0xEE, 0xAB, 0x72, 0x78, 0xC2, 0x55, 0x4A, 0x13, 0x80, 0x94, 0x6E, 0xED, 0x21, 0x29 }; + public readonly byte[] Modulus = new byte[128] { 0xA9, 0x3E, 0x00, 0x19, 0x2C, 0x4A, 0x98, 0x69, 0xD7, 0x41, 0x9A, 0xFF, 0x66, 0x2E, 0xCA, 0xD6, 0xC8, 0xB9, 0x99, 0x09, 0xFD, 0xD0, 0xE7, 0xF8, 0xCA, 0xDD, 0x15, 0x32, 0xE8, 0xE3, 0x59, 0x37, 0x40, 0x83, 0xDA, 0xB8, 0xBE, 0x71, 0x7F, 0x60, 0x91, 0x60, 0xCD, 0x6A, 0x54, 0x11, 0xBE, 0xD7, 0x92, 0x7A, 0xD3, 0xB5, 0xC0, 0x0C, 0x4C, 0x4B, 0x34, 0x76, 0x71, 0xF2, 0x3F, 0xE0, 0x1E, 0xBB, 0x2F, 0x83, 0x4B, 0xD8, 0xCA, 0x27, 0xC3, 0x55, 0x3E, 0x1E, 0x6B, 0xC2, 0x85, 0xAF, 0xC6, 0x3E, 0xC0, 0xE1, 0x1F, 0x59, 0xCA, 0xF6, 0xAC, 0x37, 0x5F, 0x4B, 0x0E, 0xB8, 0x2A, 0x4D, 0xA2, 0x2C, 0x0C, 0x10, 0x1D, 0x09, 0x13, 0x1A, 0x7C, 0x42, 0x0D, 0x4C, 0xC4, 0xD0, 0x95, 0x62, 0x4C, 0x42, 0xBC, 0xD8, 0xD7, 0x19, 0x16, 0xC8, 0xDC, 0x00, 0x48, 0x08, 0x6D, 0x74, 0x6A, 0x31, 0x55, 0xC7 }; + public readonly byte[] Exponent = new byte[3] { 0x01, 0x00, 0x01 }; public ClientInfo() { } diff --git a/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResponseBase.cs b/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResponseBase.cs index 30f4df63e..c6bcd2a4b 100644 --- a/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResponseBase.cs +++ b/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResponseBase.cs @@ -19,7 +19,7 @@ protected LoginResponseBase(RequestBase request, ResultBase result) : base(reque protected void BuildContext() { - _content.Add("responseCode", _result.ResponseCode); + _content.Add("responseCode", "h"); _content.Add("certificate"); _content.Add("length", _result.Length); _content.Add("version", _request.Version); @@ -49,9 +49,12 @@ protected void BuildContext() dataToHash.AddRange(Encoding.ASCII.GetBytes(_result.UniqueNick)); dataToHash.AddRange(Encoding.ASCII.GetBytes(_result.CdKeyHash)); - dataToHash.AddRange(ClientInfo.PeerKeyModulusBytes); - dataToHash.Add(ClientInfo.PeerKeyExponentByte); + dataToHash.AddRange(ClientInfo.PeerKeyModulus.FromHexStringToBytes()); + dataToHash.AddRange(ClientInfo.PeerKeyExponent.FromHexStringToBytes()); + // dataToHash.AddRange(new byte[] { 0x01, 0x00, 0x01 }); + // var exp = System.Numerics.BigInteger.Parse(ClientInfo.PeerKeyExponent,System.Globalization.NumberStyles.HexNumber); + // exp.ToByteArray() // server data should be convert to bytes[128] then added to list dataToHash.AddRange(ClientInfo.ServerData.FromHexStringToBytes()); var hash = md5.ComputeHash(dataToHash.ToArray()); diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickHandler.cs index 08534ba45..1d9e2dcd0 100644 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickHandler.cs +++ b/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickHandler.cs @@ -37,7 +37,7 @@ join sp in db.Subprofiles on p.Profileid equals sp.Profileid _result.UserId = data.u.Userid; _result.ProfileId = data.p.Profileid; // _result.CdKeyHash = data.sp.Cdkeyenc; - _result.CdKeyHash = "xxxxxxxxxxx"; + _result.CdKeyHash = "D41D8CD98F00B204E9800998ECF8427E"; // currently we set this to uniquenick _result.ProfileNick = data.p.Nick; _result.UniqueNick = data.sp.Uniquenick; diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs index 5b7073178..ef50addd3 100644 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs +++ b/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs @@ -31,7 +31,7 @@ join sp in db.Subprofiles on p.Profileid equals sp.Profileid _result.UserId = data.u.Userid; _result.ProfileId = data.p.Profileid; // _result.CdKeyHash = data.sp.Cdkeyenc; - _result.CdKeyHash = "xxxxxxxxxxx"; + _result.CdKeyHash = "D41D8CD98F00B204E9800998ECF8427E"; // currently we set this to uniquenick _result.ProfileNick = data.p.Nick; _result.UniqueNick = data.sp.Uniquenick; diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Direct2GameSoapEnvelope.cs b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Direct2GameSoapEnvelope.cs index 2b9a7e923..dfc4bceea 100644 --- a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Direct2GameSoapEnvelope.cs +++ b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Direct2GameSoapEnvelope.cs @@ -5,8 +5,8 @@ namespace UniSpy.Server.WebServer.Module.Direct2Game.Contract { public sealed class Direct2GameSoapEnvelope : SoapEnvelopBase { - public static XNamespace Direct2GameNamespace = "http://gamespy.net/commerce/2009/02"; - public Direct2GameSoapEnvelope() : base("gsc", Direct2GameNamespace) + public static XNamespace Direct2GameNamespace = "http://gamespy.net/commerce/"; + public Direct2GameSoapEnvelope() : base("ns1", Direct2GameNamespace) { } } diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetPurchaseHistoryResponse.cs b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetPurchaseHistoryResponse.cs index 965a1114b..2cfa7a5a3 100644 --- a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetPurchaseHistoryResponse.cs +++ b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetPurchaseHistoryResponse.cs @@ -14,14 +14,14 @@ public GetPurchaseHistoryResponse(RequestBase request, ResultBase result) : base public override void Build() { + _content.Add("GetPurchaseHistoryResponse"); _content.Add("GetPurchaseHistoryResult"); _content.Add("status"); _content.Add("code", _result.Code); + _content.ChangeToElement("GetPurchaseHistoryResult"); _content.Add("orderpurchases"); // we do not know the purchace content for each game, so currently we just make it 0 _content.Add("count", 0); - // _content.ChangeToElement("GetPurchaseHistoryResult"); - // _content.BackToParentElement(); base.Build(); } } diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetStoreAvailabilityResponse.cs b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetStoreAvailabilityResponse.cs index f3a5a5f40..d9864c830 100644 --- a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetStoreAvailabilityResponse.cs +++ b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetStoreAvailabilityResponse.cs @@ -13,12 +13,12 @@ public GetStoreAvailabilityResponse(RequestBase request, ResultBase result) : ba public override void Build() { + _content.Add("GetStoreAvailabilityResponse"); _content.Add("GetStoreAvailabilityResult"); _content.Add("status"); _content.Add("code", _result.Code); - _content.ChangeToElement("status"); - _content.Add("storestatusid", _result.StoreStatusId); _content.ChangeToElement("GetStoreAvailabilityResult"); + _content.Add("storestatusid", (int)_result.StoreStatusId); base.Build(); } } diff --git a/src/Servers/WebServer/test/Auth/RSATest.cs b/src/Servers/WebServer/test/Auth/RSATest.cs index ce5b54aa0..0d1f74e45 100644 --- a/src/Servers/WebServer/test/Auth/RSATest.cs +++ b/src/Servers/WebServer/test/Auth/RSATest.cs @@ -1,4 +1,7 @@ +using System; using System.Security.Cryptography; +using UniSpy.Server.Core.Extension; +using UniSpy.Server.Core.Logging; using Xunit; namespace UniSpy.Server.WebServer.Test.Auth @@ -11,9 +14,27 @@ public void KeyGen() using (var rsa = new RSACryptoServiceProvider(1024)) { var privateKey = rsa.ExportParameters(true); - - // The public key is used for verifying the signature var publicKey = rsa.ExportParameters(false); + var d = privateKey.D; + var modulus = privateKey.Modulus; + var exponent = privateKey.Exponent; + + Console.WriteLine(StringExtensions.ConvertByteToHexString(d)); + Console.WriteLine(StringExtensions.ConvertByteToHexString(modulus)); + Console.WriteLine(StringExtensions.ConvertByteToHexString(exponent)); + + + var newPublicParam = new RSAParameters + { + Modulus = modulus, + Exponent = exponent + }; + var newPrivateParam = new RSAParameters + { + Modulus = modulus, + Exponent = exponent, + D = d, + }; } } } From f2111d9da848f70979f26797d79025422087c80e Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 14 Jun 2024 22:33:45 +0800 Subject: [PATCH 066/231] (refactor): create python dev version of unispy --- .DS_Store | Bin 0 -> 6148 bytes .devcontainer/Dockerfile | 2 - .devcontainer/devcontainer.json | 27 - .dockerignore | 25 - .gitattributes | 1 - .github/ISSUE_TEMPLATE/bug_report.md | 46 - .github/ISSUE_TEMPLATE/custom.md | 33 - .github/labeler.yml | 24 - .github/workflows/cd-cdkey.yml | 35 - .github/workflows/cd-chat.yml | 35 - .github/workflows/cd-gs.yml | 35 - .github/workflows/cd-gtr.yml | 35 - .github/workflows/cd-nn.yml | 35 - .github/workflows/cd-pcm.yml | 35 - .github/workflows/cd-psp.yml | 35 - .github/workflows/cd-qr.yml | 35 - .github/workflows/cd-sb.yml | 35 - .github/workflows/cd-ws.yml | 35 - .github/workflows/ci.yml | 27 - .github/workflows/labeler.yml | 12 - .gitignore | 535 +- .vscode/launch.json | 222 - .vscode/tasks.json | 155 - BuildUniSpyServer.bat | 4 - BuildUniSpyServer.sh | 6 - UniSpy.Server.sln | 393 -- .../GenerateDatabaseModelLinux.sh | 10 - .../GenerateDatabaseModelWindows.bat | 5 - common/EFScript.txt | 2 - common/Icon/UniSpy_Logo.ico | Bin 51196 -> 0 bytes common/UniSpy.sql | 4798 -------------- common/UniSpyServerConfig.json | 91 - common/UniSpy_pg.sql | 5521 ----------------- common/docker-compose.build.yml | 104 - config.json | 97 + src/.DS_Store | Bin 0 -> 6148 bytes src/.devcontainer/Dockerfile | 12 + src/.devcontainer/devcontainer.json | 11 + src/.vscode/launch.json | 26 + .../src/Abstraction/BaseClass/ClientBase.cs | 173 - .../Abstraction/BaseClass/ClientInfoBase.cs | 9 - .../BaseClass/ClientManagerBase.cs | 40 - .../Abstraction/BaseClass/CmdHandlerBase.cs | 75 - .../Abstraction/BaseClass/CmdSwitcherBase.cs | 73 - .../src/Abstraction/BaseClass/EncParamBase.cs | 8 - .../src/Abstraction/BaseClass/RedisChannel.cs | 69 - .../src/Abstraction/BaseClass/RedisClient.cs | 11 - .../BaseClass/RedisKeyValueObject.cs | 16 - .../src/Abstraction/BaseClass/RequestBase.cs | 20 - .../src/Abstraction/BaseClass/ResponseBase.cs | 37 - .../src/Abstraction/BaseClass/ResultBase.cs | 9 - .../src/Abstraction/BaseClass/ServerBase.cs | 63 - .../BaseClass/ServerLauncherBase.cs | 83 - .../Core/src/Abstraction/Interface/IClient.cs | 23 - .../src/Abstraction/Interface/IConnection.cs | 54 - .../src/Abstraction/Interface/IEncryption.cs | 8 - .../src/Abstraction/Interface/IHandler.cs | 15 - .../src/Abstraction/Interface/IHttpRequest.cs | 11 - .../Core/src/Abstraction/Interface/ILogger.cs | 21 - .../Interface/IRedisChannelEvent.cs | 9 - .../src/Abstraction/Interface/IRequest.cs | 10 - .../src/Abstraction/Interface/IResponse.cs | 8 - .../Core/src/Abstraction/Interface/IServer.cs | 20 - .../src/Abstraction/Interface/ISwicher.cs | 7 - .../Core/src/Config/ConfigManager.cs | 31 - src/Libraries/Core/src/Config/UniSpyConfig.cs | 66 - .../src/Database/DatabaseModel/Addrequest.cs | 17 - .../src/Database/DatabaseModel/Blocked.cs | 15 - .../Core/src/Database/DatabaseModel/Friend.cs | 15 - .../Core/src/Database/DatabaseModel/Game.cs | 23 - .../src/Database/DatabaseModel/Grouplist.cs | 14 - .../src/Database/DatabaseModel/Message.cs | 18 - .../src/Database/DatabaseModel/Partner.cs | 11 - .../src/Database/DatabaseModel/Profile.cs | 72 - .../src/Database/DatabaseModel/Pstorage.cs | 22 - .../src/Database/DatabaseModel/Sakestorage.cs | 20 - .../src/Database/DatabaseModel/Subprofile.cs | 22 - .../Database/DatabaseModel/UnispyContext.cs | 532 -- .../Core/src/Database/DatabaseModel/User.cs | 29 - .../Core/src/Database/DatabaseType.cs | 7 - src/Libraries/Core/src/Encryption/Crc16.cs | 90 - .../Core/src/Encryption/GSEncryption.cs | 123 - .../Core/src/Encryption/UniSpyEncoding.cs | 12 - .../Core/src/Encryption/XorEncoding.cs | 82 - src/Libraries/Core/src/Exception/Exception.cs | 47 - .../src/Extension/DataOperationExtensions.cs | 19 - src/Libraries/Core/src/Extension/EasyTimer.cs | 59 - .../src/Extension/Redis/RedisChannelName.cs | 11 - .../Core/src/Extension/Redis/RedisDbNumber.cs | 13 - .../Core/src/Extension/StringExtensions.cs | 221 - src/Libraries/Core/src/Logging/LogWriter.cs | 97 - src/Libraries/Core/src/Misc/GameSpyRandom.cs | 66 - src/Libraries/Core/src/Misc/GameSpyUtils.cs | 267 - .../Core/src/Misc/PasswordEncoder.cs | 126 - src/Libraries/Core/src/Misc/RequestChecker.cs | 46 - .../Core/src/Misc/UniSpyJsonConverter.cs | 76 - .../src/Network/Http/Server/HttpConnection.cs | 49 - .../Http/Server/HttpConnectionManager.cs | 59 - .../src/Network/Http/Server/HttpRequest.cs | 27 - .../Core/src/Network/NetworkEvents.cs | 9 - .../Core/src/Network/RemoteObject.cs | 87 - .../src/Network/Tcp/Server/TcpConnection.cs | 91 - .../Tcp/Server/TcpConnectionManager.cs | 42 - .../src/Network/Udp/Server/UdpConnection.cs | 55 - .../Udp/Server/UdpConnectionManager.cs | 50 - .../Core/src/UniSpy.Server.Core.csproj | 30 - src/Libraries/LinqToRedis/.gitignore | 391 -- src/Libraries/LinqToRedis/LinqToRedis.sln | 28 - src/Libraries/LinqToRedis/README.md | 44 - .../LinqToRedis/src/Core/IRedisKey.cs | 8 - .../LinqToRedis/src/Core/RedisClient.cs | 171 - .../LinqToRedis/src/Core/RedisKeyAttribute.cs | 15 - .../src/Core/RedisKeyValueObject.cs | 116 - .../LinqToRedis/src/Core/RedisLock.cs | 51 - .../LinqToRedis/src/Linq/QueryEvaluator.cs | 106 - .../LinqToRedis/src/Linq/QueryProviderBase.cs | 16 - .../LinqToRedis/src/Linq/QueryableObject.cs | 50 - .../LinqToRedis/src/Linq/RedisQueryBuilder.cs | 139 - .../src/Linq/RedisQueryProvider.cs | 62 - .../LinqToRedis/src/UniSpy.LinqToRedis.csproj | 23 - .../LinqToRedis/test/LinqToRedisTest.cs | 173 - .../LinqToRedis/test/NatNeg/RedisClient.cs | 10 - .../LinqToRedis/test/NatNeg/UserInfo.cs | 21 - .../test/UniSpy.LinqToRedis.Test.csproj | 24 - .github/README.MD => src/README | 17 +- .../BaseClass/Channel/ChannelHandlerBase.cs | 101 - .../BaseClass/Channel/ChannelRequestBase.cs | 20 - .../BaseClass/Channel/ChannelResponseBase.cs | 8 - .../Abstraction/BaseClass/CmdHandlerBase.cs | 42 - .../BaseClass/General/LogedInHandlerBase.cs | 19 - .../BaseClass/Message/MessageHandlerBase.cs | 85 - .../BaseClass/Message/MessageRequestBase.cs | 34 - .../BaseClass/Message/MessageResultBase.cs | 9 - .../src/Abstraction/BaseClass/RequestBase.cs | 107 - .../src/Abstraction/BaseClass/ResponseBase.cs | 12 - .../src/Abstraction/BaseClass/ResultBase.cs | 7 - .../src/Abstraction/Interface/IChannel.cs | 21 - .../src/Abstraction/Interface/IShareClient.cs | 13 - .../Interface/IStorageOperation.cs | 24 - .../Chat/src/Aggregate/ChannelManager.cs | 11 - src/Servers/Chat/src/Aggregate/ChannelMode.cs | 123 - .../ChannelProperty/ChannelGeneral.cs | 120 - .../ChannelProperty/ChannelManage.cs | 83 - .../ChannelProperty/ChannelModeRelated.cs | 62 - .../ChannelProperty/ChannelUserRelated.cs | 223 - .../ChannelProperty/ChannelValidation.cs | 62 - src/Servers/Chat/src/Aggregate/ChannelUser.cs | 92 - .../Chat/src/Aggregate/KeyValueManager.cs | 68 - .../Chat/src/Aggregate/Misc/ChatConstants.cs | 7 - .../Chat/src/Aggregate/Misc/ChatCrypt.cs | 125 - .../Chat/src/Aggregate/Misc/IRCErrorCode.cs | 22 - .../src/Aggregate/Misc/IRCReplyBuilder.cs | 133 - .../Chat/src/Aggregate/Misc/PeerChatCTX.cs | 9 - .../Chat/src/Aggregate/Misc/ResponseName.cs | 87 - .../Chat/src/Aggregate/Redis/ChannelCache.cs | 25 - .../src/Aggregate/Redis/ClientInfoCache.cs | 19 - .../Aggregate/Redis/Contract/RemoteMessage.cs | 26 - .../Chat/src/Aggregate/Redis/PeerRoom.cs | 69 - .../Chat/src/Aggregate/Redis/RedisChannel.cs | 43 - .../Chat/src/Aggregate/RemoteClient.cs | 50 - src/Servers/Chat/src/Application/Client.cs | 64 - .../Chat/src/Application/ClientInfo.cs | 63 - .../Chat/src/Application/ClientManager.cs | 24 - src/Servers/Chat/src/Application/Program.cs | 26 - src/Servers/Chat/src/Application/Server.cs | 29 - .../Chat/src/Application/ServerLauncher.cs | 18 - .../Chat/src/Application/StorageOperation.cs | 167 - .../Request/Channel/GetCKeyRequest.cs | 60 - .../Request/Channel/GetChannelKeyRequest.cs | 33 - .../Contract/Request/Channel/JoinRequest.cs | 25 - .../Contract/Request/Channel/KickRequest.cs | 34 - .../Contract/Request/Channel/ModeRequest.cs | 219 - .../Contract/Request/Channel/PartRequest.cs | 23 - .../Request/Channel/SetCKeyRequest.cs | 50 - .../Request/Channel/SetChannelKeyRequest.cs | 29 - .../Contract/Request/Channel/TopicRequest.cs | 36 - .../Contract/Request/General/CdKeyRequest.cs | 24 - .../Contract/Request/General/CryptRequest.cs | 26 - .../Contract/Request/General/GetKeyRequest.cs | 49 - .../Request/General/GetUdpRelayRequest.cs | 11 - .../Contract/Request/General/InviteRequest.cs | 25 - .../Request/General/ListLimitRequest.cs | 31 - .../Contract/Request/General/ListRequest.cs | 28 - .../Request/General/LoginPreAuthRequest.cs | 23 - .../Contract/Request/General/LoginRequest.cs | 55 - .../Contract/Request/General/NamesRequest.cs | 13 - .../Contract/Request/General/NickRequest.cs | 45 - .../Contract/Request/General/PingRequest.cs | 11 - .../Contract/Request/General/PongRequest.cs | 24 - .../Contract/Request/General/QuitRequest.cs | 26 - .../Request/General/RegisterNickRequest.cs | 23 - .../Request/General/SetGroupRequest.cs | 24 - .../Contract/Request/General/SetKeyRequest.cs | 25 - .../Contract/Request/General/UserIPRequest.cs | 17 - .../Contract/Request/General/UserRequest.cs | 35 - .../Contract/Request/General/WhoIsRequest.cs | 25 - .../Contract/Request/General/WhoRequest.cs | 39 - .../Contract/Request/Message/AtmRequest.cs | 11 - .../Contract/Request/Message/NoticeRequest.cs | 11 - .../Request/Message/PrivateRequest.cs | 11 - .../Contract/Request/Message/UtmRequest.cs | 11 - .../Response/Channel/GetCKeyResponse.cs | 26 - .../Response/Channel/GetChannelKeyResponse.cs | 19 - .../Contract/Response/Channel/JoinResponse.cs | 20 - .../Contract/Response/Channel/KickResponse.cs | 18 - .../Contract/Response/Channel/ModeResponse.cs | 29 - .../Response/Channel/NamesResponse.cs | 21 - .../Contract/Response/Channel/PartResponse.cs | 19 - .../Response/Channel/SetCKeyResponse.cs | 19 - .../Response/Channel/SetChannelKeyResponse.cs | 20 - .../Response/Channel/TopicResponse.cs | 26 - .../Response/General/CdKeyResponse.cs | 16 - .../Response/General/CryptResponse.cs | 16 - .../Response/General/GetKeyResponse.cs | 24 - .../Contract/Response/General/ListResponse.cs | 21 - .../Response/General/LoginResponse.cs | 17 - .../Contract/Response/General/NickResponse.cs | 16 - .../Contract/Response/General/PingResponse.cs | 17 - .../Response/General/UserIPResponse.cs | 17 - .../Response/General/WhoIsResponse.cs | 31 - .../Contract/Response/General/WhoResponse.cs | 38 - .../Contract/Response/Message/AtmResponse.cs | 18 - .../Response/Message/NoticeResponse.cs | 18 - .../Response/Message/PrivateResponse.cs | 19 - .../Contract/Response/Message/UtmResponse.cs | 19 - .../Contract/Result/Channel/GetCKeyResult.cs | 20 - .../Result/Channel/GetChannelKeyResult.cs | 12 - .../src/Contract/Result/Channel/JoinResult.cs | 16 - .../src/Contract/Result/Channel/KickResult.cs | 13 - .../src/Contract/Result/Channel/ModeResult.cs | 12 - .../Contract/Result/Channel/NamesResult.cs | 12 - .../src/Contract/Result/Channel/PartResult.cs | 12 - .../Result/Channel/SetChannelKeyResult.cs | 11 - .../Contract/Result/Channel/TopicResult.cs | 12 - .../Contract/Result/General/CryptResult.cs | 9 - .../Contract/Result/General/GetKeyResult.cs | 18 - .../src/Contract/Result/General/ListResult.cs | 22 - .../Contract/Result/General/LoginResult.cs | 11 - .../src/Contract/Result/General/NickResult.cs | 12 - .../src/Contract/Result/General/PingResult.cs | 11 - .../src/Contract/Result/General/QuitResult.cs | 26 - .../Contract/Result/General/UserIPResult.cs | 11 - .../Contract/Result/General/WhoIsResult.cs | 18 - .../src/Contract/Result/General/WhoResult.cs | 22 - .../src/Contract/Result/Message/AtmResult.cs | 9 - .../Contract/Result/Message/NoticeResult.cs | 9 - .../Contract/Result/Message/PrivateResult.cs | 10 - .../src/Contract/Result/Message/UtmResult.cs | 9 - src/Servers/Chat/src/Dockerfile | 21 - .../Chat/src/Enumerate/ChatChannelMode.cs | 13 - src/Servers/Chat/src/Enumerate/ChatMode.cs | 21 - .../Chat/src/Enumerate/Request/ChatRequest.cs | 36 - .../Enumerate/Request/ChatServerMessage.cs | 21 - .../src/Enumerate/Response/ChatErrorCode.cs | 14 - src/Servers/Chat/src/Exception/Exception.cs | 11 - .../IRC/Channel/BadChanMaskException.cs | 13 - .../IRC/Channel/BadChannelKeyException.cs | 13 - .../IRC/Channel/BannedFromChanException.cs | 11 - .../IRC/Channel/ChannelIsFullException.cs | 11 - .../IRC/Channel/InviteOnlyChanException.cs | 13 - .../IRC/Channel/NoSuchChannelException.cs | 13 - .../IRC/General/ErrOneUSNickNameException.cs | 13 - .../IRC/General/LoginFailedException.cs | 13 - .../IRC/General/MoreParametersException.cs | 13 - .../IRC/General/NickNameInUseException.cs | 28 - .../IRC/General/NoSuchNickException.cs | 13 - .../IRC/General/NoUniqueNickException.cs | 13 - .../IRC/General/RegisterNickFaildException.cs | 13 - .../Exception/IRC/General/TooManyChannels.cs | 12 - .../IRC/General/UniqueNickExpiredException.cs | 13 - .../Chat/src/Exception/IRCChannelException.cs | 23 - .../Chat/src/Exception/IRCException.cs | 27 - .../CmdHandler/Channel/GetCKeyHandler.cs | 103 - .../Channel/GetChannelKeyHandler.cs | 39 - .../Handler/CmdHandler/Channel/JoinHandler.cs | 147 - .../Handler/CmdHandler/Channel/KickHandler.cs | 54 - .../Handler/CmdHandler/Channel/ModeHandler.cs | 62 - .../CmdHandler/Channel/NamesHandler.cs | 38 - .../Handler/CmdHandler/Channel/PartHandler.cs | 112 - .../CmdHandler/Channel/SetCKeyHandler.cs | 68 - .../Channel/SetChannelKeyHandler.cs | 43 - .../CmdHandler/Channel/TopicHandler.cs | 53 - .../CmdHandler/General/CdKeyHandler.cs | 20 - .../CmdHandler/General/CryptHandler.cs | 55 - .../CmdHandler/General/GetKeyHandler.cs | 47 - .../CmdHandler/General/GetUdpRelayHandler.cs | 17 - .../CmdHandler/General/InviteHandler.cs | 25 - .../Handler/CmdHandler/General/ListHandler.cs | 43 - .../CmdHandler/General/LoginHandler.cs | 53 - .../Handler/CmdHandler/General/NickHandler.cs | 87 - .../Handler/CmdHandler/General/PingHandler.cs | 25 - .../Handler/CmdHandler/General/QuitHandler.cs | 46 - .../CmdHandler/General/SetKeyHandler.cs | 29 - .../Handler/CmdHandler/General/UserHandler.cs | 20 - .../CmdHandler/General/UserIPHandler.cs | 28 - .../Handler/CmdHandler/General/WhoHandler.cs | 92 - .../CmdHandler/General/WhoIsHandler.cs | 45 - .../Handler/CmdHandler/Message/AtmHandler.cs | 25 - .../CmdHandler/Message/NoticeHandler.cs | 27 - .../CmdHandler/Message/PrivateHandler.cs | 47 - .../Handler/CmdHandler/Message/UtmHandler.cs | 27 - src/Servers/Chat/src/Handler/CmdSwitcher.cs | 107 - .../Chat/src/UniSpy.Server.Chat.csproj | 22 - .../Chat/test/Channel/ChannelHandlerTest.cs | 84 - .../Chat/test/Channel/ChannelRequestTest.cs | 131 - .../Chat/test/Channel/ChannelRequests.cs | 28 - src/Servers/Chat/test/Game/GameTest.cs | 313 - .../Chat/test/General/GeneralRequestTest.cs | 220 - .../Chat/test/General/GeneralRequests.cs | 51 - .../Chat/test/Message/MessageRequestTest.cs | 50 - .../Chat/test/Message/MessageRequests.cs | 15 - src/Servers/Chat/test/MockObject.cs | 27 - src/Servers/Chat/test/RedisChatChannelTest.cs | 169 - .../Chat/test/UniSpy.Server.Chat.Test.csproj | 32 - .../Abstraction/BaseClass/CmdHandlerBase.cs | 19 - .../src/Abstraction/BaseClass/RequestBase.cs | 52 - .../src/Abstraction/BaseClass/ResponseBase.cs | 13 - .../src/Abstraction/BaseClass/ResultBase.cs | 9 - .../Interface/IStorageOperation.cs | 16 - .../GameStatus/src/Aggregate/Misc/GSCrypt.cs | 42 - .../GameStatus/src/Application/Client.cs | 51 - .../GameStatus/src/Application/ClientInfo.cs | 21 - .../GameStatus/src/Application/Program.cs | 26 - .../GameStatus/src/Application/Server.cs | 22 - .../src/Application/ServerLauncher.cs | 11 - .../src/Application/StorageOperation.cs | 120 - .../src/Contract/Request/AuthGameRequest.cs | 49 - .../src/Contract/Request/AuthPlayerRequest.cs | 59 - .../Contract/Request/GetPlayerDataRequest.cs | 82 - .../Contract/Request/GetProfileIDRequest.cs | 40 - .../src/Contract/Request/NewGameRequest.cs | 52 - .../Contract/Request/SetPlayerDataRequest.cs | 79 - .../src/Contract/Request/UpdateGameRequest.cs | 71 - .../src/Contract/Response/AuthGameResponse.cs | 20 - .../Contract/Response/AuthPlayerResponse.cs | 20 - .../Response/GetPlayerDataResponse.cs | 20 - .../Contract/Response/GetProfileIDResponse.cs | 20 - .../Response/SetPlayerDataResponse.cs | 21 - .../src/Contract/Result/AuthGameResult.cs | 12 - .../src/Contract/Result/AuthPlayerResult.cs | 12 - .../Contract/Result/GetPlayerDataResult.cs | 13 - .../src/Contract/Result/GetProfileIDResult.cs | 12 - .../Contract/Result/SetPlayerDataResult.cs | 11 - src/Servers/GameStatus/src/Dockerfile | 20 - .../GameStatus/src/Enumerate/AuthMethod.cs | 10 - .../GameStatus/src/Enumerate/GSErrorCode.cs | 15 - .../src/Enumerate/PersistantStorage.cs | 22 - .../GameStatus/src/Exception/Exception.cs | 17 - .../src/Handler/CmdHandler/AuthGameHandler.cs | 31 - .../Handler/CmdHandler/AuthPlayerHandler.cs | 57 - .../CmdHandler/GetPlayerDataHandler.cs | 55 - .../Handler/CmdHandler/GetProfileIDHandler.cs | 31 - .../src/Handler/CmdHandler/NewGameHandler.cs | 29 - .../CmdHandler/SetPlayerDataHandler.cs | 24 - .../Handler/CmdHandler/UpdateGameHandler.cs | 39 - .../GameStatus/src/Handler/CmdSwitcher.cs | 59 - .../src/UniSpy.Server.GameStatus.csproj | 22 - src/Servers/GameStatus/test/GameTest.cs | 60 - src/Servers/GameStatus/test/MockObject.cs | 24 - src/Servers/GameStatus/test/RequestTest.cs | 135 - .../test/UniSpy.Server.GameStatus.Test.csproj | 31 - .../src/Application/Program.cs | 25 - .../src/Application/Server.cs | 81 - .../src/Application/ServerLauncher.cs | 22 - .../Controllers/NatNegotiationController.cs | 56 - src/Servers/GameTrafficRelay/src/Dockerfile | 20 - .../src/UniSpy.Server.GameTrafficRelay.csproj | 28 - .../src/appsettings.Development.json | 8 - .../GameTrafficRelay/src/appsettings.json | 9 - src/Servers/GameTrafficRelay/test/GameTest.cs | 73 - .../GameTrafficRelay/test/MockObject.cs | 22 - ...UniSpy.Server.GameTrafficRelay.Test.csproj | 31 - .../Abstraction/BaseClass/CmdHandlerBase.cs | 19 - .../BaseClass/CommonRequestBase.cs | 20 - .../BaseClass/CommonResponseBase.cs | 26 - .../Abstraction/BaseClass/CommonResultBase.cs | 13 - .../src/Abstraction/BaseClass/RequestBase.cs | 39 - .../src/Abstraction/BaseClass/ResponseBase.cs | 29 - .../src/Abstraction/BaseClass/ResultBase.cs | 12 - .../Interface/IStorageOperation.cs | 19 - .../GameTrafficRelay/ConnectionListener.cs | 127 - .../GameTrafficRelay/NatNegotiation.cs | 24 - .../GameTrafficRelay/NetworkUtils.cs | 45 - .../GameTrafficRelay/RelayServerInfo.cs | 28 - .../GameTrafficRelay/RelaySwitcher.cs | 26 - .../GameTrafficRelay/ServerStatusReporter.cs | 34 - .../src/Aggregate/Misc/NatProperty.cs | 20 - .../src/Aggregate/Misc/NatReportInfo.cs | 23 - .../src/Aggregate/Redis/InitPacketCache.cs | 184 - .../src/Aggregate/Redis/NatFailInfoCache.cs | 43 - .../NatNegotiation/src/Application/Client.cs | 20 - .../src/Application/ClientInfo.cs | 15 - .../NatNegotiation/src/Application/Program.cs | 26 - .../NatNegotiation/src/Application/Server.cs | 23 - .../src/Application/ServerLauncher.cs | 11 - .../src/Application/StorageOperation.cs | 58 - .../Contract/Request/AddressCheckRequest.cs | 13 - .../src/Contract/Request/ConnectAckRequest.cs | 19 - .../src/Contract/Request/ConnectRequest.cs | 13 - .../src/Contract/Request/ErtAckRequest.cs | 13 - .../src/Contract/Request/InitRequest.cs | 42 - .../src/Contract/Request/NatifyRequest.cs | 12 - .../src/Contract/Request/PingRequest.cs | 30 - .../src/Contract/Request/PreInitRequest.cs | 23 - .../src/Contract/Request/ReportRequest.cs | 50 - .../src/Contract/Response/ConnectResponse.cs | 27 - .../src/Contract/Response/InitResponse.cs | 11 - .../src/Contract/Response/PingResponse.cs | 18 - .../src/Contract/Response/PreInitResponse.cs | 19 - .../src/Contract/Response/ReportResponse.cs | 26 - .../src/Contract/Result/AddressCheckResult.cs | 13 - .../src/Contract/Result/ConnectResult.cs | 25 - .../src/Contract/Result/ErtAckResult.cs | 13 - .../src/Contract/Result/InitResult.cs | 13 - .../src/Contract/Result/NatifyResult.cs | 13 - .../src/Contract/Result/PreInitResult.cs | 17 - .../src/Contract/Result/ReportResult.cs | 13 - src/Servers/NatNegotiation/src/Dockerfile | 20 - .../src/Enumerate/ConnectPacketStatus.cs | 9 - .../src/Enumerate/NatMappingType.cs | 33 - .../src/Enumerate/NatNegResult.cs | 12 - .../src/Enumerate/NatPacketType.cs | 34 - .../src/Enumerate/NatPortType.cs | 22 - .../src/Enumerate/NatPunchStrategy.cs | 19 - .../src/Enumerate/NatStrategyType.cs | 12 - .../src/Enumerate/NatifyPacketType.cs | 18 - .../src/Exception/NNException.cs | 17 - .../Handler/CmdHandler/AddressCheckHandler.cs | 112 - .../Handler/CmdHandler/ConnectAckHandler.cs | 23 - .../src/Handler/CmdHandler/ConnectHandler.cs | 166 - .../src/Handler/CmdHandler/ErtAckHandler.cs | 27 - .../src/Handler/CmdHandler/InitHandler.cs | 146 - .../src/Handler/CmdHandler/NatifyHandler.cs | 27 - .../src/Handler/CmdHandler/PingHandler.cs | 20 - .../src/Handler/CmdHandler/ReportHandler.cs | 73 - .../NatNegotiation/src/Handler/CmdSwitcher.cs | 46 - .../src/UniSpy.Server.NatNegotiation.csproj | 24 - src/Servers/NatNegotiation/test/GameTest.cs | 383 -- .../NatNegotiation/test/HandlerTest.cs | 88 - src/Servers/NatNegotiation/test/MockObject.cs | 24 - .../NatNegotiation/test/NatDetectionTest.cs | 221 - .../NatNegotiation/test/RequestTest.cs | 87 - .../UniSpy.Server.NatNegotiation.Test.csproj | 31 - .../Abstraction/BaseClass/CmdHandlerBase.cs | 29 - .../BaseClass/LoggedInCmdHandlerBase.cs | 48 - .../src/Abstraction/BaseClass/RequestBase.cs | 62 - .../src/Abstraction/BaseClass/ResponseBase.cs | 13 - .../src/Abstraction/BaseClass/ResultBase.cs | 9 - .../src/Abstraction/Interface/IShareClient.cs | 12 - .../Interface/IStorageOperation.cs | 25 - .../src/Aggregate/Misc/LoginChallengeProof.cs | 64 - .../src/Aggregate/Misc/SDKRevision.cs | 19 - .../src/Aggregate/Misc/Status/UserStatus.cs | 20 - .../Aggregate/Misc/Status/UserStatusInfo.cs | 24 - .../src/Aggregate/Redis/UserInfoChannel.cs | 41 - .../src/Aggregate/Redis/UserInfoMessage.cs | 19 - .../src/Aggregate/Redis/UserInfoRedisKey.cs | 17 - .../Aggregate/Redis/UserInfoRedisOperator.cs | 9 - .../src/Aggregate/RemoteClient.cs | 48 - .../src/Application/Client.cs | 53 - .../src/Application/ClientInfo.cs | 97 - .../src/Application/ClientManager.cs | 17 - .../src/Application/Program.cs | 26 - .../src/Application/Server.cs | 39 - .../src/Application/ServerLauncher.cs | 12 - .../src/Application/StorageOperation.cs | 283 - .../Contract/Request/Buddy/AddBuddyRequest.cs | 35 - .../Contract/Request/Buddy/DelBuddyRequest.cs | 40 - .../Contract/Request/Buddy/InviteToRequest.cs | 49 - .../Request/Buddy/StatusInfoRequest.cs | 79 - .../Contract/Request/Buddy/StatusRequest.cs | 46 - .../Request/General/KeepAliveRequest.cs | 12 - .../Contract/Request/General/LoginRequest.cs | 184 - .../Contract/Request/General/LogoutRequest.cs | 12 - .../Request/Profile/AddBlockRequest.cs | 34 - .../Request/Profile/GetProfileRequest.cs | 38 - .../Request/Profile/NewProfileRequest.cs | 48 - .../Request/Profile/RegisterCDKeyRequest.cs | 32 - .../Request/Profile/RegisterNickRequest.cs | 44 - .../Request/Profile/UpdateProfileRequest.cs | 166 - .../Request/Profile/UpdateUIRequest.cs | 31 - .../Response/Buddy/AddBuddyResponse.cs | 30 - .../Response/Buddy/BlockListResponse.cs | 30 - .../Response/Buddy/BuddyListResponse.cs | 28 - .../Response/Buddy/StatusInfoResponse.cs | 29 - .../Response/General/KeepAliveResponse.cs | 16 - .../Response/General/LoginResponse.cs | 36 - .../Response/General/NewUserResponse.cs | 17 - .../Response/Profile/GetProfileResponse.cs | 50 - .../Response/Profile/NewProfileResponse.cs | 16 - .../Response/Profile/RegisterCDKeyResponse.cs | 16 - .../Response/Profile/RegisterNickResponse.cs | 16 - .../Contract/Result/Buddy/AddBuddyResult.cs | 12 - .../Contract/Result/Buddy/BlockListResult.cs | 14 - .../Contract/Result/Buddy/BuddyListResult.cs | 14 - .../Contract/Result/Buddy/StatusInfoResult.cs | 15 - .../src/Contract/Result/Buddy/StatusResult.cs | 13 - .../Contract/Result/General/LoginResult.cs | 27 - .../src/Contract/Result/PCMDefaultResult.cs | 11 - .../Result/Profile/GetProfileResult.cs | 44 - .../Result/Profile/NewProfileResult.cs | 14 - .../PresenceConnectionManager/src/Dockerfile | 21 - .../src/Enumerate/BuddyMessageType.cs | 32 - .../src/Enumerate/DisconnectReason.cs | 91 - .../src/Enumerate/FireWallType.cs | 10 - .../src/Enumerate/GPBasic.cs | 201 - .../src/Enumerate/GPStatusCode.cs | 14 - .../src/Enumerate/GenderType.cs | 21 - .../src/Enumerate/LoginStatus.cs | 10 - .../src/Enumerate/LoginType.cs | 23 - .../src/Enumerate/PublicMask.cs | 53 - .../src/Enumerate/QuietModeType.cs | 13 - .../src/Enumerate/SDKRevisionType.cs | 33 - .../CmdHandler/Buddy/AddBuddyHandler.cs | 28 - .../CmdHandler/Buddy/BlockListHandler.cs | 30 - .../CmdHandler/Buddy/BuddyListHandler.cs | 50 - .../Buddy/BuddyStatusInfoHandler.cs | 24 - .../CmdHandler/Buddy/DelBuddyHandler.cs | 26 - .../CmdHandler/Buddy/InviteToHandler.cs | 35 - .../Handler/CmdHandler/Buddy/StatusHandler.cs | 37 - .../CmdHandler/Buddy/StatusInfoHandler.cs | 54 - .../CmdHandler/General/KeepAliveHandler.cs | 26 - .../CmdHandler/General/LoginHandler.cs | 144 - .../CmdHandler/General/LogoutHandler.cs | 19 - .../CmdHandler/General/NewUserHandler.cs | 17 - .../CmdHandler/General/SDKRevisionHandler.cs | 26 - .../CmdHandler/Profile/AddBlockHandler.cs | 30 - .../CmdHandler/Profile/GetProfileHandler.cs | 37 - .../CmdHandler/Profile/NewProfileHandler.cs | 48 - .../Profile/RegisterCDKeyHandler.cs | 21 - .../CmdHandler/Profile/RegisterNickHandler.cs | 42 - .../CmdHandler/Profile/RemoveBlockHandler.cs | 21 - .../Profile/UpdateProfileHandler.cs | 62 - .../Profile/UpdateUserInfoHandler.cs | 24 - .../src/Handler/CmdSwitcher.cs | 89 - ...py.Server.PresenceConnectionManager.csproj | 23 - .../test/Buddy/BuddyRequestTest.cs | 53 - .../test/Buddy/BuddyRequests.cs | 16 - .../test/Game/GameTest.cs | 48 - .../test/General/GeneralRequestTest.cs | 106 - .../test/General/GeneralRequests.cs | 17 - .../test/MokeObject.cs | 31 - .../test/Persist/PersistTest.cs | 17 - .../test/Profile/ProfileRequestTest.cs | 107 - .../test/Profile/ProfileRequests.cs | 21 - ...rver.PresenceConnectionManager.Test.csproj | 31 - .../Abstraction/BaseClass/CmdHandlerBase.cs | 27 - .../src/Abstraction/BaseClass/RequestBase.cs | 36 - .../src/Abstraction/BaseClass/ResponseBase.cs | 12 - .../src/Abstraction/BaseClass/ResultBase.cs | 9 - .../Interface/IStorageOperation.cs | 31 - .../src/Aggregate/PSPRequestName.cs | 46 - .../src/Application/Client.cs | 17 - .../src/Application/ClientInfo.cs | 11 - .../src/Application/Program.cs | 27 - .../src/Application/Server.cs | 21 - .../src/Application/ServerLauncher.cs | 11 - .../src/Application/StorageOperation.cs | 360 -- .../src/Contract/Request/CheckRequest.cs | 42 - .../src/Contract/Request/NewUserRequest.cs | 110 - .../src/Contract/Request/NicksRequest.cs | 50 - .../src/Contract/Request/OthersListRequest.cs | 50 - .../src/Contract/Request/OthersRequest.cs | 52 - .../src/Contract/Request/SearchRequest.cs | 118 - .../Contract/Request/SearchUniqueRequest.cs | 45 - .../Contract/Request/UniqueSearchRequest.cs | 50 - .../src/Contract/Request/ValidRequest.cs | 42 - .../src/Contract/Response/CheckResponse.cs | 26 - .../src/Contract/Response/NewUserResponse.cs | 19 - .../src/Contract/Response/NicksResponse.cs | 32 - .../Contract/Response/OthersListResponse.cs | 25 - .../src/Contract/Response/OthersResponse.cs | 29 - .../src/Contract/Response/SearchResponse.cs | 32 - .../Contract/Response/SearchUniqueResponse.cs | 30 - .../Contract/Response/UniqueSearchResponse.cs | 28 - .../src/Contract/Response/ValidResponse.cs | 27 - .../src/Contract/Result/CheckResult.cs | 12 - .../src/Contract/Result/NewUserResult.cs | 13 - .../src/Contract/Result/NicksResult.cs | 20 - .../src/Contract/Result/OthersListResult.cs | 19 - .../src/Contract/Result/OthersResult.cs | 24 - .../src/Contract/Result/PSPDefaultResult.cs | 11 - .../src/Contract/Result/SearchResult.cs | 25 - .../src/Contract/Result/SearchUniqueResult.cs | 24 - .../src/Contract/Result/UniqueSearchResult.cs | 12 - .../src/Contract/Result/ValidResult.cs | 13 - .../PresenceSearchPlayer/src/Dockerfile | 20 - .../src/Enumerator/GPEnum.cs | 208 - .../src/Enumerator/GPErrorCode.cs | 142 - .../src/Enumerator/NewUserStatus.cs | 15 - .../GPAddBuddyAlreadyBuddyException.cs | 20 - .../AddBuddy/GPAddBuddyBadFormException.cs | 20 - .../AddBuddy/GPAddBuddyBadNewException.cs | 20 - .../Exception/AddBuddy/GPAddBuddyException.cs | 20 - .../AuthAdd/GPAuthAddBadFormException.cs | 20 - .../AuthAdd/GPAuthAddBadSigExceptiion.cs | 20 - .../Exception/AuthAdd/GPAuthAddException.cs | 20 - .../Exception/BuddyMsg/GPBuddyMsgException.cs | 20 - .../GPBuddyMsgExtInfoNotSupportedException.cs | 20 - .../BuddyMsg/GPBuddyMsgNotBuddyException.cs | 20 - .../src/Exception/Check/CheckException.cs | 24 - .../General/GPBadSessionKeyException.cs | 19 - .../General/GPConnectionCloseException.cs | 19 - .../Exception/General/GPDatabaseException.cs | 19 - .../src/Exception/General/GPException.cs | 39 - .../General/GPForcedDisconnectException.cs | 19 - .../Exception/General/GPNetworkException.cs | 19 - .../General/GPNotLoggedInException.cs | 18 - .../src/Exception/General/GPParseException.cs | 19 - .../Exception/General/GPUdpLayerException.cs | 19 - .../Login/GPLoginBadEmailException.cs | 19 - .../Login/GPLoginBadLoginTicketException.cs | 19 - .../Login/GPLoginBadNickException.cs | 19 - .../Login/GPLoginBadPasswordException.cs | 19 - .../Login/GPLoginBadPreAuthException.cs | 20 - .../Login/GPLoginBadProfileException.cs | 19 - .../Login/GPLoginBadUniquenickException.cs | 19 - .../Login/GPLoginConnectionFailedException.cs | 19 - .../src/Exception/Login/GPLoginException.cs | 28 - .../Login/GPLoginProfileDeletedException.cs | 19 - .../Login/GPLoginServerAuthFailedException.cs | 19 - .../Login/GPLoginTicketExpiredException.cs | 19 - .../Login/GPLoginTimeOutException.cs | 19 - .../GPNewProfileBadNickException.cs | 19 - .../NewProfile/GPNewProfileBadOldNick.cs | 19 - .../NewProfile/GPNewProfileException.cs | 28 - .../NewUser/GPNewUserBadNickException.cs | 19 - .../NewUser/GPNewUserBadPasswordException.cs | 19 - .../Exception/NewUser/GPNewUserException.cs | 30 - .../GPNewUserUniquenickInUseException.cs | 19 - .../GPNewUserUniquenickInvalidException.cs | 19 - .../src/Exception/Status/GPStatusException.cs | 20 - .../UpdatePro/GPUpdateProBadNickException.cs | 19 - .../UpdatePro/GPUpdateProException.cs | 28 - .../UpdateUI/GPUpdateUIBadEmailException.cs | 19 - .../Exception/UpdateUI/GPUpdateUIException.cs | 28 - .../src/Handler/CmdHandler/CheckHandler.cs | 43 - .../src/Handler/CmdHandler/NewUserHandler.cs | 145 - .../src/Handler/CmdHandler/NicksHandler.cs | 46 - .../src/Handler/CmdHandler/OthersHandler.cs | 42 - .../Handler/CmdHandler/OthersListHandler.cs | 40 - .../src/Handler/CmdHandler/PmatchHandler.cs | 52 - .../src/Handler/CmdHandler/SearchHandler.cs | 67 - .../Handler/CmdHandler/SearchUniqueHandler.cs | 39 - .../Handler/CmdHandler/UniqueSearchHandler.cs | 38 - .../src/Handler/CmdHandler/ValidHandler.cs | 37 - .../src/Handler/CmdSwitcher.cs | 65 - .../UniSpy.Server.PresenceSearchPlayer.csproj | 22 - .../PresenceSearchPlayer/test/GameTest.cs | 17 - .../PresenceSearchPlayer/test/MokeObject.cs | 24 - .../PresenceSearchPlayer/test/RawRequests.cs | 31 - .../PresenceSearchPlayer/test/RequestTests.cs | 25 - ...py.Server.PresenceSearchPlayer.Test.csproj | 31 - .../Interface/IStorageOperation.cs | 11 - .../src/Aggregate/Redis/PeerRoomInfo.cs | 70 - .../QueryReport/src/Application/Client.cs | 30 - .../QueryReport/src/Application/ClientInfo.cs | 13 - .../QueryReport/src/Application/Program.cs | 26 - .../QueryReport/src/Application/Server.cs | 28 - .../src/Application/ServerLauncher.cs | 11 - .../src/Application/StorageOperation.cs | 28 - src/Servers/QueryReport/src/Dockerfile | 20 - .../QueryReport/src/Exception/Exception.cs | 17 - .../src/UniSpy.Server.QueryReport.csproj | 23 - .../Abstraction/BaseClass/CmdHandlerBase.cs | 12 - .../V1/Abstraction/BaseClass/RequestBase.cs | 21 - .../V1/Abstraction/BaseClass/ResponseBase.cs | 10 - .../V1/Abstraction/BaseClass/ResultBase.cs | 9 - .../Interface/IStorageOperation.cs | 15 - .../src/V1/Aggregation/Enctype0.cs | 17 - .../V1/Aggregation/Redis/GameServerCache.cs | 40 - .../src/V1/Application/StorageOperation.cs | 56 - .../src/V1/Contract/Request/EchoRequest.cs | 27 - .../V1/Contract/Request/HeartbeatRequest.cs | 39 - .../V1/Contract/Request/ValidateRequest.cs | 22 - .../src/V1/Contract/Response/EchoResponse.cs | 17 - .../V1/Contract/Response/HeartbeatResponse.cs | 17 - .../src/V1/Handler/CmdHandler/EchoHandler.cs | 22 - .../V1/Handler/CmdHandler/HeartbeatHandler.cs | 39 - .../V1/Handler/CmdHandler/ValidateHandler.cs | 20 - .../QueryReport/src/V1/Handler/CmdSwitcher.cs | 39 - .../Abstraction/BaseClass/CmdHandlerBase.cs | 15 - .../V2/Abstraction/BaseClass/RequestBase.cs | 34 - .../V2/Abstraction/BaseClass/ResponseBase.cs | 28 - .../V2/Abstraction/BaseClass/ResultBase.cs | 12 - .../Interface/IStorageOperation.cs | 18 - .../src/V2/Aggregate/Misc/Encryption.cs | 116 - .../src/V2/Aggregate/Misc/NatNegCookie.cs | 22 - .../src/V2/Aggregate/Misc/RegionID.cs | 30 - .../src/V2/Aggregate/Redis/GameServerInfo.cs | 52 - .../V2/Aggregate/Redis/HeartBeatChannel.cs | 13 - .../src/V2/Aggregate/Redis/NatNegChannel.cs | 30 - .../src/V2/Application/StorageOperation.cs | 44 - .../V2/Contract/Request/AvaliableRequest.cs | 34 - .../V2/Contract/Request/ChallengeRequest.cs | 12 - .../Request/ClientMessageAckRequest.cs | 12 - .../Contract/Request/ClientMessageRequest.cs | 27 - .../src/V2/Contract/Request/EchoRequest.cs | 12 - .../V2/Contract/Request/HeartBeatRequest.cs | 204 - .../V2/Contract/Request/KeepAliveRequest.cs | 12 - .../V2/Contract/Response/AvaliableResponse.cs | 26 - .../V2/Contract/Response/ChallengeResponse.cs | 28 - .../Response/ClientMessageResponse.cs | 25 - .../V2/Contract/Response/HeartBeatResponse.cs | 31 - .../V2/Contract/Response/KeepAliveResponse.cs | 12 - .../src/V2/Contract/Result/ChallengeResult.cs | 12 - .../V2/Contract/Result/ClientMessageResult.cs | 15 - .../src/V2/Contract/Result/EchoResult.cs | 13 - .../src/V2/Contract/Result/HeartBeatResult.cs | 14 - .../src/V2/Enumerate/GeneralEnum.cs | 51 - .../src/V2/Enumerate/HeartBeatEnum.cs | 18 - .../src/V2/Enumerate/ServerAvailability.cs | 10 - .../V2/Handler/CmdHandler/AvailableHandler.cs | 23 - .../V2/Handler/CmdHandler/ChallengeHandler.cs | 37 - .../CmdHandler/ClientMessageAckHandler.cs | 20 - .../CmdHandler/ClientMessageHandler.cs | 26 - .../src/V2/Handler/CmdHandler/EchoHandler.cs | 39 - .../V2/Handler/CmdHandler/HeartBeatHandler.cs | 71 - .../V2/Handler/CmdHandler/KeepAliveHandler.cs | 28 - .../QueryReport/src/V2/Handler/CmdSwitcher.cs | 54 - .../UniSpy.Server.QueryReport.Test.csproj | 31 - src/Servers/QueryReport/test/V2/GameTest.cs | 115 - src/Servers/QueryReport/test/V2/MockObject.cs | 24 - .../QueryReport/test/V2/RequestTest.cs | 77 - .../src/Aggregate/GameServerFilter.cs | 7 - .../ServerBrowser/src/Application/Program.cs | 26 - .../src/Application/ServerLauncher.cs | 18 - src/Servers/ServerBrowser/src/Dockerfile | 23 - .../ServerBrowser/src/Exception/Exception.cs | 26 - .../src/UniSpy.Server.ServerBrowser.csproj | 24 - .../Abstraction/BaseClass/CmdHandlerBase.cs | 20 - .../V1/Abstraction/BaseClass/EnctypeBase.cs | 232 - .../V1/Abstraction/BaseClass/RequestBase.cs | 27 - .../V1/Abstraction/BaseClass/ResponseBase.cs | 10 - .../V1/Abstraction/BaseClass/ResultBase.cs | 9 - .../V1/Abstraction/Interface/IEnctypeTest.cs | 27 - .../Interface/IStorageOperation.cs | 13 - .../src/V1/Aggregate/Enctype1.cs | 321 - .../src/V1/Aggregate/Enctype2.cs | 192 - .../src/V1/Application/Client.cs | 25 - .../src/V1/Application/ClientInfo.cs | 15 - .../src/V1/Application/Server.cs | 22 - .../V1/Contract/Request/GameNameRequest.cs | 53 - .../src/V1/Contract/Request/ListRequest.cs | 52 - .../V1/Contract/Response/GameNameResponse.cs | 16 - .../src/V1/Contract/Response/ListResponse.cs | 87 - .../src/V1/Contract/Result/ListResult.cs | 16 - .../V1/Handler/CmdHandler/GameNameHandler.cs | 45 - .../src/V1/Handler/CmdHandler/ListHandler.cs | 39 - .../src/V1/Handler/CmdSwitcher.cs | 41 - .../Abstraction/BaseClass/AdHocRequestBase.cs | 28 - .../BaseClass/AdHocResponseBase.cs | 15 - .../Abstraction/BaseClass/CmdHandlerBase.cs | 15 - .../V2/Abstraction/BaseClass/RequestBase.cs | 23 - .../V2/Abstraction/BaseClass/ResponseBase.cs | 17 - .../V2/Abstraction/BaseClass/ResultBase.cs | 9 - .../ServerListUpdateOptionHandlerBase.cs | 57 - .../ServerListUpdateOptionRequestBase.cs | 25 - .../ServerListUpdateOptionResponseBase.cs | 60 - .../ServerListUpdateOptionResultBase.cs | 14 - .../src/V2/Aggregate/HeartbeatChannel.cs | 20 - .../src/V2/Aggregate/Misc/Encryption.cs | 192 - .../src/V2/Aggregate/Misc/GameFilter.cs | 9 - .../src/V2/Aggregate/Misc/KeyType.cs | 49 - .../src/V2/Aggregate/Misc/SBStringFlag.cs | 10 - .../src/V2/Aggregate/ServerInfoBuilder.cs | 127 - .../src/V2/Application/Client.cs | 20 - .../src/V2/Application/ClientInfo.cs | 34 - .../src/V2/Application/ClientManager.cs | 15 - .../src/V2/Application/Server.cs | 34 - .../Request/AdHoc/PlayerSearchRequest.cs | 35 - .../Contract/Request/AdHoc/SendMsgRequest.cs | 21 - .../Request/AdHoc/ServerInfoRequest.cs | 12 - .../Request/ServerList/ServerListRequest.cs | 76 - .../AdHoc/DeleteServerInfoResponse.cs | 21 - .../AdHoc/UpdateServerInfoResponse.cs | 79 - .../ServerList/P2PGroupRoomListResponse.cs | 56 - .../ServerList/ServerMainListResponse.cs | 53 - .../ServerNetworkInfoListResponse.cs | 63 - .../V2/Contract/Result/AdHoc/AdHocResult.cs | 13 - .../ServerList/P2PGroupRoomListResult.cs | 14 - .../Result/ServerList/ServerMainListResult.cs | 15 - .../ServerList/ServerNetworkInfoListResult.cs | 15 - .../src/V2/Enumerate/GeneralEnum.cs | 24 - .../src/V2/Enumerate/ServerListEnum.cs | 85 - .../Handler/CmdHandler/AdHoc/AdHocHandler.cs | 52 - .../CmdHandler/AdHoc/SendMsgHandler.cs | 42 - .../CmdHandler/AdHoc/ServerInfoHandler.cs | 47 - .../ServerList/ServerListHandler.cs | 153 - .../src/V2/Handler/CmdSwitcher.cs | 40 - .../UniSpy.Server.ServerBrowser.Test.csproj | 33 - .../ServerBrowser/test/V1/EnctypeTest.cs | 180 - .../ServerBrowser/test/V1/RequestTest.cs | 23 - .../ServerBrowser/test/V2/FilterTest.cs | 30 - src/Servers/ServerBrowser/test/V2/GameTest.cs | 147 - .../ServerBrowser/test/V2/MockObject.cs | 25 - .../ServerBrowser/test/V2/PeerRoomTest.cs | 17 - .../ServerBrowser/test/V2/RequestTest.cs | 36 - .../src/Abstraction/CmdHandlerBase.cs | 13 - .../WebServer/src/Abstraction/RequestBase.cs | 22 - .../WebServer/src/Abstraction/ResponseBase.cs | 18 - .../WebServer/src/Abstraction/ResultBase.cs | 9 - .../src/Abstraction/SoapEnvelopBase.cs | 79 - .../src/Aggregate/SakeArrayObject.cs | 26 - .../WebServer/src/Aggregate/SoapXElement.cs | 64 - .../WebServer/src/Aggregate/WebEndpoints.cs | 31 - .../WebServer/src/Application/Client.cs | 29 - .../WebServer/src/Application/ClientInfo.cs | 43 - .../WebServer/src/Application/Program.cs | 26 - .../WebServer/src/Application/Server.cs | 29 - .../src/Application/ServerLauncher.cs | 12 - src/Servers/WebServer/src/Dockerfile | 20 - .../WebServer/src/Exception/Exception.cs | 13 - .../WebServer/src/Handler/CmdSwitcher.cs | 121 - .../Request/CreateMatchlessSessionRequest.cs | 28 - .../Contract/Request/CreateSessionRequest.cs | 28 - .../Request/SetReportIntentionRequest.cs | 37 - .../Contract/Request/SubmitReportRequest.cs | 37 - .../Auth/Abstraction/LoginRequestBase.cs | 41 - .../Auth/Abstraction/LoginResponseBase.cs | 68 - .../Auth/Abstraction/LoginResultBase.cs | 20 - .../Module/Auth/Abstraction/ResponseBase.cs | 13 - .../Module/Auth/Contract/AuthSoapEnvelope.cs | 13 - .../Contract/Request/LoginProfileRequest.cs | 46 - .../Request/LoginProfileWithGameIdRequest.cs | 25 - .../Contract/Request/LoginPs3CertRequest.cs | 25 - .../Request/LoginPs3CertWithGameIdRequest.cs | 25 - .../Request/LoginRemoteAuthRequest.cs | 31 - .../LoginRemoteAuthWithGameIdRequest.cs | 25 - .../Request/LoginUniqueNickRequest.cs | 31 - .../Request/LoginUniqueNickWithGameId.cs | 25 - .../Contract/Response/LoginProfileResponse.cs | 22 - .../LoginProfileWithGameIdResponse.cs | 21 - .../Contract/Response/LoginPs3CertResponse.cs | 24 - .../LoginPs3CertWithGameIdResponse.cs | 24 - .../Response/LoginRemoteAuthResponse.cs | 20 - .../LoginRemoteAuthWithGameIdResponse.cs | 20 - .../Response/LoginUniqueNickResponse.cs | 21 - .../LoginUniqueNickWithGameIdResponse.cs | 21 - .../Contract/Result/LoginProfileResult.cs | 11 - .../Contract/Result/LoginPs3CertResult.cs | 13 - .../Contract/Result/LoginRemoteAuthResult.cs | 11 - .../Contract/Result/LoginUniqueNickResult.cs | 11 - .../Auth/Exception/DatabaseException.cs | 15 - .../src/Module/Auth/Exception/Exception.cs | 53 - .../Exception/InvalidPasswordException.cs | 15 - .../Auth/Exception/InvalidProfileException.cs | 15 - .../Module/Auth/Exception/ParseException.cs | 15 - .../Module/Auth/Exception/ServerException.cs | 15 - .../Auth/Exception/ServerInitException.cs | 15 - .../Exception/UniqueNickExpredException.cs | 15 - .../Auth/Exception/UserNotFoundException.cs | 15 - .../Auth/Handler/LoginProfileHandler.cs | 51 - .../Handler/LoginProfileWithGameIdHandler.cs | 49 - .../Auth/Handler/LoginPs3CertHandler.cs | 17 - .../Handler/LoginPs3CertWithGameIdHandler.cs | 15 - .../Auth/Handler/LoginRemoteAuthHandler.cs | 53 - .../LoginRemoteAuthWithGameIdHandler.cs | 46 - .../Auth/Handler/LoginUniqueNickHandler.cs | 51 - .../LoginUniqueNickWithGameIdHandler.cs | 46 - .../Direct2Game/Abstraction/RequestBase.cs | 8 - .../Direct2Game/Abstraction/ResponseBase.cs | 10 - .../Direct2Game/Abstraction/ResultBase.cs | 7 - .../Contract/Direct2GameSoapEnvelope.cs | 13 - .../Request/GetPurchaseHistoryRequest.cs | 47 - .../Request/GetStoreAvailabilityRequest.cs | 48 - .../Response/GetPurchaseHistoryResponse.cs | 28 - .../Response/GetStoreAvailabilityResponse.cs | 25 - .../Result/GetPurchaseHistoryResult.cs | 9 - .../Result/GetStoreAvailabilityResult.cs | 21 - .../Handler/GetPurchaseHistoryHandler.cs | 32 - .../Handler/GetStoreAvailabilityHandler.cs | 21 - .../Contract/Request/GetTargettedAdRequest.cs | 14 - .../Contract/Request/ReportAdUsageRequest.cs | 14 - .../Contract/Request/MotdRequest.cs | 14 - .../Contract/Request/VercheckRequest.cs | 14 - .../Contract/Request/GetContestDataRequest.cs | 28 - .../Request/GetFriendRankingsRequest.cs | 31 - .../Request/GetRegionalDataRequest.cs | 25 - .../Request/GetTenAboveRankingsRequest.cs | 31 - .../Request/GetTopTenRankingsRequest.cs | 28 - .../Contract/Request/SubmitGhostRequest.cs | 37 - .../Contract/Request/SubmitScoresRequest.cs | 35 - .../Module/Sake/Abstraction/CmdHandlerBase.cs | 35 - .../Module/Sake/Abstraction/RequestBase.cs | 29 - .../Module/Sake/Abstraction/ResponseBase.cs | 12 - .../src/Module/Sake/Abstraction/ResultBase.cs | 5 - .../Contract/Request/CreateRecordRequest.cs | 39 - .../Contract/Request/DeleteRecordRequest.cs | 21 - .../Contract/Request/GetMyRecordsRequest.cs | 28 - .../Request/GetRandomRecordsRequest.cs | 31 - .../Contract/Request/GetRecordLimitRequest.cs | 13 - .../Request/GetSpecificRecordsRequest.cs | 35 - .../Contract/Request/RateRecordRequest.cs | 24 - .../Request/SearchForRecordsRequest.cs | 53 - .../Contract/Request/UpdateRecordRequest.cs | 39 - .../Contract/Response/CreateRecordResponse.cs | 28 - .../Contract/Response/GetMyRecordResponse.cs | 16 - .../Response/SearchForRecordResponse.cs | 39 - .../Contract/Result/CreateRecordResult.cs | 16 - .../Contract/Result/GetMyRecordsResult.cs | 15 - .../Contract/Result/SearchForRecordsResult.cs | 10 - .../Module/Sake/Contract/SakeSoapEnvelope.cs | 13 - .../Sake/Handler/CreateRecordHandler.cs | 23 - .../Sake/Handler/GetMyRecordsHandler.cs | 32 - .../Sake/Handler/SearchForRecordsHandler.cs | 41 - .../src/UniSpy.Server.WebServer.csproj | 22 - .../WebServer/test/Atlas/RawRequests.cs | 75 - .../WebServer/test/Atlas/RequestsTest.cs | 55 - src/Servers/WebServer/test/Auth/GameTest.cs | 64 - .../WebServer/test/Auth/HandlerTest.cs | 34 - src/Servers/WebServer/test/Auth/RSATest.cs | 41 - .../WebServer/test/Auth/RawRequests.cs | 86 - .../WebServer/test/Auth/RequestsTest.cs | 163 - src/Servers/WebServer/test/GeneralTest.cs | 42 - src/Servers/WebServer/test/MokeObject.cs | 23 - .../WebServer/test/Racing/RawRequests.cs | 124 - .../WebServer/test/Racing/RequestsTest.cs | 83 - src/Servers/WebServer/test/Sake/GameTest.cs | 60 - .../WebServer/test/Sake/HandlerTest.cs | 107 - .../WebServer/test/Sake/RawRequests.cs | 289 - .../WebServer/test/Sake/RequestsTest.cs | 181 - .../WebServer/test/Sake/ResponseTest.cs | 27 - .../test/UniSpy.Server.WebServer.Test.csproj | 31 - .../Client.cs => backends/__init__.py} | 0 .../library/abstractions/cmd_handler_base.py | 15 + .../library/abstractions/request_base.py | 28 + .../gamespy/library/contracts/chat.py} | 0 .../gamespy/protocols/chat/requests.py | 12 + .../natneg/aggregates/init_packet_info.py | 33 + .../protocols/natneg/applications/data.py | 7 + .../protocols/natneg/contracts/requests.py | 66 + .../contracts/requests.py | 196 + .../protocols/presence_search_player/data.py} | 0 .../presence_search_player/requests.py | 60 + src/backends/routers/gamespy/chat.py | 41 + src/backends/routers/gamespy/gstats.py | 0 src/backends/routers/gamespy/natneg.py | 0 .../gamespy/presence_connection_manager.py | 17 + .../routers/gamespy/presence_search_player.py | 0 src/backends/routers/gamespy/query_report.py | 0 .../routers/gamespy/server_browser.py | 0 src/backends/routers/gamespy/webservices.py | 0 src/backends/urls.py | 9 + src/docker-compose.yaml | 28 + src/library/__init__.py | 0 src/library/abstractions/__init__.py | 0 src/library/abstractions/brocker.py | 38 + src/library/abstractions/client.py | 119 + src/library/abstractions/connections.py | 91 + src/library/abstractions/contracts.py | 68 + src/library/abstractions/enctypt_base.py | 11 + src/library/abstractions/handler.py | 76 + src/library/abstractions/redis_channel.py | 2 + .../abstractions/server_launcher_base.py | 53 + src/library/abstractions/switcher.py | 58 + src/library/database/__init__.py | 0 src/library/database/mongodb_orm.py | 12 + src/library/database/pg_orm.py | 204 + src/library/database/redis.py | 16 + src/library/encryption/__init__.py | 0 src/library/encryption/encoding.py | 12 + src/library/encryption/gs_encryption.py | 99 + src/library/encryption/xor_encryption.py | 57 + src/library/exceptions/__init__.py | 0 src/library/exceptions/error.py | 46 + src/library/extentions/__init__.py | 0 src/library/extentions/bytes_extentions.py | 7 + src/library/extentions/encoding.py | 9 + src/library/extentions/gamespy_ramdoms.py | 137 + src/library/extentions/gamespy_utils.py | 76 + src/library/extentions/password_encoder.py | 76 + src/library/extentions/redis_orm.py | 36 + src/library/extentions/string_extentions.py | 118 + src/library/log/__init__.py | 0 src/library/log/log_manager.py | 91 + src/library/network/__init__.py | 9 + src/library/network/http/__init__.py | 0 src/library/network/http/http_handler.py | 76 + src/library/network/tcp/__init__.py | 0 src/library/network/tcp/tcp_handler.py | 74 + src/library/network/udp/__init__.py | 0 src/library/network/udp/udp_handler.py | 57 + src/library/unispy_server_config.py | 122 + src/requirement.txt | 12 + src/servers/__init__.py | 0 src/servers/chat/abstractions/channel.py | 75 + src/servers/chat/abstractions/contract.py | 67 + src/servers/chat/abstractions/handler.py | 23 + src/servers/chat/abstractions/message.py | 42 + src/servers/chat/aggregates/brockers.py | 37 + src/servers/chat/aggregates/channel.py | 184 + src/servers/chat/aggregates/channel_user.py | 36 + .../chat/aggregates/key_value_manager.py | 32 + src/servers/chat/aggregates/peer_room.py | 64 + src/servers/chat/aggregates/response_name.py | 57 + src/servers/chat/aggregates/storage_info.py | 23 + src/servers/chat/applications/client.py | 17 + .../chat/applications/server_launcher.py | 18 + src/servers/chat/applications/switcher.py | 152 + src/servers/chat/contracts/__init__.py | 0 .../chat/contracts/requests/__init__.py | 0 .../chat/contracts/requests/channel.py | 348 ++ .../chat/contracts/requests/general.py | 285 + .../chat/contracts/requests/message.py | 17 + .../chat/contracts/responses/__init__.py | 0 .../chat/contracts/responses/channel.py | 173 + .../chat/contracts/responses/general.py | 161 + .../chat/contracts/responses/message.py | 68 + .../chat/contracts/results/__init__.py | 0 src/servers/chat/contracts/results/channel.py | 61 + src/servers/chat/contracts/results/general.py | 60 + src/servers/chat/contracts/results/message.py | 17 + src/servers/chat/enums/general.py | 62 + src/servers/chat/enums/irc_error_code.py | 19 + src/servers/chat/enums/peer_room.py | 8 + src/servers/chat/exceptions/channel.py | 44 + src/servers/chat/exceptions/general.py | 116 + src/servers/chat/handlers/channel.py | 182 + src/servers/chat/handlers/general.py | 209 + src/servers/chat/handlers/message.py | 68 + .../applications/connection_listener.py | 17 + .../game_traffic_relay/applications/router.py | 10 + src/servers/natneg/abstractions/contracts.py | 86 + src/servers/natneg/abstractions/handlers.py | 16 + .../natneg/aggregations/init_packet_info.py | 33 + .../natneg/aggregations/relay_server_info.py | 11 + src/servers/natneg/applications/client.py | 14 + src/servers/natneg/contracts/requests.py | 92 + src/servers/natneg/contracts/responses.py | 12 + src/servers/natneg/contracts/results.py | 48 + src/servers/natneg/enums/general.py | 73 + src/servers/natneg/handlers/handlers.py | 112 + src/servers/natneg/handlers/switcher.py | 20 + src/servers/natneg/tests/redis.py | 37 + src/servers/natneg/tests/tests.py | 118 + .../abstractions/contracts.py | 56 + .../abstractions/handler.py | 35 + .../aggregates/login_challenge.py | 35 + .../aggregates/sdk_revision.py | 59 + .../aggregates/user_status.py | 10 + .../aggregates/user_status_info.py | 17 + .../applications/client.py | 57 + .../applications/data.py | 169 + .../contracts/requests/buddy.py | 135 + .../contracts/requests/general.py | 117 + .../contracts/requests/profile.py | 262 + .../contracts/responses/buddy.py | 80 + .../contracts/responses/general.py | 61 + .../contracts/responses/profile.py | 99 + .../contracts/results/buddy.py | 30 + .../contracts/results/general.py | 19 + .../contracts/results/profile.py | 40 + .../enums/general.py | 342 + .../handlers/buddy.py | 122 + .../handlers/general.py | 74 + .../handlers/profile.py | 88 + .../handlers/switcher.py | 59 + .../abstractions/contracts.py | 48 + .../abstractions/handler.py | 16 + .../applications/client.py | 8 + .../applications/data.py | 260 + .../contracts/__init__.py | 0 .../contracts/requests.py | 293 + .../contracts/responses.py | 178 + .../contracts/results.py | 96 + .../enums/error_codes.py | 119 + .../presence_search_player/enums/general.py | 8 + .../exceptions/general.py | 433 ++ .../handlers/handlers.py | 102 + .../handlers/switcher.py | 59 + .../aggregates/game_server_info.py | 25 + .../query_report/aggregates/natneg_cookie.py | 13 + .../query_report/aggregates/peer_room_info.py | 101 + .../query_report/applications/client.py | 15 + src/servers/query_report/applications/data.py | 31 + .../query_report/exceptions/exceptions.py | 5 + src/servers/query_report/v1/__init__.py | 0 .../query_report/v1/abstractions/__init__.py | 0 .../query_report/v1/abstractions/contracts.py | 29 + .../query_report/v1/abstractions/handlers.py | 10 + .../v1/aggregations/game_server_info_v1.py | 18 + .../v2/abstractions/cmd_handler_base.py | 11 + .../v2/abstractions/request_base.py | 21 + .../v2/abstractions/response_base.py | 9 + .../v2/abstractions/result_base.py | 7 + .../v2/aggregates/game_server_info_v2.py | 46 + .../v2/aggregates/natneg_cookie.py | 12 + .../query_report/v2/applications/data.py | 1 + .../query_report/v2/applications/switcher.py | 55 + .../query_report/v2/contracts/requests.py | 184 + .../query_report/v2/contracts/responses.py | 91 + .../query_report/v2/contracts/results.py | 23 + src/servers/query_report/v2/enums/general.py | 63 + .../query_report/v2/handlers/handlers.py | 82 + .../server_browser/exceptions/general.py | 6 + src/servers/server_browser/v2/__init__.py | 0 .../v2/abstractions/__init__.py | 0 .../v2/abstractions/contracts.py | 132 + .../v2/abstractions/handlers.py | 54 + .../v2/aggregations/encryption.py | 134 + .../v2/aggregations/server_info_builder.py | 85 + .../v2/aggregations/string_flags.py | 3 + .../v2/applications/__init__.py | 0 .../server_browser/v2/applications/client.py | 17 + .../server_browser/v2/contracts/__init__.py | 0 .../server_browser/v2/contracts/requests.py | 61 + .../server_browser/v2/contracts/responses.py | 128 + .../server_browser/v2/contracts/results.py | 18 + .../server_browser/v2/enums/general.py | 70 + .../server_browser/v2/handlers/handlers.py | 124 + .../webservices/abstractions/contracts.py | 43 + .../webservices/abstractions/handler.py | 12 + .../webservices/aggregations/soap_envelop.py | 51 + .../webservices/applications/client.py | 27 + src/servers/webservices/exceptions/general.py | 6 + src/servers/webservices/modules/__init__.py | 0 .../webservices/modules/altas/___init__.py | 0 .../webservices/modules/auth/___init__.py | 0 .../modules/auth/abstractions/___init__.py | 0 .../modules/auth/abstractions/contracts.py | 104 + .../modules/auth/contracts/requests.py | 118 + .../modules/auth/contracts/responses.py | 72 + .../modules/auth/contracts/results.py | 18 + .../modules/auth/exceptions/general.py | 5 + .../modules/auth/handlers/general.py | 80 + .../modules/direct2game/__init__.py | 0 .../direct2game/abstractions/__init__.py | 0 .../direct2game/abstractions/contracts.py | 17 + .../direct2game/abstractions/handler.py | 0 .../modules/direct2game/contracts/__init__.py | 0 .../modules/direct2game/contracts/requests.py | 55 + .../direct2game/contracts/responses.py | 32 + .../modules/direct2game/contracts/results.py | 19 + .../modules/in_game_ad/__init__.py | 0 .../modules/patching_and_tracking/__init__.py | 0 .../webservices/modules/racing/__init__.py | 0 .../webservices/modules/sake/__init__.py | 0 src/servers/webservices/tests/auth.py | 0 src/servers/webservices/tests/direct2game.py | 0 src/servers/webservices/tests/sake.py | 31 + src/test/Anno1701.cs | 102 - src/test/TestClasses.cs | 16 - src/test/UniSpy.Server.Test.csproj | 38 - src/tests/__init__.py | 0 src/tests/library/__init__.py | 0 src/tests/library/encrypt_test.py | 19 + 1147 files changed, 12096 insertions(+), 48045 deletions(-) create mode 100644 .DS_Store delete mode 100644 .devcontainer/Dockerfile delete mode 100644 .devcontainer/devcontainer.json delete mode 100644 .dockerignore delete mode 100644 .gitattributes delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/custom.md delete mode 100644 .github/labeler.yml delete mode 100644 .github/workflows/cd-cdkey.yml delete mode 100644 .github/workflows/cd-chat.yml delete mode 100644 .github/workflows/cd-gs.yml delete mode 100644 .github/workflows/cd-gtr.yml delete mode 100644 .github/workflows/cd-nn.yml delete mode 100644 .github/workflows/cd-pcm.yml delete mode 100644 .github/workflows/cd-psp.yml delete mode 100644 .github/workflows/cd-qr.yml delete mode 100644 .github/workflows/cd-sb.yml delete mode 100644 .github/workflows/cd-ws.yml delete mode 100644 .github/workflows/ci.yml delete mode 100644 .github/workflows/labeler.yml delete mode 100644 .vscode/launch.json delete mode 100644 .vscode/tasks.json delete mode 100644 BuildUniSpyServer.bat delete mode 100755 BuildUniSpyServer.sh delete mode 100644 UniSpy.Server.sln delete mode 100644 common/DatabaseModelGenerator/GenerateDatabaseModelLinux.sh delete mode 100644 common/DatabaseModelGenerator/GenerateDatabaseModelWindows.bat delete mode 100644 common/EFScript.txt delete mode 100644 common/Icon/UniSpy_Logo.ico delete mode 100644 common/UniSpy.sql delete mode 100644 common/UniSpyServerConfig.json delete mode 100644 common/UniSpy_pg.sql delete mode 100644 common/docker-compose.build.yml create mode 100644 config.json create mode 100644 src/.DS_Store create mode 100644 src/.devcontainer/Dockerfile create mode 100644 src/.devcontainer/devcontainer.json create mode 100644 src/.vscode/launch.json delete mode 100644 src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs delete mode 100644 src/Libraries/Core/src/Abstraction/BaseClass/ClientInfoBase.cs delete mode 100644 src/Libraries/Core/src/Abstraction/BaseClass/ClientManagerBase.cs delete mode 100755 src/Libraries/Core/src/Abstraction/BaseClass/CmdHandlerBase.cs delete mode 100755 src/Libraries/Core/src/Abstraction/BaseClass/CmdSwitcherBase.cs delete mode 100644 src/Libraries/Core/src/Abstraction/BaseClass/EncParamBase.cs delete mode 100755 src/Libraries/Core/src/Abstraction/BaseClass/RedisChannel.cs delete mode 100644 src/Libraries/Core/src/Abstraction/BaseClass/RedisClient.cs delete mode 100644 src/Libraries/Core/src/Abstraction/BaseClass/RedisKeyValueObject.cs delete mode 100755 src/Libraries/Core/src/Abstraction/BaseClass/RequestBase.cs delete mode 100755 src/Libraries/Core/src/Abstraction/BaseClass/ResponseBase.cs delete mode 100755 src/Libraries/Core/src/Abstraction/BaseClass/ResultBase.cs delete mode 100644 src/Libraries/Core/src/Abstraction/BaseClass/ServerBase.cs delete mode 100755 src/Libraries/Core/src/Abstraction/BaseClass/ServerLauncherBase.cs delete mode 100644 src/Libraries/Core/src/Abstraction/Interface/IClient.cs delete mode 100644 src/Libraries/Core/src/Abstraction/Interface/IConnection.cs delete mode 100644 src/Libraries/Core/src/Abstraction/Interface/IEncryption.cs delete mode 100755 src/Libraries/Core/src/Abstraction/Interface/IHandler.cs delete mode 100644 src/Libraries/Core/src/Abstraction/Interface/IHttpRequest.cs delete mode 100644 src/Libraries/Core/src/Abstraction/Interface/ILogger.cs delete mode 100755 src/Libraries/Core/src/Abstraction/Interface/IRedisChannelEvent.cs delete mode 100755 src/Libraries/Core/src/Abstraction/Interface/IRequest.cs delete mode 100644 src/Libraries/Core/src/Abstraction/Interface/IResponse.cs delete mode 100755 src/Libraries/Core/src/Abstraction/Interface/IServer.cs delete mode 100644 src/Libraries/Core/src/Abstraction/Interface/ISwicher.cs delete mode 100755 src/Libraries/Core/src/Config/ConfigManager.cs delete mode 100755 src/Libraries/Core/src/Config/UniSpyConfig.cs delete mode 100644 src/Libraries/Core/src/Database/DatabaseModel/Addrequest.cs delete mode 100644 src/Libraries/Core/src/Database/DatabaseModel/Blocked.cs delete mode 100644 src/Libraries/Core/src/Database/DatabaseModel/Friend.cs delete mode 100644 src/Libraries/Core/src/Database/DatabaseModel/Game.cs delete mode 100644 src/Libraries/Core/src/Database/DatabaseModel/Grouplist.cs delete mode 100644 src/Libraries/Core/src/Database/DatabaseModel/Message.cs delete mode 100644 src/Libraries/Core/src/Database/DatabaseModel/Partner.cs delete mode 100644 src/Libraries/Core/src/Database/DatabaseModel/Profile.cs delete mode 100644 src/Libraries/Core/src/Database/DatabaseModel/Pstorage.cs delete mode 100644 src/Libraries/Core/src/Database/DatabaseModel/Sakestorage.cs delete mode 100644 src/Libraries/Core/src/Database/DatabaseModel/Subprofile.cs delete mode 100644 src/Libraries/Core/src/Database/DatabaseModel/UnispyContext.cs delete mode 100644 src/Libraries/Core/src/Database/DatabaseModel/User.cs delete mode 100755 src/Libraries/Core/src/Database/DatabaseType.cs delete mode 100755 src/Libraries/Core/src/Encryption/Crc16.cs delete mode 100644 src/Libraries/Core/src/Encryption/GSEncryption.cs delete mode 100755 src/Libraries/Core/src/Encryption/UniSpyEncoding.cs delete mode 100755 src/Libraries/Core/src/Encryption/XorEncoding.cs delete mode 100755 src/Libraries/Core/src/Exception/Exception.cs delete mode 100755 src/Libraries/Core/src/Extension/DataOperationExtensions.cs delete mode 100644 src/Libraries/Core/src/Extension/EasyTimer.cs delete mode 100755 src/Libraries/Core/src/Extension/Redis/RedisChannelName.cs delete mode 100755 src/Libraries/Core/src/Extension/Redis/RedisDbNumber.cs delete mode 100755 src/Libraries/Core/src/Extension/StringExtensions.cs delete mode 100755 src/Libraries/Core/src/Logging/LogWriter.cs delete mode 100755 src/Libraries/Core/src/Misc/GameSpyRandom.cs delete mode 100755 src/Libraries/Core/src/Misc/GameSpyUtils.cs delete mode 100755 src/Libraries/Core/src/Misc/PasswordEncoder.cs delete mode 100755 src/Libraries/Core/src/Misc/RequestChecker.cs delete mode 100755 src/Libraries/Core/src/Misc/UniSpyJsonConverter.cs delete mode 100644 src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs delete mode 100644 src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs delete mode 100644 src/Libraries/Core/src/Network/Http/Server/HttpRequest.cs delete mode 100644 src/Libraries/Core/src/Network/NetworkEvents.cs delete mode 100644 src/Libraries/Core/src/Network/RemoteObject.cs delete mode 100644 src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs delete mode 100644 src/Libraries/Core/src/Network/Tcp/Server/TcpConnectionManager.cs delete mode 100644 src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs delete mode 100644 src/Libraries/Core/src/Network/Udp/Server/UdpConnectionManager.cs delete mode 100755 src/Libraries/Core/src/UniSpy.Server.Core.csproj delete mode 100644 src/Libraries/LinqToRedis/.gitignore delete mode 100644 src/Libraries/LinqToRedis/LinqToRedis.sln delete mode 100644 src/Libraries/LinqToRedis/README.md delete mode 100644 src/Libraries/LinqToRedis/src/Core/IRedisKey.cs delete mode 100644 src/Libraries/LinqToRedis/src/Core/RedisClient.cs delete mode 100644 src/Libraries/LinqToRedis/src/Core/RedisKeyAttribute.cs delete mode 100644 src/Libraries/LinqToRedis/src/Core/RedisKeyValueObject.cs delete mode 100644 src/Libraries/LinqToRedis/src/Core/RedisLock.cs delete mode 100644 src/Libraries/LinqToRedis/src/Linq/QueryEvaluator.cs delete mode 100644 src/Libraries/LinqToRedis/src/Linq/QueryProviderBase.cs delete mode 100644 src/Libraries/LinqToRedis/src/Linq/QueryableObject.cs delete mode 100644 src/Libraries/LinqToRedis/src/Linq/RedisQueryBuilder.cs delete mode 100644 src/Libraries/LinqToRedis/src/Linq/RedisQueryProvider.cs delete mode 100644 src/Libraries/LinqToRedis/src/UniSpy.LinqToRedis.csproj delete mode 100644 src/Libraries/LinqToRedis/test/LinqToRedisTest.cs delete mode 100644 src/Libraries/LinqToRedis/test/NatNeg/RedisClient.cs delete mode 100644 src/Libraries/LinqToRedis/test/NatNeg/UserInfo.cs delete mode 100644 src/Libraries/LinqToRedis/test/UniSpy.LinqToRedis.Test.csproj rename .github/README.MD => src/README (71%) delete mode 100755 src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs delete mode 100755 src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelRequestBase.cs delete mode 100755 src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelResponseBase.cs delete mode 100755 src/Servers/Chat/src/Abstraction/BaseClass/CmdHandlerBase.cs delete mode 100755 src/Servers/Chat/src/Abstraction/BaseClass/General/LogedInHandlerBase.cs delete mode 100755 src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageHandlerBase.cs delete mode 100644 src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageRequestBase.cs delete mode 100755 src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageResultBase.cs delete mode 100755 src/Servers/Chat/src/Abstraction/BaseClass/RequestBase.cs delete mode 100755 src/Servers/Chat/src/Abstraction/BaseClass/ResponseBase.cs delete mode 100755 src/Servers/Chat/src/Abstraction/BaseClass/ResultBase.cs delete mode 100644 src/Servers/Chat/src/Abstraction/Interface/IChannel.cs delete mode 100644 src/Servers/Chat/src/Abstraction/Interface/IShareClient.cs delete mode 100644 src/Servers/Chat/src/Abstraction/Interface/IStorageOperation.cs delete mode 100644 src/Servers/Chat/src/Aggregate/ChannelManager.cs delete mode 100755 src/Servers/Chat/src/Aggregate/ChannelMode.cs delete mode 100755 src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelGeneral.cs delete mode 100644 src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelManage.cs delete mode 100644 src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelModeRelated.cs delete mode 100644 src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs delete mode 100644 src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelValidation.cs delete mode 100755 src/Servers/Chat/src/Aggregate/ChannelUser.cs delete mode 100644 src/Servers/Chat/src/Aggregate/KeyValueManager.cs delete mode 100755 src/Servers/Chat/src/Aggregate/Misc/ChatConstants.cs delete mode 100755 src/Servers/Chat/src/Aggregate/Misc/ChatCrypt.cs delete mode 100755 src/Servers/Chat/src/Aggregate/Misc/IRCErrorCode.cs delete mode 100755 src/Servers/Chat/src/Aggregate/Misc/IRCReplyBuilder.cs delete mode 100755 src/Servers/Chat/src/Aggregate/Misc/PeerChatCTX.cs delete mode 100755 src/Servers/Chat/src/Aggregate/Misc/ResponseName.cs delete mode 100644 src/Servers/Chat/src/Aggregate/Redis/ChannelCache.cs delete mode 100644 src/Servers/Chat/src/Aggregate/Redis/ClientInfoCache.cs delete mode 100644 src/Servers/Chat/src/Aggregate/Redis/Contract/RemoteMessage.cs delete mode 100644 src/Servers/Chat/src/Aggregate/Redis/PeerRoom.cs delete mode 100644 src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs delete mode 100644 src/Servers/Chat/src/Aggregate/RemoteClient.cs delete mode 100644 src/Servers/Chat/src/Application/Client.cs delete mode 100755 src/Servers/Chat/src/Application/ClientInfo.cs delete mode 100644 src/Servers/Chat/src/Application/ClientManager.cs delete mode 100755 src/Servers/Chat/src/Application/Program.cs delete mode 100644 src/Servers/Chat/src/Application/Server.cs delete mode 100644 src/Servers/Chat/src/Application/ServerLauncher.cs delete mode 100644 src/Servers/Chat/src/Application/StorageOperation.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/Channel/GetCKeyRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/Channel/GetChannelKeyRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/Channel/JoinRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/Channel/KickRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/Channel/ModeRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/Channel/PartRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/Channel/SetCKeyRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/Channel/SetChannelKeyRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/Channel/TopicRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/CdKeyRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/CryptRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/GetKeyRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/GetUdpRelayRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/InviteRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/ListLimitRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/ListRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/LoginPreAuthRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/LoginRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/NamesRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/NickRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/PingRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/PongRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/QuitRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/RegisterNickRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/SetGroupRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/SetKeyRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/UserIPRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/UserRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/WhoIsRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Request/General/WhoRequest.cs delete mode 100644 src/Servers/Chat/src/Contract/Request/Message/AtmRequest.cs delete mode 100644 src/Servers/Chat/src/Contract/Request/Message/NoticeRequest.cs delete mode 100644 src/Servers/Chat/src/Contract/Request/Message/PrivateRequest.cs delete mode 100644 src/Servers/Chat/src/Contract/Request/Message/UtmRequest.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/Channel/GetCKeyResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/Channel/GetChannelKeyResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/Channel/JoinResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/Channel/KickResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/Channel/ModeResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/Channel/NamesResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/Channel/PartResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/Channel/SetCKeyResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/Channel/SetChannelKeyResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/Channel/TopicResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/General/CdKeyResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/General/CryptResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/General/GetKeyResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/General/ListResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/General/LoginResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/General/NickResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/General/PingResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/General/UserIPResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/General/WhoIsResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/General/WhoResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/Message/AtmResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/Message/NoticeResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/Message/PrivateResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Response/Message/UtmResponse.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/Channel/GetCKeyResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/Channel/GetChannelKeyResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/Channel/JoinResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/Channel/KickResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/Channel/ModeResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/Channel/NamesResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/Channel/PartResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/Channel/SetChannelKeyResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/Channel/TopicResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/General/CryptResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/General/GetKeyResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/General/ListResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/General/LoginResult.cs delete mode 100644 src/Servers/Chat/src/Contract/Result/General/NickResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/General/PingResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/General/QuitResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/General/UserIPResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/General/WhoIsResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/General/WhoResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/Message/AtmResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/Message/NoticeResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/Message/PrivateResult.cs delete mode 100755 src/Servers/Chat/src/Contract/Result/Message/UtmResult.cs delete mode 100755 src/Servers/Chat/src/Dockerfile delete mode 100755 src/Servers/Chat/src/Enumerate/ChatChannelMode.cs delete mode 100755 src/Servers/Chat/src/Enumerate/ChatMode.cs delete mode 100755 src/Servers/Chat/src/Enumerate/Request/ChatRequest.cs delete mode 100755 src/Servers/Chat/src/Enumerate/Request/ChatServerMessage.cs delete mode 100755 src/Servers/Chat/src/Enumerate/Response/ChatErrorCode.cs delete mode 100755 src/Servers/Chat/src/Exception/Exception.cs delete mode 100755 src/Servers/Chat/src/Exception/IRC/Channel/BadChanMaskException.cs delete mode 100755 src/Servers/Chat/src/Exception/IRC/Channel/BadChannelKeyException.cs delete mode 100755 src/Servers/Chat/src/Exception/IRC/Channel/BannedFromChanException.cs delete mode 100755 src/Servers/Chat/src/Exception/IRC/Channel/ChannelIsFullException.cs delete mode 100755 src/Servers/Chat/src/Exception/IRC/Channel/InviteOnlyChanException.cs delete mode 100755 src/Servers/Chat/src/Exception/IRC/Channel/NoSuchChannelException.cs delete mode 100755 src/Servers/Chat/src/Exception/IRC/General/ErrOneUSNickNameException.cs delete mode 100755 src/Servers/Chat/src/Exception/IRC/General/LoginFailedException.cs delete mode 100755 src/Servers/Chat/src/Exception/IRC/General/MoreParametersException.cs delete mode 100755 src/Servers/Chat/src/Exception/IRC/General/NickNameInUseException.cs delete mode 100755 src/Servers/Chat/src/Exception/IRC/General/NoSuchNickException.cs delete mode 100755 src/Servers/Chat/src/Exception/IRC/General/NoUniqueNickException.cs delete mode 100755 src/Servers/Chat/src/Exception/IRC/General/RegisterNickFaildException.cs delete mode 100755 src/Servers/Chat/src/Exception/IRC/General/TooManyChannels.cs delete mode 100755 src/Servers/Chat/src/Exception/IRC/General/UniqueNickExpiredException.cs delete mode 100755 src/Servers/Chat/src/Exception/IRCChannelException.cs delete mode 100755 src/Servers/Chat/src/Exception/IRCException.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/Channel/GetCKeyHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/Channel/GetChannelKeyHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/Channel/KickHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/Channel/ModeHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/Channel/NamesHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/Channel/SetCKeyHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/Channel/SetChannelKeyHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/Channel/TopicHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/General/CdKeyHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/General/CryptHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/General/GetKeyHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/General/GetUdpRelayHandler.cs delete mode 100644 src/Servers/Chat/src/Handler/CmdHandler/General/InviteHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/General/ListHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/General/LoginHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/General/PingHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/General/QuitHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/General/SetKeyHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/General/UserHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/General/UserIPHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/General/WhoIsHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/Message/AtmHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/Message/NoticeHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdHandler/Message/UtmHandler.cs delete mode 100755 src/Servers/Chat/src/Handler/CmdSwitcher.cs delete mode 100755 src/Servers/Chat/src/UniSpy.Server.Chat.csproj delete mode 100644 src/Servers/Chat/test/Channel/ChannelHandlerTest.cs delete mode 100644 src/Servers/Chat/test/Channel/ChannelRequestTest.cs delete mode 100644 src/Servers/Chat/test/Channel/ChannelRequests.cs delete mode 100644 src/Servers/Chat/test/Game/GameTest.cs delete mode 100644 src/Servers/Chat/test/General/GeneralRequestTest.cs delete mode 100644 src/Servers/Chat/test/General/GeneralRequests.cs delete mode 100644 src/Servers/Chat/test/Message/MessageRequestTest.cs delete mode 100644 src/Servers/Chat/test/Message/MessageRequests.cs delete mode 100644 src/Servers/Chat/test/MockObject.cs delete mode 100644 src/Servers/Chat/test/RedisChatChannelTest.cs delete mode 100644 src/Servers/Chat/test/UniSpy.Server.Chat.Test.csproj delete mode 100755 src/Servers/GameStatus/src/Abstraction/BaseClass/CmdHandlerBase.cs delete mode 100755 src/Servers/GameStatus/src/Abstraction/BaseClass/RequestBase.cs delete mode 100755 src/Servers/GameStatus/src/Abstraction/BaseClass/ResponseBase.cs delete mode 100755 src/Servers/GameStatus/src/Abstraction/BaseClass/ResultBase.cs delete mode 100644 src/Servers/GameStatus/src/Abstraction/Interface/IStorageOperation.cs delete mode 100755 src/Servers/GameStatus/src/Aggregate/Misc/GSCrypt.cs delete mode 100644 src/Servers/GameStatus/src/Application/Client.cs delete mode 100644 src/Servers/GameStatus/src/Application/ClientInfo.cs delete mode 100755 src/Servers/GameStatus/src/Application/Program.cs delete mode 100644 src/Servers/GameStatus/src/Application/Server.cs delete mode 100644 src/Servers/GameStatus/src/Application/ServerLauncher.cs delete mode 100644 src/Servers/GameStatus/src/Application/StorageOperation.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Request/AuthGameRequest.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Request/AuthPlayerRequest.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Request/GetPlayerDataRequest.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Request/GetProfileIDRequest.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Request/NewGameRequest.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Request/SetPlayerDataRequest.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Request/UpdateGameRequest.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Response/AuthGameResponse.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Response/AuthPlayerResponse.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Response/GetPlayerDataResponse.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Response/GetProfileIDResponse.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Response/SetPlayerDataResponse.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Result/AuthGameResult.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Result/AuthPlayerResult.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Result/GetPlayerDataResult.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Result/GetProfileIDResult.cs delete mode 100755 src/Servers/GameStatus/src/Contract/Result/SetPlayerDataResult.cs delete mode 100755 src/Servers/GameStatus/src/Dockerfile delete mode 100755 src/Servers/GameStatus/src/Enumerate/AuthMethod.cs delete mode 100755 src/Servers/GameStatus/src/Enumerate/GSErrorCode.cs delete mode 100755 src/Servers/GameStatus/src/Enumerate/PersistantStorage.cs delete mode 100755 src/Servers/GameStatus/src/Exception/Exception.cs delete mode 100755 src/Servers/GameStatus/src/Handler/CmdHandler/AuthGameHandler.cs delete mode 100755 src/Servers/GameStatus/src/Handler/CmdHandler/AuthPlayerHandler.cs delete mode 100755 src/Servers/GameStatus/src/Handler/CmdHandler/GetPlayerDataHandler.cs delete mode 100755 src/Servers/GameStatus/src/Handler/CmdHandler/GetProfileIDHandler.cs delete mode 100755 src/Servers/GameStatus/src/Handler/CmdHandler/NewGameHandler.cs delete mode 100755 src/Servers/GameStatus/src/Handler/CmdHandler/SetPlayerDataHandler.cs delete mode 100755 src/Servers/GameStatus/src/Handler/CmdHandler/UpdateGameHandler.cs delete mode 100755 src/Servers/GameStatus/src/Handler/CmdSwitcher.cs delete mode 100755 src/Servers/GameStatus/src/UniSpy.Server.GameStatus.csproj delete mode 100644 src/Servers/GameStatus/test/GameTest.cs delete mode 100644 src/Servers/GameStatus/test/MockObject.cs delete mode 100644 src/Servers/GameStatus/test/RequestTest.cs delete mode 100644 src/Servers/GameStatus/test/UniSpy.Server.GameStatus.Test.csproj delete mode 100755 src/Servers/GameTrafficRelay/src/Application/Program.cs delete mode 100644 src/Servers/GameTrafficRelay/src/Application/Server.cs delete mode 100644 src/Servers/GameTrafficRelay/src/Application/ServerLauncher.cs delete mode 100644 src/Servers/GameTrafficRelay/src/Controllers/NatNegotiationController.cs delete mode 100755 src/Servers/GameTrafficRelay/src/Dockerfile delete mode 100644 src/Servers/GameTrafficRelay/src/UniSpy.Server.GameTrafficRelay.csproj delete mode 100644 src/Servers/GameTrafficRelay/src/appsettings.Development.json delete mode 100644 src/Servers/GameTrafficRelay/src/appsettings.json delete mode 100644 src/Servers/GameTrafficRelay/test/GameTest.cs delete mode 100644 src/Servers/GameTrafficRelay/test/MockObject.cs delete mode 100644 src/Servers/GameTrafficRelay/test/UniSpy.Server.GameTrafficRelay.Test.csproj delete mode 100755 src/Servers/NatNegotiation/src/Abstraction/BaseClass/CmdHandlerBase.cs delete mode 100644 src/Servers/NatNegotiation/src/Abstraction/BaseClass/CommonRequestBase.cs delete mode 100644 src/Servers/NatNegotiation/src/Abstraction/BaseClass/CommonResponseBase.cs delete mode 100644 src/Servers/NatNegotiation/src/Abstraction/BaseClass/CommonResultBase.cs delete mode 100755 src/Servers/NatNegotiation/src/Abstraction/BaseClass/RequestBase.cs delete mode 100755 src/Servers/NatNegotiation/src/Abstraction/BaseClass/ResponseBase.cs delete mode 100755 src/Servers/NatNegotiation/src/Abstraction/BaseClass/ResultBase.cs delete mode 100644 src/Servers/NatNegotiation/src/Abstraction/Interface/IStorageOperation.cs delete mode 100644 src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs delete mode 100644 src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/NatNegotiation.cs delete mode 100644 src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/NetworkUtils.cs delete mode 100644 src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelayServerInfo.cs delete mode 100644 src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelaySwitcher.cs delete mode 100644 src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ServerStatusReporter.cs delete mode 100644 src/Servers/NatNegotiation/src/Aggregate/Misc/NatProperty.cs delete mode 100644 src/Servers/NatNegotiation/src/Aggregate/Misc/NatReportInfo.cs delete mode 100644 src/Servers/NatNegotiation/src/Aggregate/Redis/InitPacketCache.cs delete mode 100644 src/Servers/NatNegotiation/src/Aggregate/Redis/NatFailInfoCache.cs delete mode 100644 src/Servers/NatNegotiation/src/Application/Client.cs delete mode 100644 src/Servers/NatNegotiation/src/Application/ClientInfo.cs delete mode 100755 src/Servers/NatNegotiation/src/Application/Program.cs delete mode 100644 src/Servers/NatNegotiation/src/Application/Server.cs delete mode 100644 src/Servers/NatNegotiation/src/Application/ServerLauncher.cs delete mode 100644 src/Servers/NatNegotiation/src/Application/StorageOperation.cs delete mode 100755 src/Servers/NatNegotiation/src/Contract/Request/AddressCheckRequest.cs delete mode 100644 src/Servers/NatNegotiation/src/Contract/Request/ConnectAckRequest.cs delete mode 100755 src/Servers/NatNegotiation/src/Contract/Request/ConnectRequest.cs delete mode 100755 src/Servers/NatNegotiation/src/Contract/Request/ErtAckRequest.cs delete mode 100755 src/Servers/NatNegotiation/src/Contract/Request/InitRequest.cs delete mode 100755 src/Servers/NatNegotiation/src/Contract/Request/NatifyRequest.cs delete mode 100644 src/Servers/NatNegotiation/src/Contract/Request/PingRequest.cs delete mode 100755 src/Servers/NatNegotiation/src/Contract/Request/PreInitRequest.cs delete mode 100755 src/Servers/NatNegotiation/src/Contract/Request/ReportRequest.cs delete mode 100755 src/Servers/NatNegotiation/src/Contract/Response/ConnectResponse.cs delete mode 100755 src/Servers/NatNegotiation/src/Contract/Response/InitResponse.cs delete mode 100644 src/Servers/NatNegotiation/src/Contract/Response/PingResponse.cs delete mode 100644 src/Servers/NatNegotiation/src/Contract/Response/PreInitResponse.cs delete mode 100755 src/Servers/NatNegotiation/src/Contract/Response/ReportResponse.cs delete mode 100755 src/Servers/NatNegotiation/src/Contract/Result/AddressCheckResult.cs delete mode 100755 src/Servers/NatNegotiation/src/Contract/Result/ConnectResult.cs delete mode 100755 src/Servers/NatNegotiation/src/Contract/Result/ErtAckResult.cs delete mode 100755 src/Servers/NatNegotiation/src/Contract/Result/InitResult.cs delete mode 100755 src/Servers/NatNegotiation/src/Contract/Result/NatifyResult.cs delete mode 100644 src/Servers/NatNegotiation/src/Contract/Result/PreInitResult.cs delete mode 100755 src/Servers/NatNegotiation/src/Contract/Result/ReportResult.cs delete mode 100755 src/Servers/NatNegotiation/src/Dockerfile delete mode 100644 src/Servers/NatNegotiation/src/Enumerate/ConnectPacketStatus.cs delete mode 100644 src/Servers/NatNegotiation/src/Enumerate/NatMappingType.cs delete mode 100644 src/Servers/NatNegotiation/src/Enumerate/NatNegResult.cs delete mode 100644 src/Servers/NatNegotiation/src/Enumerate/NatPacketType.cs delete mode 100644 src/Servers/NatNegotiation/src/Enumerate/NatPortType.cs delete mode 100644 src/Servers/NatNegotiation/src/Enumerate/NatPunchStrategy.cs delete mode 100644 src/Servers/NatNegotiation/src/Enumerate/NatStrategyType.cs delete mode 100644 src/Servers/NatNegotiation/src/Enumerate/NatifyPacketType.cs delete mode 100755 src/Servers/NatNegotiation/src/Exception/NNException.cs delete mode 100755 src/Servers/NatNegotiation/src/Handler/CmdHandler/AddressCheckHandler.cs delete mode 100644 src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectAckHandler.cs delete mode 100755 src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs delete mode 100755 src/Servers/NatNegotiation/src/Handler/CmdHandler/ErtAckHandler.cs delete mode 100755 src/Servers/NatNegotiation/src/Handler/CmdHandler/InitHandler.cs delete mode 100755 src/Servers/NatNegotiation/src/Handler/CmdHandler/NatifyHandler.cs delete mode 100644 src/Servers/NatNegotiation/src/Handler/CmdHandler/PingHandler.cs delete mode 100755 src/Servers/NatNegotiation/src/Handler/CmdHandler/ReportHandler.cs delete mode 100755 src/Servers/NatNegotiation/src/Handler/CmdSwitcher.cs delete mode 100755 src/Servers/NatNegotiation/src/UniSpy.Server.NatNegotiation.csproj delete mode 100644 src/Servers/NatNegotiation/test/GameTest.cs delete mode 100644 src/Servers/NatNegotiation/test/HandlerTest.cs delete mode 100644 src/Servers/NatNegotiation/test/MockObject.cs delete mode 100644 src/Servers/NatNegotiation/test/NatDetectionTest.cs delete mode 100644 src/Servers/NatNegotiation/test/RequestTest.cs delete mode 100644 src/Servers/NatNegotiation/test/UniSpy.Server.NatNegotiation.Test.csproj delete mode 100755 src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/CmdHandlerBase.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/LoggedInCmdHandlerBase.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/RequestBase.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/ResponseBase.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/ResultBase.cs delete mode 100644 src/Servers/PresenceConnectionManager/src/Abstraction/Interface/IShareClient.cs delete mode 100644 src/Servers/PresenceConnectionManager/src/Abstraction/Interface/IStorageOperation.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Aggregate/Misc/LoginChallengeProof.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Aggregate/Misc/SDKRevision.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Aggregate/Misc/Status/UserStatus.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Aggregate/Misc/Status/UserStatusInfo.cs delete mode 100644 src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoChannel.cs delete mode 100644 src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoMessage.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoRedisKey.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoRedisOperator.cs delete mode 100644 src/Servers/PresenceConnectionManager/src/Aggregate/RemoteClient.cs delete mode 100644 src/Servers/PresenceConnectionManager/src/Application/Client.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs delete mode 100644 src/Servers/PresenceConnectionManager/src/Application/ClientManager.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Application/Program.cs delete mode 100644 src/Servers/PresenceConnectionManager/src/Application/Server.cs delete mode 100644 src/Servers/PresenceConnectionManager/src/Application/ServerLauncher.cs delete mode 100644 src/Servers/PresenceConnectionManager/src/Application/StorageOperation.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/AddBuddyRequest.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/DelBuddyRequest.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/InviteToRequest.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/StatusInfoRequest.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/StatusRequest.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Request/General/KeepAliveRequest.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Request/General/LoginRequest.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Request/General/LogoutRequest.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/AddBlockRequest.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/GetProfileRequest.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/NewProfileRequest.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/RegisterCDKeyRequest.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/RegisterNickRequest.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/UpdateProfileRequest.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/UpdateUIRequest.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Response/Buddy/AddBuddyResponse.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Response/Buddy/BlockListResponse.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Response/Buddy/BuddyListResponse.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Response/Buddy/StatusInfoResponse.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Response/General/KeepAliveResponse.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Response/General/LoginResponse.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Response/General/NewUserResponse.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Response/Profile/GetProfileResponse.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Response/Profile/NewProfileResponse.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Response/Profile/RegisterCDKeyResponse.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Response/Profile/RegisterNickResponse.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/AddBuddyResult.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/BlockListResult.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/BuddyListResult.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/StatusInfoResult.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/StatusResult.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Result/General/LoginResult.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Result/PCMDefaultResult.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Result/Profile/GetProfileResult.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Contract/Result/Profile/NewProfileResult.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Dockerfile delete mode 100755 src/Servers/PresenceConnectionManager/src/Enumerate/BuddyMessageType.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Enumerate/DisconnectReason.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Enumerate/FireWallType.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Enumerate/GPBasic.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Enumerate/GPStatusCode.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Enumerate/GenderType.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Enumerate/LoginStatus.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Enumerate/LoginType.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Enumerate/PublicMask.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Enumerate/QuietModeType.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Enumerate/SDKRevisionType.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/AddBuddyHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BlockListHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BuddyListHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BuddyStatusInfoHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/DelBuddyHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/InviteToHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/StatusHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/StatusInfoHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/KeepAliveHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LoginHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LogoutHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/NewUserHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/SDKRevisionHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/AddBlockHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/GetProfileHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/NewProfileHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/RegisterCDKeyHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/RegisterNickHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/RemoveBlockHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/UpdateProfileHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/UpdateUserInfoHandler.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/Handler/CmdSwitcher.cs delete mode 100755 src/Servers/PresenceConnectionManager/src/UniSpy.Server.PresenceConnectionManager.csproj delete mode 100644 src/Servers/PresenceConnectionManager/test/Buddy/BuddyRequestTest.cs delete mode 100644 src/Servers/PresenceConnectionManager/test/Buddy/BuddyRequests.cs delete mode 100644 src/Servers/PresenceConnectionManager/test/Game/GameTest.cs delete mode 100644 src/Servers/PresenceConnectionManager/test/General/GeneralRequestTest.cs delete mode 100644 src/Servers/PresenceConnectionManager/test/General/GeneralRequests.cs delete mode 100644 src/Servers/PresenceConnectionManager/test/MokeObject.cs delete mode 100644 src/Servers/PresenceConnectionManager/test/Persist/PersistTest.cs delete mode 100644 src/Servers/PresenceConnectionManager/test/Profile/ProfileRequestTest.cs delete mode 100644 src/Servers/PresenceConnectionManager/test/Profile/ProfileRequests.cs delete mode 100644 src/Servers/PresenceConnectionManager/test/UniSpy.Server.PresenceConnectionManager.Test.csproj delete mode 100755 src/Servers/PresenceSearchPlayer/src/Abstraction/BaseClass/CmdHandlerBase.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Abstraction/BaseClass/RequestBase.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Abstraction/BaseClass/ResponseBase.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Abstraction/BaseClass/ResultBase.cs delete mode 100644 src/Servers/PresenceSearchPlayer/src/Abstraction/Interface/IStorageOperation.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Aggregate/PSPRequestName.cs delete mode 100644 src/Servers/PresenceSearchPlayer/src/Application/Client.cs delete mode 100644 src/Servers/PresenceSearchPlayer/src/Application/ClientInfo.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Application/Program.cs delete mode 100644 src/Servers/PresenceSearchPlayer/src/Application/Server.cs delete mode 100644 src/Servers/PresenceSearchPlayer/src/Application/ServerLauncher.cs delete mode 100644 src/Servers/PresenceSearchPlayer/src/Application/StorageOperation.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Request/CheckRequest.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Request/NewUserRequest.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Request/NicksRequest.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Request/OthersListRequest.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Request/OthersRequest.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Request/SearchRequest.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Request/SearchUniqueRequest.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Request/UniqueSearchRequest.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Request/ValidRequest.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Response/CheckResponse.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Response/NewUserResponse.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Response/NicksResponse.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Response/OthersListResponse.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Response/OthersResponse.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Response/SearchResponse.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Response/SearchUniqueResponse.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Response/UniqueSearchResponse.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Response/ValidResponse.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Result/CheckResult.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Result/NewUserResult.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Result/NicksResult.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Result/OthersListResult.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Result/OthersResult.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Result/PSPDefaultResult.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Result/SearchResult.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Result/SearchUniqueResult.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Result/UniqueSearchResult.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Contract/Result/ValidResult.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Dockerfile delete mode 100755 src/Servers/PresenceSearchPlayer/src/Enumerator/GPEnum.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Enumerator/GPErrorCode.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Enumerator/NewUserStatus.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/AddBuddy/GPAddBuddyAlreadyBuddyException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/AddBuddy/GPAddBuddyBadFormException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/AddBuddy/GPAddBuddyBadNewException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/AddBuddy/GPAddBuddyException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/AuthAdd/GPAuthAddBadFormException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/AuthAdd/GPAuthAddBadSigExceptiion.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/AuthAdd/GPAuthAddException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/BuddyMsg/GPBuddyMsgException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/BuddyMsg/GPBuddyMsgExtInfoNotSupportedException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/BuddyMsg/GPBuddyMsgNotBuddyException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/Check/CheckException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/General/GPBadSessionKeyException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/General/GPConnectionCloseException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/General/GPDatabaseException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/General/GPException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/General/GPForcedDisconnectException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/General/GPNetworkException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/General/GPNotLoggedInException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/General/GPParseException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/General/GPUdpLayerException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadEmailException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadLoginTicketException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadNickException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadPasswordException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadPreAuthException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadProfileException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadUniquenickException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginConnectionFailedException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginProfileDeletedException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginServerAuthFailedException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginTicketExpiredException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginTimeOutException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/NewProfile/GPNewProfileBadNickException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/NewProfile/GPNewProfileBadOldNick.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/NewProfile/GPNewProfileException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserBadNickException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserBadPasswordException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserUniquenickInUseException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserUniquenickInvalidException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/Status/GPStatusException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/UpdatePro/GPUpdateProBadNickException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/UpdatePro/GPUpdateProException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/UpdateUI/GPUpdateUIBadEmailException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Exception/UpdateUI/GPUpdateUIException.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/CheckHandler.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/NewUserHandler.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/NicksHandler.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/OthersHandler.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/OthersListHandler.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/PmatchHandler.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/SearchHandler.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/SearchUniqueHandler.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/UniqueSearchHandler.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/ValidHandler.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/Handler/CmdSwitcher.cs delete mode 100755 src/Servers/PresenceSearchPlayer/src/UniSpy.Server.PresenceSearchPlayer.csproj delete mode 100644 src/Servers/PresenceSearchPlayer/test/GameTest.cs delete mode 100644 src/Servers/PresenceSearchPlayer/test/MokeObject.cs delete mode 100644 src/Servers/PresenceSearchPlayer/test/RawRequests.cs delete mode 100644 src/Servers/PresenceSearchPlayer/test/RequestTests.cs delete mode 100644 src/Servers/PresenceSearchPlayer/test/UniSpy.Server.PresenceSearchPlayer.Test.csproj delete mode 100644 src/Servers/QueryReport/src/Abstraction/Interface/IStorageOperation.cs delete mode 100644 src/Servers/QueryReport/src/Aggregate/Redis/PeerRoomInfo.cs delete mode 100644 src/Servers/QueryReport/src/Application/Client.cs delete mode 100644 src/Servers/QueryReport/src/Application/ClientInfo.cs delete mode 100755 src/Servers/QueryReport/src/Application/Program.cs delete mode 100644 src/Servers/QueryReport/src/Application/Server.cs delete mode 100644 src/Servers/QueryReport/src/Application/ServerLauncher.cs delete mode 100644 src/Servers/QueryReport/src/Application/StorageOperation.cs delete mode 100755 src/Servers/QueryReport/src/Dockerfile delete mode 100755 src/Servers/QueryReport/src/Exception/Exception.cs delete mode 100755 src/Servers/QueryReport/src/UniSpy.Server.QueryReport.csproj delete mode 100644 src/Servers/QueryReport/src/V1/Abstraction/BaseClass/CmdHandlerBase.cs delete mode 100644 src/Servers/QueryReport/src/V1/Abstraction/BaseClass/RequestBase.cs delete mode 100644 src/Servers/QueryReport/src/V1/Abstraction/BaseClass/ResponseBase.cs delete mode 100644 src/Servers/QueryReport/src/V1/Abstraction/BaseClass/ResultBase.cs delete mode 100644 src/Servers/QueryReport/src/V1/Abstraction/Interface/IStorageOperation.cs delete mode 100644 src/Servers/QueryReport/src/V1/Aggregation/Enctype0.cs delete mode 100644 src/Servers/QueryReport/src/V1/Aggregation/Redis/GameServerCache.cs delete mode 100644 src/Servers/QueryReport/src/V1/Application/StorageOperation.cs delete mode 100644 src/Servers/QueryReport/src/V1/Contract/Request/EchoRequest.cs delete mode 100644 src/Servers/QueryReport/src/V1/Contract/Request/HeartbeatRequest.cs delete mode 100644 src/Servers/QueryReport/src/V1/Contract/Request/ValidateRequest.cs delete mode 100644 src/Servers/QueryReport/src/V1/Contract/Response/EchoResponse.cs delete mode 100644 src/Servers/QueryReport/src/V1/Contract/Response/HeartbeatResponse.cs delete mode 100644 src/Servers/QueryReport/src/V1/Handler/CmdHandler/EchoHandler.cs delete mode 100644 src/Servers/QueryReport/src/V1/Handler/CmdHandler/HeartbeatHandler.cs delete mode 100644 src/Servers/QueryReport/src/V1/Handler/CmdHandler/ValidateHandler.cs delete mode 100644 src/Servers/QueryReport/src/V1/Handler/CmdSwitcher.cs delete mode 100755 src/Servers/QueryReport/src/V2/Abstraction/BaseClass/CmdHandlerBase.cs delete mode 100755 src/Servers/QueryReport/src/V2/Abstraction/BaseClass/RequestBase.cs delete mode 100755 src/Servers/QueryReport/src/V2/Abstraction/BaseClass/ResponseBase.cs delete mode 100755 src/Servers/QueryReport/src/V2/Abstraction/BaseClass/ResultBase.cs delete mode 100644 src/Servers/QueryReport/src/V2/Abstraction/Interface/IStorageOperation.cs delete mode 100755 src/Servers/QueryReport/src/V2/Aggregate/Misc/Encryption.cs delete mode 100755 src/Servers/QueryReport/src/V2/Aggregate/Misc/NatNegCookie.cs delete mode 100755 src/Servers/QueryReport/src/V2/Aggregate/Misc/RegionID.cs delete mode 100644 src/Servers/QueryReport/src/V2/Aggregate/Redis/GameServerInfo.cs delete mode 100644 src/Servers/QueryReport/src/V2/Aggregate/Redis/HeartBeatChannel.cs delete mode 100644 src/Servers/QueryReport/src/V2/Aggregate/Redis/NatNegChannel.cs delete mode 100644 src/Servers/QueryReport/src/V2/Application/StorageOperation.cs delete mode 100755 src/Servers/QueryReport/src/V2/Contract/Request/AvaliableRequest.cs delete mode 100755 src/Servers/QueryReport/src/V2/Contract/Request/ChallengeRequest.cs delete mode 100644 src/Servers/QueryReport/src/V2/Contract/Request/ClientMessageAckRequest.cs delete mode 100755 src/Servers/QueryReport/src/V2/Contract/Request/ClientMessageRequest.cs delete mode 100644 src/Servers/QueryReport/src/V2/Contract/Request/EchoRequest.cs delete mode 100755 src/Servers/QueryReport/src/V2/Contract/Request/HeartBeatRequest.cs delete mode 100644 src/Servers/QueryReport/src/V2/Contract/Request/KeepAliveRequest.cs delete mode 100755 src/Servers/QueryReport/src/V2/Contract/Response/AvaliableResponse.cs delete mode 100755 src/Servers/QueryReport/src/V2/Contract/Response/ChallengeResponse.cs delete mode 100755 src/Servers/QueryReport/src/V2/Contract/Response/ClientMessageResponse.cs delete mode 100755 src/Servers/QueryReport/src/V2/Contract/Response/HeartBeatResponse.cs delete mode 100755 src/Servers/QueryReport/src/V2/Contract/Response/KeepAliveResponse.cs delete mode 100755 src/Servers/QueryReport/src/V2/Contract/Result/ChallengeResult.cs delete mode 100755 src/Servers/QueryReport/src/V2/Contract/Result/ClientMessageResult.cs delete mode 100755 src/Servers/QueryReport/src/V2/Contract/Result/EchoResult.cs delete mode 100755 src/Servers/QueryReport/src/V2/Contract/Result/HeartBeatResult.cs delete mode 100755 src/Servers/QueryReport/src/V2/Enumerate/GeneralEnum.cs delete mode 100755 src/Servers/QueryReport/src/V2/Enumerate/HeartBeatEnum.cs delete mode 100755 src/Servers/QueryReport/src/V2/Enumerate/ServerAvailability.cs delete mode 100755 src/Servers/QueryReport/src/V2/Handler/CmdHandler/AvailableHandler.cs delete mode 100755 src/Servers/QueryReport/src/V2/Handler/CmdHandler/ChallengeHandler.cs delete mode 100644 src/Servers/QueryReport/src/V2/Handler/CmdHandler/ClientMessageAckHandler.cs delete mode 100755 src/Servers/QueryReport/src/V2/Handler/CmdHandler/ClientMessageHandler.cs delete mode 100755 src/Servers/QueryReport/src/V2/Handler/CmdHandler/EchoHandler.cs delete mode 100755 src/Servers/QueryReport/src/V2/Handler/CmdHandler/HeartBeatHandler.cs delete mode 100755 src/Servers/QueryReport/src/V2/Handler/CmdHandler/KeepAliveHandler.cs delete mode 100755 src/Servers/QueryReport/src/V2/Handler/CmdSwitcher.cs delete mode 100644 src/Servers/QueryReport/test/UniSpy.Server.QueryReport.Test.csproj delete mode 100644 src/Servers/QueryReport/test/V2/GameTest.cs delete mode 100644 src/Servers/QueryReport/test/V2/MockObject.cs delete mode 100644 src/Servers/QueryReport/test/V2/RequestTest.cs delete mode 100644 src/Servers/ServerBrowser/src/Aggregate/GameServerFilter.cs delete mode 100755 src/Servers/ServerBrowser/src/Application/Program.cs delete mode 100644 src/Servers/ServerBrowser/src/Application/ServerLauncher.cs delete mode 100755 src/Servers/ServerBrowser/src/Dockerfile delete mode 100755 src/Servers/ServerBrowser/src/Exception/Exception.cs delete mode 100755 src/Servers/ServerBrowser/src/UniSpy.Server.ServerBrowser.csproj delete mode 100644 src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/CmdHandlerBase.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/RequestBase.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/ResponseBase.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/ResultBase.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IStorageOperation.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Application/Client.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Application/ClientInfo.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Application/Server.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Contract/Request/GameNameRequest.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Contract/Request/ListRequest.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Contract/Response/GameNameResponse.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Contract/Response/ListResponse.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Contract/Result/ListResult.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/GameNameHandler.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/ListHandler.cs delete mode 100644 src/Servers/ServerBrowser/src/V1/Handler/CmdSwitcher.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/AdHocRequestBase.cs delete mode 100644 src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/AdHocResponseBase.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/CmdHandlerBase.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/RequestBase.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ResponseBase.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ResultBase.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ServerListUpdateOption/ServerListUpdateOptionHandlerBase.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ServerListUpdateOption/ServerListUpdateOptionRequestBase.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ServerListUpdateOption/ServerListUpdateOptionResponseBase.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ServerListUpdateOption/ServerListUpdateOptionResultBase.cs delete mode 100644 src/Servers/ServerBrowser/src/V2/Aggregate/HeartbeatChannel.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Aggregate/Misc/Encryption.cs delete mode 100644 src/Servers/ServerBrowser/src/V2/Aggregate/Misc/GameFilter.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Aggregate/Misc/KeyType.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Aggregate/Misc/SBStringFlag.cs delete mode 100644 src/Servers/ServerBrowser/src/V2/Aggregate/ServerInfoBuilder.cs delete mode 100644 src/Servers/ServerBrowser/src/V2/Application/Client.cs delete mode 100644 src/Servers/ServerBrowser/src/V2/Application/ClientInfo.cs delete mode 100644 src/Servers/ServerBrowser/src/V2/Application/ClientManager.cs delete mode 100644 src/Servers/ServerBrowser/src/V2/Application/Server.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Contract/Request/AdHoc/PlayerSearchRequest.cs delete mode 100644 src/Servers/ServerBrowser/src/V2/Contract/Request/AdHoc/SendMsgRequest.cs delete mode 100644 src/Servers/ServerBrowser/src/V2/Contract/Request/AdHoc/ServerInfoRequest.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Contract/Request/ServerList/ServerListRequest.cs delete mode 100644 src/Servers/ServerBrowser/src/V2/Contract/Response/AdHoc/DeleteServerInfoResponse.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Contract/Response/AdHoc/UpdateServerInfoResponse.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Contract/Response/ServerList/P2PGroupRoomListResponse.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Contract/Response/ServerList/ServerMainListResponse.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Contract/Response/ServerList/ServerNetworkInfoListResponse.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Contract/Result/AdHoc/AdHocResult.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/P2PGroupRoomListResult.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/ServerMainListResult.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/ServerNetworkInfoListResult.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Enumerate/GeneralEnum.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Enumerate/ServerListEnum.cs delete mode 100644 src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/AdHoc/AdHocHandler.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/AdHoc/SendMsgHandler.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/AdHoc/ServerInfoHandler.cs delete mode 100644 src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/ServerList/ServerListHandler.cs delete mode 100755 src/Servers/ServerBrowser/src/V2/Handler/CmdSwitcher.cs delete mode 100644 src/Servers/ServerBrowser/test/UniSpy.Server.ServerBrowser.Test.csproj delete mode 100644 src/Servers/ServerBrowser/test/V1/EnctypeTest.cs delete mode 100644 src/Servers/ServerBrowser/test/V1/RequestTest.cs delete mode 100644 src/Servers/ServerBrowser/test/V2/FilterTest.cs delete mode 100644 src/Servers/ServerBrowser/test/V2/GameTest.cs delete mode 100644 src/Servers/ServerBrowser/test/V2/MockObject.cs delete mode 100644 src/Servers/ServerBrowser/test/V2/PeerRoomTest.cs delete mode 100644 src/Servers/ServerBrowser/test/V2/RequestTest.cs delete mode 100644 src/Servers/WebServer/src/Abstraction/CmdHandlerBase.cs delete mode 100644 src/Servers/WebServer/src/Abstraction/RequestBase.cs delete mode 100644 src/Servers/WebServer/src/Abstraction/ResponseBase.cs delete mode 100644 src/Servers/WebServer/src/Abstraction/ResultBase.cs delete mode 100644 src/Servers/WebServer/src/Abstraction/SoapEnvelopBase.cs delete mode 100644 src/Servers/WebServer/src/Aggregate/SakeArrayObject.cs delete mode 100644 src/Servers/WebServer/src/Aggregate/SoapXElement.cs delete mode 100644 src/Servers/WebServer/src/Aggregate/WebEndpoints.cs delete mode 100644 src/Servers/WebServer/src/Application/Client.cs delete mode 100644 src/Servers/WebServer/src/Application/ClientInfo.cs delete mode 100755 src/Servers/WebServer/src/Application/Program.cs delete mode 100644 src/Servers/WebServer/src/Application/Server.cs delete mode 100644 src/Servers/WebServer/src/Application/ServerLauncher.cs delete mode 100644 src/Servers/WebServer/src/Dockerfile delete mode 100644 src/Servers/WebServer/src/Exception/Exception.cs delete mode 100755 src/Servers/WebServer/src/Handler/CmdSwitcher.cs delete mode 100644 src/Servers/WebServer/src/Module/Atlas/Contract/Request/CreateMatchlessSessionRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Atlas/Contract/Request/CreateSessionRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Atlas/Contract/Request/SetReportIntentionRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Atlas/Contract/Request/SubmitReportRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Abstraction/LoginRequestBase.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResponseBase.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResultBase.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Abstraction/ResponseBase.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/AuthSoapEnvelope.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginProfileRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginProfileWithGameIdRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginPs3CertRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginPs3CertWithGameIdRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginRemoteAuthRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginRemoteAuthWithGameIdRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginUniqueNickRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginUniqueNickWithGameId.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginProfileResponse.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginProfileWithGameIdResponse.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginPs3CertResponse.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginPs3CertWithGameIdResponse.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginRemoteAuthResponse.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginRemoteAuthWithGameIdResponse.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginUniqueNickResponse.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginUniqueNickWithGameIdResponse.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Result/LoginProfileResult.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Result/LoginPs3CertResult.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Result/LoginRemoteAuthResult.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Contract/Result/LoginUniqueNickResult.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Exception/DatabaseException.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Exception/Exception.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Exception/InvalidPasswordException.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Exception/InvalidProfileException.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Exception/ParseException.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Exception/ServerException.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Exception/ServerInitException.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Exception/UniqueNickExpredException.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Exception/UserNotFoundException.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Handler/LoginProfileHandler.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Handler/LoginProfileWithGameIdHandler.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Handler/LoginPs3CertHandler.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Handler/LoginPs3CertWithGameIdHandler.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Handler/LoginRemoteAuthHandler.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Handler/LoginRemoteAuthWithGameIdHandler.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickHandler.cs delete mode 100644 src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs delete mode 100644 src/Servers/WebServer/src/Module/Direct2Game/Abstraction/RequestBase.cs delete mode 100644 src/Servers/WebServer/src/Module/Direct2Game/Abstraction/ResponseBase.cs delete mode 100644 src/Servers/WebServer/src/Module/Direct2Game/Abstraction/ResultBase.cs delete mode 100644 src/Servers/WebServer/src/Module/Direct2Game/Contract/Direct2GameSoapEnvelope.cs delete mode 100644 src/Servers/WebServer/src/Module/Direct2Game/Contract/Request/GetPurchaseHistoryRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Direct2Game/Contract/Request/GetStoreAvailabilityRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetPurchaseHistoryResponse.cs delete mode 100644 src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetStoreAvailabilityResponse.cs delete mode 100644 src/Servers/WebServer/src/Module/Direct2Game/Contract/Result/GetPurchaseHistoryResult.cs delete mode 100644 src/Servers/WebServer/src/Module/Direct2Game/Contract/Result/GetStoreAvailabilityResult.cs delete mode 100644 src/Servers/WebServer/src/Module/Direct2Game/Handler/GetPurchaseHistoryHandler.cs delete mode 100644 src/Servers/WebServer/src/Module/Direct2Game/Handler/GetStoreAvailabilityHandler.cs delete mode 100644 src/Servers/WebServer/src/Module/IngameAd/Contract/Request/GetTargettedAdRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/IngameAd/Contract/Request/ReportAdUsageRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/PatchingAndTracking/Contract/Request/MotdRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/PatchingAndTracking/Contract/Request/VercheckRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Racing/Contract/Request/GetContestDataRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Racing/Contract/Request/GetFriendRankingsRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Racing/Contract/Request/GetRegionalDataRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Racing/Contract/Request/GetTenAboveRankingsRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Racing/Contract/Request/GetTopTenRankingsRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Racing/Contract/Request/SubmitGhostRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Racing/Contract/Request/SubmitScoresRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Abstraction/CmdHandlerBase.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Abstraction/RequestBase.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Abstraction/ResponseBase.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Abstraction/ResultBase.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/Request/CreateRecordRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/Request/DeleteRecordRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/Request/GetMyRecordsRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/Request/GetRandomRecordsRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/Request/GetRecordLimitRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/Request/GetSpecificRecordsRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/Request/RateRecordRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/Request/SearchForRecordsRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/Request/UpdateRecordRequest.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/Response/CreateRecordResponse.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/Response/GetMyRecordResponse.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/Result/CreateRecordResult.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/Result/GetMyRecordsResult.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/Result/SearchForRecordsResult.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Contract/SakeSoapEnvelope.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Handler/CreateRecordHandler.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Handler/GetMyRecordsHandler.cs delete mode 100644 src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs delete mode 100755 src/Servers/WebServer/src/UniSpy.Server.WebServer.csproj delete mode 100644 src/Servers/WebServer/test/Atlas/RawRequests.cs delete mode 100644 src/Servers/WebServer/test/Atlas/RequestsTest.cs delete mode 100644 src/Servers/WebServer/test/Auth/GameTest.cs delete mode 100644 src/Servers/WebServer/test/Auth/HandlerTest.cs delete mode 100644 src/Servers/WebServer/test/Auth/RSATest.cs delete mode 100644 src/Servers/WebServer/test/Auth/RawRequests.cs delete mode 100644 src/Servers/WebServer/test/Auth/RequestsTest.cs delete mode 100644 src/Servers/WebServer/test/GeneralTest.cs delete mode 100644 src/Servers/WebServer/test/MokeObject.cs delete mode 100644 src/Servers/WebServer/test/Racing/RawRequests.cs delete mode 100644 src/Servers/WebServer/test/Racing/RequestsTest.cs delete mode 100644 src/Servers/WebServer/test/Sake/GameTest.cs delete mode 100644 src/Servers/WebServer/test/Sake/HandlerTest.cs delete mode 100644 src/Servers/WebServer/test/Sake/RawRequests.cs delete mode 100644 src/Servers/WebServer/test/Sake/RequestsTest.cs delete mode 100644 src/Servers/WebServer/test/Sake/ResponseTest.cs delete mode 100644 src/Servers/WebServer/test/UniSpy.Server.WebServer.Test.csproj rename src/{Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/Conneciton/Client.cs => backends/__init__.py} (100%) create mode 100644 src/backends/gamespy/library/abstractions/cmd_handler_base.py create mode 100644 src/backends/gamespy/library/abstractions/request_base.py rename src/{Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/Conneciton/Server.cs => backends/gamespy/library/contracts/chat.py} (100%) create mode 100644 src/backends/gamespy/protocols/chat/requests.py create mode 100644 src/backends/gamespy/protocols/natneg/aggregates/init_packet_info.py create mode 100644 src/backends/gamespy/protocols/natneg/applications/data.py create mode 100644 src/backends/gamespy/protocols/natneg/contracts/requests.py create mode 100644 src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py rename src/{Servers/QueryReport/src/V2/Aggregate/Redis/ChannelInfo.cs => backends/gamespy/protocols/presence_search_player/data.py} (100%) create mode 100644 src/backends/gamespy/protocols/presence_search_player/requests.py create mode 100644 src/backends/routers/gamespy/chat.py create mode 100644 src/backends/routers/gamespy/gstats.py create mode 100644 src/backends/routers/gamespy/natneg.py create mode 100644 src/backends/routers/gamespy/presence_connection_manager.py create mode 100644 src/backends/routers/gamespy/presence_search_player.py create mode 100644 src/backends/routers/gamespy/query_report.py create mode 100644 src/backends/routers/gamespy/server_browser.py create mode 100644 src/backends/routers/gamespy/webservices.py create mode 100644 src/backends/urls.py create mode 100644 src/docker-compose.yaml create mode 100644 src/library/__init__.py create mode 100644 src/library/abstractions/__init__.py create mode 100644 src/library/abstractions/brocker.py create mode 100644 src/library/abstractions/client.py create mode 100644 src/library/abstractions/connections.py create mode 100644 src/library/abstractions/contracts.py create mode 100644 src/library/abstractions/enctypt_base.py create mode 100644 src/library/abstractions/handler.py create mode 100644 src/library/abstractions/redis_channel.py create mode 100644 src/library/abstractions/server_launcher_base.py create mode 100644 src/library/abstractions/switcher.py create mode 100644 src/library/database/__init__.py create mode 100644 src/library/database/mongodb_orm.py create mode 100644 src/library/database/pg_orm.py create mode 100644 src/library/database/redis.py create mode 100644 src/library/encryption/__init__.py create mode 100644 src/library/encryption/encoding.py create mode 100644 src/library/encryption/gs_encryption.py create mode 100644 src/library/encryption/xor_encryption.py create mode 100644 src/library/exceptions/__init__.py create mode 100644 src/library/exceptions/error.py create mode 100644 src/library/extentions/__init__.py create mode 100644 src/library/extentions/bytes_extentions.py create mode 100644 src/library/extentions/encoding.py create mode 100644 src/library/extentions/gamespy_ramdoms.py create mode 100644 src/library/extentions/gamespy_utils.py create mode 100644 src/library/extentions/password_encoder.py create mode 100644 src/library/extentions/redis_orm.py create mode 100644 src/library/extentions/string_extentions.py create mode 100644 src/library/log/__init__.py create mode 100644 src/library/log/log_manager.py create mode 100644 src/library/network/__init__.py create mode 100644 src/library/network/http/__init__.py create mode 100644 src/library/network/http/http_handler.py create mode 100644 src/library/network/tcp/__init__.py create mode 100644 src/library/network/tcp/tcp_handler.py create mode 100644 src/library/network/udp/__init__.py create mode 100644 src/library/network/udp/udp_handler.py create mode 100644 src/library/unispy_server_config.py create mode 100644 src/requirement.txt create mode 100644 src/servers/__init__.py create mode 100644 src/servers/chat/abstractions/channel.py create mode 100644 src/servers/chat/abstractions/contract.py create mode 100644 src/servers/chat/abstractions/handler.py create mode 100644 src/servers/chat/abstractions/message.py create mode 100644 src/servers/chat/aggregates/brockers.py create mode 100644 src/servers/chat/aggregates/channel.py create mode 100644 src/servers/chat/aggregates/channel_user.py create mode 100644 src/servers/chat/aggregates/key_value_manager.py create mode 100644 src/servers/chat/aggregates/peer_room.py create mode 100644 src/servers/chat/aggregates/response_name.py create mode 100644 src/servers/chat/aggregates/storage_info.py create mode 100644 src/servers/chat/applications/client.py create mode 100644 src/servers/chat/applications/server_launcher.py create mode 100644 src/servers/chat/applications/switcher.py create mode 100644 src/servers/chat/contracts/__init__.py create mode 100644 src/servers/chat/contracts/requests/__init__.py create mode 100644 src/servers/chat/contracts/requests/channel.py create mode 100644 src/servers/chat/contracts/requests/general.py create mode 100644 src/servers/chat/contracts/requests/message.py create mode 100644 src/servers/chat/contracts/responses/__init__.py create mode 100644 src/servers/chat/contracts/responses/channel.py create mode 100644 src/servers/chat/contracts/responses/general.py create mode 100644 src/servers/chat/contracts/responses/message.py create mode 100644 src/servers/chat/contracts/results/__init__.py create mode 100644 src/servers/chat/contracts/results/channel.py create mode 100644 src/servers/chat/contracts/results/general.py create mode 100644 src/servers/chat/contracts/results/message.py create mode 100644 src/servers/chat/enums/general.py create mode 100644 src/servers/chat/enums/irc_error_code.py create mode 100644 src/servers/chat/enums/peer_room.py create mode 100644 src/servers/chat/exceptions/channel.py create mode 100644 src/servers/chat/exceptions/general.py create mode 100644 src/servers/chat/handlers/channel.py create mode 100644 src/servers/chat/handlers/general.py create mode 100644 src/servers/chat/handlers/message.py create mode 100644 src/servers/game_traffic_relay/applications/connection_listener.py create mode 100644 src/servers/game_traffic_relay/applications/router.py create mode 100644 src/servers/natneg/abstractions/contracts.py create mode 100644 src/servers/natneg/abstractions/handlers.py create mode 100644 src/servers/natneg/aggregations/init_packet_info.py create mode 100644 src/servers/natneg/aggregations/relay_server_info.py create mode 100644 src/servers/natneg/applications/client.py create mode 100644 src/servers/natneg/contracts/requests.py create mode 100644 src/servers/natneg/contracts/responses.py create mode 100644 src/servers/natneg/contracts/results.py create mode 100644 src/servers/natneg/enums/general.py create mode 100644 src/servers/natneg/handlers/handlers.py create mode 100644 src/servers/natneg/handlers/switcher.py create mode 100644 src/servers/natneg/tests/redis.py create mode 100644 src/servers/natneg/tests/tests.py create mode 100644 src/servers/presence_connection_manager/abstractions/contracts.py create mode 100644 src/servers/presence_connection_manager/abstractions/handler.py create mode 100644 src/servers/presence_connection_manager/aggregates/login_challenge.py create mode 100644 src/servers/presence_connection_manager/aggregates/sdk_revision.py create mode 100644 src/servers/presence_connection_manager/aggregates/user_status.py create mode 100644 src/servers/presence_connection_manager/aggregates/user_status_info.py create mode 100644 src/servers/presence_connection_manager/applications/client.py create mode 100644 src/servers/presence_connection_manager/applications/data.py create mode 100644 src/servers/presence_connection_manager/contracts/requests/buddy.py create mode 100644 src/servers/presence_connection_manager/contracts/requests/general.py create mode 100644 src/servers/presence_connection_manager/contracts/requests/profile.py create mode 100644 src/servers/presence_connection_manager/contracts/responses/buddy.py create mode 100644 src/servers/presence_connection_manager/contracts/responses/general.py create mode 100644 src/servers/presence_connection_manager/contracts/responses/profile.py create mode 100644 src/servers/presence_connection_manager/contracts/results/buddy.py create mode 100644 src/servers/presence_connection_manager/contracts/results/general.py create mode 100644 src/servers/presence_connection_manager/contracts/results/profile.py create mode 100644 src/servers/presence_connection_manager/enums/general.py create mode 100644 src/servers/presence_connection_manager/handlers/buddy.py create mode 100644 src/servers/presence_connection_manager/handlers/general.py create mode 100644 src/servers/presence_connection_manager/handlers/profile.py create mode 100644 src/servers/presence_connection_manager/handlers/switcher.py create mode 100644 src/servers/presence_search_player/abstractions/contracts.py create mode 100644 src/servers/presence_search_player/abstractions/handler.py create mode 100644 src/servers/presence_search_player/applications/client.py create mode 100644 src/servers/presence_search_player/applications/data.py create mode 100644 src/servers/presence_search_player/contracts/__init__.py create mode 100644 src/servers/presence_search_player/contracts/requests.py create mode 100644 src/servers/presence_search_player/contracts/responses.py create mode 100644 src/servers/presence_search_player/contracts/results.py create mode 100644 src/servers/presence_search_player/enums/error_codes.py create mode 100644 src/servers/presence_search_player/enums/general.py create mode 100644 src/servers/presence_search_player/exceptions/general.py create mode 100644 src/servers/presence_search_player/handlers/handlers.py create mode 100644 src/servers/presence_search_player/handlers/switcher.py create mode 100644 src/servers/query_report/aggregates/game_server_info.py create mode 100644 src/servers/query_report/aggregates/natneg_cookie.py create mode 100644 src/servers/query_report/aggregates/peer_room_info.py create mode 100644 src/servers/query_report/applications/client.py create mode 100644 src/servers/query_report/applications/data.py create mode 100644 src/servers/query_report/exceptions/exceptions.py create mode 100644 src/servers/query_report/v1/__init__.py create mode 100644 src/servers/query_report/v1/abstractions/__init__.py create mode 100644 src/servers/query_report/v1/abstractions/contracts.py create mode 100644 src/servers/query_report/v1/abstractions/handlers.py create mode 100644 src/servers/query_report/v1/aggregations/game_server_info_v1.py create mode 100644 src/servers/query_report/v2/abstractions/cmd_handler_base.py create mode 100644 src/servers/query_report/v2/abstractions/request_base.py create mode 100644 src/servers/query_report/v2/abstractions/response_base.py create mode 100644 src/servers/query_report/v2/abstractions/result_base.py create mode 100644 src/servers/query_report/v2/aggregates/game_server_info_v2.py create mode 100644 src/servers/query_report/v2/aggregates/natneg_cookie.py create mode 100644 src/servers/query_report/v2/applications/data.py create mode 100644 src/servers/query_report/v2/applications/switcher.py create mode 100644 src/servers/query_report/v2/contracts/requests.py create mode 100644 src/servers/query_report/v2/contracts/responses.py create mode 100644 src/servers/query_report/v2/contracts/results.py create mode 100644 src/servers/query_report/v2/enums/general.py create mode 100644 src/servers/query_report/v2/handlers/handlers.py create mode 100644 src/servers/server_browser/exceptions/general.py create mode 100644 src/servers/server_browser/v2/__init__.py create mode 100644 src/servers/server_browser/v2/abstractions/__init__.py create mode 100644 src/servers/server_browser/v2/abstractions/contracts.py create mode 100644 src/servers/server_browser/v2/abstractions/handlers.py create mode 100644 src/servers/server_browser/v2/aggregations/encryption.py create mode 100644 src/servers/server_browser/v2/aggregations/server_info_builder.py create mode 100644 src/servers/server_browser/v2/aggregations/string_flags.py create mode 100644 src/servers/server_browser/v2/applications/__init__.py create mode 100644 src/servers/server_browser/v2/applications/client.py create mode 100644 src/servers/server_browser/v2/contracts/__init__.py create mode 100644 src/servers/server_browser/v2/contracts/requests.py create mode 100644 src/servers/server_browser/v2/contracts/responses.py create mode 100644 src/servers/server_browser/v2/contracts/results.py create mode 100644 src/servers/server_browser/v2/enums/general.py create mode 100644 src/servers/server_browser/v2/handlers/handlers.py create mode 100644 src/servers/webservices/abstractions/contracts.py create mode 100644 src/servers/webservices/abstractions/handler.py create mode 100644 src/servers/webservices/aggregations/soap_envelop.py create mode 100644 src/servers/webservices/applications/client.py create mode 100644 src/servers/webservices/exceptions/general.py create mode 100644 src/servers/webservices/modules/__init__.py create mode 100644 src/servers/webservices/modules/altas/___init__.py create mode 100644 src/servers/webservices/modules/auth/___init__.py create mode 100644 src/servers/webservices/modules/auth/abstractions/___init__.py create mode 100644 src/servers/webservices/modules/auth/abstractions/contracts.py create mode 100644 src/servers/webservices/modules/auth/contracts/requests.py create mode 100644 src/servers/webservices/modules/auth/contracts/responses.py create mode 100644 src/servers/webservices/modules/auth/contracts/results.py create mode 100644 src/servers/webservices/modules/auth/exceptions/general.py create mode 100644 src/servers/webservices/modules/auth/handlers/general.py create mode 100644 src/servers/webservices/modules/direct2game/__init__.py create mode 100644 src/servers/webservices/modules/direct2game/abstractions/__init__.py create mode 100644 src/servers/webservices/modules/direct2game/abstractions/contracts.py create mode 100644 src/servers/webservices/modules/direct2game/abstractions/handler.py create mode 100644 src/servers/webservices/modules/direct2game/contracts/__init__.py create mode 100644 src/servers/webservices/modules/direct2game/contracts/requests.py create mode 100644 src/servers/webservices/modules/direct2game/contracts/responses.py create mode 100644 src/servers/webservices/modules/direct2game/contracts/results.py create mode 100644 src/servers/webservices/modules/in_game_ad/__init__.py create mode 100644 src/servers/webservices/modules/patching_and_tracking/__init__.py create mode 100644 src/servers/webservices/modules/racing/__init__.py create mode 100644 src/servers/webservices/modules/sake/__init__.py create mode 100644 src/servers/webservices/tests/auth.py create mode 100644 src/servers/webservices/tests/direct2game.py create mode 100644 src/servers/webservices/tests/sake.py delete mode 100644 src/test/Anno1701.cs delete mode 100644 src/test/TestClasses.cs delete mode 100644 src/test/UniSpy.Server.Test.csproj create mode 100644 src/tests/__init__.py create mode 100644 src/tests/library/__init__.py create mode 100644 src/tests/library/encrypt_test.py diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..7af9a8e28a2b3dd413ff6d5cea925321bc28f625 GIT binary patch literal 6148 zcmeHKy>8nu5I)9^OEn1Mp+GWn>88P|oG2Lr2#z~+E1;qs+9Hu6BN3p8fhEUi92a_x z_Ko@keVrc3i{Za6849D}8OM)0@+f>CMA8QUtT#$MfHnYdbcM~A*sL)%u6)I|DAGb? zdW|$0m$ykSla;wPU=^?mY@PyY?`}f^Bgi0!`ThGVxy`e(>U4gytwwXZ^@4N0!*}`D z=vt1WDy~LlFCKnYPw(UEI-&jtDf3xBPCjOX@p=2znJlU}D+WWIoTUSt^5s)jq;lMo zqarQ!I*}Q09EUsS?Ssk0KRI%R7j&ntm;^z`72OkmI(7K|Ywz^k#m%?-$;0&f52Gr> zBS_mG;UoOS&K$+NXjtU3xWcH)Xu%~&xWS)PDyiZ|ZzjQ*h)Jc<4S$t3|A zP{I%{H;j64+;xQ?1T*Sg|L957iQW?F$8Y0VBW)PI^Q={{gE)f5Oe&&DWmIA?la6sg^FqSfph*Wt#RsD|GpbOS zemkx&^mJgMLEBmdtOAP)G|XdL-T!a@UjHv9*(a-jRp7r;fHnJ3zlW6QZe1a#?plF< sfv!x=s}1TDZ0I`H9d#8Splic1p*n~mVQmm2X!ehQmccewfq$yNACn!{hyVZp literal 0 HcmV?d00001 diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile deleted file mode 100644 index 3b95d846b..000000000 --- a/.devcontainer/Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -ARG VARIANT="6.0-bullseye-slim" -FROM mcr.microsoft.com/vscode/devcontainers/dotnet:0-${VARIANT} \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json deleted file mode 100644 index 901133e4d..000000000 --- a/.devcontainer/devcontainer.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "name": "C# (.NET)", - "build": { - "dockerfile": "Dockerfile", - "args": { - "VARIANT": "6.0" - } - }, - "settings": {}, - "extensions": [ - "ms-dotnettools.csharp" - ], - "appPort": [ - 29910, - 6667, - 29920, - 10086, - 27901, - 29900, - 29901, - 27900, - 28910, - 28900, - 80 - ], - "remoteUser": "vscode" -} \ No newline at end of file diff --git a/.dockerignore b/.dockerignore deleted file mode 100644 index 3729ff0cd..000000000 --- a/.dockerignore +++ /dev/null @@ -1,25 +0,0 @@ -**/.classpath -**/.dockerignore -**/.env -**/.git -**/.gitignore -**/.project -**/.settings -**/.toolstarget -**/.vs -**/.vscode -**/*.*proj.user -**/*.dbmdl -**/*.jfm -**/azds.yaml -**/bin -**/charts -**/docker-compose* -**/Dockerfile* -**/node_modules -**/npm-debug.log -**/obj -**/secrets.dev.yaml -**/values.dev.yaml -LICENSE -README.md \ No newline at end of file diff --git a/.gitattributes b/.gitattributes deleted file mode 100644 index 43097e7f3..000000000 --- a/.gitattributes +++ /dev/null @@ -1 +0,0 @@ -*.cs linguist-language=C# diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index a8e3b3d75..000000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,46 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve -title: '' -labels: bug -assignees: '' - ---- - -------------- Have you found a bug in UniSpy? ----------------- -**DO NOT FILL A BUG REPORT FOR AN UNSUPPORTED GAME, PLEASE USE THE APPROPRIATE TEMPLATE FOR UNSUPPORTED GAMES** -1. Verify that the issue is still present in the last development version of UniSpy -2. Set your logging to "Verbose" and reproduce the issue (this will help us tracking which data has been sent or received from the server) -3. Fill the template below ------------- Remove this initial lines -------------------------- - -**Describe the bug** -A clear and concise description of what the bug is. - -**To Reproduce** -Steps to reproduce the behavior: -1. Go to '...' -2. Click on '....' -3. Scroll down to '....' -4. See error - -**Expected behavior** -A clear and concise description of what you expected to happen. - -**Server information (please complete the following information):** - - OS where the server is being hosted: [e.g. CentOS 7, Debian 10, Generic Linux 5.2] - - Channel: [e.g. Candidate, Development, or Master] - - Version: [e.g. 0.5.1, 0.5.1-RC2 or the git commit number for development channel] - -**Game information (if a game has triggered a specific error or issue):** -- Name: [e.g. Crysis 2, Worms 3D] -- Platform: [e.g. Windows, PS2] -- Version: [e.g. Steam version or 1.4] - -**Server logs** -Logs of the client connection and the data which the server sends or receives, or the exception log. - -MAKE SURE TO REMOVE ANY SENSIBLE DATA (such as password, username, response or challenge strings) - -**Additional context** -Add any other context about the problem here. diff --git a/.github/ISSUE_TEMPLATE/custom.md b/.github/ISSUE_TEMPLATE/custom.md deleted file mode 100644 index 30ead30eb..000000000 --- a/.github/ISSUE_TEMPLATE/custom.md +++ /dev/null @@ -1,33 +0,0 @@ ---- -name: Game compatibility -about: Fill an issue about a missing game compatibility -title: Game name -labels: compat -assignees: '' - ---- - ------ Have you found an uncompatible game in RetroSpy? ----- -1. Verify that the game does not work in the last development version -2. Make sure to enable debugging logs and reproduce the issue -3. Fill the form below -------- Delete this extra lines --------------- - -** Game information ** -Name: [e.g. Crysis 2] -Platform: [e.g. Windows, PS2] -Version: [e.g. 1.2, Steam] - -** Special game information ** -(This informations can be found while looking at the logs, for example when the game tries to login or request a server. Write "?" if the game does not send that information) -Namespace id: [e.g. 0] -Partner id: [e.g. 0, 10] -Internal game name: [e.g. capricorn or gmtest] -SDK version: [e.g. 20] - -** Server information ** -Git commit: -Logs: Make sure to hide or remove sensible informations (like username, email, respose, password) - -** Additional information ** -Any addition information that might help the issue diff --git a/.github/labeler.yml b/.github/labeler.yml deleted file mode 100644 index 74da29e0e..000000000 --- a/.github/labeler.yml +++ /dev/null @@ -1,24 +0,0 @@ -CDKey: - - "src/Servers/CDKey/**" - -Chat: - - "src/Servers/Chat/**" - -Game Status: - - "src/Servers/GameStatus/**" - -Nat Negotation: - - "src/Servers/NatNegotiation/**" - -Presence System: - - "src/Servers/PresenceConnectionManager/**" - - "src/Servers/PresenceSearchPlayer/**" - -Query Report: - - "src/Servers/QueryReport/**" - -Server Browser: - - "src/Servers/ServerBrowser/**" - -Web Services: - - "src/Servers/WebServices/**" \ No newline at end of file diff --git a/.github/workflows/cd-cdkey.yml b/.github/workflows/cd-cdkey.yml deleted file mode 100644 index 0f6cbed02..000000000 --- a/.github/workflows/cd-cdkey.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: CD - CDKey - -on: - push: - branches: - - master - - candidate - paths: - - "src/Servers/CDKey/src/**" - - "src/Libraries/Core/src/**" - -jobs: - push: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Deploy to staging - if: github.event_name == 'push' && github.ref == 'refs/heads/candidate' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/cdkey - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/CDKey/src/Dockerfile - - name: Deploy to production - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/cdkey - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/CDKey/src/Dockerfile \ No newline at end of file diff --git a/.github/workflows/cd-chat.yml b/.github/workflows/cd-chat.yml deleted file mode 100644 index 781f9c002..000000000 --- a/.github/workflows/cd-chat.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: CD - Chat - -on: - push: - branches: - - master - - candidate - paths: - - "src/Servers/Chat/src/**" - - "src/Libraries/Core/src/**" - -jobs: - push: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Deploy to staging - if: github.event_name == 'push' && github.ref == 'refs/heads/candidate' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/chat - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/Chat/src/Dockerfile - - name: Deploy to production - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/chat - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/Chat/src/Dockerfile \ No newline at end of file diff --git a/.github/workflows/cd-gs.yml b/.github/workflows/cd-gs.yml deleted file mode 100644 index 06bd736c7..000000000 --- a/.github/workflows/cd-gs.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: CD - GS - -on: - push: - branches: - - master - - candidate - paths: - - "src/Servers/GameStatus/src/**" - - "src/Libraries/Core/src/**" - -jobs: - push: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Deploy to staging - if: github.event_name == 'push' && github.ref == 'refs/heads/candidate' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/gs - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/GameStatus/src/Dockerfile - - name: Deploy to production - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/gs - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/GameStatus/src/Dockerfile \ No newline at end of file diff --git a/.github/workflows/cd-gtr.yml b/.github/workflows/cd-gtr.yml deleted file mode 100644 index 98fa68ebc..000000000 --- a/.github/workflows/cd-gtr.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: CD - GTR - -on: - push: - branches: - - master - - candidate - paths: - - "src/Servers/GameTrafficRelay/src/**" - - "src/Libraries/Core/src/**" - -jobs: - push: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Deploy to staging - if: github.event_name == 'push' && github.ref == 'refs/heads/candidate' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/gtr - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/GameTrafficRelay/src/Dockerfile - - name: Deploy to production - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/gtr - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/GameTrafficRelay/src/Dockerfile \ No newline at end of file diff --git a/.github/workflows/cd-nn.yml b/.github/workflows/cd-nn.yml deleted file mode 100644 index 05ea071f2..000000000 --- a/.github/workflows/cd-nn.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: CD - NN - -on: - push: - branches: - - master - - candidate - paths: - - "src/Servers/NatNegotiation/src/**" - - "src/Libraries/Core/src/**" - -jobs: - push: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Deploy to staging - if: github.event_name == 'push' && github.ref == 'refs/heads/candidate' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/nn - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/NatNegotiation/src/Dockerfile - - name: Deploy to production - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/nn - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/NatNegotiation/src/Dockerfile \ No newline at end of file diff --git a/.github/workflows/cd-pcm.yml b/.github/workflows/cd-pcm.yml deleted file mode 100644 index e0449af8b..000000000 --- a/.github/workflows/cd-pcm.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: CD - PCM - -on: - push: - branches: - - master - - candidate - paths: - - "src/Servers/PresenceConnectionManager/src/**" - - "src/Libraries/Core/src/**" - -jobs: - push: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Deploy to staging - if: github.event_name == 'push' && github.ref == 'refs/heads/candidate' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/pcm - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/PresenceConnectionManager/src/Dockerfile - - name: Deploy to production - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/pcm - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/PresenceConnectionManager/src/Dockerfile \ No newline at end of file diff --git a/.github/workflows/cd-psp.yml b/.github/workflows/cd-psp.yml deleted file mode 100644 index 9d82fcc88..000000000 --- a/.github/workflows/cd-psp.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: CD - PSP - -on: - push: - branches: - - master - - candidate - paths: - - "src/Servers/PresenceSearchPlayer/src/**" - - "src/Libraries/Core/src/**" - -jobs: - push: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Deploy to staging - if: github.event_name == 'push' && github.ref == 'refs/heads/candidate' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/psp - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/PresenceSearchPlayer/src/Dockerfile - - name: Deploy to production - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/psp - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/PresenceSearchPlayer/src/Dockerfile \ No newline at end of file diff --git a/.github/workflows/cd-qr.yml b/.github/workflows/cd-qr.yml deleted file mode 100644 index cb3e7ad0c..000000000 --- a/.github/workflows/cd-qr.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: CD - QR - -on: - push: - branches: - - master - - candidate - paths: - - "src/Servers/QueryReport/src/**" - - "src/Libraries/Core/src/**" - -jobs: - push: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Deploy to staging - if: github.event_name == 'push' && github.ref == 'refs/heads/candidate' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/qr - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/QueryReport/src/Dockerfile - - name: Deploy to production - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/qr - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/QueryReport/src/Dockerfile \ No newline at end of file diff --git a/.github/workflows/cd-sb.yml b/.github/workflows/cd-sb.yml deleted file mode 100644 index eca47db04..000000000 --- a/.github/workflows/cd-sb.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: CD - SB - -on: - push: - branches: - - master - - candidate - paths: - - "src/Servers/ServerBrowser/src/**" - - "src/Libraries/Core/src/**" - -jobs: - push: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Deploy to staging - if: github.event_name == 'push' && github.ref == 'refs/heads/candidate' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/sb - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/ServerBrowser/src/Dockerfile - - name: Deploy to production - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/sb - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/ServerBrowser/src/Dockerfile \ No newline at end of file diff --git a/.github/workflows/cd-ws.yml b/.github/workflows/cd-ws.yml deleted file mode 100644 index efa024577..000000000 --- a/.github/workflows/cd-ws.yml +++ /dev/null @@ -1,35 +0,0 @@ -name: CD - WS - -on: - push: - branches: - - master - - candidate - paths: - - "src/Servers/WebServer/src/**" - - "src/Libraries/Core/src/**" - -jobs: - push: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v2 - - name: Deploy to staging - if: github.event_name == 'push' && github.ref == 'refs/heads/candidate' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/ws - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/WebServer/src/Dockerfile - - name: Deploy to production - if: github.event_name == 'push' && github.ref == 'refs/heads/master' - uses: elgohr/Publish-Docker-Github-Action@3.01 - with: - name: gameprogressive/ws - username: ${{ secrets.DOCKER_HUB_USER }} - password: ${{ secrets.DOCKER_HUB_KEY }} - tagging: true - dockerfile: ./src/Servers/WebServer/src/Dockerfile \ No newline at end of file diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index dc0c99d97..000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,27 +0,0 @@ -name: CI - -on: - push: - branches: - - master - - candidate - - develop - pull_request: - -jobs: - unispy: - runs-on: ${{ matrix.os }} - strategy: - matrix: - os: [ ubuntu-latest, windows-latest, macos-latest ] - - steps: - - uses: actions/checkout@v2 - - name: Setup .NET - uses: actions/setup-dotnet@v1 - with: - dotnet-version: '6.0.100' - - name: Install dependencies - run: dotnet restore - - name: Build - run: dotnet build --no-restore \ No newline at end of file diff --git a/.github/workflows/labeler.yml b/.github/workflows/labeler.yml deleted file mode 100644 index 0d40ed334..000000000 --- a/.github/workflows/labeler.yml +++ /dev/null @@ -1,12 +0,0 @@ -name: "Pull Request Labeler" -on: -- pull_request_target - -jobs: - triage: - runs-on: ubuntu-latest - steps: - - uses: actions/labeler@v3 - with: - repo-token: "${{ secrets.GITHUB_TOKEN }}" - sync-labels: true \ No newline at end of file diff --git a/.gitignore b/.gitignore index 9839b2ad8..95ee5530c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,378 +1,167 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs +# Byte-compiled / optimized / DLL files +**/__pycache__/ +*.py[cod] +*$py.class +*.pyc +# C extensions +*.so -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ +# Distribution / packaging +.Python build/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUNIT -*.VisualState.xml -TestResult.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ -**/Properties/launchSettings.json - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_i.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: *.log -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# JustCode is a .NET coding add-in -.JustCode - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# log files +*.log.* -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# JetBrains Rider -.idea/ -*.sln.iml - -# CodeRush -.cr/ - -# Python Tools for Visual Studio (PTVS) -__pycache__/ *.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# retrospy -logs\ - -# linq2db -.tools/ -db/ - -# Prerequisites -*.d - -# Compiled Object files -*.slo -*.lo -*.o -*.obj - -# Precompiled Headers -*.gch -*.pch - -# Compiled Dynamic libraries -*.so -*.dylib -*.dll - -# Fortran module files -*.mod -*.smod - -# Compiled Static libraries -*.lai -*.la -*.a -*.lib - -# Executables -*.exe -*.out -*.app - -*.db -Logs\ -Logs/ -logs/ - -.DS_Store +*.txt +!requirement.txt \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json deleted file mode 100644 index 3a000d416..000000000 --- a/.vscode/launch.json +++ /dev/null @@ -1,222 +0,0 @@ -{ - // Use IntelliSense to find out which attributes exist for C# debugging - // Use hover for the description of the existing attributes - // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md - "version": "0.2.0", - "configurations": [ - { - "name": "PCM", - "type": "coreclr", - "request": "launch", - "program": "${workspaceRoot}/build/Debug/net6.0/UniSpy.Server.PresenceConnectionManager.dll", - "args": [], - "cwd": "${workspaceRoot}/build/Debug/net6.0/", - "stopAtEntry": false, - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "logging": { - "moduleLoad": false - } - }, - { - "name": "PSP", - "type": "coreclr", - "request": "launch", - "program": "${workspaceRoot}/build/Debug/net6.0/UniSpy.Server.PresenceSearchPlayer.dll", - "args": [], - "cwd": "${workspaceRoot}/build/Debug/net6.0/", - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "stopAtEntry": false, - "logging": { - "moduleLoad": false - } - }, - { - "name": "NatNeg", - "type": "coreclr", - "request": "launch", - "program": "${workspaceRoot}/build/Debug/net6.0/UniSpy.Server.NatNegotiation.dll", - "args": [], - "cwd": "${workspaceRoot}/build/Debug/net6.0/", - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "stopAtEntry": false, - "logging": { - "moduleLoad": false - } - }, - { - "name": "GTR", - "type": "coreclr", - "request": "launch", - "program": "${workspaceRoot}/build/Debug/net6.0/UniSpy.Server.GameTrafficRelay.dll", - "args": [], - "cwd": "${workspaceRoot}/build/Debug/net6.0/", - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "env": { - "ASPNETCORE_ENVIRONMENT": "Development" - }, - "stopAtEntry": false, - "logging": { - "moduleLoad": false - } - }, - { - "name": "Chat", - "type": "coreclr", - "request": "launch", - "program": "${workspaceRoot}/build/Debug/net6.0/UniSpy.Server.Chat.dll", - "args": [], - "cwd": "${workspaceRoot}/build/Debug/net6.0/", - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "stopAtEntry": false, - "logging": { - "moduleLoad": false - } - }, - { - "name": "QR", - "type": "coreclr", - "request": "launch", - "program": "${workspaceRoot}/build/Debug/net6.0/UniSpy.Server.QueryReport.dll", - "args": [], - "cwd": "${workspaceRoot}/build/Debug/net6.0/", - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "stopAtEntry": false, - "logging": { - "moduleLoad": false - } - }, - { - "name": "SB", - "type": "coreclr", - "request": "launch", - "program": "${workspaceRoot}/build/Debug/net6.0/UniSpy.Server.ServerBrowser.dll", - "args": [], - "cwd": "${workspaceRoot}/build/Debug/net6.0/", - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "stopAtEntry": false, - "logging": { - "moduleLoad": false - } - }, - { - "name": "GS", - "type": "coreclr", - "request": "launch", - "program": "${workspaceRoot}/build/Debug/net6.0/UniSpy.Server.GameStatus.dll", - "args": [], - "cwd": "${workspaceRoot}/build/Debug/net6.0/", - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "stopAtEntry": false, - "logging": { - "moduleLoad": false - } - }, - { - "name": "Web", - "type": "coreclr", - "request": "launch", - "program": "${workspaceRoot}/build/Debug/net6.0/UniSpy.Server.WebServer.dll", - "args": [], - "cwd": "${workspaceRoot}/build/Debug/net6.0/", - "console": "integratedTerminal", - "internalConsoleOptions": "neverOpen", - "stopAtEntry": false, - "logging": { - "moduleLoad": false - } - } - ], - "compounds": [ - { - "name": "PCM PSP", - "configurations": [ - "PCM", - "PSP" - ] - }, - { - "name": "NatNeg GTR", - "configurations": [ - "NatNeg", - "GTR" - ] - }, - { - "name": "QR SB NatNeg GTR", - "configurations": [ - "QR", - "SB", - "NatNeg", - "GTR" - ] - }, - { - "name": "GS PSP", - "configurations": [ - "PSP", - "GS" - ] - }, - { - "name": "Web PCM", - "configurations": [ - "Web", - "PCM" - ] - }, - { - "name": "Chat QR SB", - "configurations": [ - "Chat", - "QR", - "SB", - ] - }, - { - "name": "PCM PSP NN Chat QR SB GS", - "configurations": [ - "PCM", - "PSP", - "NatNeg", - "Chat", - "QR", - "SB", - "GS" - ] - }, - { - "name": "PCM PSP NatNeg Chat QR SB Web", - "configurations": [ - "PCM", - "PSP", - "NatNeg", - "Chat", - "QR", - "SB", - "Web" - ] - }, - { - "name": "PCM PSP NN GTR Chat QR SB GS Web", - "configurations": [ - "PCM", - "PSP", - "NatNeg", - "GTR", - "Chat", - "QR", - "SB", - "GS", - "Web" - ] - } - ] -} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json deleted file mode 100644 index 8264b4072..000000000 --- a/.vscode/tasks.json +++ /dev/null @@ -1,155 +0,0 @@ -{ - "version": "2.0.0", - "tasks": [ - { - "label": "build_library", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "--no-restore", - "${workspaceFolder}/src/Libraries/Core/src/UniSpy.Server.Core.csproj", - "-o", - "${workspaceFolder}/build/Debug/net6.0/", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "build_chat", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "--no-restore", - "${workspaceFolder}/src/Servers/Chat/src/UniSpy.Server.Chat.csproj", - "-o", - "${workspaceFolder}/build/Debug/net6.0/", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "build_pcm", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "--no-restore", - "${workspaceFolder}/src/Servers/PresenceConnectionManager/src/UniSpy.Server.PresenceConnectionManager.csproj", - "-o", - "${workspaceFolder}/build/Debug/net6.0/", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "build_psp", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "--no-restore", - "${workspaceFolder}/src/Servers/PresenceSearchPlayer/src/UniSpy.Server.PresenceSearchPlayer.csproj", - "-o", - "${workspaceFolder}/build/Debug/net6.0/", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "build_gs", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "--no-restore", - "${workspaceFolder}/src/Servers/GameStatus/src/UniSpy.Server.GameStatus.csproj", - "-o", - "${workspaceFolder}/build/Debug/net6.0/", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile", - }, - { - "label": "build_gtr", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "--no-restore", - "${workspaceFolder}/src/Servers/GameTrafficRelay/src/UniSpy.Server.GameTrafficRelay.csproj", - "-o", - "${workspaceFolder}/build/Debug/net6.0/", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "build_natneg", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "--no-restore", - "${workspaceFolder}/src/Servers/NatNegotiation/src/UniSpy.Server.NatNegotiation.csproj", - "-o", - "${workspaceFolder}/build/Debug/net6.0/", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "build_qr", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "--no-restore", - "${workspaceFolder}/src/Servers/QueryReport/src/UniSpy.Server.QueryReport.csproj", - "-o", - "${workspaceFolder}/build/Debug/net6.0/", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "build_sb", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "--no-restore", - "${workspaceFolder}/src/Servers/ServerBrowser/src/UniSpy.Server.ServerBrowser.csproj", - "-o", - "${workspaceFolder}/build/Debug/net6.0/", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - { - "label": "build_web", - "command": "dotnet", - "type": "process", - "args": [ - "build", - "--no-restore", - "${workspaceFolder}/src/Servers/WebServer/src/UniSpy.Server.WebServer.csproj", - "-o", - "${workspaceFolder}/build/Debug/net6.0/", - "/property:GenerateFullPaths=true", - "/consoleloggerparameters:NoSummary" - ], - "problemMatcher": "$msCompile" - }, - ] -} \ No newline at end of file diff --git a/BuildUniSpyServer.bat b/BuildUniSpyServer.bat deleted file mode 100644 index 04b28e20e..000000000 --- a/BuildUniSpyServer.bat +++ /dev/null @@ -1,4 +0,0 @@ -@ECHO OFF -ECHO Building UniSpyServer! -cd %0 -dotnet build \ No newline at end of file diff --git a/BuildUniSpyServer.sh b/BuildUniSpyServer.sh deleted file mode 100755 index cf0f1fd8c..000000000 --- a/BuildUniSpyServer.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/bin/bash -echo "Building UniSpyServer!" -cd "$( cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P )" -dotnet build -echo "Copy UniSpyServer.cfg to build directory" -cp ./common/UniSpyServer.cfg ./build/Debug/net5.0/ \ No newline at end of file diff --git a/UniSpy.Server.sln b/UniSpy.Server.sln deleted file mode 100644 index 3e606cd21..000000000 --- a/UniSpy.Server.sln +++ /dev/null @@ -1,393 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.32228.343 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Servers", "Servers", "{EBF19CC8-39E4-490E-8BAB-4B0C2FADAC98}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Chat", "Chat", "{BC1F1EF3-7152-4284-9EDA-AAE8825388A3}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.Chat", "src\Servers\Chat\src\UniSpy.Server.Chat.csproj", "{29BDDFB0-54FA-4057-BEC5-5B3DCD1DE212}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NatNegotiation", "NatNegotiation", "{09E88199-24C9-45BC-B043-7D636D4BCDF0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.NatNegotiation", "src\Servers\NatNegotiation\src\UniSpy.Server.NatNegotiation.csproj", "{8FCED8D1-D6CC-44BC-9CDA-9F0C1C786453}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GameStatus", "GameStatus", "{79343A9E-5F1A-490C-AA3F-68B1F03A8CEF}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.GameStatus", "src\Servers\GameStatus\src\UniSpy.Server.GameStatus.csproj", "{DCB6AB5F-4B6F-4B2D-83CB-F539DA614A5E}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PresenceConnectionManager", "PresenceConnectionManager", "{945EB98D-95C4-415B-8C00-C7682683FAA3}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.PresenceConnectionManager", "src\Servers\PresenceConnectionManager\src\UniSpy.Server.PresenceConnectionManager.csproj", "{23683C7C-23E6-443C-AB41-3F28EA1B2FC0}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "PresenceSearchPlayer", "PresenceSearchPlayer", "{17ABC2A8-9E3B-4CE1-AD13-A3780B0B07F0}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.PresenceSearchPlayer", "src\Servers\PresenceSearchPlayer\src\UniSpy.Server.PresenceSearchPlayer.csproj", "{0B1AA7F3-09C4-4D8B-AB54-D500BF0B2537}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "QueryReport", "QueryReport", "{D9FA7414-F8AB-4195-A483-474BF96467E6}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.QueryReport", "src\Servers\QueryReport\src\UniSpy.Server.QueryReport.csproj", "{2EE9F419-409D-4F43-A541-BBBAD6FEDCA2}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ServerBrowser", "ServerBrowser", "{520D01A8-B487-4B27-B33A-8ACB97F57BA5}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.ServerBrowser", "src\Servers\ServerBrowser\src\UniSpy.Server.ServerBrowser.csproj", "{B4049A31-CEC8-4310-957D-1FBCBBDEC413}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "WebServer", "WebServer", "{79E8F708-7806-4EF5-A8D9-22AE31053124}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.WebServer", "src\Servers\WebServer\src\UniSpy.Server.WebServer.csproj", "{4D8EA982-3285-4637-9010-310FCA5B254A}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.Chat.Test", "src\Servers\Chat\test\UniSpy.Server.Chat.Test.csproj", "{D065CD06-4296-416A-B47D-274D0B5F82E9}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.NatNegotiation.Test", "src\Servers\NatNegotiation\test\UniSpy.Server.NatNegotiation.Test.csproj", "{381A72E9-DD7B-45CE-BCEC-D70DC3BEAAEC}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.PresenceConnectionManager.Test", "src\Servers\PresenceConnectionManager\test\UniSpy.Server.PresenceConnectionManager.Test.csproj", "{EEDF9F97-98D4-465E-B8FB-0DDAE9219F72}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.PresenceSearchPlayer.Test", "src\Servers\PresenceSearchPlayer\test\UniSpy.Server.PresenceSearchPlayer.Test.csproj", "{456B57D9-E3F7-46DF-AA27-5518C7647FD1}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.GameStatus.Test", "src\Servers\GameStatus\test\UniSpy.Server.GameStatus.Test.csproj", "{B267E37A-96A5-418D-9A86-76C19564D205}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.QueryReport.Test", "src\Servers\QueryReport\test\UniSpy.Server.QueryReport.Test.csproj", "{370CBE6A-76A2-4F49-BCD9-68FC79ED4171}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.ServerBrowser.Test", "src\Servers\ServerBrowser\test\UniSpy.Server.ServerBrowser.Test.csproj", "{6017E174-2FC3-4BE9-9279-855BA7481AF3}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.WebServer.Test", "src\Servers\WebServer\test\UniSpy.Server.WebServer.Test.csproj", "{E10E3BFC-F5C8-4E70-AC3C-1DC6743BD4D8}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{FD5B4D88-8BDA-4513-BBA1-9F31B9D64BB5}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.Core", "src\Libraries\Core\src\UniSpy.Server.Core.csproj", "{D902FBA8-5B07-452E-9A0B-3381D49ABB9B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.LinqToRedis", "src\Libraries\LinqToRedis\src\UniSpy.LinqToRedis.csproj", "{7780907C-CABF-4CCD-9039-7C17CC7391F9}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GameTrafficRelay", "GameTrafficRelay", "{EF1CA185-B3C8-4F0A-B134-1114CB51E97D}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.GameTrafficRelay", "src\Servers\GameTrafficRelay\src\UniSpy.Server.GameTrafficRelay.csproj", "{555A87B1-5FFE-4F24-B665-160693901D25}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniSpy.Server.GameTrafficRelay.Test", "src\Servers\GameTrafficRelay\test\UniSpy.Server.GameTrafficRelay.Test.csproj", "{FD4B4018-7283-41DB-AC57-AB62760269FB}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{B46172F1-8E97-4934-9969-3957B9129736}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Libraries", "Libraries", "{8B1B77AA-ED59-4B6F-8926-A65AAF28A713}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LinqToRedis", "LinqToRedis", "{98525275-1CA4-421F-AF18-12E906536814}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniSpy.Server.Test", "src\test\UniSpy.Server.Test.csproj", "{8A98B2B8-92E6-4649-92D7-77FB11E3C9C8}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Servers", "Servers", "{3E629C69-B021-4899-9A61-C3696310706D}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug|x64 = Debug|x64 - Debug|x86 = Debug|x86 - Release|Any CPU = Release|Any CPU - Release|x64 = Release|x64 - Release|x86 = Release|x86 - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {29BDDFB0-54FA-4057-BEC5-5B3DCD1DE212}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {29BDDFB0-54FA-4057-BEC5-5B3DCD1DE212}.Debug|Any CPU.Build.0 = Debug|Any CPU - {29BDDFB0-54FA-4057-BEC5-5B3DCD1DE212}.Debug|x64.ActiveCfg = Debug|Any CPU - {29BDDFB0-54FA-4057-BEC5-5B3DCD1DE212}.Debug|x64.Build.0 = Debug|Any CPU - {29BDDFB0-54FA-4057-BEC5-5B3DCD1DE212}.Debug|x86.ActiveCfg = Debug|Any CPU - {29BDDFB0-54FA-4057-BEC5-5B3DCD1DE212}.Debug|x86.Build.0 = Debug|Any CPU - {29BDDFB0-54FA-4057-BEC5-5B3DCD1DE212}.Release|Any CPU.ActiveCfg = Release|Any CPU - {29BDDFB0-54FA-4057-BEC5-5B3DCD1DE212}.Release|Any CPU.Build.0 = Release|Any CPU - {29BDDFB0-54FA-4057-BEC5-5B3DCD1DE212}.Release|x64.ActiveCfg = Release|Any CPU - {29BDDFB0-54FA-4057-BEC5-5B3DCD1DE212}.Release|x64.Build.0 = Release|Any CPU - {29BDDFB0-54FA-4057-BEC5-5B3DCD1DE212}.Release|x86.ActiveCfg = Release|Any CPU - {29BDDFB0-54FA-4057-BEC5-5B3DCD1DE212}.Release|x86.Build.0 = Release|Any CPU - {8FCED8D1-D6CC-44BC-9CDA-9F0C1C786453}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8FCED8D1-D6CC-44BC-9CDA-9F0C1C786453}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8FCED8D1-D6CC-44BC-9CDA-9F0C1C786453}.Debug|x64.ActiveCfg = Debug|Any CPU - {8FCED8D1-D6CC-44BC-9CDA-9F0C1C786453}.Debug|x64.Build.0 = Debug|Any CPU - {8FCED8D1-D6CC-44BC-9CDA-9F0C1C786453}.Debug|x86.ActiveCfg = Debug|Any CPU - {8FCED8D1-D6CC-44BC-9CDA-9F0C1C786453}.Debug|x86.Build.0 = Debug|Any CPU - {8FCED8D1-D6CC-44BC-9CDA-9F0C1C786453}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8FCED8D1-D6CC-44BC-9CDA-9F0C1C786453}.Release|Any CPU.Build.0 = Release|Any CPU - {8FCED8D1-D6CC-44BC-9CDA-9F0C1C786453}.Release|x64.ActiveCfg = Release|Any CPU - {8FCED8D1-D6CC-44BC-9CDA-9F0C1C786453}.Release|x64.Build.0 = Release|Any CPU - {8FCED8D1-D6CC-44BC-9CDA-9F0C1C786453}.Release|x86.ActiveCfg = Release|Any CPU - {8FCED8D1-D6CC-44BC-9CDA-9F0C1C786453}.Release|x86.Build.0 = Release|Any CPU - {DCB6AB5F-4B6F-4B2D-83CB-F539DA614A5E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DCB6AB5F-4B6F-4B2D-83CB-F539DA614A5E}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DCB6AB5F-4B6F-4B2D-83CB-F539DA614A5E}.Debug|x64.ActiveCfg = Debug|Any CPU - {DCB6AB5F-4B6F-4B2D-83CB-F539DA614A5E}.Debug|x64.Build.0 = Debug|Any CPU - {DCB6AB5F-4B6F-4B2D-83CB-F539DA614A5E}.Debug|x86.ActiveCfg = Debug|Any CPU - {DCB6AB5F-4B6F-4B2D-83CB-F539DA614A5E}.Debug|x86.Build.0 = Debug|Any CPU - {DCB6AB5F-4B6F-4B2D-83CB-F539DA614A5E}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DCB6AB5F-4B6F-4B2D-83CB-F539DA614A5E}.Release|Any CPU.Build.0 = Release|Any CPU - {DCB6AB5F-4B6F-4B2D-83CB-F539DA614A5E}.Release|x64.ActiveCfg = Release|Any CPU - {DCB6AB5F-4B6F-4B2D-83CB-F539DA614A5E}.Release|x64.Build.0 = Release|Any CPU - {DCB6AB5F-4B6F-4B2D-83CB-F539DA614A5E}.Release|x86.ActiveCfg = Release|Any CPU - {DCB6AB5F-4B6F-4B2D-83CB-F539DA614A5E}.Release|x86.Build.0 = Release|Any CPU - {23683C7C-23E6-443C-AB41-3F28EA1B2FC0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {23683C7C-23E6-443C-AB41-3F28EA1B2FC0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {23683C7C-23E6-443C-AB41-3F28EA1B2FC0}.Debug|x64.ActiveCfg = Debug|Any CPU - {23683C7C-23E6-443C-AB41-3F28EA1B2FC0}.Debug|x64.Build.0 = Debug|Any CPU - {23683C7C-23E6-443C-AB41-3F28EA1B2FC0}.Debug|x86.ActiveCfg = Debug|Any CPU - {23683C7C-23E6-443C-AB41-3F28EA1B2FC0}.Debug|x86.Build.0 = Debug|Any CPU - {23683C7C-23E6-443C-AB41-3F28EA1B2FC0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {23683C7C-23E6-443C-AB41-3F28EA1B2FC0}.Release|Any CPU.Build.0 = Release|Any CPU - {23683C7C-23E6-443C-AB41-3F28EA1B2FC0}.Release|x64.ActiveCfg = Release|Any CPU - {23683C7C-23E6-443C-AB41-3F28EA1B2FC0}.Release|x64.Build.0 = Release|Any CPU - {23683C7C-23E6-443C-AB41-3F28EA1B2FC0}.Release|x86.ActiveCfg = Release|Any CPU - {23683C7C-23E6-443C-AB41-3F28EA1B2FC0}.Release|x86.Build.0 = Release|Any CPU - {0B1AA7F3-09C4-4D8B-AB54-D500BF0B2537}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {0B1AA7F3-09C4-4D8B-AB54-D500BF0B2537}.Debug|Any CPU.Build.0 = Debug|Any CPU - {0B1AA7F3-09C4-4D8B-AB54-D500BF0B2537}.Debug|x64.ActiveCfg = Debug|Any CPU - {0B1AA7F3-09C4-4D8B-AB54-D500BF0B2537}.Debug|x64.Build.0 = Debug|Any CPU - {0B1AA7F3-09C4-4D8B-AB54-D500BF0B2537}.Debug|x86.ActiveCfg = Debug|Any CPU - {0B1AA7F3-09C4-4D8B-AB54-D500BF0B2537}.Debug|x86.Build.0 = Debug|Any CPU - {0B1AA7F3-09C4-4D8B-AB54-D500BF0B2537}.Release|Any CPU.ActiveCfg = Release|Any CPU - {0B1AA7F3-09C4-4D8B-AB54-D500BF0B2537}.Release|Any CPU.Build.0 = Release|Any CPU - {0B1AA7F3-09C4-4D8B-AB54-D500BF0B2537}.Release|x64.ActiveCfg = Release|Any CPU - {0B1AA7F3-09C4-4D8B-AB54-D500BF0B2537}.Release|x64.Build.0 = Release|Any CPU - {0B1AA7F3-09C4-4D8B-AB54-D500BF0B2537}.Release|x86.ActiveCfg = Release|Any CPU - {0B1AA7F3-09C4-4D8B-AB54-D500BF0B2537}.Release|x86.Build.0 = Release|Any CPU - {2EE9F419-409D-4F43-A541-BBBAD6FEDCA2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2EE9F419-409D-4F43-A541-BBBAD6FEDCA2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2EE9F419-409D-4F43-A541-BBBAD6FEDCA2}.Debug|x64.ActiveCfg = Debug|Any CPU - {2EE9F419-409D-4F43-A541-BBBAD6FEDCA2}.Debug|x64.Build.0 = Debug|Any CPU - {2EE9F419-409D-4F43-A541-BBBAD6FEDCA2}.Debug|x86.ActiveCfg = Debug|Any CPU - {2EE9F419-409D-4F43-A541-BBBAD6FEDCA2}.Debug|x86.Build.0 = Debug|Any CPU - {2EE9F419-409D-4F43-A541-BBBAD6FEDCA2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2EE9F419-409D-4F43-A541-BBBAD6FEDCA2}.Release|Any CPU.Build.0 = Release|Any CPU - {2EE9F419-409D-4F43-A541-BBBAD6FEDCA2}.Release|x64.ActiveCfg = Release|Any CPU - {2EE9F419-409D-4F43-A541-BBBAD6FEDCA2}.Release|x64.Build.0 = Release|Any CPU - {2EE9F419-409D-4F43-A541-BBBAD6FEDCA2}.Release|x86.ActiveCfg = Release|Any CPU - {2EE9F419-409D-4F43-A541-BBBAD6FEDCA2}.Release|x86.Build.0 = Release|Any CPU - {B4049A31-CEC8-4310-957D-1FBCBBDEC413}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B4049A31-CEC8-4310-957D-1FBCBBDEC413}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B4049A31-CEC8-4310-957D-1FBCBBDEC413}.Debug|x64.ActiveCfg = Debug|Any CPU - {B4049A31-CEC8-4310-957D-1FBCBBDEC413}.Debug|x64.Build.0 = Debug|Any CPU - {B4049A31-CEC8-4310-957D-1FBCBBDEC413}.Debug|x86.ActiveCfg = Debug|Any CPU - {B4049A31-CEC8-4310-957D-1FBCBBDEC413}.Debug|x86.Build.0 = Debug|Any CPU - {B4049A31-CEC8-4310-957D-1FBCBBDEC413}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B4049A31-CEC8-4310-957D-1FBCBBDEC413}.Release|Any CPU.Build.0 = Release|Any CPU - {B4049A31-CEC8-4310-957D-1FBCBBDEC413}.Release|x64.ActiveCfg = Release|Any CPU - {B4049A31-CEC8-4310-957D-1FBCBBDEC413}.Release|x64.Build.0 = Release|Any CPU - {B4049A31-CEC8-4310-957D-1FBCBBDEC413}.Release|x86.ActiveCfg = Release|Any CPU - {B4049A31-CEC8-4310-957D-1FBCBBDEC413}.Release|x86.Build.0 = Release|Any CPU - {4D8EA982-3285-4637-9010-310FCA5B254A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4D8EA982-3285-4637-9010-310FCA5B254A}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4D8EA982-3285-4637-9010-310FCA5B254A}.Debug|x64.ActiveCfg = Debug|Any CPU - {4D8EA982-3285-4637-9010-310FCA5B254A}.Debug|x64.Build.0 = Debug|Any CPU - {4D8EA982-3285-4637-9010-310FCA5B254A}.Debug|x86.ActiveCfg = Debug|Any CPU - {4D8EA982-3285-4637-9010-310FCA5B254A}.Debug|x86.Build.0 = Debug|Any CPU - {4D8EA982-3285-4637-9010-310FCA5B254A}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4D8EA982-3285-4637-9010-310FCA5B254A}.Release|Any CPU.Build.0 = Release|Any CPU - {4D8EA982-3285-4637-9010-310FCA5B254A}.Release|x64.ActiveCfg = Release|Any CPU - {4D8EA982-3285-4637-9010-310FCA5B254A}.Release|x64.Build.0 = Release|Any CPU - {4D8EA982-3285-4637-9010-310FCA5B254A}.Release|x86.ActiveCfg = Release|Any CPU - {4D8EA982-3285-4637-9010-310FCA5B254A}.Release|x86.Build.0 = Release|Any CPU - {D065CD06-4296-416A-B47D-274D0B5F82E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D065CD06-4296-416A-B47D-274D0B5F82E9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D065CD06-4296-416A-B47D-274D0B5F82E9}.Debug|x64.ActiveCfg = Debug|Any CPU - {D065CD06-4296-416A-B47D-274D0B5F82E9}.Debug|x64.Build.0 = Debug|Any CPU - {D065CD06-4296-416A-B47D-274D0B5F82E9}.Debug|x86.ActiveCfg = Debug|Any CPU - {D065CD06-4296-416A-B47D-274D0B5F82E9}.Debug|x86.Build.0 = Debug|Any CPU - {D065CD06-4296-416A-B47D-274D0B5F82E9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D065CD06-4296-416A-B47D-274D0B5F82E9}.Release|Any CPU.Build.0 = Release|Any CPU - {D065CD06-4296-416A-B47D-274D0B5F82E9}.Release|x64.ActiveCfg = Release|Any CPU - {D065CD06-4296-416A-B47D-274D0B5F82E9}.Release|x64.Build.0 = Release|Any CPU - {D065CD06-4296-416A-B47D-274D0B5F82E9}.Release|x86.ActiveCfg = Release|Any CPU - {D065CD06-4296-416A-B47D-274D0B5F82E9}.Release|x86.Build.0 = Release|Any CPU - {381A72E9-DD7B-45CE-BCEC-D70DC3BEAAEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {381A72E9-DD7B-45CE-BCEC-D70DC3BEAAEC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {381A72E9-DD7B-45CE-BCEC-D70DC3BEAAEC}.Debug|x64.ActiveCfg = Debug|Any CPU - {381A72E9-DD7B-45CE-BCEC-D70DC3BEAAEC}.Debug|x64.Build.0 = Debug|Any CPU - {381A72E9-DD7B-45CE-BCEC-D70DC3BEAAEC}.Debug|x86.ActiveCfg = Debug|Any CPU - {381A72E9-DD7B-45CE-BCEC-D70DC3BEAAEC}.Debug|x86.Build.0 = Debug|Any CPU - {381A72E9-DD7B-45CE-BCEC-D70DC3BEAAEC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {381A72E9-DD7B-45CE-BCEC-D70DC3BEAAEC}.Release|Any CPU.Build.0 = Release|Any CPU - {381A72E9-DD7B-45CE-BCEC-D70DC3BEAAEC}.Release|x64.ActiveCfg = Release|Any CPU - {381A72E9-DD7B-45CE-BCEC-D70DC3BEAAEC}.Release|x64.Build.0 = Release|Any CPU - {381A72E9-DD7B-45CE-BCEC-D70DC3BEAAEC}.Release|x86.ActiveCfg = Release|Any CPU - {381A72E9-DD7B-45CE-BCEC-D70DC3BEAAEC}.Release|x86.Build.0 = Release|Any CPU - {EEDF9F97-98D4-465E-B8FB-0DDAE9219F72}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EEDF9F97-98D4-465E-B8FB-0DDAE9219F72}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EEDF9F97-98D4-465E-B8FB-0DDAE9219F72}.Debug|x64.ActiveCfg = Debug|Any CPU - {EEDF9F97-98D4-465E-B8FB-0DDAE9219F72}.Debug|x64.Build.0 = Debug|Any CPU - {EEDF9F97-98D4-465E-B8FB-0DDAE9219F72}.Debug|x86.ActiveCfg = Debug|Any CPU - {EEDF9F97-98D4-465E-B8FB-0DDAE9219F72}.Debug|x86.Build.0 = Debug|Any CPU - {EEDF9F97-98D4-465E-B8FB-0DDAE9219F72}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EEDF9F97-98D4-465E-B8FB-0DDAE9219F72}.Release|Any CPU.Build.0 = Release|Any CPU - {EEDF9F97-98D4-465E-B8FB-0DDAE9219F72}.Release|x64.ActiveCfg = Release|Any CPU - {EEDF9F97-98D4-465E-B8FB-0DDAE9219F72}.Release|x64.Build.0 = Release|Any CPU - {EEDF9F97-98D4-465E-B8FB-0DDAE9219F72}.Release|x86.ActiveCfg = Release|Any CPU - {EEDF9F97-98D4-465E-B8FB-0DDAE9219F72}.Release|x86.Build.0 = Release|Any CPU - {456B57D9-E3F7-46DF-AA27-5518C7647FD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {456B57D9-E3F7-46DF-AA27-5518C7647FD1}.Debug|Any CPU.Build.0 = Debug|Any CPU - {456B57D9-E3F7-46DF-AA27-5518C7647FD1}.Debug|x64.ActiveCfg = Debug|Any CPU - {456B57D9-E3F7-46DF-AA27-5518C7647FD1}.Debug|x64.Build.0 = Debug|Any CPU - {456B57D9-E3F7-46DF-AA27-5518C7647FD1}.Debug|x86.ActiveCfg = Debug|Any CPU - {456B57D9-E3F7-46DF-AA27-5518C7647FD1}.Debug|x86.Build.0 = Debug|Any CPU - {456B57D9-E3F7-46DF-AA27-5518C7647FD1}.Release|Any CPU.ActiveCfg = Release|Any CPU - {456B57D9-E3F7-46DF-AA27-5518C7647FD1}.Release|Any CPU.Build.0 = Release|Any CPU - {456B57D9-E3F7-46DF-AA27-5518C7647FD1}.Release|x64.ActiveCfg = Release|Any CPU - {456B57D9-E3F7-46DF-AA27-5518C7647FD1}.Release|x64.Build.0 = Release|Any CPU - {456B57D9-E3F7-46DF-AA27-5518C7647FD1}.Release|x86.ActiveCfg = Release|Any CPU - {456B57D9-E3F7-46DF-AA27-5518C7647FD1}.Release|x86.Build.0 = Release|Any CPU - {B267E37A-96A5-418D-9A86-76C19564D205}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B267E37A-96A5-418D-9A86-76C19564D205}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B267E37A-96A5-418D-9A86-76C19564D205}.Debug|x64.ActiveCfg = Debug|Any CPU - {B267E37A-96A5-418D-9A86-76C19564D205}.Debug|x64.Build.0 = Debug|Any CPU - {B267E37A-96A5-418D-9A86-76C19564D205}.Debug|x86.ActiveCfg = Debug|Any CPU - {B267E37A-96A5-418D-9A86-76C19564D205}.Debug|x86.Build.0 = Debug|Any CPU - {B267E37A-96A5-418D-9A86-76C19564D205}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B267E37A-96A5-418D-9A86-76C19564D205}.Release|Any CPU.Build.0 = Release|Any CPU - {B267E37A-96A5-418D-9A86-76C19564D205}.Release|x64.ActiveCfg = Release|Any CPU - {B267E37A-96A5-418D-9A86-76C19564D205}.Release|x64.Build.0 = Release|Any CPU - {B267E37A-96A5-418D-9A86-76C19564D205}.Release|x86.ActiveCfg = Release|Any CPU - {B267E37A-96A5-418D-9A86-76C19564D205}.Release|x86.Build.0 = Release|Any CPU - {370CBE6A-76A2-4F49-BCD9-68FC79ED4171}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {370CBE6A-76A2-4F49-BCD9-68FC79ED4171}.Debug|Any CPU.Build.0 = Debug|Any CPU - {370CBE6A-76A2-4F49-BCD9-68FC79ED4171}.Debug|x64.ActiveCfg = Debug|Any CPU - {370CBE6A-76A2-4F49-BCD9-68FC79ED4171}.Debug|x64.Build.0 = Debug|Any CPU - {370CBE6A-76A2-4F49-BCD9-68FC79ED4171}.Debug|x86.ActiveCfg = Debug|Any CPU - {370CBE6A-76A2-4F49-BCD9-68FC79ED4171}.Debug|x86.Build.0 = Debug|Any CPU - {370CBE6A-76A2-4F49-BCD9-68FC79ED4171}.Release|Any CPU.ActiveCfg = Release|Any CPU - {370CBE6A-76A2-4F49-BCD9-68FC79ED4171}.Release|Any CPU.Build.0 = Release|Any CPU - {370CBE6A-76A2-4F49-BCD9-68FC79ED4171}.Release|x64.ActiveCfg = Release|Any CPU - {370CBE6A-76A2-4F49-BCD9-68FC79ED4171}.Release|x64.Build.0 = Release|Any CPU - {370CBE6A-76A2-4F49-BCD9-68FC79ED4171}.Release|x86.ActiveCfg = Release|Any CPU - {370CBE6A-76A2-4F49-BCD9-68FC79ED4171}.Release|x86.Build.0 = Release|Any CPU - {6017E174-2FC3-4BE9-9279-855BA7481AF3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6017E174-2FC3-4BE9-9279-855BA7481AF3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6017E174-2FC3-4BE9-9279-855BA7481AF3}.Debug|x64.ActiveCfg = Debug|Any CPU - {6017E174-2FC3-4BE9-9279-855BA7481AF3}.Debug|x64.Build.0 = Debug|Any CPU - {6017E174-2FC3-4BE9-9279-855BA7481AF3}.Debug|x86.ActiveCfg = Debug|Any CPU - {6017E174-2FC3-4BE9-9279-855BA7481AF3}.Debug|x86.Build.0 = Debug|Any CPU - {6017E174-2FC3-4BE9-9279-855BA7481AF3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6017E174-2FC3-4BE9-9279-855BA7481AF3}.Release|Any CPU.Build.0 = Release|Any CPU - {6017E174-2FC3-4BE9-9279-855BA7481AF3}.Release|x64.ActiveCfg = Release|Any CPU - {6017E174-2FC3-4BE9-9279-855BA7481AF3}.Release|x64.Build.0 = Release|Any CPU - {6017E174-2FC3-4BE9-9279-855BA7481AF3}.Release|x86.ActiveCfg = Release|Any CPU - {6017E174-2FC3-4BE9-9279-855BA7481AF3}.Release|x86.Build.0 = Release|Any CPU - {E10E3BFC-F5C8-4E70-AC3C-1DC6743BD4D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {E10E3BFC-F5C8-4E70-AC3C-1DC6743BD4D8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {E10E3BFC-F5C8-4E70-AC3C-1DC6743BD4D8}.Debug|x64.ActiveCfg = Debug|Any CPU - {E10E3BFC-F5C8-4E70-AC3C-1DC6743BD4D8}.Debug|x64.Build.0 = Debug|Any CPU - {E10E3BFC-F5C8-4E70-AC3C-1DC6743BD4D8}.Debug|x86.ActiveCfg = Debug|Any CPU - {E10E3BFC-F5C8-4E70-AC3C-1DC6743BD4D8}.Debug|x86.Build.0 = Debug|Any CPU - {E10E3BFC-F5C8-4E70-AC3C-1DC6743BD4D8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {E10E3BFC-F5C8-4E70-AC3C-1DC6743BD4D8}.Release|Any CPU.Build.0 = Release|Any CPU - {E10E3BFC-F5C8-4E70-AC3C-1DC6743BD4D8}.Release|x64.ActiveCfg = Release|Any CPU - {E10E3BFC-F5C8-4E70-AC3C-1DC6743BD4D8}.Release|x64.Build.0 = Release|Any CPU - {E10E3BFC-F5C8-4E70-AC3C-1DC6743BD4D8}.Release|x86.ActiveCfg = Release|Any CPU - {E10E3BFC-F5C8-4E70-AC3C-1DC6743BD4D8}.Release|x86.Build.0 = Release|Any CPU - {D902FBA8-5B07-452E-9A0B-3381D49ABB9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D902FBA8-5B07-452E-9A0B-3381D49ABB9B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D902FBA8-5B07-452E-9A0B-3381D49ABB9B}.Debug|x64.ActiveCfg = Debug|Any CPU - {D902FBA8-5B07-452E-9A0B-3381D49ABB9B}.Debug|x64.Build.0 = Debug|Any CPU - {D902FBA8-5B07-452E-9A0B-3381D49ABB9B}.Debug|x86.ActiveCfg = Debug|Any CPU - {D902FBA8-5B07-452E-9A0B-3381D49ABB9B}.Debug|x86.Build.0 = Debug|Any CPU - {D902FBA8-5B07-452E-9A0B-3381D49ABB9B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D902FBA8-5B07-452E-9A0B-3381D49ABB9B}.Release|Any CPU.Build.0 = Release|Any CPU - {D902FBA8-5B07-452E-9A0B-3381D49ABB9B}.Release|x64.ActiveCfg = Release|Any CPU - {D902FBA8-5B07-452E-9A0B-3381D49ABB9B}.Release|x64.Build.0 = Release|Any CPU - {D902FBA8-5B07-452E-9A0B-3381D49ABB9B}.Release|x86.ActiveCfg = Release|Any CPU - {D902FBA8-5B07-452E-9A0B-3381D49ABB9B}.Release|x86.Build.0 = Release|Any CPU - {7780907C-CABF-4CCD-9039-7C17CC7391F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7780907C-CABF-4CCD-9039-7C17CC7391F9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7780907C-CABF-4CCD-9039-7C17CC7391F9}.Debug|x64.ActiveCfg = Debug|Any CPU - {7780907C-CABF-4CCD-9039-7C17CC7391F9}.Debug|x64.Build.0 = Debug|Any CPU - {7780907C-CABF-4CCD-9039-7C17CC7391F9}.Debug|x86.ActiveCfg = Debug|Any CPU - {7780907C-CABF-4CCD-9039-7C17CC7391F9}.Debug|x86.Build.0 = Debug|Any CPU - {7780907C-CABF-4CCD-9039-7C17CC7391F9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7780907C-CABF-4CCD-9039-7C17CC7391F9}.Release|Any CPU.Build.0 = Release|Any CPU - {7780907C-CABF-4CCD-9039-7C17CC7391F9}.Release|x64.ActiveCfg = Release|Any CPU - {7780907C-CABF-4CCD-9039-7C17CC7391F9}.Release|x64.Build.0 = Release|Any CPU - {7780907C-CABF-4CCD-9039-7C17CC7391F9}.Release|x86.ActiveCfg = Release|Any CPU - {7780907C-CABF-4CCD-9039-7C17CC7391F9}.Release|x86.Build.0 = Release|Any CPU - {555A87B1-5FFE-4F24-B665-160693901D25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {555A87B1-5FFE-4F24-B665-160693901D25}.Debug|Any CPU.Build.0 = Debug|Any CPU - {555A87B1-5FFE-4F24-B665-160693901D25}.Debug|x64.ActiveCfg = Debug|Any CPU - {555A87B1-5FFE-4F24-B665-160693901D25}.Debug|x64.Build.0 = Debug|Any CPU - {555A87B1-5FFE-4F24-B665-160693901D25}.Debug|x86.ActiveCfg = Debug|Any CPU - {555A87B1-5FFE-4F24-B665-160693901D25}.Debug|x86.Build.0 = Debug|Any CPU - {555A87B1-5FFE-4F24-B665-160693901D25}.Release|Any CPU.ActiveCfg = Release|Any CPU - {555A87B1-5FFE-4F24-B665-160693901D25}.Release|Any CPU.Build.0 = Release|Any CPU - {555A87B1-5FFE-4F24-B665-160693901D25}.Release|x64.ActiveCfg = Release|Any CPU - {555A87B1-5FFE-4F24-B665-160693901D25}.Release|x64.Build.0 = Release|Any CPU - {555A87B1-5FFE-4F24-B665-160693901D25}.Release|x86.ActiveCfg = Release|Any CPU - {555A87B1-5FFE-4F24-B665-160693901D25}.Release|x86.Build.0 = Release|Any CPU - {FD4B4018-7283-41DB-AC57-AB62760269FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FD4B4018-7283-41DB-AC57-AB62760269FB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FD4B4018-7283-41DB-AC57-AB62760269FB}.Debug|x64.ActiveCfg = Debug|Any CPU - {FD4B4018-7283-41DB-AC57-AB62760269FB}.Debug|x64.Build.0 = Debug|Any CPU - {FD4B4018-7283-41DB-AC57-AB62760269FB}.Debug|x86.ActiveCfg = Debug|Any CPU - {FD4B4018-7283-41DB-AC57-AB62760269FB}.Debug|x86.Build.0 = Debug|Any CPU - {FD4B4018-7283-41DB-AC57-AB62760269FB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FD4B4018-7283-41DB-AC57-AB62760269FB}.Release|Any CPU.Build.0 = Release|Any CPU - {FD4B4018-7283-41DB-AC57-AB62760269FB}.Release|x64.ActiveCfg = Release|Any CPU - {FD4B4018-7283-41DB-AC57-AB62760269FB}.Release|x64.Build.0 = Release|Any CPU - {FD4B4018-7283-41DB-AC57-AB62760269FB}.Release|x86.ActiveCfg = Release|Any CPU - {FD4B4018-7283-41DB-AC57-AB62760269FB}.Release|x86.Build.0 = Release|Any CPU - {8A98B2B8-92E6-4649-92D7-77FB11E3C9C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8A98B2B8-92E6-4649-92D7-77FB11E3C9C8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8A98B2B8-92E6-4649-92D7-77FB11E3C9C8}.Debug|x64.ActiveCfg = Debug|Any CPU - {8A98B2B8-92E6-4649-92D7-77FB11E3C9C8}.Debug|x64.Build.0 = Debug|Any CPU - {8A98B2B8-92E6-4649-92D7-77FB11E3C9C8}.Debug|x86.ActiveCfg = Debug|Any CPU - {8A98B2B8-92E6-4649-92D7-77FB11E3C9C8}.Debug|x86.Build.0 = Debug|Any CPU - {8A98B2B8-92E6-4649-92D7-77FB11E3C9C8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8A98B2B8-92E6-4649-92D7-77FB11E3C9C8}.Release|Any CPU.Build.0 = Release|Any CPU - {8A98B2B8-92E6-4649-92D7-77FB11E3C9C8}.Release|x64.ActiveCfg = Release|Any CPU - {8A98B2B8-92E6-4649-92D7-77FB11E3C9C8}.Release|x64.Build.0 = Release|Any CPU - {8A98B2B8-92E6-4649-92D7-77FB11E3C9C8}.Release|x86.ActiveCfg = Release|Any CPU - {8A98B2B8-92E6-4649-92D7-77FB11E3C9C8}.Release|x86.Build.0 = Release|Any CPU - {9F027E40-426C-4059-8269-16FDA79B56A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {9F027E40-426C-4059-8269-16FDA79B56A8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {9F027E40-426C-4059-8269-16FDA79B56A8}.Debug|x64.ActiveCfg = Debug|Any CPU - {9F027E40-426C-4059-8269-16FDA79B56A8}.Debug|x64.Build.0 = Debug|Any CPU - {9F027E40-426C-4059-8269-16FDA79B56A8}.Debug|x86.ActiveCfg = Debug|Any CPU - {9F027E40-426C-4059-8269-16FDA79B56A8}.Debug|x86.Build.0 = Debug|Any CPU - {9F027E40-426C-4059-8269-16FDA79B56A8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {9F027E40-426C-4059-8269-16FDA79B56A8}.Release|Any CPU.Build.0 = Release|Any CPU - {9F027E40-426C-4059-8269-16FDA79B56A8}.Release|x64.ActiveCfg = Release|Any CPU - {9F027E40-426C-4059-8269-16FDA79B56A8}.Release|x64.Build.0 = Release|Any CPU - {9F027E40-426C-4059-8269-16FDA79B56A8}.Release|x86.ActiveCfg = Release|Any CPU - {9F027E40-426C-4059-8269-16FDA79B56A8}.Release|x86.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {BC1F1EF3-7152-4284-9EDA-AAE8825388A3} = {EBF19CC8-39E4-490E-8BAB-4B0C2FADAC98} - {29BDDFB0-54FA-4057-BEC5-5B3DCD1DE212} = {BC1F1EF3-7152-4284-9EDA-AAE8825388A3} - {09E88199-24C9-45BC-B043-7D636D4BCDF0} = {EBF19CC8-39E4-490E-8BAB-4B0C2FADAC98} - {8FCED8D1-D6CC-44BC-9CDA-9F0C1C786453} = {09E88199-24C9-45BC-B043-7D636D4BCDF0} - {79343A9E-5F1A-490C-AA3F-68B1F03A8CEF} = {EBF19CC8-39E4-490E-8BAB-4B0C2FADAC98} - {DCB6AB5F-4B6F-4B2D-83CB-F539DA614A5E} = {79343A9E-5F1A-490C-AA3F-68B1F03A8CEF} - {945EB98D-95C4-415B-8C00-C7682683FAA3} = {EBF19CC8-39E4-490E-8BAB-4B0C2FADAC98} - {23683C7C-23E6-443C-AB41-3F28EA1B2FC0} = {945EB98D-95C4-415B-8C00-C7682683FAA3} - {17ABC2A8-9E3B-4CE1-AD13-A3780B0B07F0} = {EBF19CC8-39E4-490E-8BAB-4B0C2FADAC98} - {0B1AA7F3-09C4-4D8B-AB54-D500BF0B2537} = {17ABC2A8-9E3B-4CE1-AD13-A3780B0B07F0} - {D9FA7414-F8AB-4195-A483-474BF96467E6} = {EBF19CC8-39E4-490E-8BAB-4B0C2FADAC98} - {2EE9F419-409D-4F43-A541-BBBAD6FEDCA2} = {D9FA7414-F8AB-4195-A483-474BF96467E6} - {520D01A8-B487-4B27-B33A-8ACB97F57BA5} = {EBF19CC8-39E4-490E-8BAB-4B0C2FADAC98} - {B4049A31-CEC8-4310-957D-1FBCBBDEC413} = {520D01A8-B487-4B27-B33A-8ACB97F57BA5} - {79E8F708-7806-4EF5-A8D9-22AE31053124} = {EBF19CC8-39E4-490E-8BAB-4B0C2FADAC98} - {4D8EA982-3285-4637-9010-310FCA5B254A} = {79E8F708-7806-4EF5-A8D9-22AE31053124} - {D065CD06-4296-416A-B47D-274D0B5F82E9} = {BC1F1EF3-7152-4284-9EDA-AAE8825388A3} - {381A72E9-DD7B-45CE-BCEC-D70DC3BEAAEC} = {09E88199-24C9-45BC-B043-7D636D4BCDF0} - {EEDF9F97-98D4-465E-B8FB-0DDAE9219F72} = {945EB98D-95C4-415B-8C00-C7682683FAA3} - {456B57D9-E3F7-46DF-AA27-5518C7647FD1} = {17ABC2A8-9E3B-4CE1-AD13-A3780B0B07F0} - {B267E37A-96A5-418D-9A86-76C19564D205} = {79343A9E-5F1A-490C-AA3F-68B1F03A8CEF} - {370CBE6A-76A2-4F49-BCD9-68FC79ED4171} = {D9FA7414-F8AB-4195-A483-474BF96467E6} - {6017E174-2FC3-4BE9-9279-855BA7481AF3} = {520D01A8-B487-4B27-B33A-8ACB97F57BA5} - {E10E3BFC-F5C8-4E70-AC3C-1DC6743BD4D8} = {79E8F708-7806-4EF5-A8D9-22AE31053124} - {D902FBA8-5B07-452E-9A0B-3381D49ABB9B} = {FD5B4D88-8BDA-4513-BBA1-9F31B9D64BB5} - {7780907C-CABF-4CCD-9039-7C17CC7391F9} = {FD5B4D88-8BDA-4513-BBA1-9F31B9D64BB5} - {EF1CA185-B3C8-4F0A-B134-1114CB51E97D} = {EBF19CC8-39E4-490E-8BAB-4B0C2FADAC98} - {555A87B1-5FFE-4F24-B665-160693901D25} = {EF1CA185-B3C8-4F0A-B134-1114CB51E97D} - {FD4B4018-7283-41DB-AC57-AB62760269FB} = {EF1CA185-B3C8-4F0A-B134-1114CB51E97D} - {8B1B77AA-ED59-4B6F-8926-A65AAF28A713} = {B46172F1-8E97-4934-9969-3957B9129736} - {8A98B2B8-92E6-4649-92D7-77FB11E3C9C8} = {B46172F1-8E97-4934-9969-3957B9129736} - {3E629C69-B021-4899-9A61-C3696310706D} = {B46172F1-8E97-4934-9969-3957B9129736} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {CE7AD0FF-F1D1-4E34-8DE8-FBB2E63AA49F} - EndGlobalSection -EndGlobal diff --git a/common/DatabaseModelGenerator/GenerateDatabaseModelLinux.sh b/common/DatabaseModelGenerator/GenerateDatabaseModelLinux.sh deleted file mode 100644 index e98983480..000000000 --- a/common/DatabaseModelGenerator/GenerateDatabaseModelLinux.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -echo "Creating projects" -dotnet new console -echo "Installing ef core" -dotnet tool install --global dotnet-ef -echo "Adding required packages" -dotnet add package Microsoft.EntityFrameworkCore.Design -dotnet add package Pomelo.EntityFrameworkCore.MySql -echo "Generating database models" -dotnet ef dbcontext scaffold "Server=localhost;Port=3306;database=unispy;User=unispy;Password=;TreatTinyAsBoolean=true;" "Pomelo.EntityFrameworkCore.MySql" -v --output-dir ../../src/Libraries/UniSpyLib/Database/DatabaseModel/MySql \ No newline at end of file diff --git a/common/DatabaseModelGenerator/GenerateDatabaseModelWindows.bat b/common/DatabaseModelGenerator/GenerateDatabaseModelWindows.bat deleted file mode 100644 index a3357a29d..000000000 --- a/common/DatabaseModelGenerator/GenerateDatabaseModelWindows.bat +++ /dev/null @@ -1,5 +0,0 @@ -echo Installing ef core. -dotnet tool install --global dotnet-ef -echo Generating database models. -dotnet ef dbcontext scaffold "Server=localhost;Port=3306;database=unispy;User=unispy;Password=;TreatTinyAsBoolean=true;" "Pomelo.EntityFrameworkCore.MySql" -v --output-dir ../../src/Libraries/UniSpyLib/Database/DatabaseModel/MySql -pause \ No newline at end of file diff --git a/common/EFScript.txt b/common/EFScript.txt deleted file mode 100644 index 567598932..000000000 --- a/common/EFScript.txt +++ /dev/null @@ -1,2 +0,0 @@ - -dotnet ef dbcontext scaffold "ConnectionString" Npgsql.EntityFrameworkCore.PostgreSQL -o Models \ No newline at end of file diff --git a/common/Icon/UniSpy_Logo.ico b/common/Icon/UniSpy_Logo.ico deleted file mode 100644 index 0bdce3c8c30ea6ed8a2922109f11e024ff7238a6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 51196 zcmagEbyOTd*FM-Iz%UH%4ucb1f@|>L?(PIhkl+j&2rj{c69`FgLU4i;90CLg7Aypp zU_o}?@7-_D?~mQHeQLV;RzH2~-do*0_1rE1009U900_`E6tG4g4xpv~Y#;yz&`|;a zEAIcw48Wue04T7c(FFg+vx5MD#1Q}(82&5&1_OYk69Dk>{a1Fz1OR5&|KR_t&lv*% z61@I{|F2vL1Auei|KR^`84m#RL;n}g060U@{U!b{o&l&t>-m`azj(CF0s#6Y01&6G zsf-J!f}<_PRaH^Y{g1Z){GbqYRc+p6fUfwo)%6w8^(Zbbx{wePqmRpRaL@>1F!(D1 z!Hk6!jE!vugAF4PQdn63MdA?Z{znsmkFKU3aQ5)Y>EP%q=+<;u@G@won{?(T~r6 z_>(f**Y+|eayNwEL3AUG83*mXwI7RK)q+#SV1AcHq6z#61$)m3GKv_7qUqV?nHdX< zRnNcKA=S$U6ElBL=H~wyW~p)3+OKdta(E98!pCi!xH@{aKYRDDkhE83Ne*=DmT&YC zatp5_E9t($dF}N;w)dte7CB1JxaLQTP4YuxLu7Jd?9A+C`p=M7a1dK+Y}Kgl^uRFl z%S~Rr)82VXlWdvm1{G8%qC@!gS-D~J{eWd_pz!SyyT9ehaZtEH8v<{KM1+NJW8MMh z?-)UadvHEs8Xq5wV3(NHsy#SGu1j>FBYRYi;gxU*$kDXek6PjPk`mPC93$H$I@gcD z&N(9NLN5fQ^lUx4wG(J`mql$G)2y3eLT_=AYrYV0wzo88_HRs-rga(h4?fWGlr<)E zcXuDjcbqAhG}UBgIs6J@3{;ZmW|M>mGRj{T-x^}E7QsooO~}+h?T@oR^+HiEM|c2C zJ47wx7u*eH>F)%57fPET3nXL=_(^b!%?Skhc-8*3fZhF`3_meTjA<@kCL9l19&pHJ zgh7Qe+wD4Pjsi4OXjrYQ!`D6DXZ8G^bc>05aF&&2DE`FC$R5n5DGp$NS#V}1YMmxZ zl4{OjzNJ^`UP{Ey^;k-uh)BF%_AE;thu zn!^2<5wMT%x~{?)jcXvc0|>nI^6!jf0ee1wv-}Q>>PL)ZO& zf1w72IURwl{B^W?Pe^|JK&! zT`HTLH!^?8Af5HPUcFtEDQ=nc-ySuv zA2-+7>@W#bKF2B2U&Qbjd(9Wvz2g+{jxzGcPKy<9+A4X_(!k0BM)(f7bPTr}LWqyo zH@8#ASg`VR@E3INt?iXty?4EC7XgMa#>*pQYXm$@8|ONXl7MvgG`ZHsSWaspkNId6 zH$17dxoI$`h~sNbEGB??V;0Cp3QY=HoWi4apE}n}IEyar+B?nKuI383?4yz7y2$+$ z{u%f%+xjaIW!I3pWA55y*o2XxW%kLi;X}}imxnEh43Qshz-q>_A4s_bgiY;g^SygE zWoCDHm!By*b2X7J^~ld(o8(i}Yl+TNm9G1keCUuIC;z$qhItoYC<@NCpJ|=;S`@&c zkDB+)V%@Ed(B#nrd-30jR>D2R5_)FsSf>nNVbd^Hs)$x=-_bKm?NeLovZVa`Gd>}|IYTA?Gwo2GqZ*)69J0_8S>#V{5%r5V9VgQ_=Hg}?Wg4ntxHS>d67^+|19^-J6vPB-Rs}rCp=p&Th^K=lWhM zo@%`M7UPg$3jUwl@Yv5E+~#9ltx4p54pF{)?F{nsArsu>O&^)cy=vmA%P%FDGaoz; zRa1xF95@pjZthTfrd~H~CFsV#W2kG;56F4{LtZ4!vXje)7L)6zR#!s+m zQtN}jzt20n$YCmq6h;C&Pp@BV;tE|ORSaZXjzZGxIepGF=yR|B{;{`XTP4O!*S|() zXIMUvhl*>hbB6!OdC#g8zVDusnyH^91iYBn+jr9(AXfJvWt0{?^XrtjmufMR=-ZzmhaKJJ{G7x zOqdy2XNq>X_#1qnoTIY!RkZdvtNc#(vEa*+GKL-JaRf)D+M$Sy)8Xki4C>lmwJ%N& z*advo4v?-Fb9TCzwit%@YH85=^{f2f)wwEi*QhaGTF;(%ZX%Ag<5J%8e}ufXWdpcW z>lrfgkJ>XRM^BS#11ct(18yT0tTBH!j8F;sxx@tZ6%)DFTkdT2!B>A3p0YQA5I zp3n*^b{|E2Sv8e=H^FQ!E>d{(O5*t6Lom;=i(0aK$uFu-efGV^1ieHGIGMKnNYFjfnohLV?Du}Y%D|xOcxAIAs?`ra-hW*pHgYf2BMrXJ# z-E&%Jv7c8t*Py#oiRY~JAfk$cSdWCTb5gLUJ*#fD)Fwv8G|WA<`d?w;N8-6ADU*|w z$}q*At2EUqm7cH8W^LHkeGOC{bOc}#@DK**Q3@HwuW)C9Z_1)=n9Dx87?v}2;Ew(6 zEvL!z?hlr??%ho;6pvEO2KiQ?mbxe6o4-U7UUfg^{494_fRX*GMb1tS-sWQOyw+KU z#~v@v>}wA%&ieG5sesrxw;9#LU!V`4D~@K&Ny}(rjj~Q)C7z+y^$2{Ix^ifj&r9-p zl>654?#oun@^7M#M>s%TzpX|GZCc*jsa3*vud-U)du=!3RkVL4at-~|q{L_6J99Av zORc#DlI0K{8}JAXJ;I$^NR%qKNu~8V{ct066)-;wGIW_fZeb(ur&cHo3wCYBF(LoHJwQ)+~DOfE}89naUB`k}}gxNBW(hZCd%&goNU; zk%7E44ot2_MXac=c67=9Jzk2gb*tcuhknIKcYl5Dhq{CXm{-8Vn~aDgvCxh}>gN_F z8AZQ3(_-E~;y-`bYw%z`Hd^+z23K39aLN28cQT(~7YkkU?ZE0DOV|32u%?qd62)ex zvZj(%GFbrhNVV_0D+ckkWv?*`t0Qh6Z4FlAQo#|YSdjFbYOO1cj zbej0HNl5-15hp*5u&#LUGVea-jqy!mKQ^)i$>2HCo`qehT|M1+x*wyrcOS5pGIvY) zTgax%;W(?+w6=)ymj3TIZzh4XC2{n$A5c})RH&A>j`;uL7qI{E3%8HP`Tzg{{$KdT zf4K&lL5%zl>F~zFav~sb#l(z4AgbW-Cs1e-4E7WX?V03DI$Qi9paH5RC|g zB{p^}Hg*pJLHa*j3=OEm!C@vMiig1laB+!2AZ*}2)U+Rgz`(#jU-&;d{XDBItj>=wmd0qydA``pH0`G6W(33+n|YrWYnA0tDiR!z#DniEHBk_M3_dZbULr5ZCJ`?7(pN|32RejrSc{odjXwLQVm6}fhH0#0k z;$Qvc!zuQf;Pr*uUxMX?lw{j-{{8*dWtUR|@WVqD$B@|r@w@NWe5a58!pg28&~}^+ zw(BRqR=`UVL|f)xnhzcxKu2nbx4TmU`XBQBEyr zIke~ql8pT~Kd}7PLY;aicaWB?{wqVx_$bZsA`8_X!*gcnW9AoMC|rDl36*zBC^&qY zb=ZwQ2q{ffX;@IJDL#@ul`~c&)l5q*A}4LxRnzf-@sZ+wWKoW@=2gcUCS?r8hN;L*;R^wF8C&^NwAtD88~pUm4%JdT=xUuV3Z zi2DAdbtzY;y_Zjk;eS*;yTZRfaOSGbd&o^2nwwxdH)DF>LL5CU9WYYKKr2Ku^-h#X zDb|H>c1@0Uoo_|81M{-z!zsA|VKDUFjAQM-xMmGb(6H!1S*GA>gx(~vgBllah$epZ zm}B~jCt%m`qcH6y`|+pWdInj1+5b%LY)?+y221c{NcSI+?g^<$Z3GT)WllHCzM8p8 zEB*Zu`=j4W+CmY&Q*QCYm-%i&kE&&n+s70y7bpvq5u3Vo7hl63fAod3Q# zVTyfrO3=;MWIeGvGWvZuBJlxubPGg@)6sZxnTB6pZ`uo$(oSAPlLr^}^Mf;bdVUHc zPG`F-uLdApD`BSw4&JDT(nZz5J^3vlrojXSdna=1)+Qm%jA^q$a#Te;XqL?_W(5iMO2zfGwud)nP%-o7ceZ*45wXGf@87B45Kpbwm^#iX6#o~PJyqMVU~ zPsfD$J~?7oSD{M#0>jz1ZR3HQ6N2(B%46#9vo+kxw?)DgrLUsKK=wr)Wrk3x+S;51 z#7{XfGW~92fvQhdADT#gdEUb~`~}$FYLrB=KFPSbX);yb-Ov0~C62wwrYr6y|F=yx zvu@giI7aUGysJf#@6nUvbBE!vC0!RtQc1P7L$}W4w{(hd9}t4Vc?&yM3)tm-^LTA& zdB`wsgwOeat21|`2%)VMPBsC4zmKgl*a|Y0f~+1euv~p0ZVXIx>>qHF(Pp&#AvZGb z>^vSz@n(`TvgogyHp&8J=mzT2a0VXZSO>(Q=IB2T6BC!?d7|*6`b<)8nyySH%@k@$ zD?yE{lbWEQ)FRgM79(uKhXIsNqw4GXuMg&O#y@XX z96i9t0SJCFo!5s??~!xjHO__Fk6XViQ+;s$)9Q7+d_;ebISSBAOXXtQMdcW~eIPnu ztnLuLc)q$U=s`!vxm=3f z1z+j-qwv2G2KX}U=8YM&6i@gvv)`)zDDe#n3Jnc?{`|QR_SxX|^?|#RP-&~JqhsdO zve=Y?dcZfJDDdn0zI4hfmey8Qzn2n}-uAt>7CVQB3PwiMTFY3H&{>>N&hG&H#D^jU z)H$|_RrO6zyJHQ1H8QhcEmVf$uImnN8bGAo+ui*TZrF)mWV0qiN5-2g4p3pLIS`)2?%RF=lJ`fog zEau+dWBt$|YJ{&N<$jCaVD*-YCI5}}^O2Yl!HB`J(5O4mxsC1D({q>A)|Gp7#mH9g z;3m?c!TnRY*-nsHmbwS4O|b47BGOB!x4{JY6}6en@OR3iEbwsO(N9)?A=pikxHfaL ziOg;6xlCx2iHwu7%1vGHpMS6)n7YZ^eca zEFZfn5oNu zM_nm2k+#f7->Qbv;p5sQIo4~=Rbwguo`KOs}W*Ef1aSTlx#2>6*$~Zcun}OX%>75)o z)ACr);$L{yZ{+Fd=sj5^O=)I8b+k0t{AeF?$nYgB8_>yda`X@t*%^ESXragcpoiP{TD=sl-tKuJo zle&GA8Z8c|2&r&Rso?sMOcFaK1r>oKBv&eCx3V3y#4Rb;K|^>}XU7LLoPmV@v}fzL zKj$6tr_FhL)T!{5J0X}wcR%4WjYs;BzxFp5rgx1lWenX_9DB{?VE3dVO{3M6LJ%#r z*B5)ILH{4L`b=XCHddh!E;j|b?h$iVlPR;3vZB%3BV>fe-evI}WudZv~y-726Fa3HuD-$DwrSS0jHv zvwlq|UfLR(=y)|y2gPmR_iI*iM<)vE7Ge~z+vjDY;tWFer9Nx*9`f?5Dl8le zEv4K9|9+ezG{R(*R6a8)W;V6h`spL#H>Hgrgv&3J7$w=?^ZFqNQkjZRdKbBZoYk-& zF*i>5Jr=SLn+>{?V89%z24fli&UX-L+>{U}t2q;Fc-tnnxM2>`0}16GN#j2!V|R)E zLRsdE81|0%=;Lv~Rh$eM0FDe4-&~+3hR#0@%;P=JuxVL-F!H>Y>^c`+c|;#EU32|O zCX95b5#kn`V|)Tny&AUb`gl-5S2Vz2raL?neQ=Rm?yw(Yg3=G#wT$ke%g47 z+1Zos|7r!R!m9MzODh&bfGagVRs@s_r7P+h`#{M4>Jqv3fi?7Bd^RUT;k}d>TSYn| z))o4KP}c|EJ<%pa1a<-)4GO@A9o`fsN3R-J#@^?(C&hP=<$2-yo=Uv(qSYo{TL7J> z+BO{B$K!_d?+`_@SpjvZm&wWGva2|Dl`0U?2_*tc!g&L$h+3cq@0ef5 zKaATe_5ZWNsgwP!Zc1;+;;gaN@!}ooevn|_^?gVI=NAmp8Y{?bML%Q>q!*79eYzST z!5|wW(tzhJYJ4UW=+5l4W{{a?&VcN@2GHd(#2>B#ZSC{*lgp;exSNfD8!H+dEX0p za?6!f^KX@*RB5K;XM7m<^u_2Pe|2_tR{AB`mq!r=E@P7FUg>s3GTjIE6Vo#1jIZu} zVeiNaEPlPVp!88q$d=XeYJc+ycGdDWFw&c89@=l-k9ZhviRG}wYA;UqmRkGZk;UeP zH$mJ^l9C(RS<_){{C%l55_VyKp?cHvrhD&>eFEuGbZq}EuLJ9*Y|t}_YL$$Wp68C0 z?mV~{zjn2@b#1o1Q*T#I`kQA&d!ljgVKbTkcw5%t{r)j8|KCpMpDQ?Zdqt4oU>>3% zYNb)%4wu0IUOxU%iUC#NK8zON9aX>4EV$KvhhJ1@KNER*SESEEs}4O0 z9_;zxux0!apMPNU+R`yc$`3w{)s$5VhZgz{#-ms*v^H|@N3)g%6g7BSqM`>gMU0pn zb4o7`>6{ENd%$nBs5e`)K0mqQ276NDdR!Ad)XY?NSRB9cu(`%lzf$7BGxCcG+1Iq2vfrw#(k~qk1(*!-0APi9K%EI+?~@BuxuA~dq2gM zFXX(xxjmPClQ?1vJ|8r1{CFvloPK~*E1tDeVv?Y9^BCB|_j`PDw= z{6h5XhgR(G8zS{Z5&QWGW8l|0-pNM3@>=Wn143@A_$89opxBHTU%g(Q3m^Fvb?v34 zbUvA{{J|3CrO*@bzK(CT-B4P9D|p4bXm!I-t!w*vdH1eA<`!MXgv(r#%Ma%DN8;&e zS%h7WH%B-IapLWoes6w(2%vL+ugy3~J6<}QnmXoa7}cS0=ykZakZ$cu)yM1nv4M>J z;B@`$L@`|=WaRpuJC!s-xgG38$A!X? zF@M5pTyCd`7u+DsEMIG0J_ph{NKxS4sd-}7`oTM%B z0TDH_{~yEmUDtDZREfC6C=0aZ^6-0X{>TXre_%;5_REL^ul=RYv5L-p^RQhO%~@bFlCYdE zG_`J#I~rnE2(^j?FppZ`6C{SX%dX{dEg5yr(Gbp%3)kZF+z@Al4if$^Y@Y~a$NrS> zw74?2TyhRFW>qz_Yboh5$t6F%wq#4xfV`C;pXDvqrLjbIYj8}EzJJlv`JON5Aa}Qz zw#w-#&X=^(X-r?$Ev=2QA=d%*K4p~?`PUANyBta&`)5BJE3}gDm{}Khc*8ySPg)kz zgy`c{-m!bTI#vdrX+;z!8i6XX`Uac)69pFtp*DOhldeaKM%jOy@KM*|9_s$@{}H$Z zY_e!u_KuE>CKgH8d{U028iRxROx|J7<*P|#6pPa}Dl;K(6s6x)-JLU~h*3$^Vq)ep z)J>`1!%qJxTaO80)B|K!6IJ6~4c)jOM&?$7YV|5*qakT9qBg3FWiCv^$!x1%Xgf27y`sFM%W`AZouLCSzj!k`${J4zH! zg|6`x%UXqvwWhq{*TiFs(38c`H1WQ9x2@7819o}-bh^q@FN;Tgk&aPz%0j$1Mw%|` zDdp(v&v7I^3*sq?$Yq&IbLlY3gw+$BwUzUB7$2 z_$F&N#76Zc&$Qp((@CnbceEZsJIRg&58i@uRzM7C_PO|tVpm%|I4rV|r7u%5I_xg5~R zOln=#c~wgL+x3GE_I%>bAQt2L6F$0NqD+ne9IU5~$_+pAF*#4B^@ji1$5OmgHBzm? z&uku5%rDRlI8G;iw4nHl_O*W5iPLE|CYiAbg=9kY_QdrgwodUfXpMDJcjJDJ-Dw^m z0L1d}_SM4*lbH=+X1nJxR{`YANA1b@A>+rIs@ZEVL$5<7 zV^1U}zYs0Q`5C`!&q0Ql3^=X1esz>6k^dHJ%gRSlUM7_)`|_!N$I<{!t?lPXzOUDQ zq`YRlJ@A&CWPwxv_XODJ6s@7KuI-vKG=5hGR=&R zG>N|cbZaz=ld0lQ#!DK!z!F!kfc|<*WhHWwbIOhcg)tVcLWhp|{aQ{Tu|6i95`{-$ z;C0i_ZRWnZ|4FH74A|JjvOOruE97mrNOev^R_4 zh@C!H^CCf~$q+te2!T53Q)+4Fs+edeKuYL};rpz)wLZ<+vlfG&zo1_sxk41Ho2i~3 znfL75Xx`iJWA8^cyL+PS1_7O6Xi3CI&_C z8b{Dz<8yi_Fg&!ao_kOI%YHMx%Vcw2tu<#>hesL$E!Nj&(z>Tt%;e%|abF-rXPy85 zzmA|Y@PPS?N=NhpF5tge(1VVb{xpNB_Ya1?6I^6s5NNl0gxJ$EmDCb2%Olf;Ocu5U z>5+@K2Tx@xjuv*jznqB;dZ#snWYX9VUvRKJ%}_8QQq>t1i)0p%jatsxy}9GZ!G1>} zYhBl1BRwHF5izoyv$(w5-1gza2N+{%42H9YJ%3Sr-2U(=MhDU(oj=3^wE-$1d~VS# zjDos*a@wGTi%3&#Weo@vUE@xlAs5}$J=;`%Ld=c&o`ZOugBnQTi>PPJa0Rv4Z#9#F z*8-nv@~Xf8VxNKn;wNb{%)|*vvT^w6!uhX#HTOU^?ndo&O{XoWhJm2}cHO%P1h}-&Xw1!(Lm@ zuh6o;Ot1de_TZf_Lr1g_(wAL|my&wfj`5pnn?G?hy{6$~9vQo@T}Gu}c7=C=Uq1aU zJKe*!qn%$a;!blqQ5#5;rL#r{rvDW(bz)Ya@F|LFSL~lc?tIY75uI0lkO+0dHH!aL z2cl({TXvQ|`R%ym<%^Nlli*J^c18YWJEXFHfh(m}ulw&0943PJ^*WP%9Q#CEtBEl_ z;xu(`-lYza|H&q2Ej@vt6!rNsuk*JueNCji%OU>HizhJ4x-fvM2 zDW@stK)uAgmld|-GzOkYv%e6{*_jZQ2T_1_*o7aYQis@BANfi{E}Si10po0(c_rUV z7Sy;anX3p&jI&hCCJRnpl%$fU=y6@&HH(XF~py=OafmG$Ue9o-2&En^6 zj%`ewYpQ>%4a5wKZpXa*aq)`WIV2GiuKQdAtWT(OpkGd=spsk_rqhDs;{!f;??*yL zph)H-^$<7`IQaMTU8ka{m4%8BCP!-pMeVoPKc~RLv(ZJ%A{o;8vBzj@F?f&mT#CQ%X({#>v zOF@sKR_S`Wra79uCcmZHs4Sq3pL%(c!+$Oc?3L+^GIR|&8T)*Ai*T4f6>=vlxiF8s>b=W*_D{pJ?jX#v`tO+Us5c3L#zoWkz{lW{U+5sQ zrn>)26r(8Orc`AIoQ6rZRXyuJ;*ZYVq%j0`q|54*q=|12C~Zf3zsV(pHxW*kqN48Bs)u7Au!@IM>-5+jf;7&#*+?qb1Lz9M)WCslUEgs$tBQ278Yzhh9m%8tr-P=Rj4y)@DFK56FP%b5)E z8dN1wFX9CiH;J_opHVY~w^uxWjr~)o1LuGAW>|^!rW|6J*G*GwWZz z%{6nAt2&C2m$>i)t8aW;QZL(0h9WL_ll${sj*DpxJ8RX}Ca*VZB24Ew$yx|9FDaG= zXR$z?kp#u4tD*1L;jsMqXT68UP7EIkjOsg;Sme#Nw1mA5HuO>O&zE;Vu~!DJQxi`-HMb`+QoN`mrkvb_1MvS}Bs zAS}1!O_sv?nuK_ET<1@HxvAB6-f5r7?2tq3-c4KNzrc~!i;aMQRSQ=W&qpk-+E11~ zraxv@sIh*v<1Wemg26dRARK+a%1U{UnQ`sC_0DKzbvBE^b}3OIJ?q2a+Pf(?oj4*} z2>FrC1lJ~a>-aS@^}D&|^T#kAmfXtQqIZq?zw+aBOJWS|F5EAbK~BO4!-@i@%9<=x zYow+%x6dqBc+X=0M0{p55tZG0JQ4I0^!-`6p^55`kLf% z(SY*9sY?DdFJ90ls#y>2cR@DY-v|SxA zrV@(Wu_-C_GEBt_1FGMErt;+eb2b*7`iN`KFUouH8N-9xNGpe027M#rt_nNAW>c(t z#>&~m>?|Rl*88lyI$Ks>tGK8uNgL4YK|gqpp?SZN%y9~Ft~}pw;G~$H9sfB~lvL@{ zsVf3?S5|q^(o^rjP1E1^DMt;huJ&7fbu6MQP(vkGw_nmJRTn4nGt)BqZiiMke0<4} z|HzT);!Wn?cSVURBIp-PA9DM|cl1%jXC`|eV^JhM39f~kNi(Lqf;b^uHlkcjgPSckQX8nxby2g>A;-k% zKpTUd$>CeBm|+mdFK>wtVj*{aYcOn~B+m{U?=4Ks-QaSXvyBJB*1R(U?ff_XMXf?m z??*S#L;s6Umurs^MdlKsBc@|B`MC^IjQ%cP@>fxZp`Gys6jz^D|Ig2&0~M?nmcTO< zIeU`;uMc7iL6+(p`h^C@KmrKy89lx-@OXk{om2z_W$KcX#AvQ+1CD&W-`J{TzuaaC z5_Bsn5L4x_VYxq@+-wI%bzD_#$VmHPlvDw-U%}<$_=`zJ!!96p#@Fh)u5!%hupOEJ zg(+>|g*m{=uf@gWTEu5%K3o8kzCZ*i=NPU8zbjAB7&TDAq2WQGaDzLnXP&p5ru$xy>4ph~*}%S|jG zdXrC4p9@GmwxY?H8ql{AaPaTo2TFs z!qfvuVj18vg3e#W7$o?B8_@Yz&-10&+DsU)VCh>}!JeVNBke(Gt$$+-D(>4 z+nj)?T9k4|50hH4K2%VVZ-zphSdIfFPq@kSlQUZthN3{x0a6Oi7`1OHspD#MU{XF2 zjl^$Kb<@{di-AqxWA@;TDiV;0J?0`TaERfKh(l&`Mhqh>$>sP_NJ0`&EkP2&G)A~g z*%9WXMwBLi6<3=MlZdS?gpJ1~h7ydL52$IVrna~!j$;FYSp?ovs2NrujxFZ((pr#O zyds|_bb~l~7En{v&L@Zoj*3X(M4&7Ii@1RmP#AM_A?#nQ7YeqlkiuAy9IHlh21QOI ze7HpO}WI4n{V z*$5F;ieg35V0vwGQQxZ8#swgV4g8VH>9+ncDc<0s?7f_pK;|6^E|?k7gd(6HTciek zgRBH=XA(qD_pui%K%-%L_=T@2Zf$O5C!{BTEX7WOi=ts;3eGGM>6>nXIxK?8mkH>p zYMxEG5*NV6gt^8?h=-r+wuoepIcg#fyTh~M`c&>V{ z#^rFkAlRuGni64JWa|3G8B}{;4y}~$Y+V#Iu>nGU;>2PSMPMM#i{$?H+<+R`WKjY~ z#|=h=Gp$?GcnRUPvxZID(M=mMn_oS3?*Ibhyl6VfJE?2Lezy`o%tNeTNo!v9W=Seb!BVQduisJDi{+=1@I6`UV#L8EOGMVa3zU~>EoJwkzHRQzqKS~ zju*q{``2)u$PoUDJ7QR7DA8rDVnebZEhjI^Ugr}5j@4=~SySS6!+Z@s?{eE}{(;Q|VDq~p!>KYr$qg`; zVi6iln(z=hiHorSapeWtfV?JL(1_2Q9aw-7x}0u2^m2DIvPI`SGJcPKyAmQf9mDPm z1q;PCKXNdxYGqVE`lQO{Ldn`|<^w zkS~;h?5Coy8oFW-xU8IP3?!&$OoSK|aM>Xjo9!2lcol#NCZW#Ba2hHqDqogTo?iB% z8z3c6JiGh*FvAMM#{;09()iAVfhg4on5S!+l_EFgM${VWlNcEpA zbP%;q07!@wsbi`B21==R;fGqGptZZx|KgJrYJ z1^B-y)r2)|EPqsfzcnp+-!zK*M!`3(xLufP{s8k9nvTu?Lq5tvA*_v8hS`oo4Y60j z7IR}qG{_zpwXuvG8%z-Uj(l-k#J}{Cx*;@vr=xKpTgrnfPHyd`KXz(Czhp(~^(}@e z?|Wpf@qpyJ{7NNU8=kJJb zvKnvLXNoJaJxqZaIvODj(Y^%Z?u+PKZXEy@neB+6B91oyw#H`3ElYLoQ$x5%;^IOq zw&*%q-4(d9;bw7J$33v0fWd&t@z0VANY5GMHguguX9TL83UuW4DOotngQ4u zW$Q;C13|I1Z($NS&qY)Bc$j?^6!;$VmwA$WjZ)a&y6u~kCgv20lQD2UB6E?p9ep(M z<*=~ew4Ln&O<)WjC)~W$*VM1o-|HuL4H*&IChb=c)+v#HL)kT@%75&=-oNH_e}Gw= z1#1KCBhfeQ)NsnC4od{~fOS3W9YB%wd*L8zN7~Mlm7ZU=-KebX>ETpzl5N2Hdx9f+(l)A@x#S>JQYVIz`Sqs*)BDYnQM94@7?{$fb zE%GZuP@}Nps1p0JweDn3^_0j~RHT1_Prj&JdeZFR$RrPT3Fbb1T}p-RKbd{uWd3qY zxBNmWZaZJNtLLwK^g|ivs{h2QBd@J$NeF~bt9Y6=_44qj15L3BMwRDe# z!%lhN=7`j}l3FtcQqmu`F*~(sURgO*V1;hIHNx@k<{g;A3Zt6NB8Xq<`-i2(xJ@!c zAY6Ira22fz>$KBg(#e3XY7Nz<(52cXi;us>Z#nc-ZaaZ+EF!*h zO+U}txYc6R8CLtWO2!CRw5H&*e8glZ$Usy<5TBCpBv8?m-Y38CPL%G!!|b(uoL=T_ ztwmW-4SybItlC}vRcJ%yUr>9SZw^H!oy{EeZCE+0JvXV(sF2Z&?KZ{H#vLU_D)bk_qBfD>Y{S&I z_+sOip(M#+O$KFxvyY)^5A7CZPVxM_fT(4!xtRibN}2MOo!Oqiv3W--NK5kmuT|fM z%)SC&fGhYBY7(nY#DAq#Gxe^2^xAinICY!xDx;@uZf}dut^*Nf=0{?#qc{I(cG|jL4|ZLo?YJWXt-MS#WW_nB zTZgmBa}_SgK>CA{bST&s=*Y$hDDnXNvK`uxDn8X)tOIY}< zZ2u&WKgca-kVUBS+NAvnYQ)vYbMO_ynELa?oeWh8pEf|5lM(kU;f`Gs?ZBU1_`3z;xCB zjYCIUtTWN{P%&lN9dpUmW2rx)s}CI;KVK@83G)y=oiDzBKO_pGV5oC2Fd*Era5TxX zax*!iooI)zSIG_i{`Ksv{ojKK-NWHb^*?+3HG9)idi4Q#_povG_p*5Iy=BkZkIn2w z1)%=*AqM-aSWp7!r$_4BiPpo171HDfpLqUPk&n4d?B4*uJr*a%siAYoI)-VNOXpmc zsq80>GU57T$SY>ja4TU>R-%_ENS@@;`YW29LpF<-8uazN)WL?$SU5%eqdTLzf0jlv zOeHNiG6)#yWuw^JWP*oM_yJqwZ;VL~#N$A^IscVHtCM!E#@O~W!j*QLG=r_LT74k< zF8-iP>a)JcByUYp!U-ez4tw-_wc-evq5?YuoEA$g25apLtW|N6GlIL|6}bgDb%I=8 zEM(0D2#RcaaX#z{QkjHXCj#7q3RU!e7Lxm|>})+gj-xv8nH-(51R`Vx5hJhq^4mA( zuZ#4c2S`yz#AE76BT4+F1iAUucZN$iYYtZqObxl_1OUYP5o#9jpzyEPfKCL`75tS{ z0MzZ3N_Ddb66!|T+2l>+36RAM_RJy3-vn}bu*HP0(uB{ngswscx}~m{pxsngm+Eur zIOz4_{{TslHRH-!+Q5AdK5?|FO}E`~1?WRe5D}#4 zDbxb_0~;v7!ntQJ4z|wi-Z$z|6(#==tNrFGOyZm^Kl47E@wP}Qbo6qxE3lC@@KSGd zUAbrE_$YNnu!GNRhy?_o#sE5i4@*{~Uh@y&ta+{u027gz+Uik6ND{WBv?y79`~rCd zQH*&i=GUT_0_V-KIGf-PF!7VzAv_um<9JVuV^I`tY1sR7#(8KFd3Pa*3(JdJ>G>)O zr+v{5xBD290tZN#f8n1Jhh?$!6$$W1SE|G+24L36pX2^%GE0yf=?<2vBbi69YEb|* zwSFB@=xXEd{e|7GJXmgOAK`Bn06@0~(1pc^BWuUW`)p0*wKTvT85O=(@noDyo-Q~w z7IE`(n1P={a*SPrOOFh?7DWRq+vUSN02VbV-Ze8vJiA#}Uce3ACs!50HzP*LAYX0Y zLpDo*FTg#1w=Up&>}H1f8i<9&#uwrETXe|aS}|}TANWr$yUzj?*kgD}%Hs^;kO2B= zq51mzD>IFo)bXp$w{fLRHcnjp_%8v`I3=m8BLsG_FK|oR?YZ`V5L4uV18YG+VSu$X z%0cB!iqPZNZ)!9DQM~TL`{=TWK1pE<^2TvcEpCm5doNR4vL^5lD`Z!cSazDr zkq0&g{=}-u_0^rnH%KF72<*kCtqmCL^`EZ=-O9uXjcgeyWf+Z^|71+7SNqCA3_#Wd zMobM<^`E1YoG#gq1Bg+u+bLOQYjj!<3le5_)t2gbb}wJjewJY>8XoY6%+9auPb-Hn zc-+W!i<)bXiab_0UZ{Ls#TxgPU|kIM^oW^m4rBEY*fY4L}-U z?LHZUu!gwC=+3M-n>4pCf0miTSD^FDFQg?B%jBfi4`qxbQ^qJ1q73(c z?ZaK_QBP0L`@P@q`d{C?b7b?)Q1@`x16&1Dr$0#_HS$1fY7Xc-<* zKc8SDY=X(>Hjhj)pX{>aay^OZg*k4jk*Ozhc^;(DJ8{jQYjM?=j}6vie2Y^atq|1c zNcY+?=2ZCE_Rw4*=FO7Wgj&CH_zk%Kil&@@*(@+c`+mNe+y1$3g0jI0>g~rRZI)AJ z4qUv%iro`%(%`3Zc|Ug1y{!)GyBH+iF~`+zG)U?88}(#tRM~Zmap3x9a(Q>h@y~|F zM-yzado4=893~$$#g>a!OBzCLtPbhdudDEdZ<f@E-&TYt>u&IbP1? zXPDIH-)ul_gy|ASk74C4?F(h-UGEHNnoyl&Dr&cWdw_6~GKa6_l2HkV`j(?vwWZOC zk9Nky0|pOw=;^1EH@EdSE4P`mOK_-4B zGV!wGE4UwADw$+LH8@VysY5Hv zjD@vwZI&*3cYU`b!}t?h=dSSMHMi{uZiMH@J4@DtvuU~Q_PeV{*vD_UO8#hwenP~; zB&%rsQp<%)V*(;$`MSHA-ma~jzlYz>ns!`Za7%#Z#wYSE;;syYY9p7UD|IOef|+CCfc$m#OKd%WCb^q<=4G(MTIOQnRMq$s$Ph zSzq^+d2_PPzVjOJyViO6*4Q-TGUPYMvfR%$j_xT`FK!o89iHo6(Y{dr;OQ@r*Ks8tu4D zl{z*_nR8ydxj+C@p=)kcyv3$%-_ zWe*G;E}0vm6t2N~UL*X3|K?n0pTMqNBa=5UPF2R1@q@m#r9=_we)g>zENhNSesG=K z?bDW-^MTDQ8BS$|K_eMrDGLU5(u=H?G1`naxDlChZnwtF594ZfmBx64lyxaxR}hR8 z_0RTXm{egH-z8)Vu;%pX2fD`a=Mi=8!n(8v+(Htr8^&tfzr?oxl&ye_L{Ox-&X%IJ zhv%kvyc>&XYN7>AvLa1q;L_7Hu9V&VU0uy9weCNZaCiDZd6^XyKQ#0}I{1qw2A{AVp zEK1WplKT|tUUGY|<}I_JtL$W@Q=b0@>9oF3>Elgi(kGf;CAU-XNQ^!tH^v62D6+SV zs$)bd{`%Ii7nn0eOOJ5Sh&9t_(EEge^U(}K-NUS|h9OsvTsJ&Xpgon~ zJekj{RVCkt>4pu4o;tNIXgEX4(s{TWZvWfKd!CPvUyP7Ux~iWoSee!^SgYKT#t`!% z-QU9;zF5_`x0Jc^c!Z%3rD~F(dvJ-8Bvea%t9j~mFPExlzwKQ*W3^o05 zAg@)rVS?SYdCyQ?=DHTzob0E~N=mObZen+~`m_#HA$L(aqAl)fBe;{?M^+=hG;w#b zx8n=2onZBRJt8m29X!I4nc1bxWYW(Nb{f+lq{*ogb*rzZNm}isVhlD*?^^jepjYLK zLHW+ia(}Z^IUk&Tnno)I7py-Mbf}MW^hoz=Va52xR4!4IvY0V-Ebsl&I1|G5dB)v0 za<72#9OFDstK}^&(fv;tdX(C_60keE*ZD*w1D?KyRoOv%-EisF`yF$|bwa#^R`EIn z^gBPwCib6I5IzhN;-w^&{v(y$Fn*L(mK^}8``Liv!U<+)mE2&3O}pvB?yub0SN4C-EO@t%ecyhTx69S}nV%dQU5FV` zsJ3b^9dET47{6Wfw0V-3e!TUR^f*-$jjF~7<>=PS#;aYqj5Y=@aPA)1J8(d%Ml^8Y zx$OL9XR?Pm=9P7gwNMY(KjnL7eKFgh)poS^bHuw3tid3o9Plq(FM8|orrvN`-?XqB zjtW(=l?*L}xaPbzzA>FepN4~N2vypcDANmPMO9+!+)kxc4NK;82cHU(J)row?;32P zH1dLX`L)~V2RpVCY(Xme{LeKPf+&QN| z+FIp>738v?wHsY6BeTKnNwbaPyh+OjdexdQ;?HHuq{Sq;bCvG~T2%>#vjsgIF0m~e z^j66otB%hdgmKQwQsiOU$MA}c-wR%LJ(%)HvDwFOWaXU;n@?BU8innPkUaq8rAI0- zWzR*~owq%e+5_(ldbf;RC(G{Ay_Cng)`DMj^3j^bdTL8$u5gtr+rWhZXb??3^w(J% zYs+0|6Qs^!zSQWP-KNY`lSXt8^Tp;9J_ot4$xd9|yTbeCT8RuGt6A-i&vQDSiL3>lMQ|he$PQeW!3z~Q99!ttjJFpOJj~Vt-GjkmG!1yl9g-E&B8Lh zo&d+Pb(RuI=JEQMw)E7!y;icOJbfg4Y>em#_aoKf)D|a(4B7^VRjPr@9`dY=mDb@m1Wz4nprfI(XR7q4 z(Rp^~Zq3s*o%O6`tc{cUrhZ|1hZrdog73-dW~yy_W&P%!@zo)(SXc_o z1?ekJ8YoY8Tr?Uv9XPZj$B@ZRvMwq{-IL5cP4Ly-vb#_@O7H%8 zIe+$TGJeVK@;vNspD$&ta0PMf!-nKjV_?8?cducy%xT9xHRUunZ;W)vxT*FmQ_U*1 zb*@^p#IUtqNKh))4$%-X^A`h&>Tsa`x)Qf{W(Dv1 z318xO&6%&p;{Wha!utkrSEMSxyoxJJV5z@br9frAEaQ!%%cQ3Aq!$PPyq%L*^~9v$ zSj$rV(OM&u3mpj4L1*ON}&uECG)a%=$gB$2ZDR(syENhRg(SSBq>C-VqQn{Q62)$Ehyn(i4Yx28XQQeW31U zR2sN_e7qOL+sw?+fLqpKVM!RK#>c2C7@vM7@^HL9tq(zgftqX!uihS>!LmzWY?<1KQ1sZ@X5hK6&NPI|nlA~F zb?_MCUTUGc^2n)`gY(Q=y=^h7E$1SSU(ojh(;dI}OFGYQsr6VV3dT&DL1m@sPSLe> zU7sc55f_GNgEOy6g=Y`+PUda5p4oTi0-6YS(3W2Z?HoUejbCCMO zsKJTq<55I=^DwATy22&LpU%v`x7VL*>xF)fWn9)FXJGF~^FPpbQ``Aqg3M8^ojaWE zHIw^E` zk`9#dExgOum2uWhOdK7MHi=It@DRbV z!(}06$31Z3B%yo7V!I56?sqcxGHI8+vM#%OBIE)X4NdsCUQ(gZuukf28axYH6S#!9 zX&vNBHEI-YLEI($G3dxdplThb>YJPkPOxr97F5^g7#V19*{r?Au$xwmKwe1}qCH7N zRw2TCY`ms3Q_7iJt4q)9)(TB}zNMWxdOn$BUiEd{Y7b(s&rzeOFuTcRAyXa{QWGFs zw)vr3&jYvp9&VB!-Ane3?l4wn@9vqu?|?$*OY?Bi9nSf;0&*!&1RZN6PONqzid}Kz zURzs*5&Vmk`^;dYfI3a8%BtO8hufr?V0P=$X%C%FyU~3m7aA^nang9TT;o=Q=4FLT z+{>-YF53i-eB5ZJ@J_Mwu*=+&%R@ymww&RzrGvi9U8427u9yXG-B7csSh?U+_Tzxw zRr;r??VJ0THBVYB`t_lUPIgJa94vo7P?L&H{osLZ&z zp}}cwsZ0c!SD9J1ZxB(NvNF+}i`}kPo4XqpdRm>omNVBCmqDhwMwzN>m#yfIrRrC! za(FpUfaz;H7({lm_}bBHwudWic^4C{!FQzbV)>_MdTOU6I435Tb#~MTBylVL7ywxV$7fWws#&8(x6P8f8I}mF~`i; zu)?|wEzn~M{h2S~n~u8gJyjt%SL?_=D#9z#626$4Go?w+JEeS5Uidp?=DHCVe4Ovv zGBVd-XKhD{mSE6B!CPlzD=o?FD*Tg<4Sm{u*xv1ln4Eua7Pps*VXiXI-8O~yvaU`! z{_I(NmnM2cqtiRBmoPHiC+u`RXwEyP>*yE+X2UAG*2^=s#>#9CRt{m7sv3hs%8`4k zg*hvTVD@?9UM>4W?n@uax6EnL4~gsAH&Gy~;h64KUqwB{u-C79AEhfMDJ=Q8CVuf` zL`H_R^rjxs4O_gGtJU>SlRYvF7qvdxICgtug5!D2C~~-}cTum9h(?lxysR+IOASgd z9jV(9HK1=frKM?T)RI_wA!0s87kQ%d7TnCe?~i>?jNH51HMXvNOOc?5hFkZJx|MtP z_2`70Ii@8@RM>Do{H~m$+#WRpIo+EPHIAI!O4enkRf36IgtD*ARilWt6kj?4?7Q`V z^;qs_4YM_M@4PG55RR(o)o(Q0#y4NdF22ra-wr>}8TCudbt106IM?lgg+x-Ar_zZ$ z|IA$xMIxV<6Wo|Pq+WcaDv5r?i zo6=D=bE4}UaZDV0r&#}KXA`IU$&g!)8)K6%c#10MQMe|+mfFwymcn&MSpr;%HWZHb zP72i1_%d$*^7Q$!b~Ufrr{k^>f>`E@+7=Q4w6!T|mn#%TIFWR+qOd(bmwFlP) zj+k@k)o;BqoXnvkrT)0=Hp_L!7aKk;l;^8t)7vduIU4LWcP-Ccw~Z!&O6OB$`NX*j zm09lQDn>+F20qXT{={|U`0^vRJcFsLxLB_)UnF>txqe6f>%%^^iam#`br-ibv0Ne6 z?Yca;^9AjYjjTl*MW^g=S;E=mMZ!xp+~&$xz}2|is{0~y3{Kfm4nA%>Id<{#V3eqj z1trEKxU0d_(J@P(rSzt{mI=L@>jTs1XfOnxSYIYkf8~)wBcIC(UgAd;GkO~Sm;wUa zNxoCCXj~mG%FeQZrmDodCfJqiz0+iFlj@j{L-bfeOui{GDc3)feCGvYR*Vp~Bh2BV z>;ZPkrQzc9PaC;1Doh%w)u#7JXR%#6b?~8VI@O_hUv6h&RKRmO4V3%ysXA@V{JA7D zR;^yWgpH6v7dwdZn+6TAB50V05AJhdZ2vx}XeDW%IR8C!yqx|C2HJ1y5jLcW>&1rFP;~rHl{QSI}yXye8=RTFj z-g1AI*mYq}5ku5s6SIxj~+;O#|1fLVccTDfi3ByHj6CvgH{w0!#NDvPVK zpZO2V`qUbBei00LP$3QVsytRB(w94R)?FS9Pv`E6=H*woK0=o&c7@Ql%}gQr(z}J0 zi=)hWxh%ZsXlMeLRiqX;^pEXTo;**zYoszPf|4%>i!b$Wyy#W$-!I1DZ!)&yjC!`3 zwl+58!JX0U;4)NweEd?cSG|35KVPj;*4i*fKiLC3!3meGtOfLj4?kyNW?pjgmgnc* z#><0AU?I@FLN>o)6GyUKjCz=5x@@nZXZ_B6qb>Q$9IehDy}h?o5YO~ZezN>9>eqK9 zz{$yVvNh03qiK9W%ljo3W4vYx`&veqb-ukN?jqbZ5A4F0HVRTtzDsK!4{tl!t5oi^ zPyB_;#zHs0!FjHtKA|hT>VYHl6_>TmeY%_sYy{SPI^MKd)xD57s8aZOc()n7SXn?bh85Esoj5pyT7se721jcWS;Ww>aez>1n8aNR_eq zP}%9>z!7=EuC|+&FE!4GI%Il#Ti@Mm-p>bCIL(&|voneZlM7u(t#%#QV)lj3WVuFK z?TMya6R=`qYVzztPMixp?d^Sysd;Z%sIgbR%Fxcj)RbLe2f}tw%K2Q~u<~-Y(c@mn z^06Z{Oz{50PG`1PGG}CjDZo-Cl;Lq7yGnC2k!T}tQD-%8M0btOv0(@IA(h^=D?Cg) z6i++q3+U}N-K-~0A&}~pcRG*{uvP7ftYQie}p$y^Czn&s)zSF|7a z7iziBl?Y%v*Um`g23Nf<6}DMJpSQm=R^UvKp0k3ip{{vx)yVZ;l}gjHb#!G8pEdSw zTcEk%>I7IegaYXp>IG^0b;q)iVLHjWJNwCW-^Eq?Pw2vVR4=4yb;vE#5L5aTj*I33 z^cAV){=+VZ1EeR9xey~hxpC9Ix|aiI>n~WBQu)pVxH?|v{xL+&RzQ#D>AqFkY%PQj zp#|2bo;&nU!c?#@r1xFh=n-(zQc#M*#8pV}S$TS2TVu`>alJ6NNO|T0`g7)st0e3W zM|G_n0v%ULIZu}uwbwr07Y>;T!ci31fE)wd1@^L)@2r-t+Nyospu~u2Bkk%X?#n9T zUJM4NHrw`%8y9}=qzz12T)!uynEySL$#rkgSPs{@ecnYD?A;O~l2St>tmmrFcqitQ zzu5qHD35z5KSm!PZ@6-zN&T51&)b?_22m+W6O8&W1H6m!uq)~STm4?bK@PnQ3-r0K zUhv=ObGH0MQ-2?<_AcGagAH)YaCE}AKT#x#5*ujaze1kr@u&<^)(;8u6we$5+vPA;KpchZq%WSocJbAf~3!r zATi;D!>;#K^V^Q8bf%5i$XPfQGI|^i6XIwzk$Pdav9hf_ROUH}qQar> zQAZn%Cqapn2n#;NJ@H=tnMs*qE5d>tsx3--FTkiU3)Gss^H1)sP&jYS6W=(<`z5OF z+#P6rbQh(x*{t1JE&B0e%$pBH=L%ci zL+ty!s28LMS;y;ORI!AWH&Q=sYjAGfYnUyqvxO;fgTPI#?4r|SJY5Z{#{!JGW$)EL z3%Q`Gwhb(K6mkZy?>~7=_+zD}PUCttmH2VFi=GSKvcKJqEVCSyCzEUn-VLu&TBz&Y zu3=V0{?6A>`qV`mV(IblZLic;ZGWR|E<-i&M)1x~FUR&eI5G!0H2Qt1Qg##LXg`O| z+q8BInEb>%ejVSWdfL&MSI<_QBGrrV+S%taSa}@=1FZL2Mk8@|Z`3}gvIFEJMIK~S zCLc1qKWt;>X3@#ydx68r;ES!1`pBgUd(P-Xg@BWvB3Iei{q7*J!!=Brop2yDQWPc3ylV5_-Ob|ht4iVgZs3il1q)p zN-qjoU)wG?~Mw7Oo+Z;Cm~3b?+s=1E(C8 zRxUb6v$l$ip?if@*%wQrk#cC}+HJrtBzkud2fQ@1XcM-T#kYLZ{!gE?7>$fq$@93f zCfcOB5lrE&!qH2;laoqy6Q%TK$;s~a=jdFz;l-JG@@l>s9bP(114C=_+4oJ{xFsW} zSHhTMqhOI68tqgECRn4qW=GG{xf};*GI_Opjc_`RHtG0B3u6)=HI%xGp4U4cdI4UQ z$^e>kvc{Ho1;1UadYGK`Xs+0NS7ETpW@hygc#vKdADvkn-(;XIFJP4(?`~nX`*T9u zNuq7tsg_t|?cBB}mrv;~Cd@=89wcr^dA-@ZAtj?SzKKs%go7hWkjt6nic44d<-v%9 z!J)cg(nCFBSK#GK0+|V6o0Q($sN_=(@Sf1fXN}Egk{4dHV%gyW%QdQb#dE$D8q4p< zKYDPD4)8CL0kv5CfknN^@b2MRc;}GGxj$m{a*f(X_A4%Jiwjd@c7(}UosGXUux7nL zl7=swWH*4d*~k`|@)|YaI-Uz)!30-JgS%lPXE|l}PJWKm)~Yz#0ZW4-XIbY1u(o)` zYM32*)ym>}*vj z`5UrRQ3dZ*`Q!Tp3O~OviypJ`({_O}%b^TYJ{(~7ohlYFY*r3*bfz1;zP|Z%H(}RB z&-$d?!QCfI2AvaLIS{KPm`@BZy99UI2LHz*fn;9zx3cec$bQkg>r~Fid}kJPy9`tJF4d$5Ay0cR2;oS^_F?x zt3zd9=-^%Eq+XT8+Cc?#vsGT!@Y3PD9I8i3I?oJ4YR-d#vNEzJRYYT0J4i$$W|uip zp?v2cSXg8}84P^g?zmzsx8+VEtfP5?46$Nzy8bc4@#l4R$*jB2>N^>DR_zAkO8zFv z_ycEqCp$#SoxZS^I~|OXrT&u3t7Uq)JvJ&Z&sDpjxBfcpPm9y~at2nXfOo}^Z;}r)(K6!dk1$fW`rVFsk==>Jnom7fk}wsotR-My^uI{$3y8w$(FlDqQQza zOTB>Gnswii+|Q@Bp*BL{URE+v?7@!k;jMKqKdmTr>uep)&P;Fi?+Q8`5Y*HUf)>lZ zKBl)cT_g1y#4#l@f;k~AAksd(b|Jiex8k0p0|B#q*tPQ0io(y6FkB}u7THoC7O@^x zS_RNLD#4Z#OP^psSkv#sns3|qkw_lC$6>YpgRP<%Jfwd+7Kbo=Xa!!{M}OrCYA|?Q z@!!AxOioT>@XxQ9n_E8C)YOelOiWO z#R|G(O2^j99b+U|tVq3`YQadbEP3$Y(NRXaWsl0rO4=$3mg(?lFk)jTrcF|7LcS5u0?uuu(n?YoUf4clfJT=q%<6uJs~@JvobE0}S8tP?+RU zna@EOob#u~NDVJKHBK04vSxco*GFS0*Z{VH@Ni_J zTzI6bQ4!wEGk6S7XvGN1Q^0uI@O?&_m!w?ePxvgIib_w_A-%JFDlRo$%0t;0PZ*2G zz2RK?v_{AhDGq@6@au=I7)JoaJzHstAQj^z-y@r-7>K?|s^5_&! z$Ld=lO1RZL6%`cpy3YvR$D^Wn+60t;xAlyuoghkWINV^AGbc+C1?3#u>x z>V`+l{t1d<7`N6@3wSJ#1>;eS#%iho3z_M-=~P?_utg93^cam##|e>B`G!;37pediE7 za=B-T#I7zPTIVw0Ou&7m2h+OL>zsvUJ~lWwj2RddVo#pD#Md$cT)H#^)6=_* zv9M@h3lm_pS6!yNDe@JIOgL%_jAVSuS^P)QL8p$8wf zZK&@UHICkNHFf}FvBm=WPHkb1suD&Z7`48Lua><^--W{jtG;f_qt;iaDx%Iex1|p4 z>+7?mxZKy*bC|HkNDaYLKm`<#+~;u+{YF3xZ-5%6-3p~lJr|%$Ob8wx>dSrI=^610 zOVc5sh9}`aCwfz#BFqH^7o}&Vd`+j`Kb1I@lRVumjA`|ZWC#`jBJdEbzfXoW2Tj9x zPA4xa3wSym#PM{1VOw-NC|eGmvGkjeC!vSnsWtXWqz|H0{7Em{Vo7TG1St{uiU@e> z;hdTNfs_hO%gL#xr)JztMfC-LOQ-0~m_}vM?R2$o=`$9KpdhJH$Rg5RiLaOFCZVu5Xgrmn1g(fGhC5R_$E{3Ao30I7UTomI7Xg>$`f6wTc+ye z8%32*Vyfe){z*dlpey*}4CFZj05?8Pa8(6=jD#07%s_f6Iy663ANWuX zib03I`K0`>{cHJF9`7%y-k=Pt$W-7CP;kUTnf?tPFyi*bGgUoXHI7#!h?gtj1m6=3xA z;n=%(10bU=hOh@VFffQQF)aos4{}-@E~lcpX=rW%@5;w|dintFF1Tx0SXdc0H1r9p ztE+=K7{`i>Ut^=AqaeT4UHYW#zM2U*AW( z-ud%Ou|Vc@<-ci8c6meE2Yk<>kG?`(0Afh@UlH zzI=&&`t%9Ab&Dh?&$e3rFYW(#Bfvdh-Dy7ebhik0XZA?Lzs+l%u|xF2Zmk-f0se3< zf|RA=M^S`~4E=J7DlOx}qs+X>Gmy7d(A>dkz_9P+GF}D)={=^lnBD24Dr;y~i_z@b z%)B22U5bT*jysiw&*~poeB#hjI1SLRGMlqrPtkaehRs?#pi#O~n|S7Na?ayfx0@Ys znrotB$i~RT#K@*2M>Us#*|A$~U|qn$v4C}}sx81{gmc>j_*q!^1rFn%8G;f!aGba- zji!1;ahlBFxVC|T3$%_Nf~yG?Jw26{1?t{^_S`y6XQvy&TbuDiqO(YzM-PeCGBA|l zdlJ43^EGXXPSQ;8;bPoJX8=SS5uHY11Op$G!D#`akBIhWXTQYcaz0My85wPG+IjM1A$IH-qRstSb#)CUAW(?YqpYkl zI9r(E^cV4l9Xo=tRjbwk{r3VKv~XIirIm+WzkUllbEXihsdtZ8I=jz-fPcd=<_+=;=#wJm$^IhZRGOj|1XoXznrisxaaMAtBGP z-MjB%`uba7{Jf!$I+%`5J{BGx4m_;{=N~2}dDydO%~(fAFU$oC*52L?xTa$_Z$85= zUM#|*qpJYdo%q=1<>h0wwQYD?h>s$^kIs|~3`cMt%E(xPRa7)%LPCYm|9qT>Ffr+2 z^78U9_eGeWUm4V$iqAKE&j{Aga-8Ryn&tsdD};DH@bBB0wlFIZ`5Da`A8%-+5LLu-_gaSx1p3Gm)L{M_5pg697z*4WsE zk1r+VcFfQajcp`8{tOJ~ao+aeK_%APJAkh*RPVxth1jD zU3q|?CWiPU8gprBY5YEJ&Ky^~93!JKoXPWH9=G86baYR!=H_lpQ!^iMDu#7!i_1Ll zX)LtezdZyF^+2z0(eFb9%By=E$jG=JT@#u`UYjQ*uqT5Ie~8Ja`QUR?Gaq^(fn5w! zx#*X;@U+bg2!hR*g^R!CqEL85H{WzC1j@D&uJ3bEZ1tOKR0x7_gY(p1b0Nh$z(|M^ z!(tXT&zcR1x#>NWHb;AD^ZCpKvZn#xMI9cMa|XbtrAbfHqRdHBn2c=Q zr}(oN<)XE376wlxGH}ugbzR28ET;@2*IH3R4+ykE=YsIGz2~6MRGw3j&eII5$3zc#p0%(sJ#5>9VkINF|k}ZM^c-t?hzjjK(HFSS9x{6 zz6PBwod(t1Gst`BF7(A%DP}5vGr-YT7y=ccpfH^F&=;}GNizUAF49m_3q4?dURL(C z`GG7opPSv&vxBN2&@V6ylFYq)M@8FD|+!dMA%ZLyYA%-$& zPGqdMpGoP3zDC66+@VlEXc=5qv-M36KGO@0y zx%pZJrQY6|`AV<8=1d1{;ER0lH0GmU?^3EC^9lU$EkV(K8yip9D@p?2^$v%nJy`15 z0Gn=J3a7fjx6Swe*1hAjoI>+s@2292w;&mVB&STB^QQO^$|K1gq%e|6;3IZ8?L=va zR-QUlgvSwmrK5WcGD#uMClDV&=SLx-DOm^c78;tHKrc%{UdYExObS6JDFB#4puyLG z57dDCumt1bnY!QHzrO&xfB!!40B7JYZ-Ksx!Z|As$H~MbAMX77u#Aj4;J*iee@)=D znUAj+Q&3RArTK7XjRBtd9QaZm z$Oj+rHW(Pr0q@z0%U_|P6;Qqa@V^1_Mhnz`3+Gi&pT5F!bL%k9^$a{^rjEZC`Br9557lOP}f|osewg|tcxPAK>$fc#w9|n+FTk)}F zV$uda-w$Ko4l?m8z>x=+i#t1!?3RR&6B;WdccOa;G?wt%FpNzphVB#K^P2cv($n9; zWfvNnYcMW_AV-$tV}fK?28OeEJxE4US1*LM@(kuX3fr&&jf)+~m`iZk;?ku{ur^Au zq@*Nl+qOJ>?a|T2K-(TsR4L1XD77V`DdV z{P=NPwp+Qf0OR5+2Kl`N=2aHt;c6IfZ&*`CaKBN2nVJ@0`}U!|lmO{|_Xhs zMUO=w-85bLDKJLE^3nna#((Ss>9qbzfxnfe-V{Fi4H|`{H243sjd>m6-vdv@_*roF z_pR2c5P?N}1|~BLO0A=t_aijMweBa-sH8<;$9Dw?b9AI;*7(N^=za$NZt1LrZ@I;a&YJNk zYSv^aVJxd+mK+O~gKU3IwighdhtaU_TlSZ%Xj;teFAX<6IyW^3H2Kl(o$J39vLA%` z*W#O6)hOBCdZ_&&{3GX#hcWLPyeS$tj4>K^&Gb`wa0Vy}5=_6=W)U{M9_MWQn#OC& z_+5cHZ>B4fdT#9YZK2TazB)aZ_DSDiL#K|ztFPPX7FxHJWvWESk)Evw=~gu0SyG#3 zG(a24Ytfrxn#efku>z82>tIi%??fT`@;NLcrkZRPyuC9py1;6kG)YIm<~WQYAoxi0 z94u}s6d<`5zBg=;zkcq;Zw&6jOcM6;8!new4XyRstot{PB`C(--Q~6C4D7YjO(9M;RRSdTs?`lYSZ%m0ySHrXQ@XjJ_1q#LEm~kC!+05pvCpI zB5Di1o(;Yh5?PxkI@oQ+_#-!%^Y%yBukqk-Bl9gpedTNZ4*`X5UHt}*5hQT1<9?1+ zr30T%4ro`kHg@4jS7t!X2r|WZdY+u-MbqPDpL%lDTE8PRfMx{gzF!NZAr@4hT8=Xy zr-C%!4>$>)_r_l!rxUEcf_xW9`WZaacWaYW#$x}^;0d$xJKwCI!DBk4I%oRgGgs#K zMW@LMu-&U)>6g@uef~W(oS6crDrU>qV&67R4FhML-06~YD(!jgpV;6lk!JT7C=LJn z{i}!Z(S5hG(+CqfF58eu_}4Q3r?-TmfI~A8jR^ew@9Hi25f+%*EcfX|P;&n0K#w;k zJ-t|C`}WvJ1_rTFD2&p6gqsuBv_>+8mPkICda=K@veXWOy zsra+Id(!cYj1qMtqnOB*E0c;5ep9felL7+SUB`~aT{?L(?!r_Zr3e?|{P~5Sl$Dc8 zcJGe4mz`avhQ?uNXo&Xm<>VvW+_~@h`LlZD5hC@drMu$GT6 zy9eQPmHm}%>x1g%(ide6|riS zN%o#S*Y8?cJ-U7JWc-Eq@B5fZymTq)D3rn13M*^DFdJL`5UK-o;FF%7Ne(mTeP&q{ zMt!KQZIIZxGbR#^QBhH)B629aXHRTsRna1_Wp^Fa7DC7PcJ@k`}Vj;g@u*N{z>^ifrUFFN-)S`ij2(JuBMh;0D8f%v;ppd z#@DP#%#Mr8(?#nC?&m1}q>b6iAo_~vQE+gE$rLRq{`_t2OwmzN9O%qv4UMFy5fRxt zy1Kd-fPDwp1pSqc|4tez+tk!5fZAQXIx+Vf4g0=LQrk2A`SXiDtzDa#f$%_k_@Cf` z#txOae?P}y)28?&R@TDdnRWk@@PY;TAGd9bi;juO)kFN?SM!3_)WE<1!{f(AT9%fv z0ld6V-~E%eegzA3zU$z@IN!|7lGSLR{w3e^u9?=7FAbLP;s* zxwm(UZDV7LAfnBrb^x_Z~HxCq39{k8pxX&D!QWV6|D zN4x>?B-BrC?!3O4cp(0uuAcZ5$w#ENX7f>>5Kl(%P<|3?vhtcAMp)OPtPOF%*^jWSU^Xn?;HmQN9WJ-=Fgws>*3+yfa)dHhxXc=Hyu2`;sYdD zG`2A@F`LB1Bwu3K93t4)fMX$&ku4&UlWP*ewuA`wI=BN{2qFcA2INTs3^NXktWc$- zWU4?{vw<{Eq`H6QBUse$y1IICbMq4cVEaG>+ZK?UAA+ox|C2rajVFVy4qzjRU_${o z>QC(3=j_+g(#nVA>c7JSjZ1U$8~(7cEF++8DeUaIgEQChpM=r*YxCx~xX8%t?Px9h zZ0u1wS{sO8qchZB+bV*-=YUHEnL7zwJh)hJans=>$V&tF1RR3>HYO;Hcm?du(C5!z z$j*iXI%{3K_W0oP<;l;4g|phHc;VAYuv3Ei1K`}@2F?!79me1`!}&o6+!}CN;MU=3 zU^4?SN5G*oNg$3J*fCAAu@wx7h-9^`SdmnE_im;!qH{m40W>#=mZCF=pWoyChYm$u zS5WY%Vq)3=XA}uIqp*XU|C??BlvxOP=@FHbJj)#%V!UqOPP0I?4fXTKwr7hYTwGjS z&wbw}8`R4SP7ItBIGJgO!YGXw@MHaoBRl(oFXzvncb;v$XKQn28j=lsef_ODIOIN| zF$Oy?a5gZuPT($p^8)7$&SyG&9(*Sp2JEb$@0vs*q2)a{Z{D_=S?@myM@6M>UbxV! z37s#&mTT5^_2W95T6<|Q_Yp(|h09fmiJ9yFNt-ia(SFd@j?esS8{e<(pXG1b6qi&} zQ@?O#o&O}<+xu?*!Gp2BNM`t18$U}&dl<K<8!On6a8pqk%QBX*&T(l_T^^bW-cG$8dF1EF`o%c`LoLL6#9VELT`ax<( zL?ox((=*utos*C|b0*OR-4T)Ur~00?3-R=sb^U1=(JMc{)csqw#K)nwUcYV@nt7%} zXVm)oMo}lH_={V%B*fjhlWsOM|5xo!-2(jY{i1sc8E_`xzTQ)aK;(z#SAF<5d59jL zKAn8p&Mv{br>B?o-_-TrDvRbeJ-tNZ(4n~N{QOV9%ISQ3xxH3aako=a3fCh3@!!Jd zciTbxAtj|q8{OYb-3!l7*U;Gtok^pjayS18@A_SQei!cDyZ0IE`Ehai8=vh%wm7K~o$XP=8Ho-Hn(-I9<{u>Rlh#h>*Dops>cwsGs$_}G88PJU0G zAoUC0&HB84ePSxQqeEl$GkpF@Iy(2GI$2qZ{>Sg2Nc~`8DH>H*Psy#VeJ$}%a3#T_ z{e*bDhez@;e*WAZQoa9eAKj_BxFno?{klcypX_n8R+N+;KmVUxE5ADy2nTfciOxqe z?}yqQdduUI(^6UmvicW6W3q0wPHm| z8RGFX$2}>jP=|vf|HH4!{lAn4b1{DZ{!;_g2SjrP1fI^Sv;DU?BN+(Y=lzUzJ0=``o#tQ)mw0o#MS>Vp*-f zg8!#^NLEAZ65TERc<=g8>-qtHQb;Jf6X8xe%OJY+{CTbH;luIQxVZ9Wvn%+=``Oqy zKAa>AI6Eht`%la9-Lt`(6AuZ;p>g{PM@T5#2^}0(RrmHXFZ|jijYB>Pt&&Gi+nm!*^Y_Ql`&JGtS<`cWL|^Fa=rm3E_aB$EaQXPP{F zR=p%7By`u&qetDHoSe>OWMrt%)Gf@cJ1;M9g{!Npi=CaFm%sll%d)a6In)li8v)sQ z)^&wsYIIhbStS-E_+Xz=0U-c(Q zS7T&k6pHMwNbMjU)}B3k0^waF^z!RhSXx@zp(|Ie97eGH{R515?`9c8Ut-Zc6Qa4O zZG;2Tk)bw8_5Gb+Qc}H?oxNai)vDzD3l|cd($b1Gk^YBtZ`Ix1&5m?k@D9tTnfS~M zZ{EE55z;fEwhkOPa2@HNX6BQ^R8&-OW?7%m*w`pY!t3qZ9yX-+M0aUgT1naH&N3h% z-JF#FcfN~D@>z7B@U!*28@)6lVms1P z{uMtO(A`Oau5JV9?e>F?>@3)y-2iuU+TDP(^N?o)y0cB7&y#^YK>u|gkm^HgEHE%| z&(GTbH|a>Xtf{G!Mn=XBxXJ<^+X1HwU@sSgY`uQLkAKFeN^o1dIdj~hE+eRO3G|Bx z>S6sz+Bg3O-7{$B*yYs|9N9|KOopqIP|&JuK!NN2nR^rU=Wb)?_xS4sL^ zR$P|~?;HZWHk31m^5>u~q*G3YcK&Kdif|Vf&uZPYDIp2jIQ)q3Y;hzTAiFcTTh=*t zEdCO@D@Nxjdiqdka~t3w2TtHSo$l=TlJNeOkNU(9ZVA-A75Z_9$j)9ksI2_B1l{Gt z#^!B8GW@^Q8z7pduC7`5=Xn0A58vl?baZ^RRheyk|0E6RgA5F|$No>_&(AN=|0nYD z-}MR6F!;|FzuE9-W?lg6SqbLf0M_$iaA#p1c!To;=MN6)@{t}N*;yC@tTL<@o>}+G z#*MmZe}~JT=3l*f{V)@g9N@4SaIpuxd|}Te%)0Mp-}f_V0otp`Ud12kI)<-(W@ZKA z<;&joe~RDVmCMX5UOicZ`LZ~yH`10jzvA@IPPxBE!W@av##RljO zLL$5Q@9_Dj`EbVV`I!yl@8b5maCUa_U|0K}S@WcNRV3-xK?>ezd-k{{HOQMCWbW5@V3v UHnM&EH}?u~_W!@',NULL,'Nintendo Development Testing masterID 15',0), - (2529,'ninTest>am',NULL,'Nintendo Development Testing masterID 15 Automatch',0), - (2530,'ninTest?',NULL,'Nintendo Development Testing masterID 16',0), - (2531,'ninTest?am',NULL,'Nintendo Development Testing masterID 16 Automatch',0), - (2532,'ninTest@',NULL,'Nintendo Development Testing masterID 17',0), - (2533,'ninTest@am',NULL,'Nintendo Development Testing masterID 17 Automatch',0), - (2534,'ninTest-',NULL,'Nintendo Development Testing masterID 18',0), - (2535,'ninTest-am',NULL,'Nintendo Development Testing masterID 18 Automatch',0), - (2536,'ninTest.',NULL,'Nintendo Development Testing masterID 19',0), - (2537,'ninTest.am',NULL,'Nintendo Development Testing masterID 19 Automatch',0), - (2538,'dartspartywii',NULL,'Darts Wii Party (Wii)',0), - (2539,'3celsiuswii',NULL,'3* Celsius (WiiWare)',0), - (2540,'acejokerUSds',NULL,'Mega Man Star Force 3: Black Ace/Red Joker (US) (DS)',0), - (2541,'Rabgohomewii',NULL,'Rabbids Go Home (Wii)',0), - (2542,'tmntsmashwii',NULL,'TMNT Smash Up (Wii)',0), - (2543,'simplejudowii',NULL,'Simple The Ju-Do (WiiWare)',0), - (2544,'menofwarpcd',NULL,'Men of War MP DEMO (PC)',0), - (2545,'menofwarpcdam',NULL,'Men of War MP DEMO Automatch (PC)',0), - (2547,'rdr2ps3',NULL,'Red Dead Redemption (PS3)',0), - (2548,'rdr2ps3am',NULL,'Red Dead Redemption Automatch (PS3)',0), - (2549,'gh4vhalenwii',NULL,'Guitar Hero 4: Van Halen (Wii)',0), - (2550,'gh4vhalenwiiam',NULL,'Guitar Hero 4: Van Halen Automatch (Wii)',0), - (2551,'escviruswii',NULL,'Escape Virus (WiiWare)',0), - (2552,'rfactoryKRds',NULL,'Rune Factory: A Fantasy Harverst Moon (KOR) (DS)',0), - (2553,'banburadxds',NULL,'Banbura DX Photo Frame Radio (DS)',0), - (2554,'mebiuswii',NULL,'Mebius Drive (WiiWare)',0), - (2555,'okirakuwii',NULL,'Okiraku Daihugou Wii (WiiWare)',0), - (2556,'sbk09pc',NULL,'SBK \'09 (PC)',0), - (2557,'sbk09ps3',NULL,'SBK \'09 (PS3)',0), - (2558,'sbk09ps3am',NULL,'SBK \'09 Automatch (PS3)',0), - (2559,'sbk09pcam',NULL,'SBK \'09 Automatch (PC)',0), - (2560,'poriginpcjp',NULL,'Fear 2: Project Origin (JP) (PC)',0), - (2561,'poriginpcjpam',NULL,'Fear 2: Project Origin Automatch (JP) (PC)',0), - (2562,'poriginpcjpd',NULL,'Fear 2: Project Origin Demo (JP) (PC)',0), - (2563,'poriginps3jp',NULL,'Fear 2: Project Origin (JP) (PS3)',0), - (2564,'poriginps3jpam',NULL,'Fear 2: Project Origin Automatch (JP) (PS3)',0), - (2565,'poriginps3jpd',NULL,'Fear 2: Project Origin Demo (JP) (PS3)',0), - (2566,'section8pc',NULL,'Section 8 (PC)',0), - (2567,'section8pcam',NULL,'Section 8 Automatch (PC)',0), - (2568,'section8pcd',NULL,'Section 8 Demo (PC)',0), - (2569,'section8ps3',NULL,'Section 8 (PS3)',0), - (2570,'section8ps3am',NULL,'Section 8 Automatch (PS3)',0), - (2571,'section8ps3d',NULL,'Section 8 Demo (PS3)',0), - (2572,'section8x360',NULL,'Section 8 (Xbox360)',0), - (2573,'section8x360am',NULL,'Section 8 Automatch (Xbox360)',0), - (2574,'section8x360d',NULL,'Section 8 Demo (Xbox360)',0), - (2575,'buccaneerpc',NULL,'Buccaneer (PC)',0), - (2576,'buccaneerpcam',NULL,'Buccaneer Automatch (PC)',0), - (2577,'buccaneerpcd',NULL,'Buccaneer Demo (PC)',0), - (2578,'civ4coljp','5wddmt','Sid Meier\'s Civilization IV: Colonization (PC Japanese)',0), - (2579,'civ4coljpam','5wddmt','Sid Meier\'s Civilization IV: Colonization Automatch (PC Japanese)',0), - (2580,'beateratorpsp',NULL,'Beaterator (PSP)',0), - (2581,'beateratorpspam',NULL,'Beaterator Automatch (PSP)',0), - (2582,'beateratorpspd',NULL,'Beaterator Demo (PSP)',0), - (2583,'sonicrkords',NULL,'Sonic Rush Adventure (KOR) (DS)',0), - (2584,'mmadnesswii',NULL,'Military Madness (WiiWare)',0), - (2585,'chesschalwii',NULL,'Chess Challenge! (WiiWare)',0), - (2586,'chesschalwiiam',NULL,'Chess Challenge! Automatch (WiiWare)',0), - (2587,'narutorev3wii',NULL,'Naruto Shippuden: Clash of Ninja Revolution 3 (Wii)',0), - (2588,'decasport2wii',NULL,'Deca Sports 2 (Wii)',0), - (2589,'suparobods',NULL,'Suparobo Gakuen (DS)',0), - (2590,'gh4ghitswii',NULL,'Guitar Hero 4: Greatest Hits (Wii)',0), - (2591,'simsraceEUds',NULL,'MySims Racing DS (EU) (DS)',0), - (2592,'blockrushwii',NULL,'Blockrush! (WiiWare)',0), - (2593,'simsraceJPNds',NULL,'MySims Racing DS (JPN) (DS)',0), - (2594,'superv8pc',NULL,'Superstars V8 Racing (PC)',0), - (2595,'superv8pcam',NULL,'Superstars V8 Racing Automatch (PC)',0), - (2596,'superv8pcd',NULL,'Superstars V8 Racing Demo (PC)',0), - (2597,'superv8ps3',NULL,'Superstars V8 Racing (PS3)',0), - (2598,'superv8ps3am',NULL,'Superstars V8 Racing Automatch (PS3)',0), - (2599,'superv8ps3d',NULL,'Superstars V8 Racing Demo (PS3)',0), - (2600,'boardgamesds',NULL,'The Best of Board Games (DS)',0), - (2601,'cardgamesds',NULL,'The Best of Card Games (DS)',0), - (2602,'colcourseds',NULL,'Collision Course (DS)',0), - (2603,'brigades',NULL,'Gamespy Brigades',0), - (2604,'puyopuyo7ds',NULL,'PuyoPuyo 7 (DS/Wii)',0), - (2605,'qsolace',NULL,'Quantum of Solace',0), - (2606,'tcendwar',NULL,'Tom Clancy\'s EndWar',0), - (2607,'kidslearnwii',NULL,'Kids Learning Desk (WiiWare)',0), - (2608,'svsr10ps3',NULL,'WWE Smackdown vs. Raw 2010 (PS3)',0), - (2609,'svsr10ps3am',NULL,'WWE Smackdown vs. Raw 2010 Automatch (PS3)',0), - (2610,'svsr10ps3d',NULL,'WWE Smackdown vs. Raw 2010 Demo (PS3)',0), - (2611,'svsr10x360',NULL,'WWE Smackdown vs. Raw 2010 (Xbox 360)',0), - (2612,'svsr10x360am',NULL,'WWE Smackdown vs. Raw 2010 Automatch (Xbox 360)',0), - (2613,'svsr10x360d',NULL,'WWE Smackdown vs. Raw 2010 Demo (Xbox 360)',0), - (2614,'momo2010wii',NULL,'Momotaro Dentetsu 2010 Nendoban (Wii)',0), - (2615,'cardherods',NULL,'Card Hero DSi (DS)',0), - (2616,'cardherodsam',NULL,'Card Hero DSi Automatch (DS)',0), - (2617,'smball2iph',NULL,'Super Monkey Ball 2 (iPhone)',0), - (2618,'smball2ipham',NULL,'Super Monkey Ball 2 Automatch (iPhone)',0), - (2619,'smball2iphd',NULL,'Super Monkey Ball 2 Demo (iPhone)',0), - (2620,'beateratoriph',NULL,'Beaterator (iPhone)',0), - (2621,'beateratoripham',NULL,'Beaterator Automatch (iPhone)',0), - (2622,'beateratoriphd',NULL,'Beaterator Demo (iPhone)',0), - (2623,'conduitwii',NULL,'The Conduit (Wii)',0), - (2624,'hookagainwii',NULL,'Hooked Again! (Wii)',0), - (2625,'rfactory3ds',NULL,'Rune Factory 3 (DS)',0), - (2626,'disneydev',NULL,'Disney Development/Testing',0), - (2627,'disneydevam',NULL,'Disney Development/Testing Automatch',0), - (2628,'sporearenads',NULL,'Spore Hero Arena (DS)',0), - (2629,'treasurewldds',NULL,'Treasure World (DS)',0), - (2630,'unowii',NULL,'UNO (WiiWare)',0), - (2631,'mekurucawii',NULL,'Mekuruca (WiiWare)',0), - (2632,'bderlandspc',NULL,'Borderlands (PC)',0), - (2633,'bderlandspcam',NULL,'Borderlands Automatch (PC)',0), - (2634,'bderlandspcd',NULL,'Borderlands Demo (PC)',0), - (2635,'bderlandsps3',NULL,'Borderlands (PS3)',0), - (2636,'bderlandsps3am',NULL,'Borderlands Automatch (PS3)',0), - (2637,'bderlandsps3d',NULL,'Borderlands Demo (PS3)',0), - (2638,'bderlandsx360',NULL,'Borderlands (360)',0), - (2639,'bderlands360am',NULL,'Borderlands Automatch (360)',0), - (2640,'bderlandsx360d',NULL,'Borderlands Demo (360)',0), - (2641,'simsportsds',NULL,'MySims Sports (DS)',0), - (2642,'simsportswii',NULL,'MySims Sports (Wii)',0), - (2643,'pokedngnwii',NULL,'Pokemon Dungeon (Wii)',0), - (2644,'arma2pc',NULL,'Arma II (PC)',0), - (2645,'arma2pcam',NULL,'Arma II Automatch (PC)',0), - (2646,'arma2pcd',NULL,'Arma II Demo (PC)',0), - (2647,'rubikguidewii',NULL,'Rubik\'s Puzzle World: Guide (WiiWare)',0), - (2648,'quizmagic2ds',NULL,'Quiz Magic Academy DS2 (DS)',0), - (2649,'bandbrosEUds',NULL,'Daiggaso! Band Brothers DX (EU) (DS)',0), - (2650,'swsnow2wii',NULL,'Shaun White Snowboarding 2 (Wii)',0), - (2651,'scribnautsds',NULL,'Scribblenauts (DS)',0), - (2652,'fifasoc10ds',NULL,'FIFA Soccer 10 (DS)',0), - (2653,'foreverbl2wii',NULL,'Forever Blue 2 (Wii)',0), - (2654,'namcotest',NULL,'Namco SDK Test',0), - (2655,'namcotestam',NULL,'Namco SDK Test Automatch',0), - (2656,'namcotestd',NULL,'Namco SDK Test Demo',0), - (2657,'blindpointpc',NULL,'Blind Point (PC)',0), - (2658,'blindpointpcam',NULL,'Blind Point Automatch (PC)',0), - (2659,'blindpointpcd',NULL,'Blind Point Demo (PC)',0), - (2660,'propocket12ds',NULL,'PowerPro-kun Pocket 12 (DS)',0), - (2661,'seafarmwii',NULL,'Seafarm (WiiWare)',0), - (2662,'dragquestsds',NULL,'Dragon Quest S (DSiWare)',0), - (2663,'dawnheroesds',NULL,'Dawn of Heroes (DS)',0), - (2664,'monhunter3wii',NULL,'Monster Hunter 3 (JPN) (Wii)',0), - (2665,'appletest',NULL,'Apple SDK test',0), - (2666,'appletestam',NULL,'Apple SDK test Automatch',0), - (2667,'appletestd',NULL,'Apple SDK test Demo',0), - (2668,'harbunkods',NULL,'Harlequin Bunko (DS)',0), - (2669,'unodsi',NULL,'UNO (DSiWare)',0), - (2670,'beaterator',NULL,'Beaterator (PSP/iphone)',0), - (2671,'beateratoram',NULL,'Beaterator Automatch (PSP/iphone)',0), - (2672,'beateratord',NULL,'Beaterator Demo (PSP/iphone)',0), - (2673,'ragonlineKRds',NULL,'Ragunaroku Online DS (KOR) (DS)',0), - (2674,'dragoncrwnwii',NULL,'Dragon\'s Crown (Wii)',0), - (2675,'ascensionpc',NULL,'Ascension (PC)',0), - (2676,'ascensionpcam',NULL,'Ascension Automatch (PC)',0), - (2677,'ascensionpcd',NULL,'Ascension Demo (PC)',0), - (2678,'swbfespsp',NULL,'Star Wars: Battlefront - Elite Squadron (PSP)',0), - (2679,'swbfespspam',NULL,'Star Wars: Battlefront - Elite Squadron Automatch (PSP)',0), - (2680,'swbfespspd',NULL,'Star Wars: Battlefront - Elite Squadron Demo (PSP)',0), - (2681,'nba2k10wii',NULL,'NBA 2K10 (Wii)',0), - (2682,'nhl2k10wii',NULL,'NHL 2K10 (Wii)',0), - (2683,'mk9test',NULL,'Midway MK9 Test',0), - (2684,'mk9testam',NULL,'Midway MK9 Test Automatch',0), - (2685,'mk9testd',NULL,'Midway MK9 Test Demo',0), - (2686,'kateifestds',NULL,'Katei Kyoshi Hitman Reborn DS Vongole Festival Online (DS)',0), - (2687,'luminarc2EUds',NULL,'Luminous Arc 2 Will (EU) (DS)',0), - (2688,'tatvscapwii',NULL,'Tatsunoko vs. Capcom Ultimate All Stars (Wii)',0), - (2689,'petz09ds',NULL,'Petz Catz/Dogz/Hamsterz/Babiez 2009 (DS)',0), - (2690,'rtlwsportswii',NULL,'RTL Winter Sports 2010 (Wii)',0), - (2691,'tomenasawii',NULL,'Tomenasanner (WiiWare)',0), - (2692,'luchalibrepc',NULL,'Lucha Libre AAA 2010 (PC)',0), - (2693,'luchalibrepcam',NULL,'Lucha Libre AAA 2010 Automatch (PC)',0), - (2694,'luchalibrepcd',NULL,'Lucha Libre AAA 2010 Demo (PC)',0), - (2695,'luchalibreps3',NULL,'Lucha Libre AAA 2010 (PS3)',0), - (2696,'luchalibreps3am',NULL,'Lucha Libre AAA 2010 Automatch (PS3)',0), - (2697,'luchalibreps3d',NULL,'Lucha Libre AAA 2010 Demo (PS3)',0), - (2698,'simsflyerswii',NULL,'MySims Flyers (Wii)',0), - (2699,'ludicrouspc',NULL,'Ludicrous (PC)',0), - (2700,'ludicrouspcam',NULL,'Ludicrous Automatch (PC)',0), - (2701,'ludicrouspcd',NULL,'Ludicrous Demo (PC)',0), - (2702,'ludicrousmac',NULL,'Ludicrous (MAC)',0), - (2703,'ludicrousmacam',NULL,'Ludicrous Automatch (MAC)',0), - (2704,'ludicrousmacd',NULL,'Ludicrous Demo (MAC)',0), - (2705,'pbellumr1',NULL,'Parabellum Region 1 (PC)',0), - (2706,'pbellumr2',NULL,'Parabellum Region 2 (PC)',0), - (2707,'pbellumr3',NULL,'Parabellum Region 3 (PC)',0), - (2708,'imaginejdds',NULL,'Imagine: Jewelry Designer (DS)',0), - (2709,'imagineartds',NULL,'Imagine: Artist (DS)',0), - (2710,'tvshwking2wii',NULL,'TV Show King 2 (WiiWare)',0), - (2711,'sballrevwii',NULL,'Spaceball: Revolution (WiiWare)',0), - (2712,'orderofwarpc',NULL,'Order of War (PC)',0), - (2713,'orderofwarpcam',NULL,'Order of War Automatch (PC)',0), - (2714,'orderofwarpcd',NULL,'Order of War Demo (PC)',0), - (2715,'lbookofbigsds',NULL,'Little Book of Big Secrets (DS)',0), - (2716,'scribnauteuds',NULL,'Scribblenauts (EU) (DS)',0), - (2717,'buccaneer',NULL,'Buccaneer The Pursuit of Infamy',0), - (2718,'kenteitvwii',NULL,'Kentei! TV Wii (Wii)',0), - (2719,'yugioh5dwii',NULL,'Yu-Gi-Oh! 5D\'s Duel Simulator (Wii)',0), - (2720,'fairyfightps3',NULL,'Fairytale Fights (PS3)',0), - (2721,'fairyfightps3am',NULL,'Fairytale Fights Automatch (PS3)',0), - (2722,'fairyfightps3d',NULL,'Fairytale Fights Demo (PS3)',0), - (2723,'fairyfightpc',NULL,'Fairytale Fights (PC)',0), - (2724,'fairyfightpcam',NULL,'Fairytale Fights Automatch (PC)',0), - (2725,'fairyfightpcd',NULL,'Fairytale Fights Demo (PC)',0), - (2726,'50centjpnps3',NULL,'50 Cent: Blood on the Sand (JPN) (PS3)',0), - (2727,'50centjpnps3am',NULL,'50 Cent: Blood on the Sand Automatch (JPN) (PS3)',0), - (2728,'50centjpnps3d',NULL,'50 Cent: Blood on the Sand Demo (JPN) (PS3)',0), - (2729,'codmw2ds',NULL,'Call of Duty: Modern Warfare 2 (DS)',0), - (2730,'jbond2009ds',NULL,'James Bond 2009 (DS)',0), - (2731,'resevildrkwii',NULL,'Resident Evil: The Darkside Chronicles (Wii)',0), - (2732,'musicmakerwii',NULL,'Music Maker (Wii)',0), - (2733,'figlandds',NULL,'Figland (DS)',0), - (2734,'bonkwii',NULL,'Bonk (Wii)',0), - (2735,'bomberman2wii',NULL,'Bomberman 2 (Wii)',0), - (2736,'bomberman2wiid',NULL,'Bomberman 2 Demo (Wii)',0), - (2737,'dreamchronwii',NULL,'Dream Chronicle (Wii)',0), - (2738,'gokuidsi',NULL,'Gokui (DSiWare)',0), - (2739,'usingwii',NULL,'U-Sing (Wii)',0), - (2740,'shikagariwii',NULL,'Shikagari (Wii)',0), - (2741,'puyopuyo7wii',NULL,'Puyopuyo 7 (Wii)',0), - (2742,'winelev10wii',NULL,'Winning Eleven Play Maker 2010 (Wii)',0), - (2743,'section8pcb',NULL,'Section 8 Beta (PC)',0), - (2744,'section8pcbam',NULL,'Section 8 Beta Automatch (PC)',0), - (2745,'section8pcbd',NULL,'Section 8 Beta Demo (PC)',0), - (2746,'ubraingamesds',NULL,'Ultimate Brain Games (DS)',0), - (2747,'ucardgamesds',NULL,'Ultimate Card Games (DS)',0), - (2748,'postpetds',NULL,'PostPetDS Yumemiru Momo to Fushigi no Pen (DS)',0), - (2749,'mfightbbultds',NULL,'Metal Fight Bay Blade ULTIMATE (DS)',0), - (2750,'strategistwii',NULL,'Strategist (Wii)',0), - (2751,'bmbermanexdsi',NULL,'Bomberman Express (DSiWare)',0), - (2752,'blockoutwii',NULL,'Blockout (Wii)',0), - (2753,'rdr2x360',NULL,'Red Dead Redemption (x360)',0), - (2754,'rdr2x360am',NULL,'Red Dead Redemption Automatch (x360)',0), - (2757,'fairyfightspc',NULL,'Fairytale Fights (PC)',0), - (2758,'fairyfightspcam',NULL,'Fairytale Fights Automatch (PC)',0), - (2759,'fairyfightspcd',NULL,'Fairytale Fights Demo (PC)',0), - (2760,'stalkercoppc',NULL,'STALKER: Call of Pripyat (PC)',0), - (2761,'stalkercoppcam',NULL,'STALKER: Call of Pripyat Automatch (PC)',0), - (2762,'stalkercoppcd',NULL,'STALKER: Call of Pripyat Demo (PC)',0), - (2763,'strategistpc',NULL,'The Strategist (PC)',0), - (2764,'strategistpcam',NULL,'The Strategist Automatch (PC)',0), - (2765,'strategistpcd',NULL,'The Strategist Demo (PC)',0), - (2766,'strategistpsn',NULL,'The Strategist (PSN)',0), - (2767,'strategistpsnam',NULL,'The Strategist Automatch (PSN)',0), - (2768,'strategistpsnd',NULL,'The Strategist Demo (PSN)',0), - (2769,'tataitemogwii',NULL,'Tataite! Mogumon US/EU (WiiWare)',0), - (2770,'ufc10ps3',NULL,'UFC 2010 (PS3)',0), - (2771,'ufc10ps3am',NULL,'UFC 2010 Automatch (PS3)',0), - (2772,'ufc10ps3d',NULL,'UFC 2010 Demo (PS3)',0), - (2773,'ufc10x360',NULL,'UFC 2010 (x360)',0), - (2774,'ufc10x360am',NULL,'UFC 2010 Automatch (x360)',0), - (2775,'ufc10x360d',NULL,'UFC 2010 Demo (x360)',0), - (2776,'mmtest',NULL,'Matchmaking Backend Test',0), - (2777,'mmtestam',NULL,'Matchmaking Backend Test Automatch',0), - (2778,'talesofgrawii',NULL,'Tales of Graces (Wii)',0), - (2779,'dynamiczanwii',NULL,'Dynamic Zan (Wii)',0), - (2780,'fushigidunds',NULL,'Fushigi no Dungeon Furai no Shiren 4 Kami no Me to Akama no Heso (DS)',0), - (2781,'idraculawii',NULL,'iDracula (WiiWare)',0), - (2782,'metalfightds',NULL,'Metal Fight Bayblade (DS)',0), - (2783,'wormswiiware',NULL,'Worms (WiiWare)',0), - (2784,'wormswiiwaream',NULL,'Worms Automatch (WiiWare)',0), - (2785,'justsingds',NULL,'Just Sing! (DS)',0), - (2786,'gtacwarspsp',NULL,'Grand Theft Auto: Chinatown Wars (PSP)',0), - (2787,'gtacwarspspam',NULL,'Grand Theft Auto: Chinatown Wars Automatch (PSP)',0), - (2788,'gtacwarspspd',NULL,'Grand Theft Auto: Chinatown Wars Demo (PSP)',0), - (2789,'gtacwiphone',NULL,'Grand Theft Auto: Chinatown Wars (iPhone)',0), - (2790,'gtacwiphoneam',NULL,'Grand Theft Auto: Chinatown Wars Automatch (iPhone)',0), - (2791,'gtacwiphoned',NULL,'Grand Theft Auto: Chinatown Wars Demo (iPhone)',0), - (2792,'trkmaniads',NULL,'Trackmania (DS)',0), - (2793,'trkmaniawii',NULL,'Trackmania (Wii)',0), - (2794,'megaman10wii',NULL,'Mega Man 10 (WiiWare)',0), - (2795,'aarmy3',NULL,'America\'s Army 3',0), - (2796,'tycoonnyc',NULL,'Tycoon City - New York',0), - (2797,'sinpunish2wii',NULL,'Sin & Punishment 2 (Wii)',0), - (2798,'fuelps3ptchd',NULL,'FUEL (PS3) Patched version',0), - (2799,'fuelps3ptchdam',NULL,'FUEL Automatch (PS3) Patched version',0), - (2800,'sonicdlwii',NULL,'Sonic DL (WiiWare)',0), - (2801,'demonforgeps3',NULL,'Demon\'s Forge (PS3)',0), - (2802,'demonforgeps3am',NULL,'Demon\'s Forge Automatch (PS3)',0), - (2803,'demonforgeps3d',NULL,'Demon\'s Forge Demo (PS3)',0), - (2804,'demonforgepc',NULL,'Demon\'s Forge (PC)',0), - (2805,'demonforgepcam',NULL,'Demon\'s Forge Automatch (PC)',0), - (2806,'demonforgepcd',NULL,'Demon\'s Forge Demo (PC)',0), - (2807,'hooploopwii',NULL,'HooperLooper (WiiWare)',0), - (2809,'test1',NULL,'test1',0), - (2810,'maxpayne3pc',NULL,'Max Payne 3 (PC)',0), - (2811,'maxpayne3pcam',NULL,'Max Payne 3 Automatch (PC)',0), - (2812,'maxpayne3pcd',NULL,'Max Payne 3 Demo (PC)',0), - (2813,'maxpayne3ps3',NULL,'Max Payne 3 (PS3)',0), - (2814,'maxpayne3ps3am',NULL,'Max Payne 3 Automatch (PS3)',0), - (2815,'maxpayne3ps3d',NULL,'Max Payne 3 Demo (PS3)',0), - (2816,'maxpayne3x360',NULL,'Max Payne 3 (360)',0), - (2817,'maxpayne3x360am',NULL,'Max Payne 3 Automatch (360)',0), - (2818,'maxpayne3x360d',NULL,'Max Payne 3 Demo (360)',0), - (2819,'wordjongeuds',NULL,'Wordjong EU (DS)',0), - (2820,'sengo3wii',NULL,'Sengokumuso 3',0), - (2821,'bewarewii',NULL,'Beware (WiiWare)',0), - (2822,'hinterland',NULL,'Hinterland',0), - (2823,'hastpaint2wii',NULL,'Greg Hastings Paintball 2 (Wii)',0), - (2824,'rockstarsclub',NULL,'Rockstar Social Club',0), - (2825,'rockstarsclubam',NULL,'Rockstar Social Club Automatch',0), - (2826,'plandmajinds',NULL,'Professor Layton and Majin no Fue (DS)',0), - (2827,'powerkoushds',NULL,'Powerful Koushien (DS)',0), - (2828,'cavestorywii',NULL,'Cave Story (WiiWare)',0), - (2829,'blahblahtest',NULL,'Just another test for masterid',0), - (2830,'blahtest',NULL,'Just another test for masterid',0), - (2831,'blahmasterid',NULL,'Just another test for masterid',0), - (2832,'bädmasterid',NULL,'',0), - (2833,'explomäntest',NULL,'blah',0), - (2836,'3dpicrosseuds',NULL,'3D Picross (EU) (DS)',0), - (2837,'gticsfestwii',NULL,'GTI Club Supermini Festa (Wii)',0), - (2838,'narutor3euwii',NULL,'Naruto Shippuden: Clash of Ninja Revolution 3 EU (Wii)',0), - (2840,'sparta2pc',NULL,'Sparta 2: The Conquest of Alexander the Great (PC)',0), - (2841,'sparta2pcam',NULL,'Sparta 2: The Conquest of Alexander the Great Automatch (PC)',0), - (2842,'sparta2pcd',NULL,'Sparta 2: The Conquest of Alexander the Great Demo (PC)',0), - (2843,'superv8ncpc',NULL,'Superstars V8 Next Challenge (PC)',0), - (2844,'superv8ncpcam',NULL,'Superstars V8 Next Challenge Automatch (PC)',0), - (2845,'superv8ncpcd',NULL,'Superstars V8 Next Challenge Demo (PC)',0), - (2846,'superv8ncps3',NULL,'Superstars V8 Next Challenge (PS3)',0), - (2847,'superv8ncps3am',NULL,'Superstars V8 Next Challenge Automatch (PS3)',0), - (2848,'superv8ncps3d',NULL,'Superstars V8 Next Challenge Demo (PS3)',0), - (2849,'ikaropc',NULL,'Ikaro (PC)',0), - (2850,'ikaropcam',NULL,'Ikaro Automatch (PC)',0), - (2851,'ikaropcd',NULL,'Ikaro Demo (PC)',0), - (2852,'ufc10ps3DEV',NULL,'UFC 2010 DEV (PS3-DEV)',0), - (2853,'ufc10ps3DEVam',NULL,'UFC 2010 DEV Automatch (PS3-DEV)',0), - (2854,'ufc10ps3DEVd',NULL,'UFC 2010 DEV Demo (PS3-DEV)',0), - (2855,'ufc10x360dev',NULL,'UFC 2010 DEV (360-DEV)',0), - (2856,'ufc10x360devam',NULL,'UFC 2010 DEV Automatch (360-DEV)',0), - (2857,'ufc10x360devd',NULL,'UFC 2010 DEV Demo (360-DEV)',0), - (2858,'ragonlinenads',NULL,'Ragunaroku Online DS (NA) (DS)',0), - (2859,'hoopworldwii',NULL,'Hoopworld (Wii)',0), - (2860,'foxtrotpc',NULL,'Foxtrot (PC)',0), - (2861,'foxtrotpcam',NULL,'Foxtrot Automatch (PC)',0), - (2862,'foxtrotpcd',NULL,'Foxtrot Demo (PC)',0), - (2863,'civ5',NULL,'Civilization 5',0), - (2864,'heroeswii',NULL,'Heroes (Wii)',0), - (2865,'yugiohwc10ds',NULL,'Yu-Gi-Oh! World Championship 2010 (DS)',0), - (2866,'sbkxpc',NULL,'SBK X: Superbike World Championship (PC)',0), - (2867,'sbkxpcam',NULL,'SBK X: Superbike World Championship Automatch (PC)',0), - (2868,'sbkxpcd',NULL,'SBK X: Superbike World Championship Demo (PC)',0), - (2869,'sbkxps3',NULL,'SBK X: Superbike World Championship (PS3)',0), - (2870,'sbkxps3am',NULL,'SBK X: Superbike World Championship Automatch (PS3)',0), - (2871,'sbkxps3d',NULL,'SBK X: Superbike World Championship Demo (PS3)',0), - (2872,'famista2010ds',NULL,'Famista 2010 (DS)',0), - (2873,'bokutwinvilds',NULL,'Bokujyo Monogatari Twin Village (DS)',0), - (2874,'destruction',NULL,'Destruction 101 (Namco Bandai)',0), - (2875,'destructionam',NULL,'Destruction 101 Automatch',0), - (2876,'lumark3eyesds',NULL,'Luminous Ark 3 Eyes (DS)',0), - (2877,'othellowii',NULL,'Othello (WiiWare)',0), - (2878,'painkresurrpc',NULL,'Painkiller Resurrection (PC)',0), - (2879,'painkresurrpcam',NULL,'Painkiller Resurrection Automatch (PC)',0), - (2880,'painkresurrpcd',NULL,'Painkiller Resurrection Demo (PC)',0), - (2881,'fantcubewii',NULL,'Fantastic Cube (WiiWare)',0), - (2882,'3dpicrossUSds',NULL,'3D Picross (US) (DS)',0), - (2883,'svsr11ps3',NULL,'Smackdown vs Raw 2011 (PS3)',0), - (2884,'svsr11ps3am',NULL,'Smackdown vs Raw 2011 Automatch (PS3)',0), - (2885,'svsr11ps3d',NULL,'Smackdown vs Raw 2011 Demo (PS3)',0), - (2886,'svsr11x360',NULL,'Smackdown vs Raw 2011 (x360)',0), - (2887,'svsr11x360am',NULL,'Smackdown vs Raw 2011 Automatch (x360)',0), - (2888,'svsr11x360d',NULL,'Smackdown vs Raw 2011 Demo (x360)',0), - (2889,'bderlandruspc',NULL,'Borderlands RUS (PC)',0), - (2890,'bderlandruspcam',NULL,'Borderlands RUS Automatch (PC)',0), - (2891,'bderlandruspcd',NULL,'Borderlands RUS Demo (PC)',0), - (2892,'krabbitpcmac',NULL,'KrabbitWorld Origins (PC/Mac)',0), - (2893,'krabbitpcmacam',NULL,'KrabbitWorld Origins Automatch (PC/Mac)',0), - (2894,'krabbitpcmacd',NULL,'KrabbitWorld Origins Demo (PC/Mac)',0), - (2895,'gunnylamacwii',NULL,'GUNBLADE NY & L.A. MACHINEGUNS (Wii)',0), - (2896,'rbeaverdefwii',NULL,'Robocalypse - Beaver Defense (WiiWare)',0), - (2897,'surkatamarwii',NULL,'Surinukeru Katamari (WiiWare)',0), - (2898,'snackdsi',NULL,'Snack (DSiWare)',0), - (2899,'rpgtkooldsi',NULL,'RPG tkool DS (DSi)',0), - (2900,'mh3uswii',NULL,'Monster Hunter 3 (US/EU) (Wii)',0), - (2901,'lanoireps3',NULL,'L.A. Noire (PS3)',0), - (2902,'lanoireps3am',NULL,'L.A. Noire Automatch (PS3)',0), - (2903,'lanoireps3d',NULL,'L.A. Noire Demo (PS3)',0), - (2904,'lanoirex360',NULL,'L.A. Noire (x360)',0), - (2905,'lanoirex360am',NULL,'L.A. Noire Automatch (x360)',0), - (2906,'lanoirex360d',NULL,'L.A. Noire Demo (x360)',0), - (2907,'lanoirepc',NULL,'L.A. Noire (PC)',0), - (2908,'lanoirepcam',NULL,'L.A. Noire Automatch (PC)',0), - (2909,'lanoirepcd',NULL,'L.A. Noire Demo (PC)',0), - (2910,'digimonsleds',NULL,'Digimon Story Lost Evolution (DS)',0), - (2911,'syachi2ds',NULL,'syachi 2 (DS)',0), - (2912,'puzzleqt2ds',NULL,'Puzzle Quest 2 (DS)',0), - (2913,'phybaltraiwii',NULL,'Physiofun Balance Trainer (WiiWare)',0), - (2914,'decasport3wii',NULL,'Deca Sports 3 (Wii)',0), - (2915,'tetrisdeluxds',NULL,'Tetris Party Deluxe (DSiWare)',0), - (2916,'gsiphonefw',NULL,'GameSpy iPhone Framework',0), - (2917,'necrolcpc',NULL,'NecroVisioN: Lost Company (PC)',0), - (2918,'necrolcpcam',NULL,'NecroVisioN: Lost Company Automatch (PC)',0), - (2919,'necrolcpcd',NULL,'NecroVisioN: Lost Company Demo (PC)',0), - (2920,'startrekmac',NULL,'Star Trek: D-A-C (MAC)',0), - (2921,'startrekmacam',NULL,'Star Trek Automatch (MAC)',0), - (2922,'captsubasads',NULL,'Captain tsubasa (DS)',0), - (2923,'cb2ds',NULL,'CB2 (DS)',0), - (2924,'katekyohitds',NULL,'katekyo hitman REBORN! DS FLAME RUMBLE XX (DS)',0), - (2925,'cardiowrk2wii',NULL,'Cardio Workout 2 (Wii)',0), - (2926,'boyvgirlcwii',NULL,'Boys vs Girls Summer Camp (Wii)',0), - (2927,'keenracerswii',NULL,'Keen Racers (WiiWare)',0), - (2928,'scribnaut2pc',NULL,'Scribblenauts 2 (PC)',0), - (2929,'scribnaut2pcam',NULL,'Scribblenauts 2 Automatch (PC)',0), - (2930,'agentps3',NULL,'Agent (PS3)',0), - (2931,'agentps3am',NULL,'Agent Automatch (PS3)',0), - (2932,'girlskoreads',NULL,'Girls_Korea (DS)',0), - (2933,'jyankenparwii',NULL,'Jyanken (rock-paper-scissors) Party Paradise (WiiWare)',0), - (2934,'protocolwii',NULL,'Protocol (WiiWare)',0), - (2935,'DeathtoSpies',NULL,'Death to Spies',0), - (2936,'svsr11x360dev',NULL,'Smackdown vs Raw 2011 DEV (x360)',0), - (2937,'svsr11x360devam',NULL,'Smackdown vs Raw 2011 DEV Automatch (x360)',0), - (2938,'svsr11ps3dev',NULL,'Smackdown vs Raw 2011 DEV (PS3)',0), - (2939,'svsr11ps3devam',NULL,'Smackdown vs Raw 2011 DEV Automatch (PS3)',0), - (2940,'dynaztrialwii',NULL,'Dynamic Zan TRIAL (Wii)',0), - (2941,'molecontrolpc',NULL,'Mole Control (PC)',0), - (2942,'molecontrolpcam',NULL,'Mole Control Automatch (PC)',0), - (2943,'sakwcha2010ds',NULL,'Sakatsuku DS WorldChallenge 2010 (DS)',0), - (2944,'MenofWar',NULL,'Men of War',0), - (2945,'na2rowpc',NULL,'NAT2 Row (PC)',0), - (2946,'na2rowpcam',NULL,'NAT2 Row Automatch (PC)',0), - (2947,'na2runpc',NULL,'NAT2 Run (PC)',0), - (2948,'na2runpcam',NULL,'NAT2 Run Automatch (PC)',0), - (2949,'trackmania2ds',NULL,'Trackmania DS 2 (DS)',0), - (2950,'pangmagmichds',NULL,'Pang: Magical Michael (DS)',0), - (2951,'mysimsflyerds',NULL,'MySims Flyers (DS)',0), - (2952,'mysimsflyEUds',NULL,'MySims Flyers EU (DS)',0), - (2953,'kodawar2010ds',NULL,'Kodawari Saihai Simulation Ochanoma Pro Yakyu DS 2010 Verison (DS)',0), - (2954,'topspin4wii',NULL,'TOPSPIN 4 (Wii)',0), - (2955,'ut3onlive',NULL,'Unreal Tournament 3 ONLIVE',0), - (2956,'ut3onliveam',NULL,'Unreal Tournament 3 ONLIVE Automatch',0), - (2957,'combatzonepc',NULL,'Combat Zone - Special Forces (PC)',0), - (2958,'combatzonepcam',NULL,'Combat Zone - Special Forces Automatch (PC)',0), - (2959,'combatzonepcd',NULL,'Combat Zone - Special Forces Demo (PC)',0), - (2960,'sinpun2NAwii',NULL,'Sin & Punishment 2 NA (Wii)',0), - (2962,'capricornam',NULL,'Crysis 2 Automatch (PC)',0), - (2963,'crysis2pcd',NULL,'Crysis 2 Demo (PC)',0), - (2964,'crysis2ps3',NULL,'Crysis 2 (PS3)',0), - (2965,'crysis2ps3am',NULL,'Crysis 2 Automatch (PS3)',0), - (2966,'crysis2ps3d',NULL,'Crysis 2 Demo (PS3)',0), - (2967,'crysis2x360',NULL,'Crysis 2 (Xbox 360)',0), - (2968,'crysis2x360am',NULL,'Crysis 2 Automatch (Xbox 360)',0), - (2969,'crysis2x360d',NULL,'Crysis 2 Demo (Xbox 360)',0), - (2970,'ZumaDeluxe',NULL,'Zuma Deluxe',0), - (2971,'cellfacttwpc',NULL,'Cell Factor:TW (PC)',0), - (2972,'cellfacttwpcam',NULL,'Cell Factor:TW Automatch (PC)',0), - (2973,'firearmsevopc',NULL,'Firearms Evolution (PC)',0), - (2974,'firearmsevopcam',NULL,'Firearms Evolution Automatch (PC)',0), - (2975,'winel10jpnwii',NULL,'Winning Eleven PLAY MAKER 2010 Japan Edition (Wii)',0), - (2976,'winel10jpnwiiam',NULL,'Winning Eleven PLAY MAKER 2010 Japan Edition Automatch (Wii)',0), - (2977,'bldragonNAds',NULL,'Blue Dragon - Awakened Shadow',0), - (2978,'bldragonNAdsam',NULL,'Blue Dragon - Awakened Shadow Automatch',0), - (2979,'sonic2010wii',NULL,'SONIC 2010 (Wii)',0), - (2980,'sonic2010wiiam',NULL,'SONIC 2010 Automatch (Wii)',0), - (2981,'harmoon2kords',NULL,'Harvest Moon 2 Korea (DS)',0), - (2982,'harmoon2kordsam',NULL,'Harvest Moon 2 Korea Automatch (DS)',0), - (2983,'jbondmv2ds',NULL,'James Bond Non Movie 2 (2010) (DS)',0), - (2984,'jbondmv2dsam',NULL,'James Bond Non Movie 2 Automatch (2010) (DS)',0), - (2985,'casinotourwii',NULL,'Casino Tournament (Wii)',0), - (2986,'casinotourwiiam',NULL,'Casino Tournament Automatch (Wii)',0), - (3300,'capricorn','8TTq4M','Crysis 2 (PC)',0); -UNLOCK TABLES; - - -LOCK TABLES `grouplist` WRITE; -INSERT INTO `grouplist` (`groupid`, `gameid`, `roomname`) VALUES - (1,5,'daikatana test group'), - (2,1,'Newbies'), - (3,1,'Experts'), - (4,1,'Farm Animals'), - (5,256,'Skirmish'), - (6,256,'Domination'), - (7,192,'Test VP3 Tourney'), - (9,192,'this'), - (18,192,'b'), - (19,192,'b'), - (20,285,'Rookies'), - (21,285,'Amateurs'), - (22,285,'Pros'), - (24,192,'b'), - (25,256,'Slaughter'), - (26,256,'Soul Harvest'), - (27,256,'Allied'), - (30,192,'LumberJack VP3 Test Tourney'), - (44,192,'test6ladder'), - (57,192,'asdf'), - (63,192,'BillsTest2'), - (64,192,'BillsTest2'), - (101,192,'this'), - (102,308,'Beginner'), - (103,308,'Intermediate'), - (104,308,'Advanced'), - (105,192,'this'), - (106,192,'hello'), - (107,192,'mytest'), - (108,192,'arts ladder'), - (109,192,'Seans Ladder'), - (110,192,'seans test ladder'), - (111,192,'Seans Test Ladder'), - (112,192,'9-Ball Challenge'), - (113,192,'TestOct23'), - (114,192,'abcd'), - (115,192,'Lumberjack VP3 Test Tourny #2'), - (116,192,'9-Ball Heaven'), - (117,192,'QA Test Ladder'), - (118,192,'GSI Test - do not join'), - (119,192,'reload test'), - (122,192,'Tonys 9-Ball tourney'), - (123,192,'Tonys 9-Ball tourney 2'), - (124,192,'Tonys Moved Database Test'), - (125,192,'outputdir test tourney'), - (127,192,'b'), - (141,192,'aphexweb1 test'), - (143,192,'registration test'), - (144,192,'w'), - (147,192,'arts ladder test'), - (148,192,'gsi test ladder nov 1'), - (149,192,'test'), - (150,192,'arts ladder'), - (151,192,'gsi test ladder'), - (152,192,'tonys ladder'), - (153,192,'9-Ball Ladder: Public Test'), - (154,192,'Interplay QA test'), - (155,192,'Interplay QA test'), - (156,192,'Qa Test 2'), - (157,192,'QA DEDICATED TEST'), - (158,192,'TEN BALL - QA TEST'), - (159,192,'BILLIARDS'), - (160,192,'IP Rotation'), - (161,192,'10 BALL - QA TEST'), - (162,192,'Savy & Sean'), - (163,192,'Willow & Erik'), - (164,192,'Savy'), - (165,192,'6 Ball game'), - (166,192,'VP3 Private Patch Testing'), - (167,192,'Patch (#2) Final Testing'), - (168,337,'Main Lobby'), - (169,337,'{01}Cadet'), - (170,337,'{02}Captain'), - (171,337,'{03}Admiral'), - (172,412,'{01}General Chat'), - (173,412,'{04}Teen Chat'), - (176,412,'{02}Family And Friends'), - (177,412,'{03}College Chat'), - (189,15,'{01}Half-Life Room 1'), - (190,15,'{02}Half-Life Room 2'), - (191,15,'{01}Counter-Strike: Special Air Service'), - (192,15,'{01}Counter-Strike: GSG-9'), - (193,15,'{01}Counter-Strike: Counter-Terrorist Force'), - (194,15,'{01}Counter-Strike: Seal Team 6'), - (195,15,'{11}Firearms Room'), - (196,15,'{13}TeamFortress Classic Room'), - (197,15,'{10}Day of Defeat Room'), - (198,15,'{12}Front Line Force Room'), - (199,412,'{01}Action Games'), - (200,412,'{02}Role Playing Games'), - (201,412,'{03}Strategy Games'), - (202,412,'{04}Sports Games'), - (203,412,'{05}Simulation Games'), - (204,412,'{06}Tactical Games'), - (206,15,'{03}Help With Half-Life'), - (207,323,'{01}Counter-Terrorist Force'), - (208,323,'{01}Seal Team Six'), - (209,323,'{02}Help with Counter-Strike'), - (210,22,'{02}Quake 3 Veterans Room'), - (211,22,'{01}Quake 3 Main Room'), - (212,22,'{05}Rocket Arena 3 Room'), - (213,22,'{02}Freeze Tag'), - (214,22,'{04}Quake 3 Fortress Room'), - (215,22,'{06}Threewave CTF'), - (216,22,'{07}Urban Terror Beta 2 Room'), - (217,22,'{08}Weapons Factory Arena Room'), - (218,22,'{01}Excessive Room'), - (219,401,'{03}Spades Advanced Lobby'), - (220,401,'{01}Spades Newbie Lobby'), - (221,401,'{04}Spades Ranked Lobby'), - (222,401,'{02}Spades Social Lobby'), - (223,403,'{02}Backgammon Ranked Lobby'), - (224,403,'{01}Backgammon General Lobby'), - (225,400,'Poker Advanced Lobby'), - (226,400,'Poker Newbies Lobby'), - (227,400,'Poker Ranked Lobby'), - (228,400,'Poker Social Lobby'), - (229,402,'{01}Hearts General Lobby'), - (230,402,'{02}Hearts Ranked Lobby'), - (231,22,'{06}Help with Quake 3'), - (232,22,'{05}Team Deathmatch'), - (233,15,'{01}Counter-Strike: For Great Justice!'), - (234,15,'{01}Counter-Strike: Clan Battle Room'), - (236,15,'{13}Action Half-Life Room'), - (241,15,'{03}Opposing Force Room'), - (242,15,'{04}PlanetHalfLife Arcade Event Lobby'), - (243,401,'{05}Spades Tournament Lobby'), - (244,402,'{03}Hearts Tournament Lobby'), - (245,403,'{03}Backgammon Tournament Lobby'), - (246,192,'Squish Test ladder'), - (247,292,'Main'), - (248,292,'Tournaments'), - (250,361,'{01}Geral'), - (251,361,'{02}Jogos PC'), - (252,15,'{14}Deathmatch Classic'), - (259,414,'{01}Everon'), - (260,414,'{01}Malden'), - (261,414,'{02}Dedicated Servers'), - (262,414,'{03}Co-op'), - (263,414,'{04}Capture the Flag'), - (264,414,'{05}Test Zone'), - (265,361,'{03}Jogos Consolas'), - (266,361,'{04}Hardware'), - (267,361,'{05}Torneios e Eventos'), - (269,361,'{06}Comunidade'), - (270,483,'THPS3 Internet'), - (271,400,'Poker Tournament Lobby'), - (272,22,'{03}Orange Smoothie'), - (273,504,'Main'), - (274,328,'{01}Action'), - (275,328,'{02}Roleplaying'), - (276,328,'{03}Team (N vs. N)'), - (277,328,'{04}Social'), - (278,328,'{05}Persistent World Action'), - (279,328,'{06}Alternative'), - (280,292,'Korean'), - (281,509,'The Downs'), - (288,509,'Forest Heart'), - (295,509,'Tharsis'), - (299,523,'Main Lobby'), - (300,523,'{01}Cadet'), - (301,523,'{02}Captain'), - (302,523,'{03}Admiral'), - (303,22,'{03}Capture the Flag'), - (304,22,'{04}Deathmatch'), - (305,292,'Français'), - (306,292,'Deutsch'), - (307,292,'KIS'), - (308,292,'KAG'), - (310,362,'{01}Juegos'), - (311,362,'{02}Adolescentes'), - (312,362,'{03}Encuentros'), - (313,362,'{04}Maduritos'), - (314,362,'{05}Sexo'), - (315,564,'Tony Hawk 2x'), - (316,564,'Halo'), - (317,564,'NASCAR Heat 2002'), - (318,568,'{01}Eastern Front'), - (319,568,'{01}Western Front'), - (320,568,'{01}North African Campaign'), - (321,568,'{01}Pacific Campaign'), - (322,568,'{01}Guadalcanal Campaign'), - (323,568,'{01}Soviet Winter Offensive'), - (324,363,'{01}Juegos'), - (325,363,'{02}Adolescentes'), - (326,363,'{03}Encuentros'), - (327,363,'{04}Maduritos'), - (328,363,'{05}Romance'), - (329,492,'{01}Eastern Front'), - (330,492,'{01}Western Front'), - (331,492,'{02}Round-based Match'), - (332,492,'{04}Objective-based Match'), - (333,492,'{01}Deathmatch'), - (334,492,'{03}Team Match'), - (335,492,'{01}North Africa'), - (336,492,'{01}Siegfried Line'), - (337,492,'{03}Team Match'), - (338,492,'{04}Objective-based Match'), - (339,590,'(01)Power Plant'), - (340,590,'(01)Tiberium Refinery'), - (341,590,'(01)Weapons Factory'), - (342,590,'(01)Infantry Barracks'), - (343,590,'(01)GDI Guard Tower'), - (344,590,'(01)Construction Yard'), - (345,590,'(01)Hand of Nod'), - (346,590,'(01)Nod Airstrip'), - (347,15,'{15}Desert Crisis'), - (352,577,'(01)Power Plant'), - (353,577,'(01)Tiberium Refinery'), - (354,577,'(01)Weapons Factory'), - (355,577,'(01)Infantry Barracks'), - (356,577,'(01)GDI Guard Tower'), - (357,577,'(01)Construction Yard'), - (358,577,'(01)Hand of Nod'), - (359,577,'(01)Nod Airstrip'), - (361,564,'Tony Hawk 3'), - (362,564,'MotoGP'), - (363,328,'{07}Story'), - (364,328,'{08}Story Lite'), - (365,328,'{09}Melee (1 vs. N)'), - (366,328,'{10}Arena (1 vs. N)'), - (367,328,'{11}Persistent World Story'), - (368,328,'{12}Solo'), - (369,412,'{05}Romance'), - (370,328,'{13}Tech Support'), - (371,15,'{16}Ricochet'), - (372,564,'Australia'), - (373,564,'Europe'), - (374,564,'United Kingdom'), - (375,610,'{01}Infantry'), - (376,610,'{01}Combat Engineering'), - (377,610,'{01}Combat Operations'), - (378,610,'{01}Special Forces'), - (379,610,'{01}Armor'), - (380,610,'{01}Aviation Operations'), - (381,610,'{05}MOUT McKenna'), - (382,610,'{05}MOUT McKenna'), - (383,610,'{01}Bridge Crossing'), - (384,610,'{03}Headquarters Raid'), - (385,610,'{04}Insurgent Camp'), - (386,610,'{06}Pipeline'), - (387,610,'{02}Collapsed Tunnel'), - (388,610,'{01}Bridge Crossing'), - (389,675,'GroupRoom1'), - (390,675,'GroupRoom2'), - (391,675,'GroupRoom3'), - (392,675,'QuickMatch'), - (393,564,'NFL Fever 2003'), - (394,684,'Los Angeles (Newbies)'), - (395,684,'Los Angeles (Experts)'), - (396,684,'Tokyo (Newbies)'), - (397,684,'Tokyo (Experts)'), - (398,684,'Paris (Newbies)'), - (399,684,'Paris (Experts)'), - (400,684,'Battle (Newbies)'), - (401,684,'Battle (Experts)'), - (402,671,'(01)Allies Lobby'), - (403,671,'(01)Allies Lobby'), - (404,671,'(01)Axis Lobby'), - (405,671,'(01)Axis Lobby'), - (409,617,'Rookie'), - (410,617,'Intermediate'), - (411,617,'Expert'), - (412,541,'{01}Axis Lobby'), - (413,541,'{01}Allies Lobby'), - (414,541,'{03}Pacific Theater'), - (415,541,'{02}European Theater'), - (417,541,'{04}Russian Theater'), - (418,541,'Capture the Flag'), - (419,541,'Conquest'), - (420,541,'Co-Op'), - (421,541,'Team Deathmatch'), - (422,541,'{01}African Theater'), - (423,636,'Main Lobby'), - (424,636,'ATI Tournament Lobby'), - (441,564,'TimeSplitters 2'), - (442,564,'Tony Hawk 4'), - (443,15,'{17}Natural Selection'), - (444,716,'GroupRoom1'), - (445,716,'GroupRoom2'), - (446,716,'GroupRoom3'), - (447,564,'Deathrow'), - (448,712,'{01}General Lobby'), - (449,712,'{01}General Lobby'), - (450,712,'Free-For-All Servers'), - (451,712,'Team Deathmatch'), - (452,712,'Round-based Match'), - (453,712,'Objective Match'), - (454,712,'Tug of War'), - (455,641,'{01}General Lobby'), - (456,641,'{01}General Lobby'), - (457,641,'Free-For-All Servers'), - (458,641,'Team Deathmatch'), - (459,641,'Round-based Match'), - (460,641,'Objective Match'), - (461,641,'Tug of War'), - (462,564,'MechAssault'), - (463,564,'Unreal Championship'), - (464,564,'Ghost Recon'), - (471,617,'Rated'), - (472,617,'Unrated'), - (473,617,'Unrated Expert'), - (474,617,'Unrated Intermediate'), - (475,617,'Unrated Beginner'), - (476,730,'Beginner'), - (477,730,'Intermediate'), - (478,730,'Advanced'), - (479,721,'Rated 0'), - (480,721,'Rated 1'), - (481,721,'Rated 2'), - (483,721,'Rated 3'), - (485,557,'Ryan\'s Room'), - (486,557,'Donnie\'s Room'), - (487,541,'Desert Combat'), - (488,713,'{01}Allies Lobby'), - (489,713,'{01}Axis Lobby'), - (490,713,'{01}African Theater'), - (491,713,'{02}European Theater'), - (492,713,'{03}Italian Theater'), - (493,713,'{04}Pacific Theater'), - (494,713,'{05}Russian Theater'), - (496,675,'GroupRoom4'), - (497,675,'GroupRoom5'), - (498,675,'GroupRoom6'), - (499,675,'GroupRoom7'), - (500,675,'GroupRoom8'), - (501,675,'GroupRoom9'), - (502,675,'GroupRoom10'), - (503,675,'GroupRoom11'), - (504,675,'GroupRoom12'), - (506,557,'Kyle\'s Room'), - (507,557,'Bobby\'s Room'), - (508,557,'Nick\'s Room'), - (509,557,'Linda\'s Room'), - (510,557,'Melanie\'s Room'), - (511,557,'Cannonball\'s Room'), - (512,557,'Paulie\'s Room'), - (513,557,'Cuban Joe\'s Room'), - (514,541,'Galactic Conquest'), - (515,770,'Ryan\'s Room'), - (516,770,'Bobby\'s Room'), - (517,722,'Anything Goes'), - (519,722,'Team17'), - (523,722,'Professional League'), - (524,722,'Elite League'), - (525,721,'Rated 4'), - (527,721,'Rated 6'), - (528,721,'Rated 7'), - (529,721,'Rated 8'), - (530,721,'Rated 9'), - (531,721,'Unrated 0'), - (532,721,'Unrated 1'), - (533,721,'Unrated 2'), - (534,721,'Unrated 3'), - (535,721,'Unrated 4'), - (536,721,'Rated 5'), - (537,721,'Unrated 5'), - (538,721,'Unrated 6'), - (539,721,'Unrated 7'), - (540,721,'Unrated 8'), - (541,721,'Unrated 9'), - (542,765,'tier1'), - (543,765,'tier2'), - (544,765,'tier3'), - (545,765,'tier4'), - (546,765,'tier5'), - (547,776,'West'), - (548,776,'East'), - (549,776,'Europe'), - (550,776,'Asia'), - (551,776,'Beginner'), - (552,776,'Expert'), - (553,15,'{18}Vampire Slayer'), - (554,772,'Action Room'), - (555,772,'Asian Room'), - (556,772,'Deathmatch Room'), - (557,797,'{01}Main Lobby'), - (558,797,'{03}Tour Lobby'), - (559,797,'{02}Ladder Lobby'), - (560,564,'Wolfenstein Tides of War'), - (561,564,'Brute Force'), - (562,541,'Eve of Destruction'), - (563,564,'Midnight Club 2'), - (564,564,'Moto GP 2'), - (565,564,'Inside Pitch 2003'), - (566,564,'Star Wars: The Clone Wars'), - (567,564,'Midtown Madness 3'), - (568,541,'ActionBattlefield'), - (569,792,'Conquest Scenarios'), - (570,792,'Battle Scenarios'), - (571,823,'GroupRoom1'), - (572,824,'Unrated 0'), - (573,824,'Unrated 1'), - (575,824,'Unrated 2'), - (576,824,'Unrated 3'), - (577,824,'Unrated 4'), - (578,824,'Unrated 5'), - (579,824,'Unrated 6'), - (580,824,'Unrated 7'), - (581,824,'Unrated 8'), - (582,824,'Unrated 9'), - (586,823,'GroupRoom2'), - (587,823,'GroupRoom3'), - (588,823,'GroupRoom4'), - (589,823,'GroupRoom5'), - (590,823,'GroupRoom6'), - (591,823,'GroupRoom7'), - (592,823,'GroupRoom8'), - (593,823,'GroupRoom9'), - (594,823,'GroupRoom10'), - (595,823,'GroupRoom11'), - (596,823,'GroupRoom12'), - (597,823,'QuickMatch'), - (598,823,'GroupRoom13'), - (599,840,'Advanced'), - (600,840,'Intermediate'), - (601,840,'Beginner'), - (602,823,'GroupRoom14'), - (606,832,'Social Room'), - (607,832,'Beginner Room'), - (608,832,'Intermediate Room'), - (609,832,'Advanced Room'), - (610,851,'West'), - (611,851,'East'), - (612,851,'Europe'), - (613,851,'Asia'), - (614,851,'Beginner'), - (615,851,'Expert'), - (616,22,'{09}Urban Terror Beta 3 Room'), - (619,15,'{19}The Specialists'), - (620,15,'{20}MonkeyStrike'), - (621,15,'{21}Earth Special Forces'), - (622,722,'Amateur League'), - (623,722,'Shopping'), - (624,842,'Beginner'), - (625,842,'Casual'), - (626,842,'Expert'), - (627,842,'Elite'), - (628,832,'Practice Room'), - (631,871,'Newbies'), - (632,871,'Pros'), - (633,871,'Moto 1'), - (634,871,'Moto 2'), - (635,845,'Zaramoth'), - (636,845,'Zaramoth'), - (637,845,'Azunai'), - (638,845,'Azunai'), - (639,845,'Xeria'), - (640,845,'Xeria'), - (641,845,'Isteru'), - (642,772,'Empire Builder Room'), - (643,772,'European Room'), - (644,772,'Free For All Room'), - (645,772,'German Room'), - (646,772,'Ladder Room'), - (647,772,'No Rush Room'), - (648,772,'Tournament Room'), - (649,843,'European Public League'), - (650,843,'Massive Test Leauge'), - (651,843,'North American Public League'), - (652,843,'Asian Public League'), - (660,870,'LobbyRoom1'), - (661,870,'LobbyRoom2'), - (662,870,'QuickMatch'), - (664,891,'Group Room 2'), - (665,564,'Amped 2'), - (666,564,'Crimson Skies'), - (667,564,'NFL Fever 2004'), - (668,564,'Soldier of Fortune II'), - (669,564,'Ghost Recon: Island Thunder'), - (670,564,'Rainbow Six 3'), - (671,564,'Tony Hawk Underground'), - (672,564,'Top Spin'), - (673,791,'Conquest Scenarios'), - (674,791,'Battle Scenarios'), - (675,15,'{23}Counter-Strike: Clan Battle Room'), - (676,15,'{23}Counter-Strike: For Great Justice!'), - (677,15,'{23}Counter-Strike: GSG-9'), - (678,15,'{23}Counter-Strike: Counter-Terrorist Force'), - (679,15,'{23}Counter-Strike: Seal Team 6'), - (680,15,'{23}Counter-Strike: Special Air Service'), - (684,793,'{01}Main Lobby'), - (685,793,'{02}Halo Tournament'), - (696,886,'Main'), - (697,886,'Tournament'), - (698,868,'Main'), - (699,868,'Tournament'), - (715,852,'Search and Destroy'), - (716,852,'Behind Enemy Lines'), - (717,852,'Retrieval'), - (718,852,'Deathmatch'), - (719,924,'Rated 0'), - (720,924,'Rated 1'), - (721,924,'Rated 2'), - (722,924,'Rated 3'), - (723,924,'Rated 4'), - (724,924,'Rated 5'), - (725,924,'Rated 6'), - (726,924,'Rated 7'), - (727,924,'Rated 8'), - (728,924,'Rated 9'), - (729,924,'Unrated 0'), - (730,924,'Unrated 1'), - (731,924,'Unrated 2'), - (732,924,'Unrated 3'), - (733,924,'Unrated 4'), - (734,924,'Unrated 5'), - (735,924,'Unrated 6'), - (736,924,'Unrated 7'), - (737,924,'Unrated 8'), - (738,924,'Unrated 9'), - (739,806,'Headquarters'), - (740,806,'Briefing Room'), - (741,806,'The Bunker'), - (742,806,'Mess Hall'), - (743,946,'Room 1'), - (744,946,'Room 2'), - (745,922,'Competitive'), - (746,922,'Friendly'), - (747,918,'US - Eastern'), - (748,918,'US - Central'), - (749,918,'US - Western'), - (750,918,'Europe - English'), - (751,918,'Europe - French'), - (752,918,'Europe - Italian'), - (753,918,'Europe - German'), - (754,918,'Europe - Spanish'), - (755,976,'Beginners'), - (756,976,'Intermediate'), - (757,908,'Beginners'), - (758,908,'Experts'), - (759,908,'Europe'), - (760,908,'America'), - (761,908,'Asia'), - (766,960,'Casual Play'), - (767,960,'Rated Play'), - (768,960,'Can of Spam'), - (769,1008,'Public Demo League'), - (770,1008,'Public Demo League'), - (771,1008,'Public Demo League'), - (776,1004,'Amateur'), - (786,1004,'Rookie'), - (796,1004,'Pro'), - (812,1004,'Legend'), - (816,946,'Room 3'), - (817,1030,'Beginner'), - (818,1030,'Intermediate'), - (819,1030,'Expert'), - (820,878,'News and Events#1'), - (821,878,'News and Events#2'), - (822,878,'News and Events#3'), - (823,878,'News and Events#4'), - (824,878,'News and Events#5'), - (830,878,'Medal of Honor Chat#1'), - (831,878,'Medal of Honor Chat#2'), - (832,878,'Medal of Honor Chat#3'), - (833,878,'Medal of Honor Chat#4'), - (834,878,'Medal of Honor Chat#5'), - (840,878,'EA Chat#1'), - (841,878,'EA Chat#2'), - (850,878,'Tech Support & Help#1'), - (851,878,'Tech Support & Help#2'), - (860,878,'Clan Arena#1'), - (861,878,'Clan Arena#2'), - (862,878,'Clan Arena#3'), - (863,878,'Clan Arena#4'), - (864,878,'Clan Arena#5'), - (865,878,'Clan Arena#6'), - (866,878,'Clan Arena#7'), - (867,878,'Clan Arena#8'), - (868,878,'Clan Arena#9'), - (869,878,'Clan Arena#10'), - (870,878,'Boot Camp Training#1'), - (871,878,'Boot Camp Training#2'), - (872,878,'Boot Camp Training#3'), - (873,878,'Boot Camp Training#4'), - (874,878,'Boot Camp Training#5'), - (880,878,'Officers Club#1'), - (881,878,'Officers Club#2'), - (882,878,'Officers Club#3'), - (883,878,'Officers Club#4'), - (886,878,'Officers Club#5'), - (890,878,'The War Room#1'), - (891,878,'The War Room#2'), - (892,878,'The War Room#3'), - (893,878,'The War Room#4'), - (894,878,'The War Room#5'), - (900,878,'Off Topic Discussion#1'), - (901,878,'Off Topic Discussion#2'), - (902,878,'Off Topic Discussion#3'), - (903,878,'Off Topic Discussion#4'), - (904,878,'Off Topic Discussion#5'), - (916,1043,'Amateur'), - (925,1043,'Rookie'), - (935,1043,'Pro'), - (951,1043,'Legend'), - (955,1042,'Conquest'), - (956,1042,'King of the Hill'), - (957,1042,'Territory Control'), - (960,843,'Reliance WebWorld Tournament'), - (963,946,'Room 4'), - (964,946,'Room 5'), - (965,946,'Room 6'), - (966,946,'Room 7'), - (967,946,'Room 8'), - (968,946,'Room 9'), - (969,946,'Room 10'), - (970,1064,'Main'), - (971,1064,'Tournament'), - (972,955,'Airliners'), - (973,955,'Adventures'), - (974,955,'Bush Flying'), - (975,955,'Competitions'), - (976,955,'Flight Training'), - (977,955,'Fly-Ins'), - (978,955,'Free Flight'), - (979,955,'Helicopter Ops'), - (983,827,'AoX chat'), - (984,827,'AoX AUS'), - (985,827,'AoX CAN'), - (986,827,'AoX CHN'), - (987,827,'AoX CZE'), - (988,827,'AoX DEU'), - (989,827,'AoX ESP'), - (990,827,'AoX FRA'), - (991,827,'AoX GBR'), - (992,827,'AoX HUN'), - (993,827,'AoX ITA'), - (994,827,'AoX KOR'), - (995,827,'AoX POL'), - (996,827,'AoX RUS'), - (997,827,'AoX USA'), - (998,827,'AoX AFRICA'), - (999,827,'AoX AMERICA'), - (1000,827,'AoX ASIA'), - (1001,827,'AoX EUROPE'), - (1002,870,'LobbyRoom3'), - (1003,870,'LobbyRoom4'), - (1004,870,'LobbyRoom5'), - (1005,870,'LobbyRoom6'), - (1006,870,'LobbyRoom7'), - (1007,870,'LobbyRoom8'), - (1008,870,'LobbyRoom9'), - (1009,870,'ChatRoom1'), - (1010,870,'ChatRoom2'), - (1011,870,'ChatRoom3'), - (1012,870,'ChatRoom4'), - (1013,870,'ChatRoom5'), - (1014,870,'ChatRoom6'), - (1015,870,'ChatRoom7'), - (1016,870,'ChatRoom8'), - (1017,870,'ChatRoom9'), - (1018,870,'ChatRoom10'), - (1019,1070,'Amateur 01'), - (1020,1070,'Amateur 02'), - (1021,1070,'Amateur 03'), - (1022,1070,'Amateur 04'), - (1023,1070,'Amateur 05'), - (1029,1070,'Rookie 01'), - (1030,1070,'Rookie 02'), - (1031,1070,'Rookie 03'), - (1032,1070,'Rookie 04'), - (1033,1070,'Rookie 05'), - (1034,1070,'Pro 01'), - (1035,1070,'Pro 02'), - (1036,1070,'Pro 03'), - (1037,1070,'Pro 04'), - (1038,1070,'Pro 05'), - (1049,1070,'Legend 01'), - (1050,1070,'Legend 02'), - (1051,1070,'Legend 03'), - (1052,1070,'Legend 04'), - (1053,1070,'Legend 05'), - (1057,1071,'Amateur 01'), - (1058,1071,'Amateur 02'), - (1059,1071,'Amateur 03'), - (1060,1071,'Amateur 04'), - (1061,1071,'Amateur 05'), - (1062,1071,'Amateur 06'), - (1063,1071,'Amateur 07'), - (1064,1071,'Amateur 08'), - (1065,1071,'Amateur 09'), - (1066,1071,'Amateur 10'), - (1067,1071,'Rookie 01'), - (1068,1071,'Rookie 02'), - (1069,1071,'Rookie 03'), - (1070,1071,'Rookie 04'), - (1071,1071,'Rookie 05'), - (1072,1071,'Rookie 06'), - (1073,1071,'Rookie 07'), - (1074,1071,'Rookie 08'), - (1075,1071,'Rookie 09'), - (1076,1071,'Rookie 10'), - (1077,1071,'Pro 01'), - (1078,1071,'Pro 02'), - (1079,1071,'Pro 03'), - (1080,1071,'Pro 04'), - (1081,1071,'Pro 05'), - (1082,1071,'Pro 06'), - (1083,1071,'Pro 07'), - (1084,1071,'Pro 08'), - (1085,1071,'Pro 09'), - (1086,1071,'Pro 10'), - (1087,1071,'Pro 11'), - (1088,1071,'Pro 12'), - (1089,1071,'Pro 13'), - (1090,1071,'Pro 14'), - (1091,1071,'Pro 15'), - (1092,1071,'Pro 16'), - (1093,1071,'Legend 01'), - (1094,1071,'Legend 02'), - (1095,1079,'Amateur 01'), - (1096,1079,'Amateur 02'), - (1097,1079,'Amateur 03'), - (1098,1079,'Amateur 04'), - (1099,1079,'Amateur 05'), - (1100,1079,'Amateur 06'), - (1105,1079,'Rookie 01'), - (1106,1079,'Rookie 02'), - (1107,1079,'Rookie 03'), - (1108,1079,'Rookie 04'), - (1109,1079,'Rookie 05'), - (1110,1079,'Rookie 06'), - (1115,1079,'Pro 01'), - (1116,1079,'Pro 02'), - (1117,1079,'Pro 03'), - (1118,1079,'Pro 04'), - (1119,1079,'Pro 05'), - (1120,1079,'Pro 06'), - (1131,1079,'Legend 01'), - (1132,1079,'Legend 02'), - (1133,1079,'Legend 03'), - (1134,1079,'Legend 04'), - (1135,922,'Chat Lobby'), - (1136,1080,'Main'), - (1137,1080,'Tournament'), - (1151,1088,'Welcome'), - (1152,1094,'AoX chat'), - (1153,1094,'AoX AUS'), - (1154,1094,'AoX CAN'), - (1155,1094,'AoX CHN'), - (1156,1094,'AoX CZE'), - (1157,1094,'AoX DEU'), - (1158,1094,'AoX ESP'), - (1159,1094,'AoX FRA'), - (1160,1094,'AoX GBR'), - (1161,1094,'AoX HUN'), - (1162,1094,'AoX ITA'), - (1163,1094,'AoX KOR'), - (1164,1094,'AoX POL'), - (1165,1094,'AoX RUS'), - (1166,1094,'AoX USA'), - (1167,1094,'AoX AFRICA'), - (1168,1094,'AoX AMERICA'), - (1169,1094,'AoX ASIA'), - (1170,1094,'AoX EUROPE'), - (1171,1071,'Legend 03'), - (1172,1071,'Legend 04'), - (1173,976,'Advanced'), - (1174,1092,'Beginner'), - (1175,1092,'Intermediate'), - (1176,1092,'Advanced'), - (1177,845,'Isteru'), - (1178,845,'Gregor'), - (1179,845,'Gregor'), - (1180,845,'Rahvan'), - (1181,845,'Rahvan'), - (1182,845,'Feandan'), - (1183,845,'Feandan'), - (1184,845,'Dalziel'), - (1185,845,'Dalziel'), - (1186,845,'Istaura'), - (1187,845,'Istaura'), - (1188,845,'Agarrus'), - (1189,845,'Agarrus'), - (1190,845,'Lorethal'), - (1191,845,'Lorethal'), - (1192,845,'Vistira'), - (1193,845,'Vistira'), - (1194,845,'Keh'), - (1195,845,'Keh'), - (1196,845,'Soranith'), - (1197,845,'Soranith'), - (1198,845,'Rubicon'), - (1199,845,'Rubicon'), - (1200,845,'Thena'), - (1201,845,'Thena'), - (1202,845,'Artech'), - (1203,845,'Artech'), - (1204,845,'Ethaniel'), - (1205,845,'Ethaniel'), - (1206,845,'Kale'), - (1207,845,'Kale'), - (1208,845,'Calix'), - (1209,845,'Calix'), - (1210,845,'Culahn'), - (1211,845,'Culahn'), - (1212,845,'Rowain'), - (1213,845,'Rowain'), - (1214,845,'Kelis Carthok'), - (1215,845,'Kelis Carthok'), - (1223,1035,'Main'), - (1224,1035,'Asia'), - (1225,1035,'Europe'), - (1226,1035,'US'), - (1227,1035,'Main'), - (1228,1035,'Asia'), - (1229,1035,'Europe'), - (1230,1035,'US'), - (1231,1035,'Main'), - (1232,1035,'Asia'), - (1233,1035,'Europe'), - (1234,1035,'US'), - (1235,1135,'Novices'), - (1236,1135,'Veterans'), - (1237,1135,'English'), - (1238,1135,'French'), - (1239,1135,'German'), - (1240,1135,'Italian'), - (1241,1135,'Spanish'), - (1250,952,'US - Eastern'), - (1251,952,'US - Central'), - (1253,952,'US - Western'), - (1254,952,'Europe - English'), - (1255,952,'Europe - French'), - (1256,952,'Europe - Italian'), - (1257,952,'Europe - German'), - (1258,952,'Europe - Spanish'), - (1259,1042,'Conquest 2'), - (1260,1042,'Conquest 3'), - (1261,1156,'Main'), - (1262,1156,'Main'), - (1263,1156,'Main'), - (1264,1042,'Territory Control 2'), - (1265,1042,'Territory Control 3'), - (1266,1042,'King of the Hill 2'), - (1267,1042,'King of the Hill 3'), - (1269,1042,'Conquest'), - (1270,1042,'King of the Hill'), - (1271,1042,'Territory Control'), - (1272,1042,'Conquest 2'), - (1273,1042,'Conquest 3'), - (1274,1042,'Territory Control 3'), - (1275,1042,'King of the Hill 2'), - (1276,1042,'King of the Hill 3'), - (1277,1042,'Territory Control 2'), - (1278,1159,'West'), - (1279,1159,'East'), - (1282,1173,'General'), - (1283,1173,'OnlineBattle'), - (1284,1181,'General'), - (1285,1181,'OnlineBattle'), - (1287,1191,'Beginner'), - (1288,1191,'Intermediate'), - (1289,1191,'Advanced'), - (1290,948,'North America'), - (1291,948,'Europe'), - (1292,948,'Asia Pacific'), - (1301,948,'Unpatched'), - (1302,1195,'Beginner'), - (1303,1195,'Intermediate'), - (1304,1195,'Advanced'), - (1330,1212,'West'), - (1331,1212,'East'), - (1334,1190,'Generak'), - (1335,1190,'OnlineBattle'), - (1336,1213,'West'), - (1337,1213,'East'), - (1354,1207,'Room 1'), - (1355,1207,'Room 2'), - (1356,1207,'Room 3'), - (1357,1207,'Room 4'), - (1358,1207,'Room 5'), - (1359,1207,'Room 6'), - (1360,1207,'Room 7'), - (1361,1207,'Room 8'), - (1362,1207,'Room 9'), - (1363,1207,'Room 10'), - (1369,1,'TestGroupRoom'), - (1370,1224,'North America'), - (1371,1224,'Europe'), - (1372,1224,'Asia Pacific'), - (1381,1231,'English'), - (1382,1231,'French'), - (1383,1231,'German'), - (1384,1231,'Italian'), - (1385,1231,'Spanish'), - (1386,1231,'Eastern US'), - (1387,1231,'Central US'), - (1388,1231,'Western US'), - (1390,1196,'Exhibition Single 00'), - (1391,1196,'Exhibition Single 01'), - (1392,1196,'Exhibition Single 02'), - (1393,1196,'Exhibition Single 03'), - (1394,1196,'Exhibition Single 04'), - (1395,1196,'Exhibition Single 05'), - (1396,1196,'Exhibition Single 06'), - (1397,1196,'Exhibition Single 07'), - (1398,1196,'Exhibition Single 08'), - (1399,1196,'Exhibition Single 09'), - (1410,1196,'Exhibition Tag 00'), - (1411,1196,'Exhibition Tag 01'), - (1412,1196,'Exhibition Tag 02'), - (1413,1196,'Exhibition Tag 03'), - (1414,1196,'Exhibition Tag 04'), - (1415,1196,'Exhibition Tag 05'), - (1416,1196,'Exhibition Tag 06'), - (1417,1196,'Exhibition Tag 07'), - (1418,1196,'Exhibition Tag 08'), - (1419,1196,'Exhibition Tag 09'), - (1430,1196,'Exhibition Main 00'), - (1431,1196,'Exhibition Main 01'), - (1432,1196,'Exhibition Main 02'), - (1433,1196,'Exhibition Main 03'), - (1434,1196,'Exhibition Main 04'), - (1435,1196,'Exhibition Main 05'), - (1436,1196,'Exhibition Main 06'), - (1437,1196,'Exhibition Main 07'), - (1438,1196,'Exhibition Main 08'), - (1439,1196,'Exhibition Main 09'), - (1450,1196,'Exhibition Voice 00'), - (1451,1196,'Exhibition Voice 01'), - (1452,1196,'Exhibition Voice 02'), - (1453,1196,'Exhibition Voice 03'), - (1454,1196,'Exhibition Voice 04'), - (1455,1196,'Exhibition Voice 05'), - (1456,1196,'Exhibition Voice 06'), - (1457,1196,'Exhibition Voice 07'), - (1458,1196,'Exhibition Voice 08'), - (1459,1196,'Exhibition Voice 09'), - (1470,1196,'TitleMatch Single 00'), - (1471,1196,'TitleMatch Single 01'), - (1472,1196,'TitleMatch Single 02'), - (1473,1196,'TitleMatch Single 03'), - (1474,1196,'TitleMatch Single 04'), - (1475,1196,'TitleMatch Single 05'), - (1476,1196,'TitleMatch Single 06'), - (1477,1196,'TitleMatch Single 07'), - (1478,1196,'TitleMatch Single 08'), - (1479,1196,'TitleMatch Single 09'), - (1490,1196,'TitleMatch Tag 00'), - (1491,1196,'TitleMatch Tag 01'), - (1492,1196,'TitleMatch Tag 02'), - (1493,1196,'TitleMatch Tag 03'), - (1494,1196,'TitleMatch Tag 04'), - (1495,1196,'TitleMatch Tag 05'), - (1496,1196,'TitleMatch Tag 06'), - (1497,1196,'TitleMatch Tag 07'), - (1498,1196,'TitleMatch Tag 08'), - (1499,1196,'TitleMatch Tag 09'), - (1510,1196,'TitleMatch Main 00'), - (1511,1196,'TitleMatch Main 01'), - (1512,1196,'TitleMatch Main 02'), - (1513,1196,'TitleMatch Main 03'), - (1514,1196,'TitleMatch Main 04'), - (1515,1196,'TitleMatch Main 05'), - (1516,1196,'TitleMatch Main 06'), - (1517,1196,'TitleMatch Main 07'), - (1518,1196,'TitleMatch Main 08'), - (1519,1196,'TitleMatch Main 09'), - (1530,1196,'TitleMatch Voice 00'), - (1531,1196,'TitleMatch Voice 01'), - (1532,1196,'TitleMatch Voice 02'), - (1533,1196,'TitleMatch Voice 03'), - (1534,1196,'TitleMatch Voice 04'), - (1535,1196,'TitleMatch Voice 05'), - (1536,1196,'TitleMatch Voice 06'), - (1537,1196,'TitleMatch Voice 07'), - (1538,1196,'TitleMatch Voice 08'), - (1539,1196,'TitleMatch Voice 09'), - (1590,1196,'Trade 00'), - (1591,1196,'Trade 01'), - (1592,1196,'Trade 02'), - (1593,1196,'Trade 03'), - (1594,1196,'Trade 04'), - (1595,1196,'Trade 05'), - (1596,1196,'Trade 06'), - (1597,1196,'Trade 07'), - (1598,1196,'Trade 08'), - (1599,1196,'Trade 09'), - (1610,1228,'Group Room 1'), - (1611,1228,'Group Room 2'), - (1612,1228,'Group Room 3'), - (1613,1122,'Noodlers'), - (1614,1265,'West Coast'), - (1616,1265,'East Coast'), - (1620,1266,'QuickMatch'), - (1621,1266,'ChatRoom2'), - (1622,1266,'ChatRoom1'), - (1623,1266,'LobbyRoom1'), - (1624,1266,'LobbyRoom2'), - (1652,237,'First New One'), - (1653,237,'Second New One'), - (1654,237,'Third New One'), - (1655,237,'Fourth New One'), - (1656,1197,'Exhibition Single 00'), - (1657,1197,'Exhibition Single 01'), - (1658,1197,'Exhibition Single 02'), - (1659,1197,'Exhibition Single 03'), - (1660,1197,'Exhibition Single 04'), - (1661,1197,'Exhibition Single 05'), - (1662,1197,'Exhibition Single 06'), - (1663,1197,'Exhibition Single 07'), - (1664,1197,'Exhibition Single 08'), - (1665,1197,'Exhibition Single 09'), - (1666,1197,'Exhibition Tag 00'), - (1667,1197,'Exhibition Tag 01'), - (1668,1197,'Exhibition Tag 02'), - (1669,1197,'Exhibition Tag 03'), - (1670,1197,'Exhibition Tag 04'), - (1671,1197,'Exhibition Tag 05'), - (1672,1197,'Exhibition Tag 06'), - (1673,1197,'Exhibition Tag 07'), - (1674,1197,'Exhibition Tag 08'), - (1675,1197,'Exhibition Tag 09'), - (1676,1197,'Exhibition Main 00'), - (1677,1197,'Exhibition Main 01'), - (1678,1197,'Exhibition Main 02'), - (1679,1197,'Exhibition Main 03'), - (1680,1197,'Exhibition Main 04'), - (1681,1197,'Exhibition Main 05'), - (1682,1197,'Exhibition Main 06'), - (1683,1197,'Exhibition Main 07'), - (1684,1197,'Exhibition Main 08'), - (1685,1197,'Exhibition Main 09'), - (1686,1197,'Exhibition Voice 00'), - (1687,1197,'Exhibition Voice 01'), - (1688,1197,'Exhibition Voice 02'), - (1689,1197,'Exhibition Voice 03'), - (1690,1197,'Exhibition Voice 04'), - (1691,1197,'Exhibition Voice 05'), - (1692,1197,'Exhibition Voice 06'), - (1693,1197,'Exhibition Voice 07'), - (1694,1197,'Exhibition Voice 08'), - (1695,1197,'Exhibition Voice 09'), - (1696,1197,'TitleMatch Single 00'), - (1697,1197,'TitleMatch Single 01'), - (1698,1197,'TitleMatch Single 02'), - (1699,1197,'TitleMatch Single 03'), - (1700,1197,'TitleMatch Single 04'), - (1701,1197,'TitleMatch Single 05'), - (1702,1197,'TitleMatch Single 06'), - (1703,1197,'TitleMatch Single 07'), - (1704,1197,'TitleMatch Single 08'), - (1705,1197,'TitleMatch Single 09'), - (1706,1197,'TitleMatch Tag 00'), - (1707,1197,'TitleMatch Tag 01'), - (1708,1197,'TitleMatch Tag 02'), - (1709,1197,'TitleMatch Tag 03'), - (1710,1197,'TitleMatch Tag 04'), - (1711,1197,'TitleMatch Tag 05'), - (1712,1197,'TitleMatch Tag 06'), - (1713,1197,'TitleMatch Tag 07'), - (1714,1197,'TitleMatch Tag 08'), - (1715,1197,'TitleMatch Tag 09'), - (1716,1197,'TitleMatch Main 00'), - (1717,1197,'TitleMatch Main 01'), - (1718,1197,'TitleMatch Main 02'), - (1719,1197,'TitleMatch Main 03'), - (1720,1197,'TitleMatch Main 04'), - (1721,1197,'TitleMatch Main 05'), - (1722,1197,'TitleMatch Main 06'), - (1723,1197,'TitleMatch Main 07'), - (1724,1197,'TitleMatch Main 08'), - (1725,1197,'TitleMatch Main 09'), - (1726,1197,'TitleMatch Voice 00'), - (1727,1197,'TitleMatch Voice 01'), - (1728,1197,'TitleMatch Voice 02'), - (1729,1197,'TitleMatch Voice 03'), - (1730,1197,'TitleMatch Voice 04'), - (1731,1197,'TitleMatch Voice 05'), - (1732,1197,'TitleMatch Voice 06'), - (1733,1197,'TitleMatch Voice 07'), - (1734,1197,'TitleMatch Voice 08'), - (1735,1197,'TitleMatch Voice 09'), - (1736,1197,'Trade 00'), - (1737,1197,'Trade 01'), - (1738,1197,'Trade 02'), - (1739,1197,'Trade 03'), - (1740,1197,'Trade 04'), - (1741,1197,'Trade 05'), - (1742,1197,'Trade 06'), - (1743,1197,'Trade 07'), - (1744,1197,'Trade 08'), - (1745,1197,'Trade 09'), - (1746,1198,'Exhibition Single 00'), - (1747,1198,'Exhibition Single 01'), - (1748,1198,'Exhibition Single 02'), - (1749,1198,'Exhibition Single 03'), - (1750,1198,'Exhibition Single 04'), - (1751,1198,'Exhibition Tag 00'), - (1752,1198,'Exhibition Tag 01'), - (1753,1198,'Exhibition Tag 02'), - (1754,1198,'Exhibition Tag 03'), - (1755,1198,'Exhibition Tag 04'), - (1756,1198,'Exhibition Main 00'), - (1757,1198,'Exhibition Main 01'), - (1758,1198,'Exhibition Main 02'), - (1759,1198,'Exhibition Main 03'), - (1760,1198,'Exhibition Main 04'), - (1761,1198,'Exhibition Voice 00'), - (1762,1198,'Exhibition Voice 01'), - (1763,1198,'Exhibition Voice 02'), - (1764,1198,'Exhibition Voice 03'), - (1765,1198,'Exhibition Voice 04'), - (1766,1198,'TitleMatch Single 00'), - (1767,1198,'TitleMatch Single 01'), - (1768,1198,'TitleMatch Single 02'), - (1769,1198,'TitleMatch Single 03'), - (1770,1198,'TitleMatch Single 04'), - (1771,1198,'TitleMatch Tag 00'), - (1772,1198,'TitleMatch Tag 01'), - (1773,1198,'TitleMatch Tag 02'), - (1774,1198,'TitleMatch Tag 03'), - (1775,1198,'TitleMatch Tag 04'), - (1776,1198,'TitleMatch Main 00'), - (1777,1198,'TitleMatch Main 01'), - (1778,1198,'TitleMatch Main 02'), - (1779,1198,'TitleMatch Main 03'), - (1780,1198,'TitleMatch Main 04'), - (1781,1198,'TitleMatch Voice 00'), - (1782,1198,'TitleMatch Voice 01'), - (1783,1198,'TitleMatch Voice 02'), - (1784,1198,'TitleMatch Voice 03'), - (1785,1198,'TitleMatch Voice 04'), - (1786,1198,'Trade 00'), - (1787,1198,'Trade 01'), - (1788,1198,'Trade 02'), - (1789,1198,'Trade 03'), - (1790,1198,'Trade 04'), - (1793,1122,'Jammers'), - (1795,1066,'Rebellion'), - (1796,1066,'Empire'), - (1797,1066,'Versus (1vs1)'), - (1798,1066,'Team Games'), - (1799,1066,'General'), - (1800,1066,'Rebellion'), - (1801,1066,'Empire'), - (1802,1066,'Versus (1vs1)'), - (1803,1066,'Team Games'), - (1804,1066,'General'), - (1805,1066,'Rebellion'), - (1806,1066,'Empire'), - (1807,1066,'Versus (1vs1)'), - (1808,1066,'Team Games'), - (1809,1066,'General'), - (1810,1066,'Rebellion'), - (1811,1066,'Empire'), - (1812,1066,'Versus (1vs1)'), - (1813,1066,'Team Games'), - (1814,1066,'General'), - (1815,1066,'Rebellion'), - (1816,1066,'Empire'), - (1817,1066,'Versus (1vs1)'), - (1818,1066,'Team Games'), - (1819,1066,'General'), - (1820,1066,'Rebellion'), - (1821,1066,'Empire'), - (1822,1066,'Versus (1vs1)'), - (1823,1066,'Team Games'), - (1824,1066,'General'), - (1825,1066,'Rebellion'), - (1826,1066,'Empire'), - (1827,1066,'Versus (1vs1)'), - (1828,1066,'Team Games'), - (1829,1066,'General'), - (1830,1066,'Rebellion'), - (1831,1066,'Empire'), - (1837,1309,'West'), - (1838,1309,'East'), - (1839,1321,'QuickMatch'), - (1840,1321,'ChatRoom1'), - (1842,1321,'LobbyRoom1'), - (1843,1321,'LobbyRoom2'), - (1844,1321,'LobbyRoom3'), - (1845,1321,'LobbyRoom4'), - (1846,1321,'LobbyRoom5'), - (1847,1321,'LobbyRoom6'), - (1848,1321,'LobbyRoom7'), - (1849,1321,'LobbyRoom8'), - (1850,1283,'Western US'), - (1851,1283,'Central US'), - (1852,1283,'Eastern US'), - (1853,1283,'German'), - (1854,1283,'Spanish'), - (1855,1283,'English'), - (1856,1283,'Italian'), - (1857,1283,'French'), - (1858,1321,'LobbyRoom9'), - (1859,1122,'Chop Masters'), - (1860,1160,'USA'), - (1861,1160,'Europe'), - (1862,1190,'Debug'), - (1863,1354,'Larry'), - (1864,1354,'Curly'), - (1865,1354,'Moe'), - (1866,1376,'West'), - (1867,1376,'East'), - (1868,1391,'Action'), - (1869,1391,'Story'), - (1870,1391,'Story Lite'), - (1871,1391,'Role Play'), - (1872,1391,'Team'), - (1873,1391,'Melee'), - (1874,1391,'Arena'), - (1875,1391,'Social'), - (1876,1391,'Alternative'), - (1877,1391,'PW Story'), - (1878,1391,'PW Action'), - (1879,1391,'Solo'), - (1880,1391,'Tech Support'), - (1881,1122,'eJamming Test'), - (1882,1401,'Novices'), - (1883,1401,'Veterans'), - (1884,1401,'English'), - (1885,1401,'French'), - (1886,1401,'German'), - (1887,1401,'Italian'), - (1888,1401,'Spanish'), - (1889,1396,'Africa'), - (1890,1396,'America'), - (1891,1396,'Asia'), - (1892,1396,'Europe'), - (1893,1396,'Pacific'), - (1894,1396,'The Empire'), - (1895,1396,'Hordes of Chaos'), - (1896,1396,'High Elves'), - (1897,1396,'Skaven'), - (1898,1396,'Other'), - (1899,1396,'Kicked'), - (1900,1396,'Banned'), - (1901,1422,'QuickMatch'), - (1902,1422,'ChatRoom1'), - (1904,1426,'Beginner'), - (1905,1426,'Intermediate'), - (1906,1426,'Advanced'), - (1907,1411,'Room 1'), - (1908,1411,'Room 2'), - (1909,1411,'Room 3'), - (1910,1411,'Room 4'), - (1911,1411,'Room 5'), - (1912,1411,'Room 6'), - (1913,1411,'Room 7'), - (1914,1411,'Room 8'), - (1915,1411,'Room 9'), - (1916,1411,'Room 10'), - (1917,1463,'QuickMatch'), - (1918,1463,'ChatRoom1'), - (1919,1463,'LobbyRoom1'), - (1920,1463,'LobbyRoom2'), - (1921,1463,'LobbyRoom3'), - (1922,1463,'LobbyRoom4'), - (1923,1463,'LobbyRoom5'), - (1924,1463,'LobbyRoom6'), - (1925,1463,'LobbyRoom7'), - (1926,1463,'LobbyRoom8'), - (1927,1463,'LobbyRoom9'), - (1928,1398,'Africa'), - (1929,1398,'America'), - (1930,1398,'Asia'), - (1931,1398,'Europe'), - (1932,1398,'Pacific'), - (1933,1398,'The Empire'), - (1934,1398,'Hordes of Chaos'), - (1935,1398,'High Elves'), - (1936,1398,'Skaven'), - (1937,1398,'Other'), - (1938,1398,'Kicked'), - (1939,1398,'Banned'), - (1940,1479,'West'), - (1941,1479,'East'), - (1942,1481,'East'), - (1943,1481,'West'), - (1944,1463,'QuickMatch2'), - (1945,1483,'America'), - (1946,1483,'Asia'), - (1947,1483,'Europe'), - (1948,1321,'QuickMatch2'), - (1956,1514,'Newbies'), - (1957,1514,'Experienced Players'), - (1958,1514,'Strategists'), - (1959,1515,'Newbies'), - (1960,1515,'Experienced Players'), - (1961,1515,'Strategists'), - (1962,1523,'Skirmish'), - (1963,1523,'World Domination'), - (1964,1524,'Skirmish'), - (1965,1524,'World Domination'), - (1966,1577,'Rebellion'), - (1967,1577,'Empire'), - (1968,1577,'Versus (1vs1)'), - (1969,1577,'Team Games'), - (1970,1577,'General'), - (1971,1577,'Rebellion'), - (1972,1577,'Empire'), - (1973,1577,'Versus (1vs1)'), - (1974,1577,'Team Games'), - (1975,1577,'General'), - (1976,1577,'Rebellion'), - (1977,1577,'Empire'), - (1978,1577,'Versus (1vs1)'), - (1979,1577,'Team Games'), - (1980,1577,'General'), - (1981,1577,'Rebellion'), - (1982,1577,'Empire'), - (1983,1577,'Versus (1vs1)'), - (1984,1577,'Team Games'), - (1985,1577,'General'), - (1986,1577,'Rebellion'), - (1987,1577,'Empire'), - (1988,1577,'Versus (1vs1)'), - (1989,1577,'Team Games'), - (1990,1577,'General'), - (1991,1577,'Rebellion'), - (1992,1577,'Empire'), - (1993,1577,'Versus (1vs1)'), - (1994,1577,'Team Games'), - (1995,1577,'General'), - (1996,1577,'Rebellion'), - (1997,1577,'Empire'), - (1998,1577,'Versus (1vs1)'), - (1999,1577,'Team Games'), - (2000,1577,'General'), - (2001,1577,'Rebellion'), - (2002,1577,'Empire'), - (2003,1631,'Skirmish'), - (2004,1631,'World Domination'), - (2027,1714,'ChatRoom1'), - (2028,1714,'QuickMatch'), - (2029,1714,'LobbyRoom:1'), - (2030,1714,'LobbyRoom:2'), - (2031,1714,'LobbyRoom:3'), - (2032,1714,'LobbyRoom:4'), - (2033,1714,'LobbyRoom:5'), - (2034,1714,'LobbyRoom:6'), - (2035,1714,'LobbyRoom:7'), - (2036,1714,'LobbyRoom:8'), - (2037,1714,'LobbyRoom:9'), - (2038,1714,'LobbyRoom:10'), - (2039,1714,'LobbyRoom:11'), - (2040,1714,'LobbyRoom:12'), - (2041,1714,'LobbyRoom:13'), - (2042,1714,'LobbyRoom:14'), - (2043,1714,'LobbyRoom:15'), - (2044,1714,'LobbyRoom:16'), - (2045,1714,'LobbyBeginners:1'), - (2046,1714,'LobbyHardcore:1'), - (2047,1714,'LobbyClan:1'), - (2048,1714,'LobbyClan:2'), - (2049,1714,'LobbyTournaments:1'), - (2050,1714,'LobbyTournaments:2'), - (2051,1714,'LobbyBattlecast:1'), - (2052,1714,'LobbyCustomMap:1'), - (2053,1714,'LobbyCustomMap:2'), - (2054,1714,'LobbyCompStomp:1'), - (2055,1714,'LobbyGerman:1'), - (2056,1714,'LobbyGerman:2'), - (2057,1714,'LobbyKorean:1'), - (2058,1714,'LobbyFrench:1'), - (2059,1422,'LobbyRoom:1'), - (2060,1422,'LobbyRoom:2'), - (2061,1422,'LobbyRoom:3'), - (2062,1422,'LobbyRoom:4'), - (2063,1422,'LobbyRoom:5'), - (2064,1422,'LobbyRoom:6'), - (2065,1422,'LobbyRoom:7'), - (2066,1422,'LobbyRoom:8'), - (2067,1422,'LobbyRoom:9'), - (2068,1422,'LobbyRoom:10'), - (2069,1422,'LobbyRoom:11'), - (2070,1422,'LobbyRoom:12'), - (2071,1422,'LobbyRoom:13'), - (2072,1422,'LobbyRoom:14'), - (2073,1422,'LobbyRoom:15'), - (2074,1422,'LobbyRoom:16'), - (2075,1422,'LobbyBeginners:1'), - (2076,1422,'LobbyHardcore:1'), - (2077,1422,'LobbyClan:1'), - (2078,1422,'LobbyClan:2'), - (2079,1422,'LobbyTournaments:1'), - (2080,1422,'LobbyTournaments:2'), - (2081,1422,'LobbyBattlecast:1'), - (2082,1422,'LobbyCustomMap:1'), - (2083,1422,'LobbyCustomMap:2'), - (2084,1422,'LobbyCompStomp:1'), - (2085,1422,'LobbyGerman:1'), - (2086,1422,'LobbyGerman:2'), - (2087,1422,'LobbyKorean:1'), - (2088,1422,'LobbyFrench:1'), - (2089,1774,'Room 1'), - (2090,1774,'Room 2'), - (2091,1774,'Room 3'), - (2092,1774,'Room 4'), - (2093,1774,'Room 5'), - (2094,1774,'Room 6'), - (2095,1774,'Room 7'), - (2096,1774,'Room 8'), - (2097,1774,'Room 9'), - (2098,1774,'Room 10'), - (2099,1814,'ChatRoom1'), - (2100,1814,'QuickMatch'), - (2101,1814,'LobbyRoom:1'), - (2102,1814,'LobbyRoom:2'), - (2103,1814,'LobbyRoom:3'), - (2104,1814,'LobbyRoom:4'), - (2105,1814,'LobbyRoom:5'), - (2106,1814,'LobbyRoom:6'), - (2107,1814,'LobbyRoom:7'), - (2108,1814,'LobbyRoom:8'), - (2109,1814,'LobbyRoom:9'), - (2110,1814,'LobbyRoom:10'), - (2111,1814,'LobbyRoom:11'), - (2112,1814,'LobbyRoom:12'), - (2113,1814,'LobbyRoom:13'), - (2114,1814,'LobbyRoom:14'), - (2115,1814,'LobbyRoom:15'), - (2116,1814,'LobbyRoom:16'), - (2117,1814,'LobbyBeginners:1'), - (2118,1814,'LobbyHardcore:1'), - (2119,1814,'LobbyClan:1'), - (2120,1814,'LobbyClan:2'), - (2121,1814,'LobbyTournaments:1'), - (2122,1814,'LobbyTournaments:2'), - (2123,1814,'LobbyBattlecast:1'), - (2124,1814,'LobbyCustomMap:1'), - (2125,1814,'LobbyCustomMap:2'), - (2126,1814,'LobbyCompStomp:1'), - (2127,1814,'LobbyGerman:1'), - (2128,1814,'LobbyGerman:2'), - (2129,1814,'LobbyKorean:1'), - (2130,1814,'LobbyFrench:1'), - (2131,1884,'Room 1'), - (2132,1884,'Room 2'), - (2133,1884,'Room 3'), - (2134,1884,'Room 4'), - (2135,1884,'Room 5'), - (2136,1884,'Room 6'), - (2137,1884,'Room 7'), - (2138,1884,'Room 8'), - (2139,1884,'Room 9'), - (2140,1884,'Room 10'), - (2141,1923,'Africa'), - (2142,1923,'America'), - (2143,1923,'Asia'), - (2144,1923,'Europe'), - (2145,1923,'Pacific'), - (2146,1923,'The Empire'), - (2147,1923,'Hordes of Chaos'), - (2148,1923,'High Elves'), - (2149,1923,'Skaven'), - (2150,1923,'Other'), - (2151,1923,'Dark Elf'), - (2152,1923,'Orc'), - (2153,1923,'Kicked'), - (2154,1923,'Banned'), - (2155,1514,'Sparta FX'), - (2156,1979,'QuickMatch'), - (2157,1979,'LobbyRoom:1'), - (2158,1979,'ChatRoom1'), - (2159,2100,'QuickMatch'), - (2160,2100,'LobbyRoom:1'), - (2161,2100,'ChatRoom1'), - (2162,2094,'QuickMatch'), - (2163,2094,'LobbyRoom:1'), - (2164,2094,'ChatRoom1'), - (2166,2128,'LobbyRoom:1'), - (2167,2128,'LobbyRoom:2'), - (2168,2128,'LobbyRoom:3'), - (2169,2128,'LobbyRoom:4'), - (2170,2128,'LobbyRoom:5'), - (2171,2128,'LobbyKorean:1'), - (2172,2128,'LobbyFrench:1'), - (2173,2128,'LobbyGerman:2'), - (2174,2128,'LobbyGerman:1'), - (2175,2128,'LobbyBattlecast:1'), - (2176,2128,'LobbyRoom:6'), - (2177,2128,'ChatRoom1'), - (2178,2128,'LobbyRoom:8'), - (2179,2128,'LobbyRoom:11'), - (2180,2128,'LobbyRoom:7'), - (2181,2128,'LobbyRoom:9'), - (2182,2128,'LobbyRoom:12'), - (2183,2128,'LobbyRoom:10'), - (2184,2128,'LobbyClan:1'), - (2185,2128,'LobbyRoom:13'), - (2186,2128,'LobbyClan:2'), - (2187,2128,'LobbyRoom:14'), - (2188,2128,'LobbyRoom:16'), - (2189,2128,'LobbyRoom:15'), - (2190,2128,'LobbyBeginners:1'), - (2191,2128,'LobbyTournaments:1'), - (2192,2128,'LobbyCompStomp:1'), - (2193,2128,'LobbyHardcore:1'), - (2194,2128,'LobbyCustomMap:1'), - (2195,2128,'LobbyCustomMap:2'), - (2196,2128,'LobbyTournaments:2'), - (2198,2128,'ChatRoom1'), - (2200,2130,'LobbyRoom:1'), - (2201,2130,'LobbyRoom:2'), - (2202,2130,'LobbyRoom:3'), - (2203,2130,'LobbyRoom:4'), - (2204,2130,'LobbyRoom:5'), - (2205,2130,'LobbyKorean:1'), - (2206,2130,'LobbyFrench:1'), - (2207,2130,'LobbyGerman:2'), - (2208,2130,'LobbyGerman:1'), - (2209,2130,'LobbyBattlecast:1'), - (2210,2130,'LobbyRoom:6'), - (2211,2130,'ChatRoom1'), - (2212,2130,'LobbyRoom:8'), - (2213,2130,'LobbyRoom:11'), - (2214,2130,'LobbyRoom:7'), - (2215,2130,'LobbyRoom:9'), - (2216,2130,'LobbyRoom:12'), - (2217,2130,'LobbyRoom:10'), - (2218,2130,'LobbyClan:1'), - (2219,2130,'LobbyRoom:13'), - (2220,2130,'LobbyClan:2'), - (2221,2130,'LobbyRoom:14'), - (2222,2130,'LobbyRoom:16'), - (2223,2130,'LobbyRoom:15'), - (2224,2130,'LobbyBeginners:1'), - (2225,2130,'LobbyTournaments:1'), - (2226,2130,'LobbyCompStomp:1'), - (2227,2130,'LobbyHardcore:1'), - (2228,2130,'LobbyCustomMap:1'), - (2229,2130,'LobbyCustomMap:2'), - (2230,2130,'LobbyTournaments:2'), - (2232,2130,'ChatRoom1'), - (2233,2160,'QuickMatch'), - (2234,2160,'LobbyTournaments:2'), - (2235,2160,'LobbyTournaments:1'), - (2236,2160,'LobbyRoom:9'), - (2237,2160,'LobbyRoom:8'), - (2238,2160,'LobbyRoom:7'), - (2239,2160,'LobbyRoom:6'), - (2240,2160,'LobbyRoom:5'), - (2241,2160,'LobbyRoom:4'), - (2242,2160,'LobbyRoom:3'), - (2243,2160,'LobbyRoom:2'), - (2244,2160,'LobbyRoom:16'), - (2245,2160,'LobbyRoom:15'), - (2246,2160,'LobbyRoom:14'), - (2247,2160,'LobbyRoom:13'), - (2248,2160,'LobbyRoom:12'), - (2249,2160,'LobbyRoom:11'), - (2250,2160,'LobbyRoom:10'), - (2251,2160,'LobbyRoom:1'), - (2252,2160,'LobbyKorean:1'), - (2253,2160,'LobbyHardcore:1'), - (2254,2160,'LobbyGerman:2'), - (2255,2160,'LobbyGerman:1'), - (2256,2160,'LobbyFrench:1'), - (2257,2160,'LobbyCustomMap:2'), - (2258,2160,'LobbyCustomMap:1'), - (2259,2160,'LobbyCompStomp:1'), - (2260,2160,'LobbyClan:2'), - (2261,2160,'LobbyClan:1'), - (2262,2160,'LobbyBeginners:1'), - (2263,2160,'LobbyBattlecast:1'), - (2264,2160,'ChatRoom1'), - (2265,1979,'Russia:1'), - (2266,2238,'LobbyRoom:1'), - (2267,2238,'LobbyRoom:2'), - (2268,2238,'LobbyRoom:3'), - (2269,2238,'LobbyRoom:4'), - (2270,2238,'LobbyRoom:5'), - (2271,2238,'LobbyRoom:6'), - (2272,2238,'LobbyRoom:7'), - (2273,2238,'LobbyRoom:8'), - (2274,2238,'LobbyRoom:9'), - (2275,2238,'LobbyRoom:10'), - (2276,2238,'LobbyRoom:11'), - (2277,2238,'LobbyRoom:12'), - (2278,2238,'LobbyRoom:13'), - (2279,2238,'LobbyRoom:14'), - (2280,2238,'LobbyRoom:15'), - (2281,2238,'LobbyRoom:16'), - (2282,2238,'LobbyBeginners:1'), - (2283,2238,'LobbyHardcore:1'), - (2284,2238,'LobbyClan:1'), - (2285,2238,'LobbyClan:2'), - (2286,2238,'LobbyTournaments:1'), - (2287,2238,'LobbyTournaments:2'), - (2288,2238,'LobbyBattlecast:1'), - (2289,2238,'LobbyCustomMap:1'), - (2290,2238,'LobbyCustomMap:2'), - (2291,2238,'LobbyCompStomp:1'), - (2292,2238,'LobbyGerman:1'), - (2293,2238,'LobbyGerman:2'), - (2294,2238,'LobbyKorean:1'), - (2295,2238,'LobbyFrench:1'), - (2296,2238,'ChatRoom1'), - (2297,1814,'Russia:1'), - (2298,2298,'Deathmatch'), - (2299,2298,'RopeRace'), - (2300,2298,'Forts'), - (2301,2298,'Triathlon'), - (2302,2313,'Beginner'), - (2303,2313,'Intermediate'), - (2304,2313,'Expert'), - (2305,2374,'ChatRoom1'), - (2306,2374,'LobbyRoom:1'), - (2307,2374,'LobbyRoom:2'), - (2308,2374,'LobbyRoom:3'), - (2309,2374,'LobbyRoom:4'), - (2310,2374,'LobbyRoom:5'), - (2311,2374,'LobbyKorean:1'), - (2312,2374,'LobbyFrench:1'), - (2313,2374,'LobbyGerman:2'), - (2314,2374,'LobbyGerman:1'), - (2315,2374,'LobbyBattlecast:1'), - (2316,2374,'LobbyRoom:6'), - (2317,2374,'ChatRoom1'), - (2318,2374,'LobbyRoom:8'), - (2319,2374,'LobbyRoom:11'), - (2320,2374,'LobbyRoom:7'), - (2321,2374,'LobbyRoom:9'), - (2322,2374,'LobbyRoom:12'), - (2323,2374,'LobbyRoom:10'), - (2324,2374,'LobbyClan:1'), - (2325,2374,'LobbyRoom:13'), - (2326,2374,'LobbyClan:2'), - (2327,2374,'LobbyRoom:14'), - (2328,2374,'LobbyRoom:16'), - (2329,2374,'LobbyRoom:15'), - (2330,2374,'LobbyBeginners:1'), - (2331,2374,'LobbyTournaments:1'), - (2332,2374,'LobbyCompStomp:1'), - (2333,2374,'LobbyHardcore:1'), - (2334,2374,'LobbyCustomMap:1'), - (2335,2374,'LobbyCustomMap:2'), - (2336,2374,'LobbyTournaments:2'), - (2337,2130,'LobbyRussian:1'), - (2338,2130,'LobbyTaiwan:1'), - (2339,2128,'LobbyRoom:17'), - (2340,2128,'LobbyRoom:18'), - (2341,2128,'LobbyRoom:19'), - (2342,2128,'LobbyRoom:20'), - (2343,2128,'LobbyRoom:21'), - (2344,2128,'LobbyCoop:1'), - (2345,2128,'LobbyCoop:2'), - (2346,2128,'LobbyCoop:3'), - (2347,2128,'LobbyCoop:4'), - (2348,2128,'LobbyCoop:5'), - (2349,2128,'LobbyRussian:1'), - (2350,2128,'LobbyTaiwan:1'), - (2351,2298,'WarRoom'), - (2352,2298,'TacticsMode'), - (2353,2259,'room1'), - (2354,2259,'room2'), - (2355,2259,'room3'), - (2356,2259,'room4'), - (2357,2128,'LobbySpanish:1'), - (2358,2714,'room1'), - (2359,2714,'room2'), - (2360,2714,'room3'), - (2361,2714,'room4'), - (2362,2246,'ChatMonTesting'), - (2363,2712,'room1'), - (2364,2712,'room2'), - (2365,2712,'room3'), - (2366,2712,'room4'), - (2367,2705,'North America'), - (2368,2705,'Europe'), - (2369,2705,'Brazil'), - (2370,2840,'FX Sparta II'), - (2371,2706,'North America'), - (2372,2706,'Europe'), - (2373,2706,'Brazil'), - (2374,2707,'North America'), - (2375,2707,'Europe'), - (2376,2707,'Brazil'), - (2377,2032,'North America'), - (2378,2032,'Europe'), - (2379,2032,'Brazil'), - (2380,2034,'North America'), - (2381,2034,'Europe'), - (2382,2034,'Brazil'), - (2383,2035,'North America'), - (2384,2035,'Europe'), - (2385,2035,'Brazil'), - (2386,706,'Main Lobby'), - (2387,1003,'ThMods.com'), - (2388,917,'Main Lobby'), - (2389,1128,'Main Lobby'), - (2390,1307,'ThMods.com'), - (2391,1005,'ThMods.com'), - (2392,467,'Main Lobby'), - (2393,600,'Main Lobby'), - (2399,1003,'Lobby 2'); -UNLOCK TABLES; - - -LOCK TABLES `messages` WRITE; -UNLOCK TABLES; - - -LOCK TABLES `partner` WRITE; -INSERT INTO `partner` (`partnerid`, `partnername`) VALUES - (0,'RetroSpy'), - (95,'Crytek'); -UNLOCK TABLES; - - -LOCK TABLES `profiles` WRITE; -INSERT INTO `profiles` (`profileid`, `userid`, `nick`, `serverflag`, `status`, `statstring`, `location`, `firstname`, `lastname`, `publicmask`, `latitude`, `longitude`, `aim`, `picture`, `occupationid`, `incomeid`, `industryid`, `marriedid`, `childcount`, `interests1`, `ownership1`, `connectiontype`, `sex`, `zipcode`, `countrycode`, `homepage`, `birthday`, `birthmonth`, `birthyear`, `icquin`, `quietflags`, `streetaddr`, `streeaddr`, `city`, `cpubrandid`, `cpuspeed`, `memory`, `videocard1string`, `videocard1ram`, `videocard2string`, `videocard2ram`, `subscription`, `adminrights`) VALUES - (1,1,'spyguy',0,0,'I love RetroSpy','earth','spy','guy',0,0,0,'0',0,0,0,0,0,0,0,0,0,2,'00000','1','rspy.org',0,0,0,0,0,'','','',0,0,0,'',0,'',0,0,0), - (14,10,'gptest1',0,0,'I love RetroSpy','','','',0,0,0,'0',0,0,0,0,0,0,0,0,0,2,'00000','1','rspy.org',0,0,0,0,0,NULL,NULL,NULL,0,0,0,NULL,0,NULL,0,0,0), - (15,11,'gptest2',0,0,'I love RetroSpy','','','',0,0,0,'0',0,0,0,0,0,0,0,0,0,2,'00000','1','rspy.org',0,0,0,0,0,NULL,NULL,NULL,0,0,0,NULL,0,NULL,0,0,0), - (16,12,'gptest3',0,0,'I love RetroSpy','','','',0,0,0,'0',0,0,0,0,0,0,0,0,0,2,'00000','1','rspy.org',0,0,0,0,0,NULL,NULL,NULL,0,0,0,NULL,0,NULL,0,0,0), - (19,15,'mrpants',0,0,'I love RetroSpy','','','',0,0,0,'0',0,0,0,0,0,0,0,0,0,2,'00000','1','rspy.org',0,0,0,0,0,NULL,NULL,NULL,0,0,0,NULL,0,NULL,0,0,0); -UNLOCK TABLES; - - -LOCK TABLES `pstorage` WRITE; -INSERT INTO `pstorage` (`pstorageid`, `profileid`, `ptype`, `dindex`, `data`) VALUES - (1,1,1,0,''), - (2,1,3,0,''); -UNLOCK TABLES; - - -LOCK TABLES `sakestorage` WRITE; -UNLOCK TABLES; - - -LOCK TABLES `subprofiles` WRITE; -INSERT INTO `subprofiles` (`subprofileid`, `profileid`, `uniquenick`, `namespaceid`, `partnerid`, `productid`, `gamename`, `cdkeyenc`, `firewall`, `port`, `authtoken`) VALUES - (1,1,'spyguy',1,0,0,'gmtest',NULL,0,0,'example_token'), - (13,14,'',1,0,0,'gmtest',NULL,0,0,NULL), - (14,15,'',1,0,0,'gmtest',NULL,0,0,NULL), - (15,16,'',1,0,0,'gmtest',NULL,0,0,NULL), - (18,19,'',1,0,0,'gmtest',NULL,0,0,NULL); -UNLOCK TABLES; - - -LOCK TABLES `users` WRITE; -INSERT INTO `users` (`userid`, `email`, `password`, `emailverified`, `lastip`, `lastonline`, `createddate`, `banned`, `deleted`) VALUES - (1,'spyguy@gamespy.com','4a7d1ed414474e4033ac29ccb8653d9b',1,'127.0.0.1','2020-05-15 13:17:00','2019-08-11 01:07:27',0,0), - (10,'gptestc1@gptestc.com','c6d525669e64438c9aa156a0cc80cd14',1,NULL,'2021-02-10 12:04:13','2021-02-10 12:04:13',0,0), - (11,'gptestc2@gptestc.com','c6d525669e64438c9aa156a0cc80cd14',1,NULL,'2021-02-10 12:04:33','2021-02-10 12:04:33',0,0), - (12,'gptestc3@gptestc.com','c6d525669e64438c9aa156a0cc80cd14',1,NULL,'2021-02-10 12:04:40','2021-02-10 12:04:40',0,0), - (15,'dan@gamespy.com','6e7d5392358354f24f39064380cb7061',1,NULL,'2021-04-10 13:35:46','2021-04-10 13:35:46',0,0); -UNLOCK TABLES; - - - - - - -SET FOREIGN_KEY_CHECKS = @ORIG_FOREIGN_KEY_CHECKS; - -SET UNIQUE_CHECKS = @ORIG_UNIQUE_CHECKS; - -SET @ORIG_TIME_ZONE = @@TIME_ZONE; -SET TIME_ZONE = @ORIG_TIME_ZONE; - -SET SQL_MODE = @ORIG_SQL_MODE; - - - -# Export Finished: April 10, 2021 at 21:38:07 GMT+8 - diff --git a/common/UniSpyServerConfig.json b/common/UniSpyServerConfig.json deleted file mode 100644 index 75e04d2fc..000000000 --- a/common/UniSpyServerConfig.json +++ /dev/null @@ -1,91 +0,0 @@ -{ - "Database": { - "Server": "127.0.0.1", - "Port": "5432", - "Database": "unispy", - "Username": "root", - "Password": "0000", - "SSLMode": "Prefer", - "TrustServerCert": "false", - "SSLKey": "", - "SSLPassword": "", - "RootCert": "" - }, - "Redis": { - "Server": "127.0.0.1", - "Port": "6379", - "User": "", - "Password": "", - "SSL": "false", - "SSLHost": "" - }, - "Servers": [ - { - "ServerID": "00000000-0000-0000-0000-000000000000", - "ServerName": "PresenceConnectionManager", - "PublicAddress": "0.0.0.0", - "ListeningPort": "29900" - }, - { - "ServerID": "00000000-0000-0000-0000-000000000000", - "ServerName": "PresenceSearchPlayer", - "PublicAddress": "0.0.0.0", - "ListeningPort": "29901" - }, - { - "ServerID": "00000000-0000-0000-0000-000000000000", - "ServerName": "CDKey", - "PublicAddress": "0.0.0.0", - "ListeningPort": "29910" - }, - { - "ServerID": "00000000-0000-0000-0000-000000000000", - "ServerName": "ServerBrowserV1", - "PublicAddress": "0.0.0.0", - "ListeningPort": "28900" - }, - { - "ServerID": "00000000-0000-0000-0000-000000000000", - "ServerName": "ServerBrowserV2", - "PublicAddress": "0.0.0.0", - "ListeningPort": "28910" - }, - { - "ServerID": "00000000-0000-0000-0000-000000000000", - "ServerName": "QueryReport", - "PublicAddress": "0.0.0.0", - "ListeningPort": "27900" - }, - { - "ServerID": "00000000-0000-0000-0000-000000000000", - "ServerName": "NatNegotiation", - "PublicAddress": "0.0.0.0", - "ListeningPort": "27901" - }, - { - "ServerID": "00000000-0000-0000-0000-000000000000", - "ServerName": "GameStatus", - "PublicAddress": "0.0.0.0", - "ListeningPort": "29920" - }, - { - "ServerID": "00000000-0000-0000-0000-000000000000", - "ServerName": "Chat", - "PublicAddress": "0.0.0.0", - "ListeningPort": "6667" - }, - { - "ServerID": "00000000-0000-0000-0000-000000000000", - "ServerName": "WebServer", - "PublicAddress": "0.0.0.0", - "ListeningPort": "80" - }, - { - "ServerID": "00000000-0000-0000-0000-000000000000", - "ServerName": "GameTrafficRelay", - "PublicAddress": "0.0.0.0", - "ListeningPort": "10086" - } - ], - "MinimumLogLevel": "Verbose" -} \ No newline at end of file diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql deleted file mode 100644 index 3a8add655..000000000 --- a/common/UniSpy_pg.sql +++ /dev/null @@ -1,5521 +0,0 @@ --- --- PostgreSQL database dump --- - --- Dumped from database version 14.2 (Debian 14.2-1.pgdg110+1) --- Dumped by pg_dump version 14.1 - --- Started on 2022-03-02 20:15:24 CET - -SET statement_timeout = 0; -SET lock_timeout = 0; -SET idle_in_transaction_session_timeout = 0; -SET client_encoding = 'UTF8'; -SET standard_conforming_strings = on; -SELECT pg_catalog.set_config('search_path', '', false); -SET check_function_bodies = false; -SET xmloption = content; -SET client_min_messages = warning; -SET row_security = off; - --- --- TOC entry 5 (class 2615 OID 16385) --- Name: unispy; Type: SCHEMA; Schema: -; Owner: - --- - -CREATE SCHEMA unispy; - - --- --- TOC entry 3466 (class 0 OID 0) --- Dependencies: 5 --- Name: SCHEMA unispy; Type: COMMENT; Schema: -; Owner: - --- - -COMMENT ON SCHEMA unispy IS 'standard public schema'; - - -SET default_tablespace = ''; - -SET default_table_access_method = heap; - --- --- TOC entry 209 (class 1259 OID 16386) --- Name: addrequests; Type: TABLE; Schema: unispy; Owner: - --- - -CREATE TABLE unispy.addrequests ( - addrequestid integer NOT NULL, - profileid integer NOT NULL, - namespaceid integer NOT NULL, - targetid integer NOT NULL, - reason character varying NOT NULL, - syncrequested character varying NOT NULL -); - - --- --- TOC entry 3467 (class 0 OID 0) --- Dependencies: 209 --- Name: TABLE addrequests; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.addrequests IS 'Friend request.'; - - --- --- TOC entry 210 (class 1259 OID 16391) --- Name: addrequests_addrequestid_seq; Type: SEQUENCE; Schema: unispy; Owner: - --- - -CREATE SEQUENCE unispy.addrequests_addrequestid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- TOC entry 3468 (class 0 OID 0) --- Dependencies: 210 --- Name: addrequests_addrequestid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - --- - -ALTER SEQUENCE unispy.addrequests_addrequestid_seq OWNED BY unispy.addrequests.addrequestid; - - --- --- TOC entry 211 (class 1259 OID 16392) --- Name: blocked; Type: TABLE; Schema: unispy; Owner: - --- - -CREATE TABLE unispy.blocked ( - blockid integer NOT NULL, - profileid integer NOT NULL, - namespaceid integer NOT NULL, - targetid integer NOT NULL -); - - --- --- TOC entry 3469 (class 0 OID 0) --- Dependencies: 211 --- Name: TABLE blocked; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.blocked IS 'Block list.'; - - --- --- TOC entry 212 (class 1259 OID 16395) --- Name: blocked_blockid_seq; Type: SEQUENCE; Schema: unispy; Owner: - --- - -CREATE SEQUENCE unispy.blocked_blockid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- TOC entry 3470 (class 0 OID 0) --- Dependencies: 212 --- Name: blocked_blockid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - --- - -ALTER SEQUENCE unispy.blocked_blockid_seq OWNED BY unispy.blocked.blockid; - - --- --- TOC entry 213 (class 1259 OID 16396) --- Name: friends; Type: TABLE; Schema: unispy; Owner: - --- - -CREATE TABLE unispy.friends ( - friendid integer NOT NULL, - profileid integer NOT NULL, - namespaceid integer NOT NULL, - targetid integer NOT NULL -); - - --- --- TOC entry 3471 (class 0 OID 0) --- Dependencies: 213 --- Name: TABLE friends; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.friends IS 'Friend list.'; - - --- --- TOC entry 214 (class 1259 OID 16399) --- Name: friends_friendid_seq; Type: SEQUENCE; Schema: unispy; Owner: - --- - -CREATE SEQUENCE unispy.friends_friendid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- TOC entry 3472 (class 0 OID 0) --- Dependencies: 214 --- Name: friends_friendid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - --- - -ALTER SEQUENCE unispy.friends_friendid_seq OWNED BY unispy.friends.friendid; - - --- --- TOC entry 215 (class 1259 OID 16400) --- Name: games; Type: TABLE; Schema: unispy; Owner: - --- - -CREATE TABLE unispy.games ( - gameid integer NOT NULL, - gamename character varying NOT NULL, - secretkey character varying, - description character varying(4095) NOT NULL, - disabled boolean NOT NULL -); - - --- --- TOC entry 3473 (class 0 OID 0) --- Dependencies: 215 --- Name: TABLE games; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.games IS 'Game list.'; - - --- --- TOC entry 216 (class 1259 OID 16405) --- Name: grouplist; Type: TABLE; Schema: unispy; Owner: - --- - -CREATE TABLE unispy.grouplist ( - groupid integer NOT NULL, - gameid integer NOT NULL, - roomname text NOT NULL -); - - --- --- TOC entry 3474 (class 0 OID 0) --- Dependencies: 216 --- Name: TABLE grouplist; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.grouplist IS 'Old games use grouplist to create their game rooms.'; - - --- --- TOC entry 217 (class 1259 OID 16410) --- Name: messages; Type: TABLE; Schema: unispy; Owner: - --- - -CREATE TABLE unispy.messages ( - messageid integer NOT NULL, - namespaceid integer, - type integer, - "from" integer NOT NULL, - "to" integer NOT NULL, - date timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - message character varying NOT NULL -); - - --- --- TOC entry 3475 (class 0 OID 0) --- Dependencies: 217 --- Name: TABLE messages; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.messages IS 'Friend messages.'; - - --- --- TOC entry 218 (class 1259 OID 16416) --- Name: messages_messageid_seq; Type: SEQUENCE; Schema: unispy; Owner: - --- - -CREATE SEQUENCE unispy.messages_messageid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- TOC entry 3476 (class 0 OID 0) --- Dependencies: 218 --- Name: messages_messageid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - --- - -ALTER SEQUENCE unispy.messages_messageid_seq OWNED BY unispy.messages.messageid; - - --- --- TOC entry 219 (class 1259 OID 16417) --- Name: partner; Type: TABLE; Schema: unispy; Owner: - --- - -CREATE TABLE unispy.partner ( - partnerid integer NOT NULL, - partnername character varying NOT NULL -); - - --- --- TOC entry 3477 (class 0 OID 0) --- Dependencies: 219 --- Name: TABLE partner; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.partner IS 'Partner information, these information are used for authentication and login.'; - - --- --- TOC entry 220 (class 1259 OID 16422) --- Name: profiles; Type: TABLE; Schema: unispy; Owner: - --- - -CREATE TABLE unispy.profiles ( - profileid integer NOT NULL, - userid integer NOT NULL, - nick character varying NOT NULL, - serverflag integer DEFAULT 0 NOT NULL, - status smallint DEFAULT 0, - statstring character varying DEFAULT 'I love UniSpy'::character varying, - location character varying, - firstname character varying, - lastname character varying, - publicmask integer DEFAULT 0, - latitude double precision DEFAULT 0, - longitude double precision DEFAULT 0, - aim character varying DEFAULT ''::character varying, - picture integer DEFAULT 0, - occupationid integer DEFAULT 0, - incomeid integer DEFAULT 0, - industryid integer DEFAULT 0, - marriedid integer DEFAULT 0, - childcount integer DEFAULT 0, - interests1 integer DEFAULT 0, - ownership1 integer DEFAULT 0, - connectiontype integer DEFAULT 0, - sex smallint DEFAULT 0, - zipcode character varying DEFAULT '00000'::character varying, - countrycode character varying DEFAULT 1, - homepage character varying DEFAULT 'unispy.org'::character varying, - birthday integer DEFAULT 0, - birthmonth integer DEFAULT 0, - birthyear integer DEFAULT 0, - icquin integer DEFAULT 0, - quietflags smallint DEFAULT 0 NOT NULL, - streetaddr text, - streeaddr text, - city text, - cpubrandid integer DEFAULT 0, - cpuspeed integer DEFAULT 0, - memory smallint DEFAULT 0, - videocard1string text, - videocard1ram smallint DEFAULT 0, - videocard2string text, - videocard2ram smallint DEFAULT 0, - subscription integer DEFAULT 0, - adminrights integer DEFAULT 0 -); - - --- --- TOC entry 3478 (class 0 OID 0) --- Dependencies: 220 --- Name: TABLE profiles; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.profiles IS 'User profiles.'; - - --- --- TOC entry 221 (class 1259 OID 16459) --- Name: profiles_profileid_seq; Type: SEQUENCE; Schema: unispy; Owner: - --- - -CREATE SEQUENCE unispy.profiles_profileid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- TOC entry 3479 (class 0 OID 0) --- Dependencies: 221 --- Name: profiles_profileid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - --- - -ALTER SEQUENCE unispy.profiles_profileid_seq OWNED BY unispy.profiles.profileid; - - --- --- TOC entry 222 (class 1259 OID 16460) --- Name: pstorage; Type: TABLE; Schema: unispy; Owner: - --- - -CREATE TABLE unispy.pstorage ( - pstorageid integer NOT NULL, - profileid integer NOT NULL, - ptype integer NOT NULL, - dindex integer NOT NULL, - data jsonb -); - - --- --- TOC entry 3480 (class 0 OID 0) --- Dependencies: 222 --- Name: TABLE pstorage; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.pstorage IS 'Old games use pstorage to store game data.'; - - --- --- TOC entry 223 (class 1259 OID 16465) --- Name: pstorage_pstorageid_seq; Type: SEQUENCE; Schema: unispy; Owner: - --- - -CREATE SEQUENCE unispy.pstorage_pstorageid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- TOC entry 3481 (class 0 OID 0) --- Dependencies: 223 --- Name: pstorage_pstorageid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - --- - -ALTER SEQUENCE unispy.pstorage_pstorageid_seq OWNED BY unispy.pstorage.pstorageid; - - --- --- TOC entry 224 (class 1259 OID 16466) --- Name: sakestorage; Type: TABLE; Schema: unispy; Owner: - --- - -CREATE TABLE unispy.sakestorage ( - sakestorageid integer NOT NULL, - tableid character varying NOT NULL -); - - --- --- TOC entry 3482 (class 0 OID 0) --- Dependencies: 224 --- Name: TABLE sakestorage; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.sakestorage IS 'Sake storage system.'; - - --- --- TOC entry 225 (class 1259 OID 16471) --- Name: sakestorage_sakestorageid_seq; Type: SEQUENCE; Schema: unispy; Owner: - --- - -CREATE SEQUENCE unispy.sakestorage_sakestorageid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- TOC entry 3483 (class 0 OID 0) --- Dependencies: 225 --- Name: sakestorage_sakestorageid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - --- - -ALTER SEQUENCE unispy.sakestorage_sakestorageid_seq OWNED BY unispy.sakestorage.sakestorageid; - - --- --- TOC entry 226 (class 1259 OID 16472) --- Name: subprofiles; Type: TABLE; Schema: unispy; Owner: - --- - -CREATE TABLE unispy.subprofiles ( - subprofileid integer NOT NULL, - profileid integer NOT NULL, - uniquenick character varying, - namespaceid integer DEFAULT 0 NOT NULL, - partnerid integer DEFAULT 0 NOT NULL, - productid integer, - gamename text, - cdkeyenc character varying, - firewall smallint DEFAULT 0, - port integer DEFAULT 0, - authtoken character varying -); - - --- --- TOC entry 3484 (class 0 OID 0) --- Dependencies: 226 --- Name: TABLE subprofiles; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.subprofiles IS 'User subprofiles.'; - - --- --- TOC entry 227 (class 1259 OID 16481) --- Name: subprofiles_subprofileid_seq; Type: SEQUENCE; Schema: unispy; Owner: - --- - -CREATE SEQUENCE unispy.subprofiles_subprofileid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- TOC entry 3485 (class 0 OID 0) --- Dependencies: 227 --- Name: subprofiles_subprofileid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - --- - -ALTER SEQUENCE unispy.subprofiles_subprofileid_seq OWNED BY unispy.subprofiles.subprofileid; - - --- --- TOC entry 228 (class 1259 OID 16482) --- Name: users; Type: TABLE; Schema: unispy; Owner: - --- - -CREATE TABLE unispy.users ( - userid integer NOT NULL, - email character varying NOT NULL, - password character varying NOT NULL, - emailverified boolean DEFAULT true NOT NULL, - lastip inet, - lastonline timestamp without time zone DEFAULT CURRENT_TIMESTAMP, - createddate timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - banned boolean DEFAULT false NOT NULL, - deleted boolean DEFAULT false NOT NULL -); - - --- --- TOC entry 3486 (class 0 OID 0) --- Dependencies: 228 --- Name: TABLE users; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.users IS 'User account information.'; - - --- --- TOC entry 229 (class 1259 OID 16492) --- Name: users_userid_seq; Type: SEQUENCE; Schema: unispy; Owner: - --- - -CREATE SEQUENCE unispy.users_userid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; - - --- --- TOC entry 3487 (class 0 OID 0) --- Dependencies: 229 --- Name: users_userid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - --- - -ALTER SEQUENCE unispy.users_userid_seq OWNED BY unispy.users.userid; - - --- --- TOC entry 3219 (class 2604 OID 16493) --- Name: addrequests addrequestid; Type: DEFAULT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.addrequests ALTER COLUMN addrequestid SET DEFAULT nextval('unispy.addrequests_addrequestid_seq'::regclass); - - --- --- TOC entry 3220 (class 2604 OID 16494) --- Name: blocked blockid; Type: DEFAULT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.blocked ALTER COLUMN blockid SET DEFAULT nextval('unispy.blocked_blockid_seq'::regclass); - - --- --- TOC entry 3221 (class 2604 OID 16495) --- Name: friends friendid; Type: DEFAULT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.friends ALTER COLUMN friendid SET DEFAULT nextval('unispy.friends_friendid_seq'::regclass); - - --- --- TOC entry 3223 (class 2604 OID 16496) --- Name: messages messageid; Type: DEFAULT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.messages ALTER COLUMN messageid SET DEFAULT nextval('unispy.messages_messageid_seq'::regclass); - - --- --- TOC entry 3256 (class 2604 OID 16497) --- Name: profiles profileid; Type: DEFAULT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.profiles ALTER COLUMN profileid SET DEFAULT nextval('unispy.profiles_profileid_seq'::regclass); - - --- --- TOC entry 3257 (class 2604 OID 16498) --- Name: pstorage pstorageid; Type: DEFAULT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.pstorage ALTER COLUMN pstorageid SET DEFAULT nextval('unispy.pstorage_pstorageid_seq'::regclass); - - --- --- TOC entry 3258 (class 2604 OID 16499) --- Name: sakestorage sakestorageid; Type: DEFAULT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.sakestorage ALTER COLUMN sakestorageid SET DEFAULT nextval('unispy.sakestorage_sakestorageid_seq'::regclass); - - --- --- TOC entry 3263 (class 2604 OID 16500) --- Name: subprofiles subprofileid; Type: DEFAULT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.subprofiles ALTER COLUMN subprofileid SET DEFAULT nextval('unispy.subprofiles_subprofileid_seq'::regclass); - - --- --- TOC entry 3269 (class 2604 OID 16501) --- Name: users userid; Type: DEFAULT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.users ALTER COLUMN userid SET DEFAULT nextval('unispy.users_userid_seq'::regclass); - - --- --- TOC entry 3440 (class 0 OID 16386) --- Dependencies: 209 --- Data for Name: addrequests; Type: TABLE DATA; Schema: unispy; Owner: - --- - -COPY unispy.addrequests (addrequestid, profileid, namespaceid, targetid, reason, syncrequested) FROM stdin; -\. - - --- --- TOC entry 3442 (class 0 OID 16392) --- Dependencies: 211 --- Data for Name: blocked; Type: TABLE DATA; Schema: unispy; Owner: - --- - -COPY unispy.blocked (blockid, profileid, namespaceid, targetid) FROM stdin; -\. - - --- --- TOC entry 3444 (class 0 OID 16396) --- Dependencies: 213 --- Data for Name: friends; Type: TABLE DATA; Schema: unispy; Owner: - --- - -COPY unispy.friends (friendid, profileid, namespaceid, targetid) FROM stdin; -\. - - --- --- TOC entry 3446 (class 0 OID 16400) --- Dependencies: 215 --- Data for Name: games; Type: TABLE DATA; Schema: unispy; Owner: - --- - -COPY unispy.games (gameid, gamename, secretkey, description, disabled) FROM stdin; -1 gmtest HA6zkS Test / demo / temporary f -2 bgate 2ozFrM Baldur's Gate f -3 blood2 jUOF0p Blood II f -5 daikatana fl8aY7 John Romero's Daikatana f -6 descent3 feWh2G Descent 3 f -7 dh3 gbnYTp Deer Hunter 3 f -9 dv O1Vodm Dark Vengeance f -10 expertpool cRu7vE Expert Pool f -11 forsaken znoJ6k Forsaken f -12 gamespy2 d4kZca GameSpy 3D f -13 gspylite mgNUaC GameSpy Lite f -14 gspyweb 08NHv5 GameSpy Web f -15 halflife ZIr1wX Half Life f -17 hexenworld 6SeXQB Hexenworld f -18 kingpin QFWxY2 Kingpin: Life of Crime f -19 mplayer 3xYjaU MPlayer f -21 quake2 rtW0xg Quake II f -22 quake3 paYVJ7 Quake 3: Arena f -23 quakeworld FU6Vqn QuakeWorld f -24 rally xdNbQZ Rally Masters f -25 redline 2J5aV2 Redline f -27 sin Ij1uAB SiN f -28 slavezero Xrv9zn Slave Zero f -29 sof nJ0rZz Soldier of Fortune f -31 specops adyYWv Spec Ops f -32 tribes z83fc2 Starsiege TRIBES f -33 turok2 RWd3BG Turok 2 f -34 unreal DAncRK Unreal f -35 ut Z5Nfb0 Unreal Tournament f -36 viper SSzOWL Viper f -40 wot RSSSpA Wheel of Time f -41 giants z8erKA Giants: Citizen Kabuto f -42 dtracing ipC912 Dirt Track Racing f -43 terminus z9uima Terminus f -45 ra2 9z3312 Rocket Arena 2 f -46 aoe2 iZhjKi Age of Empires II f -47 roguespear kqeEcz Rainbow Six: Rogue Spear f -49 scrabble Pz3Vea Scrabble v2.0 f -50 boggle geaDfe Boggle f -51 werewolf 81zQQa Werewolf: The Apocalypse f -52 treadmarks u27bAA Tread Marks f -54 rock HnVZ1u Rock f -55 midmad 8gEaZv Midtown Madness f -56 aoe VzkADe Age of Empires f -57 revolt fa5lhE Re-Volt f -58 gslive Xn221z GameSpy Arcade f -61 wildwings PbNDFL Wild Wings f -62 rmth3 EvBDAc Rocky Mountain Trophy Hunter 3 f -64 metalcrush3 KvE2Pk Metal Crush 3 f -65 ta vPqkAc Total Annihilation f -70 mcmad aW7c9n Motocross Madness f -71 heroes3 5Un7pR Heroes of Might and Magic III f -72 jk 9nUz45 Star Wars Jedi Knight: Dark Forces II f -73 links98 J8yc5z Links LS 1998 f -84 xwingtie Lc8gW5 Star Wars: X-Wing vs. TIE Fighter f -99 buckmaster 4NcAZg Buckmaster Deer Hunting f -100 cneagle HNvEAc Codename: Eagle f -104 alphacent qbb4Ge Sid Meier's Alpha Centauri f -108 sanity 7AeTyu Sanity f -113 starraiders n76Cde Star Raiders f -114 kiss 9tFALe KISS: Psycho Circus f -121 risk nx6I2v Risk C.1997 f -122 cribbage TKuE2P Hasbro's Cribbage f -125 ginrummy 9rIEUi Hasbro's Gin Rummy f -126 hearts 9SKI3t Hasbro's Hearts f -128 spades YvPBIM Hasbro's Spades f -129 racko U8QYlf Hasbro's Rack-O f -130 rook Bc1Zmb Hasbro's Rook f -131 checkers 2hfuJA Hasbro's Checkers f -133 chess g11Aig Hasbro's Chess f -135 tzar byTPgq Tzar: The Burden of the Crown f -136 parcheesi PHCviG Hasbro's Parcheesi f -138 backgammon VCypJm Hasbro's Backgammon f -139 freepark alVRIq Hasbro's Free Parking f -140 connect4 2Pnx6I Hasbro's Connect 4 f -141 millebourn kD072v Hasbro's Mille Bournes f -142 msgolf99 alVRIq Microsoft Golf '99 f -144 close4bb alVRIq Close Combat IV: Battle of the Bulge f -145 aliencross IOrDfP Alien Crossfire f -146 outlaws TKuE2P Outlaws f -147 civ2gold alVRIq Civilization II Gold f -148 getmede 3MHCZ8 Get Medieval f -150 monopoly alVRIq Monopoly 2000 f -151 rb6 49qmcl Tom Clancy's Rainbow Six f -153 rebellion TKuE2P Star Wars Rebellion f -154 ccombat3 TKuE2P Close Combat III: The Russian Front f -156 jkmosith1 kD072v Star Wars Jedi Knight: Mysteries of the Sith f -157 smgettysbu 3MHCZ8 Sid Meier's Gettysburg f -158 srally2dmo kD072v Sega Rally 2 (PC Demo) f -159 fltsim2k TKuE2P Flight Simulator 2000 f -161 duke4 8n2Hge Duke Nukem Forever f -162 aowfull alVRIq Age Of Wonders f -163 darkstone 3MHCZ8 Darkstone f -164 abominatio qik37G Abomination f -165 bc3k 5LnQaz Battle Cruiser 3000 AD f -166 outlawsdem k37G3M Outlaw (Multiplay Demo) f -167 allegiance YghTwJ MS Allegiance f -169 aoe2demo alVRIq Age of Empires II (Demo) f -170 mcmaddemo 3MHCZ8 Motocross Madness (Demo) f -171 midmaddemo 3MHCZ8 Midtown Madness (Demo) f -172 mtmdemo 6I2vIO Monster Truck Madness 2 (Demo) f -173 axisallies JwWh9S Axis & Allies f -175 worms2 IOrDfP Worms 2 f -176 mtruckm2 TKuE2P Monster Truck Madness 2 f -177 powerslide nx6I2v Powerslide f -178 kissdc 6EwAbh Kiss (Dreamcast) f -179 legendsmm 5Kbawl Legends of Might and Magic f -180 mech4 uNbXef Mechwarrior 4: Vengeance f -182 majesty qik37G Majesty: The Fantasy Kingdom Sim f -307 cossacks p2vPkJ Cossacks Anthology f -183 fblackjack NeVbEa Fiendish Blackjack f -184 slancerdc UbNea2 Starlancer (Dreamcast) f -186 dogsofwar Mbe3if Dogs of War f -187 starlancer qik37G Starlancer f -188 laserarena JbEb3a Laser Arena (2015) f -189 mmadness2 MZIq1w Motocross Madness 2 f -190 obiwon UnEhYr Obi-Wan f -191 ra3 JnEfae Rocket Arena 3 f -195 sanitydemo 7AeTyu Sanity: Aiken's Artifact (Demo) f -196 sanitybeta 7AeTyu Sanity: Aiken's Artifact (Public Beta) f -198 stitandemo 9mDKzi Submarine Titans (Demo) f -199 stbotf aiiOU0 Birth of the Federation f -200 machines xS6aii Machines f -202 amworldwar nLfZDY Army Men: World War f -203 gettysburg PwZFex Sid Meier's Gettysburg! f -204 hhbball2000 zfsDV2 High Heat Baseball 2000 f -205 dogalo MZIr0w MechWarrior 3 f -206 armymen2 YBLJvU Army Men II f -207 armymenspc r1wXEX Army Men Toys in Space f -209 risk2 xboeRW Risk II f -210 starwrsfrc p4jGh6 Star Wars: Force Commander f -211 peoplesgen el1q7z Peoples General f -212 planecrazy p5jGh6 Plane Crazy f -213 linksext Fdk2q7 Links Extreme f -214 flyinghero 9mELiP Flying Heroes f -216 links2000 MIr1wW Links LS 2000 f -217 ritesofwar 2p7zgs Warhammer: Rites of War f -218 gulfwarham YDXBOF Gulf War: Operatin Desert f -219 uprising2 ALJwUh Uprising 2 f -220 earth2150 1wXEX3 Earth 2150 f -221 evolva DV24p2 Evolva f -223 7kingdoms WEX3rA Seven Kingdoms 2 f -224 migalley wUhCSC Mig Alley f -225 axallirnb GexS6a Axis & Allies: Iron Blitz f -227 mcommgold xS6aji MechCommander Gold f -228 santietam zfsCV2 Sid Meier's Antietam! f -230 panzergen2 DLiPwZ Panzer General f -231 lazgo2demo MIq0wW Lazgo 2 Demo f -232 taking p5kGg7 Total Annihilation: Kingdoms f -233 mfatigue nfRW88 Metal Fatigue f -235 starsiege MZIq1w Starsiege f -236 jkmots 6ajiPU Star Wars Jedi Knight: Mysteries of the Sith f -237 zdoom MIr0wW ZDoom f -238 warlordsb 9gV5Hm Warlords Battlecry f -240 anno1602ad sAJtHo Anno 1602 A.D. f -241 dh4 BeaPe2 Deer Hunter 4 f -242 group 72Ha31 Group Room f -243 blademasters B3Avke Legend of the Blademasters f -244 iwdale LfZDYB Icewind Dale f -245 dogsrunamock p2vPkJ dogsrunamock (?) f -246 excessive Gn3aY9 Excessive Q3 f -248 mcm2demo ajhOU0 Motocross Madness 2 Demo f -249 dtrsc p2vPkJ Dirt Track Racing: Sprint Cars f -250 chspades Yw7fc9 Championship Spades f -251 chhearts Yw7fc9 Championship Hearts f -252 stef1 H28D2r Star Trek: Voyager – Elite Force f -254 nolf Jn3Ab4 No One Lives Forever f -255 dtr h7nLfZ Dirt Track Racing f -256 sacrifice sCV34o Sacrifice f -257 rune V5Hm41 Rune f -258 aoe2tc p4jAGg Age of Empires II: The Conquerors f -259 stitans V5Hl31 Submarine Titans f -260 bang zgsCV2 Bang! Gunship Elite f -262 fakk2 YDYBNE F.A.K.K. 2 f -263 bcm tHg2t7 Battlecruiser: Millenium f -264 ds9dominion BkAg3a DS9: Dominion Wars f -265 bots JKb462 Bots (Lith) f -266 tacore 1ydybn Core Contingency f -267 mech3pm TORp4k Pirates Moon f -268 diplomacy 2p7zgs Diplomacy f -270 fargate nhwchs Far Gate f -271 nexttetris KVWE12 The Next Tetris f -272 fforce ys3k4d Freedom Force f -273 iwar2 Bk3a13 Independance War 2 f -274 gp500 cvSTOR GP500 f -275 midmad2 7nLfZD Midtown Madness 2 f -277 unreal2 Yel30y Unreal 2 f -278 4x4evo tFbq8m 4x4 Evolution f -279 crimson YBLJwU Crimson Skies f -280 harleywof bofRW8 Wheels of Freedom f -282 ageofsail2 Kb3ab5 Age of Sail 2 f -283 cskies Rp5kAG Crimson Skies f -284 rscovertops yK2A0x Rainbow Six: Covert Ops f -285 pba2001 Kbay43 PBA Bowling 2001 f -286 cskiesdemo p2uPkK Crimson Skies Demo f -287 mech4st tFcq8m MechWarrior 4: Vengeance f -289 sinmac 3Ka5BN SiN (Mac) f -290 wosinmac yX02mQ SiN: Wages of Sin (Mac) f -291 utdc KbAgl4 Unreal Tournament (Dreamcast) f -292 kohan Kbao3a Kohan: Immortal Sovereigns f -293 mcmania BAbas9 Motocross Mania f -295 furfiighters JwUhCT Fur Fighters (?) f -296 furfighters JwUhCT Fur Fighters f -297 owar xS6aii Original War f -298 cfs2 uPkKya Combat Flight Simulator 2 f -299 uno MZIq0w UNO f -302 gore NYZEAK Gore f -303 gangsters2 NEek2p Gansters II: Vendetta f -304 insanedmo 3rAJtI Insane Demo f -306 atlantis W49nx4 Atlantis f -308 ihraracing Zbmu4a IHRA Drag Racing f -309 atlantispre W49nx4 Atlantis Prequel f -310 4x4retail MIq0wX 4x4 Evolution f -311 rnconsole Jba3n1 Real Networks Console f -312 dukes dvRTOR Dukes Of Hazzard: Racing f -313 serioussam AKbna4 Serious Sam f -234 cfs \N Microsoft Combat Flight Simulator f -337 armada2 N3a2mZ Star Trek Armada 2 f -316 rfts jiPV0u Reach For The Stars f -317 cheuchre Yw7fc9 Championship Euchre f -318 links2001 8cvSTO Links 2001 f -320 4x4evodemo p4jAGg 4x4 Evolution Demo f -321 mcmaniadmo TCQMIr Motocross Mania Demo f -322 gamevoice Agm3a1 MS Game Voice f -323 cstrike ZIr1wX Counter-Strike f -324 venomworld Jg43a1 Venom World f -325 omfbattle Abm93d One Must Fall Battlegrounds f -326 furdemo 3rAJtH Fur Fighters Demo f -328 nwn ZIq1wW Neverwinter Nights f -329 strifeshadow 99mEKi Strifeshadow f -330 ssamdemo Gn3a12 Serious Sam Demo f -331 kacademy blGjuN Klingon Academy f -332 goredemo uW0xp1 Gore Demo f -334 midmad2dmo sAJtHo Midtown Madness 2 Demo f -335 gunman W78dvR Gunman Chronicles f -336 stronghold QwZFex Stronghold f -338 links2001dmo xZGexS Links 2001 Demo f -339 q3tafull Ah3mb4 Team Arena Retail f -341 battlerealms hU7wE3 Battle Realms f -342 sfc OV0tKn Starfleet Command f -343 strfltcmd2 8cvSTO Starfleet Command Volume f -344 stnw 8cvRTO Star Trek: New Worlds f -345 strfltcmd2d gW5Hm4 Empires at War Demo f -346 sfcdemo MZIr1w Starfleet Command Demo f -348 waterloo CTCQMZ Waterloo f -349 falloutbosd JwUhCT Fallout Tactics f -350 kohandemo Kbao3a Kohan Demo f -351 exploeman 8dv Explöman f -352 segarally2 ajiPV0 Sega Rally 2 f -354 streetjam jAGh7n Ultra Wheels Street Jam f -355 explomaen fZDYBN Explomän f -357 mrwtour W78cvR Motoracer World Tour f -358 wordzap q7zfsD WordZap f -359 iwdalehow h3U3Kz Icewind Dale: Heart of Winter f -360 magmay2 QW88dv Magic & Mayhem 2 f -361 chat01 xQ7fp2 Chat Group 1 f -362 chat02 xQ7fp2 Chat Group 2 f -363 chat03 xQ7fp2 Chat Group 3 f -364 Chat04 xQ7fp2 Chat Group 4 f -365 Chat05 xQ7fp2 Chat Group 5 f -366 Chat06 xQ7fp2 Chat Group 6 f -367 Chat07 xQ7fp2 Chat Group 7 f -368 Chat08 xQ7fp2 Chat Group 8 f -370 Chat10 xQ7fp2 Chat Group 10 f -371 Chat11 xQ7fp2 Chat Group 11 f -372 Chat12 xQ7fp2 Chat Group 12 f -373 Chat13 xQ7fp2 Chat Group 13 f -375 Chat15 xQ7fp2 Chat Group 15 f -376 Chat16 xQ7fp2 Chat Group 16 f -377 Chat17 xQ7fp2 Chat Group 17 f -378 Chat18 xQ7fp2 Chat Group 18 f -379 Chat19 xQ7fp2 Chat Group 19 f -381 empireearth ybneQW Empire Earth f -382 chasspart5 p4kGg7 ChessPartner 5 f -383 bg2bhaal 532HaZ Baldur's Gate II: Throne of Bhaal f -385 cultures Ir1wXE Cultures f -386 fatedragon sCV34o Fate of the Dragon f -387 sbubpop u2K9p5 Super Bubble Pop f -388 xcomenforcer M3A2rK X-Com: Enforcer f -389 aow2 csFcq8 Age of Wonders 2 f -390 startopia r5UN9g Startopia f -391 jefftest f6Ylm1 Test for Jeffs Games f -392 hhbball2002 YBNEdl High Heat Baseball 2002 f -394 por2 9agW5H Pool of Radiance 2 f -395 falloutbos fQW78d Fallout Tactics f -397 fatedragond 6UN9ag Fate of the Dragon Demo f -398 demonstar ziPwZF Demonstar f -399 tf15 V0tKnY Half-Life 1.5 f -400 gspoker PbZ35N GameSpy Poker f -401 gsspades PbZ35N GameSpy Spades f -402 gshearts PbZ35N GameSpy Hearts f -404 gscheckers PbZ35N GameSpy Checkers f -405 atlantica LfZDXB Atlantica f -406 merchant2 zdybne Merchant Prince II f -407 magmay2d ORp4kG The Art of War f -408 assimilation BOFdk1 Assimilation f -409 zax J3An3s Zax f -410 leadfoot uNctFb Leadfoot f -412 chat DagNzk Chat Service f -413 disciples hPV0uK Disciples f -414 opflash h28Doi Operation Flashpoint f -415 zsteel 4p2uPk Z: Steel Soldiers f -417 gschess BQMZIq GameSpy Chess f -418 gsreversi ItHo0r GameSpy Reversi f -419 gsyarn m31ydy GameSpy Y.A.R.N. f -420 tribes2 DAM4Kv Tribes 2 f -421 avp2 Df3M6Z Aliens vs Predator 2 f -422 bodarkness Jn33pM Blade of Darkness f -423 dominion 1zdybo Dominion f -425 opflashd DV24o2 Operation Flashpoint Demo f -426 blade Eel1q7 Blade f -427 mechcomm Ir0wXE MechCommander f -428 globalops AdN3L9 Global Operations f -429 links99 iQxZFe Links LS 1999 f -430 rulesotg iQxZGe Rules of the Game f -432 armymen V5Hm41 Army Men f -433 _news BADBAD News f -434 railsamd GjuMct Rails Across America Demo f -435 railsam sFcq99 Rails Across America f -436 kohanexp Kbao3a Kohan Expansion f -438 wz2100demo AGg7mM Warzone 2100 (Demo) f -439 sfc2opdv Gj2k7A Starfleet Command II: Orion Pirates (Dynaverse II) f -440 roguespeard S6ajhP Rogue Spear Demo f -441 redalert QwZGex Red Alert f -442 wormsarm p2uPkK Worms Armageddon f -443 takingdoms kJyalH TA: Kingdoms f -444 arc M9tZe4 Arc: Sierra f -353 explomän \N Explomän f -447 dh5 Ji2R2v Deer Hunter 5 f -448 diablo2 hPU0tK Diablo 2 f -449 starcraft LfYDYB Starcraft f -450 starcraftdmo NFel1q Starcraft Demo f -452 warcraft2bne gW5Hl4 Warcraft 2 f -453 redalert2 ajhOV0 Red Alert 2 f -454 projecteden TORp5k Project Eden f -455 roadwars ORp5kG Road Wars f -456 tiberiansun Gg6nLf Tiberian Sun f -457 chessworlds 5Hm41y Chess Worlds f -459 sfc2op EX3rAJ Starfleet Command: Orion f -460 warlordsdr exS6aj Warlords III: Dark Rising f -462 cmanager S6aiiO Cycling Manager f -463 laserarenad TBQMIr Laser Arena Demo f -464 starcraftexp DKiPxZ Starcraft: Brood Wars f -465 tsfirestorm fRW88c Tiberian Sun - Firestorm f -466 monopolyty YDXBNE Monopoly Tycoon f -467 thps3ps2 hD72Kq Tony Hawk Pro Skater 3 (PS2) f -468 emperorbfd X3rAIt Emperor: Battle For Dune f -469 claw ziPwZG Claw f -470 armymenrts Rp4jGh Army Men RTS f -472 conquestfw ORp4kG Conquest: Frontier Wars f -473 realwar 78dvRT Real War f -474 axis nYBLJv Axis f -475 anno1503 9mDKiP Anno 1503 f -476 incomingforces MIr0wW Incoming Forces f -477 ironstrategy ZDYBNF Iron Strategy f -478 heavygear2 hCTBQM Heavy Gear 2 f -480 motoracer3 rAItHo Motoracer 3 f -481 rogerwilco rW17Ko Roger Wilco f -482 conquestfwd ZIr0wW Conquest: Frontier Wars D f -483 thps3media tRKg39 Tony Hawk Pro Skater 3 Media f -484 echelon uPkKya Echelon f -485 takeda 6TN9gW Takeda f -486 cnoutbreak Jg43a1 Codename: Outbreak f -487 oldscrabble Pz4Veb Scrabble 1.0 f -489 rdpoker GjuMct Reel Deal Poker f -490 f1teamdriver QwZFex Williams F1 Team: Team Dr f -491 aquanox U3pf8x Aquanox f -492 mohaa M5Fdwc Medal of Honor Allied Assault f -493 harley3 KdF35z Harel 3 f -494 cnoutbreakd Jg43a1 Codename: Outbreak Demo f -495 ras 0r5UN9 Red Ace Squadron f -496 opfor YDYBNE Opposing Force f -498 austerlitz hCSBQM Austerlitz: Napoleons Gre f -499 dmania Dn3H2v DMania f -500 bgatetales GjuMcs Baldur's Gate: Tales of the Sword Coast f -501 cueballworldd uPkKyb Cueball World Demo f -502 st_rank 53Jx7W Global Rankings Sample f -503 rallytrophy CSCQMI Rally Trophy f -505 actval1 j9Ew2L Activision Value Title 1 f -506 etherlords 6ajiOV Etherlords f -507 swine SwK4J2 S.W.I.N.E. f -508 warriorkings hCSCQM Warrior Kings f -509 myth3 W7LHE8 Myth 3 f -517 commandos2 V0tKnY Commandos 2 f -518 mcomm2 7JsQ0t MechCommander 2 f -520 praetorians m31zdx Praetorians f -521 iwd2 Q3yu7R Icewind Dale 2 f -522 gamebot G4mBo7 GameBot Test f -523 armada2beta N3a2mZ Star Trek: Armada 2 Beta f -524 rtcwtest 78dvST Wolfenstein MP Test f -526 nvchess YDXBOF nvChess f -527 msecurity j9Ew2L Alcatraz: Prison Escape f -528 avp2demo Df3M6Z Aliens vs Predator 2 Demo f -530 chaser Pe4W2B Chaser f -531 nascar5 j3Do2f NASCAR 5 f -532 kohanag dl1p7z Kohan: Ahrimans Gift f -533 tribes2demo AdF313 Tribes 2 Demo f -534 serioussamse AKbna4 Serious Sam: Second Encounter f -535 st_ladder KwFJ2X Global Rankings Sample - Ladder f -536 swgb XEX3sA Star Wars: Galactic Battlegrounds f -537 bumperwars Rp5kAG Bumper Wars! f -538 combat p5kGh7 Combat f -540 rsblackthornd BLJvUh Black Thorn Demo f -541 bfield1942 HpWx9z Battlefield 1942 f -543 swinedemo SwK4J2 Swine Demo f -544 freedomforce lHjuMc Freedom Force f -545 il2sturmovik ajiPU0 IL-2 Sturmovik f -548 myth3demo rAItIo Myth 3 Demo f -550 ghostrecon p5jAGh Tom Clancy's Ghost Recon f -551 ghostrecond KyblGj Tom Clancy's Ghost Recon Demo f -552 fltsim2002 uKnYBL Microsoft Flight Simulator 2002 f -553 mech4bkexp csFbq9 MechWarrior Black Knight f -554 hd MIq1wX Hidden & Dangerous Enhanc f -555 strifeshadowd 99mEKi Strifeshadow Demo f -556 conflictzone g7nLfZ Conflict Zone f -558 druidking p5kGg7 Druid King f -559 itycoon2 JeW3oV Industry Tycoon 2 f -560 sof2 F8vZed Soldier of Fortune 2 f -561 armada2d N3a2mZ Star Trek: Armada II Demo f -563 rtcw Gj3aV2 Return to Castle Wolfenstein f -564 xboxtunnel 8dvSTO Xbox Tunnel Service f -565 survivor H2du2 Survivor Ultimate f -566 il2sturmovikd zfsDV2 IL-2 Sturmovik Demo f -567 haegemonia LiQwZF Haegemonia f -568 mohaad M5Fdwc Medal of Honor: Allied Assault Demo f -570 janesf18 hPV0uK Janes F/A-18 f -571 janesusaf 6aiiPU Janes USAF f -572 janesfa OFek1p Janes Fighters Anthology f -573 janesf15 XEX3rA Janes F-15 f -574 janesww2 wUhCTB Janes WWII Fighters f -575 mech4bwexpd Fel1q7 MechWarrior Black Knight f -576 f12002 DXBOFd F1 2002 f -577 ccrenegade tY1S8q Command & Conquer: Renegade f -542 battlerealmsbBAD \N Battle Realms Beta f -580 demoderby Hl31yd Demolition Derby & Figure f -581 janesattack dvSTOR Janes Attack Squadron f -582 chesk W5Hl41 Chesk f -583 hhball2003 cvSTOR High Heat Baseball 2003 f -584 duelfield 8mELiP Duelfield f -585 carnivores3 yd7J2o Carnivores 3 f -587 thps3pc KsE3a2 Tony Hawk 3 (PC) f -588 blockade 3sAJtI Operation Blockade f -589 mafia dxboeR Mafia f -590 ccrenegadedemo LsEwS3 Command & Conquer: Renegade Demo f -591 shadowforce A3p54Q Shadow Force: Razor Unit f -595 gta3pc Hu3P1h Grand Theft Auto 3 (PC) f -596 vietkong bq98mE Vietkong f -598 subcommand iPwZGe Sub Command f -599 originalwar CV34p2 Original War f -600 thps4ps2 H2r8W1 Tony Hawk: Pro Skater 4 (PS2) f -601 warlordsb2d tKnYBL Warlords Battlecry II Demo f -602 ioftheenemy uPkKya I of the Enemy f -603 sharpshooter 9gV5Hl Sharp Shooter f -605 globalopspb CHANGE Global Operations Public Beta f -606 pb4 s82J1p Extreme Paintbrawl 4 f -610 armygame g3sR2b Americas Army: Special Forces f -611 homm4 6ajhPU Heroes of Might and Magic f -612 darkplanet uPkJyb Dark Planet f -614 teamfactor RW78cv Team Factor f -615 dragonthrone p5jAGh Dragon Throne f -616 celebdm h5D7j8 Celebrity Deathmatch f -617 phoenix GknAbg Phoenix (Stainless Steel) f -618 matrixproxy m6NwA2 Matrix Proxy f -620 ghostreconds EX3rAI Ghost Recon: Desert Siege f -621 sof2demo F8vZed Soldier of Fortune 2 Demo f -622 etherlordsbeta 6ajiOV Etherlords Patch Beta f -623 aow2d S6aiiP Age of Wonders 2 Demo f -624 privateer Yh3o2d Privateers Bounty: Age of Sail 2 f -625 gcracing LziPwZ Great Clips Racing f -627 dungeonsiege H3t8Uw Dungeon Siege f -628 silenthunter2 bnfRW8 Silent Hunter 2 f -629 celtickings MIq0wW Druid King f -630 globalopsd u3Pa87 Global Ops Demo f -631 renegadebf Rt7W9x Renegade Battlefield f -633 tacticalops uMctFb Tactical Ops f -634 ut2 Lw7x0R Unreal Tournament 2003 f -635 swgbcc RTORp4 Star Wars Galactic Battle f -636 ut2d y5e8Q3 Unreal Tournament 2003 Demo f -637 voiceapp sD3GkC VoiceApp Voice SDK Test f -639 streetracer ydxboe Streetracer f -640 opflashr Y3k7x1 Operation Flashpoint: Resistance f -641 mohaas 2vPkJy Medal of Honor: Allied Assault Spearhead f -642 avp2ph P4fR9w Aliens vs. Predator 2: Primal Hunt f -643 nthunder2003 Ld5C9w NASCAR Thunder 2003 f -645 dtr2 MIq1wW Dirt Track Racing II f -646 GameSpy.com xbofQW GameSpy.com f -702 gicombat1 \N G.I. Combat f -649 darkheaven G3i4Xk Dark Heaven f -650 twc iPxZFe Takeout Weight Curling f -651 steeltide zgsDV2 Operation Steel Tide f -652 realwarrs 8cvSTO Real War: Rogue States f -654 rmth2003 Y4kC7S Trophy Hunter 2003 f -655 strongholdc fYDXBO Stronghold: Crusader f -656 soa H3pD7m Soldiers of Anarchy f -657 jbnightfire S9j3L2 James Bond: Nightfire f -658 sumofallfears 4kGh6m The Sum of All Fears f -659 nfs6 ZIr1wX Need For Speed: Hot Pursuit 2 f -660 bangler2003 hCTCQM Bass Angler 2003 f -661 netathlon2 RW88dv NetAthlon f -663 ddozenpt L3sB7f Deadly Dozen: Pacific Theater f -664 vietnamso E8d3Bo Line of Sight: Vietnam f -665 mt2003 TORp4j Monopoly 2003 f -666 soad K3e8cT Soldiers of Anarchy Demo f -668 ironstorm y5Ei7C Iron Storm f -669 civ3ptw yboeRW Civilization III: Play the World f -670 tron20 t9D3vB TRON 2.0 f -671 bfield1942d gF4i8U Battlefield 1942 Demo f -672 scrabble3 4o2vPk Scrabble 3 f -673 vietcong bq98mE Vietcong f -675 ccgenerals h5T2f6 Command & Conquer: Generals f -676 sfc3dv Gi7C8s Starfleet Command III (Dynaverse) f -677 bandits H2k9bD Bandits: Phoenix Rising f -678 xar N9gV5H Xtreme Air Racing f -679 echelonww uPkKya Echelon Wind Warriors f -684 mclub2ps2 h4Rx9d Midnight Club 2 (PS2) f -689 dh2003 hT40y1 Deerhunter 2003 f -690 hwbasharena CSBQMI Hot Wheels Bash Arena f -691 robotarena2 h4Yc9D Robot Arena 2 f -692 monopoly3 vPkKya Monopoly 3 f -694 painkiller k7F4d2 Painkiller f -695 revolution G1h3m2 Revolution f -696 ddozenptd G7b3Si Deadly Dozen Pacific Theater Demo f -697 ironstormd h9D3Li Iron Storm Demo f -698 strikefighters1 PwZFex Strike Fighters: Project f -699 moo3 g4J72d Master of Orion III f -700 suddenstrike2 Iq0wWE Sudden Strike II f -703 projectigi2 j4F9cY IGI 2: Covert Strike Demo f -704 realwarrsd 5jAGh7 Real War: Rogue States Demo f -705 pnomads FexS6a Project Nomads f -707 strongholdcd kAGh6n Stronghold: Crusader Demo f -708 blitzkrieg fYDXBN Blitzkrieg f -709 woosc Y4nD9a World of Outlaws Sprint Cars f -710 vietcongd bq98mE Vietcong Demo f -711 hlwarriors H5rW9v Highland Warriors f -712 mohaasd 2vPkJy Medal of Honor: Allied As f -714 horserace y4fR91 HorseRace f -647 fileplanet \N FilePlanet.com f -722 worms3 fZDYBO Worms 3D f -717 sandbags wXEX3r Sandbags and Bunkers f -718 crttestdead 111111 CRT - TEST f -719 nolf2 g3Fo6x No One Lives Forever 2 f -720 wkingsb agV5Hm Warrior Kings Battles f -721 riseofnations H3kC6s Rise of Nations f -723 castles 31zdyb Castles and Catapluts f -725 orbb Ykd2D3 O.R.B: Off-World Resource Base Beta f -726 echelonwwd ORp4jG Echelon Wind Warriors Demo f -728 snooker2003 ZIq1wX Snooker 2003 f -729 jeopardyps2 t9iK4V Jeopardy (PS2) f -730 riskps2 Hg3u2X Risk (PS2) f -731 wofps2 dF39h3 Wheel of Fortune (PS2) f -733 trivialppc c45S8i Trivial Pursuit (PC) US f -734 trivialpps2 h3U6d9 Trivial Pursuit (PS2) f -735 projectigi2d j4F9cY IGI 2: Covert Strike Demo f -736 projectigi2r j4F9cY IGI 2 Covert Strike f -739 wooscd Y4nD9a World of Outlaws Sprint Cars Demo f -740 nthunder2004 g3J7sp NASCAR Thunder 2004 f -741 f1comp g7W1P8 F1 1999-2000 Compilation f -742 nomansland DLziQw No Mans Land f -743 nwnxp1 ZIq1wW Neverwinter Nights: Shado f -744 praetoriansd EX3rAJ Praetorians Demo f -745 nrs2003 f3RdU7 NASCAR Racing Season 2003 f -746 gmtestam HA6zkS test (Auto-Matchmaking) f -748 devastation b6Eo3S Devastation f -749 blitz2004ps2 w3Rk7F NFL Blitz 2004 (PS2) f -750 hd2 sK8pQ9 Hidden and Dangerous 2 f -751 hd2b T1sU7v Hidden and Dangerous 2 Beta f -754 hd2d sT3p2k Hidden and Dangerous 2 Demo f -755 mrpantsqm g3R2ii Mr. Pants QuickMatch f -756 moutlawne 4o2uPk Midnight Outlaw Nitro f -758 lionheart h5R3cp Lionheart f -759 medievalvi w5R39i Medieval Total War Viking Invasion f -760 black9pc h2F9cv Black9 (PC) f -761 black9ps2 w3D8gY Black9 (PS2) f -762 cmanager3 T3d8yH Cycling Manager 3 f -764 devastationd y3Fk8c Devastation Demo f -765 hitz2004ps2 t3E8Fc NHL Hitz 2004 PS2 f -767 chaserd 3R5fi7 Chaser Demo f -768 motogp2 y3R2j7 MotoGP 2 f -769 motogp2d y3R5d1 MotoGP 2 Demo f -770 racedriverd P4f3Hw Race Driver Demo f -771 empiresam GknAbg Empires Dawn of the Modern World (AM) f -772 empires GknAbg Empires: Dawn of the Modern World f -773 crashnitro 3E8fT5 Crash Nitro Carts f -774 breed t3Fw7r Breed f -775 breedd u7Gc92 Breed Demo f -777 moo3a g4J72d Master of Orion III f -778 nwnmac Adv39k Neverwinter Nights (Mac) f -779 ravenshield csFbq9 Raven Shield f -781 spacepod 8cvRTO SpacePod f -782 agrome 8mEKiP Against Rome f -783 bfield1942sw HpWx9z Battlefield 1942: Secret Weapons of WW2 f -784 thps4pc L3C8s9 Tony Hawk: Pro Skater 4 (PC) f -785 omfbattled Abm93d One Must Fall Battlergounds Demo f -786 nwnlinux Adv39k Neverwinter Nights (Linux) f -787 blitz2004ps2e t3Fg7C NFL Blitz Pro 2004 E3 (PS2) f -789 homeworld2b t3Fd7j Homeworld 2 Beta f -790 halo QW88cv Halo Beta f -791 lotr3 y2Sc6h Lords of the Realm III f -792 lotr3b y2Sc6h Lords of the Realm III Beta f -793 halor e4Rd9J Halo: Combat Evolved f -794 bllrs2004ps2 t3w6k8 NBA Ballers (PS2) f -795 rtcwett t3R7dF Wolfenstein: Enemy Territory Test f -797 jacknick6 q7zgsC Jack Nicklaus Golden Bear f -798 wotr e8Fc3n War of the Ring f -799 terminator3 y3Fq8v Terminator 3 f -800 fwarriorpc n2X8ft Fire Warrior f -801 fwarriorps2 r3D7s9 Fire Warrior (PS2) f -803 aow3 W88dvR Age of Wonders: Shadow Magic (aow3) f -804 E3_2003 jvaLXV E3_2003 f -805 aowsm W78cvR Age of Wonders: Shadow Magic (aowsm) f -806 specialforces V4f02S Special Forces f -807 spartan GjuMct Spartan & Spartan f -808 dod Hm31yd Day of Defeat f -809 tron20d t9D3vB TRON 2.0 Demo f -811 bfield1942swd r3Yjs8 Battlefield 1942: Secret Weapons of WW2 Demo f -813 rtcwet jpvbuP Wolfenstein: Enemy Territory f -814 mphearts vStJNr mphearts f -815 hotrod Tg4so9 Hot Rod, American Street Drag f -816 civ3con h4D8Wc Civilization III: Conquests f -817 civ3conb g3e9J1 Civilization III: Conquests Beta f -818 riseofnationsam H3kC6s Rise of Nations Auto-Matching f -819 afrikakorps tbhWCq Afrika Korps f -820 apocalypticadi T3d8x7 Apocalyptica f -821 robotech2 w3D2Yb Robotech 2 (PS2) f -823 ccgenzh D6s9k3 Command & Conquer: Generals – Zero Hour f -824 ronb H3kC6s Rise of Nations Beta f -825 ronbam H3kC6s Rise of Nations Beta (Automatch) f -826 commandos3 uukfJz Commandos 3 f -828 dh2004 E8j4fP Deer Hunter 2004 f -830 dh2004d E8j4fP Deer Hunter 2004 Demo f -831 armygamemac g3sR2b Americas Army: Special Forces (Mac) f -832 bridgebaron14 hd3Y2o Bridge Baron f -834 anno1503b mEcJMZ Anno 1503 Beta f -835 contractjack h3K8f2 Contract Jack f -836 postal2 yw3R9c Postal 2 f -837 ut2004 y5rP9m Unreal Tournament 2004 f -838 ut2004d y5rP9m Unreal Tournament 2004 Demo f -839 contractjackd U3k2f8 Contract Jack Demo f -1104 regimentps2 u6qPE9 The Regiment PS2 f -948 eearth2 h3C2jU Empire Earth 2 f -976 wormsforts y3Gc8n Worms Forts: Under Siege f -842 mtgbgrounds y3Fs8K Magic The Gathering: Battlegrounds f -843 groundcontrol2 L3f2X8 Ground Control 2 f -844 bfield1942ps2 HpWx9z Battlefield Modern Combat (PS2) f -845 dsiege2 tE42u7 Dungeon Siege 2 The Azunite Prophecies f -846 judgedredddi t3D7Bz Judge Dredd f -847 coldwinter W9f5Cb1 Cold Winter f -848 haegemoniaxp LiQwZF Hegemonia Expansion f -850 castlestrike GPcglz Castle Strike f -851 homeworld2d t38kc9 Homeworld 2 Demo f -852 callofduty K3dV7n Call of Duty f -853 mohaabd y32FDc Medal of Honor: Allied Assault Breakthrough Demo f -854 twc2 PYxfvt Takeout Weight Curling 2 f -855 nthunder2004d g3J7sp NASCAR Thunder 2004 Demo f -857 mta Y4f9Jb Multi Theft Auto f -860 spellforce T8g3Ck Spellforce f -861 halomac e4Rd9J Halo (Mac) f -862 contractjackpr U3k2f8 Contract Jack PR f -864 wotrb e8Fc3n War of the Ring Beta f -865 halod yG3d9w Halo Demo f -866 wcpool2004ps2 g3J7w2 World Championship Pool 2004 (PS2) f -867 fairstrike y4Ks2n Fair Strike f -868 aarts tR3b8h Axis and Allies RTS f -870 lotrbme h3D7Lc Lord of the Rings: The Battle For Middle-Earth f -871 mototrax T2g9dX Moto Trax f -872 painkillerd k7F4d2 Painkiller Demo f -873 painkillert k7F4d2 Painkiller Multiplayer Test f -874 entente LqrTlG The Entente f -876 sforces V4f02S Special Forces f -877 slugfestps2 e8Cs3L Slugfest Pro (PS2) f -879 battlemages ZMWyOO Battle Mages f -880 bfvietnam h2P9dJ Battlefield: Vietnam f -881 planetside yQzrrQ PlanetSide f -882 daoc TkAksf Dark Age of Camelot f -883 uotd CpJvsG Ultima Online Third Dawn f -884 swg wICOeH Star Wars Galaxies f -885 eq AnoMKT Everquest f -887 serioussamps2 yG3L9f Serious Sam (PS2) f -888 omfbattlecp Abm93d One Must Fall Battlegrounds (GMX) f -889 fairstriked y4Ks2n Fair Strike Demo f -890 celtickingspu WxaKUc Nemesis of the Roman Empire f -891 test Hku6Fd Test f -892 truecrime G8d3R5 True Crime f -896 links2004 jG3d9Y Links 2004 f -897 terminator3d y3Fq8v Terminator 3 Demo f -900 wcpool2004pc ypQJss World Championship Pool 2004 f -901 postal2d yw3R9c Postal 2 Demo f -903 spellforced T8g3Ck Spellforce Demo f -904 le_projectx t3F9vY Legend Entertainment Project X f -905 racedriver2 UEzIlg Race Driver 2 f -906 bomberfunt bbeBZG BomberFUN Tournament f -907 pbfqm g3R2ii PlanetBattlefield QuickMatch f -908 gangland y6F39x Gangland f -910 juicedpc g3J8df Juiced (PC) f -911 juicedps2 g3J8df Juiced (PS2) f -913 tribesv y3D28k Tribes Vengeance f -914 racedriver2ps2 n5oS9f Race Driver 2 (PS2) f -916 indycarps2 L4H7f9 Indycar Series (PS2) f -917 thps6ps2 3Rc9Km Tony Hawks Underground 2 (PS2) f -918 sniperelps2 f3Tk3s Sniper Elite (PS2) f -920 bllrs2004ps2d t3w6k8 NBA Ballers Demo (PS2) f -921 saturdayns psZhzd Saturday Night Speedway f -922 rometw s8L3v0 Rome: Total War f -924 rontp H3kC6s Rise of Nations: Throne and Patriots f -925 rontpam H3kC6s Rise of Nations: Throne and Patriots (Automatch) f -926 dmhand YJxLbV Dead Man Hand f -927 upwords itaKPh upwords f -929 scrabbledel mZfoBF Scrabble Deluxe f -930 dsiege2am tE42u7 Dungeon Siege 2 The Azunite Prophecies (Automatch) f -931 cmr4pc t3F9f1 Colin McRae Rally 4 (PC) f -932 kumawar y3G9dE Kuma War f -933 cmr4pcd t3F9f1 Colin McRae Rally 4 Demo (PC) f -940 crashnburnps2 gj7F3p Crash N Burn (PS2) f -941 spartand JdQvnt Spartan Demo f -942 ace L2dC9x A.C.E. f -944 perimeter FRYbdA Perimeter f -945 ilrosso Y3f9Jn Il Rosso e Il Nero - The Italian Civil War f -946 whammer40000 uJ8d3N Warhammer 40,000: Dawn of War f -947 swat4 tG3j8c SWAT 4 f -949 tribesvd y3D28k Tribes Vengeance Demo f -950 tribesvb y3D28k Tribes Vengeance Beta f -952 sniperelpc hP58dm Sniper Elite (PC) f -954 altitude DZzvoR Altitude f -955 fsx y3Fd8H Flight Simulator 2006 f -956 hotwheels2pc u3Fx9h Hot Wheels 2 (PC) f -958 hotwheels2pcd u3Fx9h Hot Wheels 2 Demo (PC) f -959 cnpanzers h3Tod8 Codename Panzers f -960 gamepopulator h3Ks61 Game Populator f -961 gamepopulatoram h3Ks61 Game Populator (Automatch) f -963 livewire wuyvAa GameSpy Livewire f -965 fear n3V8cj FEAR: First Encounter Assault Recon f -966 tron20mac t9D3vB TRON 2.0 (Mac) f -967 s_cstrikecz izVsOs Steam Counter-Strike: Condition Zero f -968 wingsofwar sWSqHB Wings of War f -969 mxun05ps2 u3Fs9n MX Unleashed 05 (PS2) f -971 swbfrontps2 y3Hd2d Star Wars: Battlefront (PS2, Japan) f -973 swbfrontpc y3Hd2d Star Wars: Battlefront (PC) f -974 perimeterd FRYbdA Perimeter Demo f -975 wracing1 t3Hs27 World Racing 1 f -977 mohaamac M5Fdwc Medal of Honor: Allied Assault (Mac) f -1222 motogp3d U3ld8j MotoGP 3 Demo f -1009 fswpc R5pZ29 Full Spectrum Warrior f -1038 conflictsopc vh398A Conflict: Special Ops (PC) f -1042 eearth2d h3C2jU Empire Earth 2 Demo f -1101 civ4 y3D9Hw Civilization IV f -1102 civ4am y3D9Hw Civilization IV (Automatch) f -979 mohaabmac y32FDc Medal of Honor: Breakthrough (Mac) f -980 bfield1942mac HpWx9z Battlefield 1942 (Mac) f -982 halom e4Rd9J Halo Multiplayer Expansion f -983 nitrofamily t3Jw2c Nitro Family f -984 besieger ydG3vz Besieger f -986 mkdeceptionps2 2s9Jc4 Mortal Kombat Deceptions (PS2) f -987 swrcommando y2s8Fh Star Wars: Republic Commando f -988 fightclubps2 t3d8cK Fight Club (PS2) f -989 area51ps2 eR48fP Area 51 (PS2) f -990 dday B78iLk D-Day f -992 mohaabdm y32FDc Medal of Honor: Allied Assault Breakthrough Demo (Mac) f -993 mkdeceppalps2 2s9Jc4 Mortal Kombat Deception PAL (PS2) f -994 civ4b y3D9Hw Civilization 4 Beta f -995 topspin sItvrS Top Spin f -996 bllrs2004pal t3w6k8 NBA Ballers PAL (PS2) f -999 scrabbleo t2Dfj8 Scrabble Online f -1000 wcsnkr2004ps2 K3f39a World Championship Snooker 2004 (PS2) f -1001 olg2PS2 Yb3pP2 Outlaw Golf 2 PS2 f -1002 gtasaps2 Bn73c9 Grand Theft Auto San Andreas (PS2) f -1003 thps6pc AdLWaZ T.H.U.G. 2 f -1004 smackdnps2 k7cL91 WWE Smackdown vs RAW Sony Beta (PS2) f -1005 thps5pc AdLWaZ Tony Hawks Underground (PC) f -1006 menofvalor h3Fs9c Men of Valor f -1008 gc2demo L3f2X8 Ground Control 2 Demo f -1010 soldiersww2 qdSxsJ Soldiers: Heroes of World War II f -1011 mtxmototrax VKQslt MTX MotoTrax f -1012 pbfqmv 9wk3Lo PlanetBattlefield QuickMatch Vietnam f -1013 wcsnkr2004pc DQZHBr World Championship Snooker 2004 (PC) f -1014 locomotion uTAGyB Chris Sawyer's Locomotion f -1015 gauntletps2 y2Fg39 Gauntlet (PS2) f -1016 gotcha 9s34Pz Gotcha! f -1019 knightsoh 9f5MaL Knights of Honor f -1020 wingsofward sWSqHB Wings of War Demo f -1021 cmr5ps2 hH3Ft8 Colin McRae Rally 5 (PS2) f -1022 callofdutyps2 tR32nC Call of Duty (PS2) f -1024 hotrod2 AaP95r Hot Rod 2: Garage to Glory f -1025 mclub3ps2 g7J2cX Midnight Club 3 DUB Edition (PS2) f -1027 trivialppalps2 h3U6d9 Trivial Pursuit PAL (PS2) f -1028 trivialppalpc c45S8i Trivial Pursuit PAL (PC) f -1029 hd2ss k3Ljf9 Hidden & Dangerous 2 - Sabre Squadron f -1030 whammer40kb uJ8d3N Warhammer 40,000: Dawn of War Beta f -1032 srsyndpc A9Lkq1 Street Racing Syndicate (PC) f -1033 ddayd B78iLk D-Day Demo f -1034 godzilla2ps2 bi9Wz4 Godzilla: Save the Earth (PS2) f -1035 actofwar LaR21n Act of War: Direct Action f -1037 statesmen j8K3l0 Statesmen f -1039 conflictsops2 vh398A Conflict: Special Ops (PS2) f -1040 dh2005 qW56m4 Deer Hunter 2005 f -1041 gotchad 9s34Pz Gotcha! Demo f -1043 smackdnps2pal k7cL91 WWE Smackdown vs RAW PAL (PS2) f -1044 wcpokerps2 t3Hd9q World Championship Poker (PS2) f -1045 cmr5pc hH3Ft8 Colin McRae Rally 5 (PC) f -1046 dh2005d qW56m4 Deer Hunter 2005 Demo f -1049 doom3 kbeafe Doom 3 f -1050 cmr5pcd hH3Ft8 Colin McRae Rally 5 Demo (PC) f -1051 spoilsofwar nZ2e4T Spoils of War f -1052 saadtest 1a2B3c SaadsTest f -1054 superpower2 yYw43B Super Power 2 f -1055 swat4d tG3j8c SWAT 4 Demo f -1056 exigob mPBHcI Armies of Exigo Beta f -1058 knightsohd 9f5MaL Knights of Honor Demo f -1059 battlefield2 hW6m9a Battlefield 2 f -1060 actofwaram LaR21n Act of War: Direct Action (Automatch) f -1061 bf1942swmac HpWx9z Battlefield 1942: Secret Weapons of WW2 Mac f -1062 closecomftf iLw37m Close Combat: First to Fight f -1064 kohankowd uE4gJ7 Kohan: Kings of War Demo f -1066 swempire t3K2dF Star Wars: Empire at War f -1067 stalkersc t9Fj3M STALKER: Shadows of Chernobyl f -1068 poolshark2ps2 teH26Z Pool Shark 2 (PS2) f -1069 poolshark2pc teH26Z Pool Shark 2 (PC) f -1070 smackdnps2kor k7cL91 WWE Smackdown vs RAW (PS2) Korean f -1071 smackdnps2r k7cL91 WWE Smackdown vs RAW (PS2) Retail f -1073 swbfrontps2p y3Hd2d Star Wars: Battlefront (PS2) f -1074 trivialppcuk c45S8i Trivial Pursuit (PC) UK f -1075 trivialppcfr c45S8i Trivial Pursuit (PC) French f -1076 trivialppcgr c45S8i Trivial Pursuit (PC) German f -1077 trivialppcit c45S8i Trivial Pursuit (PC) Italian f -1078 trivialppcsp c45S8i Trivial Pursuit (PC) Spanish f -1080 aartsd tR3b8h Axis and Allies RTS demo f -1081 blitzkriegrt fYDXBN Blitzkrieg: Rolling Thunder f -1082 dungeonlords 74dBl9 Dungeon Lords f -1083 SpyNote spynot Server Monitor f -1085 blitz2005ps2 uY39vA Blitz: The League 2005 f -1086 rof t5LqW4 Rise of Legends f -1087 rofam t5LqW4 Rise of Legends (Automatch) f -1088 nsr0405 Q6vu91 NASCAR Sim Racing (2005) f -1089 ffvsttr 5tQqw9 Freedom Force vs. The Third Reich f -1092 dshard g3D8Sc The Dragonshard Wars f -1094 exigor mPBHcI Armies of Exigo Retail f -1095 exigoram mPBHcI Armies of Exigo (Automatch) f -1096 bfield1942t HpWx9z Battlefield 1942 Testing f -1099 bfvietnamt h2P9dJ Battlefield: Vietnam Testing f -1103 regimentpc u6qPE9 The Regiment PC f -1036 juicedpalps2 \N Juiced PAL (PS2) f -1195 worms4 Bs28Kl Worms 4 Mayhem f -1200 fsw10hpc 6w2X9m Full Spectrum Warrior: Ten Hammers (PC) f -1206 worms4d Bs28Kl Worms 4 Mayhem Demo f -1224 eearth2xp1 h3C2jU Empire Earth II: The Art of Supremacy f -1106 battlefield2d hW6m9a Battlefield 2 Demo f -1108 fswps2 6w2X9m Full Spectrum Warrior PS2 f -1109 dshardam g3D8Sc The Dragonshard Wars (Automatch) f -1111 source AYcFzB Half Life 2 f -1112 s_cssource EEpacW Counter-Strike Source f -1113 feard n3V8cj FEAR: First Encounter Assault Recon Demo f -1114 s_hl2dm FqmlZJ s_hl2dm f -1115 bfield1942ps2b HpWx9z Battlefield Modern Combat (PS2) Beta f -1116 whammer40kt uJ8d3N Warhammer 40000: Dawn of War test f -1117 firecapbay VJMdlD Fire Captain: Bay Area Inferno f -1118 splintcellchaos UgzOGy splintcellchaos f -1120 fearcb n3V8cj FEAR: First Encounter Assault Recon (Closed Beta) f -1121 fearob n3V8cj FEAR: First Encounter Assault Recon (Open Beta) f -1122 ejammingpc Sd7a9p eJamming Jamming Station PC f -1123 ejammingmac Sd7a9p eJamming Jamming Station MAC (engine) f -1125 titanquest Te3j7S Titan Quest f -1126 wcsnkr2005ps2 cPw49v World Championship Snooker 2005 PS2 f -1127 wcsnkr2005 cPw49v World Championship Snooker 2005 (PC) f -1128 thps7ps2 y3L9Cw Tony Hawks American Wasteland (PS2) f -1129 pariahpc D3Kcm4 Pariah (PC) f -1130 impglory eCYHgP Imperial Glory f -1133 oltps2 cH92pQ Outlaw Tennis PS2 f -1134 wptps2 jL2aEz World Poker Tour PS2 f -1135 blkhwkdnps2 7wM8sZ Delta Force: Black Hawk Down (PS2) f -1137 motogp3 lelcPr MotoGP 3 f -1138 cmmwcpoker iRU92a Chris Moneymakers World Championship Poker f -1139 ddayxp1 B78iLk D-Day: 1944 Battle of the Bulge f -1140 spcell3coop QdVGhj Splinter Cell 3 CoOp f -1142 ffvsttrd 5tQqw9 Freedom Force vs. The Third Reich MP Demo f -1143 topspinps2 sItvrS Top Spin (PS2) f -1144 betonsoldier mH2y9u Bet on Soldier f -1146 topspinps2am sItvrS Top Spin (PS2) (Automatch) f -1147 vietcong2 zX2pq6 Vietcong 2 f -1148 spyvsspyps2 y3F7Gh Spy vs Spy (PS2) f -1149 nitrosample abcdef Nitro Sample f -1150 flatoutps2 ms83Ha Flat Out (PS2) f -1151 hotpacificps2 yB7qfv Heroes of the Pacific (PS2) f -1152 hotpacificpc yB7qfv Heroes of the Pacific (PC) f -1154 cnpanzers2 h3Tod8 Codename Panzers Phase 2 f -1155 stronghold2 Lc83Jm Stronghold 2 f -1157 actofwardam LaR21n Act of War: Direct Action Demo (Automatch) f -1158 xmenlegpc 47uQsy X-Men Legends (PC) f -1159 xmenlegps2 47uQsy X-Men Legends (PS2) f -1160 coteagles cEb84M War Front: Turning Point f -1161 area51pc mW73mq Area 51 (PC) f -1164 area51pcb mW73mq Area 51 (PC) Beta f -1169 stalinsubd y3Kc9s The Stalin Subway Demo f -1170 supruler2010 cEuCxb Supreme Ruler 2010 f -1171 pariahpcd D3Kcm4 Pariah Demo (PC) f -1172 serioussam2 8dA9mN Serious Sam 2 (PC) f -1173 riskingdoms K3x9vc Rising Kingdoms f -1176 stalinsub HOqpUo The Stalin Subway f -1177 bsmidwaypc qY84Ne Battlestations Midway (PC) f -1179 bsmidwaypcam qY84Ne Battlestations Midway (PC) (Automatch) f -1180 bsmidwayps2am qY84Ne Battlestations Midway PS2 (Automatch) f -1181 riskingdomsd K3x9vc Rising KIngdoms Demo f -1182 riskingdomsam K3x9vc Rising Kingdoms (Automatch) f -1183 wsoppc u3hK2C World Series of Poker (PC) f -1184 wsopps2 u3hK2C World Series of Poker (PS2) f -1185 velocityps2 Qmx73k Velocity PS2 f -1186 velocitypc Qmx73k Velocity PC f -1188 hotpaceudps2 yB7qfv Heroes of the Pacific EU Demo (PS2) f -1189 hotpacnadps2 yB7qfv Heroes of the Pacific NA Demo (PS2) f -1190 gbrome hEf6s9 Great Battles of Rome f -1191 rafcivatwar h98Sqa Rise And Fall: Civilizations at War f -1193 rafcivatwaram h98Sqa Rise And Fall: Civilizations at War (Automatch) f -1196 smackdn2ps2 JyWnL2 WWE Smackdown vs RAW 2 (PS2) f -1197 smackdn2ps2pal JyWnL2 WWE Smackdown vs RAW 2 PAL (PS2) f -1198 smackdn2ps2kor JyWnL2 WWE Smackdown vs RAW 2 Korea (PS2) f -1199 fsw10hps2 6w2X9m Full Spectrum Warrior: Ten Hammers (PS2) f -1201 fsw10hps2kor 6w2X9m Full Spectrum Warrior: Ten Hammers (Korea, PS2) f -1202 fsw10hps2pal 6w2X9m Full Spectrum Warrior: Ten Hammers (PAL, PS2) f -1203 swbfront2pc hMO2d4 Star Wars Battlefront 2 PC f -1204 swbfront2ps2 y3Hd2d Star Wars Battlefront 2 (PS2) f -1205 swbfront2ps2j hMO2d4 Star Wars Battlefront 2 (PS2) Japanese f -1207 whammer40kwa Ue9v3H Warhammer 40,000: Winter Assault f -1209 codbigredps2 ye4Fd8 Call of Duty 2: Big Red One (PS2) f -1210 dsnattest L74dSk ds nat test f -1212 xmenlegps2pal 47uQsy X-Men Legends PAL (PS2) f -1213 xmenlegps2pals 47uQsy X-Men Legends PAL Spanish (PS2) f -1215 gbromeam hEf6s9 Great Battles of Rome (Automatch) f -1216 pbfqm2 P7RTY8 PlanetBattlefield QuickMatch 2 f -1217 wsopps2am u3hK2C World Series of Poker (PS2) (Automatch) f -1218 wsoppcam u3hK2C World Series of Poker (PC) (Automatch) f -1223 vietcong2d zX2pq6 Vietcong 2 Demo f -1226 fordvchevyps2 i79DwE Ford Versus Chevy (PS2) f -1227 hotpacificpcd yB7qfv Heroes of the Pacific PC Demo f -1228 hoodzps2 f6eP9w Hoodz (PS2) f -1229 swbfront2pcb hMO2d4 Star Wars Battlefront 2 PC Beta f -1230 swbfront2pcd hMO2d4 Star Wars Battlefront 2 PC Demo f -1233 fswps2jp 6w2X9m Full Spectrum Warrior (PS2, Japanese) f -1234 and1sballps2 J3c8Dm AND1: Streetball Online (PS2) f -1238 mariokartds yeJ3x8 Mario Kart (DS) f -1239 genetrooperpc eK4Xh7 Gene Trooper (PC) f -1240 genetrooperps2 eK4Xh7 Gene Troopers (PS2) f -1241 legionarena Gd4v8j Legion Arena f -1242 kott2pc p3iWmL Knights of the Temple 2 (PC) f -1243 kott2ps2 p3iWmL Knights of the Temple 2 (PS2) f -1244 hardtruck PGWCwm Hard Truck Tycoon f -1245 wracing2 hY39X0 World Racing 2 (PC) f -1246 wsoppsp u3hK2C World Series of Poker (PSP) f -1248 infectedpsp eRq49L Infected (PSP) f -1249 infectedpspam eRq49L Infected (PSP) (Automatch) f -1251 unavailable j39DhU Test for disabled games f -1252 tempunavail 9h1UHk Test for temporarily disabled games f -1253 betonsoldierd mH2y9u Bet On Soldier f -1254 ghpballps2 9tcGVE Greg Hastings Paintball (PS2) f -1255 flatout SxdJel FlatOut f -1257 vietcong2pd zX2pq6 Vietcong 2 Public Demo f -1258 thawds t4Vc7x Tony Hawks American Wasteland (DS) f -1259 acrossingds h2P9x6 Animal Crossing (DS) f -1260 coteaglessp cEb84M War Front: Turning Point (Singleplayer) f -1261 and1sballps2am J3c8Dm AND1: Streetball Online (PS2) (Automatch) f -1262 mariokartdsam yeJ3x8 Mario Kart (DS, Automatch) f -1265 xmenleg2psp g3Hs9C X-Men: Legends 2 (PSP) f -1266 lotrbme2 g3Fd9z Lord of the Rings: The Battle for Middle-earth 2 (Beta) f -1267 shatteredunion t2Gx8g Shattered Union f -1268 serioussam2d 8dA9mN Serious Sam 2 Demo f -1269 bllrs2005ps2 4StbWm NBA Ballers 2005 (PS2) f -1274 racedriver3pcd \N Race Driver 3 Demo (PC) f -1272 mprimeds Dh1PpC Metroid Prime Hunters (DS) f -1273 racedriver3pc BPAfNv Race Driver 3 (PC) f -1278 uchaosrrps2am \N Urban Chaos: Riot Response Automatch (PS2) f -1275 scsdw PohZyA S.C.S. Dangerous Waters f -1277 uchaosrrps2 KPd0V9 Urban Chaos: Riot Response (PS2) f -1281 rdriver3ps2d \N Race Driver 3 Demo (PS2) f -1280 rdriver3ps2 BPAfNv Race Driver 3 (PS2) f -1284 rtrooperpcam \N Rogue Trooper Automatch (PC) f -1282 wptps2pal jL2aEz World Poker Tour PAL (PS2) f -1283 rtrooperpc jK7L92 Rogue Trooper (PC) f -1291 mxun05pcam \N MX vs. ATV Unleashed Automatch (PC) f -1289 dsnattest2 L74dSk ds nat test 2 f -1290 mxun05pc v8XaWc MX vs. ATV Unleashed (PC) f -1310 marvlegps2am \N Marvel Legends Automatch (PS2) f -1292 quake4 ZuZ3hq Quake 4 f -1293 paraworld EUZpQF ParaWorld f -1294 paraworldam EUZpQF ParaWorld Automatch f -1295 paraworldd EUZpQF ParaWorld Demo f -1296 callofduty2 DSpIxw Call of Duty 2 f -1298 slugfest06ps2 e8Cs3L Slugfest '06 (PS2) f -1299 bleachds 5BuVRR Bleach (DS) f -1300 lostmagicds eI0Rml Lost Magic (DS) f -1301 wofor mxw9Nu WOFOR: War on Terror f -1302 woforam mxw9Nu WOFOR: War on Terror Automatch f -1303 woford mxw9Nu WOFOR: War on Terror Demo f -1306 Happinuds DqO198 Happinuvectorone! (DS) f -1307 thawpc v8la4w Tony Hawk's American Wasteland (PC) f -1308 ysstrategyds gq2bHQ Y's Strategy (DS) f -1309 marvlegps2 eAMh9M Marvel Legends (PS2) f -1312 marvlegpspam \N Marvel Legends Automatch (PSP, PAL) f -1311 marvlegpsp eAMh9M Marvel Legends (PSP, PAL) f -1314 marvlegpcam \N Marvel Legends Automatch (PC) f -1313 marvlegpc eAMh9M Marvel Legends (PC) f -1315 marvlegpcd \N Marvel Legends Demo (PC) f -1318 hustleps2am \N Hustle: Detroit Streets Automatch (PS2) f -1317 hustleps2 ni9hdV Hustle: Detroit Streets (PS2) f -1342 ffurtdriftps2am \N The Fast and the Furious: Tokyo Drift Automatch (PS2) f -1320 koshien2ds UKdPFf PowerPro Pocket Koshien 2 (DS) f -1321 lotrbme2r g3Fd9z Lord of the Rings: The Battle for Middle-earth 2 f -1322 tenchuds dfOICS Tenchu (DS) f -1323 contactds quPooS Contact JPN (DS) f -1324 stella flfRQv Battlefield 2142 f -1325 stellad UoiZSm Battlefield 2142 (Demo) f -1327 tetrisds JJlSi8 Tetris DS (DS) f -1328 motogp4ps2 OCNxy3 MotoGP 4 (PS2) f -1329 actofwarht LaR21n Act of War: High Treason f -1330 actofwarhtam LaR21n Act of War: High Treason Automatch f -1331 actofwarhtd LaR21n Act of War: High Treason Demo f -1333 Customrobods MH0EK4 Custom Robo DS (DS) f -1334 comrade F72JWS Comrade f -1335 greconawf Fn5GLL Ghost Recon: Advanced Warfighter f -1336 greconawfd Fn5GLL Ghost Recon: Advanced Warfighter Demo f -1337 asobids 1L77RN Asobi Taizen (DS) f -1338 timeshift rHKFnV TimeShift (PC) f -1339 timeshiftb rHKFnV TimeShift Beta (PC) f -1341 ffurtdriftps2 Bso8LK The Fast and the Furious: Tokyo Drift (PS2) f -1344 pokemondpds 1vTlwb Pokemon Diamond-Pearl (DS) f -1345 coteaglesam cEb84M War Front: Turning Point Automatch f -1346 facesofwar Shp95z Faces of War f -1347 facesofwaram Shp95z Faces of War Automatch f -1348 facesofward Shp95z Faces of War Demo f -1349 bombermanslds 9dG7KP Bomberman Story/Land DS f -1350 fherjwkk RADpDr Namco Test f -1352 digistoryds n5t4VH Digimon Story (DS) f -1353 touchpanicds zHToa5 Touch Panic (DS) f -1354 SampAppTest 38u7Te Sample App Developement f -1270 bllrs2005ps2d \N NBA Ballers 2005 Demo (PS2) f -1387 civ4wrld oQ3v8V Civilization IV: Warlords f -1388 civ4wrldam oQ3v8V Civilization IV: Warlords Automatch f -1401 blkhwkdntsps2 \N Delta Force: Black Hawk Down - Team Sabre (PS2) f -1420 flatout2pc GtGLyx FlatOut 2 (PC) f -1434 anno1701 Xa6zS3 Anno 1701 f -1464 crysis ZvZDcL Crysis (PC) f -1465 crysisd ZvZDcL Crysis Demo f -1355 SampAppTestam 38u7Te Sample App Developement Automatch f -1358 fearxp1 n3V8cj FEAR: Extraction Point f -1377 marvlegps2pam \N Marvel Legends Automatch PAL (PS2) f -1361 redorchestra 6md8c4 Red Orchestra Ostfront f -1362 airwingsds 5TTmMf Air Wings (DS) f -1363 openseasonds MpxbPX OpenSeason DS (DS) f -1364 mageknight IZNkpb Mage Knight Apocalypse f -1366 mageknightd IZNkpb Mage Knight Apocalypse Demo f -1367 starfoxds RR7XGH Starfox DS (DS) f -1369 medieval2 G23p7l Medieval 2 Total War f -1370 medieval2am G23p7l Medieval 2 Total War Automatch f -1371 taisends SNyrMR Sangokushi Taisen DS (DS) f -1372 mkarmps2 VZvp7J Mortal Kombat: Armageddon (PS2) f -1373 thps3pcr KsE3a2 Tony Hawk 3 PC (Rerelease) f -1375 ffantasy3ds 6cidXe Final Fantasy III (DS) f -1376 marvlegps2p eAMh9M Marvel Legends PAL (PS2) f -1415 civ4jpam \N Civiliation IV Automatch (Japanese) f -1378 c5 uQCWJs Conflict: Denied Ops f -1379 rfberlin 3vhvcH Rush for Berlin f -1380 swat4xp1_tmp tG3j8c SWAT 4: The Stetchkov Syndicate Temp f -1381 swordots Z5gR9Z Sword of the Stars f -1382 mahjongkcds eBtrQN Mah-Jong Kakuto Club (DS) f -1386 Nushizurids nKShDp Nushizuri DS Yama no megumi Kawa no seseragi f -1389 dsiege2bw A3GXsW Dungeon Siege II: Broken World f -1390 blic2007 X2P8Th Brian Lara International Cricket 2007 f -1391 nwn2 wstKNe NeverWinter Nights 2 f -1392 pssake H8s0Pw Professional Services Sake Test f -1393 gmtestcd HA6zkS Test (Chat CD Key validation) f -1395 yugiohgx2ds 1A1iB2 Yu-Gi-OH! Duel Monsters GX2 (DS) f -1396 whammermoc rnbkJp Warhammer: Mark of Chaos f -1397 whammermocam rnbkJp Warhammer: Mark of Chaos Automatch f -1398 whammermocd rnbkJp Warhammer: Mark of Chaos Demo f -1399 flatout2ps2 VL6s2n FlatOut 2 (PS2) f -1400 cruciform TgsP47 Genesis Rising: The Universal Crusade f -1402 socelevends IwZXVX World Soccer Winning Eleven DS (DS) f -1403 konductrads odc8Ps Konductra (DS) f -1404 strongholdl LXkm3b Stronghold Legends f -1406 wsc2007ps3 m2bcKK World Snooker Championship 2007 (PS3) f -1407 ninsake TpSP5q Nintendo Sake Test f -1408 dwctest d4q9GZ DWC NintendoTest App f -1409 FieldOps AK8zHT Field Ops f -1410 wcpoker2pc t3Hd9q World Championship Poker 2 (PC) f -1411 whammer40kdc Ue9v3H Warhammer 40,000: Dark Crusade f -1413 fullautops3 kC5tJA Full Auto 2: Battlelines (PS3) f -1414 civ4jp y3D9Hw Civiliation IV (Japanese) f -1418 thps4pcram \N Tony Hawk: Pro Skater 4 Automatch (PC) Rerelease f -1416 contactusds pEldCc Contact US (DS) f -1417 thps4pcr L3C8s9 Tony Hawk: Pro Skater 4 (PC) Rerelease f -1436 civ4ruam \N Civiliation IV Automatch (Russian) f -1419 bf2ddostest hW6m9a Battlefield 2 DDoS testing f -1422 cc3tibwars E4F3HB Command & Conquer 3: Tiberium Wars f -1423 topspin2pc tTp6Pn Top Spin 2 (PC) f -1424 thdhilljamds 6gJBca Tony Hawk's Downhill Jam (DS) f -1425 aoex JafcLp Age of Empires Expansion f -1427 rafcivatwartam h98Sqa Rise And Fall: Civilizations at War Test Automatch f -1428 bokujomonods mM94Uc Bokujo Monogatari DS2: Wish-ComeTrue Island (DS) f -1429 tothrainbowds lA7Urd TOTH Rainbow Trail of Light (DS) f -1430 mkarmpalps2 VZvp7J Mortal Kombat: Armageddon PAL (PS2) f -1431 preyd 75rDsD Prey Demo f -1432 prey znghVS Prey f -1435 civ4ru y3D9Hw Civiliation IV (Russian) f -1438 civ4cham \N Civiliation IV Automatch (Chinese) f -1437 civ4ch y3D9Hw Civiliation IV (Chinese) f -1439 cricket2007 ABiuJy Brian Lara International Cricket 2007 f -1440 eternalforces xQEvFD Eternal Forces Demo f -1441 eternalforcesam xQEvFD Eternal Forces Automatch f -1442 eforcesr xQEvFD Eternal Forces f -1444 ptacticsds Wcs0GP Panzer Tactics (DS) f -1445 tankbeatds LxDL6t Tank Beat (JPN) (DS) f -1446 mdungeonds KqfKOx Mysterious Dungeon: Shiren the Wanderer DS (DS) f -1447 dqmonjokerds 5dOqvD Dragon Quest Monsters: Joker (DS) f -1449 oishiids mpmTyO Oishii Recipe (DS) f -1450 stlegacy x9qTsK Star Trek: Legacy f -1451 NN2Simple FTmNOH NatNeg2 Simple Test f -1452 yakumands dNte7R Yakuman DS (DS) f -1453 marveltcardds GkWfL7 Marvel Trading Card Game (DS) f -1454 ffantasy3usds 6Ta8ww Final Fantasy III - US (DS) f -1457 testdriveu P5eUD8 Test Drive Unlimited (Unused) f -1458 test071806 7bxOC2 Test f -1459 chocobombds FP75Oy Chocobo & Magic Book (DS) f -1460 puyopuyods 9bx1UP Puyo Puyo! (DS) f -1462 luckystar2ds BFxkaz Lucky Star 2 (DS) f -1463 lotrbme2wk g3Fd9z Lord of the Rings: The Battle for Middle-earth 2 (Rise of the Witch-King Expansion Pack) f -1466 monsterfarmds Qhcw9n Monster Farm DS (DS) f -1467 naruto5ds TZlbyZ NARUTO: Saikyou Ninja Daikesshuu 5 (DS) f -1468 picrossds 5TLWnF Picross (DS) f -1469 wh40kp uJ8d3N Warhammer 40,000: Dawn of War Patch f -1360 digistorydsam \N Digimon Story Automatch (DS) f -1516 civ4mac CWiCbk Civilization IV (MAC) f -1518 civ4wrldmac QtCpWE Civilization IV: Warlords (MAC) f -1528 anno1701d taEf7n Anno 1701 Demo f -1530 civ4wrldjp oQ3v8V Civilization IV: Warlords (Japan) f -1532 civ4wrldcn oQ3v8V Civilization IV: Warlords (Chinese) f -1563 greconawf2 qvOwuX Ghost Recon: Advanced Warfighter 2 f -1471 digiwrldds MLh2Hn Digimon World DS (DS) f -1472 pandeponds y0zg9C Panel De Pon DS (DS) f -1473 moritashogids rqz1dU Morita Shogi DS (DS) f -1475 lithdev vFQNfR Monolith Development f -1476 lithdevam vFQNfR Monolith Development Automatch f -1477 bf2142 FIlaPo Battlefield 2142 f -1478 bf2142b sdbMvQ Battlefield 2142 (Beta) f -1479 marvlegps3 eAMh9M Marvel Legends (PS3) f -1507 whtacticspspam \N Warhammer 40,000: Tactics Automatch (PSP) f -1481 marvlegps3p eAMh9M Marvel Legends PAL (PS3) f -1483 paradisecity TCD6mz Paradise City f -1484 whammermocdam rnbkJp Warhammer: Mark of Chaos Demo Automatch f -1513 marvlegnpspam \N Marvel Legends Automatch (PSP, NTSC) f -1488 tqexp1 ZCe2KH Titan Quest: Immortal Throne f -1489 tqexp1am ZCe2KH Titan Quest: Immortal Throne (Automatch) f -1492 marveltcard GkWfL7 Marvel Trading Card Game (PC & PSP) f -1493 marveltcardps GkWfL7 Marvel Trading Card Game (PSP) f -1495 prismgs 3Zxgne PRISM: Guard Shield f -1496 prismgsd 3Zxgne PRISM: Guard Shield Demo f -1497 testdriveub BeTTbz Test Drive Unlimited f -1498 testdriveud xueb6u Test Drive Unlimited Demo f -1499 swempirexp1 2WLab8 Star Wars: Empire at War - Forces of Corruption f -1500 dsakurads Sw2t7F Dragon Sakura DS (DS) f -1501 gopetsvids DQ5xwk GoPets: Vacation Island (DS) f -1502 kurikinds 2ZaR1q Kurikin (DS) f -1503 codedarmspsp E7Emxp Coded Arms (PSP) f -1505 wiinat 4Fuy9m Wii NAT Negotiation Testing f -1506 whtacticspsp KcKyRP Warhammer 40,000: Tactics (PSP) f -1517 civ4macam \N Civilization IV Automatch (MAC) f -1508 armedass peprUy ArmA f -1509 ffcryschronds z9WMZJ Final Fantasy: Crystal Chronicles - Ring of Fate (DS) f -1510 preystarsds NrSO9m Prey The Stars (DS) f -1512 marvlegnpsp eAMh9M Marvel Legends (PSP, NTSC) f -1519 civ4wrldmacam \N Civilization IV: Warlords Automatch (MAC) f -1514 spartaaw 09mczM Sparta: Ancient Wars f -1515 spartaawd 09mczM Sparta: Ancient Wars Demo f -1524 eearth3d \N Empire Earth III Demo f -1531 civ4wrldjpam \N Civilization IV: Warlords Automatch (Japan) f -1520 pokebattlewii TzRgSc Pokemon Battle Revolution (Wii) f -1521 puzquestds nW1e6h Puzzle Quest: Challenge of the Warlords (DS) f -1522 doraemonds P6nKJz Doraemon Nobita no Shinmakai Daiboken DS (DS) f -1523 eearth3 Fk6hTz Empire Earth III f -1533 civ4wrldcnam \N Civilization IV: Warlords Automatch (Chinese) f -1527 bf2142d UoiZSm Battlefield 2142 Demo f -1529 medieval2d yVjUSz Medieval II Demo f -1546 fullautops3d \N Full Auto 2: Battlelines Demo (PS3) f -1578 swempiremacam \N Star Wars: Empire at War Automatch (Mac) f -1534 whammer40ktds 9Tkwd4 Warhammer 40,000 Tactics (DS) f -1535 mukoubuchids 2yK3lC Mukoubuchi - Goburei, Shuryoudesune (DS) f -1536 cusrobousds pO5zuq Gekitoh! Custom Robo (DS) (US) f -1537 kurikurimixds Q25SLf Kuri Kuri Mix DS (DS) f -1538 custoboeuds hZCuTq Custom Robo (EU) (DS) f -1539 whammermoct rnbkJp Warhammer: Mark of Chaos Test f -1540 whammermoctam rnbkJp Warhammer: Mark of Chaos Test Automatch f -1541 sweawfocd qafBXM Forces of Corruption Demo f -1542 aoe3wcd ZDdpQV Age of Empires 3: The Warchiefs Demo f -1543 tolmamods sLFfpP Tolmamo (DS) f -1544 patchtest BlIpIG Patching Test f -1580 marvlegjpps3am \N Marvel Legends Automatch (PS3, Japan) f -1547 themark C69nBX The Mark f -1548 themarkam C69nBX The Mark Automatch f -1549 bf2142e flfRQv Battlefield 2142 (EAD) f -1550 supcommb pPhzeh Supreme Commander (Beta) f -1551 dow_dc RUgBVL Dawn of War: Dark Crusade f -1552 fuusuibands Gu3FCH Fuusuiban (DS) f -1554 s_darkmmm ggXhvY Dark Messiah of Might and Magic f -1555 ppirates VGFzVf Puzzle Pirates f -1556 mschargedwii B4LdGW Mario Strikers Charged (Wii) f -1557 8ballstarsds b6WiRo 8-Ball Allstars (DS) f -1558 tmntds XZr5Nr Teenage Mutant Ninja Turtles (DS) f -1559 surfsupds vTS4gO Surf's Up (DS) f -1560 elemonsterds 26pNrL Elemental Monster (DS) f -1562 picrossEUds 1rAhgD Picross (EU) (DS) f -1564 greconawf2am qvOwuX Ghost Recon: Advanced Warfighter 2 Automatch f -1565 greconawf2d qvOwuX Ghost Recon: Advanced Warfighter 2 Demo f -1566 yugiohWC07ds QgkE62 Yu-Gi-Oh! Dual Monsters World Championship 2007 (DS) f -1567 tgmasterds cHgbU3 Table Game Master DS (DS) f -1568 batwars2wii XvB2pu Battalion Wars II (Wii) f -1569 Doragureidods x10zDJ Doragureido (DS) f -1570 armedassd peprUy ArmA Demo f -1571 ffantasy3euds 9zRsJF Final Fantasy III - EU (DS) f -1572 rockstardev 1a8bBi Rockstar Development f -1575 mparty1ds rUpE9b Mario Party (DS) f -1576 stalkerscd t9Fj3M STALKER: Shadows of Chernobyl Beta f -1577 swempiremac yTNCrM Star Wars: Empire at War (Mac) f -1579 marvlegjpps3 eAMh9M Marvel Legends (PS3, Japan) f -1480 marvlegps3am \N Marvel Legends Automatch (PS3) f -1640 smrailroadsjpam h32mq8 Sid Meier's Railroads! Japan Automatch f -1581 drmariowii BvQyb2 Dr. Mario (WiiWare) f -1582 springwidgets tQfwTW Spring Widgets f -1584 lotrbfme2 PvzwZF The Rise of The Witch-king f -1585 wmarkofchaos nGmBcN Warhammer Mark of Chaos f -1586 warmonger I1WnEQ Warmonger f -1587 everquest2 vPmJGO EverQuest II f -1588 startreklegacy jMaWnz Star Trek: Legacy f -1590 lozphourds t8RsDb The Legend of Zelda: Phantom Hourglass (DS) f -1591 vanguardbeta TorchQ Vanguard beta f -1592 digisunmoonds 6DPXX9 Digimon Story Sunburst/Moonlight (DS) f -1593 wmarkofchaosd nGmBcN Warhammer Mark of Chaos Demo f -1594 cruciformam TgsP47 Genesis Rising: The Universal Crusade Automatch f -1595 tcghostreconaw wLCSvJ tcghostreconaw f -1596 ghostraw Cybhqm Ghost Recon Advanced Warfighter f -1597 rainbowsixv GUePbj Rainbow Six Vegas f -1598 wmarkofchaosdam nGmBcN Warhammer Mark of Chaos Demo Automatch f -1599 crysisb ZvZDcL Crysis Beta f -1600 scotttest RPzJL7 Scott's test gamename f -1601 rftbomb xpoRNK Rush for the Bomb f -1603 motogp2007am oXCZxz MotoGP 2007 Automatch f -1604 cityofheroes tJnRie City of Heroes f -1605 cityofvl LEJaXZ City of Villains f -1606 titanquestit orNtwo Titan Quest Immortal Throne f -1607 girlsds hKe82J Girls (DS) f -1609 mmessagesds 5wFQve Mixed Messages (DS) f -1610 topanglerwii 2SbZew Top Angler (Wii) f -1611 swbfffpsp fytErP Star Wars Battlefront: Renegade Squadron (PSP) f -1612 facesow qgiNRG Faces of War f -1615 dexplorerds 8mqApN Dungeon Explorer (DS) f -1632 eearth3am \N Empire Earth III Automatch f -1618 civconps3d hn53vx Civilization Revolution Demo (PS3) f -1620 stalkerscb t9Fj3M STALKER: Shadows of Chernobyl Beta (Unused) f -1622 secondlife wpIwVb Second Life f -1623 tdubeta VsReXT Test Drive Unlimited Beta f -1625 bsmidway fLtUIc Battlestations Midway Demo f -1626 etforces GKPQiB Eternal Forces f -1627 genesisrbeta tZRxNP Genesis Rising Beta f -1628 tankbeatusds 8UdAVm Tank Beat (US) (DS) f -1629 champgamesps3 dwg55x High Stakes on the Vegas Strip: Poker Edition (PS3) f -1631 eearth3b Fk6hTz Empire Earth III Beta f -1643 facesofwarxp1am \N Faces of War Standalone Automatch (XP1) f -1633 chocmbeuds O8r2ST Chocobo & Magic Book (EU) (DS) f -1634 supcommdemo plfinb Supreme Commander Demo f -1635 tetriskords KrMqE6 Tetris DS (KOR) (DS) f -1637 wincircleds 2ckCbe Winner's Circle (DS) f -1638 itadakistds 5AesfG Itadaki Street DS (DS) f -1641 megamansfds r2zQPw Mega Man Star Force (US) (DS) f -1642 facesofwarxp1 QQxWKm Faces of War Standalone (XP1) f -1662 roguewarpcam \N Rogue Warrior Automatch (PC) f -1644 timeshiftx rHKFnV TimeShift (Xbox 360) f -1645 timeshiftps3 rHKFnV TimeShift (PS3) f -1647 kingbeetlesds 9mTZtW The King of Beetles Mushiking Super Collection (DS) f -1648 dragonbzwii oHk248 Dragonball Z (Wii) f -1649 atlas_samples Zc0eM6 ATLAS Sample Apps f -1650 cc3tibwarsd yGVzUf Command & Conquer 3 Demo f -1651 supcomm UMEjry Supreme Commander f -1652 assaultheroes WpA5Tx Assault Heroes f -1653 assaultheroesam WpA5Tx Assault Heroes Automatch f -1655 PowaPPocketds Mz5dau PowaProkun Pocket10 (DS) f -1656 GunMahjongZds XL3iSh Kidou Gekidan Haro Ichiza Gundam Mah-jong+Z (DS) f -1657 fullmatcgds fGRd5f Fullmetal Alchemist Trading Card Game (DS) f -1658 smashbrosxwii 3AxIg4 Dairantou Smash Brothers X (Wii) f -1659 disfriendsds VNTp6E Disney Friends DS (DS) f -1660 Jyotrainwii bRtr3p Minna de Jyoshiki Training Wii (Wii) f -1661 roguewarpc ey8w3N Rogue Warrior (PC) f -1665 roguewarps3am \N Rogue Warrior Automatch (PS3) f -1664 roguewarps3 bHtwt6 Rogue Warrior (PS3) f -1666 roguewarps3d \N Rogue Warrior Demo (PS3) f -1681 gta4pcam \N Grand Theft Auto 4 Automatch (PC) f -1667 archlord eLiRAC Archlord f -1668 racedriverds AJQu6F Race Driver (DS) f -1669 kaihatsuds gW0gp9 Kaihatsushitsu (DS) f -1671 redbaronww1 aMETX7 Red Baron WWI f -1672 greconawf2b qvOwuX Ghost Recon: Advanced Warfighter 2 Beta f -1673 greconawf2bam qvOwuX Ghost Recon: Advanced Warfighter 2 Beta Automatch f -1674 ubisoftdev K4wfax Ubisoft Development f -1675 ubisoftdevam K4wfax Ubisoft Development Automatch f -1676 testdriveuak k7r85E Test Drive Unlimited (Akella) f -1677 armaas fgDmOT ArmA: Armed Assault f -1678 gta4ps3 t3nTru Grand Theft Auto 4 (PS3) f -1680 gta4pc t3nTru Grand Theft Auto 4 (PC) f -1684 gta4xam \N Grand Theft Auto 4 Automatch (Xbox 360) f -1682 flashanzands 61pARy Flash Anzan Doujou (DS) f -1683 gta4x t3nTru Grand Theft Auto 4 (Xbox 360) f -1685 pokerangerds v4yMCT Pokemon Ranger 2 (DS) f -1686 megamansfeuds 8wlN9C Mega Man Star Force (EU) (DS) f -1687 mariokartwii 9r3Rmy Mario Kart Wii (Wii) f -1688 swtakoronwii xvf3KV Shall we Takoron (Wii) f -1689 phylon rbKTOq Phylon f -1691 warfronttp LTOTAa War Front: Turning Point f -1692 fearxp2 rDAg9r FEAR Perseus Mandate (PC) f -1693 risingeaglepc 9cy8vc Rising Eagle f -1694 bombls2ds 8BuVqr Touch! Bomberman Land 2 / Bomberman DS 2 (DS) f -1617 civconps3am \N Civ Console Automatch (PS3) f -1703 civ4bts Cs2iIq Civilization IV: Beyond the Sword f -1704 civ4btsam Cs2iIq Civilization IV: Beyond the Sword Automatch f -1696 sonriders2wii m2F95I Sonic Riders 2 (Wii) f -1697 sonicrushads so70kL Sonic Rush Adventure (DS) f -1698 ghostsquadwii lq8to9 Ghost Squad (Wii) f -1700 contrads bw215o Contra DS (DS) f -1701 bleach1USds d4wISd Bleach DS (US) (DS) f -1702 bleach2USds 7xEJsp Bleach DS 2 (US) (DS) f -1705 eearth3bam Fk6hTz Empire Earth III Beta Automatch f -1723 mclub4ps3am \N Midnight Club 4 Automatch (PS3) f -1707 thetsuriwii LhtR3d The Tsuri (Wii) f -1708 theracewii 6JbTWP The Race (Wii) f -1709 tankbeat2ds aAbi3S Tank Beat 2 (DS) f -1710 onslaughtpc 8pLvHm Onslaught: War of the Immortals f -1712 onslaughtpcd 8pLvHm Onslaught: War of the Immortals Demo f -1713 jikkyoprowii TKmp3m Jikkyo Powerful Pro Yakyu Wii (Wii) f -1714 cc3dev Ba77xN Command & Conquer 3 Dev Environment f -1715 cc3devam Ba77xN Command & Conquer 3 Dev Environment Automatch f -1716 wsc2007 xKCMfK World Snooker Championship 2007 f -1717 luminarcUSds aI8FCJ Luminous Arc (US) (DS) f -1718 bleach1EUds 9AxT0s Bleach DS (EU) (DS) f -1719 MSolympicds kK9ibq Mario & Sonic at the Olympic Games (DS) f -1720 keuthendev TtEZQR Keuthen.net Development f -1722 mclub4ps3 GQ8VXR Midnight Club 4 (PS3) f -1725 mclub4xboxam \N Midnight Club 4 Automatch (Xbox360) f -1724 mclub4xbox GQ8VXR Midnight Club 4 (Xbox360) f -1728 ut3pcam \N Unreal Tournament 3 Automatch (PC) f -1726 girlssecretds kNcVft Girls Secret Diary (DS) f -1727 ut3pc nT2Mtz Unreal Tournament 3 (PC) f -1734 tpfolpcam \N Turning Point: Fall of Liberty Automatch (PC) f -1729 ut3pcd KTiJdD Unreal Tournament 3 Demo (PC) f -1730 ecorisds dL9zd8 Ecoris (DS) f -1731 ww2btanks NcQeTO WWII Battle Tanks: T-34 vs Tiger f -1732 genesisr sbCDkj Genesis Rising f -1735 tpfolpcd \N Turning Point: Fall of Liberty Demo (PC) f -1737 tpfolps3am \N Turning Point: Fall of Liberty Automatch (PS3) f -1736 tpfolps3 svJqvE Turning Point: Fall of Liberty (PS3) f -1743 worldshiftpcam \N WorldShift Automatch (PC) f -1738 hsmusicalds Em72Cr High School Musical (DS) f -1739 cardheroesds xnSA6P Card Heroes (DS) f -1740 metprime3wii i8sP5E Metroid Prime 3 (Wii) f -1742 worldshiftpc 7gBmF4 WorldShift (PC) f -1744 worldshiftpcd \N WorldShift Demo (PC) f -1745 kingclubsds rL9dOy King of Clubs (DS) f -1746 MSolympicwii i6lEcz Mario & Sonic at the Olympic Games (Wii) f -1747 ingenious HiBpaV Ingenious f -1748 potco wnYrOe Pirates of the Caribbean Online f -1749 madden08ds wFuf7q Madden NFL 08 (DS) f -1751 fury KOMenT Fury f -1752 twoods08ds nNgv7v Tiger Woods 08 (DS) f -1753 otonazenshuds nWsT7z Otona no DS Bungaku Zenshu (DS) f -1754 bestfriendds i7Sk5y Best Friend - Main Pferd (DS) f -1755 nindev EdD7Ve Nintendo Network Development Testing f -1756 quakewarset i0hvyr Enemy Territory: Quake Wars f -1758 gwgalaxiesds 85buOw Geometry Wars Galaxies (DS) f -1759 gwgalaxieswii o3G7J2 Geometry Wars Galaxies (Wii) f -1760 hrollerzds ih5ZOl Homies Rollerz (DS) f -1761 dungeonr RibMFo Dungeon Runners f -1762 dirtdemo Twesup DiRT Demo f -1763 nameneverds oL7dO2 Nameless Neverland (DS) f -1764 proyakyuds Nv5Em6 Pro Yakyu Famisute DS (DS) f -1765 foreverbwii K7bZgf Forever Blue (Wii) f -1766 ee3alpha VQibCm Empire Earth III Alpha f -1768 officersgwupc xSK6e5 Officers: God With Us f -1769 officersgwupcam xSK6e5 Officers: God With Us Automatch f -1770 swordotnw TkDsNE Sword of the New World f -1771 nfsprostds 1Xq6Ru Need for Speed Pro Street (DS) f -1772 commandpc rSRJDr Commanders: Attack! f -1773 commandpcam rSRJDr Commanders: Attack! Automatch f -1775 whamdowfram pXL838 Warhammer 40,000: Dawn of War - Final Reckoning Automatch f -1776 shirenUSEUds bo4NTp Mysterious Dungeon: Shiren the Wanderer DS (US-EU) (DS) f -1777 sporeds 5kq9Pf Spore (DS) f -1778 mysecretsds Cz5NpG My Secrets (DS) f -1779 nights2wii Wo9FKJ NiGHTS: Journey of Dreams (Wii) f -1780 tgstadiumwii w1pKbR Table Game Stadium (Wii) f -1781 lovegolfwii mToBwA Wii Love Golf (Wii) f -1783 tankbattlesds kJl1BG Tank Battles (DS) f -1784 anarchyonline ThLopr Anarchy Online f -1785 hookedEUwii qLaV1p Hooked! Real Motion Fishing (EU) (Wii) f -1786 hookedJPNwii m4JMgN Hooked! Real Motion Fishing (JPN) (Wii) f -1787 tankbeatEUds Zc4TGh Tank Beat (EU) (DS) f -1788 farcry HkXyNJ Far Cry f -1789 yugiohwc08ds X6i4ay Yu-Gi-Oh! World Championship 2008 (DS) f -1790 trackfieldds zC5kgT International Track & Field (DS) f -1792 RKMvalleyds 3lwaZW River King: Mystic Valley (DS) f -1793 DSwars2ds fF4Wtd DS Wars 2 (DS) f -1794 cdkeys QcpyWG CD Key Admin Site Testing f -1795 wordjongds V6dC1l Word Jong - US (DS) f -1796 raymanrr2wii Dd8tS2 Rayman Raving Rabbids 2 (Wii) f -1797 nanostray2ds grOpw9 Nanostray 2 (DS) f -1798 guitarh3wii O5imMd Guitar Hero 3 (Wii) f -1800 segatennisps3 RvJsgM Sega SuperStars Tennis (PS3) f -1801 segatennisps3am RvJsgM Sega SuperStars Tennis Automatch f -1706 eearth3dam \N Empire Earth III Demo Automatch f -1803 fifa08ds zLTgc4 FIFA 08 Soccer (DS) f -1805 dragladeds wPb9aW Draglade (DS) f -1806 takoronUSwii 3hzhvW Takoron (US) (Wii) f -1807 dragonbzUSwii 5tRJqr Dragonball Z: Tenkaichi 3 (US) (Wii) f -1808 arkanoidds QxU6hI Arkanoid DS (DS) f -1809 rfactory2ds lo8Vq8 Rune Factory 2 (DS) f -1810 dow VLxgwe Dawn of War f -1811 nitrobikewii viBXma Nitrobike (Wii) f -1813 WSWelevenwii aFJW2D World Soccer Winning Eleven Wii (Wii) f -1814 cc3xp1 BhcJLQ Command & Conquer 3: Expansion Pack 1 f -1815 cc3xp1am BhcJLQ Command & Conquer 3: Expansion Pack 1 Automatch f -1816 pachgundamwii w101rF Pachisuro Kido Senshi Gundam Aisenshi Hen (Wii) f -1817 newgamename 3I7iFz dhdh f -1818 newgamenameam 3I7iFz dhdh Automatch f -1819 gsTiaKreisDS p4oT53 Genso Suikokuden TiaKreis (DS) f -1820 ultimateMKds irQTn8 Ultimate Mortal Kombat (DS) f -1821 MLBallstarsds KT8yGE Major League Baseball Fantasy All-Stars (DS) f -1822 wicb SITsUf World in Conflict Beta f -1823 ee3beta tvbRKD Empire Earth III Beta f -1825 mafia2pc HgPhRC Mafia 2 (PC) f -1828 mafia2ps3am \N Mafia 2 Automatch (PS3) f -1827 mafia2ps3 cn3EpM Mafia 2 (PS3) f -2419 sawpcd \N SAW Demo (PC) f -1829 chocotokiwii uxXJS3 Chocobo no Fushigina Dungeon: Toki-Wasure no Meikyu (Wii) f -1830 zeroGds ViuPw7 ZeroG (DS) f -1832 furaishi3wii xK58W8 Furai no Shiren 3 Karakuri Yashiki no Nemuri Hime (Wii) f -1833 ben10bb bwV3xN Ben 10 Bounty Battle f -1834 ben10bbam bwV3xN Ben 10 Bounty Battle Automatch f -1835 risingeagleg qVcJOg Rising Eagle f -1836 timeshiftg PAWCeH Timeshift f -1837 cadZ2JPwii Z5bgxv Caduceus Z2 (Wii) f -1838 rockmanBSDds bNM3ah Rockman 2 - Berserk: Shinobi / Dinosaur (DS) f -1839 THPGds oucE6T Tony Hawks Proving Ground (DS) f -1840 birhhpc sPZGCy Brothers In Arms: Hell's Highway (PC) f -1842 birhhps3 HrDRqe Brothers In Arms: Hell's Highway (PS3) f -1843 birhhps3am HrDRqe Brothers In Arms: Hell's Highway Clone Automatch (PS3) f -1844 sakuraTDDds Kw2K7t Sakura Taisen Dramatic Dungeon - Kimiarugatame (DS) f -1845 fsxa edkTBp Flight Simulator X: Acceleration f -1846 fsxaam edkTBp Flight Simulator X: Acceleration Automatch f -1847 ee3 dObLWQ Empire Earth 3 f -1848 nwn2mac m8NeTP NeverWinter Nights 2 (MAC) f -1853 cohofbeta \N Company of Heroes: Opposing Fronts MP Beta f -1851 quakewarsetd i0hvyr Enemy Territory: Quake Wars Demo f -1852 evosoccer08ds fI2bz5 Pro Evolution Soccer 2008 (DS) f -1863 ut3pcdam \N Unreal Tournament 3 Demo Automatch (PC) f -1854 tetrisppwii p8a6oW Tetris++ (WiiWare) f -1855 bioshock SGeqMj Bioshock Demo f -1856 bioshockd eoaXfs Bioshock f -1857 civrevods o3WUx2 Sid Meier's Civilization Revolution (DS) f -1858 ninjagaidends 4VMtoU Ninja Gaiden: Dragon Sword (DS) f -1860 clubgameKORds Jb3Tt1 Clubhouse Games (KOR) (DS) f -1861 wic mtjzlE World in Conflict Demo f -1862 wicd taMBOb World in Conflict Demo f -1880 ut3ps3am \N Unreal Tournament 3 Automatch (PS3) f -1864 evosoc08USds nV87bl Pro Evolution Soccer 2008 (US) (DS) f -1865 wormsasowii UeJRpQ Worms: A Space Oddity (Wii) f -1866 painkillerod zW4TsZ Painkiller Overdose f -1868 cnpanzers2cw 2mEAh7 Codename Panzers 2: Cold Wars (PC) f -1869 cnpanzers2cwam 2mEAh7 Codename Panzers 2: Cold Wars Automatch f -1870 cnpanzers2cwd 2mEAh7 Codename Panzers 2: Cold Wars Demo f -1871 luminarc2ds e8O5aA Luminous Arc 2 Will (DS) f -1872 noahprods aP8x4A Noah's Prophecy (DS) f -1874 thesactionwii zKsfNR The Shooting Action (Wii) f -1875 Asonpartywii g4hp4x Asondewakaru THE Party/Casino (Wii) f -1876 sptouzokuds 4TxC2h Steel Princess Touzoku Koujyo (DS) f -1877 heiseikyods 4zBVda Heisei Kyoiku Iinkai DS Zenkoku Touitsu Moshi Special (DS) f -1878 famstadiumwii s75Uvn Family Stadium Wii (Wii) f -1879 ut3ps3 nT2Mtz Unreal Tournament 3 (PS3) f -1881 ut3ps3d \N Unreal Tournament 3 Demo (PS3) f -1890 goreAVam \N Gore Automatch (Ad version) f -1882 ecocreatureds 61JwLu Eco-Creatures: Save the Forest (DS) f -1883 mohairborne TjNJcy Medal of Honor: Airborne f -1885 whamdowfrbam pXL838 Warhammer 40,000: Dawn of War - Final Reckoning Beta Automatch f -1886 puyobomberds rU1vT5 Puyo Puyo Bomber (DS) f -1887 s_tf2 AYcFzB Team Fortress 2 f -1888 painkillerodd zW4TsZ Painkiller Overdose Demo f -1889 goreAV NYZEAK Gore (Ad version) f -1891 goreAVd \N Gore Demo (Ad version) f -1902 pkodgermanam \N Painkiller Overdose Automatch (German) f -1892 fstreetv3ds d46hQk FIFA Street v3 (DS) f -1893 dragladeEUds fpEnOg Draglade (EU) (DS) f -1894 culdceptds 6qUoZg Culdcept DS (DS) f -1895 ffwcbeta oZTTQy Frontlines: Fuel of War Beta f -1896 ffowbeta wqhcSQ Frontlines: Fuel of War Beta f -1901 pkodgerman wK54dt Painkiller Overdose (German) f -1903 pkodgermand \N Painkiller Overdose Demo (German) f -1904 cohof epROcy Company of Heroes: Opposing Fronts f -1905 puzzlernumds JpBkl8 Puzzler Number Placing Fun & Oekaki Logic 2 (DS) f -1906 supcomfabeta xvuHpR Supreme Commander: Forged Alliance beta f -1907 condemned2bs kwQ9Ak Condemned 2: Bloodshot (PS3) f -1849 nwn2macam \N NeverWinter Nights 2 Automatch (MAC) f -1826 mafia2pcam \N Mafia 2 Automatch (PC) f -1934 civ4btsjp Cs2iIq Civilization IV: Beyond the Sword (Japanese) f -1909 condemned2bsd kwQ9Ak Condemned 2: Bloodshot Demo (PS3) f -1910 potbs GIMHzf Pirates of the Burning Sea f -1911 mxvsatvutps2 8GnBeH MX vs ATV Untamed (PS2) f -1928 swbfront3pcam \N Star Wars Battlefront 3 Automatch (PC) f -1914 ut3demo KTiJdD Unreal Tournament 3 Demo f -1915 ut3 UebAWH Unreal Tournament 3 f -1916 valknightswii N5Mf0P Valhalla Knights (Wii) f -1917 amfbowlingds c56nI8 AMF Bowling: Pinbusters! (DS) f -1918 ducatimotods i9Duh0 Ducati Moto (DS) f -1919 arkwarriors sTPqLc Arkadian Warriors f -1920 arkwarriorsam sTPqLc Arkadian Warriors Automatch f -1921 mxvsatvutwii fuA9hK MX vs ATV Untamed (Wii) f -1922 cheetah3ds 4aCzFd The Cheetah Girls 3 (DS) f -1923 whammermocbm EACMEZ Warhammer: Mark of Chaos - Battle March f -1925 whammermocbmd EACMEZ Warhammer: Mark of Chaos - Battle March Demo f -1926 linksds 9TKvyS Links (DS) f -1927 swbfront3pc y3AEXC Star Wars Battlefront 3 (PC) f -1937 timeshiftd \N TimeShift Demo (PC) f -1929 siextremeds 69EvKG Space Invaders Extreme f -1931 redsteel2wii eAmAH0 Red Steel 2 (Wii) f -1932 gorese NYZEAK Gore Special Edition f -1933 dinokingEUds 0E0awE Ancient Ruler Dinosaur King (EU) (DS) f -1936 charcollectds DlZ3ac Character Collection! DS (DS) f -1945 nitrobikeps2am \N Nitrobike Automatch (PS2) f -1938 gtacwarsds nm4V4b Grand Theft Auto: Chinatown Wars (DS) f -1939 fireemblemds pTLtHq Fire Emblem DS (DS) f -1940 soccerjamds vTgXza Soccer Jam (DS) f -1941 gravitronwii V9q1aK Gravitronix (WiiWare) f -1942 mdamiiwalkds 8c2EoW Minna de Aruku! Mii Walk (DS) f -1943 puzzlemojiwii 0Um7ap Kotoba no Puzzle Moji Pittan Wii (WiiWare) f -1944 nitrobikeps2 ApD3DN Nitrobike (PS2) f -1951 sbk08pcam \N SBK '08: Superbike World Championship Automatch (PC) f -1946 dinokingUSds 8DgStx Dinosaur King (US) (DS) f -1947 harvfishEUds ZGwLfc Harvest Fishing (EU) (DS) f -1949 harmoon2ds 5rNkAv Harvest Moon DS 2 (EU) (DS) f -1950 sbk08pc Xhg4AV SBK '08: Superbike World Championship (PC) f -1952 sbk08pcd \N SBK '08: Superbike World Championship Demo (PC) f -1954 sbk08ps3am \N SBK '08: Superbike World Championship Automatch (PS3) f -1953 sbk08ps3 Xhg4AV SBK '08: Superbike World Championship (PS3) f -1962 timeshiftps3d \N TimeShift Demo (PS3) f -1955 exitds Ip6JF2 Hijyoguchi: EXIT DS (DS) f -1956 spectrobes2ds uBRc5a Kaseki Choshinka Spectrobes 2 (DS) f -1957 nanost2EUds Soz92T Nanostray 2 (EU) (DS) f -1958 crysisspd IWfplN Crysis SP Demo f -1960 evosoc08USwii 94lupD Pro Evolution Soccer 2008 (US) (Wii) f -1961 sdamigowii gLq68v Samba de Amigo (Wii) f -1981 ballers3ps3am \N NBA Ballers: Chosen One Automatch (PS3) f -1963 chesswii X8qanS Wii Chess (Wii) f -1964 ecolisEUds uCaiU4 Ecolis (EU) (DS) f -1965 rdgridds 81TpiD Race Driver: Grid (DS) f -1966 swbfront3wii ILAvd9 Star Wars: Battlefront 3 (Wii) f -1967 guitarh3xpwii xkrTOa Guitar Hero 3 Expansion Pack (Wii) f -1968 callofduty4 AOPMCh Call of Duty 4: Modern Warfare f -1969 mmadnessexps3 M2ydAr Monster Madness EX (PS3) f -1971 mmadexps3d M2ydAr Monster Madness EX Demo (PS3) f -1972 terrortkdwn2 VSlLZK Terrorist Takedown 2 f -1973 terrortkdwn2am KmqStH Terrorist Takedown 2 Automatch f -1974 terrortkdwn2d KmqStH Terrorist Takedown 2 Demo f -1975 cc3xp1mb BhcJLQ Command & Conquer 3: Kane's Wrath Match Broadcast Clone f -1976 dstallionds 1fNr6d Derby Stallion DS (DS) f -1978 thecombatwii VM5pGy SIMPLE Wii Series Vol.6 THE Minna de Waiwai Combat (Wii) f -1979 cc3kw TPLstA Command and Conquer 3 Kanes Wrath f -1980 ballers3ps3 lu5P4Q NBA Ballers: Chosen One (PS3) f -1982 ballers3ps3d \N NBA Ballers: Chosen One Demo (PS3) f -1985 scourgepcam \N The Scourge Project Automatch (PC) f -1983 cc3kwmb TPLstA Command and Conquer 3 Kanes Wrath Match Broadcast f -1984 scourgepc NExdfn The Scourge Project (PC) f -1986 scourgepcd \N The Scourge Project Demo (PC) f -1988 scourgeps3am \N The Scourge Project Automatch (PS3) f -1987 scourgeps3 NExdfn The Scourge Project (PS3) f -1989 scourgeps3d \N The Scourge Project Demo (PS3) f -2002 mxvatvuPALps2am \N MX vs ATV Untamed PAL Automatch (PS2) f -1991 popwii cQbQkV Pop (WiiWare) f -1992 tenchu4wii z2VBXP Tenchu 4 (Wii) f -1993 ssoldierrwii 4w5JoH Star Soldier R (WiiWare) f -1994 2kboxingds JiBAt0 2K Boxing (DS) f -1995 bldragonds iICaoP Blue Dragon (DS) f -1996 elebitsds bVEhC3 Elebits DS - Kai to Zero no Fushigi na Bus (DS) f -1997 nobunagapktds t8SaPD Nobunaga no Yabou DS Pocket Senshi (DS) f -1998 kqmateDS 8dEm7s KaitoranmaQ Mate! (DS) f -1999 digichampds lDiK3f Digimon Championship (DS) f -2000 yakumanwii vcBUtC Yakuman Wii (WiiWare) f -2006 redbaronps3am \N Red Baron WWI Automatch (PS3) f -2003 mezasetm2wii hfBxDP Mezase!! Tsuri Master 2 (Wii) f -2004 raw2009wii hCcmdR WWE Smackdown! vs RAW 2009 (Wii) f -2005 redbaronps3 aMETX7 Red Baron WWI (PS3) f -2007 memansf2USDS ZAO34c Mega Man Star Force 2: Zerker x Shinobi / Saurian (US) f -2008 mxvatvutEUwii 9vF1oO MX vs ATV Untamed (EU) (Wii) f -2009 lostmagicwii hQrVFm Lost Magic Wii (Wii) f -1912 mxvsatvutps2am \N MX vs ATV Untamed Automatch (PS2) f -2014 blitz08ps3am \N Blitz: The League 08 Automatch (PS3) f -2013 blitz08ps3 Jk4zlB Blitz: The League 08 (PS3) f -2015 blitz08ps3d \N Blitz: The League 08 Demo (PS3) f -2022 finertiaps3am \N Fatal Inertia Automatch (PS3) f -2017 lonposwii L08ik8 Lonpos (WiiWare) f -2018 cvania08ds SwO9Jn Castlevania 2008 (DS) f -2019 nplusds qX9Muy N+ (DS) f -2020 gauntletds wUq7fL Gauntlet (DS) f -2021 finertiaps3 3vEcPe Fatal Inertia (PS3) f -2029 tpfolEUpcd \N Turning Point: Fall of Liberty Demo (EU) (PC) f -2023 topspin3usds 8R4LgD Top Spin 3 (US) (DS) f -2024 topspin3euds ETgvzA Top Spin 3 (EU) (DS) f -2026 simcitywii tLpVr1 SimCity Creator (Wii) f -2027 tpfolEUpc ltSs4H Turning Point: Fall of Liberty (EU) (PC) f -2031 tpfolEUps3am \N Turning Point: Fall of Liberty Automatch (EU) (PS3) f -2030 tpfolEUps3 svJqvE Turning Point: Fall of Liberty (EU) (PS3) f -2033 parabellumpcam \N Parabellum Automatch (PC) f -2032 parabellumpc CXabGK Parabellum (PC) f -2034 parabellumpcd \N Parabellum Demo (PC) f -2036 parabellumps3am \N Parabellum Automatch (PS3) f -2035 parabellumps3 CXabGK Parabellum (PS3) f -2039 necrovisionpcam \N NecroVision Automatch (PC) f -2037 monlabwii 8Lypy2 Monster Lab (Wii) f -2038 necrovisionpc Rn3Ptn NecroVision (PC) f -2040 necrovisionpcd \N NecroVision Demo (PC) f -2042 necrovisionpdam \N NecroVision Automatch (PC) Demo f -2041 necrovisionpd Rn3Ptn NecroVision (PC) Demo f -2044 damnationpcam \N DamNation Automatch (PC) f -2043 damnationpc Jpxpfr DamNation (PC) f -2045 damnationpcd \N DamNation Demo (PC) f -2049 parabellumpcdam \N Parabellum Demo Automatch (PC) f -2046 damnationps3 Jpxpfr DamNation (PS3) f -2048 strongholdce UWmLcS Stronghold: Crusader Extreme f -2056 mclub4xboxdevam \N Midnight Club 4 Dev Automatch (Xbox360) f -2050 madeinoreds 6vwCT1 Made in Ore (DS) f -2051 guinnesswrds iwTBGk Guinness World Records: The Video Game (DS) f -2052 guinnesswrwii x0oPvh Guinness World Records: The Video Game (Wii) f -2053 mclub4ps3dev GQ8VXR Midnight Club 4 Dev (PS3) f -2055 mclub4xboxdev GQ8VXR Midnight Club 4 Dev (Xbox360) f -2058 gta4pcdevam \N Grand Theft Auto 4 Dev Automatch (PC) f -2057 gta4pcdev t3nTru Grand Theft Auto 4 Dev (PC) f -2060 gta4ps3devam \N Grand Theft Auto 4 Dev Automatch (PS3) f -2059 gta4ps3dev t3nTru Grand Theft Auto 4 Dev (PS3) f -2062 gts4xdevam \N Grand Theft Auto 4 Dev Automatch (Xbox 360) f -2061 gts4xdev t3nTru Grand Theft Auto 4 Dev (Xbox 360) f -2065 plunderpcam \N Plunder Automatch (PC) f -2063 monfarm2ds IvFJw0 Monster Farm DS 2 (DS) f -2064 plunderpc NmLqNN Age of Booty (PC) f -2068 legendaryps3am \N Legendary Automatch (PS3) f -2066 plunderpcd NmLqNN Plunder Demo (PC) f -2073 beijing08pcam \N Beijing 2008 Automatch (PC) f -2069 callofduty4d2d AOPMCh Call of Duty 4: Modern Warfare f -2070 sekainodokods cS9uS9 Sekai no Dokodemo Shaberu! DS Oryori Navi (DS) f -2071 glracerwii kAM5wF GameLoft's Racer (WiiWare) f -2072 beijing08pc P4QzX5 Beijing 2008 (PC) f -2076 beijing08ps3am \N Beijing 2008 Automatch (PS3) f -2075 beijing08ps3 P4QzX5 Beijing 2008 (PS3) f -2077 beijing08ps3d \N Beijing 2008 Demo (PS3) f -2079 hail2chimps3am \N Hail to the Chimp Automatch (PS3) f -2078 hail2chimps3 mDeBg3 Hail to the Chimp (PS3) f -2081 wlclashpcam \N War Leaders: Clash of Nations Automatch (PC) f -2080 wlclashpc qcU8MT War Leaders: Clash of Nations (PC) f -2082 wlclashpcd \N War Leaders: Clash of Nations Demo (PC) f -2086 heistpcd \N Heist Demo (PC) f -2083 bomberman20ds JZ2s7T Bomberman 2.0 (DS) f -2084 heistpc BLhZD9 Heist (PC) f -2088 heistps3am \N Heist Automatch (PS3) f -2087 heistps3 BLhZD9 Heist (PS3) f -2091 bstrikeotspcd \N Battlestrike: Operation Thunderstorm Demo (PC) f -2089 bstrikeotspc 7vRSBa Battlestrike: Operation Thunderstorm (PC) f -2097 gta4ps3grmam \N Grand Theft Auto 4 German Automatch (PS3) f -2092 Decathletesds ry7e63 Decathletes (DS) f -2093 mleatingwii lcUrQg Major League Eating: The Game (EU/US) (WiiWare) f -2094 cc3kwcd TPLstA Command and Conquer 3 Kanes Wrath CD Key Auth f -2095 cc3kwcdam TPLstA Command and Conquer 3 Kanes Wrath CD Key Auth Automatch f -2096 gta4ps3grm t3nTru Grand Theft Auto 4 German (PS3) f -2099 gta4xgrmam \N Grand Theft Auto 4 German Automatch (Xbox 360) f -2098 gta4xgrm t3nTru Grand Theft Auto 4 German (Xbox 360) f -2111 srow2pcam \N Saint's Row 2 Automatch (PC) f -2100 cc3tibwarscd E4F3HB Command & Conquer 3: Tiberium Wars CD Key Auth f -2102 arkaUSEUds kORtxH Arkanoid DS (US/EU) (DS) f -2103 madden09ds yOuECC Madden NFL 09 (DS) f -2104 tetpartywii IZyry6 Tetris Party (WiiWare) f -2105 frontlinesfow ZEmOuj Frontlines: Fuel of War f -2106 simspartywii fZ3lCM MySims Party (Wii) f -2107 momotaro20ds gPy2cd Momotaro Dentetsu 20 Shuunen (DS) f -2108 srow2ps3 iWKxFZ Saint's Row 2 (PS3) f -2109 srow2ps3am iWKxFZ Saint's Row 2 Automatch f -2110 srow2pc odvimE Saint's Row 2 (PC) f -2113 srow2xb360am \N Saint's Row 2 Automatch (Xbox 360) f -2112 srow2xb360 XfwkNR Saint's Row 2 (Xbox 360) f -2116 aliencrashwii gRfsiO Alien Crash (WiiWare) f -2012 worldshiftpcbam \N WorldShift Beta Automatch (PC) f -2194 civ4xp3 lgNJU7 Civilization IV: 3rd Expansion f -2195 civ4xp3am lgNJU7 Civilization IV: 3rd Expansion Automatch f -2196 civ4xp3d lgNJU7 Civilization IV: 3rd Expansion Demo f -2208 crysiswars zKbZiM Crysis Wars f -2213 civ4colpc 2yheDS Sid Meier's Civilization IV: Colonization (PC/Mac) f -2118 legendarypc WUp2J6 Legendary (PC) f -2129 redalert3pcam \N Red Alert 3 Automatch (PC) f -2121 megaman9wii r6PBov Mega Man 9 (WiiWare) f -2122 dqmonjoker2ds MXsuQS Dragon Quest Monsters: Joker 2 (DS) f -2123 quizmagicds zi1Kob Quiz Magic Academy DS (DS) f -2124 Narutonin2ds c0DRrn Naruto: Path of the Ninja 2 (DS) f -2126 mfcoachwii 9q49yB My Fitness Coach (Wii) f -2127 othellods VrRUK1 Othello de Othello DS f -2128 redalert3pc uBZwpf Red Alert 3 (PC) f -2130 redalert3pcd \N Red Alert 3 Demo (PC) f -2132 redalert3ps3am \N Red Alert 3 Automatch (PS3) f -2131 redalert3ps3 uBZwpf Red Alert 3 (PS3) f -2134 plunderps3am \N Plunder Automatch (PS3) f -2133 plunderps3 RbMD9p Age of Booty (PS3) f -2135 plunderps3d \N Plunder Demo (PS3) f -2140 swbf3pspam \N Star Wars: Battlefront 3 Automatch (PSP) f -2136 svsr09ps3 Pzhfov WWE Smackdown vs. RAW 2009 (PS3) f -2137 wordjongFRds XAIqLK Word Jong - FR (DS) f -2139 swbf3psp U8yNSx Star Wars: Battlefront 3 (PSP) f -2155 tpfolEUpcBam \N Turning Point: Fall of Liberty Automatch (EU-B) (PC) f -2141 CMwrldkitwii 24vlFy Cooking Mama: World Kitchen (Wii) f -2142 shootantowii nsgl14 Shootanto (Wii) f -2143 punchoutwii yJz8h0 Punch-Out!! (Wii) f -2145 ultibandwii F8KfNf Ultimate Band (Wii) f -2146 CVjudgmentwii 9te6Ua Castlevania: Judgment (Wii) f -2147 ageofconanb QZQLGt Age of Conan beta f -2148 wrldgoowii Nz4tSw World of Goo (WiiWare) f -2149 saadtestam 1a2B3c SaadsTest f -2152 scotttestam RPzJL7 Scott's test gamename Automatch f -2153 testam Hku6Fd Test Automatch f -2154 tpfolEUpcB ltSs4H Turning Point: Fall of Liberty (EU-B) (PC) f -2156 tpfolEUpcBd \N Turning Point: Fall of Liberty Demo (EU-B) (PC) f -2158 tpfolpcBam \N Turning Point: Fall of Liberty Automatch (B) (PC) f -2157 tpfolpcB svJqvE Turning Point: Fall of Liberty (B) (PC) f -2159 tpfolpcBd \N Turning Point: Fall of Liberty Demo (B) (PC) f -2164 swbfront3pcCam \N Star Wars Battlefront 3 Automatch (PS3) f -2160 cc3arenapc gE7WcR Command & Conquer: Arena f -2161 cc3arenapcam gE7WcR Command & Conquer: Arena Automatch f -2162 cc3arenapcd gE7WcR Command & Conquer: Arena Demo f -2166 coh2pcam \N Code of Honor 2 Automatch (PC) f -2165 coh2pc J4b95X Code of Honor 2 (PC) f -2168 dimensitypcam \N Dimensity Automatch (PC) f -2167 dimensitypc ZTcB4o Dimensity (PC) f -2169 dimensitypcd \N Dimensity Demo (PC) f -2175 srow2ps3dam \N Saint's Row 2 Automatch (PS3) Demo f -2170 gta4ps3test t3nTru Grand Theft Auto 4 Test (PS3) f -2171 50centsandps3 ORydHB 50 Cent: Blood on the Sand (PS3) f -2173 locksquestds 30bDMu Construction Combat: Lock's Quest f -2174 srow2ps3d iWKxFZ Saint's Row 2 (PS3) Demo f -2180 reichpcam \N Reich Automatch (PC) f -2176 nobuyabou2ds izfHO0 Nobunaga no Yabou DS 2 (DS) f -2177 memansf2EUDS ZqlkTy Mega Man Star Force 2: Zerker x Shinobi / Saurian (EU) f -2178 bleach2EUds B0veR8 Bleach DS 2: Requiem in the black robe (EU) (DS) f -2179 reichpc bWwGZn Reich (PC) f -2182 reichps3am \N Reich Automatch (PS3) Clone f -2181 reichps3 bWwGZn Reich (PS3) f -2185 saspcam \N SAS Automatch (PC) f -2183 gloftpokerwii NtKG3P Gameloft Poker (WiiWare) f -2184 saspc fewePZ SAS (PC) f -2189 poriginps3am \N Fear 2: Project Origin Automatch (PS3) f -2187 acrossingwii Z7Fm9K Animal Crossing Wii (Wii) f -2188 poriginps3 Rl6qAT Fear 2: Project Origin (PS3) f -2190 poriginps3d \N Fear 2: Project Origin Demo (PS3) f -2192 poriginpcam \N Fear 2: Project Origin Automatch (PC) f -2191 poriginpc yAnB97 Fear 2: Project Origin (PC) f -2210 menofwarpcam \N Men of War Automatch (PC) f -2197 necrovision sgcRRY NecroVision f -2198 tecmoblkickds wdh5FE Tecmo Bowl Kickoff (DS) f -2199 bballarenaps3 W8bW5s Supersonic Acrobatic Rocket-Powered BattleCars: BattleBall Arena f -2201 ragunonlineds DBDrfl Ragunaroku Online DS (DS) f -2202 mlb2k9ds 8Z34m5 Major League Baseball 2K9 Fantasy All-Stars (DS) f -2203 kororinpa2wii aWybXG Kororinpa 2 (Wii) f -2204 stlprincessds M1LDrU Steal Princess (DS) f -2205 aoemythds a9EK9F Age of Empires: Mythologies (DS) f -2206 raymanRR3wii QDgvIN Rayman Raving Rabbids 3 (Wii) f -2207 pitcrewwii M2FFnb Pit Crew Panic (WiiWare) f -2209 menofwarpc KrMW4d Men of War (PC) f -2214 civ4colpcam \N Sid Meier's Civilization IV: Colonization Automatch (PC) f -2211 sakatsukuds OwGy5R Sakatsuku DS (DS) f -2212 wtrwarfarewii R3zu4t Water Warfare (WiiWare) f -2215 civ4colpcd \N Sid Meier's Civilization IV: Colonization Demo (PC) f -2221 hail2chimps3d \N Hail to the Chimp Demo (PS3) f -2216 tablegamestds Tk2MJq Table Game Stadium (D3-Yuki) (Wii) f -2217 ppkpocket11ds cstLhz PowerPro-kun Pocket 11 (DS) f -2218 bleach2wii hTswOX BLEACH Wii2 (Wii) f -2219 cuesportswii rNBacr Cue Sports (WiiWare) f -2119 legendarypcam \N Legendary Automatch (PC) f -2223 bgeverwii zDalN6 Best Game Ever (WiiWare) f -2224 spinvgewii qbD03S Space Invaders: Get Even (WiiWare) f -2225 tstgme gkWzAc test game f -2226 TerroristT2 VSlLZK Terrorist Takedown 2 f -2227 pokemonplatds IIup73 Pokemon Platinum (DS) f -2229 redalert3pcdmb uBZwpf Red Alert 3 Demo (PC) Match Broadcast f -2230 jbond08wii 50hs18 James Bond 2008 (Wii) f -2231 assultwii 6AStwk Assult (Wii) f -2232 mmartracewii NCAq3G Mega Mart Race (WiiWare) f -2233 fifa09ds 5VxqMN FIFA 09 Soccer (DS) f -2234 bboarderswii Z5pkm2 Battle Boarders (WiiWare) f -2235 cellfactorpsn gkVTh8 CellFactor: Ignition (PSN) f -2237 witcher OaHhFk The Witcher f -2238 redalert3pcb uBZwpf Red Alert 3 Beta (PC) f -2242 redalert3pcdam \N Red Alert 3 Demo Automatch (PC) f -2241 redalert3pcbmb uBZwpf Red Alert 3 Beta (PC) Match Broadcast f -2249 mkvsdcps3am \N Mortal Kombat vs. DC Universe Automatch (PS3) f -2243 opblitz gOlcku Operation Blitzsturm f -2244 shogiwii AtQf1n Shogi (Wii) (WiiWare) f -2245 redfactionwii OyLhJI Red Faction Wii (Wii) f -2246 ryantest RakTQT Ryan'st test gamename f -2247 ryantestam RakTQT Ryan'st test gamename Automatch f -2251 mkvsdcxboxam \N Mortal Kombat vs. DC Universe Automatch (Xbox) f -2250 mkvsdcxbox XqrAqV Mortal Kombat vs. DC Universe (Xbox) f -2260 kingtigerspcam \N King Tigers Automatch (PC) f -2252 blzrdriverds n9HLOG Blazer Drive (DS) f -2253 micchannelwii wkvBfX Mic Chat Channel (Wii) f -2255 jnglspeedwii sPCqp8 Jungle Speed (WiiWare) f -2259 kingtigerspc E4hD2t King Tigers (PC) f -2261 kingtigerspcd \N King Tigers Demo (PC) f -2263 hail2chimps3ram \N Hail to the Chimp Retail Automatch (PS3) f -2262 hail2chimps3r mDeBg3 Hail to the Chimp Retail (PS3) f -2265 stalkercsam \N Stalker: Clear Sky Automatch (PC) f -2264 stalkercs PQ7tFU Stalker: Clear Sky (PC) f -2266 stalkercsd \N Stalker: Clear Sky Demo (PC) f -2275 ut3jpps3am \N Unreal Tournament 3 Japanese Automatch (PS3) f -2267 gradiusrbwii ZXCd6z Gradius ReBirth (WiiWare) f -2268 radiohitzwii NzBSQr Radiohitz: Guess That Song! (WiiWare) f -2270 fstarzerods knJOIz Fantasy Star ZERO (DS) f -2271 igowii ikN1qM Igo (Wii) (WiiWare) f -2272 bokujyomonds aydHX0 Bokujyo Monogatari Youkoso! Kaze no Bazzare (DS) f -2273 hotncoldds ngPcan Hot 'n' Cold (DS) f -2274 ut3jpps3 nT2Mtz Unreal Tournament 3 Japanese (PS3) f -2277 ut3jppcam \N Unreal Tournament 3 Japanese Automatch (PC) f -2276 ut3jppc nT2Mtz Unreal Tournament 3 Japanese (PC) f -2279 AliensCMPCam \N Aliens: Colonial Marines Automatch (PC) f -2278 AliensCMPC 5T4ATR Aliens: Colonial Marines (PC) f -2280 AliensCMPCd \N Aliens: Colonial Marines Demo (PC) f -2282 AliensCMPS3am \N Aliens: Colonial Marines Automatch (PS3) f -2281 AliensCMPS3 5T4ATR Aliens: Colonial Marines (PS3) f -2283 AliensCMPS3d \N Aliens: Colonial Marines Demo (PS3) f -2285 Majesty2PCam \N Majesty 2 Automatch (PC) f -2284 Majesty2PC aKwmX5 Majesty 2 (PC) f -2286 Majesty2PCd \N Majesty 2 Demo (PC) f -2289 FlockPCd \N Flock Demo (PC) f -2287 FlockPC 84z6J4 Flock (PC) f -2293 FlockPSNd \N Flock Demo (PSN) f -2290 FlockPSN 84z6J4 Flock (PSN) f -2294 FlockPSNam \N Flock Automatch (PSN) f -2299 wormspspam \N Worms Automatch (PSP) f -2295 bbobblewii IdTzGr Bubble Bobble Wii (WiiWare) f -2296 cellfactorpc gkVTh8 CellFactor: Ignition (PC) f -2298 wormspsp mpCK9u Worms (PSP) f -2301 MotoGP08PCam \N MotoGP08 Automatch (PC) f -2300 MotoGP08PC ZvH4b3 MotoGP08 (PC) f -2303 MotoGP08PS3am \N MotoGP08 Automatch (PS3) f -2302 MotoGP08PS3 ZvH4b3 MotoGP08 (PS3) f -2314 neopetspapcam \N Neopets Puzzle Adventure Automatch (PC) f -2304 sonicbkwii 1SWPIm Sonic and the Black Knight (Wii) f -2305 ghero4wii xcJsPA Guitar Hero 4 (Wii) f -2306 digichampKRds KbWB9w Digimon Championship (KOR) (DS) f -2309 mswinterds uyEG4g Mario & Sonic at the Olympic Winter Games (DS) f -2310 fmasterwtwii 07pDGe Fishing Master: World Tour (Wii) f -2311 starpballwii oDN3tk Starship Pinball (WiiWare) f -2312 nfsmwucoverds qxwQMf Need for Speed Most Wanted: Undercover (DS) f -2313 neopetspapc MOEXUs Neopets Puzzle Adventure (PC) f -2315 neopetspapcd \N Neopets Puzzle Adventure Demo (PC) f -2326 ufc09ps3am \N UFC 2009 Automatch (PS3) f -2316 luminarc2USds 9kIcv6 Luminous Arc 2 Will (US) (DS) f -2318 monkmayhemwii wMe9tQ Maniac Monkey Mayhem (WiiWare) f -2319 takoronKRwii N5yalP Takoron (KOR) (Wii) f -2321 kaosmpr 6cQWlD Kaos MPR f -2322 kaosmpram 6cQWlD Kaos MPR Automatch f -2323 kaosmprd 6cQWlD Kaos MPR Demo f -2324 mcdcrewds 8qTI8b McDonald's DS Crew Development Program (DS) f -2325 ufc09ps3 DCLItd UFC 2009 (PS3) f -2327 ufc09ps3d \N UFC 2009 Demo (PS3) f -2329 ufc09x360am \N UFC 2009 Automatch (Xbox 360) f -2328 ufc09x360 Fuf44V UFC 2009 (Xbox 360) f -2330 ufc09x360d \N UFC 2009 Demo (Xbox 360) f -2331 skateitds eOpDft Skate It (DS) f -2332 robolypsewii qy0bIP Robocalypse (WiiWare) f -2333 puffinsds ckiZ8C Puffins: Island Adventures (DS) f -2420 sawps3 HtiBX3 SAW (PS3) f -2239 redalert3pcbam \N Red Alert 3 Beta (PC) Automatch f -2337 wwpuzzlewii lWG4l1 Simple: The Number - Puzzle f -2338 snightxds sJZwCL Summon Night X (DS) f -2339 hotrodwii L0kIVL High Voltage Hod Rod Show (WiiWare) f -2357 fuelpcam \N FUEL Automatch (PC) f -2341 spbobbleds OFA2iI Space Puzzle Bobble (DS) f -2343 bbarenaEUps3am w6gFKv Supersonic Acrobatic Rocket-Powered BattleCars Automatch (PSN) (EU) f -2344 bbarenaJPNps3 CwiTIz Supersonic Acrobatic Rocket-Powered BattleCars (PSN) (JPN) f -2345 bbarenaJPNps3am CwiTIz Supersonic Acrobatic Rocket-Powered BattleCars Automatch (PSN) (JPN) f -2346 girlssecEUds nySkKx Winx Club Secret Diary 2009 (EU) (DS) f -2347 ffccechods qO9rGZ Final Fantasy Crystal Chronicles: Echos of Time (Wii/DS) f -2348 unbballswii lZTqHE Unbelievaballs (Wii) f -2349 hokutokenwii dRn94f Hokuto no Ken (WiiWare) f -2350 monracersds Uo295H Monster Racers (DS) f -2351 tokyoparkwii 4Fx0VT Tokyo Friend Park II Wii (Wii) f -2352 derbydogwii I8HL3T Derby Dog (WiiWare) f -2354 bbarenaJPps3d CwiTIz Supersonic Acrobatic Rocket-Powered BattleCars Demo (PSN) (JPN) f -2355 ballarenaps3d W8bW5s Supersonic Acrobatic Rocket-Powered BattleCars: BattleBall Arena Demo f -2356 fuelpc UOXvsa FUEL (PC) f -2360 fuelps3am \N FUEL Automatch (PS3) f -2358 fuelpcd UOXvsa FUEL Demo (PC) f -2359 fuelps3 T8IuLe FUEL (PS3) f -2363 mleatingJPwiiam \N Major League Eating: The Game Automatch (JPN) (WiiWare) f -2361 fuelps3d T8IuLe FUEL Demo (PS3) f -2362 mleatingJPwii 4T0Tcg Major League Eating: The Game (JPN) (WiiWare) f -2366 sbkUSps3am \N SBK '08 Automatch (US) (PS3) f -2364 octoEUwii vRdiaE Octomania (EU) (Wii) f -2365 sbkUSps3 q8Bupt SBK '08 (US) (PS3) f -2367 sbkUSps3d \N SBK '08 Demo (US) (PS3) f -2369 sbkUSpcam \N SBK '08 Automatch (US) (PC) f -2368 sbkUSpc 6Q9XqQ SBK '08 (US) (PC) f -2370 sbkUSpcd \N SBK '08 Demo (US) (PC) f -2375 redalert3pccdam \N Red Alert 3 Automatch (PC, CDKey) f -2371 medarotds n8UPyi MedaRot DS (DS) f -2373 ekorisu2ds FzODdr Ekorisu 2 (DS) f -2374 redalert3pccd uBZwpf Red Alert 3 (PC, CDKey) f -2381 legofwreps3am \N WWE Legends of Wrestlemania Automatch (PS3) f -2376 bbladeds 7TUkXB Bay Blade (DS) f -2379 texasholdwii PEsKhp Texas Hold'em Tournament (WiiWare) f -2380 legofwreps3 PyTirM WWE Legends of Wrestlemania (PS3) f -2383 legofwrex360am \N Legends of Wrestlemania Automatch (Xbox 360) f -2382 legofwrex360 VuPdJX WWE Legends of Wrestlemania (Xbox 360) f -2396 mkvsdcEUps3am \N Mortal Kombat vs. DC Universe Automatch (EU) (PS3) f -2384 warnbriads mmOyeL Warnbria no Maho Kagaku (DS) f -2385 tsurimasterds BM8WEh Mezase!! Tsuri Master DS (DS) f -2386 jikkyonextwii 6WH0CV Jikkyo Powerful Pro Yakyu NEXT (Wii) f -2387 mua2wii QizM3A Marvel Ultimate Alliance 2: Fusion (Wii) f -2388 civrevasiaps3 xUfwlE Civilization Revolution (Asia) (PS3) f -2391 pocketwrldds 7Lx2fU My Pocket World (DS) f -2393 segaracingwii 4ue8Ke Sega Superstars Racing (Wii) f -2394 3dpicrossds uhXkFl 3D Picross (DS) f -2395 mkvsdcEUps3 r7TauG Mortal Kombat vs. DC Universe (EU) (PS3) f -2397 mkvsdcEUps3b \N Mortal Kombat vs. DC Universe Beta (EU) (PS3) f -2399 mkvsdcps3bam \N Mortal Kombat vs. DC Universe Beta Automatch (PS3) f -2398 mkvsdcps3b XqrAqV Mortal Kombat vs. DC Universe Beta (PS3) f -2411 im1pcam \N Interstellar Marines Automatch (PC) f -2400 liightwii VveRkG Liight (WiiWare) f -2401 mogumonwii yKTavT Tataite! Mogumon (WiiWare) f -2403 mini4wdds XMGZia Mini 4WD DS (DS) f -2404 puzzshangwii 33NTu6 Puzzle Games Shanghai Wii (WiiWare) f -2405 crystalw1wii U9J7QC Crystal - Defender W1 (WiiWare) f -2406 crystalw2wii AqCvfz Crystal - Defender W2 (WiiWare) f -2407 overturnwii GLXJR8 Overturn (WiiWare) f -2408 vtennisacewii ptSNgI Virtua Tennis: Ace (Wii) f -2409 yugioh5dds lwG6md Yu-Gi-Oh 5Ds (DS) f -2416 50ctsndlvps3am \N 50 Cent: Blood on the Sand - Low Violence Automatch (PS3) f -2412 im1pcd uRd8zg Interstellar Marines Demo (PC) f -2413 civrevasips3d xUfwlE Civilization Revolution Demo (Asia) (PS3) f -2414 civrevoasiads QXhNdz Sid Meier's Civilization Revolution (DS, Asia) f -2418 sawpcam \N SAW Automatch (PC) f -2417 sawpc ik9k6R SAW (PC) f -2421 sawps3am \N SAW Automatch (PS3) f -2422 sawps3d \N SAW Demo (PS3) f -2427 biahhJPps3am \N Brothers In Arms: Hell's Highway Automatch (PS3) (JPN) f -2423 ssmahjongwii r0AyOn Simple Series: The Mah-Jong (WiiWare) f -2424 carnivalkwii iTuqoN Carnival King (WiiWare) f -2425 pubdartswii nzFWQs Pub Darts (WiiWare) f -2426 biahhJPps3 oAEUPB Brothers In Arms: Hell's Highway (PS3) (JPN) f -2428 biahhJPps3d \N Brothers In Arms: Hell's Highway Demo (PS3) (JPN) f -2435 cnpanzers2cwbam \N Codename Panzers 2: Cold Wars BETA Automatch (PC) f -2429 codwawbeta iqEFLl Call of Duty: World at War Beta f -2430 fallout3 iFYnef Fallout 3 f -2431 taprace xJRENu Tap Race (iPhone Sample) f -2433 callofduty5 VycTat Call of Duty 5 f -2434 cnpanzers2cwb uazO6l Codename Panzers 2: Cold Wars BETA (PC) f -2438 biahhPRps3d \N Brothers In Arms: Hell's Highway Demo (PS3) (RUS) f -2436 biahhPRps3 hWpJhQ Brothers In Arms: Hell's Highway (PS3) (RUS) f -2643 pokedngnwii AKikuw Pokemon Dungeon (Wii) f -2340 hotrodwiiam \N High Voltage Hod Rod Show Automatch (WiiWare) f -2441 biahhRUSpc dhkWdE Brothers In Arms: Hell's Highway (PC) (RUS) f -2444 stormrisepcam \N Stormrise Automatch (PC) f -2443 stormrisepc 6zyt4S Stormrise (PC) f -2445 stormrisepcd \N Stormrise Demo (PC) f -2452 psyintdevpcam \N Psyonix Internal Development Automatch (PC) f -2446 stlprinKORds fufc4q Steal Princess (KOR) (DS) f -2447 kaiwanowads NelyD3 KAIWANOWA (DS) f -2448 mvsdk25ds ko7R42 Mario vs Donkey Kong 2.5 (DS) f -2449 stlprinEUds 7SnIvW Steal Princess (EU) (DS) f -2450 gh4metalwii m8snqf Guitar Hero 4: Metallica (Wii) f -2451 psyintdevpc u6vgFK Psyonix Internal Development (PC) f -2453 psyintdevpcd \N Psyonix Internal Development Demo (PC) f -2459 menofwarpcbam \N Men of War Automatch (PC) BETA f -2454 simsracingds iRs4Ck MySims Racing DS (DS) f -2456 evaspacewii m5yEnm Evasive Space (WiiWare) f -2457 spaceremixds Gs1FlI Space Invaders Extreme Remix (DS) f -2458 menofwarpcb mER2kk Men of War (PC) BETA f -2466 srgakuendsam \N Super Robot Gakuen Automatch (DS) f -2460 codwaw LdlpcA Call of Duty: World at War f -2461 kentomashods feZytn Ide Yohei no Kento Masho DS (DS) f -2462 beatrunnerwii CAI5ov Beat Runner (WiiWare) f -2464 rainbowislwii TpzO6m Rainbow Island Tower! (WiiWare) f -2465 srgakuends cK86go Super Robot Gakuen (DS) f -2470 mxravenpspam \N MX Raven Automatch (PSP) f -2467 cstaisends 0LTCe3 Chotto Sujin Taisen (DS) f -2468 winx2010ds JP9RGe Winx Club Secret Diary 2010 (DS) f -2469 mxravenpsp FPpfou MX Reflex (Raven) (PSP) f -2479 guinnesswripham \N Guinness World Records: The Video Game Automatch (iPhone) f -2471 sukashikds uk8Nda Sukashikashipanman DS (DS) f -2472 famista09ds OkJhLi Pro Yakyu Famista DS 2009 (DS) f -2473 hawxpc h6dGAg Tom Clancy's HAWX f -2474 fxtrainingds tXkDai FX Training DS (DS) f -2475 monhuntergwii hyCk2c Monster Hunter G (Wii) f -2476 dinerdashwii 4rTdD2 Diner Dash (WiiWare) f -2477 s_l4d sPEFlr Steam Left 4 Dead f -2480 guinnesswriphd \N Guinness World Records: The Video Game Demo (iPhone) f -2484 biahhPOLps3am \N Brothers In Arms: Hell's Highway Automatch (PS3) (POL) f -2481 konsportswii sJEymO Konami Sports Club @ Home (WiiWare) f -2482 cpenguin2ds 1XafMv Club Penguin 2 (DS) f -2483 biahhPOLps3 zEo2mk Brothers In Arms: Hell's Highway (PS3) (POL) f -2485 biahhPOLps3d \N Brothers In Arms: Hell's Highway Demo (PS3) (POL) f -2490 h2cdigitalps3d \N Hail to the Chimp Demo (PSN) f -2486 exciteracewii WLrMtU Excite Racing (Wii) f -2487 cpenguin2wii Nu3Uqi Club Penguin 2 (Wii) f -2488 tcounterwii HzKrFV Tecmo Counter f -2492 motogp09ps3am \N Moto GP 09 Automatch (PS3) f -2491 motogp09ps3 nQF5x3 Moto GP 09 (PS3) f -2493 motogp09ps3d \N Moto GP 09 Demo (PS3) f -2495 motogp09pcam \N Moto GP 09 Automatch (PC) f -2494 motogp09pc qOspfz Moto GP 09 (PC) f -2496 motogp09pcd \N Moto GP 09 Demo (PC) f -2497 spectro2wii KgKm2x Spectrobes 2 (Wii) f -2499 ninTest/am EdD7Ve Nintendo Development Testing masterID 0 Automatch f -2500 ninTest0 EdD7Ve Nintendo Development Testing masterID 1 f -2501 ninTest0am EdD7Ve Nintendo Development Testing masterID 1 Automatch f -2502 ninTest1 EdD7Ve Nintendo Development Testing masterID 2 f -2503 ninTest1am EdD7Ve Nintendo Development Testing masterID 2 Automatch f -2504 ninTest2 EdD7Ve Nintendo Development Testing masterID 3 f -2506 ninTest3 EdD7Ve Nintendo Development Testing masterID 4 f -2507 ninTest3am EdD7Ve Nintendo Development Testing masterID 4 Automatch f -2508 ninTest4 EdD7Ve Nintendo Development Testing masterID 5 f -2509 ninTest4am EdD7Ve Nintendo Development Testing masterID 5 Automatch f -2510 ninTest5 EdD7Ve Nintendo Development Testing masterID 6 f -2511 ninTest5am EdD7Ve Nintendo Development Testing masterID 6 Automatch f -2512 ninTest6 EdD7Ve Nintendo Development Testing masterID 7 f -2514 ninTest7 EdD7Ve Nintendo Development Testing masterID 8 f -2515 ninTest7am EdD7Ve Nintendo Development Testing masterID 8 Automatch f -2516 ninTest8 EdD7Ve Nintendo Development Testing masterID 9 f -2517 ninTest8am EdD7Ve Nintendo Development Testing masterID 9 Automatch f -2518 ninTest9 EdD7Ve Nintendo Development Testing masterID 10 f -2519 ninTest9am EdD7Ve Nintendo Development Testing masterID 10 Automatch f -2521 ninTest:am EdD7Ve Nintendo Development Testing masterID 11 Automatch f -2522 ninTest; EdD7Ve Nintendo Development Testing masterID 12 f -2524 ninTest< EdD7Ve Nintendo Development Testing masterID 13 f -2525 ninTest EdD7Ve Nintendo Development Testing masterID 15 f -2529 ninTest>am EdD7Ve Nintendo Development Testing masterID 15 Automatch f -2530 ninTest? EdD7Ve Nintendo Development Testing masterID 16 f -2531 ninTest?am EdD7Ve Nintendo Development Testing masterID 16 Automatch f -2532 ninTest@ EdD7Ve Nintendo Development Testing masterID 17 f -2534 ninTest- EdD7Ve Nintendo Development Testing masterID 18 f -2535 ninTest-am EdD7Ve Nintendo Development Testing masterID 18 Automatch f -2603 brigades nUAsKm Gamespy Brigades f -2442 biahhRUSpcam \N Brothers In Arms: Hell's Highway Automatch (PC) (RUS) f -2578 civ4coljp 5wddmt Sid Meier's Civilization IV: Colonization (PC Japanese) f -2537 ninTest.am EdD7Ve Nintendo Development Testing masterID 19 Automatch f -2538 dartspartywii xyHrNT Darts Wii Party (Wii) f -2539 3celsiuswii xR1sEX 3* Celsius (WiiWare) f -2541 Rabgohomewii sngh8x Rabbids Go Home (Wii) f -2542 tmntsmashwii IXIdNe TMNT Smash Up (Wii) f -2543 simplejudowii t4wmAP Simple The Ju-Do (WiiWare) f -2544 menofwarpcd z4L7mK Men of War MP DEMO (PC) f -2548 rdr2ps3am \N Red Dead Redemption Automatch (PS3) f -2547 rdr2ps3 5aL4Db Red Dead Redemption (PS3) f -2550 gh4vhalenwiiam \N Guitar Hero 4: Van Halen Automatch (Wii) f -2549 gh4vhalenwii yDGso1 Guitar Hero 4: Van Halen (Wii) f -2558 sbk09ps3am \N SBK '09 Automatch (PS3) f -2551 escviruswii gWke73 Escape Virus (WiiWare) f -2552 rfactoryKRds dBJ0km Rune Factory: A Fantasy Harverst Moon (KOR) (DS) f -2553 banburadxds 04cR2B Banbura DX Photo Frame Radio (DS) f -2554 mebiuswii T0zyn9 Mebius Drive (WiiWare) f -2556 sbk09pc pQAyX6 SBK '09 (PC) f -2557 sbk09ps3 hxVmss SBK '09 (PS3) f -2559 sbk09pcam \N SBK '09 Automatch (PC) f -2561 poriginpcjpam \N Fear 2: Project Origin Automatch (JP) (PC) f -2560 poriginpcjp w2OQ5I Fear 2: Project Origin (JP) (PC) f -2562 poriginpcjpd \N Fear 2: Project Origin Demo (JP) (PC) f -2565 poriginps3jpd \N Fear 2: Project Origin Demo (JP) (PS3) f -2563 poriginps3jp MF2kB1 Fear 2: Project Origin (JP) (PS3) f -2567 section8pcam \N Section 8 Automatch (PC) f -2566 section8pc 2UMehS Section 8 (PC) f -2568 section8pcd \N Section 8 Demo (PC) f -2570 section8ps3am \N Section 8 Automatch (PS3) f -2569 section8ps3 RGZq4i Section 8 (PS3) f -2571 section8ps3d \N Section 8 Demo (PS3) f -2573 section8x360am \N Section 8 Automatch (Xbox360) f -2572 section8x360 fB8QDw Section 8 (Xbox360) f -2574 section8x360d \N Section 8 Demo (Xbox360) f -2576 buccaneerpcam \N Buccaneer Automatch (PC) f -2575 buccaneerpc vFNtGi Buccaneer (PC) f -2577 buccaneerpcd \N Buccaneer Demo (PC) f -2581 beateratorpspam \N Beaterator Automatch (PSP) f -2580 beateratorpsp VXtdws Beaterator (PSP) f -2582 beateratorpspd \N Beaterator Demo (PSP) f -2586 chesschalwiiam \N Chess Challenge! Automatch (WiiWare) f -2583 sonicrkords PrnrAp Sonic Rush Adventure (KOR) (DS) f -2584 mmadnesswii Ok1Lrl Military Madness (WiiWare) f -2585 chesschalwii EU1zXz Chess Challenge! (WiiWare) f -2595 superv8pcam \N Superstars V8 Racing Automatch (PC) f -2587 narutorev3wii 2bLXrL Naruto Shippuden: Clash of Ninja Revolution 3 (Wii) f -2588 decasport2wii pSFeW6 Deca Sports 2 (Wii) f -2589 suparobods fJgMKq Suparobo Gakuen (DS) f -2590 gh4ghitswii lUHbE5 Guitar Hero 4: Greatest Hits (Wii) f -2591 simsraceEUds HmxBFc MySims Racing DS (EU) (DS) f -2592 blockrushwii LbsgGO Blockrush! (WiiWare) f -2593 simsraceJPNds fN26Ba MySims Racing DS (JPN) (DS) f -2596 superv8pcd \N Superstars V8 Racing Demo (PC) f -2598 superv8ps3am \N Superstars V8 Racing Automatch (PS3) f -2597 superv8ps3 0vzJCz Superstars V8 Racing (PS3) f -2599 superv8ps3d \N Superstars V8 Racing Demo (PS3) f -2610 svsr10ps3d \N WWE Smackdown vs. Raw 2010 Demo (PS3) f -2600 boardgamesds fFgBAt The Best of Board Games (DS) f -2601 cardgamesds 6iGEJe The Best of Card Games (DS) f -2602 colcourseds T9aQ3K Collision Course (DS) f -2605 qsolace MjcwlP Quantum of Solace f -2606 tcendwar wNPcIq Tom Clancy's EndWar f -2607 kidslearnwii ws94sA Kids Learning Desk (WiiWare) f -2608 svsr10ps3 XcUkIx WWE Smackdown vs. Raw 2010 (PS3) f -2612 svsr10x360am \N WWE Smackdown vs. Raw 2010 Automatch (Xbox 360) f -2611 svsr10x360 ONqHu9 WWE Smackdown vs. Raw 2010 (Xbox 360) f -2613 svsr10x360d \N WWE Smackdown vs. Raw 2010 Demo (Xbox 360) f -2616 cardherodsam \N Card Hero DSi Automatch (DS) f -2614 momo2010wii 2lbGXb Momotaro Dentetsu 2010 Nendoban (Wii) f -2615 cardherods FRzL49 Card Hero DSi (DS) f -2618 smball2ipham \N Super Monkey Ball 2 Automatch (iPhone) f -2617 smball2iph cqWhHg Super Monkey Ball 2 (iPhone) f -2619 smball2iphd \N Super Monkey Ball 2 Demo (iPhone) f -2622 beateratoriphd \N Beaterator Demo (iPhone) f -2620 beateratoriph qV4GA6 Beaterator (iPhone) f -2633 bderlandspcam \N Borderlands Automatch (PC) f -2623 conduitwii GTd9OX The Conduit (Wii) f -2624 hookagainwii 7LR7m6 Hooked Again! (Wii) f -2625 rfactory3ds JpHDcA Rune Factory 3 (DS) f -2626 disneydev ZpO4Dp Disney Development/Testing f -2627 disneydevam ZpO4Dp Disney Development/Testing Automatch f -2628 sporearenads mhxKle Spore Hero Arena (DS) f -2629 treasurewldds cKei7w Treasure World (DS) f -2630 unowii 2hUZSq UNO (WiiWare) f -2632 bderlandspc a2Lg16 Borderlands (PC) f -2634 bderlandspcd \N Borderlands Demo (PC) f -2637 bderlandsps3d \N Borderlands Demo (PS3) f -2635 bderlandsps3 Z1kXis Borderlands (PS3) f -2639 bderlands360am \N Borderlands Automatch (360) f -2638 bderlandsx360 1Eu2fy Borderlands (360) f -2640 bderlandsx360d \N Borderlands Demo (360) f -2641 simsportsds Qw1de8 MySims Sports (DS) f -2642 simsportswii T18tBM MySims Sports (Wii) f -2545 menofwarpcdam \N Men of War MP DEMO Automatch (PC) f -2645 arma2pcam zbMmN3 Arma II Automatch (PC) f -2646 arma2pcd zbMmN3 Arma II Demo (PC) f -2648 quizmagic2ds JGqTW6 Quiz Magic Academy DS2 (DS) f -2649 bandbrosEUds WrU6Ov Daiggaso! Band Brothers DX (EU) (DS) f -2650 swsnow2wii 2cPMrL Shaun White Snowboarding 2 (Wii) f -2651 scribnautsds d4dJKr Scribblenauts (DS) f -2652 fifasoc10ds ZULq4H FIFA Soccer 10 (DS) f -2653 foreverbl2wii Ly8iAL Forever Blue 2 (Wii) f -2654 namcotest hNdo7u Namco SDK Test f -2655 namcotestam hNdo7u Namco SDK Test Automatch f -2656 namcotestd hNdo7u Namco SDK Test Demo f -2657 blindpointpc IGbJEs Blind Point (PC) f -2671 beateratoram \N Beaterator Automatch (PSP/iphone) f -2660 propocket12ds 98gFV2 PowerPro-kun Pocket 12 (DS) f -2661 seafarmwii tNQRr7 Seafarm (WiiWare) f -2662 dragquestsds r6ToyA Dragon Quest S (DSiWare) f -2663 dawnheroesds HpsSGM Dawn of Heroes (DS) f -2664 monhunter3wii mO984l Monster Hunter 3 (JPN) (Wii) f -2665 appletest TZHVox Apple SDK test f -2667 appletestd TZHVox Apple SDK test Demo f -2668 harbunkods renLKS Harlequin Bunko (DS) f -2669 unodsi w2G3ae UNO (DSiWare) f -2670 beaterator VXtdws Beaterator (PSP/iphone) f -2672 beateratord \N Beaterator Demo (PSP/iphone) f -2676 ascensionpcam \N Ascension Automatch (PC) f -2674 dragoncrwnwii y4QTvo Dragon's Crown (Wii) f -2675 ascensionpc 1aT6fS Ascension (PC) f -2677 ascensionpcd \N Ascension Demo (PC) f -2680 swbfespspd \N Star Wars: Battlefront - Elite Squadron Demo (PSP) f -2678 swbfespsp wLfbMH Star Wars: Battlefront - Elite Squadron (PSP) f -2693 luchalibrepcam \N Lucha Libre AAA 2010 Automatch (PC) f -2694 luchalibrepcd \N Lucha Libre AAA 2010 Demo (PC) f -2681 nba2k10wii qWpDTI NBA 2K10 (Wii) f -2682 nhl2k10wii UzhSDM NHL 2K10 (Wii) f -2683 mk9test a0GZNV Midway MK9 Test f -2685 mk9testd a0GZNV Midway MK9 Test Demo f -2686 kateifestds kJcEq8 Katei Kyoshi Hitman Reborn DS Vongole Festival Online (DS) f -2687 luminarc2EUds lJsN7I Luminous Arc 2 Will (EU) (DS) f -2688 tatvscapwii eJMWz4 Tatsunoko vs. Capcom Ultimate All Stars (Wii) f -2689 petz09ds kLg8PL Petz Catz/Dogz/Hamsterz/Babiez 2009 (DS) f -2690 rtlwsportswii flKPhR RTL Winter Sports 2010 (Wii) f -2691 tomenasawii r15HmN Tomenasanner (WiiWare) f -2696 luchalibreps3am \N Lucha Libre AAA 2010 Automatch (PS3) f -2697 luchalibreps3d \N Lucha Libre AAA 2010 Demo (PS3) f -2695 luchalibreps3 DNbubV Lucha Libre AAA 2010 (PS3) f -2700 ludicrouspcam \N Ludicrous Automatch (PC) f -2701 ludicrouspcd \N Ludicrous Demo (PC) f -2698 simsflyerswii d5wfc2 MySims Flyers (Wii) f -2699 ludicrouspc JH70r6 Ludicrous (PC) f -2704 ludicrousmacd \N Ludicrous Demo (MAC) f -2713 orderofwarpcam \N Order of War Automatch (PC) f -2702 ludicrousmac P99WDn Ludicrous (MAC) f -2714 orderofwarpcd \N Order of War Demo (PC) f -2705 pbellumr1 CXabGK Parabellum Region 1 (PC) f -2706 pbellumr2 CXabGK Parabellum Region 2 (PC) f -2707 pbellumr3 CXabGK Parabellum Region 3 (PC) f -2708 imaginejdds Co6Ih6 Imagine: Jewelry Designer (DS) f -2709 imagineartds Jb87QW Imagine: Artist (DS) f -2711 sballrevwii emMKr3 Spaceball: Revolution (WiiWare) f -2712 orderofwarpc P8pcV7 Order of War (PC) f -2721 fairyfightps3am \N Fairytale Fights Automatch (PS3) f -2722 fairyfightps3d \N Fairytale Fights Demo (PS3) f -2715 lbookofbigsds zTtFaT Little Book of Big Secrets (DS) f -2716 scribnauteuds 5aGp82 Scribblenauts (EU) (DS) f -2717 buccaneer sAhRTM Buccaneer The Pursuit of Infamy f -2718 kenteitvwii uGRdPx Kentei! TV Wii (Wii) f -2720 fairyfightps3 qTLu9D Fairytale Fights (PS3) f -2724 fairyfightpcam \N Fairytale Fights Automatch (PC) f -2725 fairyfightpcd \N Fairytale Fights Demo (PC) f -2723 fairyfightpc R6JnVy Fairytale Fights (PC) f -2728 50centjpnps3d \N 50 Cent: Blood on the Sand Demo (JPN) (PS3) f -2736 bomberman2wiid \N Bomberman 2 Demo (Wii) f -2726 50centjpnps3 ZmGGQs 50 Cent: Blood on the Sand (JPN) (PS3) f -2744 section8pcbam \N Section 8 Beta Automatch (PC) f -2729 codmw2ds 0DzDcW Call of Duty: Modern Warfare 2 (DS) f -2730 jbond2009ds asL1Wh James Bond 2009 (DS) f -2731 resevildrkwii qBhaV0 Resident Evil: The Darkside Chronicles (Wii) f -2732 musicmakerwii wDFJt2 Music Maker (Wii) f -2733 figlandds eIDvPq Figland (DS) f -2734 bonkwii QeXwBs Bonk (Wii) f -2735 bomberman2wii mWTGGw Bomberman 2 (Wii) f -2745 section8pcbd \N Section 8 Beta Demo (PC) f -2737 dreamchronwii 2Q2ePF Dream Chronicle (Wii) f -2738 gokuidsi yQLxLL Gokui (DSiWare) f -2739 usingwii 6vcnoA U-Sing (Wii) f -2741 puyopuyo7wii h9HtSg Puyopuyo 7 (Wii) f -2742 winelev10wii cZzNkJ Winning Eleven Play Maker 2010 (Wii) f -2743 section8pcb 2UMehS Section 8 Beta (PC) f -2747 ucardgamesds PpmQVg Ultimate Card Games (DS) f -2748 postpetds 126D8H PostPetDS Yumemiru Momo to Fushigi no Pen (DS) f -2749 mfightbbultds v2cC6e Metal Fight Bay Blade ULTIMATE (DS) f -2750 strategistwii sP7muH Strategist (Wii) f -2751 bmbermanexdsi nhQakb Bomberman Express (DSiWare) f -2659 blindpointpcd \N Blind Point Demo (PC) f -2753 rdr2x360 H1Dgd3 Red Dead Redemption (x360) f -2758 fairyfightspcam \N Fairytale Fights Automatch (PC) f -2757 fairyfightspc BqQzb9 Fairytale Fights (PC) f -2759 fairyfightspcd \N Fairytale Fights Demo (PC) f -2762 stalkercoppcd \N STALKER: Call of Pripyat Demo (PC) f -2760 stalkercoppc LTU2z2 STALKER: Call of Pripyat (PC) f -2764 strategistpcam \N The Strategist Automatch (PC) f -2763 strategistpc a3Nydp The Strategist (PC) f -2765 strategistpcd \N The Strategist Demo (PC) f -2767 strategistpsnam \N The Strategist Automatch (PSN) f -2766 strategistpsn Ep4yXH The Strategist (PSN) f -2768 strategistpsnd \N The Strategist Demo (PSN) f -2771 ufc10ps3am \N UFC 2010 Automatch (PS3) f -2769 tataitemogwii qND9s1 Tataite! Mogumon US/EU (WiiWare) f -2770 ufc10ps3 WFpvzz UFC 2010 (PS3) f -2772 ufc10ps3d \N UFC 2010 Demo (PS3) f -2775 ufc10x360d \N UFC 2010 Demo (x360) f -2773 ufc10x360 oEwztT UFC 2010 (x360) f -2784 wormswiiwaream \N Worms Automatch (WiiWare) f -2776 mmtest F24ooQ Matchmaking Backend Test f -2777 mmtestam F24ooQ Matchmaking Backend Test Automatch f -2778 talesofgrawii WEp7vX Tales of Graces (Wii) f -2779 dynamiczanwii JKoAWz Dynamic Zan (Wii) f -2781 idraculawii v1xcTU iDracula (WiiWare) f -2782 metalfightds noSUQC Metal Fight Bayblade (DS) f -2783 wormswiiware nQV5pT Worms (WiiWare) f -2787 gtacwarspspam \N Grand Theft Auto: Chinatown Wars Automatch (PSP) f -2785 justsingds hwg1XV Just Sing! (DS) f -2786 gtacwarspsp UXrDJm Grand Theft Auto: Chinatown Wars (PSP) f -2788 gtacwarspspd \N Grand Theft Auto: Chinatown Wars Demo (PSP) f -2790 gtacwiphoneam \N Grand Theft Auto: Chinatown Wars Automatch (iPhone) f -2789 gtacwiphone 3NQ6vh Grand Theft Auto: Chinatown Wars (iPhone) f -2791 gtacwiphoned \N Grand Theft Auto: Chinatown Wars Demo (iPhone) f -2799 fuelps3ptchdam \N FUEL Automatch (PS3) Patched version f -2792 trkmaniads VzwMkX Trackmania (DS) f -2793 trkmaniawii 9mdZHR Trackmania (Wii) f -2794 megaman10wii th2moV Mega Man 10 (WiiWare) f -2795 aarmy3 zwAbg5 America's Army 3 f -2797 sinpunish2wii B2Tcgk Sin & Punishment 2 (Wii) f -2798 fuelps3ptchd T8IuLe FUEL (PS3) Patched version f -2802 demonforgeps3am \N Demon's Forge Automatch (PS3) f -2800 sonicdlwii DkJwkG Sonic DL (WiiWare) f -2801 demonforgeps3 9Cpt5m Demon's Forge (PS3) f -2803 demonforgeps3d \N Demon's Forge Demo (PS3) f -2806 demonforgepcd \N Demon's Forge Demo (PC) f -2804 demonforgepc XEuc92 Demon's Forge (PC) f -2811 maxpayne3pcam \N Max Payne 3 Automatch (PC) f -2807 hooploopwii 4b2QnG HooperLooper (WiiWare) f -2809 test1 ThAO8k test1 f -2810 maxpayne3pc qyAD44 Max Payne 3 (PC) f -2812 maxpayne3pcd \N Max Payne 3 Demo (PC) f -2814 maxpayne3ps3am \N Max Payne 3 Automatch (PS3) f -2813 maxpayne3ps3 QN8v5P Max Payne 3 (PS3) f -2817 maxpayne3x360am \N Max Payne 3 Automatch (360) f -2816 maxpayne3x360 28xd4T Max Payne 3 (360) f -2818 maxpayne3x360d \N Max Payne 3 Demo (360) f -2832 bädmasterid \N bädmasterid f -2819 wordjongeuds 3rwTkL Wordjong EU (DS) f -2820 sengo3wii Esqv7G Sengokumuso 3 f -2821 bewarewii iTHrhz Beware (WiiWare) f -2822 hinterland FZNxKf Hinterland f -2824 rockstarsclub 2MJPhH Rockstar Social Club f -2825 rockstarsclubam 2MJPhH Rockstar Social Club Automatch f -2826 plandmajinds BThDbL Professor Layton and Majin no Fue (DS) f -2827 powerkoushds nTHkC7 Powerful Koushien (DS) f -2828 cavestorywii tWThgd Cave Story (WiiWare) f -2829 blahblahtest uH88tT Just another test for masterid f -2830 blahtest uH88tT Just another test for masterid f -2831 blahmasterid uH88tT Just another test for masterid f -2833 explomäntest \N blah f -2845 superv8ncpcd \N Superstars V8 Next Challenge Demo (PC) f -2836 3dpicrosseuds UAX3WC 3D Picross (EU) (DS) f -2838 narutor3euwii 64ncJ9 Naruto Shippuden: Clash of Ninja Revolution 3 EU (Wii) f -2840 sparta2pc JfHMee Sparta 2: The Conquest of Alexander the Great (PC) f -2841 sparta2pcam JfHMee Sparta 2: The Conquest of Alexander the Great Automatch (PC) f -2842 sparta2pcd JfHMee Sparta 2: The Conquest of Alexander the Great Demo (PC) f -2843 superv8ncpc 4fKWpe Superstars V8 Next Challenge (PC) f -2847 superv8ncps3am \N Superstars V8 Next Challenge Automatch (PS3) f -2846 superv8ncps3 eLgtAp Superstars V8 Next Challenge (PS3) f -2848 superv8ncps3d \N Superstars V8 Next Challenge Demo (PS3) f -2850 ikaropcam \N Ikaro Automatch (PC) f -2849 ikaropc kG5bEO Ikaro (PC) f -2851 ikaropcd \N Ikaro Demo (PC) f -2853 ufc10ps3DEVam \N UFC 2010 DEV Automatch (PS3-DEV) f -2852 ufc10ps3DEV 2gN8O2 UFC 2010 DEV (PS3-DEV) f -2854 ufc10ps3DEVd \N UFC 2010 DEV Demo (PS3-DEV) f -2856 ufc10x360devam \N UFC 2010 DEV Automatch (360-DEV) f -2855 ufc10x360dev h2SP6e UFC 2010 DEV (360-DEV) f -2857 ufc10x360devd \N UFC 2010 DEV Demo (360-DEV) f -2862 foxtrotpcd \N Foxtrot Demo (PC) f -2858 ragonlinenads k6p7se Ragunaroku Online DS (NA) (DS) f -2859 hoopworldwii mZSW86 Hoopworld (Wii) f -2860 foxtrotpc lTvP98 Foxtrot (PC) f -2863 civ5 kB4qBk Civilization 5 f -2754 rdr2x360am \N Red Dead Redemption Automatch (x360) f -2866 sbkxpc P8ThQm SBK X: Superbike World Championship (PC) f -2868 sbkxpcd \N SBK X: Superbike World Championship Demo (PC) f -2870 sbkxps3am \N SBK X: Superbike World Championship Automatch (PS3) f -2869 sbkxps3 BCvlzO SBK X: Superbike World Championship (PS3) f -2871 sbkxps3d \N SBK X: Superbike World Championship Demo (PS3) f -2879 painkresurrpcam \N Painkiller Resurrection Automatch (PC) f -2872 famista2010ds bdhXZm Famista 2010 (DS) f -2873 bokutwinvilds z9VMe9 Bokujyo Monogatari Twin Village (DS) f -2874 destruction vt3f71 Destruction 101 (Namco Bandai) f -2876 lumark3eyesds 65yvsC Luminous Ark 3 Eyes (DS) f -2877 othellowii uV8aBd Othello (WiiWare) f -2878 painkresurrpc tmQ4wN Painkiller Resurrection (PC) f -2880 painkresurrpcd \N Painkiller Resurrection Demo (PC) f -2884 svsr11ps3am \N Smackdown vs Raw 2011 Automatch (PS3) f -2881 fantcubewii 2wDUcM Fantastic Cube (WiiWare) f -2882 3dpicrossUSds 2IOxzX 3D Picross (US) (DS) f -2885 svsr11ps3d \N Smackdown vs Raw 2011 Demo (PS3) f -2887 svsr11x360am \N Smackdown vs Raw 2011 Automatch (x360) f -2886 svsr11x360 4q9ULG Smackdown vs Raw 2011 (x360) f -2888 svsr11x360d \N Smackdown vs Raw 2011 Demo (x360) f -2890 bderlandruspcam \N Borderlands RUS Automatch (PC) f -2889 bderlandruspc Pe4PcU Borderlands RUS (PC) f -2891 bderlandruspcd \N Borderlands RUS Demo (PC) f -2894 krabbitpcmacd \N KrabbitWorld Origins Demo (PC/Mac) f -2892 krabbitpcmac Jf9OhT KrabbitWorld Origins (PC/Mac) f -2902 lanoireps3am \N L.A. Noire Automatch (PS3) f -2895 gunnylamacwii CeF2yx GUNBLADE NY & L.A. MACHINEGUNS (Wii) f -2896 rbeaverdefwii 6k1gxH Robocalypse - Beaver Defense (WiiWare) f -2897 surkatamarwii TgGSxT Surinukeru Katamari (WiiWare) f -2898 snackdsi zrSxhe Snack (DSiWare) f -2899 rpgtkooldsi NaGK7x RPG tkool DS (DSi) f -2900 mh3uswii IwkoVF Monster Hunter 3 (US/EU) (Wii) f -2901 lanoireps3 yPpSqe L.A. Noire (PS3) f -2903 lanoireps3d \N L.A. Noire Demo (PS3) f -2906 lanoirex360d \N L.A. Noire Demo (x360) f -2904 lanoirex360 fKw37T L.A. Noire (x360) f -2908 lanoirepcam \N L.A. Noire Automatch (PC) f -2907 lanoirepc sx37ex L.A. Noire (PC) f -2909 lanoirepcd \N L.A. Noire Demo (PC) f -2918 necrolcpcam \N NecroVisioN: Lost Company Automatch (PC) f -2910 digimonsleds mB26Li Digimon Story Lost Evolution (DS) f -2911 syachi2ds tXH2sN syachi 2 (DS) f -2912 puzzleqt2ds hMqc5z Puzzle Quest 2 (DS) f -2914 decasport3wii rKsv8q Deca Sports 3 (Wii) f -2915 tetrisdeluxds LEtvxd Tetris Party Deluxe (DSiWare) f -2916 gsiphonefw FaI3pa GameSpy iPhone Framework f -2917 necrolcpc JFKyCM NecroVisioN: Lost Company (PC) f -2919 necrolcpcd \N NecroVisioN: Lost Company Demo (PC) f -2921 startrekmacam \N Star Trek Automatch (MAC) f -2920 startrekmac nbxWDg Star Trek: D-A-C (MAC) f -2929 scribnaut2pcam \N Scribblenauts 2 Automatch (PC) f -2922 captsubasads A738z3 Captain tsubasa (DS) f -2923 cb2ds V47Nu4 CB2 (DS) f -2925 cardiowrk2wii ByKsx6 Cardio Workout 2 (Wii) f -2926 boyvgirlcwii gWFTR4 Boys vs Girls Summer Camp (Wii) f -2927 keenracerswii 9McTZh Keen Racers (WiiWare) f -2928 scribnaut2pc 6P7Qdd Scribblenauts 2 (PC) f -2931 agentps3am \N Agent Automatch (PS3) f -2930 agentps3 8me2Ja Agent (PS3) f -2937 svsr11x360devam \N Smackdown vs Raw 2011 DEV Automatch (x360) f -2932 girlskoreads QiFGmi Girls_Korea (DS) f -2934 protocolwii Hd4g3T Protocol (WiiWare) f -2935 DeathtoSpies LOhgNO Death to Spies f -2936 svsr11x360dev h5DZhP Smackdown vs Raw 2011 DEV (x360) f -2939 svsr11ps3devam \N Smackdown vs Raw 2011 DEV Automatch (PS3) f -2938 svsr11ps3dev gSTArg Smackdown vs Raw 2011 DEV (PS3) f -2942 molecontrolpcam \N Mole Control Automatch (PC) f -2940 dynaztrialwii QyQTgC Dynamic Zan TRIAL (Wii) f -2941 molecontrolpc LqpHUN Mole Control (PC) f -2946 na2rowpcam \N NAT2 Row Automatch (PC) f -2943 sakwcha2010ds a92bdC Sakatsuku DS WorldChallenge 2010 (DS) f -2944 MenofWar AkxMQE Men of War f -2945 na2rowpc mxw6bp NAT2 Row (PC) f -2948 na2runpcam \N NAT2 Run Automatch (PC) f -2947 na2runpc eDCC2L NAT2 Run (PC) f -2959 combatzonepcd \N Combat Zone - Special Forces Demo (PC) f -2949 trackmania2ds iaukpU Trackmania DS 2 (DS) f -2951 mysimsflyerds intJay MySims Flyers (DS) f -2952 mysimsflyEUds AhABRa MySims Flyers EU (DS) f -2953 kodawar2010ds dXZiwq Kodawari Saihai Simulation Ochanoma Pro Yakyu DS 2010 Verison (DS) f -2954 topspin4wii 7AzniN TOPSPIN 4 (Wii) f -2955 ut3onlive 7cxD9c Unreal Tournament 3 ONLIVE f -2956 ut3onliveam 7cxD9c Unreal Tournament 3 ONLIVE Automatch f -2957 combatzonepc 3NncWS Combat Zone - Special Forces (PC) f -2963 crysis2pcd \N Crysis 2 Demo (PC) f -2960 sinpun2NAwii cVXGtt Sin & Punishment 2 NA (Wii) f -2962 capricornam XeS9dz Crysis 2 Automatch (PC) f -2964 crysis2ps3 \N Crysis 2 (PS3) f -2966 crysis2ps3d \N Crysis 2 Demo (PS3) f -2965 crysis2ps3am lhgvHv Crysis 2 Automatch (PS3) f -2967 crysis2x360 \N Crysis 2 (Xbox 360) f -2969 crysis2x360d \N Crysis 2 Demo (Xbox 360) f -2968 crysis2x360am A3Xz9h Crysis 2 Automatch (Xbox 360) f -2867 sbkxpcam \N SBK X: Superbike World Championship Automatch (PC) f -3300 capricorn 8TTq4M Crysis 2 (PC) f -68 railty2 T8nM3z Railroad Tycoon II f -226 rrt2scnd fZDYBN Railroad Tycoon 2: The Second Century f -859 railty3 w4D2Ha Railroad Tycoon 3 f -4 bz2 tGbcNv Battlezone II: Combat Commander f -8 drakan zCt4De Drakan: Order of the Flame f -16 heretic2 2iuCAS Heretic II f -20 quake1 7W7yZz Quake f -26 shogo MQMhRK Shogo: Mobile Armor Division f -30 southpark yoI7mE South Park f -39 nerfarena zEh7ir Nerf ArenaBlast f -44 darkreign2 PwE7Nd Dark Reign 2 f -48 scompany EyzWAv Shadow Company f -53 avp WtGzHr Aliens versus Predator f -60 paintball kCVbAZ Paintball f -67 mech3 z8vRn7 Mech Warrior 3 f -75 irl2000 U7tb4Z Indy Racing League 2000 f -112 jetfighter4 M3pL73 Jet Fighter 4: Fortress America f -123 populoustb qik37G Populous: The Beginning f -134 dominos VFHX8a Hasbro's Dominos f -137 pente NeB26l Hasbro's Pente f -143 civ2tot alVRIq Civilization II: Test of Time f -149 gruntz alVRIq Gruntz f -152 wz2100 kD072v Warzone 2100 f -155 baldursg 3MHCZ8 Baludurs Gate f -160 aowdemo alVRIq Age Of Wonders (Demo) f -1421 smrailroads h32mq8 Sid Meier's Railroads! f -1639 smrailroadsjp h32mq8 Sid Meier's Railroads! Japan f -168 rsurbanops 4nHpA3 Rogue Spear: Urban Ops f -174 rallychamp TKuE2P Mobil1 Rally Championship f -181 sofretail iVn3a3 Soldier of Fortune: Retail f -185 fbackgammon Un3apK Small Rockets Backgammon f -192 virtualpool3 NA3vu0 Virtual Pool 3 f -197 frogger ZIq0wX Hasbro's Frogger f -201 amairtac 8dvSTO Army Men - Air Tactics f -208 hhbball2001 5TN9ag High Heat Baseball 2001 f -215 fltsim98 OU0uKn Microsoft Flight Simulator 98 f -222 eawar MIq1wW European Air War f -229 heroes3arm vPkKya Heroes of Might and Magic f -239 bangdemo Hl31zd Bang! Gunship Elite Demo f -247 bgate2 U9b3an Baldur's Gate II: Shadows of Amn f -253 orb Ykd2D3 O.R.B: Off-World Resource Base f -261 aoe2tcdemo wUhCSC Age of Empires II: The Conquerors Demo f -269 bandw KbEab3 Black and White f -276 insane QxZFex Insane f -281 dtrscdmo p2vPkJ Dirt Track Racing: Sprint f -288 wosin Kd29DX SiN: Wages of Sin f -294 close5 XBOEdl Close Combat 5 f -301 deusex Av3M99 Deus Ex f -305 close5dmo V0tKnY Close Combat 5 Demo f -314 runedemo V5Hm41 Rune Demo f -315 suddenstrike vUhCSB Sudden Strike f -319 stefdemo H28D2r Star Trek: Voyager – Elite Force Demo f -327 q3tademo ek2p7z Team Arena Demo f -333 majestyx wUhCTC Majesty Expansion f -340 botbattles Admg3p Tex Atomics Big Bot Battles f -347 crmgdntdr2k W5Hl31 Carmageddon TDR 2000 f -356 bcommander Nm3aZ9 Star Trek: Bridge Commander f -369 Chat09 xQ7fp2 Chat Group 9 f -374 Chat14 xQ7fp2 Chat Group 14 f -380 Chat20 xQ7fp2 Chat Group 20 f -384 legendsmmbeta 5Kbawl Legends of Might and Magic Beta f -396 moonproject YDXBNE Moon Project f -403 gsbgammon PbZ35N GameSpy Backgammon f -411 leadfootd uNctFb Leadfoot Demo f -416 redlinenet OFek2p Redline Multi-Player Inst f -424 disciples2 tKnYBL Disciples 2 f -431 avpnotgold DLiQwZ Aliens vs. Predator f -437 cueballworld sAJtHo Jimmy White Cueball World f -445 diablo blGjuM Diablo f -446 tetrisworlds D3pQe2 Tetris Worlds f -451 rsblackthorn Gh2W6n Rogue Spear: Black Thorn f -458 americax CSCQMZ America Addon f -461 stef1exp zgsCV2 Star Trek: Voyager - Elite Force expansion pack f -471 legendsmmbeta2 5Kbawl Legends of Might and Magic First Look 2 f -479 sfc2dv k7tEH3 Starfleet Command 2: Empires At War Dynaverse f -488 st_highscore KS3p2Q Stats and Tracking Sample f -497 rallychampx h6nLfY Rally Championship Extrem f -504 kohanagdemo Kbao3a Kohan: Ahrimans Gift Demo f -519 masterrally p5jGg6 Master Rally f -525 mechcomm2 6ajiPV MechCommander 2 f -529 swgbd AGh6nM Star Wars: Galactic Battlegrounds Demo f -539 etherlordsd 6ajiOV Etherlords Demo f -549 strongholdd Rp5kGg Stronghold Demo f -557 racedriver Hl31zd TOCA Race Driver f -562 avp2lv Df3M6Z Aliens vs. Predator 2 (Low violence) f -569 serioussamsed AKbna4 Serious Sam: Second Encounter Demo f -578 redalert2exp eRW78c Command & Conquer: Yuri's Revenge f -579 capitalism2 ihPU0u Capitalism 2 f -586 jk2 6ajhOV Star Wars Jedi Knight II: Jedi Outcast f -2972 cellfacttwpcam 4aN3Pn Cell Factor:TW Automatch (PC) f -2975 winel10jpnwii \N Winning Eleven PLAY MAKER 2010 Japan Edition (Wii) f -2974 firearmsevopcam WrgNsZ Firearms Evolution Automatch (PC) f -2981 harmoon2kords \N Harvest Moon 2 Korea (DS) f -2977 bldragonNAds 1mJhT4 Blue Dragon - Awakened Shadow f -2978 bldragonNAdsam JfXyGi Blue Dragon - Awakened Shadow Automatch f -2979 sonic2010wii JfXyGi SONIC 2010 (Wii) f -2980 sonic2010wiiam LhuHFv SONIC 2010 Automatch (Wii) f -2983 jbondmv2ds \N James Bond Non Movie 2 (2010) (DS) f -2982 harmoon2kordsam Gn1cxG Harvest Moon 2 Korea Automatch (DS) f -2985 casinotourwii \N Casino Tournament (Wii) f -2984 jbondmv2dsam 4AiRCn James Bond Non Movie 2 Automatch (2010) (DS) f -2986 casinotourwiiam WykxqZ Casino Tournament Automatch (Wii) f -2973 firearmsevopc \N Firearms Evolution (PC) f -597 mooncommander ziQwZF Moon Commander f -604 medieval L3d8Sh Medieval: Total War f -613 mobileforces g3H6eR Mobile Forces f -619 mobileforcesd g3H6eR Mobile Forces Demo f -626 survivorm ZDXBOF Survivor: Marquesas f -632 warlordsb2 Gg7nLf Warlords Battlecry II f -638 sumofallfearsd RW78cv The Sum of All Fears Demo f -648 gored k2X9tQ Gore Retail Demo f -653 ww2frontline blHjuM World War II: Frontline Command f -662 sfc3 q3k7xH Starfleet Command III f -667 celtickingsdemo TCQMZI Celtic Kings Demo f -674 th2003d G4i3x7 Trophy Hunter 2003 Demo f -683 dtr2d U4iX9e Dirt Track Racing 2 Demo f -693 banditsd H2k9bD Bandits: Phoenix Rising Demo f -701 mostwanted H3kEn7 Most Wanted f -706 thps5ps2 G2k8cF Tony Hawk's Underground (PS2) f -713 bfield1942rtr HpWx9z Battlefield 1942: Road to Rome f -715 netathlon nYALJv NetAthlon f -716 ccgeneralsb g3T9s2 Command & Conquer: Generals Beta f -724 mech4merc q7zgsC MechWarrior 4: Mercenarie f -732 dhunterps2 G2Qvo9 Deer Hunter (PS2) f -738 il2sturmovikfb h53Ew8 IL-2 Sturmovik Forgotten Battles f -747 nolf2d dHg7w3 No One Lives Forever: The Operative 2 Demo f -757 vietnamsod y3Ed9q Line of Sight: Vietnam Demo f -766 wkingsbd agV5Hm Warrior Kings Battles Demo f -776 homeworld2 t38kc9 Homeworld 2 f -780 stef2 MIr1wX Star Trek: Elite Force II f -788 blitz2004ps2b y3G9dJ NFL Blitz Pro 2004 Beta (PS2) f -796 mclub2pc y6E3c9 Midnight Club 2 (PC) f -802 mohaab y32FDc Medal of Honor: Allied Assault Breakthrough f -810 omfbattleb Abm93d One Must Fall Battlegrounds f -822 spacepodd y3R2cD Space Pod Demo f -827 exigo mPBHcI Armies of Exigo f -833 jk3 e4F2N7 Star Wars Jedi Knight: Jedi Academy f -840 empiresd GknAbg Empires: Dawn of the Modern World Demo f -841 empiresdam GknAbg Empires: Dawn of the Modern World f -849 asbball2005ps2 Y3pG1m All-star Baseball 2005 f -869 nwnxp2 ZIq1wW Neverwinter Nights: Hordes of Underdark f -878 mohpa S6v8Lm Medal of Honor: Pacific Assault f -886 kohankow uE4gJ7 Kohan: Kings of War f -895 unreal2d Yel30y Unreal 2 Demo f -902 unreal2demo Yel30y Unreal 2 Demo f -909 halomacd e4Rd9J Halo Demo (Mac) f -915 racedriver2d M29dF4 Race Driver 2 Demo f -923 conan 4J8df9 Conan: The Dark Axe f -928 saturdaynsd psZhzd Saturday Night Speedway Demo f -939 afrikakorpsd tfHGsW Desert Rats vs. Afrika Korps Demo f -951 ganglandd y6F39x Gangland Demo f -957 hotwheels2ps2 u3Fx9h Hot Wheels 2 (PS2) f -964 ravenshieldas vMJRUd Raven Shield: Athena's Sword f -970 mxun05ps2am u3Fs9n MX Unleashed 05 (PS2) (Automatch) f -978 mohaasmac h2P1c9 Medal of Honor: Allied Assault Spearhead (Mac) f -981 bfield1942rtrm HpWx9z Battlefield 1942 Road to Rome (Mac) f -991 exigoam mPBHcI Armies of Exigo (Automatch) f -997 whammer40000am uJ8d3N Warhammer 40,000: Dawn of War f -1007 srsyndps2 A9Lkq1 Street Racing Syndicate (PS2) f -1017 menofvalord kJm48s Men of Valor Demo f -1023 crashnburnps2b gj7F3p Crash N Burn Sony Beta (PS2) f -1031 whammer40kbam uJ8d3N Warhammer 40,000: Dawn of War Beta (Automatch) f -1048 callofdutyps2d tR32nC Call of Duty Sony Beta (PS2) f -1057 exigobam mPBHcI Armies of Exigo Beta (Automatch) f -1063 closecomftfmac iLw37m Close Combat: First to Fight Mac f -1072 callofdutyuo KDDIdK Call of Duty: United Offensive f -1079 smackdnps2palr k7cL91 WWE Smackdown vs RAW (PS2) PAL Retail f -1093 mohpad S6v8Lm Medal of Honor: Pacific Assault Demo f -1105 olvps2 7w2pP3 Outlaw Volleyball PS2 f -1110 spoilsofwaram nZ2e4T Spoils of War (Automatch) f -1119 fswps2pal 6w2X9m Full Spectrum Warrior PAL PS2 f -1124 wcpokerpalps2 t3Hd9q World Championship Poker PAL (PS2) f -1132 swrcommandoj y2s8Fh Star Wars Republic Commando Japanese Dist f -1145 swrcommandot y2s8Fh Star Wars Republic Commando Thai Dist f -1156 actofward LaR21n Act of War: Direct Action Demo f -1165 fswps2kor 6w2X9m Full Spectrum Warrior Korean (PS2) f -1178 bsmidwayps2 qY84Ne Battlestations Midway (PS2) f -1187 swat4xp1 tG3j8c SWAT 4: The Stetchkov Syndicate f -1194 fearobsc n3VBcj FEAR: First Encounter Assault Recon (Open Beta Special Content) f -1208 whammer40kwaam Ue9v3H Warhammer 40,000: Winter Assault (Automatch) f -1225 afllive05ps2 j72Lm2 AFL Live 2005 (ps2) f -1231 rtrooperps2 jK7L92 Rogue Trooper (PS2) f -1236 swempiream t3K2dF Star Wars: Empire at War (Automatch) f -1247 wsoppspam u3hK2C World Series of Poker (PSP) (Automatch) f -1256 bfield2xp1 hW6m9a Battlefield 2: Special Forces f -1264 acrossingdsam h2P9x6 Animal Crossing (DS, Automatch) f -1276 scsdwd agGBzE S.C.S. Dangerous Waters Demo f -1285 bf2sttest NFFtwb Battlefield 2 Snapshot testing f -1305 wofordam mxw9Nu WOFOR: War on Terror Demo Automatch f -1316 marvlegpcdam eAMh9M Marvel Legends Demo Automatch (PC) f -1326 runefactoryds dBOUMT Rune Factory (DS) f -1332 actofwarhtdam LaR21n Act of War: High Treason Demo Automatch f -1340 scsdws hmhQeA S.C.S. Dangerous Waters Steam f -1351 tiumeshiftu NhcH1f TimeShift (Unlock codes) f -1359 narutorpg3ds bBPaXO Naruto RPG 3 (DS) f -1368 rockmanwds sdJvVk Rockman WAVES (DS) f -1374 mmvdkds d8Wm37 Mini Mario vs Donkey Kong (DS) f -1383 whammermok rnbkJp Warhammer: Mark of Chaos (OLD) f -1297 bfield1942ps2am \N Battlefield Modern Combat Automatch (PS2) f -1394 gmtestcdam \N Test Automatch (Chat CD Key validation) f -1405 wsc2007ps2 bpDHED World Snooker Championship 2007 (PS2) f -1412 whammer40kdcam Ue9v3H Warhammer 40,000: Dark Crusade Automatch f -1426 rafcivatwart h98Sqa Rise And Fall: Civilizations at War Test f -1433 jumpsstars2ds VXkOdX Jump Super Stars 2 (DS) f -1443 bandbrosds yvcEXe Daiggaso! Band Brothers DX (DS) f -1448 draculagolds 1VyHxN Akumajou Dracula: Gallery of Labyrinth (DS) f -1461 otonatrainds G8skCH Imasara hitoniwa kikenai Otona no Jyoshikiryoku Training DS (DS) f -1470 wh40kwap Ue9v3H Warhammer 40,000: Winter Assault Patch f -1474 wormsow2ds PHK0dR Worms Open Warfare 2 (DS) f -1494 heroesmanads 8lrZB5 Seiken Densetsu: Heroes of Mana (DS) f -1511 bleach2ds Txc4SQ Bleach DS 2: Requiem in the black robe (DS) f -1525 cc3tibwarsmb GmMKoK Command & Conquer 3: Tiberium Wars Match Broadcast f -1545 dkracingds VBr5Sm Diddy Kong Racing DS (DS) f -1553 sweawfoc oFgIYB Star Wars: Empire at War - Forces of Corruption f -1561 cc3tibwarsam E4F3HB Command & Conquer 3: Tiberium Wars Automatch f -1573 rockstardevam 1a8bBi Rockstar Development Automatch f -1583 springwidgetsam tQfwTW Spring Widgets Automatch f -1589 freessbalpha qXtSmt Freestyle Street Basketball Client Alpha f -1602 motogp2007 oXCZxz MotoGP 2007 f -1608 mariokartkods Uu2GJ4 Mario Kart DS (DS) (KOR) f -1616 civconps3 hn53vx Civilization Revolution (PS3) f -1624 elevenkords qiM82O World Soccer Winning Eleven DS (KOR) (DS) f -1636 jissenpachwii 5tc98w Jissen Pachinko Slot (Wii) f -1646 powerpinconds za0kET Powershot Pinball Constructor (DS) f -1654 pokedungeonds SVbm3x Pokemon Fushigi no Dungeon (DS) f -1670 ardinokingds 6wO62C Ancient Ruler Dinosaur King (DS) f -1690 wsc2007pc L6cr8f World Snooker Championship 2007 (PC) f -1695 momoden16wii TuDtif Momotaro Dentetsu 16 - Hokkaido Daiido no Maki! (Wii) f -1699 runefantasyds 58Ae2N Rune Factory: A Fantasy Harvest Moon (DS) f -1711 onslaughtpcam 8pLvHm Onslaught: War of the Immortals Automatch f -1721 keuthendevam TtEZQR Keuthen.net Development Automatch f -1733 tpfolpc svJqvE Turning Point: Fall of Liberty (PC) f -1741 Digidwndskds SEmI1f Digimon World Dawn/Dusk (DS) f -1750 vanguardsoh QVrtku Vanguard Saga of Heroes f -1757 momotarodends gro5rK Momotaro Dentetsu 16 ~ Hokkaido Daiido no Maki! (DS) f -1767 rachelwood L2muET Rachel Wood Test Game Name f -1774 whamdowfr pXL838 Warhammer 40,000: Dawn of War - Soulstorm f -1782 hookedfishwii q7ghtd Hooked! Real Motion Fishing (Wii) f -1791 quakewarsetb i0hvyr Enemy Territory: Quake Wars Beta f -1799 suddenstrike3 QNiEOS Sudden Strike 3: Arms for Victory f -1802 dfriendsEUds AoJWo6 Disney Friends DS (EU) f -1804 suitelifeds q3Vrvd Suite Life of Zack & Cody: Circle of Spies (DS) f -1812 bokujyods O5ZdFP Bokujyo Monogatari Himawari Shoto wa Oosawagi! (DS) f -1824 WSWeleven07ds sb2kFV World Soccer Winning Eleven DS 2007 (DS) f -1831 suitelifeEUds 7AyK8d Suite Life of Zack & Cody: Circle of Spies (EU) (DS) f -1841 birhhpcam sPZGCy Brothers In Arms: Hell's Highway Automatch (PC) f -1850 greconawf2g pdhHKC Ghost Recon Advanced Warfighter 2 f -1859 MOHADemo rcLGZj Medal of Honor Airborne Demo f -1867 painkillerodam zW4TsZ Painkiller Overdose Automatch f -1873 wiibombmanwii xx7Mvb Wii Bomberman / WiiWare Bomberman / Bomberman Land Wii 2 (Wii) f -1884 whamdowfrb pXL838 Warhammer 40,000: Dawn of War - Final Reckoning Beta f -1897 painkilleroddam zW4TsZ Painkiller Overdose Demo Automatch f -1908 condemned2bsam kwQ9Ak Condemned 2: Bloodshot Automatch f -1913 jikkyopprowii FVeCbl Jikkyo Powerful Pro Yakyu Wii Kettei ban (Wii) f -1924 whammermocbmam EACMEZ Warhammer: Mark of Chaos - Battle March Automatch f -1948 harmooniohds iCyIlW Harvest Moon : Island of Happiness (US) (DS) f -1959 evosoc08EUwii de5f31 Pro Evolution Soccer 2008 (EU) (Wii) f -1977 blkuzushiwii Fi1p8K THE Block Kuzushi - With the Stage Creation feature (Wii) f -1990 rfactoryEUds CK8ylc Rune Factory: A Fantasy Harverst Moon (EU) (DS) f -2001 mxvatvuPALps2 cps6m8 MX vs ATV Untamed PAL (PS2) f -2010 shirends2ds T5gnTX Fushigi no Dungeon: Furai no Shiren DS2 (DS) f -2011 worldshiftpcb 7gBmF4 WorldShift Beta (PC) f -2016 sangotends yln2Zs Sangokushitaisen Ten (DS) f -2025 wiilinkwii Be3reo Wii Link (Wii) f -2067 legendaryps3 9HaHVD Legendary (PS3) f -2101 cc3tibwarscdam E4F3HB Command & Conquer 3: Tiberium Wars CD Key Auth Automatch f -2117 nakedbrbndds d5ZTKM Naked Brothers Band World of Music Tour (DS) f -2120 legendarypcd WUp2J6 Legendary Demo (PC) f -2125 draglade2ds cTbHQV Custom Beat Battle: Draglade 2 (DS) f -2138 bbangminids ErZPG8 Big Bang Mini (DS) f -2144 cod5victoryds z8ooR0 Call of Duty 5: Victory (DS) f -2151 digichampUSds TiuO7K Digimon Championship (US) (DS) f -2163 swbfront3ps3 y3AEXC Star Wars Battlefront 3 (PS3) f -2186 toribashwii 3wygG8 Toribash (WiiWare) f -1504 codedarmspspam \N Coded Arms Automatch (PSP) f -1663 roguewarpcd \N Rogue Warrior Demo (PC) f -1679 gta4ps3am \N Grand Theft Auto 4 Automatch (PS3) f -1935 civ4btsjpam \N Civilization IV: Beyond the Sword Automatch (Japanese) f -1970 mmadnessexps3am \N Monster Madness EX Automatch (PS3) f -2028 tpfolEUpcam \N Turning Point: Fall of Liberty Automatch (EU) (PC) f -2047 damnationps3am \N DamNation Automatch (PS3) f -2054 mclub4ps3devam \N Midnight Club 4 Dev Automatch (PS3) f -2074 beijing08pcd \N Beijing 2008 Demo (PC) f -2085 heistpcam \N Heist Automatch (PC) f -2172 50centsandps3am \N 50 Cent: Blood on the Sand Automatch (PS3) f -2193 poriginpcd \N Fear 2: Project Origin Demo (PC) f -2200 bballarenaps3am W8bW5s Supersonic Acrobatic Rocket-Powered BattleCars: BattleBall Arena Automatch f -2220 svsr09x360 Pzhfov WWE Smackdown vs. RAW 2009 (Xbox 360) f -2222 cod5wii XSq2xz Call of Duty 5 (Wii) f -2228 redalert3pcmb uBZwpf Red Alert 3 (PC) Match Broadcast f -2248 mkvsdcps3 XqrAqV Mortal Kombat vs. DC Universe (PS3) f -2254 rman2blkredds c2ZOsn Ryusei no Rockman 3: Black Ace / Red Joker (JP) (DS) f -2269 kkhrebornwii 76Trpf Katei Kyoshi Hitman REBORN! Kindan no Yami no Delta (Wii) f -2307 mswinterwii O53Z7t Mario & Sonic at the Olympic Winter Games (Wii) f -2317 chocotokids yfVdWO Shido to Chocobo no Fushigina Dungeon Tokiwasure no Meikyu DS+(DS) f -2334 koinudewii GyW0xG Koinu de Kururin Wii (WiiWare) f -2335 lonposUSwii AtQIeu Lonpos (US) (WiiWare) f -2336 wwkuzushiwii 8EzbpJ SIMPLE THE Block Kuzushi (WiiWare) f -2342 bbarenaEUps3 w6gFKv Supersonic Acrobatic Rocket-Powered BattleCars (PSN) (EU) f -2353 bbarenaEUps3d w6gFKv Supersonic Acrobatic Rocket-Powered BattleCars Demo (PSN) (EU) f -2372 idolmasterds oIRn8T The Idolmaster DS (DS) f -2377 takameijinwii mgiHxl Takahashi Meijin no Boukenshima (WiiWare) f -2392 segaracingds HQKW0J Sega Superstars Racing (DS) f -2402 weleplay09wii Eamkm6 Winning Eleven PLAY MAKER 2009 (Wii) f -2410 im1pc uRd8zg Interstellar Marines (PC) f -2415 50ctsndlvps3 n5qRt7 50 Cent: Blood on the Sand - Low Violence (PS3) f -2439 biahhPCHpc NFBVyk Brothers In Arms: Hell's Highway (PC) (POL/CZE/HUNG) f -2455 airhockeywii vhxMTl World Air Hockey Challenge! (WiiWare) f -2463 hunterdanwii 55Fqd5 Hunter Dan's Triple Crown Tournament Fishing (Wii) f -2478 guinnesswriph euFh7c Guinness World Records: The Video Game (iPhone) f -2489 h2cdigitalps3 HlgicF Hail to the Chimp (PSN) f -2498 ninTest/ EdD7Ve Nintendo Development Testing masterID 0 f -2505 ninTest2am EdD7Ve Nintendo Development Testing masterID 3 Automatch f -2513 ninTest6am EdD7Ve Nintendo Development Testing masterID 7 Automatch f -2523 ninTest;am EdD7Ve Nintendo Development Testing masterID 12 Automatch f -2533 ninTest@am EdD7Ve Nintendo Development Testing masterID 17 Automatch f -2536 ninTest. EdD7Ve Nintendo Development Testing masterID 19 f -2540 acejokerUSds ZS4JZy Mega Man Star Force 3: Black Ace/Red Joker (US) (DS) f -2555 okirakuwii LdIyFm Okiraku Daihugou Wii (WiiWare) f -2594 superv8pc GfQdlV Superstars V8 Racing (PC) f -2604 puyopuyo7ds 1SeVl7 PuyoPuyo 7 (DS/Wii) f -2631 mekurucawii fUq0HT Mekuruca (WiiWare) f -2644 arma2pc zbMmN3 Arma II (PC) f -2647 rubikguidewii nTLw4A Rubik's Puzzle World: Guide (WiiWare) f -2666 appletestam TZHVox Apple SDK test Automatch f -2673 ragonlineKRds fhZRmu Ragunaroku Online DS (KOR) (DS) f -2684 mk9testam a0GZNV Midway MK9 Test Automatch f -2692 luchalibrepc dGu4VZ Lucha Libre AAA 2010 (PC) f -2710 tvshwking2wii pNpvGC TV Show King 2 (WiiWare) f -2719 yugioh5dwii bTL9yI Yu-Gi-Oh! 5D's Duel Simulator (Wii) f -2740 shikagariwii IrKIwG Shikagari (Wii) f -2746 ubraingamesds MzT7MD Ultimate Brain Games (DS) f -2752 blockoutwii 6DPfd2 Blockout (Wii) f -2780 fushigidunds VVNqVT Fushigi no Dungeon Furai no Shiren 4 Kami no Me to Akama no Heso (DS) f -2796 tycoonnyc VgxCbC Tycoon City - New York f -2823 hastpaint2wii yt6N8J Greg Hastings Paintball 2 (Wii) f -2837 gticsfestwii DN9tTG GTI Club Supermini Festa (Wii) f -2864 heroeswii vaKmz5 Heroes (Wii) f -2865 yugiohwc10ds TuaRVH Yu-Gi-Oh! World Championship 2010 (DS) f -2875 destructionam vt3f71 Destruction 101 Automatch f -2883 svsr11ps3 8TMLdH Smackdown vs Raw 2011 (PS3) f -2913 phybaltraiwii nb1GZR Physiofun Balance Trainer (WiiWare) f -2924 katekyohitds 9kXaZG katekyo hitman REBORN! DS FLAME RUMBLE XX (DS) f -2933 jyankenparwii t2ge59 Jyanken (rock-paper-scissors) Party Paradise (WiiWare) f -2950 pangmagmichds ccXnxb Pang: Magical Michael (DS) f -2971 cellfacttwpc bxYYnG Cell Factor:TW (PC) f -2976 winel10jpnwiiam 1mJhT4 Winning Eleven PLAY MAKER 2010 Japan Edition Automatch (Wii) f -856 moutlawned \N Midnight Outlaw Illegal Street Drag Nitro Edition Demo f -1482 marvlegps3pam \N Marvel Legends PAL Automatch (PS3) f -1485 djangosabds \N Bokura No Taiyou: Django & Sabata (DS) f -2288 FlockPCam \N Flock Automatch (PC) f -2297 cellfactorpcam \N CellFactor: Ignition Automatch (PSN) Clone f -2432 tapraceam \N Tap Race Automatch (iPhone Sample) f -2437 biahhPRps3am \N Brothers In Arms: Hell's Highway Automatch (PS3) (POL/RUS) f -2440 biahhPCHpcam \N Brothers In Arms: Hell's Highway Automatch (PC) (POL/CZE/HUNG) f -2564 poriginps3jpam \N Fear 2: Project Origin Automatch (JP) (PS3) f -2579 civ4coljpam \N Sid Meier's Civilization IV: Colonization Automatch (PC Japanese) f -2609 svsr10ps3am \N WWE Smackdown vs. Raw 2010 Automatch (PS3) f -2621 beateratoripham \N Beaterator Automatch (iPhone) f -2636 bderlandsps3am \N Borderlands Automatch (PS3) f -2658 blindpointpcam \N Blind Point Automatch (PC) f -2703 ludicrousmacam \N Ludicrous Automatch (MAC) f -2727 50centjpnps3am \N 50 Cent: Blood on the Sand Automatch (JPN) (PS3) f -2761 stalkercoppcam \N STALKER: Call of Pripyat Automatch (PC) f -2774 ufc10x360am \N UFC 2010 Automatch (x360) f -2805 demonforgepcam \N Demon's Forge Automatch (PC) f -2815 maxpayne3ps3d \N Max Payne 3 Demo (PS3) f -2844 superv8ncpcam \N Superstars V8 Next Challenge Automatch (PC) f -2861 foxtrotpcam \N Foxtrot Automatch (PC) f -2893 krabbitpcmacam \N KrabbitWorld Origins Automatch (PC/Mac) f -2905 lanoirex360am \N L.A. Noire Automatch (x360) f -2958 combatzonepcam \N Combat Zone - Special Forces Automatch (PC) f -2970 ZumaDeluxe \N Zuma Deluxe f -2090 bstrikeotspcam \N Battlestrike: Operation Thunderstorm Automatch (PC) f -2236 cellfactorpsnam \N CellFactor: Ignition Automatch (PSN) f -2679 swbfespspam \N Star Wars: Battlefront - Elite Squadron Automatch (PSP) f -\. - - --- --- TOC entry 3447 (class 0 OID 16405) --- Dependencies: 216 --- Data for Name: grouplist; Type: TABLE DATA; Schema: unispy; Owner: - --- - -COPY unispy.grouplist (groupid, gameid, roomname) FROM stdin; -1 5 daikatana test group -2 1 Newbies -3 1 Experts -4 1 Farm Animals -5 256 Skirmish -6 256 Domination -7 192 Test VP3 Tourney -9 192 this -18 192 b -19 192 b -20 285 Rookies -21 285 Amateurs -22 285 Pros -24 192 b -25 256 Slaughter -26 256 Soul Harvest -27 256 Allied -30 192 LumberJack VP3 Test Tourney -44 192 test6ladder -57 192 asdf -63 192 BillsTest2 -64 192 BillsTest2 -101 192 this -102 308 Beginner -103 308 Intermediate -104 308 Advanced -105 192 this -106 192 hello -107 192 mytest -108 192 arts ladder -109 192 Seans Ladder -110 192 seans test ladder -111 192 Seans Test Ladder -112 192 9-Ball Challenge -113 192 TestOct23 -114 192 abcd -115 192 Lumberjack VP3 Test Tourny #2 -116 192 9-Ball Heaven -117 192 QA Test Ladder -118 192 GSI Test - do not join -119 192 reload test -122 192 Tonys 9-Ball tourney -123 192 Tonys 9-Ball tourney 2 -124 192 Tonys Moved Database Test -125 192 outputdir test tourney -127 192 b -141 192 aphexweb1 test -143 192 registration test -144 192 w -147 192 arts ladder test -148 192 gsi test ladder nov 1 -149 192 test -150 192 arts ladder -151 192 gsi test ladder -152 192 tonys ladder -153 192 9-Ball Ladder: Public Test -154 192 Interplay QA test -155 192 Interplay QA test -156 192 Qa Test 2 -157 192 QA DEDICATED TEST -158 192 TEN BALL - QA TEST -159 192 BILLIARDS -160 192 IP Rotation -161 192 10 BALL - QA TEST -162 192 Savy & Sean -163 192 Willow & Erik -164 192 Savy -165 192 6 Ball game -166 192 VP3 Private Patch Testing -167 192 Patch (#2) Final Testing -168 337 Main Lobby -169 337 {01}Cadet -170 337 {02}Captain -171 337 {03}Admiral -172 412 {01}General Chat -173 412 {04}Teen Chat -176 412 {02}Family And Friends -177 412 {03}College Chat -189 15 {01}Half-Life Room 1 -190 15 {02}Half-Life Room 2 -191 15 {01}Counter-Strike: Special Air Service -192 15 {01}Counter-Strike: GSG-9 -193 15 {01}Counter-Strike: Counter-Terrorist Force -194 15 {01}Counter-Strike: Seal Team 6 -195 15 {11}Firearms Room -196 15 {13}TeamFortress Classic Room -197 15 {10}Day of Defeat Room -198 15 {12}Front Line Force Room -199 412 {01}Action Games -200 412 {02}Role Playing Games -201 412 {03}Strategy Games -202 412 {04}Sports Games -203 412 {05}Simulation Games -204 412 {06}Tactical Games -206 15 {03}Help With Half-Life -207 323 {01}Counter-Terrorist Force -208 323 {01}Seal Team Six -209 323 {02}Help with Counter-Strike -210 22 {02}Quake 3 Veterans Room -211 22 {01}Quake 3 Main Room -212 22 {05}Rocket Arena 3 Room -213 22 {02}Freeze Tag -214 22 {04}Quake 3 Fortress Room -215 22 {06}Threewave CTF -216 22 {07}Urban Terror Beta 2 Room -217 22 {08}Weapons Factory Arena Room -218 22 {01}Excessive Room -219 401 {03}Spades Advanced Lobby -220 401 {01}Spades Newbie Lobby -221 401 {04}Spades Ranked Lobby -222 401 {02}Spades Social Lobby -223 403 {02}Backgammon Ranked Lobby -224 403 {01}Backgammon General Lobby -225 400 Poker Advanced Lobby -226 400 Poker Newbies Lobby -227 400 Poker Ranked Lobby -228 400 Poker Social Lobby -229 402 {01}Hearts General Lobby -230 402 {02}Hearts Ranked Lobby -231 22 {06}Help with Quake 3 -232 22 {05}Team Deathmatch -233 15 {01}Counter-Strike: For Great Justice! -234 15 {01}Counter-Strike: Clan Battle Room -236 15 {13}Action Half-Life Room -241 15 {03}Opposing Force Room -242 15 {04}PlanetHalfLife Arcade Event Lobby -243 401 {05}Spades Tournament Lobby -244 402 {03}Hearts Tournament Lobby -245 403 {03}Backgammon Tournament Lobby -246 192 Squish Test ladder -247 292 Main -248 292 Tournaments -250 361 {01}Geral -251 361 {02}Jogos PC -252 15 {14}Deathmatch Classic -259 414 {01}Everon -260 414 {01}Malden -261 414 {02}Dedicated Servers -262 414 {03}Co-op -263 414 {04}Capture the Flag -264 414 {05}Test Zone -265 361 {03}Jogos Consolas -266 361 {04}Hardware -267 361 {05}Torneios e Eventos -269 361 {06}Comunidade -270 483 THPS3 Internet -271 400 Poker Tournament Lobby -272 22 {03}Orange Smoothie -273 504 Main -274 328 {01}Action -275 328 {02}Roleplaying -276 328 {03}Team (N vs. N) -277 328 {04}Social -278 328 {05}Persistent World Action -279 328 {06}Alternative -280 292 Korean -281 509 The Downs -288 509 Forest Heart -295 509 Tharsis -299 523 Main Lobby -300 523 {01}Cadet -301 523 {02}Captain -302 523 {03}Admiral -303 22 {03}Capture the Flag -304 22 {04}Deathmatch -305 292 Français -306 292 Deutsch -307 292 KIS -308 292 KAG -310 362 {01}Juegos -311 362 {02}Adolescentes -312 362 {03}Encuentros -313 362 {04}Maduritos -314 362 {05}Sexo -315 564 Tony Hawk 2x -316 564 Halo -317 564 NASCAR Heat 2002 -318 568 {01}Eastern Front -319 568 {01}Western Front -320 568 {01}North African Campaign -321 568 {01}Pacific Campaign -322 568 {01}Guadalcanal Campaign -323 568 {01}Soviet Winter Offensive -324 363 {01}Juegos -325 363 {02}Adolescentes -326 363 {03}Encuentros -327 363 {04}Maduritos -328 363 {05}Romance -329 492 {01}Eastern Front -330 492 {01}Western Front -331 492 {02}Round-based Match -332 492 {04}Objective-based Match -333 492 {01}Deathmatch -334 492 {03}Team Match -335 492 {01}North Africa -336 492 {01}Siegfried Line -337 492 {03}Team Match -338 492 {04}Objective-based Match -339 590 (01)Power Plant -340 590 (01)Tiberium Refinery -341 590 (01)Weapons Factory -342 590 (01)Infantry Barracks -343 590 (01)GDI Guard Tower -344 590 (01)Construction Yard -345 590 (01)Hand of Nod -346 590 (01)Nod Airstrip -347 15 {15}Desert Crisis -352 577 (01)Power Plant -353 577 (01)Tiberium Refinery -354 577 (01)Weapons Factory -355 577 (01)Infantry Barracks -356 577 (01)GDI Guard Tower -357 577 (01)Construction Yard -358 577 (01)Hand of Nod -359 577 (01)Nod Airstrip -361 564 Tony Hawk 3 -362 564 MotoGP -363 328 {07}Story -364 328 {08}Story Lite -365 328 {09}Melee (1 vs. N) -366 328 {10}Arena (1 vs. N) -367 328 {11}Persistent World Story -368 328 {12}Solo -369 412 {05}Romance -370 328 {13}Tech Support -371 15 {16}Ricochet -372 564 Australia -373 564 Europe -374 564 United Kingdom -375 610 {01}Infantry -376 610 {01}Combat Engineering -377 610 {01}Combat Operations -378 610 {01}Special Forces -379 610 {01}Armor -380 610 {01}Aviation Operations -381 610 {05}MOUT McKenna -382 610 {05}MOUT McKenna -383 610 {01}Bridge Crossing -384 610 {03}Headquarters Raid -385 610 {04}Insurgent Camp -386 610 {06}Pipeline -387 610 {02}Collapsed Tunnel -388 610 {01}Bridge Crossing -389 675 GroupRoom1 -390 675 GroupRoom2 -391 675 GroupRoom3 -392 675 QuickMatch -393 564 NFL Fever 2003 -394 684 Los Angeles (Newbies) -395 684 Los Angeles (Experts) -396 684 Tokyo (Newbies) -397 684 Tokyo (Experts) -398 684 Paris (Newbies) -399 684 Paris (Experts) -400 684 Battle (Newbies) -401 684 Battle (Experts) -402 671 (01)Allies Lobby -403 671 (01)Allies Lobby -404 671 (01)Axis Lobby -405 671 (01)Axis Lobby -409 617 Rookie -410 617 Intermediate -411 617 Expert -412 541 {01}Axis Lobby -413 541 {01}Allies Lobby -414 541 {03}Pacific Theater -415 541 {02}European Theater -417 541 {04}Russian Theater -418 541 Capture the Flag -419 541 Conquest -420 541 Co-Op -421 541 Team Deathmatch -422 541 {01}African Theater -423 636 Main Lobby -424 636 ATI Tournament Lobby -441 564 TimeSplitters 2 -442 564 Tony Hawk 4 -443 15 {17}Natural Selection -444 716 GroupRoom1 -445 716 GroupRoom2 -446 716 GroupRoom3 -447 564 Deathrow -448 712 {01}General Lobby -449 712 {01}General Lobby -450 712 Free-For-All Servers -451 712 Team Deathmatch -452 712 Round-based Match -453 712 Objective Match -454 712 Tug of War -455 641 {01}General Lobby -456 641 {01}General Lobby -457 641 Free-For-All Servers -458 641 Team Deathmatch -459 641 Round-based Match -460 641 Objective Match -461 641 Tug of War -462 564 MechAssault -463 564 Unreal Championship -464 564 Ghost Recon -471 617 Rated -472 617 Unrated -473 617 Unrated Expert -474 617 Unrated Intermediate -475 617 Unrated Beginner -476 730 Beginner -477 730 Intermediate -478 730 Advanced -479 721 Rated 0 -480 721 Rated 1 -481 721 Rated 2 -483 721 Rated 3 -485 557 Ryan's Room -486 557 Donnie's Room -487 541 Desert Combat -488 713 {01}Allies Lobby -489 713 {01}Axis Lobby -490 713 {01}African Theater -491 713 {02}European Theater -492 713 {03}Italian Theater -493 713 {04}Pacific Theater -494 713 {05}Russian Theater -496 675 GroupRoom4 -497 675 GroupRoom5 -498 675 GroupRoom6 -499 675 GroupRoom7 -500 675 GroupRoom8 -501 675 GroupRoom9 -502 675 GroupRoom10 -503 675 GroupRoom11 -504 675 GroupRoom12 -506 557 Kyle's Room -507 557 Bobby's Room -508 557 Nick's Room -509 557 Linda's Room -510 557 Melanie's Room -511 557 Cannonball's Room -512 557 Paulie's Room -513 557 Cuban Joe's Room -514 541 Galactic Conquest -515 770 Ryan's Room -516 770 Bobby's Room -517 722 Anything Goes -519 722 Team17 -523 722 Professional League -524 722 Elite League -525 721 Rated 4 -527 721 Rated 6 -528 721 Rated 7 -529 721 Rated 8 -530 721 Rated 9 -531 721 Unrated 0 -532 721 Unrated 1 -533 721 Unrated 2 -534 721 Unrated 3 -535 721 Unrated 4 -536 721 Rated 5 -537 721 Unrated 5 -538 721 Unrated 6 -539 721 Unrated 7 -540 721 Unrated 8 -541 721 Unrated 9 -542 765 tier1 -543 765 tier2 -544 765 tier3 -545 765 tier4 -546 765 tier5 -547 776 West -548 776 East -549 776 Europe -550 776 Asia -551 776 Beginner -552 776 Expert -553 15 {18}Vampire Slayer -554 772 Action Room -555 772 Asian Room -556 772 Deathmatch Room -557 797 {01}Main Lobby -558 797 {03}Tour Lobby -559 797 {02}Ladder Lobby -560 564 Wolfenstein Tides of War -561 564 Brute Force -562 541 Eve of Destruction -563 564 Midnight Club 2 -564 564 Moto GP 2 -565 564 Inside Pitch 2003 -566 564 Star Wars: The Clone Wars -567 564 Midtown Madness 3 -568 541 ActionBattlefield -569 792 Conquest Scenarios -570 792 Battle Scenarios -571 823 GroupRoom1 -572 824 Unrated 0 -573 824 Unrated 1 -575 824 Unrated 2 -576 824 Unrated 3 -577 824 Unrated 4 -578 824 Unrated 5 -579 824 Unrated 6 -580 824 Unrated 7 -581 824 Unrated 8 -582 824 Unrated 9 -586 823 GroupRoom2 -587 823 GroupRoom3 -588 823 GroupRoom4 -589 823 GroupRoom5 -590 823 GroupRoom6 -591 823 GroupRoom7 -592 823 GroupRoom8 -593 823 GroupRoom9 -594 823 GroupRoom10 -595 823 GroupRoom11 -596 823 GroupRoom12 -597 823 QuickMatch -598 823 GroupRoom13 -599 840 Advanced -600 840 Intermediate -601 840 Beginner -602 823 GroupRoom14 -606 832 Social Room -607 832 Beginner Room -608 832 Intermediate Room -609 832 Advanced Room -610 851 West -611 851 East -612 851 Europe -613 851 Asia -614 851 Beginner -615 851 Expert -616 22 {09}Urban Terror Beta 3 Room -619 15 {19}The Specialists -620 15 {20}MonkeyStrike -621 15 {21}Earth Special Forces -622 722 Amateur League -623 722 Shopping -624 842 Beginner -625 842 Casual -626 842 Expert -627 842 Elite -628 832 Practice Room -631 871 Newbies -632 871 Pros -633 871 Moto 1 -634 871 Moto 2 -635 845 Zaramoth -636 845 Zaramoth -637 845 Azunai -638 845 Azunai -639 845 Xeria -640 845 Xeria -641 845 Isteru -642 772 Empire Builder Room -643 772 European Room -644 772 Free For All Room -645 772 German Room -646 772 Ladder Room -647 772 No Rush Room -648 772 Tournament Room -649 843 European Public League -650 843 Massive Test Leauge -651 843 North American Public League -652 843 Asian Public League -660 870 LobbyRoom1 -661 870 LobbyRoom2 -662 870 QuickMatch -664 891 Group Room 2 -665 564 Amped 2 -666 564 Crimson Skies -667 564 NFL Fever 2004 -668 564 Soldier of Fortune II -669 564 Ghost Recon: Island Thunder -670 564 Rainbow Six 3 -671 564 Tony Hawk Underground -672 564 Top Spin -673 791 Conquest Scenarios -674 791 Battle Scenarios -675 15 {23}Counter-Strike: Clan Battle Room -676 15 {23}Counter-Strike: For Great Justice! -677 15 {23}Counter-Strike: GSG-9 -678 15 {23}Counter-Strike: Counter-Terrorist Force -679 15 {23}Counter-Strike: Seal Team 6 -680 15 {23}Counter-Strike: Special Air Service -684 793 {01}Main Lobby -685 793 {02}Halo Tournament -696 886 Main -697 886 Tournament -698 868 Main -699 868 Tournament -715 852 Search and Destroy -716 852 Behind Enemy Lines -717 852 Retrieval -718 852 Deathmatch -719 924 Rated 0 -720 924 Rated 1 -721 924 Rated 2 -722 924 Rated 3 -723 924 Rated 4 -724 924 Rated 5 -725 924 Rated 6 -726 924 Rated 7 -727 924 Rated 8 -728 924 Rated 9 -729 924 Unrated 0 -730 924 Unrated 1 -731 924 Unrated 2 -732 924 Unrated 3 -733 924 Unrated 4 -734 924 Unrated 5 -735 924 Unrated 6 -736 924 Unrated 7 -737 924 Unrated 8 -738 924 Unrated 9 -739 806 Headquarters -740 806 Briefing Room -741 806 The Bunker -742 806 Mess Hall -743 946 Room 1 -744 946 Room 2 -745 922 Competitive -746 922 Friendly -747 918 US - Eastern -748 918 US - Central -749 918 US - Western -750 918 Europe - English -751 918 Europe - French -752 918 Europe - Italian -753 918 Europe - German -754 918 Europe - Spanish -755 976 Beginners -756 976 Intermediate -757 908 Beginners -758 908 Experts -759 908 Europe -760 908 America -761 908 Asia -766 960 Casual Play -767 960 Rated Play -768 960 Can of Spam -769 1008 Public Demo League -770 1008 Public Demo League -771 1008 Public Demo League -776 1004 Amateur -786 1004 Rookie -796 1004 Pro -812 1004 Legend -816 946 Room 3 -817 1030 Beginner -818 1030 Intermediate -819 1030 Expert -820 878 News and Events#1 -821 878 News and Events#2 -822 878 News and Events#3 -823 878 News and Events#4 -824 878 News and Events#5 -830 878 Medal of Honor Chat#1 -831 878 Medal of Honor Chat#2 -832 878 Medal of Honor Chat#3 -833 878 Medal of Honor Chat#4 -834 878 Medal of Honor Chat#5 -840 878 EA Chat#1 -841 878 EA Chat#2 -850 878 Tech Support & Help#1 -851 878 Tech Support & Help#2 -860 878 Clan Arena#1 -861 878 Clan Arena#2 -862 878 Clan Arena#3 -863 878 Clan Arena#4 -864 878 Clan Arena#5 -865 878 Clan Arena#6 -866 878 Clan Arena#7 -867 878 Clan Arena#8 -868 878 Clan Arena#9 -869 878 Clan Arena#10 -870 878 Boot Camp Training#1 -871 878 Boot Camp Training#2 -872 878 Boot Camp Training#3 -873 878 Boot Camp Training#4 -874 878 Boot Camp Training#5 -880 878 Officers Club#1 -881 878 Officers Club#2 -882 878 Officers Club#3 -883 878 Officers Club#4 -886 878 Officers Club#5 -890 878 The War Room#1 -891 878 The War Room#2 -892 878 The War Room#3 -893 878 The War Room#4 -894 878 The War Room#5 -900 878 Off Topic Discussion#1 -901 878 Off Topic Discussion#2 -902 878 Off Topic Discussion#3 -903 878 Off Topic Discussion#4 -904 878 Off Topic Discussion#5 -916 1043 Amateur -925 1043 Rookie -935 1043 Pro -951 1043 Legend -955 1042 Conquest -956 1042 King of the Hill -957 1042 Territory Control -960 843 Reliance WebWorld Tournament -963 946 Room 4 -964 946 Room 5 -965 946 Room 6 -966 946 Room 7 -967 946 Room 8 -968 946 Room 9 -969 946 Room 10 -970 1064 Main -971 1064 Tournament -972 955 Airliners -973 955 Adventures -974 955 Bush Flying -975 955 Competitions -976 955 Flight Training -977 955 Fly-Ins -978 955 Free Flight -979 955 Helicopter Ops -983 827 AoX chat -984 827 AoX AUS -985 827 AoX CAN -986 827 AoX CHN -987 827 AoX CZE -988 827 AoX DEU -989 827 AoX ESP -990 827 AoX FRA -991 827 AoX GBR -992 827 AoX HUN -993 827 AoX ITA -994 827 AoX KOR -995 827 AoX POL -996 827 AoX RUS -997 827 AoX USA -998 827 AoX AFRICA -999 827 AoX AMERICA -1000 827 AoX ASIA -1001 827 AoX EUROPE -1002 870 LobbyRoom3 -1003 870 LobbyRoom4 -1004 870 LobbyRoom5 -1005 870 LobbyRoom6 -1006 870 LobbyRoom7 -1007 870 LobbyRoom8 -1008 870 LobbyRoom9 -1009 870 ChatRoom1 -1010 870 ChatRoom2 -1011 870 ChatRoom3 -1012 870 ChatRoom4 -1013 870 ChatRoom5 -1014 870 ChatRoom6 -1015 870 ChatRoom7 -1016 870 ChatRoom8 -1017 870 ChatRoom9 -1018 870 ChatRoom10 -1019 1070 Amateur 01 -1020 1070 Amateur 02 -1021 1070 Amateur 03 -1022 1070 Amateur 04 -1023 1070 Amateur 05 -1029 1070 Rookie 01 -1030 1070 Rookie 02 -1031 1070 Rookie 03 -1032 1070 Rookie 04 -1033 1070 Rookie 05 -1034 1070 Pro 01 -1035 1070 Pro 02 -1036 1070 Pro 03 -1037 1070 Pro 04 -1038 1070 Pro 05 -1049 1070 Legend 01 -1050 1070 Legend 02 -1051 1070 Legend 03 -1052 1070 Legend 04 -1053 1070 Legend 05 -1057 1071 Amateur 01 -1058 1071 Amateur 02 -1059 1071 Amateur 03 -1060 1071 Amateur 04 -1061 1071 Amateur 05 -1062 1071 Amateur 06 -1063 1071 Amateur 07 -1064 1071 Amateur 08 -1065 1071 Amateur 09 -1066 1071 Amateur 10 -1067 1071 Rookie 01 -1068 1071 Rookie 02 -1069 1071 Rookie 03 -1070 1071 Rookie 04 -1071 1071 Rookie 05 -1072 1071 Rookie 06 -1073 1071 Rookie 07 -1074 1071 Rookie 08 -1075 1071 Rookie 09 -1076 1071 Rookie 10 -1077 1071 Pro 01 -1078 1071 Pro 02 -1079 1071 Pro 03 -1080 1071 Pro 04 -1081 1071 Pro 05 -1082 1071 Pro 06 -1083 1071 Pro 07 -1084 1071 Pro 08 -1085 1071 Pro 09 -1086 1071 Pro 10 -1087 1071 Pro 11 -1088 1071 Pro 12 -1089 1071 Pro 13 -1090 1071 Pro 14 -1091 1071 Pro 15 -1092 1071 Pro 16 -1093 1071 Legend 01 -1094 1071 Legend 02 -1095 1079 Amateur 01 -1096 1079 Amateur 02 -1097 1079 Amateur 03 -1098 1079 Amateur 04 -1099 1079 Amateur 05 -1100 1079 Amateur 06 -1105 1079 Rookie 01 -1106 1079 Rookie 02 -1107 1079 Rookie 03 -1108 1079 Rookie 04 -1109 1079 Rookie 05 -1110 1079 Rookie 06 -1115 1079 Pro 01 -1116 1079 Pro 02 -1117 1079 Pro 03 -1118 1079 Pro 04 -1119 1079 Pro 05 -1120 1079 Pro 06 -1131 1079 Legend 01 -1132 1079 Legend 02 -1133 1079 Legend 03 -1134 1079 Legend 04 -1135 922 Chat Lobby -1136 1080 Main -1137 1080 Tournament -1151 1088 Welcome -1152 1094 AoX chat -1153 1094 AoX AUS -1154 1094 AoX CAN -1155 1094 AoX CHN -1156 1094 AoX CZE -1157 1094 AoX DEU -1158 1094 AoX ESP -1159 1094 AoX FRA -1160 1094 AoX GBR -1161 1094 AoX HUN -1162 1094 AoX ITA -1163 1094 AoX KOR -1164 1094 AoX POL -1165 1094 AoX RUS -1166 1094 AoX USA -1167 1094 AoX AFRICA -1168 1094 AoX AMERICA -1169 1094 AoX ASIA -1170 1094 AoX EUROPE -1171 1071 Legend 03 -1172 1071 Legend 04 -1173 976 Advanced -1174 1092 Beginner -1175 1092 Intermediate -1176 1092 Advanced -1177 845 Isteru -1178 845 Gregor -1179 845 Gregor -1180 845 Rahvan -1181 845 Rahvan -1182 845 Feandan -1183 845 Feandan -1184 845 Dalziel -1185 845 Dalziel -1186 845 Istaura -1187 845 Istaura -1188 845 Agarrus -1189 845 Agarrus -1190 845 Lorethal -1191 845 Lorethal -1192 845 Vistira -1193 845 Vistira -1194 845 Keh -1195 845 Keh -1196 845 Soranith -1197 845 Soranith -1198 845 Rubicon -1199 845 Rubicon -1200 845 Thena -1201 845 Thena -1202 845 Artech -1203 845 Artech -1204 845 Ethaniel -1205 845 Ethaniel -1206 845 Kale -1207 845 Kale -1208 845 Calix -1209 845 Calix -1210 845 Culahn -1211 845 Culahn -1212 845 Rowain -1213 845 Rowain -1214 845 Kelis Carthok -1215 845 Kelis Carthok -1223 1035 Main -1224 1035 Asia -1225 1035 Europe -1226 1035 US -1227 1035 Main -1228 1035 Asia -1229 1035 Europe -1230 1035 US -1231 1035 Main -1232 1035 Asia -1233 1035 Europe -1234 1035 US -1235 1135 Novices -1236 1135 Veterans -1237 1135 English -1238 1135 French -1239 1135 German -1240 1135 Italian -1241 1135 Spanish -1250 952 US - Eastern -1251 952 US - Central -1253 952 US - Western -1254 952 Europe - English -1255 952 Europe - French -1256 952 Europe - Italian -1257 952 Europe - German -1258 952 Europe - Spanish -1259 1042 Conquest 2 -1260 1042 Conquest 3 -1261 1156 Main -1262 1156 Main -1263 1156 Main -1264 1042 Territory Control 2 -1265 1042 Territory Control 3 -1266 1042 King of the Hill 2 -1267 1042 King of the Hill 3 -1269 1042 Conquest -1270 1042 King of the Hill -1271 1042 Territory Control -1272 1042 Conquest 2 -1273 1042 Conquest 3 -1274 1042 Territory Control 3 -1275 1042 King of the Hill 2 -1276 1042 King of the Hill 3 -1277 1042 Territory Control 2 -1278 1159 West -1279 1159 East -1282 1173 General -1283 1173 OnlineBattle -1284 1181 General -1285 1181 OnlineBattle -1287 1191 Beginner -1288 1191 Intermediate -1289 1191 Advanced -1290 948 North America -1291 948 Europe -1292 948 Asia Pacific -1301 948 Unpatched -1302 1195 Beginner -1303 1195 Intermediate -1304 1195 Advanced -1330 1212 West -1331 1212 East -1334 1190 Generak -1335 1190 OnlineBattle -1336 1213 West -1337 1213 East -1354 1207 Room 1 -1355 1207 Room 2 -1356 1207 Room 3 -1357 1207 Room 4 -1358 1207 Room 5 -1359 1207 Room 6 -1360 1207 Room 7 -1361 1207 Room 8 -1362 1207 Room 9 -1363 1207 Room 10 -1369 1 TestGroupRoom -1370 1224 North America -1371 1224 Europe -1372 1224 Asia Pacific -1381 1231 English -1382 1231 French -1383 1231 German -1384 1231 Italian -1385 1231 Spanish -1386 1231 Eastern US -1387 1231 Central US -1388 1231 Western US -1390 1196 Exhibition Single 00 -1391 1196 Exhibition Single 01 -1392 1196 Exhibition Single 02 -1393 1196 Exhibition Single 03 -1394 1196 Exhibition Single 04 -1395 1196 Exhibition Single 05 -1396 1196 Exhibition Single 06 -1397 1196 Exhibition Single 07 -1398 1196 Exhibition Single 08 -1399 1196 Exhibition Single 09 -1410 1196 Exhibition Tag 00 -1411 1196 Exhibition Tag 01 -1412 1196 Exhibition Tag 02 -1413 1196 Exhibition Tag 03 -1414 1196 Exhibition Tag 04 -1415 1196 Exhibition Tag 05 -1416 1196 Exhibition Tag 06 -1417 1196 Exhibition Tag 07 -1418 1196 Exhibition Tag 08 -1419 1196 Exhibition Tag 09 -1430 1196 Exhibition Main 00 -1431 1196 Exhibition Main 01 -1432 1196 Exhibition Main 02 -1433 1196 Exhibition Main 03 -1434 1196 Exhibition Main 04 -1435 1196 Exhibition Main 05 -1436 1196 Exhibition Main 06 -1437 1196 Exhibition Main 07 -1438 1196 Exhibition Main 08 -1439 1196 Exhibition Main 09 -1450 1196 Exhibition Voice 00 -1451 1196 Exhibition Voice 01 -1452 1196 Exhibition Voice 02 -1453 1196 Exhibition Voice 03 -1454 1196 Exhibition Voice 04 -1455 1196 Exhibition Voice 05 -1456 1196 Exhibition Voice 06 -1457 1196 Exhibition Voice 07 -1458 1196 Exhibition Voice 08 -1459 1196 Exhibition Voice 09 -1470 1196 TitleMatch Single 00 -1471 1196 TitleMatch Single 01 -1472 1196 TitleMatch Single 02 -1473 1196 TitleMatch Single 03 -1474 1196 TitleMatch Single 04 -1475 1196 TitleMatch Single 05 -1476 1196 TitleMatch Single 06 -1477 1196 TitleMatch Single 07 -1478 1196 TitleMatch Single 08 -1479 1196 TitleMatch Single 09 -1490 1196 TitleMatch Tag 00 -1491 1196 TitleMatch Tag 01 -1492 1196 TitleMatch Tag 02 -1493 1196 TitleMatch Tag 03 -1494 1196 TitleMatch Tag 04 -1495 1196 TitleMatch Tag 05 -1496 1196 TitleMatch Tag 06 -1497 1196 TitleMatch Tag 07 -1498 1196 TitleMatch Tag 08 -1499 1196 TitleMatch Tag 09 -1510 1196 TitleMatch Main 00 -1511 1196 TitleMatch Main 01 -1512 1196 TitleMatch Main 02 -1513 1196 TitleMatch Main 03 -1514 1196 TitleMatch Main 04 -1515 1196 TitleMatch Main 05 -1516 1196 TitleMatch Main 06 -1517 1196 TitleMatch Main 07 -1518 1196 TitleMatch Main 08 -1519 1196 TitleMatch Main 09 -1530 1196 TitleMatch Voice 00 -1531 1196 TitleMatch Voice 01 -1532 1196 TitleMatch Voice 02 -1533 1196 TitleMatch Voice 03 -1534 1196 TitleMatch Voice 04 -1535 1196 TitleMatch Voice 05 -1536 1196 TitleMatch Voice 06 -1537 1196 TitleMatch Voice 07 -1538 1196 TitleMatch Voice 08 -1539 1196 TitleMatch Voice 09 -1590 1196 Trade 00 -1591 1196 Trade 01 -1592 1196 Trade 02 -1593 1196 Trade 03 -1594 1196 Trade 04 -1595 1196 Trade 05 -1596 1196 Trade 06 -1597 1196 Trade 07 -1598 1196 Trade 08 -1599 1196 Trade 09 -1610 1228 Group Room 1 -1611 1228 Group Room 2 -1612 1228 Group Room 3 -1613 1122 Noodlers -1614 1265 West Coast -1616 1265 East Coast -1620 1266 QuickMatch -1621 1266 ChatRoom2 -1622 1266 ChatRoom1 -1623 1266 LobbyRoom1 -1624 1266 LobbyRoom2 -1652 237 First New One -1653 237 Second New One -1654 237 Third New One -1655 237 Fourth New One -1656 1197 Exhibition Single 00 -1657 1197 Exhibition Single 01 -1658 1197 Exhibition Single 02 -1659 1197 Exhibition Single 03 -1660 1197 Exhibition Single 04 -1661 1197 Exhibition Single 05 -1662 1197 Exhibition Single 06 -1663 1197 Exhibition Single 07 -1664 1197 Exhibition Single 08 -1665 1197 Exhibition Single 09 -1666 1197 Exhibition Tag 00 -1667 1197 Exhibition Tag 01 -1668 1197 Exhibition Tag 02 -1669 1197 Exhibition Tag 03 -1670 1197 Exhibition Tag 04 -1671 1197 Exhibition Tag 05 -1672 1197 Exhibition Tag 06 -1673 1197 Exhibition Tag 07 -1674 1197 Exhibition Tag 08 -1675 1197 Exhibition Tag 09 -1676 1197 Exhibition Main 00 -1677 1197 Exhibition Main 01 -1678 1197 Exhibition Main 02 -1679 1197 Exhibition Main 03 -1680 1197 Exhibition Main 04 -1681 1197 Exhibition Main 05 -1682 1197 Exhibition Main 06 -1683 1197 Exhibition Main 07 -1684 1197 Exhibition Main 08 -1685 1197 Exhibition Main 09 -1686 1197 Exhibition Voice 00 -1687 1197 Exhibition Voice 01 -1688 1197 Exhibition Voice 02 -1689 1197 Exhibition Voice 03 -1690 1197 Exhibition Voice 04 -1691 1197 Exhibition Voice 05 -1692 1197 Exhibition Voice 06 -1693 1197 Exhibition Voice 07 -1694 1197 Exhibition Voice 08 -1695 1197 Exhibition Voice 09 -1696 1197 TitleMatch Single 00 -1697 1197 TitleMatch Single 01 -1698 1197 TitleMatch Single 02 -1699 1197 TitleMatch Single 03 -1700 1197 TitleMatch Single 04 -1701 1197 TitleMatch Single 05 -1702 1197 TitleMatch Single 06 -1703 1197 TitleMatch Single 07 -1704 1197 TitleMatch Single 08 -1705 1197 TitleMatch Single 09 -1706 1197 TitleMatch Tag 00 -1707 1197 TitleMatch Tag 01 -1708 1197 TitleMatch Tag 02 -1709 1197 TitleMatch Tag 03 -1710 1197 TitleMatch Tag 04 -1711 1197 TitleMatch Tag 05 -1712 1197 TitleMatch Tag 06 -1713 1197 TitleMatch Tag 07 -1714 1197 TitleMatch Tag 08 -1715 1197 TitleMatch Tag 09 -1716 1197 TitleMatch Main 00 -1717 1197 TitleMatch Main 01 -1718 1197 TitleMatch Main 02 -1719 1197 TitleMatch Main 03 -1720 1197 TitleMatch Main 04 -1721 1197 TitleMatch Main 05 -1722 1197 TitleMatch Main 06 -1723 1197 TitleMatch Main 07 -1724 1197 TitleMatch Main 08 -1725 1197 TitleMatch Main 09 -1726 1197 TitleMatch Voice 00 -1727 1197 TitleMatch Voice 01 -1728 1197 TitleMatch Voice 02 -1729 1197 TitleMatch Voice 03 -1730 1197 TitleMatch Voice 04 -1731 1197 TitleMatch Voice 05 -1732 1197 TitleMatch Voice 06 -1733 1197 TitleMatch Voice 07 -1734 1197 TitleMatch Voice 08 -1735 1197 TitleMatch Voice 09 -1736 1197 Trade 00 -1737 1197 Trade 01 -1738 1197 Trade 02 -1739 1197 Trade 03 -1740 1197 Trade 04 -1741 1197 Trade 05 -1742 1197 Trade 06 -1743 1197 Trade 07 -1744 1197 Trade 08 -1745 1197 Trade 09 -1746 1198 Exhibition Single 00 -1747 1198 Exhibition Single 01 -1748 1198 Exhibition Single 02 -1749 1198 Exhibition Single 03 -1750 1198 Exhibition Single 04 -1751 1198 Exhibition Tag 00 -1752 1198 Exhibition Tag 01 -1753 1198 Exhibition Tag 02 -1754 1198 Exhibition Tag 03 -1755 1198 Exhibition Tag 04 -1756 1198 Exhibition Main 00 -1757 1198 Exhibition Main 01 -1758 1198 Exhibition Main 02 -1759 1198 Exhibition Main 03 -1760 1198 Exhibition Main 04 -1761 1198 Exhibition Voice 00 -1762 1198 Exhibition Voice 01 -1763 1198 Exhibition Voice 02 -1764 1198 Exhibition Voice 03 -1765 1198 Exhibition Voice 04 -1766 1198 TitleMatch Single 00 -1767 1198 TitleMatch Single 01 -1768 1198 TitleMatch Single 02 -1769 1198 TitleMatch Single 03 -1770 1198 TitleMatch Single 04 -1771 1198 TitleMatch Tag 00 -1772 1198 TitleMatch Tag 01 -1773 1198 TitleMatch Tag 02 -1774 1198 TitleMatch Tag 03 -1775 1198 TitleMatch Tag 04 -1776 1198 TitleMatch Main 00 -1777 1198 TitleMatch Main 01 -1778 1198 TitleMatch Main 02 -1779 1198 TitleMatch Main 03 -1780 1198 TitleMatch Main 04 -1781 1198 TitleMatch Voice 00 -1782 1198 TitleMatch Voice 01 -1783 1198 TitleMatch Voice 02 -1784 1198 TitleMatch Voice 03 -1785 1198 TitleMatch Voice 04 -1786 1198 Trade 00 -1787 1198 Trade 01 -1788 1198 Trade 02 -1789 1198 Trade 03 -1790 1198 Trade 04 -1793 1122 Jammers -1795 1066 Rebellion -1796 1066 Empire -1797 1066 Versus (1vs1) -1798 1066 Team Games -1799 1066 General -1800 1066 Rebellion -1801 1066 Empire -1802 1066 Versus (1vs1) -1803 1066 Team Games -1804 1066 General -1805 1066 Rebellion -1806 1066 Empire -1807 1066 Versus (1vs1) -1808 1066 Team Games -1809 1066 General -1810 1066 Rebellion -1811 1066 Empire -1812 1066 Versus (1vs1) -1813 1066 Team Games -1814 1066 General -1815 1066 Rebellion -1816 1066 Empire -1817 1066 Versus (1vs1) -1818 1066 Team Games -1819 1066 General -1820 1066 Rebellion -1821 1066 Empire -1822 1066 Versus (1vs1) -1823 1066 Team Games -1824 1066 General -1825 1066 Rebellion -1826 1066 Empire -1827 1066 Versus (1vs1) -1828 1066 Team Games -1829 1066 General -1830 1066 Rebellion -1831 1066 Empire -1837 1309 West -1838 1309 East -1839 1321 QuickMatch -1840 1321 ChatRoom1 -1842 1321 LobbyRoom1 -1843 1321 LobbyRoom2 -1844 1321 LobbyRoom3 -1845 1321 LobbyRoom4 -1846 1321 LobbyRoom5 -1847 1321 LobbyRoom6 -1848 1321 LobbyRoom7 -1849 1321 LobbyRoom8 -1850 1283 Western US -1851 1283 Central US -1852 1283 Eastern US -1853 1283 German -1854 1283 Spanish -1855 1283 English -1856 1283 Italian -1857 1283 French -1858 1321 LobbyRoom9 -1859 1122 Chop Masters -1860 1160 USA -1861 1160 Europe -1862 1190 Debug -1863 1354 Larry -1864 1354 Curly -1865 1354 Moe -1866 1376 West -1867 1376 East -1868 1391 Action -1869 1391 Story -1870 1391 Story Lite -1871 1391 Role Play -1872 1391 Team -1873 1391 Melee -1874 1391 Arena -1875 1391 Social -1876 1391 Alternative -1877 1391 PW Story -1878 1391 PW Action -1879 1391 Solo -1880 1391 Tech Support -1881 1122 eJamming Test -1882 1401 Novices -1883 1401 Veterans -1884 1401 English -1885 1401 French -1886 1401 German -1887 1401 Italian -1888 1401 Spanish -1889 1396 Africa -1890 1396 America -1891 1396 Asia -1892 1396 Europe -1893 1396 Pacific -1894 1396 The Empire -1895 1396 Hordes of Chaos -1896 1396 High Elves -1897 1396 Skaven -1898 1396 Other -1899 1396 Kicked -1900 1396 Banned -1901 1422 QuickMatch -1902 1422 ChatRoom1 -1904 1426 Beginner -1905 1426 Intermediate -1906 1426 Advanced -1907 1411 Room 1 -1908 1411 Room 2 -1909 1411 Room 3 -1910 1411 Room 4 -1911 1411 Room 5 -1912 1411 Room 6 -1913 1411 Room 7 -1914 1411 Room 8 -1915 1411 Room 9 -1916 1411 Room 10 -1917 1463 QuickMatch -1918 1463 ChatRoom1 -1919 1463 LobbyRoom1 -1920 1463 LobbyRoom2 -1921 1463 LobbyRoom3 -1922 1463 LobbyRoom4 -1923 1463 LobbyRoom5 -1924 1463 LobbyRoom6 -1925 1463 LobbyRoom7 -1926 1463 LobbyRoom8 -1927 1463 LobbyRoom9 -1928 1398 Africa -1929 1398 America -1930 1398 Asia -1931 1398 Europe -1932 1398 Pacific -1933 1398 The Empire -1934 1398 Hordes of Chaos -1935 1398 High Elves -1936 1398 Skaven -1937 1398 Other -1938 1398 Kicked -1939 1398 Banned -1940 1479 West -1941 1479 East -1942 1481 East -1943 1481 West -1944 1463 QuickMatch2 -1945 1483 America -1946 1483 Asia -1947 1483 Europe -1948 1321 QuickMatch2 -1956 1514 Newbies -1957 1514 Experienced Players -1958 1514 Strategists -1959 1515 Newbies -1960 1515 Experienced Players -1961 1515 Strategists -1962 1523 Skirmish -1963 1523 World Domination -1964 1524 Skirmish -1965 1524 World Domination -1966 1577 Rebellion -1967 1577 Empire -1968 1577 Versus (1vs1) -1969 1577 Team Games -1970 1577 General -1971 1577 Rebellion -1972 1577 Empire -1973 1577 Versus (1vs1) -1974 1577 Team Games -1975 1577 General -1976 1577 Rebellion -1977 1577 Empire -1978 1577 Versus (1vs1) -1979 1577 Team Games -1980 1577 General -1981 1577 Rebellion -1982 1577 Empire -1983 1577 Versus (1vs1) -1984 1577 Team Games -1985 1577 General -1986 1577 Rebellion -1987 1577 Empire -1988 1577 Versus (1vs1) -1989 1577 Team Games -1990 1577 General -1991 1577 Rebellion -1992 1577 Empire -1993 1577 Versus (1vs1) -1994 1577 Team Games -1995 1577 General -1996 1577 Rebellion -1997 1577 Empire -1998 1577 Versus (1vs1) -1999 1577 Team Games -2000 1577 General -2001 1577 Rebellion -2002 1577 Empire -2003 1631 Skirmish -2004 1631 World Domination -2027 1714 ChatRoom1 -2028 1714 QuickMatch -2029 1714 LobbyRoom:1 -2030 1714 LobbyRoom:2 -2031 1714 LobbyRoom:3 -2032 1714 LobbyRoom:4 -2033 1714 LobbyRoom:5 -2034 1714 LobbyRoom:6 -2035 1714 LobbyRoom:7 -2036 1714 LobbyRoom:8 -2037 1714 LobbyRoom:9 -2038 1714 LobbyRoom:10 -2039 1714 LobbyRoom:11 -2040 1714 LobbyRoom:12 -2041 1714 LobbyRoom:13 -2042 1714 LobbyRoom:14 -2043 1714 LobbyRoom:15 -2044 1714 LobbyRoom:16 -2045 1714 LobbyBeginners:1 -2046 1714 LobbyHardcore:1 -2047 1714 LobbyClan:1 -2048 1714 LobbyClan:2 -2049 1714 LobbyTournaments:1 -2050 1714 LobbyTournaments:2 -2051 1714 LobbyBattlecast:1 -2052 1714 LobbyCustomMap:1 -2053 1714 LobbyCustomMap:2 -2054 1714 LobbyCompStomp:1 -2055 1714 LobbyGerman:1 -2056 1714 LobbyGerman:2 -2057 1714 LobbyKorean:1 -2058 1714 LobbyFrench:1 -2059 1422 LobbyRoom:1 -2060 1422 LobbyRoom:2 -2061 1422 LobbyRoom:3 -2062 1422 LobbyRoom:4 -2063 1422 LobbyRoom:5 -2064 1422 LobbyRoom:6 -2065 1422 LobbyRoom:7 -2066 1422 LobbyRoom:8 -2067 1422 LobbyRoom:9 -2068 1422 LobbyRoom:10 -2069 1422 LobbyRoom:11 -2070 1422 LobbyRoom:12 -2071 1422 LobbyRoom:13 -2072 1422 LobbyRoom:14 -2073 1422 LobbyRoom:15 -2074 1422 LobbyRoom:16 -2075 1422 LobbyBeginners:1 -2076 1422 LobbyHardcore:1 -2077 1422 LobbyClan:1 -2078 1422 LobbyClan:2 -2079 1422 LobbyTournaments:1 -2080 1422 LobbyTournaments:2 -2081 1422 LobbyBattlecast:1 -2082 1422 LobbyCustomMap:1 -2083 1422 LobbyCustomMap:2 -2084 1422 LobbyCompStomp:1 -2085 1422 LobbyGerman:1 -2086 1422 LobbyGerman:2 -2087 1422 LobbyKorean:1 -2088 1422 LobbyFrench:1 -2089 1774 Room 1 -2090 1774 Room 2 -2091 1774 Room 3 -2092 1774 Room 4 -2093 1774 Room 5 -2094 1774 Room 6 -2095 1774 Room 7 -2096 1774 Room 8 -2097 1774 Room 9 -2098 1774 Room 10 -2099 1814 ChatRoom1 -2100 1814 QuickMatch -2101 1814 LobbyRoom:1 -2102 1814 LobbyRoom:2 -2103 1814 LobbyRoom:3 -2104 1814 LobbyRoom:4 -2105 1814 LobbyRoom:5 -2106 1814 LobbyRoom:6 -2107 1814 LobbyRoom:7 -2108 1814 LobbyRoom:8 -2109 1814 LobbyRoom:9 -2110 1814 LobbyRoom:10 -2111 1814 LobbyRoom:11 -2112 1814 LobbyRoom:12 -2113 1814 LobbyRoom:13 -2114 1814 LobbyRoom:14 -2115 1814 LobbyRoom:15 -2116 1814 LobbyRoom:16 -2117 1814 LobbyBeginners:1 -2118 1814 LobbyHardcore:1 -2119 1814 LobbyClan:1 -2120 1814 LobbyClan:2 -2121 1814 LobbyTournaments:1 -2122 1814 LobbyTournaments:2 -2123 1814 LobbyBattlecast:1 -2124 1814 LobbyCustomMap:1 -2125 1814 LobbyCustomMap:2 -2126 1814 LobbyCompStomp:1 -2127 1814 LobbyGerman:1 -2128 1814 LobbyGerman:2 -2129 1814 LobbyKorean:1 -2130 1814 LobbyFrench:1 -2131 1884 Room 1 -2132 1884 Room 2 -2133 1884 Room 3 -2134 1884 Room 4 -2135 1884 Room 5 -2136 1884 Room 6 -2137 1884 Room 7 -2138 1884 Room 8 -2139 1884 Room 9 -2140 1884 Room 10 -2141 1923 Africa -2142 1923 America -2143 1923 Asia -2144 1923 Europe -2145 1923 Pacific -2146 1923 The Empire -2147 1923 Hordes of Chaos -2148 1923 High Elves -2149 1923 Skaven -2150 1923 Other -2151 1923 Dark Elf -2152 1923 Orc -2153 1923 Kicked -2154 1923 Banned -2155 1514 Sparta FX -2156 1979 QuickMatch -2157 1979 LobbyRoom:1 -2158 1979 ChatRoom1 -2159 2100 QuickMatch -2160 2100 LobbyRoom:1 -2161 2100 ChatRoom1 -2162 2094 QuickMatch -2163 2094 LobbyRoom:1 -2164 2094 ChatRoom1 -2166 2128 LobbyRoom:1 -2167 2128 LobbyRoom:2 -2168 2128 LobbyRoom:3 -2169 2128 LobbyRoom:4 -2170 2128 LobbyRoom:5 -2171 2128 LobbyKorean:1 -2172 2128 LobbyFrench:1 -2173 2128 LobbyGerman:2 -2174 2128 LobbyGerman:1 -2175 2128 LobbyBattlecast:1 -2176 2128 LobbyRoom:6 -2177 2128 ChatRoom1 -2178 2128 LobbyRoom:8 -2179 2128 LobbyRoom:11 -2180 2128 LobbyRoom:7 -2181 2128 LobbyRoom:9 -2182 2128 LobbyRoom:12 -2183 2128 LobbyRoom:10 -2184 2128 LobbyClan:1 -2185 2128 LobbyRoom:13 -2186 2128 LobbyClan:2 -2187 2128 LobbyRoom:14 -2188 2128 LobbyRoom:16 -2189 2128 LobbyRoom:15 -2190 2128 LobbyBeginners:1 -2191 2128 LobbyTournaments:1 -2192 2128 LobbyCompStomp:1 -2193 2128 LobbyHardcore:1 -2194 2128 LobbyCustomMap:1 -2195 2128 LobbyCustomMap:2 -2196 2128 LobbyTournaments:2 -2198 2128 ChatRoom1 -2200 2130 LobbyRoom:1 -2201 2130 LobbyRoom:2 -2202 2130 LobbyRoom:3 -2203 2130 LobbyRoom:4 -2204 2130 LobbyRoom:5 -2205 2130 LobbyKorean:1 -2206 2130 LobbyFrench:1 -2207 2130 LobbyGerman:2 -2208 2130 LobbyGerman:1 -2209 2130 LobbyBattlecast:1 -2210 2130 LobbyRoom:6 -2211 2130 ChatRoom1 -2212 2130 LobbyRoom:8 -2213 2130 LobbyRoom:11 -2214 2130 LobbyRoom:7 -2215 2130 LobbyRoom:9 -2216 2130 LobbyRoom:12 -2217 2130 LobbyRoom:10 -2218 2130 LobbyClan:1 -2219 2130 LobbyRoom:13 -2220 2130 LobbyClan:2 -2221 2130 LobbyRoom:14 -2222 2130 LobbyRoom:16 -2223 2130 LobbyRoom:15 -2224 2130 LobbyBeginners:1 -2225 2130 LobbyTournaments:1 -2226 2130 LobbyCompStomp:1 -2227 2130 LobbyHardcore:1 -2228 2130 LobbyCustomMap:1 -2229 2130 LobbyCustomMap:2 -2230 2130 LobbyTournaments:2 -2232 2130 ChatRoom1 -2233 2160 QuickMatch -2234 2160 LobbyTournaments:2 -2235 2160 LobbyTournaments:1 -2236 2160 LobbyRoom:9 -2237 2160 LobbyRoom:8 -2238 2160 LobbyRoom:7 -2239 2160 LobbyRoom:6 -2240 2160 LobbyRoom:5 -2241 2160 LobbyRoom:4 -2242 2160 LobbyRoom:3 -2243 2160 LobbyRoom:2 -2244 2160 LobbyRoom:16 -2245 2160 LobbyRoom:15 -2246 2160 LobbyRoom:14 -2247 2160 LobbyRoom:13 -2248 2160 LobbyRoom:12 -2249 2160 LobbyRoom:11 -2250 2160 LobbyRoom:10 -2251 2160 LobbyRoom:1 -2252 2160 LobbyKorean:1 -2253 2160 LobbyHardcore:1 -2254 2160 LobbyGerman:2 -2255 2160 LobbyGerman:1 -2256 2160 LobbyFrench:1 -2257 2160 LobbyCustomMap:2 -2258 2160 LobbyCustomMap:1 -2259 2160 LobbyCompStomp:1 -2260 2160 LobbyClan:2 -2261 2160 LobbyClan:1 -2262 2160 LobbyBeginners:1 -2263 2160 LobbyBattlecast:1 -2264 2160 ChatRoom1 -2265 1979 Russia:1 -2266 2238 LobbyRoom:1 -2267 2238 LobbyRoom:2 -2268 2238 LobbyRoom:3 -2269 2238 LobbyRoom:4 -2270 2238 LobbyRoom:5 -2271 2238 LobbyRoom:6 -2272 2238 LobbyRoom:7 -2273 2238 LobbyRoom:8 -2274 2238 LobbyRoom:9 -2275 2238 LobbyRoom:10 -2276 2238 LobbyRoom:11 -2277 2238 LobbyRoom:12 -2278 2238 LobbyRoom:13 -2279 2238 LobbyRoom:14 -2280 2238 LobbyRoom:15 -2281 2238 LobbyRoom:16 -2282 2238 LobbyBeginners:1 -2283 2238 LobbyHardcore:1 -2284 2238 LobbyClan:1 -2285 2238 LobbyClan:2 -2286 2238 LobbyTournaments:1 -2287 2238 LobbyTournaments:2 -2288 2238 LobbyBattlecast:1 -2289 2238 LobbyCustomMap:1 -2290 2238 LobbyCustomMap:2 -2291 2238 LobbyCompStomp:1 -2292 2238 LobbyGerman:1 -2293 2238 LobbyGerman:2 -2294 2238 LobbyKorean:1 -2295 2238 LobbyFrench:1 -2296 2238 ChatRoom1 -2297 1814 Russia:1 -2298 2298 Deathmatch -2299 2298 RopeRace -2300 2298 Forts -2301 2298 Triathlon -2302 2313 Beginner -2303 2313 Intermediate -2304 2313 Expert -2305 2374 ChatRoom1 -2306 2374 LobbyRoom:1 -2307 2374 LobbyRoom:2 -2308 2374 LobbyRoom:3 -2309 2374 LobbyRoom:4 -2310 2374 LobbyRoom:5 -2311 2374 LobbyKorean:1 -2312 2374 LobbyFrench:1 -2313 2374 LobbyGerman:2 -2314 2374 LobbyGerman:1 -2315 2374 LobbyBattlecast:1 -2316 2374 LobbyRoom:6 -2317 2374 ChatRoom1 -2318 2374 LobbyRoom:8 -2319 2374 LobbyRoom:11 -2320 2374 LobbyRoom:7 -2321 2374 LobbyRoom:9 -2322 2374 LobbyRoom:12 -2323 2374 LobbyRoom:10 -2324 2374 LobbyClan:1 -2325 2374 LobbyRoom:13 -2326 2374 LobbyClan:2 -2327 2374 LobbyRoom:14 -2328 2374 LobbyRoom:16 -2329 2374 LobbyRoom:15 -2330 2374 LobbyBeginners:1 -2331 2374 LobbyTournaments:1 -2332 2374 LobbyCompStomp:1 -2333 2374 LobbyHardcore:1 -2334 2374 LobbyCustomMap:1 -2335 2374 LobbyCustomMap:2 -2336 2374 LobbyTournaments:2 -2337 2130 LobbyRussian:1 -2338 2130 LobbyTaiwan:1 -2339 2128 LobbyRoom:17 -2340 2128 LobbyRoom:18 -2341 2128 LobbyRoom:19 -2342 2128 LobbyRoom:20 -2343 2128 LobbyRoom:21 -2344 2128 LobbyCoop:1 -2345 2128 LobbyCoop:2 -2346 2128 LobbyCoop:3 -2347 2128 LobbyCoop:4 -2348 2128 LobbyCoop:5 -2349 2128 LobbyRussian:1 -2350 2128 LobbyTaiwan:1 -2351 2298 WarRoom -2352 2298 TacticsMode -2353 2259 room1 -2354 2259 room2 -2355 2259 room3 -2356 2259 room4 -2357 2128 LobbySpanish:1 -2358 2714 room1 -2359 2714 room2 -2360 2714 room3 -2361 2714 room4 -2362 2246 ChatMonTesting -2363 2712 room1 -2364 2712 room2 -2365 2712 room3 -2366 2712 room4 -2367 2705 North America -2368 2705 Europe -2369 2705 Brazil -2370 2840 FX Sparta II -2371 2706 North America -2372 2706 Europe -2373 2706 Brazil -2374 2707 North America -2375 2707 Europe -2376 2707 Brazil -2377 2032 North America -2378 2032 Europe -2379 2032 Brazil -2380 2034 North America -2381 2034 Europe -2382 2034 Brazil -2383 2035 North America -2384 2035 Europe -2385 2035 Brazil -2386 706 Main Lobby -2387 1003 ThMods.com -2388 917 Main Lobby -2389 1128 Main Lobby -2390 1307 ThMods.com -2391 1005 ThMods.com -2392 467 Main Lobby -2393 600 Main Lobby -2399 1003 Lobby 2 -\. - - --- --- TOC entry 3448 (class 0 OID 16410) --- Dependencies: 217 --- Data for Name: messages; Type: TABLE DATA; Schema: unispy; Owner: - --- - -COPY unispy.messages (messageid, namespaceid, type, "from", "to", date, message) FROM stdin; -\. - - --- --- TOC entry 3450 (class 0 OID 16417) --- Dependencies: 219 --- Data for Name: partner; Type: TABLE DATA; Schema: unispy; Owner: - --- - -COPY unispy.partner (partnerid, partnername) FROM stdin; -0 UniSpy -95 Crytek -\. - - --- --- TOC entry 3451 (class 0 OID 16422) --- Dependencies: 220 --- Data for Name: profiles; Type: TABLE DATA; Schema: unispy; Owner: - --- - -COPY unispy.profiles (profileid, userid, nick, serverflag, status, statstring, location, firstname, lastname, publicmask, latitude, longitude, aim, picture, occupationid, incomeid, industryid, marriedid, childcount, interests1, ownership1, connectiontype, sex, zipcode, countrycode, homepage, birthday, birthmonth, birthyear, icquin, quietflags, streetaddr, streeaddr, city, cpubrandid, cpuspeed, memory, videocard1string, videocard1ram, videocard2string, videocard2ram, subscription, adminrights) FROM stdin; -1 1 spyguy 0 0 I love UniSpy earth spy guy 0 0 0 0 0 0 0 0 0 0 0 0 0 00000 1 unispy.org 0 0 0 0 0 \N \N \N 0 0 0 \N 0 \N 0 0 0 -2 2 uniguy 0 0 I love UniSpy earth uni guy 0 0 0 0 0 0 0 0 0 0 0 0 0 00000 1 unispy.org 0 0 0 0 0 \N \N \N 0 0 0 \N 0 \N 0 0 0 -3 3 gptest1 0 0 I love UniSpy \N \N \N 0 0 0 0 0 0 0 0 0 0 0 0 0 00000 1 unispy.org 0 0 0 0 0 \N \N \N 0 0 0 \N 0 \N 0 0 0 -4 4 gptest2 0 0 I love UniSpy \N \N \N 0 0 0 0 0 0 0 0 0 0 0 0 0 00000 1 unispy.org 0 0 0 0 0 \N \N \N 0 0 0 \N 0 \N 0 0 0 -5 5 gptest3 0 0 I love UniSpy \N \N \N 0 0 0 0 0 0 0 0 0 0 0 0 0 00000 1 unispy.org 0 0 0 0 0 \N \N \N 0 0 0 \N 0 \N 0 0 0 -\. - - --- --- TOC entry 3453 (class 0 OID 16460) --- Dependencies: 222 --- Data for Name: pstorage; Type: TABLE DATA; Schema: unispy; Owner: - --- - -COPY unispy.pstorage (pstorageid, profileid, ptype, dindex, data) FROM stdin; -\. - - --- --- TOC entry 3455 (class 0 OID 16466) --- Dependencies: 224 --- Data for Name: sakestorage; Type: TABLE DATA; Schema: unispy; Owner: - --- - -COPY unispy.sakestorage (sakestorageid, tableid) FROM stdin; -\. - - --- --- TOC entry 3457 (class 0 OID 16472) --- Dependencies: 226 --- Data for Name: subprofiles; Type: TABLE DATA; Schema: unispy; Owner: - --- - -COPY unispy.subprofiles (subprofileid, profileid, uniquenick, namespaceid, partnerid, productid, gamename, cdkeyenc, firewall, port, authtoken) FROM stdin; -1 1 spyguy 1 0 0 gmtest \N 0 0 example_token -2 2 uniguy 1 0 0 gmtest \N 0 0 \N -3 3 gptest1 1 0 0 gmtest \N 0 0 \N -4 4 gptest2 1 0 0 gmtest \N 0 0 \N -5 5 gptest3 1 0 0 gmtest \N 0 0 \N -\. - - --- --- TOC entry 3459 (class 0 OID 16482) --- Dependencies: 228 --- Data for Name: users; Type: TABLE DATA; Schema: unispy; Owner: - --- - -COPY unispy.users (userid, email, password, emailverified, lastip, lastonline, createddate, banned, deleted) FROM stdin; -1 spyguy@gamespy.com 4a7d1ed414474e4033ac29ccb8653d9b t \N 2022-01-19 20:01:49.828006 2022-01-19 20:01:49.828006 f f -2 uni@unispy.org 4a7d1ed414474e4033ac29ccb8653d9b t \N 2022-01-19 20:02:57.595514 2022-01-19 20:02:57.595514 f f -3 gptestc1@gptestc.com c6d525669e64438c9aa156a0cc80cd14 t \N 2022-01-19 20:03:44.754069 2022-01-19 20:03:44.754069 f f -4 gptestc2@gptestc.com c6d525669e64438c9aa156a0cc80cd14 t \N 2022-01-19 20:03:44.761986 2022-01-19 20:03:44.761986 f f -5 gptestc3@gptestc.com c6d525669e64438c9aa156a0cc80cd14 t \N 2022-01-19 20:03:44.764527 2022-01-19 20:03:44.764527 f f -\. - - --- --- TOC entry 3488 (class 0 OID 0) --- Dependencies: 210 --- Name: addrequests_addrequestid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - --- - -SELECT pg_catalog.setval('unispy.addrequests_addrequestid_seq', 1, false); - - --- --- TOC entry 3489 (class 0 OID 0) --- Dependencies: 212 --- Name: blocked_blockid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - --- - -SELECT pg_catalog.setval('unispy.blocked_blockid_seq', 1, false); - - --- --- TOC entry 3490 (class 0 OID 0) --- Dependencies: 214 --- Name: friends_friendid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - --- - -SELECT pg_catalog.setval('unispy.friends_friendid_seq', 1, false); - - --- --- TOC entry 3491 (class 0 OID 0) --- Dependencies: 218 --- Name: messages_messageid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - --- - -SELECT pg_catalog.setval('unispy.messages_messageid_seq', 1, false); - - --- --- TOC entry 3492 (class 0 OID 0) --- Dependencies: 221 --- Name: profiles_profileid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - --- - -SELECT pg_catalog.setval('unispy.profiles_profileid_seq', 5, true); - - --- --- TOC entry 3493 (class 0 OID 0) --- Dependencies: 223 --- Name: pstorage_pstorageid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - --- - -SELECT pg_catalog.setval('unispy.pstorage_pstorageid_seq', 1, true); - - --- --- TOC entry 3494 (class 0 OID 0) --- Dependencies: 225 --- Name: sakestorage_sakestorageid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - --- - -SELECT pg_catalog.setval('unispy.sakestorage_sakestorageid_seq', 1, false); - - --- --- TOC entry 3495 (class 0 OID 0) --- Dependencies: 227 --- Name: subprofiles_subprofileid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - --- - -SELECT pg_catalog.setval('unispy.subprofiles_subprofileid_seq', 5, true); - - --- --- TOC entry 3496 (class 0 OID 0) --- Dependencies: 229 --- Name: users_userid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - --- - -SELECT pg_catalog.setval('unispy.users_userid_seq', 5, true); - - --- --- TOC entry 3271 (class 2606 OID 16503) --- Name: addrequests addrequests_pk; Type: CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.addrequests - ADD CONSTRAINT addrequests_pk PRIMARY KEY (addrequestid); - - --- --- TOC entry 3273 (class 2606 OID 16505) --- Name: blocked blocked_pk; Type: CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.blocked - ADD CONSTRAINT blocked_pk PRIMARY KEY (blockid); - - --- --- TOC entry 3275 (class 2606 OID 16507) --- Name: friends friends_pk; Type: CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.friends - ADD CONSTRAINT friends_pk PRIMARY KEY (friendid); - - --- --- TOC entry 3277 (class 2606 OID 16509) --- Name: games games_pk; Type: CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.games - ADD CONSTRAINT games_pk PRIMARY KEY (gameid); - - --- --- TOC entry 3279 (class 2606 OID 16511) --- Name: grouplist grouplist_pk; Type: CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.grouplist - ADD CONSTRAINT grouplist_pk PRIMARY KEY (groupid); - - --- --- TOC entry 3281 (class 2606 OID 16513) --- Name: messages messages_pk; Type: CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.messages - ADD CONSTRAINT messages_pk PRIMARY KEY (messageid); - - --- --- TOC entry 3283 (class 2606 OID 16515) --- Name: partner partner_pk; Type: CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.partner - ADD CONSTRAINT partner_pk PRIMARY KEY (partnerid); - - --- --- TOC entry 3285 (class 2606 OID 16517) --- Name: profiles profiles_pk; Type: CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.profiles - ADD CONSTRAINT profiles_pk PRIMARY KEY (profileid); - - --- --- TOC entry 3287 (class 2606 OID 16519) --- Name: pstorage pstorage_pk; Type: CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.pstorage - ADD CONSTRAINT pstorage_pk PRIMARY KEY (pstorageid); - - --- --- TOC entry 3289 (class 2606 OID 16521) --- Name: sakestorage sakestorage_pk; Type: CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.sakestorage - ADD CONSTRAINT sakestorage_pk PRIMARY KEY (sakestorageid); - - --- --- TOC entry 3291 (class 2606 OID 16523) --- Name: subprofiles subprofiles_pk; Type: CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.subprofiles - ADD CONSTRAINT subprofiles_pk PRIMARY KEY (subprofileid); - - --- --- TOC entry 3293 (class 2606 OID 16525) --- Name: users users_pk; Type: CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.users - ADD CONSTRAINT users_pk PRIMARY KEY (userid); - - --- --- TOC entry 3294 (class 2606 OID 16526) --- Name: addrequests addrequests_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.addrequests - ADD CONSTRAINT addrequests_fk FOREIGN KEY (profileid) REFERENCES unispy.profiles(profileid); - - --- --- TOC entry 3295 (class 2606 OID 16531) --- Name: blocked blocked_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.blocked - ADD CONSTRAINT blocked_fk FOREIGN KEY (profileid) REFERENCES unispy.profiles(profileid); - - --- --- TOC entry 3296 (class 2606 OID 16536) --- Name: friends friends_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.friends - ADD CONSTRAINT friends_fk FOREIGN KEY (profileid) REFERENCES unispy.profiles(profileid); - - --- --- TOC entry 3297 (class 2606 OID 16541) --- Name: grouplist grouplist_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.grouplist - ADD CONSTRAINT grouplist_fk FOREIGN KEY (gameid) REFERENCES unispy.games(gameid); - - --- --- TOC entry 3298 (class 2606 OID 16546) --- Name: profiles profiles_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.profiles - ADD CONSTRAINT profiles_fk FOREIGN KEY (userid) REFERENCES unispy.users(userid); - - --- --- TOC entry 3299 (class 2606 OID 16551) --- Name: pstorage pstorage_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.pstorage - ADD CONSTRAINT pstorage_fk FOREIGN KEY (profileid) REFERENCES unispy.profiles(profileid); - - --- --- TOC entry 3300 (class 2606 OID 16556) --- Name: subprofiles subprofiles_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - --- - -ALTER TABLE ONLY unispy.subprofiles - ADD CONSTRAINT subprofiles_fk FOREIGN KEY (profileid) REFERENCES unispy.profiles(profileid); - - --- Completed on 2022-03-02 20:15:24 CET - --- --- PostgreSQL database dump complete --- diff --git a/common/docker-compose.build.yml b/common/docker-compose.build.yml deleted file mode 100644 index 571e96503..000000000 --- a/common/docker-compose.build.yml +++ /dev/null @@ -1,104 +0,0 @@ -# Use this docker-compose file to build RetroSpy for test purposes. - -version: '3.7' -services: - # cdkey: - # build: - # context: ../ - # dockerfile: ./src/Servers/CDKey/src/Dockerfile - # image: gameprogressive/cdkey - # ports: - # - "29910:29910/udp" - # stdin_open: true - # tty: true - - chat: - build: - context: ../ - dockerfile: ./src/Servers/Chat/src/Dockerfile - image: gameprogressive/chat - ports: - - "6667:6667" - stdin_open: true - tty: true - - gs: - build: - context: ../ - dockerfile: ./src/Servers/GameStatus/src/Dockerfile - image: gameprogressive/gs - ports: - - "29920:29920" - stdin_open: true - tty: true - - gtr: - build: - context: ../ - dockerfile: ./src/Servers/GameTrafficRelay/src/Dockerfile - image: gameprogressive/gtr - ports: - - "10086:10086/udp" - stdin_open: true - tty: true - - nn: - build: - context: ../ - dockerfile: ./src/Servers/NatNegotiation/src/Dockerfile - image: gameprogressive/nn - ports: - - "27901:27901/udp" - stdin_open: true - tty: true - - pcm: - build: - context: ../ - dockerfile: ./src/Servers/PresenceConnectionManager/src/Dockerfile - image: gameprogressive/pcm - ports: - - "29900:29900" - stdin_open: true - tty: true - - psp: - build: - context: ../ - dockerfile: ./src/Servers/PresenceSearchPlayer/src/Dockerfile - image: gameprogressive/psp - ports: - - "29901:29901" - stdin_open: true - tty: true - - qr: - build: - context: ../ - dockerfile: ./src/Servers/QueryReport/src/Dockerfile - image: gameprogressive/qr - ports: - - "27900:27900/udp" - stdin_open: true - tty: true - - sb: - build: - context: ../ - dockerfile: ./src/Servers/ServerBrowser/src/Dockerfile - image: gameprogressive/sb - ports: - - "28910:28910" - - "28900:28900" - stdin_open: true - tty: true - - ws: - build: - context: ../ - dockerfile: ./src/Servers/WebServer/src/Dockerfile - image: gameprogressive/ws - ports: - - "80:80" - stdin_open: true - tty: true diff --git a/config.json b/config.json new file mode 100644 index 000000000..51cf7a701 --- /dev/null +++ b/config.json @@ -0,0 +1,97 @@ +{ + "database": { + "server": "ip", + "port": 5432, + "database": "unispy-db", + "username": "unispy", + "password": "123456", + "ssl_mode": "require", + "trust_server_cert": "false", + "ssl_key": "", + "ssl_password": "", + "root_cert": "" + }, + "redis": { + "server": "ip", + "port": 5678, + "user": "", + "password": "password", + "ssl": "true", + "ssl_host": "" + }, + "servers": [ + { + "server_id": "00000000-0000-0000-0000-000000000000", + "server_name": "PresenceConnectionManager", + "public_address": "0.0.0.0", + "listening_port": 29900 + }, + { + "server_id": "00000000-0000-0000-0000-000000000000", + "server_name": "PresenceSearchPlayer", + "public_address": "0.0.0.0", + "listening_port": 29901 + }, + { + "server_id": "00000000-0000-0000-0000-000000000000", + "server_name": "CDKey", + "public_address": "0.0.0.0", + "listening_port": 29910 + }, + { + "server_id": "00000000-0000-0000-0000-000000000000", + "server_name": "ServerBrowserV1", + "public_address": "0.0.0.0", + "listening_port": 28900 + }, + { + "server_id": "00000000-0000-0000-0000-000000000000", + "server_name": "ServerBrowserV2", + "public_address": "0.0.0.0", + "listening_port": 28910 + }, + { + "server_id": "00000000-0000-0000-0000-000000000000", + "server_name": "QueryReport", + "public_address": "0.0.0.0", + "listening_port": 27900 + }, + { + "server_id": "00000000-0000-0000-0000-000000000000", + "server_name": "NatNegotiation", + "public_address": "0.0.0.0", + "listening_port": 27901 + }, + { + "server_id": "00000000-0000-0000-0000-000000000000", + "server_name": "GameStatus", + "public_address": "0.0.0.0", + "listening_port": 29920 + }, + { + "server_id": "00000000-0000-0000-0000-000000000000", + "server_name": "Chat", + "public_address": "0.0.0.0", + "listening_port": 6667 + }, + { + "server_id": "00000000-0000-0000-0000-000000000000", + "server_name": "WebServices", + "public_address": "0.0.0.0", + "listening_port": 80 + }, + { + "server_id": "00000000-0000-0000-0000-000000000000", + "server_name": "GameTrafficRelay", + "public_address": "0.0.0.0", + "listening_port": 10086 + } + ], + "backend": { + "url": "http://localhost:8080" + }, + "logging": { + "path": "~/Downloads/unispy_server/log/", + "min_log_level": "verbose" + } +} \ No newline at end of file diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..193262bcbdff0866577cc1e23ba5718c6feed83a GIT binary patch literal 6148 zcmeHKI|>3p3{6x}u(7n9D|mxJ^aOhW3&lnh6s@=NTprDr52CC#f{naD@@6u5v+OH2 z8xhg@c|8-Eh{yg>muLQd8@Ilv zF-rxg02QDDRDcS6setuf*k~NcNCl_>6?iCM--iM>tch))e>xC+1ON_@cEj3d31G1V zuqL*F$iOtHz@Tc57#eisOV-uIHZbU-Iech7S#v^Be>(0jUM^Y#8L0pjcvYYu+llr6 zCH%wu|4QPH3Q&Q+Qa}fbW--SrWpC}gob}oQU%{>B1~a>v35N5qNppj Y#(7O_1D%e%(}DaMFkNU=;MWS=0Vkdn#{d8T literal 0 HcmV?d00001 diff --git a/src/.devcontainer/Dockerfile b/src/.devcontainer/Dockerfile new file mode 100644 index 000000000..5ec40a9c7 --- /dev/null +++ b/src/.devcontainer/Dockerfile @@ -0,0 +1,12 @@ +FROM node:18 + +# Install basic development tools +RUN apt update && apt install -y less man-db sudo +RUN apt install openssh-server -y +# Ensure default `node` user has access to `sudo` +ARG USERNAME=node +RUN echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ + && chmod 0440 /etc/sudoers.d/$USERNAME + +# Set `DEVCONTAINER` environment variable to help with orientation +ENV DEVCONTAINER=true \ No newline at end of file diff --git a/src/.devcontainer/devcontainer.json b/src/.devcontainer/devcontainer.json new file mode 100644 index 000000000..5e217ff0b --- /dev/null +++ b/src/.devcontainer/devcontainer.json @@ -0,0 +1,11 @@ +// See https://containers.dev/implementors/json_reference/ for configuration reference +{ + "name": "Untitled Node.js project", + "build": { + "dockerfile": "Dockerfile" + }, +"remoteUser": "node", +"features": { + "ghcr.io/devcontainers/features/git:1": {} +} +} diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json new file mode 100644 index 000000000..7431a95a1 --- /dev/null +++ b/src/.vscode/launch.json @@ -0,0 +1,26 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Python Debugger: Current File", + "type": "debugpy", + "request": "launch", + "program": "${file}", + "console": "integratedTerminal", + "env": { + "PYTHONPATH": "${workspaceFolder}" + }, + "justMyCode": true + }, + { + "name": "pcm", + "type": "debugpy", + "request": "launch", + "program": "${workSpace}/servers/presence_connection_manager.py", + "console": "integratedTerminal" + } + ] +} \ No newline at end of file diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs deleted file mode 100644 index 4921a64f5..000000000 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ClientBase.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.Core.Extension; - -namespace UniSpy.Server.Core.Abstraction.BaseClass -{ - public abstract class ClientBase : IClient, ITestClient, IDisposable - { - public IServer Server { get; private set; } - public IConnection Connection { get; private set; } - public ICryptography Crypto { get; set; } - public ClientInfoBase Info { get; protected set; } - /// - /// The timer to count and invoke some event - /// - protected EasyTimer _timer { get; set; } - /// - /// Is logging the raw byte[] requests - /// - public bool IsLogRaw { get; protected set; } - public ClientBase(IConnection connection, IServer server) - { - Connection = connection; - Server = server; - EventBinding(); - } - protected virtual void EventBinding() - { - switch (Connection.ConnectionType) - { - case NetworkConnectionType.Tcp: - ((ITcpConnection)Connection).OnReceive += OnReceived; - ((ITcpConnection)Connection).OnConnect += OnConnected; - ((ITcpConnection)Connection).OnDisconnect += OnDisconnected; - break; - case NetworkConnectionType.Udp: - ((IUdpConnection)Connection).OnReceive += OnReceived; - _timer = new EasyTimer(TimeSpan.FromMinutes(5), TimeSpan.FromMinutes(1)); - _timer.Elapsed += (s, e) => CheckExpiredClient(); - _timer.Start(); - break; - case NetworkConnectionType.Http: - ((IHttpConnection)Connection).OnReceive += OnReceived; - break; - case NetworkConnectionType.Test: - this.LogVerbose("Using unit-test mock connection."); - break; - default: - throw new Exception("Unsupported connection type."); - } - } - /// - /// Only work for tcp - /// - // protected virtual void OnConnected() => ClientManagerBase.AddClient(this); - protected virtual void OnConnected() { } - - /// - /// Only work for tcp - /// - protected virtual void OnDisconnected() => Dispose(); - - protected abstract ISwitcher CreateSwitcher(object buffer); - /// - /// Invoked when received message from game client - /// - /// Byte[], string, Http - protected virtual void OnReceived(object buffer) - { - switch (Connection.ConnectionType) - { - case NetworkConnectionType.Tcp: - goto default; - case NetworkConnectionType.Udp: - _timer.RefreshLastActiveTime(); - goto default; - case NetworkConnectionType.Http: - var tempBuffer = ((IHttpRequest)buffer).Body; - this.LogNetworkReceiving(tempBuffer); - break; - default: - buffer = DecryptMessage((byte[])buffer); - this.LogNetworkReceiving((byte[])buffer); - break; - } - // we let child class to create swicher for us - var switcher = CreateSwitcher(buffer); - switcher.Handle(); - } - protected virtual byte[] DecryptMessage(byte[] buffer) - { - if (Crypto is not null) - { - return Crypto.Decrypt(buffer); - } - else - { - return buffer; - } - } - /// - /// Check if the udp client is expired, - /// this method will only be invoked by UdpServer - /// - protected void CheckExpiredClient() - { - // we calculate the interval between last packe and current time - if (_timer.IsExpired) - { - Dispose(); - } - } - - public void Dispose() - { - this.LogDebug("client disposed."); - switch (Connection.ConnectionType) - { - case NetworkConnectionType.Tcp: - ((ITcpConnection)Connection).OnReceive -= OnReceived; - ((ITcpConnection)Connection).OnConnect -= OnConnected; - ((ITcpConnection)Connection).OnDisconnect -= OnDisconnected; - ClientManagerBase.RemoveClient(this); - break; - case NetworkConnectionType.Udp: - ((IUdpConnection)Connection).OnReceive -= OnReceived; - _timer.Dispose(); - ClientManagerBase.RemoveClient(this); - break; - case NetworkConnectionType.Http: - ((IHttpConnection)Connection).OnReceive -= OnReceived; - break; - } - } - /// - /// Sending IResponse to client(ciphertext or plaintext) - /// - public void Send(IResponse response) - { - byte[] buffer = null; - response.Build(); - if (response.SendingBuffer.GetType() == typeof(string)) - { - buffer = UniSpyEncoding.GetBytes((string)response.SendingBuffer); - } - else - { - buffer = (byte[])response.SendingBuffer; - } - this.LogNetworkSending(buffer); - //Encrypt the response if Crypto is not null - if (Crypto is not null) - { - buffer = Crypto.Encrypt(buffer); - } - Connection.Send(buffer); - } - /// - /// Received function for unit-test - /// - /// Raw byte array - void ITestClient.TestReceived(byte[] buffer) - { - if (Crypto is not null) - { - Crypto = null; - } - OnReceived(buffer); - } - } -} diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ClientInfoBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ClientInfoBase.cs deleted file mode 100644 index b2349b10e..000000000 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ClientInfoBase.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UniSpy.Server.Core.Abstraction.BaseClass -{ - public abstract class ClientInfoBase - { - public ClientInfoBase() - { - } - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ClientManagerBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ClientManagerBase.cs deleted file mode 100644 index adeb65ad3..000000000 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ClientManagerBase.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Collections.Concurrent; -using System.Net; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Core.Abstraction.BaseClass -{ - /// - /// This class provide global access to client pool, - /// if you need search client in specific server, - /// please inherit this class and create static method. - /// - public abstract class ClientManagerBase - { - public static readonly ConcurrentDictionary ClientPool = new ConcurrentDictionary(); - public static void AddClient(IClient client) - { - ClientPool.TryAdd(client.Connection.RemoteIPEndPoint, client); - } - public static IClient RemoveClient(IClient client) - { - return RemoveClient(client.Connection.RemoteIPEndPoint); - } - public static IClient RemoveClient(IPEndPoint endPoint) - { - ClientPool.TryRemove(endPoint, out var client); - return client; - } - public static IClient GetClient(IPEndPoint endPoint) - { - if (ClientPool.TryGetValue(endPoint, out var client)) - { - return client; - } - else - { - return default; - } - } - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/CmdHandlerBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/CmdHandlerBase.cs deleted file mode 100755 index f914831ea..000000000 --- a/src/Libraries/Core/src/Abstraction/BaseClass/CmdHandlerBase.cs +++ /dev/null @@ -1,75 +0,0 @@ -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.Core.Abstraction.BaseClass -{ - public abstract class CmdHandlerBase : IHandler, ITestHandler - { - protected IClient _client { get; } - protected IRequest _request { get; } - protected ResultBase _result { get; set; } - protected IResponse _response { get; set; } - - RequestBase ITestHandler.Request => (RequestBase)_request; - ResultBase ITestHandler.Result => (ResultBase)_result; - ResponseBase ITestHandler.Response => (ResponseBase)_response; - - public CmdHandlerBase(IClient client, IRequest request) - { - _client = client; - _request = request; - } - public virtual void Handle() - { - try - { - // we first log this class - LogCurrentClass(); - // then we handle it - RequestCheck(); - DataOperation(); - ResponseConstruct(); - if (_response is null) - { - return; - } - Response(); - } - catch (System.Exception ex) - { - HandleException(ex); - } - } - protected virtual void RequestCheck() - { - // if there is gamespy raw request we convert it to unispy request - if (_request.RawRequest is not null) - { - _request.Parse(); - } - } - protected virtual void DataOperation() { } - protected virtual void ResponseConstruct() { } - /// - /// The response process - /// - protected virtual void Response() - { - _client.Send(_response); - } - - protected virtual void HandleException(System.Exception ex) => UniSpy.Exception.HandleException(ex, _client); - - private void LogCurrentClass() - { - if (_client is null) - { - LogWriter.LogCurrentClass(this); - } - else - { - _client.LogCurrentClass(this); - } - } - } -} diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/CmdSwitcherBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/CmdSwitcherBase.cs deleted file mode 100755 index 83e9fe121..000000000 --- a/src/Libraries/Core/src/Abstraction/BaseClass/CmdSwitcherBase.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.Core.Abstraction.BaseClass -{ - public abstract class CmdSwitcherBase : ISwitcher - { - protected IClient _client; - protected object _rawRequest { get; private set; } - protected List> _requests { get; private set; } - protected List _handlers { get; private set; } - - public CmdSwitcherBase(IClient client, object rawRequest) - { - _client = client; - _rawRequest = rawRequest; - _handlers = new List(); - _requests = new List>(); - } - - public virtual void Handle() - { - try - { - //First we process the message split it to raw requests. - ProcessRawRequest(); - // Then we process the raw requests to UniSpy requests. - if (_requests.Count == 0) - { - return; - } - // Then we create UniSpy handlers by UniSpy requests. - foreach (var rawRequest in _requests) - { - var handler = CreateCmdHandlers(rawRequest.Key, rawRequest.Value); - if (handler is null) - { - _client.LogWarn("Request ignored."); - continue; - } - _handlers.Add(handler); - } - if (_handlers.Count == 0) - { - return; - } - - // TODO changes foreach to parallel foreach - foreach (var handler in _handlers) - { - handler.Handle(); - } - } - catch (System.Exception e) - { - UniSpy.Exception.HandleException(e, _client); - } - } - /// - /// Split the raw requests into single raw request - /// get the request type and create dictionary of (request type, request) - /// - /// request type, single request - protected abstract void ProcessRawRequest(); - /// - /// Call this in size - /// - /// - /// - protected abstract IHandler CreateCmdHandlers(object name, object rawRequest); - } -} diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/EncParamBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/EncParamBase.cs deleted file mode 100644 index ce7f39b06..000000000 --- a/src/Libraries/Core/src/Abstraction/BaseClass/EncParamBase.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace UniSpy.Server.Core.Abstraction.BaseClass -{ - /// - /// The base class of encryption parameters - /// IEncryption can use the parameter to encrypt message - /// - public abstract record EncParamBase { } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/RedisChannel.cs b/src/Libraries/Core/src/Abstraction/BaseClass/RedisChannel.cs deleted file mode 100755 index 33e92a26b..000000000 --- a/src/Libraries/Core/src/Abstraction/BaseClass/RedisChannel.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using Newtonsoft.Json; -using StackExchange.Redis; -using UniSpy.Server.Core.Config; - -namespace UniSpy.Server.Core.Abstraction.BaseClass -{ - - public abstract class RedisChannelBase : IDisposable - { - public delegate void OnReceivedMessageEventHandler(T message); - public event OnReceivedMessageEventHandler OnReceived; - protected string _redisChannelName; - /// - /// Get all subscriber in Redis - /// - protected ISubscriber _subscriber = ConfigManager.Config.Redis.RedisConnection.GetSubscriber(); - public bool IsStarted { get; private set; } - public RedisChannelBase(string redisChannelName) - { - _redisChannelName = redisChannelName; - OnReceived += ReceivedMessage; - } - - /// - /// Run StartSubscribe manually - /// Some server only require publish message - /// so they do not need to run this function - /// - public void Subscribe() - { - if (IsStarted == false) - { - _subscriber.Subscribe(_redisChannelName, (channel, message) - => - { - T msg = DeserializeMessage(message); - OnReceived(msg); - }); - IsStarted = true; - } - } - - public virtual void ReceivedMessage(T message) - { - - } - - public virtual void PublishMessage(T message) - { - if (message is null) - { - return; - } - string jsonStr = SerializeMessage(message); - _subscriber.Publish(_redisChannelName, jsonStr); - } - - protected virtual string SerializeMessage(T message) => JsonConvert.SerializeObject(message); - - protected virtual T DeserializeMessage(string message) => JsonConvert.DeserializeObject(message); - public void Unsubscribe() - { - _subscriber.Unsubscribe(_redisChannelName); - OnReceived -= ReceivedMessage; - } - public void Dispose() => Unsubscribe(); - } -} diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/RedisClient.cs b/src/Libraries/Core/src/Abstraction/BaseClass/RedisClient.cs deleted file mode 100644 index 16ad67ac5..000000000 --- a/src/Libraries/Core/src/Abstraction/BaseClass/RedisClient.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.Core.Config; - -namespace UniSpy.Server.Core.Abstraction.BaseClass -{ - public class RedisClient : LinqToRedis.RedisClient where TValue : RedisKeyValueObject - { - public RedisClient() : base(ConfigManager.Config.Redis.RedisConnection) - { - } - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/RedisKeyValueObject.cs b/src/Libraries/Core/src/Abstraction/BaseClass/RedisKeyValueObject.cs deleted file mode 100644 index ccf61d73e..000000000 --- a/src/Libraries/Core/src/Abstraction/BaseClass/RedisKeyValueObject.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using UniSpy.Server.Core.Extension.Redis; - -namespace UniSpy.Server.Core.Abstraction.BaseClass -{ - public record RedisKeyValueObject : LinqToRedis.RedisKeyValueObject - { - public RedisKeyValueObject() - { - } - - public RedisKeyValueObject(RedisDbNumber db, TimeSpan? expireTime = null) : base((int)db, expireTime) - { - } - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/RequestBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/RequestBase.cs deleted file mode 100755 index 21db0c3ff..000000000 --- a/src/Libraries/Core/src/Abstraction/BaseClass/RequestBase.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Xml.Serialization; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Core.Abstraction.BaseClass -{ - public abstract class RequestBase : IRequest - { - [XmlIgnoreAttribute] - public object CommandName { get; protected set; } - [XmlIgnoreAttribute] - public object RawRequest { get; protected set; } - public RequestBase() { } - public RequestBase(object rawRequest) - { - RawRequest = rawRequest; - } - - public abstract void Parse(); - } -} diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ResponseBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ResponseBase.cs deleted file mode 100755 index f86195035..000000000 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ResponseBase.cs +++ /dev/null @@ -1,37 +0,0 @@ -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Core.Abstraction.BaseClass -{ - public abstract class ResponseBase : IResponse - { - /// - /// Represents the plaintext response data - /// - public object SendingBuffer { get; protected set; } - - /* - /// - /// Create new inhereted type of this property to make access easier without type convertion every time - /// - public ResultBase Result => _result; - /// - /// Create new inhereted type of this property to make access easier without type convertion every time - /// - public RequestBase Request => _request; - */ - - protected ResultBase _result { get; } - protected RequestBase _request { get; } - protected IClient _client { get; } - public ResponseBase(RequestBase request, ResultBase result) - { - _request = request; - _result = result; - } - - /// - /// Build response message - /// - public abstract void Build(); - } -} diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ResultBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ResultBase.cs deleted file mode 100755 index c014f07a0..000000000 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ResultBase.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UniSpy.Server.Core.Abstraction.BaseClass -{ - public abstract class ResultBase - { - public ResultBase() - { - } - } -} diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ServerBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ServerBase.cs deleted file mode 100644 index 56b5e1682..000000000 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ServerBase.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using Newtonsoft.Json; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Config; - -namespace UniSpy.Server.Core.Abstraction.BaseClass -{ - /// - /// Represent UniSpy server instance abstraction - /// - public abstract class ServerBase : IServer - { - public Guid Id { get; private set; } - public IPEndPoint ListeningIPEndPoint { get; private set; } - public IPEndPoint PublicIPEndPoint { get; private set; } - [JsonIgnore] - public IConnectionManager ConnectionManager; - protected UniSpyServerConfig _cfg; - public string Name { get; } = _name; - /// - /// This name is for reading from child class, indicating the server name - /// - protected static string _name; - - public ServerBase() - { - SetServerInfo(); - ConnectionManager = CreateConnectionManager(ListeningIPEndPoint); - ConnectionManager.OnInitialization += HandleConnectionInitialization; - } - /// - /// This constructor is for unittests - /// - public ServerBase(IConnectionManager manager) - { - SetServerInfo(); - ConnectionManager = manager; - } - public void SetServerInfo() - { - _cfg = ConfigManager.Config.Servers.Where(s => s.ServerName == _name).First(); - Id = _cfg.ServerID; - ListeningIPEndPoint = _cfg.ListeningIPEndPoint; - PublicIPEndPoint = _cfg.PublicIPEndPoint; - } - protected abstract IConnectionManager CreateConnectionManager(IPEndPoint endPoint); - protected virtual IClient HandleConnectionInitialization(IConnection connection) - { - var client = ClientManagerBase.GetClient(connection.RemoteIPEndPoint); - if (client is null) - { - client = CreateClient(connection); - ClientManagerBase.AddClient(client); - } - return client; - } - protected abstract IClient CreateClient(IConnection connection); - - public virtual void Start() => ConnectionManager.Start(); - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Abstraction/BaseClass/ServerLauncherBase.cs b/src/Libraries/Core/src/Abstraction/BaseClass/ServerLauncherBase.cs deleted file mode 100755 index 1338eb1ad..000000000 --- a/src/Libraries/Core/src/Abstraction/BaseClass/ServerLauncherBase.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using ConsoleTables; -using Figgle; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Config; -using UniSpy.Server.Core.Database.DatabaseModel; -using System.Collections.Generic; - -namespace UniSpy.Server.Core.Abstraction.BaseClass.Factory -{ - /// - /// Each server launcher only launch an instance of IServer - /// - public abstract class ServerLauncherBase - { - /// - /// UniSpy server version - /// - public static readonly string Version = "0.8.1"; - - public static List ServerInstances { get; protected set; } = new List(); - public ServerLauncherBase() - { - } - public virtual void Start() - { - ShowUniSpyLogo(); - ConnectMySql(); - ConnectRedis(); - LaunchServer(); - } - protected abstract List LaunchNetworkService(); - protected virtual void LaunchServer() - { - ServerInstances = LaunchNetworkService(); - if (ServerInstances.Count == 0) - { - throw new Exception("Server created failed"); - } - var table = new ConsoleTable("Server Name", "Listening Address", "Listening Port"); - Console.Title = $"UniSpyServer {Version} - {ServerInstances[0].Name}"; - - foreach (var server in ServerInstances) - { - server.Start(); - table.AddRow(server.Name, server.ListeningIPEndPoint.Address, server.ListeningIPEndPoint.Port); - } - table.Write(ConsoleTables.Format.Alternative); - Console.WriteLine("Server successfully started!"); - } - - protected void ConnectRedis() - { - var redisConfig = ConfigManager.Config.Redis; - try - { - using var r = StackExchange.Redis.ConnectionMultiplexer.Connect(redisConfig.ConnectionString); - } - catch (System.Exception e) - { - throw new UniSpy.Exception("Can not connect to Redis", e); - } - Console.WriteLine($"Successfully connected to Redis at {redisConfig.Server}:{redisConfig.Port}"); - } - protected void ConnectMySql() - { - //Determine which database is used and establish the database connection. - var dbConfig = ConfigManager.Config.Database; - if (!new UniSpyContext().Database.CanConnect()) - { - throw new Exception($"Can not connect to {dbConfig.Type}!"); - } - - Console.WriteLine($"Successfully connected to {dbConfig.Type} at {dbConfig.Server}:{dbConfig.Port}"); - } - protected static void ShowUniSpyLogo() - { - Console.WriteLine(""); - Console.WriteLine(FiggleFonts.Small.Render("UniSpy.Server")); - Console.WriteLine(@"Version: " + Version); - } - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Abstraction/Interface/IClient.cs b/src/Libraries/Core/src/Abstraction/Interface/IClient.cs deleted file mode 100644 index 10f1f7500..000000000 --- a/src/Libraries/Core/src/Abstraction/Interface/IClient.cs +++ /dev/null @@ -1,23 +0,0 @@ -using UniSpy.Server.Core.Abstraction.BaseClass; - -namespace UniSpy.Server.Core.Abstraction.Interface -{ - public interface ITestClient - { - public void TestReceived(byte[] buffer); - } - public interface IClient - { - /// - /// whether log raw request and response message bytes - /// - /// - bool IsLogRaw { get; } - IConnection Connection { get; } - IServer Server { get; } - ICryptography Crypto { get; } - // we store client info here - ClientInfoBase Info { get; } - public void Send(IResponse response); - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Abstraction/Interface/IConnection.cs b/src/Libraries/Core/src/Abstraction/Interface/IConnection.cs deleted file mode 100644 index e5c8b2fa8..000000000 --- a/src/Libraries/Core/src/Abstraction/Interface/IConnection.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System; -using System.Net; -using UniSpy.Server.Core.Events; - -namespace UniSpy.Server.Core.Abstraction.Interface -{ - - public enum NetworkConnectionType - { - Tcp, - Udp, - Http, - Https, - Test, - } - /// - /// Represent a remote connection, tcp, udp, http etc. - /// - public interface IConnection - { - event OnReceivedEventHandler OnReceive; - IConnectionManager Manager { get; } - IPEndPoint RemoteIPEndPoint { get; } - void Send(string response); - void Send(byte[] response); - NetworkConnectionType ConnectionType { get; } - } - /// - /// Represent a udp connection - /// - public interface IUdpConnection : IConnection - { - void Send(IPEndPoint endPoint, byte[] response); - void Send(IPEndPoint endPoint, string response); - } - /// - /// Represent a tcp connection - /// - public interface ITcpConnection : IConnection, IDisposable - { - event OnConnectedEventHandler OnConnect; - event OnDisconnectedEventHandler OnDisconnect; - void Disconnect(); - } - public interface IHttpConnection : IConnection - { - } - - public interface IConnectionManager : IDisposable - { - event OnConnectingEventHandler OnInitialization; - void Start(); - } -} diff --git a/src/Libraries/Core/src/Abstraction/Interface/IEncryption.cs b/src/Libraries/Core/src/Abstraction/Interface/IEncryption.cs deleted file mode 100644 index cac6ad841..000000000 --- a/src/Libraries/Core/src/Abstraction/Interface/IEncryption.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace UniSpy.Server.Core.Abstraction.Interface -{ - public interface ICryptography - { - byte[] Encrypt(byte[] data); - byte[] Decrypt(byte[] data); - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Abstraction/Interface/IHandler.cs b/src/Libraries/Core/src/Abstraction/Interface/IHandler.cs deleted file mode 100755 index 25f519164..000000000 --- a/src/Libraries/Core/src/Abstraction/Interface/IHandler.cs +++ /dev/null @@ -1,15 +0,0 @@ -using UniSpy.Server.Core.Abstraction.BaseClass; - -namespace UniSpy.Server.Core.Abstraction.Interface -{ - public interface IHandler - { - void Handle(); - } - public interface ITestHandler - { - RequestBase Request { get; } - ResultBase Result { get; } - ResponseBase Response { get; } - } -} diff --git a/src/Libraries/Core/src/Abstraction/Interface/IHttpRequest.cs b/src/Libraries/Core/src/Abstraction/Interface/IHttpRequest.cs deleted file mode 100644 index 17a00e1d7..000000000 --- a/src/Libraries/Core/src/Abstraction/Interface/IHttpRequest.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace UniSpy.Server.Core.Abstraction.Interface -{ - public interface IHttpRequest - { - string Body { get; } - Uri Url { get; } - string Method { get; } - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Abstraction/Interface/ILogger.cs b/src/Libraries/Core/src/Abstraction/Interface/ILogger.cs deleted file mode 100644 index c5c68bec9..000000000 --- a/src/Libraries/Core/src/Abstraction/Interface/ILogger.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Net; - -namespace UniSpy.Server.Core.Abstraction.Interface -{ - public interface ILogger - { - public void LogInfo(string message); - public void LogVerbose(string message); - public void LogDebug(string message); - public void LogWarn(string messsage); - public void LogError(string message); - public void LogNetworkReceiving(byte[] message); - public void LogNetworkReceiving(string message); - public void LogNetworkSending(byte[] message); - public void LogNetworkSending(string message); - public void LogNetworkForwarding(IPEndPoint receiver, byte[] message); - public void LogNetworkForwarding(IPEndPoint receiver, string message); - public void LogNetworkMultiCast(byte[] message); - public void LogNetworkMultiCast(string message); - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Abstraction/Interface/IRedisChannelEvent.cs b/src/Libraries/Core/src/Abstraction/Interface/IRedisChannelEvent.cs deleted file mode 100755 index 437c788a1..000000000 --- a/src/Libraries/Core/src/Abstraction/Interface/IRedisChannelEvent.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UniSpy.Server.Core.Abstraction.Interface -{ - public interface IRedisChannelEvent - { - void StartSubscribe(); - void ReceivedMessage(); - void PublishMessage(); - } -} diff --git a/src/Libraries/Core/src/Abstraction/Interface/IRequest.cs b/src/Libraries/Core/src/Abstraction/Interface/IRequest.cs deleted file mode 100755 index 13928fb93..000000000 --- a/src/Libraries/Core/src/Abstraction/Interface/IRequest.cs +++ /dev/null @@ -1,10 +0,0 @@ - -namespace UniSpy.Server.Core.Abstraction.Interface -{ - public interface IRequest - { - object CommandName { get; } - object RawRequest { get; } - void Parse(); - } -} diff --git a/src/Libraries/Core/src/Abstraction/Interface/IResponse.cs b/src/Libraries/Core/src/Abstraction/Interface/IResponse.cs deleted file mode 100644 index fb3ef883d..000000000 --- a/src/Libraries/Core/src/Abstraction/Interface/IResponse.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace UniSpy.Server.Core.Abstraction.Interface -{ - public interface IResponse - { - object SendingBuffer { get; } - void Build(); - } -} diff --git a/src/Libraries/Core/src/Abstraction/Interface/IServer.cs b/src/Libraries/Core/src/Abstraction/Interface/IServer.cs deleted file mode 100755 index af1c094ea..000000000 --- a/src/Libraries/Core/src/Abstraction/Interface/IServer.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using System.Net; -using StackExchange.Redis; - -namespace UniSpy.Server.Core.Abstraction.Interface -{ - /// - /// Represents unispy server instance - /// - public interface IServer - { - static IConnectionMultiplexer RedisConnection { get; } - Guid Id { get; } - string Name { get; } - IPEndPoint ListeningIPEndPoint { get; } - IPEndPoint PublicIPEndPoint { get; } - // UniSpyServerConfig ServerConfig { get; } - void Start(); - } -} diff --git a/src/Libraries/Core/src/Abstraction/Interface/ISwicher.cs b/src/Libraries/Core/src/Abstraction/Interface/ISwicher.cs deleted file mode 100644 index 739bc5ab1..000000000 --- a/src/Libraries/Core/src/Abstraction/Interface/ISwicher.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace UniSpy.Server.Core.Abstraction.Interface -{ - public interface ISwitcher - { - void Handle(); - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Config/ConfigManager.cs b/src/Libraries/Core/src/Config/ConfigManager.cs deleted file mode 100755 index 9d74dd281..000000000 --- a/src/Libraries/Core/src/Config/ConfigManager.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using Newtonsoft.Json; -using System.IO; - -namespace UniSpy.Server.Core.Config -{ - public class ConfigManager - { - public static bool IsConfigFileExist => File.Exists(ConfigPath); - public static UniSpyConfig Config = LoadConfigFile(); - public const string ConfigPath = "UniSpyServerConfig.json"; - public const string EnvVarName = "UNISPYCONFIG"; - private static UniSpyConfig LoadConfigFile() - { - var config = Environment.GetEnvironmentVariable(EnvVarName); - if (config is not null && config !="") - { - return JsonConvert.DeserializeObject(config); - } - - if (!IsConfigFileExist) - { - throw new UniSpy.Exception("UniSpy server config file not found"); - } - using (StreamReader fstream = File.OpenText(ConfigPath)) - { - return JsonConvert.DeserializeObject(fstream.ReadToEnd()); - } - } - } -} diff --git a/src/Libraries/Core/src/Config/UniSpyConfig.cs b/src/Libraries/Core/src/Config/UniSpyConfig.cs deleted file mode 100755 index fbc431b2b..000000000 --- a/src/Libraries/Core/src/Config/UniSpyConfig.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using Newtonsoft.Json; -using Serilog.Events; -using StackExchange.Redis; -using UniSpy.Server.Core.Database; - -namespace UniSpy.Server.Core.Config -{ - public class UniSpyConfig - { - public UniSpyDatabaseConfig Database; - public UniSpyRedisConfig Redis; - public List Servers; - public LogEventLevel MinimumLogLevel; - } - public class UniSpyDatabaseConfig - { - public string ConnectionString => - $"Server={Server};" - + $"Port={Port};" - + $"Database={Database};" - + $"Username={Username};" - + $"Password={Password};" - + $"SSL Mode={SSLMode};" - + $"Trust Server Certificate={TrustServerCert};" - + $"SSL Certificate={SSLCert};" - + $"SSL Key={SSLKey};" - + $"SSL Password={SSLPassword};" - + $"Root Certificate={RootCert};"; - public DatabaseType Type; - public string Server; - public int Port; - public string Database; - public string Username; - public string Password; - public string SSLMode; - public bool TrustServerCert; - public string SSLCert; - public string SSLKey; - public string SSLPassword; - public string RootCert; - } - public class UniSpyRedisConfig - { - public string ConnectionString => $"{Server}:{Port},user={User},password={Password},ssl={SSL},sslHost={SSLHost},abortConnect=false"; - public string Server; - public int Port; - public string User; - public string Password; - public bool SSL; - public string SSLHost; - [JsonIgnore] - public IConnectionMultiplexer RedisConnection => ConnectionMultiplexer.Connect(ConnectionString); - } - public class UniSpyServerConfig - { - public Guid ServerID; - public string ServerName; - public IPEndPoint ListeningIPEndPoint => new IPEndPoint(IPAddress.Any, ListeningPort); - public IPEndPoint PublicIPEndPoint => new IPEndPoint(IPAddress.Parse(PublicAddress), ListeningPort); - public string PublicAddress; - public int ListeningPort; - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Addrequest.cs b/src/Libraries/Core/src/Database/DatabaseModel/Addrequest.cs deleted file mode 100644 index 3c14bff67..000000000 --- a/src/Libraries/Core/src/Database/DatabaseModel/Addrequest.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace UniSpy.Server.Core.Database.DatabaseModel -{ - /// - /// Friend request. - /// - public partial class Addrequest - { - public int Addrequestid { get; set; } - public int Profileid { get; set; } - public int Namespaceid { get; set; } - public int Targetid { get; set; } - public string Reason { get; set; } = null!; - public string Syncrequested { get; set; } = null!; - - public virtual Profile Profile { get; set; } = null!; - } -} diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Blocked.cs b/src/Libraries/Core/src/Database/DatabaseModel/Blocked.cs deleted file mode 100644 index 0305931d1..000000000 --- a/src/Libraries/Core/src/Database/DatabaseModel/Blocked.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace UniSpy.Server.Core.Database.DatabaseModel -{ - /// - /// Block list. - /// - public partial class Blocked - { - public int Blockid { get; set; } - public int Profileid { get; set; } - public int Namespaceid { get; set; } - public int Targetid { get; set; } - - public virtual Profile Profile { get; set; } = null!; - } -} diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Friend.cs b/src/Libraries/Core/src/Database/DatabaseModel/Friend.cs deleted file mode 100644 index bb29c1015..000000000 --- a/src/Libraries/Core/src/Database/DatabaseModel/Friend.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace UniSpy.Server.Core.Database.DatabaseModel -{ - /// - /// Friend list. - /// - public partial class Friend - { - public int Friendid { get; set; } - public int Profileid { get; set; } - public int Namespaceid { get; set; } - public int Targetid { get; set; } - - public virtual Profile Profile { get; set; } = null!; - } -} diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Game.cs b/src/Libraries/Core/src/Database/DatabaseModel/Game.cs deleted file mode 100644 index f6999dece..000000000 --- a/src/Libraries/Core/src/Database/DatabaseModel/Game.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Collections.Generic; - -namespace UniSpy.Server.Core.Database.DatabaseModel -{ - /// - /// Game list. - /// - public partial class Game - { - public Game() - { - Grouplists = new HashSet(); - } - - public int Gameid { get; set; } - public string Gamename { get; set; } = null!; - public string Secretkey { get; set; } - public string Description { get; set; } = null!; - public bool Disabled { get; set; } - - public virtual ICollection Grouplists { get; set; } - } -} diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Grouplist.cs b/src/Libraries/Core/src/Database/DatabaseModel/Grouplist.cs deleted file mode 100644 index fe2568849..000000000 --- a/src/Libraries/Core/src/Database/DatabaseModel/Grouplist.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace UniSpy.Server.Core.Database.DatabaseModel -{ - /// - /// Old games use grouplist to create their game rooms. - /// - public partial class Grouplist - { - public int Groupid { get; set; } - public int Gameid { get; set; } - public string Roomname { get; set; } = null!; - - public virtual Game Game { get; set; } = null!; - } -} diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Message.cs b/src/Libraries/Core/src/Database/DatabaseModel/Message.cs deleted file mode 100644 index 9053093cb..000000000 --- a/src/Libraries/Core/src/Database/DatabaseModel/Message.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; - -namespace UniSpy.Server.Core.Database.DatabaseModel -{ - /// - /// Friend messages. - /// - public partial class Message - { - public int Messageid { get; set; } - public int? Namespaceid { get; set; } - public int? Type { get; set; } - public int From { get; set; } - public int To { get; set; } - public DateTime Date { get; set; } - public string Message1 { get; set; } = null!; - } -} diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Partner.cs b/src/Libraries/Core/src/Database/DatabaseModel/Partner.cs deleted file mode 100644 index 95fc3ffa1..000000000 --- a/src/Libraries/Core/src/Database/DatabaseModel/Partner.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace UniSpy.Server.Core.Database.DatabaseModel -{ - /// - /// Partner information, these information are used for authentication and login. - /// - public partial class Partner - { - public int Partnerid { get; set; } - public string Partnername { get; set; } = null!; - } -} diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Profile.cs b/src/Libraries/Core/src/Database/DatabaseModel/Profile.cs deleted file mode 100644 index b1f97bc44..000000000 --- a/src/Libraries/Core/src/Database/DatabaseModel/Profile.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Collections.Generic; - -namespace UniSpy.Server.Core.Database.DatabaseModel -{ - /// - /// User profiles. - /// - public partial class Profile - { - public Profile() - { - Addrequests = new HashSet(); - Blockeds = new HashSet(); - Friends = new HashSet(); - Pstorages = new HashSet(); - Sakestorages = new HashSet(); - Subprofiles = new HashSet(); - } - - public int Profileid { get; set; } - public int Userid { get; set; } - public string Nick { get; set; } = null!; - public int Serverflag { get; set; } - public short? Status { get; set; } - public string Statstring { get; set; } - public string Location { get; set; } - public string Firstname { get; set; } - public string Lastname { get; set; } - public int? Publicmask { get; set; } - public double? Latitude { get; set; } - public double? Longitude { get; set; } - public string Aim { get; set; } - public int? Picture { get; set; } - public int? Occupationid { get; set; } - public int? Incomeid { get; set; } - public int? Industryid { get; set; } - public int? Marriedid { get; set; } - public int? Childcount { get; set; } - public int? Interests1 { get; set; } - public int? Ownership1 { get; set; } - public int? Connectiontype { get; set; } - public short? Sex { get; set; } - public string Zipcode { get; set; } - public string Countrycode { get; set; } - public string Homepage { get; set; } - public int? Birthday { get; set; } - public int? Birthmonth { get; set; } - public int? Birthyear { get; set; } - public int? Icquin { get; set; } - public short Quietflags { get; set; } - public string Streetaddr { get; set; } - public string Streeaddr { get; set; } - public string City { get; set; } - public int? Cpubrandid { get; set; } - public int? Cpuspeed { get; set; } - public short? Memory { get; set; } - public string Videocard1string { get; set; } - public short? Videocard1ram { get; set; } - public string Videocard2string { get; set; } - public short? Videocard2ram { get; set; } - public int? Subscription { get; set; } - public int? Adminrights { get; set; } - - public virtual User User { get; set; } = null!; - public virtual ICollection Addrequests { get; set; } - public virtual ICollection Blockeds { get; set; } - public virtual ICollection Friends { get; set; } - public virtual ICollection Pstorages { get; set; } - public virtual ICollection Sakestorages { get; set; } - public virtual ICollection Subprofiles { get; set; } - } -} diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Pstorage.cs b/src/Libraries/Core/src/Database/DatabaseModel/Pstorage.cs deleted file mode 100644 index 5e077a99b..000000000 --- a/src/Libraries/Core/src/Database/DatabaseModel/Pstorage.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations.Schema; - -namespace UniSpy.Server.Core.Database.DatabaseModel; - -/// -/// Old games use pstorage to store game data. -/// -public partial class Pstorage -{ - public int Pstorageid { get; set; } - - public int Profileid { get; set; } - - public int Ptype { get; set; } - - public int Dindex { get; set; } - [Column(TypeName = "jsonb")] - public Dictionary Data { get; set; } - - public virtual Profile Profile { get; set; } = null!; -} diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Sakestorage.cs b/src/Libraries/Core/src/Database/DatabaseModel/Sakestorage.cs deleted file mode 100644 index 8d3928905..000000000 --- a/src/Libraries/Core/src/Database/DatabaseModel/Sakestorage.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.ComponentModel.DataAnnotations.Schema; -using System.Text.Json; - -namespace UniSpy.Server.Core.Database.DatabaseModel -{ - /// - /// Sake storage system. - /// - public partial class Sakestorage - { - public int Sakestorageid { get; set; } - public string Tableid { get; set; } = null!; - public int Profileid { get; set; } - [Column(TypeName = "jsonb")] - public JsonDocument Userdata { get; set; } = null!; - public virtual Profile Profile { get; set; } = null!; - } -} - - diff --git a/src/Libraries/Core/src/Database/DatabaseModel/Subprofile.cs b/src/Libraries/Core/src/Database/DatabaseModel/Subprofile.cs deleted file mode 100644 index dd7d3d413..000000000 --- a/src/Libraries/Core/src/Database/DatabaseModel/Subprofile.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace UniSpy.Server.Core.Database.DatabaseModel -{ - /// - /// User subprofiles. - /// - public partial class Subprofile - { - public int Subprofileid { get; set; } - public int Profileid { get; set; } - public string Uniquenick { get; set; } - public int Namespaceid { get; set; } - public int Partnerid { get; set; } - public int? Productid { get; set; } - public string Gamename { get; set; } - public string Cdkeyenc { get; set; } - public short? Firewall { get; set; } - public int? Port { get; set; } - public string Authtoken { get; set; } - - public virtual Profile Profile { get; set; } = null!; - } -} diff --git a/src/Libraries/Core/src/Database/DatabaseModel/UnispyContext.cs b/src/Libraries/Core/src/Database/DatabaseModel/UnispyContext.cs deleted file mode 100644 index d0c5f1448..000000000 --- a/src/Libraries/Core/src/Database/DatabaseModel/UnispyContext.cs +++ /dev/null @@ -1,532 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using UniSpy.Server.Core.Config; - -namespace UniSpy.Server.Core.Database.DatabaseModel -{ - public partial class UniSpyContext : DbContext - { - public UniSpyContext() - { - } - - public UniSpyContext(DbContextOptions options) - : base(options) - { - } - - public virtual DbSet Addrequests { get; set; } = null!; - public virtual DbSet Blockeds { get; set; } = null!; - public virtual DbSet Friends { get; set; } = null!; - public virtual DbSet Games { get; set; } = null!; - public virtual DbSet Grouplists { get; set; } = null!; - public virtual DbSet Messages { get; set; } = null!; - public virtual DbSet Partners { get; set; } = null!; - public virtual DbSet Profiles { get; set; } = null!; - public virtual DbSet Pstorages { get; set; } = null!; - public virtual DbSet Sakestorages { get; set; } = null!; - public virtual DbSet Subprofiles { get; set; } = null!; - public virtual DbSet Users { get; set; } = null!; - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - if (!optionsBuilder.IsConfigured) - { - optionsBuilder.UseNpgsql(ConfigManager.Config.Database.ConnectionString); - } - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity(entity => - { - entity.ToTable("addrequests", "unispy"); - - entity.HasComment("Friend request."); - - entity.Property(e => e.Addrequestid) - .HasColumnName("addrequestid") - .HasDefaultValueSql("nextval('addrequests_addrequestid_seq'::regclass)"); - - entity.Property(e => e.Namespaceid).HasColumnName("namespaceid"); - - entity.Property(e => e.Profileid).HasColumnName("profileid"); - - entity.Property(e => e.Reason) - .HasColumnType("character varying") - .HasColumnName("reason"); - - entity.Property(e => e.Syncrequested) - .HasColumnType("character varying") - .HasColumnName("syncrequested"); - - entity.Property(e => e.Targetid).HasColumnName("targetid"); - - entity.HasOne(d => d.Profile) - .WithMany(p => p.Addrequests) - .HasForeignKey(d => d.Profileid) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("addrequests_fk"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => e.Blockid) - .HasName("blocked_pk"); - - entity.ToTable("blocked", "unispy"); - - entity.HasComment("Block list."); - - entity.Property(e => e.Blockid) - .HasColumnName("blockid") - .HasDefaultValueSql("nextval('blocked_blockid_seq'::regclass)"); - - entity.Property(e => e.Namespaceid).HasColumnName("namespaceid"); - - entity.Property(e => e.Profileid).HasColumnName("profileid"); - - entity.Property(e => e.Targetid).HasColumnName("targetid"); - - entity.HasOne(d => d.Profile) - .WithMany(p => p.Blockeds) - .HasForeignKey(d => d.Profileid) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("blocked_fk"); - }); - - modelBuilder.Entity(entity => - { - entity.ToTable("friends", "unispy"); - - entity.HasComment("Friend list."); - - entity.Property(e => e.Friendid) - .HasColumnName("friendid") - .HasDefaultValueSql("nextval('friends_friendid_seq'::regclass)"); - - entity.Property(e => e.Namespaceid).HasColumnName("namespaceid"); - - entity.Property(e => e.Profileid).HasColumnName("profileid"); - - entity.Property(e => e.Targetid).HasColumnName("targetid"); - - entity.HasOne(d => d.Profile) - .WithMany(p => p.Friends) - .HasForeignKey(d => d.Profileid) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("friends_fk"); - }); - - modelBuilder.Entity(entity => - { - entity.ToTable("games", "unispy"); - - entity.HasComment("Game list."); - - entity.Property(e => e.Gameid) - .ValueGeneratedNever() - .HasColumnName("gameid"); - - entity.Property(e => e.Description) - .HasMaxLength(4095) - .HasColumnName("description"); - - entity.Property(e => e.Disabled).HasColumnName("disabled"); - - entity.Property(e => e.Gamename) - .HasColumnType("character varying") - .HasColumnName("gamename"); - - entity.Property(e => e.Secretkey) - .HasColumnType("character varying") - .HasColumnName("secretkey"); - }); - - modelBuilder.Entity(entity => - { - entity.HasKey(e => e.Groupid) - .HasName("grouplist_pk"); - - entity.ToTable("grouplist", "unispy"); - - entity.HasComment("Old games use grouplist to create their game rooms."); - - entity.Property(e => e.Groupid) - .ValueGeneratedNever() - .HasColumnName("groupid"); - - entity.Property(e => e.Gameid).HasColumnName("gameid"); - - entity.Property(e => e.Roomname).HasColumnName("roomname"); - - entity.HasOne(d => d.Game) - .WithMany(p => p.Grouplists) - .HasForeignKey(d => d.Gameid) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("grouplist_fk"); - }); - - modelBuilder.Entity(entity => - { - entity.ToTable("messages", "unispy"); - - entity.HasComment("Friend messages."); - - entity.Property(e => e.Messageid) - .HasColumnName("messageid") - .HasDefaultValueSql("nextval('messages_messageid_seq'::regclass)"); - - entity.Property(e => e.Date) - .HasColumnType("timestamp without time zone") - .HasColumnName("date") - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - - entity.Property(e => e.From).HasColumnName("from"); - - entity.Property(e => e.Message1) - .HasColumnType("character varying") - .HasColumnName("message"); - - entity.Property(e => e.Namespaceid).HasColumnName("namespaceid"); - - entity.Property(e => e.To).HasColumnName("to"); - - entity.Property(e => e.Type).HasColumnName("type"); - }); - - modelBuilder.Entity(entity => - { - entity.ToTable("partner", "unispy"); - - entity.HasComment("Partner information, these information are used for authentication and login."); - - entity.Property(e => e.Partnerid) - .ValueGeneratedNever() - .HasColumnName("partnerid"); - - entity.Property(e => e.Partnername) - .HasColumnType("character varying") - .HasColumnName("partnername"); - }); - - modelBuilder.Entity(entity => - { - entity.ToTable("profiles", "unispy"); - - entity.HasComment("User profiles."); - - entity.Property(e => e.Profileid) - .HasColumnName("profileid") - .HasDefaultValueSql("nextval('profiles_profileid_seq'::regclass)"); - - entity.Property(e => e.Adminrights) - .HasColumnName("adminrights") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Aim) - .HasColumnType("character varying") - .HasColumnName("aim") - .HasDefaultValueSql("''::character varying"); - - entity.Property(e => e.Birthday) - .HasColumnName("birthday") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Birthmonth) - .HasColumnName("birthmonth") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Birthyear) - .HasColumnName("birthyear") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Childcount) - .HasColumnName("childcount") - .HasDefaultValueSql("0"); - - entity.Property(e => e.City).HasColumnName("city"); - - entity.Property(e => e.Connectiontype) - .HasColumnName("connectiontype") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Countrycode) - .HasColumnType("character varying") - .HasColumnName("countrycode") - .HasDefaultValueSql("1"); - - entity.Property(e => e.Cpubrandid) - .HasColumnName("cpubrandid") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Cpuspeed) - .HasColumnName("cpuspeed") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Firstname) - .HasColumnType("character varying") - .HasColumnName("firstname"); - - entity.Property(e => e.Homepage) - .HasColumnType("character varying") - .HasColumnName("homepage") - .HasDefaultValueSql("'unispy.org'::character varying"); - - entity.Property(e => e.Icquin) - .HasColumnName("icquin") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Incomeid) - .HasColumnName("incomeid") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Industryid) - .HasColumnName("industryid") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Interests1) - .HasColumnName("interests1") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Lastname) - .HasColumnType("character varying") - .HasColumnName("lastname"); - - entity.Property(e => e.Latitude) - .HasColumnName("latitude") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Location) - .HasColumnType("character varying") - .HasColumnName("location"); - - entity.Property(e => e.Longitude) - .HasColumnName("longitude") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Marriedid) - .HasColumnName("marriedid") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Memory) - .HasColumnName("memory") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Nick) - .HasColumnType("character varying") - .HasColumnName("nick"); - - entity.Property(e => e.Occupationid) - .HasColumnName("occupationid") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Ownership1) - .HasColumnName("ownership1") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Picture) - .HasColumnName("picture") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Publicmask) - .HasColumnName("publicmask") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Quietflags).HasColumnName("quietflags"); - - entity.Property(e => e.Serverflag).HasColumnName("serverflag"); - - entity.Property(e => e.Sex) - .HasColumnName("sex") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Statstring) - .HasColumnType("character varying") - .HasColumnName("statstring") - .HasDefaultValueSql("'I love UniSpy'::character varying"); - - entity.Property(e => e.Status) - .HasColumnName("status") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Streeaddr).HasColumnName("streeaddr"); - - entity.Property(e => e.Streetaddr).HasColumnName("streetaddr"); - - entity.Property(e => e.Subscription) - .HasColumnName("subscription") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Userid).HasColumnName("userid"); - - entity.Property(e => e.Videocard1ram) - .HasColumnName("videocard1ram") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Videocard1string).HasColumnName("videocard1string"); - - entity.Property(e => e.Videocard2ram) - .HasColumnName("videocard2ram") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Videocard2string).HasColumnName("videocard2string"); - - entity.Property(e => e.Zipcode) - .HasColumnType("character varying") - .HasColumnName("zipcode") - .HasDefaultValueSql("'00000'::character varying"); - - entity.HasOne(d => d.User) - .WithMany(p => p.Profiles) - .HasForeignKey(d => d.Userid) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("profiles_fk"); - }); - - modelBuilder.Entity(entity => - { - entity.ToTable("pstorage", "unispy"); - - entity.HasComment("Old games use pstorage to store game data."); - - entity.Property(e => e.Pstorageid) - .HasColumnName("pstorageid") - .HasDefaultValueSql("nextval('pstorage_pstorageid_seq'::regclass)"); - - entity.Property(e => e.Data) - .HasColumnType("jsonb") - .HasColumnName("data"); - - entity.Property(e => e.Dindex).HasColumnName("dindex"); - - entity.Property(e => e.Profileid).HasColumnName("profileid"); - - entity.Property(e => e.Ptype).HasColumnName("ptype"); - - entity.HasOne(d => d.Profile) - .WithMany(p => p.Pstorages) - .HasForeignKey(d => d.Profileid) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("pstorage_fk"); - }); - - modelBuilder.Entity(entity => - { - entity.ToTable("sakestorage", "unispy"); - - entity.HasComment("Sake storage system."); - - entity.Property(e => e.Sakestorageid) - .HasColumnName("sakestorageid") - .HasDefaultValueSql("nextval('sakestorage_sakestorageid_seq'::regclass)"); - - entity.Property(e => e.Profileid).HasColumnName("profileid"); - - entity.Property(e => e.Tableid) - .HasColumnType("character varying") - .HasColumnName("tableid"); - - entity.Property(e => e.Userdata) - .HasColumnType("jsonb") - .HasColumnName("userdata"); - - entity.HasOne(d => d.Profile) - .WithMany(p => p.Sakestorages) - .HasForeignKey(d => d.Profileid) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("profileid_fk"); - }); - - modelBuilder.Entity(entity => - { - entity.ToTable("subprofiles", "unispy"); - - entity.HasComment("User subprofiles."); - - entity.Property(e => e.Subprofileid) - .HasColumnName("subprofileid") - .HasDefaultValueSql("nextval('subprofiles_subprofileid_seq'::regclass)"); - - entity.Property(e => e.Authtoken) - .HasColumnType("character varying") - .HasColumnName("authtoken"); - - entity.Property(e => e.Cdkeyenc) - .HasColumnType("character varying") - .HasColumnName("cdkeyenc"); - - entity.Property(e => e.Firewall) - .HasColumnName("firewall") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Gamename).HasColumnName("gamename"); - - entity.Property(e => e.Namespaceid).HasColumnName("namespaceid"); - - entity.Property(e => e.Partnerid).HasColumnName("partnerid"); - - entity.Property(e => e.Port) - .HasColumnName("port") - .HasDefaultValueSql("0"); - - entity.Property(e => e.Productid).HasColumnName("productid"); - - entity.Property(e => e.Profileid).HasColumnName("profileid"); - - entity.Property(e => e.Uniquenick) - .HasColumnType("character varying") - .HasColumnName("uniquenick"); - - entity.HasOne(d => d.Profile) - .WithMany(p => p.Subprofiles) - .HasForeignKey(d => d.Profileid) - .OnDelete(DeleteBehavior.ClientSetNull) - .HasConstraintName("subprofiles_fk"); - }); - - modelBuilder.Entity(entity => - { - entity.ToTable("users", "unispy"); - - entity.HasComment("User account information."); - - entity.Property(e => e.Userid) - .HasColumnName("userid") - .HasDefaultValueSql("nextval('users_userid_seq'::regclass)"); - - entity.Property(e => e.Banned).HasColumnName("banned"); - - entity.Property(e => e.Createddate) - .HasColumnType("timestamp without time zone") - .HasColumnName("createddate") - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - - entity.Property(e => e.Deleted).HasColumnName("deleted"); - - entity.Property(e => e.Email) - .HasColumnType("character varying") - .HasColumnName("email"); - - entity.Property(e => e.Emailverified) - .IsRequired() - .HasColumnName("emailverified") - .HasDefaultValueSql("true"); - - entity.Property(e => e.Lastip).HasColumnName("lastip"); - - entity.Property(e => e.Lastonline) - .HasColumnType("timestamp without time zone") - .HasColumnName("lastonline") - .HasDefaultValueSql("CURRENT_TIMESTAMP"); - - entity.Property(e => e.Password) - .HasColumnType("character varying") - .HasColumnName("password"); - }); - - OnModelCreatingPartial(modelBuilder); - } - - partial void OnModelCreatingPartial(ModelBuilder modelBuilder); - } -} diff --git a/src/Libraries/Core/src/Database/DatabaseModel/User.cs b/src/Libraries/Core/src/Database/DatabaseModel/User.cs deleted file mode 100644 index ef47b81ef..000000000 --- a/src/Libraries/Core/src/Database/DatabaseModel/User.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; - -namespace UniSpy.Server.Core.Database.DatabaseModel -{ - /// - /// User account information. - /// - public partial class User - { - public User() - { - Profiles = new HashSet(); - } - - public int Userid { get; set; } - public string Email { get; set; } = null!; - public string Password { get; set; } = null!; - public bool? Emailverified { get; set; } - public IPAddress Lastip { get; set; } - public DateTime Lastonline { get; set; } - public DateTime Createddate { get; set; } - public bool Banned { get; set; } - public bool Deleted { get; set; } - - public virtual ICollection Profiles { get; set; } - } -} diff --git a/src/Libraries/Core/src/Database/DatabaseType.cs b/src/Libraries/Core/src/Database/DatabaseType.cs deleted file mode 100755 index e2ef1f780..000000000 --- a/src/Libraries/Core/src/Database/DatabaseType.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace UniSpy.Server.Core.Database -{ - public enum DatabaseType - { - PostgreSql - } -} diff --git a/src/Libraries/Core/src/Encryption/Crc16.cs b/src/Libraries/Core/src/Encryption/Crc16.cs deleted file mode 100755 index 7157b30cb..000000000 --- a/src/Libraries/Core/src/Encryption/Crc16.cs +++ /dev/null @@ -1,90 +0,0 @@ -namespace UniSpy.Server.Core.Encryption -{ - public enum Crc16Mode : ushort - { - Standard = 0xA001, - CCITT = 4129, - CCITTKermit = 0x8408 - } - - /// - /// Credits go to http://www.sanity-free.com/134/standard_crc_16_in_csharp.html - /// - public class Crc16 - { - /// - /// The Crc16 Table - /// - public ushort[] CrcTable { get; protected set; } - - public Crc16() - { - // Build Standard Crc16 Table - BuildCrcTable(0xA001); - } - - public Crc16(ushort polynomial) - { - BuildCrcTable(polynomial); - } - - public Crc16(Crc16Mode Mode) - { - BuildCrcTable((ushort)Mode); - } - - /// - /// Calculates the Checksum for the input string - /// - /// - /// - public ushort ComputeChecksum(string Input) - { - return ComputeChecksum(UniSpyEncoding.GetBytes(Input)); - } - - /// - /// Calculates the Checksum for the given bytes - /// - /// - /// - public ushort ComputeChecksum(byte[] bytes) - { - ushort crc = 0; - for (int i = 0; i < bytes.Length; ++i) - { - crc = (ushort)(CrcTable[(bytes[i] ^ crc) & 0xFF] ^ (crc >> 8)); - } - return crc; - } - - /// - /// Builds the Crc table programmatically with the given polynomial - /// - /// - private void BuildCrcTable(ushort polynomial) - { - ushort value; - ushort temp; - - // Build standard Crc16 Table - CrcTable = new ushort[256]; - for (ushort i = 0; i < 256; ++i) - { - value = 0; - temp = i; - for (byte j = 0; j < 8; ++j) - { - if (((value ^ temp) & 0x0001) != 0) - value = (ushort)((value >> 1) ^ polynomial); - else - value >>= 1; - - temp >>= 1; - } - - CrcTable[i] = value; - } - } - } -} diff --git a/src/Libraries/Core/src/Encryption/GSEncryption.cs b/src/Libraries/Core/src/Encryption/GSEncryption.cs deleted file mode 100644 index 9c83b08a5..000000000 --- a/src/Libraries/Core/src/Encryption/GSEncryption.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Core.Encryption -{ - public sealed class GameSpyCTX - { - public byte Buffer1; - public byte Buffer2; - public byte[] SBox = new byte[256]; - } - /// - /// This class is used to encrypt and decrypt the data for Chat. - /// - /// Note: this is a C-sharp version of the peerchat algorithm created by aluigi. - /// Original C implementation: http://aluigi.altervista.org/papers/gs_peerchat.h - /// - public sealed class GSEncryption : ICryptography - { - public const string DigitsHex = "0123456789abcdef"; - public const string DigitsCrypt = "aFl4uOD9sfWq1vGp"; - public const string NewDigitsCrypt = "qJ1h4N9cP3lzD0Ka"; - public const uint IPXorMask = 0xc3801dc7; - public const string ClientKey = "0000000000000000"; - public const string ServerKey = "0000000000000000"; - public static byte[] Handle(GameSpyCTX ctx, byte[] data) - { - byte num1 = ctx.Buffer1; - byte num2 = ctx.Buffer2; - byte t; - int datapos = 0; - List buffer = new List(); - long size = data.Length; - - while (size-- > 0) - { - num1 = (byte)((num1 + 1) % 256); - num2 = (byte)((ctx.SBox[num1] + num2) % 256); - t = ctx.SBox[num1]; - ctx.SBox[num1] = ctx.SBox[num2]; - ctx.SBox[num2] = t; - t = (byte)((ctx.SBox[num2] + ctx.SBox[num1]) % 256); - byte temp = (byte)(data[datapos++] ^ ctx.SBox[t]); - buffer.Add(temp); - } - - ctx.Buffer1 = num1; - ctx.Buffer2 = num2; - return buffer.ToArray(); - } - - /// - /// Prepare the key - /// - /// - /// - /// - public static void Init(GameSpyCTX ctx, string challengeKey, string secretKey) - { - byte[] challengeBytes = UniSpyEncoding.GetBytes(challengeKey); - byte[] secretKeyBytes = UniSpyEncoding.GetBytes(secretKey); - - ctx.Buffer1 = 0; - ctx.Buffer2 = 0; - - int secretKeyIndex = 0; - for (int i = 0; i < challengeBytes.Length; i++, secretKeyIndex++) - { - if (secretKeyIndex >= secretKeyBytes.Length) - { - secretKeyIndex = 0; - } - - challengeBytes[i] ^= secretKeyBytes[secretKeyIndex]; - } - - byte index1 = 255; - - for (int i = 0; i < 256; i++, index1--) - { - ctx.SBox[i] = index1; - } - - index1 = 0; - - for (int i = 0, index2 = 0; i < ctx.SBox.Length; i++, index1++) - { - if (index1 >= challengeBytes.Length) - { - index1 = 0; - } - - index2 = (byte)((challengeBytes[index1] + ctx.SBox[i] + index2) % 256); - byte t = ctx.SBox[i]; - ctx.SBox[i] = ctx.SBox[index2]; - ctx.SBox[index2] = t; - } - } - - public static bool EncodeIP() - { - return false; - } - public GameSpyCTX ClientCtx { get; private set; } - public GameSpyCTX ServerCtx { get; private set; } - public GSEncryption(string gameSecretKey) - { - ClientCtx = new GameSpyCTX(); - ServerCtx = new GameSpyCTX(); - Init(ClientCtx, ClientKey, gameSecretKey); - Init(ServerCtx, ServerKey, gameSecretKey); - } - public byte[] Encrypt(byte[] data) - { - return Handle(ServerCtx, data); - } - - public byte[] Decrypt(byte[] data) - { - return Handle(ClientCtx, data); - } - } -} diff --git a/src/Libraries/Core/src/Encryption/UniSpyEncoding.cs b/src/Libraries/Core/src/Encryption/UniSpyEncoding.cs deleted file mode 100755 index b7d305227..000000000 --- a/src/Libraries/Core/src/Encryption/UniSpyEncoding.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; -using System.Text; - -namespace UniSpy.Server.Core.Encryption -{ - public static class UniSpyEncoding - { - public static Func GetString => Encoding.ASCII.GetString; - public static Func GetBytes => Encoding.ASCII.GetBytes; - - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Encryption/XorEncoding.cs b/src/Libraries/Core/src/Encryption/XorEncoding.cs deleted file mode 100755 index 92362dac1..000000000 --- a/src/Libraries/Core/src/Encryption/XorEncoding.cs +++ /dev/null @@ -1,82 +0,0 @@ -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Core.Encryption -{ - public enum XorType : int - { - Type0, - Type1, - Type2, - Type3 - } - public class XOREncoding : ICryptography - { - public XorType EncryptionType { get; private set; } - - public XOREncoding(XorType type) - { - EncryptionType = type; - } - - public static string Encode(string plainText, XorType type) => UniSpyEncoding.GetString(Encode(UniSpyEncoding.GetBytes(plainText), type)); - - /// - /// simple xor encoding for Gstats,GPSP,GPCM - /// - /// - /// default encryption string used in GPSP,GPCM - /// used in GStats - /// used in GStats - /// - /// - public static byte[] Encode(byte[] plaintext, XorType type) - { - string seed0 = "gamespy"; - string seed1 = "GameSpy3D"; - string seed2 = "Industries"; - string seed3 = "ProjectAphex"; - - int length = plaintext.Length; - int index = 0; - byte[] temp; - switch (type) - { - case XorType.Type0: - temp = UniSpyEncoding.GetBytes(seed0); - break; - case XorType.Type1: - temp = UniSpyEncoding.GetBytes(seed1); - break; - case XorType.Type2: - temp = UniSpyEncoding.GetBytes(seed2); - break; - case XorType.Type3: - temp = UniSpyEncoding.GetBytes(seed3); - break; - default: - temp = UniSpyEncoding.GetBytes(seed0); - break; - } - - for (int i = 0; length > 0; length--) - { - if (i >= temp.Length) - i = 0; - - plaintext[index++] ^= temp[i++]; - } - - return plaintext; - } - - public byte[] Encrypt(byte[] data) - { - return Encode(data, EncryptionType); - } - - public byte[] Decrypt(byte[] data) - { - return Encode(data, EncryptionType); - } - } -} diff --git a/src/Libraries/Core/src/Exception/Exception.cs b/src/Libraries/Core/src/Exception/Exception.cs deleted file mode 100755 index d72994870..000000000 --- a/src/Libraries/Core/src/Exception/Exception.cs +++ /dev/null @@ -1,47 +0,0 @@ -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Logging; - -namespace UniSpy -{ - public class Exception : System.Exception - { - public Exception() - { - } - - public Exception(string message) : base(message) - { - } - - public Exception(string message, System.Exception innerException) : base(message, innerException) - { - } - public static void HandleException(System.Exception ex, IClient client = null) - { - // we only log exception message when this message is UniSpy.Exception - if (ex is UniSpy.Exception) - { - if (client is null) - { - LogWriter.LogError(ex.Message); - } - else - { - client.LogError(ex.Message); - } - } - else - { - if (client is null) - { - LogWriter.LogError(ex.ToString()); - } - else - { - client.LogError(ex.ToString()); - } - } - } - } - -} diff --git a/src/Libraries/Core/src/Extension/DataOperationExtensions.cs b/src/Libraries/Core/src/Extension/DataOperationExtensions.cs deleted file mode 100755 index 2ff30c6bc..000000000 --- a/src/Libraries/Core/src/Extension/DataOperationExtensions.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Linq; -using UniSpy.Server.Core.Database.DatabaseModel; -namespace UniSpy.Server.Core.Extension -{ - public class DataOperationExtensions - { - public static string GetSecretKey(string gameName) - { - using (var db = new UniSpyContext()) - { - var result = from p in db.Games - where p.Gamename == gameName - select new { p.Secretkey }; - - return result.First().Secretkey; - } - } - } -} diff --git a/src/Libraries/Core/src/Extension/EasyTimer.cs b/src/Libraries/Core/src/Extension/EasyTimer.cs deleted file mode 100644 index 79d193264..000000000 --- a/src/Libraries/Core/src/Extension/EasyTimer.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Timers; - -namespace UniSpy.Server.Core.Extension -{ - public class EasyTimer - { - public TimeSpan? ExpireTime { get; private set; } - public DateTime LastActiveTime { get; private set; } - public TimeSpan IdleTime => DateTime.Now - LastActiveTime; - /// - /// The timer to count and invoke some event - /// - private Timer _timer; - public bool IsExpired - { - get - { - if (ExpireTime is null) - { - return false; - } - else - { - return IdleTime > ExpireTime; - } - } - } - public event ElapsedEventHandler Elapsed - { - add { _timer.Elapsed += value; } - remove { _timer.Elapsed -= value; } - } - /// - /// Easy timer constructor, remember to call Start() - /// - public EasyTimer(TimeSpan? expireTimeSpan, TimeSpan intervalTimeSpan) - { - _timer = new Timer - { - Enabled = true, - Interval = intervalTimeSpan.TotalMilliseconds, - AutoReset = true - }; - ExpireTime = expireTimeSpan; - } - public EasyTimer(TimeSpan intervalTimeSpan) : this(null, intervalTimeSpan) { } - /// - /// Start the timer - /// - public void Start() - { - RefreshLastActiveTime(); - _timer.Start(); - } - public void RefreshLastActiveTime() => LastActiveTime = DateTime.Now; - public void Dispose() => _timer.Dispose(); - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Extension/Redis/RedisChannelName.cs b/src/Libraries/Core/src/Extension/Redis/RedisChannelName.cs deleted file mode 100755 index 9b3354329..000000000 --- a/src/Libraries/Core/src/Extension/Redis/RedisChannelName.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace UniSpy.Server.Core.Extension.Redis -{ - public class RedisChannelName - { - public const string NatNegCookieChannel = "QueryReport:NatNegCookieChannel"; - public const string HeartbeatChannel = "QueryReport:HeartbeatChannel"; - public const string PresenceConnectionManagerBuddyMessageChannel = "PresenceConnectionManager:BuddyMessageChannel"; - public const string ChatChannelPrefix = "Chat:Channel"; - public const string PresenceConnectionManagerUserInfoChannel = "PresenceConnectionManager:UserInfoChannel"; - } -} diff --git a/src/Libraries/Core/src/Extension/Redis/RedisDbNumber.cs b/src/Libraries/Core/src/Extension/Redis/RedisDbNumber.cs deleted file mode 100755 index b9626fa75..000000000 --- a/src/Libraries/Core/src/Extension/Redis/RedisDbNumber.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace UniSpy.Server.Core.Extension.Redis -{ - public enum RedisDbNumber : int - { - ChatChannel, - ChatUser, - GameServerV1, - GameServerV2, - NatAddressInfo, - NatFailInfo, - GameTrafficRelay - } -} diff --git a/src/Libraries/Core/src/Extension/StringExtensions.cs b/src/Libraries/Core/src/Extension/StringExtensions.cs deleted file mode 100755 index d3210365e..000000000 --- a/src/Libraries/Core/src/Extension/StringExtensions.cs +++ /dev/null @@ -1,221 +0,0 @@ -using System.Numerics; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Security.Cryptography; -using System.Text; -using UniSpy.Server.Core.Encryption; - -namespace UniSpy.Server.Core.Extension -{ - public static class StringExtensions - { - /// - /// Returns all the index's of the given string input - /// - /// - /// - public static List IndexesOf(this string str, string value) - { - if (string.IsNullOrEmpty(value)) - throw new ArgumentException("the string to find may not be empty", "value"); - List indexes = new List(); - for (int index = 0; ; index += value.Length) - { - index = str.IndexOf(value, index); - if (index == -1) - return indexes; - indexes.Add(index); - } - } - - /// - /// Removes any invalid file path characters from this string - /// - public static string MakeFileNameSafe(this string fileName) - { - return Path.GetInvalidFileNameChars().Aggregate(fileName, (current, c) => current.Replace(c.ToString(), string.Empty)); - } - - /// - /// Converts the input string into its MD5 Hex variant - /// - /// Uppercase the characters? - /// The encoding of the string. Default is UTF8 - /// - public static string GetMD5Hash(this string input, bool upperCase = false, Encoding Encoding = null) - { - using (MD5 Md5 = MD5.Create()) - { - return Md5.ComputeHash(Encoding.ASCII.GetBytes(input)).ToHex(upperCase); - } - } - /// - /// Converts the byte array to its Hex string equivlent - /// - /// Do we uppercase the hex string? - /// - public static string ToHex(this byte[] bytes, bool upperCase = true) - { - StringBuilder result = new StringBuilder(bytes.Length * 2); - for (int i = 0; i < bytes.Length; i++) - result.Append(bytes[i].ToString(upperCase ? "X2" : "x2")); - - return result.ToString(); - } - #region Undisplayable char convert - /// - /// Convert byte array to pure hex string dispite if byte is printable or non-pritable - /// - /// - /// - public static string ConvertByteToHexString(this byte[] buffer) - { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < buffer.Length; i++) - { - if (i == buffer.Length - 1) - { - sb.Append("0x" + buffer[i].ToString("X2")); - } - else - { - sb.Append("0x" + buffer[i].ToString("X2") + ","); - } - } - return sb.ToString(); - } - /// - /// Convert nonpritable to hex string, combine with printable char to create a string: - /// [0x00] [0x01] hello [0x02] - /// - /// Raw byte array - /// - public static string ConvertNonprintableBytesToHexString(this byte[] buffer) - { - StringBuilder temp = new StringBuilder(); - for (int i = 0; i < buffer.Length; i++) - { - if (buffer[i] < 0x1F || buffer[i] > 0x7E) - { - temp.Append($"[{buffer[i]:X2}]"); - } - else - { - temp.Append((char)buffer[i]); - } - } - return temp.ToString(); - } - - /// - /// Only convert printable bytes to string - /// - /// raw byte array - /// - public static string ConvertPrintableBytesToString(this byte[] buffer) - { - char delimiter = ' '; - StringBuilder temp = new StringBuilder(); - for (int i = 0; i < buffer.Length; i++) - { - if (buffer[i] < 0x1F || buffer[i] > 0x7E) - { - // if the last char in temp is not delimiter, we add delemiter - if (temp.Length != 0 && temp[temp.Length - 1] != delimiter) - { - temp.Append(delimiter); - } - } - else - { - temp.Append((char)buffer[i]); - } - } - return temp.ToString(); - } - public static string ConvertNonprintableCharToHex(this string buffer) - { - return ConvertNonprintableBytesToHexString(UniSpyEncoding.GetBytes(buffer)); - } - #endregion - - public static Dictionary ConvertKVStringToDictionary(this string kvStr) - { - Dictionary dic = new Dictionary(); - List keyValueList = - kvStr.Split("\\").ToList(); - - for (int i = 0; i < keyValueList.Count; i = i + 2) - { - if (keyValueList.Count < i + 2) // Count starts from 1, i from 0 - { - dic.TryAdd(keyValueList[i], ""); - } - else - { - dic.TryAdd(keyValueList[i], keyValueList[i + 1]); - } - } - - return dic; - } - public static string ConvertDictionaryToKVString(this Dictionary kv) - { - string buffer = @"\"; - foreach (var data in kv) - { - buffer += data.Key + @"\" + data.Value + @"\"; - } - - buffer = buffer.Substring(0, buffer.Length - 1); - - return buffer; - } - - public static List ConvertKeyStrToList(this string keyStr) - { - List data = keyStr.Split(@"\", StringSplitOptions.RemoveEmptyEntries).ToList(); - - return data; - } - - /// - /// Check the validation of response string - /// - /// return true if the string is valid - public static bool CheckResponseValidation(this string buffer) - { - return buffer?.Length > 3; - } - - public static bool CheckResponseValidation(this byte[] buffer) - { - if (buffer is null) - { - return false; - } - - if (buffer.Length < 3) - { - return false; - } - return true; - } - /// - /// Get byte array from a hexstring - /// the hexstring is like "01020304" - /// two char as a byte - /// - /// - public static byte[] FromHexStringToBytes(this string hex) - { - var data = Enumerable.Range(0, hex.Length) - .Where(x => x % 2 == 0) - .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) - .ToArray(); - return data; - } - } -} diff --git a/src/Libraries/Core/src/Logging/LogWriter.cs b/src/Libraries/Core/src/Logging/LogWriter.cs deleted file mode 100755 index eb32baef3..000000000 --- a/src/Libraries/Core/src/Logging/LogWriter.cs +++ /dev/null @@ -1,97 +0,0 @@ -using Serilog; -using Serilog.Events; -using UniSpy.Server.Core.Abstraction.BaseClass.Factory; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Config; -using UniSpy.Server.Core.Extension; - -namespace UniSpy.Server.Core.Logging -{ - /// - /// Provides an object wrapper for a file that is used to - /// store LogMessage's into. Uses a Multi-Thread safe Queueing - /// system, and provides full Asynchronous writing and flushing - /// - public static class LogWriter - { - public static readonly LoggerConfiguration LogConfig; - static LogWriter() - { - LogConfig = new LoggerConfiguration(); - if (ConfigManager.IsConfigFileExist) - { - switch (ConfigManager.Config.MinimumLogLevel) - { - case LogEventLevel.Verbose: - LogConfig.MinimumLevel.Verbose(); - break; - case LogEventLevel.Information: - LogConfig.MinimumLevel.Information(); - break; - case LogEventLevel.Debug: - LogConfig.MinimumLevel.Debug(); - break; - case LogEventLevel.Warning: - LogConfig.MinimumLevel.Warning(); - break; - case LogEventLevel.Error: - LogConfig.MinimumLevel.Error(); - break; - case LogEventLevel.Fatal: - LogConfig.MinimumLevel.Fatal(); - break; - } - } - - var path = ServerLauncherBase.ServerInstances.Count == 0 ? "" : ServerLauncherBase.ServerInstances[0].Name; - LogConfig = LogConfig - .WriteTo.Console(outputTemplate: "{Timestamp:[HH:mm:ss]} [{Level:u4}] {Message:}{NewLine}{Exception}") - .WriteTo.File( - path: $"Logs/[{path}]-.log", - outputTemplate: "{Timestamp:[yyyy-MM-dd HH:mm:ss]} [{Level:u4}] {Message:}{NewLine}{Exception}", - rollingInterval: RollingInterval.Day); - Log.Logger = LogConfig.CreateLogger(); - } - public static void LogVerbose(string message) => Log.Verbose(message); - public static void LogDebug(string message) => Log.Debug(message); - public static void LogInfo(string message) => Log.Information(message); - public static void LogWarn(string message) => Log.Warning(message); - public static void LogError(string message) => Log.Error(message); - public static void LogError(System.Exception e) => LogError(e.ToString()); - public static void LogFatal(string message) => Log.Fatal(message); - public static void LogCurrentClass(object param) => LogVerbose($"[ => ] [{param.GetType().Name}]"); - public static void LogCurrentClass(this IClient client, object param) => LogVerbose(FormatLogMessage(client, $"[ => ] [{param.GetType().Name}]")); - - public static string FormatLogMessage(this IClient client, string message) => $"[{client.Connection.RemoteIPEndPoint}] {message}"; - public static string FormatNetworkLogMessage(string type, string message) => $"[{type}] {message}"; - public static string FormatNetworkLogMessage(string type, byte[] message, bool isLogRaw) - { - if (isLogRaw) - { - // we first show printable bytes, then show all bytes - var tempLog = $"{StringExtensions.ConvertPrintableBytesToString(message)} [{StringExtensions.ConvertByteToHexString(message)}]"; - return $"[{type}] {tempLog}"; - } - else - { - var tempLog = StringExtensions.ConvertNonprintableBytesToHexString(message); - return $"[{type}] {tempLog}"; - } - } - - public static void LogVerbose(this IClient client, string message) => Log.Verbose(FormatLogMessage(client, message)); - public static void LogInfo(this IClient client, string message) => Log.Information(FormatLogMessage(client, message)); - public static void LogDebug(this IClient client, string message) => Log.Debug(FormatLogMessage(client, message)); - public static void LogWarn(this IClient client, string message) => Log.Warning(FormatLogMessage(client, message)); - public static void LogError(this IClient client, string message) => Log.Error(FormatLogMessage(client, message)); - public static void LogError(this IClient client, Exception e) => Log.Error(FormatLogMessage(client, e.Message)); - public static void LogFatal(this IClient client, string message) => Log.Fatal(FormatLogMessage(client, message)); - - public static void LogNetworkReceiving(this IClient client, string message) => LogDebug(client, FormatNetworkLogMessage("Recv", message)); - public static void LogNetworkSending(this IClient client, string message) => LogDebug(client, FormatNetworkLogMessage("Send", message)); - public static void LogNetworkReceiving(this IClient client, byte[] message) => LogDebug(client, FormatNetworkLogMessage("Recv", message, client.IsLogRaw)); - public static void LogNetworkSending(this IClient client, byte[] message) => LogDebug(client, FormatNetworkLogMessage("Send", message, client.IsLogRaw)); - public static void LogNetworkMultiCast(this IClient client, string message) => LogDebug(client, FormatNetworkLogMessage("Cast", message)); - public static void LogNetworkMultiCast(this IClient client, byte[] message) => LogDebug(client, FormatNetworkLogMessage("Cast", message, client.IsLogRaw)); - } -} diff --git a/src/Libraries/Core/src/Misc/GameSpyRandom.cs b/src/Libraries/Core/src/Misc/GameSpyRandom.cs deleted file mode 100755 index d397f5c2c..000000000 --- a/src/Libraries/Core/src/Misc/GameSpyRandom.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.Text; - -namespace UniSpy.Server.Core.Misc -{ - public class GameSpyRandom - { - public enum StringType - { - Alpha, - AlphaNumeric, - Hex - } - /// - /// Array of characters used in generating a signiture - /// - private static char[] AlphaChars = { - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' - }; - - /// - /// An array of Alpha Numeric characters used in generating a random string - /// - private static char[] AlphaNumChars = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', - 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', - 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', - 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' - }; - - /// - /// Array of Hex cahracters - /// - private static char[] HexChars = { - '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', - 'a', 'b', 'c', 'd', 'e', 'f' - }; - - public static string GenerateRandomString(int count, StringType type) - { - Random random = new Random((int)DateTime.Now.Ticks); - - StringBuilder builder = new StringBuilder(count); - - for (int i = 0; i < count; i++) - { - switch (type) - { - case StringType.AlphaNumeric: - builder.Append(AlphaNumChars[random.Next(AlphaNumChars.Length)]); - break; - default: - builder.Append(AlphaChars[random.Next(AlphaChars.Length)]); - break; - case StringType.Hex: - builder.Append(HexChars[random.Next(HexChars.Length)]); - break; - } - } - - return builder.ToString(); - } - } -} diff --git a/src/Libraries/Core/src/Misc/GameSpyUtils.cs b/src/Libraries/Core/src/Misc/GameSpyUtils.cs deleted file mode 100755 index b5a48e188..000000000 --- a/src/Libraries/Core/src/Misc/GameSpyUtils.cs +++ /dev/null @@ -1,267 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Linq; -using System.Net; -using System.Text.RegularExpressions; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.Core.Misc -{ - public static class GameSpyUtils - { - /// - /// Get the string type request command name from rawrequest - /// - public static string GetRequestName(string request) - { - var frags = request.Split('\\', StringSplitOptions.RemoveEmptyEntries); - if (frags.Length < 4) - { - throw new UniSpy.Exception("Request is not valid."); - } - return frags[0]; - } - /// - /// Split command to key value array then convert it to dictionary - /// - /// a string like request - /// - public static Dictionary ConvertToKeyValue(string request) - { - string[] commandParts = request.Replace(@"\final\", "").TrimStart('\\').Split('\\'); - return ConvertToKeyValue(commandParts); - } - - /// - /// Converts a trimmed presence message from the client string to a keyValue pair dictionary - /// - /// The array of data from the client - /// A converted dictionary - public static Dictionary ConvertToKeyValue(string[] parts) - { - // we remove final item - parts = parts.Where(p => p != "final").ToArray(); - Dictionary dict = new Dictionary(); - try - { - for (int i = 0; i < parts.Length; i += 2) - { - if (!dict.ContainsKey(parts[i])) - { - dict.Add(parts[i].ToLower(), parts[i + 1]); - } - //Some game send uppercase key to us, so we have to deal with it - } - } - catch (IndexOutOfRangeException) { } - - return dict; - } - - - public static void PrintReceivedGPDictToLogger(Dictionary recv) - { - LogWriter.LogDebug( - $"Received request {recv.Keys.First()} with content: {string.Join(";", recv.Select(x => x.Key + "=" + x.Value).ToArray())}"); - } - - /// - /// Check the correctness of the email account format. - /// - /// email account - /// - public static bool IsEmailFormatCorrect(string email) - { - - if (string.IsNullOrWhiteSpace(email)) - return false; - - try - { - // Normalize the domain - email = Regex.Replace(email, @"(@)(.+)$", DomainMapper, - RegexOptions.None, TimeSpan.FromMilliseconds(200)); - - // Examines the domain part of the email and normalizes it. - string DomainMapper(Match match) - { - // Use IdnMapping class to convert Unicode domain names. - var idn = new IdnMapping(); - - // Pull out and process domain name (throws ArgumentException on invalid) - var domainName = idn.GetAscii(match.Groups[2].Value); - - return match.Groups[1].Value + domainName; - } - } - catch - { - return false; - } - - try - { - return Regex.IsMatch(email, - @"^(?("")("".+?(? (!char.IsLetterOrDigit(ch) && ch != '@' && ch != '.')); - } - /// - /// Check is the format of nick or uniquenick are correct - /// - /// - /// - public static bool IsNickOrUniquenickFormatCorrect(string nick) - { - string pattern = @"^\w+$"; - Regex regex = new Regex(pattern); - return regex.IsMatch(nick); - } - - /// - /// Check if a date is correct - /// - /// - /// - /// - /// True if the date is valid, otherwise false - public static bool IsValidDate(int day, int month, int year) - { - // Check for a blank. - ///////////////////// - if ((day == 0) && (month == 0) && (year == 0)) - return false; - - // Validate the day of the month. - ///////////////////////////////// - switch (month) - { - // No month. - //////////// - case 0: - // Can't specify a day without a month. - /////////////////////////////////////// - if (day != 0) - return false; - break; - - // 31-day month. - //////////////// - case 1: - case 3: - case 5: - case 7: - case 8: - case 10: - case 12: - if (day > 31) - return false; - break; - - // 30-day month. - //////////////// - case 4: - case 6: - case 9: - case 11: - if (day > 30) - return false; - break; - - // 28/29-day month. - /////////////////// - case 2: - // Leap year? - ///////////// - if ((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0)) - { - if (day > 29) - return false; - } - else - { - if (day > 28) - return false; - } - break; - - // Invalid month. - ///////////////// - default: - return false; - } - - // Check that the date is in the valid range. - ///////////////////////////////////////////// - if (year < 1900) - return false; - if (year > 2079) - return false; - if (year == 2079) - { - if (month > 6) - return false; - if ((month == 6) && (day > 6)) - return false; - } - - return true; - } - public static bool IsNumber(string strNumber) - { - Regex objNotNumberPattern = new Regex("[^0-9.-]"); - Regex objTwoDotPattern = new Regex("[0-9]*[.][0-9]*[.][0-9]*"); - Regex objTwoMinusPattern = new Regex("[0-9]*[-][0-9]*[-][0-9]*"); - string strValidRealPattern = "^([-]|[.]|[-.]|[0-9])[0-9]*[.]*[0-9]+$"; - string strValidIntegerPattern = "^([-]|[0-9])[0-9]*$"; - Regex objNumberPattern = new Regex("(" + strValidRealPattern + ")|(" + strValidIntegerPattern + ")"); - - return !objNotNumberPattern.IsMatch(strNumber) && - !objTwoDotPattern.IsMatch(strNumber) && - !objTwoMinusPattern.IsMatch(strNumber) && - objNumberPattern.IsMatch(strNumber); - } - - /// - /// Compute the interval of time - /// - /// old time - /// milisecond - public static byte ComputeTimeInterval(DateTime old) - { - return (byte)DateTime.Now.Subtract(old).TotalMilliseconds; - } - - - /// - /// An extension method to determine if an IP address is internal, as specified in RFC1918 - /// - /// The IP address that will be tested - /// Returns true if the IP is internal, false if it is external - public static bool IsInternal(this IPAddress toTest) - { - if (IPAddress.IsLoopback(toTest)) return true; - else if (toTest.ToString() == "::1") return false; - - byte[] bytes = toTest.GetAddressBytes(); - switch (bytes[0]) - { - case 10: - return true; - case 172: - return bytes[1] < 32 && bytes[1] >= 16; - case 192: - return bytes[1] == 168; - default: - return false; - } - } - } -} diff --git a/src/Libraries/Core/src/Misc/PasswordEncoder.cs b/src/Libraries/Core/src/Misc/PasswordEncoder.cs deleted file mode 100755 index 15d4a0ea3..000000000 --- a/src/Libraries/Core/src/Misc/PasswordEncoder.cs +++ /dev/null @@ -1,126 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.Core.Extension; - -namespace UniSpy.Server.Core.Misc -{ - public class PasswordEncoder - { - /// - /// process password to string which stores in our database - /// - /// - public static string ProcessPassword(Dictionary dict) - { - string md5Password; - if (dict.ContainsKey("passwordenc")) - { - //we decoded gamespy encoded password then get md5 of it - md5Password = StringExtensions.GetMD5Hash(Decode(dict["passwordenc"])); - } - else if (dict.ContainsKey("passenc")) - { - //we decoded gamespy encoded password then get md5 of it - md5Password = StringExtensions.GetMD5Hash(Decode(dict["passenc"])); - } - else if (dict.ContainsKey("pass")) - { - md5Password = StringExtensions.GetMD5Hash(dict["pass"]); - } - else if (dict.ContainsKey("password")) - { - md5Password = StringExtensions.GetMD5Hash(dict["password"]); - } - else - { - throw new UniSpy.Exception("Can not find password field in request"); - } - return md5Password; - } - /// - /// Encodes a password to Gamespy format - /// - /// - /// - private static string Encode(string Password) - { - // Get password string as UTF8 String, Convert to Base64 - byte[] PasswordBytes = Encoding.UTF8.GetBytes(Password); - string Pass = Convert.ToBase64String(GameSpyEncodeMethod(PasswordBytes)); - - // Convert Standard Base64 to Gamespy Base 64 - StringBuilder builder = new StringBuilder(Pass); - builder.Replace('=', '_'); - builder.Replace('+', '['); - builder.Replace('/', ']'); - return builder.ToString(); - } - - /// - /// Decodes a Gamespy encoded password - /// - /// - /// - private static string Decode(string password) - { - // Convert Gamespy Base64 to Standard Base 64 - password = password.Replace('_', '=').Replace('[', '+').Replace(']', '/'); - // Decode passsword - byte[] passwordBytes = Convert.FromBase64String(password); - return UniSpyEncoding.GetString(GameSpyEncodeMethod(passwordBytes)); - } - - /// - /// Gamespy's XOR method to encrypt and decrypt a password - /// - /// - /// - private static byte[] GameSpyEncodeMethod(byte[] pass) - { - int a = 0; - int num = 0x79707367; // gamespy - for (int i = 0; i < pass.Length; ++i) - { - num = GameSpyByteShift(num); - a = num % 0xFF; - pass[i] ^= (byte)a; - } - - return pass; - } - - /// - /// Not exactly sure what this does, but i know its used to - /// reverse the encryption and decryption of a string - /// - /// - /// - private static int GameSpyByteShift(int num) - { - int c = (num >> 16) & 0xffff; - int a = num & 0xffff; - - c *= 0x41a7; - a *= 0x41a7; - a += ((c & 0x7fff) << 16); - - if (a < 0) - { - a &= 0x7fffffff; - a++; - } - - a += (c >> 15); - - if (a < 0) - { - a &= 0x7fffffff; - a++; - } - - return a; - } - } -} diff --git a/src/Libraries/Core/src/Misc/RequestChecker.cs b/src/Libraries/Core/src/Misc/RequestChecker.cs deleted file mode 100755 index 3e0f7ef51..000000000 --- a/src/Libraries/Core/src/Misc/RequestChecker.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace UniSpy.Server.Core.Misc -{ - public class RequestChecker - { - public static bool IsUniqueNickLegal(string uniquenick) - { - if (uniquenick.Length > 3 && uniquenick.Length < 15) - { - return true; - } - - return false; - } - public static bool IsNicknameLegal(string nick) - { - if (nick.Length > 2 && nick.Length < 15) - { - return true; - } - - return false; - } - public static bool IsEmailLegal(string email) - { - return true; - - } - public static bool IsNamespaceidLegal(ushort namespaceid) - { - return true; - } - public static bool IsPartneridLegal(ushort partnerid) - { - return true; - } - - public static bool IsPasswordLegal(string password) - { - if (password.Length > 4 && password.Length < 20) - { - return true; - } - return false; - } - } -} diff --git a/src/Libraries/Core/src/Misc/UniSpyJsonConverter.cs b/src/Libraries/Core/src/Misc/UniSpyJsonConverter.cs deleted file mode 100755 index 29311e812..000000000 --- a/src/Libraries/Core/src/Misc/UniSpyJsonConverter.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Net; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; - -namespace UniSpy.Server.Core.Misc -{ - - public class IPAddressConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return (objectType == typeof(IPAddress)); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - writer.WriteValue(value.ToString()); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - return IPAddress.Parse((string)reader.Value); - } - } - public class IPAddresConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return (objectType == typeof(IPAddress)); - } - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - IPAddress ep = (IPAddress)value; - JObject jo = new JObject(); - jo.Add("Address", JToken.FromObject(ep.ToString(), serializer)); - jo.WriteTo(writer); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - JObject jo = JObject.Load(reader); - IPAddress address = IPAddress.Parse(jo["Address"].ToObject()); - return address; - } - } - public class IPEndPointConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - return (objectType == typeof(IPEndPoint)); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - IPEndPoint ep = (IPEndPoint)value; - JObject jo = new JObject(); - jo.Add("Address", JToken.FromObject(ep.Address.ToString(), serializer)); - jo.Add("Port", ep.Port); - jo.WriteTo(writer); - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - // if (reader.Value == null) - // { - // return null; - // } - JObject jo = JObject.Load(reader); - IPAddress address = IPAddress.Parse(jo["Address"].ToObject()); - int port = (int)jo["Port"]; - return new IPEndPoint(address, port); - } - } - -} diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs b/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs deleted file mode 100644 index 95fa48284..000000000 --- a/src/Libraries/Core/src/Network/Http/Server/HttpConnection.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Net; -using System.Text; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Events; - -namespace UniSpy.Server.Core.Network.Http.Server; - -public class HttpConnection : IHttpConnection -{ - public IConnectionManager Manager { get; private set; } - public HttpListenerContext Context { get; private set; } - public IPEndPoint RemoteIPEndPoint { get; private set; } - - public NetworkConnectionType ConnectionType { get; } = NetworkConnectionType.Http; - public event OnReceivedEventHandler OnReceive; - public HttpConnection(HttpListenerContext context, IConnectionManager manager) - { - Manager = manager; - Context = context; - RemoteIPEndPoint = context.Request.RemoteEndPoint; - } - public void OnReceived(IHttpRequest request) - { - OnReceive(request); - if (Context.Request.Headers["Connection"] == "close") - { - Context.Response.Close(); - } - } - - public void Send(string response) - { - Send(Encoding.UTF8.GetBytes(response)); - } - - public void Send(byte[] response) - { - Context.Response.StatusCode = (int)HttpStatusCode.OK; - Context.Response.ContentType = "application/xml"; - Context.Response.SendChunked = false; - Context.Response.ContentLength64 = response.Length; - Context.Response.OutputStream.Write(response); - if (Context.Request.Headers["Connection"] == "close") - { - Context.Response.Close(); - } - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs b/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs deleted file mode 100644 index d98b900ba..000000000 --- a/src/Libraries/Core/src/Network/Http/Server/HttpConnectionManager.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Net; -using System.Threading.Tasks; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Events; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.Core.Network.Http.Server; - -public class HttpConnectionManager : IConnectionManager -{ - public event OnConnectingEventHandler OnInitialization; - public HttpListener Listener { get; private set; } - public HttpConnectionManager(IPEndPoint endPoint) - { - Listener = new HttpListener(); - string host; - if (endPoint.Address.ToString() == "0.0.0.0") - { - host = "+"; - } - else if (endPoint.Address.ToString() == "127.0.0.1") - { - host = "localhost"; - } - else - { - host = endPoint.Address.ToString(); - } - - Listener.Prefixes.Add($"http://{host}:{endPoint.Port}/"); - } - public void Start() - { - Listener.Start(); - Task.Run(() => - { - while (true) - { - var context = Listener.GetContext(); - Task.Run(() => - { - - var conn = new HttpConnection(context, this); - OnInitialization(conn); - var raw = context.Request; - var request = new HttpRequest(raw); - conn.OnReceived(request); - }); - } - }); - } - - public void Dispose() - { - Listener.Stop(); - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/Http/Server/HttpRequest.cs b/src/Libraries/Core/src/Network/Http/Server/HttpRequest.cs deleted file mode 100644 index 35e7707a6..000000000 --- a/src/Libraries/Core/src/Network/Http/Server/HttpRequest.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.IO; -using System.Net; -using System.Text; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Core.Network.Http.Server; -public class HttpRequest : IHttpRequest -{ - public string Body { get; private set; } - public Uri Url { get; private set; } - public string Method { get; private set; } - public HttpListenerRequest RawRequest { get; private set; } - public HttpRequest(HttpListenerRequest rawRequest) - { - RawRequest = rawRequest; - Method = RawRequest.HttpMethod; - Url = RawRequest.Url; - using (var stream = rawRequest.InputStream) - { - using (var reader = new StreamReader(stream, rawRequest.ContentEncoding)) - { - Body = reader.ReadToEnd(); - } - } - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/NetworkEvents.cs b/src/Libraries/Core/src/Network/NetworkEvents.cs deleted file mode 100644 index cee7adc8b..000000000 --- a/src/Libraries/Core/src/Network/NetworkEvents.cs +++ /dev/null @@ -1,9 +0,0 @@ -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Core.Events -{ - public delegate void OnConnectedEventHandler(); - public delegate void OnReceivedEventHandler(object buffer); - public delegate void OnDisconnectedEventHandler(); - public delegate IClient OnConnectingEventHandler(IConnection connection); -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/RemoteObject.cs b/src/Libraries/Core/src/Network/RemoteObject.cs deleted file mode 100644 index e73b3dd07..000000000 --- a/src/Libraries/Core/src/Network/RemoteObject.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using System.Net; -using Newtonsoft.Json; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Events; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.Core.Misc; - -namespace UniSpy.Server.Core.Network -{ - public class ConcreteTypeConverter : JsonConverter - { - public override bool CanConvert(Type objectType) - { - //assume we can convert to anything for now - return true; - } - - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - //explicitly specify the concrete type we want to create - return serializer.Deserialize(reader); - } - - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - //use the default serialization - it works fine - serializer.Serialize(writer, value); - } - } - public class RemoteServer : IServer - { - public Guid Id { get; private set; } - public string Name { get; private set; } - [JsonConverter(typeof(IPEndPointConverter))] - public IPEndPoint ListeningIPEndPoint { get; private set; } - [JsonConverter(typeof(IPEndPointConverter))] - public IPEndPoint PublicIPEndPoint { get; private set; } - public RemoteServer() - { - } - public RemoteServer(IServer server) - { - Id = server.Id; - Name = server.Name; - ListeningIPEndPoint = server.ListeningIPEndPoint; - PublicIPEndPoint = server.PublicIPEndPoint; - } - public void Start() { } - } - public class RemoteTcpConnectionManager : IConnectionManager - { -#pragma warning disable CS0067 - public event OnConnectingEventHandler OnInitialization; - public RemoteTcpConnectionManager() { } - public void Start() { } - public void Dispose() { } - } - - public class RemoteTcpConnection : ITcpConnection - { - - [JsonConverter(typeof(ConcreteTypeConverter))] - public IConnectionManager Manager { get; set; } - [JsonConverter(typeof(IPEndPointConverter))] - public IPEndPoint RemoteIPEndPoint { get; set; } - public NetworkConnectionType ConnectionType { get; set; } -#pragma warning disable CS0067 - public event OnConnectedEventHandler OnConnect; -#pragma warning disable CS0067 - public event OnDisconnectedEventHandler OnDisconnect; -#pragma warning disable CS0067 - public event OnReceivedEventHandler OnReceive; - public RemoteTcpConnection() { } - public RemoteTcpConnection(ITcpConnection conn, IConnectionManager manager) - { - Manager = manager; - RemoteIPEndPoint = conn.RemoteIPEndPoint; - ConnectionType = conn.ConnectionType; - } - public void Disconnect() => LogWriter.LogDebug("Remote tcp connection do not have this method."); - public void Send(string response) => LogWriter.LogDebug("Remote tcp connection do not have this method."); - public void Send(byte[] response) => LogWriter.LogDebug("Remote tcp connection do not have this method."); - - public void Dispose() { } - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs deleted file mode 100644 index 34f3fe8f8..000000000 --- a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnection.cs +++ /dev/null @@ -1,91 +0,0 @@ -using System.Linq; -using System.Net; -using System.Net.Sockets; -using System.Threading.Tasks; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.Core.Events; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.Core.Network.Tcp.Server -{ - public class TcpConnection : ITcpConnection - { - public IConnectionManager Manager { get; private set; } - - public IPEndPoint RemoteIPEndPoint { get; private set; } - - public NetworkConnectionType ConnectionType { get; } = NetworkConnectionType.Tcp; - - public event OnConnectedEventHandler OnConnect; - public event OnDisconnectedEventHandler OnDisconnect; - public event OnReceivedEventHandler OnReceive; - - public TcpClient Client { get; private set; } - public TcpConnection(TcpClient client, IConnectionManager manager) - { - RemoteIPEndPoint = (IPEndPoint)client.Client.RemoteEndPoint; - Client = client; - Manager = manager; - } - public void Disconnect() - { - Client.Client.Disconnect(true); - OnDisconnected(); - } - - public void Send(string response) => Send(UniSpyEncoding.GetBytes(response)); - - public void Send(byte[] response) => Client.Client.Send(response); - - public void OnConnected() - { - OnConnect(); - Task.Run(() => StartReceiving()); - } - - private void StartReceiving() - { - if (Client.Connected == false) - { - OnDisconnected(); - return; - } - var stream = Client.GetStream(); - byte[] buffer = new byte[2048]; - int bytesRead; - while (true) - { - try - { - if (!stream.CanRead || !stream.CanWrite) - { - break; - } - bytesRead = stream.Read(buffer, 0, buffer.Length); - if (bytesRead == 0) - { - OnDisconnected(); - break; // Connection closed by the client - } - - var receivedData = buffer.Take(bytesRead).ToArray(); - OnReceive(receivedData); - } - catch (SocketException ex) - { - LogWriter.LogError(ex); - OnDisconnected(); - break; - } - } - } - public void OnDisconnected() - { - OnDisconnect(); - Client.Close(); - } - - public void Dispose() => Client.Dispose(); - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnectionManager.cs b/src/Libraries/Core/src/Network/Tcp/Server/TcpConnectionManager.cs deleted file mode 100644 index 63c032003..000000000 --- a/src/Libraries/Core/src/Network/Tcp/Server/TcpConnectionManager.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Collections.Concurrent; -using System.Net; -using System.Net.Sockets; -using System.Threading.Tasks; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Events; - -namespace UniSpy.Server.Core.Network.Tcp.Server; - -public class TcpConnectionManager : IConnectionManager, IDisposable -{ - public event OnConnectingEventHandler OnInitialization; - public TcpListener Listener; - public TcpConnectionManager(IPEndPoint endPoint) - { - Listener = new TcpListener(endPoint); - } - - public void Start() - { - Listener.Start(); - Task.Run(() => - { - while (true) - { - var client = Listener.AcceptTcpClient(); - var conn = new TcpConnection(client, this); - Task.Run(() => - { - OnInitialization(conn); - conn.OnConnected(); - }); - } - }); - } - - public void Dispose() - { - Listener.Stop(); - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs b/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs deleted file mode 100644 index 59b924988..000000000 --- a/src/Libraries/Core/src/Network/Udp/Server/UdpConnection.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Net; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.Core.Events; - -namespace UniSpy.Server.Core.Network.Udp.Server; - -public class UdpConnection : IUdpConnection -{ - public IConnectionManager Manager { get; private set; } - - public IPEndPoint RemoteIPEndPoint { get; private set; } - - public NetworkConnectionType ConnectionType { get; } = NetworkConnectionType.Udp; - - public event OnReceivedEventHandler OnReceive; - - public UdpConnection(IPEndPoint endPoint, IConnectionManager manager) - { - RemoteIPEndPoint = endPoint; - Manager = manager; - } - public void OnReceived(byte[] buffer) - { - OnReceive(buffer); - } - public void Send(IPEndPoint endPoint, byte[] response) - { - (Manager as UdpConnectionManager).Listener.Send(response, response.Length, endPoint); - } - - public void Send(IPEndPoint endPoint, string response) - { - Send(endPoint, UniSpyEncoding.GetBytes(response)); - } - - public void Send(string response) - { - Send(UniSpyEncoding.GetBytes(response)); - } - - public void Send(byte[] response) - { - (Manager as UdpConnectionManager).Listener.Send(response, response.Length, RemoteIPEndPoint); - } - - public override bool Equals(object obj) - { - return RemoteIPEndPoint.Equals((obj as UdpConnection).RemoteIPEndPoint); - } - public override int GetHashCode() - { - return RemoteIPEndPoint.GetHashCode(); - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/Network/Udp/Server/UdpConnectionManager.cs b/src/Libraries/Core/src/Network/Udp/Server/UdpConnectionManager.cs deleted file mode 100644 index aaa23b246..000000000 --- a/src/Libraries/Core/src/Network/Udp/Server/UdpConnectionManager.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Collections.Concurrent; -using System; -using System.Net; -using System.Net.Sockets; -using System.Threading.Tasks; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Events; - -namespace UniSpy.Server.Core.Network.Udp.Server; - -public class UdpConnectionManager : IConnectionManager, IDisposable -{ - public event OnConnectingEventHandler OnInitialization; - public UdpClient Listener { get; private set; } - public ConcurrentDictionary Pool = new(); - public UdpConnectionManager(IPEndPoint endPoint) - { - Listener = new UdpClient(endPoint); - } - - public void Start() - { - Task.Run(() => - { - while (true) - { - var clientEndPoint = new IPEndPoint(IPAddress.Any, 0); - var data = Listener.Receive(ref clientEndPoint); - - Task.Run(() => - { - UdpConnection conn; - if (!Pool.TryGetValue(clientEndPoint, out conn)) - { - conn = new UdpConnection(clientEndPoint, this); - Pool.TryAdd(clientEndPoint, conn); - } - OnInitialization(conn); - conn.OnReceived(data); - }); - } - }); - } - - - public void Dispose() - { - Listener.Dispose(); - } -} \ No newline at end of file diff --git a/src/Libraries/Core/src/UniSpy.Server.Core.csproj b/src/Libraries/Core/src/UniSpy.Server.Core.csproj deleted file mode 100755 index 8b2a01c11..000000000 --- a/src/Libraries/Core/src/UniSpy.Server.Core.csproj +++ /dev/null @@ -1,30 +0,0 @@ - - - - net6.0 - ..\..\..\..\common\Icon\UniSpy_Logo.ico - - - AnyCPU - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Libraries/LinqToRedis/.gitignore b/src/Libraries/LinqToRedis/.gitignore deleted file mode 100644 index c14372969..000000000 --- a/src/Libraries/LinqToRedis/.gitignore +++ /dev/null @@ -1,391 +0,0 @@ -## Ignore Visual Studio temporary files, build results, and -## files generated by popular Visual Studio add-ons. -## -## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore - -# User-specific files -*.rsuser -*.suo -*.user -*.userosscache -*.sln.docstates - -# User-specific files (MonoDevelop/Xamarin Studio) -*.userprefs - -# Mono auto generated files -mono_crash.* - -# Build results -[Dd]ebug/ -[Dd]ebugPublic/ -[Rr]elease/ -[Rr]eleases/ -x64/ -x86/ -[Ww][Ii][Nn]32/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -bld/ -[Bb]in/ -[Oo]bj/ -[Ll]og/ -[Ll]ogs/ - -# Visual Studio 2015/2017 cache/options directory -.vs/ -# Uncomment if you have tasks that create the project's static files in wwwroot -#wwwroot/ - -# Visual Studio 2017 auto generated files -Generated\ Files/ - -# MSTest test Results -[Tt]est[Rr]esult*/ -[Bb]uild[Ll]og.* - -# NUnit -*.VisualState.xml -TestResult.xml -nunit-*.xml - -# Build Results of an ATL Project -[Dd]ebugPS/ -[Rr]eleasePS/ -dlldata.c - -# Benchmark Results -BenchmarkDotNet.Artifacts/ - -# .NET Core -project.lock.json -project.fragment.lock.json -artifacts/ - -# ASP.NET Scaffolding -ScaffoldingReadMe.txt - -# StyleCop -StyleCopReport.xml - -# Files built by Visual Studio -*_i.c -*_p.c -*_h.h -*.ilk -*.meta -*.obj -*.iobj -*.pch -*.pdb -*.ipdb -*.pgc -*.pgd -*.rsp -*.sbr -*.tlb -*.tli -*.tlh -*.tmp -*.tmp_proj -*_wpftmp.csproj -*.log -*.tlog -*.vspscc -*.vssscc -.builds -*.pidb -*.svclog -*.scc - -# Chutzpah Test files -_Chutzpah* - -# Visual C++ cache files -ipch/ -*.aps -*.ncb -*.opendb -*.opensdf -*.sdf -*.cachefile -*.VC.db -*.VC.VC.opendb - -# Visual Studio profiler -*.psess -*.vsp -*.vspx -*.sap - -# Visual Studio Trace Files -*.e2e - -# TFS 2012 Local Workspace -$tf/ - -# Guidance Automation Toolkit -*.gpState - -# ReSharper is a .NET coding add-in -_ReSharper*/ -*.[Rr]e[Ss]harper -*.DotSettings.user - -# TeamCity is a build add-in -_TeamCity* - -# DotCover is a Code Coverage Tool -*.dotCover - -# AxoCover is a Code Coverage Tool -.axoCover/* -!.axoCover/settings.json - -# Coverlet is a free, cross platform Code Coverage Tool -coverage*.json -coverage*.xml -coverage*.info - -# Visual Studio code coverage results -*.coverage -*.coveragexml - -# NCrunch -_NCrunch_* -.*crunch*.local.xml -nCrunchTemp_* - -# MightyMoose -*.mm.* -AutoTest.Net/ - -# Web workbench (sass) -.sass-cache/ - -# Installshield output folder -[Ee]xpress/ - -# DocProject is a documentation generator add-in -DocProject/buildhelp/ -DocProject/Help/*.HxT -DocProject/Help/*.HxC -DocProject/Help/*.hhc -DocProject/Help/*.hhk -DocProject/Help/*.hhp -DocProject/Help/Html2 -DocProject/Help/html - -# Click-Once directory -publish/ - -# Publish Web Output -*.[Pp]ublish.xml -*.azurePubxml -# Note: Comment the next line if you want to checkin your web deploy settings, -# but database connection strings (with potential passwords) will be unencrypted -*.pubxml -*.publishproj - -# Microsoft Azure Web App publish settings. Comment the next line if you want to -# checkin your Azure Web App publish settings, but sensitive information contained -# in these scripts will be unencrypted -PublishScripts/ - -# NuGet Packages -*.nupkg -# NuGet Symbol Packages -*.snupkg -# The packages folder can be ignored because of Package Restore -**/[Pp]ackages/* -# except build/, which is used as an MSBuild target. -!**/[Pp]ackages/build/ -# Uncomment if necessary however generally it will be regenerated when needed -#!**/[Pp]ackages/repositories.config -# NuGet v3's project.json files produces more ignorable files -*.nuget.props -*.nuget.targets - -# Nuget personal access tokens and Credentials -nuget.config - -# Microsoft Azure Build Output -csx/ -*.build.csdef - -# Microsoft Azure Emulator -ecf/ -rcf/ - -# Windows Store app package directories and files -AppPackages/ -BundleArtifacts/ -Package.StoreAssociation.xml -_pkginfo.txt -*.appx -*.appxbundle -*.appxupload - -# Visual Studio cache files -# files ending in .cache can be ignored -*.[Cc]ache -# but keep track of directories ending in .cache -!?*.[Cc]ache/ - -# Others -ClientBin/ -~$* -*~ -*.dbmdl -*.dbproj.schemaview -*.jfm -*.pfx -*.publishsettings -orleans.codegen.cs - -# Including strong name files can present a security risk -# (https://github.com/github/gitignore/pull/2483#issue-259490424) -#*.snk - -# Since there are multiple workflows, uncomment next line to ignore bower_components -# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) -#bower_components/ - -# RIA/Silverlight projects -Generated_Code/ - -# Backup & report files from converting an old project file -# to a newer Visual Studio version. Backup files are not needed, -# because we have git ;-) -_UpgradeReport_Files/ -Backup*/ -UpgradeLog*.XML -UpgradeLog*.htm -ServiceFabricBackup/ -*.rptproj.bak - -# SQL Server files -*.mdf -*.ldf -*.ndf - -# Business Intelligence projects -*.rdl.data -*.bim.layout -*.bim_*.settings -*.rptproj.rsuser -*- [Bb]ackup.rdl -*- [Bb]ackup ([0-9]).rdl -*- [Bb]ackup ([0-9][0-9]).rdl - -# Microsoft Fakes -FakesAssemblies/ - -# GhostDoc plugin setting file -*.GhostDoc.xml - -# Node.js Tools for Visual Studio -.ntvs_analysis.dat -node_modules/ - -# Visual Studio 6 build log -*.plg - -# Visual Studio 6 workspace options file -*.opt - -# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) -*.vbw - -# Visual Studio LightSwitch build output -**/*.HTMLClient/GeneratedArtifacts -**/*.DesktopClient/GeneratedArtifacts -**/*.DesktopClient/ModelManifest.xml -**/*.Server/GeneratedArtifacts -**/*.Server/ModelManifest.xml -_Pvt_Extensions - -# Paket dependency manager -.paket/paket.exe -paket-files/ - -# FAKE - F# Make -.fake/ - -# CodeRush personal settings -.cr/personal - -# Python Tools for Visual Studio (PTVS) -__pycache__/ -*.pyc - -# Cake - Uncomment if you are using it -# tools/** -# !tools/packages.config - -# Tabs Studio -*.tss - -# Telerik's JustMock configuration file -*.jmconfig - -# BizTalk build output -*.btp.cs -*.btm.cs -*.odx.cs -*.xsd.cs - -# OpenCover UI analysis results -OpenCover/ - -# Azure Stream Analytics local run output -ASALocalRun/ - -# MSBuild Binary and Structured Log -*.binlog - -# NVidia Nsight GPU debugger configuration file -*.nvuser - -# MFractors (Xamarin productivity tool) working folder -.mfractor/ - -# Local History for Visual Studio -.localhistory/ - -# BeatPulse healthcheck temp database -healthchecksdb - -# Backup folder for Package Reference Convert tool in Visual Studio 2017 -MigrationBackup/ - -# Ionide (cross platform F# VS Code tools) working folder -.ionide/ - -# Fody - auto-generated XML schema -FodyWeavers.xsd - -# VS Code files for those working on multiple tools -.vscode/* -!.vscode/settings.json -!.vscode/tasks.json -!.vscode/launch.json -!.vscode/extensions.json -*.code-workspace - -# Local History for Visual Studio Code -.history/ - -# Windows Installer files from build outputs -*.cab -*.msi -*.msix -*.msm -*.msp - -# JetBrains Rider -.idea/ -*.sln.iml - -# MAC cache -*.DS_Store diff --git a/src/Libraries/LinqToRedis/LinqToRedis.sln b/src/Libraries/LinqToRedis/LinqToRedis.sln deleted file mode 100644 index 6964007f3..000000000 --- a/src/Libraries/LinqToRedis/LinqToRedis.sln +++ /dev/null @@ -1,28 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 17 -VisualStudioVersion = 17.0.31903.59 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniSpy.LinqToRedis", "src\UniSpy.LinqToRedis.csproj", "{8EA3FD73-4113-4630-BC83-566A34AD0C11}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniSpy.LinqToRedis.Test", "test\UniSpy.LinqToRedis.Test.csproj", "{34A8446C-0ABD-4535-8AFF-F452E8DC65A8}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {8EA3FD73-4113-4630-BC83-566A34AD0C11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8EA3FD73-4113-4630-BC83-566A34AD0C11}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8EA3FD73-4113-4630-BC83-566A34AD0C11}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8EA3FD73-4113-4630-BC83-566A34AD0C11}.Release|Any CPU.Build.0 = Release|Any CPU - {34A8446C-0ABD-4535-8AFF-F452E8DC65A8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {34A8446C-0ABD-4535-8AFF-F452E8DC65A8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {34A8446C-0ABD-4535-8AFF-F452E8DC65A8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {34A8446C-0ABD-4535-8AFF-F452E8DC65A8}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection -EndGlobal diff --git a/src/Libraries/LinqToRedis/README.md b/src/Libraries/LinqToRedis/README.md deleted file mode 100644 index e2a56f628..000000000 --- a/src/Libraries/LinqToRedis/README.md +++ /dev/null @@ -1,44 +0,0 @@ -# LinqToRedis -This small tool will give you the power to query redis key value data with Linq.
-This project is inspired by UniSpyServer. - -# Usage -The example is shown below. -1. You need to create your class inherent from RedisKeyValueObject -2. Define some properties with \[RedisKey\] attribute -3. Create your own class which inherent from RedisClient<>, remember to specify the database -4. Use (1) GetValue method (2) index access (3) Linq to query from redis - -# Note -1. The linq query buildin only support ==,&&,|| operation, you can not query more complex expression -2. The properties with \[RedisKey\] attribute are the key used to search on redis, the raw redis query string is build to query from redis -3. Use LinqToRedis to query simple condition, get the data from redis, then use linq for List<> or Dictionary<> to query more complex data - -# Example code -``` - public record UserInfo : RedisKeyValueObject - { - [RedisKey] - public Guid? ServerID { get; set; } - [RedisKey] - public int? Cookie { get; set; } - public string UserName { get; set; } - public string RemoteEndPoint { get; set; } - } - - - internal class RedisClient : UniSpyServer.LinqToRedis.RedisClient - { - public RedisClient() : base("127.0.0.1:6789", 0) - { - } - } -``` -Then you can use the general way to query from redis. -``` - var key = new UserInfo{ Cookie=1 }; - var client = new RedisClient(); - var results1 = client.GetValue(key); - var results2 = client[key]; - var results3 = client.Where(x => x.Cookie == 1).ToList(); -``` diff --git a/src/Libraries/LinqToRedis/src/Core/IRedisKey.cs b/src/Libraries/LinqToRedis/src/Core/IRedisKey.cs deleted file mode 100644 index 9560028c1..000000000 --- a/src/Libraries/LinqToRedis/src/Core/IRedisKey.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace UniSpy.LinqToRedis -{ - public interface IRedisKey - { - string SearchKey { get; } - string FullKey { get; } - } -} \ No newline at end of file diff --git a/src/Libraries/LinqToRedis/src/Core/RedisClient.cs b/src/Libraries/LinqToRedis/src/Core/RedisClient.cs deleted file mode 100644 index ac4501e25..000000000 --- a/src/Libraries/LinqToRedis/src/Core/RedisClient.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Threading.Tasks; -using Newtonsoft.Json; -using StackExchange.Redis; -using UniSpy.LinqToRedis.Linq; - -namespace UniSpy.LinqToRedis -{ - /// - /// The redis linq client - /// - /// - public class RedisClient where TValue : RedisKeyValueObject - { - public TimeSpan? ExpireTime { get; private set; } - public IConnectionMultiplexer Multiplexer { get; private set; } - public IDatabase Db { get; private set; } - protected EndPoint[] _endPoints => Multiplexer.GetEndPoints(); - private RedisQueryProvider _provider; - /// - /// Search redis key value storage by key - /// - /// The redis key class - /// - public QueryableObject Context; - /// - /// The default keyvalue object, used to provide default information - /// - public readonly TValue DefaultKVObject = ((TValue)System.Activator.CreateInstance(typeof(TValue))); - public RedisClient(string connectionString) : this(ConnectionMultiplexer.Connect(connectionString)) { } - /// - /// Use existing multiplexer for performance - /// - public RedisClient(IConnectionMultiplexer multiplexer) - { - CheckValidation(); - Multiplexer = multiplexer; - Db = Multiplexer.GetDatabase((int)DefaultKVObject.Db); - _provider = new RedisQueryProvider(this); - Context = new QueryableObject(_provider); - } - private void CheckValidation() - { - // check if Db is set - if (DefaultKVObject.Db is null) - { - throw new ArgumentNullException($"The RedisKeyValueObject:{this.GetType().Name} must set database number in constructor"); - } - var properties = typeof(TValue).GetProperties().Where(p => p.GetCustomAttributes(typeof(RedisKeyAttribute), true).Select(a => a is RedisKeyAttribute).Any()).ToList(); - if (properties.Count() == 0) - { - throw new ArgumentNullException($"The RedisKeyValueObject:{this.GetType().Name} must have a key"); - } - // we need to check whether - var valueProperties = properties.Where(p => p.PropertyType.IsValueType == true && p.PropertyType.IsGenericType == false).ToList(); - if (valueProperties.Count >= 1) - { - var propNames = ""; - foreach (var prop in valueProperties) - { - propNames += $" {typeof(TValue).Name}.{prop.Name}"; - } - throw new ArgumentException($"The RedisKey object must be reference type, please convert the following properties to reference type:{propNames}"); - } - } - - public async Task DeleteKeyValueAsync(TValue key) - { - await Db.KeyDeleteAsync(key.FullKey); - } - public void DeleteKeyValue(TValue key) - { - Db.KeyDeleteAsync(key.FullKey); - } - public List GetValues(TValue key) - { - return GetKeyValues(key).Values.ToList(); - } - public async Task> GetValuesAsync(TValue key) - { - var dict = await GetKeyValuesAsync(key); - return dict.Values.ToList(); - } - public List GetMatchedKeys(IRedisKey key = null) - { - return Task.Run(async () => await GetMatchedKeysAsync(key)).Result; - } - public async Task> GetMatchedKeysAsync(IRedisKey key = null) - { - var matchedKeys = new List(); - var searchKey = key is null ? DefaultKVObject.SearchKey : key.SearchKey; - foreach (var end in _endPoints) - { - var server = Multiplexer.GetServer(end); - // we get specific key from database - await foreach (var k in server.KeysAsync(pattern: searchKey, database: Db.Database)) - { - matchedKeys.Add(k); - } - } - return matchedKeys; - } - /// - /// Get matched key value by key from database. - /// if key is null this will get all key value from database - /// - /// - public Dictionary GetKeyValues(IRedisKey key = null) - { - return Task.Run>(async () => await GetKeyValuesAsync(key)).Result; - } - - public async Task> GetKeyValuesAsync(IRedisKey key = null) - { - var dict = new Dictionary(); - var keys = await GetMatchedKeysAsync(key); - foreach (var k in keys) - { - var value = await Db.StringGetAsync(k.ToString()); - dict.Add(k, JsonConvert.DeserializeObject(value)); - } - return dict; - } - - public bool SetValue(TValue value) - { - return Task.Run(async () => await SetValueAsync(value)).Result; - } - public async Task SetValueAsync(TValue value) - { - return await Db.StringSetAsync(value.FullKey, JsonConvert.SerializeObject((TValue)value), value.ExpireTime); - } - public TValue GetValue(IRedisKey key) - { - return Task.Run(async () => await GetValueAsync(key)).Result; - } - public async Task GetValueAsync(IRedisKey key) - { - var value = await Db.StringGetAsync(key.FullKey); - if (value.IsNull) - { - return default; - } - return JsonConvert.DeserializeObject(value); - } - - /// - /// The index access of redis key value object is always sync - /// - public TValue this[IRedisKey key] - { - get => GetValue(key); - set => SetValue(value); - } - public void FlushDb() - { - Task.Run(async () => await FlushDbAsync()); - } - public async Task FlushDbAsync() - { - var keys = GetMatchedKeysAsync(); - foreach (var key in await keys) - { - await Db.KeyDeleteAsync(key); - } - } - } -} diff --git a/src/Libraries/LinqToRedis/src/Core/RedisKeyAttribute.cs b/src/Libraries/LinqToRedis/src/Core/RedisKeyAttribute.cs deleted file mode 100644 index bea2d4935..000000000 --- a/src/Libraries/LinqToRedis/src/Core/RedisKeyAttribute.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System; - -namespace UniSpy.LinqToRedis -{ - /// - /// Mark property as redis key attribute, so you can search by this key - /// The key must be simple object, which mean its ToString method can out put a valid string - /// Such as int, string, DateTime, Guid, IPEndPoint, etc. - /// - [AttributeUsage(validOn: AttributeTargets.Property | AttributeTargets.Field)] - public class RedisKeyAttribute : Attribute - { - public RedisKeyAttribute() { } - } -} \ No newline at end of file diff --git a/src/Libraries/LinqToRedis/src/Core/RedisKeyValueObject.cs b/src/Libraries/LinqToRedis/src/Core/RedisKeyValueObject.cs deleted file mode 100644 index f6eb0b795..000000000 --- a/src/Libraries/LinqToRedis/src/Core/RedisKeyValueObject.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using Newtonsoft.Json; - -namespace UniSpy.LinqToRedis -{ - public abstract record RedisKeyValueObject : IRedisKey - { - - // [JsonIgnore] - // protected List _supportedTypes = new List - // { - // typeof(string), - // typeof(int), - // typeof(long), - // typeof(double), - // typeof(bool), - // typeof(int?), - // typeof(int), - // typeof(int?), - // typeof(long?), - // typeof(double?), - // typeof(bool?), - // typeof(DateTime), - // typeof(byte[]), - // typeof(byte?[]), - // typeof(Guid?), - // typeof(Guid), - // typeof(TimeSpan), - // typeof(IPEndPoint), - // typeof(Nullable), - // typeof(Enum) - // }; - [JsonIgnore] - public TimeSpan? ExpireTime { get; private set; } - [JsonIgnore] - public string FullKey => BuildFullKey(); - [JsonIgnore] - public string SearchKey => BuildSearchKey(); - [RedisKey] - public int? Db { get; private set; } - public RedisKeyValueObject(int db, TimeSpan? expireTime = null) - { - ExpireTime = expireTime; - Db = db; - } - /// - /// This is using for json deserialization - /// - protected RedisKeyValueObject() { } - - private string BuildFullKey() - { - string fullKey = null; - var properties = GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(RedisKeyAttribute), true).Select(a => a is RedisKeyAttribute).Any()).ToList(); - - foreach (var property in properties) - { - // if (!_supportedTypes.Contains(property.PropertyType)) - // { - // throw new NotSupportedException($"The complex type:{property.PropertyType} is not supported"); - // } - if (property.GetValue(this) is null) - { - throw new ArgumentNullException($"{property.Name} is null when building full key."); - } - var keyValueStr = $"{property.Name}={property.GetValue(this)}"; - if (fullKey is null) - { - fullKey = $"{keyValueStr}"; - } - else - { - fullKey = $"{fullKey}:{keyValueStr}"; - } - } - return fullKey; - } - private string BuildSearchKey() - { - var builder = new StringBuilder(); - var properties = GetType().GetProperties().Where(p => p.GetCustomAttributes(typeof(RedisKeyAttribute), false).Where(a => a is RedisKeyAttribute).Any()).ToList(); - if (properties.Count() == 0) - { - throw new ArgumentNullException($"The RedisKeyValueObject:{this.GetType().Name} must have a key"); - } - - - var propKVList = new List>(); - foreach (var property in properties) - { - propKVList.Add(new KeyValuePair(property.Name, property.GetValue(this))); - } - - foreach (var item in propKVList) - { - if (item.Value is null) - { - builder.Append($"{item.Key}=*"); - } - else - { - builder.Append($"{item.Key}={item.Value}"); - } - - if (item.Key != propKVList.Last().Key) - { - builder.Append(":"); - } - } - return builder.ToString(); - } - } -} \ No newline at end of file diff --git a/src/Libraries/LinqToRedis/src/Core/RedisLock.cs b/src/Libraries/LinqToRedis/src/Core/RedisLock.cs deleted file mode 100644 index fb1948a5a..000000000 --- a/src/Libraries/LinqToRedis/src/Core/RedisLock.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using StackExchange.Redis; -using System.Threading.Tasks; - -namespace UniSpy.LinqToRedis -{ - public class RedisLock : IDisposable - { - public TimeSpan LockTime { get; private set; } - public TimeSpan? RetryTime { get; private set; } - public TimeSpan? RetryInterval { get; private set; } - public IDatabase Db { get; private set; } - /// - /// The token used to lock the object - /// - /// - public RedisValue LockToken { get; private set; } = Environment.MachineName; - /// - /// The key using to lock the object, original key is ChannelName:myfriend the lock key will be Lock_ChannelName:myfriend - /// - /// - public string LockKey { get; private set; } - public RedisLock(TimeSpan lockTime, IDatabase db, RedisKeyValueObject key) : this(lockTime, null, null, db, key) { } - public RedisLock(TimeSpan lockTime, TimeSpan? retryTime, TimeSpan? retryInterval, IDatabase db, RedisKeyValueObject key) - { - LockTime = lockTime; - RetryTime = retryTime; - RetryInterval = retryInterval; - Db = db; - LockKey = "Lock_" + key.FullKey; - } - - public bool LockTake() - { - return Db.LockTake(LockKey, LockToken, LockTime); - } - public async Task LockTakeAsync() - { - return await Db.LockTakeAsync(LockKey, LockToken, LockTime); - } - public bool LockRelease() - { - return Db.LockRelease(LockKey, LockToken); - } - - public void Dispose() - { - Db.LockRelease(LockKey, LockToken); - } - } -} \ No newline at end of file diff --git a/src/Libraries/LinqToRedis/src/Linq/QueryEvaluator.cs b/src/Libraries/LinqToRedis/src/Linq/QueryEvaluator.cs deleted file mode 100644 index 0097de414..000000000 --- a/src/Libraries/LinqToRedis/src/Linq/QueryEvaluator.cs +++ /dev/null @@ -1,106 +0,0 @@ - -using System; -using System.Collections.Generic; -using System.Linq.Expressions; - -namespace UniSpy.LinqToRedis.Linq -{ - public static class QueryEvaluator - { - /// - /// Performs evaluation and replacement of independent sub-trees - /// - /// The root of the noderession tree. - /// A function that decides whether a given noderession node can be part of the local function. - /// A new tree with sub-trees evaluated and replaced. - public static Expression PartialEval(Expression noderession, Func fnCanBeEvaluated) - => new SubtreeEvaluator(new Nominator(fnCanBeEvaluated).Nominate(noderession)).Eval(noderession); - - /// - /// Performs evaluation and replacement of independent sub-trees - /// - /// The root of the noderession tree. - /// A new tree with sub-trees evaluated and replaced. - public static Expression PartialEval(Expression noderession) => PartialEval(noderession, QueryEvaluator.CanBeEvaluatedLocally); - - private static bool CanBeEvaluatedLocally(Expression noderession) => noderession.NodeType != ExpressionType.Parameter; - - /// - /// Evaluates and replaces sub-trees when first candidate is reached (top-down) - /// - class SubtreeEvaluator : ExpressionVisitor - { - HashSet candidates; - internal SubtreeEvaluator(HashSet candidates) - { - this.candidates = candidates; - } - internal Expression Eval(Expression node) => this.Visit(node); - - public override Expression Visit(Expression node) - { - if (node is null) - { - return null; - } - if (this.candidates.Contains(node)) - { - return this.Evaluate(node); - } - return base.Visit(node); - } - private Expression Evaluate(Expression node) - { - if (node.NodeType == ExpressionType.Constant) - { - return node; - } - LambdaExpression lambda = Expression.Lambda(node); - Delegate fn = lambda.Compile(); - return Expression.Constant(fn.DynamicInvoke(null), node.Type); - } - } - /// - /// Performs bottom-up analysis to determine which nodes can possibly - /// be part of an evaluated sub-tree. - /// - internal class Nominator : ExpressionVisitor - { - Func fnCanBeEvaluated; - HashSet candidates; - bool cannotBeEvaluated; - internal Nominator(Func fnCanBeEvaluated) - { - this.fnCanBeEvaluated = fnCanBeEvaluated; - } - internal HashSet Nominate(Expression noderession) - { - this.candidates = new HashSet(); - this.Visit(noderession); - return this.candidates; - } - public override Expression Visit(Expression node) - { - if (node is not null) - { - bool saveCannotBeEvaluated = this.cannotBeEvaluated; - this.cannotBeEvaluated = false; - base.Visit(node); - if (!this.cannotBeEvaluated) - { - if (this.fnCanBeEvaluated(node)) - { - this.candidates.Add(node); - } - else - { - this.cannotBeEvaluated = true; - } - } - this.cannotBeEvaluated |= saveCannotBeEvaluated; - } - return node; - } - } - } -} \ No newline at end of file diff --git a/src/Libraries/LinqToRedis/src/Linq/QueryProviderBase.cs b/src/Libraries/LinqToRedis/src/Linq/QueryProviderBase.cs deleted file mode 100644 index c697385e3..000000000 --- a/src/Libraries/LinqToRedis/src/Linq/QueryProviderBase.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System; -using System.Linq; -using System.Linq.Expressions; - -namespace UniSpy.LinqToRedis.Linq -{ - public abstract class QueryProviderBase : IQueryProvider - { - public QueryProviderBase() { } - IQueryable IQueryProvider.CreateQuery(Expression expression) => new QueryableObject(this, expression); - IQueryable IQueryProvider.CreateQuery(Expression expression) => (IQueryable)Activator.CreateInstance(typeof(QueryableObject<>).MakeGenericType(expression.Type), new object[] { this, expression }); - T IQueryProvider.Execute(Expression expression) => (T)Execute(expression); - object IQueryProvider.Execute(Expression expression) => Execute(expression); - public abstract object Execute(Expression expression); - } -} \ No newline at end of file diff --git a/src/Libraries/LinqToRedis/src/Linq/QueryableObject.cs b/src/Libraries/LinqToRedis/src/Linq/QueryableObject.cs deleted file mode 100644 index ba967bea0..000000000 --- a/src/Libraries/LinqToRedis/src/Linq/QueryableObject.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; - -namespace UniSpy.LinqToRedis.Linq -{ - public class QueryableObject : IQueryable - { - private QueryProviderBase _provider; - private Expression _expression; - public QueryableObject(QueryProviderBase provider) - { - if (provider is null) - { - throw new ArgumentNullException("provider"); - } - _provider = provider; - _expression = Expression.Constant(this); - } - public QueryableObject(QueryProviderBase provider, Expression expression) - { - if (provider is null) - { - throw new ArgumentNullException("provider"); - } - - if (expression is null) - { - throw new ArgumentNullException("expression"); - } - - if (!typeof(IQueryable).IsAssignableFrom(expression.Type)) - { - throw new ArgumentOutOfRangeException("expression"); - } - - _provider = provider; - _expression = expression; - } - public Type ElementType => typeof(T); - public Expression Expression => _expression; - public IQueryProvider Provider => _provider; - - public IEnumerator GetEnumerator() => ((IEnumerable)_provider.Execute(_expression)).GetEnumerator(); - - IEnumerator IEnumerable.GetEnumerator() => this.GetEnumerator(); - } -} \ No newline at end of file diff --git a/src/Libraries/LinqToRedis/src/Linq/RedisQueryBuilder.cs b/src/Libraries/LinqToRedis/src/Linq/RedisQueryBuilder.cs deleted file mode 100644 index 82e2d304e..000000000 --- a/src/Libraries/LinqToRedis/src/Linq/RedisQueryBuilder.cs +++ /dev/null @@ -1,139 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; - -namespace UniSpy.LinqToRedis.Linq -{ - /// - /// Provide high level API for redis query building - /// - /// - public class RedisQueryBuilder : ExpressionVisitor where TKey : RedisKeyValueObject - { - public TKey KeyObject { get; private set; } - private List _builderStack = new List(); - private Expression _expression; - public RedisQueryBuilder(Expression expression) - { - _expression = expression; - } - - public void Build() - { - KeyObject = (TKey)Activator.CreateInstance(typeof(TKey)); - Visit(_expression); - System.Threading.Tasks.Parallel.For(0, _builderStack.Count, - i => - { - if (i % 2 == 0) - { - var keyProperty = KeyObject.GetType().GetProperty((string)_builderStack[i]); - // get target type - var targetType = IsNullableType(keyProperty.PropertyType) ? Nullable.GetUnderlyingType(keyProperty.PropertyType) : keyProperty.PropertyType; - // convert value to target type - var value = _builderStack[i + 1]; - // _builderStack[i + 1] = Convert.ChangeType(_builderStack[i + 1], targetType); - if (targetType.IsEnum) - { - value = Enum.ToObject(targetType, value); - } - else - { - value = Convert.ChangeType(value, targetType); - } - keyProperty.SetValue(KeyObject, value); - } - }); - } - private static bool IsNullableType(Type type) => type.IsGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>)); - - private static Expression StripQuotes(Expression node) - { - while (node.NodeType == ExpressionType.Quote) - { - node = ((UnaryExpression)node).Operand; - } - return node; - } - - protected override Expression VisitMethodCall(MethodCallExpression node) - { - Expression correctNode; - if (node.Arguments.Count > 1) - { - // this indicate the first linq expression - if (node.Arguments[0].Type.GenericTypeArguments.FirstOrDefault().IsSubclassOf(typeof(RedisKeyValueObject))) - { - correctNode = node.Arguments[1]; - } - else - { - correctNode = node.Arguments[0]; - } - } - else - { - correctNode = node.Arguments[0]; - } - - - switch (node.Method.Name) - { - case "Where": - case "Count": - case "First": - case "FirstOrDefault": - Visit(correctNode); - break; - default: - throw new NotSupportedException(string.Format("The method '{0}' is not supported", node.Method.Name)); - } - - return node; - } - protected override Expression VisitParameter(ParameterExpression node) - { - return base.VisitParameter(node); - } - protected override Expression VisitMember(MemberExpression node) - { - // we check if property do not have RedisKeyAttribute - // every property that queries must have RedisKeyAttribute - - var property = node.Member.DeclaringType.GetProperty(node.Member.Name); - if (property.GetCustomAttributes(typeof(RedisKeyAttribute), true).Count() != 1) - { - throw new NotSupportedException($"The property: {node.Member.Name} is not key, please use the property with RedisKeyAttribute or add RedisKeyAttribute to this property."); - } - _builderStack.Add(node.Member.Name); - - return node; - } - protected override Expression VisitBinary(BinaryExpression node) - { - Visit(node.Left); - switch (node.NodeType) - { - case ExpressionType.And: - case ExpressionType.AndAlso: - case ExpressionType.Equal: - break; - default: - throw new NotSupportedException(string.Format("The binary operator '{0}' is not supported", node.NodeType)); - } - Visit(node.Right); - return node; - } - - protected override Expression VisitConstant(ConstantExpression node) - { - if (node.Value is null) - { - throw new NotSupportedException("The constant must have value"); - } - _builderStack.Add(node.Value); - return node; - } - } -} \ No newline at end of file diff --git a/src/Libraries/LinqToRedis/src/Linq/RedisQueryProvider.cs b/src/Libraries/LinqToRedis/src/Linq/RedisQueryProvider.cs deleted file mode 100644 index aa535848a..000000000 --- a/src/Libraries/LinqToRedis/src/Linq/RedisQueryProvider.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; - -namespace UniSpy.LinqToRedis.Linq -{ - public class RedisQueryProvider : QueryProviderBase where TValue : RedisKeyValueObject - { - private RedisClient _client; - public RedisQueryProvider(RedisClient client) : base() - { - _client = client; - } - - public override object Execute(Expression expression) - { - // if (node.Method.Name == "ToList") - /* TODO currently we do not know how to get the ToList() function name - we just simply do this*/ - if (expression.GetType() == typeof(ConstantExpression)) - { - return _client.GetKeyValues().Values; - } - var node = (MethodCallExpression)expression; - - if (node.Method.Name == "Count" && node.Arguments.Count == 1) - { - return _client.GetMatchedKeys().Count; - } - - var matchedKeys = new List(); - expression = QueryEvaluator.PartialEval(expression); - var builder = new RedisQueryBuilder(expression); - builder.Build(); - - if (node.Method.Name == "Count" && node.Arguments.Count != 1) - { - return _client.GetMatchedKeys(builder.KeyObject).Count; - } - - var values = _client.GetValues(builder.KeyObject); - - switch (node.Method.Name) - { - case "Where": - return values; - case "FirstOrDefault": - return values.FirstOrDefault(); - case "First": - if (values.Count == 0) - { - throw new InvalidOperationException("The result is empty, try to use FirstOrDefault instead."); - } - return values[0]; - // return values.First(); - default: - throw new NotSupportedException(string.Format("The method '{0}' is not supported", node.Method.Name)); - } - } - } -} \ No newline at end of file diff --git a/src/Libraries/LinqToRedis/src/UniSpy.LinqToRedis.csproj b/src/Libraries/LinqToRedis/src/UniSpy.LinqToRedis.csproj deleted file mode 100644 index 7f852da9c..000000000 --- a/src/Libraries/LinqToRedis/src/UniSpy.LinqToRedis.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - net6.0 - UniSpy.LinqToRedis - 1.0.4 - xiaojiuwo - GameProgressive - true - ..\..\..\..\common\Icon\UniSpy_Logo.ico - - - AnyCPU - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - - diff --git a/src/Libraries/LinqToRedis/test/LinqToRedisTest.cs b/src/Libraries/LinqToRedis/test/LinqToRedisTest.cs deleted file mode 100644 index a82e797b6..000000000 --- a/src/Libraries/LinqToRedis/test/LinqToRedisTest.cs +++ /dev/null @@ -1,173 +0,0 @@ -using System; -using System.Linq; -using System.Threading.Tasks; -using Newtonsoft.Json; -using UniSpy.LinqToRedis; -using Xunit; - -namespace UniSpy.Redis.Test -{ - public class LinqToRedisTest - { - [Fact] - public void GetAllValues() - { - var redis = new RedisClient(); - var data = redis.Context.ToList(); - } - [Fact] - public void PerformaceTest() - { - // Given - var redisClient = new RedisClient(); - var value = new UserInfo - { - Cookie = 0, - ServerID = new Guid(), - UserName = "hello0", - RemoteEndPoint = "127.0.0.1:7890" - }; - - // linqtoredis performance - // When - var start = System.DateTime.Now; - for (var i = 0; i < 100; i++) - { - redisClient.SetValue(value); - value.Cookie++; - } - Console.WriteLine("linqtoredis sync: {0}", (System.DateTime.Now.Subtract(start).TotalSeconds)); - - start = System.DateTime.Now; - - for (var i = 0; i < 100; i++) - { - _ = redisClient.SetValueAsync(value); - value.Cookie++; - } - - Console.WriteLine("linqtoredis async: {0}", (System.DateTime.Now.Subtract(start).TotalSeconds)); - // natural redis api performance - start = System.DateTime.Now; - for (var i = 0; i < 100; i++) - { - redisClient.Db.StringSet(value.FullKey, JsonConvert.SerializeObject(value)); - value.Cookie++; - } - Console.WriteLine("natural redis api sync: {0}", (System.DateTime.Now.Subtract(start).TotalSeconds)); - start = System.DateTime.Now; - - for (var i = 0; i < 100; i++) - { - redisClient.Db.StringSetAsync(value.FullKey, JsonConvert.SerializeObject(value)); - value.Cookie++; - } - Console.WriteLine("natural redis api async: {0}", (System.DateTime.Now.Subtract(start).TotalSeconds)); - } - [Fact] - public void ReadTest1() - { - var redis = new RedisClient(); - var data2 = redis.Context.Where(k => k.Cookie == 0).ToList(); - var data3 = redis.Context.Where(k => k.Cookie == 0).FirstOrDefault(); - var data4 = redis.Context.FirstOrDefault(k => k.Cookie == 0); - } - [Fact] - public void ReadTest2() - { - var redis = new RedisClient(); - var data2 = redis.Context.Where(k => k.RemoteEndPoint == "127.0.0.1:7777").ToList(); - Console.WriteLine(data2.Count); - } - [Fact] - public void WriteTest1() - { - var redis = new RedisClient(); - var value = new UserInfo - { - Cookie = 0, - ServerID = new Guid(), - UserName = "hello0", - RemoteEndPoint = "127.0.0.1:7890" - }; - - redis.SetValue(value); - } - [Fact] - public void WriteTest2() - { - var redis = new RedisClient(); - - for (int i = 0; i < 100; i++) - { - var value = new UserInfo - { - Cookie = 0, - ServerID = new Guid(), - UserName = "hello2", - RemoteEndPoint = $"127.0.0.1:{i}" - }; - redis.SetValue(value); - } - } - [Fact] - public void IndexWriteTest() - { - var redis = new RedisClient(); - - for (int i = 0; i < 100; i++) - { - var value = new UserInfo - { - Cookie = 0, - ServerID = new Guid(), - UserName = "hello2", - RemoteEndPoint = $"127.0.0.1:{i}" - }; - // you can directly using value object to set value - redis[value] = value; - // or for understanding you can convert value object to IRedisKey - var key = (IRedisKey)value; - redis[key] = value; - } - } - [Fact] - public void IndexReadTest() - { - var redis = new RedisClient(); - - for (int i = 0; i < 100; i++) - { - var key = new UserInfo - { - Cookie = i, - ServerID = new Guid() - }; - var value = redis[key]; - } - } - - [Fact] - public void CountTest() - { - var redis = new RedisClient(); - var data1 = redis.Context.Count(); - var data2 = redis.Context.Count(k => k.Cookie == 134); - Assert.Throws(() => redis.Context.First(k => k.Cookie == 134)); - var data4 = redis.Context.Where(k => k.Cookie == 0).First(); - } - [Fact] - public void SearchKeyBuild() - { - // Given - var user1 = new UserInfo() { Cookie = 0 }; - var key1 = user1.SearchKey; - - var user2 = new UserInfo() { ServerID = Guid.NewGuid(), Cookie = 0 }; - var key2 = user2.SearchKey; - - var user3 = new UserInfo() { ServerID = Guid.NewGuid(), Cookie = 0, UserName = "xiaojiuwo", RemoteEndPoint = "192.168.1.1" }; - var key3 = user3.SearchKey; - } - } -} diff --git a/src/Libraries/LinqToRedis/test/NatNeg/RedisClient.cs b/src/Libraries/LinqToRedis/test/NatNeg/RedisClient.cs deleted file mode 100644 index 179b6338b..000000000 --- a/src/Libraries/LinqToRedis/test/NatNeg/RedisClient.cs +++ /dev/null @@ -1,10 +0,0 @@ - -namespace UniSpy.Redis.Test -{ - internal class RedisClient : UniSpy.LinqToRedis.RedisClient - { - public RedisClient() : base("127.0.0.1:6379", 10) - { - } - } -} \ No newline at end of file diff --git a/src/Libraries/LinqToRedis/test/NatNeg/UserInfo.cs b/src/Libraries/LinqToRedis/test/NatNeg/UserInfo.cs deleted file mode 100644 index 5ffd9b2d7..000000000 --- a/src/Libraries/LinqToRedis/test/NatNeg/UserInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using UniSpy.LinqToRedis; - -namespace UniSpy.Redis.Test -{ - public record UserInfo : RedisKeyValueObject - { - [RedisKey] - public Guid? ServerID { get; set; } - [RedisKey] - public int? Cookie { get; set; } - [RedisKey] - public string RemoteEndPoint { get; set; } - public string UserName { get; set; } - public UserInfo() : base(TimeSpan.FromMinutes(3)) - { - // we set the expire time to 3 minutes - } - - } -} \ No newline at end of file diff --git a/src/Libraries/LinqToRedis/test/UniSpy.LinqToRedis.Test.csproj b/src/Libraries/LinqToRedis/test/UniSpy.LinqToRedis.Test.csproj deleted file mode 100644 index 8c5e9adbf..000000000 --- a/src/Libraries/LinqToRedis/test/UniSpy.LinqToRedis.Test.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - net6.0 - - false - - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - diff --git a/.github/README.MD b/src/README similarity index 71% rename from .github/README.MD rename to src/README index fd1ea7c6e..c3499728f 100644 --- a/.github/README.MD +++ b/src/README @@ -1,5 +1,3 @@ -# UniSpy Server - [![license](https://img.shields.io/github/license/GameProgressive/UniSpyServer.svg)](../LICENSE) ![CIPass](https://github.com/GameProgressive/UniSpyServer/workflows/CI/badge.svg)\ ![platforms](https://img.shields.io/badge/platform-win32%20%7C%20win64%20%7C%20linux%20%7C%20osx-brightgreen.svg)\ @@ -20,11 +18,18 @@ Technical papers and documentation about the GameSpy protocol and the games that * [Luigi Auriemma](https://aluigi.altervista.org/papers.htm#distrust) for his gamespy papers that were used as a reference * [BattleSpy](https://github.com/BF2Statistics/BattleSpy) for their library, that we used as a base for UniSpy * [NetCoreServer](https://github.com/chronoxor/NetCoreServer) for their TCP and UDP server -* [openspy-core-v2](https://github.com/chc/openspy-core-v2) for code reference that were used to understand GameSpy protocol in detail - -## Thanks -* [CHC](https://github.com/chc) for sharing his knowledge and giving us much help ## License This project is licensed under the [GNU Affero General Public License v3.0](../LICENSE). + + +## Why rewrite C# to python +* The vscode extensions for C# development is become more and more hard to use, and microsoft abandoned the open-source OmniSharp project, replacing it with ites own closed source language server. +* The c# project seems hard to run by users, it require a lot of deploy knowledge and hard for collaborations, for the future of the gamespy emulator, I choose to rewrite this into a opensource and easy high level language - python. + +## Setup instructions + +1. Install postgresql and redis +2. Python version > 3.10 +3. export UNISPY_CONFIG= \ No newline at end of file diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs deleted file mode 100755 index cb1f38762..000000000 --- a/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelHandlerBase.cs +++ /dev/null @@ -1,101 +0,0 @@ -using UniSpy.Server.Chat.Error.IRC.Channel; -using UniSpy.Server.Chat.Error.IRC.General; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate.Redis.Contract; -using System; -using UniSpy.Server.Chat.Aggregate.Redis; - -namespace UniSpy.Server.Chat.Abstraction.BaseClass -{ - public abstract class ChannelHandlerBase : LogedInHandlerBase - { - /// - /// The matched channel of chat request - /// - protected Channel _channel; - /// - /// The channel user of current IClient - /// - protected ChannelUser _user; - private new ChannelRequestBase _request => (ChannelRequestBase)base._request; - public ChannelHandlerBase(IShareClient client, IRequest request) : base(client, request) { } - protected override void RequestCheck() - { - if (_request.RawRequest is not null) - { - base.RequestCheck(); - } - if (_channel is null) - { - _channel = _client.Info.GetLocalJoinedChannel(_request.ChannelName); - } - if (_channel is null) - { - throw new NoSuchChannelException($"No such channel {_request.ChannelName}", _request.ChannelName); - } - if (_user is null) - { - _user = _channel.GetUser(_client); - } - if (_user is null) - { - throw new NoSuchNickException($"Can not find user with nickname: {_client.Info.NickName} username: {_client.Info.UserName}"); - } - } - - public override void Handle() - { - base.Handle(); - try - { - // we do not publish message when the message is received from remote client - if (_client.IsRemoteClient) - { - return; - } - if (_channel is null) - { - return; - } - if (_request.RawRequest is null) - { - return; - } - PublishMessage(); - UpdateChannelCache(); - } - catch (Exception ex) - { - HandleException(ex); - } - } - /// - /// publish message to redis channel, only localclient can publish message - /// - protected virtual void PublishMessage() - { - - var msg = new RemoteMessage(_request, _client.GetRemoteClient()); - _channel.Broker.PublishMessage(msg); - } - - protected virtual void UpdateChannelCache() - { - var key = new ChannelCache - { - ChannelName = _channel.Name, - GameName = _channel.GameName - }; - - using (var locker = new LinqToRedis.RedisLock(TimeSpan.FromSeconds(10), Application.StorageOperation.Persistance.ChannelCacheClient.Db, key)) - { - if (locker.LockTake()) - { - Aggregate.Channel.UpdateChannelCache(_user, _channel); - } - } - } - } -} diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelRequestBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelRequestBase.cs deleted file mode 100755 index 97a561766..000000000 --- a/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelRequestBase.cs +++ /dev/null @@ -1,20 +0,0 @@ - -namespace UniSpy.Server.Chat.Abstraction.BaseClass -{ - public class ChannelRequestBase : RequestBase - { - public string ChannelName { get; set; } - public ChannelRequestBase() { } - public ChannelRequestBase(string rawRequest) : base(rawRequest) { } - public override void Parse() - { - base.Parse(); - - if (_cmdParams is null || _cmdParams?.Count < 1) - { - throw new Chat.Exception("Channel name is missing."); - } - ChannelName = _cmdParams[0]; - } - } -} diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelResponseBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelResponseBase.cs deleted file mode 100755 index 05436bc31..000000000 --- a/src/Servers/Chat/src/Abstraction/BaseClass/Channel/ChannelResponseBase.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace UniSpy.Server.Chat.Abstraction.BaseClass -{ - public abstract class ChannelResponseBase : ResponseBase - { - protected new ChannelRequestBase _request => (ChannelRequestBase)base._request; - protected ChannelResponseBase(RequestBase request, ResultBase result) : base(request, result){ } - } -} diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/CmdHandlerBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/CmdHandlerBase.cs deleted file mode 100755 index ecb5b2720..000000000 --- a/src/Servers/Chat/src/Abstraction/BaseClass/CmdHandlerBase.cs +++ /dev/null @@ -1,42 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate.Redis.Contract; -using UniSpy.Server.Chat.Error.IRC.General; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Abstraction.BaseClass -{ - /// - /// error code condition is complicated - /// there are 2 types error code - /// 1.irc numeric error code - /// 2.self defined code - /// we do not want to send self defined code, so if we find errorCode < noerror - /// we just return. - /// if error code bigger than noerror we need to process it in ConstructResponse() - ///we also need to check the error code != noerror in ConstructResponse() - /// - public abstract class CmdHandlerBase : UniSpy.Server.Core.Abstraction.BaseClass.CmdHandlerBase - { - protected new IShareClient _client => (IShareClient)base._client; - protected new RequestBase _request => (RequestBase)base._request; - protected new ResultBase _result { get => (ResultBase)base._result; set => base._result = value; } - protected new ResponseBase _response { get => (ResponseBase)base._response; set => base._response = value; } - public CmdHandlerBase(IShareClient client, IRequest request) : base(client, request) { } - protected override void HandleException(System.Exception ex) - { - if (ex is IRCException) - { - _client.Send(((IRCException)ex)); - } - base.HandleException(ex); - } - protected override void Response() - { - if (_client.Info.IsQuietMode) - { - return; - } - base.Response(); - } - } -} diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/General/LogedInHandlerBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/General/LogedInHandlerBase.cs deleted file mode 100755 index 88bc1d67f..000000000 --- a/src/Servers/Chat/src/Abstraction/BaseClass/General/LogedInHandlerBase.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Abstraction.BaseClass -{ - public abstract class LogedInHandlerBase : CmdHandlerBase - { - public LogedInHandlerBase(IShareClient client, IRequest request) : base(client, request) { } - - protected override void RequestCheck() - { - if (!_client.Info.IsLoggedIn) - { - new Chat.Exception($"{_client.Info.NickName} Please login first!"); - } - base.RequestCheck(); - } - } -} diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageHandlerBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageHandlerBase.cs deleted file mode 100755 index 9b584d2d6..000000000 --- a/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageHandlerBase.cs +++ /dev/null @@ -1,85 +0,0 @@ -using System.Linq; -using UniSpy.Server.Chat.Abstraction.BaseClass.Message; -using UniSpy.Server.Chat.Error.IRC.General; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Chat.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Abstraction.BaseClass -{ - public abstract class MessageHandlerBase : ChannelHandlerBase - { - protected new MessageRequestBase _request => (MessageRequestBase)base._request; - protected new MessageResultBase _result { get => (MessageResultBase)base._result; set => base._result = value; } - protected ChannelUser _receiver; - public MessageHandlerBase(IShareClient client, IRequest request) : base(client, request) { } - - protected override void RequestCheck() - { - _request.Parse(); - switch (_request.Type) - { - case MessageType.ChannelMessage: - ChannelMessageRequestCheck(); - break; - case MessageType.UserMessage: - UserMessageRequestCheck(); - break; - default: - throw new Chat.Exception("Unknown chat message request type."); - } - } - protected virtual void ChannelMessageRequestCheck() => base.RequestCheck(); - protected virtual void UserMessageRequestCheck() - { - // todo check if we only allow user join one channel - // fist we find this user in our local client pool, beacuse nick name is unique, this search is safe - var client = ClientManager.GetClientByNickName(_request.NickName); - // we get a first channel in his joined list - _channel = client.Info.JoinedChannels.Values.First(); - // we find this user in this channel - _receiver = _channel.GetUser(_request.NickName); - if (_receiver is null) - { - throw new NoSuchNickException( - $"No nickname: {_request.NickName} found in channel: {_channel.Name}."); - } - } - protected override void DataOperation() - { - _result.UserIRCPrefix = _client.Info.IRCPrefix; - switch (_request.Type) - { - case MessageType.ChannelMessage: - _result.TargetName = _request.ChannelName; - ChannelMessageDataOpration(); - break; - case MessageType.UserMessage: - _result.TargetName = _request.NickName; - UserMessageDataOperation(); - break; - } - } - - protected virtual void ChannelMessageDataOpration() { } - protected virtual void UserMessageDataOperation() { } - protected override void Response() - { - // response can not be null! - switch (_request.Type) - { - case MessageType.ChannelMessage: - _channel.MultiCast(_user.Client, _response, true); - break; - case MessageType.UserMessage: - _receiver.Client.Send(_response); - break; - } - } - protected override void UpdateChannelCache() - { - // we do nothing here, when there is a channel message - } - } -} diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageRequestBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageRequestBase.cs deleted file mode 100644 index 7aa6ea71d..000000000 --- a/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageRequestBase.cs +++ /dev/null @@ -1,34 +0,0 @@ -namespace UniSpy.Server.Chat.Abstraction.BaseClass -{ - public enum MessageType - { - ChannelMessage, - UserMessage - } - - public abstract class MessageRequestBase : ChannelRequestBase - { - public MessageRequestBase(string rawRequest) : base(rawRequest){ } - public MessageType? Type { get; protected set; } - public string NickName { get; protected set; } - public string Message { get; protected set; } - - public override void Parse() - { - base.Parse(); - - if (ChannelName.Contains("#")) - { - Type = MessageType.ChannelMessage; - } - else - { - // todo check if there need user message - Type = MessageType.UserMessage; - ChannelName = null; - NickName = _cmdParams[0]; - } - Message = _longParam; - } - } -} diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageResultBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageResultBase.cs deleted file mode 100755 index e39e9434a..000000000 --- a/src/Servers/Chat/src/Abstraction/BaseClass/Message/MessageResultBase.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UniSpy.Server.Chat.Abstraction.BaseClass.Message -{ - public abstract class MessageResultBase : ResultBase - { - public string UserIRCPrefix { get; set; } - public string TargetName { get; set; } - protected MessageResultBase(){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/RequestBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/RequestBase.cs deleted file mode 100755 index 6402a933c..000000000 --- a/src/Servers/Chat/src/Abstraction/BaseClass/RequestBase.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Newtonsoft.Json; - -namespace UniSpy.Server.Chat.Abstraction.BaseClass -{ - public class RequestBase : UniSpy.Server.Core.Abstraction.BaseClass.RequestBase - { - /// - /// True means there are no errors - /// False means there are errors - /// - [JsonProperty] - public new string RawRequest { get => (string)base.RawRequest; set => base.RawRequest = value; } - [JsonProperty] - public new string CommandName { get => (string)base.CommandName; protected set => base.CommandName = value; } - [JsonProperty] - protected string _prefix; - [JsonProperty] - protected List _cmdParams; - [JsonProperty] - protected string _longParam; - public RequestBase() { } - /// - /// create instance for Handler - /// - /// - public RequestBase(string rawRequest) : base(rawRequest) { } - - public override void Parse() - { - // at most 2 colon character - // we do not sure about all command - // so i block this code here - RawRequest = RawRequest.Replace("\r", "").Replace("\n", ""); - List dataFrag = new List(); - - if (RawRequest.Where(r => r.Equals(':')).Count() > 2) - { - throw new Chat.Exception($"IRC request is invalid {RawRequest}"); - } - - int indexOfColon = RawRequest.IndexOf(':'); - - string rawRequest = RawRequest; - if (indexOfColon == 0 && indexOfColon != -1) - { - int prefixIndex = rawRequest.IndexOf(' '); - _prefix = rawRequest.Substring(indexOfColon, prefixIndex); - rawRequest = rawRequest.Substring(prefixIndex); - } - - indexOfColon = rawRequest.IndexOf(':'); - if (indexOfColon != 0 && indexOfColon != -1) - { - _longParam = rawRequest.Substring(indexOfColon + 1); - //reset the request string - rawRequest = rawRequest.Remove(indexOfColon); - } - - dataFrag = rawRequest.Trim(' ').Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList(); - - CommandName = dataFrag[0]; - - if (dataFrag.Count > 1) - { - _cmdParams = dataFrag.Skip(1).ToList(); - } - } - - public static string GetCommandName(string request) - { - // at most 2 colon character - // we do not sure about all command - // so i block this code here - List dataFrag = new List(); - - if (request.Where(r => r.Equals(':')).Count() > 2) - { - return null; - } - - int indexOfColon = request.IndexOf(':'); - - string rawRequest = request; - if (indexOfColon == 0 && indexOfColon != -1) - { - int prefixIndex = rawRequest.IndexOf(' '); - var prefix = rawRequest.Substring(indexOfColon, prefixIndex); - rawRequest = rawRequest.Substring(prefixIndex); - } - - indexOfColon = rawRequest.IndexOf(':'); - if (indexOfColon != 0 && indexOfColon != -1) - { - var longParam = rawRequest.Substring(indexOfColon + 1); - //reset the request string - rawRequest = rawRequest.Remove(indexOfColon); - } - - dataFrag = rawRequest.Trim(' ').Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList(); - - return dataFrag[0]; - } - } -} diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/ResponseBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/ResponseBase.cs deleted file mode 100755 index 7fa64e9fe..000000000 --- a/src/Servers/Chat/src/Abstraction/BaseClass/ResponseBase.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace UniSpy.Server.Chat.Abstraction.BaseClass -{ - public abstract class ResponseBase : UniSpy.Server.Core.Abstraction.BaseClass.ResponseBase - { - public const string ServerDomain = "unispy.net"; - public new string SendingBuffer{ get => (string)base.SendingBuffer; - protected set => base.SendingBuffer = value; } - protected new ResultBase _result => (ResultBase)base._result; - protected new RequestBase _request => (RequestBase)base._request; - public ResponseBase(RequestBase request, ResultBase result) : base(request, result){ } - } -} diff --git a/src/Servers/Chat/src/Abstraction/BaseClass/ResultBase.cs b/src/Servers/Chat/src/Abstraction/BaseClass/ResultBase.cs deleted file mode 100755 index 20652bb87..000000000 --- a/src/Servers/Chat/src/Abstraction/BaseClass/ResultBase.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace UniSpy.Server.Chat.Abstraction.BaseClass -{ - public abstract class ResultBase : UniSpy.Server.Core.Abstraction.BaseClass.ResultBase - { - public ResultBase(){ } - } -} diff --git a/src/Servers/Chat/src/Abstraction/Interface/IChannel.cs b/src/Servers/Chat/src/Abstraction/Interface/IChannel.cs deleted file mode 100644 index c61e5f6c4..000000000 --- a/src/Servers/Chat/src/Abstraction/Interface/IChannel.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Abstraction.Interface -{ - public interface IChannel - { - void MultiCast(IClient sender, IResponse message, bool isSkipSender); - // void MultiCastExceptSender(ChannelUser sender, IResponse message); - string GetAllUsersNickString(); - void AddBindOnUserAndChannel(ChannelUser joiner); - void RemoveBindOnUserAndChannel(ChannelUser leaver); - ChannelUser GetChannelUser(IClient client); - bool IsUserBanned(ChannelUser user); - void SetProperties(ChannelUser changer, ModeRequest request); - void SetChannelKeyValue(Dictionary keyValue); - string GetChannelValueString(List keys); - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Abstraction/Interface/IShareClient.cs b/src/Servers/Chat/src/Abstraction/Interface/IShareClient.cs deleted file mode 100644 index 05ad506cb..000000000 --- a/src/Servers/Chat/src/Abstraction/Interface/IShareClient.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Abstraction.Interface -{ - public interface IShareClient : IClient, ITestClient - { - public new ClientInfo Info { get; } - public bool IsRemoteClient { get; } - public RemoteClient GetRemoteClient(); - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Abstraction/Interface/IStorageOperation.cs b/src/Servers/Chat/src/Abstraction/Interface/IStorageOperation.cs deleted file mode 100644 index 38662b441..000000000 --- a/src/Servers/Chat/src/Abstraction/Interface/IStorageOperation.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Chat.Aggregate.Redis; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Core.Database.DatabaseModel; - -namespace UniSpy.Server.Chat.Abstraction.Interface -{ - public interface IStorageOperation - { - public Dictionary> PeerGroupList { get; } - public ChannelCache.RedisClient ChannelCacheClient { get; } - public ClientInfoCache.RedisClient ClientCacheClient { get; } - (int userId, int profileId, bool emailVerified, bool banned) NickAndEmailLogin(string nickName, string email, string passwordHash); - (int userId, int profileId, bool emailVerified, bool banned) UniqueNickLogin(string uniqueNick, int namespaceId); - bool IsChannelExist(ChannelCache key); - Channel GetChannel(ChannelCache key); - void UpdateChannel(Channel channel); - void RemoveChannel(Channel channel); - void UpdateClient(IShareClient client); - void RemoveClient(IShareClient client); - bool IsClientExist(ClientInfoCache key); - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/ChannelManager.cs b/src/Servers/Chat/src/Aggregate/ChannelManager.cs deleted file mode 100644 index 3323c381d..000000000 --- a/src/Servers/Chat/src/Aggregate/ChannelManager.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Concurrent; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate.Redis; - -namespace UniSpy.Server.Chat.Aggregate -{ - public static class LocalChannelManager - { - - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/ChannelMode.cs b/src/Servers/Chat/src/Aggregate/ChannelMode.cs deleted file mode 100755 index 7658769e2..000000000 --- a/src/Servers/Chat/src/Aggregate/ChannelMode.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System.Collections.Generic; -using System.Text; -using Newtonsoft.Json; -using UniSpy.Server.Chat.Contract.Request.Channel; - -namespace UniSpy.Server.Chat.Aggregate -{ - public sealed class ChannelMode - { - //i - toggle the invite-only channel flag; - [JsonProperty] - public bool IsInviteOnly { get; private set; } = false; - //p - toggle the private channel flag; - [JsonProperty] - public bool IsPrivateChannel { get; private set; } = false; - //s - toggle the secret channel flag; - [JsonProperty] - public bool IsSecretChannel { get; private set; } = false; - //m - toggle the moderated channel; - [JsonProperty] - public bool IsModeratedChannel { get; private set; } = false; - //n - toggle the no messages to channel from clients on the outside; - [JsonProperty] - public bool IsAllowExternalMessage { get; private set; } = false; - //t - toggle the topic settable by channel operator only flag; - [JsonProperty] - public bool IsTopicOnlySetByChannelOperator { get; private set; } = false; - // e - toggle the operator allow channel limits flag; - [JsonProperty] - public bool IsOperatorAbeyChannelLimits { get; private set; } = true; - public List InviteNickNames { get; private set; } = new List(); - /// - /// default constructor - /// - public ChannelMode() - { - } - - public void SetChannelModes(ModeOperationType operation) - { - switch (operation) - { - case ModeOperationType.SetOperatorAbeyChannelLimits: - IsOperatorAbeyChannelLimits = true; - break; - case ModeOperationType.RemoveOperatorAbeyChannelLimits: - IsOperatorAbeyChannelLimits = false; - break; - case ModeOperationType.SetInvitedOnly: - IsInviteOnly = true; - break; - case ModeOperationType.RemoveInvitedOnly: - IsInviteOnly = false; - break; - case ModeOperationType.SetPrivateChannelFlag: - IsPrivateChannel = true; - break; - case ModeOperationType.RemovePrivateChannelFlag: - IsPrivateChannel = false; - break; - case ModeOperationType.SetSecretChannelFlag: - IsSecretChannel = true; - break; - case ModeOperationType.RemoveSecretChannelFlag: - IsSecretChannel = false; - break; - case ModeOperationType.SetModeratedChannelFlag: - IsModeratedChannel = true; - break; - case ModeOperationType.RemoveModeratedChannelFlag: - IsModeratedChannel = false; - break; - case ModeOperationType.EnableExternalMessagesFlag: - IsAllowExternalMessage = true; - break; - case ModeOperationType.DisableExternalMessagesFlag: - IsAllowExternalMessage = false; - break; - case ModeOperationType.SetTopicChangeByOperatorFlag: - IsTopicOnlySetByChannelOperator = true; - break; - case ModeOperationType.RemoveTopicChangeByOperatorFlag: - IsTopicOnlySetByChannelOperator = false; - break; - } - } - - public override string ToString() - { - - var buffer = new StringBuilder(); - - buffer.Append("+"); - if (IsInviteOnly) - { - buffer.Append("i"); - } - if (IsPrivateChannel) - { - buffer.Append("p"); - } - if (IsSecretChannel) - { - buffer.Append("s"); - } - if (IsModeratedChannel) - { - buffer.Append("m"); - } - if (IsAllowExternalMessage) - { - buffer.Append("n"); - } - if (IsTopicOnlySetByChannelOperator) - { - buffer.Append("t"); - } - - //response is like +nt - return buffer.ToString(); - } - } -} diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelGeneral.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelGeneral.cs deleted file mode 100755 index 7436749e2..000000000 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelGeneral.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System; -using System.Linq; -using Newtonsoft.Json; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate.Redis; - -namespace UniSpy.Server.Chat.Aggregate -{ - public enum PeerRoomType - { - /// - /// The main room for a game. - /// - Title, - /// - /// A room where players meet before starting a game. - /// - Staging, - /// - /// A room which is, in general, for a particular type of gameplay (team, dm, etc.). - /// - Group, - /// - /// The normal room - /// - Normal - } - public sealed partial class Channel - { - public Guid ServerId { get; private set; } - public string GameName { get; private set; } - /// - /// Channel name - /// - public string Name { get; private set; } - /// - /// The maximum number of users that can be in the channel - /// - public int MaxNumberUser { get; private set; } = 200; - public DateTime CreateTime { get; private set; } = DateTime.Now; - /// - /// Channel key values - /// - public KeyValueManager KeyValues { get; private set; } = new KeyValueManager(); - public PeerRoomType? RoomType { get; private set; } - public string Password { get; private set; } - public string Topic { get; set; } - /// - /// Join handler creates Broker and stored on local - /// - [JsonIgnore] - public ChannelMessageBroker Broker - { - get - { - if (_broker is null) - { - MessageBrokers.TryGetValue(Name, out _broker); - } - return _broker; - } - } - [JsonIgnore] - private ChannelMessageBroker _broker; - public int? GroupId { get; private set; } - public string RoomName { get; private set; } - public bool IsValidPeerRoom => GroupId is not null && RoomName is not null; - public string PreviousJoinedChannel { get; private set; } - public Channel() { } - public Channel(string name, IShareClient client, string password = null) - { - ServerId = client.Server.Id; - Name = name; - Password = password; - RoomType = PeerRoom.GetRoomType(Name); - GameName = client.Info.GameName; - PreviousJoinedChannel = client.Info.PreviousJoinedChannel; - switch (RoomType) - { - case PeerRoomType.Group: - GetGroupId(); - GetPeerRoomName(); - break; - case PeerRoomType.Staging: - GetStagingRoomName(); - break; - case PeerRoomType.Title: - GetTitileRoomName(); - break; - } - } - private void GetGroupId() - { - var groupIdStr = Name.Split("!", StringSplitOptions.RemoveEmptyEntries)[1]; - if (!int.TryParse(groupIdStr, out var groupId)) - { - throw new Chat.Exception("Peer room group id is incorrect"); - } - GroupId = groupId; - } - private void GetPeerRoomName() - { - if (Chat.Application.StorageOperation.Persistance.PeerGroupList.ContainsKey(GameName)) - { - var grouplist = Chat.Application.StorageOperation.Persistance.PeerGroupList[GameName]; - var room = grouplist.Where(g => g.Groupid == GroupId).FirstOrDefault(); - if (room is null) - { - throw new Chat.Exception($"Invalid peer room: {Name}"); - } - RoomName = room.Roomname; - } - } - private void GetStagingRoomName() - { - RoomName = Name.Split('!', StringSplitOptions.RemoveEmptyEntries).Last(); - } - private void GetTitileRoomName() => GetStagingRoomName(); - } -} diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelManage.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelManage.cs deleted file mode 100644 index 96facec65..000000000 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelManage.cs +++ /dev/null @@ -1,83 +0,0 @@ -using System; -using System.Collections.Concurrent; -using Newtonsoft.Json; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate.Redis; - -namespace UniSpy.Server.Chat.Aggregate -{ - /// - /// The code manage local and remote channel - /// - public sealed partial class Channel - { - /// - /// The local channel manager - /// - [JsonIgnore] - public static readonly ConcurrentDictionary LocalChannels = new(); - [JsonIgnore] - public static readonly ConcurrentDictionary MessageBrokers = new(); - /// - /// You need to manually check channel existance then get channel - /// - public static Channel GetLocalChannel(string name) - { - LocalChannels.TryGetValue(name, out var channel); - return channel; - } - public static void RemoveLocalChannel(Channel channel) - { - LocalChannels.TryRemove(channel.Name, out var chan); - RemoveMessageBrocker(channel); - } - public static Channel CreateLocalChannel(string name, IShareClient creator = null, string password = null) - { - var channel = new Channel(name, creator, password); - LocalChannels.TryAdd(name, channel); - AddMessageBrocker(channel); - return channel; - } - public static void UpdateChannelCache(ChannelUser user, Channel channel) - { - if (user.Client.IsRemoteClient) - { - return; - } - Application.StorageOperation.Persistance.UpdateChannel(channel); - } - public static Channel GetChannelCache(ChannelCache key) - { - return Application.StorageOperation.Persistance.GetChannel(key); - } - public static void RemoveChannelCache(ChannelUser user, Channel channel) - { - if (user.Client.IsRemoteClient) - { - return; - } - StackExchange.Redis.RedisValue token = Environment.MachineName; - if (Application.StorageOperation.Persistance.ChannelCacheClient.Db.LockTake(channel.Name, token, TimeSpan.FromSeconds(10))) - { - Application.StorageOperation.Persistance.RemoveChannel(channel); - } - } - - public static ChannelMessageBroker AddMessageBrocker(Channel channel) - { - ChannelMessageBroker broker; - if (!MessageBrokers.TryGetValue(channel.Name, out broker)) - { - broker = new ChannelMessageBroker(channel.Name); - broker.Subscribe(); - MessageBrokers.TryAdd(channel.Name, broker); - } - return broker; - } - public static void RemoveMessageBrocker(Channel channel) - { - MessageBrokers.TryRemove(channel.Name, out var broker); - broker.Dispose(); - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelModeRelated.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelModeRelated.cs deleted file mode 100644 index 19ef95dd4..000000000 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelModeRelated.cs +++ /dev/null @@ -1,62 +0,0 @@ -using Newtonsoft.Json; -using UniSpy.Server.Chat.Contract.Request.Channel; - -namespace UniSpy.Server.Chat.Aggregate -{ - public partial class Channel - { - [JsonProperty] - public ChannelMode Mode { get; private set; } = new ChannelMode(); - - /// - /// We only care about how to set mode in this channel - /// we do not need to care about if the user is legal - /// because MODEHandler will check for us - /// - /// - /// - public void SetProperties(ChannelUser changer, ModeRequest request) - { - // todo check permission of each operation - foreach (var op in request.ModeOperations) - { - switch (op) - { - case ModeOperationType.AddChannelUserLimits: - MaxNumberUser = request.LimitNumber; - break; - case ModeOperationType.RemoveChannelUserLimits: - MaxNumberUser = 200; - break; - case ModeOperationType.AddBanOnUser: - BanUser(request); - break; - case ModeOperationType.RemoveBanOnUser: - UnBanUser(request); - break; - case ModeOperationType.AddChannelPassword: - Password = request.Password; - break; - case ModeOperationType.RemoveChannelPassword: - Password = null; - break; - case ModeOperationType.AddChannelOperator: - AddChannelOperator(request); - break; - case ModeOperationType.RemoveChannelOperator: - RemoveChannelOperator(request); - break; - case ModeOperationType.EnableUserVoicePermission: - EnableUserVoicePermission(request); - break; - case ModeOperationType.DisableUserVoicePermission: - DisableUserVoicePermission(request); - break; - default: - Mode.SetChannelModes(op); - break; - } - } - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs deleted file mode 100644 index bafb2df96..000000000 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelUserRelated.cs +++ /dev/null @@ -1,223 +0,0 @@ -using Newtonsoft.Json; -using System.Collections.Concurrent; -using System.Linq; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Error.IRC.General; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Aggregate -{ - public partial class Channel - { - /// - /// | key -> Nickname | value -> ChannelUser| - /// - [JsonProperty] - public ConcurrentDictionary BanList { get; private set; } = new(); - /// - /// | key -> Nickname | value -> ChannelUser| - /// - [JsonProperty] - public ConcurrentDictionary Users { get; private set; } = new(); - [JsonProperty] - public string _creatorNickName { get; private set; } - [JsonIgnore] - public ChannelUser Creator - { - get - { - if (Users.Values.Where(u => u.Client.Info.NickName == _creatorNickName).Count() == 1) - { - return Users[_creatorNickName]; - } - else - { - return null; - } - } - } - private void BanUser(ModeRequest request) - { - var result = Users.Values.Where(u => u.Client.Info.NickName == request.NickName); - if (result.Count() != 1) - { - return; - } - ChannelUser user = result.First(); - - if (BanList.Values.Where(u => u.Client.Info.NickName == request.NickName).Count() == 1) - { - return; - } - - BanList.TryAdd(user.Client.Info.NickName, user); - } - private void UnBanUser(ModeRequest request) - { - var result = BanList.Where(u => u.Value.Client.Info.NickName == request.NickName); - if (result.Count() == 1) - { - var keyValue = result.First(); - BanList.TryRemove(keyValue); - return; - } - if (result.Count() > 1) - { - throw new ErrOneUSNickNameException("Multiple user with same nick name in channel {Name}"); - } - } - - private void AddChannelOperator(ModeRequest request) - { - //check whether this user is in this channel - var result = Users.Where(u => u.Value.Client.Info.UserName == request.UserName); - if (result.Count() != 1) - { - return; - } - var kv = result.First(); - - //if this user is already in operator we do not add it - if (kv.Value.IsChannelOperator) - { - return; - } - kv.Value.IsChannelOperator = true; - } - - private void RemoveChannelOperator(ModeRequest request) - { - var result = Users.Where(u => u.Value.Client.Info.UserName == request.UserName); - if (result.Count() != 1) - { - return; - } - var keyValue = result.First(); - - if (keyValue.Value.IsChannelCreator) - { - keyValue.Value.IsChannelCreator = false; - } - } - - private void EnableUserVoicePermission(ModeRequest request) - { - var result = Users.Where(u => u.Value.Client.Info.UserName == request.UserName); - if (result.Count() != 1) - { - return; - } - - var kv = result.First(); - - if (kv.Value.IsVoiceable) - { - kv.Value.IsVoiceable = true; - } - - } - private void DisableUserVoicePermission(ModeRequest request) - { - var result = Users.Where(u => u.Value.Client.Info.UserName == request.UserName); - if (result.Count() != 1) - { - return; - } - - var kv = result.First(); - if (kv.Value.IsVoiceable) - { - kv.Value.IsVoiceable = false; - } - } - public ChannelUser GetUser(string nickName) => Users.ContainsKey(nickName) == true ? Users[nickName] : null; - public ChannelUser GetUser(IShareClient client) => Users.Values.FirstOrDefault(u => u.Client.Connection.RemoteIPEndPoint == client.Connection.RemoteIPEndPoint); - public ChannelUser AddUser(IShareClient client, string password = null, bool isChannelCreator = false, bool isChannelOperator = false) - { - Validation(client, password); - var user = new ChannelUser(client, this); - switch (RoomType) - { - case PeerRoomType.Normal: - case PeerRoomType.Staging: - // user created room - user.IsChannelCreator = isChannelCreator; - user.IsChannelOperator = isChannelOperator; - break; - } - AddBindOnUserAndChannel(user); - return user; - } - - public void RemoveUser(ChannelUser user) - { - user.Client.Info.PreviousJoinedChannel = Name; - RemoveBindOnUserAndChannel(user); - } - - public bool IsUserExisted(ChannelUser user) => IsUserExisted(user.Client); - public bool IsUserExisted(IShareClient client) => Users.ContainsKey(client.Info.NickName); - public bool IsUserBanned(ChannelUser user) => IsUserBanned(user.Client); - private bool IsUserBanned(IShareClient client) - { - if (!BanList.ContainsKey(client.Info.NickName)) - { - return false; - } - if (BanList[client.Info.NickName].Client.Connection.RemoteIPEndPoint != client.Connection.RemoteIPEndPoint) - { - return false; - } - return true; - } - public string GetAllUsersNickString() - { - string nicks = ""; - foreach (var user in Users.Values) - { - if (user.IsChannelCreator) - { - nicks += $"@{user.Client.Info.NickName}"; - } - else - { - nicks += user.Client.Info.NickName; - } - - if (!user.Equals(Users.Values.Last())) - { - nicks += " "; - } - } - return nicks; - } - public static void AddBindOnUserAndChannel(ChannelUser joiner) - { - joiner.Channel.Users.TryAdd(joiner.Client.Info.NickName, joiner); - joiner.Client.Info.JoinedChannels.TryAdd(joiner.Channel.Name, joiner.Channel); - } - public static void RemoveBindOnUserAndChannel(ChannelUser leaver) - { - leaver.Channel.Users.TryRemove(leaver.Client.Info.NickName, out _); - leaver.Client.Info.JoinedChannels.Remove(leaver.Channel.Name, out _); - } - /// - /// Send message to all users in this channel - /// except the sender - /// - public void MultiCast(IClient sender, IResponse message, bool isSkipSender = false) - { - var users = Users.Values.Where(u => !u.IsRemoteClient).ToList(); - foreach (var user in users) - { - if (isSkipSender - && sender.Connection.RemoteIPEndPoint.Equals(user.RemoteIPEndPoint)) - { - continue; - } - user.Client.Send(message); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelValidation.cs b/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelValidation.cs deleted file mode 100644 index 288dc8abe..000000000 --- a/src/Servers/Chat/src/Aggregate/ChannelProperty/ChannelValidation.cs +++ /dev/null @@ -1,62 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Error.IRC.Channel; - -namespace UniSpy.Server.Chat.Aggregate -{ - public partial class Channel - { - public void VerifyPassword(string pass) - { - if (Password != pass) - { - throw new Chat.Exception("Password is not correct"); - } - } - private void Validation(IShareClient client, string password) - { - if (Mode.IsInviteOnly) - { - //invited only - throw new InviteOnlyChanException("This is an invited only channel.", Name); - } - if (IsUserBanned(client)) - { - throw new BannedFromChanException($"You are banned from this channel:{Name}.", Name); - } - if (IsUserExisted(client)) - { - throw new Chat.Exception($"{client.Info.NickName} is already in channel {Name}"); - } - if (client.Info.IsJoinedChannel(Name)) - { - // we do not send anything to this user and users in this channel - throw new Chat.Exception($"User: {client.Info.NickName} is already joined the channel: {Name}"); - } - if (Password is not null) - { - if (password is null) - { - throw new Chat.Exception("You must input password to join this channel."); - } - if (Password != password) - { - throw new Chat.Exception("Password is not correct"); - } - } - - if (Mode.IsInviteOnly) - { - if (!Mode.InviteNickNames.Contains(client.Info.NickName)) - { - throw new InviteOnlyChanException("You must invited to this channel", Name); - } - else - { - // user is already join, we remove him from list - Mode.InviteNickNames.Remove(client.Info.NickName); - } - } - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/ChannelUser.cs b/src/Servers/Chat/src/Aggregate/ChannelUser.cs deleted file mode 100755 index 008cc7b2c..000000000 --- a/src/Servers/Chat/src/Aggregate/ChannelUser.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using System.Net; -using System.Text; -using Newtonsoft.Json; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Misc; - -namespace UniSpy.Server.Chat.Aggregate -{ - public sealed class ChannelUser - { - [JsonProperty] - public Guid? ServerId { get; private set; } - /// - /// Indicate whether this client is shared from redis channel - /// - public bool IsVoiceable { get; set; } = true; - public bool IsChannelCreator { get; set; } - public bool IsChannelOperator { get; set; } - public bool IsRemoteClient => Client.IsRemoteClient; - /// - /// The remote ip end point of this user - /// - [JsonProperty] - [JsonConverter(typeof(IPEndPointConverter))] - public IPEndPoint RemoteIPEndPoint { get; private set; } - /// - /// The client reference - /// - [JsonIgnore] - public IShareClient Client - { - get - { - if (_client is not null) - { - return _client; - } - else - { - var client = ClientManager.GetClient(RemoteIPEndPoint); - if (client is null) - { - throw new UniSpy.Exception("the client is not on local server, please check logic"); - } - return (IShareClient)client; - } - } - } - [JsonIgnore] - private IShareClient _client; - /// - /// The user key values storage - /// - public KeyValueManager KeyValues { get; private set; } = new KeyValueManager(); - /// - /// The channel where user current in. - /// - [JsonIgnore] - public Channel Channel { get; private set; } - [JsonIgnore] - public string Modes - { - get - { - var buffer = new StringBuilder(); - - if (IsChannelOperator) - { - buffer.Append("@"); - } - - if (IsVoiceable) - { - buffer.Append("+"); - } - - return buffer.ToString(); - } - } - public ChannelUser() { } - public ChannelUser(IShareClient client, Channel channel) - { - _client = client; - Channel = channel; - ServerId = client.Server.Id; - RemoteIPEndPoint = client.Connection.RemoteIPEndPoint; - } - } -} diff --git a/src/Servers/Chat/src/Aggregate/KeyValueManager.cs b/src/Servers/Chat/src/Aggregate/KeyValueManager.cs deleted file mode 100644 index daf71e313..000000000 --- a/src/Servers/Chat/src/Aggregate/KeyValueManager.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System.Collections.Concurrent; -using System.Collections.Generic; - - -namespace UniSpy.Server.Chat.Aggregate -{ - public class KeyValueManager - { - public ConcurrentDictionary Dict { get; private set; } = new ConcurrentDictionary(); - - public KeyValueManager() - { - } - - public void Update(Dictionary data) - { - // TODO check if all key is send through the request or - // TODO only updated key send through the request - foreach (var key in data.Keys) - { - //we update the key value - Dict[key] = data[key]; - } - } - public void Update(KeyValuePair data) - { - Dict[data.Key] = data.Value; - } - public static string BuildKeyValueString(Dictionary keyValues) - { - string flags = ""; - foreach (var kv in keyValues) - { - flags += $@"\{kv.Key}\{kv.Value}"; - } - return flags; - } - public string GetValueString(List keys) - { - string values = ""; - foreach (var key in keys) - { - if (Dict.ContainsKey(key)) - { - values += @"\" + Dict[key]; - } - else - { - values += @"\"; - // throw new Chat.Exception($"Can not find key: {key}"); - } - } - return values; - } - - public bool IsContainAllKey(List keys) - { - foreach (var key in keys) - { - if (!Dict.ContainsKey(key)) - { - return false; - } - } - return true; - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/Misc/ChatConstants.cs b/src/Servers/Chat/src/Aggregate/Misc/ChatConstants.cs deleted file mode 100755 index 760e2f41e..000000000 --- a/src/Servers/Chat/src/Aggregate/Misc/ChatConstants.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace UniSpy.Server.Chat.Aggregate.Misc -{ - public sealed class ChatConstants - { - //we hard coded random key here for simplisity - } -} diff --git a/src/Servers/Chat/src/Aggregate/Misc/ChatCrypt.cs b/src/Servers/Chat/src/Aggregate/Misc/ChatCrypt.cs deleted file mode 100755 index 5c2581291..000000000 --- a/src/Servers/Chat/src/Aggregate/Misc/ChatCrypt.cs +++ /dev/null @@ -1,125 +0,0 @@ -using System.Collections.Generic; -using Newtonsoft.Json; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; - -namespace UniSpy.Server.Chat.Aggregate.Misc -{ - /// - /// This class is used to encrypt and decrypt the data for Chat. - /// - /// Note: this is a C-sharp version of the peerchat algorithm created by aluigi. - /// Original C implementation: http://aluigi.altervista.org/papers/gs_peerchat.h - /// - public sealed class ChatCrypt : ICryptography - { - public const string DigitsHex = "0123456789abcdef"; - public const string DigitsCrypt = "aFl4uOD9sfWq1vGp"; - public const string NewDigitsCrypt = "qJ1h4N9cP3lzD0Ka"; - public const uint IPXorMask = 0xc3801dc7; - public const string ClientKey = "0000000000000000"; - public const string ServerKey = "0000000000000000"; - public static byte[] Handle(PeerChatCTX ctx, byte[] data) - { - byte num1 = ctx.Buffer1; - byte num2 = ctx.Buffer2; - byte t; - int datapos = 0; - List buffer = new List(); - long size = data.Length; - - while (size-- > 0) - { - num1 = (byte)((num1 + 1) % 256); - num2 = (byte)((ctx.SBox[num1] + num2) % 256); - t = ctx.SBox[num1]; - ctx.SBox[num1] = ctx.SBox[num2]; - ctx.SBox[num2] = t; - t = (byte)((ctx.SBox[num2] + ctx.SBox[num1]) % 256); - byte temp = (byte)(data[datapos++] ^ ctx.SBox[t]); - buffer.Add(temp); - } - - ctx.Buffer1 = num1; - ctx.Buffer2 = num2; - return buffer.ToArray(); - } - - /// - /// Prepare the key - /// - /// - /// - /// - public static void Init(PeerChatCTX ctx, string challengeKey, string secretKey) - { - byte[] challengeBytes = UniSpyEncoding.GetBytes(challengeKey); - byte[] secretKeyBytes = UniSpyEncoding.GetBytes(secretKey); - - ctx.Buffer1 = 0; - ctx.Buffer2 = 0; - - int secretKeyIndex = 0; - for (int i = 0; i < challengeBytes.Length; i++, secretKeyIndex++) - { - if (secretKeyIndex >= secretKeyBytes.Length) - { - secretKeyIndex = 0; - } - - challengeBytes[i] ^= secretKeyBytes[secretKeyIndex]; - } - - byte index1 = 255; - - for (int i = 0; i < 256; i++, index1--) - { - ctx.SBox[i] = index1; - } - - index1 = 0; - - for (int i = 0, index2 = 0; i < ctx.SBox.Length; i++, index1++) - { - if (index1 >= challengeBytes.Length) - { - index1 = 0; - } - - index2 = (byte)((challengeBytes[index1] + ctx.SBox[i] + index2) % 256); - byte t = ctx.SBox[i]; - ctx.SBox[i] = ctx.SBox[index2]; - ctx.SBox[index2] = t; - } - } - - public static bool EncodeIP() - { - return false; - } - [JsonProperty] - public PeerChatCTX ClientCtx { get; private set; } - [JsonProperty] - public PeerChatCTX ServerCtx { get; private set; } - /// - /// using for json deserialization - /// - public ChatCrypt() { } - public ChatCrypt(string gameSecretKey) - { - ClientCtx = new PeerChatCTX(); - ServerCtx = new PeerChatCTX(); - Init(ClientCtx, ClientKey, gameSecretKey); - Init(ServerCtx, ServerKey, gameSecretKey); - } - public byte[] Encrypt(byte[] data) - { - return Handle(ServerCtx, data); - } - - public byte[] Decrypt(byte[] data) - { - return Handle(ClientCtx, data); - } - } -} diff --git a/src/Servers/Chat/src/Aggregate/Misc/IRCErrorCode.cs b/src/Servers/Chat/src/Aggregate/Misc/IRCErrorCode.cs deleted file mode 100755 index 2e8bad287..000000000 --- a/src/Servers/Chat/src/Aggregate/Misc/IRCErrorCode.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace UniSpy.Server.Chat.Aggregate.Misc -{ - public sealed class IRCErrorCode - { - //GameSpy standard irc error - public const string NoSuchNick = "401"; - public const string NoSuchChannel = "403"; - public const string TooManyChannels = "405"; - public const string ErrOneUSNickName = "432"; - public const string NickNameInUse = "433"; - public const string MoreParameters = "461"; - public const string ChannelIsFull = "471"; - public const string InviteOnlyChan = "473"; - public const string BannedFromChan = "474"; - public const string BadChannelKey = "475"; - public const string BadChanMask = "476"; - public const string LoginFailed = "708"; - public const string NoUniqueNick = "709"; - public const string UniqueNIickExpired = "710"; - public const string RegisterNickFailed = "711"; - } -} diff --git a/src/Servers/Chat/src/Aggregate/Misc/IRCReplyBuilder.cs b/src/Servers/Chat/src/Aggregate/Misc/IRCReplyBuilder.cs deleted file mode 100755 index 653250c46..000000000 --- a/src/Servers/Chat/src/Aggregate/Misc/IRCReplyBuilder.cs +++ /dev/null @@ -1,133 +0,0 @@ -// using System.Collections.Generic; -// using UniSpy.Server.Chat.Abstraction.BaseClass; - -// namespace UniSpy.Server.Chat.Aggregate.Misc -// { -// public sealed class IRCReplyBuilder -// { -// #region advance irc reply builder - -// public static string BuildChannelError(string ircError, string channelName, string message) -// { -// return Build(ircError, $"* {channelName}", message); -// } - -// public static string BuildBadChanMaskError(string channelName) -// { -// return BuildChannelError(IRCErrorCode.BadChanMask, channelName, "Bad channel mask."); -// } - -// public static string BuildBadChannelKeyError(string channelName) -// { -// return BuildChannelError(IRCErrorCode.BannedFromChan, channelName, "Wrong channel password."); -// } - -// public static string BuildBannedFromChannelError(string channelName) -// { -// return BuildChannelError(IRCErrorCode.BadChannelKey, channelName, "Banned from channel."); -// } - -// public static string BuildChannelIsFullError(string channelName) -// { -// return BuildChannelError(IRCErrorCode.ChannelIsFull, channelName, "Channel is full."); -// } - -// public static string BuildInvitedOnlyChannelError(string channelName) -// { -// return BuildChannelError(IRCErrorCode.InviteOnlyChan, channelName, "Invite only channel."); -// } -// public static string BuildNoSuchChannelError(string channelName) -// { -// return BuildChannelError(IRCErrorCode.NoSuchChannel, channelName, "There is no such channel."); -// } -// public static string BuildToManyChannelError(string channelName) -// { -// return BuildChannelError(IRCErrorCode.TooManyChannels, channelName, "You have joined to many channels."); -// } - -// public static string BuildNickNameInUseError(string oldNick, string newNick) -// { -// return Build(IRCErrorCode.NickNameInUse, $"{oldNick} {newNick} 0"); -// } - -// public static string BuildLoginFailedError() -// { -// return Build(IRCErrorCode.LoginFailed); -// } - -// public static string BuildNoUniqueNickError() -// { -// return Build(IRCErrorCode.NoUniqueNick); -// } - -// public static string BuildUniquenickExpireError() -// { -// return Build(IRCErrorCode.UniqueNIickExpired); -// } - -// public static string BuildRegisterNickFailedError(List nickNames) -// { -// string suggestNicks = ""; - -// foreach (var nick in nickNames) -// { -// suggestNicks += @"\" + nick; -// } -// return Build(IRCErrorCode.RegisterNickFailed, $"* numberOfSuggestNick {suggestNicks} 0"); -// } - -// public static string BuildErrOneUSNickNameError(string userPrefix) -// { -// return Build(userPrefix, IRCErrorCode.ErrOneUSNickName, null, null); -// } - -// public static string BuildNoSuchNickError() -// { -// return Build(IRCErrorCode.NoSuchNick); -// } -// #endregion - -// #region Basic IRC reply builder -// public static string Build(string command) -// { -// return Build(command, null); -// } - -// public static string Build(string cmd, string cmdParams) -// { -// return Build(cmd, cmdParams, null); -// } - -// public static string Build(string cmd, string cmdParams, string tailing) -// { -// return Build(ResponseBase.ServerDomain, cmd, cmdParams, tailing); -// } -// public static string Build(IRCErrorCode cmd, string cmdParams, string tailing) -// { -// return Build(cmd.ToString(), cmdParams, tailing); -// } -// public static string Build(string prefix, string cmd, string cmdParams, string tailing) -// { -// string buffer = ""; - -// if (prefix != "" || prefix is not null) -// { -// buffer = $":{prefix} "; -// } - -// buffer += $"{cmd} {cmdParams}"; - -// if (tailing == "" || tailing is null) -// { -// buffer += "\r\n"; -// } -// else -// { -// buffer += $" :{tailing}\r\n"; -// } - -// return buffer; -// } -// #endregion -// } -// } diff --git a/src/Servers/Chat/src/Aggregate/Misc/PeerChatCTX.cs b/src/Servers/Chat/src/Aggregate/Misc/PeerChatCTX.cs deleted file mode 100755 index 99b439ace..000000000 --- a/src/Servers/Chat/src/Aggregate/Misc/PeerChatCTX.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UniSpy.Server.Chat.Aggregate.Misc -{ - public sealed class PeerChatCTX - { - public byte Buffer1; - public byte Buffer2; - public byte[] SBox = new byte[256]; - } -} diff --git a/src/Servers/Chat/src/Aggregate/Misc/ResponseName.cs b/src/Servers/Chat/src/Aggregate/Misc/ResponseName.cs deleted file mode 100755 index e3ffd89ef..000000000 --- a/src/Servers/Chat/src/Aggregate/Misc/ResponseName.cs +++ /dev/null @@ -1,87 +0,0 @@ -namespace UniSpy.Server.Chat.Aggregate.Misc -{ - public sealed class ResponseName - { - public const string Welcome = "001"; - public const string UserIP = "302"; - public const string WhoIsUser = "311"; - public const string EndOfWho = "315"; - public const string EndOfWhoIs = "318"; - public const string WhoIsChannels = "319"; - public const string ListStart = "321"; - public const string List = "322"; - public const string ListEnd = "323"; - public const string ChannelModels = "324"; - public const string NoTopic = "331"; - public const string Topic = "332"; - public const string WhoReply = "352"; - public const string NameReply = "353"; - public const string EndOfNames = "366"; - public const string BanList = "367"; - public const string EndOfBanList = "368"; - public const string GetKey = "700"; - public const string EndGetKey = "701"; - public const string GetCKey = "702"; - public const string EndGetCKey = "703"; - public const string GetChanKey = "704"; - public const string SecureKey = "705"; - public const string CDKey = "706"; - public const string Login = "707"; - public const string GetUDPRelay = "712"; - - /// - /// Send a private message - /// - public const string PrivateMsg = "PRIVMSG"; - /// - /// Send a notice message - /// - public const string Notice = "NOTICE"; - /// - /// Send an under the table message - /// - public const string UnderTheTableMsg = "UTM"; - /// - /// Send an above the table message - /// - public const string AboveTheTableMsg = "ATM"; - public const string Ping = "PING"; - public const string Pong = "PONG"; - /// - /// Search with nickname - /// - public const string Nick = "NICK"; - /// - /// Join a channel - /// - public const string Join = "JOIN"; - /// - /// Leave a channel - /// - public const string Part = "PART"; - /// - /// Kick a user from a channel - /// - public const string Kick = "KICK"; - /// - /// Quit irc chat server - /// - public const string Quit = "QUIT"; - - public const string Kill = "KILL"; - /// - /// Change channel topic - /// - public const string ChannelTopic = "TOPIC"; - /// - /// Change channel mode - /// - public const string Mode = "MODE"; - - public const string Error = "ERROR"; - /// - /// Invite a user to a channel - /// - public const string Invite = "INVITE"; - } -} diff --git a/src/Servers/Chat/src/Aggregate/Redis/ChannelCache.cs b/src/Servers/Chat/src/Aggregate/Redis/ChannelCache.cs deleted file mode 100644 index 43eeafcb5..000000000 --- a/src/Servers/Chat/src/Aggregate/Redis/ChannelCache.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using UniSpy.LinqToRedis; -using UniSpy.Server.Core.Extension.Redis; - -namespace UniSpy.Server.Chat.Aggregate.Redis -{ - public record ChannelCache : Core.Abstraction.BaseClass.RedisKeyValueObject - { - [RedisKey] - public string GameName { get; set; } - [RedisKey] - public string ChannelName { get; set; } - // [RedisKey] - // public string PreviousJoinedChannel => Channel?.PreviousJoinedChannel; - public Channel Channel { get; set; } - public ChannelCache() : base(RedisDbNumber.ChatChannel, TimeSpan.FromMinutes(1)) - { - } - public class RedisClient : Core.Abstraction.BaseClass.RedisClient - { - public RedisClient() { } - } - } - -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/Redis/ClientInfoCache.cs b/src/Servers/Chat/src/Aggregate/Redis/ClientInfoCache.cs deleted file mode 100644 index 6df2115ea..000000000 --- a/src/Servers/Chat/src/Aggregate/Redis/ClientInfoCache.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using UniSpy.LinqToRedis; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Core.Extension.Redis; - -namespace UniSpy.Server.Chat.Aggregate.Redis -{ - public record ClientInfoCache : Core.Abstraction.BaseClass.RedisKeyValueObject - { - [RedisKey] - public string NickName { get; set; } - public ClientInfo Info { get; set; } - public ClientInfoCache() : base(RedisDbNumber.ChatChannel, TimeSpan.FromMinutes(2)) { } - public class RedisClient : Core.Abstraction.BaseClass.RedisClient - { - public RedisClient() { } - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/Redis/Contract/RemoteMessage.cs b/src/Servers/Chat/src/Aggregate/Redis/Contract/RemoteMessage.cs deleted file mode 100644 index 2bfed57c5..000000000 --- a/src/Servers/Chat/src/Aggregate/Redis/Contract/RemoteMessage.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Newtonsoft.Json; -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Core.Encryption; - -namespace UniSpy.Server.Chat.Aggregate.Redis.Contract -{ - public class RemoteMessage - { - [JsonProperty] - public string Type { get; private set; } - [JsonProperty] - public byte[] RawRequest { get; private set; } - [JsonProperty] - public RemoteClient Client { get; private set; } - /// - /// Constructor for json deserialization - /// - public RemoteMessage() { } - public RemoteMessage(RequestBase request, RemoteClient client) - { - RawRequest = UniSpyEncoding.GetBytes(request.RawRequest); - Type = request.CommandName; - Client = client; - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/Redis/PeerRoom.cs b/src/Servers/Chat/src/Aggregate/Redis/PeerRoom.cs deleted file mode 100644 index 7f3b2ded7..000000000 --- a/src/Servers/Chat/src/Aggregate/Redis/PeerRoom.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using System.Linq; - -namespace UniSpy.Server.Chat.Aggregate.Redis -{ - public static class PeerRoom - { - /// - /// When game connects to the server, the player will enter the default channel for communicating with other players. - /// - public const string TitleRoomPrefix = "#GSP"; - /// - /// When a player creates their own game and is waiting for others to join they are placed in a separate chat room called the "staging room" - /// Staging rooms have two title seperator like #GSP!xxxx!xxxx - /// - public const string StagingRoomPrefix = "#GSP"; - /// - /// group rooms is used split the list of games into categories (by gametype, skill, region, etc.). In this case, when entering the title room, the user would get a list of group rooms instead of a list of games - /// Group room have one title seperator like #GPG!xxxxxx - /// - public const string GroupRoomPrefix = "#GPG"; - public const char TitleSeperator = '!'; - /// - /// Group room #GPG!622 - /// Staging room #GSP!worms3!Ml4lz344lM - /// Normal room #islanbul - /// - /// - /// - public static PeerRoomType GetRoomType(string channelName) - { - if (IsGroupRoom(channelName)) - { - return PeerRoomType.Group; - } - else if (IsStagingRoom(channelName)) - { - return PeerRoomType.Staging; - } - else if (IsTitleRoom(channelName)) - { - return PeerRoomType.Title; - } - else - { - return PeerRoomType.Normal; - } - } - private static bool IsStagingRoom(string channelName) - { - var a = channelName.Count(c => c == TitleSeperator) == 2 ? true : false; - var b = channelName.StartsWith(StagingRoomPrefix, StringComparison.CurrentCultureIgnoreCase) ? true : false; - return a && b; - } - private static bool IsTitleRoom(string channelName) - { - var a = channelName.Count(c => c == TitleSeperator) == 1 ? true : false; - var b = channelName.StartsWith(TitleRoomPrefix, StringComparison.CurrentCultureIgnoreCase) ? true : false; - return a && b; - } - private static bool IsGroupRoom(string channelName) - { - - var a = channelName.Count(c => c == TitleSeperator) == 1 ? true : false; - var b = channelName.StartsWith(GroupRoomPrefix, StringComparison.CurrentCultureIgnoreCase) ? true : false; - return a && b; - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs b/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs deleted file mode 100644 index 84cbde050..000000000 --- a/src/Servers/Chat/src/Aggregate/Redis/RedisChannel.cs +++ /dev/null @@ -1,43 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Redis.Contract; -using UniSpy.Server.Chat.Handler; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Extension.Redis; -using System.Threading.Tasks; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Core.Encryption; - -namespace UniSpy.Server.Chat.Aggregate.Redis -{ - /// - /// When a local channel is created the user message will send to redis channel - /// redis channel is like a broadcast platform which will broadcast the message to all the user - /// when user is connected to unispy chat server - /// - public class ChannelMessageBroker : RedisChannelBase - { - public ChannelMessageBroker(string chatChannelName) : base($"{RedisChannelName.ChatChannelPrefix}:{chatChannelName}") { } - - public override void ReceivedMessage(RemoteMessage message) - { - // we are uint testing so we skip publish message here - if (ServerLauncher.ServerInstances.Count == 0) - { - return; - } - // base.ReceivedMessage(message); - if (message.Client.Server.Id == ServerLauncher.Server.Id) - { - return; - } - var switcher = new CmdSwitcher(message.Client, UniSpyEncoding.GetString(message.RawRequest)); - if (System.Diagnostics.Debugger.IsAttached) - { - switcher.Handle(); - } - else - { - Task.Run(() => switcher.Handle()); - } - } - } -} diff --git a/src/Servers/Chat/src/Aggregate/RemoteClient.cs b/src/Servers/Chat/src/Aggregate/RemoteClient.cs deleted file mode 100644 index 985c2e65b..000000000 --- a/src/Servers/Chat/src/Aggregate/RemoteClient.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Newtonsoft.Json; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Chat.Handler; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.Core.Network; - -namespace UniSpy.Server.Chat.Aggregate -{ - public class RemoteClient : IShareClient - { - [JsonProperty] - public bool IsRemoteClient => !ClientManager.ClientPool.ContainsKey(Connection.RemoteIPEndPoint); - public bool IsLogRaw { get; set; } - [JsonConverter(typeof(ConcreteTypeConverter))] - public IConnection Connection { get; set; } - [JsonConverter(typeof(ConcreteTypeConverter))] - public ICryptography Crypto { get; set; } - [JsonConverter(typeof(ConcreteTypeConverter))] - public ClientInfoBase Info { get; set; } - [JsonIgnore] - ClientInfo IShareClient.Info => (ClientInfo)Info; - [JsonConverter(typeof(ConcreteTypeConverter))] - public IServer Server { get; set; } - /// - /// using for json deserialization - /// - public RemoteClient() { } - public RemoteClient(Client client) - { - Connection = new RemoteTcpConnection(client.Connection, new RemoteTcpConnectionManager()); - Server = new RemoteServer(client.Server); - Info = client.Info; - Crypto = client.Crypto; - } - public void Send(IResponse response) { } - public RemoteClient GetRemoteClient() => this; - - public void TestReceived(byte[] buffer) - { - this.LogNetworkReceiving(buffer); - var switcher = new CmdSwitcher(this, UniSpyEncoding.GetString(buffer)); - switcher.Handle(); - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Application/Client.cs b/src/Servers/Chat/src/Application/Client.cs deleted file mode 100644 index a521d32fa..000000000 --- a/src/Servers/Chat/src/Application/Client.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Handler; -using UniSpy.Server.Chat.Handler.CmdHandler.General; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.Core.Extension; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.Chat.Application -{ - public class Client : ClientBase, IShareClient - { - public new ClientInfo Info { get => (ClientInfo)base.Info; private set => base.Info = value; } - public new ITcpConnection Connection => (ITcpConnection)base.Connection; - public bool IsRemoteClient => !ClientManager.ClientPool.ContainsKey(Connection.RemoteIPEndPoint); - private RemoteClient _remoteClient; - public Client(IConnection connection, IServer server) : base(connection, server) - { - Info = new ClientInfo(); - _remoteClient = new RemoteClient(this); - } - public Client(IConnection connection, IServer server, ClientInfo info) : this(connection, server) - { - Info = info; - _remoteClient = new RemoteClient(this); - } - protected override void EventBinding() - { - base.EventBinding(); - // bind a event that can update the client info to redis - _timer = new EasyTimer(TimeSpan.FromMinutes(1)); - _timer.Elapsed += (s, e) => Application.StorageOperation.Persistance.UpdateClient(this); - _timer.Start(); - } - protected override void OnReceived(object buffer) - { - var message = DecryptMessage((byte[])buffer); - this.LogNetworkReceiving(message); - var switcher = CreateSwitcher(message); - switcher.Handle(); - } - protected override void OnDisconnected() - { - if (Info.IsLoggedIn) - { - var req = new QuitRequest() - { - Reason = $"{Info.NickName} Disconnected." - }; - new QuitHandler(this, req).Handle(); - Info.IsLoggedIn = false; - } - StorageOperation.Persistance.RemoveClient(this); - base.OnDisconnected(); - } - protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, UniSpyEncoding.GetString((byte[])buffer)); - public RemoteClient GetRemoteClient() => _remoteClient; - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Application/ClientInfo.cs b/src/Servers/Chat/src/Application/ClientInfo.cs deleted file mode 100755 index 7e1f43696..000000000 --- a/src/Servers/Chat/src/Application/ClientInfo.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Collections.Generic; -using System; -using Newtonsoft.Json; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Core.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Application -{ - public sealed class ClientInfo : ClientInfoBase - { - /// - /// indicates which channel this user is in - /// (We do not send this information to our public channel) - /// - [JsonIgnore] - public Dictionary JoinedChannels { get; private set; } = new(); - - // secure connection - public string GameName { get; set; } - public string NickName { get; set; } - public string UserName { get; set; } - public string Name { get; set; } - public string ServerIP { get; set; } - public int NameSpaceID { get; set; } = 0; - public string UniqueNickName { get; set; } - public string GameSecretKey { get; set; } - public bool IsLoggedIn { get; set; } = false; - public bool IsUsingEncryption { get; set; } = false; - public bool IsQuietMode { get; set; } = false; - [JsonIgnore] - public string IRCPrefix => $"{NickName}!{UserName}@{Chat.Abstraction.BaseClass.ResponseBase.ServerDomain}"; - public string PreviousJoinedChannel { get; set; } = ""; - public Guid ServerId { get; private set; } - /// - /// Global user key values - /// - /// - public KeyValueManager KeyValues { get; private set; } = new KeyValueManager(); - - public ClientInfo() - { - } - - public bool IsJoinedChannel(string channelName) => JoinedChannels.ContainsKey(channelName); - - public Channel GetLocalJoinedChannel(string channelName) - { - if (JoinedChannels.ContainsKey(channelName)) - { - return JoinedChannels[channelName]; - } - else - { - return null; - } - } - public ClientInfo DeepCopy() - { - var infoCopy = (ClientInfo)this.MemberwiseClone(); - return infoCopy; - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Application/ClientManager.cs b/src/Servers/Chat/src/Application/ClientManager.cs deleted file mode 100644 index c866fd5f2..000000000 --- a/src/Servers/Chat/src/Application/ClientManager.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Application -{ - public sealed class ClientManager : ClientManagerBase - { - /// - /// We need to make sure client is get by nickname, otherwise we throw exception - /// - public static Client GetClientByNickName(string nickName) - { - var client = (Client)ClientPool.Values.FirstOrDefault(c => ((IShareClient)c).Info.NickName == nickName); - return client; - } - public static List GetAllClientInfo() - { - var infos = ClientPool.Values.Select(c => ((ClientInfo)(c.Info))).ToList(); - return infos; - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Application/Program.cs b/src/Servers/Chat/src/Application/Program.cs deleted file mode 100755 index 25ca2f409..000000000 --- a/src/Servers/Chat/src/Application/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.Chat.Application -{ - public class Program - { - static void Main(string[] args) - { - try - { - new ServerLauncher().Start(); - Console.WriteLine("Press < Q > to exit. "); - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - catch (System.Exception e) - { - UniSpy.Exception.HandleException(e); - } - finally - { - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - } - } -} diff --git a/src/Servers/Chat/src/Application/Server.cs b/src/Servers/Chat/src/Application/Server.cs deleted file mode 100644 index 579919444..000000000 --- a/src/Servers/Chat/src/Application/Server.cs +++ /dev/null @@ -1,29 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Redis; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Network.Tcp.Server; -using UniSpy.Server.Core.Abstraction.Interface; -using System.Net; - -namespace UniSpy.Server.Chat.Application -{ - public sealed class Server : ServerBase - { - static Server() - { - _name = "Chat"; - } - public Server() { } - - public Server(IConnectionManager manager) : base(manager) { } - - public override void Start() - { - base.Start(); - _ = Chat.Application.StorageOperation.Persistance.PeerGroupList; - } - - protected override IClient CreateClient(IConnection connection) => new Client(connection, this); - - protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new TcpConnectionManager(endPoint); - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Application/ServerLauncher.cs b/src/Servers/Chat/src/Application/ServerLauncher.cs deleted file mode 100644 index d65b58de0..000000000 --- a/src/Servers/Chat/src/Application/ServerLauncher.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.BaseClass.Factory; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Application -{ - public sealed class ServerLauncher : ServerLauncherBase - { - public static IServer Server => ServerInstances[0]; - protected override List LaunchNetworkService() => new List { new Server() }; - public override void Start() - { - base.Start(); - // initialize peergrouplist - _ = Chat.Application.StorageOperation.Persistance.PeerGroupList; - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Application/StorageOperation.cs b/src/Servers/Chat/src/Application/StorageOperation.cs deleted file mode 100644 index 2238dc4ed..000000000 --- a/src/Servers/Chat/src/Application/StorageOperation.cs +++ /dev/null @@ -1,167 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Chat.Aggregate.Redis; -using UniSpy.Server.Core.Database.DatabaseModel; - -namespace UniSpy.Server.Chat.Application -{ - public sealed class StorageOperation : IStorageOperation - { - public static IStorageOperation Persistance = new StorageOperation(); - /// - /// The peer group list in memory - /// - Dictionary> IStorageOperation.PeerGroupList => _peerGroupList; - private readonly Dictionary> _peerGroupList = GetAllGroupList(); - public ChannelCache.RedisClient ChannelCacheClient { get; } = new(); - public ClientInfoCache.RedisClient ClientCacheClient { get; } = new(); - - - public StorageOperation() - { - } - - public (int userId, int profileId, bool emailVerified, bool banned) NickAndEmailLogin(string nickName, string email, string passwordHash) - { - using (var db = new UniSpyContext()) - { - var result = from u in db.Users - join p in db.Profiles on u.Userid equals p.Userid - where u.Email == email - && p.Nick == nickName - && u.Password == passwordHash - select new - { - userId = u.Userid, - profileId = p.Profileid, - emailVerified = u.Emailverified, - banned = u.Banned - }; - - if (result.Count() != 1) - { - throw new Chat.Exception($"Can not find user with nickname:{nickName} in database."); - } - return (userId: result.First().userId, - profileId: result.First().profileId, - emailVerified: (bool)result.First().emailVerified, - banned: result.First().banned); - } - } - public (int userId, int profileId, bool emailVerified, bool banned) UniqueNickLogin(string uniqueNick, int namespaceId) - { - using (var db = new UniSpyContext()) - { - var result = from n in db.Subprofiles - join p in db.Profiles on n.Profileid equals p.Profileid - join u in db.Users on p.Userid equals u.Userid - where n.Uniquenick == uniqueNick - && n.Namespaceid == namespaceId - select new - { - userId = u.Userid, - profileId = p.Profileid, - uniquenick = n.Uniquenick, - emailVerified = u.Emailverified, - banned = u.Banned - }; - if (result.Count() != 1) - { - throw new Chat.Exception($"Can not find user with uniquenick:{uniqueNick} in database."); - } - // _result.ProfileId = result.First().profileid; - // _result.UserID = result.First().userid; - // Select( - return (userId: result.First().userId, - profileId: result.First().profileId, - emailVerified: (bool)result.First().emailVerified, - banned: result.First().banned); - } - } - - public bool IsChannelExist(ChannelCache key) - { - return ChannelCacheClient.Context.Count(c => c.ChannelName == key.ChannelName && c.GameName == key.GameName) == 1 ? true : false; - } - - public Channel GetChannel(ChannelCache key) - { - var result = ChannelCacheClient.Context.Where(c => c.ChannelName == key.ChannelName && c.GameName == key.GameName).FirstOrDefault(); - return result?.Channel; - } - - public void UpdateChannel(Channel channel) - { - var data = new ChannelCache - { - ChannelName = channel.Name, - GameName = channel.GameName, - Channel = channel - }; - ChannelCacheClient.SetValue(data); - } - - public void RemoveChannel(Channel channel) - { - var data = new ChannelCache - { - ChannelName = channel.Name, - }; - ChannelCacheClient.DeleteKeyValue(data); - } - - public void UpdateClient(IShareClient client) - { - // we do not update client info when its nickname is not registered - if (client.Info.NickName is null) - { - return; - } - var data = new ClientInfoCache - { - NickName = client.Info.NickName, - Info = client.Info - }; - ClientCacheClient.SetValue(data); - } - - public void RemoveClient(IShareClient client) - { - var data = new ClientInfoCache - { - NickName = client.Info.NickName - }; - ClientCacheClient.DeleteKeyValue(data); - } - - public bool IsClientExist(ClientInfoCache key) - { - return ClientCacheClient.Context.Count(c => c.NickName == key.NickName) == 1 ? true : false; - } - - private static Dictionary> GetAllGroupList() - { - using (var db = new UniSpyContext()) - { - var result = from g in db.Games - join gl in db.Grouplists on g.Gameid equals gl.Gameid - select new Grouplist - { - Game = g, - Gameid = g.Gameid, - Groupid = gl.Groupid, - Roomname = gl.Roomname - }; - var result2 = from g in result - group g by g.Game.Gamename into dd - select new KeyValuePair>(dd.Key, dd.ToList()); - - var data = result2.ToDictionary(x => x.Key, x => x.Value); - return data; - } - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Contract/Request/Channel/GetCKeyRequest.cs b/src/Servers/Chat/src/Contract/Request/Channel/GetCKeyRequest.cs deleted file mode 100755 index fcb9c2d5b..000000000 --- a/src/Servers/Chat/src/Contract/Request/Channel/GetCKeyRequest.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.Channel -{ - public enum GetKeyReqeustType - { - GetChannelAllUserKeyValue, - GetChannelSpecificUserKeyValue - } - - - public sealed class GetCKeyRequest : ChannelRequestBase - { - public GetKeyReqeustType RequestType { get; private set; } - public string NickName { get; private set; } - public string Cookie { get; private set; } - public List Keys { get; private set; } - public GetCKeyRequest(string rawRequest) : base(rawRequest) - { - Keys = new List(); - } - - public override void Parse() - { - base.Parse(); - - if (_cmdParams.Count != 4) - { - throw new Chat.Exception("The number of IRC parameters are incorrect."); - } - - if (_longParam is null) - { - throw new Chat.Exception("The IRC long parameter is incorrect."); - } - - NickName = _cmdParams[1]; - - if (NickName == "*") - { - RequestType = GetKeyReqeustType.GetChannelAllUserKeyValue; - } - else - { - RequestType = GetKeyReqeustType.GetChannelSpecificUserKeyValue; - } - Cookie = _cmdParams[2]; - - if (!_longParam.Contains("\0") && !_longParam.Contains("\\")) - { - throw new Chat.Exception("The key provide is incorrect."); - } - - Keys = _longParam.TrimStart('\\').TrimEnd('\0').Split("\\").ToList(); - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/Channel/GetChannelKeyRequest.cs b/src/Servers/Chat/src/Contract/Request/Channel/GetChannelKeyRequest.cs deleted file mode 100755 index 18106d8ac..000000000 --- a/src/Servers/Chat/src/Contract/Request/Channel/GetChannelKeyRequest.cs +++ /dev/null @@ -1,33 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using System.Collections.Generic; -using System.Linq; - -namespace UniSpy.Server.Chat.Contract.Request.Channel -{ - - public sealed class GetChannelKeyRequest : ChannelRequestBase - { - public GetChannelKeyRequest(string rawRequest) : base(rawRequest){ } - public string Cookie { get; private set; } - public List Keys { get; private set; } - - public override void Parse() - { - base.Parse(); - - if (_cmdParams.Count != 3) - { - throw new Chat.Exception("The cmdParams number is invalid."); - } - - if (_longParam is null || _longParam.Last() != '\0') - { - throw new Chat.Exception("The longParam number is invalid."); - } - - Cookie = _cmdParams[1]; - - Keys = _longParam.TrimStart('\\').TrimEnd('\0').Split("\\").ToList(); - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/Channel/JoinRequest.cs b/src/Servers/Chat/src/Contract/Request/Channel/JoinRequest.cs deleted file mode 100755 index bccc49aac..000000000 --- a/src/Servers/Chat/src/Contract/Request/Channel/JoinRequest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Contract.Request.Channel -{ - - public sealed class JoinRequest : ChannelRequestBase - { - public string Password { get; private set; } - public JoinRequest(string rawRequest) : base(rawRequest) { } - public override void Parse() - { - base.Parse(); - - if (_cmdParams.Count > 2) - { - throw new Chat.Exception("number of IRC parameters are incorrect."); - } - - if (_cmdParams.Count == 2) - { - Password = _cmdParams[1]; - } - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/Channel/KickRequest.cs b/src/Servers/Chat/src/Contract/Request/Channel/KickRequest.cs deleted file mode 100755 index 29a36e957..000000000 --- a/src/Servers/Chat/src/Contract/Request/Channel/KickRequest.cs +++ /dev/null @@ -1,34 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.Channel -{ - - public sealed class KickRequest : ChannelRequestBase - { - public new string ChannelName{ get => base.ChannelName; set => base.ChannelName = value; } - public KickRequest() { } - public KickRequest(string rawRequest) : base(rawRequest){ } - public string KickeeNickName { get; set; } - public string Reason { get; set; } - - public override void Parse() - { - base.Parse(); - - if (_cmdParams.Count != 2) - { - throw new Chat.Exception("The number of IRC parameters are incorrect."); - } - - KickeeNickName = _cmdParams[1]; - - if (_longParam is null) - { - throw new Chat.Exception("The IRC long parameters is missing."); - } - - Reason = _longParam; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/Channel/ModeRequest.cs b/src/Servers/Chat/src/Contract/Request/Channel/ModeRequest.cs deleted file mode 100755 index 2572e353e..000000000 --- a/src/Servers/Chat/src/Contract/Request/Channel/ModeRequest.cs +++ /dev/null @@ -1,219 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text.RegularExpressions; -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.Channel -{ - //request: - //"MODE +/-q" - - //"MODE +/-k " - - // "MODE +l " - // "MODE -l" - - // "MODE +b" actually we do not care about this request - // "MODE +/-b " - - // "MODE +/-co " - // "MODE +/-cv " - - // "MODE " - // "MODE " - public enum ModeOperationType - { - EnableUserQuietFlag, - DisableUserQuietFlag, - AddChannelPassword, - RemoveChannelPassword, - AddChannelUserLimits, - RemoveChannelUserLimits, - AddBanOnUser, - GetBannedUsers, - RemoveBanOnUser, - AddChannelOperator, - RemoveChannelOperator, - EnableUserVoicePermission, - DisableUserVoicePermission, - SetInvitedOnly, - RemoveInvitedOnly, - SetPrivateChannelFlag, - RemovePrivateChannelFlag, - SetSecretChannelFlag, - RemoveSecretChannelFlag, - SetModeratedChannelFlag, - RemoveModeratedChannelFlag, - EnableExternalMessagesFlag, - DisableExternalMessagesFlag, - SetTopicChangeByOperatorFlag, - RemoveTopicChangeByOperatorFlag, - SetOperatorAbeyChannelLimits, - RemoveOperatorAbeyChannelLimits, - } - public enum ModeRequestType - { - GetChannelModes, - GetChannelAndUserModes, - SetChannelModes - - } - - public sealed class ModeRequest : ChannelRequestBase - { - public ModeRequestType RequestType { get; set; } - public List ModeOperations { get; set; } - public string NickName { get; set; } - public string UserName { get; set; } - public int LimitNumber { get; set; } - public string ModeFlag { get; set; } - public string Password { get; set; } - public ModeRequest(string rawRequest) : base(rawRequest) - { - ModeOperations = new List(); - } - public ModeRequest() { } - - public override void Parse() - { - // we skip parsing when this request is created manmually - if (RawRequest is null) - { - return; - } - base.Parse(); - if (_cmdParams.Count == 1) - { - RequestType = ModeRequestType.GetChannelModes; - } - else if (_cmdParams.Count == 2 || _cmdParams.Count == 3) - { - RequestType = ModeRequestType.SetChannelModes; - ModeFlag = _cmdParams[1]; - var modeFlags = Regex.Split(ModeFlag, @"(?=\+|\-)").Where(s => !String.IsNullOrEmpty(s)).ToArray(); - foreach (var flag in modeFlags) - { - switch (flag) - { - case "+e": - ModeOperations.Add(ModeOperationType.SetOperatorAbeyChannelLimits); - break; - case "-e": - ModeOperations.Add(ModeOperationType.RemoveOperatorAbeyChannelLimits); - break; - case "+t": - ModeOperations.Add(ModeOperationType.SetTopicChangeByOperatorFlag); - break; - case "-t": - ModeOperations.Add(ModeOperationType.RemoveTopicChangeByOperatorFlag); - break; - case "+n": - ModeOperations.Add(ModeOperationType.EnableExternalMessagesFlag); - break; - case "-n": - ModeOperations.Add(ModeOperationType.DisableExternalMessagesFlag); - break; - case "+m": - ModeOperations.Add(ModeOperationType.SetModeratedChannelFlag); - break; - case "-m": - ModeOperations.Add(ModeOperationType.RemoveModeratedChannelFlag); - break; - case "+s": - ModeOperations.Add(ModeOperationType.SetSecretChannelFlag); - break; - case "-s": - ModeOperations.Add(ModeOperationType.RemoveSecretChannelFlag); - break; - case "+i": - ModeOperations.Add(ModeOperationType.SetInvitedOnly); - break; - case "-i": - ModeOperations.Add(ModeOperationType.RemoveInvitedOnly); - break; - case "-p": - ModeOperations.Add(ModeOperationType.RemovePrivateChannelFlag); - break; - case "+p": - ModeOperations.Add(ModeOperationType.SetPrivateChannelFlag); - break; - case "+q": - NickName = _cmdParams[0]; - ModeOperations.Add(ModeOperationType.EnableUserQuietFlag); - break; - case "-q": - NickName = _cmdParams[0]; - ModeOperations.Add(ModeOperationType.DisableUserQuietFlag); - break; - case "+k": - ChannelName = _cmdParams[0]; - Password = _cmdParams[2]; - ModeOperations.Add(ModeOperationType.AddChannelPassword); - break; - case "-k": - ChannelName = _cmdParams[0]; - Password = _cmdParams[2]; - ModeOperations.Add(ModeOperationType.RemoveChannelPassword); - break; - case "+l": - ChannelName = _cmdParams[0]; - LimitNumber = int.Parse(_cmdParams[2]); - ModeOperations.Add(ModeOperationType.AddChannelUserLimits); - break; - case "-l": - ChannelName = _cmdParams[0]; - ModeOperations.Add(ModeOperationType.RemoveChannelUserLimits); - break; - case "+b": - ChannelName = _cmdParams[0]; - if (_cmdParams.Count == 3) - { - NickName = _cmdParams[2]; - ModeOperations.Add(ModeOperationType.AddBanOnUser); - } - else - { - ModeOperations.Add(ModeOperationType.GetBannedUsers); - } - break; - case "-b": - ChannelName = _cmdParams[0]; - ModeOperations.Add(ModeOperationType.RemoveBanOnUser); - break; - case "+co": - ChannelName = _cmdParams[0]; - UserName = _cmdParams[2]; - ModeOperations.Add(ModeOperationType.AddChannelOperator); - break; - case "-co": - ChannelName = _cmdParams[0]; - UserName = _cmdParams[2]; - ModeOperations.Add(ModeOperationType.RemoveChannelOperator); - break; - case "+cv": - ChannelName = _cmdParams[0]; - NickName = _cmdParams[2]; - ModeOperations.Add(ModeOperationType.EnableUserVoicePermission); - break; - case "-cv": - ChannelName = _cmdParams[0]; - NickName = _cmdParams[2]; - ModeOperations.Add(ModeOperationType.DisableUserVoicePermission); - break; - default: - throw new Chat.Exception("Unknown mode request type."); - } - - } - // TODO! We ignore multiple mode flags, maybe we need it in future for supporting games - - } - else - { - throw new Chat.Exception("number of IRC parameters are incorrect."); - } - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/Channel/PartRequest.cs b/src/Servers/Chat/src/Contract/Request/Channel/PartRequest.cs deleted file mode 100755 index fc708029c..000000000 --- a/src/Servers/Chat/src/Contract/Request/Channel/PartRequest.cs +++ /dev/null @@ -1,23 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.Channel -{ - - public sealed class PartRequest : ChannelRequestBase - { - public new string ChannelName{ get => base.ChannelName; set => base.ChannelName = value; } - public string Reason { get; set; } - public PartRequest() { } - public PartRequest(string rawRequest) : base(rawRequest){ } - public override void Parse() - { - base.Parse(); - if (_longParam is null) - { - throw new Chat.Exception("The reason of living channel is missing."); - } - Reason = _longParam; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/Channel/SetCKeyRequest.cs b/src/Servers/Chat/src/Contract/Request/Channel/SetCKeyRequest.cs deleted file mode 100755 index dc31307e2..000000000 --- a/src/Servers/Chat/src/Contract/Request/Channel/SetCKeyRequest.cs +++ /dev/null @@ -1,50 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using System.Collections.Generic; -using UniSpy.Server.Core.Extension; -using System.Linq; - -namespace UniSpy.Server.Chat.Contract.Request.Channel -{ - - public sealed class SetCKeyRequest : ChannelRequestBase - { - public string Channel { get; private set; } - public string NickName { get; private set; } - public string Cookie { get; private set; } - public bool IsBroadCast { get; private set; } - public string KeyValueString { get; private set; } - public Dictionary KeyValues { get; private set; } - - public SetCKeyRequest(string rawRequest) : base(rawRequest) - { - KeyValues = new Dictionary(); - } - - public override void Parse() - { - base.Parse(); - - if (_cmdParams is null) - { - throw new Chat.Exception("The cmdParams from SETCKEY request are missing."); - } - - if (_longParam is null) - { - throw new Chat.Exception("The longParam from SETCKEY request is missing."); - } - - Channel = _cmdParams[0]; - NickName = _cmdParams[1]; - _longParam = _longParam.Substring(1); - KeyValues = StringExtensions.ConvertKVStringToDictionary(_longParam); - KeyValueString = _longParam; - // todo check whether there exists other b_* key value - if (KeyValues.Keys.Where(x => x.Contains("b_")).Count() > 0) - { - Cookie = "BCAST"; - IsBroadCast = true; - } - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/Channel/SetChannelKeyRequest.cs b/src/Servers/Chat/src/Contract/Request/Channel/SetChannelKeyRequest.cs deleted file mode 100755 index b9a53bfbd..000000000 --- a/src/Servers/Chat/src/Contract/Request/Channel/SetChannelKeyRequest.cs +++ /dev/null @@ -1,29 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using System.Collections.Generic; -using UniSpy.Server.Core.Extension; - -namespace UniSpy.Server.Chat.Contract.Request.Channel -{ - - public sealed class SetChannelKeyRequest : ChannelRequestBase - { - public string KeyValueString { get; private set; } - public Dictionary KeyValues { get; private set; } - - public SetChannelKeyRequest(string rawRequest) : base(rawRequest) { } - - public override void Parse() - { - base.Parse(); - - if (_longParam is null) - { - throw new Chat.Exception("Channel keys and values are missing."); - } - _longParam = _longParam.Substring(1); - - KeyValues = StringExtensions.ConvertKVStringToDictionary(_longParam); - KeyValueString = _longParam; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/Channel/TopicRequest.cs b/src/Servers/Chat/src/Contract/Request/Channel/TopicRequest.cs deleted file mode 100755 index 6f64860a3..000000000 --- a/src/Servers/Chat/src/Contract/Request/Channel/TopicRequest.cs +++ /dev/null @@ -1,36 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.Channel -{ - public enum TopicRequestType - { - GetChannelTopic, - SetChannelTopic - } - - - public sealed class TopicRequest : ChannelRequestBase - { - public new string ChannelName{ get => base.ChannelName; set => base.ChannelName = value; } - public TopicRequest(string rawRequest) : base(rawRequest){ } - - public string Channel { get; private set; } - public string ChannelTopic { get; private set; } - public TopicRequestType RequestType { get; private set; } - - public override void Parse() - { - base.Parse(); - - if (_longParam is null) - { - RequestType = TopicRequestType.GetChannelTopic; - return; - } - - RequestType = TopicRequestType.SetChannelTopic; - ChannelTopic = _longParam; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/CdKeyRequest.cs b/src/Servers/Chat/src/Contract/Request/General/CdKeyRequest.cs deleted file mode 100755 index c5d14a55b..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/CdKeyRequest.cs +++ /dev/null @@ -1,24 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class CdKeyRequest : RequestBase - { - - public string CdKey { get; private set; } - public CdKeyRequest(string rawRequest) : base(rawRequest) { } - public override void Parse() - { - base.Parse(); - - if (_cmdParams.Count < 1) - { - throw new Chat.Exception("The number of IRC cmdParams are incorrect."); - } - - CdKey = _cmdParams[0]; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/CryptRequest.cs b/src/Servers/Chat/src/Contract/Request/General/CryptRequest.cs deleted file mode 100755 index b38a3428e..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/CryptRequest.cs +++ /dev/null @@ -1,26 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class CryptRequest : RequestBase - { - - public string VersionID { get; private set; } - public string GameName { get; private set; } - public CryptRequest(string rawRequest) : base(rawRequest) { } - public override void Parse() - { - base.Parse(); - - if (_cmdParams.Count < 3) - { - throw new Chat.Exception("The number of IRC params in CRYPT request is incorrect."); - } - - VersionID = _cmdParams[1]; - GameName = _cmdParams[2]; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/GetKeyRequest.cs b/src/Servers/Chat/src/Contract/Request/General/GetKeyRequest.cs deleted file mode 100755 index 13e5a50d8..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/GetKeyRequest.cs +++ /dev/null @@ -1,49 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using System.Collections.Generic; -using UniSpy.Server.Core.Extension; - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - /// - /// Retrieves a list of global key/value for a single user, or all users. - /// - - public sealed class GetKeyRequest : RequestBase - { - public bool IsGetAllUser { get; private set; } - public string NickName { get; private set; } - public string Cookie { get; private set; } - public string UnkownCmdParam { get; private set; } - public List Keys { get; private set; } - public GetKeyRequest(string rawRequest) : base(rawRequest) - { - Keys = new List(); - } - - public override void Parse() - { - base.Parse(); - - if (_cmdParams.Count < 2) - { - throw new Chat.Exception("The number of IRC cmd params in GETKEY request is incorrect."); - } - - if (_longParam is null) - { - throw new Chat.Exception("The number of IRC long params in GETKEY request is incorrect."); - } - - NickName = _cmdParams[0]; - Cookie = _cmdParams[1]; - UnkownCmdParam = _cmdParams[2]; - - _longParam = _longParam.Substring(0, _longParam.Length); - if (NickName == "*") - { - IsGetAllUser = true; - } - Keys = StringExtensions.ConvertKeyStrToList(_longParam); - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/GetUdpRelayRequest.cs b/src/Servers/Chat/src/Contract/Request/General/GetUdpRelayRequest.cs deleted file mode 100755 index 84f97f258..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/GetUdpRelayRequest.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class GetUdpRelayRequest : ChannelRequestBase - { - public GetUdpRelayRequest(string rawRequest) : base(rawRequest){ } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/InviteRequest.cs b/src/Servers/Chat/src/Contract/Request/General/InviteRequest.cs deleted file mode 100755 index 900fdf5ea..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/InviteRequest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class InviteRequest : RequestBase - { - public string ChannelName { get; private set; } - public string NickName { get; private set; } - public InviteRequest(string rawRequest) : base(rawRequest) { } - public override void Parse() - { - base.Parse(); - - if (_cmdParams.Count != 2) - { - throw new Chat.Exception("The number of IRC cmd params in GETKEY request is incorrect."); - } - - ChannelName = _cmdParams[0]; - NickName = _cmdParams[1]; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/ListLimitRequest.cs b/src/Servers/Chat/src/Contract/Request/General/ListLimitRequest.cs deleted file mode 100755 index 558a3cbca..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/ListLimitRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class ListLimitRequest : RequestBase - { - public int MaxNumberOfChannels { get; private set; } - public string Filter { get; private set; } - public ListLimitRequest(string rawRequest) : base(rawRequest) { } - public override void Parse() - { - base.Parse(); - - if (_cmdParams.Count != 2) - { - throw new Chat.Exception("The number of IRC cmd params in GETKEY request is incorrect."); - } - - int max; - if (!int.TryParse(_cmdParams[0], out max)) - { - throw new Chat.Exception("The max number format is incorrect."); - } - - MaxNumberOfChannels = max; - Filter = _cmdParams[1]; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/ListRequest.cs b/src/Servers/Chat/src/Contract/Request/General/ListRequest.cs deleted file mode 100755 index f20fd2b1f..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/ListRequest.cs +++ /dev/null @@ -1,28 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class ListRequest : RequestBase - { - public ListRequest(string rawRequest) : base(rawRequest) { } - - public bool IsSearchingChannel { get; private set; } - public bool IsSearchingUser { get; private set; } - public string Filter { get; private set; } - - public override void Parse() - { - base.Parse(); - - if (_cmdParams is null || _cmdParams?.Count == 0) - { - throw new Chat.Exception("The Search filter is missing."); - } - - IsSearchingChannel = true; - Filter = _cmdParams[0]; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/LoginPreAuthRequest.cs b/src/Servers/Chat/src/Contract/Request/General/LoginPreAuthRequest.cs deleted file mode 100755 index c4c1ff2d8..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/LoginPreAuthRequest.cs +++ /dev/null @@ -1,23 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - - public sealed class LoginPreAuth : RequestBase - { - public LoginPreAuth(string rawRequest) : base(rawRequest){ } - - public string AuthToken { get; private set; } - public string PartnerChallenge { get; private set; } - - public override void Parse() - { - base.Parse(); - - AuthToken = _cmdParams[0]; - PartnerChallenge = _cmdParams[1]; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/LoginRequest.cs b/src/Servers/Chat/src/Contract/Request/General/LoginRequest.cs deleted file mode 100755 index 8461818b3..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/LoginRequest.cs +++ /dev/null @@ -1,55 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using System.Linq; - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - public enum LoginReqeustType - { - UniqueNickLogin, - NickAndEmailLogin, - } - - public sealed class LoginRequest : RequestBase - { - public LoginRequest(string rawRequest) : base(rawRequest){ } - - public LoginReqeustType ReqeustType { get; private set; } - public int NamespaceId { get; private set; } - public string NickName { get; private set; } - public string Email { get; private set; } - public string UniqueNick { get; private set; } - public string PasswordHash { get; private set; } - - public override void Parse() - { - base.Parse(); - - int namespaceid; - if (!int.TryParse(_cmdParams[0], out namespaceid)) - { - throw new Chat.Exception("The namespaceid format is incorrect."); - } - NamespaceId = namespaceid; - - if (_cmdParams[1] == "*") - { - ReqeustType = LoginReqeustType.NickAndEmailLogin; - PasswordHash = _cmdParams[2]; - - if (_longParam.Count(c => c == '@') != 2) - { - throw new Chat.Exception("The profile nick format is incorrect."); - } - - int profilenickIndex = _longParam.IndexOf("@"); - NickName = _longParam.Substring(0, profilenickIndex); - Email = _longParam.Substring(profilenickIndex + 1); - return; - } - - ReqeustType = LoginReqeustType.UniqueNickLogin; - UniqueNick = _cmdParams[1]; - PasswordHash = _cmdParams[2]; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/NamesRequest.cs b/src/Servers/Chat/src/Contract/Request/General/NamesRequest.cs deleted file mode 100755 index 6bf1cb476..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/NamesRequest.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class NamesRequest : ChannelRequestBase - { - public new string ChannelName{ get => base.ChannelName; set => base.ChannelName = value; } - public NamesRequest(string rawRequest) : base(rawRequest){ } - public NamesRequest() { } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/NickRequest.cs b/src/Servers/Chat/src/Contract/Request/General/NickRequest.cs deleted file mode 100755 index c9e05adb4..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/NickRequest.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Error.IRC.General; - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class NickRequest : RequestBase - { - private static List _invalidChars = new List() { '#', '@', '$', '%', '^', '&', '!', '~' }; - public NickRequest(string rawRequest) : base(rawRequest) { } - - public string NickName { get; private set; } - - public override void Parse() - { - base.Parse(); - - if (_cmdParams?.Count == 1) - { - NickName = _cmdParams[0]; - } - else if (_longParam is not null) - { - NickName = _longParam; - } - else - { - throw new Chat.Exception("NICK request is invalid."); - } - - foreach (var c in _invalidChars) - { - if (NickName.Contains(c)) - { - var validNickName = NickName.Replace(c, '0'); - throw new NickNameInUseException( - $"The nick name: {NickName} contains invalid character", - NickName, - validNickName); - } - } - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/PingRequest.cs b/src/Servers/Chat/src/Contract/Request/General/PingRequest.cs deleted file mode 100755 index 1c32b8289..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/PingRequest.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class PingRequest : RequestBase - { - public PingRequest(string rawRequest) : base(rawRequest){ } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/PongRequest.cs b/src/Servers/Chat/src/Contract/Request/General/PongRequest.cs deleted file mode 100755 index 20b65250a..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/PongRequest.cs +++ /dev/null @@ -1,24 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class PongRequest : RequestBase - { - public PongRequest(string rawRequest) : base(rawRequest){ } - - public string EchoMessage { get; private set; } - - public override void Parse() - { - base.Parse(); - - if (_longParam is null) - { - throw new Chat.Exception("Echo message is missing."); - } - EchoMessage = _longParam; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/QuitRequest.cs b/src/Servers/Chat/src/Contract/Request/General/QuitRequest.cs deleted file mode 100755 index 15081e03f..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/QuitRequest.cs +++ /dev/null @@ -1,26 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class QuitRequest : RequestBase - { - public QuitRequest() { } - public QuitRequest(string rawRequest) : base(rawRequest){ } - - public string Reason { get; set; } - - public override void Parse() - { - base.Parse(); - - if (_longParam is null) - { - throw new Chat.Exception("Quit reason is missing."); - } - - Reason = _longParam; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/RegisterNickRequest.cs b/src/Servers/Chat/src/Contract/Request/General/RegisterNickRequest.cs deleted file mode 100755 index c9decbf51..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/RegisterNickRequest.cs +++ /dev/null @@ -1,23 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class RegisterNickRequest : RequestBase - { - public RegisterNickRequest(string rawRequest) : base(rawRequest){ } - - public string NamespaceID { get; private set; } - public string UniqueNick { get; private set; } - public string CDKey { get; private set; } - public override void Parse() - { - base.Parse(); - - NamespaceID = _cmdParams[0]; - UniqueNick = _cmdParams[1]; - CDKey = _cmdParams[2]; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/SetGroupRequest.cs b/src/Servers/Chat/src/Contract/Request/General/SetGroupRequest.cs deleted file mode 100755 index 217ad9958..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/SetGroupRequest.cs +++ /dev/null @@ -1,24 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class SetGroupRequest : ChannelRequestBase - { - public SetGroupRequest(string rawRequest) : base(rawRequest){ } - - public string GroupName { get; private set; } - public override void Parse() - { - base.Parse(); - - if (_cmdParams.Count != 1) - { - throw new Chat.Exception("The number of IRC cmd params in GETKEY request is incorrect."); - } - - GroupName = _cmdParams[0]; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/SetKeyRequest.cs b/src/Servers/Chat/src/Contract/Request/General/SetKeyRequest.cs deleted file mode 100755 index ca43b45d0..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/SetKeyRequest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Core.Extension; - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class SetKeyRequest : RequestBase - { - public Dictionary KeyValues { get; private set; } - - public SetKeyRequest(string rawRequest) : base(rawRequest){ } - - public override void Parse() - { - base.Parse(); - - if (_longParam is null) - { - throw new Chat.Exception("The keys and values are missing."); - } - KeyValues = StringExtensions.ConvertKVStringToDictionary(_longParam); - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/UserIPRequest.cs b/src/Servers/Chat/src/Contract/Request/General/UserIPRequest.cs deleted file mode 100755 index e2198e6c6..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/UserIPRequest.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class UserIPRequest : RequestBase - { - public UserIPRequest(string rawRequest) : base(rawRequest){ } - - public override void Parse() - { - base.Parse(); - // USRIP content is empty! - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/UserRequest.cs b/src/Servers/Chat/src/Contract/Request/General/UserRequest.cs deleted file mode 100755 index 33243ee08..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/UserRequest.cs +++ /dev/null @@ -1,35 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class UserRequest : RequestBase - { - public UserRequest(string rawRequest) : base(rawRequest) { } - - public string UserName { get; private set; } - public string Hostname { get; private set; } - public string ServerName { get; private set; } - public string NickName { get; private set; } - public string Name { get; private set; } - - public override void Parse() - { - base.Parse(); - if (_cmdParams.Count == 3) - { - UserName = _cmdParams[0]; - Hostname = _cmdParams[1]; - ServerName = _cmdParams[2]; - } - else - { - Hostname = _cmdParams[0]; - ServerName = _cmdParams[1]; - } - - Name = _longParam; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/WhoIsRequest.cs b/src/Servers/Chat/src/Contract/Request/General/WhoIsRequest.cs deleted file mode 100755 index 2433f55ec..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/WhoIsRequest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - - public sealed class WhoIsRequest : RequestBase - { - public WhoIsRequest(string rawRequest) : base(rawRequest){ } - - public string NickName { get; private set; } - - public override void Parse() - { - base.Parse(); - - if (_cmdParams.Count != 1) - { - throw new Chat.Exception("The number of IRC cmd params in WHOIS request is incorrect."); - } - - NickName = _cmdParams[0]; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/General/WhoRequest.cs b/src/Servers/Chat/src/Contract/Request/General/WhoRequest.cs deleted file mode 100755 index 0cb558bcd..000000000 --- a/src/Servers/Chat/src/Contract/Request/General/WhoRequest.cs +++ /dev/null @@ -1,39 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.General -{ - public enum WhoRequestType - { - GetChannelUsersInfo, - GetUserInfo - } - - public sealed class WhoRequest : RequestBase - { - public WhoRequest(string rawRequest) : base(rawRequest){ } - - public WhoRequestType RequestType { get; private set; } - public string ChannelName { get; private set; } - public string NickName { get; private set; } - public override void Parse() - { - base.Parse(); - - if (_cmdParams.Count != 1) - { - throw new Chat.Exception("The number of IRC cmd params in WHO request is incorrect."); - } - - if (_cmdParams[0].Contains("#")) - { - RequestType = WhoRequestType.GetChannelUsersInfo; - ChannelName = _cmdParams[0]; - return; - } - - RequestType = WhoRequestType.GetUserInfo; - NickName = _cmdParams[0]; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/Message/AtmRequest.cs b/src/Servers/Chat/src/Contract/Request/Message/AtmRequest.cs deleted file mode 100644 index d0914c3ed..000000000 --- a/src/Servers/Chat/src/Contract/Request/Message/AtmRequest.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.Message -{ - - public sealed class AtmRequest : MessageRequestBase - { - public AtmRequest(string rawRequest) : base(rawRequest){ } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/Message/NoticeRequest.cs b/src/Servers/Chat/src/Contract/Request/Message/NoticeRequest.cs deleted file mode 100644 index c1c005f13..000000000 --- a/src/Servers/Chat/src/Contract/Request/Message/NoticeRequest.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.Message -{ - - public sealed class NoticeRequest : MessageRequestBase - { - public NoticeRequest(string rawRequest) : base(rawRequest){ } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/Message/PrivateRequest.cs b/src/Servers/Chat/src/Contract/Request/Message/PrivateRequest.cs deleted file mode 100644 index 2a6b33c07..000000000 --- a/src/Servers/Chat/src/Contract/Request/Message/PrivateRequest.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.Message -{ - - public sealed class PrivateRequest : MessageRequestBase - { - public PrivateRequest(string rawRequest) : base(rawRequest){ } - } -} diff --git a/src/Servers/Chat/src/Contract/Request/Message/UtmRequest.cs b/src/Servers/Chat/src/Contract/Request/Message/UtmRequest.cs deleted file mode 100644 index b4528986c..000000000 --- a/src/Servers/Chat/src/Contract/Request/Message/UtmRequest.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - - -namespace UniSpy.Server.Chat.Contract.Request.Message -{ - - public sealed class UtmRequest : MessageRequestBase - { - public UtmRequest(string rawRequest) : base(rawRequest){ } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/Channel/GetCKeyResponse.cs b/src/Servers/Chat/src/Contract/Response/Channel/GetCKeyResponse.cs deleted file mode 100755 index 808743404..000000000 --- a/src/Servers/Chat/src/Contract/Response/Channel/GetCKeyResponse.cs +++ /dev/null @@ -1,26 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; - -namespace UniSpy.Server.Chat.Contract.Response.Channel -{ - public sealed class GetCKeyResponse : ResponseBase - { - private new GetCKeyRequest _request => (GetCKeyRequest)base._request; - private new GetCKeyResult _result => (GetCKeyResult)base._result; - - public GetCKeyResponse(GetCKeyRequest request, GetCKeyResult result) : base(request, result) { } - - public override void Build() - { - SendingBuffer = ""; - foreach (var data in _result.DataResults) - { - SendingBuffer += $":{ServerDomain} {ResponseName.GetCKey} * {_request.ChannelName} {data.NickName} {_request.Cookie} {data.UserValues}\r\n"; - } - - SendingBuffer += $":{ServerDomain} {ResponseName.EndGetCKey} * {_request.ChannelName} {_request.Cookie} :End Of GETCKEY.\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/Channel/GetChannelKeyResponse.cs b/src/Servers/Chat/src/Contract/Response/Channel/GetChannelKeyResponse.cs deleted file mode 100755 index 0522b3955..000000000 --- a/src/Servers/Chat/src/Contract/Response/Channel/GetChannelKeyResponse.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; - -namespace UniSpy.Server.Chat.Contract.Response.Channel -{ - public sealed class GetChannelKeyResponse : ChannelResponseBase - { - private new GetChannelKeyRequest _request => (GetChannelKeyRequest)base._request; - private new GetChannelKeyResult _result => (GetChannelKeyResult)base._result; - public GetChannelKeyResponse(GetChannelKeyRequest request, GetChannelKeyResult result) : base(request, result) { } - - public override void Build() - { - SendingBuffer = $":{_result.ChannelUserIRCPrefix} {ResponseName.GetChanKey} * {_result.ChannelName} {_request.Cookie} {_result.Values}\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/Channel/JoinResponse.cs b/src/Servers/Chat/src/Contract/Response/Channel/JoinResponse.cs deleted file mode 100755 index 28e6f0deb..000000000 --- a/src/Servers/Chat/src/Contract/Response/Channel/JoinResponse.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; - -namespace UniSpy.Server.Chat.Contract.Response.Channel -{ - public sealed class JoinResponse : ResponseBase - { - private new JoinRequest _request => (JoinRequest)base._request; - private new JoinResult _result => (JoinResult)base._result; - // public string SendingBufferOfChannelUsers { get; private set; } - public JoinResponse(JoinRequest request, JoinResult result) : base(request, result) { } - - public override void Build() - { - SendingBuffer = $":{_result.JoinerPrefix} {ResponseName.Join} {_request.ChannelName}\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/Channel/KickResponse.cs b/src/Servers/Chat/src/Contract/Response/Channel/KickResponse.cs deleted file mode 100755 index 42a39fa47..000000000 --- a/src/Servers/Chat/src/Contract/Response/Channel/KickResponse.cs +++ /dev/null @@ -1,18 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; - -namespace UniSpy.Server.Chat.Contract.Response.Channel -{ - public sealed class KickResponse : ResponseBase - { - private new KickRequest _request => (KickRequest)base._request; - private new KickResult _result => (KickResult)base._result; - public KickResponse(KickRequest request, KickResult result) : base(request, result) { } - public override void Build() - { - SendingBuffer = $":{_result.KickerIRCPrefix} {ResponseName.Kick} {_result.ChannelName} {_result.KickeeNickName} :{_request.Reason}\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/Channel/ModeResponse.cs b/src/Servers/Chat/src/Contract/Response/Channel/ModeResponse.cs deleted file mode 100755 index 129592d6a..000000000 --- a/src/Servers/Chat/src/Contract/Response/Channel/ModeResponse.cs +++ /dev/null @@ -1,29 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; - -namespace UniSpy.Server.Chat.Contract.Response.Channel -{ - //TODO apply channel abstraction into this class - public sealed class ModeResponse : ResponseBase - { - private new ModeRequest _request => (ModeRequest)base._request; - private new ModeResult _result => (ModeResult)base._result; - public ModeResponse(ModeRequest request, ModeResult result) : base(request, result) { } - public override void Build() - { - if (_request.RequestType == ModeRequestType.GetChannelModes) - { - //channel modes reply - SendingBuffer = $":{ServerDomain} {ResponseName.ChannelModels} * {_result.ChannelName} {_result.ChannelModes}\r\n"; - } - else - { - //channel user mode reply - // the client will know which mode its ask for - SendingBuffer = $":{ServerDomain} {ResponseName.Mode} * {_result.ChannelName} {_result.ChannelModes}\r\n"; - } - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/Channel/NamesResponse.cs b/src/Servers/Chat/src/Contract/Response/Channel/NamesResponse.cs deleted file mode 100755 index 901541eff..000000000 --- a/src/Servers/Chat/src/Contract/Response/Channel/NamesResponse.cs +++ /dev/null @@ -1,21 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Result.Channel; - -namespace UniSpy.Server.Chat.Contract.Response.Channel -{ - public sealed class NamesResponse : ChannelResponseBase - { - private new NamesRequest _request => (NamesRequest)base._request; - private new NamesResult _result => (NamesResult)base._result; - public NamesResponse(NamesRequest request, NamesResult result) : base(request, result) { } - public override void Build() - { - SendingBuffer = $":{ServerDomain} {ResponseName.NameReply} {_result.RequesterNickName} = {_result.ChannelName} :{_result.AllChannelUserNicks}\r\n"; - - SendingBuffer += $":{ServerDomain} {ResponseName.EndOfNames} {_result.RequesterNickName} {_result.ChannelName} :End of NAMES list.\r\n"; - - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/Channel/PartResponse.cs b/src/Servers/Chat/src/Contract/Response/Channel/PartResponse.cs deleted file mode 100755 index 2f27b39b1..000000000 --- a/src/Servers/Chat/src/Contract/Response/Channel/PartResponse.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; - -namespace UniSpy.Server.Chat.Contract.Response.Channel -{ - public sealed class PartResponse : ResponseBase - { - private new PartRequest _request => (PartRequest)base._request; - private new PartResult _result => (PartResult)base._result; - public PartResponse(PartRequest request, PartResult result) : base(request, result) { } - - public override void Build() - { - SendingBuffer = $":{_result.LeaverIRCPrefix} {ResponseName.Part} {_result.ChannelName} :{_request.Reason ?? "Unknown reason"}\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/Channel/SetCKeyResponse.cs b/src/Servers/Chat/src/Contract/Response/Channel/SetCKeyResponse.cs deleted file mode 100755 index 153a6ceee..000000000 --- a/src/Servers/Chat/src/Contract/Response/Channel/SetCKeyResponse.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.Channel; - -namespace UniSpy.Server.Chat.Contract.Response.Channel -{ - public sealed class SetCKeyResponse : ResponseBase - { - private new SetCKeyRequest _request => (SetCKeyRequest)base._request; - public SetCKeyResponse(SetCKeyRequest request) : base(request, null) { } - public override void Build() - { - // the broadcast message must contain keys and values. - SendingBuffer = $":{ServerDomain} {ResponseName.GetCKey} * {_request.ChannelName} {_request.NickName} {_request.Cookie} {_request.KeyValueString}\r\n"; - - SendingBuffer += $":{ServerDomain} {ResponseName.EndGetCKey} * {_request.ChannelName} {_request.Cookie} :End Of SETCKEY.\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/Channel/SetChannelKeyResponse.cs b/src/Servers/Chat/src/Contract/Response/Channel/SetChannelKeyResponse.cs deleted file mode 100755 index 7c773050d..000000000 --- a/src/Servers/Chat/src/Contract/Response/Channel/SetChannelKeyResponse.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; - -namespace UniSpy.Server.Chat.Contract.Response.Channel -{ - public sealed class SetChannelKeyResponse : ChannelResponseBase - { - private new SetChannelKeyRequest _request => (SetChannelKeyRequest)base._request; - private new SetChannelKeyResult _result => (SetChannelKeyResult)base._result; - public SetChannelKeyResponse(SetChannelKeyRequest request, SetChannelKeyResult result) : base(request, result) { } - - public override void Build() - { - // the broadcast message must contain key and values. - SendingBuffer = $":{_result.ChannelUserIRCPrefix} {ResponseName.GetChanKey} * {_request.ChannelName} BCAST {_request.KeyValueString}\r\n"; - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Contract/Response/Channel/TopicResponse.cs b/src/Servers/Chat/src/Contract/Response/Channel/TopicResponse.cs deleted file mode 100755 index 0cd53a854..000000000 --- a/src/Servers/Chat/src/Contract/Response/Channel/TopicResponse.cs +++ /dev/null @@ -1,26 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; - -namespace UniSpy.Server.Chat.Contract.Response.Channel -{ - public sealed class TopicResponse : ResponseBase - { - private new TopicRequest _request => (TopicRequest)base._request; - private new TopicResult _result => (TopicResult)base._result; - public TopicResponse(TopicRequest request, TopicResult result) : base(request, result) { } - - public override void Build() - { - if (_result.ChannelTopic == "" || _result.ChannelTopic is null) - { - SendingBuffer = $":{ServerDomain} {ResponseName.NoTopic} * {_result.ChannelName}\r\n"; - } - else - { - SendingBuffer = $":{ServerDomain} {ResponseName.Topic} * {_result.ChannelName} :{_result.ChannelTopic}\r\n"; - } - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/General/CdKeyResponse.cs b/src/Servers/Chat/src/Contract/Response/General/CdKeyResponse.cs deleted file mode 100755 index 59fcb1f28..000000000 --- a/src/Servers/Chat/src/Contract/Response/General/CdKeyResponse.cs +++ /dev/null @@ -1,16 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; - -namespace UniSpy.Server.Chat.Contract.Response.General -{ - public sealed class CdKeyResponse : ResponseBase - { - public CdKeyResponse(RequestBase request, ResultBase result) : base(request, result) { } - - public override void Build() - { - //CDKey is always true - SendingBuffer = $":{ServerDomain} {ResponseName.CDKey} * 1 :Authenticated \r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/General/CryptResponse.cs b/src/Servers/Chat/src/Contract/Response/General/CryptResponse.cs deleted file mode 100755 index a53507e40..000000000 --- a/src/Servers/Chat/src/Contract/Response/General/CryptResponse.cs +++ /dev/null @@ -1,16 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Result.General; - -namespace UniSpy.Server.Chat.Contract.Response.General -{ - public sealed class CryptResponse : ResponseBase - { - private new CryptResult _result => (CryptResult)base._result; - public CryptResponse(RequestBase request, ResultBase result) : base(request, result) { } - public override void Build() - { - SendingBuffer = $":{ServerDomain} {ResponseName.SecureKey} * {ChatCrypt.ClientKey} {ChatCrypt.ServerKey}\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/General/GetKeyResponse.cs b/src/Servers/Chat/src/Contract/Response/General/GetKeyResponse.cs deleted file mode 100755 index 0463a8903..000000000 --- a/src/Servers/Chat/src/Contract/Response/General/GetKeyResponse.cs +++ /dev/null @@ -1,24 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Result.General; - -namespace UniSpy.Server.Chat.Contract.Response.General -{ - public sealed class GetKeyResponse : ResponseBase - { - private new GetKeyResult _result => (GetKeyResult)base._result; - private new GetKeyRequest _request => (GetKeyRequest)base._request; - public GetKeyResponse(RequestBase request, ResultBase result) : base(request, result) { } - - public override void Build() - { - SendingBuffer = ""; - foreach (var value in _result.Values) - { - SendingBuffer += $":{ServerDomain} {ResponseName.GetKey} * {_result.NickName} {_request.Cookie} {value}\r\n"; - } - SendingBuffer += $":{ServerDomain} {ResponseName.EndGetKey} * {_request.Cookie} * :End of GETKEY.\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/General/ListResponse.cs b/src/Servers/Chat/src/Contract/Response/General/ListResponse.cs deleted file mode 100755 index b18eb561f..000000000 --- a/src/Servers/Chat/src/Contract/Response/General/ListResponse.cs +++ /dev/null @@ -1,21 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Result.General; - -namespace UniSpy.Server.Chat.Contract.Response.General -{ - public sealed class ListResponse : ResponseBase - { - private new ListResult _result => (ListResult)base._result; - public ListResponse(RequestBase request, ResultBase result) : base(request, result) { } - public override void Build() - { - SendingBuffer = ""; - foreach (var info in _result.ChannelInfoList) - { - SendingBuffer += $":{_result.UserIRCPrefix} {ResponseName.ListStart} * {info.ChannelName} {info.TotalChannelUsers} {info.ChannelTopic}\r\n"; - } - SendingBuffer += $":{_result.UserIRCPrefix} {ResponseName.ListEnd}\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/General/LoginResponse.cs b/src/Servers/Chat/src/Contract/Response/General/LoginResponse.cs deleted file mode 100755 index 5c057c043..000000000 --- a/src/Servers/Chat/src/Contract/Response/General/LoginResponse.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Result.General; - -namespace UniSpy.Server.Chat.Contract.Response.General -{ - public sealed class LoginResponse : ResponseBase - { - private new LoginResult _result => (LoginResult)base._result; - public LoginResponse(RequestBase request, ResultBase result) : base(request, result) { } - - public override void Build() - { - SendingBuffer = $":{ServerDomain} {ResponseName.Login} * {_result.UserID} {_result.ProfileId}\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/General/NickResponse.cs b/src/Servers/Chat/src/Contract/Response/General/NickResponse.cs deleted file mode 100755 index ca5545986..000000000 --- a/src/Servers/Chat/src/Contract/Response/General/NickResponse.cs +++ /dev/null @@ -1,16 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Result.General; - -namespace UniSpy.Server.Chat.Contract.Response.General -{ - public sealed class NickResponse : ResponseBase - { - private new NickResult _result => (NickResult)base._result; - public NickResponse(RequestBase request, ResultBase result) : base(request, result) { } - public override void Build() - { - SendingBuffer = $":{ServerDomain} {ResponseName.Welcome} {_result.NickName} :Welcome to UniSpy!\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/General/PingResponse.cs b/src/Servers/Chat/src/Contract/Response/General/PingResponse.cs deleted file mode 100755 index 7912dfee5..000000000 --- a/src/Servers/Chat/src/Contract/Response/General/PingResponse.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Result.General; - -namespace UniSpy.Server.Chat.Contract.Response.General -{ - public sealed class PingResponse : ResponseBase - { - private new PingResult _result => (PingResult)base._result; - public PingResponse(RequestBase request, ResultBase result) : base(request, result) { } - - public override void Build() - { - SendingBuffer = $":{_result.RequesterIRCPrefix} {ResponseName.Pong}\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/General/UserIPResponse.cs b/src/Servers/Chat/src/Contract/Response/General/UserIPResponse.cs deleted file mode 100755 index 9476d62e4..000000000 --- a/src/Servers/Chat/src/Contract/Response/General/UserIPResponse.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Result.General; - -namespace UniSpy.Server.Chat.Contract.Response.General -{ - public sealed class UserIPResponse : ResponseBase - { - private new UserIPResult _result => (UserIPResult)base._result; - public UserIPResponse(RequestBase request, ResultBase result) : base(request, result) { } - - public override void Build() - { - SendingBuffer = $":{ServerDomain} {ResponseName.UserIP} :@{_result.RemoteIPAddress}\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/General/WhoIsResponse.cs b/src/Servers/Chat/src/Contract/Response/General/WhoIsResponse.cs deleted file mode 100755 index d671090fc..000000000 --- a/src/Servers/Chat/src/Contract/Response/General/WhoIsResponse.cs +++ /dev/null @@ -1,31 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Result.General; -using System.Linq; - -namespace UniSpy.Server.Chat.Contract.Response.General -{ - public sealed class WhoIsResponse : ResponseBase - { - private new WhoIsResult _result => (WhoIsResult)base._result; - public WhoIsResponse(RequestBase request, ResultBase result) : base(request, result) { } - public override void Build() - { - SendingBuffer = $":{ServerDomain} {ResponseName.WhoIsUser} {_result.NickName} {_result.Name} {_result.UserName} {_result.PublicIPAddress} * :{_result.UserName}\r\n"; - - if (_result.JoinedChannelName.Count() != 0) - { - string channelNames = ""; - //todo remove last space - foreach (var name in _result.JoinedChannelName) - { - channelNames += $"@{name} "; - } - - SendingBuffer += $":{ServerDomain} {ResponseName.WhoIsChannels} {_result.NickName} {_result.Name} :{channelNames}\r\n"; - } - - SendingBuffer += $":{ServerDomain} {ResponseName.EndOfWhoIs} {_result.NickName} {_result.Name} :End of WHOIS list.\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/General/WhoResponse.cs b/src/Servers/Chat/src/Contract/Response/General/WhoResponse.cs deleted file mode 100755 index 7f9ac50a8..000000000 --- a/src/Servers/Chat/src/Contract/Response/General/WhoResponse.cs +++ /dev/null @@ -1,38 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Result.General; - -namespace UniSpy.Server.Chat.Contract.Response.General -{ - public sealed class WhoResponse : ResponseBase - { - private new WhoRequest _request => (WhoRequest)base._request; - private new WhoResult _result => (WhoResult)base._result; - public WhoResponse(RequestBase request, ResultBase result) : base(request, result) { } - public override void Build() - { - SendingBuffer = ""; - foreach (var data in _result.DataModels) - { - SendingBuffer += $":{ServerDomain} {ResponseName.WhoReply} * {data.ChannelName} {data.UserName} {data.PublicIPAddress} * {data.NickName} {data.Modes} *\r\n"; - } - switch (_request.RequestType) - { - case WhoRequestType.GetChannelUsersInfo: - if (_result.DataModels.Count > 0) - { - SendingBuffer += $":{ServerDomain} {ResponseName.EndOfWho} * {_request.ChannelName} * :End of WHO.\r\n"; - } - break; - case WhoRequestType.GetUserInfo: - if (_result.DataModels.Count > 0) - { - SendingBuffer += $":{ServerDomain} {ResponseName.EndOfWho} * {_request.NickName} * :End of WHO.\r\n"; - } - break; - } - } - } -} - diff --git a/src/Servers/Chat/src/Contract/Response/Message/AtmResponse.cs b/src/Servers/Chat/src/Contract/Response/Message/AtmResponse.cs deleted file mode 100755 index 914b451a0..000000000 --- a/src/Servers/Chat/src/Contract/Response/Message/AtmResponse.cs +++ /dev/null @@ -1,18 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Result.Message; - -namespace UniSpy.Server.Chat.Contract.Response.Message -{ - public sealed class AtmResponse : ResponseBase - { - private new MessageRequestBase _request => (MessageRequestBase)base._request; - private new AtmResult _result => (AtmResult)base._result; - public AtmResponse(RequestBase request, ResultBase result) : base(request, result) { } - - public override void Build() - { - SendingBuffer = $":{_result.UserIRCPrefix} {ResponseName.AboveTheTableMsg} {_result.TargetName} :{_request.Message}\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/Message/NoticeResponse.cs b/src/Servers/Chat/src/Contract/Response/Message/NoticeResponse.cs deleted file mode 100755 index 27e540ef9..000000000 --- a/src/Servers/Chat/src/Contract/Response/Message/NoticeResponse.cs +++ /dev/null @@ -1,18 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.Message; -using UniSpy.Server.Chat.Contract.Result.Message; - -namespace UniSpy.Server.Chat.Contract.Response.Message -{ - public sealed class NoticeResponse : ResponseBase - { - private new NoticeResult _result => (NoticeResult)base._result; - private new NoticeRequest _request => (NoticeRequest)base._request; - public NoticeResponse(RequestBase request, ResultBase result) : base(request, result){ } - public override void Build() - { - SendingBuffer = $":{_result.UserIRCPrefix} {ResponseName.Notice} {_result.TargetName} :{_request.Message}\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/Message/PrivateResponse.cs b/src/Servers/Chat/src/Contract/Response/Message/PrivateResponse.cs deleted file mode 100755 index b11507774..000000000 --- a/src/Servers/Chat/src/Contract/Response/Message/PrivateResponse.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.Message; -using UniSpy.Server.Chat.Contract.Result.Message; - -namespace UniSpy.Server.Chat.Contract.Response.Message -{ - public sealed class PrivateResponse : ResponseBase - { - private new PrivateResult _result => (PrivateResult)base._result; - private new PrivateRequest _request => (PrivateRequest)base._request; - public PrivateResponse(RequestBase request, ResultBase result) : base(request, result) { } - - public override void Build() - { - SendingBuffer = $":{_result.UserIRCPrefix} {ResponseName.PrivateMsg} {_result.TargetName} :{_request.Message}\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Response/Message/UtmResponse.cs b/src/Servers/Chat/src/Contract/Response/Message/UtmResponse.cs deleted file mode 100755 index 3ce159fc8..000000000 --- a/src/Servers/Chat/src/Contract/Response/Message/UtmResponse.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.Message; -using UniSpy.Server.Chat.Contract.Result.Message; - -namespace UniSpy.Server.Chat.Contract.Response.Message -{ - public sealed class UtmResponse : ResponseBase - { - private new UtmResult _result => (UtmResult)base._result; - private new UtmRequest _request => (UtmRequest)base._request; - public UtmResponse(RequestBase request, ResultBase result) : base(request, result) { } - - public override void Build() - { - SendingBuffer = $":{_result.UserIRCPrefix} {ResponseName.UnderTheTableMsg} {_result.TargetName} :{_request.Message}\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/Channel/GetCKeyResult.cs b/src/Servers/Chat/src/Contract/Result/Channel/GetCKeyResult.cs deleted file mode 100755 index 390a0dd03..000000000 --- a/src/Servers/Chat/src/Contract/Result/Channel/GetCKeyResult.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using System.Collections.Generic; - -namespace UniSpy.Server.Chat.Contract.Result.Channel -{ - public sealed class GetCKeyDataModel - { - public string NickName { get; set; } - public string UserValues { get; set; } - } - public sealed class GetCKeyResult : ResultBase - { - public List DataResults { get; } - public string ChannelName { get; set; } - public GetCKeyResult() - { - DataResults = new List(); - } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/Channel/GetChannelKeyResult.cs b/src/Servers/Chat/src/Contract/Result/Channel/GetChannelKeyResult.cs deleted file mode 100755 index 4d0f25909..000000000 --- a/src/Servers/Chat/src/Contract/Result/Channel/GetChannelKeyResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Contract.Result.Channel -{ - public sealed class GetChannelKeyResult : ResultBase - { - public string ChannelUserIRCPrefix { get; set; } - public string ChannelName { get; set; } - public string Values { get; set; } - public GetChannelKeyResult(){ } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/Channel/JoinResult.cs b/src/Servers/Chat/src/Contract/Result/Channel/JoinResult.cs deleted file mode 100755 index 4d493d630..000000000 --- a/src/Servers/Chat/src/Contract/Result/Channel/JoinResult.cs +++ /dev/null @@ -1,16 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Contract.Result.Channel -{ - public sealed class JoinResult : ResultBase - { - public string JoinerPrefix { get; set; } - public string JoinerNickName { get; set; } - public string AllChannelUserNicks { get; set; } - public string ChannelModes { get; set; } - - // public string ChannelModes { get; set; } - // public List NickNames { get; set; } - public JoinResult(){ } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/Channel/KickResult.cs b/src/Servers/Chat/src/Contract/Result/Channel/KickResult.cs deleted file mode 100755 index 999ddf37e..000000000 --- a/src/Servers/Chat/src/Contract/Result/Channel/KickResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Contract.Result.Channel -{ - public sealed class KickResult : ResultBase - { - public string ChannelName { get; set; } - public string KickerNickName { get; set; } - public string KickeeNickName { get; set; } - public string KickerIRCPrefix { get; set; } - public KickResult(){ } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/Channel/ModeResult.cs b/src/Servers/Chat/src/Contract/Result/Channel/ModeResult.cs deleted file mode 100755 index 944ab0de1..000000000 --- a/src/Servers/Chat/src/Contract/Result/Channel/ModeResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Contract.Result.Channel -{ - public sealed class ModeResult : ResultBase - { - public string ChannelModes { get; set; } - public string ChannelName { get; set; } - public string JoinerNickName { get; set; } - public ModeResult(){ } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/Channel/NamesResult.cs b/src/Servers/Chat/src/Contract/Result/Channel/NamesResult.cs deleted file mode 100755 index 72e7356dc..000000000 --- a/src/Servers/Chat/src/Contract/Result/Channel/NamesResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Contract.Result.Channel -{ - public sealed class NamesResult : ResultBase - { - public string AllChannelUserNicks { get; set; } - public string ChannelName { get; set; } - public string RequesterNickName { get; set; } - public NamesResult(){ } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/Channel/PartResult.cs b/src/Servers/Chat/src/Contract/Result/Channel/PartResult.cs deleted file mode 100755 index 70bdf1449..000000000 --- a/src/Servers/Chat/src/Contract/Result/Channel/PartResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Contract.Result.Channel -{ - public sealed class PartResult : ResultBase - { - public string LeaverIRCPrefix { get; set; } - public bool IsChannelCreator { get; set; } - public string ChannelName { get; set; } - public PartResult() { } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/Channel/SetChannelKeyResult.cs b/src/Servers/Chat/src/Contract/Result/Channel/SetChannelKeyResult.cs deleted file mode 100755 index e0388c398..000000000 --- a/src/Servers/Chat/src/Contract/Result/Channel/SetChannelKeyResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Contract.Result.Channel -{ - public sealed class SetChannelKeyResult : ResultBase - { - public string ChannelUserIRCPrefix { get; set; } - public string ChannelName { get; set; } - public SetChannelKeyResult(){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Contract/Result/Channel/TopicResult.cs b/src/Servers/Chat/src/Contract/Result/Channel/TopicResult.cs deleted file mode 100755 index 2b15af961..000000000 --- a/src/Servers/Chat/src/Contract/Result/Channel/TopicResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Contract.Result.Channel -{ - public sealed class TopicResult : ResultBase - { - public string ChannelName { get; set; } - public string ChannelTopic { get; set; } - - public TopicResult(){ } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/General/CryptResult.cs b/src/Servers/Chat/src/Contract/Result/General/CryptResult.cs deleted file mode 100755 index 3d2556fca..000000000 --- a/src/Servers/Chat/src/Contract/Result/General/CryptResult.cs +++ /dev/null @@ -1,9 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Contract.Result.General -{ - public sealed class CryptResult : ResultBase - { - public CryptResult(){ } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/General/GetKeyResult.cs b/src/Servers/Chat/src/Contract/Result/General/GetKeyResult.cs deleted file mode 100755 index 87b4faad3..000000000 --- a/src/Servers/Chat/src/Contract/Result/General/GetKeyResult.cs +++ /dev/null @@ -1,18 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using System.Collections.Generic; - -namespace UniSpy.Server.Chat.Contract.Result.General -{ - public sealed class GetKeyResult : ResultBase - { - public List Values { get; } - /// - /// The reciever's nick name - /// - public string NickName { get; set; } - public GetKeyResult() - { - Values = new List(); - } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/General/ListResult.cs b/src/Servers/Chat/src/Contract/Result/General/ListResult.cs deleted file mode 100755 index ac50acd5b..000000000 --- a/src/Servers/Chat/src/Contract/Result/General/ListResult.cs +++ /dev/null @@ -1,22 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using System.Collections.Generic; - -namespace UniSpy.Server.Chat.Contract.Result.General -{ - public sealed class ListDataModel - { - public string ChannelName { get; set; } - public int TotalChannelUsers { get; set; } - public string ChannelTopic { get; set; } - } - - public sealed class ListResult : ResultBase - { - public string UserIRCPrefix { get; set; } - public List ChannelInfoList { get; } - public ListResult() - { - ChannelInfoList = new List(); - } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/General/LoginResult.cs b/src/Servers/Chat/src/Contract/Result/General/LoginResult.cs deleted file mode 100755 index c5649c8ad..000000000 --- a/src/Servers/Chat/src/Contract/Result/General/LoginResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Contract.Result.General -{ - public sealed class LoginResult : ResultBase - { - public int ProfileId { get; set; } - public int UserID { get; set; } - public LoginResult() { } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/General/NickResult.cs b/src/Servers/Chat/src/Contract/Result/General/NickResult.cs deleted file mode 100644 index 7304bd3e3..000000000 --- a/src/Servers/Chat/src/Contract/Result/General/NickResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Contract.Result.General -{ - public sealed class NickResult : ResultBase - { - public string NickName { get; set; } - public NickResult() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Contract/Result/General/PingResult.cs b/src/Servers/Chat/src/Contract/Result/General/PingResult.cs deleted file mode 100755 index b37b798cb..000000000 --- a/src/Servers/Chat/src/Contract/Result/General/PingResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Contract.Result.General -{ - public sealed class PingResult : ResultBase - { - public string RequesterIRCPrefix { get; set; } - public PingResult() - { } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/General/QuitResult.cs b/src/Servers/Chat/src/Contract/Result/General/QuitResult.cs deleted file mode 100755 index 74a235489..000000000 --- a/src/Servers/Chat/src/Contract/Result/General/QuitResult.cs +++ /dev/null @@ -1,26 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using System.Collections.Generic; - -namespace UniSpy.Server.Chat.Contract.Result.General -{ - public sealed record QuitDataModel - { - public string ChannelName { get; set; } - public bool IsPeerServer { get; set; } - public bool IsChannelCreator { get; set; } - public string LeaveReplySendingBuffer { get; set; } - public string KickReplySendingBuffer { get; set; } - } - - public sealed class QuitResult : ResultBase - { - public string QuiterPrefix { get; set; } - public List ChannelInfos { get; } - public string Message { get; set; } - - public QuitResult() - { - ChannelInfos = new List(); - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Contract/Result/General/UserIPResult.cs b/src/Servers/Chat/src/Contract/Result/General/UserIPResult.cs deleted file mode 100755 index e041b84af..000000000 --- a/src/Servers/Chat/src/Contract/Result/General/UserIPResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Net; -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Contract.Result.General -{ - public sealed class UserIPResult : ResultBase - { - public IPAddress RemoteIPAddress { get; set; } - public UserIPResult(){ } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/General/WhoIsResult.cs b/src/Servers/Chat/src/Contract/Result/General/WhoIsResult.cs deleted file mode 100755 index fa67a6eba..000000000 --- a/src/Servers/Chat/src/Contract/Result/General/WhoIsResult.cs +++ /dev/null @@ -1,18 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using System.Collections.Generic; - -namespace UniSpy.Server.Chat.Contract.Result.General -{ - public sealed class WhoIsResult : ResultBase - { - public List JoinedChannelName { get; } - public string NickName { get; set; } - public string UserName { get; set; } - public string Name { get; set; } - public string PublicIPAddress { get; set; } - public WhoIsResult() - { - JoinedChannelName = new List(); - } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/General/WhoResult.cs b/src/Servers/Chat/src/Contract/Result/General/WhoResult.cs deleted file mode 100755 index 926d0acae..000000000 --- a/src/Servers/Chat/src/Contract/Result/General/WhoResult.cs +++ /dev/null @@ -1,22 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using System.Collections.Generic; - -namespace UniSpy.Server.Chat.Contract.Result.General -{ - public sealed class WhoDataModel - { - public string ChannelName { get; set; } - public string UserName { get; set; } - public string PublicIPAddress { get; set; } - public string NickName { get; set; } - public string Modes { get; set; } - } - public sealed class WhoResult : ResultBase - { - public List DataModels { get; } - public WhoResult() - { - DataModels = new List(); - } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/Message/AtmResult.cs b/src/Servers/Chat/src/Contract/Result/Message/AtmResult.cs deleted file mode 100755 index 5e18158e2..000000000 --- a/src/Servers/Chat/src/Contract/Result/Message/AtmResult.cs +++ /dev/null @@ -1,9 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass.Message; - -namespace UniSpy.Server.Chat.Contract.Result.Message -{ - public sealed class AtmResult : MessageResultBase - { - public AtmResult(){ } - } -} diff --git a/src/Servers/Chat/src/Contract/Result/Message/NoticeResult.cs b/src/Servers/Chat/src/Contract/Result/Message/NoticeResult.cs deleted file mode 100755 index e20cab2c1..000000000 --- a/src/Servers/Chat/src/Contract/Result/Message/NoticeResult.cs +++ /dev/null @@ -1,9 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass.Message; - -namespace UniSpy.Server.Chat.Contract.Result.Message -{ - public sealed class NoticeResult : MessageResultBase - { - public NoticeResult(){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Contract/Result/Message/PrivateResult.cs b/src/Servers/Chat/src/Contract/Result/Message/PrivateResult.cs deleted file mode 100755 index f6432330d..000000000 --- a/src/Servers/Chat/src/Contract/Result/Message/PrivateResult.cs +++ /dev/null @@ -1,10 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass.Message; - -namespace UniSpy.Server.Chat.Contract.Result.Message -{ - public sealed class PrivateResult : MessageResultBase - { - public bool IsBroadcastMessage { get; set; } - public PrivateResult(){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Contract/Result/Message/UtmResult.cs b/src/Servers/Chat/src/Contract/Result/Message/UtmResult.cs deleted file mode 100755 index 3a0df9044..000000000 --- a/src/Servers/Chat/src/Contract/Result/Message/UtmResult.cs +++ /dev/null @@ -1,9 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass.Message; - -namespace UniSpy.Server.Chat.Contract.Result.Message -{ - public sealed class UtmResult : MessageResultBase - { - public UtmResult(){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Dockerfile b/src/Servers/Chat/src/Dockerfile deleted file mode 100755 index 311578d34..000000000 --- a/src/Servers/Chat/src/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base -WORKDIR /app -EXPOSE 6667 - -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build -WORKDIR /src -COPY ["src/Servers/Chat/src/UniSpy.Server.Chat.csproj", "src/Servers/Chat/src/"] -COPY ["src/Libraries/Core/src/UniSpy.Server.Core.csproj", "src/Libraries/Core/src/"] -COPY ["src/Servers/QueryReport/src/UniSpy.Server.QueryReport.csproj", "src/Servers/QueryReport/src/"] -RUN dotnet restore "src/Servers/Chat/src/UniSpy.Server.Chat.csproj" -COPY . . -WORKDIR "/src/src/Servers/Chat/src" -RUN dotnet build "UniSpy.Server.Chat.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "UniSpy.Server.Chat.csproj" -c Release -o /app/publish - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "UniSpy.Server.Chat.dll"] \ No newline at end of file diff --git a/src/Servers/Chat/src/Enumerate/ChatChannelMode.cs b/src/Servers/Chat/src/Enumerate/ChatChannelMode.cs deleted file mode 100755 index 0ea13a575..000000000 --- a/src/Servers/Chat/src/Enumerate/ChatChannelMode.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace UniSpy.Server.Chat.Enumerate -{ - public enum ChatChannelMode - { - InviteOnly, - Private, - Secret, - Moderated, - NoExternalMessages, - OnlyOpsChangeTopic, - OpsObeyChannelLimit, - } -} diff --git a/src/Servers/Chat/src/Enumerate/ChatMode.cs b/src/Servers/Chat/src/Enumerate/ChatMode.cs deleted file mode 100755 index fafb1073b..000000000 --- a/src/Servers/Chat/src/Enumerate/ChatMode.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace UniSpy.Server.Chat.Enumerate -{ - public enum ChatMode : int - { - End = 0, - Ban = 1, - InviteOnly = 2, - Limit = 3, - Private = 4, - Secret = 5, - Key = 6, - ModeRated = 7, - NoExternalMessages = 8, - OlnyOPSChangeTopic = 9, - OP = 10, - Voice = 11, - UsersHidden = 12, - ReceiveWallOPS = 13, - OPSObeyChannelLimit = 14, - } -} diff --git a/src/Servers/Chat/src/Enumerate/Request/ChatRequest.cs b/src/Servers/Chat/src/Enumerate/Request/ChatRequest.cs deleted file mode 100755 index e9e3b3266..000000000 --- a/src/Servers/Chat/src/Enumerate/Request/ChatRequest.cs +++ /dev/null @@ -1,36 +0,0 @@ -namespace UniSpy.Server.Chat.Enumerate.Request -{ - public enum ChatRequestType - { - CRYPT, - USRIP, - NICK, - USER, - LOGINPREAUTH, - LOGIN, - REGISTERNICK, - QUIT, - MODE, - CDKEY, - LIST, - LISTLIMIT, - JOIN, - PART, - PRIVMSG, - NOTICE, - UTM, - ATM, - TOPIC, - SETGROUP, - NAMES, - WHOIS, - WHO, - INVITE, - KICK, - GETUDPRELAY, - SETCHANKEY, - SETCKEY, - SETKEY, - PONG, - } -} diff --git a/src/Servers/Chat/src/Enumerate/Request/ChatServerMessage.cs b/src/Servers/Chat/src/Enumerate/Request/ChatServerMessage.cs deleted file mode 100755 index bb5569cac..000000000 --- a/src/Servers/Chat/src/Enumerate/Request/ChatServerMessage.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace UniSpy.Server.Chat.Enumerate -{ - public enum ChatServerMessage - { - PRIVMSG, - NOTICE, - UTM, - ATM, - PING, - NICK, - JOIN, - PART, - KICK, - QUIT, - KILL, - TOPIC, - MODE, - ERROR, - INVITE - } -} diff --git a/src/Servers/Chat/src/Enumerate/Response/ChatErrorCode.cs b/src/Servers/Chat/src/Enumerate/Response/ChatErrorCode.cs deleted file mode 100755 index fc2583860..000000000 --- a/src/Servers/Chat/src/Enumerate/Response/ChatErrorCode.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace UniSpy.Server.Chat.Enumerate -{ - public enum ChatErrorCode - { - Parse, - DataOperation, - ConstructResponse, - NotChannelOperator, - UserAlreadyInChannel, - UnSupportedGame, - NoError, - IRCError, - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/Exception.cs b/src/Servers/Chat/src/Exception/Exception.cs deleted file mode 100755 index e2c093084..000000000 --- a/src/Servers/Chat/src/Exception/Exception.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace UniSpy.Server.Chat -{ - public class Exception : UniSpy.Exception - { - public Exception() { } - - public Exception(string message) : base(message) { } - - public Exception(string message, System.Exception innerException) : base(message, innerException) { } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRC/Channel/BadChanMaskException.cs b/src/Servers/Chat/src/Exception/IRC/Channel/BadChanMaskException.cs deleted file mode 100755 index 9e9037153..000000000 --- a/src/Servers/Chat/src/Exception/IRC/Channel/BadChanMaskException.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Misc; - -namespace UniSpy.Server.Chat.Error.IRC.Channel -{ - public sealed class BadChanMaskException : IRCChannelException - { - public BadChanMaskException(){ } - - public BadChanMaskException(string message, string channelName) : base(message, IRCErrorCode.BadChanMask, channelName){ } - - public BadChanMaskException(string message, string channelName, System.Exception innerException) : base(message, IRCErrorCode.BadChanMask, channelName, innerException){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRC/Channel/BadChannelKeyException.cs b/src/Servers/Chat/src/Exception/IRC/Channel/BadChannelKeyException.cs deleted file mode 100755 index 7864eaf87..000000000 --- a/src/Servers/Chat/src/Exception/IRC/Channel/BadChannelKeyException.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Misc; - -namespace UniSpy.Server.Chat.Error.IRC.Channel -{ - public sealed class BadChannelKeyException : IRCChannelException - { - public BadChannelKeyException(){ } - - public BadChannelKeyException(string message, string channelName) : base(message, IRCErrorCode.BadChannelKey, channelName){ } - - public BadChannelKeyException(string message, string channelName, System.Exception innerException) : base(message, IRCErrorCode.BadChannelKey, channelName, innerException){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRC/Channel/BannedFromChanException.cs b/src/Servers/Chat/src/Exception/IRC/Channel/BannedFromChanException.cs deleted file mode 100755 index dd56eca63..000000000 --- a/src/Servers/Chat/src/Exception/IRC/Channel/BannedFromChanException.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Misc; - -namespace UniSpy.Server.Chat.Error.IRC.Channel -{ - public sealed class BannedFromChanException : IRCChannelException - { - public BannedFromChanException(string message, string channelName) : base(message, IRCErrorCode.BannedFromChan, channelName){ } - - public BannedFromChanException(string message, string channelName, System.Exception innerException) : base(message, IRCErrorCode.BannedFromChan, channelName, innerException){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRC/Channel/ChannelIsFullException.cs b/src/Servers/Chat/src/Exception/IRC/Channel/ChannelIsFullException.cs deleted file mode 100755 index d7a6eaf6f..000000000 --- a/src/Servers/Chat/src/Exception/IRC/Channel/ChannelIsFullException.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Misc; - -namespace UniSpy.Server.Chat.Error.IRC.Channel -{ - public sealed class ChannelIsFullException : IRCChannelException - { - public ChannelIsFullException(string message, string channelName) : base(message, IRCErrorCode.ChannelIsFull, channelName){ } - - public ChannelIsFullException(string message, string channelName, System.Exception innerException) : base(message, IRCErrorCode.ChannelIsFull, channelName, innerException){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRC/Channel/InviteOnlyChanException.cs b/src/Servers/Chat/src/Exception/IRC/Channel/InviteOnlyChanException.cs deleted file mode 100755 index 7dec4bcce..000000000 --- a/src/Servers/Chat/src/Exception/IRC/Channel/InviteOnlyChanException.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Misc; - -namespace UniSpy.Server.Chat.Error.IRC.Channel -{ - public sealed class InviteOnlyChanException : IRCChannelException - { - public InviteOnlyChanException(){ } - - public InviteOnlyChanException(string message, string channelName) : base(message, IRCErrorCode.InviteOnlyChan, channelName){ } - - public InviteOnlyChanException(string message, string channelName, System.Exception innerException) : base(message, IRCErrorCode.InviteOnlyChan, channelName, innerException){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRC/Channel/NoSuchChannelException.cs b/src/Servers/Chat/src/Exception/IRC/Channel/NoSuchChannelException.cs deleted file mode 100755 index 8a65b203d..000000000 --- a/src/Servers/Chat/src/Exception/IRC/Channel/NoSuchChannelException.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Misc; - -namespace UniSpy.Server.Chat.Error.IRC.Channel -{ - public sealed class NoSuchChannelException : IRCChannelException - { - public NoSuchChannelException(){ } - - public NoSuchChannelException(string message, string channelName) : base(message, IRCErrorCode.NoSuchChannel, channelName){ } - - public NoSuchChannelException(string message, string channelName, System.Exception innerException) : base(message, IRCErrorCode.NoSuchChannel, channelName, innerException){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRC/General/ErrOneUSNickNameException.cs b/src/Servers/Chat/src/Exception/IRC/General/ErrOneUSNickNameException.cs deleted file mode 100755 index 70367b383..000000000 --- a/src/Servers/Chat/src/Exception/IRC/General/ErrOneUSNickNameException.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Misc; - -namespace UniSpy.Server.Chat.Error.IRC.General -{ - public sealed class ErrOneUSNickNameException : IRCException - { - public ErrOneUSNickNameException(){ } - - public ErrOneUSNickNameException(string message) : base(message, IRCErrorCode.ErrOneUSNickName){ } - - public ErrOneUSNickNameException(string message, System.Exception innerException) : base(message, IRCErrorCode.ErrOneUSNickName, innerException){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRC/General/LoginFailedException.cs b/src/Servers/Chat/src/Exception/IRC/General/LoginFailedException.cs deleted file mode 100755 index a38a4f98c..000000000 --- a/src/Servers/Chat/src/Exception/IRC/General/LoginFailedException.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Misc; - -namespace UniSpy.Server.Chat.Error.IRC.General -{ - public sealed class LoginFailedException : IRCException - { - public LoginFailedException(){ } - - public LoginFailedException(string message) : base(message, IRCErrorCode.LoginFailed){ } - - public LoginFailedException(string message, System.Exception innerException) : base(message, IRCErrorCode.LoginFailed, innerException){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRC/General/MoreParametersException.cs b/src/Servers/Chat/src/Exception/IRC/General/MoreParametersException.cs deleted file mode 100755 index f7eb151a0..000000000 --- a/src/Servers/Chat/src/Exception/IRC/General/MoreParametersException.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Misc; - -namespace UniSpy.Server.Chat.Error.IRC.General -{ - public sealed class MoreParametersException : IRCException - { - public MoreParametersException(){ } - - public MoreParametersException(string message) : base(message, IRCErrorCode.MoreParameters){ } - - public MoreParametersException(string message, System.Exception innerException) : base(message, IRCErrorCode.MoreParameters, innerException){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRC/General/NickNameInUseException.cs b/src/Servers/Chat/src/Exception/IRC/General/NickNameInUseException.cs deleted file mode 100755 index 2a0315443..000000000 --- a/src/Servers/Chat/src/Exception/IRC/General/NickNameInUseException.cs +++ /dev/null @@ -1,28 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; - -namespace UniSpy.Server.Chat.Error.IRC.General -{ - public sealed class NickNameInUseException : IRCException - { - private string _oldNickName; - private string _newNickName; - public NickNameInUseException() { } - - public NickNameInUseException(string message, string oldNick, string newNick) : base(message, IRCErrorCode.NickNameInUse) - { - _oldNickName = oldNick; - _newNickName = newNick; - } - - public NickNameInUseException(string message, string oldNick, string newNick, System.Exception innerException) : base(message, IRCErrorCode.NickNameInUse, innerException) - { - _oldNickName = oldNick; - _newNickName = newNick; - } - public override void Build() - { - SendingBuffer = $":{ResponseBase.ServerDomain} {IRCErrorCode.NickNameInUse} {_oldNickName} {_newNickName} *\r\n"; - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRC/General/NoSuchNickException.cs b/src/Servers/Chat/src/Exception/IRC/General/NoSuchNickException.cs deleted file mode 100755 index dc17ce106..000000000 --- a/src/Servers/Chat/src/Exception/IRC/General/NoSuchNickException.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Misc; - -namespace UniSpy.Server.Chat.Error.IRC.General -{ - public sealed class NoSuchNickException : IRCException - { - public NoSuchNickException(){ } - - public NoSuchNickException(string message) : base(message, IRCErrorCode.NoSuchNick){ } - - public NoSuchNickException(string message, System.Exception innerException) : base(message, IRCErrorCode.NoSuchNick, innerException){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRC/General/NoUniqueNickException.cs b/src/Servers/Chat/src/Exception/IRC/General/NoUniqueNickException.cs deleted file mode 100755 index 05f5c705f..000000000 --- a/src/Servers/Chat/src/Exception/IRC/General/NoUniqueNickException.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Misc; - -namespace UniSpy.Server.Chat.Error.IRC.General -{ - public sealed class NoUniqueNickException : IRCException - { - public NoUniqueNickException(){ } - - public NoUniqueNickException(string message) : base(message, IRCErrorCode.NoUniqueNick){ } - - public NoUniqueNickException(string message, System.Exception innerException) : base(message, IRCErrorCode.NoUniqueNick, innerException){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRC/General/RegisterNickFaildException.cs b/src/Servers/Chat/src/Exception/IRC/General/RegisterNickFaildException.cs deleted file mode 100755 index f4b101960..000000000 --- a/src/Servers/Chat/src/Exception/IRC/General/RegisterNickFaildException.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Misc; - -namespace UniSpy.Server.Chat.Error.IRC.General -{ - public sealed class RegisterNickFaildException : IRCException - { - public RegisterNickFaildException(){ } - - public RegisterNickFaildException(string message) : base(message, IRCErrorCode.RegisterNickFailed){ } - - public RegisterNickFaildException(string message, System.Exception innerException) : base(message, IRCErrorCode.RegisterNickFailed, innerException){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRC/General/TooManyChannels.cs b/src/Servers/Chat/src/Exception/IRC/General/TooManyChannels.cs deleted file mode 100755 index 389e49c2c..000000000 --- a/src/Servers/Chat/src/Exception/IRC/General/TooManyChannels.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Misc; -namespace UniSpy.Server.Chat.Error.IRC.General -{ - public sealed class TooManyChannels : IRCException - { - public TooManyChannels(){ } - - public TooManyChannels(string message) : base(message, IRCErrorCode.TooManyChannels){ } - - public TooManyChannels(string message, System.Exception innerException) : base(message, IRCErrorCode.TooManyChannels, innerException){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRC/General/UniqueNickExpiredException.cs b/src/Servers/Chat/src/Exception/IRC/General/UniqueNickExpiredException.cs deleted file mode 100755 index 50db8524e..000000000 --- a/src/Servers/Chat/src/Exception/IRC/General/UniqueNickExpiredException.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Misc; - -namespace UniSpy.Server.Chat.Error.IRC.General -{ - public sealed class UniqueNickExpiredException : IRCException - { - public UniqueNickExpiredException(){ } - - public UniqueNickExpiredException(string message) : base(message, IRCErrorCode.UniqueNIickExpired){ } - - public UniqueNickExpiredException(string message, System.Exception innerException) : base(message, IRCErrorCode.UniqueNIickExpired, innerException){ } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRCChannelException.cs b/src/Servers/Chat/src/Exception/IRCChannelException.cs deleted file mode 100755 index d1cc9bb00..000000000 --- a/src/Servers/Chat/src/Exception/IRCChannelException.cs +++ /dev/null @@ -1,23 +0,0 @@ -using UniSpy.Server.Chat.Error.IRC.General; -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat -{ - public class IRCChannelException : IRCException - { - public string ChannelName { get; private set; } - public IRCChannelException() { } - public IRCChannelException(string message, string errorCode, string channelName) : base(message, errorCode) - { - ChannelName = channelName; - } - public IRCChannelException(string message, string errorCode, string channelName, System.Exception innerException) : base(message, errorCode, innerException) - { - ChannelName = channelName; - } - public override void Build() - { - SendingBuffer = $":{ResponseBase.ServerDomain} {ErrorCode} * {ChannelName} :{Message}\r\n"; - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Exception/IRCException.cs b/src/Servers/Chat/src/Exception/IRCException.cs deleted file mode 100755 index efdd20da9..000000000 --- a/src/Servers/Chat/src/Exception/IRCException.cs +++ /dev/null @@ -1,27 +0,0 @@ -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Error.IRC.General -{ - public class IRCException : UniSpy.Exception, IResponse - { - public string ErrorCode { get; private set; } - public object SendingBuffer { get; protected set; } - - public IRCException() { } - - public IRCException(string message, string errorCode) : base(message) - { - ErrorCode = errorCode; - } - - public IRCException(string message, string errorCode, System.Exception innerException) : base(message, innerException) - { - ErrorCode = errorCode; - } - - public virtual void Build() - { - SendingBuffer = $":{Abstraction.BaseClass.ResponseBase.ServerDomain} {ErrorCode}\r\n"; - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetCKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetCKeyHandler.cs deleted file mode 100755 index 008676514..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetCKeyHandler.cs +++ /dev/null @@ -1,103 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Error.IRC.General; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Response.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; -using System; -using UniSpy.Server.Chat.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.Channel -{ - - public sealed class GetCKeyHandler : ChannelHandlerBase - { - private new GetCKeyRequest _request => (GetCKeyRequest)base._request; - private new GetCKeyResult _result { get => (GetCKeyResult)base._result; set => base._result = value; } - private TimeSpan _waitingTime = TimeSpan.FromSeconds(10); - public GetCKeyHandler(IShareClient client, GetCKeyRequest request) : base(client, request) - { - _result = new GetCKeyResult(); - } - - - protected override void DataOperation() - { - switch (_request.RequestType) - { - case GetKeyReqeustType.GetChannelAllUserKeyValue: - GetChannelAllUserKeyValue(); - break; - case GetKeyReqeustType.GetChannelSpecificUserKeyValue: - GetChannelSpecificUserKeyValue(); - break; - } - } - - private void GetChannelAllUserKeyValue() - { - foreach (var user in _channel.Users.Values) - { - GetUserKeyValue(user); - } - } - - private void GetChannelSpecificUserKeyValue() - { - var user = _channel.GetUser(_request.NickName); - if (user is null) - { - throw new NoSuchNickException($"Can not find user with nickname:{_request.NickName} in channels."); - } - GetUserKeyValue(user); - } - - private void GetUserKeyValue(ChannelUser user) - { - // if (user.BelongedChannel.RoomType == Aggregate.PeerRoomType.Staging) - // { - // WaittingForKey(user); - // } - // we get user's values - string userValues = user.KeyValues.GetValueString(_request.Keys); - var model = new GetCKeyDataModel - { - NickName = user.Client.Info.NickName, - UserValues = userValues - }; - _result.DataResults.Add(model); - } - - protected override void ResponseConstruct() - { - _response = new GetCKeyResponse(_request, _result); - } - /// - /// We do not publish message in GetCKey - /// - protected override void PublishMessage() { } - private void WaittingForKey(ChannelUser user) - { - // if user did not contains all key and value we wait for it - var startTime = DateTime.Now; - var elapsed = DateTime.Now - startTime; - while (elapsed < _waitingTime) - { - if (!user.KeyValues.IsContainAllKey(_request.Keys)) - { - elapsed = DateTime.Now - startTime; - // each detection we wait for 5 seconds. - System.Threading.Tasks.Task.Delay(TimeSpan.FromSeconds(5)); - } - else - { - break; - } - } - if (elapsed > _waitingTime) - { - throw new Chat.Exception($"No key value presents after waitting for user:{_request.NickName} channel:{_request.ChannelName}"); - } - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetChannelKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetChannelKeyHandler.cs deleted file mode 100755 index b3890f3d1..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/GetChannelKeyHandler.cs +++ /dev/null @@ -1,39 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Response.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.Channel -{ - // Sets channel key/values. - // If user is NULL or "", the keys will be set on the channel. - // Otherwise, they will be set on the user, - // Only ops can set channel keys on other users. - // Set a value to NULL or "" to clear that key. - - public sealed class GetChannelKeyHandler : ChannelHandlerBase - { - private new GetChannelKeyRequest _request => (GetChannelKeyRequest)base._request; - private new GetChannelKeyResult _result { get => (GetChannelKeyResult)base._result; set => base._result = value; } - public GetChannelKeyHandler(IShareClient client, GetChannelKeyRequest request) : base(client, request) - { - _result = new GetChannelKeyResult(); - } - - protected override void DataOperation() - { - _result.ChannelUserIRCPrefix = _user.Client.Info.IRCPrefix; - _result.Values = _channel.KeyValues.GetValueString(_request.Keys); - _result.ChannelName = _channel.Name; - } - /// - /// We do not publish message in GetChanKey - /// - protected override void PublishMessage() { } - protected override void ResponseConstruct() - { - _response = new GetChannelKeyResponse(_request, _result); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs deleted file mode 100755 index 12a645482..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/JoinHandler.cs +++ /dev/null @@ -1,147 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Error.IRC.General; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Response.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Error.IRC.Channel; -using System; -using UniSpy.Server.Chat.Aggregate; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.Channel -{ - /// - /// Game will only join one channel at one time - /// - - public sealed class JoinHandler : ChannelHandlerBase - { - private new JoinRequest _request => (JoinRequest)base._request; - private new JoinResult _result { get => (JoinResult)base._result; set => base._result = value; } - private new JoinResponse _response { get => (JoinResponse)base._response; set => base._response = value; } - public JoinHandler(IShareClient client, JoinRequest request) : base(client, request) - { - _result = new JoinResult(); - } - - //1.筛选出所要加入的频道,如果不存在则创建(select the channel that user want to join, if channel does not exist creating it) - //2.检查用户名nickname是否已经在频道中存在(check if user's nickname existed in channel) - //若存在则提醒用户名字冲突 - //不存在则加入频道 - //广播加入信息 - //发送频道模式给此用户 - //发送频道用户列表给此用户 - protected override void RequestCheck() - { - if (!_client.Info.IsLoggedIn) - { - new Chat.Exception($"{_client.Info.NickName} Please login first!"); - } - _request.Parse(); - //some GameSpy game only allow one player join one chat room - //but GameSpy Arcade can join more than one channel - if (_client.Info.JoinedChannels.Count > 3) - { - throw new TooManyChannels($"{_client.Info.NickName} is join too many channels"); - } - if (_client.Info.GameName == null) - { - throw new NoSuchChannelException("Game name is required for join a channel", _request.ChannelName); - } - } - - protected override void DataOperation() - { - // we acquire redis lock - // redis lock - var key = new Aggregate.Redis.ChannelCache - { - ChannelName = _request.ChannelName, - GameName = _client.Info.GameName - }; - using (var locker = new LinqToRedis.RedisLock(TimeSpan.FromSeconds(10), Application.StorageOperation.Persistance.ChannelCacheClient.Db, key)) - { - if (locker.LockTake()) - { - _channel = Aggregate.Channel.GetLocalChannel(_request.ChannelName); - // if local channel is null - if (_channel is null) - { - _channel = Aggregate.Channel.GetChannelCache(key); - } - // if remote channel is null - if (_channel is null) - { - // create channel - _channel = Aggregate.Channel.CreateLocalChannel(_request.ChannelName, _client, _request.Password); - // we need to check whether this channel is gamespy official channel - switch (_channel.RoomType) - { - case PeerRoomType.Title: - case PeerRoomType.Group: - _user = _channel.AddUser(_client, _request.Password ?? null); - break; - case PeerRoomType.Normal: - case PeerRoomType.Staging: - _user = _channel.AddUser(_client, _request.Password ?? null, true, true); - break; - } - } - else - { - _user = _channel.AddUser(_client, _request.Password ?? null); - } - base.UpdateChannelCache(); // <= we update the channel cache here - } - else - { - throw new BadChannelKeyException("The channel is created by other person, try to re-join this channel", _request.ChannelName); - } - } - - _result.AllChannelUserNicks = _channel.GetAllUsersNickString(); - _result.JoinerNickName = _client.Info.NickName; - _result.ChannelModes = _channel.Mode.ToString(); - _result.JoinerPrefix = _client.Info.IRCPrefix; - } - - protected override void UpdateChannelCache() - { - // we do not update channel cache again in base class because we update it when channel is created - } - protected override void ResponseConstruct() - { - _response = new JoinResponse(_request, _result); - } - - protected override void Response() - { - // base.Response(); - if (_response is null) - { - return; - } - - //first we send join information to all user in this channel - _channel.MultiCast(_client, _response); - - var namesRequest = new NamesRequest - { - ChannelName = _request.ChannelName - }; - var nameHandler = new NamesHandler(_client, namesRequest, _channel, _user); - nameHandler.Handle(); - var userModeRequest = new ModeRequest - { - RequestType = ModeRequestType.GetChannelAndUserModes, - ChannelName = _request.ChannelName, - NickName = _user.Client.Info.NickName, - UserName = _user.Client.Info.UserName, - Password = _request.Password is null ? null : _request.Password - }; - var modeHandler = new ModeHandler(_client, userModeRequest, _channel, _user); - modeHandler.Handle(); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/KickHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/KickHandler.cs deleted file mode 100755 index 10c1374d5..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/KickHandler.cs +++ /dev/null @@ -1,54 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Response.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.Channel -{ - - public sealed class KickHandler : ChannelHandlerBase - { - private new KickRequest _request => (KickRequest)base._request; - private new KickResponse _response { get => (KickResponse)base._response; set => base._response = value; } - private new KickResult _result { get => (KickResult)base._result; set => base._result = value; } - private ChannelUser _kickee; - public KickHandler(IShareClient client, KickRequest request) : base(client, request) - { - _result = new KickResult(); - } - - protected override void RequestCheck() - { - base.RequestCheck(); - - if (!_user.IsChannelOperator) - { - throw new Chat.Exception("The Kick operation failed, because you are not channel operator."); - } - _kickee = _channel.GetUser(_request.KickeeNickName); - if (_kickee is null) - { - throw new Chat.Exception($"Can not find kickee:{_request.KickeeNickName} in channel."); - } - } - protected override void DataOperation() - { - _result.ChannelName = _channel.Name; - _result.KickerNickName = _client.Info.NickName; - _result.KickerIRCPrefix = _client.Info.IRCPrefix; - _result.KickeeNickName = _kickee.Client.Info.NickName; - } - protected override void ResponseConstruct() - { - _response = new KickResponse(_request, _result); - } - - protected override void Response() - { - _channel.MultiCast(_client, _response); - _channel.RemoveUser(_kickee); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/ModeHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/ModeHandler.cs deleted file mode 100755 index 2b8625720..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/ModeHandler.cs +++ /dev/null @@ -1,62 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Response.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.Channel -{ - /// - /// Get or set channel or user mode - /// - - public sealed class ModeHandler : ChannelHandlerBase - { - private new ModeRequest _request => (ModeRequest)base._request; - private new ModeResult _result { get => (ModeResult)base._result; set => base._result = value; } - public ModeHandler(IShareClient client, ModeRequest request) : base(client, request) { } - public ModeHandler(IShareClient client, ModeRequest request, Aggregate.Channel channel, Aggregate.ChannelUser user) : base(client, request) - { - _user = user; - _channel = channel; - } - protected override void DataOperation() - { - _result = new ModeResult(); - switch (_request.RequestType) - { - // We get user nick name then get channel modes - case ModeRequestType.GetChannelAndUserModes: - _result.JoinerNickName = _request.NickName; - goto case ModeRequestType.GetChannelModes; - case ModeRequestType.GetChannelModes: - _result.ChannelModes = _channel.Mode.ToString(); - _result.ChannelName = _channel.Name; - break; - case ModeRequestType.SetChannelModes: - _channel.SetProperties(_user, _request); - break; - } - } - protected override void PublishMessage() - { - if (_request.RequestType == ModeRequestType.SetChannelModes) - { - base.PublishMessage(); - } - } - protected override void ResponseConstruct() - { - // we only response to get channel modes - switch (_request.RequestType) - { - case ModeRequestType.GetChannelAndUserModes: - case ModeRequestType.GetChannelModes: - _response = new ModeResponse(_request, _result); - break; - case ModeRequestType.SetChannelModes: - break; - } - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/NamesHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/NamesHandler.cs deleted file mode 100755 index de0e17bfa..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/NamesHandler.cs +++ /dev/null @@ -1,38 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Response.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; -using UniSpy.Server.Chat.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.Channel -{ - - public sealed class NamesHandler : ChannelHandlerBase - { - private new NamesRequest _request => (NamesRequest)base._request; - private new NamesResult _result { get => (NamesResult)base._result; set => base._result = value; } - public NamesHandler(IShareClient client, NamesRequest request) : base(client, request) { } - public NamesHandler(IShareClient client, NamesRequest request, Aggregate.Channel channel, Aggregate.ChannelUser user) : base(client, request) - { - _user = user; - _channel = channel; - } - - protected override void DataOperation() - { - _result = new NamesResult(); - _result.AllChannelUserNicks = _channel.GetAllUsersNickString(); - _result.ChannelName = _channel.Name; - _result.RequesterNickName = _user.Client.Info.NickName; - } - - /// - /// We do not publish message in names handler - /// - protected override void PublishMessage() { } - protected override void ResponseConstruct() - { - _response = new NamesResponse(_request, _result); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs deleted file mode 100755 index a03680abb..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/PartHandler.cs +++ /dev/null @@ -1,112 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Error.IRC.Channel; -using UniSpy.Server.Chat.Error.IRC.General; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Response.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; -using UniSpy.Server.Chat.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.Channel -{ - - public sealed class PartHandler : ChannelHandlerBase - { - private new PartRequest _request => (PartRequest)base._request; - private new PartResponse _response { get => (PartResponse)base._response; set => base._response = value; } - private new PartResult _result { get => (PartResult)base._result; set => base._result = value; } - public PartHandler(IShareClient client, PartRequest request) : base(client, request) { } - protected override void RequestCheck() - { - if (_request.RawRequest is null) - { - _channel = _client.Info.GetLocalJoinedChannel(_request.ChannelName); - if (_channel is null) - { - throw new NoSuchChannelException($"No such channel {_request.ChannelName}", _request.ChannelName); - } - _user = _channel.GetUser(_client); - if (_user is null) - { - throw new NoSuchNickException($"Can not find user with nickname: {_client.Info.NickName} username: {_client.Info.UserName}"); - } - } - else - { - base.RequestCheck(); - } - } - protected override void DataOperation() - { - _result = new PartResult(); - _result.LeaverIRCPrefix = _user.Client.Info.IRCPrefix; - _result.ChannelName = _channel.Name; - switch (_channel.RoomType) - { - case PeerRoomType.Normal: - case PeerRoomType.Staging: - KickAllUser(); - goto default; - default: - // we need always remove the connection in leaver and channel - _channel.RemoveUser(_user); - break; - } - } - private void KickAllUser() - { - if (_user.IsChannelCreator) - { - // we first send all user message to let them known the creator is leaving - foreach (var user in _channel.Users.Values) - { - // we do not need to send part message to leaver - if (user.Client.Info.NickName == _user.Client.Info.NickName) - { - continue; - } - // We create a new KICKHandler to handle KICK operation for us - var kickRequest = new KickRequest - { - KickeeNickName = user.Client.Info.NickName, - ChannelName = _channel.Name, - Reason = _request.Reason, - }; - new KickHandler(_client, kickRequest).Handle(); - } - // we remove the local channel and unbind events - switch (_channel.RoomType) - { - case PeerRoomType.Normal: - case PeerRoomType.Staging: - Aggregate.Channel.RemoveLocalChannel(_channel); - break; - } - } - } - protected override void UpdateChannelCache() - { - switch (_channel.RoomType) - { - case PeerRoomType.Normal: - case PeerRoomType.Staging: - Aggregate.Channel.RemoveChannelCache(_user, _channel); - break; - default: - base.UpdateChannelCache(); - break; - } - } - protected override void ResponseConstruct() - { - _response = new PartResponse(_request, _result); - } - - protected override void Response() - { - // when user leave channel we must broadcast leave message, whatever the room type is. - // otherwise the other client will not delete this user in his client list - _channel.MultiCast(_user.Client, _response, true); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetCKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetCKeyHandler.cs deleted file mode 100755 index 8c46320f0..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetCKeyHandler.cs +++ /dev/null @@ -1,68 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Error.IRC.General; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Response.Channel; -using UniSpy.Server.Chat.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.Channel -{ - // Sets channel key/values. - // If user is NULL or "", the keys will be set on the channel. - // Otherwise, they will be set on the user, - // Only ops can set channel keys on other users. - // Set a value to NULL or "" to clear that key. - - public sealed class SetCKeyHandler : ChannelHandlerBase - { - private new SetCKeyRequest _request => (SetCKeyRequest)base._request; - private ChannelUser _otherUser; - public SetCKeyHandler(IShareClient client, SetCKeyRequest request) : base(client, request) - { - } - - protected override void RequestCheck() - { - _request.Parse(); - - base.RequestCheck(); - if (_request.NickName != _client.Info.NickName) - { - if (_channel.Mode.IsTopicOnlySetByChannelOperator) - { - throw new Chat.Exception("SETCKEY failed because you are not channel operator."); - } - _otherUser = _channel.GetUser(_request.NickName); - if (_otherUser is null) - { - throw new NoSuchNickException($"Can not find user:{_request.NickName} in channel {_request.ChannelName}"); - } - } - } - - protected override void DataOperation() - { - if (_otherUser is not null) - { - _otherUser.KeyValues.Update(_request.KeyValues); - } - else - { - _user.KeyValues.Update(_request.KeyValues); - } - } - - protected override void ResponseConstruct() - { - if (_request.IsBroadCast) - { - _response = new SetCKeyResponse(_request); - } - } - //! if there are key start with b_ we must broadcast to everyone - protected override void Response() - { - _channel.MultiCast(_client, _response); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetChannelKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetChannelKeyHandler.cs deleted file mode 100755 index 4056adeb9..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/SetChannelKeyHandler.cs +++ /dev/null @@ -1,43 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Response.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.Channel -{ - // Sets channel key/values. - // If user is NULL or "", the keys will be set on the channel. - // Otherwise, they will be set on the user, - // Only ops can set channel keys on other users. - // Set a value to NULL or "" to clear that key. - public sealed class SetChannelKeyHandler : ChannelHandlerBase - { - private new SetChannelKeyRequest _request => (SetChannelKeyRequest)base._request; - private new SetChannelKeyResult _result { get => (SetChannelKeyResult)base._result; set => base._result = value; } - public SetChannelKeyHandler(IShareClient client, SetChannelKeyRequest request) : base(client, request) - { - _result = new SetChannelKeyResult(); - } - protected override void DataOperation() - { - if (!_user.IsChannelOperator) - { - throw new Chat.Exception("SETCHANKEY failed because you are not channel operator."); - } - _channel.KeyValues.Update(_request.KeyValues); - - _result.ChannelName = _result.ChannelName; - _result.ChannelUserIRCPrefix = _user.Client.Info.IRCPrefix; - } - protected override void ResponseConstruct() - { - _response = new SetChannelKeyResponse(_request, _result); - } - //! setchankey must be broadcast to every one in this channel except sender - protected override void Response() - { - _channel.MultiCast(_client, _response, true); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Channel/TopicHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Channel/TopicHandler.cs deleted file mode 100755 index 530a1ec22..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/Channel/TopicHandler.cs +++ /dev/null @@ -1,53 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Response.Channel; -using UniSpy.Server.Chat.Contract.Result.Channel; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.Channel -{ - - public sealed class TopicHandler : ChannelHandlerBase - { - private new TopicRequest _request => (TopicRequest)base._request; - private new TopicResult _result { get => (TopicResult)base._result; set => base._result = value; } - public TopicHandler(IShareClient client, TopicRequest request) : base(client, request) - { - _result = new TopicResult(); - } - - protected override void DataOperation() - { - if (!_user.IsChannelOperator) - { - throw new Chat.Exception("Edit topic failed because you are not channel operator."); - } - switch (_request.RequestType) - { - case TopicRequestType.GetChannelTopic: - break; - case TopicRequestType.SetChannelTopic: - _channel.Topic = _request.ChannelTopic; - break; - } - _result.ChannelName = _channel.Name; - _result.ChannelTopic = _channel.Topic; - } - protected override void ResponseConstruct() - { - _response = new TopicResponse(_request, _result); - } - protected override void Response() - { - switch (_request.RequestType) - { - case TopicRequestType.GetChannelTopic: - _client.Send(_response); - break; - case TopicRequestType.SetChannelTopic: - _channel.MultiCast(_client, _response); - break; - } - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/CdKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/CdKeyHandler.cs deleted file mode 100755 index f87baa97f..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/CdKeyHandler.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Response.General; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.General -{ - - public sealed class CdKeyHandler : CmdHandlerBase - { - private new CdKeyRequest _request => (CdKeyRequest)base._request; - - public CdKeyHandler(IShareClient client, CdKeyRequest request) : base(client, request){ } - - protected override void ResponseConstruct() - { - _response = new CdKeyResponse(_request, _result); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/CryptHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/CryptHandler.cs deleted file mode 100755 index fca598df9..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/CryptHandler.cs +++ /dev/null @@ -1,55 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Response.General; -using UniSpy.Server.Chat.Contract.Result.General; -using UniSpy.Server.Core.Extension; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.General -{ - - public sealed class CryptHandler : CmdHandlerBase - { - private new CryptRequest _request => (CryptRequest)base._request; - private new CryptResult _result { get => (CryptResult)base._result; set => base._result = value; } - private ICryptography _crypto; - // CRYPT des 1 gamename - public CryptHandler(IShareClient client, CryptRequest request) : base(client, request) - { - _result = new CryptResult(); - } - protected override void DataOperation() - { - // we do not use crypto for remote client - if (!_client.IsRemoteClient) - { - string secretKey = DataOperationExtensions.GetSecretKey(_request.GameName); - if (secretKey is null) - { - (_client as Client)?.Connection.Disconnect(); - throw new Chat.Exception("secret key not found."); - } - _client.Info.GameSecretKey = secretKey; - // 2. Prepare two keys - _crypto = new ChatCrypt(_client.Info.GameSecretKey); - } - _client.Info.GameName = _request.GameName; - } - protected override void ResponseConstruct() - { - _response = new CryptResponse(_request, _result); - } - - protected override void Response() - { - base.Response(); - if (!_client.IsRemoteClient && _client.GetType() == typeof(Client)) - { - ((Client)_client).Crypto = _crypto; - } - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/GetKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/GetKeyHandler.cs deleted file mode 100755 index 89621937d..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/GetKeyHandler.cs +++ /dev/null @@ -1,47 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Response.General; -using UniSpy.Server.Chat.Contract.Result.General; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Chat.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.General -{ - /// - /// Get key value of the one user or all user - /// - public sealed class GetKeyHandler : LogedInHandlerBase - { - private new GetKeyRequest _request => (GetKeyRequest)base._request; - private new GetKeyResult _result { get => (GetKeyResult)base._result; set => base._result = value; } - public GetKeyHandler(IShareClient client, GetKeyRequest request) : base(client, request) - { - _result = new GetKeyResult(); - } - - protected override void DataOperation() - { - if (_request.IsGetAllUser) - { - var matchedClients = ClientManager.GetAllClientInfo(); - foreach (ClientInfo info in matchedClients) - { - var values = info.KeyValues.GetValueString(_request.Keys); - _result.Values.Add(values); - } - } - else - { - _result.NickName = _request.NickName; - - } - var target = ClientManager.GetClientByNickName(_request.NickName); - _result.Values.Add(target.Info.KeyValues.GetValueString(_request.Keys)); - } - - protected override void ResponseConstruct() - { - _response = new GetKeyResponse(_request, _result); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/GetUdpRelayHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/GetUdpRelayHandler.cs deleted file mode 100755 index a4ad86d1e..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/GetUdpRelayHandler.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Contract.Request.General; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.General -{ - /// - /// currently we do not know how this work - /// so we do not implement it - /// - - public sealed class GetUdpRelayHandler : CmdHandlerBase - { - new GetUdpRelayRequest _request => (GetUdpRelayRequest)base._request; - public GetUdpRelayHandler(IShareClient client, GetUdpRelayRequest request) : base(client, request) { } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/InviteHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/InviteHandler.cs deleted file mode 100644 index 2b7e92add..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/InviteHandler.cs +++ /dev/null @@ -1,25 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Chat.Contract.Request.General; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.General -{ - public sealed class InviteHandler : CmdHandlerBase - { - private new InviteRequest _request => (InviteRequest)base._request; - public InviteHandler(IShareClient client, InviteRequest request) : base(client, request) - { - } - protected override void RequestCheck() - { - base.RequestCheck(); - if (!_client.Info.JoinedChannels.ContainsKey(_request.ChannelName)) - { - throw new Chat.Exception("To invite user, you must in the channel."); - } - var chan = Aggregate.Channel.GetLocalChannel(_request.ChannelName); - chan.Mode.InviteNickNames.Add(_request.NickName); - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/ListHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/ListHandler.cs deleted file mode 100755 index 8873dd5d8..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/ListHandler.cs +++ /dev/null @@ -1,43 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Response.General; -using UniSpy.Server.Chat.Contract.Result.General; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.General -{ - //todo unfinished - - public sealed class ListHandler : LogedInHandlerBase - { - private new ListRequest _request => (ListRequest)base._request; - private new ListResult _result { get => (ListResult)base._result; set => base._result = value; } - //:irc.foonet.com 321 Pants Channel :Users Name\r\n:irc.foonet.com 323 Pants :End of /LIST\r\n - public ListHandler(IShareClient client, ListRequest request) : base(client, request) - { - _result = new ListResult(); - } - protected override void DataOperation() - { - //add list response header - foreach (var channel in Aggregate.Channel.LocalChannels.Values) - { - //TODO - //add channel information here - ListDataModel channelInfo = new ListDataModel - { - ChannelName = channel.Name, - TotalChannelUsers = channel.Users.Count, - ChannelTopic = channel.Topic - }; - _result.ChannelInfoList.Add(channelInfo); - } - } - - protected override void ResponseConstruct() - { - _response = new ListResponse(_request, _result); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/LoginHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/LoginHandler.cs deleted file mode 100755 index 36f797167..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/LoginHandler.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Linq; -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Response.General; -using UniSpy.Server.Chat.Contract.Result.General; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Chat.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.General -{ - - public sealed class LoginHandler : CmdHandlerBase - { - - private new LoginRequest _request => (LoginRequest)base._request; - private new LoginResult _result { get => (LoginResult)base._result; set => base._result = value; } - public LoginHandler(IShareClient client, LoginRequest request) : base(client, request) - { - _result = new LoginResult(); - } - - protected override void RequestCheck() - { - /// TODO: Verify which games does send a GS encoded password and not md5 - //we decoded gamespy encoded password then get md5 of it - //_password = GameSpyUtils.DecodePassword(_request.PasswordHash); - //_password = StringExtensions.GetMD5Hash(_password); - base.RequestCheck(); - } - - protected override void DataOperation() - { - switch (_request.ReqeustType) - { - case LoginReqeustType.NickAndEmailLogin: - // the ignored variables _ will used in future - (_result.ProfileId, _result.UserID, _, _) = StorageOperation.Persistance.NickAndEmailLogin(_request.NickName, _request.Email, _request.PasswordHash); - //todo check whether NICK * will occur in this situation - break; - case LoginReqeustType.UniqueNickLogin: - (_result.ProfileId, _result.UserID, _, _) = StorageOperation.Persistance.UniqueNickLogin(_request.UniqueNick, _request.NamespaceId); - // we set user's uniquenick - _client.Info.UniqueNickName = _request.UniqueNick; - break; - } - } - - protected override void ResponseConstruct() - { - _response = new LoginResponse(_request, _result); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs deleted file mode 100755 index c7f6c2560..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/NickHandler.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System; -using System.Linq; -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Chat.Error.IRC.General; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Response.General; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Contract.Result.General; -using UniSpy.Server.Chat.Aggregate.Redis; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.General -{ - - public sealed class NickHandler : CmdHandlerBase - { - private new NickRequest _request => (NickRequest)base._request; - private new NickResult _result { get => (NickResult)base._result; set => base._result = value; } - private string _tempNickName; - public NickHandler(IShareClient client, NickRequest request) : base(client, request) - { - _result = new NickResult(); - } - - - private void SetUniqueNickAsNickName() - { - if (_client.Info.UniqueNickName is null) - { - throw new Chat.Exception("uniquenick is not set."); - } - if (_client.Info.UniqueNickName == "") - { - throw new Chat.Exception("uniquenick can not be empty string"); - } - var postFix = ""; - if (_client.Info.GameName.Length > 2) - { - postFix = _client.Info.GameName.Substring(0, 3); - } - else - { - postFix = _client.Info.GameName.Substring(0, 2); - } - _tempNickName = $"{_client.Info.UniqueNickName}-{postFix}"; - } - protected override void DataOperation() - { - if (_request.NickName == "*") - { - //client using its - as his nickname in chat - SetUniqueNickAsNickName(); - } - else - { - _tempNickName = _request.NickName; - } - var key = new ClientInfoCache { NickName = _tempNickName }; - using (var locker = new LinqToRedis.RedisLock(TimeSpan.FromSeconds(10), Application.StorageOperation.Persistance.ClientCacheClient.Db, key)) - { - if (locker.LockTake()) - { - if (!Application.StorageOperation.Persistance.IsClientExist(key)) - { - _client.Info.NickName = _request.NickName; - Application.StorageOperation.Persistance.UpdateClient(_client); - } - else - { - throw new NickNameInUseException(); - } - } - else - { - throw new NickNameInUseException(); - } - - } - - _result.NickName = _client.Info.NickName; - } - protected override void ResponseConstruct() - { - _response = new NickResponse(_request, _result); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/PingHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/PingHandler.cs deleted file mode 100755 index 87a3a7464..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/PingHandler.cs +++ /dev/null @@ -1,25 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Response.General; -using UniSpy.Server.Chat.Contract.Result.General; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.General -{ - - public sealed class PingHandler : LogedInHandlerBase - { - private new PingRequest _request => (PingRequest)base._request; - private new PingResult _result { get => (PingResult)base._result; set => base._result = value; } - public PingHandler(IShareClient client, PingRequest request) : base(client, request){ } - protected override void DataOperation() - { - _result = new PingResult(); - _result.RequesterIRCPrefix = _client.Info.IRCPrefix; - } - protected override void ResponseConstruct() - { - _response = new PingResponse(_request, _result); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/QuitHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/QuitHandler.cs deleted file mode 100755 index 82d4ef714..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/QuitHandler.cs +++ /dev/null @@ -1,46 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Handler.CmdHandler.Channel; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.General -{ - - public sealed class QuitHandler : LogedInHandlerBase - { - private new QuitRequest _request => (QuitRequest)base._request; - // when a user disconnected with server we can call this function - public QuitHandler(IShareClient client, QuitRequest request) : base(client, request) { } - protected override void RequestCheck() - { - if (_request.RawRequest is null) - { - return; - } - base.RequestCheck(); - } - - protected override void DataOperation() - { - foreach (var channel in _client.Info.JoinedChannels.Values) - { - var user = channel.GetUser(_client); - if (user is null) - { - continue; - } - // we create a PARTHandler to handle our quit request - var partRequest = new PartRequest() - { - ChannelName = channel.Name, - Reason = _request.Reason - }; - new PartHandler(_client, partRequest).Handle(); - // client is loged out - _client.Info.IsLoggedIn = false; - } - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/SetKeyHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/SetKeyHandler.cs deleted file mode 100755 index 38021546c..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/SetKeyHandler.cs +++ /dev/null @@ -1,29 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Contract.Request.General; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.General -{ - /// - /// set global key value on this client - /// - public sealed class SetKeyHandler : LogedInHandlerBase - { - private new SetKeyRequest _request => (SetKeyRequest)base._request; - public SetKeyHandler(IShareClient client, SetKeyRequest request) : base(client, request) { } - - protected override void DataOperation() - { - _client.Info.KeyValues.Update(_request.KeyValues); - // foreach (var channel in _client.Info.JoinedChannels.Values) - // { - // ChannelUser user = channel.GetChannelUser(_client); - // //if (user==null) - // //{ - // // continue; - // //} - // user.UpdateUserKeyValues(_request.KeyValues); - // } - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/UserHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/UserHandler.cs deleted file mode 100755 index e00bfb1b8..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/UserHandler.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Contract.Request.General; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.General -{ - - public sealed class UserHandler : CmdHandlerBase - { - private new UserRequest _request => (UserRequest)base._request; - public UserHandler(IShareClient client, UserRequest request) : base(client, request){ } - - protected override void DataOperation() - { - _client.Info.UserName = _request.UserName; - _client.Info.Name = _request.Name; - _client.Info.IsLoggedIn = true; - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/UserIPHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/UserIPHandler.cs deleted file mode 100755 index 375918b2d..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/UserIPHandler.cs +++ /dev/null @@ -1,28 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Response.General; -using UniSpy.Server.Chat.Contract.Result.General; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.General -{ - - public sealed class UserIPHandler : CmdHandlerBase - { - - private new UserIPRequest _request => (UserIPRequest)base._request; - private new UserIPResult _result { get => (UserIPResult)base._result; set => base._result = value; } - public UserIPHandler(IShareClient client, UserIPRequest request) : base(client, request){ } - - protected override void DataOperation() - { - _result = new UserIPResult(); - _result.RemoteIPAddress = _client.Connection.RemoteIPEndPoint.Address; - - } - protected override void ResponseConstruct() - { - _response = new UserIPResponse(_request, _result); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs deleted file mode 100755 index 602c10ae7..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/WhoHandler.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System.Net; -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Chat.Aggregate.Misc; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Response.General; -using UniSpy.Server.Chat.Contract.Result.General; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.General -{ - /// - /// Get a channel user's basic information - /// same as WHOIS - /// - - public sealed class WhoHandler : LogedInHandlerBase - { - private new WhoRequest _request => (WhoRequest)base._request; - private new WhoResult _result { get => (WhoResult)base._result; set => base._result = value; } - public WhoHandler(IShareClient client, WhoRequest request) : base(client, request) { } - - protected override void DataOperation() - { - _result = new WhoResult(); - - switch (_request.RequestType) - { - case WhoRequestType.GetChannelUsersInfo: - GetChannelUsersInfo(); - break; - case WhoRequestType.GetUserInfo: - GetUserInfo(); - break; - } - } - - private void GetChannelUsersInfo() - { - - var channel = Aggregate.Channel.GetLocalChannel(_request.ChannelName); - if (channel is null) - { - throw new IRCChannelException($"Channel do not exist.", IRCErrorCode.NoSuchChannel, _request.ChannelName); - } - foreach (var user in channel.Users.Values) - { - var data = new WhoDataModel - { - ChannelName = channel.Name, - UserName = user.Client.Info.UserName, - NickName = user.Client.Info.NickName, - PublicIPAddress = user.Client.Connection.RemoteIPEndPoint.Address.ToString(), - Modes = user.Modes - }; - _result.DataModels.Add(data); - } - } - /// - /// Send all channel user information - /// - private void GetUserInfo() - { - var client = ClientManager.GetClientByNickName(_request.NickName); - if (client is null) - { - throw new Chat.Exception($"Client not exist with nickname {_request.NickName}"); - } - - foreach (var channel in client.Info.JoinedChannels.Values) - { - var user = channel.GetUser(client); - var data = new WhoDataModel - { - ChannelName = channel.Name, - NickName = client.Info.NickName, - UserName = client.Info.UserName, - PublicIPAddress = client.Connection.RemoteIPEndPoint.Address.ToString(), - Modes = user.Modes - }; - _result.DataModels.Add(data); - } - } - protected override void ResponseConstruct() - { - _response = new WhoResponse(_request, _result); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/General/WhoIsHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/General/WhoIsHandler.cs deleted file mode 100755 index 0e26dbdf0..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/General/WhoIsHandler.cs +++ /dev/null @@ -1,45 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Response.General; -using UniSpy.Server.Chat.Contract.Result.General; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.General -{ - - public sealed class WhoIsHandler : CmdHandlerBase - { - private new WhoIsRequest _request => (WhoIsRequest)base._request; - private new WhoIsResult _result { get => (WhoIsResult)base._result; set => base._result = value; } - private ClientInfo _clientInfo; - public WhoIsHandler(IShareClient client, WhoIsRequest request) : base(client, request) { } - - protected override void RequestCheck() - { - _result = new WhoIsResult(); - - // there only existed one nick name - base.RequestCheck(); - var client = ClientManager.GetClientByNickName(_request.NickName); - _clientInfo = client?.Info; - } - protected override void DataOperation() - { - _result.NickName = _clientInfo.NickName; - _result.Name = _clientInfo.Name; - _result.UserName = _clientInfo.UserName; - _result.PublicIPAddress = _client.Connection.RemoteIPEndPoint.Address.ToString(); - foreach (var channel in _clientInfo.JoinedChannels.Values) - { - _result.JoinedChannelName.Add(channel.Name); - } - } - protected override void ResponseConstruct() - { - _response = new WhoIsResponse(_request, _result); - } - - - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Message/AtmHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Message/AtmHandler.cs deleted file mode 100755 index bf7be6ad0..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/Message/AtmHandler.cs +++ /dev/null @@ -1,25 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Contract.Request.Message; -using UniSpy.Server.Chat.Contract.Response.Message; -using UniSpy.Server.Chat.Contract.Result.Message; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.Message -{ - /// - /// Above the table message handler - /// - public sealed class AtmHandler : MessageHandlerBase - { - new AtmRequest _request => (AtmRequest)base._request; - new AtmResult _result{ get => (AtmResult)base._result; set => base._result = value; } - public AtmHandler(IShareClient client, AtmRequest request) : base(client, request) - { - _result = new AtmResult(); - } - protected override void ResponseConstruct() - { - _response = new AtmResponse(_request, _result); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Message/NoticeHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Message/NoticeHandler.cs deleted file mode 100755 index a79364c69..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/Message/NoticeHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Contract.Request.Message; -using UniSpy.Server.Chat.Contract.Response.Message; -using UniSpy.Server.Chat.Contract.Result.Message; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.Message -{ - - public sealed class NoticeHandler : MessageHandlerBase - { - private new NoticeRequest _request => (NoticeRequest)base._request; - private new NoticeResult _result{ get => (NoticeResult)base._result; set => base._result = value; } - public NoticeHandler(IShareClient client, NoticeRequest request) : base(client, request) - { - _result = new NoticeResult(); - } - - protected override void ResponseConstruct() - { - _response = new NoticeResponse(_request, _result); - } - } -} - - - diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs deleted file mode 100755 index 25aa7174f..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/Message/PrivateHandler.cs +++ /dev/null @@ -1,47 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Chat.Contract.Request.Message; -using UniSpy.Server.Chat.Contract.Response.Message; -using UniSpy.Server.Chat.Contract.Result.Message; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.Message -{ - - public sealed class PrivateHandler : MessageHandlerBase - { - private new PrivateRequest _request => (PrivateRequest)base._request; - private new PrivateResult _result { get => (PrivateResult)base._result; set => base._result = value; } - public PrivateHandler(IShareClient client, PrivateRequest request) : base(client, request) - { - _result = new PrivateResult(); - } - protected override void ChannelMessageDataOpration() - { - // var data = ClientManager.ClientPool; - // if (_channel.Mode.IsModeratedChannel) - // { - // return; - // } - - if (_channel.IsUserBanned(_user)) - { - return; - } - - if (!_user.IsVoiceable) - { - return; - } - // if (_user.Client.Info.IsQuietMode) - // { - // return; - // } - _result.IsBroadcastMessage = true; - } - protected override void ResponseConstruct() - { - _response = new PrivateResponse(_request, _result); - } - } -} diff --git a/src/Servers/Chat/src/Handler/CmdHandler/Message/UtmHandler.cs b/src/Servers/Chat/src/Handler/CmdHandler/Message/UtmHandler.cs deleted file mode 100755 index d5cb499f1..000000000 --- a/src/Servers/Chat/src/Handler/CmdHandler/Message/UtmHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.BaseClass; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Contract.Request.Message; -using UniSpy.Server.Chat.Contract.Response.Message; -using UniSpy.Server.Chat.Contract.Result.Message; - -namespace UniSpy.Server.Chat.Handler.CmdHandler.Message -{ - /// - /// Under the table message, this message will not display in regular chat - /// - public sealed class UtmHandler : MessageHandlerBase - { - private new UtmRequest _request => (UtmRequest)base._request; - private new UtmResult _result { get => (UtmResult)base._result; set => base._result = value; } - public UtmHandler(IShareClient client, UtmRequest request) : base(client, request) - { - _result = new UtmResult(); - } - - protected override void ResponseConstruct() - { - _response = new UtmResponse(_request, _result); - } - - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/Handler/CmdSwitcher.cs b/src/Servers/Chat/src/Handler/CmdSwitcher.cs deleted file mode 100755 index dec8bbbe2..000000000 --- a/src/Servers/Chat/src/Handler/CmdSwitcher.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Request.Message; -using UniSpy.Server.Chat.Handler.CmdHandler.Channel; -using UniSpy.Server.Chat.Handler.CmdHandler.General; -using UniSpy.Server.Chat.Handler.CmdHandler.Message; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Handler -{ - /// - /// Process request to Commands - /// - public sealed class CmdSwitcher : CmdSwitcherBase - { - private new string _rawRequest => (string)base._rawRequest; - private new IShareClient _client => (IShareClient)base._client; - public CmdSwitcher(IShareClient client, string rawRequest) : base(client, rawRequest) { } - - protected override void ProcessRawRequest() - { - string[] splitedRawRequests = _rawRequest.Replace("\r", "") - .Split("\n", StringSplitOptions.RemoveEmptyEntries); - foreach (var rawRequest in splitedRawRequests) - { - var name = rawRequest.Trim(' ').Split(' ', StringSplitOptions.RemoveEmptyEntries).ToList().First(); - _requests.Add(new KeyValuePair(name, rawRequest)); - } - } - - protected override IHandler CreateCmdHandlers(object name, object rawRequest) - { - var request = (string)rawRequest; - switch ((string)name) - { - # region General - case "CRYPT": - return new CryptHandler(_client, new CryptRequest(request)); - case "CDKEY": - return new CdKeyHandler(_client, new CdKeyRequest(request)); - case "GETKEY": - return new GetKeyHandler(_client, new GetKeyRequest(request)); - case "LIST": - return new ListHandler(_client, new ListRequest(request)); - case "LOGIN": - return new LoginHandler(_client, new LoginRequest(request)); - case "NICK": - return new NickHandler(_client, new NickRequest(request)); - case "PING": - return new PingHandler(_client, new PingRequest(request)); - case "QUIT": - return new QuitHandler(_client, new QuitRequest(request)); - case "SETKEY": - return new SetKeyHandler(_client, new SetKeyRequest(request)); - case "USER": - return new UserHandler(_client, new UserRequest(request)); - case "USRIP": - return new UserIPHandler(_client, new UserIPRequest(request)); - case "WHO": - return new WhoHandler(_client, new WhoRequest(request)); - case "WHOIS": - return new WhoIsHandler(_client, new WhoIsRequest(request)); - #endregion - - #region Channel - case "GETCHANKEY": - return new GetChannelKeyHandler(_client, new GetChannelKeyRequest(request)); - case "GETCKEY": - return new GetCKeyHandler(_client, new GetCKeyRequest(request)); - case "JOIN": - return new JoinHandler(_client, new JoinRequest(request)); - case "KICK": - return new KickHandler(_client, new KickRequest(request)); - case "MODE": - return new ModeHandler(_client, new ModeRequest(request)); - case "NAMES": - return new NamesHandler(_client, new NamesRequest(request)); - case "PART": - return new PartHandler(_client, new PartRequest(request)); - case "SETCHANKEY": - return new SetChannelKeyHandler(_client, new SetChannelKeyRequest(request)); - case "SETCKEY": - return new SetCKeyHandler(_client, new SetCKeyRequest(request)); - case "TOPIC": - return new TopicHandler(_client, new TopicRequest(request)); - #endregion - #region Message - case "ATM": - return new AtmHandler(_client, new AtmRequest(request)); - case "NOTICE": - return new NoticeHandler(_client, new NoticeRequest(request)); - case "PRIVMSG": - return new PrivateHandler(_client, new PrivateRequest(request)); - case "UTM": - return new UtmHandler(_client, new UtmRequest(request)); - default: - return null; - #endregion - } - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/src/UniSpy.Server.Chat.csproj b/src/Servers/Chat/src/UniSpy.Server.Chat.csproj deleted file mode 100755 index 8b9381e25..000000000 --- a/src/Servers/Chat/src/UniSpy.Server.Chat.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - Exe - net6.0 - ..\..\..\..\common\Icon\UniSpy_Logo.ico - Linux - ..\..\..\.. - - - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - - - \ No newline at end of file diff --git a/src/Servers/Chat/test/Channel/ChannelHandlerTest.cs b/src/Servers/Chat/test/Channel/ChannelHandlerTest.cs deleted file mode 100644 index a171ce893..000000000 --- a/src/Servers/Chat/test/Channel/ChannelHandlerTest.cs +++ /dev/null @@ -1,84 +0,0 @@ -using UniSpy.Server.Chat.Aggregate.Redis; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Contract.Request.Message; -using UniSpy.Server.Chat.Handler.CmdHandler.Channel; -using UniSpy.Server.Chat.Handler.CmdHandler.General; -using UniSpy.Server.Chat.Handler.CmdHandler.Message; -using Xunit; - -namespace UniSpy.Server.Chat.Test.Channel -{ - public class ChannelHandlerTest - { - [Fact] - public void RoomTypeTest() - { - Assert.True(PeerRoom.GetRoomType("#GPG!622") == Chat.Aggregate.PeerRoomType.Group); - Assert.True(PeerRoom.GetRoomType("#GSP!worms3!Ml4lz344lM") == Chat.Aggregate.PeerRoomType.Staging); - Assert.True(PeerRoom.GetRoomType("#islanbul") == Chat.Aggregate.PeerRoomType.Normal); - } - [Fact] - public void JoinHandleTest() - { - var client1 = (Client)MockObject.CreateClient(port: 1234); - client1.Info.GameName = "worm3"; - var client2 = (Client)MockObject.CreateClient(port: 1235); - client2.Info.GameName = "worm3"; - SingleJoinTest(client1, "unispy1", "unispy1", "#GSP!room!test1"); - SingleJoinTest(client2, "unispy2", "unispy2", "#GSP!room!test1"); - } - [Fact] - public void ChannelMsgTest() - { - var client1 = (Client)MockObject.CreateClient(port: 1236); - var client2 = (Client)MockObject.CreateClient(port: 1237); - SingleJoinTest(client1, "unispy3", "unispy3", "#GSP!room!test2"); - SingleJoinTest(client2, "unispy4", "unispy4", "#GSP!room!test2"); - var privMsgReq = new PrivateRequest("PRIVMSG #GSP!room!test2 :hello this is a test."); - var privMsgHandler = new PrivateHandler(client1, privMsgReq); - privMsgHandler.Handle(); - } - private void SingleLoginTest(Client client, string userName = "unispy", string nickName = "unispy") - { - var userReq = new UserRequest($"USER {userName} 127.0.0.1 peerchat.unispy.org :{userName}"); - var nickReq = new NickRequest($"NICK {nickName}"); - var userHandler = new UserHandler(client, userReq); - var nickHandler = new NickHandler(client, nickReq); - userHandler.Handle(); - nickHandler.Handle(); - } - private void SingleJoinTest(Client client, string userName = "unispy", string nickName = "unispy", string channelName = "#GSP!room!test") - { - SingleLoginTest(client, userName, nickName); - var joinReq = new JoinRequest($"JOIN {channelName}"); - var joinHandler = new JoinHandler(client, joinReq); - // we know the endpoint object is not set, so System.NullReferenceException will be thrown - joinHandler.Handle(); - Assert.Single(client.Info.JoinedChannels); - Assert.True(client.Info.JoinedChannels.ContainsKey(channelName)); - Assert.True(client.Info.IsJoinedChannel(channelName)); - } - [Fact] - public void SetCKeyTest() - { - var client = (Client)MockObject.CreateClient(port: 1238); - - SingleJoinTest(client, "spyguy6", "spyguy6", "#GSP!room!test14"); - var request = new SetCKeyRequest(ChannelRequests.SetCKey); - var handler = new SetCKeyHandler(client, request); - handler.Handle(); - } - [Fact] - public void ModeTest() - { - var client = (Client)MockObject.CreateClient(port: 1239); - - SingleJoinTest(client, "spyguy7", "spyguy7", "#GSP!gmtest!MlNK4q4l1M"); - var request = new ModeRequest("MODE #GSP!gmtest!MlNK4q4l1M -i-p-s+m-n+t+l+e 2"); - var handler = new ModeHandler(client, request); - handler.Handle(); - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/test/Channel/ChannelRequestTest.cs b/src/Servers/Chat/test/Channel/ChannelRequestTest.cs deleted file mode 100644 index eb74ff753..000000000 --- a/src/Servers/Chat/test/Channel/ChannelRequestTest.cs +++ /dev/null @@ -1,131 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Chat.Contract.Request.Channel; -using Xunit; - -namespace UniSpy.Server.Chat.Test.Channel -{ - public class ChannelRequestTest - { - [Fact] - public void GetChannelKey() - { - var request = new GetChannelKeyRequest(ChannelRequests.GetChannelKey); - request.Parse(); - Assert.Equal("#GSP!room!test", request.ChannelName); - Assert.Equal("0000", request.Cookie); - Assert.Equal("username", request.Keys[0]); - Assert.Equal("nickname", request.Keys[1]); - } - - [Fact] - public void GetCKeyChannelSpecificUser() - { - var request = new GetCKeyRequest(ChannelRequests.GetCKeyChannelSpecificUser); - request.Parse(); - Assert.Equal(GetKeyReqeustType.GetChannelSpecificUserKeyValue, request.RequestType); - Assert.Equal("#GSP!room!test", request.ChannelName); - Assert.Equal("spyguy", request.NickName); - Assert.Equal("0000", request.Cookie); - Assert.Equal("username", request.Keys[0]); - Assert.Equal("nickname", request.Keys[1]); - } - - [Fact] - public void GetCKeyChannelAllUser() - { - var request = new GetCKeyRequest(ChannelRequests.GetCKeyChannelAllUser); - request.Parse(); - Assert.Equal(GetKeyReqeustType.GetChannelAllUserKeyValue, request.RequestType); - Assert.Equal("#GSP!room!test", request.ChannelName); - Assert.Equal("0000", request.Cookie); - Assert.Equal("username", request.Keys[0]); - Assert.Equal("nickname", request.Keys[1]); - } - - [Fact] - public void Join() - { - var request = new JoinRequest(ChannelRequests.Join); - request.Parse(); - Assert.Equal("#GSP!room!test", request.ChannelName); - - request = new JoinRequest(ChannelRequests.JoinWithPass); - request.Parse(); - Assert.Equal("#GSP!room!test", request.ChannelName); - Assert.Equal("pass123", request.Password); - } - - [Fact] - public void Kick() - { - var request = new KickRequest(ChannelRequests.Kick); - request.Parse(); - Assert.Equal("spyguy", request.KickeeNickName); - Assert.Equal("Spam", request.Reason); - } - - [Fact] - public void Mode() - { - var request = new ModeRequest(ChannelRequests.ModeChannel); - request.Parse(); - Assert.Equal(ModeOperationType.AddChannelUserLimits, request.ModeOperations[0]); - Assert.Equal("#GSP!room!test", request.ChannelName); - Assert.Equal("+l", request.ModeFlag); - Assert.Equal((int)2, request.LimitNumber); - - - request = new ModeRequest("MODE #GSP!gmtest!MlNK4q4l1M -i-p-s+m-n+t+l+e 2"); - request.Parse(); - - } - - [Fact] - public void Part() - { - var request = new PartRequest(ChannelRequests.Part); - request.Parse(); - Assert.Equal("test", request.Reason); - } - - [Fact(Skip = "TODO: add tests")] - public void SetChannelKey() - { - var request = new SetChannelKeyRequest(ChannelRequests.SetChannelKey); - request.Parse(); - } - - [Fact] - public void SetCKey() - { - var request = new SetCKeyRequest(ChannelRequests.SetCKey); - request.Parse(); - Dictionary dict1 = new Dictionary - { - { - "b_flags", "sh" - } - }; - Assert.Equal("#GSP!room!test", request.Channel); - Assert.Equal("spyguy", request.NickName); - Assert.Equal(dict1, request.KeyValues); - } - - [Fact] - public void TopicGetChannelTopic() - { - var request = new TopicRequest(ChannelRequests.TopicGetChannelTopic); - request.Parse(); - Assert.Equal("#GSP!room!test", request.ChannelName); - } - - [Fact] - public void TopicSetChannelTopic() - { - var request = new TopicRequest(ChannelRequests.TopicSetChannelTopic); - request.Parse(); - Assert.Equal("#GSP!room!test", request.ChannelName); - Assert.Equal("This is a topic message.", request.ChannelTopic); - } - } -} diff --git a/src/Servers/Chat/test/Channel/ChannelRequests.cs b/src/Servers/Chat/test/Channel/ChannelRequests.cs deleted file mode 100644 index 19c066a15..000000000 --- a/src/Servers/Chat/test/Channel/ChannelRequests.cs +++ /dev/null @@ -1,28 +0,0 @@ -namespace UniSpy.Server.Chat.Test.Channel -{ - public class ChannelRequests - { - public const string GetChannelKey = "GETCHANKEY #GSP!room!test 0000 0 :\\username\\nickname\0"; - public const string GetCKeyChannelSpecificUser = "GETCKEY #GSP!room!test spyguy 0000 0 :\\username\\nickname\0"; - public const string GetCKeyChannelAllUser = "GETCKEY #GSP!room!test * 0000 0 :\\username\\nickname\0"; - - public const string Join = "JOIN #GSP!room!test"; - - public const string JoinWithPass = "JOIN #GSP!room!test pass123"; - - public const string Kick = "KICK #islabul spyguy :Spam"; - - public const string ModeChannel = "MODE #GSP!room!test +l 2"; - public const string ModeUser = "MODE spyguy +s"; - - public const string Part = "PART #GSP!room!test :test"; - - public const string SetChannelKey = "SETCHANNELKEY #GSP!room!test 0000 0:\\b_flags\\sh\0"; - - public const string SetCKey = "SETCKEY #GSP!room!test spyguy 0000 0:\\b_flags\\sh\0"; - - public const string TopicGetChannelTopic = "TOPIC #GSP!room!test"; - - public const string TopicSetChannelTopic = "TOPIC #GSP!room!test :This is a topic message."; - } -} diff --git a/src/Servers/Chat/test/Game/GameTest.cs b/src/Servers/Chat/test/Game/GameTest.cs deleted file mode 100644 index 17ae2dadf..000000000 --- a/src/Servers/Chat/test/Game/GameTest.cs +++ /dev/null @@ -1,313 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Chat.Handler; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; -using Xunit; - -namespace UniSpy.Server.Chat.Test -{ - public class GameTest - { - [Fact] - public void Civilization4() - { - var rawRequests = new List(){ - // "CRYPT des 1 anno1701", - "USRIP", - "USER X419pGl4sX|18 127.0.0.1 peerchat.gamespy.com :aa3041ada9385b28fc4d4e47db288769", - "NICK a1701-5", - "CDKEY 81123-67814-77652-27631-11723-47707-22638-10701", - "JOIN #GSP!anno1701 ", - "MODE #GSP!anno1701", - @"GETCKEY #GSP!anno1701 * 008 0 :\b_flags","WHO a1701-5", - "JOIN #GSP!anno1701!M9zK0KJaKM ", - "MODE #GSP!anno1701!M9zK0KJaKM", - @"SETCKEY #GSP!anno1701 a1701-5 :\b_flags\s", - @"SETCKEY #GSP!anno1701!M9zK0KJaKM a1701-5 :\b_flags\sh", - @"GETCKEY #GSP!anno1701!M9zK0KJaKM * 009 0 :\b_flags", - "TOPIC #GSP!anno1701!M9zK0KJaKM :test", - "MODE #GSP!anno1701!M9zK0KJaKM +l 4", - "MODE #GSP!anno1701!M9zK0KJaKM -i-p-s+m+n+t+l+e 4", - "PART #GSP!anno1701 :" - }; - var client = (Client)MockObject.CreateClient(); - - foreach (var raw in rawRequests) - { - new CmdSwitcher(client, raw).Handle(); - } - } - [Fact] - public void TcpMessageSplitingTest() - { - var raws = new List(){ - UniSpyEncoding.GetBytes("GETCKEY"), - UniSpyEncoding.GetBytes(" world"), - UniSpyEncoding.GetBytes(" hi").Concat(new byte[]{0x0D,0x0A}).ToArray(), - }; - var client = MockObject.CreateClient(); - foreach (var raw in raws) - { - ((ITestClient)client).TestReceived(raw); - } - } - [Fact] - public void Worms3dTest() - { - var client1 = MockObject.CreateClient("107.244.81.1", 8888); - var client2 = MockObject.CreateClient("91.34.72.1", 8889); - var request1 = new List() - { - "CRYPT des 1 worms3\r\n", - "USRIP\r\n", - "USER X419pGl4sX|6 127.0.0.1 peerchat.gamespy.com :aa3041ada9385b28fc4d4e47db288769\r\n", - "NICK worms10\r\n", - "JOIN #GPG!622\r\n", - "MODE #GPG!622\r\n", - @"GETCKEY #GPG!622 * 024 0 :\username\b_flags"+"\r\n", - "JOIN #GSP!worms3!Ml4lz344lM\r\n", - "MODE #GSP!worms3!Ml4lz344lM\r\n", - @"SETCKEY #GPG!622 worms10 :\b_flags\s"+"\r\n", - @"SETCKEY #GSP!worms3!Ml4lz344lM worms10 :\b_flags\sh"+"\r\n", - @"GETCKEY #GSP!worms3!Ml4lz344lM * 025 0 :\username\b_flags"+"\r\n", - "TOPIC #GSP!worms3!Ml4lz344lM :tesr\r\n", - "MODE #GSP!worms3!Ml4lz344lM +l 2\r\n", - // "PART #GPG!622 :Joined staging room\r\n", - @"SETCKEY #GSP!worms3!Ml4lz344lM worms10 :\b_firewall\1\b_profileid\6\b_ipaddress\\b_publicip\255.255.255.255\b_privateip\192.168.0.60\b_authresponse\\b_gamever\1073\b_val\0"+"\r\n", - "WHO worms10\r\n", - @"SETCHANKEY #GSP!worms3!Ml4lz344lM :\b_hostname\test\b_hostport\\b_MaxPlayers\2\b_NumPlayers\1\b_SchemeChanging\0\b_gamever\1073\b_gametype\\b_mapname\Random\b_firewall\1\b_publicip\255.255.255.255\b_privateip\192.168.0.60\b_gamemode\openstaging\b_val\0\b_password\1"+"\r\n", - @"GETKEY worms20 026 0 :\b_firewall\b_profileid\b_ipaddress\b_publicip\b_privateip\b_authresponse\b_gamever\b_val"+"\r\n", - @"GETCKEY #GSP!worms3!Ml4lz344lM worms20 027 0 :\b_firewall\b_profileid\b_ipaddress\b_publicip\b_privateip\b_authresponse\b_gamever\b_val"+"\r\n", - @"SETCHANKEY #GSP!worms3!Ml4lz344lM :\b_hostname\test\b_hostport\\b_MaxPlayers\2\b_NumPlayers\1\b_SchemeChanging\0\b_gamever\1073\b_gametype\\b_mapname\Random\b_firewall\1\b_publicip\255.255.255.255\b_privateip\192.168.0.60\b_gamemode\openstaging\b_val\0\b_password\1"+"\r\n", - @"SETCHANKEY #GSP!worms3!Ml4lz344lM :\b_hostname\test\b_hostport\\b_MaxPlayers\2\b_NumPlayers\1\b_SchemeChanging\0\b_gamever\1073\b_gametype\\b_mapname\Random\b_firewall\1\b_publicip\255.255.255.255\b_privateip\192.168.0.60\b_gamemode\openstaging\b_val\0\b_password\1"+"\r\n", - "UTM #GSP!worms3!Ml4lz344lM :MDM |Obj|3|Land.Time|0|LogicalSeed|3891226431|GraphicalSeed|3269271590|Land.RealSeed|3281489942|Land.Theme|Pirate.Lumps|LevelToUse|FE.Level.RandomLand|Land.Ind|0|Wormpot.Reel1|17|Wormpot.Reel2|17|Wormpot.Reel3|17|TimeStamp|6206364", - "UTM #GSP!worms3!Ml4lz344lM :TDM aA", - "UTM #GSP!worms3!Ml4lz344lM :SDM ASFE.Scheme.StandardCUnAACADCBBCACBBFFBKBB8C/C3C!A!A*C*C() - { - "CRYPT des 1 worms3\r\n", - "USRIP\r\n", - "USER X419pGl4sX|7 127.0.0.1 peerchat.gamespy.com :5bb4f409fae8bc5aa1595cb6d5168a1c\r\n", - "NICK worms20\r\n", - "JOIN #GPG!622\r\n" , - "MODE #GPG!622\r\n", - @"GETCKEY #GPG!622 * 008 0 :\username\b_flags"+"\r\n", - "JOIN #GSP!worms3!Ml4lz344lM\r\n" , - "MODE #GSP!worms3!Ml4lz344lM\r\n", - @"SETCKEY #GPG!622 worms20 :\b_flags\s"+"\r\n", - @"SETCKEY #GSP!worms3!Ml4lz344lM worms20 :\b_flags\s"+"\r\n", - @"GETCKEY #GSP!worms3!Ml4lz344lM * 009 0 :\username\b_flags"+"\r\n", - // "PART #GPG!622 :Joined staging room"+"\r\n", - "UTM #GSP!worms3!Ml4lz344lM :APE [01]privateip[02]192.168.0.141[01]publicip[02]255.255.255.255\r\n", - "WHO worms10"+"\r\n", - @"GETCKEY #GSP!worms3!Ml4lz344lM worms10 010 0 :\b_firewall\b_profileid\b_ipaddress\b_publicip\b_privateip\b_authresponse\b_gamever\b_val", - "WHO worms20"+"\r\n" - }; - // first process client2, then client1 will get client2 in channel - foreach (var raw in request1) - { - ((ITestClient)client1).TestReceived(UniSpyEncoding.GetBytes(raw)); - } - ; - foreach (var raw in request2) - { - ((ITestClient)client2).TestReceived(UniSpyEncoding.GetBytes(raw)); - } - } - [Fact(Skip = "Error in full test")] - public void Worm3dTest20220613() - { - - var request1 = new List() - { - // "CRYPT des 1 worms3\r\n", - "USRIP\r\n", - "USER X419pGl4sX|7 127.0.0.1 peerchat.gamespy.com :9964fa3fe73f6a1fbb986d2a04b1eb65\r\n", - "NICK worms10\r\n", - "JOIN #GPG!622\r\n", - "MODE #GPG!622\r\n", - @"GETCKEY #GPG!622 * 000 0 :\username\b_flags"+"\r\n", - "PRIVMSG #GPG!622 :hello\r\n", - "JOIN #GSP!worms3!MJ0NJ4c3aM\r\n", - "MODE #GSP!worms3!MJ0NJ4c3aM\r\n", - @"SETCKEY #GPG!622 worms10 :\b_flags\s\r\n", - @"SETCKEY #GSP!worms3!MJ0NJ4c3aM worms10 :\b_flags\sh"+"\r\n", - @"GETCKEY #GSP!worms3!MJ0NJ4c3aM * 001 0 :\username\b_flags"+"\r\n", - "TOPIC #GSP!worms3!MJ0NJ4c3aM :main lobby message test\r\n", - "MODE #GSP!worms3!MJ0NJ4c3aM +l 2"+"\r\n", - // "PART #GPG!622 :Joined staging room\r\n", - @"SETCKEY #GSP!worms3!MJ0NJ4c3aM worms10 :\b_firewall\1\b_profileid\7\b_ipaddress\\b_publicip\255.255.255.255\b_privateip\192.168.0.145\b_authresponse\\b_gamever\1073\b_val\0\r\n", - "WHO worms10"+"\r\n", - @"SETCHANKEY #GSP!worms3!MJ0NJ4c3aM :\b_hostname\\b_hostport\\b_MaxPlayers\2\b_NumPlayers\0\b_SchemeChanging\0\b_gamever\1073\b_gametype\\b_mapname\Random\b_firewall\1\b_publicip\255.255.255.255\b_privateip\192.168.0.145\b_gamemode\openstaging\b_val\0\b_password\1"+"\r\n", - "PRIVMSG #GSP!worms3!MJ0NJ4c3aM :staging lobby message test"+"\r\n", - @"UTM #GSP!worms3!MJ0NJ4c3aM :ATS \nick\worms10\msg\2046\|name|Human Team 1|w0|Dave|w1|Patrick|w2|The Morriss|w3|Mike|w4|Tony|w5|Gluckman|skill|0|grave|0|SWeapon|0|flag|71|speech|Classic|InGame|0|player|worms10|alliance|1|wormCount|77111416"+"\r\n", - @"UTM #GSP!worms3!MJ0NJ4c3aM :RTS \team\Human Team 1\nick\worms10\msg\2174"+"\r\n" - }; - - var request2 = new List() - { - // "CRYPT des 1 worms3\r\n", - "USRIP\r\n", - "USER X419pGl4sX|6 127.0.0.1 peerchat.gamespy.com :dd6283e1e349806c20991020e0d6897a\r\n", - "NICK xiaojiuwo5\r\n", - "JOIN #GPG!622\r\n" , - "MODE #GPG!622\r\n", - @"GETCKEY #GPG!622 * 000 0 :\username\b_flags"+"\r\n", - "PING\r\n", - "PRIVMSG #GPG!622 :hello from the other side\r\n", - "JOIN #GSP!worms3!MJ0NJ4c3aM\r\n", - "MODE #GSP!worms3!MJ0NJ4c3aM\r\n", - @"SETCKEY #GPG!622 xiaojiuwo5 :\b_flags\s"+"\r\n", - @"SETCKEY #GSP!worms3!MJ0NJ4c3aM xiaojiuwo5 :\b_flags\s"+"\r\n", - @"GETCKEY #GSP!worms3!MJ0NJ4c3aM * 001 0 :\username\b_flags"+"\r\n", - // "PART #GPG!622 :Joined staging room\r\n", - "UTM #GSP!worms3!MJ0NJ4c3aM :APE [01]privateip[02]192.168.0.109[01]publicip[02]255.255.255.255\r\n", - "WHO xiaojiuwo5\r\n", - // "PART #GSP!worms3!MJ0NJ4c3aM :Left Game\r\n" - }; - var client1 = MockObject.CreateClient(); - var client2 = MockObject.CreateClient(); - - foreach (var raw in request1) - { - ((ITestClient)client1).TestReceived(UniSpyEncoding.GetBytes(raw)); - } - foreach (var raw in request2) - { - ((ITestClient)client2).TestReceived(UniSpyEncoding.GetBytes(raw)); - } - // var userCount = ChannelManager.Channels.First().Value.Users.Count; - // Assert.Equal(2, userCount); - int count1 = ((Client)client1).Info.JoinedChannels.Count(); - Assert.Equal(2, count1); - int count2 = ((Client)client2).Info.JoinedChannels.Count(); - Assert.Equal(2, count2); - - } - [Fact] - public void ConflictGlobalStorm() - { - var client = MockObject.CreateClient(); - - var request = new List() - { - // "CRYPT des 1 conflictsopc\r\n", - ":s 705 * WKNYSMENXOZQHYVS MIYLETFVYDMBDFFV\r\n", - "LOGIN 1 cgs1 149815eb972b3c370dee3b89d645ae14\r\n" - }; - foreach (var raw in request) - { - ((ITestClient)client).TestReceived(UniSpyEncoding.GetBytes(raw)); - } - } - - [Fact] - public void Worm3d20230309() - { - var client1 = MockObject.CreateClient("91.52.107.144", 42292); - var client2 = MockObject.CreateClient("91.52.107.144", 50187); - - // Given - var requests = new List>() - { - new KeyValuePair(client1,"USRIP"+"\r\n"), - new KeyValuePair(client1,"USER XO4Gqlff1X|1 127.0.0.1 peerchat.gamespy.com :e60465b8fb4bc71d36812797f498fc5b" + "\r\n"), - new KeyValuePair(client1,"NICK spyguy" + "\r\n"), - - new KeyValuePair(client1,"JOIN #GSP!worms3!Mllz4DcahM" + "\r\n"), - new KeyValuePair(client1,"MODE #GSP!worms3!Mllz4DcahM" + "\r\n"), - new KeyValuePair(client1,@"SETCKEY #GSP!worms3!Mllz4DcahM spyguy :\b_flags\sh" + "\r\n"), - new KeyValuePair(client1,@"GETCKEY #GSP!worms3!Mllz4DcahM * 001 0 :\username\b_flags" + "\r\n"), - new KeyValuePair(client1,"TOPIC #GSP!worms3!Mllz4DcahM :test" + "\r\n"), - new KeyValuePair(client1,"MODE #GSP!worms3!Mllz4DcahM +l 2" + "\r\n"), - new KeyValuePair(client1,@"SETCKEY #GSP!worms3!Mllz4DcahM spyguy :\b_firewall\1\b_profileid\1\b_ipaddress\\b_publicip\91.52.107.144\b_privateip\192.168.0.50\b_authresponse\\b_gamever\1073\b_val\0" + "\r\n"), - new KeyValuePair(client1,"WHO spyguy" + "\r\n"), - new KeyValuePair(client1,@"SETCHANKEY #GSP!worms3!Mllz4DcahM :\b_hostname\\b_hostport\\b_MaxPlayers\4\b_NumPlayers\0\b_SchemeChanging\0\b_gamever\1073\b_gametype\\b_mapname\Random\b_firewall\1\b_publicip\91.52.107.144\b_privateip\192.168.0.50\b_gamemode\openstaging\b_val\0\b_password\1" + "\r\n"), - - - - new KeyValuePair(client2,"USRIP" + "\r\n"), - new KeyValuePair(client2,"USER XO4Gqlff1X|31 127.0.0.1 peerchat.gamespy.com :a54aaa669bd5cd97e7799659de33ed22" + "\r\n"), - new KeyValuePair(client2,"NICK unispy" + "\r\n"), - new KeyValuePair(client2,"JOIN #GSP!worms3!Mllz4DcahM" + "\r\n"), - new KeyValuePair(client2,"MODE #GSP!worms3!Mllz4DcahM" + "\r\n"), - - new KeyValuePair(client1,@"GETKEY unispy 002 0 :\b_firewall\b_profileid\b_ipaddress\b_publicip\b_privateip\b_authresponse\b_gamever\b_val" + "\r\n"), - new KeyValuePair(client1,@"GETCKEY #GSP!worms3!Mllz4DcahM unispy 003 0 :\b_firewall\b_profileid\b_ipaddress\b_publicip\b_privateip\b_authresponse\b_gamever\b_val" + "\r\n"), - new KeyValuePair(client1,@"SETCHANKEY #GSP!worms3!Mllz4DcahM :\b_hostname\\b_hostport\\b_MaxPlayers\4\b_NumPlayers\0\b_SchemeChanging\0\b_gamever\1073\b_gametype\\b_mapname\Random\b_firewall\1\b_publicip\91.52.107.144\b_privateip\192.168.0.50\b_gamemode\openstaging\b_val\0\b_password\1" + "\r\n"), - new KeyValuePair(client1,@"SETCHANKEY #GSP!worms3!Mllz4DcahM :\b_hostname\\b_hostport\\b_MaxPlayers\4\b_NumPlayers\0\b_SchemeChanging\0\b_gamever\1073\b_gametype\\b_mapname\Random\b_firewall\1\b_publicip\91.52.107.144\b_privateip\192.168.0.50\b_gamemode\openstaging\b_val\0\b_password\1" + "\r\n"), - new KeyValuePair(client1,"UTM #GSP!worms3!Mllz4DcahM :MDM |Obj|3|Land.Time|0|LogicalSeed|237235259|GraphicalSeed|4280690287|Land.RealSeed|2147483642|Land.Theme|Pirate.Lumps|LevelToUse|FE.Level.RandomLand|Land.Ind|0|Wormpot.Reel1|17|Wormpot.Reel2|17|Wormpot.Reel3|17|TimeStamp|98044" + "\r\n"), - new KeyValuePair(client1,"UTM #GSP!worms3!Mllz4DcahM :TDM aA" + "\r\n"), - new KeyValuePair(client1,"UTM #GSP!worms3!Mllz4DcahM :SDM ASFE.Scheme.StandardCUnAACADCBBCACBBFFBKBB8C/C3C!A!A*C*C(client2,@"GETCKEY #GSP!worms3!Mllz4DcahM * 008 0 :\username\b_flags" + "\r\n"), - new KeyValuePair(client2,"UTM #GSP!worms3!Mllz4DcahM :APE [01]privateip[02]192.168.0.113[01]publicip[02]91.52.107.144" + "\r\n"), - new KeyValuePair(client2,"WHO unispy" + "\r\n"), - new KeyValuePair(client2,@"GETCKEY #GSP!worms3!Mllz4DcahM spyguy 009 0 :\b_firewall\b_profileid\b_ipaddress\b_publicip\b_privateip\b_authresponse\b_gamever\b_val") - }; - foreach (var item in requests) - { - ((ITestClient)(item.Key)).TestReceived(UniSpyEncoding.GetBytes(item.Value)); - } - } - - [Fact] - public void CrysisWars20230514() - { - // Given - // some game just use chat to authenticate do not use chat server to chat - var client = MockObject.CreateClient(); - var requests = new List() - { - "CRYPT des 1 crysiswars\r\n", - "LOGIN 56 crysiswars3 4f83fb3e73b69253048ab90d9efb335c\r\n", - "USER 127.0.0.1 peerchat.gamespy.com :\r\nNICK *\r\n", - "LIST *\r\nJOIN #gsp!crysiswars\r\n", - "MODE #gsp!crysiswars\r\nSETCKEY #gsp!crysiswars crysiswars3-cry "+@":\Profile\11\\DE\TimePlayed\0\Accuracy\0\KillsPerMinute\0\Kills\0\Deaths\0\FavoriteGameMode\\FavoriteMap\\FavoriteWeapon\\FavoriteVehicle\\FavoriteSuitMode\"+"\r\n" - }; - foreach (var req in requests) - { - (client as ITestClient).TestReceived(UniSpyEncoding.GetBytes(req)); - } - } - - [Fact] - public void WormsFortsUnderSiege20230520() - { - var client = MockObject.CreateClient(); - var requests = new List() - { - "CRYPT des 1 wormsforts\r\n", - "LOGIN 1 wforts 149815eb972b3c370dee3b89d645ae14\r\n", - "USRIP\r\n", - "USER XaWqp4Df1X|19 127.0.0.1 peerchat.gamespy.com :52d55c23867fcafe3453c00a3a395503\r\nNICK *\r\n" - }; - foreach (var req in requests) - { - (client as ITestClient).TestReceived(UniSpyEncoding.GetBytes(req)); - } - } - - [Fact] - public void Crysis2_20230926() - { - var client = MockObject.CreateClient(); - - var requests = new List{ - "CRYPT des 1 capricorn\r\n", - "LOGIN 95 Sporesirius baec04ae6862e941b948c8a1a361c096\r\n", - "USRIP\r\n", - "USER XpaGflff1X|39 127.0.0.1 peerchat.unispy.dev :e51130924b08de265d9ae69113103f78\r\nNICK *\r\n", - "QUIT :Later!\r\n" - }; - foreach (var req in requests) - { - (client as ITestClient).TestReceived(UniSpyEncoding.GetBytes(req)); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/test/General/GeneralRequestTest.cs b/src/Servers/Chat/test/General/GeneralRequestTest.cs deleted file mode 100644 index a56a2f83f..000000000 --- a/src/Servers/Chat/test/General/GeneralRequestTest.cs +++ /dev/null @@ -1,220 +0,0 @@ -using Xunit; -using UniSpy.Server.Chat.Contract.Request.General; - -namespace UniSpy.Server.Chat.Test.General -{ - public class GeneralRequestTest - { - [Fact] - public void CDKey() - { - var request = new CdKeyRequest(GeneralRequests.CDKey); - request.Parse(); - Assert.Equal("XXXX-XXXX-XXXX-XXXX", request.CdKey); - } - - [Fact] - public void Crypt() - { - var request = new CryptRequest(GeneralRequests.Crypt); - request.Parse(); - Assert.Equal("1", request.VersionID); - Assert.Equal("gmtest", request.GameName); - } - - [Fact] - public void GetKey() - { - var request = new GetKeyRequest(GeneralRequests.GetKey); - request.Parse(); - Assert.Equal("spyguy", request.NickName); - Assert.Equal("004", request.Cookie); - Assert.Equal("0", request.UnkownCmdParam); - Assert.Equal("b_firewall", request.Keys[0]); - Assert.Equal("b_profileid", request.Keys[1]); - Assert.Equal("b_ipaddress", request.Keys[2]); - Assert.Equal("b_publicip", request.Keys[3]); - Assert.Equal("b_privateip", request.Keys[4]); - Assert.Equal("b_authresponse", request.Keys[5]); - Assert.Equal("b_gamever", request.Keys[6]); - Assert.Equal("b_val", request.Keys[7]); - } - - [Fact(Skip = "No reqeust")] - public void GetUdpRelay() - { - var request = new GetUdpRelayRequest(GeneralRequests.GetUdpRelay); - request.Parse(); - } - - [Fact] - public void Invite() - { - var request = new InviteRequest(GeneralRequests.Invite); - request.Parse(); - Assert.Equal("test", request.ChannelName); - Assert.Equal("spyguy", request.NickName); - } - - [Fact] - public void ListLimit() - { - var request = new ListLimitRequest(GeneralRequests.ListLimit); - request.Parse(); - Assert.Equal("5", request.MaxNumberOfChannels.ToString()); - Assert.Equal("test", request.Filter); - } - - [Fact] - public void List() - { - var request = new ListRequest(GeneralRequests.List); - request.Parse(); - Assert.Equal("test", request.Filter); - } - - [Fact] - public void LoginPreAuth() - { - var request = new LoginPreAuth(GeneralRequests.LoginPreAuth); - request.Parse(); - Assert.Equal("xxxxx", request.AuthToken); - Assert.Equal("yyyyy", request.PartnerChallenge); - } - - [Fact] - public void LoginNickAndEmail() - { - var request = new LoginRequest(GeneralRequests.LoginNickAndEmail); - request.Parse(); - Assert.Equal(LoginReqeustType.NickAndEmailLogin, request.ReqeustType); - Assert.Equal("0", request.NamespaceId.ToString()); - Assert.Equal("spyguy", request.NickName); - Assert.Equal("xxxxx", request.PasswordHash); - Assert.Equal("spyguy@unispy.org", request.Email); - } - - [Fact] - public void LoginUniqueNick() - { - var request = new LoginRequest(GeneralRequests.LoginUniqueNick); - request.Parse(); - Assert.Equal(LoginReqeustType.UniqueNickLogin, request.ReqeustType); - Assert.Equal("0", request.NamespaceId.ToString()); - Assert.Equal("spyguy", request.UniqueNick); - Assert.Equal("xxxxx", request.PasswordHash); - } - - [Fact(Skip = "No reqeust")] - public void Names() - { - var request = new NamesRequest(GeneralRequests.Names); - request.Parse(); - } - - [Fact] - public void Nick() - { - var request = new NickRequest(GeneralRequests.Nick); - request.Parse(); - Assert.Equal("spyguy", request.NickName); - } - - [Fact(Skip = "No reqeust")] - public void Ping() - { - var request = new PingRequest(GeneralRequests.Ping); - request.Parse(); - } - - [Fact] - public void Pong() - { - var request = new PongRequest(GeneralRequests.Pong); - request.Parse(); - Assert.Equal("Pong!", request.EchoMessage); - } - - [Fact] - public void Quit() - { - var request = new QuitRequest(GeneralRequests.Quit); - request.Parse(); - Assert.Equal("Later!", request.Reason); - } - - [Fact] - public void RegisterNick() - { - var request = new RegisterNickRequest(GeneralRequests.RegisterNick); - request.Parse(); - Assert.Equal("0", request.NamespaceID); - Assert.Equal("spyguy", request.UniqueNick); - Assert.Equal("XXXX-XXXX-XXXX-XXXX", request.CDKey); - } - - [Fact] - public void SetGroup() - { - var request = new SetGroupRequest(GeneralRequests.SetGroup); - request.Parse(); - Assert.Equal("test", request.GroupName); - } - - [Fact(Skip = "TODO: add test")] - public void SetKey() - { - var request = new SetKeyRequest(GeneralRequests.SetKey); - request.Parse(); - } - - [Fact(Skip = "No reqeust")] - public void UserIP() - { - var request = new UserIPRequest(GeneralRequests.UserIP); - request.Parse(); - } - - [Fact] - public void User() - { - var request = new UserRequest(GeneralRequests.User); - request.Parse(); - Assert.Equal("spyguy", request.UserName); - Assert.Equal("127.0.0.1", request.Hostname); - Assert.Equal("peerchat.unispy.org", request.ServerName); - Assert.Equal("spyguy2", request.Name); - - request = new UserRequest("USER 127.0.0.1 peerchat.gamespy.com :"); - request.Parse(); - Assert.Equal("127.0.0.1", request.Hostname); - Assert.Equal("peerchat.gamespy.com", request.ServerName); - } - - [Fact] - public void WhoIs() - { - var request = new WhoIsRequest(GeneralRequests.WhoIs); - request.Parse(); - Assert.Equal("spyguy", request.NickName); - } - - [Fact] - public void WhoChannelUsersInfo() - { - var request = new WhoRequest(GeneralRequests.WhoChannelUsersInfo); - request.Parse(); - Assert.Equal(WhoRequestType.GetChannelUsersInfo, request.RequestType); - Assert.Equal("#room", request.ChannelName); - } - - [Fact] - public void WhoUserInfo() - { - var request = new WhoRequest(GeneralRequests.WhoUserInfo); - request.Parse(); - Assert.Equal(WhoRequestType.GetUserInfo, request.RequestType); - Assert.Equal("spyguy", request.NickName); - } - } -} diff --git a/src/Servers/Chat/test/General/GeneralRequests.cs b/src/Servers/Chat/test/General/GeneralRequests.cs deleted file mode 100644 index 34fb6e235..000000000 --- a/src/Servers/Chat/test/General/GeneralRequests.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace UniSpy.Server.Chat.Test.General -{ - public class GeneralRequests - { - public const string CDKey = "CDKEY XXXX-XXXX-XXXX-XXXX"; - - public const string Crypt = "CRYPT des 1 gmtest"; - - public const string GetKey = "GETKEY spyguy 004 0 :\\b_firewall\\b_profileid\\b_ipaddress\\b_publicip\\b_privateip\\b_authresponse\\b_gamever\\b_val"; // CRLF - - public const string GetUdpRelay = "GETUDPRELAY"; - - public const string Invite = "INVITE test spyguy"; - - public const string ListLimit = "LISTLIMIT 5 test"; - - public const string List = "LIST test"; - - public const string LoginPreAuth = "LOGINPREAUTH xxxxx yyyyy"; - - public const string LoginNickAndEmail = "LOGIN 0 * xxxxx :spyguy@spyguy@unispy.org"; // TODO: add binary data test [0D][0A] - - public const string LoginUniqueNick = "LOGIN 0 spyguy xxxxx"; - - public const string Names = "NAMES"; - - public const string Nick = "NICK :spyguy"; - - public const string Ping = "PING"; // TODO: add binary data test [0D][0A] - - public const string Pong = "PONG :Pong!"; - - public const string Quit = "QUIT :Later!"; // TODO: add binary data test [0D][0A] - - public const string RegisterNick = "REGISTERNICK 0 spyguy XXXX-XXXX-XXXX-XXXX"; - - public const string SetGroup = "SETGROUP test"; - - public const string SetKey = "SETKEY :test"; - - public const string UserIP = "USRIP"; - - public const string User = "USER spyguy 127.0.0.1 peerchat.unispy.org :spyguy2"; - - public const string WhoIs = "WHOIS spyguy"; - - public const string WhoChannelUsersInfo = "WHO #room"; - - public const string WhoUserInfo = "WHO spyguy"; - } -} diff --git a/src/Servers/Chat/test/Message/MessageRequestTest.cs b/src/Servers/Chat/test/Message/MessageRequestTest.cs deleted file mode 100644 index 81fe0dc85..000000000 --- a/src/Servers/Chat/test/Message/MessageRequestTest.cs +++ /dev/null @@ -1,50 +0,0 @@ -using Xunit; -using UniSpy.Server.Chat.Contract.Request.Message; -using UniSpy.Server.Chat.Abstraction.BaseClass; - -namespace UniSpy.Server.Chat.Test.Message -{ - public class MessageRequestTest - { - - [Fact] - public void AboveTheTableMsg() - { - var request = new AtmRequest(MessageRequests.AboveTheTableMsg); - request.Parse(); - Assert.Equal(MessageType.ChannelMessage, request.Type); - Assert.Null(request.NickName); - Assert.Equal("#GSP!room!test", request.ChannelName); - } - - [Fact] - public void Notice() - { - var request = new NoticeRequest(MessageRequests.Notice); - request.Parse(); - Assert.Equal(MessageType.ChannelMessage, request.Type); - Assert.Null(request.NickName); - Assert.Equal("#GSP!room!test", request.ChannelName); - } - - [Fact] - public void PrivateMsg() - { - var request = new PrivateRequest(MessageRequests.PrivateMsg); - request.Parse(); - Assert.Equal(MessageType.ChannelMessage, request.Type); - Assert.Null(request.NickName); - Assert.Equal("#GSP!room!test", request.ChannelName); - } - - [Fact] - public void UnderTheTableMsg() - { - var request = new UtmRequest(MessageRequests.UnderTheTableMsg); - request.Parse(); - Assert.Equal(MessageType.ChannelMessage, request.Type); - Assert.Null(request.NickName); - Assert.Equal("#GSP!room!test", request.ChannelName); - } - } -} diff --git a/src/Servers/Chat/test/Message/MessageRequests.cs b/src/Servers/Chat/test/Message/MessageRequests.cs deleted file mode 100644 index e4c4b96ad..000000000 --- a/src/Servers/Chat/test/Message/MessageRequests.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace UniSpy.Server.Chat.Test.Message -{ - public class MessageRequests - { - public const string AboveTheTableMsg = "ATM #GSP!room!test :hello this is a test."; - - public const string Notice = "NOTICE #GSP!room!test :hello this is a test."; - - public const string PrivateMsg = "PRIVMSG #GSP!room!test :hello this is a test."; - - public const string UnderTheTableMsg = "UTM #GSP!room!test :hello this is a test."; - - public const string ActionMsg = "PRIVMSG #GSP!room!test :\001ACTION hello this is a test.\001"; - } -} diff --git a/src/Servers/Chat/test/MockObject.cs b/src/Servers/Chat/test/MockObject.cs deleted file mode 100644 index 5db332c77..000000000 --- a/src/Servers/Chat/test/MockObject.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Linq; -using System.Net; -using Moq; -using UniSpy.Server.Chat.Application; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Chat.Test -{ - public class MockObject - { - public static IClient CreateClient(string ipAddress = "79.209.235.252", int port = 50558) - { - var managerMock = new Mock(); - var connectionMock = new Mock(); - connectionMock.Setup(s => s.RemoteIPEndPoint).Returns(new IPEndPoint(IPAddress.Parse(ipAddress), port)); - connectionMock.Setup(s => s.Manager).Returns(managerMock.Object); - connectionMock.Setup(s => s.ConnectionType).Returns(NetworkConnectionType.Tcp); - var serverMock = new Chat.Application.Server(managerMock.Object); - var client = new Client(connectionMock.Object, serverMock); - client.Info.GameName = "gmtest"; - ClientManager.ClientPool.TryAdd(client.Connection.RemoteIPEndPoint, client); - // if(ServerLauncher.ServerInstances.Count(s=>s.id)) - // ServerLauncher.ServerInstances.Add(serverMock); - return client; - } - } -} \ No newline at end of file diff --git a/src/Servers/Chat/test/RedisChatChannelTest.cs b/src/Servers/Chat/test/RedisChatChannelTest.cs deleted file mode 100644 index 3323c1524..000000000 --- a/src/Servers/Chat/test/RedisChatChannelTest.cs +++ /dev/null @@ -1,169 +0,0 @@ -using UniSpy.Server.Chat.Application; -using Xunit; -using Newtonsoft.Json; -using UniSpy.Server.Chat.Aggregate.Redis.Contract; -using UniSpy.Server.Chat.Contract.Request.Channel; -using UniSpy.Server.Chat.Test.Channel; -using UniSpy.Server.Chat.Handler.CmdHandler.Channel; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.Chat.Contract.Request.General; -using UniSpy.Server.Chat.Test.General; -using UniSpy.Server.Core.Abstraction.Interface; -using System.Collections.Generic; -using System.Net; -using Moq; -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate.Misc; - -namespace UniSpy.Server.Chat.Test -{ - public class RedisChatChannelTest - { - public RedisChatChannelTest() - { - var mockServer = new Mock(); - mockServer.Setup(s => s.PublicIPEndPoint).Returns(IPEndPoint.Parse("202.91.0.1:123")); - mockServer.Setup(s => s.Id).Returns(System.Guid.Parse("00000000-0000-0000-0000-000000000000")); - ServerLauncher.ServerInstances.Add(mockServer.Object); - } - - [Fact] - public void JsonSerialization() - { - var client = MockObject.CreateClient() as Client; - client.Info.IsLoggedIn = true; - client.Info.NickName = "xiaojiuwo1"; - var remoteClient = client.GetRemoteClient(); - var request = new JoinRequest(ChannelRequests.Join); - request.Parse(); - var clientStr = JsonConvert.SerializeObject(remoteClient); - var clientObj = JsonConvert.DeserializeObject(clientStr); - var message = new RemoteMessage(request, remoteClient); - var msgStr = JsonConvert.SerializeObject(message); - var msgObj = JsonConvert.DeserializeObject(msgStr); - Assert.True(msgObj.Type == "JOIN"); - var req = new JoinRequest(UniSpyEncoding.GetString(msgObj.RawRequest)); - var handler = new JoinHandler(msgObj.Client, req); - handler.Handle(); - Assert.True(((ClientInfo)(msgObj.Client.Info)).JoinedChannels.Count == 1); - } - [Fact] - public void RedisChannel() - { - var client = MockObject.CreateClient() as Client; - - client.Info.IsLoggedIn = true; - client.Info.NickName = "xiaojiuwo2"; - var remoteClient = client.GetRemoteClient(); - var request = new JoinRequest(ChannelRequests.Join); - request.Parse(); - remoteClient.Crypto = new ChatCrypt("hello"); - var message = new RemoteMessage(request, remoteClient); - var msgStr = JsonConvert.SerializeObject(message); - var msgObj = JsonConvert.DeserializeObject(msgStr); - // var chan = new GeneralMessageChannel(); - // chan.ReceivedMessage(msgObj); - } - [Fact] - public void Crypt() - { - - var request1 = new QuitRequest() { Reason = "client disconnected." }; - var client = MockObject.CreateClient() as Client; - - client.Info.IsLoggedIn = true; - client.Info.NickName = "xiaojiuwo3"; - var remoteClient = client.GetRemoteClient(); - var request = new CryptRequest(GeneralRequests.Crypt); - request.Parse(); - var message = new RemoteMessage(request, remoteClient); - // var chan = new GeneralMessageChannel(); - // chan.ReceivedMessage(message); - } - [Fact] - public void GeneralTest() - { - var type = typeof(GeneralRequests); - // var fields = type.GetFields(); - var request1 = new List() - { - "CRYPT des 1 worms3\r\n", - "USRIP\r\n", - "USER X419pGl4sX|7 127.0.0.1 peerchat.gamespy.com :9964fa3fe73f6a1fbb986d2a04b1eb65\r\n", - "NICK worms10\r\n", - "JOIN #GPG!622\r\n", - "MODE #GPG!622\r\n", - @"GETCKEY #GPG!622 * 000 0 :\username\b_flags"+"\r\n", - "PRIVMSG #GPG!622 :hello\r\n", - "JOIN #GSP!worms3!MJ0NJ4c3aM\r\n", - "MODE #GSP!worms3!MJ0NJ4c3aM\r\n", - @"SETCKEY #GPG!622 worms10 :\b_flags\s\r\n", - @"SETCKEY #GSP!worms3!MJ0NJ4c3aM worms10 :\b_flags\sh"+"\r\n", - @"GETCKEY #GSP!worms3!MJ0NJ4c3aM * 001 0 :\username\b_flags"+"\r\n", - "TOPIC #GSP!worms3!MJ0NJ4c3aM :main lobby message test\r\n", - "MODE #GSP!worms3!MJ0NJ4c3aM +l 2"+"\r\n", - "PART #GPG!622 :Joined staging room\r\n", - @"SETCKEY #GSP!worms3!MJ0NJ4c3aM worms10 :\b_firewall\1\b_profileid\7\b_ipaddress\\b_publicip\255.255.255.255\b_privateip\192.168.0.145\b_authresponse\\b_gamever\1073\b_val\0\r\n", - "WHO worms10"+"\r\n", - @"SETCHANKEY #GSP!worms3!MJ0NJ4c3aM :\b_hostname\\b_hostport\\b_MaxPlayers\2\b_NumPlayers\0\b_SchemeChanging\0\b_gamever\1073\b_gametype\\b_mapname\Random\b_firewall\1\b_publicip\255.255.255.255\b_privateip\192.168.0.145\b_gamemode\openstaging\b_val\0\b_password\1"+"\r\n", - "PRIVMSG #GSP!worms3!MJ0NJ4c3aM :staging lobby message test"+"\r\n", - @"UTM #GSP!worms3!MJ0NJ4c3aM :ATS \nick\worms10\msg\2046\|name|Human Team 1|w0|Dave|w1|Patrick|w2|The Morriss|w3|Mike|w4|Tony|w5|Gluckman|skill|0|grave|0|SWeapon|0|flag|71|speech|Classic|InGame|0|player|worms10|alliance|1|wormCount|77111416"+"\r\n", - @"UTM #GSP!worms3!MJ0NJ4c3aM :RTS \team\Human Team 1\nick\worms10\msg\2174"+"\r\n" - }; - var client1 = MockObject.CreateClient(port: 1234) as Client; - // Client.ClientPool.Remove(client1.Connection.RemoteIPEndPoint, out _); - ClientManager.RemoveClient(client1); - var remoteClient = client1.GetRemoteClient() as ITestClient; - ClientManager.AddClient((IShareClient)remoteClient); - foreach (var r in request1) - { - var count = ClientManager.ClientPool.Count; - remoteClient.TestReceived(UniSpyEncoding.GetBytes(r)); - count = ClientManager.ClientPool.Count; - } - // IChatClient - var request2 = new List() - { - // "CRYPT des 1 worms3\r\n", - "USRIP\r\n", - "USER X419pGl4sX|6 127.0.0.1 peerchat.gamespy.com :dd6283e1e349806c20991020e0d6897a\r\n", - "NICK xiaojiuwo4\r\n", - "JOIN #GPG!622\r\n" , - "MODE #GPG!622\r\n", - @"GETCKEY #GPG!622 * 000 0 :\username\b_flags"+"\r\n", - "PING\r\n", - "PRIVMSG #GPG!622 :hello from the other side\r\n", - "JOIN #GSP!worms3!MJ0NJ4c3aM\r\n", - "MODE #GSP!worms3!MJ0NJ4c3aM\r\n", - @"SETCKEY #GPG!622 xiaojiuwo4 :\b_flags\s"+"\r\n", - @"SETCKEY #GSP!worms3!MJ0NJ4c3aM xiaojiuwo4 :\b_flags\s"+"\r\n", - @"GETCKEY #GSP!worms3!MJ0NJ4c3aM * 001 0 :\username\b_flags"+"\r\n", - // "PART #GPG!622 :Joined staging room\r\n", - "UTM #GSP!worms3!MJ0NJ4c3aM :APE [01]privateip[02]192.168.0.109[01]publicip[02]255.255.255.255\r\n", - "WHO xiaojiuwo4\r\n", - // "PART #GSP!worms3!MJ0NJ4c3aM :Left Game\r\n" - }; - var client2 = MockObject.CreateClient(port: 1235) as ITestClient; - ClientManager.AddClient((IShareClient)client2); - foreach (var r in request2) - { - var count = ClientManager.ClientPool.Count; - client2.TestReceived(UniSpyEncoding.GetBytes(r)); - count = ClientManager.ClientPool.Count; - } - } - [Fact] - public void ChannelInfoTest() - { - var client = MockObject.CreateClient() as Client; - client.Info.IsLoggedIn = true; - client.Info.NickName = "xiaojiuwo"; - var channel = Aggregate.Channel.CreateLocalChannel("xiaojiuwo", creator: client); - var user = channel.GetUser(client); - - var channelStr = JsonConvert.SerializeObject(channel); - var channelObj = JsonConvert.DeserializeObject(channelStr); - } - } -} diff --git a/src/Servers/Chat/test/UniSpy.Server.Chat.Test.csproj b/src/Servers/Chat/test/UniSpy.Server.Chat.Test.csproj deleted file mode 100644 index 4970e5e64..000000000 --- a/src/Servers/Chat/test/UniSpy.Server.Chat.Test.csproj +++ /dev/null @@ -1,32 +0,0 @@ - - - - net6.0 - false - ..\..\..\..\common\Icon\UniSpy_Logo.ico - - - AnyCPU - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - \ No newline at end of file diff --git a/src/Servers/GameStatus/src/Abstraction/BaseClass/CmdHandlerBase.cs b/src/Servers/GameStatus/src/Abstraction/BaseClass/CmdHandlerBase.cs deleted file mode 100755 index fceb1c697..000000000 --- a/src/Servers/GameStatus/src/Abstraction/BaseClass/CmdHandlerBase.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.GameStatus.Application; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.GameStatus.Abstraction.BaseClass -{ - /// - /// we only use selfdefine error code here - /// so we do not need to send it to client - /// - public abstract class CmdHandlerBase : UniSpy.Server.Core.Abstraction.BaseClass.CmdHandlerBase - { - protected new Client _client => (Client)base._client; - protected new RequestBase _request => (RequestBase)base._request; - protected new ResultBase _result { get => (ResultBase)base._result; set => base._result = value; } - protected CmdHandlerBase(IClient client, IRequest request) : base(client, request) - { - } - } -} diff --git a/src/Servers/GameStatus/src/Abstraction/BaseClass/RequestBase.cs b/src/Servers/GameStatus/src/Abstraction/BaseClass/RequestBase.cs deleted file mode 100755 index bd8b0098c..000000000 --- a/src/Servers/GameStatus/src/Abstraction/BaseClass/RequestBase.cs +++ /dev/null @@ -1,52 +0,0 @@ - -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.Core.Misc; - -namespace UniSpy.Server.GameStatus.Abstraction.BaseClass -{ - public abstract class RequestBase : UniSpy.Server.Core.Abstraction.BaseClass.RequestBase - { - public new string CommandName { get => (string)base.CommandName; protected set => base.CommandName = value; } - public new string RawRequest => (string)base.RawRequest; - /// - /// LocalId is used to indicate the request sequence in gamespy clients - /// - public int? LocalId { get; protected set; } - public Dictionary KeyValues { get; protected set; } - - public RequestBase(string rawRequest) : base(rawRequest) - { - } - public static Dictionary ConvertGameDataToKeyValues(string gameData) - { - var parts = gameData.Split("\u0001", System.StringSplitOptions.RemoveEmptyEntries); - return GameSpyUtils.ConvertToKeyValue(parts); - } - public override void Parse() - { - // the localid lid is used to indicate the request sequence on the client side - // we just respond it with same lid to client - KeyValues = GameSpyUtils.ConvertToKeyValue(RawRequest); - CommandName = KeyValues.Keys.First(); - - if (KeyValues.ContainsKey("lid")) - { - if (!int.TryParse(KeyValues["lid"], out var localId)) - { - throw new GameStatus.Exception("localid format is incorrect."); - } - LocalId = localId; - } - //worms 3d use id not lid so we added an condition here - if (KeyValues.ContainsKey("id")) - { - if (!int.TryParse(KeyValues["id"], out var localId)) - { - throw new GameStatus.Exception("localid format is incorrect."); - } - LocalId = localId; - } - } - } -} diff --git a/src/Servers/GameStatus/src/Abstraction/BaseClass/ResponseBase.cs b/src/Servers/GameStatus/src/Abstraction/BaseClass/ResponseBase.cs deleted file mode 100755 index fe304674b..000000000 --- a/src/Servers/GameStatus/src/Abstraction/BaseClass/ResponseBase.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace UniSpy.Server.GameStatus.Abstraction.BaseClass -{ - public abstract class ResponseBase : UniSpy.Server.Core.Abstraction.BaseClass.ResponseBase - { - protected new RequestBase _request => (RequestBase)base._request; - protected new ResultBase _result => (ResultBase)base._result; - protected new string SendingBuffer{ get => (string)base.SendingBuffer; set => base.SendingBuffer = value; } - public ResponseBase(UniSpy.Server.Core.Abstraction.BaseClass.RequestBase request, UniSpy.Server.Core.Abstraction.BaseClass.ResultBase result) : base(request, result) - { - } - - } -} diff --git a/src/Servers/GameStatus/src/Abstraction/BaseClass/ResultBase.cs b/src/Servers/GameStatus/src/Abstraction/BaseClass/ResultBase.cs deleted file mode 100755 index cdc1440fa..000000000 --- a/src/Servers/GameStatus/src/Abstraction/BaseClass/ResultBase.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UniSpy.Server.GameStatus.Abstraction.BaseClass -{ - public abstract class ResultBase : UniSpy.Server.Core.Abstraction.BaseClass.ResultBase - { - public ResultBase() - { - } - } -} diff --git a/src/Servers/GameStatus/src/Abstraction/Interface/IStorageOperation.cs b/src/Servers/GameStatus/src/Abstraction/Interface/IStorageOperation.cs deleted file mode 100644 index 606d7894a..000000000 --- a/src/Servers/GameStatus/src/Abstraction/Interface/IStorageOperation.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.GameStatus.Enumerate; - -namespace UniSpy.Server.GameStatus.Abstraction.Interface -{ - public interface IStorageOperation - { - void CreateNewGameData(); - void CreateNewPlayerData(Dictionary playerData); - void UpdatePlayerData(int profileId, PersistStorageType storageType, int dataIndex, Dictionary data); - int GetProfileId(string token); - int GetProfileId(string cdKey, string nickName); - int GetProfileId(int profileId); - Dictionary GetPlayerData(int profileId, PersistStorageType storageType, int dataIndex); - } -} \ No newline at end of file diff --git a/src/Servers/GameStatus/src/Aggregate/Misc/GSCrypt.cs b/src/Servers/GameStatus/src/Aggregate/Misc/GSCrypt.cs deleted file mode 100755 index 9d45bba81..000000000 --- a/src/Servers/GameStatus/src/Aggregate/Misc/GSCrypt.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Linq; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; - - -namespace UniSpy.Server.GameStatus.Aggregate.Misc -{ - public class GSCrypt : ICryptography - { - /// - /// Decrypt the message, skip decrypt \final\ - /// Must contains \final\ - /// - public byte[] Decrypt(byte[] buffer) - { - if (!UniSpyEncoding.GetString(buffer).Contains(@"\final\")) - { - throw new GameStatus.Exception(@"Ciphertext must contains delimeter \final\"); - } - //remove \final\, later we add final back - byte[] cipher = buffer.Take(buffer.Length - 7).ToArray(); - byte[] plain = XOREncoding.Encode(cipher, XorType.Type1); - Array.Copy(plain, buffer, plain.Length); - return buffer; - } - /// - /// Encrypt the message, skip encrypt \final\ - /// - public byte[] Encrypt(byte[] buffer) - { - if (!UniSpyEncoding.GetString(buffer).Contains(@"\final\")) - { - throw new GameStatus.Exception(@"Plaintext must contains delimeter \final\"); - } - byte[] plain = buffer.Take(buffer.Length - 7).ToArray(); - byte[] cipher = XOREncoding.Encode(plain, XorType.Type1); - Array.Copy(cipher, buffer, cipher.Length); - return buffer; - } - } -} diff --git a/src/Servers/GameStatus/src/Application/Client.cs b/src/Servers/GameStatus/src/Application/Client.cs deleted file mode 100644 index b4402a580..000000000 --- a/src/Servers/GameStatus/src/Application/Client.cs +++ /dev/null @@ -1,51 +0,0 @@ -using UniSpy.Server.GameStatus.Aggregate.Misc; -using UniSpy.Server.GameStatus.Handler; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.Core.Logging; -using System; - -namespace UniSpy.Server.GameStatus.Application -{ - public sealed class Client : ClientBase - { - public new ClientInfo Info { get => (ClientInfo)base.Info; private set => base.Info = value; } - public Client(IConnection connection, IServer server) : base(connection, server) - { - Info = new ClientInfo(); - } - protected override void OnConnected() - { - Crypto = new GSCrypt(); - this.LogNetworkSending(ClientInfo.ChallengeResponse); - Connection.Send(Crypto.Encrypt(ClientInfo.ChallengeResponseBytes)); - base.OnConnected(); - } - protected override byte[] DecryptMessage(byte[] buffer) - { - // unit test - if (Crypto is null) - { - return buffer; - } - - // multiple request; - var buffers = UniSpyEncoding.GetString(buffer).Split(@"\final\", StringSplitOptions.RemoveEmptyEntries); - if (buffers.Length > 1) - { - string message = ""; - foreach (var buf in buffers) - { - var completeBuf = UniSpyEncoding.GetBytes(buf + @"\final\"); - message += UniSpyEncoding.GetString(Crypto.Decrypt(completeBuf)); - } - return UniSpyEncoding.GetBytes(message); - } - return Crypto.Decrypt(buffer); - } - - - protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, UniSpyEncoding.GetString((byte[])buffer)); - } -} \ No newline at end of file diff --git a/src/Servers/GameStatus/src/Application/ClientInfo.cs b/src/Servers/GameStatus/src/Application/ClientInfo.cs deleted file mode 100644 index 088d828ab..000000000 --- a/src/Servers/GameStatus/src/Application/ClientInfo.cs +++ /dev/null @@ -1,21 +0,0 @@ -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Encryption; - -namespace UniSpy.Server.GameStatus.Application -{ - public sealed class ClientInfo : ClientInfoBase - { - public const string ChallengeResponse = @"\challenge\00000000000000000000\final\"; - public static byte[] ChallengeResponseBytes => UniSpyEncoding.GetBytes(ClientInfo.ChallengeResponse); - public int? SessionKey { get; set; } - public string GameName { get; set; } - public bool IsUserAuthenticated { get; set; } - public bool IsPlayerAuthenticated { get; set; } - public bool IsGameAuthenticated { get; set; } - public int? ProfileId { get; set; } - public int? GameSessionKey { get; set; } - public ClientInfo() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/GameStatus/src/Application/Program.cs b/src/Servers/GameStatus/src/Application/Program.cs deleted file mode 100755 index 5c41c26ca..000000000 --- a/src/Servers/GameStatus/src/Application/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.GameStatus.Application -{ - public class Program - { - static void Main(string[] args) - { - try - { - new ServerLauncher().Start(); - Console.WriteLine("Press < Q > to exit. "); - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - catch (System.Exception e) - { - UniSpy.Exception.HandleException(e); - } - finally - { - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - } - } -} diff --git a/src/Servers/GameStatus/src/Application/Server.cs b/src/Servers/GameStatus/src/Application/Server.cs deleted file mode 100644 index 7ec2ccd78..000000000 --- a/src/Servers/GameStatus/src/Application/Server.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Net; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Network.Tcp.Server; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.GameStatus.Application -{ - public sealed class Server : ServerBase - { - static Server() - { - _name = "GameStatus"; - } - - public Server(){ } - - public Server(IConnectionManager manager) : base(manager){} - - protected override IClient CreateClient(IConnection connection) => new Client(connection, this); - protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new TcpConnectionManager(endPoint); - } -} \ No newline at end of file diff --git a/src/Servers/GameStatus/src/Application/ServerLauncher.cs b/src/Servers/GameStatus/src/Application/ServerLauncher.cs deleted file mode 100644 index dd4658ce7..000000000 --- a/src/Servers/GameStatus/src/Application/ServerLauncher.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.BaseClass.Factory; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.GameStatus.Application -{ - public sealed class ServerLauncher : ServerLauncherBase - { - protected override List LaunchNetworkService() => new List { new Server() }; - } -} \ No newline at end of file diff --git a/src/Servers/GameStatus/src/Application/StorageOperation.cs b/src/Servers/GameStatus/src/Application/StorageOperation.cs deleted file mode 100644 index 18e503b8e..000000000 --- a/src/Servers/GameStatus/src/Application/StorageOperation.cs +++ /dev/null @@ -1,120 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.GameStatus.Abstraction.Interface; - -using UniSpy.Server.Core.Database.DatabaseModel; -using System.Linq; -using UniSpy.Server.GameStatus.Enumerate; - -namespace UniSpy.Server.GameStatus.Application -{ - internal sealed class StorageOperation : IStorageOperation - { - public static IStorageOperation Persistance = new StorageOperation(); - public void CreateNewGameData() - { - throw new GameStatus.Exception("Implement create storage for game data"); - } - - public void CreateNewPlayerData(Dictionary playerData) - { - throw new GameStatus.Exception("Implement create storage for player data"); - } - public void UpdatePlayerData(int profileId, PersistStorageType storageType, int dataIndex, Dictionary data) - { - using (var db = new UniSpyContext()) - { - - var result = from p in db.Pstorages - where p.Profileid == profileId - && p.Dindex == dataIndex - && p.Ptype == (int)storageType - select p; - - Pstorage ps; - if (result.Count() == 0) - { - //insert a new record in database - ps = new Pstorage(); - ps.Dindex = dataIndex; - ps.Profileid = profileId; - ps.Ptype = (int)storageType; - ps.Data = data; - db.Pstorages.Add(ps); - } - else if (result.Count() == 1) - { - //update an existed record in database - ps = result.First(); - ps.Data = data; - } - - db.SaveChanges(); - } - } - - public int GetProfileId(string token) - { - using (var db = new UniSpyContext()) - { - var result = from s in db.Subprofiles - where s.Authtoken == token - select s.Profileid; - if (result.Count() != 1) - { - throw new GameStatus.Exception("No records found in database by authtoken."); - } - return result.First(); - } - } - - public int GetProfileId(string cdKey, string nickName) - { - using (var db = new UniSpyContext()) - { - var result = from s in db.Subprofiles - join p in db.Profiles on s.Profileid equals p.Profileid - where s.Cdkeyenc == cdKey && p.Nick == nickName - select s.Profileid; - if (result.Count() != 1) - { - throw new GameStatus.Exception("No records found in database by cdkey hash."); - } - return result.First(); - } - } - - public int GetProfileId(int profileId) - { - using (var db = new UniSpyContext()) - { - var result = from p in db.Profiles - where p.Profileid == profileId - select p.Profileid; - if (result.Count() != 1) - { - throw new GameStatus.Exception("No records found in database by profileid."); - } - return result.First(); - } - } - - public Dictionary GetPlayerData(int profileId, PersistStorageType storageType, int dataIndex) - { - using (var db = new UniSpyContext()) - { - var result = from ps in db.Pstorages - where ps.Ptype == (int)storageType - && ps.Dindex == dataIndex - && ps.Profileid == profileId - select ps.Data; - - if (result.Count() != 1) - { - throw new GameStatus.Exception("No records found in database."); - } - return result.FirstOrDefault(); - } - - } - } -} \ No newline at end of file diff --git a/src/Servers/GameStatus/src/Contract/Request/AuthGameRequest.cs b/src/Servers/GameStatus/src/Contract/Request/AuthGameRequest.cs deleted file mode 100755 index c93150a04..000000000 --- a/src/Servers/GameStatus/src/Contract/Request/AuthGameRequest.cs +++ /dev/null @@ -1,49 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; - - -namespace UniSpy.Server.GameStatus.Contract.Request -{ - /// - /// Request: //auth\\gamename\%s\response\%s\port\%d\id\1 */ - /// - - public sealed class AuthGameRequest : RequestBase - { - public string GameName { get; private set; } - public int Port { get; private set; } - public AuthGameRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - if (!KeyValues.ContainsKey("lid") && !KeyValues.ContainsKey("id")) - { - throw new GameStatus.Exception("localid is missing."); - } - base.Parse(); - if (!KeyValues.ContainsKey("gamename")) - { - throw new GameStatus.Exception("gamename is missing."); - } - - if (!KeyValues.ContainsKey("response")) - { - throw new GameStatus.Exception("response is missing."); - } - - if (KeyValues.ContainsKey("port")) - { - int port; - if (!int.TryParse(KeyValues["port"], out port)) - { - throw new GameStatus.Exception("port format is incorrect."); - } - Port = port; - } - - GameName = KeyValues["gamename"]; - } - } -} diff --git a/src/Servers/GameStatus/src/Contract/Request/AuthPlayerRequest.cs b/src/Servers/GameStatus/src/Contract/Request/AuthPlayerRequest.cs deleted file mode 100755 index a6968d249..000000000 --- a/src/Servers/GameStatus/src/Contract/Request/AuthPlayerRequest.cs +++ /dev/null @@ -1,59 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; -using UniSpy.Server.GameStatus.Enumerate; - - -namespace UniSpy.Server.GameStatus.Contract.Request -{ - // worm3d \authp\\pid\1\resp\7b6658e99f448388fbeddc93654e6dd4\lid\295[19][17]R([1B]zm}BKy[16]+sOhT[07][7F]{/[04]sz;j[00][15]RG=[16][1B]jBP9\final\ - public sealed class AuthPlayerRequest : RequestBase - { - public AuthMethod RequestType { get; private set; } - public int ProfileId { get; private set; } - public string AuthToken { get; private set; } - public string Response { get; private set; } - public string CdKeyHash { get; private set; } - public string NickName { get; private set; } - - public AuthPlayerRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - - if (!KeyValues.ContainsKey("lid") && !KeyValues.ContainsKey("id")) - { - throw new GameStatus.Exception("localid is missing."); - } - - if (KeyValues.ContainsKey("pid") && KeyValues.ContainsKey("resp")) - { - //we parse profileid here - int profileID; - if (!int.TryParse(KeyValues["pid"], out profileID)) - { - throw new GameStatus.Exception("pid format is incorrect."); - } - ProfileId = profileID; - RequestType = AuthMethod.ProfileIdAuth; - } - else if (KeyValues.ContainsKey("authtoken") && KeyValues.ContainsKey("response")) - { - AuthToken = KeyValues["authtoken"]; - Response = KeyValues["response"]; - RequestType = AuthMethod.PartnerIdAuth; - } - else if (KeyValues.ContainsKey("keyhash") && KeyValues.ContainsKey("nick")) - { - RequestType = AuthMethod.CDkeyAuth; - CdKeyHash = KeyValues["keyhash"]; - NickName = KeyValues["nick"]; - } - else - { - throw new GameStatus.Exception("Unknown authp request method."); - } - } - } -} diff --git a/src/Servers/GameStatus/src/Contract/Request/GetPlayerDataRequest.cs b/src/Servers/GameStatus/src/Contract/Request/GetPlayerDataRequest.cs deleted file mode 100755 index 3bbf16a68..000000000 --- a/src/Servers/GameStatus/src/Contract/Request/GetPlayerDataRequest.cs +++ /dev/null @@ -1,82 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; -using UniSpy.Server.GameStatus.Enumerate; - -using System; -using System.Collections.Generic; - -namespace UniSpy.Server.GameStatus.Contract.Request -{ - - public sealed class GetPlayerDataRequest : RequestBase - { - public int ProfileId { get; private set; } - public PersistStorageType StorageType { get; private set; } - public int DataIndex { get; private set; } - public List Keys { get; private set; } - public bool GetAllDataFlag { get; private set; } - public GetPlayerDataRequest(string rawRequest) : base(rawRequest) - { - Keys = new List(); - } - - public override void Parse() - { - base.Parse(); - if (!KeyValues.ContainsKey("lid") && !KeyValues.ContainsKey("id")) - { - throw new GameStatus.Exception("localid is missing."); - } - - if (KeyValues.ContainsKey("pid")) - { - int profileID; - if (!int.TryParse(KeyValues["pid"], out profileID)) - { - throw new GameStatus.Exception("pid format is incorrect."); - } - ProfileId = profileID; - } - - if (KeyValues.ContainsKey("ptype")) - { - PersistStorageType storageType; - if (!Enum.TryParse(KeyValues["ptype"], out storageType)) - { - throw new GameStatus.Exception("ptype format is incorrect."); - } - StorageType = storageType; - } - - - if (KeyValues.ContainsKey("dindex")) - { - int dataIndex; - if (!int.TryParse(KeyValues["dindex"], out dataIndex)) - { - throw new GameStatus.Exception("dindex format is incorrect."); - } - DataIndex = dataIndex; - } - - if (!KeyValues.ContainsKey("keys")) - { - throw new GameStatus.Exception("keys is missing."); - } - - string keys = KeyValues["keys"]; - if (keys == "") - { - GetAllDataFlag = true; - } - else - { - string[] keyArray = keys.Split('\x1'); - foreach (var key in keyArray) - { - Keys.Add(key); - } - GetAllDataFlag = false; - } - } - } -} diff --git a/src/Servers/GameStatus/src/Contract/Request/GetProfileIDRequest.cs b/src/Servers/GameStatus/src/Contract/Request/GetProfileIDRequest.cs deleted file mode 100755 index 0f70d04da..000000000 --- a/src/Servers/GameStatus/src/Contract/Request/GetProfileIDRequest.cs +++ /dev/null @@ -1,40 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; - - -namespace UniSpy.Server.GameStatus.Contract.Request -{ - - public sealed class GetProfileIDRequest : RequestBase - { - public string Nick { get; private set; } - public string KeyHash { get; private set; } - - public GetProfileIDRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - if (!KeyValues.ContainsKey("lid") && !KeyValues.ContainsKey("id")) - { - throw new GameStatus.Exception("localid is missing."); - } - - if (!KeyValues.ContainsKey("nick") || !KeyValues.ContainsKey("keyhash")) - { - throw new GameStatus.Exception("nick or keyhash is missing."); - } - - if (KeyValues.ContainsKey("nick")) - { - Nick = KeyValues["nick"]; - } - - if (KeyValues.ContainsKey("keyhash")) - { - KeyHash = KeyValues["keyhash"]; - } - } - } -} diff --git a/src/Servers/GameStatus/src/Contract/Request/NewGameRequest.cs b/src/Servers/GameStatus/src/Contract/Request/NewGameRequest.cs deleted file mode 100755 index 236be0c54..000000000 --- a/src/Servers/GameStatus/src/Contract/Request/NewGameRequest.cs +++ /dev/null @@ -1,52 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; - - -namespace UniSpy.Server.GameStatus.Contract.Request -{ - // "\newgame\\sesskey\%d\challenge\%d"; - //"\newgame\\connid\%d\sesskey\%d" - // worm3d \newgame\\connid\0\sesskey\87563063\final\ - public sealed class NewGameRequest : RequestBase - { - public bool IsClientLocalStorageAvailable { get; private set; } - public string Challenge { get; private set; } - public int? ConnectionID { get; private set; } - /// - /// new game data storage session key, this is different from authgame session key - /// - public int? SessionKey { get; private set; } - public NewGameRequest(string rawRequest) : base(rawRequest) - { - } - public override void Parse() - { - base.Parse(); - - if (!KeyValues.ContainsKey("sesskey")) - { - throw new GameStatus.Exception("sesskey is missing."); - } - if (!int.TryParse(KeyValues["sesskey"], out var sessKey)) - { - throw new GameStatus.Exception("sesskey is not a valid int."); - } - SessionKey = sessKey; - - if (!KeyValues.ContainsKey("connid")) - { - throw new GameStatus.Exception("connid is missing."); - } - if (!int.TryParse(KeyValues["connid"], out var connectionID)) - { - throw new GameStatus.Exception("connid format is incorrect."); - } - ConnectionID = connectionID; - - if (KeyValues.ContainsKey("challenge")) - { - IsClientLocalStorageAvailable = true; - Challenge = KeyValues["challenge"]; - } - } - } -} diff --git a/src/Servers/GameStatus/src/Contract/Request/SetPlayerDataRequest.cs b/src/Servers/GameStatus/src/Contract/Request/SetPlayerDataRequest.cs deleted file mode 100755 index 530695480..000000000 --- a/src/Servers/GameStatus/src/Contract/Request/SetPlayerDataRequest.cs +++ /dev/null @@ -1,79 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; -using UniSpy.Server.GameStatus.Enumerate; - -using System; - -namespace UniSpy.Server.GameStatus.Contract.Request -{ - /// - /// "\setpd\\pid\4\ptype\4\dindex\4\kv\\key1\value1\key2\value2\key3\value3\lid\2\length\5\data\final\" - /// - - - public sealed class SetPlayerDataRequest : RequestBase - { - public SetPlayerDataRequest(string request) : base(request) - { - } - public int ProfileId { get; private set; } - public PersistStorageType StorageType { get; private set; } - public int DataIndex { get; private set; } - public int Length { get; private set; } - public string Report { get; private set; } - public string Data { get; private set; } - public override void Parse() - { - base.Parse(); - - if (!KeyValues.ContainsKey("pid")) - throw new GameStatus.Exception("pid is missing."); - - if (!KeyValues.ContainsKey("ptype")) - throw new GameStatus.Exception("ptype is missing."); - - if (!KeyValues.ContainsKey("dindex")) - throw new GameStatus.Exception("dindex is missing."); - - if (!KeyValues.ContainsKey("length")) - throw new GameStatus.Exception("length is missing."); - - - int profileID; - if (!int.TryParse(KeyValues["pid"], out profileID)) - { - throw new GameStatus.Exception("pid format is incorrect."); - } - ProfileId = profileID; - - int storageType; - if (!int.TryParse(KeyValues["ptype"], out storageType)) - { - throw new GameStatus.Exception("ptype is missing."); - } - - if (!Enum.IsDefined(typeof(PersistStorageType), storageType)) - { - throw new GameStatus.Exception("storage type is incorrect."); - } - - StorageType = (PersistStorageType)storageType; - - int dindex; - if (!int.TryParse(KeyValues["dindex"], out dindex)) - { - throw new GameStatus.Exception("dindex format is incorrect."); - } - DataIndex = dindex; - - int length; - if (!int.TryParse(KeyValues["length"], out length)) - { - throw new GameStatus.Exception("length format is incorrect."); - } - Length = length; - - Report = KeyValues["report"]; - Data = KeyValues["data"]; - } - } -} diff --git a/src/Servers/GameStatus/src/Contract/Request/UpdateGameRequest.cs b/src/Servers/GameStatus/src/Contract/Request/UpdateGameRequest.cs deleted file mode 100755 index 337d390c7..000000000 --- a/src/Servers/GameStatus/src/Contract/Request/UpdateGameRequest.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.GameStatus.Abstraction.BaseClass; - - -namespace UniSpy.Server.GameStatus.Contract.Request -{ - - public sealed class UpdateGameRequest : RequestBase - { - public int? ConnectionID { get; private set; } - public bool IsDone { get; private set; } - public bool IsClientLocalStorageAvailable { get; private set; } - public string GameData { get; private set; } - public Dictionary GameDataKeyValues { get; private set; } - public int? SessionKey { get; private set; } - public UpdateGameRequest(string rawRequest) : base(rawRequest) - { - } - public override void Parse() - { - base.Parse(); - if (!KeyValues.ContainsKey("gamedata")) - { - throw new GameStatus.Exception("request must contians gamedata"); - } - GameData = KeyValues["gamedata"]; - GameDataKeyValues = ConvertGameDataToKeyValues(GameData); - - if (KeyValues.ContainsKey("dl")) - { - IsClientLocalStorageAvailable = true; - } - - if (!KeyValues.ContainsKey("done")) - { - throw new GameStatus.Exception("done is missing."); - } - - - if (KeyValues["done"] == "1") - { - IsDone = true; - } - else if (KeyValues["done"] == "0") - { - IsDone = false; - } - else - { - throw new GameStatus.Exception("done format is incorrect."); - } - - - if (!int.TryParse(KeyValues["sesskey"], out var sessKey)) - { - throw new GameStatus.Exception("sesskey is not a valid int."); - } - SessionKey = sessKey; - - - if (KeyValues.ContainsKey("connid")) - { - if (!int.TryParse(KeyValues["connid"], out var connID)) - { - throw new GameStatus.Exception("connid is not a valid int."); - } - ConnectionID = connID; - } - } - } -} \ No newline at end of file diff --git a/src/Servers/GameStatus/src/Contract/Response/AuthGameResponse.cs b/src/Servers/GameStatus/src/Contract/Response/AuthGameResponse.cs deleted file mode 100755 index 5d8c08231..000000000 --- a/src/Servers/GameStatus/src/Contract/Response/AuthGameResponse.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; -using UniSpy.Server.GameStatus.Contract.Request; -using UniSpy.Server.GameStatus.Contract.Result; - -namespace UniSpy.Server.GameStatus.Contract.Response -{ - public sealed class AuthGameResponse : ResponseBase - { - private new AuthGameRequest _request => (AuthGameRequest)base._request; - private new AuthGameResult _result => (AuthGameResult)base._result; - public AuthGameResponse(AuthGameRequest request, AuthGameResult result) : base(request, result) - { - } - - public override void Build() - { - SendingBuffer = $@"\sesskey\{_result.SessionKey}\lid\{ _request.LocalId}\final\"; - } - } -} diff --git a/src/Servers/GameStatus/src/Contract/Response/AuthPlayerResponse.cs b/src/Servers/GameStatus/src/Contract/Response/AuthPlayerResponse.cs deleted file mode 100755 index b993089b7..000000000 --- a/src/Servers/GameStatus/src/Contract/Response/AuthPlayerResponse.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; -using UniSpy.Server.GameStatus.Contract.Request; -using UniSpy.Server.GameStatus.Contract.Result; - -namespace UniSpy.Server.GameStatus.Contract.Response -{ - public sealed class AuthPlayerResponse : ResponseBase - { - private new AuthPlayerResult _result => (AuthPlayerResult)base._result; - private new AuthPlayerRequest _request => (AuthPlayerRequest)base._request; - public AuthPlayerResponse(AuthPlayerRequest request, AuthPlayerResult result) : base(request, result) - { - } - - public override void Build() - { - SendingBuffer = $@"\pauthr\{_result.ProfileId}\lid\{ _request.LocalId}\final\"; - } - } -} diff --git a/src/Servers/GameStatus/src/Contract/Response/GetPlayerDataResponse.cs b/src/Servers/GameStatus/src/Contract/Response/GetPlayerDataResponse.cs deleted file mode 100755 index 8f10eb8c2..000000000 --- a/src/Servers/GameStatus/src/Contract/Response/GetPlayerDataResponse.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; -using UniSpy.Server.GameStatus.Contract.Request; -using UniSpy.Server.GameStatus.Contract.Result; - -namespace UniSpy.Server.GameStatus.Contract.Response -{ - public sealed class GetPlayerDataResponse : ResponseBase - { - private new GetPlayerDataResult _result => (GetPlayerDataResult)base._result; - private new GetPlayerDataRequest _request => (GetPlayerDataRequest)base._request; - public GetPlayerDataResponse(GetPlayerDataRequest request, GetPlayerDataResult result) : base(request, result) - { - } - - public override void Build() - { - SendingBuffer = $@"\getpdr\1\pid\{_request.ProfileId}\lid\{_request.LocalId}\mod\1234\length\5\data\mydata\final\"; - } - } -} diff --git a/src/Servers/GameStatus/src/Contract/Response/GetProfileIDResponse.cs b/src/Servers/GameStatus/src/Contract/Response/GetProfileIDResponse.cs deleted file mode 100755 index b195d982d..000000000 --- a/src/Servers/GameStatus/src/Contract/Response/GetProfileIDResponse.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; -using UniSpy.Server.GameStatus.Contract.Request; -using UniSpy.Server.GameStatus.Contract.Result; - -namespace UniSpy.Server.GameStatus.Contract.Response -{ - public sealed class GetProfileIDResponse : ResponseBase - { - private new GetProfileIDResult _result => (GetProfileIDResult)base._result; - private new GetProfileIDRequest _request => (GetProfileIDRequest)base._request; - public GetProfileIDResponse(GetProfileIDRequest request, GetProfileIDResult result) : base(request, result) - { - } - - public override void Build() - { - SendingBuffer = $@"\getpidr\{_result.ProfileId}\lid\{ _request.LocalId}\final\"; - } - } -} diff --git a/src/Servers/GameStatus/src/Contract/Response/SetPlayerDataResponse.cs b/src/Servers/GameStatus/src/Contract/Response/SetPlayerDataResponse.cs deleted file mode 100755 index c11613f63..000000000 --- a/src/Servers/GameStatus/src/Contract/Response/SetPlayerDataResponse.cs +++ /dev/null @@ -1,21 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; -using UniSpy.Server.GameStatus.Contract.Request; -using UniSpy.Server.GameStatus.Contract.Result; -using System; - -namespace UniSpy.Server.GameStatus.Contract.Response -{ - public sealed class SetPlayerDataResponse : ResponseBase - { - private new SetPlayerDataResult _result => (SetPlayerDataResult)base._result; - private new SetPlayerDataRequest _request => (SetPlayerDataRequest)base._request; - public SetPlayerDataResponse(SetPlayerDataRequest request, SetPlayerDataResult result) : base(request, result) - { - } - - public override void Build() - { - throw new NotImplementedException(); - } - } -} diff --git a/src/Servers/GameStatus/src/Contract/Result/AuthGameResult.cs b/src/Servers/GameStatus/src/Contract/Result/AuthGameResult.cs deleted file mode 100755 index 660472f18..000000000 --- a/src/Servers/GameStatus/src/Contract/Result/AuthGameResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; - -namespace UniSpy.Server.GameStatus.Contract.Result -{ - public sealed class AuthGameResult : ResultBase - { - public int SessionKey { get; set; } - public AuthGameResult() - { - } - } -} diff --git a/src/Servers/GameStatus/src/Contract/Result/AuthPlayerResult.cs b/src/Servers/GameStatus/src/Contract/Result/AuthPlayerResult.cs deleted file mode 100755 index 5b368a9fa..000000000 --- a/src/Servers/GameStatus/src/Contract/Result/AuthPlayerResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; - -namespace UniSpy.Server.GameStatus.Contract.Result -{ - public sealed class AuthPlayerResult : ResultBase - { - public int? ProfileId { get; set; } - public AuthPlayerResult() - { - } - } -} diff --git a/src/Servers/GameStatus/src/Contract/Result/GetPlayerDataResult.cs b/src/Servers/GameStatus/src/Contract/Result/GetPlayerDataResult.cs deleted file mode 100755 index ce8252068..000000000 --- a/src/Servers/GameStatus/src/Contract/Result/GetPlayerDataResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; -using System.Collections.Generic; - -namespace UniSpy.Server.GameStatus.Contract.Result -{ - public sealed class GetPlayerDataResult : ResultBase - { - public Dictionary KeyValues { get; set; } - public GetPlayerDataResult() - { - } - } -} diff --git a/src/Servers/GameStatus/src/Contract/Result/GetProfileIDResult.cs b/src/Servers/GameStatus/src/Contract/Result/GetProfileIDResult.cs deleted file mode 100755 index 15c7a88c9..000000000 --- a/src/Servers/GameStatus/src/Contract/Result/GetProfileIDResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; - -namespace UniSpy.Server.GameStatus.Contract.Result -{ - public sealed class GetProfileIDResult : ResultBase - { - public int ProfileId { get; set; } - public GetProfileIDResult() - { - } - } -} diff --git a/src/Servers/GameStatus/src/Contract/Result/SetPlayerDataResult.cs b/src/Servers/GameStatus/src/Contract/Result/SetPlayerDataResult.cs deleted file mode 100755 index 2429cbe9f..000000000 --- a/src/Servers/GameStatus/src/Contract/Result/SetPlayerDataResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; - -namespace UniSpy.Server.GameStatus.Contract.Result -{ - public sealed class SetPlayerDataResult : ResultBase - { - public SetPlayerDataResult() - { - } - } -} diff --git a/src/Servers/GameStatus/src/Dockerfile b/src/Servers/GameStatus/src/Dockerfile deleted file mode 100755 index 9c4fdf87d..000000000 --- a/src/Servers/GameStatus/src/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base -WORKDIR /app -EXPOSE 29920 - -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build -WORKDIR /src -COPY ["src/Servers/GameStatus/src/UniSpy.Server.GameStatus.csproj", "src/Servers/GameStatus/src/"] -COPY ["src/Libraries/Core/src/UniSpy.Server.Core.csproj", "src/Libraries/Core/src/"] -RUN dotnet restore "src/Servers/GameStatus/src/UniSpy.Server.GameStatus.csproj" -COPY . . -WORKDIR "/src/src/Servers/GameStatus/src" -RUN dotnet build "UniSpy.Server.GameStatus.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "UniSpy.Server.GameStatus.csproj" -c Release -o /app/publish - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "UniSpy.Server.GameStatus.dll"] \ No newline at end of file diff --git a/src/Servers/GameStatus/src/Enumerate/AuthMethod.cs b/src/Servers/GameStatus/src/Enumerate/AuthMethod.cs deleted file mode 100755 index 014461cc9..000000000 --- a/src/Servers/GameStatus/src/Enumerate/AuthMethod.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace UniSpy.Server.GameStatus.Enumerate -{ - public enum AuthMethod - { - Unknown, - ProfileIdAuth, - PartnerIdAuth, - CDkeyAuth, - } -} diff --git a/src/Servers/GameStatus/src/Enumerate/GSErrorCode.cs b/src/Servers/GameStatus/src/Enumerate/GSErrorCode.cs deleted file mode 100755 index f69b29b9d..000000000 --- a/src/Servers/GameStatus/src/Enumerate/GSErrorCode.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace UniSpy.Server.GameStatus.Enumerate -{ - /// - /// In gamespy protocol there are no error response message - /// from server to client, which mean we only need to make - /// public error system. - /// - public enum GSErrorCode - { - General, - Parse, - Database, - NoError - } -} diff --git a/src/Servers/GameStatus/src/Enumerate/PersistantStorage.cs b/src/Servers/GameStatus/src/Enumerate/PersistantStorage.cs deleted file mode 100755 index 499c55a5a..000000000 --- a/src/Servers/GameStatus/src/Enumerate/PersistantStorage.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace UniSpy.Server.GameStatus.Enumerate -{ - public enum PersistStorageType : int - { - /// - /// Readable only by the authenticated client it belongs to, can only by set on the server - /// - PrivateRO, - /// - /// Readable only by the authenticated client it belongs to, set by the authenticated client it belongs to - /// - PrivateRW, - /// - /// Readable by any client, can only be set on the server - /// - PublicRO, - /// - /// Readable by any client, set by the authenicated client is belongs to - /// - PublicRW, - } -} diff --git a/src/Servers/GameStatus/src/Exception/Exception.cs b/src/Servers/GameStatus/src/Exception/Exception.cs deleted file mode 100755 index ad126c155..000000000 --- a/src/Servers/GameStatus/src/Exception/Exception.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace UniSpy.Server.GameStatus -{ - public class Exception : UniSpy.Exception - { - public Exception() - { - } - - public Exception(string message) : base(message) - { - } - - public Exception(string message, System.Exception innerException) : base(message, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/GameStatus/src/Handler/CmdHandler/AuthGameHandler.cs b/src/Servers/GameStatus/src/Handler/CmdHandler/AuthGameHandler.cs deleted file mode 100755 index 8aaa836f4..000000000 --- a/src/Servers/GameStatus/src/Handler/CmdHandler/AuthGameHandler.cs +++ /dev/null @@ -1,31 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; -using UniSpy.Server.GameStatus.Contract.Request; -using UniSpy.Server.GameStatus.Contract.Response; -using UniSpy.Server.GameStatus.Contract.Result; -using UniSpy.Server.GameStatus.Application; - -namespace UniSpy.Server.GameStatus.Handler.CmdHandler -{ - - public sealed class AuthGameHandler : CmdHandlerBase - { - private new AuthGameRequest _request => (AuthGameRequest)base._request; - private new AuthGameResult _result { get => (AuthGameResult)base._result; set => base._result = value; } - public AuthGameHandler(Client client, AuthGameRequest request) : base(client, request) - { - _result = new AuthGameResult(); - } - protected override void DataOperation() - { - // for now we do not check this challenge correction - _client.Info.SessionKey = 2020; - _client.Info.GameName = _request.GameName; - _client.Info.IsGameAuthenticated = true; - } - - protected override void ResponseConstruct() - { - _response = new AuthGameResponse(_request, _result); - } - } -} diff --git a/src/Servers/GameStatus/src/Handler/CmdHandler/AuthPlayerHandler.cs b/src/Servers/GameStatus/src/Handler/CmdHandler/AuthPlayerHandler.cs deleted file mode 100755 index f46f11aea..000000000 --- a/src/Servers/GameStatus/src/Handler/CmdHandler/AuthPlayerHandler.cs +++ /dev/null @@ -1,57 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; -using UniSpy.Server.GameStatus.Enumerate; -using UniSpy.Server.GameStatus.Contract.Request; -using UniSpy.Server.GameStatus.Contract.Response; -using UniSpy.Server.GameStatus.Contract.Result; -using UniSpy.Server.GameStatus.Application; - -namespace UniSpy.Server.GameStatus.Handler.CmdHandler -{ - /// - /// Authenticate with partnerid or profileid - /// because we are not gamespy - /// so we do not check response string - /// - public sealed class AuthPlayerHandler : CmdHandlerBase - { - private new AuthPlayerRequest _request => (AuthPlayerRequest)base._request; - private new AuthPlayerResult _result { get => (AuthPlayerResult)base._result; set => base._result = value; } - public AuthPlayerHandler(Client client, AuthPlayerRequest request) : base(client, request) - { - _result = new AuthPlayerResult(); - } - protected override void DataOperation() - { - //search database for user's password - //We do not store user's plaintext password, so we can not check this response - - switch (_request.RequestType) - { - case AuthMethod.PartnerIdAuth: - _client.Info.ProfileId = StorageOperation.Persistance.GetProfileId(_request.AuthToken); - break; - case AuthMethod.ProfileIdAuth: - //even if we do not check response challenge - //we have to check the pid is in our databse - _client.Info.ProfileId = StorageOperation.Persistance.GetProfileId(_request.ProfileId); - break; - case AuthMethod.CDkeyAuth: - _client.Info.ProfileId = StorageOperation.Persistance.GetProfileId(_request.CdKeyHash, _request.NickName); - break; - default: - throw new GameStatus.Exception("Unknown AuthP request type."); - } - if (_client.Info.ProfileId is null) - { - throw new GameStatus.Exception("Can not find profileID"); - } - _result.ProfileId = _client.Info.ProfileId; - _client.Info.IsPlayerAuthenticated = true; - } - - protected override void ResponseConstruct() - { - _response = new AuthPlayerResponse(_request, _result); - } - } -} diff --git a/src/Servers/GameStatus/src/Handler/CmdHandler/GetPlayerDataHandler.cs b/src/Servers/GameStatus/src/Handler/CmdHandler/GetPlayerDataHandler.cs deleted file mode 100755 index 9c27d93e7..000000000 --- a/src/Servers/GameStatus/src/Handler/CmdHandler/GetPlayerDataHandler.cs +++ /dev/null @@ -1,55 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.GameStatus.Abstraction.BaseClass; -using UniSpy.Server.GameStatus.Application; - -using UniSpy.Server.GameStatus.Contract.Request; -using UniSpy.Server.GameStatus.Contract.Response; -using UniSpy.Server.GameStatus.Contract.Result; - -namespace UniSpy.Server.GameStatus.Handler.CmdHandler -{ - - public sealed class GetPlayerDataHandler : CmdHandlerBase - { - //\getpd\\pid\%d\ptype\%d\dindex\%d\keys\%s\lid\%d - private new GetPlayerDataRequest _request => (GetPlayerDataRequest)base._request; - private new GetPlayerDataResult _result { get => (GetPlayerDataResult)base._result; set => base._result = value; } - public GetPlayerDataHandler(Client client, GetPlayerDataRequest request) : base(client, request) - { - _result = new GetPlayerDataResult(); - } - protected override void DataOperation() - { - //search player data in database; - Dictionary keyValues; - - keyValues = StorageOperation.Persistance.GetPlayerData(_request.ProfileId, _request.StorageType, _request.DataIndex); - //TODO figure out what is the function of keys in request - - - if (_request.GetAllDataFlag) - { - _result.KeyValues = keyValues; - } - else - { - foreach (var key in _request.Keys) - { - if (keyValues.ContainsKey(key)) - { - _result.KeyValues.Add(key, keyValues[key]); - } - else - { - throw new GameStatus.Exception($"can not find key:{key} in GetPD request."); - } - } - } - } - - protected override void ResponseConstruct() - { - _response = new GetPlayerDataResponse(_request, _result); - } - } -} diff --git a/src/Servers/GameStatus/src/Handler/CmdHandler/GetProfileIDHandler.cs b/src/Servers/GameStatus/src/Handler/CmdHandler/GetProfileIDHandler.cs deleted file mode 100755 index 3966654d9..000000000 --- a/src/Servers/GameStatus/src/Handler/CmdHandler/GetProfileIDHandler.cs +++ /dev/null @@ -1,31 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; -using UniSpy.Server.GameStatus.Contract.Request; -using UniSpy.Server.GameStatus.Contract.Response; -using UniSpy.Server.GameStatus.Contract.Result; -using UniSpy.Server.GameStatus.Application; - -namespace UniSpy.Server.GameStatus.Handler.CmdHandler -{ - - public sealed class GetProfileIdHandler : CmdHandlerBase - { - //request \getpid\\nick\%s\keyhash\%s\lid\%d - //response \getpidr - private int _profileId; - private new GetProfileIDRequest _request => (GetProfileIDRequest)base._request; - private new GetProfileIDResult _result { get => (GetProfileIDResult)base._result; set => base._result = value; } - public GetProfileIdHandler(Client client, GetProfileIDRequest request) : base(client, request) - { - _result = new GetProfileIDResult(); - } - protected override void DataOperation() - { - _profileId = StorageOperation.Persistance.GetProfileId(_request.KeyHash, _request.Nick); - } - - protected override void ResponseConstruct() - { - _response = new GetProfileIDResponse(_request, _result); - } - } -} diff --git a/src/Servers/GameStatus/src/Handler/CmdHandler/NewGameHandler.cs b/src/Servers/GameStatus/src/Handler/CmdHandler/NewGameHandler.cs deleted file mode 100755 index 23f76387e..000000000 --- a/src/Servers/GameStatus/src/Handler/CmdHandler/NewGameHandler.cs +++ /dev/null @@ -1,29 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; -using UniSpy.Server.GameStatus.Application; -using UniSpy.Server.GameStatus.Contract.Request; - -namespace UniSpy.Server.GameStatus.Handler.CmdHandler -{ - /// - /// Create a game specified information storage space - /// for further game snap shot storage - /// - - public sealed class NewGameHandler : CmdHandlerBase - { - private new NewGameRequest _request => (NewGameRequest)base._request; - public NewGameHandler(Client client, NewGameRequest request) : base(client, request) - { - } - protected override void RequestCheck() - { - base.RequestCheck(); - _client.Info.GameSessionKey = _request.SessionKey; - } - protected override void DataOperation() - { - // store game data to database - StorageOperation.Persistance.CreateNewGameData(); - } - } -} diff --git a/src/Servers/GameStatus/src/Handler/CmdHandler/SetPlayerDataHandler.cs b/src/Servers/GameStatus/src/Handler/CmdHandler/SetPlayerDataHandler.cs deleted file mode 100755 index 4b07d07a7..000000000 --- a/src/Servers/GameStatus/src/Handler/CmdHandler/SetPlayerDataHandler.cs +++ /dev/null @@ -1,24 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; -using UniSpy.Server.GameStatus.Contract.Request; -using UniSpy.Server.GameStatus.Application; - -namespace UniSpy.Server.GameStatus.Handler.CmdHandler -{ - /// - /// Save persist storage data - /// No response for this handler - /// - public sealed class SetPlayerDataHandler : CmdHandlerBase - { - private new SetPlayerDataRequest _request => (SetPlayerDataRequest)base._request; - public SetPlayerDataHandler(Client client, SetPlayerDataRequest request) : base(client, request) - { - } - - protected override void DataOperation() - { - throw new GameStatus.Exception("implement set player data"); - // StorageOperation.Persistance.UpdatePlayerData(_request.ProfileId, _request.StorageType, _request.DataIndex, _request.KeyValues); - } - } -} diff --git a/src/Servers/GameStatus/src/Handler/CmdHandler/UpdateGameHandler.cs b/src/Servers/GameStatus/src/Handler/CmdHandler/UpdateGameHandler.cs deleted file mode 100755 index 31cebe2e3..000000000 --- a/src/Servers/GameStatus/src/Handler/CmdHandler/UpdateGameHandler.cs +++ /dev/null @@ -1,39 +0,0 @@ -using UniSpy.Server.GameStatus.Abstraction.BaseClass; - -using UniSpy.Server.GameStatus.Contract.Request; -using UniSpy.Server.GameStatus.Application; - -namespace UniSpy.Server.GameStatus.Handler.CmdHandler -{ - /// - /// Handles game snapshot and update game data - /// - - public sealed class UpdateGameHandler : CmdHandlerBase - { - //old request "\updgame\\sesskey\%d\done\%d\gamedata\%s" - //new request "\updgame\\sesskey\%d\connid\%d\done\%d\gamedata\%s" - private new UpdateGameRequest _request => (UpdateGameRequest)base._request; - public UpdateGameHandler(Client client, UpdateGameRequest request) : base(client, request) - { - } - protected override void RequestCheck() - { - base.RequestCheck(); - if (_request.SessionKey != _client.Info.GameSessionKey) - { - throw new GameStatus.Exception("Game session key is not match"); - } - } - protected override void DataOperation() - { - if (_request.GameData is null) - { - // the gamedata is null, we do not need to process this request - return; - } - // replace game data with new data - throw new GameStatus.Exception("Implement update game handler."); - } - } -} diff --git a/src/Servers/GameStatus/src/Handler/CmdSwitcher.cs b/src/Servers/GameStatus/src/Handler/CmdSwitcher.cs deleted file mode 100755 index 8faff7887..000000000 --- a/src/Servers/GameStatus/src/Handler/CmdSwitcher.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Linq; -using System.Collections.Generic; -using UniSpy.Server.GameStatus.Contract.Request; -using UniSpy.Server.GameStatus.Handler.CmdHandler; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.GameStatus.Application; - -namespace UniSpy.Server.GameStatus.Handler -{ - public sealed class CmdSwitcher : CmdSwitcherBase - { - private new string _rawRequest => (string)base._rawRequest; - private new Client _client => (Client)base._client; - public CmdSwitcher(Client client, string rawRequest) : base(client, rawRequest) - { - } - protected override void ProcessRawRequest() - { - var rawRequests = _rawRequest.Split(@"\final\", StringSplitOptions.RemoveEmptyEntries); - - foreach (var rawRequest in rawRequests) - { - if (_rawRequest[0] != '\\') - { - // throw new UniSpy.Exception("Invalid request"); - _client.LogError($"Invaid request: {rawRequest}"); - continue; - } - - var name = rawRequest.TrimStart('\\').Split('\\').First(); - _requests.Add(new KeyValuePair(name, rawRequest)); - } - } - - protected override IHandler CreateCmdHandlers(object name, object rawRequest) - { - switch ((string)name) - { - case "auth": - return new AuthGameHandler(_client, new AuthGameRequest((string)rawRequest)); - case "authp": - return new AuthPlayerHandler(_client, new AuthPlayerRequest((string)rawRequest)); - case "newgame": - return new NewGameHandler(_client, new NewGameRequest((string)rawRequest)); - case "getpd": - return new GetPlayerDataHandler(_client, new GetPlayerDataRequest((string)rawRequest)); - case "setpd": - return new SetPlayerDataHandler(_client, new SetPlayerDataRequest((string)rawRequest)); - case "updgame": - return new UpdateGameHandler(_client, new UpdateGameRequest((string)rawRequest)); - default: - return null; - } - } - } -} diff --git a/src/Servers/GameStatus/src/UniSpy.Server.GameStatus.csproj b/src/Servers/GameStatus/src/UniSpy.Server.GameStatus.csproj deleted file mode 100755 index 8b9381e25..000000000 --- a/src/Servers/GameStatus/src/UniSpy.Server.GameStatus.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - Exe - net6.0 - ..\..\..\..\common\Icon\UniSpy_Logo.ico - Linux - ..\..\..\.. - - - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - - - \ No newline at end of file diff --git a/src/Servers/GameStatus/test/GameTest.cs b/src/Servers/GameStatus/test/GameTest.cs deleted file mode 100644 index 5adf71c07..000000000 --- a/src/Servers/GameStatus/test/GameTest.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; -using Xunit; - -namespace UniSpy.Server.GameStatus.Test -{ - public class GameTest - { - [Fact] - public void Worm3d20230331() - { - var client1 = (ITestClient)MockObject.CreateClient(port: 123); - var client2 = (ITestClient)MockObject.CreateClient(port: 124); - // Given - var requests = new List> - { - new KeyValuePair(client1,@"\auth\\gamename\worms3\response\bc3ca727a7825879eb9f13d9fd51bbb9\port\0\id\1\final\"), - new KeyValuePair(client2,@"\auth\\gamename\worms3\response\bc3ca727a7825879eb9f13d9fd51bbb9\port\0\id\1\final\"), - new KeyValuePair(client2,@"\newgame\\connid\0\sesskey\144562\final\"), - new KeyValuePair(client2,@"\newgame\\connid\0\sesskey\144563\final\"), - new KeyValuePair(client1,@"\authp\\pid\1\resp\7b6658e99f448388fbeddc93654e6dd4\lid\2\final\"), - new KeyValuePair(client1,@"\authp\\pid\1\resp\7b6658e99f448388fbeddc93654e6dd4\lid\1\final\"), - new KeyValuePair(client1,@"\setpd\\pid\1\ptype\1\dindex\0\kv\1\lid\2\length\111\data\\report\|title||victories|0|timestamp|66613|league|Team17|winner||crc|-1|player_0|spyguy|ip_0||pid_0|0|auth_0|[00]\final\"), - new KeyValuePair(client2,@"\authp\\pid\16\resp\7b6658e99f448388fbeddc93654e6dd4\lid\2\final\"), - new KeyValuePair(client2,@"\authp\\pid\16\resp\7b6658e99f448388fbeddc93654e6dd4\lid\1\final\"), - new KeyValuePair(client2,@"\setpd\\pid\16\ptype\1\dindex\0\kv\1\lid\2\length\125\data\\report\|title||victories|0|timestamp|66613|league|Team17|winner||crc|-1|player_0|unispy|ip_0|192.168.1.102|pid_0|16|auth_0|[00]\final\") - }; - - foreach (var item in requests) - { - item.Key.TestReceived(UniSpyEncoding.GetBytes(item.Value)); - } - } - [Fact] - public void Gmtest() - { - // Given - var requests = new List() - { - @"\auth\\gamename\crysis2\response\xxxxx\port\30\id\1\final\", - @"\getpd\\pid\0\ptype\0\dindex\1\keys\hello" + "\x1" + @"hi\lid\1\final\", - @"\getpid\\nick\xiaojiuwo\keyhash\00000\lid\1\final\", - @"\newgame\\connid\123\sesskey\123456\lid\1\final\", - @"\newgame\\connid\123\sesskey\2020\lid\1\final\", - @"\newgame\\connid\123\sesskey\123456\challenge\123456789\lid\1\final\", - @"\newgame\\connid\123\sesskey\2020\challenge\123456789\lid\1\final\", - @"\setpd\\pid\123\ptype\0\dindex\1\kv\%d\lid\1\length\5\data\11\lid\1\final\", - @"\updgame\\sesskey\0\done\1\gamedata\hello\lid\1\final\", - @"\updgame\\sesskey\2020\done\1\gamedata\hello\lid\1\final\", - @"\updgame\\sesskey\2020\connid\1\done\1\gamedata\hello\lid\1\final\", - @"\updgame\\sesskey\0\connid\1\done\1\gamedata\hello\lid\1\final\", - }; - foreach (var req in requests) - { - ((ITestClient)MockObject.Client).TestReceived(UniSpyEncoding.GetBytes(req)); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/GameStatus/test/MockObject.cs b/src/Servers/GameStatus/test/MockObject.cs deleted file mode 100644 index f6947c39b..000000000 --- a/src/Servers/GameStatus/test/MockObject.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Net; -using Moq; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.GameStatus.Application; - -namespace UniSpy.Server.GameStatus.Test -{ - public static class MockObject - { - public static IClient Client = CreateClient(); - public static Client CreateClient(string ipAddress = "192.168.1.1", int port = 9999) - { - - var managerMock = new Mock(); - var connectionMock = new Mock(); - connectionMock.Setup(s => s.RemoteIPEndPoint).Returns(new IPEndPoint(IPAddress.Parse(ipAddress), port)); - connectionMock.Setup(s => s.Manager).Returns(managerMock.Object); - connectionMock.Setup(s => s.ConnectionType).Returns(NetworkConnectionType.Tcp); - var serverMock = new GameStatus.Application.Server(managerMock.Object); - - return new Client(connectionMock.Object, serverMock); - } - } -} \ No newline at end of file diff --git a/src/Servers/GameStatus/test/RequestTest.cs b/src/Servers/GameStatus/test/RequestTest.cs deleted file mode 100644 index 91e681918..000000000 --- a/src/Servers/GameStatus/test/RequestTest.cs +++ /dev/null @@ -1,135 +0,0 @@ -using UniSpy.Server.GameStatus.Enumerate; -using UniSpy.Server.GameStatus.Contract.Request; -using Xunit; -using UniSpy.Server.Core.Encryption; - -namespace UniSpy.Server.GameStatus.Test -{ - public class RequestTest - { - [Fact] - public void SetPlayerData20230329() - { - // Given - var raw = @"\setpd\\pid\1\ptype\1\dindex\0\kv\1\lid\2\length\111\data\\report\|title||victories|0|timestamp|37155|league|Team17|winner||crc|-1|player_0|spyguy|ip_0||pid_0|0|auth_0|[00]\final\"; - var req = new SetPlayerDataRequest(raw); - req.Parse(); - // When - - // Then - } - [Fact] - public void GameSpySDKUpdateGameRequest20230329() - { - var raw1 = "\\updgame\\\\sesskey\\20298203\\connid\\0\\done\\0\\gamedata\\\u0001hostname\u0001My l33t Server\u0001mapname\u0001Level 33\u0001gametype\u0001hunter\u0001gamever\u00011.230000\u0001player_0\u0001Bob!\u0001points_0\u00014\u0001deaths_0\u00012\u0001pid_0\u000132432423\u0001auth_0\u00017cca8e60a13781eebc820a50754f57cd\u0001player_1\u0001Joey\u0001points_1\u00012\u0001deaths_1\u00014\u0001pid_1\u0001643423\u0001auth_1\u000119ea14d9d92a7fcc635cf5716944d9bc\\final\\"; - var raw2 = "\\updgame\\\\sesskey\\20298203\\connid\\0\\done\\1\\gamedata\\\u0001hostname\u0001My l33t Server\u0001mapname\u0001Level 33\u0001gametype\u0001hunter\u0001gamever\u00011.230000\u0001player_0\u0001Bob!\u0001points_0\u00016\u0001deaths_0\u00013\u0001pid_0\u000132432423\u0001auth_0\u00017cca8e60a13781eebc820a50754f57cd\u0001player_1\u0001Joey\u0001points_1\u00013\u0001deaths_1\u00016\u0001pid_1\u0001643423\u0001auth_1\u000119ea14d9d92a7fcc635cf5716944d9bc\\final\\"; - var r1 = new UpdateGameRequest(raw1); - r1.Parse(); - var r2 = new UpdateGameRequest(raw2); - r2.Parse(); - } - [Fact] - public void Worm3dAuthPlayer() - { - var request = "2\x0F\x16\x10]%+=veKaB3a(UC`b$\x1CO\x11VZX\x09w\x1Cu\x08L@\x13=X!\x1E{\x0EL\x1DLf[qN \x04G\x130[#N'\x09(IC`b$"; - var buffer = UniSpyEncoding.GetBytes(request); - var buffer2 = XOREncoding.Encode(buffer, XorType.Type1); - var data = UniSpyEncoding.GetString(buffer2); - } - [Fact] - public void AuthPlayerTest() - { - var raw = @"\auth\\gamename\crysis2\response\xxxxx\port\30\id\1\final"; - var request = new AuthGameRequest(raw); - request.Parse(); - Assert.Equal("crysis2", request.GameName); - Assert.Equal(30, request.Port); - Assert.Equal((int)1, request.LocalId); - } - [Fact] - public void AuthGameTest() - { - var raw = @"\auth\\gamename\gmtest\response\xxxx\port\0\id\1"; - var request = new AuthGameRequest(raw); - request.Parse(); - Assert.Equal("gmtest", request.GameName); - Assert.Equal(0, request.Port); - Assert.Equal((int)1, request.LocalId); - } - [Fact] - public void GetPlayerDataTest() - { - var raw = @"\getpd\\pid\0\ptype\0\dindex\1\keys\hello" + "\x1" + @"hi\lid\1"; - var request = new GetPlayerDataRequest(raw); - request.Parse(); - Assert.Equal((int)0, request.ProfileId); - Assert.Equal(PersistStorageType.PrivateRO, request.StorageType); - Assert.Equal((int)1, request.DataIndex); - Assert.Equal(2, request.Keys.Count); - Assert.Equal("hello", request.Keys[0]); - Assert.Equal("hi", request.Keys[1]); - } - [Fact] - public void GetProfileIDTest() - { - var raw = @"\getpid\\nick\xiaojiuwo\keyhash\00000\lid\1"; - var request = new GetProfileIDRequest(raw); - request.Parse(); - Assert.Equal("xiaojiuwo", request.Nick); - Assert.Equal("00000", request.KeyHash); - Assert.Equal((int)1, request.LocalId); - } - [Fact] - public void NewGameTest() - { - var raw = @"\newgame\\connid\123\sesskey\123456\lid\1"; - var request = new NewGameRequest(raw); - request.Parse(); - Assert.Equal((int)123, request.ConnectionID); - Assert.Equal((int)123456, request.SessionKey); - Assert.Equal((int)1, request.LocalId); - - raw = @"\newgame\\connid\123\sesskey\123456\challenge\123456789\lid\1"; - request = new NewGameRequest(raw); - request.Parse(); - Assert.Equal((int)123, request.ConnectionID); - Assert.Equal((int)123456, request.SessionKey); - Assert.Equal("123456789", request.Challenge); - Assert.Equal((int)1, request.LocalId); - } - [Fact] - public void SetPlayerDataTest() - { - var raw = @"\setpd\\pid\123\ptype\0\dindex\1\kv\%d\lid\1\length\5\data\11\lid\1"; - var request = new SetPlayerDataRequest(raw); - Assert.Throws(() => request.Parse()); - raw = @"\setpd\\pid\123\ptype\0\dindex\1\kv\%d\lid\1\report\hello\length\5\data\11\lid\1"; - request = new SetPlayerDataRequest(raw); - request.Parse(); - Assert.Equal((int)123, request.ProfileId); - Assert.Equal(PersistStorageType.PrivateRO, request.StorageType); - Assert.Equal((int)1, request.DataIndex); - Assert.Equal((int)5, request.Length); - } - [Fact] - public void UpdateGameTest() - { - var raw = @"\updgame\\sesskey\0\done\1\gamedata\hello\lid\1"; - var request = new UpdateGameRequest(raw); - request.Parse(); - Assert.True(0 == request.SessionKey); - Assert.True(true == request.IsDone); - Assert.True("hello" == request.GameData); - Assert.Null(request.ConnectionID); - - - raw = @"\updgame\\sesskey\0\connid\1\done\1\gamedata\hello\lid\1"; - request = new UpdateGameRequest(raw); - request.Parse(); - Assert.True(0 == request.SessionKey); - Assert.True(true == request.IsDone); - Assert.True("hello" == request.GameData); - Assert.True(1 == request.ConnectionID); - } - } -} diff --git a/src/Servers/GameStatus/test/UniSpy.Server.GameStatus.Test.csproj b/src/Servers/GameStatus/test/UniSpy.Server.GameStatus.Test.csproj deleted file mode 100644 index fc0c4c027..000000000 --- a/src/Servers/GameStatus/test/UniSpy.Server.GameStatus.Test.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - net6.0 - false - ..\..\..\..\common\Icon\UniSpy_Logo.ico - - - AnyCPU - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/src/Application/Program.cs b/src/Servers/GameTrafficRelay/src/Application/Program.cs deleted file mode 100755 index 8a64790ef..000000000 --- a/src/Servers/GameTrafficRelay/src/Application/Program.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; - -namespace UniSpy.Server.GameTrafficRelay.Application -{ - public class Program - { - static void Main(string[] args) - { - try - { - new ServerLauncher().Start(); - Console.WriteLine("Press < Q > to exit. "); - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - catch (System.Exception e) - { - UniSpy.Exception.HandleException(e); - } - finally - { - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - } - } -} diff --git a/src/Servers/GameTrafficRelay/src/Application/Server.cs b/src/Servers/GameTrafficRelay/src/Application/Server.cs deleted file mode 100644 index 272a62c2a..000000000 --- a/src/Servers/GameTrafficRelay/src/Application/Server.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Net; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay; - -namespace UniSpy.Server.GameTrafficRelay.Application -{ - public class Server : ServerBase - { - private ServerStatusReporter _reporter; - static Server() - { - _name = "GameTrafficRelay"; - } - - public Server() : base(null) - { - _reporter = new ServerStatusReporter(this); - } - - public override void Start() - { - _reporter.Start(); - var builder = WebApplication.CreateBuilder(); - builder.Host.ConfigureLogging((ctx, loggerConfiguration) => - { - loggerConfiguration.AddFilter("Microsoft.Hosting.Lifetime", LogLevel.Warning); - }); - // builder.Host.UseSerilog((ctx, loggerConfiguration) => - // { - // loggerConfiguration = LogWriter.LogConfig; - // loggerConfiguration.Enrich.FromLogContext() - // .Enrich.WithProperty("ApplicationName", typeof(Program).Assembly.GetName().Name) - // .Enrich.WithProperty("Environment", ctx.HostingEnvironment) - // .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) - // .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information); - // // .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Warning); - // #if DEBUG - // // Used to filter out potentially bad data due debugging. - // // Very useful when doing Seq dashboards and want to remove logs under debugging session. - // loggerConfiguration.Enrich.WithProperty("DebuggerAttached", Debugger.IsAttached); - // #endif - // }); - - // Add services to the container. - builder.Services.AddControllers() - .AddNewtonsoftJson(); - // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle - builder.Services.AddEndpointsApiExplorer(); - builder.Services.AddSwaggerGen(); - - var app = builder.Build(); - - // Configure the HTTP request pipeline. - if (app.Environment.IsDevelopment()) - { - app.UseSwagger(); - app.UseSwaggerUI(); - } - - // app.UseSerilogRequestLogging(); - - app.UseHttpsRedirection(); - - app.UseAuthorization(); - - app.MapControllers(); - // asp.net will block here, we do not want to block our codes, so we let them run in background - Task.Run(() => app.Run($"http://{ListeningIPEndPoint}")); - } - - protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => null; - - protected override IClient CreateClient(IConnection connection) => null; - } -} \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/src/Application/ServerLauncher.cs b/src/Servers/GameTrafficRelay/src/Application/ServerLauncher.cs deleted file mode 100644 index e06b68f5d..000000000 --- a/src/Servers/GameTrafficRelay/src/Application/ServerLauncher.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Collections.Generic; -using System.Net; -using UniSpy.Server.Core.Abstraction.BaseClass.Factory; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.GameTrafficRelay.Application -{ - public sealed class ServerLauncher : ServerLauncherBase - { - public static IServer Server => ServerInstances[0]; - protected override List LaunchNetworkService() - { - - var server = new Server(); - if (server.PublicIPEndPoint.Address.Equals(IPAddress.Any) || server.PublicIPEndPoint.Address.Equals(IPAddress.Loopback)) - { - throw new UniSpy.Exception("Game traffic relay server public address can not set to 0.0.0.0 or 127.0.0.1 !"); - } - return new List { server }; - } - } -} \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/src/Controllers/NatNegotiationController.cs b/src/Servers/GameTrafficRelay/src/Controllers/NatNegotiationController.cs deleted file mode 100644 index 37552c900..000000000 --- a/src/Servers/GameTrafficRelay/src/Controllers/NatNegotiationController.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System.Net; -using Microsoft.AspNetCore.Mvc; -using Microsoft.Extensions.Logging; -using System.Threading.Tasks; -using UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay; -using UniSpy.Server.NatNegotiation.Handler.CmdHandler; - -namespace UniSpy.Server.GameTrafficRelay.Controller -{ - [ApiController] - [Route("[controller]")] - public class NatNegotiationController : ControllerBase - { - private readonly ILogger _logger; - public NatNegotiationController(ILogger logger) - { - _logger = logger; - } - [HttpPost] - public Task GetNatNegotiationInfo(NatNegotiationRequest request) - { - NatNegotiationResponse response; - if (request.GameClientIPs is null || request.GameServerIPs is null) - { - response = new NatNegotiationResponse() - { - Port = -1, - Message = "game client/server's address is missing from request" - }; - return Task.FromResult(response); - } - // natneg connecthandler will send 2 request to game traffic relay - ConnectionListener listener; - lock (PingHandler.ConnectionListeners) - { - if (!PingHandler.ConnectionListeners.TryGetValue(request.Cookie, out listener)) - { - var relayEnd = NetworkUtils.GetAvaliableLocalEndPoint(); - listener = new ConnectionListener(relayEnd, - request.Cookie, - request.GameServerIPs, - request.GameClientIPs); - - PingHandler.ConnectionListeners.TryAdd(request.Cookie, listener); - } - } - response = new NatNegotiationResponse() - { - Port = listener.ListeningEndPoint.Port - }; - return Task.FromResult(response); - } - } -} - - diff --git a/src/Servers/GameTrafficRelay/src/Dockerfile b/src/Servers/GameTrafficRelay/src/Dockerfile deleted file mode 100755 index 88e190851..000000000 --- a/src/Servers/GameTrafficRelay/src/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base -WORKDIR /app -EXPOSE 10086/udp - -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build -WORKDIR /src -COPY ["src/Servers/GameTrafficRelay/src/UniSpy.Server.GameTrafficRelay.csproj", "src/Servers/GameTrafficRelay/src/"] -COPY ["src/Libraries/Core/src/UniSpy.Server.Core.csproj", "src/Libraries/Core/src/"] -RUN dotnet restore "src/Servers/GameTrafficRelay/src/UniSpy.Server.GameTrafficRelay.csproj" -COPY . . -WORKDIR "/src/src/Servers/GameTrafficRelay/src" -RUN dotnet build "UniSpy.Server.GameTrafficRelay.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "UniSpy.Server.GameTrafficRelay.csproj" -c Release -o /app/publish - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "UniSpy.Server.GameTrafficRelay.dll"] \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/src/UniSpy.Server.GameTrafficRelay.csproj b/src/Servers/GameTrafficRelay/src/UniSpy.Server.GameTrafficRelay.csproj deleted file mode 100644 index 2aabf0df9..000000000 --- a/src/Servers/GameTrafficRelay/src/UniSpy.Server.GameTrafficRelay.csproj +++ /dev/null @@ -1,28 +0,0 @@ - - - - Exe - net6.0 - ..\..\..\..\common\Icon\UniSpy_Logo.ico - Linux - ..\..\..\.. - - - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/src/appsettings.Development.json b/src/Servers/GameTrafficRelay/src/appsettings.Development.json deleted file mode 100644 index ff66ba6b2..000000000 --- a/src/Servers/GameTrafficRelay/src/appsettings.Development.json +++ /dev/null @@ -1,8 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - } -} diff --git a/src/Servers/GameTrafficRelay/src/appsettings.json b/src/Servers/GameTrafficRelay/src/appsettings.json deleted file mode 100644 index 4d566948d..000000000 --- a/src/Servers/GameTrafficRelay/src/appsettings.json +++ /dev/null @@ -1,9 +0,0 @@ -{ - "Logging": { - "LogLevel": { - "Default": "Information", - "Microsoft.AspNetCore": "Warning" - } - }, - "AllowedHosts": "*" -} diff --git a/src/Servers/GameTrafficRelay/test/GameTest.cs b/src/Servers/GameTrafficRelay/test/GameTest.cs deleted file mode 100644 index cde87d187..000000000 --- a/src/Servers/GameTrafficRelay/test/GameTest.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Threading; -using System.Net; -using Xunit; -using UniSpy.Server.GameTrafficRelay.Controller; -using System.Net.Sockets; -using UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay; - -namespace UniSpy.Server.GameTrafficRelay.Test -{ - public class GameTest - { - public GameTest() - { - MockObject.CreateServer(); - } - - [Fact] - public void GetNagNegotiationInfo() - { - // var mockServer = new Mock(); - // mockServer.Setup(s => s.PublicIPEndPoint).Returns(IPEndPoint.Parse("202.91.0.1:123")); - // ServerLauncher.ServerInstances.Add(mockServer.Object); - - var httpContext = new Microsoft.AspNetCore.Http.DefaultHttpContext(); - - var controllerContext = new Microsoft.AspNetCore.Mvc.ControllerContext() - { - HttpContext = httpContext - }; - controllerContext.HttpContext.Connection.LocalIpAddress = IPAddress.Parse("127.0.0.1"); - var controller = new NatNegotiationController(null) - { - ControllerContext = controllerContext - }; - - var request = new NatNegotiationRequest() - { - Cookie = 123456, - ServerId = System.Guid.NewGuid(), - GameClientIPs = new System.Collections.Generic.List() { "127.0.0.1:1234" }, - GameServerIPs = new System.Collections.Generic.List() { "127.0.0.1:1235" } - }; - - var resp = controller.GetNatNegotiationInfo(request).Result; - var relayEnd = new IPEndPoint(IPAddress.Loopback, resp.Port); - - var sockClient = new Socket(AddressFamily.InterNetwork, - SocketType.Dgram, - ProtocolType.Udp); - sockClient.Bind(IPEndPoint.Parse("127.0.0.1:1234")); - // we only use one client to send message to check if the listener shutdown - var req = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x07, 0x00, 0x00, 0x02, 0x9A, 0xC0, 0xA8, 0x01, 0x67, 0x6C, 0xFD, 0x00, 0x00 }; - sockClient.SendTo(req, relayEnd); - var sockServer = new Socket(AddressFamily.InterNetwork, - SocketType.Dgram, - ProtocolType.Udp); - sockServer.Bind(IPEndPoint.Parse("127.0.0.1:1235")); - sockServer.SendTo(req, relayEnd); - Thread.Sleep(2000); - - sockClient.SendTo(req, relayEnd); - Thread.Sleep(2000); - sockServer.SendTo(req, relayEnd); - // // Thread.Sleep(10000); - // // we check if listener is stoped - // Assert.False(IsPortUsing(resp.IPEndPoint1.Port)); - // Assert.False(IsPortUsing(resp.IPEndPoint2.Port)); - // // because we are not runing server, so the server object is null - // Assert.Null(resp.IPEndPoint1); - // Assert.Null(resp.IPEndPoint2); - } - } -} \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/test/MockObject.cs b/src/Servers/GameTrafficRelay/test/MockObject.cs deleted file mode 100644 index f8262b8fc..000000000 --- a/src/Servers/GameTrafficRelay/test/MockObject.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Net; -using Moq; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.GameTrafficRelay.Test -{ - public static class MockObject - { - public static IServer Server = CreateServer(); - - public static IServer CreateServer(string ipAddress = "192.168.1.1", int port = 9999) - { - var managerMock = new Mock(); - var connectionMock = new Mock(); - connectionMock.Setup(s => s.RemoteIPEndPoint).Returns(new IPEndPoint(IPAddress.Parse(ipAddress), port)); - connectionMock.Setup(s => s.Manager).Returns(managerMock.Object); - connectionMock.Setup(s => s.ConnectionType).Returns(NetworkConnectionType.Tcp); - var serverMock = new GameTrafficRelay.Application.Server(); - return serverMock; - } - } -} \ No newline at end of file diff --git a/src/Servers/GameTrafficRelay/test/UniSpy.Server.GameTrafficRelay.Test.csproj b/src/Servers/GameTrafficRelay/test/UniSpy.Server.GameTrafficRelay.Test.csproj deleted file mode 100644 index 6e8d71534..000000000 --- a/src/Servers/GameTrafficRelay/test/UniSpy.Server.GameTrafficRelay.Test.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - net6.0 - false - ..\..\..\..\common\Icon\UniSpy_Logo.ico - - - AnyCPU - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Abstraction/BaseClass/CmdHandlerBase.cs b/src/Servers/NatNegotiation/src/Abstraction/BaseClass/CmdHandlerBase.cs deleted file mode 100755 index b5856b6c2..000000000 --- a/src/Servers/NatNegotiation/src/Abstraction/BaseClass/CmdHandlerBase.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.NatNegotiation.Application; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.NatNegotiation.Abstraction.BaseClass -{ - /// - /// because we are using self defined error code so we do not need - /// to send it to client, when we detect errorCode != noerror we just log it - /// - public abstract class CmdHandlerBase : UniSpy.Server.Core.Abstraction.BaseClass.CmdHandlerBase - { - protected new Client _client => (Client)base._client; - protected new RequestBase _request => (RequestBase)base._request; - protected new ResultBase _result { get => (ResultBase)base._result; set => base._result = value; } - public CmdHandlerBase(IClient client, IRequest request) : base(client, request) - { - } - } -} diff --git a/src/Servers/NatNegotiation/src/Abstraction/BaseClass/CommonRequestBase.cs b/src/Servers/NatNegotiation/src/Abstraction/BaseClass/CommonRequestBase.cs deleted file mode 100644 index ac0e33d14..000000000 --- a/src/Servers/NatNegotiation/src/Abstraction/BaseClass/CommonRequestBase.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using UniSpy.Server.NatNegotiation.Enumerate; - -namespace UniSpy.Server.NatNegotiation.Abstraction.BaseClass -{ - public abstract class CommonRequestBase : RequestBase - { - public NatClientIndex ClientIndex { get; protected set; } - public bool UseGamePort { get; protected set; } - protected CommonRequestBase(byte[] rawRequest) : base(rawRequest) - { - } - public override void Parse() - { - base.Parse(); - ClientIndex = (NatClientIndex)RawRequest[13]; - UseGamePort = Convert.ToBoolean(RawRequest[14]); - } - } -} diff --git a/src/Servers/NatNegotiation/src/Abstraction/BaseClass/CommonResponseBase.cs b/src/Servers/NatNegotiation/src/Abstraction/BaseClass/CommonResponseBase.cs deleted file mode 100644 index 32f01034c..000000000 --- a/src/Servers/NatNegotiation/src/Abstraction/BaseClass/CommonResponseBase.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace UniSpy.Server.NatNegotiation.Abstraction.BaseClass -{ - public abstract class CommonResponseBase : ResponseBase - { - private new CommonResultBase _result => (CommonResultBase)base._result; - private new CommonRequestBase _request => (CommonRequestBase)base._request; - public CommonResponseBase(UniSpy.Server.Core.Abstraction.BaseClass.RequestBase request, UniSpy.Server.Core.Abstraction.BaseClass.ResultBase result) : base(request, result) - { - } - public override void Build() - { - base.Build(); - List data = new List(); - data.AddRange(SendingBuffer); - data.Add((byte)_request.PortType); - data.Add((byte)_request.ClientIndex); - data.Add(Convert.ToByte(_request.UseGamePort)); - data.AddRange(_result.RemoteIPAddressBytes); - data.AddRange(_result.RemotePortBytes); - SendingBuffer = data.ToArray(); - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Abstraction/BaseClass/CommonResultBase.cs b/src/Servers/NatNegotiation/src/Abstraction/BaseClass/CommonResultBase.cs deleted file mode 100644 index 19b553108..000000000 --- a/src/Servers/NatNegotiation/src/Abstraction/BaseClass/CommonResultBase.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System; -using System.Linq; -using System.Net; - -namespace UniSpy.Server.NatNegotiation.Abstraction.BaseClass -{ - public abstract class CommonResultBase : ResultBase - { - public IPEndPoint RemoteIPEndPoint { get; set; } - public byte[] RemoteIPAddressBytes => RemoteIPEndPoint.Address.GetAddressBytes().ToArray(); - public byte[] RemotePortBytes => BitConverter.GetBytes((ushort)RemoteIPEndPoint.Port).Reverse().ToArray(); - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Abstraction/BaseClass/RequestBase.cs b/src/Servers/NatNegotiation/src/Abstraction/BaseClass/RequestBase.cs deleted file mode 100755 index 151ed3344..000000000 --- a/src/Servers/NatNegotiation/src/Abstraction/BaseClass/RequestBase.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Linq; -using UniSpy.Server.NatNegotiation.Enumerate; - -namespace UniSpy.Server.NatNegotiation.Abstraction.BaseClass -{ - /// - /// NatNeg request base - /// - public abstract class RequestBase : UniSpy.Server.Core.Abstraction.BaseClass.RequestBase - { - public static readonly byte[] MagicData = { 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2 }; - public new byte[] RawRequest => (byte[])base.RawRequest; - public byte Version { get; protected set; } - /// - /// Cookie is a random int, which we can not trace two clients - /// - /// - public uint Cookie { get; protected set; } - public NatPortType PortType { get; protected set; } - public new RequestType CommandName { get => (RequestType)base.CommandName; set => base.CommandName = value; } - public RequestBase(byte[] rawRequest) : base(rawRequest) - { - } - public RequestBase() { } - public override void Parse() - { - if (RawRequest.Length < 12) - { - return; - } - - Version = RawRequest[6]; - CommandName = (RequestType)RawRequest[7]; - Cookie = BitConverter.ToUInt32(RawRequest.Skip(8).Take(4).ToArray()); - PortType = (NatPortType)RawRequest[12]; - } - } -} diff --git a/src/Servers/NatNegotiation/src/Abstraction/BaseClass/ResponseBase.cs b/src/Servers/NatNegotiation/src/Abstraction/BaseClass/ResponseBase.cs deleted file mode 100755 index b785d1b4d..000000000 --- a/src/Servers/NatNegotiation/src/Abstraction/BaseClass/ResponseBase.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System; -using System.Collections.Generic; -// ReSharper disable All - -namespace UniSpy.Server.NatNegotiation.Abstraction.BaseClass -{ - public abstract class ResponseBase : UniSpy.Server.Core.Abstraction.BaseClass.ResponseBase - { - protected new RequestBase _request => (RequestBase)base._request; - protected new ResultBase _result => (ResultBase)base._result; - public new byte[] SendingBuffer - { - get => (byte[])base.SendingBuffer; - protected set => base.SendingBuffer = value; - } - public ResponseBase(UniSpy.Server.Core.Abstraction.BaseClass.RequestBase request, UniSpy.Server.Core.Abstraction.BaseClass.ResultBase result) : base(request, result) - { - } - public override void Build() - { - List data = new List(); - data.AddRange(RequestBase.MagicData); - data.Add((byte)_request.Version); - data.Add((byte)_result.PacketType); - data.AddRange(BitConverter.GetBytes((uint)_request.Cookie)); - SendingBuffer = data.ToArray(); - } - } -} diff --git a/src/Servers/NatNegotiation/src/Abstraction/BaseClass/ResultBase.cs b/src/Servers/NatNegotiation/src/Abstraction/BaseClass/ResultBase.cs deleted file mode 100755 index 4f5b6f75d..000000000 --- a/src/Servers/NatNegotiation/src/Abstraction/BaseClass/ResultBase.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.NatNegotiation.Enumerate; - -namespace UniSpy.Server.NatNegotiation.Abstraction.BaseClass -{ - public class ResultBase : UniSpy.Server.Core.Abstraction.BaseClass.ResultBase - { - public ResponseType? PacketType { get; set; } - public ResultBase() - { - } - } -} diff --git a/src/Servers/NatNegotiation/src/Abstraction/Interface/IStorageOperation.cs b/src/Servers/NatNegotiation/src/Abstraction/Interface/IStorageOperation.cs deleted file mode 100644 index d6deade8b..000000000 --- a/src/Servers/NatNegotiation/src/Abstraction/Interface/IStorageOperation.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Collections.Generic; -using UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay; -using UniSpy.Server.NatNegotiation.Aggregate.Redis; -using UniSpy.Server.NatNegotiation.Aggregate.Redis.Fail; - -namespace UniSpy.Server.NatNegotiation.Abstraction.Interface -{ - public interface IStorageOperation - { - List GetAvaliableRelayServers(); - void UpdateInitInfo(InitPacketCache info); - int CountInitInfo(uint cookie, byte version); - List GetInitInfos(Guid serverId, uint cookie); - void RemoveInitInfo(InitPacketCache info); - void UpdateNatFailInfo(NatFailInfoCache info); - int GetNatFailInfo(NatFailInfoCache info); - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs deleted file mode 100644 index 8d658c76d..000000000 --- a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ConnectionListener.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Extension; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.Core.Network.Udp.Server; -using UniSpy.Server.NatNegotiation.Handler.CmdHandler; - -namespace UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay -{ - public class ConnectionListener : IDisposable - { - private IConnectionManager _manager; - public uint Cookie { get; private set; } - public IPEndPoint ListeningEndPoint { get; private set; } - public bool IsDisposed { get; private set; } - private EasyTimer _timer; - private IUdpConnection _clientConnection; - private IUdpConnection _serverConnection; - private List _gameServerValidIPs; - private List _gameClientValidIPs; - public ConnectionListener(IPEndPoint listeningEndPoint, uint cookie, List gameServerIPs, List gameClientIPs) - { - ListeningEndPoint = listeningEndPoint; - _manager = new UdpConnectionManager(listeningEndPoint); - _manager.OnInitialization += OnIntialization; - _gameServerValidIPs = gameServerIPs; - _gameClientValidIPs = gameClientIPs; - Cookie = cookie; - _timer = new EasyTimer(TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(10)); - _timer.Elapsed += (s, e) => CheckExpiredClient(); - // after create listener we start it - Start(); - LogWriter.LogDebug($"[{ListeningEndPoint}] gamespy client listener started."); - } - public void Start() - { - _timer.Start(); - _manager.Start(); - } - - private IClient OnIntialization(IConnection connection) - { - lock (_clientConnection) - { - if (_gameServerValidIPs.Contains(connection.RemoteIPEndPoint.ToString()) && _clientConnection is null) - { - _clientConnection = connection as IUdpConnection; - connection.OnReceive += (buffer) => OnReceived((IUdpConnection)connection, (byte[])buffer); - return default; - } - } - lock (_serverConnection) - { - if (_serverConnection is null) - { - if (_gameClientValidIPs.Contains(connection.RemoteIPEndPoint.ToString()) && _serverConnection is null) - { - _serverConnection = connection as IUdpConnection; - connection.OnReceive += (buffer) => OnReceived((IUdpConnection)connection, (byte[])buffer); - return default; - } - } - } - return default; - } - private void CheckExpiredClient() - { - if (_serverConnection is null || _clientConnection is null || _timer.IsExpired) - { - if (!IsDisposed) - { - Dispose(); - PingHandler.ConnectionListeners.TryRemove(this.Cookie, out _); - LogWriter.LogDebug($"[{ListeningEndPoint}] gamespy listener shutdown."); - _timer.Dispose(); - - } - } - } - private bool CheckValidation(byte[] buffer) - { - var magic = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2 }; - if (!buffer.Take(6).SequenceEqual(magic)) - { - return false; - } - var cookie = buffer.Skip(8).Take(4).ToArray(); - - if (Cookie != BitConverter.ToInt32(cookie)) - { - return false; - } - - return true; - } - protected void OnReceived(IUdpConnection connection, byte[] buffer) - { - - if (_clientConnection.Equals(connection)) - { - ForwardMessage(_clientConnection, _serverConnection, buffer); - return; - } - - if (_serverConnection.Equals(connection)) - { - ForwardMessage(_serverConnection, _clientConnection, buffer); - return; - } - } - - public void ForwardMessage(IUdpConnection sender, IUdpConnection receiver, byte[] data) - { - _timer.RefreshLastActiveTime(); - receiver.Send(data); - LogWriter.LogDebug($"[{sender}] => [{receiver}] {StringExtensions.ConvertPrintableBytesToString(data)} [{StringExtensions.ConvertByteToHexString(data)}]"); - } - - public void Dispose() - { - _manager.Dispose(); - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/NatNegotiation.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/NatNegotiation.cs deleted file mode 100644 index e49d7576e..000000000 --- a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/NatNegotiation.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Collections.Generic; -using System; - -namespace UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay -{ - public record NatNegotiationRequest - { - public uint Cookie { get; set; } - public Guid ServerId { get; set; } - /// - /// Gameserver public ip endpoint, used to validate the negotiator's ip - /// - public List GameServerIPs { get; set; } - /// - /// Gameclient public ip endpoint, used to validate the negotiator's ip - /// - public List GameClientIPs { get; set; } - } - public record NatNegotiationResponse - { - public int Port { get; set; } - public string Message { get; set; } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/NetworkUtils.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/NetworkUtils.cs deleted file mode 100644 index af7e98cc8..000000000 --- a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/NetworkUtils.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Net; -using System.Linq; -using System.Net.NetworkInformation; - -namespace UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay -{ - public sealed class NetworkUtils - { - /// - /// Find 1 port is not using by other programs - /// - public static int GetAvailablePort(int startingPort) - { - var properties = IPGlobalProperties.GetIPGlobalProperties(); - - //getting active connections - var tcpConnectionPorts = properties.GetActiveTcpConnections() - .Where(n => n.LocalEndPoint.Port >= startingPort) - .Select(n => n.LocalEndPoint.Port); - - //getting active tcp listners - WCF service listening in tcp - var tcpListenerPorts = properties.GetActiveTcpListeners() - .Where(n => n.Port >= startingPort) - .Select(n => n.Port); - - //getting active udp listeners - var udpListenerPorts = properties.GetActiveUdpListeners() - .Where(n => n.Port >= startingPort) - .Select(n => n.Port); - - var port = Enumerable.Range(startingPort, ushort.MaxValue) - .Where(i => !tcpConnectionPorts.Contains(i)) - .Where(i => !tcpListenerPorts.Contains(i)) - .Where(i => !udpListenerPorts.Contains(i)) - .First(); - - return port; - } - public static IPEndPoint GetAvaliableLocalEndPoint(int startingPort = 1025) - { - var port = GetAvailablePort(startingPort); - return new IPEndPoint(IPAddress.Any, port); - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelayServerInfo.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelayServerInfo.cs deleted file mode 100644 index 22202265d..000000000 --- a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelayServerInfo.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Net; -using Newtonsoft.Json; -using StackExchange.Redis; -using UniSpy.LinqToRedis; -using UniSpy.Server.Core.Extension.Redis; -using UniSpy.Server.Core.Misc; - -namespace UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay -{ - public record RelayServerCache : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject - { - [RedisKey] - public Guid? ServerID { get; init; } - [RedisKey] - [JsonConverter(typeof(IPEndPointConverter))] - public IPEndPoint PublicIPEndPoint { get; init; } - public int ClientCount { get; set; } - - public RelayServerCache() : base(RedisDbNumber.GameTrafficRelay, TimeSpan.FromMinutes(1)) - { - } - public class RedisClient : UniSpy.Server.Core.Abstraction.BaseClass.RedisClient - { - public RedisClient() { } - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelaySwitcher.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelaySwitcher.cs deleted file mode 100644 index 7baa6b3bd..000000000 --- a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/RelaySwitcher.cs +++ /dev/null @@ -1,26 +0,0 @@ -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.NatNegotiation.Application; -using UniSpy.Server.NatNegotiation.Contract.Request; -using UniSpy.Server.NatNegotiation.Enumerate; -using UniSpy.Server.NatNegotiation.Handler.CmdHandler; - -namespace UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay -{ - public class RelaySwitcher : Handler.CmdSwitcher - { - public RelaySwitcher(Client client, byte[] rawRequest) : base(client, rawRequest) - { - } - protected override IHandler CreateCmdHandlers(object name, object rawRequest) - { - var req = (byte[])rawRequest; - switch ((Enumerate.RequestType)name) - { - case RequestType.Ping: - return new PingHandler(_client, new PingRequest(req)); - default: - throw new NatNegotiation.Exception("GameTrafficRelay only process ping request"); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ServerStatusReporter.cs b/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ServerStatusReporter.cs deleted file mode 100644 index abda6ac4a..000000000 --- a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/ServerStatusReporter.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using UniSpy.Server.Core.Extension; - -namespace UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay -{ - public class ServerStatusReporter - { - private EasyTimer _myTimer; - private RelayServerCache.RedisClient _redisClient = new(); - private Core.Abstraction.Interface.IServer _server; - public ServerStatusReporter(Core.Abstraction.Interface.IServer server) - { - _server = server; - _myTimer = new EasyTimer(TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(10)); - _myTimer.Elapsed += (s, e) => UpdateServerInfo(); - UpdateServerInfo(); - } - public void Start() - { - _myTimer.Start(); - } - private void UpdateServerInfo() - { - var info = new RelayServerCache() - { - ServerID = _server.Id, - PublicIPEndPoint = _server.PublicIPEndPoint, - ClientCount = Handler.CmdHandler.PingHandler.ConnectionListeners.Values.Count - }; - _ = _redisClient.SetValueAsync(info); - var data = _redisClient.GetKeyValues(); - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Aggregate/Misc/NatProperty.cs b/src/Servers/NatNegotiation/src/Aggregate/Misc/NatProperty.cs deleted file mode 100644 index 8f7ec37b4..000000000 --- a/src/Servers/NatNegotiation/src/Aggregate/Misc/NatProperty.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.NatNegotiation.Enumerate; - -namespace UniSpy.Server.NatNegotiation.Aggregate.Misc -{ - public record NatProperty - { - public NatType NatType { get; set; } - public NatPortMappingScheme PortMapping { get; set; } - public int PortIncrement { get; set; } - public bool isPortRestricted { get; set; } - public bool isIPRestricted { get; set; } - public NatProperty() - { - NatType = NatType.Unknown; - PortMapping = NatPortMappingScheme.Unrecognized; - isPortRestricted = false; - isIPRestricted = false; - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Aggregate/Misc/NatReportInfo.cs b/src/Servers/NatNegotiation/src/Aggregate/Misc/NatReportInfo.cs deleted file mode 100644 index 8a93fccde..000000000 --- a/src/Servers/NatNegotiation/src/Aggregate/Misc/NatReportInfo.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Net; -using UniSpy.Server.NatNegotiation.Enumerate; - -namespace UniSpy.Server.NatNegotiation.Aggregate.Misc -{ - public class NatReportRecord - { - public Guid ServerId { get; init; } - public IPAddress PublicIPAddress { get; init; } - public IPAddress PrivateIPAddress { get; init; } - public NatPunchStrategy CurrentPunchStrategy { get; set; } - } - public class NatReportInfo - { - public NatReportRecord MyRecord; - public NatReportRecord OthersRecord; - public static string CreateKey(IPAddress myPublicIPAddress, IPAddress myPrivateIPAddress, Guid otherClientServerId, IPAddress otherClientPrivateIP, NatClientIndex clientIndex) - { - return $"{myPublicIPAddress} {myPrivateIPAddress} {otherClientServerId} {otherClientPrivateIP} {clientIndex}"; - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Aggregate/Redis/InitPacketCache.cs b/src/Servers/NatNegotiation/src/Aggregate/Redis/InitPacketCache.cs deleted file mode 100644 index 5f22b3742..000000000 --- a/src/Servers/NatNegotiation/src/Aggregate/Redis/InitPacketCache.cs +++ /dev/null @@ -1,184 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using Newtonsoft.Json; -using UniSpy.LinqToRedis; -using UniSpy.Server.NatNegotiation.Enumerate; -using UniSpy.Server.Core.Misc; -using UniSpy.Server.NatNegotiation.Handler.CmdHandler; -using System.Linq; -using UniSpy.Server.Core.Extension.Redis; - -namespace UniSpy.Server.NatNegotiation.Aggregate.Redis -{ - public record InitPacketCache : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject - { - [RedisKey] - public Guid? ServerID { get; init; } - [RedisKey] - public uint? Cookie { get; init; } - [RedisKey] - public byte? Version { get; init; } - [RedisKey] - public NatPortType? PortType { get; init; } - [RedisKey] - public NatClientIndex? ClientIndex { get; init; } - [RedisKey] - public string GameName { get; init; } - public bool UseGamePort { get; init; } - [JsonConverter(typeof(IPEndPointConverter))] - public IPEndPoint PublicIPEndPoint { get; init; } - /// - /// The nat negotiation private ip and port using as p2p port - /// nn1,nn2,nn3 is using to detect NAT type - /// - [JsonConverter(typeof(IPEndPointConverter))] - public IPEndPoint PrivateIPEndPoint { get; init; } - - public InitPacketCache() : base(RedisDbNumber.NatAddressInfo, TimeSpan.FromMinutes(3)) - { - } - public class RedisClient : UniSpy.Server.Core.Abstraction.BaseClass.RedisClient - { - public RedisClient() - { - } - } - } - public record NatInitInfo - { - public Dictionary AddressInfos { get; private set; } - public uint Cookie => (uint)AddressInfos.Values.Last().Cookie; - public byte Version => (byte)AddressInfos.Values.Last().Version; - public NatClientIndex ClientIndex => (NatClientIndex)AddressInfos.Values.Last().ClientIndex; - /// - /// Indicate whether game use the socket create by natneg sdk to communicate, - /// if game did not use game port, after negotiation the game communication is not established, - /// there is a port created by game that will be use to communicate - /// - public bool UseGamePort => AddressInfos.Values.Last().UseGamePort; - /// - /// Some game will not send GP init packet, we use NN3 as default. - /// - public IPEndPoint PublicIPEndPoint => AddressInfos.Values.Last().PublicIPEndPoint; - - /// - /// The private address will show in NN2 and NN3 packets, in here we use private ip in NN3 as default - /// - public IPEndPoint PrivateIPEndPoint => AddressInfos.Values.Last().PrivateIPEndPoint; - - public NatType NatType - { - get - { - if (Version == 2) - { - return AddressCheckHandler.DetermineNatTypeVersion2(this).NatType; - } - else - { - return AddressCheckHandler.DetermineNatTypeVersion3(this).NatType; - } - } - } - private void ProcessVersion2(List infos) - { - if (!(AddressInfos.ContainsKey(NatPortType.NN1) - && AddressInfos.ContainsKey(NatPortType.NN2))) - { - throw new NatNegotiation.Exception("Incomplete init packets"); - } - - if (AddressInfos[NatPortType.NN1].Cookie != AddressInfos[NatPortType.NN2].Cookie) - { - throw new NatNegotiation.Exception("Broken cookie"); - } - if (AddressInfos[NatPortType.NN1].Version != AddressInfos[NatPortType.NN2].Version) - { - throw new NatNegotiation.Exception("Broken version"); - } - if (AddressInfos[NatPortType.NN1].ClientIndex != AddressInfos[NatPortType.NN2].ClientIndex) - { - throw new NatNegotiation.Exception("Broken client index"); - } - // if (!AddressInfos[NatPortType.NN1].PrivateIPEndPoint.Equals(AddressInfos[NatPortType.NN2].PrivateIPEndPoint)) - // { - // throw new NatNegotiation.Exception("Client is sending wrong initpacket."); - // } - if (AddressInfos.ContainsKey(NatPortType.GP)) - { - if (AddressInfos[NatPortType.GP].Cookie != AddressInfos[NatPortType.NN1].Cookie || - AddressInfos[NatPortType.GP].Version != AddressInfos[NatPortType.NN1].Version || - AddressInfos[NatPortType.GP].ClientIndex != AddressInfos[NatPortType.NN1].ClientIndex || - AddressInfos[NatPortType.GP].UseGamePort != AddressInfos[NatPortType.NN1].UseGamePort) - { - throw new NatNegotiation.Exception("GP packet info is not correct"); - } - } - } - private void ProcessVersion3(List infos) - { - // todo - // some game will not send GP packet to natneg server, currently do not know the reason of it, need more game for analysis. - // this will happen in GameClient - if (!(AddressInfos.ContainsKey(NatPortType.NN1) - && AddressInfos.ContainsKey(NatPortType.NN2) - && AddressInfos.ContainsKey(NatPortType.NN3))) - { - throw new NatNegotiation.Exception("Incomplete init packets"); - } - - if (AddressInfos[NatPortType.NN1].Cookie != AddressInfos[NatPortType.NN2].Cookie - || AddressInfos[NatPortType.NN1].Cookie != AddressInfos[NatPortType.NN3].Cookie) - { - throw new NatNegotiation.Exception("Broken cookie"); - } - if (AddressInfos[NatPortType.NN1].Version != AddressInfos[NatPortType.NN2].Version - || AddressInfos[NatPortType.NN1].Version != AddressInfos[NatPortType.NN3].Version) - { - throw new NatNegotiation.Exception("Broken version"); - } - - if (AddressInfos[NatPortType.NN1].ClientIndex != AddressInfos[NatPortType.NN2].ClientIndex - || AddressInfos[NatPortType.NN1].ClientIndex != AddressInfos[NatPortType.NN3].ClientIndex) - { - throw new NatNegotiation.Exception("Broken client index"); - } - if (AddressInfos[NatPortType.NN1].UseGamePort != AddressInfos[NatPortType.NN2].UseGamePort - || AddressInfos[NatPortType.NN1].UseGamePort != AddressInfos[NatPortType.NN3].UseGamePort) - { - throw new NatNegotiation.Exception("Broken use game port"); - } - if (!AddressInfos[NatPortType.NN2].PrivateIPEndPoint.Equals(AddressInfos[NatPortType.NN3].PrivateIPEndPoint)) - { - throw new NatNegotiation.Exception("Client is sending wrong initpacket."); - } - if (AddressInfos.ContainsKey(NatPortType.GP)) - { - if (AddressInfos[NatPortType.GP].Cookie != AddressInfos[NatPortType.NN1].Cookie || - AddressInfos[NatPortType.GP].Version != AddressInfos[NatPortType.NN1].Version || - AddressInfos[NatPortType.GP].ClientIndex != AddressInfos[NatPortType.NN1].ClientIndex || - AddressInfos[NatPortType.GP].UseGamePort != AddressInfos[NatPortType.NN1].UseGamePort) - { - throw new NatNegotiation.Exception("GP packet info is not correct"); - } - } - } - - public NatInitInfo(List infos) - { - AddressInfos = infos.Select((i) => new { i }).ToDictionary(a => ((NatPortType)a.i.PortType), a => a.i); - switch (this.Version) - { - case 1: - throw new NatNegotiation.Exception("version 1 do not implemented."); - case 2: - ProcessVersion2(infos); - break; - case 3: - ProcessVersion3(infos); - break; - } - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Aggregate/Redis/NatFailInfoCache.cs b/src/Servers/NatNegotiation/src/Aggregate/Redis/NatFailInfoCache.cs deleted file mode 100644 index 723d1c464..000000000 --- a/src/Servers/NatNegotiation/src/Aggregate/Redis/NatFailInfoCache.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using System.Net; -using Newtonsoft.Json; -using UniSpy.LinqToRedis; -using UniSpy.Server.Core.Extension.Redis; -using UniSpy.Server.Core.Misc; -using UniSpy.Server.NatNegotiation.Enumerate; - -namespace UniSpy.Server.NatNegotiation.Aggregate.Redis.Fail -{ - /// - /// The information pair using to switch strategy - /// - /// - public record NatFailInfoCache : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject - { - [RedisKey] - [JsonConverter(typeof(IPAddresConverter))] - public IPAddress PublicIPAddress1 { get; init; } - [RedisKey] - [JsonConverter(typeof(IPAddresConverter))] - public IPAddress PublicIPAddress2 { get; init; } - public NatFailInfoCache() : base(RedisDbNumber.NatFailInfo, TimeSpan.FromDays(1)) { } - public NatFailInfoCache(NatInitInfo info1, NatInitInfo info2) : base(RedisDbNumber.NatFailInfo, TimeSpan.FromDays(1)) - { - // we need to store in sequence to make consistancy and reduce duplications - if (info1.ClientIndex == NatClientIndex.GameClient) - { - PublicIPAddress1 = info1.PublicIPEndPoint.Address; - PublicIPAddress2 = info2.PublicIPEndPoint.Address; - } - else - { - PublicIPAddress1 = info2.PublicIPEndPoint.Address; - PublicIPAddress2 = info1.PublicIPEndPoint.Address; - } - } - public class RedisClient : UniSpy.Server.Core.Abstraction.BaseClass.RedisClient - { - public RedisClient() { } - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Application/Client.cs b/src/Servers/NatNegotiation/src/Application/Client.cs deleted file mode 100644 index c2a2d5643..000000000 --- a/src/Servers/NatNegotiation/src/Application/Client.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System; -using UniSpy.Server.NatNegotiation.Handler; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Extension; - -namespace UniSpy.Server.NatNegotiation.Application -{ - public class Client : ClientBase - { - public new ClientInfo Info { get => (ClientInfo)base.Info; private set => base.Info = value; } - public new IUdpConnection Connection => (IUdpConnection)base.Connection; - public Client(IConnection connection, IServer server) : base(connection, server) - { - Info = new ClientInfo(); - IsLogRaw = true; - } - protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, (byte[])buffer); - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Application/ClientInfo.cs b/src/Servers/NatNegotiation/src/Application/ClientInfo.cs deleted file mode 100644 index 593fae0b9..000000000 --- a/src/Servers/NatNegotiation/src/Application/ClientInfo.cs +++ /dev/null @@ -1,15 +0,0 @@ -using UniSpy.Server.NatNegotiation.Enumerate; -using UniSpy.Server.Core.Abstraction.BaseClass; - -namespace UniSpy.Server.NatNegotiation.Application -{ - public sealed class ClientInfo : ClientInfoBase - { - public uint? Cookie { get; set; } - public NatClientIndex? ClientIndex { get; set; } - public bool IsNeigotiating { get; set; } - public ClientInfo() : base() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Application/Program.cs b/src/Servers/NatNegotiation/src/Application/Program.cs deleted file mode 100755 index edd148cbd..000000000 --- a/src/Servers/NatNegotiation/src/Application/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.NatNegotiation.Application -{ - public class Program - { - static void Main(string[] args) - { - try - { - new ServerLauncher().Start(); - Console.WriteLine("Press < Q > to exit. "); - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - catch (System.Exception e) - { - UniSpy.Exception.HandleException(e); - } - finally - { - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - } - } -} diff --git a/src/Servers/NatNegotiation/src/Application/Server.cs b/src/Servers/NatNegotiation/src/Application/Server.cs deleted file mode 100644 index 09908841d..000000000 --- a/src/Servers/NatNegotiation/src/Application/Server.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Net; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Network.Udp.Server; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.NatNegotiation.Application -{ - public sealed class Server : ServerBase - { - static Server() - { - _name = "NatNegotiation"; - } - - public Server() { } - - public Server(IConnectionManager manager) : base(manager) { } - - protected override IClient CreateClient(IConnection connection) => new Client(connection, this); - - protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new UdpConnectionManager(endPoint); - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Application/ServerLauncher.cs b/src/Servers/NatNegotiation/src/Application/ServerLauncher.cs deleted file mode 100644 index 62a055b2e..000000000 --- a/src/Servers/NatNegotiation/src/Application/ServerLauncher.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.BaseClass.Factory; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.NatNegotiation.Application -{ - public sealed class ServerLauncher : ServerLauncherBase - { - protected override List LaunchNetworkService() => new List { new Server() }; - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Application/StorageOperation.cs b/src/Servers/NatNegotiation/src/Application/StorageOperation.cs deleted file mode 100644 index 470cfb813..000000000 --- a/src/Servers/NatNegotiation/src/Application/StorageOperation.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.NatNegotiation.Abstraction.Interface; -using UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay; -using UniSpy.Server.NatNegotiation.Aggregate.Redis; -using UniSpy.Server.NatNegotiation.Aggregate.Redis.Fail; - -namespace UniSpy.Server.NatNegotiation.Application -{ - internal sealed class StorageOperation : IStorageOperation - { - /// - /// natneg init information redis server. - /// - private InitPacketCache.RedisClient _redisClient = new(); - private RelayServerCache.RedisClient _relayRedisClient = new(); - private NatFailInfoCache.RedisClient _natFailRedisClient = new(); - public static IStorageOperation Persistance = new StorageOperation(); - public List GetAvaliableRelayServers() - { - return _relayRedisClient.Context.ToList(); - } - public int CountInitInfo(uint cookie, byte version) - { - return _redisClient.Context.Count(k => - k.Cookie == cookie - && k.Version == version); - } - - public List GetInitInfos(Guid serverId, uint cookie) - { - return _redisClient.Context.Where(k => - k.ServerID == serverId - && k.Cookie == cookie).ToList(); - } - - public void UpdateInitInfo(Aggregate.Redis.InitPacketCache info) - { - _ = _redisClient.SetValueAsync(info); - } - - public void RemoveInitInfo(Aggregate.Redis.InitPacketCache info) - { - _redisClient.DeleteKeyValue(info); - } - - public void UpdateNatFailInfo(Aggregate.Redis.Fail.NatFailInfoCache info) - { - _ = _natFailRedisClient.SetValueAsync(info); - } - - public int GetNatFailInfo(Aggregate.Redis.Fail.NatFailInfoCache info) - { - return _natFailRedisClient.Context.Where(i => i.PublicIPAddress1.Equals(info.PublicIPAddress1) && i.PublicIPAddress2.Equals(info.PublicIPAddress2)).Count(); - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Contract/Request/AddressCheckRequest.cs b/src/Servers/NatNegotiation/src/Contract/Request/AddressCheckRequest.cs deleted file mode 100755 index 38201f888..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Request/AddressCheckRequest.cs +++ /dev/null @@ -1,13 +0,0 @@ - -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; - -namespace UniSpy.Server.NatNegotiation.Contract.Request -{ - - public sealed class AddressCheckRequest : CommonRequestBase - { - public AddressCheckRequest(byte[] rawRequest) : base(rawRequest) - { - } - } -} diff --git a/src/Servers/NatNegotiation/src/Contract/Request/ConnectAckRequest.cs b/src/Servers/NatNegotiation/src/Contract/Request/ConnectAckRequest.cs deleted file mode 100644 index c5bb9b939..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Request/ConnectAckRequest.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Enumerate; - -namespace UniSpy.Server.NatNegotiation.Contract.Request -{ - - public sealed class ConnectAckRequest : RequestBase - { - public NatClientIndex ClientIndex { get; private set; } - public ConnectAckRequest(byte[] rawRequest) : base(rawRequest) - { - } - public override void Parse() - { - base.Parse(); - ClientIndex = (NatClientIndex)RawRequest[13]; - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Contract/Request/ConnectRequest.cs b/src/Servers/NatNegotiation/src/Contract/Request/ConnectRequest.cs deleted file mode 100755 index d7435fad0..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Request/ConnectRequest.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Enumerate; -namespace UniSpy.Server.NatNegotiation.Contract.Request -{ - - public sealed class ConnectRequest : RequestBase - { - public new byte Version { get => base.Version; init => base.Version = value; } - public new uint Cookie { get => base.Cookie; init => base.Cookie = value; } - public NatClientIndex ClientIndex { get; init; } - public ConnectRequest() { } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Contract/Request/ErtAckRequest.cs b/src/Servers/NatNegotiation/src/Contract/Request/ErtAckRequest.cs deleted file mode 100755 index 68eca6ee4..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Request/ErtAckRequest.cs +++ /dev/null @@ -1,13 +0,0 @@ - -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; - -namespace UniSpy.Server.NatNegotiation.Contract.Request -{ - - public sealed class ErtAckRequest : CommonRequestBase - { - public ErtAckRequest(byte[] rawRequest) : base(rawRequest) - { - } - } -} diff --git a/src/Servers/NatNegotiation/src/Contract/Request/InitRequest.cs b/src/Servers/NatNegotiation/src/Contract/Request/InitRequest.cs deleted file mode 100755 index 203d9ce17..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Request/InitRequest.cs +++ /dev/null @@ -1,42 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.Core.Misc; - -namespace UniSpy.Server.NatNegotiation.Contract.Request -{ - - public sealed class InitRequest : CommonRequestBase - { - public string GameName { get; private set; } - /// - /// GamePort (GP) private ip address and port, using for NAT negotiation - /// - [Newtonsoft.Json.JsonConverter(typeof(IPEndPointConverter))] - public IPEndPoint PrivateIPEndPoint { get; private set; } - public InitRequest(byte[] rawRequest) : base(rawRequest) - { - } - public override void Parse() - { - base.Parse(); - var ipBytes = RawRequest.Skip(15).Take(4).ToArray(); - var portBytes = RawRequest.Skip(19).Take(2).Reverse().ToArray(); - // we found that the local port is not correct in gamespy and wireshark - ushort port; - - port = (ushort)BitConverter.ToUInt16(portBytes); - PrivateIPEndPoint = new IPEndPoint(new IPAddress(ipBytes), port); - if (RawRequest.Length > 21) - { - if (RawRequest[RawRequest.Length - 1] == 0) - { - var gameNameBytes = RawRequest.Skip(21).Take(RawRequest.Length - 22).ToArray(); - GameName = UniSpyEncoding.GetString(gameNameBytes); - } - } - } - } -} diff --git a/src/Servers/NatNegotiation/src/Contract/Request/NatifyRequest.cs b/src/Servers/NatNegotiation/src/Contract/Request/NatifyRequest.cs deleted file mode 100755 index 09980102c..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Request/NatifyRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; - -namespace UniSpy.Server.NatNegotiation.Contract.Request -{ - - public sealed class NatifyRequest : CommonRequestBase - { - public NatifyRequest(byte[] rawRequest) : base(rawRequest) - { - } - } -} diff --git a/src/Servers/NatNegotiation/src/Contract/Request/PingRequest.cs b/src/Servers/NatNegotiation/src/Contract/Request/PingRequest.cs deleted file mode 100644 index 1cc61040c..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Request/PingRequest.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; - -using UniSpy.Server.NatNegotiation.Enumerate; - -namespace UniSpy.Server.NatNegotiation.Contract.Request -{ - /// - /// When client can not start p2p game, it will use our server to redirect his game traffic - /// - - public sealed class PingRequest : CommonRequestBase - { - public IPEndPoint GuessedTargetIPEndPoint { get; set; } - public bool? GotYourData { get; private set; } - public bool? IsFinished { get; private set; } - public PingRequest(byte[] rawRequest) : base(rawRequest) { } - public override void Parse() - { - base.Parse(); - var _ipBytes = RawRequest.Skip(12).Take(4).ToArray(); - var _portBytes = RawRequest.Skip(16).Take(2).ToArray(); - GuessedTargetIPEndPoint = new IPEndPoint(new IPAddress(_ipBytes), BitConverter.ToUInt16(_portBytes)); - GotYourData = Convert.ToBoolean(RawRequest[18]); - IsFinished = Convert.ToBoolean(RawRequest[19]); - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Contract/Request/PreInitRequest.cs b/src/Servers/NatNegotiation/src/Contract/Request/PreInitRequest.cs deleted file mode 100755 index 7bf8495ae..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Request/PreInitRequest.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Linq; -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Enumerate; - -namespace UniSpy.Server.NatNegotiation.Contract.Request -{ - - public sealed class PreInitRequest : RequestBase - { - public PreInitState? State { get; private set; } - public uint? TargetCookie { get; private set; } - public PreInitRequest(byte[] rawRequest) : base(rawRequest) - { - } - public override void Parse() - { - base.Parse(); - State = (PreInitState)RawRequest[12]; - TargetCookie = BitConverter.ToUInt32(RawRequest.Skip(13).Take(4).ToArray()); - } - } -} diff --git a/src/Servers/NatNegotiation/src/Contract/Request/ReportRequest.cs b/src/Servers/NatNegotiation/src/Contract/Request/ReportRequest.cs deleted file mode 100755 index 782fc76d1..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Request/ReportRequest.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Linq; -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Enumerate; -using UniSpy.Server.Core.Encryption; - -namespace UniSpy.Server.NatNegotiation.Contract.Request -{ - - public sealed class ReportRequest : CommonRequestBase - { - public bool? IsNatSuccess { get; private set; } - public new RequestType CommandName { get => (RequestType)base.CommandName; set => base.CommandName = value; } - public string GameName { get; private set; } - public NatType NatType { get; private set; } - public NatPortMappingScheme? MappingScheme { get; private set; } - public ReportRequest(byte[] rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - // base.Parse(); - if (RawRequest.Length < 12) - { - return; - } - Version = RawRequest[6]; - CommandName = (RequestType)RawRequest[7]; - Cookie = BitConverter.ToUInt32(RawRequest.Skip(8).Take(4).ToArray()); - // port type is set 204 by gamespy - PortType = (NatPortType)RawRequest[12]; - - ClientIndex = (NatClientIndex)RawRequest[13]; - if (RawRequest[14] == 0) - { - IsNatSuccess = false; - } - else - { - IsNatSuccess = true; - } - NatType = (NatType)RawRequest[15]; - MappingScheme = (NatPortMappingScheme)RawRequest[17]; - // initpacket is 23 long, so there are 0x00 before gamename, we need to skip it - var endIndex = Array.FindIndex(RawRequest.Skip(23).ToArray(), 1, k => k == 0); - GameName = UniSpyEncoding.GetString(RawRequest.Skip(23).Take(endIndex).ToArray()); - } - } -} diff --git a/src/Servers/NatNegotiation/src/Contract/Response/ConnectResponse.cs b/src/Servers/NatNegotiation/src/Contract/Response/ConnectResponse.cs deleted file mode 100755 index 278776c59..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Response/ConnectResponse.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Contract.Request; -using UniSpy.Server.NatNegotiation.Contract.Result; - -namespace UniSpy.Server.NatNegotiation.Contract.Response -{ - public sealed class ConnectResponse : ResponseBase - { - private new ConnectRequest _request => (ConnectRequest)base._request; - private new ConnectResult _result => (ConnectResult)base._result; - public ConnectResponse(ConnectRequest request, ConnectResult result) : base(request, result) - { - } - public override void Build() - { - base.Build(); - List data = new List(); - data.AddRange(SendingBuffer); - data.AddRange(_result.RemoteIPAddressBytes); - data.AddRange(_result.RemotePortBytes); - data.Add((byte)_result.GotYourData); - data.Add((byte)_result.Finished); - SendingBuffer = data.ToArray(); - } - } -} diff --git a/src/Servers/NatNegotiation/src/Contract/Response/InitResponse.cs b/src/Servers/NatNegotiation/src/Contract/Response/InitResponse.cs deleted file mode 100755 index 3b61ae8fb..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Response/InitResponse.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; - -namespace UniSpy.Server.NatNegotiation.Contract.Response -{ - public sealed class InitResponse : CommonResponseBase - { - public InitResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - } -} diff --git a/src/Servers/NatNegotiation/src/Contract/Response/PingResponse.cs b/src/Servers/NatNegotiation/src/Contract/Response/PingResponse.cs deleted file mode 100644 index 971b48afe..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Response/PingResponse.cs +++ /dev/null @@ -1,18 +0,0 @@ -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Contract.Request; - -namespace UniSpy.Server.NatNegotiation.Contract.Response -{ - public class PingResponse : ResponseBase - { - private new PingRequest _request => (PingRequest)base._request; - public PingResponse(PingRequest request) : base(request, null) - { - } - - public override void Build() - { - SendingBuffer = _request.RawRequest; - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Contract/Response/PreInitResponse.cs b/src/Servers/NatNegotiation/src/Contract/Response/PreInitResponse.cs deleted file mode 100644 index 17ef349dd..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Response/PreInitResponse.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Contract.Request; -using UniSpy.Server.NatNegotiation.Contract.Result; - -namespace UniSpy.Server.NatNegotiation.Contract.Response -{ - public sealed class PreInitResponse : CommonResponseBase - { - private new PreInitRequest _request => (PreInitRequest)base._request; - private new PreInitResult _result => (PreInitResult)base._result; - public PreInitResponse(PreInitRequest request, PreInitResult result) : base(request, result) - { - } - public override void Build() - { - base.Build(); - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Contract/Response/ReportResponse.cs b/src/Servers/NatNegotiation/src/Contract/Response/ReportResponse.cs deleted file mode 100755 index c831a7f7f..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Response/ReportResponse.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Contract.Request; -using UniSpy.Server.NatNegotiation.Contract.Result; - -namespace UniSpy.Server.NatNegotiation.Contract.Response -{ - public sealed class ReportResponse : ResponseBase - { - private new ReportRequest _request => (ReportRequest)base._request; - private new ReportResult _result => (ReportResult)base._result; - public ReportResponse(ReportRequest request, ResultBase result) : base(request, result) - { - } - public override void Build() - { - base.Build(); - var data = new List(); - data.AddRange(SendingBuffer); - // at least 9 bytes more - // the rest bytes did not read by natneg client, but you have to make total bytes length > 21 to pass initpacket check - data.AddRange(new byte[] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); - SendingBuffer = data.ToArray(); - } - } -} diff --git a/src/Servers/NatNegotiation/src/Contract/Result/AddressCheckResult.cs b/src/Servers/NatNegotiation/src/Contract/Result/AddressCheckResult.cs deleted file mode 100755 index 03cb404b1..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Result/AddressCheckResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Enumerate; - -namespace UniSpy.Server.NatNegotiation.Contract.Result -{ - public sealed class AddressCheckResult : CommonResultBase - { - public AddressCheckResult() - { - PacketType = ResponseType.AddressReply; - } - } -} diff --git a/src/Servers/NatNegotiation/src/Contract/Result/ConnectResult.cs b/src/Servers/NatNegotiation/src/Contract/Result/ConnectResult.cs deleted file mode 100755 index f53615392..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Result/ConnectResult.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Enumerate; - -namespace UniSpy.Server.NatNegotiation.Contract.Result -{ - public class ConnectResult : ResultBase - { - public byte? GotYourData { get; private set; } - public ConnectPacketStatus? Finished { get; private set; } - public IPEndPoint RemoteEndPoint { get; set; } - public byte[] RemoteIPAddressBytes => RemoteEndPoint.Address.GetAddressBytes(); - public byte[] RemotePortBytes => BitConverter.GetBytes((ushort)RemoteEndPoint.Port).Reverse().ToArray(); - public byte Version; - public int Cookie; - public ConnectResult() - { - PacketType = ResponseType.Connect; - GotYourData = 1; - Finished = ConnectPacketStatus.NoError; - } - } -} diff --git a/src/Servers/NatNegotiation/src/Contract/Result/ErtAckResult.cs b/src/Servers/NatNegotiation/src/Contract/Result/ErtAckResult.cs deleted file mode 100755 index 3007a85d3..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Result/ErtAckResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Enumerate; - -namespace UniSpy.Server.NatNegotiation.Contract.Result -{ - public sealed class ErtAckResult : CommonResultBase - { - public ErtAckResult() - { - PacketType = ResponseType.ErtAck; - } - } -} diff --git a/src/Servers/NatNegotiation/src/Contract/Result/InitResult.cs b/src/Servers/NatNegotiation/src/Contract/Result/InitResult.cs deleted file mode 100755 index 62716e1e0..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Result/InitResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Enumerate; - -namespace UniSpy.Server.NatNegotiation.Contract.Result -{ - public sealed class InitResult : CommonResultBase - { - public InitResult() - { - PacketType = ResponseType.InitAck; - } - } -} diff --git a/src/Servers/NatNegotiation/src/Contract/Result/NatifyResult.cs b/src/Servers/NatNegotiation/src/Contract/Result/NatifyResult.cs deleted file mode 100755 index bcb57ee7d..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Result/NatifyResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Enumerate; - -namespace UniSpy.Server.NatNegotiation.Contract.Result -{ - public sealed class NatifyResult : CommonResultBase - { - public NatifyResult() - { - PacketType = ResponseType.ErtTest; - } - } -} diff --git a/src/Servers/NatNegotiation/src/Contract/Result/PreInitResult.cs b/src/Servers/NatNegotiation/src/Contract/Result/PreInitResult.cs deleted file mode 100644 index b21afa892..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Result/PreInitResult.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Enumerate; - -namespace UniSpy.Server.NatNegotiation.Contract.Result -{ - public sealed class PreInitResult : ResultBase - { - public int ClientIndex { get; private set; } - public PreInitState State { get; private set; } - public int ClientID { get; private set; } - public PreInitResult() - { - PacketType = ResponseType.PreInitAck; - State = PreInitState.Ready; - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Contract/Result/ReportResult.cs b/src/Servers/NatNegotiation/src/Contract/Result/ReportResult.cs deleted file mode 100755 index 9093d2efb..000000000 --- a/src/Servers/NatNegotiation/src/Contract/Result/ReportResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Enumerate; - -namespace UniSpy.Server.NatNegotiation.Contract.Result -{ - public sealed class ReportResult : ResultBase - { - public ReportResult() - { - PacketType = ResponseType.ReportAck; - } - } -} diff --git a/src/Servers/NatNegotiation/src/Dockerfile b/src/Servers/NatNegotiation/src/Dockerfile deleted file mode 100755 index ac4039932..000000000 --- a/src/Servers/NatNegotiation/src/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base -WORKDIR /app -EXPOSE 27901/udp - -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build -WORKDIR /src -COPY ["src/Servers/NatNegotiation/src/UniSpy.Server.NatNegotiation.csproj", "src/Servers/NatNegotiation/src/"] -COPY ["src/Libraries/Core/src/UniSpy.Server.Core.csproj", "src/Libraries/Core/src/"] -RUN dotnet restore "src/Servers/NatNegotiation/src/UniSpy.Server.NatNegotiation.csproj" -COPY . . -WORKDIR "/src/src/Servers/NatNegotiation/src" -RUN dotnet build "UniSpy.Server.NatNegotiation.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "UniSpy.Server.NatNegotiation.csproj" -c Release -o /app/publish - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "UniSpy.Server.NatNegotiation.dll"] \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Enumerate/ConnectPacketStatus.cs b/src/Servers/NatNegotiation/src/Enumerate/ConnectPacketStatus.cs deleted file mode 100644 index b75d94f5b..000000000 --- a/src/Servers/NatNegotiation/src/Enumerate/ConnectPacketStatus.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UniSpy.Server.NatNegotiation.Enumerate -{ - public enum ConnectPacketStatus : byte - { - NoError, - DeadHeartBeat, - InitPacketTimeOut - } -} diff --git a/src/Servers/NatNegotiation/src/Enumerate/NatMappingType.cs b/src/Servers/NatNegotiation/src/Enumerate/NatMappingType.cs deleted file mode 100644 index 92c0f72b4..000000000 --- a/src/Servers/NatNegotiation/src/Enumerate/NatMappingType.cs +++ /dev/null @@ -1,33 +0,0 @@ -namespace UniSpy.Server.NatNegotiation.Enumerate -{ - public enum NatType : byte - { - NoNat, - FirewallOnly, - /// - /// C发数据到210.21.12.140:8000,NAT会将数据包送到A(192.168.0.4:5000)。因为NAT上已经有了192.168.0.4:5000到210.21.12.140:8000的映射 - /// - FullCone, - /// - /// C无法和A通信,因为A从来没有和C通信过,NAT将拒绝C试图与A连接的动作。但B可以通过210.21.12.140:8000与A的 192.168.0.4:5000通信,且这里B可以使用任何端口与A通信。如:210.15.27.166:2001 -> 210.21.12.140:8000,NAT会送到A的5000端口上 - /// - AddressRestrictedCone, - /// - /// C无法与A通信,因为A从来没有和C通信过。而B也只能用它的210.15.27.166:2000与A的192.168.0.4:5000通信,因为A也从来没有和B的其他端口通信过。该类型NAT是端口受限的。 - /// - PortRestrictedCone, - /// - /// 上面3种类型,统称为Cone NAT,有一个共同点:只要是从同一个内部地址和端口出来的包,NAT都将它转换成同一个外部地址和端口。但是Symmetric有点不同,具体表现在: 只要是从同一个内部地址和端口出来,且到同一个外部目标地址和端口,则NAT也都将它转换成同一个外部地址和端口。但如果从同一个内部地址和端口出来,是 到另一个外部目标地址和端口,则NAT将使用不同的映射,转换成不同的端口(外部地址只有一个,故不变)。而且和Port Restricted Cone一样,只有曾经收到过内部地址发来包的外部地址,才能通过NAT映射后的地址向该内部地址发包。 - /// - Symmetric, - Unknown - } - public enum NatPortMappingScheme : byte - { - Unrecognized, - PrivateAsPublic, - ConsistentPort, - Incremental, - Mixed, - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Enumerate/NatNegResult.cs b/src/Servers/NatNegotiation/src/Enumerate/NatNegResult.cs deleted file mode 100644 index 265206309..000000000 --- a/src/Servers/NatNegotiation/src/Enumerate/NatNegResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace UniSpy.Server.NatNegotiation.Enumerate -{ - public enum NatNegResult - { - Success, - DeadBeatPartner, - InitTimeOut, - PingTimeOut, - UnknownError, - NoResult - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Enumerate/NatPacketType.cs b/src/Servers/NatNegotiation/src/Enumerate/NatPacketType.cs deleted file mode 100644 index d1ee76d96..000000000 --- a/src/Servers/NatNegotiation/src/Enumerate/NatPacketType.cs +++ /dev/null @@ -1,34 +0,0 @@ - -namespace UniSpy.Server.NatNegotiation.Enumerate -{ - public enum RequestType : byte - { - /// - /// Initialize nat negotiation with cookie - /// - Init = 0, - ErtAck = 3, - Connect = 5, - ConnectAck = 6, - Ping = 7, - AddressCheck = 10, - NatifyRequest = 12, - Report = 13, - PreInit = 15 - } - public enum ResponseType : byte - { - InitAck = 1, - ErtTest = 2, - ErtAck = 3, - Connect = 5, - AddressReply = 11, - ReportAck = 14, - PreInitAck = 16 - } - public enum NatClientIndex : byte - { - GameClient = 0, - GameServer = 1 - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Enumerate/NatPortType.cs b/src/Servers/NatNegotiation/src/Enumerate/NatPortType.cs deleted file mode 100644 index 456969b1d..000000000 --- a/src/Servers/NatNegotiation/src/Enumerate/NatPortType.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace UniSpy.Server.NatNegotiation.Enumerate -{ - public enum NatPortType : byte - { - /// - /// Use game port for nat neg - /// - GP, - /// - /// IP and Port both restricted - /// - NN1, - /// - /// IP not restricted - /// - NN2, - /// - /// Port not restriced - /// - NN3 - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Enumerate/NatPunchStrategy.cs b/src/Servers/NatNegotiation/src/Enumerate/NatPunchStrategy.cs deleted file mode 100644 index 4ee85fd36..000000000 --- a/src/Servers/NatNegotiation/src/Enumerate/NatPunchStrategy.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace UniSpy.Server.NatNegotiation.Enumerate -{ - - public enum NatPunchStrategy - { - /// - /// If 2 clients are guessed in same LAN and natneg fail we use public ip to negotiate - /// - UsingPublicIPEndPoint, - /// - /// If 2 clients are probabaly in same LAN, we use private ip to negotiate - /// - UsingPrivateIPEndpoint, - /// - /// If 2 clients are failed with public ip negotation, we use game relay service to redirect the game traffic - /// - UsingGameRelay - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Enumerate/NatStrategyType.cs b/src/Servers/NatNegotiation/src/Enumerate/NatStrategyType.cs deleted file mode 100644 index b3e3b154e..000000000 --- a/src/Servers/NatNegotiation/src/Enumerate/NatStrategyType.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace UniSpy.Server.NatNegotiation.Enumerate -{ - public enum NatStrategyType - { - UsePublicIP, - /// - /// currently we disable it - /// - UsePrivateIP, - UseGameTrafficRelay - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Enumerate/NatifyPacketType.cs b/src/Servers/NatNegotiation/src/Enumerate/NatifyPacketType.cs deleted file mode 100644 index af58b97ca..000000000 --- a/src/Servers/NatNegotiation/src/Enumerate/NatifyPacketType.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace UniSpy.Server.NatNegotiation.Enumerate -{ - public enum NatifyPacketType : byte - { - PacketMap1a, - PacketMap2, - PacketMap3, - PacketMap1b, - NumPackets - } - - public enum PreInitState : byte - { - WaitingForClient = 0, - WaitingForMatchUp = 1, - Ready = 2 - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Exception/NNException.cs b/src/Servers/NatNegotiation/src/Exception/NNException.cs deleted file mode 100755 index 6842a3b3c..000000000 --- a/src/Servers/NatNegotiation/src/Exception/NNException.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace UniSpy.Server.NatNegotiation -{ - public class Exception : UniSpy.Exception - { - public Exception() - { - } - - public Exception(string message) : base(message) - { - } - - public Exception(string message, System.Exception innerException) : base(message, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/AddressCheckHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/AddressCheckHandler.cs deleted file mode 100755 index 86fd0e038..000000000 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/AddressCheckHandler.cs +++ /dev/null @@ -1,112 +0,0 @@ -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Enumerate; -using UniSpy.Server.NatNegotiation.Aggregate.Misc; -using UniSpy.Server.NatNegotiation.Aggregate.Redis; -using UniSpy.Server.NatNegotiation.Contract.Request; -using UniSpy.Server.NatNegotiation.Contract.Response; -using UniSpy.Server.NatNegotiation.Contract.Result; -using UniSpy.Server.NatNegotiation.Application; - -namespace UniSpy.Server.NatNegotiation.Handler.CmdHandler -{ - public sealed class AddressCheckHandler : CmdHandlerBase - { - private new AddressCheckRequest _request => (AddressCheckRequest)base._request; - private new AddressCheckResult _result { get => (AddressCheckResult)base._result; set => base._result = value; } - public AddressCheckHandler(Client client, AddressCheckRequest request) : base(client, request) - { - _result = new AddressCheckResult(); - } - protected override void DataOperation() - { - _result.RemoteIPEndPoint = _client.Connection.RemoteIPEndPoint; - } - protected override void ResponseConstruct() - { - _response = new InitResponse(_request, _result); - } - public static NatProperty DetermineNatTypeVersion2(NatInitInfo initInfo) - { - NatProperty natProperty = new NatProperty(); - if (initInfo.AddressInfos.Count < 3) - { - throw new NatNegotiation.Exception("We need 3 init records to determine the nat type."); - } - var nn1 = initInfo.AddressInfos[NatPortType.NN1]; - var nn2 = initInfo.AddressInfos[NatPortType.NN2]; - - // no nat - if (nn1.PublicIPEndPoint.Address.Equals(nn1.PrivateIPEndPoint.Address) - && nn2.PublicIPEndPoint.Equals(nn2.PrivateIPEndPoint)) - { - natProperty.NatType = NatType.NoNat; - } - // detect cone - else if (nn1.PublicIPEndPoint.Equals(nn2.PublicIPEndPoint)) - { - natProperty.NatType = NatType.FullCone; - } - else if (!nn1.PublicIPEndPoint.Equals(nn2.PublicIPEndPoint)) - { - natProperty.NatType = NatType.Symmetric; - natProperty.PortMapping = NatPortMappingScheme.Unrecognized; - //todo get all interval of the port increment value - var portIncrement1 = nn2.PublicIPEndPoint.Port - nn1.PublicIPEndPoint.Port; - } - else - { - natProperty.NatType = NatType.Unknown; - } - return natProperty; - } - public static NatProperty DetermineNatTypeVersion3(NatInitInfo initInfo) - { - NatProperty natProperty = new NatProperty(); - if (initInfo.AddressInfos.Count < 3) - { - throw new NatNegotiation.Exception("We need 3 init records to determine the nat type."); - } - var nn1 = initInfo.AddressInfos[NatPortType.NN1]; - var nn2 = initInfo.AddressInfos[NatPortType.NN2]; - var nn3 = initInfo.AddressInfos[NatPortType.NN3]; - - // no nat - if (nn1.PublicIPEndPoint.Address.Equals(nn1.PrivateIPEndPoint.Address) - && nn2.PublicIPEndPoint.Equals(nn2.PrivateIPEndPoint) - && nn3.PublicIPEndPoint.Equals(nn3.PrivateIPEndPoint)) - { - natProperty.NatType = NatType.NoNat; - } - // detect cone - else if (nn1.PublicIPEndPoint.Equals(nn2.PublicIPEndPoint) - && nn2.PublicIPEndPoint.Equals(nn3.PublicIPEndPoint)) - { - natProperty.NatType = NatType.FullCone; - } - else if (!nn1.PublicIPEndPoint.Equals(nn2.PublicIPEndPoint) - || !nn2.PublicIPEndPoint.Equals(nn3.PublicIPEndPoint)) - { - natProperty.NatType = NatType.Symmetric; - natProperty.PortMapping = NatPortMappingScheme.Incremental; - //todo get all interval of the port increment value - var portIncrement1 = nn2.PublicIPEndPoint.Port - nn1.PublicIPEndPoint.Port; - var portIncrement2 = nn3.PublicIPEndPoint.Port - nn2.PublicIPEndPoint.Port; - if (portIncrement1 == portIncrement2) - { - natProperty.PortIncrement = portIncrement1; - } - else - { - var increaseInterval = portIncrement2 - portIncrement1; - natProperty.PortIncrement = portIncrement2 + increaseInterval; - } - } - else - { - natProperty.NatType = NatType.Unknown; - - } - return natProperty; - } - } -} diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectAckHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectAckHandler.cs deleted file mode 100644 index af77fc0bb..000000000 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectAckHandler.cs +++ /dev/null @@ -1,23 +0,0 @@ -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Contract.Request; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.NatNegotiation.Application; - -namespace UniSpy.Server.NatNegotiation.Handler.CmdHandler -{ - /// - /// The connect ack handler indicate that the client is already received the connect packet and start nat negotiation - /// - - public class ConnectAckHandler : CmdHandlerBase - { - private new ConnectAckRequest _request => (ConnectAckRequest)base._request; - public ConnectAckHandler(Client client, ConnectAckRequest request) : base(client, request) - { - } - protected override void DataOperation() - { - _client.LogInfo($"client:{_request.ClientIndex} aknowledged connect request."); - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs deleted file mode 100755 index e925569fb..000000000 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ConnectHandler.cs +++ /dev/null @@ -1,166 +0,0 @@ -using System.Linq; -using System.Net; -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Application; -using UniSpy.Server.NatNegotiation.Enumerate; -using UniSpy.Server.NatNegotiation.Aggregate.Redis; -using UniSpy.Server.NatNegotiation.Contract.Request; -using UniSpy.Server.NatNegotiation.Contract.Response; -using UniSpy.Server.NatNegotiation.Contract.Result; -using RestSharp; -using RestSharp.Serializers.NewtonsoftJson; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay; - -namespace UniSpy.Server.NatNegotiation.Handler.CmdHandler -{ - - public sealed class ConnectHandler : CmdHandlerBase - { - /// - /// Indicate the init is already finished - /// |cooie|isFinished| - /// - // public static Dictionary ConnectStatus; - private new ConnectRequest _request => (ConnectRequest)base._request; - private new ConnectResult _result { get => (ConnectResult)base._result; set => base._result = value; } - private NatInitInfo _othersInitInfo; - private NatInitInfo _myInitInfo; - private IPEndPoint _guessedOthersIPEndPoint; - public ConnectHandler(Client client, ConnectRequest request) : base(client, request) - { - _result = new ConnectResult(); - } - protected override void RequestCheck() - { - // detecting nat - var addressInfos = StorageOperation.Persistance.GetInitInfos(_client.Server.Id, _request.Cookie); - // if (addressInfos.Count < InitHandler.InitPacketCount) - // { - // throw new NatNegotiation.Exception($"The number of init info in redis with cookie: {_client.Info.Cookie} is not bigger than 7."); - // } - var otherClientIndex = (NatClientIndex)(1 - (int)_request.ClientIndex); - // we need both info to determine nat type - _othersInitInfo = new NatInitInfo(addressInfos.Where(i => i.ClientIndex == otherClientIndex).OrderBy(i => i.PortType).ToList()); - _myInitInfo = new NatInitInfo(addressInfos.Where(i => i.ClientIndex == _request.ClientIndex).OrderBy(i => i.PortType).ToList()); - } - - // NOTE: If GTR is not used and both clients are on the same LAN, we need to use PrivateIPEndPoint. - protected override void DataOperation() - { - var info = new NatNegotiation.Aggregate.Redis.Fail.NatFailInfoCache(_myInitInfo, _othersInitInfo); - NatStrategyType strategy; - - if (StorageOperation.Persistance.GetNatFailInfo(info) == 0) - { - strategy = DetermineNATStrategy(_myInitInfo, _othersInitInfo); - } - else - { - _client.LogInfo("The public ip is in nat fail record database, we use game traffic relay."); - strategy = NatStrategyType.UseGameTrafficRelay; - } - switch (strategy) - { - case NatStrategyType.UsePublicIP: - UsingPublicAddressToNegotiate(); - break; - // due to multi NAT situation, we have to check if clients are in same public ip address, and both client are no NAT - // if two clients have one public ip like 202.91.34.188, we set the negotiate address for each other such as - // client1 public ip 202.91.34.188:123, private ip: 192.168.1.100 - // client2 public ip 202.91.34.188:124, private ip: 192.168.1.101 - // there are situations as follows. - // 1. clients are in multi NAT with different router - // 2. clients are in one NAT with same router - // there are solutions as follows. - // 1. we can set public ip as negotiation address, but if situation 1 happen, the router will not transfer data for each clients. - // 2. we can set private IP as negotiation address, but if situation 1 happen, two clients can not receive each information, because they are not in same router. - // 3. we can set private ip as negotiation address, if clients are in one NAT, small possibility - // gamespy sdk need 100% success on nat negotiation, therefore if clients have same public ip and have NAT, to make sure 100% success, we use GameTrafficRelay server. - case NatStrategyType.UsePrivateIP: - // temprary use GTR - UsingPrivateAddressToNegotiate(); - break; - case NatStrategyType.UseGameTrafficRelay: - UsingGameRelayServerToNegotiate(); - break; - } - _client.LogInfo($"My NAT type: {_myInitInfo.NatType}, others NAT type: {_othersInitInfo.NatType} Strategy: {strategy}, Guessing IPEndPoint: {_guessedOthersIPEndPoint}"); - } - - protected override void ResponseConstruct() - { - _response = new ConnectResponse( - _request, - new ConnectResult { RemoteEndPoint = _guessedOthersIPEndPoint }); - } - - private void UsingPublicAddressToNegotiate() - { - _guessedOthersIPEndPoint = _othersInitInfo.PublicIPEndPoint; - } - private void UsingPrivateAddressToNegotiate() - { - _guessedOthersIPEndPoint = _othersInitInfo.PrivateIPEndPoint; - } - private void UsingGameRelayServerToNegotiate() - { - var relayServers = StorageOperation.Persistance.GetAvaliableRelayServers(); - if (relayServers.Count == 0) - { - throw new NatNegotiation.Exception("No GameRelayServer found, you must start a GameRelayServer!"); - } - //todo the optimized server will be selected - var relayEndPoint = relayServers.OrderBy(x => x.ClientCount).First().PublicIPEndPoint; - var myIPs = _myInitInfo.AddressInfos.Select(x => x.Value.PublicIPEndPoint.ToString()).ToList(); - var otherIPs = _othersInitInfo.AddressInfos.Select(x => x.Value.PublicIPEndPoint.ToString()).ToList(); - var req = new NatNegotiationRequest() - { - Cookie = _myInitInfo.Cookie, - ServerId = _client.Server.Id, - GameClientIPs = _myInitInfo.ClientIndex == NatClientIndex.GameClient ? myIPs : otherIPs, - GameServerIPs = _myInitInfo.ClientIndex == NatClientIndex.GameServer ? myIPs : otherIPs - }; - var client = new RestClient($"http://{relayEndPoint}/NatNegotiation").UseNewtonsoftJson(); - var request = new RestRequest().AddJsonBody(req); - var resp = client.Post(request); - if (resp.Port == -1) - { - throw new NatNegotiation.Exception(resp.Message); - } - // we create endpoint by using the relay server address and the relay port - _guessedOthersIPEndPoint = new IPEndPoint(relayEndPoint.Address, resp.Port); - } - - public static NatStrategyType DetermineNATStrategy(NatInitInfo info1, NatInitInfo info2) - { - bool IsBothHaveSamePublicIP = info1.PublicIPEndPoint.Address.Equals(info2.PublicIPEndPoint.Address); - // whether first 3 bytes of ip address is same, like 192.168.1.101 and 192.168.1.102 - bool IsBothInSamePrivateIPRange = info1.PrivateIPEndPoint.Address.GetAddressBytes().Take(3) - .SequenceEqual(info2.PrivateIPEndPoint.Address.GetAddressBytes().Take(3)); - if (info1.NatType < NatType.Symmetric && info2.NatType < NatType.Symmetric) - { - if (IsBothHaveSamePublicIP) - { - if (IsBothInSamePrivateIPRange) - { - return NatStrategyType.UsePrivateIP; - - } - else - { - return NatStrategyType.UseGameTrafficRelay; - } - } - else - { - return NatStrategyType.UsePublicIP; - } - } - else - { - return NatStrategyType.UseGameTrafficRelay; - } - } - } -} diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ErtAckHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/ErtAckHandler.cs deleted file mode 100755 index cbc7d2dd5..000000000 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ErtAckHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Contract.Request; -using UniSpy.Server.NatNegotiation.Contract.Response; -using UniSpy.Server.NatNegotiation.Contract.Result; -using UniSpy.Server.NatNegotiation.Application; - -namespace UniSpy.Server.NatNegotiation.Handler.CmdHandler -{ - - public sealed class ErtAckHandler : CmdHandlerBase - { - private new ErtAckRequest _request => (ErtAckRequest)base._request; - private new ErtAckResult _result { get => (ErtAckResult)base._result; set => base._result = value; } - public ErtAckHandler(Client client, ErtAckRequest request) : base(client, request) - { - _result = new ErtAckResult(); - } - protected override void DataOperation() - { - _result.RemoteIPEndPoint = _client.Connection.RemoteIPEndPoint; - } - protected override void ResponseConstruct() - { - _response = new InitResponse(_request, _result); - } - } -} diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/InitHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/InitHandler.cs deleted file mode 100755 index 888f2fe8c..000000000 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/InitHandler.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System.Threading.Tasks; -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Application; -using UniSpy.Server.NatNegotiation.Enumerate; -using UniSpy.Server.NatNegotiation.Aggregate.Redis; -using UniSpy.Server.NatNegotiation.Contract.Request; -using UniSpy.Server.NatNegotiation.Contract.Response; -using UniSpy.Server.NatNegotiation.Contract.Result; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.NatNegotiation.Handler.CmdHandler -{ - - public sealed class InitHandler : CmdHandlerBase - { - public static readonly int InitPacketCount = 6; - private new InitRequest _request => (InitRequest)base._request; - private new InitResult _result { get => (InitResult)base._result; set => base._result = value; } - /// - /// Local NatInitInfo storage, after all init packets are received we send all into redis database - /// - private InitPacketCache _cache; - public InitHandler(Client client, InitRequest request) : base(client, request) - { - _result = new InitResult(); - } - protected override void DataOperation() - { - _cache = new InitPacketCache() - { - ServerID = _client.Server.Id, - Cookie = (uint)_request.Cookie, - UseGamePort = _request.UseGamePort, - ClientIndex = (NatClientIndex)_request.ClientIndex, - Version = _request.Version, - PortType = _request.PortType, - GameName = _request.GameName, - PublicIPEndPoint = _client.Connection.RemoteIPEndPoint, - PrivateIPEndPoint = _request.PrivateIPEndPoint - }; - _client.LogInfo($"Received init request with private ip: [{_cache.PrivateIPEndPoint}], cookie: {_cache.Cookie}, client index: {_cache.ClientIndex}."); - _result.RemoteIPEndPoint = _client.Connection.RemoteIPEndPoint; - } - protected override void ResponseConstruct() - { - _response = new InitResponse(_request, _result); - } - protected override void Response() - { - base.Response(); - - lock (_client.Info) - { - if (_client.Info.ClientIndex is null) - { - _client.Info.ClientIndex = _request.ClientIndex; - } - if (_client.Info.Cookie is null) - { - _client.Info.Cookie = _request.Cookie; - } - } - - // todo make the code do not block and redis do not have thread theaf problem - StorageOperation.Persistance.UpdateInitInfo(_cache); - // init packet nn3 is the last one client send, although receiving nn3 does not mean we received other init packets, but we can use this as a flag to prevent start multiple connect handler - switch (_request.Version) - { - case 1: - throw new NatNegotiation.Exception("The natneg version 1 is not implemented"); - case 2: - if (_request.PortType == NatPortType.NN2 - && _client.Info.IsNeigotiating == false) - { - goto default; - } - break; - case 3: - if (_request.PortType == NatPortType.NN3 - && _client.Info.IsNeigotiating == false) - { - goto default; - } - break; - default: - _client.Info.IsNeigotiating = true; - PrepareForConnectingAsync(); - break; - } - } - - - /// - /// Prepare to send connect response - /// - private async void PrepareForConnectingAsync() - { - _client.LogInfo($"Watting for negotiator's initInfo with cookie:{_request.Cookie}."); - await Task.Delay(2000); - int totalWaitCount = 4; - if (_request.Version == 2) - { - totalWaitCount = 3; - } - - int waitCount = 1; - // we only wait 8 seconds - while (waitCount <= totalWaitCount) - { - var initCount = StorageOperation.Persistance.CountInitInfo(_request.Cookie, _request.Version); - // some client do not send GP init packet, we can only receive 7 packets - if (initCount >= InitPacketCount) - { - _client.LogInfo("2 neigotiators found, start negotiating."); - StartConnecting(); - break; - } - else - { - _client.LogInfo($"No negotiator found with cookie: {_request.Cookie}, retry count: {waitCount}."); - } - waitCount++; - await Task.Delay(2000); - } - // if server can not find the client2 within 8 retry, then we log the error. - if (waitCount > 4) - { - _client.LogWarn($"cookie: {_request.Cookie} have no negotiator found , we clean init information, please connect again."); - } - } - - /// - /// Start connect handler to tell each other's public ip and port - /// - private void StartConnecting() - { - var request = new ConnectRequest - { - Version = _request.Version, - Cookie = _request.Cookie, - ClientIndex = _request.ClientIndex - }; - new ConnectHandler(_client, request).Handle(); - } - } -} diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/NatifyHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/NatifyHandler.cs deleted file mode 100755 index 64cf9f423..000000000 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/NatifyHandler.cs +++ /dev/null @@ -1,27 +0,0 @@ -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Contract.Request; -using UniSpy.Server.NatNegotiation.Contract.Response; -using UniSpy.Server.NatNegotiation.Contract.Result; -using UniSpy.Server.NatNegotiation.Application; - -namespace UniSpy.Server.NatNegotiation.Handler.CmdHandler -{ - - public sealed class NatifyHandler : CmdHandlerBase - { - private new NatifyRequest _request => (NatifyRequest)base._request; - private new NatifyResult _result { get => (NatifyResult)base._result; set => base._result = value; } - public NatifyHandler(Client client, NatifyRequest request) : base(client, request) - { - _result = new NatifyResult(); - } - protected override void DataOperation() - { - _result.RemoteIPEndPoint = _client.Connection.RemoteIPEndPoint; - } - protected override void ResponseConstruct() - { - _response = new InitResponse(_request, _result); - } - } -} diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/PingHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/PingHandler.cs deleted file mode 100644 index 1256f4271..000000000 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/PingHandler.cs +++ /dev/null @@ -1,20 +0,0 @@ -using System.Collections.Concurrent; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.NatNegotiation.Aggregate.GameTrafficRelay; -using UniSpy.Server.NatNegotiation.Contract.Request; - -namespace UniSpy.Server.NatNegotiation.Handler.CmdHandler -{ - public class PingHandler : CmdHandlerBase - { - public static ConcurrentDictionary ConnectionListeners = new ConcurrentDictionary(); - public PingHandler(IClient client, PingRequest request) : base(client, request) - { - } - protected override void RequestCheck() - { - base.RequestCheck(); - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ReportHandler.cs b/src/Servers/NatNegotiation/src/Handler/CmdHandler/ReportHandler.cs deleted file mode 100755 index 9be68c947..000000000 --- a/src/Servers/NatNegotiation/src/Handler/CmdHandler/ReportHandler.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System.Linq; -using UniSpy.Server.NatNegotiation.Abstraction.BaseClass; -using UniSpy.Server.NatNegotiation.Application; -using UniSpy.Server.NatNegotiation.Enumerate; -using UniSpy.Server.NatNegotiation.Aggregate.Redis; -using UniSpy.Server.NatNegotiation.Contract.Request; -using UniSpy.Server.NatNegotiation.Contract.Response; -using UniSpy.Server.NatNegotiation.Contract.Result; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.NatNegotiation.Handler.CmdHandler -{ - /// - /// Get nat neg result report success or fail - /// - - public sealed class ReportHandler : CmdHandlerBase - { - private new ReportRequest _request => (ReportRequest)base._request; - private new ReportResult _result { get => (ReportResult)base._result; set => base._result = value; } - /// - /// The first layer key is the current client's public ip address, the second layer key is the current clients's private ip address, the third layer key is the other client's natneg server id, and the fourth key is the other client's private ip address - /// the hash value of the same IPAddress is the same, so it can be used as the dictionary key. - /// - // internal static Dictionary NatFailRecordInfos; - private NatInitInfo _myInitInfo; - private NatInitInfo _othersInitInfo; - // private string _searchKey; - static ReportHandler() - { - // NatFailRecordInfos = new Dictionary(); - } - public ReportHandler(Client client, ReportRequest request) : base(client, request) - { - _result = new ReportResult(); - } - protected override void RequestCheck() - { - base.RequestCheck(); - var addressInfos = StorageOperation.Persistance.GetInitInfos(_client.Server.Id, (uint)_request.Cookie); - if (addressInfos.Count < InitHandler.InitPacketCount) - { - throw new NatNegotiation.Exception($"The number of init info in redis with cookie: {_request.Cookie} is not bigger than 6."); - } - - NatClientIndex otherClientIndex = (NatClientIndex)(1 - _request.ClientIndex); - // we need both info to determine nat type - _othersInitInfo = new NatInitInfo(addressInfos.Where(i => i.ClientIndex == otherClientIndex).OrderBy(i => i.PortType).ToList()); - _myInitInfo = new NatInitInfo(addressInfos.Where(i => i.ClientIndex == _request.ClientIndex).OrderBy(i => i.PortType).ToList()); - } - protected override void ResponseConstruct() - { - _response = new ReportResponse(_request, _result); - } - - protected override void Response() - { - // we first response, the client will timeout if no response is received in some interval - base.Response(); - _client.LogInfo($"natneg success: {(bool)_request.IsNatSuccess}, version: {_request.Version}, gamename: {_request.GameName}, nat type: {_request.NatType} mapping scheme: {_request.MappingScheme}, cookie: {_request.Cookie}, client index: {_request.ClientIndex} port type: {_request.PortType}"); - // when negotiation failed we store the information - if (!(bool)_request.IsNatSuccess) - { - //todo we save the fail information some where else. - var info = new NatNegotiation.Aggregate.Redis.Fail.NatFailInfoCache(_myInitInfo, _othersInitInfo); - if (StorageOperation.Persistance.GetNatFailInfo(info) == 0) - { - StorageOperation.Persistance.UpdateNatFailInfo(info); - } - } - } - } -} diff --git a/src/Servers/NatNegotiation/src/Handler/CmdSwitcher.cs b/src/Servers/NatNegotiation/src/Handler/CmdSwitcher.cs deleted file mode 100755 index 400f4fd24..000000000 --- a/src/Servers/NatNegotiation/src/Handler/CmdSwitcher.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.NatNegotiation.Enumerate; -using UniSpy.Server.NatNegotiation.Contract.Request; -using UniSpy.Server.NatNegotiation.Handler.CmdHandler; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.NatNegotiation.Application; - -namespace UniSpy.Server.NatNegotiation.Handler -{ - public class CmdSwitcher : CmdSwitcherBase - { - private new byte[] _rawRequest => (byte[])base._rawRequest; - private new Client _client => (Client)base._client; - public CmdSwitcher(Client client, byte[] rawRequest) : base(client, rawRequest) - { - } - protected override void ProcessRawRequest() - { - var name = (RequestType)_rawRequest[7]; - _requests.Add(new KeyValuePair(name, _rawRequest)); - } - - protected override IHandler CreateCmdHandlers(object name, object rawRequest) - { - var req = (byte[])rawRequest; - switch ((RequestType)name) - { - case RequestType.Init: - return new InitHandler(_client, new InitRequest(req)); - case RequestType.AddressCheck: - return new AddressCheckHandler(_client, new AddressCheckRequest(req)); - case RequestType.NatifyRequest: - return new NatifyHandler(_client, new NatifyRequest(req)); - case RequestType.ErtAck: - return new ErtAckHandler(_client, new ErtAckRequest(req)); - case RequestType.Report: - return new ReportHandler(_client, new ReportRequest(req)); - case RequestType.ConnectAck: - return new ConnectAckHandler(_client, new ConnectAckRequest(req)); - default: - return null; - } - } - } -} diff --git a/src/Servers/NatNegotiation/src/UniSpy.Server.NatNegotiation.csproj b/src/Servers/NatNegotiation/src/UniSpy.Server.NatNegotiation.csproj deleted file mode 100755 index 5932d9961..000000000 --- a/src/Servers/NatNegotiation/src/UniSpy.Server.NatNegotiation.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - Exe - net6.0 - ..\..\..\..\common\Icon\UniSpy_Logo.ico - Linux - ..\..\..\.. - - - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - - - - - \ No newline at end of file diff --git a/src/Servers/NatNegotiation/test/GameTest.cs b/src/Servers/NatNegotiation/test/GameTest.cs deleted file mode 100644 index bbcab1c20..000000000 --- a/src/Servers/NatNegotiation/test/GameTest.cs +++ /dev/null @@ -1,383 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using UniSpy.Server.NatNegotiation.Aggregate.Redis; -using UniSpy.Server.Core.Abstraction.Interface; -using Xunit; - -namespace UniSpy.Server.NatNegotiation.Test -{ - public class GameTest - { - [Fact] - public void Anno1701_20230209() - { - new InitPacketCache.RedisClient().Db.Execute("FLUSHALL"); - var clients = new Dictionary() - { - {"client1gp",MockObject.CreateClient("192.168.0.109",1111)}, - {"client1nn",MockObject.CreateClient("192.168.0.109",64900)}, - {"client2nn",MockObject.CreateClient("192.168.0.109",4901)}, - }; - // Given - var gameClientInit = new List>() - { - //gameclient init - new KeyValuePair("client2nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x50,0x7A,0x03,0x00,0x01,0xAC,0x1A,0x50,0x01,0x54,0xC5,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair("client2nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x50,0x7A,0x02,0x00,0x01,0xAC,0x1A,0x50,0x01,0x54,0xC5,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair("client2nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x50,0x7A,0x01,0x00,0x01,0xAC,0x1A,0x50,0x01,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}) - }; - var gameServerInit = new List>() - { - //gameserver init - new KeyValuePair("client1gp",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x50,0x7A,0x03,0x01,0x01,0xC0,0xA8,0x00,0xD5,0x54,0xC5,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair("client1nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x50,0x7A,0x02,0x01,0x01,0xC0,0xA8,0x00,0xD5,0x54,0xC5,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair("client1nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x50,0x7A,0x00,0x01,0x01,0xC0,0xA8,0x00,0xD5,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair("client1nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x50,0x7A,0x01,0x01,0x01,0xC0,0xA8,0x00,0xD5,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - }; - foreach (var request in gameServerInit) - { - Task.Run(() => ((ITestClient)clients[request.Key]).TestReceived(request.Value)); - } - foreach (var request in gameClientInit) - { - Task.Run(() => ((ITestClient)clients[request.Key]).TestReceived(request.Value)); - } - Thread.Sleep(5000); - // Then - } - [Fact] - public void Anno1701_20230127() - { - // Given - var req1 = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x06, 0x00, 0x00, 0x35, 0x7A, 0xD4, 0x00, 0xA7, 0x08, 0xC0, 0xFC, 0xA7, 0x08, 0x00 }; - var req2 = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x06, 0x00, 0x00, 0x35, 0x7A, 0x99, 0x01, 0x78, 0x6D, 0xF0, 0x53, 0xE8, 0x13, 0x5D }; - // When - var client = (ITestClient)MockObject.CreateClient("192.168.0.109", 64900); - client.TestReceived(req1); - client.TestReceived(req2); - // Then - } - [Fact] - /// - /// natneg success - /// - public void GameSpySDK20221116() - { - new InitPacketCache.RedisClient().Db.Execute("FLUSHDB"); - var clients = new Dictionary() - { - {"client1gp",MockObject.CreateClient("192.168.0.109",1111)}, - {"client1nn",MockObject.CreateClient("192.168.0.109",64900)}, - {"client2nn",MockObject.CreateClient("192.168.0.109",4901)}, - }; - var gameServerInit = new List>() - { - //gameserver init - new KeyValuePair("client1gp",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA6,0x38,0xF1,0x2B,0x00,0x01,0x01,0xC0,0xA8,0x00,0x6D,0x00,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - new KeyValuePair("client1nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA6,0x38,0xF1,0x2B,0x01,0x01,0x01,0xC0,0xA8,0x00,0x6D,0x00,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - new KeyValuePair("client1nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA6,0x38,0xF1,0x2B,0x02,0x01,0x01,0xC0,0xA8,0x00,0x6D,0x2B,0x67,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - new KeyValuePair("client1nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA6,0x38,0xF1,0x2B,0x03,0x01,0x01,0xC0,0xA8,0x00,0x6D,0x2B,0x67,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - }; - var gameClientInit = new List>() - { - //gameclient init - new KeyValuePair("client2nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA6,0x38,0xF1,0x2B,0x01,0x00,0x00,0xC0,0xA8,0x00,0x6D,0x00,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - new KeyValuePair("client2nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA6,0x38,0xF1,0x2B,0x02,0x00,0x00,0xC0,0xA8,0x00,0x6D,0xFD,0x84,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - new KeyValuePair("client2nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA6,0x38,0xF1,0x2B,0x03,0x00,0x00,0xC0,0xA8,0x00,0x6D,0xFD,0x84,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}) - }; - var gameServerReport = new KeyValuePair("client1gp", new byte[] { - 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x0D, 0xA6, 0x38, 0xF1, 0x2B, 0xCC, 0x00, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); - var gameClientReport = new KeyValuePair("client2nn", new byte[] { - 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x0D, 0xA6, 0x38, 0xF1, 0x2B, 0xCC, 0x00, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); - foreach (var request in gameServerInit) - { - ((ITestClient)clients[request.Key]).TestReceived(request.Value); - } - foreach (var request in gameClientInit) - { - ((ITestClient)clients[request.Key]).TestReceived(request.Value); - } - Thread.Sleep(5000); - - Task.Run(() => ((ITestClient)clients[gameServerReport.Key]).TestReceived(gameServerReport.Value)); - Task.Run(() => ((ITestClient)clients[gameClientReport.Key]).TestReceived(gameClientReport.Value)); - - Thread.Sleep(5000); - - } - [Fact] - /// - /// natneg fail with retry, test the strategy change - /// - public void NatPunchStrategy20221117() - { - new InitPacketCache.RedisClient().Db.Execute("FLUSHDB"); - var clients1 = new Dictionary() - { - {"client1gp",MockObject.CreateClient("192.168.0.109",1111)}, - {"client1nn",MockObject.CreateClient("192.168.0.109",64900)}, - {"client2nn",MockObject.CreateClient("192.168.0.109",4901)}, - }; - var gameServerInit = new List>() - { - //gameserver init - new KeyValuePair("client1gp",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA6,0x38,0xF1,0x2B,0x00,0x01,0x01,0xC0,0xA8,0x00,0x6D,0x00,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - new KeyValuePair("client1nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA6,0x38,0xF1,0x2B,0x01,0x01,0x01,0xC0,0xA8,0x00,0x6D,0x00,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - new KeyValuePair("client1nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA6,0x38,0xF1,0x2B,0x02,0x01,0x01,0xC0,0xA8,0x00,0x6D,0x2B,0x67,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - new KeyValuePair("client1nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA6,0x38,0xF1,0x2B,0x03,0x01,0x01,0xC0,0xA8,0x00,0x6D,0x2B,0x67,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - }; - var gameClientInit = new List>() - { - //gameclient init - new KeyValuePair("client2nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA6,0x38,0xF1,0x2B,0x01,0x00,0x00,0xC0,0xA8,0x00,0x6D,0x00,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - new KeyValuePair("client2nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA6,0x38,0xF1,0x2B,0x02,0x00,0x00,0xC0,0xA8,0x00,0x6D,0xFD,0x84,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - new KeyValuePair("client2nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA6,0x38,0xF1,0x2B,0x03,0x00,0x00,0xC0,0xA8,0x00,0x6D,0xFD,0x84,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}) - }; - // natneg fail - var gameServerReport = new KeyValuePair("client1gp", new byte[] { - 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x0D, 0xA6, 0x38, 0xF1, 0x2B, 0xCC, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); - var gameClientReport = new KeyValuePair("client2nn", new byte[] { - 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x0D, 0xA6, 0x38, 0xF1, 0x2B, 0xCC, 0x01, 0x00, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); - foreach (var request in gameServerInit) - { - ((ITestClient)clients1[request.Key]).TestReceived(request.Value); - } - foreach (var request in gameClientInit) - { - ((ITestClient)clients1[request.Key]).TestReceived(request.Value); - } - Thread.Sleep(5000); - - Task.Run(() => ((ITestClient)clients1[gameServerReport.Key]).TestReceived(gameServerReport.Value)); - Task.Run(() => ((ITestClient)clients1[gameClientReport.Key]).TestReceived(gameClientReport.Value)); - - Thread.Sleep(5000); - - var clients2 = new Dictionary() - { - {"client1gp",MockObject.CreateClient("192.168.0.109",1111)}, - {"client1nn",MockObject.CreateClient("192.168.0.109",64902)}, - {"client2nn",MockObject.CreateClient("192.168.0.109",4902)}, - }; - // new negotiation - var gameServerInit2 = new List>() - { - //gameserver init - new KeyValuePair("client1gp",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA7,0x38,0xF1,0x2B,0x00,0x01,0x01,0xC0,0xA8,0x00,0x6D,0x00,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - new KeyValuePair("client1nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA7,0x38,0xF1,0x2B,0x01,0x01,0x01,0xC0,0xA8,0x00,0x6D,0x00,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - new KeyValuePair("client1nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA7,0x38,0xF1,0x2B,0x02,0x01,0x01,0xC0,0xA8,0x00,0x6D,0x2B,0x67,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - new KeyValuePair("client1nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA7,0x38,0xF1,0x2B,0x03,0x01,0x01,0xC0,0xA8,0x00,0x6D,0x2B,0x67,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - }; - var gameClientInit2 = new List>() - { - //gameclient init - new KeyValuePair("client2nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA7,0x38,0xF1,0x2B,0x01,0x00,0x00,0xC0,0xA8,0x00,0x6D,0x00,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - new KeyValuePair("client2nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA7,0x38,0xF1,0x2B,0x02,0x00,0x00,0xC0,0xA8,0x00,0x6D,0xFD,0x84,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}), - new KeyValuePair("client2nn",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0xA7,0x38,0xF1,0x2B,0x03,0x00,0x00,0xC0,0xA8,0x00,0x6D,0xFD,0x84,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}) - }; - // natneg success - var gameServerReport2 = new KeyValuePair("client1gp", new byte[] { - 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x0D, 0xA7, 0x38, 0xF1, 0x2B, 0xCC, 0x00, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); - var gameClientReport2 = new KeyValuePair("client2nn", new byte[] { - 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x0D, 0xA7, 0x38, 0xF1, 0x2B, 0xCC, 0x01, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }); - foreach (var request in gameServerInit2) - { - ((ITestClient)clients2[request.Key]).TestReceived(request.Value); - } - foreach (var request in gameClientInit2) - { - ((ITestClient)clients2[request.Key]).TestReceived(request.Value); - } - Thread.Sleep(5000); - - Task.Run(() => ((ITestClient)clients2[gameServerReport2.Key]).TestReceived(gameServerReport2.Value)); - Task.Run(() => ((ITestClient)clients2[gameClientReport2.Key]).TestReceived(gameClientReport2.Value)); - - Thread.Sleep(5000); - - } - [Fact] - public void NatFullConeTest() - { - // clean all stuff in database - new InitPacketCache.RedisClient().Db.Execute("FLUSHDB"); - - var client = MockObject.CreateClient("192.168.1.2", 9999); - var server = MockObject.CreateClient("192.168.1.3", 9999); - var clientInitGP = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x00, 0x00, 0x00, 0x02, 0x9A, 0x00, 0x00, 0x01, 0x7F, 0x00, 0x01, 0x01, 0x00, 0x00, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00 }; - var clientInitNN1 = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x00, 0x00, 0x00, 0x02, 0x9A, 0x01, 0x00, 0x01, 0x7F, 0x00, 0x01, 0x01, 0x00, 0x00, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00 }; - var clientInitNN2 = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x00, 0x00, 0x00, 0x02, 0x9A, 0x02, 0x00, 0x01, 0x7F, 0x00, 0x01, 0x01, 0xBB, 0x37, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00 }; - var clientInitNN3 = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x00, 0x00, 0x00, 0x02, 0x9A, 0x03, 0x00, 0x01, 0x7F, 0x00, 0x01, 0x01, 0xBB, 0x37, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00 }; - - var serverInitGP = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x00, 0x00, 0x00, 0x02, 0x9A, 0x00, 0x01, 0x01, 0x7F, 0x00, 0x01, 0x01, 0x00, 0x00, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00 }; - var serverInitNN1 = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x00, 0x00, 0x00, 0x02, 0x9A, 0x01, 0x01, 0x01, 0x7F, 0x00, 0x01, 0x01, 0x00, 0x00, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00 }; - var serverInitNN2 = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x00, 0x00, 0x00, 0x02, 0x9A, 0x02, 0x01, 0x01, 0x7F, 0x00, 0x01, 0x01, 0xD2, 0xAE, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00 }; - var serverInitNN3 = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x00, 0x00, 0x00, 0x02, 0x9A, 0x03, 0x01, 0x01, 0x7F, 0x00, 0x01, 0x01, 0xD2, 0xAE, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00 }; - var clientRequests = new List { clientInitGP, clientInitNN1, clientInitNN2, clientInitNN3 }; - var serverRequests = new List { serverInitGP, serverInitNN1, serverInitNN2, serverInitNN3 }; - - - foreach (var request in clientRequests) - { - ((ITestClient)client).TestReceived(request); - } - foreach (var request in serverRequests) - { - ((ITestClient)server).TestReceived(request); - } - // because the process is running in background we need to wait it finish, so we can debug - // Thread.Sleep(10000); - // Console.Read(); - } - - [Fact] - public void NatConePortIncrement() - { - // clean all stuff in database - new InitPacketCache.RedisClient().Db.Execute("FLUSHDB"); - - var client = MockObject.CreateClient("192.168.1.2", 53935); - var server = MockObject.CreateClient("192.168.1.3", 53935); - - var clientInitGP = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x00, 0x00, 0x00, 0x02, 0x9A, 0x00, 0x00, 0x01, 0x7F, 0x00, 0x01, 0x01, 0x00, 0x00, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00 }; - var clientInitNN1 = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x00, 0x00, 0x00, 0x02, 0x9A, 0x01, 0x00, 0x01, 0x7F, 0x00, 0x01, 0x01, 0x00, 0x00, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00 }; - var clientInitNN2 = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x00, 0x00, 0x00, 0x02, 0x9A, 0x02, 0x00, 0x01, 0x7F, 0x00, 0x01, 0x01, 0xBB, 0x37, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00 }; - var clientInitNN3 = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x00, 0x00, 0x00, 0x02, 0x9A, 0x03, 0x00, 0x01, 0x7F, 0x00, 0x01, 0x01, 0xBB, 0x37, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00 }; - - var serverInitGP = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x00, 0x00, 0x00, 0x02, 0x9A, 0x00, 0x01, 0x01, 0x7F, 0x00, 0x01, 0x01, 0x00, 0x00, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00 }; - var serverInitNN1 = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x00, 0x00, 0x00, 0x02, 0x9A, 0x01, 0x01, 0x01, 0x7F, 0x00, 0x01, 0x01, 0x00, 0x00, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00 }; - var serverInitNN2 = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x00, 0x00, 0x00, 0x02, 0x9A, 0x02, 0x01, 0x01, 0x7F, 0x00, 0x01, 0x01, 0xD2, 0xAE, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00 }; - var serverInitNN3 = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x00, 0x00, 0x00, 0x02, 0x9A, 0x03, 0x01, 0x01, 0x7F, 0x00, 0x01, 0x01, 0xD2, 0xAE, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00 }; - var clientRequests = new List { clientInitGP, clientInitNN1, clientInitNN2, clientInitNN3 }; - var serverRequests = new List { serverInitGP, serverInitNN1, serverInitNN2, serverInitNN3 }; - - foreach (var request in clientRequests) - { - ((ITestClient)client).TestReceived(request); - } - foreach (var request in serverRequests) - { - ((ITestClient)server).TestReceived(request); - } - // because the process is running in background we need to wait it finish, so we can debug - // Thread.Sleep(10000); - } - - [Fact] - public void Anno1701() - { - var client1GP = MockObject.CreateClient("31.18.120.193", 21701); - var client1NN1 = MockObject.CreateClient("31.18.120.193", 51463); - var client1NN2 = MockObject.CreateClient("31.18.120.193", 51463); - var client1NN3 = MockObject.CreateClient("31.18.120.193", 51463); - - var client2GP = MockObject.CreateClient("79.209.224.29", 21701); - var client2NN1 = MockObject.CreateClient("79.209.224.29", 51463); - var client2NN2 = MockObject.CreateClient("79.209.224.29", 51463); - var client2NN3 = MockObject.CreateClient("79.209.224.29", 51463); - - - var client3GP = MockObject.CreateClient("79.209.224.29", 1024); - var client3NN1 = MockObject.CreateClient("79.209.224.29", 55111); - var client3NN2 = MockObject.CreateClient("79.209.224.29", 55111); - var client3NN3 = MockObject.CreateClient("79.209.224.29", 55111); - - var requests = new List>() - { - new KeyValuePair(client1GP,new byte[] {0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x1D,0x76,0x00,0x00,0x01,0xC0,0xA8,0x00,0xD5,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair(client1NN1,new byte[] {0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x1D,0x76,0x01,0x00,0x01,0xC0,0xA8,0x00,0xD5,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair(client1NN2,new byte[] {0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x1D,0x76,0x02,0x00,0x01,0xC0,0xA8,0x00,0xD5,0x54,0xC5,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair(client1NN3,new byte[] {0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x1D,0x76,0x03,0x00,0x01,0xC0,0xA8,0x00,0xD5,0x54,0xC5,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - - new KeyValuePair(client2GP,new byte[] {0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x1D,0x76,0x00,0x01,0x01,0xC0,0xA8,0x00,0x32,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair(client2NN1,new byte[] {0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x1D,0x76,0x01,0x01,0x01,0xC0,0xA8,0x00,0x32,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair(client2NN2,new byte[] {0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x1D,0x76,0x02,0x01,0x01,0xC0,0xA8,0x00,0x32,0x54,0xC5,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair(client2NN3,new byte[] {0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x1D,0x76,0x03,0x01,0x01,0xC0,0xA8,0x00,0x32,0x54,0xC5,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - }; - - foreach (var req in requests) - { - ((ITestClient)req.Key).TestReceived(req.Value); - } - // because the process is running in background we need to wait it finish, so we can debug - Thread.Sleep(10000); - } - - [Fact] - public void Flatout2pc20230519() - { - new InitPacketCache.RedisClient().FlushDb(); - var client1GP = MockObject.CreateClient("91.43.63.201", 23756); - var client1NN1 = MockObject.CreateClient("91.43.63.201", 64871); - var client1NN2 = MockObject.CreateClient("91.43.63.201", 64871); - - var client2GP = MockObject.CreateClient("91.43.63.201", 1025); - var client2NN1 = MockObject.CreateClient("91.43.63.201", 41159); - var client2NN2 = MockObject.CreateClient("91.43.63.201", 41159); - var requests = new List>() - { - new KeyValuePair(client1GP,new byte[] {0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0xC8,0xDF,0x69,0x75,0x00,0x00,0x01,0xC0,0xA8,0x00,0x71,0x00,0x00,0x66,0x6C,0x61,0x74,0x6F,0x75,0x74,0x32,0x70,0x63,0x00}), - new KeyValuePair(client1NN1,new byte[] {0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0xC8,0xDF,0x69,0x75,0x01,0x00,0x01,0xC0,0xA8,0x00,0x71,0x00,0x00,0x66,0x6C,0x61,0x74,0x6F,0x75,0x74,0x32,0x70,0x63,0x00}), - new KeyValuePair(client1NN2,new byte[] {0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0xC8,0xDF,0x69,0x75,0x02,0x00,0x01,0xC0,0xA8,0x00,0x71,0x00,0x00,0x66,0x6C,0x61,0x74,0x6F,0x75,0x74,0x32,0x70,0x63,0x00}), - new KeyValuePair(client2GP,new byte[] {0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0xC8,0xDF,0x69,0x75,0x00,0x01,0x01,0xC0,0xA8,0x00,0x3C,0x00,0x00,0x66,0x6C,0x61,0x74,0x6F,0x75,0x74,0x32,0x70,0x63,0x00}), - new KeyValuePair(client2NN1,new byte[] {0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0xC8,0xDF,0x69,0x75,0x01,0x01,0x01,0xC0,0xA8,0x00,0x3C,0x00,0x00,0x66,0x6C,0x61,0x74,0x6F,0x75,0x74,0x32,0x70,0x63,0x00}), - new KeyValuePair(client2NN1,new byte[] {0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0xC8,0xDF,0x69,0x75,0x02,0x01,0x01,0xC0,0xA8,0x00,0x3C,0x00,0x00,0x66,0x6C,0x61,0x74,0x6F,0x75,0x74,0x32,0x70,0x63,0x00}) - }; - - foreach (var req in requests) - { - (req.Key as ITestClient).TestReceived(req.Value); - } - Thread.Sleep(10000); - } - [Fact] - /// - /// NN1 packet's private port can not equal to NN2's port - /// - public void Gautletps2Test20230620() - { - var client1 = MockObject.CreateClient("192.168.9.13", 57430); - var client2 = MockObject.CreateClient("192.168.9.12", 59624); - - var requests = new List>() - { - new KeyValuePair(client1,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x0B,0x90,0x1C,0x24,0x02,0x00,0x01,0xC0,0xA8,0x09,0x0D,0x08,0x0C,0x67,0x61,0x75,0x6E,0x74,0x6C,0x65,0x74,0x70,0x73,0x32,0x00}), - new KeyValuePair(client1,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x0B,0x90,0x1C,0x24,0x00,0x00,0x01,0xC0,0xA8,0x09,0x0D,0x00,0x00,0x67,0x61,0x75,0x6E,0x74,0x6C,0x65,0x74,0x70,0x73,0x32,0x00}), - new KeyValuePair(client1,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x0B,0x90,0x1C,0x24,0x01,0x00,0x01,0xC0,0xA8,0x09,0x0D,0x00,0x00,0x67,0x61,0x75,0x6E,0x74,0x6C,0x65,0x74,0x70,0x73,0x32,0x00}), - new KeyValuePair(client2,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x0B,0x90,0x1C,0x24,0x00,0x01,0x01,0xC0,0xA8,0x09,0x0C,0x00,0x00,0x67,0x61,0x75,0x6E,0x74,0x6C,0x65,0x74,0x70,0x73,0x32,0x00}), - new KeyValuePair(client2,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x0B,0x90,0x1C,0x24,0x01,0x01,0x01,0xC0,0xA8,0x09,0x0C,0x00,0x00,0x67,0x61,0x75,0x6E,0x74,0x6C,0x65,0x74,0x70,0x73,0x32,0x00}), - new KeyValuePair(client2,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x0B,0x90,0x1C,0x24,0x02,0x01,0x01,0xC0,0xA8,0x09,0x0C,0x08,0x0C,0x67,0x61,0x75,0x6E,0x74,0x6C,0x65,0x74,0x70,0x73,0x32,0x00}) - }; - - foreach (var item in requests) - { - item.Key.TestReceived(item.Value); - } - } - - [Fact] - public void MarvelPs2_20230707() - { - var client1GP = MockObject.CreateClient("192.168.9.2", 5165); - var client1NN = MockObject.CreateClient("192.168.9.2", 59300); - var client2GP = MockObject.CreateClient("192.168.9.9", 5165); - var client2NN = MockObject.CreateClient("192.168.9.9", 62545); - - var requests = new List>() - { - new KeyValuePair(client1GP,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x7D,0x47,0xCF,0x14,0x00,0x01,0x01,0xC0,0xA8,0x09,0x02,0x00,0x00,0x6D,0x61,0x72,0x76,0x6C,0x65,0x67,0x70,0x73,0x32,0x70,0x00,0x00}), - new KeyValuePair(client1NN,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x7D,0x47,0xCF,0x14,0x01,0x01,0x01,0xC0,0xA8,0x09,0x02,0x00,0x00,0x6D,0x61,0x72,0x76,0x6C,0x65,0x67,0x70,0x73,0x32,0x70,0x00}), - new KeyValuePair(client1NN,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x7D,0x47,0xCF,0x14,0x02,0x01,0x01,0xC0,0xA8,0x09,0x02,0x14,0x2D,0x6D,0x61,0x72,0x76,0x6C,0x65,0x67,0x70,0x73,0x32,0x70,0x00}), - new KeyValuePair(client2GP,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x7D,0x47,0xCF,0x14,0x00,0x00,0x01,0xC0,0xA8,0x09,0x09,0x00,0x00,0x6D,0x61,0x72,0x76,0x6C,0x65,0x67,0x70,0x73,0x32,0x70,0x00}), - new KeyValuePair(client2NN,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x7D,0x47,0xCF,0x14,0x02,0x00,0x01,0xC0,0xA8,0x09,0x09,0x14,0x2D,0x6D,0x61,0x72,0x76,0x6C,0x65,0x67,0x70,0x73,0x32,0x70,0x00}), - new KeyValuePair(client2NN,new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x02,0x00,0x7D,0x47,0xCF,0x14,0x01,0x00,0x01,0xC0,0xA8,0x09,0x09,0x00,0x00,0x6D,0x61,0x72,0x76,0x6C,0x65,0x67,0x70,0x73,0x32,0x70,0x00}) - }; - - foreach (var item in requests) - { - item.Key.TestReceived(item.Value); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/test/HandlerTest.cs b/src/Servers/NatNegotiation/test/HandlerTest.cs deleted file mode 100644 index f4bc6261d..000000000 --- a/src/Servers/NatNegotiation/test/HandlerTest.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.Net; -using UniSpy.Server.NatNegotiation.Application; -using UniSpy.Server.NatNegotiation.Contract.Request; -using UniSpy.Server.NatNegotiation.Handler.CmdHandler; -using Xunit; -namespace UniSpy.Server.NatNegotiation.Test -{ - - public class HandlerTest - { - Client _client; - public HandlerTest() - { - _client = MockObject.CreateClient(); - } - [Fact] - public void InitTest() - { - var rawRequest = new byte[] { - 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, - 0x00, - 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - var request = new InitRequest(rawRequest); - var handler = new InitHandler(_client, request); - handler.Handle(); - } - [Fact] - public void AddressTest() - { - var rawRequest = new byte[] { 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, 0x0a, 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - var request = new AddressCheckRequest(rawRequest); - var handler = new AddressCheckHandler(_client, request); - handler.Handle(); - } - [Fact] - public void ErtTest() - { - var rawRequest = new byte[] { - 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, - 0x03, - 0x00, 0x00, 0x03, 0x09, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - var request = new ErtAckRequest(rawRequest); - var handler = new ErtAckHandler(_client, request); - handler.Handle(); - } - [Fact] - public void NatifyTest() - { - var rawRequest = new byte[] { - 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, - 0x0c, - 0x00, 0x00, 0x03, 0x09, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - var request = new NatifyRequest(rawRequest); - var handler = new NatifyHandler(_client, request); - handler.Handle(); - } - - [Fact] - public void ReportTest() - { - var r1 = new UniSpy.Server.NatNegotiation.Aggregate.Misc.NatReportRecord() - { - ServerId = System.Guid.Empty, - PublicIPAddress = IPAddress.Parse("202.91.34.186"), - PrivateIPAddress = IPAddress.Parse("192.168.1.1") - }; - - var r2 = new UniSpy.Server.NatNegotiation.Aggregate.Misc.NatReportRecord() - { - ServerId = System.Guid.Empty, - PublicIPAddress = IPAddress.Parse("202.91.34.186"), - PrivateIPAddress = IPAddress.Parse("192.168.1.1") - }; - Assert.Equal(r1.PublicIPAddress, r2.PublicIPAddress); - Assert.Equal(r1.PrivateIPAddress, r2.PrivateIPAddress); - Assert.Equal(r1.ServerId, r2.ServerId); - - - // _client.Info.Cookie = - var req = new byte[] { 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x03, 0x0D, 0x00, 0x00, 0x02, 0x9A, 0xCC, 0x00, 0x01, 0x06, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - var request = new ReportRequest(req); - var handler = new ReportHandler(_client, request); - handler.Handle(); - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/test/MockObject.cs b/src/Servers/NatNegotiation/test/MockObject.cs deleted file mode 100644 index 41d4fbe96..000000000 --- a/src/Servers/NatNegotiation/test/MockObject.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Net; -using Moq; -using UniSpy.Server.NatNegotiation.Application; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.NatNegotiation.Test -{ - public static class MockObject - { - public static IClient Client = CreateClient(); - - public static Client CreateClient(string ipAddress = "192.168.1.1", int port = 9999) - { - var managerMock = new Mock(); - var connectionMock = new Mock(); - connectionMock.Setup(s => s.RemoteIPEndPoint).Returns(new IPEndPoint(IPAddress.Parse(ipAddress), port)); - connectionMock.Setup(s => s.Manager).Returns(managerMock.Object); - connectionMock.Setup(s => s.ConnectionType).Returns(NetworkConnectionType.Udp); - var serverMock = new NatNegotiation.Application.Server(managerMock.Object); - - return new Client(connectionMock.Object, serverMock); - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/test/NatDetectionTest.cs b/src/Servers/NatNegotiation/test/NatDetectionTest.cs deleted file mode 100644 index b01e30dbd..000000000 --- a/src/Servers/NatNegotiation/test/NatDetectionTest.cs +++ /dev/null @@ -1,221 +0,0 @@ -using System.Collections.Generic; -using System.Net; -using UniSpy.Server.NatNegotiation.Enumerate; -using UniSpy.Server.NatNegotiation.Aggregate.Redis; -using UniSpy.Server.NatNegotiation.Handler.CmdHandler; -using Xunit; - -namespace UniSpy.Server.NatNegotiation.Test -{ - public class NatDetectionTest - { - [Fact] - public void PublicIPTest() - { - var list = new List() - { - new InitPacketCache() - { - PortType = NatPortType.GP, - ClientIndex = NatClientIndex.GameClient, - Cookie = 123, - Version=3, - GameName ="gmtest", - PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:1"), - PrivateIPEndPoint = IPEndPoint.Parse("10.0.0.1:0") - }, - new InitPacketCache() - { - PortType = NatPortType.NN1, - ClientIndex = NatClientIndex.GameClient, - Cookie = 123, - Version=3, - GameName ="gmtest", - PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:2"), - PrivateIPEndPoint = IPEndPoint.Parse("10.0.0.1:0") - }, - new InitPacketCache() - { - PortType = NatPortType.NN2, - ClientIndex = NatClientIndex.GameClient, - Cookie = 123, - Version=3, - GameName ="gmtest", - PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:2"), - PrivateIPEndPoint = IPEndPoint.Parse("10.0.0.1:2") - }, - new InitPacketCache() - { - PortType = NatPortType.NN3, - ClientIndex = NatClientIndex.GameClient, - Cookie = 123, - Version=3, - GameName ="gmtest", - PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:2"), - PrivateIPEndPoint = IPEndPoint.Parse("10.0.0.1:2") - } - }; - var initInfo = new NatInitInfo(list); - var prop = AddressCheckHandler.DetermineNatTypeVersion3(initInfo); - - Assert.Equal(NatType.NoNat, prop.NatType); - } - [Fact] - public void FullConeTest() - { - var list = new List() - { - new InitPacketCache() - { - PortType = NatPortType.GP, - ClientIndex = NatClientIndex.GameClient, - Cookie = 123, - Version=3, - GameName ="gmtest", - PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:1"), - PrivateIPEndPoint = IPEndPoint.Parse("192.168.1.1:0") - }, - new InitPacketCache() - { - PortType = NatPortType.NN1, - ClientIndex = NatClientIndex.GameClient, - Cookie = 123, - Version=3, - GameName ="gmtest", - PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:2"), - PrivateIPEndPoint = IPEndPoint.Parse("192.168.1.1:0") - }, - new InitPacketCache() - { - PortType = NatPortType.NN2, - ClientIndex = NatClientIndex.GameClient, - Cookie = 123, - Version=3, - GameName ="gmtest", - PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:2"), - PrivateIPEndPoint = IPEndPoint.Parse("192.168.1.1:2") - }, - new InitPacketCache() - { - PortType = NatPortType.NN3, - ClientIndex = NatClientIndex.GameClient, - Cookie = 123, - Version=3, - GameName ="gmtest", - PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:2"), - PrivateIPEndPoint = IPEndPoint.Parse("192.168.1.1:2") - } - }; - var initInfo = new NatInitInfo(list); - var prop = AddressCheckHandler.DetermineNatTypeVersion3(initInfo); - Assert.Equal(NatType.FullCone, prop.NatType); - - } - - [Fact] - public void SymetricTest() - { - var list = new List() - { - new InitPacketCache() - { - PortType = NatPortType.GP, - ClientIndex = NatClientIndex.GameClient, - Cookie = 123, - Version=3, - GameName ="gmtest", - PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:1"), - PrivateIPEndPoint = IPEndPoint.Parse("192.168.1.1:1") - }, - new InitPacketCache() - { - PortType = NatPortType.NN1, - ClientIndex = NatClientIndex.GameClient, - Cookie = 123, - Version=3, - GameName ="gmtest", - PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:2"), - PrivateIPEndPoint = IPEndPoint.Parse("192.168.1.1:2") - }, - new InitPacketCache() - { - PortType = NatPortType.NN2, - ClientIndex = NatClientIndex.GameClient, - Cookie = 123, - Version=3, - GameName ="gmtest", - PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:3"), - PrivateIPEndPoint = IPEndPoint.Parse("192.168.1.1:2") - }, - new InitPacketCache() - { - PortType = NatPortType.NN3, - ClientIndex = NatClientIndex.GameClient, - Cookie = 123, - Version=3, - GameName ="gmtest", - PublicIPEndPoint = IPEndPoint.Parse("10.0.0.1:4"), - PrivateIPEndPoint = IPEndPoint.Parse("192.168.1.1:2") - } - }; - var initInfo = new NatInitInfo(list); - var prop = AddressCheckHandler.DetermineNatTypeVersion3(initInfo); - Assert.Equal(NatType.Symmetric, prop.NatType); - - } - [Fact] - public void NatStrategyTest() - { - - } - [Fact] - public void SposiriusNetworkTest() - { - var list = new List() - { - new InitPacketCache() - { - PortType = NatPortType.GP, - ClientIndex = NatClientIndex.GameClient, - Cookie = 123, - Version=3, - GameName ="gmtest", - PublicIPEndPoint = IPEndPoint.Parse("91.52.105.210:51520"), - PrivateIPEndPoint = IPEndPoint.Parse("192.168.0.60:0") - }, - new InitPacketCache() - { - PortType = NatPortType.NN1, - ClientIndex = NatClientIndex.GameClient, - Cookie = 123, - Version=3, - GameName ="gmtest", - PublicIPEndPoint = IPEndPoint.Parse("91.52.105.210:51521"), - PrivateIPEndPoint = IPEndPoint.Parse("192.168.0.60:0") - },new InitPacketCache() - { - PortType = NatPortType.NN2, - ClientIndex = NatClientIndex.GameClient, - Cookie = 123, - Version=3, - GameName ="gmtest", - PublicIPEndPoint = IPEndPoint.Parse("91.52.105.210:49832"), - PrivateIPEndPoint = IPEndPoint.Parse("192.168.0.60:49832") - },new InitPacketCache() - { - PortType = NatPortType.NN3, - ClientIndex = NatClientIndex.GameClient, - Cookie = 123, - Version=3, - GameName ="gmtest", - PublicIPEndPoint = IPEndPoint.Parse("91.52.105.210:49832"), - PrivateIPEndPoint = IPEndPoint.Parse("192.168.0.60:49832") - }, - }; - var initInfo = new NatInitInfo(list); - - var prop = AddressCheckHandler.DetermineNatTypeVersion3(initInfo); - Assert.Equal(NatType.Symmetric, prop.NatType); - } - } -} \ No newline at end of file diff --git a/src/Servers/NatNegotiation/test/RequestTest.cs b/src/Servers/NatNegotiation/test/RequestTest.cs deleted file mode 100644 index 052720468..000000000 --- a/src/Servers/NatNegotiation/test/RequestTest.cs +++ /dev/null @@ -1,87 +0,0 @@ -using UniSpy.Server.NatNegotiation.Enumerate; -using UniSpy.Server.NatNegotiation.Contract.Request; -using Xunit; - -namespace UniSpy.Server.NatNegotiation.Test -{ - public class RequestTest - { - [Fact] - public void InitTest() - { - var rawRequest = new byte[] { - 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, - 0x00, - 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - var request = new InitRequest(rawRequest); - request.Parse(); - Assert.True(RequestType.Init == request.CommandName); - Assert.True((uint)151191552 == request.Cookie); - Assert.True(NatClientIndex.GameClient == request.ClientIndex); - Assert.True(false == request.UseGamePort); - Assert.True((byte)3 == request.Version); - Assert.True(NatPortType.NN1 == request.PortType); - } - [Fact] - public void AddressTest() - { - var rawRequest = new byte[] { 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, 0x0a, 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - var request = new AddressCheckRequest(rawRequest); - request.Parse(); - Assert.True(RequestType.AddressCheck == request.CommandName); - Assert.True((uint)151191552 == request.Cookie); - Assert.True(NatClientIndex.GameClient == request.ClientIndex); - Assert.True(false == request.UseGamePort); - Assert.True((byte)3 == request.Version); - Assert.True(NatPortType.NN1 == request.PortType); - } - [Fact] - public void ErtAckTest() - { - var rawRequest = new byte[] { - 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, - 0x03, - 0x00, 0x00, 0x03, 0x09, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - var request = new ErtAckRequest(rawRequest); - request.Parse(); - Assert.True(RequestType.ErtAck == request.CommandName); - Assert.True((uint)151191552 == request.Cookie); - Assert.True(NatClientIndex.GameClient == request.ClientIndex); - Assert.True((byte)3 == request.Version); - Assert.True(false == request.UseGamePort); - Assert.True(NatPortType.NN1 == request.PortType); - } - [Fact] - public void NatifyTest() - { - var rawRequest = new byte[] { - 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, - 0x0c, - 0x00, 0x00, 0x03, 0x09, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - var request = new ErtAckRequest(rawRequest); - request.Parse(); - Assert.True(RequestType.NatifyRequest == request.CommandName); - Assert.True((uint)151191552 == request.Cookie); - Assert.True(NatClientIndex.GameClient == request.ClientIndex); - Assert.True((byte)3 == request.Version); - Assert.True(false == request.UseGamePort); - Assert.True(NatPortType.NN1 == request.PortType); - } - [Fact(Skip = "Not implemented")] - public void ReportTest() - { - - } - [Fact] - public void PreInitTest() - { - var raw = new byte[] { 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x04, 0x0f, 0xb5, 0xe0, 0x95, 0x2a, 0x00, 0x24, 0x38, 0xb2, 0xb3, 0x5e }; - var request = new PreInitRequest(raw); - request.Parse(); - Assert.True(RequestType.PreInit == request.CommandName); - Assert.True(PreInitState.WaitingForClient == request.State); - } - } -} diff --git a/src/Servers/NatNegotiation/test/UniSpy.Server.NatNegotiation.Test.csproj b/src/Servers/NatNegotiation/test/UniSpy.Server.NatNegotiation.Test.csproj deleted file mode 100644 index 9b6f82b32..000000000 --- a/src/Servers/NatNegotiation/test/UniSpy.Server.NatNegotiation.Test.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - net6.0 - false - ..\..\..\..\common\Icon\UniSpy_Logo.ico - - - AnyCPU - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/CmdHandlerBase.cs b/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/CmdHandlerBase.cs deleted file mode 100755 index 5c7c927f1..000000000 --- a/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/CmdHandlerBase.cs +++ /dev/null @@ -1,29 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass -{ - public abstract class CmdHandlerBase : UniSpy.Server.Core.Abstraction.BaseClass.CmdHandlerBase - { - /// - /// Because all errors are sent by SendGPError() - /// so we if the error code != noerror we send it - /// - protected new Client _client => (Client)base._client; - protected new RequestBase _request => (RequestBase)base._request; - protected new ResultBase _result { get => (ResultBase)base._result; set => base._result = value; } - public CmdHandlerBase(IClient client, IRequest request) : base(client, request) - { - } - - protected override void HandleException(System.Exception ex) - { - if (ex is GPException) - { - _client.Send(ex as IResponse); - } - base.HandleException(ex); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/LoggedInCmdHandlerBase.cs b/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/LoggedInCmdHandlerBase.cs deleted file mode 100755 index 3931ef350..000000000 --- a/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/LoggedInCmdHandlerBase.cs +++ /dev/null @@ -1,48 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.PresenceConnectionManager.Aggregate.Redis; - -namespace UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass -{ - public abstract class LoggedInCmdHandlerBase : CmdHandlerBase - { - public LoggedInCmdHandlerBase(IClient client, IRequest request) : base(client, request) - { - } - protected override void RequestCheck() - { - if (_client.Info.LoginStat != Enumerate.LoginStatus.Completed) - { - throw new GPException("You are not logged in, please login first."); - } - base.RequestCheck(); - } - public override void Handle() - { - base.Handle(); - try - { - // we publish this message to redis channel - PublishMessage(); - } - catch (Exception ex) - { - HandleException(ex); - } - } - protected virtual void PublishMessage() - { - // we do not publish message when the message is received from remote client - if (_client.Info.IsRemoteClient) - { - return; - } - if (_client.Info.LoginStat != Enumerate.LoginStatus.Completed) - { - return; - } - var msg = new UserInfoMessage(_client.GetRemoteClient()); - Application.Server.UserInfoChannel.PublishMessage(msg); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/RequestBase.cs b/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/RequestBase.cs deleted file mode 100755 index 0995077f5..000000000 --- a/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/RequestBase.cs +++ /dev/null @@ -1,62 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.Core.Extension; -using UniSpy.Server.Core.Misc; - -namespace UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass -{ - public abstract class RequestBase : UniSpy.Server.Core.Abstraction.BaseClass.RequestBase - { - public new string CommandName{ get => (string)base.CommandName; - protected set => base.CommandName = value; } - public int OperationID { get; protected set; } - - public new string RawRequest{ get => (string)base.RawRequest; set => base.RawRequest = value; } - public Dictionary RequestKeyValues { get; protected set; } - - public RequestBase() - { - } - - public RequestBase(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - RequestKeyValues = GameSpyUtils.ConvertToKeyValue(RawRequest); - CommandName = RequestKeyValues.Keys.First(); - - if (RequestKeyValues.ContainsKey("id")) - { - int operationID; - if (!int.TryParse(RequestKeyValues["id"], out operationID)) - { - throw new GPParseException("namespaceid is invalid."); - } - OperationID = operationID; - } - } - - public static string NormalizeRequest(string message) - { - if (message.Contains("login")) - { - message = message.Replace(@"\-", @"\"); - - int pos = message.IndexesOf("\\")[1]; - - if (message.Substring(pos, 2) != "\\\\") - { - message = message.Insert(pos, "\\"); - } - return message; - } - else - { - return message; - } - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/ResponseBase.cs b/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/ResponseBase.cs deleted file mode 100755 index 4127efd2a..000000000 --- a/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/ResponseBase.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass -{ - public abstract class ResponseBase : UniSpy.Server.Core.Abstraction.BaseClass.ResponseBase - { - protected new ResultBase _result => (ResultBase)base._result; - protected new RequestBase _request => (RequestBase)base._request; - public new string SendingBuffer { get => (string)base.SendingBuffer; protected set => base.SendingBuffer = value; } - - public ResponseBase(RequestBase request, ResultBase result) : base(request, result) - { - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/ResultBase.cs b/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/ResultBase.cs deleted file mode 100755 index e5d822055..000000000 --- a/src/Servers/PresenceConnectionManager/src/Abstraction/BaseClass/ResultBase.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass -{ - public abstract class ResultBase : UniSpy.Server.Core.Abstraction.BaseClass.ResultBase - { - public ResultBase() - { - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Abstraction/Interface/IShareClient.cs b/src/Servers/PresenceConnectionManager/src/Abstraction/Interface/IShareClient.cs deleted file mode 100644 index f0f078cdd..000000000 --- a/src/Servers/PresenceConnectionManager/src/Abstraction/Interface/IShareClient.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.PresenceConnectionManager.Aggregate; -using UniSpy.Server.PresenceConnectionManager.Application; - -namespace UniSpy.Server.PresenceConnectionManager.Abstraction.Interface -{ - public interface IShareClient : IClient, ITestClient - { - public new ClientInfo Info { get; } - public RemoteClient GetRemoteClient(); - } -} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Abstraction/Interface/IStorageOperation.cs b/src/Servers/PresenceConnectionManager/src/Abstraction/Interface/IStorageOperation.cs deleted file mode 100644 index 2ed717e1e..000000000 --- a/src/Servers/PresenceConnectionManager/src/Abstraction/Interface/IStorageOperation.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.PresenceConnectionManager.Contract.Result; -using UniSpy.Server.Core.Database.DatabaseModel; - -namespace UniSpy.Server.PresenceConnectionManager.Abstraction.Interface -{ - public interface IStorageOperation - { - List GetBlockedProfileIds(int profileId, int namespaceId); - List GetFriendProfileIds(int profileId, int namespaceId); - void DeleteFriendByProfileId(int profileId, int targetId, int namespaceId); - (int, int, int) GetUsersInfos(string email, string nickName); - (int, int, int) GetUsersInfos(string uniqueNick, int namespaceId); - (int, int, int) GetUsersInfos(string authToken, int partnerId, int namespaceId); - void UpdateBlockInfo(int targetId, int profileId, int namespaceId); - void UpdateFriendInfo(int targetId, int profileId, int namespaceId); - void UpdateProfileInfo(Profile profile); - GetProfileDataModel GetProfileInfos(int profileId, int namespaceId); - void UpdateUniqueNick(int subProfileId, string uniqueNick); - void UpdateNickName(int profileId, string oldNick, string newNick); - void AddNickName(int userId, int profileId, string newNick); - void UpdateSubProfileInfo(Subprofile subprofile); - bool IsEmailExist(string email); - } -} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/Misc/LoginChallengeProof.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/Misc/LoginChallengeProof.cs deleted file mode 100755 index fb1e3398c..000000000 --- a/src/Servers/PresenceConnectionManager/src/Aggregate/Misc/LoginChallengeProof.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System.Text; -using UniSpy.Server.PresenceConnectionManager.Enumerate; -using UniSpy.Server.Core.Extension; - -namespace UniSpy.Server.PresenceConnectionManager.Structure -{ - public sealed class LoginChallengeProof - { - public const string ServerChallenge = "0000000000"; - public string UserData { get; private set; } - public LoginType LoginType { get; private set; } - public int? PartnerID { get; private set; } - public string Challenge1 { get; private set; } - public string Challenge2 { get; private set; } - public string PasswordHash { get; private set; } - /// - /// The context using to build the challenge and response, the challenge1 can be server challenge or client challenge as same as challenge2. - /// - public LoginChallengeProof(string userData, LoginType loginType, int? partnerID, string challenge1, string challenge2, string passwordHash) - { - UserData = userData; - LoginType = loginType; - PartnerID = partnerID; - Challenge1 = challenge1; - Challenge2 = challenge2; - PasswordHash = passwordHash; - } - - /// - /// Generates an MD5 hash, which is used to verify the connections login information - /// - /// - /// The proof verification MD5 hash string that can be compared to what the _connection sends, - /// to verify that the users entered password matches the specific user data in the database. - /// - public static string GenerateProof(LoginChallengeProof data) - { - string tempUserData = data.UserData; - - // Login types NickEmail and AuthToken don't use partnerID append - if (data.PartnerID is not null) - { - if (data?.PartnerID != (int)GPPartnerID.Gamespy - && data?.LoginType != LoginType.AuthToken) - { - tempUserData = $@"{data.PartnerID}@{data.UserData}"; - } - } - - // Generate our response string - var responseString = new StringBuilder(); - responseString.Append(data.PasswordHash); - responseString.Append(' ', 48); // 48 spaces - responseString.Append(tempUserData); - responseString.Append(data.Challenge1); - responseString.Append(data.Challenge2); - responseString.Append(data.PasswordHash); - - string hashString = responseString.ToString().GetMD5Hash(); - - return hashString; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/Misc/SDKRevision.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/Misc/SDKRevision.cs deleted file mode 100755 index fe94c084d..000000000 --- a/src/Servers/PresenceConnectionManager/src/Aggregate/Misc/SDKRevision.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Enumerate; - -namespace UniSpy.Server.PresenceConnectionManager.Aggregate.Misc -{ - public sealed class SdkRevision - { - public SdkRevisionType? SDKRevisionType { get; set; } - public bool IsSDKRevisionValid => SDKRevisionType == 0 ? false : true; - public bool IsSupportGPINewAuthNotification => (SDKRevisionType ^ Enumerate.SdkRevisionType.GPINewAuthNotification) != 0 ? true : false; - public bool IsSupportGPINewRevokeNotification => (SDKRevisionType ^ Enumerate.SdkRevisionType.GPINewRevokeNotification) != 0 ? true : false; - public bool IsSupportGPINewStatusNotification => (SDKRevisionType ^ Enumerate.SdkRevisionType.GPINewStatusNotification) != 0 ? true : false; - public bool IsSupportGPINewListRetrevalOnLogin => (SDKRevisionType ^ Enumerate.SdkRevisionType.GPINewListRetrevalOnLogin) != 0 ? true : false; - public bool IsSupportGPIRemoteAuthIDSNotification => (SDKRevisionType ^ Enumerate.SdkRevisionType.GPIRemoteAuthIDSNotification) != 0 ? true : false; - public bool IsSupportGPINewCDKeyRegistration => (SDKRevisionType ^ Enumerate.SdkRevisionType.GPINewCDKeyRegistration) != 0 ? true : false; - public SdkRevision() - { - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/Misc/Status/UserStatus.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/Misc/Status/UserStatus.cs deleted file mode 100755 index 6914d6659..000000000 --- a/src/Servers/PresenceConnectionManager/src/Aggregate/Misc/Status/UserStatus.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Enumerate; - -namespace UniSpy.Server.PresenceConnectionManager.Aggregate.Misc -{ - /// - /// This status class is for old SDK version - /// - public sealed class UserStatus - { - public GPStatusCode CurrentStatus { get; set; } - public string StatusString { get; set; } - public string LocationString { get; set; } - public UserStatus() - { - CurrentStatus = GPStatusCode.Offline; - StatusString = ""; - LocationString = ""; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/Misc/Status/UserStatusInfo.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/Misc/Status/UserStatusInfo.cs deleted file mode 100755 index 1e5362d34..000000000 --- a/src/Servers/PresenceConnectionManager/src/Aggregate/Misc/Status/UserStatusInfo.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace UniSpy.Server.PresenceConnectionManager.Aggregate.Misc -{ - /// - /// This status info class is for new SDK version - /// - public sealed class UserStatusInfo - { - public string StatusState { get; set; } - public string BuddyIP { get; set; } - public string HostIP { get; set; } - public string HostPrivateIP { get; set; } - public int? QueryReportPort { get; set; } - public int? HostPort { get; set; } - public int? SessionFlags { get; set; } - public string RichStatus { get; set; } - public string GameType { get; set; } - public string GameVariant { get; set; } - public string GameMapName { get; set; } - public string QuietModeFlags { get; set; } - public UserStatusInfo() - { - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoChannel.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoChannel.cs deleted file mode 100644 index 7b2bc9e4b..000000000 --- a/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoChannel.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Extension.Redis; -using UniSpy.Server.PresenceConnectionManager.Abstraction.Interface; -using UniSpy.Server.PresenceConnectionManager.Application; - -namespace UniSpy.Server.PresenceConnectionManager.Aggregate.Redis -{ - public class UserInfoChannel : RedisChannelBase - { - public UserInfoChannel() : base(RedisChannelName.PresenceConnectionManagerUserInfoChannel) - { - } - public override void ReceivedMessage(UserInfoMessage message) - { - // when we are in debug mode, the linq operation will not be executed, so we have to execute manually to make program do not crash - if (System.Diagnostics.Debugger.IsAttached) - { - _ = ((ClientInfo)(message.Client.Info)).UserInfo; - _ = ((ClientInfo)(message.Client.Info)).ProfileInfo; - _ = ((ClientInfo)(message.Client.Info)).SubProfileInfo; - } - - if (message.Client.Server.Id == ServerLauncher.Server.Id) - { - return; - } - var client = (IShareClient)ClientManager.GetClient(message.Client.Connection.RemoteIPEndPoint); - if (client is null) - { - ClientManager.AddClient(message.Client); - client = message.Client; - } - else - { - // we update the remote client info - ((RemoteClient)client).Info = message.Client.Info; - } - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoMessage.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoMessage.cs deleted file mode 100644 index cff60f120..000000000 --- a/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoMessage.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Net; -using System; -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.Core.Misc; -using Newtonsoft.Json; - -namespace UniSpy.Server.PresenceConnectionManager.Aggregate.Redis -{ - public class UserInfoMessage - { - [JsonProperty] - public RemoteClient Client { get; private set; } - public UserInfoMessage() { } - public UserInfoMessage(RemoteClient client) - { - Client = client; - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoRedisKey.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoRedisKey.cs deleted file mode 100755 index 21d84963e..000000000 --- a/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoRedisKey.cs +++ /dev/null @@ -1,17 +0,0 @@ -// using Newtonsoft.Json; -// using System; -// using UniSpy.Server.Core.Abstraction.BaseClass.Redis; - -// namespace UniSpy.Server.PresenceConnectionManager.Handler.SystemHandler.Redis -// { -// public sealed class UserInfoRedisKey : RedisKey -// { -// [JsonProperty(Order = -2)] -// public Guid ServerID { get; set; } -// public string SessionHashValue { get; set; } -// public UserInfoRedisKey() -// { -// Db = UniSpy.Server.Core.Extensions.DbNumber.GamePresence; -// } -// } -// } \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoRedisOperator.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoRedisOperator.cs deleted file mode 100755 index 8913ef0b3..000000000 --- a/src/Servers/PresenceConnectionManager/src/Aggregate/Redis/UserInfoRedisOperator.cs +++ /dev/null @@ -1,9 +0,0 @@ -// using UniSpy.Server.PresenceConnectionManager.Structure.Data; -// using UniSpy.Server.Core.Abstraction.BaseClass.Redis; - -// namespace UniSpy.Server.PresenceConnectionManager.Handler.SystemHandler.Redis -// { -// public sealed class UserInfoRedisOperator : UniSpyRedisOperator -// { -// } -// } diff --git a/src/Servers/PresenceConnectionManager/src/Aggregate/RemoteClient.cs b/src/Servers/PresenceConnectionManager/src/Aggregate/RemoteClient.cs deleted file mode 100644 index 9fde82013..000000000 --- a/src/Servers/PresenceConnectionManager/src/Aggregate/RemoteClient.cs +++ /dev/null @@ -1,48 +0,0 @@ -using Newtonsoft.Json; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.Core.Network; -using UniSpy.Server.PresenceConnectionManager.Abstraction.Interface; -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceConnectionManager.Handler; - -namespace UniSpy.Server.PresenceConnectionManager.Aggregate -{ - - public class RemoteClient : IShareClient - { - public bool IsLogRaw { get; set; } - [JsonConverter(typeof(ConcreteTypeConverter))] - public IConnection Connection { get; set; } - public ICryptography Crypto => null; - [JsonConverter(typeof(ConcreteTypeConverter))] - public ClientInfoBase Info { get; set; } - [JsonIgnore] - ClientInfo IShareClient.Info => (ClientInfo)Info; - [JsonConverter(typeof(ConcreteTypeConverter))] - public IServer Server { get; set; } - /// - /// using for json deserialization - /// - public RemoteClient() { } - public RemoteClient(Client client) - { - Connection = new RemoteTcpConnection(client.Connection, new RemoteTcpConnectionManager()); - Server = new RemoteServer(client.Server); - Info = client.Info.DeepCopy(); - ((ClientInfo)Info).IsRemoteClient = true; - } - - public RemoteClient GetRemoteClient() => this; - - public void Send(IResponse response) { } - public void TestReceived(byte[] buffer) - { - this.LogNetworkReceiving(buffer); - var switcher = new CmdSwitcher(this, UniSpyEncoding.GetString(buffer)); - switcher.Handle(); - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Application/Client.cs b/src/Servers/PresenceConnectionManager/src/Application/Client.cs deleted file mode 100644 index 35a8f9102..000000000 --- a/src/Servers/PresenceConnectionManager/src/Application/Client.cs +++ /dev/null @@ -1,53 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Enumerate; -using UniSpy.Server.PresenceConnectionManager.Handler; -using UniSpy.Server.PresenceConnectionManager.Structure; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.PresenceConnectionManager.Abstraction.Interface; -using UniSpy.Server.PresenceConnectionManager.Aggregate; - -namespace UniSpy.Server.PresenceConnectionManager.Application -{ - public sealed class Client : ClientBase, IShareClient - { - public new ITcpConnection Connection => (ITcpConnection)base.Connection; - public new ClientInfo Info { get => (ClientInfo)base.Info; set => base.Info = value; } - private RemoteClient _remoteClient; - public Client(IConnection connection, IServer server) : base(connection, server) - { - Info = new ClientInfo(); - _remoteClient = new RemoteClient(this); - } - public Client(IConnection connection, IServer server, ClientInfo info) : this(connection, server) - { - Info = info; - _remoteClient = new RemoteClient(this); - } - protected override void OnConnected() - { - base.OnConnected(); - // Only send the login challenge once - if (Info.LoginStat != LoginStatus.Connected) - { - Connection.Disconnect(); - // Throw the error - this.LogWarn("The server challenge has already been sent. Cannot send another login challenge."); - } - - Info.LoginStat = LoginStatus.Processing; - string sendingBuffer = $@"\lc\1\challenge\{LoginChallengeProof.ServerChallenge}\id\{1}\final\"; - this.LogNetworkSending(sendingBuffer); - Connection.Send(sendingBuffer); - } - - protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, UniSpyEncoding.GetString((byte[])buffer)); - - public RemoteClient GetRemoteClient() - { - _remoteClient.Info = Info; - return _remoteClient; - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs b/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs deleted file mode 100755 index 686025c0a..000000000 --- a/src/Servers/PresenceConnectionManager/src/Application/ClientInfo.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using UniSpy.Server.PresenceConnectionManager.Enumerate; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Database.DatabaseModel; -using UniSpy.Server.PresenceConnectionManager.Aggregate.Misc; -using System.Linq; -using Newtonsoft.Json; - -namespace UniSpy.Server.PresenceConnectionManager.Application -{ - public sealed class ClientInfo : ClientInfoBase - { - public int? UserId { get; set; } - public int? ProfileId { get; set; } - public int? SubProfileId { get; set; } - public const ushort SessionKey = 1111; - public const string LoginTicket = "0000000000000000000000__"; - public DateTime CreatedTime { get; private set; } = DateTime.Now; - public Guid UserGuid { get; private set; } - public SdkRevision SdkRevision { get; private set; } = new SdkRevision(); - public UserStatus Status { get; set; } = new UserStatus(); - public UserStatusInfo StatusInfo { get; set; } - public LoginStatus LoginStat { get; set; } = LoginStatus.Connected; - - [JsonIgnore] - private User _userInfo; - [JsonIgnore] - private Profile _profileInfo; - [JsonIgnore] - private Subprofile _subProfileInfo; - [JsonIgnore] - public User UserInfo - { - get - { - if (UserId is null) - { - throw new UniSpy.Exception("UserId is not setted"); - } - if (_userInfo is null) - { - using (var db = new UniSpyContext()) - { - _userInfo = db.Users.Where(s => s.Userid == UserId).First(); - } - } - return _userInfo; - } - } - [JsonIgnore] - public Profile ProfileInfo - { - get - { - if (ProfileId is null) - { - throw new UniSpy.Exception("ProfileId is not set"); - } - if (_profileInfo is null) - { - using (var db = new UniSpyContext()) - { - _profileInfo = db.Profiles.Where(s => s.Profileid == ProfileId).First(); - } - } - return _profileInfo; - } - } - [JsonIgnore] - public Subprofile SubProfileInfo - { - get - { - if (SubProfileId is null) - { - throw new UniSpy.Exception("Subprofile is not set"); - } - if (_subProfileInfo is null) - { - using (var db = new UniSpyContext()) - { - _subProfileInfo = db.Subprofiles.Where(s => s.Subprofileid == SubProfileId).First(); - } - } - return _subProfileInfo; - } - } - - public bool IsRemoteClient { get; set; } - public ClientInfo() { } - public ClientInfo DeepCopy() - { - var infoCopy = (ClientInfo)this.MemberwiseClone(); - return infoCopy; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Application/ClientManager.cs b/src/Servers/PresenceConnectionManager/src/Application/ClientManager.cs deleted file mode 100644 index 55bc225f9..000000000 --- a/src/Servers/PresenceConnectionManager/src/Application/ClientManager.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Net; -using System.Linq; -using UniSpy.Server.Core.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceConnectionManager.Application -{ - public class ClientManager : ClientManagerBase - { - public static Client GetClient(int profileid, int? productid = null, int? namespaceId = null) - { - return (Client)ClientPool.Values.FirstOrDefault( - c => ((ClientInfo)c.Info).SubProfileInfo.Productid == productid - && ((ClientInfo)c.Info).SubProfileInfo.Profileid == profileid - && ((ClientInfo)c.Info).SubProfileInfo.Namespaceid == namespaceId); - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Application/Program.cs b/src/Servers/PresenceConnectionManager/src/Application/Program.cs deleted file mode 100755 index 60c63a3c2..000000000 --- a/src/Servers/PresenceConnectionManager/src/Application/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.PresenceConnectionManager.Application -{ - public class Program - { - static void Main(string[] args) - { - try - { - new ServerLauncher().Start(); - Console.WriteLine("Press < Q > to exit. "); - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - catch (System.Exception e) - { - UniSpy.Exception.HandleException(e); - } - finally - { - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Application/Server.cs b/src/Servers/PresenceConnectionManager/src/Application/Server.cs deleted file mode 100644 index 47cd47c7d..000000000 --- a/src/Servers/PresenceConnectionManager/src/Application/Server.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Net; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Network.Tcp.Server; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.PresenceConnectionManager.Aggregate.Redis; -using UniSpy.Server.PresenceConnectionManager.Abstraction.Interface; - -namespace UniSpy.Server.PresenceConnectionManager.Application -{ - public sealed class Server : ServerBase - { - public static readonly UserInfoChannel UserInfoChannel = new UserInfoChannel(); - static Server() - { - _name = "PresenceConnectionManager"; - } - public Server() { } - public Server(IConnectionManager manager) : base(manager) { } - protected override IClient CreateClient(IConnection connection) => new Client(connection, this); - protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new TcpConnectionManager(endPoint); - protected override IClient HandleConnectionInitialization(IConnection connection) - { - var client = (IShareClient)base.HandleConnectionInitialization(connection); - if (client.Info.IsRemoteClient) - { - var info = client.Info; - info.IsRemoteClient = false; - // we parse the info into our client - client = new Client(connection, this, info); - } - return client; - } - public override void Start() - { - base.Start(); - UserInfoChannel.Subscribe(); - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Application/ServerLauncher.cs b/src/Servers/PresenceConnectionManager/src/Application/ServerLauncher.cs deleted file mode 100644 index de9024218..000000000 --- a/src/Servers/PresenceConnectionManager/src/Application/ServerLauncher.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.BaseClass.Factory; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.PresenceConnectionManager.Application -{ - public sealed class ServerLauncher : ServerLauncherBase - { - public static IServer Server => ServerInstances[0]; - protected override List LaunchNetworkService() => new List { new Server() }; - } -} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Application/StorageOperation.cs b/src/Servers/PresenceConnectionManager/src/Application/StorageOperation.cs deleted file mode 100644 index ca322f607..000000000 --- a/src/Servers/PresenceConnectionManager/src/Application/StorageOperation.cs +++ /dev/null @@ -1,283 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.Interface; -using UniSpy.Server.Core.Database.DatabaseModel; -using System.Linq; -using System.Collections.Generic; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.PresenceSearchPlayer.Exception.Login; -using UniSpy.Server.PresenceConnectionManager.Contract.Result; - - -namespace UniSpy.Server.PresenceConnectionManager.Application -{ - internal sealed class StorageOperation : IStorageOperation - { - public static IStorageOperation Persistance = new StorageOperation(); - public bool IsEmailExist(string email) - { - using (var db = new UniSpyContext()) - { - var result = from u in db.Users - //According to FSW partnerid is not nessesary - where u.Email == email - select u.Userid; - - if (result.Count() == 0) - { - return false; - } - return true; - } - } - public void DeleteFriendByProfileId(int profileId, int targetId, int namespaceId) - { - using (var db = new UniSpyContext()) - { - var result = db.Friends.Where(f => f.Profileid == profileId - && f.Targetid == targetId - && f.Namespaceid == namespaceId).FirstOrDefault(); - if (result is not null) - { - db.Friends.Remove(result); - } - else - { - throw new GPDatabaseException("More than one buddy found in database, please check database."); - } - } - } - - public List GetBlockedProfileIds(int profileId, int namespaceId) - { - using (var db = new UniSpyContext()) - { - return db.Blockeds.Where(f => f.Profileid == profileId - && f.Namespaceid == namespaceId) - .Select(f => f.Targetid).ToList(); - } - - } - public List GetFriendProfileIds(int profileId, int namespaceId) - { - using (var db = new UniSpyContext()) - { - return db.Friends.Where(f => f.Profileid == profileId - && f.Namespaceid == namespaceId).Select(f => f.Targetid).ToList(); - } - } - - public GetProfileDataModel GetProfileInfos(int profileId, int namespaceId) - { - using (var db = new UniSpyContext()) - { - //we have to make sure the search target has the same namespaceID - var result = from p in db.Profiles - join s in db.Subprofiles on p.Profileid equals s.Profileid - join u in db.Users on p.Userid equals u.Userid - where p.Profileid == profileId - && s.Namespaceid == namespaceId - select new GetProfileDataModel - { - Nick = p.Nick, - ProfileId = p.Profileid, - UniqueNick = s.Uniquenick, - Email = u.Email, - Firstname = p.Firstname, - Lastname = p.Lastname, - Icquin = p.Icquin, - Homepage = p.Homepage, - Zipcode = p.Zipcode, - Countrycode = p.Countrycode, - Longitude = p.Longitude, - Latitude = p.Latitude, - Location = p.Location, - Birthday = p.Birthday, - Birthmonth = p.Birthmonth, - Birthyear = p.Birthyear, - Sex = (byte)p.Sex, - Publicmask = p.Publicmask, - Aim = p.Aim, - Picture = p.Picture, - Occupationid = p.Occupationid, - Industryid = p.Industryid, - Incomeid = p.Incomeid, - Marriedid = p.Marriedid, - Childcount = p.Childcount, - Interests1 = p.Interests1, - Ownership1 = p.Ownership1, - Connectiontype = p.Connectiontype, - }; - - return result.FirstOrDefault(); - } - } - - public (int, int, int) GetUsersInfos(string email, string nickName) - { - using (var db = new UniSpyContext()) - { - var result = from u in db.Users - join p in db.Profiles on u.Userid equals p.Userid - join n in db.Subprofiles on p.Profileid equals n.Profileid - where u.Email == email - && p.Nick == nickName - select new { u, p, n }; - - var info = result.FirstOrDefault(); - if (info is null) - { - throw new GPLoginBadProfileException("email and nick dose not exist"); - } - return (info.u.Userid, info.p.Profileid, info.n.Subprofileid); - } - } - - public (int, int, int) GetUsersInfos(string uniqueNick, int namespaceId) - { - - using (var db = new UniSpyContext()) - { - var result = from n in db.Subprofiles - join p in db.Profiles on n.Profileid equals p.Profileid - join u in db.Users on p.Userid equals u.Userid - where n.Uniquenick == uniqueNick - && n.Namespaceid == namespaceId - select new { u, p, n }; - - var info = result.FirstOrDefault(); - if (info is null) - { - throw new GPLoginBadUniquenickException($"The uniquenick: {uniqueNick} is invalid."); - } - return (info.u.Userid, info.p.Profileid, info.n.Subprofileid); - } - } - - public (int, int, int) GetUsersInfos(string authToken, int partnerId, int namespaceId) - { - using (var db = new UniSpyContext()) - { - var result = from u in db.Users - join p in db.Profiles on u.Userid equals p.Userid - join n in db.Subprofiles on p.Profileid equals n.Profileid - where n.Authtoken == authToken - && n.Partnerid == partnerId - && n.Namespaceid == namespaceId - select new { u, p, n }; - - var info = result.FirstOrDefault(); - if (info is null) - { - throw new GPLoginBadPreAuthException("The pre-authentication token is invalid."); - } - return (info.u.Userid, info.p.Profileid, info.n.Subprofileid); - } - } - - public void UpdateBlockInfo(int targetId, int profileId, int namespaceId) - { - using (var db = new UniSpyContext()) - { - if (db.Blockeds.Where(b => b.Targetid == targetId - && b.Namespaceid == namespaceId - && b.Profileid == profileId).Count() == 0) - { - Blocked blocked = new Blocked - { - Profileid = profileId, - Targetid = targetId, - Namespaceid = namespaceId - }; - db.Blockeds.Update(blocked); - } - } - } - - public void UpdateFriendInfo(int targetId, int profileId, int namespaceId) - { - using (var db = new UniSpyContext()) - { - if (db.Friends.Where(b => b.Targetid == targetId - && b.Namespaceid == namespaceId - && b.Profileid == profileId).Count() == 0) - { - Friend friend = new Friend - { - Profileid = profileId, - Targetid = targetId, - Namespaceid = namespaceId - }; - db.Friends.Update(friend); - } - } - } - public void AddNickName(int userId, int profileId, string newNick) - { - using (var db = new UniSpyContext()) - { - - var profiles = new UniSpy.Server.Core.Database.DatabaseModel.Profile - { - Profileid = profileId, - Nick = newNick, - Userid = userId - }; - - db.Add(profiles); - db.SaveChanges(); - } - } - public void UpdateNickName(int profileId, string oldNick, string newNick) - { - using (var db = new UniSpyContext()) - { - var result = from p in db.Profiles - where p.Profileid == profileId - && p.Nick == oldNick - select p; - - if (result.Count() != 1) - { - throw new GPDatabaseException("No user infomation found in database."); - } - else - { - result.First().Nick = newNick; - } - - var profile = db.Profiles.Where(p => p.Profileid == profileId - && p.Nick == oldNick).First(); - profile.Nick = newNick; - db.Profiles.Add(profile); - db.SaveChanges(); - } - } - - public void UpdateProfileInfo(Profile profile) - { - using (var db = new UniSpyContext()) - { - db.Update(profile); - db.SaveChanges(); - } - } - - public void UpdateUniqueNick(int subProfileId, string uniqueNick) - { - using (var db = new UniSpyContext()) - { - var result = db.Subprofiles.FirstOrDefault(s => s.Subprofileid == subProfileId); - result.Uniquenick = uniqueNick; - db.Subprofiles.Update(result); - db.SaveChanges(); - } - } - public void UpdateSubProfileInfo(Subprofile subprofile) - { - using (var db = new UniSpyContext()) - { - db.Subprofiles.Update(subprofile); - db.SaveChanges(); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/AddBuddyRequest.cs b/src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/AddBuddyRequest.cs deleted file mode 100755 index c722c9583..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/AddBuddyRequest.cs +++ /dev/null @@ -1,35 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Request -{ - - public sealed class AddBuddyRequest : RequestBase - { - public int FriendProfileID { get; private set; } - public string Reason { get; private set; } - public AddBuddyRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - - - if (!RequestKeyValues.ContainsKey("sesskey") || !RequestKeyValues.ContainsKey("newprofileid") || !RequestKeyValues.ContainsKey("reason")) - { - throw new GPParseException("addbuddy request is invalid."); - } - - int friendPID; - if (!int.TryParse(RequestKeyValues["newprofileid"], out friendPID)) - { - throw new GPParseException("newprofileid format is incorrect."); - } - - FriendProfileID = friendPID; - Reason = RequestKeyValues["reason"]; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/DelBuddyRequest.cs b/src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/DelBuddyRequest.cs deleted file mode 100755 index c7f1b351b..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/DelBuddyRequest.cs +++ /dev/null @@ -1,40 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Request -{ - /// - /// Delete a user from my friend list - /// - - public sealed class DelBuddyRequest : RequestBase - { - //\delbuddy\\sesskey\<>\delprofileid\<>\final\ - /// - /// The target friendId needs to delete - /// - /// - public int TargetId { get; private set; } - public DelBuddyRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - - if (!RequestKeyValues.ContainsKey("delprofileid")) - { - throw new GPParseException("delprofileid is missing."); - } - - int deleteProfileID; - if (!int.TryParse(RequestKeyValues["delprofileid"], out deleteProfileID)) - { - throw new GPParseException("delprofileid format is incorrect."); - } - - TargetId = deleteProfileID; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/InviteToRequest.cs b/src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/InviteToRequest.cs deleted file mode 100755 index 22423675a..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/InviteToRequest.cs +++ /dev/null @@ -1,49 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Request -{ - /// - /// Invite a user to a room or a game - /// - - public sealed class InviteToRequest : RequestBase - { - public int ProductId { get; private set; } - public int ProfileId { get; private set; } - public InviteToRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - - if (!RequestKeyValues.ContainsKey("productid")) - { - throw new GPParseException("productid is missing."); - } - - if (!RequestKeyValues.ContainsKey("sesskey")) - { - throw new GPParseException("sesskey is missing."); - - } - - int productID; - if (!int.TryParse(RequestKeyValues["productid"], out productID)) - { - throw new GPParseException("productid format is incorrect."); - } - - ProductId = productID; - - int profileID; - if (!int.TryParse(RequestKeyValues["profileid"], out profileID)) - { - throw new GPParseException("profileid format is incorrect."); - } - ProfileId = profileID; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/StatusInfoRequest.cs b/src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/StatusInfoRequest.cs deleted file mode 100755 index d0ec395ae..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/StatusInfoRequest.cs +++ /dev/null @@ -1,79 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Aggregate.Misc; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Request -{ - /// - /// Update a user's status information - /// - - public sealed class StatusInfoRequest : RequestBase - { - public bool IsGetStatusInfo { get; set; } - - #region Get buddy status info - public int ProfileId { get; set; } - public int? NamespaceID { get; set; } - #endregion - public UserStatusInfo StatusInfo { get; private set; } - - public StatusInfoRequest() - { - IsGetStatusInfo = true; - } - - public StatusInfoRequest(string rawRequest) : base(rawRequest) - { - IsGetStatusInfo = false; - } - - public override void Parse() - { - base.Parse(); - - if (!RequestKeyValues.ContainsKey("state") - || !RequestKeyValues.ContainsKey("hostIp") - || !RequestKeyValues.ContainsKey("hprivIp") - || !RequestKeyValues.ContainsKey("qport") - || !RequestKeyValues.ContainsKey("hport") - || !RequestKeyValues.ContainsKey("sessflags") - || !RequestKeyValues.ContainsKey("rechStatus") - || !RequestKeyValues.ContainsKey("gameType") - || !RequestKeyValues.ContainsKey("gameVariant") - || !RequestKeyValues.ContainsKey("gameMapName")) - { - throw new GPParseException("StatusInfo request is invalid."); - } - - StatusInfo.StatusState = RequestKeyValues["state"]; - StatusInfo.HostIP = RequestKeyValues["hostIp"]; - StatusInfo.HostPrivateIP = RequestKeyValues["hprivIp"]; - - int qport; - if (!int.TryParse(RequestKeyValues["qport"], out qport)) - { - throw new GPParseException("qport format is incorrect."); - } - StatusInfo.QueryReportPort = qport; - int hport; - if (int.TryParse(RequestKeyValues["hport"], out hport)) - { - throw new GPParseException("hport format is incorrect."); - } - StatusInfo.HostPort = hport; - - int sessflags; - if (!int.TryParse(RequestKeyValues["sessflags"], out sessflags)) - { - throw new GPParseException("sessflags format is incorrect."); - } - StatusInfo.SessionFlags = sessflags; - - StatusInfo.RichStatus = RequestKeyValues["rechStatus"]; - StatusInfo.GameType = RequestKeyValues["gameType"]; - StatusInfo.GameVariant = RequestKeyValues["gameVariant"]; - StatusInfo.GameMapName = RequestKeyValues["gameMapName"]; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/StatusRequest.cs b/src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/StatusRequest.cs deleted file mode 100755 index ec0206b40..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Request/Buddy/StatusRequest.cs +++ /dev/null @@ -1,46 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Aggregate.Misc; -using UniSpy.Server.PresenceConnectionManager.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Request -{ - /// - /// Update a user's status information - /// - - public sealed class StatusRequest : RequestBase - { - public UserStatus Status { get; private set; } - public bool IsGetStatus { get; set; } - public StatusRequest(string rawRequest) : base(rawRequest) - { - Status = new UserStatus(); - IsGetStatus = false; - } - - public override void Parse() - { - base.Parse(); - - if (!RequestKeyValues.ContainsKey("status")) - throw new GPParseException("status is missing."); - - if (!RequestKeyValues.ContainsKey("statstring")) - throw new GPParseException("statstring is missing."); - - if (!RequestKeyValues.ContainsKey("locstring")) - throw new GPParseException("locstring is missing."); - - int statusCode; - if (!int.TryParse(RequestKeyValues["status"], out statusCode)) - { - throw new GPParseException("status format is incorrect."); - } - - Status.CurrentStatus = (GPStatusCode)statusCode; - Status.LocationString = RequestKeyValues["locstring"]; - Status.StatusString = RequestKeyValues["statstring"]; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Request/General/KeepAliveRequest.cs b/src/Servers/PresenceConnectionManager/src/Contract/Request/General/KeepAliveRequest.cs deleted file mode 100755 index 31c03bdca..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Request/General/KeepAliveRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Request -{ - - public sealed class KeepAliveRequest : RequestBase - { - public KeepAliveRequest(string rawRequest) : base(rawRequest) - { - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Request/General/LoginRequest.cs b/src/Servers/PresenceConnectionManager/src/Contract/Request/General/LoginRequest.cs deleted file mode 100755 index 9e4d49d08..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Request/General/LoginRequest.cs +++ /dev/null @@ -1,184 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Request -{ - - public sealed class LoginRequest : RequestBase - { - public string UserChallenge { get; private set; } - public string Response { get; private set; } - public string UniqueNick { get; private set; } - public string UserData { get; private set; } - public int? NamespaceID { get; private set; } - public string AuthToken { get; private set; } - public string Nick { get; private set; } - public string Email { get; private set; } - public int? ProductID { get; private set; } - public LoginType? Type { get; private set; } - public SdkRevisionType? SdkRevisionType { get; private set; } - - public LoginRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - - - if (!RequestKeyValues.ContainsKey("challenge")) - throw new GPParseException("challenge is missing"); - - if (!RequestKeyValues.ContainsKey("response")) - { - throw new GPParseException("response is missing"); - } - - UserChallenge = RequestKeyValues["challenge"]; - Response = RequestKeyValues["response"]; - - if (RequestKeyValues.ContainsKey("uniquenick") && RequestKeyValues.ContainsKey("namespaceid")) - { - int namespaceID; - if (!int.TryParse(RequestKeyValues["namespaceid"], out namespaceID)) - { - throw new GPParseException("namespaceid format is incorrect"); - } - Type = LoginType.UniquenickNamespaceID; - UniqueNick = RequestKeyValues["uniquenick"]; - UserData = UniqueNick; - NamespaceID = namespaceID; - } - else if (RequestKeyValues.ContainsKey("authtoken")) - { - Type = LoginType.AuthToken; - AuthToken = RequestKeyValues["authtoken"]; - UserData = AuthToken; - } - else if (RequestKeyValues.ContainsKey("user")) - { - Type = LoginType.NickEmail; - UserData = RequestKeyValues["user"]; - - int Pos = UserData.IndexOf('@'); - if (Pos == -1 || Pos < 1 || (Pos + 1) >= UserData.Length) - { - throw new GPParseException("user format is incorrect"); - } - Nick = UserData.Substring(0, Pos); - Email = UserData.Substring(Pos + 1); - - // we need to get namespaceid for email login - if (RequestKeyValues.ContainsKey("namespaceid")) - { - int namespaceID; - if (!int.TryParse(RequestKeyValues["namespaceid"], out namespaceID)) - { - throw new GPParseException("namespaceid format is incorrect"); - } - NamespaceID = namespaceID; - } - } - else - { - throw new GPParseException("Unknown login method detected."); - } - - ParseOtherData(); - } - - public int? GamePort { get; private set; } - public int? UserID { get; private set; } - public int? ProfileId { get; private set; } - public int? PartnerID { get; private set; } - public string GameName { get; private set; } - public QuietModeType? QuietModeFlags { get; private set; } - public string Firewall { get; private set; } - - private void ParseOtherData() - { - if (RequestKeyValues.ContainsKey("userid")) - { - int userID; - if (!int.TryParse(RequestKeyValues["userid"], out userID)) - { - throw new GPParseException("partnerid format is incorrect"); - } - UserID = userID; - - } - if (RequestKeyValues.ContainsKey("profileid")) - { - int profileID; - if (!int.TryParse(RequestKeyValues["profileid"], out profileID)) - { - throw new GPParseException("profileid format is incorrect"); - } - ProfileId = profileID; - } - if (RequestKeyValues.ContainsKey("partnerid")) - { - int partnerID; - if (!int.TryParse(RequestKeyValues["partnerid"], out partnerID)) - { - throw new GPParseException("partnerid format is incorrect"); - } - PartnerID = partnerID; - } - - //store sdkrevision - if (RequestKeyValues.ContainsKey("sdkrevision")) - { - int sdkRevisionType; - if (!int.TryParse(RequestKeyValues["sdkrevision"], out sdkRevisionType)) - { - throw new GPParseException("sdkrevision format is incorrect"); - } - - SdkRevisionType = (SdkRevisionType)sdkRevisionType; - } - - if (RequestKeyValues.ContainsKey("gamename")) - { - GameName = RequestKeyValues["gamename"]; - } - - if (RequestKeyValues.ContainsKey("port")) - { - int htonGamePort; - if (!int.TryParse(RequestKeyValues["port"], out htonGamePort)) - { - throw new GPParseException("port format is incorrect"); - } - GamePort = htonGamePort; - } - if (RequestKeyValues.ContainsKey("productid")) - { - int productID; - if (!int.TryParse(RequestKeyValues["productid"], out productID)) - { - throw new GPParseException("productid format is incorrect"); - } - ProductID = productID; - } - - if (RequestKeyValues.ContainsKey("firewall")) - { - Firewall = RequestKeyValues["firewall"]; - } - - if (RequestKeyValues.ContainsKey("quiet")) - { - int quiet; - if (!int.TryParse(RequestKeyValues["quiet"], out quiet)) - { - throw new GPParseException("quiet format is incorrect"); - } - - QuietModeFlags = (QuietModeType)quiet; - } - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Request/General/LogoutRequest.cs b/src/Servers/PresenceConnectionManager/src/Contract/Request/General/LogoutRequest.cs deleted file mode 100755 index 8e36d9f0d..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Request/General/LogoutRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Request -{ - - public sealed class LogoutRequest : RequestBase - { - public LogoutRequest(string rawRequest) : base(rawRequest) - { - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/AddBlockRequest.cs b/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/AddBlockRequest.cs deleted file mode 100755 index 07d187cba..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/AddBlockRequest.cs +++ /dev/null @@ -1,34 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Request -{ - - public sealed class AddBlockRequest : RequestBase - { - public int TargetId; - public AddBlockRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - - if (!RequestKeyValues.ContainsKey("profileid")) - { - throw new GPParseException("profileid is missing"); - - } - - int profileID; - if (!int.TryParse(RequestKeyValues["profileid"], out profileID)) - { - throw new GPParseException("profileid format is incorrect"); - } - - TargetId = profileID; - - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/GetProfileRequest.cs b/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/GetProfileRequest.cs deleted file mode 100755 index 375dfb001..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/GetProfileRequest.cs +++ /dev/null @@ -1,38 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Request -{ - - public sealed class GetProfileRequest : RequestBase - { - public int ProfileId { get; private set; } - public string SessionKey { get; private set; } - public GetProfileRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - - if (!RequestKeyValues.ContainsKey("profileid")) - { - throw new GPParseException("profileid is missing"); - } - - int profileID; - if (!int.TryParse(RequestKeyValues["profileid"], out profileID)) - { - throw new GPParseException("profileid format is incorrect"); - } - ProfileId = profileID; - - if (!RequestKeyValues.ContainsKey("sesskey")) - { - throw new GPParseException("sesskey is missing"); - } - SessionKey = RequestKeyValues["sesskey"]; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/NewProfileRequest.cs b/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/NewProfileRequest.cs deleted file mode 100755 index f8dd8ba58..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/NewProfileRequest.cs +++ /dev/null @@ -1,48 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Request -{ - - public sealed class NewProfileRequest : RequestBase - { - public NewProfileRequest(string rawRequest) : base(rawRequest) - { - } - - public bool IsReplaceNickName { get; private set; } - public string SessionKey { get; private set; } - public string NewNick { get; private set; } - public string OldNick { get; private set; } - public override void Parse() - { - base.Parse(); - - if (!RequestKeyValues.ContainsKey("sesskey")) - { - throw new GPParseException("sesskey is missing"); - } - SessionKey = RequestKeyValues["sesskey"]; - - if (RequestKeyValues.ContainsKey("replace")) - { - if (!RequestKeyValues.ContainsKey("oldnick") && !RequestKeyValues.ContainsKey("nick")) - { - throw new GPParseException("oldnick or nick is missing."); - } - OldNick = RequestKeyValues["oldnick"]; - NewNick = RequestKeyValues["nick"]; - IsReplaceNickName = true; - } - else - { - if (!RequestKeyValues.ContainsKey("nick")) - { - throw new GPParseException("nick is missing."); - } - NewNick = RequestKeyValues["nick"]; - IsReplaceNickName = false; - } - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/RegisterCDKeyRequest.cs b/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/RegisterCDKeyRequest.cs deleted file mode 100755 index 188b23d31..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/RegisterCDKeyRequest.cs +++ /dev/null @@ -1,32 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Request -{ - - public sealed class RegisterCDKeyRequest : RequestBase - { - public string SessionKey { get; private set; } - public string CDKeyEnc { get; private set; } - public RegisterCDKeyRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - - if (!RequestKeyValues.ContainsKey("sesskey")) - { - throw new GPParseException("sesskey is missing"); - } - SessionKey = RequestKeyValues["sesskey"]; - - if (!RequestKeyValues.ContainsKey("cdkeyenc")) - { - throw new GPParseException("cdkeyenc is missing"); - } - CDKeyEnc = RequestKeyValues["cdkeyenc"]; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/RegisterNickRequest.cs b/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/RegisterNickRequest.cs deleted file mode 100755 index 6539e8776..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/RegisterNickRequest.cs +++ /dev/null @@ -1,44 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Request -{ - - public sealed class RegisterNickRequest : RequestBase - { - public string UniqueNick { get; private set; } - public string SessionKey { get; private set; } - public int PartnerId { get; private set; } - public RegisterNickRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - - if (!RequestKeyValues.ContainsKey("sesskey")) - { - throw new GPParseException("sesskey is missing"); - } - SessionKey = RequestKeyValues["sesskey"]; - - if (!RequestKeyValues.ContainsKey("uniquenick")) - { - throw new GPParseException("uniquenick is missing"); - } - UniqueNick = RequestKeyValues["uniquenick"]; - - // PartnerId is optional - if (RequestKeyValues.ContainsKey("partnerid")) - { - int partnerID; - if (!int.TryParse(RequestKeyValues["partnerid"], out partnerID)) - { - throw new GPParseException("partnerid is missing"); - } - PartnerId = partnerID; - } - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/UpdateProfileRequest.cs b/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/UpdateProfileRequest.cs deleted file mode 100755 index d2caa7215..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/UpdateProfileRequest.cs +++ /dev/null @@ -1,166 +0,0 @@ -using System; -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.Core.Misc; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Request -{ - - public sealed class UpdateProfileRequest : RequestBase - { - public UpdateProfileRequest(string rawRequest) : base(rawRequest) - { - } - - public bool HasPublicMaskFlag { get; private set; } - public PublicMasks PublicMask { get; private set; } - - public string SessionKey { get; private set; } - public int PartnerID { get; private set; } - public string Nick { get; private set; } - public string Uniquenick { get; private set; } - - public bool HasFirstNameFlag { get; private set; } - public string FirstName { get; private set; } - public bool HasLastNameFlag { get; private set; } - public string LastName { get; private set; } - - public bool HasICQFlag { get; private set; } - public int ICQUIN { get; private set; } - - public bool HasHomePageFlag { get; private set; } - public string HomePage { get; private set; } - - public bool HasBirthdayFlag { get; private set; } - public int BirthDay { get; private set; } - public ushort BirthMonth { get; private set; } - public ushort BirthYear { get; private set; } - - public bool HasSexFlag { get; private set; } - public byte Sex { get; private set; } - - public bool HasZipCode { get; private set; } - public string ZipCode { get; private set; } - - public bool HasCountryCode { get; private set; } - public string CountryCode { get; private set; } - - public override void Parse() - { - base.Parse(); - - if (RequestKeyValues.ContainsKey("publicmask")) - { - PublicMasks mask; - if (!Enum.TryParse(RequestKeyValues["publicmask"], out mask)) - { - throw new GPParseException("publicmask format is incorrect"); - } - HasPublicMaskFlag = true; - PublicMask = mask; - } - - if (!RequestKeyValues.ContainsKey("sesskey")) - { - throw new GPParseException("sesskey is missing"); - } - SessionKey = RequestKeyValues["sesskey"]; - - if (RequestKeyValues.ContainsKey("firstname")) - { - FirstName = RequestKeyValues["firstname"]; - HasFirstNameFlag = true; - } - - if (RequestKeyValues.ContainsKey("lastname")) - { - LastName = RequestKeyValues["lastname"]; - HasLastNameFlag = true; - } - - if (RequestKeyValues.ContainsKey("icquin")) - { - int icq; - if (!int.TryParse(RequestKeyValues["icquin"], out icq)) - { - throw new GPParseException("icquin format is incorrect"); - } - HasICQFlag = true; - ICQUIN = icq; - } - - - if (RequestKeyValues.ContainsKey("homepage")) - { - HasHomePageFlag = true; - HomePage = RequestKeyValues["homepage"]; - } - - if (RequestKeyValues.ContainsKey("birthday")) - { - int date; - - if (int.TryParse(RequestKeyValues["birthday"], out date)) - { - int d = ((date >> 24) & 0xFF); - ushort m = (ushort)((date >> 16) & 0xFF); - ushort y = (ushort)(date & 0xFFFF); - - if (GameSpyUtils.IsValidDate(d, m, y)) - { - BirthDay = d; - BirthMonth = m; - BirthYear = y; - } - } - } - if (RequestKeyValues.ContainsKey("sex")) - { - byte sex; - - if (!byte.TryParse(RequestKeyValues["sex"], out sex)) - { - throw new GPParseException("sex format is incorrect"); - } - HasSexFlag = true; - Sex = sex; - } - - if (RequestKeyValues.ContainsKey("zipcode")) - { - HasZipCode = true; - ZipCode = RequestKeyValues["zipcode"]; - } - - if (RequestKeyValues.ContainsKey("countrycode")) - { - HasCountryCode = true; - CountryCode = RequestKeyValues["countrycode"]; - } - - if (RequestKeyValues.ContainsKey("partnerid")) - { - int partnerid; - if (!int.TryParse(RequestKeyValues["partnerid"], out partnerid)) - { - throw new GPParseException("partnerid is incorrect"); - } - PartnerID = partnerid; - } - - if (RequestKeyValues.ContainsKey("nick")) - { - // throw new GPParseException("nickname is missing."); - Nick = RequestKeyValues["nick"]; - } - - if (RequestKeyValues.ContainsKey("uniquenick")) - { - // throw new GPParseException("uniquenick is missing."); - Uniquenick = RequestKeyValues["uniquenick"]; - } - } - } -} - diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/UpdateUIRequest.cs b/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/UpdateUIRequest.cs deleted file mode 100755 index 72b62160e..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Request/Profile/UpdateUIRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Request -{ - - public sealed class UpdateUiRequest : RequestBase - { - public UpdateUiRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - - if (RequestKeyValues.ContainsKey("")) - { - - } - //cpubrandid - //cpuspeed - //memory - //videocard1ram - //videocard2ram - //connectionid - //connectionspeed - //hasnetwork - //pic - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Response/Buddy/AddBuddyResponse.cs b/src/Servers/PresenceConnectionManager/src/Contract/Response/Buddy/AddBuddyResponse.cs deleted file mode 100755 index 00eba9d24..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Response/Buddy/AddBuddyResponse.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using UniSpy.Server.PresenceConnectionManager.Contract.Result; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Response -{ - public sealed class AddBuddyResponse : ResponseBase - { - private new AddBuddyRequest _request => (AddBuddyRequest)base._request; - private new AddBuddyResult _result => (AddBuddyResult)base._result; - public AddBuddyResponse(AddBuddyRequest request, AddBuddyResult result) : base(request, result) - { - } - - public override void Build() - { - // \bm\\f\\date\ - // GPI_BM_MESSAGE: \msg\\ - // GPI_BM_UTM:\msg\\ - // GPI_BM_REQUEST:\msg\|signed|\ - // GPI_BM_AUTH: - // GPI_BM_REVOKE: - // GPI_BM_STATUS:\msg\|s|\ or \msg\|ss||ls||ip||p||qm| - // GPI_BM_INVITE:\msg\|p||l| - // GPI_BM_PING:\msg\\ - throw new NotImplementedException(); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Response/Buddy/BlockListResponse.cs b/src/Servers/PresenceConnectionManager/src/Contract/Response/Buddy/BlockListResponse.cs deleted file mode 100755 index 2f1812175..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Response/Buddy/BlockListResponse.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Linq; -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Contract.Result; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Response -{ - public sealed class BlockListResponse : ResponseBase - { - private new BlockListResult _result => (BlockListResult)base._result; - - public BlockListResponse(RequestBase request, BlockListResult result) : base(request, result) - { - } - - public override void Build() - { - // \blk\< num in list >\list\< profileid list - comma delimited >\final\ - SendingBuffer = $@"\blk\{_result.ProfileIdList.Count()}\list\"; - foreach (var pid in _result.ProfileIdList) - { - SendingBuffer += $@"{pid}"; - if (pid != _result.ProfileIdList.Last()) - { - SendingBuffer += ","; - } - } - SendingBuffer += @"\final\"; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Response/Buddy/BuddyListResponse.cs b/src/Servers/PresenceConnectionManager/src/Contract/Response/Buddy/BuddyListResponse.cs deleted file mode 100755 index e71fcc570..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Response/Buddy/BuddyListResponse.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Linq; -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Contract.Result; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Response -{ - public sealed class BuddyListResponse : ResponseBase - { - private new BuddyListResult _result => (BuddyListResult)base._result; - public BuddyListResponse(RequestBase request, BuddyListResult result) : base(request, result) - { - } - public override void Build() - { - // \bdy\< num in list >\list\< profileid list - comma delimited >\final\ - SendingBuffer = $@"\bdy\{_result.ProfileIDList.Count()}\list\"; - foreach (var pid in _result.ProfileIDList) - { - SendingBuffer += $@"{pid}"; - if (pid != _result.ProfileIDList.Last()) - { - SendingBuffer += ","; - } - } - SendingBuffer += @"\final\"; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Response/Buddy/StatusInfoResponse.cs b/src/Servers/PresenceConnectionManager/src/Contract/Response/Buddy/StatusInfoResponse.cs deleted file mode 100755 index 0343e26fb..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Response/Buddy/StatusInfoResponse.cs +++ /dev/null @@ -1,29 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Contract.Result; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Response -{ - public sealed class StatusInfoResponse : ResponseBase - { - private new StatusInfoResult _result => (StatusInfoResult)base._result; - - public StatusInfoResponse(RequestBase request, StatusInfoResult result) : base(request, result) - { - } - /// - /// \bsi\\state\\profile\\bip\\bport\\hostip\\hprivip\\qport\\hport\\sessflags\\rstatus\\ - /// gameType\\gameVnt\\gameMn\\product\\qmodeflags\ - /// - public override void Build() - { - SendingBuffer = $@"bsi\\state\{_result.StatusInfo.StatusState}\ - profile\{_result.ProfileId}\bip\{_result.StatusInfo.BuddyIP} - hostIp\{_result.StatusInfo}\ - hprivIp\{_result.StatusInfo.HostPrivateIP}\qport\{_result.StatusInfo.QueryReportPort}\ - hport\{_result.StatusInfo.HostPort}\sessflags\{_result.StatusInfo.SessionFlags}\ - rstatus\{_result.StatusInfo.RichStatus}\gameType\{_result.StatusInfo.GameType}\ - gameVnt\{_result.StatusInfo.GameVariant}\gameMn\{_result.StatusInfo.GameMapName}\ - product\{_result.ProductId}\qmodeflags\{_result.StatusInfo.QuietModeFlags}final\"; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Response/General/KeepAliveResponse.cs b/src/Servers/PresenceConnectionManager/src/Contract/Response/General/KeepAliveResponse.cs deleted file mode 100755 index f711576de..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Response/General/KeepAliveResponse.cs +++ /dev/null @@ -1,16 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Response -{ - public sealed class KeepAliveResponse : ResponseBase - { - public KeepAliveResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - - public override void Build() - { - SendingBuffer = @"\ka\\final\"; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Response/General/LoginResponse.cs b/src/Servers/PresenceConnectionManager/src/Contract/Response/General/LoginResponse.cs deleted file mode 100755 index 7605b85cb..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Response/General/LoginResponse.cs +++ /dev/null @@ -1,36 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceConnectionManager.Enumerate; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using UniSpy.Server.PresenceConnectionManager.Contract.Result; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Response -{ - public sealed class LoginResponse : ResponseBase - { - private new LoginRequest _request => (LoginRequest)base._request; - private new LoginResult _result => (LoginResult)base._result; - public LoginResponse(LoginRequest request, LoginResult result) : base(request, result) - { - } - - public override void Build() - { - //string checkSumStr = _result.DatabaseResults.Nick + _result.DatabaseResults.UniqueNick + _result.DatabaseResults.NamespaceID; - - //_connection.UserData.SessionKey = _crc.ComputeChecksum(checkSumStr); - - SendingBuffer = @"\lc\2\sesskey\" + ClientInfo.SessionKey; - SendingBuffer += @"\proof\" + _result.ResponseProof; - SendingBuffer += @"\userid\" + _result.DatabaseResults.UserId; - SendingBuffer += @"\profileid\" + _result.DatabaseResults.ProfileId; - - if (_result.DatabaseResults.UniqueNick is not null) - { - SendingBuffer += @"\uniquenick\" + _result.DatabaseResults.UniqueNick; - } - SendingBuffer += $@"\lt\{ClientInfo.LoginTicket}"; - SendingBuffer += $@"\id\{_request.OperationID}\final\"; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Response/General/NewUserResponse.cs b/src/Servers/PresenceConnectionManager/src/Contract/Response/General/NewUserResponse.cs deleted file mode 100755 index 16934744f..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Response/General/NewUserResponse.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Response -{ - public sealed class NewUserResponse : PresenceSearchPlayer.Contract.Response.NewUserResponse - { - public NewUserResponse(NewUserRequest request, NewUserResult result) : base(request, result) - { - } - - public override void Build() - { - SendingBuffer = $@"\nur\\userid\{_result.User.Userid}\profileid\{_result.SubProfile.Profileid}\id\{_request.OperationID}\final\"; - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Response/Profile/GetProfileResponse.cs b/src/Servers/PresenceConnectionManager/src/Contract/Response/Profile/GetProfileResponse.cs deleted file mode 100755 index 22165e43f..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Response/Profile/GetProfileResponse.cs +++ /dev/null @@ -1,50 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Contract.Result; -using UniSpy.Server.Core.Misc; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Response -{ - public sealed class GetProfileResponse : ResponseBase - { - private new GetProfileResult _result => (GetProfileResult)base._result; - public GetProfileResponse(GetProfileRequest request, GetProfileResult result) : base(request, result) - { - } - public override void Build() - { - SendingBuffer = @"\pi\\profileid\" + _result.UserProfile.ProfileId; - SendingBuffer += @"\nick\" + _result.UserProfile.Nick; - SendingBuffer += @"\uniquenick\" + _result.UserProfile.UniqueNick; - SendingBuffer += @"\email\" + _result.UserProfile.Email; - SendingBuffer += @"\firstname\" + _result.UserProfile.Firstname; - SendingBuffer += @"\lastname\" + _result.UserProfile.Lastname; - SendingBuffer += @"\icquin\" + _result.UserProfile.Icquin; - SendingBuffer += @"\homepage\" + _result.UserProfile.Homepage; - SendingBuffer += @"\zipcode\" + _result.UserProfile.Zipcode; - SendingBuffer += @"\countrycode\" + _result.UserProfile.Countrycode; - SendingBuffer += @"\lon\" + _result.UserProfile.Longitude; - SendingBuffer += @"\lat\" + _result.UserProfile.Longitude; - SendingBuffer += @"\loc\" + _result.UserProfile.Location; - - int birthStr = (int)_result.UserProfile.Birthday << 24 | (int)_result.UserProfile.Birthmonth << 16 | (int)_result.UserProfile.Birthyear; - - SendingBuffer += @"\birthday\" + birthStr; - SendingBuffer += @"\sex\" + _result.UserProfile.Sex; - SendingBuffer += @"\publicmask\" + _result.UserProfile.Publicmask; - SendingBuffer += @"\aim\" + _result.UserProfile.Aim; - SendingBuffer += @"\picture\" + _result.UserProfile.Picture; - SendingBuffer += @"\ooc" + _result.UserProfile.Occupationid; - SendingBuffer += @"\ind\" + _result.UserProfile.Industryid; - SendingBuffer += @"\inc\" + _result.UserProfile.Incomeid; - SendingBuffer += @"\mar\" + _result.UserProfile.Marriedid; - SendingBuffer += @"\chc\" + _result.UserProfile.Childcount; - SendingBuffer += @"\i1\" + _result.UserProfile.Interests1; - SendingBuffer += @"\o1\" + _result.UserProfile.Ownership1; - SendingBuffer += @"\conn\" + _result.UserProfile.Connectiontype; - // SUPER NOTE: Please check the Signature of the PID, otherwise when it will be compared with other peers, it will break everything (See gpiPeer.c @ peerSig) - SendingBuffer += @"\sig\+" + GameSpyRandom.GenerateRandomString(10, GameSpyRandom.StringType.Hex); - SendingBuffer += @"\id\" + _request.OperationID + @"\final\"; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Response/Profile/NewProfileResponse.cs b/src/Servers/PresenceConnectionManager/src/Contract/Response/Profile/NewProfileResponse.cs deleted file mode 100755 index 38dd67128..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Response/Profile/NewProfileResponse.cs +++ /dev/null @@ -1,16 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Response -{ - public sealed class NewProfileResponse : ResponseBase - { - public NewProfileResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - - public override void Build() - { - SendingBuffer = $@"\npr\\profileid\{SendingBuffer}\id\{_request.OperationID}\final\"; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Response/Profile/RegisterCDKeyResponse.cs b/src/Servers/PresenceConnectionManager/src/Contract/Response/Profile/RegisterCDKeyResponse.cs deleted file mode 100755 index 74865f934..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Response/Profile/RegisterCDKeyResponse.cs +++ /dev/null @@ -1,16 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Response -{ - public sealed class RegisterCDKeyResponse : ResponseBase - { - public RegisterCDKeyResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - - public override void Build() - { - SendingBuffer = @"\rc\\final\"; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Response/Profile/RegisterNickResponse.cs b/src/Servers/PresenceConnectionManager/src/Contract/Response/Profile/RegisterNickResponse.cs deleted file mode 100755 index b4de59ba4..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Response/Profile/RegisterNickResponse.cs +++ /dev/null @@ -1,16 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Response -{ - public sealed class RegisterNickResponse : ResponseBase - { - public RegisterNickResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - - public override void Build() - { - SendingBuffer = $@"\rn\\id\{_request.OperationID}\final\"; - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/AddBuddyResult.cs b/src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/AddBuddyResult.cs deleted file mode 100755 index 98be80050..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/AddBuddyResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Result -{ - public sealed class AddBuddyResult : ResultBase - { - public AddBuddyResult() - { - } - - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/BlockListResult.cs b/src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/BlockListResult.cs deleted file mode 100755 index 0cc395c6b..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/BlockListResult.cs +++ /dev/null @@ -1,14 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using System.Collections.Generic; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Result -{ - public sealed class BlockListResult : ResultBase - { - public List ProfileIdList; - - public BlockListResult() - { - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/BuddyListResult.cs b/src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/BuddyListResult.cs deleted file mode 100755 index 47c39a09d..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/BuddyListResult.cs +++ /dev/null @@ -1,14 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using System.Collections.Generic; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Result -{ - public sealed class BuddyListResult : ResultBase - { - public List ProfileIDList { get; set; } - - public BuddyListResult() - { - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/StatusInfoResult.cs b/src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/StatusInfoResult.cs deleted file mode 100755 index 5954dec06..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/StatusInfoResult.cs +++ /dev/null @@ -1,15 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Aggregate.Misc; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Result -{ - public sealed class StatusInfoResult : ResultBase - { - public int ProfileId { get; set; } - public int ProductId { get; set; } - public UserStatusInfo StatusInfo { get; set; } - public StatusInfoResult() - { - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/StatusResult.cs b/src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/StatusResult.cs deleted file mode 100755 index 6c1948087..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Result/Buddy/StatusResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Aggregate.Misc; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Result -{ - public sealed class StatusResult : ResultBase - { - public UserStatus Status { get; set; } - public StatusResult() - { - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Result/General/LoginResult.cs b/src/Servers/PresenceConnectionManager/src/Contract/Result/General/LoginResult.cs deleted file mode 100755 index d4a76d6d1..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Result/General/LoginResult.cs +++ /dev/null @@ -1,27 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Result -{ - public sealed class LogInDataModel - { - public int UserId; - public int ProfileId; - public string Nick; - public string Email; - public string UniqueNick; - public string PasswordHash; - public bool EmailVerifiedFlag; - public bool BannedFlag; - public int NamespaceId; - public int SubProfileId; - } - - public sealed class LoginResult : ResultBase - { - public LogInDataModel DatabaseResults { get; set; } - public string ResponseProof { get; set; } - public LoginResult() - { - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Result/PCMDefaultResult.cs b/src/Servers/PresenceConnectionManager/src/Contract/Result/PCMDefaultResult.cs deleted file mode 100755 index 232faaca1..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Result/PCMDefaultResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Result -{ - public sealed class PCMDefaultResult : ResultBase - { - public PCMDefaultResult() - { - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Result/Profile/GetProfileResult.cs b/src/Servers/PresenceConnectionManager/src/Contract/Result/Profile/GetProfileResult.cs deleted file mode 100755 index 49aa382a1..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Result/Profile/GetProfileResult.cs +++ /dev/null @@ -1,44 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Result -{ - public sealed class GetProfileDataModel - { - public string Nick; - public int ProfileId; - public string UniqueNick; - public string Email; - public string Firstname; - public string Lastname; - public int? Icquin; - public string Homepage; - public string Zipcode; - public string Countrycode; - public double? Longitude; - public double? Latitude; - public string Location; - public int? Birthday; - public int? Birthmonth; - public int? Birthyear; - public byte? Sex; - public int? Publicmask; - public string Aim; - public int? Picture; - public int? Occupationid; - public int? Industryid; - public int? Incomeid; - public int? Marriedid; - public int? Childcount; - public int? Interests1; - public int? Ownership1; - public int? Connectiontype; - - } - public sealed class GetProfileResult : ResultBase - { - public GetProfileDataModel UserProfile; - public GetProfileResult() - { - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Contract/Result/Profile/NewProfileResult.cs b/src/Servers/PresenceConnectionManager/src/Contract/Result/Profile/NewProfileResult.cs deleted file mode 100755 index 669292841..000000000 --- a/src/Servers/PresenceConnectionManager/src/Contract/Result/Profile/NewProfileResult.cs +++ /dev/null @@ -1,14 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceConnectionManager.Contract.Result -{ - public sealed class NewProfileResult : ResultBase - { - public int ProfileId; - - public NewProfileResult() - { - } - - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Dockerfile b/src/Servers/PresenceConnectionManager/src/Dockerfile deleted file mode 100755 index 1c8a93f5c..000000000 --- a/src/Servers/PresenceConnectionManager/src/Dockerfile +++ /dev/null @@ -1,21 +0,0 @@ -FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base -WORKDIR /app -EXPOSE 29900 - -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build -WORKDIR /src -COPY ["src/Servers/PresenceConnectionManager/src/UniSpy.Server.PresenceConnectionManager.csproj", "src/Servers/PresenceConnectionManager/src/"] -COPY ["src/Libraries/Core/src/UniSpy.Server.Core.csproj", "src/Libraries/Core/src/"] -COPY ["src/Servers/PresenceSearchPlayer/src/UniSpy.Server.PresenceSearchPlayer.csproj", "src/Servers/PresenceSearchPlayer/src/"] -RUN dotnet restore "src/Servers/PresenceConnectionManager/src/UniSpy.Server.PresenceConnectionManager.csproj" -COPY . . -WORKDIR "/src/src/Servers/PresenceConnectionManager/src" -RUN dotnet build "UniSpy.Server.PresenceConnectionManager.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "UniSpy.Server.PresenceConnectionManager.csproj" -c Release -o /app/publish - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "UniSpy.Server.PresenceConnectionManager.dll"] \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/Enumerate/BuddyMessageType.cs b/src/Servers/PresenceConnectionManager/src/Enumerate/BuddyMessageType.cs deleted file mode 100755 index d45d48f9f..000000000 --- a/src/Servers/PresenceConnectionManager/src/Enumerate/BuddyMessageType.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace UniSpy.Server.PresenceConnectionManager.Enumerate -{ - public enum BuddyMessageType - { - #region response - //BM status - BmMessage = 1, - BmRquest = 2, - BmReply = 3, // only used on the backend - BmAuth = 4, - BmUTM = 5, - BmRevoke = 6, // remote buddy removed from local list - BmStatus = 100, - BmInvite = 101, - BmPing = 102, - #endregion - #region request - BmPong = 103, - BmKeysRequest = 104, - BmKeysReply = 105, - BmFileSendRequest = 200, - BmFileSendReply = 201, - BmFileBegin = 202, - BmFileEnd = 203, - BmFileData = 204, - BmFile_SKIP = 205, - BmFileTransferThrottle = 206, - BmFileTransferCancel = 207, - BmFileTransferKeepAlive = 208, - #endregion - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Enumerate/DisconnectReason.cs b/src/Servers/PresenceConnectionManager/src/Enumerate/DisconnectReason.cs deleted file mode 100755 index 57fd1ceb0..000000000 --- a/src/Servers/PresenceConnectionManager/src/Enumerate/DisconnectReason.cs +++ /dev/null @@ -1,91 +0,0 @@ -namespace UniSpy.Server.PresenceConnectionManager.Enumerate -{ - public enum DisconnectReason : int - { - /// - /// Client sends the "logout" command - /// - NormalLogout, - - /// - /// Keep Alive Packet failed to send (may not work with new async socket code) - /// - KeepAliveFailed, - - /// - /// The client failed to complete the login 15 seconds after creating the connection - /// - LoginTimedOut, - - /// - /// The username sent by the client does not exist - /// - InvalidUsername, - - /// - /// The provided password for the player nick is incorrect - /// - InvalidPassword, - - /// - /// Invalid login query sent by client - /// - InvalidLoginQuery, - - /// - /// Create player failed, username exists already - /// - CreateFailedUsernameExists, - - /// - /// Failed to create new player account due to a database exception - /// - CreateFailedDatabaseError, - - /// - /// A general login failure (check error log) - /// - GeneralError, - - /// - /// The stream disconnected unexpectedly. This can happen if the user clicks the - /// "Quit" button on the top of the main menu instead of the "Logout" button. - /// - Disconnected, - - /// - /// The player was forcefully logged out by console command - /// - ForcedLogout, - - /// - /// A new login detected with the old player _connection still logged in - /// - NewLoginDetected, - - /// - /// Forced server shutdown - /// - ForcedServerShutdown, - - /// - /// The client challenge was already sent by the server for this connection - /// - ClientChallengeAlreadySent, - - /// - /// The player is banned and cannot login - /// - PlayerIsBanned, - - /// - /// The player information is not valid - /// - InvalidPlayer, - - /// - /// The player account is not activated - /// - PlayerIsNotActivated - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Enumerate/FireWallType.cs b/src/Servers/PresenceConnectionManager/src/Enumerate/FireWallType.cs deleted file mode 100755 index f62592725..000000000 --- a/src/Servers/PresenceConnectionManager/src/Enumerate/FireWallType.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace UniSpy.Server.PresenceConnectionManager.Enumerate -{ - public enum FireWallType - { - // Firewall - /////////// - Firewall = 1, - NoFirewall = 0, - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Enumerate/GPBasic.cs b/src/Servers/PresenceConnectionManager/src/Enumerate/GPBasic.cs deleted file mode 100755 index 043eeca46..000000000 --- a/src/Servers/PresenceConnectionManager/src/Enumerate/GPBasic.cs +++ /dev/null @@ -1,201 +0,0 @@ -namespace UniSpy.Server.PresenceConnectionManager.Enumerate -{ - public enum GPBasic : uint - { - // Global States. - ///////////////// - InfoCaching = 0x0100, - Simulation, - InfoChachingBuddyAndBlockOnly, - - // Blocking - /////////// - Blocking = 1, - NonBlocking = 0, - - // Firewall - /////////// - Firewall = 1, - NoFirewall = 0, - - // Check Cache - ////////////// - CheckCache = 1, - DontCheckCache = 0, - - // Is Valid Email. - // PANTS|02.15.00 - ////////////////// - EmailValid = 1, - EmailInvalid = 0, - - // Fatal Error. - /////////////// - Fatal = 1, - NonFatal = 0, - - // Sex - ////// - Male = 0x0500, - Fefamle, - PAT, - - // Profile Search. - ////////////////// - More = 0x0600, - Done, - - // Set Info - /////////// - Nick = 0x0700, - Uniquenick, - Email, - Password, - FirstName, - LastName, - ICQUIN, - HomePage, - ZIPCode, - CountryCode, - Birthday, - Sex, - CPUBrand, - CPUSpeed, - Memory, - VideoCard1String, - VideoCard1RAM, - VideoCard2String, - VideoCard2RAM, - ConnectionID, - ConnectionSpeed, - HasNetwork, - OSString, - AIMName, // PANTS|03.20.01 - PIC, - OccupationID, - IndustryID, - InComeID, - MarriedID, - ChildCount, - Interest1, - - // New Profile. - /////////////// - Replace = 1, - DontReplace = 0, - - // Is Connected. - //////////////// - Connected = 1, - NotConnected = 0, - - // Public mask. - /////////////// - MaskNone = 0x00000000, - MaskHomepage = 0x00000001, - MaskZIPCode = 0x00000002, - MaskContryCode = 0x00000004, - MaskBirthday = 0x00000008, - MaskSex = 0x00000010, - MaskEmail = 0x00000020, - MaskAll = 0xFFFFFFFF, - - // Session flags - ///////////////// - SessIsClosed = 0x00000001, - SessIsOpen = 0x00000002, - SessHasPassword = 0x00000004, - SessIsBehindNAT = 0x00000008, - SessIsRanked = 0x000000010, - - // CPU Brand ID - /////////////// - Intel = 1, - AMD, - CYRIX, - Motorola, - Alpha, - - // Connection ID. - ///////////////// - Modem = 1, - ISDN, - CableModem, - DSL, - Satellite, - Ethernet, - Wireless, - - // Transfer callback type. - // *** the transfer is ended when these types are received - ////////////////////////// - TransferSendRequest = 0x800, // arg->num == numFiles - TransferAccepted, - TransferRejected, // *** - TransferNotAccepting, // *** - TransferNoConnection, // *** - TransferDone, // *** - TransferCancelled, // *** - TransferLostConnection, // *** - TransferError, // *** - TransferThrottle, // arg->num == Bps - FileBegin, - FileProgress, // arg->num == numBytes - FileEnd, - FileDirectory, - FileSkip, - FileFaild, // arg->num == error - - //FILE_FAILED error - /////////////////////// - FileReadError = 0x900, - FileWriteError, - FileDataError, - - // Transfer Side. - ///////////////// - TransferSender = 0xA00, - TansferReciever, - - // UTM send options. - //////////////////// - DontRout = 0xB00, // only send direct - - // Quiet mode flags. - //////////////////// - SlienceNone = 0x00000000, - SlienceMessage = 0x00000001, - SlienceUTMS = 0x00000002, - SlienceList = 0x00000004, // includes requests, auths, and revokes - SlienceAll = 0xFFFFFFFF, - - NewStatusInfoSupported = 0xC00, - NewStatusInfoNotSupported = 0xC01, - } - - /// - /// This enum represents the known Partner IDs, This value is set to 0 when - /// a game is directly connecting to GameSpy, otherwise the Partner ID would be - /// different for any service that uses GameSpy as backend (for example Nintendo - /// Wifi Connection). - /// - public enum GPPartnerID : int - { - /// - /// The client is directly connecting to the Server - /// - Gamespy = 0, - - // Unknown Partner ID from 1 to 9 - // EA partner ID should range here, it was - - /// - /// Unknown usage for this partner id, but it exists in the - /// GameSpy SDK - /// - IGN = 10, - //Nintendo = 11, // Please verify this - } - - -} diff --git a/src/Servers/PresenceConnectionManager/src/Enumerate/GPStatusCode.cs b/src/Servers/PresenceConnectionManager/src/Enumerate/GPStatusCode.cs deleted file mode 100755 index ac52e663f..000000000 --- a/src/Servers/PresenceConnectionManager/src/Enumerate/GPStatusCode.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace UniSpy.Server.PresenceConnectionManager.Enumerate -{ - public enum GPStatusCode - { - // Status - ///////// - Offline = 0, - Online = 1, - Playing = 2, - Staging = 3, - Chatting = 4, - Away = 5, - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Enumerate/GenderType.cs b/src/Servers/PresenceConnectionManager/src/Enumerate/GenderType.cs deleted file mode 100755 index 4f727934e..000000000 --- a/src/Servers/PresenceConnectionManager/src/Enumerate/GenderType.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace UniSpy.Server.PresenceConnectionManager.Enumerate -{ - public enum GenderType : ushort - { - /// - /// Gender is male - /// - Male, - - /// - /// Gender is female - /// - Female, - - /// - /// Unspecified or unknown gender, this is - /// used to mask the gender when the information is queried - /// - Pat - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Enumerate/LoginStatus.cs b/src/Servers/PresenceConnectionManager/src/Enumerate/LoginStatus.cs deleted file mode 100755 index 7bfb2a2fe..000000000 --- a/src/Servers/PresenceConnectionManager/src/Enumerate/LoginStatus.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace UniSpy.Server.PresenceConnectionManager.Enumerate -{ - public enum LoginStatus - { - Connected, - Processing, - Completed, - Disconnected - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Enumerate/LoginType.cs b/src/Servers/PresenceConnectionManager/src/Enumerate/LoginType.cs deleted file mode 100755 index aa7eea2da..000000000 --- a/src/Servers/PresenceConnectionManager/src/Enumerate/LoginType.cs +++ /dev/null @@ -1,23 +0,0 @@ -namespace UniSpy.Server.PresenceConnectionManager.Enumerate -{ - /// - /// This enumation defins the supported login method for the users. - /// - public enum LoginType : int - { - /// - /// Login with user combo (nick@email) - /// - NickEmail, - - /// - /// Login with unique nickname - /// - UniquenickNamespaceID, - - /// - /// Pre-authenticated login - /// - AuthToken - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Enumerate/PublicMask.cs b/src/Servers/PresenceConnectionManager/src/Enumerate/PublicMask.cs deleted file mode 100755 index d78513e4e..000000000 --- a/src/Servers/PresenceConnectionManager/src/Enumerate/PublicMask.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace UniSpy.Server.PresenceConnectionManager.Enumerate -{ - /// - /// This enumerator contains the masks used to hide certain informations. - /// - /// The public mask works by ORing this bytes. - /// - /// If the MASK_HOMEPAGE is ORed with MASK_ZIPCODE both the Homepage - /// and the Zipcode will be showed to the user. - /// - public enum PublicMasks : uint - { - /// - /// Show the essential informations for getting the profile info - /// - None = 0x00000000, - - /// - /// Show the user homepage - /// - Homepage = 0x00000001, - - /// - /// Show the ZIP code - /// - ZipCode = 0x00000002, - - /// - /// Show the country code where the player lives - /// - CountryCode = 0x00000004, - - /// - /// Show the birth date - /// - Birthday = 0x00000008, - - /// - /// Show the gender - /// - Sex = 0x00000010, - - /// - /// Show the Email - /// - Email = 0x00000020, - - /// - /// Show all the informations - /// - All = 0xFFFFFFFF, - }; -} diff --git a/src/Servers/PresenceConnectionManager/src/Enumerate/QuietModeType.cs b/src/Servers/PresenceConnectionManager/src/Enumerate/QuietModeType.cs deleted file mode 100755 index 0809bf11d..000000000 --- a/src/Servers/PresenceConnectionManager/src/Enumerate/QuietModeType.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace UniSpy.Server.PresenceConnectionManager.Enumerate -{ - public enum QuietModeType : uint - { - // Quiet mode flags. - //////////////////// - SlienceNone = 0x00000000, - SlienceMessage = 0x00000001, - SlienceUTMS = 0x00000002, - SlienceList = 0x00000004, // includes requests, auths, and revokes - SlienceAll = 0xFFFFFFFF, - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Enumerate/SDKRevisionType.cs b/src/Servers/PresenceConnectionManager/src/Enumerate/SDKRevisionType.cs deleted file mode 100755 index a22c658ce..000000000 --- a/src/Servers/PresenceConnectionManager/src/Enumerate/SDKRevisionType.cs +++ /dev/null @@ -1,33 +0,0 @@ - -namespace UniSpy.Server.PresenceConnectionManager.Enumerate -{ - public enum SdkRevisionType : int - { - Unknown = 0, - /// - /// Extended message support - /// - GPINewAuthNotification = 1,//1 - /// - /// Remove friend from remote - /// - GPINewRevokeNotification = 1 << 2,//10 - /// - /// New Status Info support - /// - GPINewStatusNotification = 1 << 3,//1000 - /// - /// Buddy List + Block List retrieval on login - /// - GPINewListRetrevalOnLogin = 1 << 4,// - /// - /// Remote Auth logins now return namespaceid/partnerid on login - /// - GPIRemoteAuthIDSNotification = 1 << 5, - /// - /// New CD Key registration style as opposed to using product ids - /// - GPINewCDKeyRegistration = 1 << 6, - - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/AddBuddyHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/AddBuddyHandler.cs deleted file mode 100755 index 892e38beb..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/AddBuddyHandler.cs +++ /dev/null @@ -1,28 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using UniSpy.Server.PresenceConnectionManager.Contract.Result; -using UniSpy.Server.PresenceConnectionManager.Application; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Buddy -{ - //\addbuddy\\sesskey\<>\newprofileid\<>\reason\<>\final\ - - public sealed class AddBuddyHandler : LoggedInCmdHandlerBase - { - private new AddBuddyRequest _request => (AddBuddyRequest)base._request; - - public AddBuddyHandler(Client client, AddBuddyRequest request) : base(client, request) - { - _result = new AddBuddyResult(); - } - - protected override void DataOperation() - { - throw new System.NotImplementedException(); - //Check if the friend is online - //if(online) - //else - //store add request to database - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BlockListHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BlockListHandler.cs deleted file mode 100755 index 7f772baec..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BlockListHandler.cs +++ /dev/null @@ -1,30 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceConnectionManager.Contract.Response; -using UniSpy.Server.PresenceConnectionManager.Contract.Result; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Buddy -{ - public sealed class BlockListHandler : LoggedInCmdHandlerBase - { - private new BlockListResult _result { get => (BlockListResult)base._result; set => base._result = value; } - - public BlockListHandler(IClient client) : base(client, null) - { - _result = new BlockListResult(); - } - protected override void RequestCheck() { } - protected override void DataOperation() - { - - _result.ProfileIdList = StorageOperation.Persistance.GetBlockedProfileIds(_client.Info.ProfileInfo.Profileid, - _client.Info.SubProfileInfo.Namespaceid); - } - - protected override void ResponseConstruct() - { - _response = new BlockListResponse(null, _result); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BuddyListHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BuddyListHandler.cs deleted file mode 100755 index 2cf3d87dc..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BuddyListHandler.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System.Threading.Tasks; -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using UniSpy.Server.PresenceConnectionManager.Contract.Response; -using UniSpy.Server.PresenceConnectionManager.Contract.Result; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Buddy -{ - public sealed class BuddyListHandler : LoggedInCmdHandlerBase - { - private new BuddyListResult _result { get => (BuddyListResult)base._result; set => base._result = value; } - public BuddyListHandler(IClient client) : base(client, null) - { - _result = new BuddyListResult(); - } - protected override void RequestCheck() { } - protected override void DataOperation() - { - var friendsId = StorageOperation.Persistance.GetFriendProfileIds(_client.Info.ProfileInfo.Profileid, - _client.Info.SubProfileInfo.Namespaceid); - _result.ProfileIDList = friendsId; - - } - protected override void ResponseConstruct() - { - _response = new BuddyListResponse(_request, _result); - } - protected override void Response() - { - base.Response(); - if (!_client.Info.SdkRevision.IsSupportGPINewStatusNotification) - { - return; - } - - Parallel.ForEach(_result.ProfileIDList, (profileID) => - { - var request = new StatusInfoRequest - { - ProfileId = profileID, - NamespaceID = (int)_client.Info.SubProfileInfo.Namespaceid, - IsGetStatusInfo = true - }; - new StatusInfoHandler(_client, request).Handle(); - }); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BuddyStatusInfoHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BuddyStatusInfoHandler.cs deleted file mode 100755 index df1c6a2af..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/BuddyStatusInfoHandler.cs +++ /dev/null @@ -1,24 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.PresenceConnectionManager.Application; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Buddy -{ - public sealed class BuddyStatusInfoHandler : LoggedInCmdHandlerBase - { - // This is what the message should look like. Its broken up for easy viewing. - // - // "\bsi\\state\\profile\\bip\\bport\\hostip\\hprivip\" - // "\qport\\hport\\sessflags\\rstatus\\gameType\" - // "\gameVnt\\gameMn\\product\\qmodeflags\" - public BuddyStatusInfoHandler(Client client, IRequest request) : base(client, request) - { - throw new System.NotImplementedException(); - } - - protected override void DataOperation() - { - throw new System.NotImplementedException(); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/DelBuddyHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/DelBuddyHandler.cs deleted file mode 100755 index 2d528eb7d..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/DelBuddyHandler.cs +++ /dev/null @@ -1,26 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Buddy -{ - /// - /// handles dell buddy request,remove friends from friends list - /// - - public sealed class DelBuddyHandler : LoggedInCmdHandlerBase - { - private new DelBuddyRequest _request => (DelBuddyRequest)base._request; - //delete friend in database then send bm_revoke message to friend - public DelBuddyHandler(Client client, DelBuddyRequest request) : base(client, request) - { - } - - protected override void DataOperation() - { - StorageOperation.Persistance.DeleteFriendByProfileId(_client.Info.ProfileInfo.Profileid, - _request.TargetId, - _client.Info.SubProfileInfo.Namespaceid); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/InviteToHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/InviteToHandler.cs deleted file mode 100755 index 28ab82204..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/InviteToHandler.cs +++ /dev/null @@ -1,35 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Buddy -{ - /// - /// This function sets which games the local profile can be invited to. - /// - public sealed class InviteToHandler : LoggedInCmdHandlerBase - { - //_connection.SendAsync(@"\pinvite\\sesskey\223\profileid\13\productid\1038\final\"); - private new InviteToRequest _request => (InviteToRequest)base._request; - public InviteToHandler(Client client, InviteToRequest request) : base(client, request) - { - } - - protected override void DataOperation() - { - var client = ClientManager.GetClient(_request.ProfileId, _request.ProductId); - - //user is offline - if (client is null) - { - return; - } - else - { - - } - //TODO - //parse user to buddy message system - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/StatusHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/StatusHandler.cs deleted file mode 100755 index a04468198..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/StatusHandler.cs +++ /dev/null @@ -1,37 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using UniSpy.Server.PresenceConnectionManager.Contract.Result; -using UniSpy.Server.PresenceConnectionManager.Application; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Buddy -{ - /// - /// TODO Status should be stored in redis - /// - - public sealed class StatusHandler : LoggedInCmdHandlerBase - { - private new StatusRequest _request => (StatusRequest)base._request; - private new StatusResult _result { get => (StatusResult)base._result; set => base._result = value; } - - public StatusHandler(Client client, StatusRequest request) : base(client, request) - { - _result = new StatusResult(); - } - - protected override void DataOperation() - { - // set user status - if (_request.IsGetStatus) - { - //TODO check if statushandler need send response - } - else - { - _client.Info.Status.CurrentStatus = _request.Status.CurrentStatus; - _client.Info.Status.StatusString = _request.Status.StatusString; - _client.Info.Status.LocationString = _request.Status.LocationString; - } - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/StatusInfoHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/StatusInfoHandler.cs deleted file mode 100755 index 51ce14470..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Buddy/StatusInfoHandler.cs +++ /dev/null @@ -1,54 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using UniSpy.Server.PresenceConnectionManager.Contract.Response; -using UniSpy.Server.PresenceConnectionManager.Contract.Result; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Buddy -{ - /// - /// TODO Status info should be stored in redis - /// - - public sealed class StatusInfoHandler : LoggedInCmdHandlerBase - { - private new StatusInfoRequest _request => (StatusInfoRequest)base._request; - private new StatusInfoResult _result { get => (StatusInfoResult)base._result; set => base._result = value; } - - public StatusInfoHandler(Client client, StatusInfoRequest request) : base(client, request) - { - _result = new StatusInfoResult(); - } - - protected override void DataOperation() - { - if (_request.IsGetStatusInfo) - { - var result = ClientManager.GetClient(_request.ProfileId, null, _request.NamespaceID); - - if (result is not null) - { - // user is not online we do not need to send status info - _result.StatusInfo = result.Info.StatusInfo; - } - } - else - { - _client.Info.StatusInfo = _request.StatusInfo; - // TODO notify every online friend? - } - } - - protected override void ResponseConstruct() - { - if (_request.IsGetStatusInfo) - { - _response = new StatusInfoResponse(_request, _result); - } - else - { - base.ResponseConstruct(); - } - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/KeepAliveHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/KeepAliveHandler.cs deleted file mode 100755 index 8d516c5f0..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/KeepAliveHandler.cs +++ /dev/null @@ -1,26 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using UniSpy.Server.PresenceConnectionManager.Contract.Response; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.General -{ - - public sealed class KeepAliveHandler : CmdHandlerBase - { - public KeepAliveHandler(Client client, KeepAliveRequest request) : base(client, request) - { - } - - protected override void DataOperation() - { - //we need to keep the player cache online - //so that other players can find the player - } - - protected override void ResponseConstruct() - { - _response = new KeepAliveResponse(_request, _result); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LoginHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LoginHandler.cs deleted file mode 100755 index 383a44180..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LoginHandler.cs +++ /dev/null @@ -1,144 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Enumerate; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using UniSpy.Server.PresenceConnectionManager.Contract.Response; -using UniSpy.Server.PresenceConnectionManager.Contract.Result; -using UniSpy.Server.PresenceConnectionManager.Structure; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.PresenceSearchPlayer.Exception.Login; -using UniSpy.Server.PresenceConnectionManager.Application; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.General -{ - - public sealed class LoginHandler : CmdHandlerBase - { - private new LoginRequest _request => (LoginRequest)base._request; - private new LoginResult _result { get => (LoginResult)base._result; set => base._result = value; } - public LoginHandler(Client client, LoginRequest request) : base(client, request) - { - _result = new LoginResult(); - } - - protected override void DataOperation() - { - try - { - switch (_request.Type) - { - case LoginType.NickEmail: - NickEmailLogin(); - break; - - case LoginType.UniquenickNamespaceID: - UniquenickLogin(); - break; - - case LoginType.AuthToken: - AuthtokenLogin(); - break; - // loginticket - } - _result.DatabaseResults = new LogInDataModel - { - Email = _client.Info.UserInfo.Email, - UserId = _client.Info.UserInfo.Userid, - ProfileId = _client.Info.ProfileInfo.Profileid, - SubProfileId = _client.Info.SubProfileInfo.Subprofileid, - Nick = _client.Info.ProfileInfo.Nick, - UniqueNick = _client.Info.SubProfileInfo.Uniquenick, - PasswordHash = _client.Info.UserInfo.Password, - EmailVerifiedFlag = (bool)_client.Info.UserInfo.Emailverified, - BannedFlag = (bool)_client.Info.UserInfo.Banned, - NamespaceId = _client.Info.SubProfileInfo.Namespaceid - }; - _client.Info.Status.CurrentStatus = GPStatusCode.Online; - } - catch (System.Exception e) - { - throw new GPDatabaseException(e.Message); - } - - IsChallengeCorrect(); - - if (!_result.DatabaseResults.EmailVerifiedFlag) - { - throw new GPLoginBadProfileException(); - } - - // Check if the user is flagged as banned. - if (_result.DatabaseResults.BannedFlag) - { - throw new GPLoginProfileDeletedException(); - } - - // PartnerID is optional - LoginChallengeProof proofData = new LoginChallengeProof( - _request.UserData, - (LoginType)_request.Type, - _request.PartnerID, - LoginChallengeProof.ServerChallenge, - _request.UserChallenge, - _result.DatabaseResults.PasswordHash); - - _result.ResponseProof = - LoginChallengeProof.GenerateProof(proofData); - - _client.Info.LoginStat = LoginStatus.Completed; - } - - protected override void ResponseConstruct() - { - _response = new LoginResponse(_request, _result); - } - - protected override void Response() - { - base.Response(); - //Arves is correct we need to check this - // save information to client object - new SdkRevisionHandler(_client, _request).Handle(); - } - - private void NickEmailLogin() - { - //Check email existence - if (!StorageOperation.Persistance.IsEmailExist(_request.Email)) - { - throw new GPLoginBadEmailException($"email: {_request.Email} is invalid."); - } - - (_client.Info.UserId, _client.Info.ProfileId, _client.Info.SubProfileId) = PresenceConnectionManager.Application.StorageOperation.Persistance.GetUsersInfos(_request.Email, _request.Nick); - } - - - private void UniquenickLogin() - { - (_client.Info.UserId, _client.Info.ProfileId, _client.Info.SubProfileId) = PresenceConnectionManager.Application.StorageOperation.Persistance.GetUsersInfos(_request.UniqueNick, (int)_request.NamespaceID); - } - - private void AuthtokenLogin() - { - (_client.Info.UserId, _client.Info.ProfileId, _client.Info.SubProfileId) = PresenceConnectionManager.Application.StorageOperation.Persistance.GetUsersInfos(_request.AuthToken, (int)_request.PartnerID, (int)_request.NamespaceID); - } - - private void IsChallengeCorrect() - { - // PartnerID is optional - LoginChallengeProof proofData = new LoginChallengeProof( - _request.UserData, - (LoginType)_request.Type, - (int?)_request.PartnerID, - _request.UserChallenge, - LoginChallengeProof.ServerChallenge, - _result.DatabaseResults.PasswordHash); - - string response = LoginChallengeProof.GenerateProof(proofData); - - if (_request.Response != response) - { - throw new GPLoginBadPasswordException("The response is not valid, this maybe caused by wrong password."); - } - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LogoutHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LogoutHandler.cs deleted file mode 100755 index 85f5c0152..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/LogoutHandler.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.General -{ - - public sealed class LogoutHandler : CmdHandlerBase - { - public LogoutHandler(Client client, LogoutRequest request) : base(client, request) - { - } - - protected override void DataOperation() - { - _client.Connection.Disconnect(); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/NewUserHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/NewUserHandler.cs deleted file mode 100755 index 719ee50f5..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/NewUserHandler.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.PresenceConnectionManager.Contract.Response; - - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.General -{ - public sealed class NewUserHandler : PresenceSearchPlayer.Handler.CmdHandler.NewUserHandler - { - public NewUserHandler(IClient client, IRequest request) : base(client, request) - { - } - protected override void ResponseConstruct() - { - _response = new NewUserResponse(_request, _result); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/SDKRevisionHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/SDKRevisionHandler.cs deleted file mode 100755 index 7d5f60120..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/General/SDKRevisionHandler.cs +++ /dev/null @@ -1,26 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Buddy; -using UniSpy.Server.PresenceConnectionManager.Application; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.General -{ - - public sealed class SdkRevisionHandler : CmdHandlerBase - { - private new LoginRequest _request => (LoginRequest)base._request; - public SdkRevisionHandler(Client client, LoginRequest request) : base(client, request) - { - } - protected override void RequestCheck() { } - protected override void DataOperation() - { - if (_client.Info.SdkRevision.IsSupportGPINewListRetrevalOnLogin) - { - //send buddy list and block list - new BuddyListHandler(_client).Handle(); - new BlockListHandler(_client).Handle(); - } - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/AddBlockHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/AddBlockHandler.cs deleted file mode 100755 index d7d9476e9..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/AddBlockHandler.cs +++ /dev/null @@ -1,30 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Profile -{ - - public sealed class AddBlockHandler : LoggedInCmdHandlerBase - { - private new AddBlockRequest _request => (AddBlockRequest)base._request; - public AddBlockHandler(Client client, AddBlockRequest request) : base(client, request) - { - } - protected override void RequestCheck() - { - base.RequestCheck(); - if (_client.Info.ProfileInfo.Profileid == _request.TargetId) - { - throw new GPException("You can not block your self."); - } - } - protected override void DataOperation() - { - StorageOperation.Persistance.UpdateBlockInfo(_request.TargetId, - _client.Info.ProfileInfo.Profileid, - _client.Info.SubProfileInfo.Namespaceid); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/GetProfileHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/GetProfileHandler.cs deleted file mode 100755 index 71be02d39..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/GetProfileHandler.cs +++ /dev/null @@ -1,37 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using UniSpy.Server.PresenceConnectionManager.Contract.Response; -using UniSpy.Server.PresenceConnectionManager.Contract.Result; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Profile -{ - - public sealed class GetProfileHandler : LoggedInCmdHandlerBase - { - // \getprofile\\sesskey\19150\profileid\2\id\2\final\ - private new GetProfileRequest _request => (GetProfileRequest)base._request; - private new GetProfileResult _result { get => (GetProfileResult)base._result; set => base._result = value; } - - public GetProfileHandler(Client client, GetProfileRequest request) : base(client, request) - { - _result = new GetProfileResult(); - } - protected override void DataOperation() - { - _result.UserProfile = StorageOperation.Persistance.GetProfileInfos(_request.ProfileId, _client.Info.SubProfileInfo.Namespaceid); - - if (_result.UserProfile is null) - { - throw new GPDatabaseException($"No profile of profileid:{_request.ProfileId} found in database."); - } - } - - protected override void ResponseConstruct() - { - _response = new GetProfileResponse(_request, _result); - } - } -} - diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/NewProfileHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/NewProfileHandler.cs deleted file mode 100755 index d55be1814..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/NewProfileHandler.cs +++ /dev/null @@ -1,48 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using UniSpy.Server.PresenceConnectionManager.Contract.Response; -using UniSpy.Server.PresenceConnectionManager.Contract.Result; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Profile -{ - - public sealed class NewProfileHandler : Abstraction.BaseClass.CmdHandlerBase - { - private new NewProfileRequest _request => (NewProfileRequest)base._request; - private new NewProfileResult _result { get => (NewProfileResult)base._result; set => base._result = value; } - public NewProfileHandler(Client client, NewProfileRequest request) : base(client, request) - { - _result = new NewProfileResult(); - } - protected override void RequestCheck() - { - base.RequestCheck(); - if (_client.Info.ProfileInfo.Nick != _request.OldNick) - { - throw new GPException("The old nickname is not identical to current nickname."); - } - } - protected override void DataOperation() - { - if (_request.IsReplaceNickName) - { - StorageOperation.Persistance.UpdateNickName(_client.Info.ProfileInfo.Profileid, - _request.OldNick, - _request.NewNick); - } - else - { - StorageOperation.Persistance.AddNickName(_client.Info.UserInfo.Userid, - _client.Info.ProfileInfo.Profileid, - _request.NewNick); - } - _result.ProfileId = _client.Info.ProfileInfo.Profileid; - } - - protected override void ResponseConstruct() - { - _response = new NewProfileResponse(_request, _result); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/RegisterCDKeyHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/RegisterCDKeyHandler.cs deleted file mode 100755 index f098f2fbf..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/RegisterCDKeyHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Profile -{ - - public sealed class RegisterCDKeyHandler : LoggedInCmdHandlerBase - { - private new RegisterCDKeyRequest _request => (RegisterCDKeyRequest)base._request; - public RegisterCDKeyHandler(Client client, RegisterCDKeyRequest request) : base(client, request) - { - } - - protected override void DataOperation() - { - _client.Info.SubProfileInfo.Cdkeyenc = _request.CDKeyEnc; - StorageOperation.Persistance.UpdateSubProfileInfo(_client.Info.SubProfileInfo); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/RegisterNickHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/RegisterNickHandler.cs deleted file mode 100755 index f56026e19..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/RegisterNickHandler.cs +++ /dev/null @@ -1,42 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using UniSpy.Server.PresenceConnectionManager.Contract.Response; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Profile -{ - - public sealed class RegisterNickHandler : LoggedInCmdHandlerBase - { - private new RegisterNickRequest _request => (RegisterNickRequest)base._request; - public RegisterNickHandler(Client client, RegisterNickRequest request) : base(client, request) - { - } - protected override void RequestCheck() - { - base.RequestCheck(); - if (_request.UniqueNick == _client.Info.SubProfileInfo.Uniquenick) - { - throw new GPException("new uniquenick is identical to old uniquenick, no update needed"); - } - } - protected override void DataOperation() - { - try - { - StorageOperation.Persistance.UpdateUniqueNick(_client.Info.SubProfileInfo.Subprofileid, - _request.UniqueNick); - } - catch (System.Exception e) - { - throw new GPDatabaseException(e.Message); - } - } - - protected override void ResponseConstruct() - { - _response = new RegisterNickResponse(_request, _result); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/RemoveBlockHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/RemoveBlockHandler.cs deleted file mode 100755 index 0b9776313..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/RemoveBlockHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System; -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.PresenceConnectionManager.Application; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Profile -{ - - public sealed class RemoveBlockHandler : LoggedInCmdHandlerBase - { - public RemoveBlockHandler(Client client, IRequest request) : base(client, request) - { - throw new NotImplementedException(); - } - - protected override void DataOperation() - { - throw new NotImplementedException(); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/UpdateProfileHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/UpdateProfileHandler.cs deleted file mode 100755 index 980e74268..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/UpdateProfileHandler.cs +++ /dev/null @@ -1,62 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Profile -{ - - public sealed class UpdateProfileHandler : Abstraction.BaseClass.CmdHandlerBase - { - private new UpdateProfileRequest _request => (UpdateProfileRequest)base._request; - public UpdateProfileHandler(Client client, UpdateProfileRequest request) : base(client, request) - { - } - - protected override void DataOperation() - { - var profile = _client.Info.ProfileInfo; - - if (_request.HasPublicMaskFlag) - { - profile.Publicmask = (int)_request.PublicMask; - } - if (_request.HasFirstNameFlag) - { - profile.Firstname = _request.FirstName; - } - if (_request.HasLastNameFlag) - { - profile.Lastname = _request.LastName; - } - if (_request.HasICQFlag) - { - profile.Icquin = _request.ICQUIN; - } - if (_request.HasHomePageFlag) - { - profile.Homepage = _request.HomePage; - } - if (_request.HasBirthdayFlag) - { - profile.Birthday = _request.BirthDay; - profile.Birthmonth = _request.BirthMonth; - profile.Birthyear = _request.BirthYear; - } - if (_request.HasSexFlag) - { - profile.Sex = _request.Sex; - } - - if (_request.HasZipCode) - { - profile.Zipcode = _request.ZipCode; - } - if (_request.HasCountryCode) - { - profile.Countrycode = _request.CountryCode; - } - - StorageOperation.Persistance.UpdateProfileInfo(profile); - } - } -} - diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/UpdateUserInfoHandler.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/UpdateUserInfoHandler.cs deleted file mode 100755 index 075ceff58..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdHandler/Profile/UpdateUserInfoHandler.cs +++ /dev/null @@ -1,24 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.PresenceConnectionManager.Application; - -namespace UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Profile -{ - /// - /// Update user information (email) - /// - - public sealed class UpdateUserInfoHandler : LoggedInCmdHandlerBase - { - public UpdateUserInfoHandler(Client client, IRequest request) : base(client, request) - { - //todo find what data is belong to user info - throw new System.NotImplementedException(); - } - - protected override void DataOperation() - { - throw new System.NotImplementedException(); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/src/Handler/CmdSwitcher.cs b/src/Servers/PresenceConnectionManager/src/Handler/CmdSwitcher.cs deleted file mode 100755 index f5a183543..000000000 --- a/src/Servers/PresenceConnectionManager/src/Handler/CmdSwitcher.cs +++ /dev/null @@ -1,89 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Buddy; -using UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.General; -using UniSpy.Server.PresenceConnectionManager.Handler.CmdHandler.Profile; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using UniSpy.Server.PresenceConnectionManager.Application; -using UniSpy.Server.PresenceConnectionManager.Abstraction.Interface; - -namespace UniSpy.Server.PresenceConnectionManager.Handler -{ - public sealed class CmdSwitcher : CmdSwitcherBase - { - private new string _rawRequest => (string)base._rawRequest; - private new Client _client => (Client)base._client; - public CmdSwitcher(IShareClient client, string rawRequest) : base(client, rawRequest) - { - } - protected override void ProcessRawRequest() - { - if (_rawRequest[0] != '\\') - { - throw new GPParseException("Request format is invalid"); - } - var rawRequests = _rawRequest.Split(@"\final\", StringSplitOptions.RemoveEmptyEntries); - - foreach (var rawRequest in rawRequests) - { - var name = rawRequest.TrimStart('\\').Split('\\').First(); - _requests.Add(new KeyValuePair(name, rawRequest)); - } - } - - protected override IHandler CreateCmdHandlers(object name, object rawRequest) - { - switch ((string)name) - { - # region General - case "ka": - return new KeepAliveHandler(_client, new KeepAliveRequest((string)rawRequest)); - case "login": - return new LoginHandler(_client, new LoginRequest((string)rawRequest)); - case "logout": - return new LogoutHandler(_client, new LogoutRequest((string)rawRequest)); - case "newuser": - return new NewUserHandler(_client, new NewUserRequest((string)rawRequest)); - #endregion - # region Profile - case "addblock": - return new AddBlockHandler(_client, new AddBlockRequest((string)rawRequest)); - case "getprofile": - return new GetProfileHandler(_client, new GetProfileRequest((string)rawRequest)); - case "newprofile": - return new NewProfileHandler(_client, new NewProfileRequest((string)rawRequest)); - case "registercdkey": - return new RegisterCDKeyHandler(_client, new RegisterCDKeyRequest((string)rawRequest)); - case "registernick": - return new RegisterNickHandler(_client, new RegisterNickRequest((string)rawRequest)); - case "updatepro": - return new UpdateProfileHandler(_client, new UpdateProfileRequest((string)rawRequest)); - case "updateui": - // return new UpdateUserInfoHandler(_client, new UpdateUserInfoRequest((string)rawRequest)); - throw new NotImplementedException(); - - # endregion - # region Buddy - case "addbuddy": - return new AddBuddyHandler(_client, new AddBuddyRequest((string)rawRequest)); - case "delbuddy": - return new DelBuddyHandler(_client, new DelBuddyRequest((string)rawRequest)); - case "bsi": - // return new BuddyStatusInfoHandler(_client, new BuddyStatusInfoRequest((string)rawRequest)); - throw new NotImplementedException(); - case "status": - return new StatusHandler(_client, new StatusRequest((string)rawRequest)); - case "statusinfo": - return new StatusInfoHandler(_client, new StatusInfoRequest((string)rawRequest)); - #endregion - default: - return null; - } - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/src/UniSpy.Server.PresenceConnectionManager.csproj b/src/Servers/PresenceConnectionManager/src/UniSpy.Server.PresenceConnectionManager.csproj deleted file mode 100755 index 332590493..000000000 --- a/src/Servers/PresenceConnectionManager/src/UniSpy.Server.PresenceConnectionManager.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - Exe - net6.0 - ..\..\..\..\common\Icon\UniSpy_Logo.ico - Linux - ..\..\..\.. - - - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - - - - \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/test/Buddy/BuddyRequestTest.cs b/src/Servers/PresenceConnectionManager/test/Buddy/BuddyRequestTest.cs deleted file mode 100644 index 88056abc3..000000000 --- a/src/Servers/PresenceConnectionManager/test/Buddy/BuddyRequestTest.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Xunit; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; - -namespace UniSpy.Server.PresenceConnectionManager.Test -{ - public class BuddyRequestTest - { - [Fact] - public void AddBuddy() - { - var request = new AddBuddyRequest(BuddyRequests.AddBuddy); - request.Parse(); - Assert.Equal((int)0, request.FriendProfileID); - Assert.Equal("test", request.Reason); - } - - [Fact] - public void DelBuddy() - { - var request = new DelBuddyRequest(BuddyRequests.DelBuddy); - request.Parse(); - Assert.Equal((int)0, request.TargetId); - } - - [Fact] - public void InviteTo() - { - var request = new InviteToRequest(BuddyRequests.InviteTo); - request.Parse(); - Assert.Equal((int)0, request.ProductId); - Assert.Equal((int)0, request.ProfileId); - } - - // TODO: - /*[Fact] - public void StatusInfo() - { - var request = new StatusInfoRequest(BuddyRequests.StatusInfo); - request.Parse(); - Assert.Equal((int)0, request.ProfileId); - Assert.Equal((int)0, request.NamespaceID); - }*/ - - [Fact] - public void Status() - { - var request = new StatusRequest(BuddyRequests.Status); - request.Parse(); - Assert.Equal("test", request.Status.StatusString); - Assert.Equal("test", request.Status.LocationString); - } - } -} diff --git a/src/Servers/PresenceConnectionManager/test/Buddy/BuddyRequests.cs b/src/Servers/PresenceConnectionManager/test/Buddy/BuddyRequests.cs deleted file mode 100644 index 5c0309cde..000000000 --- a/src/Servers/PresenceConnectionManager/test/Buddy/BuddyRequests.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace UniSpy.Server.PresenceConnectionManager.Test -{ - public static class BuddyRequests - { - public const string AddBuddy = @"\addbuddy\\sesskey\0\newprofileid\0\reason\test\final\"; - - public const string DelBuddy = @"\delbuddy\\sesskey\0\delprofileid\0\final\"; - - public const string InviteTo = @"\inviteto\\sesskey\0\productid\0\profileid\0\final\"; - - //public const string StatusInfo = @"\inviteto\\sesskey\0\productid\0\profileid\0\final\"; - - public const string Status = @"\status\0\statstring\test\locstring\test\final\"; - - } -} diff --git a/src/Servers/PresenceConnectionManager/test/Game/GameTest.cs b/src/Servers/PresenceConnectionManager/test/Game/GameTest.cs deleted file mode 100644 index 81f1dfdad..000000000 --- a/src/Servers/PresenceConnectionManager/test/Game/GameTest.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using Xunit; - -namespace UniSpy.Server.PresenceConnectionManager.Test -{ - public class GameTest - { - [Fact] - public void Civilization4() - { - var rawRequests = new List() - { - @"\newuser\\email\civ4@unispy.org\nick\civ4-tk\passwordenc\JMHGwQ__\productid\10435\gamename\civ4\namespaceid\17\uniquenick\civ4-tk\id\1\final\", - @"\login\\challenge\xMsHUXuWNXL3KMwmhoQZJrP0RVsArCYT\uniquenick\civ4-tk\userid\25\profileid\26\response\7f2c9c6685570ea18b7207d2cbd72452\firewall\1\port\0\productid\10435\gamename\civ4\namespaceid\17\sdkrevision\1\id\1\final\" - }; - foreach (var raw in rawRequests) - { - ((ITestClient)MokeObject.client).TestReceived(UniSpyEncoding.GetBytes(raw)); - } - } - [Fact] - public void ConflictGlobalStorm() - { - var rawRequests = new List() - { - @"\lc\1\challenge\NRNUJLZMLX\id\1\final\", - @"\login\\challenge\KMylyQbZfqzKn9otxx32q4076sOUnKif\user\cgs1@cgs1@rs.de\response\c1a6638bbcfe130e4287bfe4aa792949\port\-15737\productid\10469\gamename\conflictsopc\namespaceid\1\id\1\final\", - @"\inviteto\\sesskey\58366\products\1038\final\" - }; - foreach (var raw in rawRequests) - { - ((ITestClient)MokeObject.client).TestReceived(UniSpyEncoding.GetBytes(raw)); - } - } - [Fact] - public void swbfrontps2Test() - { - var raw = @"\status\1\sesskey\1111\statstring\EN LIGNE\locstring\\final\"; - var req = new StatusRequest(raw); - req.Parse(); - Assert.True(req.Status.LocationString == ""); - Assert.True(req.Status.StatusString == "EN LIGNE"); - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/test/General/GeneralRequestTest.cs b/src/Servers/PresenceConnectionManager/test/General/GeneralRequestTest.cs deleted file mode 100644 index 45cbd4742..000000000 --- a/src/Servers/PresenceConnectionManager/test/General/GeneralRequestTest.cs +++ /dev/null @@ -1,106 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Enumerate; -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using Xunit; - -namespace UniSpy.Server.PresenceConnectionManager.Test -{ - public class GeneralRequestTest - { - //TODO: no keep alive request - /*[Fact] - public void KeepAlive() - { - var request = new KeepAliveRequest(GeneralRequests.KeepAlive); - request.Parse(); - Assert.Equal("xxxx", "xxxx"); - }*/ - - [Fact] - public void LoginAuthtoken() - { - var request = new LoginRequest(GeneralRequests.LoginAuthtoken); - request.Parse(); - Assert.Equal(LoginType.AuthToken, request.Type); - Assert.Equal("xxxx", request.UserChallenge); - Assert.Equal("xxxx", request.AuthToken); - Assert.Equal((int)0, request.UserID); - Assert.Equal((int)0, request.ProfileId); - Assert.Equal((int)0, request.PartnerID); - Assert.Equal("xxxxx", request.Response); - Assert.Equal("1", request.Firewall); - Assert.Equal(request.GamePort, (ushort)0); - Assert.Equal(request.ProductID, (int)0); - Assert.Equal("gmtest", request.GameName); - Assert.Equal((SdkRevisionType)3, request.SdkRevisionType); - Assert.Equal((QuietModeType)0, request.QuietModeFlags); - } - - [Fact] - public void LoginUniquenick() - { - var request = new LoginRequest(GeneralRequests.LoginUniquenick); - request.Parse(); - Assert.Equal(LoginType.UniquenickNamespaceID, request.Type); - Assert.Equal("xxxx", request.UserChallenge); - Assert.Equal("spyguy", request.UniqueNick); - Assert.Equal((int)0, request.NamespaceID); - Assert.Equal((int)0, request.UserID); - Assert.Equal((int)0, request.ProfileId); - Assert.Equal((int)0, request.PartnerID); - Assert.Equal("xxxxx", request.Response); - Assert.Equal("1", request.Firewall); - Assert.Equal((ushort)0, request.GamePort); - Assert.Equal((int)0, request.ProductID); - Assert.Equal("gmtest", request.GameName); - Assert.Equal((SdkRevisionType)3, request.SdkRevisionType); - Assert.Equal((QuietModeType)0, request.QuietModeFlags); - } - - [Fact] - public void LoginUser() - { - var request = new LoginRequest(GeneralRequests.LoginUser); - request.Parse(); - Assert.Equal(LoginType.NickEmail, request.Type); - Assert.Equal("xxxx", request.UserChallenge); - Assert.Equal("spyguy", request.Nick); - Assert.Equal("spyguy@unispy.org", request.Email); - Assert.Equal((int)0, request.NamespaceID); - Assert.Equal((int)0, request.UserID); - Assert.Equal((int)0, request.ProfileId); - Assert.Equal((int)0, request.PartnerID); - Assert.Equal("xxxxx", request.Response); - Assert.Equal("1", request.Firewall); - Assert.Equal((ushort)0, request.GamePort); - Assert.Equal((int)0, request.ProductID); - Assert.Equal("gmtest", request.GameName); - Assert.Equal((SdkRevisionType)3, request.SdkRevisionType); - Assert.Equal((QuietModeType)0, request.QuietModeFlags); - } - - //TODO: no logout request - /*[Fact] - public void Logout() - { - var request = new LogoutRequest(GeneralRequests.Logout); - request.Parse(); - Assert.Equal("xxxx", "xxxx"); - }*/ - - //TODO: passwordenc doesn't work - /*[Fact] - public void NewUser() - { - var request = new NewUserRequest(GeneralRequests.NewUser); - request.Parse(); - Assert.Equal("spyguy@unispy.org", request.Email); - Assert.Equal("spyguy", request.Nick); - Assert.Equal("0000", request.Password); - Assert.Equal((int)0, request.ProductID); - Assert.Equal("gmtest", request.GameName); - Assert.Equal((int)0, request.NamespaceID); - Assert.Equal("spyguy", request.Uniquenick); - Assert.Equal("xxxx", request.CDKeyEnc); - }*/ - } -} diff --git a/src/Servers/PresenceConnectionManager/test/General/GeneralRequests.cs b/src/Servers/PresenceConnectionManager/test/General/GeneralRequests.cs deleted file mode 100644 index bfdcd102b..000000000 --- a/src/Servers/PresenceConnectionManager/test/General/GeneralRequests.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace UniSpy.Server.PresenceConnectionManager.Test -{ - public static class GeneralRequests - { - //public const string KeepAlive = @""; - - public const string LoginAuthtoken = @"\login\\challenge\xxxx\authtoken\xxxx\userid\0\profileid\0\partnerid\0\response\xxxxx\firewall\1\port\0000\productid\0\gamename\gmtest\sdkrevision\3\quiet\0\id\1\final\"; - - public const string LoginUniquenick = @"\login\\challenge\xxxx\uniquenick\spyguy\userid\0\profileid\0\namespaceid\0\partnerid\0\response\xxxxx\firewall\1\port\0000\productid\0\gamename\gmtest\sdkrevision\3\quiet\0\id\1\final\"; - - public const string LoginUser = @"\login\\challenge\xxxx\user\spyguy@spyguy@unispy.org\userid\0\profileid\0\partnerid\0\namespaceid\0\response\xxxxx\firewall\1\port\0000\productid\0\gamename\gmtest\sdkrevision\3\quiet\0\id\1\final\"; - - //public const string Logout = @""; - - //public const string NewUser = @"\newuser\email\spyguy@unispy.org\nick\spyguy\passwordenc\4a7d1ed414474e4033ac29ccb8653d9b\productid\0\gamename\gmtest\namespaceid\0\uniquenick\spyguy\cdkeyenc\xxxx\id\1\final\"; - } -} diff --git a/src/Servers/PresenceConnectionManager/test/MokeObject.cs b/src/Servers/PresenceConnectionManager/test/MokeObject.cs deleted file mode 100644 index 01f2ec6ed..000000000 --- a/src/Servers/PresenceConnectionManager/test/MokeObject.cs +++ /dev/null @@ -1,31 +0,0 @@ -using UniSpy.Server.Core.Abstraction.Interface; -using System.Net; -using Moq; -using UniSpy.Server.PresenceConnectionManager.Application; - -namespace UniSpy.Server.PresenceConnectionManager.Test -{ - public class MokeObject - { - /// - /// PCM test client, create one here, use anywhere - /// - public static IClient client; - static MokeObject() - { - client = CreateClient(); - } - - public static IClient CreateClient(string ipAddress = "192.168.1.1", int port = 9990) - { - var managerMock = new Mock(); - var connectionMock = new Mock(); - connectionMock.Setup(s => s.RemoteIPEndPoint).Returns(new IPEndPoint(IPAddress.Parse(ipAddress), port)); - connectionMock.Setup(s => s.Manager).Returns(managerMock.Object); - connectionMock.Setup(s => s.ConnectionType).Returns(NetworkConnectionType.Tcp); - var serverMock = new PresenceConnectionManager.Application.Server(managerMock.Object); - - return new Client(connectionMock.Object, serverMock); - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/test/Persist/PersistTest.cs b/src/Servers/PresenceConnectionManager/test/Persist/PersistTest.cs deleted file mode 100644 index 70022877d..000000000 --- a/src/Servers/PresenceConnectionManager/test/Persist/PersistTest.cs +++ /dev/null @@ -1,17 +0,0 @@ -using Xunit; -namespace UniSpy.Server.PresenceConnectionManager.Test.Persist -{ - public class PersistTest - { - [Fact] - public void DatabaseAcessTest() - { - // StorageOperation.Persistance.AddNickName() - // Given - - // When - - // Then - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceConnectionManager/test/Profile/ProfileRequestTest.cs b/src/Servers/PresenceConnectionManager/test/Profile/ProfileRequestTest.cs deleted file mode 100644 index e33722f65..000000000 --- a/src/Servers/PresenceConnectionManager/test/Profile/ProfileRequestTest.cs +++ /dev/null @@ -1,107 +0,0 @@ -using UniSpy.Server.PresenceConnectionManager.Contract.Request; -using Xunit; - -namespace UniSpy.Server.PresenceConnectionManager.Test -{ - public class ProfileRequestTest - { - [Fact] - public void AddBlock() - { - var request = new AddBlockRequest(ProfileRequests.AddBlock); - request.Parse(); - Assert.Equal((int)0, request.TargetId); - } - - [Fact] - public void GetProfile() - { - var request = new GetProfileRequest(ProfileRequests.GetProfile); - request.Parse(); - Assert.Equal((int)0, request.ProfileId); - Assert.Equal("xxxx", request.SessionKey); - } - - [Fact] - public void NewProfile() - { - var request = new NewProfileRequest(ProfileRequests.NewProfile); - request.Parse(); - Assert.Equal("xxxx", request.SessionKey); - Assert.Equal("spyguy", request.NewNick); - } - - [Fact] - public void NewProfileReplace() - { - var request = new NewProfileRequest(ProfileRequests.NewProfileReplace); - request.Parse(); - Assert.Equal("xxxx", request.SessionKey); - Assert.Equal("spyguy2", request.NewNick); - Assert.Equal("spyguy", request.OldNick); - } - - [Fact] - public void RegisterCDKey() - { - var request = new RegisterCDKeyRequest(ProfileRequests.RegisterCDKey); - request.Parse(); - Assert.Equal("xxxx", request.SessionKey); - Assert.Equal("xxxx", request.CDKeyEnc); - } - - [Fact] - public void RegisterNick() - { - var request = new RegisterNickRequest(ProfileRequests.RegisterNick); - request.Parse(); - Assert.Equal("xxxx", request.SessionKey); - Assert.Equal("spyguy", request.UniqueNick); - Assert.Equal((int)0, request.PartnerId); - } - [Fact] - public void UpdateProfile() - { - // Given - var crysisWarsRaw = @"\updatepro\\sesskey\1111\countrycode\DE\birthday\168232912\partnerid\0\final\"; - var request = new UpdateProfileRequest(crysisWarsRaw); - request.Parse(); - // When - - // Then - } - //TODO: publicmasks doesn't work - /*[Fact] - public void UpdatePro() - { - var request = new UpdateProRequest(ProfileRequests.UpdatePro); - request.Parse(); - Assert.Equal(PublicMasks.All, request.PublicMask); - Assert.Equal("xxxx", request.SessionKey); - Assert.Equal("Spy", request.FirstName); - Assert.Equal("Guy", request.LastName); - Assert.Equal((int)0, request.ICQUIN); - Assert.Equal("unispy.org", request.HomePage); - Assert.Equal("00000", request.ZipCode); - Assert.Equal("US", request.CountryCode); - Assert.Equal(20, request.BirthDay); - Assert.Equal(3, request.BirthMonth); - Assert.Equal(1980, request.BirthYear); - Assert.Equal(0, request.Sex); - Assert.Equal((int)0, request.PartnerID); - Assert.Equal("spyguy", request.Nick); - Assert.Equal("spyguy", request.Uniquenick); - }*/ - - //TODO: not implemented - /*[Fact] - public void UpdateUi() - { - var request = new UpdateUiRequest(ProfileRequests.UpdateUi); - request.Parse(); - Assert.Equal("xxxx", request.SessionKey); - Assert.Equal("0000", request.Password); - Assert.Equal("spyguy@unispy.org", request.Email); - }*/ - } -} diff --git a/src/Servers/PresenceConnectionManager/test/Profile/ProfileRequests.cs b/src/Servers/PresenceConnectionManager/test/Profile/ProfileRequests.cs deleted file mode 100644 index 1aa316484..000000000 --- a/src/Servers/PresenceConnectionManager/test/Profile/ProfileRequests.cs +++ /dev/null @@ -1,21 +0,0 @@ -namespace UniSpy.Server.PresenceConnectionManager.Test -{ - public static class ProfileRequests - { - public const string AddBlock = @"\addblock\\profileid\0\final\"; - - public const string GetProfile = @"\getprofile\\sesskey\xxxx\profileid\0\final\"; - - public const string NewProfile = @"\newprofile\\sesskey\xxxx\nick\spyguy\id\1\final\"; - - public const string NewProfileReplace = @"\newprofile\\sesskey\xxxx\nick\spyguy2\replace\1\oldnick\spyguy\id\1\final\"; - - public const string RegisterCDKey = @"\registercdkey\\sesskey\xxxx\cdkeyenc\xxxx\id\1\final\"; - - public const string RegisterNick = @"\registernick\\sesskey\xxxx\uniquenick\spyguy\partnerid\0\id\1\final\"; - - //public const string UpdatePro = @"\updatepro\\sesskey\xxxx\firstname\Spy\lastname\Guy\icquin\0\homepage\unispy.org\zipcode\00000\countrycode\US\birthday\335742908\sex\0\aim\spyguy@aim.com\pic\0\occ\0\ind\0\inc\0\mar\0\chc\0\i1\0\nick\spyguy\uniquenick\spyguy\publicmask\0\partnerid\0\final\"; - - //public const string UpdateUi = @"\updateui\\sesskey\xxxx\passwordenc\4a7d1ed414474e4033ac29ccb8653d9b\email\spyguy@unispy.org\final\"; - } -} diff --git a/src/Servers/PresenceConnectionManager/test/UniSpy.Server.PresenceConnectionManager.Test.csproj b/src/Servers/PresenceConnectionManager/test/UniSpy.Server.PresenceConnectionManager.Test.csproj deleted file mode 100644 index 8bc0f33ad..000000000 --- a/src/Servers/PresenceConnectionManager/test/UniSpy.Server.PresenceConnectionManager.Test.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - net6.0 - false - ..\..\..\..\common\Icon\UniSpy_Logo.ico - - - AnyCPU - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Abstraction/BaseClass/CmdHandlerBase.cs b/src/Servers/PresenceSearchPlayer/src/Abstraction/BaseClass/CmdHandlerBase.cs deleted file mode 100755 index d4d165c3e..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Abstraction/BaseClass/CmdHandlerBase.cs +++ /dev/null @@ -1,27 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass -{ - public abstract class CmdHandlerBase : UniSpy.Server.Core.Abstraction.BaseClass.CmdHandlerBase - { - /// - /// Be careful the return of query function should be List type, - /// the decision formula should use _result.Count==0 - /// - protected new RequestBase _request => (RequestBase)base._request; - protected new ResultBase _result { get => (ResultBase)base._result; set => base._result = value; } - public CmdHandlerBase(IClient client, IRequest request) : base(client, request) - { - } - - protected override void HandleException(System.Exception ex) - { - if (ex is GPException) - { - _client.Send(((GPException)ex)); - } - base.HandleException(ex); - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Abstraction/BaseClass/RequestBase.cs b/src/Servers/PresenceSearchPlayer/src/Abstraction/BaseClass/RequestBase.cs deleted file mode 100755 index c54970adb..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Abstraction/BaseClass/RequestBase.cs +++ /dev/null @@ -1,36 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.Core.Misc; - -namespace UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass -{ - public abstract class RequestBase : UniSpy.Server.Core.Abstraction.BaseClass.RequestBase - { - public Dictionary RequestKeyValues { get; protected set; } - public new string RawRequest{ get => (string)base.RawRequest; - } - public new string CommandName{ get => (string)base.CommandName; - protected set => base.CommandName = value; } - public ushort OperationID { get; protected set; } - public RequestBase(string rawRequest) : base(rawRequest) - { - RequestKeyValues = GameSpyUtils.ConvertToKeyValue(rawRequest); - } - - public override void Parse() - { - CommandName = RequestKeyValues.Keys.First(); - - if (RequestKeyValues.ContainsKey("id")) - { - ushort operationID; - if (!ushort.TryParse(RequestKeyValues["id"], out operationID)) - { - throw new GPParseException("operation id is invalid."); - } - OperationID = operationID; - } - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Abstraction/BaseClass/ResponseBase.cs b/src/Servers/PresenceSearchPlayer/src/Abstraction/BaseClass/ResponseBase.cs deleted file mode 100755 index 6e68b53b6..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Abstraction/BaseClass/ResponseBase.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass -{ - public abstract class ResponseBase : UniSpy.Server.Core.Abstraction.BaseClass.ResponseBase - { - protected new ResultBase _result => (ResultBase)base._result; - protected new RequestBase _request => (RequestBase)base._request; - public new string SendingBuffer { get => (string)base.SendingBuffer; protected set => base.SendingBuffer = value; } - public ResponseBase(RequestBase request, ResultBase result) : base(request, result) - { - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Abstraction/BaseClass/ResultBase.cs b/src/Servers/PresenceSearchPlayer/src/Abstraction/BaseClass/ResultBase.cs deleted file mode 100755 index 61f3cbc51..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Abstraction/BaseClass/ResultBase.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass -{ - public abstract class ResultBase : UniSpy.Server.Core.Abstraction.BaseClass.ResultBase - { - public ResultBase() - { - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Abstraction/Interface/IStorageOperation.cs b/src/Servers/PresenceSearchPlayer/src/Abstraction/Interface/IStorageOperation.cs deleted file mode 100644 index d49957912..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Abstraction/Interface/IStorageOperation.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; -using UniSpy.Server.Core.Database.DatabaseModel; - -namespace UniSpy.Server.PresenceSearchPlayer.Abstraction.Interface -{ - public interface IStorageOperation - { - int? GetProfileId(string email, string password, string nickName, int? partnerId); - bool VerifyEmail(string email); - bool VerifyEmailAndPassword(string email, string password); - void AddUser(User user); - void AddProfile(Profile profile); - void AddSubProfile(Subprofile subprofile); - void UpdateProfile(Profile profile); - void UpdateSubProfile(Subprofile subProfile); - User GetUser(string email); - Profile GetProfile(int userId, string nickName); - Subprofile GetSubProfile(int profileId, int namespaceId, int productId); - List GetAllNickAndUniqueNick(string email, string password, int namespaceId); - List GetFriendsInfo(int profileId, int namespaceId, string gameName); - List GetMatchedProfileIdInfos(List profileIds, int namespaceId); - List GetMatchedInfosByNick(string nickName); - List GetMatchedInfosByEmail(string email); - List GetMatchedInfosByNickAndEmail(string nickName, string email); - List GetMatchedInfosByUniqueNickAndNamespaceId(string uniqueNick, int namespaceId); - List GetMatchedInfosByNamespaceId(List namespaceIds, string uniqueNick); - bool IsUniqueNickExist(string uniqueNick, int namespaceId, string gameName); - bool IsEmailExist(string email); - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Aggregate/PSPRequestName.cs b/src/Servers/PresenceSearchPlayer/src/Aggregate/PSPRequestName.cs deleted file mode 100755 index d97ebe6bd..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Aggregate/PSPRequestName.cs +++ /dev/null @@ -1,46 +0,0 @@ -namespace UniSpy.Server.PresenceSearchPlayer.Aggregate -{ - public class PSPRequestName - { - /// - /// Search account existance - /// - public const string Search = "search"; - /// - /// Check if the email is existed - /// - public const string Valid = "valid"; - /// - /// Search an user with nick name - /// - public const string Nicks = "nicks"; - /// - /// get the players match search condition - /// - public const string PMatch = "pmatch"; - /// - /// Check if the account is valid and password is correct - /// - public const string Check = "check"; - /// - /// Create a new user - /// - public const string NewUser = "newuser"; - /// - /// Search an user with uniquenick - /// - public const string SearchUnique = "searchunique"; - /// - /// Get the accounts information which match search condition - /// - public const string Others = "others"; - /// - /// search other players friend list to see who is in his list? - /// - public const string OtherList = "otherslist"; - /// - /// search a user with uniquenick and namespaceid - /// - public const string UniqueSearch = "uniquesearch"; - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Application/Client.cs b/src/Servers/PresenceSearchPlayer/src/Application/Client.cs deleted file mode 100644 index 4eaa33c26..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Application/Client.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Handler; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; - -namespace UniSpy.Server.PresenceSearchPlayer.Application -{ - public sealed class Client : ClientBase - { - public Client(IConnection connection, IServer server) : base(connection, server) - { - Info = new ClientInfo(); - } - - protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, UniSpyEncoding.GetString((byte[])buffer)); - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Application/ClientInfo.cs b/src/Servers/PresenceSearchPlayer/src/Application/ClientInfo.cs deleted file mode 100644 index c6315ffcb..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Application/ClientInfo.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.Core.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceSearchPlayer.Application -{ - public sealed class ClientInfo : ClientInfoBase - { - public ClientInfo( ) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Application/Program.cs b/src/Servers/PresenceSearchPlayer/src/Application/Program.cs deleted file mode 100755 index 14aaf1ef9..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Application/Program.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.PresenceSearchPlayer.Application -{ - public class Program - { - static void Main(string[] args) - { - try - { - new ServerLauncher().Start(); - Console.WriteLine("Press < Q > to exit. "); - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - catch (System.Exception e) - { - UniSpy.Exception.HandleException(e); - } - finally - { - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - } - } -} - diff --git a/src/Servers/PresenceSearchPlayer/src/Application/Server.cs b/src/Servers/PresenceSearchPlayer/src/Application/Server.cs deleted file mode 100644 index 745ae7231..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Application/Server.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Net; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Network.Tcp.Server; - -namespace UniSpy.Server.PresenceSearchPlayer.Application -{ - public sealed class Server : ServerBase - { - static Server() - { - _name = "PresenceSearchPlayer"; - } - public Server() { } - - public Server(IConnectionManager manager) : base(manager) { } - protected override IClient CreateClient(IConnection connection) => new Client(connection, this); - - protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new TcpConnectionManager(endPoint); - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Application/ServerLauncher.cs b/src/Servers/PresenceSearchPlayer/src/Application/ServerLauncher.cs deleted file mode 100644 index 2340ed426..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Application/ServerLauncher.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.BaseClass.Factory; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.PresenceSearchPlayer.Application -{ - public sealed class ServerLauncher : ServerLauncherBase - { - protected override List LaunchNetworkService() => new List { new Server() }; - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Application/StorageOperation.cs b/src/Servers/PresenceSearchPlayer/src/Application/StorageOperation.cs deleted file mode 100644 index c31376b57..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Application/StorageOperation.cs +++ /dev/null @@ -1,360 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.PresenceSearchPlayer.Abstraction.Interface; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; -using UniSpy.Server.Core.Database.DatabaseModel; - -namespace UniSpy.Server.PresenceSearchPlayer.Application -{ - internal sealed class StorageOperation : IStorageOperation - { - public static IStorageOperation Persistance = new StorageOperation(); - public bool VerifyEmail(string email) - { - using (var db = new UniSpyContext()) - { - return db.Users.Where(e => e.Email == email).Count() < 1; - } - } - public bool VerifyEmailAndPassword(string email, string password) - { - using (var db = new UniSpyContext()) - { - return db.Users.Where(u => u.Email == email && u.Password == password).Count() < 1; - } - } - public int? GetProfileId(string email, string password, string nickName, int? partnerId) - { - using (var db = new UniSpyContext()) - { - // Not every game uses PartnerId; optional - var result = from p in db.Profiles - join u in db.Users on p.Userid equals u.Userid - join sp in db.Subprofiles on p.Profileid equals sp.Profileid - where u.Email.Equals(email) - && u.Password.Equals(password) - && p.Nick.Equals(nickName) - || sp.Partnerid.Equals(partnerId) - select p.Profileid; - - if (result.Count() == 1) - { - return result.First(); - } - else - { - return null; - } - } - } - - public void AddSubProfile(Subprofile subProfile) - { - using (var db = new UniSpyContext()) - { - db.Subprofiles.Add(subProfile); - db.SaveChanges(); - } - } - - public void AddProfile(Profile profile) - { - using (var db = new UniSpyContext()) - { - db.Profiles.Add(profile); - db.SaveChanges(); - } - } - public void AddUser(User user) - { - using (var db = new UniSpyContext()) - { - db.Users.Add(user); - db.SaveChanges(); - } - } - public void UpdateProfile(Profile profile) - { - using (var db = new UniSpyContext()) - { - db.Profiles.Update(profile); - db.SaveChanges(); - } - } - public void UpdateSubProfile(Subprofile subprofile) - { - using (var db = new UniSpyContext()) - { - db.Subprofiles.Update(subprofile); - db.SaveChanges(); - } - } - - public User GetUser(string email) - { - using (var db = new UniSpyContext()) - { - return db.Users.Where(u => u.Email == email).Select(u => u).FirstOrDefault(); - } - } - - public Profile GetProfile(int userId, string nickName) - { - using (var db = new UniSpyContext()) - { - return db.Profiles.Where(p => p.Userid == userId && p.Nick == nickName).FirstOrDefault(); - } - } - - public Subprofile GetSubProfile(int profileId, int namespaceId, int productId) - { - using (var db = new UniSpyContext()) - { - return db.Subprofiles.Where(s => - s.Profileid == profileId && - s.Namespaceid == namespaceId && - s.Productid == productId).FirstOrDefault(); - } - } - - public List GetAllNickAndUniqueNick(string email, string password, int namespaceId) - { - using (var db = new UniSpyContext()) - { - var result = from u in db.Users - join p in db.Profiles on u.Userid equals p.Userid - join n in db.Subprofiles on p.Profileid equals n.Profileid - where u.Email == email - && u.Password == password - && n.Namespaceid == namespaceId - select new NicksDataModel - { - NickName = p.Nick, - UniqueNick = n.Uniquenick - }; - return result.ToList(); - } - } - - public List GetFriendsInfo(int profileId, int namespaceId, string gameName) - { - var dataList = new List(); - using (var db = new UniSpyContext()) - { - var result = from b in db.Friends - where b.Profileid == profileId && b.Namespaceid == namespaceId - select b.Targetid; - - foreach (var info in result) - { - var b = from p in db.Profiles - join n in db.Subprofiles on p.Profileid equals n.Profileid - join u in db.Users on p.Userid equals u.Userid - where n.Namespaceid == namespaceId - && n.Profileid == info && n.Gamename == gameName - select new OthersDatabaseModel - { - ProfileId = p.Profileid, - Nick = p.Nick, - Uniquenick = n.Uniquenick, - Lastname = p.Lastname, - Firstname = p.Firstname, - Userid = u.Userid, - Email = u.Email - }; - - dataList.Add(b.First()); - } - } - return dataList; - } - - public List GetMatchedProfileIdInfos(List profileIds, int namespaceId) - { - var dataList = new List(); - using (var db = new UniSpyContext()) - { - foreach (var pid in profileIds) - { - var result = from n in db.Subprofiles - where n.Profileid == pid - && n.Namespaceid == namespaceId - //select new { uniquenick = n.Uniquenick }; - select new OthersListDatabaseModel - { - ProfileId = n.Profileid, - Uniquenick = n.Uniquenick - }; - - dataList.Add(result.First()); - } - } - return dataList; - } - - public List GetMatchedInfosByNick(string nickName) - { - using (var db = new UniSpyContext()) - { - var result = from p in db.Profiles - join n in db.Subprofiles on p.Profileid equals n.Profileid - join u in db.Users on p.Userid equals u.Userid - where p.Nick == nickName - //&& n.Namespaceid == _request.NamespaceID - select new SearchDataBaseModel - { - ProfileId = n.Profileid, - Nick = p.Nick, - Uniquenick = n.Uniquenick, - Email = u.Email, - Firstname = p.Firstname, - Lastname = p.Lastname, - NamespaceID = n.Namespaceid - }; - return result.ToList(); - } - - } - - public List GetMatchedInfosByEmail(string email) - { - - using (var db = new UniSpyContext()) - - { - var result = from p in db.Profiles - join n in db.Subprofiles on p.Profileid equals n.Profileid - join u in db.Users on p.Userid equals u.Userid - where u.Email == email - select new SearchDataBaseModel - { - ProfileId = n.Profileid, - Nick = p.Nick, - Uniquenick = n.Uniquenick, - Email = u.Email, - Firstname = p.Firstname, - Lastname = p.Lastname, - NamespaceID = n.Namespaceid - }; - return result.ToList(); - } - - } - - public List GetMatchedInfosByNickAndEmail(string nickName, string email) - { - using (var db = new UniSpyContext()) - { - var result = from p in db.Profiles - join n in db.Subprofiles on p.Profileid equals n.Profileid - join u in db.Users on p.Userid equals u.Userid - where p.Nick == nickName && u.Email == email - //&& n.Namespaceid == _request.NamespaceID - //&& n.Gamename == _request.GameName - //&& n.Partnerid == _request.PartnerID - select new SearchDataBaseModel - { - ProfileId = n.Profileid, - Nick = p.Nick, - Uniquenick = n.Uniquenick, - Email = u.Email, - Firstname = p.Firstname, - Lastname = p.Lastname, - NamespaceID = n.Namespaceid - }; - return result.ToList(); - } - } - - public List GetMatchedInfosByUniqueNickAndNamespaceId(string uniqueNick, int namespaceId) - { - - using (var db = new UniSpyContext()) - { - var result = from p in db.Profiles - join n in db.Subprofiles on p.Profileid equals n.Profileid - join u in db.Users on p.Userid equals u.Userid - where n.Uniquenick == uniqueNick - && n.Namespaceid == namespaceId - //&& n.Gamename == _request.GameName - //&& n.Partnerid == _request.PartnerID - select new SearchDataBaseModel - { - ProfileId = n.Profileid, - Nick = p.Nick, - Uniquenick = n.Uniquenick, - Email = u.Email, - Firstname = p.Firstname, - Lastname = p.Lastname, - NamespaceID = n.Namespaceid - }; - return result.ToList(); - } - - } - - public List GetMatchedInfosByNamespaceId(List namespaceIds, string uniqueNick) - { - using (var db = new UniSpyContext()) - { - var dataList = new List(); - foreach (var nsId in namespaceIds) - { - var result = from p in db.Profiles - join n in db.Subprofiles on p.Profileid equals n.Profileid - join u in db.Users on p.Userid equals u.Userid - where n.Uniquenick == uniqueNick - && n.Namespaceid == nsId - select new SearchUniqueDatabaseModel - { - ProfileId = n.Profileid, - Nick = p.Nick, - Uniquenick = n.Uniquenick, - Email = u.Email, - Firstname = p.Firstname, - Lastname = p.Lastname, - NamespaceID = n.Namespaceid - }; - dataList.Add(result.First()); - } - return dataList; - } - } - - public bool IsUniqueNickExist(string uniqueNick, int namespaceId, string gameName) - { - using (var db = new UniSpyContext()) - { - var result = from p in db.Profiles - join n in db.Subprofiles on p.Profileid equals n.Profileid - where n.Uniquenick == uniqueNick - && n.Namespaceid == namespaceId - && n.Gamename == gameName - select p.Profileid; - - if (result.Count() != 0) - { - return true; - } - - return false; - } - } - public bool IsEmailExist(string email) - { - using (var db = new UniSpyContext()) - { - var result = from u in db.Users - //According to FSW partnerid is not nessesary - where u.Email == email - select u.Userid; - - if (result.Count() == 0) - { - return false; - } - return true; - } - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Request/CheckRequest.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Request/CheckRequest.cs deleted file mode 100755 index eff8f7e75..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Request/CheckRequest.cs +++ /dev/null @@ -1,42 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.Core.Misc; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Request -{ - - public sealed class CheckRequest : RequestBase - { - // \check\\nick\\email\\partnerid\0\passenc\\gamename\gmtest\final\ - public CheckRequest(string rawRequest) : base(rawRequest) - { - } - - public string Nick { get; private set; } - public string Password { get; private set; } - public string Email { get; private set; } - public int? PartnerId { get; private set; } - public override void Parse() - { - base.Parse(); - Password = PasswordEncoder.ProcessPassword(RequestKeyValues); - if (!RequestKeyValues.ContainsKey("nick") || !RequestKeyValues.ContainsKey("email") || Password is null) - { - throw new GPParseException("check request is incompelete."); - } - - if (!GameSpyUtils.IsEmailFormatCorrect(RequestKeyValues["email"])) - { - throw new GPParseException("email format is incorrect"); - } - - Nick = RequestKeyValues["nick"]; - Email = RequestKeyValues["email"]; - - if (RequestKeyValues.ContainsKey("partnerid")) - { - PartnerId = int.Parse(RequestKeyValues["partnerid"]); - } - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Request/NewUserRequest.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Request/NewUserRequest.cs deleted file mode 100755 index ba3bcff4d..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Request/NewUserRequest.cs +++ /dev/null @@ -1,110 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.Core.Misc; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Request -{ - - public sealed class NewUserRequest : RequestBase - { - public int ProductID { get; private set; } - public int GamePort { get; private set; } - public string CDKey { get; private set; } - public bool HasGameNameFlag { get; private set; } - public bool HasProductIDFlag { get; private set; } - public bool HasCDKeyEncFlag { get; private set; } - public bool HasPartnerIDFlag { get; private set; } - public bool HasGamePortFlag { get; private set; } - public string Nick { get; private set; } - public string Email { get; private set; } - public string Password { get; private set; } - public string Uniquenick { get; private set; } - public int PartnerID { get; private set; } - public string GameName { get; private set; } - public int NamespaceID { get; private set; } - public NewUserRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - Password = PasswordEncoder.ProcessPassword(RequestKeyValues); - - if (!RequestKeyValues.ContainsKey("nick")) - { - throw new GPParseException("nickname is missing."); - } - if (!RequestKeyValues.ContainsKey("email")) - { - throw new GPParseException("email is missing."); - } - if (!GameSpyUtils.IsEmailFormatCorrect(RequestKeyValues["email"])) - { - throw new GPParseException("email format is incorrect."); - } - Nick = RequestKeyValues["nick"]; - Email = RequestKeyValues["email"]; - - if (RequestKeyValues.ContainsKey("uniquenick") && RequestKeyValues.ContainsKey("namespaceid")) - { - if (RequestKeyValues.ContainsKey("namespaceid")) - { - int namespaceID; - if (!int.TryParse(RequestKeyValues["namespaceid"], out namespaceID)) - { - throw new GPParseException("namespaceid is incorrect."); - } - NamespaceID = namespaceID; - } - Uniquenick = RequestKeyValues["uniquenick"]; - } - ParseOtherInfo(); - } - - private void ParseOtherInfo() - { - //parse other info - if (RequestKeyValues.ContainsKey("partnerid")) - { - int partnerid; - if (!int.TryParse(RequestKeyValues["partnerid"], out partnerid)) - { - throw new GPParseException("partnerid is incorrect."); - } - HasPartnerIDFlag = true; - PartnerID = partnerid; - } - if (RequestKeyValues.ContainsKey("productid")) - { - int productid; - if (!int.TryParse(RequestKeyValues["productid"], out productid)) - { - throw new GPParseException("productid is incorrect."); - } - HasProductIDFlag = true; - ProductID = productid; - } - if (RequestKeyValues.ContainsKey("gamename")) - { - HasGameNameFlag = true; - GameName = RequestKeyValues["gamename"]; - } - if (RequestKeyValues.ContainsKey("port")) - { - int port; - if (!int.TryParse(RequestKeyValues["port"], out port)) - { - throw new GPParseException("port is incorrect."); - } - HasGamePortFlag = true; - GamePort = port; - } - if (RequestKeyValues.ContainsKey("cdkey")) - { - HasCDKeyEncFlag = true; - CDKey = RequestKeyValues["cdkey"]; - } - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Request/NicksRequest.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Request/NicksRequest.cs deleted file mode 100755 index b2d3e5fdb..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Request/NicksRequest.cs +++ /dev/null @@ -1,50 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.Core.Misc; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Request -{ - - public sealed class NicksRequest : RequestBase - { - public NicksRequest(string rawRequest) : base(rawRequest) - { - } - - public string Password { get; private set; } - public string Email { get; private set; } - public int NamespaceID { get; private set; } - public bool IsRequireUniqueNicks { get; private set; } - - public override void Parse() - { - base.Parse(); - Password = PasswordEncoder.ProcessPassword(RequestKeyValues); - - if (!RequestKeyValues.ContainsKey("email")) - { - throw new GPParseException("email is missing."); - } - - IsRequireUniqueNicks = true; - - if (RequestKeyValues.ContainsKey("pass")) - { - // Old games might send an error is unique nicknames are sent (like GSA 1.0) - IsRequireUniqueNicks = false; - } - - Email = RequestKeyValues["email"]; - - if (RequestKeyValues.ContainsKey("namespaceid")) - { - int namespaceID; - if (!int.TryParse(RequestKeyValues["namespaceid"], out namespaceID)) - { - throw new GPParseException("namespaceid is incorrect."); - } - NamespaceID = namespaceID; - } - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Request/OthersListRequest.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Request/OthersListRequest.cs deleted file mode 100755 index a0953363b..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Request/OthersListRequest.cs +++ /dev/null @@ -1,50 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using System.Collections.Generic; -using System.Linq; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Request -{ - - //request: \otherslist\sesskey\\profileid\\numopids\ - //\opids\|||******\namespaceid\<>\gamename\<>\final\ - - public sealed class OthersListRequest : RequestBase - { - public List ProfileIDs { get; private set; } - public int NamespaceID { get; private set; } - public OthersListRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - - if (!RequestKeyValues.ContainsKey("opids") || !RequestKeyValues.ContainsKey("namespaceid")) - { - throw new GPParseException("opids or namespaceid is missing."); - } - - try - { - ProfileIDs = RequestKeyValues["opids"].TrimStart('|').Split('|').Select(int.Parse).ToList(); - } - catch (System.Exception e) - { - throw new GPParseException("opids is incorrect", e); - } - - if (RequestKeyValues.ContainsKey("namespaceid")) - { - int namespaceID; - if (!int.TryParse(RequestKeyValues["namespaceid"], out namespaceID)) - { - throw new GPParseException("namespaceid is incorrect."); - } - - NamespaceID = namespaceID; - } - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Request/OthersRequest.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Request/OthersRequest.cs deleted file mode 100755 index 09e7c508c..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Request/OthersRequest.cs +++ /dev/null @@ -1,52 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Request -{ - - public sealed class OthersRequest : RequestBase - { - public OthersRequest(string rawRequest) : base(rawRequest) - { - } - - public int ProfileId { get; private set; } - public string GameName { get; private set; } - public int NamespaceID { get; private set; } - public override void Parse() - { - base.Parse(); - - - if (!RequestKeyValues.ContainsKey("gamename")) - { - throw new GPParseException("gamename is missing."); - } - - if (!RequestKeyValues.ContainsKey("profileid") || !RequestKeyValues.ContainsKey("namespaceid")) - { - throw new GPParseException("profileid or namespaceid is missing."); - } - - int profileID = 0; - if (!RequestKeyValues.ContainsKey("profileid") && !int.TryParse(RequestKeyValues["profileid"], out profileID)) - { - throw new GPParseException("profileid is incorrect."); - } - - if (RequestKeyValues.ContainsKey("namespaceid")) - { - int namespaceID; - if (!int.TryParse(RequestKeyValues["namespaceid"], out namespaceID)) - { - throw new GPParseException("namespaceid is incorrect."); - } - - NamespaceID = namespaceID; - } - - ProfileId = profileID; - GameName = RequestKeyValues["gamename"]; - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Request/SearchRequest.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Request/SearchRequest.cs deleted file mode 100755 index f6b87c8ec..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Request/SearchRequest.cs +++ /dev/null @@ -1,118 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Request -{ - public enum SearchRequestType - { - NickSearch, - NickEmailSearch, - UniquenickNamespaceIDSearch, - EmailSearch - } - - - public sealed class SearchRequest : RequestBase - { - public int SkipNum { get; private set; } - public SearchRequestType RequestType { get; private set; } - public string GameName { get; private set; } - public int ProfileId { get; private set; } - public int PartnerID { get; private set; } - public string Email { get; private set; } - public string Nick { get; private set; } - public string Uniquenick { get; private set; } - public int NamespaceID { get; private set; } - public SearchRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - - - if (!RequestKeyValues.ContainsKey("profileid") - && !RequestKeyValues.ContainsKey("nick") - && !RequestKeyValues.ContainsKey("email") - && !RequestKeyValues.ContainsKey("namespaceid") - && !RequestKeyValues.ContainsKey("gamename")) - { - throw new GPParseException(" Search request is incomplete."); - } - - if (RequestKeyValues.ContainsKey("gamename")) - { - GameName = RequestKeyValues["gamename"]; - } - - if (RequestKeyValues.ContainsKey("profileid")) - { - int profileID; - if (!int.TryParse(RequestKeyValues["profileid"], out profileID)) - { - throw new GPParseException("profileid is incorrect."); - - } - ProfileId = profileID; - } - - if (RequestKeyValues.ContainsKey("partnerid")) - { - int partnerID; - if (!int.TryParse(RequestKeyValues["partnerid"], out partnerID)) - { - throw new GPParseException("partnerid is incorrect."); - } - PartnerID = partnerID; - } - - if (RequestKeyValues.ContainsKey("skip")) - { - int skip; - if (!int.TryParse(RequestKeyValues["skip"], out skip)) - { - throw new GPParseException("skip number is incorrect."); - } - SkipNum = skip; - } - - if (RequestKeyValues.ContainsKey("uniquenick") && RequestKeyValues.ContainsKey("namespaceid")) - { - if (RequestKeyValues.ContainsKey("namespaceid")) - { - int namespaceID; - if (!int.TryParse(RequestKeyValues["namespaceid"], out namespaceID)) - { - throw new GPParseException("namespaceid is incorrect."); - } - NamespaceID = namespaceID; - } - RequestType = SearchRequestType.UniquenickNamespaceIDSearch; - - Uniquenick = RequestKeyValues["uniquenick"]; - } - else if (RequestKeyValues.ContainsKey("nick") && RequestKeyValues.ContainsKey("email")) - { - RequestType = SearchRequestType.NickEmailSearch; - Nick = RequestKeyValues["nick"]; - Email = RequestKeyValues["email"]; - } - else if (RequestKeyValues.ContainsKey("nick")) - { - RequestType = SearchRequestType.NickSearch; - Nick = RequestKeyValues["nick"]; - } - else if (RequestKeyValues.ContainsKey("email")) - { - //\search\\sesskey\0\profileid\0\namespaceid\1\email\spyguy@gamespy.cn\gamename\conflictsopc\final\ - Email = RequestKeyValues["email"]; - RequestType = SearchRequestType.EmailSearch; - } - else - { - throw new GPParseException("unknown search request type."); - } - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Request/SearchUniqueRequest.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Request/SearchUniqueRequest.cs deleted file mode 100755 index b01d1cebe..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Request/SearchUniqueRequest.cs +++ /dev/null @@ -1,45 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using System.Collections.Generic; -using System.Linq; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Request -{ - - public sealed class SearchUniqueRequest : RequestBase - { - public string Uniquenick { get; private set; } - public List NamespaceIds { get; private set; } - public SearchUniqueRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - - if (!RequestKeyValues.ContainsKey("uniquenick") || !RequestKeyValues.ContainsKey("namespaces")) - { - throw new GPParseException("searchunique request is incomplete."); - } - - try - { - Uniquenick = RequestKeyValues["uniquenick"]; - } - catch - { - throw new GPParseException("uniquenick is missing."); - } - - try - { - NamespaceIds = RequestKeyValues["namespaces"].TrimStart(',').Split(',').Select(int.Parse).ToList(); - } - catch - { - throw new GPParseException("namespaces is incorrect."); - } - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Request/UniqueSearchRequest.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Request/UniqueSearchRequest.cs deleted file mode 100755 index 37a724dde..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Request/UniqueSearchRequest.cs +++ /dev/null @@ -1,50 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Request -{ - - - public sealed class UniqueSearchRequest : RequestBase - { - public string PreferredNick { get; private set; } - public int NamespaceID { get; private set; } - public UniqueSearchRequest(string rawRequest) : base(rawRequest) - { - } - - public string GameName { get; private set; } - - public override void Parse() - { - base.Parse(); - - - if (!RequestKeyValues.ContainsKey("preferrednick")) - { - throw new GPParseException("preferrednick is missing."); - } - - PreferredNick = RequestKeyValues["preferrednick"]; - - if (!RequestKeyValues.ContainsKey("gamename")) - { - throw new GPParseException("gamename is missing."); - } - GameName = RequestKeyValues["gamename"]; - - if (!RequestKeyValues.ContainsKey("namespaceid")) - { - throw new GPParseException("namespaceid is missing."); - } - - int namespaceID; - if (!int.TryParse(RequestKeyValues["namespaceid"], out namespaceID)) - { - throw new GPParseException("namespaceid is incorrect."); - } - - NamespaceID = namespaceID; - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Request/ValidRequest.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Request/ValidRequest.cs deleted file mode 100755 index f636289fe..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Request/ValidRequest.cs +++ /dev/null @@ -1,42 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.Core.Misc; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Request -{ - - public sealed class ValidRequest : RequestBase - { - public ValidRequest(string rawRequest) : base(rawRequest) - { - } - - public int NamespaceID { get; private set; } - public string Email { get; private set; } - public string GameName { get; private set; } - - public override void Parse() - { - base.Parse(); - - - if (!RequestKeyValues.ContainsKey("email") && !GameSpyUtils.IsEmailFormatCorrect(RequestKeyValues["email"])) - { - throw new GPParseException("valid request is incomplete."); - } - - Email = RequestKeyValues["email"]; - - if (RequestKeyValues.ContainsKey("namespaceid")) - { - int namespaceID; - if (!int.TryParse(RequestKeyValues["namespaceid"], out namespaceID)) - { - throw new GPParseException("namespaceid is incorrect."); - } - - NamespaceID = namespaceID; - } - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Response/CheckResponse.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Response/CheckResponse.cs deleted file mode 100755 index aaafdbe52..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Response/CheckResponse.cs +++ /dev/null @@ -1,26 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Response -{ - public sealed class CheckResponse : ResponseBase - { - private new CheckResult _result => (CheckResult)base._result; - - public CheckResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - - public override void Build() - { - if (_result.ProfileId is null) - { - SendingBuffer = $@"\cur\1\final\"; - } - else - { - SendingBuffer = $@"\cur\0\pid\{_result.ProfileId}\final\"; - } - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Response/NewUserResponse.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Response/NewUserResponse.cs deleted file mode 100755 index 07e7e0a6d..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Response/NewUserResponse.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Response -{ - public class NewUserResponse : ResponseBase - { - protected new NewUserRequest _request => (NewUserRequest)base._request; - protected new NewUserResult _result => (NewUserResult)base._result; - public NewUserResponse(NewUserRequest request, NewUserResult result) : base(request, result) - { - } - public override void Build() - { - SendingBuffer = $@"\nur\\pid\{_result.SubProfile.Profileid}\final\"; - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Response/NicksResponse.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Response/NicksResponse.cs deleted file mode 100755 index 547d28cf6..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Response/NicksResponse.cs +++ /dev/null @@ -1,32 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Response -{ - public sealed class NicksResponse : ResponseBase - { - private new NicksRequest _request => (NicksRequest)base._request; - private new NicksResult _result => (NicksResult)base._result; - - public NicksResponse(NicksRequest request, NicksResult result) : base(request, result) - { - } - - public override void Build() - { - SendingBuffer = @"\nr\"; - foreach (var info in _result.DataBaseResults) - { - SendingBuffer += @"\nick\"; - SendingBuffer += info.NickName; - if (_request.IsRequireUniqueNicks) - { - SendingBuffer += @"\uniquenick\"; - SendingBuffer += info.UniqueNick; - } - } - SendingBuffer += @"\ndone\final\"; - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Response/OthersListResponse.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Response/OthersListResponse.cs deleted file mode 100755 index a6eb7391f..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Response/OthersListResponse.cs +++ /dev/null @@ -1,25 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Response -{ - public sealed class OthersListResponse : ResponseBase - { - private new OthersListResult _result => (OthersListResult)base._result; - public OthersListResponse(RequestBase request, OthersListResult result) : base(request, result) - { - } - - public override void Build() - { - SendingBuffer = @"\otherslist\"; - foreach (var info in _result.DatabaseResults) - { - SendingBuffer += $@"\o\{info.ProfileId}"; - SendingBuffer += $@"\uniquenick\{info.Uniquenick}"; - } - - SendingBuffer += @"oldone"; - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Response/OthersResponse.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Response/OthersResponse.cs deleted file mode 100755 index 749821d3f..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Response/OthersResponse.cs +++ /dev/null @@ -1,29 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Response -{ - public sealed class OthersResponse : ResponseBase - { - private new OthersResult _result => (OthersResult)base._result; - public OthersResponse(RequestBase request, OthersResult result) : base(request, result) - { - } - - public override void Build() - { - SendingBuffer = @"\others\"; - - foreach (var info in _result.DatabaseResults) - { - SendingBuffer += @"\o\" + info.ProfileId; - SendingBuffer += @"\nick\" + info.Nick; - SendingBuffer += @"\uniquenick\" + info.Uniquenick; - SendingBuffer += @"\first\" + info.Firstname; - SendingBuffer += @"\last\" + info.Lastname; - SendingBuffer += @"\email\" + info.Email; - } - SendingBuffer += @"\odone\final\"; - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Response/SearchResponse.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Response/SearchResponse.cs deleted file mode 100755 index f27525567..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Response/SearchResponse.cs +++ /dev/null @@ -1,32 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Response -{ - public sealed class SearchResponse : ResponseBase - { - private new SearchResult _result => (SearchResult)base._result; - - public SearchResponse(RequestBase request, SearchResult result) : base(request, result) - { - } - - - public override void Build() - { - SendingBuffer = @"\bsr\"; - foreach (var info in _result.DataBaseResults) - { - SendingBuffer += info.ProfileId.ToString(); - SendingBuffer += @"\nick\" + info.Nick; - SendingBuffer += @"\uniquenick\" + info.Uniquenick; - SendingBuffer += @"\namespaceid\" + info.NamespaceID; - SendingBuffer += @"\firstname\" + info.Firstname; - SendingBuffer += @"\lastname\" + info.Lastname; - SendingBuffer += @"\email\" + info.Email; - } - //TODO check how many user data is allowed send to the client - SendingBuffer += @"\bsrdone\\more\0\final\"; - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Response/SearchUniqueResponse.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Response/SearchUniqueResponse.cs deleted file mode 100755 index 7bce0b6ea..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Response/SearchUniqueResponse.cs +++ /dev/null @@ -1,30 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Response -{ - public sealed class SearchUniqueResponse : ResponseBase - { - private new SearchUniqueResult _result => (SearchUniqueResult)base._result; - - public SearchUniqueResponse(RequestBase request, SearchUniqueResult result) : base(request, result) - { - } - - public override void Build() - { - SendingBuffer = @"\bsr"; - foreach (var info in _result.DatabaseResults) - { - SendingBuffer += info.ProfileId.ToString(); - SendingBuffer += @"\nick\" + info.Nick; - SendingBuffer += @"\uniquenick\" + info.Uniquenick; - SendingBuffer += @"\namespaceid\" + info.NamespaceID; - SendingBuffer += @"\firstname\" + info.Firstname; - SendingBuffer += @"\lastname\" + info.Lastname; - SendingBuffer += @"\email\" + info.Email; - } - SendingBuffer += @"\bsrdone\\more\0"; - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Response/UniqueSearchResponse.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Response/UniqueSearchResponse.cs deleted file mode 100755 index cc0eb56ba..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Response/UniqueSearchResponse.cs +++ /dev/null @@ -1,28 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Response -{ - public sealed class UniqueSearchResponse : ResponseBase - { - private new UniqueSearchRequest _request => (UniqueSearchRequest)base._request; - private new UniqueSearchResult _result => (UniqueSearchResult)base._result; - public UniqueSearchResponse(UniqueSearchRequest request, UniqueSearchResult result) : base(request, result) - { - } - - - public override void Build() - { - if (_result.IsUniquenickExist) - { - SendingBuffer = @"\us\1\nick\Choose another name\usdone\final\"; - } - else - { - SendingBuffer = $@"\us\1\nick\{_request.PreferredNick}\usdone\final\"; - } - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Response/ValidResponse.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Response/ValidResponse.cs deleted file mode 100755 index 5f893547a..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Response/ValidResponse.cs +++ /dev/null @@ -1,27 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Response -{ - public sealed class ValidResponse : ResponseBase - { - private new ValidResult _result => (ValidResult)base._result; - private new ValidRequest _request => (ValidRequest)base._request; - public ValidResponse(RequestBase request, ValidResult result) : base(request, result) - { - } - - public override void Build() - { - if (_result.IsAccountValid) - { - SendingBuffer = @"\vr\1\final\"; - } - else - { - SendingBuffer = @"\vr\0\final\"; - } - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Result/CheckResult.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Result/CheckResult.cs deleted file mode 100755 index 4bc894f1e..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Result/CheckResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Result -{ - public sealed class CheckResult : ResultBase - { - public int? ProfileId { get; set; } - public CheckResult() - { - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Result/NewUserResult.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Result/NewUserResult.cs deleted file mode 100755 index b299143b0..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Result/NewUserResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.Core.Database.DatabaseModel; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Result -{ - public sealed class NewUserResult : ResultBase - { - public User User; - public Profile Profile; - public Subprofile SubProfile; - public NewUserResult() { } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Result/NicksResult.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Result/NicksResult.cs deleted file mode 100755 index 0849aff9c..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Result/NicksResult.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using System.Collections.Generic; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Result -{ - public sealed class NicksDataModel - { - public string NickName; - public string UniqueNick; - } - - public sealed class NicksResult : ResultBase - { - public List DataBaseResults { get; set; } - public bool IsRequireUniqueNicks { get; set; } - public NicksResult() - { - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Result/OthersListResult.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Result/OthersListResult.cs deleted file mode 100755 index ca993ed6d..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Result/OthersListResult.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using System.Collections.Generic; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Result -{ - public sealed class OthersListDatabaseModel - { - public int ProfileId; - public string Uniquenick; - } - public sealed class OthersListResult : ResultBase - { - public List DatabaseResults; - public OthersListResult() - { - DatabaseResults = new List(); - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Result/OthersResult.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Result/OthersResult.cs deleted file mode 100755 index ea825483f..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Result/OthersResult.cs +++ /dev/null @@ -1,24 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using System.Collections.Generic; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Result -{ - public sealed class OthersDatabaseModel - { - public int ProfileId; - public string Nick; - public string Uniquenick; - public string Lastname; - public string Firstname; - public int Userid; - public string Email; - } - - public sealed class OthersResult : ResultBase - { - public List DatabaseResults { get; set; } - public OthersResult() - { - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Result/PSPDefaultResult.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Result/PSPDefaultResult.cs deleted file mode 100755 index 540099ce0..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Result/PSPDefaultResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Result -{ - public sealed class PSPDefaultResult : ResultBase - { - public PSPDefaultResult() - { - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Result/SearchResult.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Result/SearchResult.cs deleted file mode 100755 index 0b26f5de9..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Result/SearchResult.cs +++ /dev/null @@ -1,25 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using System.Collections.Generic; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Result -{ - public sealed class SearchDataBaseModel - { - public int ProfileId; - public string Nick; - public string Uniquenick; - public string Email; - public string Firstname; - public string Lastname; - public int NamespaceID; - } - - public sealed class SearchResult : ResultBase - { - public List DataBaseResults; - public SearchResult() - { - DataBaseResults = new List(); - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Result/SearchUniqueResult.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Result/SearchUniqueResult.cs deleted file mode 100755 index 80655dce1..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Result/SearchUniqueResult.cs +++ /dev/null @@ -1,24 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using System.Collections.Generic; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Result -{ - public sealed class SearchUniqueDatabaseModel - { - public int ProfileId; - public string Nick; - public string Uniquenick; - public string Email; - public string Firstname; - public string Lastname; - public int NamespaceID; - } - - public sealed class SearchUniqueResult : ResultBase - { - public List DatabaseResults { get; set; } - public SearchUniqueResult() - { - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Result/UniqueSearchResult.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Result/UniqueSearchResult.cs deleted file mode 100755 index bd62ff6a3..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Result/UniqueSearchResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Result -{ - public sealed class UniqueSearchResult : ResultBase - { - public bool IsUniquenickExist; - public UniqueSearchResult() - { - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Contract/Result/ValidResult.cs b/src/Servers/PresenceSearchPlayer/src/Contract/Result/ValidResult.cs deleted file mode 100755 index 2c05c5f6e..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Contract/Result/ValidResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; - -namespace UniSpy.Server.PresenceSearchPlayer.Contract.Result -{ - public sealed class ValidResult : ResultBase - { - public bool IsAccountValid; - public ValidResult() - { - IsAccountValid = false; - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Dockerfile b/src/Servers/PresenceSearchPlayer/src/Dockerfile deleted file mode 100755 index 9b32fe7d1..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base -WORKDIR /app -EXPOSE 29901 - -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build -WORKDIR /src -COPY ["src/Servers/PresenceSearchPlayer/src/UniSpy.Server.PresenceSearchPlayer.csproj", "src/Servers/PresenceSearchPlayer/src/"] -COPY ["src/Libraries/Core/src/UniSpy.Server.Core.csproj", "src/Libraries/Core/src/"] -RUN dotnet restore "src/Servers/PresenceSearchPlayer/src/UniSpy.Server.PresenceSearchPlayer.csproj" -COPY . . -WORKDIR "/src/src/Servers/PresenceSearchPlayer/src" -RUN dotnet build "UniSpy.Server.PresenceSearchPlayer.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "UniSpy.Server.PresenceSearchPlayer.csproj" -c Release -o /app/publish - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "UniSpy.Server.PresenceSearchPlayer.dll"] \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Enumerator/GPEnum.cs b/src/Servers/PresenceSearchPlayer/src/Enumerator/GPEnum.cs deleted file mode 100755 index 68c1cbd22..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Enumerator/GPEnum.cs +++ /dev/null @@ -1,208 +0,0 @@ -namespace UniSpy.Server.PresenceSearchPlayer.Enumerate -{ - public enum GPEnum : uint - { - // Callbacks - //////////// - Error = 0, - RecvBuddyRequest, - RecvBuddyStatus, - RecvBuddyMessage, - RecvBuddyUTM, - RecvGameInvite, - TransferCallback, - RecvBuddyAuth, - RecvBuddyRevoke, - - // Global States. - ///////////////// - InfoCaching = 0x0100, - Simulation, - InfoChachingBuddyAndBlockOnly, - - // Blocking - /////////// - Blocking = 1, - NonBlocking = 0, - - // Firewall - /////////// - Firewall = 1, - NoFirewall = 0, - - // Check Cache - ////////////// - CheckCache = 1, - DontCheckCache = 0, - - // Is Valid Email. - // PANTS|02.15.00 - ////////////////// - EmailValid = 1, - EmailInvalid = 0, - - // Fatal Error. - /////////////// - Fatal = 1, - NonFatal = 0, - - // Sex - ////// - Male = 0x0500, - Fefamle, - PAT, - - // Profile Search. - ////////////////// - More = 0x0600, - Done, - - // Set Info - /////////// - Nick = 0x0700, - Uniquenick, - Email, - Password, - FirstName, - LastName, - ICQUIN, - HomePage, - ZIPCode, - CountryCode, - Birthday, - Sex, - CPUBrand, - CPUSpeed, - Memory, - VideoCard1String, - VideoCard1RAM, - VideoCard2String, - VideoCard2RAM, - ConnectionID, - ConnectionSpeed, - HasNetwork, - OSString, - AIMName, // PANTS|03.20.01 - PIC, - OccupationID, - IndustryID, - InComeID, - MarriedID, - ChildCount, - Interest1, - - // New Profile. - /////////////// - Replace = 1, - DontReplace = 0, - - // Is Connected. - //////////////// - Connected = 1, - NotConnected = 0, - - // Public mask. - /////////////// - MaskNone = 0x00000000, - MaskHomepage = 0x00000001, - MaskZIPCode = 0x00000002, - MaskContryCode = 0x00000004, - MaskBirthday = 0x00000008, - MaskSex = 0x00000010, - MaskEmail = 0x00000020, - MaskAll = 0xFFFFFFFF, - - // Status - ///////// - Offline = 0, - Online = 1, - Playing = 2, - Staging = 3, - Chatting = 4, - Away = 5, - - // Session flags - ///////////////// - SessIsClosed = 0x00000001, - SessIsOpen = 0x00000002, - SessHasPassword = 0x00000004, - SessIsBehindNAT = 0x00000008, - SessIsRanked = 0x000000010, - - - // CPU Brand ID - /////////////// - Intel = 1, - AMD, - CYRIX, - Motorola, - Alpha, - - // Connection ID. - ///////////////// - Modem = 1, - ISDN, - CableModem, - DSL, - Satellite, - Ethernet, - Wireless, - - // Transfer callback type. - // *** the transfer is ended when these types are received - ////////////////////////// - TransferSendRequest = 0x800, // arg->num == numFiles - TransferAccepted, - TransferRejected, // *** - TransferNotAccepting, // *** - TransferNoConnection, // *** - TransferDone, // *** - TransferCancelled, // *** - TransferLostConnection, // *** - TransferError, // *** - TransferThrottle, // arg->num == Bps - FileBegin, - FileProgress, // arg->num == numBytes - FileEnd, - FileDirectory, - FileSkip, - FileFaild, // arg->num == error - - // FILE_FAILED error - /////////////////////// - FileReadError = 0x900, - FileWriteError, - FileDataError, - - // Transfer Side. - ///////////////// - TransferSender = 0xA00, - TansferReciever, - - // UTM send options. - //////////////////// - DontRout = 0xB00, // only send direct - - // Quiet mode flags. - //////////////////// - SlienceNone = 0x00000000, - SlienceMessage = 0x00000001, - SlienceUTMS = 0x00000002, - SlienceList = 0x00000004, // includes requests, auths, and revokes - SlienceAll = 0xFFFFFFFF, - - NewStatusInfoSupported = 0xC00, - NewStatusInfoNotSupported = 0xC01 - } - - public enum GPSPResult : int - { - NoError, - MemoryError, - ParameterError, - NetworkError, - ServerError, - MISCError, - Count - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Enumerator/GPErrorCode.cs b/src/Servers/PresenceSearchPlayer/src/Enumerator/GPErrorCode.cs deleted file mode 100755 index c215495ad..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Enumerator/GPErrorCode.cs +++ /dev/null @@ -1,142 +0,0 @@ -namespace UniSpy.Server.PresenceSearchPlayer.Enumerate -{ - public enum GPErrorCode : int - { - // General. - /////////// - General = 0x0000, - Parse, - NotLoggedIn, - BadSessionKey, - DatabaseError, - Network, - ForcedDisconnect, - ConnectionClose, - UdpLayer, - - // Login. - ///////// - Login = 0x0100, - LoginTimeOut, - LoginBadNick, - LoginBadEmail, - LoginBadPassword, - LoginBadProfile, - LoginProfileDeleted, - LoginConnectionFailed, - LoginServerAuthFaild, - LoginBadUniquenick, - LoginBadPreAuth, - LoginBadLoginTicket, - LoginTicketExpired, - // Newuser. - /////////// - NewUser = 0x0200, - NewUserBadNick, - NewUserBadPasswords, - NewUserUniquenickInvalid, - NewUserUniquenickInUse, - - // Updateui. - //////////// - UpdateUI = 0x0300, - UpdateUIBadEmail, - - // Newprofile. - ////////////// - NewProfile = 0x0400, - NewProfileBadNick, - NewProfileBadOldNick, - - // Updatepro. - ///////////// - UpdatePro = 0x0500, - UpdateProBadNick, - - // Addbuddy. - //////////// - AddBuddy = 0x0600, - AddBuddyBadForm, - AddBuddyBadNew, - AddBuddyAlreadyBuddy, - - // Authadd. - /////////// - AuthAdd = 0x0700, - AuthAddBadForm, - AuthAddBadSig, - - // Status. - ////////// - Status = 0x0800, - - // Bm. - ////// - Bm = 0x0900, - BmNotBuddy, - BmExtInfoNotSupported, - BmBuddyOffline, - - // Getprofile. - ////////////// - GetProfile = 0x0A00, - GetProfileBadProfile, - - // Delbuddy. - //////////// - DelBuddy = 0x0B00, - DelBuddyNotBuddy, - - // Delprofile. - ///////////// - DelProfile = 0x0C00, - DelProfileLastProfile, - - // Search. - ////////// - Search = 0x0D00, - SearchConnectionFailed, - SearchTimeOut, - - // Check. - ///////// - Check = 0x0E00, - CheckBadMail, - CheckBadNick, - CheckBadPassword, - - // Revoke. - ////////// - Revoke = 0x0F00, - RevokeNotBuddy, - - // Registeruniquenick. - ////////////////////// - RegisterUniquenick = 0x1000, - RegisterUniquenickTaken, - RegisterUniquenickReserved, - RegisterUniquenickBadNamespace, - - // Register UniSpy.Server.CDkey. - ////////////////// - RegisterCDKey = 0x1100, - RegisterCDKeyBadKey, - RegisterCDKeyAlreadySet, - RegisterCDKeyAlreadyTaken, - - // AddBlock. - //////////// - AddBlock = 0x1200, - AddBlockAlreadyBlocked, - - // RemoveBlock. - /////////////// - RemoveBlock = 0x1300, - RemoveBlockNotBlocked, - - - // RetroSpy self defined NoError code - /////////////// - NoError = 0xffff, - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Enumerator/NewUserStatus.cs b/src/Servers/PresenceSearchPlayer/src/Enumerator/NewUserStatus.cs deleted file mode 100755 index 829d2da42..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Enumerator/NewUserStatus.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace UniSpy.Server.PresenceSearchPlayer.Entity.Enumerator -{ - public enum NewUserStatus - { - CheckAccount, - AccountNotExist, - AccountExist, - CheckProfile, - ProfileNotExist, - ProfileExist, - CheckSubProfile, - SubProfileNotExist, - SubProfileExist - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/AddBuddy/GPAddBuddyAlreadyBuddyException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/AddBuddy/GPAddBuddyAlreadyBuddyException.cs deleted file mode 100755 index 03c28d5fb..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/AddBuddy/GPAddBuddyAlreadyBuddyException.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.AddBuddy -{ - public class GPAddBuddyAlreadyBuddyException : GPException - { - public GPAddBuddyAlreadyBuddyException() : base("The buddy you are adding is already in your buddy list!", GPErrorCode.AddBlockAlreadyBlocked) - { - } - - public GPAddBuddyAlreadyBuddyException(string message) : base(message, GPErrorCode.AddBuddyAlreadyBuddy) - { - } - - public GPAddBuddyAlreadyBuddyException(string message, System.Exception innerException) : base(message, GPErrorCode.AddBuddyAlreadyBuddy, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/AddBuddy/GPAddBuddyBadFormException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/AddBuddy/GPAddBuddyBadFormException.cs deleted file mode 100755 index da4e92ec5..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/AddBuddy/GPAddBuddyBadFormException.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.AddBuddy -{ - public class GPAddBuddyBadFormException : GPException - { - public GPAddBuddyBadFormException() : base("Add buddy format invalid!", GPErrorCode.AddBuddyBadForm) - { - } - - public GPAddBuddyBadFormException(string message) : base(message, GPErrorCode.AddBuddyBadForm) - { - } - - public GPAddBuddyBadFormException(string message, System.Exception innerException) : base(message, GPErrorCode.AddBuddyBadForm, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/AddBuddy/GPAddBuddyBadNewException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/AddBuddy/GPAddBuddyBadNewException.cs deleted file mode 100755 index a862ee3bf..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/AddBuddy/GPAddBuddyBadNewException.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.AddBuddy -{ - public class GPAddBuddyBadNewException : GPException - { - public GPAddBuddyBadNewException() : base("The buddy name provided is invalid!", GPErrorCode.AddBuddyBadNew) - { - } - - public GPAddBuddyBadNewException(string message) : base(message, GPErrorCode.AddBuddyBadNew) - { - } - - public GPAddBuddyBadNewException(string message, System.Exception innerException) : base(message, GPErrorCode.AddBuddyBadNew, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/AddBuddy/GPAddBuddyException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/AddBuddy/GPAddBuddyException.cs deleted file mode 100755 index d11c3acd1..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/AddBuddy/GPAddBuddyException.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.AddBuddy -{ - public class GPAddBuddyException : GPException - { - public GPAddBuddyException() : base("Unknown error occur at add buddy!", GPErrorCode.AddBuddy) - { - } - - public GPAddBuddyException(string message) : base(message, GPErrorCode.AddBuddy) - { - } - - public GPAddBuddyException(string message, System.Exception innerException) : base(message, GPErrorCode.AddBuddy, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/AuthAdd/GPAuthAddBadFormException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/AuthAdd/GPAuthAddBadFormException.cs deleted file mode 100755 index efc2f4567..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/AuthAdd/GPAuthAddBadFormException.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.AuthAdd -{ - public class GPAuthAddBadFormException : GPException - { - public GPAuthAddBadFormException() : base("The authentication is in bad form!", GPErrorCode.AuthAddBadForm) - { - } - - public GPAuthAddBadFormException(string message) : base(message, GPErrorCode.AuthAddBadForm) - { - } - - public GPAuthAddBadFormException(string message, System.Exception innerException) : base(message, GPErrorCode.AuthAddBadForm, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/AuthAdd/GPAuthAddBadSigExceptiion.cs b/src/Servers/PresenceSearchPlayer/src/Exception/AuthAdd/GPAuthAddBadSigExceptiion.cs deleted file mode 100755 index 2d3004024..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/AuthAdd/GPAuthAddBadSigExceptiion.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.AuthAdd -{ - public class GPAuthAddBadSigException : GPException - { - public GPAuthAddBadSigException() : base("The signature in authentication is invalid!", GPErrorCode.AuthAddBadSig) - { - } - - public GPAuthAddBadSigException(string message) : base(message, GPErrorCode.AuthAddBadSig) - { - } - - public GPAuthAddBadSigException(string message, System.Exception innerException) : base(message, GPErrorCode.AuthAddBadSig, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/AuthAdd/GPAuthAddException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/AuthAdd/GPAuthAddException.cs deleted file mode 100755 index aac9e6a16..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/AuthAdd/GPAuthAddException.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.AuthAdd -{ - public class GPAuthAddException : GPException - { - public GPAuthAddException() : base("The adding of authentication failed!", GPErrorCode.AuthAdd) - { - } - - public GPAuthAddException(string message) : base(message, GPErrorCode.AuthAdd) - { - } - - public GPAuthAddException(string message, System.Exception innerException) : base(message, GPErrorCode.AuthAdd, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/BuddyMsg/GPBuddyMsgException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/BuddyMsg/GPBuddyMsgException.cs deleted file mode 100755 index c64e19d9c..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/BuddyMsg/GPBuddyMsgException.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.BM -{ - public class GPBuddyMsgException : GPException - { - public GPBuddyMsgException() : base("Unknown error occur when processing buddy message!", GPErrorCode.Bm) - { - } - - public GPBuddyMsgException(string message) : base(message, GPErrorCode.Bm) - { - } - - public GPBuddyMsgException(string message, System.Exception innerException) : base(message, GPErrorCode.Bm, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/BuddyMsg/GPBuddyMsgExtInfoNotSupportedException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/BuddyMsg/GPBuddyMsgExtInfoNotSupportedException.cs deleted file mode 100755 index 72783b882..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/BuddyMsg/GPBuddyMsgExtInfoNotSupportedException.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception -{ - public class GPBuddyMsgExtInfoNotSupportedException : GPException - { - public GPBuddyMsgExtInfoNotSupportedException() : base("Buddy message is not supported.", GPErrorCode.BmExtInfoNotSupported) - { - } - - public GPBuddyMsgExtInfoNotSupportedException(string message) : base(message, GPErrorCode.BmExtInfoNotSupported) - { - } - - public GPBuddyMsgExtInfoNotSupportedException(string message, System.Exception innerException) : base(message, GPErrorCode.BmExtInfoNotSupported, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/BuddyMsg/GPBuddyMsgNotBuddyException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/BuddyMsg/GPBuddyMsgNotBuddyException.cs deleted file mode 100755 index 61d722575..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/BuddyMsg/GPBuddyMsgNotBuddyException.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.BM -{ - public class GPBuddyMsgNotBuddyException : GPException - { - public GPBuddyMsgNotBuddyException() : base("The message receiver is not your buddy!", GPErrorCode.BmNotBuddy) - { - } - - public GPBuddyMsgNotBuddyException(string message) : base(message, GPErrorCode.BmNotBuddy) - { - } - - public GPBuddyMsgNotBuddyException(string message, System.Exception innerException) : base(message, GPErrorCode.BmNotBuddy, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/Check/CheckException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/Check/CheckException.cs deleted file mode 100755 index 3bdeaa1cf..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/Check/CheckException.cs +++ /dev/null @@ -1,24 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Aggregate.Exception -{ - public class CheckException : GPException - { - public CheckException() : this("There was an error checking the user account.", GPErrorCode.Check) - { - } - - public CheckException(string message, GPErrorCode errorCode) : base(message, errorCode) - { - } - - public CheckException(string message, GPErrorCode errorCode, System.Exception innerException) : base(message, errorCode, innerException) - { - } - public override void Build() - { - SendingBuffer = $@"\cur\{(int)ErrorCode}\final\"; ; - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPBadSessionKeyException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/General/GPBadSessionKeyException.cs deleted file mode 100755 index 8acb6aab6..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPBadSessionKeyException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.General -{ - public class GPBadSessionKeyException : GPException - { - public GPBadSessionKeyException() : base("Your connection key is not valid!", GPErrorCode.BadSessionKey) - { - } - - public GPBadSessionKeyException(string message) : base(message, GPErrorCode.BadSessionKey) - { - } - - public GPBadSessionKeyException(string message, System.Exception innerException) : base(message, GPErrorCode.BadSessionKey, innerException) - { - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPConnectionCloseException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/General/GPConnectionCloseException.cs deleted file mode 100755 index fa8e67586..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPConnectionCloseException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.General -{ - public class GPConnectionCloseException : GPException - { - public GPConnectionCloseException() : base("Client connection accidently closed!", GPErrorCode.ConnectionClose) - { - } - - public GPConnectionCloseException(string message) : base(message, GPErrorCode.ConnectionClose) - { - } - - public GPConnectionCloseException(string message, System.Exception innerException) : base(message, GPErrorCode.ConnectionClose, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPDatabaseException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/General/GPDatabaseException.cs deleted file mode 100755 index eb2b3f6b5..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPDatabaseException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.General -{ - public class GPDatabaseException : GPException - { - public GPDatabaseException() : base("Database error!", GPErrorCode.DatabaseError) - { - } - - public GPDatabaseException(string message) : base(message, GPErrorCode.DatabaseError) - { - } - - public GPDatabaseException(string message, System.Exception innerException) : base(message, GPErrorCode.DatabaseError, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/General/GPException.cs deleted file mode 100755 index 5d006133d..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPException.cs +++ /dev/null @@ -1,39 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.General -{ - public class GPException : UniSpy.Exception, IResponse - { - public GPErrorCode ErrorCode { get; private set; } - public string SendingBuffer { get; protected set; } - object IResponse.SendingBuffer => SendingBuffer; - - public GPException() : this("General Error!", GPErrorCode.General) - { - } - - public GPException(string message) : this(message, GPErrorCode.General) - { - } - - public GPException(string message, System.Exception innerException) : this(message, GPErrorCode.General, innerException) - { - } - - public GPException(string message, GPErrorCode errorCode) : base(message) - { - ErrorCode = errorCode; - } - - public GPException(string message, GPErrorCode errorCode, System.Exception innerException) : base(message, innerException) - { - ErrorCode = errorCode; - } - - public virtual void Build() - { - SendingBuffer = $@"\error\\err\{(int)ErrorCode}\fatal\\errmsg\{this.Message}\final\"; - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPForcedDisconnectException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/General/GPForcedDisconnectException.cs deleted file mode 100755 index 925f34608..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPForcedDisconnectException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.General -{ - public class GPForcedDisconnectException : GPException - { - public GPForcedDisconnectException() : base("Client is forced to disconnect!", GPErrorCode.ForcedDisconnect) - { - } - - public GPForcedDisconnectException(string message) : base(message, GPErrorCode.ForcedDisconnect) - { - } - - public GPForcedDisconnectException(string message, System.Exception innerException) : base(message, GPErrorCode.ForcedDisconnect, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPNetworkException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/General/GPNetworkException.cs deleted file mode 100755 index faea36192..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPNetworkException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.General -{ - public class GPNetworkException : GPException - { - public GPNetworkException() : base("Unknown network error!", GPErrorCode.Network) - { - } - - public GPNetworkException(string message) : base(message, GPErrorCode.Network) - { - } - - public GPNetworkException(string message, System.Exception innerException) : base(message, GPErrorCode.Network, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPNotLoggedInException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/General/GPNotLoggedInException.cs deleted file mode 100755 index 7a7d8e9b6..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPNotLoggedInException.cs +++ /dev/null @@ -1,18 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -namespace UniSpy.Server.PresenceSearchPlayer.Exception.General -{ - public class GPNotLoggedInException : GPException - { - public GPNotLoggedInException() : base("You are not logged in, please login first!", GPErrorCode.NotLoggedIn) - { - } - - public GPNotLoggedInException(string message) : base(message, GPErrorCode.NotLoggedIn) - { - } - - public GPNotLoggedInException(string message, System.Exception innerException) : base(message, GPErrorCode.NotLoggedIn, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPParseException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/General/GPParseException.cs deleted file mode 100755 index f2b3c40ad..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPParseException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.General -{ - public class GPParseException : GPException - { - public GPParseException() : base("Request parsing error!", GPErrorCode.Parse) - { - } - - public GPParseException(string message) : base(message, GPErrorCode.Parse) - { - } - - public GPParseException(string message, System.Exception innerException) : base(message, GPErrorCode.Parse, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPUdpLayerException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/General/GPUdpLayerException.cs deleted file mode 100755 index b51536de8..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/General/GPUdpLayerException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.General -{ - public class GPUdpLayerException : GPException - { - public GPUdpLayerException() : base("Unkown UDP layer error!", GPErrorCode.UdpLayer) - { - } - - public GPUdpLayerException(string message) : base(message, GPErrorCode.UdpLayer) - { - } - - public GPUdpLayerException(string message, System.Exception innerException) : base(message, GPErrorCode.UdpLayer, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadEmailException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadEmailException.cs deleted file mode 100755 index 09b733235..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadEmailException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.Login -{ - public class GPLoginBadEmailException : GPLoginException - { - public GPLoginBadEmailException() : base("Email provided is invalid!", GPErrorCode.LoginBadEmail) - { - } - - public GPLoginBadEmailException(string message) : base(message, GPErrorCode.LoginBadEmail) - { - } - - public GPLoginBadEmailException(string message, System.Exception innerException) : base(message, GPErrorCode.LoginBadEmail, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadLoginTicketException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadLoginTicketException.cs deleted file mode 100755 index 9466b08e0..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadLoginTicketException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.Login -{ - public class GPLoginBadLoginTicketException : GPLoginException - { - public GPLoginBadLoginTicketException() : base("The login ticket is invalid!", GPErrorCode.LoginBadLoginTicket) - { - } - - public GPLoginBadLoginTicketException(string message) : base(message, GPErrorCode.LoginBadLoginTicket) - { - } - - public GPLoginBadLoginTicketException(string message, System.Exception innerException) : base(message, GPErrorCode.LoginBadLoginTicket, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadNickException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadNickException.cs deleted file mode 100755 index 16e36d6bf..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadNickException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.Login -{ - public class GPLoginBadNickException : GPLoginException - { - public GPLoginBadNickException() : base("Nickname is in valid!", GPErrorCode.LoginBadNick) - { - } - - public GPLoginBadNickException(string message) : base(message, GPErrorCode.LoginBadNick) - { - } - - public GPLoginBadNickException(string message, System.Exception innerException) : base(message, GPErrorCode.LoginBadNick, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadPasswordException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadPasswordException.cs deleted file mode 100755 index f6f773987..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadPasswordException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.Login -{ - public class GPLoginBadPasswordException : GPLoginException - { - public GPLoginBadPasswordException() : base("Password provided is invalid!", GPErrorCode.LoginBadPassword) - { - } - - public GPLoginBadPasswordException(string message) : base(message, GPErrorCode.LoginBadPassword) - { - } - - public GPLoginBadPasswordException(string message, System.Exception innerException) : base(message, GPErrorCode.LoginBadPassword, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadPreAuthException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadPreAuthException.cs deleted file mode 100755 index 82d98e6b6..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadPreAuthException.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.Login -{ - public class GPLoginBadPreAuthException : GPException - { - public GPLoginBadPreAuthException() : base("Login pre-authentication failed.", GPErrorCode.LoginBadPreAuth) - { - } - - public GPLoginBadPreAuthException(string message) : base(message, GPErrorCode.LoginBadPreAuth) - { - } - - public GPLoginBadPreAuthException(string message, System.Exception innerException) : base(message, GPErrorCode.LoginBadPreAuth, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadProfileException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadProfileException.cs deleted file mode 100755 index 13c16b648..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadProfileException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.Login -{ - public class GPLoginBadProfileException : GPLoginException - { - public GPLoginBadProfileException() : base("User profile is damaged!", GPErrorCode.LoginBadProfile) - { - } - - public GPLoginBadProfileException(string message) : base(message, GPErrorCode.LoginBadProfile) - { - } - - public GPLoginBadProfileException(string message, System.Exception innerException) : base(message, GPErrorCode.LoginBadProfile, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadUniquenickException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadUniquenickException.cs deleted file mode 100755 index 8c4f08e54..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginBadUniquenickException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.Login -{ - public class GPLoginBadUniquenickException : GPLoginException - { - public GPLoginBadUniquenickException() : base("The uniquenick provided is invalid!", GPErrorCode.LoginBadUniquenick) - { - } - - public GPLoginBadUniquenickException(string message) : base(message, GPErrorCode.LoginBadUniquenick) - { - } - - public GPLoginBadUniquenickException(string message, System.Exception innerException) : base(message, GPErrorCode.LoginBadUniquenick, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginConnectionFailedException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginConnectionFailedException.cs deleted file mode 100755 index f2b7c45b7..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginConnectionFailedException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.Login -{ - public class GPLoginConnectionFailedException : GPLoginException - { - public GPLoginConnectionFailedException() : base("Login connection failed.", GPErrorCode.LoginConnectionFailed) - { - } - - public GPLoginConnectionFailedException(string message) : base(message, GPErrorCode.LoginConnectionFailed) - { - } - - public GPLoginConnectionFailedException(string message, System.Exception innerException) : base(message, GPErrorCode.LoginConnectionFailed, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginException.cs deleted file mode 100755 index 1bd6c6986..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginException.cs +++ /dev/null @@ -1,28 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.Login -{ - public class GPLoginException : GPException - { - public GPLoginException() : base("Unknown login error!", GPErrorCode.Login) - { - } - - public GPLoginException(string message) : base(message, GPErrorCode.Login) - { - } - - public GPLoginException(string message, System.Exception innerException) : base(message, GPErrorCode.Login, innerException) - { - } - - public GPLoginException(string message, GPErrorCode errorCode) : base(message, errorCode) - { - } - - public GPLoginException(string message, GPErrorCode errorCode, System.Exception innerException) : base(message, errorCode, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginProfileDeletedException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginProfileDeletedException.cs deleted file mode 100755 index 294df1973..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginProfileDeletedException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.Login -{ - public class GPLoginProfileDeletedException : GPLoginException - { - public GPLoginProfileDeletedException() : base("User's profile has been deleted!", GPErrorCode.LoginProfileDeleted) - { - } - - public GPLoginProfileDeletedException(string message) : base(message, GPErrorCode.LoginProfileDeleted) - { - } - - public GPLoginProfileDeletedException(string message, System.Exception innerException) : base(message, GPErrorCode.LoginProfileDeleted, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginServerAuthFailedException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginServerAuthFailedException.cs deleted file mode 100755 index f40fddd31..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginServerAuthFailedException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.Login -{ - public class GPLoginServerAuthFailedException : GPLoginException - { - public GPLoginServerAuthFailedException() : base("Login server authentication failed!", GPErrorCode.LoginServerAuthFaild) - { - } - - public GPLoginServerAuthFailedException(string message) : base(message) - { - } - - public GPLoginServerAuthFailedException(string message, System.Exception innerException) : base(message, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginTicketExpiredException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginTicketExpiredException.cs deleted file mode 100755 index 69538be86..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginTicketExpiredException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.Login -{ - public class GPLoginTicketExpiredException : GPLoginException - { - public GPLoginTicketExpiredException() : base("The login ticket have expired!", GPErrorCode.LoginTicketExpired) - { - } - - public GPLoginTicketExpiredException(string message) : base(message, GPErrorCode.LoginTicketExpired) - { - } - - public GPLoginTicketExpiredException(string message, System.Exception innerException) : base(message, GPErrorCode.LoginTicketExpired, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginTimeOutException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginTimeOutException.cs deleted file mode 100755 index 07cc27b1a..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/Login/GPLoginTimeOutException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.Login -{ - public class GPLoginTimeOutException : GPLoginException - { - public GPLoginTimeOutException() : base("Login timeout!", GPErrorCode.LoginTimeOut) - { - } - - public GPLoginTimeOutException(string message) : base(message, GPErrorCode.LoginTimeOut) - { - } - - public GPLoginTimeOutException(string message, System.Exception innerException) : base(message, GPErrorCode.LoginTimeOut, innerException) - { - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/NewProfile/GPNewProfileBadNickException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/NewProfile/GPNewProfileBadNickException.cs deleted file mode 100755 index 1d45e1c41..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/NewProfile/GPNewProfileBadNickException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.NewProfile -{ - public class GPNewProfileBadNickException : GPNewProfileException - { - public GPNewProfileBadNickException() : base("Nickname is invalid at creating new profile!", GPErrorCode.NewProfileBadNick) - { - } - - public GPNewProfileBadNickException(string message) : base(message, GPErrorCode.NewProfileBadNick) - { - } - - public GPNewProfileBadNickException(string message, System.Exception innerException) : base(message, GPErrorCode.NewProfileBadNick, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/NewProfile/GPNewProfileBadOldNick.cs b/src/Servers/PresenceSearchPlayer/src/Exception/NewProfile/GPNewProfileBadOldNick.cs deleted file mode 100755 index 5865aee81..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/NewProfile/GPNewProfileBadOldNick.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.NewProfile -{ - public class GPNewProfileBadOldNickException : GPNewProfileException - { - public GPNewProfileBadOldNickException() : base("There is an already exist nickname!", GPErrorCode.NewProfileBadOldNick) - { - } - - public GPNewProfileBadOldNickException(string message) : base(message, GPErrorCode.NewProfileBadOldNick) - { - } - - public GPNewProfileBadOldNickException(string message, System.Exception innerException) : base(message, GPErrorCode.NewProfileBadOldNick, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/NewProfile/GPNewProfileException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/NewProfile/GPNewProfileException.cs deleted file mode 100755 index b93fb0b19..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/NewProfile/GPNewProfileException.cs +++ /dev/null @@ -1,28 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.NewProfile -{ - public class GPNewProfileException : GPException - { - public GPNewProfileException() : base("An unknown error occur when creating new profile!", GPErrorCode.NewProfile) - { - } - - public GPNewProfileException(string message) : base(message, GPErrorCode.NewProfile) - { - } - - public GPNewProfileException(string message, System.Exception innerException) : base(message, GPErrorCode.NewProfile, innerException) - { - } - - public GPNewProfileException(string message, GPErrorCode errorCode) : base(message, errorCode) - { - } - - public GPNewProfileException(string message, GPErrorCode errorCode, System.Exception innerException) : base(message, errorCode, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserBadNickException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserBadNickException.cs deleted file mode 100755 index 90b021671..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserBadNickException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.NewUser -{ - public class GPNewUserBadNickException : GPNewUserException - { - public GPNewUserBadNickException() : base("The nickname provided is invalid!", GPErrorCode.NewUserBadNick) - { - } - - public GPNewUserBadNickException(string message) : base(message, GPErrorCode.NewUserBadNick) - { - } - - public GPNewUserBadNickException(string message, System.Exception innerException) : base(message, GPErrorCode.NewUserBadNick, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserBadPasswordException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserBadPasswordException.cs deleted file mode 100755 index 3152ecfdc..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserBadPasswordException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.NewUser -{ - public class GPNewUserBadPasswordException : GPNewUserException - { - public GPNewUserBadPasswordException() : base("Password is invalid!", GPErrorCode.NewUserBadPasswords) - { - } - - public GPNewUserBadPasswordException(string message) : base(message, GPErrorCode.NewUserBadPasswords) - { - } - - public GPNewUserBadPasswordException(string message, System.Exception innerException) : base(message, GPErrorCode.NewUserBadPasswords, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserException.cs deleted file mode 100755 index cf0c75ae6..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserException.cs +++ /dev/null @@ -1,30 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.NewUser -{ - public class GPNewUserException : GPException - { - public GPNewUserException() : this("There was an unknown error creating user account.", GPErrorCode.NewUser) - { - } - public GPNewUserException(string message) : base(message, GPErrorCode.NewUser) - { - } - public GPNewUserException(string message, System.Exception innerException) : base(message, GPErrorCode.NewUser, innerException) - { - } - - public GPNewUserException(string message, GPErrorCode errorCode) : base(message, errorCode) - { - } - - public GPNewUserException(string message, GPErrorCode errorCode, System.Exception innerException) : base(message, errorCode, innerException) - { - } - public override void Build() - { - SendingBuffer = $@"\nur\{(int)ErrorCode}\final\"; - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserUniquenickInUseException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserUniquenickInUseException.cs deleted file mode 100755 index 56c415c0f..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserUniquenickInUseException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.NewUser -{ - public class GPNewUserUniquenickInUseException : GPNewUserException - { - public GPNewUserUniquenickInUseException() : base("Uniquenick is in use!", GPErrorCode.NewUserUniquenickInUse) - { - } - - public GPNewUserUniquenickInUseException(string message) : base(message, GPErrorCode.NewUserUniquenickInUse) - { - } - - public GPNewUserUniquenickInUseException(string message, System.Exception innerException) : base(message, GPErrorCode.NewUserUniquenickInUse, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserUniquenickInvalidException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserUniquenickInvalidException.cs deleted file mode 100755 index 5232fe9e9..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/NewUser/GPNewUserUniquenickInvalidException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.NewUser -{ - public class GPNewUserUniquenickInvalidException : GPNewUserException - { - public GPNewUserUniquenickInvalidException() : base("Uniquenick is invalid!", GPErrorCode.NewUserUniquenickInvalid) - { - } - - public GPNewUserUniquenickInvalidException(string message) : base(message, GPErrorCode.NewUserUniquenickInvalid) - { - } - - public GPNewUserUniquenickInvalidException(string message, System.Exception innerException) : base(message, GPErrorCode.NewUserUniquenickInvalid, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/Status/GPStatusException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/Status/GPStatusException.cs deleted file mode 100755 index dd8071271..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/Status/GPStatusException.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.Status -{ - public class GPStatusException : GPException - { - public GPStatusException() : base("Unknown error happen when processing player status", GPErrorCode.Status) - { - } - - public GPStatusException(string message) : base(message, GPErrorCode.Status) - { - } - - public GPStatusException(string message, System.Exception innerException) : base(message, GPErrorCode.Status, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/UpdatePro/GPUpdateProBadNickException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/UpdatePro/GPUpdateProBadNickException.cs deleted file mode 100755 index a6e53462f..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/UpdatePro/GPUpdateProBadNickException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.UpdatePro -{ - public class GPUpdateProBadNickException : GPUpdateProException - { - public GPUpdateProBadNickException() : base("Nickname is invalid for updating profile!", GPErrorCode.UpdateProBadNick) - { - } - - public GPUpdateProBadNickException(string message) : base(message, GPErrorCode.UpdateProBadNick) - { - } - - public GPUpdateProBadNickException(string message, System.Exception innerException) : base(message, GPErrorCode.UpdateProBadNick, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/UpdatePro/GPUpdateProException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/UpdatePro/GPUpdateProException.cs deleted file mode 100755 index f6bfc60cf..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/UpdatePro/GPUpdateProException.cs +++ /dev/null @@ -1,28 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.UpdatePro -{ - public class GPUpdateProException : GPException - { - public GPUpdateProException() : base("Update profile unknown error!", GPErrorCode.UpdatePro) - { - } - - public GPUpdateProException(string message) : base(message, GPErrorCode.UpdatePro) - { - } - - public GPUpdateProException(string message, System.Exception innerException) : base(message, GPErrorCode.UpdatePro, innerException) - { - } - - public GPUpdateProException(string message, GPErrorCode errorCode) : base(message, errorCode) - { - } - - public GPUpdateProException(string message, GPErrorCode errorCode, System.Exception innerException) : base(message, errorCode, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/UpdateUI/GPUpdateUIBadEmailException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/UpdateUI/GPUpdateUIBadEmailException.cs deleted file mode 100755 index 11e1c5cc6..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/UpdateUI/GPUpdateUIBadEmailException.cs +++ /dev/null @@ -1,19 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.UpdateUI -{ - public class GPUpdateUIBadEmailException : GPUpdateUIException - { - public GPUpdateUIBadEmailException() : base("Email is invalid!", GPErrorCode.UpdateUIBadEmail) - { - } - - public GPUpdateUIBadEmailException(string message) : base(message, GPErrorCode.UpdateUIBadEmail) - { - } - - public GPUpdateUIBadEmailException(string message, System.Exception innerException) : base(message, GPErrorCode.UpdateUIBadEmail, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Exception/UpdateUI/GPUpdateUIException.cs b/src/Servers/PresenceSearchPlayer/src/Exception/UpdateUI/GPUpdateUIException.cs deleted file mode 100755 index e8ae0d7b2..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Exception/UpdateUI/GPUpdateUIException.cs +++ /dev/null @@ -1,28 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; - -namespace UniSpy.Server.PresenceSearchPlayer.Exception.UpdateUI -{ - public class GPUpdateUIException : GPException - { - public GPUpdateUIException() : base("Update user info unknown error!", GPErrorCode.UpdateUI) - { - } - - public GPUpdateUIException(string message) : base(message, GPErrorCode.UpdateUI) - { - } - - public GPUpdateUIException(string message, System.Exception innerException) : base(message, GPErrorCode.UpdateUI, innerException) - { - } - - public GPUpdateUIException(string message, GPErrorCode errorCode) : base(message, errorCode) - { - } - - public GPUpdateUIException(string message, GPErrorCode errorCode, System.Exception innerException) : base(message, errorCode, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/CheckHandler.cs b/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/CheckHandler.cs deleted file mode 100755 index f78dd45dc..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/CheckHandler.cs +++ /dev/null @@ -1,43 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Application; -using UniSpy.Server.PresenceSearchPlayer.Enumerate; -using UniSpy.Server.PresenceSearchPlayer.Aggregate.Exception; -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using UniSpy.Server.PresenceSearchPlayer.Contract.Response; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; - -namespace UniSpy.Server.PresenceSearchPlayer.Handler.CmdHandler -{ - - public sealed class CheckHandler : CmdHandlerBase - { - // \check\\nick\\email\\partnerid\0\passenc\\gamename\gmtest\final\ - //\cur\pid\\final - //check is request recieved correct and convert password into our MD5 type - private new CheckRequest _request => (CheckRequest)base._request; - private new CheckResult _result { get => (CheckResult)base._result; set => base._result = value; } - public CheckHandler(Client client, CheckRequest request) : base(client, request) - { - _result = new CheckResult(); - } - - protected override void DataOperation() - { - if (StorageOperation.Persistance.VerifyEmail(_request.Email)) - { - throw new CheckException("No account exists with the provided email address.", GPErrorCode.CheckBadMail); - } - - if (StorageOperation.Persistance.VerifyEmailAndPassword(_request.Email, _request.Password)) - { - throw new CheckException("No account exists with the provided email address.", GPErrorCode.CheckBadPassword); - } - _result.ProfileId = StorageOperation.Persistance.GetProfileId(_request.Email, _request.Password, _request.Nick, _request.PartnerId); - } - - protected override void ResponseConstruct() - { - _response = new CheckResponse(_request, _result); - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/NewUserHandler.cs b/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/NewUserHandler.cs deleted file mode 100755 index f18ecd844..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/NewUserHandler.cs +++ /dev/null @@ -1,145 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Application; -using UniSpy.Server.PresenceSearchPlayer.Entity.Enumerator; -using UniSpy.Server.PresenceSearchPlayer.Exception.NewUser; -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using UniSpy.Server.PresenceSearchPlayer.Contract.Response; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Database.DatabaseModel; - -namespace UniSpy.Server.PresenceSearchPlayer.Handler.CmdHandler -{ - - public class NewUserHandler : CmdHandlerBase - { - protected new NewUserRequest _request => (NewUserRequest)base._request; - protected new NewUserResult _result { get => (NewUserResult)base._result; set => base._result = value; } - - public NewUserHandler(IClient client, IRequest request) : base(client, request) - { - _result = new NewUserResult(); - } - - protected override void DataOperation() - { - DatabaseOperationByType(); - UpdateOtherInfo(); - } - - private void UpdateOtherInfo() - { - if (_request.HasPartnerIDFlag) - { - _result.SubProfile.Partnerid = _request.PartnerID; - } - - if (_request.HasProductIDFlag) - { - _result.SubProfile.Productid = _request.ProductID; - } - - if (_request.HasGameNameFlag) - { - _result.SubProfile.Gamename = _request.GameName; - } - - if (_request.HasGamePortFlag) - { - _result.SubProfile.Port = _request.GamePort; - } - - if (_request.HasCDKeyEncFlag) - { - _result.SubProfile.Cdkeyenc = _request.CDKey; - } - StorageOperation.Persistance.UpdateSubProfile(_result.SubProfile); - } - - private void DatabaseOperationByType() - { - - switch (NewUserStatus.CheckAccount) - { - case NewUserStatus.CheckAccount: - var user = StorageOperation.Persistance.GetUser(_request.Email); - if (user is null) - { - goto case NewUserStatus.AccountNotExist; - } - else - { - _result.User = user; - goto case NewUserStatus.AccountExist; - } - - case NewUserStatus.AccountNotExist: - _result.User = new User { Email = _request.Email, Password = _request.Password }; - StorageOperation.Persistance.AddUser(_result.User); - goto case NewUserStatus.CheckProfile; - - case NewUserStatus.AccountExist: - - if (_result.User.Password != _request.Password) - { - throw new GPNewUserBadPasswordException("password is incorrect when creating new user."); - } - else - { - goto case NewUserStatus.CheckProfile; - } - - case NewUserStatus.CheckProfile: - var profile = StorageOperation.Persistance.GetProfile(_result.User.Userid, _request.Nick); - if (profile is null) - { - goto case NewUserStatus.ProfileNotExist; - } - else - { - _result.Profile = profile; - goto case NewUserStatus.ProfileExist; - } - - case NewUserStatus.ProfileNotExist: - _result.Profile = new Profile { Userid = _result.User.Userid, Nick = _request.Nick }; - StorageOperation.Persistance.AddProfile(_result.Profile); - goto case NewUserStatus.CheckSubProfile; - - case NewUserStatus.ProfileExist: - //we do nothing here - - case NewUserStatus.CheckSubProfile: - var subProfile = StorageOperation.Persistance.GetSubProfile(_result.Profile.Profileid, _request.NamespaceID, _request.ProductID); - if (subProfile is null) - { - goto case NewUserStatus.SubProfileNotExist; - } - else - { - _result.SubProfile = subProfile; - goto case NewUserStatus.SubProfileExist; - } - - case NewUserStatus.SubProfileNotExist: - //we create subprofile and return - _result.SubProfile = new Subprofile - { - Profileid = _result.Profile.Profileid, - Uniquenick = _request.Uniquenick, - Namespaceid = _request.NamespaceID - }; - StorageOperation.Persistance.AddSubProfile(_result.SubProfile); - break; - - case NewUserStatus.SubProfileExist: - throw new GPNewUserUniquenickInUseException("unique nick is in use."); - } - } - - protected override void ResponseConstruct() - { - _response = new NewUserResponse(_request, _result); - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/NicksHandler.cs b/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/NicksHandler.cs deleted file mode 100755 index d87823d9e..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/NicksHandler.cs +++ /dev/null @@ -1,46 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using UniSpy.Server.PresenceSearchPlayer.Contract.Response; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.PresenceSearchPlayer.Application; - -/////////////////////////Finished?///////////////////////////////// -namespace UniSpy.Server.PresenceSearchPlayer.Handler.CmdHandler -{ - /// - /// Uses a email and namespaceid to find all nick in this account - /// - - public sealed class NicksHandler : CmdHandlerBase - { - private new NicksResult _result { get => (NicksResult)base._result; set => base._result = value; } - private new NicksRequest _request => (NicksRequest)base._request; - public NicksHandler(IClient client, IRequest request) : base(client, request) - { - _result = new NicksResult(); - } - - protected override void DataOperation() - { - try - { - _result.DataBaseResults = StorageOperation.Persistance.GetAllNickAndUniqueNick(_request.Email, - _request.Password, - _request.NamespaceID); - - //we store data in strong type so we can use in next step - } - catch (System.Exception e) - { - throw new GPDatabaseException($"Unknown error occurs in database operation.", e); - } - } - - protected override void ResponseConstruct() - { - _response = new NicksResponse(_request, _result); - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/OthersHandler.cs b/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/OthersHandler.cs deleted file mode 100755 index 90c604ae4..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/OthersHandler.cs +++ /dev/null @@ -1,42 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using UniSpy.Server.PresenceSearchPlayer.Contract.Response; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; -using UniSpy.Server.PresenceSearchPlayer.Application; - -namespace UniSpy.Server.PresenceSearchPlayer.Handler.CmdHandler -{ - - /// - /// Get buddy's information - /// - - public sealed class OthersHandler : CmdHandlerBase - { - private new OthersRequest _request => (OthersRequest)base._request; - private new OthersResult _result { get => (OthersResult)base._result; set => base._result = value; } - public OthersHandler(Client client, OthersRequest request) : base(client, request) - { - _result = new OthersResult(); - } - - protected override void DataOperation() - { - try - { - - _result.DatabaseResults = StorageOperation.Persistance.GetFriendsInfo(_request.ProfileId, _request.NamespaceID, _request.GameName); - } - catch (System.Exception e) - { - throw new GPDatabaseException("Unknown error occurs in database operation.", e); - } - } - - protected override void ResponseConstruct() - { - _response = new OthersResponse(_request, _result); - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/OthersListHandler.cs b/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/OthersListHandler.cs deleted file mode 100755 index 0bfd7a719..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/OthersListHandler.cs +++ /dev/null @@ -1,40 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using UniSpy.Server.PresenceSearchPlayer.Contract.Response; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; -using UniSpy.Server.PresenceSearchPlayer.Application; - -namespace UniSpy.Server.PresenceSearchPlayer.Handler.CmdHandler -{ - - - public sealed class OthersListHandler : CmdHandlerBase - { - private new OthersListRequest _request => (OthersListRequest)base._request; - - private new OthersListResult _result { get => (OthersListResult)base._result; set => base._result = value; } - - public OthersListHandler(Client client, OthersListRequest request) : base(client, request) - { - _result = new OthersListResult(); - } - - protected override void DataOperation() - { - try - { - _result.DatabaseResults = StorageOperation.Persistance.GetMatchedProfileIdInfos(_request.ProfileIDs, _request.NamespaceID); - } - catch (System.Exception e) - { - throw new GPDatabaseException("Unknown error occurs in database operation.", e); - } - } - - protected override void ResponseConstruct() - { - _response = new OthersListResponse(_request, _result); - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/PmatchHandler.cs b/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/PmatchHandler.cs deleted file mode 100755 index e3def1cfe..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/PmatchHandler.cs +++ /dev/null @@ -1,52 +0,0 @@ -//using UniSpy.Server.Core.Common; -//using UniSpy.Server.PresenceSearchPlayer.Enumerate; -//using System; -//using System.Collections.Generic; - -//namespace UniSpy.Server.PresenceSearchPlayer.Handler.CommandHandler.Pmatch -//{ -// /// -// /// Search the all players in specific game -// /// -// public class PmatchHandler:GPSPHandlerBase -// { - -// public PmatchHandler(string rawRequest) :base(rawRequest) -// { -// } - -// //pmath\\sesskey\\profileid\\productid\\ - -// protected override void CheckRequest(GPSPSession connection) -// { -// base.CheckRequest(connection); -// if (!KeyValues.ContainsKey("sesskey") || !KeyValues.ContainsKey("profileid") || !KeyValues.ContainsKey("productid")) -// { -// _errorCode = GPErrorCode.Parse; -// } -// } - -// protected override void DataBaseOperation(GPSPSession connection) -// { -// _result = PmatchQuery.PlayerMatch(Convert.Toint16(KeyValues["productid"])); -// } - -// protected override void ConstructResponse(GPSPSession connection) -// { -// if(_result.Count==0) -// { -// _sendingBuffer= @"\psrdone\final\"; -// return; -// } -// foreach (Dictionary player in _result) -// { -// _sendingBuffer += @"\psr\" + player["profileid"]; -// _sendingBuffer += @"\status\" + player["statstring"]; -// _sendingBuffer += @"\nick\" + player["nick"]; -// _sendingBuffer += @"\statuscode\" + player["status"]; -// } -// -// _sendingBuffer += @"\psrdone\final\"; -// } -// } -//} diff --git a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/SearchHandler.cs b/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/SearchHandler.cs deleted file mode 100755 index ce6782d0d..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/SearchHandler.cs +++ /dev/null @@ -1,67 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using UniSpy.Server.PresenceSearchPlayer.Contract.Response; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; -using System.Linq; -using UniSpy.Server.PresenceSearchPlayer.Application; - - -//last one we search with email this may get few profile so we can not return GPErrorCode -//SearchWithEmail(client,dict ); -//\search\\sesskey\0\profileid\0\namespaceid\1\partnerid\0\nick\mycrysis\uniquenick\xiaojiuwo\email\koujiangheng@live.cn\gamename\gmtest\final\ -//\bsrdone\more\\final\ -//string sendingbuffer = -//"\\bsr\\1\\nick\\mycrysis\\uniquenick\\1\\namespaceid\\0\\firstname\\jiangheng\\lastname\\kou\\email\\koujiangheng@live.cn\\bsrdone\\0\\final\\"; -//client.Stream.SendAsync(sendingbuffer); -//\more\\final\ -//\search\sesskey\0\profileid\0\namespaceid\0\nick\gbr359_jordips\gamename\gbrome\final\ - -namespace UniSpy.Server.PresenceSearchPlayer.Handler.CmdHandler -{ - - public sealed class SearchHandler : CmdHandlerBase - { - private new SearchRequest _request => (SearchRequest)base._request; - private new SearchResult _result { get => (SearchResult)base._result; set => base._result = value; } - public SearchHandler(Client client, SearchRequest request) : base(client, request) - { - _result = new SearchResult(); - } - protected override void DataOperation() - { - //TODO verify the search condition whether needed namespaceid!!!!! - List result; - switch (_request.RequestType) - { - case SearchRequestType.NickSearch: - result = StorageOperation.Persistance.GetMatchedInfosByNick(_request.Nick); - break; - case SearchRequestType.NickEmailSearch: - result = StorageOperation.Persistance.GetMatchedInfosByNickAndEmail(_request.Nick, - _request.Email); - break; - case SearchRequestType.UniquenickNamespaceIDSearch: - result = StorageOperation.Persistance.GetMatchedInfosByUniqueNickAndNamespaceId(_request.Uniquenick, - _request.NamespaceID); - break; - case SearchRequestType.EmailSearch: - result = StorageOperation.Persistance.GetMatchedInfosByEmail(_request.Email); - break; - default: - result = null; - break; - } - if (result is null) - { - return; - } - _result.DataBaseResults = result.ToList(); - } - - protected override void ResponseConstruct() - { - _response = new SearchResponse(_request, _result); - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/SearchUniqueHandler.cs b/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/SearchUniqueHandler.cs deleted file mode 100755 index 10a94854c..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/SearchUniqueHandler.cs +++ /dev/null @@ -1,39 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using UniSpy.Server.PresenceSearchPlayer.Contract.Response; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; -using UniSpy.Server.PresenceSearchPlayer.Application; - -namespace UniSpy.Server.PresenceSearchPlayer.Handler.CmdHandler -{ - /// - /// Search with uniquenick and namespace - /// - - public sealed class SearchUniqueHandler : CmdHandlerBase - { - private new SearchUniqueRequest _request => (SearchUniqueRequest)base._request; - private new SearchUniqueResult _result { get => (SearchUniqueResult)base._result; set => base._result = value; } - public SearchUniqueHandler(Client client, SearchUniqueRequest request) : base(client, request) - { - _result = new SearchUniqueResult(); - } - protected override void DataOperation() - { - try - { - _result.DatabaseResults = StorageOperation.Persistance.GetMatchedInfosByNamespaceId(_request.NamespaceIds, _request.Uniquenick); - } - catch (System.Exception e) - { - throw new GPDatabaseException("Unknown error occurs in database operation.", e); - } - } - - protected override void ResponseConstruct() - { - _response = new SearchUniqueResponse(_request, _result); - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/UniqueSearchHandler.cs b/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/UniqueSearchHandler.cs deleted file mode 100755 index d5550c3b3..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/UniqueSearchHandler.cs +++ /dev/null @@ -1,38 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using UniSpy.Server.PresenceSearchPlayer.Contract.Response; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; -using UniSpy.Server.PresenceSearchPlayer.Application; - -namespace UniSpy.Server.PresenceSearchPlayer.Handler.CmdHandler -{ - - public sealed class UniqueSearchHandler : CmdHandlerBase - { - private new UniqueSearchRequest _request => (UniqueSearchRequest)base._request; - private new UniqueSearchResult _result { get => (UniqueSearchResult)base._result; set => base._result = value; } - public UniqueSearchHandler(Client client, UniqueSearchRequest request) : base(client, request) - { - _result = new UniqueSearchResult(); - } - protected override void DataOperation() - { - try - { - _result.IsUniquenickExist = StorageOperation.Persistance.IsUniqueNickExist(_request.PreferredNick, - _request.NamespaceID, - _request.GameName); - } - catch (System.Exception e) - { - throw new GPDatabaseException("Unknown error occurs in database operation.", e); - } - } - - protected override void ResponseConstruct() - { - _response = new UniqueSearchResponse(_request, _result); - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/ValidHandler.cs b/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/ValidHandler.cs deleted file mode 100755 index 159a594d7..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Handler/CmdHandler/ValidHandler.cs +++ /dev/null @@ -1,37 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Abstraction.BaseClass; -using UniSpy.Server.PresenceSearchPlayer.Exception.General; -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using UniSpy.Server.PresenceSearchPlayer.Contract.Response; -using UniSpy.Server.PresenceSearchPlayer.Contract.Result; -using UniSpy.Server.PresenceSearchPlayer.Application; - -namespace UniSpy.Server.PresenceSearchPlayer.Handler.CmdHandler -{ - - public sealed class ValidHandler : CmdHandlerBase - { - private new ValidRequest _request => (ValidRequest)base._request; - private new ValidResult _result { get => (ValidResult)base._result; set => base._result = value; } - - public ValidHandler(Client client, ValidRequest request) : base(client, request) - { - _result = new ValidResult(); - } - protected override void DataOperation() - { - try - { - _result.IsAccountValid = StorageOperation.Persistance.IsEmailExist(_request.Email); - } - catch (System.Exception e) - { - throw new GPDatabaseException("Unknown error occurs in database operation.", e); - } - } - - protected override void ResponseConstruct() - { - _response = new ValidResponse(_request, _result); - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/Handler/CmdSwitcher.cs b/src/Servers/PresenceSearchPlayer/src/Handler/CmdSwitcher.cs deleted file mode 100755 index ea591e058..000000000 --- a/src/Servers/PresenceSearchPlayer/src/Handler/CmdSwitcher.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using UniSpy.Server.PresenceSearchPlayer.Handler.CmdHandler; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.PresenceSearchPlayer.Application; - -namespace UniSpy.Server.PresenceSearchPlayer.Handler -{ - public sealed class CmdSwitcher : CmdSwitcherBase - { - private new string _rawRequest => (string)base._rawRequest; - private new Client _client => (Client)base._client; - public CmdSwitcher(Client client, string rawRequest) : base(client, rawRequest) - { - } - protected override void ProcessRawRequest() - { - if (_rawRequest[0] != '\\') - { - _client.LogInfo("Invalid request recieved!"); - return; - } - string[] rawRequests = _rawRequest.Split("\\final\\", StringSplitOptions.RemoveEmptyEntries); - foreach (var rawRequest in rawRequests) - { - var name = rawRequest.TrimStart('\\').Split("\\").First(); - _requests.Add(new KeyValuePair(name, rawRequest)); - } - } - - protected override IHandler CreateCmdHandlers(object name, object rawRequest) - { - switch ((string)name) - { - case "check": - return new CheckHandler(_client, new CheckRequest((string)rawRequest)); - case "newuser": - return new NewUserHandler(_client, new NewUserRequest((string)rawRequest)); - case "nicks": - return new NicksHandler(_client, new NicksRequest((string)rawRequest)); - case "others": - return new OthersHandler(_client, new OthersRequest((string)rawRequest)); - case "otherslist": - return new OthersListHandler(_client, new OthersListRequest((string)rawRequest)); - case "pmatch": - // return new PMatchHandler(_client, new PMatchRequest((string)rawRequest)); - throw new NotImplementedException(); - case "search": - return new SearchHandler(_client, new SearchRequest((string)rawRequest)); - case "searchunique": - return new SearchUniqueHandler(_client, new SearchUniqueRequest((string)rawRequest)); - case "uniquesearch": - return new UniqueSearchHandler(_client, new UniqueSearchRequest((string)rawRequest)); - case "valid": - return new ValidHandler(_client, new ValidRequest((string)rawRequest)); - default: - return null; - } - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/src/UniSpy.Server.PresenceSearchPlayer.csproj b/src/Servers/PresenceSearchPlayer/src/UniSpy.Server.PresenceSearchPlayer.csproj deleted file mode 100755 index 8b9381e25..000000000 --- a/src/Servers/PresenceSearchPlayer/src/UniSpy.Server.PresenceSearchPlayer.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - Exe - net6.0 - ..\..\..\..\common\Icon\UniSpy_Logo.ico - Linux - ..\..\..\.. - - - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - - - \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/test/GameTest.cs b/src/Servers/PresenceSearchPlayer/test/GameTest.cs deleted file mode 100644 index c723c4a94..000000000 --- a/src/Servers/PresenceSearchPlayer/test/GameTest.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Handler; -using Xunit; - -namespace UniSpy.Server.PresenceSearchPlayer.Test -{ - public class GameTest - { - [Fact] - public void CheckTest() - { - var raw = @"\check\\nick\spyguy\email\spyguy@gamespy.com\pass\0000\final\"; - var client = MokeObject.CreateClient(); - var switcher = new CmdSwitcher(client, raw); - switcher.Handle(); - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/test/MokeObject.cs b/src/Servers/PresenceSearchPlayer/test/MokeObject.cs deleted file mode 100644 index 66ba41863..000000000 --- a/src/Servers/PresenceSearchPlayer/test/MokeObject.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Net; -using Moq; -using UniSpy.Server.PresenceSearchPlayer.Application; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.PresenceSearchPlayer.Test -{ - public class MokeObject - { - public static IClient Client = CreateClient(); - - public static Client CreateClient(string ipAddress = "192.168.1.1", int port = 9999) - { - var managerMock = new Mock(); - var connectionMock = new Mock(); - connectionMock.Setup(s => s.RemoteIPEndPoint).Returns(new IPEndPoint(IPAddress.Parse(ipAddress), port)); - connectionMock.Setup(s => s.Manager).Returns(managerMock.Object); - connectionMock.Setup(s => s.ConnectionType).Returns(NetworkConnectionType.Tcp); - var serverMock = new PresenceSearchPlayer.Application.Server(managerMock.Object); - - return new Client(connectionMock.Object, serverMock); - } - } -} \ No newline at end of file diff --git a/src/Servers/PresenceSearchPlayer/test/RawRequests.cs b/src/Servers/PresenceSearchPlayer/test/RawRequests.cs deleted file mode 100644 index 93d1cb787..000000000 --- a/src/Servers/PresenceSearchPlayer/test/RawRequests.cs +++ /dev/null @@ -1,31 +0,0 @@ -namespace UniSpy.Server.PresenceSearchPlayer.Test -{ - public class RawRequests - { - /// - /// Optional parameter: nick, uniquenick, email, firstname, lastname, icquin, skip - /// - public const string Profile = @"\search\\sesskey\xxxx\profileid\0\namespaceid\0\nick\spyguy\uniquenick\spyguy\email\spyguy@unispy.org\firstname\spy\lastname\guy\icquin\123\skip\0\gamename\gmtest\final\"; - - public const string ProfileUniquenick = @"\searchunique\\sesskey\xxxx\profileid\0\uniquenick\spyguy\namespaces\1,2,3,4,5\gamename\gmtest\final\"; - - public const string IsValid = @"\valid\\email\spyguy@unispy.org\partnerid\1\gamename\gmtest\final\"; - - public const string Nick = @"\nicks\\email\spyguy@unispy.org\passenc\xxxxx\namespaceid\0\partnerid\0\gamename\gmtest\final\"; - - public const string Players = @"\pmatch\\sesskey\123456\profileid\0\productid\0\final\"; - - public const string Check = @"\check\\nick\xiaojiuwo\email\xiaojiuwo@gamespy.com\partnerid\0\passenc\xxxx\gamename\gmtest\final\"; - - /// - /// Optional parameter: cdkey - /// - public const string NewUser = @"\newuser\\nick\xiaojiuwo\email\xiaojiuwo@gamespy.com\passenc\xxxx\productID\0\namespaceid\0\uniquenick\xiaojiuwo\cdkey\xxx-xxx-xxx-xxx\partnerid\0\gamename\gmtest\final\"; - - public const string OthersBuddy = @"\others\\sesskey\123456\profileid\0\namespaceid\0\gamename\gmtest\final\"; - - public const string OthersBuddyList = @"\otherlist\\sesskey\123456\profileid\0\numopids\2\opids\1|2\namespaceid\0\gamename\gmtest\final\"; - - public const string SuggestUnique = @"uniquesearch\\preferrednick\xiaojiuwo\namespaceid\0\gamename\gmtest\final\"; - } -} diff --git a/src/Servers/PresenceSearchPlayer/test/RequestTests.cs b/src/Servers/PresenceSearchPlayer/test/RequestTests.cs deleted file mode 100644 index 8c17fbd9d..000000000 --- a/src/Servers/PresenceSearchPlayer/test/RequestTests.cs +++ /dev/null @@ -1,25 +0,0 @@ -using UniSpy.Server.PresenceSearchPlayer.Contract.Request; -using Xunit; - -namespace UniSpy.Server.PresenceSearchPlayer.Test -{ - public class RequestTests - { - [Fact] - public void NewUserTest() - { - var request = new NewUserRequest(RawRequests.NewUser); - request.Parse(); - Assert.Equal("xiaojiuwo", request.Nick); - Assert.Equal("xiaojiuwo@gamespy.com", request.Email); - // password 'xxx' is decoded and hash to '1e034b66363e5a081874ae022767f685' - Assert.Equal("0d1b08c34858921bc7c662b228acb7ba", request.Password); - Assert.Equal((int)0, request.ProductID); - Assert.Equal((int)0, request.NamespaceID); - Assert.Equal("xiaojiuwo", request.Uniquenick); - Assert.Equal("xxx-xxx-xxx-xxx", request.CDKey); - Assert.Equal((int)0, request.PartnerID); - Assert.Equal("gmtest", request.GameName); - } - } -} diff --git a/src/Servers/PresenceSearchPlayer/test/UniSpy.Server.PresenceSearchPlayer.Test.csproj b/src/Servers/PresenceSearchPlayer/test/UniSpy.Server.PresenceSearchPlayer.Test.csproj deleted file mode 100644 index 56714d7eb..000000000 --- a/src/Servers/PresenceSearchPlayer/test/UniSpy.Server.PresenceSearchPlayer.Test.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - net6.0 - false - ..\..\..\..\common\Icon\UniSpy_Logo.ico - - - AnyCPU - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - \ No newline at end of file diff --git a/src/Servers/QueryReport/src/Abstraction/Interface/IStorageOperation.cs b/src/Servers/QueryReport/src/Abstraction/Interface/IStorageOperation.cs deleted file mode 100644 index ec3f0b7fb..000000000 --- a/src/Servers/QueryReport/src/Abstraction/Interface/IStorageOperation.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Chat.Aggregate; - -namespace UniSpy.Server.QueryReport.Abstraction.Interface -{ - public interface IStorageOperation - { - List GetPeerStagingChannel(string gameName, int groupId); - List GetPeerGroupChannel(int groupId); - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/Aggregate/Redis/PeerRoomInfo.cs b/src/Servers/QueryReport/src/Aggregate/Redis/PeerRoomInfo.cs deleted file mode 100644 index 81284b4e1..000000000 --- a/src/Servers/QueryReport/src/Aggregate/Redis/PeerRoomInfo.cs +++ /dev/null @@ -1,70 +0,0 @@ -using System; -using System.Collections.Generic; - - -//!fix move this to root namespace -namespace UniSpy.Server.QueryReport.Aggregate.Redis.PeerGroup -{ - public record PeerRoomInfo - { - public Guid? ServerId { get; set; } - public string GameName { get; private set; } - public int? GroupId { get; private set; } - public string RoomName { get; private set; } - public int NumberOfWaitingPlayers - { - get => int.Parse(KeyValues["numwaiting"]); - set => KeyValues["numwaiting"] = value.ToString(); - } - public int MaxNumberOfWaitingPlayers - { - get => int.Parse(KeyValues["maxwaiting"]); - set => KeyValues["maxwaiting"] = value.ToString(); - } - public int NumberOfServers - { - get => int.Parse(KeyValues["numservers"]); - set => KeyValues["numservers"] = value.ToString(); - } - - public int MaxNumberOfPlayers - { - get => int.Parse(KeyValues["maxplayers"]); - set => KeyValues["maxplayers"] = value.ToString(); - } - public string Password => (string)KeyValues["password"]; - public int NumberOfGames - { - get => int.Parse(KeyValues["numgames"]); - set => KeyValues["numgames"] = value.ToString(); - } - public int NumberOfPlayers - { - get => int.Parse(KeyValues["numplayers"]); - set => KeyValues["numplayers"] = value.ToString(); - } - public int NumberOfPlayingPlayers - { - get => int.Parse(KeyValues["numplaying"]); - set => KeyValues["numplaying"] = value.ToString(); - } - public Dictionary KeyValues { get; private set; } = new Dictionary(); - public PeerRoomInfo(string gameName, int groupId, string roomName) - { - // this is the default value that every game needed - GameName = gameName; - GroupId = groupId; - RoomName = roomName; - KeyValues.Add("groupid", groupId.ToString()); - KeyValues.Add("hostname", roomName); - KeyValues.Add("numwaiting", "0"); - KeyValues.Add("maxwaiting", "200"); - KeyValues.Add("numservers", "0"); - KeyValues.Add("numplayers", "0"); - KeyValues.Add("maxplayers", "200"); - KeyValues.Add("password", ""); - KeyValues.Add("numgames", "0"); - KeyValues.Add("numplaying", "0"); - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/Application/Client.cs b/src/Servers/QueryReport/src/Application/Client.cs deleted file mode 100644 index f02b504a0..000000000 --- a/src/Servers/QueryReport/src/Application/Client.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System.Text; -using System; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; - -namespace UniSpy.Server.QueryReport.Application -{ - public sealed class Client : ClientBase - { - public Client(IConnection connection, IServer server) : base(connection, server) - { - IsLogRaw = true; - Info = new ClientInfo(); - } - public new ClientInfo Info { get => (ClientInfo)base.Info; private set => base.Info = value; } - protected override ISwitcher CreateSwitcher(object buffer) - { - var data = (byte[])buffer; - if (data[0] == Convert.ToInt32('\\')) - { - return new V1.Handler.CmdSwitcher(this, UniSpyEncoding.GetString(data)); - } - else - { - return new V2.Handler.CmdSwitcher(this, data); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/Application/ClientInfo.cs b/src/Servers/QueryReport/src/Application/ClientInfo.cs deleted file mode 100644 index 33ed03644..000000000 --- a/src/Servers/QueryReport/src/Application/ClientInfo.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.Core.Abstraction.BaseClass; - -namespace UniSpy.Server.QueryReport.Application -{ - public sealed class ClientInfo : ClientInfoBase - { - public uint? InstantKey { get; set; } - public string GameSecretKey { get; set; } - public ClientInfo() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/Application/Program.cs b/src/Servers/QueryReport/src/Application/Program.cs deleted file mode 100755 index 4a53afb75..000000000 --- a/src/Servers/QueryReport/src/Application/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.QueryReport.Application -{ - public class Program - { - static void Main(string[] args) - { - try - { - new ServerLauncher().Start(); - Console.WriteLine("Press < Q > to exit. "); - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - catch (System.Exception e) - { - UniSpy.Exception.HandleException(e); - } - finally - { - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - } - } -} diff --git a/src/Servers/QueryReport/src/Application/Server.cs b/src/Servers/QueryReport/src/Application/Server.cs deleted file mode 100644 index fc6869c88..000000000 --- a/src/Servers/QueryReport/src/Application/Server.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Net; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Network.Udp.Server; - -namespace UniSpy.Server.QueryReport.Application -{ - public sealed class Server : ServerBase - { - static Server() - { - _name = "QueryReport"; - } - public Server() { } - - public Server(IConnectionManager manager) : base(manager) { } - - public override void Start() - { - base.Start(); - _ = Chat.Application.StorageOperation.Persistance.PeerGroupList; - V2.Application.StorageOperation.NatNegChannel.Subscribe(); - } - protected override IClient CreateClient(IConnection connection) => new Client(connection, this); - protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new UdpConnectionManager(endPoint); - - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/Application/ServerLauncher.cs b/src/Servers/QueryReport/src/Application/ServerLauncher.cs deleted file mode 100644 index 695fb2fa5..000000000 --- a/src/Servers/QueryReport/src/Application/ServerLauncher.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.BaseClass.Factory; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.QueryReport.Application -{ - public sealed class ServerLauncher : ServerLauncherBase - { - protected override List LaunchNetworkService() => new List { new Server() }; - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/Application/StorageOperation.cs b/src/Servers/QueryReport/src/Application/StorageOperation.cs deleted file mode 100644 index e50132c45..000000000 --- a/src/Servers/QueryReport/src/Application/StorageOperation.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.Chat.Aggregate; -using UniSpy.Server.Chat.Aggregate.Redis; -using UniSpy.Server.QueryReport.Abstraction.Interface; - -namespace UniSpy.Server.QueryReport.Application -{ - public class StorageOperation : IStorageOperation - { - public static IStorageOperation Persistance = new StorageOperation(); - public List GetPeerStagingChannel(string gameName, int groupId) - { - var stagingName = $"{PeerRoom.StagingRoomPrefix}!{gameName}!*"; - var groupName = $"{PeerRoom.GroupRoomPrefix}!{groupId}"; - var stagingRooms = Chat.Application.StorageOperation.Persistance.ChannelCacheClient.Context - .Where(c => c.ChannelName == stagingName).ToList() - .Where(c => c.Channel.PreviousJoinedChannel == groupName).Select(c => c.Channel).ToList(); - return stagingRooms; - } - public List GetPeerGroupChannel(int groupId) - { - var groupName = $"{PeerRoom.GroupRoomPrefix}!{groupId}"; - var groupRooms = Chat.Application.StorageOperation.Persistance.ChannelCacheClient.Context.Where(c => c.ChannelName == groupName).ToList().Select(c => c.Channel).ToList(); - return groupRooms; - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/Dockerfile b/src/Servers/QueryReport/src/Dockerfile deleted file mode 100755 index a9b0c8e75..000000000 --- a/src/Servers/QueryReport/src/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base -WORKDIR /app -EXPOSE 27900/udp - -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build -WORKDIR /src -COPY ["src/Servers/QueryReport/src/UniSpy.Server.QueryReport.csproj", "src/Servers/QueryReport/src/"] -COPY ["src/Libraries/Core/src/UniSpy.Server.Core.csproj", "src/Libraries/Core/src/"] -RUN dotnet restore "src/Servers/QueryReport/src/UniSpy.Server.QueryReport.csproj" -COPY . . -WORKDIR "/src/src/Servers/QueryReport/src" -RUN dotnet build "UniSpy.Server.QueryReport.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "UniSpy.Server.QueryReport.csproj" -c Release -o /app/publish - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "UniSpy.Server.QueryReport.dll"] \ No newline at end of file diff --git a/src/Servers/QueryReport/src/Exception/Exception.cs b/src/Servers/QueryReport/src/Exception/Exception.cs deleted file mode 100755 index 1f042f547..000000000 --- a/src/Servers/QueryReport/src/Exception/Exception.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace UniSpy.Server.QueryReport -{ - public sealed class Exception : UniSpy.Exception - { - public Exception() - { - } - - public Exception(string message) : base(message) - { - } - - public Exception(string message, System.Exception innerException) : base(message, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/UniSpy.Server.QueryReport.csproj b/src/Servers/QueryReport/src/UniSpy.Server.QueryReport.csproj deleted file mode 100755 index 958ec9895..000000000 --- a/src/Servers/QueryReport/src/UniSpy.Server.QueryReport.csproj +++ /dev/null @@ -1,23 +0,0 @@ - - - - Exe - net6.0 - ..\..\..\..\common\Icon\UniSpy_Logo.ico - Linux - ..\..\..\.. - - - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - - - - \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Abstraction/BaseClass/CmdHandlerBase.cs b/src/Servers/QueryReport/src/V1/Abstraction/BaseClass/CmdHandlerBase.cs deleted file mode 100644 index 88bb30cde..000000000 --- a/src/Servers/QueryReport/src/V1/Abstraction/BaseClass/CmdHandlerBase.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.QueryReport.Application; - -namespace UniSpy.Server.QueryReport.V1.Abstraction.BaseClass -{ - public abstract class CmdHandlerBase : UniSpy.Server.Core.Abstraction.BaseClass.CmdHandlerBase - { - protected new Client _client => (Client)base._client; - public CmdHandlerBase(Client client, RequestBase request) : base(client, request) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Abstraction/BaseClass/RequestBase.cs b/src/Servers/QueryReport/src/V1/Abstraction/BaseClass/RequestBase.cs deleted file mode 100644 index 554dc3e81..000000000 --- a/src/Servers/QueryReport/src/V1/Abstraction/BaseClass/RequestBase.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.Core.Misc; - -namespace UniSpy.Server.QueryReport.V1.Abstraction.BaseClass -{ - public abstract class RequestBase : UniSpy.Server.Core.Abstraction.BaseClass.RequestBase - { - public new string RawRequest { get => (string)base.RawRequest; protected set => base.RawRequest = value; } - public new string CommandName { get => (string)base.CommandName; protected set => base.CommandName = value; } - public Dictionary KeyValues { get; private set; } - public RequestBase(string rawRequest) : base(rawRequest) - { - } - public override void Parse() - { - KeyValues = GameSpyUtils.ConvertToKeyValue(RawRequest); - CommandName = KeyValues.Keys.First(); - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Abstraction/BaseClass/ResponseBase.cs b/src/Servers/QueryReport/src/V1/Abstraction/BaseClass/ResponseBase.cs deleted file mode 100644 index f3a2eed23..000000000 --- a/src/Servers/QueryReport/src/V1/Abstraction/BaseClass/ResponseBase.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace UniSpy.Server.QueryReport.V1.Abstraction.BaseClass -{ - public abstract class ResponseBase : UniSpy.Server.Core.Abstraction.BaseClass.ResponseBase - { - public new string SendingBuffer { get => (string)base.SendingBuffer; protected set => base.SendingBuffer = value; } - public ResponseBase(RequestBase request, ResultBase result) : base(request, result) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Abstraction/BaseClass/ResultBase.cs b/src/Servers/QueryReport/src/V1/Abstraction/BaseClass/ResultBase.cs deleted file mode 100644 index b2e93529c..000000000 --- a/src/Servers/QueryReport/src/V1/Abstraction/BaseClass/ResultBase.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UniSpy.Server.QueryReport.V1.Abstraction.BaseClass -{ - public abstract class ResultBase : UniSpy.Server.Core.Abstraction.BaseClass.ResultBase - { - public ResultBase() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Abstraction/Interface/IStorageOperation.cs b/src/Servers/QueryReport/src/V1/Abstraction/Interface/IStorageOperation.cs deleted file mode 100644 index 3fc7fa6f0..000000000 --- a/src/Servers/QueryReport/src/V1/Abstraction/Interface/IStorageOperation.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using System.Net; -using UniSpy.Server.QueryReport.V1.Aggregation.Redis; - -namespace UniSpy.Server.QueryReport.V1.Abstraction.Interface -{ - public interface IStorageOperation - { - List GetServersInfo(string gameName); - string GetGameSecretKey(string gameName); - GameServerCache GetServerInfo(IPEndPoint endPoint); - void UpdateServerInfo(GameServerCache info); - void RemoveServerInfo(IPEndPoint endPoint); - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Aggregation/Enctype0.cs b/src/Servers/QueryReport/src/V1/Aggregation/Enctype0.cs deleted file mode 100644 index 3c3576dbb..000000000 --- a/src/Servers/QueryReport/src/V1/Aggregation/Enctype0.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.QueryReport.V1.Aggregate -{ - public class Enctype0 : ICryptography - { - public byte[] Decrypt(byte[] data) - { - throw new System.NotImplementedException(); - } - - public byte[] Encrypt(byte[] data) - { - throw new System.NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Aggregation/Redis/GameServerCache.cs b/src/Servers/QueryReport/src/V1/Aggregation/Redis/GameServerCache.cs deleted file mode 100644 index 90592c7dc..000000000 --- a/src/Servers/QueryReport/src/V1/Aggregation/Redis/GameServerCache.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using Newtonsoft.Json; -using UniSpy.LinqToRedis; -using UniSpy.Server.Core.Config; -using UniSpy.Server.Core.Extension.Redis; -using UniSpy.Server.Core.Misc; - -namespace UniSpy.Server.QueryReport.V1.Aggregation.Redis -{ - public record GameServerCache : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject - { - [RedisKey] - public Guid? ServerID { get; set; } - [RedisKey] - [JsonConverter(typeof(IPAddresConverter))] - public IPAddress HostIPAddress { get; set; } - [RedisKey] - public int? HostPort { get; set; } - [JsonIgnore] - public IPEndPoint HostIPEndPoint => new IPEndPoint(HostIPAddress, (int)HostPort); - [RedisKey] - public string GameName { get; set; } - public bool IsValidated { get; set; } - /// - /// The key values that contians all the information about this game server - /// - public Dictionary KeyValues { get; set; } - public GameServerCache() : base(RedisDbNumber.GameServerV1, TimeSpan.FromSeconds(30)) - { - } - public class RedisClient : RedisClient - { - public RedisClient() : base(ConfigManager.Config.Redis.RedisConnection) - { - } - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Application/StorageOperation.cs b/src/Servers/QueryReport/src/V1/Application/StorageOperation.cs deleted file mode 100644 index 32816863e..000000000 --- a/src/Servers/QueryReport/src/V1/Application/StorageOperation.cs +++ /dev/null @@ -1,56 +0,0 @@ -using UniSpy.Server.Core.Database.DatabaseModel; -using UniSpy.Server.QueryReport.V1.Abstraction.Interface; -using System.Linq; -using UniSpy.Server.QueryReport.V1.Aggregation.Redis; -using System.Net; -using System.Collections.Generic; - -namespace UniSpy.Server.QueryReport.V1.Application -{ - public class StorageOperation : IStorageOperation - { - private static GameServerCache.RedisClient _redisClient = new(); - public static IStorageOperation Persistance = new StorageOperation(); - public string GetGameSecretKey(string gameName) - { - using (var db = new UniSpyContext()) - { - var result = from p in db.Games - where p.Gamename == gameName - select new { p.Secretkey }; - - if (result.Count() != 1) - { - throw new QueryReport.Exception($"No secret key found for game:{gameName}"); - } - return result.First().Secretkey; - } - } - public List GetServersInfo(string gameName) - { - var result = _redisClient.Context.Where(s => s.GameName == gameName).ToList(); - return result; - } - public GameServerCache GetServerInfo(IPEndPoint endPoint) - { - var result = _redisClient.Context.Where(s => s.HostIPAddress == endPoint.Address && s.HostPort == endPoint.Port); - if (result.Count() != 1) - { - throw new QueryReport.Exception("Multiple server found in redis."); - } - return result.First(); - } - public void UpdateServerInfo(GameServerCache info) - { - _ = _redisClient.SetValueAsync(info); - } - public void RemoveServerInfo(IPEndPoint endPoint) - { - var result = _redisClient.Context.Where(s => s.HostIPAddress == endPoint.Address && s.HostPort == endPoint.Port); - foreach (var info in result) - { - _redisClient.DeleteKeyValue(info); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Contract/Request/EchoRequest.cs b/src/Servers/QueryReport/src/V1/Contract/Request/EchoRequest.cs deleted file mode 100644 index a7a5e5f03..000000000 --- a/src/Servers/QueryReport/src/V1/Contract/Request/EchoRequest.cs +++ /dev/null @@ -1,27 +0,0 @@ -using UniSpy.Server.QueryReport.V1.Abstraction.BaseClass; - -namespace UniSpy.Server.QueryReport.V1.Contract.Request -{ - public sealed class EchoRequest : RequestBase - { - public string Validate { get; private set; } - public string GameName { get; private set; } - public EchoRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - if (!KeyValues.ContainsKey("validate")) - { - throw new QueryReport.Exception("validate missing from request."); - } - if (!KeyValues.ContainsKey("gamename")) - { - throw new QueryReport.Exception("gamename missing from request."); - } - - throw new System.NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Contract/Request/HeartbeatRequest.cs b/src/Servers/QueryReport/src/V1/Contract/Request/HeartbeatRequest.cs deleted file mode 100644 index 260c1aba4..000000000 --- a/src/Servers/QueryReport/src/V1/Contract/Request/HeartbeatRequest.cs +++ /dev/null @@ -1,39 +0,0 @@ -using UniSpy.Server.QueryReport.V1.Abstraction.BaseClass; - - -namespace UniSpy.Server.QueryReport.V1.Contract.Request -{ - public sealed class HeartbeatRequest : RequestBase - { - public int QueryReportPort { get; private set; } - public string GameName { get; private set; } - public bool IsStateChanged { get; private set; } - public HeartbeatRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - if (!KeyValues.ContainsKey("heartbeat")) - { - throw new QueryReport.Exception("No query report port found."); - } - if (!int.TryParse(KeyValues["heartbeat"], out var port)) - { - throw new QueryReport.Exception("Query report port invalid."); - } - QueryReportPort = port; - if (!KeyValues.ContainsKey("gamename")) - { - throw new QueryReport.Exception("No gamename found."); - } - GameName = KeyValues["gamename"]; - - if (KeyValues.ContainsKey("statechanged")) - { - IsStateChanged = true; - } - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Contract/Request/ValidateRequest.cs b/src/Servers/QueryReport/src/V1/Contract/Request/ValidateRequest.cs deleted file mode 100644 index 062b30375..000000000 --- a/src/Servers/QueryReport/src/V1/Contract/Request/ValidateRequest.cs +++ /dev/null @@ -1,22 +0,0 @@ - -using UniSpy.Server.QueryReport.V1.Abstraction.BaseClass; - -namespace UniSpy.Server.QueryReport.V1.Contract.Request -{ - public sealed class ValidateRequest : RequestBase - { - public string ValidateKey { get; private set; } - public ValidateRequest(string rawRequest) : base(rawRequest) - { - } - public override void Parse() - { - base.Parse(); - if (!KeyValues.ContainsKey("validate")) - { - throw new QueryReport.Exception("validate request format not correct."); - } - ValidateKey = KeyValues["validate"]; - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Contract/Response/EchoResponse.cs b/src/Servers/QueryReport/src/V1/Contract/Response/EchoResponse.cs deleted file mode 100644 index 91b712bc6..000000000 --- a/src/Servers/QueryReport/src/V1/Contract/Response/EchoResponse.cs +++ /dev/null @@ -1,17 +0,0 @@ - -using UniSpy.Server.QueryReport.V1.Abstraction.BaseClass; - -namespace UniSpy.Server.QueryReport.V1.Contract.Response -{ - public sealed class EchoResponse : ResponseBase - { - public EchoResponse(RequestBase request) : base(request, null) - { - } - - public override void Build() - { - SendingBuffer = $@"/echo/{HeartbeatResponse.Challenge}/final/"; - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Contract/Response/HeartbeatResponse.cs b/src/Servers/QueryReport/src/V1/Contract/Response/HeartbeatResponse.cs deleted file mode 100644 index caa983187..000000000 --- a/src/Servers/QueryReport/src/V1/Contract/Response/HeartbeatResponse.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UniSpy.Server.QueryReport.V1.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V1.Contract.Request; - -namespace UniSpy.Server.QueryReport.V1.Contract.Response -{ - public sealed class HeartbeatResponse : ResponseBase - { - public const string Challenge = "000000"; - public HeartbeatResponse(HeartbeatRequest request) : base(request, null) - { - } - public override void Build() - { - SendingBuffer = $@"/secure/{Challenge}/final/"; - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Handler/CmdHandler/EchoHandler.cs b/src/Servers/QueryReport/src/V1/Handler/CmdHandler/EchoHandler.cs deleted file mode 100644 index cf129189d..000000000 --- a/src/Servers/QueryReport/src/V1/Handler/CmdHandler/EchoHandler.cs +++ /dev/null @@ -1,22 +0,0 @@ -using UniSpy.Server.QueryReport.Application; -using UniSpy.Server.QueryReport.V1.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V1.Contract.Request; -using UniSpy.Server.QueryReport.V1.Contract.Response; - -namespace UniSpy.Server.QueryReport.V1.Handler.CmdHandler -{ - /// - /// Keep alive request - /// - public sealed class EchoHandler : CmdHandlerBase - { - private new EchoRequest _request => (EchoRequest)base._request; - public EchoHandler(Client client, EchoRequest request) : base(client, request) - { - } - protected override void ResponseConstruct() - { - _response = new EchoResponse(_request); - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Handler/CmdHandler/HeartbeatHandler.cs b/src/Servers/QueryReport/src/V1/Handler/CmdHandler/HeartbeatHandler.cs deleted file mode 100644 index 699770969..000000000 --- a/src/Servers/QueryReport/src/V1/Handler/CmdHandler/HeartbeatHandler.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Net; -using UniSpy.Server.QueryReport.Application; -using UniSpy.Server.QueryReport.V1.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V1.Aggregation.Redis; -using UniSpy.Server.QueryReport.V1.Contract.Request; -using UniSpy.Server.QueryReport.V1.Contract.Response; - -namespace UniSpy.Server.QueryReport.V1.Handler.CmdHandler -{ - public sealed class HeartbeatHandler : CmdHandlerBase - { - private new HeartbeatRequest _request => (HeartbeatRequest)base._request; - public HeartbeatHandler(Client client, HeartbeatRequest request) : base(client, request) - { - } - protected override void DataOperation() - { - // _result.Challenge = Challenge; - var gameServerEnd = new IPEndPoint(_client.Connection.RemoteIPEndPoint.Address, _request.QueryReportPort); - _client.Info.GameSecretKey = QueryReport.V1.Application.StorageOperation.Persistance.GetGameSecretKey(_request.GameName); - // _result.Challenge = Challenge; - var info = new GameServerCache() - { - ServerID = _client.Server.Id, - HostIPAddress = _client.Connection.RemoteIPEndPoint.Address, - // check whether this indicate game port - HostPort = _request.QueryReportPort, - GameName = _request.GameName, - KeyValues = _request.KeyValues - }; - //todo whether we need to update the part that info changed - QueryReport.V1.Application.StorageOperation.Persistance.UpdateServerInfo(info); - } - protected override void ResponseConstruct() - { - _response = new HeartbeatResponse(_request); - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Handler/CmdHandler/ValidateHandler.cs b/src/Servers/QueryReport/src/V1/Handler/CmdHandler/ValidateHandler.cs deleted file mode 100644 index dd4a11df9..000000000 --- a/src/Servers/QueryReport/src/V1/Handler/CmdHandler/ValidateHandler.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.QueryReport.Application; -using UniSpy.Server.QueryReport.V1.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V1.Contract.Request; - -namespace UniSpy.Server.QueryReport.V1.Handler.CmdHandler -{ - /// - /// After sending heartbeat response, the game server will send validate to qr server to indicate his identity - /// - public sealed class ValidateHandler : CmdHandlerBase - { - public ValidateHandler(Client client, ValidateRequest request) : base(client, request) - { - } - protected override void DataOperation() - { - throw new System.Exception(); - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V1/Handler/CmdSwitcher.cs b/src/Servers/QueryReport/src/V1/Handler/CmdSwitcher.cs deleted file mode 100644 index 94864744b..000000000 --- a/src/Servers/QueryReport/src/V1/Handler/CmdSwitcher.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.Core.Misc; -using UniSpy.Server.QueryReport.Application; - -namespace UniSpy.Server.QueryReport.V1.Handler -{ - public sealed class CmdSwitcher : CmdSwitcherBase - { - private new string _rawRequest => (string)base._rawRequest; - private new Client _client => (Client)base._client; - public CmdSwitcher(Client client, string rawRequest) : base(client, rawRequest) - { - } - - protected override IHandler CreateCmdHandlers(object name, object rawRequest) - { - //todo add v1 support - _client.LogError("todo add v1 support"); - switch ((string)name) - { - case "heartbeat": - case "echo": - case "validate": - default: - return null; - } - } - - protected override void ProcessRawRequest() - { - // qr v1 protocol - var name = GameSpyUtils.GetRequestName(_rawRequest); - _requests.Add(new KeyValuePair(name, _rawRequest)); - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V2/Abstraction/BaseClass/CmdHandlerBase.cs b/src/Servers/QueryReport/src/V2/Abstraction/BaseClass/CmdHandlerBase.cs deleted file mode 100755 index b4e557463..000000000 --- a/src/Servers/QueryReport/src/V2/Abstraction/BaseClass/CmdHandlerBase.cs +++ /dev/null @@ -1,15 +0,0 @@ -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.QueryReport.Application; - -namespace UniSpy.Server.QueryReport.V2.Abstraction.BaseClass -{ - public abstract class CmdHandlerBase : UniSpy.Server.Core.Abstraction.BaseClass.CmdHandlerBase - { - protected new Client _client => (Client)base._client; - protected new RequestBase _request => (RequestBase)base._request; - protected new ResultBase _result { get => (ResultBase)base._result; set => base._result = value; } - protected CmdHandlerBase(IClient client, IRequest request) : base(client, request) - { - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Abstraction/BaseClass/RequestBase.cs b/src/Servers/QueryReport/src/V2/Abstraction/BaseClass/RequestBase.cs deleted file mode 100755 index 36026532c..000000000 --- a/src/Servers/QueryReport/src/V2/Abstraction/BaseClass/RequestBase.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Linq; -using UniSpy.Server.QueryReport.V2.Enumerate; - - -namespace UniSpy.Server.QueryReport.V2.Abstraction.BaseClass -{ - public abstract class RequestBase : UniSpy.Server.Core.Abstraction.BaseClass.RequestBase - { - public static readonly byte[] MagicData = { 0xFE, 0XFD }; - public uint? InstantKey { get; protected set; } - public new RequestType CommandName { get => (RequestType)base.CommandName; set => base.CommandName = value; } - public new byte[] RawRequest { get => (byte[])base.RawRequest; protected set => base.RawRequest = value; } - - public RequestBase(byte[] rawRequest) : base(rawRequest) - { - } - - protected RequestBase() - { - } - - public override void Parse() - { - if (RawRequest.Length < 3) - { - throw new QueryReport.Exception("Query report request is invalid."); - } - CommandName = (RequestType)RawRequest[0]; - var instantKeyBytes = RawRequest.Skip(1).Take(4).ToArray(); - InstantKey = BitConverter.ToUInt32(instantKeyBytes); - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Abstraction/BaseClass/ResponseBase.cs b/src/Servers/QueryReport/src/V2/Abstraction/BaseClass/ResponseBase.cs deleted file mode 100755 index 9c0ca96e2..000000000 --- a/src/Servers/QueryReport/src/V2/Abstraction/BaseClass/ResponseBase.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace UniSpy.Server.QueryReport.V2.Abstraction.BaseClass -{ - public abstract class ResponseBase : UniSpy.Server.Core.Abstraction.BaseClass.ResponseBase - { - protected new RequestBase _request => (RequestBase)base._request; - protected new ResultBase _result => (ResultBase)base._result; - public new byte[] SendingBuffer - { - get => (byte[])base.SendingBuffer; - protected set => base.SendingBuffer = value; - } - public ResponseBase(RequestBase request, ResultBase result) : base(request, result) - { - } - - public override void Build() - { - List data = new List(); - data.AddRange(RequestBase.MagicData); - data.Add((byte)_request.CommandName); - data.AddRange(BitConverter.GetBytes((uint)_request.InstantKey)); - SendingBuffer = data.ToArray(); - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Abstraction/BaseClass/ResultBase.cs b/src/Servers/QueryReport/src/V2/Abstraction/BaseClass/ResultBase.cs deleted file mode 100755 index 05aee3a9b..000000000 --- a/src/Servers/QueryReport/src/V2/Abstraction/BaseClass/ResultBase.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Enumerate; - -namespace UniSpy.Server.QueryReport.V2.Abstraction.BaseClass -{ - public abstract class ResultBase : UniSpy.Server.Core.Abstraction.BaseClass.ResultBase - { - public PacketType? PacketType { get; protected set; } - public ResultBase() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V2/Abstraction/Interface/IStorageOperation.cs b/src/Servers/QueryReport/src/V2/Abstraction/Interface/IStorageOperation.cs deleted file mode 100644 index 766903fc3..000000000 --- a/src/Servers/QueryReport/src/V2/Abstraction/Interface/IStorageOperation.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; -using System.Net; -using UniSpy.Server.QueryReport.V2.Aggregate.Redis.GameServer; -using UniSpy.Server.QueryReport.V2.Contract.Request; - -namespace UniSpy.Server.QueryReport.V2.Abstraction.Interface -{ - public interface IStorageOperation - { - List GetServerInfos(uint instantKey); - void UpdateGameServer(GameServerCache info); - void RemoveGameServer(GameServerCache info); - GameServerCache GetGameServerInfo(IPEndPoint end); - List GetGameServerInfos(string gameName); - public void PublishClientMessage(ClientMessageRequest message); - - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V2/Aggregate/Misc/Encryption.cs b/src/Servers/QueryReport/src/V2/Aggregate/Misc/Encryption.cs deleted file mode 100755 index 00c766a22..000000000 --- a/src/Servers/QueryReport/src/V2/Aggregate/Misc/Encryption.cs +++ /dev/null @@ -1,116 +0,0 @@ -namespace UniSpy.Server.QueryReport.V2.Handler.SystemHandler.Encryption -{ - public class Encryption - { - public static byte GSValFunc(int reg) - { - if (reg < 26) - return (byte)(reg + 'A'); - - if (reg < 52) - return (byte)(reg + 'G'); - - if (reg < 62) - return (byte)(reg - 4); - - if (reg == 62) - return (byte)('+'); - - if (reg == 63) - return (byte)('/'); - - return (0); - } - - public static byte[] GSSecKey(byte[] dst, byte[] src, byte[] key, int enctype) - { - int i, size, keysz; - - byte[] enctmp = new byte[256]; - byte[] tmp = new byte[66]; - byte x, y, z, a, b; - //byte[] p; - byte[] enctype1_data = { - 0x01,0xba,0xfa,0xb2,0x51,0x00,0x54,0x80,0x75,0x16,0x8e,0x8e,0x02,0x08,0x36, - 0xa5,0x2d,0x05,0x0d,0x16,0x52,0x07,0xb4,0x22,0x8c,0xe9,0x09,0xd6,0xb9,0x26, - 0x00,0x04,0x06,0x05,0x00,0x13,0x18,0xc4,0x1e,0x5b,0x1d,0x76,0x74,0xfc,0x50, - 0x51,0x06,0x16,0x00,0x51,0x28,0x00,0x04,0x0a,0x29,0x78,0x51,0x00,0x01,0x11, - 0x52,0x16,0x06,0x4a,0x20,0x84,0x01,0xa2,0x1e,0x16,0x47,0x16,0x32,0x51,0x9a, - 0xc4,0x03,0x2a,0x73,0xe1,0x2d,0x4f,0x18,0x4b,0x93,0x4c,0x0f,0x39,0x0a,0x00, - 0x04,0xc0,0x12,0x0c,0x9a,0x5e,0x02,0xb3,0x18,0xb8,0x07,0x0c,0xcd,0x21,0x05, - 0xc0,0xa9,0x41,0x43,0x04,0x3c,0x52,0x75,0xec,0x98,0x80,0x1d,0x08,0x02,0x1d, - 0x58,0x84,0x01,0x4e,0x3b,0x6a,0x53,0x7a,0x55,0x56,0x57,0x1e,0x7f,0xec,0xb8, - 0xad,0x00,0x70,0x1f,0x82,0xd8,0xfc,0x97,0x8b,0xf0,0x83,0xfe,0x0e,0x76,0x03, - 0xbe,0x39,0x29,0x77,0x30,0xe0,0x2b,0xff,0xb7,0x9e,0x01,0x04,0xf8,0x01,0x0e, - 0xe8,0x53,0xff,0x94,0x0c,0xb2,0x45,0x9e,0x0a,0xc7,0x06,0x18,0x01,0x64,0xb0, - 0x03,0x98,0x01,0xeb,0x02,0xb0,0x01,0xb4,0x12,0x49,0x07,0x1f,0x5f,0x5e,0x5d, - 0xa0,0x4f,0x5b,0xa0,0x5a,0x59,0x58,0xcf,0x52,0x54,0xd0,0xb8,0x34,0x02,0xfc, - 0x0e,0x42,0x29,0xb8,0xda,0x00,0xba,0xb1,0xf0,0x12,0xfd,0x23,0xae,0xb6,0x45, - 0xa9,0xbb,0x06,0xb8,0x88,0x14,0x24,0xa9,0x00,0x14,0xcb,0x24,0x12,0xae,0xcc, - 0x57,0x56,0xee,0xfd,0x08,0x30,0xd9,0xfd,0x8b,0x3e,0x0a,0x84,0x46,0xfa,0x77,0xb8 - }; - - size = src.Length; - - if (size < 1 || size > 65) - { - dst[0] = 0; - return dst; - } - - keysz = key.Length; - - for (i = 0; i < 256; i++) - { - enctmp[i] = (byte)i; - } - - a = 0; b = 0; - - for (i = 0; src[i] != 0; i++) - { - a += (byte)(src[i] + 1); - x = enctmp[a]; - b += x; - y = enctmp[b]; - enctmp[b] = x; - enctmp[a] = y; - tmp[i] = (byte)(src[i] ^ enctmp[(x + y) & 0xff]); - } - - for (size = i; size % 3 != 0; size++) - { - tmp[size] = 0; - } - - if (enctype == 1) - { - for (i = 0; i < size; i++) - { - tmp[i] = enctype1_data[tmp[i]]; - } - } - else if (enctype == 2) - { - for (i = 0; i < size; i++) - { - tmp[i] ^= key[i % keysz]; - } - } - - for (i = 0; i < size; i += 3) - { - x = tmp[i]; - y = tmp[i + 1]; - z = tmp[i + 2]; - - dst[i++] = GSValFunc(x >> 2); - dst[i++] = GSValFunc(((x & 3) << 4) | (y >> 4)); - dst[i++] = GSValFunc(((y & 15) << 2) | (z >> 6)); - dst[i++] = GSValFunc(z & 63); - } - - return dst; - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V2/Aggregate/Misc/NatNegCookie.cs b/src/Servers/QueryReport/src/V2/Aggregate/Misc/NatNegCookie.cs deleted file mode 100755 index 024c16236..000000000 --- a/src/Servers/QueryReport/src/V2/Aggregate/Misc/NatNegCookie.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Net; -using Newtonsoft.Json; -using UniSpy.Server.Core.Misc; - -namespace UniSpy.Server.QueryReport.V2.Aggregate.NatNeg -{ - public record NatNegCookie - { - [JsonConverter(typeof(IPAddresConverter))] - public IPAddress HostIPAddress { get; init; } - public ushort HostPort { get; init; } - [JsonConverter(typeof(IPEndPointConverter))] - public IPEndPoint HeartBeatIPEndPoint { get; init; } - public string GameName { get; init; } - public byte[] NatNegMessage { get; init; } - public uint InstantKey { get; init; } - - public NatNegCookie() - { - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Aggregate/Misc/RegionID.cs b/src/Servers/QueryReport/src/V2/Aggregate/Misc/RegionID.cs deleted file mode 100755 index 0bc63c8d0..000000000 --- a/src/Servers/QueryReport/src/V2/Aggregate/Misc/RegionID.cs +++ /dev/null @@ -1,30 +0,0 @@ -namespace UniSpy.Server.QueryReport.V2.Aggregate -{ - public enum RegionID - { - Americas = 1, - NorthAmerica = 2, - Caribbean = 4, - CentralAmerica = 8, - SouthAmerica = 16, - Africa = 32, - CentralAfrica = 64, - EastAfrica = 128, - NorthAfrica = 256, - SouthAfrica = 512, - WestAfrica = 1024, - Asia = 2048, - EastAsia = 4096, - Pacific = 8192, - SouthAsia = 16384, - SouthEastAsia = 32768, - Europe = 65536, - BalticStates = 131072, - CIS = 262144, - EasternEurope = 524288, - MiddleEast = 1048576, - SouthEastEurope = 2097152, - WesternEurope = 4194304 - - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V2/Aggregate/Redis/GameServerInfo.cs b/src/Servers/QueryReport/src/V2/Aggregate/Redis/GameServerInfo.cs deleted file mode 100644 index 130c46092..000000000 --- a/src/Servers/QueryReport/src/V2/Aggregate/Redis/GameServerInfo.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Net; -using Newtonsoft.Json; -using UniSpy.LinqToRedis; -using UniSpy.Server.QueryReport.V2.Enumerate; -using UniSpy.Server.Core.Misc; -using UniSpy.Server.Core.Extension.Redis; -using System.Linq; - -namespace UniSpy.Server.QueryReport.V2.Aggregate.Redis.GameServer -{ - public record GameServerCache : UniSpy.Server.Core.Abstraction.BaseClass.RedisKeyValueObject - { - [RedisKey] - public Guid? ServerID { get; set; } - [RedisKey] - [JsonConverter(typeof(IPAddresConverter))] - public IPAddress HostIPAddress { get; set; } - [RedisKey] - public uint? InstantKey { get; set; } - [RedisKey] - public string GameName { get; set; } - /// - /// The port for send heartbeat to query report server. - /// When a client want to connect to this game server, Server browser will send natneg message to this port.👌 - /// Must set this as nullable object, otherwise it will have default value - /// - [RedisKey] - public ushort? QueryReportPort { get; set; } - [JsonIgnore] - public IPEndPoint QueryReportIPEndPoint => new IPEndPoint(HostIPAddress, (int)QueryReportPort); - [JsonIgnore] - /// - /// Get the hton bytes of query report port, easy for access - /// - public byte[] QueryReportPortBytes => BitConverter.GetBytes((ushort)QueryReportPort).Reverse().ToArray(); - public DateTime LastPacketReceivedTime { get; set; } - // public IPEndPoint RemoteQueryReportIPEndPoint { get; set; } - public GameServerStatus ServerStatus { get; set; } - public Dictionary ServerData { get; set; } - public List> PlayerData { get; set; } - public List> TeamData { get; set; } - public GameServerCache() : base(RedisDbNumber.GameServerV2, TimeSpan.FromSeconds(30)) - { - } - internal class RedisClient : UniSpy.Server.Core.Abstraction.BaseClass.RedisClient - { - public RedisClient() { } - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V2/Aggregate/Redis/HeartBeatChannel.cs b/src/Servers/QueryReport/src/V2/Aggregate/Redis/HeartBeatChannel.cs deleted file mode 100644 index 578c01835..000000000 --- a/src/Servers/QueryReport/src/V2/Aggregate/Redis/HeartBeatChannel.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.Core.Extension.Redis; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V2.Aggregate.Redis.GameServer; - -namespace UniSpy.Server.QueryReport.V2.Aggregate.Redis -{ - public class HeartbeatChannel : RedisChannelBase - { - public HeartbeatChannel() : base(RedisChannelName.HeartbeatChannel) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V2/Aggregate/Redis/NatNegChannel.cs b/src/Servers/QueryReport/src/V2/Aggregate/Redis/NatNegChannel.cs deleted file mode 100644 index 86ea60186..000000000 --- a/src/Servers/QueryReport/src/V2/Aggregate/Redis/NatNegChannel.cs +++ /dev/null @@ -1,30 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Contract.Request; -using UniSpy.Server.QueryReport.V2.Handler.CmdHandler; -using UniSpy.Server.Core.Extension.Redis; -using UniSpy.Server.Core.Extension; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.Application; -using System.Net; - -namespace UniSpy.Server.QueryReport.V2.Aggregate.Redis -{ - public sealed class NatNegChannel : RedisChannelBase - { - public NatNegChannel() : base(RedisChannelName.NatNegCookieChannel) - { - } - public override void ReceivedMessage(ClientMessageRequest message) - { - var client = (Client)ClientManagerBase.GetClient(message.TargetIPEndPoint); - if (client is null) - { - LogWriter.LogWarn($"Client:{message.TargetIPEndPoint} not found, we ignore natneg message from SB: {message.ServerBrowserSenderId}"); - return; - } - - client.LogInfo($"Get client message from server browser: {message.ServerBrowserSenderId} [{StringExtensions.ConvertByteToHexString(message.NatNegMessage)}]"); - new ClientMessageHandler(client, message).Handle(); - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V2/Application/StorageOperation.cs b/src/Servers/QueryReport/src/V2/Application/StorageOperation.cs deleted file mode 100644 index 4489a6d1f..000000000 --- a/src/Servers/QueryReport/src/V2/Application/StorageOperation.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.QueryReport.V2.Abstraction.Interface; -using UniSpy.Server.QueryReport.V2.Aggregate.Redis.GameServer; -using System.Linq; -using UniSpy.Server.QueryReport.V2.Aggregate.Redis; -using UniSpy.Server.Core.Database.DatabaseModel; -using System.Net; -using UniSpy.Server.QueryReport.V2.Contract.Request; - -namespace UniSpy.Server.QueryReport.V2.Application -{ - public sealed class StorageOperation : IStorageOperation - { - private static GameServerCache.RedisClient _gameServerRedisClient = new(); - public static IStorageOperation Persistance = new StorageOperation(); - public static NatNegChannel NatNegChannel = new NatNegChannel(); - /// - /// we do not run subscribe() in QR because QR only need to push. - /// We run subscribe() in SB, because SB need to receive message. - /// - public static HeartbeatChannel HeartbeatChannel = new HeartbeatChannel(); - public List GetServerInfos(uint instantKey) => _gameServerRedisClient.Context.Where(x => x.InstantKey == instantKey).ToList(); - - public void RemoveGameServer(GameServerCache info) => _gameServerRedisClient.DeleteKeyValue(info); - public void UpdateGameServer(GameServerCache info) => _ = _gameServerRedisClient.SetValueAsync(info); - public List GetGameServerInfos(string gameName) - { - return _gameServerRedisClient.Context.Where(x => x.GameName == gameName).ToList(); - } - - public GameServerCache GetGameServerInfo(IPEndPoint end) - { - return _gameServerRedisClient.Context.FirstOrDefault(x => - x.HostIPAddress == end.Address & - x.QueryReportPort == (ushort)end.Port); - } - - // publish client message here - public void PublishClientMessage(ClientMessageRequest message) - { - NatNegChannel.PublishMessage(message); - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V2/Contract/Request/AvaliableRequest.cs b/src/Servers/QueryReport/src/V2/Contract/Request/AvaliableRequest.cs deleted file mode 100755 index 07b73aed4..000000000 --- a/src/Servers/QueryReport/src/V2/Contract/Request/AvaliableRequest.cs +++ /dev/null @@ -1,34 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; - - -namespace UniSpy.Server.QueryReport.V2.Contract.Request -{ - - public sealed class AvaliableRequest : RequestBase - { - public static readonly byte[] Prefix = { 0x09, 0x00, 0x00, 0x00, 0x00 }; - public static readonly byte Postfix = 0x00; - public AvaliableRequest(byte[] rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - //prefix check - for (int i = 0; i < AvaliableRequest.Prefix.Length; i++) - { - if (RawRequest[i] != AvaliableRequest.Prefix[i]) - { - throw new QueryReport.Exception("Avaliable request prefix is invalid."); - } - } - - //postfix check - if (RawRequest[RawRequest.Length - 1] != AvaliableRequest.Postfix) - { - throw new QueryReport.Exception("Avaliable request postfix is invalid."); - } - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Contract/Request/ChallengeRequest.cs b/src/Servers/QueryReport/src/V2/Contract/Request/ChallengeRequest.cs deleted file mode 100755 index 24dc3a9a4..000000000 --- a/src/Servers/QueryReport/src/V2/Contract/Request/ChallengeRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; - -namespace UniSpy.Server.QueryReport.V2.Contract.Request -{ - - public sealed class ChallengeRequest : RequestBase - { - public ChallengeRequest(byte[] rawRequest) : base(rawRequest) - { - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Contract/Request/ClientMessageAckRequest.cs b/src/Servers/QueryReport/src/V2/Contract/Request/ClientMessageAckRequest.cs deleted file mode 100644 index 65d9d2b2f..000000000 --- a/src/Servers/QueryReport/src/V2/Contract/Request/ClientMessageAckRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; - -namespace UniSpy.Server.QueryReport.V2.Contract.Request -{ - - public sealed class ClientMessageAckRequest : RequestBase - { - public ClientMessageAckRequest(byte[] rawRequest) : base(rawRequest) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V2/Contract/Request/ClientMessageRequest.cs b/src/Servers/QueryReport/src/V2/Contract/Request/ClientMessageRequest.cs deleted file mode 100755 index 9f2700c1f..000000000 --- a/src/Servers/QueryReport/src/V2/Contract/Request/ClientMessageRequest.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System; -using System.Net; -using Newtonsoft.Json; -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; -using UniSpy.Server.Core.Misc; -using System.Linq; - -namespace UniSpy.Server.QueryReport.V2.Contract.Request -{ - public sealed class ClientMessageRequest : RequestBase - { - public Guid ServerBrowserSenderId { get; init; } - public new uint? InstantKey { get => base.InstantKey; set => base.InstantKey = value; } - public byte[] NatNegMessage { get; init; } - [JsonConverter(typeof(IPEndPointConverter))] - public IPEndPoint TargetIPEndPoint { get; init; } - /// - /// query report server will check on MessageKey, so this must different than previous ones - /// - /// - public int? MessageKey => new Random().Next(int.MinValue, int.MaxValue); - public uint Cookie => BitConverter.ToUInt32(NatNegMessage.Skip(6).Take(4).ToArray()); - public ClientMessageRequest() : base(null) - { - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Contract/Request/EchoRequest.cs b/src/Servers/QueryReport/src/V2/Contract/Request/EchoRequest.cs deleted file mode 100644 index 5dc1d2cfb..000000000 --- a/src/Servers/QueryReport/src/V2/Contract/Request/EchoRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; - -namespace UniSpy.Server.QueryReport.V2.Contract.Request -{ - - public class EchoRequest : RequestBase - { - public EchoRequest(byte[] rawRequest) : base(rawRequest) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V2/Contract/Request/HeartBeatRequest.cs b/src/Servers/QueryReport/src/V2/Contract/Request/HeartBeatRequest.cs deleted file mode 100755 index 863b7a590..000000000 --- a/src/Servers/QueryReport/src/V2/Contract/Request/HeartBeatRequest.cs +++ /dev/null @@ -1,204 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V2.Enumerate; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.QueryReport.V2.Contract.Request -{ - - public sealed class HeartBeatRequest : RequestBase - { - public Dictionary ServerData { get; private set; } - public List> PlayerData { get; private set; } - public List> TeamData { get; private set; } - public GameServerStatus ServerStatus { get; private set; } - public uint? GroupId { get; private set; } - public string GameName - { - get - { - List tempKeyVal = DataPartition.Split('\0').ToList(); - int indexOfGameName = tempKeyVal.IndexOf("gamename"); - return tempKeyVal[indexOfGameName + 1]; - } - } - public string DataPartition { get; private set; } - // public HeartBeatReportType ReportType { get; set; } - - public HeartBeatRequest(byte[] rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - int playerPos, teamPos; - int playerLenth, teamLength; - DataPartition = UniSpyEncoding.GetString(RawRequest.Skip(5).ToArray()); - - playerPos = DataPartition.IndexOf("player_\0", StringComparison.Ordinal); - teamPos = DataPartition.IndexOf("team_t\0", StringComparison.Ordinal); - // todo if there is no server data, we whether need to throw exception? - if (playerPos != -1 && teamPos != -1) - { - // ReportType = HeartBeatReportType.ServerTeamPlayerData; - playerLenth = teamPos - playerPos; - teamLength = DataPartition.Length - teamPos; - - var serverDataStr = DataPartition.Substring(0, playerPos - 4); - ParseServerData(serverDataStr); - var playerDataStr = DataPartition.Substring(playerPos - 1, playerLenth - 2); - ParsePlayerData(playerDataStr); - var teamDataStr = DataPartition.Substring(teamPos - 1, teamLength); - ParseTeamData(teamDataStr); - } - else if (playerPos != -1) - { - //normal heart beat - // ReportType = HeartBeatReportType.ServerPlayerData; - playerLenth = DataPartition.Length - playerPos; - var serverDataStr = DataPartition.Substring(0, playerPos - 4); - ParseServerData(serverDataStr); - var playerDataStr = DataPartition.Substring(playerPos - 1, playerLenth); - ParsePlayerData(playerDataStr); - } - else if (playerPos == -1 && teamPos == -1) - { - // ReportType = HeartBeatReportType.ServerData; - var serverDataStr = DataPartition; - ParseServerData(serverDataStr); - } - else - { - throw new QueryReport.Exception("HeartBeat request is invalid."); - } - - if (ServerData.ContainsKey("groupid")) - { - if (!uint.TryParse(ServerData["groupid"], out var groupId)) - { - throw new QueryReport.Exception("GroupId is invalid."); - - } - GroupId = groupId; - } - } - private void ParseServerData(string serverDataStr) - { - ServerData = new Dictionary(); - string[] keyValueArray = serverDataStr.Split("\0"); - - for (int i = 0; i < keyValueArray.Length; i += 2) - { - if (i + 2 > keyValueArray.Length) - { - break; - } - - string tempKey = keyValueArray[i]; - string tempValue = keyValueArray[i + 1]; - - if (tempKey == "") - { - LogWriter.LogVerbose("Skiping empty key value"); - continue; - } - // no matter happens we just update the key value - if (ServerData.ContainsKey(tempKey)) - { - ServerData[tempKey] = tempValue; - } - else - { - ServerData.Add(tempKey, tempValue); - } - } - if (!ServerData.ContainsKey("statechanged")) - { - ServerStatus = GameServerStatus.Normal; - } - else - { - ServerStatus = (GameServerStatus)Enum.Parse(typeof(GameServerStatus), ServerData?["statechanged"]); - } - } - private void ParsePlayerData(string playerDataStr) - { - PlayerData = new List>(); - // _client.LogInfo(StringExtensions.ReplaceUnreadableCharToHex(playerDataStr)); - //TODO check if each update contains all player information - int playerCount = Convert.ToInt32(playerDataStr[0]); - playerDataStr = playerDataStr.Substring(1); - - //we first read the key index - int IndexOfKey = playerDataStr.IndexOf("\0\0", StringComparison.Ordinal); - //then get all the keys - string keyStr = playerDataStr.Substring(0, IndexOfKey); - List keys = keyStr.Split('\0', StringSplitOptions.RemoveEmptyEntries).ToList(); - - string valuesStr = playerDataStr.Substring(IndexOfKey + 2); - List values = valuesStr.Split('\0').ToList(); - - //according to player total number and key total number to add the data into list - for (int playerIndex = 0; playerIndex < playerCount; playerIndex++) - { - Dictionary keyValue = new Dictionary(); - - for (int keyIndex = 0; keyIndex < keys.Count; keyIndex++) - { - string tempKey = keys[keyIndex] + playerIndex.ToString(); - string tempValue = values[playerIndex * keys.Count + keyIndex]; - if (keyValue.ContainsKey(tempKey)) - { - keyValue[tempKey] = tempValue; - } - else - { - keyValue.Add(tempKey, tempValue); - } - } - PlayerData.Add(keyValue); - } - } - private void ParseTeamData(string teamDataStr) - { - TeamData = new List>(); - // _client.LogInfo(// StringExtensions.ReplaceUnreadableCharToHex(teamDataStr)); - //TODO check if each update contains all team information - - int teamCount = System.Convert.ToInt32(teamDataStr[0]); - teamDataStr = teamDataStr.Substring(1); - - int endKeyIndex = teamDataStr.IndexOf("\0\0", System.StringComparison.Ordinal); - string keyStr = teamDataStr.Substring(0, endKeyIndex); - List keys = keyStr.Split('\0', System.StringSplitOptions.RemoveEmptyEntries).ToList(); - - string valueStr = teamDataStr.Substring(endKeyIndex + 2); - List values = valueStr.Split('\0').ToList(); - - for (int teamIndex = 0; teamIndex < teamCount; teamIndex++) - { - Dictionary keyValue = new Dictionary(); - // iterate the index get the keys and values - for (int keyIndex = 0; keyIndex < keys.Count(); keyIndex++) - { - string tempKey = keys[keyIndex] + teamIndex.ToString(); - string tempValue = values[teamIndex * keys.Count + keyIndex]; - if (keyValue.ContainsKey(tempKey)) - { - keyValue[tempKey] = tempValue; - } - else - { - keyValue.Add(tempKey, tempValue); - } - //LogWriter.ToLog(LogEventLevel.Verbose, $"Updated new team key value {tempKey}:{tempValue}"); - } - TeamData.Add(keyValue); - } - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Contract/Request/KeepAliveRequest.cs b/src/Servers/QueryReport/src/V2/Contract/Request/KeepAliveRequest.cs deleted file mode 100644 index 83bc2d107..000000000 --- a/src/Servers/QueryReport/src/V2/Contract/Request/KeepAliveRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; - -namespace UniSpy.Server.QueryReport.V2.Contract.Request -{ - - public class KeepAliveRequest : RequestBase - { - public KeepAliveRequest(byte[] rawRequest) : base(rawRequest) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V2/Contract/Response/AvaliableResponse.cs b/src/Servers/QueryReport/src/V2/Contract/Response/AvaliableResponse.cs deleted file mode 100755 index a483becec..000000000 --- a/src/Servers/QueryReport/src/V2/Contract/Response/AvaliableResponse.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V2.Enumerate; -using UniSpy.Server.QueryReport.V2.Contract.Request; - -namespace UniSpy.Server.QueryReport.V2.Contract.Response -{ - public sealed class AvaliableResponse : ResponseBase - { - public static readonly byte[] ResponsePrefix = { 0xfe, 0xfd, 0x09, 0x00, 0x00, 0x00 }; - - public AvaliableResponse(AvaliableRequest request) : base(request, null) - { - } - - public override void Build() - { - List data = new List(); - - data.AddRange(ResponsePrefix); - data.Add((byte)ServerAvailability.Available); - // NOTE: Change this if you want to make the server not avaliable. - SendingBuffer = data.ToArray(); - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Contract/Response/ChallengeResponse.cs b/src/Servers/QueryReport/src/V2/Contract/Response/ChallengeResponse.cs deleted file mode 100755 index 7e3c0ee4e..000000000 --- a/src/Servers/QueryReport/src/V2/Contract/Response/ChallengeResponse.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V2.Contract.Request; -using UniSpy.Server.QueryReport.V2.Contract.Result; -using UniSpy.Server.Core.Encryption; - -namespace UniSpy.Server.QueryReport.V2.Contract.Response -{ - public sealed class ChallengeResponse : ResponseBase - { - private static string Message = "RetroSpy echo!"; - - public ChallengeResponse(ChallengeRequest request, ChallengeResult result) : base(request, result) - { - } - - public override void Build() - { - base.Build(); - List data = new List(); - - data.AddRange(SendingBuffer); - data.AddRange(UniSpyEncoding.GetBytes(Message)); - - SendingBuffer = data.ToArray(); - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Contract/Response/ClientMessageResponse.cs b/src/Servers/QueryReport/src/V2/Contract/Response/ClientMessageResponse.cs deleted file mode 100755 index 3624e69d0..000000000 --- a/src/Servers/QueryReport/src/V2/Contract/Response/ClientMessageResponse.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System; -using System.Collections.Generic; -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V2.Contract.Request; - -namespace UniSpy.Server.QueryReport.V2.Contract.Response -{ - public sealed class ClientMessageResponse : ResponseBase - { - private new ClientMessageRequest _request => (ClientMessageRequest)base._request; - public ClientMessageResponse(ClientMessageRequest request) : base(request, null) - { - } - - public override void Build() - { - base.Build(); - List data = new List(); - data.AddRange(SendingBuffer); - data.AddRange(BitConverter.GetBytes((int)_request.MessageKey)); - data.AddRange(_request.NatNegMessage); - SendingBuffer = data.ToArray(); - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Contract/Response/HeartBeatResponse.cs b/src/Servers/QueryReport/src/V2/Contract/Response/HeartBeatResponse.cs deleted file mode 100755 index 84c4dd5d4..000000000 --- a/src/Servers/QueryReport/src/V2/Contract/Response/HeartBeatResponse.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System; -using System.Collections.Generic; -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V2.Contract.Request; -using UniSpy.Server.QueryReport.V2.Contract.Result; - -namespace UniSpy.Server.QueryReport.V2.Contract.Response -{ - public sealed class HeartBeatResponse : ResponseBase - { - private new HeartBeatRequest _request => (HeartBeatRequest)base._request; - private new HeartBeatResult _result => (HeartBeatResult)base._result; - private static readonly byte[] Challenge = { 0x54, 0x54, 0x54, 0x00, 0x00 }; - private static readonly byte[] Spliter = { 0x00, 0x00, 0x00, 0x00 }; - public HeartBeatResponse(HeartBeatRequest request, HeartBeatResult result) : base(request, result) - { - } - - public override void Build() - { - base.Build(); - List data = new List(); - data.AddRange(SendingBuffer); - data.AddRange(Challenge); - data.AddRange(_result.RemoteIPEndPoint.Address.GetAddressBytes()); - data.AddRange(Spliter); - data.AddRange(BitConverter.GetBytes((int)_result.RemoteIPEndPoint.Port)); - SendingBuffer = data.ToArray(); - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Contract/Response/KeepAliveResponse.cs b/src/Servers/QueryReport/src/V2/Contract/Response/KeepAliveResponse.cs deleted file mode 100755 index 65b6fd317..000000000 --- a/src/Servers/QueryReport/src/V2/Contract/Response/KeepAliveResponse.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V2.Contract.Request; - -namespace UniSpy.Server.QueryReport.V2.Contract.Response -{ - public sealed class KeepAliveResponse : ResponseBase - { - public KeepAliveResponse(KeepAliveRequest request) : base(request, null) - { - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Contract/Result/ChallengeResult.cs b/src/Servers/QueryReport/src/V2/Contract/Result/ChallengeResult.cs deleted file mode 100755 index 70ab40c54..000000000 --- a/src/Servers/QueryReport/src/V2/Contract/Result/ChallengeResult.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; - -namespace UniSpy.Server.QueryReport.V2.Contract.Result -{ - public sealed class ChallengeResult : ResultBase - { - public ChallengeResult() - { - PacketType = Enumerate.PacketType.Challenge; - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Contract/Result/ClientMessageResult.cs b/src/Servers/QueryReport/src/V2/Contract/Result/ClientMessageResult.cs deleted file mode 100755 index 2bfb65470..000000000 --- a/src/Servers/QueryReport/src/V2/Contract/Result/ClientMessageResult.cs +++ /dev/null @@ -1,15 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; - -namespace UniSpy.Server.QueryReport.V2.Contract.Result -{ - public sealed class ClientMessageResult : ResultBase - { - public byte[] NatNegMessage { get; set; } - public int? MessageKey { get; set; } - public ClientMessageResult() - { - //we need to change packet type to client message then send - PacketType = Enumerate.PacketType.ClientMessage; - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Contract/Result/EchoResult.cs b/src/Servers/QueryReport/src/V2/Contract/Result/EchoResult.cs deleted file mode 100755 index a314343da..000000000 --- a/src/Servers/QueryReport/src/V2/Contract/Result/EchoResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V2.Aggregate.Redis.GameServer; - -namespace UniSpy.Server.QueryReport.V2.Contract.Result -{ - public sealed class EchoResult : ResultBase - { - public GameServerCache Info { get; set; } - public EchoResult() - { - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Contract/Result/HeartBeatResult.cs b/src/Servers/QueryReport/src/V2/Contract/Result/HeartBeatResult.cs deleted file mode 100755 index 9ca70f10b..000000000 --- a/src/Servers/QueryReport/src/V2/Contract/Result/HeartBeatResult.cs +++ /dev/null @@ -1,14 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; -using System.Net; - -namespace UniSpy.Server.QueryReport.V2.Contract.Result -{ - public sealed class HeartBeatResult : ResultBase - { - public IPEndPoint RemoteIPEndPoint { get; set; } - public HeartBeatResult() - { - PacketType = Enumerate.PacketType.HeartBeat; - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Enumerate/GeneralEnum.cs b/src/Servers/QueryReport/src/V2/Enumerate/GeneralEnum.cs deleted file mode 100755 index 51e524875..000000000 --- a/src/Servers/QueryReport/src/V2/Enumerate/GeneralEnum.cs +++ /dev/null @@ -1,51 +0,0 @@ -namespace UniSpy.Server.QueryReport.V2.Enumerate -{ - public enum RequestType : byte - { - //Client request - Challenge = 0x01, - HeartBeat = 0x03, - ClientMessage = 0x06, - ClientMessageAck = 0x07, - AddError = 0x04, - Echo = 0x02, - KeepAlive = 0x08, - AvaliableCheck = 0x09 - } - public enum ResponseType : byte - { - //Server response - Query = 0x00, - Echo = 0x02, - ADDError = 0x04, - ClientMessage = 0x06, - RequireIPVerify = 0x09, - ClientRegistered = 0x0A, - } - public enum PacketType : byte - { - //Server response - Query = 0x00, - Challenge = 0x01, - Echo = 0x02, - ADDError = 0x04, - ClientMessage = 0x06, - RequireIPVerify = 0x09, - ClientRegistered = 0x0A, - - //Client request - HeartBeat = 0x03, - EchoResponse = 0x05, - ClientMessageACK = 0x07, - KeepAlive = 0x08, - AvaliableCheck = 0x09 - } - - public enum QRStateChange : byte - { - NormalHeartBeat = 0, - GameModeChange = 1, - ServerShutDown = 2, - CanNotRecieveChallenge = 3 - } -} diff --git a/src/Servers/QueryReport/src/V2/Enumerate/HeartBeatEnum.cs b/src/Servers/QueryReport/src/V2/Enumerate/HeartBeatEnum.cs deleted file mode 100755 index f81594c91..000000000 --- a/src/Servers/QueryReport/src/V2/Enumerate/HeartBeatEnum.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace UniSpy.Server.QueryReport.V2.Enumerate -{ - public enum HeartBeatReportType - { - ServerTeamPlayerData, - ServerPlayerData, - ServerData, - } - - public enum GameServerStatus - { - Normal = 0, - Update = 1, - Shutdown = 2, - // todo check this flag - Playing = 3, - } -} diff --git a/src/Servers/QueryReport/src/V2/Enumerate/ServerAvailability.cs b/src/Servers/QueryReport/src/V2/Enumerate/ServerAvailability.cs deleted file mode 100755 index 6e6fa7481..000000000 --- a/src/Servers/QueryReport/src/V2/Enumerate/ServerAvailability.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace UniSpy.Server.QueryReport.V2.Enumerate -{ - public enum ServerAvailability : uint - { - Available = 0, - Waiting = 1, - PermanentUnavailable = 2, - TemporarilyUnavailable = 3, - }; -} diff --git a/src/Servers/QueryReport/src/V2/Handler/CmdHandler/AvailableHandler.cs b/src/Servers/QueryReport/src/V2/Handler/CmdHandler/AvailableHandler.cs deleted file mode 100755 index 3c3610496..000000000 --- a/src/Servers/QueryReport/src/V2/Handler/CmdHandler/AvailableHandler.cs +++ /dev/null @@ -1,23 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V2.Contract.Request; -using UniSpy.Server.QueryReport.V2.Contract.Response; -using UniSpy.Server.QueryReport.Application; - -namespace UniSpy.Server.QueryReport.V2.Handler.CmdHandler -{ - /// - /// AvailableCheckHandler - /// - - public sealed class AvailableHandler : CmdHandlerBase - { - private new AvaliableRequest _request => (AvaliableRequest)base._request; - public AvailableHandler(Client client, AvaliableRequest request) : base(client, request) - { - } - protected override void ResponseConstruct() - { - _response = new AvaliableResponse(_request); - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Handler/CmdHandler/ChallengeHandler.cs b/src/Servers/QueryReport/src/V2/Handler/CmdHandler/ChallengeHandler.cs deleted file mode 100755 index a967fc13f..000000000 --- a/src/Servers/QueryReport/src/V2/Handler/CmdHandler/ChallengeHandler.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Linq; -using UniSpy.Server.QueryReport.Application; -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V2.Contract.Request; -using UniSpy.Server.QueryReport.V2.Contract.Response; -using UniSpy.Server.QueryReport.V2.Contract.Result; - -namespace UniSpy.Server.QueryReport.V2.Handler.CmdHandler -{ - - public sealed class ChallengeHandler : CmdHandlerBase - { - private new ChallengeRequest _request => (ChallengeRequest)base._request; - //we do not need to implement this to check the correctness of the challenge response - private new ChallengeResult _result { get => (ChallengeResult)base._result; set => base._result = value; } - public ChallengeHandler(Client client, ChallengeRequest request) : base(client, request) - { - _result = new ChallengeResult(); - } - - protected override void DataOperation() - { - var servers = QueryReport.V2.Application.StorageOperation.Persistance.GetServerInfos((uint)_request.InstantKey); - if (servers.Count() == 0) - { - throw new QueryReport.Exception("No server found in redis, please make sure there is only one server."); - } - // _gameServerInfo = servers.First(); - } - - protected override void ResponseConstruct() - { - // We send the echo packet to check the ping - _response = new ChallengeResponse(_request, _result); - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Handler/CmdHandler/ClientMessageAckHandler.cs b/src/Servers/QueryReport/src/V2/Handler/CmdHandler/ClientMessageAckHandler.cs deleted file mode 100644 index 04aa5e1d4..000000000 --- a/src/Servers/QueryReport/src/V2/Handler/CmdHandler/ClientMessageAckHandler.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.QueryReport.Application; -using UniSpy.Server.QueryReport.V2.Contract.Request; - -namespace UniSpy.Server.QueryReport.V2.Handler.CmdHandler -{ - - public sealed class ClientMessageAckHandler : CmdHandlerBase - { - public ClientMessageAckHandler(Client client, ClientMessageAckRequest request) : base(client, request) - { - } - protected override void DataOperation() - { - base.DataOperation(); - _client.LogInfo("Get client message ack."); - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/src/V2/Handler/CmdHandler/ClientMessageHandler.cs b/src/Servers/QueryReport/src/V2/Handler/CmdHandler/ClientMessageHandler.cs deleted file mode 100755 index dc62492e8..000000000 --- a/src/Servers/QueryReport/src/V2/Handler/CmdHandler/ClientMessageHandler.cs +++ /dev/null @@ -1,26 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V2.Contract.Request; -using UniSpy.Server.QueryReport.V2.Contract.Response; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.QueryReport.Application; - -namespace UniSpy.Server.QueryReport.V2.Handler.CmdHandler -{ - - public sealed class ClientMessageHandler : CmdHandlerBase - { - private new ClientMessageRequest _request => (ClientMessageRequest)base._request; - public ClientMessageHandler(Client client, ClientMessageRequest request) : base(client, request) - { - } - protected override void RequestCheck() - { - // we do not need to execute request.Parse() - _client.LogInfo($"Received client message with cookie: {_request.Cookie}"); - } - protected override void ResponseConstruct() - { - _response = new ClientMessageResponse(_request); - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Handler/CmdHandler/EchoHandler.cs b/src/Servers/QueryReport/src/V2/Handler/CmdHandler/EchoHandler.cs deleted file mode 100755 index 907119d1f..000000000 --- a/src/Servers/QueryReport/src/V2/Handler/CmdHandler/EchoHandler.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using System.Linq; -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V2.Contract.Request; -using UniSpy.Server.QueryReport.V2.Contract.Result; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.QueryReport.Application; - -namespace UniSpy.Server.QueryReport.V2.Handler.CmdHandler -{ - - public sealed class EchoHandler : CmdHandlerBase - { - private new EchoRequest _request => (EchoRequest)base._request; - private new EchoResult _result { get => (EchoResult)base._result; set => base._result = value; } - public EchoHandler(Client client, EchoRequest request) : base(client, request) - { - _result = new EchoResult(); - } - - protected override void DataOperation() - { - //TODO prevent one pc create multiple game servers - var servers = QueryReport.V2.Application.StorageOperation.Persistance.GetServerInfos((uint)_request.InstantKey); - if (servers.Count() != 1) - { - _client.LogInfo("Can not find game server"); - return; - } - //add recive echo packet on gameserverList - - //we get the first key in matchedKeys - _result.Info = servers.First(); - _result.Info.LastPacketReceivedTime = DateTime.Now; - // StorageOperation.Persistance.UpdateGameServer(_result.Info); - QueryReport.V2.Application.StorageOperation.Persistance.UpdateGameServer(_result.Info); - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Handler/CmdHandler/HeartBeatHandler.cs b/src/Servers/QueryReport/src/V2/Handler/CmdHandler/HeartBeatHandler.cs deleted file mode 100755 index fa13f4e8e..000000000 --- a/src/Servers/QueryReport/src/V2/Handler/CmdHandler/HeartBeatHandler.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.V2.Enumerate; -using UniSpy.Server.QueryReport.V2.Aggregate.Redis.GameServer; -using UniSpy.Server.QueryReport.V2.Contract.Request; -using UniSpy.Server.QueryReport.V2.Contract.Response; -using UniSpy.Server.QueryReport.V2.Contract.Result; -using UniSpy.Server.QueryReport.Application; - -namespace UniSpy.Server.QueryReport.V2.Handler.CmdHandler -{ - - public sealed class HeartBeatHandler : CmdHandlerBase - { - private new HeartBeatRequest _request => (HeartBeatRequest)base._request; - private new HeartBeatResult _result { get => (HeartBeatResult)base._result; set => base._result = value; } - private GameServerCache _gameServerInfo; - public HeartBeatHandler(Client client, HeartBeatRequest request) : base(client, request) - { - _result = new HeartBeatResult(); - } - protected override void DataOperation() - { - //Parse the endpoint information into result class - _result.RemoteIPEndPoint = _client.Connection.RemoteIPEndPoint; - - CheckSpamGameServer(); - // todo check if server data is null - //normal heart beat - _gameServerInfo.ServerData = _request.ServerData; - _gameServerInfo.PlayerData = _request.PlayerData; - _gameServerInfo.TeamData = _request.TeamData; - _gameServerInfo.LastPacketReceivedTime = DateTime.Now; - UpdateGameServerByState(); - // we publish message to notice all sb server to send adhoc message to their clients. - QueryReport.V2.Application.StorageOperation.HeartbeatChannel.PublishMessage(_gameServerInfo); - } - - private void UpdateGameServerByState() - { - if (_gameServerInfo.ServerStatus == GameServerStatus.Shutdown) - { - QueryReport.V2.Application.StorageOperation.Persistance.RemoveGameServer(_gameServerInfo); - } - else - { - QueryReport.V2.Application.StorageOperation.Persistance.UpdateGameServer(_gameServerInfo); - } - } - - protected override void ResponseConstruct() - { - _response = new HeartBeatResponse(_request, _result); - } - - private void CheckSpamGameServer() - { - // Ensures that an IP address creates a server for each game, we check if redis has multiple game servers - _gameServerInfo = new GameServerCache() - { - ServerID = _client.Server.Id, - HostIPAddress = _client.Connection.RemoteIPEndPoint.Address, - QueryReportPort = (ushort)_client.Connection.RemoteIPEndPoint.Port, - GameName = _request.GameName, - InstantKey = _request.InstantKey, - ServerStatus = _request.ServerStatus, - LastPacketReceivedTime = DateTime.Now - }; - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Handler/CmdHandler/KeepAliveHandler.cs b/src/Servers/QueryReport/src/V2/Handler/CmdHandler/KeepAliveHandler.cs deleted file mode 100755 index 7024cd9e9..000000000 --- a/src/Servers/QueryReport/src/V2/Handler/CmdHandler/KeepAliveHandler.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Linq; -using UniSpy.Server.QueryReport.V2.Abstraction.BaseClass; -using UniSpy.Server.QueryReport.Application; -using UniSpy.Server.QueryReport.V2.Contract.Request; - -namespace UniSpy.Server.QueryReport.V2.Handler.CmdHandler -{ - - public sealed class KeepAliveHandler : CmdHandlerBase - { - public KeepAliveHandler(Client client, KeepAliveRequest request) : base(client, request) - { - } - protected override void DataOperation() - { - var result = QueryReport.V2.Application.StorageOperation.Persistance.GetServerInfos((uint)_request.InstantKey); - if (result.Count != 1) - { - throw new QueryReport.Exception("No server or multiple servers found in redis, please make sure there is only one server."); - } - - var gameServer = result.First(); - gameServer.LastPacketReceivedTime = DateTime.Now; - QueryReport.V2.Application.StorageOperation.Persistance.UpdateGameServer(gameServer); - } - } -} diff --git a/src/Servers/QueryReport/src/V2/Handler/CmdSwitcher.cs b/src/Servers/QueryReport/src/V2/Handler/CmdSwitcher.cs deleted file mode 100755 index 36c37345f..000000000 --- a/src/Servers/QueryReport/src/V2/Handler/CmdSwitcher.cs +++ /dev/null @@ -1,54 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.QueryReport.Application; -using UniSpy.Server.QueryReport.V2.Enumerate; -using UniSpy.Server.QueryReport.V2.Handler.CmdHandler; - -namespace UniSpy.Server.QueryReport.V2.Handler -{ - public sealed class CmdSwitcher : CmdSwitcherBase - { - private new byte[] _rawRequest => (byte[])base._rawRequest; - private new Client _client => (Client)base._client; - public CmdSwitcher(Client client, byte[] rawRequest) : base(client, rawRequest) - { - } - protected override void ProcessRawRequest() - { - // var rawRequest = base._rawRequest as byte[]; - if (_rawRequest.Length < 4) - { - throw new UniSpy.Exception("Invalid request"); - } - - // qr v2 protocol - var name = (RequestType)_rawRequest[0]; - var rawRequest = _rawRequest; - _requests.Add(new KeyValuePair(name, rawRequest)); - } - - protected override IHandler CreateCmdHandlers(object name, object rawRequest) - { - var req = (byte[])rawRequest; - // query report v2 - switch ((RequestType)name) - { - case RequestType.HeartBeat: - return new HeartBeatHandler(_client, new Contract.Request.HeartBeatRequest(req)); - case RequestType.Challenge: - return new ChallengeHandler(_client, new Contract.Request.ChallengeRequest(req)); - case RequestType.AvaliableCheck: - return new AvailableHandler(_client, new Contract.Request.AvaliableRequest(req)); - case RequestType.ClientMessageAck: - return new ClientMessageAckHandler(_client, new Contract.Request.ClientMessageAckRequest(req)); - case RequestType.Echo: - return new EchoHandler(_client, new Contract.Request.EchoRequest(req)); - case RequestType.KeepAlive: - return new KeepAliveHandler(_client, new Contract.Request.KeepAliveRequest(req)); - default: - return null; - } - } - } -} diff --git a/src/Servers/QueryReport/test/UniSpy.Server.QueryReport.Test.csproj b/src/Servers/QueryReport/test/UniSpy.Server.QueryReport.Test.csproj deleted file mode 100644 index 2d8b5e942..000000000 --- a/src/Servers/QueryReport/test/UniSpy.Server.QueryReport.Test.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - net6.0 - false - ..\..\..\..\common\Icon\UniSpy_Logo.ico - - - AnyCPU - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - \ No newline at end of file diff --git a/src/Servers/QueryReport/test/V2/GameTest.cs b/src/Servers/QueryReport/test/V2/GameTest.cs deleted file mode 100644 index 727ad7cc2..000000000 --- a/src/Servers/QueryReport/test/V2/GameTest.cs +++ /dev/null @@ -1,115 +0,0 @@ -using UniSpy.Server.Core.Abstraction.Interface; -using Xunit; - -namespace UniSpy.Server.QueryReport.V2.Test -{ - public class GameTest - { - IClient _client; - public GameTest() - { - _client = MockObject.Client; - } - [Fact] - public void FaltOut2() - { - var raw = new byte[]{0x03,0xc2,0x94,0x1c,0xe7,0x6c,0x6f,0x63,0x61,0x6c,0x69,0x70,0x30,0x00,0x31,0x39 - ,0x32,0x2e,0x31,0x36,0x38,0x2e,0x30,0x2e,0x35,0x30,0x00,0x6c,0x6f,0x63,0x61,0x6c - ,0x69,0x70,0x31,0x00,0x31,0x37,0x32,0x2e,0x31,0x36,0x2e,0x37,0x34,0x2e,0x31,0x00 - ,0x6c,0x6f,0x63,0x61,0x6c,0x69,0x70,0x32,0x00,0x31,0x37,0x32,0x2e,0x31,0x37,0x2e - ,0x30,0x2e,0x31,0x00,0x6c,0x6f,0x63,0x61,0x6c,0x69,0x70,0x33,0x00,0x31,0x39,0x32 - ,0x2e,0x31,0x36,0x38,0x2e,0x31,0x32,0x32,0x2e,0x31,0x00,0x6c,0x6f,0x63,0x61,0x6c - ,0x69,0x70,0x34,0x00,0x31,0x37,0x32,0x2e,0x31,0x36,0x2e,0x36,0x35,0x2e,0x31,0x00 - ,0x6c,0x6f,0x63,0x61,0x6c,0x70,0x6f,0x72,0x74,0x00,0x32,0x33,0x37,0x35,0x36,0x00 - ,0x6e,0x61,0x74,0x6e,0x65,0x67,0x00,0x31,0x00,0x73,0x74,0x61,0x74,0x65,0x63,0x68 - ,0x61,0x6e,0x67,0x65,0x64,0x00,0x31,0x00,0x67,0x61,0x6d,0x65,0x6e,0x61,0x6d,0x65 - ,0x00,0x66,0x6c,0x61,0x74,0x6f,0x75,0x74,0x32,0x70,0x63,0x00,0x70,0x75,0x62,0x6c - ,0x69,0x63,0x69,0x70,0x00,0x30,0x00,0x70,0x75,0x62,0x6c,0x69,0x63,0x70,0x6f,0x72 - ,0x74,0x00,0x30,0x00,0x68,0x6f,0x73,0x74,0x6b,0x65,0x79,0x00,0x2d,0x38,0x32,0x30 - ,0x39,0x36,0x36,0x33,0x32,0x32,0x00,0x68,0x6f,0x73,0x74,0x6e,0x61,0x6d,0x65,0x00 - ,0x53,0x70,0x6f,0x72,0x65,0x00,0x67,0x61,0x6d,0x65,0x76,0x65,0x72,0x00,0x46,0x4f - ,0x31,0x34,0x00,0x67,0x61,0x6d,0x65,0x74,0x79,0x70,0x65,0x00,0x72,0x61,0x63,0x65 - ,0x00,0x67,0x61,0x6d,0x65,0x76,0x61,0x72,0x69,0x61,0x6e,0x74,0x00,0x6e,0x6f,0x72 - ,0x6d,0x61,0x6c,0x5f,0x72,0x61,0x63,0x65,0x00,0x67,0x61,0x6d,0x65,0x6d,0x6f,0x64 - ,0x65,0x00,0x6f,0x70,0x65,0x6e,0x77,0x61,0x69,0x74,0x69,0x6e,0x67,0x00,0x6e,0x75 - ,0x6d,0x70,0x6c,0x61,0x79,0x65,0x72,0x73,0x00,0x31,0x00,0x6d,0x61,0x78,0x70,0x6c - ,0x61,0x79,0x65,0x72,0x73,0x00,0x38,0x00,0x6d,0x61,0x70,0x6e,0x61,0x6d,0x65,0x00 - ,0x54,0x69,0x6d,0x62,0x65,0x72,0x6c,0x61,0x6e,0x64,0x73,0x5f,0x31,0x00,0x74,0x69 - ,0x6d,0x65,0x6c,0x69,0x6d,0x69,0x74,0x00,0x30,0x00,0x70,0x61,0x73,0x73,0x77,0x6f - ,0x72,0x64,0x00,0x30,0x00,0x63,0x61,0x72,0x5f,0x74,0x79,0x70,0x65,0x00,0x30,0x00 - ,0x63,0x61,0x72,0x5f,0x63,0x6c,0x61,0x73,0x73,0x00,0x30,0x00,0x72,0x61,0x63,0x65 - ,0x73,0x5f,0x70,0x00,0x31,0x30,0x30,0x00,0x64,0x65,0x72,0x62,0x69,0x65,0x73,0x5f - ,0x70,0x00,0x30,0x00,0x73,0x74,0x75,0x6e,0x74,0x73,0x5f,0x70,0x00,0x30,0x00,0x6e - ,0x6f,0x72,0x6d,0x61,0x6c,0x5f,0x72,0x61,0x63,0x65,0x5f,0x70,0x00,0x31,0x30,0x30 - ,0x00,0x70,0x6f,0x6e,0x67,0x5f,0x72,0x61,0x63,0x65,0x5f,0x70,0x00,0x30,0x00,0x77 - ,0x72,0x65,0x63,0x6b,0x5f,0x64,0x65,0x72,0x62,0x79,0x5f,0x70,0x00,0x30,0x00,0x73 - ,0x75,0x72,0x76,0x69,0x76,0x6f,0x72,0x5f,0x64,0x65,0x72,0x62,0x79,0x5f,0x70,0x00 - ,0x30,0x00,0x66,0x72,0x61,0x67,0x5f,0x64,0x65,0x72,0x62,0x79,0x5f,0x70,0x00,0x30 - ,0x00,0x74,0x61,0x67,0x5f,0x70,0x00,0x30,0x00,0x75,0x70,0x67,0x72,0x61,0x64,0x65 - ,0x73,0x00,0x32,0x00,0x6e,0x69,0x74,0x72,0x6f,0x5f,0x72,0x65,0x67,0x65,0x6e,0x65 - ,0x72,0x61,0x74,0x69,0x6f,0x6e,0x00,0x32,0x00,0x64,0x61,0x6d,0x61,0x67,0x65,0x5f - ,0x6c,0x65,0x76,0x65,0x6c,0x00,0x32,0x00,0x64,0x65,0x72,0x62,0x79,0x5f,0x64,0x61 - ,0x6d,0x61,0x67,0x65,0x5f,0x6c,0x65,0x76,0x65,0x6c,0x00,0x31,0x00,0x6e,0x65,0x78 - ,0x74,0x5f,0x72,0x61,0x63,0x65,0x5f,0x74,0x79,0x70,0x65,0x00,0x6e,0x6f,0x72,0x6d - ,0x61,0x6c,0x5f,0x72,0x61,0x63,0x65,0x00,0x6c,0x61,0x70,0x73,0x5f,0x6f,0x72,0x5f - ,0x74,0x69,0x6d,0x65,0x6c,0x69,0x6d,0x69,0x74,0x00,0x34,0x00,0x6e,0x75,0x6d,0x5f - ,0x72,0x61,0x63,0x65,0x73,0x00,0x31,0x00,0x6e,0x75,0x6d,0x5f,0x64,0x65,0x72,0x62 - ,0x69,0x65,0x73,0x00,0x30,0x00,0x6e,0x75,0x6d,0x5f,0x73,0x74,0x75,0x6e,0x74,0x73 - ,0x00,0x30,0x00,0x64,0x61,0x74,0x61,0x63,0x68,0x65,0x63,0x6b,0x73,0x75,0x6d,0x00 - ,0x33,0x35,0x34,0x36,0x64,0x35,0x38,0x30,0x39,0x33,0x32,0x33,0x37,0x65,0x62,0x33 - ,0x33,0x62,0x32,0x61,0x39,0x36,0x62,0x62,0x38,0x31,0x33,0x33,0x37,0x30,0x64,0x38 - ,0x34,0x36,0x66,0x66,0x63,0x65,0x63,0x38,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00}; - ((ITestClient)_client).TestReceived(raw); - } - [Fact] - public void Worms3d() - { - var raw = new byte[] { - 0x03,0x51,0x5d,0xa0 - ,0xe8,0x6c,0x6f,0x63,0x61,0x6c,0x69,0x70,0x30,0x00,0x31,0x39,0x32,0x2e,0x31,0x36 - ,0x38,0x2e,0x30,0x2e,0x36,0x30,0x00,0x6c,0x6f,0x63,0x61,0x6c,0x70,0x6f,0x72,0x74 - ,0x00,0x36,0x35,0x30,0x30,0x00,0x6e,0x61,0x74,0x6e,0x65,0x67,0x00,0x31,0x00,0x73 - ,0x74,0x61,0x74,0x65,0x63,0x68,0x61,0x6e,0x67,0x65,0x64,0x00,0x33,0x00,0x67,0x61 - ,0x6d,0x65,0x6e,0x61,0x6d,0x65,0x00,0x77,0x6f,0x72,0x6d,0x73,0x33,0x00,0x68,0x6f - ,0x73,0x74,0x6e,0x61,0x6d,0x65,0x00,0x74,0x65,0x73,0x74,0x00,0x67,0x61,0x6d,0x65 - ,0x6d,0x6f,0x64,0x65,0x00,0x6f,0x70,0x65,0x6e,0x73,0x74,0x61,0x67,0x69,0x6e,0x67 - ,0x00,0x67,0x72,0x6f,0x75,0x70,0x69,0x64,0x00,0x36,0x32,0x32,0x00,0x6e,0x75,0x6d - ,0x70,0x6c,0x61,0x79,0x65,0x72,0x73,0x00,0x31,0x00,0x6d,0x61,0x78,0x70,0x6c,0x61 - ,0x79,0x65,0x72,0x73,0x00,0x32,0x00,0x68,0x6f,0x73,0x74,0x6e,0x61,0x6d,0x65,0x00 - ,0x74,0x65,0x73,0x74,0x00,0x68,0x6f,0x73,0x74,0x70,0x6f,0x72,0x74,0x00,0x00,0x6d - ,0x61,0x78,0x70,0x6c,0x61,0x79,0x65,0x72,0x73,0x00,0x32,0x00,0x6e,0x75,0x6d,0x70 - ,0x6c,0x61,0x79,0x65,0x72,0x73,0x00,0x31,0x00,0x53,0x63,0x68,0x65,0x6d,0x65,0x43 - ,0x68,0x61,0x6e,0x67,0x69,0x6e,0x67,0x00,0x30,0x00,0x67,0x61,0x6d,0x65,0x76,0x65 - ,0x72,0x00,0x31,0x30,0x37,0x33,0x00,0x67,0x61,0x6d,0x65,0x74,0x79,0x70,0x65,0x00 - ,0x00,0x6d,0x61,0x70,0x6e,0x61,0x6d,0x65,0x00,0x00,0x66,0x69,0x72,0x65,0x77,0x61 - ,0x6c,0x6c,0x00,0x30,0x00,0x70,0x75,0x62,0x6c,0x69,0x63,0x69,0x70,0x00,0x32,0x35 - ,0x35,0x2e,0x32,0x35,0x35,0x2e,0x32,0x35,0x35,0x2e,0x32,0x35,0x35,0x00,0x70,0x72 - ,0x69,0x76,0x61,0x74,0x65,0x69,0x70,0x00,0x31,0x39,0x32,0x2e,0x31,0x36,0x38,0x2e - ,0x30,0x2e,0x36,0x30,0x00,0x67,0x61,0x6d,0x65,0x6d,0x6f,0x64,0x65,0x00,0x6f,0x70 - ,0x65,0x6e,0x73,0x74,0x61,0x67,0x69,0x6e,0x67,0x00,0x76,0x61,0x6c,0x00,0x30,0x00 - ,0x70,0x61,0x73,0x73,0x77,0x6f,0x72,0x64,0x00,0x30,0x00,0x00,0x00,0x01,0x70,0x6c - ,0x61,0x79,0x65,0x72,0x5f,0x00,0x70,0x69,0x6e,0x67,0x5f,0x00,0x68,0x6f,0x73,0x74 - ,0x6e,0x61,0x6d,0x65,0x00,0x68,0x6f,0x73,0x74,0x70,0x6f,0x72,0x74,0x00,0x6d,0x61 - ,0x78,0x70,0x6c,0x61,0x79,0x65,0x72,0x73,0x00,0x6e,0x75,0x6d,0x70,0x6c,0x61,0x79 - ,0x65,0x72,0x73,0x00,0x53,0x63,0x68,0x65,0x6d,0x65,0x43,0x68,0x61,0x6e,0x67,0x69 - ,0x6e,0x67,0x00,0x67,0x61,0x6d,0x65,0x76,0x65,0x72,0x00,0x67,0x61,0x6d,0x65,0x74 - ,0x79,0x70,0x65,0x00,0x6d,0x61,0x70,0x6e,0x61,0x6d,0x65,0x00,0x66,0x69,0x72,0x65 - ,0x77,0x61,0x6c,0x6c,0x00,0x70,0x75,0x62,0x6c,0x69,0x63,0x69,0x70,0x00,0x70,0x72 - ,0x69,0x76,0x61,0x74,0x65,0x69,0x70,0x00,0x67,0x61,0x6d,0x65,0x6d,0x6f,0x64,0x65 - ,0x00,0x76,0x61,0x6c,0x00,0x70,0x61,0x73,0x73,0x77,0x6f,0x72,0x64,0x00,0x00,0x77 - ,0x6f,0x72,0x6d,0x73,0x31,0x30,0x00,0x30,0x00,0x00,0x00,0x00,0x00,0x00,0x31,0x30 - ,0x37,0x33,0x00,0x00,0x00,0x31,0x00,0x32,0x35,0x35,0x2e,0x32,0x35,0x35,0x2e,0x32 - ,0x35,0x35,0x2e,0x32,0x35,0x35,0x00,0x31,0x39,0x32,0x2e,0x31,0x36,0x38,0x2e,0x30 - ,0x2e,0x36,0x30,0x00,0x00,0x30,0x00,0x00,0x00,0x00,0x68,0x6f,0x73,0x74,0x6e,0x61 - ,0x6d,0x65,0x00,0x68,0x6f,0x73,0x74,0x70,0x6f,0x72,0x74,0x00,0x6d,0x61,0x78,0x70 - ,0x6c,0x61,0x79,0x65,0x72,0x73,0x00,0x6e,0x75,0x6d,0x70,0x6c,0x61,0x79,0x65,0x72 - ,0x73,0x00,0x53,0x63,0x68,0x65,0x6d,0x65,0x43,0x68,0x61,0x6e,0x67,0x69,0x6e,0x67 - ,0x00,0x67,0x61,0x6d,0x65,0x76,0x65,0x72,0x00,0x67,0x61,0x6d,0x65,0x74,0x79,0x70 - ,0x65,0x00,0x6d,0x61,0x70,0x6e,0x61,0x6d,0x65,0x00,0x66,0x69,0x72,0x65,0x77,0x61 - ,0x6c,0x6c,0x00,0x70,0x75,0x62,0x6c,0x69,0x63,0x69,0x70,0x00,0x70,0x72,0x69,0x76 - ,0x61,0x74,0x65,0x69,0x70,0x00,0x67,0x61,0x6d,0x65,0x6d,0x6f,0x64,0x65,0x00,0x76 - ,0x61,0x6c,0x00,0x70,0x61,0x73,0x73,0x77,0x6f,0x72,0x64,0x00,0x00 }; - ((ITestClient)_client).TestReceived(raw); - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/test/V2/MockObject.cs b/src/Servers/QueryReport/test/V2/MockObject.cs deleted file mode 100644 index 942c3608f..000000000 --- a/src/Servers/QueryReport/test/V2/MockObject.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Net; -using Moq; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.QueryReport.Application; - -namespace UniSpy.Server.QueryReport.V2.Test -{ - public class MockObject - { - public static IClient Client = CreateClient(); - - public static Client CreateClient(string ipAddress = "192.168.1.1", int port = 9999) - { - var managerMock = new Mock(); - var connectionMock = new Mock(); - connectionMock.Setup(s => s.RemoteIPEndPoint).Returns(new IPEndPoint(IPAddress.Parse(ipAddress), port)); - connectionMock.Setup(s => s.Manager).Returns(managerMock.Object); - connectionMock.Setup(s => s.ConnectionType).Returns(NetworkConnectionType.Udp); - var serverMock = new QueryReport.Application.Server(managerMock.Object); - - return new Client(connectionMock.Object, serverMock); - } - } -} \ No newline at end of file diff --git a/src/Servers/QueryReport/test/V2/RequestTest.cs b/src/Servers/QueryReport/test/V2/RequestTest.cs deleted file mode 100644 index e0a31d556..000000000 --- a/src/Servers/QueryReport/test/V2/RequestTest.cs +++ /dev/null @@ -1,77 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Enumerate; -using UniSpy.Server.QueryReport.V2.Contract.Request; -using Xunit; -namespace UniSpy.Server.QueryReport.V2.Test -{ - public class RequestTest - { - [Fact] - public void AvaliableTest() - { - var rawRequest = new byte[]{ - 0x09,//packet type - 0x00,0x00,0x00,0x00,//instant key - 0x09, 0x00, 0x00, 0x00, 0x00,//prefix - 0x67, 0x61, 0x6D, 0x65 ,0x73, 0x70, 0x79,//gamename - 0x00 - }; - var request = new AvaliableRequest(rawRequest); - request.Parse(); - Assert.Equal(RequestType.AvaliableCheck, request.CommandName); - Assert.Equal((uint)0, request.InstantKey); - } - [Fact] - public void ChallengeTest() - { - var rawRequest = new byte[]{ - 0x01,//packet type - 0x00,0x00,0x00,0x00,//instant key - 0x67, 0x61, 0x6D, 0x65 ,0x73, 0x70, 0x79,//gamename - 0x00 - }; - var request = new ChallengeRequest(rawRequest); - request.Parse(); - Assert.Equal(RequestType.Challenge, request.CommandName); - Assert.Equal((uint)0, request.InstantKey); - } - [Fact] - public void EchoRequest() - { - var rawRequest = new byte[]{ - 0x02,//packet type - 0x00,0x00,0x00,0x00,//instant key - 0x67, 0x61, 0x6D, 0x65 ,0x73, 0x70, 0x79,//gamename - 0x00 - }; - var request = new EchoRequest(rawRequest); - request.Parse(); - Assert.Equal(RequestType.Echo, request.CommandName); - Assert.Equal((uint)0, request.InstantKey); - } - [Fact] - public void HeartBeatTest() - { - var rawRequest = new byte[] { 0x03, 0xae, 0x1f, 0x77, 0x64, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x70, 0x30, 0x00, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x39, 0x00, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x31, 0x31, 0x31, 0x31, 0x31, 0x00, 0x6e, 0x61, 0x74, 0x6e, 0x65, 0x67, 0x00, 0x31, 0x00, 0x73, 0x74, 0x61, 0x74, 0x65, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x00, 0x33, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x67, 0x6d, 0x74, 0x65, 0x73, 0x74, 0x00, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x70, 0x79, 0x20, 0x51, 0x52, 0x32, 0x20, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x76, 0x65, 0x72, 0x00, 0x32, 0x2e, 0x30, 0x30, 0x00, 0x68, 0x6f, 0x73, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x32, 0x35, 0x30, 0x30, 0x30, 0x00, 0x6d, 0x61, 0x70, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x67, 0x6d, 0x74, 0x6d, 0x61, 0x70, 0x31, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x00, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x00, 0x6e, 0x75, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00, 0x36, 0x00, 0x6e, 0x75, 0x6d, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x00, 0x32, 0x00, 0x6d, 0x61, 0x78, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00, 0x33, 0x32, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x00, 0x6f, 0x70, 0x65, 0x6e, 0x70, 0x6c, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x00, 0x74, 0x65, 0x61, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x31, 0x00, 0x66, 0x72, 0x61, 0x67, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00, 0x30, 0x00, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00, 0x34, 0x30, 0x00, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x79, 0x00, 0x38, 0x30, 0x30, 0x00, 0x72, 0x61, 0x6e, 0x6b, 0x69, 0x6e, 0x67, 0x6f, 0x6e, 0x00, 0x31, 0x00, 0x00, 0x00, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x00, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x00, 0x64, 0x65, 0x61, 0x74, 0x68, 0x73, 0x5f, 0x00, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x00, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x00, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x00, 0x00, 0x4a, 0x6f, 0x65, 0x20, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x00, 0x32, 0x37, 0x00, 0x36, 0x00, 0x31, 0x32, 0x33, 0x00, 0x31, 0x00, 0x34, 0x30, 0x39, 0x00, 0x4c, 0x33, 0x33, 0x74, 0x20, 0x30, 0x6e, 0x33, 0x00, 0x36, 0x00, 0x32, 0x33, 0x00, 0x32, 0x37, 0x37, 0x00, 0x30, 0x00, 0x36, 0x37, 0x33, 0x00, 0x52, 0x61, 0x70, 0x74, 0x6f, 0x72, 0x00, 0x33, 0x30, 0x00, 0x31, 0x00, 0x31, 0x34, 0x36, 0x00, 0x31, 0x00, 0x37, 0x30, 0x31, 0x00, 0x47, 0x72, 0x38, 0x31, 0x00, 0x32, 0x31, 0x00, 0x31, 0x36, 0x00, 0x31, 0x32, 0x35, 0x00, 0x31, 0x00, 0x35, 0x38, 0x32, 0x00, 0x46, 0x6c, 0x75, 0x62, 0x62, 0x65, 0x72, 0x00, 0x33, 0x00, 0x32, 0x31, 0x00, 0x31, 0x31, 0x30, 0x00, 0x30, 0x00, 0x32, 0x39, 0x38, 0x00, 0x53, 0x61, 0x72, 0x67, 0x65, 0x00, 0x33, 0x00, 0x32, 0x38, 0x00, 0x31, 0x32, 0x35, 0x00, 0x31, 0x00, 0x35, 0x39, 0x30, 0x00, 0x00, 0x02, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x74, 0x00, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x00, 0x61, 0x76, 0x67, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x00, 0x00, 0x52, 0x65, 0x64, 0x00, 0x32, 0x39, 0x34, 0x00, 0x33, 0x37, 0x39, 0x00, 0x42, 0x6c, 0x75, 0x65, 0x00, 0x38, 0x39, 0x00, 0x33, 0x38, 0x33, 0x00 }; - var request = new HeartBeatRequest(rawRequest); - request.Parse(); - Assert.Equal("gmtest", request.GameName); - Assert.Equal((uint)1685528494, request.InstantKey); - Assert.Equal(6, request.PlayerData.Count); - Assert.Equal(19, request.ServerData.Count); - Assert.Equal(2, request.TeamData.Count); - // Assert.Equal(HeartBeatReportType.ServerTeamPlayerData, request.ReportType); - } - [Fact] - public void KeepAliveTest() - { - var rawRequest = new byte[]{ - 0x08,//packet type - 0x00,0x00,0x00,0x00,//instant key - }; - var request = new EchoRequest(rawRequest); - request.Parse(); - Assert.Equal(RequestType.KeepAlive, request.CommandName); - Assert.Equal((uint)0, request.InstantKey); - } - } -} diff --git a/src/Servers/ServerBrowser/src/Aggregate/GameServerFilter.cs b/src/Servers/ServerBrowser/src/Aggregate/GameServerFilter.cs deleted file mode 100644 index 433eab087..000000000 --- a/src/Servers/ServerBrowser/src/Aggregate/GameServerFilter.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace UniSpy.Server.ServerBrowser.Aggregate -{ - public class GameServerFilter - { - // public static Process(List<>) - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/Application/Program.cs b/src/Servers/ServerBrowser/src/Application/Program.cs deleted file mode 100755 index 13cbc22c4..000000000 --- a/src/Servers/ServerBrowser/src/Application/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.ServerBrowser.Application -{ - public sealed class Program - { - static void Main(string[] args) - { - try - { - new ServerLauncher().Start(); - Console.WriteLine("Press < Q > to exit. "); - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - catch (System.Exception e) - { - UniSpy.Exception.HandleException(e); - } - finally - { - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - } - } -} diff --git a/src/Servers/ServerBrowser/src/Application/ServerLauncher.cs b/src/Servers/ServerBrowser/src/Application/ServerLauncher.cs deleted file mode 100644 index 48913b483..000000000 --- a/src/Servers/ServerBrowser/src/Application/ServerLauncher.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.BaseClass.Factory; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.ServerBrowser.Application -{ - public sealed class ServerLauncher : ServerLauncherBase - { - public static IServer ServerV1 => ServerInstances[0]; - public static IServer ServerV2 => ServerInstances[1]; - protected override List LaunchNetworkService() => - new List - { - new ServerBrowser.V1.Application.Server(), - new ServerBrowser.V2.Application.Server() - }; - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/Dockerfile b/src/Servers/ServerBrowser/src/Dockerfile deleted file mode 100755 index 5bd89b150..000000000 --- a/src/Servers/ServerBrowser/src/Dockerfile +++ /dev/null @@ -1,23 +0,0 @@ -FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base -WORKDIR /app -EXPOSE 28910 -EXPOSE 28900 - -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build -WORKDIR /src -COPY ["src/Servers/ServerBrowser/src/UniSpy.Server.ServerBrowser.csproj", "src/Servers/ServerBrowser/src/"] -COPY ["src/Servers/NatNegotiation/src/UniSpy.Server.NatNegotiation.csproj", "src/Servers/NatNegotiation/src/"] -COPY ["src/Libraries/Core/src/UniSpy.Server.Core.csproj", "src/Libraries/Core/src/"] -COPY ["src/Servers/QueryReport/src/UniSpy.Server.QueryReport.csproj", "src/Servers/QueryReport/src/"] -RUN dotnet restore "src/Servers/ServerBrowser/src/UniSpy.Server.ServerBrowser.csproj" -COPY . . -WORKDIR "/src/src/Servers/ServerBrowser/src" -RUN dotnet build "UniSpy.Server.ServerBrowser.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "UniSpy.Server.ServerBrowser.csproj" -c Release -o /app/publish - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "UniSpy.Server.ServerBrowser.dll"] \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/Exception/Exception.cs b/src/Servers/ServerBrowser/src/Exception/Exception.cs deleted file mode 100755 index 464e5ec8a..000000000 --- a/src/Servers/ServerBrowser/src/Exception/Exception.cs +++ /dev/null @@ -1,26 +0,0 @@ -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.ServerBrowser -{ - public class Exception : UniSpy.Exception, IResponse - { - public Exception() - { - } - - public Exception(string message) : base(message) - { - } - - public Exception(string message, System.Exception innerException) : base(message, innerException) - { - } - - public object SendingBuffer { get; private set; } - - public void Build() - { - SendingBuffer = @$"\error\{Message}\final\"; - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/UniSpy.Server.ServerBrowser.csproj b/src/Servers/ServerBrowser/src/UniSpy.Server.ServerBrowser.csproj deleted file mode 100755 index e6dfd4a58..000000000 --- a/src/Servers/ServerBrowser/src/UniSpy.Server.ServerBrowser.csproj +++ /dev/null @@ -1,24 +0,0 @@ - - - - Exe - net6.0 - ..\..\..\..\common\Icon\UniSpy_Logo.ico - Linux - ..\..\..\.. - - - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - - - - - \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/CmdHandlerBase.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/CmdHandlerBase.cs deleted file mode 100644 index fc5a8bffc..000000000 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/CmdHandlerBase.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.ServerBrowser.V1.Application; - -namespace UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass -{ - public abstract class CmdHandlerBase : UniSpy.Server.Core.Abstraction.BaseClass.CmdHandlerBase - { - protected new Client _client => (Client)base._client; - public CmdHandlerBase(Client client, RequestBase request) : base(client, request) - { - } - protected override void HandleException(System.Exception ex) - { - base.HandleException(ex); - if (ex is ServerBrowser.Exception) - { - _client.Send((ServerBrowser.Exception)ex); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs deleted file mode 100644 index d8cecc844..000000000 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/EnctypeBase.cs +++ /dev/null @@ -1,232 +0,0 @@ -using System; -using System.Linq; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass -{ - /// - /// The encryption type 1 and 2 base class which contains the functions using in enctype1 and enctype2. - /// What confuses me the most is why gamespy doesn't use standard encryption schemes like DES, AES etc. - /// - public abstract class EnctypeBase : ICryptography, IEnctypeShareTest - { - public byte[] Decrypt(byte[] data) => data; - public abstract byte[] Encrypt(byte[] data); - /// - /// The encryption function 2 - /// - /// the input tbuff - /// tbuff start index - /// datap is a subset of tbuff - /// datap start index - /// unknown - protected void ChangeSboxEncryptPlaintext(uint[] sbox, byte[] data, int startIndex, int len) - { - int pIndex, sIndex; - // convert uint array to byte array - pIndex = sIndex = 309 * sizeof(uint); - SboxElementExchange(sbox, 309, 16); - var sboxBytes = ConvertUintToBytes(sbox); - - while (len-- > 0) - { - if ((pIndex - sIndex) == 63) - { - pIndex = sIndex; - //convert int array to byte array - SboxElementExchange(sbox, 0, 16); - sboxBytes = ConvertUintToBytes(sbox); - } - data[startIndex] ^= sboxBytes[pIndex]; - startIndex++; - pIndex++; - } - sbox = ConvertBytesToUint(sboxBytes); - } - /// - /// Encryption related function2 - /// - /// the input data - /// the another index of input data - /// unknown - protected void SboxElementExchange(uint[] sbox, uint startIndex, int len) - { - uint t1, t2, t3, t4, t5; - uint limit; - uint p; - - t2 = sbox[304]; - t1 = sbox[305]; - t3 = sbox[306]; - t5 = sbox[307]; - limit = (uint)(startIndex + len); - while (startIndex < limit) - { - p = t2 + 272; - while (t5 <= ushort.MaxValue) - { - t1 += t5; - p++; - t3 += t1; - t1 += t3; - sbox[p - 17] = t1; - sbox[p - 1] = t3; - t4 = (t3 << 24) | (t3 >> 8); - sbox[p + 15] = t5; - t5 <<= 1; - t2++; - t1 ^= sbox[t1 & 0xff]; - t4 ^= sbox[t4 & 0xff]; - t3 = (t4 << 24) | (t4 >> 8); - t4 = (t1 >> 24) | (t1 << 8); - t4 ^= sbox[t4 & 0xff]; - t3 ^= sbox[t3 & 0xff]; - t1 = (t4 >> 24) | (t4 << 8); - } - t3 ^= t1; - sbox[startIndex++] = t3; - t2--; - t1 = sbox[t2 + 256]; - t5 = sbox[t2 + 272]; - t1 = ~t1; - t3 = (t1 << 24) | (t1 >> 8); - t3 ^= sbox[t3 & 0xff]; - t5 ^= sbox[t5 & 0xff]; - t1 = (t3 << 24) | (t3 >> 8); - t4 = (t5 >> 24) | (t5 << 8); - t1 ^= sbox[t1 & 0xff]; - t4 ^= sbox[t4 & 0xff]; - t3 = (t4 >> 24) | (t4 << 8); - t5 = (sbox[t2 + 288] << 1) + 1; - } - - sbox[304] = t2; - sbox[305] = t1; - sbox[306] = t3; - sbox[307] = t5; - } - protected void EncShare3(uint[] data, int n1 = 0, int n2 = 0) - { - uint t1, t2, t3, t4; - int i; - - t2 = (uint)n1; - t1 = 0; - t4 = 1; - data[304] = 0; - - for (i = 32768; i != 0; i >>= 1) - { - t2 += t4; - t1 += t2; - t2 += t1; - - if ((n2 & i) != 0) - { - t2 = ~t2; - t4 = (t4 << 1) + 1; - t3 = (t2 << 24) | (t2 >> 8); - t3 ^= data[t3 & 0xFF]; - t1 ^= data[t1 & 0xFF]; - t2 = (t3 << 24) | (t3 >> 8); - t3 = (t1 >> 24) | (t1 << 8); - t2 ^= data[t2 & 0xFF]; - t3 ^= data[t3 & 0xFF]; - t1 = (t3 >> 24) | (t3 << 8); - } - else - { - data[data[304] + 256] = t2; - data[data[304] + 272] = t1; - data[data[304] + 288] = t4; - data[304]++; - t3 = (t1 << 24) | (t1 >> 8); - t2 ^= data[t2 & 0xFF]; - t3 ^= data[t3 & 0xFF]; - t1 = (t3 << 24) | (t3 >> 8); - t3 = (t2 >> 24) | (t2 << 8); - t3 ^= data[t3 & 0xFF]; - t1 ^= data[t1 & 0xFF]; - t2 = (t3 >> 24) | (t3 << 8); - t4 <<= 1; - } - } - - data[305] = t2; - data[306] = t1; - data[307] = t4; - data[308] = (uint)n1; - } - /// - /// Seems this function is used to initialize encryption parameter - /// - /// the sbox seed - /// seed size - /// sbox - protected void ConstructSbox(byte[] input, int size, uint[] output) - { - uint tmp; - int i; - byte pos, x, y; - - for (i = 0; i < 256; i++) - output[i] = 0; - - for (y = 0; y < 4; y++) - { - for (i = 0; i < 256; i++) - { - output[i] = (output[i] << 8) + (uint)i; - } - - for (pos = y, x = 0; x < 2; x++) - { - for (i = 0; i < 256; i++) - { - tmp = output[i]; - pos += (byte)(tmp + input[i % size]); - output[i] = output[pos]; - output[pos] = tmp; - } - } - } - - for (i = 0; i < 256; i++) - output[i] ^= (uint)i; - - EncShare3(output); - } - public static byte[] ConvertUintToBytes(uint[] input) - { - var ontputBytes = new byte[input.Length * sizeof(uint)]; - for (var i = 0; i < input.Length; i++) - { - Array.Copy(BitConverter.GetBytes(input[i]), 0, ontputBytes, i * sizeof(uint), sizeof(uint)); - } - return ontputBytes.ToArray(); - } - public static uint[] ConvertBytesToUint(byte[] input) - { - if (input.Length % sizeof(uint) != 0) - { - throw new System.Exception("the input length is not correct"); - } - var onputInts = new uint[input.Length / sizeof(uint)]; - for (var i = 0; i < (input.Length / sizeof(uint)); i++) - { - var tempBytes = input.Skip(i * sizeof(uint)).Take(sizeof(uint)).ToArray(); - var tempUint = BitConverter.ToUInt32(tempBytes); - onputInts[i] = tempUint; - } - return onputInts; - } - - void IEnctypeShareTest.Encshare1(uint[] tbuff, byte[] datap, int datapIndex, int len) => ChangeSboxEncryptPlaintext(tbuff, datap, datapIndex, len); - - void IEnctypeShareTest.Encshare2(uint[] tbuff, uint tbuffp, int len) => SboxElementExchange(tbuff, tbuffp, len); - - void IEnctypeShareTest.EncShare3(uint[] data, int n1, int n2) => EncShare3(data, n1, n2); - - void IEnctypeShareTest.EncShare4(byte[] src, int size, uint[] dest) => ConstructSbox(src, size, dest); - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/RequestBase.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/RequestBase.cs deleted file mode 100644 index d0fcd84c5..000000000 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/RequestBase.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using UniSpy.Server.Core.Misc; - -namespace UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass -{ - public abstract class RequestBase : UniSpy.Server.Core.Abstraction.BaseClass.RequestBase - { - public new string RawRequest { get => (string)base.RawRequest; protected set => base.RawRequest = value; } - public new string CommandName { get => (string)base.CommandName; protected set => base.CommandName = value; } - public Dictionary KeyValues { get; private set; } - public string GameName { get; private set; } - public RequestBase(string rawRequest) : base(rawRequest) - { - } - public override void Parse() - { - KeyValues = GameSpyUtils.ConvertToKeyValue(RawRequest); - CommandName = KeyValues.Keys.First(); - if (!KeyValues.ContainsKey("gamename")) - { - throw new ServerBrowser.Exception("No game name present in request."); - } - GameName = KeyValues["gamename"]; - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/ResponseBase.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/ResponseBase.cs deleted file mode 100644 index 5a5cf3b37..000000000 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/ResponseBase.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass -{ - public abstract class ResponseBase : UniSpy.Server.Core.Abstraction.BaseClass.ResponseBase - { - public new string SendingBuffer { get => (string)base.SendingBuffer; protected set => base.SendingBuffer = value; } - public ResponseBase(RequestBase request, ResultBase result) : base(request, result) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/ResultBase.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/ResultBase.cs deleted file mode 100644 index 1b551651c..000000000 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/BaseClass/ResultBase.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass -{ - public abstract class ResultBase : UniSpy.Server.Core.Abstraction.BaseClass.ResultBase - { - public ResultBase() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs deleted file mode 100644 index d1e6d9ed7..000000000 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IEnctypeTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -public interface IEnctype1Test : IEnctypeShareTest -{ - void EncryptByEnc0Key(byte[] data, int size, byte[] crypt); - void CreateEnc0Key(byte[] data, int len, byte[] buff); - void CreateEnc1Key(byte[] validateKey, byte[] enc1Key); - int Func5(int cnt, byte[] id, ref int n1, ref int n2, byte[] encKey); - void EncryptByEnc1Key(byte[] data, int len, byte[] enc1Key); - int SubstituteEnc1key(int len, byte[] enc1Key); - void Func8(byte[] data, int len, byte[] enctype1_data); - byte[] Enc0Key { get; } - byte[] Enc1Key { get; } - byte[] ValidateKey { get; } -} - -public interface IEnctypeShareTest -{ - void Encshare1(uint[] tbuff, byte[] datap, int datapIndex, int len); - void Encshare2(uint[] tbuff, uint tbuffp, int len); - void EncShare3(uint[] data, int n1 = 0, int n2 = 0); - void EncShare4(byte[] src, int size, uint[] dest); -} - -public interface IEnctype2Test : IEnctypeShareTest -{ - // void Encoder(uint[] data); - -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IStorageOperation.cs b/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IStorageOperation.cs deleted file mode 100644 index c1e2b2b03..000000000 --- a/src/Servers/ServerBrowser/src/V1/Abstraction/Interface/IStorageOperation.cs +++ /dev/null @@ -1,13 +0,0 @@ -// using System.Net; -// using UniSpy.Server.ServerBrowser.V1.Aggregation.Redis; - -// namespace UniSpy.Server.ServerBrowser.V1.Abstraction.Interface -// { -// public interface IStorageOperation -// { -// string GetGameSecretKey(string gameName); -// GameServerInfo GetServerInfo(IPEndPoint endPoint); -// void UpdateServerInfo(GameServerInfo info); -// void RemoveServerInfo(IPEndPoint endPoint); -// } -// } \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs deleted file mode 100644 index 4fc691c8e..000000000 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype1.cs +++ /dev/null @@ -1,321 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; - -namespace UniSpy.Server.ServerBrowser.V1.Aggregate -{ - public class Enctype1 : EnctypeBase, IEnctype1Test - { - /// - /// The server key - /// 256 bytes - /// - public static readonly byte[] MasterKey = new byte[256] { 0x01, 0xba, 0xfa, 0xb2, 0x51, 0x00, 0x54, 0x80, 0x75, 0x16, 0x8e, 0x8e, 0x02, 0x08, 0x36, 0xa5, 0x2d, 0x05, 0x0d, 0x16, 0x52, 0x07, 0xb4, 0x22, 0x8c, 0xe9, 0x09, 0xd6, 0xb9, 0x26, 0x00, 0x04, 0x06, 0x05, 0x00, 0x13, 0x18, 0xc4, 0x1e, 0x5b, 0x1d, 0x76, 0x74, 0xfc, 0x50, 0x51, 0x06, 0x16, 0x00, 0x51, 0x28, 0x00, 0x04, 0x0a, 0x29, 0x78, 0x51, 0x00, 0x01, 0x11, 0x52, 0x16, 0x06, 0x4a, 0x20, 0x84, 0x01, 0xa2, 0x1e, 0x16, 0x47, 0x16, 0x32, 0x51, 0x9a, 0xc4, 0x03, 0x2a, 0x73, 0xe1, 0x2d, 0x4f, 0x18, 0x4b, 0x93, 0x4c, 0x0f, 0x39, 0x0a, 0x00, 0x04, 0xc0, 0x12, 0x0c, 0x9a, 0x5e, 0x02, 0xb3, 0x18, 0xb8, 0x07, 0x0c, 0xcd, 0x21, 0x05, 0xc0, 0xa9, 0x41, 0x43, 0x04, 0x3c, 0x52, 0x75, 0xec, 0x98, 0x80, 0x1d, 0x08, 0x02, 0x1d, 0x58, 0x84, 0x01, 0x4e, 0x3b, 0x6a, 0x53, 0x7a, 0x55, 0x56, 0x57, 0x1e, 0x7f, 0xec, 0xb8, 0xad, 0x00, 0x70, 0x1f, 0x82, 0xd8, 0xfc, 0x97, 0x8b, 0xf0, 0x83, 0xfe, 0x0e, 0x76, 0x03, 0xbe, 0x39, 0x29, 0x77, 0x30, 0xe0, 0x2b, 0xff, 0xb7, 0x9e, 0x01, 0x04, 0xf8, 0x01, 0x0e, 0xe8, 0x53, 0xff, 0x94, 0x0c, 0xb2, 0x45, 0x9e, 0x0a, 0xc7, 0x06, 0x18, 0x01, 0x64, 0xb0, 0x03, 0x98, 0x01, 0xeb, 0x02, 0xb0, 0x01, 0xb4, 0x12, 0x49, 0x07, 0x1f, 0x5f, 0x5e, 0x5d, 0xa0, 0x4f, 0x5b, 0xa0, 0x5a, 0x59, 0x58, 0xcf, 0x52, 0x54, 0xd0, 0xb8, 0x34, 0x02, 0xfc, 0x0e, 0x42, 0x29, 0xb8, 0xda, 0x00, 0xba, 0xb1, 0xf0, 0x12, 0xfd, 0x23, 0xae, 0xb6, 0x45, 0xa9, 0xbb, 0x06, 0xb8, 0x88, 0x14, 0x24, 0xa9, 0x00, 0x14, 0xcb, 0x24, 0x12, 0xae, 0xcc, 0x57, 0x56, 0xee, 0xfd, 0x08, 0x30, 0xd9, 0xfd, 0x8b, 0x3e, 0x0a, 0x84, 0x46, 0xfa, 0x77, 0xb8 }; - /// - /// Client key - /// 16 bytes - /// - public static readonly byte[] ClientKey = new byte[16] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; - public byte[] ValidateKey { get; private set; } - private byte[] _sboxInitSeed = BitConverter.GetBytes(0); - private uint[] _sbox = new uint[326]; - private byte[] _enc0Key = new byte[258]; - private byte[] _enc1Key = new byte[261]; - /// - /// The enc0seed is using to generate enc0key - /// - private byte[] _enc0Seed = Enumerable.Repeat(0x01, 16).ToArray(); - /// - /// _enc0seedIndex is the index of enc0seed, so we find the index in master key, - /// we only use the 0 index value in master key so we hard code the index - /// - private byte[] _enc0seedIndex = Enumerable.Repeat(0x00, 16).ToArray(); - public Enctype1(string validateKey) - { - ValidateKey = Encoding.ASCII.GetBytes(validateKey); - } - - public override byte[] Encrypt(byte[] data) - { - // create a copy - var plaintext = data.ToArray(); - - if (plaintext.Length <= 1) - { - throw new ServerBrowser.Exception("The data can not be empty"); - } - int tempLen = (plaintext.Length >> 1) - 17; - if (tempLen >= 0) - { - ConstructSbox(_sboxInitSeed, _sboxInitSeed.Length, _sbox); - ChangeSboxEncryptPlaintext(_sbox, plaintext, 0, tempLen); - } - CreateEnc0Key(_enc0Seed, _enc0Key); - EncryptByEnc0Key(plaintext, plaintext.Length, _enc0Key); - tempLen = (plaintext.Length >> 2) - 5; - if (tempLen >= 0) - { - CreateEnc1Key(ValidateKey, _enc1Key); - EncryptByEnc1Key(plaintext, tempLen, _enc1Key); - } - var ciphertext = BuildOutput(ValidateKey, _enc0seedIndex, plaintext, _sboxInitSeed); - return ciphertext; - } - - private byte[] BuildOutput(byte[] validateKey, byte[] scrambleData, byte[] encryptedData, byte[] sboxInitSeed) - { - var output = new List(); - output.Add(42); - output.Add(218); - // 6 to 19 is unknown data, we do not use it - output.AddRange(Enumerable.Repeat(0, 13).ToArray()); - output.AddRange(scrambleData); - output.Add(0); - // sboxInitSeed - output.AddRange(sboxInitSeed); - // unused data - output.AddRange(Enumerable.Repeat(0x00, 18).ToArray()); - - output.AddRange(encryptedData); - // insert total message length to index 0 - // 4 bytes message length - output.InsertRange(0, BitConverter.GetBytes(output.Count + 4).Reverse()); - return output.ToArray(); - } - - void EncryptByEnc0Key(byte[] data, int size, byte[] enc0Key) - { - byte n1, n2, t; - n1 = enc0Key[256]; - n2 = enc0Key[257]; - int dataIndex = 0; - while (size-- > 0) - { - t = enc0Key[++n1]; - n2 += t; - enc0Key[n1] = enc0Key[n2]; - enc0Key[n2] = t; - - t += enc0Key[n1]; - data[dataIndex] ^= enc0Key[t]; - dataIndex++; - } - enc0Key[256] = n1; - enc0Key[257] = n2; - } - void CreateEnc0Key(byte[] scrambleData, byte[] enc0Key) - { - int i; - byte pos = 0, tmp, rev = 0xff; - for (i = 0; i <= byte.MaxValue; i++) - { - enc0Key[i] = rev--; - } - enc0Key[256] = 0; - enc0Key[257] = 0; - - for (i = 0; i <= byte.MaxValue; i++) - { - tmp = enc0Key[i]; - pos += (byte)(scrambleData[i % scrambleData.Length] + tmp); - enc0Key[i] = enc0Key[pos]; - enc0Key[pos] = tmp; - } - } - void CreateEnc1Key(byte[] validateKey, byte[] enc1Key) - { - // Declare variables i, n1, n2, t1, t2 - int i, - n1 = 0, - n2 = 0; - byte t1, - t2; - - // If idlen is less than 1, return directly - if (validateKey.Length < 1) - return; - - // Initialize encryption array _enc1key - for (i = 0; i < 256; i++) - enc1Key[i] = (byte)i; - - // Shuffle the encryption array _enc1key - for (i = 255; i >= 0; i--) - { - t1 = (byte)Func5(i, validateKey, ref n1, ref n2, enc1Key); - t2 = enc1Key[i]; - enc1Key[i] = enc1Key[t1]; - enc1Key[t1] = t2; - } - - // Set specific values to some elements of the encryption array _enc1key - enc1Key[256] = enc1Key[1]; - enc1Key[257] = enc1Key[3]; - enc1Key[258] = enc1Key[5]; - enc1Key[259] = enc1Key[7]; - enc1Key[260] = enc1Key[n1 & 0xff]; - } - int Func5(int cnt, byte[] id, ref int n1, ref int n2, byte[] encKey) - { - // Declare variables i, tmp, mask, and initialize mask as 1 - int i, - tmp, - mask = 1; - - // If cnt is 0, return 0 - if (cnt == 0) - return 0; - - // If cnt is greater than 1, increase the value of mask until it's greater than or equal to cnt - if (cnt > 1) - { - do - { - mask = (mask << 1) + 1; - } while (mask < cnt); - } - - // Initialize i as 0 - i = 0; - - // Iterate until finding a tmp value less than or equal to cnt - do - { - // Update the values of n1 and n2, and calculate the tmp value - n1 = encKey[n1 & 0xff] + id[n2]; - n2++; - if (n2 >= id.Length) - { - n2 = 0; - n1 += id.Length; - } - tmp = n1 & mask; - - // If the iteration exceeds 11 times, take tmp modulo cnt - if (++i > 11) - tmp %= cnt; - } while (tmp > cnt); - - // Return the tmp value - return tmp; - } - /// - /// init the enckey, data is not touched - /// - void EncryptByEnc1Key(byte[] data, int len, byte[] enc1Key) - { - int i = 0; - while (len-- > 0) - { - data[i] = (byte)SubstituteEnc1key(data[i], enc1Key); - i++; - } - } - int SubstituteEnc1key(int len, byte[] enc1Key) - { - // Declare variables a, b, c as unsigned char - byte a, b, c; - - // Get certain elements from the encryption array _enc1key and calculate new values based on them - a = enc1Key[256]; - b = enc1Key[257]; - c = enc1Key[a]; - enc1Key[256] = (byte)(a + 1); - enc1Key[257] = (byte)(b + c); - - a = enc1Key[260]; - b = enc1Key[257]; - b = enc1Key[b]; - c = enc1Key[a]; - enc1Key[a] = b; - - a = enc1Key[259]; - b = enc1Key[257]; - a = enc1Key[a]; - enc1Key[b] = a; - - a = enc1Key[256]; - b = enc1Key[259]; - a = enc1Key[a]; - enc1Key[b] = a; - - a = enc1Key[256]; - enc1Key[a] = c; - b = enc1Key[258]; - a = enc1Key[c]; - c = enc1Key[259]; - b = (byte)(b + a); - enc1Key[258] = b; - - a = b; - c = enc1Key[c]; - b = enc1Key[257]; - b = enc1Key[b]; - a = enc1Key[a]; - c += b; - b = enc1Key[260]; - b = enc1Key[b]; - c += b; - b = enc1Key[c]; - c = enc1Key[256]; - c = enc1Key[c]; - a += c; - c = enc1Key[b]; - b = enc1Key[a]; - - // Store the len value in variable a and perform XOR on c and a - a = (byte)len; - c ^= b; - enc1Key[260] = a; - c ^= a; - enc1Key[259] = c; - - // Return c value - return c; - } - void Func8(byte[] data, int len, byte[] enctype1_data) - { - // Iterate over each byte in data - var dataIndex = 0; - while (len-- > 0) - { - // Encrypt the byte in data using the enctype1_data array - data[dataIndex] = enctype1_data[data[dataIndex]]; - dataIndex++; - } - } - - void EncryptScrambleData(byte[] scrambleData, byte[] masterKey) - { - for (int i = 0; i < scrambleData.Length; i++) - { - byte offset = FindIndexInMasterKey(scrambleData[i], masterKey); - scrambleData[i] = offset; - } - } - byte FindIndexInMasterKey(byte data, byte[] masterKey) - { - for (byte i = 0; i < masterKey.Length; i++) - { - if (data == masterKey[i]) - { - return i; - } - } - throw new System.Exception("No index found for scramble data"); - } - - void IEnctype1Test.EncryptByEnc0Key(byte[] data, int size, byte[] crypt) => EncryptByEnc0Key(data, size, crypt); - void IEnctype1Test.CreateEnc0Key(byte[] data, int len, byte[] buff) => CreateEnc0Key(data, buff); - void IEnctype1Test.CreateEnc1Key(byte[] validateKey, byte[] enc1Key) => CreateEnc1Key(validateKey, enc1Key); - int IEnctype1Test.Func5(int cnt, byte[] id, ref int n1, ref int n2, byte[] encKey) => Func5(cnt, id, ref n1, ref n2, encKey); - void IEnctype1Test.EncryptByEnc1Key(byte[] data, int len, byte[] enc1Key) => EncryptByEnc1Key(data, len, enc1Key); - int IEnctype1Test.SubstituteEnc1key(int len, byte[] enc1Key) => SubstituteEnc1key(len, enc1Key); - void IEnctype1Test.Func8(byte[] data, int len, byte[] enctype1_data) => Func8(data, len, enctype1_data); - byte[] IEnctype1Test.Enc0Key => _enc0Key; - byte[] IEnctype1Test.Enc1Key => _enc1Key; - byte[] IEnctype1Test.ValidateKey => ValidateKey; - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs b/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs deleted file mode 100644 index 42b91ea23..000000000 --- a/src/Servers/ServerBrowser/src/V1/Aggregate/Enctype2.cs +++ /dev/null @@ -1,192 +0,0 @@ -using System.Linq; -using System; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; -using System.Collections.Generic; - -namespace UniSpy.Server.ServerBrowser.V1.Aggregate -{ - public class Enctype2Params - { - public const int CRYPT_HEADER_LENGTH = 16; - public const int SBOX_SIZE = 256; - public const int NUM_KEYSETUP_SKIP = 2; - public const int CRYPT_MIN_LEAF_NUM = (1 << (CRYPT_HEADER_LENGTH)); - public uint[] Sbox = new uint[SBOX_SIZE]; - public uint[] XStack = new uint[CRYPT_HEADER_LENGTH]; - public uint[] YStack = new uint[CRYPT_HEADER_LENGTH]; - public uint[] ZStack = new uint[CRYPT_HEADER_LENGTH]; - public int Index; - public uint X, Y, Z; - public uint TreeNum; - public uint[] EncryptKey { get; set; } = new uint[16]; - public byte[] Seed { get; private set; } = new byte[13]; - public bool IsInitialized = false; - - public Enctype2Params(byte[] gameSecretKey) - { - InitCryptParam(); - Array.Copy(gameSecretKey, Seed, gameSecretKey.Length); - } - - private void InitCryptParam() - { - uint i, j, k, index; - uint tmp; - for (j = 0; j < 4; j++) - { - for (i = 0; i < Enctype2Params.SBOX_SIZE; i++) - { - Sbox[i] = Sbox[i] * Enctype2Params.SBOX_SIZE + i; - } - index = j; - for (k = 0; k < Enctype2Params.NUM_KEYSETUP_SKIP; k++) - { - for (i = 0; i < Enctype2Params.SBOX_SIZE; i++) - { - index += (Seed[i % Seed.Length] + Sbox[i]); - index &= Enctype2Params.SBOX_SIZE - 1; - tmp = Sbox[i]; - Sbox[i] = Sbox[index]; - Sbox[index] = tmp; - } - } - } - for (i = 0; i < Enctype2Params.SBOX_SIZE; i++) - { - Sbox[i] ^= (byte)i; - } - UpdateParams(); - IsInitialized = true; - } - public static void Accumlator(ref uint x, ref uint y, ref uint z) - { - x += z; - y += x; - x += y; - } - public static void ByteShift1(ref uint x, ref uint y, ref uint z, uint[] sbox) - { - x = ~x; - x = (((x) << 24) | ((x) >> 8)); - x ^= sbox[x & 0xFF]; - y ^= sbox[y & 0xFF]; - x = (((x) << 24) | ((x) >> 8)); - y = (((y) << 8) | ((y) >> 24)); - x ^= sbox[x & 0xFF]; - y ^= sbox[y & 0xFF]; - y = (((y) << 8) | ((y) >> 24)); - z += (z + 1); - } - public static void ByteShift2(ref uint x, ref uint y, ref uint z, uint[] sbox) - { - y = (((y) << 24) | ((y) >> 8)); - x ^= sbox[x & 0xFF]; - y ^= sbox[y & 0xFF]; - y = (((y) << 24) | ((y) >> 8)); - x = (((x) << 8) | ((x) >> 24)); - x ^= sbox[x & 0xFF]; - y ^= sbox[y & 0xFF]; - x = (((x) << 8) | ((x) >> 24)); - z += z; - } - private void UpdateParams(uint treeNum = 0, uint leafNum = 0) - { - int i = 1 << (CRYPT_HEADER_LENGTH - 1); - X = treeNum; - Y = 0; - Z = 1; - Index = 0; - while (i > 0) - { - Accumlator(ref X, ref Y, ref Z); - if ((i & leafNum) != 0) - { - ByteShift1(ref X, ref Y, ref Z, Sbox); - } - else - { - XStack[Index] = X; - YStack[Index] = Y; - ZStack[Index] = Z; - Index++; - - ByteShift2(ref X, ref Y, ref Z, Sbox); - } - i >>= 1; - } - TreeNum = treeNum; - } - public void InitKeyData() - { - uint x, y, z; - int index; - x = X; - y = Y; - z = Z; - index = Index; - for (uint i = 0; i < EncryptKey.Length; i++) - { - while (z < Enctype2Params.CRYPT_MIN_LEAF_NUM) - { - Enctype2Params.Accumlator(ref x, ref y, ref z); - XStack[index] = x; - YStack[index] = y; - ZStack[index] = z; - index++; - Enctype2Params.ByteShift2(ref x, ref y, ref z, Sbox); - } - EncryptKey[i] = x ^ y; - index--; - if (index < 0) - { - index = 0; - } - x = XStack[index]; - y = YStack[index]; - z = ZStack[index]; - Enctype2Params.ByteShift1(ref x, ref y, ref z, Sbox); - } - X = x; - Y = y; - Z = z; - Index = index; - } - } - public class Enctype2 : EnctypeBase, IEnctype2Test - { - public const int HeaderSize = 8; - public byte[] GameSecreteKey { get; private set; } - private Enctype2Params _params; - private List _buffer = new List(); - public Enctype2(string gameSecretKey) - { - GameSecreteKey = UniSpyEncoding.GetBytes(gameSecretKey); - _params = new Enctype2Params(GameSecreteKey); - _buffer.Add((byte)(_params.Seed.Length ^ 0xEC)); - _buffer.AddRange(_params.Seed); - } - - public override byte[] Encrypt(byte[] data) - { - int i; - var plainText = data.ToArray(); - var encKeyBytes = EnctypeBase.ConvertUintToBytes(_params.EncryptKey); - for (i = 0; i < plainText.Length; i++) - { - var modIndex = i % (encKeyBytes.Length - 1); - if (modIndex == 0) - { - _params.InitKeyData(); - encKeyBytes = EnctypeBase.ConvertUintToBytes(_params.EncryptKey); - } - plainText[i] ^= encKeyBytes[modIndex]; - } - _buffer.AddRange(plainText); - return _buffer.ToArray(); - } - - - // void IEnctype2Test.Encoder(uint[] data) => InitKeyData(data); - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Application/Client.cs b/src/Servers/ServerBrowser/src/V1/Application/Client.cs deleted file mode 100644 index c2b3ba072..000000000 --- a/src/Servers/ServerBrowser/src/V1/Application/Client.cs +++ /dev/null @@ -1,25 +0,0 @@ -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.ServerBrowser.V1.Handler; - -namespace UniSpy.Server.ServerBrowser.V1.Application -{ - public sealed class Client : ClientBase - { - public new ClientInfo Info { get => (ClientInfo)base.Info; private set => base.Info = value; } - public Client(IConnection connection, IServer server) : base(connection, server) - { - Info = new ClientInfo(); - } - protected override void OnConnected() - { - this.LogNetworkSending(ClientInfo.ChallengeResponse); - Connection.Send(ClientInfo.ChallengeResponse); - base.OnConnected(); - } - - protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, UniSpyEncoding.GetString((byte[])buffer)); - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Application/ClientInfo.cs b/src/Servers/ServerBrowser/src/V1/Application/ClientInfo.cs deleted file mode 100644 index 81ceef0b4..000000000 --- a/src/Servers/ServerBrowser/src/V1/Application/ClientInfo.cs +++ /dev/null @@ -1,15 +0,0 @@ -using UniSpy.Server.Core.Abstraction.BaseClass; - -namespace UniSpy.Server.ServerBrowser.V1.Application -{ - public sealed class ClientInfo : ClientInfoBase - { - public const string Challenge = @"000000"; - public const string ChallengeResponse = $@"\basic\\secure\{Challenge}\final\"; - public string GameSecretKey { get; set; } - public string ValidateKey { get; set; } - public ClientInfo() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Application/Server.cs b/src/Servers/ServerBrowser/src/V1/Application/Server.cs deleted file mode 100644 index 26886b9a8..000000000 --- a/src/Servers/ServerBrowser/src/V1/Application/Server.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Net; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Network.Tcp.Server; - -namespace UniSpy.Server.ServerBrowser.V1.Application -{ - public sealed class Server : ServerBase - { - static Server() - { - _name = "ServerBrowserV1"; - } - public Server() { } - - public Server(IConnectionManager manager) : base(manager) { } - protected override IClient CreateClient(IConnection connection) => new Client(connection, this); - - - protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new TcpConnectionManager(endPoint); - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Contract/Request/GameNameRequest.cs b/src/Servers/ServerBrowser/src/V1/Contract/Request/GameNameRequest.cs deleted file mode 100644 index 718fe692b..000000000 --- a/src/Servers/ServerBrowser/src/V1/Contract/Request/GameNameRequest.cs +++ /dev/null @@ -1,53 +0,0 @@ -using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; - -namespace UniSpy.Server.ServerBrowser.V1.Contract.Request -{ - public enum EncryptionType - { - // do not encrypt - Plaintext, - // use enctype 1 - Type1, - // use enctype 2 - Type2 - } - /// - /// verify client - /// The request maybe \gamename\\gamever\\location\0\validate\\secure\\final\\queryid\1.1\" - /// - public sealed class GameNameRequest : RequestBase - { - public EncryptionType EncType { get; private set; } - public string Version { get; private set; } - public string ValidateKey { get; private set; } - public GameNameRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - if (KeyValues.ContainsKey("enctype")) - { - if (!System.Enum.TryParse(KeyValues["enctype"], out var type)) - { - throw new ServerBrowser.Exception("Encryption type format is not correct."); - } - EncType = type; - } - - if (!KeyValues.ContainsKey("gamever")) - { - throw new ServerBrowser.Exception("Game engine version is not presented in request."); - } - - Version = KeyValues["gamever"]; - //process secure - // star trek armada 2 do not use encryption - if (KeyValues.ContainsKey("secure")) - { - ValidateKey = KeyValues["secure"]; - } - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Contract/Request/ListRequest.cs b/src/Servers/ServerBrowser/src/V1/Contract/Request/ListRequest.cs deleted file mode 100644 index aa671df58..000000000 --- a/src/Servers/ServerBrowser/src/V1/Contract/Request/ListRequest.cs +++ /dev/null @@ -1,52 +0,0 @@ -using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; - -namespace UniSpy.Server.ServerBrowser.V1.Contract.Request -{ - public enum ListRequestType - { - Basic, - Info, - Group - } - /// - /// Query for game server list of specific game name with filter - /// - public sealed class ListRequest : RequestBase - { - public string Filter { get; private set; } - public bool IsSendingCompressFormat { get; private set; } - public bool IsSendAllInfo { get; private set; } - public ListRequestType? Type { get; private set; } - public ListRequest(string rawRequest) : base(rawRequest) - { - } - public override void Parse() - { - // base.Parse(); - var firstGameIndex = RawRequest.IndexOf('\\', 10); - var gameNameHeader = RawRequest.Substring(0, firstGameIndex); - - - if (KeyValues.ContainsKey("where")) - { - Filter = KeyValues["where"]; - } - switch (KeyValues[CommandName]) - { - case "cmp": - IsSendingCompressFormat = true; - Type = ListRequestType.Basic; - break; - case "info2": - IsSendAllInfo = true; - Type = ListRequestType.Info; - break; - case "groups": - Type = ListRequestType.Group; - IsSendingCompressFormat = true; - IsSendAllInfo = true; - break; - } - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Contract/Response/GameNameResponse.cs b/src/Servers/ServerBrowser/src/V1/Contract/Response/GameNameResponse.cs deleted file mode 100644 index 838dd0061..000000000 --- a/src/Servers/ServerBrowser/src/V1/Contract/Response/GameNameResponse.cs +++ /dev/null @@ -1,16 +0,0 @@ -using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; - -namespace UniSpy.Server.ServerBrowser.V1.Contract.Response -{ - public sealed class GameNameResponse : ResponseBase - { - public GameNameResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - - public override void Build() - { - throw new System.NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Contract/Response/ListResponse.cs b/src/Servers/ServerBrowser/src/V1/Contract/Response/ListResponse.cs deleted file mode 100644 index aee141828..000000000 --- a/src/Servers/ServerBrowser/src/V1/Contract/Response/ListResponse.cs +++ /dev/null @@ -1,87 +0,0 @@ -using System.Text.RegularExpressions; -using System.Linq; -using System; -using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; -using UniSpy.Server.ServerBrowser.V1.Contract.Request; -using UniSpy.Server.ServerBrowser.V1.Contract.Result; - -namespace UniSpy.Server.ServerBrowser.V1.Contract.Response -{ - public sealed class ListResponse : ResponseBase - { - private new ListRequest _request => (ListRequest)base._request; - private new ListResult _result => (ListResult)base._result; - public ListResponse(ListRequest request, ListResult result) : base(request, result) - { - } - - public override void Build() - { - SendingBuffer = $@"\fieldcount\{_result.ServersInfo.Count}"; - switch (_request.Type) - { - case ListRequestType.Basic: - BuildServerGeneralInfo(); - break; - case ListRequestType.Info: - BuildServerAllInfo(); - break; - case ListRequestType.Group: - BuildGroupInfo(); - break; - } - - SendingBuffer += @"\final\"; - } - /// - /// Build group infos - /// - public void BuildGroupInfo() - { - SendingBuffer = @"\group" + SendingBuffer; - foreach (var group in _result.PeerRoomsInfo) - { - foreach (var kv in group.KeyValues) - { - SendingBuffer += @$"\{kv.Key}\{kv.Value}"; - } - } - } - /// - /// Build servers detailed info (contain all keyvalues of all server) - /// - public void BuildServerAllInfo() - { - SendingBuffer = @"\list2" + SendingBuffer; - foreach (var info in _result.ServersInfo) - { - foreach (var kv in info.KeyValues) - { - SendingBuffer += @$"\{kv.Key}\{kv.Value}"; - } - } - } - - /// - /// Build general info for servers (ip and port) - /// - public void BuildServerGeneralInfo() - { - SendingBuffer = @"\list2" + SendingBuffer; - - foreach (var info in _result.ServersInfo) - { - if (_request.IsSendingCompressFormat) - { - var portBytes = BitConverter.GetBytes((short)info.HostPort); - var buffer = BitConverter.ToString(info.HostIPAddress.GetAddressBytes()) + BitConverter.ToString(portBytes); - SendingBuffer += buffer; - } - else - { - SendingBuffer += @$"\ip\{info.HostIPEndPoint}"; - } - } - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Contract/Result/ListResult.cs b/src/Servers/ServerBrowser/src/V1/Contract/Result/ListResult.cs deleted file mode 100644 index a935594e9..000000000 --- a/src/Servers/ServerBrowser/src/V1/Contract/Result/ListResult.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.QueryReport.Aggregate.Redis.PeerGroup; -using UniSpy.Server.QueryReport.V1.Aggregation.Redis; -using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; - -namespace UniSpy.Server.ServerBrowser.V1.Contract.Result -{ - public sealed class ListResult : ResultBase - { - public List ServersInfo { get; set; } - public List PeerRoomsInfo { get; set; } - public ListResult() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/GameNameHandler.cs b/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/GameNameHandler.cs deleted file mode 100644 index 58c2d30d6..000000000 --- a/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/GameNameHandler.cs +++ /dev/null @@ -1,45 +0,0 @@ -using UniSpy.Server.Core.Extension; -using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; -using UniSpy.Server.ServerBrowser.V1.Aggregate; -using UniSpy.Server.ServerBrowser.V1.Application; -using UniSpy.Server.ServerBrowser.V1.Contract.Request; - -namespace UniSpy.Server.ServerBrowser.V1.Handler.CmdHandler -{ - public sealed class GameNameHandler : CmdHandlerBase - { - private new GameNameRequest _request => (GameNameRequest)base._request; - public GameNameHandler(Client client, GameNameRequest request) : base(client, request) - { - } - - protected override void RequestCheck() - { - // todo create ICryptographic implementation here based on the enctype - base.RequestCheck(); - _client.Info.GameSecretKey = DataOperationExtensions.GetSecretKey(_request.GameName); - _client.Info.ValidateKey = _request.ValidateKey; - } - protected override void DataOperation() - { - switch (_request.EncType) - { - case EncryptionType.Plaintext: - break; - case EncryptionType.Type1: - _client.Crypto = new Enctype1(_request.ValidateKey); - break; - case EncryptionType.Type2: - _client.Crypto = new Enctype2(_client.Info.GameSecretKey); - break; - } - } - - protected override void Response() - { - base.Response(); - // reset the crypto object to null - _client.Crypto = null; - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/ListHandler.cs b/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/ListHandler.cs deleted file mode 100644 index 180b43e56..000000000 --- a/src/Servers/ServerBrowser/src/V1/Handler/CmdHandler/ListHandler.cs +++ /dev/null @@ -1,39 +0,0 @@ -using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; -using UniSpy.Server.ServerBrowser.V1.Application; -using UniSpy.Server.ServerBrowser.V1.Contract.Request; -using UniSpy.Server.ServerBrowser.V1.Contract.Response; -using UniSpy.Server.ServerBrowser.V1.Contract.Result; - -namespace UniSpy.Server.ServerBrowser.V1.Handler.CmdHandler -{ - public class ListHandler : CmdHandlerBase - { - private new ListResult _result { get => (ListResult)base._result; set => base._result = value; } - private new ListResponse _response { get => (ListResponse)base._response; set => base._response = value; } - private new ListRequest _request => (ListRequest)base._request; - public ListHandler(Client client, ListRequest request) : base(client, request) - { - _result = new ListResult(); - } - protected override void RequestCheck() - { - switch (_request.Type) - { - - case ListRequestType.Info: - case ListRequestType.Basic: - _result.ServersInfo = QueryReport.V1.Application.StorageOperation.Persistance.GetServersInfo(_request.GameName); - break; - case ListRequestType.Group: - // _result.PeerRoomsInfo = - // QueryReport.V2.Application.StorageOperation.Persistance.GetPeerRoomsInfo(_request.GameName); - // todo - break; - } - } - protected override void ResponseConstruct() - { - _response = new ListResponse(_request, _result); - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V1/Handler/CmdSwitcher.cs b/src/Servers/ServerBrowser/src/V1/Handler/CmdSwitcher.cs deleted file mode 100644 index bd6f5904a..000000000 --- a/src/Servers/ServerBrowser/src/V1/Handler/CmdSwitcher.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Misc; -using UniSpy.Server.ServerBrowser.V1.Application; -using UniSpy.Server.ServerBrowser.V1.Contract.Request; -using UniSpy.Server.ServerBrowser.V1.Handler.CmdHandler; - -namespace UniSpy.Server.ServerBrowser.V1.Handler -{ - public sealed class CmdSwitcher : CmdSwitcherBase - { - private new string _rawRequest => (string)base._rawRequest; - private new Client _client => (Client)base._client; - public CmdSwitcher(Client client, string rawRequest) : base(client, rawRequest) - { - } - - protected override IHandler CreateCmdHandlers(object name, object rawRequest) - { - //todo add v1 support - var request = (string)rawRequest; - switch ((string)name) - { - case "gamename": - return new GameNameHandler(_client, new GameNameRequest(request)); - case "list": - return new ListHandler(_client, new ListRequest(request)); - default: - return null; - } - } - - protected override void ProcessRawRequest() - { - // sb v1 protocol - var name = GameSpyUtils.GetRequestName(_rawRequest); - _requests.Add(new KeyValuePair(name, _rawRequest)); - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/AdHocRequestBase.cs b/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/AdHocRequestBase.cs deleted file mode 100755 index 819294634..000000000 --- a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/AdHocRequestBase.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; -using System.Linq; -using System.Net; -using UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass; - -namespace UniSpy.Server.ServerBrowser.V2.Abstraction -{ - public abstract class AdHocRequestBase : RequestBase - { - /// - /// The game server client search for - /// - public IPEndPoint GameServerPublicIPEndPoint { get; private set; } - public AdHocRequestBase(byte[] rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - byte[] ip = RawRequest.Skip(3).Take(4).ToArray(); - // raw request is in big endian we need to convert it to little endian - byte[] port = RawRequest.Skip(7).Take(2).Reverse().ToArray(); - //TODO fix for gbrome!!!!!!!!!!!!!!!!!!! - GameServerPublicIPEndPoint = new IPEndPoint(new IPAddress(ip), BitConverter.ToUInt16(port)); - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/AdHocResponseBase.cs b/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/AdHocResponseBase.cs deleted file mode 100644 index 942570e8f..000000000 --- a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/AdHocResponseBase.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.ServerBrowser.V2.Contract.Result; - -namespace UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass -{ - public abstract class AdHocResponseBase : ResponseBase - { - public new AdHocResult _result => (AdHocResult)base._result; - protected List _buffer = new List(); - - protected AdHocResponseBase(ResultBase result) : base(null, result) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/CmdHandlerBase.cs b/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/CmdHandlerBase.cs deleted file mode 100755 index b39b9e775..000000000 --- a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/CmdHandlerBase.cs +++ /dev/null @@ -1,15 +0,0 @@ -using UniSpy.Server.ServerBrowser.V2.Application; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass -{ - public abstract class CmdHandlerBase : UniSpy.Server.Core.Abstraction.BaseClass.CmdHandlerBase - { - protected new Client _client => (Client)base._client; - protected new RequestBase _request => (RequestBase)base._request; - protected new ResultBase _result { get => (ResultBase)base._result; set => base._result = value; } - public CmdHandlerBase(IClient client, IRequest request) : base(client, request) - { - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/RequestBase.cs b/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/RequestBase.cs deleted file mode 100755 index f0f5f3bc4..000000000 --- a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/RequestBase.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System; -using System.Linq; -using UniSpy.Server.ServerBrowser.V2.Enumerate; - -namespace UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass -{ - public abstract class RequestBase : UniSpy.Server.Core.Abstraction.BaseClass.RequestBase - { - public int RequestLength { get; private set; } - public new byte[] RawRequest => (byte[])base.RawRequest; - public new RequestType CommandName { get => (RequestType)base.CommandName; protected set => base.CommandName = value; } - public RequestBase() { } - protected RequestBase(byte[] rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - RequestLength = BitConverter.ToInt16(RawRequest.Take(2).Reverse().ToArray()); - CommandName = (RequestType)RawRequest[2]; - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ResponseBase.cs b/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ResponseBase.cs deleted file mode 100755 index 9e535b727..000000000 --- a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ResponseBase.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass -{ - - /// - /// SB always need to response to client even there are no server or error occured - /// - public abstract class ResponseBase : UniSpy.Server.Core.Abstraction.BaseClass.ResponseBase - { - protected new RequestBase _request => (RequestBase)base._request; - protected new ResultBase _result => (ResultBase)base._result; - public new byte[] SendingBuffer { get => (byte[])base.SendingBuffer; set => base.SendingBuffer = value; } - public ResponseBase(RequestBase request, ResultBase result) : base(request, result) - { - } - - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ResultBase.cs b/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ResultBase.cs deleted file mode 100755 index 870f40a85..000000000 --- a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ResultBase.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass -{ - public class ResultBase : UniSpy.Server.Core.Abstraction.BaseClass.ResultBase - { - public ResultBase() - { - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ServerListUpdateOption/ServerListUpdateOptionHandlerBase.cs b/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ServerListUpdateOption/ServerListUpdateOptionHandlerBase.cs deleted file mode 100755 index fe51c9786..000000000 --- a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ServerListUpdateOption/ServerListUpdateOptionHandlerBase.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Linq; -using UniSpy.Server.ServerBrowser.V2.Aggregate.Misc; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Extension; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass -{ - public abstract class ServerListUpdateOptionHandlerBase : CmdHandlerBase - { - protected new ServerListUpdateOptionRequestBase _request => (ServerListUpdateOptionRequestBase)base._request; - protected new ServerListUpdateOptionResultBase _result { get => (ServerListUpdateOptionResultBase)base._result; set => base._result = value; } - protected new ServerListUpdateOptionResponseBase _response { get => (ServerListUpdateOptionResponseBase)base._response; set => base._response = value; } - public ServerListUpdateOptionHandlerBase(IClient client, IRequest request) : base(client, request) - { - } - protected override void RequestCheck() - { - base.RequestCheck(); - if (_client.Info.GameSecretKey is null) - { - string secretKey = DataOperationExtensions - .GetSecretKey(_request.GameName); - //we first check and get secrete key from database - if (secretKey is null) - { - throw new System.ArgumentNullException("Can not find secretkey in database."); - } - //this is client public ip and default query port - _client.Info.GameSecretKey = secretKey; - } - if (_client.Info.ClientChallenge is null) - { - _client.Info.ClientChallenge = _request.ClientChallenge; - } - // initialize sb encryption - if (_client.Crypto is null) - { - _client.Crypto = new EnctypeX( - _client.Info.GameSecretKey, - _client.Info.ClientChallenge); - } - _client.Info.GameName = _request.GameName; - } - - protected override void Response() - { - _response.Build(); - var bodyBuffer = _response.SendingBuffer.Skip(14).ToArray(); - var headBuffer = _response.SendingBuffer.Take(14).ToArray(); - var bufferEncrypted = _client.Crypto.Encrypt(bodyBuffer); - var buffer = headBuffer.Concat(bufferEncrypted).ToArray(); - _client.LogNetworkSending(_response.SendingBuffer); - _client.Connection.Send(buffer); - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ServerListUpdateOption/ServerListUpdateOptionRequestBase.cs b/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ServerListUpdateOption/ServerListUpdateOptionRequestBase.cs deleted file mode 100755 index 96318511a..000000000 --- a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ServerListUpdateOption/ServerListUpdateOptionRequestBase.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Net; -using UniSpy.Server.ServerBrowser.V2.Enumerate; -namespace UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass -{ - public abstract class ServerListUpdateOptionRequestBase : RequestBase - { - public byte? RequestVersion { get; protected set; } - public byte? ProtocolVersion { get; protected set; } - public byte? EncodingVersion { get; protected set; } - public int? GameVersion { get; protected set; } - public int? QueryOptions { get; protected set; } - public string DevGameName { get; protected set; } - public string GameName { get; protected set; } - public string ClientChallenge { get; protected set; } - public ServerListUpdateOption? UpdateOption { get; protected set; } - public string[] Keys { get; protected set; } - public string Filter { get; protected set; } - public IPAddress SourceIP { get; protected set; } - public int? MaxServers { get; protected set; } - protected ServerListUpdateOptionRequestBase() { } - protected ServerListUpdateOptionRequestBase(byte[] rawRequest) : base(rawRequest) - { - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ServerListUpdateOption/ServerListUpdateOptionResponseBase.cs b/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ServerListUpdateOption/ServerListUpdateOptionResponseBase.cs deleted file mode 100755 index de9bc2e5f..000000000 --- a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ServerListUpdateOption/ServerListUpdateOptionResponseBase.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.ServerBrowser.V2.Application; -using UniSpy.Server.ServerBrowser.V2.Enumerate; -using UniSpy.Server.ServerBrowser.V2.Aggregate.Misc; -using UniSpy.Server.Core.Encryption; - -namespace UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass -{ - public abstract class ServerListUpdateOptionResponseBase : ResponseBase - { - protected new ServerListUpdateOptionRequestBase _request => (ServerListUpdateOptionRequestBase)base._request; - protected new ServerListUpdateOptionResultBase _result => (ServerListUpdateOptionResultBase)base._result; - protected List _serversInfoBuffer; - public ServerListUpdateOptionResponseBase(RequestBase request, ResultBase result) : base(request, result) - { - _serversInfoBuffer = new List(); - } - - public override void Build() - { - //todo check protocol version to build response data - //Add crypt header - var cryptHeader = BuildCryptHeader(); - _serversInfoBuffer.AddRange(cryptHeader); - _serversInfoBuffer.AddRange(_result.ClientRemoteIP); - _serversInfoBuffer.AddRange(ClientInfo.HtonQueryReportDefaultPort); - } - public byte[] BuildCryptHeader() - { - // cryptHeader have 14 bytes, when we encrypt data we need skip the first 14 bytes - var cryptHeader = new List(); - cryptHeader.Add(2 ^ 0xEC); - #region message length? - cryptHeader.AddRange(new byte[] { 0, 0 }); - #endregion - cryptHeader.Add((byte)(ClientInfo.ServerChallenge.Length ^ 0xEA)); - cryptHeader.AddRange(UniSpyEncoding.GetBytes(ClientInfo.ServerChallenge)); - return cryptHeader.ToArray(); - } - protected abstract void BuildServersFullInfo(); - - protected void BuildUniqueValue() - { - //because we are using NTS string so we do not have any value here - _serversInfoBuffer.Add(0); - } - protected void BuildServerKeys() - { - //we add the total number of the requested keys - _serversInfoBuffer.Add((byte)_request.Keys.Length); - //then we add the keys - foreach (var key in _request.Keys) - { - _serversInfoBuffer.Add((byte)DataKeyType.String); - _serversInfoBuffer.AddRange(UniSpyEncoding.GetBytes(key)); - _serversInfoBuffer.Add(StringFlag.StringSpliter); - } - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ServerListUpdateOption/ServerListUpdateOptionResultBase.cs b/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ServerListUpdateOption/ServerListUpdateOptionResultBase.cs deleted file mode 100755 index d95ea40b7..000000000 --- a/src/Servers/ServerBrowser/src/V2/Abstraction/BaseClass/ServerListUpdateOption/ServerListUpdateOptionResultBase.cs +++ /dev/null @@ -1,14 +0,0 @@ -using UniSpy.Server.ServerBrowser.V2.Enumerate; - -namespace UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass -{ - public abstract class ServerListUpdateOptionResultBase : ResultBase - { - public byte[] ClientRemoteIP { get; set; } - public GameServerFlags Flag { get; set; } - public string GameSecretKey { get; set; } - protected ServerListUpdateOptionResultBase() - { - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Aggregate/HeartbeatChannel.cs b/src/Servers/ServerBrowser/src/V2/Aggregate/HeartbeatChannel.cs deleted file mode 100644 index 662bc6e64..000000000 --- a/src/Servers/ServerBrowser/src/V2/Aggregate/HeartbeatChannel.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.Core.Logging; -using UniSpy.Server.QueryReport.V2.Aggregate.Redis.GameServer; -using UniSpy.Server.ServerBrowser.V2.Handler.CmdHandler.AdHoc; - -namespace UniSpy.Server.ServerBrowser.V2.Aggregate -{ - public sealed class HeartbeatChannel : QueryReport.V2.Aggregate.Redis.HeartbeatChannel - { - /// - /// we do not run subscribe() in QR because QR only need to push - /// We run subscribe() in SB, because SB need to receive message - /// - public override void ReceivedMessage(GameServerCache message) - { - LogWriter.LogInfo($"Received game server message from QR:{message.ServerID}"); - var handler = new AdHocHandler(message); - handler.Handle(); - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Aggregate/Misc/Encryption.cs b/src/Servers/ServerBrowser/src/V2/Aggregate/Misc/Encryption.cs deleted file mode 100755 index c704211cf..000000000 --- a/src/Servers/ServerBrowser/src/V2/Aggregate/Misc/Encryption.cs +++ /dev/null @@ -1,192 +0,0 @@ -using UniSpy.Server.ServerBrowser.V2.Application; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; -namespace UniSpy.Server.ServerBrowser.V2.Aggregate.Misc -{ - public class EncryptionParameters - { - public byte[] Register = new byte[256]; - public byte Index0; - public byte Index1; - public byte Index2; - public byte Index3; - public byte Index4; - } - public class EnctypeX : ICryptography - { - private EncryptionParameters _encParams; - private byte[] _clientChallenge = new byte[8]; - private byte[] _serverChallenge; - private byte[] _secretKey; - public EnctypeX(string secretKey, string clientChallenge) - { - _encParams = new EncryptionParameters(); - _clientChallenge = UniSpyEncoding.GetBytes(clientChallenge); - _serverChallenge = UniSpyEncoding.GetBytes(ClientInfo.ServerChallenge); - _secretKey = UniSpyEncoding.GetBytes(secretKey); - InitEncryptionAlgorithm(); - } - - /// - /// Initialize the challenge which generates the permutation table - /// - /// game secret key - /// the random number challenge that server or client generate - public void InitEncryptionAlgorithm() - { - if (_clientChallenge.Length != 8) - { - //we have error client challenge - throw new System.ArgumentException("Client challenge length not valid!"); - } - - for (int i = 0; i < _serverChallenge.Length; i++) - { - int tempIndex0 = i * _secretKey[i % _secretKey.Length] % 8; - int tempIndex1 = i % 8; - byte bitwiseResult = (byte)(_clientChallenge[tempIndex1] ^ _serverChallenge[i]); - _clientChallenge[tempIndex0] ^= (byte)(bitwiseResult & 0xFF); - } - InitEncryptionParameters(); - } - - private void InitEncryptionParameters() - { - int i; - byte toSwap, swapTemp, randomSum; - byte keyPosition; - - if (_clientChallenge.Length < 1) - { - NonChallengeMappingInit(); - return; - } - - //generate ascii table - for (i = 0; i < 256; i++) - { - _encParams.Register[i] = (byte)i; - } - - toSwap = 0; - keyPosition = 0; - randomSum = 0; - for (i = 255; i > 0; i--) - { - toSwap = IndexPositionGeneration(i, ref randomSum, ref keyPosition); - swapTemp = _encParams.Register[i]; - _encParams.Register[i] = _encParams.Register[toSwap]; - _encParams.Register[toSwap] = swapTemp; - } - - // Initialize the indices and data dependencies. - // Indices are set to different values instead of all 0 - // to reduce what is known about the state of the state.cards - // when the first byte is emitted. - - _encParams.Index0 = _encParams.Register[1]; - _encParams.Index1 = _encParams.Register[3]; - _encParams.Index2 = _encParams.Register[5]; - _encParams.Index3 = _encParams.Register[7]; - _encParams.Index4 = _encParams.Register[randomSum]; - } - private void NonChallengeMappingInit() - { - // Initialize the indices and data dependencies. - byte i, j; - _encParams.Index0 = 1; - _encParams.Index1 = 3; - _encParams.Index2 = 5; - _encParams.Index3 = 7; - _encParams.Index4 = 11; - - // Start with state.cards all in inverse order. - for (i = 0, j = 255; i <= 255; i++, j--) - { - _encParams.Register[i] = j; - } - } - - private byte ByteShift(byte b) - { - byte swapTempStorage; - _encParams.Index1 = (byte)(_encParams.Index1 + _encParams.Register[_encParams.Index0++]); - swapTempStorage = _encParams.Register[_encParams.Index4]; - _encParams.Register[_encParams.Index4] = _encParams.Register[_encParams.Index1]; - _encParams.Register[_encParams.Index1] = _encParams.Register[_encParams.Index3]; - _encParams.Register[_encParams.Index3] = _encParams.Register[_encParams.Index0]; - _encParams.Register[_encParams.Index0] = swapTempStorage; - _encParams.Index2 = (byte)(_encParams.Index2 + _encParams.Register[swapTempStorage]); - - - _encParams.Index4 = (byte)(b ^ _encParams.Register[(_encParams.Register[_encParams.Index2] + - _encParams.Register[_encParams.Index0]) & 0xFF] ^ - _encParams.Register[_encParams.Register[(_encParams.Register[_encParams.Index3] + - _encParams.Register[_encParams.Index4] + - _encParams.Register[_encParams.Index1]) & 0xFF]]); - _encParams.Index3 = b; - - return _encParams.Index4; - } - - - /// - /// Generate permutation index position - /// - /// - /// - /// - /// - /// - private byte IndexPositionGeneration(int limit, ref byte randomSum, ref byte keyPosition) - { - byte swapIndex, retryLimiter, bitMask; - - if (limit == 0) - { - return 0; - } - - retryLimiter = 0; - bitMask = 1; - - while (bitMask < limit) - { - bitMask = (byte)((bitMask << 1) + 1); - } - - do - { - randomSum = (byte)(_encParams.Register[randomSum] + _clientChallenge[keyPosition++]); - - if (keyPosition >= _clientChallenge.Length) - { - keyPosition = 0; - randomSum = (byte)(randomSum + _clientChallenge.Length); - } - swapIndex = (byte)(bitMask & randomSum); - if (++retryLimiter > 11) - { - swapIndex %= (byte)limit; - } - } - while (swapIndex > limit); - - return swapIndex; - } - - public byte[] Encrypt(byte[] plainText) - { - int i; - byte[] cipherText = new byte[plainText.Length]; - for (i = 0; i < plainText.Length; i++) - { - cipherText[i] = ByteShift(plainText[i]); - } - return cipherText; - } - public byte[] Decrypt(byte[] buffer) => buffer; - - } - -} diff --git a/src/Servers/ServerBrowser/src/V2/Aggregate/Misc/GameFilter.cs b/src/Servers/ServerBrowser/src/V2/Aggregate/Misc/GameFilter.cs deleted file mode 100644 index d592d20bd..000000000 --- a/src/Servers/ServerBrowser/src/V2/Aggregate/Misc/GameFilter.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UniSpy.Server.ServerBrowser.V2.Entity -{ - class GameFilter - { - public GameFilter() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Aggregate/Misc/KeyType.cs b/src/Servers/ServerBrowser/src/V2/Aggregate/Misc/KeyType.cs deleted file mode 100755 index cc0a86db7..000000000 --- a/src/Servers/ServerBrowser/src/V2/Aggregate/Misc/KeyType.cs +++ /dev/null @@ -1,49 +0,0 @@ -namespace UniSpy.Server.ServerBrowser.V2.Handler.SystemHandler.KeyType -{ - /// - /// all unique value string has 255 limits, - /// we do not think there will be a lot of server for each game - /// so we parse every value in string format for simplicity - /// - public class KeyTypeHandler - { - public Enumerate.DataKeyType GetKeyType(string key, string value) - { - if (IsByte(key, value)) - { - return Enumerate.DataKeyType.Byte; - } - else if (IsShort(key, value)) - { - return Enumerate.DataKeyType.Short; - } - else - { - return Enumerate.DataKeyType.String; - } - } - - public bool IsByte(string key, string value) - { - if (value.Length == 1) - { - if (key.Contains("numplayers") || key.Contains("maxplayers") || key.Contains("password")) - { - return true; - } - } - - return false; - } - - public bool IsShort(string key, string value) - { - if (value.Length == 2) - { - return true; - } - - return false; - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Aggregate/Misc/SBStringFlag.cs b/src/Servers/ServerBrowser/src/V2/Aggregate/Misc/SBStringFlag.cs deleted file mode 100755 index 7efe5827d..000000000 --- a/src/Servers/ServerBrowser/src/V2/Aggregate/Misc/SBStringFlag.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace UniSpy.Server.ServerBrowser.V2.Aggregate.Misc -{ - public class StringFlag - { - public static readonly byte[] AllServerEndFlag = { 0, 255, 255, 255, 255 }; - public static readonly byte StringSpliter = 0; - public static readonly byte NTSStringFlag = 0xFF; - - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Aggregate/ServerInfoBuilder.cs b/src/Servers/ServerBrowser/src/V2/Aggregate/ServerInfoBuilder.cs deleted file mode 100644 index 40582fff5..000000000 --- a/src/Servers/ServerBrowser/src/V2/Aggregate/ServerInfoBuilder.cs +++ /dev/null @@ -1,127 +0,0 @@ -using System.Collections.Generic; -using System.Net; -using UniSpy.Server.QueryReport.V2.Aggregate.Redis.GameServer; -using UniSpy.Server.ServerBrowser.V2.Application; -using UniSpy.Server.ServerBrowser.V2.Enumerate; - -namespace UniSpy.Server.ServerBrowser.V2.Aggregate -{ - public static class ServerInfoBuilder - { - - /// - /// Add more server info here - /// the sequence of server info is important - /// 1.PRIVATE_IP_FLAG length=4 - /// 2.ICMP_IP_FLAG length=4 - /// 3.NONSTANDARD_PORT_FLAG length=2 - /// 4.NONSTANDARD_PRIVATE_PORT_FLAG length=2 - /// - /// - /// - public static List BuildServerInfoHeader(GameServerFlags flag, GameServerCache serverInfo) - { - List header = new List(); - // add key flag - header.Add((byte)flag); - // we add server public ip here - header.AddRange(serverInfo.HostIPAddress.GetAddressBytes()); - // we check host port is standard port or not - CheckNonStandardPort(header, serverInfo); - // check if game can directly query information from server - CheckUnsolicitedUdp(header, serverInfo); - // we check the natneg flag - CheckNatNegFlag(header, serverInfo); - // now we check if there are private ip - CheckPrivateIP(header, serverInfo); - // we check private port here - CheckNonStandardPrivatePort(header, serverInfo); - // we check icmp support here - CheckICMPSupport(header, serverInfo); - - // _serverListData.AddRange(header); - return header; - } - public static void CheckNatNegFlag(List header, GameServerCache serverInfo) - { - if (serverInfo.ServerData.ContainsKey("natneg")) - { - var natNegFlag = int.Parse(serverInfo.ServerData["natneg"]); - var unsolicitedUdp = header[0] & (byte)GameServerFlags.UnsolicitedUdpFlag; - if (natNegFlag == 1 && unsolicitedUdp == 0) - { - header[0] ^= (byte)GameServerFlags.ConnectNegotiateFlag; - } - } - } - public static void CheckUnsolicitedUdp(List header, GameServerCache serverInfo) - { - if (serverInfo.ServerData.ContainsKey("allow_unsolicited_udp")) - { - var unsolicitedUdp = int.Parse(serverInfo.ServerData["unsolicitedudp"]); - if (unsolicitedUdp == 1) - { - header[0] ^= (byte)GameServerFlags.UnsolicitedUdpFlag; - } - } - } - /// - /// !when game create a channel chat, it will use both the public ip and private ip to build the name. - /// !Known game: Worm3d - /// - public static void CheckPrivateIP(List header, GameServerCache serverInfo) - { - if (Chat.Application.StorageOperation.Persistance.PeerGroupList.ContainsKey(serverInfo.GameName)) - { - // We already have the localip. Bytes are worng. - if (serverInfo.ServerData.ContainsKey("localip0")) - { - header[0] ^= (byte)GameServerFlags.PrivateIPFlag; - // there are multiple localip in dictionary we do not know which one is needed here, - // so we just send the first one. - byte[] bytesAddress = IPAddress.Parse(serverInfo.ServerData["localip0"]).GetAddressBytes(); - header.AddRange(bytesAddress); - } - } - } - public static void CheckNonStandardPort(List header, GameServerCache serverInfo) - { - // !! only dedicated server have different query report port and host port - // !! but peer server have same query report port and host port - // todo we have to check when we need send host port or query report port - if (serverInfo.QueryReportPort != ClientInfo.QueryReportDefaultPort) - { - header[0] ^= (byte)GameServerFlags.NonStandardPort; - byte[] htonPort = serverInfo.QueryReportPortBytes; - header.AddRange(htonPort); - } - } - /// - /// !disabled because crysiswars localport is 64100 which is larger than short integer, need to find whether this function is needed. - /// - public static void CheckNonStandardPrivatePort(List header, GameServerCache serverInfo) - { - // we check private port here - // if (serverInfo.ServerData.ContainsKey("localport")) - // { - // if (serverInfo.ServerData["localport"] != "" - // && serverInfo.ServerData["localport"] != ClientInfo.QueryReportDefaultPort.ToString()) - // { - // header[0] ^= (byte)GameServerFlags.NonStandardPrivatePortFlag; - // byte[] port = BitConverter.GetBytes(short.Parse(serverInfo.ServerData["localport"])); - // header.AddRange(port); - // } - // } - } - public static void CheckICMPSupport(List header, GameServerCache serverInfo) - { - if (serverInfo.ServerData.ContainsKey("icmp_address")) - { - header[0] ^= (byte)GameServerFlags.IcmpIpFlag; - byte[] bytesAddress = IPAddress.Parse(serverInfo.ServerData["icmp_address"]).GetAddressBytes(); - header.AddRange(bytesAddress); - } - } - - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Application/Client.cs b/src/Servers/ServerBrowser/src/V2/Application/Client.cs deleted file mode 100644 index 7b19f9930..000000000 --- a/src/Servers/ServerBrowser/src/V2/Application/Client.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.ServerBrowser.V2.Handler; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.ServerBrowser.V2.Aggregate.Misc; - -namespace UniSpy.Server.ServerBrowser.V2.Application -{ - public sealed class Client : ClientBase - { - public new ClientInfo Info { get => (ClientInfo)base.Info; set => base.Info = value; } - public Client(IConnection connection, IServer server) : base(connection, server) - { - // Crypto is init in ServerListHandler - Info = new ClientInfo(); - IsLogRaw = true; - } - - protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, (byte[])buffer); - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Application/ClientInfo.cs b/src/Servers/ServerBrowser/src/V2/Application/ClientInfo.cs deleted file mode 100644 index 000f20bbd..000000000 --- a/src/Servers/ServerBrowser/src/V2/Application/ClientInfo.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System; -using System.Linq; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.ServerBrowser.V2.Enumerate; - -namespace UniSpy.Server.ServerBrowser.V2.Application -{ - public sealed class ClientInfo : ClientInfoBase - { - public const string LocalIP0 = "localip0"; - public const string LocalIP1 = "localip1"; - public const string PrivatePort = "privateport"; - public const string HostPort = "hostport"; - public const string HostPortNumber = "6500"; - /// - /// we are not gamespy, - /// for simplicity we use hard coded challenge - /// to reduce computation cost of our program - /// - public const string ServerChallenge = "0000000000"; - public const ushort QueryReportDefaultPort = 6500; - public static byte[] HtonQueryReportDefaultPort => BitConverter.GetBytes(QueryReportDefaultPort).Reverse().ToArray(); - public string GameSecretKey { get; set; } - public string ClientChallenge { get; set; } - public ServerListUpdateOption? SearchType { get; set; } - /// - /// The game name that this client is searching for - /// - public string GameName { get; set; } - public ClientInfo() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Application/ClientManager.cs b/src/Servers/ServerBrowser/src/V2/Application/ClientManager.cs deleted file mode 100644 index 04103b8ba..000000000 --- a/src/Servers/ServerBrowser/src/V2/Application/ClientManager.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Net; -using UniSpy.Server.Core.Abstraction.BaseClass; -using System.Linq; -using System.Collections.Generic; - -namespace UniSpy.Server.ServerBrowser.V2.Application -{ - public class ClientManager : ClientManagerBase - { - public static List GetClient(string gameName) - { - return ClientPool.Values.Where(c=>(((Client)c).Info).GameName == gameName).Select(c=>((Client)c)).ToList(); - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Application/Server.cs b/src/Servers/ServerBrowser/src/V2/Application/Server.cs deleted file mode 100644 index d4e2dc53a..000000000 --- a/src/Servers/ServerBrowser/src/V2/Application/Server.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Net; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.Core.Network.Tcp.Server; - -namespace UniSpy.Server.ServerBrowser.V2.Application -{ - public sealed class Server : ServerBase - { - static Server() - { - _name = "ServerBrowserV2"; - } - public Server() { } - - public Server(IConnectionManager manager) : base(manager) { } - public override void Start() - { - QueryReport.V2.Application.StorageOperation.HeartbeatChannel.Subscribe(); - QueryReport.V2.Application.StorageOperation.HeartbeatChannel.OnReceived += ReceivedMessage; - base.Start(); - } - public void ReceivedMessage(QueryReport.V2.Aggregate.Redis.GameServer.GameServerCache message) - { - LogWriter.LogInfo($"Received game server message from QR:{message.ServerID}"); - var handler = new Handler.CmdHandler.AdHoc.AdHocHandler(message); - handler.Handle(); - } - protected override IClient CreateClient(IConnection connection) => new Client(connection, this); - - protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new TcpConnectionManager(endPoint); - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Contract/Request/AdHoc/PlayerSearchRequest.cs b/src/Servers/ServerBrowser/src/V2/Contract/Request/AdHoc/PlayerSearchRequest.cs deleted file mode 100755 index c5733e0fc..000000000 --- a/src/Servers/ServerBrowser/src/V2/Contract/Request/AdHoc/PlayerSearchRequest.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Linq; -using UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass; -using UniSpy.Server.Core.Encryption; - -namespace UniSpy.Server.ServerBrowser.V2.Contract.Request -{ - - public sealed class PlayerSearchRequest : RequestBase - { - public int SearchOption { get; private set; } - public new int CommandName => SearchOption; - public int MaxResults { get; private set; } - public string SearchName { get; private set; } - public string Message { get; private set; } - - - public PlayerSearchRequest(byte[] rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - SearchOption = Convert.ToInt16(RawRequest.Skip(3).Take(4).ToArray()); - MaxResults = Convert.ToInt16(RawRequest.Skip(7).Take(4).ToArray()); - - int nameLength = BitConverter.ToInt32(RawRequest.Skip(11).Take(4).ToArray()); - SearchName = UniSpyEncoding.GetString(RawRequest.Skip(15).Take(nameLength).ToArray()); - - int messageLength = BitConverter.ToInt32(RawRequest.Skip(15).Take(4).ToArray()); - Message = UniSpyEncoding.GetString(RawRequest.Skip(15 + nameLength + 4).Take(messageLength).ToArray()); - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Contract/Request/AdHoc/SendMsgRequest.cs b/src/Servers/ServerBrowser/src/V2/Contract/Request/AdHoc/SendMsgRequest.cs deleted file mode 100644 index 0459495b2..000000000 --- a/src/Servers/ServerBrowser/src/V2/Contract/Request/AdHoc/SendMsgRequest.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Linq; -using UniSpy.Server.ServerBrowser.V2.Abstraction; - -namespace UniSpy.Server.ServerBrowser.V2.Contract.Request -{ - - - public class SendMsgRequest : AdHocRequestBase - { - public byte[] PrefixMessage { get; private set; } - public byte[] ClientMessage { get; private set; } - public SendMsgRequest(byte[] rawRequest) : base(rawRequest) - { - } - public override void Parse() - { - base.Parse(); - ClientMessage = RawRequest.Skip(9).ToArray(); - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Contract/Request/AdHoc/ServerInfoRequest.cs b/src/Servers/ServerBrowser/src/V2/Contract/Request/AdHoc/ServerInfoRequest.cs deleted file mode 100644 index 3bc0c7b20..000000000 --- a/src/Servers/ServerBrowser/src/V2/Contract/Request/AdHoc/ServerInfoRequest.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.ServerBrowser.V2.Abstraction; - -namespace UniSpy.Server.ServerBrowser.V2.Contract.Request -{ - - public class ServerInfoRequest : AdHocRequestBase - { - public ServerInfoRequest(byte[] rawRequest) : base(rawRequest) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Contract/Request/ServerList/ServerListRequest.cs b/src/Servers/ServerBrowser/src/V2/Contract/Request/ServerList/ServerListRequest.cs deleted file mode 100755 index a64b861e9..000000000 --- a/src/Servers/ServerBrowser/src/V2/Contract/Request/ServerList/ServerListRequest.cs +++ /dev/null @@ -1,76 +0,0 @@ -using System; -using System.Linq; -using UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass; -using UniSpy.Server.ServerBrowser.V2.Enumerate; - -using UniSpy.Server.Core.Encryption; - -namespace UniSpy.Server.ServerBrowser.V2.Contract.Request -{ - /// - /// ServerList also called ServerRule in GameSpy SDK - /// - - public sealed class ServerListRequest : ServerListUpdateOptionRequestBase - { - public ServerListRequest(byte[] rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - CommandName = RequestType.ServerListRequest; - // if (RequestLength != RawRequest.Length) - // { - // throw new ServerBrowser.Exception("Server list request length is not correct."); - // } - - RequestVersion = RawRequest[2]; - ProtocolVersion = RawRequest[3]; - EncodingVersion = RawRequest[4]; - GameVersion = BitConverter.ToInt32(RawRequest.Skip(5).Take(4).ToArray()); - - //because there are empty string we can not use StringSplitOptions.RemoveEmptyEntries - var remainData = RawRequest.Skip(9).ToList(); - var devGameNameIndex = remainData.FindIndex(x => x == 0); - DevGameName = UniSpyEncoding.GetString(remainData.Take(devGameNameIndex).ToArray()); - remainData = remainData.Skip(devGameNameIndex + 1).ToList(); - var gameNameIndex = remainData.FindIndex(x => x == 0); - GameName = UniSpyEncoding.GetString(remainData.Take(gameNameIndex).ToArray()); - remainData = remainData.Skip(gameNameIndex + 1).ToList(); - // client challenge length is 8 - ClientChallenge = UniSpyEncoding.GetString(remainData.Take(8).ToArray()); - remainData = remainData.Skip(8).ToList(); - - var filterIndex = remainData.FindIndex(x => x == 0); - if (filterIndex > 0) - { - Filter = UniSpyEncoding.GetString(remainData.Take(filterIndex).ToArray()); - } - remainData = remainData.Skip(filterIndex + 1).ToList(); - - var keysIndex = remainData.FindIndex(x => x == 0); - Keys = UniSpyEncoding.GetString(remainData.Take(keysIndex).ToArray()).Split("\\", StringSplitOptions.RemoveEmptyEntries); - remainData = remainData.Skip(keysIndex + 1).ToList(); - //gamespy send this in big endian, we need to convert to little endian - byte[] byteUpdateOptions = remainData.Take(4).Reverse().ToArray(); - UpdateOption = (ServerListUpdateOption)BitConverter.ToInt32(byteUpdateOptions); - remainData = remainData.Skip(4).ToList(); - if ((UpdateOption & ServerListUpdateOption.AlternateSourceIP) != 0) - { - SourceIP = new System.Net.IPAddress(remainData.Take(4).ToArray()); - remainData = remainData.Skip(7).ToList(); - } - - if ((UpdateOption & ServerListUpdateOption.LimitResultCount) != 0) - { - if (remainData.Count != 4) - { - throw new ServerBrowser.Exception("The max number of server is incorrect."); - } - MaxServers = BitConverter.ToInt32(remainData.Take(4).Reverse().ToArray()); - } - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Contract/Response/AdHoc/DeleteServerInfoResponse.cs b/src/Servers/ServerBrowser/src/V2/Contract/Response/AdHoc/DeleteServerInfoResponse.cs deleted file mode 100644 index ec80ff446..000000000 --- a/src/Servers/ServerBrowser/src/V2/Contract/Response/AdHoc/DeleteServerInfoResponse.cs +++ /dev/null @@ -1,21 +0,0 @@ -using UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass; -using UniSpy.Server.ServerBrowser.V2.Contract.Result; -using UniSpy.Server.ServerBrowser.V2.Enumerate; - -namespace UniSpy.Server.ServerBrowser.V2.Contract.Response.AdHoc -{ - public sealed class DeleteServerInfoResponse : AdHocResponseBase - { - public DeleteServerInfoResponse(AdHocResult result) : base(result) - { - } - - public override void Build() - { - _buffer.Add((byte)ResponseType.DeleteServerMessage); - _buffer.AddRange(_result.GameServerInfo.HostIPAddress.GetAddressBytes()); - _buffer.AddRange(_result.GameServerInfo.QueryReportPortBytes); - SendingBuffer = _buffer.ToArray(); - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Contract/Response/AdHoc/UpdateServerInfoResponse.cs b/src/Servers/ServerBrowser/src/V2/Contract/Response/AdHoc/UpdateServerInfoResponse.cs deleted file mode 100755 index 0c0d511ef..000000000 --- a/src/Servers/ServerBrowser/src/V2/Contract/Response/AdHoc/UpdateServerInfoResponse.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System; -using System.Linq; -using UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass; -using UniSpy.Server.ServerBrowser.V2.Enumerate; -using UniSpy.Server.ServerBrowser.V2.Aggregate.Misc; -using UniSpy.Server.ServerBrowser.V2.Contract.Result; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.ServerBrowser.V2.Aggregate; - -namespace UniSpy.Server.ServerBrowser.V2.Contract.Response -{ - /// - /// Get a server's information - /// - public sealed class UpdateServerInfoResponse : AdHocResponseBase - { - public UpdateServerInfoResponse(AdHocResult result) : base(result) - { - } - - public override void Build() - { - lock (_buffer) - { - _buffer.Add((byte)ResponseType.PushServerMessage); - BuildSingleServerFullInfo(); - // add message length here - var msgLength = BitConverter.GetBytes((ushort)(_buffer.Count + 2)).Reverse().ToArray(); - // we add the message length at the start - _buffer.InsertRange(0, msgLength); - } - SendingBuffer = _buffer.ToArray(); - } - - private void BuildSingleServerFullInfo() - { - var header = ServerInfoBuilder.BuildServerInfoHeader( - GameServerFlags.HasFullRulesFlag, - _result.GameServerInfo); - _buffer.AddRange(header); - if (_result.GameServerInfo.ServerData is not null) - { - foreach (var kv in _result.GameServerInfo.ServerData) - { - _buffer.AddRange(UniSpyEncoding.GetBytes(kv.Key)); - _buffer.Add(StringFlag.StringSpliter); - _buffer.AddRange(UniSpyEncoding.GetBytes(kv.Value)); - _buffer.Add(StringFlag.StringSpliter); - } - } - if (_result.GameServerInfo.PlayerData is not null) - { - foreach (var player in _result.GameServerInfo.PlayerData) - { - foreach (var kv in player) - { - _buffer.AddRange(UniSpyEncoding.GetBytes(kv.Key)); - _buffer.Add(StringFlag.StringSpliter); - _buffer.AddRange(UniSpyEncoding.GetBytes(kv.Value)); - _buffer.Add(StringFlag.StringSpliter); - } - } - } - if (_result.GameServerInfo.TeamData is not null) - { - foreach (var team in _result.GameServerInfo.TeamData) - { - foreach (var kv in team) - { - _buffer.AddRange(UniSpyEncoding.GetBytes(kv.Key)); - _buffer.Add(StringFlag.StringSpliter); - _buffer.AddRange(UniSpyEncoding.GetBytes(kv.Value)); - _buffer.Add(StringFlag.StringSpliter); - } - } - } - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Contract/Response/ServerList/P2PGroupRoomListResponse.cs b/src/Servers/ServerBrowser/src/V2/Contract/Response/ServerList/P2PGroupRoomListResponse.cs deleted file mode 100755 index 626acccbe..000000000 --- a/src/Servers/ServerBrowser/src/V2/Contract/Response/ServerList/P2PGroupRoomListResponse.cs +++ /dev/null @@ -1,56 +0,0 @@ -using System; -using System.Linq; -using UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass; -using UniSpy.Server.ServerBrowser.V2.Enumerate; -using UniSpy.Server.ServerBrowser.V2.Aggregate.Misc; -using UniSpy.Server.ServerBrowser.V2.Contract.Request; -using UniSpy.Server.ServerBrowser.V2.Contract.Result; -using UniSpy.Server.Core.Encryption; - -namespace UniSpy.Server.ServerBrowser.V2.Contract.Response.ServerList -{ - public sealed class P2PGroupRoomListResponse : ServerListUpdateOptionResponseBase - { - private new ServerListRequest _request => (ServerListRequest)base._request; - private new P2PGroupRoomListResult _result => (P2PGroupRoomListResult)base._result; - public P2PGroupRoomListResponse(ServerListUpdateOptionRequestBase request, ServerListUpdateOptionResultBase result) : base(request, result) - { - } - public override void Build() - { - base.Build(); - // we add the server keys - BuildServerKeys(); - //we use NTS string so total unique value list is 0 - BuildUniqueValue(); - //add server infomation such as public ip etc. - BuildServersFullInfo(); - SendingBuffer = _serversInfoBuffer.ToArray(); - } - - protected override void BuildServersFullInfo() - { - foreach (var room in _result.PeerRoomsInfo) - { - //add has key flag - _serversInfoBuffer.Add((byte)GameServerFlags.HasKeysFlag); - //in group list server ip is group id - - var groupIdBytes = BitConverter.GetBytes((int)room.GroupId).Reverse().ToArray(); - _serversInfoBuffer.AddRange(groupIdBytes); - - foreach (var key in _request.Keys) - { - _serversInfoBuffer.Add(StringFlag.NTSStringFlag); - var value = room.KeyValues.ContainsKey(key) ? room.KeyValues[key] : ""; - // if key is uint or int, we need first convert to ASCII string then get bytes - _serversInfoBuffer.AddRange(UniSpyEncoding.GetBytes(value.ToString())); - _serversInfoBuffer.Add(StringFlag.StringSpliter); - } - } - // group id = 0 means the end flag of group list - var endFlag = BitConverter.GetBytes((int)0); - _serversInfoBuffer.AddRange(endFlag); - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Contract/Response/ServerList/ServerMainListResponse.cs b/src/Servers/ServerBrowser/src/V2/Contract/Response/ServerList/ServerMainListResponse.cs deleted file mode 100755 index 164da16d7..000000000 --- a/src/Servers/ServerBrowser/src/V2/Contract/Response/ServerList/ServerMainListResponse.cs +++ /dev/null @@ -1,53 +0,0 @@ -using UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass; -using UniSpy.Server.ServerBrowser.V2.Aggregate.Misc; -using UniSpy.Server.ServerBrowser.V2.Contract.Request; -using UniSpy.Server.ServerBrowser.V2.Contract.Result; -using UniSpy.Server.Core.Encryption; - -namespace UniSpy.Server.ServerBrowser.V2.Aggregate.Packet.Response -{ - public sealed class ServerMainListResponse : ServerListUpdateOptionResponseBase - { - private new ServerListRequest _request => (ServerListRequest)base._request; - private new ServerMainListResult _result => (ServerMainListResult)base._result; - public ServerMainListResponse(ServerListUpdateOptionRequestBase request, ServerListUpdateOptionResultBase result) : base(request, result) - { - } - - public override void Build() - { - // we add the other header - base.Build(); - // we add the server keys - BuildServerKeys(); - //we use NTS string so total unique value list is 0 - BuildUniqueValue(); - //add server infomation such as public ip etc. - BuildServersFullInfo(); - - SendingBuffer = _serversInfoBuffer.ToArray(); - } - - protected override void BuildServersFullInfo() - { - foreach (var serverInfo in _result.GameServerInfos) - { - var header = ServerInfoBuilder.BuildServerInfoHeader(_result.Flag, serverInfo); - _serversInfoBuffer.AddRange(header); - foreach (var key in _request.Keys) - { - _serversInfoBuffer.Add(StringFlag.NTSStringFlag); - // if the key is in our database, we just add it - // otherwise we leave it empty, game will set default value - if (serverInfo.ServerData.ContainsKey(key)) - { - _serversInfoBuffer.AddRange(UniSpyEncoding.GetBytes(serverInfo.ServerData[key])); - } - _serversInfoBuffer.Add(StringFlag.StringSpliter); - } - } - //after all server information is added we add the end flag - _serversInfoBuffer.AddRange(StringFlag.AllServerEndFlag); - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Contract/Response/ServerList/ServerNetworkInfoListResponse.cs b/src/Servers/ServerBrowser/src/V2/Contract/Response/ServerList/ServerNetworkInfoListResponse.cs deleted file mode 100755 index d9a3b8d69..000000000 --- a/src/Servers/ServerBrowser/src/V2/Contract/Response/ServerList/ServerNetworkInfoListResponse.cs +++ /dev/null @@ -1,63 +0,0 @@ -using UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass; -using UniSpy.Server.ServerBrowser.V2.Enumerate; -using UniSpy.Server.ServerBrowser.V2.Aggregate.Misc; -using UniSpy.Server.ServerBrowser.V2.Contract.Result.ServerList; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.ServerBrowser.V2.Aggregate; - -namespace UniSpy.Server.ServerBrowser.V2.Contract.Response.ServerList -{ - /// - /// Get all server's full information - /// - public sealed class ServerNetworkInfoListResponse : ServerListUpdateOptionResponseBase - { - private new ServerNetworkInfoListResult _result => (ServerNetworkInfoListResult)base._result; - public ServerNetworkInfoListResponse(ServerListUpdateOptionRequestBase request, ServerListUpdateOptionResultBase result) : base(request, result) - { - } - public override void Build() - { - base.Build(); - SendingBuffer = _serversInfoBuffer.ToArray(); - } - protected override void BuildServersFullInfo() - { - foreach (var server in _result.ServersInfo) - { - var header = ServerInfoBuilder.BuildServerInfoHeader( - GameServerFlags.HasFullRulesFlag, - server); - _serversInfoBuffer.AddRange(header); - - foreach (var kv in server.ServerData) - { - _serversInfoBuffer.AddRange(UniSpyEncoding.GetBytes(kv.Key)); - _serversInfoBuffer.Add(StringFlag.StringSpliter); - _serversInfoBuffer.AddRange(UniSpyEncoding.GetBytes(kv.Value)); - _serversInfoBuffer.Add(StringFlag.StringSpliter); - } - foreach (var player in server.PlayerData) - { - foreach (var kv in player) - { - _serversInfoBuffer.AddRange(UniSpyEncoding.GetBytes(kv.Key)); - _serversInfoBuffer.Add(StringFlag.StringSpliter); - _serversInfoBuffer.AddRange(UniSpyEncoding.GetBytes(kv.Value)); - _serversInfoBuffer.Add(StringFlag.StringSpliter); - } - } - foreach (var team in server.TeamData) - { - foreach (var kv in team) - { - _serversInfoBuffer.AddRange(UniSpyEncoding.GetBytes(kv.Key)); - _serversInfoBuffer.Add(StringFlag.StringSpliter); - _serversInfoBuffer.AddRange(UniSpyEncoding.GetBytes(kv.Value)); - _serversInfoBuffer.Add(StringFlag.StringSpliter); - } - } - } - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Contract/Result/AdHoc/AdHocResult.cs b/src/Servers/ServerBrowser/src/V2/Contract/Result/AdHoc/AdHocResult.cs deleted file mode 100755 index 1bf9ed50c..000000000 --- a/src/Servers/ServerBrowser/src/V2/Contract/Result/AdHoc/AdHocResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.QueryReport.V2.Aggregate.Redis.GameServer; -using UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass; - -namespace UniSpy.Server.ServerBrowser.V2.Contract.Result -{ - public sealed class AdHocResult : ResultBase - { - public GameServerCache GameServerInfo { get; set; } - public AdHocResult() - { - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/P2PGroupRoomListResult.cs b/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/P2PGroupRoomListResult.cs deleted file mode 100755 index 8389e9e19..000000000 --- a/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/P2PGroupRoomListResult.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.QueryReport.Aggregate.Redis.PeerGroup; -using UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass; - -namespace UniSpy.Server.ServerBrowser.V2.Contract.Result -{ - public sealed class P2PGroupRoomListResult : ServerListUpdateOptionResultBase - { - public List PeerRoomsInfo { get; set; } - public P2PGroupRoomListResult() - { - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/ServerMainListResult.cs b/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/ServerMainListResult.cs deleted file mode 100755 index a084f922b..000000000 --- a/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/ServerMainListResult.cs +++ /dev/null @@ -1,15 +0,0 @@ -using UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass; -using System.Collections.Generic; -using UniSpy.Server.QueryReport.V2.Aggregate.Redis.GameServer; - -namespace UniSpy.Server.ServerBrowser.V2.Contract.Result -{ - public class ServerMainListResult : ServerListUpdateOptionResultBase - { - public List GameServerInfos { get; set; } - public ServerMainListResult() - { - GameServerInfos = new List(); - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/ServerNetworkInfoListResult.cs b/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/ServerNetworkInfoListResult.cs deleted file mode 100755 index 31b4d5c00..000000000 --- a/src/Servers/ServerBrowser/src/V2/Contract/Result/ServerList/ServerNetworkInfoListResult.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.QueryReport.V2.Aggregate.Redis.GameServer; - -namespace UniSpy.Server.ServerBrowser.V2.Contract.Result.ServerList -{ - - public sealed class ServerNetworkInfoListResult : ServerMainListResult - { - public List ServersInfo { get; private set; } - public ServerNetworkInfoListResult() - { - ServersInfo = new List(); - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Enumerate/GeneralEnum.cs b/src/Servers/ServerBrowser/src/V2/Enumerate/GeneralEnum.cs deleted file mode 100755 index 31d0d87e8..000000000 --- a/src/Servers/ServerBrowser/src/V2/Enumerate/GeneralEnum.cs +++ /dev/null @@ -1,24 +0,0 @@ -namespace UniSpy.Server.ServerBrowser.V2.Enumerate -{ - public enum PlayerSearchOptions - { - SearchAllGames = 1, - SearchLeftSubstring = 2, - SearchRightSubString = 4, - SearchAnySubString = 8, - } - - public enum QueryType - { - Basic, - Full, - ICMP - } - - public enum DataKeyType - { - String, - Byte, - Short - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Enumerate/ServerListEnum.cs b/src/Servers/ServerBrowser/src/V2/Enumerate/ServerListEnum.cs deleted file mode 100755 index 4a0930d06..000000000 --- a/src/Servers/ServerBrowser/src/V2/Enumerate/ServerListEnum.cs +++ /dev/null @@ -1,85 +0,0 @@ -namespace UniSpy.Server.ServerBrowser.V2.Enumerate -{ - public enum RequestType : byte - { - ServerListRequest, - ServerInfoRequest, - SendMessageRequest, - KeepAliveReply, - MapLoopRequest, - PlayerSearchRequest - } - - public enum ResponseType - { - PushKeysMessage = 1, - PushServerMessage = 2, - KeepAliveMessage = 3, - DeleteServerMessage = 4, - MapLoopMessage = 5, - PlayerSearchMessage = 6 - } - - public enum ProtocolVersion - { - ListProtocolVersion1 = 0, - ListEncodingVersion = 3 - } - - public enum ServerListUpdateOption - { - /// - /// This is used to tell server browser client want main server list (keys and values) - /// - ServerMainList = 0, - SendFieldForAll = 1, - /// - /// get the full information of all servers - /// - ServerFullInfoList = 2, - /// - /// Push new local game status to QueryReport server - /// - P2PServerMainList = 4, - AlternateSourceIP = 8, - P2PGroupRoomList = 32, - NoListCache = 64, - LimitResultCount = 128 - } - - public enum GameServerFlags - { - /// - /// game can directly send request to dedicate server - /// - UnsolicitedUdpFlag = 1, - /// - /// private ip exist - /// - PrivateIPFlag = 2, - /// - /// connect with nat neg - /// - ConnectNegotiateFlag = 4, - /// - /// server has icmp - /// - IcmpIpFlag = 8, - /// - /// non standard query port - /// - NonStandardPort = 16, - /// - /// nonstandard private port - /// - NonStandardPrivatePortFlag = 32, - /// - /// has standard keys - /// - HasKeysFlag = 64, - /// - /// has full rules keys - /// - HasFullRulesFlag = 128 - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/AdHoc/AdHocHandler.cs b/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/AdHoc/AdHocHandler.cs deleted file mode 100644 index 184d448b8..000000000 --- a/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/AdHoc/AdHocHandler.cs +++ /dev/null @@ -1,52 +0,0 @@ -using System.Threading.Tasks; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.QueryReport.V2.Aggregate.Redis.GameServer; -using UniSpy.Server.ServerBrowser.V2.Application; -using UniSpy.Server.ServerBrowser.V2.Contract.Response; -using UniSpy.Server.ServerBrowser.V2.Contract.Response.AdHoc; -using UniSpy.Server.ServerBrowser.V2.Contract.Result; -using UniSpy.Server.ServerBrowser.V2.Enumerate; - -namespace UniSpy.Server.ServerBrowser.V2.Handler.CmdHandler.AdHoc -{ - public class AdHocHandler : IHandler - { - public GameServerCache _message; - public AdHocHandler(GameServerCache message) - { - LogWriter.LogCurrentClass(this); - _message = message; - } - - public void Handle() - { - IResponse response = null; - var result = new AdHocResult() { GameServerInfo = _message }; - switch (_message.ServerStatus) - { - case QueryReport.V2.Enumerate.GameServerStatus.Normal: - case QueryReport.V2.Enumerate.GameServerStatus.Update: - case QueryReport.V2.Enumerate.GameServerStatus.Playing: - response = new UpdateServerInfoResponse(result); - break; - case QueryReport.V2.Enumerate.GameServerStatus.Shutdown: - response = new DeleteServerInfoResponse(result); - break; - } - - var clients = ClientManager.GetClient(_message.GameName); - Parallel.ForEach(clients, client => - { - if (client.Info.GameName == _message.GameName - && client.Crypto is not null - && (client.Info.SearchType == ServerListUpdateOption.ServerMainList - || client.Info.SearchType == ServerListUpdateOption.P2PServerMainList)) - { - client.LogInfo($"Sending AdHoc message {_message.ServerStatus} to client"); - client.Send(response); - } - }); - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/AdHoc/SendMsgHandler.cs b/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/AdHoc/SendMsgHandler.cs deleted file mode 100755 index b67a31c78..000000000 --- a/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/AdHoc/SendMsgHandler.cs +++ /dev/null @@ -1,42 +0,0 @@ -using UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass; -using UniSpy.Server.ServerBrowser.V2.Contract.Request; -using UniSpy.Server.QueryReport.V2.Contract.Request; -using UniSpy.Server.Core.Extension; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.ServerBrowser.V2.Application; - -namespace UniSpy.Server.ServerBrowser.V2.Handler.CmdHandler -{ - /// - /// Natneg message maybe incompelete - /// when debugging sdk the natneg message will split to 2 request - /// we have to save first message then wait for next message - /// - public sealed class SendMsgHandler : CmdHandlerBase - { - private new SendMsgRequest _request => (SendMsgRequest)base._request; - public SendMsgHandler(Client client, SendMsgRequest request) : base(client, request) - { - } - protected override void DataOperation() - { - var gameServer = QueryReport.V2.Application.StorageOperation.Persistance.GetGameServerInfo(_request.GameServerPublicIPEndPoint); - - if (gameServer is null) - { - throw new ServerBrowser.Exception($"No match server found by address {_request.GameServerPublicIPEndPoint}, we ignore client request."); - } - - var message = new ClientMessageRequest() - { - ServerBrowserSenderId = _client.Server.Id, - NatNegMessage = _request.ClientMessage, - InstantKey = gameServer.InstantKey, - TargetIPEndPoint = gameServer.QueryReportIPEndPoint, - CommandName = QueryReport.V2.Enumerate.RequestType.ClientMessage - }; - QueryReport.V2.Application.StorageOperation.Persistance.PublishClientMessage(message); - _client.LogInfo($"Send client message to QueryReport Server: {gameServer.ServerID} [{StringExtensions.ConvertByteToHexString(message.NatNegMessage)}]"); - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/AdHoc/ServerInfoHandler.cs b/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/AdHoc/ServerInfoHandler.cs deleted file mode 100755 index 19fea349a..000000000 --- a/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/AdHoc/ServerInfoHandler.cs +++ /dev/null @@ -1,47 +0,0 @@ -using UniSpy.Server.ServerBrowser.V2.Application; -using UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass; -using UniSpy.Server.ServerBrowser.V2.Contract.Request; -using UniSpy.Server.ServerBrowser.V2.Contract.Response; -using UniSpy.Server.ServerBrowser.V2.Contract.Result; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.ServerBrowser.V2.Handler.CmdHandler -{ - /// - /// Get full rules for a server (for example, to get - /// player information from a server that only has basic information so far) - /// - - public sealed class ServerInfoHandler : CmdHandlerBase - { - private new ServerInfoRequest _request => (ServerInfoRequest)base._request; - private new AdHocResult _result { get => (AdHocResult)base._result; set => base._result = value; } - - public ServerInfoHandler(Client client, ServerInfoRequest request) : base(client, request) - { - _result = new AdHocResult(); - } - - protected override void DataOperation() - { - _result.GameServerInfo = QueryReport.V2.Application.StorageOperation.Persistance.GetGameServerInfo(_request.GameServerPublicIPEndPoint); - - //TODO if there are no server found, we still send response back to client - if (_result.GameServerInfo is null) - { - // throw new ServerBrowser.Exception("No server found in database."); - _client.LogInfo($"No server found on IP {_request.GameServerPublicIPEndPoint}."); - return; - } - } - - protected override void ResponseConstruct() - { - if (_result.GameServerInfo is null) - { - return; - } - _response = new UpdateServerInfoResponse(_result); - } - } -} diff --git a/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/ServerList/ServerListHandler.cs b/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/ServerList/ServerListHandler.cs deleted file mode 100644 index be6541cae..000000000 --- a/src/Servers/ServerBrowser/src/V2/Handler/CmdHandler/ServerList/ServerListHandler.cs +++ /dev/null @@ -1,153 +0,0 @@ -using System.Linq; -using UniSpy.Server.ServerBrowser.V2.Application; -using UniSpy.Server.ServerBrowser.V2.Abstraction.BaseClass; -using UniSpy.Server.ServerBrowser.V2.Enumerate; -using UniSpy.Server.ServerBrowser.V2.Aggregate.Packet.Response; -using UniSpy.Server.ServerBrowser.V2.Contract.Response.ServerList; -using UniSpy.Server.ServerBrowser.V2.Contract.Result; -using UniSpy.Server.ServerBrowser.V2.Contract.Request; -using UniSpy.Server.QueryReport.Aggregate.Redis.PeerGroup; -using System.Collections.Generic; - -namespace UniSpy.Server.ServerBrowser.V2.Handler.CmdHandler -{ - - public class ServerListHandler : ServerListUpdateOptionHandlerBase - { - public ServerListHandler(Client client, ServerListRequest request) : base(client, request) - { - } - protected override void RequestCheck() - { - base.RequestCheck(); - switch (_request.UpdateOption) - { - case ServerListUpdateOption.ServerMainList: - case ServerListUpdateOption.P2PServerMainList: - case ServerListUpdateOption.LimitResultCount: - case ServerListUpdateOption.ServerFullInfoList: - _result = new ServerMainListResult(); - break; - case ServerListUpdateOption.P2PGroupRoomList: - _result = new P2PGroupRoomListResult(); - break; - default: - throw new ServerBrowser.Exception("unknown serverlist update option type"); - } - } - protected override void DataOperation() - { - _result.GameSecretKey = _client.Info.GameSecretKey; - _result.ClientRemoteIP = _client.Connection.RemoteIPEndPoint.Address.GetAddressBytes(); - //todo check protocol version!!!! - switch (_request.UpdateOption) - { - case ServerListUpdateOption.ServerMainList: - ServerMainList(); - break; - case ServerListUpdateOption.P2PServerMainList: - case ServerListUpdateOption.LimitResultCount: - P2PServerMainList(); - break; - case ServerListUpdateOption.P2PGroupRoomList: - P2PGroupRoomList(); - break; - case ServerListUpdateOption.ServerFullInfoList: - // do nothing here - break; - default: - throw new ServerBrowser.Exception("unknown serverlist update option type"); - } - _client.Info.SearchType = _request.UpdateOption; - - } - private void P2PGroupRoomList() - { - - // first get the peer room in memory, if there is no such game we do not continue - if (!Chat.Application.StorageOperation.Persistance.PeerGroupList.ContainsKey(_request.GameName)) - { - throw new ServerBrowser.Exception($"Invalid game name: {_request.GameName}."); - } - - // Game name is unique in redis database - var grouplist = Chat.Application.StorageOperation.Persistance.PeerGroupList[_request.GameName]; - // we do not create peer room cache on redis, we just send peer room info to client - var tempInfos = new List(); - foreach (var group in grouplist) - { - // we create room info, set the room properties to default - var roomInfo = new PeerRoomInfo(group.Game.Gamename, group.Groupid, group.Roomname); - tempInfos.Add(roomInfo); - // get the channels info from redis where groupid equals above - var groupRooms = QueryReport.Application.StorageOperation.Persistance.GetPeerGroupChannel(group.Groupid); - // get the channels info from redis where created under gamename and groupid above - var stagingRooms = QueryReport.Application.StorageOperation.Persistance.GetPeerStagingChannel(group.Game.Gamename, group.Groupid); - if (groupRooms.Count != 0) - { - roomInfo.NumberOfWaitingPlayers = groupRooms.Sum(r => r.Users.Count); - } - if (stagingRooms.Count != 0) - { - roomInfo.NumberOfPlayingPlayers = stagingRooms.Sum(r => r.Users.Count); - roomInfo.NumberOfGames = stagingRooms.Count; - } - roomInfo.NumberOfPlayers = roomInfo.NumberOfWaitingPlayers + roomInfo.NumberOfPlayingPlayers; - // if there did not have any rooms in redis, the properties in roominfo stay default - } - ((P2PGroupRoomListResult)_result).PeerRoomsInfo = tempInfos; - } - private void P2PServerMainList() - { - var serverInfos = QueryReport.V2.Application.StorageOperation.Persistance.GetGameServerInfos(_request.GameName); - ((ServerMainListResult)_result).Flag = GameServerFlags.HasKeysFlag; - var filteredGameServerInfos = new List(); - //TODO do filter - if (_request.Filter is not null) - { - if (_request.Filter.Contains("groupid=")) - { - var groupId = _request.Filter.Replace("groupid=", ""); - filteredGameServerInfos = serverInfos.Where(s => s.ServerData.ContainsKey("groupid=")).Where(s => s.ServerData["groupid"] == groupId).ToList(); - ((ServerMainListResult)_result).GameServerInfos = filteredGameServerInfos; - } - else - { - filteredGameServerInfos = serverInfos; - } - } - else - { - filteredGameServerInfos = serverInfos; - } - - ((ServerMainListResult)_result).GameServerInfos = filteredGameServerInfos; - } - private void ServerMainList() - { - var serverInfos = QueryReport.V2.Application.StorageOperation.Persistance.GetGameServerInfos(_request.GameName); - ((ServerMainListResult)_result).GameServerInfos = serverInfos; - ((ServerMainListResult)_result).Flag = GameServerFlags.HasKeysFlag; - } - - protected override void ResponseConstruct() - { - switch (_request.UpdateOption) - { - case ServerListUpdateOption.ServerMainList: - case ServerListUpdateOption.P2PServerMainList: - case ServerListUpdateOption.LimitResultCount: - _response = new ServerMainListResponse(_request, _result); - break; - case ServerListUpdateOption.P2PGroupRoomList: - _response = new P2PGroupRoomListResponse(_request, _result); - break; - case ServerListUpdateOption.ServerFullInfoList: - _response = new ServerNetworkInfoListResponse(_request, _result); - break; - default: - throw new ServerBrowser.Exception("unknown serverlist update option type"); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/src/V2/Handler/CmdSwitcher.cs b/src/Servers/ServerBrowser/src/V2/Handler/CmdSwitcher.cs deleted file mode 100755 index 3a1480962..000000000 --- a/src/Servers/ServerBrowser/src/V2/Handler/CmdSwitcher.cs +++ /dev/null @@ -1,40 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.ServerBrowser.V2.Enumerate; -using UniSpy.Server.ServerBrowser.V2.Contract.Request; -using UniSpy.Server.ServerBrowser.V2.Handler.CmdHandler; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.ServerBrowser.V2.Application; - -namespace UniSpy.Server.ServerBrowser.V2.Handler -{ - public sealed class CmdSwitcher : CmdSwitcherBase - { - private new byte[] _rawRequest => (byte[])base._rawRequest; - private new Client _client => (Client)base._client; - public CmdSwitcher(Client client, byte[] rawRequest) : base(client, rawRequest) - { - } - protected override void ProcessRawRequest() - { - var name = (RequestType)_rawRequest[2]; - _requests.Add(new KeyValuePair(name, _rawRequest)); - } - - protected override IHandler CreateCmdHandlers(object name, object rawRequest) - { - var req = (byte[])rawRequest; - switch ((RequestType)name) - { - case RequestType.ServerListRequest: - return new ServerListHandler(_client, new ServerListRequest(req)); - case RequestType.ServerInfoRequest: - return new ServerInfoHandler(_client, new ServerInfoRequest(req)); - case RequestType.SendMessageRequest: - return new SendMsgHandler(_client, new SendMsgRequest(req)); - default: - return null; - } - } - } -} diff --git a/src/Servers/ServerBrowser/test/UniSpy.Server.ServerBrowser.Test.csproj b/src/Servers/ServerBrowser/test/UniSpy.Server.ServerBrowser.Test.csproj deleted file mode 100644 index b69d73c28..000000000 --- a/src/Servers/ServerBrowser/test/UniSpy.Server.ServerBrowser.Test.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - - net6.0 - false - ..\..\..\..\common\Icon\UniSpy_Logo.ico - - - AnyCPU - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - - \ No newline at end of file diff --git a/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs b/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs deleted file mode 100644 index af1ce959b..000000000 --- a/src/Servers/ServerBrowser/test/V1/EnctypeTest.cs +++ /dev/null @@ -1,180 +0,0 @@ -using System.Text; -using System; -using System.Linq; -using UniSpy.Server.ServerBrowser.V1.Abstraction.BaseClass; -using UniSpy.Server.ServerBrowser.V1.Aggregate; -using Xunit; -using UniSpy.Server.Core.Encryption; - -public class EnctypeTest -{ - private IEnctypeShareTest encShare = new Enctype2("abcdef"); - [Fact] - public void EncShare2Test() - { - uint[] tbuff = Enumerable.Repeat(1, 326).ToArray(); - var tbuffUint = Array.ConvertAll(tbuff, Convert.ToUInt32); - var tbuffpUint = tbuffUint.ToArray(); - encShare.Encshare2(tbuffUint, 309, 6); - var correctResult = new uint[] - { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 17498629, 67830052, 39783468, 510855193, 366629229, 3551672311, 2479564971, 2781774699, 4118137534, 3094373108, 829124753, 3611234592, 1213911675, 3241236538, 3419395059, 3, 17236227, 34013461, 20580121, 306578602, 230830554, 2185634525, 1540642169, 2099507104, 1913332720, 3987761791, 3463819041, 860209457, 3948159327, 2584192363, 2377682576, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 32775, 32774, 32772, 12, 2389626517, 2938163061, 4097, 1, 1549944694, 2745022601, 3181633273, 1113334022, 2827919296, 1467047999, 1485013325, 2809953970, 4233124407, 61842888, 2999413561, 1295553734, 4062552591, 232414704, 3507545467, 787421828, 1 }; - Assert.True(correctResult.SequenceEqual(correctResult)); - } - - [Fact] - public void EncShare1Test() - { - uint[] sbox = Enumerable.Repeat(1, 326).ToArray(); - byte[] data = EnctypeBase.ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); - encShare.Encshare1(sbox, data, 0, 16); - uint[] tbuffCorrect = new uint[] { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 5, 17498629, 67830052, 39783468, 510855193, 366629229, 3551672311, 2479564971, 2781774699, 4118137534, 3094373108, 829124753, 3611234592, 1213911675, 3241236538, 3419395059, 3, 17236227, 34013461, 20580121, 306578602, 230830554, 2185634525, 1540642169, 2099507104, 1913332720, 3987761791, 3463819041, 860209457, 3948159327, 2584192363, 2377682576, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 32775, 32774, 32772, 12, 2389626517, 2938163061, 4097, 1, 1549944694, 2745022601, 3181633273, 1113334022, 2827919296, 1467047999, 1485013325, 2809953970, 4233124407, 61842888, 2999413561, 1295553734, 4062552591, 232414704, 3507545467, 787421828, 1 }; - uint[] datapCorrect = new uint[] { 1549944692, 2745022603, 3181633275, 1113334020, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; - uint[] datapUints = EnctypeBase.ConvertBytesToUint(data); - Assert.True(datapCorrect.SequenceEqual(datapUints)); - Assert.True(tbuffCorrect.SequenceEqual(sbox)); - } - - [Fact] - public void EncShare3Test() - { - uint[] data = Enumerable.Repeat(2, 326).ToArray(); - var dataCorrect = new uint[] { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 33620998, 135004685, 69605426, 1014894209, 659661192, 2751357934, 70666098, 903130807, 49046030, 3998375437, 390165736, 1818952629, 3823375448, 2963547126, 1207763347, 1, 33620484, 67633672, 35065374, 609093707, 416178922, 41172573, 2689180916, 3999573889, 1412900922, 2353420577, 3107029142, 4152417908, 3761014253, 2622030354, 604491503, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 16, 26297853, 3438224389, 65536, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; - encShare.EncShare3(data, 0, 0); - Assert.True(dataCorrect.SequenceEqual(data)); - } - - [Fact] - public void EncShare4Test() - { - byte[] datap = EnctypeBase.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); - uint[] dest = Enumerable.Repeat(2, 326).ToArray(); - var destCorrect = new uint[] { 791505064, 2677899357, 1869417374, 1465234252, 2475789121, 740983918, 2189457908, 1330493599, 3469488310, 404138664, 3890561660, 2037844579, 3772710031, 3014765711, 1414723042, 3250538244, 2239997656, 3368442198, 2829463375, 3873709831, 1616809334, 2408416094, 2593714885, 3065269826, 2728411308, 3115826211, 623071236, 1987339440, 3503169148, 3149487561, 3216855940, 4277978053, 2694731547, 943095479, 2947390224, 3284255792, 892575532, 4092678516, 3301083488, 1701020929, 50269918, 1583105766, 1178877547, 3233684681, 100976904, 1279948990, 909431883, 2341052024, 656793440, 1229403759, 825219562, 4059029186, 1111521199, 3435794874, 3486359808, 2088400399, 1498911874, 2458935151, 2138956613, 2661076370, 4193770664, 875740486, 3789516497, 67269043, 3048411365, 3722181363, 3907409649, 842022855, 1785245261, 2997891633, 1027315374, 1448379989, 4244284893, 3856866799, 1397854789, 4294782009, 168303206, 2206334467, 3200035771, 976798254, 2054714062, 1667360351, 1852589230, 4042177498, 3823179561, 1599942934, 437785722, 2223115414, 151501413, 3317924486, 3031575332, 4143189866, 134631422, 4227418534, 2981074340, 3385320959, 117789415, 2711583408, 2879989466, 3991617966, 286206350, 1818888530, 2071585337, 3739025234, 1431558871, 2021051280, 3267424079, 2745248443, 2307337611, 589374105, 2795805112, 1734712546, 2492609632, 4126389475, 4176931104, 2374707415, 3621103376, 3924286366, 2004218049, 84087549, 3957934975, 1212547806, 1549461370, 3082111598, 4025327728, 3132657759, 202009759, 1953642301, 2290488196, 2273689622, 1532595819, 2576839429, 724152551, 2610524026, 1717830723, 1802078870, 2543150389, 2762113475, 538843459, 926252336, 1835774642, 2442121321, 1768364936, 1077865321, 707273192, 3941128409, 50449982, 218878597, 6393, 2778976161, 3183168775, 3520064586, 3974786543, 2863201253, 959919482, 4075851646, 3166321558, 3334784883, 690431172, 336772646, 2964244513, 3705314749, 420976600, 1566245846, 387311668, 1010479510, 2896847722, 2846308697, 319944579, 2930534087, 3351637619, 1195746181, 1650488629, 1751570372, 3418979436, 3570593063, 4008508924, 505193660, 2627368049, 572549046, 252515951, 673636639, 1482054814, 2913665291, 4109518194, 2812631805, 3654778850, 454681732, 33435212, 1296779424, 1364191760, 3536885831, 2644195397, 269380140, 1145189234, 1515732185, 757817052, 1128356723, 1347352576, 3637938136, 1684159756, 3840047978, 3671636355, 2155743464, 2509489735, 555702320, 2559995931, 185189712, 3587438174, 606263025, 2324213583, 2526306506, 488365343, 1094685107, 1313605119, 1886282224, 2105240036, 774640903, 808383289, 993638370, 3553739022, 1162027228, 639955374, 4210601392, 3452684985, 1381031990, 1246242604, 2172609167, 1970533518, 3688480660, 2391593397, 4261137494, 1263113115, 1919957700, 3604219589, 3806348272, 3402139713, 303092719, 1044122905, 370419094, 353606213, 1903121829, 2357873757, 235667635, 4160071907, 2425266738, 3098990011, 521996276, 858858457, 2122099102, 3755824166, 2256844040, 1061009100, 1936812966, 471501886, 1633679743, 2, 3648983511, 3019444315, 3087473871, 3504257679, 3636804204, 677370271, 1517421204, 319045361, 2414764241, 1428393162, 1627687985, 3434938190, 1735044768, 1113717230, 3367048133, 1, 1803453643, 3619135896, 151234568, 3605443923, 1593688450, 2992556862, 3389812064, 1790820139, 3447432721, 1637898815, 1432460662, 2272000269, 3310238632, 964792890, 2941880083, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 16, 2110503879, 2280433177, 65536, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; - - encShare.EncShare4(datap, 1, dest); - - Assert.True(destCorrect.SequenceEqual(dest)); - } -} - - -public class Encrypt2Test -{ - private IEnctype2Test _enctype2 = new Enctype2("abcdef"); - [Fact] - public void EncoderTest() - { - // var data = Enumerable.Repeat(2, 326).ToArray(); - // var dataBytes = EnctypeBase.ConvertUintToBytes(data); - // var secretKeyBytes = EnctypeBase.ConvertBytesToUint(((Enctype2)_enctype2).GameSecreteKey); - // _enctype2.Encoder(secretKeyBytes); - // var dataCorrect = new uint[] { 1667391972, 6710628, 3464277760, 3324666663, 1732499745, 230, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; - // data = EnctypeBase.ConvertBytesToUint(dataBytes); - // Assert.True(dataCorrect.SequenceEqual(data)); - } -} - - -public class Enctype1Test -{ - [Fact] - public void Func5Test() - { - IEnctype1Test enc1 = (IEnctype1Test)new Enctype1("abcdef"); - var encKey = UniSpyEncoding.GetBytes("abcdef"); - var key = UniSpyEncoding.GetBytes("abcdef"); - int n1 = 2, n2 = 2; - enc1.Func5(5, key, ref n1, ref n2, encKey); - Assert.True(n1 == 99); - Assert.True(n2 == 1); - Assert.True(key.SequenceEqual(UniSpyEncoding.GetBytes("abcdef"))); - } - [Fact] - public void Func4Test() - { - IEnctype1Test enc1 = (IEnctype1Test)new Enctype1("abcdef"); - - var key = UniSpyEncoding.GetBytes("abcdef"); - enc1.CreateEnc1Key(enc1.ValidateKey, key); - Assert.True(key.SequenceEqual(UniSpyEncoding.GetBytes("abcdef"))); - var correctEnc1Key = new byte[] { 35, 53, 63, 48, 8, 15, 110, 160, 40, 34, 38, 172, 125, 28, 118, 9, 22, 107, 36, 16, 46, 104, 6, 136, 32, 50, 101, 132, 21, 119, 190, 20, 114, 154, 33, 42, 98, 39, 131, 70, 56, 144, 4, 120, 2, 26, 149, 18, 0, 153, 24, 44, 12, 152, 10, 138, 51, 133, 71, 17, 5, 202, 3, 208, 11, 128, 134, 83, 14, 41, 23, 52, 122, 151, 47, 116, 27, 108, 139, 214, 113, 75, 250, 78, 145, 86, 57, 124, 111, 45, 126, 59, 147, 65, 141, 137, 143, 69, 96, 148, 30, 105, 95, 196, 129, 146, 62, 109, 54, 81, 130, 102, 68, 115, 60, 117, 58, 140, 74, 121, 66, 123, 64, 142, 135, 127, 72, 99, 166, 76, 220, 82, 226, 112, 89, 80, 77, 92, 29, 90, 87, 84, 93, 178, 232, 100, 150, 88, 155, 184, 238, 106, 244, 94, 161, 156, 157, 158, 159, 1, 167, 162, 163, 164, 165, 7, 173, 168, 169, 170, 171, 13, 179, 174, 175, 176, 177, 19, 185, 180, 181, 182, 183, 25, 191, 186, 187, 188, 189, 31, 197, 192, 193, 194, 195, 37, 203, 198, 199, 200, 201, 43, 209, 204, 205, 206, 207, 49, 215, 210, 211, 212, 213, 55, 221, 216, 217, 218, 219, 61, 227, 222, 223, 224, 225, 67, 233, 228, 229, 230, 231, 73, 239, 234, 235, 236, 237, 79, 245, 240, 241, 242, 243, 85, 251, 246, 247, 248, 249, 91, 103, 252, 253, 254, 255, 97, 53, 48, 15, 160, 45 }; - Assert.True(enc1.Enc1Key.SequenceEqual(correctEnc1Key)); - } - - [Fact] - public void Func7Test() - { - IEnctype1Test enc1 = (IEnctype1Test)new Enctype1("abcdef"); - var r = enc1.SubstituteEnc1key(10, enc1.Enc1Key); - var correctEnc1Key = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 10, 10 }; - Assert.True(enc1.Enc1Key.SequenceEqual(correctEnc1Key)); - } - - [Fact] - public void Func8Test() - { - IEnctype1Test enc1 = (IEnctype1Test)new Enctype1("abcdef"); - var data = EnctypeBase.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); - enc1.Func8(data, 10, Enctype1.MasterKey); - var correctData = new uint[] { 16843194, 16843194, 442, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }; - var dataUints = EnctypeBase.ConvertBytesToUint(data); - Assert.True(correctData.SequenceEqual(dataUints)); - } - - [Fact] - public void Func6Test() - { - IEnctype1Test enc1 = (IEnctype1Test)new Enctype1("abcdef"); - var data = EnctypeBase.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); - enc1.EncryptByEnc1Key(data, 10, enc1.Enc1Key); - var correctData = new byte[] { 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0 }; - Assert.True(correctData.SequenceEqual(data)); - var enc1KeyCorrect = new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0 }; - Assert.True(enc1KeyCorrect.SequenceEqual(enc1.Enc1Key)); - } - - [Fact] - public void Func3Test() - { - IEnctype1Test enc1 = (IEnctype1Test)new Enctype1("abcdef"); - var data = EnctypeBase.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); - var buff = EnctypeBase.ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); - enc1.CreateEnc0Key(data, 10, buff); - var correctBuff = new uint[] { 2423783724, 2626217045, 2666177577, 3362939995, 3417775348, 3999423967, 1642197989, 91342666, 3246176910, 455134510, 3298402371, 2675676894, 4059931565, 2176375884, 1087007327, 3680970979, 2960389948, 3992074825, 449167537, 4019542232, 3920131915, 1672128080, 1953860086, 2635063843, 499266173, 995782236, 1863130809, 2899638540, 3943859670, 3611120871, 2705294232, 1747078419, 1389594391, 1704679349, 830416758, 2357868666, 556737645, 2486454320, 184857584, 1150109371, 1338197782, 1775337177, 3216789815, 2952129716, 713304926, 4209027549, 171131454, 2933556984, 2184588750, 896240384, 1895567724, 121182412, 4283344853, 2548565524, 242815122, 1444522835, 3062851200, 3486691985, 3499345562, 631422715, 149333344, 1096400159, 4261619269, 164173058, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; - var buffUints = EnctypeBase.ConvertBytesToUint(buff); - Assert.True(correctBuff.SequenceEqual(buffUints)); - } - - [Fact] - public void Func2Test() - { - IEnctype1Test enc1 = (IEnctype1Test)new Enctype1("abcdef"); - var data = EnctypeBase.ConvertUintToBytes(Enumerable.Repeat(1, 326).ToArray()); - var crypt = EnctypeBase.ConvertUintToBytes(Enumerable.Repeat(2, 326).ToArray()); - enc1.EncryptByEnc0Key(data, 10, crypt); - var correctBuff = new uint[] { 33554432, 131584, 512, 0, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1548, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 }; - var buffUints = EnctypeBase.ConvertBytesToUint(crypt); - Assert.True(correctBuff.SequenceEqual(buffUints)); - } - - [Fact] - public void FinalEnctype1Test() - { - var key = "123456"; - var plainText = Encoding.ASCII.GetBytes("000000000000000000000000000000000000"); - var enc = new Enctype1(key); - var result = enc.Encrypt(plainText); - var correct = new byte[] { 0, 0, 0, 94, 42, 218, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 252, 154, 163, 177, 4, 195, 33, 192, 235, 227, 121, 208, 204, 19, 11, 74, 63, 102, 184, 149, 11, 229, 45, 113, 154, 18, 116, 175, 222, 66, 32, 146, 129, 11, 22, 161 }; - Assert.True(result.SequenceEqual(correct)); - } - - [Fact] - public void FinalEnctype2Test() - { - var key = "123456"; - var plainText = Encoding.ASCII.GetBytes("000000000000000000000000000000000000"); - var enc = new Enctype2(key); - var result = enc.Encrypt(plainText); - var correct = new byte[] { 157, 76, 254, 23, 81, 26, 246, 17, 233, 115, 87, 214, 24, 146, 105, 83, 161, 42, 182, 182, 54, 110, 0, 236, 163, 144, 200, 28, 156, 151, 57, 95, 42, 141, 219, 192 }; - Assert.True(result.SequenceEqual(correct)); - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/test/V1/RequestTest.cs b/src/Servers/ServerBrowser/test/V1/RequestTest.cs deleted file mode 100644 index 44c530557..000000000 --- a/src/Servers/ServerBrowser/test/V1/RequestTest.cs +++ /dev/null @@ -1,23 +0,0 @@ -using UniSpy.Server.ServerBrowser.V1.Contract.Request; -using Xunit; - -namespace UniSpy.Server.ServerBrowser.Test.V1 -{ - public class RequestTest - { - [Fact] - public void Armada220230413() - { - var raw1 = @"\gamename\armada2\gamever\1.6\location\0\validate\Qsu/4AdL\final\\queryid\1.1\"; - var request1 = new GameNameRequest(raw1); - request1.Parse(); - Assert.True(request1.GameName == "armada2"); - Assert.True(request1.Version == "1.6"); - var raw2 = @"\list\groups\gamename\armada2\final\"; - var request2 = new ListRequest(raw2); - request2.Parse(); - Assert.True(request2.Type == ListRequestType.Group); - Assert.True(request2.GameName == "armada2"); - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/test/V2/FilterTest.cs b/src/Servers/ServerBrowser/test/V2/FilterTest.cs deleted file mode 100644 index 0ac2c8c01..000000000 --- a/src/Servers/ServerBrowser/test/V2/FilterTest.cs +++ /dev/null @@ -1,30 +0,0 @@ -using System; -using System.Linq; -using Xunit; - -namespace UniSpy.Server.ServerBrowser.V2.Test -{ - public class Person - { - public string Name { get; set; } - public int Age { get; set; } - public int Weight { get; set; } - public DateTime FavouriteDay { get; set; } - } - public class FilterTest - { - [Fact(Skip = "not implemented")] - public void SimpleQueryFilter() - { - // Given - var temp = "(country = 'US' and numplayers > 5) or hostname like '%GameSpy%'"; - var delimiterChars = new char[] { '(', ')' }; - var frags = temp.Split(delimiterChars).ToList(); - frags.RemoveAll(s => s == ""); - // When - Assert.Equal("country = 'US' and numplayers > 5", frags[0]); - Assert.Equal("or hostname like '%GameSpy%'", frags[1]); - // Then - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/test/V2/GameTest.cs b/src/Servers/ServerBrowser/test/V2/GameTest.cs deleted file mode 100644 index 330a46bfe..000000000 --- a/src/Servers/ServerBrowser/test/V2/GameTest.cs +++ /dev/null @@ -1,147 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.Interface; -using Xunit; - -namespace UniSpy.Server.ServerBrowser.V2.Test -{ - public class GameTest - { - [Fact] - public void Gmtest20200309() - { - var qrReq = new byte[] { 0x03, 0xEA, 0x2B, 0xAF, 0x50, 0x6C, 0x6F, 0x63, 0x61, 0x6C, 0x69, 0x70, 0x30, 0x00, 0x31, 0x39, 0x32, 0x2E, 0x31, 0x36, 0x38, 0x2E, 0x31, 0x32, 0x32, 0x2E, 0x32, 0x32, 0x36, 0x00, 0x6C, 0x6F, 0x63, 0x61, 0x6C, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x31, 0x31, 0x31, 0x31, 0x31, 0x00, 0x6E, 0x61, 0x74, 0x6E, 0x65, 0x67, 0x00, 0x31, 0x00, 0x73, 0x74, 0x61, 0x74, 0x65, 0x63, 0x68, 0x61, 0x6E, 0x67, 0x65, 0x64, 0x00, 0x33, 0x00, 0x67, 0x61, 0x6D, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x00, 0x67, 0x6D, 0x74, 0x65, 0x73, 0x74, 0x00, 0x68, 0x6F, 0x73, 0x74, 0x6E, 0x61, 0x6D, 0x65, 0x00, 0x47, 0x61, 0x6D, 0x65, 0x53, 0x70, 0x79, 0x20, 0x51, 0x52, 0x32, 0x20, 0x53, 0x61, 0x6D, 0x70, 0x6C, 0x65, 0x00, 0x67, 0x61, 0x6D, 0x65, 0x76, 0x65, 0x72, 0x00, 0x32, 0x2E, 0x30, 0x30, 0x00, 0x68, 0x6F, 0x73, 0x74, 0x70, 0x6F, 0x72, 0x74, 0x00, 0x32, 0x35, 0x30, 0x30, 0x30, 0x00, 0x6D, 0x61, 0x70, 0x6E, 0x61, 0x6D, 0x65, 0x00, 0x67, 0x6D, 0x74, 0x6D, 0x61, 0x70, 0x31, 0x00, 0x67, 0x61, 0x6D, 0x65, 0x74, 0x79, 0x70, 0x65, 0x00, 0x61, 0x72, 0x65, 0x6E, 0x61, 0x00, 0x6E, 0x75, 0x6D, 0x70, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00, 0x31, 0x30, 0x00, 0x6E, 0x75, 0x6D, 0x74, 0x65, 0x61, 0x6D, 0x73, 0x00, 0x32, 0x00, 0x6D, 0x61, 0x78, 0x70, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00, 0x33, 0x32, 0x00, 0x67, 0x61, 0x6D, 0x65, 0x6D, 0x6F, 0x64, 0x65, 0x00, 0x6F, 0x70, 0x65, 0x6E, 0x70, 0x6C, 0x61, 0x79, 0x69, 0x6E, 0x67, 0x00, 0x74, 0x65, 0x61, 0x6D, 0x70, 0x6C, 0x61, 0x79, 0x00, 0x31, 0x00, 0x66, 0x72, 0x61, 0x67, 0x6C, 0x69, 0x6D, 0x69, 0x74, 0x00, 0x30, 0x00, 0x74, 0x69, 0x6D, 0x65, 0x6C, 0x69, 0x6D, 0x69, 0x74, 0x00, 0x34, 0x30, 0x00, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x79, 0x00, 0x38, 0x30, 0x30, 0x00, 0x72, 0x61, 0x6E, 0x6B, 0x69, 0x6E, 0x67, 0x6F, 0x6E, 0x00, 0x31, 0x00, 0x00, 0x00, 0x0A, 0x70, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x5F, 0x00, 0x73, 0x63, 0x6F, 0x72, 0x65, 0x5F, 0x00, 0x64, 0x65, 0x61, 0x74, 0x68, 0x73, 0x5F, 0x00, 0x70, 0x69, 0x6E, 0x67, 0x5F, 0x00, 0x74, 0x65, 0x61, 0x6D, 0x5F, 0x00, 0x74, 0x69, 0x6D, 0x65, 0x5F, 0x00, 0x00, 0x4A, 0x6F, 0x65, 0x20, 0x50, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x00, 0x34, 0x00, 0x32, 0x00, 0x37, 0x37, 0x00, 0x30, 0x00, 0x31, 0x38, 0x35, 0x00, 0x4C, 0x33, 0x33, 0x74, 0x20, 0x30, 0x6E, 0x33, 0x00, 0x36, 0x00, 0x32, 0x34, 0x00, 0x36, 0x38, 0x00, 0x31, 0x00, 0x38, 0x32, 0x30, 0x00, 0x52, 0x61, 0x70, 0x74, 0x6F, 0x72, 0x00, 0x31, 0x30, 0x00, 0x32, 0x39, 0x00, 0x32, 0x31, 0x36, 0x00, 0x31, 0x00, 0x36, 0x36, 0x34, 0x00, 0x47, 0x72, 0x38, 0x31, 0x00, 0x38, 0x00, 0x36, 0x00, 0x33, 0x32, 0x37, 0x00, 0x31, 0x00, 0x36, 0x39, 0x37, 0x00, 0x46, 0x6C, 0x75, 0x62, 0x62, 0x65, 0x72, 0x00, 0x31, 0x35, 0x00, 0x32, 0x00, 0x31, 0x37, 0x39, 0x00, 0x30, 0x00, 0x34, 0x38, 0x00, 0x53, 0x61, 0x72, 0x67, 0x65, 0x00, 0x39, 0x00, 0x31, 0x32, 0x00, 0x33, 0x33, 0x37, 0x00, 0x30, 0x00, 0x32, 0x39, 0x36, 0x00, 0x56, 0x6F, 0x69, 0x64, 0x00, 0x32, 0x37, 0x00, 0x32, 0x39, 0x00, 0x34, 0x35, 0x00, 0x30, 0x00, 0x33, 0x35, 0x35, 0x00, 0x72, 0x75, 0x6E, 0x61, 0x77, 0x61, 0x79, 0x00, 0x32, 0x34, 0x00, 0x34, 0x00, 0x31, 0x39, 0x37, 0x00, 0x31, 0x00, 0x34, 0x32, 0x38, 0x00, 0x50, 0x68, 0x33, 0x61, 0x72, 0x00, 0x33, 0x30, 0x00, 0x33, 0x30, 0x00, 0x33, 0x33, 0x39, 0x00, 0x31, 0x00, 0x35, 0x32, 0x35, 0x00, 0x77, 0x68, 0x30, 0x30, 0x74, 0x00, 0x33, 0x31, 0x00, 0x32, 0x38, 0x00, 0x32, 0x36, 0x39, 0x00, 0x31, 0x00, 0x37, 0x37, 0x00, 0x00, 0x02, 0x74, 0x65, 0x61, 0x6D, 0x5F, 0x74, 0x00, 0x73, 0x63, 0x6F, 0x72, 0x65, 0x5F, 0x74, 0x00, 0x61, 0x76, 0x67, 0x70, 0x69, 0x6E, 0x67, 0x5F, 0x74, 0x00, 0x00, 0x52, 0x65, 0x64, 0x00, 0x34, 0x38, 0x37, 0x00, 0x33, 0x33, 0x36, 0x00, 0x42, 0x6C, 0x75, 0x65, 0x00, 0x38, 0x32, 0x00, 0x34, 0x35, 0x38, 0x00 }; - var sbReq = new byte[] { 0x00, 0x09, 0x01, 0xC0, 0xA8, 0x7A, 0xE2, 0x2B, 0x67 }; - var qrClient = QueryReport.V2.Test.MockObject.CreateClient("192.168.122.226", 11111); - var sbClient = ServerBrowser.V2.Test.MockObject.CreateClient(); - - (qrClient as ITestClient).TestReceived(qrReq); - (sbClient as ITestClient).TestReceived(sbReq); - - } - [Fact] - public void SplitSendMessageTest() - { - // Given - var splitedRequests = new List() - { - // send message request - new byte[]{0x00,0x13,0x02,0x4F,0xD1,0xE0,0x1D,0x54,0xC5}, - // natneg message request - new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x00,0x00,0x2C,0xFD} - }; - var compeleteRequest = new byte[] { 0x00, 0x13, 0x02, 0x4F, 0xD1, 0xE0, 0x1D, 0x54, 0xC5, 0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2, 0x00, 0x00, 0x2C, 0xFD }; - // When - foreach (var req in splitedRequests) - { - ((ITestClient)MockObject.SBClient).TestReceived(req); - } - ((ITestClient)MockObject.SBClient).TestReceived(compeleteRequest); - } - [Fact] - public void Gmtest20220613() - { - // Given - var qrRequests = new List() - { - new byte[]{0x09,0x00,0x00,0x00,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00}, - new byte[]{0x03,0xB4,0xA3,0xCC,0x80,0x6C,0x6F,0x63,0x61,0x6C,0x69,0x70,0x30,0x00,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x30,0x2E,0x31,0x30,0x39,0x00,0x6C,0x6F,0x63,0x61,0x6C,0x70,0x6F,0x72,0x74,0x00,0x36,0x35,0x30,0x30,0x00,0x6E,0x61,0x74,0x6E,0x65,0x67,0x00,0x30,0x00,0x73,0x74,0x61,0x74,0x65,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x00,0x33,0x00,0x67,0x61,0x6D,0x65,0x6E,0x61,0x6D,0x65,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x69,0x70,0x00,0x30,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x70,0x6F,0x72,0x74,0x00,0x30,0x00,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x00,0x4D,0x79,0x20,0x53,0x65,0x72,0x76,0x65,0x72,0x00,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x00,0x6F,0x70,0x65,0x6E,0x73,0x74,0x61,0x67,0x69,0x6E,0x67,0x00,0x67,0x72,0x6F,0x75,0x70,0x69,0x64,0x00,0x33,0x00,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x31,0x00,0x6D,0x61,0x78,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x38,0x00,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x00,0x31,0x2E,0x30,0x31,0x00,0x00,0x00,0x01,0x70,0x6C,0x61,0x79,0x65,0x72,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x00,0x50,0x65,0x65,0x72,0x50,0x6C,0x61,0x79,0x65,0x72,0x31,0x00,0x30,0x00,0x00,0x00,0x00}, - // new byte[]{0x08,0xB4,0xA3,0xCC,0x80}, - // new byte[] {0x03,0xB4,0xA3,0xCC,0x80,0x6C,0x6F,0x63,0x61,0x6C,0x69,0x70,0x30,0x00,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x30,0x2E,0x31,0x30,0x39,0x00,0x6C,0x6F,0x63,0x61,0x6C,0x70,0x6F,0x72,0x74,0x00,0x36,0x35,0x30,0x30,0x00,0x6E,0x61,0x74,0x6E,0x65,0x67,0x00,0x30,0x00,0x73,0x74,0x61,0x74,0x65,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x00,0x32,0x00,0x67,0x61,0x6D,0x65,0x6E,0x61,0x6D,0x65,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x69,0x70,0x00,0x30,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x70,0x6F,0x72,0x74,0x00,0x30,0x00,0x00} - }; - var sb1Requests = new List() - { - new byte[]{0x00,0x5A,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00,0x29,0x3E,0x7C,0x23,0x43,0x5D,0x68,0x49,0x00,0x5C,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x5C,0x6E,0x75,0x6D,0x77,0x61,0x69,0x74,0x69,0x6E,0x67,0x5C,0x6D,0x61,0x78,0x77,0x61,0x69,0x74,0x69,0x6E,0x67,0x5C,0x6E,0x75,0x6D,0x73,0x65,0x72,0x76,0x65,0x72,0x73,0x5C,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x00,0x00,0x00,0x20} - }; - var sb2Requests = new List() - { - new byte[]{0x00,0x5A,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00,0x29,0x3E,0x7C,0x23,0x43,0x5D,0x68,0x49,0x00,0x5C,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x5C,0x6E,0x75,0x6D,0x77,0x61,0x69,0x74,0x69,0x6E,0x67,0x5C,0x6D,0x61,0x78,0x77,0x61,0x69,0x74,0x69,0x6E,0x67,0x5C,0x6E,0x75,0x6D,0x73,0x65,0x72,0x76,0x65,0x72,0x73,0x5C,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x00,0x00,0x00,0x20}, - new byte[]{0x00,0x5A,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00,0x24,0x68,0x41,0x78,0x23,0x39,0x59,0x70,0x00,0x5C,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x5C,0x6E,0x75,0x6D,0x77,0x61,0x69,0x74,0x69,0x6E,0x67,0x5C,0x6D,0x61,0x78,0x77,0x61,0x69,0x74,0x69,0x6E,0x67,0x5C,0x6E,0x75,0x6D,0x73,0x65,0x72,0x76,0x65,0x72,0x73,0x5C,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x00,0x00,0x00,0x20}, - new byte[]{0x00,0x5E,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00,0x28,0x76,0x7D,0x30,0x33,0x74,0x35,0x52,0x67,0x72,0x6F,0x75,0x70,0x69,0x64,0x3D,0x33,0x00,0x5C,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x5C,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x5C,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x5C,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6D,0x61,0x78,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x00,0x00,0x00,0x04}, - new byte[]{0x00,0x5E,0x00,0x01,0x03,0x00,0x00,0x00,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00,0x67,0x6D,0x74,0x65,0x73,0x74,0x00,0x2D,0x42,0x4C,0x67,0x32,0x73,0x28,0x26,0x67,0x72,0x6F,0x75,0x70,0x69,0x64,0x3D,0x33,0x00,0x5C,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x5C,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x5C,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x5C,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6D,0x61,0x78,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x00,0x00,0x00,0x04} - }; - foreach (var qrReq in qrRequests) - { - ((ITestClient)MockObject.QRClient).TestReceived(qrReq); - } - - foreach (var sbReq in sb2Requests) - { - ((ITestClient)MockObject.SBClient).TestReceived(sbReq); - } - } - /// - /// Fixed 20221108 - /// - [Fact] - public void Anno1701Date20220620() - { - // because when search on redis, redis require the server ip and port as key words, - // the ip and port in qr should match when sb execute ServerInfoRequest - // therefore, we create client based on IP 91.43.50.186:21701 to test qr and sb - var qrClient = QueryReport.V2.Test.MockObject.CreateClient("91.43.50.186", 21701); - var sbClient = MockObject.CreateClient(); - var qrRequests = new List() - { - // avaliable check - new byte[] {0x09,0x00,0x00,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}, - // heart beat - new byte[] {0x03,0x1D,0x55,0xCC,0xCA,0x6C,0x6F,0x63,0x61,0x6C,0x69,0x70,0x30,0x00,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x30,0x2E,0x38,0x30,0x00,0x6C,0x6F,0x63,0x61,0x6C,0x70,0x6F,0x72,0x74,0x00,0x32,0x31,0x37,0x30,0x31,0x00,0x6E,0x61,0x74,0x6E,0x65,0x67,0x00,0x31,0x00,0x73,0x74,0x61,0x74,0x65,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x00,0x33,0x00,0x67,0x61,0x6D,0x65,0x6E,0x61,0x6D,0x65,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x69,0x70,0x00,0x30,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x70,0x6F,0x72,0x74,0x00,0x30,0x00,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x00,0x28,0x75,0x6E,0x6B,0x6E,0x6F,0x77,0x6E,0x20,0x67,0x61,0x6D,0x65,0x29,0x00,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x00,0x6F,0x70,0x65,0x6E,0x73,0x74,0x61,0x67,0x69,0x6E,0x67,0x00,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x31,0x00,0x6D,0x61,0x78,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x00,0x32,0x31,0x39,0x30,0x33,0x00,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x52,0x61,0x6E,0x64,0x6F,0x6D,0x20,0x6D,0x61,0x70,0x00,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x00,0x45,0x61,0x73,0x79,0x00,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x6F,0x70,0x74,0x69,0x6F,0x6E,0x73,0x00,0x33,0x37,0x37,0x35,0x36,0x33,0x30,0x37,0x36,0x00,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x00,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x00,0x00,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x50,0x76,0x50,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x77,0x69,0x6E,0x63,0x6F,0x6E,0x64,0x69,0x74,0x69,0x6F,0x6E,0x73,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x75,0x73,0x65,0x72,0x63,0x6F,0x6E,0x74,0x65,0x6E,0x74,0x5F,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x00,0x00,0x00,0x01,0x70,0x6C,0x61,0x79,0x65,0x72,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x5F,0x32,0x32,0x30,0x00,0x30,0x00,0x30,0x00,0x00,0x01,0x00}, - new byte[] {0x03,0x1D,0x55,0xCC,0xCA,0x6C,0x6F,0x63,0x61,0x6C,0x69,0x70,0x30,0x00,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x30,0x2E,0x38,0x30,0x00,0x6C,0x6F,0x63,0x61,0x6C,0x70,0x6F,0x72,0x74,0x00,0x32,0x31,0x37,0x30,0x31,0x00,0x6E,0x61,0x74,0x6E,0x65,0x67,0x00,0x31,0x00,0x73,0x74,0x61,0x74,0x65,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x00,0x33,0x00,0x67,0x61,0x6D,0x65,0x6E,0x61,0x6D,0x65,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x69,0x70,0x00,0x30,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x70,0x6F,0x72,0x74,0x00,0x30,0x00,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x00,0x28,0x75,0x6E,0x6B,0x6E,0x6F,0x77,0x6E,0x20,0x67,0x61,0x6D,0x65,0x29,0x00,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x00,0x6F,0x70,0x65,0x6E,0x73,0x74,0x61,0x67,0x69,0x6E,0x67,0x00,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x31,0x00,0x6D,0x61,0x78,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x00,0x32,0x31,0x39,0x30,0x33,0x00,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x52,0x61,0x6E,0x64,0x6F,0x6D,0x20,0x6D,0x61,0x70,0x00,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x00,0x45,0x61,0x73,0x79,0x00,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x6F,0x70,0x74,0x69,0x6F,0x6E,0x73,0x00,0x33,0x37,0x37,0x35,0x36,0x33,0x30,0x37,0x36,0x00,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x00,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x00,0x00,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x50,0x76,0x50,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x77,0x69,0x6E,0x63,0x6F,0x6E,0x64,0x69,0x74,0x69,0x6F,0x6E,0x73,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x75,0x73,0x65,0x72,0x63,0x6F,0x6E,0x74,0x65,0x6E,0x74,0x5F,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x00,0x00,0x00,0x01,0x70,0x6C,0x61,0x79,0x65,0x72,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x5F,0x32,0x32,0x30,0x00,0x30,0x00,0x30,0x00,0x00,0x01,0x00}, - new byte[] {0x03,0x1D,0x55,0xCC,0xCA,0x6C,0x6F,0x63,0x61,0x6C,0x69,0x70,0x30,0x00,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x30,0x2E,0x38,0x30,0x00,0x6C,0x6F,0x63,0x61,0x6C,0x70,0x6F,0x72,0x74,0x00,0x32,0x31,0x37,0x30,0x31,0x00,0x6E,0x61,0x74,0x6E,0x65,0x67,0x00,0x31,0x00,0x73,0x74,0x61,0x74,0x65,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x00,0x31,0x00,0x67,0x61,0x6D,0x65,0x6E,0x61,0x6D,0x65,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x69,0x70,0x00,0x30,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x70,0x6F,0x72,0x74,0x00,0x30,0x00,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x00,0x28,0x75,0x6E,0x6B,0x6E,0x6F,0x77,0x6E,0x20,0x67,0x61,0x6D,0x65,0x29,0x00,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x00,0x6F,0x70,0x65,0x6E,0x73,0x74,0x61,0x67,0x69,0x6E,0x67,0x00,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x31,0x00,0x6D,0x61,0x78,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x00,0x32,0x31,0x39,0x30,0x33,0x00,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x52,0x61,0x6E,0x64,0x6F,0x6D,0x20,0x6D,0x61,0x70,0x00,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x00,0x45,0x61,0x73,0x79,0x00,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x6F,0x70,0x74,0x69,0x6F,0x6E,0x73,0x00,0x31,0x30,0x39,0x31,0x32,0x37,0x36,0x32,0x30,0x00,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x30,0x00,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x50,0x76,0x50,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x77,0x69,0x6E,0x63,0x6F,0x6E,0x64,0x69,0x74,0x69,0x6F,0x6E,0x73,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x75,0x73,0x65,0x72,0x63,0x6F,0x6E,0x74,0x65,0x6E,0x74,0x5F,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x00,0x00,0x00,0x01,0x70,0x6C,0x61,0x79,0x65,0x72,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x5F,0x32,0x32,0x30,0x00,0x30,0x00,0x30,0x00,0x00,0x01,0x00}, - new byte[] {0x03,0x1D,0x55,0xCC,0xCA,0x6C,0x6F,0x63,0x61,0x6C,0x69,0x70,0x30,0x00,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x30,0x2E,0x38,0x30,0x00,0x6C,0x6F,0x63,0x61,0x6C,0x70,0x6F,0x72,0x74,0x00,0x32,0x31,0x37,0x30,0x31,0x00,0x6E,0x61,0x74,0x6E,0x65,0x67,0x00,0x31,0x00,0x73,0x74,0x61,0x74,0x65,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x00,0x31,0x00,0x67,0x61,0x6D,0x65,0x6E,0x61,0x6D,0x65,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x69,0x70,0x00,0x30,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x70,0x6F,0x72,0x74,0x00,0x30,0x00,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x00,0x28,0x75,0x6E,0x6B,0x6E,0x6F,0x77,0x6E,0x20,0x67,0x61,0x6D,0x65,0x29,0x00,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x00,0x6F,0x70,0x65,0x6E,0x73,0x74,0x61,0x67,0x69,0x6E,0x67,0x00,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x31,0x00,0x6D,0x61,0x78,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x00,0x32,0x31,0x39,0x30,0x33,0x00,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x52,0x61,0x6E,0x64,0x6F,0x6D,0x20,0x6D,0x61,0x70,0x00,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x00,0x45,0x61,0x73,0x79,0x00,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x6F,0x70,0x74,0x69,0x6F,0x6E,0x73,0x00,0x31,0x30,0x39,0x31,0x32,0x37,0x36,0x32,0x30,0x00,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x30,0x00,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x50,0x76,0x50,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x77,0x69,0x6E,0x63,0x6F,0x6E,0x64,0x69,0x74,0x69,0x6F,0x6E,0x73,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x75,0x73,0x65,0x72,0x63,0x6F,0x6E,0x74,0x65,0x6E,0x74,0x5F,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x00,0x00,0x00,0x01,0x70,0x6C,0x61,0x79,0x65,0x72,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x5F,0x32,0x32,0x30,0x00,0x30,0x00,0x30,0x00,0x00,0x01,0x00}, - new byte[] {0x03,0x1D,0x55,0xCC,0xCA,0x6C,0x6F,0x63,0x61,0x6C,0x69,0x70,0x30,0x00,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x30,0x2E,0x38,0x30,0x00,0x6C,0x6F,0x63,0x61,0x6C,0x70,0x6F,0x72,0x74,0x00,0x32,0x31,0x37,0x30,0x31,0x00,0x6E,0x61,0x74,0x6E,0x65,0x67,0x00,0x31,0x00,0x67,0x61,0x6D,0x65,0x6E,0x61,0x6D,0x65,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x69,0x70,0x00,0x30,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x70,0x6F,0x72,0x74,0x00,0x30,0x00,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x00,0x28,0x75,0x6E,0x6B,0x6E,0x6F,0x77,0x6E,0x20,0x67,0x61,0x6D,0x65,0x29,0x00,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x00,0x6F,0x70,0x65,0x6E,0x73,0x74,0x61,0x67,0x69,0x6E,0x67,0x00,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x31,0x00,0x6D,0x61,0x78,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x00,0x32,0x31,0x39,0x30,0x33,0x00,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x52,0x61,0x6E,0x64,0x6F,0x6D,0x20,0x6D,0x61,0x70,0x00,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x00,0x45,0x61,0x73,0x79,0x00,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x6F,0x70,0x74,0x69,0x6F,0x6E,0x73,0x00,0x31,0x30,0x39,0x31,0x32,0x37,0x36,0x32,0x30,0x00,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x30,0x00,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x50,0x76,0x50,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x77,0x69,0x6E,0x63,0x6F,0x6E,0x64,0x69,0x74,0x69,0x6F,0x6E,0x73,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x75,0x73,0x65,0x72,0x63,0x6F,0x6E,0x74,0x65,0x6E,0x74,0x5F,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x00,0x00,0x00,0x01,0x70,0x6C,0x61,0x79,0x65,0x72,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x5F,0x32,0x32,0x30,0x00,0x30,0x00,0x30,0x00,0x00,0x01,0x00}, - new byte[] {0x03,0x1D,0x55,0xCC,0xCA,0x6C,0x6F,0x63,0x61,0x6C,0x69,0x70,0x30,0x00,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x30,0x2E,0x38,0x30,0x00,0x6C,0x6F,0x63,0x61,0x6C,0x70,0x6F,0x72,0x74,0x00,0x32,0x31,0x37,0x30,0x31,0x00,0x6E,0x61,0x74,0x6E,0x65,0x67,0x00,0x31,0x00,0x67,0x61,0x6D,0x65,0x6E,0x61,0x6D,0x65,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x69,0x70,0x00,0x30,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x70,0x6F,0x72,0x74,0x00,0x30,0x00,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x00,0x28,0x75,0x6E,0x6B,0x6E,0x6F,0x77,0x6E,0x20,0x67,0x61,0x6D,0x65,0x29,0x00,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x00,0x6F,0x70,0x65,0x6E,0x73,0x74,0x61,0x67,0x69,0x6E,0x67,0x00,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x31,0x00,0x6D,0x61,0x78,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x00,0x32,0x31,0x39,0x30,0x33,0x00,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x52,0x61,0x6E,0x64,0x6F,0x6D,0x20,0x6D,0x61,0x70,0x00,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x00,0x45,0x61,0x73,0x79,0x00,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x6F,0x70,0x74,0x69,0x6F,0x6E,0x73,0x00,0x31,0x30,0x39,0x31,0x32,0x37,0x36,0x32,0x30,0x00,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x30,0x00,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x50,0x76,0x50,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x77,0x69,0x6E,0x63,0x6F,0x6E,0x64,0x69,0x74,0x69,0x6F,0x6E,0x73,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x75,0x73,0x65,0x72,0x63,0x6F,0x6E,0x74,0x65,0x6E,0x74,0x5F,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x00,0x00,0x00,0x01,0x70,0x6C,0x61,0x79,0x65,0x72,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x5F,0x32,0x32,0x30,0x00,0x30,0x00,0x30,0x00,0x00,0x01,0x00}, - new byte[] {0x03,0x1D,0x55,0xCC,0xCA,0x6C,0x6F,0x63,0x61,0x6C,0x69,0x70,0x30,0x00,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x30,0x2E,0x38,0x30,0x00,0x6C,0x6F,0x63,0x61,0x6C,0x70,0x6F,0x72,0x74,0x00,0x32,0x31,0x37,0x30,0x31,0x00,0x6E,0x61,0x74,0x6E,0x65,0x67,0x00,0x31,0x00,0x67,0x61,0x6D,0x65,0x6E,0x61,0x6D,0x65,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x69,0x70,0x00,0x30,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x70,0x6F,0x72,0x74,0x00,0x30,0x00,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x00,0x28,0x75,0x6E,0x6B,0x6E,0x6F,0x77,0x6E,0x20,0x67,0x61,0x6D,0x65,0x29,0x00,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x00,0x6F,0x70,0x65,0x6E,0x73,0x74,0x61,0x67,0x69,0x6E,0x67,0x00,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x31,0x00,0x6D,0x61,0x78,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x00,0x32,0x31,0x39,0x30,0x33,0x00,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x52,0x61,0x6E,0x64,0x6F,0x6D,0x20,0x6D,0x61,0x70,0x00,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x00,0x45,0x61,0x73,0x79,0x00,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x6F,0x70,0x74,0x69,0x6F,0x6E,0x73,0x00,0x31,0x30,0x39,0x31,0x32,0x37,0x36,0x32,0x30,0x00,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x30,0x00,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x50,0x76,0x50,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x77,0x69,0x6E,0x63,0x6F,0x6E,0x64,0x69,0x74,0x69,0x6F,0x6E,0x73,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x75,0x73,0x65,0x72,0x63,0x6F,0x6E,0x74,0x65,0x6E,0x74,0x5F,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x00,0x00,0x00,0x01,0x70,0x6C,0x61,0x79,0x65,0x72,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x5F,0x32,0x32,0x30,0x00,0x30,0x00,0x30,0x00,0x00,0x01,0x00}, - new byte[] {0x03,0x1D,0x55,0xCC,0xCA,0x6C,0x6F,0x63,0x61,0x6C,0x69,0x70,0x30,0x00,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x30,0x2E,0x38,0x30,0x00,0x6C,0x6F,0x63,0x61,0x6C,0x70,0x6F,0x72,0x74,0x00,0x32,0x31,0x37,0x30,0x31,0x00,0x6E,0x61,0x74,0x6E,0x65,0x67,0x00,0x31,0x00,0x67,0x61,0x6D,0x65,0x6E,0x61,0x6D,0x65,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x69,0x70,0x00,0x30,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x70,0x6F,0x72,0x74,0x00,0x30,0x00,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x00,0x28,0x75,0x6E,0x6B,0x6E,0x6F,0x77,0x6E,0x20,0x67,0x61,0x6D,0x65,0x29,0x00,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x00,0x6F,0x70,0x65,0x6E,0x73,0x74,0x61,0x67,0x69,0x6E,0x67,0x00,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x31,0x00,0x6D,0x61,0x78,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x00,0x32,0x31,0x39,0x30,0x33,0x00,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x52,0x61,0x6E,0x64,0x6F,0x6D,0x20,0x6D,0x61,0x70,0x00,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x00,0x45,0x61,0x73,0x79,0x00,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x6F,0x70,0x74,0x69,0x6F,0x6E,0x73,0x00,0x31,0x30,0x39,0x31,0x32,0x37,0x36,0x32,0x30,0x00,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x30,0x00,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x50,0x76,0x50,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x77,0x69,0x6E,0x63,0x6F,0x6E,0x64,0x69,0x74,0x69,0x6F,0x6E,0x73,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x75,0x73,0x65,0x72,0x63,0x6F,0x6E,0x74,0x65,0x6E,0x74,0x5F,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x00,0x00,0x00,0x01,0x70,0x6C,0x61,0x79,0x65,0x72,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x5F,0x32,0x32,0x30,0x00,0x30,0x00,0x30,0x00,0x00,0x01,0x00}, - //client message - new byte[] {0xFE,0xFD,0x03,0x1D,0x55,0xCC,0xCA,0x54,0x54,0x54,0x00,0x00,0x5B,0x2B,0x32,0xBA,0x00,0x00,0x00,0x00,0xC5,0x54,0x00,0x00}, - // keep alive - new byte[] {0x08,0x1D,0x55,0xCC,0xCA} - }; - - var sbRequests = new List() - { - new byte[] {0x00,0x9A,0x00,0x01,0x03,0x8F,0x55,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x44,0x3A,0x40,0x6F,0x29,0x4F,0x6B,0x68,0x67,0x72,0x6F,0x75,0x70,0x69,0x64,0x20,0x69,0x73,0x20,0x6E,0x75,0x6C,0x6C,0x00,0x5C,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x5C,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x5C,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x5C,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x5C,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x5C,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x5C,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x5C,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x00,0x00,0x00,0x04}, - new byte[] {0x00,0x9A,0x00,0x01,0x03,0x8F,0x55,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x41,0x48,0x6C,0x3D,0x27,0x6C,0x68,0x49,0x67,0x72,0x6F,0x75,0x70,0x69,0x64,0x20,0x69,0x73,0x20,0x6E,0x75,0x6C,0x6C,0x00,0x5C,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x5C,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x5C,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x5C,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x5C,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x5C,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x5C,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x5C,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x00,0x00,0x00,0x04}, - new byte[] {0x00,0x9A,0x00,0x01,0x03,0x8F,0x55,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x54,0x73,0x46,0x68,0x48,0x6A,0x76,0x51,0x67,0x72,0x6F,0x75,0x70,0x69,0x64,0x20,0x69,0x73,0x20,0x6E,0x75,0x6C,0x6C,0x00,0x5C,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x5C,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x5C,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x5C,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x5C,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x5C,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x5C,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x5C,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x00,0x00,0x00,0x04}, - //! error occur in this request - new byte[] {0x00,0x09,0x01,0x5B,0x2B,0x32,0xBA,0x54,0xC5}, - // natneg request - new byte[] {0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x00,0x00,0x17,0x31} - }; - - foreach (var qrReq in qrRequests) - { - ((ITestClient)qrClient).TestReceived(qrReq); - } - - foreach (var sbReq in sbRequests) - { - ((ITestClient)sbClient).TestReceived(sbReq); - } - } - [Fact] - public void Anno1701Date20221104() - { - var qrClient1 = QueryReport.V2.Test.MockObject.CreateClient("79.209.224.29", 21701); - var qrClient2 = QueryReport.V2.Test.MockObject.CreateClient("31.18.120.193", 21701); - var sbClient1 = ServerBrowser.V2.Test.MockObject.CreateClient("79.209.224.29", 45340); - var sbclient2 = ServerBrowser.V2.Test.MockObject.CreateClient("31.18.120.193", 50587); - var requests = new List>(){ - new KeyValuePair("qr1",new byte[] {0x09,0x00,0x00,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x0}), - new KeyValuePair("qr1",new byte[] {0x03,0x98,0x92,0x25,0xA0,0x6C,0x6F,0x63,0x61,0x6C,0x69,0x70,0x30,0x00,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x30,0x2E,0x35,0x30,0x00,0x6C,0x6F,0x63,0x61,0x6C,0x69,0x70,0x31,0x00,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x31,0x32,0x32,0x2E,0x31,0x00,0x6C,0x6F,0x63,0x61,0x6C,0x70,0x6F,0x72,0x74,0x00,0x32,0x31,0x37,0x30,0x31,0x00,0x6E,0x61,0x74,0x6E,0x65,0x67,0x00,0x31,0x00,0x73,0x74,0x61,0x74,0x65,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x00,0x33,0x00,0x67,0x61,0x6D,0x65,0x6E,0x61,0x6D,0x65,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x69,0x70,0x00,0x30,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x70,0x6F,0x72,0x74,0x00,0x30,0x00,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x00,0x28,0x75,0x6E,0x6B,0x6E,0x6F,0x77,0x6E,0x20,0x67,0x61,0x6D,0x65,0x29,0x00,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x00,0x6F,0x70,0x65,0x6E,0x73,0x74,0x61,0x67,0x69,0x6E,0x67,0x00,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x31,0x00,0x6D,0x61,0x78,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x00,0x32,0x31,0x39,0x30,0x33,0x00,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x52,0x61,0x6E,0x64,0x6F,0x6D,0x20,0x6D,0x61,0x70,0x00,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x00,0x45,0x61,0x73,0x79,0x00,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x6F,0x70,0x74,0x69,0x6F,0x6E,0x73,0x00,0x33,0x36,0x39,0x31,0x37,0x34,0x34,0x36,0x38,0x00,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x00,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x00,0x00,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x50,0x76,0x50,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x77,0x69,0x6E,0x63,0x6F,0x6E,0x64,0x69,0x74,0x69,0x6F,0x6E,0x73,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x75,0x73,0x65,0x72,0x63,0x6F,0x6E,0x74,0x65,0x6E,0x74,0x5F,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x00,0x00,0x00,0x01,0x70,0x6C,0x61,0x79,0x65,0x72,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x00,0x73,0x70,0x6F,0x72,0x65,0x73,0x69,0x72,0x69,0x75,0x73,0x00,0x30,0x00,0x30,0x00,0x00,0x01,0x00}), - new KeyValuePair("sb1",new byte[] {0x00,0x9A,0x00,0x01,0x03,0x8F,0x55,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x52,0x63,0x58,0x3B,0x4D,0x28,0x7B,0x47,0x67,0x72,0x6F,0x75,0x70,0x69,0x64,0x20,0x69,0x73,0x20,0x6E,0x75,0x6C,0x6C,0x00,0x5C,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x5C,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x5C,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x5C,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x5C,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x5C,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x5C,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x5C,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x00,0x00,0x00,0x04}), - new KeyValuePair("qr2",new byte[] {0x07,0x98,0x92,0x25,0xA0,0x00,0x00,0x00,0x00}), - - }; - } - [Fact] - public void AartsTest20230618() - { - //aarts aarts F|Cy9!&w \hostname\gamemode\hostport\hostname\gamename\gametype\gamever\mapname\numplayers\maxplayers\gamemode\password\groupid\mapsessiontype\mapids\internet - var raw = new byte[] { 0x00, 0xB8, 0x00, 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x61, 0x61, 0x72, 0x74, 0x73, 0x00, 0x61, 0x61, 0x72, 0x74, 0x73, 0x00, 0x46, 0x7C, 0x43, 0x79, 0x39, 0x21, 0x26, 0x77, 0x00, 0x5C, 0x68, 0x6F, 0x73, 0x74, 0x6E, 0x61, 0x6D, 0x65, 0x5C, 0x67, 0x61, 0x6D, 0x65, 0x6D, 0x6F, 0x64, 0x65, 0x5C, 0x68, 0x6F, 0x73, 0x74, 0x70, 0x6F, 0x72, 0x74, 0x5C, 0x68, 0x6F, 0x73, 0x74, 0x6E, 0x61, 0x6D, 0x65, 0x5C, 0x67, 0x61, 0x6D, 0x65, 0x6E, 0x61, 0x6D, 0x65, 0x5C, 0x67, 0x61, 0x6D, 0x65, 0x74, 0x79, 0x70, 0x65, 0x5C, 0x67, 0x61, 0x6D, 0x65, 0x76, 0x65, 0x72, 0x5C, 0x6D, 0x61, 0x70, 0x6E, 0x61, 0x6D, 0x65, 0x5C, 0x6E, 0x75, 0x6D, 0x70, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x73, 0x5C, 0x6D, 0x61, 0x78, 0x70, 0x6C, 0x61, 0x79, 0x65, 0x72, 0x73, 0x5C, 0x67, 0x61, 0x6D, 0x65, 0x6D, 0x6F, 0x64, 0x65, 0x5C, 0x70, 0x61, 0x73, 0x73, 0x77, 0x6F, 0x72, 0x64, 0x5C, 0x67, 0x72, 0x6F, 0x75, 0x70, 0x69, 0x64, 0x5C, 0x6D, 0x61, 0x70, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6F, 0x6E, 0x74, 0x79, 0x70, 0x65, 0x5C, 0x6D, 0x61, 0x70, 0x69, 0x64, 0x73, 0x5C, 0x69, 0x6E, 0x74, 0x65, 0x72, 0x6E, 0x65, 0x74, 0x00, 0x00, 0x00, 0x00, 0x04 }; - var client = MockObject.CreateClient() as ITestClient; - client.TestReceived(raw); - } - } -} diff --git a/src/Servers/ServerBrowser/test/V2/MockObject.cs b/src/Servers/ServerBrowser/test/V2/MockObject.cs deleted file mode 100644 index 78a4d16af..000000000 --- a/src/Servers/ServerBrowser/test/V2/MockObject.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Net; -using Moq; -using UniSpy.Server.ServerBrowser.V2.Application; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.ServerBrowser.V2.Test -{ - public static class MockObject - { - public static IClient QRClient = QueryReport.V2.Test.MockObject.CreateClient(); - public static IClient SBClient = CreateClient(); - - public static Client CreateClient(string ipAddress = "192.168.1.2", int port = 9999) - { - var managerMock = new Mock(); - var connectionMock = new Mock(); - connectionMock.Setup(s => s.RemoteIPEndPoint).Returns(new IPEndPoint(IPAddress.Parse(ipAddress), port)); - connectionMock.Setup(s => s.Manager).Returns(managerMock.Object); - connectionMock.Setup(s => s.ConnectionType).Returns(NetworkConnectionType.Tcp); - var serverMock = new ServerBrowser.V2.Application.Server(managerMock.Object); - - return new Client(connectionMock.Object, serverMock); - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/test/V2/PeerRoomTest.cs b/src/Servers/ServerBrowser/test/V2/PeerRoomTest.cs deleted file mode 100644 index 829396781..000000000 --- a/src/Servers/ServerBrowser/test/V2/PeerRoomTest.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UniSpy.Server.Chat.Abstraction.Interface; -using UniSpy.Server.Chat.Aggregate; -using Xunit; - -namespace UniSpy.Server.ServerBrowser.Test.V2 -{ - public class PeerRoomTest - { - [Fact] - public void TestName() - { - var client = Chat.Test.MockObject.CreateClient(); - var chan = new Channel("Experts", (IShareClient)client); - Chat.Application.StorageOperation.Persistance.UpdateChannel(chan); - } - } -} \ No newline at end of file diff --git a/src/Servers/ServerBrowser/test/V2/RequestTest.cs b/src/Servers/ServerBrowser/test/V2/RequestTest.cs deleted file mode 100644 index 9ccf8ddde..000000000 --- a/src/Servers/ServerBrowser/test/V2/RequestTest.cs +++ /dev/null @@ -1,36 +0,0 @@ -using UniSpy.Server.ServerBrowser.V2.Enumerate; -using UniSpy.Server.ServerBrowser.V2.Contract.Request; -using Xunit; - -namespace UniSpy.Server.ServerBrowser.V2.Test -{ - public class RequestTest - { - [Fact] - public void ServerListTest() - { - var raw = new byte[]{0x00, 0x55, - 0x00, //command name - 0x01, 0x03, 0x00, 0x00, 0x00, - 0x00, 0x67, 0x6d, 0x74, 0x65, 0x73, 0x74, 0x00, - 0x67, 0x6d, 0x74, 0x65, 0x73, 0x74, 0x00, 0x6e, - 0x29, 0x29, 0x34, 0x31, 0x58, 0x4d, 0x36, 0x00, - 0x5c, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, - 0x65, 0x5c, 0x67, 0x61, 0x6d, 0x65, 0x74, 0x79, - 0x70, 0x65, 0x5c, 0x6d, 0x61, 0x70, 0x6e, 0x61, - 0x6d, 0x65, 0x5c, 0x6e, 0x75, 0x6d, 0x70, 0x6c, - 0x61, 0x79, 0x65, 0x72, 0x73, 0x5c, 0x6d, 0x61, - 0x78, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, - 0x00, 0x00, 0x00, 0x00, 0x00}; - var request = new ServerListRequest(raw); - request.Parse(); - Assert.Equal(RequestType.ServerListRequest, request.CommandName); - - raw = new byte[] { 0x00, 0x58, - 0x00, - 0x01, 0x03, 0x00, 0x00, 0x00, 0x00, 0x67, 0x6d, 0x74, 0x65, 0x73, 0x74, 0x00, 0x67, 0x6d, 0x74, 0x65, 0x73, 0x74, 0x00, 0x42, 0x46, 0x5d, 0x6c, 0x6b, 0x22, 0x2c, 0x35, 0x31, 0x32, 0x33, 0x00, 0x5c, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x5c, 0x6e, 0x75, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x5c, 0x6d, 0x61, 0x78, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x5c, 0x6d, 0x61, 0x70, 0x6e, 0x61, 0x6d, 0x65, 0x5c, 0x67, 0x61, 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00 }; - request = new ServerListRequest(raw); - request.Parse(); - } - } -} diff --git a/src/Servers/WebServer/src/Abstraction/CmdHandlerBase.cs b/src/Servers/WebServer/src/Abstraction/CmdHandlerBase.cs deleted file mode 100644 index 7a2bbd156..000000000 --- a/src/Servers/WebServer/src/Abstraction/CmdHandlerBase.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.WebServer.Application; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.WebServer.Abstraction -{ - public abstract class CmdHandlerBase : UniSpy.Server.Core.Abstraction.BaseClass.CmdHandlerBase - { - protected new Client _client => (Client)base._client; - protected CmdHandlerBase(IClient client, IRequest request) : base(client, request) - { - } - } -} diff --git a/src/Servers/WebServer/src/Abstraction/RequestBase.cs b/src/Servers/WebServer/src/Abstraction/RequestBase.cs deleted file mode 100644 index bcaa0beba..000000000 --- a/src/Servers/WebServer/src/Abstraction/RequestBase.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System.Xml.Linq; - -namespace UniSpy.Server.WebServer.Abstraction -{ - public abstract class RequestBase : UniSpy.Server.Core.Abstraction.BaseClass.RequestBase - { - public new string RawRequest => (string)base.RawRequest; - protected XElement _contentElement { get; private set; } - public RequestBase(string rawRequest) : base(rawRequest) - { - } - - protected RequestBase() - { - } - public override void Parse() - { - dynamic xelements = XElement.Parse(RawRequest); - _contentElement = xelements.FirstNode.FirstNode; - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Abstraction/ResponseBase.cs b/src/Servers/WebServer/src/Abstraction/ResponseBase.cs deleted file mode 100644 index 63020e006..000000000 --- a/src/Servers/WebServer/src/Abstraction/ResponseBase.cs +++ /dev/null @@ -1,18 +0,0 @@ -namespace UniSpy.Server.WebServer.Abstraction -{ - public abstract class ResponseBase : UniSpy.Server.Core.Abstraction.BaseClass.ResponseBase - { - protected SoapEnvelopBase _content { get; set; } - public new string SendingBuffer { get => (string)base.SendingBuffer; set => base.SendingBuffer = value; } - public ResponseBase(RequestBase request, ResultBase result) : base(request, result) - { - } - public override void Build() - { - // Because the response is kind of soap object, so we did not use SoapXElement as a soap object - // SoapXElement only acts like XElement - // !! call at last - SendingBuffer = _content.ToString(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Abstraction/ResultBase.cs b/src/Servers/WebServer/src/Abstraction/ResultBase.cs deleted file mode 100644 index 542bfd055..000000000 --- a/src/Servers/WebServer/src/Abstraction/ResultBase.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace UniSpy.Server.WebServer.Abstraction -{ - public abstract class ResultBase : UniSpy.Server.Core.Abstraction.BaseClass.ResultBase - { - protected ResultBase() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Abstraction/SoapEnvelopBase.cs b/src/Servers/WebServer/src/Abstraction/SoapEnvelopBase.cs deleted file mode 100644 index 2f7a062c8..000000000 --- a/src/Servers/WebServer/src/Abstraction/SoapEnvelopBase.cs +++ /dev/null @@ -1,79 +0,0 @@ -using System.IO; -using System.Linq; -using System.Text; -using System.Xml.Linq; - -namespace UniSpy.Server.WebServer.Abstraction -{ - public abstract class SoapEnvelopBase - { - private static XDeclaration _declaration = new XDeclaration("1.0", "utf-8", null); - public static XNamespace SoapEnvelopNamespace = "http://schemas.xmlsoap.org/soap/envelope/"; - public XElement SoapHeader = new XElement(SoapEnvelopNamespace + "Envelope", - new XAttribute(XNamespace.Xmlns + "SOAP-ENV", SoapEnvelopNamespace), - new XAttribute(XNamespace.Xmlns + "SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/"), - new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"), - new XAttribute(XNamespace.Xmlns + "xsd", "http://www.w3.org/2001/XMLSchema")); - private XNamespace _bodyNamespace; - public XDocument Content { get; private set; } - public XElement Body => Content.Root.Descendants().First(p => p.Name.LocalName == "Body"); - public XElement CurrentElement { get; private set; } - public SoapEnvelopBase(string nsShortName, XNamespace bodyNamespace) - { - _bodyNamespace = bodyNamespace; - Content = new XDocument(_declaration); - Content.Declaration = _declaration; - SoapHeader.Add(new XAttribute(XNamespace.Xmlns + nsShortName, _bodyNamespace)); - Content.Add(SoapHeader); - Content.Root.Add(new XElement(SoapEnvelopNamespace + "Body")); - CurrentElement = Body; - } - public void FinishAddSubElement() - { - CurrentElement = Body; - } - public void ChangeToElement(string name) - { - CurrentElement = Content.Descendants().First(p => p.Name.LocalName == name); - } - public void BackToParentElement() - { - CurrentElement = CurrentElement.Parent; - } - /// - /// Add the element and go inside it - /// - public virtual void Add(string name) - { - CurrentElement.Add(new XElement(_bodyNamespace + name)); - ChangeToElement(name); - } - /// - /// Add the element and stay current position - /// - public virtual void Add(string name, object value) - { - CurrentElement.Add(new XElement(_bodyNamespace + name, value)); - } - - public override string ToString() - { - using (var writer = new MyStringWriter()) - { - Content.Save(writer, SaveOptions.DisableFormatting); - return writer.ToString(); - } - } - - private class MyStringWriter : StringWriter - { - public MyStringWriter() - { - Encoding = Encoding.UTF8; - } - - public override Encoding Encoding { get; } - } - } - -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Aggregate/SakeArrayObject.cs b/src/Servers/WebServer/src/Aggregate/SakeArrayObject.cs deleted file mode 100644 index b5b494a18..000000000 --- a/src/Servers/WebServer/src/Aggregate/SakeArrayObject.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections.Generic; -namespace UniSpy.Server.WebServer.Aggregate -{ - - public record RecordFieldObject : FieldObject - { - public string FieldValue { get; private set; } - public RecordFieldObject(string fieldValue, string fieldName, string filedType) : base(fieldName, filedType) - { - FieldValue = fieldValue; - } - } - - public record FieldObject - { - - public string FieldName { get; private set; } - public string FiledType { get; private set; } - - public FieldObject(string fieldName, string filedType) - { - this.FieldName = fieldName; - this.FiledType = filedType; - } - } -} diff --git a/src/Servers/WebServer/src/Aggregate/SoapXElement.cs b/src/Servers/WebServer/src/Aggregate/SoapXElement.cs deleted file mode 100644 index 80edd0dc8..000000000 --- a/src/Servers/WebServer/src/Aggregate/SoapXElement.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Xml.Linq; - -namespace UniSpy.Server.WebServer.Aggregate -{ - public class SoapXElement : XElement - { - private static XDeclaration _declaration = new XDeclaration("1.0", "utf-8", null); - public static XNamespace SoapEnvelopNamespace = "http://schemas.xmlsoap.org/soap/envelope/"; - public static readonly XElement SoapHeader = new XElement(SoapEnvelopNamespace + "Envelope", - new XAttribute(XNamespace.Xmlns + "SOAP-ENV", SoapEnvelopNamespace), - new XAttribute(XNamespace.Xmlns + "SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/"), - new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"), - new XAttribute(XNamespace.Xmlns + "xsd", "http://www.w3.org/2001/XMLSchema")); - public static XNamespace SakeNamespace = "http://gamespy.net/sake"; - public static XElement SakeSoapHeader - { - get - { - var element = new XElement(SoapHeader); - element.Add(new XAttribute(XNamespace.Xmlns + "ns1", SakeNamespace)); - return element; - } - } - public static XNamespace AuthNamespace = "http://gamespy.net/AuthService/"; - public static XElement AuthSoapHeader - { - get - { - var element = new XElement(SoapHeader); - element.Add(new XAttribute(XNamespace.Xmlns + "ns1", AuthNamespace)); - return element; - } - } - public static XNamespace Direct2GameNamespace = "http://gamespy.net/commerce/2009/02"; - public static XElement Direct2GameSoapHeader - { - get - { - var element = new XElement(SoapHeader); - element.Add(new XAttribute(XNamespace.Xmlns + "gsc", Direct2GameNamespace)); - return element; - } - } - public SoapXElement(XElement other) : base(other) { } - public SoapXElement(XName name) : base(name) { } - public SoapXElement(XName name, object content) : base(name, content) { } - public SoapXElement(XName name, params object[] content) : base(name, content) { } - public List GetArrayObjects() - { - throw new NotImplementedException(); - } - public XElement SetArrayObjects(List objects) - { - throw new NotImplementedException(); - } - - public override string ToString() - { - return _declaration + Environment.NewLine + base.ToString(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Aggregate/WebEndpoints.cs b/src/Servers/WebServer/src/Aggregate/WebEndpoints.cs deleted file mode 100644 index ac30593cd..000000000 --- a/src/Servers/WebServer/src/Aggregate/WebEndpoints.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Generic; - -namespace UniSpy.Server.WebServer.Aggregate -{ - public class WebEndpoints - { - //Contains all endpoints of the web server - public static readonly List AvailableEndpoints = new List - { - // WSMotd Service - "/motd/motd.asp", - "/motd/vercheck.asp", - // WSAuth Service - "/AuthService/AuthService.asmx", - // WSSake Service - "/SakeStorageServer/StorageServer.asmx", - "/SakeFileServer/download.aspx", - "/SakeFileServer/upload.aspx", - // WSAtlas Service - "/CompetitionService/CompetitionService.asmx", - // WSDirect2Game Service - "/commerce/1.1/catalogservice.svc", - "/commerce/1.1/accountservice.svc", - "/commerce/1.1/purchaseservice.svc", - // WSIngameAd Service - "/IGNAdServer/service1.asmx", - // WSRacing Service - "/RaceService/NintendoRacingService.asmx", - }; - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Application/Client.cs b/src/Servers/WebServer/src/Application/Client.cs deleted file mode 100644 index 299552a4d..000000000 --- a/src/Servers/WebServer/src/Application/Client.cs +++ /dev/null @@ -1,29 +0,0 @@ -using UniSpy.Server.WebServer.Handler; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.WebServer.Application -{ - public sealed class Client : ClientBase - { - public Client(IConnection connection, IServer server) : base(connection, server) - { - Info = new ClientInfo(); - } - - protected override ISwitcher CreateSwitcher(object buffer) => new CmdSwitcher(this, (IHttpRequest)buffer); - - protected override void OnReceived(object buffer) - { - var rq = (IHttpRequest)buffer; - - if (rq.Body.Length == 0 || rq.Body == "") - { - this.LogWarn($"ignore empty message"); - return; - } - base.OnReceived(buffer); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Application/ClientInfo.cs b/src/Servers/WebServer/src/Application/ClientInfo.cs deleted file mode 100644 index 3d0fe8c81..000000000 --- a/src/Servers/WebServer/src/Application/ClientInfo.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Extension; - -namespace UniSpy.Server.WebServer.Application -{ - /// - /// Note!! the public exponent on SDK must set to 000001 - /// because the multiple inverse of 1 is 1 - /// data^1 mod n = data - /// enc^1 mod n = data^1^1 mod n = data - /// I do not want let our server to compute the rsa encryption so I made this trick - /// I am very glad that my major is cryptography during my master degree - /// - public sealed class ClientInfo : ClientInfoBase - { - public const string PeerKeyExponent = "010001"; - public const string PeerKeyModulus = "aefb5064bbd1eb632fa8d57aab1c49366ce0ee3161cbef19f2b7971b63b811790ecbf6a47b34c55f65a0766b40c261c5d69c394cd320842dd2bccba883d30eae8fdba5d03b21b09bfc600dcb30b1b2f3fbe8077630b006dcb54c4254f14891762f72e7bbfe743eb8baf65f9e8c8d11ebe46f6b59e986b4c394cfbc2c8606e29f"; - /// - /// The exponent generated for user - /// exponent is 000001 - /// - // public static byte[] PeerKeyExponentByte => BitConverter.GetBytes(int.Parse(PeerKeyExponent)); - /// - /// The modulus generated for user aefb5064bbd1eb632fa8d57aab1c49366ce0ee3161cbef19f2b7971b63b811790ecbf6a47b34c55f65a0766b40c261c5d69c394cd320842dd2bccba883d30eae8fdba5d03b21b09bfc600dcb30b1b2f3fbe8077630b006dcb54c4254f14891762f72e7bbfe743eb8baf65f9e8c8d11ebe46f6b59e986b4c394cfbc2c8606e29f - /// - // public static byte[] PeerKeyModulusBytes => PeerKeyModulus.FromHexStringToBytes(); - /// - /// should be 256 characters - /// - public const string ServerData = - "95980bf5011ce73f2866b995a272420c36f1e8b4ac946f0b5bfe87c9fef0811036da00cfa85e77e00af11c924d425ec06b1dd052feab1250376155272904cbf9da831b0ce3d52964424c0a426b869e2c0ad11ffa3e70496e27ea250adb707a96b3496bff190eafc0b6b9c99db75b02c2a822bb1b5b3d954e7b2c0f9b1487e3e1"; - public static int ExpireTime => (int)((DateTimeOffset)DateTime.UtcNow + TimeSpan.FromMinutes(5)).ToUnixTimeSeconds(); - public const string SignaturePreFix = "0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003020300C06082A864886F70D020505000410"; - - public readonly byte[] PrivateKeyD = new byte[128] { 0x96, 0xE2, 0xF4, 0xF9, 0x7C, 0x78, 0x81, 0x0C, 0x13, 0xE2, 0xA4, 0xCA, 0xC1, 0x12, 0x44, 0xBB, 0x5B, 0x37, 0xC5, 0x4E, 0x98, 0x20, 0xF1, 0x3F, 0xE9, 0xB5, 0x53, 0xDA, 0x10, 0xB1, 0xE8, 0xEF, 0x8E, 0xB0, 0x3F, 0x87, 0x68, 0x1B, 0x0E, 0x62, 0x4D, 0x1A, 0x8D, 0xE9, 0x17, 0x4C, 0xBE, 0xE5, 0xB8, 0xED, 0x92, 0xE4, 0xBE, 0x74, 0xF8, 0x6C, 0x30, 0x38, 0xCD, 0x7C, 0x1A, 0x20, 0xB9, 0xA3, 0xDB, 0x1D, 0x49, 0x22, 0x62, 0x87, 0x38, 0x68, 0xFB, 0xA0, 0x8E, 0x1E, 0xAB, 0x5C, 0xBA, 0x86, 0x3F, 0x8F, 0xDB, 0xF4, 0x5E, 0xEA, 0x61, 0x4B, 0xBF, 0x6C, 0xFC, 0x47, 0x00, 0x81, 0x44, 0x2A, 0x97, 0x78, 0x7E, 0xB6, 0xEC, 0xA7, 0x1C, 0x48, 0x96, 0x81, 0x6C, 0x2A, 0x62, 0x72, 0x4C, 0x0E, 0x8C, 0xAA, 0xEE, 0xAB, 0x72, 0x78, 0xC2, 0x55, 0x4A, 0x13, 0x80, 0x94, 0x6E, 0xED, 0x21, 0x29 }; - public readonly byte[] Modulus = new byte[128] { 0xA9, 0x3E, 0x00, 0x19, 0x2C, 0x4A, 0x98, 0x69, 0xD7, 0x41, 0x9A, 0xFF, 0x66, 0x2E, 0xCA, 0xD6, 0xC8, 0xB9, 0x99, 0x09, 0xFD, 0xD0, 0xE7, 0xF8, 0xCA, 0xDD, 0x15, 0x32, 0xE8, 0xE3, 0x59, 0x37, 0x40, 0x83, 0xDA, 0xB8, 0xBE, 0x71, 0x7F, 0x60, 0x91, 0x60, 0xCD, 0x6A, 0x54, 0x11, 0xBE, 0xD7, 0x92, 0x7A, 0xD3, 0xB5, 0xC0, 0x0C, 0x4C, 0x4B, 0x34, 0x76, 0x71, 0xF2, 0x3F, 0xE0, 0x1E, 0xBB, 0x2F, 0x83, 0x4B, 0xD8, 0xCA, 0x27, 0xC3, 0x55, 0x3E, 0x1E, 0x6B, 0xC2, 0x85, 0xAF, 0xC6, 0x3E, 0xC0, 0xE1, 0x1F, 0x59, 0xCA, 0xF6, 0xAC, 0x37, 0x5F, 0x4B, 0x0E, 0xB8, 0x2A, 0x4D, 0xA2, 0x2C, 0x0C, 0x10, 0x1D, 0x09, 0x13, 0x1A, 0x7C, 0x42, 0x0D, 0x4C, 0xC4, 0xD0, 0x95, 0x62, 0x4C, 0x42, 0xBC, 0xD8, 0xD7, 0x19, 0x16, 0xC8, 0xDC, 0x00, 0x48, 0x08, 0x6D, 0x74, 0x6A, 0x31, 0x55, 0xC7 }; - public readonly byte[] Exponent = new byte[3] { 0x01, 0x00, 0x01 }; - public ClientInfo() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Application/Program.cs b/src/Servers/WebServer/src/Application/Program.cs deleted file mode 100755 index 89f286fe1..000000000 --- a/src/Servers/WebServer/src/Application/Program.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System; -using UniSpy.Server.Core.Logging; - -namespace UniSpy.Server.WebServer.Application -{ - public class Program - { - static void Main(string[] args) - { - try - { - new ServerLauncher().Start(); - Console.WriteLine("Press < Q > to exit. "); - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - catch (System.Exception e) - { - UniSpy.Exception.HandleException(e); - } - finally - { - while (Console.ReadKey().Key != ConsoleKey.Q) { } - } - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Application/Server.cs b/src/Servers/WebServer/src/Application/Server.cs deleted file mode 100644 index a4c0f3865..000000000 --- a/src/Servers/WebServer/src/Application/Server.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Net; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Network.Http.Server; - -namespace UniSpy.Server.WebServer.Application -{ - public sealed class Server : ServerBase - { - static Server() - { - _name = "WebServer"; - } - public Server() { } - - public Server(IConnectionManager manager) : base(manager) { } - - protected override IClient CreateClient(IConnection connection) => new Client(connection, this); - - protected override IConnectionManager CreateConnectionManager(IPEndPoint endPoint) => new HttpConnectionManager(endPoint); - - protected override IClient HandleConnectionInitialization(IConnection connection) - { - var client = CreateClient(connection); - return client; - } - - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Application/ServerLauncher.cs b/src/Servers/WebServer/src/Application/ServerLauncher.cs deleted file mode 100644 index 0d01c5b71..000000000 --- a/src/Servers/WebServer/src/Application/ServerLauncher.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.BaseClass.Factory; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.WebServer.Application -{ - public sealed class ServerLauncher : ServerLauncherBase - { - public static IServer Server => ServerInstances[0]; - protected override List LaunchNetworkService() => new List { new Server() }; - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Dockerfile b/src/Servers/WebServer/src/Dockerfile deleted file mode 100644 index 522703b37..000000000 --- a/src/Servers/WebServer/src/Dockerfile +++ /dev/null @@ -1,20 +0,0 @@ -FROM mcr.microsoft.com/dotnet/runtime:6.0 AS base -WORKDIR /app -EXPOSE 80 - -FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build -WORKDIR /src -COPY ["src/Servers/WebServer/src/UniSpy.Server.WebServer.csproj", "src/Servers/WebServer/src/"] -COPY ["src/Libraries/Core/src/UniSpy.Server.Core.csproj", "src/Libraries/Core/src/"] -RUN dotnet restore "src/Servers/WebServer/src/UniSpy.Server.WebServer.csproj" -COPY . . -WORKDIR "/src/src/Servers/WebServer/src" -RUN dotnet build "UniSpy.Server.WebServer.csproj" -c Release -o /app/build - -FROM build AS publish -RUN dotnet publish "UniSpy.Server.WebServer.csproj" -c Release -o /app/publish - -FROM base AS final -WORKDIR /app -COPY --from=publish /app/publish . -ENTRYPOINT ["dotnet", "UniSpy.Server.WebServer.dll"] \ No newline at end of file diff --git a/src/Servers/WebServer/src/Exception/Exception.cs b/src/Servers/WebServer/src/Exception/Exception.cs deleted file mode 100644 index 5e481c50a..000000000 --- a/src/Servers/WebServer/src/Exception/Exception.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace UniSpy.Server.WebServer -{ - public class Exception : UniSpy.Exception - { - public Exception(string message) : base(message) - { - } - - public Exception(string message, System.Exception innerException) : base(message, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Handler/CmdSwitcher.cs b/src/Servers/WebServer/src/Handler/CmdSwitcher.cs deleted file mode 100755 index d6fd5c7ce..000000000 --- a/src/Servers/WebServer/src/Handler/CmdSwitcher.cs +++ /dev/null @@ -1,121 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Xml.Linq; -using UniSpy.Server.WebServer.Module.Sake.Contract.Request; -using UniSpy.Server.Core.Abstraction.BaseClass; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.WebServer.Module.Sake.Handler; -using UniSpy.Server.WebServer.Module.Auth.Handler; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; -using UniSpy.Server.WebServer.Module.Direct2Game.Contract.Request; -using UniSpy.Server.WebServer.Module.Direct2Game.Handler; -using UniSpy.Server.Core.Logging; -using UniSpy.Server.WebServer.Application; - -namespace UniSpy.Server.WebServer.Handler -{ - public class CmdSwitcher : CmdSwitcherBase - { - private new IHttpRequest _rawRequest => (IHttpRequest)base._rawRequest; - private new Client _client => (Client)base._client; - public CmdSwitcher(Client client, IHttpRequest rawRequest) : base(client, rawRequest) - { - } - - protected override void ProcessRawRequest() - { - // var uri = new Uri(_rawRequest.Url); - // if (WebEndpoints.AvailableEndpoints.Contains(uri.LocalPath)) - // { - // throw new UniSpy.Exception($"Invalid http path access:{_rawRequest.Url}"); - // } - try - { - dynamic xelements = XElement.Parse(_rawRequest.Body); - var name = xelements.FirstNode.FirstNode.Name.LocalName; - _requests.Add(new KeyValuePair(name, _rawRequest.Body)); - } - catch (System.Exception ex) - { - throw new WebServer.Exception("xml parsing error", ex); - } - } - - protected override IHandler CreateCmdHandlers(object name, object rawRequest) - { - switch ((string)name) - { - #region Altas - case "CreateMatchlessSession": - case "CreateSession": - case "SetReportIntention": - case "SubmitReport": - throw new NotImplementedException(); - #endregion - #region Auth - case "LoginProfile": - return new LoginProfileHandler(_client, new LoginProfileRequest((string)rawRequest)); - case "LoginProfileWithGameId": - return new LoginProfileWithGameIdHandler(_client, new LoginProfileWithGameIdRequest((string)rawRequest)); - case "LoginRemoteAuth": - return new LoginRemoteAuthHandler(_client, new LoginRemoteAuthRequest((string)rawRequest)); - case "LoginRemoteAuthWithGameId": - return new LoginRemoteAuthWithGameIdHandler(_client, new LoginRemoteAuthWithGameIdRequest((string)rawRequest)); - case "LoginUniqueNick": - return new LoginUniqueNickHandler(_client, new LoginUniqueNickRequest((string)rawRequest)); - case "LoginUniqueNickWithGameId": - return new LoginUniqueNickWithGameIdHandler(_client, new LoginUniqueNickWithGameIdRequest((string)rawRequest)); - #endregion - #region Direct2Game - case "GetStoreAvailability": - return new GetStoreAvailabilityHandler(_client, new GetStoreAvailabilityRequest((string)rawRequest)); - - case "GetPurchaseHistory": - return new GetPurchaseHistoryHandler(_client, new GetPurchaseHistoryRequest((string)rawRequest)); - case "GetTargettedAd": - throw new NotImplementedException(); - #endregion - #region InGameAd - case "ReportAdUsage": - throw new NotImplementedException(); - #endregion - #region PatchingAndTracking - case "Motd": - case "Vercheck": - throw new NotImplementedException(); - #endregion - #region Racing - case "GetContestData": - case "GetFriendRankings": - case "GetRegionalData": - case "GetTenAboveRankings": - case "GetTopTenRankings": - case "SubmitScores": - throw new NotImplementedException(); - #endregion - #region Sake - case "CreateRecord": - return new CreateRecordHandler(_client, new CreateRecordRequest((string)rawRequest)); - case "DeleteRecord": - throw new NotImplementedException(); - case "GetMyRecords": - return new GetMyRecordsHandler(_client, new GetMyRecordsRequest((string)rawRequest)); - case "GetRandomRecords": - throw new NotImplementedException(); - case "GetRecordLimit": - throw new NotImplementedException(); - case "RateRecord": - throw new NotImplementedException(); - case "SearchForRecords": - return new SearchForRecordsHandler(_client, new SearchForRecordsRequest((string)rawRequest)); - case "UpdateRecord": - // return new UpdateRecordHandler(_client, new UpdateRecordRequest((string)rawRequest)); - throw new NotImplementedException(); - #endregion - default: - _client.LogError($"Unkown {(string)name} request received"); - return null; - } - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Atlas/Contract/Request/CreateMatchlessSessionRequest.cs b/src/Servers/WebServer/src/Module/Atlas/Contract/Request/CreateMatchlessSessionRequest.cs deleted file mode 100644 index a34b057f1..000000000 --- a/src/Servers/WebServer/src/Module/Atlas/Contract/Request/CreateMatchlessSessionRequest.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.Atlas.Contract.Request -{ - - public class CreateMatchlessSessionRequest : RequestBase - { - public string Certificate { get; set; } - public string Proof { get; set; } - public int GameId { get; set; } - public CreateMatchlessSessionRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - var certificate = _contentElement.Descendants().Where(p => p.Name.LocalName == "certificate").First().Value; - Certificate = certificate; - var proof = _contentElement.Descendants().Where(p => p.Name.LocalName == "proof").First().Value; - Proof = proof; - var gameid = _contentElement.Descendants().Where(p => p.Name.LocalName == "gameid").First().Value; - GameId = int.Parse(gameid); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Atlas/Contract/Request/CreateSessionRequest.cs b/src/Servers/WebServer/src/Module/Atlas/Contract/Request/CreateSessionRequest.cs deleted file mode 100644 index edee2e652..000000000 --- a/src/Servers/WebServer/src/Module/Atlas/Contract/Request/CreateSessionRequest.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.Atlas.Contract.Request -{ - - public class CreateSessionRequest : RequestBase - { - public string Certificate { get; set; } - public string Proof { get; set; } - public int GameId { get; set; } - public CreateSessionRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - var certificate = _contentElement.Descendants().Where(p => p.Name.LocalName == "certificate").First().Value; - Certificate = certificate; - var proof = _contentElement.Descendants().Where(p => p.Name.LocalName == "proof").First().Value; - Proof = proof; - var gameid = _contentElement.Descendants().Where(p => p.Name.LocalName == "gameid").First().Value; - GameId = int.Parse(gameid); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Atlas/Contract/Request/SetReportIntentionRequest.cs b/src/Servers/WebServer/src/Module/Atlas/Contract/Request/SetReportIntentionRequest.cs deleted file mode 100644 index 82ea60e56..000000000 --- a/src/Servers/WebServer/src/Module/Atlas/Contract/Request/SetReportIntentionRequest.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.Atlas.Contract.Request -{ - - public class SetReportIntentionRequest : RequestBase - { - public string Certificate { get; set; } - public string Proof { get; set; } - public int CsId { get; set; } - public int CcId { get; set; } - public int GameId { get; set; } - public string Authoritative { get; set; } - public SetReportIntentionRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - var certificate = _contentElement.Descendants().Where(p => p.Name.LocalName == "certificate").First().Value; - Certificate = certificate; - var proof = _contentElement.Descendants().Where(p => p.Name.LocalName == "proof").First().Value; - Proof = proof; - var csid = _contentElement.Descendants().Where(p => p.Name.LocalName == "csid").First().Value; - CsId = int.Parse(csid); - var ccid = _contentElement.Descendants().Where(p => p.Name.LocalName == "ccid").First().Value; - CcId = int.Parse(ccid); - var gameid = _contentElement.Descendants().Where(p => p.Name.LocalName == "gameid").First().Value; - GameId = int.Parse(gameid); - var authoritative = _contentElement.Descendants().Where(p => p.Name.LocalName == "authoritative").First().Value; - Authoritative = authoritative; - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Atlas/Contract/Request/SubmitReportRequest.cs b/src/Servers/WebServer/src/Module/Atlas/Contract/Request/SubmitReportRequest.cs deleted file mode 100644 index a7cfa35e2..000000000 --- a/src/Servers/WebServer/src/Module/Atlas/Contract/Request/SubmitReportRequest.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.Atlas.Contract.Request -{ - - public class SubmitReportRequest : RequestBase - { - public string Certificate { get; set; } - public string Proof { get; set; } - public int CsId { get; set; } - public int CcId { get; set; } - public int GameId { get; set; } - public string Authoritative { get; set; } - public SubmitReportRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - var certificate = _contentElement.Descendants().Where(p => p.Name.LocalName == "certificate").First().Value; - Certificate = certificate; - var proof = _contentElement.Descendants().Where(p => p.Name.LocalName == "proof").First().Value; - Proof = proof; - var csid = _contentElement.Descendants().Where(p => p.Name.LocalName == "csid").First().Value; - CsId = int.Parse(csid); - var ccid = _contentElement.Descendants().Where(p => p.Name.LocalName == "ccid").First().Value; - CcId = int.Parse(ccid); - var gameid = _contentElement.Descendants().Where(p => p.Name.LocalName == "gameid").First().Value; - GameId = int.Parse(gameid); - var authoritative = _contentElement.Descendants().Where(p => p.Name.LocalName == "authoritative").First().Value; - Authoritative = authoritative; - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginRequestBase.cs b/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginRequestBase.cs deleted file mode 100644 index 99c7f972d..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginRequestBase.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.Auth.Abstraction -{ - public abstract class LoginRequestBase : RequestBase - { - protected LoginRequestBase(string rawRequest) : base(rawRequest) - { - } - - public int Version { get; private set; } - public int PartnerCode { get; private set; } - public int NamespaceId { get; private set; } - public override void Parse() - { - base.Parse(); - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "version")) - { - throw new Auth.Exception("version is missing from the request"); - } - var version = _contentElement.Descendants().First(p => p.Name.LocalName == "version").Value; - Version = int.Parse(version); - - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "partnercode")) - { - throw new Auth.Exception("partnercode is missing from the request"); - } - var partnercode = _contentElement.Descendants().First(p => p.Name.LocalName == "partnercode").Value; - PartnerCode = int.Parse(partnercode); - - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "namespaceid")) - { - throw new Auth.Exception("namespaceid is missing from the request"); - } - var namespaceid = _contentElement.Descendants().First(p => p.Name.LocalName == "namespaceid").Value; - NamespaceId = int.Parse(namespaceid); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResponseBase.cs b/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResponseBase.cs deleted file mode 100644 index c6bcd2a4b..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResponseBase.cs +++ /dev/null @@ -1,68 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Security.Cryptography; -using System.Text; -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Application; -using UniSpy.Server.Core.Extension; - -namespace UniSpy.Server.WebServer.Module.Auth.Abstraction -{ - - public abstract class LoginResponseBase : ResponseBase - { - protected new LoginResultBase _result => (LoginResultBase)base._result; - protected new LoginRequestBase _request => (LoginRequestBase)base._request; - protected LoginResponseBase(RequestBase request, ResultBase result) : base(request, result) - { - } - - protected void BuildContext() - { - _content.Add("responseCode", "h"); - _content.Add("certificate"); - _content.Add("length", _result.Length); - _content.Add("version", _request.Version); - _content.Add("partnercode", _request.PartnerCode); - _content.Add("namespaceid", _request.NamespaceId); - _content.Add("userid", _result.UserId); - _content.Add("profileid", _result.ProfileId); - _content.Add("expiretime", ClientInfo.ExpireTime); - _content.Add("profilenick", _result.ProfileNick); - _content.Add("uniquenick", _result.UniqueNick); - _content.Add("cdkeyhash", _result.CdKeyHash); - _content.Add("peerkeymodulus", ClientInfo.PeerKeyModulus); - _content.Add("peerkeyexponent", ClientInfo.PeerKeyExponent); - _content.Add("serverdata", ClientInfo.ServerData); - - using (var md5 = MD5.Create()) - { - var dataToHash = new List(); - dataToHash.AddRange(BitConverter.GetBytes(_result.Length)); - dataToHash.AddRange(BitConverter.GetBytes(_request.Version)); - dataToHash.AddRange(BitConverter.GetBytes(_request.PartnerCode)); - dataToHash.AddRange(BitConverter.GetBytes(_request.NamespaceId)); - dataToHash.AddRange(BitConverter.GetBytes(_result.UserId)); - dataToHash.AddRange(BitConverter.GetBytes(_result.ProfileId)); - dataToHash.AddRange(BitConverter.GetBytes(ClientInfo.ExpireTime)); - dataToHash.AddRange(Encoding.ASCII.GetBytes(_result.ProfileNick)); - dataToHash.AddRange(Encoding.ASCII.GetBytes(_result.UniqueNick)); - dataToHash.AddRange(Encoding.ASCII.GetBytes(_result.CdKeyHash)); - - dataToHash.AddRange(ClientInfo.PeerKeyModulus.FromHexStringToBytes()); - dataToHash.AddRange(ClientInfo.PeerKeyExponent.FromHexStringToBytes()); - // dataToHash.AddRange(new byte[] { 0x01, 0x00, 0x01 }); - - // var exp = System.Numerics.BigInteger.Parse(ClientInfo.PeerKeyExponent,System.Globalization.NumberStyles.HexNumber); - // exp.ToByteArray() - // server data should be convert to bytes[128] then added to list - dataToHash.AddRange(ClientInfo.ServerData.FromHexStringToBytes()); - var hash = md5.ComputeHash(dataToHash.ToArray()); - var hashString = BitConverter.ToString(hash).Replace("-", string.Empty); - _content.Add("signature", ClientInfo.SignaturePreFix + hashString); - } - _content.BackToParentElement(); - _content.Add("peerkeyprivate", ClientInfo.PeerKeyExponent); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResultBase.cs b/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResultBase.cs deleted file mode 100644 index 067b6e449..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Abstraction/LoginResultBase.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Auth.Abstraction -{ - public abstract class LoginResultBase : ResultBase - { - public int? ResponseCode { get; set; } - public int Length { get; set; } - public int UserId { get; set; } - public int ProfileId { get; set; } - public string ProfileNick { get; set; } - public string UniqueNick { get; set; } - public string CdKeyHash { get; set; } - public LoginResultBase() - { - ResponseCode = 0; - Length = 303; - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Abstraction/ResponseBase.cs b/src/Servers/WebServer/src/Module/Auth/Abstraction/ResponseBase.cs deleted file mode 100644 index 8c916e1c3..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Abstraction/ResponseBase.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Contract; - -namespace UniSpy.Server.WebServer.Module.Auth.Abstraction -{ - public abstract class ResponseBase : WebServer.Abstraction.ResponseBase - { - public ResponseBase(RequestBase request, ResultBase result) : base(request, result) - { - _content = new AuthSoapEnvelope(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/AuthSoapEnvelope.cs b/src/Servers/WebServer/src/Module/Auth/Contract/AuthSoapEnvelope.cs deleted file mode 100644 index 9ab8742f4..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/AuthSoapEnvelope.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Xml.Linq; -using UniSpy.Server.WebServer.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Auth.Contract -{ - public sealed class AuthSoapEnvelope : SoapEnvelopBase - { - public static XNamespace AuthNamespace = "http://gamespy.net/AuthService/"; - public AuthSoapEnvelope() : base("ns1", AuthNamespace) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginProfileRequest.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginProfileRequest.cs deleted file mode 100644 index d76806cff..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginProfileRequest.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Module.Auth.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Request -{ - - public class LoginProfileRequest : LoginRequestBase - { - public string Email { get; private set; } - public string Uniquenick { get; private set; } - public string CDKey { get; private set; } - public string Password { get; private set; } - public LoginProfileRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "email")) - { - throw new Auth.Exception("email is missing from the request"); - } - Email = _contentElement.Descendants().First(p => p.Name.LocalName == "email").Value; - - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "uniquenick")) - { - throw new Auth.Exception("uniquenick is missing from the request"); - } - Uniquenick = _contentElement.Descendants().First(p => p.Name.LocalName == "uniquenick").Value; - - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "cdkey")) - { - throw new Auth.Exception("cdkey is missing from the request"); - } - CDKey = _contentElement.Descendants().First(p => p.Name.LocalName == "cdkey").Value; - - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "password")) - { - throw new Auth.Exception("password is missing from the request"); - } - Password = _contentElement.Descendants().First(p => p.Name.LocalName == "password").Value; - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginProfileWithGameIdRequest.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginProfileWithGameIdRequest.cs deleted file mode 100644 index 3e5077f4d..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginProfileWithGameIdRequest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Linq; - - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Request -{ - - public sealed class LoginProfileWithGameIdRequest : LoginProfileRequest - { - public int GameId { get; private set; } - public LoginProfileWithGameIdRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "gameid")) - { - throw new Auth.Exception("gameid is missing from the request"); - } - var gameid = _contentElement.Descendants().FirstOrDefault(p => p.Name.LocalName == "gameid").Value; - GameId = int.Parse(gameid); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginPs3CertRequest.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginPs3CertRequest.cs deleted file mode 100644 index 911dae6ae..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginPs3CertRequest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Module.Auth.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Request -{ - - public class LoginPs3CertRequest : LoginRequestBase - { - public string PS3cert { get; private set; } - public LoginPs3CertRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "npticket")) - { - throw new Auth.Exception("ps3cert is missing from the request"); - } - PS3cert = _contentElement.Descendants().First(p => p.Name.LocalName == "npticket").Value; - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginPs3CertWithGameIdRequest.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginPs3CertWithGameIdRequest.cs deleted file mode 100644 index c76898cff..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginPs3CertWithGameIdRequest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Linq; - - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Request -{ - - public sealed class LoginPs3CertWithGameIdRequest : LoginPs3CertRequest - { - public int? GameId { get; private set; } - public LoginPs3CertWithGameIdRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "gameid")) - { - throw new Auth.Exception("gameid is missing from the request"); - } - var gameid = _contentElement.Descendants().FirstOrDefault(p => p.Name.LocalName == "gameid").Value; - GameId = int.Parse(gameid); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginRemoteAuthRequest.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginRemoteAuthRequest.cs deleted file mode 100644 index 58c75f32f..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginRemoteAuthRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Module.Auth.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Request -{ - - public class LoginRemoteAuthRequest : LoginRequestBase - { - public string AuthToken { get; private set; } - public string Challenge { get; private set; } - public LoginRemoteAuthRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "authtoken")) - { - throw new Auth.Exception("authtoken is missing from the request"); - } - AuthToken = _contentElement.Descendants().First(p => p.Name.LocalName == "authtoken").Value; - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "challenge")) - { - throw new Auth.Exception("challenge is missing from the request"); - } - Challenge = _contentElement.Descendants().First(p => p.Name.LocalName == "challenge").Value; - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginRemoteAuthWithGameIdRequest.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginRemoteAuthWithGameIdRequest.cs deleted file mode 100644 index 67b68f463..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginRemoteAuthWithGameIdRequest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Linq; - - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Request -{ - - public class LoginRemoteAuthWithGameIdRequest : LoginRemoteAuthRequest - { - public int? GameId { get; private set; } - public LoginRemoteAuthWithGameIdRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "gameid")) - { - throw new Auth.Exception("gameid is missing from the request"); - } - var gameid = _contentElement.Descendants().FirstOrDefault(p => p.Name.LocalName == "gameid").Value; - GameId = int.Parse(gameid); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginUniqueNickRequest.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginUniqueNickRequest.cs deleted file mode 100644 index 658b15185..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginUniqueNickRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Module.Auth.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Request -{ - - public class LoginUniqueNickRequest : LoginRequestBase - { - public string Uniquenick { get; private set; } - public string Password { get; private set; } - public LoginUniqueNickRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "uniquenick")) - { - throw new Auth.Exception("uniquenick is missing from the request"); - } - Uniquenick = _contentElement.Descendants().First(p => p.Name.LocalName == "uniquenick").Value; - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "password")) - { - throw new Auth.Exception("password is missing from the request"); - } - Password = _contentElement.Descendants().First(p => p.Name.LocalName == "password").Value; - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginUniqueNickWithGameId.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginUniqueNickWithGameId.cs deleted file mode 100644 index 24bb1f5cc..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Request/LoginUniqueNickWithGameId.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Linq; - - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Request -{ - - public class LoginUniqueNickWithGameIdRequest : LoginUniqueNickRequest - { - public int? GameId { get; private set; } - public LoginUniqueNickWithGameIdRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "gameid")) - { - throw new Auth.Exception("gameid is missing from the request"); - } - var gameid = _contentElement.Descendants().FirstOrDefault(p => p.Name.LocalName == "gameid").Value; - GameId = int.Parse(gameid); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginProfileResponse.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginProfileResponse.cs deleted file mode 100644 index 2ecb45d91..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginProfileResponse.cs +++ /dev/null @@ -1,22 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Response -{ - public class LoginProfileResponse : LoginResponseBase - { - protected new LoginProfileRequest _request => (LoginProfileRequest)base._request; - protected new LoginResultBase _result => (LoginResultBase)base._result; - protected new AuthSoapEnvelope _content { get => (AuthSoapEnvelope)base._content; set => base._content = value; } - public LoginProfileResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - public override void Build() - { - _content.Add("LoginProfileResult"); - BuildContext(); - base.Build(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginProfileWithGameIdResponse.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginProfileWithGameIdResponse.cs deleted file mode 100644 index beb9b9a55..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginProfileWithGameIdResponse.cs +++ /dev/null @@ -1,21 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Response -{ - public class LoginProfileWithGameIdResponse : LoginResponseBase - { - protected new LoginProfileWithGameIdRequest _request => (LoginProfileWithGameIdRequest)base._request; - protected new LoginResultBase _result => (LoginResultBase)base._result; - public LoginProfileWithGameIdResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - public override void Build() - { - _content.Add("LoginProfileWithGameIdResult"); - BuildContext(); - base.Build(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginPs3CertResponse.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginPs3CertResponse.cs deleted file mode 100644 index 257fc3446..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginPs3CertResponse.cs +++ /dev/null @@ -1,24 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; -using UniSpy.Server.WebServer.Module.Auth.Contract.Result; - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Response -{ - public class LoginPs3CertResponse : LoginResponseBase - { - protected new LoginPs3CertRequest _request => (LoginPs3CertRequest)base._request; - protected new LoginPs3CertResult _result => (LoginPs3CertResult)base._result; - public LoginPs3CertResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - public override void Build() - { - _content.Add("LoginPs3CertResult"); - _content.Add("responseCode", _result.ResponseCode); - _content.Add("authToken", _result.AuthToken); - _content.Add("partnerChallenge", _result.PartnerChallenge); - base.Build(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginPs3CertWithGameIdResponse.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginPs3CertWithGameIdResponse.cs deleted file mode 100644 index f06d94bd5..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginPs3CertWithGameIdResponse.cs +++ /dev/null @@ -1,24 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; -using UniSpy.Server.WebServer.Module.Auth.Contract.Result; - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Response -{ - public class LoginPs3CertWithGameIdResponse : LoginResponseBase - { - protected new LoginPs3CertWithGameIdRequest _request => (LoginPs3CertWithGameIdRequest)base._request; - protected new LoginPs3CertResult _result => (LoginPs3CertResult)base._result; - public LoginPs3CertWithGameIdResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - public override void Build() - { - _content.Add("LoginPs3CertWithGameIdResult"); - _content.Add("responseCode", _result.ResponseCode); - _content.Add("authToken", _result.AuthToken); - _content.Add("partnerChallenge", _result.PartnerChallenge); - base.Build(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginRemoteAuthResponse.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginRemoteAuthResponse.cs deleted file mode 100644 index 7ed17b154..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginRemoteAuthResponse.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Response -{ - public class LoginRemoteAuthResponse : LoginResponseBase - { - protected new LoginRemoteAuthRequest _request => (LoginRemoteAuthRequest)base._request; - protected new LoginResultBase _result => (LoginResultBase)base._result; - public LoginRemoteAuthResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - public override void Build() - { - _content.Add("LoginRemoteAuthResult"); - BuildContext(); - base.Build(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginRemoteAuthWithGameIdResponse.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginRemoteAuthWithGameIdResponse.cs deleted file mode 100644 index 7d3a19413..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginRemoteAuthWithGameIdResponse.cs +++ /dev/null @@ -1,20 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Response -{ - public class LoginRemoteAuthWithGameIdResponse : LoginResponseBase - { - protected new LoginRemoteAuthWithGameIdRequest _request => (LoginRemoteAuthWithGameIdRequest)base._request; - protected new LoginResultBase _result => (LoginResultBase)base._result; - public LoginRemoteAuthWithGameIdResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - public override void Build() - { - _content.Add("LoginRemoteAuthWithGameIdResult"); - BuildContext(); - base.Build(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginUniqueNickResponse.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginUniqueNickResponse.cs deleted file mode 100644 index ffaa5b494..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginUniqueNickResponse.cs +++ /dev/null @@ -1,21 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Response -{ - public class LoginUniqueNickResponse : LoginResponseBase - { - protected new LoginRemoteAuthRequest _request => (LoginRemoteAuthRequest)base._request; - protected new LoginResultBase _result => (LoginResultBase)base._result; - public LoginUniqueNickResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - public override void Build() - { - _content.Add("LoginUniqueNickResult"); - BuildContext(); - base.Build(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginUniqueNickWithGameIdResponse.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginUniqueNickWithGameIdResponse.cs deleted file mode 100644 index 8b59a3c60..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Response/LoginUniqueNickWithGameIdResponse.cs +++ /dev/null @@ -1,21 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Response -{ - public class LoginUniqueNickWithGameIdResponse : LoginResponseBase - { - protected new LoginRemoteAuthRequest _request => (LoginRemoteAuthRequest)base._request; - protected new LoginResultBase _result => (LoginResultBase)base._result; - public LoginUniqueNickWithGameIdResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - public override void Build() - { - _content.Add("LoginUniqueNickWithGameIdResult"); - BuildContext(); - base.Build(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Result/LoginProfileResult.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Result/LoginProfileResult.cs deleted file mode 100644 index c082817a6..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Result/LoginProfileResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.WebServer.Module.Auth.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Result -{ - public sealed class LoginProfileResult : LoginResultBase - { - public LoginProfileResult() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Result/LoginPs3CertResult.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Result/LoginPs3CertResult.cs deleted file mode 100644 index 00c2b9789..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Result/LoginPs3CertResult.cs +++ /dev/null @@ -1,13 +0,0 @@ -using UniSpy.Server.WebServer.Module.Auth.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Result -{ - public class LoginPs3CertResult : LoginResultBase - { - public string AuthToken { get; set; } - public string PartnerChallenge { get; set; } - public LoginPs3CertResult() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Result/LoginRemoteAuthResult.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Result/LoginRemoteAuthResult.cs deleted file mode 100644 index 6cf534af4..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Result/LoginRemoteAuthResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.WebServer.Module.Auth.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Result -{ - public sealed class LoginRemoteAuthResult : LoginResultBase - { - public LoginRemoteAuthResult() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Contract/Result/LoginUniqueNickResult.cs b/src/Servers/WebServer/src/Module/Auth/Contract/Result/LoginUniqueNickResult.cs deleted file mode 100644 index 6cb43b826..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Contract/Result/LoginUniqueNickResult.cs +++ /dev/null @@ -1,11 +0,0 @@ -using UniSpy.Server.WebServer.Module.Auth.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Auth.Contract.Result -{ - public sealed class LoginUniqueNickResult : LoginResultBase - { - public LoginUniqueNickResult() - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Exception/DatabaseException.cs b/src/Servers/WebServer/src/Module/Auth/Exception/DatabaseException.cs deleted file mode 100644 index 59dec49c3..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Exception/DatabaseException.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace UniSpy.Server.WebServer.Module.Auth -{ - public class DatabaseException : Auth.Exception - { - public DatabaseException() : base("Database error!", AuthErrorCode.DatabaseError) - { - } - public DatabaseException(string message) : base(message, AuthErrorCode.DatabaseError) - { - } - public DatabaseException(string message, System.Exception innerException) : base(message, AuthErrorCode.DatabaseError, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Exception/Exception.cs b/src/Servers/WebServer/src/Module/Auth/Exception/Exception.cs deleted file mode 100644 index 45a847107..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Exception/Exception.cs +++ /dev/null @@ -1,53 +0,0 @@ -namespace UniSpy.Server.WebServer.Module.Auth -{ - public enum AuthErrorCode - { - Success = 0, - ServerInitFailed, - UserNotFound, - InvalidPassword, - InvalidProfile, - UniqueNickExpired, - - DatabaseError, - ServerError, - FailureMax, // must be the last failure - - // Login result (mLoginResult) - HttpError = 100, // ghttp reported an error, response ignored - ParseError, // couldn't parse http response - InvalidCertificate, // login success but certificate was invalid! - LoginFailed, // failed login or other error condition - OutOfMemory, // could not process due to insufficient memory - InvalidParameters, // check the function arguments - NoAvailabilityCheck,// No availability check was performed - Cancelled, // login request was cancelled - UnknownError, // error occured, but detailed information not available - - // response codes dealing with errors in response headers - InvalidGameID = 200, // make sure GameID is properly set with wsSetGameCredentials - InvalidAccessKey, // make sure Access Key is properly set with wsSetGameCredentials - - // login results dealing with errors in response headers - InvalidGameCredentials // check the parameters passed to wsSetGameCredentials - } - public class Exception : UniSpy.Exception - { - public AuthErrorCode ErrorCode { get; private set; } - public Exception() : this("Unkown Error!", AuthErrorCode.UnknownError) - { - } - - public Exception(string message) : this(message, AuthErrorCode.UnknownError) - { - } - public Exception(string message, AuthErrorCode code) : base(message) - { - ErrorCode = code; - } - public Exception(string message, AuthErrorCode code, System.Exception innerException) : base(message, innerException) - { - ErrorCode = code; - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Exception/InvalidPasswordException.cs b/src/Servers/WebServer/src/Module/Auth/Exception/InvalidPasswordException.cs deleted file mode 100644 index 8518d3167..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Exception/InvalidPasswordException.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace UniSpy.Server.WebServer.Module.Auth -{ - public class InvalidPasswordException : Auth.Exception - { - public InvalidPasswordException() : base("Password is invalid!", AuthErrorCode.InvalidPassword) - { - } - public InvalidPasswordException(string message) : base(message, AuthErrorCode.InvalidPassword) - { - } - public InvalidPasswordException(string message, System.Exception innerException) : base(message, AuthErrorCode.InvalidPassword, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Exception/InvalidProfileException.cs b/src/Servers/WebServer/src/Module/Auth/Exception/InvalidProfileException.cs deleted file mode 100644 index 628215391..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Exception/InvalidProfileException.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace UniSpy.Server.WebServer.Module.Auth -{ - public class InvalidProfileException : Auth.Exception - { - public InvalidProfileException() : base("Profile is invalid!", AuthErrorCode.InvalidProfile) - { - } - public InvalidProfileException(string message) : base(message, AuthErrorCode.InvalidProfile) - { - } - public InvalidProfileException(string message, System.Exception innerException) : base(message, AuthErrorCode.InvalidProfile, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Exception/ParseException.cs b/src/Servers/WebServer/src/Module/Auth/Exception/ParseException.cs deleted file mode 100644 index 528bfef05..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Exception/ParseException.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace UniSpy.Server.WebServer.Module.Auth -{ - public class ParseException : Auth.Exception - { - public ParseException() : base("Parse error!", AuthErrorCode.ParseError) - { - } - public ParseException(string message) : base(message, AuthErrorCode.ParseError) - { - } - public ParseException(string message, System.Exception innerException) : base(message, AuthErrorCode.ParseError, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Exception/ServerException.cs b/src/Servers/WebServer/src/Module/Auth/Exception/ServerException.cs deleted file mode 100644 index 4fae7aa48..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Exception/ServerException.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace UniSpy.Server.WebServer.Module.Auth -{ - public class ServerException : Auth.Exception - { - public ServerException() : base("Server error!", AuthErrorCode.ServerError) - { - } - public ServerException(string message) : base(message, AuthErrorCode.ServerError) - { - } - public ServerException(string message, System.Exception innerException) : base(message, AuthErrorCode.ServerError, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Exception/ServerInitException.cs b/src/Servers/WebServer/src/Module/Auth/Exception/ServerInitException.cs deleted file mode 100644 index 0e6cfb7d9..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Exception/ServerInitException.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace UniSpy.Server.WebServer.Module.Auth -{ - public class ServerInitException : Auth.Exception - { - public ServerInitException() : base("An unknown error occur when initializing server!", AuthErrorCode.ServerError) - { - } - public ServerInitException(string message) : base(message, AuthErrorCode.ServerError) - { - } - public ServerInitException(string message, System.Exception innerException) : base(message, AuthErrorCode.ServerError, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Exception/UniqueNickExpredException.cs b/src/Servers/WebServer/src/Module/Auth/Exception/UniqueNickExpredException.cs deleted file mode 100644 index aa622a8fc..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Exception/UniqueNickExpredException.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace UniSpy.Server.WebServer.Module.Auth -{ - public class UniqueNickExpredException : Auth.Exception - { - public UniqueNickExpredException() : base("Nickname is invalid at creating new profile!", AuthErrorCode.UniqueNickExpired) - { - } - public UniqueNickExpredException(string message) : base(message, AuthErrorCode.UniqueNickExpired) - { - } - public UniqueNickExpredException(string message, System.Exception innerException) : base(message, AuthErrorCode.UniqueNickExpired, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Exception/UserNotFoundException.cs b/src/Servers/WebServer/src/Module/Auth/Exception/UserNotFoundException.cs deleted file mode 100644 index f010feeee..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Exception/UserNotFoundException.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace UniSpy.Server.WebServer.Module.Auth -{ - public class UserNotFoundException : Auth.Exception - { - public UserNotFoundException() : base("User not found!", AuthErrorCode.UserNotFound) - { - } - public UserNotFoundException(string message) : base(message, AuthErrorCode.UserNotFound) - { - } - public UserNotFoundException(string message, System.Exception innerException) : base(message, AuthErrorCode.UserNotFound, innerException) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginProfileHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginProfileHandler.cs deleted file mode 100644 index e6bd53c45..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginProfileHandler.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; -using UniSpy.Server.WebServer.Module.Auth.Contract.Response; -using UniSpy.Server.WebServer.Module.Auth.Contract.Result; -using UniSpy.Server.Core.Database.DatabaseModel; -using UniSpy.Server.WebServer.Application; - -namespace UniSpy.Server.WebServer.Module.Auth.Handler -{ - - public class LoginProfileHandler : CmdHandlerBase - { - protected new LoginProfileRequest _request => (LoginProfileRequest)base._request; - protected new LoginResultBase _result { get => (LoginResultBase)base._result; set => base._result = value; } - public LoginProfileHandler(Client client, LoginProfileRequest request) : base(client, request) - { - _result = new LoginProfileResult(); - } - protected override void DataOperation() - { - using (var db = new UniSpyContext()) - { - var result = from p in db.Profiles - join u in db.Users on p.Userid equals u.Userid - join sp in db.Subprofiles on p.Profileid equals sp.Profileid - where sp.Uniquenick == _request.Uniquenick - && sp.Cdkeyenc == _request.CDKey - && sp.Partnerid == _request.PartnerCode - && sp.Namespaceid == _request.NamespaceId - && u.Email == _request.Email - select new { u, p, sp }; - if (result.Count() != 1) - { - throw new Auth.Exception("No account exists with the provided email address."); - } - var data = result.First(); - _result.UserId = data.u.Userid; - _result.ProfileId = data.p.Profileid; - _result.CdKeyHash = data.sp.Cdkeyenc; - // currently we set this to uniquenick - _result.ProfileNick = data.sp.Uniquenick; - } - } - protected override void ResponseConstruct() - { - _response = new LoginProfileResponse(_request, _result); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginProfileWithGameIdHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginProfileWithGameIdHandler.cs deleted file mode 100644 index 2c851cd61..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginProfileWithGameIdHandler.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; -using UniSpy.Server.WebServer.Module.Auth.Contract.Response; -using UniSpy.Server.Core.Database.DatabaseModel; -using UniSpy.Server.WebServer.Application; - -namespace UniSpy.Server.WebServer.Module.Auth.Handler -{ - - public sealed class LoginProfileWithGameIdHandler : LoginProfileHandler - { - private new LoginProfileWithGameIdRequest _request => (LoginProfileWithGameIdRequest)base._request; - public LoginProfileWithGameIdHandler(Client client, LoginProfileWithGameIdRequest request) : base(client, request) - { - } - protected override void DataOperation() - { - using (var db = new UniSpyContext()) - { - var result = from p in db.Profiles - join u in db.Users on p.Userid equals u.Userid - join sp in db.Subprofiles on p.Profileid equals sp.Profileid - where sp.Uniquenick == _request.Uniquenick - && sp.Cdkeyenc == _request.CDKey - && sp.Partnerid == _request.PartnerCode - && sp.Namespaceid == _request.NamespaceId - && u.Email == _request.Email - // we do not care about game id now - select new { u, p, sp }; - if (result.Count() != 1) - { - throw new Auth.Exception("No account exists with the provided email address."); - } - var data = result.First(); - _result.UserId = data.u.Userid; - _result.ProfileId = data.p.Profileid; - _result.CdKeyHash = data.sp.Cdkeyenc; - // currently we set this to uniquenick - _result.ProfileNick = data.sp.Uniquenick; - } - } - - protected override void ResponseConstruct() - { - // base.ResponseConstruct(); - _response = new LoginProfileWithGameIdResponse(_request, _result); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginPs3CertHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginPs3CertHandler.cs deleted file mode 100644 index 3bf5ebbde..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginPs3CertHandler.cs +++ /dev/null @@ -1,17 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; -using UniSpy.Server.WebServer.Module.Auth.Contract.Result; -using UniSpy.Server.WebServer.Application; - -namespace UniSpy.Server.WebServer.Module.Auth.Handler -{ - - public class LoginPs3CertHandler : CmdHandlerBase - { - protected new LoginPs3CertRequest _request => (LoginPs3CertRequest)base._request; - protected new LoginPs3CertResult _result => (LoginPs3CertResult)base._result; - public LoginPs3CertHandler(Client client, LoginPs3CertRequest request) : base(client, request) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginPs3CertWithGameIdHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginPs3CertWithGameIdHandler.cs deleted file mode 100644 index 99ad582db..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginPs3CertWithGameIdHandler.cs +++ /dev/null @@ -1,15 +0,0 @@ - -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; -using UniSpy.Server.WebServer.Application; - -namespace UniSpy.Server.WebServer.Module.Auth.Handler -{ - - public sealed class LoginPs3CertWithGameIdHandler : LoginPs3CertHandler - { - private new LoginPs3CertWithGameIdRequest _request => (LoginPs3CertWithGameIdRequest)base._request; - public LoginPs3CertWithGameIdHandler(Client client, LoginPs3CertWithGameIdRequest request) : base(client, request) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginRemoteAuthHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginRemoteAuthHandler.cs deleted file mode 100644 index b4b797056..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginRemoteAuthHandler.cs +++ /dev/null @@ -1,53 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; -using UniSpy.Server.WebServer.Module.Auth.Contract.Response; -using UniSpy.Server.WebServer.Module.Auth.Contract.Result; -using UniSpy.Server.Core.Database.DatabaseModel; -using UniSpy.Server.WebServer.Application; - -namespace UniSpy.Server.WebServer.Module.Auth.Handler -{ - - public class LoginRemoteAuthHandler : CmdHandlerBase - { - protected new LoginRemoteAuthRequest _request => (LoginRemoteAuthRequest)base._request; - protected new LoginResultBase _result { get => (LoginResultBase)base._result; set => base._result = value; } - public LoginRemoteAuthHandler(Client client, LoginRemoteAuthRequest request) : base(client, request) - { - _result = new LoginRemoteAuthResult(); - } - protected override void DataOperation() - { - using (var db = new UniSpyContext()) - { - // var result = from p in db.Profiles - // join u in db.Users on p.Userid equals u.UserId - // join sp in db.Subprofiles on p.ProfileId equals sp.ProfileId - // where sp.Authtoken == _request.AuthToken && - // sp.PartnerId == _request.GameId - // select new { u, p, sp }; - // if (result.Count() != 1) - // { - // throw new System.Exception("No account exists with the provided email address."); - // } - - // var data = result.First(); - // _result.UserId = data.u.UserId; - // _result.ProfileId = data.p.ProfileId; - // _result.CdKeyHash = data.sp.Cdkeyenc; - // currently we set this to uniquenick - // _result.ProfileNick = data.sp.Uniquenick; - _result.UserId = 1; - _result.ProfileId = 1; - _result.CdKeyHash = "00000000000000s"; - _result.ProfileNick = "xiaojiuwo"; - _result.UniqueNick = "xiaojiuwo"; - } - } - protected override void ResponseConstruct() - { - _response = new LoginRemoteAuthResponse(_request, _result); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginRemoteAuthWithGameIdHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginRemoteAuthWithGameIdHandler.cs deleted file mode 100644 index f3dce839a..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginRemoteAuthWithGameIdHandler.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; -using UniSpy.Server.WebServer.Module.Auth.Contract.Response; -using UniSpy.Server.Core.Database.DatabaseModel; -using UniSpy.Server.WebServer.Application; - -namespace UniSpy.Server.WebServer.Module.Auth.Handler -{ - - public sealed class LoginRemoteAuthWithGameIdHandler : LoginRemoteAuthHandler - { - private new LoginRemoteAuthWithGameIdRequest _request => (LoginRemoteAuthWithGameIdRequest)base._request; - public LoginRemoteAuthWithGameIdHandler(Client client, LoginRemoteAuthWithGameIdRequest request) : base(client, request) - { - } - protected override void DataOperation() - { - using (var db = new UniSpyContext()) - { - var result = from p in db.Profiles - join u in db.Users on p.Userid equals u.Userid - join sp in db.Subprofiles on p.Profileid equals sp.Profileid - where sp.Authtoken == _request.AuthToken && - sp.Partnerid == _request.GameId - select new { u, p, sp }; - if (result.Count() != 1) - { - throw new Auth.Exception("No account exists with the provided email address."); - } - - var data = result.First(); - _result.UserId = data.u.Userid; - _result.ProfileId = data.p.Profileid; - _result.CdKeyHash = data.sp.Cdkeyenc; - // currently we set this to uniquenick - _result.ProfileNick = data.sp.Uniquenick; - _result.UniqueNick = data.sp.Uniquenick; - } - } - protected override void ResponseConstruct() - { - // base.ResponseConstruct(); - _response = new LoginRemoteAuthWithGameIdResponse(_request, _result); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickHandler.cs deleted file mode 100644 index 1d9e2dcd0..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickHandler.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Abstraction; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; -using UniSpy.Server.WebServer.Module.Auth.Contract.Response; -using UniSpy.Server.WebServer.Module.Auth.Contract.Result; -using UniSpy.Server.Core.Database.DatabaseModel; -using UniSpy.Server.WebServer.Application; - -namespace UniSpy.Server.WebServer.Module.Auth.Handler -{ - - public class LoginUniqueNickHandler : CmdHandlerBase - { - protected new LoginUniqueNickRequest _request => (LoginUniqueNickRequest)base._request; - protected new LoginResultBase _result { get => (LoginResultBase)base._result; set => base._result = value; } - public LoginUniqueNickHandler(Client client, LoginUniqueNickRequest request) : base(client, request) - { - _result = new LoginUniqueNickResult(); - } - protected override void DataOperation() - { - using (var db = new UniSpyContext()) - { - var result = from p in db.Profiles - join u in db.Users on p.Userid equals u.Userid - join sp in db.Subprofiles on p.Profileid equals sp.Profileid - where sp.Uniquenick == _request.Uniquenick - && sp.Namespaceid == _request.NamespaceId - // && u.Password == _request.Password - select new { u, p, sp }; - if (result.Count() != 1) - { - throw new Auth.Exception("No account exists with the provided email address."); - } - var data = result.First(); - _result.UserId = data.u.Userid; - _result.ProfileId = data.p.Profileid; - // _result.CdKeyHash = data.sp.Cdkeyenc; - _result.CdKeyHash = "D41D8CD98F00B204E9800998ECF8427E"; - // currently we set this to uniquenick - _result.ProfileNick = data.p.Nick; - _result.UniqueNick = data.sp.Uniquenick; - } - } - protected override void ResponseConstruct() - { - _response = new LoginUniqueNickResponse(_request, _result); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs b/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs deleted file mode 100644 index ef50addd3..000000000 --- a/src/Servers/WebServer/src/Module/Auth/Handler/LoginUniqueNickWithGameIdHandler.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; -using UniSpy.Server.WebServer.Module.Auth.Contract.Response; -using UniSpy.Server.Core.Database.DatabaseModel; -using UniSpy.Server.WebServer.Application; - -namespace UniSpy.Server.WebServer.Module.Auth.Handler -{ - - public sealed class LoginUniqueNickWithGameIdHandler : LoginUniqueNickHandler - { - private new LoginUniqueNickRequest _request => (LoginUniqueNickRequest)base._request; - public LoginUniqueNickWithGameIdHandler(Client client, LoginUniqueNickRequest request) : base(client, request) - { - } - protected override void DataOperation() - { - using (var db = new UniSpyContext()) - { - var result = from p in db.Profiles - join u in db.Users on p.Userid equals u.Userid - join sp in db.Subprofiles on p.Profileid equals sp.Profileid - where sp.Uniquenick == _request.Uniquenick && - sp.Namespaceid == _request.NamespaceId - select new { u, p, sp }; - if (result.Count() != 1) - { - throw new Auth.Exception("No account exists with the provided email address."); - } - var data = result.First(); - _result.UserId = data.u.Userid; - _result.ProfileId = data.p.Profileid; - // _result.CdKeyHash = data.sp.Cdkeyenc; - _result.CdKeyHash = "D41D8CD98F00B204E9800998ECF8427E"; - // currently we set this to uniquenick - _result.ProfileNick = data.p.Nick; - _result.UniqueNick = data.sp.Uniquenick; - } - } - protected override void ResponseConstruct() - { - // base.ResponseConstruct(); - _response = new LoginUniqueNickWithGameIdResponse(_request, _result); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Abstraction/RequestBase.cs b/src/Servers/WebServer/src/Module/Direct2Game/Abstraction/RequestBase.cs deleted file mode 100644 index 3d598a433..000000000 --- a/src/Servers/WebServer/src/Module/Direct2Game/Abstraction/RequestBase.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace UniSpy.Server.WebServer.Module.Direct2Game.Abstraction; - -public class RequestBase : UniSpy.Server.WebServer.Abstraction.RequestBase -{ - public RequestBase(string rawRequest) : base(rawRequest) - { - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Abstraction/ResponseBase.cs b/src/Servers/WebServer/src/Module/Direct2Game/Abstraction/ResponseBase.cs deleted file mode 100644 index f797222b8..000000000 --- a/src/Servers/WebServer/src/Module/Direct2Game/Abstraction/ResponseBase.cs +++ /dev/null @@ -1,10 +0,0 @@ -using UniSpy.Server.WebServer.Module.Direct2Game.Contract; - -namespace UniSpy.Server.WebServer.Module.Direct2Game.Abstraction; -public class ResponseBase : UniSpy.Server.WebServer.Abstraction.ResponseBase -{ - public ResponseBase(RequestBase request, ResultBase result) : base(request, result) - { - _content = new Direct2GameSoapEnvelope(); - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Abstraction/ResultBase.cs b/src/Servers/WebServer/src/Module/Direct2Game/Abstraction/ResultBase.cs deleted file mode 100644 index d4dda2698..000000000 --- a/src/Servers/WebServer/src/Module/Direct2Game/Abstraction/ResultBase.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace UniSpy.Server.WebServer.Module.Direct2Game.Abstraction; -public class ResultBase : UniSpy.Server.WebServer.Abstraction.ResultBase -{ - public ResultBase() - { - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Direct2GameSoapEnvelope.cs b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Direct2GameSoapEnvelope.cs deleted file mode 100644 index dfc4bceea..000000000 --- a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Direct2GameSoapEnvelope.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Xml.Linq; -using UniSpy.Server.WebServer.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Direct2Game.Contract -{ - public sealed class Direct2GameSoapEnvelope : SoapEnvelopBase - { - public static XNamespace Direct2GameNamespace = "http://gamespy.net/commerce/"; - public Direct2GameSoapEnvelope() : base("ns1", Direct2GameNamespace) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Request/GetPurchaseHistoryRequest.cs b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Request/GetPurchaseHistoryRequest.cs deleted file mode 100644 index bc1742a2d..000000000 --- a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Request/GetPurchaseHistoryRequest.cs +++ /dev/null @@ -1,47 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Module.Direct2Game.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Direct2Game.Contract.Request -{ - - public class GetPurchaseHistoryRequest : RequestBase - { - public int GameId { get; private set; } - - public string AccessToken { get; private set; } - - public string Proof { get; private set; } - public string Certificate { get; private set; } - public GetPurchaseHistoryRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "gameid")) - { - throw new WebServer.Exception("gameid is missing from the request"); - } - var gameid = _contentElement.Descendants().First(p => p.Name.LocalName == "gameid").Value; - GameId = int.Parse(gameid); - - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "proof")) - { - throw new WebServer.Exception("proof is missing from the request"); - } - Proof = _contentElement.Descendants().First(p => p.Name.LocalName == "proof").Value; - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "certificate")) - { - throw new WebServer.Exception("certificate is missing from the request"); - } - Certificate = _contentElement.Descendants().First(p => p.Name.LocalName == "certificate").Value; - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "accesstoken")) - { - throw new WebServer.Exception("accesstoken is missing from the request"); - } - AccessToken = _contentElement.Descendants().First(p => p.Name.LocalName == "accesstoken").Value; - } - - } -} diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Request/GetStoreAvailabilityRequest.cs b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Request/GetStoreAvailabilityRequest.cs deleted file mode 100644 index 94997848e..000000000 --- a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Request/GetStoreAvailabilityRequest.cs +++ /dev/null @@ -1,48 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Module.Direct2Game.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Direct2Game.Contract.Request -{ - - public class GetStoreAvailabilityRequest : RequestBase - { - public int GameId { get; private set; } - public int Version { get; private set; } - public string Region { get; private set; } - public string AccessToken { get; private set; } - - public GetStoreAvailabilityRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "gameid")) - { - throw new WebServer.Exception("gameid is missing from the request"); - } - var gameid = _contentElement.Descendants().First(p => p.Name.LocalName == "gameid").Value; - GameId = int.Parse(gameid); - - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "version")) - { - throw new WebServer.Exception("version is missing from the request"); - } - var version = _contentElement.Descendants().First(p => p.Name.LocalName == "version").Value; - Version = int.Parse(version); - - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "region")) - { - throw new WebServer.Exception("region is missing from the request"); - } - Region = _contentElement.Descendants().First(p => p.Name.LocalName == "region").Value; - - if (!_contentElement.Descendants().Any(p => p.Name.LocalName == "accesstoken")) - { - throw new WebServer.Exception("accesstoken is missing from the request"); - } - AccessToken = _contentElement.Descendants().First(p => p.Name.LocalName == "accesstoken").Value; - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetPurchaseHistoryResponse.cs b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetPurchaseHistoryResponse.cs deleted file mode 100644 index 2cfa7a5a3..000000000 --- a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetPurchaseHistoryResponse.cs +++ /dev/null @@ -1,28 +0,0 @@ - -using UniSpy.Server.WebServer.Module.Direct2Game.Abstraction; -using UniSpy.Server.WebServer.Module.Direct2Game.Contract.Result; - -namespace UniSpy.Server.WebServer.Module.Direct2Game.Contract.Response -{ - public class GetPurchaseHistoryResponse : ResponseBase - { - private new GetPurchaseHistoryResult _result => (GetPurchaseHistoryResult)base._result; - - public GetPurchaseHistoryResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - - public override void Build() - { - _content.Add("GetPurchaseHistoryResponse"); - _content.Add("GetPurchaseHistoryResult"); - _content.Add("status"); - _content.Add("code", _result.Code); - _content.ChangeToElement("GetPurchaseHistoryResult"); - _content.Add("orderpurchases"); - // we do not know the purchace content for each game, so currently we just make it 0 - _content.Add("count", 0); - base.Build(); - } - } -} diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetStoreAvailabilityResponse.cs b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetStoreAvailabilityResponse.cs deleted file mode 100644 index d9864c830..000000000 --- a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Response/GetStoreAvailabilityResponse.cs +++ /dev/null @@ -1,25 +0,0 @@ -using UniSpy.Server.WebServer.Module.Direct2Game.Abstraction; -using UniSpy.Server.WebServer.Module.Direct2Game.Contract.Result; - -namespace UniSpy.Server.WebServer.Module.Direct2Game.Contract.Request -{ - public class GetStoreAvailabilityResponse : ResponseBase - { - private new GetStoreAvailabilityResult _result => (GetStoreAvailabilityResult)base._result; - - public GetStoreAvailabilityResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - - public override void Build() - { - _content.Add("GetStoreAvailabilityResponse"); - _content.Add("GetStoreAvailabilityResult"); - _content.Add("status"); - _content.Add("code", _result.Code); - _content.ChangeToElement("GetStoreAvailabilityResult"); - _content.Add("storestatusid", (int)_result.StoreStatusId); - base.Build(); - } - } -} diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Result/GetPurchaseHistoryResult.cs b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Result/GetPurchaseHistoryResult.cs deleted file mode 100644 index 63ea47346..000000000 --- a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Result/GetPurchaseHistoryResult.cs +++ /dev/null @@ -1,9 +0,0 @@ -using UniSpy.Server.WebServer.Module.Direct2Game.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Direct2Game.Contract.Result -{ - public class GetPurchaseHistoryResult : ResultBase - { - public int Code { get; set; } = 0; - } -} diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Result/GetStoreAvailabilityResult.cs b/src/Servers/WebServer/src/Module/Direct2Game/Contract/Result/GetStoreAvailabilityResult.cs deleted file mode 100644 index a7f6f59d3..000000000 --- a/src/Servers/WebServer/src/Module/Direct2Game/Contract/Result/GetStoreAvailabilityResult.cs +++ /dev/null @@ -1,21 +0,0 @@ -using UniSpy.Server.WebServer.Module.Direct2Game.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Direct2Game.Contract.Result -{ - public enum AvaliableCode - { - StoreOnline = 10, - StoreOfflineForMaintaince = 20, - StoreOfflineRetired = 50, - StoreNotYetLaunched = 100 - } - public class GetStoreAvailabilityResult : ResultBase - { - public int Code { get; set; } = 0; - public AvaliableCode StoreStatusId { get; set; } = AvaliableCode.StoreOnline; - - public GetStoreAvailabilityResult() - { - } - } -} diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Handler/GetPurchaseHistoryHandler.cs b/src/Servers/WebServer/src/Module/Direct2Game/Handler/GetPurchaseHistoryHandler.cs deleted file mode 100644 index c48997f8f..000000000 --- a/src/Servers/WebServer/src/Module/Direct2Game/Handler/GetPurchaseHistoryHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Module.Direct2Game.Contract.Request; -using UniSpy.Server.WebServer.Module.Direct2Game.Contract.Response; -using UniSpy.Server.WebServer.Module.Direct2Game.Contract.Result; -using UniSpy.Server.WebServer.Application; - -namespace UniSpy.Server.WebServer.Module.Direct2Game.Handler -{ - - internal class GetPurchaseHistoryHandler : CmdHandlerBase - { - protected new GetPurchaseHistoryRequest _request => (GetPurchaseHistoryRequest)base._request; - protected new GetPurchaseHistoryResult _result = new GetPurchaseHistoryResult(); - - public GetPurchaseHistoryHandler(Client client, GetPurchaseHistoryRequest request) : base(client, request) - { - - } - - protected override void DataOperation() - { - - } - - protected override void ResponseConstruct() - { - _result.Code = 0; - - _response = new GetPurchaseHistoryResponse(_request, _result); - } - } -} diff --git a/src/Servers/WebServer/src/Module/Direct2Game/Handler/GetStoreAvailabilityHandler.cs b/src/Servers/WebServer/src/Module/Direct2Game/Handler/GetStoreAvailabilityHandler.cs deleted file mode 100644 index 25a9a484d..000000000 --- a/src/Servers/WebServer/src/Module/Direct2Game/Handler/GetStoreAvailabilityHandler.cs +++ /dev/null @@ -1,21 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Module.Direct2Game.Contract.Request; -using UniSpy.Server.WebServer.Module.Direct2Game.Contract.Result; -using UniSpy.Server.WebServer.Application; - -namespace UniSpy.Server.WebServer.Module.Direct2Game.Handler -{ - public class GetStoreAvailabilityHandler : CmdHandlerBase - { - protected new GetStoreAvailabilityRequest _request => (GetStoreAvailabilityRequest)base._request; - protected new GetStoreAvailabilityResult _result = new GetStoreAvailabilityResult(); - public GetStoreAvailabilityHandler(Client client, GetStoreAvailabilityRequest request) : base(client, request) - { - } - - protected override void ResponseConstruct() - { - _response = new GetStoreAvailabilityResponse(_request, _result); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/IngameAd/Contract/Request/GetTargettedAdRequest.cs b/src/Servers/WebServer/src/Module/IngameAd/Contract/Request/GetTargettedAdRequest.cs deleted file mode 100644 index ebeee71e3..000000000 --- a/src/Servers/WebServer/src/Module/IngameAd/Contract/Request/GetTargettedAdRequest.cs +++ /dev/null @@ -1,14 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.IngameAd.Contract.Request -{ - - public class GetTargettedAdRequest : RequestBase - { - public override void Parse() - { - throw new System.NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/IngameAd/Contract/Request/ReportAdUsageRequest.cs b/src/Servers/WebServer/src/Module/IngameAd/Contract/Request/ReportAdUsageRequest.cs deleted file mode 100644 index 9a7ee4dfe..000000000 --- a/src/Servers/WebServer/src/Module/IngameAd/Contract/Request/ReportAdUsageRequest.cs +++ /dev/null @@ -1,14 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.IngameAd.Contract.Request -{ - - public class ReportAdUsageRequest : RequestBase - { - public override void Parse() - { - throw new System.NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/PatchingAndTracking/Contract/Request/MotdRequest.cs b/src/Servers/WebServer/src/Module/PatchingAndTracking/Contract/Request/MotdRequest.cs deleted file mode 100644 index cd1832df7..000000000 --- a/src/Servers/WebServer/src/Module/PatchingAndTracking/Contract/Request/MotdRequest.cs +++ /dev/null @@ -1,14 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.PatchingAndTracking.Contract.Request -{ - - public class MotdRequest : RequestBase - { - public override void Parse() - { - throw new System.NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/PatchingAndTracking/Contract/Request/VercheckRequest.cs b/src/Servers/WebServer/src/Module/PatchingAndTracking/Contract/Request/VercheckRequest.cs deleted file mode 100644 index b8ba41a9f..000000000 --- a/src/Servers/WebServer/src/Module/PatchingAndTracking/Contract/Request/VercheckRequest.cs +++ /dev/null @@ -1,14 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.PatchingAndTracking.Contract.Request -{ - - public class VercheckRequest : RequestBase - { - public override void Parse() - { - throw new System.NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Racing/Contract/Request/GetContestDataRequest.cs b/src/Servers/WebServer/src/Module/Racing/Contract/Request/GetContestDataRequest.cs deleted file mode 100644 index b6975b981..000000000 --- a/src/Servers/WebServer/src/Module/Racing/Contract/Request/GetContestDataRequest.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.Racing.Contract.Request -{ - - public class GetContestDataRequest : RequestBase - { - public int GameId { get; set; } - public int RegionId { get; set; } - public int CourseId { get; set; } - public GetContestDataRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - var gameid = _contentElement.Descendants().Where(p => p.Name.LocalName == "gameid").First().Value; - GameId = int.Parse(gameid); - var regionid = _contentElement.Descendants().Where(p => p.Name.LocalName == "regionid").First().Value; - RegionId = int.Parse(regionid); - var courseid = _contentElement.Descendants().Where(p => p.Name.LocalName == "courseid").First().Value; - CourseId = int.Parse(courseid); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Racing/Contract/Request/GetFriendRankingsRequest.cs b/src/Servers/WebServer/src/Module/Racing/Contract/Request/GetFriendRankingsRequest.cs deleted file mode 100644 index 5a2d7dc23..000000000 --- a/src/Servers/WebServer/src/Module/Racing/Contract/Request/GetFriendRankingsRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.Racing.Contract.Request -{ - - public class GetFriendRankingsRequest : RequestBase - { - public int GameId { get; set; } - public int RegionId { get; set; } - public int CourseId { get; set; } - public int ProfileId { get; set; } - public GetFriendRankingsRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - var gameid = _contentElement.Descendants().Where(p => p.Name.LocalName == "gameid").First().Value; - GameId = int.Parse(gameid); - var regionid = _contentElement.Descendants().Where(p => p.Name.LocalName == "regionid").First().Value; - RegionId = int.Parse(regionid); - var courseid = _contentElement.Descendants().Where(p => p.Name.LocalName == "courseid").First().Value; - CourseId = int.Parse(courseid); - var profileid = _contentElement.Descendants().Where(p => p.Name.LocalName == "profileid").First().Value; - ProfileId = int.Parse(profileid); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Racing/Contract/Request/GetRegionalDataRequest.cs b/src/Servers/WebServer/src/Module/Racing/Contract/Request/GetRegionalDataRequest.cs deleted file mode 100644 index cf75cbb9a..000000000 --- a/src/Servers/WebServer/src/Module/Racing/Contract/Request/GetRegionalDataRequest.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.Racing.Contract.Request -{ - - public class GetRegionalDataRequest : RequestBase - { - public int GameId { get; set; } - public int RegionId { get; set; } - public GetRegionalDataRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - var gameid = _contentElement.Descendants().Where(p => p.Name.LocalName == "gameid").First().Value; - GameId = int.Parse(gameid); - var regionid = _contentElement.Descendants().Where(p => p.Name.LocalName == "regionid").First().Value; - RegionId = int.Parse(regionid); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Racing/Contract/Request/GetTenAboveRankingsRequest.cs b/src/Servers/WebServer/src/Module/Racing/Contract/Request/GetTenAboveRankingsRequest.cs deleted file mode 100644 index 716691514..000000000 --- a/src/Servers/WebServer/src/Module/Racing/Contract/Request/GetTenAboveRankingsRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.Racing.Contract.Request -{ - - public class GetTenAboveRankingsRequest : RequestBase - { - public int GameId { get; set; } - public int RegionId { get; set; } - public int CourseId { get; set; } - public int ProfileId { get; set; } - public GetTenAboveRankingsRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - var gameid = _contentElement.Descendants().Where(p => p.Name.LocalName == "gameid").First().Value; - GameId = int.Parse(gameid); - var regionid = _contentElement.Descendants().Where(p => p.Name.LocalName == "regionid").First().Value; - RegionId = int.Parse(regionid); - var courseid = _contentElement.Descendants().Where(p => p.Name.LocalName == "courseid").First().Value; - CourseId = int.Parse(courseid); - var profileid = _contentElement.Descendants().Where(p => p.Name.LocalName == "profileid").First().Value; - ProfileId = int.Parse(profileid); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Racing/Contract/Request/GetTopTenRankingsRequest.cs b/src/Servers/WebServer/src/Module/Racing/Contract/Request/GetTopTenRankingsRequest.cs deleted file mode 100644 index 4a0da75b4..000000000 --- a/src/Servers/WebServer/src/Module/Racing/Contract/Request/GetTopTenRankingsRequest.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.Racing.Contract.Request -{ - - public class GetTopTenRankingsRequest : RequestBase - { - public int GameId { get; set; } - public int RegionId { get; set; } - public int CourseId { get; set; } - public GetTopTenRankingsRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - var gameid = _contentElement.Descendants().Where(p => p.Name.LocalName == "gameid").First().Value; - GameId = int.Parse(gameid); - var regionid = _contentElement.Descendants().Where(p => p.Name.LocalName == "regionid").First().Value; - RegionId = int.Parse(regionid); - var courseid = _contentElement.Descendants().Where(p => p.Name.LocalName == "courseid").First().Value; - CourseId = int.Parse(courseid); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Racing/Contract/Request/SubmitGhostRequest.cs b/src/Servers/WebServer/src/Module/Racing/Contract/Request/SubmitGhostRequest.cs deleted file mode 100644 index 2579c459f..000000000 --- a/src/Servers/WebServer/src/Module/Racing/Contract/Request/SubmitGhostRequest.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.Racing.Contract.Request -{ - - public class SubmitGhostRequest : RequestBase - { - public int GameId { get; set; } - public int RegionId { get; set; } - public int CourseId { get; set; } - public int ProfileId { get; set; } - public string Score { get; set; } - public int FileId { get; set; } - public SubmitGhostRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - var gameid = _contentElement.Descendants().Where(p => p.Name.LocalName == "gameid").First().Value; - GameId = int.Parse(gameid); - var regionid = _contentElement.Descendants().Where(p => p.Name.LocalName == "regionid").First().Value; - RegionId = int.Parse(regionid); - var courseid = _contentElement.Descendants().Where(p => p.Name.LocalName == "courseid").First().Value; - CourseId = int.Parse(courseid); - var profileid = _contentElement.Descendants().Where(p => p.Name.LocalName == "profileid").First().Value; - ProfileId = int.Parse(profileid); - var score = _contentElement.Descendants().Where(p => p.Name.LocalName == "score").First().Value; - Score = score; - var fileid = _contentElement.Descendants().Where(p => p.Name.LocalName == "fileid").First().Value; - FileId = int.Parse(fileid); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Racing/Contract/Request/SubmitScoresRequest.cs b/src/Servers/WebServer/src/Module/Racing/Contract/Request/SubmitScoresRequest.cs deleted file mode 100644 index a22944dd6..000000000 --- a/src/Servers/WebServer/src/Module/Racing/Contract/Request/SubmitScoresRequest.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Abstraction; - - -namespace UniSpy.Server.WebServer.Module.Racing.Contract.Request -{ - - public class SubmitScoresRequest : RequestBase - { - public int GameData { get; set; } - public int RegionId { get; set; } - public int ProfileId { get; set; } - public int GameId { get; set; } - public int ScoreMode { get; set; } - public string ScoreDatas { get; set; } - public SubmitScoresRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - var gamedata = _contentElement.Descendants().Where(p => p.Name.LocalName == "gamedata").First().Value; - GameData = int.Parse(gamedata); - var regionid = _contentElement.Descendants().Where(p => p.Name.LocalName == "regionid").First().Value; - RegionId = int.Parse(regionid); - var gameid = _contentElement.Descendants().Where(p => p.Name.LocalName == "gameid").First().Value; - GameId = int.Parse(gameid); - var scoremode = _contentElement.Descendants().Where(p => p.Name.LocalName == "scoremode").First().Value; - ScoreMode = int.Parse(scoremode); - var scoredatas = _contentElement.Descendants().Where(p => p.Name.LocalName == "scoredatas").First().Value; - ScoreDatas = scoredatas; - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Abstraction/CmdHandlerBase.cs b/src/Servers/WebServer/src/Module/Sake/Abstraction/CmdHandlerBase.cs deleted file mode 100644 index 967fea850..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Abstraction/CmdHandlerBase.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; -using System.IO; -using Newtonsoft.Json; -using UniSpy.Server.WebServer.Aggregate; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.WebServer.Module.Sake.Abstraction -{ - public abstract class CmdHandlerBase : WebServer.Abstraction.CmdHandlerBase - { - protected new RequestBase _request => (RequestBase)base._request; - protected string _sakeFilePath => $"./sake_storage/{_request.GameId}/{_request.TableId}/sake_storage.json"; - protected List _sakeData { get; private set; } - protected CmdHandlerBase(IClient client, IRequest request) : base(client, request) - { - } - - protected override void RequestCheck() - { - base.RequestCheck(); - //todo get secretkey from database where gameid - } - protected override void DataOperation() - { - if (!File.Exists(_sakeFilePath)) - { - new FileInfo(_sakeFilePath).Directory.Create(); - } - else - { - _sakeData = JsonConvert.DeserializeObject>(File.ReadAllText(_sakeFilePath)); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Abstraction/RequestBase.cs b/src/Servers/WebServer/src/Module/Sake/Abstraction/RequestBase.cs deleted file mode 100644 index 9d52e3482..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Abstraction/RequestBase.cs +++ /dev/null @@ -1,29 +0,0 @@ -using System.Linq; - -namespace UniSpy.Server.WebServer.Module.Sake.Abstraction -{ - public abstract class RequestBase : WebServer.Abstraction.RequestBase - { - public int GameId { get; set; } - public string SecretKey { get; set; } - public string LoginTicket { get; set; } - public string TableId { get; set; } - - public RequestBase(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - var gameId = _contentElement.Descendants().Where(p => p.Name.LocalName == "gameid").First().Value; - GameId = int.Parse(gameId); - var secretKey = _contentElement.Descendants().Where(p => p.Name.LocalName == "secretKey").First().Value; - SecretKey = secretKey; - var loginTicket = _contentElement.Descendants().Where(p => p.Name.LocalName == "loginTicket").First().Value; - LoginTicket = loginTicket; - var tableid = _contentElement.Descendants().Where(p => p.Name.LocalName == "tableid").First().Value; - TableId = tableid; - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Abstraction/ResponseBase.cs b/src/Servers/WebServer/src/Module/Sake/Abstraction/ResponseBase.cs deleted file mode 100644 index f9b513a92..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Abstraction/ResponseBase.cs +++ /dev/null @@ -1,12 +0,0 @@ -using UniSpy.Server.WebServer.Module.Sake.Contract; - -namespace UniSpy.Server.WebServer.Module.Sake.Abstraction -{ - public abstract class ResponseBase : WebServer.Abstraction.ResponseBase - { - public ResponseBase(RequestBase request, ResultBase result) : base(request, result) - { - _content = new SakeSoapEnvelope(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Abstraction/ResultBase.cs b/src/Servers/WebServer/src/Module/Sake/Abstraction/ResultBase.cs deleted file mode 100644 index cb4adaa82..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Abstraction/ResultBase.cs +++ /dev/null @@ -1,5 +0,0 @@ -namespace UniSpy.Server.WebServer.Module.Sake.Abstraction; - -public class ResultBase : UniSpy.Server.WebServer.Abstraction.ResultBase -{ -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/CreateRecordRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/CreateRecordRequest.cs deleted file mode 100644 index 68838a0b0..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/CreateRecordRequest.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Text.Json; -using System.Xml.Linq; -using UniSpy.Server.WebServer.Aggregate; -using UniSpy.Server.WebServer.Module.Sake.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request -{ - - public class CreateRecordRequest : RequestBase - { - public static readonly List SakeTypes = new List() - { - "binaryDataValue", - "booleanValue", - "dateAndTimeValue", - "unicodeStringValue", - "asciiStringValue", - "floatValue", - "int64Value", - "intValue", - "shortValue", - "byteValue" - }; - public JsonDocument Values { get; set; } - public CreateRecordRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - var valuesNode = _contentElement.Descendants().Where(p => p.Name.LocalName == "ArrayOfRecordValue").First(); - var jStr = Newtonsoft.Json.JsonConvert.SerializeXNode(valuesNode).Replace("ns1:", ""); - Values = JsonDocument.Parse(jStr); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/DeleteRecordRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/DeleteRecordRequest.cs deleted file mode 100644 index 0d3930fb8..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/DeleteRecordRequest.cs +++ /dev/null @@ -1,21 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Module.Sake.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request -{ - - public class DeleteRecordRequest : RequestBase - { - public string RecordId { get; set; } - public DeleteRecordRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - var recordid = _contentElement.Descendants().Where(p => p.Name.LocalName == "recordid").First().Value; - RecordId = recordid; - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetMyRecordsRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetMyRecordsRequest.cs deleted file mode 100644 index 0b2cb7b99..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetMyRecordsRequest.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Xml.Linq; -using UniSpy.Server.WebServer.Aggregate; -using UniSpy.Server.WebServer.Module.Sake.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request -{ - - public class GetMyRecordsRequest : RequestBase - { - public List Fields { get; set; } - public GetMyRecordsRequest(string rawRequest) : base(rawRequest) - { - Fields = new List(); - } - - public override void Parse() - { - base.Parse(); - var fieldsNode = _contentElement.Descendants().Where(p => p.Name.LocalName == "fields").First(); - foreach (XElement element in fieldsNode.Nodes()) - { - Fields.Add(new FieldObject(element.Value, element.Name.LocalName)); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetRandomRecordsRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetRandomRecordsRequest.cs deleted file mode 100644 index 13da34b8e..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetRandomRecordsRequest.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Xml.Linq; -using UniSpy.Server.WebServer.Aggregate; -using UniSpy.Server.WebServer.Module.Sake.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request -{ - - public class GetRandomRecordsRequest : RequestBase - { - public string Max { get; set; } - public List Fields { get; set; } - public GetRandomRecordsRequest(string rawRequest) : base(rawRequest) - { - Fields = new List(); - } - - public override void Parse() - { - base.Parse(); - var max = _contentElement.Descendants().Where(p => p.Name.LocalName == "max").First().Value; - Max = max; - var fieldsNode = _contentElement.Descendants().Where(p => p.Name.LocalName == "fields").First(); - foreach (XElement element in fieldsNode.Nodes()) - { - Fields.Add(new FieldObject(element.Value, element.Name.LocalName)); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetRecordLimitRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetRecordLimitRequest.cs deleted file mode 100644 index 0403556ff..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetRecordLimitRequest.cs +++ /dev/null @@ -1,13 +0,0 @@ - -using UniSpy.Server.WebServer.Module.Sake.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request -{ - - public class GetRecordLimitRequest : RequestBase - { - public GetRecordLimitRequest(string rawRequest) : base(rawRequest) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetSpecificRecordsRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetSpecificRecordsRequest.cs deleted file mode 100644 index 22114dbae..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/GetSpecificRecordsRequest.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Xml.Linq; -using UniSpy.Server.WebServer.Aggregate; -using UniSpy.Server.WebServer.Module.Sake.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request -{ - - public class GetSpecificRecordsRequest : RequestBase - { - public List RecordIds { get; set; } - public List Fields { get; set; } - public GetSpecificRecordsRequest(string rawRequest) : base(rawRequest) - { - RecordIds = new List(); - Fields = new List(); - } - - public override void Parse() - { - base.Parse(); - var recordidsNode = _contentElement.Descendants().Where(p => p.Name.LocalName == "recordids").First(); - foreach (XElement element in recordidsNode.Nodes()) - { - RecordIds.Add(new FieldObject(element.Value, element.Name.LocalName)); - } - var fieldsNode = _contentElement.Descendants().Where(p => p.Name.LocalName == "fields").First(); - foreach (XElement element in fieldsNode.Nodes()) - { - Fields.Add(new FieldObject(element.Value, element.Name.LocalName)); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/RateRecordRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/RateRecordRequest.cs deleted file mode 100644 index d2da530c7..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/RateRecordRequest.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Module.Sake.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request -{ - - public class RateRecordRequest : RequestBase - { - public string RecordId { get; set; } - public string Rating { get; set; } - public RateRecordRequest(string rawRequest) : base(rawRequest) - { - } - - public override void Parse() - { - base.Parse(); - var recordid = _contentElement.Descendants().Where(p => p.Name.LocalName == "recordid").First().Value; - RecordId = recordid; - var rating = _contentElement.Descendants().Where(p => p.Name.LocalName == "rating").First().Value; - Rating = rating; - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/SearchForRecordsRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/SearchForRecordsRequest.cs deleted file mode 100644 index 8b6fd312c..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/SearchForRecordsRequest.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Xml.Linq; -using UniSpy.Server.WebServer.Aggregate; -using UniSpy.Server.WebServer.Module.Sake.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request -{ - - public class SearchForRecordsRequest : RequestBase - { - public string Filter { get; set; } - public string Sort { get; set; } - public string Offset { get; set; } - public string Max { get; set; } - public string Surrounding { get; set; } - public int OwnerIds { get; set; } - public string CacheFlag { get; set; } - - public List Fields { get; set; } - public SearchForRecordsRequest(string rawRequest) : base(rawRequest) - { - Fields = new List(); - } - - public override void Parse() - { - base.Parse(); - var filter = _contentElement.Descendants().Where(p => p.Name.LocalName == "filter").First().Value; - Filter = filter; - var sort = _contentElement.Descendants().Where(p => p.Name.LocalName == "sort").First().Value; - Sort = sort; - var offset = _contentElement.Descendants().Where(p => p.Name.LocalName == "offset").First().Value; - Offset = offset; - var max = _contentElement.Descendants().Where(p => p.Name.LocalName == "max").First().Value; - Max = max; - var surrounding = _contentElement.Descendants().Where(p => p.Name.LocalName == "surrounding").First().Value; - Surrounding = surrounding; - var ownerids = _contentElement.Descendants().Where(p => p.Name.LocalName == "ownerids").First().Value; - if (int.TryParse(ownerids, out var ids)) - { - OwnerIds = ids; - } - var cacheFlag = _contentElement.Descendants().Where(p => p.Name.LocalName == "cacheFlag").First().Value; - CacheFlag = cacheFlag; - var fieldsNode = _contentElement.Descendants().Where(p => p.Name.LocalName == "fields").First(); - foreach (XElement element in fieldsNode.Nodes()) - { - Fields.Add(new FieldObject(element.Value, element.Name.LocalName)); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Request/UpdateRecordRequest.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Request/UpdateRecordRequest.cs deleted file mode 100644 index 362801bff..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Request/UpdateRecordRequest.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using System.Xml.Linq; -using UniSpy.Server.WebServer.Aggregate; -using UniSpy.Server.WebServer.Module.Sake.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Sake.Contract.Request -{ - - public class UpdateRecordRequest : RequestBase - { - public string RecordId { get; set; } - - public List Values { get; set; } - public UpdateRecordRequest(string rawRequest) : base(rawRequest) - { - Values = new List(); - } - - public override void Parse() - { - base.Parse(); - var recordid = _contentElement.Descendants().Where(p => p.Name.LocalName == "recordid").First().Value; - RecordId = recordid; - var valuesNode = _contentElement.Descendants().Where(p => p.Name.LocalName == "values").First(); - foreach (XElement element in valuesNode.Nodes()) - { - // TODO move this code to RecordFieldObject as static method which takes XElement as input returns a List - // first we find the value name by string "name" - var name = element.Descendants().Where(p => p.Name.LocalName == "name").First().Value; - // then we get the value type by string "value" - var type = element.Descendants().Where(p => p.Name.LocalName == "value").First().Descendants().First().Name.LocalName; - // then we get the actual value by its type we get before - var value = element.Descendants().Where(p => p.Name.LocalName == type).First().Value; - Values.Add(new RecordFieldObject(value, name, type)); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Response/CreateRecordResponse.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Response/CreateRecordResponse.cs deleted file mode 100644 index 2af6986da..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Response/CreateRecordResponse.cs +++ /dev/null @@ -1,28 +0,0 @@ -using UniSpy.Server.WebServer.Module.Sake.Abstraction; -using UniSpy.Server.WebServer.Module.Sake.Contract.Request; -using UniSpy.Server.WebServer.Module.Sake.Contract.Result; - -namespace UniSpy.Server.WebServer.Module.Sake.Contract.Response -{ - public sealed class CreateRecordResponse : ResponseBase - { - public new CreateRecordResult _result => (CreateRecordResult)base._result; - public new CreateRecordRequest _request => (CreateRecordRequest)base._request; - public CreateRecordResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - public override void Build() - { - base.Build(); - _content.Add("CreateRecordResult"); - _content.Add("tableid", _result.TableID); - _content.Add("recordid", _result.RecordID); - - foreach (var field in _result.Fields) - { - _content.Add("fileds", field); - } - base.Build(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Response/GetMyRecordResponse.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Response/GetMyRecordResponse.cs deleted file mode 100644 index 3c1735594..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Response/GetMyRecordResponse.cs +++ /dev/null @@ -1,16 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Sake.Contract.Response -{ - public class GetMyRecordResponse : ResponseBase - { - public GetMyRecordResponse(RequestBase request, ResultBase result) : base(request, result) - { - } - - public override void Build() - { - throw new System.NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs deleted file mode 100644 index 347901480..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Response/SearchForRecordResponse.cs +++ /dev/null @@ -1,39 +0,0 @@ -using UniSpy.Server.WebServer.Module.Sake.Abstraction; -using UniSpy.Server.WebServer.Module.Sake.Contract.Request; -using UniSpy.Server.WebServer.Module.Sake.Contract.Result; - -namespace UniSpy.Server.WebServer.Module.Sake.Contract.Response -{ - internal class SearchForRecordResponse : ResponseBase - { - private new SearchForRecordsRequest _request => (SearchForRecordsRequest)base._request; - private new SearchForRecordsResult _result => (SearchForRecordsResult)base._result; - public SearchForRecordResponse(SearchForRecordsRequest request, SearchForRecordsResult result) : base(request, result) - { - } - - public override void Build() - { - _content.Add("SearchForRecordsResponse"); - _content.Add("SearchForRecordsResult", "Success"); - if (_result.UserData is not null) - { - var temp = Newtonsoft.Json.JsonConvert.DeserializeXNode(_result.UserData.RootElement.ToString()).Root; - // add namespace to root element - temp.Name = SakeSoapEnvelope.SakeNamespace + temp.Name.LocalName; - // Add the namespace to each selected node - foreach (var element in temp.DescendantsAndSelf()) - { - element.Name = SakeSoapEnvelope.SakeNamespace + element.Name.LocalName; - } - _content.Add("values", temp); - } - else - { - _content.Add("values"); - } - - base.Build(); - } - } -} diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Result/CreateRecordResult.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Result/CreateRecordResult.cs deleted file mode 100644 index ea3be3757..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Result/CreateRecordResult.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.WebServer.Module.Sake.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Sake.Contract.Result -{ - public sealed class CreateRecordResult : ResultBase - { - public string TableID { get; init; } - public string RecordID { get; init; } - public List Fields { get; private set; } - public CreateRecordResult() - { - Fields = new List(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Result/GetMyRecordsResult.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Result/GetMyRecordsResult.cs deleted file mode 100644 index aa5933ea0..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Result/GetMyRecordsResult.cs +++ /dev/null @@ -1,15 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Aggregate; - -namespace UniSpy.Server.WebServer.Module.Sake.Contract.Result -{ - public class GetMyRecordsResult : ResultBase - { - public List Records { get; private set; } - public GetMyRecordsResult() - { - Records = new List(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/Result/SearchForRecordsResult.cs b/src/Servers/WebServer/src/Module/Sake/Contract/Result/SearchForRecordsResult.cs deleted file mode 100644 index f8771b552..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Contract/Result/SearchForRecordsResult.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Text.Json; -using UniSpy.Server.WebServer.Module.Sake.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Sake.Contract.Result -{ - public class SearchForRecordsResult : ResultBase - { - public JsonDocument UserData; - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Contract/SakeSoapEnvelope.cs b/src/Servers/WebServer/src/Module/Sake/Contract/SakeSoapEnvelope.cs deleted file mode 100644 index 3b65b7f7c..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Contract/SakeSoapEnvelope.cs +++ /dev/null @@ -1,13 +0,0 @@ -using System.Xml.Linq; -using UniSpy.Server.WebServer.Abstraction; - -namespace UniSpy.Server.WebServer.Module.Sake.Contract -{ - public sealed class SakeSoapEnvelope : SoapEnvelopBase - { - public static XNamespace SakeNamespace = "http://gamespy.net/sake"; - public SakeSoapEnvelope() : base("ns1", SakeNamespace) - { - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Handler/CreateRecordHandler.cs b/src/Servers/WebServer/src/Module/Sake/Handler/CreateRecordHandler.cs deleted file mode 100644 index 5e035d6ea..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Handler/CreateRecordHandler.cs +++ /dev/null @@ -1,23 +0,0 @@ -using Newtonsoft.Json; -using UniSpy.Server.WebServer.Module.Sake.Abstraction; -using UniSpy.Server.WebServer.Module.Sake.Contract.Request; -using UniSpy.Server.WebServer.Application; - -namespace UniSpy.Server.WebServer.Module.Sake.Handler -{ - - public class CreateRecordHandler : CmdHandlerBase - { - protected new CreateRecordRequest _request => (CreateRecordRequest)base._request; - public CreateRecordHandler(Client client, CreateRecordRequest request) : base(client, request) - { - } - protected override void DataOperation() - { - base.DataOperation(); - var jsonStr = JsonConvert.SerializeObject(_request.Values); - // File.WriteAllText(_sakeFilePath, jsonStr); - throw new System.NotImplementedException(); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/src/Module/Sake/Handler/GetMyRecordsHandler.cs b/src/Servers/WebServer/src/Module/Sake/Handler/GetMyRecordsHandler.cs deleted file mode 100644 index 0bb210cc6..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Handler/GetMyRecordsHandler.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System.Linq; -using UniSpy.Server.WebServer.Module.Sake.Abstraction; -using UniSpy.Server.WebServer.Module.Sake.Contract.Request; -using UniSpy.Server.WebServer.Module.Sake.Contract.Result; -using UniSpy.Server.WebServer.Application; - -namespace UniSpy.Server.WebServer.Module.Sake.Handler -{ - - public class GetMyRecordsHandler : CmdHandlerBase - { - private new GetMyRecordsRequest _request => (GetMyRecordsRequest)base._request; - private new GetMyRecordsResult _result { get => (GetMyRecordsResult)base._result; set => base._result = value; } - public GetMyRecordsHandler(Client client, GetMyRecordsRequest request) : base(client, request) - { - } - protected override void DataOperation() - { - base.DataOperation(); - _result = new GetMyRecordsResult(); - - foreach (var field in _request.Fields) - { - var record = _sakeData.FirstOrDefault(x => x.FieldName == field.FieldName && x.FiledType == field.FiledType); - if (record is not null) - { - _result.Records.Add(record); - } - } - } - } -} diff --git a/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs b/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs deleted file mode 100644 index 522b9debf..000000000 --- a/src/Servers/WebServer/src/Module/Sake/Handler/SearchForRecordsHandler.cs +++ /dev/null @@ -1,41 +0,0 @@ -using UniSpy.Server.WebServer.Abstraction; -using UniSpy.Server.WebServer.Module.Sake.Contract.Response; -using UniSpy.Server.WebServer.Module.Sake.Contract.Request; -using UniSpy.Server.WebServer.Application; -using UniSpy.Server.WebServer.Module.Sake.Contract.Result; -using UniSpy.Server.Core.Database.DatabaseModel; -using System.Linq; - -namespace UniSpy.Server.WebServer.Module.Sake.Handler -{ - - internal class SearchForRecordsHandler : CmdHandlerBase - { - private new SearchForRecordsRequest _request => (SearchForRecordsRequest)base._request; - private new SearchForRecordsResult _result { get => (SearchForRecordsResult)base._result; set => base._result = value; } - public SearchForRecordsHandler(Client client, SearchForRecordsRequest request) : base(client, request) - { - _result = new SearchForRecordsResult(); - } - protected override void DataOperation() - { - base.DataOperation(); - // search user data from database - using (var db = new UniSpyContext()) - { - _result.UserData = db.Sakestorages.Where( - t => t.Profileid == _request.OwnerIds - && t.Tableid == _request.TableId) - .Select(s => s.Userdata).FirstOrDefault(); - } - // if (_result.UserData is null) - // { - // throw new WebServer.Exception("There are no sake data found in the database, please manually add it in the database"); - // } - } - protected override void ResponseConstruct() - { - _response = new SearchForRecordResponse(_request, _result); - } - } -} diff --git a/src/Servers/WebServer/src/UniSpy.Server.WebServer.csproj b/src/Servers/WebServer/src/UniSpy.Server.WebServer.csproj deleted file mode 100755 index 8b9381e25..000000000 --- a/src/Servers/WebServer/src/UniSpy.Server.WebServer.csproj +++ /dev/null @@ -1,22 +0,0 @@ - - - - Exe - net6.0 - ..\..\..\..\common\Icon\UniSpy_Logo.ico - Linux - ..\..\..\.. - - - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - - - \ No newline at end of file diff --git a/src/Servers/WebServer/test/Atlas/RawRequests.cs b/src/Servers/WebServer/test/Atlas/RawRequests.cs deleted file mode 100644 index 816fa0784..000000000 --- a/src/Servers/WebServer/test/Atlas/RawRequests.cs +++ /dev/null @@ -1,75 +0,0 @@ -namespace UniSpy.Server.WebServer.Test.Atlas -{ - public class RawRequests - { - public const string CreateMatchlessSession = - @" - - - - XXXXXX - XXXXXX - 0 - - - "; - - public const string CreateSession = - @" - - - - XXXXXX - XXXXXX - 0 - - - "; - - public const string SetReportIntention = - @" - - - - XXXXXX - XXXXXX - 0 - 0 - 0 - XXXXXX - - - "; - - public const string SubmitReport = - @" - - - - XXXXXX - XXXXXX - 0 - 0 - 0 - XXXXXX - - - "; - } -} diff --git a/src/Servers/WebServer/test/Atlas/RequestsTest.cs b/src/Servers/WebServer/test/Atlas/RequestsTest.cs deleted file mode 100644 index 366020b21..000000000 --- a/src/Servers/WebServer/test/Atlas/RequestsTest.cs +++ /dev/null @@ -1,55 +0,0 @@ -using Xunit; -using UniSpy.Server.WebServer.Module.Atlas.Contract.Request; - -namespace UniSpy.Server.WebServer.Test.Atlas -{ - public class RequestsTest - { - // - // These are the SOAP requests of ATLAS (Competition) - // Endpoint: {FQDN}/competition/ - // - [Fact] - public void CreateMatchlessSession() - { - var request = new CreateMatchlessSessionRequest(RawRequests.CreateMatchlessSession); - request.Parse(); - Assert.Equal("XXXXXX", request.Certificate); - Assert.Equal("XXXXXX", request.Proof); - Assert.Equal("0", request.GameId.ToString()); - } - [Fact] - public void CreateSession() - { - var request = new CreateSessionRequest(RawRequests.CreateSession); - request.Parse(); - Assert.Equal("XXXXXX", request.Certificate); - Assert.Equal("XXXXXX", request.Proof); - Assert.Equal("0", request.GameId.ToString()); - } - [Fact] - public void SetReportIntention() - { - var request = new SetReportIntentionRequest(RawRequests.SetReportIntention); - request.Parse(); - Assert.Equal("XXXXXX", request.Certificate); - Assert.Equal("XXXXXX", request.Proof); - Assert.Equal("0", request.CsId.ToString()); - Assert.Equal("0", request.CcId.ToString()); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("XXXXXX", request.Authoritative); - } - [Fact] - public void SubmitReport() - { - var request = new SubmitReportRequest(RawRequests.SubmitReport); - request.Parse(); - Assert.Equal("XXXXXX", request.Certificate); - Assert.Equal("XXXXXX", request.Proof); - Assert.Equal("0", request.CsId.ToString()); - Assert.Equal("0", request.CcId.ToString()); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("XXXXXX", request.Authoritative); - } - } -} diff --git a/src/Servers/WebServer/test/Auth/GameTest.cs b/src/Servers/WebServer/test/Auth/GameTest.cs deleted file mode 100644 index 6e373ed54..000000000 --- a/src/Servers/WebServer/test/Auth/GameTest.cs +++ /dev/null @@ -1,64 +0,0 @@ -using Xunit; - -namespace UniSpy.Server.WebServer.Test.Auth; - -public class GameTest -{ - [Fact] - public void Crysis2Auth() - { - // var correct = @" - // - // - // - // 0 - // 0529e96f6f937c4ccb05427782412d6febba9f8cd983f9245ce34a1446d711bceed0da10748a5420c290ffad9e959de906b2498698dc66ca2b63e2e6b02d344ba9f936b44571eed3f91bcec3154d3dbb258510d13b89ecea7be33a5690b51d6e35acc0fe04f1a41b3f0fd4dd35d9166d8c9d3921142fdc92f9967e927bc8c991 - // - // 0 - // 1 - // 95 - // 95 - // 67436 - // 10040172 - // 1687112622 - // MyCrysis - // asdf19 - // D41D8CD98F00B204E9800998ECF8427E - // aefb5064bbd1eb632fa8d57aab1c49366ce0ee3161cbef19f2b7971b63b811790ecbf6a47b34c55f65a0766b40c261c5d69c394cd320842dd2bccba883d30eae8fdba5d03b21b09bfc600dcb30b1b2f3fbe8077630b006dcb54c4254f14891762f72e7bbfe743eb8baf65f9e8c8d11ebe46f6b59e986b4c394cfbc2c8606e29f - // 010001 - // a75fbef2b50236c991e438efe81f0941a645a23d00e3f9bb409681e6c9b37e92a97c88afe65663081642c494c06860ee5dd45f9adab4d9caac3ed766717027eb76f8e6c6a30cbd32637abacfad0dcaf0e96948d20182766099a5133e89b92c498bc3470d1bab666d549e8af51edc2f009d63ecdff84551ed04138b6129adfd9f - // 5F3E3EB96D4301D21E4F8375BB6FE4800F5CB27CD5B3D7DE49CB46B0B3E75A63A3D534AF7DA0FAB21B8C7A4EF56A316FACEECEAB153AD313876FC2A1895A151982897359328D06258B0125789FEBF928D117B7C8A342F8C40135708A17A65A3B189D3A0B7EBFEF21E1C435A409B1DA9449D6EF17D69BAAD844FADFFA30B227A8 - // - // - // - // "; - // Given - var raw = @" - - - - 1 - 95 - 95 - spyguy - - 0000 - - - - "; - // When - var request = new Module.Auth.Contract.Request.LoginUniqueNickRequest(raw); - request.Parse(); - var handler = new Module.Auth.Handler.LoginUniqueNickHandler(MokeObject.Client,request); - handler.Handle(); - // Then - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/test/Auth/HandlerTest.cs b/src/Servers/WebServer/test/Auth/HandlerTest.cs deleted file mode 100644 index 384ceaf79..000000000 --- a/src/Servers/WebServer/test/Auth/HandlerTest.cs +++ /dev/null @@ -1,34 +0,0 @@ -using System.Collections.Generic; -using Moq; -using UniSpy.Server.WebServer.Handler; -using UniSpy.Server.WebServer.Test.Auth; -using UniSpy.Server.Core.Abstraction.Interface; -using Xunit; -using UniSpy.Server.WebServer.Application; - -namespace UniSpy.Server.WebServer.Test -{ - public class HandlerTest - { - private Client _client = MokeObject.CreateClient(); - - [Fact] - public void SwitcherTest() - { - // Given - var requests = new List(){ - RawRequests.LoginProfile, - RawRequests.LoginPs3Cert, - RawRequests.LoginRemoteAuth, - RawRequests.LoginUniqueNick - }; - foreach (var req in requests) - { - var requestMock = new Mock(); - requestMock.Setup(r => r.Body).Returns(req); - var sw = new CmdSwitcher(_client, requestMock.Object); - sw.Handle(); - } - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/test/Auth/RSATest.cs b/src/Servers/WebServer/test/Auth/RSATest.cs deleted file mode 100644 index 0d1f74e45..000000000 --- a/src/Servers/WebServer/test/Auth/RSATest.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Security.Cryptography; -using UniSpy.Server.Core.Extension; -using UniSpy.Server.Core.Logging; -using Xunit; - -namespace UniSpy.Server.WebServer.Test.Auth -{ - public class RSATest - { - [Fact] - public void KeyGen() - { - using (var rsa = new RSACryptoServiceProvider(1024)) - { - var privateKey = rsa.ExportParameters(true); - var publicKey = rsa.ExportParameters(false); - var d = privateKey.D; - var modulus = privateKey.Modulus; - var exponent = privateKey.Exponent; - - Console.WriteLine(StringExtensions.ConvertByteToHexString(d)); - Console.WriteLine(StringExtensions.ConvertByteToHexString(modulus)); - Console.WriteLine(StringExtensions.ConvertByteToHexString(exponent)); - - - var newPublicParam = new RSAParameters - { - Modulus = modulus, - Exponent = exponent - }; - var newPrivateParam = new RSAParameters - { - Modulus = modulus, - Exponent = exponent, - D = d, - }; - } - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/test/Auth/RawRequests.cs b/src/Servers/WebServer/test/Auth/RawRequests.cs deleted file mode 100644 index 1c916a1f9..000000000 --- a/src/Servers/WebServer/test/Auth/RawRequests.cs +++ /dev/null @@ -1,86 +0,0 @@ -namespace UniSpy.Server.WebServer.Test.Auth -{ - public class RawRequests - { - public const string LoginProfile = - @" - - - - 1 - 0 - 0 - 0 - spyguy@unispy.org - spyguy - XXXXXXXXXXX - - XXXXXXXXXXX - - - - "; - - public const string LoginPs3Cert = - @" - - - - 0 - 0 - 0 - 0001 - 0 - 0001 - - - "; - - public const string LoginRemoteAuth = - @" - - - - 1 - 0 - 0 - 0 - XXXXXXXXXXX - XXXXXXXXXXX - - - "; - - public const string LoginUniqueNick = - @" - - - - 1 - 0 - 0 - spyguy - - XXXXXXXXXXX - - - - "; - } -} diff --git a/src/Servers/WebServer/test/Auth/RequestsTest.cs b/src/Servers/WebServer/test/Auth/RequestsTest.cs deleted file mode 100644 index 96b68812f..000000000 --- a/src/Servers/WebServer/test/Auth/RequestsTest.cs +++ /dev/null @@ -1,163 +0,0 @@ -using System; -using System.Numerics; -using UniSpy.Server.WebServer.Application; -using UniSpy.Server.WebServer.Module.Auth.Contract; -using UniSpy.Server.WebServer.Module.Auth.Contract.Request; -using UniSpy.Server.WebServer.Module.Direct2Game.Contract.Response; -using UniSpy.Server.Core.Extension; -using Xunit; - -namespace UniSpy.Server.WebServer.Test.Auth -{ - public class RequestsTest - { - // - // These are the SOAP requests of AUTH - // Endpoint: {FQDN}/AuthService/ - // - [Fact] - public void LoginProfile() - { - var request = new LoginProfileWithGameIdRequest(RawRequests.LoginProfile); - request.Parse(); - Assert.Equal("1", request.Version.ToString()); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("0", request.PartnerCode.ToString()); - Assert.Equal("0", request.NamespaceId.ToString()); - Assert.Equal("spyguy@unispy.org", request.Email); - Assert.Equal("spyguy", request.Uniquenick); - Assert.Equal("XXXXXXXXXXX", request.CDKey); - Assert.Equal("XXXXXXXXXXX", request.Password); - } - [Fact] - public void LoginPs3Cert() - { - var request = new LoginPs3CertWithGameIdRequest(RawRequests.LoginPs3Cert); - request.Parse(); - Assert.Equal(0, request.GameId); - Assert.Equal(1, request.PartnerCode); - Assert.Equal("0001", request.PS3cert); - } - [Fact] - public void LoginRemoteAuth() - { - var request = new LoginRemoteAuthWithGameIdRequest(RawRequests.LoginRemoteAuth); - request.Parse(); - Assert.Equal("1", request.Version.ToString()); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("0", request.PartnerCode.ToString()); - Assert.Equal("0", request.NamespaceId.ToString()); - Assert.Equal("XXXXXXXXXXX", request.AuthToken); - Assert.Equal("XXXXXXXXXXX", request.Challenge); - } - [Fact] - public void LoginUniqueNick() - { - var request = new LoginUniqueNickRequest(RawRequests.LoginUniqueNick); - request.Parse(); - Assert.Equal("1", request.Version.ToString()); - Assert.Equal("0", request.PartnerCode.ToString()); - Assert.Equal("0", request.NamespaceId.ToString()); - Assert.Equal("spyguy", request.Uniquenick); - Assert.Equal("XXXXXXXXXXX", request.Password); - } - - [Fact] - public void CustomRSATest() - { - BigInteger publicExponent = BigInteger.Parse("010001", System.Globalization.NumberStyles.AllowHexSpecifier); - BigInteger privateExponent = BigInteger.Parse("00af12efb486a5f594f4b86d153cef694fba59bde5005411e271ad9e53ae41bd3183b3b06459de85907bfdcee256180bd450f7f547dd1c81f57e14b477a48cef415f957de5ea723a0050be386fd2c1369761340f23ed43aa4299926107e3c56845ea32685a7ced12a32bfd6b6a2aefe8b8b9fdf0893f486342f36fd6000d691ee1", System.Globalization.NumberStyles.AllowHexSpecifier); - - BigInteger Modulo = BigInteger.Parse("00e2201247fcb3ef29e45e842eee4a1b072ae59c115de6f4e0bb857b4e9282b2ee2b6ce1aef46a5eea7ad7bd0a5a4a969a186e2dd1e379e4a27a0e2120f49da702d4a3b892f5c776fee869d218f145b6e32f32b71063a0222addc256e8fdc977b2324a71370777295d45240f4f5fdf7cd7ab9c2393fdce0781c5118b9e1e905537", System.Globalization.NumberStyles.AllowHexSpecifier); - - var data = new BigInteger(123); - - var enc = BigInteger.ModPow(data, privateExponent, Modulo); - var dec = BigInteger.ModPow(enc, publicExponent, Modulo); - - var signature = enc.ToString("x"); - var bytes = enc.ToByteArray(isBigEndian: true); - var gamespyFormat = BitConverter.ToString(bytes).Replace("-", string.Empty); - - var sigbytes = "0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003020300C06082A864886F70D020505000410".FromHexStringToBytes(); - } - - - [Fact] - public void XmlTest() - { - // XNamespace SoapEnvelopNamespace = "http://schemas.xmlsoap.org/soap/envelope/"; - - // var env = new XElement(SoapEnvelopNamespace + "Envelope", - // new XAttribute(XNamespace.Xmlns + "SOAP-ENV", SoapEnvelopNamespace), - // new XAttribute(XNamespace.Xmlns + "SOAP-ENC", "http://schemas.xmlsoap.org/soap/encoding/"), - // new XAttribute(XNamespace.Xmlns + "xsi", "http://www.w3.org/2001/XMLSchema-instance"), - // new XAttribute(XNamespace.Xmlns + "xsd", "http://www.w3.org/2001/XMLSchema")); - // XNamespace SakeNamespace = "http://gamespy.net/sake"; - // var attri = new XAttribute(XNamespace.Xmlns + "ns1", SakeNamespace); - // env.Add(attri); - // env.SetAttributeValue(XNamespace.Xmlns + "ns1", SakeNamespace); - // var soapEnvelop = new XElement(SoapXElement.Direct2GameSoapHeader); - // soapEnvelop.Add(new XElement(SoapXElement.SoapEnvelopNamespace + "Body")); - // var nn1 = soapEnvelop.NextNode; - - // var _soapBody = new XElement(SoapXElement.SoapEnvelopNamespace + "Body"); - // _soapBody.Add(new XElement(SoapXElement.Direct2GameNamespace + "GetStoreAvailabilityResult")); - - // var e = _soapBody.Elements().First(); - // e.Add(new XElement(SoapXElement.Direct2GameNamespace + "status")); - // e.Elements().First().Add(new XElement(SoapXElement.Direct2GameNamespace + "code", 0)); - // e.Add(new XElement(SoapXElement.Direct2GameNamespace + "storestatusid", 1)); - - // _soapBody.Add(e); - // soapEnvelop.Add(e); - // soapEnvelop.DescendantNodes(); - // soapEnvelop.Descendants(); - // soapEnvelop.Nodes(); - // var nn = soapEnvelop.NextNode; - // var auth = new AuthSoapEnvelope(); - // auth.Add("heello", "hello"); - // auth.Add("bitch", "bitch"); - // // var nn2 = soapEnvelop.FirstNode.NextNode.FirstNode; - // // Given - // var authEle = new AuthXElement("certificate"); - // authEle.Add("authtoken", "XXXXXXXXXXX"); - // authEle.Add("challenge", "XXXXXXXXXXX"); - // authEle.Add("email", "xiaojiuwo@gamspy.com"); - // authEle.Add("password", "XXXXXXXXXXX"); - // authEle.Add("partnercode", "0"); - // authEle.Add("uniquenick", "xiaojiuwo"); - // auth.Add("element", authEle.InnerElement); - - // When - var _content = new AuthSoapEnvelope(); - _content.Add("responseCode", 0); - _content.Add("certificate"); - _content.Add("length", 0); - _content.Add("version", 2); - _content.Add("partnercode", 2); - _content.Add("namespaceid", 4); - _content.Add("userid", 1); - _content.Add("profileid", 1); - _content.Add("expretime", ClientInfo.ExpireTime); - _content.Add("uniqueid", "xiaojiuwo"); - _content.ChangeToElement("Body"); - _content.Add("values"); - _content.Add("value1", 1); - _content.Add("value2", 2); - _content.Add("value3", 3); - // Then - } - - [Fact(Skip = "not implemented")] - public void TestName() - { - // Given - var resp = new GetPurchaseHistoryResponse(null, null); - resp.Build(); - // When - - // Then - } - } -} diff --git a/src/Servers/WebServer/test/GeneralTest.cs b/src/Servers/WebServer/test/GeneralTest.cs deleted file mode 100644 index 88f28a44e..000000000 --- a/src/Servers/WebServer/test/GeneralTest.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Moq; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.Core.Encryption; -using UniSpy.Server.Core.Network.Http.Server; -using UniSpy.Server.WebServer.Handler; -using Xunit; - -namespace UniSpy.Server.WebServer.Test -{ - public class GeneralTest - { - [Fact] - public void InlegalMessageTest() - { - var client = MokeObject.CreateClient(); - var req = new Mock(); - req.Setup(s => s.Body).Returns("username=admin&psd=Feefifofum"); - req.Setup(s => s.Url).Returns(new System.Uri("abcdefg")); - var switcher = new CmdSwitcher(client, req.Object); - switcher.Handle(); - } - - // [Fact] - // public void BufferCacheTest() - // { - // // Given - // var req1 = "POST /AuthService/AuthService.asmx HTTP/1.1\r\nHost: capricorn.auth.pubsvs.gamespy.com\r\nUser-Agent: GameSpyHTTP/1.0\r\nConnection: close\r\nContent-Length: 861\r\nContent-Type: text/xml\r\nSOAPAction: \"http://gamespy.net/AuthService/LoginUniqueNick\"\r\n"; - // var req2 = "\r\n19595asdf210002899d0429a3bc5c24934320e526bed7ad645fb56c7783e2c8afef77e0dd1005d3383b58eb6919baae85881183148a4b4ebe1169475c85e305a2ca4f2d30d9ecf2275dda0706a14d15c5eb755bbca034168b13997f9111ebe821d640f64622586f231479a8516d47c046a819b19e03ba4697c68e3879dafab9380032333233"; - // // When - // var cache = new HttpBufferCache(); - // string completeBuffer; - // Assert.False(cache.ProcessBuffer(req1, out completeBuffer)); - - // Assert.True(cache.ProcessBuffer(req2, out completeBuffer)); - - // Assert.True(cache.InCompleteBuffer == null); - // var resultreq = "POST /AuthService/AuthService.asmx HTTP/1.1\r\nHost: capricorn.auth.pubsvs.gamespy.com\r\nUser-Agent: GameSpyHTTP/1.0\r\nConnection: close\r\nContent-Length: 861\r\nContent-Type: text/xml\r\nSOAPAction: \"http://gamespy.net/AuthService/LoginUniqueNick\"\r\n\r\n19595asdf210002899d0429a3bc5c24934320e526bed7ad645fb56c7783e2c8afef77e0dd1005d3383b58eb6919baae85881183148a4b4ebe1169475c85e305a2ca4f2d30d9ecf2275dda0706a14d15c5eb755bbca034168b13997f9111ebe821d640f64622586f231479a8516d47c046a819b19e03ba4697c68e3879dafab9380032333233"; - // Assert.True(completeBuffer == resultreq); - // // Then - // } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/test/MokeObject.cs b/src/Servers/WebServer/test/MokeObject.cs deleted file mode 100644 index 59db56916..000000000 --- a/src/Servers/WebServer/test/MokeObject.cs +++ /dev/null @@ -1,23 +0,0 @@ -using System.Net; -using Moq; -using UniSpy.Server.Core.Abstraction.Interface; -using UniSpy.Server.WebServer.Application; - -namespace UniSpy.Server.WebServer.Test -{ - public static class MokeObject - { - public static Client Client = CreateClient(); - - public static Client CreateClient(string ipAddress = "192.168.1.2", int port = 9999) - { - var managerMock = new Mock(); - var connectionMock = new Mock(); - connectionMock.Setup(s => s.RemoteIPEndPoint).Returns(new IPEndPoint(IPAddress.Parse(ipAddress), port)); - connectionMock.Setup(s => s.Manager).Returns(managerMock.Object); - connectionMock.Setup(s => s.ConnectionType).Returns(NetworkConnectionType.Http); - var serverMock = new WebServer.Application.Server(managerMock.Object); - return new Client(connectionMock.Object, serverMock); - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/test/Racing/RawRequests.cs b/src/Servers/WebServer/test/Racing/RawRequests.cs deleted file mode 100644 index 02726389d..000000000 --- a/src/Servers/WebServer/test/Racing/RawRequests.cs +++ /dev/null @@ -1,124 +0,0 @@ -namespace UniSpy.Server.WebServer.Test.Racing -{ - public class RawRequests - { - public const string GetContestData = - @" - - - - 0 - 0 - 0 - - - "; - - public const string GetFriendRankings = - @" - - - - 0 - 0 - 0 - 0 - - - "; - - public const string GetRegionalData = - @" - - - - 0 - 0 - - - "; - - public const string GetTenAboveRankings = - @" - - - - 0 - 0 - 0 - 0 - - - "; - - public const string GetTopTenRankings = - @" - - - - 0 - 0 - 0 - - - "; - - public const string SubmitGhost = - @" - - - - 0 - 0 - 0 - 0 - XXXXXX - 0 - - - "; - - public const string SubmitScores = - @" - - - - 0 - 0 - 0 - 0 - 0 - XXXXXX - - - "; - } -} diff --git a/src/Servers/WebServer/test/Racing/RequestsTest.cs b/src/Servers/WebServer/test/Racing/RequestsTest.cs deleted file mode 100644 index ba212838a..000000000 --- a/src/Servers/WebServer/test/Racing/RequestsTest.cs +++ /dev/null @@ -1,83 +0,0 @@ -using Xunit; -using UniSpy.Server.WebServer.Module.Racing.Contract.Request; - -namespace UniSpy.Server.WebServer.Test.Racing -{ - public class RequestsTest - { - // - // These are the SOAP requests of RACE - // Endpoint: {FQDN}/RaceService/ - // - [Fact] - public void GetContestData() - { - var request = new GetContestDataRequest(RawRequests.GetContestData); - request.Parse(); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("0", request.RegionId.ToString()); - Assert.Equal("0", request.CourseId.ToString()); - } - [Fact] - public void GetFriendRankings() - { - var request = new GetFriendRankingsRequest(RawRequests.GetFriendRankings); - request.Parse(); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("0", request.RegionId.ToString()); - Assert.Equal("0", request.CourseId.ToString()); - Assert.Equal("0", request.ProfileId.ToString()); - } - [Fact] - public void GetRegionalData() - { - var request = new GetRegionalDataRequest(RawRequests.GetRegionalData); - request.Parse(); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("0", request.RegionId.ToString()); - } - [Fact] - public void GetTenAboveRankings() - { - var request = new GetTenAboveRankingsRequest(RawRequests.GetTenAboveRankings); - request.Parse(); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("0", request.RegionId.ToString()); - Assert.Equal("0", request.CourseId.ToString()); - Assert.Equal("0", request.ProfileId.ToString()); - } - [Fact] - public void GetTopTenRankings() - { - var request = new GetTopTenRankingsRequest(RawRequests.GetTopTenRankings); - request.Parse(); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("0", request.RegionId.ToString()); - Assert.Equal("0", request.CourseId.ToString()); - } - [Fact] - public void SubmitGhost() - { - var request = new SubmitGhostRequest(RawRequests.SubmitGhost); - request.Parse(); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("0", request.RegionId.ToString()); - Assert.Equal("0", request.CourseId.ToString()); - Assert.Equal("0", request.ProfileId.ToString()); - Assert.Equal("XXXXXX", request.Score); - Assert.Equal("0", request.FileId.ToString()); - } - [Fact] - public void SubmitScores() - { - var request = new SubmitScoresRequest(RawRequests.SubmitScores); - request.Parse(); - Assert.Equal("0", request.GameData.ToString()); - Assert.Equal("0", request.RegionId.ToString()); - Assert.Equal("0", request.ProfileId.ToString()); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("0", request.ScoreMode.ToString()); - Assert.Equal("XXXXXX", request.ScoreDatas); - } - } -} diff --git a/src/Servers/WebServer/test/Sake/GameTest.cs b/src/Servers/WebServer/test/Sake/GameTest.cs deleted file mode 100644 index cfdc368d2..000000000 --- a/src/Servers/WebServer/test/Sake/GameTest.cs +++ /dev/null @@ -1,60 +0,0 @@ -using Moq; -using UniSpy.Server.Core.Abstraction.Interface; -using Xunit; - -namespace UniSpy.Server.WebServer.Test.Sake -{ - public class GameTest - { - [Fact] - public void Crysis2SakeTest() - { - var raw = @" - - - - 3300 - 8TTq4M - 0000000000000000000000__ - DEDICATEDSTATS - PROFILE = 35 - recordid - 0 - 1 - 0 - 2 - 0 - - DATA - recordid - - - - "; - - var req = new Mock(); - req.Setup(r => r.Body).Returns(raw); - - var switcher = new WebServer.Handler.CmdSwitcher(MokeObject.Client, req.Object); - switcher.Handle(); - - } - - [Fact] - public void Gunbrosand20231018() - { - var raw = "33008TTq4M0000000000000000000000__DEDICATEDSTATSPROFILE = 7recordid0100DATArecordid"; - var req = new Mock(); - req.Setup(r => r.Body).Returns(raw); - - var switcher = new WebServer.Handler.CmdSwitcher(MokeObject.Client, req.Object); - switcher.Handle(); - - } - } -} \ No newline at end of file diff --git a/src/Servers/WebServer/test/Sake/HandlerTest.cs b/src/Servers/WebServer/test/Sake/HandlerTest.cs deleted file mode 100644 index c86bf3c41..000000000 --- a/src/Servers/WebServer/test/Sake/HandlerTest.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System.Linq; -using System.Xml.Linq; -using UniSpy.Server.WebServer.Module.Sake.Handler; -using UniSpy.Server.WebServer.Module.Sake.Contract.Request; -using Xunit; - -namespace UniSpy.Server.WebServer.Test.Sake -{ - public class HandlerTest - { - [Fact] - public void CreateRecordTest() - { - var client = MokeObject.CreateClient(); - var request = new CreateRecordRequest(RawRequests.CreateRecord); - var handler = new CreateRecordHandler(client, request); - Assert.Throws(() => handler.Handle()); - } - [Fact] - public void JsonDocumentTest() - { - var data = "[{\"binaryDataValue\":\"ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg\"},{\"intValue\":\"0\"}]"; - - var jsonDoc = System.Text.Json.JsonDocument.Parse(data); - var dataList = jsonDoc.RootElement.EnumerateArray(); - foreach (var element in dataList) - { - var kvEmu = element.EnumerateObject(); - foreach (var kv in kvEmu) - { - var key = kv.Name; - var value = kv.Value.GetString(); - } - } - // Given - - // When - - // Then - } - [Fact] - public void XMLToJsonTest() - { - var data = @" - - - - Success - - - - - ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAg - - - - - 0 - - - - - - - "; - var xele = XElement.Parse(data); - var array = xele.Descendants().Where(d => d.Name.LocalName == "ArrayOfRecordValue").First(); - var jsonText = Newtonsoft.Json.JsonConvert.SerializeXNode(array); - jsonText = jsonText.Replace("ns1:", ""); - // var newJsonText = jsonText.Replace("{","n.) - jsonDoc.RootElement.ToString(); - - var newXele = Newtonsoft.Json.JsonConvert.DeserializeXNode(jsonText).Root; - var ns = Module.Sake.Contract.SakeSoapEnvelope.SakeNamespace; - // Add a namespace declaration to the root element - newXele.Add(new XAttribute(XNamespace.Xmlns + "ns1", ns)); - - // Select the nodes that need the namespace - // var recordValueNodes = newXele.Descendants(Module.Sake.Contract.SakeSoapEnvelope.SakeNamespace + "RecordValue"); - - // Add the namespace to each selected node - foreach (var childNode in newXele.DescendantsAndSelf()) - { - foreach (var attribute in childNode.Attributes()) - { - attribute.Remove(); - childNode.Add(new XAttribute(ns + attribute.Name.LocalName, attribute.Value)); - } - foreach (var element in childNode.Elements()) - { - element.Name = ns + element.Name.LocalName; - } - } - - // Print the modified XML content - string modifiedXml = newXele.ToString(); - // var xeleStr = newXele.ToString().Replace("<", " - - - - 0 - XXXXXX - xxxxxxxx_YYYYYYYYYY__ - nicks - - - "; - - public const string RateRecord = - @" - - - - 0 - XXXXXX - xxxxxxxx_YYYYYYYYYY__ - test - 158 - 200 - - - "; - - public const string GetRandomRecords = - @" - - - - 0 - XXXXXX - xxxxxxxx_YYYYYYYYYY__ - levels - 1 - - recordid - score - - - - "; - - public const string GetSpecificRecords = - @" - - - - 0 - XXXXXX - xxxxxxxx_YYYYYYYYYY__ - scores - - 1 - 2 - 4 - 5 - - - recordid - ownerid - score - - - - "; - - public const string GetMyRecords = - @" - - - - 0 - XXXXXX - xxxxxxxx_YYYYYYYYYY__ - test - - recordid - ownerid - MyByte - MyShort - MyInt - MyFloat - MyAsciiString - MyUnicodeString - MyBoolean - MyDateAndTime - MyBinaryData - MyFileID - num_ratings - average_rating - - - - "; - - public const string SearchForRecords = - @" - - - - 0 - XXXXXX - xxxxxxxx_YYYYYYYYYY__ - scores - - - 0 - 3 - 0 - - 0 - - score - recordid - - - - "; - - public const string DeleteRecord = - @" - - - - 0 - XXXXXX - xxxxxxxx_YYYYYYYYYY__ - test - 150 - - - "; - - public const string UpdateRecord = - @" - - - - 0 - XXXXXX - xxxxxxxx_YYYYYYYYYY__ - test - 158 - - - MyByte - - - 123 - - - - - MyShort - - - 12345 - - - - - MyInt - - - 123456789 - - - - - MyFloat - - - 3.141593 - - - - - MyAsciiString - - - ascii - - - - - MyUnicodeString - - - unicode - - - - - MyBoolean - - - 1 - - - - - MyDateAndTime - - - 2020-05-21T11:13:41Z - - - - - MyBinaryData - - - EjRWq80= - - - - - - - "; - - public const string CreateRecord = - @" - - - - 0 - XXXXXX - xxxxxxxx_YYYYYYYYYY__ - test - - - MyAsciiString - - - this is a record - - - - - - - "; - } -} diff --git a/src/Servers/WebServer/test/Sake/RequestsTest.cs b/src/Servers/WebServer/test/Sake/RequestsTest.cs deleted file mode 100644 index 55903d83a..000000000 --- a/src/Servers/WebServer/test/Sake/RequestsTest.cs +++ /dev/null @@ -1,181 +0,0 @@ -using UniSpy.Server.WebServer.Module.Sake.Contract.Request; -using Xunit; - -namespace UniSpy.Server.WebServer.Test.Sake -{ - public class RequestsTest - { - // - // These are the SOAP requests of SAKE - // Endpoint: {FQDN}/sake/ - // - [Fact] - public void GetRecordLimit() - { - var request = new GetRecordLimitRequest(RawRequests.GetRecordLimit); - request.Parse(); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("XXXXXX", request.SecretKey); - Assert.Equal("xxxxxxxx_YYYYYYYYYY__", request.LoginTicket); - Assert.Equal("nicks", request.TableId); - } - [Fact] - public void RateRecord() - { - var request = new RateRecordRequest(RawRequests.RateRecord); - request.Parse(); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("XXXXXX", request.SecretKey); - Assert.Equal("xxxxxxxx_YYYYYYYYYY__", request.LoginTicket); - Assert.Equal("test", request.TableId); - Assert.Equal("158", request.RecordId); - Assert.Equal("200", request.Rating); - } - [Fact] - public void GetRandomRecords() - { - var request = new GetRandomRecordsRequest(RawRequests.GetRandomRecords); - request.Parse(); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("XXXXXX", request.SecretKey); - Assert.Equal("xxxxxxxx_YYYYYYYYYY__", request.LoginTicket); - Assert.Equal("levels", request.TableId); - Assert.Equal("1", request.Max); - Assert.Equal("recordid", request.Fields[0].FieldName); - Assert.Equal("string", request.Fields[0].FiledType); - Assert.Equal("score", request.Fields[1].FieldName); - Assert.Equal("string", request.Fields[1].FiledType); - } - [Fact] - public void GetSpecificRecords() - { - var request = new GetSpecificRecordsRequest(RawRequests.GetSpecificRecords); - request.Parse(); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("XXXXXX", request.SecretKey); - Assert.Equal("xxxxxxxx_YYYYYYYYYY__", request.LoginTicket); - Assert.Equal("scores", request.TableId); - Assert.Equal("1", request.RecordIds[0].FieldName); - Assert.Equal("int", request.RecordIds[0].FiledType); - Assert.Equal("2", request.RecordIds[1].FieldName); - Assert.Equal("int", request.RecordIds[1].FiledType); - Assert.Equal("4", request.RecordIds[2].FieldName); - Assert.Equal("int", request.RecordIds[2].FiledType); - Assert.Equal("5", request.RecordIds[3].FieldName); - Assert.Equal("int", request.RecordIds[3].FiledType); - Assert.Equal("recordid", request.Fields[0].FieldName); - Assert.Equal("string", request.Fields[0].FiledType); - Assert.Equal("ownerid", request.Fields[1].FieldName); - Assert.Equal("string", request.Fields[1].FiledType); - Assert.Equal("score", request.Fields[2].FieldName); - Assert.Equal("string", request.Fields[2].FiledType); - } - [Fact] - public void GetMyRecords() - { - var request = new GetMyRecordsRequest(RawRequests.GetMyRecords); - request.Parse(); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("XXXXXX", request.SecretKey); - Assert.Equal("xxxxxxxx_YYYYYYYYYY__", request.LoginTicket); - Assert.Equal("test", request.TableId); - Assert.Equal("recordid", request.Fields[0].FieldName); - Assert.Equal("string", request.Fields[0].FiledType); - Assert.Equal("ownerid", request.Fields[1].FieldName); - Assert.Equal("string", request.Fields[1].FiledType); - Assert.Equal("MyByte", request.Fields[2].FieldName); - Assert.Equal("string", request.Fields[2].FiledType); - Assert.Equal("MyShort", request.Fields[3].FieldName); - Assert.Equal("string", request.Fields[3].FiledType); - Assert.Equal("MyInt", request.Fields[4].FieldName); - Assert.Equal("string", request.Fields[4].FiledType); - Assert.Equal("MyFloat", request.Fields[5].FieldName); - Assert.Equal("string", request.Fields[5].FiledType); - Assert.Equal("MyAsciiString", request.Fields[6].FieldName); - Assert.Equal("string", request.Fields[6].FiledType); - Assert.Equal("MyUnicodeString", request.Fields[7].FieldName); - Assert.Equal("string", request.Fields[7].FiledType); - Assert.Equal("MyBoolean", request.Fields[8].FieldName); - Assert.Equal("string", request.Fields[8].FiledType); - Assert.Equal("MyDateAndTime", request.Fields[9].FieldName); - Assert.Equal("string", request.Fields[9].FiledType); - Assert.Equal("MyBinaryData", request.Fields[10].FieldName); - Assert.Equal("string", request.Fields[10].FiledType); - Assert.Equal("MyFileID", request.Fields[11].FieldName); - Assert.Equal("string", request.Fields[11].FiledType); - Assert.Equal("num_ratings", request.Fields[12].FieldName); - Assert.Equal("string", request.Fields[12].FiledType); - Assert.Equal("average_rating", request.Fields[13].FieldName); - Assert.Equal("string", request.Fields[13].FiledType); - } - [Fact] - public void SearchForRecords() - { - var request = new SearchForRecordsRequest(RawRequests.SearchForRecords); - request.Parse(); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("XXXXXX", request.SecretKey); - Assert.Equal("xxxxxxxx_YYYYYYYYYY__", request.LoginTicket); - Assert.Equal("scores", request.TableId); - Assert.Equal("", request.Filter); - Assert.Equal("", request.Sort); - Assert.Equal("0", request.Offset); - Assert.Equal("3", request.Max); - Assert.Equal("0", request.Surrounding); - Assert.Equal(2, request.OwnerIds); - Assert.Equal("0", request.CacheFlag); - Assert.Equal("score", request.Fields[0].FieldName); - Assert.Equal("string", request.Fields[0].FiledType); - Assert.Equal("recordid", request.Fields[1].FieldName); - Assert.Equal("string", request.Fields[1].FiledType); - } - [Fact] - public void DeleteRecord() - { - var request = new DeleteRecordRequest(RawRequests.DeleteRecord); - request.Parse(); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("XXXXXX", request.SecretKey); - Assert.Equal("xxxxxxxx_YYYYYYYYYY__", request.LoginTicket); - Assert.Equal("test", request.TableId); - Assert.Equal("150", request.RecordId); - } - // - // TODO: Deserialization of RecordFields - // - [Fact] - public void UpdateRecord() - { - var request = new UpdateRecordRequest(RawRequests.UpdateRecord); - request.Parse(); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("XXXXXX", request.SecretKey); - Assert.Equal("xxxxxxxx_YYYYYYYYYY__", request.LoginTicket); - Assert.Equal("test", request.TableId); - Assert.Equal("158", request.RecordId); - - // Assert.Equal("score", request.Values[0].FieldName); - // Assert.Equal("string", request.Values[0].FiledType); - // Assert.Equal("recordid", request.Values[1].FieldName); - // Assert.Equal("string", request.Values[1].FiledType); - } - // - // TODO: Deserialization of RecordFields - // - [Fact] - public void CreateRecord() - { - var request = new CreateRecordRequest(RawRequests.CreateRecord); - request.Parse(); - Assert.Equal("0", request.GameId.ToString()); - Assert.Equal("XXXXXX", request.SecretKey); - Assert.Equal("xxxxxxxx_YYYYYYYYYY__", request.LoginTicket); - Assert.Equal("test", request.TableId); - - // Assert.Equal("score", request.Values[0].FieldName); - // Assert.Equal("string", request.Values[0].FiledType); - // Assert.Equal("recordid", request.Values[1].FieldName); - // Assert.Equal("string", request.Values[1].FiledType); - } - } -} diff --git a/src/Servers/WebServer/test/Sake/ResponseTest.cs b/src/Servers/WebServer/test/Sake/ResponseTest.cs deleted file mode 100644 index b14d2505d..000000000 --- a/src/Servers/WebServer/test/Sake/ResponseTest.cs +++ /dev/null @@ -1,27 +0,0 @@ -using UniSpy.Server.WebServer.Module.Sake.Contract.Request; -using UniSpy.Server.WebServer.Module.Sake.Contract.Response; -using UniSpy.Server.WebServer.Module.Sake.Contract.Result; -using Xunit; - -namespace UniSpy.Server.WebServer.Test.Sake -{ - public class ResponseTest - { - [Fact] - public void CreateRecordTest() - { - var request = new CreateRecordRequest(RawRequests.CreateRecord); - request.Parse(); - var result = new CreateRecordResult() - { - RecordID = "0", - TableID = "0", - }; - result.Fields.Add("hello0"); - result.Fields.Add("hello1"); - result.Fields.Add("hello2"); - var response = new CreateRecordResponse(request, result); - response.Build(); - } - } -} diff --git a/src/Servers/WebServer/test/UniSpy.Server.WebServer.Test.csproj b/src/Servers/WebServer/test/UniSpy.Server.WebServer.Test.csproj deleted file mode 100644 index 9b120467c..000000000 --- a/src/Servers/WebServer/test/UniSpy.Server.WebServer.Test.csproj +++ /dev/null @@ -1,31 +0,0 @@ - - - - net6.0 - false - ..\..\..\..\common\Icon\UniSpy_Logo.ico - - - AnyCPU - ..\..\..\..\build\$(Configuration) - - - ..\..\..\..\build\$(Configuration) - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - \ No newline at end of file diff --git a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/Conneciton/Client.cs b/src/backends/__init__.py similarity index 100% rename from src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/Conneciton/Client.cs rename to src/backends/__init__.py diff --git a/src/backends/gamespy/library/abstractions/cmd_handler_base.py b/src/backends/gamespy/library/abstractions/cmd_handler_base.py new file mode 100644 index 000000000..9229fdc08 --- /dev/null +++ b/src/backends/gamespy/library/abstractions/cmd_handler_base.py @@ -0,0 +1,15 @@ +import abc +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from library.abstractions.request_base import RequestBase + + +class CmdHandlerBase(abc.ABC): + _request: RequestBase + + def handle(self): + self.request_check() + + def request_check(self): + pass diff --git a/src/backends/gamespy/library/abstractions/request_base.py b/src/backends/gamespy/library/abstractions/request_base.py new file mode 100644 index 000000000..5c834b4c6 --- /dev/null +++ b/src/backends/gamespy/library/abstractions/request_base.py @@ -0,0 +1,28 @@ +import abc +from dataclasses import dataclass +from uuid import UUID + +import jsonschema + + +@dataclass +class RequestBase(abc.ABC): + """ + The ultimate request base class of all gamespy requests + """ + + server_id: UUID + raw_request: object + json_schema = { + "type": "object", + "properties": { + "server_id": { + "type": "string", + "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", + }, + }, + } + pass + + def validate(self) -> None: + jsonschema.validate(self.__dict__, self.json_schema) diff --git a/src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/Conneciton/Server.cs b/src/backends/gamespy/library/contracts/chat.py similarity index 100% rename from src/Servers/NatNegotiation/src/Aggregate/GameTrafficRelay/Conneciton/Server.cs rename to src/backends/gamespy/library/contracts/chat.py diff --git a/src/backends/gamespy/protocols/chat/requests.py b/src/backends/gamespy/protocols/chat/requests.py new file mode 100644 index 000000000..9bd508595 --- /dev/null +++ b/src/backends/gamespy/protocols/chat/requests.py @@ -0,0 +1,12 @@ + + +# import abc +# from dataclasses import dataclass +# from servers.chat.enum +# @dataclass +# class RequestBase(abc.ABC): +# raw_request:str +# command_name:str + +# class LoginRequest(RequestBase): +# request_type:LoginRequestType \ No newline at end of file diff --git a/src/backends/gamespy/protocols/natneg/aggregates/init_packet_info.py b/src/backends/gamespy/protocols/natneg/aggregates/init_packet_info.py new file mode 100644 index 000000000..9cae60973 --- /dev/null +++ b/src/backends/gamespy/protocols/natneg/aggregates/init_packet_info.py @@ -0,0 +1,33 @@ +from mongoengine import ( + Document, + StringField, + IntField, + UUIDField, + EnumField, + BooleanField, +) + +from servers.natneg.enums.general import NatClientIndex, NatPortType +import datetime + + +class InitPacketInfo(Document): + server_id = UUIDField(binary=False, required=True) + cookie = IntField(required=True) + version = IntField(required=True) + port_type = EnumField(NatPortType, required=True) + client_index = EnumField(NatClientIndex, required=True) + game_name = StringField(required=True) + use_game_port = BooleanField(required=True) + public_ip = StringField(required=True) + public_port = IntField(required=True) + private_ip = StringField(required=True) + private_port = IntField(required=True) + meta = {"expireAfterSeconds": int(datetime.timedelta(minutes=3).total_seconds())} + + +class NatFailInfo(Document): + public_ip_address1 = StringField(required=True) + public_ip_address2 = StringField(required=True) + meta = {"expireAfterSeconds": int(datetime.timedelta(days=1).total_seconds())} + """expire after 1 day""" diff --git a/src/backends/gamespy/protocols/natneg/applications/data.py b/src/backends/gamespy/protocols/natneg/applications/data.py new file mode 100644 index 000000000..57300f77d --- /dev/null +++ b/src/backends/gamespy/protocols/natneg/applications/data.py @@ -0,0 +1,7 @@ +from servers.natneg.contracts.requests import InitRequest +from backends.gamespy.protocols.natneg.aggregates.init_packet_info import InitPacketInfo + + +def store_init_packet(request: InitRequest): + json_dict = request.to_serializable_dict() + info = InitPacketInfo(json_dict) diff --git a/src/backends/gamespy/protocols/natneg/contracts/requests.py b/src/backends/gamespy/protocols/natneg/contracts/requests.py new file mode 100644 index 000000000..79513449e --- /dev/null +++ b/src/backends/gamespy/protocols/natneg/contracts/requests.py @@ -0,0 +1,66 @@ +from dataclasses import dataclass +from servers.natneg.enums.general import ( + NatClientIndex, + NatPortMappingScheme, + NatPortType, + NatType, + PreInitState, + RequestType, +) +from typing import Union + +import backends.gamespy.library.abstractions.request_base as lib + +@dataclass +class RequestBase(lib.RequestBase): + raw_request: list + version: int + cookie: list + port_type: NatPortType + command_name: Union[RequestType, int] + +@dataclass +class CommonRequestBase(RequestBase): + client_index: Union[NatClientIndex, int] + use_game_port: bool + +@dataclass +class AddressCheckRequest(CommonRequestBase): + pass + +@dataclass +class ConnectAckRequest(RequestBase): + client_index: Union[NatClientIndex, int] + +@dataclass +class ConnectRequest(RequestBase): + """ + Server will send this request to client to let them connect to each other + """ + + client_index: Union[NatClientIndex, int] + +@dataclass +class ErtAckRequest(CommonRequestBase): + pass + +@dataclass +class InitRequest(CommonRequestBase): + game_name: str = None + private_ip_address: str = None + +@dataclass +class NatifyRequest(CommonRequestBase): + pass + +@dataclass +class PreInitRequest(RequestBase): + state: Union[PreInitState, int] + target_cookie: list + +@dataclass +class ReportRequest(CommonRequestBase): + is_nat_success: bool + game_name: str + nat_type: NatType + mapping_scheme: NatPortMappingScheme diff --git a/src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py b/src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py new file mode 100644 index 000000000..ee4dfd319 --- /dev/null +++ b/src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py @@ -0,0 +1,196 @@ +from dataclasses import dataclass +from typing import Union + +import jsonschema +from servers.presence_connection_manager.aggregates.user_status import UserStatus +from servers.presence_connection_manager.aggregates.user_status_info import ( + UserStatusInfo, +) +from servers.presence_connection_manager.enums.general import ( + LoginType, + PublicMasks, + SdkRevisionType, +) + +import backends.gamespy.library.abstractions.request_base as lib + + +@dataclass +class RequestBase(lib.RequestBase): + raw_request: str + json_schema = { + "type": "object", + "properties": { + "raw_request": {"type": "string"}, + }, + } + + def validate(self) -> None: + super().validate() + jsonschema.validate(self.__dict__, self.json_schema) + + +# region buddy +@dataclass +class AddBuddyRequest(RequestBase): + friend_profile_id: int + reason: str + json_schema = { + "type": "object", + "properties": { + "friend_profile_id": {"type": "number"}, + "reason": {"type": "string"}, + }, + } + + def validate(self) -> None: + super().validate() + jsonschema.validate(self.__dict__, self.json_schema) + + +@dataclass +class DelBuddyRequest(RequestBase): + friend_profile_id: int + + +@dataclass +class InviteToRequest(RequestBase): + product_id: int + profile_id: int + session_key: str + """the invite target profile id""" + + +@dataclass +class StatusInfoRequest(RequestBase): + is_get_status_info: bool + profile_id: int + namespace_id: int + status_info: UserStatusInfo + + +@dataclass +class StatusRequest(RequestBase): + status: UserStatus + is_get_status: bool + + +# region general +@dataclass +class KeepAliveRequest(RequestBase): + pass + + +@dataclass +class LoginRequest(RequestBase): + user_challenge: str + response: str + unique_nick: str + user_data: str + namespace_id: int + auth_token: str + nick: str + email: str + product_id: int + type: Union[LoginType, int] + sdk_revision_type: Union[SdkRevisionType, int] + game_port: int + user_id: int + profile_id: int + partner_id: int + game_name: int + quiet_mode_flags: int + firewall: bool + + +@dataclass +class LogoutRequest(RequestBase): + pass + + +# region profile +@dataclass +class AddBlockRequest(RequestBase): + taget_id: int + + +@dataclass +class GetProfileRequest(RequestBase): + profile_id: int + session_key: str + + +@dataclass +class NewProfileRequest(RequestBase): + is_replace_nick_name: bool + session_key: str + new_nick: str + old_nick: str + + +@dataclass +class RegisterCDKeyRequest(RequestBase): + session_key: str + cdkey_enc: str + + +@dataclass +class RegisterNickRequest(RequestBase): + unique_nick: str + session_key: str + partner_id: int + + +@dataclass +class UpdateProfileRequest(RequestBase): + has_public_mask_flag: bool = None + public_mask: Union[PublicMasks, int] = None + session_key: str = None + partner_id: int = None + nick: str = None + uniquenick: str = None + has_first_name_flag: bool = None + first_name: str = None + has_last_name_flag: bool = None + last_name: str = None + has_icq_flag: bool = None + icq_uin: int = None + has_home_page_flag: bool = None + home_page: str = None + has_birthday_flag: bool = False + birth_day: int = None + birth_month: int = None + birth_year: int = None + has_sex_flag: bool = False + sex: bool = None + has_zip_code: bool = False + zip_code: str = None + has_country_code: bool = False + country_code: str = None + + +@dataclass +class UpdateUiRequest(RequestBase): + cpubrandid: str = None + cpuspeed: str = None + memory: str = None + videocard1ram: str = None + videocard2ram: str = None + connectionid: str = None + connectionspeed: str = None + hasnetwork: str = None + pic: str = None + + +if __name__ == "__main__": + from jsonschema import validate + + data = { + "server_id": "550e8400-e29b-41d4-a716-446655440000", + "raw_request": "hello", + "friend_profile_id": 1, + "reason": "hello", + } + r = AddBuddyRequest(**data) + r.validate() + pass diff --git a/src/Servers/QueryReport/src/V2/Aggregate/Redis/ChannelInfo.cs b/src/backends/gamespy/protocols/presence_search_player/data.py similarity index 100% rename from src/Servers/QueryReport/src/V2/Aggregate/Redis/ChannelInfo.cs rename to src/backends/gamespy/protocols/presence_search_player/data.py diff --git a/src/backends/gamespy/protocols/presence_search_player/requests.py b/src/backends/gamespy/protocols/presence_search_player/requests.py new file mode 100644 index 000000000..91f2c868d --- /dev/null +++ b/src/backends/gamespy/protocols/presence_search_player/requests.py @@ -0,0 +1,60 @@ +import abc +from dataclasses import dataclass +from backends.gamespy.library.abstractions.request_base import RequestBase as RB +from servers.presence_connection_manager.enums.general import LoginType, SdkRevisionType + + +class RequestBase(RB, abc.ABC): + operation_id: int = None + + +# we just need to recreate the requests and just put the property inside it. The result we can use the results inside servers. +class CheckRequest(RequestBase): + nick: str + password: str + email: str + partner_id: int + + +class NewUserRequest(RequestBase): + product_id: int + game_port: int + cd_key: str + has_game_name: bool + has_product_id: bool + has_cdkey: bool + has_partner_id: bool + has_game_port: bool + nick: str + email: str + password: str + partner_id: int + game_name: str + uniquenick: str + + +class NicksRequest(RequestBase): + password: str + email: str + is_require_uniquenicks: bool + + +class LoginRequest(RequestBase): + user_challenge: str + response: str + unique_nick: str + user_data: str + namespace_id: int + auth_token: str + nick: str + email: str + product_id: int + type: LoginType + sdk_revision_type: SdkRevisionType + game_port: int + user_id: int + profile_id: int + partner_id: int + game_name: int + quiet_mode_flags: int + firewall: bool diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py new file mode 100644 index 000000000..24dc6df25 --- /dev/null +++ b/src/backends/routers/gamespy/chat.py @@ -0,0 +1,41 @@ +from flask import session +from flask_socketio import SocketIO, emit + +import socketio +from backends.urls import CHAT + + +# @socketio.on(f"/{CHAT}/join") +# def join(request: "JoinRequest"): +# raise NotImplementedError() + + +# @socketio.on(f"/{CHAT}/setckey") +# def setckey(request: "SetCKeyRequest"): +# sender = session.get("nickname") +# raise NotImplementedError() + + +# @socketio.on(f"/{CHAT}/setchankey") +# def set_channel_key(request: "SetChannelKeyRequest"): +# raise NotImplementedError() + + +# @socketio.on(f"/{CHAT}/atm") +# def atm(request: "ATMRequest"): +# raise NotImplementedError() + + +# @socketio.on(f"/{CHAT}/utm") +# def utm(request: "UTMRequest"): +# raise NotImplementedError() + + +# @socketio.on(f"/{CHAT}/notice") +# def notice(request: "UTMRequest"): +# raise NotImplementedError() + + +# @socketio.on(f"/{CHAT}/private") +# def notice(request: "PrivateRequest"): +# raise NotImplementedError() diff --git a/src/backends/routers/gamespy/gstats.py b/src/backends/routers/gamespy/gstats.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/routers/gamespy/natneg.py b/src/backends/routers/gamespy/natneg.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/routers/gamespy/presence_connection_manager.py b/src/backends/routers/gamespy/presence_connection_manager.py new file mode 100644 index 000000000..37e2570bf --- /dev/null +++ b/src/backends/routers/gamespy/presence_connection_manager.py @@ -0,0 +1,17 @@ +from flask import Flask, request +from backends.gamespy.servers.presence_search_player.requests import ( + LoginRequest, +) +from backends.urls import * + +app = Flask(__name__) + + +@app.route(f"/{PRESENCE_CONNECTION_MANAGER}/login", methods=["POST"]) +def pcm_login(): + req = LoginRequest(request.json) + pass + + + + diff --git a/src/backends/routers/gamespy/presence_search_player.py b/src/backends/routers/gamespy/presence_search_player.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/routers/gamespy/server_browser.py b/src/backends/routers/gamespy/server_browser.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/routers/gamespy/webservices.py b/src/backends/routers/gamespy/webservices.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/urls.py b/src/backends/urls.py new file mode 100644 index 000000000..f7eac3c60 --- /dev/null +++ b/src/backends/urls.py @@ -0,0 +1,9 @@ +PRESENCE_CONNECTION_MANAGER = "PCM" +PRESENCE_SEARCH_PLAYER = "PSP" +SERVER_BROWSER_V1 = "SB_V1" +SERVER_BROWSER_V2 = "SB_V2" +QUERY_REPORT = "QR" +NATNEG = "NN" +GAMESTATUS = "GS" +CHAT = "Chat" +SOAP_SERVICE = "SOAP" diff --git a/src/docker-compose.yaml b/src/docker-compose.yaml new file mode 100644 index 000000000..09cc38a09 --- /dev/null +++ b/src/docker-compose.yaml @@ -0,0 +1,28 @@ +version: '3' + +services: + redis: + image: redis + restart: always + ports: + - "6379:6379" + command: redis-server --requirepass 123456 + + mongodb: + image: mongo + restart: always + ports: + - "27017:27017" + environment: + MONGO_INITDB_ROOT_USERNAME: unispy + MONGO_INITDB_ROOT_PASSWORD: 123456 + + postgresql: + image: postgres + ports: + - "5432:5432" + restart: always + environment: + POSTGRES_USER: unispy + POSTGRES_PASSWORD: 123456 + POSTGRES_DB: unispy diff --git a/src/library/__init__.py b/src/library/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/library/abstractions/__init__.py b/src/library/abstractions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/library/abstractions/brocker.py b/src/library/abstractions/brocker.py new file mode 100644 index 000000000..96268e251 --- /dev/null +++ b/src/library/abstractions/brocker.py @@ -0,0 +1,38 @@ +import abc + + +class BrockerBase: + _subscriber: object + is_started: bool = False + _name: str + call_backs: list + """ + brocker subscribe name + """ + + def __init__(self, name: str) -> None: + assert isinstance(name, str) + self._name = name + + @abc.abstractmethod + def _subscribe(self): + """ + define the brocker event binding + """ + pass + + @abc.abstractmethod + def receive_message(self, message): + pass + + @abc.abstractmethod + def publish_message(self, message): + + pass + + @abc.abstractmethod + def unsubscribe(self): + pass + + def __del__(self): + self.unsubscribe() diff --git a/src/library/abstractions/client.py b/src/library/abstractions/client.py new file mode 100644 index 000000000..8604ec4a2 --- /dev/null +++ b/src/library/abstractions/client.py @@ -0,0 +1,119 @@ +import abc +from library.encryption.encoding import Encoding +from library.log.log_manager import LogWriter +from library.log.log_manager import LogWriter +from library.unispy_server_config import ServerConfig + +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from library.abstractions.connections import ConnectionBase + from library.abstractions.switcher import SwitcherBase + from library.abstractions.enctypt_base import EncryptBase + from library.abstractions.contracts import ResponseBase + from library.abstractions.client import ClientInfoBase + + +class ClientBase(abc.ABC): + server_config: ServerConfig + connection: object + logger: LogWriter + crypto: EncryptBase = None + info: ClientInfoBase + is_log_raw: bool = False + + def __init__( + self, connection: ConnectionBase, server_config: ServerConfig, logger: LogWriter + ): + assert isinstance(server_config, ServerConfig) + # assert isinstance(logger, LogWriter) + self.server_config = server_config + from library.abstractions.connections import ConnectionBase + + self.connection: ConnectionBase = connection + self.logger = logger + + def on_connected(self) -> None: + pass + + def on_disconnected(self) -> None: + self.__del__() + + @abc.abstractedmethod + def create_switcher(self, buffer) -> SwitcherBase: + pass + + def on_received(self, buffer) -> None: + switcher: SwitcherBase = self.create_switcher(buffer) + switcher.handle() + + def decrypt_message(self, buffer) -> bytes: + if self.crypto is not None: + return self.crypto.decrypt(buffer) + else: + return buffer + + def send(self, response: ResponseBase) -> None: + response.build() + sending_buffer = response.sending_buffer + if isinstance(sending_buffer, str): + buffer = Encoding.get_bytes(sending_buffer) + else: + buffer = sending_buffer + + self.log_network_sending(buffer) + + if self.crypto is not None: + buffer = self.crypto.encrypt(buffer) + + self.connection.send(buffer) + + def test_received(self, buffer) -> None: + if self.crypto is not None: + self.crypto = None + + self.on_received(buffer) + + def log_verbose(self, message: str) -> None: + pass + + def log_info(self, message: str) -> None: + pass + + def log_warn(self, message: str) -> None: + pass + + def log_error(self, message: str) -> None: + pass + + def log_network_sending(self, data: object) -> None: + pass + + def log_network_receving(self, data: object) -> None: + pass + + def log_current_class(self, object) -> None: + pass + + +class EasyTimer: + def __init__(self, interval, refresh_interval) -> None: + self.interval = interval + self.refresh_interval = refresh_interval + self.is_expired = False + + def elapsed(self, s, e) -> None: + pass + + def start(self) -> None: + pass + + def refresh_last_active_time(self) -> None: + pass + + def dispose(self) -> None: + pass + + +class ClientInfoBase: + pass diff --git a/src/library/abstractions/connections.py b/src/library/abstractions/connections.py new file mode 100644 index 000000000..e055f891e --- /dev/null +++ b/src/library/abstractions/connections.py @@ -0,0 +1,91 @@ +import abc +import socketserver +from library.abstractions.client import ClientBase +from library.extentions.string_extentions import IPEndPoint +from library.log.log_manager import LogWriter +from library.unispy_server_config import ServerConfig + + +class ConnectionBase(abc.ABC): + remote_ip: str + remote_port: int + _is_started: bool = False + config: ServerConfig + t_client: type[ClientBase] + logger: LogWriter + handler: socketserver.BaseRequestHandler + _client: ClientBase + + def __init__( + self, + handler: socketserver.BaseRequestHandler, + config: ServerConfig, + t_client: ClientBase, + logger: LogWriter, + ) -> None: + super().__init__() + assert isinstance(config, ServerConfig) + assert issubclass(t_client, ClientBase) + # assert isinstance(logger, LogWriter) + assert issubclass(type(handler), socketserver.BaseRequestHandler) + self.remote_ip = handler.client_address[0] + self.remote_port = int(handler.client_address[1]) + self.config = config + self.t_client = t_client + self.logger = logger + self._client = self.t_client(self, self.config, self.logger) + + def on_received(self, data: bytes) -> None: + self._client.on_received(data) + + @abc.abstractmethod + def send(self, data: bytes) -> None: + if not self._is_started: + raise Exception("Server is not running.") + assert isinstance(data, bytes) + + +class UcpConnectionBase(ConnectionBase, abc.ABC): + pass + + +class TcpConnectionBase(ConnectionBase, abc.ABC): + @abc.abstractmethod + def on_connected(self): + pass + + @abc.abstractmethod + def on_disconnected(self): + pass + + @abc.abstractmethod + def disconnect(self): + pass + + +class HttpConnectionBase(TcpConnectionBase, abc.ABC): + pass + + +class ServerBase(abc.ABC): + _config: ServerConfig + _t_client: type[ClientBase] + _logger: LogWriter + _server: socketserver.BaseServer + + def __init__( + self, config: ServerConfig, t_client: type[ClientBase], logger: LogWriter + ) -> None: + assert isinstance(config, ServerConfig) + assert issubclass(t_client, ClientBase) + # assert isinstance(logger, LogWriter) + self._config = config + self._t_client = t_client + self._logger = logger + + @abc.abstractclassmethod + def start(self): + pass + + def __del__(self): + self._server.shutdown() diff --git a/src/library/abstractions/contracts.py b/src/library/abstractions/contracts.py new file mode 100644 index 000000000..2e9ce8c1b --- /dev/null +++ b/src/library/abstractions/contracts.py @@ -0,0 +1,68 @@ +import abc +from copy import deepcopy +from dataclasses import dataclass +import enum + + +class RequestBase(abc.ABC): + command_name: object = None + raw_request: object = None + + def __init__(self, raw_request: object) -> None: + """ + raw_request is for gamespy protocol\n + json_dict is for restapi deserialization + """ + super().__init__() + if raw_request is None: + raise Exception("raw_request should not be None") + + if raw_request is not None: + if (not isinstance(raw_request, bytes)) and ( + not isinstance(raw_request, str) + ): + raise Exception("Unsupported raw_request type") + self.raw_request = raw_request + return + + def parse(self) -> None: + pass + + def to_serializable_dict(self) -> dict: + """ + create a json serializable dict of this class + """ + result = deepcopy(self.__dict__) + for key, value in result.items(): + if isinstance(value, bytes): + result[key] = list(value) + elif isinstance(value, enum.Enum): + result[key] = value.value + elif isinstance(value, enum.IntEnum): + result[key] = value.value + return result + + +@dataclass +class ResultBase(abc.ABC): + pass + + +class ResponseBase(abc.ABC): + sending_buffer: object + _result: ResultBase + _request: RequestBase + + def __init__(self, request: RequestBase, result: ResultBase) -> None: + super().__init__() + if request is not None: + assert issubclass(type(request), RequestBase) + self._request = request + + if result is not None: + assert issubclass(type(result), ResultBase) + self._result = result + + @abc.abstractmethod + def build(self) -> None: + pass diff --git a/src/library/abstractions/enctypt_base.py b/src/library/abstractions/enctypt_base.py new file mode 100644 index 000000000..eb1f53b1e --- /dev/null +++ b/src/library/abstractions/enctypt_base.py @@ -0,0 +1,11 @@ +import abc + + +class EncryptBase(abc.ABC): + @abc.abstractmethod + def encrypt(self, data: bytes) -> bytes: + assert isinstance(data, bytes) + + @abc.abstractmethod + def decrypt(self, data: bytes) -> bytes: + assert isinstance(data, bytes) diff --git a/src/library/abstractions/handler.py b/src/library/abstractions/handler.py new file mode 100644 index 000000000..39a097775 --- /dev/null +++ b/src/library/abstractions/handler.py @@ -0,0 +1,76 @@ +import abc +from library.abstractions.client import ClientBase +from library.exceptions.error import UniSpyException +from typing import TYPE_CHECKING, List +from typing import Type + + +if TYPE_CHECKING: + from library.abstractions.contracts import RequestBase, ResultBase, ResponseBase + + +class CmdHandlerBase(abc.ABC): + + _client: ClientBase + _request: RequestBase + _result: ResultBase = None + _response: ResponseBase = None + _backend_url: str + _result_type: Type + """ + store the backend url + """ + + def __init__(self, client: ClientBase, request: RequestBase) -> None: + assert issubclass(type(client), ClientBase) + assert issubclass(type(request), RequestBase) + self._client = client + self._request = request + + def handle(self) -> None: + try: + # we first log this class + self.__log_current_class() + # then we handle it + self._request_check() + self._data_operate() + self._response_construct() + if self._response is None: + return + except Exception as ex: + self._handle_exception(ex) + + @abc.abstractmethod + def _request_check(self) -> None: + # if there is gamespy raw request we convert it to unispy request + if self._request.raw_request is not None: + self._request._parse_raw() + + @abc.abstractmethod + def _data_operate(self) -> None: + "default use restapi to access to our backend service" + if self._result is not None: + result_type = type(self._result) + # get the http response and create it with this type + pass + + @abc.abstractmethod + def _response_construct(self) -> None: + """construct response here in specific child class""" + pass + + @abc.abstractmethod + def _response_send(self) -> None: + """ + Send response back to client, this is a virtual function which can be override only by child class + """ + self._client.send(self._response) + + def _handle_exception(self, ex) -> None: + UniSpyException.handle_exception(ex, self._client) + + def _log_current_class(self) -> None: + if self._client is None: + self._client.log_current_class(self) + else: + self._client.log_current_class(self) diff --git a/src/library/abstractions/redis_channel.py b/src/library/abstractions/redis_channel.py new file mode 100644 index 000000000..505c059fb --- /dev/null +++ b/src/library/abstractions/redis_channel.py @@ -0,0 +1,2 @@ +class RedisChannelBase: + pass diff --git a/src/library/abstractions/server_launcher_base.py b/src/library/abstractions/server_launcher_base.py new file mode 100644 index 000000000..2f2464e3e --- /dev/null +++ b/src/library/abstractions/server_launcher_base.py @@ -0,0 +1,53 @@ +import abc +from library.log.log_manager import LogManager, LogWriter +from library.unispy_server_config import CONFIG, ServerConfig +import pyfiglet +import requests + +__server_name_mapping = { + "PresenceConnectionManager": "PCM", + "PresenceSearchPlayer": "PSP", + "CDKey": "CDKey", + "ServerBrwoserV1": "SBv1", + "ServerBrowserV2": "SBv2", + "QueryReport": "QR", + "NatNegotiation": "NN", + "GameStatus": "GS", + "Chat": "Chat", + "WebServices": "Web", + "GameTrafficReplay": "GTR", +} + + +class ServerLauncherBase(abc.ABC): + config: ServerConfig + logger: LogWriter + + def start(self): + self.__show_unispy_logo() + self._connect_to_backend() + self._create_logger() + self._launch_server() + + def __show_unispy_logo(self): + print(pyfiglet.Figlet().renderText("UniSpy.Server")) + + @abc.abstractmethod + def _launch_server(self) -> None: + pass + + def _connect_to_backend(self): + try: + resp: requests.Response = requests.get(url=CONFIG.backend.url) + if resp.status_code == 200: + data = resp.json() + if data["status"] != "online": + raise Exception( + f"backend server: {CONFIG.backend.url} not available." + ) + except: + raise Exception(f"backend server: {CONFIG.backend.url} not available.") + + def _create_logger(self): + short_name = __server_name_mapping[self.config.server_name] + self.logger = LogManager.create(CONFIG.logging.path, short_name) diff --git a/src/library/abstractions/switcher.py b/src/library/abstractions/switcher.py new file mode 100644 index 000000000..3ddf45b97 --- /dev/null +++ b/src/library/abstractions/switcher.py @@ -0,0 +1,58 @@ +import abc +from library.abstractions.client import ClientBase +from library.abstractions.handler import CmdHandlerBase +from typing import TYPE_CHECKING, List + + +class SwitcherBase(abc.ABC): + + _client: ClientBase + _raw_request: object + _handlers: List[CmdHandlerBase] + _requests: List[tuple] + """ + [ + (request_name,raw_request), + (request_name,raw_request), + (request_name,raw_request), + ... + ] + + """ + + def __init__(self, client: ClientBase, raw_request: object) -> None: + + assert isinstance(client, ClientBase) + self._client = client + self._raw_request = raw_request + self._handlers = [] + self._requests = [] + + def handle(self): + from library.exceptions.error import UniSpyException + + try: + self._process_raw_request() + if len(self._requests) == 0: + return + for request in self._requests: + handler = self._create_cmd_handlers(request[0], request[1]) + if handler is None: + self._client.log_warn("Request ignored.") + continue + self._handlers.append(handler) + if len(self._handlers) == 0: + return + + for handler in self._handlers: + handler.handle() + except Exception as e: + UniSpyException.handle_exception(e) + + @abc.abstractmethod + def _process_raw_request(self) -> None: + pass + + @abc.abstractmethod + def _create_cmd_handlers(self, name: object, rawRequest: object) -> CmdHandlerBase: + pass diff --git a/src/library/database/__init__.py b/src/library/database/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/library/database/mongodb_orm.py b/src/library/database/mongodb_orm.py new file mode 100644 index 000000000..ba9b19cd9 --- /dev/null +++ b/src/library/database/mongodb_orm.py @@ -0,0 +1,12 @@ +from mongoengine import connect + +from library.unispy_server_config import CONFIG + + +def connect_to_db(): + connect(host=CONFIG.mongodb.url) + + +def get_ttl_param(seconds: int): + assert isinstance(seconds, int) + return {"indexes": [{"fields": ["created"], "expireAfterSeconds": seconds}]} diff --git a/src/library/database/pg_orm.py b/src/library/database/pg_orm.py new file mode 100644 index 000000000..3bd3ffcc9 --- /dev/null +++ b/src/library/database/pg_orm.py @@ -0,0 +1,204 @@ +from datetime import datetime +from sqlalchemy import ( + Boolean, + Double, + SmallInteger, + Text, + create_engine, + Column, + Integer, + String, + ForeignKey, + DateTime, + text, +) +from sqlalchemy.dialects.postgresql import JSONB, INET +from sqlalchemy.ext.declarative import DeclarativeMeta +from sqlalchemy.orm import sessionmaker, declarative_base + +Base: DeclarativeMeta = declarative_base() +from sqlalchemy.orm.session import Session + + +class Users(Base): + __tablename__ = "users" + + userid = Column(Integer, primary_key=True, autoincrement=True) + email = Column(String, nullable=False) + password = Column(String, nullable=False) + emailverified = Column(Boolean, default=True, nullable=False) + lastip = Column(INET) + lastonline = Column(DateTime, default=datetime.now()) + createddate = Column(DateTime, default=datetime.now(), nullable=False) + banned = Column(Boolean, default=False, nullable=False) + deleted = Column(Boolean, default=False, nullable=False) + + +class Profiles(Base): + __tablename__ = "profiles" + + profileid = Column(Integer, primary_key=True, autoincrement=True) + userid = Column(Integer, ForeignKey("users.userid"), nullable=False) + nick = Column(String, nullable=False) + serverflag = Column(Integer, nullable=False, default=0) + status = Column(SmallInteger, default=0) + statstring = Column(String, default="I love UniSpy") + location = Column(String) + firstname = Column(String) + lastname = Column(String) + publicmask = Column(Integer, default=0) + latitude = Column(Double, default=0) + longitude = Column(Double, default=0) + aim = Column(String, default="") + picture = Column(Integer, default=0) + occupationid = Column(Integer, default=0) + incomeid = Column(Integer, default=0) + industryid = Column(Integer, default=0) + marriedid = Column(Integer, default=0) + childcount = Column(Integer, default=0) + interests1 = Column(Integer, default=0) + ownership1 = Column(Integer, default=0) + connectiontype = Column(Integer, default=0) + sex = Column(SmallInteger, default=0) + zipcode = Column(String, default="00000") + countrycode = Column(String, default="1") + homepage = Column(String, default="unispy.org") + birthday = Column(Integer, default=0) + birthmonth = Column(Integer, default=0) + birthyear = Column(Integer, default=0) + icquin = Column(Integer, default=0) + quietflags = Column(SmallInteger, nullable=False, default=0) + streetaddr = Column(Text) + streeaddr = Column(Text) + city = Column(Text) + cpubrandid = Column(Integer, default=0) + cpuspeed = Column(Integer, default=0) + memory = Column(SmallInteger, default=0) + videocard1string = Column(Text) + videocard1ram = Column(SmallInteger, default=0) + videocard2string = Column(Text) + videocard2ram = Column(SmallInteger, default=0) + subscription = Column(Integer, default=0) + adminrights = Column(Integer, default=0) + + +class SubProfiles(Base): + __tablename__ = "subprofiles" + + subprofileid = Column( + Integer, ForeignKey("profiles.profileid"), primary_key=True, autoincrement=True + ) + profileid = Column(Integer, nullable=False) + uniquenick = Column(String) + namespaceid = Column(Integer, nullable=False, default=0) + partnerid = Column(Integer, nullable=False, default=0) + productid = Column(Integer) + gamename = Column(Text) + cdkeyenc = Column(String) + firewall = Column(SmallInteger, default=0) + port = Column(Integer, default=0) + authtoken = Column(String) + + +class AddRequest(Base): + __tablename__ = "addrequests" + + addrequestid = Column(Integer, primary_key=True, autoincrement=True) + profileid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) + namespaceid = Column(Integer, nullable=False) + targetid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) + reason = Column(String, nullable=False) + syncrequested = Column(String, nullable=False) + + +class Blocked(Base): + __tablename__ = "blocked" + + blockid = Column(Integer, primary_key=True, autoincrement=True) + profileid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) + namespaceid = Column(Integer, nullable=False) + targetid = Column(Integer, nullable=False) + + +class Friends(Base): + __tablename__ = "friends" + + friendid = Column(Integer, primary_key=True, autoincrement=True) + profileid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) + namespaceid = Column(Integer, nullable=False) + targetid = Column(Integer, nullable=False) + + +class Games(Base): + __tablename__ = "games" + + gameid = Column(Integer, primary_key=True) + gamename = Column(String, nullable=False) + secretkey = Column(String) + description = Column(String(4095), nullable=False) + disabled = Column(Boolean, nullable=False) + + +class GroupList(Base): + __tablename__ = "grouplist" + + groupid = Column(Integer, primary_key=True) + gameid = Column(Integer, ForeignKey("games.gameid"), nullable=False) + roomname = Column(Text, nullable=False) + + +class Messages(Base): + __tablename__ = "messages" + + messageid = Column(Integer, primary_key=True, autoincrement=True) + namespaceid = Column(Integer) + type = Column(Integer) + from_user = Column(Integer, nullable=False) + to_user = Column(Integer, nullable=False) + date = Column(DateTime, nullable=False, default=datetime.now()) + message = Column(Text, nullable=False) + + +class Partner(Base): + __tablename__ = "partner" + + partnerid = Column(Integer, primary_key=True) + partnername = Column(String, nullable=False) + + +class PStorage(Base): + __tablename__ = "pstorage" + + pstorageid = Column(Integer, primary_key=True, autoincrement=True) + profileid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) + ptype = Column(Integer, nullable=False) + dindex = Column(Integer, nullable=False) + data = Column(JSONB) + + +class SakeStorage(Base): + __tablename__ = "sakestorage" + + sakestorageid = Column(Integer, primary_key=True, autoincrement=True) + tableid = Column(String, nullable=False) + + +from sqlalchemy import create_engine +from library.unispy_server_config import CONFIG + + +def connect_to_db() -> Session: + ENGINE = create_engine(CONFIG.postgresql.url) + session = sessionmaker(bind=ENGINE)() + Base.metadata.create_all(ENGINE) + with ENGINE.connect() as conn: + conn.execute(text("SELECT 1")) + return session + + +PG_SESSION = connect_to_db() + +if __name__ == "__main__": + session = connect_to_db() + # session.query().filter + pass diff --git a/src/library/database/redis.py b/src/library/database/redis.py new file mode 100644 index 000000000..345660fb3 --- /dev/null +++ b/src/library/database/redis.py @@ -0,0 +1,16 @@ +import asyncio +# import redis +import aioredis + +from library.unispy_server_config import CONFIG + + +# SESSION = redis.Redis.from_url(CONFIG.redis.url) + +pool = aioredis.from_url(CONFIG.redis.url) +loop=asyncio.get_event_loop() +loop.run_until_complete(pool.set("hello","hi")) +data = loop.run_until_complete(pool.get("hello")) +pass + + diff --git a/src/library/encryption/__init__.py b/src/library/encryption/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/library/encryption/encoding.py b/src/library/encryption/encoding.py new file mode 100644 index 000000000..7f1df25c7 --- /dev/null +++ b/src/library/encryption/encoding.py @@ -0,0 +1,12 @@ +from typing import List + + +class Encoding: + @staticmethod + def get_string(data: bytes) -> str: + assert isinstance(data, bytes) + return data.decode("ascii") + + def get_bytes(data: str) -> List[int]: + assert isinstance(data, str) + return list(data.encode()) diff --git a/src/library/encryption/gs_encryption.py b/src/library/encryption/gs_encryption.py new file mode 100644 index 000000000..f92f66675 --- /dev/null +++ b/src/library/encryption/gs_encryption.py @@ -0,0 +1,99 @@ +from library.abstractions.enctypt_base import EncryptBase +import hashlib + +DIGITS_HEX = "0123456789abcdef" +DIGITS_CRYPT = "aFl4uOD9sfWq1vGp" +NEW_DIGITS_CRYPT = "qJ1h4N9cP3lzD0Ka" +IP_XOR_MASK = 0xC3801DC7 +CLIENT_KEY = "0000000000000000" +SERVER_KEY = "0000000000000000" + + +class PeerChatCtx: + def __init__(self): + self.buffer1 = 0 + self.buffer2 = 0 + self.sbox = [0] * 256 + + +def init(ctx: PeerChatCtx, challenge_key: str, secret_key: str): + assert isinstance(ctx, PeerChatCtx) + assert isinstance(challenge_key, str) + assert isinstance(secret_key, str) + + challenge_bytes = list(challenge_key.encode("ascii")) + secret_key_bytes = list(secret_key.encode("ascii")) + + ctx.buffer1 = 0 + ctx.buffer2 = 0 + + secret_key_index = 0 + for i in range(len(challenge_bytes)): + if secret_key_index >= len(secret_key_bytes): + secret_key_index = 0 + + challenge_bytes[i] ^= secret_key_bytes[secret_key_index] + secret_key_index += 1 + + index1 = 255 + for i in range(256): + ctx.sbox[i] = index1 + index1 -= 1 + + index1 = 0 + index2 = 0 + for i in range(len(ctx.sbox)): + if index1 >= len(challenge_bytes): + index1 = 0 + + index2 = (challenge_bytes[index1] + ctx.sbox[i] + index2) % 256 + t = ctx.sbox[i] + ctx.sbox[i] = ctx.sbox[index2] + ctx.sbox[index2] = t + index1 += 1 + + +def handle(ctx: PeerChatCtx, data): + num1 = ctx.buffer1 + num2 = ctx.buffer2 + buffer = [] + size = len(data) + datapos = 0 + + while size > 0: + num1 = (num1 + 1) % 256 + num2 = (ctx.sbox[num1] + num2) % 256 + t = ctx.sbox[num1] + ctx.sbox[num1] = ctx.sbox[num2] + ctx.sbox[num2] = t + t = (ctx.sbox[num2] + ctx.sbox[num1]) % 256 + temp = data[datapos] ^ ctx.sbox[t] + buffer.append(temp) + datapos += 1 + size -= 1 + + ctx.buffer1 = num1 + ctx.buffer2 = num2 + return bytes(buffer) + + +class ChatCrypt(EncryptBase): + def __init__(self, game_secret_key): + self.client_ctx = PeerChatCtx() + self.server_ctx = PeerChatCtx() + init(self.client_ctx, CLIENT_KEY, game_secret_key) + init(self.server_ctx, SERVER_KEY, game_secret_key) + + def encrypt(self, data: bytes) -> bytes: + super().encrypt(data) + return self.handle(self.server_ctx, data) + + def decrypt(self, data: bytes) -> bytes: + super().decrypt(data) + return self.handle(self.client_ctx, data) + + +if __name__ == "__main__": + enc = ChatCrypt("123456") + cipher = enc.encrypt("hello".encode()) + pass diff --git a/src/library/encryption/xor_encryption.py b/src/library/encryption/xor_encryption.py new file mode 100644 index 000000000..386b95ac7 --- /dev/null +++ b/src/library/encryption/xor_encryption.py @@ -0,0 +1,57 @@ +from enum import IntEnum +import base64 + +from library.abstractions.enctypt_base import EncryptBase + + +class XorType(IntEnum): + TYPE_0 = 0 + TYPE_1 = 1 + TYPE_2 = 2 + TYPE_3 = 3 + + +class XorEncoding(EncryptBase): + def __init__(self, xor_type): + self.encryption_type = xor_type + + @staticmethod + def encode(plaintext: bytes, enc_type: XorType): + assert isinstance(plaintext, bytes) + assert isinstance(enc_type, XorType) + seed_0 = b"gamespy" + seed_1 = b"GameSpy3D" + seed_2 = b"Industries" + seed_3 = b"ProjectAphex" + + length = len(plaintext) + index = 0 + temp = seed_0 + + if enc_type == XorType.TYPE_0: + temp = seed_0 + elif enc_type == XorType.TYPE_1: + temp = seed_1 + elif enc_type == XorType.TYPE_2: + temp = seed_2 + elif enc_type == XorType.TYPE_3: + temp = seed_3 + + temp_length = len(temp) + + for i in range(length): + if i >= temp_length: + i = 0 + + plaintext[index] ^= temp[i] + index += 1 + + return plaintext + + def encrypt(self, data: bytes): + super().encrypt(data) + return XorEncoding.encode(data, self.encryption_type) + + def decrypt(self, data: bytes): + super().decrypt(data) + return XorEncoding.encode(data, self.encryption_type) diff --git a/src/library/exceptions/__init__.py b/src/library/exceptions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/library/exceptions/error.py b/src/library/exceptions/error.py new file mode 100644 index 000000000..fc1907891 --- /dev/null +++ b/src/library/exceptions/error.py @@ -0,0 +1,46 @@ +# from library.abstractions.client_base import ClientBase + + +class UniSpyException(Exception): + message: str + """the error message""" + + def __init__(self, message: str) -> None: + self.message = message + + @staticmethod + # def handle_exception(e: Exception, client: ClientBase = None): + def handle_exception(e: Exception, client = None): + if issubclass(e, UniSpyException): + if client is None: + # LogWriter.LogError(ex.Message); + pass + else: + # client.LogError(ex.Message); + pass + else: + if client is None: + # LogWriter.LogError(ex.ToString()); + pass + else: + # client.LogError(ex.ToString()); + pass + + def __repr__(self) -> str: + # return super().__repr__() + return f'Error message: "{self.message}"' + + +class DatabaseConnectionException(UniSpyException): + def __init__(self, message: str = "Can not connect to database.") -> None: + super().__init__(message) + + +class RedisConnectionException(UniSpyException): + def __init__(self, message: str = "Can not connect to redis") -> None: + super().__init__(message) + + +if __name__ == "__main__": + err = UniSpyException("test") + pass diff --git a/src/library/extentions/__init__.py b/src/library/extentions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/library/extentions/bytes_extentions.py b/src/library/extentions/bytes_extentions.py new file mode 100644 index 000000000..7adda794a --- /dev/null +++ b/src/library/extentions/bytes_extentions.py @@ -0,0 +1,7 @@ +def bytes_to_int(input: bytes) -> int: + assert isinstance(input, bytes) + return int.from_bytes(input, "little") + +def int_to_bytes(input: int) -> bytes: + assert isinstance(input, int) + return input.to_bytes(4, "little", signed=False) diff --git a/src/library/extentions/encoding.py b/src/library/extentions/encoding.py new file mode 100644 index 000000000..2d4df5d10 --- /dev/null +++ b/src/library/extentions/encoding.py @@ -0,0 +1,9 @@ +def get_string(data: bytes) -> str: + return data.decode("ascii") + + +def get_bytes(data: str) -> bytes: + return data.encode("ascii") + + + diff --git a/src/library/extentions/gamespy_ramdoms.py b/src/library/extentions/gamespy_ramdoms.py new file mode 100644 index 000000000..dfffeee51 --- /dev/null +++ b/src/library/extentions/gamespy_ramdoms.py @@ -0,0 +1,137 @@ +from enum import IntEnum +import random +import datetime + + +class StringType(IntEnum): + ALPHANUMERIC = 0 + ALPHA = 1 + HEX = 2 + + +def generate_random_string(count: int, type: StringType) -> str: + random.seed(datetime.datetime.now()) + + alpha_chars = [ + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + ] + + alpha_num_chars = [ + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "a", + "b", + "c", + "d", + "e", + "f", + "g", + "h", + "i", + "j", + "k", + "l", + "m", + "n", + "o", + "p", + "q", + "r", + "s", + "t", + "u", + "v", + "w", + "x", + "y", + "z", + "A", + "B", + "C", + "D", + "E", + "F", + "G", + "H", + "I", + "J", + "K", + "L", + "M", + "N", + "O", + "P", + "Q", + "R", + "S", + "T", + "U", + "V", + "W", + "X", + "Y", + "Z", + ] + + hex_chars = [ + "0", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "a", + "b", + "c", + "d", + "e", + "f", + ] + + builder = [] + for _ in range(count): + if type == StringType.ALPHANUMERIC: + builder.append(random.choice(alpha_num_chars)) + elif type == StringType.HEX: + builder.append(random.choice(hex_chars)) + else: + builder.append(random.choice(alpha_chars)) + + return "".join(builder) diff --git a/src/library/extentions/gamespy_utils.py b/src/library/extentions/gamespy_utils.py new file mode 100644 index 000000000..63f1b7e2d --- /dev/null +++ b/src/library/extentions/gamespy_utils.py @@ -0,0 +1,76 @@ +from email_validator import validate_email, EmailNotValidError + + +def is_email_format_correct(email: str) -> bool: + assert isinstance(email, str) + try: + validate_email(email, check_deliverability=False) + + except EmailNotValidError as e: + return False + + return True + + +def convert_to_key_value(request: str) -> dict: + assert isinstance(request, str) + command_parts = request.replace("\\final\\", "").lstrip("\\").split("\\") + + parts = [part for part in command_parts if part != "final"] + dict = {} + try: + for i in range(0, len(parts), 2): + if parts[i] not in dict: + dict[parts[i].lower()] = parts[i + 1] + # Some game send uppercase key to us, so we have to deal with it + except IndexError: + pass + return dict + + +def is_valid_date(day: int, month: int, year: int) -> bool: + # Check for a blank. + if (day, month, year) == (0, 0, 0): + return False + + # Validate the day of the month. + match month: + case 0: + # Can't specify a day without a month. + if day != 0: + return False + case 1, 3, 5, 7, 8, 10, 12: + # 31-day month. + if day > 31: + return False + case 4, 6, 9, 11: + # 30-day month. + if day > 30: + return False + case 2: + # 28/29-day month. + # Leap year? + if ((year % 4 == 0) and (year % 100 != 0)) or (year % 400 == 0): + if day > 29: + return False + else: + if day > 28: + return False + case _: + # Invalid month. + return False + + # Check that the date is in the valid range. + if year < 1900 or year > 2079: + return False + elif year == 2079: + match month: + case 6 if day > 6: + return False + case _: + if month > 6: + return False + + return True + + diff --git a/src/library/extentions/password_encoder.py b/src/library/extentions/password_encoder.py new file mode 100644 index 000000000..f34f5634a --- /dev/null +++ b/src/library/extentions/password_encoder.py @@ -0,0 +1,76 @@ +import hashlib +import base64 + +from library.exceptions.error import UniSpyException + + +def process_password(request: dict): + """process password in standard format and return the password""" + assert isinstance(request, dict) + if "passwordenc" in request: + md5_password = get_md5_hash(decode(request["passwordenc"])) + elif "passenc" in request: + md5_password = get_md5_hash(decode(request["passenc"])) + elif "pass" in request: + md5_password = get_md5_hash(request["pass"]) + elif "password" in request: + md5_password = get_md5_hash(request["password"]) + else: + raise UniSpyException("Can not find password field in request") + return md5_password + + +def encode(password: str): + assert isinstance(password, str) + password_bytes = password.encode("utf-8") + pass_encoded = base64.b64encode(game_spy_encode_method(password_bytes)) + pass_encoded = pass_encoded.decode("utf-8") + pass_encoded = pass_encoded.replace("=", "_").replace("+", "[").replace("/", "]") + return pass_encoded + + +def decode(password: str): + assert isinstance(password, str) + password = password.replace("_", "=").replace("[", "+").replace("]", "/") + password_bytes = base64.b64decode(password) + return game_spy_encode_method(password_bytes).decode("utf-8") + + +def game_spy_encode_method(password_bytes: bytes): + assert isinstance(password_bytes, bytes) + a = 0 + num = 0x79707367 # gamespy + for i in range(len(password_bytes)): + num = game_spy_byte_shift(num) + a = num % 0xFF + password_bytes[i] ^= a + return password_bytes + + +def game_spy_byte_shift(num): + assert isinstance(num, int) + c = (num >> 16) & 0xFFFF + a = num & 0xFFFF + + c *= 0x41A7 + a *= 0x41A7 + a += (c & 0x7FFF) << 16 + + if a < 0: + a &= 0x7FFFFFFF + a += 1 + + a += c >> 15 + + if a < 0: + a &= 0x7FFFFFFF + a += 1 + + return a + + +def get_md5_hash(data): + isinstance(data, str) + md5_hash = hashlib.md5() + md5_hash.update(data.encode("utf-8")) + return md5_hash.hexdigest() diff --git a/src/library/extentions/redis_orm.py b/src/library/extentions/redis_orm.py new file mode 100644 index 000000000..79d4a0a03 --- /dev/null +++ b/src/library/extentions/redis_orm.py @@ -0,0 +1,36 @@ +import redis + + +class RedisORM: + def __init__(self, host="localhost", port=6379, db=0): + # self.redis_conn = redis.StrictRedis(host=host, port=port, db=db) + pass + + def query(self, table_class): + return QueryBuilder(self.redis_conn, table_class) + + +class QueryBuilder: + def __init__(self, redis_conn, table_class): + self.redis_conn = redis_conn + self.table_class = table_class + + def filter_by(self, **kwargs): + self.filter_criteria = kwargs + return self + + def first(self): + key = f"{self.table_class.__name__}:{self.filter_criteria['url']}" + data = self.redis_conn.hgetall(key) + return data + + +# Example usage +class User: + pass + + +redis_orm = RedisORM() +query = QueryBuilder(None, None) +result = query.filter_by(url="example.com", name="hello").first() +print(result) diff --git a/src/library/extentions/string_extentions.py b/src/library/extentions/string_extentions.py new file mode 100644 index 000000000..686fc0949 --- /dev/null +++ b/src/library/extentions/string_extentions.py @@ -0,0 +1,118 @@ +import socket +import struct +from typing import Literal + + +def convert_nonprintable_bytes_to_hex_string(buffer: bytes) -> str: + assert isinstance(buffer, bytes) + temp = "" + for byte in buffer: + if byte < 0x1F or byte > 0x7E: + temp += f"[{byte:02X}]" + else: + temp += chr(byte) + return temp + + +def convert_printable_bytes_to_string(buffer: bytes) -> str: + assert isinstance(buffer, bytes) + delimiter = " " + temp = "" + for byte in buffer: + if byte < 0x1F or byte > 0x7E: + if temp and temp[-1] != delimiter: + temp += delimiter + else: + temp += chr(byte) + return temp + + +def convert_kvstring_to_dictionary(kv_str: str): + assert isinstance(kv_str, str) + dic = {} + key_value_list = kv_str.split("\\") + + for i in range(0, len(key_value_list), 2): + if len(key_value_list) < i + 2: + dic[key_value_list[i]] = "" + else: + dic[key_value_list[i]] = key_value_list[i + 1] + + return dic + + +def convert_keystr_to_list(key_str: str): + assert isinstance(key_str, str) + + data = key_str.split("\\") + data = [item for item in data if item] # Remove empty strings from the list + + return data + + +def convert_byte_to_hex_string(buffer: bytes): + assert isinstance(buffer, bytes) + hex_string = ", ".join(["0x" + format(byte, "02X") for byte in buffer]) + return hex_string + + +def from_hex_string_to_bytes(hex_str: str): + """ + use in webservice auth public key convertion + """ + assert isinstance(hex_str, str) + data = [int(hex_str[i : i + 2], 16) for i in range(0, len(hex_str), 2)] + return bytes(data) + + +if __name__ == "__main__": + sig = "0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003020300C06082A864886F70D020505000410" + + data = from_hex_string_to_bytes(sig) + pass + + +def get_first_capitalized_char(name: str): + assert isinstance(name, str) + result = "" + for char in name: + if char.isupper(): + result += char + return result + + +def get_server_short_name(name: str): + if len(name) >= 4: + short_name = get_first_capitalized_char(name) + else: + short_name = name + return short_name + + +def format_network_message( + type: Literal["recv", "send"], message: bytes, is_log_raw: bool = False +): + assert type in ["recv", "send"] + if is_log_raw: + tempLog = f"{convert_printable_bytes_to_string(message)} [{convert_byte_to_hex_string(message)}]" + else: + tempLog = convert_nonprintable_bytes_to_hex_string(message) + + return f"[{type}] {tempLog}" + + +class IPEndPoint: + ip: str + port: int + + def __init__(self, ip: str, port: int) -> None: + assert isinstance(ip, str) + assert isinstance(port, int) + self.ip = ip + self.port = port + + def get_ip_bytes(self) -> bytes: + return socket.inet_aton(self.ip) + + def get_port_bytes(self) -> bytes: + return struct.pack("!H", self.port) diff --git a/src/library/log/__init__.py b/src/library/log/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/library/log/log_manager.py b/src/library/log/log_manager.py new file mode 100644 index 000000000..acd7a4a1a --- /dev/null +++ b/src/library/log/log_manager.py @@ -0,0 +1,91 @@ +import logging +from logging.handlers import TimedRotatingFileHandler +import os +import threading + + +class LogWriter: + original_logger: logging.Logger + __thread_lock: threading.Lock + + def __init__(self, logger) -> None: + self.original_logger = logger + self.__thread_lock = threading.Lock() + + def info(self, message: str): + self.__thread_lock.acquire() + self.original_logger.info(message) + self.__thread_lock.release() + + def error(self, message: str): + self.__thread_lock.acquire() + self.original_logger.error(message) + self.__thread_lock.release() + + def warn(self, message: str): + self.__thread_lock.acquire() + self.original_logger.warn(message) + self.__thread_lock.release() + + +class FakeLogger(LogWriter): + def __init__(self) -> None: + super().__init__(None) + + def info(self, message): + print(message) + + def error(self, message): + print(message) + + def warn(self, message): + print(message) + + +def create_dir(path): + """ + 创建对应目录,如果该目录不存在 + """ + log_path = os.path.dirname(path) + if not os.path.exists(log_path): + os.makedirs(log_path) + + +class LogManager: + + @staticmethod + def create(log_file_path, logger_name) -> None: + create_dir(log_file_path) + + logging.basicConfig( + filename=log_file_path, + level=logging.INFO, + format=f"%(asctime)s [{logger_name}] [%(levelname)s]: %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + # 滚动日志文件 + file_handler = TimedRotatingFileHandler( + log_file_path, + when="midnight", + interval=1, + backupCount=7, + encoding="utf-8", + ) + formatter = logging.Formatter( + f"%(asctime)s [{logger_name}] [%(levelname)s]: %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + file_handler.setLevel( + logging.DEBUG + ) # Set the desired log level for the console + file_handler.setFormatter(formatter) + + # 控制台日志输出 + console_handler = logging.StreamHandler() + console_handler.setLevel(logging.DEBUG) + console_handler.setFormatter(formatter) + + logger = logging.getLogger(log_file_path) + logger.addHandler(file_handler) + logger.addHandler(console_handler) + return LogWriter(logger) diff --git a/src/library/network/__init__.py b/src/library/network/__init__.py new file mode 100644 index 000000000..e78e8f8f4 --- /dev/null +++ b/src/library/network/__init__.py @@ -0,0 +1,9 @@ +DATA_SIZE = 2048 +import abc + + +class Server(abc.ABC): + + @abc.abstractclassmethod + def start(self): + pass diff --git a/src/library/network/http/__init__.py b/src/library/network/http/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/library/network/http/http_handler.py b/src/library/network/http/http_handler.py new file mode 100644 index 000000000..6d0171b93 --- /dev/null +++ b/src/library/network/http/http_handler.py @@ -0,0 +1,76 @@ +from urllib.parse import urlparse +from library.abstractions.client import ClientBase +from library.abstractions.connections import ConnectionBase, ServerBase +from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer +from library.unispy_server_config import CONFIG + + +class HttpRequest: + url: str + headers: dict + content: str + + def __init__(self, url: str, headers: dict, content: str) -> None: + self.url = url + self.headers = headers + self.content = content + + +class HttpResponse: + def __init__(self, request: HttpRequest, content: str) -> None: + assert isinstance(request, HttpRequest) + assert isinstance(content, str) + self.request = request + self.content = content + + def get_content_bytes(self) -> bytes: + return self.content.encode("ascii") + + +class HttpConnection(ConnectionBase): + handler: BaseHTTPRequestHandler + + def send(self, data: bytes) -> None: + self.handler.send_response(200) + self.handler.send_header("Content-type", "text/xml") + self.handler.end_headers() + self.handler.wfile.write(data.get_content_bytes()) + + +class HttpHandler(BaseHTTPRequestHandler): + conn: HttpConnection + + def do_POST(self) -> None: + parsed_url = urlparse(self.path) + content_length = int(self.headers["Content-Length"]) + data = self.rfile.read(content_length).decode() + request = HttpRequest(parsed_url, self.headers, data) + if self.conn is None: + self.conn = HttpConnection(self, *self.server.handler_params) + self.conn.on_received(request) + + +class HttpServer(ServerBase): + def start(self) -> None: + self._server = ThreadingHTTPServer( + (self._config.public_address, self._config.listening_port), HttpHandler + ) + self._server.handler_params = (self._config, self._t_client, self._logger) + self._server.serve_forever() + + +class TestClient(ClientBase): + def create_switcher(self, buffer) -> None: + # return super().create_switcher(buffer) + print(buffer) + pass + + def on_connected(self) -> None: + # return super().on_connected() + print("connected!") + pass + + +if __name__ == "__main__": + # create_http_server(list(CONFIG.servers.values())[0], ClientBase) + s = HttpServer(list(CONFIG.servers.values())[0], TestClient, None) diff --git a/src/library/network/tcp/__init__.py b/src/library/network/tcp/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/library/network/tcp/tcp_handler.py b/src/library/network/tcp/tcp_handler.py new file mode 100644 index 000000000..484cabe06 --- /dev/null +++ b/src/library/network/tcp/tcp_handler.py @@ -0,0 +1,74 @@ +import socket +import socketserver +from library.abstractions.client import ClientBase +from library.abstractions.connections import ConnectionBase, ServerBase + +from library.network import DATA_SIZE +from library.unispy_server_config import CONFIG + + +class TcpConnection(ConnectionBase): + def send(self, data) -> None: + sock: socket.socket = self.handler.request + sock.sendall(data) + + def on_connected(self) -> None: + self._client.on_connected() + + def on_disconnected(self) -> None: + self._client.on_disconnected() + + def disconnect(self) -> None: + sock: socket.socket = self.handler.request + sock.close() + + +class TcpHandler(socketserver.BaseRequestHandler): + request: socket.socket + conn: TcpConnection = None + + def handle(self) -> None: + if self.conn is None: + self.conn = TcpConnection(self, *self.server.handler_params) + self.conn.on_connected() + while True: + try: + data = self.request.recv(DATA_SIZE) + # ignore disconnect data + if not data: + break + self.conn.on_received(data) + except ConnectionResetError: + self.conn.on_disconnected() + except Exception as e: + print(e) + pass + + +class TcpServer(ServerBase): + def start(self) -> None: + self._server = socketserver.ThreadingTCPServer( + (self._config.public_address, self._config.listening_port), + TcpHandler, + ) + self._server.allow_reuse_address = True + self._server.handler_params = (self._config, self._t_client, self._logger) + self._server.serve_forever() + + +class TestClient(ClientBase): + def create_switcher(self, buffer) -> None: + # return super().create_switcher(buffer) + print(buffer) + pass + + def on_connected(self) -> None: + # return super().on_connected() + print("connected!") + pass + + +if __name__ == "__main__": + s = TcpServer(list(CONFIG.servers.values())[0], TestClient, None) + s.start() + pass diff --git a/src/library/network/udp/__init__.py b/src/library/network/udp/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/library/network/udp/udp_handler.py b/src/library/network/udp/udp_handler.py new file mode 100644 index 000000000..cf5830c88 --- /dev/null +++ b/src/library/network/udp/udp_handler.py @@ -0,0 +1,57 @@ +import socket +import socketserver + +from library.abstractions.client import ClientBase +from library.abstractions.connections import ConnectionBase, ServerBase +from library.extentions.string_extentions import IPEndPoint +from library.log.log_manager import LogWriter +from library.unispy_server_config import CONFIG, ServerConfig + + +class UdpConnection(ConnectionBase): + def send(self, data) -> None: + conn: socket.socket = self.handler.request[1] + conn.sendto(data, self.handler.client_address) + + +class UdpHandler(socketserver.BaseRequestHandler): + request: socket.socket + conn: UdpConnection + + def handle(self) -> None: + data = self.request[0] + conn = UdpConnection(self, *self.server.handler_params) + conn.on_received(data) + + def send(self, data: bytes) -> None: + conn: socket.socket = self.request[1] + conn.sendto(data, self.client_address) + + +class UdpServer(ServerBase): + def start(self) -> None: + self._server = socketserver.ThreadingUDPServer( + (self._config.public_address, self._config.listening_port), + UdpHandler, + ) + self._server.handler_params = (self._config, self._t_client, self._logger) + self._server.serve_forever() + + +class TestClient(ClientBase): + def create_switcher(self, buffer) -> None: + # return super().create_switcher(buffer) + print(buffer) + pass + + def on_connected(self) -> None: + # return super().on_connected() + print("connected!") + pass + + +if __name__ == "__main__": + # create_udp_server(list(CONFIG.servers.values())[0], ClientBase) + s = UdpServer(list(CONFIG.servers.values())[0], TestClient, None) + s.start() + pass diff --git a/src/library/unispy_server_config.py b/src/library/unispy_server_config.py new file mode 100644 index 000000000..099f2a6a3 --- /dev/null +++ b/src/library/unispy_server_config.py @@ -0,0 +1,122 @@ +import json +from typing import Dict, Optional +from uuid import UUID +import os + +from library.exceptions.error import UniSpyException + + +class PostgreSql: + url: str + + def __init__( + self, + server, + port, + database, + username, + password, + ssl_mode, + trust_server_cert, + ssl_key, + ssl_password, + root_cert, + ) -> None: + self.server = server + self.port = int(port) + self.database = database + self.username = username + self.password = password + self.ssl_mode = ssl_mode + self.trust_server_cert = trust_server_cert + self.ssl_key = ssl_key + self.ssl_password = ssl_password + self.root_cert = root_cert + self.url = f"postgresql://{self.username}:{self.password}@{self.server}:{self.port}/{self.database}?sslmode={self.ssl_mode}" + + +class RedisConfig: + url: str + + def __init__(self, server, port, user, password, ssl, ssl_host) -> None: + self.server = server + self.port = int(port) + self.user = user + self.password = password + self.ssl = ssl + self.ssl_host = ssl_host + if self.ssl == "true": + self.url = ( + f"rediss://{self.user}:{self.password}@{self.server}:{self.port}/0" + ) + + +class ServerConfig: + def __init__(self, server_id, server_name, public_address, listening_port) -> None: + self.server_id = UUID(server_id) + self.server_name = server_name + self.public_address = public_address + self.listening_port = int(listening_port) + + +class LoggingConfig: + def __init__(self, path: str, min_log_level: str) -> None: + self.path = path + self.min_log_level = min_log_level + + +class BackendConfig: + def __init__(self, url: str) -> None: + self.url = url + + +class MongoDbConfig: + server: str + port: int + username: str + password: str + database: str + url: str + + def __init__(self, server, port, username, password, database) -> None: + self.server = server + self.port = port + self.username = username + self.password = password + self.database = database + self.url = f"mongodb+srv://{self.username}:{self.password}@{server}" + if port is not None: + self.url += f":{port}" + if database is not None: + self.url += f"/{database}" + + +class UniSpyServerConfig: + postgresql: PostgreSql + redis: RedisConfig + backend: BackendConfig + servers: Dict[str, ServerConfig] = {} + mongodb: MongoDbConfig + + def __init__(self, config: Dict[str, str]) -> None: + self.mongodb = MongoDbConfig(**config["mongodb"]) + self.postgresql = PostgreSql(**config["postgresql"]) + self.redis = RedisConfig(**config["redis"]) + self.backend = BackendConfig(**config["backend"]) + self.logging = LoggingConfig(**config["logging"]) + for info in config["servers"]: + self.servers[info["server_name"]] = ServerConfig(**info) + + +unispy_config = os.environ.get("UNISPY_CONFIG") +if unispy_config is None: + raise UniSpyException( + "Unispy server config not found, you should set the UNISPY_CONFIG in the system enviroment." + ) +with open(unispy_config, "r") as f: + config = json.load(f) + CONFIG = UniSpyServerConfig(config) + pass + +if __name__ == "__main__": + pass diff --git a/src/requirement.txt b/src/requirement.txt new file mode 100644 index 000000000..67be29ee4 --- /dev/null +++ b/src/requirement.txt @@ -0,0 +1,12 @@ +pyfiglet +psycopg2-binary +sqlalchemy +jsonpickle == 3.0.3 + +aioredis == 2.0.1 +email_validator == 2.1.1 +attrs +requests +mongoengine == 0.28.2 +flask_socketio +socketio \ No newline at end of file diff --git a/src/servers/__init__.py b/src/servers/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/chat/abstractions/channel.py b/src/servers/chat/abstractions/channel.py new file mode 100644 index 000000000..9b36c3b24 --- /dev/null +++ b/src/servers/chat/abstractions/channel.py @@ -0,0 +1,75 @@ +from servers.chat.abstractions.contract import * +from servers.chat.abstractions.handler import PostLoginHandlerBase +from servers.chat.exceptions.channel import NoSuchChannelException +from servers.chat.exceptions.general import ChatException, NoSuchNickException + + +class ChannelHandlerBase(PostLoginHandlerBase): + _channel: Channel + _user: ChannelUser + _request: ChannelRequestBase + + def _request_check(self) -> None: + if self._request.raw_request is None: + return super()._request_check() + + if self._channel is None: + self._channel = self._client.info.get_local_channel( + self._request.channel_name + ) + if self._channel is None: + raise NoSuchChannelException( + f"No such channel {self._request.channel_name}", + self._request.channel_name, + ) + + if self._user is None: + self._user = self._channel.get_user(self._client) + + if self._user is None: + raise NoSuchNickException( + f"Can not find user with nickname: {self._client.info.nickname} username: {self._client.info.username}" + ) + + def handle(self) -> None: + super().handle() + try: + # we do not publish message when the message is received from remote client + if self._client.is_remote_client: + return + if self._channel is None: + return + + if self.request.raw_request is None: + return + + publish_message() + update_channel_cache() + except Exception as e: + self._handle_exception(e) + + def publish_message(self): + meg = RemoteMessage(self._request, self._client.get_remote_client()) + self._channel.broker.publish_message(msg) + + def update_channel_cache(self): + pass + + +class ChannelRequestBase(RequestBase): + channel_name: str + + def parse(self) -> None: + super().parse() + if self._cmd_params is None or len(self._cmd_params) < 1: + raise ChatException("Channel name is missing.") + + self.channel_name = self._cmd_params[0] + + +class ChannelResponseBase(ResponseBase): + _request: ChannelRequestBase + def __init__(self, request: RequestBase, result: ResultBase) -> None: + super().__init__(request, result) + assert isinstance(request, RequestBase) + assert isinstance(result, ResultBase) diff --git a/src/servers/chat/abstractions/contract.py b/src/servers/chat/abstractions/contract.py new file mode 100644 index 000000000..d85d2e143 --- /dev/null +++ b/src/servers/chat/abstractions/contract.py @@ -0,0 +1,67 @@ +import abc +import library.abstractions.contracts + + +class RequestBase(library.abstractions.contracts.RequestBase, abc.ABC): + raw_request: str = None + command_name: str = None + _prefix: str = None + _cmd_params: list = None + _longParam: str = None + + def __init__(self, raw_request: str) -> None: + assert isinstance(raw_request, str) + super().__init__(raw_request) + + @abc.abstractmethod + def parse(self) -> None: + # at most 2 colon character + # we do not sure about all command + # so i block this code here + self.raw_request = self.raw_request.replace("\r", "").replace("\n", "") + dataFrag = [] + + if self.raw_request.count(":") > 2: + raise Exception(f"IRC request is invalid {self.raw_request}") + + indexOfColon = self.raw_request.index(":") + + rawRequest = self.raw_request + if indexOfColon == 0 and indexOfColon != -1: + prefixIndex = rawRequest.index(" ") + self._prefix = rawRequest[indexOfColon:prefixIndex] + rawRequest = rawRequest[prefixIndex:] + + indexOfColon = rawRequest.index(":") + if indexOfColon != 0 and indexOfColon != -1: + self._longParam = rawRequest[indexOfColon + 1 :] + # reset the request string + rawRequest = rawRequest[:indexOfColon] + + dataFrag = rawRequest.strip(" ").split(" ") + + self.command_name = dataFrag[0] + + if len(dataFrag) > 1: + self._cmd_params = dataFrag[1:] + + +class ResultBase(library.abstractions.contracts.ResultBase, abc.ABC): + pass + + +SERVER_DOMAIN = "unispy.net" + + +class ResponseBase(library.abstractions.contracts.ResponseBase, abc.ABC): + sending_buffer: str + _result: ResultBase + _request: RequestBase + + +if __name__ == "__main__": + # Example usage: + request = RequestBase() + request.raw_request = "your_raw_request_here" + request.parse() + print(request.command_name) diff --git a/src/servers/chat/abstractions/handler.py b/src/servers/chat/abstractions/handler.py new file mode 100644 index 000000000..f55d00341 --- /dev/null +++ b/src/servers/chat/abstractions/handler.py @@ -0,0 +1,23 @@ +from library.abstractions.client import ClientBase +from servers.chat.abstractions.contract import RequestBase +from servers.chat.exceptions.general import IRCException +import library.abstractions.handler + + +class CmdHandlerBase(library.abstractions.handler.CmdHandlerBase): + _request: RequestBase + + def __init__(self, client: ClientBase, request: RequestBase): + super().__init__(client, request) + assert issubclass(request, RequestBase) + + def _handle_exception(self, ex) -> None: + t_ex = type(ex) + if t_ex is IRCException: + self._client.send() + + super()._handle_exception(ex) + + +class PostLoginHandlerBase(CmdHandlerBase): + pass diff --git a/src/servers/chat/abstractions/message.py b/src/servers/chat/abstractions/message.py new file mode 100644 index 000000000..178b72dc1 --- /dev/null +++ b/src/servers/chat/abstractions/message.py @@ -0,0 +1,42 @@ +from library.abstractions.client import ClientBase +from servers.chat.abstractions.channel import ChannelHandlerBase, ChannelRequestBase +from servers.chat.abstractions.contract import ResultBase +from servers.chat.enums.general import MessageType + + +class MessageRequestBase(ChannelRequestBase): + type: MessageType + nick_name: str + message: str + + def parse(self): + super().parse() + if "#" in self.channel_name: + self.type = MessageType.CHANNEL_MESSAGE + else: + self.type = MessageType.USER_MESSAGE + self.channel_name = None + self.nick_name = self._cmd_params[0] + + self.message = self._longParam + + +class MessageResultBase(ResultBase): + user_irc_prefix: str + target_name: str + + +class MessageHandlerBase(ChannelHandlerBase): + _request: MessageRequestBase + _result: MessageResultBase + _receiver: ChannelUser + + def __init__(self, client: ClientBase, request: MessageRequestBase): + assert isinstance(request, MessageRequestBase) + super().__init__(client, request) + + def _update_channel_cache(self): + """we do nothing here, channel message do not need to update channel cache""" + pass + + diff --git a/src/servers/chat/aggregates/brockers.py b/src/servers/chat/aggregates/brockers.py new file mode 100644 index 000000000..48dd2f1f4 --- /dev/null +++ b/src/servers/chat/aggregates/brockers.py @@ -0,0 +1,37 @@ +import threading +import redis +from library.abstractions.brocker import BrockerBase +from library.unispy_server_config import CONFIG + + +class SocketIOBrocker(BrockerBase): + pass + + +class RedisBrocker(BrockerBase): + _subscriber: redis.client.PubSub + + def __init__(self, name: str) -> None: + super().__init__(name) + self.__redis = redis.from_url(CONFIG.redis.url) + self._subscriber = self.__redis.pubsub() + self.sub_thread = threading.Thread(target=self._subscribe) + self.sub_thread.daemon = True + + def _subscribe(self): + self._subscriber.subscribe(self._name) + for message in self._subscriber.listen(): + if message["type"] == "message": + print(message["data"]) + + def publish_message(self, message): + self.__redis.publish(self._name, message) + + def start(self): + self._subscribe() + # self.sub_thread.start() + + +if __name__ == "__main__": + b = RedisBrocker("hello") + b.start() diff --git a/src/servers/chat/aggregates/channel.py b/src/servers/chat/aggregates/channel.py new file mode 100644 index 000000000..39b179805 --- /dev/null +++ b/src/servers/chat/aggregates/channel.py @@ -0,0 +1,184 @@ +import datetime +from uuid import UUID +from servers.chat.abstractions.contract import ResponseBase +from servers.chat.aggregates.channel_user import ChannelUser +from servers.chat.aggregates.key_value_manager import KeyValueManager +from servers.chat.aggregates.peer_room import PeerRoom +from servers.chat.applications.client import Client +from servers.chat.contracts.requests.channel import ModeRequest +from servers.chat.enums.peer_room import PeerRoomType +from servers.chat.exceptions.general import ChatException +from servers.server_browser.v2.aggregations.server_info_builder import PEER_GROUP_LIST + + +class Channel: + server_id: UUID + game_name: str + name: str + max_num_user: int = 200 + create_time: datetime.datetime = datetime.datetime.now(datetime.timezone.utc) + kv_manager: KeyValueManager = KeyValueManager() + room_type: PeerRoomType + password: str + topic: str = None + group_id: int = None + room_name: str = None + previously_join_channel: str = None + + @property + def is_valid_peer_room(self) -> bool: + return self.group_id is not None and self.room_name is not None + + def __init__(self, name: str, client: Client, password: str = None) -> None: + self.server_id = client.server_config.server_id + self.name = name + self.password = password + self.game_name = client.info.gamename + self.previously_join_channel = client.info.previously_joined_channel + self.room_type = PeerRoom.get_room_type(name) + # setup the message broker + self._broker = None + + match self.room_type: + case PeerRoomType.Group: + self.get_group_id() + self.get_peer_room_name() + case PeerRoomType.Staging: + self.get_staging_room_name() + case PeerRoomType.Title: + self.get_title_room_name() + + def get_group_id(self): + group_id_str = self.name.split("!")[1] + try: + group_id = int(group_id_str) + except ValueError: + raise Exception("Peer room group id is incorrect") + self.group_id = group_id + + def get_peer_room_name(self): + if self.game_name in PEER_GROUP_LIST: + grouplist = PEER_GROUP_LIST[self.game_name] + room = next((g for g in grouplist if g["group_id"] == self.group_id), None) + if room is None: + raise Exception(f"Invalid peer room: {self.name}") + self.room_name = room["room_name"] + + def get_staging_room_name(self): + self.room_name = self.name.split("!")[-1] + + def get_title_room_name(self): + self.get_staging_room_name() + + # from multiprocessing import Manager + + ban_list: dict[str, ChannelUser] = {} + users: dict[str, ChannelUser] = {} + _creator_nick_name: str + + @property + def creator(self) -> ChannelUser: + if self._creator_nick_name in self.users: + return self.users[self._creator_nick_name] + else: + return None + + def __add_ban_user(self, request: ModeRequest): + assert isinstance(request, ModeRequest) + if request.nick_name not in self.users: + raise ChatException( + f"user:{request.nick_name} did not connected to this server" + ) + user = self.users[request.nick_name] + + self.ban_list[request.nick_name] = user + + def __remove_ban_user(self, nick_name: str): + if nick_name in self.ban_list: + del self.ban_list[nick_name:str] + + def __add_channel_operator(self, nick_name: str): + if nick_name not in self.users: + return + + user = self.users[nick_name] + if not user.is_channel_creator: + user.is_channel_creator = True + + def __remove_channel_operator(self, nick_name: str): + if nick_name not in self.users: + return + + user = self.users[nick_name] + user.is_channel_creator = False + + def __user_voice_permission(self, nick_name: str, enable: bool = True): + if nick_name not in self.users: + return + user = self.users[nick_name] + user.is_voiceable = enable + + def get_user(self, nick_name: str) -> ChannelUser: + if nick_name in self.users: + return self.users[nick_name] + return None + + def get_user(self, client: Client) -> ChannelUser: + for user in self.users.values(): + if ( + client.connection.remote_ip == user.remote_ip + and client.connection.remote_port == user.remote_port + ): + return user + return None + + def add_bind_on_user_and_channel(joiner: ChannelUser): + joiner.channel.users[joiner.client.info.nick_name] + joiner.client.info.joined_channels[joiner.channel.name] = joiner.channel + + def remove_bind_on_user_and_channel(leaver: ChannelUser): + del leaver.channel.users[leaver.client.info.nick_name] + del leaver.client.info.joined_channels[leaver.channel.name] + + def multicast(self, sender: Client, message: ResponseBase, is_skip_snder=False): + for nick, user in self.users.items(): + if is_skip_snder: + if sender.info.nick_name == nick: + continue + else: + user.client.send(message) + + def remove_user(self, user: ChannelUser): + user.client.info.previously_joined_channel = self.name + + +# channel_manager = Manager() +# brocker_manager = Manager() +# local_channels: dict = channel_manager.dict() +# message_brokers: dict = brocker_manager.dict() +local_channels: dict = {} +message_brokers: dict = {} + + +"""The code blow is for channel manage""" + + +def get_local_channel(name: str) -> Channel: + if name in local_channels: + return local_channels[name] + + +def remove_local_channel(name: str) -> None: + if name in local_channels: + del local_channels[name] + + +def add_message_broker(name: str) -> object: + if name not in message_brokers: + message_brokers[name] = broker + return message_brokers[name] + + +def remove_message_brocker(name: str) -> None: + if name in message_brokers: + del message_brokers[name] diff --git a/src/servers/chat/aggregates/channel_user.py b/src/servers/chat/aggregates/channel_user.py new file mode 100644 index 000000000..153e8ff52 --- /dev/null +++ b/src/servers/chat/aggregates/channel_user.py @@ -0,0 +1,36 @@ +from uuid import UUID + +from servers.chat.aggregates.key_value_manager import KeyValueManager +from servers.chat.applications.client import Client +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from servers.chat.aggregates.channel import Channel + + +class ChannelUser: + server_id: UUID + is_voiceable: bool = True + is_channel_operator: bool = False + is_channel_creator: bool = False + remote_ip: str + remote_port: int + client: Client + kv_manager: KeyValueManager = KeyValueManager() + channel: "Channel" + + @property + def modes(self): + buffer = "" + if self.is_channel_creator: + buffer += "@" + if self.is_voiceable: + buffer += "+" + return buffer + + def __init__(self, client: Client, channel: "Channel") -> None: + self.client = client + self.channel = channel + self.server_id = client.server_config.server_id + self.remote_ip = client.connection.remote_ip + self.remote_port = client.connection.remote_port diff --git a/src/servers/chat/aggregates/key_value_manager.py b/src/servers/chat/aggregates/key_value_manager.py new file mode 100644 index 000000000..cc0cfed9f --- /dev/null +++ b/src/servers/chat/aggregates/key_value_manager.py @@ -0,0 +1,32 @@ +class KeyValueManager: + data: dict + """ + store the key and values + """ + + def __init__(self): + self.data = {} + + def update(self, data: dict): + for key, value in data.items(): + self.data[key] = value + + def build_key_value_string(self, key_values: dict): + flags = "" + for key, value in key_values.items(): + flags += f"\\{key}\\{value}" + return flags + + def get_value_string(self, keys: list[str]) -> list[str]: + values = "" + for key in keys: + if key in self.data: + values += f"\\{self.data[key]}" + else: + values += "\\" + # Uncomment the line below to raise an exception if key is not found + # raise Exception(f"Can not find key: {key}") + return values + + def is_contain_all_key(self, keys: list[str]): + return all(key in self.data for key in keys) diff --git a/src/servers/chat/aggregates/peer_room.py b/src/servers/chat/aggregates/peer_room.py new file mode 100644 index 000000000..2e6649ca6 --- /dev/null +++ b/src/servers/chat/aggregates/peer_room.py @@ -0,0 +1,64 @@ +from servers.chat.enums.peer_room import PeerRoomType + + +class PeerRoom: + + TitleRoomPrefix = "#GSP" + """ When game connects to the server, the player will enter the default channel for communicating with other players.""" + StagingRoomPrefix = "#GSP" + """ + When a player creates their own game and is waiting for others to join they are placed in a separate chat room called the "staging room"\n + Staging rooms have two title seperator like #GSP!xxxx!xxxx + """ + GroupRoomPrefix = "#GPG" + """ + group rooms is used split the list of games into categories (by gametype, skill, region, etc.). In this case, when entering the title room, the user would get a list of group rooms instead of a list of games\n + Group room have one title seperator like #GPG!xxxxxx + """ + TitleSeperator = "!" + + """ + Group room #GPG!622\n + Staging room #GSP!worms3!Ml4lz344lM\n + Normal room #islanbul + """ + + @staticmethod + def get_room_type(channel_name: str) -> PeerRoomType: + if PeerRoom.is_group_room(channel_name): + return PeerRoomType.Group + elif PeerRoom.is_staging_room(channel_name): + return PeerRoomType.Staging + elif PeerRoom.is_title_room(channel_name): + return PeerRoomType.Title + else: + return PeerRoomType.Normal + + @staticmethod + def is_staging_room(channel_name: str) -> bool: + a = channel_name.count(PeerRoom.TitleSeperator) == 2 + b = channel_name.startswith( + PeerRoom.StagingRoomPrefix, 0, len(PeerRoom.StagingRoomPrefix) + ) + return a and b + + @staticmethod + def is_title_room(channel_name: str) -> bool: + a = channel_name.count(PeerRoom.TitleSeperator) == 1 + b = channel_name.startswith( + PeerRoom.TitleRoomPrefix, 0, len(PeerRoom.TitleRoomPrefix) + ) + return a and b + + @staticmethod + def is_group_room(channel_name: str) -> bool: + a = channel_name.count(PeerRoom.TitleSeperator) == 1 + b = channel_name.startswith( + PeerRoom.GroupRoomPrefix, 0, len(PeerRoom.GroupRoomPrefix) + ) + return a and b + + +if __name__ == "__main__": + result = PeerRoom.get_room_type("#GSP!worms3!Ml4lz344lM") + pass \ No newline at end of file diff --git a/src/servers/chat/aggregates/response_name.py b/src/servers/chat/aggregates/response_name.py new file mode 100644 index 000000000..6ec27ef66 --- /dev/null +++ b/src/servers/chat/aggregates/response_name.py @@ -0,0 +1,57 @@ +WELCOME = "001" +USER_IP = "302" +WHO_IS_USER = "311" +END_OF_WHO = "315" +END_OF_WHO_IS = "318" +WHO_IS_CHANNELS = "319" +LIST_START = "321" +LIST = "322" +LIST_END = "323" +CHANNEL_MODELS = "324" +NO_TOPIC = "331" +TOPIC = "332" +WHO_REPLY = "352" +NAME_REPLY = "353" +END_OF_NAMES = "366" +BAN_LIST = "367" +END_OF_BAN_LIST = "368" +GET_KEY = "700" +END_GET_KEY = "701" +GET_CKEY = "702" +END_GET_CKEY = "703" +GET_CHAN_KEY = "704" +SECURE_KEY = "705" +CD_KEY = "706" +LOGIN = "707" +GET_UDP_RELAY = "712" + +# Send a private message +PRIVATE_MSG = "PRIVMSG" +# Send a notice message +NOTICE = "NOTICE" +# Send an under the table message +UNDER_THE_TABLE_MSG = "UTM" +# Send an above the table message +ABOVE_THE_TABLE_MSG = "ATM" +PING = "PING" +PONG = "PONG" +# Search with nickname +NICK = "NICK" +# Join a channel +JOIN = "JOIN" +# Leave a channel +PART = "PART" +# Kick a user from a channel +KICK = "KICK" +# Quit irc chat server +QUIT = "QUIT" + +KILL = "KILL" +# Change channel topic +CHANNEL_TOPIC = "TOPIC" +# Change channel mode +MODE = "MODE" + +ERROR = "ERROR" +# Invite a user to a channel +INVITE = "INVITE" diff --git a/src/servers/chat/aggregates/storage_info.py b/src/servers/chat/aggregates/storage_info.py new file mode 100644 index 000000000..13983615b --- /dev/null +++ b/src/servers/chat/aggregates/storage_info.py @@ -0,0 +1,23 @@ +from mongoengine import ( + Document, + StringField, + IntField, + UUIDField, + BooleanField, + DictField, +) + + +class ChannelInfo(Document): + game_name = StringField(required=True) + channel_name = StringField(required=True) + + +class ChannelUser(Document): + server_id = UUIDField(binary=False, required=True) + is_voiceable = BooleanField(required=True) + is_channel_operator = BooleanField(required=True) + is_channel_creator = BooleanField(required=True) + remote_ip_address = StringField(required=True) + remote_port = IntField(required=True) + key_values = DictField(required=True) diff --git a/src/servers/chat/applications/client.py b/src/servers/chat/applications/client.py new file mode 100644 index 000000000..ce5ecb838 --- /dev/null +++ b/src/servers/chat/applications/client.py @@ -0,0 +1,17 @@ +from library.abstractions.client import ClientBase +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from servers.chat.aggregates.channel import Channel + + +class ClientInfo: + previously_joined_channel: "Channel" + joined_channels: list["Channel"] + nick_name: str + gamename: str + + +class Client(ClientBase): + info: ClientInfo = ClientInfo() + pass diff --git a/src/servers/chat/applications/server_launcher.py b/src/servers/chat/applications/server_launcher.py new file mode 100644 index 000000000..ecb667c25 --- /dev/null +++ b/src/servers/chat/applications/server_launcher.py @@ -0,0 +1,18 @@ +from library.abstractions.server_launcher_base import ServerLauncherBase +from library.network.tcp.tcp_handler import create_tcp_server +from library.unispy_server_config import CONFIG +from servers.chat.applications.client import Client + + +class ServerLauncher(ServerLauncherBase): + def __init__(self) -> None: + super().__init__() + self.config = CONFIG.servers["Chat"] + + def _launch_server(self): + create_tcp_server(self.config, Client) + + +if __name__ == "__main__": + s = ServerLauncher() + s.start() diff --git a/src/servers/chat/applications/switcher.py b/src/servers/chat/applications/switcher.py new file mode 100644 index 000000000..bd795fc41 --- /dev/null +++ b/src/servers/chat/applications/switcher.py @@ -0,0 +1,152 @@ +from library.abstractions.client import ClientBase +from library.abstractions.handler import CmdHandlerBase +from library.abstractions.switcher import SwitcherBase +from servers.chat.contracts.requests.channel import ( + GetCKeyRequest, + GetChannelKeyRequest, + JoinRequest, + KickRequest, + ModeRequest, + NamesRequest, + PartRequest, + SetCKeyRequest, + SetChannelKeyRequest, + TopicRequest, +) +from servers.chat.contracts.requests.general import ( + CdkeyRequest, + CryptRequest, + GetKeyRequest, + ListRequest, + LoginRequest, + NickRequest, + PingRequest, + QuitRequest, + SetKeyRequest, + UserIPRequest, + UserRequest, + WhoIsRequest, + WhoRequest, +) +from servers.chat.contracts.requests.message import ( + ATMRequest, + NoticeRequest, + PrivateRequest, + UTMRequest, +) +from servers.chat.handlers.channel import ( + GetCKeyHandler, + GetChannelKeyHandler, + JoinHandler, + KickHandler, + ModeHandler, + NamesHandler, + PartHandler, + SetCKeyHandler, + SetChannelKeyHandler, + TopicHandler, +) +from servers.chat.handlers.general import ( + CdKeyHandler, + CryptHandler, + GetKeyHandler, + ListHandler, + LoginHandler, + NickHandler, + PingHandler, + QuitHandler, + SetKeyHandler, + UserHandler, + UserIPHandler, + WhoHandler, + WhoIsHandler, +) +from servers.chat.handlers.message import ( + ATMHandler, + NoticeHandler, + PrivateHandler, + UTMHandler, +) + + +class Switcher(SwitcherBase): + _raw_request: str + _client: ClientBase + + def __init__(self, client: ClientBase, raw_request: str) -> None: + assert isinstance(raw_request, str) + super().__init__(client, raw_request) + + def _process_raw_request(self) -> None: + splited_raw_requests = self._raw_request.replace("\r", "").split("\n") + for raw_request in splited_raw_requests: + name = raw_request.strip(" ").split(" ")[0] + self._requests.append((name, raw_request)) + + def _create_cmd_handlers(self, name: str, rawRequest: str) -> CmdHandlerBase: + request = rawRequest + assert isinstance(name, str) + match name: + # region General + case "CRYPT": + return CryptHandler(self._client, CryptRequest(request)) + case "CDKEY": + return CdKeyHandler(self._client, CdkeyRequest(request)) + case "GETKEY": + return GetKeyHandler(self._client, GetKeyRequest(request)) + case "LIST": + return ListHandler(self._client, ListRequest(request)) + case "LOGIN": + return LoginHandler(self._client, LoginRequest(request)) + case "NICK": + return NickHandler(self._client, NickRequest(request)) + case "PING": + return PingHandler(self._client, PingRequest(request)) + case "QUIT": + return QuitHandler(self._client, QuitRequest(request)) + case "SETKEY": + return SetKeyHandler(self._client, SetKeyRequest(request)) + case "USER": + return UserHandler(self._client, UserRequest(request)) + case "USRIP": + return UserIPHandler(self._client, UserIPRequest(request)) + case "WHO": + return WhoHandler(self._client, WhoRequest(request)) + case "WHOIS": + return WhoIsHandler(self._client, WhoIsRequest(request)) + # endregion + + # region Channel + case "GETCHANKEY": + return GetChannelKeyHandler(self._client, GetChannelKeyRequest(request)) + case "GETCKEY": + return GetCKeyHandler(self._client, GetCKeyRequest(request)) + case "JOIN": + return JoinHandler(self._client, JoinRequest(request)) + case "KICK": + return KickHandler(self._client, KickRequest(request)) + case "MODE": + return ModeHandler(self._client, ModeRequest(request)) + case "NAMES": + return NamesHandler(self._client, NamesRequest(request)) + case "PART": + return PartHandler(self._client, PartRequest(request)) + case "SETCHANKEY": + return SetChannelKeyHandler(self._client, SetChannelKeyRequest(request)) + case "SETCKEY": + return SetCKeyHandler(self._client, SetCKeyRequest(request)) + case "TOPIC": + return TopicHandler(self._client, TopicRequest(request)) + # endregion + # region Message + case "ATM": + return ATMHandler(self._client, ATMRequest(request)) + case "NOTICE": + return NoticeHandler(self._client, NoticeRequest(request)) + case "PRIVMSG": + return PrivateHandler(self._client, PrivateRequest(request)) + case "UTM": + return UTMHandler(self._client, UTMRequest(request)) + case _: + return None + # endregion diff --git a/src/servers/chat/contracts/__init__.py b/src/servers/chat/contracts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/chat/contracts/requests/__init__.py b/src/servers/chat/contracts/requests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/chat/contracts/requests/channel.py b/src/servers/chat/contracts/requests/channel.py new file mode 100644 index 000000000..99c1128f9 --- /dev/null +++ b/src/servers/chat/contracts/requests/channel.py @@ -0,0 +1,348 @@ +import re +from typing import List +from servers.chat.abstractions.channel import ChannelRequestBase +from servers.chat.enums.general import ( + GetKeyRequestType, + ModeOperationType, + ModeRequestType, + TopicRequestType, +) +from servers.chat.exceptions.general import ChatException +from library.extentions.string_extentions import convert_kvstring_to_dictionary + + +class GetChannelKeyRequest(ChannelRequestBase): + cookie: str + keys: List + + def parse(self): + super().parse() + + if len(self._cmd_params) != 3: + raise ChatException("The cmdParams number is invalid.") + + if self._longParam is None or self._longParam[-1] != "\0": + raise ChatException("The longParam number is invalid.") + self.cookie = self._cmd_params[0] + self.keys = self._longParam.strip("\\").rstrip("\0").split("\\") + + +class GetCKeyRequest(ChannelRequestBase): + nick_name: str + cookie: str + keys: List + request_type: GetKeyRequestType + + def parse(self): + super().parse() + if len(self._cmd_params) != 4: + raise ChatException("The number of IRC parameters are incorrect.") + if self._longParam is None: + raise ChatException("The IRC long parameter is incorrect.") + + self.nick_name = self._cmd_params[1] + if self.nick_name == "*": + self.request_type = GetKeyRequestType.GET_CHANNEL_ALL_USER_KEY_VALUE + else: + self.request_type = GetKeyRequestType.GET_CHANNEL_SPECIFIC_USER_KEY_VALUE + + self.cookie = self._cmd_params[2] + + if "\0" not in self._longParam and "\\" not in self._longParam: + + raise ChatException("The key provide is incorrect.") + + self.keys = self._longParam.strip("\\").rstrip("\0").split("\\") + + +class GetUdpRelayRequest(ChannelRequestBase): + pass + + +class JoinRequest(ChannelRequestBase): + password: str + + def parse(self): + super().parse() + if len(self._cmd_params) > 2: + raise ChatException("The number of IRC parameters are incorrect.") + + if len(self._cmd_params) == 2: + self.password = self._cmd_params[1] + + +class KickRequest(ChannelRequestBase): + kickee_nick_name: str + reason: str + + def parse(self): + super().parse() + if len(self._cmd_params) != 2: + raise ChatException("The number of IRC parameters are incorrect.") + self.kickee_nick_name = self._cmd_params[1] + + if self._longParam is None: + raise ChatException("The IRC long parameters is missing.") + + self.reason = self._longParam + + +class ModeRequest(ChannelRequestBase): + # request: + # "MODE +/-q" + + # "MODE +/-k " + + # "MODE +l " + # "MODE -l" + + # "MODE +b" actually we do not care about this request + # "MODE +/-b " + + # "MODE +/-co " + # "MODE +/-cv " + + # "MODE " + # "MODE " + request_type: ModeRequestType + mode_operations: list = [] + nick_name: str + user_name: str + limit_number: int + mode_flag: str + password: str + + def parse(self): + if self.raw_request is None: + return + super().parse() + if len(self._cmd_params) == 1: + self.request_type == ModeRequestType.GET_CHANNEL_MODES + elif len(self._cmd_params) == 2 or len(self._cmd_params) == 3: + self.request_type = ModeRequestType.SET_CHANNEL_MODES + self.mode_flag = self._cmd_params[1] + modeFlags = [s for s in re.split(r"(?=\+|\-)", self.mode_flag) if s.strip()] + modeFlags = list(filter(None, modeFlags)) + for flag in modeFlags: + match flag: + case "+e": + self.mode_operations.append( + ModeOperationType.SET_OPERATOR_ABEY_CHANNEL_LIMITS + ) + case "-e": + self.mode_operations.append( + ModeOperationType.REMOVE_OPERATOR_ABEY_CHANNEL_LIMITS + ) + case "+t": + self.mode_operations.append( + ModeOperationType.SET_TOPIC_CHANGE_BY_OPERATOR_FLAG + ) + case "-t": + self.mode_operations.append( + ModeOperationType.REMOVE_TOPIC_CHANGE_BY_OPERATOR_FLAG + ) + case "+n": + self.mode_operations.append( + ModeOperationType.ENABLE_EXTERNAL_MESSAGES_FLAG + ) + case "-n": + self.mode_operations.append( + ModeOperationType.DISABLE_EXTERNAL_MESSAGES_FLAG + ) + case "+m": + self.mode_operations.append( + ModeOperationType.SET_MODERATED_CHANNEL_FLAG + ) + case "-m": + self.mode_operations.append( + ModeOperationType.REMOVE_MODERATED_CHANNEL_FLAG + ) + case "+s": + self.mode_operations.append( + ModeOperationType.SET_SECRET_CHANNEL_FLAG + ) + + case "-s": + self.mode_operations.append( + ModeOperationType.REMOVE_SECRET_CHANNEL_FLAG + ) + case "+i": + self.mode_operations.append(ModeOperationType.SET_INVITED_ONLY) + case "-i": + self.mode_operations.append( + ModeOperationType.REMOVE_INVITED_ONLY + ) + case "-p": + self.mode_operations.append( + ModeOperationType.REMOVE_PRIVATE_CHANNEL_FLAG + ) + case "-p": + self.mode_operations.append( + ModeOperationType.SET_PRIVATE_CHANNEL_FLAG + ) + case "+q": + self.mode_operations.append( + ModeOperationType.ENABLE_USER_QUIET_FLAG + ) + case "-q": + self.mode_operations.append( + ModeOperationType.DISABLE_USER_QUIET_FLAG + ) + case "+k": + self.mode_operations.append( + ModeOperationType.ADD_CHANNEL_PASSWORD + ) + case "-k": + self.mode_operations.append( + ModeOperationType.REMOVE_CHANNEL_PASSWORD + ) + case "+l": + self.channel_name = self._cmd_params[0] + self.limit_number = int(self._cmd_params[2]) + self.mode_operations.append( + ModeOperationType.REMOVE_CHANNEL_PASSWORD + ) + + case "+l": + self.channel_name = self._cmd_params[0] + self.limit_number = int(self._cmd_params[2]) + self.mode_operations.append( + ModeOperationType.ADD_CHANNEL_USER_LIMITS + ) + case "-l": + self.channel_name = self._cmd_params[0] + self.mode_operations.append( + ModeOperationType.REMOVE_CHANNEL_USER_LIMITS + ) + case "+b": + self.channel_name = self._cmd_params[0] + if len(self._cmd_params) == 3: + self.nick_name = self._cmd_params[2] + self.mode_operations.append( + ModeOperationType.ADD_BAN_ON_USER + ) + else: + self.mode_operations.append( + ModeOperationType.GET_BANNED_USERS + ) + case "-b": + self.channel_name = self._cmd_params[0] + self.mode_operations.append( + ModeOperationType.REMOVE_BAN_ON_USER + ) + case "+co": + self.channel_name = self._cmd_params[0] + self.user_name = self._cmd_params[2] + self.mode_operations.append( + ModeOperationType.ADD_CHANNEL_OPERATOR + ) + case "-co": + self.channel_name = self._cmd_params[0] + self.user_name = self._cmd_params[2] + self.mode_operations.append( + ModeOperationType.REMOVE_CHANNEL_OPERATOR + ) + case "+cv": + self.channel_name = self._cmd_params[0] + self.user_name = self._cmd_params[2] + self.mode_operations.append( + ModeOperationType.ENABLE_USER_VOICE_PERMISSION + ) + case "-cv": + self.channel_name = self._cmd_params[0] + self.user_name = self._cmd_params[2] + self.mode_operations.append( + ModeOperationType.DISABLE_USER_VOICE_PERMISSION + ) + # Add more cases for other flags following the same pattern + case _: + raise ChatException("Unknown mode request type.") + else: + raise ChatException("The number of IRC parameters are incorrect.") + + +class NamesRequest(ChannelRequestBase): + channel_name: str + + def __init__(self, raw_request: str = None) -> None: + if raw_request is not None: + super().__init__(raw_request) + + +class PartRequest(ChannelRequestBase): + channel_name: str + reason: str = "Unknown reason" + + def __init__(self, raw_request: str = None) -> None: + if raw_request is not None: + super().__init__(raw_request) + + def parse(self): + super().parse() + + if self._longParam is None: + raise ChatException("The reason of living channel is missing.") + self.reason = self._longParam + + +class SetChannelKeyRequest(ChannelRequestBase): + key_value_string: str + key_values: dict[str, str] + + def parse(self): + super().parse() + if self._longParam is None: + raise ChatException("Channel keys and values are missing.") + self._longParam = self._longParam[1:] + self.key_value_string = self._longParam + self.key_values = convert_kvstring_to_dictionary(self.key_value_string) + + +class SetCKeyRequest(ChannelRequestBase): + nick_name: str + cookie: str + is_broadcast: bool + key_value_string: str + key_values: dict[str, str] + + def parse(self) -> None: + super().parse() + if self._cmd_params is None: + raise ChatException("The cmdParams from SETCKEY request are missing.") + + if self._longParam is None: + raise ChatException("The longParam from SETCKEY request is missing.") + + self.channel_name = self._cmd_params[0] + self.nick_name = self._cmd_params[1] + self.key_value_string = self._longParam[1:] + self.key_values = convert_kvstring_to_dictionary(self.key_value_string) + + if "b_" in self.key_values: + self.cookie = "BCAST" + self.is_broadcast = True + + +class SetGroupRequest(ChannelRequestBase): + group_name: str + + def parse(self) -> None: + super().parse() + if len(self._cmd_params) != 1: + raise ChatException( + "the number of IRC cmd params in GETKEY request is incorrect." + ) + self.group_name = self._cmd_params[0] + + +class TopicRequest(ChannelRequestBase): + channel_topic: str + request_type: TopicRequestType + + def parse(self) -> None: + super().parse() + if self._longParam is None: + self.request_type = TopicRequestType.GET_CHANNEL_TOPIC + else: + self.request_type = TopicRequestType.SET_CHANNEL_TOPIC + self.channel_topic = self._longParam diff --git a/src/servers/chat/contracts/requests/general.py b/src/servers/chat/contracts/requests/general.py new file mode 100644 index 000000000..b424adfd1 --- /dev/null +++ b/src/servers/chat/contracts/requests/general.py @@ -0,0 +1,285 @@ +from typing import Dict +from library.extentions.string_extentions import ( + convert_keystr_to_list, + convert_kvstring_to_dictionary, +) +from servers.chat.abstractions.contract import RequestBase +from servers.chat.enums.general import LoginRequestType, WhoRequestType +from servers.chat.exceptions.general import ChatException +from servers.chat.exceptions.general import NickNameInUseException + + +class CdkeyRequest(RequestBase): + cdkey: str + + def parse(self): + super().parse() + if len(self._cmd_params) < 1: + raise ChatException("The number of IRC cmdParams are incorrect.") + + self.cdkey = self._cmd_params[0] + + +class CryptRequest(RequestBase): + version_id: str + gamename: str + + def parse(self): + super().parse() + if len(self._cmd_params) < 3: + raise ChatException( + "The number of IRC params in CRYPT request is incorrect." + ) + self.version_id = self._cmd_params[1] + self.gamename = self._cmd_params[2] + + +class GetUdpRelayRequest(RequestBase): + pass + + +class InviteRequest(RequestBase): + channel_name: str + nick_name: str + + def parse(self): + super().parse() + if len(self._cmd_params) > 2: + raise ChatException( + "The number of IRC cmd params in Invite request is incorrect." + ) + self.channel_name = self._cmd_params[0] + self.nick_name = self._cmd_params[1] + + +class ListLimitRequest(RequestBase): + max_number_of_channels: int + filter: str + + def parse(self): + super().parse() + if len(self._cmd_params) != 2: + raise ChatException( + "The number of IRC cmd params in ListLimit request is incorrect." + ) + try: + self.max_number_of_channels = int(self._cmd_params[0]) + + except Exception as e: + raise ChatException("The max number format is incorrect.") + + self.filter = self._cmd_params[1] + + +class ListRequest(RequestBase): + is_searching_channel: bool = False + is_searching_user: bool = False + filter: str + + def parse(self): + super().parse() + if self._cmd_params is None or self._cmd_params.count() == 0: + raise ChatException("The Search filter is missing.") + + self.is_searching_channel = True + self.filter = self._cmd_params[0] + + +class LoginPreAuth(RequestBase): + auth_token: str + partner_challenge: str + + def parse(self): + super().parse() + self.auth_token = self._cmd_params[0] + self.partner_challenge = self._cmd_params[1] + + +class LoginRequest(RequestBase): + request_type: LoginRequestType + namespace_id: int + nick_name: str + email: str + unique_nick: str + password_hash: str + + def parse(self): + super().parse() + try: + self.namespace_id = int(self._cmd_params[0]) + + except Exception as e: + raise ChatException("The namespaceid format is incorrect.") + + if self._cmd_params[1] == "*": + self.request_type = LoginRequestType.NICK_AND_EMAIL_LOGIN + self.password_hash = self._cmd_params[2] + if self._longParam.count("@") != 2: + raise ChatException("The profile nick format is incorrect.") + + profile_nick_index = self._longParam.index("@") + self.nick_name = self._longParam[0:profile_nick_index] + self.email = self._longParam[profile_nick_index + 1 :] + return + + self.request_type = LoginRequestType.UNIQUE_NICK_LOGIN + self.unique_nick = self._cmd_params[1] + self.password_hash = self._cmd_params[2] + + +class NickRequest(RequestBase): + _invalid_chars = "#@$%^&*()~" + nick_name: str + + def parse(self): + super().parse() + if len(self._cmd_params) == 1: + self.nick_name = self._cmd_params[0] + elif self._longParam is None: + self.nick_name = self._longParam + else: + raise ChatException("NICK request is invalid.") + + for c in self._invalid_chars: + if c in self.nick_name: + raise NickNameInUseException( + self.nick_name, + self.nick_name, + f"The nick name: {self.nick_name} contains invalid character.", + ) + + +class PingRequest(RequestBase): + pass + + +class PongRequest(RequestBase): + echo_message: str + + def parse(self): + super().parse() + if self._longParam is None: + raise ChatException("Echo message is missing.") + self.echo_message = self._longParam + + +class QuitRequest(RequestBase): + reason: str + + def parse(self): + super().parse() + if self._longParam is None: + raise ChatException("Quit reason is missing.") + + self.reason = self._longParam + + +class RegisterNickRequest(RequestBase): + namespace_id: int + unique_nick: str + cdkey: str + + def parse(self): + super().parse() + self.namespace_id = self._cmd_params[0] + self.unique_nick = self._cmd_params[1] + self.cdkey = self._cmd_params[2] + + +class SetKeyRequest(RequestBase): + key_values: Dict[str, str] + + def parse(self): + super().parse() + if self._longParam is None: + raise ChatException("The keys and values are missing.") + + self.key_values = convert_kvstring_to_dictionary(self._longParam) + + +class UserIPRequest(RequestBase): + remote_ip_address: str + + +class UserRequest(RequestBase): + user_name: str + host_name: str + server_name: str + nick_name: str + name: str + + def parse(self): + super().parse() + if len(self._cmd_params) == 3: + self.user_name = self._cmd_params[0] + self.host_name = self._cmd_params[1] + self.server_name = self._cmd_params[2] + else: + self.host_name = self._cmd_params[0] + self.server_name = self._cmd_params[1] + + self.name = self._longParam + + +class WhoIsRequest(RequestBase): + nick_name: str + + def parse(self): + super().parse() + if len(self._cmd_params) != 1: + raise ChatException( + "The number of IRC cmd params in WHOIS request is incorrect." + ) + + self.nick_name = self._cmd_params[0] + + +class WhoRequest(RequestBase): + request_type: WhoRequestType + channel_name: str + nick_name: str + + def parse(self): + super().parse() + if len(self._cmd_params) != 1: + raise ChatException( + "The number of IRC cmd params in WHO request is incorrect." + ) + + if "#" in self._cmd_params[0]: + self.request_type = WhoRequestType.GET_CHANNEL_USER_INFO + self.channel_name = self._cmd_params[0] + return + + self.request_type = WhoRequestType.GET_USER_INFO + self.nick_name = self._cmd_params[0] + + +class GetKeyRequest(RequestBase): + is_get_all_user: bool = False + nick_name: str + cookie: str + unknown_cmd_param: str + keys: list[str] + + def parse(self) -> None: + super().parse() + if len(self._cmd_params) < 2: + raise ChatException( + "The number of IRC cmd params in GETKEY request is incorrect." + ) + + if self._longParam is None: + raise ChatException( + "The number of IRC cmd params in GETKEY request is incorrect." + ) + + self.nick_name = self._cmd_params[0] + self.cookie = self._cmd_params[1] + self.unknown_cmd_param = self._cmd_params[2] + + self._longParam = self._longParam[: len(self._longParam)] + if self.nick_name == "*": + self.is_get_all_user = True + + self.keys = convert_keystr_to_list(self._longParam) diff --git a/src/servers/chat/contracts/requests/message.py b/src/servers/chat/contracts/requests/message.py new file mode 100644 index 000000000..65c408194 --- /dev/null +++ b/src/servers/chat/contracts/requests/message.py @@ -0,0 +1,17 @@ +from servers.chat.abstractions.message import MessageRequestBase + + +class ATMRequest(MessageRequestBase): + pass + + +class NoticeRequest(MessageRequestBase): + pass + + +class PrivateRequest(MessageRequestBase): + pass + + +class UTMRequest(MessageRequestBase): + pass diff --git a/src/servers/chat/contracts/responses/__init__.py b/src/servers/chat/contracts/responses/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/chat/contracts/responses/channel.py b/src/servers/chat/contracts/responses/channel.py new file mode 100644 index 000000000..1a64da2db --- /dev/null +++ b/src/servers/chat/contracts/responses/channel.py @@ -0,0 +1,173 @@ +from library.abstractions.contracts import RequestBase, ResultBase +from servers.chat.abstractions.channel import ChannelResponseBase +from servers.chat.abstractions.contract import ( + SERVER_DOMAIN, + RequestBase, + ResponseBase, + ResultBase, +) +from servers.chat.aggregates.response_name import * +from servers.chat.contracts.requests.channel import ( + GetCKeyRequest, + GetChannelKeyRequest, + JoinRequest, + KickRequest, + ModeRequest, + NamesRequest, + PartRequest, + SetCKeyRequest, + SetChannelKeyRequest, + TopicRequest, +) +from servers.chat.contracts.results.channel import ( + GetCKeyResult, + GetChannelKeyResult, + JoinResult, + KickResult, + ModeResult, + NamesResult, + PartResult, + TopicResult, +) +from servers.chat.enums.general import ModeRequestType + + +class GetChannelKeyResponse(ChannelResponseBase): + _request: GetChannelKeyRequest + _result: GetChannelKeyResult + + def build(self) -> None: + self.sending_buffer = f":{self._result.channel_user_irc_prefix} {GET_CHAN_KEY} * {self._result.channel_name} {self._request.cookie} {self._result.values}\r\n" + + +class GetCKeyResponse(ResponseBase): + _request: GetCKeyRequest + _result: GetCKeyResult + + def __init__(self, request: GetCKeyRequest, result: GetCKeyResult) -> None: + assert isinstance(request, GetCKeyRequest) + assert isinstance(result, GetCKeyResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = "" + for nick_name, user_values in self._result.infos: + self.sending_buffer += f":{SERVER_DOMAIN} {GET_CKEY} * {self._request.channel_name} {nick_name} {self._request.cookie} {user_values}\r\n" + + self.sending_buffer += f"{SERVER_DOMAIN} {END_GET_CKEY} * {self._request.channel_name} {self._request.cookie} :End Of GETCKEY.\r\n" + + +class JoinResponse(ResponseBase): + _request: JoinRequest + _result: JoinResult + + def __init__(self, request: JoinRequest, result: JoinResult) -> None: + assert isinstance(request, JoinRequest) + assert isinstance(result, JoinResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = ( + f"{self._result.joiner_prefix} {JOIN} {self._request.channel_name}\r\n" + ) + + +class KickResponse(ResponseBase): + _request: KickRequest + _result: KickResult + + def __init__(self, request: KickRequest, result: KickResult) -> None: + assert isinstance(request, KickRequest) + assert isinstance(result, KickResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = f"{self._result.kicker_irc_prefix} {KICK} {self._result.channel_name} {self._result.kickee_nick_name} :{self._request.reason}\r\n" + + +class ModeResponse(ResponseBase): + _request: ModeRequest + _result: ModeResult + + def __init__(self, request: ModeRequest, result: ModeResult) -> None: + assert isinstance(request, ModeRequest) + assert isinstance(result, ModeResult) + super().__init__(request, result) + + def build(self) -> None: + if self._request.request_type == ModeRequestType.GET_CHANNEL_MODES: + self.sending_buffer = f":{SERVER_DOMAIN} {CHANNEL_MODELS} * {self._result.channel_modes} {self._result.channel_modes}\r\n" + + +class NamesResponse(ChannelResponseBase): + _request: NamesRequest + _result: NamesResult + + def __init__(self, request: NamesRequest, result: NamesResult) -> None: + assert isinstance(request, NamesRequest) + assert isinstance(result, NamesResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = f":{SERVER_DOMAIN} {NAME_REPLY} {self._result.requester_nick_name} = {self._result.channel_name} :{self._result.all_channel_user_nicks}\r\n" + + self.sending_buffer = f":{SERVER_DOMAIN} {END_OF_NAMES} {self._result.requester_nick_name} {self._result.channel_name} :End of NAMES list. \r\n" + + +class PartResponse(ResponseBase): + _request: PartRequest + _result: PartResult + + def __init__(self, request: PartRequest, result: PartResult) -> None: + assert isinstance(request, PartRequest) + assert isinstance(result, PartResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = f":{self._result.leaver_irc_prefix} {PART} {self._result.channel_name} :{self._request.reason}\r\n" + + +class SetChannelKeyResponse(ChannelResponseBase): + _request: SetChannelKeyRequest + _result: GetChannelKeyResult + + def __init__( + self, request: SetChannelKeyRequest, result: GetChannelKeyResult + ) -> None: + assert isinstance(request, SetChannelKeyRequest) + assert isinstance(result, GetChannelKeyResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = f":{self._result.channel_user_irc_prefix} {GET_CHAN_KEY} * {self._request.channel_name} BCAST {self._request.key_value_string}\r\n" + + +class SetCKeyResponse(ResponseBase): + _request: SetCKeyRequest + + def __init__(self, request: SetCKeyRequest) -> None: + assert isinstance(request, SetCKeyRequest) + super().__init__(request, None) + + def build(self) -> None: + self.sending_buffer = f":{SERVER_DOMAIN} {GET_CKEY} * {self._request.channel_name} {self._request.nick_name} {self._request.cookie} {self._request.key_value_string}\r\n" + + self.sending_buffer = f":{SERVER_DOMAIN} {END_GET_CKEY} * {self._request.channel_name} {self._request.nick_name} {self._request.cookie} :End Of SETCKEY.\r\n" + + +class TopicResponse(ResponseBase): + _request: TopicRequest + _result: TopicResult + + def __init__(self, request: TopicRequest, result: TopicResult) -> None: + assert isinstance(request, TopicRequest) + assert isinstance(result, TopicResult) + super().__init__(request, result) + + def build(self) -> None: + if self._result.channel_topic == "" or self._result.channel_topic is None: + self.sending_buffer = ( + f":{SERVER_DOMAIN} {NO_TOPIC} * {self._result.channel_name}\r\n" + ) + else: + self.sending_buffer = f":{SERVER_DOMAIN} {TOPIC} * {self._result.channel_name} :{self._result.channel_topic}\r\n" diff --git a/src/servers/chat/contracts/responses/general.py b/src/servers/chat/contracts/responses/general.py new file mode 100644 index 000000000..f1c8101f8 --- /dev/null +++ b/src/servers/chat/contracts/responses/general.py @@ -0,0 +1,161 @@ +from library.abstractions.contracts import RequestBase, ResultBase +from library.encryption.gs_encryption import CLIENT_KEY, SERVER_KEY +from servers.chat.abstractions.contract import SERVER_DOMAIN, ResponseBase +from servers.chat.aggregates.response_name import * +from servers.chat.contracts.requests.general import GetKeyRequest, WhoRequest +from servers.chat.contracts.results.general import ( + GetKeyResult, + ListResult, + LoginResult, + NickResult, + PingResult, + UserIPResult, + WhoIsResult, + WhoResult, +) +from servers.chat.enums.general import WhoRequestType + + +class CdKeyResponse(ResponseBase): + def build(self) -> None: + self.sending_buffer = f":{SERVER_DOMAIN} {CD_KEY} * 1 :Authenticated.\r\n" + + +class CryptResponse(ResponseBase): + def __init__(self) -> None: + pass + + def build(self) -> None: + self.sending_buffer = ( + f":{SERVER_DOMAIN} {SECURE_KEY} * {CLIENT_KEY} {SERVER_KEY}\r\n" + ) + + +class GetKeyResponse(ResponseBase): + _request: GetKeyRequest + _result: GetKeyResult + + def __init__(self, request: GetKeyRequest, result: GetKeyResult) -> None: + assert isinstance(request, GetKeyRequest) + assert isinstance(result, GetKeyResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = "" + + for value in self._result.values: + self.sending_buffer += f":{SERVER_DOMAIN} {GET_KEY} * {self._result.nick_name} {self._request.cookie} {value}\r\n" + + self.sending_buffer += f":{SERVER_DOMAIN} {END_GET_KEY} * {self._request.cookie} * :End Of GETKEY.\r\n" + + +class ListResponse(ResponseBase): + _result: ListResult + + def __init__(self, result: ListResult) -> None: + assert isinstance(result, ListResult) + super().__init__(None, result) + + def build(self) -> None: + self.sending_buffer = "" + for ( + channel_name, + total_channel_user, + channel_topic, + ) in self._result.channel_info_list: + self.sending_buffer += f":{self._result.user_irc_prefix} {LIST_START} * {channel_name} {total_channel_user} {channel_topic}\r\n" + self.sending_buffer += f":{self._result.user_irc_prefix} {LIST_END}\r\n" + + +class LoginResponse(ResponseBase): + _result: LoginResult + + def __init__(self, result: LoginResult) -> None: + assert isinstance(result, LoginResult) + super().__init__() + + def build(self) -> None: + self.sending_buffer = f":{SERVER_DOMAIN} {LOGIN} * {self._result.user_id} {self._result.profile_id}\r\n" + + +class NickResponse(ResponseBase): + _result: NickResult + + def __init__(self, result: NickResult) -> None: + assert isinstance(result, NickResult) + super().__init__(None, result) + + def build(self) -> None: + self.sending_buffer = f":{SERVER_DOMAIN} {WELCOME} {self._result.nick_name} :Welcome to UniSpy!\r\n" + + +class PingResponse(ResponseBase): + _result: PingResult + + def __init__(self, result: PingResult) -> None: + assert isinstance(result, PingResult) + super().__init__(None, result) + + def build(self) -> None: + self.sending_buffer = f":{self._result.requester_irc_prefix} {PONG}\r\n" + + +class UserIPResponse(ResponseBase): + _result: UserIPResult + + def __init__(self, result: UserIPResult) -> None: + assert isinstance(result, UserIPResult) + super().__init__(None, result) + + def build(self) -> None: + self.sending_buffer = ( + f":{SERVER_DOMAIN} {USER_IP} :@{self._result.remote_ip_address}\r\n" + ) + + +class WhoIsResponse(ResponseBase): + _result: WhoIsResult + + def __init__(self, result: WhoIsResult) -> None: + assert isinstance(result, UserIPResult) + super().__init__(None, result) + + def build(self) -> None: + self.sending_buffer = f":{SERVER_DOMAIN} {WHO_IS_USER} {self._result.nick_name} {self._result.name} {self._result.user_name} {self._result.public_ip_address} * :{self._result.user_name}\r\n" + + if len(self._result.joined_channel_name) != 0: + channel_name = "" + for name in self._result.joined_channel_name: + channel_name += f"@{name} " + + self.sending_buffer += f":{SERVER_DOMAIN} {WHO_IS_CHANNELS} {self._result.nick_name} {self._result.name} :{channel_name}\r\n" + + self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO_IS} {self._result.nick_name} {self._result.name} :End of WHOIS list. \r\n" + + +class WhoResponse(ResponseBase): + _request: WhoRequest + _result: WhoResult + + def __init__(self, request: WhoRequest, result: WhoResult) -> None: + assert isinstance(request, WhoRequest) + assert isinstance(result, WhoResult) + super().__init__(request, result) + + def build(self): + self.sending_buffer = "" + for ( + channel_name, + user_name, + public_ip_address, + nick_name, + modes, + ) in self._result.infos: + self.sending_buffer += f":{SERVER_DOMAIN} {WHO_REPLY} * {channel_name} {user_name} {public_ip_address} * {nick_name} {modes} *\r\n" + + if self._request.request_type == WhoRequestType.GET_CHANNEL_USER_INFO: + if len(self._result.infos) > 0: + self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.channel_name} * :End of WHO.\r\n" + elif self._request.request_type == WhoRequestType.GET_USER_INFO: + if len(self._result.infos) > 0: + self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.nick_name} * :End of WHO.\r\n" diff --git a/src/servers/chat/contracts/responses/message.py b/src/servers/chat/contracts/responses/message.py new file mode 100644 index 000000000..0d7d7a168 --- /dev/null +++ b/src/servers/chat/contracts/responses/message.py @@ -0,0 +1,68 @@ +from library.abstractions.contracts import RequestBase, ResultBase +from servers.chat.abstractions.contract import ResponseBase +from servers.chat.aggregates.response_name import * +from servers.chat.contracts.requests.message import ( + ATMRequest, + NoticeRequest, + PrivateRequest, + UTMRequest, +) +from servers.chat.contracts.results.message import ( + ATMResult, + NoticeResult, + PrivateResult, + UTMResult, +) + + +class ATMResponse(ResponseBase): + _request: ATMRequest + _result: ATMResult + + def __init__(self, request: ATMRequest, result: ATMResult) -> None: + assert isinstance(request, ATMRequest) + assert isinstance(result, ATMResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = f":{self._result.user_irc_prefix} {ABOVE_THE_TABLE_MSG} {self._result.target_name} :{self._request.message}\r\n" + + +class NoticeResponse(ResponseBase): + _request: NoticeRequest + _result: NoticeResult + + def __init__(self, request: NoticeRequest, result: NoticeResult) -> None: + assert isinstance(result, NoticeResult) + assert isinstance(request, NoticeRequest) + + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = f":{self._result.user_irc_prefix} {NOTICE} {self._result.target_name} {self._request.message}\r\n" + + +class PrivateResponse(ResponseBase): + _request: PrivateRequest + _result: PrivateResult + + def __init__(self, request: PrivateRequest, result: PrivateResult) -> None: + assert isinstance(result, PrivateRequest) + assert isinstance(request, PrivateResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = f":{self._result.user_irc_prefix} {PRIVATE_MSG} {self._result.target_name} :{self._request.message}\r\n" + + +class UTMResponse(ResponseBase): + _request: UTMRequest + _result: UTMResult + + def __init__(self, request: UTMRequest, result: UTMResult) -> None: + assert isinstance(request, UTMRequest) + assert isinstance(result, UTMResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = f":{self._result.user_irc_prefix} {UNDER_THE_TABLE_MSG} {self._result.target_name} :{self._request.message}" diff --git a/src/servers/chat/contracts/results/__init__.py b/src/servers/chat/contracts/results/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/chat/contracts/results/channel.py b/src/servers/chat/contracts/results/channel.py new file mode 100644 index 000000000..ffbdc0f2a --- /dev/null +++ b/src/servers/chat/contracts/results/channel.py @@ -0,0 +1,61 @@ +from servers.chat.abstractions.contract import ResultBase + + +class GetChannelKeyResult(ResultBase): + channel_user_irc_prefix: str + channel_name: str + values: str + + +class GetCKeyResult(ResultBase): + infos: list[tuple] + """ nick_name:str, user_values:str""" + channel_name: str + + +class JoinResult(ResultBase): + joiner_prefix: str + joiner_nick_name: str + all_channel_user_nicks: str + channel_modes: str + + +class KickResult(ResultBase): + channel_name: str + kicker_irc_prefix: str + kicker_nick_name: str + kickee_nick_name: str + + +class ModeResult(ResultBase): + channel_name: str + channel_modes: str + joiner_nick_name: str + + +class NamesResult(ResultBase): + all_channel_user_nicks: str + channel_name: str + requester_nick_name: str + + +class PartResult(ResultBase): + leaver_irc_prefix: str + is_channel_creator: bool + channel_name: str + + +class GetChannelKeyResult(ResultBase): + channel_user_irc_prefix: str + channel_name: str + values: str + + +class TopicResult(ResultBase): + channel_name: str + channel_topic: str + + +class SetChannelKeyResult(ResultBase): + channel_user_irc_prefix: str + channel_name: str diff --git a/src/servers/chat/contracts/results/general.py b/src/servers/chat/contracts/results/general.py new file mode 100644 index 000000000..91c0199cd --- /dev/null +++ b/src/servers/chat/contracts/results/general.py @@ -0,0 +1,60 @@ +from typing import List, Tuple +from servers.chat.abstractions.contract import ResultBase + + +class CryptResult(ResultBase): + pass + + +class GetKeyResult(ResultBase): + nick_name: str + values: list = [] + + +class ListResult(ResultBase): + user_irc_prefix: str + channel_info_list: List[Tuple[str, int, str]] = [] + """(channel_name:str,total_channel_user:int,channel_topic:str)""" + + +class LoginResult(ResultBase): + profile_id: int + user_id: int + + +class NickResult(ResultBase): + nick_name: str + + +class PingResult(ResultBase): + requester_irc_prefix: str + + +class QuitResult(ResultBase): + quiter_prefix: str + channel_infos: List[Tuple[str, bool, bool, str, str]] + # (channel_name: str, is_peer_server: bool, is_channel_operator: bool,leave_reply_sending_buffer: str,kick_replay_sending_buffer: str) + message: str + + +class UserIPResult(ResultBase): + remote_ip_address: str + + +class WhoIsResult(ResultBase): + nick_name: str + user_name: str + name: str + public_ip_address: str + joined_channel_name: list[str] = [] + + +class WhoResult(ResultBase): + infos: list[tuple[str, str, str, str, str]] + """ + public string ChannelName { get; set; } + public string UserName { get; set; } + public string PublicIPAddress { get; set; } + public string NickName { get; set; } + public string Modes { get; set; } + """ diff --git a/src/servers/chat/contracts/results/message.py b/src/servers/chat/contracts/results/message.py new file mode 100644 index 000000000..eecefd04f --- /dev/null +++ b/src/servers/chat/contracts/results/message.py @@ -0,0 +1,17 @@ +from servers.chat.abstractions.message import MessageResultBase + + +class ATMResult(MessageResultBase): + pass + + +class NoticeResult(MessageResultBase): + pass + + +class PrivateResult(MessageResultBase): + is_broadcast_message: bool = False + + +class UTMResult(MessageResultBase): + pass diff --git a/src/servers/chat/enums/general.py b/src/servers/chat/enums/general.py new file mode 100644 index 000000000..42cbfc5e9 --- /dev/null +++ b/src/servers/chat/enums/general.py @@ -0,0 +1,62 @@ +from enum import IntEnum + + +class LoginRequestType(IntEnum): + UNIQUE_NICK_LOGIN = 1 + NICK_AND_EMAIL_LOGIN = 2 + + +class WhoRequestType(IntEnum): + GET_CHANNEL_USER_INFO = 0 + GET_USER_INFO = 1 + + +class MessageType(IntEnum): + CHANNEL_MESSAGE = 0 + USER_MESSAGE = 1 + + +class GetKeyRequestType(IntEnum): + GET_CHANNEL_ALL_USER_KEY_VALUE = 0 + GET_CHANNEL_SPECIFIC_USER_KEY_VALUE = 1 + + +class ModeOperationType(IntEnum): + ENABLE_USER_QUIET_FLAG = 0 + DISABLE_USER_QUIET_FLAG = 1 + ADD_CHANNEL_PASSWORD = 2 + REMOVE_CHANNEL_PASSWORD = 3 + ADD_CHANNEL_USER_LIMITS = 4 + REMOVE_CHANNEL_USER_LIMITS = 5 + ADD_BAN_ON_USER = 6 + GET_BANNED_USERS = 7 + REMOVE_BAN_ON_USER = 8 + ADD_CHANNEL_OPERATOR = 9 + REMOVE_CHANNEL_OPERATOR = 10 + ENABLE_USER_VOICE_PERMISSION = 11 + DISABLE_USER_VOICE_PERMISSION = 12 + SET_INVITED_ONLY = 13 + REMOVE_INVITED_ONLY = 14 + SET_PRIVATE_CHANNEL_FLAG = 15 + REMOVE_PRIVATE_CHANNEL_FLAG = 16 + SET_SECRET_CHANNEL_FLAG = 17 + REMOVE_SECRET_CHANNEL_FLAG = 18 + SET_MODERATED_CHANNEL_FLAG = 19 + REMOVE_MODERATED_CHANNEL_FLAG = 20 + ENABLE_EXTERNAL_MESSAGES_FLAG = 21 + DISABLE_EXTERNAL_MESSAGES_FLAG = 22 + SET_TOPIC_CHANGE_BY_OPERATOR_FLAG = 23 + REMOVE_TOPIC_CHANGE_BY_OPERATOR_FLAG = 24 + SET_OPERATOR_ABEY_CHANNEL_LIMITS = 25 + REMOVE_OPERATOR_ABEY_CHANNEL_LIMITS = 26 + + +class ModeRequestType(IntEnum): + GET_CHANNEL_MODES = 0 + GET_CHANNEL_AND_USER_MODES = 1 + SET_CHANNEL_MODES = 2 + + +class TopicRequestType(IntEnum): + GET_CHANNEL_TOPIC = 0 + SET_CHANNEL_TOPIC = 1 diff --git a/src/servers/chat/enums/irc_error_code.py b/src/servers/chat/enums/irc_error_code.py new file mode 100644 index 000000000..48d3e7e0c --- /dev/null +++ b/src/servers/chat/enums/irc_error_code.py @@ -0,0 +1,19 @@ +from enum import Enum + + +class IRCErrorCode(Enum): + NO_SUCH_NICK = "401" + NO_SUCH_CHANNEL = "403" + TOO_MANY_CHANNELS = "405" + ERR_ONE_US_NICK_NAME = "432" + NICK_NAME_IN_USE = "433" + MORE_PARAMETERS = "461" + CHANNEL_IS_FULL = "471" + INVITE_ONLY_CHAN = "473" + BANNED_FROM_CHAN = "474" + BAD_CHANNEL_KEY = "475" + BAD_CHAN_MASK = "476" + LOGIN_FAILED = "708" + NO_UNIQUE_NICK = "709" + UNIQUE_NICK_EXPIRED = "710" + REGISTER_NICK_FAILED = "711" diff --git a/src/servers/chat/enums/peer_room.py b/src/servers/chat/enums/peer_room.py new file mode 100644 index 000000000..421fb05a2 --- /dev/null +++ b/src/servers/chat/enums/peer_room.py @@ -0,0 +1,8 @@ +from enum import IntEnum + + +class PeerRoomType(IntEnum): + Group = 0 + Staging = 1 + Title = 2 + Normal = 3 diff --git a/src/servers/chat/exceptions/channel.py b/src/servers/chat/exceptions/channel.py new file mode 100644 index 000000000..2181fcfd8 --- /dev/null +++ b/src/servers/chat/exceptions/channel.py @@ -0,0 +1,44 @@ +from servers.chat.enums.irc_error_code import IRCErrorCode +from servers.chat.exceptions.general import IRCChannelException + + +class BadChannelMaskException(IRCChannelException): + def __init__( + self, message: str, error_code: IRCErrorCode = IRCErrorCode.BAD_CHAN_MASK + ) -> None: + super().__init__(message, error_code) + + +class BadChannelKeyException(IRCChannelException): + def __init__( + self, message: str, error_code: IRCErrorCode = IRCErrorCode.BAD_CHANNEL_KEY + ) -> None: + super().__init__(message, error_code) + + +class BannedFromChanException(IRCChannelException): + def __init__( + self, message: str, error_code: IRCErrorCode = IRCErrorCode.BANNED_FROM_CHAN + ) -> None: + super().__init__(message, error_code) + + +class ChannelIsFullException(IRCChannelException): + def __init__( + self, message: str, error_code: IRCErrorCode = IRCErrorCode.CHANNEL_IS_FULL + ) -> None: + super().__init__(message, error_code) + + +class InviteOnlyChanException(IRCChannelException): + def __init__( + self, message: str, error_code: IRCErrorCode = IRCErrorCode.INVITE_ONLY_CHAN + ) -> None: + super().__init__(message, error_code) + + +class NoSuchChannelException(IRCChannelException): + def __init__( + self, message: str, error_code: IRCErrorCode = IRCErrorCode.NO_SUCH_CHANNEL + ) -> None: + super().__init__(message, error_code) diff --git a/src/servers/chat/exceptions/general.py b/src/servers/chat/exceptions/general.py new file mode 100644 index 000000000..02283fe36 --- /dev/null +++ b/src/servers/chat/exceptions/general.py @@ -0,0 +1,116 @@ +from servers.chat.abstractions.contract import SERVER_DOMAIN +from servers.chat.enums.irc_error_code import IRCErrorCode +import abc + +from library.exceptions.error import UniSpyException as ER + + +class ChatException(ER): + pass + + +class IRCException(ChatException): + error_code: "IRCErrorCode" + sending_buffer: "str" + + def __init__(self, message: "str", error_code: "IRCErrorCode") -> None: + super().__init__(message) + assert isinstance(error_code, IRCErrorCode) + self.error_code = error_code + + @abc.abstractedmethod + def build(self): + pass + + +class IRCChannelException(IRCException): + channel_name: str + + def __init__(self, message: str, error_code: IRCErrorCode) -> None: + super().__init__(message, error_code) + + def build(self): + self.sending_buffer = f":{SERVER_DOMAIN} {self.error_code} * {self.channel_name} :{self.message}\r\n" + + +class ErrOneUSNickNameException(IRCException): + def __init__( + self, + message: "str", + error_code: "IRCErrorCode" = IRCErrorCode.ERR_ONE_US_NICK_NAME, + ) -> None: + super().__init__(message, error_code) + + +class LoginFailedException(IRCException): + def __init__( + self, + message: "str", + error_code: "IRCErrorCode" = IRCErrorCode.LOGIN_FAILED, + ) -> None: + super().__init__(message, error_code) + + +class MoreParametersException(IRCException): + def __init__( + self, + message: "str", + error_code: "IRCErrorCode" = IRCErrorCode.MORE_PARAMETERS, + ) -> None: + super().__init__(message, error_code) + + +class NickNameInUseException(IRCException): + old_nick: str + new_nick: str + + def __init__( + self, + old_nick: str, + new_nick: str, + message: str, + error_code: IRCErrorCode = IRCErrorCode.NICK_NAME_IN_USE, + ) -> None: + super().__init__(message, error_code) + self.old_nick = old_nick + self.new_nick = new_nick + + def build(self): + self.sending_buffer = ( + f"{SERVER_DOMAIN} {self.error_code} {self.old_nick} {self.new_nick} *\r\n" + ) + + +class NoSuchNickException(IRCException): + def __init__( + self, message: str, error_code: IRCErrorCode = IRCErrorCode.NO_SUCH_NICK + ) -> None: + super().__init__(message, error_code) + + +class NoUniqueNickException(IRCException): + def __init__( + self, message: str, error_code: IRCErrorCode = IRCErrorCode.NO_UNIQUE_NICK + ) -> None: + super().__init__(message, error_code) + + +class RegisterNickFaildException(IRCException): + def __init__( + self, message: str, error_code: IRCErrorCode = IRCErrorCode.REGISTER_NICK_FAILED + ) -> None: + super().__init__(message, error_code) + + +class TooManyChannelsException(IRCException): + def __init__( + self, message: str, error_code: IRCErrorCode.TOO_MANY_CHANNELS + ) -> None: + super().__init__(message, error_code) + + +class UniqueNickExpiredException(IRCException): + def __init__( + self, message: str, error_code: IRCErrorCode = IRCErrorCode.UNIQUE_NICK_EXPIRED + ) -> None: + super().__init__(message, error_code) diff --git a/src/servers/chat/handlers/channel.py b/src/servers/chat/handlers/channel.py new file mode 100644 index 000000000..447bc372f --- /dev/null +++ b/src/servers/chat/handlers/channel.py @@ -0,0 +1,182 @@ +from library.abstractions.client import ClientBase +from servers.chat.abstractions.channel import ChannelHandlerBase +from servers.chat.aggregates.response_name import * +from servers.chat.contracts.requests.channel import ( + GetCKeyRequest, + GetChannelKeyRequest, + JoinRequest, + KickRequest, + ModeRequest, + NamesRequest, + PartRequest, + SetCKeyRequest, + SetChannelKeyRequest, + TopicRequest, +) +from servers.chat.contracts.responses.channel import ( + GetCKeyResponse, + JoinResponse, + KickResponse, + ModeResponse, + NamesResponse, + PartResponse, + SetCKeyResponse, + SetChannelKeyResponse, + TopicResponse, +) +from servers.chat.contracts.results.channel import ( + GetCKeyResult, + GetChannelKeyResult, + JoinResult, + KickResult, + ModeResult, + NamesResult, + PartResult, + SetChannelKeyResult, + TopicResult, +) +from servers.chat.enums.general import ModeRequestType, TopicRequestType +from servers.chat.exceptions.general import ChatException + + +class GetChannelKeyHandler(ChannelHandlerBase): + _request: GetChannelKeyRequest + _result: GetChannelKeyResult + + def __init__(self, client: ClientBase, request: GetChannelKeyRequest): + assert isinstance(request, GetChannelKeyRequest) + super().__init__(client, request) + + +class GetCKeyHandler(ChannelHandlerBase): + _request: GetCKeyRequest + _result: GetCKeyResult + + def __init__(self, client: ClientBase, request: GetCKeyRequest): + assert isinstance(request, GetCKeyRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = GetCKeyResponse(self._request, self._result) + + +class JoinHandler(ChannelHandlerBase): + _request: JoinRequest + _result: JoinResult + + def __init__(self, client: ClientBase, request: JoinRequest): + assert isinstance(request, JoinRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = JoinResponse(self._request, self._result) + + +class KickHandler(ChannelHandlerBase): + _request: KickRequest + _result: KickResult + + def __init__(self, client: ClientBase, request: KickRequest): + assert isinstance(request, KickRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = KickResponse(self._request, self._result) + + +class ModeHandler(ChannelHandlerBase): + _request: ModeRequest + _result: ModeResult + + def __init__(self, client: ClientBase, request: ModeRequest): + assert isinstance(request, ModeRequest) + super().__init__(client, request) + + def _response_construct(self): + match self._request.request_type: + case ( + ModeRequestType.GET_CHANNEL_AND_USER_MODES, + ModeRequestType.GET_CHANNEL_MODES, + ): + self._response = ModeResponse(self._request, self._result) + case ModeRequestType.SET_CHANNEL_MODES: + pass + case _: + raise ChatException("Unknown mode request type") + + +class NamesHandler(ChannelHandlerBase): + _request: NamesRequest + _result: NamesResult + + def __init__(self, client: ClientBase, request: NamesRequest): + assert isinstance(request, NamesRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = NamesResponse(self._request, self._result) + + +class PartHandler(ChannelHandlerBase): + _request: PartRequest + _result: PartResult + + def __init__(self, client: ClientBase, request: PartRequest): + assert isinstance(request, PartRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = PartResponse(self._request, self._result) + + def _response_send(self): + self._channel.multicast(self._user.client, self._response, True) + + +class SetChannelKeyHandler(ChannelHandlerBase): + _request: SetChannelKeyRequest + _result: SetChannelKeyResult + + def __init__(self, client: ClientBase, request: SetChannelKeyRequest): + assert isinstance(self._request, SetChannelKeyRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = SetChannelKeyResponse(self._request, self._result) + + def _response_send(self): + self._channel.multicast(self._client, self._response, True) + + +class SetCKeyHandler(ChannelHandlerBase): + _request: SetCKeyRequest + + def __init__(self, client: ClientBase, request: SetCKeyRequest): + assert isinstance(request, SetCKeyRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = SetCKeyResponse(self._request) + + def _response_send(self) -> None: + self._channel.multicast(self._client, self._response) + + +class TopicHandler(ChannelHandlerBase): + _request: TopicRequest + _result: TopicResult + + def __init__(self, client: ClientBase, request: TopicRequest): + assert isinstance(request, TopicRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = TopicResponse(self._request, self._result) + + def _response_send(self) -> None: + match self._request.request_type: + case TopicRequestType.GET_CHANNEL_TOPIC: + super()._response_send() + case TopicRequestType.SET_CHANNEL_TOPIC: + self._channel.multicast(self._client, self._response) + + diff --git a/src/servers/chat/handlers/general.py b/src/servers/chat/handlers/general.py new file mode 100644 index 000000000..b7ce6ebee --- /dev/null +++ b/src/servers/chat/handlers/general.py @@ -0,0 +1,209 @@ +from typing import Type +from library.abstractions.client import ClientBase +from servers.chat.abstractions.contract import RequestBase +from servers.chat.abstractions.handler import CmdHandlerBase, PostLoginHandlerBase +from servers.chat.contracts.requests.channel import GetUdpRelayRequest +from servers.chat.contracts.requests.general import ( + CdkeyRequest, + CryptRequest, + GetKeyRequest, + InviteRequest, + ListRequest, + LoginRequest, + NickRequest, + PingRequest, + QuitRequest, + SetKeyRequest, + UserIPRequest, + WhoIsRequest, + WhoRequest, +) +from servers.chat.contracts.responses.general import ( + CdKeyResponse, + CryptResponse, + GetKeyResponse, + ListResponse, + LoginResponse, + NickResponse, + PingResponse, + UserIPResponse, + WhoIsResponse, + WhoResponse, +) +from servers.chat.contracts.results.general import ( + CryptResult, + GetKeyResult, + ListResult, + LoginResult, + NickResult, + PingResult, + UserIPResult, + WhoIsResult, + WhoResult, +) + + +class CdKeyHandler(CmdHandlerBase): + _request: CdkeyRequest + + def __init__(self, client: ClientBase, request: CdkeyRequest): + assert isinstance(request, CdkeyRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = CdKeyResponse(self._request, self._result) + + +class CryptHandler(CmdHandlerBase): + _request: CryptRequest + _result: CryptResult + + def __init__(self, client: ClientBase, request: RequestBase): + assert isinstance(request, CryptRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = CryptResponse() + + +class GetKeyHandler(CmdHandlerBase): + _request: GetKeyRequest + _result: GetKeyResult + + def __init__(self, client: ClientBase, request: GetKeyRequest): + assert isinstance(request, GetKeyRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = GetKeyResponse(self._request, self._result) + + +class GetUdpRelayHandler(CmdHandlerBase): + _request: GetUdpRelayRequest + + def __init__(self, client: ClientBase, request: GetUdpRelayRequest): + assert isinstance(request, GetUdpRelayRequest) + super().__init__(client, request) + + +class InviteHandler(CmdHandlerBase): + _request: InviteRequest + + def __init__(self, client: ClientBase, request: InviteRequest): + assert isinstance(request, InviteRequest) + super().__init__(client, request) + + +class ListHandler(PostLoginHandlerBase): + _request: ListRequest + _result: ListResult + + def __init__(self, client: ClientBase, request: ListRequest): + assert isinstance(request, ListRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = ListResponse(self._request, self._result) + + +class LoginHandler(CmdHandlerBase): + _request: LoginRequest + _result: LoginResult + + def __init__(self, client: ClientBase, request: LoginRequest): + assert isinstance(request, LoginRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = LoginResponse(self._result) + + +class NickHandler(CmdHandlerBase): + _request: NickRequest + _result: NickResult + + def __init__(self, client: ClientBase, request: NickRequest): + assert isinstance(request, NickRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = NickResponse(self._request, self._result) + + +class PingHandler(CmdHandlerBase): + _request: PingRequest + _result: PingResult + + def __init__(self, client: ClientBase, request: PingRequest): + assert isinstance(request, PingRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = PingResponse(self._result) + + +class QuitHandler(CmdHandlerBase): + _request: QuitRequest + + def __init__(self, client: ClientBase, request: QuitRequest): + assert isinstance(request, QuitHandler) + super().__init__(client, request) + + +class SetKeyHandler(PostLoginHandlerBase): + _request: SetKeyRequest + + def __init__(self, client: ClientBase, request: SetKeyRequest): + assert isinstance(request, SetKeyRequest) + super().__init__(client, request) + + +class UserHandler(CmdHandlerBase): + _request: SetKeyRequest + + def __init__(self, client: ClientBase, request: SetKeyRequest): + assert isinstance(request, SetKeyRequest) + super().__init__(client, request) + + +class UserIPHandler(CmdHandlerBase): + _request: UserIPRequest + _result: UserIPResult + + def __init__(self, client: ClientBase, request: UserIPRequest): + assert isinstance(request, UserIPRequest) + super().__init__(client, request) + + def _request_check(self) -> None: + super()._request_check() + self._request.remote_ip_address = ( + f"{self._client.connection.remote_ip}:{self._client.connection.remote_port}" + ) + + def _response_construct(self) -> None: + self._response = UserIPResponse(self._result) + + +class WhoHandler(CmdHandlerBase): + _request: WhoRequest + _result: WhoResult + + def __init__(self, client: ClientBase, request: WhoRequest): + assert isinstance(request, WhoRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = WhoResponse(self._request, self._result) + + +class WhoIsHandler(CmdHandlerBase): + _request: WhoIsRequest + _result: WhoIsResult + _result_type: Type = WhoIsResult + + def __init__(self, client: ClientBase, request: WhoIsRequest): + assert isinstance(request, WhoIsRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = WhoIsResponse(self._result) diff --git a/src/servers/chat/handlers/message.py b/src/servers/chat/handlers/message.py new file mode 100644 index 000000000..787d8772a --- /dev/null +++ b/src/servers/chat/handlers/message.py @@ -0,0 +1,68 @@ +from library.abstractions.client import ClientBase +from servers.chat.abstractions.message import MessageHandlerBase, MessageRequestBase +from servers.chat.contracts.requests.message import ( + ATMRequest, + NoticeRequest, + PrivateRequest, + UTMRequest, +) +from servers.chat.contracts.responses.message import ( + ATMResponse, + NoticeResponse, + PrivateResponse, + UTMResponse, +) +from servers.chat.contracts.results.message import ( + ATMResult, + NoticeResult, + PrivateResult, + UTMResult, +) + + +class ATMHandler(MessageHandlerBase): + _request: ATMRequest + _result: ATMResult + + def __init__(self, client: ClientBase, request: ATMRequest): + assert isinstance(request, ATMRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = ATMResponse(self._request, self._result) + + +class UTMHandler(MessageHandlerBase): + _request: UTMRequest + _result: UTMResult + + def __init__(self, client: ClientBase, request: UTMRequest): + assert isinstance(request, UTMRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = UTMResponse(self._request, self._result) + + +class NoticeHandler(MessageHandlerBase): + _request: NoticeRequest + _result: NoticeResult + + def __init__(self, client: ClientBase, request: NoticeRequest): + assert isinstance(request, NoticeRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = NoticeResponse(self._request, self._result) + + +class PrivateHandler(MessageHandlerBase): + _request: PrivateRequest + _result: PrivateResult + + def __init__(self, client: ClientBase, request: PrivateRequest): + assert isinstance(request, PrivateRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = PrivateResponse(self._request, self._result) diff --git a/src/servers/game_traffic_relay/applications/connection_listener.py b/src/servers/game_traffic_relay/applications/connection_listener.py new file mode 100644 index 000000000..04af88bf6 --- /dev/null +++ b/src/servers/game_traffic_relay/applications/connection_listener.py @@ -0,0 +1,17 @@ +import socketserver +from library.extentions.string_extentions import IPEndPoint + + +class ConnectionListener: + cookie: bytes + ip_end_point: IPEndPoint + + def __init__(self, ip_end_point: IPEndPoint) -> None: + assert isinstance(ip_end_point, IPEndPoint) + self.ip_end_point = ip_end_point + + def start(self): + with socketserver.ThreadingUDPServer( + self.ip_end_point.ip, self.ip_end_point.port + ) as s: + s.serve_forever() diff --git a/src/servers/game_traffic_relay/applications/router.py b/src/servers/game_traffic_relay/applications/router.py new file mode 100644 index 000000000..8b6a9058d --- /dev/null +++ b/src/servers/game_traffic_relay/applications/router.py @@ -0,0 +1,10 @@ +from flask import Flask, request +from backends.urls import * + +app = Flask(__name__) + + +@app.route(f"/GetNatNegotiationInfo", methods=["POST"]) +async def get_natneg_info(): + data = request.json + \ No newline at end of file diff --git a/src/servers/natneg/abstractions/contracts.py b/src/servers/natneg/abstractions/contracts.py new file mode 100644 index 000000000..2930b6689 --- /dev/null +++ b/src/servers/natneg/abstractions/contracts.py @@ -0,0 +1,86 @@ +import abc +import library.abstractions.contracts +from library.extentions.string_extentions import IPEndPoint +from servers.natneg.enums.general import ( + NatClientIndex, + NatPortType, + RequestType, + ResponseType, +) + +MAGIC_DATA = bytes([0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2]) + + +class RequestBase(library.abstractions.contracts.RequestBase): + version: int + cookie: bytes + port_type: NatPortType + command_name: bytes + raw_request: bytes + + def __init__(self, raw_request: bytes = None): + assert isinstance(raw_request, bytes) + self.raw_request = raw_request + + def parse(self) -> None: + if len(self.raw_request) < 12: + return + + self.version = self.raw_request[6] + self.command_name = RequestType(self.raw_request[7]) + self.cookie = self.raw_request[8:12] + self.port_type = NatPortType(self.raw_request[12]) + + +class ResultBase(library.abstractions.contracts.ResultBase): + packet_type: ResponseType + pass + + +class ResponseBase(library.abstractions.contracts.ResponseBase): + _request: RequestBase + _result: ResultBase + sending_buffer: bytes + + def __init__(self, request: RequestBase, result: ResultBase) -> None: + super().__init__(request, result) + assert issubclass(request, RequestBase) + assert issubclass(result, ResultBase) + + def build(self) -> None: + data = bytes() + data += MAGIC_DATA + data += self._request.version.to_bytes(1, "little") + data += int(self._result.packet_type).to_bytes(1, "little") + data += self._request.cookie + self.sending_buffer = data + + +class CommonRequestBase(RequestBase): + client_index: NatClientIndex + use_game_port: bool + + def parse(self): + super().parse() + self.client_index = NatClientIndex(self.raw_request[13]) + self.use_game_port = bool(self.raw_request[14]) + + +class CommonResultBase(ResultBase, abc.ABC): + ip_endpoint: IPEndPoint + + +class CommonResponseBase(ResponseBase): + _result: CommonResultBase + _request: CommonRequestBase + + def build(self) -> None: + super().build() + data = bytes() + data += self.sending_buffer + data += int(self._request.port_type).to_bytes(1, "little") + data += int(self._request.client_index).to_bytes(1, "little") + data += bytes(self._request.use_game_port) + data += self._result.ip_endpoint.get_ip_bytes() + data += self._result.ip_endpoint.get_port_bytes() + self.sending_buffer = data diff --git a/src/servers/natneg/abstractions/handlers.py b/src/servers/natneg/abstractions/handlers.py new file mode 100644 index 000000000..d07b62feb --- /dev/null +++ b/src/servers/natneg/abstractions/handlers.py @@ -0,0 +1,16 @@ +import abc +from servers.natneg.applications.client import Client +import library.abstractions.handler +from servers.natneg.abstractions.contracts import RequestBase + + +class CmdHandlerBase(library.abstractions.handler.CmdHandlerBase, abc.ABC): + def __init__(self, client: Client, request: RequestBase) -> None: + super().__init__(client, request) + assert isinstance(client, Client) + assert issubclass(request, RequestBase) + + +if __name__ == "__main__": + cmd = CmdHandlerBase(None, None) + pass diff --git a/src/servers/natneg/aggregations/init_packet_info.py b/src/servers/natneg/aggregations/init_packet_info.py new file mode 100644 index 000000000..9cae60973 --- /dev/null +++ b/src/servers/natneg/aggregations/init_packet_info.py @@ -0,0 +1,33 @@ +from mongoengine import ( + Document, + StringField, + IntField, + UUIDField, + EnumField, + BooleanField, +) + +from servers.natneg.enums.general import NatClientIndex, NatPortType +import datetime + + +class InitPacketInfo(Document): + server_id = UUIDField(binary=False, required=True) + cookie = IntField(required=True) + version = IntField(required=True) + port_type = EnumField(NatPortType, required=True) + client_index = EnumField(NatClientIndex, required=True) + game_name = StringField(required=True) + use_game_port = BooleanField(required=True) + public_ip = StringField(required=True) + public_port = IntField(required=True) + private_ip = StringField(required=True) + private_port = IntField(required=True) + meta = {"expireAfterSeconds": int(datetime.timedelta(minutes=3).total_seconds())} + + +class NatFailInfo(Document): + public_ip_address1 = StringField(required=True) + public_ip_address2 = StringField(required=True) + meta = {"expireAfterSeconds": int(datetime.timedelta(days=1).total_seconds())} + """expire after 1 day""" diff --git a/src/servers/natneg/aggregations/relay_server_info.py b/src/servers/natneg/aggregations/relay_server_info.py new file mode 100644 index 000000000..118633004 --- /dev/null +++ b/src/servers/natneg/aggregations/relay_server_info.py @@ -0,0 +1,11 @@ +from mongoengine import Document, StringField, IntField, UUIDField + + +class RelayServerInfo(Document): + server_id = UUIDField(binary=False, required=True) + public_ip_address = StringField(required=True) + public_port = IntField(required=True) + client_count = IntField(required=True) + # report how many client are connect to this relay server + + diff --git a/src/servers/natneg/applications/client.py b/src/servers/natneg/applications/client.py new file mode 100644 index 000000000..325f7b980 --- /dev/null +++ b/src/servers/natneg/applications/client.py @@ -0,0 +1,14 @@ +from library.abstractions.client import ClientBase +from library.log.log_manager import LogWriter +from library.unispy_server_config import ServerConfig + + +class Client(ClientBase): + def __init__(self, connection, server_config: ServerConfig, logger: LogWriter): + super().__init__(connection, server_config, logger) + self.is_log_raw = True + + def create_switcher(self, buffer: bytes): + # return super().create_switcher(buffer) + assert isinstance(buffer, bytes) + return diff --git a/src/servers/natneg/contracts/requests.py b/src/servers/natneg/contracts/requests.py new file mode 100644 index 000000000..2a23a1862 --- /dev/null +++ b/src/servers/natneg/contracts/requests.py @@ -0,0 +1,92 @@ +from socket import inet_ntoa +import struct +from library.extentions.string_extentions import IPEndPoint +from servers.natneg.abstractions.contracts import CommonRequestBase, RequestBase +from servers.natneg.enums.general import ( + NatClientIndex, + NatPortMappingScheme, + NatPortType, + NatType, + PreInitState, + RequestType, +) + + +class AddressCheckRequest(CommonRequestBase): + pass + + +class ConnectAckRequest(RequestBase): + client_index: NatClientIndex + + def parse(self) -> None: + super().parse() + self.client_index = NatClientIndex(self.raw_request[13]) + + +class ConnectRequest(RequestBase): + """ + Server will send this request to client to let them connect to each other + """ + + client_index: NatClientIndex + + +class ErtAckRequest(CommonRequestBase): + pass + + +class InitRequest(CommonRequestBase): + def __init__(self, raw_request: bytes) -> None: + super().__init__(raw_request) + self.game_name = None + self.private_ip_endpoint = None + + def parse(self) -> None: + super().parse() + ip_bytes = self.raw_request[15:19] + port_bytes = self.raw_request[19:21][::-1] + port = struct.unpack("H", port_bytes)[0] + ip_address_str = inet_ntoa(ip_bytes) + self.private_ip_endpoint = IPEndPoint(ip_address_str, port) + + if len(self.raw_request) > 21 and self.raw_request[-1] == 0: + game_name_bytes = self.raw_request[21:-1] + self.game_name = game_name_bytes.decode("ascii") + + +class NatifyRequest(CommonRequestBase): + pass + + +class PreInitRequest(RequestBase): + state: PreInitState + target_cookie: bytes + + def parse(self) -> None: + super().parse() + self.state = PreInitState(self.raw_request[12]) + self.target_cookie = int.from_bytes(self.raw_request[13:17], byteorder="big") + + +class ReportRequest(CommonRequestBase): + is_nat_success: bool = None + game_name: str + nat_type: NatType + mapping_scheme: NatPortMappingScheme + + def parse(self): + super().parse() + if len(self.raw_request) < 12: + return + self.version = self.raw_request[6] + self.command_name = RequestType(self.raw_request[7]) + self.cookie = int.from_bytes(self.raw_request[8:12], byteorder="big") + self.port_type = NatPortType(self.raw_request[12]) + self.client_index = NatClientIndex(self.raw_request[13]) + self.is_nat_success = False if self.raw_request[14] == 0 else True + self.nat_type = NatType(self.raw_request[15]) + self.mapping_scheme = NatPortMappingScheme(self.raw_request[17]) + + end_index = self.raw_request[23:].index(0) + self.game_name = self.raw_request[23 : 23 + end_index].decode("ascii") diff --git a/src/servers/natneg/contracts/responses.py b/src/servers/natneg/contracts/responses.py new file mode 100644 index 000000000..46d5d97b5 --- /dev/null +++ b/src/servers/natneg/contracts/responses.py @@ -0,0 +1,12 @@ +from servers.natneg.abstractions.contracts import ( + CommonResponseBase, + RequestBase, + ResultBase, +) + + +class InitResponse(CommonResponseBase): + def __init__(self, request: RequestBase, result: ResultBase) -> None: + super().__init__(request, result) + assert issubclass(request, RequestBase) + assert issubclass(result, ResultBase) diff --git a/src/servers/natneg/contracts/results.py b/src/servers/natneg/contracts/results.py new file mode 100644 index 000000000..21a8afb21 --- /dev/null +++ b/src/servers/natneg/contracts/results.py @@ -0,0 +1,48 @@ +from servers.natneg.abstractions.contracts import CommonResultBase, ResultBase +from servers.natneg.enums.general import ConnectPacketStatus, PreInitState, ResponseType + + +class AddressCheckResult(CommonResultBase): + packet_type: ResponseType = bytes(ResponseType.ADDRESS_REPLY) + + +class ConnectResult(ResultBase): + got_your_data: bytes = bytes([1]) + finished: ConnectPacketStatus = ConnectPacketStatus.NO_ERROR + ip: str + port: int + version: bytes + cookie: bytes + + def __init__(self) -> None: + super().__init__() + self.packet_type = ResponseType.CONNECT + + +class ErtAckResult(CommonResultBase): + def __init__(self) -> None: + super().__init__() + self.packet_type = ResponseType.ERT_ACK + + +class InitResult(CommonResultBase): + packet_type: ResponseType = ResponseType.INIT_ACK + + +class NatifyResult(CommonResultBase): + packet_type: ResponseType = ResponseType.ERT_TEST + + +class PreInitResult(ResultBase): + client_index: int + state: PreInitState + client_id: int + + def __init__(self) -> None: + super().__init__() + self.packet_type = ResponseType.PRE_INIT_ACK + self.state = PreInitState.READY + + +class ReportResult(ResultBase): + packet_type = ResponseType.REPORT_ACK diff --git a/src/servers/natneg/enums/general.py b/src/servers/natneg/enums/general.py new file mode 100644 index 000000000..e2281fbd3 --- /dev/null +++ b/src/servers/natneg/enums/general.py @@ -0,0 +1,73 @@ +from enum import Enum + + +class RequestType(Enum): + INIT = 0 + ERT_ACK = 3 + CONNECT = 5 + CONNECT_ACK = 6 + PING = 7 + ADDRESS_CHECK = 10 + NATIFY_REQUEST = 12 + REPORT = 13 + PRE_INIT = 15 + + +class NatPortType(Enum): + GP = 0 + NN1 = 1 + NN2 = 2 + NN3 = 3 + + +class ResponseType(Enum): + INIT_ACK = 1 + ERT_TEST = 2 + ERT_ACK = 3 + CONNECT = 5 + ADDRESS_REPLY = 11 + REPORT_ACK = 14 + PRE_INIT_ACK = 16 + + +class ConnectPacketStatus(Enum): + NO_ERROR = 0 + BEAD_HEART_BEAT = 1 + INIT_PACKET_TIMEOUT = 2 + + +class NatifyPacketType(Enum): + PACKET_MAP_1A = 0 + PACKET_MAP_2 = 1 + PACKET_MAP_3 = 2 + PACKET_MAP_1B = 3 + NUM_PACKETS = 4 + + +class PreInitState(Enum): + WAITING_FOR_CLIENT = 0 + WAITING_FOR_MATCH_UP = 1 + READY = 2 + + +class NatType(Enum): + NO_NAT = 0 + FIREWALL_ONLY = 1 + FULL_CONE = 2 + ADDRESS_RESTRICTED_CONE = 3 + PORT_RESTRICTED_CONE = 4 + SYMMETRIC = 5 + UNKNOWN = 6 + + +class NatPortMappingScheme(Enum): + UNRECOGNIZED = 0 + PRIVATE_AS_PUBLIC = 1 + CONSISTENT_PORT = 2 + INCREMENTAL = 3 + MIXED = 4 + + +class NatClientIndex(Enum): + GAME_CLIENT = 0 + GAME_SERVER = 1 diff --git a/src/servers/natneg/handlers/handlers.py b/src/servers/natneg/handlers/handlers.py new file mode 100644 index 000000000..a7f32eaac --- /dev/null +++ b/src/servers/natneg/handlers/handlers.py @@ -0,0 +1,112 @@ +from library.abstractions.contracts import RequestBase +from library.extentions.string_extentions import IPEndPoint +from servers.natneg.abstractions.handlers import CmdHandlerBase +from servers.natneg.applications.client import Client +from servers.natneg.contracts.requests import AddressCheckRequest, ConnectAckRequest, ConnectRequest, ErtAckRequest, InitRequest, NatifyRequest, ReportRequest +from servers.natneg.contracts.responses import InitResponse +from servers.natneg.contracts.results import AddressCheckResult, ConnectResult, ErtAckResult, InitResult, NatifyResult, ReportResult + + +class AddressCheckHandler(CmdHandlerBase): + _request: AddressCheckRequest + _result: AddressCheckResult = AddressCheckResult() + _response: InitResponse + + def __init__(self, client: Client, request: AddressCheckRequest) -> None: + super().__init__(client, request) + assert isinstance(client, Client) + assert isinstance(request, AddressCheckRequest) + + def _data_operate(self) -> None: + self._result.ip_endpoint = IPEndPoint( + self._client.connection.ip, self._client.connection.port + ) + + def _response_construct(self) -> None: + self._response = InitResponse(self._request, self._result) + + +class ConnectAckHandler(CmdHandlerBase): + _request: ConnectAckRequest + + def _data_operate(self) -> None: + self._client.log_info( + f"client:{self._request.client_index} aknowledged connect request." + ) + + +class ConnectHandler(CmdHandlerBase): + _request: ConnectRequest + _result: ConnectResult + + def __init__(self, client: Client, request: ConnectRequest) -> None: + super().__init__(client, request) + assert isinstance(request, ConnectRequest) + + +class ErtAckHandler(CmdHandlerBase): + _request: ErtAckRequest + _result: ErtAckResult = ErtAckResult() + _response: InitResponse + + def __init__(self, client: Client, request: ErtAckRequest) -> None: + super().__init__(client, request) + assert isinstance(request, ErtAckRequest) + + def _data_operate(self) -> None: + self._result.ip_endpoint = IPEndPoint( + self._client.connection.ip, self._client.connection.port + ) + + def _response_construct(self) -> None: + self._response = InitResponse(self._request, self._result) + + +class InitHandler(CmdHandlerBase): + """ + In init process, we need response the initresponse first to make client not timeout + """ + + _request: InitRequest + _result: InitResult + _response: InitResponse + + def __init__(self, client: Client, request: InitRequest) -> None: + super().__init__(client, request) + assert isinstance(request, InitRequest) + + def _response_construct(self): + self._response = InitResponse(self._request, self._result) + + +class NatifyHandler(CmdHandlerBase): + _request: NatifyRequest + _result: NatifyResult = NatifyResult() + _response: InitResponse + + def __init__(self, client: Client, request: NatifyRequest) -> None: + super().__init__(client, request) + assert isinstance(request, NatifyRequest) + + def _data_operate(self): + self._result.ip_endpoint = IPEndPoint( + self._client.connection.ip, self._client.connection.port + ) + + def _response_construct(self): + self._response = InitResponse(self._request, self._result) + + +class PingHandler(CmdHandlerBase): + def __init__(self, client: Client, request: RequestBase) -> None: + super().__init__(client, request) + raise NotImplementedError() + + +class ReportHandler(CmdHandlerBase): + _request: ReportRequest + _result: ReportResult + + def __init__(self, client: Client, request: ReportRequest) -> None: + super().__init__(client, request) + assert isinstance(request, ReportRequest) diff --git a/src/servers/natneg/handlers/switcher.py b/src/servers/natneg/handlers/switcher.py new file mode 100644 index 000000000..a23ee4bc8 --- /dev/null +++ b/src/servers/natneg/handlers/switcher.py @@ -0,0 +1,20 @@ +from library.abstractions.switcher import SwitcherBase +from servers.natneg.applications.client import Client + + +class CmdSwitcher(SwitcherBase): + _rawRequest: bytes + _client: Client + + def __init__(self, client: Client, rawRequest: bytes) -> None: + super().__init__(client, rawRequest) + assert issubclass(client, Client) + assert isinstance(rawRequest, bytes) + + def _process_raw_request(self) -> None: + name = self._rawRequest[7] + self._requests.append((name, self._rawRequest)) + + def _create_cmd_handlers(self, name: int, rawRequest: bytes) -> None: + assert isinstance(name, int) + assert isinstance(rawRequest, bytes) diff --git a/src/servers/natneg/tests/redis.py b/src/servers/natneg/tests/redis.py new file mode 100644 index 000000000..ff50b72a2 --- /dev/null +++ b/src/servers/natneg/tests/redis.py @@ -0,0 +1,37 @@ +import datetime +from typing import Optional + +from pydantic.v1 import EmailStr + +from redis_om import Field, HashModel, Migrator + + +class Customer(HashModel): + first_name: str + last_name: str = Field(index=True) + email: EmailStr + join_date: datetime.date + age: int = Field(index=True) + bio: Optional[str] + + +# Now, if we use this model with a Redis deployment that has the +# RediSearch module installed, we can run queries like the following. + +# Before running queries, we need to run migrations to set up the +# indexes that Redis OM will use. You can also use the `migrate` +# CLI tool for this! +Migrator().run() + +# Find all customers with the last name "Brookins" +Customer.find(Customer.last_name == "Brookins").all() + +# Find all customers that do NOT have the last name "Brookins" +Customer.find(Customer.last_name != "Brookins").all() + +# Find all customers whose last name is "Brookins" OR whose age is +# 100 AND whose last name is "Smith" +Customer.find( + (Customer.last_name == "Brookins") + | (Customer.age == 100) & (Customer.last_name == "Smith") +).all() diff --git a/src/servers/natneg/tests/tests.py b/src/servers/natneg/tests/tests.py new file mode 100644 index 000000000..7477add84 --- /dev/null +++ b/src/servers/natneg/tests/tests.py @@ -0,0 +1,118 @@ +import unittest +from library.extentions.bytes_extentions import int_to_bytes +from servers.natneg.contracts.requests import ( + AddressCheckRequest, + ErtAckRequest, + InitRequest, + NatifyRequest, + PreInitRequest, +) +from servers.natneg.enums.general import ( + NatClientIndex, + NatPortType, + PreInitState, + RequestType, +) + + +class UnitTests(unittest.TestCase): + def test_init(self) -> None: + raw = bytes( + [ + 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, + 0x00, + 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + ) # fmt: skip + req = InitRequest(raw) + req.parse() + cookie = 151191552 + self.assertEqual(cookie.to_bytes(4, "little"), req.cookie) + self.assertEqual(RequestType.INIT, req.command_name) + self.assertEqual(NatClientIndex.GAME_CLIENT, req.client_index) + self.assertEqual(False, req.use_game_port) + self.assertEqual(3, req.version) + self.assertEqual(NatPortType.NN1, req.port_type) + + def test_address_check(self) -> None: + raw = bytes( + [ + 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, 0x0a, 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + ) # fmt: skip + + req = AddressCheckRequest(raw) + req.parse() + cookie = 151191552 + self.assertEqual(cookie.to_bytes(4, "little"), req.cookie) + self.assertEqual(RequestType.ADDRESS_CHECK, req.command_name) + self.assertEqual(NatClientIndex.GAME_CLIENT, req.client_index) + self.assertEqual(False, req.use_game_port) + self.assertEqual(3, req.version) + self.assertEqual(NatPortType.NN1, req.port_type) + + def test_ert_ack(self) -> None: + raw = bytes( + [ + 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, + 0x03, + 0x00, 0x00, 0x03, 0x09, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + ) # fmt: skip + req = ErtAckRequest(raw) + req.parse() + cookie = 151191552 + self.assertEqual(cookie.to_bytes(4, "little"), req.cookie) + self.assertEqual(RequestType.ERT_ACK, req.command_name) + self.assertEqual(NatClientIndex.GAME_CLIENT, req.client_index) + self.assertEqual(3, req.version) + self.assertEqual(False, req.use_game_port) + self.assertEqual(NatPortType.NN1, req.port_type) + + def test_nattify(self) -> None: + raw = bytes( + [ + 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, + 0x0c, + 0x00, 0x00, 0x03, 0x09, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + ) # fmt: skip + req = NatifyRequest(raw) + req.parse() + cookie = 151191552 + self.assertEqual(cookie.to_bytes(4, "little"), req.cookie) + self.assertEqual(RequestType.NATIFY_REQUEST, req.command_name) + self.assertEqual(NatClientIndex.GAME_CLIENT, req.client_index) + self.assertEqual(3, req.version) + self.assertEqual(False, req.use_game_port) + self.assertEqual(NatPortType.NN1, req.port_type) + + def test_preinit(self) -> None: + + raw = bytes( + [ + 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x04, 0x0f, 0xb5, 0xe0, 0x95, 0x2a, 0x00, 0x24, 0x38, 0xb2, 0xb3, 0x5e + ] + ) # fmt: skip + + req = PreInitRequest(raw) + req.parse() + b_cookie = bytes( + [ + 0xB5, + 0xE0, + 0x95, + 0x2A, + ] + ) + self.assertEqual(b_cookie, req.cookie) + self.assertEqual(RequestType.PRE_INIT, req.command_name) + self.assertEqual(4, req.version) + self.assertEqual(NatPortType.GP, req.port_type) + self.assertEqual(PreInitState.WAITING_FOR_CLIENT, req.state) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/servers/presence_connection_manager/abstractions/contracts.py b/src/servers/presence_connection_manager/abstractions/contracts.py new file mode 100644 index 000000000..fa6357ed9 --- /dev/null +++ b/src/servers/presence_connection_manager/abstractions/contracts.py @@ -0,0 +1,56 @@ +import abc + +import library.abstractions +import library.abstractions.contracts +from library.extentions.gamespy_utils import convert_to_key_value +from servers.presence_search_player.exceptions.general import ( + GPParseException, +) +from typing import Dict + +import library.abstractions.contracts + + +def normalize_request(message: str): + if "login" in message: + message = message.replace("\\-", "\\") + pos = message.index("\\", message.index("\\") + 1) + if message[pos : pos + 2] != "\\\\": + message = message[:pos] + "\\" + message[pos:] + return message + + +class RequestBase(library.abstractions.contracts.RequestBase, abc.ABC): + command_name: str + operation_id: int + raw_request: str + request_key_values: Dict[str, str] + + def __init__(self, raw_request: "str") -> None: + assert isinstance(raw_request, str) + super().__init__(raw_request) + + def parse(self): + super().parse() + self.request_key_values = convert_to_key_value(self.raw_request) + self.command_name = list(self.request_key_values.keys())[0] + + if "id" in self.request_key_values: + self.operation_id = int(self.request_key_values["id"]) + else: + raise GPParseException("namespaceid is invalid.") + + +class ResultBase(library.abstractions.contracts.ResultBase): + pass + + +class ResponseBase(library.abstractions.contracts.ResponseBase, abc.ABC): + _request: RequestBase + _result: ResultBase + sending_buffer: str + + def __init__(self, request: RequestBase, result: ResultBase) -> None: + assert issubclass(type(request), RequestBase) + assert issubclass(type(result), ResultBase) + super().__init__(request, result) diff --git a/src/servers/presence_connection_manager/abstractions/handler.py b/src/servers/presence_connection_manager/abstractions/handler.py new file mode 100644 index 000000000..37908d109 --- /dev/null +++ b/src/servers/presence_connection_manager/abstractions/handler.py @@ -0,0 +1,35 @@ +import abc + +from servers.presence_connection_manager.applications.client import Client +from servers.presence_connection_manager.enums.general import LoginStatus +from servers.presence_search_player.exceptions.general import GPException + +from servers.presence_connection_manager.abstractions.contracts import ( + RequestBase, + ResultBase, +) +import library + + +class CmdHandlerBase(library.abstractions.handler.CmdHandlerBase): + _client: Client + _request: RequestBase + _result: ResultBase + + def __init__(self, client: Client, request: RequestBase) -> None: + assert isinstance(client, Client) + assert issubclass(type(request), RequestBase) + super().__init__(client, request) + + def _handle_exception(self, ex) -> None: + if ex is GPException: + self._client.send(ex) + super()._handle_exception(ex) + + +class LoginHandlerBase(CmdHandlerBase, abc.ABC): + + def _request_check(self) -> None: + if self._client.info.login_status != LoginStatus.COMPLETED: + raise GPException("please login first.") + super()._request_check() diff --git a/src/servers/presence_connection_manager/aggregates/login_challenge.py b/src/servers/presence_connection_manager/aggregates/login_challenge.py new file mode 100644 index 000000000..c768d054b --- /dev/null +++ b/src/servers/presence_connection_manager/aggregates/login_challenge.py @@ -0,0 +1,35 @@ +import hashlib + +from servers.presence_connection_manager.enums.general import GPPartnerId, LoginType + +SERVER_CHALLENGE = "0000000000" + + +class LoginChallengeProof: + + def __init__( + self, userData, loginType, partnerID, challenge1, challenge2, passwordHash + ): + self.userData = userData + self.loginType = loginType + self.partnerID = partnerID + self.challenge1 = challenge1 + self.challenge2 = challenge2 + self.passwordHash = passwordHash + + +@staticmethod +def generate_proof(data: LoginChallengeProof): + tempUserData = data.userData + + if data.partnerID is not None: + if ( + data.partnerID != GPPartnerId.GAMESPY.value + and data.loginType != LoginType.AUTH_TOKEN + ): + tempUserData = f"{data.partnerID}@{data.userData}" + + responseString = f"{data.passwordHash} {' ' * 48}{tempUserData}{data.challenge1}{data.challenge2}{data.passwordHash}" + hashString = hashlib.md5(responseString.encode()).hexdigest() + + return hashString diff --git a/src/servers/presence_connection_manager/aggregates/sdk_revision.py b/src/servers/presence_connection_manager/aggregates/sdk_revision.py new file mode 100644 index 000000000..d68f6226c --- /dev/null +++ b/src/servers/presence_connection_manager/aggregates/sdk_revision.py @@ -0,0 +1,59 @@ +from servers.presence_connection_manager.enums.general import SdkRevisionType + + +class SdkRevision: + def __init__(self, sdk_type: SdkRevisionType): + assert isinstance(sdk_type, SdkRevisionType) + self.sdk_type = sdk_type + + @property + def is_sdk_revision_valid(self): + return False if self.sdk_type == 0 else True + + @property + def is_support_gpi_new_auth_notification(self): + return ( + True + if (self.sdk_type ^ SdkRevisionType.GPINEW_AUTH_NOTIFICATION) != 0 + else False + ) + + @property + def is_support_gpi_new_revoke_notification(self): + return ( + True + if (self.sdk_type ^ SdkRevisionType.GPINEW_REVOKE_NOTIFICATION) != 0 + else False + ) + + @property + def is_support_gpi_new_status_notification(self): + return ( + True + if (self.sdk_type ^ SdkRevisionType.GPINEW_STATUS_NOTIFICATION) != 0 + else False + ) + + @property + def is_support_gpi_new_list_retreval_on_login(self): + return ( + True + if (self.sdk_type ^ SdkRevisionType.GPINEW_LIST_RETRIEVAL_ON_LOGIN) != 0 + else False + ) + + @property + def is_support_gpi_remote_auth_ids_notification(self): + return ( + True + if (self.sdk_type ^ SdkRevisionType.GPIREMOTE_AUTH_IDS_NOTIFICATION) != 0 + else False + ) + + @property + def is_support_gpi_new_cdkey_registration(self): + return ( + True + if (self.sdk_type ^ SdkRevisionType.GPINEW_CD_KEY_REGISTRATION) != 0 + else False + ) diff --git a/src/servers/presence_connection_manager/aggregates/user_status.py b/src/servers/presence_connection_manager/aggregates/user_status.py new file mode 100644 index 000000000..fc171c48d --- /dev/null +++ b/src/servers/presence_connection_manager/aggregates/user_status.py @@ -0,0 +1,10 @@ +from dataclasses import dataclass +from typing import Union +from servers.presence_connection_manager.enums.general import GPStatusCode + + +@dataclass +class UserStatus: + status_string: str + location_string: str + current_status: Union[GPStatusCode, int] = GPStatusCode.OFFLINE diff --git a/src/servers/presence_connection_manager/aggregates/user_status_info.py b/src/servers/presence_connection_manager/aggregates/user_status_info.py new file mode 100644 index 000000000..5a6d03416 --- /dev/null +++ b/src/servers/presence_connection_manager/aggregates/user_status_info.py @@ -0,0 +1,17 @@ +from dataclasses import dataclass + + +@dataclass +class UserStatusInfo: + status_state: str + buddy_ip: str + host_ip: str + host_private_ip: str + query_report_port: int + host_port: int + session_flags: str + rich_status: str + game_type: str + game_variant: str + game_map_name: str + quiet_mode_flags: str diff --git a/src/servers/presence_connection_manager/applications/client.py b/src/servers/presence_connection_manager/applications/client.py new file mode 100644 index 000000000..f925fd949 --- /dev/null +++ b/src/servers/presence_connection_manager/applications/client.py @@ -0,0 +1,57 @@ +from library.abstractions.client import ClientBase, ClientInfoBase + +from library.abstractions.connections import TcpConnectionBase +from library.abstractions.switcher import SwitcherBase +from library.log.log_manager import LogWriter +from library.unispy_server_config import ServerConfig +from servers.presence_connection_manager.aggregates.login_challenge import ( + SERVER_CHALLENGE, +) +from servers.presence_connection_manager.handlers.switcher import Switcher +from servers.presence_connection_manager.enums.general import LoginStatus + +from typing import TYPE_CHECKING + +from servers.presence_connection_manager.aggregates.sdk_revision import SdkRevision +from servers.presence_connection_manager.enums.general import LoginStatus + +LOGIN_TICKET = "0000000000000000000000__" +SESSION_KEY = 1111 + + +class ClientInfo(ClientInfoBase): + user_id: int + profile_id: int + sub_profile_id: int + login_status: LoginStatus = LoginStatus.CONNECTED + namespace_id: int + sdk_revision: SdkRevision + + +class Client(ClientBase): + info: ClientInfo + connection: TcpConnectionBase + + def __init__( + self, + connection: TcpConnectionBase, + server_config: ServerConfig, + logger: LogWriter, + ): + super().__init__(connection, server_config, logger) + self.info = ClientInfo() + + def on_connected(self) -> None: + super().on_connected() + if self.info.login_status != LoginStatus.CONNECTED: + self.connection.disconnect() + self.log_warn( + "The server challenge has already been sent. Cannot send another login challenge." + ) + self.info.login_status = LoginStatus.PROCESSING + buffer = f"\\lc\\1\\challenge\\{SERVER_CHALLENGE}\\id\\1\\final\\" + self.send(buffer) + self.log_network_sending(buffer) + + def create_switcher(self, buffer) -> "SwitcherBase": + return Switcher(self, buffer) diff --git a/src/servers/presence_connection_manager/applications/data.py b/src/servers/presence_connection_manager/applications/data.py new file mode 100644 index 000000000..7fd44225d --- /dev/null +++ b/src/servers/presence_connection_manager/applications/data.py @@ -0,0 +1,169 @@ +from sqlalchemy import insert +from library.database.pg_orm import ( + Blocked, + Friends, + Profiles, + SubProfiles, + Users, + PG_SESSION, +) +from servers.presence_search_player.exceptions.general import GPDatabaseException + + +def is_email_exist(email: str) -> bool: + if PG_SESSION.query(Users).filter(Users.email == email).count() == 1: + return True + else: + return False + + +def delete_friend_by_profile_id(profile_id: int): + friend = PG_SESSION.query(Friends).filter(Friends.friendid == profile_id).first() + if friend is None: + raise GPDatabaseException( + f"friend deletion have errors on profile id:{profile_id}" + ) + else: + PG_SESSION.delete(friend) + PG_SESSION.commit() + + +def get_blocked_profile_id_list(profile_id: int, namespace_id: int) -> list[int]: + result = ( + PG_SESSION.query(Blocked.targetid) + .filter(Blocked.profileid == profile_id, Blocked.namespaceid == namespace_id) + .all() + ) + return result + + +def get_friend_profile_id_list(profile_id: int, namespace_id: int) -> list[int]: + result = ( + PG_SESSION.query(Friends.targetid) + .filter(Friends.profileid == profile_id, Friends.namespaceid == namespace_id) + .all() + ) + return result + + +def get_profile_info_list(profile_id: int, namespace_id: int): + result = ( + PG_SESSION.query(Profiles, SubProfiles, Users) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .join(Users, Profiles.userid == Users.userid) + .filter( + Profiles.profileid == profile_id, SubProfiles.namespaceid == namespace_id + ) + .first() + ) + return result + + +def get_user_info_list(email: str, nick_name: str) -> list[tuple[int, int, int]]: + """ + return (userid, profileid, subprofileid) + """ + result = ( + PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter(Users.email == email, Profiles.nick == nick_name) + .all() + ) + return result + + +def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: + result = ( + PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter( + SubProfiles.uniquenick == unique_nick, + SubProfiles.namespaceid == namespace_id, + ) + .first() + ) + return result + + +def get_user_infos(unique_nick: str, namespace_id: int) -> list[tuple[int, int, int]]: + result = ( + PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter( + SubProfiles.uniquenick == unique_nick, + SubProfiles.namespaceid == namespace_id, + ) + .all() + ) + return result + + +def update_block_info_list(target_id: int, profile_id: int, namespace_id: int) -> None: + result = ( + PG_SESSION.query(Blocked) + .filter( + Blocked.targetid == target_id, + Blocked.namespaceid == namespace_id, + Blocked.profileid == profile_id, + ) + .count() + ) + if result == 0: + b = Blocked(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) + PG_SESSION.add(b) + PG_SESSION.commit() + + +def update_friend_info(target_id: int, profile_id: int, namespace_id: int): + result = ( + PG_SESSION.query(Friends) + .filter( + Friends.targetid == target_id, + Friends.namespaceid == namespace_id, + Friends.profileid == profile_id, + ) + .count() + ) + f = Friends(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) + + if result == 0: + PG_SESSION.add(f) + PG_SESSION.commit() + + +def add_nick_name(profile_id: int, old_nick: str, new_nick: str): + + result = ( + PG_SESSION.query(Profiles) + .filter(Profiles.profileid == profile_id, Profiles.nick == old_nick) + .first() + ) + + if result is None: + raise GPDatabaseException("No user infomation found in database.") + + result.nick = new_nick + PG_SESSION.commit() + + +def update_profile_info(profile: Profiles): + PG_SESSION.add(profile) + PG_SESSION.commit() + + +def update_unique_nick(subprofile_id: int, unique_nick: str): + result = ( + PG_SESSION.query(SubProfiles) + .filter(SubProfiles.subprofileid == subprofile_id) + .first() + ) + result.uniquenick = unique_nick + PG_SESSION.commit() + + +def update_subprofile_info(subprofile: SubProfiles): + PG_SESSION.add(subprofile) + PG_SESSION.commit() diff --git a/src/servers/presence_connection_manager/contracts/requests/buddy.py b/src/servers/presence_connection_manager/contracts/requests/buddy.py new file mode 100644 index 000000000..172391346 --- /dev/null +++ b/src/servers/presence_connection_manager/contracts/requests/buddy.py @@ -0,0 +1,135 @@ +from servers.presence_connection_manager.abstractions.contracts import RequestBase +from servers.presence_connection_manager.aggregates.user_status import UserStatus +from servers.presence_connection_manager.aggregates.user_status_info import UserStatusInfo +from servers.presence_connection_manager.enums.general import GPStatusCode +from servers.presence_search_player.exceptions.general import GPParseException + + +class AddBuddyRequest(RequestBase): + friend_profile_id: int + reason: str + + def parse(self): + super().parse() + if ( + ("sesskey" not in self.request_key_values) + or ("newprofileid" not in self.request_key_values) + or ("reason" not in self.request_key_values) + ): + raise GPParseException("addbuddy request is invalid.") + + self.friend_profile_id = int(self.request_key_values["newprofileid"]) + if self.friend_profile_id == 0: + raise GPParseException("newprofileid format is incorrect.") + + self.reason = self.request_key_values["reason"] + + +class DelBuddyRequest(RequestBase): + friend_profile_id: int + + def parse(self): + super().parse() + if "delprofileid" not in self.request_key_values: + raise GPParseException("delprofileid is missing.") + + self.friend_profile_id = int(self.request_key_values["delprofileid"]) + if self.friend_profile_id == 0: + raise GPParseException("delprofileid format is incorrect.") + + +class InviteToRequest(RequestBase): + product_id: int + profile_id: int + session_key: str + """the invite target profile id""" + + def parse(self): + super().parse() + if "productid" not in self.request_key_values: + raise GPParseException("productid is missing.") + + if "sesskey" not in self.request_key_values: + raise GPParseException("sesskey is missing.") + + try: + self.product_id = int(self.request_key_values["productid"]) + except ValueError: + raise GPParseException("productid format is incorrect.") + + try: + self.profile_id = int(self.request_key_values["profileid"]) + except ValueError: + raise GPParseException("profileid format is incorrect.") + + self.session_key = self.request_key_values["sesskey"] + + +class StatusInfoRequest(RequestBase): + def __init__(self): + super().__init__() + self.is_get_status_info = False + self.profile_id = 0 + self.namespace_id = None + self.status_info = UserStatusInfo() + + def parse(self): + super().parse() + + if ( + "state" not in self.request_key_values + or "hostip" not in self.request_key_values + or "hprivip" not in self.request_key_values + or "qport" not in self.request_key_values + or "hport" not in self.request_key_values + or "sessflags" not in self.request_key_values + or "rechstatus" not in self.request_key_values + or "gametype" not in self.request_key_values + or "gamevariant" not in self.request_key_values + or "gamemapname" not in self.request_key_values + ): + raise GPParseException("StatusInfo request is invalid.") + + self.status_info.status_state = self.request_key_values["state"] + self.status_info.host_ip = self.request_key_values["hostip"] + self.status_info.host_private_ip = self.request_key_values["hprivip"] + + try: + self.status_info.query_report_port = int(self.request_key_values["qport"]) + self.status_info.host_port = int(self.request_key_values["hport"]) + self.status_info.session_flags = int(self.request_key_values["sessflags"]) + except ValueError: + raise GPParseException("qport, hport, or sessflags format is incorrect.") + + self.status_info.rich_status = self.request_key_values["rechstatus"] + self.status_info.game_type = self.request_key_values["gametype"] + self.status_info.game_variant = self.request_key_values["gamevariant"] + self.status_info.game_map_name = self.request_key_values["gamemapname"] + + +class StatusRequest(RequestBase): + def __init__(self, raw_request): + super().__init__(raw_request) + self.status = UserStatus() + self.IsGetStatus = False + + def parse(self): + super().parse() + + if "status" not in self.request_key_values: + raise GPParseException("status is missing.") + + if "statstring" not in self.request_key_values: + raise GPParseException("statstring is missing.") + + if "locstring" not in self.request_key_values: + raise GPParseException("locstring is missing.") + + try: + status_code = int(self.request_key_values["status"]) + self.status.current_status = GPStatusCode(status_code) + except ValueError: + raise GPParseException("status format is incorrect.") + + self.status.location_string = self.request_key_values["locstring"] + self.status.status_string = self.request_key_values["statstring"] diff --git a/src/servers/presence_connection_manager/contracts/requests/general.py b/src/servers/presence_connection_manager/contracts/requests/general.py new file mode 100644 index 000000000..edac1efa0 --- /dev/null +++ b/src/servers/presence_connection_manager/contracts/requests/general.py @@ -0,0 +1,117 @@ +from servers.presence_connection_manager.abstractions.contracts import RequestBase +from servers.presence_connection_manager.enums.general import ( + LoginType, + QuietModeType, + SdkRevisionType, +) +from servers.presence_search_player.exceptions.general import ( + GPParseException, +) + + +class KeepAliveRequest(RequestBase): + pass + + +class LoginRequest(RequestBase): + user_challenge: str + response: str + unique_nick: str + user_data: str + namespace_id: int + auth_token: str + nick: str + email: str + product_id: int + type: LoginType + sdk_revision_type: SdkRevisionType + game_port: int + user_id: int + profile_id: int + partner_id: int + game_name: int + quiet_mode_flags: int + firewall: bool + + def __init__(self, raw_request): + super().__init__(raw_request) + + def parse(self): + super().parse() + + if "challenge" not in self.request_key_values: + raise GPParseException("challenge is missing") + + if "response" not in self.request_key_values: + raise GPParseException("response is missing") + + self.user_challenge = self.request_key_values["challenge"] + self.response = self.request_key_values["response"] + + if ( + "uniquenick" in self.request_key_values + and "namespaceid" in self.request_key_values + ): + namespace_id = int(self.request_key_values["namespaceid"]) + self.type = LoginType.UNIQUENICK_NAMESPACE_ID + self.unique_nick = self.request_key_values["uniquenick"] + self.user_data = self.unique_nick + self.namespace_id = namespace_id + elif "authtoken" in self.request_key_values: + self.type = LoginType.AUTH_TOKEN + self.auth_token = self.request_key_values["authtoken"] + self.user_data = self.auth_token + elif "user" in self.request_key_values: + self.type = LoginType.NICK_EMAIL + self.user_data = self.request_key_values["user"] + pos = self.user_data.index("@") + if pos == -1 or pos < 1 or (pos + 1) >= len(self.user_data): + raise GPParseException("user format is incorrect") + self.nick = self.user_data[:pos] + self.email = self.user_data[pos + 1 :] + if "namespaceid" in self.request_key_values: + namespace_id = int(self.request_key_values["namespaceid"]) + self.namespace_id = namespace_id + else: + raise GPParseException("Unknown login method detected.") + + self.parse_other_data() + + def parse_other_data(self): + if "userid" in self.request_key_values: + user_id = int(self.request_key_values["userid"]) + self.user_id = user_id + + if "profileid" in self.request_key_values: + profile_id = int(self.request_key_values["profileid"]) + self.profile_id = profile_id + + if "partnerid" in self.request_key_values: + partner_id = int(self.request_key_values["partnerid"]) + self.partner_id = partner_id + + if "sdkrevision" in self.request_key_values: + sdk_revision_type = int(self.request_key_values["sdkrevision"]) + self.sdk_revision_type = SdkRevisionType(sdk_revision_type) + + if "gamename" in self.request_key_values: + self.game_name = self.request_key_values["gamename"] + + if "port" in self.request_key_values: + game_port = int(self.request_key_values["port"]) + self.game_port = game_port + + if "productid" in self.request_key_values: + product_id = int(self.request_key_values["productid"]) + self.product_id = product_id + + if "firewall" in self.request_key_values: + self.firewall = self.request_key_values["firewall"] + + if "quiet" in self.request_key_values: + quiet = int(self.request_key_values["quiet"]) + self.quiet_mode_flags = QuietModeType(quiet) + + +class LogoutRequest(RequestBase): + pass diff --git a/src/servers/presence_connection_manager/contracts/requests/profile.py b/src/servers/presence_connection_manager/contracts/requests/profile.py new file mode 100644 index 000000000..0c56c3a20 --- /dev/null +++ b/src/servers/presence_connection_manager/contracts/requests/profile.py @@ -0,0 +1,262 @@ +from library.extentions.gamespy_utils import is_valid_date +from servers.presence_connection_manager.abstractions.contracts import RequestBase +from servers.presence_connection_manager.enums.general import PublicMasks +from servers.presence_search_player.exceptions.general import ( + GPParseException, +) + + +class AddBlockRequest(RequestBase): + taget_id: int + + def parse(self): + super().parse() + + if "profileid" not in self.request_key_values: + raise GPParseException("profileid is missing") + + try: + self.taget_id = int(self.request_key_values["profileid"]) + except ValueError: + raise GPParseException("profileid format is incorrect") + + +class GetProfileRequest(RequestBase): + profile_id: int + session_key: str + + def parse(self): + super().parse() + + if "profileid" not in self.request_key_values: + raise GPParseException("profileid is missing") + + try: + self.profile_id = int(self.request_key_values["profileid"]) + except ValueError: + raise GPParseException("profileid format is incorrect") + + if "sesskey" not in self.request_key_values: + raise GPParseException("sesskey is missing") + + self.session_key = self.request_key_values["sesskey"] + + +class NewProfileRequest(RequestBase): + is_replace_nick_name: bool + session_key: str + new_nick: str + old_nick: str + + def parse(self): + super().parse() + + if "sesskey" not in self.request_key_values: + raise GPParseException("sesskey is missing") + + self.session_key = self.request_key_values["sesskey"] + + if "replace" in self.request_key_values: + if ( + "oldnick" not in self.request_key_values + and "nick" not in self.request_key_values + ): + raise GPParseException("oldnick or nick is missing.") + + if "oldnick" in self.request_key_values: + self.old_nick = self.request_key_values["oldnick"] + if "nick" in self.request_key_values: + self.new_nick = self.request_key_values["nick"] + + self.is_replace_nick_name = True + else: + if "nick" not in self.request_key_values: + raise GPParseException("nick is missing.") + + self.new_nick = self.request_key_values["nick"] + self.is_replace_nick_name = False + + +class RegisterCDKeyRequest(RequestBase): + session_key: str + cdkey_enc: str + + def parse(self): + super().parse() + + if "sesskey" not in self.request_key_values: + raise GPParseException("sesskey is missing") + + self.session_key = self.request_key_values["sesskey"] + + if "cdkeyenc" not in self.request_key_values: + raise GPParseException("cdkeyenc is missing") + + self.cdkey_enc = self.request_key_values["cdkeyenc"] + + +class RegisterNickRequest(RequestBase): + unique_nick: str + session_key: str + partner_id: int + + def parse(self): + super().parse() + + if "sesskey" not in self.request_key_values: + raise GPParseException("sesskey is missing") + + self.session_key = self.request_key_values["sesskey"] + + if "uniquenick" not in self.request_key_values: + raise GPParseException("uniquenick is missing") + + self.unique_nick = self.request_key_values["uniquenick"] + + if "partnerid" in self.request_key_values: + try: + self.partner_id = int(self.request_key_values["partnerid"]) + except ValueError: + raise GPParseException("partnerid is missing") + + +class UpdateProfileRequest(RequestBase): + has_public_mask_flag: bool = None + public_mask: PublicMasks = None + session_key: str = None + partner_id: int = None + nick: str = None + uniquenick: str = None + has_first_name_flag: bool = None + first_name: str = None + has_last_name_flag: bool = None + last_name: str = None + has_icq_flag: bool = None + icq_uin: int = None + has_home_page_flag: bool = None + home_page: str = None + has_birthday_flag: bool = False + birth_day: int = None + birth_month: int = None + birth_year: int = None + has_sex_flag: bool = False + sex: bool = None + has_zip_code: bool = False + zip_code: str = None + has_country_code: bool = False + country_code: str = None + + def parse(self): + super().parse() + + if "publicmask" in self.request_key_values: + if not self.request_key_values["publicmask"].isdigit(): + raise GPParseException("publicmask format is incorrect") + self.has_public_mask_flag = True + self.public_mask = PublicMasks(int(self.request_key_values["publicmask"])) + + if "sesskey" not in self.request_key_values: + raise GPParseException("sesskey is missing") + self.session_key = self.request_key_values["sesskey"] + + if "firstname" in self.request_key_values: + self.first_name = self.request_key_values["firstname"] + self.has_first_name_flag = True + + if "lastname" in self.request_key_values: + self.last_name = self.request_key_values["lastname"] + self.has_last_name_flag = True + + if "icquin" in self.request_key_values: + if not self.request_key_values["icquin"].isdigit(): + raise GPParseException("icquin format is incorrect") + self.has_icq_flag = True + self.icq_uin = int(self.request_key_values["icquin"]) + + # Remaining attribute assignments... + if "homepage" in self.request_key_values: + self.home_page = self.request_key_values["homepage"] + self.has_home_page_flag = True + + if "birthday" in self.request_key_values: + try: + date = int(self.request_key_values["birthday"]) + d = (date >> 24) & 0xFF + m = (date >> 16) & 0xFF + y = date & 0xFFFF + if is_valid_date(d, m, y): + self.birth_day = d + self.birth_month = m + self.birth_year = y + self.has_birthday_flag = True + except ValueError: + pass + + if "sex" in self.request_key_values: + try: + self.sex = int(self.request_key_values["sex"]) + self.has_sex_flag = True + except ValueError: + raise GPParseException("sex format is incorrect") + + if "zipcode" in self.request_key_values: + self.zip_code = self.request_key_values["zipcode"] + self.has_zip_code = True + + if "countrycode" in self.request_key_values: + self.country_code = self.request_key_values["countrycode"] + self.has_country_code = True + + if "partnerid" in self.request_key_values: + try: + self.partner_id = int(self.request_key_values["partnerid"]) + except ValueError: + raise GPParseException("partnerid is incorrect") + + if "nick" in self.request_key_values: + self.nick = self.request_key_values["nick"] + + if "uniquenick" in self.request_key_values: + self.uniquenick = self.request_key_values["uniquenick"] + + +class UpdateUiRequest(RequestBase): + cpubrandid: str = None + cpuspeed: str = None + memory: str = None + videocard1ram: str = None + videocard2ram: str = None + connectionid: str = None + connectionspeed: str = None + hasnetwork: str = None + pic: str = None + + def parse(self): + super().parse() + + if "cpubrandid" in self.request_key_values: + self.cpubrandid = self.request_key_values["cpubrandid"] + + if "cpuspeed" in self.request_key_values: + self.cpuspeed = self.request_key_values["cpuspeed"] + + if "memory" in self.request_key_values: + self.memory = self.request_key_values["memory"] + + if "videocard1ram" in self.request_key_values: + self.videocard1ram = self.request_key_values["videocard1ram"] + + if "videocard2ram" in self.request_key_values: + self.videocard2ram = self.request_key_values["videocard2ram"] + + if "connectionid" in self.request_key_values: + self.connectionid = self.request_key_values["connectionid"] + + if "connectionspeed" in self.request_key_values: + self.connectionspeed = self.request_key_values["connectionspeed"] + + if "hasnetwork" in self.request_key_values: + self.hasnetwork = self.request_key_values["hasnetwork"] + + if "pic" in self.request_key_values: + self.pic = self.request_key_values["pic"] diff --git a/src/servers/presence_connection_manager/contracts/responses/buddy.py b/src/servers/presence_connection_manager/contracts/responses/buddy.py new file mode 100644 index 000000000..7870a90e3 --- /dev/null +++ b/src/servers/presence_connection_manager/contracts/responses/buddy.py @@ -0,0 +1,80 @@ +from servers.presence_connection_manager.abstractions.contracts import ( + RequestBase, + ResponseBase, +) +from servers.presence_connection_manager.contracts.requests.buddy import AddBuddyRequest, StatusInfoRequest +from servers.presence_connection_manager.contracts.results.buddy import ( + AddBuddyResult, + BlockListResult, + BuddyListResult, + StatusInfoResult, +) + + +class AddBuddyResponse(ResponseBase): + def __init__(self, request: AddBuddyRequest, result: AddBuddyResult) -> None: + assert issubclass(type(request), AddBuddyRequest) + assert issubclass(type(result), AddBuddyResult) + super().__init__(request, result) + + def build(self) -> None: + # return super().build() + raise NotImplementedError() + # \bm\\f\\date\ + # GPI_BM_MESSAGE: \msg\\ + # GPI_BM_UTM:\msg\\ + # GPI_BM_REQUEST:\msg\|signed|\ + # GPI_BM_AUTH: + # GPI_BM_REVOKE: + # GPI_BM_STATUS:\msg\|s|\ or \msg\|ss||ls||ip||p||qm| + # GPI_BM_INVITE:\msg\|p||l| + # GPI_BM_PING:\msg\\ + + +class BlockListResponse(ResponseBase): + _result: BlockListResult + + def __init__(self, result: BlockListResult): + assert isinstance(result, BlockListResult) + self._result = result + + def build(self): + # \blk\< num in list >\list\< profileid list - comma delimited >\final\ + self.sending_buffer = f"\\blk\\{len(self._result.profile_ids)}\\list\\" + self.sending_buffer += ",".join(str(pid) for pid in self._result.profile_ids) + self.sending_buffer += "\\final\\" + + +class BuddyListResponse(ResponseBase): + _result: BuddyListResult + + def __init__(self, request: RequestBase, result: BuddyListResult): + super().__init__(request, result) + + def build(self): + # \bdy\< num in list >\list\< profileid list - comma delimited >\final\ + self.sending_buffer = f"\\bdy\\{len(self._result.profile_ids)}\\list\\" + self.sending_buffer += ",".join(str(pid) for pid in self._result.profile_ids) + self.sending_buffer += "\\final\\" + + +class StatusInfoResponse(ResponseBase): + _result: StatusInfoResult + + def __init__(self, request: StatusInfoRequest, result: StatusInfoResult): + assert isinstance(request, StatusInfoRequest) + assert isinstance(result, StatusInfoResult) + super().__init__(request, result) + + def build(self): + # \bsi\\state\\profile\\bip\\bport\\hostip\\hprivip\\qport\\hport\\sessflags\\rstatus\\gameType\\gameVnt\\gameMn\\product\\qmodeflags\ + self.sending_buffer = ( + f"\\bsi\\state\\{self._result.status_info.status_state}\\" + f"profile\\{self._result.profile_id}\\bip\\{self._result.status_info.buddy_ip}\\" + f"hostIp\\{self._result.status_info.host_ip}\\hprivIp\\{self._result.status_info.host_private_ip}\\" + f"qport\\{self._result.status_info.query_report_port}\\hport\\{self._result.status_info.host_port}\\" + f"sessflags\\{self._result.status_info.session_flags}\\rstatus\\{self._result.status_info.rich_status}\\" + f"gameType\\{self._result.status_info.game_type}\\gameVnt\\{self._result.status_info.game_variant}\\" + f"gameMn\\{self._result.status_info.game_map_name}\\product\\{self._result.product_id}\\" + f"qmodeflags\\{self._result.status_info.quiet_mode_flags}\\final\\" + ) diff --git a/src/servers/presence_connection_manager/contracts/responses/general.py b/src/servers/presence_connection_manager/contracts/responses/general.py new file mode 100644 index 000000000..c92140245 --- /dev/null +++ b/src/servers/presence_connection_manager/contracts/responses/general.py @@ -0,0 +1,61 @@ +from servers.chat.contracts.requests.general import LoginRequest +from servers.chat.contracts.results.general import LoginResult +from servers.presence_connection_manager.abstractions.contracts import ResponseBase +from servers.presence_connection_manager.applications.client import ( + LOGIN_TICKET, + SESSION_KEY, +) +from servers.presence_connection_manager.contracts.requests.general import ( + KeepAliveRequest, +) +from servers.presence_search_player.contracts.requests import ( + NewUserRequest, +) +from servers.presence_search_player.contracts.responses import ( + NewUserResponse as NUR, +) +from servers.presence_search_player.contracts.results import NewUserResult + + +class KeepAliveResponse(ResponseBase): + def __init__(self, request: KeepAliveRequest) -> None: + super().__init__(request, None) + + def build(self) -> None: + self.sending_buffer = "\\ka\\final\\" + + +class LoginResponse(ResponseBase): + _result: LoginResult + + def __init__(self, request: LoginRequest, result: LoginResult): + super().__init__(request, result) + + def build(self): + # string checkSumStr = _result.DatabaseResults.Nick + _result.DatabaseResults.UniqueNick + _result.DatabaseResults.NamespaceID; + # _connection.UserData.SessionKey = _crc.ComputeChecksum(checkSumStr); + + self.sending_buffer = ( + "\\lc\\2\\sesskey\\" + + SESSION_KEY + + "\\proof\\" + + self._result.response_proof + + "\\userid\\" + + self._result.data.user_id + + "\\profileid\\" + + self._result.data.profile_id + ) + + if self._result.data.unique_nick is not None: + self.sending_buffer += "\\uniquenick\\" + self._result.data.unique_nick + + self.sending_buffer += f"\\lt\\{LOGIN_TICKET}" + self.sending_buffer += f"\\id\\{self._request.operation_id}\\final\\" + + +class NewUserResponse(NUR): + _result: NewUserResult + _request: NewUserRequest + + def build(self): + self.sending_buffer = f"\\nur\\userid\\{self._result.user.userid}\\profileid\\{self._result.sub_profile.profileid}\\id\\{self._request.operation_id}\\final\\" diff --git a/src/servers/presence_connection_manager/contracts/responses/profile.py b/src/servers/presence_connection_manager/contracts/responses/profile.py new file mode 100644 index 000000000..19d54a659 --- /dev/null +++ b/src/servers/presence_connection_manager/contracts/responses/profile.py @@ -0,0 +1,99 @@ +from library.extentions.gamespy_ramdoms import StringType, generate_random_string +from servers.chat.contracts.requests.general import RegisterNickRequest +from servers.presence_connection_manager.abstractions.contracts import ResponseBase +from servers.presence_connection_manager.contracts.requests.profile import GetProfileRequest, NewProfileRequest +from servers.presence_connection_manager.contracts.results.profile import GetProfileResult, NewProfileResult + + +class GetProfileResponse(ResponseBase): + _result: GetProfileResult + _request: GetProfileRequest + + def __init__(self, request: GetProfileRequest, result: GetProfileResult): + assert isinstance(request, GetProfileRequest) + assert isinstance(result, GetProfileResult) + super().__init__(request, result) + + def build(self): + self.sending_buffer = ( + "\\pi\\profileid\\" + + str(self._result.user_profile.profile_id) + + "\\nick\\" + + self._result.user_profile.nick + + "\\uniquenick\\" + + self._result.user_profile.unique_nick + + "\\email\\" + + self._result.user_profile.email + + "\\firstname\\" + + self._result.user_profile.firstname + + "\\lastname\\" + + self._result.user_profile.lastname + + "\\icquin\\" + + self._result.user_profile.icquin + + "\\homepage\\" + + self._result.user_profile.homepage + + "\\zipcode\\" + + self._result.user_profile.zipcode + + "\\countrycode\\" + + self._result.user_profile.countrycode + + "\\lon\\" + + str(self._result.user_profile.longitude) + + "\\lat\\" + + str(self._result.user_profile.latitude) + + "\\loc\\" + + self._result.user_profile.location + ) + + birth_str = ( + (self._result.user_profile.birthday << 24) + | (self._result.user_profile.birthmonth << 16) + | self._result.user_profile.birthyear + ) + self.sending_buffer += "\\birthday\\" + str(birth_str) + + self.sending_buffer += "\\sex\\" + self._result.user_profile.sex + self.sending_buffer += "\\publicmask\\" + self._result.user_profile.publicmask + self.sending_buffer += "\\aim\\" + self._result.user_profile.aim + self.sending_buffer += "\\picture\\" + self._result.user_profile.picture + self.sending_buffer += "\\ooc" + str(self._result.user_profile.occupationid) + self.sending_buffer += "\\ind\\" + str(self._result.user_profile.industryid) + self.sending_buffer += "\\inc\\" + str(self._result.user_profile.incomeid) + self.sending_buffer += "\\mar\\" + str(self._result.user_profile.marriedid) + self.sending_buffer += "\\chc\\" + str(self._result.user_profile.childcount) + self.sending_buffer += "\\i1\\" + self._result.user_profile.interests1 + self.sending_buffer += "\\o1\\" + self._result.user_profile.ownership1 + self.sending_buffer += "\\conn\\" + self._result.user_profile.connectiontype + self.sending_buffer += "\\sig\\+" + generate_random_string(10, StringType.HEX) + self.sending_buffer += "\\id\\" + str(self._request.operation_id) + "\\final\\" + + +class NewProfileResponse(ResponseBase): + _request: NewProfileRequest + _result: NewProfileResult + + def __init__(self, request: NewProfileRequest, result: NewProfileResult): + assert isinstance(request, NewProfileRequest) + assert isinstance(result, NewProfileResult) + super().__init__(request, result) + + def build(self): + self.sending_buffer = f"\\npr\\\\profileid\\{self.sending_buffer}\\id\\{self._request.operation_id}\\final\\" + + +class RegisterCDKeyResposne(ResponseBase): + def __init__(self) -> None: + pass + + def build(self): + self.sending_buffer = "\\rc\\\\final\\" + + +class RegisterNickResponse(ResponseBase): + _request: RegisterNickRequest + + def __init__(self, request: RegisterNickRequest) -> None: + assert isinstance(request, RegisterNickRequest) + self._request = request + + def build(self) -> None: + self.sending_buffer = f"\\rn\\\\id\\{self._request.operation_id}\\final\\" diff --git a/src/servers/presence_connection_manager/contracts/results/buddy.py b/src/servers/presence_connection_manager/contracts/results/buddy.py new file mode 100644 index 000000000..f2d1c3956 --- /dev/null +++ b/src/servers/presence_connection_manager/contracts/results/buddy.py @@ -0,0 +1,30 @@ +from servers.presence_connection_manager.abstractions.contracts import ResultBase +from servers.presence_connection_manager.aggregates.user_status import UserStatus +from servers.presence_connection_manager.aggregates.user_status_info import ( + UserStatusInfo, +) + + +class AddBuddyResult(ResultBase): + pass + + +class BlockListResult(ResultBase): + profile_ids: list[int] + + +class BuddyListResult(ResultBase): + profile_ids: list[int] + + +class StatusInfoResult(ResultBase): + profile_id: int + product_id: int + status_info: UserStatusInfo + + +class StatusResult(ResultBase): + status: UserStatus + + +# class NewUserResult() \ No newline at end of file diff --git a/src/servers/presence_connection_manager/contracts/results/general.py b/src/servers/presence_connection_manager/contracts/results/general.py new file mode 100644 index 000000000..970d6c04a --- /dev/null +++ b/src/servers/presence_connection_manager/contracts/results/general.py @@ -0,0 +1,19 @@ +from servers.presence_connection_manager.abstractions.contracts import ResultBase + + +class LoginDataModel: + user_id: int + profile_id: int + nick: str + email: str + unique_nick: str + password_hash: str + email_verified_flag: bool + banned_flag: bool + namespace_id: int + sub_profile_id: int + + +class LoginResult(ResultBase): + response_proof: str + data: LoginDataModel diff --git a/src/servers/presence_connection_manager/contracts/results/profile.py b/src/servers/presence_connection_manager/contracts/results/profile.py new file mode 100644 index 000000000..a209797d6 --- /dev/null +++ b/src/servers/presence_connection_manager/contracts/results/profile.py @@ -0,0 +1,40 @@ +from servers.presence_connection_manager.abstractions.contracts import ResultBase + + +class GetProfileDataModel: + nick: str = None + profile_id: int = None + unique_nick: str = None + email: str = None + firstname: str = None + lastname: str = None + icquin: int = None + homepage: str = None + zipcode: str = None + countrycode: str = None + longitude: float = None + latitude: float = None + location: str = None + birthday: int = None + birthmonth: int = None + birthyear: int = None + sex: int = None + publicmask: int = None + aim: str = None + picture: int = None + occupationid: int = None + industryid: int = None + incomeid: int = None + marriedid: int = None + childcount: int = None + interests1: int = None + ownership1: int = None + connectiontype: int = None + + +class GetProfileResult(ResultBase): + user_profile: GetProfileDataModel = None + + +class NewProfileResult(ResultBase): + profile_id: int diff --git a/src/servers/presence_connection_manager/enums/general.py b/src/servers/presence_connection_manager/enums/general.py new file mode 100644 index 000000000..397c346ea --- /dev/null +++ b/src/servers/presence_connection_manager/enums/general.py @@ -0,0 +1,342 @@ +from enum import IntEnum, IntFlag + + +class BuddyMessageType(IntEnum): + # response + BM_MESSAGE = 1 + BM_REQUEST = 2 + BM_REPLY = 3 # only used on the backend + BM_AUTH = 4 + BM_UTM = 5 + BM_REVOKE = 6 # remote buddy removed from local list + BM_STATUS = 100 + BM_INVITE = 101 + BM_PING = 102 + + # request + BM_PONG = 103 + BM_KEYS_REQUEST = 104 + BM_KEYS_REPLY = 105 + BM_FILE_SEND_REQUEST = 200 + BM_FILE_SEND_REPLY = 201 + BM_FILE_BEGIN = 202 + BM_FILE_END = 203 + BM_FILE_DATA = 204 + BM_FILE_SKIP = 205 + BM_FILE_TRANSFER_THROTTLE = 206 + BM_FILE_TRANSFER_CANCEL = 207 + BM_FILE_TRANSFER_KEEP_ALIVE = 208 + + +class DisconnectReason(IntEnum): + NORMAL_LOGOUT = 0 + KEEP_ALIVE_FAILED = 1 + LOGIN_TIMED_OUT = 2 + INVALID_USERNAME = 3 + INVALID_PASSWORD = 4 + INVALID_LOGIN_QUERY = 5 + CREATE_FAILED_USERNAME_EXISTS = 6 + CREATE_FAILED_DATABASE_ERROR = 7 + GENERAL_ERROR = 8 + DISCONNECTED = 9 + FORCED_LOGOUT = 10 + NEW_LOGIN_DETECTED = 11 + FORCED_SERVER_SHUTDOWN = 12 + CLIENT_CHALLENGE_ALREADY_SENT = 13 + PLAYER_IS_BANNED = 14 + INVALID_PLAYER = 15 + PLAYER_IS_NOT_ACTIVATED = 16 + + +class FireWallType(IntEnum): + NO_FIREWALL = 0 + FIREWALL = 1 + + +class GenderType(IntEnum): + MALE = 0 + FEMALE = 1 + PAT = 2 + + +class GPBasic(IntEnum): + INFO_CACHING = 0x0100 + SIMULATION = 0x0101 + INFO_CACHING_BUDDY_AND_BLOCK_ONLY = 0x0102 + BLOCKING = 0x0103 + NON_BLOCKING = 0x0104 + FIREWALL = 0x0105 + NO_FIREWALL = 0x0106 + CHECK_CACHE = 0x0107 + DONT_CHECK_CACHE = 0x0108 + EMAIL_VALID = 0x0109 + EMAIL_INVALID = 0x010A + FATAL = 0x010B + NON_FATAL = 0x010C + MALE = 0x0500 + FEMALE = 0x0501 + PAT = 0x0502 + MORE = 0x0600 + DONE = 0x0601 + NICK = 0x0700 + UNIQUENICK = 0x0701 + EMAIL = 0x0702 + PASSWORD = 0x0703 + FIRST_NAME = 0x0704 + LAST_NAME = 0x0705 + ICQ_UIN = 0x0706 + HOMEPAGE = 0x0707 + ZIP_CODE = 0x0708 + COUNTRY_CODE = 0x0709 + BIRTHDAY = 0x070A + SEX = 0x070B + CPU_BRAND = 0x070C + CPU_SPEED = 0x070D + MEMORY = 0x070E + VIDEO_CARD1_STRING = 0x070F + VIDEO_CARD1_RAM = 0x0710 + VIDEO_CARD2_STRING = 0x0711 + VIDEO_CARD2_RAM = 0x0712 + CONNECTION_ID = 0x0713 + CONNECTION_SPEED = 0x0714 + HAS_NETWORK = 0x0715 + OS_STRING = 0x0716 + AIM_NAME = 0x0717 + PIC = 0x0718 + OCCUPATION_ID = 0x0719 + INDUSTRY_ID = 0x071A + INCOME_ID = 0x071B + MARRIED_ID = 0x071C + CHILD_COUNT = 0x071D + INTEREST1 = 0x071E + REPLACE = 0x0800 + DONT_REPLACE = 0x0801 + CONNECTED = 0x0900 + NOT_CONNECTED = 0x0901 + MASK_NONE = 0x0A00 + MASK_HOMEPAGE = 0x0A01 + MASK_ZIP_CODE = 0x0A02 + MASK_COUNTRY_CODE = 0x0A03 + MASK_BIRTHDAY = 0x0A04 + MASK_SEX = 0x0A05 + MASK_EMAIL = 0x0A06 + MASK_ALL = 0x0A07 + SESS_IS_CLOSED = 0x0B00 + SESS_IS_OPEN = 0x0B01 + SESS_HAS_PASSWORD = 0x0B02 + SESS_IS_BEHIND_NAT = 0x0B03 + SESS_IS_RANKED = 0x0B04 + INTEL = 0x0C00 + AMD = 0x0C01 + CYRIX = 0x0C02 + MOTOROLA = 0x0C03 + ALPHA = 0x0C04 + MODEM = 0x0D00 + ISDN = 0x0D01 + CABLE_MODEM = 0x0D02 + DSL = 0x0D03 + SATELLITE = 0x0D04 + ETHERNET = 0x0D05 + WIRELESS = 0x0D06 + TRANSFER_SEND_REQUEST = 0x0E00 + TRANSFER_ACCEPTED = 0x0E01 + TRANSFER_REJECTED = 0x0E02 + TRANSFER_NOT_ACCEPTING = 0x0E03 + TRANSFER_NO_CONNECTION = 0x0E04 + TRANSFER_DONE = 0x0E05 + TRANSFER_CANCELLED = 0x0E06 + TRANSFER_LOST_CONNECTION = 0x0E07 + TRANSFER_ERROR = 0x0E08 + TRANSFER_THROTTLE = 0x0E09 + + +class GPStatusCode(IntEnum): + OFFLINE = 0 + ONLINE = 1 + PLAYING = 2 + STAGING = 3 + CHATTING = 4 + AWAY = 5 + + +class LoginStatus(IntEnum): + CONNECTED = 0 + PROCESSING = 1 + COMPLETED = 2 + DISCONNECTED = 3 + + +class LoginType(IntEnum): + NICK_EMAIL = 0 + UNIQUENICK_NAMESPACE_ID = 1 + AUTH_TOKEN = 2 + + +class PublicMasks(IntFlag): + NONE = 0x00000000 + HOMEPAGE = 0x00000001 + ZIP_CODE = 0x00000002 + COUNTRY_CODE = 0x00000004 + BIRTHDAY = 0x00000008 + SEX = 0x00000010 + EMAIL = 0x00000020 + ALL = 0xFFFFFFFF + + +class QuietModeType(IntFlag): + SILENCE_NONE = 0x00000000 + SILENCE_MESSAGE = 0x00000001 + SILENCE_UTMS = 0x00000002 + SILENCE_LIST = 0x00000004 + SILENCE_ALL = 0xFFFFFFFF + + +class SdkRevisionType(IntEnum): + UNKNOWN = 0 + GPINEW_AUTH_NOTIFICATION = 1 + GPINEW_REVOKE_NOTIFICATION = 1 << 2 + GPINEW_STATUS_NOTIFICATION = 1 << 3 + GPINEW_LIST_RETRIEVAL_ON_LOGIN = 1 << 4 + GPIREMOTE_AUTH_IDS_NOTIFICATION = 1 << 5 + GPINEW_CD_KEY_REGISTRATION = 1 << 6 + + +class GPBasic(IntEnum): + # Global States + INFO_CACHING = 0x0100 + SIMULATION = 0x0101 + INFO_CACHING_BUDDY_AND_BLOCK_ONLY = 0x0102 + + # Blocking + BLOCKING = 1 + NON_BLOCKING = 0 + + # Firewall + FIREWALL = 1 + NO_FIREWALL = 0 + + # Check Cache + CHECK_CACHE = 1 + DONT_CHECK_CACHE = 0 + + # Is Valid Email + EMAIL_VALID = 1 + EMAIL_INVALID = 0 + + # Fatal Error + FATAL = 1 + NON_FATAL = 0 + + # Sex + MALE = 0x0500 + FEMALE = 0x0501 + PAT = 0x0502 + + # Profile Search + MORE = 0x0600 + DONE = 0x0601 + + # Set Info + NICK = 0x0700 + UNIQUENICK = 0x0701 + EMAIL = 0x0702 + PASSWORD = 0x0703 + FIRST_NAME = 0x0704 + LAST_NAME = 0x0705 + ICQ_UIN = 0x0706 + HOME_PAGE = 0x0707 + ZIP_CODE = 0x0708 + COUNTRY_CODE = 0x0709 + BIRTHDAY = 0x070A + SEX = 0x070B + CPU_BRAND = 0x070C + CPU_SPEED = 0x070D + MEMORY = 0x070E + VIDEO_CARD1_STRING = 0x070F + VIDEO_CARD1_RAM = 0x0710 + VIDEO_CARD2_STRING = 0x0711 + VIDEO_CARD2_RAM = 0x0712 + CONNECTION_ID = 0x0713 + CONNECTION_SPEED = 0x0714 + HAS_NETWORK = 0x0715 + OS_STRING = 0x0716 + AIM_NAME = 0x0717 + PIC = 0x0718 + OCCUPATION_ID = 0x0719 + INDUSTRY_ID = 0x071A + INCOME_ID = 0x071B + MARRIED_ID = 0x071C + CHILD_COUNT = 0x071D + INTEREST1 = 0x071E + + # New Profile + REPLACE = 1 + DONT_REPLACE = 0 + + # Is Connected + CONNECTED = 1 + NOT_CONNECTED = 0 + + # Public mask + MASK_NONE = 0x00000000 + MASK_HOMEPAGE = 0x00000001 + MASK_ZIPCODE = 0x00000002 + MASK_COUNTRYCODE = 0x00000004 + MASK_BIRTHDAY = 0x00000008 + MASK_SEX = 0x00000010 + MASK_EMAIL = 0x00000020 + MASK_ALL = 0xFFFFFFFF + + # Session flags + SESS_IS_CLOSED = 0x00000001 + SESS_IS_OPEN = 0x00000002 + SESS_HAS_PASSWORD = 0x00000004 + SESS_IS_BEHIND_NAT = 0x00000008 + SESS_IS_RANKED = 0x00000010 + + # CPU Brand ID + INTEL = 1 + AMD = 2 + CYRIX = 3 + MOTOROLA = 4 + ALPHA = 5 + + # Connection ID + MODEM = 1 + ISDN = 2 + CABLE_MODEM = 3 + DSL = 4 + SATELLITE = 5 + ETHERNET = 6 + WIRELESS = 7 + + # Transfer callback type + TRANSFER_SEND_REQUEST = 0x800 + TRANSFER_ACCEPTED = 0x801 + TRANSFER_REJECTED = 0x802 + TRANSFER_NOT_ACCEPTING = 0x803 + TRANSFER_NO_CONNECTION = 0x804 + TRANSFER_DONE = 0x805 + TRANSFER_CANCELLED = 0x806 + TRANSFER_LOST_CONNECTION = 0x807 + TRANSFER_ERROR = 0x808 + TRANSFER_THROTTLE = 0x809 + FILE_BEGIN = 0x80A + FILE_PROGRESS = 0x80B + FILE_END = 0x80C + + +class GPPartnerId(IntEnum): + GAMESPY = (0,) + IGN = 10 + + +class PublicMasks(IntEnum): + NONE = 0x00000000 + HOMEPAGE = 0x00000001 + ZIP_CODE = 0x00000002 + COUNTRY_CODE = 0x00000004 + BIRTHDAY = 0x00000008 + SEX = 0x00000010 + EMAIL = 0x00000020 + ALL = 0xFFFFFFFF diff --git a/src/servers/presence_connection_manager/handlers/buddy.py b/src/servers/presence_connection_manager/handlers/buddy.py new file mode 100644 index 000000000..1b113545e --- /dev/null +++ b/src/servers/presence_connection_manager/handlers/buddy.py @@ -0,0 +1,122 @@ +from multiprocessing.pool import Pool +from servers.presence_connection_manager.abstractions.contracts import RequestBase +from servers.presence_connection_manager.abstractions.handler import ( + CmdHandlerBase, + LoginHandlerBase, +) +from servers.presence_connection_manager.applications.client import Client +from servers.presence_connection_manager.contracts.requests.buddy import ( + DelBuddyRequest, + StatusInfoRequest, + StatusRequest, +) +from servers.presence_connection_manager.contracts.responses.buddy import ( + BlockListResponse, + BuddyListResponse, + StatusInfoResponse, +) +from servers.presence_connection_manager.contracts.results.buddy import ( + BlockListResult, + BuddyListResult, + StatusInfoResult, + StatusResult, +) + + +class AddBuddyHandler(CmdHandlerBase): + def __init__(self, client: Client, request: RequestBase) -> None: + raise NotImplementedError() + super().__init__(client, request) + + +class BlockListHandler(CmdHandlerBase): + _result: BlockListResult + + def __init__(self, client: Client) -> None: + assert isinstance(client, Client) + + def _response_construct(self) -> None: + self._response = BlockListResponse(self._result) + + +class BuddyListHandler(LoginHandlerBase): + _result: BuddyListResult = BuddyListResult() + + def __init__(self, client: Client): + assert isinstance(client, Client) + self._client = client + + def response_construct(self): + self._response = BuddyListResponse(self._request, self._result) + + def handle_status_info(self, profile_id): + request = StatusInfoRequest() + request.profile_id = profile_id + request.namespace_id = int(self._client.info.namespace_id) + request.is_get_status_info = True + + StatusInfoHandler(self._client, request).handle() + + def _response_send(self) -> None: + super()._response_send() + + if not self._client.info.sdk_revision.is_support_gpi_new_status_notification: + return + + with Pool() as pool: + pool.map(self.handle_status_info, self._result.profile_id_list) + + +class BuddyStatusInfoHandler(CmdHandlerBase): + """ + This is what the message should look like. Its broken up for easy viewing. + + \bsi\\state\\profile\\bip\\bport\\hostip\\hprivip\\qport\\hport\\sessflags\\rstatus\\gameType\\gameVnt\\gameMn\\product\\qmodeflags\ + """ + + def __init__(self, client: Client, request: RequestBase) -> None: + raise NotImplementedError() + super().__init__(client, request) + + +class DelBuddyHandler(LoginHandlerBase): + _request: DelBuddyRequest + + def __init__(self, client: Client, request: DelBuddyRequest) -> None: + assert isinstance(request, DelBuddyRequest) + super().__init__(client, request) + + +class InviteToHandler(LoginHandlerBase): + def __init__(self, client: Client, request: RequestBase) -> None: + raise NotImplementedError() + super().__init__(client, request) + + pass + + +class StatusHandler(CmdHandlerBase): + _request: StatusRequest + _result: StatusResult = StatusResult() + + def __init__(self, client: Client, request: StatusRequest) -> None: + assert isinstance(request, StatusRequest) + super().__init__(client, request) + + def _response_send(self) -> None: + # TODO check if statushandler need send response + raise NotImplementedError() + + +class StatusInfoHandler(LoginHandlerBase): + _request: StatusInfoRequest + _result: StatusInfoResult = StatusInfoResult() + + def __init__(self, client: Client, request: StatusInfoRequest) -> None: + assert isinstance(request, StatusInfoRequest) + super().__init__(client, request) + + def _response_send(self) -> None: + if self._request.is_get_status_info: + self._response = StatusInfoResponse(self._request, self._result) + super()._response_send() diff --git a/src/servers/presence_connection_manager/handlers/general.py b/src/servers/presence_connection_manager/handlers/general.py new file mode 100644 index 000000000..c675ce955 --- /dev/null +++ b/src/servers/presence_connection_manager/handlers/general.py @@ -0,0 +1,74 @@ +from servers.chat.contracts.requests.general import LoginRequest +from servers.chat.contracts.results.general import LoginResult +from servers.presence_connection_manager.abstractions.handler import ( + CmdHandlerBase, + LoginHandlerBase, +) +from servers.presence_connection_manager.aggregates.sdk_revision import SdkRevision +from servers.presence_connection_manager.applications.client import Client +from servers.presence_connection_manager.contracts.requests.general import ( + KeepAliveRequest, + LogoutRequest, +) +from servers.presence_connection_manager.contracts.responses.general import ( + KeepAliveResponse, + LoginResponse, +) +from servers.presence_connection_manager.handlers.buddy import ( + BlockListHandler, + BuddyListHandler, +) +from servers.presence_search_player.contracts.responses import NewUserResponse + + +class KeepAliveHandler(CmdHandlerBase): + def __init__(self, client: Client, request: KeepAliveRequest) -> None: + assert isinstance(request, KeepAliveRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = KeepAliveResponse(self._request) + + +class LoginHandelr(CmdHandlerBase): + + _request: LoginRequest + _result: LoginResult + + def __init__(self, client: Client, request: LoginRequest) -> None: + assert isinstance(request, LoginRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = LoginResponse(self._request, self._result) + + +class LogoutHandler(LoginHandlerBase): + _request: LogoutRequest + + def __init__(self, client: Client, request: LogoutRequest) -> None: + assert isinstance(request, LogoutRequest) + super().__init__(client, request) + + +import servers.presence_search_player.handlers.handlers + + +class NewUserHandler(servers.presence_search_player.handlers.handlers.NewUserHandler): + + def _response_construct(self): + self._response = NewUserResponse(self._request, self._response) + + +class SdkRevisionHandler(CmdHandlerBase): + _request: LoginRequest + + def __init__(self, client: Client, request: LoginRequest) -> None: + assert isinstance(request, LoginRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._client.info.sdk_revision = SdkRevision(self._request.sdk_revision_type) + if self._client.info.sdk_revision.is_support_gpi_new_status_notification: + BuddyListHandler(self._client).handle() + BlockListHandler(self._client).handle() diff --git a/src/servers/presence_connection_manager/handlers/profile.py b/src/servers/presence_connection_manager/handlers/profile.py new file mode 100644 index 000000000..bd4eed2ae --- /dev/null +++ b/src/servers/presence_connection_manager/handlers/profile.py @@ -0,0 +1,88 @@ +from servers.chat.contracts.requests.general import RegisterNickRequest +from servers.presence_connection_manager.abstractions.contracts import RequestBase +from servers.presence_connection_manager.abstractions.handler import CmdHandlerBase +from servers.presence_connection_manager.applications.client import Client +from servers.presence_connection_manager.contracts.requests.profile import ( + AddBlockRequest, + GetProfileRequest, + NewProfileRequest, + RegisterCDKeyRequest, + UpdateProfileRequest, +) +from servers.presence_connection_manager.contracts.responses.profile import ( + GetProfileResponse, + NewProfileResponse, + RegisterNickResponse, +) +from servers.presence_connection_manager.contracts.results.profile import NewProfileResult + + +class AddBlockHandler(CmdHandlerBase): + _request: AddBlockRequest + + def __init__(self, client: Client, request: AddBlockRequest) -> None: + assert isinstance(request, AddBlockRequest) + super().__init__(client, request) + + +class GetProfileHandler(CmdHandlerBase): + _request: GetProfileRequest + _result: GetProfileResponse + + def __init__(self, client: Client, request: GetProfileRequest) -> None: + assert isinstance(request, GetProfileRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = GetProfileResponse(self._request, self._result) + + +class NewProfileHandler(CmdHandlerBase): + _request: NewProfileRequest + _result: NewProfileResult + + def __init__(self, client: Client, request: NewProfileRequest) -> None: + assert isinstance(request, NewProfileRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = NewProfileResponse(self._request, self._result) + + +class RegisterCDKeyHandler(CmdHandlerBase): + _request: RegisterCDKeyRequest + + def __init__(self, client: Client, request: RegisterCDKeyRequest) -> None: + assert isinstance(request, RegisterCDKeyRequest) + super().__init__(client, request) + + +class RegisterNickHandler(CmdHandlerBase): + + _request: RegisterNickRequest + + def __init__(self, client: Client, request: RegisterNickRequest) -> None: + assert isinstance(request, RegisterNickRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = RegisterNickResponse(self._request, self._result) + + +class RemoveBlockHandler(CmdHandlerBase): + def __init__(self, client: Client, request: RequestBase) -> None: + raise NotImplementedError() + + super().__init__(client, request) + + +class UpdateProfileHandler(CmdHandlerBase): + _request: UpdateProfileRequest + + def __init__(self, client: Client, request: UpdateProfileRequest) -> None: + assert isinstance(request, UpdateProfileRequest) + super().__init__(client, request) + + +class UpdateUserInfoHandler(CmdHandlerBase): + raise NotImplementedError() diff --git a/src/servers/presence_connection_manager/handlers/switcher.py b/src/servers/presence_connection_manager/handlers/switcher.py new file mode 100644 index 000000000..843f1997a --- /dev/null +++ b/src/servers/presence_connection_manager/handlers/switcher.py @@ -0,0 +1,59 @@ +from library.abstractions.switcher import SwitcherBase +from servers.chat.contracts.requests.general import LoginRequest, RegisterNickRequest +from servers.presence_connection_manager.abstractions.handler import LoginHandlerBase +from servers.presence_connection_manager.applications.client import Client +from servers.presence_connection_manager.contracts.requests.buddy import StatusInfoRequest, StatusRequest +from servers.presence_connection_manager.contracts.requests.general import KeepAliveRequest, LogoutRequest +from servers.presence_connection_manager.contracts.requests.profile import AddBlockRequest, GetProfileRequest, NewProfileRequest, RegisterCDKeyRequest, UpdateProfileRequest +from servers.presence_connection_manager.handlers.buddy import StatusHandler, StatusInfoHandler +from servers.presence_connection_manager.handlers.general import KeepAliveHandler, LogoutHandler, NewUserHandler +from servers.presence_connection_manager.handlers.profile import AddBlockHandler, GetProfileHandler, NewProfileHandler, RegisterCDKeyHandler, RegisterNickHandler, UpdateProfileHandler +from servers.presence_search_player.contracts.requests import NewUserRequest +from servers.presence_search_player.exceptions.general import ( + GPParseException, +) + + +class Switcher(SwitcherBase): + _raw_request: str + + def __init__(self, client: Client, raw_request: str) -> None: + assert isinstance(client, Client) + super().__init__(client, raw_request) + + def _process_raw_request(self) -> None: + if self._raw_request[0] != "\\": + raise GPParseException("Request format is invalid") + raw_requests = self._raw_request.split("\\final\\") + for raw_request in raw_requests: + name = raw_request.strip("\\").split("\\")[0] + self._requests.append((name, raw_request)) + + def _create_cmd_handlers(self, name: str, raw_request: str) -> None: + match name: + case "ka": + return KeepAliveHandler(self._client, KeepAliveRequest(raw_request)) + case "login": + return LoginHandlerBase(self._client, LoginRequest(raw_request)) + case "logout": + return LogoutHandler(self._client, LogoutRequest(raw_request)) + case "newuser": + return NewUserHandler(self._client, NewUserRequest(raw_request)) + case "addblock": + return AddBlockHandler(self._client, AddBlockRequest(raw_request)) + case "getprofile": + return GetProfileHandler(self._client, GetProfileRequest(raw_request)) + case "newprofile": + return NewProfileHandler(self._client, NewProfileRequest(raw_request)) + case "registercdkey": + return RegisterCDKeyHandler(self._client, RegisterCDKeyRequest(raw_request)) + case "registernick": + return RegisterNickHandler(self._client, RegisterNickRequest(raw_request)) + case "updatepro": + return UpdateProfileHandler(self._client, UpdateProfileRequest(raw_request)) + case "status": + return StatusHandler(self._client, StatusRequest(raw_request)) + case "statusinfo": + return StatusInfoHandler(self._client, StatusInfoRequest(raw_request)) + case _: + return None \ No newline at end of file diff --git a/src/servers/presence_search_player/abstractions/contracts.py b/src/servers/presence_search_player/abstractions/contracts.py new file mode 100644 index 000000000..d3b956115 --- /dev/null +++ b/src/servers/presence_search_player/abstractions/contracts.py @@ -0,0 +1,48 @@ +import abc +from typing import Dict +import library +from library.extentions.gamespy_utils import convert_to_key_value +from servers.presence_search_player.exceptions.general import ( + GPParseException, +) + +from servers.presence_search_player.abstractions.contracts import ( + RequestBase, + ResultBase, +) + + +class RequestBase(library.abstractions.contracts.RequestBase, abc.ABC): + request_dict: Dict[str, str] + raw_request: str + command_name: str + operation_id: int + namespace_id: int + + def __init__(self, raw_request: str) -> None: + assert isinstance(raw_request, str) + super().__init__(raw_request) + + def parse(self) -> None: + self.request_dict = convert_to_key_value(self.raw_request) + self.command_name = self.request_dict.keys()[0] + if "id" in self.request_dict.keys(): + try: + self.operation_id = int(self.request_dict["id"]) + except ValueError: + raise GPParseException("operation id is invalid.") + + if "namespaceid" in self.request_dict: + try: + self.namespace_id = int(self.request_dict["namespaceid"]) + except ValueError: + raise GPParseException("namespaceid is incorrect.") + + +class ResultBase(library.abstractions.contracts.ResultBase, abc.ABC): + pass + + +class ResponseBase(library.abstractions.contracts.ResponseBase, abc.ABC): + _result: ResultBase + _request: RequestBase diff --git a/src/servers/presence_search_player/abstractions/handler.py b/src/servers/presence_search_player/abstractions/handler.py new file mode 100644 index 000000000..f62ff48f9 --- /dev/null +++ b/src/servers/presence_search_player/abstractions/handler.py @@ -0,0 +1,16 @@ +from library.abstractions.handler import CmdHandlerBase as CHB +from servers.presence_search_player.abstractions.contracts import RequestBase +from servers.presence_search_player.applications.client import Client +from servers.presence_search_player.exceptions.general import GPException + + +class CmdHandlerBase(CHB): + def __init__(self, client: Client, request: RequestBase) -> None: + assert issubclass(type(request), RequestBase) + assert isinstance(client, Client) + super().__init__(client, request) + + def _handle_exception(self, ex) -> None: + if ex is GPException: + self._client.send(ex) + super()._handle_exception(ex) diff --git a/src/servers/presence_search_player/applications/client.py b/src/servers/presence_search_player/applications/client.py new file mode 100644 index 000000000..5f952e908 --- /dev/null +++ b/src/servers/presence_search_player/applications/client.py @@ -0,0 +1,8 @@ +from library.abstractions.client import ClientBase +from servers.presence_search_player.handlers.switcher import CmdSwitcher + + +class Client(ClientBase): + + def _create_switcher(self, buffer) -> CmdSwitcher: + return CmdSwitcher(self, buffer) diff --git a/src/servers/presence_search_player/applications/data.py b/src/servers/presence_search_player/applications/data.py new file mode 100644 index 000000000..0e65cc7c3 --- /dev/null +++ b/src/servers/presence_search_player/applications/data.py @@ -0,0 +1,260 @@ +from sqlalchemy import insert +from library.database.pg_orm import ( + Friends, + Profiles, + SubProfiles, + Users, + PG_SESSION, +) + + +def verify_email(email: str): + if PG_SESSION.query(Users).filter(Users.email == email).count() == 1: + return True + else: + return False + + +def verify_email_and_password(email: str, password: str): + result = ( + PG_SESSION.query(Users) + .filter(Users.email == email, Users.password == password) + .count() + ) + if result == 1: + return True + return False + + +def get_profile_id(email: str, password: str, nick_name: str, partner_id: int): + result = ( + PG_SESSION.query(Profiles, SubProfiles, Users) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter( + Users.email == email, + Users.password == password, + Profiles.nick == nick_name, + SubProfiles.partnerid == partner_id, + ) + .first() + ) + + return result + + +def add_user(user: Users): + PG_SESSION.add(user) + PG_SESSION.commit() + + +def add_profile(profile: Profiles): + PG_SESSION.add(profile) + PG_SESSION.commit() + + +def add_sub_profile(subprofile: SubProfiles): + PG_SESSION.add(subprofile) + PG_SESSION.commit() + + +def update_user(user: Users): + PG_SESSION.merge(user) + PG_SESSION.commit() + + +def update_profile(profile: Profiles): + PG_SESSION.merge(profile) + PG_SESSION.commit() + + +def update_subprofile(subprofile: SubProfiles): + PG_SESSION.merge(subprofile) + PG_SESSION.commit() + + +def get_user(email: str): + result = PG_SESSION.query(Users).filter(Users.email == email).first() + return result + + +def get_profile(user_id: int, nick_name: str) -> Profiles: + result = PG_SESSION.query(Profiles).filter( + Profiles.userid == user_id, Profiles.nick == nick_name + ) + return result + + +def get_sub_profile(profile_id: int, namespace_id: int, product_id: int) -> SubProfiles: + PG_SESSION.query(SubProfiles).filter( + SubProfiles.profileid == profile_id, + SubProfiles.namespaceid == namespace_id, + SubProfiles.namespaceid == product_id, + ) + + +def get_nick_and_unique_nick_list(email: str, password: str, namespace_id: int): + """ + return [(nick, uniquenick)] + """ + result = ( + PG_SESSION.query(Profiles.nick, SubProfiles.uniquenick) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter( + Users.email == email, + Users.password == password, + SubProfiles.namespaceid == namespace_id, + ) + .all() + ) + return result + + +def get_friend_info_list(profile_id: int, namespace_id: int, game_name: str): + """ + return [(profileid, nick, uniquenick, lastname, firstname, userid, email)] + """ + + result = ( + PG_SESSION.query( + Profiles.profileid, + Profiles.nick, + SubProfiles.uniquenick, + Profiles.lastname, + Profiles.firstname, + Users.userid, + Users.email, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter( + Profiles.profileid.in_(PG_SESSION.query(Friends.profileid == profile_id)), + SubProfiles.namespaceid == namespace_id, + SubProfiles.gamename == game_name, + ) + .all() + ) + return result + + +def get_matched_profile_info_list( + profile_ids: list[int], namespace_id: int +) -> list[tuple[int, str]]: + """ + return [(profileid,uniquenick)] + + """ + result = ( + PG_SESSION.query(SubProfiles.profileid, SubProfiles.uniquenick) + .filter( + SubProfiles.profileid.in_(profile_ids), + SubProfiles.namespaceid == namespace_id, + ) + .all() + ) + return result + + +def get_matched_info_by_nick( + nick_name: str, +) -> list[tuple[int, str, str, str, str, int]]: + result = ( + PG_SESSION.query( + Profiles.profileid, + Profiles.nick, + Profiles.firstname, + Profiles.lastname, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter(Profiles.nick == nick_name) + .all() + ) + return result + + +def get_matched_info_by_email( + email: str, +) -> list[tuple[int, str, str, str, str, int]]: + result = ( + PG_SESSION.query( + Profiles.profileid, + Profiles.nick, + Profiles.firstname, + Profiles.lastname, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter(Users.email == email) + .all() + ) + return result + + +def get_matched_info_by_nick_and_email(nick_name: str, email: str): + result = ( + PG_SESSION.query( + Profiles.profileid, + Profiles.nick, + Profiles.firstname, + Profiles.lastname, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter(Users.email == email, Profiles.nick == nick_name) + .all() + ) + return result + + +def get_matched_info_by_uniquenick_and_namespaceid( + unique_nick: str, namespace_id: int +) -> list[tuple[int, str, str, str, str, int]]: + result = ( + PG_SESSION.query( + Profiles.profileid, + Profiles.nick, + Profiles.firstname, + Profiles.lastname, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter( + SubProfiles.uniquenick == unique_nick, + SubProfiles.namespaceid == namespace_id, + ) + .all() + ) + return result + + +def is_uniquenick_exist(unique_nick: str, namespace_id: int, game_name: str): + result = ( + PG_SESSION.query(Profiles) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter( + SubProfiles.uniquenick == unique_nick, + SubProfiles.gamename == game_name, + SubProfiles.namespaceid == namespace_id, + ) + .all() + ) + + return result + + +def is_email_exist(email: str): + result = PG_SESSION.query(Users.userid).filter(Users.email == email).count() + #! According to FSW partnerid is not nessesary + if result == 0: + return False + return True diff --git a/src/servers/presence_search_player/contracts/__init__.py b/src/servers/presence_search_player/contracts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/presence_search_player/contracts/requests.py b/src/servers/presence_search_player/contracts/requests.py new file mode 100644 index 000000000..352c17ab6 --- /dev/null +++ b/src/servers/presence_search_player/contracts/requests.py @@ -0,0 +1,293 @@ +from library.extentions.gamespy_utils import is_email_format_correct +from library.extentions.password_encoder import process_password +from servers.presence_search_player.abstractions.contracts import RequestBase +from servers.presence_search_player.enums.general import SearchType +from servers.presence_search_player.exceptions.general import ( + GPParseException, +) + + +class CheckRequest(RequestBase): + # \check\\nick\\email\\partnerid\0\passenc\\gamename\gmtest\final\ + nick: str + password: str + email: str + partner_id: int + + def __init__(self, raw_request: str) -> None: + super().__init__(raw_request) + + def parse(self): + super().parse() + self.password = process_password(self.request_dict) + if "nick" not in self.request_dict or "email" not in self.request_dict: + raise GPParseException("check request is incompelete.") + + if is_email_format_correct(self.request_dict["email"]): + raise GPParseException(" email format is incorrect.") + + self.nick = self.request_dict["nick"] + self.email = self.request_dict["email"] + + if "partner_id" in self.request_dict.keys(): + self.partner_id = int(self.request_dict["partner_id"]) + + +class NewUserRequest(RequestBase): + product_id: int + game_port: int + cd_key: str + has_game_name: bool + has_product_id: bool + has_cdkey: bool + has_partner_id: bool + has_game_port: bool + nick: str + email: str + password: str + partner_id: int + game_name: str + uniquenick: str + + def parse(self): + super().parse() + self.password = process_password(self.request_dict) + + if "nick" not in self.request_dict: + raise GPParseException("nickname is missing.") + if "email" not in self.request_dict: + raise GPParseException("email is missing.") + if not is_email_format_correct(self.request_dict["email"]): + raise GPParseException("email format is incorrect.") + self.nick = self.request_dict["nick"] + self.email = self.request_dict["email"] + + if "uniquenick" in self.request_dict and "namespaceid" in self.request_dict: + if "namespaceid" in self.request_dict: + try: + self.namespace_id = int(self.request_dict["namespaceid"]) + except ValueError: + raise GPParseException("namespaceid is incorrect.") + + self.uniquenick = self.request_dict["uniquenick"] + self.parse_other_info() + + def parse_other_info(self): + if "partnerid" in self.request_dict: + try: + self.partner_id = int(self.request_dict["partnerid"]) + self.has_partner_id_flag = True + except ValueError: + raise GPParseException("partnerid is incorrect.") + + if "productid" in self.request_dict: + try: + self.product_id = int(self.request_dict["productid"]) + self.has_product_id_flag = True + except ValueError: + raise GPParseException("productid is incorrect.") + + if "gamename" in self.request_dict: + self.has_game_name_flag = True + self.game_name = self.request_dict["gamename"] + + if "port" in self.request_dict: + try: + self.game_port = int(self.request_dict["port"]) + self.has_game_port_flag = True + except ValueError: + raise GPParseException("port is incorrect.") + + if "cdkey" in self.request_dict: + self.has_cd_key_enc_flag = True + self.cd_key = self.request_dict["cdkey"] + + +class NicksRequest(RequestBase): + password: str + email: str + is_require_uniquenicks: bool = True + + def parse(self): + super().parse() + self.password = process_password(self.request_dict) + if "email" not in self.request_dict.keys(): + raise GPParseException("email is missing.") + + self.email = self.request_dict["email"] + + if "pass" in self.request_dict.keys(): + self.is_require_uniquenicks = False + + +class OthersListRequest(RequestBase): + profile_ids: list[int] = [] + namespace_id: int = 0 + + def parse(self) -> None: + super().parse() + if "opids" not in self.request_dict or "namespaceid" not in self.request_dict: + raise GPParseException("opids or namespaceid is missing.") + + try: + self.profile_ids = [ + int(opid) for opid in self.request_dict["opids"].strip("|").split("|") + ] + except Exception as e: + raise GPParseException("opids is incorrect", e) + + +class OthersRequest(RequestBase): + profile_id: int = None + game_name: int + + def parse(self): + super().parse() + + if "gamename" not in self.request_dict: + raise GPParseException("gamename is missing.") + + self.game_name = self.request_dict["gamename"] + + if ( + "profileid" not in self.request_dict + or "namespaceid" not in self.request_dict + ): + raise GPParseException("profileid or namespaceid is missing.") + + if "profileid" not in self.request_dict: + raise GPParseException("profileid is incorrect.") + + try: + self.profile_id = int(self.request_dict["profileid"]) + except ValueError: + raise GPParseException("profileid is incorrect.") + + +class SearchRequest(RequestBase): + skip_num: int + request_type: SearchType + game_name: str + profile_id: int + partner_id: int + email: str + nick: str + uniquenick: str + + def parse(self) -> None: + super().parse() + + if ( + "profileid" not in self.request_dict + and "nick" not in self.request_dict + and "email" not in self.request_dict + and "namespaceid" not in self.request_dict + and "gamename" not in self.request_dict + ): + raise GPParseException("Search request is incomplete.") + + if "gamename" in self.request_dict: + self.game_name = self.request_dict["gamename"] + + if "profileid" in self.request_dict: + try: + self.profile_id = int(self.request_dict["profileid"]) + except ValueError: + raise GPParseException("profileid is incorrect.") + + if "partnerid" in self.request_dict: + try: + self.partner_id = int(self.request_dict["partnerid"]) + except ValueError: + raise GPParseException("partnerid is incorrect.") + + if "skip" in self.request_dict: + try: + self.skip_num = int(self.request_dict["skip"]) + except ValueError: + raise GPParseException("skip number is incorrect.") + + if "uniquenick" in self.request_dict and "namespaceid" in self.request_dict: + self.request_type = SearchType.UNIQUENICK_NAMESPACEID_SEARCH + self.uniquenick = self.request_dict["uniquenick"] + elif "nick" in self.request_dict and "email" in self.request_dict: + self.request_type = SearchType.NICK_EMAIL_SEARCH + self.nick = self.request_dict["nick"] + self.email = self.request_dict["email"] + elif "nick" in self.request_dict: + self.request_type = SearchType.NICK_SEARCH + self.nick = self.request_dict["nick"] + elif "email" in self.request_dict: + self.request_type = SearchType.EMAIL_SEARCH + self.email = self.request_dict["email"] + else: + raise GPParseException("unknown search type.") + + +class SearchUniqueRequest(RequestBase): + uniquenick: str + namespace_ids: list[int] = [] + + def parse(self): + super().parse() + + if ( + "uniquenick" not in self.request_dict + or "namespaces" not in self.request_dict + ): + raise GPParseException("searchunique request is incomplete.") + + try: + self.uniquenick = self.request_dict["uniquenick"] + except KeyError: + raise GPParseException("uniquenick is missing.") + + try: + self.namespace_ids = [ + int(namespace_id) + for namespace_id in self.request_dict["namespaces"] + .lstrip(",") + .split(",") + ] + except ValueError: + raise GPParseException("namespaces is incorrect.") + + +class UniqueSearchRequest(RequestBase): + preferred_nick: str + game_name: str + + def parse(self): + super().parse() + + if "preferrednick" not in self.request_dict: + raise GPParseException("preferrednick is missing.") + + self.preferred_nick = self.request_dict["preferrednick"] + + if "gamename" not in self.request_dict: + raise GPParseException("gamename is missing.") + + self.game_name = self.request_dict["gamename"] + + if "namespaceid" not in self.request_dict: + raise GPParseException("namespaceid is missing.") + + try: + self.namespace_id = int(self.request_dict["namespaceid"]) + except ValueError: + raise GPParseException("namespaceid is incorrect.") + + +class ValidRequest(RequestBase): + email: str + + def parse(self): + super().parse() + + if "email" not in self.request_dict or not is_email_format_correct( + self.request_dict["email"] + ): + raise GPParseException("valid request is incomplete.") + + self.email = self.request_dict["email"] diff --git a/src/servers/presence_search_player/contracts/responses.py b/src/servers/presence_search_player/contracts/responses.py new file mode 100644 index 000000000..f5add98e1 --- /dev/null +++ b/src/servers/presence_search_player/contracts/responses.py @@ -0,0 +1,178 @@ +from servers.presence_connection_manager.abstractions.contracts import ResponseBase +from servers.presence_search_player.contracts.requests import ( + CheckRequest, + NewUserRequest, + NicksRequest, + UniqueSearchRequest, + ValidRequest, +) +from servers.presence_search_player.contracts.results import ( + CheckResult, + NewUserResult, + NicksResult, + OthersListResult, + OthersResult, + SearchResult, + SearchUniqueResult, + UniqueSearchResult, + ValidResult, +) + + +class CheckResponse(ResponseBase): + _result: CheckResult + + def __init__(self, request: CheckRequest, result: CheckResult) -> None: + assert isinstance(request, CheckRequest) + assert isinstance(result, CheckResult) + super().__init__(request, result) + + def build(self): + if self._result.profile_id is None: + self.sending_buffer = f"\\cur\\1\\final\\" + else: + self.sending_buffer = f"\\cur\\0\\pid\\{self._result.profile_id}\\final\\" + + +class NewUserResponse(ResponseBase): + _result: NewUserResult + _request: NewUserRequest + + def __init__(self, request: NewUserRequest, result: NewUserResult) -> None: + assert isinstance(request, NewUserRequest) + assert isinstance(result, NewUserResult) + super().__init__(request, result) + + def build(self): + self.sending_buffer = ( + f"\\nur\\\\pid\\{self._result.subprofiles.profileid}\\final\\" + ) + + +class NicksResponse(ResponseBase): + _request: NicksRequest + _result: NicksResult + + def __init__(self, request: NicksRequest, result: NicksResult) -> None: + assert isinstance(request, NicksRequest) + assert isinstance(result, NicksResult) + + super().__init__(request, result) + + def build(self): + self.sending_buffer = "\\nr\\" + for info in self._result.data: + self.sending_buffer += f"\\nick\\{info.nick}" + if self._request.is_require_uniquenicks: + self.sending_buffer += f"\\uniquenick\\{info.uniquenick}" + self.sending_buffer += "\\ndone\\final\\" + + +class OthersListResponse(ResponseBase): + _result: OthersListResult + + def __init__(self, result: OthersListResult) -> None: + assert isinstance(result, OthersListResult) + super().__init__(None, result) + + def build(self): + self.sending_buffer = "\\otherslist\\" + for info in self._result.data: + self.sending_buffer += f"\\o\\{info.profile_id}" + self.sending_buffer += f"\\uniquenick\\{info.unique_nick}" + self.sending_buffer += "oldone" + + +class OthersResponse(ResponseBase): + _result: OthersResult + + def __init__(self, result: OthersResult) -> None: + assert isinstance(result, OthersResult) + super().__init__(None, result) + + def build(self): + self.sending_buffer = "\\others\\" + for info in self._result.data: + self.sending_buffer += f"\\o\\{info.profile_id}" + self.sending_buffer += f"\\nick\\{info.nick}" + self.sending_buffer += f"\\uniquenick\\{info.uniquenick}" + self.sending_buffer += f"\\first\\{info.firstname}" + self.sending_buffer += f"\\last\\{info.lastname}" + self.sending_buffer += f"\\email\\{info.email}" + self.sending_buffer += "\\odone\\final\\" + + +class SearchResponse(ResponseBase): + _result: SearchResult + + def __init__(self, result: SearchResult) -> None: + assert isinstance(result, SearchResult) + super().__init__(None, result) + + def build(self): + self.sending_buffer = f"\\bsr\\" + for info in self._result.result: + self.sending_buffer += str(info.profile_id) + self.sending_buffer += f"\\nick\\{info.nick}" + self.sending_buffer += f"\\uniquenick\\{info.uniquenick}" + self.sending_buffer += f"\\namespaceid\\{info.namespace_id}" + self.sending_buffer += f"\\firstname\\{info.firstname}" + self.sending_buffer += f"\\lastname\\{info.lastname}" + self.sending_buffer += f"\\email\\{info.email}" + self.sending_buffer += f"\\bsrdone\\\\more\\0\\final\\" + + +class SearchUniqueResponse(ResponseBase): + _result: SearchUniqueResult + + def __init__(self, result: SearchUniqueResult) -> None: + assert isinstance(result, SearchUniqueResult) + super().__init__(None, result) + + def build(self): + self.sending_buffer = "\\bsr" + for info in self._result.data: + self.sending_buffer += str(info.profile_id) + self.sending_buffer += f"\\nick\\{info.nick}" + self.sending_buffer += f"\\uniquenick\\{info.uniquenick}" + self.sending_buffer += f"\\namespaceid\\{info.namespace_id}" + self.sending_buffer += f"\\firstname\\{info.firstname}" + self.sending_buffer += f"\\lastname\\{info.lastname}" + self.sending_buffer += f"\\email\\{info.email}" + self.sending_buffer += "\\bsrdone\\\\more\\0" + + +class ValidResponse(ResponseBase): + _request: ValidRequest + _result: ValidResult + + def __init__(self, request: ValidRequest, result: ValidResult) -> None: + assert isinstance(request, ValidRequest) + assert isinstance(result, ValidResult) + super().__init__(request, result) + + def build(self): + if self._result.is_account_valid: + self.sendingbuffer = "\\vr\\1\\final\\" + else: + self.sendingbuffer = "\\vr\\0\\final\\" + + +class UniqueSearchResponse(ResponseBase): + _request: UniqueSearchRequest + _result: UniqueSearchResult + + def __init__( + self, request: UniqueSearchRequest, result: UniqueSearchResult + ) -> None: + assert isinstance(request, UniqueSearchRequest) + assert isinstance(result, UniqueSearchResult) + super().__init__(request, result) + + def build(self): + if self._result.is_uniquenick_exist: + self.sending_buffer = "\\us\\1\\nick\\Choose another name\\usdone\\final\\" + else: + self.sending_buffer = ( + f"\\us\\1\\nick\\{self._request.preferred_nick}\\usdone\\final\\" + ) diff --git a/src/servers/presence_search_player/contracts/results.py b/src/servers/presence_search_player/contracts/results.py new file mode 100644 index 000000000..b23da3985 --- /dev/null +++ b/src/servers/presence_search_player/contracts/results.py @@ -0,0 +1,96 @@ +from library.database.pg_orm import Profiles, SubProfiles, Users +from servers.presence_connection_manager.abstractions.contracts import ResultBase + + +class CheckResult(ResultBase): + profile_id: int + + +class NewUserResult(ResultBase): + user: Users + profile: Profiles + subprofiles: SubProfiles + + +class NickResultModel: + nick: str + uniquenick: str + + +class NicksResult(ResultBase): + data: list[NickResultModel] = [] + """ [ + (nick1, uniquenick1), + (nick2, uniquenick2), + (nick3, uniquenick3), + ... + ] + """ + is_require_uniquenicks: bool = False + + +class OthersListModel: + profile_id: int + unique_nick: str + + +class OthersListResult(ResultBase): + data: list[OthersListModel] = [] + """ + [ + (prifileid1,uniquenick1), + (prifileid2,uniquenick2), + (prifileid3,uniquenick3), + ... + ] + """ + + +class OthersResultModel: + profile_id: int + nick: str + uniquenick: str + lastname: str + firstname: str + user_id: int + email: str + + +class OthersResult(ResultBase): + data: list[OthersResultModel] = [] + + +class SearchResultDataModel: + profile_id: int + nick: str + uniquenick: str + email: str + firstname: str + lastname: str + namespace_id: int + + +class SearchResult(ResultBase): + result: list[SearchResultDataModel] = [] + + +class SearchUniqueResultModel: + profile_id: int + nick: str + uniquenick: str + email: str + firstname: str + lastname: str + namespace_id: int + + +class SearchUniqueResult(ResultBase): + data: list[SearchUniqueResultModel] = [] + + +class UniqueSearchResult(ResultBase): + is_uniquenick_exist: bool + + +class ValidResult(ResultBase): + is_account_valid: bool = False diff --git a/src/servers/presence_search_player/enums/error_codes.py b/src/servers/presence_search_player/enums/error_codes.py new file mode 100644 index 000000000..ee8d75879 --- /dev/null +++ b/src/servers/presence_search_player/enums/error_codes.py @@ -0,0 +1,119 @@ +from enum import Enum + + +from enum import IntEnum + + +class GPErrorCode(IntEnum): + # General. + GENERAL = 0x0000 + PARSE = 0x0001 + NOT_LOGGED_IN = 0x0002 + BAD_SESSION_KEY = 0x0003 + DATABASE_ERROR = 0x0004 + NETWORK = 0x0005 + FORCED_DISCONNECT = 0x0006 + CONNECTION_CLOSE = 0x0007 + UDP_LAYER = 0x0008 + + # Login. + LOGIN = 0x0100 + LOGIN_TIME_OUT = 0x0101 + LOGIN_BAD_NICK = 0x0102 + LOGIN_BAD_EMAIL = 0x0103 + LOGIN_BAD_PASSWORD = 0x0104 + LOGIN_BAD_PROFILE = 0x0105 + LOGIN_PROFILE_DELETED = 0x0106 + LOGIN_CONNECTION_FAILED = 0x0107 + LOGIN_SERVER_AUTH_FAILED = 0x0108 + LOGIN_BAD_UNIQUENICK = 0x0109 + LOGIN_BAD_PRE_AUTH = 0x010A + LOGIN_BAD_LOGIN_TICKET = 0x010B + LOGIN_TICKET_EXPIRED = 0x010C + + # Newuser. + NEW_USER = 0x0200 + NEW_USER_BAD_NICK = 0x0201 + NEW_USER_BAD_PASSWORDS = 0x0202 + NEW_USER_UNIQUENICK_INVALID = 0x0203 + NEW_USER_UNIQUENICK_IN_USE = 0x0204 + + # Updateui. + UPDATE_UI = 0x0300 + UPDATE_UI_BAD_EMAIL = 0x0301 + + # Newprofile. + NEW_PROFILE = 0x0400 + NEW_PROFILE_BAD_NICK = 0x0401 + NEW_PROFILE_BAD_OLD_NICK = 0x0402 + + # Updatepro. + UPDATE_PRO = 0x0500 + UPDATE_PRO_BAD_NICK = 0x0501 + + # Addbuddy. + ADD_BUDDY = 0x0600 + ADD_BUDDY_BAD_FORM = 0x0601 + ADD_BUDDY_BAD_NEW = 0x0602 + ADD_BUDDY_ALREADY_BUDDY = 0x0603 + + # Authadd. + AUTH_ADD = 0x0700 + AUTH_ADD_BAD_FORM = 0x0701 + AUTH_ADD_BAD_SIG = 0x0702 + + # Status. + STATUS = 0x0800 + + # Bm. + BM = 0x0900 + BM_NOT_BUDDY = 0x0901 + BM_EXT_INFO_NOT_SUPPORTED = 0x0902 + BM_BUDDY_OFFLINE = 0x0903 + + # Getprofile. + GET_PROFILE = 0x0A00 + GET_PROFILE_BAD_PROFILE = 0x0A01 + + # Delbuddy. + DEL_BUDDY = 0x0B00 + DEL_BUDDY_NOT_BUDDY = 0x0B01 + + # Delprofile. + DEL_PROFILE = 0x0C00 + DEL_PROFILE_LAST_PROFILE = 0x0C01 + + # Search. + SEARCH = 0x0D00 + SEARCH_CONNECTION_FAILED = 0x0D01 + SEARCH_TIME_OUT = 0x0D02 + + # Check. + CHECK = 0x0E00 + CHECK_BAD_MAIL = 0x0E01 + CHECK_BAD_NICK = 0x0E02 + CHECK_BAD_PASSWORD = 0x0E03 + + # Revoke. + REVOKE = 0x0F00 + REVOKE_NOT_BUDDY = 0x0F01 + + # Registeruniquenick. + REGISTER_UNIQUENICK = 0x1000 + REGISTER_UNIQUENICK_TAKEN = 0x1001 + REGISTER_UNIQUENICK_RESERVED = 0x1002 + REGISTER_UNIQUENICK_BAD_NAMESPACE = 0x1003 + + # Register UniSpy.Server.CDkey. + REGISTER_CDKEY = 0x1100 + REGISTER_CDKEY_BAD_KEY = 0x1101 + REGISTER_CDKEY_ALREADY_SET = 0x1102 + REGISTER_CDKEY_ALREADY_TAKEN = 0x1103 + + # AddBlock. + ADD_BLOCK = 0x1200 + ADD_BLOCK_ALREADY_BLOCKED = 0x1201 + + # RemoveBlock. + REMOVE_BLOCK = 0x1300 + REMOVE_BLOCK_NOT_BLOCKED = 0x1301 diff --git a/src/servers/presence_search_player/enums/general.py b/src/servers/presence_search_player/enums/general.py new file mode 100644 index 000000000..2e2108f0b --- /dev/null +++ b/src/servers/presence_search_player/enums/general.py @@ -0,0 +1,8 @@ +from enum import IntEnum + + +class SearchType(IntEnum): + NICK_SEARCH = 0 + NICK_EMAIL_SEARCH = 1 + UNIQUENICK_NAMESPACEID_SEARCH = 2 + EMAIL_SEARCH = 3 diff --git a/src/servers/presence_search_player/exceptions/general.py b/src/servers/presence_search_player/exceptions/general.py new file mode 100644 index 000000000..1ba02d9c7 --- /dev/null +++ b/src/servers/presence_search_player/exceptions/general.py @@ -0,0 +1,433 @@ +from library.exceptions.error import UniSpyException +from servers.presence_search_player.enums.error_codes import GPErrorCode +from library.abstractions.contracts import ResponseBase + + +class GPException(UniSpyException, ResponseBase): + error_code: GPErrorCode + sending_buffer: str + + def __init__( + self, + message: str = "General error.", + error_code: GPErrorCode = GPErrorCode.GENERAL, + ) -> None: + UniSpyException.__init__(self, message) + self.error_code = error_code + + def build(self) -> str: + self.sending_buffer = f"\\error\\\\err\\{int(self.error_code)}\\fatal\\\\errmsg\\{self.message}\\final\\" + + +class GPParseException(GPException): + def __init__( + self, + message: str = "Request parsing error.", + error_code: GPErrorCode = GPErrorCode.PARSE, + ) -> None: + super().__init__(message, error_code) + + +class GPUdpLayerException(GPException): + def __init__( + self, + message: str = "Unknown UDP layer error.", + error_code: GPErrorCode = GPErrorCode.UDP_LAYER, + ) -> None: + super().__init__(message, error_code) + + +class GPNotLoginException(GPException): + def __init__( + self, + message: str = "You are not logged in, please login first.", + error_code: GPErrorCode = GPErrorCode.NOT_LOGGED_IN, + ) -> None: + super().__init__(message, error_code) + + +class GPNetworkException(GPException): + def __init__( + self, + message: str = "Unknown network error.", + error_code: GPErrorCode = GPErrorCode.NETWORK, + ) -> None: + super().__init__(message, error_code) + + +class GPForceDisconnectException(GPException): + def __init__( + self, + message: str = "Client is forced to disconnect.", + error_code: GPErrorCode = GPErrorCode.FORCED_DISCONNECT, + ) -> None: + super().__init__(message, error_code) + + +class GPDatabaseException(GPException): + def __init__( + self, + message: str = "Database error.", + error_code: GPErrorCode = GPErrorCode.DATABASE_ERROR, + ) -> None: + super().__init__(message, error_code) + + +class GPConnectionCloseException(GPException): + def __init__( + self, + message: str = "Client connection accidently closed.", + error_code: GPErrorCode = GPErrorCode.CONNECTION_CLOSE, + ) -> None: + super().__init__(message, error_code) + + +class GPBadSessionKeyException(GPException): + def __init__( + self, + message: str = "Session key is invalid.", + error_code: GPErrorCode = GPErrorCode.BAD_SESSION_KEY, + ) -> None: + super().__init__(message, error_code) + + +class GPAddBuddyException(GPException): + def __init__( + self, + message: str = "Unknown error occur at add buddy.", + error_code: GPErrorCode = GPErrorCode.ADD_BUDDY, + ) -> None: + super().__init__(message, error_code) + + +class GPAddBuddyAlreadyException(GPAddBuddyException): + def __init__( + self, + message: str = "The buddy you are adding is already in your buddy list.", + error_code: GPErrorCode = GPErrorCode.ADD_BUDDY_ALREADY_BUDDY, + ) -> None: + super().__init__(message, error_code) + + +class GPAddBuddyBadFormatException(GPAddBuddyException): + def __init__( + self, + message: str = "Add buddy format invalid.", + error_code: GPErrorCode = GPErrorCode.ADD_BUDDY_BAD_FORM, + ) -> None: + super().__init__(message, error_code) + + +class GPAddBuddyBadNewException(GPAddBuddyException): + def __init__( + self, + message: str = "The buddy name provided is invalid.", + error_code: GPErrorCode = GPErrorCode.ADD_BUDDY_BAD_NEW, + ) -> None: + super().__init__(message, error_code) + + +class AuthAddException(GPException): + def __init__( + self, + message: str = "The adding of authentication failed.", + error_code: GPErrorCode = GPErrorCode.AUTH_ADD, + ) -> None: + super().__init__(message, error_code) + + +class AuthAddBadFormatException(AuthAddException): + def __init__( + self, + message: str = "The authentication is in bad form.", + error_code: GPErrorCode = GPErrorCode.AUTH_ADD_BAD_FORM, + ) -> None: + super().__init__(message, error_code) + + +class AuthAddBadSigException(AuthAddException): + def __init__( + self, + message: str = "The signature in authentication is invalid.", + error_code: GPErrorCode = GPErrorCode.AUTH_ADD_BAD_SIG, + ) -> None: + super().__init__(message, error_code) + + +class GPBuddyMsgException(GPException): + def __init__( + self, + message: str = "Unknown error occur when processing buddy message.", + error_code: GPErrorCode = GPErrorCode.BM, + ) -> None: + super().__init__(message, error_code) + + +class GPBuddyMsgExtInfoNotSupportedException(GPBuddyMsgException): + def __init__( + self, + message: str = "Buddy message is not supported.", + error_code: GPErrorCode = GPErrorCode.BM_EXT_INFO_NOT_SUPPORTED, + ) -> None: + super().__init__(message, error_code) + + +class GPBuddyMsgNotBuddyException(GPBuddyMsgException): + def __init__( + self, + message: str = "The message receiver is not your buddy.", + error_code: GPErrorCode = GPErrorCode.BM_NOT_BUDDY, + ) -> None: + super().__init__(message, error_code) + + +class CheckException(GPException): + def __init__( + self, + message: str = "There was an error checking the user account.", + error_code: GPErrorCode = GPErrorCode.CHECK, + ) -> None: + super().__init__(message, error_code) + + def build(self) -> str: + self.sending_buffer = f"\\cur\\{int(self.error_code)}\\final\\" + + +class GPLoginException(GPException): + def __init__( + self, + message: str = "Unknown login error.", + error_code: GPErrorCode = GPErrorCode.LOGIN, + ) -> None: + super().__init__(message, error_code) + + +class GPLoginBadEmailException(GPLoginException): + def __init__( + self, + message: str = "Email provided is invalid.", + error_code: GPErrorCode = GPErrorCode.LOGIN_BAD_EMAIL, + ) -> None: + super().__init__(message, error_code) + + +class GPLoginBadLoginTicketException(GPLoginException): + def __init__( + self, + message: str = "The login ticket is invalid.", + error_code: GPErrorCode = GPErrorCode.LOGIN_TICKET_EXPIRED, + ) -> None: + super().__init__(message, error_code) + + +class GPLoginBadNickException(GPLoginException): + def __init__( + self, + message: str = "Nickname is in valid.", + error_code: GPErrorCode = GPErrorCode.LOGIN_BAD_NICK, + ) -> None: + super().__init__(message, error_code) + + +class GPLoginBadPasswordException(GPLoginException): + def __init__( + self, + message: str = "Password provided is invalid.", + error_code: GPErrorCode = GPErrorCode.LOGIN_BAD_PASSWORD, + ) -> None: + super().__init__(message, error_code) + + +class GPLoginBadPreAuthException(GPLoginException): + def __init__( + self, + message: str = "Login pre-authentication failed.", + error_code: GPErrorCode = GPErrorCode.LOGIN_BAD_PRE_AUTH, + ) -> None: + super().__init__(message, error_code) + + +class GPLoginBadProfileException(GPLoginException): + def __init__( + self, + message: str = "User profile is damaged.", + error_code: GPErrorCode = GPErrorCode.LOGIN_BAD_PROFILE, + ) -> None: + super().__init__(message, error_code) + + +class GPLoginBadUniquenickException(GPLoginException): + def __init__( + self, + message: str = "The uniquenick provided is invalid.", + error_code: GPErrorCode = GPErrorCode.LOGIN_BAD_UNIQUENICK, + ) -> None: + super().__init__(message, error_code) + + +class GPLoginConnectionFailedException(GPLoginException): + def __init__( + self, + message: str = "Login connection failed.", + error_code: GPErrorCode = GPErrorCode.LOGIN_CONNECTION_FAILED, + ) -> None: + super().__init__(message, error_code) + + +class GPLoginProfileDeletedException(GPLoginException): + def __init__( + self, + message: str = "Login connection failed.", + error_code: GPErrorCode = GPErrorCode.LOGIN_PROFILE_DELETED, + ) -> None: + super().__init__(message, error_code) + + +class GPLoginServerAuthFailedException(GPLoginException): + def __init__( + self, + message: str = "Login server authentication failed.", + error_code: GPErrorCode = GPErrorCode.LOGIN_SERVER_AUTH_FAILED, + ) -> None: + super().__init__(message, error_code) + + +class GPLoginTicketExpiredException(GPLoginException): + def __init__( + self, + message: str = "The login ticket have expired.", + error_code: GPErrorCode = GPErrorCode.LOGIN_TICKET_EXPIRED, + ) -> None: + super().__init__(message, error_code) + + +class GPLoginTimeOutException(GPLoginException): + def __init__( + self, + message: str = "Login timeout.", + error_code: GPErrorCode = GPErrorCode.LOGIN_TIME_OUT, + ) -> None: + super().__init__(message, error_code) + + +class GPNewProfileException(GPException): + def __init__( + self, + message: str = "An unknown error occur when creating new profile.", + error_code: GPErrorCode = GPErrorCode.NEW_PROFILE, + ) -> None: + super().__init__(message, error_code) + + +class GPNewProfileBadNickException(GPNewProfileException): + def __init__( + self, + message: str = "Nickname is invalid at creating new profile.", + error_code: GPErrorCode = GPErrorCode.NEW_PROFILE_BAD_NICK, + ) -> None: + super().__init__(message, error_code) + + +class GPNewProfileBadOldNickException(GPNewProfileException): + def __init__( + self, + message: str = "There is an already exist nickname.", + error_code: GPErrorCode = GPErrorCode.NEW_PROFILE_BAD_OLD_NICK, + ) -> None: + super().__init__(message, error_code) + + +class GPNewUserException(GPException): + def __init__( + self, + message: str = "There was an unknown error creating user account.", + error_code: GPErrorCode = GPErrorCode.NEW_USER, + ) -> None: + super().__init__(message, error_code) + + +class GPNewUserBadNickException(GPException): + def __init__( + self, + message: str = "The nickname provided is invalid.", + error_code: GPErrorCode = GPErrorCode.NEW_USER_BAD_NICK, + ) -> None: + super().__init__(message, error_code) + + +class GPNewUserBadPasswordException(GPException): + def __init__( + self, + message: str = "Password is invalid.", + error_code: GPErrorCode = GPErrorCode.NEW_USER_BAD_PASSWORDS, + ) -> None: + super().__init__(message, error_code) + + +class GPNewUserUniquenickInUseException(GPException): + def __init__( + self, + message: str = "Uniquenick is in use.", + error_code: GPErrorCode = GPErrorCode.NEW_USER_UNIQUENICK_IN_USE, + ) -> None: + super().__init__(message, error_code) + + +class GPNewUserUniquenickInvalidException(GPException): + def __init__( + self, + message: str = "Uniquenick is invalid.", + error_code: GPErrorCode = GPErrorCode.NEW_USER_UNIQUENICK_INVALID, + ) -> None: + super().__init__(message, error_code) + + +class GPStatusException(GPException): + def __init__( + self, + message: str = "Unknown error happen when processing player status.", + error_code: GPErrorCode = GPErrorCode.STATUS, + ) -> None: + super().__init__(message, error_code) + + +class GPUpdateProfileException(GPException): + def __init__( + self, + message: str = "Update profile unknown error.", + error_code: GPErrorCode = GPErrorCode.UPDATE_PRO, + ) -> None: + super().__init__(message, error_code) + + +class GPUpdateProBadNickException(GPUpdateProfileException): + def __init__( + self, + message: str = "Nickname is invalid for updating profile.", + error_code: GPErrorCode = GPErrorCode.UPDATE_PRO_BAD_NICK, + ) -> None: + super().__init__(message, error_code) + + +class GPUpdateUIException(GPException): + def __init__( + self, + message: str = "Update user info unknown error.", + error_code: GPErrorCode = GPErrorCode.UPDATE_UI, + ) -> None: + super().__init__(message, error_code) + + +class GPUpdateUIBadEmailException(GPException): + def __init__( + self, + message: str = "Email is invalid.", + error_code: GPErrorCode = GPErrorCode.UPDATE_UI_BAD_EMAIL, + ) -> None: + super().__init__(message, error_code) + + +MAPPING = { + "GPException":GPException, + +} diff --git a/src/servers/presence_search_player/handlers/handlers.py b/src/servers/presence_search_player/handlers/handlers.py new file mode 100644 index 000000000..35e8ea62e --- /dev/null +++ b/src/servers/presence_search_player/handlers/handlers.py @@ -0,0 +1,102 @@ +from servers.presence_connection_manager.abstractions.handler import CmdHandlerBase +from servers.presence_connection_manager.applications.client import Client +from servers.presence_connection_manager.contracts.responses.general import NewUserResponse +from servers.presence_search_player.contracts.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest +from servers.presence_search_player.contracts.responses import CheckResponse, NicksResponse, OthersListResponse, OthersResponse, SearchResponse, SearchUniqueResponse, UniqueSearchResponse, ValidResponse +from servers.presence_search_player.contracts.results import CheckResult, NewUserResult, NicksResult + + + +class CheckHandler(CmdHandlerBase): + _request: CheckRequest + _result: CheckResult + _response: CheckResponse + + def __init__(self, client: Client, request: CheckRequest) -> None: + assert isinstance(request, CheckRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = CheckResponse(self._request, self._result) + + +class NewUserHandler(CmdHandlerBase): + _request: NewUserRequest + _result: NewUserResult + _response: NewUserResponse + + def __init__(self, client: Client, request: NewUserRequest) -> None: + assert isinstance(request, NewUserRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = NewUserResponse(self._request, self._result) +class NicksHandler(CmdHandlerBase): + _request: NicksRequest + _result: NicksResult + _response: NicksResponse + + def __init__(self, client: Client, request: NicksRequest) -> None: + assert isinstance(request, NicksRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = NicksResponse(self._request, self._result) + +class OthersHandler(CmdHandlerBase): + def __init__(self, client: Client, request: OthersRequest) -> None: + assert isinstance(request, OthersRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = OthersResponse(self._result) + +class OthersListHandler(CmdHandlerBase): + def __init__(self, client: Client, request: OthersListRequest) -> None: + assert isinstance(request, OthersListRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = OthersListResponse(self._result) + +class SearchHandler(CmdHandlerBase): + """ + last one we search with email this may get few profile so we can not return GPErrorCode + SearchWithEmail(client,dict ); + \search\\sesskey\0\profileid\0\namespaceid\1\partnerid\0\nick\mycrysis\uniquenick\xiaojiuwo\email\koujiangheng@live.cn\gamename\gmtest\final\ + \bsrdone\more\\final\ + string sendingbuffer = + "\\bsr\\1\\nick\\mycrysis\\uniquenick\\1\\namespaceid\\0\\firstname\\jiangheng\\lastname\\kou\\email\\koujiangheng@live.cn\\bsrdone\\0\\final\\"; + client.Stream.SendAsync(sendingbuffer); + \more\\final\ + \search\sesskey\0\profileid\0\namespaceid\0\nick\gbr359_jordips\gamename\gbrome\final\ + """ + def __init__(self, client: Client, request: SearchRequest) -> None: + assert isinstance(request, SearchRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = SearchResponse(self._result) + + +class SearchUniqueHandler(CmdHandlerBase): + def __init__(self, client: Client, request: SearchUniqueRequest) -> None: + assert isinstance(request, SearchUniqueRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = SearchUniqueResponse(self._result) +class UniqueSearchHandler(CmdHandlerBase): + def __init__(self, client: Client, request: UniqueSearchRequest) -> None: + assert isinstance(request, UniqueSearchRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = UniqueSearchResponse(self._request, self._result) +class ValidHandler(CmdHandlerBase): + def __init__(self, client: Client, request: ValidRequest) -> None: + assert isinstance(request, ValidRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = ValidResponse(self._request, self._result) diff --git a/src/servers/presence_search_player/handlers/switcher.py b/src/servers/presence_search_player/handlers/switcher.py new file mode 100644 index 000000000..93c0728ec --- /dev/null +++ b/src/servers/presence_search_player/handlers/switcher.py @@ -0,0 +1,59 @@ +from servers.presence_connection_manager.handlers.general import NewUserHandler +from servers.presence_search_player.abstractions.handler import CmdHandlerBase +from servers.presence_search_player.applications.client import Client +from servers.presence_search_player.contracts.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest + +from library.abstractions.switcher import SwitcherBase +from servers.presence_search_player.handlers.handlers import CheckHandler, NicksHandler, OthersHandler, OthersListHandler, SearchHandler, SearchUniqueHandler, UniqueSearchHandler, ValidHandler + + +class CmdSwitcher(SwitcherBase): + def __init__(self, client: Client, raw_request: str): + super().__init__(client, raw_request) + + @property + def _raw_request(self) -> str: + return super()._raw_request + + @property + def _client(self) -> Client: + return super()._client + + def process_raw_request(self): + if self._raw_request[0] != "\\": + self._client.log_info("Invalid request received!") + return + raw_requests = self._raw_request.split("\\final\\") + for raw_request in raw_requests: + name, request = raw_request.strip("\\").split("\\", 1) + self._requests.append((name, request)) + + def create_cmd_handlers(self, name: str, raw_request: str) -> CmdHandlerBase: + match name: + case "check": + return CheckHandler(self._client, CheckRequest(raw_request)) + case "newuser": + return NewUserHandler(self._client, NewUserRequest(raw_request)) + case "nicks": + return NicksHandler(self._client, NicksRequest(raw_request)) + case "others": + return OthersHandler(self._client, OthersRequest(raw_request)) + case "otherslist": + return OthersListHandler(self._client, OthersListRequest(raw_request)) + case "pmatch": + # return PMatchHandler(self._client, PMatchRequest(raw_request)) + raise NotImplementedError() + case "search": + return SearchHandler(self._client, SearchRequest(raw_request)) + case "searchunique": + return SearchUniqueHandler( + self._client, SearchUniqueRequest(raw_request) + ) + case "uniquesearch": + return UniqueSearchHandler( + self._client, UniqueSearchRequest(raw_request) + ) + case "valid": + return ValidHandler(self._client, ValidRequest(raw_request)) + case _: + return None diff --git a/src/servers/query_report/aggregates/game_server_info.py b/src/servers/query_report/aggregates/game_server_info.py new file mode 100644 index 000000000..608ee544a --- /dev/null +++ b/src/servers/query_report/aggregates/game_server_info.py @@ -0,0 +1,25 @@ +from datetime import datetime +import socket +from uuid import UUID + +from servers.query_report.v2.enums.general import GameServerStatus + + +class GameServerInfo: + server_id: UUID + host_ip_address: str + instant_key: int + game_name: str + query_report_port: int + + last_heart_beart_received_time: datetime + status: GameServerStatus + server_data: dict[str, str] + player_data: list[dict[str, str]] + team_data: list[dict[str, str]] + @property + def query_report_port_bytes(self) -> bytes: + return self.query_report_port.to_bytes(2, "big") + @property + def host_ip_address_bytes(self) -> bytes: + return socket.inet_aton(self.host_ip_address) diff --git a/src/servers/query_report/aggregates/natneg_cookie.py b/src/servers/query_report/aggregates/natneg_cookie.py new file mode 100644 index 000000000..74133f70b --- /dev/null +++ b/src/servers/query_report/aggregates/natneg_cookie.py @@ -0,0 +1,13 @@ + +from attr import dataclass + + +@dataclass +class NatNegCookie: + host_ip:str + host_port:int + heartbeat_ip:str + heartbeat_port:int + game_name:str + natneg_message:bytes + instant_key:int \ No newline at end of file diff --git a/src/servers/query_report/aggregates/peer_room_info.py b/src/servers/query_report/aggregates/peer_room_info.py new file mode 100644 index 000000000..3e2933d16 --- /dev/null +++ b/src/servers/query_report/aggregates/peer_room_info.py @@ -0,0 +1,101 @@ +from uuid import UUID + + +{ + "groupid": "groupid", + "hostname": "hostname", + "number_of_waiting_player": "numwaiting", + "max_number_of_waiting_players": "maxwaiting", + "number_of_servers": "numservers", + "number_of_players": "numplayers", + "max_number_of_players": "maxplayers", + "password": "password", + "number_of_games": "numgames", + "number_of_playing_players": "numplaying", +} + + +class PeerRoomInfo: + server_id: UUID + game_name: str + group_id: int + room_name: str + + raw_key_values: dict[str, str] + + def __init__(self, game_name, group_id, room_name) -> None: + self.game_name = game_name + self.group_id = group_id + self.room_name = room_name + self.raw_key_values = { + "groupid": group_id, + "hostname": room_name, + "numwaiting": 0, + "maxwaiting": 200, + "numservers": 0, + "numplayers": 0, + "maxplayers": 200, + "password": "", + "numgames": 0, + "numplaying": 0, + } + + @property + def number_of_servers(self) -> int: + return int(self.raw_key_values["numservers"]) + + @number_of_servers.setter + def number_of_servers(self, value: int): + assert isinstance(value, int) + self.raw_key_values["numservers"] = value + + @property + def number_of_players(self) -> int: + return int(self.raw_key_values["numplayers"]) + + @number_of_players.setter + def number_of_players(self, value: int): + assert isinstance(value, int) + self.raw_key_values["numplayers"] = value + + @property + def max_number_of_waiting_players(self) -> int: + return int(self.raw_key_values["maxwaiting"]) + + @max_number_of_waiting_players.setter + def max_number_of_waiting_players(self, value: int): + assert isinstance(value, int) + self.raw_key_values["maxwaiting"] = value + + @property + def max_number_of_players(self) -> int: + return int(self.raw_key_values["maxplayers"]) + + @max_number_of_players.setter + def max_number_of_players(self, value: int): + assert isinstance(value, int) + self.raw_key_values["maxplayers"] = value + + @property + def number_of_games(self) -> int: + return int(self.raw_key_values["numgames"]) + + @number_of_games.setter + def number_of_games(self, value: int): + assert isinstance(value, int) + self.raw_key_values["numgames"] = value + + @property + def number_of_playing_players(self) -> int: + return self.raw_key_values["numplaying"] + + @number_of_playing_players.setter + def number_of_playing_players(self, value: int): + assert isinstance(value, int) + self.raw_key_values["numplaying"] = value + + def get_gamespy_format_data(self): + """ + convert everything to string + """ + return {key: str(value) for key, value in self.raw_key_values.items()} diff --git a/src/servers/query_report/applications/client.py b/src/servers/query_report/applications/client.py new file mode 100644 index 000000000..55f04e221 --- /dev/null +++ b/src/servers/query_report/applications/client.py @@ -0,0 +1,15 @@ +from library.abstractions.client import ClientBase + +# import servers.query_report.v1 +from servers.query_report.v2.applications.switcher import CmdSwitcher as V2CmdSwitcher + + +class Client(ClientBase): + is_log_raw: bool = True + + def _create_switcher(self, buffer): + if buffer[0] == ord("\\"): + raise NotImplementedError("v1 protocol not implemented") + return V1CmdSwitcher(self, (buffer)) + else: + return V2CmdSwitcher(self, buffer) diff --git a/src/servers/query_report/applications/data.py b/src/servers/query_report/applications/data.py new file mode 100644 index 000000000..519e954ca --- /dev/null +++ b/src/servers/query_report/applications/data.py @@ -0,0 +1,31 @@ +from library.database.pg_orm import PG_SESSION, GroupList, Games + + +def get_all_groups(): + result: list[tuple[Games, GroupList]] = ( + PG_SESSION.query(Games, GroupList) + .join(GroupList, Games.gameid == GroupList.gameid) + .all() + ) + + # Group the results by Game name + grouped_result = {} + for game, group in result: + if game.gamename not in grouped_result: + grouped_result[game.gamename] = [] + grouped_result[game.gamename].append( + { + "game_id": group.gameid, + "game_name": game.gamename, + "group_id": group.groupid, + "room_name": group.roomname, + "secret_key": game.secretkey, + } + ) + + # Convert the grouped result to the desired format + return grouped_result + + +if __name__ == "__main__": + get_all_groups() diff --git a/src/servers/query_report/exceptions/exceptions.py b/src/servers/query_report/exceptions/exceptions.py new file mode 100644 index 000000000..94aa0e2d8 --- /dev/null +++ b/src/servers/query_report/exceptions/exceptions.py @@ -0,0 +1,5 @@ +from library.exceptions.error import UniSpyException + + +class QRException(UniSpyException): + pass diff --git a/src/servers/query_report/v1/__init__.py b/src/servers/query_report/v1/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/query_report/v1/abstractions/__init__.py b/src/servers/query_report/v1/abstractions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/query_report/v1/abstractions/contracts.py b/src/servers/query_report/v1/abstractions/contracts.py new file mode 100644 index 000000000..673de7213 --- /dev/null +++ b/src/servers/query_report/v1/abstractions/contracts.py @@ -0,0 +1,29 @@ +import library.abstractions.contracts +import abc + +from library.extentions.gamespy_utils import convert_to_key_value + + +class RequestBase(library.abstractions.contracts.RequestBase, abc.ABC): + request_dict: dict[str, str] = {} + + def __init__(self, raw_request: str) -> None: + assert isinstance(raw_request, str) + super().__init__(raw_request) + + def parse(self) -> None: + self.request_dict = convert_to_key_value(self.raw_request) + self.command_name = self.request_dict.keys()[0] + + +class ResultBase(library.abstractions.contracts.ResultBase, abc.ABC): + pass + + +class ResponseBase(library.abstractions.contracts.ResponseBase): + sending_buffer: str + + def __init__(self, request: RequestBase, result: ResultBase) -> None: + assert issubclass(type(request), RequestBase) + assert issubclass(type(result), ResultBase) + super().__init__(request, result) diff --git a/src/servers/query_report/v1/abstractions/handlers.py b/src/servers/query_report/v1/abstractions/handlers.py new file mode 100644 index 000000000..5a1595299 --- /dev/null +++ b/src/servers/query_report/v1/abstractions/handlers.py @@ -0,0 +1,10 @@ +import library.abstractions.handler +from servers.query_report.v1.abstractions.contracts import RequestBase +from servers.query_report.applications.client import Client + + +class CmdHandlerBase(library.abstractions.handler.CmdHandlerBase): + def __init__(self, client: Client, request: RequestBase) -> None: + assert issubclass(type(request), RequestBase) + assert isinstance(client, Client) + super().__init__(client, request) diff --git a/src/servers/query_report/v1/aggregations/game_server_info_v1.py b/src/servers/query_report/v1/aggregations/game_server_info_v1.py new file mode 100644 index 000000000..da5578cef --- /dev/null +++ b/src/servers/query_report/v1/aggregations/game_server_info_v1.py @@ -0,0 +1,18 @@ +from mongoengine import ( + Document, + StringField, + IntField, + UUIDField, + BooleanField, + DictField, +) + + +class GameServerInfoV1(Document): + server_id = UUIDField(binary=False, required=True) + host_ip_address = StringField(required=True) + host_port = IntField(required=True) + game_name = StringField(required=True) + is_validated = BooleanField(required=True) + server_data = DictField(required=True) + meta = {"expireAfterSeconds": 30} diff --git a/src/servers/query_report/v2/abstractions/cmd_handler_base.py b/src/servers/query_report/v2/abstractions/cmd_handler_base.py new file mode 100644 index 000000000..cdca69808 --- /dev/null +++ b/src/servers/query_report/v2/abstractions/cmd_handler_base.py @@ -0,0 +1,11 @@ +import abc +from library.abstractions.handler import CmdHandlerBase as CHB +from servers.query_report.v2.abstractions.request_base import RequestBase +from servers.query_report.applications.client import Client + + +class CmdHandlerBase(CHB, abc.ABC): + def __init__(self, client: Client, request: RequestBase) -> None: + assert issubclass(type(request), RequestBase) + assert isinstance(client, Client) + super().__init__(client, request) diff --git a/src/servers/query_report/v2/abstractions/request_base.py b/src/servers/query_report/v2/abstractions/request_base.py new file mode 100644 index 000000000..f48dfff2a --- /dev/null +++ b/src/servers/query_report/v2/abstractions/request_base.py @@ -0,0 +1,21 @@ +import library.abstractions.contracts +from servers.query_report.exceptions.exceptions import QRException +from servers.query_report.v2.enums.general import RequestType + +MAGIC_DATA = [0xFE, 0xFD] + + +class RequestBase(library.abstractions.contracts.RequestBase): + instant_key: int + command_name: RequestType + raw_request: bytes + + def __init__(self, raw_request: bytes) -> None: + assert isinstance(raw_request, bytes) + super().__init__(raw_request) + + def parse(self): + if len(self.raw_request) < 3: + raise QRException + self.command_name = RequestType(self.raw_request[0]) + self.instant_key = int(self.raw_request[1:5]) diff --git a/src/servers/query_report/v2/abstractions/response_base.py b/src/servers/query_report/v2/abstractions/response_base.py new file mode 100644 index 000000000..797f56d34 --- /dev/null +++ b/src/servers/query_report/v2/abstractions/response_base.py @@ -0,0 +1,9 @@ +import abc +import library.abstractions.contracts +from servers.query_report.v2.contracts.requests import RequestBase +from servers.query_report.v2.contracts.results import ResultBase + + +class ResponseBase(library.abstractions.contracts.ResponseBase, abc.ABC): + _result: ResultBase + _request: RequestBase diff --git a/src/servers/query_report/v2/abstractions/result_base.py b/src/servers/query_report/v2/abstractions/result_base.py new file mode 100644 index 000000000..c911bbadd --- /dev/null +++ b/src/servers/query_report/v2/abstractions/result_base.py @@ -0,0 +1,7 @@ +import abc +import library.abstractions.contracts +from servers.query_report.v2.enums.general import PacketType + + +class ResultBase(library.abstractions.contracts.ResultBase, abc.ABC): + packet_type: PacketType = None diff --git a/src/servers/query_report/v2/aggregates/game_server_info_v2.py b/src/servers/query_report/v2/aggregates/game_server_info_v2.py new file mode 100644 index 000000000..39a5e4ce9 --- /dev/null +++ b/src/servers/query_report/v2/aggregates/game_server_info_v2.py @@ -0,0 +1,46 @@ +from datetime import datetime, timezone +import uuid +from mongoengine import ( + Document, + StringField, + IntField, + UUIDField, + DateTimeField, + EnumField, + ListField, + DictField, +) + +from library.database.mongodb_orm import connect_to_db, get_ttl_param +from servers.query_report.v2.enums.general import GameServerStatus + + +class GameServerInfoV2(Document): + created = DateTimeField(default=datetime.now(timezone.utc)) + server_id = UUIDField(binary=False, required=True) + host_ip_address = StringField(required=True) + instant_key = IntField(required=True) + last_package_received_time = DateTimeField(required=True) + server_status = EnumField(GameServerStatus, required=True) + server_data = DictField(required=True) + query_report_port = IntField() + player_data = ListField(DictField()) + team_data = ListField(DictField()) + meta = get_ttl_param(30) + """expire after 30 seconds""" + + +if __name__ == "__main__": + connect_to_db() + result = GameServerInfoV2.objects().first() + if result is None: + dd = GameServerInfoV2( + server_id=uuid.UUID("212b7218-1758-11ef-94cc-70a8d36da155"), + host_ip_address="192.168.0.1", + instant_key=123, + last_package_received_time=datetime.now(timezone.utc), + server_status=GameServerStatus.NORMAL, + server_data={"hello": "hi"}, + ) + dd.save() + pass diff --git a/src/servers/query_report/v2/aggregates/natneg_cookie.py b/src/servers/query_report/v2/aggregates/natneg_cookie.py new file mode 100644 index 000000000..d02e72da8 --- /dev/null +++ b/src/servers/query_report/v2/aggregates/natneg_cookie.py @@ -0,0 +1,12 @@ +from attr import dataclass + + +@dataclass +class NatNegCookie: + ipaddress:str + host_port:int + heartbeat_ip:str + heartbeat_port:int + game_name:str + natneg_message:bytes + instant_key:int \ No newline at end of file diff --git a/src/servers/query_report/v2/applications/data.py b/src/servers/query_report/v2/applications/data.py new file mode 100644 index 000000000..8b1378917 --- /dev/null +++ b/src/servers/query_report/v2/applications/data.py @@ -0,0 +1 @@ + diff --git a/src/servers/query_report/v2/applications/switcher.py b/src/servers/query_report/v2/applications/switcher.py new file mode 100644 index 000000000..20d4f002a --- /dev/null +++ b/src/servers/query_report/v2/applications/switcher.py @@ -0,0 +1,55 @@ +from library.abstractions.switcher import SwitcherBase +from library.exceptions.error import UniSpyException +from servers.presence_connection_manager.contracts.requests.general import ( + KeepAliveRequest, +) +from servers.query_report.applications.client import Client +from servers.query_report.v2.abstractions.cmd_handler_base import CmdHandlerBase + +from servers.query_report.v2.contracts.requests import ( + AvaliableRequest, + ChallengeRequest, + ClientMessageRequest, + EchoRequest, + HeartBeatRequest, +) +from servers.query_report.v2.enums.general import RequestType +from servers.query_report.v2.handlers.handlers import ( + AvailableHandler, + ChallengeHanler, + ClientMessageAckHandler, + EchoHandler, + HeartBeatHandler, + KeepAliveHandler, +) + + +class CmdSwitcher(SwitcherBase): + def __init__(self, client: Client, raw_request: bytes): + super().__init__(client, raw_request) + + def _process_raw_request(self) -> None: + if len(self._raw_request) < 4: + raise UniSpyException("Invalid request") + + name = RequestType(self._raw_request[0]) + raw_request = self._raw_request + self._requests.append((name, raw_request)) + + def _create_cmd_handlers(self, name: int, raw_request: bytes) -> CmdHandlerBase: + req = raw_request + match name: + case RequestType.HEARTBEAT: + return HeartBeatHandler(self._client, HeartBeatRequest(req)) + case RequestType.CHALLENGE: + return ChallengeHanler(self._client, ChallengeRequest(req)) + case RequestType.AVALIABLE_CHECK: + return AvailableHandler(self._client, AvaliableRequest(req)) + case RequestType.CLIENT_MESSAGE_ACK: + return ClientMessageAckHandler(self._client, ClientMessageRequest(req)) + case RequestType.ECHO: + return EchoHandler(self._client, EchoRequest(req)) + case RequestType.KEEP_ALIVE: + return KeepAliveHandler(self._client, KeepAliveRequest(req)) + case _: + return None diff --git a/src/servers/query_report/v2/contracts/requests.py b/src/servers/query_report/v2/contracts/requests.py new file mode 100644 index 000000000..80a9c72cf --- /dev/null +++ b/src/servers/query_report/v2/contracts/requests.py @@ -0,0 +1,184 @@ +from uuid import UUID +from library.extentions.encoding import get_string +from library.log.log_manager import LogWriter +from servers.query_report.exceptions.exceptions import QRException +from servers.query_report.v2.abstractions.request_base import RequestBase +from servers.query_report.v2.enums.general import GameServerStatus + + +PREFIX = bytes([0x09, 0x00, 0x00, 0x00, 0x00]) +POSTFIX = bytes([0x00]) + + +class AvaliableRequest(RequestBase): + + def parse(self): + super().parse() + for i in range(len(PREFIX)): + if self.raw_request[i] != PREFIX[i]: + raise QRException("Avaliable request prefix is invalid.") + + # postfix check + if self.raw_request[len(self.raw_request) - 1] != POSTFIX: + raise QRException("Avaliable request postfix is invalid.") + + +class ChallengeRequest(RequestBase): + pass + + +class ClientMessageAckRequest(RequestBase): + pass + + +class ClientMessageRequest(RequestBase): + server_browser_sender_id: UUID + natneg_message: bytes + target_ip_address: bytes + target_port: bytes + message_key: int + + @property + def cookie(self): + return self.natneg_message[6:10] + + +class HeartBeatRequest(RequestBase): + server_data: dict[str, str] + player_data: list[dict[str, str]] + team_data: list[dict[str, str]] + server_status: GameServerStatus + group_id: int + + def parse(self): + super().parse() + player_pos, team_pos = 0, 0 + player_length, team_length = 0, 0 + self.data_partition = get_string(self.raw_request[5:]) + + player_pos = self.data_partition.index("player_\0", 0) + team_pos = self.data_partition.index("team_t\0", 0) + + if player_pos != -1 and team_pos != -1: + player_length = team_pos - player_pos + team_length = len(self.data_partition) - team_pos + + server_data_str = self.data_partition[: player_pos - 4] + self.parse_server_data(server_data_str) + + player_data_str = self.data_partition[ + player_pos - 1 : player_pos - 1 + player_length - 2 + ] + self.parse_player_data(player_data_str) + + team_data_str = self.data_partition[ + team_pos - 1 : team_pos - 1 + team_length + ] + self.parse_team_data(team_data_str) + + elif player_pos != -1: + player_length = len(self.data_partition) - player_pos + + server_data_str = self.data_partition[: player_pos - 4] + self.parse_server_data(server_data_str) + + player_data_str = self.data_partition[player_pos - 1 :] + self.parse_player_data(player_data_str) + + elif player_pos == -1 and team_pos == -1: + server_data_str = self.data_partition + self.parse_server_data(server_data_str) + + else: + raise QRException("HeartBeat request is invalid.") + + if "groupid" in self.server_data: + group_id = 0 + if not int(self.server_data["groupid"], group_id): + raise QRException("GroupId is invalid.") + self.group_id = group_id + + def parse_server_data(self, server_data_str: str): + self.server_data = {} + key_value_array = server_data_str.split("\0") + + for i in range(0, len(key_value_array), 2): + if i + 2 > len(key_value_array): + break + + temp_key = key_value_array[i] + temp_value = key_value_array[i + 1] + + if temp_key == "": + LogWriter.LogVerbose("Skipping empty key value") + continue + + if temp_key in self.server_data: + self.server_data[temp_key] = temp_value + else: + self.server_data[temp_key] = temp_value + + if "statechanged" not in self.server_data: + self.server_status = GameServerStatus.NORMAL + else: + self.server_status = GameServerStatus[self.server_data["statechanged"]] + + def parse_player_data(self, player_data_str: str): + self.player_data = [] + player_count = int(player_data_str[0]) + player_data_str = player_data_str[1:] + + index_of_key = player_data_str.index("\0\0", 0) + key_str = player_data_str[:index_of_key] + keys = key_str.split("\0") + + values_str = player_data_str[index_of_key + 2 :] + values = values_str.split("\0") + + for player_index in range(player_count): + key_value = {} + + for key_index in range(len(keys)): + temp_key = keys[key_index] + str(player_index) + temp_value = values[player_index * len(keys) + key_index] + + if temp_key in key_value: + key_value[temp_key] = temp_value + else: + key_value[temp_key] = temp_value + + self.player_data.append(key_value) + + def parse_team_data(self, team_data_str: str): + self.team_data = [] + team_count = int(team_data_str[0]) + team_data_str = team_data_str[1:] + + end_key_index = team_data_str.index("\0\0", 0) + key_str = team_data_str[:end_key_index] + keys = key_str.split("\0") + + value_str = team_data_str[end_key_index + 2 :] + values = value_str.split("\0") + + for team_index in range(team_count): + key_value = {} + + for key_index in range(len(keys)): + temp_key = keys[key_index] + str(team_index) + temp_value = values[team_index * len(keys) + key_index] + + if temp_key in key_value: + key_value[temp_key] = temp_value + else: + key_value[temp_key] = temp_value + + self.team_data.append(key_value) + + +class EchoRequest(RequestBase): + pass + + +class KeepAliveRequest(RequestBase): + pass diff --git a/src/servers/query_report/v2/contracts/responses.py b/src/servers/query_report/v2/contracts/responses.py new file mode 100644 index 000000000..44eaa0c2d --- /dev/null +++ b/src/servers/query_report/v2/contracts/responses.py @@ -0,0 +1,91 @@ +from library.extentions.encoding import get_bytes +from servers.presence_connection_manager.contracts.requests.general import ( + KeepAliveRequest, +) +from servers.query_report.v2.abstractions.response_base import ResponseBase +from servers.query_report.v2.contracts.requests import ( + AvaliableRequest, + ChallengeRequest, + ClientMessageRequest, + HeartBeatRequest, +) +from servers.query_report.v2.contracts.results import ChallengeResult, HeartBeatResult +from servers.query_report.v2.enums.general import ServerAvailability + + +RESPONSE_PREFIX = bytes([0xFE, 0xFD, 0x09, 0x00, 0x00, 0x00]) + + +class AvaliableResponse(ResponseBase): + def __init__(self, request: AvaliableRequest) -> None: + assert isinstance(request, AvaliableRequest) + super().__init__(request, None) + + def build(self): + data = bytearray() + data.extend(RESPONSE_PREFIX) + data.append(ServerAvailability.AVAILABLE) + self.sending_buffer = bytes(data) + + +MESSAGE = "UniSpy echo!" + + +class ChallengeResponse(ResponseBase): + def __init__(self, request: ChallengeRequest, result: ChallengeResult) -> None: + assert isinstance(request, ChallengeRequest) + assert isinstance(result, ChallengeResult) + super().__init__(request, result) + + def build(self) -> None: + super().build() + data = bytearray() + data.extend(self.sending_buffer) + data.extend(get_bytes(MESSAGE)) + self.sending_buffer = bytes(data) + + +class ClientMessageResponse(ResponseBase): + _request: ClientMessageRequest + + def __init__(self, request: ClientMessageRequest) -> None: + assert isinstance(request, ClientMessageRequest) + super().__init__(request, None) + + def build(self): + super().build() + data = bytearray() + data.extend(self.sending_buffer) + data.extend(self._request.message_key.to_bytes(4, byteorder="little")) + data.extend(self._request.natneg_message) + self.sending_buffer = bytes(data) + + +CHALLENGE = bytes([0x54, 0x54, 0x54, 0x00, 0x00]) +SPLITER = bytes([0x00, 0x00, 0x00, 0x00]) + + +class HeartBeatResponse(ResponseBase): + _request: HeartBeatRequest + _result: HeartBeatResult + + def __init__(self, request: HeartBeatRequest, result: HeartBeatResult) -> None: + assert isinstance(request, HeartBeatRequest) + assert isinstance(result, HeartBeatResult) + super().__init__(request, result) + + def build(self) -> None: + super().build() + data = bytearray() + data.extend(self.sending_buffer) + data.extend(CHALLENGE) + data.extend(self._result.remote_ip_endpoint.get_ip_bytes()) + data.extend(SPLITER) + data.extend(self._result.remote_ip_endpoint.get_port_bytes()) + self.sending_buffer = bytes(data) + + +class KeepAliveResponse(ResponseBase): + def __init__(self, request: KeepAliveRequest) -> None: + assert isinstance(request, KeepAliveRequest) + super().__init__(request, None) diff --git a/src/servers/query_report/v2/contracts/results.py b/src/servers/query_report/v2/contracts/results.py new file mode 100644 index 000000000..cb4901230 --- /dev/null +++ b/src/servers/query_report/v2/contracts/results.py @@ -0,0 +1,23 @@ +from library.extentions.string_extentions import IPEndPoint +from servers.query_report.v2.abstractions.result_base import ResultBase +from servers.query_report.v2.enums.general import PacketType + + +class ChallengeResult(ResultBase): + packet_type: PacketType = PacketType.CHALLENGE + + +class ClientMessageResult(ResultBase): + natneg_message: bytes + message_key: int + packet_type: PacketType = PacketType.CLIENT_MESSAGE + + +class EchoResult(ResultBase): + info: dict + packet_type: PacketType = PacketType.ECHO + + +class HeartBeatResult(ResultBase): + remote_ip_endpoint: IPEndPoint + packet_type: PacketType = PacketType.HEARTBEAT diff --git a/src/servers/query_report/v2/enums/general.py b/src/servers/query_report/v2/enums/general.py new file mode 100644 index 000000000..fe02ea866 --- /dev/null +++ b/src/servers/query_report/v2/enums/general.py @@ -0,0 +1,63 @@ +from enum import IntEnum + + +class RequestType(IntEnum): + CHALLENGE = 0x01 + HEARTBEAT = 0x03 + CLIENT_MESSAGE = 0x06 + CLIENT_MESSAGE_ACK = 0x07 + ADD_ERROR = 0x04 + ECHO = 0x02 + KEEP_ALIVE = 0x08 + AVALIABLE_CHECK = 0x09 + + +class ResponseType(IntEnum): + QUERY = 0x00 + ECHO = 0x02 + ADD_ERROR = 0x04 + CLIENT_MESSAGE = 0x06 + REQUIRE_IP_VERIFY = 0x09 + CLIENT_REGISTERED = 0x0A + + +class PacketType(IntEnum): + QUERY = 0x00 + CHALLENGE = 0x01 + ECHO = 0x02 + ADD_ERROR = 0x04 + CLIENT_MESSAGE = 0x06 + REQUIRE_IP_VERIFY = 0x09 + CLIENT_REGISTERED = 0x0A + HEARTBEAT = 0x03 + ECHO_RESPONSE = 0x05 + CLIENT_MESSAGE_ACK = 0x07 + KEEP_ALIVE = 0x08 + AVALIABLE_CHECK = 0x09 + + +class QRStateChange(IntEnum): + NORMAL_HEARTBEAT = 0 + GAME_MODE_CHANGE = 1 + SERVER_SHUTDOWN = 2 + CANNOT_RECIEVE_CHALLENGE = 3 + + +class HeartBeatReportType(IntEnum): + SERVER_TEAM_PLAYER_DATA = 1 + SERVER_PLAYER_DATA = 2 + SERVER_DATA = 3 + + +class GameServerStatus(IntEnum): + NORMAL = 0 + UPDATE = 1 + SHUTDOWN = 2 + PLAYING = 3 + + +class ServerAvailability(IntEnum): + AVAILABLE = 0 + WAITING = 1 + PERMANENT_UNAVAILABLE = 2 + TEMPORARILY_UNAVAILABLE = 3 diff --git a/src/servers/query_report/v2/handlers/handlers.py b/src/servers/query_report/v2/handlers/handlers.py new file mode 100644 index 000000000..2732cc801 --- /dev/null +++ b/src/servers/query_report/v2/handlers/handlers.py @@ -0,0 +1,82 @@ +from servers.presence_connection_manager.contracts.requests.general import ( + KeepAliveRequest, +) +from servers.query_report.applications.client import Client +from servers.query_report.v2.abstractions.cmd_handler_base import CmdHandlerBase +from servers.query_report.v2.contracts.requests import ( + AvaliableRequest, + ChallengeRequest, + ClientMessageAckRequest, + ClientMessageRequest, + EchoRequest, + HeartBeatRequest, +) +from servers.query_report.v2.contracts.responses import ( + AvaliableResponse, + ChallengeResponse, + ClientMessageResponse, + HeartBeatResponse, +) +from servers.query_report.v2.contracts.results import ChallengeResult + + +class AvailableHandler(CmdHandlerBase): + _request: AvaliableRequest + + def __init__(self, client: Client, request: AvaliableRequest) -> None: + assert isinstance(request, AvaliableRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = AvaliableResponse(self._request) + + +class ChallengeHanler(CmdHandlerBase): + _request: ChallengeRequest + _result: ChallengeResult + + def __init__(self, client: Client, request: ChallengeRequest) -> None: + assert isinstance(request, ChallengeRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = ChallengeResponse(self._request, self._result) + + +class ClientMessageAckHandler(CmdHandlerBase): + def __init__(self, client: Client, request: ClientMessageAckRequest) -> None: + assert isinstance(request, ClientMessageAckRequest) + super().__init__(client, request) + + def _response_construct(self): + self._client.log_info("Get client message ack") + + +class ClientMessageHandler(CmdHandlerBase): + def __init__(self, client: Client, request: ClientMessageRequest) -> None: + assert isinstance(request, ClientMessageRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = ClientMessageResponse(self._request) + + +class EchoHandler(CmdHandlerBase): + def __init__(self, client: Client, request: EchoRequest) -> None: + assert isinstance(request, EchoRequest) + super().__init__(client, request) + + +class HeartBeatHandler(CmdHandlerBase): + def __init__(self, client: Client, request: HeartBeatRequest) -> None: + assert isinstance(request, HeartBeatRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = HeartBeatResponse(self._request, self._result) + + +class KeepAliveHandler(CmdHandlerBase): + def __init__(self, client: Client, request: KeepAliveRequest) -> None: + assert isinstance(request, KeepAliveRequest) + super().__init__(client, request) diff --git a/src/servers/server_browser/exceptions/general.py b/src/servers/server_browser/exceptions/general.py new file mode 100644 index 000000000..16ac04506 --- /dev/null +++ b/src/servers/server_browser/exceptions/general.py @@ -0,0 +1,6 @@ +from library.exceptions.error import UniSpyException + + +class ServerBrowserException(UniSpyException): + def __init__(self, message: str) -> None: + super().__init__(message) diff --git a/src/servers/server_browser/v2/__init__.py b/src/servers/server_browser/v2/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/server_browser/v2/abstractions/__init__.py b/src/servers/server_browser/v2/abstractions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/server_browser/v2/abstractions/contracts.py b/src/servers/server_browser/v2/abstractions/contracts.py new file mode 100644 index 000000000..d590bc770 --- /dev/null +++ b/src/servers/server_browser/v2/abstractions/contracts.py @@ -0,0 +1,132 @@ +from socket import inet_ntoa +from typing import List, Optional +import library.abstractions.contracts +import abc + +from library.extentions.encoding import get_bytes +from servers.server_browser.v2.aggregations.encryption import SERVER_CHALLENGE +from servers.server_browser.v2.aggregations.string_flags import STRING_SPLITER +from servers.server_browser.v2.contracts.results import AdHocResult +from servers.server_browser.v2.enums.general import ( + DataKeyType, + GameServerFlags, + RequestType, + ServerListUpdateOption, +) + +QUERY_REPORT_DEFAULT_PORT = 6500 + + +class RequestBase(library.abstractions.contracts.RequestBase, abc.ABC): + request_length: int + raw_request: bytes + command_name: RequestType + + def __init__(self, raw_request: bytes) -> None: + assert isinstance(raw_request, bytes) + super().__init__(raw_request) + + def parse(self) -> None: + self.request_length = int.from_bytes(self.raw_request[:2], byteorder="little") + self.command_name = RequestType(self.raw_request[2]) + + +class ResultBase(library.abstractions.contracts.ResultBase, abc.ABC): + pass + + +class ResponseBase(library.abstractions.contracts.ResponseBase, abc.ABC): + _request: RequestBase + _result: ResultBase + sending_buffer: bytes + + def __init__(self, request: RequestBase, result: ResultBase) -> None: + assert issubclass(type(request), RequestBase) + assert issubclass(type(result), ResultBase) + super().__init__(request, result) + + +class ServerListUpdateOptionRequestBase(RequestBase, abc.ABC): + def __init__(self): + self.request_version: Optional[int] = None + self.protocol_version: Optional[int] = None + self.encoding_version: Optional[int] = None + self.game_version: Optional[int] = None + self.query_options: Optional[int] = None + self.dev_game_name: Optional[str] = None + self.game_name: Optional[str] = None + self.client_challenge: Optional[str] = None + self.update_option: Optional[ServerListUpdateOption] = None + self.keys: Optional[List[str]] = None + self.filter: Optional[str] = None + self.source_ip: str + self.max_servers: Optional[int] = None + + def __init__(self, raw_request: bytes): + assert isinstance(raw_request, bytes) + super().__init__(raw_request) + + +class ServerListUpdateOptionResultBase(ResultBase, abc.ABC): + client_remote_ip: bytes + flag: GameServerFlags + game_secret_key: str + + +class ServerListUpdateOptionResponseBase(ResponseBase, abc.ABC): + _request: ServerListUpdateOptionRequestBase + _result: ServerListUpdateOptionResultBase + _servers_info_buffers: list = [] + + def __init__( + self, + request: ServerListUpdateOptionRequestBase, + result: ServerListUpdateOptionResultBase, + ) -> None: + assert issubclass(type(request), ServerListUpdateOptionRequestBase) + assert issubclass(type(result), ServerListUpdateOptionResultBase) + super().__init__(request, result) + + def build(self) -> None: + crypt_header = self.build_crypt_header() + self._servers_info_buffers.extend(crypt_header) + self._servers_info_buffers.extend(self._result.client_remote_ip) + self._servers_info_buffers.extend(bytes(6500)) + + def build_crypt_header(self) -> list: + # cryptHeader have 14 bytes, when we encrypt data we need skip the first 14 bytes + crypt_header = [] + crypt_header.append(2 ^ 0xEC) + crypt_header.extend([0, 0]) # message length? + crypt_header.append(len(SERVER_CHALLENGE) ^ 0xEA) + crypt_header.extend(get_bytes(SERVER_CHALLENGE)) + return crypt_header + + def build_server_keys(self) -> None: + # we add the total number of the requested keys + self._servers_info_buffers.append(len(self._request.keys)) + # then we add the keys + for key in self._request.keys: + self._servers_info_buffers.append(DataKeyType.STRING) + self._servers_info_buffers.extend(get_bytes(key)) + self._servers_info_buffers.append(STRING_SPLITER) + + def build_unique_value(self): + self._servers_info_buffers.append(0) + + +class AdHocRequestBase(RequestBase): + game_server_public_ip: bytes + game_server_public_port: bytes + + def parse(self) -> None: + super().parse() + self.game_server_public_ip = inet_ntoa(self.raw_request[3:7]) + self.game_server_public_port = int.from_bytes( + self.raw_request[7:9], byteorder="big" + ) + + +class AdHocResponseBase(ResponseBase): + _result: AdHocResult + _buffer: list[int] = [] diff --git a/src/servers/server_browser/v2/abstractions/handlers.py b/src/servers/server_browser/v2/abstractions/handlers.py new file mode 100644 index 000000000..20091fd7a --- /dev/null +++ b/src/servers/server_browser/v2/abstractions/handlers.py @@ -0,0 +1,54 @@ +from library.abstractions.handler import CmdHandlerBase as CMB +import abc + +from servers.server_browser.v2.abstractions.contracts import ( + ServerListUpdateOptionRequestBase, + ServerListUpdateOptionResponseBase, + ServerListUpdateOptionResultBase, +) +from servers.server_browser.v2.aggregations.encryption import EnctypeX +from servers.server_browser.v2.applications.client import Client +from servers.server_browser.v2.abstractions.contracts import ( + RequestBase, + ResultBase, + ResponseBase, +) + + +class CmdHandlerBase(CMB, abc.ABC): + _client: Client + _request: RequestBase + _result: ResultBase + _response: ResponseBase + + def __init__(self, client: Client, request: RequestBase) -> None: + assert isinstance(client, Client) + assert issubclass(type(request), RequestBase) + super().__init__(client, request) + + +class ServerListUpdateOptionHandlerBase(CmdHandlerBase): + _request: ServerListUpdateOptionRequestBase + _result: ServerListUpdateOptionResultBase + _response: ServerListUpdateOptionResponseBase + + def _data_operate(self) -> None: + # send to backend to query the game secret key + secretkey = None + if secretkey is None: + raise NotImplementedError("not implemented") + self._client.info.game_secret_key = secretkey + # use secret key to construct _client.crypto + self._client.crypto = EnctypeX( + self._client.info.game_secret_key, self._client.info.client_challenge + ) + pass + + def _response_send(self) -> None: + self._response.build() + head_buffer = self._response.sending_buffer[:14] + body_buffer = self._response.sending_buffer[14:] + encrypted_body_buffer = self._client.crypto.encrypt(body_buffer) + buffer = head_buffer + encrypted_body_buffer + self._client.log_network_sending(buffer) + self._client.send(buffer) diff --git a/src/servers/server_browser/v2/aggregations/encryption.py b/src/servers/server_browser/v2/aggregations/encryption.py new file mode 100644 index 000000000..d964e4e61 --- /dev/null +++ b/src/servers/server_browser/v2/aggregations/encryption.py @@ -0,0 +1,134 @@ +from library.abstractions.enctypt_base import EncryptBase + +SERVER_CHALLENGE = "0000000000" + + +class EncryptionParameters: + def __init__(self): + self.Register = bytearray(256) + self.Index0 = 0 + self.Index1 = 0 + self.Index2 = 0 + self.Index3 = 0 + self.Index4 = 0 + + +class EnctypeX(EncryptBase): + def __init__(self, secretKey: str, clientChallenge: str): + assert isinstance(secretKey, str) + assert isinstance(clientChallenge, str) + self._encParams = EncryptionParameters() + self._clientChallenge = bytearray(clientChallenge.encode("ascii")) + self._serverChallenge = bytearray(SERVER_CHALLENGE.encode("ascii")) + self._secretKey = bytearray(secretKey.encode("ascii")) + self.init_encryption_algorithm() + + def init_encryption_algorithm(self): + if len(self._clientChallenge) != 8: + raise ValueError("Client challenge length not valid!") + + for i in range(len(self._serverChallenge)): + tempIndex0 = i * self._secretKey[i % len(self._secretKey)] % 8 + tempIndex1 = i % 8 + bitwiseResult = self._clientChallenge[tempIndex1] ^ self._serverChallenge[i] + self._clientChallenge[tempIndex0] ^= bitwiseResult & 0xFF + + self.init_encryption_parameters() + + def init_encryption_parameters(self): + if len(self._clientChallenge) < 1: + self.non_challenge_mapping_init() + return + + self._encParams.Register = bytearray(range(256)) + toSwap = 0 + keyPosition = 0 + randomSum = 0 + for i in range(255, 0, -1): + toSwap = self.index_position_generation(i, randomSum, keyPosition) + swapTemp = self._encParams.Register[i] + self._encParams.Register[i] = self._encParams.Register[toSwap] + self._encParams.Register[toSwap] = swapTemp + + self._encParams.Index0 = self._encParams.Register[1] + self._encParams.Index1 = self._encParams.Register[3] + self._encParams.Index2 = self._encParams.Register[5] + self._encParams.Index3 = self._encParams.Register[7] + self._encParams.Index4 = self._encParams.Register[randomSum] + + def non_challenge_mapping_init(self): + self._encParams.Index0 = 1 + self._encParams.Index1 = 3 + self._encParams.Index2 = 5 + self._encParams.Index3 = 7 + self._encParams.Index4 = 11 + self._encParams.Register = bytearray(range(255, -1, -1)) + + def byte_shift(self, b): + self._encParams.Index1 += self._encParams.Register[self._encParams.Index0] + swapTempStorage = self._encParams.Register[self._encParams.Index4] + self._encParams.Register[self._encParams.Index4] = self._encParams.Register[ + self._encParams.Index1 + ] + self._encParams.Register[self._encParams.Index1] = self._encParams.Register[ + self._encParams.Index3 + ] + self._encParams.Register[self._encParams.Index3] = self._encParams.Register[ + self._encParams.Index0 + ] + self._encParams.Register[self._encParams.Index0] = swapTempStorage + self._encParams.Index2 += self._encParams.Register[swapTempStorage] + + self._encParams.Index4 = ( + b + ^ self._encParams.Register[ + ( + self._encParams.Register[self._encParams.Index2] + + self._encParams.Register[self._encParams.Index0] + ) + & 0xFF + ] + ^ self._encParams.Register[ + self._encParams.Register[ + ( + self._encParams.Register[self._encParams.Index3] + + self._encParams.Register[self._encParams.Index4] + + self._encParams.Register[self._encParams.Index1] + ) + & 0xFF + ] + ] + ) + self._encParams.Index3 = b + + return self._encParams.Index4 + + def index_position_generation(self, limit, randomSum, keyPosition): + swapIndex, retryLimiter, bitMask = 0, 0, 1 + if limit == 0: + return 0 + + while bitMask < limit: + bitMask = (bitMask << 1) + 1 + + while True: + randomSum = ( + self._encParams.Register[randomSum] + self._clientChallenge[keyPosition] + ) + + if keyPosition >= len(self._clientChallenge): + keyPosition = 0 + randomSum += len(self._clientChallenge) + + swapIndex = bitMask & randomSum + retryLimiter += 1 + if retryLimiter > 11: + swapIndex %= limit + + if swapIndex <= limit: + break + + return swapIndex + + def encrypt(self, plainText): + return plainText diff --git a/src/servers/server_browser/v2/aggregations/server_info_builder.py b/src/servers/server_browser/v2/aggregations/server_info_builder.py new file mode 100644 index 000000000..8fbc16dd1 --- /dev/null +++ b/src/servers/server_browser/v2/aggregations/server_info_builder.py @@ -0,0 +1,85 @@ +from socket import inet_aton + +from servers.query_report.aggregates.game_server_info import GameServerInfo + +from servers.query_report.applications.data import get_all_groups +from servers.server_browser.v2.abstractions.contracts import QUERY_REPORT_DEFAULT_PORT +from servers.server_browser.v2.enums.general import GameServerFlags + +PEER_GROUP_LIST = get_all_groups() + + +def build_server_info_header( + flag: GameServerFlags, server_info: GameServerInfo +) -> list[int]: + header = [] + # add key flag + header.append(flag.value) + # we add server public ip here + header.extend(inet_aton(server_info.host_ip_address)) + # we check host port is standard port or not + check_non_standard_port(header, server_info) + # check if game can directly query information from server + check_unsolicited_udp(header, server_info) + # we check the natneg flag + check_nat_neg_flag(header, server_info) + # now we check if there are private ip + check_private_ip(header, server_info) + # we check private port here + check_non_standard_private_port(header, server_info) + # we check icmp support here + check_icmp_support(header, server_info) + + return header + + +def check_nat_neg_flag(header: list[int], server_info: GameServerInfo): + if "natneg" in server_info.server_data: + nat_neg_flag = int(server_info.server_data["natneg"]) + unsolicited_udp = header[0] & GameServerFlags.UNSOLICITED_UDP_FLAG.value + if nat_neg_flag == 1 and unsolicited_udp == 0: + header[0] ^= GameServerFlags.CONNECT_NEGOTIATE_FLAG.value + + +def check_unsolicited_udp(header: list[int], server_info: GameServerInfo): + if "allow_unsolicited_udp" in server_info.server_data: + unsolicited_udp = int(server_info.server_data["unsolicitedudp"]) + if unsolicited_udp == 1: + header[0] ^= GameServerFlags.UNSOLICITED_UDP_FLAG.value + + +def check_private_ip(header: list[int], server_info: GameServerInfo): + #!when game create a channel chat, it will use both the public ip and private ip to build the name. + #!known game: Worm3d + + if server_info.game_name in PEER_GROUP_LIST: + if "localip0" in server_info.server_data: + header[0] ^= GameServerFlags.PRIVATE_IP_FLAG.value + bytes_address = inet_aton(server_info.server_data["localip0"]) + header.extend(bytes_address) + + +def check_non_standard_port(header: list[int], server_info: GameServerInfo): + # !! only dedicated server have different query report port and host port + # !! but peer server have same query report port and host port + # todo we have to check when we need send host port or query report port + if server_info.query_report_port != QUERY_REPORT_DEFAULT_PORT: + header[0] ^= GameServerFlags.NON_STANDARD_PORT_FLAG.value + hton_port = server_info.query_report_port_bytes + header.extend(hton_port) + + +def check_non_standard_private_port(header: list[int], server_info: GameServerInfo): + if "localport" in server_info.server_data: + local_port = server_info.server_data["localport"] + if local_port != "" and local_port != QUERY_REPORT_DEFAULT_PORT: + header[0] ^= GameServerFlags.NON_STANDARD_PRIVATE_PORT_FLAG.value + port = int(local_port).to_bytes(2, byteorder="big") + header.extend(port) + + +def check_icmp_support(header: list[int], server_info: GameServerInfo): + if "icmp_address" in server_info.server_data: + header[0] ^= GameServerFlags.ICMP_IP_FLAG.value + bytes_address = inet_aton(server_info.server_data["icmp_address"]) + header.extend(bytes_address) diff --git a/src/servers/server_browser/v2/aggregations/string_flags.py b/src/servers/server_browser/v2/aggregations/string_flags.py new file mode 100644 index 000000000..181200db8 --- /dev/null +++ b/src/servers/server_browser/v2/aggregations/string_flags.py @@ -0,0 +1,3 @@ +ALL_SERVER_END_FLAG = bytes([0, 255, 255, 255, 255]) +STRING_SPLITER = bytes([0]) +NTS_STRING_FLAG = bytes([0xFF]) diff --git a/src/servers/server_browser/v2/applications/__init__.py b/src/servers/server_browser/v2/applications/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/server_browser/v2/applications/client.py b/src/servers/server_browser/v2/applications/client.py new file mode 100644 index 000000000..33d8d36cf --- /dev/null +++ b/src/servers/server_browser/v2/applications/client.py @@ -0,0 +1,17 @@ +from library.abstractions.client import ClientBase, ClientInfoBase +from servers.server_browser.v2.enums.general import ServerListUpdateOption + + +class ClientInfo(ClientInfoBase): + game_secret_key: str + client_challenge: str + search_type: ServerListUpdateOption + game_name: str + + +class Client(ClientBase): + is_log_raw: bool = True + info: ClientInfo + + def create_switcher(self, buffer) -> None: + pass diff --git a/src/servers/server_browser/v2/contracts/__init__.py b/src/servers/server_browser/v2/contracts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/server_browser/v2/contracts/requests.py b/src/servers/server_browser/v2/contracts/requests.py new file mode 100644 index 000000000..a558f3993 --- /dev/null +++ b/src/servers/server_browser/v2/contracts/requests.py @@ -0,0 +1,61 @@ +from socket import inet_ntoa +from servers.server_browser.v2.abstractions.contracts import ( + AdHocRequestBase, + ServerListUpdateOptionRequestBase, +) +from servers.server_browser.v2.enums.general import RequestType, ServerListUpdateOption + + +class ServerListRequest(ServerListUpdateOptionRequestBase): + command_name: RequestType = RequestType.SERVER_LIST_REQUEST + + def parse(self) -> None: + self.request_version = int(self.raw_request[2]) + self.protocol_version = int(self.raw_request[3]) + self.encoding_version = int(self.raw_request[4]) + self.game_version = int(self.raw_request[5:9]) + + remain_data = self.raw_request[9:] + dev_game_name_index = remain_data.index(0) + self.dev_game_name = remain_data[:dev_game_name_index].decode() + remain_data = remain_data[dev_game_name_index + 1 :] + game_name_index = remain_data.index(0) + self.game_name = remain_data[:game_name_index].decode() + remain_data = remain_data[game_name_index + 1 :] + self.client_challenge = remain_data[:8].decode() + remain_data = remain_data[8:] + + filter_index = remain_data.index(0) + if filter_index > 0: + self.filter = remain_data[:filter_index].decode() + remain_data = remain_data[filter_index + 1 :] + + keys_index = remain_data.index(0) + self.keys = remain_data[:keys_index].decode().split("\\") + remain_data = remain_data[keys_index + 1 :] + + byte_update_options = remain_data[:4][::-1] + self.update_option = ServerListUpdateOption(int(byte_update_options)) + remain_data = remain_data[4:] + + if self.update_option & ServerListUpdateOption.ALTERNATE_SOURCE_IP: + self.source_ip = inet_ntoa(remain_data[:4]) + remain_data = remain_data[4:] + + if self.update_option & ServerListUpdateOption.LIMIT_RESULT_COUNT: + if len(remain_data) != 4: + raise Exception("The max number of server is incorrect.") + self.max_servers = int(remain_data[:4][::-1]) + + +class SendMessageRequest(AdHocRequestBase): + prefix_message: bytes + client_message: bytes + + def parse(self) -> None: + super().parse() + self.client_message = self.raw_request[9:] + + +class ServerInfoRequest(AdHocRequestBase): + pass diff --git a/src/servers/server_browser/v2/contracts/responses.py b/src/servers/server_browser/v2/contracts/responses.py new file mode 100644 index 000000000..e4012905f --- /dev/null +++ b/src/servers/server_browser/v2/contracts/responses.py @@ -0,0 +1,128 @@ +from library.extentions.encoding import get_bytes +from servers.server_browser.v2.abstractions.contracts import ( + AdHocResponseBase, + ServerListUpdateOptionResponseBase, +) +from servers.server_browser.v2.aggregations.server_info_builder import ( + build_server_info_header, +) +from servers.server_browser.v2.aggregations.string_flags import * +from servers.server_browser.v2.contracts.requests import ServerListRequest +from servers.server_browser.v2.contracts.results import ( + AdHocResult, + P2PGroupRoomListResult, + ServerMainListResult, +) + +from servers.server_browser.v2.enums.general import GameServerFlags, ResponseType + + +class DeleteServerInfoResponse(AdHocResponseBase): + _result: AdHocResult + + def __init__(self, result: AdHocResult) -> None: + assert isinstance(result, AdHocResult) + super().__init__(None, result) + + def build(self): + buffer = bytearray() + buffer.append(ResponseType.DELETE_SERVER_MESSAGE) + buffer.extend(self._result.game_server_info.host_ip_address_bytes) + self.sending_buffer = bytes(buffer) + + +class UpdateServerInfoResponse(AdHocResponseBase): + def __init__(self, result: AdHocResult) -> None: + assert isinstance(result, AdHocResult) + super().__init__(None, result) + + def build(self) -> None: + self._buffer.append(ResponseType.PUSH_SERVER_MESSAGE) + self.__build_single_server_full_info() + msg_leng = len(self._buffer).to_bytes(2, "big") + self._buffer.insert(0, msg_leng) + self.sending_buffer = bytes(self._buffer) + + def __build_single_server_full_info(self): + header = build_server_info_header( + GameServerFlags.HAS_FULL_RULES_FLAG, self._result.game_server_info + ) + self._buffer.extend(header) + if self._result.game_server_info.server_data is not None: + server_data = UpdateServerInfoResponse._build_kv() + self._buffer.extend(server_data) + if self._result.game_server_info.player_data is not None: + server_data = UpdateServerInfoResponse._build_kv() + self._buffer.extend(server_data) + if self._result.game_server_info.team_data is not None: + server_data = UpdateServerInfoResponse._build_kv() + self._buffer.extend(server_data) + + def _build_kv(data: dict): + buffer = [] + for k, v in data.items(): + buffer.extend(get_bytes(k)) + buffer.append(STRING_SPLITER) + buffer.extend(get_bytes(v)) + buffer.append(STRING_SPLITER) + return buffer + + +class P2PGroupRoomListResponse(ServerListUpdateOptionResponseBase): + _request: ServerListRequest + _result: P2PGroupRoomListResult + + def build(self) -> None: + super().build() + self.build_server_keys() + self.build_unique_value() + self._build_servers_full_info() + self.sending_buffer = bytes(self._servers_info_buffers) + + def _build_servers_full_info(self): + for room in self._result.peer_room_infos: + self._servers_info_buffers.append(GameServerFlags.HAS_KEYS_FLAG) + group_id_bytes = room.group_id.to_bytes("big") + self._servers_info_buffers.extend(group_id_bytes) + for key in self._request.keys: + self._servers_info_buffers.append(NTS_STRING_FLAG) + value = ( + room.raw_key_values[key] + if key in room.raw_key_values.keys() + else "" + ) + self._servers_info_buffers.extend(get_bytes(value)) + self._servers_info_buffers.append(STRING_SPLITER) + end_flag = int(0).to_bytes("little") + self._servers_info_buffers.extend(end_flag) + + +class ServerMainListResponse(ServerListUpdateOptionResponseBase): + _request: ServerListRequest + _result: ServerMainListResult + + def __build_servers_full_info(self): + for info in self._result.servers_info: + header = build_server_info_header(self._result.flag, info) + self._servers_info_buffers.extend(header) + for key in self._request.keys: + self._servers_info_buffers.append(NTS_STRING_FLAG) + if key in info.server_data.keys(): + self._servers_info_buffers.extend(get_bytes(info.server_data[key])) + self._servers_info_buffers.append(STRING_SPLITER) + + self._servers_info_buffers.extend(ALL_SERVER_END_FLAG) + + def build(self) -> None: + super().build() + self.build_server_keys() + self.build_unique_value() + self.__build_servers_full_info() + self.sending_buffer = bytes(self._servers_info_buffers) + + +class ServerNetworkInfoListResponse(ServerListUpdateOptionResponseBase): + + def build(self) -> None: + super().build() + self.sending_buffer = bytes(self._servers_info_buffers) diff --git a/src/servers/server_browser/v2/contracts/results.py b/src/servers/server_browser/v2/contracts/results.py new file mode 100644 index 000000000..46008080c --- /dev/null +++ b/src/servers/server_browser/v2/contracts/results.py @@ -0,0 +1,18 @@ +from servers.query_report.aggregates.game_server_info import GameServerInfo +from servers.query_report.aggregates.peer_room_info import PeerRoomInfo +from servers.server_browser.v2.abstractions.contracts import ( + ResultBase, + ServerListUpdateOptionResultBase, +) + + +class AdHocResult(ResultBase): + game_server_info: GameServerInfo + + +class P2PGroupRoomListResult(ResultBase): + peer_room_infos: list[PeerRoomInfo] + + +class ServerMainListResult(ServerListUpdateOptionResultBase): + servers_info: list[GameServerInfo] = [] diff --git a/src/servers/server_browser/v2/enums/general.py b/src/servers/server_browser/v2/enums/general.py new file mode 100644 index 000000000..1f6252bc7 --- /dev/null +++ b/src/servers/server_browser/v2/enums/general.py @@ -0,0 +1,70 @@ +from enum import IntEnum + + +class PlayerSearchOptions(IntEnum): + SEARCH_ALL_GAMES = 1 + SEARCH_LEFT_SUBSTRING = 2 + SEARCH_RIGHT_SUBSTRING = 4 + SEARCH_ANY_SUBSTRING = 8 + + +class QueryType(IntEnum): + BASIC = 1 + FULL = 2 + ICMP = 3 + + +class DataKeyType(IntEnum): + STRING = 1 + BYTE = 2 + SHORT = 3 + + +class RequestType(IntEnum): + SERVER_LIST_REQUEST = 0 + SERVER_INFO_REQUEST = 1 + SEND_MESSAGE_REQUEST = 2 + KEEP_ALIVE_REPLY = 3 + MAP_LOOP_REQUEST = 4 + PLAYER_SEARCH_REQUEST = 5 + + +class ResponseType(IntEnum): + PUSH_KEYS_MESSAGE = 1 + PUSH_SERVER_MESSAGE = 2 + KEEP_ALIVE_MESSAGE = 3 + DELETE_SERVER_MESSAGE = 4 + MAP_LOOP_MESSAGE = 5 + PLAYER_SEARCH_MESSAGE = 6 + + +class ProtocolVersion(IntEnum): + LIST_PROTOCOL_VERSION_1 = 0 + LIST_ENCODING_VERSION = 3 + + +class ServerListUpdateOption(IntEnum): + SERVER_MAIN_LIST = 0 + SEND_FIELD_FOR_ALL = 1 + SERVER_FULL_INFO_LIST = 2 + P2P_SERVER_MAIN_LIST = 4 + ALTERNATE_SOURCE_IP = 8 + P2P_GROUP_ROOM_LIST = 32 + NO_LIST_CACHE = 64 + LIMIT_RESULT_COUNT = 128 + + +class GameServerFlags(IntEnum): + UNSOLICITED_UDP_FLAG = 1 + PRIVATE_IP_FLAG = 2 + CONNECT_NEGOTIATE_FLAG = 4 + ICMP_IP_FLAG = 8 + NON_STANDARD_PORT_FLAG = 16 + NON_STANDARD_PRIVATE_PORT_FLAG = 32 + HAS_KEYS_FLAG = 64 + HAS_FULL_RULES_FLAG = 128 + + +if __name__ == "__main__": + GameServerFlags.PRIVATE_IP_FLAG.value + pass \ No newline at end of file diff --git a/src/servers/server_browser/v2/handlers/handlers.py b/src/servers/server_browser/v2/handlers/handlers.py new file mode 100644 index 000000000..a52047093 --- /dev/null +++ b/src/servers/server_browser/v2/handlers/handlers.py @@ -0,0 +1,124 @@ +from concurrent.futures import ProcessPoolExecutor +from servers.query_report.aggregates.game_server_info import GameServerInfo +from servers.query_report.v2.contracts.requests import ClientMessageRequest +from servers.query_report.v2.enums.general import GameServerStatus +from servers.server_browser.exceptions.general import ServerBrowserException +from servers.server_browser.v2.contracts.requests import ( + SendMessageRequest, + ServerInfoRequest, + ServerListRequest, +) +from servers.server_browser.v2.contracts.responses import ( + DeleteServerInfoResponse, + P2PGroupRoomListResponse, + ServerMainListResponse, + ServerNetworkInfoListResponse, + UpdateServerInfoResponse, +) +from servers.server_browser.v2.contracts.results import ( + AdHocResult, + ServerMainListResult, +) +from servers.server_browser.v2.enums.general import RequestType, ServerListUpdateOption +from servers.server_browser.v2.abstractions.handlers import CmdHandlerBase + +from servers.server_browser.v2.applications.client import Client + + +class AdHocHandler(CmdHandlerBase): + _message: GameServerInfo + + def __init__(self, message: GameServerInfo) -> None: + self._log_current_class() + self._message = message + + def handle(self) -> None: + result = AdHocResult() + result.game_server_info = self._message + match (self._message.status): + case ( + status + ) if status == GameServerStatus.NORMAL or status == GameServerStatus.UPDATE or status == GameServerStatus.PLAYING: + self.response = UpdateServerInfoResponse(result) + case GameServerStatus.SHUTDOWN: + self.response = DeleteServerInfoResponse(result) + case _: + pass + + clients = get_clients(self._message.game_name) + + with ProcessPoolExecutor() as executor: + executor.map(self.send_message, clients) + + def send_message(self, client: Client): + if ( + client.info.game_name == self._message.game_name + and client.crypto is not None + and ( + client.info.search_type == ServerListUpdateOption.SERVER_MAIN_LIST + or client.info.search_type + == ServerListUpdateOption.P2P_SERVER_MAIN_LIST + ) + ): + client.log_info( + f"Sending AdHoc message { self._message.status} to client" + ) + client.send(self.response) + + +class SendMessageHandler(CmdHandlerBase): + _request: SendMessageRequest + _result: GameServerInfo + + def __init__(self, client: Client, request: SendMessageRequest) -> None: + assert isinstance(request, SendMessageRequest) + # super().__init__(client, request) + assert isinstance(client, Client) + + def _response_construct(self) -> None: + message = ClientMessageRequest() + message.server_browser_sender_id = self._client.server_config.server_id + message.natneg_message = self._request.client_message + message.instant_key = self._result.instant_key + message.target_ip_address = self._result.host_ip_address + message.target_port = self._result.query_report_port + message.command_name = RequestType.CLIENT_MESSAGE + + def _response_send(self) -> None: + """ + QueryReport.V2.Application.StorageOperation.Persistance.PublishClientMessage(message); + _client.LogInfo($"Send client message to QueryReport Server: {gameServer.ServerID} [{StringExtensions.ConvertByteToHexString(message.NatNegMessage)}]"); + """ + raise NotImplementedError() + + +class ServerInfoHandler(CmdHandlerBase): + _request: ServerInfoRequest + _result: AdHocResult + + def _response_construct(self) -> None: + if self._result.game_server_info is not None: + self._response = UpdateServerInfoResponse(self._result) + + +class ServerListHandler(CmdHandlerBase): + _request: ServerListRequest + _result: ServerMainListResult + + def response_construct(self): + match self._request.update_option: + case option if option in [ + ServerListUpdateOption.SERVER_MAIN_LIST, + ServerListUpdateOption.P2P_SERVER_MAIN_LIST, + ServerListUpdateOption.LIMIT_RESULT_COUNT, + ServerListUpdateOption.SERVER_FULL_INFO_LIST, + ]: + self._response = ServerMainListResponse(self._request, self._result) + case ServerListUpdateOption.P2P_GROUP_ROOM_LIST: + self._response = P2PGroupRoomListResponse(self._request, self._result) + case ServerListUpdateOption.SERVER_FULL_INFO_LIST: + self._response = ServerNetworkInfoListResponse( + self._request, self._result + ) + case _: + raise ServerBrowserException("unknown serverlist update option type") diff --git a/src/servers/webservices/abstractions/contracts.py b/src/servers/webservices/abstractions/contracts.py new file mode 100644 index 000000000..a7f01ab66 --- /dev/null +++ b/src/servers/webservices/abstractions/contracts.py @@ -0,0 +1,43 @@ +import library.abstractions.contracts as lib +import xml.etree.ElementTree as ET + +from library.exceptions.error import UniSpyException +from servers.webservices.aggregations.soap_envelop import SoapEnvelop + + +class RequestBase(lib.RequestBase): + raw_request: str + _content_element: ET.Element + + def __init__(self, raw_request: str) -> None: + assert isinstance(raw_request, str) + super().__init__(raw_request) + + def parse(self) -> None: + xelements = ET.fromstring(self.raw_request) + self._content_element = xelements[0][0] + + +class ResultBase(lib.ResultBase): + pass + + +class ResponseBase(lib.ResponseBase): + _content: SoapEnvelop + """ + Soap envelope content, should be initialized in response sub class + """ + sending_buffer: str + + def __init__(self, request: RequestBase, result: ResultBase) -> None: + assert issubclass(type(request), RequestBase) + assert issubclass(type(result), ResultBase) + if not hasattr(self, "_content"): + raise UniSpyException( + "Soap envelope content must be initialized in response sub class" + ) + assert isinstance(self._content, SoapEnvelop) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = str(self._content) diff --git a/src/servers/webservices/abstractions/handler.py b/src/servers/webservices/abstractions/handler.py new file mode 100644 index 000000000..ab37498a4 --- /dev/null +++ b/src/servers/webservices/abstractions/handler.py @@ -0,0 +1,12 @@ +import library.abstractions.handler as lib +from servers.webservices.applications.client import Client +from servers.webservices.abstractions.contracts import RequestBase + + +class CmdHandlerBase(lib.CmdHandlerBase): + _client: Client + + def __init__(self, client: Client, request: RequestBase) -> None: + assert isinstance(client, Client) + assert issubclass(type(request), RequestBase) + super().__init__(client, request) diff --git a/src/servers/webservices/aggregations/soap_envelop.py b/src/servers/webservices/aggregations/soap_envelop.py new file mode 100644 index 000000000..4b25e9d47 --- /dev/null +++ b/src/servers/webservices/aggregations/soap_envelop.py @@ -0,0 +1,51 @@ +import xml.etree.ElementTree as ET + + +class SoapEnvelop: + soap_envelop_namespace = "http://schemas.xmlsoap.org/soap/envelope/" + current_element: ET.Element + content: ET.Element + + def __init__(self, body_namespace: str): + self._body_namespace = body_namespace + ET.register_namespace("SOAP-ENV", self.soap_envelop_namespace) + self.content = ET.Element(f"{{{self.soap_envelop_namespace}}}Envelope") + self.body = ET.Element(f"{{{self.soap_envelop_namespace}}}Body") + self.content.append(self.body) + self.current_element = self.body + + def finish_add_sub_element(self): + self.current_element = ET.SubElement( + self.current_element, + ) + + def change_to_element(self, name: str): + self.current_element = self.body.find(f".//{{{self._body_namespace}}}{name}") + + def back_to_parent_element(self): + self.current_element = self.body + + def add(self, name: str, value: object = None): + new_element = ET.SubElement( + self.current_element, f"{{{self._body_namespace}}}{name}" + ) + + if value is not None: + new_element.text = value + self.current_element = new_element + + def __str__(self) -> str: + return ET.tostring( + self.content, xml_declaration=True, encoding="utf-8", method="xml" + ).decode() + + +if __name__ == "__main__": + import xml.etree.ElementTree as ET + + s = SoapEnvelop("http://gamespy.net/AuthService/") + s.add("level1", "1") + s.add("level1.1", "1.1") + s.back_to_parent_element() + s.add("level2", "2") + str(s) diff --git a/src/servers/webservices/applications/client.py b/src/servers/webservices/applications/client.py new file mode 100644 index 000000000..60d648f07 --- /dev/null +++ b/src/servers/webservices/applications/client.py @@ -0,0 +1,27 @@ +from library.abstractions.client import ClientBase, ClientInfoBase + + +class ClientInfo(ClientInfoBase): + """ + Note!! the public exponent on SDK must set to 000001\n + because the multiple inverse of 1 is 1\n + data^1 mod n = data\n + enc^1 mod n = data^1^1 mod n = data\n + I do not want let our server to compute the rsa encryption so I made this trick\n + """ + + PEER_KEY_PRIVATE = "" + PEER_KEY_EXPONENT = "000001" + PEER_KEY_MODULUS = "aefb5064bbd1eb632fa8d57aab1c49366ce0ee3161cbef19f2b7971b63b811790ecbf6a47b34c55f65a0766b40c261c5d69c394cd320842dd2bccba883d30eae8fdba5d03b21b09bfc600dcb30b1b2f3fbe8077630b006dcb54c4254f14891762f72e7bbfe743eb8baf65f9e8c8d11ebe46f6b59e986b4c394cfbc2c8606e29f" + SERVER_DATA = "95980bf5011ce73f2866b995a272420c36f1e8b4ac946f0b5bfe87c9fef0811036da00cfa85e77e00af11c924d425ec06b1dd052feab1250376155272904cbf9da831b0ce3d52964424c0a426b869e2c0ad11ffa3e70496e27ea250adb707a96b3496bff190eafc0b6b9c99db75b02c2a822bb1b5b3d954e7b2c0f9b1487e3e1" + SIGNATURE_PREFIX = "0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003020300C06082A864886F70D020505000410" + + PRIVATE_KEY_D = [0x96, 0xE2, 0xF4, 0xF9, 0x7C, 0x78, 0x81, 0x0C, 0x13, 0xE2, 0xA4, 0xCA, 0xC1, 0x12, 0x44, 0xBB, 0x5B, 0x37, 0xC5, 0x4E, 0x98, 0x20, 0xF1, 0x3F, 0xE9, 0xB5, 0x53, 0xDA, 0x10, 0xB1, 0xE8, 0xEF, 0x8E, 0xB0, 0x3F, 0x87, 0x68, 0x1B, 0x0E, 0x62, 0x4D, 0x1A, 0x8D, 0xE9, 0x17, 0x4C, 0xBE, 0xE5, 0xB8, 0xED, 0x92, 0xE4, 0xBE, 0x74, 0xF8, 0x6C, 0x30, 0x38, 0xCD, 0x7C, 0x1A, 0x20, 0xB9, 0xA3, 0xDB, 0x1D, 0x49, 0x22, 0x62, 0x87, 0x38, 0x68, 0xFB, 0xA0, 0x8E, 0x1E, 0xAB, 0x5C, 0xBA, 0x86, 0x3F, 0x8F, 0xDB, 0xF4, 0x5E, 0xEA, 0x61, 0x4B, 0xBF, 0x6C, 0xFC, 0x47, 0x00, 0x81, 0x44, 0x2A, 0x97, 0x78, 0x7E, 0xB6, 0xEC, 0xA7, 0x1C, 0x48, 0x96, 0x81, 0x6C, 0x2A, 0x62, 0x72, 0x4C, 0x0E, 0x8C, 0xAA, 0xEE, 0xAB, 0x72, 0x78, 0xC2, 0x55, 0x4A, 0x13, 0x80, 0x94, 0x6E, 0xED, 0x21, 0x29] # fmt: skip + + MODULUES = [ 0xA9, 0x3E, 0x00, 0x19, 0x2C, 0x4A, 0x98, 0x69, 0xD7, 0x41, 0x9A, 0xFF, 0x66, 0x2E, 0xCA, 0xD6, 0xC8, 0xB9, 0x99, 0x09, 0xFD, 0xD0, 0xE7, 0xF8, 0xCA, 0xDD, 0x15, 0x32, 0xE8, 0xE3, 0x59, 0x37, 0x40, 0x83, 0xDA, 0xB8, 0xBE, 0x71, 0x7F, 0x60, 0x91, 0x60, 0xCD, 0x6A, 0x54, 0x11, 0xBE, 0xD7, 0x92, 0x7A, 0xD3, 0xB5, 0xC0, 0x0C, 0x4C, 0x4B, 0x34, 0x76, 0x71, 0xF2, 0x3F, 0xE0, 0x1E, 0xBB, 0x2F, 0x83, 0x4B, 0xD8, 0xCA, 0x27, 0xC3, 0x55, 0x3E, 0x1E, 0x6B, 0xC2, 0x85, 0xAF, 0xC6, 0x3E, 0xC0, 0xE1, 0x1F, 0x59, 0xCA, 0xF6, 0xAC, 0x37, 0x5F, 0x4B, 0x0E, 0xB8, 0x2A, 0x4D, 0xA2, 0x2C, 0x0C, 0x10, 0x1D, 0x09, 0x13, 0x1A, 0x7C, 0x42, 0x0D, 0x4C, 0xC4, 0xD0, 0x95, 0x62, 0x4C, 0x42, 0xBC, 0xD8, 0xD7, 0x19, 0x16, 0xC8, 0xDC, 0x00, 0x48, 0x08, 0x6D, 0x74, 0x6A, 0x31, 0x55, 0xC7] # fmt: skip + + EXPONENT = [0x01, 0x00, 0x01] # fmt: skip + + +class Client(ClientBase): + info: ClientInfo diff --git a/src/servers/webservices/exceptions/general.py b/src/servers/webservices/exceptions/general.py new file mode 100644 index 000000000..f1c74eb6d --- /dev/null +++ b/src/servers/webservices/exceptions/general.py @@ -0,0 +1,6 @@ +from library.exceptions.error import UniSpyException + + +class WebExceptions(UniSpyException): + pass + diff --git a/src/servers/webservices/modules/__init__.py b/src/servers/webservices/modules/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/modules/altas/___init__.py b/src/servers/webservices/modules/altas/___init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/modules/auth/___init__.py b/src/servers/webservices/modules/auth/___init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/modules/auth/abstractions/___init__.py b/src/servers/webservices/modules/auth/abstractions/___init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/modules/auth/abstractions/contracts.py b/src/servers/webservices/modules/auth/abstractions/contracts.py new file mode 100644 index 000000000..3dc4c5008 --- /dev/null +++ b/src/servers/webservices/modules/auth/abstractions/contracts.py @@ -0,0 +1,104 @@ +import hashlib +import struct +import servers.webservices.abstractions.contracts as lib +from servers.webservices.aggregations.soap_envelop import SoapEnvelop +from servers.webservices.applications.client import ClientInfo +from servers.webservices.modules.auth.exceptions.general import AuthException +import datetime + +NAMESPACE = "http://gamespy.net/AuthService/" + + +class LoginRequestBase(lib.RequestBase): + version: int + partner_code: int + namespace_id: int + + def parse(self) -> None: + super().parse() + version = self._content_element.find(f".//{{{NAMESPACE}}}version") + if version is None: + raise AuthException("version is missing from the request.") + self.version = int(version) + partner_id = self._content_element.find(f".//{{{NAMESPACE}}}version") + if version is None: + raise AuthException("partner id is missing from the request.") + self.partner_code = int(partner_id) + namespace_id = self._content_element.find(f".//{{{NAMESPACE}}}version") + if namespace_id is None: + raise AuthException("namespace id is missing from the request.") + self.namespace_id = int(namespace_id) + + +class LoginResultBase(lib.ResultBase): + response_code: int = 0 + length: int = 303 + user_id: int + profile_id: int + profile_nick: str + unique_nick: str + cdkey_hash: str + + +class LoginResponseBase(lib.ResponseBase): + _request: LoginRequestBase + _result: LoginResultBase + _content: SoapEnvelop = SoapEnvelop("http://gamespy.net/AuthService/") + _expiretime: int = int( + (datetime.datetime.now() + datetime.timedelta(days=1)).timestamp() + ) + + def __init__(self, request: LoginRequestBase, result: LoginResultBase) -> None: + assert isinstance(request, LoginRequestBase) + assert isinstance(result, LoginResultBase) + super().__init__(request, result) + + def build(self) -> None: + self._build_context() + super().build() + + def _build_context(self): + self._content.add("responseCode", "h") + self._content.add("certificate") + self._content.add("length", self._result.length) + self._content.add("version", self._request.version) + self._content.add("partnercode", self._request.partner_code) + self._content.add("namespaceid", self._request.namespace_id) + self._content.add("userid", self._result.user_id) + self._content.add("profileid", self._result.profile_id) + self._content.add("expiretime", self._expiretime) + self._content.add("profilenick", self._result.profile_nick) + self._content.add("uniquenick", self._result.unique_nick) + self._content.add("cdkeyhash", self._result.cdkey_hash) + self._content.add("peerkeymodulus", ClientInfo.PEER_KEY_MODULUS) + self._content.add("peerkeyexponent", ClientInfo.PEER_KEY_EXPONENT) + self._content.add("serverdata", ClientInfo.SERVER_DATA) + hash_str = self.__compute_hash() + self._content.add("signature", ClientInfo.SIGNATURE_PREFIX + hash_str) + self._content.back_to_parent_element() + self._content.add("peerkeyprivate", ClientInfo.PEER_KEY_EXPONENT) + + def __compute_hash(self) -> str: + """return md5 str""" + data_to_hash = bytearray() + data_to_hash.extend(self._result.length.to_bytes(4, byteorder="little")) + data_to_hash.extend(self._request.version.to_bytes(4, byteorder="little")) + data_to_hash.extend(self._request.partner_code.to_bytes(4, byteorder="little")) + data_to_hash.extend(self._request.namespace_id.to_bytes(4, byteorder="little")) + data_to_hash.extend(self._result.user_id.to_bytes(4, byteorder="little")) + data_to_hash.extend(self._result.profile_id.to_bytes(4, byteorder="little")) + data_to_hash.extend(self._expiretime.to_bytes(4, byteorder="little")) + data_to_hash.extend(self._result.profile_nick.encode("ascii")) + data_to_hash.extend(self._result.unique_nick.encode("ascii")) + data_to_hash.extend(self._result.cdkey_hash.encode("ascii")) + + data_to_hash.extend(bytes.fromhex(ClientInfo.PEER_KEY_MODULUS)) + data_to_hash.append(0x01) + + # server data should be convert to bytes[128] then added to list + data_to_hash.extend(bytes.fromhex(ClientInfo.SERVER_DATA)) + + hash_object = hashlib.md5() + hash_object.update(data_to_hash) + hash_string = hash_object.hexdigest() + return hash_string diff --git a/src/servers/webservices/modules/auth/contracts/requests.py b/src/servers/webservices/modules/auth/contracts/requests.py new file mode 100644 index 000000000..d0ac32993 --- /dev/null +++ b/src/servers/webservices/modules/auth/contracts/requests.py @@ -0,0 +1,118 @@ +from servers.webservices.modules.auth.abstractions.contracts import ( + NAMESPACE, + LoginRequestBase, +) +from servers.webservices.modules.auth.exceptions.general import AuthException + + +class LoginProfileRequest(LoginRequestBase): + email: str + uniquenick: str + cdkey: str + password: str + + def parse(self) -> None: + super().parse() + self.email = self._content_element.find(f".//{{{NAMESPACE}}}email") + if self.email is None: + raise AuthException("email is missing from the request.") + + self.uniquenick = self._content_element.find(f".//{{{NAMESPACE}}}uniquenick") + if self.uniquenick is None: + raise AuthException("uniquenick is missing from the request.") + + self.cdkey = self._content_element.find(f".//{{{NAMESPACE}}}cdkey") + if self.cdkey is None: + raise AuthException("cdkey is missing from the request.") + + self.password = self._content_element.find(f".//{{{NAMESPACE}}}password") + if self.password is None: + raise AuthException("password is missing from the request.") + + +class LoginProfileWithGameIdRequest(LoginProfileRequest): + game_id: int + + def parse(self) -> None: + super().parse() + game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") + if game_id is None: + raise AuthException("game id is missing from the request.") + + self.game_id = int(game_id) + + +class LoginPs3CertRequest(LoginRequestBase): + ps3_cert: str + + def parse(self) -> None: + super().parse() + self.ps3_cert = self._content_element.find(f".//{{{NAMESPACE}}}npticket") + if self.ps3_cert is None: + raise AuthException("ps3cert is missing from the request") + + +class LoginPs3CertWithGameIdRequest(LoginPs3CertRequest): + game_id: int + + def parse(self) -> None: + super().parse() + game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") + if game_id is None: + raise AuthException("game id is missing from the request.") + + self.game_id = int(game_id) + + +class LoginRemoteAuthRequest(LoginRequestBase): + auth_token: str + challenge: str + + def parse(self) -> None: + super().parse() + self.auth_token = self._content_element.find(f".//{{{NAMESPACE}}}authtoken") + if self.auth_token is None: + raise AuthException("authtoken is missing from the request.") + + self.challenge = self._content_element.find(f".//{{{NAMESPACE}}}challenge") + if self.challenge is None: + raise AuthException("challenge is missing from the request.") + + +class LoginRemoteAuthWithGameIdRequest(LoginRemoteAuthRequest): + game_id: int + + def parse(self) -> None: + super().parse() + game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") + if game_id is None: + raise AuthException("game id is missing from the request.") + + self.game_id = int(game_id) + + +class LoginUniqueNickRequest(LoginRequestBase): + uniquenick: str + password: str + + def parse(self) -> None: + super().parse() + self.uniquenick = self._content_element.find(f".//{{{NAMESPACE}}}uniquenick") + if self.uniquenick is None: + raise AuthException("uniquenick is missing from the request.") + + self.password = self._content_element.find(f".//{{{NAMESPACE}}}password") + if self.password is None: + raise AuthException("password is missing from the request.") + + +class LoginUniqueNickWithGameIdRequest(LoginUniqueNickRequest): + game_id: int + + def parse(self) -> None: + super().parse() + game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") + if game_id is None: + raise AuthException("game id is missing from the request.") + + self.game_id = int(game_id) diff --git a/src/servers/webservices/modules/auth/contracts/responses.py b/src/servers/webservices/modules/auth/contracts/responses.py new file mode 100644 index 000000000..12e168e22 --- /dev/null +++ b/src/servers/webservices/modules/auth/contracts/responses.py @@ -0,0 +1,72 @@ +from servers.webservices.modules.auth.abstractions.contracts import LoginResponseBase +from servers.webservices.modules.auth.contracts.requests import ( + LoginProfileRequest, + LoginProfileWithGameIdRequest, +) +from servers.webservices.modules.auth.contracts.results import ( + LoginProfileResult, + LoginPs3CertResult, +) + + +class LoginProfileResponse(LoginResponseBase): + _request: LoginProfileRequest + _result: LoginProfileResult + + def build(self) -> None: + self._content.add("LoginProfileResult") + super().build() + + +class LoginProfileWithGameIdResponse(LoginResponseBase): + _request: LoginProfileWithGameIdRequest + + def build(self) -> None: + self._content.add("LoginProfileWithGameIdResult") + super().build() + + +class LoginPs3CertResponse(LoginResponseBase): + _result: LoginPs3CertResult + + def build(self) -> None: + self._content.add("LoginPs3CertResult") + self._content.add("responseCode", self._result.response_code) + self._content.add("authToken", self._result.auth_token) + self._content.add("partnerChallenge", self._result.partner_challenge) + super().build() + + +class LoginPs3CertWithGameIdResponse(LoginResponseBase): + _result: LoginPs3CertResult + + def build(self) -> None: + self._content.add("LoginPs3CertWithGameIdResult") + self._content.add("responseCode", self._result.response_code) + self._content.add("authToken", self._result.auth_token) + self._content.add("partnerChallenge", self._result.partner_challenge) + super().build() + + +class LoginRemoteAuthResponse(LoginResponseBase): + def build(self) -> None: + self._content.add("LoginRemoteAuthResult") + super().build() + + +class LoginRemoteAuthWithGameIdResponse(LoginResponseBase): + def build(self) -> None: + self._content.add("LoginRemoteAuthWithGameIdResult") + super().build() + + +class LoginUniqueNickResponse(LoginResponseBase): + def build(self) -> None: + self._content.add("LoginUniqueNickResult") + super().build() + + +class LoginUniqueNickWithGameIdResponse(LoginResponseBase): + def build(self) -> None: + self._content.add("LoginUniqueNickWithGameIdResult") + super().build() diff --git a/src/servers/webservices/modules/auth/contracts/results.py b/src/servers/webservices/modules/auth/contracts/results.py new file mode 100644 index 000000000..fbd63099d --- /dev/null +++ b/src/servers/webservices/modules/auth/contracts/results.py @@ -0,0 +1,18 @@ +from servers.webservices.modules.auth.abstractions.contracts import LoginResultBase + + +class LoginProfileResult(LoginResultBase): + pass + + +class LoginPs3CertResult(LoginResultBase): + auth_token: str + partner_challenge: str + + +class LoginRemoteAuthResult(LoginResultBase): + pass + + +class LoginUniqueNickResult(LoginResultBase): + pass diff --git a/src/servers/webservices/modules/auth/exceptions/general.py b/src/servers/webservices/modules/auth/exceptions/general.py new file mode 100644 index 000000000..993184499 --- /dev/null +++ b/src/servers/webservices/modules/auth/exceptions/general.py @@ -0,0 +1,5 @@ +from servers.webservices.exceptions.general import WebExceptions + + +class AuthException(WebExceptions): + pass diff --git a/src/servers/webservices/modules/auth/handlers/general.py b/src/servers/webservices/modules/auth/handlers/general.py new file mode 100644 index 000000000..12e728ff7 --- /dev/null +++ b/src/servers/webservices/modules/auth/handlers/general.py @@ -0,0 +1,80 @@ +from servers.webservices.abstractions.handler import CmdHandlerBase +from servers.webservices.modules.auth.abstractions.contracts import LoginResultBase +from servers.webservices.modules.auth.contracts.requests import ( + LoginProfileRequest, + LoginProfileWithGameIdRequest, + LoginPs3CertRequest, + LoginPs3CertWithGameIdRequest, + LoginRemoteAuthRequest, + LoginRemoteAuthWithGameIdRequest, + LoginUniqueNickRequest, + LoginUniqueNickWithGameIdRequest, +) +from servers.webservices.modules.auth.contracts.responses import ( + LoginProfileResponse, + LoginProfileWithGameIdResponse, + LoginRemoteAuthResponse, + LoginRemoteAuthWithGameIdResponse, + LoginUniqueNickResponse, + LoginUniqueNickWithGameIdResponse, +) +from servers.webservices.modules.auth.contracts.results import ( + LoginProfileResult, + LoginPs3CertResult, + LoginRemoteAuthResult, +) + + +class LoginProfileHandler(CmdHandlerBase): + _request: LoginProfileRequest + _result: LoginProfileResult + + def _response_construct(self) -> None: + self._response = LoginProfileResponse(self._request, self._result) + + +class LoginProfileWithGameIdHandler(CmdHandlerBase): + _request: LoginProfileWithGameIdRequest + _result: LoginProfileResult + + def _response_construct(self) -> None: + self._response = LoginProfileWithGameIdResponse(self._request, self._result) + + +class LoginPs3CertHandler(CmdHandlerBase): + _request: LoginPs3CertRequest + _result: LoginPs3CertResult + + +class LoginPs3CertWithGameIdHandler(CmdHandlerBase): + _request: LoginPs3CertWithGameIdRequest + _result: LoginPs3CertResult + + +class LoginRemoteAuthHandler(CmdHandlerBase): + _request: LoginRemoteAuthRequest + _result: LoginRemoteAuthResult + + def _response_construct(self) -> None: + self._response = LoginRemoteAuthResponse(self._request, self._result) + + +class LoginRemoteAuthWithGameIdHandler(CmdHandlerBase): + _request: LoginRemoteAuthWithGameIdRequest + + def _response_construct(self) -> None: + self._response = LoginRemoteAuthWithGameIdResponse(self._request, self._result) + + +class LoginUniqueNickHandler(CmdHandlerBase): + _request: LoginUniqueNickRequest + + def _response_construct(self) -> None: + self._response = LoginUniqueNickResponse(self._request, self._result) + + +class LoginUniqueNickWithGameIdHandler(CmdHandlerBase): + _request: LoginUniqueNickWithGameIdRequest + + def _response_construct(self) -> None: + self._response = LoginUniqueNickWithGameIdResponse(self._request, self._result) diff --git a/src/servers/webservices/modules/direct2game/__init__.py b/src/servers/webservices/modules/direct2game/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/modules/direct2game/abstractions/__init__.py b/src/servers/webservices/modules/direct2game/abstractions/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/modules/direct2game/abstractions/contracts.py b/src/servers/webservices/modules/direct2game/abstractions/contracts.py new file mode 100644 index 000000000..f372548e8 --- /dev/null +++ b/src/servers/webservices/modules/direct2game/abstractions/contracts.py @@ -0,0 +1,17 @@ +import servers.webservices.abstractions.contracts as lib +from servers.webservices.aggregations.soap_envelop import SoapEnvelop + +NAMESPACE = "http://gamespy.net/commerce/" + + +class RequestBase(lib.RequestBase): + pass + + +class ResultBase(lib.ResultBase): + pass + + +class ResponseBase(lib.ResponseBase): + _content: SoapEnvelop = SoapEnvelop(NAMESPACE) + pass diff --git a/src/servers/webservices/modules/direct2game/abstractions/handler.py b/src/servers/webservices/modules/direct2game/abstractions/handler.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/modules/direct2game/contracts/__init__.py b/src/servers/webservices/modules/direct2game/contracts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/modules/direct2game/contracts/requests.py b/src/servers/webservices/modules/direct2game/contracts/requests.py new file mode 100644 index 000000000..cf6aa1a75 --- /dev/null +++ b/src/servers/webservices/modules/direct2game/contracts/requests.py @@ -0,0 +1,55 @@ +from servers.webservices.exceptions.general import WebExceptions +from servers.webservices.modules.direct2game.abstractions.contracts import ( + NAMESPACE, + RequestBase, +) + + +class GetPurchaseHistoryRequest(RequestBase): + game_id: int + access_token: str + proof: str + certificate: str + + def parse(self) -> None: + super().parse() + game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") + if game_id is None: + raise WebExceptions("gameid is missing from the request.") + self.game_id = int(game_id) + + self.proof = self._content_element.find(f".//{{{NAMESPACE}}}proof") + if self.proof is None: + raise WebExceptions("proof is missing from the request.") + self.access_token = self._content_element.find( + f".//{{{NAMESPACE}}}access_token" + ) + if self.access_token is None: + raise WebExceptions("access_token is missing from the request.") + self.certificate = self._content_element.find(f".//{{{NAMESPACE}}}certificate") + if self.certificate is None: + raise WebExceptions("certificate is missing from the request.") + + +class GetStoreAvailabilityRequest(RequestBase): + game_id: int + version: int + region: str + access_token: str + + def parse(self) -> None: + super().parse() + game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") + if game_id is None: + raise WebExceptions("gameid is missing from the request.") + self.game_id = int(game_id) + version = self._content_element.find(f".//{{{NAMESPACE}}}version") + if version is None: + raise WebExceptions("version is missing from the request.") + self.version = int(version) + self.region = self._content_element.find(f".//{{{NAMESPACE}}}region") + if self.region is None: + raise WebExceptions("region is missing from the request.") + self.access_token = self._content_element.find(f".//{{{NAMESPACE}}}accesstoken") + if self.access_token is None: + raise WebExceptions("accesstoken is missing from the request.") diff --git a/src/servers/webservices/modules/direct2game/contracts/responses.py b/src/servers/webservices/modules/direct2game/contracts/responses.py new file mode 100644 index 000000000..140c54646 --- /dev/null +++ b/src/servers/webservices/modules/direct2game/contracts/responses.py @@ -0,0 +1,32 @@ +from servers.webservices.modules.direct2game.abstractions.contracts import ResponseBase +from servers.webservices.modules.direct2game.contracts.results import ( + GetPurchaseHistoryResult, + GetStoreAvailabilityResult, +) + + +class GetPurchaseHistoryResponse(ResponseBase): + _result: GetPurchaseHistoryResult + + def build(self) -> None: + self._content.add("GetPurchaseHistoryResponse") + self._content.add("GetPurchaseHistoryResult") + self._content.add("status") + self._content.add("code", self._result.code) + self._content.change_to_element("GetPurchaseHistoryResult") + self._content.add("orderpurchases") + self._content.add("count", 0) + super().build() + + +class GetStoreAvailabilityResponse(ResponseBase): + _result: GetStoreAvailabilityResult + + def build(self) -> None: + self._content.add("GetStoreAvailabilityResponse") + self._content.add("GetStoreAvailabilityResult") + self._content.add("status") + self._content.add("code", self._result.code) + self._content.change_to_element("GetStoreAvailabilityResult") + self._content.add("storestatusid", int(self._result.store_status_id)) + super().build() diff --git a/src/servers/webservices/modules/direct2game/contracts/results.py b/src/servers/webservices/modules/direct2game/contracts/results.py new file mode 100644 index 000000000..c23f1e3bb --- /dev/null +++ b/src/servers/webservices/modules/direct2game/contracts/results.py @@ -0,0 +1,19 @@ +from enum import IntEnum + +from servers.webservices.modules.direct2game.abstractions.contracts import ResultBase + + +class GetPurchaseHistoryResult(ResultBase): + code: int = 0 + + +class AvailableCode(IntEnum): + STORE_ONLINE = 10 + STORE_OFFLINE_FOR_MAINTAIN = 20 + STORE_OFFLINE_RETIRED = 50 + STORE_NOT_YET_LAUNCHED = 100 + + +class GetStoreAvailabilityResult(ResultBase): + code: int = 0 + store_status_id: AvailableCode = AvailableCode.STORE_ONLINE diff --git a/src/servers/webservices/modules/in_game_ad/__init__.py b/src/servers/webservices/modules/in_game_ad/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/modules/patching_and_tracking/__init__.py b/src/servers/webservices/modules/patching_and_tracking/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/modules/racing/__init__.py b/src/servers/webservices/modules/racing/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/modules/sake/__init__.py b/src/servers/webservices/modules/sake/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/tests/auth.py b/src/servers/webservices/tests/auth.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/tests/direct2game.py b/src/servers/webservices/tests/direct2game.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/tests/sake.py b/src/servers/webservices/tests/sake.py new file mode 100644 index 000000000..69323a006 --- /dev/null +++ b/src/servers/webservices/tests/sake.py @@ -0,0 +1,31 @@ +import unittest + +from servers.webservices.modules.auth.contracts.requests import LoginUniqueNickRequest + + +class Auth(unittest.TestCase): + def test_login_unique_nick(self) -> None: + raw = """ + + + + + 1 + 95 + 95 + spyguy + + 0000 + + + + + """ + r = LoginUniqueNickRequest(raw) + r.parse() + \ No newline at end of file diff --git a/src/test/Anno1701.cs b/src/test/Anno1701.cs deleted file mode 100644 index f4f891ca0..000000000 --- a/src/test/Anno1701.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.Interface; -using Xunit; - -namespace UniSpy.Server.Test -{ - public class Anno1701 - { - [Fact] - public void Test20221106() - { - var qrClient = QueryReport.V2.Test.MockObject.CreateClient("79.209.224.29", 21701); - var sbClient = ServerBrowser.V2.Test.MockObject.CreateClient("79.209.224.29", 51104); - - var clients = new Dictionary(){ - {"qr",qrClient}, - {"sb",sbClient} - }; - - var requests = new List>() - { - new KeyValuePair("qr",new byte[]{0x09,0x00,0x00,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair("qr",new byte[]{0x03,0xB2,0x1A,0xF9,0x05,0x6C,0x6F,0x63,0x61,0x6C,0x69,0x70,0x30,0x00,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x30,0x2E,0x35,0x30,0x00,0x6C,0x6F,0x63,0x61,0x6C,0x69,0x70,0x31,0x00,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x31,0x32,0x32,0x2E,0x31,0x00,0x6C,0x6F,0x63,0x61,0x6C,0x70,0x6F,0x72,0x74,0x00,0x32,0x31,0x37,0x30,0x31,0x00,0x6E,0x61,0x74,0x6E,0x65,0x67,0x00,0x31,0x00,0x73,0x74,0x61,0x74,0x65,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x00,0x33,0x00,0x67,0x61,0x6D,0x65,0x6E,0x61,0x6D,0x65,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x69,0x70,0x00,0x30,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x70,0x6F,0x72,0x74,0x00,0x30,0x00,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x00,0x74,0x65,0x73,0x74,0x00,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x00,0x6F,0x70,0x65,0x6E,0x73,0x74,0x61,0x67,0x69,0x6E,0x67,0x00,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x31,0x00,0x6D,0x61,0x78,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x00,0x32,0x31,0x39,0x30,0x33,0x00,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x52,0x61,0x6E,0x64,0x6F,0x6D,0x20,0x6D,0x61,0x70,0x00,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x00,0x45,0x61,0x73,0x79,0x00,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x6F,0x70,0x74,0x69,0x6F,0x6E,0x73,0x00,0x33,0x36,0x39,0x31,0x37,0x34,0x34,0x36,0x38,0x00,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x31,0x00,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x50,0x76,0x50,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x77,0x69,0x6E,0x63,0x6F,0x6E,0x64,0x69,0x74,0x69,0x6F,0x6E,0x73,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x75,0x73,0x65,0x72,0x63,0x6F,0x6E,0x74,0x65,0x6E,0x74,0x5F,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x00,0x00,0x00,0x01,0x70,0x6C,0x61,0x79,0x65,0x72,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x00,0x73,0x70,0x6F,0x72,0x65,0x73,0x69,0x72,0x69,0x75,0x73,0x00,0x30,0x00,0x30,0x00,0x00,0x01,0x00}), - new KeyValuePair("sb",new byte[]{0x00,0x9A,0x00,0x01,0x03,0x8F,0x55,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x4C,0x3E,0x7C,0x42,0x54,0x26,0x56,0x26,0x67,0x72,0x6F,0x75,0x70,0x69,0x64,0x20,0x69,0x73,0x20,0x6E,0x75,0x6C,0x6C,0x00,0x5C,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x5C,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x5C,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x5C,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x5C,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x5C,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x5C,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x5C,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x00,0x00,0x00,0x04}), - new KeyValuePair("sb",new byte[]{0x00,0x9A,0x00,0x01,0x03,0x8F,0x55,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x37,0x42,0x26,0x5C,0x24,0x44,0x4A,0x51,0x67,0x72,0x6F,0x75,0x70,0x69,0x64,0x20,0x69,0x73,0x20,0x6E,0x75,0x6C,0x6C,0x00,0x5C,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x5C,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x5C,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x5C,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x5C,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x5C,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x5C,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x5C,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x00,0x00,0x00,0x04}), - new KeyValuePair("sb",new byte[] {0x00,0x9A,0x00,0x01,0x03,0x8F,0x55,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x75,0x4D,0x32,0x73,0x72,0x35,0x24,0x71,0x67,0x72,0x6F,0x75,0x70,0x69,0x64,0x20,0x69,0x73,0x20,0x6E,0x75,0x6C,0x6C,0x00,0x5C,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x5C,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x5C,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x5C,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x5C,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x5C,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x5C,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x5C,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x00,0x00,0x00,0x04}), - // send message request - new KeyValuePair("sb",new byte[]{0x00,0x13,0x02,0x4F,0xD1,0xE0,0x1D,0x54,0xC5}), - // natneg message request - new KeyValuePair("sb",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x00,0x00,0x2C,0xFD}) - }; - // When - TestClasses.ServersSimulation(requests, clients); - - // Then - } - [Fact] - public void Test20221104() - { - var qrClient1 = QueryReport.V2.Test.MockObject.CreateClient("79.209.224.29", 21701); - var qrClient2 = QueryReport.V2.Test.MockObject.CreateClient("31.18.120.193", 21701); - var sbClient1 = ServerBrowser.V2.Test.MockObject.CreateClient("79.209.224.29", 45340); - var sbClient2 = ServerBrowser.V2.Test.MockObject.CreateClient("31.18.120.193", 50587); - var natClient1 = NatNegotiation.Test.MockObject.CreateClient("79.209.224.29", 123); - var natClient2 = NatNegotiation.Test.MockObject.CreateClient("31.18.120.193", 123); - - var clients = new Dictionary(){ - {"qr1",qrClient1}, - {"qr2",qrClient2}, - {"sb1",sbClient1}, - {"sb2",sbClient2}, - {"nat1",natClient1}, - {"nat2",natClient2} - }; - var requests = new List>() - { - new KeyValuePair("qr1",new byte[] {0x09,0x00,0x00,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x0}), - new KeyValuePair("qr1",new byte[] {0x03,0x98,0x92,0x25,0xA0,0x6C,0x6F,0x63,0x61,0x6C,0x69,0x70,0x30,0x00,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x30,0x2E,0x35,0x30,0x00,0x6C,0x6F,0x63,0x61,0x6C,0x69,0x70,0x31,0x00,0x31,0x39,0x32,0x2E,0x31,0x36,0x38,0x2E,0x31,0x32,0x32,0x2E,0x31,0x00,0x6C,0x6F,0x63,0x61,0x6C,0x70,0x6F,0x72,0x74,0x00,0x32,0x31,0x37,0x30,0x31,0x00,0x6E,0x61,0x74,0x6E,0x65,0x67,0x00,0x31,0x00,0x73,0x74,0x61,0x74,0x65,0x63,0x68,0x61,0x6E,0x67,0x65,0x64,0x00,0x33,0x00,0x67,0x61,0x6D,0x65,0x6E,0x61,0x6D,0x65,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x69,0x70,0x00,0x30,0x00,0x70,0x75,0x62,0x6C,0x69,0x63,0x70,0x6F,0x72,0x74,0x00,0x30,0x00,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x00,0x28,0x75,0x6E,0x6B,0x6E,0x6F,0x77,0x6E,0x20,0x67,0x61,0x6D,0x65,0x29,0x00,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x00,0x6F,0x70,0x65,0x6E,0x73,0x74,0x61,0x67,0x69,0x6E,0x67,0x00,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x31,0x00,0x6D,0x61,0x78,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x34,0x00,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x00,0x32,0x31,0x39,0x30,0x33,0x00,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x52,0x61,0x6E,0x64,0x6F,0x6D,0x20,0x6D,0x61,0x70,0x00,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x00,0x45,0x61,0x73,0x79,0x00,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x6F,0x70,0x74,0x69,0x6F,0x6E,0x73,0x00,0x33,0x36,0x39,0x31,0x37,0x34,0x34,0x36,0x38,0x00,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x00,0x00,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x00,0x00,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x50,0x76,0x50,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x77,0x69,0x6E,0x63,0x6F,0x6E,0x64,0x69,0x74,0x69,0x6F,0x6E,0x73,0x00,0x30,0x00,0x73,0x65,0x74,0x74,0x69,0x6E,0x67,0x73,0x5F,0x75,0x73,0x65,0x72,0x63,0x6F,0x6E,0x74,0x65,0x6E,0x74,0x5F,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x00,0x00,0x00,0x00,0x01,0x70,0x6C,0x61,0x79,0x65,0x72,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x70,0x69,0x6E,0x67,0x5F,0x00,0x00,0x73,0x70,0x6F,0x72,0x65,0x73,0x69,0x72,0x69,0x75,0x73,0x00,0x30,0x00,0x30,0x00,0x00,0x01,0x00}), - new KeyValuePair("sb1",new byte[] {0x00,0x9A,0x00,0x01,0x03,0x8F,0x55,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00,0x52,0x63,0x58,0x3B,0x4D,0x28,0x7B,0x47,0x67,0x72,0x6F,0x75,0x70,0x69,0x64,0x20,0x69,0x73,0x20,0x6E,0x75,0x6C,0x6C,0x00,0x5C,0x68,0x6F,0x73,0x74,0x6E,0x61,0x6D,0x65,0x5C,0x67,0x61,0x6D,0x65,0x6D,0x6F,0x64,0x65,0x5C,0x67,0x61,0x6D,0x65,0x76,0x65,0x72,0x5C,0x67,0x61,0x6D,0x65,0x74,0x79,0x70,0x65,0x5C,0x70,0x61,0x73,0x73,0x77,0x6F,0x72,0x64,0x5C,0x6D,0x61,0x70,0x6E,0x61,0x6D,0x65,0x5C,0x6E,0x75,0x6D,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6E,0x75,0x6D,0x61,0x69,0x70,0x6C,0x61,0x79,0x65,0x72,0x73,0x5C,0x6F,0x70,0x65,0x6E,0x73,0x6C,0x6F,0x74,0x73,0x5C,0x67,0x61,0x6D,0x65,0x76,0x61,0x72,0x69,0x61,0x6E,0x74,0x00,0x00,0x00,0x00,0x04}), - new KeyValuePair("qr2",new byte[] {0x07,0x98,0x92,0x25,0xA0,0x00,0x00,0x00,0x00}), - }; - - - TestClasses.ServersSimulation(requests, clients); - } - /// - /// Natneg fail - /// - [Fact] - public void Anno1701Test20201109() - { - // Given - var requests = new List>() - { - new KeyValuePair("client1",new byte[] {0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x2D,0xA5,0x00,0x00,0x01,0xC0,0xA8,0x00,0x8D,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair("client2",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x2D,0xA5,0x02,0x00,0x01,0xC0,0xA8,0x00,0x8D,0x54,0xC5,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair("client2",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x2D,0xA5,0x03,0x00,0x01,0xC0,0xA8,0x00,0x8D,0x54,0xC5,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair("client2",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x2D,0xA5,0x01,0x00,0x01,0xC0,0xA8,0x00,0x8D,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair("client3",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x2D,0xA5,0x00,0x01,0x01,0xC0,0xA8,0x00,0x32,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair("client4",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x2D,0xA5,0x01,0x01,0x01,0xC0,0xA8,0x00,0x32,0x00,0x00,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair("client4",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x2D,0xA5,0x02,0x01,0x01,0xC0,0xA8,0x00,0x32,0x54,0xC5,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}), - new KeyValuePair("client4",new byte[]{0xFD,0xFC,0x1E,0x66,0x6A,0xB2,0x03,0x00,0x00,0x00,0x2D,0xA5,0x03,0x01,0x01,0xC0,0xA8,0x00,0x32,0x54,0xC5,0x61,0x6E,0x6E,0x6F,0x31,0x37,0x30,0x31,0x00}) - }; - var client1 = NatNegotiation.Test.MockObject.CreateClient("79.209.224.29", 21701); - var client2 = NatNegotiation.Test.MockObject.CreateClient("79.209.224.29", 51298); - var client3 = NatNegotiation.Test.MockObject.CreateClient("79.209.224.29", 1024); - var client4 = NatNegotiation.Test.MockObject.CreateClient("79.209.224.29", 41218); - - - var clients = new Dictionary() - { - {"client1",client1}, - {"client2",client2}, - {"client3",client3}, - {"client4",client4}, - }; - TestClasses.ServersSimulation(requests, clients); - // because the process is running in background we need to wait it finish, so we can debug - // Thread.Sleep(10000); - } - } -} \ No newline at end of file diff --git a/src/test/TestClasses.cs b/src/test/TestClasses.cs deleted file mode 100644 index bfb19d818..000000000 --- a/src/test/TestClasses.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; -using UniSpy.Server.Core.Abstraction.Interface; - -namespace UniSpy.Server.Test -{ - public static class TestClasses - { - public static void ServersSimulation(List> requests, Dictionary clients) - { - foreach (var req in requests) - { - ((ITestClient)clients[req.Key]).TestReceived(req.Value); - } - } - } -} \ No newline at end of file diff --git a/src/test/UniSpy.Server.Test.csproj b/src/test/UniSpy.Server.Test.csproj deleted file mode 100644 index 32d422d28..000000000 --- a/src/test/UniSpy.Server.Test.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - net6.0 - false - ..\..\common\Icon\UniSpy_Logo.ico - - - AnyCPU - ..\..\build\$(Configuration) - - - ..\..\build\$(Configuration) - - - - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - - - - - - - - \ No newline at end of file diff --git a/src/tests/__init__.py b/src/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/tests/library/__init__.py b/src/tests/library/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/tests/library/encrypt_test.py b/src/tests/library/encrypt_test.py new file mode 100644 index 000000000..eb4dc2780 --- /dev/null +++ b/src/tests/library/encrypt_test.py @@ -0,0 +1,19 @@ +import unittest +from library.encryption.gs_encryption import ChatCrypt + + +class GSEncryptionTest(unittest.TestCase): + def test_encryption(self): + enc = ChatCrypt("123345") + result = enc.encrypt("hello") + self.assertEqual(result, b"\xe9D\x91Q\xb9") + + def test_decryption(self): + enc = ChatCrypt("123345") + result = enc.decrypt(b"\xe9D\x91Q\xb9") + self.assertEqual(result, "hello") + + + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From 9434cbede659d9d152f5955c91e33613816253ac Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 14 Jun 2024 22:35:56 +0800 Subject: [PATCH 067/231] fix: added github doc files. --- .github/README.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 .github/README.md diff --git a/.github/README.md b/.github/README.md new file mode 100644 index 000000000..9ee8c6709 --- /dev/null +++ b/.github/README.md @@ -0,0 +1,30 @@ +# UniSpy Server +[![license](https://img.shields.io/github/license/GameProgressive/UniSpyServer.svg)](../LICENSE) +![CIPass](https://github.com/GameProgressive/UniSpyServer/workflows/CI/badge.svg)\ +![platforms](https://img.shields.io/badge/platform-win32%20%7C%20win64%20%7C%20linux%20%7C%20osx-brightgreen.svg)\ +[![discord-banner](https://discord.com/api/guilds/512314008079171615/widget.png?style=banner2)](https://discord.gg/NpggYaD) + +UniSpy is a GameSpy Project that aims to create GameSpy services. + +The server is written in C# and is inspired by the old OpenSpy project. + +## Information & Documentation +See the [wiki](https://github.com/GameProgressive/UniSpyServer/wiki) for more information about project and the UniSpy Server. +You can also use our Software Development Kit to create games or learn about the [UniSpySDK](https://github.com/GameProgressive/UniSpySDK). + +Technical papers and documentation about the GameSpy protocol and the games that use it, [GameSpyDocs](https://github.com/GameProgressive/GameSpyDocs). + +## Credits +* The [contributors](https://github.com/GameProgressive/UniSpyServer/graphs/contributors) of the project +* [Luigi Auriemma](https://aluigi.altervista.org/papers.htm#distrust) for his gamespy papers that were used as a reference +* [BattleSpy](https://github.com/BF2Statistics/BattleSpy) for their library, that we used as a base for UniSpy +* [NetCoreServer](https://github.com/chronoxor/NetCoreServer) for their TCP and UDP server + + +## License +This project is licensed under the [GNU Affero General Public License v3.0](../LICENSE). + + +## Why rewrite C# to python +* The vscode extensions for C# development is become more and more hard to use, and microsoft abandoned the open-source OmniSharp project, replacing it with ites own closed source language server. +* The c# project seems hard to run by users, it require a lot of deploy knowledge and hard for collaborations, for the future of the gamespy emulator, I choose to rewrite this into a opensource and easy high level language - python. From aa0b3fd19bf3b9efddbb80e4b348d44e667edd5e Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 14 Jun 2024 22:36:49 +0800 Subject: [PATCH 068/231] Update README.md --- .github/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index 9ee8c6709..ad1e790e9 100644 --- a/.github/README.md +++ b/.github/README.md @@ -26,5 +26,5 @@ This project is licensed under the [GNU Affero General Public License v3.0](../L ## Why rewrite C# to python -* The vscode extensions for C# development is become more and more hard to use, and microsoft abandoned the open-source OmniSharp project, replacing it with ites own closed source language server. +* The vscode extensions for C# development is become more and more hard to use, and microsoft abandoned the open-source OmniSharp project and replacing it with it's own closed source language server. * The c# project seems hard to run by users, it require a lot of deploy knowledge and hard for collaborations, for the future of the gamespy emulator, I choose to rewrite this into a opensource and easy high level language - python. From 80be8bd0e7d25ecea180e6821c3a106233c5921e Mon Sep 17 00:00:00 2001 From: jiangbangyu Date: Fri, 21 Jun 2024 11:31:11 +0800 Subject: [PATCH 069/231] refactor: change flask to fastapi --- .../library/abstractions/request_base.py | 24 ++-------- src/backends/gamespy/protocols/__init__.py | 0 .../gamespy/protocols/chat/__init__.py | 0 .../gamespy/protocols/chat/requests.py | 2 +- .../gamespy/protocols/natneg/__init__.py | 0 .../natneg/aggregates/init_packet_info.py | 33 ------------- .../natneg/{applications => }/data.py | 0 .../natneg/{contracts => }/requests.py | 0 .../contracts/__init__.py | 0 .../contracts/requests.py | 48 ++++--------------- .../presence_search_player/__init__.py | 0 src/backends/routers/gamespy/gstats.py | 24 ++++++++++ .../gamespy/presence_connection_manager.py | 17 +++---- src/requirement.txt | 3 +- 14 files changed, 46 insertions(+), 105 deletions(-) create mode 100644 src/backends/gamespy/protocols/__init__.py create mode 100644 src/backends/gamespy/protocols/chat/__init__.py create mode 100644 src/backends/gamespy/protocols/natneg/__init__.py delete mode 100644 src/backends/gamespy/protocols/natneg/aggregates/init_packet_info.py rename src/backends/gamespy/protocols/natneg/{applications => }/data.py (100%) rename src/backends/gamespy/protocols/natneg/{contracts => }/requests.py (100%) create mode 100644 src/backends/gamespy/protocols/presence_connection_manager/contracts/__init__.py create mode 100644 src/backends/gamespy/protocols/presence_search_player/__init__.py diff --git a/src/backends/gamespy/library/abstractions/request_base.py b/src/backends/gamespy/library/abstractions/request_base.py index 5c834b4c6..82a1f0883 100644 --- a/src/backends/gamespy/library/abstractions/request_base.py +++ b/src/backends/gamespy/library/abstractions/request_base.py @@ -1,28 +1,10 @@ -import abc from dataclasses import dataclass -from uuid import UUID - -import jsonschema - - +from pydantic import BaseModel, Field,UUID4 @dataclass -class RequestBase(abc.ABC): +class RequestBase(BaseModel): """ The ultimate request base class of all gamespy requests """ - server_id: UUID + server_id: UUID4 raw_request: object - json_schema = { - "type": "object", - "properties": { - "server_id": { - "type": "string", - "pattern": "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$", - }, - }, - } - pass - - def validate(self) -> None: - jsonschema.validate(self.__dict__, self.json_schema) diff --git a/src/backends/gamespy/protocols/__init__.py b/src/backends/gamespy/protocols/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/gamespy/protocols/chat/__init__.py b/src/backends/gamespy/protocols/chat/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/gamespy/protocols/chat/requests.py b/src/backends/gamespy/protocols/chat/requests.py index 9bd508595..e47014d9e 100644 --- a/src/backends/gamespy/protocols/chat/requests.py +++ b/src/backends/gamespy/protocols/chat/requests.py @@ -1,4 +1,4 @@ - +from fastapi import BaseModel # import abc # from dataclasses import dataclass diff --git a/src/backends/gamespy/protocols/natneg/__init__.py b/src/backends/gamespy/protocols/natneg/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/gamespy/protocols/natneg/aggregates/init_packet_info.py b/src/backends/gamespy/protocols/natneg/aggregates/init_packet_info.py deleted file mode 100644 index 9cae60973..000000000 --- a/src/backends/gamespy/protocols/natneg/aggregates/init_packet_info.py +++ /dev/null @@ -1,33 +0,0 @@ -from mongoengine import ( - Document, - StringField, - IntField, - UUIDField, - EnumField, - BooleanField, -) - -from servers.natneg.enums.general import NatClientIndex, NatPortType -import datetime - - -class InitPacketInfo(Document): - server_id = UUIDField(binary=False, required=True) - cookie = IntField(required=True) - version = IntField(required=True) - port_type = EnumField(NatPortType, required=True) - client_index = EnumField(NatClientIndex, required=True) - game_name = StringField(required=True) - use_game_port = BooleanField(required=True) - public_ip = StringField(required=True) - public_port = IntField(required=True) - private_ip = StringField(required=True) - private_port = IntField(required=True) - meta = {"expireAfterSeconds": int(datetime.timedelta(minutes=3).total_seconds())} - - -class NatFailInfo(Document): - public_ip_address1 = StringField(required=True) - public_ip_address2 = StringField(required=True) - meta = {"expireAfterSeconds": int(datetime.timedelta(days=1).total_seconds())} - """expire after 1 day""" diff --git a/src/backends/gamespy/protocols/natneg/applications/data.py b/src/backends/gamespy/protocols/natneg/data.py similarity index 100% rename from src/backends/gamespy/protocols/natneg/applications/data.py rename to src/backends/gamespy/protocols/natneg/data.py diff --git a/src/backends/gamespy/protocols/natneg/contracts/requests.py b/src/backends/gamespy/protocols/natneg/requests.py similarity index 100% rename from src/backends/gamespy/protocols/natneg/contracts/requests.py rename to src/backends/gamespy/protocols/natneg/requests.py diff --git a/src/backends/gamespy/protocols/presence_connection_manager/contracts/__init__.py b/src/backends/gamespy/protocols/presence_connection_manager/contracts/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py b/src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py index ee4dfd319..44fc91ff7 100644 --- a/src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py +++ b/src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py @@ -2,6 +2,7 @@ from typing import Union import jsonschema +from pydantic import UUID4, BaseModel from servers.presence_connection_manager.aggregates.user_status import UserStatus from servers.presence_connection_manager.aggregates.user_status_info import ( UserStatusInfo, @@ -15,45 +16,24 @@ import backends.gamespy.library.abstractions.request_base as lib -@dataclass -class RequestBase(lib.RequestBase): - raw_request: str - json_schema = { - "type": "object", - "properties": { - "raw_request": {"type": "string"}, - }, - } +class RequestBase(BaseModel): + server_id: UUID4 - def validate(self) -> None: - super().validate() - jsonschema.validate(self.__dict__, self.json_schema) + +class ErrorOnParse(RequestBase): + raw_request: str # region buddy -@dataclass class AddBuddyRequest(RequestBase): friend_profile_id: int reason: str - json_schema = { - "type": "object", - "properties": { - "friend_profile_id": {"type": "number"}, - "reason": {"type": "string"}, - }, - } - def validate(self) -> None: - super().validate() - jsonschema.validate(self.__dict__, self.json_schema) - -@dataclass class DelBuddyRequest(RequestBase): friend_profile_id: int -@dataclass class InviteToRequest(RequestBase): product_id: int profile_id: int @@ -61,7 +41,6 @@ class InviteToRequest(RequestBase): """the invite target profile id""" -@dataclass class StatusInfoRequest(RequestBase): is_get_status_info: bool profile_id: int @@ -69,19 +48,18 @@ class StatusInfoRequest(RequestBase): status_info: UserStatusInfo -@dataclass class StatusRequest(RequestBase): status: UserStatus is_get_status: bool # region general -@dataclass + + class KeepAliveRequest(RequestBase): pass -@dataclass class LoginRequest(RequestBase): user_challenge: str response: str @@ -103,24 +81,22 @@ class LoginRequest(RequestBase): firewall: bool -@dataclass class LogoutRequest(RequestBase): pass # region profile -@dataclass + + class AddBlockRequest(RequestBase): taget_id: int -@dataclass class GetProfileRequest(RequestBase): profile_id: int session_key: str -@dataclass class NewProfileRequest(RequestBase): is_replace_nick_name: bool session_key: str @@ -128,20 +104,17 @@ class NewProfileRequest(RequestBase): old_nick: str -@dataclass class RegisterCDKeyRequest(RequestBase): session_key: str cdkey_enc: str -@dataclass class RegisterNickRequest(RequestBase): unique_nick: str session_key: str partner_id: int -@dataclass class UpdateProfileRequest(RequestBase): has_public_mask_flag: bool = None public_mask: Union[PublicMasks, int] = None @@ -169,7 +142,6 @@ class UpdateProfileRequest(RequestBase): country_code: str = None -@dataclass class UpdateUiRequest(RequestBase): cpubrandid: str = None cpuspeed: str = None diff --git a/src/backends/gamespy/protocols/presence_search_player/__init__.py b/src/backends/gamespy/protocols/presence_search_player/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/routers/gamespy/gstats.py b/src/backends/routers/gamespy/gstats.py index e69de29bb..81873390f 100644 --- a/src/backends/routers/gamespy/gstats.py +++ b/src/backends/routers/gamespy/gstats.py @@ -0,0 +1,24 @@ +from typing import Annotated +import uvicorn + +from fastapi import Body, FastAPI +from pydantic import BaseModel, Field + +app = FastAPI() + + +class Item(BaseModel): + name: str + description: str | None = None + price: float + tax: float | None = None + tags: list = [] + +@app.put("/items/{item_id}") +async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]): + results = {"item_id": item_id, "item": item} + return results + + +if __name__ == "__main__": + uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/src/backends/routers/gamespy/presence_connection_manager.py b/src/backends/routers/gamespy/presence_connection_manager.py index 37e2570bf..5ed5b70cf 100644 --- a/src/backends/routers/gamespy/presence_connection_manager.py +++ b/src/backends/routers/gamespy/presence_connection_manager.py @@ -1,17 +1,12 @@ -from flask import Flask, request -from backends.gamespy.servers.presence_search_player.requests import ( - LoginRequest, -) +from fastapi import FastAPI + +from backends.gamespy.protocols.presence_search_player.requests import LoginRequest from backends.urls import * -app = Flask(__name__) +app = FastAPI() @app.route(f"/{PRESENCE_CONNECTION_MANAGER}/login", methods=["POST"]) -def pcm_login(): - req = LoginRequest(request.json) - pass - - - +def pcm_login(request: LoginRequest): + pass diff --git a/src/requirement.txt b/src/requirement.txt index 67be29ee4..7577c2a25 100644 --- a/src/requirement.txt +++ b/src/requirement.txt @@ -9,4 +9,5 @@ attrs requests mongoengine == 0.28.2 flask_socketio -socketio \ No newline at end of file +socketio +fastapi \ No newline at end of file From 284592de6c502c3ef7d3896193029deea02e4cde Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 26 Jun 2024 03:22:36 +0000 Subject: [PATCH 070/231] update code --- .../modules/direct2game/handlers/general.py | 39 +++++++++++ .../modules/sake/abstractions/contracts.py | 41 ++++++++++++ .../modules/sake/contracts/requests.py | 64 +++++++++++++++++++ .../modules/sake/exceptions/general.py | 5 ++ 4 files changed, 149 insertions(+) create mode 100644 src/servers/webservices/modules/direct2game/handlers/general.py create mode 100644 src/servers/webservices/modules/sake/abstractions/contracts.py create mode 100644 src/servers/webservices/modules/sake/contracts/requests.py create mode 100644 src/servers/webservices/modules/sake/exceptions/general.py diff --git a/src/servers/webservices/modules/direct2game/handlers/general.py b/src/servers/webservices/modules/direct2game/handlers/general.py new file mode 100644 index 000000000..0b480eda9 --- /dev/null +++ b/src/servers/webservices/modules/direct2game/handlers/general.py @@ -0,0 +1,39 @@ +from servers.webservices.abstractions.contracts import RequestBase +from servers.webservices.abstractions.handler import CmdHandlerBase +from servers.webservices.applications.client import Client +from servers.webservices.modules.direct2game.contracts.requests import ( + GetPurchaseHistoryRequest, + GetStoreAvailabilityRequest, +) +from servers.webservices.modules.direct2game.contracts.responses import ( + GetPurchaseHistoryResponse, + GetStoreAvailabilityResponse, +) +from servers.webservices.modules.direct2game.contracts.results import ( + GetPurchaseHistoryResult, + GetStoreAvailabilityResult, +) + + +class GetPurchaseHistoryHandler(CmdHandlerBase): + _request: GetPurchaseHistoryRequest + _result: GetPurchaseHistoryResult + + def __init__(self, client: Client, request: GetPurchaseHistoryRequest) -> None: + assert isinstance(request, GetPurchaseHistoryRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = GetPurchaseHistoryResponse(self._request, self._result) + + +class GetStoreAvailabilityHandler(CmdHandlerBase): + _request: GetStoreAvailabilityRequest + _result: GetStoreAvailabilityResult + + def __init__(self, client: Client, request: GetStoreAvailabilityRequest) -> None: + assert isinstance(request, GetStoreAvailabilityRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = GetStoreAvailabilityResponse(self._request, self._result) diff --git a/src/servers/webservices/modules/sake/abstractions/contracts.py b/src/servers/webservices/modules/sake/abstractions/contracts.py new file mode 100644 index 000000000..53c956b16 --- /dev/null +++ b/src/servers/webservices/modules/sake/abstractions/contracts.py @@ -0,0 +1,41 @@ +import servers.webservices.abstractions.contracts as lib +from servers.webservices.aggregations.soap_envelop import SoapEnvelop +from servers.webservices.modules.sake.exceptions.general import SakeException + +NAMESPACE = "http://gamespy.net/sake" + + +class RequestBase(lib.RequestBase): + game_id: int + secret_key: str + login_ticket: str + table_id: str + + def parse(self) -> None: + super().parse() + game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") + if game_id is None: + raise SakeException("gameid is missing from the request.") + self.game_id = int(game_id) + + self.secret_key = self._content_element.find(f".//{{{NAMESPACE}}}secretKey") + if self.secret_key is None: + raise SakeException("secretkey id is missing from the request.") + + self.login_ticket = self._content_element.find(f".//{{{NAMESPACE}}}loginTicket") + if self.login_ticket is None: + raise SakeException("loginTicket is missing from the request.") + + self.table_id = self._content_element.find(f".//{{{NAMESPACE}}}tableid") + if self.table_id is None: + raise SakeException("tableid is missing from the request.") + + +class ResultBase(lib.ResultBase): + pass + + +class ResponseBase(lib.ResponseBase): + def __init__(self, request: RequestBase, result: ResultBase) -> None: + self._content = SoapEnvelop(NAMESPACE) + super().__init__(request, result) \ No newline at end of file diff --git a/src/servers/webservices/modules/sake/contracts/requests.py b/src/servers/webservices/modules/sake/contracts/requests.py new file mode 100644 index 000000000..9aba13882 --- /dev/null +++ b/src/servers/webservices/modules/sake/contracts/requests.py @@ -0,0 +1,64 @@ +from servers.webservices.modules.sake.abstractions.contracts import ( + RequestBase, + NAMESPACE, +) +import xmltodict + +from servers.webservices.modules.sake.exceptions.general import SakeException + + +class CreateRecordRequest(RequestBase): + values: dict + + def parse(self) -> None: + super().parse() + value_node = self._content_element.find(f".//{{{NAMESPACE}}}values") + if value_node is None: + raise SakeException("values is missing from request") + + self.values = xmltodict.parse(value_node) + + +class DeleteRecordRequest(RequestBase): + record_id: int + + def parse(self) -> None: + super().parse() + record_id = self._content_element.find(f".//{{{NAMESPACE}}}recordid") + + if record_id is None: + raise SakeException("recordid is missing from request") + + self.record_id = int(record_id) + + +class GetMyRecordRequest(RequestBase): + fields: dict + + def parse(self) -> None: + super().parse() + fields_node = self._content_element.find(f".//{{{NAMESPACE}}}fields") + if fields_node is None: + raise SakeException("fields is missing from request") + self.fields = xmltodict.parse(fields_node) + + +class GetRandomRecordsRequest(RequestBase): + max: str + fields: dict + + def parse(self) -> None: + super().parse() + max = self._content_element.find(f".//{{{NAMESPACE}}}max") + if max is None: + raise SakeException("max is missing from request") + self.max = int(max) + + fields_node = self._content_element.find(f".//{{{NAMESPACE}}}fields") + if fields_node is None: + raise SakeException("fields is missing from request") + self.fields = xmltodict.parse(fields_node) + + +class GetRecordLimitRequest(RequestBase): + pass \ No newline at end of file diff --git a/src/servers/webservices/modules/sake/exceptions/general.py b/src/servers/webservices/modules/sake/exceptions/general.py new file mode 100644 index 000000000..f64aab3da --- /dev/null +++ b/src/servers/webservices/modules/sake/exceptions/general.py @@ -0,0 +1,5 @@ +from servers.webservices.exceptions.general import WebExceptions + + +class SakeException(WebExceptions): + pass From 4afbc3c444451cb6617c5614831fc5067e2d3485 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 28 Jun 2024 13:06:15 +0000 Subject: [PATCH 071/231] refactor: file arrangement --- .gitignore | 2 +- common/UniSpy_pg.sql | 5521 +++++++++++++++++ config.json => common/config.json | 0 src/.devcontainer/Dockerfile | 12 - src/.devcontainer/devcontainer.json | 11 - src/backends/gamespy/protocols/natneg/data.py | 2 +- .../contracts/requests.py | 12 - src/backends/routers/gamespy/natneg.py | 1 + .../gamespy/presence_connection_manager.py | 4 +- .../routers/gamespy/presence_search_player.py | 1 + src/backends/routers/gamespy/query_report.py | 1 + .../routers/gamespy/server_browser.py | 1 + src/backends/routers/gamespy/webservices.py | 1 + src/docker-compose.yaml | 4 +- src/{requirement.txt => requirements.txt} | 0 .../game_traffic_relay/applications/router.py | 10 +- .../game_traffic_relay/contracts/general.py | 17 + 17 files changed, 5555 insertions(+), 45 deletions(-) create mode 100644 common/UniSpy_pg.sql rename config.json => common/config.json (100%) delete mode 100644 src/.devcontainer/Dockerfile delete mode 100644 src/.devcontainer/devcontainer.json rename src/{requirement.txt => requirements.txt} (100%) create mode 100644 src/servers/game_traffic_relay/contracts/general.py diff --git a/.gitignore b/.gitignore index 95ee5530c..e390fdba9 100644 --- a/.gitignore +++ b/.gitignore @@ -164,4 +164,4 @@ cython_debug/ *.pyc *.txt -!requirement.txt \ No newline at end of file +!requirements.txt \ No newline at end of file diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql new file mode 100644 index 000000000..3a8add655 --- /dev/null +++ b/common/UniSpy_pg.sql @@ -0,0 +1,5521 @@ +-- +-- PostgreSQL database dump +-- + +-- Dumped from database version 14.2 (Debian 14.2-1.pgdg110+1) +-- Dumped by pg_dump version 14.1 + +-- Started on 2022-03-02 20:15:24 CET + +SET statement_timeout = 0; +SET lock_timeout = 0; +SET idle_in_transaction_session_timeout = 0; +SET client_encoding = 'UTF8'; +SET standard_conforming_strings = on; +SELECT pg_catalog.set_config('search_path', '', false); +SET check_function_bodies = false; +SET xmloption = content; +SET client_min_messages = warning; +SET row_security = off; + +-- +-- TOC entry 5 (class 2615 OID 16385) +-- Name: unispy; Type: SCHEMA; Schema: -; Owner: - +-- + +CREATE SCHEMA unispy; + + +-- +-- TOC entry 3466 (class 0 OID 0) +-- Dependencies: 5 +-- Name: SCHEMA unispy; Type: COMMENT; Schema: -; Owner: - +-- + +COMMENT ON SCHEMA unispy IS 'standard public schema'; + + +SET default_tablespace = ''; + +SET default_table_access_method = heap; + +-- +-- TOC entry 209 (class 1259 OID 16386) +-- Name: addrequests; Type: TABLE; Schema: unispy; Owner: - +-- + +CREATE TABLE unispy.addrequests ( + addrequestid integer NOT NULL, + profileid integer NOT NULL, + namespaceid integer NOT NULL, + targetid integer NOT NULL, + reason character varying NOT NULL, + syncrequested character varying NOT NULL +); + + +-- +-- TOC entry 3467 (class 0 OID 0) +-- Dependencies: 209 +-- Name: TABLE addrequests; Type: COMMENT; Schema: unispy; Owner: - +-- + +COMMENT ON TABLE unispy.addrequests IS 'Friend request.'; + + +-- +-- TOC entry 210 (class 1259 OID 16391) +-- Name: addrequests_addrequestid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- + +CREATE SEQUENCE unispy.addrequests_addrequestid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- TOC entry 3468 (class 0 OID 0) +-- Dependencies: 210 +-- Name: addrequests_addrequestid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- + +ALTER SEQUENCE unispy.addrequests_addrequestid_seq OWNED BY unispy.addrequests.addrequestid; + + +-- +-- TOC entry 211 (class 1259 OID 16392) +-- Name: blocked; Type: TABLE; Schema: unispy; Owner: - +-- + +CREATE TABLE unispy.blocked ( + blockid integer NOT NULL, + profileid integer NOT NULL, + namespaceid integer NOT NULL, + targetid integer NOT NULL +); + + +-- +-- TOC entry 3469 (class 0 OID 0) +-- Dependencies: 211 +-- Name: TABLE blocked; Type: COMMENT; Schema: unispy; Owner: - +-- + +COMMENT ON TABLE unispy.blocked IS 'Block list.'; + + +-- +-- TOC entry 212 (class 1259 OID 16395) +-- Name: blocked_blockid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- + +CREATE SEQUENCE unispy.blocked_blockid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- TOC entry 3470 (class 0 OID 0) +-- Dependencies: 212 +-- Name: blocked_blockid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- + +ALTER SEQUENCE unispy.blocked_blockid_seq OWNED BY unispy.blocked.blockid; + + +-- +-- TOC entry 213 (class 1259 OID 16396) +-- Name: friends; Type: TABLE; Schema: unispy; Owner: - +-- + +CREATE TABLE unispy.friends ( + friendid integer NOT NULL, + profileid integer NOT NULL, + namespaceid integer NOT NULL, + targetid integer NOT NULL +); + + +-- +-- TOC entry 3471 (class 0 OID 0) +-- Dependencies: 213 +-- Name: TABLE friends; Type: COMMENT; Schema: unispy; Owner: - +-- + +COMMENT ON TABLE unispy.friends IS 'Friend list.'; + + +-- +-- TOC entry 214 (class 1259 OID 16399) +-- Name: friends_friendid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- + +CREATE SEQUENCE unispy.friends_friendid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- TOC entry 3472 (class 0 OID 0) +-- Dependencies: 214 +-- Name: friends_friendid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- + +ALTER SEQUENCE unispy.friends_friendid_seq OWNED BY unispy.friends.friendid; + + +-- +-- TOC entry 215 (class 1259 OID 16400) +-- Name: games; Type: TABLE; Schema: unispy; Owner: - +-- + +CREATE TABLE unispy.games ( + gameid integer NOT NULL, + gamename character varying NOT NULL, + secretkey character varying, + description character varying(4095) NOT NULL, + disabled boolean NOT NULL +); + + +-- +-- TOC entry 3473 (class 0 OID 0) +-- Dependencies: 215 +-- Name: TABLE games; Type: COMMENT; Schema: unispy; Owner: - +-- + +COMMENT ON TABLE unispy.games IS 'Game list.'; + + +-- +-- TOC entry 216 (class 1259 OID 16405) +-- Name: grouplist; Type: TABLE; Schema: unispy; Owner: - +-- + +CREATE TABLE unispy.grouplist ( + groupid integer NOT NULL, + gameid integer NOT NULL, + roomname text NOT NULL +); + + +-- +-- TOC entry 3474 (class 0 OID 0) +-- Dependencies: 216 +-- Name: TABLE grouplist; Type: COMMENT; Schema: unispy; Owner: - +-- + +COMMENT ON TABLE unispy.grouplist IS 'Old games use grouplist to create their game rooms.'; + + +-- +-- TOC entry 217 (class 1259 OID 16410) +-- Name: messages; Type: TABLE; Schema: unispy; Owner: - +-- + +CREATE TABLE unispy.messages ( + messageid integer NOT NULL, + namespaceid integer, + type integer, + "from" integer NOT NULL, + "to" integer NOT NULL, + date timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, + message character varying NOT NULL +); + + +-- +-- TOC entry 3475 (class 0 OID 0) +-- Dependencies: 217 +-- Name: TABLE messages; Type: COMMENT; Schema: unispy; Owner: - +-- + +COMMENT ON TABLE unispy.messages IS 'Friend messages.'; + + +-- +-- TOC entry 218 (class 1259 OID 16416) +-- Name: messages_messageid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- + +CREATE SEQUENCE unispy.messages_messageid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- TOC entry 3476 (class 0 OID 0) +-- Dependencies: 218 +-- Name: messages_messageid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- + +ALTER SEQUENCE unispy.messages_messageid_seq OWNED BY unispy.messages.messageid; + + +-- +-- TOC entry 219 (class 1259 OID 16417) +-- Name: partner; Type: TABLE; Schema: unispy; Owner: - +-- + +CREATE TABLE unispy.partner ( + partnerid integer NOT NULL, + partnername character varying NOT NULL +); + + +-- +-- TOC entry 3477 (class 0 OID 0) +-- Dependencies: 219 +-- Name: TABLE partner; Type: COMMENT; Schema: unispy; Owner: - +-- + +COMMENT ON TABLE unispy.partner IS 'Partner information, these information are used for authentication and login.'; + + +-- +-- TOC entry 220 (class 1259 OID 16422) +-- Name: profiles; Type: TABLE; Schema: unispy; Owner: - +-- + +CREATE TABLE unispy.profiles ( + profileid integer NOT NULL, + userid integer NOT NULL, + nick character varying NOT NULL, + serverflag integer DEFAULT 0 NOT NULL, + status smallint DEFAULT 0, + statstring character varying DEFAULT 'I love UniSpy'::character varying, + location character varying, + firstname character varying, + lastname character varying, + publicmask integer DEFAULT 0, + latitude double precision DEFAULT 0, + longitude double precision DEFAULT 0, + aim character varying DEFAULT ''::character varying, + picture integer DEFAULT 0, + occupationid integer DEFAULT 0, + incomeid integer DEFAULT 0, + industryid integer DEFAULT 0, + marriedid integer DEFAULT 0, + childcount integer DEFAULT 0, + interests1 integer DEFAULT 0, + ownership1 integer DEFAULT 0, + connectiontype integer DEFAULT 0, + sex smallint DEFAULT 0, + zipcode character varying DEFAULT '00000'::character varying, + countrycode character varying DEFAULT 1, + homepage character varying DEFAULT 'unispy.org'::character varying, + birthday integer DEFAULT 0, + birthmonth integer DEFAULT 0, + birthyear integer DEFAULT 0, + icquin integer DEFAULT 0, + quietflags smallint DEFAULT 0 NOT NULL, + streetaddr text, + streeaddr text, + city text, + cpubrandid integer DEFAULT 0, + cpuspeed integer DEFAULT 0, + memory smallint DEFAULT 0, + videocard1string text, + videocard1ram smallint DEFAULT 0, + videocard2string text, + videocard2ram smallint DEFAULT 0, + subscription integer DEFAULT 0, + adminrights integer DEFAULT 0 +); + + +-- +-- TOC entry 3478 (class 0 OID 0) +-- Dependencies: 220 +-- Name: TABLE profiles; Type: COMMENT; Schema: unispy; Owner: - +-- + +COMMENT ON TABLE unispy.profiles IS 'User profiles.'; + + +-- +-- TOC entry 221 (class 1259 OID 16459) +-- Name: profiles_profileid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- + +CREATE SEQUENCE unispy.profiles_profileid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- TOC entry 3479 (class 0 OID 0) +-- Dependencies: 221 +-- Name: profiles_profileid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- + +ALTER SEQUENCE unispy.profiles_profileid_seq OWNED BY unispy.profiles.profileid; + + +-- +-- TOC entry 222 (class 1259 OID 16460) +-- Name: pstorage; Type: TABLE; Schema: unispy; Owner: - +-- + +CREATE TABLE unispy.pstorage ( + pstorageid integer NOT NULL, + profileid integer NOT NULL, + ptype integer NOT NULL, + dindex integer NOT NULL, + data jsonb +); + + +-- +-- TOC entry 3480 (class 0 OID 0) +-- Dependencies: 222 +-- Name: TABLE pstorage; Type: COMMENT; Schema: unispy; Owner: - +-- + +COMMENT ON TABLE unispy.pstorage IS 'Old games use pstorage to store game data.'; + + +-- +-- TOC entry 223 (class 1259 OID 16465) +-- Name: pstorage_pstorageid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- + +CREATE SEQUENCE unispy.pstorage_pstorageid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- TOC entry 3481 (class 0 OID 0) +-- Dependencies: 223 +-- Name: pstorage_pstorageid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- + +ALTER SEQUENCE unispy.pstorage_pstorageid_seq OWNED BY unispy.pstorage.pstorageid; + + +-- +-- TOC entry 224 (class 1259 OID 16466) +-- Name: sakestorage; Type: TABLE; Schema: unispy; Owner: - +-- + +CREATE TABLE unispy.sakestorage ( + sakestorageid integer NOT NULL, + tableid character varying NOT NULL +); + + +-- +-- TOC entry 3482 (class 0 OID 0) +-- Dependencies: 224 +-- Name: TABLE sakestorage; Type: COMMENT; Schema: unispy; Owner: - +-- + +COMMENT ON TABLE unispy.sakestorage IS 'Sake storage system.'; + + +-- +-- TOC entry 225 (class 1259 OID 16471) +-- Name: sakestorage_sakestorageid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- + +CREATE SEQUENCE unispy.sakestorage_sakestorageid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- TOC entry 3483 (class 0 OID 0) +-- Dependencies: 225 +-- Name: sakestorage_sakestorageid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- + +ALTER SEQUENCE unispy.sakestorage_sakestorageid_seq OWNED BY unispy.sakestorage.sakestorageid; + + +-- +-- TOC entry 226 (class 1259 OID 16472) +-- Name: subprofiles; Type: TABLE; Schema: unispy; Owner: - +-- + +CREATE TABLE unispy.subprofiles ( + subprofileid integer NOT NULL, + profileid integer NOT NULL, + uniquenick character varying, + namespaceid integer DEFAULT 0 NOT NULL, + partnerid integer DEFAULT 0 NOT NULL, + productid integer, + gamename text, + cdkeyenc character varying, + firewall smallint DEFAULT 0, + port integer DEFAULT 0, + authtoken character varying +); + + +-- +-- TOC entry 3484 (class 0 OID 0) +-- Dependencies: 226 +-- Name: TABLE subprofiles; Type: COMMENT; Schema: unispy; Owner: - +-- + +COMMENT ON TABLE unispy.subprofiles IS 'User subprofiles.'; + + +-- +-- TOC entry 227 (class 1259 OID 16481) +-- Name: subprofiles_subprofileid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- + +CREATE SEQUENCE unispy.subprofiles_subprofileid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- TOC entry 3485 (class 0 OID 0) +-- Dependencies: 227 +-- Name: subprofiles_subprofileid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- + +ALTER SEQUENCE unispy.subprofiles_subprofileid_seq OWNED BY unispy.subprofiles.subprofileid; + + +-- +-- TOC entry 228 (class 1259 OID 16482) +-- Name: users; Type: TABLE; Schema: unispy; Owner: - +-- + +CREATE TABLE unispy.users ( + userid integer NOT NULL, + email character varying NOT NULL, + password character varying NOT NULL, + emailverified boolean DEFAULT true NOT NULL, + lastip inet, + lastonline timestamp without time zone DEFAULT CURRENT_TIMESTAMP, + createddate timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, + banned boolean DEFAULT false NOT NULL, + deleted boolean DEFAULT false NOT NULL +); + + +-- +-- TOC entry 3486 (class 0 OID 0) +-- Dependencies: 228 +-- Name: TABLE users; Type: COMMENT; Schema: unispy; Owner: - +-- + +COMMENT ON TABLE unispy.users IS 'User account information.'; + + +-- +-- TOC entry 229 (class 1259 OID 16492) +-- Name: users_userid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- + +CREATE SEQUENCE unispy.users_userid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- TOC entry 3487 (class 0 OID 0) +-- Dependencies: 229 +-- Name: users_userid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- + +ALTER SEQUENCE unispy.users_userid_seq OWNED BY unispy.users.userid; + + +-- +-- TOC entry 3219 (class 2604 OID 16493) +-- Name: addrequests addrequestid; Type: DEFAULT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.addrequests ALTER COLUMN addrequestid SET DEFAULT nextval('unispy.addrequests_addrequestid_seq'::regclass); + + +-- +-- TOC entry 3220 (class 2604 OID 16494) +-- Name: blocked blockid; Type: DEFAULT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.blocked ALTER COLUMN blockid SET DEFAULT nextval('unispy.blocked_blockid_seq'::regclass); + + +-- +-- TOC entry 3221 (class 2604 OID 16495) +-- Name: friends friendid; Type: DEFAULT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.friends ALTER COLUMN friendid SET DEFAULT nextval('unispy.friends_friendid_seq'::regclass); + + +-- +-- TOC entry 3223 (class 2604 OID 16496) +-- Name: messages messageid; Type: DEFAULT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.messages ALTER COLUMN messageid SET DEFAULT nextval('unispy.messages_messageid_seq'::regclass); + + +-- +-- TOC entry 3256 (class 2604 OID 16497) +-- Name: profiles profileid; Type: DEFAULT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.profiles ALTER COLUMN profileid SET DEFAULT nextval('unispy.profiles_profileid_seq'::regclass); + + +-- +-- TOC entry 3257 (class 2604 OID 16498) +-- Name: pstorage pstorageid; Type: DEFAULT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.pstorage ALTER COLUMN pstorageid SET DEFAULT nextval('unispy.pstorage_pstorageid_seq'::regclass); + + +-- +-- TOC entry 3258 (class 2604 OID 16499) +-- Name: sakestorage sakestorageid; Type: DEFAULT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.sakestorage ALTER COLUMN sakestorageid SET DEFAULT nextval('unispy.sakestorage_sakestorageid_seq'::regclass); + + +-- +-- TOC entry 3263 (class 2604 OID 16500) +-- Name: subprofiles subprofileid; Type: DEFAULT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.subprofiles ALTER COLUMN subprofileid SET DEFAULT nextval('unispy.subprofiles_subprofileid_seq'::regclass); + + +-- +-- TOC entry 3269 (class 2604 OID 16501) +-- Name: users userid; Type: DEFAULT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.users ALTER COLUMN userid SET DEFAULT nextval('unispy.users_userid_seq'::regclass); + + +-- +-- TOC entry 3440 (class 0 OID 16386) +-- Dependencies: 209 +-- Data for Name: addrequests; Type: TABLE DATA; Schema: unispy; Owner: - +-- + +COPY unispy.addrequests (addrequestid, profileid, namespaceid, targetid, reason, syncrequested) FROM stdin; +\. + + +-- +-- TOC entry 3442 (class 0 OID 16392) +-- Dependencies: 211 +-- Data for Name: blocked; Type: TABLE DATA; Schema: unispy; Owner: - +-- + +COPY unispy.blocked (blockid, profileid, namespaceid, targetid) FROM stdin; +\. + + +-- +-- TOC entry 3444 (class 0 OID 16396) +-- Dependencies: 213 +-- Data for Name: friends; Type: TABLE DATA; Schema: unispy; Owner: - +-- + +COPY unispy.friends (friendid, profileid, namespaceid, targetid) FROM stdin; +\. + + +-- +-- TOC entry 3446 (class 0 OID 16400) +-- Dependencies: 215 +-- Data for Name: games; Type: TABLE DATA; Schema: unispy; Owner: - +-- + +COPY unispy.games (gameid, gamename, secretkey, description, disabled) FROM stdin; +1 gmtest HA6zkS Test / demo / temporary f +2 bgate 2ozFrM Baldur's Gate f +3 blood2 jUOF0p Blood II f +5 daikatana fl8aY7 John Romero's Daikatana f +6 descent3 feWh2G Descent 3 f +7 dh3 gbnYTp Deer Hunter 3 f +9 dv O1Vodm Dark Vengeance f +10 expertpool cRu7vE Expert Pool f +11 forsaken znoJ6k Forsaken f +12 gamespy2 d4kZca GameSpy 3D f +13 gspylite mgNUaC GameSpy Lite f +14 gspyweb 08NHv5 GameSpy Web f +15 halflife ZIr1wX Half Life f +17 hexenworld 6SeXQB Hexenworld f +18 kingpin QFWxY2 Kingpin: Life of Crime f +19 mplayer 3xYjaU MPlayer f +21 quake2 rtW0xg Quake II f +22 quake3 paYVJ7 Quake 3: Arena f +23 quakeworld FU6Vqn QuakeWorld f +24 rally xdNbQZ Rally Masters f +25 redline 2J5aV2 Redline f +27 sin Ij1uAB SiN f +28 slavezero Xrv9zn Slave Zero f +29 sof nJ0rZz Soldier of Fortune f +31 specops adyYWv Spec Ops f +32 tribes z83fc2 Starsiege TRIBES f +33 turok2 RWd3BG Turok 2 f +34 unreal DAncRK Unreal f +35 ut Z5Nfb0 Unreal Tournament f +36 viper SSzOWL Viper f +40 wot RSSSpA Wheel of Time f +41 giants z8erKA Giants: Citizen Kabuto f +42 dtracing ipC912 Dirt Track Racing f +43 terminus z9uima Terminus f +45 ra2 9z3312 Rocket Arena 2 f +46 aoe2 iZhjKi Age of Empires II f +47 roguespear kqeEcz Rainbow Six: Rogue Spear f +49 scrabble Pz3Vea Scrabble v2.0 f +50 boggle geaDfe Boggle f +51 werewolf 81zQQa Werewolf: The Apocalypse f +52 treadmarks u27bAA Tread Marks f +54 rock HnVZ1u Rock f +55 midmad 8gEaZv Midtown Madness f +56 aoe VzkADe Age of Empires f +57 revolt fa5lhE Re-Volt f +58 gslive Xn221z GameSpy Arcade f +61 wildwings PbNDFL Wild Wings f +62 rmth3 EvBDAc Rocky Mountain Trophy Hunter 3 f +64 metalcrush3 KvE2Pk Metal Crush 3 f +65 ta vPqkAc Total Annihilation f +70 mcmad aW7c9n Motocross Madness f +71 heroes3 5Un7pR Heroes of Might and Magic III f +72 jk 9nUz45 Star Wars Jedi Knight: Dark Forces II f +73 links98 J8yc5z Links LS 1998 f +84 xwingtie Lc8gW5 Star Wars: X-Wing vs. TIE Fighter f +99 buckmaster 4NcAZg Buckmaster Deer Hunting f +100 cneagle HNvEAc Codename: Eagle f +104 alphacent qbb4Ge Sid Meier's Alpha Centauri f +108 sanity 7AeTyu Sanity f +113 starraiders n76Cde Star Raiders f +114 kiss 9tFALe KISS: Psycho Circus f +121 risk nx6I2v Risk C.1997 f +122 cribbage TKuE2P Hasbro's Cribbage f +125 ginrummy 9rIEUi Hasbro's Gin Rummy f +126 hearts 9SKI3t Hasbro's Hearts f +128 spades YvPBIM Hasbro's Spades f +129 racko U8QYlf Hasbro's Rack-O f +130 rook Bc1Zmb Hasbro's Rook f +131 checkers 2hfuJA Hasbro's Checkers f +133 chess g11Aig Hasbro's Chess f +135 tzar byTPgq Tzar: The Burden of the Crown f +136 parcheesi PHCviG Hasbro's Parcheesi f +138 backgammon VCypJm Hasbro's Backgammon f +139 freepark alVRIq Hasbro's Free Parking f +140 connect4 2Pnx6I Hasbro's Connect 4 f +141 millebourn kD072v Hasbro's Mille Bournes f +142 msgolf99 alVRIq Microsoft Golf '99 f +144 close4bb alVRIq Close Combat IV: Battle of the Bulge f +145 aliencross IOrDfP Alien Crossfire f +146 outlaws TKuE2P Outlaws f +147 civ2gold alVRIq Civilization II Gold f +148 getmede 3MHCZ8 Get Medieval f +150 monopoly alVRIq Monopoly 2000 f +151 rb6 49qmcl Tom Clancy's Rainbow Six f +153 rebellion TKuE2P Star Wars Rebellion f +154 ccombat3 TKuE2P Close Combat III: The Russian Front f +156 jkmosith1 kD072v Star Wars Jedi Knight: Mysteries of the Sith f +157 smgettysbu 3MHCZ8 Sid Meier's Gettysburg f +158 srally2dmo kD072v Sega Rally 2 (PC Demo) f +159 fltsim2k TKuE2P Flight Simulator 2000 f +161 duke4 8n2Hge Duke Nukem Forever f +162 aowfull alVRIq Age Of Wonders f +163 darkstone 3MHCZ8 Darkstone f +164 abominatio qik37G Abomination f +165 bc3k 5LnQaz Battle Cruiser 3000 AD f +166 outlawsdem k37G3M Outlaw (Multiplay Demo) f +167 allegiance YghTwJ MS Allegiance f +169 aoe2demo alVRIq Age of Empires II (Demo) f +170 mcmaddemo 3MHCZ8 Motocross Madness (Demo) f +171 midmaddemo 3MHCZ8 Midtown Madness (Demo) f +172 mtmdemo 6I2vIO Monster Truck Madness 2 (Demo) f +173 axisallies JwWh9S Axis & Allies f +175 worms2 IOrDfP Worms 2 f +176 mtruckm2 TKuE2P Monster Truck Madness 2 f +177 powerslide nx6I2v Powerslide f +178 kissdc 6EwAbh Kiss (Dreamcast) f +179 legendsmm 5Kbawl Legends of Might and Magic f +180 mech4 uNbXef Mechwarrior 4: Vengeance f +182 majesty qik37G Majesty: The Fantasy Kingdom Sim f +307 cossacks p2vPkJ Cossacks Anthology f +183 fblackjack NeVbEa Fiendish Blackjack f +184 slancerdc UbNea2 Starlancer (Dreamcast) f +186 dogsofwar Mbe3if Dogs of War f +187 starlancer qik37G Starlancer f +188 laserarena JbEb3a Laser Arena (2015) f +189 mmadness2 MZIq1w Motocross Madness 2 f +190 obiwon UnEhYr Obi-Wan f +191 ra3 JnEfae Rocket Arena 3 f +195 sanitydemo 7AeTyu Sanity: Aiken's Artifact (Demo) f +196 sanitybeta 7AeTyu Sanity: Aiken's Artifact (Public Beta) f +198 stitandemo 9mDKzi Submarine Titans (Demo) f +199 stbotf aiiOU0 Birth of the Federation f +200 machines xS6aii Machines f +202 amworldwar nLfZDY Army Men: World War f +203 gettysburg PwZFex Sid Meier's Gettysburg! f +204 hhbball2000 zfsDV2 High Heat Baseball 2000 f +205 dogalo MZIr0w MechWarrior 3 f +206 armymen2 YBLJvU Army Men II f +207 armymenspc r1wXEX Army Men Toys in Space f +209 risk2 xboeRW Risk II f +210 starwrsfrc p4jGh6 Star Wars: Force Commander f +211 peoplesgen el1q7z Peoples General f +212 planecrazy p5jGh6 Plane Crazy f +213 linksext Fdk2q7 Links Extreme f +214 flyinghero 9mELiP Flying Heroes f +216 links2000 MIr1wW Links LS 2000 f +217 ritesofwar 2p7zgs Warhammer: Rites of War f +218 gulfwarham YDXBOF Gulf War: Operatin Desert f +219 uprising2 ALJwUh Uprising 2 f +220 earth2150 1wXEX3 Earth 2150 f +221 evolva DV24p2 Evolva f +223 7kingdoms WEX3rA Seven Kingdoms 2 f +224 migalley wUhCSC Mig Alley f +225 axallirnb GexS6a Axis & Allies: Iron Blitz f +227 mcommgold xS6aji MechCommander Gold f +228 santietam zfsCV2 Sid Meier's Antietam! f +230 panzergen2 DLiPwZ Panzer General f +231 lazgo2demo MIq0wW Lazgo 2 Demo f +232 taking p5kGg7 Total Annihilation: Kingdoms f +233 mfatigue nfRW88 Metal Fatigue f +235 starsiege MZIq1w Starsiege f +236 jkmots 6ajiPU Star Wars Jedi Knight: Mysteries of the Sith f +237 zdoom MIr0wW ZDoom f +238 warlordsb 9gV5Hm Warlords Battlecry f +240 anno1602ad sAJtHo Anno 1602 A.D. f +241 dh4 BeaPe2 Deer Hunter 4 f +242 group 72Ha31 Group Room f +243 blademasters B3Avke Legend of the Blademasters f +244 iwdale LfZDYB Icewind Dale f +245 dogsrunamock p2vPkJ dogsrunamock (?) f +246 excessive Gn3aY9 Excessive Q3 f +248 mcm2demo ajhOU0 Motocross Madness 2 Demo f +249 dtrsc p2vPkJ Dirt Track Racing: Sprint Cars f +250 chspades Yw7fc9 Championship Spades f +251 chhearts Yw7fc9 Championship Hearts f +252 stef1 H28D2r Star Trek: Voyager – Elite Force f +254 nolf Jn3Ab4 No One Lives Forever f +255 dtr h7nLfZ Dirt Track Racing f +256 sacrifice sCV34o Sacrifice f +257 rune V5Hm41 Rune f +258 aoe2tc p4jAGg Age of Empires II: The Conquerors f +259 stitans V5Hl31 Submarine Titans f +260 bang zgsCV2 Bang! Gunship Elite f +262 fakk2 YDYBNE F.A.K.K. 2 f +263 bcm tHg2t7 Battlecruiser: Millenium f +264 ds9dominion BkAg3a DS9: Dominion Wars f +265 bots JKb462 Bots (Lith) f +266 tacore 1ydybn Core Contingency f +267 mech3pm TORp4k Pirates Moon f +268 diplomacy 2p7zgs Diplomacy f +270 fargate nhwchs Far Gate f +271 nexttetris KVWE12 The Next Tetris f +272 fforce ys3k4d Freedom Force f +273 iwar2 Bk3a13 Independance War 2 f +274 gp500 cvSTOR GP500 f +275 midmad2 7nLfZD Midtown Madness 2 f +277 unreal2 Yel30y Unreal 2 f +278 4x4evo tFbq8m 4x4 Evolution f +279 crimson YBLJwU Crimson Skies f +280 harleywof bofRW8 Wheels of Freedom f +282 ageofsail2 Kb3ab5 Age of Sail 2 f +283 cskies Rp5kAG Crimson Skies f +284 rscovertops yK2A0x Rainbow Six: Covert Ops f +285 pba2001 Kbay43 PBA Bowling 2001 f +286 cskiesdemo p2uPkK Crimson Skies Demo f +287 mech4st tFcq8m MechWarrior 4: Vengeance f +289 sinmac 3Ka5BN SiN (Mac) f +290 wosinmac yX02mQ SiN: Wages of Sin (Mac) f +291 utdc KbAgl4 Unreal Tournament (Dreamcast) f +292 kohan Kbao3a Kohan: Immortal Sovereigns f +293 mcmania BAbas9 Motocross Mania f +295 furfiighters JwUhCT Fur Fighters (?) f +296 furfighters JwUhCT Fur Fighters f +297 owar xS6aii Original War f +298 cfs2 uPkKya Combat Flight Simulator 2 f +299 uno MZIq0w UNO f +302 gore NYZEAK Gore f +303 gangsters2 NEek2p Gansters II: Vendetta f +304 insanedmo 3rAJtI Insane Demo f +306 atlantis W49nx4 Atlantis f +308 ihraracing Zbmu4a IHRA Drag Racing f +309 atlantispre W49nx4 Atlantis Prequel f +310 4x4retail MIq0wX 4x4 Evolution f +311 rnconsole Jba3n1 Real Networks Console f +312 dukes dvRTOR Dukes Of Hazzard: Racing f +313 serioussam AKbna4 Serious Sam f +234 cfs \N Microsoft Combat Flight Simulator f +337 armada2 N3a2mZ Star Trek Armada 2 f +316 rfts jiPV0u Reach For The Stars f +317 cheuchre Yw7fc9 Championship Euchre f +318 links2001 8cvSTO Links 2001 f +320 4x4evodemo p4jAGg 4x4 Evolution Demo f +321 mcmaniadmo TCQMIr Motocross Mania Demo f +322 gamevoice Agm3a1 MS Game Voice f +323 cstrike ZIr1wX Counter-Strike f +324 venomworld Jg43a1 Venom World f +325 omfbattle Abm93d One Must Fall Battlegrounds f +326 furdemo 3rAJtH Fur Fighters Demo f +328 nwn ZIq1wW Neverwinter Nights f +329 strifeshadow 99mEKi Strifeshadow f +330 ssamdemo Gn3a12 Serious Sam Demo f +331 kacademy blGjuN Klingon Academy f +332 goredemo uW0xp1 Gore Demo f +334 midmad2dmo sAJtHo Midtown Madness 2 Demo f +335 gunman W78dvR Gunman Chronicles f +336 stronghold QwZFex Stronghold f +338 links2001dmo xZGexS Links 2001 Demo f +339 q3tafull Ah3mb4 Team Arena Retail f +341 battlerealms hU7wE3 Battle Realms f +342 sfc OV0tKn Starfleet Command f +343 strfltcmd2 8cvSTO Starfleet Command Volume f +344 stnw 8cvRTO Star Trek: New Worlds f +345 strfltcmd2d gW5Hm4 Empires at War Demo f +346 sfcdemo MZIr1w Starfleet Command Demo f +348 waterloo CTCQMZ Waterloo f +349 falloutbosd JwUhCT Fallout Tactics f +350 kohandemo Kbao3a Kohan Demo f +351 exploeman 8dv Explöman f +352 segarally2 ajiPV0 Sega Rally 2 f +354 streetjam jAGh7n Ultra Wheels Street Jam f +355 explomaen fZDYBN Explomän f +357 mrwtour W78cvR Motoracer World Tour f +358 wordzap q7zfsD WordZap f +359 iwdalehow h3U3Kz Icewind Dale: Heart of Winter f +360 magmay2 QW88dv Magic & Mayhem 2 f +361 chat01 xQ7fp2 Chat Group 1 f +362 chat02 xQ7fp2 Chat Group 2 f +363 chat03 xQ7fp2 Chat Group 3 f +364 Chat04 xQ7fp2 Chat Group 4 f +365 Chat05 xQ7fp2 Chat Group 5 f +366 Chat06 xQ7fp2 Chat Group 6 f +367 Chat07 xQ7fp2 Chat Group 7 f +368 Chat08 xQ7fp2 Chat Group 8 f +370 Chat10 xQ7fp2 Chat Group 10 f +371 Chat11 xQ7fp2 Chat Group 11 f +372 Chat12 xQ7fp2 Chat Group 12 f +373 Chat13 xQ7fp2 Chat Group 13 f +375 Chat15 xQ7fp2 Chat Group 15 f +376 Chat16 xQ7fp2 Chat Group 16 f +377 Chat17 xQ7fp2 Chat Group 17 f +378 Chat18 xQ7fp2 Chat Group 18 f +379 Chat19 xQ7fp2 Chat Group 19 f +381 empireearth ybneQW Empire Earth f +382 chasspart5 p4kGg7 ChessPartner 5 f +383 bg2bhaal 532HaZ Baldur's Gate II: Throne of Bhaal f +385 cultures Ir1wXE Cultures f +386 fatedragon sCV34o Fate of the Dragon f +387 sbubpop u2K9p5 Super Bubble Pop f +388 xcomenforcer M3A2rK X-Com: Enforcer f +389 aow2 csFcq8 Age of Wonders 2 f +390 startopia r5UN9g Startopia f +391 jefftest f6Ylm1 Test for Jeffs Games f +392 hhbball2002 YBNEdl High Heat Baseball 2002 f +394 por2 9agW5H Pool of Radiance 2 f +395 falloutbos fQW78d Fallout Tactics f +397 fatedragond 6UN9ag Fate of the Dragon Demo f +398 demonstar ziPwZF Demonstar f +399 tf15 V0tKnY Half-Life 1.5 f +400 gspoker PbZ35N GameSpy Poker f +401 gsspades PbZ35N GameSpy Spades f +402 gshearts PbZ35N GameSpy Hearts f +404 gscheckers PbZ35N GameSpy Checkers f +405 atlantica LfZDXB Atlantica f +406 merchant2 zdybne Merchant Prince II f +407 magmay2d ORp4kG The Art of War f +408 assimilation BOFdk1 Assimilation f +409 zax J3An3s Zax f +410 leadfoot uNctFb Leadfoot f +412 chat DagNzk Chat Service f +413 disciples hPV0uK Disciples f +414 opflash h28Doi Operation Flashpoint f +415 zsteel 4p2uPk Z: Steel Soldiers f +417 gschess BQMZIq GameSpy Chess f +418 gsreversi ItHo0r GameSpy Reversi f +419 gsyarn m31ydy GameSpy Y.A.R.N. f +420 tribes2 DAM4Kv Tribes 2 f +421 avp2 Df3M6Z Aliens vs Predator 2 f +422 bodarkness Jn33pM Blade of Darkness f +423 dominion 1zdybo Dominion f +425 opflashd DV24o2 Operation Flashpoint Demo f +426 blade Eel1q7 Blade f +427 mechcomm Ir0wXE MechCommander f +428 globalops AdN3L9 Global Operations f +429 links99 iQxZFe Links LS 1999 f +430 rulesotg iQxZGe Rules of the Game f +432 armymen V5Hm41 Army Men f +433 _news BADBAD News f +434 railsamd GjuMct Rails Across America Demo f +435 railsam sFcq99 Rails Across America f +436 kohanexp Kbao3a Kohan Expansion f +438 wz2100demo AGg7mM Warzone 2100 (Demo) f +439 sfc2opdv Gj2k7A Starfleet Command II: Orion Pirates (Dynaverse II) f +440 roguespeard S6ajhP Rogue Spear Demo f +441 redalert QwZGex Red Alert f +442 wormsarm p2uPkK Worms Armageddon f +443 takingdoms kJyalH TA: Kingdoms f +444 arc M9tZe4 Arc: Sierra f +353 explomän \N Explomän f +447 dh5 Ji2R2v Deer Hunter 5 f +448 diablo2 hPU0tK Diablo 2 f +449 starcraft LfYDYB Starcraft f +450 starcraftdmo NFel1q Starcraft Demo f +452 warcraft2bne gW5Hl4 Warcraft 2 f +453 redalert2 ajhOV0 Red Alert 2 f +454 projecteden TORp5k Project Eden f +455 roadwars ORp5kG Road Wars f +456 tiberiansun Gg6nLf Tiberian Sun f +457 chessworlds 5Hm41y Chess Worlds f +459 sfc2op EX3rAJ Starfleet Command: Orion f +460 warlordsdr exS6aj Warlords III: Dark Rising f +462 cmanager S6aiiO Cycling Manager f +463 laserarenad TBQMIr Laser Arena Demo f +464 starcraftexp DKiPxZ Starcraft: Brood Wars f +465 tsfirestorm fRW88c Tiberian Sun - Firestorm f +466 monopolyty YDXBNE Monopoly Tycoon f +467 thps3ps2 hD72Kq Tony Hawk Pro Skater 3 (PS2) f +468 emperorbfd X3rAIt Emperor: Battle For Dune f +469 claw ziPwZG Claw f +470 armymenrts Rp4jGh Army Men RTS f +472 conquestfw ORp4kG Conquest: Frontier Wars f +473 realwar 78dvRT Real War f +474 axis nYBLJv Axis f +475 anno1503 9mDKiP Anno 1503 f +476 incomingforces MIr0wW Incoming Forces f +477 ironstrategy ZDYBNF Iron Strategy f +478 heavygear2 hCTBQM Heavy Gear 2 f +480 motoracer3 rAItHo Motoracer 3 f +481 rogerwilco rW17Ko Roger Wilco f +482 conquestfwd ZIr0wW Conquest: Frontier Wars D f +483 thps3media tRKg39 Tony Hawk Pro Skater 3 Media f +484 echelon uPkKya Echelon f +485 takeda 6TN9gW Takeda f +486 cnoutbreak Jg43a1 Codename: Outbreak f +487 oldscrabble Pz4Veb Scrabble 1.0 f +489 rdpoker GjuMct Reel Deal Poker f +490 f1teamdriver QwZFex Williams F1 Team: Team Dr f +491 aquanox U3pf8x Aquanox f +492 mohaa M5Fdwc Medal of Honor Allied Assault f +493 harley3 KdF35z Harel 3 f +494 cnoutbreakd Jg43a1 Codename: Outbreak Demo f +495 ras 0r5UN9 Red Ace Squadron f +496 opfor YDYBNE Opposing Force f +498 austerlitz hCSBQM Austerlitz: Napoleons Gre f +499 dmania Dn3H2v DMania f +500 bgatetales GjuMcs Baldur's Gate: Tales of the Sword Coast f +501 cueballworldd uPkKyb Cueball World Demo f +502 st_rank 53Jx7W Global Rankings Sample f +503 rallytrophy CSCQMI Rally Trophy f +505 actval1 j9Ew2L Activision Value Title 1 f +506 etherlords 6ajiOV Etherlords f +507 swine SwK4J2 S.W.I.N.E. f +508 warriorkings hCSCQM Warrior Kings f +509 myth3 W7LHE8 Myth 3 f +517 commandos2 V0tKnY Commandos 2 f +518 mcomm2 7JsQ0t MechCommander 2 f +520 praetorians m31zdx Praetorians f +521 iwd2 Q3yu7R Icewind Dale 2 f +522 gamebot G4mBo7 GameBot Test f +523 armada2beta N3a2mZ Star Trek: Armada 2 Beta f +524 rtcwtest 78dvST Wolfenstein MP Test f +526 nvchess YDXBOF nvChess f +527 msecurity j9Ew2L Alcatraz: Prison Escape f +528 avp2demo Df3M6Z Aliens vs Predator 2 Demo f +530 chaser Pe4W2B Chaser f +531 nascar5 j3Do2f NASCAR 5 f +532 kohanag dl1p7z Kohan: Ahrimans Gift f +533 tribes2demo AdF313 Tribes 2 Demo f +534 serioussamse AKbna4 Serious Sam: Second Encounter f +535 st_ladder KwFJ2X Global Rankings Sample - Ladder f +536 swgb XEX3sA Star Wars: Galactic Battlegrounds f +537 bumperwars Rp5kAG Bumper Wars! f +538 combat p5kGh7 Combat f +540 rsblackthornd BLJvUh Black Thorn Demo f +541 bfield1942 HpWx9z Battlefield 1942 f +543 swinedemo SwK4J2 Swine Demo f +544 freedomforce lHjuMc Freedom Force f +545 il2sturmovik ajiPU0 IL-2 Sturmovik f +548 myth3demo rAItIo Myth 3 Demo f +550 ghostrecon p5jAGh Tom Clancy's Ghost Recon f +551 ghostrecond KyblGj Tom Clancy's Ghost Recon Demo f +552 fltsim2002 uKnYBL Microsoft Flight Simulator 2002 f +553 mech4bkexp csFbq9 MechWarrior Black Knight f +554 hd MIq1wX Hidden & Dangerous Enhanc f +555 strifeshadowd 99mEKi Strifeshadow Demo f +556 conflictzone g7nLfZ Conflict Zone f +558 druidking p5kGg7 Druid King f +559 itycoon2 JeW3oV Industry Tycoon 2 f +560 sof2 F8vZed Soldier of Fortune 2 f +561 armada2d N3a2mZ Star Trek: Armada II Demo f +563 rtcw Gj3aV2 Return to Castle Wolfenstein f +564 xboxtunnel 8dvSTO Xbox Tunnel Service f +565 survivor H2du2 Survivor Ultimate f +566 il2sturmovikd zfsDV2 IL-2 Sturmovik Demo f +567 haegemonia LiQwZF Haegemonia f +568 mohaad M5Fdwc Medal of Honor: Allied Assault Demo f +570 janesf18 hPV0uK Janes F/A-18 f +571 janesusaf 6aiiPU Janes USAF f +572 janesfa OFek1p Janes Fighters Anthology f +573 janesf15 XEX3rA Janes F-15 f +574 janesww2 wUhCTB Janes WWII Fighters f +575 mech4bwexpd Fel1q7 MechWarrior Black Knight f +576 f12002 DXBOFd F1 2002 f +577 ccrenegade tY1S8q Command & Conquer: Renegade f +542 battlerealmsbBAD \N Battle Realms Beta f +580 demoderby Hl31yd Demolition Derby & Figure f +581 janesattack dvSTOR Janes Attack Squadron f +582 chesk W5Hl41 Chesk f +583 hhball2003 cvSTOR High Heat Baseball 2003 f +584 duelfield 8mELiP Duelfield f +585 carnivores3 yd7J2o Carnivores 3 f +587 thps3pc KsE3a2 Tony Hawk 3 (PC) f +588 blockade 3sAJtI Operation Blockade f +589 mafia dxboeR Mafia f +590 ccrenegadedemo LsEwS3 Command & Conquer: Renegade Demo f +591 shadowforce A3p54Q Shadow Force: Razor Unit f +595 gta3pc Hu3P1h Grand Theft Auto 3 (PC) f +596 vietkong bq98mE Vietkong f +598 subcommand iPwZGe Sub Command f +599 originalwar CV34p2 Original War f +600 thps4ps2 H2r8W1 Tony Hawk: Pro Skater 4 (PS2) f +601 warlordsb2d tKnYBL Warlords Battlecry II Demo f +602 ioftheenemy uPkKya I of the Enemy f +603 sharpshooter 9gV5Hl Sharp Shooter f +605 globalopspb CHANGE Global Operations Public Beta f +606 pb4 s82J1p Extreme Paintbrawl 4 f +610 armygame g3sR2b Americas Army: Special Forces f +611 homm4 6ajhPU Heroes of Might and Magic f +612 darkplanet uPkJyb Dark Planet f +614 teamfactor RW78cv Team Factor f +615 dragonthrone p5jAGh Dragon Throne f +616 celebdm h5D7j8 Celebrity Deathmatch f +617 phoenix GknAbg Phoenix (Stainless Steel) f +618 matrixproxy m6NwA2 Matrix Proxy f +620 ghostreconds EX3rAI Ghost Recon: Desert Siege f +621 sof2demo F8vZed Soldier of Fortune 2 Demo f +622 etherlordsbeta 6ajiOV Etherlords Patch Beta f +623 aow2d S6aiiP Age of Wonders 2 Demo f +624 privateer Yh3o2d Privateers Bounty: Age of Sail 2 f +625 gcracing LziPwZ Great Clips Racing f +627 dungeonsiege H3t8Uw Dungeon Siege f +628 silenthunter2 bnfRW8 Silent Hunter 2 f +629 celtickings MIq0wW Druid King f +630 globalopsd u3Pa87 Global Ops Demo f +631 renegadebf Rt7W9x Renegade Battlefield f +633 tacticalops uMctFb Tactical Ops f +634 ut2 Lw7x0R Unreal Tournament 2003 f +635 swgbcc RTORp4 Star Wars Galactic Battle f +636 ut2d y5e8Q3 Unreal Tournament 2003 Demo f +637 voiceapp sD3GkC VoiceApp Voice SDK Test f +639 streetracer ydxboe Streetracer f +640 opflashr Y3k7x1 Operation Flashpoint: Resistance f +641 mohaas 2vPkJy Medal of Honor: Allied Assault Spearhead f +642 avp2ph P4fR9w Aliens vs. Predator 2: Primal Hunt f +643 nthunder2003 Ld5C9w NASCAR Thunder 2003 f +645 dtr2 MIq1wW Dirt Track Racing II f +646 GameSpy.com xbofQW GameSpy.com f +702 gicombat1 \N G.I. Combat f +649 darkheaven G3i4Xk Dark Heaven f +650 twc iPxZFe Takeout Weight Curling f +651 steeltide zgsDV2 Operation Steel Tide f +652 realwarrs 8cvSTO Real War: Rogue States f +654 rmth2003 Y4kC7S Trophy Hunter 2003 f +655 strongholdc fYDXBO Stronghold: Crusader f +656 soa H3pD7m Soldiers of Anarchy f +657 jbnightfire S9j3L2 James Bond: Nightfire f +658 sumofallfears 4kGh6m The Sum of All Fears f +659 nfs6 ZIr1wX Need For Speed: Hot Pursuit 2 f +660 bangler2003 hCTCQM Bass Angler 2003 f +661 netathlon2 RW88dv NetAthlon f +663 ddozenpt L3sB7f Deadly Dozen: Pacific Theater f +664 vietnamso E8d3Bo Line of Sight: Vietnam f +665 mt2003 TORp4j Monopoly 2003 f +666 soad K3e8cT Soldiers of Anarchy Demo f +668 ironstorm y5Ei7C Iron Storm f +669 civ3ptw yboeRW Civilization III: Play the World f +670 tron20 t9D3vB TRON 2.0 f +671 bfield1942d gF4i8U Battlefield 1942 Demo f +672 scrabble3 4o2vPk Scrabble 3 f +673 vietcong bq98mE Vietcong f +675 ccgenerals h5T2f6 Command & Conquer: Generals f +676 sfc3dv Gi7C8s Starfleet Command III (Dynaverse) f +677 bandits H2k9bD Bandits: Phoenix Rising f +678 xar N9gV5H Xtreme Air Racing f +679 echelonww uPkKya Echelon Wind Warriors f +684 mclub2ps2 h4Rx9d Midnight Club 2 (PS2) f +689 dh2003 hT40y1 Deerhunter 2003 f +690 hwbasharena CSBQMI Hot Wheels Bash Arena f +691 robotarena2 h4Yc9D Robot Arena 2 f +692 monopoly3 vPkKya Monopoly 3 f +694 painkiller k7F4d2 Painkiller f +695 revolution G1h3m2 Revolution f +696 ddozenptd G7b3Si Deadly Dozen Pacific Theater Demo f +697 ironstormd h9D3Li Iron Storm Demo f +698 strikefighters1 PwZFex Strike Fighters: Project f +699 moo3 g4J72d Master of Orion III f +700 suddenstrike2 Iq0wWE Sudden Strike II f +703 projectigi2 j4F9cY IGI 2: Covert Strike Demo f +704 realwarrsd 5jAGh7 Real War: Rogue States Demo f +705 pnomads FexS6a Project Nomads f +707 strongholdcd kAGh6n Stronghold: Crusader Demo f +708 blitzkrieg fYDXBN Blitzkrieg f +709 woosc Y4nD9a World of Outlaws Sprint Cars f +710 vietcongd bq98mE Vietcong Demo f +711 hlwarriors H5rW9v Highland Warriors f +712 mohaasd 2vPkJy Medal of Honor: Allied As f +714 horserace y4fR91 HorseRace f +647 fileplanet \N FilePlanet.com f +722 worms3 fZDYBO Worms 3D f +717 sandbags wXEX3r Sandbags and Bunkers f +718 crttestdead 111111 CRT - TEST f +719 nolf2 g3Fo6x No One Lives Forever 2 f +720 wkingsb agV5Hm Warrior Kings Battles f +721 riseofnations H3kC6s Rise of Nations f +723 castles 31zdyb Castles and Catapluts f +725 orbb Ykd2D3 O.R.B: Off-World Resource Base Beta f +726 echelonwwd ORp4jG Echelon Wind Warriors Demo f +728 snooker2003 ZIq1wX Snooker 2003 f +729 jeopardyps2 t9iK4V Jeopardy (PS2) f +730 riskps2 Hg3u2X Risk (PS2) f +731 wofps2 dF39h3 Wheel of Fortune (PS2) f +733 trivialppc c45S8i Trivial Pursuit (PC) US f +734 trivialpps2 h3U6d9 Trivial Pursuit (PS2) f +735 projectigi2d j4F9cY IGI 2: Covert Strike Demo f +736 projectigi2r j4F9cY IGI 2 Covert Strike f +739 wooscd Y4nD9a World of Outlaws Sprint Cars Demo f +740 nthunder2004 g3J7sp NASCAR Thunder 2004 f +741 f1comp g7W1P8 F1 1999-2000 Compilation f +742 nomansland DLziQw No Mans Land f +743 nwnxp1 ZIq1wW Neverwinter Nights: Shado f +744 praetoriansd EX3rAJ Praetorians Demo f +745 nrs2003 f3RdU7 NASCAR Racing Season 2003 f +746 gmtestam HA6zkS test (Auto-Matchmaking) f +748 devastation b6Eo3S Devastation f +749 blitz2004ps2 w3Rk7F NFL Blitz 2004 (PS2) f +750 hd2 sK8pQ9 Hidden and Dangerous 2 f +751 hd2b T1sU7v Hidden and Dangerous 2 Beta f +754 hd2d sT3p2k Hidden and Dangerous 2 Demo f +755 mrpantsqm g3R2ii Mr. Pants QuickMatch f +756 moutlawne 4o2uPk Midnight Outlaw Nitro f +758 lionheart h5R3cp Lionheart f +759 medievalvi w5R39i Medieval Total War Viking Invasion f +760 black9pc h2F9cv Black9 (PC) f +761 black9ps2 w3D8gY Black9 (PS2) f +762 cmanager3 T3d8yH Cycling Manager 3 f +764 devastationd y3Fk8c Devastation Demo f +765 hitz2004ps2 t3E8Fc NHL Hitz 2004 PS2 f +767 chaserd 3R5fi7 Chaser Demo f +768 motogp2 y3R2j7 MotoGP 2 f +769 motogp2d y3R5d1 MotoGP 2 Demo f +770 racedriverd P4f3Hw Race Driver Demo f +771 empiresam GknAbg Empires Dawn of the Modern World (AM) f +772 empires GknAbg Empires: Dawn of the Modern World f +773 crashnitro 3E8fT5 Crash Nitro Carts f +774 breed t3Fw7r Breed f +775 breedd u7Gc92 Breed Demo f +777 moo3a g4J72d Master of Orion III f +778 nwnmac Adv39k Neverwinter Nights (Mac) f +779 ravenshield csFbq9 Raven Shield f +781 spacepod 8cvRTO SpacePod f +782 agrome 8mEKiP Against Rome f +783 bfield1942sw HpWx9z Battlefield 1942: Secret Weapons of WW2 f +784 thps4pc L3C8s9 Tony Hawk: Pro Skater 4 (PC) f +785 omfbattled Abm93d One Must Fall Battlergounds Demo f +786 nwnlinux Adv39k Neverwinter Nights (Linux) f +787 blitz2004ps2e t3Fg7C NFL Blitz Pro 2004 E3 (PS2) f +789 homeworld2b t3Fd7j Homeworld 2 Beta f +790 halo QW88cv Halo Beta f +791 lotr3 y2Sc6h Lords of the Realm III f +792 lotr3b y2Sc6h Lords of the Realm III Beta f +793 halor e4Rd9J Halo: Combat Evolved f +794 bllrs2004ps2 t3w6k8 NBA Ballers (PS2) f +795 rtcwett t3R7dF Wolfenstein: Enemy Territory Test f +797 jacknick6 q7zgsC Jack Nicklaus Golden Bear f +798 wotr e8Fc3n War of the Ring f +799 terminator3 y3Fq8v Terminator 3 f +800 fwarriorpc n2X8ft Fire Warrior f +801 fwarriorps2 r3D7s9 Fire Warrior (PS2) f +803 aow3 W88dvR Age of Wonders: Shadow Magic (aow3) f +804 E3_2003 jvaLXV E3_2003 f +805 aowsm W78cvR Age of Wonders: Shadow Magic (aowsm) f +806 specialforces V4f02S Special Forces f +807 spartan GjuMct Spartan & Spartan f +808 dod Hm31yd Day of Defeat f +809 tron20d t9D3vB TRON 2.0 Demo f +811 bfield1942swd r3Yjs8 Battlefield 1942: Secret Weapons of WW2 Demo f +813 rtcwet jpvbuP Wolfenstein: Enemy Territory f +814 mphearts vStJNr mphearts f +815 hotrod Tg4so9 Hot Rod, American Street Drag f +816 civ3con h4D8Wc Civilization III: Conquests f +817 civ3conb g3e9J1 Civilization III: Conquests Beta f +818 riseofnationsam H3kC6s Rise of Nations Auto-Matching f +819 afrikakorps tbhWCq Afrika Korps f +820 apocalypticadi T3d8x7 Apocalyptica f +821 robotech2 w3D2Yb Robotech 2 (PS2) f +823 ccgenzh D6s9k3 Command & Conquer: Generals – Zero Hour f +824 ronb H3kC6s Rise of Nations Beta f +825 ronbam H3kC6s Rise of Nations Beta (Automatch) f +826 commandos3 uukfJz Commandos 3 f +828 dh2004 E8j4fP Deer Hunter 2004 f +830 dh2004d E8j4fP Deer Hunter 2004 Demo f +831 armygamemac g3sR2b Americas Army: Special Forces (Mac) f +832 bridgebaron14 hd3Y2o Bridge Baron f +834 anno1503b mEcJMZ Anno 1503 Beta f +835 contractjack h3K8f2 Contract Jack f +836 postal2 yw3R9c Postal 2 f +837 ut2004 y5rP9m Unreal Tournament 2004 f +838 ut2004d y5rP9m Unreal Tournament 2004 Demo f +839 contractjackd U3k2f8 Contract Jack Demo f +1104 regimentps2 u6qPE9 The Regiment PS2 f +948 eearth2 h3C2jU Empire Earth 2 f +976 wormsforts y3Gc8n Worms Forts: Under Siege f +842 mtgbgrounds y3Fs8K Magic The Gathering: Battlegrounds f +843 groundcontrol2 L3f2X8 Ground Control 2 f +844 bfield1942ps2 HpWx9z Battlefield Modern Combat (PS2) f +845 dsiege2 tE42u7 Dungeon Siege 2 The Azunite Prophecies f +846 judgedredddi t3D7Bz Judge Dredd f +847 coldwinter W9f5Cb1 Cold Winter f +848 haegemoniaxp LiQwZF Hegemonia Expansion f +850 castlestrike GPcglz Castle Strike f +851 homeworld2d t38kc9 Homeworld 2 Demo f +852 callofduty K3dV7n Call of Duty f +853 mohaabd y32FDc Medal of Honor: Allied Assault Breakthrough Demo f +854 twc2 PYxfvt Takeout Weight Curling 2 f +855 nthunder2004d g3J7sp NASCAR Thunder 2004 Demo f +857 mta Y4f9Jb Multi Theft Auto f +860 spellforce T8g3Ck Spellforce f +861 halomac e4Rd9J Halo (Mac) f +862 contractjackpr U3k2f8 Contract Jack PR f +864 wotrb e8Fc3n War of the Ring Beta f +865 halod yG3d9w Halo Demo f +866 wcpool2004ps2 g3J7w2 World Championship Pool 2004 (PS2) f +867 fairstrike y4Ks2n Fair Strike f +868 aarts tR3b8h Axis and Allies RTS f +870 lotrbme h3D7Lc Lord of the Rings: The Battle For Middle-Earth f +871 mototrax T2g9dX Moto Trax f +872 painkillerd k7F4d2 Painkiller Demo f +873 painkillert k7F4d2 Painkiller Multiplayer Test f +874 entente LqrTlG The Entente f +876 sforces V4f02S Special Forces f +877 slugfestps2 e8Cs3L Slugfest Pro (PS2) f +879 battlemages ZMWyOO Battle Mages f +880 bfvietnam h2P9dJ Battlefield: Vietnam f +881 planetside yQzrrQ PlanetSide f +882 daoc TkAksf Dark Age of Camelot f +883 uotd CpJvsG Ultima Online Third Dawn f +884 swg wICOeH Star Wars Galaxies f +885 eq AnoMKT Everquest f +887 serioussamps2 yG3L9f Serious Sam (PS2) f +888 omfbattlecp Abm93d One Must Fall Battlegrounds (GMX) f +889 fairstriked y4Ks2n Fair Strike Demo f +890 celtickingspu WxaKUc Nemesis of the Roman Empire f +891 test Hku6Fd Test f +892 truecrime G8d3R5 True Crime f +896 links2004 jG3d9Y Links 2004 f +897 terminator3d y3Fq8v Terminator 3 Demo f +900 wcpool2004pc ypQJss World Championship Pool 2004 f +901 postal2d yw3R9c Postal 2 Demo f +903 spellforced T8g3Ck Spellforce Demo f +904 le_projectx t3F9vY Legend Entertainment Project X f +905 racedriver2 UEzIlg Race Driver 2 f +906 bomberfunt bbeBZG BomberFUN Tournament f +907 pbfqm g3R2ii PlanetBattlefield QuickMatch f +908 gangland y6F39x Gangland f +910 juicedpc g3J8df Juiced (PC) f +911 juicedps2 g3J8df Juiced (PS2) f +913 tribesv y3D28k Tribes Vengeance f +914 racedriver2ps2 n5oS9f Race Driver 2 (PS2) f +916 indycarps2 L4H7f9 Indycar Series (PS2) f +917 thps6ps2 3Rc9Km Tony Hawks Underground 2 (PS2) f +918 sniperelps2 f3Tk3s Sniper Elite (PS2) f +920 bllrs2004ps2d t3w6k8 NBA Ballers Demo (PS2) f +921 saturdayns psZhzd Saturday Night Speedway f +922 rometw s8L3v0 Rome: Total War f +924 rontp H3kC6s Rise of Nations: Throne and Patriots f +925 rontpam H3kC6s Rise of Nations: Throne and Patriots (Automatch) f +926 dmhand YJxLbV Dead Man Hand f +927 upwords itaKPh upwords f +929 scrabbledel mZfoBF Scrabble Deluxe f +930 dsiege2am tE42u7 Dungeon Siege 2 The Azunite Prophecies (Automatch) f +931 cmr4pc t3F9f1 Colin McRae Rally 4 (PC) f +932 kumawar y3G9dE Kuma War f +933 cmr4pcd t3F9f1 Colin McRae Rally 4 Demo (PC) f +940 crashnburnps2 gj7F3p Crash N Burn (PS2) f +941 spartand JdQvnt Spartan Demo f +942 ace L2dC9x A.C.E. f +944 perimeter FRYbdA Perimeter f +945 ilrosso Y3f9Jn Il Rosso e Il Nero - The Italian Civil War f +946 whammer40000 uJ8d3N Warhammer 40,000: Dawn of War f +947 swat4 tG3j8c SWAT 4 f +949 tribesvd y3D28k Tribes Vengeance Demo f +950 tribesvb y3D28k Tribes Vengeance Beta f +952 sniperelpc hP58dm Sniper Elite (PC) f +954 altitude DZzvoR Altitude f +955 fsx y3Fd8H Flight Simulator 2006 f +956 hotwheels2pc u3Fx9h Hot Wheels 2 (PC) f +958 hotwheels2pcd u3Fx9h Hot Wheels 2 Demo (PC) f +959 cnpanzers h3Tod8 Codename Panzers f +960 gamepopulator h3Ks61 Game Populator f +961 gamepopulatoram h3Ks61 Game Populator (Automatch) f +963 livewire wuyvAa GameSpy Livewire f +965 fear n3V8cj FEAR: First Encounter Assault Recon f +966 tron20mac t9D3vB TRON 2.0 (Mac) f +967 s_cstrikecz izVsOs Steam Counter-Strike: Condition Zero f +968 wingsofwar sWSqHB Wings of War f +969 mxun05ps2 u3Fs9n MX Unleashed 05 (PS2) f +971 swbfrontps2 y3Hd2d Star Wars: Battlefront (PS2, Japan) f +973 swbfrontpc y3Hd2d Star Wars: Battlefront (PC) f +974 perimeterd FRYbdA Perimeter Demo f +975 wracing1 t3Hs27 World Racing 1 f +977 mohaamac M5Fdwc Medal of Honor: Allied Assault (Mac) f +1222 motogp3d U3ld8j MotoGP 3 Demo f +1009 fswpc R5pZ29 Full Spectrum Warrior f +1038 conflictsopc vh398A Conflict: Special Ops (PC) f +1042 eearth2d h3C2jU Empire Earth 2 Demo f +1101 civ4 y3D9Hw Civilization IV f +1102 civ4am y3D9Hw Civilization IV (Automatch) f +979 mohaabmac y32FDc Medal of Honor: Breakthrough (Mac) f +980 bfield1942mac HpWx9z Battlefield 1942 (Mac) f +982 halom e4Rd9J Halo Multiplayer Expansion f +983 nitrofamily t3Jw2c Nitro Family f +984 besieger ydG3vz Besieger f +986 mkdeceptionps2 2s9Jc4 Mortal Kombat Deceptions (PS2) f +987 swrcommando y2s8Fh Star Wars: Republic Commando f +988 fightclubps2 t3d8cK Fight Club (PS2) f +989 area51ps2 eR48fP Area 51 (PS2) f +990 dday B78iLk D-Day f +992 mohaabdm y32FDc Medal of Honor: Allied Assault Breakthrough Demo (Mac) f +993 mkdeceppalps2 2s9Jc4 Mortal Kombat Deception PAL (PS2) f +994 civ4b y3D9Hw Civilization 4 Beta f +995 topspin sItvrS Top Spin f +996 bllrs2004pal t3w6k8 NBA Ballers PAL (PS2) f +999 scrabbleo t2Dfj8 Scrabble Online f +1000 wcsnkr2004ps2 K3f39a World Championship Snooker 2004 (PS2) f +1001 olg2PS2 Yb3pP2 Outlaw Golf 2 PS2 f +1002 gtasaps2 Bn73c9 Grand Theft Auto San Andreas (PS2) f +1003 thps6pc AdLWaZ T.H.U.G. 2 f +1004 smackdnps2 k7cL91 WWE Smackdown vs RAW Sony Beta (PS2) f +1005 thps5pc AdLWaZ Tony Hawks Underground (PC) f +1006 menofvalor h3Fs9c Men of Valor f +1008 gc2demo L3f2X8 Ground Control 2 Demo f +1010 soldiersww2 qdSxsJ Soldiers: Heroes of World War II f +1011 mtxmototrax VKQslt MTX MotoTrax f +1012 pbfqmv 9wk3Lo PlanetBattlefield QuickMatch Vietnam f +1013 wcsnkr2004pc DQZHBr World Championship Snooker 2004 (PC) f +1014 locomotion uTAGyB Chris Sawyer's Locomotion f +1015 gauntletps2 y2Fg39 Gauntlet (PS2) f +1016 gotcha 9s34Pz Gotcha! f +1019 knightsoh 9f5MaL Knights of Honor f +1020 wingsofward sWSqHB Wings of War Demo f +1021 cmr5ps2 hH3Ft8 Colin McRae Rally 5 (PS2) f +1022 callofdutyps2 tR32nC Call of Duty (PS2) f +1024 hotrod2 AaP95r Hot Rod 2: Garage to Glory f +1025 mclub3ps2 g7J2cX Midnight Club 3 DUB Edition (PS2) f +1027 trivialppalps2 h3U6d9 Trivial Pursuit PAL (PS2) f +1028 trivialppalpc c45S8i Trivial Pursuit PAL (PC) f +1029 hd2ss k3Ljf9 Hidden & Dangerous 2 - Sabre Squadron f +1030 whammer40kb uJ8d3N Warhammer 40,000: Dawn of War Beta f +1032 srsyndpc A9Lkq1 Street Racing Syndicate (PC) f +1033 ddayd B78iLk D-Day Demo f +1034 godzilla2ps2 bi9Wz4 Godzilla: Save the Earth (PS2) f +1035 actofwar LaR21n Act of War: Direct Action f +1037 statesmen j8K3l0 Statesmen f +1039 conflictsops2 vh398A Conflict: Special Ops (PS2) f +1040 dh2005 qW56m4 Deer Hunter 2005 f +1041 gotchad 9s34Pz Gotcha! Demo f +1043 smackdnps2pal k7cL91 WWE Smackdown vs RAW PAL (PS2) f +1044 wcpokerps2 t3Hd9q World Championship Poker (PS2) f +1045 cmr5pc hH3Ft8 Colin McRae Rally 5 (PC) f +1046 dh2005d qW56m4 Deer Hunter 2005 Demo f +1049 doom3 kbeafe Doom 3 f +1050 cmr5pcd hH3Ft8 Colin McRae Rally 5 Demo (PC) f +1051 spoilsofwar nZ2e4T Spoils of War f +1052 saadtest 1a2B3c SaadsTest f +1054 superpower2 yYw43B Super Power 2 f +1055 swat4d tG3j8c SWAT 4 Demo f +1056 exigob mPBHcI Armies of Exigo Beta f +1058 knightsohd 9f5MaL Knights of Honor Demo f +1059 battlefield2 hW6m9a Battlefield 2 f +1060 actofwaram LaR21n Act of War: Direct Action (Automatch) f +1061 bf1942swmac HpWx9z Battlefield 1942: Secret Weapons of WW2 Mac f +1062 closecomftf iLw37m Close Combat: First to Fight f +1064 kohankowd uE4gJ7 Kohan: Kings of War Demo f +1066 swempire t3K2dF Star Wars: Empire at War f +1067 stalkersc t9Fj3M STALKER: Shadows of Chernobyl f +1068 poolshark2ps2 teH26Z Pool Shark 2 (PS2) f +1069 poolshark2pc teH26Z Pool Shark 2 (PC) f +1070 smackdnps2kor k7cL91 WWE Smackdown vs RAW (PS2) Korean f +1071 smackdnps2r k7cL91 WWE Smackdown vs RAW (PS2) Retail f +1073 swbfrontps2p y3Hd2d Star Wars: Battlefront (PS2) f +1074 trivialppcuk c45S8i Trivial Pursuit (PC) UK f +1075 trivialppcfr c45S8i Trivial Pursuit (PC) French f +1076 trivialppcgr c45S8i Trivial Pursuit (PC) German f +1077 trivialppcit c45S8i Trivial Pursuit (PC) Italian f +1078 trivialppcsp c45S8i Trivial Pursuit (PC) Spanish f +1080 aartsd tR3b8h Axis and Allies RTS demo f +1081 blitzkriegrt fYDXBN Blitzkrieg: Rolling Thunder f +1082 dungeonlords 74dBl9 Dungeon Lords f +1083 SpyNote spynot Server Monitor f +1085 blitz2005ps2 uY39vA Blitz: The League 2005 f +1086 rof t5LqW4 Rise of Legends f +1087 rofam t5LqW4 Rise of Legends (Automatch) f +1088 nsr0405 Q6vu91 NASCAR Sim Racing (2005) f +1089 ffvsttr 5tQqw9 Freedom Force vs. The Third Reich f +1092 dshard g3D8Sc The Dragonshard Wars f +1094 exigor mPBHcI Armies of Exigo Retail f +1095 exigoram mPBHcI Armies of Exigo (Automatch) f +1096 bfield1942t HpWx9z Battlefield 1942 Testing f +1099 bfvietnamt h2P9dJ Battlefield: Vietnam Testing f +1103 regimentpc u6qPE9 The Regiment PC f +1036 juicedpalps2 \N Juiced PAL (PS2) f +1195 worms4 Bs28Kl Worms 4 Mayhem f +1200 fsw10hpc 6w2X9m Full Spectrum Warrior: Ten Hammers (PC) f +1206 worms4d Bs28Kl Worms 4 Mayhem Demo f +1224 eearth2xp1 h3C2jU Empire Earth II: The Art of Supremacy f +1106 battlefield2d hW6m9a Battlefield 2 Demo f +1108 fswps2 6w2X9m Full Spectrum Warrior PS2 f +1109 dshardam g3D8Sc The Dragonshard Wars (Automatch) f +1111 source AYcFzB Half Life 2 f +1112 s_cssource EEpacW Counter-Strike Source f +1113 feard n3V8cj FEAR: First Encounter Assault Recon Demo f +1114 s_hl2dm FqmlZJ s_hl2dm f +1115 bfield1942ps2b HpWx9z Battlefield Modern Combat (PS2) Beta f +1116 whammer40kt uJ8d3N Warhammer 40000: Dawn of War test f +1117 firecapbay VJMdlD Fire Captain: Bay Area Inferno f +1118 splintcellchaos UgzOGy splintcellchaos f +1120 fearcb n3V8cj FEAR: First Encounter Assault Recon (Closed Beta) f +1121 fearob n3V8cj FEAR: First Encounter Assault Recon (Open Beta) f +1122 ejammingpc Sd7a9p eJamming Jamming Station PC f +1123 ejammingmac Sd7a9p eJamming Jamming Station MAC (engine) f +1125 titanquest Te3j7S Titan Quest f +1126 wcsnkr2005ps2 cPw49v World Championship Snooker 2005 PS2 f +1127 wcsnkr2005 cPw49v World Championship Snooker 2005 (PC) f +1128 thps7ps2 y3L9Cw Tony Hawks American Wasteland (PS2) f +1129 pariahpc D3Kcm4 Pariah (PC) f +1130 impglory eCYHgP Imperial Glory f +1133 oltps2 cH92pQ Outlaw Tennis PS2 f +1134 wptps2 jL2aEz World Poker Tour PS2 f +1135 blkhwkdnps2 7wM8sZ Delta Force: Black Hawk Down (PS2) f +1137 motogp3 lelcPr MotoGP 3 f +1138 cmmwcpoker iRU92a Chris Moneymakers World Championship Poker f +1139 ddayxp1 B78iLk D-Day: 1944 Battle of the Bulge f +1140 spcell3coop QdVGhj Splinter Cell 3 CoOp f +1142 ffvsttrd 5tQqw9 Freedom Force vs. The Third Reich MP Demo f +1143 topspinps2 sItvrS Top Spin (PS2) f +1144 betonsoldier mH2y9u Bet on Soldier f +1146 topspinps2am sItvrS Top Spin (PS2) (Automatch) f +1147 vietcong2 zX2pq6 Vietcong 2 f +1148 spyvsspyps2 y3F7Gh Spy vs Spy (PS2) f +1149 nitrosample abcdef Nitro Sample f +1150 flatoutps2 ms83Ha Flat Out (PS2) f +1151 hotpacificps2 yB7qfv Heroes of the Pacific (PS2) f +1152 hotpacificpc yB7qfv Heroes of the Pacific (PC) f +1154 cnpanzers2 h3Tod8 Codename Panzers Phase 2 f +1155 stronghold2 Lc83Jm Stronghold 2 f +1157 actofwardam LaR21n Act of War: Direct Action Demo (Automatch) f +1158 xmenlegpc 47uQsy X-Men Legends (PC) f +1159 xmenlegps2 47uQsy X-Men Legends (PS2) f +1160 coteagles cEb84M War Front: Turning Point f +1161 area51pc mW73mq Area 51 (PC) f +1164 area51pcb mW73mq Area 51 (PC) Beta f +1169 stalinsubd y3Kc9s The Stalin Subway Demo f +1170 supruler2010 cEuCxb Supreme Ruler 2010 f +1171 pariahpcd D3Kcm4 Pariah Demo (PC) f +1172 serioussam2 8dA9mN Serious Sam 2 (PC) f +1173 riskingdoms K3x9vc Rising Kingdoms f +1176 stalinsub HOqpUo The Stalin Subway f +1177 bsmidwaypc qY84Ne Battlestations Midway (PC) f +1179 bsmidwaypcam qY84Ne Battlestations Midway (PC) (Automatch) f +1180 bsmidwayps2am qY84Ne Battlestations Midway PS2 (Automatch) f +1181 riskingdomsd K3x9vc Rising KIngdoms Demo f +1182 riskingdomsam K3x9vc Rising Kingdoms (Automatch) f +1183 wsoppc u3hK2C World Series of Poker (PC) f +1184 wsopps2 u3hK2C World Series of Poker (PS2) f +1185 velocityps2 Qmx73k Velocity PS2 f +1186 velocitypc Qmx73k Velocity PC f +1188 hotpaceudps2 yB7qfv Heroes of the Pacific EU Demo (PS2) f +1189 hotpacnadps2 yB7qfv Heroes of the Pacific NA Demo (PS2) f +1190 gbrome hEf6s9 Great Battles of Rome f +1191 rafcivatwar h98Sqa Rise And Fall: Civilizations at War f +1193 rafcivatwaram h98Sqa Rise And Fall: Civilizations at War (Automatch) f +1196 smackdn2ps2 JyWnL2 WWE Smackdown vs RAW 2 (PS2) f +1197 smackdn2ps2pal JyWnL2 WWE Smackdown vs RAW 2 PAL (PS2) f +1198 smackdn2ps2kor JyWnL2 WWE Smackdown vs RAW 2 Korea (PS2) f +1199 fsw10hps2 6w2X9m Full Spectrum Warrior: Ten Hammers (PS2) f +1201 fsw10hps2kor 6w2X9m Full Spectrum Warrior: Ten Hammers (Korea, PS2) f +1202 fsw10hps2pal 6w2X9m Full Spectrum Warrior: Ten Hammers (PAL, PS2) f +1203 swbfront2pc hMO2d4 Star Wars Battlefront 2 PC f +1204 swbfront2ps2 y3Hd2d Star Wars Battlefront 2 (PS2) f +1205 swbfront2ps2j hMO2d4 Star Wars Battlefront 2 (PS2) Japanese f +1207 whammer40kwa Ue9v3H Warhammer 40,000: Winter Assault f +1209 codbigredps2 ye4Fd8 Call of Duty 2: Big Red One (PS2) f +1210 dsnattest L74dSk ds nat test f +1212 xmenlegps2pal 47uQsy X-Men Legends PAL (PS2) f +1213 xmenlegps2pals 47uQsy X-Men Legends PAL Spanish (PS2) f +1215 gbromeam hEf6s9 Great Battles of Rome (Automatch) f +1216 pbfqm2 P7RTY8 PlanetBattlefield QuickMatch 2 f +1217 wsopps2am u3hK2C World Series of Poker (PS2) (Automatch) f +1218 wsoppcam u3hK2C World Series of Poker (PC) (Automatch) f +1223 vietcong2d zX2pq6 Vietcong 2 Demo f +1226 fordvchevyps2 i79DwE Ford Versus Chevy (PS2) f +1227 hotpacificpcd yB7qfv Heroes of the Pacific PC Demo f +1228 hoodzps2 f6eP9w Hoodz (PS2) f +1229 swbfront2pcb hMO2d4 Star Wars Battlefront 2 PC Beta f +1230 swbfront2pcd hMO2d4 Star Wars Battlefront 2 PC Demo f +1233 fswps2jp 6w2X9m Full Spectrum Warrior (PS2, Japanese) f +1234 and1sballps2 J3c8Dm AND1: Streetball Online (PS2) f +1238 mariokartds yeJ3x8 Mario Kart (DS) f +1239 genetrooperpc eK4Xh7 Gene Trooper (PC) f +1240 genetrooperps2 eK4Xh7 Gene Troopers (PS2) f +1241 legionarena Gd4v8j Legion Arena f +1242 kott2pc p3iWmL Knights of the Temple 2 (PC) f +1243 kott2ps2 p3iWmL Knights of the Temple 2 (PS2) f +1244 hardtruck PGWCwm Hard Truck Tycoon f +1245 wracing2 hY39X0 World Racing 2 (PC) f +1246 wsoppsp u3hK2C World Series of Poker (PSP) f +1248 infectedpsp eRq49L Infected (PSP) f +1249 infectedpspam eRq49L Infected (PSP) (Automatch) f +1251 unavailable j39DhU Test for disabled games f +1252 tempunavail 9h1UHk Test for temporarily disabled games f +1253 betonsoldierd mH2y9u Bet On Soldier f +1254 ghpballps2 9tcGVE Greg Hastings Paintball (PS2) f +1255 flatout SxdJel FlatOut f +1257 vietcong2pd zX2pq6 Vietcong 2 Public Demo f +1258 thawds t4Vc7x Tony Hawks American Wasteland (DS) f +1259 acrossingds h2P9x6 Animal Crossing (DS) f +1260 coteaglessp cEb84M War Front: Turning Point (Singleplayer) f +1261 and1sballps2am J3c8Dm AND1: Streetball Online (PS2) (Automatch) f +1262 mariokartdsam yeJ3x8 Mario Kart (DS, Automatch) f +1265 xmenleg2psp g3Hs9C X-Men: Legends 2 (PSP) f +1266 lotrbme2 g3Fd9z Lord of the Rings: The Battle for Middle-earth 2 (Beta) f +1267 shatteredunion t2Gx8g Shattered Union f +1268 serioussam2d 8dA9mN Serious Sam 2 Demo f +1269 bllrs2005ps2 4StbWm NBA Ballers 2005 (PS2) f +1274 racedriver3pcd \N Race Driver 3 Demo (PC) f +1272 mprimeds Dh1PpC Metroid Prime Hunters (DS) f +1273 racedriver3pc BPAfNv Race Driver 3 (PC) f +1278 uchaosrrps2am \N Urban Chaos: Riot Response Automatch (PS2) f +1275 scsdw PohZyA S.C.S. Dangerous Waters f +1277 uchaosrrps2 KPd0V9 Urban Chaos: Riot Response (PS2) f +1281 rdriver3ps2d \N Race Driver 3 Demo (PS2) f +1280 rdriver3ps2 BPAfNv Race Driver 3 (PS2) f +1284 rtrooperpcam \N Rogue Trooper Automatch (PC) f +1282 wptps2pal jL2aEz World Poker Tour PAL (PS2) f +1283 rtrooperpc jK7L92 Rogue Trooper (PC) f +1291 mxun05pcam \N MX vs. ATV Unleashed Automatch (PC) f +1289 dsnattest2 L74dSk ds nat test 2 f +1290 mxun05pc v8XaWc MX vs. ATV Unleashed (PC) f +1310 marvlegps2am \N Marvel Legends Automatch (PS2) f +1292 quake4 ZuZ3hq Quake 4 f +1293 paraworld EUZpQF ParaWorld f +1294 paraworldam EUZpQF ParaWorld Automatch f +1295 paraworldd EUZpQF ParaWorld Demo f +1296 callofduty2 DSpIxw Call of Duty 2 f +1298 slugfest06ps2 e8Cs3L Slugfest '06 (PS2) f +1299 bleachds 5BuVRR Bleach (DS) f +1300 lostmagicds eI0Rml Lost Magic (DS) f +1301 wofor mxw9Nu WOFOR: War on Terror f +1302 woforam mxw9Nu WOFOR: War on Terror Automatch f +1303 woford mxw9Nu WOFOR: War on Terror Demo f +1306 Happinuds DqO198 Happinuvectorone! (DS) f +1307 thawpc v8la4w Tony Hawk's American Wasteland (PC) f +1308 ysstrategyds gq2bHQ Y's Strategy (DS) f +1309 marvlegps2 eAMh9M Marvel Legends (PS2) f +1312 marvlegpspam \N Marvel Legends Automatch (PSP, PAL) f +1311 marvlegpsp eAMh9M Marvel Legends (PSP, PAL) f +1314 marvlegpcam \N Marvel Legends Automatch (PC) f +1313 marvlegpc eAMh9M Marvel Legends (PC) f +1315 marvlegpcd \N Marvel Legends Demo (PC) f +1318 hustleps2am \N Hustle: Detroit Streets Automatch (PS2) f +1317 hustleps2 ni9hdV Hustle: Detroit Streets (PS2) f +1342 ffurtdriftps2am \N The Fast and the Furious: Tokyo Drift Automatch (PS2) f +1320 koshien2ds UKdPFf PowerPro Pocket Koshien 2 (DS) f +1321 lotrbme2r g3Fd9z Lord of the Rings: The Battle for Middle-earth 2 f +1322 tenchuds dfOICS Tenchu (DS) f +1323 contactds quPooS Contact JPN (DS) f +1324 stella flfRQv Battlefield 2142 f +1325 stellad UoiZSm Battlefield 2142 (Demo) f +1327 tetrisds JJlSi8 Tetris DS (DS) f +1328 motogp4ps2 OCNxy3 MotoGP 4 (PS2) f +1329 actofwarht LaR21n Act of War: High Treason f +1330 actofwarhtam LaR21n Act of War: High Treason Automatch f +1331 actofwarhtd LaR21n Act of War: High Treason Demo f +1333 Customrobods MH0EK4 Custom Robo DS (DS) f +1334 comrade F72JWS Comrade f +1335 greconawf Fn5GLL Ghost Recon: Advanced Warfighter f +1336 greconawfd Fn5GLL Ghost Recon: Advanced Warfighter Demo f +1337 asobids 1L77RN Asobi Taizen (DS) f +1338 timeshift rHKFnV TimeShift (PC) f +1339 timeshiftb rHKFnV TimeShift Beta (PC) f +1341 ffurtdriftps2 Bso8LK The Fast and the Furious: Tokyo Drift (PS2) f +1344 pokemondpds 1vTlwb Pokemon Diamond-Pearl (DS) f +1345 coteaglesam cEb84M War Front: Turning Point Automatch f +1346 facesofwar Shp95z Faces of War f +1347 facesofwaram Shp95z Faces of War Automatch f +1348 facesofward Shp95z Faces of War Demo f +1349 bombermanslds 9dG7KP Bomberman Story/Land DS f +1350 fherjwkk RADpDr Namco Test f +1352 digistoryds n5t4VH Digimon Story (DS) f +1353 touchpanicds zHToa5 Touch Panic (DS) f +1354 SampAppTest 38u7Te Sample App Developement f +1270 bllrs2005ps2d \N NBA Ballers 2005 Demo (PS2) f +1387 civ4wrld oQ3v8V Civilization IV: Warlords f +1388 civ4wrldam oQ3v8V Civilization IV: Warlords Automatch f +1401 blkhwkdntsps2 \N Delta Force: Black Hawk Down - Team Sabre (PS2) f +1420 flatout2pc GtGLyx FlatOut 2 (PC) f +1434 anno1701 Xa6zS3 Anno 1701 f +1464 crysis ZvZDcL Crysis (PC) f +1465 crysisd ZvZDcL Crysis Demo f +1355 SampAppTestam 38u7Te Sample App Developement Automatch f +1358 fearxp1 n3V8cj FEAR: Extraction Point f +1377 marvlegps2pam \N Marvel Legends Automatch PAL (PS2) f +1361 redorchestra 6md8c4 Red Orchestra Ostfront f +1362 airwingsds 5TTmMf Air Wings (DS) f +1363 openseasonds MpxbPX OpenSeason DS (DS) f +1364 mageknight IZNkpb Mage Knight Apocalypse f +1366 mageknightd IZNkpb Mage Knight Apocalypse Demo f +1367 starfoxds RR7XGH Starfox DS (DS) f +1369 medieval2 G23p7l Medieval 2 Total War f +1370 medieval2am G23p7l Medieval 2 Total War Automatch f +1371 taisends SNyrMR Sangokushi Taisen DS (DS) f +1372 mkarmps2 VZvp7J Mortal Kombat: Armageddon (PS2) f +1373 thps3pcr KsE3a2 Tony Hawk 3 PC (Rerelease) f +1375 ffantasy3ds 6cidXe Final Fantasy III (DS) f +1376 marvlegps2p eAMh9M Marvel Legends PAL (PS2) f +1415 civ4jpam \N Civiliation IV Automatch (Japanese) f +1378 c5 uQCWJs Conflict: Denied Ops f +1379 rfberlin 3vhvcH Rush for Berlin f +1380 swat4xp1_tmp tG3j8c SWAT 4: The Stetchkov Syndicate Temp f +1381 swordots Z5gR9Z Sword of the Stars f +1382 mahjongkcds eBtrQN Mah-Jong Kakuto Club (DS) f +1386 Nushizurids nKShDp Nushizuri DS Yama no megumi Kawa no seseragi f +1389 dsiege2bw A3GXsW Dungeon Siege II: Broken World f +1390 blic2007 X2P8Th Brian Lara International Cricket 2007 f +1391 nwn2 wstKNe NeverWinter Nights 2 f +1392 pssake H8s0Pw Professional Services Sake Test f +1393 gmtestcd HA6zkS Test (Chat CD Key validation) f +1395 yugiohgx2ds 1A1iB2 Yu-Gi-OH! Duel Monsters GX2 (DS) f +1396 whammermoc rnbkJp Warhammer: Mark of Chaos f +1397 whammermocam rnbkJp Warhammer: Mark of Chaos Automatch f +1398 whammermocd rnbkJp Warhammer: Mark of Chaos Demo f +1399 flatout2ps2 VL6s2n FlatOut 2 (PS2) f +1400 cruciform TgsP47 Genesis Rising: The Universal Crusade f +1402 socelevends IwZXVX World Soccer Winning Eleven DS (DS) f +1403 konductrads odc8Ps Konductra (DS) f +1404 strongholdl LXkm3b Stronghold Legends f +1406 wsc2007ps3 m2bcKK World Snooker Championship 2007 (PS3) f +1407 ninsake TpSP5q Nintendo Sake Test f +1408 dwctest d4q9GZ DWC NintendoTest App f +1409 FieldOps AK8zHT Field Ops f +1410 wcpoker2pc t3Hd9q World Championship Poker 2 (PC) f +1411 whammer40kdc Ue9v3H Warhammer 40,000: Dark Crusade f +1413 fullautops3 kC5tJA Full Auto 2: Battlelines (PS3) f +1414 civ4jp y3D9Hw Civiliation IV (Japanese) f +1418 thps4pcram \N Tony Hawk: Pro Skater 4 Automatch (PC) Rerelease f +1416 contactusds pEldCc Contact US (DS) f +1417 thps4pcr L3C8s9 Tony Hawk: Pro Skater 4 (PC) Rerelease f +1436 civ4ruam \N Civiliation IV Automatch (Russian) f +1419 bf2ddostest hW6m9a Battlefield 2 DDoS testing f +1422 cc3tibwars E4F3HB Command & Conquer 3: Tiberium Wars f +1423 topspin2pc tTp6Pn Top Spin 2 (PC) f +1424 thdhilljamds 6gJBca Tony Hawk's Downhill Jam (DS) f +1425 aoex JafcLp Age of Empires Expansion f +1427 rafcivatwartam h98Sqa Rise And Fall: Civilizations at War Test Automatch f +1428 bokujomonods mM94Uc Bokujo Monogatari DS2: Wish-ComeTrue Island (DS) f +1429 tothrainbowds lA7Urd TOTH Rainbow Trail of Light (DS) f +1430 mkarmpalps2 VZvp7J Mortal Kombat: Armageddon PAL (PS2) f +1431 preyd 75rDsD Prey Demo f +1432 prey znghVS Prey f +1435 civ4ru y3D9Hw Civiliation IV (Russian) f +1438 civ4cham \N Civiliation IV Automatch (Chinese) f +1437 civ4ch y3D9Hw Civiliation IV (Chinese) f +1439 cricket2007 ABiuJy Brian Lara International Cricket 2007 f +1440 eternalforces xQEvFD Eternal Forces Demo f +1441 eternalforcesam xQEvFD Eternal Forces Automatch f +1442 eforcesr xQEvFD Eternal Forces f +1444 ptacticsds Wcs0GP Panzer Tactics (DS) f +1445 tankbeatds LxDL6t Tank Beat (JPN) (DS) f +1446 mdungeonds KqfKOx Mysterious Dungeon: Shiren the Wanderer DS (DS) f +1447 dqmonjokerds 5dOqvD Dragon Quest Monsters: Joker (DS) f +1449 oishiids mpmTyO Oishii Recipe (DS) f +1450 stlegacy x9qTsK Star Trek: Legacy f +1451 NN2Simple FTmNOH NatNeg2 Simple Test f +1452 yakumands dNte7R Yakuman DS (DS) f +1453 marveltcardds GkWfL7 Marvel Trading Card Game (DS) f +1454 ffantasy3usds 6Ta8ww Final Fantasy III - US (DS) f +1457 testdriveu P5eUD8 Test Drive Unlimited (Unused) f +1458 test071806 7bxOC2 Test f +1459 chocobombds FP75Oy Chocobo & Magic Book (DS) f +1460 puyopuyods 9bx1UP Puyo Puyo! (DS) f +1462 luckystar2ds BFxkaz Lucky Star 2 (DS) f +1463 lotrbme2wk g3Fd9z Lord of the Rings: The Battle for Middle-earth 2 (Rise of the Witch-King Expansion Pack) f +1466 monsterfarmds Qhcw9n Monster Farm DS (DS) f +1467 naruto5ds TZlbyZ NARUTO: Saikyou Ninja Daikesshuu 5 (DS) f +1468 picrossds 5TLWnF Picross (DS) f +1469 wh40kp uJ8d3N Warhammer 40,000: Dawn of War Patch f +1360 digistorydsam \N Digimon Story Automatch (DS) f +1516 civ4mac CWiCbk Civilization IV (MAC) f +1518 civ4wrldmac QtCpWE Civilization IV: Warlords (MAC) f +1528 anno1701d taEf7n Anno 1701 Demo f +1530 civ4wrldjp oQ3v8V Civilization IV: Warlords (Japan) f +1532 civ4wrldcn oQ3v8V Civilization IV: Warlords (Chinese) f +1563 greconawf2 qvOwuX Ghost Recon: Advanced Warfighter 2 f +1471 digiwrldds MLh2Hn Digimon World DS (DS) f +1472 pandeponds y0zg9C Panel De Pon DS (DS) f +1473 moritashogids rqz1dU Morita Shogi DS (DS) f +1475 lithdev vFQNfR Monolith Development f +1476 lithdevam vFQNfR Monolith Development Automatch f +1477 bf2142 FIlaPo Battlefield 2142 f +1478 bf2142b sdbMvQ Battlefield 2142 (Beta) f +1479 marvlegps3 eAMh9M Marvel Legends (PS3) f +1507 whtacticspspam \N Warhammer 40,000: Tactics Automatch (PSP) f +1481 marvlegps3p eAMh9M Marvel Legends PAL (PS3) f +1483 paradisecity TCD6mz Paradise City f +1484 whammermocdam rnbkJp Warhammer: Mark of Chaos Demo Automatch f +1513 marvlegnpspam \N Marvel Legends Automatch (PSP, NTSC) f +1488 tqexp1 ZCe2KH Titan Quest: Immortal Throne f +1489 tqexp1am ZCe2KH Titan Quest: Immortal Throne (Automatch) f +1492 marveltcard GkWfL7 Marvel Trading Card Game (PC & PSP) f +1493 marveltcardps GkWfL7 Marvel Trading Card Game (PSP) f +1495 prismgs 3Zxgne PRISM: Guard Shield f +1496 prismgsd 3Zxgne PRISM: Guard Shield Demo f +1497 testdriveub BeTTbz Test Drive Unlimited f +1498 testdriveud xueb6u Test Drive Unlimited Demo f +1499 swempirexp1 2WLab8 Star Wars: Empire at War - Forces of Corruption f +1500 dsakurads Sw2t7F Dragon Sakura DS (DS) f +1501 gopetsvids DQ5xwk GoPets: Vacation Island (DS) f +1502 kurikinds 2ZaR1q Kurikin (DS) f +1503 codedarmspsp E7Emxp Coded Arms (PSP) f +1505 wiinat 4Fuy9m Wii NAT Negotiation Testing f +1506 whtacticspsp KcKyRP Warhammer 40,000: Tactics (PSP) f +1517 civ4macam \N Civilization IV Automatch (MAC) f +1508 armedass peprUy ArmA f +1509 ffcryschronds z9WMZJ Final Fantasy: Crystal Chronicles - Ring of Fate (DS) f +1510 preystarsds NrSO9m Prey The Stars (DS) f +1512 marvlegnpsp eAMh9M Marvel Legends (PSP, NTSC) f +1519 civ4wrldmacam \N Civilization IV: Warlords Automatch (MAC) f +1514 spartaaw 09mczM Sparta: Ancient Wars f +1515 spartaawd 09mczM Sparta: Ancient Wars Demo f +1524 eearth3d \N Empire Earth III Demo f +1531 civ4wrldjpam \N Civilization IV: Warlords Automatch (Japan) f +1520 pokebattlewii TzRgSc Pokemon Battle Revolution (Wii) f +1521 puzquestds nW1e6h Puzzle Quest: Challenge of the Warlords (DS) f +1522 doraemonds P6nKJz Doraemon Nobita no Shinmakai Daiboken DS (DS) f +1523 eearth3 Fk6hTz Empire Earth III f +1533 civ4wrldcnam \N Civilization IV: Warlords Automatch (Chinese) f +1527 bf2142d UoiZSm Battlefield 2142 Demo f +1529 medieval2d yVjUSz Medieval II Demo f +1546 fullautops3d \N Full Auto 2: Battlelines Demo (PS3) f +1578 swempiremacam \N Star Wars: Empire at War Automatch (Mac) f +1534 whammer40ktds 9Tkwd4 Warhammer 40,000 Tactics (DS) f +1535 mukoubuchids 2yK3lC Mukoubuchi - Goburei, Shuryoudesune (DS) f +1536 cusrobousds pO5zuq Gekitoh! Custom Robo (DS) (US) f +1537 kurikurimixds Q25SLf Kuri Kuri Mix DS (DS) f +1538 custoboeuds hZCuTq Custom Robo (EU) (DS) f +1539 whammermoct rnbkJp Warhammer: Mark of Chaos Test f +1540 whammermoctam rnbkJp Warhammer: Mark of Chaos Test Automatch f +1541 sweawfocd qafBXM Forces of Corruption Demo f +1542 aoe3wcd ZDdpQV Age of Empires 3: The Warchiefs Demo f +1543 tolmamods sLFfpP Tolmamo (DS) f +1544 patchtest BlIpIG Patching Test f +1580 marvlegjpps3am \N Marvel Legends Automatch (PS3, Japan) f +1547 themark C69nBX The Mark f +1548 themarkam C69nBX The Mark Automatch f +1549 bf2142e flfRQv Battlefield 2142 (EAD) f +1550 supcommb pPhzeh Supreme Commander (Beta) f +1551 dow_dc RUgBVL Dawn of War: Dark Crusade f +1552 fuusuibands Gu3FCH Fuusuiban (DS) f +1554 s_darkmmm ggXhvY Dark Messiah of Might and Magic f +1555 ppirates VGFzVf Puzzle Pirates f +1556 mschargedwii B4LdGW Mario Strikers Charged (Wii) f +1557 8ballstarsds b6WiRo 8-Ball Allstars (DS) f +1558 tmntds XZr5Nr Teenage Mutant Ninja Turtles (DS) f +1559 surfsupds vTS4gO Surf's Up (DS) f +1560 elemonsterds 26pNrL Elemental Monster (DS) f +1562 picrossEUds 1rAhgD Picross (EU) (DS) f +1564 greconawf2am qvOwuX Ghost Recon: Advanced Warfighter 2 Automatch f +1565 greconawf2d qvOwuX Ghost Recon: Advanced Warfighter 2 Demo f +1566 yugiohWC07ds QgkE62 Yu-Gi-Oh! Dual Monsters World Championship 2007 (DS) f +1567 tgmasterds cHgbU3 Table Game Master DS (DS) f +1568 batwars2wii XvB2pu Battalion Wars II (Wii) f +1569 Doragureidods x10zDJ Doragureido (DS) f +1570 armedassd peprUy ArmA Demo f +1571 ffantasy3euds 9zRsJF Final Fantasy III - EU (DS) f +1572 rockstardev 1a8bBi Rockstar Development f +1575 mparty1ds rUpE9b Mario Party (DS) f +1576 stalkerscd t9Fj3M STALKER: Shadows of Chernobyl Beta f +1577 swempiremac yTNCrM Star Wars: Empire at War (Mac) f +1579 marvlegjpps3 eAMh9M Marvel Legends (PS3, Japan) f +1480 marvlegps3am \N Marvel Legends Automatch (PS3) f +1640 smrailroadsjpam h32mq8 Sid Meier's Railroads! Japan Automatch f +1581 drmariowii BvQyb2 Dr. Mario (WiiWare) f +1582 springwidgets tQfwTW Spring Widgets f +1584 lotrbfme2 PvzwZF The Rise of The Witch-king f +1585 wmarkofchaos nGmBcN Warhammer Mark of Chaos f +1586 warmonger I1WnEQ Warmonger f +1587 everquest2 vPmJGO EverQuest II f +1588 startreklegacy jMaWnz Star Trek: Legacy f +1590 lozphourds t8RsDb The Legend of Zelda: Phantom Hourglass (DS) f +1591 vanguardbeta TorchQ Vanguard beta f +1592 digisunmoonds 6DPXX9 Digimon Story Sunburst/Moonlight (DS) f +1593 wmarkofchaosd nGmBcN Warhammer Mark of Chaos Demo f +1594 cruciformam TgsP47 Genesis Rising: The Universal Crusade Automatch f +1595 tcghostreconaw wLCSvJ tcghostreconaw f +1596 ghostraw Cybhqm Ghost Recon Advanced Warfighter f +1597 rainbowsixv GUePbj Rainbow Six Vegas f +1598 wmarkofchaosdam nGmBcN Warhammer Mark of Chaos Demo Automatch f +1599 crysisb ZvZDcL Crysis Beta f +1600 scotttest RPzJL7 Scott's test gamename f +1601 rftbomb xpoRNK Rush for the Bomb f +1603 motogp2007am oXCZxz MotoGP 2007 Automatch f +1604 cityofheroes tJnRie City of Heroes f +1605 cityofvl LEJaXZ City of Villains f +1606 titanquestit orNtwo Titan Quest Immortal Throne f +1607 girlsds hKe82J Girls (DS) f +1609 mmessagesds 5wFQve Mixed Messages (DS) f +1610 topanglerwii 2SbZew Top Angler (Wii) f +1611 swbfffpsp fytErP Star Wars Battlefront: Renegade Squadron (PSP) f +1612 facesow qgiNRG Faces of War f +1615 dexplorerds 8mqApN Dungeon Explorer (DS) f +1632 eearth3am \N Empire Earth III Automatch f +1618 civconps3d hn53vx Civilization Revolution Demo (PS3) f +1620 stalkerscb t9Fj3M STALKER: Shadows of Chernobyl Beta (Unused) f +1622 secondlife wpIwVb Second Life f +1623 tdubeta VsReXT Test Drive Unlimited Beta f +1625 bsmidway fLtUIc Battlestations Midway Demo f +1626 etforces GKPQiB Eternal Forces f +1627 genesisrbeta tZRxNP Genesis Rising Beta f +1628 tankbeatusds 8UdAVm Tank Beat (US) (DS) f +1629 champgamesps3 dwg55x High Stakes on the Vegas Strip: Poker Edition (PS3) f +1631 eearth3b Fk6hTz Empire Earth III Beta f +1643 facesofwarxp1am \N Faces of War Standalone Automatch (XP1) f +1633 chocmbeuds O8r2ST Chocobo & Magic Book (EU) (DS) f +1634 supcommdemo plfinb Supreme Commander Demo f +1635 tetriskords KrMqE6 Tetris DS (KOR) (DS) f +1637 wincircleds 2ckCbe Winner's Circle (DS) f +1638 itadakistds 5AesfG Itadaki Street DS (DS) f +1641 megamansfds r2zQPw Mega Man Star Force (US) (DS) f +1642 facesofwarxp1 QQxWKm Faces of War Standalone (XP1) f +1662 roguewarpcam \N Rogue Warrior Automatch (PC) f +1644 timeshiftx rHKFnV TimeShift (Xbox 360) f +1645 timeshiftps3 rHKFnV TimeShift (PS3) f +1647 kingbeetlesds 9mTZtW The King of Beetles Mushiking Super Collection (DS) f +1648 dragonbzwii oHk248 Dragonball Z (Wii) f +1649 atlas_samples Zc0eM6 ATLAS Sample Apps f +1650 cc3tibwarsd yGVzUf Command & Conquer 3 Demo f +1651 supcomm UMEjry Supreme Commander f +1652 assaultheroes WpA5Tx Assault Heroes f +1653 assaultheroesam WpA5Tx Assault Heroes Automatch f +1655 PowaPPocketds Mz5dau PowaProkun Pocket10 (DS) f +1656 GunMahjongZds XL3iSh Kidou Gekidan Haro Ichiza Gundam Mah-jong+Z (DS) f +1657 fullmatcgds fGRd5f Fullmetal Alchemist Trading Card Game (DS) f +1658 smashbrosxwii 3AxIg4 Dairantou Smash Brothers X (Wii) f +1659 disfriendsds VNTp6E Disney Friends DS (DS) f +1660 Jyotrainwii bRtr3p Minna de Jyoshiki Training Wii (Wii) f +1661 roguewarpc ey8w3N Rogue Warrior (PC) f +1665 roguewarps3am \N Rogue Warrior Automatch (PS3) f +1664 roguewarps3 bHtwt6 Rogue Warrior (PS3) f +1666 roguewarps3d \N Rogue Warrior Demo (PS3) f +1681 gta4pcam \N Grand Theft Auto 4 Automatch (PC) f +1667 archlord eLiRAC Archlord f +1668 racedriverds AJQu6F Race Driver (DS) f +1669 kaihatsuds gW0gp9 Kaihatsushitsu (DS) f +1671 redbaronww1 aMETX7 Red Baron WWI f +1672 greconawf2b qvOwuX Ghost Recon: Advanced Warfighter 2 Beta f +1673 greconawf2bam qvOwuX Ghost Recon: Advanced Warfighter 2 Beta Automatch f +1674 ubisoftdev K4wfax Ubisoft Development f +1675 ubisoftdevam K4wfax Ubisoft Development Automatch f +1676 testdriveuak k7r85E Test Drive Unlimited (Akella) f +1677 armaas fgDmOT ArmA: Armed Assault f +1678 gta4ps3 t3nTru Grand Theft Auto 4 (PS3) f +1680 gta4pc t3nTru Grand Theft Auto 4 (PC) f +1684 gta4xam \N Grand Theft Auto 4 Automatch (Xbox 360) f +1682 flashanzands 61pARy Flash Anzan Doujou (DS) f +1683 gta4x t3nTru Grand Theft Auto 4 (Xbox 360) f +1685 pokerangerds v4yMCT Pokemon Ranger 2 (DS) f +1686 megamansfeuds 8wlN9C Mega Man Star Force (EU) (DS) f +1687 mariokartwii 9r3Rmy Mario Kart Wii (Wii) f +1688 swtakoronwii xvf3KV Shall we Takoron (Wii) f +1689 phylon rbKTOq Phylon f +1691 warfronttp LTOTAa War Front: Turning Point f +1692 fearxp2 rDAg9r FEAR Perseus Mandate (PC) f +1693 risingeaglepc 9cy8vc Rising Eagle f +1694 bombls2ds 8BuVqr Touch! Bomberman Land 2 / Bomberman DS 2 (DS) f +1617 civconps3am \N Civ Console Automatch (PS3) f +1703 civ4bts Cs2iIq Civilization IV: Beyond the Sword f +1704 civ4btsam Cs2iIq Civilization IV: Beyond the Sword Automatch f +1696 sonriders2wii m2F95I Sonic Riders 2 (Wii) f +1697 sonicrushads so70kL Sonic Rush Adventure (DS) f +1698 ghostsquadwii lq8to9 Ghost Squad (Wii) f +1700 contrads bw215o Contra DS (DS) f +1701 bleach1USds d4wISd Bleach DS (US) (DS) f +1702 bleach2USds 7xEJsp Bleach DS 2 (US) (DS) f +1705 eearth3bam Fk6hTz Empire Earth III Beta Automatch f +1723 mclub4ps3am \N Midnight Club 4 Automatch (PS3) f +1707 thetsuriwii LhtR3d The Tsuri (Wii) f +1708 theracewii 6JbTWP The Race (Wii) f +1709 tankbeat2ds aAbi3S Tank Beat 2 (DS) f +1710 onslaughtpc 8pLvHm Onslaught: War of the Immortals f +1712 onslaughtpcd 8pLvHm Onslaught: War of the Immortals Demo f +1713 jikkyoprowii TKmp3m Jikkyo Powerful Pro Yakyu Wii (Wii) f +1714 cc3dev Ba77xN Command & Conquer 3 Dev Environment f +1715 cc3devam Ba77xN Command & Conquer 3 Dev Environment Automatch f +1716 wsc2007 xKCMfK World Snooker Championship 2007 f +1717 luminarcUSds aI8FCJ Luminous Arc (US) (DS) f +1718 bleach1EUds 9AxT0s Bleach DS (EU) (DS) f +1719 MSolympicds kK9ibq Mario & Sonic at the Olympic Games (DS) f +1720 keuthendev TtEZQR Keuthen.net Development f +1722 mclub4ps3 GQ8VXR Midnight Club 4 (PS3) f +1725 mclub4xboxam \N Midnight Club 4 Automatch (Xbox360) f +1724 mclub4xbox GQ8VXR Midnight Club 4 (Xbox360) f +1728 ut3pcam \N Unreal Tournament 3 Automatch (PC) f +1726 girlssecretds kNcVft Girls Secret Diary (DS) f +1727 ut3pc nT2Mtz Unreal Tournament 3 (PC) f +1734 tpfolpcam \N Turning Point: Fall of Liberty Automatch (PC) f +1729 ut3pcd KTiJdD Unreal Tournament 3 Demo (PC) f +1730 ecorisds dL9zd8 Ecoris (DS) f +1731 ww2btanks NcQeTO WWII Battle Tanks: T-34 vs Tiger f +1732 genesisr sbCDkj Genesis Rising f +1735 tpfolpcd \N Turning Point: Fall of Liberty Demo (PC) f +1737 tpfolps3am \N Turning Point: Fall of Liberty Automatch (PS3) f +1736 tpfolps3 svJqvE Turning Point: Fall of Liberty (PS3) f +1743 worldshiftpcam \N WorldShift Automatch (PC) f +1738 hsmusicalds Em72Cr High School Musical (DS) f +1739 cardheroesds xnSA6P Card Heroes (DS) f +1740 metprime3wii i8sP5E Metroid Prime 3 (Wii) f +1742 worldshiftpc 7gBmF4 WorldShift (PC) f +1744 worldshiftpcd \N WorldShift Demo (PC) f +1745 kingclubsds rL9dOy King of Clubs (DS) f +1746 MSolympicwii i6lEcz Mario & Sonic at the Olympic Games (Wii) f +1747 ingenious HiBpaV Ingenious f +1748 potco wnYrOe Pirates of the Caribbean Online f +1749 madden08ds wFuf7q Madden NFL 08 (DS) f +1751 fury KOMenT Fury f +1752 twoods08ds nNgv7v Tiger Woods 08 (DS) f +1753 otonazenshuds nWsT7z Otona no DS Bungaku Zenshu (DS) f +1754 bestfriendds i7Sk5y Best Friend - Main Pferd (DS) f +1755 nindev EdD7Ve Nintendo Network Development Testing f +1756 quakewarset i0hvyr Enemy Territory: Quake Wars f +1758 gwgalaxiesds 85buOw Geometry Wars Galaxies (DS) f +1759 gwgalaxieswii o3G7J2 Geometry Wars Galaxies (Wii) f +1760 hrollerzds ih5ZOl Homies Rollerz (DS) f +1761 dungeonr RibMFo Dungeon Runners f +1762 dirtdemo Twesup DiRT Demo f +1763 nameneverds oL7dO2 Nameless Neverland (DS) f +1764 proyakyuds Nv5Em6 Pro Yakyu Famisute DS (DS) f +1765 foreverbwii K7bZgf Forever Blue (Wii) f +1766 ee3alpha VQibCm Empire Earth III Alpha f +1768 officersgwupc xSK6e5 Officers: God With Us f +1769 officersgwupcam xSK6e5 Officers: God With Us Automatch f +1770 swordotnw TkDsNE Sword of the New World f +1771 nfsprostds 1Xq6Ru Need for Speed Pro Street (DS) f +1772 commandpc rSRJDr Commanders: Attack! f +1773 commandpcam rSRJDr Commanders: Attack! Automatch f +1775 whamdowfram pXL838 Warhammer 40,000: Dawn of War - Final Reckoning Automatch f +1776 shirenUSEUds bo4NTp Mysterious Dungeon: Shiren the Wanderer DS (US-EU) (DS) f +1777 sporeds 5kq9Pf Spore (DS) f +1778 mysecretsds Cz5NpG My Secrets (DS) f +1779 nights2wii Wo9FKJ NiGHTS: Journey of Dreams (Wii) f +1780 tgstadiumwii w1pKbR Table Game Stadium (Wii) f +1781 lovegolfwii mToBwA Wii Love Golf (Wii) f +1783 tankbattlesds kJl1BG Tank Battles (DS) f +1784 anarchyonline ThLopr Anarchy Online f +1785 hookedEUwii qLaV1p Hooked! Real Motion Fishing (EU) (Wii) f +1786 hookedJPNwii m4JMgN Hooked! Real Motion Fishing (JPN) (Wii) f +1787 tankbeatEUds Zc4TGh Tank Beat (EU) (DS) f +1788 farcry HkXyNJ Far Cry f +1789 yugiohwc08ds X6i4ay Yu-Gi-Oh! World Championship 2008 (DS) f +1790 trackfieldds zC5kgT International Track & Field (DS) f +1792 RKMvalleyds 3lwaZW River King: Mystic Valley (DS) f +1793 DSwars2ds fF4Wtd DS Wars 2 (DS) f +1794 cdkeys QcpyWG CD Key Admin Site Testing f +1795 wordjongds V6dC1l Word Jong - US (DS) f +1796 raymanrr2wii Dd8tS2 Rayman Raving Rabbids 2 (Wii) f +1797 nanostray2ds grOpw9 Nanostray 2 (DS) f +1798 guitarh3wii O5imMd Guitar Hero 3 (Wii) f +1800 segatennisps3 RvJsgM Sega SuperStars Tennis (PS3) f +1801 segatennisps3am RvJsgM Sega SuperStars Tennis Automatch f +1706 eearth3dam \N Empire Earth III Demo Automatch f +1803 fifa08ds zLTgc4 FIFA 08 Soccer (DS) f +1805 dragladeds wPb9aW Draglade (DS) f +1806 takoronUSwii 3hzhvW Takoron (US) (Wii) f +1807 dragonbzUSwii 5tRJqr Dragonball Z: Tenkaichi 3 (US) (Wii) f +1808 arkanoidds QxU6hI Arkanoid DS (DS) f +1809 rfactory2ds lo8Vq8 Rune Factory 2 (DS) f +1810 dow VLxgwe Dawn of War f +1811 nitrobikewii viBXma Nitrobike (Wii) f +1813 WSWelevenwii aFJW2D World Soccer Winning Eleven Wii (Wii) f +1814 cc3xp1 BhcJLQ Command & Conquer 3: Expansion Pack 1 f +1815 cc3xp1am BhcJLQ Command & Conquer 3: Expansion Pack 1 Automatch f +1816 pachgundamwii w101rF Pachisuro Kido Senshi Gundam Aisenshi Hen (Wii) f +1817 newgamename 3I7iFz dhdh f +1818 newgamenameam 3I7iFz dhdh Automatch f +1819 gsTiaKreisDS p4oT53 Genso Suikokuden TiaKreis (DS) f +1820 ultimateMKds irQTn8 Ultimate Mortal Kombat (DS) f +1821 MLBallstarsds KT8yGE Major League Baseball Fantasy All-Stars (DS) f +1822 wicb SITsUf World in Conflict Beta f +1823 ee3beta tvbRKD Empire Earth III Beta f +1825 mafia2pc HgPhRC Mafia 2 (PC) f +1828 mafia2ps3am \N Mafia 2 Automatch (PS3) f +1827 mafia2ps3 cn3EpM Mafia 2 (PS3) f +2419 sawpcd \N SAW Demo (PC) f +1829 chocotokiwii uxXJS3 Chocobo no Fushigina Dungeon: Toki-Wasure no Meikyu (Wii) f +1830 zeroGds ViuPw7 ZeroG (DS) f +1832 furaishi3wii xK58W8 Furai no Shiren 3 Karakuri Yashiki no Nemuri Hime (Wii) f +1833 ben10bb bwV3xN Ben 10 Bounty Battle f +1834 ben10bbam bwV3xN Ben 10 Bounty Battle Automatch f +1835 risingeagleg qVcJOg Rising Eagle f +1836 timeshiftg PAWCeH Timeshift f +1837 cadZ2JPwii Z5bgxv Caduceus Z2 (Wii) f +1838 rockmanBSDds bNM3ah Rockman 2 - Berserk: Shinobi / Dinosaur (DS) f +1839 THPGds oucE6T Tony Hawks Proving Ground (DS) f +1840 birhhpc sPZGCy Brothers In Arms: Hell's Highway (PC) f +1842 birhhps3 HrDRqe Brothers In Arms: Hell's Highway (PS3) f +1843 birhhps3am HrDRqe Brothers In Arms: Hell's Highway Clone Automatch (PS3) f +1844 sakuraTDDds Kw2K7t Sakura Taisen Dramatic Dungeon - Kimiarugatame (DS) f +1845 fsxa edkTBp Flight Simulator X: Acceleration f +1846 fsxaam edkTBp Flight Simulator X: Acceleration Automatch f +1847 ee3 dObLWQ Empire Earth 3 f +1848 nwn2mac m8NeTP NeverWinter Nights 2 (MAC) f +1853 cohofbeta \N Company of Heroes: Opposing Fronts MP Beta f +1851 quakewarsetd i0hvyr Enemy Territory: Quake Wars Demo f +1852 evosoccer08ds fI2bz5 Pro Evolution Soccer 2008 (DS) f +1863 ut3pcdam \N Unreal Tournament 3 Demo Automatch (PC) f +1854 tetrisppwii p8a6oW Tetris++ (WiiWare) f +1855 bioshock SGeqMj Bioshock Demo f +1856 bioshockd eoaXfs Bioshock f +1857 civrevods o3WUx2 Sid Meier's Civilization Revolution (DS) f +1858 ninjagaidends 4VMtoU Ninja Gaiden: Dragon Sword (DS) f +1860 clubgameKORds Jb3Tt1 Clubhouse Games (KOR) (DS) f +1861 wic mtjzlE World in Conflict Demo f +1862 wicd taMBOb World in Conflict Demo f +1880 ut3ps3am \N Unreal Tournament 3 Automatch (PS3) f +1864 evosoc08USds nV87bl Pro Evolution Soccer 2008 (US) (DS) f +1865 wormsasowii UeJRpQ Worms: A Space Oddity (Wii) f +1866 painkillerod zW4TsZ Painkiller Overdose f +1868 cnpanzers2cw 2mEAh7 Codename Panzers 2: Cold Wars (PC) f +1869 cnpanzers2cwam 2mEAh7 Codename Panzers 2: Cold Wars Automatch f +1870 cnpanzers2cwd 2mEAh7 Codename Panzers 2: Cold Wars Demo f +1871 luminarc2ds e8O5aA Luminous Arc 2 Will (DS) f +1872 noahprods aP8x4A Noah's Prophecy (DS) f +1874 thesactionwii zKsfNR The Shooting Action (Wii) f +1875 Asonpartywii g4hp4x Asondewakaru THE Party/Casino (Wii) f +1876 sptouzokuds 4TxC2h Steel Princess Touzoku Koujyo (DS) f +1877 heiseikyods 4zBVda Heisei Kyoiku Iinkai DS Zenkoku Touitsu Moshi Special (DS) f +1878 famstadiumwii s75Uvn Family Stadium Wii (Wii) f +1879 ut3ps3 nT2Mtz Unreal Tournament 3 (PS3) f +1881 ut3ps3d \N Unreal Tournament 3 Demo (PS3) f +1890 goreAVam \N Gore Automatch (Ad version) f +1882 ecocreatureds 61JwLu Eco-Creatures: Save the Forest (DS) f +1883 mohairborne TjNJcy Medal of Honor: Airborne f +1885 whamdowfrbam pXL838 Warhammer 40,000: Dawn of War - Final Reckoning Beta Automatch f +1886 puyobomberds rU1vT5 Puyo Puyo Bomber (DS) f +1887 s_tf2 AYcFzB Team Fortress 2 f +1888 painkillerodd zW4TsZ Painkiller Overdose Demo f +1889 goreAV NYZEAK Gore (Ad version) f +1891 goreAVd \N Gore Demo (Ad version) f +1902 pkodgermanam \N Painkiller Overdose Automatch (German) f +1892 fstreetv3ds d46hQk FIFA Street v3 (DS) f +1893 dragladeEUds fpEnOg Draglade (EU) (DS) f +1894 culdceptds 6qUoZg Culdcept DS (DS) f +1895 ffwcbeta oZTTQy Frontlines: Fuel of War Beta f +1896 ffowbeta wqhcSQ Frontlines: Fuel of War Beta f +1901 pkodgerman wK54dt Painkiller Overdose (German) f +1903 pkodgermand \N Painkiller Overdose Demo (German) f +1904 cohof epROcy Company of Heroes: Opposing Fronts f +1905 puzzlernumds JpBkl8 Puzzler Number Placing Fun & Oekaki Logic 2 (DS) f +1906 supcomfabeta xvuHpR Supreme Commander: Forged Alliance beta f +1907 condemned2bs kwQ9Ak Condemned 2: Bloodshot (PS3) f +1849 nwn2macam \N NeverWinter Nights 2 Automatch (MAC) f +1826 mafia2pcam \N Mafia 2 Automatch (PC) f +1934 civ4btsjp Cs2iIq Civilization IV: Beyond the Sword (Japanese) f +1909 condemned2bsd kwQ9Ak Condemned 2: Bloodshot Demo (PS3) f +1910 potbs GIMHzf Pirates of the Burning Sea f +1911 mxvsatvutps2 8GnBeH MX vs ATV Untamed (PS2) f +1928 swbfront3pcam \N Star Wars Battlefront 3 Automatch (PC) f +1914 ut3demo KTiJdD Unreal Tournament 3 Demo f +1915 ut3 UebAWH Unreal Tournament 3 f +1916 valknightswii N5Mf0P Valhalla Knights (Wii) f +1917 amfbowlingds c56nI8 AMF Bowling: Pinbusters! (DS) f +1918 ducatimotods i9Duh0 Ducati Moto (DS) f +1919 arkwarriors sTPqLc Arkadian Warriors f +1920 arkwarriorsam sTPqLc Arkadian Warriors Automatch f +1921 mxvsatvutwii fuA9hK MX vs ATV Untamed (Wii) f +1922 cheetah3ds 4aCzFd The Cheetah Girls 3 (DS) f +1923 whammermocbm EACMEZ Warhammer: Mark of Chaos - Battle March f +1925 whammermocbmd EACMEZ Warhammer: Mark of Chaos - Battle March Demo f +1926 linksds 9TKvyS Links (DS) f +1927 swbfront3pc y3AEXC Star Wars Battlefront 3 (PC) f +1937 timeshiftd \N TimeShift Demo (PC) f +1929 siextremeds 69EvKG Space Invaders Extreme f +1931 redsteel2wii eAmAH0 Red Steel 2 (Wii) f +1932 gorese NYZEAK Gore Special Edition f +1933 dinokingEUds 0E0awE Ancient Ruler Dinosaur King (EU) (DS) f +1936 charcollectds DlZ3ac Character Collection! DS (DS) f +1945 nitrobikeps2am \N Nitrobike Automatch (PS2) f +1938 gtacwarsds nm4V4b Grand Theft Auto: Chinatown Wars (DS) f +1939 fireemblemds pTLtHq Fire Emblem DS (DS) f +1940 soccerjamds vTgXza Soccer Jam (DS) f +1941 gravitronwii V9q1aK Gravitronix (WiiWare) f +1942 mdamiiwalkds 8c2EoW Minna de Aruku! Mii Walk (DS) f +1943 puzzlemojiwii 0Um7ap Kotoba no Puzzle Moji Pittan Wii (WiiWare) f +1944 nitrobikeps2 ApD3DN Nitrobike (PS2) f +1951 sbk08pcam \N SBK '08: Superbike World Championship Automatch (PC) f +1946 dinokingUSds 8DgStx Dinosaur King (US) (DS) f +1947 harvfishEUds ZGwLfc Harvest Fishing (EU) (DS) f +1949 harmoon2ds 5rNkAv Harvest Moon DS 2 (EU) (DS) f +1950 sbk08pc Xhg4AV SBK '08: Superbike World Championship (PC) f +1952 sbk08pcd \N SBK '08: Superbike World Championship Demo (PC) f +1954 sbk08ps3am \N SBK '08: Superbike World Championship Automatch (PS3) f +1953 sbk08ps3 Xhg4AV SBK '08: Superbike World Championship (PS3) f +1962 timeshiftps3d \N TimeShift Demo (PS3) f +1955 exitds Ip6JF2 Hijyoguchi: EXIT DS (DS) f +1956 spectrobes2ds uBRc5a Kaseki Choshinka Spectrobes 2 (DS) f +1957 nanost2EUds Soz92T Nanostray 2 (EU) (DS) f +1958 crysisspd IWfplN Crysis SP Demo f +1960 evosoc08USwii 94lupD Pro Evolution Soccer 2008 (US) (Wii) f +1961 sdamigowii gLq68v Samba de Amigo (Wii) f +1981 ballers3ps3am \N NBA Ballers: Chosen One Automatch (PS3) f +1963 chesswii X8qanS Wii Chess (Wii) f +1964 ecolisEUds uCaiU4 Ecolis (EU) (DS) f +1965 rdgridds 81TpiD Race Driver: Grid (DS) f +1966 swbfront3wii ILAvd9 Star Wars: Battlefront 3 (Wii) f +1967 guitarh3xpwii xkrTOa Guitar Hero 3 Expansion Pack (Wii) f +1968 callofduty4 AOPMCh Call of Duty 4: Modern Warfare f +1969 mmadnessexps3 M2ydAr Monster Madness EX (PS3) f +1971 mmadexps3d M2ydAr Monster Madness EX Demo (PS3) f +1972 terrortkdwn2 VSlLZK Terrorist Takedown 2 f +1973 terrortkdwn2am KmqStH Terrorist Takedown 2 Automatch f +1974 terrortkdwn2d KmqStH Terrorist Takedown 2 Demo f +1975 cc3xp1mb BhcJLQ Command & Conquer 3: Kane's Wrath Match Broadcast Clone f +1976 dstallionds 1fNr6d Derby Stallion DS (DS) f +1978 thecombatwii VM5pGy SIMPLE Wii Series Vol.6 THE Minna de Waiwai Combat (Wii) f +1979 cc3kw TPLstA Command and Conquer 3 Kanes Wrath f +1980 ballers3ps3 lu5P4Q NBA Ballers: Chosen One (PS3) f +1982 ballers3ps3d \N NBA Ballers: Chosen One Demo (PS3) f +1985 scourgepcam \N The Scourge Project Automatch (PC) f +1983 cc3kwmb TPLstA Command and Conquer 3 Kanes Wrath Match Broadcast f +1984 scourgepc NExdfn The Scourge Project (PC) f +1986 scourgepcd \N The Scourge Project Demo (PC) f +1988 scourgeps3am \N The Scourge Project Automatch (PS3) f +1987 scourgeps3 NExdfn The Scourge Project (PS3) f +1989 scourgeps3d \N The Scourge Project Demo (PS3) f +2002 mxvatvuPALps2am \N MX vs ATV Untamed PAL Automatch (PS2) f +1991 popwii cQbQkV Pop (WiiWare) f +1992 tenchu4wii z2VBXP Tenchu 4 (Wii) f +1993 ssoldierrwii 4w5JoH Star Soldier R (WiiWare) f +1994 2kboxingds JiBAt0 2K Boxing (DS) f +1995 bldragonds iICaoP Blue Dragon (DS) f +1996 elebitsds bVEhC3 Elebits DS - Kai to Zero no Fushigi na Bus (DS) f +1997 nobunagapktds t8SaPD Nobunaga no Yabou DS Pocket Senshi (DS) f +1998 kqmateDS 8dEm7s KaitoranmaQ Mate! (DS) f +1999 digichampds lDiK3f Digimon Championship (DS) f +2000 yakumanwii vcBUtC Yakuman Wii (WiiWare) f +2006 redbaronps3am \N Red Baron WWI Automatch (PS3) f +2003 mezasetm2wii hfBxDP Mezase!! Tsuri Master 2 (Wii) f +2004 raw2009wii hCcmdR WWE Smackdown! vs RAW 2009 (Wii) f +2005 redbaronps3 aMETX7 Red Baron WWI (PS3) f +2007 memansf2USDS ZAO34c Mega Man Star Force 2: Zerker x Shinobi / Saurian (US) f +2008 mxvatvutEUwii 9vF1oO MX vs ATV Untamed (EU) (Wii) f +2009 lostmagicwii hQrVFm Lost Magic Wii (Wii) f +1912 mxvsatvutps2am \N MX vs ATV Untamed Automatch (PS2) f +2014 blitz08ps3am \N Blitz: The League 08 Automatch (PS3) f +2013 blitz08ps3 Jk4zlB Blitz: The League 08 (PS3) f +2015 blitz08ps3d \N Blitz: The League 08 Demo (PS3) f +2022 finertiaps3am \N Fatal Inertia Automatch (PS3) f +2017 lonposwii L08ik8 Lonpos (WiiWare) f +2018 cvania08ds SwO9Jn Castlevania 2008 (DS) f +2019 nplusds qX9Muy N+ (DS) f +2020 gauntletds wUq7fL Gauntlet (DS) f +2021 finertiaps3 3vEcPe Fatal Inertia (PS3) f +2029 tpfolEUpcd \N Turning Point: Fall of Liberty Demo (EU) (PC) f +2023 topspin3usds 8R4LgD Top Spin 3 (US) (DS) f +2024 topspin3euds ETgvzA Top Spin 3 (EU) (DS) f +2026 simcitywii tLpVr1 SimCity Creator (Wii) f +2027 tpfolEUpc ltSs4H Turning Point: Fall of Liberty (EU) (PC) f +2031 tpfolEUps3am \N Turning Point: Fall of Liberty Automatch (EU) (PS3) f +2030 tpfolEUps3 svJqvE Turning Point: Fall of Liberty (EU) (PS3) f +2033 parabellumpcam \N Parabellum Automatch (PC) f +2032 parabellumpc CXabGK Parabellum (PC) f +2034 parabellumpcd \N Parabellum Demo (PC) f +2036 parabellumps3am \N Parabellum Automatch (PS3) f +2035 parabellumps3 CXabGK Parabellum (PS3) f +2039 necrovisionpcam \N NecroVision Automatch (PC) f +2037 monlabwii 8Lypy2 Monster Lab (Wii) f +2038 necrovisionpc Rn3Ptn NecroVision (PC) f +2040 necrovisionpcd \N NecroVision Demo (PC) f +2042 necrovisionpdam \N NecroVision Automatch (PC) Demo f +2041 necrovisionpd Rn3Ptn NecroVision (PC) Demo f +2044 damnationpcam \N DamNation Automatch (PC) f +2043 damnationpc Jpxpfr DamNation (PC) f +2045 damnationpcd \N DamNation Demo (PC) f +2049 parabellumpcdam \N Parabellum Demo Automatch (PC) f +2046 damnationps3 Jpxpfr DamNation (PS3) f +2048 strongholdce UWmLcS Stronghold: Crusader Extreme f +2056 mclub4xboxdevam \N Midnight Club 4 Dev Automatch (Xbox360) f +2050 madeinoreds 6vwCT1 Made in Ore (DS) f +2051 guinnesswrds iwTBGk Guinness World Records: The Video Game (DS) f +2052 guinnesswrwii x0oPvh Guinness World Records: The Video Game (Wii) f +2053 mclub4ps3dev GQ8VXR Midnight Club 4 Dev (PS3) f +2055 mclub4xboxdev GQ8VXR Midnight Club 4 Dev (Xbox360) f +2058 gta4pcdevam \N Grand Theft Auto 4 Dev Automatch (PC) f +2057 gta4pcdev t3nTru Grand Theft Auto 4 Dev (PC) f +2060 gta4ps3devam \N Grand Theft Auto 4 Dev Automatch (PS3) f +2059 gta4ps3dev t3nTru Grand Theft Auto 4 Dev (PS3) f +2062 gts4xdevam \N Grand Theft Auto 4 Dev Automatch (Xbox 360) f +2061 gts4xdev t3nTru Grand Theft Auto 4 Dev (Xbox 360) f +2065 plunderpcam \N Plunder Automatch (PC) f +2063 monfarm2ds IvFJw0 Monster Farm DS 2 (DS) f +2064 plunderpc NmLqNN Age of Booty (PC) f +2068 legendaryps3am \N Legendary Automatch (PS3) f +2066 plunderpcd NmLqNN Plunder Demo (PC) f +2073 beijing08pcam \N Beijing 2008 Automatch (PC) f +2069 callofduty4d2d AOPMCh Call of Duty 4: Modern Warfare f +2070 sekainodokods cS9uS9 Sekai no Dokodemo Shaberu! DS Oryori Navi (DS) f +2071 glracerwii kAM5wF GameLoft's Racer (WiiWare) f +2072 beijing08pc P4QzX5 Beijing 2008 (PC) f +2076 beijing08ps3am \N Beijing 2008 Automatch (PS3) f +2075 beijing08ps3 P4QzX5 Beijing 2008 (PS3) f +2077 beijing08ps3d \N Beijing 2008 Demo (PS3) f +2079 hail2chimps3am \N Hail to the Chimp Automatch (PS3) f +2078 hail2chimps3 mDeBg3 Hail to the Chimp (PS3) f +2081 wlclashpcam \N War Leaders: Clash of Nations Automatch (PC) f +2080 wlclashpc qcU8MT War Leaders: Clash of Nations (PC) f +2082 wlclashpcd \N War Leaders: Clash of Nations Demo (PC) f +2086 heistpcd \N Heist Demo (PC) f +2083 bomberman20ds JZ2s7T Bomberman 2.0 (DS) f +2084 heistpc BLhZD9 Heist (PC) f +2088 heistps3am \N Heist Automatch (PS3) f +2087 heistps3 BLhZD9 Heist (PS3) f +2091 bstrikeotspcd \N Battlestrike: Operation Thunderstorm Demo (PC) f +2089 bstrikeotspc 7vRSBa Battlestrike: Operation Thunderstorm (PC) f +2097 gta4ps3grmam \N Grand Theft Auto 4 German Automatch (PS3) f +2092 Decathletesds ry7e63 Decathletes (DS) f +2093 mleatingwii lcUrQg Major League Eating: The Game (EU/US) (WiiWare) f +2094 cc3kwcd TPLstA Command and Conquer 3 Kanes Wrath CD Key Auth f +2095 cc3kwcdam TPLstA Command and Conquer 3 Kanes Wrath CD Key Auth Automatch f +2096 gta4ps3grm t3nTru Grand Theft Auto 4 German (PS3) f +2099 gta4xgrmam \N Grand Theft Auto 4 German Automatch (Xbox 360) f +2098 gta4xgrm t3nTru Grand Theft Auto 4 German (Xbox 360) f +2111 srow2pcam \N Saint's Row 2 Automatch (PC) f +2100 cc3tibwarscd E4F3HB Command & Conquer 3: Tiberium Wars CD Key Auth f +2102 arkaUSEUds kORtxH Arkanoid DS (US/EU) (DS) f +2103 madden09ds yOuECC Madden NFL 09 (DS) f +2104 tetpartywii IZyry6 Tetris Party (WiiWare) f +2105 frontlinesfow ZEmOuj Frontlines: Fuel of War f +2106 simspartywii fZ3lCM MySims Party (Wii) f +2107 momotaro20ds gPy2cd Momotaro Dentetsu 20 Shuunen (DS) f +2108 srow2ps3 iWKxFZ Saint's Row 2 (PS3) f +2109 srow2ps3am iWKxFZ Saint's Row 2 Automatch f +2110 srow2pc odvimE Saint's Row 2 (PC) f +2113 srow2xb360am \N Saint's Row 2 Automatch (Xbox 360) f +2112 srow2xb360 XfwkNR Saint's Row 2 (Xbox 360) f +2116 aliencrashwii gRfsiO Alien Crash (WiiWare) f +2012 worldshiftpcbam \N WorldShift Beta Automatch (PC) f +2194 civ4xp3 lgNJU7 Civilization IV: 3rd Expansion f +2195 civ4xp3am lgNJU7 Civilization IV: 3rd Expansion Automatch f +2196 civ4xp3d lgNJU7 Civilization IV: 3rd Expansion Demo f +2208 crysiswars zKbZiM Crysis Wars f +2213 civ4colpc 2yheDS Sid Meier's Civilization IV: Colonization (PC/Mac) f +2118 legendarypc WUp2J6 Legendary (PC) f +2129 redalert3pcam \N Red Alert 3 Automatch (PC) f +2121 megaman9wii r6PBov Mega Man 9 (WiiWare) f +2122 dqmonjoker2ds MXsuQS Dragon Quest Monsters: Joker 2 (DS) f +2123 quizmagicds zi1Kob Quiz Magic Academy DS (DS) f +2124 Narutonin2ds c0DRrn Naruto: Path of the Ninja 2 (DS) f +2126 mfcoachwii 9q49yB My Fitness Coach (Wii) f +2127 othellods VrRUK1 Othello de Othello DS f +2128 redalert3pc uBZwpf Red Alert 3 (PC) f +2130 redalert3pcd \N Red Alert 3 Demo (PC) f +2132 redalert3ps3am \N Red Alert 3 Automatch (PS3) f +2131 redalert3ps3 uBZwpf Red Alert 3 (PS3) f +2134 plunderps3am \N Plunder Automatch (PS3) f +2133 plunderps3 RbMD9p Age of Booty (PS3) f +2135 plunderps3d \N Plunder Demo (PS3) f +2140 swbf3pspam \N Star Wars: Battlefront 3 Automatch (PSP) f +2136 svsr09ps3 Pzhfov WWE Smackdown vs. RAW 2009 (PS3) f +2137 wordjongFRds XAIqLK Word Jong - FR (DS) f +2139 swbf3psp U8yNSx Star Wars: Battlefront 3 (PSP) f +2155 tpfolEUpcBam \N Turning Point: Fall of Liberty Automatch (EU-B) (PC) f +2141 CMwrldkitwii 24vlFy Cooking Mama: World Kitchen (Wii) f +2142 shootantowii nsgl14 Shootanto (Wii) f +2143 punchoutwii yJz8h0 Punch-Out!! (Wii) f +2145 ultibandwii F8KfNf Ultimate Band (Wii) f +2146 CVjudgmentwii 9te6Ua Castlevania: Judgment (Wii) f +2147 ageofconanb QZQLGt Age of Conan beta f +2148 wrldgoowii Nz4tSw World of Goo (WiiWare) f +2149 saadtestam 1a2B3c SaadsTest f +2152 scotttestam RPzJL7 Scott's test gamename Automatch f +2153 testam Hku6Fd Test Automatch f +2154 tpfolEUpcB ltSs4H Turning Point: Fall of Liberty (EU-B) (PC) f +2156 tpfolEUpcBd \N Turning Point: Fall of Liberty Demo (EU-B) (PC) f +2158 tpfolpcBam \N Turning Point: Fall of Liberty Automatch (B) (PC) f +2157 tpfolpcB svJqvE Turning Point: Fall of Liberty (B) (PC) f +2159 tpfolpcBd \N Turning Point: Fall of Liberty Demo (B) (PC) f +2164 swbfront3pcCam \N Star Wars Battlefront 3 Automatch (PS3) f +2160 cc3arenapc gE7WcR Command & Conquer: Arena f +2161 cc3arenapcam gE7WcR Command & Conquer: Arena Automatch f +2162 cc3arenapcd gE7WcR Command & Conquer: Arena Demo f +2166 coh2pcam \N Code of Honor 2 Automatch (PC) f +2165 coh2pc J4b95X Code of Honor 2 (PC) f +2168 dimensitypcam \N Dimensity Automatch (PC) f +2167 dimensitypc ZTcB4o Dimensity (PC) f +2169 dimensitypcd \N Dimensity Demo (PC) f +2175 srow2ps3dam \N Saint's Row 2 Automatch (PS3) Demo f +2170 gta4ps3test t3nTru Grand Theft Auto 4 Test (PS3) f +2171 50centsandps3 ORydHB 50 Cent: Blood on the Sand (PS3) f +2173 locksquestds 30bDMu Construction Combat: Lock's Quest f +2174 srow2ps3d iWKxFZ Saint's Row 2 (PS3) Demo f +2180 reichpcam \N Reich Automatch (PC) f +2176 nobuyabou2ds izfHO0 Nobunaga no Yabou DS 2 (DS) f +2177 memansf2EUDS ZqlkTy Mega Man Star Force 2: Zerker x Shinobi / Saurian (EU) f +2178 bleach2EUds B0veR8 Bleach DS 2: Requiem in the black robe (EU) (DS) f +2179 reichpc bWwGZn Reich (PC) f +2182 reichps3am \N Reich Automatch (PS3) Clone f +2181 reichps3 bWwGZn Reich (PS3) f +2185 saspcam \N SAS Automatch (PC) f +2183 gloftpokerwii NtKG3P Gameloft Poker (WiiWare) f +2184 saspc fewePZ SAS (PC) f +2189 poriginps3am \N Fear 2: Project Origin Automatch (PS3) f +2187 acrossingwii Z7Fm9K Animal Crossing Wii (Wii) f +2188 poriginps3 Rl6qAT Fear 2: Project Origin (PS3) f +2190 poriginps3d \N Fear 2: Project Origin Demo (PS3) f +2192 poriginpcam \N Fear 2: Project Origin Automatch (PC) f +2191 poriginpc yAnB97 Fear 2: Project Origin (PC) f +2210 menofwarpcam \N Men of War Automatch (PC) f +2197 necrovision sgcRRY NecroVision f +2198 tecmoblkickds wdh5FE Tecmo Bowl Kickoff (DS) f +2199 bballarenaps3 W8bW5s Supersonic Acrobatic Rocket-Powered BattleCars: BattleBall Arena f +2201 ragunonlineds DBDrfl Ragunaroku Online DS (DS) f +2202 mlb2k9ds 8Z34m5 Major League Baseball 2K9 Fantasy All-Stars (DS) f +2203 kororinpa2wii aWybXG Kororinpa 2 (Wii) f +2204 stlprincessds M1LDrU Steal Princess (DS) f +2205 aoemythds a9EK9F Age of Empires: Mythologies (DS) f +2206 raymanRR3wii QDgvIN Rayman Raving Rabbids 3 (Wii) f +2207 pitcrewwii M2FFnb Pit Crew Panic (WiiWare) f +2209 menofwarpc KrMW4d Men of War (PC) f +2214 civ4colpcam \N Sid Meier's Civilization IV: Colonization Automatch (PC) f +2211 sakatsukuds OwGy5R Sakatsuku DS (DS) f +2212 wtrwarfarewii R3zu4t Water Warfare (WiiWare) f +2215 civ4colpcd \N Sid Meier's Civilization IV: Colonization Demo (PC) f +2221 hail2chimps3d \N Hail to the Chimp Demo (PS3) f +2216 tablegamestds Tk2MJq Table Game Stadium (D3-Yuki) (Wii) f +2217 ppkpocket11ds cstLhz PowerPro-kun Pocket 11 (DS) f +2218 bleach2wii hTswOX BLEACH Wii2 (Wii) f +2219 cuesportswii rNBacr Cue Sports (WiiWare) f +2119 legendarypcam \N Legendary Automatch (PC) f +2223 bgeverwii zDalN6 Best Game Ever (WiiWare) f +2224 spinvgewii qbD03S Space Invaders: Get Even (WiiWare) f +2225 tstgme gkWzAc test game f +2226 TerroristT2 VSlLZK Terrorist Takedown 2 f +2227 pokemonplatds IIup73 Pokemon Platinum (DS) f +2229 redalert3pcdmb uBZwpf Red Alert 3 Demo (PC) Match Broadcast f +2230 jbond08wii 50hs18 James Bond 2008 (Wii) f +2231 assultwii 6AStwk Assult (Wii) f +2232 mmartracewii NCAq3G Mega Mart Race (WiiWare) f +2233 fifa09ds 5VxqMN FIFA 09 Soccer (DS) f +2234 bboarderswii Z5pkm2 Battle Boarders (WiiWare) f +2235 cellfactorpsn gkVTh8 CellFactor: Ignition (PSN) f +2237 witcher OaHhFk The Witcher f +2238 redalert3pcb uBZwpf Red Alert 3 Beta (PC) f +2242 redalert3pcdam \N Red Alert 3 Demo Automatch (PC) f +2241 redalert3pcbmb uBZwpf Red Alert 3 Beta (PC) Match Broadcast f +2249 mkvsdcps3am \N Mortal Kombat vs. DC Universe Automatch (PS3) f +2243 opblitz gOlcku Operation Blitzsturm f +2244 shogiwii AtQf1n Shogi (Wii) (WiiWare) f +2245 redfactionwii OyLhJI Red Faction Wii (Wii) f +2246 ryantest RakTQT Ryan'st test gamename f +2247 ryantestam RakTQT Ryan'st test gamename Automatch f +2251 mkvsdcxboxam \N Mortal Kombat vs. DC Universe Automatch (Xbox) f +2250 mkvsdcxbox XqrAqV Mortal Kombat vs. DC Universe (Xbox) f +2260 kingtigerspcam \N King Tigers Automatch (PC) f +2252 blzrdriverds n9HLOG Blazer Drive (DS) f +2253 micchannelwii wkvBfX Mic Chat Channel (Wii) f +2255 jnglspeedwii sPCqp8 Jungle Speed (WiiWare) f +2259 kingtigerspc E4hD2t King Tigers (PC) f +2261 kingtigerspcd \N King Tigers Demo (PC) f +2263 hail2chimps3ram \N Hail to the Chimp Retail Automatch (PS3) f +2262 hail2chimps3r mDeBg3 Hail to the Chimp Retail (PS3) f +2265 stalkercsam \N Stalker: Clear Sky Automatch (PC) f +2264 stalkercs PQ7tFU Stalker: Clear Sky (PC) f +2266 stalkercsd \N Stalker: Clear Sky Demo (PC) f +2275 ut3jpps3am \N Unreal Tournament 3 Japanese Automatch (PS3) f +2267 gradiusrbwii ZXCd6z Gradius ReBirth (WiiWare) f +2268 radiohitzwii NzBSQr Radiohitz: Guess That Song! (WiiWare) f +2270 fstarzerods knJOIz Fantasy Star ZERO (DS) f +2271 igowii ikN1qM Igo (Wii) (WiiWare) f +2272 bokujyomonds aydHX0 Bokujyo Monogatari Youkoso! Kaze no Bazzare (DS) f +2273 hotncoldds ngPcan Hot 'n' Cold (DS) f +2274 ut3jpps3 nT2Mtz Unreal Tournament 3 Japanese (PS3) f +2277 ut3jppcam \N Unreal Tournament 3 Japanese Automatch (PC) f +2276 ut3jppc nT2Mtz Unreal Tournament 3 Japanese (PC) f +2279 AliensCMPCam \N Aliens: Colonial Marines Automatch (PC) f +2278 AliensCMPC 5T4ATR Aliens: Colonial Marines (PC) f +2280 AliensCMPCd \N Aliens: Colonial Marines Demo (PC) f +2282 AliensCMPS3am \N Aliens: Colonial Marines Automatch (PS3) f +2281 AliensCMPS3 5T4ATR Aliens: Colonial Marines (PS3) f +2283 AliensCMPS3d \N Aliens: Colonial Marines Demo (PS3) f +2285 Majesty2PCam \N Majesty 2 Automatch (PC) f +2284 Majesty2PC aKwmX5 Majesty 2 (PC) f +2286 Majesty2PCd \N Majesty 2 Demo (PC) f +2289 FlockPCd \N Flock Demo (PC) f +2287 FlockPC 84z6J4 Flock (PC) f +2293 FlockPSNd \N Flock Demo (PSN) f +2290 FlockPSN 84z6J4 Flock (PSN) f +2294 FlockPSNam \N Flock Automatch (PSN) f +2299 wormspspam \N Worms Automatch (PSP) f +2295 bbobblewii IdTzGr Bubble Bobble Wii (WiiWare) f +2296 cellfactorpc gkVTh8 CellFactor: Ignition (PC) f +2298 wormspsp mpCK9u Worms (PSP) f +2301 MotoGP08PCam \N MotoGP08 Automatch (PC) f +2300 MotoGP08PC ZvH4b3 MotoGP08 (PC) f +2303 MotoGP08PS3am \N MotoGP08 Automatch (PS3) f +2302 MotoGP08PS3 ZvH4b3 MotoGP08 (PS3) f +2314 neopetspapcam \N Neopets Puzzle Adventure Automatch (PC) f +2304 sonicbkwii 1SWPIm Sonic and the Black Knight (Wii) f +2305 ghero4wii xcJsPA Guitar Hero 4 (Wii) f +2306 digichampKRds KbWB9w Digimon Championship (KOR) (DS) f +2309 mswinterds uyEG4g Mario & Sonic at the Olympic Winter Games (DS) f +2310 fmasterwtwii 07pDGe Fishing Master: World Tour (Wii) f +2311 starpballwii oDN3tk Starship Pinball (WiiWare) f +2312 nfsmwucoverds qxwQMf Need for Speed Most Wanted: Undercover (DS) f +2313 neopetspapc MOEXUs Neopets Puzzle Adventure (PC) f +2315 neopetspapcd \N Neopets Puzzle Adventure Demo (PC) f +2326 ufc09ps3am \N UFC 2009 Automatch (PS3) f +2316 luminarc2USds 9kIcv6 Luminous Arc 2 Will (US) (DS) f +2318 monkmayhemwii wMe9tQ Maniac Monkey Mayhem (WiiWare) f +2319 takoronKRwii N5yalP Takoron (KOR) (Wii) f +2321 kaosmpr 6cQWlD Kaos MPR f +2322 kaosmpram 6cQWlD Kaos MPR Automatch f +2323 kaosmprd 6cQWlD Kaos MPR Demo f +2324 mcdcrewds 8qTI8b McDonald's DS Crew Development Program (DS) f +2325 ufc09ps3 DCLItd UFC 2009 (PS3) f +2327 ufc09ps3d \N UFC 2009 Demo (PS3) f +2329 ufc09x360am \N UFC 2009 Automatch (Xbox 360) f +2328 ufc09x360 Fuf44V UFC 2009 (Xbox 360) f +2330 ufc09x360d \N UFC 2009 Demo (Xbox 360) f +2331 skateitds eOpDft Skate It (DS) f +2332 robolypsewii qy0bIP Robocalypse (WiiWare) f +2333 puffinsds ckiZ8C Puffins: Island Adventures (DS) f +2420 sawps3 HtiBX3 SAW (PS3) f +2239 redalert3pcbam \N Red Alert 3 Beta (PC) Automatch f +2337 wwpuzzlewii lWG4l1 Simple: The Number - Puzzle f +2338 snightxds sJZwCL Summon Night X (DS) f +2339 hotrodwii L0kIVL High Voltage Hod Rod Show (WiiWare) f +2357 fuelpcam \N FUEL Automatch (PC) f +2341 spbobbleds OFA2iI Space Puzzle Bobble (DS) f +2343 bbarenaEUps3am w6gFKv Supersonic Acrobatic Rocket-Powered BattleCars Automatch (PSN) (EU) f +2344 bbarenaJPNps3 CwiTIz Supersonic Acrobatic Rocket-Powered BattleCars (PSN) (JPN) f +2345 bbarenaJPNps3am CwiTIz Supersonic Acrobatic Rocket-Powered BattleCars Automatch (PSN) (JPN) f +2346 girlssecEUds nySkKx Winx Club Secret Diary 2009 (EU) (DS) f +2347 ffccechods qO9rGZ Final Fantasy Crystal Chronicles: Echos of Time (Wii/DS) f +2348 unbballswii lZTqHE Unbelievaballs (Wii) f +2349 hokutokenwii dRn94f Hokuto no Ken (WiiWare) f +2350 monracersds Uo295H Monster Racers (DS) f +2351 tokyoparkwii 4Fx0VT Tokyo Friend Park II Wii (Wii) f +2352 derbydogwii I8HL3T Derby Dog (WiiWare) f +2354 bbarenaJPps3d CwiTIz Supersonic Acrobatic Rocket-Powered BattleCars Demo (PSN) (JPN) f +2355 ballarenaps3d W8bW5s Supersonic Acrobatic Rocket-Powered BattleCars: BattleBall Arena Demo f +2356 fuelpc UOXvsa FUEL (PC) f +2360 fuelps3am \N FUEL Automatch (PS3) f +2358 fuelpcd UOXvsa FUEL Demo (PC) f +2359 fuelps3 T8IuLe FUEL (PS3) f +2363 mleatingJPwiiam \N Major League Eating: The Game Automatch (JPN) (WiiWare) f +2361 fuelps3d T8IuLe FUEL Demo (PS3) f +2362 mleatingJPwii 4T0Tcg Major League Eating: The Game (JPN) (WiiWare) f +2366 sbkUSps3am \N SBK '08 Automatch (US) (PS3) f +2364 octoEUwii vRdiaE Octomania (EU) (Wii) f +2365 sbkUSps3 q8Bupt SBK '08 (US) (PS3) f +2367 sbkUSps3d \N SBK '08 Demo (US) (PS3) f +2369 sbkUSpcam \N SBK '08 Automatch (US) (PC) f +2368 sbkUSpc 6Q9XqQ SBK '08 (US) (PC) f +2370 sbkUSpcd \N SBK '08 Demo (US) (PC) f +2375 redalert3pccdam \N Red Alert 3 Automatch (PC, CDKey) f +2371 medarotds n8UPyi MedaRot DS (DS) f +2373 ekorisu2ds FzODdr Ekorisu 2 (DS) f +2374 redalert3pccd uBZwpf Red Alert 3 (PC, CDKey) f +2381 legofwreps3am \N WWE Legends of Wrestlemania Automatch (PS3) f +2376 bbladeds 7TUkXB Bay Blade (DS) f +2379 texasholdwii PEsKhp Texas Hold'em Tournament (WiiWare) f +2380 legofwreps3 PyTirM WWE Legends of Wrestlemania (PS3) f +2383 legofwrex360am \N Legends of Wrestlemania Automatch (Xbox 360) f +2382 legofwrex360 VuPdJX WWE Legends of Wrestlemania (Xbox 360) f +2396 mkvsdcEUps3am \N Mortal Kombat vs. DC Universe Automatch (EU) (PS3) f +2384 warnbriads mmOyeL Warnbria no Maho Kagaku (DS) f +2385 tsurimasterds BM8WEh Mezase!! Tsuri Master DS (DS) f +2386 jikkyonextwii 6WH0CV Jikkyo Powerful Pro Yakyu NEXT (Wii) f +2387 mua2wii QizM3A Marvel Ultimate Alliance 2: Fusion (Wii) f +2388 civrevasiaps3 xUfwlE Civilization Revolution (Asia) (PS3) f +2391 pocketwrldds 7Lx2fU My Pocket World (DS) f +2393 segaracingwii 4ue8Ke Sega Superstars Racing (Wii) f +2394 3dpicrossds uhXkFl 3D Picross (DS) f +2395 mkvsdcEUps3 r7TauG Mortal Kombat vs. DC Universe (EU) (PS3) f +2397 mkvsdcEUps3b \N Mortal Kombat vs. DC Universe Beta (EU) (PS3) f +2399 mkvsdcps3bam \N Mortal Kombat vs. DC Universe Beta Automatch (PS3) f +2398 mkvsdcps3b XqrAqV Mortal Kombat vs. DC Universe Beta (PS3) f +2411 im1pcam \N Interstellar Marines Automatch (PC) f +2400 liightwii VveRkG Liight (WiiWare) f +2401 mogumonwii yKTavT Tataite! Mogumon (WiiWare) f +2403 mini4wdds XMGZia Mini 4WD DS (DS) f +2404 puzzshangwii 33NTu6 Puzzle Games Shanghai Wii (WiiWare) f +2405 crystalw1wii U9J7QC Crystal - Defender W1 (WiiWare) f +2406 crystalw2wii AqCvfz Crystal - Defender W2 (WiiWare) f +2407 overturnwii GLXJR8 Overturn (WiiWare) f +2408 vtennisacewii ptSNgI Virtua Tennis: Ace (Wii) f +2409 yugioh5dds lwG6md Yu-Gi-Oh 5Ds (DS) f +2416 50ctsndlvps3am \N 50 Cent: Blood on the Sand - Low Violence Automatch (PS3) f +2412 im1pcd uRd8zg Interstellar Marines Demo (PC) f +2413 civrevasips3d xUfwlE Civilization Revolution Demo (Asia) (PS3) f +2414 civrevoasiads QXhNdz Sid Meier's Civilization Revolution (DS, Asia) f +2418 sawpcam \N SAW Automatch (PC) f +2417 sawpc ik9k6R SAW (PC) f +2421 sawps3am \N SAW Automatch (PS3) f +2422 sawps3d \N SAW Demo (PS3) f +2427 biahhJPps3am \N Brothers In Arms: Hell's Highway Automatch (PS3) (JPN) f +2423 ssmahjongwii r0AyOn Simple Series: The Mah-Jong (WiiWare) f +2424 carnivalkwii iTuqoN Carnival King (WiiWare) f +2425 pubdartswii nzFWQs Pub Darts (WiiWare) f +2426 biahhJPps3 oAEUPB Brothers In Arms: Hell's Highway (PS3) (JPN) f +2428 biahhJPps3d \N Brothers In Arms: Hell's Highway Demo (PS3) (JPN) f +2435 cnpanzers2cwbam \N Codename Panzers 2: Cold Wars BETA Automatch (PC) f +2429 codwawbeta iqEFLl Call of Duty: World at War Beta f +2430 fallout3 iFYnef Fallout 3 f +2431 taprace xJRENu Tap Race (iPhone Sample) f +2433 callofduty5 VycTat Call of Duty 5 f +2434 cnpanzers2cwb uazO6l Codename Panzers 2: Cold Wars BETA (PC) f +2438 biahhPRps3d \N Brothers In Arms: Hell's Highway Demo (PS3) (RUS) f +2436 biahhPRps3 hWpJhQ Brothers In Arms: Hell's Highway (PS3) (RUS) f +2643 pokedngnwii AKikuw Pokemon Dungeon (Wii) f +2340 hotrodwiiam \N High Voltage Hod Rod Show Automatch (WiiWare) f +2441 biahhRUSpc dhkWdE Brothers In Arms: Hell's Highway (PC) (RUS) f +2444 stormrisepcam \N Stormrise Automatch (PC) f +2443 stormrisepc 6zyt4S Stormrise (PC) f +2445 stormrisepcd \N Stormrise Demo (PC) f +2452 psyintdevpcam \N Psyonix Internal Development Automatch (PC) f +2446 stlprinKORds fufc4q Steal Princess (KOR) (DS) f +2447 kaiwanowads NelyD3 KAIWANOWA (DS) f +2448 mvsdk25ds ko7R42 Mario vs Donkey Kong 2.5 (DS) f +2449 stlprinEUds 7SnIvW Steal Princess (EU) (DS) f +2450 gh4metalwii m8snqf Guitar Hero 4: Metallica (Wii) f +2451 psyintdevpc u6vgFK Psyonix Internal Development (PC) f +2453 psyintdevpcd \N Psyonix Internal Development Demo (PC) f +2459 menofwarpcbam \N Men of War Automatch (PC) BETA f +2454 simsracingds iRs4Ck MySims Racing DS (DS) f +2456 evaspacewii m5yEnm Evasive Space (WiiWare) f +2457 spaceremixds Gs1FlI Space Invaders Extreme Remix (DS) f +2458 menofwarpcb mER2kk Men of War (PC) BETA f +2466 srgakuendsam \N Super Robot Gakuen Automatch (DS) f +2460 codwaw LdlpcA Call of Duty: World at War f +2461 kentomashods feZytn Ide Yohei no Kento Masho DS (DS) f +2462 beatrunnerwii CAI5ov Beat Runner (WiiWare) f +2464 rainbowislwii TpzO6m Rainbow Island Tower! (WiiWare) f +2465 srgakuends cK86go Super Robot Gakuen (DS) f +2470 mxravenpspam \N MX Raven Automatch (PSP) f +2467 cstaisends 0LTCe3 Chotto Sujin Taisen (DS) f +2468 winx2010ds JP9RGe Winx Club Secret Diary 2010 (DS) f +2469 mxravenpsp FPpfou MX Reflex (Raven) (PSP) f +2479 guinnesswripham \N Guinness World Records: The Video Game Automatch (iPhone) f +2471 sukashikds uk8Nda Sukashikashipanman DS (DS) f +2472 famista09ds OkJhLi Pro Yakyu Famista DS 2009 (DS) f +2473 hawxpc h6dGAg Tom Clancy's HAWX f +2474 fxtrainingds tXkDai FX Training DS (DS) f +2475 monhuntergwii hyCk2c Monster Hunter G (Wii) f +2476 dinerdashwii 4rTdD2 Diner Dash (WiiWare) f +2477 s_l4d sPEFlr Steam Left 4 Dead f +2480 guinnesswriphd \N Guinness World Records: The Video Game Demo (iPhone) f +2484 biahhPOLps3am \N Brothers In Arms: Hell's Highway Automatch (PS3) (POL) f +2481 konsportswii sJEymO Konami Sports Club @ Home (WiiWare) f +2482 cpenguin2ds 1XafMv Club Penguin 2 (DS) f +2483 biahhPOLps3 zEo2mk Brothers In Arms: Hell's Highway (PS3) (POL) f +2485 biahhPOLps3d \N Brothers In Arms: Hell's Highway Demo (PS3) (POL) f +2490 h2cdigitalps3d \N Hail to the Chimp Demo (PSN) f +2486 exciteracewii WLrMtU Excite Racing (Wii) f +2487 cpenguin2wii Nu3Uqi Club Penguin 2 (Wii) f +2488 tcounterwii HzKrFV Tecmo Counter f +2492 motogp09ps3am \N Moto GP 09 Automatch (PS3) f +2491 motogp09ps3 nQF5x3 Moto GP 09 (PS3) f +2493 motogp09ps3d \N Moto GP 09 Demo (PS3) f +2495 motogp09pcam \N Moto GP 09 Automatch (PC) f +2494 motogp09pc qOspfz Moto GP 09 (PC) f +2496 motogp09pcd \N Moto GP 09 Demo (PC) f +2497 spectro2wii KgKm2x Spectrobes 2 (Wii) f +2499 ninTest/am EdD7Ve Nintendo Development Testing masterID 0 Automatch f +2500 ninTest0 EdD7Ve Nintendo Development Testing masterID 1 f +2501 ninTest0am EdD7Ve Nintendo Development Testing masterID 1 Automatch f +2502 ninTest1 EdD7Ve Nintendo Development Testing masterID 2 f +2503 ninTest1am EdD7Ve Nintendo Development Testing masterID 2 Automatch f +2504 ninTest2 EdD7Ve Nintendo Development Testing masterID 3 f +2506 ninTest3 EdD7Ve Nintendo Development Testing masterID 4 f +2507 ninTest3am EdD7Ve Nintendo Development Testing masterID 4 Automatch f +2508 ninTest4 EdD7Ve Nintendo Development Testing masterID 5 f +2509 ninTest4am EdD7Ve Nintendo Development Testing masterID 5 Automatch f +2510 ninTest5 EdD7Ve Nintendo Development Testing masterID 6 f +2511 ninTest5am EdD7Ve Nintendo Development Testing masterID 6 Automatch f +2512 ninTest6 EdD7Ve Nintendo Development Testing masterID 7 f +2514 ninTest7 EdD7Ve Nintendo Development Testing masterID 8 f +2515 ninTest7am EdD7Ve Nintendo Development Testing masterID 8 Automatch f +2516 ninTest8 EdD7Ve Nintendo Development Testing masterID 9 f +2517 ninTest8am EdD7Ve Nintendo Development Testing masterID 9 Automatch f +2518 ninTest9 EdD7Ve Nintendo Development Testing masterID 10 f +2519 ninTest9am EdD7Ve Nintendo Development Testing masterID 10 Automatch f +2521 ninTest:am EdD7Ve Nintendo Development Testing masterID 11 Automatch f +2522 ninTest; EdD7Ve Nintendo Development Testing masterID 12 f +2524 ninTest< EdD7Ve Nintendo Development Testing masterID 13 f +2525 ninTest EdD7Ve Nintendo Development Testing masterID 15 f +2529 ninTest>am EdD7Ve Nintendo Development Testing masterID 15 Automatch f +2530 ninTest? EdD7Ve Nintendo Development Testing masterID 16 f +2531 ninTest?am EdD7Ve Nintendo Development Testing masterID 16 Automatch f +2532 ninTest@ EdD7Ve Nintendo Development Testing masterID 17 f +2534 ninTest- EdD7Ve Nintendo Development Testing masterID 18 f +2535 ninTest-am EdD7Ve Nintendo Development Testing masterID 18 Automatch f +2603 brigades nUAsKm Gamespy Brigades f +2442 biahhRUSpcam \N Brothers In Arms: Hell's Highway Automatch (PC) (RUS) f +2578 civ4coljp 5wddmt Sid Meier's Civilization IV: Colonization (PC Japanese) f +2537 ninTest.am EdD7Ve Nintendo Development Testing masterID 19 Automatch f +2538 dartspartywii xyHrNT Darts Wii Party (Wii) f +2539 3celsiuswii xR1sEX 3* Celsius (WiiWare) f +2541 Rabgohomewii sngh8x Rabbids Go Home (Wii) f +2542 tmntsmashwii IXIdNe TMNT Smash Up (Wii) f +2543 simplejudowii t4wmAP Simple The Ju-Do (WiiWare) f +2544 menofwarpcd z4L7mK Men of War MP DEMO (PC) f +2548 rdr2ps3am \N Red Dead Redemption Automatch (PS3) f +2547 rdr2ps3 5aL4Db Red Dead Redemption (PS3) f +2550 gh4vhalenwiiam \N Guitar Hero 4: Van Halen Automatch (Wii) f +2549 gh4vhalenwii yDGso1 Guitar Hero 4: Van Halen (Wii) f +2558 sbk09ps3am \N SBK '09 Automatch (PS3) f +2551 escviruswii gWke73 Escape Virus (WiiWare) f +2552 rfactoryKRds dBJ0km Rune Factory: A Fantasy Harverst Moon (KOR) (DS) f +2553 banburadxds 04cR2B Banbura DX Photo Frame Radio (DS) f +2554 mebiuswii T0zyn9 Mebius Drive (WiiWare) f +2556 sbk09pc pQAyX6 SBK '09 (PC) f +2557 sbk09ps3 hxVmss SBK '09 (PS3) f +2559 sbk09pcam \N SBK '09 Automatch (PC) f +2561 poriginpcjpam \N Fear 2: Project Origin Automatch (JP) (PC) f +2560 poriginpcjp w2OQ5I Fear 2: Project Origin (JP) (PC) f +2562 poriginpcjpd \N Fear 2: Project Origin Demo (JP) (PC) f +2565 poriginps3jpd \N Fear 2: Project Origin Demo (JP) (PS3) f +2563 poriginps3jp MF2kB1 Fear 2: Project Origin (JP) (PS3) f +2567 section8pcam \N Section 8 Automatch (PC) f +2566 section8pc 2UMehS Section 8 (PC) f +2568 section8pcd \N Section 8 Demo (PC) f +2570 section8ps3am \N Section 8 Automatch (PS3) f +2569 section8ps3 RGZq4i Section 8 (PS3) f +2571 section8ps3d \N Section 8 Demo (PS3) f +2573 section8x360am \N Section 8 Automatch (Xbox360) f +2572 section8x360 fB8QDw Section 8 (Xbox360) f +2574 section8x360d \N Section 8 Demo (Xbox360) f +2576 buccaneerpcam \N Buccaneer Automatch (PC) f +2575 buccaneerpc vFNtGi Buccaneer (PC) f +2577 buccaneerpcd \N Buccaneer Demo (PC) f +2581 beateratorpspam \N Beaterator Automatch (PSP) f +2580 beateratorpsp VXtdws Beaterator (PSP) f +2582 beateratorpspd \N Beaterator Demo (PSP) f +2586 chesschalwiiam \N Chess Challenge! Automatch (WiiWare) f +2583 sonicrkords PrnrAp Sonic Rush Adventure (KOR) (DS) f +2584 mmadnesswii Ok1Lrl Military Madness (WiiWare) f +2585 chesschalwii EU1zXz Chess Challenge! (WiiWare) f +2595 superv8pcam \N Superstars V8 Racing Automatch (PC) f +2587 narutorev3wii 2bLXrL Naruto Shippuden: Clash of Ninja Revolution 3 (Wii) f +2588 decasport2wii pSFeW6 Deca Sports 2 (Wii) f +2589 suparobods fJgMKq Suparobo Gakuen (DS) f +2590 gh4ghitswii lUHbE5 Guitar Hero 4: Greatest Hits (Wii) f +2591 simsraceEUds HmxBFc MySims Racing DS (EU) (DS) f +2592 blockrushwii LbsgGO Blockrush! (WiiWare) f +2593 simsraceJPNds fN26Ba MySims Racing DS (JPN) (DS) f +2596 superv8pcd \N Superstars V8 Racing Demo (PC) f +2598 superv8ps3am \N Superstars V8 Racing Automatch (PS3) f +2597 superv8ps3 0vzJCz Superstars V8 Racing (PS3) f +2599 superv8ps3d \N Superstars V8 Racing Demo (PS3) f +2610 svsr10ps3d \N WWE Smackdown vs. Raw 2010 Demo (PS3) f +2600 boardgamesds fFgBAt The Best of Board Games (DS) f +2601 cardgamesds 6iGEJe The Best of Card Games (DS) f +2602 colcourseds T9aQ3K Collision Course (DS) f +2605 qsolace MjcwlP Quantum of Solace f +2606 tcendwar wNPcIq Tom Clancy's EndWar f +2607 kidslearnwii ws94sA Kids Learning Desk (WiiWare) f +2608 svsr10ps3 XcUkIx WWE Smackdown vs. Raw 2010 (PS3) f +2612 svsr10x360am \N WWE Smackdown vs. Raw 2010 Automatch (Xbox 360) f +2611 svsr10x360 ONqHu9 WWE Smackdown vs. Raw 2010 (Xbox 360) f +2613 svsr10x360d \N WWE Smackdown vs. Raw 2010 Demo (Xbox 360) f +2616 cardherodsam \N Card Hero DSi Automatch (DS) f +2614 momo2010wii 2lbGXb Momotaro Dentetsu 2010 Nendoban (Wii) f +2615 cardherods FRzL49 Card Hero DSi (DS) f +2618 smball2ipham \N Super Monkey Ball 2 Automatch (iPhone) f +2617 smball2iph cqWhHg Super Monkey Ball 2 (iPhone) f +2619 smball2iphd \N Super Monkey Ball 2 Demo (iPhone) f +2622 beateratoriphd \N Beaterator Demo (iPhone) f +2620 beateratoriph qV4GA6 Beaterator (iPhone) f +2633 bderlandspcam \N Borderlands Automatch (PC) f +2623 conduitwii GTd9OX The Conduit (Wii) f +2624 hookagainwii 7LR7m6 Hooked Again! (Wii) f +2625 rfactory3ds JpHDcA Rune Factory 3 (DS) f +2626 disneydev ZpO4Dp Disney Development/Testing f +2627 disneydevam ZpO4Dp Disney Development/Testing Automatch f +2628 sporearenads mhxKle Spore Hero Arena (DS) f +2629 treasurewldds cKei7w Treasure World (DS) f +2630 unowii 2hUZSq UNO (WiiWare) f +2632 bderlandspc a2Lg16 Borderlands (PC) f +2634 bderlandspcd \N Borderlands Demo (PC) f +2637 bderlandsps3d \N Borderlands Demo (PS3) f +2635 bderlandsps3 Z1kXis Borderlands (PS3) f +2639 bderlands360am \N Borderlands Automatch (360) f +2638 bderlandsx360 1Eu2fy Borderlands (360) f +2640 bderlandsx360d \N Borderlands Demo (360) f +2641 simsportsds Qw1de8 MySims Sports (DS) f +2642 simsportswii T18tBM MySims Sports (Wii) f +2545 menofwarpcdam \N Men of War MP DEMO Automatch (PC) f +2645 arma2pcam zbMmN3 Arma II Automatch (PC) f +2646 arma2pcd zbMmN3 Arma II Demo (PC) f +2648 quizmagic2ds JGqTW6 Quiz Magic Academy DS2 (DS) f +2649 bandbrosEUds WrU6Ov Daiggaso! Band Brothers DX (EU) (DS) f +2650 swsnow2wii 2cPMrL Shaun White Snowboarding 2 (Wii) f +2651 scribnautsds d4dJKr Scribblenauts (DS) f +2652 fifasoc10ds ZULq4H FIFA Soccer 10 (DS) f +2653 foreverbl2wii Ly8iAL Forever Blue 2 (Wii) f +2654 namcotest hNdo7u Namco SDK Test f +2655 namcotestam hNdo7u Namco SDK Test Automatch f +2656 namcotestd hNdo7u Namco SDK Test Demo f +2657 blindpointpc IGbJEs Blind Point (PC) f +2671 beateratoram \N Beaterator Automatch (PSP/iphone) f +2660 propocket12ds 98gFV2 PowerPro-kun Pocket 12 (DS) f +2661 seafarmwii tNQRr7 Seafarm (WiiWare) f +2662 dragquestsds r6ToyA Dragon Quest S (DSiWare) f +2663 dawnheroesds HpsSGM Dawn of Heroes (DS) f +2664 monhunter3wii mO984l Monster Hunter 3 (JPN) (Wii) f +2665 appletest TZHVox Apple SDK test f +2667 appletestd TZHVox Apple SDK test Demo f +2668 harbunkods renLKS Harlequin Bunko (DS) f +2669 unodsi w2G3ae UNO (DSiWare) f +2670 beaterator VXtdws Beaterator (PSP/iphone) f +2672 beateratord \N Beaterator Demo (PSP/iphone) f +2676 ascensionpcam \N Ascension Automatch (PC) f +2674 dragoncrwnwii y4QTvo Dragon's Crown (Wii) f +2675 ascensionpc 1aT6fS Ascension (PC) f +2677 ascensionpcd \N Ascension Demo (PC) f +2680 swbfespspd \N Star Wars: Battlefront - Elite Squadron Demo (PSP) f +2678 swbfespsp wLfbMH Star Wars: Battlefront - Elite Squadron (PSP) f +2693 luchalibrepcam \N Lucha Libre AAA 2010 Automatch (PC) f +2694 luchalibrepcd \N Lucha Libre AAA 2010 Demo (PC) f +2681 nba2k10wii qWpDTI NBA 2K10 (Wii) f +2682 nhl2k10wii UzhSDM NHL 2K10 (Wii) f +2683 mk9test a0GZNV Midway MK9 Test f +2685 mk9testd a0GZNV Midway MK9 Test Demo f +2686 kateifestds kJcEq8 Katei Kyoshi Hitman Reborn DS Vongole Festival Online (DS) f +2687 luminarc2EUds lJsN7I Luminous Arc 2 Will (EU) (DS) f +2688 tatvscapwii eJMWz4 Tatsunoko vs. Capcom Ultimate All Stars (Wii) f +2689 petz09ds kLg8PL Petz Catz/Dogz/Hamsterz/Babiez 2009 (DS) f +2690 rtlwsportswii flKPhR RTL Winter Sports 2010 (Wii) f +2691 tomenasawii r15HmN Tomenasanner (WiiWare) f +2696 luchalibreps3am \N Lucha Libre AAA 2010 Automatch (PS3) f +2697 luchalibreps3d \N Lucha Libre AAA 2010 Demo (PS3) f +2695 luchalibreps3 DNbubV Lucha Libre AAA 2010 (PS3) f +2700 ludicrouspcam \N Ludicrous Automatch (PC) f +2701 ludicrouspcd \N Ludicrous Demo (PC) f +2698 simsflyerswii d5wfc2 MySims Flyers (Wii) f +2699 ludicrouspc JH70r6 Ludicrous (PC) f +2704 ludicrousmacd \N Ludicrous Demo (MAC) f +2713 orderofwarpcam \N Order of War Automatch (PC) f +2702 ludicrousmac P99WDn Ludicrous (MAC) f +2714 orderofwarpcd \N Order of War Demo (PC) f +2705 pbellumr1 CXabGK Parabellum Region 1 (PC) f +2706 pbellumr2 CXabGK Parabellum Region 2 (PC) f +2707 pbellumr3 CXabGK Parabellum Region 3 (PC) f +2708 imaginejdds Co6Ih6 Imagine: Jewelry Designer (DS) f +2709 imagineartds Jb87QW Imagine: Artist (DS) f +2711 sballrevwii emMKr3 Spaceball: Revolution (WiiWare) f +2712 orderofwarpc P8pcV7 Order of War (PC) f +2721 fairyfightps3am \N Fairytale Fights Automatch (PS3) f +2722 fairyfightps3d \N Fairytale Fights Demo (PS3) f +2715 lbookofbigsds zTtFaT Little Book of Big Secrets (DS) f +2716 scribnauteuds 5aGp82 Scribblenauts (EU) (DS) f +2717 buccaneer sAhRTM Buccaneer The Pursuit of Infamy f +2718 kenteitvwii uGRdPx Kentei! TV Wii (Wii) f +2720 fairyfightps3 qTLu9D Fairytale Fights (PS3) f +2724 fairyfightpcam \N Fairytale Fights Automatch (PC) f +2725 fairyfightpcd \N Fairytale Fights Demo (PC) f +2723 fairyfightpc R6JnVy Fairytale Fights (PC) f +2728 50centjpnps3d \N 50 Cent: Blood on the Sand Demo (JPN) (PS3) f +2736 bomberman2wiid \N Bomberman 2 Demo (Wii) f +2726 50centjpnps3 ZmGGQs 50 Cent: Blood on the Sand (JPN) (PS3) f +2744 section8pcbam \N Section 8 Beta Automatch (PC) f +2729 codmw2ds 0DzDcW Call of Duty: Modern Warfare 2 (DS) f +2730 jbond2009ds asL1Wh James Bond 2009 (DS) f +2731 resevildrkwii qBhaV0 Resident Evil: The Darkside Chronicles (Wii) f +2732 musicmakerwii wDFJt2 Music Maker (Wii) f +2733 figlandds eIDvPq Figland (DS) f +2734 bonkwii QeXwBs Bonk (Wii) f +2735 bomberman2wii mWTGGw Bomberman 2 (Wii) f +2745 section8pcbd \N Section 8 Beta Demo (PC) f +2737 dreamchronwii 2Q2ePF Dream Chronicle (Wii) f +2738 gokuidsi yQLxLL Gokui (DSiWare) f +2739 usingwii 6vcnoA U-Sing (Wii) f +2741 puyopuyo7wii h9HtSg Puyopuyo 7 (Wii) f +2742 winelev10wii cZzNkJ Winning Eleven Play Maker 2010 (Wii) f +2743 section8pcb 2UMehS Section 8 Beta (PC) f +2747 ucardgamesds PpmQVg Ultimate Card Games (DS) f +2748 postpetds 126D8H PostPetDS Yumemiru Momo to Fushigi no Pen (DS) f +2749 mfightbbultds v2cC6e Metal Fight Bay Blade ULTIMATE (DS) f +2750 strategistwii sP7muH Strategist (Wii) f +2751 bmbermanexdsi nhQakb Bomberman Express (DSiWare) f +2659 blindpointpcd \N Blind Point Demo (PC) f +2753 rdr2x360 H1Dgd3 Red Dead Redemption (x360) f +2758 fairyfightspcam \N Fairytale Fights Automatch (PC) f +2757 fairyfightspc BqQzb9 Fairytale Fights (PC) f +2759 fairyfightspcd \N Fairytale Fights Demo (PC) f +2762 stalkercoppcd \N STALKER: Call of Pripyat Demo (PC) f +2760 stalkercoppc LTU2z2 STALKER: Call of Pripyat (PC) f +2764 strategistpcam \N The Strategist Automatch (PC) f +2763 strategistpc a3Nydp The Strategist (PC) f +2765 strategistpcd \N The Strategist Demo (PC) f +2767 strategistpsnam \N The Strategist Automatch (PSN) f +2766 strategistpsn Ep4yXH The Strategist (PSN) f +2768 strategistpsnd \N The Strategist Demo (PSN) f +2771 ufc10ps3am \N UFC 2010 Automatch (PS3) f +2769 tataitemogwii qND9s1 Tataite! Mogumon US/EU (WiiWare) f +2770 ufc10ps3 WFpvzz UFC 2010 (PS3) f +2772 ufc10ps3d \N UFC 2010 Demo (PS3) f +2775 ufc10x360d \N UFC 2010 Demo (x360) f +2773 ufc10x360 oEwztT UFC 2010 (x360) f +2784 wormswiiwaream \N Worms Automatch (WiiWare) f +2776 mmtest F24ooQ Matchmaking Backend Test f +2777 mmtestam F24ooQ Matchmaking Backend Test Automatch f +2778 talesofgrawii WEp7vX Tales of Graces (Wii) f +2779 dynamiczanwii JKoAWz Dynamic Zan (Wii) f +2781 idraculawii v1xcTU iDracula (WiiWare) f +2782 metalfightds noSUQC Metal Fight Bayblade (DS) f +2783 wormswiiware nQV5pT Worms (WiiWare) f +2787 gtacwarspspam \N Grand Theft Auto: Chinatown Wars Automatch (PSP) f +2785 justsingds hwg1XV Just Sing! (DS) f +2786 gtacwarspsp UXrDJm Grand Theft Auto: Chinatown Wars (PSP) f +2788 gtacwarspspd \N Grand Theft Auto: Chinatown Wars Demo (PSP) f +2790 gtacwiphoneam \N Grand Theft Auto: Chinatown Wars Automatch (iPhone) f +2789 gtacwiphone 3NQ6vh Grand Theft Auto: Chinatown Wars (iPhone) f +2791 gtacwiphoned \N Grand Theft Auto: Chinatown Wars Demo (iPhone) f +2799 fuelps3ptchdam \N FUEL Automatch (PS3) Patched version f +2792 trkmaniads VzwMkX Trackmania (DS) f +2793 trkmaniawii 9mdZHR Trackmania (Wii) f +2794 megaman10wii th2moV Mega Man 10 (WiiWare) f +2795 aarmy3 zwAbg5 America's Army 3 f +2797 sinpunish2wii B2Tcgk Sin & Punishment 2 (Wii) f +2798 fuelps3ptchd T8IuLe FUEL (PS3) Patched version f +2802 demonforgeps3am \N Demon's Forge Automatch (PS3) f +2800 sonicdlwii DkJwkG Sonic DL (WiiWare) f +2801 demonforgeps3 9Cpt5m Demon's Forge (PS3) f +2803 demonforgeps3d \N Demon's Forge Demo (PS3) f +2806 demonforgepcd \N Demon's Forge Demo (PC) f +2804 demonforgepc XEuc92 Demon's Forge (PC) f +2811 maxpayne3pcam \N Max Payne 3 Automatch (PC) f +2807 hooploopwii 4b2QnG HooperLooper (WiiWare) f +2809 test1 ThAO8k test1 f +2810 maxpayne3pc qyAD44 Max Payne 3 (PC) f +2812 maxpayne3pcd \N Max Payne 3 Demo (PC) f +2814 maxpayne3ps3am \N Max Payne 3 Automatch (PS3) f +2813 maxpayne3ps3 QN8v5P Max Payne 3 (PS3) f +2817 maxpayne3x360am \N Max Payne 3 Automatch (360) f +2816 maxpayne3x360 28xd4T Max Payne 3 (360) f +2818 maxpayne3x360d \N Max Payne 3 Demo (360) f +2832 bädmasterid \N bädmasterid f +2819 wordjongeuds 3rwTkL Wordjong EU (DS) f +2820 sengo3wii Esqv7G Sengokumuso 3 f +2821 bewarewii iTHrhz Beware (WiiWare) f +2822 hinterland FZNxKf Hinterland f +2824 rockstarsclub 2MJPhH Rockstar Social Club f +2825 rockstarsclubam 2MJPhH Rockstar Social Club Automatch f +2826 plandmajinds BThDbL Professor Layton and Majin no Fue (DS) f +2827 powerkoushds nTHkC7 Powerful Koushien (DS) f +2828 cavestorywii tWThgd Cave Story (WiiWare) f +2829 blahblahtest uH88tT Just another test for masterid f +2830 blahtest uH88tT Just another test for masterid f +2831 blahmasterid uH88tT Just another test for masterid f +2833 explomäntest \N blah f +2845 superv8ncpcd \N Superstars V8 Next Challenge Demo (PC) f +2836 3dpicrosseuds UAX3WC 3D Picross (EU) (DS) f +2838 narutor3euwii 64ncJ9 Naruto Shippuden: Clash of Ninja Revolution 3 EU (Wii) f +2840 sparta2pc JfHMee Sparta 2: The Conquest of Alexander the Great (PC) f +2841 sparta2pcam JfHMee Sparta 2: The Conquest of Alexander the Great Automatch (PC) f +2842 sparta2pcd JfHMee Sparta 2: The Conquest of Alexander the Great Demo (PC) f +2843 superv8ncpc 4fKWpe Superstars V8 Next Challenge (PC) f +2847 superv8ncps3am \N Superstars V8 Next Challenge Automatch (PS3) f +2846 superv8ncps3 eLgtAp Superstars V8 Next Challenge (PS3) f +2848 superv8ncps3d \N Superstars V8 Next Challenge Demo (PS3) f +2850 ikaropcam \N Ikaro Automatch (PC) f +2849 ikaropc kG5bEO Ikaro (PC) f +2851 ikaropcd \N Ikaro Demo (PC) f +2853 ufc10ps3DEVam \N UFC 2010 DEV Automatch (PS3-DEV) f +2852 ufc10ps3DEV 2gN8O2 UFC 2010 DEV (PS3-DEV) f +2854 ufc10ps3DEVd \N UFC 2010 DEV Demo (PS3-DEV) f +2856 ufc10x360devam \N UFC 2010 DEV Automatch (360-DEV) f +2855 ufc10x360dev h2SP6e UFC 2010 DEV (360-DEV) f +2857 ufc10x360devd \N UFC 2010 DEV Demo (360-DEV) f +2862 foxtrotpcd \N Foxtrot Demo (PC) f +2858 ragonlinenads k6p7se Ragunaroku Online DS (NA) (DS) f +2859 hoopworldwii mZSW86 Hoopworld (Wii) f +2860 foxtrotpc lTvP98 Foxtrot (PC) f +2863 civ5 kB4qBk Civilization 5 f +2754 rdr2x360am \N Red Dead Redemption Automatch (x360) f +2866 sbkxpc P8ThQm SBK X: Superbike World Championship (PC) f +2868 sbkxpcd \N SBK X: Superbike World Championship Demo (PC) f +2870 sbkxps3am \N SBK X: Superbike World Championship Automatch (PS3) f +2869 sbkxps3 BCvlzO SBK X: Superbike World Championship (PS3) f +2871 sbkxps3d \N SBK X: Superbike World Championship Demo (PS3) f +2879 painkresurrpcam \N Painkiller Resurrection Automatch (PC) f +2872 famista2010ds bdhXZm Famista 2010 (DS) f +2873 bokutwinvilds z9VMe9 Bokujyo Monogatari Twin Village (DS) f +2874 destruction vt3f71 Destruction 101 (Namco Bandai) f +2876 lumark3eyesds 65yvsC Luminous Ark 3 Eyes (DS) f +2877 othellowii uV8aBd Othello (WiiWare) f +2878 painkresurrpc tmQ4wN Painkiller Resurrection (PC) f +2880 painkresurrpcd \N Painkiller Resurrection Demo (PC) f +2884 svsr11ps3am \N Smackdown vs Raw 2011 Automatch (PS3) f +2881 fantcubewii 2wDUcM Fantastic Cube (WiiWare) f +2882 3dpicrossUSds 2IOxzX 3D Picross (US) (DS) f +2885 svsr11ps3d \N Smackdown vs Raw 2011 Demo (PS3) f +2887 svsr11x360am \N Smackdown vs Raw 2011 Automatch (x360) f +2886 svsr11x360 4q9ULG Smackdown vs Raw 2011 (x360) f +2888 svsr11x360d \N Smackdown vs Raw 2011 Demo (x360) f +2890 bderlandruspcam \N Borderlands RUS Automatch (PC) f +2889 bderlandruspc Pe4PcU Borderlands RUS (PC) f +2891 bderlandruspcd \N Borderlands RUS Demo (PC) f +2894 krabbitpcmacd \N KrabbitWorld Origins Demo (PC/Mac) f +2892 krabbitpcmac Jf9OhT KrabbitWorld Origins (PC/Mac) f +2902 lanoireps3am \N L.A. Noire Automatch (PS3) f +2895 gunnylamacwii CeF2yx GUNBLADE NY & L.A. MACHINEGUNS (Wii) f +2896 rbeaverdefwii 6k1gxH Robocalypse - Beaver Defense (WiiWare) f +2897 surkatamarwii TgGSxT Surinukeru Katamari (WiiWare) f +2898 snackdsi zrSxhe Snack (DSiWare) f +2899 rpgtkooldsi NaGK7x RPG tkool DS (DSi) f +2900 mh3uswii IwkoVF Monster Hunter 3 (US/EU) (Wii) f +2901 lanoireps3 yPpSqe L.A. Noire (PS3) f +2903 lanoireps3d \N L.A. Noire Demo (PS3) f +2906 lanoirex360d \N L.A. Noire Demo (x360) f +2904 lanoirex360 fKw37T L.A. Noire (x360) f +2908 lanoirepcam \N L.A. Noire Automatch (PC) f +2907 lanoirepc sx37ex L.A. Noire (PC) f +2909 lanoirepcd \N L.A. Noire Demo (PC) f +2918 necrolcpcam \N NecroVisioN: Lost Company Automatch (PC) f +2910 digimonsleds mB26Li Digimon Story Lost Evolution (DS) f +2911 syachi2ds tXH2sN syachi 2 (DS) f +2912 puzzleqt2ds hMqc5z Puzzle Quest 2 (DS) f +2914 decasport3wii rKsv8q Deca Sports 3 (Wii) f +2915 tetrisdeluxds LEtvxd Tetris Party Deluxe (DSiWare) f +2916 gsiphonefw FaI3pa GameSpy iPhone Framework f +2917 necrolcpc JFKyCM NecroVisioN: Lost Company (PC) f +2919 necrolcpcd \N NecroVisioN: Lost Company Demo (PC) f +2921 startrekmacam \N Star Trek Automatch (MAC) f +2920 startrekmac nbxWDg Star Trek: D-A-C (MAC) f +2929 scribnaut2pcam \N Scribblenauts 2 Automatch (PC) f +2922 captsubasads A738z3 Captain tsubasa (DS) f +2923 cb2ds V47Nu4 CB2 (DS) f +2925 cardiowrk2wii ByKsx6 Cardio Workout 2 (Wii) f +2926 boyvgirlcwii gWFTR4 Boys vs Girls Summer Camp (Wii) f +2927 keenracerswii 9McTZh Keen Racers (WiiWare) f +2928 scribnaut2pc 6P7Qdd Scribblenauts 2 (PC) f +2931 agentps3am \N Agent Automatch (PS3) f +2930 agentps3 8me2Ja Agent (PS3) f +2937 svsr11x360devam \N Smackdown vs Raw 2011 DEV Automatch (x360) f +2932 girlskoreads QiFGmi Girls_Korea (DS) f +2934 protocolwii Hd4g3T Protocol (WiiWare) f +2935 DeathtoSpies LOhgNO Death to Spies f +2936 svsr11x360dev h5DZhP Smackdown vs Raw 2011 DEV (x360) f +2939 svsr11ps3devam \N Smackdown vs Raw 2011 DEV Automatch (PS3) f +2938 svsr11ps3dev gSTArg Smackdown vs Raw 2011 DEV (PS3) f +2942 molecontrolpcam \N Mole Control Automatch (PC) f +2940 dynaztrialwii QyQTgC Dynamic Zan TRIAL (Wii) f +2941 molecontrolpc LqpHUN Mole Control (PC) f +2946 na2rowpcam \N NAT2 Row Automatch (PC) f +2943 sakwcha2010ds a92bdC Sakatsuku DS WorldChallenge 2010 (DS) f +2944 MenofWar AkxMQE Men of War f +2945 na2rowpc mxw6bp NAT2 Row (PC) f +2948 na2runpcam \N NAT2 Run Automatch (PC) f +2947 na2runpc eDCC2L NAT2 Run (PC) f +2959 combatzonepcd \N Combat Zone - Special Forces Demo (PC) f +2949 trackmania2ds iaukpU Trackmania DS 2 (DS) f +2951 mysimsflyerds intJay MySims Flyers (DS) f +2952 mysimsflyEUds AhABRa MySims Flyers EU (DS) f +2953 kodawar2010ds dXZiwq Kodawari Saihai Simulation Ochanoma Pro Yakyu DS 2010 Verison (DS) f +2954 topspin4wii 7AzniN TOPSPIN 4 (Wii) f +2955 ut3onlive 7cxD9c Unreal Tournament 3 ONLIVE f +2956 ut3onliveam 7cxD9c Unreal Tournament 3 ONLIVE Automatch f +2957 combatzonepc 3NncWS Combat Zone - Special Forces (PC) f +2963 crysis2pcd \N Crysis 2 Demo (PC) f +2960 sinpun2NAwii cVXGtt Sin & Punishment 2 NA (Wii) f +2962 capricornam XeS9dz Crysis 2 Automatch (PC) f +2964 crysis2ps3 \N Crysis 2 (PS3) f +2966 crysis2ps3d \N Crysis 2 Demo (PS3) f +2965 crysis2ps3am lhgvHv Crysis 2 Automatch (PS3) f +2967 crysis2x360 \N Crysis 2 (Xbox 360) f +2969 crysis2x360d \N Crysis 2 Demo (Xbox 360) f +2968 crysis2x360am A3Xz9h Crysis 2 Automatch (Xbox 360) f +2867 sbkxpcam \N SBK X: Superbike World Championship Automatch (PC) f +3300 capricorn 8TTq4M Crysis 2 (PC) f +68 railty2 T8nM3z Railroad Tycoon II f +226 rrt2scnd fZDYBN Railroad Tycoon 2: The Second Century f +859 railty3 w4D2Ha Railroad Tycoon 3 f +4 bz2 tGbcNv Battlezone II: Combat Commander f +8 drakan zCt4De Drakan: Order of the Flame f +16 heretic2 2iuCAS Heretic II f +20 quake1 7W7yZz Quake f +26 shogo MQMhRK Shogo: Mobile Armor Division f +30 southpark yoI7mE South Park f +39 nerfarena zEh7ir Nerf ArenaBlast f +44 darkreign2 PwE7Nd Dark Reign 2 f +48 scompany EyzWAv Shadow Company f +53 avp WtGzHr Aliens versus Predator f +60 paintball kCVbAZ Paintball f +67 mech3 z8vRn7 Mech Warrior 3 f +75 irl2000 U7tb4Z Indy Racing League 2000 f +112 jetfighter4 M3pL73 Jet Fighter 4: Fortress America f +123 populoustb qik37G Populous: The Beginning f +134 dominos VFHX8a Hasbro's Dominos f +137 pente NeB26l Hasbro's Pente f +143 civ2tot alVRIq Civilization II: Test of Time f +149 gruntz alVRIq Gruntz f +152 wz2100 kD072v Warzone 2100 f +155 baldursg 3MHCZ8 Baludurs Gate f +160 aowdemo alVRIq Age Of Wonders (Demo) f +1421 smrailroads h32mq8 Sid Meier's Railroads! f +1639 smrailroadsjp h32mq8 Sid Meier's Railroads! Japan f +168 rsurbanops 4nHpA3 Rogue Spear: Urban Ops f +174 rallychamp TKuE2P Mobil1 Rally Championship f +181 sofretail iVn3a3 Soldier of Fortune: Retail f +185 fbackgammon Un3apK Small Rockets Backgammon f +192 virtualpool3 NA3vu0 Virtual Pool 3 f +197 frogger ZIq0wX Hasbro's Frogger f +201 amairtac 8dvSTO Army Men - Air Tactics f +208 hhbball2001 5TN9ag High Heat Baseball 2001 f +215 fltsim98 OU0uKn Microsoft Flight Simulator 98 f +222 eawar MIq1wW European Air War f +229 heroes3arm vPkKya Heroes of Might and Magic f +239 bangdemo Hl31zd Bang! Gunship Elite Demo f +247 bgate2 U9b3an Baldur's Gate II: Shadows of Amn f +253 orb Ykd2D3 O.R.B: Off-World Resource Base f +261 aoe2tcdemo wUhCSC Age of Empires II: The Conquerors Demo f +269 bandw KbEab3 Black and White f +276 insane QxZFex Insane f +281 dtrscdmo p2vPkJ Dirt Track Racing: Sprint f +288 wosin Kd29DX SiN: Wages of Sin f +294 close5 XBOEdl Close Combat 5 f +301 deusex Av3M99 Deus Ex f +305 close5dmo V0tKnY Close Combat 5 Demo f +314 runedemo V5Hm41 Rune Demo f +315 suddenstrike vUhCSB Sudden Strike f +319 stefdemo H28D2r Star Trek: Voyager – Elite Force Demo f +327 q3tademo ek2p7z Team Arena Demo f +333 majestyx wUhCTC Majesty Expansion f +340 botbattles Admg3p Tex Atomics Big Bot Battles f +347 crmgdntdr2k W5Hl31 Carmageddon TDR 2000 f +356 bcommander Nm3aZ9 Star Trek: Bridge Commander f +369 Chat09 xQ7fp2 Chat Group 9 f +374 Chat14 xQ7fp2 Chat Group 14 f +380 Chat20 xQ7fp2 Chat Group 20 f +384 legendsmmbeta 5Kbawl Legends of Might and Magic Beta f +396 moonproject YDXBNE Moon Project f +403 gsbgammon PbZ35N GameSpy Backgammon f +411 leadfootd uNctFb Leadfoot Demo f +416 redlinenet OFek2p Redline Multi-Player Inst f +424 disciples2 tKnYBL Disciples 2 f +431 avpnotgold DLiQwZ Aliens vs. Predator f +437 cueballworld sAJtHo Jimmy White Cueball World f +445 diablo blGjuM Diablo f +446 tetrisworlds D3pQe2 Tetris Worlds f +451 rsblackthorn Gh2W6n Rogue Spear: Black Thorn f +458 americax CSCQMZ America Addon f +461 stef1exp zgsCV2 Star Trek: Voyager - Elite Force expansion pack f +471 legendsmmbeta2 5Kbawl Legends of Might and Magic First Look 2 f +479 sfc2dv k7tEH3 Starfleet Command 2: Empires At War Dynaverse f +488 st_highscore KS3p2Q Stats and Tracking Sample f +497 rallychampx h6nLfY Rally Championship Extrem f +504 kohanagdemo Kbao3a Kohan: Ahrimans Gift Demo f +519 masterrally p5jGg6 Master Rally f +525 mechcomm2 6ajiPV MechCommander 2 f +529 swgbd AGh6nM Star Wars: Galactic Battlegrounds Demo f +539 etherlordsd 6ajiOV Etherlords Demo f +549 strongholdd Rp5kGg Stronghold Demo f +557 racedriver Hl31zd TOCA Race Driver f +562 avp2lv Df3M6Z Aliens vs. Predator 2 (Low violence) f +569 serioussamsed AKbna4 Serious Sam: Second Encounter Demo f +578 redalert2exp eRW78c Command & Conquer: Yuri's Revenge f +579 capitalism2 ihPU0u Capitalism 2 f +586 jk2 6ajhOV Star Wars Jedi Knight II: Jedi Outcast f +2972 cellfacttwpcam 4aN3Pn Cell Factor:TW Automatch (PC) f +2975 winel10jpnwii \N Winning Eleven PLAY MAKER 2010 Japan Edition (Wii) f +2974 firearmsevopcam WrgNsZ Firearms Evolution Automatch (PC) f +2981 harmoon2kords \N Harvest Moon 2 Korea (DS) f +2977 bldragonNAds 1mJhT4 Blue Dragon - Awakened Shadow f +2978 bldragonNAdsam JfXyGi Blue Dragon - Awakened Shadow Automatch f +2979 sonic2010wii JfXyGi SONIC 2010 (Wii) f +2980 sonic2010wiiam LhuHFv SONIC 2010 Automatch (Wii) f +2983 jbondmv2ds \N James Bond Non Movie 2 (2010) (DS) f +2982 harmoon2kordsam Gn1cxG Harvest Moon 2 Korea Automatch (DS) f +2985 casinotourwii \N Casino Tournament (Wii) f +2984 jbondmv2dsam 4AiRCn James Bond Non Movie 2 Automatch (2010) (DS) f +2986 casinotourwiiam WykxqZ Casino Tournament Automatch (Wii) f +2973 firearmsevopc \N Firearms Evolution (PC) f +597 mooncommander ziQwZF Moon Commander f +604 medieval L3d8Sh Medieval: Total War f +613 mobileforces g3H6eR Mobile Forces f +619 mobileforcesd g3H6eR Mobile Forces Demo f +626 survivorm ZDXBOF Survivor: Marquesas f +632 warlordsb2 Gg7nLf Warlords Battlecry II f +638 sumofallfearsd RW78cv The Sum of All Fears Demo f +648 gored k2X9tQ Gore Retail Demo f +653 ww2frontline blHjuM World War II: Frontline Command f +662 sfc3 q3k7xH Starfleet Command III f +667 celtickingsdemo TCQMZI Celtic Kings Demo f +674 th2003d G4i3x7 Trophy Hunter 2003 Demo f +683 dtr2d U4iX9e Dirt Track Racing 2 Demo f +693 banditsd H2k9bD Bandits: Phoenix Rising Demo f +701 mostwanted H3kEn7 Most Wanted f +706 thps5ps2 G2k8cF Tony Hawk's Underground (PS2) f +713 bfield1942rtr HpWx9z Battlefield 1942: Road to Rome f +715 netathlon nYALJv NetAthlon f +716 ccgeneralsb g3T9s2 Command & Conquer: Generals Beta f +724 mech4merc q7zgsC MechWarrior 4: Mercenarie f +732 dhunterps2 G2Qvo9 Deer Hunter (PS2) f +738 il2sturmovikfb h53Ew8 IL-2 Sturmovik Forgotten Battles f +747 nolf2d dHg7w3 No One Lives Forever: The Operative 2 Demo f +757 vietnamsod y3Ed9q Line of Sight: Vietnam Demo f +766 wkingsbd agV5Hm Warrior Kings Battles Demo f +776 homeworld2 t38kc9 Homeworld 2 f +780 stef2 MIr1wX Star Trek: Elite Force II f +788 blitz2004ps2b y3G9dJ NFL Blitz Pro 2004 Beta (PS2) f +796 mclub2pc y6E3c9 Midnight Club 2 (PC) f +802 mohaab y32FDc Medal of Honor: Allied Assault Breakthrough f +810 omfbattleb Abm93d One Must Fall Battlegrounds f +822 spacepodd y3R2cD Space Pod Demo f +827 exigo mPBHcI Armies of Exigo f +833 jk3 e4F2N7 Star Wars Jedi Knight: Jedi Academy f +840 empiresd GknAbg Empires: Dawn of the Modern World Demo f +841 empiresdam GknAbg Empires: Dawn of the Modern World f +849 asbball2005ps2 Y3pG1m All-star Baseball 2005 f +869 nwnxp2 ZIq1wW Neverwinter Nights: Hordes of Underdark f +878 mohpa S6v8Lm Medal of Honor: Pacific Assault f +886 kohankow uE4gJ7 Kohan: Kings of War f +895 unreal2d Yel30y Unreal 2 Demo f +902 unreal2demo Yel30y Unreal 2 Demo f +909 halomacd e4Rd9J Halo Demo (Mac) f +915 racedriver2d M29dF4 Race Driver 2 Demo f +923 conan 4J8df9 Conan: The Dark Axe f +928 saturdaynsd psZhzd Saturday Night Speedway Demo f +939 afrikakorpsd tfHGsW Desert Rats vs. Afrika Korps Demo f +951 ganglandd y6F39x Gangland Demo f +957 hotwheels2ps2 u3Fx9h Hot Wheels 2 (PS2) f +964 ravenshieldas vMJRUd Raven Shield: Athena's Sword f +970 mxun05ps2am u3Fs9n MX Unleashed 05 (PS2) (Automatch) f +978 mohaasmac h2P1c9 Medal of Honor: Allied Assault Spearhead (Mac) f +981 bfield1942rtrm HpWx9z Battlefield 1942 Road to Rome (Mac) f +991 exigoam mPBHcI Armies of Exigo (Automatch) f +997 whammer40000am uJ8d3N Warhammer 40,000: Dawn of War f +1007 srsyndps2 A9Lkq1 Street Racing Syndicate (PS2) f +1017 menofvalord kJm48s Men of Valor Demo f +1023 crashnburnps2b gj7F3p Crash N Burn Sony Beta (PS2) f +1031 whammer40kbam uJ8d3N Warhammer 40,000: Dawn of War Beta (Automatch) f +1048 callofdutyps2d tR32nC Call of Duty Sony Beta (PS2) f +1057 exigobam mPBHcI Armies of Exigo Beta (Automatch) f +1063 closecomftfmac iLw37m Close Combat: First to Fight Mac f +1072 callofdutyuo KDDIdK Call of Duty: United Offensive f +1079 smackdnps2palr k7cL91 WWE Smackdown vs RAW (PS2) PAL Retail f +1093 mohpad S6v8Lm Medal of Honor: Pacific Assault Demo f +1105 olvps2 7w2pP3 Outlaw Volleyball PS2 f +1110 spoilsofwaram nZ2e4T Spoils of War (Automatch) f +1119 fswps2pal 6w2X9m Full Spectrum Warrior PAL PS2 f +1124 wcpokerpalps2 t3Hd9q World Championship Poker PAL (PS2) f +1132 swrcommandoj y2s8Fh Star Wars Republic Commando Japanese Dist f +1145 swrcommandot y2s8Fh Star Wars Republic Commando Thai Dist f +1156 actofward LaR21n Act of War: Direct Action Demo f +1165 fswps2kor 6w2X9m Full Spectrum Warrior Korean (PS2) f +1178 bsmidwayps2 qY84Ne Battlestations Midway (PS2) f +1187 swat4xp1 tG3j8c SWAT 4: The Stetchkov Syndicate f +1194 fearobsc n3VBcj FEAR: First Encounter Assault Recon (Open Beta Special Content) f +1208 whammer40kwaam Ue9v3H Warhammer 40,000: Winter Assault (Automatch) f +1225 afllive05ps2 j72Lm2 AFL Live 2005 (ps2) f +1231 rtrooperps2 jK7L92 Rogue Trooper (PS2) f +1236 swempiream t3K2dF Star Wars: Empire at War (Automatch) f +1247 wsoppspam u3hK2C World Series of Poker (PSP) (Automatch) f +1256 bfield2xp1 hW6m9a Battlefield 2: Special Forces f +1264 acrossingdsam h2P9x6 Animal Crossing (DS, Automatch) f +1276 scsdwd agGBzE S.C.S. Dangerous Waters Demo f +1285 bf2sttest NFFtwb Battlefield 2 Snapshot testing f +1305 wofordam mxw9Nu WOFOR: War on Terror Demo Automatch f +1316 marvlegpcdam eAMh9M Marvel Legends Demo Automatch (PC) f +1326 runefactoryds dBOUMT Rune Factory (DS) f +1332 actofwarhtdam LaR21n Act of War: High Treason Demo Automatch f +1340 scsdws hmhQeA S.C.S. Dangerous Waters Steam f +1351 tiumeshiftu NhcH1f TimeShift (Unlock codes) f +1359 narutorpg3ds bBPaXO Naruto RPG 3 (DS) f +1368 rockmanwds sdJvVk Rockman WAVES (DS) f +1374 mmvdkds d8Wm37 Mini Mario vs Donkey Kong (DS) f +1383 whammermok rnbkJp Warhammer: Mark of Chaos (OLD) f +1297 bfield1942ps2am \N Battlefield Modern Combat Automatch (PS2) f +1394 gmtestcdam \N Test Automatch (Chat CD Key validation) f +1405 wsc2007ps2 bpDHED World Snooker Championship 2007 (PS2) f +1412 whammer40kdcam Ue9v3H Warhammer 40,000: Dark Crusade Automatch f +1426 rafcivatwart h98Sqa Rise And Fall: Civilizations at War Test f +1433 jumpsstars2ds VXkOdX Jump Super Stars 2 (DS) f +1443 bandbrosds yvcEXe Daiggaso! Band Brothers DX (DS) f +1448 draculagolds 1VyHxN Akumajou Dracula: Gallery of Labyrinth (DS) f +1461 otonatrainds G8skCH Imasara hitoniwa kikenai Otona no Jyoshikiryoku Training DS (DS) f +1470 wh40kwap Ue9v3H Warhammer 40,000: Winter Assault Patch f +1474 wormsow2ds PHK0dR Worms Open Warfare 2 (DS) f +1494 heroesmanads 8lrZB5 Seiken Densetsu: Heroes of Mana (DS) f +1511 bleach2ds Txc4SQ Bleach DS 2: Requiem in the black robe (DS) f +1525 cc3tibwarsmb GmMKoK Command & Conquer 3: Tiberium Wars Match Broadcast f +1545 dkracingds VBr5Sm Diddy Kong Racing DS (DS) f +1553 sweawfoc oFgIYB Star Wars: Empire at War - Forces of Corruption f +1561 cc3tibwarsam E4F3HB Command & Conquer 3: Tiberium Wars Automatch f +1573 rockstardevam 1a8bBi Rockstar Development Automatch f +1583 springwidgetsam tQfwTW Spring Widgets Automatch f +1589 freessbalpha qXtSmt Freestyle Street Basketball Client Alpha f +1602 motogp2007 oXCZxz MotoGP 2007 f +1608 mariokartkods Uu2GJ4 Mario Kart DS (DS) (KOR) f +1616 civconps3 hn53vx Civilization Revolution (PS3) f +1624 elevenkords qiM82O World Soccer Winning Eleven DS (KOR) (DS) f +1636 jissenpachwii 5tc98w Jissen Pachinko Slot (Wii) f +1646 powerpinconds za0kET Powershot Pinball Constructor (DS) f +1654 pokedungeonds SVbm3x Pokemon Fushigi no Dungeon (DS) f +1670 ardinokingds 6wO62C Ancient Ruler Dinosaur King (DS) f +1690 wsc2007pc L6cr8f World Snooker Championship 2007 (PC) f +1695 momoden16wii TuDtif Momotaro Dentetsu 16 - Hokkaido Daiido no Maki! (Wii) f +1699 runefantasyds 58Ae2N Rune Factory: A Fantasy Harvest Moon (DS) f +1711 onslaughtpcam 8pLvHm Onslaught: War of the Immortals Automatch f +1721 keuthendevam TtEZQR Keuthen.net Development Automatch f +1733 tpfolpc svJqvE Turning Point: Fall of Liberty (PC) f +1741 Digidwndskds SEmI1f Digimon World Dawn/Dusk (DS) f +1750 vanguardsoh QVrtku Vanguard Saga of Heroes f +1757 momotarodends gro5rK Momotaro Dentetsu 16 ~ Hokkaido Daiido no Maki! (DS) f +1767 rachelwood L2muET Rachel Wood Test Game Name f +1774 whamdowfr pXL838 Warhammer 40,000: Dawn of War - Soulstorm f +1782 hookedfishwii q7ghtd Hooked! Real Motion Fishing (Wii) f +1791 quakewarsetb i0hvyr Enemy Territory: Quake Wars Beta f +1799 suddenstrike3 QNiEOS Sudden Strike 3: Arms for Victory f +1802 dfriendsEUds AoJWo6 Disney Friends DS (EU) f +1804 suitelifeds q3Vrvd Suite Life of Zack & Cody: Circle of Spies (DS) f +1812 bokujyods O5ZdFP Bokujyo Monogatari Himawari Shoto wa Oosawagi! (DS) f +1824 WSWeleven07ds sb2kFV World Soccer Winning Eleven DS 2007 (DS) f +1831 suitelifeEUds 7AyK8d Suite Life of Zack & Cody: Circle of Spies (EU) (DS) f +1841 birhhpcam sPZGCy Brothers In Arms: Hell's Highway Automatch (PC) f +1850 greconawf2g pdhHKC Ghost Recon Advanced Warfighter 2 f +1859 MOHADemo rcLGZj Medal of Honor Airborne Demo f +1867 painkillerodam zW4TsZ Painkiller Overdose Automatch f +1873 wiibombmanwii xx7Mvb Wii Bomberman / WiiWare Bomberman / Bomberman Land Wii 2 (Wii) f +1884 whamdowfrb pXL838 Warhammer 40,000: Dawn of War - Final Reckoning Beta f +1897 painkilleroddam zW4TsZ Painkiller Overdose Demo Automatch f +1908 condemned2bsam kwQ9Ak Condemned 2: Bloodshot Automatch f +1913 jikkyopprowii FVeCbl Jikkyo Powerful Pro Yakyu Wii Kettei ban (Wii) f +1924 whammermocbmam EACMEZ Warhammer: Mark of Chaos - Battle March Automatch f +1948 harmooniohds iCyIlW Harvest Moon : Island of Happiness (US) (DS) f +1959 evosoc08EUwii de5f31 Pro Evolution Soccer 2008 (EU) (Wii) f +1977 blkuzushiwii Fi1p8K THE Block Kuzushi - With the Stage Creation feature (Wii) f +1990 rfactoryEUds CK8ylc Rune Factory: A Fantasy Harverst Moon (EU) (DS) f +2001 mxvatvuPALps2 cps6m8 MX vs ATV Untamed PAL (PS2) f +2010 shirends2ds T5gnTX Fushigi no Dungeon: Furai no Shiren DS2 (DS) f +2011 worldshiftpcb 7gBmF4 WorldShift Beta (PC) f +2016 sangotends yln2Zs Sangokushitaisen Ten (DS) f +2025 wiilinkwii Be3reo Wii Link (Wii) f +2067 legendaryps3 9HaHVD Legendary (PS3) f +2101 cc3tibwarscdam E4F3HB Command & Conquer 3: Tiberium Wars CD Key Auth Automatch f +2117 nakedbrbndds d5ZTKM Naked Brothers Band World of Music Tour (DS) f +2120 legendarypcd WUp2J6 Legendary Demo (PC) f +2125 draglade2ds cTbHQV Custom Beat Battle: Draglade 2 (DS) f +2138 bbangminids ErZPG8 Big Bang Mini (DS) f +2144 cod5victoryds z8ooR0 Call of Duty 5: Victory (DS) f +2151 digichampUSds TiuO7K Digimon Championship (US) (DS) f +2163 swbfront3ps3 y3AEXC Star Wars Battlefront 3 (PS3) f +2186 toribashwii 3wygG8 Toribash (WiiWare) f +1504 codedarmspspam \N Coded Arms Automatch (PSP) f +1663 roguewarpcd \N Rogue Warrior Demo (PC) f +1679 gta4ps3am \N Grand Theft Auto 4 Automatch (PS3) f +1935 civ4btsjpam \N Civilization IV: Beyond the Sword Automatch (Japanese) f +1970 mmadnessexps3am \N Monster Madness EX Automatch (PS3) f +2028 tpfolEUpcam \N Turning Point: Fall of Liberty Automatch (EU) (PC) f +2047 damnationps3am \N DamNation Automatch (PS3) f +2054 mclub4ps3devam \N Midnight Club 4 Dev Automatch (PS3) f +2074 beijing08pcd \N Beijing 2008 Demo (PC) f +2085 heistpcam \N Heist Automatch (PC) f +2172 50centsandps3am \N 50 Cent: Blood on the Sand Automatch (PS3) f +2193 poriginpcd \N Fear 2: Project Origin Demo (PC) f +2200 bballarenaps3am W8bW5s Supersonic Acrobatic Rocket-Powered BattleCars: BattleBall Arena Automatch f +2220 svsr09x360 Pzhfov WWE Smackdown vs. RAW 2009 (Xbox 360) f +2222 cod5wii XSq2xz Call of Duty 5 (Wii) f +2228 redalert3pcmb uBZwpf Red Alert 3 (PC) Match Broadcast f +2248 mkvsdcps3 XqrAqV Mortal Kombat vs. DC Universe (PS3) f +2254 rman2blkredds c2ZOsn Ryusei no Rockman 3: Black Ace / Red Joker (JP) (DS) f +2269 kkhrebornwii 76Trpf Katei Kyoshi Hitman REBORN! Kindan no Yami no Delta (Wii) f +2307 mswinterwii O53Z7t Mario & Sonic at the Olympic Winter Games (Wii) f +2317 chocotokids yfVdWO Shido to Chocobo no Fushigina Dungeon Tokiwasure no Meikyu DS+(DS) f +2334 koinudewii GyW0xG Koinu de Kururin Wii (WiiWare) f +2335 lonposUSwii AtQIeu Lonpos (US) (WiiWare) f +2336 wwkuzushiwii 8EzbpJ SIMPLE THE Block Kuzushi (WiiWare) f +2342 bbarenaEUps3 w6gFKv Supersonic Acrobatic Rocket-Powered BattleCars (PSN) (EU) f +2353 bbarenaEUps3d w6gFKv Supersonic Acrobatic Rocket-Powered BattleCars Demo (PSN) (EU) f +2372 idolmasterds oIRn8T The Idolmaster DS (DS) f +2377 takameijinwii mgiHxl Takahashi Meijin no Boukenshima (WiiWare) f +2392 segaracingds HQKW0J Sega Superstars Racing (DS) f +2402 weleplay09wii Eamkm6 Winning Eleven PLAY MAKER 2009 (Wii) f +2410 im1pc uRd8zg Interstellar Marines (PC) f +2415 50ctsndlvps3 n5qRt7 50 Cent: Blood on the Sand - Low Violence (PS3) f +2439 biahhPCHpc NFBVyk Brothers In Arms: Hell's Highway (PC) (POL/CZE/HUNG) f +2455 airhockeywii vhxMTl World Air Hockey Challenge! (WiiWare) f +2463 hunterdanwii 55Fqd5 Hunter Dan's Triple Crown Tournament Fishing (Wii) f +2478 guinnesswriph euFh7c Guinness World Records: The Video Game (iPhone) f +2489 h2cdigitalps3 HlgicF Hail to the Chimp (PSN) f +2498 ninTest/ EdD7Ve Nintendo Development Testing masterID 0 f +2505 ninTest2am EdD7Ve Nintendo Development Testing masterID 3 Automatch f +2513 ninTest6am EdD7Ve Nintendo Development Testing masterID 7 Automatch f +2523 ninTest;am EdD7Ve Nintendo Development Testing masterID 12 Automatch f +2533 ninTest@am EdD7Ve Nintendo Development Testing masterID 17 Automatch f +2536 ninTest. EdD7Ve Nintendo Development Testing masterID 19 f +2540 acejokerUSds ZS4JZy Mega Man Star Force 3: Black Ace/Red Joker (US) (DS) f +2555 okirakuwii LdIyFm Okiraku Daihugou Wii (WiiWare) f +2594 superv8pc GfQdlV Superstars V8 Racing (PC) f +2604 puyopuyo7ds 1SeVl7 PuyoPuyo 7 (DS/Wii) f +2631 mekurucawii fUq0HT Mekuruca (WiiWare) f +2644 arma2pc zbMmN3 Arma II (PC) f +2647 rubikguidewii nTLw4A Rubik's Puzzle World: Guide (WiiWare) f +2666 appletestam TZHVox Apple SDK test Automatch f +2673 ragonlineKRds fhZRmu Ragunaroku Online DS (KOR) (DS) f +2684 mk9testam a0GZNV Midway MK9 Test Automatch f +2692 luchalibrepc dGu4VZ Lucha Libre AAA 2010 (PC) f +2710 tvshwking2wii pNpvGC TV Show King 2 (WiiWare) f +2719 yugioh5dwii bTL9yI Yu-Gi-Oh! 5D's Duel Simulator (Wii) f +2740 shikagariwii IrKIwG Shikagari (Wii) f +2746 ubraingamesds MzT7MD Ultimate Brain Games (DS) f +2752 blockoutwii 6DPfd2 Blockout (Wii) f +2780 fushigidunds VVNqVT Fushigi no Dungeon Furai no Shiren 4 Kami no Me to Akama no Heso (DS) f +2796 tycoonnyc VgxCbC Tycoon City - New York f +2823 hastpaint2wii yt6N8J Greg Hastings Paintball 2 (Wii) f +2837 gticsfestwii DN9tTG GTI Club Supermini Festa (Wii) f +2864 heroeswii vaKmz5 Heroes (Wii) f +2865 yugiohwc10ds TuaRVH Yu-Gi-Oh! World Championship 2010 (DS) f +2875 destructionam vt3f71 Destruction 101 Automatch f +2883 svsr11ps3 8TMLdH Smackdown vs Raw 2011 (PS3) f +2913 phybaltraiwii nb1GZR Physiofun Balance Trainer (WiiWare) f +2924 katekyohitds 9kXaZG katekyo hitman REBORN! DS FLAME RUMBLE XX (DS) f +2933 jyankenparwii t2ge59 Jyanken (rock-paper-scissors) Party Paradise (WiiWare) f +2950 pangmagmichds ccXnxb Pang: Magical Michael (DS) f +2971 cellfacttwpc bxYYnG Cell Factor:TW (PC) f +2976 winel10jpnwiiam 1mJhT4 Winning Eleven PLAY MAKER 2010 Japan Edition Automatch (Wii) f +856 moutlawned \N Midnight Outlaw Illegal Street Drag Nitro Edition Demo f +1482 marvlegps3pam \N Marvel Legends PAL Automatch (PS3) f +1485 djangosabds \N Bokura No Taiyou: Django & Sabata (DS) f +2288 FlockPCam \N Flock Automatch (PC) f +2297 cellfactorpcam \N CellFactor: Ignition Automatch (PSN) Clone f +2432 tapraceam \N Tap Race Automatch (iPhone Sample) f +2437 biahhPRps3am \N Brothers In Arms: Hell's Highway Automatch (PS3) (POL/RUS) f +2440 biahhPCHpcam \N Brothers In Arms: Hell's Highway Automatch (PC) (POL/CZE/HUNG) f +2564 poriginps3jpam \N Fear 2: Project Origin Automatch (JP) (PS3) f +2579 civ4coljpam \N Sid Meier's Civilization IV: Colonization Automatch (PC Japanese) f +2609 svsr10ps3am \N WWE Smackdown vs. Raw 2010 Automatch (PS3) f +2621 beateratoripham \N Beaterator Automatch (iPhone) f +2636 bderlandsps3am \N Borderlands Automatch (PS3) f +2658 blindpointpcam \N Blind Point Automatch (PC) f +2703 ludicrousmacam \N Ludicrous Automatch (MAC) f +2727 50centjpnps3am \N 50 Cent: Blood on the Sand Automatch (JPN) (PS3) f +2761 stalkercoppcam \N STALKER: Call of Pripyat Automatch (PC) f +2774 ufc10x360am \N UFC 2010 Automatch (x360) f +2805 demonforgepcam \N Demon's Forge Automatch (PC) f +2815 maxpayne3ps3d \N Max Payne 3 Demo (PS3) f +2844 superv8ncpcam \N Superstars V8 Next Challenge Automatch (PC) f +2861 foxtrotpcam \N Foxtrot Automatch (PC) f +2893 krabbitpcmacam \N KrabbitWorld Origins Automatch (PC/Mac) f +2905 lanoirex360am \N L.A. Noire Automatch (x360) f +2958 combatzonepcam \N Combat Zone - Special Forces Automatch (PC) f +2970 ZumaDeluxe \N Zuma Deluxe f +2090 bstrikeotspcam \N Battlestrike: Operation Thunderstorm Automatch (PC) f +2236 cellfactorpsnam \N CellFactor: Ignition Automatch (PSN) f +2679 swbfespspam \N Star Wars: Battlefront - Elite Squadron Automatch (PSP) f +\. + + +-- +-- TOC entry 3447 (class 0 OID 16405) +-- Dependencies: 216 +-- Data for Name: grouplist; Type: TABLE DATA; Schema: unispy; Owner: - +-- + +COPY unispy.grouplist (groupid, gameid, roomname) FROM stdin; +1 5 daikatana test group +2 1 Newbies +3 1 Experts +4 1 Farm Animals +5 256 Skirmish +6 256 Domination +7 192 Test VP3 Tourney +9 192 this +18 192 b +19 192 b +20 285 Rookies +21 285 Amateurs +22 285 Pros +24 192 b +25 256 Slaughter +26 256 Soul Harvest +27 256 Allied +30 192 LumberJack VP3 Test Tourney +44 192 test6ladder +57 192 asdf +63 192 BillsTest2 +64 192 BillsTest2 +101 192 this +102 308 Beginner +103 308 Intermediate +104 308 Advanced +105 192 this +106 192 hello +107 192 mytest +108 192 arts ladder +109 192 Seans Ladder +110 192 seans test ladder +111 192 Seans Test Ladder +112 192 9-Ball Challenge +113 192 TestOct23 +114 192 abcd +115 192 Lumberjack VP3 Test Tourny #2 +116 192 9-Ball Heaven +117 192 QA Test Ladder +118 192 GSI Test - do not join +119 192 reload test +122 192 Tonys 9-Ball tourney +123 192 Tonys 9-Ball tourney 2 +124 192 Tonys Moved Database Test +125 192 outputdir test tourney +127 192 b +141 192 aphexweb1 test +143 192 registration test +144 192 w +147 192 arts ladder test +148 192 gsi test ladder nov 1 +149 192 test +150 192 arts ladder +151 192 gsi test ladder +152 192 tonys ladder +153 192 9-Ball Ladder: Public Test +154 192 Interplay QA test +155 192 Interplay QA test +156 192 Qa Test 2 +157 192 QA DEDICATED TEST +158 192 TEN BALL - QA TEST +159 192 BILLIARDS +160 192 IP Rotation +161 192 10 BALL - QA TEST +162 192 Savy & Sean +163 192 Willow & Erik +164 192 Savy +165 192 6 Ball game +166 192 VP3 Private Patch Testing +167 192 Patch (#2) Final Testing +168 337 Main Lobby +169 337 {01}Cadet +170 337 {02}Captain +171 337 {03}Admiral +172 412 {01}General Chat +173 412 {04}Teen Chat +176 412 {02}Family And Friends +177 412 {03}College Chat +189 15 {01}Half-Life Room 1 +190 15 {02}Half-Life Room 2 +191 15 {01}Counter-Strike: Special Air Service +192 15 {01}Counter-Strike: GSG-9 +193 15 {01}Counter-Strike: Counter-Terrorist Force +194 15 {01}Counter-Strike: Seal Team 6 +195 15 {11}Firearms Room +196 15 {13}TeamFortress Classic Room +197 15 {10}Day of Defeat Room +198 15 {12}Front Line Force Room +199 412 {01}Action Games +200 412 {02}Role Playing Games +201 412 {03}Strategy Games +202 412 {04}Sports Games +203 412 {05}Simulation Games +204 412 {06}Tactical Games +206 15 {03}Help With Half-Life +207 323 {01}Counter-Terrorist Force +208 323 {01}Seal Team Six +209 323 {02}Help with Counter-Strike +210 22 {02}Quake 3 Veterans Room +211 22 {01}Quake 3 Main Room +212 22 {05}Rocket Arena 3 Room +213 22 {02}Freeze Tag +214 22 {04}Quake 3 Fortress Room +215 22 {06}Threewave CTF +216 22 {07}Urban Terror Beta 2 Room +217 22 {08}Weapons Factory Arena Room +218 22 {01}Excessive Room +219 401 {03}Spades Advanced Lobby +220 401 {01}Spades Newbie Lobby +221 401 {04}Spades Ranked Lobby +222 401 {02}Spades Social Lobby +223 403 {02}Backgammon Ranked Lobby +224 403 {01}Backgammon General Lobby +225 400 Poker Advanced Lobby +226 400 Poker Newbies Lobby +227 400 Poker Ranked Lobby +228 400 Poker Social Lobby +229 402 {01}Hearts General Lobby +230 402 {02}Hearts Ranked Lobby +231 22 {06}Help with Quake 3 +232 22 {05}Team Deathmatch +233 15 {01}Counter-Strike: For Great Justice! +234 15 {01}Counter-Strike: Clan Battle Room +236 15 {13}Action Half-Life Room +241 15 {03}Opposing Force Room +242 15 {04}PlanetHalfLife Arcade Event Lobby +243 401 {05}Spades Tournament Lobby +244 402 {03}Hearts Tournament Lobby +245 403 {03}Backgammon Tournament Lobby +246 192 Squish Test ladder +247 292 Main +248 292 Tournaments +250 361 {01}Geral +251 361 {02}Jogos PC +252 15 {14}Deathmatch Classic +259 414 {01}Everon +260 414 {01}Malden +261 414 {02}Dedicated Servers +262 414 {03}Co-op +263 414 {04}Capture the Flag +264 414 {05}Test Zone +265 361 {03}Jogos Consolas +266 361 {04}Hardware +267 361 {05}Torneios e Eventos +269 361 {06}Comunidade +270 483 THPS3 Internet +271 400 Poker Tournament Lobby +272 22 {03}Orange Smoothie +273 504 Main +274 328 {01}Action +275 328 {02}Roleplaying +276 328 {03}Team (N vs. N) +277 328 {04}Social +278 328 {05}Persistent World Action +279 328 {06}Alternative +280 292 Korean +281 509 The Downs +288 509 Forest Heart +295 509 Tharsis +299 523 Main Lobby +300 523 {01}Cadet +301 523 {02}Captain +302 523 {03}Admiral +303 22 {03}Capture the Flag +304 22 {04}Deathmatch +305 292 Français +306 292 Deutsch +307 292 KIS +308 292 KAG +310 362 {01}Juegos +311 362 {02}Adolescentes +312 362 {03}Encuentros +313 362 {04}Maduritos +314 362 {05}Sexo +315 564 Tony Hawk 2x +316 564 Halo +317 564 NASCAR Heat 2002 +318 568 {01}Eastern Front +319 568 {01}Western Front +320 568 {01}North African Campaign +321 568 {01}Pacific Campaign +322 568 {01}Guadalcanal Campaign +323 568 {01}Soviet Winter Offensive +324 363 {01}Juegos +325 363 {02}Adolescentes +326 363 {03}Encuentros +327 363 {04}Maduritos +328 363 {05}Romance +329 492 {01}Eastern Front +330 492 {01}Western Front +331 492 {02}Round-based Match +332 492 {04}Objective-based Match +333 492 {01}Deathmatch +334 492 {03}Team Match +335 492 {01}North Africa +336 492 {01}Siegfried Line +337 492 {03}Team Match +338 492 {04}Objective-based Match +339 590 (01)Power Plant +340 590 (01)Tiberium Refinery +341 590 (01)Weapons Factory +342 590 (01)Infantry Barracks +343 590 (01)GDI Guard Tower +344 590 (01)Construction Yard +345 590 (01)Hand of Nod +346 590 (01)Nod Airstrip +347 15 {15}Desert Crisis +352 577 (01)Power Plant +353 577 (01)Tiberium Refinery +354 577 (01)Weapons Factory +355 577 (01)Infantry Barracks +356 577 (01)GDI Guard Tower +357 577 (01)Construction Yard +358 577 (01)Hand of Nod +359 577 (01)Nod Airstrip +361 564 Tony Hawk 3 +362 564 MotoGP +363 328 {07}Story +364 328 {08}Story Lite +365 328 {09}Melee (1 vs. N) +366 328 {10}Arena (1 vs. N) +367 328 {11}Persistent World Story +368 328 {12}Solo +369 412 {05}Romance +370 328 {13}Tech Support +371 15 {16}Ricochet +372 564 Australia +373 564 Europe +374 564 United Kingdom +375 610 {01}Infantry +376 610 {01}Combat Engineering +377 610 {01}Combat Operations +378 610 {01}Special Forces +379 610 {01}Armor +380 610 {01}Aviation Operations +381 610 {05}MOUT McKenna +382 610 {05}MOUT McKenna +383 610 {01}Bridge Crossing +384 610 {03}Headquarters Raid +385 610 {04}Insurgent Camp +386 610 {06}Pipeline +387 610 {02}Collapsed Tunnel +388 610 {01}Bridge Crossing +389 675 GroupRoom1 +390 675 GroupRoom2 +391 675 GroupRoom3 +392 675 QuickMatch +393 564 NFL Fever 2003 +394 684 Los Angeles (Newbies) +395 684 Los Angeles (Experts) +396 684 Tokyo (Newbies) +397 684 Tokyo (Experts) +398 684 Paris (Newbies) +399 684 Paris (Experts) +400 684 Battle (Newbies) +401 684 Battle (Experts) +402 671 (01)Allies Lobby +403 671 (01)Allies Lobby +404 671 (01)Axis Lobby +405 671 (01)Axis Lobby +409 617 Rookie +410 617 Intermediate +411 617 Expert +412 541 {01}Axis Lobby +413 541 {01}Allies Lobby +414 541 {03}Pacific Theater +415 541 {02}European Theater +417 541 {04}Russian Theater +418 541 Capture the Flag +419 541 Conquest +420 541 Co-Op +421 541 Team Deathmatch +422 541 {01}African Theater +423 636 Main Lobby +424 636 ATI Tournament Lobby +441 564 TimeSplitters 2 +442 564 Tony Hawk 4 +443 15 {17}Natural Selection +444 716 GroupRoom1 +445 716 GroupRoom2 +446 716 GroupRoom3 +447 564 Deathrow +448 712 {01}General Lobby +449 712 {01}General Lobby +450 712 Free-For-All Servers +451 712 Team Deathmatch +452 712 Round-based Match +453 712 Objective Match +454 712 Tug of War +455 641 {01}General Lobby +456 641 {01}General Lobby +457 641 Free-For-All Servers +458 641 Team Deathmatch +459 641 Round-based Match +460 641 Objective Match +461 641 Tug of War +462 564 MechAssault +463 564 Unreal Championship +464 564 Ghost Recon +471 617 Rated +472 617 Unrated +473 617 Unrated Expert +474 617 Unrated Intermediate +475 617 Unrated Beginner +476 730 Beginner +477 730 Intermediate +478 730 Advanced +479 721 Rated 0 +480 721 Rated 1 +481 721 Rated 2 +483 721 Rated 3 +485 557 Ryan's Room +486 557 Donnie's Room +487 541 Desert Combat +488 713 {01}Allies Lobby +489 713 {01}Axis Lobby +490 713 {01}African Theater +491 713 {02}European Theater +492 713 {03}Italian Theater +493 713 {04}Pacific Theater +494 713 {05}Russian Theater +496 675 GroupRoom4 +497 675 GroupRoom5 +498 675 GroupRoom6 +499 675 GroupRoom7 +500 675 GroupRoom8 +501 675 GroupRoom9 +502 675 GroupRoom10 +503 675 GroupRoom11 +504 675 GroupRoom12 +506 557 Kyle's Room +507 557 Bobby's Room +508 557 Nick's Room +509 557 Linda's Room +510 557 Melanie's Room +511 557 Cannonball's Room +512 557 Paulie's Room +513 557 Cuban Joe's Room +514 541 Galactic Conquest +515 770 Ryan's Room +516 770 Bobby's Room +517 722 Anything Goes +519 722 Team17 +523 722 Professional League +524 722 Elite League +525 721 Rated 4 +527 721 Rated 6 +528 721 Rated 7 +529 721 Rated 8 +530 721 Rated 9 +531 721 Unrated 0 +532 721 Unrated 1 +533 721 Unrated 2 +534 721 Unrated 3 +535 721 Unrated 4 +536 721 Rated 5 +537 721 Unrated 5 +538 721 Unrated 6 +539 721 Unrated 7 +540 721 Unrated 8 +541 721 Unrated 9 +542 765 tier1 +543 765 tier2 +544 765 tier3 +545 765 tier4 +546 765 tier5 +547 776 West +548 776 East +549 776 Europe +550 776 Asia +551 776 Beginner +552 776 Expert +553 15 {18}Vampire Slayer +554 772 Action Room +555 772 Asian Room +556 772 Deathmatch Room +557 797 {01}Main Lobby +558 797 {03}Tour Lobby +559 797 {02}Ladder Lobby +560 564 Wolfenstein Tides of War +561 564 Brute Force +562 541 Eve of Destruction +563 564 Midnight Club 2 +564 564 Moto GP 2 +565 564 Inside Pitch 2003 +566 564 Star Wars: The Clone Wars +567 564 Midtown Madness 3 +568 541 ActionBattlefield +569 792 Conquest Scenarios +570 792 Battle Scenarios +571 823 GroupRoom1 +572 824 Unrated 0 +573 824 Unrated 1 +575 824 Unrated 2 +576 824 Unrated 3 +577 824 Unrated 4 +578 824 Unrated 5 +579 824 Unrated 6 +580 824 Unrated 7 +581 824 Unrated 8 +582 824 Unrated 9 +586 823 GroupRoom2 +587 823 GroupRoom3 +588 823 GroupRoom4 +589 823 GroupRoom5 +590 823 GroupRoom6 +591 823 GroupRoom7 +592 823 GroupRoom8 +593 823 GroupRoom9 +594 823 GroupRoom10 +595 823 GroupRoom11 +596 823 GroupRoom12 +597 823 QuickMatch +598 823 GroupRoom13 +599 840 Advanced +600 840 Intermediate +601 840 Beginner +602 823 GroupRoom14 +606 832 Social Room +607 832 Beginner Room +608 832 Intermediate Room +609 832 Advanced Room +610 851 West +611 851 East +612 851 Europe +613 851 Asia +614 851 Beginner +615 851 Expert +616 22 {09}Urban Terror Beta 3 Room +619 15 {19}The Specialists +620 15 {20}MonkeyStrike +621 15 {21}Earth Special Forces +622 722 Amateur League +623 722 Shopping +624 842 Beginner +625 842 Casual +626 842 Expert +627 842 Elite +628 832 Practice Room +631 871 Newbies +632 871 Pros +633 871 Moto 1 +634 871 Moto 2 +635 845 Zaramoth +636 845 Zaramoth +637 845 Azunai +638 845 Azunai +639 845 Xeria +640 845 Xeria +641 845 Isteru +642 772 Empire Builder Room +643 772 European Room +644 772 Free For All Room +645 772 German Room +646 772 Ladder Room +647 772 No Rush Room +648 772 Tournament Room +649 843 European Public League +650 843 Massive Test Leauge +651 843 North American Public League +652 843 Asian Public League +660 870 LobbyRoom1 +661 870 LobbyRoom2 +662 870 QuickMatch +664 891 Group Room 2 +665 564 Amped 2 +666 564 Crimson Skies +667 564 NFL Fever 2004 +668 564 Soldier of Fortune II +669 564 Ghost Recon: Island Thunder +670 564 Rainbow Six 3 +671 564 Tony Hawk Underground +672 564 Top Spin +673 791 Conquest Scenarios +674 791 Battle Scenarios +675 15 {23}Counter-Strike: Clan Battle Room +676 15 {23}Counter-Strike: For Great Justice! +677 15 {23}Counter-Strike: GSG-9 +678 15 {23}Counter-Strike: Counter-Terrorist Force +679 15 {23}Counter-Strike: Seal Team 6 +680 15 {23}Counter-Strike: Special Air Service +684 793 {01}Main Lobby +685 793 {02}Halo Tournament +696 886 Main +697 886 Tournament +698 868 Main +699 868 Tournament +715 852 Search and Destroy +716 852 Behind Enemy Lines +717 852 Retrieval +718 852 Deathmatch +719 924 Rated 0 +720 924 Rated 1 +721 924 Rated 2 +722 924 Rated 3 +723 924 Rated 4 +724 924 Rated 5 +725 924 Rated 6 +726 924 Rated 7 +727 924 Rated 8 +728 924 Rated 9 +729 924 Unrated 0 +730 924 Unrated 1 +731 924 Unrated 2 +732 924 Unrated 3 +733 924 Unrated 4 +734 924 Unrated 5 +735 924 Unrated 6 +736 924 Unrated 7 +737 924 Unrated 8 +738 924 Unrated 9 +739 806 Headquarters +740 806 Briefing Room +741 806 The Bunker +742 806 Mess Hall +743 946 Room 1 +744 946 Room 2 +745 922 Competitive +746 922 Friendly +747 918 US - Eastern +748 918 US - Central +749 918 US - Western +750 918 Europe - English +751 918 Europe - French +752 918 Europe - Italian +753 918 Europe - German +754 918 Europe - Spanish +755 976 Beginners +756 976 Intermediate +757 908 Beginners +758 908 Experts +759 908 Europe +760 908 America +761 908 Asia +766 960 Casual Play +767 960 Rated Play +768 960 Can of Spam +769 1008 Public Demo League +770 1008 Public Demo League +771 1008 Public Demo League +776 1004 Amateur +786 1004 Rookie +796 1004 Pro +812 1004 Legend +816 946 Room 3 +817 1030 Beginner +818 1030 Intermediate +819 1030 Expert +820 878 News and Events#1 +821 878 News and Events#2 +822 878 News and Events#3 +823 878 News and Events#4 +824 878 News and Events#5 +830 878 Medal of Honor Chat#1 +831 878 Medal of Honor Chat#2 +832 878 Medal of Honor Chat#3 +833 878 Medal of Honor Chat#4 +834 878 Medal of Honor Chat#5 +840 878 EA Chat#1 +841 878 EA Chat#2 +850 878 Tech Support & Help#1 +851 878 Tech Support & Help#2 +860 878 Clan Arena#1 +861 878 Clan Arena#2 +862 878 Clan Arena#3 +863 878 Clan Arena#4 +864 878 Clan Arena#5 +865 878 Clan Arena#6 +866 878 Clan Arena#7 +867 878 Clan Arena#8 +868 878 Clan Arena#9 +869 878 Clan Arena#10 +870 878 Boot Camp Training#1 +871 878 Boot Camp Training#2 +872 878 Boot Camp Training#3 +873 878 Boot Camp Training#4 +874 878 Boot Camp Training#5 +880 878 Officers Club#1 +881 878 Officers Club#2 +882 878 Officers Club#3 +883 878 Officers Club#4 +886 878 Officers Club#5 +890 878 The War Room#1 +891 878 The War Room#2 +892 878 The War Room#3 +893 878 The War Room#4 +894 878 The War Room#5 +900 878 Off Topic Discussion#1 +901 878 Off Topic Discussion#2 +902 878 Off Topic Discussion#3 +903 878 Off Topic Discussion#4 +904 878 Off Topic Discussion#5 +916 1043 Amateur +925 1043 Rookie +935 1043 Pro +951 1043 Legend +955 1042 Conquest +956 1042 King of the Hill +957 1042 Territory Control +960 843 Reliance WebWorld Tournament +963 946 Room 4 +964 946 Room 5 +965 946 Room 6 +966 946 Room 7 +967 946 Room 8 +968 946 Room 9 +969 946 Room 10 +970 1064 Main +971 1064 Tournament +972 955 Airliners +973 955 Adventures +974 955 Bush Flying +975 955 Competitions +976 955 Flight Training +977 955 Fly-Ins +978 955 Free Flight +979 955 Helicopter Ops +983 827 AoX chat +984 827 AoX AUS +985 827 AoX CAN +986 827 AoX CHN +987 827 AoX CZE +988 827 AoX DEU +989 827 AoX ESP +990 827 AoX FRA +991 827 AoX GBR +992 827 AoX HUN +993 827 AoX ITA +994 827 AoX KOR +995 827 AoX POL +996 827 AoX RUS +997 827 AoX USA +998 827 AoX AFRICA +999 827 AoX AMERICA +1000 827 AoX ASIA +1001 827 AoX EUROPE +1002 870 LobbyRoom3 +1003 870 LobbyRoom4 +1004 870 LobbyRoom5 +1005 870 LobbyRoom6 +1006 870 LobbyRoom7 +1007 870 LobbyRoom8 +1008 870 LobbyRoom9 +1009 870 ChatRoom1 +1010 870 ChatRoom2 +1011 870 ChatRoom3 +1012 870 ChatRoom4 +1013 870 ChatRoom5 +1014 870 ChatRoom6 +1015 870 ChatRoom7 +1016 870 ChatRoom8 +1017 870 ChatRoom9 +1018 870 ChatRoom10 +1019 1070 Amateur 01 +1020 1070 Amateur 02 +1021 1070 Amateur 03 +1022 1070 Amateur 04 +1023 1070 Amateur 05 +1029 1070 Rookie 01 +1030 1070 Rookie 02 +1031 1070 Rookie 03 +1032 1070 Rookie 04 +1033 1070 Rookie 05 +1034 1070 Pro 01 +1035 1070 Pro 02 +1036 1070 Pro 03 +1037 1070 Pro 04 +1038 1070 Pro 05 +1049 1070 Legend 01 +1050 1070 Legend 02 +1051 1070 Legend 03 +1052 1070 Legend 04 +1053 1070 Legend 05 +1057 1071 Amateur 01 +1058 1071 Amateur 02 +1059 1071 Amateur 03 +1060 1071 Amateur 04 +1061 1071 Amateur 05 +1062 1071 Amateur 06 +1063 1071 Amateur 07 +1064 1071 Amateur 08 +1065 1071 Amateur 09 +1066 1071 Amateur 10 +1067 1071 Rookie 01 +1068 1071 Rookie 02 +1069 1071 Rookie 03 +1070 1071 Rookie 04 +1071 1071 Rookie 05 +1072 1071 Rookie 06 +1073 1071 Rookie 07 +1074 1071 Rookie 08 +1075 1071 Rookie 09 +1076 1071 Rookie 10 +1077 1071 Pro 01 +1078 1071 Pro 02 +1079 1071 Pro 03 +1080 1071 Pro 04 +1081 1071 Pro 05 +1082 1071 Pro 06 +1083 1071 Pro 07 +1084 1071 Pro 08 +1085 1071 Pro 09 +1086 1071 Pro 10 +1087 1071 Pro 11 +1088 1071 Pro 12 +1089 1071 Pro 13 +1090 1071 Pro 14 +1091 1071 Pro 15 +1092 1071 Pro 16 +1093 1071 Legend 01 +1094 1071 Legend 02 +1095 1079 Amateur 01 +1096 1079 Amateur 02 +1097 1079 Amateur 03 +1098 1079 Amateur 04 +1099 1079 Amateur 05 +1100 1079 Amateur 06 +1105 1079 Rookie 01 +1106 1079 Rookie 02 +1107 1079 Rookie 03 +1108 1079 Rookie 04 +1109 1079 Rookie 05 +1110 1079 Rookie 06 +1115 1079 Pro 01 +1116 1079 Pro 02 +1117 1079 Pro 03 +1118 1079 Pro 04 +1119 1079 Pro 05 +1120 1079 Pro 06 +1131 1079 Legend 01 +1132 1079 Legend 02 +1133 1079 Legend 03 +1134 1079 Legend 04 +1135 922 Chat Lobby +1136 1080 Main +1137 1080 Tournament +1151 1088 Welcome +1152 1094 AoX chat +1153 1094 AoX AUS +1154 1094 AoX CAN +1155 1094 AoX CHN +1156 1094 AoX CZE +1157 1094 AoX DEU +1158 1094 AoX ESP +1159 1094 AoX FRA +1160 1094 AoX GBR +1161 1094 AoX HUN +1162 1094 AoX ITA +1163 1094 AoX KOR +1164 1094 AoX POL +1165 1094 AoX RUS +1166 1094 AoX USA +1167 1094 AoX AFRICA +1168 1094 AoX AMERICA +1169 1094 AoX ASIA +1170 1094 AoX EUROPE +1171 1071 Legend 03 +1172 1071 Legend 04 +1173 976 Advanced +1174 1092 Beginner +1175 1092 Intermediate +1176 1092 Advanced +1177 845 Isteru +1178 845 Gregor +1179 845 Gregor +1180 845 Rahvan +1181 845 Rahvan +1182 845 Feandan +1183 845 Feandan +1184 845 Dalziel +1185 845 Dalziel +1186 845 Istaura +1187 845 Istaura +1188 845 Agarrus +1189 845 Agarrus +1190 845 Lorethal +1191 845 Lorethal +1192 845 Vistira +1193 845 Vistira +1194 845 Keh +1195 845 Keh +1196 845 Soranith +1197 845 Soranith +1198 845 Rubicon +1199 845 Rubicon +1200 845 Thena +1201 845 Thena +1202 845 Artech +1203 845 Artech +1204 845 Ethaniel +1205 845 Ethaniel +1206 845 Kale +1207 845 Kale +1208 845 Calix +1209 845 Calix +1210 845 Culahn +1211 845 Culahn +1212 845 Rowain +1213 845 Rowain +1214 845 Kelis Carthok +1215 845 Kelis Carthok +1223 1035 Main +1224 1035 Asia +1225 1035 Europe +1226 1035 US +1227 1035 Main +1228 1035 Asia +1229 1035 Europe +1230 1035 US +1231 1035 Main +1232 1035 Asia +1233 1035 Europe +1234 1035 US +1235 1135 Novices +1236 1135 Veterans +1237 1135 English +1238 1135 French +1239 1135 German +1240 1135 Italian +1241 1135 Spanish +1250 952 US - Eastern +1251 952 US - Central +1253 952 US - Western +1254 952 Europe - English +1255 952 Europe - French +1256 952 Europe - Italian +1257 952 Europe - German +1258 952 Europe - Spanish +1259 1042 Conquest 2 +1260 1042 Conquest 3 +1261 1156 Main +1262 1156 Main +1263 1156 Main +1264 1042 Territory Control 2 +1265 1042 Territory Control 3 +1266 1042 King of the Hill 2 +1267 1042 King of the Hill 3 +1269 1042 Conquest +1270 1042 King of the Hill +1271 1042 Territory Control +1272 1042 Conquest 2 +1273 1042 Conquest 3 +1274 1042 Territory Control 3 +1275 1042 King of the Hill 2 +1276 1042 King of the Hill 3 +1277 1042 Territory Control 2 +1278 1159 West +1279 1159 East +1282 1173 General +1283 1173 OnlineBattle +1284 1181 General +1285 1181 OnlineBattle +1287 1191 Beginner +1288 1191 Intermediate +1289 1191 Advanced +1290 948 North America +1291 948 Europe +1292 948 Asia Pacific +1301 948 Unpatched +1302 1195 Beginner +1303 1195 Intermediate +1304 1195 Advanced +1330 1212 West +1331 1212 East +1334 1190 Generak +1335 1190 OnlineBattle +1336 1213 West +1337 1213 East +1354 1207 Room 1 +1355 1207 Room 2 +1356 1207 Room 3 +1357 1207 Room 4 +1358 1207 Room 5 +1359 1207 Room 6 +1360 1207 Room 7 +1361 1207 Room 8 +1362 1207 Room 9 +1363 1207 Room 10 +1369 1 TestGroupRoom +1370 1224 North America +1371 1224 Europe +1372 1224 Asia Pacific +1381 1231 English +1382 1231 French +1383 1231 German +1384 1231 Italian +1385 1231 Spanish +1386 1231 Eastern US +1387 1231 Central US +1388 1231 Western US +1390 1196 Exhibition Single 00 +1391 1196 Exhibition Single 01 +1392 1196 Exhibition Single 02 +1393 1196 Exhibition Single 03 +1394 1196 Exhibition Single 04 +1395 1196 Exhibition Single 05 +1396 1196 Exhibition Single 06 +1397 1196 Exhibition Single 07 +1398 1196 Exhibition Single 08 +1399 1196 Exhibition Single 09 +1410 1196 Exhibition Tag 00 +1411 1196 Exhibition Tag 01 +1412 1196 Exhibition Tag 02 +1413 1196 Exhibition Tag 03 +1414 1196 Exhibition Tag 04 +1415 1196 Exhibition Tag 05 +1416 1196 Exhibition Tag 06 +1417 1196 Exhibition Tag 07 +1418 1196 Exhibition Tag 08 +1419 1196 Exhibition Tag 09 +1430 1196 Exhibition Main 00 +1431 1196 Exhibition Main 01 +1432 1196 Exhibition Main 02 +1433 1196 Exhibition Main 03 +1434 1196 Exhibition Main 04 +1435 1196 Exhibition Main 05 +1436 1196 Exhibition Main 06 +1437 1196 Exhibition Main 07 +1438 1196 Exhibition Main 08 +1439 1196 Exhibition Main 09 +1450 1196 Exhibition Voice 00 +1451 1196 Exhibition Voice 01 +1452 1196 Exhibition Voice 02 +1453 1196 Exhibition Voice 03 +1454 1196 Exhibition Voice 04 +1455 1196 Exhibition Voice 05 +1456 1196 Exhibition Voice 06 +1457 1196 Exhibition Voice 07 +1458 1196 Exhibition Voice 08 +1459 1196 Exhibition Voice 09 +1470 1196 TitleMatch Single 00 +1471 1196 TitleMatch Single 01 +1472 1196 TitleMatch Single 02 +1473 1196 TitleMatch Single 03 +1474 1196 TitleMatch Single 04 +1475 1196 TitleMatch Single 05 +1476 1196 TitleMatch Single 06 +1477 1196 TitleMatch Single 07 +1478 1196 TitleMatch Single 08 +1479 1196 TitleMatch Single 09 +1490 1196 TitleMatch Tag 00 +1491 1196 TitleMatch Tag 01 +1492 1196 TitleMatch Tag 02 +1493 1196 TitleMatch Tag 03 +1494 1196 TitleMatch Tag 04 +1495 1196 TitleMatch Tag 05 +1496 1196 TitleMatch Tag 06 +1497 1196 TitleMatch Tag 07 +1498 1196 TitleMatch Tag 08 +1499 1196 TitleMatch Tag 09 +1510 1196 TitleMatch Main 00 +1511 1196 TitleMatch Main 01 +1512 1196 TitleMatch Main 02 +1513 1196 TitleMatch Main 03 +1514 1196 TitleMatch Main 04 +1515 1196 TitleMatch Main 05 +1516 1196 TitleMatch Main 06 +1517 1196 TitleMatch Main 07 +1518 1196 TitleMatch Main 08 +1519 1196 TitleMatch Main 09 +1530 1196 TitleMatch Voice 00 +1531 1196 TitleMatch Voice 01 +1532 1196 TitleMatch Voice 02 +1533 1196 TitleMatch Voice 03 +1534 1196 TitleMatch Voice 04 +1535 1196 TitleMatch Voice 05 +1536 1196 TitleMatch Voice 06 +1537 1196 TitleMatch Voice 07 +1538 1196 TitleMatch Voice 08 +1539 1196 TitleMatch Voice 09 +1590 1196 Trade 00 +1591 1196 Trade 01 +1592 1196 Trade 02 +1593 1196 Trade 03 +1594 1196 Trade 04 +1595 1196 Trade 05 +1596 1196 Trade 06 +1597 1196 Trade 07 +1598 1196 Trade 08 +1599 1196 Trade 09 +1610 1228 Group Room 1 +1611 1228 Group Room 2 +1612 1228 Group Room 3 +1613 1122 Noodlers +1614 1265 West Coast +1616 1265 East Coast +1620 1266 QuickMatch +1621 1266 ChatRoom2 +1622 1266 ChatRoom1 +1623 1266 LobbyRoom1 +1624 1266 LobbyRoom2 +1652 237 First New One +1653 237 Second New One +1654 237 Third New One +1655 237 Fourth New One +1656 1197 Exhibition Single 00 +1657 1197 Exhibition Single 01 +1658 1197 Exhibition Single 02 +1659 1197 Exhibition Single 03 +1660 1197 Exhibition Single 04 +1661 1197 Exhibition Single 05 +1662 1197 Exhibition Single 06 +1663 1197 Exhibition Single 07 +1664 1197 Exhibition Single 08 +1665 1197 Exhibition Single 09 +1666 1197 Exhibition Tag 00 +1667 1197 Exhibition Tag 01 +1668 1197 Exhibition Tag 02 +1669 1197 Exhibition Tag 03 +1670 1197 Exhibition Tag 04 +1671 1197 Exhibition Tag 05 +1672 1197 Exhibition Tag 06 +1673 1197 Exhibition Tag 07 +1674 1197 Exhibition Tag 08 +1675 1197 Exhibition Tag 09 +1676 1197 Exhibition Main 00 +1677 1197 Exhibition Main 01 +1678 1197 Exhibition Main 02 +1679 1197 Exhibition Main 03 +1680 1197 Exhibition Main 04 +1681 1197 Exhibition Main 05 +1682 1197 Exhibition Main 06 +1683 1197 Exhibition Main 07 +1684 1197 Exhibition Main 08 +1685 1197 Exhibition Main 09 +1686 1197 Exhibition Voice 00 +1687 1197 Exhibition Voice 01 +1688 1197 Exhibition Voice 02 +1689 1197 Exhibition Voice 03 +1690 1197 Exhibition Voice 04 +1691 1197 Exhibition Voice 05 +1692 1197 Exhibition Voice 06 +1693 1197 Exhibition Voice 07 +1694 1197 Exhibition Voice 08 +1695 1197 Exhibition Voice 09 +1696 1197 TitleMatch Single 00 +1697 1197 TitleMatch Single 01 +1698 1197 TitleMatch Single 02 +1699 1197 TitleMatch Single 03 +1700 1197 TitleMatch Single 04 +1701 1197 TitleMatch Single 05 +1702 1197 TitleMatch Single 06 +1703 1197 TitleMatch Single 07 +1704 1197 TitleMatch Single 08 +1705 1197 TitleMatch Single 09 +1706 1197 TitleMatch Tag 00 +1707 1197 TitleMatch Tag 01 +1708 1197 TitleMatch Tag 02 +1709 1197 TitleMatch Tag 03 +1710 1197 TitleMatch Tag 04 +1711 1197 TitleMatch Tag 05 +1712 1197 TitleMatch Tag 06 +1713 1197 TitleMatch Tag 07 +1714 1197 TitleMatch Tag 08 +1715 1197 TitleMatch Tag 09 +1716 1197 TitleMatch Main 00 +1717 1197 TitleMatch Main 01 +1718 1197 TitleMatch Main 02 +1719 1197 TitleMatch Main 03 +1720 1197 TitleMatch Main 04 +1721 1197 TitleMatch Main 05 +1722 1197 TitleMatch Main 06 +1723 1197 TitleMatch Main 07 +1724 1197 TitleMatch Main 08 +1725 1197 TitleMatch Main 09 +1726 1197 TitleMatch Voice 00 +1727 1197 TitleMatch Voice 01 +1728 1197 TitleMatch Voice 02 +1729 1197 TitleMatch Voice 03 +1730 1197 TitleMatch Voice 04 +1731 1197 TitleMatch Voice 05 +1732 1197 TitleMatch Voice 06 +1733 1197 TitleMatch Voice 07 +1734 1197 TitleMatch Voice 08 +1735 1197 TitleMatch Voice 09 +1736 1197 Trade 00 +1737 1197 Trade 01 +1738 1197 Trade 02 +1739 1197 Trade 03 +1740 1197 Trade 04 +1741 1197 Trade 05 +1742 1197 Trade 06 +1743 1197 Trade 07 +1744 1197 Trade 08 +1745 1197 Trade 09 +1746 1198 Exhibition Single 00 +1747 1198 Exhibition Single 01 +1748 1198 Exhibition Single 02 +1749 1198 Exhibition Single 03 +1750 1198 Exhibition Single 04 +1751 1198 Exhibition Tag 00 +1752 1198 Exhibition Tag 01 +1753 1198 Exhibition Tag 02 +1754 1198 Exhibition Tag 03 +1755 1198 Exhibition Tag 04 +1756 1198 Exhibition Main 00 +1757 1198 Exhibition Main 01 +1758 1198 Exhibition Main 02 +1759 1198 Exhibition Main 03 +1760 1198 Exhibition Main 04 +1761 1198 Exhibition Voice 00 +1762 1198 Exhibition Voice 01 +1763 1198 Exhibition Voice 02 +1764 1198 Exhibition Voice 03 +1765 1198 Exhibition Voice 04 +1766 1198 TitleMatch Single 00 +1767 1198 TitleMatch Single 01 +1768 1198 TitleMatch Single 02 +1769 1198 TitleMatch Single 03 +1770 1198 TitleMatch Single 04 +1771 1198 TitleMatch Tag 00 +1772 1198 TitleMatch Tag 01 +1773 1198 TitleMatch Tag 02 +1774 1198 TitleMatch Tag 03 +1775 1198 TitleMatch Tag 04 +1776 1198 TitleMatch Main 00 +1777 1198 TitleMatch Main 01 +1778 1198 TitleMatch Main 02 +1779 1198 TitleMatch Main 03 +1780 1198 TitleMatch Main 04 +1781 1198 TitleMatch Voice 00 +1782 1198 TitleMatch Voice 01 +1783 1198 TitleMatch Voice 02 +1784 1198 TitleMatch Voice 03 +1785 1198 TitleMatch Voice 04 +1786 1198 Trade 00 +1787 1198 Trade 01 +1788 1198 Trade 02 +1789 1198 Trade 03 +1790 1198 Trade 04 +1793 1122 Jammers +1795 1066 Rebellion +1796 1066 Empire +1797 1066 Versus (1vs1) +1798 1066 Team Games +1799 1066 General +1800 1066 Rebellion +1801 1066 Empire +1802 1066 Versus (1vs1) +1803 1066 Team Games +1804 1066 General +1805 1066 Rebellion +1806 1066 Empire +1807 1066 Versus (1vs1) +1808 1066 Team Games +1809 1066 General +1810 1066 Rebellion +1811 1066 Empire +1812 1066 Versus (1vs1) +1813 1066 Team Games +1814 1066 General +1815 1066 Rebellion +1816 1066 Empire +1817 1066 Versus (1vs1) +1818 1066 Team Games +1819 1066 General +1820 1066 Rebellion +1821 1066 Empire +1822 1066 Versus (1vs1) +1823 1066 Team Games +1824 1066 General +1825 1066 Rebellion +1826 1066 Empire +1827 1066 Versus (1vs1) +1828 1066 Team Games +1829 1066 General +1830 1066 Rebellion +1831 1066 Empire +1837 1309 West +1838 1309 East +1839 1321 QuickMatch +1840 1321 ChatRoom1 +1842 1321 LobbyRoom1 +1843 1321 LobbyRoom2 +1844 1321 LobbyRoom3 +1845 1321 LobbyRoom4 +1846 1321 LobbyRoom5 +1847 1321 LobbyRoom6 +1848 1321 LobbyRoom7 +1849 1321 LobbyRoom8 +1850 1283 Western US +1851 1283 Central US +1852 1283 Eastern US +1853 1283 German +1854 1283 Spanish +1855 1283 English +1856 1283 Italian +1857 1283 French +1858 1321 LobbyRoom9 +1859 1122 Chop Masters +1860 1160 USA +1861 1160 Europe +1862 1190 Debug +1863 1354 Larry +1864 1354 Curly +1865 1354 Moe +1866 1376 West +1867 1376 East +1868 1391 Action +1869 1391 Story +1870 1391 Story Lite +1871 1391 Role Play +1872 1391 Team +1873 1391 Melee +1874 1391 Arena +1875 1391 Social +1876 1391 Alternative +1877 1391 PW Story +1878 1391 PW Action +1879 1391 Solo +1880 1391 Tech Support +1881 1122 eJamming Test +1882 1401 Novices +1883 1401 Veterans +1884 1401 English +1885 1401 French +1886 1401 German +1887 1401 Italian +1888 1401 Spanish +1889 1396 Africa +1890 1396 America +1891 1396 Asia +1892 1396 Europe +1893 1396 Pacific +1894 1396 The Empire +1895 1396 Hordes of Chaos +1896 1396 High Elves +1897 1396 Skaven +1898 1396 Other +1899 1396 Kicked +1900 1396 Banned +1901 1422 QuickMatch +1902 1422 ChatRoom1 +1904 1426 Beginner +1905 1426 Intermediate +1906 1426 Advanced +1907 1411 Room 1 +1908 1411 Room 2 +1909 1411 Room 3 +1910 1411 Room 4 +1911 1411 Room 5 +1912 1411 Room 6 +1913 1411 Room 7 +1914 1411 Room 8 +1915 1411 Room 9 +1916 1411 Room 10 +1917 1463 QuickMatch +1918 1463 ChatRoom1 +1919 1463 LobbyRoom1 +1920 1463 LobbyRoom2 +1921 1463 LobbyRoom3 +1922 1463 LobbyRoom4 +1923 1463 LobbyRoom5 +1924 1463 LobbyRoom6 +1925 1463 LobbyRoom7 +1926 1463 LobbyRoom8 +1927 1463 LobbyRoom9 +1928 1398 Africa +1929 1398 America +1930 1398 Asia +1931 1398 Europe +1932 1398 Pacific +1933 1398 The Empire +1934 1398 Hordes of Chaos +1935 1398 High Elves +1936 1398 Skaven +1937 1398 Other +1938 1398 Kicked +1939 1398 Banned +1940 1479 West +1941 1479 East +1942 1481 East +1943 1481 West +1944 1463 QuickMatch2 +1945 1483 America +1946 1483 Asia +1947 1483 Europe +1948 1321 QuickMatch2 +1956 1514 Newbies +1957 1514 Experienced Players +1958 1514 Strategists +1959 1515 Newbies +1960 1515 Experienced Players +1961 1515 Strategists +1962 1523 Skirmish +1963 1523 World Domination +1964 1524 Skirmish +1965 1524 World Domination +1966 1577 Rebellion +1967 1577 Empire +1968 1577 Versus (1vs1) +1969 1577 Team Games +1970 1577 General +1971 1577 Rebellion +1972 1577 Empire +1973 1577 Versus (1vs1) +1974 1577 Team Games +1975 1577 General +1976 1577 Rebellion +1977 1577 Empire +1978 1577 Versus (1vs1) +1979 1577 Team Games +1980 1577 General +1981 1577 Rebellion +1982 1577 Empire +1983 1577 Versus (1vs1) +1984 1577 Team Games +1985 1577 General +1986 1577 Rebellion +1987 1577 Empire +1988 1577 Versus (1vs1) +1989 1577 Team Games +1990 1577 General +1991 1577 Rebellion +1992 1577 Empire +1993 1577 Versus (1vs1) +1994 1577 Team Games +1995 1577 General +1996 1577 Rebellion +1997 1577 Empire +1998 1577 Versus (1vs1) +1999 1577 Team Games +2000 1577 General +2001 1577 Rebellion +2002 1577 Empire +2003 1631 Skirmish +2004 1631 World Domination +2027 1714 ChatRoom1 +2028 1714 QuickMatch +2029 1714 LobbyRoom:1 +2030 1714 LobbyRoom:2 +2031 1714 LobbyRoom:3 +2032 1714 LobbyRoom:4 +2033 1714 LobbyRoom:5 +2034 1714 LobbyRoom:6 +2035 1714 LobbyRoom:7 +2036 1714 LobbyRoom:8 +2037 1714 LobbyRoom:9 +2038 1714 LobbyRoom:10 +2039 1714 LobbyRoom:11 +2040 1714 LobbyRoom:12 +2041 1714 LobbyRoom:13 +2042 1714 LobbyRoom:14 +2043 1714 LobbyRoom:15 +2044 1714 LobbyRoom:16 +2045 1714 LobbyBeginners:1 +2046 1714 LobbyHardcore:1 +2047 1714 LobbyClan:1 +2048 1714 LobbyClan:2 +2049 1714 LobbyTournaments:1 +2050 1714 LobbyTournaments:2 +2051 1714 LobbyBattlecast:1 +2052 1714 LobbyCustomMap:1 +2053 1714 LobbyCustomMap:2 +2054 1714 LobbyCompStomp:1 +2055 1714 LobbyGerman:1 +2056 1714 LobbyGerman:2 +2057 1714 LobbyKorean:1 +2058 1714 LobbyFrench:1 +2059 1422 LobbyRoom:1 +2060 1422 LobbyRoom:2 +2061 1422 LobbyRoom:3 +2062 1422 LobbyRoom:4 +2063 1422 LobbyRoom:5 +2064 1422 LobbyRoom:6 +2065 1422 LobbyRoom:7 +2066 1422 LobbyRoom:8 +2067 1422 LobbyRoom:9 +2068 1422 LobbyRoom:10 +2069 1422 LobbyRoom:11 +2070 1422 LobbyRoom:12 +2071 1422 LobbyRoom:13 +2072 1422 LobbyRoom:14 +2073 1422 LobbyRoom:15 +2074 1422 LobbyRoom:16 +2075 1422 LobbyBeginners:1 +2076 1422 LobbyHardcore:1 +2077 1422 LobbyClan:1 +2078 1422 LobbyClan:2 +2079 1422 LobbyTournaments:1 +2080 1422 LobbyTournaments:2 +2081 1422 LobbyBattlecast:1 +2082 1422 LobbyCustomMap:1 +2083 1422 LobbyCustomMap:2 +2084 1422 LobbyCompStomp:1 +2085 1422 LobbyGerman:1 +2086 1422 LobbyGerman:2 +2087 1422 LobbyKorean:1 +2088 1422 LobbyFrench:1 +2089 1774 Room 1 +2090 1774 Room 2 +2091 1774 Room 3 +2092 1774 Room 4 +2093 1774 Room 5 +2094 1774 Room 6 +2095 1774 Room 7 +2096 1774 Room 8 +2097 1774 Room 9 +2098 1774 Room 10 +2099 1814 ChatRoom1 +2100 1814 QuickMatch +2101 1814 LobbyRoom:1 +2102 1814 LobbyRoom:2 +2103 1814 LobbyRoom:3 +2104 1814 LobbyRoom:4 +2105 1814 LobbyRoom:5 +2106 1814 LobbyRoom:6 +2107 1814 LobbyRoom:7 +2108 1814 LobbyRoom:8 +2109 1814 LobbyRoom:9 +2110 1814 LobbyRoom:10 +2111 1814 LobbyRoom:11 +2112 1814 LobbyRoom:12 +2113 1814 LobbyRoom:13 +2114 1814 LobbyRoom:14 +2115 1814 LobbyRoom:15 +2116 1814 LobbyRoom:16 +2117 1814 LobbyBeginners:1 +2118 1814 LobbyHardcore:1 +2119 1814 LobbyClan:1 +2120 1814 LobbyClan:2 +2121 1814 LobbyTournaments:1 +2122 1814 LobbyTournaments:2 +2123 1814 LobbyBattlecast:1 +2124 1814 LobbyCustomMap:1 +2125 1814 LobbyCustomMap:2 +2126 1814 LobbyCompStomp:1 +2127 1814 LobbyGerman:1 +2128 1814 LobbyGerman:2 +2129 1814 LobbyKorean:1 +2130 1814 LobbyFrench:1 +2131 1884 Room 1 +2132 1884 Room 2 +2133 1884 Room 3 +2134 1884 Room 4 +2135 1884 Room 5 +2136 1884 Room 6 +2137 1884 Room 7 +2138 1884 Room 8 +2139 1884 Room 9 +2140 1884 Room 10 +2141 1923 Africa +2142 1923 America +2143 1923 Asia +2144 1923 Europe +2145 1923 Pacific +2146 1923 The Empire +2147 1923 Hordes of Chaos +2148 1923 High Elves +2149 1923 Skaven +2150 1923 Other +2151 1923 Dark Elf +2152 1923 Orc +2153 1923 Kicked +2154 1923 Banned +2155 1514 Sparta FX +2156 1979 QuickMatch +2157 1979 LobbyRoom:1 +2158 1979 ChatRoom1 +2159 2100 QuickMatch +2160 2100 LobbyRoom:1 +2161 2100 ChatRoom1 +2162 2094 QuickMatch +2163 2094 LobbyRoom:1 +2164 2094 ChatRoom1 +2166 2128 LobbyRoom:1 +2167 2128 LobbyRoom:2 +2168 2128 LobbyRoom:3 +2169 2128 LobbyRoom:4 +2170 2128 LobbyRoom:5 +2171 2128 LobbyKorean:1 +2172 2128 LobbyFrench:1 +2173 2128 LobbyGerman:2 +2174 2128 LobbyGerman:1 +2175 2128 LobbyBattlecast:1 +2176 2128 LobbyRoom:6 +2177 2128 ChatRoom1 +2178 2128 LobbyRoom:8 +2179 2128 LobbyRoom:11 +2180 2128 LobbyRoom:7 +2181 2128 LobbyRoom:9 +2182 2128 LobbyRoom:12 +2183 2128 LobbyRoom:10 +2184 2128 LobbyClan:1 +2185 2128 LobbyRoom:13 +2186 2128 LobbyClan:2 +2187 2128 LobbyRoom:14 +2188 2128 LobbyRoom:16 +2189 2128 LobbyRoom:15 +2190 2128 LobbyBeginners:1 +2191 2128 LobbyTournaments:1 +2192 2128 LobbyCompStomp:1 +2193 2128 LobbyHardcore:1 +2194 2128 LobbyCustomMap:1 +2195 2128 LobbyCustomMap:2 +2196 2128 LobbyTournaments:2 +2198 2128 ChatRoom1 +2200 2130 LobbyRoom:1 +2201 2130 LobbyRoom:2 +2202 2130 LobbyRoom:3 +2203 2130 LobbyRoom:4 +2204 2130 LobbyRoom:5 +2205 2130 LobbyKorean:1 +2206 2130 LobbyFrench:1 +2207 2130 LobbyGerman:2 +2208 2130 LobbyGerman:1 +2209 2130 LobbyBattlecast:1 +2210 2130 LobbyRoom:6 +2211 2130 ChatRoom1 +2212 2130 LobbyRoom:8 +2213 2130 LobbyRoom:11 +2214 2130 LobbyRoom:7 +2215 2130 LobbyRoom:9 +2216 2130 LobbyRoom:12 +2217 2130 LobbyRoom:10 +2218 2130 LobbyClan:1 +2219 2130 LobbyRoom:13 +2220 2130 LobbyClan:2 +2221 2130 LobbyRoom:14 +2222 2130 LobbyRoom:16 +2223 2130 LobbyRoom:15 +2224 2130 LobbyBeginners:1 +2225 2130 LobbyTournaments:1 +2226 2130 LobbyCompStomp:1 +2227 2130 LobbyHardcore:1 +2228 2130 LobbyCustomMap:1 +2229 2130 LobbyCustomMap:2 +2230 2130 LobbyTournaments:2 +2232 2130 ChatRoom1 +2233 2160 QuickMatch +2234 2160 LobbyTournaments:2 +2235 2160 LobbyTournaments:1 +2236 2160 LobbyRoom:9 +2237 2160 LobbyRoom:8 +2238 2160 LobbyRoom:7 +2239 2160 LobbyRoom:6 +2240 2160 LobbyRoom:5 +2241 2160 LobbyRoom:4 +2242 2160 LobbyRoom:3 +2243 2160 LobbyRoom:2 +2244 2160 LobbyRoom:16 +2245 2160 LobbyRoom:15 +2246 2160 LobbyRoom:14 +2247 2160 LobbyRoom:13 +2248 2160 LobbyRoom:12 +2249 2160 LobbyRoom:11 +2250 2160 LobbyRoom:10 +2251 2160 LobbyRoom:1 +2252 2160 LobbyKorean:1 +2253 2160 LobbyHardcore:1 +2254 2160 LobbyGerman:2 +2255 2160 LobbyGerman:1 +2256 2160 LobbyFrench:1 +2257 2160 LobbyCustomMap:2 +2258 2160 LobbyCustomMap:1 +2259 2160 LobbyCompStomp:1 +2260 2160 LobbyClan:2 +2261 2160 LobbyClan:1 +2262 2160 LobbyBeginners:1 +2263 2160 LobbyBattlecast:1 +2264 2160 ChatRoom1 +2265 1979 Russia:1 +2266 2238 LobbyRoom:1 +2267 2238 LobbyRoom:2 +2268 2238 LobbyRoom:3 +2269 2238 LobbyRoom:4 +2270 2238 LobbyRoom:5 +2271 2238 LobbyRoom:6 +2272 2238 LobbyRoom:7 +2273 2238 LobbyRoom:8 +2274 2238 LobbyRoom:9 +2275 2238 LobbyRoom:10 +2276 2238 LobbyRoom:11 +2277 2238 LobbyRoom:12 +2278 2238 LobbyRoom:13 +2279 2238 LobbyRoom:14 +2280 2238 LobbyRoom:15 +2281 2238 LobbyRoom:16 +2282 2238 LobbyBeginners:1 +2283 2238 LobbyHardcore:1 +2284 2238 LobbyClan:1 +2285 2238 LobbyClan:2 +2286 2238 LobbyTournaments:1 +2287 2238 LobbyTournaments:2 +2288 2238 LobbyBattlecast:1 +2289 2238 LobbyCustomMap:1 +2290 2238 LobbyCustomMap:2 +2291 2238 LobbyCompStomp:1 +2292 2238 LobbyGerman:1 +2293 2238 LobbyGerman:2 +2294 2238 LobbyKorean:1 +2295 2238 LobbyFrench:1 +2296 2238 ChatRoom1 +2297 1814 Russia:1 +2298 2298 Deathmatch +2299 2298 RopeRace +2300 2298 Forts +2301 2298 Triathlon +2302 2313 Beginner +2303 2313 Intermediate +2304 2313 Expert +2305 2374 ChatRoom1 +2306 2374 LobbyRoom:1 +2307 2374 LobbyRoom:2 +2308 2374 LobbyRoom:3 +2309 2374 LobbyRoom:4 +2310 2374 LobbyRoom:5 +2311 2374 LobbyKorean:1 +2312 2374 LobbyFrench:1 +2313 2374 LobbyGerman:2 +2314 2374 LobbyGerman:1 +2315 2374 LobbyBattlecast:1 +2316 2374 LobbyRoom:6 +2317 2374 ChatRoom1 +2318 2374 LobbyRoom:8 +2319 2374 LobbyRoom:11 +2320 2374 LobbyRoom:7 +2321 2374 LobbyRoom:9 +2322 2374 LobbyRoom:12 +2323 2374 LobbyRoom:10 +2324 2374 LobbyClan:1 +2325 2374 LobbyRoom:13 +2326 2374 LobbyClan:2 +2327 2374 LobbyRoom:14 +2328 2374 LobbyRoom:16 +2329 2374 LobbyRoom:15 +2330 2374 LobbyBeginners:1 +2331 2374 LobbyTournaments:1 +2332 2374 LobbyCompStomp:1 +2333 2374 LobbyHardcore:1 +2334 2374 LobbyCustomMap:1 +2335 2374 LobbyCustomMap:2 +2336 2374 LobbyTournaments:2 +2337 2130 LobbyRussian:1 +2338 2130 LobbyTaiwan:1 +2339 2128 LobbyRoom:17 +2340 2128 LobbyRoom:18 +2341 2128 LobbyRoom:19 +2342 2128 LobbyRoom:20 +2343 2128 LobbyRoom:21 +2344 2128 LobbyCoop:1 +2345 2128 LobbyCoop:2 +2346 2128 LobbyCoop:3 +2347 2128 LobbyCoop:4 +2348 2128 LobbyCoop:5 +2349 2128 LobbyRussian:1 +2350 2128 LobbyTaiwan:1 +2351 2298 WarRoom +2352 2298 TacticsMode +2353 2259 room1 +2354 2259 room2 +2355 2259 room3 +2356 2259 room4 +2357 2128 LobbySpanish:1 +2358 2714 room1 +2359 2714 room2 +2360 2714 room3 +2361 2714 room4 +2362 2246 ChatMonTesting +2363 2712 room1 +2364 2712 room2 +2365 2712 room3 +2366 2712 room4 +2367 2705 North America +2368 2705 Europe +2369 2705 Brazil +2370 2840 FX Sparta II +2371 2706 North America +2372 2706 Europe +2373 2706 Brazil +2374 2707 North America +2375 2707 Europe +2376 2707 Brazil +2377 2032 North America +2378 2032 Europe +2379 2032 Brazil +2380 2034 North America +2381 2034 Europe +2382 2034 Brazil +2383 2035 North America +2384 2035 Europe +2385 2035 Brazil +2386 706 Main Lobby +2387 1003 ThMods.com +2388 917 Main Lobby +2389 1128 Main Lobby +2390 1307 ThMods.com +2391 1005 ThMods.com +2392 467 Main Lobby +2393 600 Main Lobby +2399 1003 Lobby 2 +\. + + +-- +-- TOC entry 3448 (class 0 OID 16410) +-- Dependencies: 217 +-- Data for Name: messages; Type: TABLE DATA; Schema: unispy; Owner: - +-- + +COPY unispy.messages (messageid, namespaceid, type, "from", "to", date, message) FROM stdin; +\. + + +-- +-- TOC entry 3450 (class 0 OID 16417) +-- Dependencies: 219 +-- Data for Name: partner; Type: TABLE DATA; Schema: unispy; Owner: - +-- + +COPY unispy.partner (partnerid, partnername) FROM stdin; +0 UniSpy +95 Crytek +\. + + +-- +-- TOC entry 3451 (class 0 OID 16422) +-- Dependencies: 220 +-- Data for Name: profiles; Type: TABLE DATA; Schema: unispy; Owner: - +-- + +COPY unispy.profiles (profileid, userid, nick, serverflag, status, statstring, location, firstname, lastname, publicmask, latitude, longitude, aim, picture, occupationid, incomeid, industryid, marriedid, childcount, interests1, ownership1, connectiontype, sex, zipcode, countrycode, homepage, birthday, birthmonth, birthyear, icquin, quietflags, streetaddr, streeaddr, city, cpubrandid, cpuspeed, memory, videocard1string, videocard1ram, videocard2string, videocard2ram, subscription, adminrights) FROM stdin; +1 1 spyguy 0 0 I love UniSpy earth spy guy 0 0 0 0 0 0 0 0 0 0 0 0 0 00000 1 unispy.org 0 0 0 0 0 \N \N \N 0 0 0 \N 0 \N 0 0 0 +2 2 uniguy 0 0 I love UniSpy earth uni guy 0 0 0 0 0 0 0 0 0 0 0 0 0 00000 1 unispy.org 0 0 0 0 0 \N \N \N 0 0 0 \N 0 \N 0 0 0 +3 3 gptest1 0 0 I love UniSpy \N \N \N 0 0 0 0 0 0 0 0 0 0 0 0 0 00000 1 unispy.org 0 0 0 0 0 \N \N \N 0 0 0 \N 0 \N 0 0 0 +4 4 gptest2 0 0 I love UniSpy \N \N \N 0 0 0 0 0 0 0 0 0 0 0 0 0 00000 1 unispy.org 0 0 0 0 0 \N \N \N 0 0 0 \N 0 \N 0 0 0 +5 5 gptest3 0 0 I love UniSpy \N \N \N 0 0 0 0 0 0 0 0 0 0 0 0 0 00000 1 unispy.org 0 0 0 0 0 \N \N \N 0 0 0 \N 0 \N 0 0 0 +\. + + +-- +-- TOC entry 3453 (class 0 OID 16460) +-- Dependencies: 222 +-- Data for Name: pstorage; Type: TABLE DATA; Schema: unispy; Owner: - +-- + +COPY unispy.pstorage (pstorageid, profileid, ptype, dindex, data) FROM stdin; +\. + + +-- +-- TOC entry 3455 (class 0 OID 16466) +-- Dependencies: 224 +-- Data for Name: sakestorage; Type: TABLE DATA; Schema: unispy; Owner: - +-- + +COPY unispy.sakestorage (sakestorageid, tableid) FROM stdin; +\. + + +-- +-- TOC entry 3457 (class 0 OID 16472) +-- Dependencies: 226 +-- Data for Name: subprofiles; Type: TABLE DATA; Schema: unispy; Owner: - +-- + +COPY unispy.subprofiles (subprofileid, profileid, uniquenick, namespaceid, partnerid, productid, gamename, cdkeyenc, firewall, port, authtoken) FROM stdin; +1 1 spyguy 1 0 0 gmtest \N 0 0 example_token +2 2 uniguy 1 0 0 gmtest \N 0 0 \N +3 3 gptest1 1 0 0 gmtest \N 0 0 \N +4 4 gptest2 1 0 0 gmtest \N 0 0 \N +5 5 gptest3 1 0 0 gmtest \N 0 0 \N +\. + + +-- +-- TOC entry 3459 (class 0 OID 16482) +-- Dependencies: 228 +-- Data for Name: users; Type: TABLE DATA; Schema: unispy; Owner: - +-- + +COPY unispy.users (userid, email, password, emailverified, lastip, lastonline, createddate, banned, deleted) FROM stdin; +1 spyguy@gamespy.com 4a7d1ed414474e4033ac29ccb8653d9b t \N 2022-01-19 20:01:49.828006 2022-01-19 20:01:49.828006 f f +2 uni@unispy.org 4a7d1ed414474e4033ac29ccb8653d9b t \N 2022-01-19 20:02:57.595514 2022-01-19 20:02:57.595514 f f +3 gptestc1@gptestc.com c6d525669e64438c9aa156a0cc80cd14 t \N 2022-01-19 20:03:44.754069 2022-01-19 20:03:44.754069 f f +4 gptestc2@gptestc.com c6d525669e64438c9aa156a0cc80cd14 t \N 2022-01-19 20:03:44.761986 2022-01-19 20:03:44.761986 f f +5 gptestc3@gptestc.com c6d525669e64438c9aa156a0cc80cd14 t \N 2022-01-19 20:03:44.764527 2022-01-19 20:03:44.764527 f f +\. + + +-- +-- TOC entry 3488 (class 0 OID 0) +-- Dependencies: 210 +-- Name: addrequests_addrequestid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- + +SELECT pg_catalog.setval('unispy.addrequests_addrequestid_seq', 1, false); + + +-- +-- TOC entry 3489 (class 0 OID 0) +-- Dependencies: 212 +-- Name: blocked_blockid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- + +SELECT pg_catalog.setval('unispy.blocked_blockid_seq', 1, false); + + +-- +-- TOC entry 3490 (class 0 OID 0) +-- Dependencies: 214 +-- Name: friends_friendid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- + +SELECT pg_catalog.setval('unispy.friends_friendid_seq', 1, false); + + +-- +-- TOC entry 3491 (class 0 OID 0) +-- Dependencies: 218 +-- Name: messages_messageid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- + +SELECT pg_catalog.setval('unispy.messages_messageid_seq', 1, false); + + +-- +-- TOC entry 3492 (class 0 OID 0) +-- Dependencies: 221 +-- Name: profiles_profileid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- + +SELECT pg_catalog.setval('unispy.profiles_profileid_seq', 5, true); + + +-- +-- TOC entry 3493 (class 0 OID 0) +-- Dependencies: 223 +-- Name: pstorage_pstorageid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- + +SELECT pg_catalog.setval('unispy.pstorage_pstorageid_seq', 1, true); + + +-- +-- TOC entry 3494 (class 0 OID 0) +-- Dependencies: 225 +-- Name: sakestorage_sakestorageid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- + +SELECT pg_catalog.setval('unispy.sakestorage_sakestorageid_seq', 1, false); + + +-- +-- TOC entry 3495 (class 0 OID 0) +-- Dependencies: 227 +-- Name: subprofiles_subprofileid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- + +SELECT pg_catalog.setval('unispy.subprofiles_subprofileid_seq', 5, true); + + +-- +-- TOC entry 3496 (class 0 OID 0) +-- Dependencies: 229 +-- Name: users_userid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- + +SELECT pg_catalog.setval('unispy.users_userid_seq', 5, true); + + +-- +-- TOC entry 3271 (class 2606 OID 16503) +-- Name: addrequests addrequests_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.addrequests + ADD CONSTRAINT addrequests_pk PRIMARY KEY (addrequestid); + + +-- +-- TOC entry 3273 (class 2606 OID 16505) +-- Name: blocked blocked_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.blocked + ADD CONSTRAINT blocked_pk PRIMARY KEY (blockid); + + +-- +-- TOC entry 3275 (class 2606 OID 16507) +-- Name: friends friends_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.friends + ADD CONSTRAINT friends_pk PRIMARY KEY (friendid); + + +-- +-- TOC entry 3277 (class 2606 OID 16509) +-- Name: games games_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.games + ADD CONSTRAINT games_pk PRIMARY KEY (gameid); + + +-- +-- TOC entry 3279 (class 2606 OID 16511) +-- Name: grouplist grouplist_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.grouplist + ADD CONSTRAINT grouplist_pk PRIMARY KEY (groupid); + + +-- +-- TOC entry 3281 (class 2606 OID 16513) +-- Name: messages messages_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.messages + ADD CONSTRAINT messages_pk PRIMARY KEY (messageid); + + +-- +-- TOC entry 3283 (class 2606 OID 16515) +-- Name: partner partner_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.partner + ADD CONSTRAINT partner_pk PRIMARY KEY (partnerid); + + +-- +-- TOC entry 3285 (class 2606 OID 16517) +-- Name: profiles profiles_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.profiles + ADD CONSTRAINT profiles_pk PRIMARY KEY (profileid); + + +-- +-- TOC entry 3287 (class 2606 OID 16519) +-- Name: pstorage pstorage_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.pstorage + ADD CONSTRAINT pstorage_pk PRIMARY KEY (pstorageid); + + +-- +-- TOC entry 3289 (class 2606 OID 16521) +-- Name: sakestorage sakestorage_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.sakestorage + ADD CONSTRAINT sakestorage_pk PRIMARY KEY (sakestorageid); + + +-- +-- TOC entry 3291 (class 2606 OID 16523) +-- Name: subprofiles subprofiles_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.subprofiles + ADD CONSTRAINT subprofiles_pk PRIMARY KEY (subprofileid); + + +-- +-- TOC entry 3293 (class 2606 OID 16525) +-- Name: users users_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.users + ADD CONSTRAINT users_pk PRIMARY KEY (userid); + + +-- +-- TOC entry 3294 (class 2606 OID 16526) +-- Name: addrequests addrequests_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.addrequests + ADD CONSTRAINT addrequests_fk FOREIGN KEY (profileid) REFERENCES unispy.profiles(profileid); + + +-- +-- TOC entry 3295 (class 2606 OID 16531) +-- Name: blocked blocked_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.blocked + ADD CONSTRAINT blocked_fk FOREIGN KEY (profileid) REFERENCES unispy.profiles(profileid); + + +-- +-- TOC entry 3296 (class 2606 OID 16536) +-- Name: friends friends_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.friends + ADD CONSTRAINT friends_fk FOREIGN KEY (profileid) REFERENCES unispy.profiles(profileid); + + +-- +-- TOC entry 3297 (class 2606 OID 16541) +-- Name: grouplist grouplist_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.grouplist + ADD CONSTRAINT grouplist_fk FOREIGN KEY (gameid) REFERENCES unispy.games(gameid); + + +-- +-- TOC entry 3298 (class 2606 OID 16546) +-- Name: profiles profiles_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.profiles + ADD CONSTRAINT profiles_fk FOREIGN KEY (userid) REFERENCES unispy.users(userid); + + +-- +-- TOC entry 3299 (class 2606 OID 16551) +-- Name: pstorage pstorage_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.pstorage + ADD CONSTRAINT pstorage_fk FOREIGN KEY (profileid) REFERENCES unispy.profiles(profileid); + + +-- +-- TOC entry 3300 (class 2606 OID 16556) +-- Name: subprofiles subprofiles_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - +-- + +ALTER TABLE ONLY unispy.subprofiles + ADD CONSTRAINT subprofiles_fk FOREIGN KEY (profileid) REFERENCES unispy.profiles(profileid); + + +-- Completed on 2022-03-02 20:15:24 CET + +-- +-- PostgreSQL database dump complete +-- diff --git a/config.json b/common/config.json similarity index 100% rename from config.json rename to common/config.json diff --git a/src/.devcontainer/Dockerfile b/src/.devcontainer/Dockerfile deleted file mode 100644 index 5ec40a9c7..000000000 --- a/src/.devcontainer/Dockerfile +++ /dev/null @@ -1,12 +0,0 @@ -FROM node:18 - -# Install basic development tools -RUN apt update && apt install -y less man-db sudo -RUN apt install openssh-server -y -# Ensure default `node` user has access to `sudo` -ARG USERNAME=node -RUN echo $USERNAME ALL=\(root\) NOPASSWD:ALL > /etc/sudoers.d/$USERNAME \ - && chmod 0440 /etc/sudoers.d/$USERNAME - -# Set `DEVCONTAINER` environment variable to help with orientation -ENV DEVCONTAINER=true \ No newline at end of file diff --git a/src/.devcontainer/devcontainer.json b/src/.devcontainer/devcontainer.json deleted file mode 100644 index 5e217ff0b..000000000 --- a/src/.devcontainer/devcontainer.json +++ /dev/null @@ -1,11 +0,0 @@ -// See https://containers.dev/implementors/json_reference/ for configuration reference -{ - "name": "Untitled Node.js project", - "build": { - "dockerfile": "Dockerfile" - }, -"remoteUser": "node", -"features": { - "ghcr.io/devcontainers/features/git:1": {} -} -} diff --git a/src/backends/gamespy/protocols/natneg/data.py b/src/backends/gamespy/protocols/natneg/data.py index 57300f77d..b1713abd9 100644 --- a/src/backends/gamespy/protocols/natneg/data.py +++ b/src/backends/gamespy/protocols/natneg/data.py @@ -1,5 +1,5 @@ from servers.natneg.contracts.requests import InitRequest -from backends.gamespy.protocols.natneg.aggregates.init_packet_info import InitPacketInfo +from servers.natneg.aggregations.init_packet_info import InitPacketInfo def store_init_packet(request: InitRequest): diff --git a/src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py b/src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py index 44fc91ff7..b0e172d17 100644 --- a/src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py +++ b/src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py @@ -1,7 +1,5 @@ -from dataclasses import dataclass from typing import Union -import jsonschema from pydantic import UUID4, BaseModel from servers.presence_connection_manager.aggregates.user_status import UserStatus from servers.presence_connection_manager.aggregates.user_status_info import ( @@ -155,14 +153,4 @@ class UpdateUiRequest(RequestBase): if __name__ == "__main__": - from jsonschema import validate - - data = { - "server_id": "550e8400-e29b-41d4-a716-446655440000", - "raw_request": "hello", - "friend_profile_id": 1, - "reason": "hello", - } - r = AddBuddyRequest(**data) - r.validate() pass diff --git a/src/backends/routers/gamespy/natneg.py b/src/backends/routers/gamespy/natneg.py index e69de29bb..b594b9a81 100644 --- a/src/backends/routers/gamespy/natneg.py +++ b/src/backends/routers/gamespy/natneg.py @@ -0,0 +1 @@ +from fastapi import FastAPI diff --git a/src/backends/routers/gamespy/presence_connection_manager.py b/src/backends/routers/gamespy/presence_connection_manager.py index 5ed5b70cf..cd5767df9 100644 --- a/src/backends/routers/gamespy/presence_connection_manager.py +++ b/src/backends/routers/gamespy/presence_connection_manager.py @@ -6,7 +6,7 @@ app = FastAPI() -@app.route(f"/{PRESENCE_CONNECTION_MANAGER}/login", methods=["POST"]) -def pcm_login(request: LoginRequest): +@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/login") +def login(request: LoginRequest): pass diff --git a/src/backends/routers/gamespy/presence_search_player.py b/src/backends/routers/gamespy/presence_search_player.py index e69de29bb..b594b9a81 100644 --- a/src/backends/routers/gamespy/presence_search_player.py +++ b/src/backends/routers/gamespy/presence_search_player.py @@ -0,0 +1 @@ +from fastapi import FastAPI diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index e69de29bb..b594b9a81 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -0,0 +1 @@ +from fastapi import FastAPI diff --git a/src/backends/routers/gamespy/server_browser.py b/src/backends/routers/gamespy/server_browser.py index e69de29bb..b594b9a81 100644 --- a/src/backends/routers/gamespy/server_browser.py +++ b/src/backends/routers/gamespy/server_browser.py @@ -0,0 +1 @@ +from fastapi import FastAPI diff --git a/src/backends/routers/gamespy/webservices.py b/src/backends/routers/gamespy/webservices.py index e69de29bb..b594b9a81 100644 --- a/src/backends/routers/gamespy/webservices.py +++ b/src/backends/routers/gamespy/webservices.py @@ -0,0 +1 @@ +from fastapi import FastAPI diff --git a/src/docker-compose.yaml b/src/docker-compose.yaml index 09cc38a09..759d08f22 100644 --- a/src/docker-compose.yaml +++ b/src/docker-compose.yaml @@ -18,7 +18,7 @@ services: MONGO_INITDB_ROOT_PASSWORD: 123456 postgresql: - image: postgres + image: postgres:14 ports: - "5432:5432" restart: always @@ -26,3 +26,5 @@ services: POSTGRES_USER: unispy POSTGRES_PASSWORD: 123456 POSTGRES_DB: unispy + volumes: + - ../postgre_creation.sql:/docker-entrypoint-initdb.d/postgre_creation.sql diff --git a/src/requirement.txt b/src/requirements.txt similarity index 100% rename from src/requirement.txt rename to src/requirements.txt diff --git a/src/servers/game_traffic_relay/applications/router.py b/src/servers/game_traffic_relay/applications/router.py index 8b6a9058d..25481dee5 100644 --- a/src/servers/game_traffic_relay/applications/router.py +++ b/src/servers/game_traffic_relay/applications/router.py @@ -1,10 +1,10 @@ -from flask import Flask, request +from fastapi import FastAPI from backends.urls import * +from servers.game_traffic_relay.contracts.general import InitPacketInfo -app = Flask(__name__) +app = FastAPI() -@app.route(f"/GetNatNegotiationInfo", methods=["POST"]) -async def get_natneg_info(): +@app.post(f"/GetNatNegotiationInfo") +async def get_natneg_info(request: InitPacketInfo): data = request.json - \ No newline at end of file diff --git a/src/servers/game_traffic_relay/contracts/general.py b/src/servers/game_traffic_relay/contracts/general.py new file mode 100644 index 000000000..ce02eb50c --- /dev/null +++ b/src/servers/game_traffic_relay/contracts/general.py @@ -0,0 +1,17 @@ +from pydantic import BaseModel, UUID4 + +from servers.natneg.enums.general import NatClientIndex, NatPortType + + +class InitPacketInfo(BaseModel): + server_id: UUID4 + cookie: int + version: int + port_type: NatPortType + client_index: NatClientIndex + game_name: str + use_game_port: bool + public_ip: str + public_port: int + private_ip: str + private_port: int From b45133eebc60ec3784f363e1f672273c8e1aa547 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 2 Jul 2024 08:48:48 +0000 Subject: [PATCH 072/231] refactor(web): fix parsing issues --- src/.devcontainer/devcontainer.json | 29 ++++ src/.github/dependabot.yml | 12 ++ src/.vscode/settings.json | 3 + src/library/abstractions/client.py | 20 +-- src/requirements.txt | 3 +- .../modules/auth/abstractions/contracts.py | 40 +++-- .../modules/auth/contracts/requests.py | 30 ++-- .../modules/sake/contracts/requests.py | 147 +++++++++++++++++- .../modules/sake/contracts/responses.py | 26 ++++ .../modules/sake/contracts/results.py | 20 +++ src/servers/webservices/tests/sake.py | 13 +- 11 files changed, 301 insertions(+), 42 deletions(-) create mode 100644 src/.devcontainer/devcontainer.json create mode 100644 src/.github/dependabot.yml create mode 100644 src/.vscode/settings.json create mode 100644 src/servers/webservices/modules/sake/contracts/responses.py create mode 100644 src/servers/webservices/modules/sake/contracts/results.py diff --git a/src/.devcontainer/devcontainer.json b/src/.devcontainer/devcontainer.json new file mode 100644 index 000000000..0cb8892ad --- /dev/null +++ b/src/.devcontainer/devcontainer.json @@ -0,0 +1,29 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the +// README at: https://github.com/devcontainers/templates/tree/main/src/python +{ + "name": "Python 3", + // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + "image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye", + // Features to add to the dev container. More info: https://containers.dev/features. + // "features": {}, + // Use 'forwardPorts' to make a list of ports inside the container available locally. + "forwardPorts": [ + 29910, + 6667, + 29920, + 10086, + 27901, + 29900, + 29901, + 27900, + 28910, + 28900, + 80 + ] + // Use 'postCreateCommand' to run commands after the container is created. + // "postCreateCommand": "pip3 install --user -r requirements.txt", + // Configure tool-specific properties. + // "customizations": {}, + // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. + // "remoteUser": "root" +} \ No newline at end of file diff --git a/src/.github/dependabot.yml b/src/.github/dependabot.yml new file mode 100644 index 000000000..f33a02cd1 --- /dev/null +++ b/src/.github/dependabot.yml @@ -0,0 +1,12 @@ +# To get started with Dependabot version updates, you'll need to specify which +# package ecosystems to update and where the package manifests are located. +# Please see the documentation for more information: +# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates +# https://containers.dev/guide/dependabot + +version: 2 +updates: + - package-ecosystem: "devcontainers" + directory: "/" + schedule: + interval: weekly diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json new file mode 100644 index 000000000..a6735e59a --- /dev/null +++ b/src/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "python.analysis.typeCheckingMode": "off" +} \ No newline at end of file diff --git a/src/library/abstractions/client.py b/src/library/abstractions/client.py index 8604ec4a2..d7f4bb1ea 100644 --- a/src/library/abstractions/client.py +++ b/src/library/abstractions/client.py @@ -15,15 +15,15 @@ class ClientBase(abc.ABC): - server_config: ServerConfig + server_config: "ServerConfig" connection: object - logger: LogWriter - crypto: EncryptBase = None - info: ClientInfoBase - is_log_raw: bool = False + logger: "LogWriter" + crypto: "EncryptBase" = None + info: "ClientInfoBase" + is_log_raw: "bool" = False def __init__( - self, connection: ConnectionBase, server_config: ServerConfig, logger: LogWriter + self, connection: "ConnectionBase", server_config: "ServerConfig", logger: "LogWriter" ): assert isinstance(server_config, ServerConfig) # assert isinstance(logger, LogWriter) @@ -39,12 +39,12 @@ def on_connected(self) -> None: def on_disconnected(self) -> None: self.__del__() - @abc.abstractedmethod - def create_switcher(self, buffer) -> SwitcherBase: + @abc.abstractmethod + def create_switcher(self, buffer) -> "SwitcherBase": pass def on_received(self, buffer) -> None: - switcher: SwitcherBase = self.create_switcher(buffer) + switcher: "SwitcherBase" = self.create_switcher(buffer) switcher.handle() def decrypt_message(self, buffer) -> bytes: @@ -53,7 +53,7 @@ def decrypt_message(self, buffer) -> bytes: else: return buffer - def send(self, response: ResponseBase) -> None: + def send(self, response: "ResponseBase") -> None: response.build() sending_buffer = response.sending_buffer if isinstance(sending_buffer, str): diff --git a/src/requirements.txt b/src/requirements.txt index 7577c2a25..85173e29d 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -10,4 +10,5 @@ requests mongoengine == 0.28.2 flask_socketio socketio -fastapi \ No newline at end of file +fastapi +xmltodict \ No newline at end of file diff --git a/src/servers/webservices/modules/auth/abstractions/contracts.py b/src/servers/webservices/modules/auth/abstractions/contracts.py index 3dc4c5008..114bc24bf 100644 --- a/src/servers/webservices/modules/auth/abstractions/contracts.py +++ b/src/servers/webservices/modules/auth/abstractions/contracts.py @@ -1,5 +1,4 @@ import hashlib -import struct import servers.webservices.abstractions.contracts as lib from servers.webservices.aggregations.soap_envelop import SoapEnvelop from servers.webservices.applications.client import ClientInfo @@ -16,18 +15,21 @@ class LoginRequestBase(lib.RequestBase): def parse(self) -> None: super().parse() - version = self._content_element.find(f".//{{{NAMESPACE}}}version") - if version is None: + version_node = self._content_element.find( + f".//{{{NAMESPACE}}}version") + if version_node is None or version_node.text is None: raise AuthException("version is missing from the request.") - self.version = int(version) - partner_id = self._content_element.find(f".//{{{NAMESPACE}}}version") - if version is None: + self.version = int(version_node.text) + partner_id_node = self._content_element.find( + f".//{{{NAMESPACE}}}version") + if partner_id_node is None or partner_id_node.text is None: raise AuthException("partner id is missing from the request.") - self.partner_code = int(partner_id) - namespace_id = self._content_element.find(f".//{{{NAMESPACE}}}version") - if namespace_id is None: + self.partner_code = int(partner_id_node.text) + namespace_id_node = self._content_element.find( + f".//{{{NAMESPACE}}}version") + if namespace_id_node is None or namespace_id_node.text is None: raise AuthException("namespace id is missing from the request.") - self.namespace_id = int(namespace_id) + self.namespace_id = int(namespace_id_node.text) class LoginResultBase(lib.ResultBase): @@ -81,12 +83,18 @@ def _build_context(self): def __compute_hash(self) -> str: """return md5 str""" data_to_hash = bytearray() - data_to_hash.extend(self._result.length.to_bytes(4, byteorder="little")) - data_to_hash.extend(self._request.version.to_bytes(4, byteorder="little")) - data_to_hash.extend(self._request.partner_code.to_bytes(4, byteorder="little")) - data_to_hash.extend(self._request.namespace_id.to_bytes(4, byteorder="little")) - data_to_hash.extend(self._result.user_id.to_bytes(4, byteorder="little")) - data_to_hash.extend(self._result.profile_id.to_bytes(4, byteorder="little")) + data_to_hash.extend( + self._result.length.to_bytes(4, byteorder="little")) + data_to_hash.extend( + self._request.version.to_bytes(4, byteorder="little")) + data_to_hash.extend( + self._request.partner_code.to_bytes(4, byteorder="little")) + data_to_hash.extend( + self._request.namespace_id.to_bytes(4, byteorder="little")) + data_to_hash.extend( + self._result.user_id.to_bytes(4, byteorder="little")) + data_to_hash.extend( + self._result.profile_id.to_bytes(4, byteorder="little")) data_to_hash.extend(self._expiretime.to_bytes(4, byteorder="little")) data_to_hash.extend(self._result.profile_nick.encode("ascii")) data_to_hash.extend(self._result.unique_nick.encode("ascii")) diff --git a/src/servers/webservices/modules/auth/contracts/requests.py b/src/servers/webservices/modules/auth/contracts/requests.py index d0ac32993..286c7dd4d 100644 --- a/src/servers/webservices/modules/auth/contracts/requests.py +++ b/src/servers/webservices/modules/auth/contracts/requests.py @@ -17,7 +17,8 @@ def parse(self) -> None: if self.email is None: raise AuthException("email is missing from the request.") - self.uniquenick = self._content_element.find(f".//{{{NAMESPACE}}}uniquenick") + self.uniquenick = self._content_element.find( + f".//{{{NAMESPACE}}}uniquenick") if self.uniquenick is None: raise AuthException("uniquenick is missing from the request.") @@ -25,7 +26,8 @@ def parse(self) -> None: if self.cdkey is None: raise AuthException("cdkey is missing from the request.") - self.password = self._content_element.find(f".//{{{NAMESPACE}}}password") + self.password = self._content_element.find( + f".//{{{NAMESPACE}}}password") if self.password is None: raise AuthException("password is missing from the request.") @@ -47,7 +49,8 @@ class LoginPs3CertRequest(LoginRequestBase): def parse(self) -> None: super().parse() - self.ps3_cert = self._content_element.find(f".//{{{NAMESPACE}}}npticket") + self.ps3_cert = self._content_element.find( + f".//{{{NAMESPACE}}}npticket") if self.ps3_cert is None: raise AuthException("ps3cert is missing from the request") @@ -70,11 +73,13 @@ class LoginRemoteAuthRequest(LoginRequestBase): def parse(self) -> None: super().parse() - self.auth_token = self._content_element.find(f".//{{{NAMESPACE}}}authtoken") + self.auth_token = self._content_element.find( + f".//{{{NAMESPACE}}}authtoken") if self.auth_token is None: raise AuthException("authtoken is missing from the request.") - self.challenge = self._content_element.find(f".//{{{NAMESPACE}}}challenge") + self.challenge = self._content_element.find( + f".//{{{NAMESPACE}}}challenge") if self.challenge is None: raise AuthException("challenge is missing from the request.") @@ -97,13 +102,20 @@ class LoginUniqueNickRequest(LoginRequestBase): def parse(self) -> None: super().parse() - self.uniquenick = self._content_element.find(f".//{{{NAMESPACE}}}uniquenick") - if self.uniquenick is None: + unique_nick_node = self._content_element.find( + f".//{{{NAMESPACE}}}uniquenick") + if unique_nick_node is None or unique_nick_node.text is None: raise AuthException("uniquenick is missing from the request.") + self.uniquenick = unique_nick_node.text - self.password = self._content_element.find(f".//{{{NAMESPACE}}}password") - if self.password is None: + password_node = self._content_element.find( + f".//{{{NAMESPACE}}}password") + if password_node is None: raise AuthException("password is missing from the request.") + password_value_node = password_node.find(f".//{{{NAMESPACE}}}Value") + if password_value_node is None or password_value_node.text is None: + raise AuthException("No password value found") + self.password = password_value_node.text class LoginUniqueNickWithGameIdRequest(LoginUniqueNickRequest): diff --git a/src/servers/webservices/modules/sake/contracts/requests.py b/src/servers/webservices/modules/sake/contracts/requests.py index 9aba13882..7e236a552 100644 --- a/src/servers/webservices/modules/sake/contracts/requests.py +++ b/src/servers/webservices/modules/sake/contracts/requests.py @@ -1,3 +1,4 @@ +from typing import OrderedDict from servers.webservices.modules.sake.abstractions.contracts import ( RequestBase, NAMESPACE, @@ -61,4 +62,148 @@ def parse(self) -> None: class GetRecordLimitRequest(RequestBase): - pass \ No newline at end of file + pass + + +class GetSpecificRecordsRequest(RequestBase): + record_ids: list[tuple] + """ + [ + (field_name,field_type), + (field_name,field_type), + (field_name,field_type), + ... + (field_name,field_type) + ] + """ + fields: list[tuple] + """ + [ + (field_name,field_type), + (field_name,field_type), + (field_name,field_type), + ... + (field_name,field_type) + ] + """ + + def parse(self) -> None: + super().parse() + record_id_node = self._content_element.find( + f".//{{{NAMESPACE}}}recordids") + if record_id_node is None: + raise SakeException("No record id found.") + self.record_ids = xmltodict.parse(str(record_id_node)) + fields = self._content_element.find( + f".//{{{NAMESPACE}}}recordids") + if fields is None: + raise SakeException("No record id found.") + + self.fields = xmltodict.parse(str(fields)) + + +class RateRecordRequest(RequestBase): + record_id: str + rating: str + + def parse(self) -> None: + super().parse() + record_id = self._content_element.find( + f".//{{{NAMESPACE}}}recordid") + if record_id is None: + raise SakeException("No record id found.") + self.record_id = record_id + + rating = self._content_element.find( + f".//{{{NAMESPACE}}}rating") + if rating is None or rating.text is None: + raise SakeException("No rating found.") + self.rating = rating.text + + +class SearchForRecordsRequest(RequestBase): + filter: str + sort: str + offset: str + max: str + surrounding: str + owner_ids: str + cache_flag: str + fields: OrderedDict[str, object] + """ + [ + (field_name,field_type), + (field_name,field_type), + (field_name,field_type), + ... + (field_name,field_type) + ] + """ + + def parse(self) -> None: + super().parse() + self.filter = self._content_element.find( + f".//{{{NAMESPACE}}}filter") + if self.filter is None: + raise SakeException("No filter found.") + + self.sort = self._content_element.find( + f".//{{{NAMESPACE}}}sort") + if self.sort is None: + raise SakeException("No sort found.") + + self.offset = self._content_element.find( + f".//{{{NAMESPACE}}}offset") + if self.offset is None: + raise SakeException("No offset found.") + + self.max = self._content_element.find( + f".//{{{NAMESPACE}}}max") + if self.max is None: + raise SakeException("No max found.") + + self.surrounding = self._content_element.find( + f".//{{{NAMESPACE}}}surrounding") + if self.sort is None: + raise SakeException("No surrounding found.") + self.owner_ids = self._content_element.find( + f".//{{{NAMESPACE}}}ownerids") + if self.owner_ids is None: + raise SakeException("No ownderids found.") + + self.cache_flag = self._content_element.find( + f".//{{{NAMESPACE}}}cacheFlag") + if self.cache_flag is None: + raise SakeException("No cache flag found.") + + fields = self._content_element.find( + f".//{{{NAMESPACE}}}fields") + if fields is None: + raise SakeException("No record id found.") + + self.fields = xmltodict.parse(str(fields)) + + +class UpdateRecordRequest(RequestBase): + record_id: str + values: OrderedDict[str, object] + """ + [ + (field_name,field_type,field_value), + (field_name,field_type,field_value), + (field_name,field_type,field_value), + ... + (field_name,field_type,field_value) + ] + """ + + def parse(self) -> None: + super().parse() + record_id = self._content_element.find( + f".//{{{NAMESPACE}}}recordid") + if record_id is None: + raise SakeException("No record id found.") + self.record_id = record_id + values_node = self._content_element.find( + f".//{{{NAMESPACE}}}values") + self.values = xmltodict.parse(str(values_node)) diff --git a/src/servers/webservices/modules/sake/contracts/responses.py b/src/servers/webservices/modules/sake/contracts/responses.py new file mode 100644 index 000000000..d245a19a4 --- /dev/null +++ b/src/servers/webservices/modules/sake/contracts/responses.py @@ -0,0 +1,26 @@ +from servers.webservices.modules.sake.abstractions.contracts import ResponseBase +from servers.webservices.modules.sake.contracts.requests import CreateRecordRequest +from servers.webservices.modules.sake.contracts.results import CreateRecordResult + + +class CreateRecordResponse(ResponseBase): + _result: CreateRecordResult + _request: CreateRecordRequest + + def build(self) -> None: + self._content.add("CreateRecordResult") + self._content.add("tableid", self._result.table_id) + self._content.add("recordid", self._result.record_id) + + for field in self._result.fields: + self._content.add("fields", field) + + super().build() + + +class GetMyRecordResponse(ResponseBase): + pass + + +class SearchForRecordsResponse(ResponseBase): + pass \ No newline at end of file diff --git a/src/servers/webservices/modules/sake/contracts/results.py b/src/servers/webservices/modules/sake/contracts/results.py new file mode 100644 index 000000000..c47c6c2bb --- /dev/null +++ b/src/servers/webservices/modules/sake/contracts/results.py @@ -0,0 +1,20 @@ +from servers.webservices.modules.sake.abstractions.contracts import ResultBase + + +class CreateRecordResult(ResultBase): + table_id: str + record_id: str + fields: list + + +class GetMyRecordsResult(ResultBase): + records: list[tuple] + """ + [ + (field_name,field_type,field_value), + (field_name,field_type,field_value), + (field_name,field_type,field_value), + ... + (field_name,field_type,field_value) + ] + """ diff --git a/src/servers/webservices/tests/sake.py b/src/servers/webservices/tests/sake.py index 69323a006..0c3fbdd4a 100644 --- a/src/servers/webservices/tests/sake.py +++ b/src/servers/webservices/tests/sake.py @@ -5,8 +5,7 @@ class Auth(unittest.TestCase): def test_login_unique_nick(self) -> None: - raw = """ - + raw = """ None: - - """ + """ r = LoginUniqueNickRequest(raw) r.parse() - \ No newline at end of file + pass + + +if __name__ == "__main__": + a = Auth() + a.test_login_unique_nick() \ No newline at end of file From 71ff91e9fef0a3a3d085da918248e7cb6a525b60 Mon Sep 17 00:00:00 2001 From: Bildcraft1 Date: Sun, 14 Jul 2024 15:12:29 +0200 Subject: [PATCH 073/231] WIP unit tests for PCM We still need MokeObject for the tests --- .../tests/buddy_request_test.py | 42 +++++++++++ .../tests/game_test.py | 31 ++++++++ .../tests/general_request_test.py | 71 +++++++++++++++++++ .../tests/moke_object.py | 18 +++++ .../tests/profile_request_test.py | 61 ++++++++++++++++ 5 files changed, 223 insertions(+) create mode 100644 src/servers/presence_connection_manager/tests/buddy_request_test.py create mode 100644 src/servers/presence_connection_manager/tests/game_test.py create mode 100644 src/servers/presence_connection_manager/tests/general_request_test.py create mode 100644 src/servers/presence_connection_manager/tests/moke_object.py create mode 100644 src/servers/presence_connection_manager/tests/profile_request_test.py diff --git a/src/servers/presence_connection_manager/tests/buddy_request_test.py b/src/servers/presence_connection_manager/tests/buddy_request_test.py new file mode 100644 index 000000000..362cb2ed2 --- /dev/null +++ b/src/servers/presence_connection_manager/tests/buddy_request_test.py @@ -0,0 +1,42 @@ +import unittest + +from servers.presence_connection_manager.contracts.requests.buddy import ( + AddBuddyRequest, + DelBuddyRequest, + InviteToRequest, + StatusRequest, +) + + +class BuddyRequestTest(unittest.TestCase): + ADD_BUDDY = "\\addbuddy\\\\sesskey\\0\\newprofileid\\0\\reason\\test\\final\\" + DEL_BUDDY = "\\delbuddy\\\\sesskey\\0\\delprofileid\\0\\final\\" + INVITE_TO = "\\inviteto\\\\sesskey\\0\\productid\\0\\profileid\\0\\final\\" + STATUS = "\\status\\0\\statstring\\test\\locstring\\test\\final\\" + + def test_add_buddy(self) -> None: + request = AddBuddyRequest(BuddyRequestTest.ADD_BUDDY) + request.parse() + self.assertEqual(0, request.friend_profile_id) + self.assertEqual("test", request.reason) + + def test_del_buddy(self) -> None: + request = DelBuddyRequest(BuddyRequestTest.DEL_BUDDY) + request.parse() + self.assertEqual(0, request.friend_profile_id) + + def test_invite_to(self) -> None: + request = InviteToRequest(BuddyRequestTest.INVITE_TO) + request.parse() + self.assertEqual(0, request.product_id) + self.assertEqual(0, request.profile_id) + + def test_status_test(self) -> None: + request = StatusRequest(BuddyRequestTest.STATUS) + request.parse() + self.assertEqual("test", request.status.status_string) + self.assertEqual("test", request.status.location_string) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/servers/presence_connection_manager/tests/game_test.py b/src/servers/presence_connection_manager/tests/game_test.py new file mode 100644 index 000000000..a61c2411d --- /dev/null +++ b/src/servers/presence_connection_manager/tests/game_test.py @@ -0,0 +1,31 @@ +import unittest + +from servers.presence_connection_manager.contracts.requests.buddy import StatusRequest + + +class GameTest(unittest.TestCase): + def test_civilization_4(self) -> None: + raw_requests = [ + "\\newuser\\\\email\\civ4@unispy.org\\nick\\civ4-tk\\passwordenc\\JMHGwQ__\\productid\\10435\\gamename\\civ4\\namespaceid\\17\\uniquenick\\civ4-tk\\id\\1\\final\\", + "\\login\\\\challenge\\xMsHUXuWNXL3KMwmhoQZJrP0RVsArCYT\\uniquenick\\civ4-tk\\userid\\25\\profileid\\26\\response\\7f2c9c6685570ea18b7207d2cbd72452\\firewall\\1\\port\\0\\productid\\10435\\gamename\\civ4\\namespaceid\\17\\sdkrevision\\1\\id\\1\\final\\", + ] + for x in raw_requests: + # TODO MokeObject Implementation + pass + + def test_conflict_global_storm(self) -> None: + raw_requests = [ + "\\lc\\1\\challenge\\NRNUJLZMLX\\id\\1\\final\\", + "\\login\\\\challenge\\KMylyQbZfqzKn9otxx32q4076sOUnKif\\user\\cgs1@cgs1@rs.de\\response\\c1a6638bbcfe130e4287bfe4aa792949\\port\\-15737\\productid\\10469\\gamename\\conflictsopc\\namespaceid\\1\\id\\1\\final\\", + "\\inviteto\\\\sesskey\\58366\\products\\1038\\final\\", + ] + for x in raw_requests: + # TODO MokeObject Implementation + pass + + def test_sbwfrontps2(self) -> None: + raw = "\\status\\1\\sesskey\\1111\\statstring\\EN LIGNE\\locstring\\\\final\\" + request = StatusRequest(raw) + request.parse() + self.assertTrue(request.status.location_string == "") + self.assertTrue(request.status.status_string == "EN LIGNE") diff --git a/src/servers/presence_connection_manager/tests/general_request_test.py b/src/servers/presence_connection_manager/tests/general_request_test.py new file mode 100644 index 000000000..37e2210a5 --- /dev/null +++ b/src/servers/presence_connection_manager/tests/general_request_test.py @@ -0,0 +1,71 @@ +import unittest +from servers.presence_connection_manager.contracts.requests.general import LoginRequest +from servers.presence_connection_manager.enums.general import ( + LoginType, + QuietModeType, + SdkRevisionType, +) + + +class GeneralRequestTest(unittest.TestCase): + LOGIN_AUTH_TOKEN = "\\login\\\\challenge\\xxxx\\authtoken\\xxxx\\userid\\0\\profileid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\3\\quiet\\0\\id\\1\\final\\" + LOGIN_UNIQUE_NICK = "\\login\\\\challenge\\xxxx\\uniquenick\\spyguy\\userid\\0\\profileid\\0\\namespaceid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\3\\quiet\\0\\id\\1\\final\\" + LOGIN_USER = "\\login\\\\challenge\\xxxx\\user\\spyguy@spyguy@unispy.org\\userid\\0\\profileid\\0\\partnerid\\0\\namespaceid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\3\\quiet\\0\\id\\1\\final\\" + + def test_login_auth_token(self) -> None: + request = LoginRequest(GeneralRequestTest.LOGIN_AUTH_TOKEN) + request.parse() + self.assertEqual(LoginType.AUTH_TOKEN, request.type) + self.assertEqual("xxxx", request.user_challenge) + self.assertEqual("xxxx", request.auth_token) + self.assertEqual(0, request.user_id) + self.assertEqual(0, request.profile_id) + self.assertEqual(0, request.partner_id) + self.assertEqual("xxxxx", request.response) + self.assertEqual("1", request.firewall) + self.assertEqual(request.game_port, 0) + self.assertEqual(request.product_id, 0) + self.assertEqual("gmtest", request.game_name) + self.assertEqual(SdkRevisionType(3), request.sdk_revision_type) + self.assertEqual(QuietModeType(0), request.quiet_mode_flags) + + def test_login_unique_nick(self) -> None: + request = LoginRequest(GeneralRequestTest.LOGIN_UNIQUE_NICK) + request.parse() + self.assertEqual(LoginType.UNIQUENICK_NAMESPACE_ID, request.type) + self.assertEqual("xxxx", request.user_challenge) + self.assertEqual("spyguy", request.unique_nick) + self.assertEqual(0, request.namespace_id) + self.assertEqual(0, request.user_id) + self.assertEqual(0, request.profile_id) + self.assertEqual(0, request.partner_id) + self.assertEqual("xxxxx", request.response) + self.assertEqual("1", request.firewall) + self.assertEqual(0, request.game_port) + self.assertEqual(0, request.product_id) + self.assertEqual("gmtest", request.game_name) + self.assertEqual(SdkRevisionType(3), request.sdk_revision_type) + self.assertEqual(QuietModeType(0), request.quiet_mode_flags) + + def test_login_user(self) -> None: + request = LoginRequest(GeneralRequestTest.LOGIN_USER) + request.parse() + self.assertEqual(LoginType.NICK_EMAIL, request.type) + self.assertEqual("xxxx", request.user_challenge) + self.assertEqual("spyguy", request.nick) + self.assertEqual("spyguy@unispy.org", request.email) + self.assertEqual(0, request.namespace_id) + self.assertEqual(0, request.user_id) + self.assertEqual(0, request.profile_id) + self.assertEqual(0, request.partner_id) + self.assertEqual("xxxxx", request.response) + self.assertEqual("1", request.firewall) + self.assertEqual(0, request.game_port) + self.assertEqual(0, request.product_id) + self.assertEqual("gmtest", request.game_name) + self.assertEqual(SdkRevisionType(3), request.sdk_revision_type) + self.assertEqual(QuietModeType(0), request.quiet_mode_flags) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/servers/presence_connection_manager/tests/moke_object.py b/src/servers/presence_connection_manager/tests/moke_object.py new file mode 100644 index 000000000..ea89c0fd5 --- /dev/null +++ b/src/servers/presence_connection_manager/tests/moke_object.py @@ -0,0 +1,18 @@ +import unittest +from unittest.mock import Mock +from servers.presence_connection_manager.applications.client import Client + +class MokeObject: + client = None + + # TODO We first need Server implementation + @staticmethod + def create_client(ip_address="192.168.1.1", port=9990): + manager_mock = Mock(IConnectionManager) + connection_mock = Mock() + connection_mock.RemoteIPEndPoint = (ip_address, port) + connection_mock.Manager = manager_mock + connection_mock.ConnectionType = "Tcp" + server_mock = Server(manager_mock) + + return Client(connection_mock, server_mock) diff --git a/src/servers/presence_connection_manager/tests/profile_request_test.py b/src/servers/presence_connection_manager/tests/profile_request_test.py new file mode 100644 index 000000000..d3843ef39 --- /dev/null +++ b/src/servers/presence_connection_manager/tests/profile_request_test.py @@ -0,0 +1,61 @@ +import unittest + +from servers.presence_connection_manager.contracts.requests.profile import ( + AddBlockRequest, + GetProfileRequest, + NewProfileRequest, + RegisterCDKeyRequest, + RegisterNickRequest, + UpdateProfileRequest, +) + + +class ProfileRequestTest(unittest.TestCase): + ADD_BLOCK = "\\addblock\\profileid\\0\\final\\" + GET_PROFILE = "\\getprofile\\sesskey\\xxxx\\profileid\\0\\final\\" + NEW_PROFILE = "\\newprofile\\sesskey\\xxxx\\nick\\spyguy\\id\\1\\final\\" + NEW_PROFILE_REPLACE = "\\newprofile\\sesskey\\xxxx\\nick\\spyguy2\\replace\\1\\oldnick\\spyguy\\id\\1\\final\\" + REGISTER_CD_KEY = "\\registercdkey\\sesskey\\xxxx\\cdkeyenc\\xxxx\\id\\1\\final\\" + REGISTER_NICK = "\\registernick\\sesskey\\xxxx\\uniquenick\\spyguy\\partnerid\\0\\id\\1\\final\\" + + def test_add_block(self) -> None: + request = AddBlockRequest(ProfileRequestTest.ADD_BLOCK) + request.parse() + self.assertEqual(0, request.taget_id) + + def test_get_profile(self) -> None: + request = GetProfileRequest(ProfileRequestTest.GET_PROFILE) + request.parse() + self.assertEqual("xxxx", request.session_key) + self.assertEqual(0, request.profile_id) + + def test_new_profile(self) -> None: + request = NewProfileRequest(ProfileRequestTest.NEW_PROFILE) + request.parse() + self.assertEqual("xxxx", request.session_key) + self.assertEqual("spyguy", request.new_nick) + + def test_new_profile_replace(self) -> None: + request = NewProfileRequest(ProfileRequestTest.NEW_PROFILE_REPLACE) + request.parse() + self.assertEqual("xxxx", request.session_key) + self.assertEqual("spyguy2", request.new_nick) + self.assertEqual("spyguy", request.old_nick) + + def test_register_cd_key(self) -> None: + request = RegisterCDKeyRequest(ProfileRequestTest.REGISTER_CD_KEY) + request.parse() + self.assertEqual("xxxx", request.session_key) + self.assertEqual("xxxx", request.cdkey_enc) + + def test_register_nick(self) -> None: + request = RegisterNickRequest(ProfileRequestTest.REGISTER_NICK) + request.parse() + self.assertEqual("xxxx", request.session_key) + self.assertEqual("spyguy", request.unique_nick) + self.assertEqual(0, request.partner_id) + + def test_update_profile(self) -> None: + crysisWarsRaw = "\\updatepro\\sesskey\\1111\\countrycode\\DE\\birthday\\168232912\\partnerid\\0\\final\\" + request = UpdateProfileRequest(crysisWarsRaw) + request.Parse() From 3061e50f9d18f55974f546ef8077b3dc8560e134 Mon Sep 17 00:00:00 2001 From: Bildcraft1 Date: Sun, 14 Jul 2024 15:25:57 +0200 Subject: [PATCH 074/231] Use Enum Properties to access value --- .../tests/general_request_test.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/servers/presence_connection_manager/tests/general_request_test.py b/src/servers/presence_connection_manager/tests/general_request_test.py index 37e2210a5..baf30cb7a 100644 --- a/src/servers/presence_connection_manager/tests/general_request_test.py +++ b/src/servers/presence_connection_manager/tests/general_request_test.py @@ -26,8 +26,8 @@ def test_login_auth_token(self) -> None: self.assertEqual(request.game_port, 0) self.assertEqual(request.product_id, 0) self.assertEqual("gmtest", request.game_name) - self.assertEqual(SdkRevisionType(3), request.sdk_revision_type) - self.assertEqual(QuietModeType(0), request.quiet_mode_flags) + self.assertEqual(SdkRevisionType.GPINEW_STATUS_NOTIFICATION, request.sdk_revision_type) + self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) def test_login_unique_nick(self) -> None: request = LoginRequest(GeneralRequestTest.LOGIN_UNIQUE_NICK) @@ -44,8 +44,8 @@ def test_login_unique_nick(self) -> None: self.assertEqual(0, request.game_port) self.assertEqual(0, request.product_id) self.assertEqual("gmtest", request.game_name) - self.assertEqual(SdkRevisionType(3), request.sdk_revision_type) - self.assertEqual(QuietModeType(0), request.quiet_mode_flags) + self.assertEqual(SdkRevisionType.GPINEW_STATUS_NOTIFICATION, request.sdk_revision_type) + self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) def test_login_user(self) -> None: request = LoginRequest(GeneralRequestTest.LOGIN_USER) @@ -63,8 +63,8 @@ def test_login_user(self) -> None: self.assertEqual(0, request.game_port) self.assertEqual(0, request.product_id) self.assertEqual("gmtest", request.game_name) - self.assertEqual(SdkRevisionType(3), request.sdk_revision_type) - self.assertEqual(QuietModeType(0), request.quiet_mode_flags) + self.assertEqual(SdkRevisionType.GPINEW_STATUS_NOTIFICATION, request.sdk_revision_type) + self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) if __name__ == "__main__": From b50b813b02dea48ca159941d1520c28dc347cb2c Mon Sep 17 00:00:00 2001 From: Bildcraft1 Date: Tue, 16 Jul 2024 00:54:46 +0200 Subject: [PATCH 075/231] Update contracts --- .../contracts/requests/general.py | 6 ++++-- .../presence_connection_manager/contracts/results/buddy.py | 3 --- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/src/servers/presence_connection_manager/contracts/requests/general.py b/src/servers/presence_connection_manager/contracts/requests/general.py index edac1efa0..155e15e36 100644 --- a/src/servers/presence_connection_manager/contracts/requests/general.py +++ b/src/servers/presence_connection_manager/contracts/requests/general.py @@ -10,7 +10,8 @@ class KeepAliveRequest(RequestBase): - pass + def __init__(self, raw_request): + super().__init__(raw_request) class LoginRequest(RequestBase): @@ -114,4 +115,5 @@ def parse_other_data(self): class LogoutRequest(RequestBase): - pass + def __init__(self, raw_request): + super().__init__(raw_request) diff --git a/src/servers/presence_connection_manager/contracts/results/buddy.py b/src/servers/presence_connection_manager/contracts/results/buddy.py index f2d1c3956..dca14d080 100644 --- a/src/servers/presence_connection_manager/contracts/results/buddy.py +++ b/src/servers/presence_connection_manager/contracts/results/buddy.py @@ -25,6 +25,3 @@ class StatusInfoResult(ResultBase): class StatusResult(ResultBase): status: UserStatus - - -# class NewUserResult() \ No newline at end of file From 0bdb9f7163019e93f8aa75d30db5c04a986e9ce0 Mon Sep 17 00:00:00 2001 From: Bildcraft1 Date: Tue, 16 Jul 2024 01:02:13 +0200 Subject: [PATCH 076/231] Revert "Update contracts" This reverts commit b50b813b02dea48ca159941d1520c28dc347cb2c. --- .../contracts/requests/general.py | 6 ++---- .../presence_connection_manager/contracts/results/buddy.py | 3 +++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/servers/presence_connection_manager/contracts/requests/general.py b/src/servers/presence_connection_manager/contracts/requests/general.py index 155e15e36..edac1efa0 100644 --- a/src/servers/presence_connection_manager/contracts/requests/general.py +++ b/src/servers/presence_connection_manager/contracts/requests/general.py @@ -10,8 +10,7 @@ class KeepAliveRequest(RequestBase): - def __init__(self, raw_request): - super().__init__(raw_request) + pass class LoginRequest(RequestBase): @@ -115,5 +114,4 @@ def parse_other_data(self): class LogoutRequest(RequestBase): - def __init__(self, raw_request): - super().__init__(raw_request) + pass diff --git a/src/servers/presence_connection_manager/contracts/results/buddy.py b/src/servers/presence_connection_manager/contracts/results/buddy.py index dca14d080..f2d1c3956 100644 --- a/src/servers/presence_connection_manager/contracts/results/buddy.py +++ b/src/servers/presence_connection_manager/contracts/results/buddy.py @@ -25,3 +25,6 @@ class StatusInfoResult(ResultBase): class StatusResult(ResultBase): status: UserStatus + + +# class NewUserResult() \ No newline at end of file From 3d7dfa41a1d002172e8ab493d3ccc62820fd9e6a Mon Sep 17 00:00:00 2001 From: Bildcraft1 Date: Wed, 17 Jul 2024 22:41:55 +0200 Subject: [PATCH 077/231] Initial work on Handlers --- .../applications/data.py | 5 +- .../handlers/buddy.py | 36 ++++++++++++- .../handlers/general.py | 10 +++- .../handlers/profile.py | 52 ++++++++++++++++++- 4 files changed, 97 insertions(+), 6 deletions(-) diff --git a/src/servers/presence_connection_manager/applications/data.py b/src/servers/presence_connection_manager/applications/data.py index 7fd44225d..3ed1d6dfa 100644 --- a/src/servers/presence_connection_manager/applications/data.py +++ b/src/servers/presence_connection_manager/applications/data.py @@ -17,8 +17,8 @@ def is_email_exist(email: str) -> bool: return False -def delete_friend_by_profile_id(profile_id: int): - friend = PG_SESSION.query(Friends).filter(Friends.friendid == profile_id).first() +def delete_friend_by_profile_id(profile_id: int, target_id: int, namespace_id: int): + friend = PG_SESSION.query(Friends).filter(Friends.ProfileId == profile_id, Friends.TargetId == target_id, Friends.NamespaceId == namespace_id).first() if friend is None: raise GPDatabaseException( f"friend deletion have errors on profile id:{profile_id}" @@ -153,7 +153,6 @@ def update_profile_info(profile: Profiles): PG_SESSION.add(profile) PG_SESSION.commit() - def update_unique_nick(subprofile_id: int, unique_nick: str): result = ( PG_SESSION.query(SubProfiles) diff --git a/src/servers/presence_connection_manager/handlers/buddy.py b/src/servers/presence_connection_manager/handlers/buddy.py index 1b113545e..a79971709 100644 --- a/src/servers/presence_connection_manager/handlers/buddy.py +++ b/src/servers/presence_connection_manager/handlers/buddy.py @@ -5,6 +5,11 @@ LoginHandlerBase, ) from servers.presence_connection_manager.applications.client import Client +from servers.presence_connection_manager.applications.data import ( + get_blocked_profile_id_list, + get_friend_profile_id_list, + delete_friend_by_profile_id, +) from servers.presence_connection_manager.contracts.requests.buddy import ( DelBuddyRequest, StatusInfoRequest, @@ -35,6 +40,9 @@ class BlockListHandler(CmdHandlerBase): def __init__(self, client: Client) -> None: assert isinstance(client, Client) + def _data_operation(self) -> None: + self._result.profile_ids = get_blocked_profile_id_list(self._client.info.profile_id, self._client.info.namespace_id) + def _response_construct(self) -> None: self._response = BlockListResponse(self._result) @@ -46,6 +54,10 @@ def __init__(self, client: Client): assert isinstance(client, Client) self._client = client + def _data_operation(self) -> None: + friends_id = get_friend_profile_id_list(self._client.info.profile_id, self._client.info.namespace_id) + self._result.profile_ids = friends_id + def response_construct(self): self._response = BuddyListResponse(self._request, self._result) @@ -78,6 +90,9 @@ def __init__(self, client: Client, request: RequestBase) -> None: raise NotImplementedError() super().__init__(client, request) + def _data_operation(self) -> None: + raise NotImplementedError() + class DelBuddyHandler(LoginHandlerBase): _request: DelBuddyRequest @@ -86,13 +101,22 @@ def __init__(self, client: Client, request: DelBuddyRequest) -> None: assert isinstance(request, DelBuddyRequest) super().__init__(client, request) + def _data_operation(self) -> None: + delete_friend_by_profile_id(self._client.info.profile_id, self._request.friend_profile_id, self._client.info.namespace_id) + class InviteToHandler(LoginHandlerBase): def __init__(self, client: Client, request: RequestBase) -> None: raise NotImplementedError() super().__init__(client, request) - pass + def _data_operation(self) -> None: + if(self._client == None): + return + else: + # TODO + # Parse user to Buddy Message System + raise NotImplementedError() class StatusHandler(CmdHandlerBase): @@ -116,6 +140,16 @@ def __init__(self, client: Client, request: StatusInfoRequest) -> None: assert isinstance(request, StatusInfoRequest) super().__init__(client, request) + def _data_operation(self) -> None: + if(self._request.is_get_status_info): + if(self._client != None): + # User is not online we do not need to send status info + self._result.status_info = self._client.info.status_info + else: + self._result.status_info = self._request.status_info + # TODO + # Notify every online friend? + def _response_send(self) -> None: if self._request.is_get_status_info: self._response = StatusInfoResponse(self._request, self._result) diff --git a/src/servers/presence_connection_manager/handlers/general.py b/src/servers/presence_connection_manager/handlers/general.py index c675ce955..d42ba38f0 100644 --- a/src/servers/presence_connection_manager/handlers/general.py +++ b/src/servers/presence_connection_manager/handlers/general.py @@ -26,11 +26,16 @@ def __init__(self, client: Client, request: KeepAliveRequest) -> None: assert isinstance(request, KeepAliveRequest) super().__init__(client, request) + def _data_operation(self) -> None: + # We need to keep the player cache online + # So that other players can find the player + pass + def _response_construct(self) -> None: self._response = KeepAliveResponse(self._request) -class LoginHandelr(CmdHandlerBase): +class LoginHandler(CmdHandlerBase): _request: LoginRequest _result: LoginResult @@ -50,6 +55,9 @@ def __init__(self, client: Client, request: LogoutRequest) -> None: assert isinstance(request, LogoutRequest) super().__init__(client, request) + def _data_operation(self) -> None: + self._client.connection.disconnect() + import servers.presence_search_player.handlers.handlers diff --git a/src/servers/presence_connection_manager/handlers/profile.py b/src/servers/presence_connection_manager/handlers/profile.py index bd4eed2ae..80f407b58 100644 --- a/src/servers/presence_connection_manager/handlers/profile.py +++ b/src/servers/presence_connection_manager/handlers/profile.py @@ -1,7 +1,9 @@ +from library.database.pg_orm import Profiles from servers.chat.contracts.requests.general import RegisterNickRequest from servers.presence_connection_manager.abstractions.contracts import RequestBase from servers.presence_connection_manager.abstractions.handler import CmdHandlerBase from servers.presence_connection_manager.applications.client import Client +from servers.presence_connection_manager.applications.data import add_nick_name, get_profile_info_list, update_block_info_list, update_profile_info, update_subprofile_info, update_unique_nick from servers.presence_connection_manager.contracts.requests.profile import ( AddBlockRequest, GetProfileRequest, @@ -24,6 +26,9 @@ def __init__(self, client: Client, request: AddBlockRequest) -> None: assert isinstance(request, AddBlockRequest) super().__init__(client, request) + def _data_operation(self) -> None: + update_block_info_list(self._request.taget_id, self._client.info.profile_id, self._client.info.namespace_id) + class GetProfileHandler(CmdHandlerBase): _request: GetProfileRequest @@ -33,6 +38,13 @@ def __init__(self, client: Client, request: GetProfileRequest) -> None: assert isinstance(request, GetProfileRequest) super().__init__(client, request) + def data_operation(self) -> None: + self._result.user_profile = get_profile_info_list(self._request.profile_id, self._client.info.namespace_id) + if(self._result.user_profile is None): + # TODO + # raise right exception + raise NotImplementedError() + def _response_construct(self) -> None: self._response = GetProfileResponse(self._request, self._result) @@ -45,6 +57,12 @@ def __init__(self, client: Client, request: NewProfileRequest) -> None: assert isinstance(request, NewProfileRequest) super().__init__(client, request) + def _data_operation(self) -> None: + if(self._request.is_replace_nick_name): + update_unique_nick(self._client.info.profile_id, self._request.new_nick) + else: + add_nick_name(self._client.info.profile_id, self._request.new_nick, self._request.new_nick) + def _response_construct(self) -> None: self._response = NewProfileResponse(self._request, self._result) @@ -56,6 +74,11 @@ def __init__(self, client: Client, request: RegisterCDKeyRequest) -> None: assert isinstance(request, RegisterCDKeyRequest) super().__init__(client, request) + def _data_operation(self) -> None: + self._request.cdkey_enc = self._request.cdkey_enc + # TODO + # update subprofile info + update_subprofile_info(self._client.info.sub_profile_id) class RegisterNickHandler(CmdHandlerBase): @@ -65,6 +88,9 @@ def __init__(self, client: Client, request: RegisterNickRequest) -> None: assert isinstance(request, RegisterNickRequest) super().__init__(client, request) + def data_operation(self) -> None: + update_unique_nick(self._client.info.profile_id, self._request.unique_nick) + def _response_construct(self) -> None: self._response = RegisterNickResponse(self._request, self._result) @@ -72,7 +98,6 @@ def _response_construct(self) -> None: class RemoveBlockHandler(CmdHandlerBase): def __init__(self, client: Client, request: RequestBase) -> None: raise NotImplementedError() - super().__init__(client, request) @@ -83,6 +108,31 @@ def __init__(self, client: Client, request: UpdateProfileRequest) -> None: assert isinstance(request, UpdateProfileRequest) super().__init__(client, request) + def _data_operation(self) -> None: + profile = self._client.info + if(self._request.has_public_mask_flag): + profile.public_mask = self._request.public_mask + if(self._request.has_first_name_flag): + profile.first_name = self._request.first_name + if(self._request.has_last_name_flag): + profile.last_name = self._request.last_name + if(self._request.has_icq_flag): + profile.icq = self._request.icq_uin + if(self._request.has_home_page_flag): + profile.home_page = self._request.home_page + if(self._request.has_birthday_flag): + profile.birth_day = self._request.birth_day + profile.birth_month = self._request.birth_month + profile.birth_year = self._request.birth_year + if(self._request.has_sex_flag): + profile.sex = self._request.sex + if(self._request.has_zip_code): + profile.zip_code = self._request.zip_code + if(self._request.has_country_flag): + profile.country_code = self._request.country_code + + update_profile_info(Profiles(profile)) + class UpdateUserInfoHandler(CmdHandlerBase): raise NotImplementedError() From 5f650fc6ddf0381549a657023f8d0471e5933449 Mon Sep 17 00:00:00 2001 From: Bildcraft1 Date: Wed, 17 Jul 2024 23:06:06 +0200 Subject: [PATCH 078/231] Revert "Initial work on Handlers" This reverts commit 3d7dfa41a1d002172e8ab493d3ccc62820fd9e6a. --- .../applications/data.py | 5 +- .../handlers/buddy.py | 36 +------------ .../handlers/general.py | 10 +--- .../handlers/profile.py | 52 +------------------ 4 files changed, 6 insertions(+), 97 deletions(-) diff --git a/src/servers/presence_connection_manager/applications/data.py b/src/servers/presence_connection_manager/applications/data.py index 3ed1d6dfa..7fd44225d 100644 --- a/src/servers/presence_connection_manager/applications/data.py +++ b/src/servers/presence_connection_manager/applications/data.py @@ -17,8 +17,8 @@ def is_email_exist(email: str) -> bool: return False -def delete_friend_by_profile_id(profile_id: int, target_id: int, namespace_id: int): - friend = PG_SESSION.query(Friends).filter(Friends.ProfileId == profile_id, Friends.TargetId == target_id, Friends.NamespaceId == namespace_id).first() +def delete_friend_by_profile_id(profile_id: int): + friend = PG_SESSION.query(Friends).filter(Friends.friendid == profile_id).first() if friend is None: raise GPDatabaseException( f"friend deletion have errors on profile id:{profile_id}" @@ -153,6 +153,7 @@ def update_profile_info(profile: Profiles): PG_SESSION.add(profile) PG_SESSION.commit() + def update_unique_nick(subprofile_id: int, unique_nick: str): result = ( PG_SESSION.query(SubProfiles) diff --git a/src/servers/presence_connection_manager/handlers/buddy.py b/src/servers/presence_connection_manager/handlers/buddy.py index a79971709..1b113545e 100644 --- a/src/servers/presence_connection_manager/handlers/buddy.py +++ b/src/servers/presence_connection_manager/handlers/buddy.py @@ -5,11 +5,6 @@ LoginHandlerBase, ) from servers.presence_connection_manager.applications.client import Client -from servers.presence_connection_manager.applications.data import ( - get_blocked_profile_id_list, - get_friend_profile_id_list, - delete_friend_by_profile_id, -) from servers.presence_connection_manager.contracts.requests.buddy import ( DelBuddyRequest, StatusInfoRequest, @@ -40,9 +35,6 @@ class BlockListHandler(CmdHandlerBase): def __init__(self, client: Client) -> None: assert isinstance(client, Client) - def _data_operation(self) -> None: - self._result.profile_ids = get_blocked_profile_id_list(self._client.info.profile_id, self._client.info.namespace_id) - def _response_construct(self) -> None: self._response = BlockListResponse(self._result) @@ -54,10 +46,6 @@ def __init__(self, client: Client): assert isinstance(client, Client) self._client = client - def _data_operation(self) -> None: - friends_id = get_friend_profile_id_list(self._client.info.profile_id, self._client.info.namespace_id) - self._result.profile_ids = friends_id - def response_construct(self): self._response = BuddyListResponse(self._request, self._result) @@ -90,9 +78,6 @@ def __init__(self, client: Client, request: RequestBase) -> None: raise NotImplementedError() super().__init__(client, request) - def _data_operation(self) -> None: - raise NotImplementedError() - class DelBuddyHandler(LoginHandlerBase): _request: DelBuddyRequest @@ -101,22 +86,13 @@ def __init__(self, client: Client, request: DelBuddyRequest) -> None: assert isinstance(request, DelBuddyRequest) super().__init__(client, request) - def _data_operation(self) -> None: - delete_friend_by_profile_id(self._client.info.profile_id, self._request.friend_profile_id, self._client.info.namespace_id) - class InviteToHandler(LoginHandlerBase): def __init__(self, client: Client, request: RequestBase) -> None: raise NotImplementedError() super().__init__(client, request) - def _data_operation(self) -> None: - if(self._client == None): - return - else: - # TODO - # Parse user to Buddy Message System - raise NotImplementedError() + pass class StatusHandler(CmdHandlerBase): @@ -140,16 +116,6 @@ def __init__(self, client: Client, request: StatusInfoRequest) -> None: assert isinstance(request, StatusInfoRequest) super().__init__(client, request) - def _data_operation(self) -> None: - if(self._request.is_get_status_info): - if(self._client != None): - # User is not online we do not need to send status info - self._result.status_info = self._client.info.status_info - else: - self._result.status_info = self._request.status_info - # TODO - # Notify every online friend? - def _response_send(self) -> None: if self._request.is_get_status_info: self._response = StatusInfoResponse(self._request, self._result) diff --git a/src/servers/presence_connection_manager/handlers/general.py b/src/servers/presence_connection_manager/handlers/general.py index d42ba38f0..c675ce955 100644 --- a/src/servers/presence_connection_manager/handlers/general.py +++ b/src/servers/presence_connection_manager/handlers/general.py @@ -26,16 +26,11 @@ def __init__(self, client: Client, request: KeepAliveRequest) -> None: assert isinstance(request, KeepAliveRequest) super().__init__(client, request) - def _data_operation(self) -> None: - # We need to keep the player cache online - # So that other players can find the player - pass - def _response_construct(self) -> None: self._response = KeepAliveResponse(self._request) -class LoginHandler(CmdHandlerBase): +class LoginHandelr(CmdHandlerBase): _request: LoginRequest _result: LoginResult @@ -55,9 +50,6 @@ def __init__(self, client: Client, request: LogoutRequest) -> None: assert isinstance(request, LogoutRequest) super().__init__(client, request) - def _data_operation(self) -> None: - self._client.connection.disconnect() - import servers.presence_search_player.handlers.handlers diff --git a/src/servers/presence_connection_manager/handlers/profile.py b/src/servers/presence_connection_manager/handlers/profile.py index 80f407b58..bd4eed2ae 100644 --- a/src/servers/presence_connection_manager/handlers/profile.py +++ b/src/servers/presence_connection_manager/handlers/profile.py @@ -1,9 +1,7 @@ -from library.database.pg_orm import Profiles from servers.chat.contracts.requests.general import RegisterNickRequest from servers.presence_connection_manager.abstractions.contracts import RequestBase from servers.presence_connection_manager.abstractions.handler import CmdHandlerBase from servers.presence_connection_manager.applications.client import Client -from servers.presence_connection_manager.applications.data import add_nick_name, get_profile_info_list, update_block_info_list, update_profile_info, update_subprofile_info, update_unique_nick from servers.presence_connection_manager.contracts.requests.profile import ( AddBlockRequest, GetProfileRequest, @@ -26,9 +24,6 @@ def __init__(self, client: Client, request: AddBlockRequest) -> None: assert isinstance(request, AddBlockRequest) super().__init__(client, request) - def _data_operation(self) -> None: - update_block_info_list(self._request.taget_id, self._client.info.profile_id, self._client.info.namespace_id) - class GetProfileHandler(CmdHandlerBase): _request: GetProfileRequest @@ -38,13 +33,6 @@ def __init__(self, client: Client, request: GetProfileRequest) -> None: assert isinstance(request, GetProfileRequest) super().__init__(client, request) - def data_operation(self) -> None: - self._result.user_profile = get_profile_info_list(self._request.profile_id, self._client.info.namespace_id) - if(self._result.user_profile is None): - # TODO - # raise right exception - raise NotImplementedError() - def _response_construct(self) -> None: self._response = GetProfileResponse(self._request, self._result) @@ -57,12 +45,6 @@ def __init__(self, client: Client, request: NewProfileRequest) -> None: assert isinstance(request, NewProfileRequest) super().__init__(client, request) - def _data_operation(self) -> None: - if(self._request.is_replace_nick_name): - update_unique_nick(self._client.info.profile_id, self._request.new_nick) - else: - add_nick_name(self._client.info.profile_id, self._request.new_nick, self._request.new_nick) - def _response_construct(self) -> None: self._response = NewProfileResponse(self._request, self._result) @@ -74,11 +56,6 @@ def __init__(self, client: Client, request: RegisterCDKeyRequest) -> None: assert isinstance(request, RegisterCDKeyRequest) super().__init__(client, request) - def _data_operation(self) -> None: - self._request.cdkey_enc = self._request.cdkey_enc - # TODO - # update subprofile info - update_subprofile_info(self._client.info.sub_profile_id) class RegisterNickHandler(CmdHandlerBase): @@ -88,9 +65,6 @@ def __init__(self, client: Client, request: RegisterNickRequest) -> None: assert isinstance(request, RegisterNickRequest) super().__init__(client, request) - def data_operation(self) -> None: - update_unique_nick(self._client.info.profile_id, self._request.unique_nick) - def _response_construct(self) -> None: self._response = RegisterNickResponse(self._request, self._result) @@ -98,6 +72,7 @@ def _response_construct(self) -> None: class RemoveBlockHandler(CmdHandlerBase): def __init__(self, client: Client, request: RequestBase) -> None: raise NotImplementedError() + super().__init__(client, request) @@ -108,31 +83,6 @@ def __init__(self, client: Client, request: UpdateProfileRequest) -> None: assert isinstance(request, UpdateProfileRequest) super().__init__(client, request) - def _data_operation(self) -> None: - profile = self._client.info - if(self._request.has_public_mask_flag): - profile.public_mask = self._request.public_mask - if(self._request.has_first_name_flag): - profile.first_name = self._request.first_name - if(self._request.has_last_name_flag): - profile.last_name = self._request.last_name - if(self._request.has_icq_flag): - profile.icq = self._request.icq_uin - if(self._request.has_home_page_flag): - profile.home_page = self._request.home_page - if(self._request.has_birthday_flag): - profile.birth_day = self._request.birth_day - profile.birth_month = self._request.birth_month - profile.birth_year = self._request.birth_year - if(self._request.has_sex_flag): - profile.sex = self._request.sex - if(self._request.has_zip_code): - profile.zip_code = self._request.zip_code - if(self._request.has_country_flag): - profile.country_code = self._request.country_code - - update_profile_info(Profiles(profile)) - class UpdateUserInfoHandler(CmdHandlerBase): raise NotImplementedError() From 26c22295874fa101b0ec6fb021a19bdeefbfdd9f Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 20 Jul 2024 23:18:04 +0000 Subject: [PATCH 079/231] refactor: merge code from Bildcraft1/UniSpyServer --- .../tests/buddy_requests.py | 42 +++++++++++ .../presence_connection_manager/tests/game.py | 31 ++++++++ .../tests/general_requests.py | 74 +++++++++++++++++++ .../tests/profile_requests.py | 61 +++++++++++++++ 4 files changed, 208 insertions(+) create mode 100644 src/servers/presence_connection_manager/tests/buddy_requests.py create mode 100644 src/servers/presence_connection_manager/tests/game.py create mode 100644 src/servers/presence_connection_manager/tests/general_requests.py create mode 100644 src/servers/presence_connection_manager/tests/profile_requests.py diff --git a/src/servers/presence_connection_manager/tests/buddy_requests.py b/src/servers/presence_connection_manager/tests/buddy_requests.py new file mode 100644 index 000000000..d5111bf6d --- /dev/null +++ b/src/servers/presence_connection_manager/tests/buddy_requests.py @@ -0,0 +1,42 @@ +import unittest + +from servers.presence_connection_manager.contracts.requests.buddy import ( + AddBuddyRequest, + DelBuddyRequest, + InviteToRequest, + StatusRequest, +) + + +class BuddyRequestTest(unittest.TestCase): + ADD_BUDDY = "\\addbuddy\\\\sesskey\\0\\newprofileid\\0\\reason\\test\\final\\" + DEL_BUDDY = "\\delbuddy\\\\sesskey\\0\\delprofileid\\0\\final\\" + INVITE_TO = "\\inviteto\\\\sesskey\\0\\productid\\0\\profileid\\0\\final\\" + STATUS = "\\status\\0\\statstring\\test\\locstring\\test\\final\\" + + def test_add_buddy(self) -> None: + request = AddBuddyRequest(BuddyRequestTest.ADD_BUDDY) + request.parse() + self.assertEqual(0, request.friend_profile_id) + self.assertEqual("test", request.reason) + + def test_del_buddy(self) -> None: + request = DelBuddyRequest(BuddyRequestTest.DEL_BUDDY) + request.parse() + self.assertEqual(0, request.friend_profile_id) + + def test_invite_to(self) -> None: + request = InviteToRequest(BuddyRequestTest.INVITE_TO) + request.parse() + self.assertEqual(0, request.product_id) + self.assertEqual(0, request.profile_id) + + def test_status_test(self) -> None: + request = StatusRequest(BuddyRequestTest.STATUS) + request.parse() + self.assertEqual("test", request.status.status_string) + self.assertEqual("test", request.status.location_string) + + +if __name__ == "__main__": + unittest.main() \ No newline at end of file diff --git a/src/servers/presence_connection_manager/tests/game.py b/src/servers/presence_connection_manager/tests/game.py new file mode 100644 index 000000000..c1e432023 --- /dev/null +++ b/src/servers/presence_connection_manager/tests/game.py @@ -0,0 +1,31 @@ +import unittest + +from servers.presence_connection_manager.contracts.requests.buddy import StatusRequest + + +class GameTest(unittest.TestCase): + def test_civilization_4(self) -> None: + raw_requests = [ + "\\newuser\\\\email\\civ4@unispy.org\\nick\\civ4-tk\\passwordenc\\JMHGwQ__\\productid\\10435\\gamename\\civ4\\namespaceid\\17\\uniquenick\\civ4-tk\\id\\1\\final\\", + "\\login\\\\challenge\\xMsHUXuWNXL3KMwmhoQZJrP0RVsArCYT\\uniquenick\\civ4-tk\\userid\\25\\profileid\\26\\response\\7f2c9c6685570ea18b7207d2cbd72452\\firewall\\1\\port\\0\\productid\\10435\\gamename\\civ4\\namespaceid\\17\\sdkrevision\\1\\id\\1\\final\\", + ] + for x in raw_requests: + # TODO MokeObject Implementation + pass + + def test_conflict_global_storm(self) -> None: + raw_requests = [ + "\\lc\\1\\challenge\\NRNUJLZMLX\\id\\1\\final\\", + "\\login\\\\challenge\\KMylyQbZfqzKn9otxx32q4076sOUnKif\\user\\cgs1@cgs1@rs.de\\response\\c1a6638bbcfe130e4287bfe4aa792949\\port\\-15737\\productid\\10469\\gamename\\conflictsopc\\namespaceid\\1\\id\\1\\final\\", + "\\inviteto\\\\sesskey\\58366\\products\\1038\\final\\", + ] + for x in raw_requests: + # TODO MokeObject Implementation + pass + + def test_sbwfrontps2(self) -> None: + raw = "\\status\\1\\sesskey\\1111\\statstring\\EN LIGNE\\locstring\\\\final\\" + request = StatusRequest(raw) + request.parse() + self.assertTrue(request.status.location_string == "") + self.assertTrue(request.status.status_string == "EN LIGNE") \ No newline at end of file diff --git a/src/servers/presence_connection_manager/tests/general_requests.py b/src/servers/presence_connection_manager/tests/general_requests.py new file mode 100644 index 000000000..c536a7793 --- /dev/null +++ b/src/servers/presence_connection_manager/tests/general_requests.py @@ -0,0 +1,74 @@ +import unittest +from servers.presence_connection_manager.contracts.requests.general import LoginRequest +from servers.presence_connection_manager.enums.general import ( + LoginType, + QuietModeType, + SdkRevisionType, +) + + +class GeneralRequestTest(unittest.TestCase): + LOGIN_AUTH_TOKEN = "\\login\\\\challenge\\xxxx\\authtoken\\xxxx\\userid\\0\\profileid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\3\\quiet\\0\\id\\1\\final\\" + LOGIN_UNIQUE_NICK = "\\login\\\\challenge\\xxxx\\uniquenick\\spyguy\\userid\\0\\profileid\\0\\namespaceid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\3\\quiet\\0\\id\\1\\final\\" + LOGIN_USER = "\\login\\\\challenge\\xxxx\\user\\spyguy@spyguy@unispy.org\\userid\\0\\profileid\\0\\partnerid\\0\\namespaceid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\3\\quiet\\0\\id\\1\\final\\" + + def test_login_auth_token(self) -> None: + request = LoginRequest(GeneralRequestTest.LOGIN_AUTH_TOKEN) + request.parse() + self.assertEqual(LoginType.AUTH_TOKEN, request.type) + self.assertEqual("xxxx", request.user_challenge) + self.assertEqual("xxxx", request.auth_token) + self.assertEqual(0, request.user_id) + self.assertEqual(0, request.profile_id) + self.assertEqual(0, request.partner_id) + self.assertEqual("xxxxx", request.response) + self.assertEqual("1", request.firewall) + self.assertEqual(request.game_port, 0) + self.assertEqual(request.product_id, 0) + self.assertEqual("gmtest", request.game_name) + self.assertEqual( + SdkRevisionType.GPINEW_STATUS_NOTIFICATION, request.sdk_revision_type) + self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) + + def test_login_unique_nick(self) -> None: + request = LoginRequest(GeneralRequestTest.LOGIN_UNIQUE_NICK) + request.parse() + self.assertEqual(LoginType.UNIQUENICK_NAMESPACE_ID, request.type) + self.assertEqual("xxxx", request.user_challenge) + self.assertEqual("spyguy", request.unique_nick) + self.assertEqual(0, request.namespace_id) + self.assertEqual(0, request.user_id) + self.assertEqual(0, request.profile_id) + self.assertEqual(0, request.partner_id) + self.assertEqual("xxxxx", request.response) + self.assertEqual("1", request.firewall) + self.assertEqual(0, request.game_port) + self.assertEqual(0, request.product_id) + self.assertEqual("gmtest", request.game_name) + self.assertEqual( + SdkRevisionType.GPINEW_STATUS_NOTIFICATION, request.sdk_revision_type) + self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) + + def test_login_user(self) -> None: + request = LoginRequest(GeneralRequestTest.LOGIN_USER) + request.parse() + self.assertEqual(LoginType.NICK_EMAIL, request.type) + self.assertEqual("xxxx", request.user_challenge) + self.assertEqual("spyguy", request.nick) + self.assertEqual("spyguy@unispy.org", request.email) + self.assertEqual(0, request.namespace_id) + self.assertEqual(0, request.user_id) + self.assertEqual(0, request.profile_id) + self.assertEqual(0, request.partner_id) + self.assertEqual("xxxxx", request.response) + self.assertEqual("1", request.firewall) + self.assertEqual(0, request.game_port) + self.assertEqual(0, request.product_id) + self.assertEqual("gmtest", request.game_name) + self.assertEqual( + SdkRevisionType.GPINEW_STATUS_NOTIFICATION, request.sdk_revision_type) + self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/servers/presence_connection_manager/tests/profile_requests.py b/src/servers/presence_connection_manager/tests/profile_requests.py new file mode 100644 index 000000000..9f733e1f1 --- /dev/null +++ b/src/servers/presence_connection_manager/tests/profile_requests.py @@ -0,0 +1,61 @@ +import unittest + +from servers.presence_connection_manager.contracts.requests.profile import ( + AddBlockRequest, + GetProfileRequest, + NewProfileRequest, + RegisterCDKeyRequest, + RegisterNickRequest, + UpdateProfileRequest, +) + + +class ProfileRequestTest(unittest.TestCase): + ADD_BLOCK = "\\addblock\\profileid\\0\\final\\" + GET_PROFILE = "\\getprofile\\sesskey\\xxxx\\profileid\\0\\final\\" + NEW_PROFILE = "\\newprofile\\sesskey\\xxxx\\nick\\spyguy\\id\\1\\final\\" + NEW_PROFILE_REPLACE = "\\newprofile\\sesskey\\xxxx\\nick\\spyguy2\\replace\\1\\oldnick\\spyguy\\id\\1\\final\\" + REGISTER_CD_KEY = "\\registercdkey\\sesskey\\xxxx\\cdkeyenc\\xxxx\\id\\1\\final\\" + REGISTER_NICK = "\\registernick\\sesskey\\xxxx\\uniquenick\\spyguy\\partnerid\\0\\id\\1\\final\\" + + def test_add_block(self) -> None: + request = AddBlockRequest(ProfileRequestTest.ADD_BLOCK) + request.parse() + self.assertEqual(0, request.taget_id) + + def test_get_profile(self) -> None: + request = GetProfileRequest(ProfileRequestTest.GET_PROFILE) + request.parse() + self.assertEqual("xxxx", request.session_key) + self.assertEqual(0, request.profile_id) + + def test_new_profile(self) -> None: + request = NewProfileRequest(ProfileRequestTest.NEW_PROFILE) + request.parse() + self.assertEqual("xxxx", request.session_key) + self.assertEqual("spyguy", request.new_nick) + + def test_new_profile_replace(self) -> None: + request = NewProfileRequest(ProfileRequestTest.NEW_PROFILE_REPLACE) + request.parse() + self.assertEqual("xxxx", request.session_key) + self.assertEqual("spyguy2", request.new_nick) + self.assertEqual("spyguy", request.old_nick) + + def test_register_cd_key(self) -> None: + request = RegisterCDKeyRequest(ProfileRequestTest.REGISTER_CD_KEY) + request.parse() + self.assertEqual("xxxx", request.session_key) + self.assertEqual("xxxx", request.cdkey_enc) + + def test_register_nick(self) -> None: + request = RegisterNickRequest(ProfileRequestTest.REGISTER_NICK) + request.parse() + self.assertEqual("xxxx", request.session_key) + self.assertEqual("spyguy", request.unique_nick) + self.assertEqual(0, request.partner_id) + + def test_update_profile(self) -> None: + crysisWarsRaw = "\\updatepro\\sesskey\\1111\\countrycode\\DE\\birthday\\168232912\\partnerid\\0\\final\\" + request = UpdateProfileRequest(crysisWarsRaw) + request.Parse() \ No newline at end of file From 7fc31e12516bd0183cecd61fc2e5ba085dd0de6e Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 20 Jul 2024 23:49:33 +0000 Subject: [PATCH 080/231] refactor: split src and tests folder --- src/.devcontainer/devcontainer.json | 4 +- .../library/abstractions/cmd_handler_base.py | 2 +- src/backends/gamespy/protocols/natneg/data.py | 19 +++++++--- .../gamespy/protocols/natneg/requests.py | 24 ++++++------ .../contracts/requests.py | 6 +-- .../presence_search_player/requests.py | 2 +- src/library/{ => src}/__init__.py | 0 .../{ => src}/abstractions/__init__.py | 0 src/library/{ => src}/abstractions/brocker.py | 0 src/library/{ => src}/abstractions/client.py | 20 +++++----- .../{ => src}/abstractions/connections.py | 8 ++-- .../{ => src}/abstractions/contracts.py | 0 .../{ => src}/abstractions/enctypt_base.py | 0 src/library/{ => src}/abstractions/handler.py | 16 ++++---- .../{ => src}/abstractions/redis_channel.py | 0 .../abstractions/server_launcher_base.py | 10 ++++- .../{ => src}/abstractions/switcher.py | 6 +-- src/library/{ => src}/database/__init__.py | 0 src/library/{ => src}/database/mongodb_orm.py | 2 +- src/library/{ => src}/database/pg_orm.py | 2 +- src/library/{ => src}/database/redis.py | 2 +- src/library/{ => src}/encryption/__init__.py | 0 src/library/{ => src}/encryption/encoding.py | 0 .../{ => src}/encryption/gs_encryption.py | 2 +- .../{ => src}/encryption/xor_encryption.py | 2 +- src/library/{ => src}/exceptions/__init__.py | 0 src/library/{ => src}/exceptions/error.py | 2 +- src/library/{ => src}/extentions/__init__.py | 0 .../{ => src}/extentions/bytes_extentions.py | 0 src/library/{ => src}/extentions/encoding.py | 0 .../{ => src}/extentions/gamespy_ramdoms.py | 0 .../{ => src}/extentions/gamespy_utils.py | 0 .../{ => src}/extentions/password_encoder.py | 2 +- src/library/{ => src}/extentions/redis_orm.py | 0 .../{ => src}/extentions/string_extentions.py | 0 src/library/{ => src}/log/__init__.py | 0 src/library/{ => src}/log/log_manager.py | 0 src/library/{ => src}/network/__init__.py | 0 .../http => src/network}/http_handler.py | 6 +-- .../tcp => src/network}/tcp_handler.py | 8 ++-- .../udp => src/network}/udp_handler.py | 10 ++--- src/library/{ => src}/unispy_server_config.py | 2 +- .../chat/applications/server_launcher.py | 18 --------- .../chat/{ => src}/abstractions/channel.py | 8 ++-- .../chat/{ => src}/abstractions/contract.py | 8 ++-- .../chat/{ => src}/abstractions/handler.py | 10 ++--- .../chat/{ => src}/abstractions/message.py | 8 ++-- .../chat/{ => src}/aggregates/brockers.py | 4 +- .../chat/{ => src}/aggregates/channel.py | 18 ++++----- .../chat/{ => src}/aggregates/channel_user.py | 6 +-- .../{ => src}/aggregates/key_value_manager.py | 0 .../chat/{ => src}/aggregates/peer_room.py | 2 +- .../{ => src}/aggregates/response_name.py | 0 .../chat/{ => src}/aggregates/storage_info.py | 0 .../chat/{ => src}/applications/client.py | 4 +- .../chat/src/applications/server_launcher.py | 20 ++++++++++ .../chat/{ => src}/applications/switcher.py | 18 ++++----- .../chat/src/contracts}/__init__.py | 0 .../chat/src/contracts/requests}/__init__.py | 0 .../{ => src}/contracts/requests/channel.py | 8 ++-- .../{ => src}/contracts/requests/general.py | 10 ++--- .../{ => src}/contracts/requests/message.py | 2 +- .../chat/src/contracts/responses}/__init__.py | 0 .../{ => src}/contracts/responses/channel.py | 14 +++---- .../{ => src}/contracts/responses/general.py | 14 +++---- .../{ => src}/contracts/responses/message.py | 10 ++--- .../contracts/results}/__init__.py | 0 .../{ => src}/contracts/results/channel.py | 2 +- .../{ => src}/contracts/results/general.py | 2 +- .../{ => src}/contracts/results/message.py | 2 +- src/servers/chat/{ => src}/enums/general.py | 0 .../chat/{ => src}/enums/irc_error_code.py | 0 src/servers/chat/{ => src}/enums/peer_room.py | 0 .../chat/{ => src}/exceptions/channel.py | 4 +- .../chat/{ => src}/exceptions/general.py | 6 +-- .../chat/{ => src}/handlers/channel.py | 16 ++++---- .../chat/{ => src}/handlers/general.py | 14 +++---- .../chat/{ => src}/handlers/message.py | 10 ++--- .../applications/connection_listener.py | 2 +- .../{ => src}/applications/router.py | 2 +- .../{ => src}/contracts/general.py | 2 +- .../{ => src}/abstractions/contracts.py | 12 +++--- .../natneg/{ => src}/abstractions/handlers.py | 8 ++-- .../aggregations/init_packet_info.py | 8 ++-- .../aggregations/relay_server_info.py | 0 .../natneg/{ => src}/applications/client.py | 6 +-- .../natneg/{ => src}/contracts/requests.py | 6 +-- .../natneg/{ => src}/contracts/responses.py | 2 +- .../natneg/{ => src}/contracts/results.py | 4 +- src/servers/natneg/{ => src}/enums/general.py | 0 .../natneg/{ => src}/handlers/handlers.py | 14 +++---- .../natneg/{ => src}/handlers/switcher.py | 4 +- src/servers/natneg/tests/tests.py | 6 +-- .../{ => src}/abstractions/contracts.py | 16 ++++---- .../{ => src}/abstractions/handler.py | 10 ++--- .../{ => src}/aggregates/login_challenge.py | 2 +- .../{ => src}/aggregates/sdk_revision.py | 2 +- .../{ => src}/aggregates/user_status.py | 2 +- .../{ => src}/aggregates/user_status_info.py | 0 .../{ => src}/applications/client.py | 20 +++++----- .../{ => src}/applications/data.py | 4 +- .../{ => src}/contracts/requests/buddy.py | 10 ++--- .../{ => src}/contracts/requests/general.py | 6 +-- .../{ => src}/contracts/requests/profile.py | 8 ++-- .../{ => src}/contracts/responses/buddy.py | 6 +-- .../{ => src}/contracts/responses/general.py | 16 ++++---- .../{ => src}/contracts/responses/profile.py | 9 ++--- .../{ => src}/contracts/results/buddy.py | 6 +-- .../{ => src}/contracts/results/general.py | 2 +- .../{ => src}/contracts/results/profile.py | 2 +- .../{ => src}/enums/general.py | 0 .../{ => src}/handlers/buddy.py | 12 +++--- .../{ => src}/handlers/general.py | 20 +++++----- .../{ => src}/handlers/profile.py | 14 +++---- .../{ => src}/handlers/switcher.py | 24 ++++++------ .../tests/buddy_requests.py | 2 +- .../presence_connection_manager/tests/game.py | 2 +- .../tests/general_requests.py | 4 +- .../tests/profile_requests.py | 2 +- .../applications/client.py | 8 ---- .../{ => src}/abstractions/contracts.py | 12 +++--- .../{ => src}/abstractions/handler.py | 8 ++-- .../src/applications/client.py | 8 ++++ .../{ => src}/applications/data.py | 2 +- .../src/contracts}/__init__.py | 0 .../{ => src}/contracts/requests.py | 10 ++--- .../{ => src}/contracts/responses.py | 6 +-- .../{ => src}/contracts/results.py | 4 +- .../{ => src}/enums/error_codes.py | 0 .../{ => src}/enums/general.py | 0 .../{ => src}/exceptions/general.py | 6 +-- .../{ => src}/handlers/handlers.py | 12 +++--- .../{ => src}/handlers/switcher.py | 12 +++--- .../query_report/exceptions/exceptions.py | 5 --- .../{ => src}/aggregates/game_server_info.py | 2 +- .../{ => src}/aggregates/natneg_cookie.py | 0 .../{ => src}/aggregates/peer_room_info.py | 0 .../{ => src}/applications/client.py | 4 +- .../{ => src}/applications/data.py | 2 +- .../query_report/src/exceptions/exceptions.py | 5 +++ .../src/v1}/__init__.py | 0 .../src/v1/abstractions}/__init__.py | 0 .../{ => src}/v1/abstractions/contracts.py | 10 ++--- .../src/v1/abstractions/handlers.py | 10 +++++ .../v1/aggregations/game_server_info_v1.py | 0 .../v2/abstractions/cmd_handler_base.py | 6 +-- .../{ => src}/v2/abstractions/request_base.py | 8 ++-- .../src/v2/abstractions/response_base.py | 9 +++++ .../src/v2/abstractions/result_base.py | 7 ++++ .../v2/aggregates/game_server_info_v2.py | 4 +- .../{ => src}/v2/aggregates/natneg_cookie.py | 0 .../{ => src}/v2/applications/data.py | 0 .../{ => src}/v2/applications/switcher.py | 16 ++++---- .../{ => src}/v2/contracts/requests.py | 10 ++--- .../{ => src}/v2/contracts/responses.py | 12 +++--- .../{ => src}/v2/contracts/results.py | 6 +-- .../{ => src}/v2/enums/general.py | 0 .../{ => src}/v2/handlers/handlers.py | 12 +++--- .../query_report/v1/abstractions/handlers.py | 10 ----- .../v2/abstractions/response_base.py | 9 ----- .../v2/abstractions/result_base.py | 7 ---- .../{ => src}/exceptions/general.py | 2 +- .../src/v2}/__init__.py | 0 .../src/v2/abstractions}/__init__.py | 0 .../{ => src}/v2/abstractions/contracts.py | 18 ++++----- .../{ => src}/v2/abstractions/handlers.py | 10 ++--- .../{ => src}/v2/aggregations/encryption.py | 2 +- .../v2/aggregations/server_info_builder.py | 8 ++-- .../{ => src}/v2/aggregations/string_flags.py | 0 .../src/v2/applications}/__init__.py | 0 .../{ => src}/v2/applications/client.py | 4 +- .../{v2 => src/v2/contracts}/__init__.py | 0 .../{ => src}/v2/contracts/requests.py | 4 +- .../{ => src}/v2/contracts/responses.py | 14 +++---- .../{ => src}/v2/contracts/results.py | 6 +-- .../{ => src}/v2/enums/general.py | 0 .../{ => src}/v2/handlers/handlers.py | 20 +++++----- src/servers/webservices/exceptions/general.py | 6 --- .../modules/auth/exceptions/general.py | 5 --- .../modules/patching_and_tracking/__init__.py | 0 .../webservices/modules/racing/__init__.py | 0 .../webservices/modules/sake/__init__.py | 0 .../modules/sake/contracts/responses.py | 26 ------------- .../modules/sake/exceptions/general.py | 5 --- .../{ => src}/abstractions/contracts.py | 6 +-- .../{ => src}/abstractions/handler.py | 6 +-- .../{ => src}/aggregations/soap_envelop.py | 0 .../{ => src}/applications/client.py | 2 +- .../webservices/src/exceptions/general.py | 6 +++ .../src/modules}/__init__.py | 0 .../{ => src}/modules/altas/___init__.py | 0 .../{ => src}/modules/auth/___init__.py | 0 .../modules/auth/abstractions/___init__.py | 0 .../modules/auth/abstractions/general.py} | 8 ++-- .../modules/auth/contracts/requests.py | 4 +- .../modules/auth/contracts/responses.py | 6 +-- .../modules/auth/contracts/results.py | 2 +- .../src/modules/auth/exceptions/general.py | 5 +++ .../modules/auth/handlers/general.py | 10 ++--- .../src/modules/direct2game}/__init__.py | 0 .../direct2game/abstractions}/__init__.py | 0 .../direct2game/abstractions/contracts.py | 4 +- .../direct2game/abstractions/handler.py | 0 .../direct2game/contracts}/__init__.py | 0 .../modules/direct2game/contracts/requests.py | 4 +- .../direct2game/contracts/responses.py | 4 +- .../modules/direct2game/contracts/results.py | 2 +- .../modules/direct2game/handlers/general.py | 12 +++--- .../modules/in_game_ad}/__init__.py | 0 .../patching_and_tracking}/__init__.py | 0 .../modules/racing}/__init__.py | 0 .../modules/sake}/__init__.py | 0 .../modules/sake/abstractions/general.py} | 23 +++++++---- .../modules/sake/contracts/requests.py | 6 +-- .../src/modules/sake/contracts/responses.py | 38 +++++++++++++++++++ .../modules/sake/contracts/results.py | 7 +++- .../src/modules/sake/exceptions/general.py | 5 +++ .../src/modules/sake/handlers/general.py | 31 +++++++++++++++ src/servers/webservices/tests/sake.py | 2 +- src/tests/library/encrypt_test.py | 2 +- 220 files changed, 657 insertions(+), 582 deletions(-) rename src/library/{ => src}/__init__.py (100%) rename src/library/{ => src}/abstractions/__init__.py (100%) rename src/library/{ => src}/abstractions/brocker.py (100%) rename src/library/{ => src}/abstractions/client.py (81%) rename src/library/{ => src}/abstractions/connections.py (90%) rename src/library/{ => src}/abstractions/contracts.py (100%) rename src/library/{ => src}/abstractions/enctypt_base.py (100%) rename src/library/{ => src}/abstractions/handler.py (82%) rename src/library/{ => src}/abstractions/redis_channel.py (100%) rename src/library/{ => src}/abstractions/server_launcher_base.py (83%) rename src/library/{ => src}/abstractions/switcher.py (88%) rename src/library/{ => src}/database/__init__.py (100%) rename src/library/{ => src}/database/mongodb_orm.py (82%) rename src/library/{ => src}/database/pg_orm.py (99%) rename src/library/{ => src}/database/redis.py (84%) rename src/library/{ => src}/encryption/__init__.py (100%) rename src/library/{ => src}/encryption/encoding.py (100%) rename src/library/{ => src}/encryption/gs_encryption.py (97%) rename src/library/{ => src}/encryption/xor_encryption.py (95%) rename src/library/{ => src}/exceptions/__init__.py (100%) rename src/library/{ => src}/exceptions/error.py (95%) rename src/library/{ => src}/extentions/__init__.py (100%) rename src/library/{ => src}/extentions/bytes_extentions.py (100%) rename src/library/{ => src}/extentions/encoding.py (100%) rename src/library/{ => src}/extentions/gamespy_ramdoms.py (100%) rename src/library/{ => src}/extentions/gamespy_utils.py (100%) rename src/library/{ => src}/extentions/password_encoder.py (97%) rename src/library/{ => src}/extentions/redis_orm.py (100%) rename src/library/{ => src}/extentions/string_extentions.py (100%) rename src/library/{ => src}/log/__init__.py (100%) rename src/library/{ => src}/log/log_manager.py (100%) rename src/library/{ => src}/network/__init__.py (100%) rename src/library/{network/http => src/network}/http_handler.py (92%) rename src/library/{network/tcp => src/network}/tcp_handler.py (89%) rename src/library/{network/udp => src/network}/udp_handler.py (82%) rename src/library/{ => src}/unispy_server_config.py (98%) delete mode 100644 src/servers/chat/applications/server_launcher.py rename src/servers/chat/{ => src}/abstractions/channel.py (88%) rename src/servers/chat/{ => src}/abstractions/contract.py (86%) rename src/servers/chat/{ => src}/abstractions/handler.py (58%) rename src/servers/chat/{ => src}/abstractions/message.py (78%) rename src/servers/chat/{ => src}/aggregates/brockers.py (89%) rename src/servers/chat/{ => src}/aggregates/channel.py (90%) rename src/servers/chat/{ => src}/aggregates/channel_user.py (81%) rename src/servers/chat/{ => src}/aggregates/key_value_manager.py (100%) rename src/servers/chat/{ => src}/aggregates/peer_room.py (97%) rename src/servers/chat/{ => src}/aggregates/response_name.py (100%) rename src/servers/chat/{ => src}/aggregates/storage_info.py (100%) rename src/servers/chat/{ => src}/applications/client.py (69%) create mode 100644 src/servers/chat/src/applications/server_launcher.py rename src/servers/chat/{ => src}/applications/switcher.py (90%) rename src/{library/network/http => servers/chat/src/contracts}/__init__.py (100%) rename src/{library/network/tcp => servers/chat/src/contracts/requests}/__init__.py (100%) rename src/servers/chat/{ => src}/contracts/requests/channel.py (97%) rename src/servers/chat/{ => src}/contracts/requests/general.py (95%) rename src/servers/chat/{ => src}/contracts/requests/message.py (74%) rename src/{library/network/udp => servers/chat/src/contracts/responses}/__init__.py (100%) rename src/servers/chat/{ => src}/contracts/responses/channel.py (93%) rename src/servers/chat/{ => src}/contracts/responses/general.py (91%) rename src/servers/chat/{ => src}/contracts/responses/message.py (86%) rename src/servers/chat/{contracts => src/contracts/results}/__init__.py (100%) rename src/servers/chat/{ => src}/contracts/results/channel.py (94%) rename src/servers/chat/{ => src}/contracts/results/general.py (95%) rename src/servers/chat/{ => src}/contracts/results/message.py (76%) rename src/servers/chat/{ => src}/enums/general.py (100%) rename src/servers/chat/{ => src}/enums/irc_error_code.py (100%) rename src/servers/chat/{ => src}/enums/peer_room.py (100%) rename src/servers/chat/{ => src}/exceptions/channel.py (90%) rename src/servers/chat/{ => src}/exceptions/general.py (93%) rename src/servers/chat/{ => src}/handlers/channel.py (91%) rename src/servers/chat/{ => src}/handlers/general.py (92%) rename src/servers/chat/{ => src}/handlers/message.py (84%) rename src/servers/game_traffic_relay/{ => src}/applications/connection_listener.py (86%) rename src/servers/game_traffic_relay/{ => src}/applications/router.py (71%) rename src/servers/game_traffic_relay/{ => src}/contracts/general.py (81%) rename src/servers/natneg/{ => src}/abstractions/contracts.py (86%) rename src/servers/natneg/{ => src}/abstractions/handlers.py (54%) rename src/servers/natneg/{ => src}/aggregations/init_packet_info.py (76%) rename src/servers/natneg/{ => src}/aggregations/relay_server_info.py (100%) rename src/servers/natneg/{ => src}/applications/client.py (68%) rename src/servers/natneg/{ => src}/contracts/requests.py (92%) rename src/servers/natneg/{ => src}/contracts/responses.py (84%) rename src/servers/natneg/{ => src}/contracts/results.py (86%) rename src/servers/natneg/{ => src}/enums/general.py (100%) rename src/servers/natneg/{ => src}/handlers/handlers.py (84%) rename src/servers/natneg/{ => src}/handlers/switcher.py (82%) rename src/servers/presence_connection_manager/{ => src}/abstractions/contracts.py (73%) rename src/servers/presence_connection_manager/{ => src}/abstractions/handler.py (65%) rename src/servers/presence_connection_manager/{ => src}/aggregates/login_challenge.py (91%) rename src/servers/presence_connection_manager/{ => src}/aggregates/sdk_revision.py (95%) rename src/servers/presence_connection_manager/{ => src}/aggregates/user_status.py (72%) rename src/servers/presence_connection_manager/{ => src}/aggregates/user_status_info.py (100%) rename src/servers/presence_connection_manager/{ => src}/applications/client.py (63%) rename src/servers/presence_connection_manager/{ => src}/applications/data.py (97%) rename src/servers/presence_connection_manager/{ => src}/contracts/requests/buddy.py (91%) rename src/servers/presence_connection_manager/{ => src}/contracts/requests/general.py (94%) rename src/servers/presence_connection_manager/{ => src}/contracts/requests/profile.py (96%) rename src/servers/presence_connection_manager/{ => src}/contracts/responses/buddy.py (92%) rename src/servers/presence_connection_manager/{ => src}/contracts/responses/general.py (72%) rename src/servers/presence_connection_manager/{ => src}/contracts/responses/profile.py (89%) rename src/servers/presence_connection_manager/{ => src}/contracts/results/buddy.py (60%) rename src/servers/presence_connection_manager/{ => src}/contracts/results/general.py (78%) rename src/servers/presence_connection_manager/{ => src}/contracts/results/profile.py (91%) rename src/servers/presence_connection_manager/{ => src}/enums/general.py (100%) rename src/servers/presence_connection_manager/{ => src}/handlers/buddy.py (87%) rename src/servers/presence_connection_manager/{ => src}/handlers/general.py (70%) rename src/servers/presence_connection_manager/{ => src}/handlers/profile.py (80%) rename src/servers/presence_connection_manager/{ => src}/handlers/switcher.py (62%) delete mode 100644 src/servers/presence_search_player/applications/client.py rename src/servers/presence_search_player/{ => src}/abstractions/contracts.py (71%) rename src/servers/presence_search_player/{ => src}/abstractions/handler.py (56%) create mode 100644 src/servers/presence_search_player/src/applications/client.py rename src/servers/presence_search_player/{ => src}/applications/data.py (99%) rename src/servers/{chat/contracts/requests => presence_search_player/src/contracts}/__init__.py (100%) rename src/servers/presence_search_player/{ => src}/contracts/requests.py (96%) rename src/servers/presence_search_player/{ => src}/contracts/responses.py (96%) rename src/servers/presence_search_player/{ => src}/contracts/results.py (91%) rename src/servers/presence_search_player/{ => src}/enums/error_codes.py (100%) rename src/servers/presence_search_player/{ => src}/enums/general.py (100%) rename src/servers/presence_search_player/{ => src}/exceptions/general.py (98%) rename src/servers/presence_search_player/{ => src}/handlers/handlers.py (82%) rename src/servers/presence_search_player/{ => src}/handlers/switcher.py (74%) delete mode 100644 src/servers/query_report/exceptions/exceptions.py rename src/servers/query_report/{ => src}/aggregates/game_server_info.py (89%) rename src/servers/query_report/{ => src}/aggregates/natneg_cookie.py (100%) rename src/servers/query_report/{ => src}/aggregates/peer_room_info.py (100%) rename src/servers/query_report/{ => src}/applications/client.py (70%) rename src/servers/query_report/{ => src}/applications/data.py (92%) create mode 100644 src/servers/query_report/src/exceptions/exceptions.py rename src/servers/{chat/contracts/responses => query_report/src/v1}/__init__.py (100%) rename src/servers/{chat/contracts/results => query_report/src/v1/abstractions}/__init__.py (100%) rename src/servers/query_report/{ => src}/v1/abstractions/contracts.py (64%) create mode 100644 src/servers/query_report/src/v1/abstractions/handlers.py rename src/servers/query_report/{ => src}/v1/aggregations/game_server_info_v1.py (100%) rename src/servers/query_report/{ => src}/v2/abstractions/cmd_handler_base.py (55%) rename src/servers/query_report/{ => src}/v2/abstractions/request_base.py (64%) create mode 100644 src/servers/query_report/src/v2/abstractions/response_base.py create mode 100644 src/servers/query_report/src/v2/abstractions/result_base.py rename src/servers/query_report/{ => src}/v2/aggregates/game_server_info_v2.py (89%) rename src/servers/query_report/{ => src}/v2/aggregates/natneg_cookie.py (100%) rename src/servers/query_report/{ => src}/v2/applications/data.py (100%) rename src/servers/query_report/{ => src}/v2/applications/switcher.py (74%) rename src/servers/query_report/{ => src}/v2/contracts/requests.py (94%) rename src/servers/query_report/{ => src}/v2/contracts/responses.py (85%) rename src/servers/query_report/{ => src}/v2/contracts/results.py (68%) rename src/servers/query_report/{ => src}/v2/enums/general.py (100%) rename src/servers/query_report/{ => src}/v2/handlers/handlers.py (84%) delete mode 100644 src/servers/query_report/v1/abstractions/handlers.py delete mode 100644 src/servers/query_report/v2/abstractions/response_base.py delete mode 100644 src/servers/query_report/v2/abstractions/result_base.py rename src/servers/server_browser/{ => src}/exceptions/general.py (69%) rename src/servers/{presence_search_player/contracts => server_browser/src/v2}/__init__.py (100%) rename src/servers/{query_report/v1 => server_browser/src/v2/abstractions}/__init__.py (100%) rename src/servers/server_browser/{ => src}/v2/abstractions/contracts.py (86%) rename src/servers/server_browser/{ => src}/v2/abstractions/handlers.py (82%) rename src/servers/server_browser/{ => src}/v2/aggregations/encryption.py (98%) rename src/servers/server_browser/{ => src}/v2/aggregations/server_info_builder.py (91%) rename src/servers/server_browser/{ => src}/v2/aggregations/string_flags.py (100%) rename src/servers/{query_report/v1/abstractions => server_browser/src/v2/applications}/__init__.py (100%) rename src/servers/server_browser/{ => src}/v2/applications/client.py (65%) rename src/servers/server_browser/{v2 => src/v2/contracts}/__init__.py (100%) rename src/servers/server_browser/{ => src}/v2/contracts/requests.py (93%) rename src/servers/server_browser/{ => src}/v2/contracts/responses.py (89%) rename src/servers/server_browser/{ => src}/v2/contracts/results.py (58%) rename src/servers/server_browser/{ => src}/v2/enums/general.py (100%) rename src/servers/server_browser/{ => src}/v2/handlers/handlers.py (85%) delete mode 100644 src/servers/webservices/exceptions/general.py delete mode 100644 src/servers/webservices/modules/auth/exceptions/general.py delete mode 100644 src/servers/webservices/modules/patching_and_tracking/__init__.py delete mode 100644 src/servers/webservices/modules/racing/__init__.py delete mode 100644 src/servers/webservices/modules/sake/__init__.py delete mode 100644 src/servers/webservices/modules/sake/contracts/responses.py delete mode 100644 src/servers/webservices/modules/sake/exceptions/general.py rename src/servers/webservices/{ => src}/abstractions/contracts.py (86%) rename src/servers/webservices/{ => src}/abstractions/handler.py (60%) rename src/servers/webservices/{ => src}/aggregations/soap_envelop.py (100%) rename src/servers/webservices/{ => src}/applications/client.py (97%) create mode 100644 src/servers/webservices/src/exceptions/general.py rename src/servers/{server_browser/v2/abstractions => webservices/src/modules}/__init__.py (100%) rename src/servers/webservices/{ => src}/modules/altas/___init__.py (100%) rename src/servers/webservices/{ => src}/modules/auth/___init__.py (100%) rename src/servers/webservices/{ => src}/modules/auth/abstractions/___init__.py (100%) rename src/servers/webservices/{modules/auth/abstractions/contracts.py => src/modules/auth/abstractions/general.py} (94%) rename src/servers/webservices/{ => src}/modules/auth/contracts/requests.py (96%) rename src/servers/webservices/{ => src}/modules/auth/contracts/responses.py (89%) rename src/servers/webservices/{ => src}/modules/auth/contracts/results.py (75%) create mode 100644 src/servers/webservices/src/modules/auth/exceptions/general.py rename src/servers/webservices/{ => src}/modules/auth/handlers/general.py (85%) rename src/servers/{server_browser/v2/applications => webservices/src/modules/direct2game}/__init__.py (100%) rename src/servers/{server_browser/v2/contracts => webservices/src/modules/direct2game/abstractions}/__init__.py (100%) rename src/servers/webservices/{ => src}/modules/direct2game/abstractions/contracts.py (63%) rename src/servers/webservices/{ => src}/modules/direct2game/abstractions/handler.py (100%) rename src/servers/webservices/{modules => src/modules/direct2game/contracts}/__init__.py (100%) rename src/servers/webservices/{ => src}/modules/direct2game/contracts/requests.py (93%) rename src/servers/webservices/{ => src}/modules/direct2game/contracts/responses.py (86%) rename src/servers/webservices/{ => src}/modules/direct2game/contracts/results.py (80%) rename src/servers/webservices/{ => src}/modules/direct2game/handlers/general.py (71%) rename src/servers/webservices/{modules/direct2game => src/modules/in_game_ad}/__init__.py (100%) rename src/servers/webservices/{modules/direct2game/abstractions => src/modules/patching_and_tracking}/__init__.py (100%) rename src/servers/webservices/{modules/direct2game/contracts => src/modules/racing}/__init__.py (100%) rename src/servers/webservices/{modules/in_game_ad => src/modules/sake}/__init__.py (100%) rename src/servers/webservices/{modules/sake/abstractions/contracts.py => src/modules/sake/abstractions/general.py} (57%) rename src/servers/webservices/{ => src}/modules/sake/contracts/requests.py (96%) create mode 100644 src/servers/webservices/src/modules/sake/contracts/responses.py rename src/servers/webservices/{ => src}/modules/sake/contracts/results.py (64%) create mode 100644 src/servers/webservices/src/modules/sake/exceptions/general.py create mode 100644 src/servers/webservices/src/modules/sake/handlers/general.py diff --git a/src/.devcontainer/devcontainer.json b/src/.devcontainer/devcontainer.json index 0cb8892ad..0fec28679 100644 --- a/src/.devcontainer/devcontainer.json +++ b/src/.devcontainer/devcontainer.json @@ -19,9 +19,9 @@ 28910, 28900, 80 - ] + ], // Use 'postCreateCommand' to run commands after the container is created. - // "postCreateCommand": "pip3 install --user -r requirements.txt", + "postCreateCommand": "pip3 install --user -r requirements.txt" // Configure tool-specific properties. // "customizations": {}, // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. diff --git a/src/backends/gamespy/library/abstractions/cmd_handler_base.py b/src/backends/gamespy/library/abstractions/cmd_handler_base.py index 9229fdc08..a93274701 100644 --- a/src/backends/gamespy/library/abstractions/cmd_handler_base.py +++ b/src/backends/gamespy/library/abstractions/cmd_handler_base.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: - from library.abstractions.request_base import RequestBase + from library.src.abstractions.contracts import RequestBase class CmdHandlerBase(abc.ABC): diff --git a/src/backends/gamespy/protocols/natneg/data.py b/src/backends/gamespy/protocols/natneg/data.py index b1713abd9..a62151f11 100644 --- a/src/backends/gamespy/protocols/natneg/data.py +++ b/src/backends/gamespy/protocols/natneg/data.py @@ -1,7 +1,16 @@ -from servers.natneg.contracts.requests import InitRequest -from servers.natneg.aggregations.init_packet_info import InitPacketInfo +# from servers.natneg.contracts.requests import InitRequest +from backends.gamespy.protocols.natneg.requests import InitRequest +from servers.natneg.src.aggregations.init_packet_info import InitPacketInfo +from mongoengine import QuerySet +def store_init_packet(request: InitRequest) -> None: + info = InitPacketInfo(request.model_dump()) + # InitPacketInfo.objects(server_id=info.server_id, cookie=info.cookie, version=info.version, port_type=info.port_type, + # client_index=info.client_index, game_name=info.game_name, use_game_port=info.use_game_port, + # public_ip=info.public_ip, public_port=info.public_port, private_ip=info.private_ip, private_port=info.private_port) + info.update(upsert=True) -def store_init_packet(request: InitRequest): - json_dict = request.to_serializable_dict() - info = InitPacketInfo(json_dict) + +def count_inif_info(cookie: int, version: int) -> int: + result = InitPacketInfo.objects(cookie=cookie, version=version).count() + return result diff --git a/src/backends/gamespy/protocols/natneg/requests.py b/src/backends/gamespy/protocols/natneg/requests.py index 79513449e..a5e22bcd8 100644 --- a/src/backends/gamespy/protocols/natneg/requests.py +++ b/src/backends/gamespy/protocols/natneg/requests.py @@ -1,5 +1,5 @@ from dataclasses import dataclass -from servers.natneg.enums.general import ( +from servers.natneg.src.enums.general import ( NatClientIndex, NatPortMappingScheme, NatPortType, @@ -11,28 +11,28 @@ import backends.gamespy.library.abstractions.request_base as lib -@dataclass + class RequestBase(lib.RequestBase): raw_request: list version: int - cookie: list + cookie: int port_type: NatPortType command_name: Union[RequestType, int] -@dataclass + class CommonRequestBase(RequestBase): client_index: Union[NatClientIndex, int] use_game_port: bool -@dataclass + class AddressCheckRequest(CommonRequestBase): pass -@dataclass + class ConnectAckRequest(RequestBase): client_index: Union[NatClientIndex, int] -@dataclass + class ConnectRequest(RequestBase): """ Server will send this request to client to let them connect to each other @@ -40,25 +40,25 @@ class ConnectRequest(RequestBase): client_index: Union[NatClientIndex, int] -@dataclass + class ErtAckRequest(CommonRequestBase): pass -@dataclass + class InitRequest(CommonRequestBase): game_name: str = None private_ip_address: str = None -@dataclass + class NatifyRequest(CommonRequestBase): pass -@dataclass + class PreInitRequest(RequestBase): state: Union[PreInitState, int] target_cookie: list -@dataclass + class ReportRequest(CommonRequestBase): is_nat_success: bool game_name: str diff --git a/src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py b/src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py index b0e172d17..5c55ba95d 100644 --- a/src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py +++ b/src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py @@ -1,11 +1,11 @@ from typing import Union from pydantic import UUID4, BaseModel -from servers.presence_connection_manager.aggregates.user_status import UserStatus -from servers.presence_connection_manager.aggregates.user_status_info import ( +from servers.presence_connection_manager.src.aggregates.user_status import UserStatus +from servers.presence_connection_manager.src.aggregates.user_status_info import ( UserStatusInfo, ) -from servers.presence_connection_manager.enums.general import ( +from servers.presence_connection_manager.src.enums.general import ( LoginType, PublicMasks, SdkRevisionType, diff --git a/src/backends/gamespy/protocols/presence_search_player/requests.py b/src/backends/gamespy/protocols/presence_search_player/requests.py index 91f2c868d..722704d37 100644 --- a/src/backends/gamespy/protocols/presence_search_player/requests.py +++ b/src/backends/gamespy/protocols/presence_search_player/requests.py @@ -1,7 +1,7 @@ import abc from dataclasses import dataclass from backends.gamespy.library.abstractions.request_base import RequestBase as RB -from servers.presence_connection_manager.enums.general import LoginType, SdkRevisionType +from servers.presence_connection_manager.src.enums.general import LoginType, SdkRevisionType class RequestBase(RB, abc.ABC): diff --git a/src/library/__init__.py b/src/library/src/__init__.py similarity index 100% rename from src/library/__init__.py rename to src/library/src/__init__.py diff --git a/src/library/abstractions/__init__.py b/src/library/src/abstractions/__init__.py similarity index 100% rename from src/library/abstractions/__init__.py rename to src/library/src/abstractions/__init__.py diff --git a/src/library/abstractions/brocker.py b/src/library/src/abstractions/brocker.py similarity index 100% rename from src/library/abstractions/brocker.py rename to src/library/src/abstractions/brocker.py diff --git a/src/library/abstractions/client.py b/src/library/src/abstractions/client.py similarity index 81% rename from src/library/abstractions/client.py rename to src/library/src/abstractions/client.py index d7f4bb1ea..6e5ac49b3 100644 --- a/src/library/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -1,17 +1,17 @@ import abc -from library.encryption.encoding import Encoding -from library.log.log_manager import LogWriter -from library.log.log_manager import LogWriter -from library.unispy_server_config import ServerConfig +from library.src.encryption.encoding import Encoding +from library.src.log.log_manager import LogWriter +from library.src.log.log_manager import LogWriter +from library.src.unispy_server_config import ServerConfig from typing import TYPE_CHECKING if TYPE_CHECKING: - from library.abstractions.connections import ConnectionBase - from library.abstractions.switcher import SwitcherBase - from library.abstractions.enctypt_base import EncryptBase - from library.abstractions.contracts import ResponseBase - from library.abstractions.client import ClientInfoBase + from library.src.abstractions.connections import ConnectionBase + from library.src.abstractions.switcher import SwitcherBase + from library.src.abstractions.enctypt_base import EncryptBase + from library.src.abstractions.contracts import ResponseBase + from library.src.abstractions.client import ClientInfoBase class ClientBase(abc.ABC): @@ -28,7 +28,7 @@ def __init__( assert isinstance(server_config, ServerConfig) # assert isinstance(logger, LogWriter) self.server_config = server_config - from library.abstractions.connections import ConnectionBase + from library.src.abstractions.connections import ConnectionBase self.connection: ConnectionBase = connection self.logger = logger diff --git a/src/library/abstractions/connections.py b/src/library/src/abstractions/connections.py similarity index 90% rename from src/library/abstractions/connections.py rename to src/library/src/abstractions/connections.py index e055f891e..46685efe9 100644 --- a/src/library/abstractions/connections.py +++ b/src/library/src/abstractions/connections.py @@ -1,9 +1,9 @@ import abc import socketserver -from library.abstractions.client import ClientBase -from library.extentions.string_extentions import IPEndPoint -from library.log.log_manager import LogWriter -from library.unispy_server_config import ServerConfig +from library.src.abstractions.client import ClientBase +from library.src.extentions.string_extentions import IPEndPoint +from library.src.log.log_manager import LogWriter +from library.src.unispy_server_config import ServerConfig class ConnectionBase(abc.ABC): diff --git a/src/library/abstractions/contracts.py b/src/library/src/abstractions/contracts.py similarity index 100% rename from src/library/abstractions/contracts.py rename to src/library/src/abstractions/contracts.py diff --git a/src/library/abstractions/enctypt_base.py b/src/library/src/abstractions/enctypt_base.py similarity index 100% rename from src/library/abstractions/enctypt_base.py rename to src/library/src/abstractions/enctypt_base.py diff --git a/src/library/abstractions/handler.py b/src/library/src/abstractions/handler.py similarity index 82% rename from src/library/abstractions/handler.py rename to src/library/src/abstractions/handler.py index 39a097775..9f1a50bd0 100644 --- a/src/library/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -1,27 +1,27 @@ import abc -from library.abstractions.client import ClientBase -from library.exceptions.error import UniSpyException +from library.src.abstractions.client import ClientBase +from library.src.exceptions.error import UniSpyException from typing import TYPE_CHECKING, List from typing import Type if TYPE_CHECKING: - from library.abstractions.contracts import RequestBase, ResultBase, ResponseBase + from library.src.abstractions.contracts import RequestBase, ResultBase, ResponseBase class CmdHandlerBase(abc.ABC): - _client: ClientBase - _request: RequestBase - _result: ResultBase = None - _response: ResponseBase = None + _client: "ClientBase" + _request: "RequestBase" + _result: "ResultBase" = None + _response: "ResponseBase" = None _backend_url: str _result_type: Type """ store the backend url """ - def __init__(self, client: ClientBase, request: RequestBase) -> None: + def __init__(self, client: "ClientBase", request: "RequestBase") -> None: assert issubclass(type(client), ClientBase) assert issubclass(type(request), RequestBase) self._client = client diff --git a/src/library/abstractions/redis_channel.py b/src/library/src/abstractions/redis_channel.py similarity index 100% rename from src/library/abstractions/redis_channel.py rename to src/library/src/abstractions/redis_channel.py diff --git a/src/library/abstractions/server_launcher_base.py b/src/library/src/abstractions/server_launcher_base.py similarity index 83% rename from src/library/abstractions/server_launcher_base.py rename to src/library/src/abstractions/server_launcher_base.py index 2f2464e3e..c3f9a93ec 100644 --- a/src/library/abstractions/server_launcher_base.py +++ b/src/library/src/abstractions/server_launcher_base.py @@ -1,8 +1,9 @@ import abc -from library.log.log_manager import LogManager, LogWriter -from library.unispy_server_config import CONFIG, ServerConfig +from library.src.log.log_manager import LogManager, LogWriter +from library.src.unispy_server_config import CONFIG, ServerConfig import pyfiglet import requests +VERSION = 0.45 __server_name_mapping = { "PresenceConnectionManager": "PCM", @@ -30,7 +31,10 @@ def start(self): self._launch_server() def __show_unispy_logo(self): + # display logo print(pyfiglet.Figlet().renderText("UniSpy.Server")) + # display version info + print(f"version {VERSION}") @abc.abstractmethod def _launch_server(self) -> None: @@ -46,7 +50,9 @@ def _connect_to_backend(self): f"backend server: {CONFIG.backend.url} not available." ) except: + # fmt: off raise Exception(f"backend server: {CONFIG.backend.url} not available.") + # fmt: on def _create_logger(self): short_name = __server_name_mapping[self.config.server_name] diff --git a/src/library/abstractions/switcher.py b/src/library/src/abstractions/switcher.py similarity index 88% rename from src/library/abstractions/switcher.py rename to src/library/src/abstractions/switcher.py index 3ddf45b97..a8b3977ca 100644 --- a/src/library/abstractions/switcher.py +++ b/src/library/src/abstractions/switcher.py @@ -1,6 +1,6 @@ import abc -from library.abstractions.client import ClientBase -from library.abstractions.handler import CmdHandlerBase +from library.src.abstractions.client import ClientBase +from library.src.abstractions.handler import CmdHandlerBase from typing import TYPE_CHECKING, List @@ -29,7 +29,7 @@ def __init__(self, client: ClientBase, raw_request: object) -> None: self._requests = [] def handle(self): - from library.exceptions.error import UniSpyException + from library.src.exceptions.error import UniSpyException try: self._process_raw_request() diff --git a/src/library/database/__init__.py b/src/library/src/database/__init__.py similarity index 100% rename from src/library/database/__init__.py rename to src/library/src/database/__init__.py diff --git a/src/library/database/mongodb_orm.py b/src/library/src/database/mongodb_orm.py similarity index 82% rename from src/library/database/mongodb_orm.py rename to src/library/src/database/mongodb_orm.py index ba9b19cd9..d797e8752 100644 --- a/src/library/database/mongodb_orm.py +++ b/src/library/src/database/mongodb_orm.py @@ -1,6 +1,6 @@ from mongoengine import connect -from library.unispy_server_config import CONFIG +from library.src.unispy_server_config import CONFIG def connect_to_db(): diff --git a/src/library/database/pg_orm.py b/src/library/src/database/pg_orm.py similarity index 99% rename from src/library/database/pg_orm.py rename to src/library/src/database/pg_orm.py index 3bd3ffcc9..6ed014807 100644 --- a/src/library/database/pg_orm.py +++ b/src/library/src/database/pg_orm.py @@ -184,7 +184,7 @@ class SakeStorage(Base): from sqlalchemy import create_engine -from library.unispy_server_config import CONFIG +from library.src.unispy_server_config import CONFIG def connect_to_db() -> Session: diff --git a/src/library/database/redis.py b/src/library/src/database/redis.py similarity index 84% rename from src/library/database/redis.py rename to src/library/src/database/redis.py index 345660fb3..77813587b 100644 --- a/src/library/database/redis.py +++ b/src/library/src/database/redis.py @@ -2,7 +2,7 @@ # import redis import aioredis -from library.unispy_server_config import CONFIG +from library.src.unispy_server_config import CONFIG # SESSION = redis.Redis.from_url(CONFIG.redis.url) diff --git a/src/library/encryption/__init__.py b/src/library/src/encryption/__init__.py similarity index 100% rename from src/library/encryption/__init__.py rename to src/library/src/encryption/__init__.py diff --git a/src/library/encryption/encoding.py b/src/library/src/encryption/encoding.py similarity index 100% rename from src/library/encryption/encoding.py rename to src/library/src/encryption/encoding.py diff --git a/src/library/encryption/gs_encryption.py b/src/library/src/encryption/gs_encryption.py similarity index 97% rename from src/library/encryption/gs_encryption.py rename to src/library/src/encryption/gs_encryption.py index f92f66675..62ca28205 100644 --- a/src/library/encryption/gs_encryption.py +++ b/src/library/src/encryption/gs_encryption.py @@ -1,4 +1,4 @@ -from library.abstractions.enctypt_base import EncryptBase +from library.src.abstractions.enctypt_base import EncryptBase import hashlib DIGITS_HEX = "0123456789abcdef" diff --git a/src/library/encryption/xor_encryption.py b/src/library/src/encryption/xor_encryption.py similarity index 95% rename from src/library/encryption/xor_encryption.py rename to src/library/src/encryption/xor_encryption.py index 386b95ac7..576aafe82 100644 --- a/src/library/encryption/xor_encryption.py +++ b/src/library/src/encryption/xor_encryption.py @@ -1,7 +1,7 @@ from enum import IntEnum import base64 -from library.abstractions.enctypt_base import EncryptBase +from library.src.abstractions.enctypt_base import EncryptBase class XorType(IntEnum): diff --git a/src/library/exceptions/__init__.py b/src/library/src/exceptions/__init__.py similarity index 100% rename from src/library/exceptions/__init__.py rename to src/library/src/exceptions/__init__.py diff --git a/src/library/exceptions/error.py b/src/library/src/exceptions/error.py similarity index 95% rename from src/library/exceptions/error.py rename to src/library/src/exceptions/error.py index fc1907891..269b29797 100644 --- a/src/library/exceptions/error.py +++ b/src/library/src/exceptions/error.py @@ -1,4 +1,4 @@ -# from library.abstractions.client_base import ClientBase +# from library.src.abstractions.client_base import ClientBase class UniSpyException(Exception): diff --git a/src/library/extentions/__init__.py b/src/library/src/extentions/__init__.py similarity index 100% rename from src/library/extentions/__init__.py rename to src/library/src/extentions/__init__.py diff --git a/src/library/extentions/bytes_extentions.py b/src/library/src/extentions/bytes_extentions.py similarity index 100% rename from src/library/extentions/bytes_extentions.py rename to src/library/src/extentions/bytes_extentions.py diff --git a/src/library/extentions/encoding.py b/src/library/src/extentions/encoding.py similarity index 100% rename from src/library/extentions/encoding.py rename to src/library/src/extentions/encoding.py diff --git a/src/library/extentions/gamespy_ramdoms.py b/src/library/src/extentions/gamespy_ramdoms.py similarity index 100% rename from src/library/extentions/gamespy_ramdoms.py rename to src/library/src/extentions/gamespy_ramdoms.py diff --git a/src/library/extentions/gamespy_utils.py b/src/library/src/extentions/gamespy_utils.py similarity index 100% rename from src/library/extentions/gamespy_utils.py rename to src/library/src/extentions/gamespy_utils.py diff --git a/src/library/extentions/password_encoder.py b/src/library/src/extentions/password_encoder.py similarity index 97% rename from src/library/extentions/password_encoder.py rename to src/library/src/extentions/password_encoder.py index f34f5634a..466e60cb7 100644 --- a/src/library/extentions/password_encoder.py +++ b/src/library/src/extentions/password_encoder.py @@ -1,7 +1,7 @@ import hashlib import base64 -from library.exceptions.error import UniSpyException +from library.src.exceptions.error import UniSpyException def process_password(request: dict): diff --git a/src/library/extentions/redis_orm.py b/src/library/src/extentions/redis_orm.py similarity index 100% rename from src/library/extentions/redis_orm.py rename to src/library/src/extentions/redis_orm.py diff --git a/src/library/extentions/string_extentions.py b/src/library/src/extentions/string_extentions.py similarity index 100% rename from src/library/extentions/string_extentions.py rename to src/library/src/extentions/string_extentions.py diff --git a/src/library/log/__init__.py b/src/library/src/log/__init__.py similarity index 100% rename from src/library/log/__init__.py rename to src/library/src/log/__init__.py diff --git a/src/library/log/log_manager.py b/src/library/src/log/log_manager.py similarity index 100% rename from src/library/log/log_manager.py rename to src/library/src/log/log_manager.py diff --git a/src/library/network/__init__.py b/src/library/src/network/__init__.py similarity index 100% rename from src/library/network/__init__.py rename to src/library/src/network/__init__.py diff --git a/src/library/network/http/http_handler.py b/src/library/src/network/http_handler.py similarity index 92% rename from src/library/network/http/http_handler.py rename to src/library/src/network/http_handler.py index 6d0171b93..358a6f678 100644 --- a/src/library/network/http/http_handler.py +++ b/src/library/src/network/http_handler.py @@ -1,8 +1,8 @@ from urllib.parse import urlparse -from library.abstractions.client import ClientBase -from library.abstractions.connections import ConnectionBase, ServerBase +from library.src.abstractions.client import ClientBase +from library.src.abstractions.connections import ConnectionBase, ServerBase from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer -from library.unispy_server_config import CONFIG +from library.src.unispy_server_config import CONFIG class HttpRequest: diff --git a/src/library/network/tcp/tcp_handler.py b/src/library/src/network/tcp_handler.py similarity index 89% rename from src/library/network/tcp/tcp_handler.py rename to src/library/src/network/tcp_handler.py index 484cabe06..6650da5ab 100644 --- a/src/library/network/tcp/tcp_handler.py +++ b/src/library/src/network/tcp_handler.py @@ -1,10 +1,10 @@ import socket import socketserver -from library.abstractions.client import ClientBase -from library.abstractions.connections import ConnectionBase, ServerBase +from library.src.abstractions.client import ClientBase +from library.src.abstractions.connections import ConnectionBase, ServerBase -from library.network import DATA_SIZE -from library.unispy_server_config import CONFIG +from library.src.network import DATA_SIZE +from library.src.unispy_server_config import CONFIG class TcpConnection(ConnectionBase): diff --git a/src/library/network/udp/udp_handler.py b/src/library/src/network/udp_handler.py similarity index 82% rename from src/library/network/udp/udp_handler.py rename to src/library/src/network/udp_handler.py index cf5830c88..4c27a3f4b 100644 --- a/src/library/network/udp/udp_handler.py +++ b/src/library/src/network/udp_handler.py @@ -1,11 +1,11 @@ import socket import socketserver -from library.abstractions.client import ClientBase -from library.abstractions.connections import ConnectionBase, ServerBase -from library.extentions.string_extentions import IPEndPoint -from library.log.log_manager import LogWriter -from library.unispy_server_config import CONFIG, ServerConfig +from library.src.abstractions.client import ClientBase +from library.src.abstractions.connections import ConnectionBase, ServerBase +from library.src.extentions.string_extentions import IPEndPoint +from library.src.log.log_manager import LogWriter +from library.src.unispy_server_config import CONFIG, ServerConfig class UdpConnection(ConnectionBase): diff --git a/src/library/unispy_server_config.py b/src/library/src/unispy_server_config.py similarity index 98% rename from src/library/unispy_server_config.py rename to src/library/src/unispy_server_config.py index 099f2a6a3..6f25026ee 100644 --- a/src/library/unispy_server_config.py +++ b/src/library/src/unispy_server_config.py @@ -3,7 +3,7 @@ from uuid import UUID import os -from library.exceptions.error import UniSpyException +from library.src.exceptions.error import UniSpyException class PostgreSql: diff --git a/src/servers/chat/applications/server_launcher.py b/src/servers/chat/applications/server_launcher.py deleted file mode 100644 index ecb667c25..000000000 --- a/src/servers/chat/applications/server_launcher.py +++ /dev/null @@ -1,18 +0,0 @@ -from library.abstractions.server_launcher_base import ServerLauncherBase -from library.network.tcp.tcp_handler import create_tcp_server -from library.unispy_server_config import CONFIG -from servers.chat.applications.client import Client - - -class ServerLauncher(ServerLauncherBase): - def __init__(self) -> None: - super().__init__() - self.config = CONFIG.servers["Chat"] - - def _launch_server(self): - create_tcp_server(self.config, Client) - - -if __name__ == "__main__": - s = ServerLauncher() - s.start() diff --git a/src/servers/chat/abstractions/channel.py b/src/servers/chat/src/abstractions/channel.py similarity index 88% rename from src/servers/chat/abstractions/channel.py rename to src/servers/chat/src/abstractions/channel.py index 9b36c3b24..a8a7ec0dc 100644 --- a/src/servers/chat/abstractions/channel.py +++ b/src/servers/chat/src/abstractions/channel.py @@ -1,7 +1,7 @@ -from servers.chat.abstractions.contract import * -from servers.chat.abstractions.handler import PostLoginHandlerBase -from servers.chat.exceptions.channel import NoSuchChannelException -from servers.chat.exceptions.general import ChatException, NoSuchNickException +from servers.chat.src.abstractions.contract import * +from servers.chat.src.abstractions.handler import PostLoginHandlerBase +from servers.chat.src.exceptions.channel import NoSuchChannelException +from servers.chat.src.exceptions.general import ChatException, NoSuchNickException class ChannelHandlerBase(PostLoginHandlerBase): diff --git a/src/servers/chat/abstractions/contract.py b/src/servers/chat/src/abstractions/contract.py similarity index 86% rename from src/servers/chat/abstractions/contract.py rename to src/servers/chat/src/abstractions/contract.py index d85d2e143..36ee329b5 100644 --- a/src/servers/chat/abstractions/contract.py +++ b/src/servers/chat/src/abstractions/contract.py @@ -1,8 +1,8 @@ import abc -import library.abstractions.contracts +import library.src.abstractions.contracts -class RequestBase(library.abstractions.contracts.RequestBase, abc.ABC): +class RequestBase(library.src.abstractions.contracts.RequestBase, abc.ABC): raw_request: str = None command_name: str = None _prefix: str = None @@ -46,14 +46,14 @@ def parse(self) -> None: self._cmd_params = dataFrag[1:] -class ResultBase(library.abstractions.contracts.ResultBase, abc.ABC): +class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): pass SERVER_DOMAIN = "unispy.net" -class ResponseBase(library.abstractions.contracts.ResponseBase, abc.ABC): +class ResponseBase(library.src.abstractions.contracts.ResponseBase, abc.ABC): sending_buffer: str _result: ResultBase _request: RequestBase diff --git a/src/servers/chat/abstractions/handler.py b/src/servers/chat/src/abstractions/handler.py similarity index 58% rename from src/servers/chat/abstractions/handler.py rename to src/servers/chat/src/abstractions/handler.py index f55d00341..ef7c5a7a6 100644 --- a/src/servers/chat/abstractions/handler.py +++ b/src/servers/chat/src/abstractions/handler.py @@ -1,10 +1,10 @@ -from library.abstractions.client import ClientBase -from servers.chat.abstractions.contract import RequestBase -from servers.chat.exceptions.general import IRCException -import library.abstractions.handler +from library.src.abstractions.client import ClientBase +from servers.chat.src.abstractions.contract import RequestBase +from servers.chat.src.exceptions.general import IRCException +import library.src.abstractions.handler -class CmdHandlerBase(library.abstractions.handler.CmdHandlerBase): +class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase): _request: RequestBase def __init__(self, client: ClientBase, request: RequestBase): diff --git a/src/servers/chat/abstractions/message.py b/src/servers/chat/src/abstractions/message.py similarity index 78% rename from src/servers/chat/abstractions/message.py rename to src/servers/chat/src/abstractions/message.py index 178b72dc1..bb1272121 100644 --- a/src/servers/chat/abstractions/message.py +++ b/src/servers/chat/src/abstractions/message.py @@ -1,7 +1,7 @@ -from library.abstractions.client import ClientBase -from servers.chat.abstractions.channel import ChannelHandlerBase, ChannelRequestBase -from servers.chat.abstractions.contract import ResultBase -from servers.chat.enums.general import MessageType +from library.src.abstractions.client import ClientBase +from servers.chat.src.abstractions.channel import ChannelHandlerBase, ChannelRequestBase +from servers.chat.src.abstractions.contract import ResultBase +from servers.chat.src.enums.general import MessageType class MessageRequestBase(ChannelRequestBase): diff --git a/src/servers/chat/aggregates/brockers.py b/src/servers/chat/src/aggregates/brockers.py similarity index 89% rename from src/servers/chat/aggregates/brockers.py rename to src/servers/chat/src/aggregates/brockers.py index 48dd2f1f4..9ce0ed744 100644 --- a/src/servers/chat/aggregates/brockers.py +++ b/src/servers/chat/src/aggregates/brockers.py @@ -1,7 +1,7 @@ import threading import redis -from library.abstractions.brocker import BrockerBase -from library.unispy_server_config import CONFIG +from library.src.abstractions.brocker import BrockerBase +from library.src.unispy_server_config import CONFIG class SocketIOBrocker(BrockerBase): diff --git a/src/servers/chat/aggregates/channel.py b/src/servers/chat/src/aggregates/channel.py similarity index 90% rename from src/servers/chat/aggregates/channel.py rename to src/servers/chat/src/aggregates/channel.py index 39b179805..943baca2e 100644 --- a/src/servers/chat/aggregates/channel.py +++ b/src/servers/chat/src/aggregates/channel.py @@ -1,14 +1,14 @@ import datetime from uuid import UUID -from servers.chat.abstractions.contract import ResponseBase -from servers.chat.aggregates.channel_user import ChannelUser -from servers.chat.aggregates.key_value_manager import KeyValueManager -from servers.chat.aggregates.peer_room import PeerRoom -from servers.chat.applications.client import Client -from servers.chat.contracts.requests.channel import ModeRequest -from servers.chat.enums.peer_room import PeerRoomType -from servers.chat.exceptions.general import ChatException -from servers.server_browser.v2.aggregations.server_info_builder import PEER_GROUP_LIST +from servers.chat.src.abstractions.contract import ResponseBase +from servers.chat.src.aggregates.channel_user import ChannelUser +from servers.chat.src.aggregates.key_value_manager import KeyValueManager +from servers.chat.src.aggregates.peer_room import PeerRoom +from servers.chat.src.applications.client import Client +from servers.chat.src.contracts.requests.channel import ModeRequest +from servers.chat.src.enums.peer_room import PeerRoomType +from servers.chat.src.exceptions.general import ChatException +from servers.server_browser.src.v2.aggregations.server_info_builder import PEER_GROUP_LIST class Channel: diff --git a/src/servers/chat/aggregates/channel_user.py b/src/servers/chat/src/aggregates/channel_user.py similarity index 81% rename from src/servers/chat/aggregates/channel_user.py rename to src/servers/chat/src/aggregates/channel_user.py index 153e8ff52..bf6217555 100644 --- a/src/servers/chat/aggregates/channel_user.py +++ b/src/servers/chat/src/aggregates/channel_user.py @@ -1,11 +1,11 @@ from uuid import UUID -from servers.chat.aggregates.key_value_manager import KeyValueManager -from servers.chat.applications.client import Client +from servers.chat.src.aggregates.key_value_manager import KeyValueManager +from servers.chat.src.applications.client import Client from typing import TYPE_CHECKING if TYPE_CHECKING: - from servers.chat.aggregates.channel import Channel + from servers.chat.src.aggregates.channel import Channel class ChannelUser: diff --git a/src/servers/chat/aggregates/key_value_manager.py b/src/servers/chat/src/aggregates/key_value_manager.py similarity index 100% rename from src/servers/chat/aggregates/key_value_manager.py rename to src/servers/chat/src/aggregates/key_value_manager.py diff --git a/src/servers/chat/aggregates/peer_room.py b/src/servers/chat/src/aggregates/peer_room.py similarity index 97% rename from src/servers/chat/aggregates/peer_room.py rename to src/servers/chat/src/aggregates/peer_room.py index 2e6649ca6..1e197f52c 100644 --- a/src/servers/chat/aggregates/peer_room.py +++ b/src/servers/chat/src/aggregates/peer_room.py @@ -1,4 +1,4 @@ -from servers.chat.enums.peer_room import PeerRoomType +from servers.chat.src.enums.peer_room import PeerRoomType class PeerRoom: diff --git a/src/servers/chat/aggregates/response_name.py b/src/servers/chat/src/aggregates/response_name.py similarity index 100% rename from src/servers/chat/aggregates/response_name.py rename to src/servers/chat/src/aggregates/response_name.py diff --git a/src/servers/chat/aggregates/storage_info.py b/src/servers/chat/src/aggregates/storage_info.py similarity index 100% rename from src/servers/chat/aggregates/storage_info.py rename to src/servers/chat/src/aggregates/storage_info.py diff --git a/src/servers/chat/applications/client.py b/src/servers/chat/src/applications/client.py similarity index 69% rename from src/servers/chat/applications/client.py rename to src/servers/chat/src/applications/client.py index ce5ecb838..6d1724386 100644 --- a/src/servers/chat/applications/client.py +++ b/src/servers/chat/src/applications/client.py @@ -1,8 +1,8 @@ -from library.abstractions.client import ClientBase +from library.src.abstractions.client import ClientBase from typing import TYPE_CHECKING if TYPE_CHECKING: - from servers.chat.aggregates.channel import Channel + from servers.chat.src.aggregates.channel import Channel class ClientInfo: diff --git a/src/servers/chat/src/applications/server_launcher.py b/src/servers/chat/src/applications/server_launcher.py new file mode 100644 index 000000000..67a1d4c77 --- /dev/null +++ b/src/servers/chat/src/applications/server_launcher.py @@ -0,0 +1,20 @@ +from library.src.abstractions.server_launcher_base import ServerLauncherBase +from library.src.network.tcp_handler import TcpServer +from library.src.unispy_server_config import CONFIG +from servers.chat.src.applications.client import Client + + +class ServerLauncher(ServerLauncherBase): + server: "TcpServer" + + def __init__(self) -> None: + super().__init__() + self.config = CONFIG.servers["Chat"] + + def _launch_server(self): + TcpServer(self.config, Client).start() + + +if __name__ == "__main__": + s = ServerLauncher() + s.start() diff --git a/src/servers/chat/applications/switcher.py b/src/servers/chat/src/applications/switcher.py similarity index 90% rename from src/servers/chat/applications/switcher.py rename to src/servers/chat/src/applications/switcher.py index bd795fc41..adee52ec0 100644 --- a/src/servers/chat/applications/switcher.py +++ b/src/servers/chat/src/applications/switcher.py @@ -1,7 +1,7 @@ -from library.abstractions.client import ClientBase -from library.abstractions.handler import CmdHandlerBase -from library.abstractions.switcher import SwitcherBase -from servers.chat.contracts.requests.channel import ( +from library.src.abstractions.client import ClientBase +from library.src.abstractions.handler import CmdHandlerBase +from library.src.abstractions.switcher import SwitcherBase +from servers.chat.src.contracts.requests.channel import ( GetCKeyRequest, GetChannelKeyRequest, JoinRequest, @@ -13,7 +13,7 @@ SetChannelKeyRequest, TopicRequest, ) -from servers.chat.contracts.requests.general import ( +from servers.chat.src.contracts.requests.general import ( CdkeyRequest, CryptRequest, GetKeyRequest, @@ -28,13 +28,13 @@ WhoIsRequest, WhoRequest, ) -from servers.chat.contracts.requests.message import ( +from servers.chat.src.contracts.requests.message import ( ATMRequest, NoticeRequest, PrivateRequest, UTMRequest, ) -from servers.chat.handlers.channel import ( +from servers.chat.src.handlers.channel import ( GetCKeyHandler, GetChannelKeyHandler, JoinHandler, @@ -46,7 +46,7 @@ SetChannelKeyHandler, TopicHandler, ) -from servers.chat.handlers.general import ( +from servers.chat.src.handlers.general import ( CdKeyHandler, CryptHandler, GetKeyHandler, @@ -61,7 +61,7 @@ WhoHandler, WhoIsHandler, ) -from servers.chat.handlers.message import ( +from servers.chat.src.handlers.message import ( ATMHandler, NoticeHandler, PrivateHandler, diff --git a/src/library/network/http/__init__.py b/src/servers/chat/src/contracts/__init__.py similarity index 100% rename from src/library/network/http/__init__.py rename to src/servers/chat/src/contracts/__init__.py diff --git a/src/library/network/tcp/__init__.py b/src/servers/chat/src/contracts/requests/__init__.py similarity index 100% rename from src/library/network/tcp/__init__.py rename to src/servers/chat/src/contracts/requests/__init__.py diff --git a/src/servers/chat/contracts/requests/channel.py b/src/servers/chat/src/contracts/requests/channel.py similarity index 97% rename from src/servers/chat/contracts/requests/channel.py rename to src/servers/chat/src/contracts/requests/channel.py index 99c1128f9..e502b22da 100644 --- a/src/servers/chat/contracts/requests/channel.py +++ b/src/servers/chat/src/contracts/requests/channel.py @@ -1,14 +1,14 @@ import re from typing import List -from servers.chat.abstractions.channel import ChannelRequestBase -from servers.chat.enums.general import ( +from servers.chat.src.abstractions.channel import ChannelRequestBase +from servers.chat.src.enums.general import ( GetKeyRequestType, ModeOperationType, ModeRequestType, TopicRequestType, ) -from servers.chat.exceptions.general import ChatException -from library.extentions.string_extentions import convert_kvstring_to_dictionary +from servers.chat.src.exceptions.general import ChatException +from library.src.extentions.string_extentions import convert_kvstring_to_dictionary class GetChannelKeyRequest(ChannelRequestBase): diff --git a/src/servers/chat/contracts/requests/general.py b/src/servers/chat/src/contracts/requests/general.py similarity index 95% rename from src/servers/chat/contracts/requests/general.py rename to src/servers/chat/src/contracts/requests/general.py index b424adfd1..a2a36c160 100644 --- a/src/servers/chat/contracts/requests/general.py +++ b/src/servers/chat/src/contracts/requests/general.py @@ -1,12 +1,12 @@ from typing import Dict -from library.extentions.string_extentions import ( +from library.src.extentions.string_extentions import ( convert_keystr_to_list, convert_kvstring_to_dictionary, ) -from servers.chat.abstractions.contract import RequestBase -from servers.chat.enums.general import LoginRequestType, WhoRequestType -from servers.chat.exceptions.general import ChatException -from servers.chat.exceptions.general import NickNameInUseException +from servers.chat.src.abstractions.contract import RequestBase +from servers.chat.src.enums.general import LoginRequestType, WhoRequestType +from servers.chat.src.exceptions.general import ChatException +from servers.chat.src.exceptions.general import NickNameInUseException class CdkeyRequest(RequestBase): diff --git a/src/servers/chat/contracts/requests/message.py b/src/servers/chat/src/contracts/requests/message.py similarity index 74% rename from src/servers/chat/contracts/requests/message.py rename to src/servers/chat/src/contracts/requests/message.py index 65c408194..7870af701 100644 --- a/src/servers/chat/contracts/requests/message.py +++ b/src/servers/chat/src/contracts/requests/message.py @@ -1,4 +1,4 @@ -from servers.chat.abstractions.message import MessageRequestBase +from servers.chat.src.abstractions.message import MessageRequestBase class ATMRequest(MessageRequestBase): diff --git a/src/library/network/udp/__init__.py b/src/servers/chat/src/contracts/responses/__init__.py similarity index 100% rename from src/library/network/udp/__init__.py rename to src/servers/chat/src/contracts/responses/__init__.py diff --git a/src/servers/chat/contracts/responses/channel.py b/src/servers/chat/src/contracts/responses/channel.py similarity index 93% rename from src/servers/chat/contracts/responses/channel.py rename to src/servers/chat/src/contracts/responses/channel.py index 1a64da2db..2d37bcd1f 100644 --- a/src/servers/chat/contracts/responses/channel.py +++ b/src/servers/chat/src/contracts/responses/channel.py @@ -1,13 +1,13 @@ -from library.abstractions.contracts import RequestBase, ResultBase -from servers.chat.abstractions.channel import ChannelResponseBase -from servers.chat.abstractions.contract import ( +from library.src.abstractions.contracts import RequestBase, ResultBase +from servers.chat.src.abstractions.channel import ChannelResponseBase +from servers.chat.src.abstractions.contract import ( SERVER_DOMAIN, RequestBase, ResponseBase, ResultBase, ) -from servers.chat.aggregates.response_name import * -from servers.chat.contracts.requests.channel import ( +from servers.chat.src.aggregates.response_name import * +from servers.chat.src.contracts.requests.channel import ( GetCKeyRequest, GetChannelKeyRequest, JoinRequest, @@ -19,7 +19,7 @@ SetChannelKeyRequest, TopicRequest, ) -from servers.chat.contracts.results.channel import ( +from servers.chat.src.contracts.results.channel import ( GetCKeyResult, GetChannelKeyResult, JoinResult, @@ -29,7 +29,7 @@ PartResult, TopicResult, ) -from servers.chat.enums.general import ModeRequestType +from servers.chat.src.enums.general import ModeRequestType class GetChannelKeyResponse(ChannelResponseBase): diff --git a/src/servers/chat/contracts/responses/general.py b/src/servers/chat/src/contracts/responses/general.py similarity index 91% rename from src/servers/chat/contracts/responses/general.py rename to src/servers/chat/src/contracts/responses/general.py index f1c8101f8..6dc095d01 100644 --- a/src/servers/chat/contracts/responses/general.py +++ b/src/servers/chat/src/contracts/responses/general.py @@ -1,9 +1,9 @@ -from library.abstractions.contracts import RequestBase, ResultBase -from library.encryption.gs_encryption import CLIENT_KEY, SERVER_KEY -from servers.chat.abstractions.contract import SERVER_DOMAIN, ResponseBase -from servers.chat.aggregates.response_name import * -from servers.chat.contracts.requests.general import GetKeyRequest, WhoRequest -from servers.chat.contracts.results.general import ( +from library.src.abstractions.contracts import RequestBase, ResultBase +from library.src.encryption.gs_encryption import CLIENT_KEY, SERVER_KEY +from servers.chat.src.abstractions.contract import SERVER_DOMAIN, ResponseBase +from servers.chat.src.aggregates.response_name import * +from servers.chat.src.contracts.requests.general import GetKeyRequest, WhoRequest +from servers.chat.src.contracts.results.general import ( GetKeyResult, ListResult, LoginResult, @@ -13,7 +13,7 @@ WhoIsResult, WhoResult, ) -from servers.chat.enums.general import WhoRequestType +from servers.chat.src.enums.general import WhoRequestType class CdKeyResponse(ResponseBase): diff --git a/src/servers/chat/contracts/responses/message.py b/src/servers/chat/src/contracts/responses/message.py similarity index 86% rename from src/servers/chat/contracts/responses/message.py rename to src/servers/chat/src/contracts/responses/message.py index 0d7d7a168..8fa6c0663 100644 --- a/src/servers/chat/contracts/responses/message.py +++ b/src/servers/chat/src/contracts/responses/message.py @@ -1,13 +1,13 @@ -from library.abstractions.contracts import RequestBase, ResultBase -from servers.chat.abstractions.contract import ResponseBase -from servers.chat.aggregates.response_name import * -from servers.chat.contracts.requests.message import ( +from library.src.abstractions.contracts import RequestBase, ResultBase +from servers.chat.src.abstractions.contract import ResponseBase +from servers.chat.src.aggregates.response_name import * +from servers.chat.src.contracts.requests.message import ( ATMRequest, NoticeRequest, PrivateRequest, UTMRequest, ) -from servers.chat.contracts.results.message import ( +from servers.chat.src.contracts.results.message import ( ATMResult, NoticeResult, PrivateResult, diff --git a/src/servers/chat/contracts/__init__.py b/src/servers/chat/src/contracts/results/__init__.py similarity index 100% rename from src/servers/chat/contracts/__init__.py rename to src/servers/chat/src/contracts/results/__init__.py diff --git a/src/servers/chat/contracts/results/channel.py b/src/servers/chat/src/contracts/results/channel.py similarity index 94% rename from src/servers/chat/contracts/results/channel.py rename to src/servers/chat/src/contracts/results/channel.py index ffbdc0f2a..f7f8e8e0b 100644 --- a/src/servers/chat/contracts/results/channel.py +++ b/src/servers/chat/src/contracts/results/channel.py @@ -1,4 +1,4 @@ -from servers.chat.abstractions.contract import ResultBase +from servers.chat.src.abstractions.contract import ResultBase class GetChannelKeyResult(ResultBase): diff --git a/src/servers/chat/contracts/results/general.py b/src/servers/chat/src/contracts/results/general.py similarity index 95% rename from src/servers/chat/contracts/results/general.py rename to src/servers/chat/src/contracts/results/general.py index 91c0199cd..77a23afa0 100644 --- a/src/servers/chat/contracts/results/general.py +++ b/src/servers/chat/src/contracts/results/general.py @@ -1,5 +1,5 @@ from typing import List, Tuple -from servers.chat.abstractions.contract import ResultBase +from servers.chat.src.abstractions.contract import ResultBase class CryptResult(ResultBase): diff --git a/src/servers/chat/contracts/results/message.py b/src/servers/chat/src/contracts/results/message.py similarity index 76% rename from src/servers/chat/contracts/results/message.py rename to src/servers/chat/src/contracts/results/message.py index eecefd04f..35c4c81c4 100644 --- a/src/servers/chat/contracts/results/message.py +++ b/src/servers/chat/src/contracts/results/message.py @@ -1,4 +1,4 @@ -from servers.chat.abstractions.message import MessageResultBase +from servers.chat.src.abstractions.message import MessageResultBase class ATMResult(MessageResultBase): diff --git a/src/servers/chat/enums/general.py b/src/servers/chat/src/enums/general.py similarity index 100% rename from src/servers/chat/enums/general.py rename to src/servers/chat/src/enums/general.py diff --git a/src/servers/chat/enums/irc_error_code.py b/src/servers/chat/src/enums/irc_error_code.py similarity index 100% rename from src/servers/chat/enums/irc_error_code.py rename to src/servers/chat/src/enums/irc_error_code.py diff --git a/src/servers/chat/enums/peer_room.py b/src/servers/chat/src/enums/peer_room.py similarity index 100% rename from src/servers/chat/enums/peer_room.py rename to src/servers/chat/src/enums/peer_room.py diff --git a/src/servers/chat/exceptions/channel.py b/src/servers/chat/src/exceptions/channel.py similarity index 90% rename from src/servers/chat/exceptions/channel.py rename to src/servers/chat/src/exceptions/channel.py index 2181fcfd8..9fbf4270a 100644 --- a/src/servers/chat/exceptions/channel.py +++ b/src/servers/chat/src/exceptions/channel.py @@ -1,5 +1,5 @@ -from servers.chat.enums.irc_error_code import IRCErrorCode -from servers.chat.exceptions.general import IRCChannelException +from servers.chat.src.enums.irc_error_code import IRCErrorCode +from servers.chat.src.exceptions.general import IRCChannelException class BadChannelMaskException(IRCChannelException): diff --git a/src/servers/chat/exceptions/general.py b/src/servers/chat/src/exceptions/general.py similarity index 93% rename from src/servers/chat/exceptions/general.py rename to src/servers/chat/src/exceptions/general.py index 02283fe36..8842a7601 100644 --- a/src/servers/chat/exceptions/general.py +++ b/src/servers/chat/src/exceptions/general.py @@ -1,8 +1,8 @@ -from servers.chat.abstractions.contract import SERVER_DOMAIN -from servers.chat.enums.irc_error_code import IRCErrorCode +from servers.chat.src.abstractions.contract import SERVER_DOMAIN +from servers.chat.src.enums.irc_error_code import IRCErrorCode import abc -from library.exceptions.error import UniSpyException as ER +from library.src.exceptions.error import UniSpyException as ER class ChatException(ER): diff --git a/src/servers/chat/handlers/channel.py b/src/servers/chat/src/handlers/channel.py similarity index 91% rename from src/servers/chat/handlers/channel.py rename to src/servers/chat/src/handlers/channel.py index 447bc372f..d5fed05de 100644 --- a/src/servers/chat/handlers/channel.py +++ b/src/servers/chat/src/handlers/channel.py @@ -1,7 +1,7 @@ -from library.abstractions.client import ClientBase -from servers.chat.abstractions.channel import ChannelHandlerBase -from servers.chat.aggregates.response_name import * -from servers.chat.contracts.requests.channel import ( +from library.src.abstractions.client import ClientBase +from servers.chat.src.abstractions.channel import ChannelHandlerBase +from servers.chat.src.aggregates.response_name import * +from servers.chat.src.contracts.requests.channel import ( GetCKeyRequest, GetChannelKeyRequest, JoinRequest, @@ -13,7 +13,7 @@ SetChannelKeyRequest, TopicRequest, ) -from servers.chat.contracts.responses.channel import ( +from servers.chat.src.contracts.responses.channel import ( GetCKeyResponse, JoinResponse, KickResponse, @@ -24,7 +24,7 @@ SetChannelKeyResponse, TopicResponse, ) -from servers.chat.contracts.results.channel import ( +from servers.chat.src.contracts.results.channel import ( GetCKeyResult, GetChannelKeyResult, JoinResult, @@ -35,8 +35,8 @@ SetChannelKeyResult, TopicResult, ) -from servers.chat.enums.general import ModeRequestType, TopicRequestType -from servers.chat.exceptions.general import ChatException +from servers.chat.src.enums.general import ModeRequestType, TopicRequestType +from servers.chat.src.exceptions.general import ChatException class GetChannelKeyHandler(ChannelHandlerBase): diff --git a/src/servers/chat/handlers/general.py b/src/servers/chat/src/handlers/general.py similarity index 92% rename from src/servers/chat/handlers/general.py rename to src/servers/chat/src/handlers/general.py index b7ce6ebee..be24ed273 100644 --- a/src/servers/chat/handlers/general.py +++ b/src/servers/chat/src/handlers/general.py @@ -1,9 +1,9 @@ from typing import Type -from library.abstractions.client import ClientBase -from servers.chat.abstractions.contract import RequestBase -from servers.chat.abstractions.handler import CmdHandlerBase, PostLoginHandlerBase -from servers.chat.contracts.requests.channel import GetUdpRelayRequest -from servers.chat.contracts.requests.general import ( +from library.src.abstractions.client import ClientBase +from servers.chat.src.abstractions.contract import RequestBase +from servers.chat.src.abstractions.handler import CmdHandlerBase, PostLoginHandlerBase +from servers.chat.src.contracts.requests.channel import GetUdpRelayRequest +from servers.chat.src.contracts.requests.general import ( CdkeyRequest, CryptRequest, GetKeyRequest, @@ -18,7 +18,7 @@ WhoIsRequest, WhoRequest, ) -from servers.chat.contracts.responses.general import ( +from servers.chat.src.contracts.responses.general import ( CdKeyResponse, CryptResponse, GetKeyResponse, @@ -30,7 +30,7 @@ WhoIsResponse, WhoResponse, ) -from servers.chat.contracts.results.general import ( +from servers.chat.src.contracts.results.general import ( CryptResult, GetKeyResult, ListResult, diff --git a/src/servers/chat/handlers/message.py b/src/servers/chat/src/handlers/message.py similarity index 84% rename from src/servers/chat/handlers/message.py rename to src/servers/chat/src/handlers/message.py index 787d8772a..dffde5c72 100644 --- a/src/servers/chat/handlers/message.py +++ b/src/servers/chat/src/handlers/message.py @@ -1,18 +1,18 @@ -from library.abstractions.client import ClientBase -from servers.chat.abstractions.message import MessageHandlerBase, MessageRequestBase -from servers.chat.contracts.requests.message import ( +from library.src.abstractions.client import ClientBase +from servers.chat.src.abstractions.message import MessageHandlerBase, MessageRequestBase +from servers.chat.src.contracts.requests.message import ( ATMRequest, NoticeRequest, PrivateRequest, UTMRequest, ) -from servers.chat.contracts.responses.message import ( +from servers.chat.src.contracts.responses.message import ( ATMResponse, NoticeResponse, PrivateResponse, UTMResponse, ) -from servers.chat.contracts.results.message import ( +from servers.chat.src.contracts.results.message import ( ATMResult, NoticeResult, PrivateResult, diff --git a/src/servers/game_traffic_relay/applications/connection_listener.py b/src/servers/game_traffic_relay/src/applications/connection_listener.py similarity index 86% rename from src/servers/game_traffic_relay/applications/connection_listener.py rename to src/servers/game_traffic_relay/src/applications/connection_listener.py index 04af88bf6..04eeae052 100644 --- a/src/servers/game_traffic_relay/applications/connection_listener.py +++ b/src/servers/game_traffic_relay/src/applications/connection_listener.py @@ -1,5 +1,5 @@ import socketserver -from library.extentions.string_extentions import IPEndPoint +from library.src.extentions.string_extentions import IPEndPoint class ConnectionListener: diff --git a/src/servers/game_traffic_relay/applications/router.py b/src/servers/game_traffic_relay/src/applications/router.py similarity index 71% rename from src/servers/game_traffic_relay/applications/router.py rename to src/servers/game_traffic_relay/src/applications/router.py index 25481dee5..f85742006 100644 --- a/src/servers/game_traffic_relay/applications/router.py +++ b/src/servers/game_traffic_relay/src/applications/router.py @@ -1,6 +1,6 @@ from fastapi import FastAPI from backends.urls import * -from servers.game_traffic_relay.contracts.general import InitPacketInfo +from servers.game_traffic_relay.src.contracts.general import InitPacketInfo app = FastAPI() diff --git a/src/servers/game_traffic_relay/contracts/general.py b/src/servers/game_traffic_relay/src/contracts/general.py similarity index 81% rename from src/servers/game_traffic_relay/contracts/general.py rename to src/servers/game_traffic_relay/src/contracts/general.py index ce02eb50c..b656242fe 100644 --- a/src/servers/game_traffic_relay/contracts/general.py +++ b/src/servers/game_traffic_relay/src/contracts/general.py @@ -1,6 +1,6 @@ from pydantic import BaseModel, UUID4 -from servers.natneg.enums.general import NatClientIndex, NatPortType +from servers.natneg.src.enums.general import NatClientIndex, NatPortType class InitPacketInfo(BaseModel): diff --git a/src/servers/natneg/abstractions/contracts.py b/src/servers/natneg/src/abstractions/contracts.py similarity index 86% rename from src/servers/natneg/abstractions/contracts.py rename to src/servers/natneg/src/abstractions/contracts.py index 2930b6689..989c487c6 100644 --- a/src/servers/natneg/abstractions/contracts.py +++ b/src/servers/natneg/src/abstractions/contracts.py @@ -1,7 +1,7 @@ import abc -import library.abstractions.contracts -from library.extentions.string_extentions import IPEndPoint -from servers.natneg.enums.general import ( +import library.src.abstractions.contracts +from library.src.extentions.string_extentions import IPEndPoint +from servers.natneg.src.enums.general import ( NatClientIndex, NatPortType, RequestType, @@ -11,7 +11,7 @@ MAGIC_DATA = bytes([0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2]) -class RequestBase(library.abstractions.contracts.RequestBase): +class RequestBase(library.src.abstractions.contracts.RequestBase): version: int cookie: bytes port_type: NatPortType @@ -32,12 +32,12 @@ def parse(self) -> None: self.port_type = NatPortType(self.raw_request[12]) -class ResultBase(library.abstractions.contracts.ResultBase): +class ResultBase(library.src.abstractions.contracts.ResultBase): packet_type: ResponseType pass -class ResponseBase(library.abstractions.contracts.ResponseBase): +class ResponseBase(library.src.abstractions.contracts.ResponseBase): _request: RequestBase _result: ResultBase sending_buffer: bytes diff --git a/src/servers/natneg/abstractions/handlers.py b/src/servers/natneg/src/abstractions/handlers.py similarity index 54% rename from src/servers/natneg/abstractions/handlers.py rename to src/servers/natneg/src/abstractions/handlers.py index d07b62feb..538c9b040 100644 --- a/src/servers/natneg/abstractions/handlers.py +++ b/src/servers/natneg/src/abstractions/handlers.py @@ -1,10 +1,10 @@ import abc -from servers.natneg.applications.client import Client -import library.abstractions.handler -from servers.natneg.abstractions.contracts import RequestBase +from servers.natneg.src.applications.client import Client +import library.src.abstractions.handler +from servers.natneg.src.abstractions.contracts import RequestBase -class CmdHandlerBase(library.abstractions.handler.CmdHandlerBase, abc.ABC): +class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase, abc.ABC): def __init__(self, client: Client, request: RequestBase) -> None: super().__init__(client, request) assert isinstance(client, Client) diff --git a/src/servers/natneg/aggregations/init_packet_info.py b/src/servers/natneg/src/aggregations/init_packet_info.py similarity index 76% rename from src/servers/natneg/aggregations/init_packet_info.py rename to src/servers/natneg/src/aggregations/init_packet_info.py index 9cae60973..2246a9952 100644 --- a/src/servers/natneg/aggregations/init_packet_info.py +++ b/src/servers/natneg/src/aggregations/init_packet_info.py @@ -7,7 +7,7 @@ BooleanField, ) -from servers.natneg.enums.general import NatClientIndex, NatPortType +from servers.natneg.src.enums.general import NatClientIndex, NatPortType import datetime @@ -23,11 +23,13 @@ class InitPacketInfo(Document): public_port = IntField(required=True) private_ip = StringField(required=True) private_port = IntField(required=True) - meta = {"expireAfterSeconds": int(datetime.timedelta(minutes=3).total_seconds())} + meta = {"expireAfterSeconds": int( + datetime.timedelta(minutes=3).total_seconds())} class NatFailInfo(Document): public_ip_address1 = StringField(required=True) public_ip_address2 = StringField(required=True) - meta = {"expireAfterSeconds": int(datetime.timedelta(days=1).total_seconds())} + meta = {"expireAfterSeconds": int( + datetime.timedelta(days=1).total_seconds())} """expire after 1 day""" diff --git a/src/servers/natneg/aggregations/relay_server_info.py b/src/servers/natneg/src/aggregations/relay_server_info.py similarity index 100% rename from src/servers/natneg/aggregations/relay_server_info.py rename to src/servers/natneg/src/aggregations/relay_server_info.py diff --git a/src/servers/natneg/applications/client.py b/src/servers/natneg/src/applications/client.py similarity index 68% rename from src/servers/natneg/applications/client.py rename to src/servers/natneg/src/applications/client.py index 325f7b980..2c73cf923 100644 --- a/src/servers/natneg/applications/client.py +++ b/src/servers/natneg/src/applications/client.py @@ -1,6 +1,6 @@ -from library.abstractions.client import ClientBase -from library.log.log_manager import LogWriter -from library.unispy_server_config import ServerConfig +from library.src.abstractions.client import ClientBase +from library.src.log.log_manager import LogWriter +from library.src.unispy_server_config import ServerConfig class Client(ClientBase): diff --git a/src/servers/natneg/contracts/requests.py b/src/servers/natneg/src/contracts/requests.py similarity index 92% rename from src/servers/natneg/contracts/requests.py rename to src/servers/natneg/src/contracts/requests.py index 2a23a1862..6d2ad6309 100644 --- a/src/servers/natneg/contracts/requests.py +++ b/src/servers/natneg/src/contracts/requests.py @@ -1,8 +1,8 @@ from socket import inet_ntoa import struct -from library.extentions.string_extentions import IPEndPoint -from servers.natneg.abstractions.contracts import CommonRequestBase, RequestBase -from servers.natneg.enums.general import ( +from library.src.extentions.string_extentions import IPEndPoint +from servers.natneg.src.abstractions.contracts import CommonRequestBase, RequestBase +from servers.natneg.src.enums.general import ( NatClientIndex, NatPortMappingScheme, NatPortType, diff --git a/src/servers/natneg/contracts/responses.py b/src/servers/natneg/src/contracts/responses.py similarity index 84% rename from src/servers/natneg/contracts/responses.py rename to src/servers/natneg/src/contracts/responses.py index 46d5d97b5..7b4855fcc 100644 --- a/src/servers/natneg/contracts/responses.py +++ b/src/servers/natneg/src/contracts/responses.py @@ -1,4 +1,4 @@ -from servers.natneg.abstractions.contracts import ( +from servers.natneg.src.abstractions.contracts import ( CommonResponseBase, RequestBase, ResultBase, diff --git a/src/servers/natneg/contracts/results.py b/src/servers/natneg/src/contracts/results.py similarity index 86% rename from src/servers/natneg/contracts/results.py rename to src/servers/natneg/src/contracts/results.py index 21a8afb21..34b2b5021 100644 --- a/src/servers/natneg/contracts/results.py +++ b/src/servers/natneg/src/contracts/results.py @@ -1,5 +1,5 @@ -from servers.natneg.abstractions.contracts import CommonResultBase, ResultBase -from servers.natneg.enums.general import ConnectPacketStatus, PreInitState, ResponseType +from servers.natneg.src.abstractions.contracts import CommonResultBase, ResultBase +from servers.natneg.src.enums.general import ConnectPacketStatus, PreInitState, ResponseType class AddressCheckResult(CommonResultBase): diff --git a/src/servers/natneg/enums/general.py b/src/servers/natneg/src/enums/general.py similarity index 100% rename from src/servers/natneg/enums/general.py rename to src/servers/natneg/src/enums/general.py diff --git a/src/servers/natneg/handlers/handlers.py b/src/servers/natneg/src/handlers/handlers.py similarity index 84% rename from src/servers/natneg/handlers/handlers.py rename to src/servers/natneg/src/handlers/handlers.py index a7f32eaac..541c4ecd0 100644 --- a/src/servers/natneg/handlers/handlers.py +++ b/src/servers/natneg/src/handlers/handlers.py @@ -1,10 +1,10 @@ -from library.abstractions.contracts import RequestBase -from library.extentions.string_extentions import IPEndPoint -from servers.natneg.abstractions.handlers import CmdHandlerBase -from servers.natneg.applications.client import Client -from servers.natneg.contracts.requests import AddressCheckRequest, ConnectAckRequest, ConnectRequest, ErtAckRequest, InitRequest, NatifyRequest, ReportRequest -from servers.natneg.contracts.responses import InitResponse -from servers.natneg.contracts.results import AddressCheckResult, ConnectResult, ErtAckResult, InitResult, NatifyResult, ReportResult +from library.src.abstractions.contracts import RequestBase +from library.src.extentions.string_extentions import IPEndPoint +from servers.natneg.src.abstractions.handlers import CmdHandlerBase +from servers.natneg.src.applications.client import Client +from servers.natneg.src.contracts.requests import AddressCheckRequest, ConnectAckRequest, ConnectRequest, ErtAckRequest, InitRequest, NatifyRequest, ReportRequest +from servers.natneg.src.contracts.responses import InitResponse +from servers.natneg.src.contracts.results import AddressCheckResult, ConnectResult, ErtAckResult, InitResult, NatifyResult, ReportResult class AddressCheckHandler(CmdHandlerBase): diff --git a/src/servers/natneg/handlers/switcher.py b/src/servers/natneg/src/handlers/switcher.py similarity index 82% rename from src/servers/natneg/handlers/switcher.py rename to src/servers/natneg/src/handlers/switcher.py index a23ee4bc8..16e70fff0 100644 --- a/src/servers/natneg/handlers/switcher.py +++ b/src/servers/natneg/src/handlers/switcher.py @@ -1,5 +1,5 @@ -from library.abstractions.switcher import SwitcherBase -from servers.natneg.applications.client import Client +from library.src.abstractions.switcher import SwitcherBase +from servers.natneg.src.applications.client import Client class CmdSwitcher(SwitcherBase): diff --git a/src/servers/natneg/tests/tests.py b/src/servers/natneg/tests/tests.py index 7477add84..6032c97e9 100644 --- a/src/servers/natneg/tests/tests.py +++ b/src/servers/natneg/tests/tests.py @@ -1,13 +1,13 @@ import unittest -from library.extentions.bytes_extentions import int_to_bytes -from servers.natneg.contracts.requests import ( +from library.src.extentions.bytes_extentions import int_to_bytes +from servers.natneg.src.contracts.requests import ( AddressCheckRequest, ErtAckRequest, InitRequest, NatifyRequest, PreInitRequest, ) -from servers.natneg.enums.general import ( +from servers.natneg.src.enums.general import ( NatClientIndex, NatPortType, PreInitState, diff --git a/src/servers/presence_connection_manager/abstractions/contracts.py b/src/servers/presence_connection_manager/src/abstractions/contracts.py similarity index 73% rename from src/servers/presence_connection_manager/abstractions/contracts.py rename to src/servers/presence_connection_manager/src/abstractions/contracts.py index fa6357ed9..3967050bb 100644 --- a/src/servers/presence_connection_manager/abstractions/contracts.py +++ b/src/servers/presence_connection_manager/src/abstractions/contracts.py @@ -1,14 +1,14 @@ import abc -import library.abstractions -import library.abstractions.contracts -from library.extentions.gamespy_utils import convert_to_key_value -from servers.presence_search_player.exceptions.general import ( +import library.src.abstractions +import library.src.abstractions.contracts +from library.src.extentions.gamespy_utils import convert_to_key_value +from servers.presence_search_player.src.exceptions.general import ( GPParseException, ) from typing import Dict -import library.abstractions.contracts +import library.src.abstractions.contracts def normalize_request(message: str): @@ -20,7 +20,7 @@ def normalize_request(message: str): return message -class RequestBase(library.abstractions.contracts.RequestBase, abc.ABC): +class RequestBase(library.src.abstractions.contracts.RequestBase, abc.ABC): command_name: str operation_id: int raw_request: str @@ -41,11 +41,11 @@ def parse(self): raise GPParseException("namespaceid is invalid.") -class ResultBase(library.abstractions.contracts.ResultBase): +class ResultBase(library.src.abstractions.contracts.ResultBase): pass -class ResponseBase(library.abstractions.contracts.ResponseBase, abc.ABC): +class ResponseBase(library.src.abstractions.contracts.ResponseBase, abc.ABC): _request: RequestBase _result: ResultBase sending_buffer: str diff --git a/src/servers/presence_connection_manager/abstractions/handler.py b/src/servers/presence_connection_manager/src/abstractions/handler.py similarity index 65% rename from src/servers/presence_connection_manager/abstractions/handler.py rename to src/servers/presence_connection_manager/src/abstractions/handler.py index 37908d109..f06a69494 100644 --- a/src/servers/presence_connection_manager/abstractions/handler.py +++ b/src/servers/presence_connection_manager/src/abstractions/handler.py @@ -1,17 +1,17 @@ import abc -from servers.presence_connection_manager.applications.client import Client -from servers.presence_connection_manager.enums.general import LoginStatus -from servers.presence_search_player.exceptions.general import GPException +from servers.presence_connection_manager.src.applications.client import Client +from servers.presence_connection_manager.src.enums.general import LoginStatus +from servers.presence_search_player.src.exceptions.general import GPException -from servers.presence_connection_manager.abstractions.contracts import ( +from servers.presence_connection_manager.src.abstractions.contracts import ( RequestBase, ResultBase, ) import library -class CmdHandlerBase(library.abstractions.handler.CmdHandlerBase): +class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase): _client: Client _request: RequestBase _result: ResultBase diff --git a/src/servers/presence_connection_manager/aggregates/login_challenge.py b/src/servers/presence_connection_manager/src/aggregates/login_challenge.py similarity index 91% rename from src/servers/presence_connection_manager/aggregates/login_challenge.py rename to src/servers/presence_connection_manager/src/aggregates/login_challenge.py index c768d054b..9a2039cfe 100644 --- a/src/servers/presence_connection_manager/aggregates/login_challenge.py +++ b/src/servers/presence_connection_manager/src/aggregates/login_challenge.py @@ -1,6 +1,6 @@ import hashlib -from servers.presence_connection_manager.enums.general import GPPartnerId, LoginType +from servers.presence_connection_manager.src.enums.general import GPPartnerId, LoginType SERVER_CHALLENGE = "0000000000" diff --git a/src/servers/presence_connection_manager/aggregates/sdk_revision.py b/src/servers/presence_connection_manager/src/aggregates/sdk_revision.py similarity index 95% rename from src/servers/presence_connection_manager/aggregates/sdk_revision.py rename to src/servers/presence_connection_manager/src/aggregates/sdk_revision.py index d68f6226c..d58a6b6c7 100644 --- a/src/servers/presence_connection_manager/aggregates/sdk_revision.py +++ b/src/servers/presence_connection_manager/src/aggregates/sdk_revision.py @@ -1,4 +1,4 @@ -from servers.presence_connection_manager.enums.general import SdkRevisionType +from servers.presence_connection_manager.src.enums.general import SdkRevisionType class SdkRevision: diff --git a/src/servers/presence_connection_manager/aggregates/user_status.py b/src/servers/presence_connection_manager/src/aggregates/user_status.py similarity index 72% rename from src/servers/presence_connection_manager/aggregates/user_status.py rename to src/servers/presence_connection_manager/src/aggregates/user_status.py index fc171c48d..077528dab 100644 --- a/src/servers/presence_connection_manager/aggregates/user_status.py +++ b/src/servers/presence_connection_manager/src/aggregates/user_status.py @@ -1,6 +1,6 @@ from dataclasses import dataclass from typing import Union -from servers.presence_connection_manager.enums.general import GPStatusCode +from servers.presence_connection_manager.src.enums.general import GPStatusCode @dataclass diff --git a/src/servers/presence_connection_manager/aggregates/user_status_info.py b/src/servers/presence_connection_manager/src/aggregates/user_status_info.py similarity index 100% rename from src/servers/presence_connection_manager/aggregates/user_status_info.py rename to src/servers/presence_connection_manager/src/aggregates/user_status_info.py diff --git a/src/servers/presence_connection_manager/applications/client.py b/src/servers/presence_connection_manager/src/applications/client.py similarity index 63% rename from src/servers/presence_connection_manager/applications/client.py rename to src/servers/presence_connection_manager/src/applications/client.py index f925fd949..e2439fc48 100644 --- a/src/servers/presence_connection_manager/applications/client.py +++ b/src/servers/presence_connection_manager/src/applications/client.py @@ -1,19 +1,19 @@ -from library.abstractions.client import ClientBase, ClientInfoBase +from library.src.abstractions.client import ClientBase, ClientInfoBase -from library.abstractions.connections import TcpConnectionBase -from library.abstractions.switcher import SwitcherBase -from library.log.log_manager import LogWriter -from library.unispy_server_config import ServerConfig -from servers.presence_connection_manager.aggregates.login_challenge import ( +from library.src.abstractions.connections import TcpConnectionBase +from library.src.abstractions.switcher import SwitcherBase +from library.src.log.log_manager import LogWriter +from library.src.unispy_server_config import ServerConfig +from servers.presence_connection_manager.src.aggregates.login_challenge import ( SERVER_CHALLENGE, ) -from servers.presence_connection_manager.handlers.switcher import Switcher -from servers.presence_connection_manager.enums.general import LoginStatus +from servers.presence_connection_manager.src.handlers.switcher import Switcher +from servers.presence_connection_manager.src.enums.general import LoginStatus from typing import TYPE_CHECKING -from servers.presence_connection_manager.aggregates.sdk_revision import SdkRevision -from servers.presence_connection_manager.enums.general import LoginStatus +from servers.presence_connection_manager.src.aggregates.sdk_revision import SdkRevision +from servers.presence_connection_manager.src.enums.general import LoginStatus LOGIN_TICKET = "0000000000000000000000__" SESSION_KEY = 1111 diff --git a/src/servers/presence_connection_manager/applications/data.py b/src/servers/presence_connection_manager/src/applications/data.py similarity index 97% rename from src/servers/presence_connection_manager/applications/data.py rename to src/servers/presence_connection_manager/src/applications/data.py index 7fd44225d..6599ee82f 100644 --- a/src/servers/presence_connection_manager/applications/data.py +++ b/src/servers/presence_connection_manager/src/applications/data.py @@ -1,5 +1,5 @@ from sqlalchemy import insert -from library.database.pg_orm import ( +from library.src.database.pg_orm import ( Blocked, Friends, Profiles, @@ -7,7 +7,7 @@ Users, PG_SESSION, ) -from servers.presence_search_player.exceptions.general import GPDatabaseException +from servers.presence_search_player.src.exceptions.general import GPDatabaseException def is_email_exist(email: str) -> bool: diff --git a/src/servers/presence_connection_manager/contracts/requests/buddy.py b/src/servers/presence_connection_manager/src/contracts/requests/buddy.py similarity index 91% rename from src/servers/presence_connection_manager/contracts/requests/buddy.py rename to src/servers/presence_connection_manager/src/contracts/requests/buddy.py index 172391346..53c109dec 100644 --- a/src/servers/presence_connection_manager/contracts/requests/buddy.py +++ b/src/servers/presence_connection_manager/src/contracts/requests/buddy.py @@ -1,8 +1,8 @@ -from servers.presence_connection_manager.abstractions.contracts import RequestBase -from servers.presence_connection_manager.aggregates.user_status import UserStatus -from servers.presence_connection_manager.aggregates.user_status_info import UserStatusInfo -from servers.presence_connection_manager.enums.general import GPStatusCode -from servers.presence_search_player.exceptions.general import GPParseException +from servers.presence_connection_manager.src.abstractions.contracts import RequestBase +from servers.presence_connection_manager.src.aggregates.user_status import UserStatus +from servers.presence_connection_manager.src.aggregates.user_status_info import UserStatusInfo +from servers.presence_connection_manager.src.enums.general import GPStatusCode +from servers.presence_search_player.src.exceptions.general import GPParseException class AddBuddyRequest(RequestBase): diff --git a/src/servers/presence_connection_manager/contracts/requests/general.py b/src/servers/presence_connection_manager/src/contracts/requests/general.py similarity index 94% rename from src/servers/presence_connection_manager/contracts/requests/general.py rename to src/servers/presence_connection_manager/src/contracts/requests/general.py index edac1efa0..dd12e80c9 100644 --- a/src/servers/presence_connection_manager/contracts/requests/general.py +++ b/src/servers/presence_connection_manager/src/contracts/requests/general.py @@ -1,10 +1,10 @@ -from servers.presence_connection_manager.abstractions.contracts import RequestBase -from servers.presence_connection_manager.enums.general import ( +from servers.presence_connection_manager.src.abstractions.contracts import RequestBase +from servers.presence_connection_manager.src.enums.general import ( LoginType, QuietModeType, SdkRevisionType, ) -from servers.presence_search_player.exceptions.general import ( +from servers.presence_search_player.src.exceptions.general import ( GPParseException, ) diff --git a/src/servers/presence_connection_manager/contracts/requests/profile.py b/src/servers/presence_connection_manager/src/contracts/requests/profile.py similarity index 96% rename from src/servers/presence_connection_manager/contracts/requests/profile.py rename to src/servers/presence_connection_manager/src/contracts/requests/profile.py index 0c56c3a20..11d85ab3a 100644 --- a/src/servers/presence_connection_manager/contracts/requests/profile.py +++ b/src/servers/presence_connection_manager/src/contracts/requests/profile.py @@ -1,7 +1,7 @@ -from library.extentions.gamespy_utils import is_valid_date -from servers.presence_connection_manager.abstractions.contracts import RequestBase -from servers.presence_connection_manager.enums.general import PublicMasks -from servers.presence_search_player.exceptions.general import ( +from library.src.extentions.gamespy_utils import is_valid_date +from servers.presence_connection_manager.src.abstractions.contracts import RequestBase +from servers.presence_connection_manager.src.enums.general import PublicMasks +from servers.presence_search_player.src.exceptions.general import ( GPParseException, ) diff --git a/src/servers/presence_connection_manager/contracts/responses/buddy.py b/src/servers/presence_connection_manager/src/contracts/responses/buddy.py similarity index 92% rename from src/servers/presence_connection_manager/contracts/responses/buddy.py rename to src/servers/presence_connection_manager/src/contracts/responses/buddy.py index 7870a90e3..f8636da18 100644 --- a/src/servers/presence_connection_manager/contracts/responses/buddy.py +++ b/src/servers/presence_connection_manager/src/contracts/responses/buddy.py @@ -1,9 +1,9 @@ -from servers.presence_connection_manager.abstractions.contracts import ( +from servers.presence_connection_manager.src.abstractions.contracts import ( RequestBase, ResponseBase, ) -from servers.presence_connection_manager.contracts.requests.buddy import AddBuddyRequest, StatusInfoRequest -from servers.presence_connection_manager.contracts.results.buddy import ( +from servers.presence_connection_manager.src.contracts.requests.buddy import AddBuddyRequest, StatusInfoRequest +from servers.presence_connection_manager.src.contracts.results.buddy import ( AddBuddyResult, BlockListResult, BuddyListResult, diff --git a/src/servers/presence_connection_manager/contracts/responses/general.py b/src/servers/presence_connection_manager/src/contracts/responses/general.py similarity index 72% rename from src/servers/presence_connection_manager/contracts/responses/general.py rename to src/servers/presence_connection_manager/src/contracts/responses/general.py index c92140245..9b4af2e5c 100644 --- a/src/servers/presence_connection_manager/contracts/responses/general.py +++ b/src/servers/presence_connection_manager/src/contracts/responses/general.py @@ -1,20 +1,20 @@ -from servers.chat.contracts.requests.general import LoginRequest -from servers.chat.contracts.results.general import LoginResult -from servers.presence_connection_manager.abstractions.contracts import ResponseBase -from servers.presence_connection_manager.applications.client import ( +from servers.chat.src.contracts.requests.general import LoginRequest +from servers.chat.src.contracts.results.general import LoginResult +from servers.presence_connection_manager.src.abstractions.contracts import ResponseBase +from servers.presence_connection_manager.src.applications.client import ( LOGIN_TICKET, SESSION_KEY, ) -from servers.presence_connection_manager.contracts.requests.general import ( +from servers.presence_connection_manager.src.contracts.requests.general import ( KeepAliveRequest, ) -from servers.presence_search_player.contracts.requests import ( +from servers.presence_search_player.src.contracts.requests import ( NewUserRequest, ) -from servers.presence_search_player.contracts.responses import ( +from servers.presence_search_player.src.contracts.responses import ( NewUserResponse as NUR, ) -from servers.presence_search_player.contracts.results import NewUserResult +from servers.presence_search_player.src.contracts.results import NewUserResult class KeepAliveResponse(ResponseBase): diff --git a/src/servers/presence_connection_manager/contracts/responses/profile.py b/src/servers/presence_connection_manager/src/contracts/responses/profile.py similarity index 89% rename from src/servers/presence_connection_manager/contracts/responses/profile.py rename to src/servers/presence_connection_manager/src/contracts/responses/profile.py index 19d54a659..00664f8e3 100644 --- a/src/servers/presence_connection_manager/contracts/responses/profile.py +++ b/src/servers/presence_connection_manager/src/contracts/responses/profile.py @@ -1,8 +1,7 @@ -from library.extentions.gamespy_ramdoms import StringType, generate_random_string -from servers.chat.contracts.requests.general import RegisterNickRequest -from servers.presence_connection_manager.abstractions.contracts import ResponseBase -from servers.presence_connection_manager.contracts.requests.profile import GetProfileRequest, NewProfileRequest -from servers.presence_connection_manager.contracts.results.profile import GetProfileResult, NewProfileResult +from library.src.extentions.gamespy_ramdoms import StringType, generate_random_string +from servers.presence_connection_manager.src.abstractions.contracts import ResponseBase +from servers.presence_connection_manager.src.contracts.requests.profile import GetProfileRequest, NewProfileRequest, RegisterNickRequest +from servers.presence_connection_manager.src.contracts.results.profile import GetProfileResult, NewProfileResult class GetProfileResponse(ResponseBase): diff --git a/src/servers/presence_connection_manager/contracts/results/buddy.py b/src/servers/presence_connection_manager/src/contracts/results/buddy.py similarity index 60% rename from src/servers/presence_connection_manager/contracts/results/buddy.py rename to src/servers/presence_connection_manager/src/contracts/results/buddy.py index f2d1c3956..97d08f34c 100644 --- a/src/servers/presence_connection_manager/contracts/results/buddy.py +++ b/src/servers/presence_connection_manager/src/contracts/results/buddy.py @@ -1,6 +1,6 @@ -from servers.presence_connection_manager.abstractions.contracts import ResultBase -from servers.presence_connection_manager.aggregates.user_status import UserStatus -from servers.presence_connection_manager.aggregates.user_status_info import ( +from servers.presence_connection_manager.src.abstractions.contracts import ResultBase +from servers.presence_connection_manager.src.aggregates.user_status import UserStatus +from servers.presence_connection_manager.src.aggregates.user_status_info import ( UserStatusInfo, ) diff --git a/src/servers/presence_connection_manager/contracts/results/general.py b/src/servers/presence_connection_manager/src/contracts/results/general.py similarity index 78% rename from src/servers/presence_connection_manager/contracts/results/general.py rename to src/servers/presence_connection_manager/src/contracts/results/general.py index 970d6c04a..b300724f2 100644 --- a/src/servers/presence_connection_manager/contracts/results/general.py +++ b/src/servers/presence_connection_manager/src/contracts/results/general.py @@ -1,4 +1,4 @@ -from servers.presence_connection_manager.abstractions.contracts import ResultBase +from servers.presence_connection_manager.src.abstractions.contracts import ResultBase class LoginDataModel: diff --git a/src/servers/presence_connection_manager/contracts/results/profile.py b/src/servers/presence_connection_manager/src/contracts/results/profile.py similarity index 91% rename from src/servers/presence_connection_manager/contracts/results/profile.py rename to src/servers/presence_connection_manager/src/contracts/results/profile.py index a209797d6..27876e6e3 100644 --- a/src/servers/presence_connection_manager/contracts/results/profile.py +++ b/src/servers/presence_connection_manager/src/contracts/results/profile.py @@ -1,4 +1,4 @@ -from servers.presence_connection_manager.abstractions.contracts import ResultBase +from servers.presence_connection_manager.src.abstractions.contracts import ResultBase class GetProfileDataModel: diff --git a/src/servers/presence_connection_manager/enums/general.py b/src/servers/presence_connection_manager/src/enums/general.py similarity index 100% rename from src/servers/presence_connection_manager/enums/general.py rename to src/servers/presence_connection_manager/src/enums/general.py diff --git a/src/servers/presence_connection_manager/handlers/buddy.py b/src/servers/presence_connection_manager/src/handlers/buddy.py similarity index 87% rename from src/servers/presence_connection_manager/handlers/buddy.py rename to src/servers/presence_connection_manager/src/handlers/buddy.py index 1b113545e..c08308e4e 100644 --- a/src/servers/presence_connection_manager/handlers/buddy.py +++ b/src/servers/presence_connection_manager/src/handlers/buddy.py @@ -1,21 +1,21 @@ from multiprocessing.pool import Pool -from servers.presence_connection_manager.abstractions.contracts import RequestBase -from servers.presence_connection_manager.abstractions.handler import ( +from servers.presence_connection_manager.src.abstractions.contracts import RequestBase +from servers.presence_connection_manager.src.abstractions.handler import ( CmdHandlerBase, LoginHandlerBase, ) -from servers.presence_connection_manager.applications.client import Client -from servers.presence_connection_manager.contracts.requests.buddy import ( +from servers.presence_connection_manager.src.applications.client import Client +from servers.presence_connection_manager.src.contracts.requests.buddy import ( DelBuddyRequest, StatusInfoRequest, StatusRequest, ) -from servers.presence_connection_manager.contracts.responses.buddy import ( +from servers.presence_connection_manager.src.contracts.responses.buddy import ( BlockListResponse, BuddyListResponse, StatusInfoResponse, ) -from servers.presence_connection_manager.contracts.results.buddy import ( +from servers.presence_connection_manager.src.contracts.results.buddy import ( BlockListResult, BuddyListResult, StatusInfoResult, diff --git a/src/servers/presence_connection_manager/handlers/general.py b/src/servers/presence_connection_manager/src/handlers/general.py similarity index 70% rename from src/servers/presence_connection_manager/handlers/general.py rename to src/servers/presence_connection_manager/src/handlers/general.py index c675ce955..da5cecca7 100644 --- a/src/servers/presence_connection_manager/handlers/general.py +++ b/src/servers/presence_connection_manager/src/handlers/general.py @@ -1,24 +1,24 @@ -from servers.chat.contracts.requests.general import LoginRequest -from servers.chat.contracts.results.general import LoginResult -from servers.presence_connection_manager.abstractions.handler import ( +from servers.chat.src.contracts.requests.general import LoginRequest +from servers.chat.src.contracts.results.general import LoginResult +from servers.presence_connection_manager.src.abstractions.handler import ( CmdHandlerBase, LoginHandlerBase, ) -from servers.presence_connection_manager.aggregates.sdk_revision import SdkRevision -from servers.presence_connection_manager.applications.client import Client -from servers.presence_connection_manager.contracts.requests.general import ( +from servers.presence_connection_manager.src.aggregates.sdk_revision import SdkRevision +from servers.presence_connection_manager.src.applications.client import Client +from servers.presence_connection_manager.src.contracts.requests.general import ( KeepAliveRequest, LogoutRequest, ) -from servers.presence_connection_manager.contracts.responses.general import ( +from servers.presence_connection_manager.src.contracts.responses.general import ( KeepAliveResponse, LoginResponse, ) -from servers.presence_connection_manager.handlers.buddy import ( +from servers.presence_connection_manager.src.handlers.buddy import ( BlockListHandler, BuddyListHandler, ) -from servers.presence_search_player.contracts.responses import NewUserResponse +from servers.presence_search_player.src.contracts.responses import NewUserResponse class KeepAliveHandler(CmdHandlerBase): @@ -51,7 +51,7 @@ def __init__(self, client: Client, request: LogoutRequest) -> None: super().__init__(client, request) -import servers.presence_search_player.handlers.handlers +import servers.presence_search_player.src.handlers.handlers class NewUserHandler(servers.presence_search_player.handlers.handlers.NewUserHandler): diff --git a/src/servers/presence_connection_manager/handlers/profile.py b/src/servers/presence_connection_manager/src/handlers/profile.py similarity index 80% rename from src/servers/presence_connection_manager/handlers/profile.py rename to src/servers/presence_connection_manager/src/handlers/profile.py index bd4eed2ae..afdc5445f 100644 --- a/src/servers/presence_connection_manager/handlers/profile.py +++ b/src/servers/presence_connection_manager/src/handlers/profile.py @@ -1,20 +1,20 @@ -from servers.chat.contracts.requests.general import RegisterNickRequest -from servers.presence_connection_manager.abstractions.contracts import RequestBase -from servers.presence_connection_manager.abstractions.handler import CmdHandlerBase -from servers.presence_connection_manager.applications.client import Client -from servers.presence_connection_manager.contracts.requests.profile import ( +from servers.chat.src.contracts.requests.general import RegisterNickRequest +from servers.presence_connection_manager.src.abstractions.contracts import RequestBase +from servers.presence_connection_manager.src.abstractions.handler import CmdHandlerBase +from servers.presence_connection_manager.src.applications.client import Client +from servers.presence_connection_manager.src.contracts.requests.profile import ( AddBlockRequest, GetProfileRequest, NewProfileRequest, RegisterCDKeyRequest, UpdateProfileRequest, ) -from servers.presence_connection_manager.contracts.responses.profile import ( +from servers.presence_connection_manager.src.contracts.responses.profile import ( GetProfileResponse, NewProfileResponse, RegisterNickResponse, ) -from servers.presence_connection_manager.contracts.results.profile import NewProfileResult +from servers.presence_connection_manager.src.contracts.results.profile import NewProfileResult class AddBlockHandler(CmdHandlerBase): diff --git a/src/servers/presence_connection_manager/handlers/switcher.py b/src/servers/presence_connection_manager/src/handlers/switcher.py similarity index 62% rename from src/servers/presence_connection_manager/handlers/switcher.py rename to src/servers/presence_connection_manager/src/handlers/switcher.py index 843f1997a..3f1d6e75f 100644 --- a/src/servers/presence_connection_manager/handlers/switcher.py +++ b/src/servers/presence_connection_manager/src/handlers/switcher.py @@ -1,15 +1,15 @@ -from library.abstractions.switcher import SwitcherBase -from servers.chat.contracts.requests.general import LoginRequest, RegisterNickRequest -from servers.presence_connection_manager.abstractions.handler import LoginHandlerBase -from servers.presence_connection_manager.applications.client import Client -from servers.presence_connection_manager.contracts.requests.buddy import StatusInfoRequest, StatusRequest -from servers.presence_connection_manager.contracts.requests.general import KeepAliveRequest, LogoutRequest -from servers.presence_connection_manager.contracts.requests.profile import AddBlockRequest, GetProfileRequest, NewProfileRequest, RegisterCDKeyRequest, UpdateProfileRequest -from servers.presence_connection_manager.handlers.buddy import StatusHandler, StatusInfoHandler -from servers.presence_connection_manager.handlers.general import KeepAliveHandler, LogoutHandler, NewUserHandler -from servers.presence_connection_manager.handlers.profile import AddBlockHandler, GetProfileHandler, NewProfileHandler, RegisterCDKeyHandler, RegisterNickHandler, UpdateProfileHandler -from servers.presence_search_player.contracts.requests import NewUserRequest -from servers.presence_search_player.exceptions.general import ( +from library.src.abstractions.switcher import SwitcherBase +from servers.chat.src.contracts.requests.general import LoginRequest, RegisterNickRequest +from servers.presence_connection_manager.src.abstractions.handler import LoginHandlerBase +from servers.presence_connection_manager.src.applications.client import Client +from servers.presence_connection_manager.src.contracts.requests.buddy import StatusInfoRequest, StatusRequest +from servers.presence_connection_manager.src.contracts.requests.general import KeepAliveRequest, LogoutRequest +from servers.presence_connection_manager.src.contracts.requests.profile import AddBlockRequest, GetProfileRequest, NewProfileRequest, RegisterCDKeyRequest, UpdateProfileRequest +from servers.presence_connection_manager.src.handlers.buddy import StatusHandler, StatusInfoHandler +from servers.presence_connection_manager.src.handlers.general import KeepAliveHandler, LogoutHandler, NewUserHandler +from servers.presence_connection_manager.src.handlers.profile import AddBlockHandler, GetProfileHandler, NewProfileHandler, RegisterCDKeyHandler, RegisterNickHandler, UpdateProfileHandler +from servers.presence_search_player.src.contracts.requests import NewUserRequest +from servers.presence_search_player.src.exceptions.general import ( GPParseException, ) diff --git a/src/servers/presence_connection_manager/tests/buddy_requests.py b/src/servers/presence_connection_manager/tests/buddy_requests.py index d5111bf6d..a9be50b80 100644 --- a/src/servers/presence_connection_manager/tests/buddy_requests.py +++ b/src/servers/presence_connection_manager/tests/buddy_requests.py @@ -1,6 +1,6 @@ import unittest -from servers.presence_connection_manager.contracts.requests.buddy import ( +from servers.presence_connection_manager.src.contracts.requests.buddy import ( AddBuddyRequest, DelBuddyRequest, InviteToRequest, diff --git a/src/servers/presence_connection_manager/tests/game.py b/src/servers/presence_connection_manager/tests/game.py index c1e432023..9cbfb0d53 100644 --- a/src/servers/presence_connection_manager/tests/game.py +++ b/src/servers/presence_connection_manager/tests/game.py @@ -1,6 +1,6 @@ import unittest -from servers.presence_connection_manager.contracts.requests.buddy import StatusRequest +from servers.presence_connection_manager.src.contracts.requests.buddy import StatusRequest class GameTest(unittest.TestCase): diff --git a/src/servers/presence_connection_manager/tests/general_requests.py b/src/servers/presence_connection_manager/tests/general_requests.py index c536a7793..82cbc1049 100644 --- a/src/servers/presence_connection_manager/tests/general_requests.py +++ b/src/servers/presence_connection_manager/tests/general_requests.py @@ -1,6 +1,6 @@ import unittest -from servers.presence_connection_manager.contracts.requests.general import LoginRequest -from servers.presence_connection_manager.enums.general import ( +from servers.presence_connection_manager.src.contracts.requests.general import LoginRequest +from servers.presence_connection_manager.src.enums.general import ( LoginType, QuietModeType, SdkRevisionType, diff --git a/src/servers/presence_connection_manager/tests/profile_requests.py b/src/servers/presence_connection_manager/tests/profile_requests.py index 9f733e1f1..c20bc1cb5 100644 --- a/src/servers/presence_connection_manager/tests/profile_requests.py +++ b/src/servers/presence_connection_manager/tests/profile_requests.py @@ -1,6 +1,6 @@ import unittest -from servers.presence_connection_manager.contracts.requests.profile import ( +from servers.presence_connection_manager.src.contracts.requests.profile import ( AddBlockRequest, GetProfileRequest, NewProfileRequest, diff --git a/src/servers/presence_search_player/applications/client.py b/src/servers/presence_search_player/applications/client.py deleted file mode 100644 index 5f952e908..000000000 --- a/src/servers/presence_search_player/applications/client.py +++ /dev/null @@ -1,8 +0,0 @@ -from library.abstractions.client import ClientBase -from servers.presence_search_player.handlers.switcher import CmdSwitcher - - -class Client(ClientBase): - - def _create_switcher(self, buffer) -> CmdSwitcher: - return CmdSwitcher(self, buffer) diff --git a/src/servers/presence_search_player/abstractions/contracts.py b/src/servers/presence_search_player/src/abstractions/contracts.py similarity index 71% rename from src/servers/presence_search_player/abstractions/contracts.py rename to src/servers/presence_search_player/src/abstractions/contracts.py index d3b956115..5dd83d1bd 100644 --- a/src/servers/presence_search_player/abstractions/contracts.py +++ b/src/servers/presence_search_player/src/abstractions/contracts.py @@ -1,18 +1,18 @@ import abc from typing import Dict import library -from library.extentions.gamespy_utils import convert_to_key_value -from servers.presence_search_player.exceptions.general import ( +from library.src.extentions.gamespy_utils import convert_to_key_value +from servers.presence_search_player.src.exceptions.general import ( GPParseException, ) -from servers.presence_search_player.abstractions.contracts import ( +from servers.presence_search_player.src.abstractions.contracts import ( RequestBase, ResultBase, ) -class RequestBase(library.abstractions.contracts.RequestBase, abc.ABC): +class RequestBase(library.src.abstractions.contracts.RequestBase, abc.ABC): request_dict: Dict[str, str] raw_request: str command_name: str @@ -39,10 +39,10 @@ def parse(self) -> None: raise GPParseException("namespaceid is incorrect.") -class ResultBase(library.abstractions.contracts.ResultBase, abc.ABC): +class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): pass -class ResponseBase(library.abstractions.contracts.ResponseBase, abc.ABC): +class ResponseBase(library.src.abstractions.contracts.ResponseBase, abc.ABC): _result: ResultBase _request: RequestBase diff --git a/src/servers/presence_search_player/abstractions/handler.py b/src/servers/presence_search_player/src/abstractions/handler.py similarity index 56% rename from src/servers/presence_search_player/abstractions/handler.py rename to src/servers/presence_search_player/src/abstractions/handler.py index f62ff48f9..0c96c4344 100644 --- a/src/servers/presence_search_player/abstractions/handler.py +++ b/src/servers/presence_search_player/src/abstractions/handler.py @@ -1,7 +1,7 @@ -from library.abstractions.handler import CmdHandlerBase as CHB -from servers.presence_search_player.abstractions.contracts import RequestBase -from servers.presence_search_player.applications.client import Client -from servers.presence_search_player.exceptions.general import GPException +from library.src.abstractions.handler import CmdHandlerBase as CHB +from servers.presence_search_player.src.abstractions.contracts import RequestBase +from servers.presence_search_player.src.applications.client import Client +from servers.presence_search_player.src.exceptions.general import GPException class CmdHandlerBase(CHB): diff --git a/src/servers/presence_search_player/src/applications/client.py b/src/servers/presence_search_player/src/applications/client.py new file mode 100644 index 000000000..08d5bde1e --- /dev/null +++ b/src/servers/presence_search_player/src/applications/client.py @@ -0,0 +1,8 @@ +from library.src.abstractions.client import ClientBase +from servers.presence_search_player.src.handlers.switcher import CmdSwitcher + + +class Client(ClientBase): + + def _create_switcher(self, buffer) -> CmdSwitcher: + return CmdSwitcher(self, buffer) diff --git a/src/servers/presence_search_player/applications/data.py b/src/servers/presence_search_player/src/applications/data.py similarity index 99% rename from src/servers/presence_search_player/applications/data.py rename to src/servers/presence_search_player/src/applications/data.py index 0e65cc7c3..869deb1da 100644 --- a/src/servers/presence_search_player/applications/data.py +++ b/src/servers/presence_search_player/src/applications/data.py @@ -1,5 +1,5 @@ from sqlalchemy import insert -from library.database.pg_orm import ( +from library.src.database.pg_orm import ( Friends, Profiles, SubProfiles, diff --git a/src/servers/chat/contracts/requests/__init__.py b/src/servers/presence_search_player/src/contracts/__init__.py similarity index 100% rename from src/servers/chat/contracts/requests/__init__.py rename to src/servers/presence_search_player/src/contracts/__init__.py diff --git a/src/servers/presence_search_player/contracts/requests.py b/src/servers/presence_search_player/src/contracts/requests.py similarity index 96% rename from src/servers/presence_search_player/contracts/requests.py rename to src/servers/presence_search_player/src/contracts/requests.py index 352c17ab6..188be1915 100644 --- a/src/servers/presence_search_player/contracts/requests.py +++ b/src/servers/presence_search_player/src/contracts/requests.py @@ -1,8 +1,8 @@ -from library.extentions.gamespy_utils import is_email_format_correct -from library.extentions.password_encoder import process_password -from servers.presence_search_player.abstractions.contracts import RequestBase -from servers.presence_search_player.enums.general import SearchType -from servers.presence_search_player.exceptions.general import ( +from library.src.extentions.gamespy_utils import is_email_format_correct +from library.src.extentions.password_encoder import process_password +from servers.presence_search_player.src.abstractions.contracts import RequestBase +from servers.presence_search_player.src.enums.general import SearchType +from servers.presence_search_player.src.exceptions.general import ( GPParseException, ) diff --git a/src/servers/presence_search_player/contracts/responses.py b/src/servers/presence_search_player/src/contracts/responses.py similarity index 96% rename from src/servers/presence_search_player/contracts/responses.py rename to src/servers/presence_search_player/src/contracts/responses.py index f5add98e1..576371213 100644 --- a/src/servers/presence_search_player/contracts/responses.py +++ b/src/servers/presence_search_player/src/contracts/responses.py @@ -1,12 +1,12 @@ -from servers.presence_connection_manager.abstractions.contracts import ResponseBase -from servers.presence_search_player.contracts.requests import ( +from servers.presence_connection_manager.src.abstractions.contracts import ResponseBase +from servers.presence_search_player.src.contracts.requests import ( CheckRequest, NewUserRequest, NicksRequest, UniqueSearchRequest, ValidRequest, ) -from servers.presence_search_player.contracts.results import ( +from servers.presence_search_player.src.contracts.results import ( CheckResult, NewUserResult, NicksResult, diff --git a/src/servers/presence_search_player/contracts/results.py b/src/servers/presence_search_player/src/contracts/results.py similarity index 91% rename from src/servers/presence_search_player/contracts/results.py rename to src/servers/presence_search_player/src/contracts/results.py index b23da3985..ff7f01715 100644 --- a/src/servers/presence_search_player/contracts/results.py +++ b/src/servers/presence_search_player/src/contracts/results.py @@ -1,5 +1,5 @@ -from library.database.pg_orm import Profiles, SubProfiles, Users -from servers.presence_connection_manager.abstractions.contracts import ResultBase +from library.src.database.pg_orm import Profiles, SubProfiles, Users +from servers.presence_connection_manager.src.abstractions.contracts import ResultBase class CheckResult(ResultBase): diff --git a/src/servers/presence_search_player/enums/error_codes.py b/src/servers/presence_search_player/src/enums/error_codes.py similarity index 100% rename from src/servers/presence_search_player/enums/error_codes.py rename to src/servers/presence_search_player/src/enums/error_codes.py diff --git a/src/servers/presence_search_player/enums/general.py b/src/servers/presence_search_player/src/enums/general.py similarity index 100% rename from src/servers/presence_search_player/enums/general.py rename to src/servers/presence_search_player/src/enums/general.py diff --git a/src/servers/presence_search_player/exceptions/general.py b/src/servers/presence_search_player/src/exceptions/general.py similarity index 98% rename from src/servers/presence_search_player/exceptions/general.py rename to src/servers/presence_search_player/src/exceptions/general.py index 1ba02d9c7..07b08b646 100644 --- a/src/servers/presence_search_player/exceptions/general.py +++ b/src/servers/presence_search_player/src/exceptions/general.py @@ -1,6 +1,6 @@ -from library.exceptions.error import UniSpyException -from servers.presence_search_player.enums.error_codes import GPErrorCode -from library.abstractions.contracts import ResponseBase +from library.src.exceptions.error import UniSpyException +from servers.presence_search_player.src.enums.error_codes import GPErrorCode +from library.src.abstractions.contracts import ResponseBase class GPException(UniSpyException, ResponseBase): diff --git a/src/servers/presence_search_player/handlers/handlers.py b/src/servers/presence_search_player/src/handlers/handlers.py similarity index 82% rename from src/servers/presence_search_player/handlers/handlers.py rename to src/servers/presence_search_player/src/handlers/handlers.py index 35e8ea62e..42328d901 100644 --- a/src/servers/presence_search_player/handlers/handlers.py +++ b/src/servers/presence_search_player/src/handlers/handlers.py @@ -1,9 +1,9 @@ -from servers.presence_connection_manager.abstractions.handler import CmdHandlerBase -from servers.presence_connection_manager.applications.client import Client -from servers.presence_connection_manager.contracts.responses.general import NewUserResponse -from servers.presence_search_player.contracts.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest -from servers.presence_search_player.contracts.responses import CheckResponse, NicksResponse, OthersListResponse, OthersResponse, SearchResponse, SearchUniqueResponse, UniqueSearchResponse, ValidResponse -from servers.presence_search_player.contracts.results import CheckResult, NewUserResult, NicksResult +from servers.presence_connection_manager.src.abstractions.handler import CmdHandlerBase +from servers.presence_connection_manager.src.applications.client import Client +from servers.presence_connection_manager.src.contracts.responses.general import NewUserResponse +from servers.presence_search_player.src.contracts.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest +from servers.presence_search_player.src.contracts.responses import CheckResponse, NicksResponse, OthersListResponse, OthersResponse, SearchResponse, SearchUniqueResponse, UniqueSearchResponse, ValidResponse +from servers.presence_search_player.src.contracts.results import CheckResult, NewUserResult, NicksResult diff --git a/src/servers/presence_search_player/handlers/switcher.py b/src/servers/presence_search_player/src/handlers/switcher.py similarity index 74% rename from src/servers/presence_search_player/handlers/switcher.py rename to src/servers/presence_search_player/src/handlers/switcher.py index 93c0728ec..3f1b8426d 100644 --- a/src/servers/presence_search_player/handlers/switcher.py +++ b/src/servers/presence_search_player/src/handlers/switcher.py @@ -1,10 +1,10 @@ -from servers.presence_connection_manager.handlers.general import NewUserHandler -from servers.presence_search_player.abstractions.handler import CmdHandlerBase -from servers.presence_search_player.applications.client import Client -from servers.presence_search_player.contracts.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest +from servers.presence_connection_manager.src.handlers.general import NewUserHandler +from servers.presence_search_player.src.abstractions.handler import CmdHandlerBase +from servers.presence_search_player.src.applications.client import Client +from servers.presence_search_player.src.contracts.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest -from library.abstractions.switcher import SwitcherBase -from servers.presence_search_player.handlers.handlers import CheckHandler, NicksHandler, OthersHandler, OthersListHandler, SearchHandler, SearchUniqueHandler, UniqueSearchHandler, ValidHandler +from library.src.abstractions.switcher import SwitcherBase +from servers.presence_search_player.src.handlers.handlers import CheckHandler, NicksHandler, OthersHandler, OthersListHandler, SearchHandler, SearchUniqueHandler, UniqueSearchHandler, ValidHandler class CmdSwitcher(SwitcherBase): diff --git a/src/servers/query_report/exceptions/exceptions.py b/src/servers/query_report/exceptions/exceptions.py deleted file mode 100644 index 94aa0e2d8..000000000 --- a/src/servers/query_report/exceptions/exceptions.py +++ /dev/null @@ -1,5 +0,0 @@ -from library.exceptions.error import UniSpyException - - -class QRException(UniSpyException): - pass diff --git a/src/servers/query_report/aggregates/game_server_info.py b/src/servers/query_report/src/aggregates/game_server_info.py similarity index 89% rename from src/servers/query_report/aggregates/game_server_info.py rename to src/servers/query_report/src/aggregates/game_server_info.py index 608ee544a..7f1c23905 100644 --- a/src/servers/query_report/aggregates/game_server_info.py +++ b/src/servers/query_report/src/aggregates/game_server_info.py @@ -2,7 +2,7 @@ import socket from uuid import UUID -from servers.query_report.v2.enums.general import GameServerStatus +from servers.query_report.src.v2.enums.general import GameServerStatus class GameServerInfo: diff --git a/src/servers/query_report/aggregates/natneg_cookie.py b/src/servers/query_report/src/aggregates/natneg_cookie.py similarity index 100% rename from src/servers/query_report/aggregates/natneg_cookie.py rename to src/servers/query_report/src/aggregates/natneg_cookie.py diff --git a/src/servers/query_report/aggregates/peer_room_info.py b/src/servers/query_report/src/aggregates/peer_room_info.py similarity index 100% rename from src/servers/query_report/aggregates/peer_room_info.py rename to src/servers/query_report/src/aggregates/peer_room_info.py diff --git a/src/servers/query_report/applications/client.py b/src/servers/query_report/src/applications/client.py similarity index 70% rename from src/servers/query_report/applications/client.py rename to src/servers/query_report/src/applications/client.py index 55f04e221..52c9ba41a 100644 --- a/src/servers/query_report/applications/client.py +++ b/src/servers/query_report/src/applications/client.py @@ -1,7 +1,7 @@ -from library.abstractions.client import ClientBase +from library.src.abstractions.client import ClientBase # import servers.query_report.v1 -from servers.query_report.v2.applications.switcher import CmdSwitcher as V2CmdSwitcher +from servers.query_report.src.v2.applications.switcher import CmdSwitcher as V2CmdSwitcher class Client(ClientBase): diff --git a/src/servers/query_report/applications/data.py b/src/servers/query_report/src/applications/data.py similarity index 92% rename from src/servers/query_report/applications/data.py rename to src/servers/query_report/src/applications/data.py index 519e954ca..3d55c2e63 100644 --- a/src/servers/query_report/applications/data.py +++ b/src/servers/query_report/src/applications/data.py @@ -1,4 +1,4 @@ -from library.database.pg_orm import PG_SESSION, GroupList, Games +from library.src.database.pg_orm import PG_SESSION, GroupList, Games def get_all_groups(): diff --git a/src/servers/query_report/src/exceptions/exceptions.py b/src/servers/query_report/src/exceptions/exceptions.py new file mode 100644 index 000000000..91c3b0b75 --- /dev/null +++ b/src/servers/query_report/src/exceptions/exceptions.py @@ -0,0 +1,5 @@ +from library.src.exceptions.error import UniSpyException + + +class QRException(UniSpyException): + pass diff --git a/src/servers/chat/contracts/responses/__init__.py b/src/servers/query_report/src/v1/__init__.py similarity index 100% rename from src/servers/chat/contracts/responses/__init__.py rename to src/servers/query_report/src/v1/__init__.py diff --git a/src/servers/chat/contracts/results/__init__.py b/src/servers/query_report/src/v1/abstractions/__init__.py similarity index 100% rename from src/servers/chat/contracts/results/__init__.py rename to src/servers/query_report/src/v1/abstractions/__init__.py diff --git a/src/servers/query_report/v1/abstractions/contracts.py b/src/servers/query_report/src/v1/abstractions/contracts.py similarity index 64% rename from src/servers/query_report/v1/abstractions/contracts.py rename to src/servers/query_report/src/v1/abstractions/contracts.py index 673de7213..8f05300e8 100644 --- a/src/servers/query_report/v1/abstractions/contracts.py +++ b/src/servers/query_report/src/v1/abstractions/contracts.py @@ -1,10 +1,10 @@ -import library.abstractions.contracts +import library.src.abstractions.contracts import abc -from library.extentions.gamespy_utils import convert_to_key_value +from library.src.extentions.gamespy_utils import convert_to_key_value -class RequestBase(library.abstractions.contracts.RequestBase, abc.ABC): +class RequestBase(library.src.abstractions.contracts.RequestBase, abc.ABC): request_dict: dict[str, str] = {} def __init__(self, raw_request: str) -> None: @@ -16,11 +16,11 @@ def parse(self) -> None: self.command_name = self.request_dict.keys()[0] -class ResultBase(library.abstractions.contracts.ResultBase, abc.ABC): +class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): pass -class ResponseBase(library.abstractions.contracts.ResponseBase): +class ResponseBase(library.src.abstractions.contracts.ResponseBase): sending_buffer: str def __init__(self, request: RequestBase, result: ResultBase) -> None: diff --git a/src/servers/query_report/src/v1/abstractions/handlers.py b/src/servers/query_report/src/v1/abstractions/handlers.py new file mode 100644 index 000000000..7a615966a --- /dev/null +++ b/src/servers/query_report/src/v1/abstractions/handlers.py @@ -0,0 +1,10 @@ +import library.src.abstractions.handler +from servers.query_report.src.v1.abstractions.contracts import RequestBase +from servers.query_report.src.applications.client import Client + + +class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase): + def __init__(self, client: Client, request: RequestBase) -> None: + assert issubclass(type(request), RequestBase) + assert isinstance(client, Client) + super().__init__(client, request) diff --git a/src/servers/query_report/v1/aggregations/game_server_info_v1.py b/src/servers/query_report/src/v1/aggregations/game_server_info_v1.py similarity index 100% rename from src/servers/query_report/v1/aggregations/game_server_info_v1.py rename to src/servers/query_report/src/v1/aggregations/game_server_info_v1.py diff --git a/src/servers/query_report/v2/abstractions/cmd_handler_base.py b/src/servers/query_report/src/v2/abstractions/cmd_handler_base.py similarity index 55% rename from src/servers/query_report/v2/abstractions/cmd_handler_base.py rename to src/servers/query_report/src/v2/abstractions/cmd_handler_base.py index cdca69808..8ef6d1571 100644 --- a/src/servers/query_report/v2/abstractions/cmd_handler_base.py +++ b/src/servers/query_report/src/v2/abstractions/cmd_handler_base.py @@ -1,7 +1,7 @@ import abc -from library.abstractions.handler import CmdHandlerBase as CHB -from servers.query_report.v2.abstractions.request_base import RequestBase -from servers.query_report.applications.client import Client +from library.src.abstractions.handler import CmdHandlerBase as CHB +from servers.query_report.src.v2.abstractions.request_base import RequestBase +from servers.query_report.src.applications.client import Client class CmdHandlerBase(CHB, abc.ABC): diff --git a/src/servers/query_report/v2/abstractions/request_base.py b/src/servers/query_report/src/v2/abstractions/request_base.py similarity index 64% rename from src/servers/query_report/v2/abstractions/request_base.py rename to src/servers/query_report/src/v2/abstractions/request_base.py index f48dfff2a..00638aac8 100644 --- a/src/servers/query_report/v2/abstractions/request_base.py +++ b/src/servers/query_report/src/v2/abstractions/request_base.py @@ -1,11 +1,11 @@ -import library.abstractions.contracts -from servers.query_report.exceptions.exceptions import QRException -from servers.query_report.v2.enums.general import RequestType +import library.src.abstractions.contracts +from servers.query_report.src.exceptions.exceptions import QRException +from servers.query_report.src.v2.enums.general import RequestType MAGIC_DATA = [0xFE, 0xFD] -class RequestBase(library.abstractions.contracts.RequestBase): +class RequestBase(library.src.abstractions.contracts.RequestBase): instant_key: int command_name: RequestType raw_request: bytes diff --git a/src/servers/query_report/src/v2/abstractions/response_base.py b/src/servers/query_report/src/v2/abstractions/response_base.py new file mode 100644 index 000000000..4add60583 --- /dev/null +++ b/src/servers/query_report/src/v2/abstractions/response_base.py @@ -0,0 +1,9 @@ +import abc +import library.src.abstractions.contracts +from servers.query_report.src.v2.contracts.requests import RequestBase +from servers.query_report.src.v2.contracts.results import ResultBase + + +class ResponseBase(library.src.abstractions.contracts.ResponseBase, abc.ABC): + _result: ResultBase + _request: RequestBase diff --git a/src/servers/query_report/src/v2/abstractions/result_base.py b/src/servers/query_report/src/v2/abstractions/result_base.py new file mode 100644 index 000000000..c3ef79e0c --- /dev/null +++ b/src/servers/query_report/src/v2/abstractions/result_base.py @@ -0,0 +1,7 @@ +import abc +import library.src.abstractions.contracts +from servers.query_report.src.v2.enums.general import PacketType + + +class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): + packet_type: PacketType = None diff --git a/src/servers/query_report/v2/aggregates/game_server_info_v2.py b/src/servers/query_report/src/v2/aggregates/game_server_info_v2.py similarity index 89% rename from src/servers/query_report/v2/aggregates/game_server_info_v2.py rename to src/servers/query_report/src/v2/aggregates/game_server_info_v2.py index 39a5e4ce9..182cfcac2 100644 --- a/src/servers/query_report/v2/aggregates/game_server_info_v2.py +++ b/src/servers/query_report/src/v2/aggregates/game_server_info_v2.py @@ -11,8 +11,8 @@ DictField, ) -from library.database.mongodb_orm import connect_to_db, get_ttl_param -from servers.query_report.v2.enums.general import GameServerStatus +from library.src.database.mongodb_orm import connect_to_db, get_ttl_param +from servers.query_report.src.v2.enums.general import GameServerStatus class GameServerInfoV2(Document): diff --git a/src/servers/query_report/v2/aggregates/natneg_cookie.py b/src/servers/query_report/src/v2/aggregates/natneg_cookie.py similarity index 100% rename from src/servers/query_report/v2/aggregates/natneg_cookie.py rename to src/servers/query_report/src/v2/aggregates/natneg_cookie.py diff --git a/src/servers/query_report/v2/applications/data.py b/src/servers/query_report/src/v2/applications/data.py similarity index 100% rename from src/servers/query_report/v2/applications/data.py rename to src/servers/query_report/src/v2/applications/data.py diff --git a/src/servers/query_report/v2/applications/switcher.py b/src/servers/query_report/src/v2/applications/switcher.py similarity index 74% rename from src/servers/query_report/v2/applications/switcher.py rename to src/servers/query_report/src/v2/applications/switcher.py index 20d4f002a..2a77ec83c 100644 --- a/src/servers/query_report/v2/applications/switcher.py +++ b/src/servers/query_report/src/v2/applications/switcher.py @@ -1,20 +1,20 @@ -from library.abstractions.switcher import SwitcherBase -from library.exceptions.error import UniSpyException -from servers.presence_connection_manager.contracts.requests.general import ( +from library.src.abstractions.switcher import SwitcherBase +from library.src.exceptions.error import UniSpyException +from servers.presence_connection_manager.src.contracts.requests.general import ( KeepAliveRequest, ) -from servers.query_report.applications.client import Client -from servers.query_report.v2.abstractions.cmd_handler_base import CmdHandlerBase +from servers.query_report.src.applications.client import Client +from servers.query_report.src.v2.abstractions.cmd_handler_base import CmdHandlerBase -from servers.query_report.v2.contracts.requests import ( +from servers.query_report.src.v2.contracts.requests import ( AvaliableRequest, ChallengeRequest, ClientMessageRequest, EchoRequest, HeartBeatRequest, ) -from servers.query_report.v2.enums.general import RequestType -from servers.query_report.v2.handlers.handlers import ( +from servers.query_report.src.v2.enums.general import RequestType +from servers.query_report.src.v2.handlers.handlers import ( AvailableHandler, ChallengeHanler, ClientMessageAckHandler, diff --git a/src/servers/query_report/v2/contracts/requests.py b/src/servers/query_report/src/v2/contracts/requests.py similarity index 94% rename from src/servers/query_report/v2/contracts/requests.py rename to src/servers/query_report/src/v2/contracts/requests.py index 80a9c72cf..8281d3576 100644 --- a/src/servers/query_report/v2/contracts/requests.py +++ b/src/servers/query_report/src/v2/contracts/requests.py @@ -1,9 +1,9 @@ from uuid import UUID -from library.extentions.encoding import get_string -from library.log.log_manager import LogWriter -from servers.query_report.exceptions.exceptions import QRException -from servers.query_report.v2.abstractions.request_base import RequestBase -from servers.query_report.v2.enums.general import GameServerStatus +from library.src.extentions.encoding import get_string +from library.src.log.log_manager import LogWriter +from servers.query_report.src.exceptions.exceptions import QRException +from servers.query_report.src.v2.abstractions.request_base import RequestBase +from servers.query_report.src.v2.enums.general import GameServerStatus PREFIX = bytes([0x09, 0x00, 0x00, 0x00, 0x00]) diff --git a/src/servers/query_report/v2/contracts/responses.py b/src/servers/query_report/src/v2/contracts/responses.py similarity index 85% rename from src/servers/query_report/v2/contracts/responses.py rename to src/servers/query_report/src/v2/contracts/responses.py index 44eaa0c2d..47a5c4b71 100644 --- a/src/servers/query_report/v2/contracts/responses.py +++ b/src/servers/query_report/src/v2/contracts/responses.py @@ -1,16 +1,16 @@ -from library.extentions.encoding import get_bytes -from servers.presence_connection_manager.contracts.requests.general import ( +from library.src.extentions.encoding import get_bytes +from servers.presence_connection_manager.src.contracts.requests.general import ( KeepAliveRequest, ) -from servers.query_report.v2.abstractions.response_base import ResponseBase -from servers.query_report.v2.contracts.requests import ( +from servers.query_report.src.v2.abstractions.response_base import ResponseBase +from servers.query_report.src.v2.contracts.requests import ( AvaliableRequest, ChallengeRequest, ClientMessageRequest, HeartBeatRequest, ) -from servers.query_report.v2.contracts.results import ChallengeResult, HeartBeatResult -from servers.query_report.v2.enums.general import ServerAvailability +from servers.query_report.src.v2.contracts.results import ChallengeResult, HeartBeatResult +from servers.query_report.src.v2.enums.general import ServerAvailability RESPONSE_PREFIX = bytes([0xFE, 0xFD, 0x09, 0x00, 0x00, 0x00]) diff --git a/src/servers/query_report/v2/contracts/results.py b/src/servers/query_report/src/v2/contracts/results.py similarity index 68% rename from src/servers/query_report/v2/contracts/results.py rename to src/servers/query_report/src/v2/contracts/results.py index cb4901230..54351073e 100644 --- a/src/servers/query_report/v2/contracts/results.py +++ b/src/servers/query_report/src/v2/contracts/results.py @@ -1,6 +1,6 @@ -from library.extentions.string_extentions import IPEndPoint -from servers.query_report.v2.abstractions.result_base import ResultBase -from servers.query_report.v2.enums.general import PacketType +from library.src.extentions.string_extentions import IPEndPoint +from servers.query_report.src.v2.abstractions.result_base import ResultBase +from servers.query_report.src.v2.enums.general import PacketType class ChallengeResult(ResultBase): diff --git a/src/servers/query_report/v2/enums/general.py b/src/servers/query_report/src/v2/enums/general.py similarity index 100% rename from src/servers/query_report/v2/enums/general.py rename to src/servers/query_report/src/v2/enums/general.py diff --git a/src/servers/query_report/v2/handlers/handlers.py b/src/servers/query_report/src/v2/handlers/handlers.py similarity index 84% rename from src/servers/query_report/v2/handlers/handlers.py rename to src/servers/query_report/src/v2/handlers/handlers.py index 2732cc801..ffe0d6f12 100644 --- a/src/servers/query_report/v2/handlers/handlers.py +++ b/src/servers/query_report/src/v2/handlers/handlers.py @@ -1,9 +1,9 @@ -from servers.presence_connection_manager.contracts.requests.general import ( +from servers.presence_connection_manager.src.contracts.requests.general import ( KeepAliveRequest, ) -from servers.query_report.applications.client import Client -from servers.query_report.v2.abstractions.cmd_handler_base import CmdHandlerBase -from servers.query_report.v2.contracts.requests import ( +from servers.query_report.src.applications.client import Client +from servers.query_report.src.v2.abstractions.cmd_handler_base import CmdHandlerBase +from servers.query_report.src.v2.contracts.requests import ( AvaliableRequest, ChallengeRequest, ClientMessageAckRequest, @@ -11,13 +11,13 @@ EchoRequest, HeartBeatRequest, ) -from servers.query_report.v2.contracts.responses import ( +from servers.query_report.src.v2.contracts.responses import ( AvaliableResponse, ChallengeResponse, ClientMessageResponse, HeartBeatResponse, ) -from servers.query_report.v2.contracts.results import ChallengeResult +from servers.query_report.src.v2.contracts.results import ChallengeResult class AvailableHandler(CmdHandlerBase): diff --git a/src/servers/query_report/v1/abstractions/handlers.py b/src/servers/query_report/v1/abstractions/handlers.py deleted file mode 100644 index 5a1595299..000000000 --- a/src/servers/query_report/v1/abstractions/handlers.py +++ /dev/null @@ -1,10 +0,0 @@ -import library.abstractions.handler -from servers.query_report.v1.abstractions.contracts import RequestBase -from servers.query_report.applications.client import Client - - -class CmdHandlerBase(library.abstractions.handler.CmdHandlerBase): - def __init__(self, client: Client, request: RequestBase) -> None: - assert issubclass(type(request), RequestBase) - assert isinstance(client, Client) - super().__init__(client, request) diff --git a/src/servers/query_report/v2/abstractions/response_base.py b/src/servers/query_report/v2/abstractions/response_base.py deleted file mode 100644 index 797f56d34..000000000 --- a/src/servers/query_report/v2/abstractions/response_base.py +++ /dev/null @@ -1,9 +0,0 @@ -import abc -import library.abstractions.contracts -from servers.query_report.v2.contracts.requests import RequestBase -from servers.query_report.v2.contracts.results import ResultBase - - -class ResponseBase(library.abstractions.contracts.ResponseBase, abc.ABC): - _result: ResultBase - _request: RequestBase diff --git a/src/servers/query_report/v2/abstractions/result_base.py b/src/servers/query_report/v2/abstractions/result_base.py deleted file mode 100644 index c911bbadd..000000000 --- a/src/servers/query_report/v2/abstractions/result_base.py +++ /dev/null @@ -1,7 +0,0 @@ -import abc -import library.abstractions.contracts -from servers.query_report.v2.enums.general import PacketType - - -class ResultBase(library.abstractions.contracts.ResultBase, abc.ABC): - packet_type: PacketType = None diff --git a/src/servers/server_browser/exceptions/general.py b/src/servers/server_browser/src/exceptions/general.py similarity index 69% rename from src/servers/server_browser/exceptions/general.py rename to src/servers/server_browser/src/exceptions/general.py index 16ac04506..1dcab6e3e 100644 --- a/src/servers/server_browser/exceptions/general.py +++ b/src/servers/server_browser/src/exceptions/general.py @@ -1,4 +1,4 @@ -from library.exceptions.error import UniSpyException +from library.src.exceptions.error import UniSpyException class ServerBrowserException(UniSpyException): diff --git a/src/servers/presence_search_player/contracts/__init__.py b/src/servers/server_browser/src/v2/__init__.py similarity index 100% rename from src/servers/presence_search_player/contracts/__init__.py rename to src/servers/server_browser/src/v2/__init__.py diff --git a/src/servers/query_report/v1/__init__.py b/src/servers/server_browser/src/v2/abstractions/__init__.py similarity index 100% rename from src/servers/query_report/v1/__init__.py rename to src/servers/server_browser/src/v2/abstractions/__init__.py diff --git a/src/servers/server_browser/v2/abstractions/contracts.py b/src/servers/server_browser/src/v2/abstractions/contracts.py similarity index 86% rename from src/servers/server_browser/v2/abstractions/contracts.py rename to src/servers/server_browser/src/v2/abstractions/contracts.py index d590bc770..70804174e 100644 --- a/src/servers/server_browser/v2/abstractions/contracts.py +++ b/src/servers/server_browser/src/v2/abstractions/contracts.py @@ -1,13 +1,13 @@ from socket import inet_ntoa from typing import List, Optional -import library.abstractions.contracts +import library.src.abstractions.contracts import abc -from library.extentions.encoding import get_bytes -from servers.server_browser.v2.aggregations.encryption import SERVER_CHALLENGE -from servers.server_browser.v2.aggregations.string_flags import STRING_SPLITER -from servers.server_browser.v2.contracts.results import AdHocResult -from servers.server_browser.v2.enums.general import ( +from library.src.extentions.encoding import get_bytes +from servers.server_browser.src.v2.aggregations.encryption import SERVER_CHALLENGE +from servers.server_browser.src.v2.aggregations.string_flags import STRING_SPLITER +from servers.server_browser.src.v2.contracts.results import AdHocResult +from servers.server_browser.src.v2.enums.general import ( DataKeyType, GameServerFlags, RequestType, @@ -17,7 +17,7 @@ QUERY_REPORT_DEFAULT_PORT = 6500 -class RequestBase(library.abstractions.contracts.RequestBase, abc.ABC): +class RequestBase(library.src.abstractions.contracts.RequestBase, abc.ABC): request_length: int raw_request: bytes command_name: RequestType @@ -31,11 +31,11 @@ def parse(self) -> None: self.command_name = RequestType(self.raw_request[2]) -class ResultBase(library.abstractions.contracts.ResultBase, abc.ABC): +class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): pass -class ResponseBase(library.abstractions.contracts.ResponseBase, abc.ABC): +class ResponseBase(library.src.abstractions.contracts.ResponseBase, abc.ABC): _request: RequestBase _result: ResultBase sending_buffer: bytes diff --git a/src/servers/server_browser/v2/abstractions/handlers.py b/src/servers/server_browser/src/v2/abstractions/handlers.py similarity index 82% rename from src/servers/server_browser/v2/abstractions/handlers.py rename to src/servers/server_browser/src/v2/abstractions/handlers.py index 20091fd7a..37ba3f541 100644 --- a/src/servers/server_browser/v2/abstractions/handlers.py +++ b/src/servers/server_browser/src/v2/abstractions/handlers.py @@ -1,14 +1,14 @@ -from library.abstractions.handler import CmdHandlerBase as CMB +from library.src.abstractions.handler import CmdHandlerBase as CMB import abc -from servers.server_browser.v2.abstractions.contracts import ( +from servers.server_browser.src.v2.abstractions.contracts import ( ServerListUpdateOptionRequestBase, ServerListUpdateOptionResponseBase, ServerListUpdateOptionResultBase, ) -from servers.server_browser.v2.aggregations.encryption import EnctypeX -from servers.server_browser.v2.applications.client import Client -from servers.server_browser.v2.abstractions.contracts import ( +from servers.server_browser.src.v2.aggregations.encryption import EnctypeX +from servers.server_browser.src.v2.applications.client import Client +from servers.server_browser.src.v2.abstractions.contracts import ( RequestBase, ResultBase, ResponseBase, diff --git a/src/servers/server_browser/v2/aggregations/encryption.py b/src/servers/server_browser/src/v2/aggregations/encryption.py similarity index 98% rename from src/servers/server_browser/v2/aggregations/encryption.py rename to src/servers/server_browser/src/v2/aggregations/encryption.py index d964e4e61..b0f83014c 100644 --- a/src/servers/server_browser/v2/aggregations/encryption.py +++ b/src/servers/server_browser/src/v2/aggregations/encryption.py @@ -1,4 +1,4 @@ -from library.abstractions.enctypt_base import EncryptBase +from library.src.abstractions.enctypt_base import EncryptBase SERVER_CHALLENGE = "0000000000" diff --git a/src/servers/server_browser/v2/aggregations/server_info_builder.py b/src/servers/server_browser/src/v2/aggregations/server_info_builder.py similarity index 91% rename from src/servers/server_browser/v2/aggregations/server_info_builder.py rename to src/servers/server_browser/src/v2/aggregations/server_info_builder.py index 8fbc16dd1..42db03743 100644 --- a/src/servers/server_browser/v2/aggregations/server_info_builder.py +++ b/src/servers/server_browser/src/v2/aggregations/server_info_builder.py @@ -1,10 +1,10 @@ from socket import inet_aton -from servers.query_report.aggregates.game_server_info import GameServerInfo +from servers.query_report.src.aggregates.game_server_info import GameServerInfo -from servers.query_report.applications.data import get_all_groups -from servers.server_browser.v2.abstractions.contracts import QUERY_REPORT_DEFAULT_PORT -from servers.server_browser.v2.enums.general import GameServerFlags +from servers.query_report.src.applications.data import get_all_groups +from servers.server_browser.src.v2.abstractions.contracts import QUERY_REPORT_DEFAULT_PORT +from servers.server_browser.src.v2.enums.general import GameServerFlags PEER_GROUP_LIST = get_all_groups() diff --git a/src/servers/server_browser/v2/aggregations/string_flags.py b/src/servers/server_browser/src/v2/aggregations/string_flags.py similarity index 100% rename from src/servers/server_browser/v2/aggregations/string_flags.py rename to src/servers/server_browser/src/v2/aggregations/string_flags.py diff --git a/src/servers/query_report/v1/abstractions/__init__.py b/src/servers/server_browser/src/v2/applications/__init__.py similarity index 100% rename from src/servers/query_report/v1/abstractions/__init__.py rename to src/servers/server_browser/src/v2/applications/__init__.py diff --git a/src/servers/server_browser/v2/applications/client.py b/src/servers/server_browser/src/v2/applications/client.py similarity index 65% rename from src/servers/server_browser/v2/applications/client.py rename to src/servers/server_browser/src/v2/applications/client.py index 33d8d36cf..beb74bdcc 100644 --- a/src/servers/server_browser/v2/applications/client.py +++ b/src/servers/server_browser/src/v2/applications/client.py @@ -1,5 +1,5 @@ -from library.abstractions.client import ClientBase, ClientInfoBase -from servers.server_browser.v2.enums.general import ServerListUpdateOption +from library.src.abstractions.client import ClientBase, ClientInfoBase +from servers.server_browser.src.v2.enums.general import ServerListUpdateOption class ClientInfo(ClientInfoBase): diff --git a/src/servers/server_browser/v2/__init__.py b/src/servers/server_browser/src/v2/contracts/__init__.py similarity index 100% rename from src/servers/server_browser/v2/__init__.py rename to src/servers/server_browser/src/v2/contracts/__init__.py diff --git a/src/servers/server_browser/v2/contracts/requests.py b/src/servers/server_browser/src/v2/contracts/requests.py similarity index 93% rename from src/servers/server_browser/v2/contracts/requests.py rename to src/servers/server_browser/src/v2/contracts/requests.py index a558f3993..d0dcf7263 100644 --- a/src/servers/server_browser/v2/contracts/requests.py +++ b/src/servers/server_browser/src/v2/contracts/requests.py @@ -1,9 +1,9 @@ from socket import inet_ntoa -from servers.server_browser.v2.abstractions.contracts import ( +from servers.server_browser.src.v2.abstractions.contracts import ( AdHocRequestBase, ServerListUpdateOptionRequestBase, ) -from servers.server_browser.v2.enums.general import RequestType, ServerListUpdateOption +from servers.server_browser.src.v2.enums.general import RequestType, ServerListUpdateOption class ServerListRequest(ServerListUpdateOptionRequestBase): diff --git a/src/servers/server_browser/v2/contracts/responses.py b/src/servers/server_browser/src/v2/contracts/responses.py similarity index 89% rename from src/servers/server_browser/v2/contracts/responses.py rename to src/servers/server_browser/src/v2/contracts/responses.py index e4012905f..41c05c231 100644 --- a/src/servers/server_browser/v2/contracts/responses.py +++ b/src/servers/server_browser/src/v2/contracts/responses.py @@ -1,20 +1,20 @@ -from library.extentions.encoding import get_bytes -from servers.server_browser.v2.abstractions.contracts import ( +from library.src.extentions.encoding import get_bytes +from servers.server_browser.src.v2.abstractions.contracts import ( AdHocResponseBase, ServerListUpdateOptionResponseBase, ) -from servers.server_browser.v2.aggregations.server_info_builder import ( +from servers.server_browser.src.v2.aggregations.server_info_builder import ( build_server_info_header, ) -from servers.server_browser.v2.aggregations.string_flags import * -from servers.server_browser.v2.contracts.requests import ServerListRequest -from servers.server_browser.v2.contracts.results import ( +from servers.server_browser.src.v2.aggregations.string_flags import * +from servers.server_browser.src.v2.contracts.requests import ServerListRequest +from servers.server_browser.src.v2.contracts.results import ( AdHocResult, P2PGroupRoomListResult, ServerMainListResult, ) -from servers.server_browser.v2.enums.general import GameServerFlags, ResponseType +from servers.server_browser.src.v2.enums.general import GameServerFlags, ResponseType class DeleteServerInfoResponse(AdHocResponseBase): diff --git a/src/servers/server_browser/v2/contracts/results.py b/src/servers/server_browser/src/v2/contracts/results.py similarity index 58% rename from src/servers/server_browser/v2/contracts/results.py rename to src/servers/server_browser/src/v2/contracts/results.py index 46008080c..e51763289 100644 --- a/src/servers/server_browser/v2/contracts/results.py +++ b/src/servers/server_browser/src/v2/contracts/results.py @@ -1,6 +1,6 @@ -from servers.query_report.aggregates.game_server_info import GameServerInfo -from servers.query_report.aggregates.peer_room_info import PeerRoomInfo -from servers.server_browser.v2.abstractions.contracts import ( +from servers.query_report.src.aggregates.game_server_info import GameServerInfo +from servers.query_report.src.aggregates.peer_room_info import PeerRoomInfo +from servers.server_browser.src.v2.abstractions.contracts import ( ResultBase, ServerListUpdateOptionResultBase, ) diff --git a/src/servers/server_browser/v2/enums/general.py b/src/servers/server_browser/src/v2/enums/general.py similarity index 100% rename from src/servers/server_browser/v2/enums/general.py rename to src/servers/server_browser/src/v2/enums/general.py diff --git a/src/servers/server_browser/v2/handlers/handlers.py b/src/servers/server_browser/src/v2/handlers/handlers.py similarity index 85% rename from src/servers/server_browser/v2/handlers/handlers.py rename to src/servers/server_browser/src/v2/handlers/handlers.py index a52047093..b2c82df15 100644 --- a/src/servers/server_browser/v2/handlers/handlers.py +++ b/src/servers/server_browser/src/v2/handlers/handlers.py @@ -1,28 +1,28 @@ from concurrent.futures import ProcessPoolExecutor -from servers.query_report.aggregates.game_server_info import GameServerInfo -from servers.query_report.v2.contracts.requests import ClientMessageRequest -from servers.query_report.v2.enums.general import GameServerStatus -from servers.server_browser.exceptions.general import ServerBrowserException -from servers.server_browser.v2.contracts.requests import ( +from servers.query_report.src.aggregates.game_server_info import GameServerInfo +from servers.query_report.src.v2.contracts.requests import ClientMessageRequest +from servers.query_report.src.v2.enums.general import GameServerStatus +from servers.server_browser.src.exceptions.general import ServerBrowserException +from servers.server_browser.src.v2.contracts.requests import ( SendMessageRequest, ServerInfoRequest, ServerListRequest, ) -from servers.server_browser.v2.contracts.responses import ( +from servers.server_browser.src.v2.contracts.responses import ( DeleteServerInfoResponse, P2PGroupRoomListResponse, ServerMainListResponse, ServerNetworkInfoListResponse, UpdateServerInfoResponse, ) -from servers.server_browser.v2.contracts.results import ( +from servers.server_browser.src.v2.contracts.results import ( AdHocResult, ServerMainListResult, ) -from servers.server_browser.v2.enums.general import RequestType, ServerListUpdateOption -from servers.server_browser.v2.abstractions.handlers import CmdHandlerBase +from servers.server_browser.src.v2.enums.general import RequestType, ServerListUpdateOption +from servers.server_browser.src.v2.abstractions.handlers import CmdHandlerBase -from servers.server_browser.v2.applications.client import Client +from servers.server_browser.src.v2.applications.client import Client class AdHocHandler(CmdHandlerBase): diff --git a/src/servers/webservices/exceptions/general.py b/src/servers/webservices/exceptions/general.py deleted file mode 100644 index f1c74eb6d..000000000 --- a/src/servers/webservices/exceptions/general.py +++ /dev/null @@ -1,6 +0,0 @@ -from library.exceptions.error import UniSpyException - - -class WebExceptions(UniSpyException): - pass - diff --git a/src/servers/webservices/modules/auth/exceptions/general.py b/src/servers/webservices/modules/auth/exceptions/general.py deleted file mode 100644 index 993184499..000000000 --- a/src/servers/webservices/modules/auth/exceptions/general.py +++ /dev/null @@ -1,5 +0,0 @@ -from servers.webservices.exceptions.general import WebExceptions - - -class AuthException(WebExceptions): - pass diff --git a/src/servers/webservices/modules/patching_and_tracking/__init__.py b/src/servers/webservices/modules/patching_and_tracking/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/webservices/modules/racing/__init__.py b/src/servers/webservices/modules/racing/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/webservices/modules/sake/__init__.py b/src/servers/webservices/modules/sake/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/webservices/modules/sake/contracts/responses.py b/src/servers/webservices/modules/sake/contracts/responses.py deleted file mode 100644 index d245a19a4..000000000 --- a/src/servers/webservices/modules/sake/contracts/responses.py +++ /dev/null @@ -1,26 +0,0 @@ -from servers.webservices.modules.sake.abstractions.contracts import ResponseBase -from servers.webservices.modules.sake.contracts.requests import CreateRecordRequest -from servers.webservices.modules.sake.contracts.results import CreateRecordResult - - -class CreateRecordResponse(ResponseBase): - _result: CreateRecordResult - _request: CreateRecordRequest - - def build(self) -> None: - self._content.add("CreateRecordResult") - self._content.add("tableid", self._result.table_id) - self._content.add("recordid", self._result.record_id) - - for field in self._result.fields: - self._content.add("fields", field) - - super().build() - - -class GetMyRecordResponse(ResponseBase): - pass - - -class SearchForRecordsResponse(ResponseBase): - pass \ No newline at end of file diff --git a/src/servers/webservices/modules/sake/exceptions/general.py b/src/servers/webservices/modules/sake/exceptions/general.py deleted file mode 100644 index f64aab3da..000000000 --- a/src/servers/webservices/modules/sake/exceptions/general.py +++ /dev/null @@ -1,5 +0,0 @@ -from servers.webservices.exceptions.general import WebExceptions - - -class SakeException(WebExceptions): - pass diff --git a/src/servers/webservices/abstractions/contracts.py b/src/servers/webservices/src/abstractions/contracts.py similarity index 86% rename from src/servers/webservices/abstractions/contracts.py rename to src/servers/webservices/src/abstractions/contracts.py index a7f01ab66..3490c6f51 100644 --- a/src/servers/webservices/abstractions/contracts.py +++ b/src/servers/webservices/src/abstractions/contracts.py @@ -1,8 +1,8 @@ -import library.abstractions.contracts as lib +import library.src.abstractions.contracts as lib import xml.etree.ElementTree as ET -from library.exceptions.error import UniSpyException -from servers.webservices.aggregations.soap_envelop import SoapEnvelop +from library.src.exceptions.error import UniSpyException +from servers.webservices.src.aggregations.soap_envelop import SoapEnvelop class RequestBase(lib.RequestBase): diff --git a/src/servers/webservices/abstractions/handler.py b/src/servers/webservices/src/abstractions/handler.py similarity index 60% rename from src/servers/webservices/abstractions/handler.py rename to src/servers/webservices/src/abstractions/handler.py index ab37498a4..0be598b93 100644 --- a/src/servers/webservices/abstractions/handler.py +++ b/src/servers/webservices/src/abstractions/handler.py @@ -1,6 +1,6 @@ -import library.abstractions.handler as lib -from servers.webservices.applications.client import Client -from servers.webservices.abstractions.contracts import RequestBase +import library.src.abstractions.handler as lib +from servers.webservices.src.applications.client import Client +from servers.webservices.src.abstractions.contracts import RequestBase class CmdHandlerBase(lib.CmdHandlerBase): diff --git a/src/servers/webservices/aggregations/soap_envelop.py b/src/servers/webservices/src/aggregations/soap_envelop.py similarity index 100% rename from src/servers/webservices/aggregations/soap_envelop.py rename to src/servers/webservices/src/aggregations/soap_envelop.py diff --git a/src/servers/webservices/applications/client.py b/src/servers/webservices/src/applications/client.py similarity index 97% rename from src/servers/webservices/applications/client.py rename to src/servers/webservices/src/applications/client.py index 60d648f07..3d6777da9 100644 --- a/src/servers/webservices/applications/client.py +++ b/src/servers/webservices/src/applications/client.py @@ -1,4 +1,4 @@ -from library.abstractions.client import ClientBase, ClientInfoBase +from library.src.abstractions.client import ClientBase, ClientInfoBase class ClientInfo(ClientInfoBase): diff --git a/src/servers/webservices/src/exceptions/general.py b/src/servers/webservices/src/exceptions/general.py new file mode 100644 index 000000000..5922c267b --- /dev/null +++ b/src/servers/webservices/src/exceptions/general.py @@ -0,0 +1,6 @@ +from library.src.exceptions.error import UniSpyException + + +class WebExceptions(UniSpyException): + pass + diff --git a/src/servers/server_browser/v2/abstractions/__init__.py b/src/servers/webservices/src/modules/__init__.py similarity index 100% rename from src/servers/server_browser/v2/abstractions/__init__.py rename to src/servers/webservices/src/modules/__init__.py diff --git a/src/servers/webservices/modules/altas/___init__.py b/src/servers/webservices/src/modules/altas/___init__.py similarity index 100% rename from src/servers/webservices/modules/altas/___init__.py rename to src/servers/webservices/src/modules/altas/___init__.py diff --git a/src/servers/webservices/modules/auth/___init__.py b/src/servers/webservices/src/modules/auth/___init__.py similarity index 100% rename from src/servers/webservices/modules/auth/___init__.py rename to src/servers/webservices/src/modules/auth/___init__.py diff --git a/src/servers/webservices/modules/auth/abstractions/___init__.py b/src/servers/webservices/src/modules/auth/abstractions/___init__.py similarity index 100% rename from src/servers/webservices/modules/auth/abstractions/___init__.py rename to src/servers/webservices/src/modules/auth/abstractions/___init__.py diff --git a/src/servers/webservices/modules/auth/abstractions/contracts.py b/src/servers/webservices/src/modules/auth/abstractions/general.py similarity index 94% rename from src/servers/webservices/modules/auth/abstractions/contracts.py rename to src/servers/webservices/src/modules/auth/abstractions/general.py index 114bc24bf..ef4e64c2c 100644 --- a/src/servers/webservices/modules/auth/abstractions/contracts.py +++ b/src/servers/webservices/src/modules/auth/abstractions/general.py @@ -1,8 +1,8 @@ import hashlib -import servers.webservices.abstractions.contracts as lib -from servers.webservices.aggregations.soap_envelop import SoapEnvelop -from servers.webservices.applications.client import ClientInfo -from servers.webservices.modules.auth.exceptions.general import AuthException +import servers.webservices.src.abstractions.contracts as lib +from servers.webservices.src.aggregations.soap_envelop import SoapEnvelop +from servers.webservices.src.applications.client import ClientInfo +from servers.webservices.src.modules.auth.exceptions.general import AuthException import datetime NAMESPACE = "http://gamespy.net/AuthService/" diff --git a/src/servers/webservices/modules/auth/contracts/requests.py b/src/servers/webservices/src/modules/auth/contracts/requests.py similarity index 96% rename from src/servers/webservices/modules/auth/contracts/requests.py rename to src/servers/webservices/src/modules/auth/contracts/requests.py index 286c7dd4d..a8cef4925 100644 --- a/src/servers/webservices/modules/auth/contracts/requests.py +++ b/src/servers/webservices/src/modules/auth/contracts/requests.py @@ -1,8 +1,8 @@ -from servers.webservices.modules.auth.abstractions.contracts import ( +from servers.webservices.src.modules.auth.abstractions.general import ( NAMESPACE, LoginRequestBase, ) -from servers.webservices.modules.auth.exceptions.general import AuthException +from servers.webservices.src.modules.auth.exceptions.general import AuthException class LoginProfileRequest(LoginRequestBase): diff --git a/src/servers/webservices/modules/auth/contracts/responses.py b/src/servers/webservices/src/modules/auth/contracts/responses.py similarity index 89% rename from src/servers/webservices/modules/auth/contracts/responses.py rename to src/servers/webservices/src/modules/auth/contracts/responses.py index 12e168e22..90584c7a6 100644 --- a/src/servers/webservices/modules/auth/contracts/responses.py +++ b/src/servers/webservices/src/modules/auth/contracts/responses.py @@ -1,9 +1,9 @@ -from servers.webservices.modules.auth.abstractions.contracts import LoginResponseBase -from servers.webservices.modules.auth.contracts.requests import ( +from servers.webservices.src.modules.auth.abstractions.general import LoginResponseBase +from servers.webservices.src.modules.auth.contracts.requests import ( LoginProfileRequest, LoginProfileWithGameIdRequest, ) -from servers.webservices.modules.auth.contracts.results import ( +from servers.webservices.src.modules.auth.contracts.results import ( LoginProfileResult, LoginPs3CertResult, ) diff --git a/src/servers/webservices/modules/auth/contracts/results.py b/src/servers/webservices/src/modules/auth/contracts/results.py similarity index 75% rename from src/servers/webservices/modules/auth/contracts/results.py rename to src/servers/webservices/src/modules/auth/contracts/results.py index fbd63099d..6d1181f16 100644 --- a/src/servers/webservices/modules/auth/contracts/results.py +++ b/src/servers/webservices/src/modules/auth/contracts/results.py @@ -1,4 +1,4 @@ -from servers.webservices.modules.auth.abstractions.contracts import LoginResultBase +from servers.webservices.src.modules.auth.abstractions.general import LoginResultBase class LoginProfileResult(LoginResultBase): diff --git a/src/servers/webservices/src/modules/auth/exceptions/general.py b/src/servers/webservices/src/modules/auth/exceptions/general.py new file mode 100644 index 000000000..16994567a --- /dev/null +++ b/src/servers/webservices/src/modules/auth/exceptions/general.py @@ -0,0 +1,5 @@ +from servers.webservices.src.exceptions.general import WebExceptions + + +class AuthException(WebExceptions): + pass diff --git a/src/servers/webservices/modules/auth/handlers/general.py b/src/servers/webservices/src/modules/auth/handlers/general.py similarity index 85% rename from src/servers/webservices/modules/auth/handlers/general.py rename to src/servers/webservices/src/modules/auth/handlers/general.py index 12e728ff7..6aaaded33 100644 --- a/src/servers/webservices/modules/auth/handlers/general.py +++ b/src/servers/webservices/src/modules/auth/handlers/general.py @@ -1,6 +1,6 @@ -from servers.webservices.abstractions.handler import CmdHandlerBase -from servers.webservices.modules.auth.abstractions.contracts import LoginResultBase -from servers.webservices.modules.auth.contracts.requests import ( +from servers.webservices.src.abstractions.handler import CmdHandlerBase +from servers.webservices.src.modules.auth.abstractions.general import LoginResultBase +from servers.webservices.src.modules.auth.contracts.requests import ( LoginProfileRequest, LoginProfileWithGameIdRequest, LoginPs3CertRequest, @@ -10,7 +10,7 @@ LoginUniqueNickRequest, LoginUniqueNickWithGameIdRequest, ) -from servers.webservices.modules.auth.contracts.responses import ( +from servers.webservices.src.modules.auth.contracts.responses import ( LoginProfileResponse, LoginProfileWithGameIdResponse, LoginRemoteAuthResponse, @@ -18,7 +18,7 @@ LoginUniqueNickResponse, LoginUniqueNickWithGameIdResponse, ) -from servers.webservices.modules.auth.contracts.results import ( +from servers.webservices.src.modules.auth.contracts.results import ( LoginProfileResult, LoginPs3CertResult, LoginRemoteAuthResult, diff --git a/src/servers/server_browser/v2/applications/__init__.py b/src/servers/webservices/src/modules/direct2game/__init__.py similarity index 100% rename from src/servers/server_browser/v2/applications/__init__.py rename to src/servers/webservices/src/modules/direct2game/__init__.py diff --git a/src/servers/server_browser/v2/contracts/__init__.py b/src/servers/webservices/src/modules/direct2game/abstractions/__init__.py similarity index 100% rename from src/servers/server_browser/v2/contracts/__init__.py rename to src/servers/webservices/src/modules/direct2game/abstractions/__init__.py diff --git a/src/servers/webservices/modules/direct2game/abstractions/contracts.py b/src/servers/webservices/src/modules/direct2game/abstractions/contracts.py similarity index 63% rename from src/servers/webservices/modules/direct2game/abstractions/contracts.py rename to src/servers/webservices/src/modules/direct2game/abstractions/contracts.py index f372548e8..efae5a815 100644 --- a/src/servers/webservices/modules/direct2game/abstractions/contracts.py +++ b/src/servers/webservices/src/modules/direct2game/abstractions/contracts.py @@ -1,5 +1,5 @@ -import servers.webservices.abstractions.contracts as lib -from servers.webservices.aggregations.soap_envelop import SoapEnvelop +import servers.webservices.src.abstractions.contracts as lib +from servers.webservices.src.aggregations.soap_envelop import SoapEnvelop NAMESPACE = "http://gamespy.net/commerce/" diff --git a/src/servers/webservices/modules/direct2game/abstractions/handler.py b/src/servers/webservices/src/modules/direct2game/abstractions/handler.py similarity index 100% rename from src/servers/webservices/modules/direct2game/abstractions/handler.py rename to src/servers/webservices/src/modules/direct2game/abstractions/handler.py diff --git a/src/servers/webservices/modules/__init__.py b/src/servers/webservices/src/modules/direct2game/contracts/__init__.py similarity index 100% rename from src/servers/webservices/modules/__init__.py rename to src/servers/webservices/src/modules/direct2game/contracts/__init__.py diff --git a/src/servers/webservices/modules/direct2game/contracts/requests.py b/src/servers/webservices/src/modules/direct2game/contracts/requests.py similarity index 93% rename from src/servers/webservices/modules/direct2game/contracts/requests.py rename to src/servers/webservices/src/modules/direct2game/contracts/requests.py index cf6aa1a75..8740957e8 100644 --- a/src/servers/webservices/modules/direct2game/contracts/requests.py +++ b/src/servers/webservices/src/modules/direct2game/contracts/requests.py @@ -1,5 +1,5 @@ -from servers.webservices.exceptions.general import WebExceptions -from servers.webservices.modules.direct2game.abstractions.contracts import ( +from servers.webservices.src.exceptions.general import WebExceptions +from servers.webservices.src.modules.direct2game.abstractions.contracts import ( NAMESPACE, RequestBase, ) diff --git a/src/servers/webservices/modules/direct2game/contracts/responses.py b/src/servers/webservices/src/modules/direct2game/contracts/responses.py similarity index 86% rename from src/servers/webservices/modules/direct2game/contracts/responses.py rename to src/servers/webservices/src/modules/direct2game/contracts/responses.py index 140c54646..240e9f627 100644 --- a/src/servers/webservices/modules/direct2game/contracts/responses.py +++ b/src/servers/webservices/src/modules/direct2game/contracts/responses.py @@ -1,5 +1,5 @@ -from servers.webservices.modules.direct2game.abstractions.contracts import ResponseBase -from servers.webservices.modules.direct2game.contracts.results import ( +from servers.webservices.src.modules.direct2game.abstractions.contracts import ResponseBase +from servers.webservices.src.modules.direct2game.contracts.results import ( GetPurchaseHistoryResult, GetStoreAvailabilityResult, ) diff --git a/src/servers/webservices/modules/direct2game/contracts/results.py b/src/servers/webservices/src/modules/direct2game/contracts/results.py similarity index 80% rename from src/servers/webservices/modules/direct2game/contracts/results.py rename to src/servers/webservices/src/modules/direct2game/contracts/results.py index c23f1e3bb..7f4ce7b60 100644 --- a/src/servers/webservices/modules/direct2game/contracts/results.py +++ b/src/servers/webservices/src/modules/direct2game/contracts/results.py @@ -1,6 +1,6 @@ from enum import IntEnum -from servers.webservices.modules.direct2game.abstractions.contracts import ResultBase +from servers.webservices.src.modules.direct2game.abstractions.contracts import ResultBase class GetPurchaseHistoryResult(ResultBase): diff --git a/src/servers/webservices/modules/direct2game/handlers/general.py b/src/servers/webservices/src/modules/direct2game/handlers/general.py similarity index 71% rename from src/servers/webservices/modules/direct2game/handlers/general.py rename to src/servers/webservices/src/modules/direct2game/handlers/general.py index 0b480eda9..e35ecd633 100644 --- a/src/servers/webservices/modules/direct2game/handlers/general.py +++ b/src/servers/webservices/src/modules/direct2game/handlers/general.py @@ -1,15 +1,15 @@ -from servers.webservices.abstractions.contracts import RequestBase -from servers.webservices.abstractions.handler import CmdHandlerBase -from servers.webservices.applications.client import Client -from servers.webservices.modules.direct2game.contracts.requests import ( +from servers.webservices.src.abstractions.contracts import RequestBase +from servers.webservices.src.abstractions.handler import CmdHandlerBase +from servers.webservices.src.applications.client import Client +from servers.webservices.src.modules.direct2game.contracts.requests import ( GetPurchaseHistoryRequest, GetStoreAvailabilityRequest, ) -from servers.webservices.modules.direct2game.contracts.responses import ( +from servers.webservices.src.modules.direct2game.contracts.responses import ( GetPurchaseHistoryResponse, GetStoreAvailabilityResponse, ) -from servers.webservices.modules.direct2game.contracts.results import ( +from servers.webservices.src.modules.direct2game.contracts.results import ( GetPurchaseHistoryResult, GetStoreAvailabilityResult, ) diff --git a/src/servers/webservices/modules/direct2game/__init__.py b/src/servers/webservices/src/modules/in_game_ad/__init__.py similarity index 100% rename from src/servers/webservices/modules/direct2game/__init__.py rename to src/servers/webservices/src/modules/in_game_ad/__init__.py diff --git a/src/servers/webservices/modules/direct2game/abstractions/__init__.py b/src/servers/webservices/src/modules/patching_and_tracking/__init__.py similarity index 100% rename from src/servers/webservices/modules/direct2game/abstractions/__init__.py rename to src/servers/webservices/src/modules/patching_and_tracking/__init__.py diff --git a/src/servers/webservices/modules/direct2game/contracts/__init__.py b/src/servers/webservices/src/modules/racing/__init__.py similarity index 100% rename from src/servers/webservices/modules/direct2game/contracts/__init__.py rename to src/servers/webservices/src/modules/racing/__init__.py diff --git a/src/servers/webservices/modules/in_game_ad/__init__.py b/src/servers/webservices/src/modules/sake/__init__.py similarity index 100% rename from src/servers/webservices/modules/in_game_ad/__init__.py rename to src/servers/webservices/src/modules/sake/__init__.py diff --git a/src/servers/webservices/modules/sake/abstractions/contracts.py b/src/servers/webservices/src/modules/sake/abstractions/general.py similarity index 57% rename from src/servers/webservices/modules/sake/abstractions/contracts.py rename to src/servers/webservices/src/modules/sake/abstractions/general.py index 53c956b16..044d2ea3e 100644 --- a/src/servers/webservices/modules/sake/abstractions/contracts.py +++ b/src/servers/webservices/src/modules/sake/abstractions/general.py @@ -1,6 +1,7 @@ -import servers.webservices.abstractions.contracts as lib -from servers.webservices.aggregations.soap_envelop import SoapEnvelop -from servers.webservices.modules.sake.exceptions.general import SakeException +import servers.webservices.src.abstractions.handler as h +import servers.webservices.src.abstractions.contracts as lib +from servers.webservices.src.aggregations.soap_envelop import SoapEnvelop +from servers.webservices.src.modules.sake.exceptions.general import SakeException NAMESPACE = "http://gamespy.net/sake" @@ -18,15 +19,18 @@ def parse(self) -> None: raise SakeException("gameid is missing from the request.") self.game_id = int(game_id) - self.secret_key = self._content_element.find(f".//{{{NAMESPACE}}}secretKey") + self.secret_key = self._content_element.find( + f".//{{{NAMESPACE}}}secretKey") if self.secret_key is None: raise SakeException("secretkey id is missing from the request.") - self.login_ticket = self._content_element.find(f".//{{{NAMESPACE}}}loginTicket") + self.login_ticket = self._content_element.find( + f".//{{{NAMESPACE}}}loginTicket") if self.login_ticket is None: raise SakeException("loginTicket is missing from the request.") - self.table_id = self._content_element.find(f".//{{{NAMESPACE}}}tableid") + self.table_id = self._content_element.find( + f".//{{{NAMESPACE}}}tableid") if self.table_id is None: raise SakeException("tableid is missing from the request.") @@ -38,4 +42,9 @@ class ResultBase(lib.ResultBase): class ResponseBase(lib.ResponseBase): def __init__(self, request: RequestBase, result: ResultBase) -> None: self._content = SoapEnvelop(NAMESPACE) - super().__init__(request, result) \ No newline at end of file + super().__init__(request, result) + + +class CmdHandlerBase(h.CmdHandlerBase): + _request: "RequestBase" + _result: "ResultBase" diff --git a/src/servers/webservices/modules/sake/contracts/requests.py b/src/servers/webservices/src/modules/sake/contracts/requests.py similarity index 96% rename from src/servers/webservices/modules/sake/contracts/requests.py rename to src/servers/webservices/src/modules/sake/contracts/requests.py index 7e236a552..4db7fa916 100644 --- a/src/servers/webservices/modules/sake/contracts/requests.py +++ b/src/servers/webservices/src/modules/sake/contracts/requests.py @@ -1,11 +1,11 @@ from typing import OrderedDict -from servers.webservices.modules.sake.abstractions.contracts import ( +from servers.webservices.src.modules.sake.abstractions.general import ( RequestBase, NAMESPACE, ) import xmltodict -from servers.webservices.modules.sake.exceptions.general import SakeException +from servers.webservices.src.modules.sake.exceptions.general import SakeException class CreateRecordRequest(RequestBase): @@ -33,7 +33,7 @@ def parse(self) -> None: self.record_id = int(record_id) -class GetMyRecordRequest(RequestBase): +class GetMyRecordsRequest(RequestBase): fields: dict def parse(self) -> None: diff --git a/src/servers/webservices/src/modules/sake/contracts/responses.py b/src/servers/webservices/src/modules/sake/contracts/responses.py new file mode 100644 index 000000000..e75d5de79 --- /dev/null +++ b/src/servers/webservices/src/modules/sake/contracts/responses.py @@ -0,0 +1,38 @@ +from servers.webservices.src.modules.sake.abstractions.general import ResponseBase +from servers.webservices.src.modules.sake.contracts.requests import CreateRecordRequest, SearchForRecordsRequest +from servers.webservices.src.modules.sake.contracts.results import CreateRecordResult, SearchForRecordsResult + + +class CreateRecordResponse(ResponseBase): + _result: "CreateRecordResult" + _request: "CreateRecordRequest" + + def build(self) -> None: + self._content.add("CreateRecordResult") + self._content.add("tableid", self._result.table_id) + self._content.add("recordid", self._result.record_id) + + for field in self._result.fields: + self._content.add("fields", field) + + super().build() + + +class GetMyRecordResponse(ResponseBase): + pass + + +class SearchForRecordsResponse(ResponseBase): + _result: "SearchForRecordsResult" + _request: "SearchForRecordsRequest" + + def build(self) -> None: + self._content.add("SearchForRecordsResponse") + self._content.add("SearchForRecordsResult", "Success") + if self._result.user_data is not None: + for key, value in self._result.user_data.items(): + self._content.add("values", value) + else: + self._content.add("values") + + super().build() diff --git a/src/servers/webservices/modules/sake/contracts/results.py b/src/servers/webservices/src/modules/sake/contracts/results.py similarity index 64% rename from src/servers/webservices/modules/sake/contracts/results.py rename to src/servers/webservices/src/modules/sake/contracts/results.py index c47c6c2bb..7c77cedba 100644 --- a/src/servers/webservices/modules/sake/contracts/results.py +++ b/src/servers/webservices/src/modules/sake/contracts/results.py @@ -1,4 +1,5 @@ -from servers.webservices.modules.sake.abstractions.contracts import ResultBase +from typing import OrderedDict +from servers.webservices.src.modules.sake.abstractions.general import ResultBase class CreateRecordResult(ResultBase): @@ -18,3 +19,7 @@ class GetMyRecordsResult(ResultBase): (field_name,field_type,field_value) ] """ + + +class SearchForRecordsResult(ResultBase): + user_data: OrderedDict[str, str] diff --git a/src/servers/webservices/src/modules/sake/exceptions/general.py b/src/servers/webservices/src/modules/sake/exceptions/general.py new file mode 100644 index 000000000..010a7bca1 --- /dev/null +++ b/src/servers/webservices/src/modules/sake/exceptions/general.py @@ -0,0 +1,5 @@ +from servers.webservices.src.exceptions.general import WebExceptions + + +class SakeException(WebExceptions): + pass diff --git a/src/servers/webservices/src/modules/sake/handlers/general.py b/src/servers/webservices/src/modules/sake/handlers/general.py new file mode 100644 index 000000000..b3ffb18d5 --- /dev/null +++ b/src/servers/webservices/src/modules/sake/handlers/general.py @@ -0,0 +1,31 @@ +from servers.webservices.src.applications.client import Client +from servers.webservices.src.modules.sake.abstractions.general import CmdHandlerBase +from servers.webservices.src.modules.sake.contracts.requests import CreateRecordRequest, GetMyRecordsRequest, SearchForRecordsRequest +from servers.webservices.src.modules.sake.contracts.results import CreateRecordResult, GetMyRecordsResult, SearchForRecordsResult + + +class CreateRecordHandler(CmdHandlerBase): + _request: "CreateRecordRequest" + _result: "CreateRecordResult" + + def __init__(self, client: "Client", request: "CreateRecordRequest") -> None: + assert isinstance(request, CreateRecordRequest) + super().__init__(client, request) + + +class GetMyRecordsHandler(CmdHandlerBase): + _request: "GetMyRecordsRequest" + _result: "GetMyRecordsResult" + + def __init__(self, client: "Client", request: "GetMyRecordsRequest") -> None: + assert isinstance(request, GetMyRecordsRequest) + super().__init__(client, request) + + +class SearchForRecordsHandler(CmdHandlerBase): + _request: "SearchForRecordsRequest" + _result: "SearchForRecordsResult" + + def __init__(self, client: "Client", request: "SearchForRecordsRequest") -> None: + assert isinstance(request, SearchForRecordsRequest) + super().__init__(client, request) diff --git a/src/servers/webservices/tests/sake.py b/src/servers/webservices/tests/sake.py index 0c3fbdd4a..94206a747 100644 --- a/src/servers/webservices/tests/sake.py +++ b/src/servers/webservices/tests/sake.py @@ -1,6 +1,6 @@ import unittest -from servers.webservices.modules.auth.contracts.requests import LoginUniqueNickRequest +from servers.webservices.src.modules.auth.contracts.requests import LoginUniqueNickRequest class Auth(unittest.TestCase): diff --git a/src/tests/library/encrypt_test.py b/src/tests/library/encrypt_test.py index eb4dc2780..3bdf6626d 100644 --- a/src/tests/library/encrypt_test.py +++ b/src/tests/library/encrypt_test.py @@ -1,5 +1,5 @@ import unittest -from library.encryption.gs_encryption import ChatCrypt +from library.src.encryption.gs_encryption import ChatCrypt class GSEncryptionTest(unittest.TestCase): From ba0013c52d3d6faa33fff3947745337b6a3ac696 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 20 Jul 2024 23:56:38 +0000 Subject: [PATCH 081/231] refactor: added __init__.py --- src/library/src/__init__ copy.py | 0 src/library/tests/__init__.py | 0 src/servers/chat/src/__init__.py | 0 src/servers/chat/tests/__init__.py | 0 src/servers/game_status/src/abstractions/base_classes/__init__.py | 0 src/servers/game_status/tests/__init__.py | 0 src/servers/game_traffic_relay/src/__init__.py | 0 src/servers/game_traffic_relay/tests/__init__.py | 0 src/servers/natneg/src/__init__.py | 0 src/servers/natneg/tests/__init__.py | 0 src/servers/presence_connection_manager/src/__init__.py | 0 src/servers/presence_connection_manager/tests/__init__.py | 0 src/servers/presence_search_player/src/__init__.py | 0 src/servers/presence_search_player/tests/__init__.py | 0 src/servers/query_report/src/__init__.py | 0 src/servers/query_report/tests/__init__.py | 0 src/servers/server_browser/src/__init__.py | 0 src/servers/server_browser/tests/__init__.py | 0 src/servers/webservices/src/__init__.py | 0 src/servers/webservices/tests/__init__.py | 0 20 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/library/src/__init__ copy.py create mode 100644 src/library/tests/__init__.py create mode 100644 src/servers/chat/src/__init__.py create mode 100644 src/servers/chat/tests/__init__.py create mode 100644 src/servers/game_status/src/abstractions/base_classes/__init__.py create mode 100644 src/servers/game_status/tests/__init__.py create mode 100644 src/servers/game_traffic_relay/src/__init__.py create mode 100644 src/servers/game_traffic_relay/tests/__init__.py create mode 100644 src/servers/natneg/src/__init__.py create mode 100644 src/servers/natneg/tests/__init__.py create mode 100644 src/servers/presence_connection_manager/src/__init__.py create mode 100644 src/servers/presence_connection_manager/tests/__init__.py create mode 100644 src/servers/presence_search_player/src/__init__.py create mode 100644 src/servers/presence_search_player/tests/__init__.py create mode 100644 src/servers/query_report/src/__init__.py create mode 100644 src/servers/query_report/tests/__init__.py create mode 100644 src/servers/server_browser/src/__init__.py create mode 100644 src/servers/server_browser/tests/__init__.py create mode 100644 src/servers/webservices/src/__init__.py create mode 100644 src/servers/webservices/tests/__init__.py diff --git a/src/library/src/__init__ copy.py b/src/library/src/__init__ copy.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/library/tests/__init__.py b/src/library/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/chat/src/__init__.py b/src/servers/chat/src/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/chat/tests/__init__.py b/src/servers/chat/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/game_status/src/abstractions/base_classes/__init__.py b/src/servers/game_status/src/abstractions/base_classes/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/game_status/tests/__init__.py b/src/servers/game_status/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/game_traffic_relay/src/__init__.py b/src/servers/game_traffic_relay/src/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/game_traffic_relay/tests/__init__.py b/src/servers/game_traffic_relay/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/natneg/src/__init__.py b/src/servers/natneg/src/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/natneg/tests/__init__.py b/src/servers/natneg/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/presence_connection_manager/src/__init__.py b/src/servers/presence_connection_manager/src/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/presence_connection_manager/tests/__init__.py b/src/servers/presence_connection_manager/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/presence_search_player/src/__init__.py b/src/servers/presence_search_player/src/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/presence_search_player/tests/__init__.py b/src/servers/presence_search_player/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/query_report/src/__init__.py b/src/servers/query_report/src/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/query_report/tests/__init__.py b/src/servers/query_report/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/server_browser/src/__init__.py b/src/servers/server_browser/src/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/server_browser/tests/__init__.py b/src/servers/server_browser/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/src/__init__.py b/src/servers/webservices/src/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/tests/__init__.py b/src/servers/webservices/tests/__init__.py new file mode 100644 index 000000000..e69de29bb From 653c63ac124aa0351df554b35fbf07ee033d4eb1 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 24 Jul 2024 12:56:51 +0000 Subject: [PATCH 082/231] refactor: added mock object --- src/.devcontainer/devcontainer.json | 2 +- src/.vscode/settings.json | 3 +- src/library/src/abstractions/handler.py | 30 +++++++++++--- src/library/src/log/log_manager.py | 23 ----------- src/library/tests/mock_objects/general.py | 31 ++++++++++++++ .../natneg/__init__.py} | 0 src/servers/natneg/src/contracts/results.py | 2 +- src/servers/natneg/tests/handler_tests.py | 41 +++++++++++++++++++ .../tests/{tests.py => requests_test.py} | 0 9 files changed, 100 insertions(+), 32 deletions(-) create mode 100644 src/library/tests/mock_objects/general.py rename src/{library/src/__init__ copy.py => servers/natneg/__init__.py} (100%) create mode 100644 src/servers/natneg/tests/handler_tests.py rename src/servers/natneg/tests/{tests.py => requests_test.py} (100%) diff --git a/src/.devcontainer/devcontainer.json b/src/.devcontainer/devcontainer.json index 0fec28679..181abe62a 100644 --- a/src/.devcontainer/devcontainer.json +++ b/src/.devcontainer/devcontainer.json @@ -3,7 +3,7 @@ { "name": "Python 3", // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye", + "image": "mcr.microsoft.com/devcontainers/python:1-3.10-bullseye", // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, // Use 'forwardPorts' to make a list of ports inside the container available locally. diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index a6735e59a..ee78415b9 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -1,3 +1,4 @@ { - "python.analysis.typeCheckingMode": "off" + "python.analysis.typeCheckingMode": "off", + "workbench.iconTheme": "material-icon-theme", } \ No newline at end of file diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index 9f1a50bd0..541cfd4f0 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -1,9 +1,11 @@ import abc from library.src.abstractions.client import ClientBase from library.src.exceptions.error import UniSpyException -from typing import TYPE_CHECKING, List +from typing import TYPE_CHECKING from typing import Type +import requests +from library.src.unispy_server_config import CONFIG if TYPE_CHECKING: from library.src.abstractions.contracts import RequestBase, ResultBase, ResponseBase @@ -15,15 +17,26 @@ class CmdHandlerBase(abc.ABC): _request: "RequestBase" _result: "ResultBase" = None _response: "ResponseBase" = None - _backend_url: str - _result_type: Type + _backend_url: "str" = None """ store the backend url """ + _result_cls: "Type" = None + """ + the result class type + """ def __init__(self, client: "ClientBase", request: "RequestBase") -> None: + + if (self._backend_url is not None and self._result_cls is None) or (self._backend_url is None and self._result_cls is not None): + raise UniSpyException( + "The backend url and result_cls should not be None or not None at same time") + assert issubclass(type(client), ClientBase) assert issubclass(type(request), RequestBase) + # if some subclass do not need result, override the __init__() in that subclass + assert issubclass(self._result_cls, ResultBase) + self._client = client self._request = request @@ -49,9 +62,13 @@ def _request_check(self) -> None: @abc.abstractmethod def _data_operate(self) -> None: "default use restapi to access to our backend service" - if self._result is not None: - result_type = type(self._result) - # get the http response and create it with this type + if self._result_cls is None: + return + + # get the http response and create it with this type + url = CONFIG.backend.url + self._backend_url + result = requests.post(url) + self._result = self._result_cls(**result) pass @abc.abstractmethod @@ -71,6 +88,7 @@ def _handle_exception(self, ex) -> None: def _log_current_class(self) -> None: if self._client is None: + # todo self._client.log_current_class(self) else: self._client.log_current_class(self) diff --git a/src/library/src/log/log_manager.py b/src/library/src/log/log_manager.py index acd7a4a1a..a2a2f03c2 100644 --- a/src/library/src/log/log_manager.py +++ b/src/library/src/log/log_manager.py @@ -1,45 +1,22 @@ import logging from logging.handlers import TimedRotatingFileHandler import os -import threading class LogWriter: original_logger: logging.Logger - __thread_lock: threading.Lock def __init__(self, logger) -> None: self.original_logger = logger - self.__thread_lock = threading.Lock() def info(self, message: str): - self.__thread_lock.acquire() self.original_logger.info(message) - self.__thread_lock.release() def error(self, message: str): - self.__thread_lock.acquire() self.original_logger.error(message) - self.__thread_lock.release() def warn(self, message: str): - self.__thread_lock.acquire() self.original_logger.warn(message) - self.__thread_lock.release() - - -class FakeLogger(LogWriter): - def __init__(self) -> None: - super().__init__(None) - - def info(self, message): - print(message) - - def error(self, message): - print(message) - - def warn(self, message): - print(message) def create_dir(path): diff --git a/src/library/tests/mock_objects/general.py b/src/library/tests/mock_objects/general.py new file mode 100644 index 000000000..1ce5defa2 --- /dev/null +++ b/src/library/tests/mock_objects/general.py @@ -0,0 +1,31 @@ +import socketserver +from library.src.abstractions.client import ClientBase +from library.src.abstractions.connections import ConnectionBase +from library.src.log.log_manager import LogWriter + + +class ClientMock(ClientBase): + pass + + +class ConnectionMock(ConnectionBase): + def send(self, data: bytes) -> None: + return print(data) + + +class RequestHandlerMock(socketserver.BaseRequestHandler): + pass + + +class LogMock(LogWriter): + def __init__(self) -> None: + super().__init__(None) + + def info(self, message): + print(message) + + def error(self, message): + print(message) + + def warn(self, message): + print(message) diff --git a/src/library/src/__init__ copy.py b/src/servers/natneg/__init__.py similarity index 100% rename from src/library/src/__init__ copy.py rename to src/servers/natneg/__init__.py diff --git a/src/servers/natneg/src/contracts/results.py b/src/servers/natneg/src/contracts/results.py index 34b2b5021..5f4abd43d 100644 --- a/src/servers/natneg/src/contracts/results.py +++ b/src/servers/natneg/src/contracts/results.py @@ -3,7 +3,7 @@ class AddressCheckResult(CommonResultBase): - packet_type: ResponseType = bytes(ResponseType.ADDRESS_REPLY) + packet_type: ResponseType = ResponseType.ADDRESS_REPLY class ConnectResult(ResultBase): diff --git a/src/servers/natneg/tests/handler_tests.py b/src/servers/natneg/tests/handler_tests.py new file mode 100644 index 000000000..87b1a2131 --- /dev/null +++ b/src/servers/natneg/tests/handler_tests.py @@ -0,0 +1,41 @@ +import unittest + +from library.src.unispy_server_config import CONFIG +from library.tests.mock_objects.general import ClientMock, ConnectionMock, LogMock, RequestHandlerMock +from servers.natneg.src.contracts.requests import InitRequest +from servers.natneg.src.contracts.results import InitResult +from servers.natneg.src.handlers.handlers import InitHandler + + +def create_client(): + handler = RequestHandlerMock() + logger = LogMock() + conn = ConnectionMock( + handler=handler, + config=CONFIG.servers["NatNegotiation"], t_client=ClientMock, + logger=logger) + return conn._client + + +class HandlerTests(unittest.TestCase): + + def init_test(self): + raw = bytes( + [ + 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, + 0x00, + 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + ) # fmt: skip + req = InitRequest(raw) + client = create_client() + handler = InitHandler(client, req) + # change function pointer + handler._data_operate = lambda: None + handler.handle() + handler._result = InitResult() + + +if __name__ == "__main__": + test = HandlerTests() + test.init_test() diff --git a/src/servers/natneg/tests/tests.py b/src/servers/natneg/tests/requests_test.py similarity index 100% rename from src/servers/natneg/tests/tests.py rename to src/servers/natneg/tests/requests_test.py From 59e9cea3add48ed3a960f2d2b514a55d5f073fb3 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 26 Jul 2024 02:49:42 +0000 Subject: [PATCH 083/231] refactor: added mock objects and passed init_test --- src/.devcontainer/devcontainer.json | 2 +- src/library/src/abstractions/client.py | 22 +++++---- src/library/src/abstractions/connections.py | 4 +- src/library/src/abstractions/contracts.py | 3 ++ src/library/src/abstractions/handler.py | 45 ++++++++++++------- src/library/src/exceptions/error.py | 4 +- .../src/extentions/string_extentions.py | 24 +++++----- src/library/src/log/log_manager.py | 3 ++ src/library/tests/mock_objects/general.py | 12 ++--- src/requirements.txt | 3 +- .../natneg/src/abstractions/contracts.py | 20 +++++---- .../natneg/src/abstractions/handlers.py | 2 +- src/servers/natneg/src/contracts/requests.py | 17 +++---- src/servers/natneg/src/contracts/responses.py | 15 ++++--- src/servers/natneg/src/handlers/handlers.py | 21 ++++++--- src/servers/natneg/tests/handler_tests.py | 16 ++++--- src/servers/natneg/tests/mock_objects.py | 6 +++ 17 files changed, 137 insertions(+), 82 deletions(-) create mode 100644 src/servers/natneg/tests/mock_objects.py diff --git a/src/.devcontainer/devcontainer.json b/src/.devcontainer/devcontainer.json index 181abe62a..0fec28679 100644 --- a/src/.devcontainer/devcontainer.json +++ b/src/.devcontainer/devcontainer.json @@ -3,7 +3,7 @@ { "name": "Python 3", // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - "image": "mcr.microsoft.com/devcontainers/python:1-3.10-bullseye", + "image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye", // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, // Use 'forwardPorts' to make a list of ports inside the container available locally. diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index 6e5ac49b3..ed79f7f96 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -7,6 +7,7 @@ from typing import TYPE_CHECKING if TYPE_CHECKING: + from library.src.abstractions.handler import CmdHandlerBase from library.src.abstractions.connections import ConnectionBase from library.src.abstractions.switcher import SwitcherBase from library.src.abstractions.enctypt_base import EncryptBase @@ -32,6 +33,8 @@ def __init__( self.connection: ConnectionBase = connection self.logger = logger + self.__log_prefix = f"[{self.connection.remote_ip}:{ + self.connection.remote_port}]" def on_connected(self) -> None: pass @@ -74,26 +77,27 @@ def test_received(self, buffer) -> None: self.on_received(buffer) - def log_verbose(self, message: str) -> None: - pass + def log_debug(self, message: str) -> None: + self.logger.debug(f"{self.__log_prefix}: {message}") def log_info(self, message: str) -> None: - pass + self.logger.info(f"{self.__log_prefix}: {message}") def log_warn(self, message: str) -> None: - pass + self.logger.warn(f"{self.__log_prefix}: {message}") def log_error(self, message: str) -> None: - pass + self.logger.error(f"{self.__log_prefix}: {message}") def log_network_sending(self, data: object) -> None: - pass + self.logger.info(f"{self.__log_prefix} [send]: {data}") def log_network_receving(self, data: object) -> None: - pass + self.logger.info(f"{self.__log_prefix} [recv]: {data}") - def log_current_class(self, object) -> None: - pass + def log_current_class(self, object: "CmdHandlerBase") -> None: + self.logger.debug(f"{self.__log_prefix} [=>] <{ + object.__class__.__name__}>") class EasyTimer: diff --git a/src/library/src/abstractions/connections.py b/src/library/src/abstractions/connections.py index 46685efe9..45fe21525 100644 --- a/src/library/src/abstractions/connections.py +++ b/src/library/src/abstractions/connections.py @@ -1,7 +1,7 @@ import abc import socketserver from library.src.abstractions.client import ClientBase -from library.src.extentions.string_extentions import IPEndPoint +# from library.src.extentions.string_extentions import IPEndPoint from library.src.log.log_manager import LogWriter from library.src.unispy_server_config import ServerConfig @@ -27,7 +27,7 @@ def __init__( assert isinstance(config, ServerConfig) assert issubclass(t_client, ClientBase) # assert isinstance(logger, LogWriter) - assert issubclass(type(handler), socketserver.BaseRequestHandler) + # assert issubclass(type(handler), socketserver.BaseRequestHandler) self.remote_ip = handler.client_address[0] self.remote_port = int(handler.client_address[1]) self.config = config diff --git a/src/library/src/abstractions/contracts.py b/src/library/src/abstractions/contracts.py index 2e9ce8c1b..83f96b388 100644 --- a/src/library/src/abstractions/contracts.py +++ b/src/library/src/abstractions/contracts.py @@ -2,6 +2,7 @@ from copy import deepcopy from dataclasses import dataclass import enum +from uuid import UUID class RequestBase(abc.ABC): @@ -40,6 +41,8 @@ def to_serializable_dict(self) -> dict: result[key] = value.value elif isinstance(value, enum.IntEnum): result[key] = value.value + elif isinstance(value, UUID): + result[key] = str(value) return result diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index 541cfd4f0..b68c30a27 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -7,8 +7,8 @@ from library.src.unispy_server_config import CONFIG -if TYPE_CHECKING: - from library.src.abstractions.contracts import RequestBase, ResultBase, ResponseBase +# if TYPE_CHECKING: +from library.src.abstractions.contracts import RequestBase, ResultBase, ResponseBase class CmdHandlerBase(abc.ABC): @@ -28,14 +28,15 @@ class CmdHandlerBase(abc.ABC): def __init__(self, client: "ClientBase", request: "RequestBase") -> None: - if (self._backend_url is not None and self._result_cls is None) or (self._backend_url is None and self._result_cls is not None): + if self._backend_url is None: raise UniSpyException( "The backend url and result_cls should not be None or not None at same time") assert issubclass(type(client), ClientBase) assert issubclass(type(request), RequestBase) # if some subclass do not need result, override the __init__() in that subclass - assert issubclass(self._result_cls, ResultBase) + if self._result_cls is not None: + assert issubclass(self._result_cls, ResultBase) self._client = client self._request = request @@ -43,42 +44,53 @@ def __init__(self, client: "ClientBase", request: "RequestBase") -> None: def handle(self) -> None: try: # we first log this class - self.__log_current_class() + self._log_current_class() # then we handle it self._request_check() self._data_operate() self._response_construct() if self._response is None: return + self._response_send() except Exception as ex: self._handle_exception(ex) - @abc.abstractmethod def _request_check(self) -> None: + """ + virtual function, can be override + """ # if there is gamespy raw request we convert it to unispy request if self._request.raw_request is not None: - self._request._parse_raw() + self._request.parse() - @abc.abstractmethod def _data_operate(self) -> None: - "default use restapi to access to our backend service" - if self._result_cls is None: - return + """ + virtual function, can be override + """ + + # default use restapi to access to our backend service # get the http response and create it with this type - url = CONFIG.backend.url + self._backend_url - result = requests.post(url) + url = f"{ + CONFIG.backend.url}/{self._client.server_config.server_name}/{self._backend_url}/" + data = self._request.to_serializable_dict() + data["server_id"] = str(self._client.server_config.server_id) + + result = requests.post(url, json=data) + # if the result cls is not declared, we do not parse the response values + + if self._result_cls is None: + return self._result = self._result_cls(**result) pass - @abc.abstractmethod def _response_construct(self) -> None: """construct response here in specific child class""" pass - @abc.abstractmethod def _response_send(self) -> None: """ + virtual function, can be override Send response back to client, this is a virtual function which can be override only by child class """ self._client.send(self._response) @@ -89,6 +101,7 @@ def _handle_exception(self, ex) -> None: def _log_current_class(self) -> None: if self._client is None: # todo - self._client.log_current_class(self) + # self._client.log_current_class(self) + print(self) else: self._client.log_current_class(self) diff --git a/src/library/src/exceptions/error.py b/src/library/src/exceptions/error.py index 269b29797..012d2c2c7 100644 --- a/src/library/src/exceptions/error.py +++ b/src/library/src/exceptions/error.py @@ -10,8 +10,8 @@ def __init__(self, message: str) -> None: @staticmethod # def handle_exception(e: Exception, client: ClientBase = None): - def handle_exception(e: Exception, client = None): - if issubclass(e, UniSpyException): + def handle_exception(e: Exception, client=None): + if issubclass(type(e), UniSpyException): if client is None: # LogWriter.LogError(ex.Message); pass diff --git a/src/library/src/extentions/string_extentions.py b/src/library/src/extentions/string_extentions.py index 686fc0949..f204d3c9a 100644 --- a/src/library/src/extentions/string_extentions.py +++ b/src/library/src/extentions/string_extentions.py @@ -101,18 +101,18 @@ def format_network_message( return f"[{type}] {tempLog}" -class IPEndPoint: - ip: str - port: int +# class IPEndPoint: +# ip: str +# port: int - def __init__(self, ip: str, port: int) -> None: - assert isinstance(ip, str) - assert isinstance(port, int) - self.ip = ip - self.port = port +# def __init__(self, ip: str, port: int) -> None: +# assert isinstance(ip, str) +# assert isinstance(port, int) +# self.ip = ip +# self.port = port - def get_ip_bytes(self) -> bytes: - return socket.inet_aton(self.ip) +# def get_ip_bytes(self) -> bytes: +# return socket.inet_aton(self.ip) - def get_port_bytes(self) -> bytes: - return struct.pack("!H", self.port) +# def get_port_bytes(self) -> bytes: +# return struct.pack("!H", self.port) diff --git a/src/library/src/log/log_manager.py b/src/library/src/log/log_manager.py index a2a2f03c2..d5b1a7785 100644 --- a/src/library/src/log/log_manager.py +++ b/src/library/src/log/log_manager.py @@ -9,6 +9,9 @@ class LogWriter: def __init__(self, logger) -> None: self.original_logger = logger + def debug(self, message: str): + self.original_logger.debug(message) + def info(self, message: str): self.original_logger.info(message) diff --git a/src/library/tests/mock_objects/general.py b/src/library/tests/mock_objects/general.py index 1ce5defa2..e41597a7d 100644 --- a/src/library/tests/mock_objects/general.py +++ b/src/library/tests/mock_objects/general.py @@ -1,11 +1,9 @@ import socketserver from library.src.abstractions.client import ClientBase from library.src.abstractions.connections import ConnectionBase +from library.src.abstractions.switcher import SwitcherBase from library.src.log.log_manager import LogWriter - - -class ClientMock(ClientBase): - pass +from servers.natneg.src.handlers.switcher import CmdSwitcher class ConnectionMock(ConnectionBase): @@ -13,7 +11,8 @@ def send(self, data: bytes) -> None: return print(data) -class RequestHandlerMock(socketserver.BaseRequestHandler): +class RequestHandlerMock(): + client_address: tuple = ("192.168.0.1", 0) pass @@ -21,6 +20,9 @@ class LogMock(LogWriter): def __init__(self) -> None: super().__init__(None) + def debug(self, message): + print(message) + def info(self, message): print(message) diff --git a/src/requirements.txt b/src/requirements.txt index 85173e29d..44735aacf 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -11,4 +11,5 @@ mongoengine == 0.28.2 flask_socketio socketio fastapi -xmltodict \ No newline at end of file +xmltodict +responses \ No newline at end of file diff --git a/src/servers/natneg/src/abstractions/contracts.py b/src/servers/natneg/src/abstractions/contracts.py index 989c487c6..d578aa11e 100644 --- a/src/servers/natneg/src/abstractions/contracts.py +++ b/src/servers/natneg/src/abstractions/contracts.py @@ -1,6 +1,7 @@ import abc +import socket import library.src.abstractions.contracts -from library.src.extentions.string_extentions import IPEndPoint +# from library.src.extentions.string_extentions import IPEndPoint from servers.natneg.src.enums.general import ( NatClientIndex, NatPortType, @@ -44,14 +45,14 @@ class ResponseBase(library.src.abstractions.contracts.ResponseBase): def __init__(self, request: RequestBase, result: ResultBase) -> None: super().__init__(request, result) - assert issubclass(request, RequestBase) - assert issubclass(result, ResultBase) + assert issubclass(type(request), RequestBase) + assert issubclass(type(result), ResultBase) def build(self) -> None: data = bytes() data += MAGIC_DATA data += self._request.version.to_bytes(1, "little") - data += int(self._result.packet_type).to_bytes(1, "little") + data += self._result.packet_type.value.to_bytes(1, "little") data += self._request.cookie self.sending_buffer = data @@ -67,7 +68,8 @@ def parse(self): class CommonResultBase(ResultBase, abc.ABC): - ip_endpoint: IPEndPoint + public_ip_addr: str + public_port: int class CommonResponseBase(ResponseBase): @@ -78,9 +80,9 @@ def build(self) -> None: super().build() data = bytes() data += self.sending_buffer - data += int(self._request.port_type).to_bytes(1, "little") - data += int(self._request.client_index).to_bytes(1, "little") + data += self._request.port_type.value.to_bytes(1, "little") + data += self._request.client_index.value.to_bytes(1, "little") data += bytes(self._request.use_game_port) - data += self._result.ip_endpoint.get_ip_bytes() - data += self._result.ip_endpoint.get_port_bytes() + data += socket.inet_aton(self._result.public_ip_addr) + data += self._result.public_port.to_bytes(2) self.sending_buffer = data diff --git a/src/servers/natneg/src/abstractions/handlers.py b/src/servers/natneg/src/abstractions/handlers.py index 538c9b040..312c2ba4f 100644 --- a/src/servers/natneg/src/abstractions/handlers.py +++ b/src/servers/natneg/src/abstractions/handlers.py @@ -8,7 +8,7 @@ class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase, abc.ABC): def __init__(self, client: Client, request: RequestBase) -> None: super().__init__(client, request) assert isinstance(client, Client) - assert issubclass(request, RequestBase) + assert issubclass(type(request), RequestBase) if __name__ == "__main__": diff --git a/src/servers/natneg/src/contracts/requests.py b/src/servers/natneg/src/contracts/requests.py index 6d2ad6309..a636f44fd 100644 --- a/src/servers/natneg/src/contracts/requests.py +++ b/src/servers/natneg/src/contracts/requests.py @@ -1,6 +1,6 @@ from socket import inet_ntoa import struct -from library.src.extentions.string_extentions import IPEndPoint +# from library.src.extentions.string_extentions import IPEndPoint from servers.natneg.src.abstractions.contracts import CommonRequestBase, RequestBase from servers.natneg.src.enums.general import ( NatClientIndex, @@ -37,10 +37,9 @@ class ErtAckRequest(CommonRequestBase): class InitRequest(CommonRequestBase): - def __init__(self, raw_request: bytes) -> None: - super().__init__(raw_request) - self.game_name = None - self.private_ip_endpoint = None + game_name: str = None + private_ip: str = None + private_port: int = None def parse(self) -> None: super().parse() @@ -48,7 +47,8 @@ def parse(self) -> None: port_bytes = self.raw_request[19:21][::-1] port = struct.unpack("H", port_bytes)[0] ip_address_str = inet_ntoa(ip_bytes) - self.private_ip_endpoint = IPEndPoint(ip_address_str, port) + self.private_ip = ip_address_str + self.private_port = port if len(self.raw_request) > 21 and self.raw_request[-1] == 0: game_name_bytes = self.raw_request[21:-1] @@ -66,7 +66,8 @@ class PreInitRequest(RequestBase): def parse(self) -> None: super().parse() self.state = PreInitState(self.raw_request[12]) - self.target_cookie = int.from_bytes(self.raw_request[13:17], byteorder="big") + self.target_cookie = int.from_bytes( + self.raw_request[13:17], byteorder="big") class ReportRequest(CommonRequestBase): @@ -89,4 +90,4 @@ def parse(self): self.mapping_scheme = NatPortMappingScheme(self.raw_request[17]) end_index = self.raw_request[23:].index(0) - self.game_name = self.raw_request[23 : 23 + end_index].decode("ascii") + self.game_name = self.raw_request[23: 23 + end_index].decode("ascii") diff --git a/src/servers/natneg/src/contracts/responses.py b/src/servers/natneg/src/contracts/responses.py index 7b4855fcc..7b41563f1 100644 --- a/src/servers/natneg/src/contracts/responses.py +++ b/src/servers/natneg/src/contracts/responses.py @@ -1,12 +1,15 @@ from servers.natneg.src.abstractions.contracts import ( - CommonResponseBase, - RequestBase, - ResultBase, + CommonResponseBase ) +from servers.natneg.src.contracts.requests import InitRequest +from servers.natneg.src.contracts.results import InitResult class InitResponse(CommonResponseBase): - def __init__(self, request: RequestBase, result: ResultBase) -> None: + _request: InitRequest + _result: InitResult + + def __init__(self, request: "InitRequest", result: "InitResult") -> None: super().__init__(request, result) - assert issubclass(request, RequestBase) - assert issubclass(result, ResultBase) + assert isinstance(request, InitRequest) + assert isinstance(result, InitResult) diff --git a/src/servers/natneg/src/handlers/handlers.py b/src/servers/natneg/src/handlers/handlers.py index 541c4ecd0..48714559f 100644 --- a/src/servers/natneg/src/handlers/handlers.py +++ b/src/servers/natneg/src/handlers/handlers.py @@ -1,5 +1,5 @@ from library.src.abstractions.contracts import RequestBase -from library.src.extentions.string_extentions import IPEndPoint +# from library.src.extentions.string_extentions import IPEndPoint from servers.natneg.src.abstractions.handlers import CmdHandlerBase from servers.natneg.src.applications.client import Client from servers.natneg.src.contracts.requests import AddressCheckRequest, ConnectAckRequest, ConnectRequest, ErtAckRequest, InitRequest, NatifyRequest, ReportRequest @@ -9,7 +9,7 @@ class AddressCheckHandler(CmdHandlerBase): _request: AddressCheckRequest - _result: AddressCheckResult = AddressCheckResult() + _result: AddressCheckResult _response: InitResponse def __init__(self, client: Client, request: AddressCheckRequest) -> None: @@ -18,9 +18,13 @@ def __init__(self, client: Client, request: AddressCheckRequest) -> None: assert isinstance(request, AddressCheckRequest) def _data_operate(self) -> None: - self._result.ip_endpoint = IPEndPoint( - self._client.connection.ip, self._client.connection.port - ) + """ + address check did not require restapi backend, \n + just send the remote ip back to the client + """ + self._result = AddressCheckResult() + self._result.public_ip_addr = self._client.connection.remote_ip + self._result.public_port = self._client.connection.remote_port def _response_construct(self) -> None: self._response = InitResponse(self._request, self._result) @@ -70,11 +74,18 @@ class InitHandler(CmdHandlerBase): _request: InitRequest _result: InitResult _response: InitResponse + _backend_url: str = "init" def __init__(self, client: Client, request: InitRequest) -> None: super().__init__(client, request) assert isinstance(request, InitRequest) + def _data_operate(self) -> None: + super()._data_operate() + self._result = InitResult() + self._result.public_ip_addr = self._client.connection.remote_ip + self._result.public_port = self._client.connection.remote_port + def _response_construct(self): self._response = InitResponse(self._request, self._result) diff --git a/src/servers/natneg/tests/handler_tests.py b/src/servers/natneg/tests/handler_tests.py index 87b1a2131..8bb92bea1 100644 --- a/src/servers/natneg/tests/handler_tests.py +++ b/src/servers/natneg/tests/handler_tests.py @@ -1,10 +1,13 @@ import unittest from library.src.unispy_server_config import CONFIG -from library.tests.mock_objects.general import ClientMock, ConnectionMock, LogMock, RequestHandlerMock +from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock from servers.natneg.src.contracts.requests import InitRequest from servers.natneg.src.contracts.results import InitResult from servers.natneg.src.handlers.handlers import InitHandler +import responses + +from servers.natneg.tests.mock_objects import ClientMock def create_client(): @@ -14,11 +17,12 @@ def create_client(): handler=handler, config=CONFIG.servers["NatNegotiation"], t_client=ClientMock, logger=logger) + return conn._client class HandlerTests(unittest.TestCase): - + @responses.activate def init_test(self): raw = bytes( [ @@ -29,11 +33,13 @@ def init_test(self): ) # fmt: skip req = InitRequest(raw) client = create_client() + handler = InitHandler(client, req) - # change function pointer - handler._data_operate = lambda: None + url = f"{ + CONFIG.backend.url}/{handler._client.server_config.server_name}/{handler._backend_url}/" + responses.add(responses.POST, url, json={"message": "ok"}, status=200) + handler.handle() - handler._result = InitResult() if __name__ == "__main__": diff --git a/src/servers/natneg/tests/mock_objects.py b/src/servers/natneg/tests/mock_objects.py new file mode 100644 index 000000000..689bc650d --- /dev/null +++ b/src/servers/natneg/tests/mock_objects.py @@ -0,0 +1,6 @@ +from servers.natneg.src.applications.client import Client + + +class ClientMock(Client): + + pass From 1b5560e66e95ac611903f12df718ef2e49fb34d8 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 31 Jul 2024 05:52:02 +0000 Subject: [PATCH 084/231] refactor: fixing typing errors --- common/config.json | 51 ++++--- src/.vscode/launch.json | 2 +- src/.vscode/settings.json | 2 +- src/library/src/abstractions/connections.py | 2 +- src/library/src/abstractions/contracts.py | 5 +- src/library/src/unispy_server_config.py | 138 ++++++++---------- src/library/tests/mock_objects/general.py | 5 +- src/requirements.txt | 6 +- .../chat/src/contracts/results/channel.py | 25 +++- .../chat/src/contracts/results/general.py | 32 ++-- .../natneg/src/abstractions/contracts.py | 18 ++- .../natneg/src/abstractions/handlers.py | 2 +- .../natneg/src/aggregations/natneg_cookie.py | 13 ++ src/servers/natneg/src/contracts/requests.py | 16 +- src/servers/natneg/src/contracts/responses.py | 44 +++++- src/servers/natneg/src/contracts/results.py | 29 ++-- src/servers/natneg/src/handlers/handlers.py | 87 ++++++++--- src/servers/natneg/src/handlers/switcher.py | 2 +- src/servers/natneg/tests/handler_tests.py | 3 +- src/servers/natneg/tests/redis.py | 54 +++---- .../src/applications/data.py | 39 ++++- .../src/aggregates/natneg_cookie.py | 13 -- .../src/v2/abstractions/cmd_handler_base.py | 2 +- .../{request_base.py => contracts.py} | 20 +++ .../src/v2/abstractions/response_base.py | 9 -- .../src/v2/abstractions/result_base.py | 7 - .../src/v2/aggregates/natneg_cookie.py | 12 -- .../query_report/src/v2/contracts/requests.py | 2 +- .../src/v2/contracts/responses.py | 2 +- .../query_report/src/v2/contracts/results.py | 6 +- .../src/v2/handlers/handlers.py | 9 +- .../src/modules/sake/contracts/requests.py | 3 + .../src/modules/sake/contracts/results.py | 17 +-- 33 files changed, 403 insertions(+), 274 deletions(-) create mode 100644 src/servers/natneg/src/aggregations/natneg_cookie.py delete mode 100644 src/servers/query_report/src/aggregates/natneg_cookie.py rename src/servers/query_report/src/v2/abstractions/{request_base.py => contracts.py} (62%) delete mode 100644 src/servers/query_report/src/v2/abstractions/response_base.py delete mode 100644 src/servers/query_report/src/v2/abstractions/result_base.py delete mode 100644 src/servers/query_report/src/v2/aggregates/natneg_cookie.py diff --git a/common/config.json b/common/config.json index 51cf7a701..8aabdb7e5 100644 --- a/common/config.json +++ b/common/config.json @@ -1,6 +1,13 @@ { - "database": { - "server": "ip", + "backend": { + "url": "http://localhost:8080" + }, + "logging": { + "path": "~/Downloads/unispy_server/log/", + "min_log_level": "debug" + }, + "postgresql": { + "server": "127.0.0.1", "port": 5432, "database": "unispy-db", "username": "unispy", @@ -11,87 +18,87 @@ "ssl_password": "", "root_cert": "" }, + "mongodb": { + "server": "127.0.0.1", + "port": 5432, + "username": "unispy", + "password": "123456", + "database": null + }, "redis": { - "server": "ip", + "server": "127.0.0.1", "port": 5678, "user": "", "password": "password", "ssl": "true", "ssl_host": "" }, - "servers": [ - { + "servers": { + "PresenceConnectionManager": { "server_id": "00000000-0000-0000-0000-000000000000", "server_name": "PresenceConnectionManager", "public_address": "0.0.0.0", "listening_port": 29900 }, - { + "PresenceSearchPlayer": { "server_id": "00000000-0000-0000-0000-000000000000", "server_name": "PresenceSearchPlayer", "public_address": "0.0.0.0", "listening_port": 29901 }, - { + "CDKey": { "server_id": "00000000-0000-0000-0000-000000000000", "server_name": "CDKey", "public_address": "0.0.0.0", "listening_port": 29910 }, - { + "ServerBrowserV1": { "server_id": "00000000-0000-0000-0000-000000000000", "server_name": "ServerBrowserV1", "public_address": "0.0.0.0", "listening_port": 28900 }, - { + "ServerBrowserV2": { "server_id": "00000000-0000-0000-0000-000000000000", "server_name": "ServerBrowserV2", "public_address": "0.0.0.0", "listening_port": 28910 }, - { + "QueryReport": { "server_id": "00000000-0000-0000-0000-000000000000", "server_name": "QueryReport", "public_address": "0.0.0.0", "listening_port": 27900 }, - { + "NatNegotiation": { "server_id": "00000000-0000-0000-0000-000000000000", "server_name": "NatNegotiation", "public_address": "0.0.0.0", "listening_port": 27901 }, - { + "GameStatus": { "server_id": "00000000-0000-0000-0000-000000000000", "server_name": "GameStatus", "public_address": "0.0.0.0", "listening_port": 29920 }, - { + "Chat": { "server_id": "00000000-0000-0000-0000-000000000000", "server_name": "Chat", "public_address": "0.0.0.0", "listening_port": 6667 }, - { + "WebServices": { "server_id": "00000000-0000-0000-0000-000000000000", "server_name": "WebServices", "public_address": "0.0.0.0", "listening_port": 80 }, - { + "GameTrafficRelay": { "server_id": "00000000-0000-0000-0000-000000000000", "server_name": "GameTrafficRelay", "public_address": "0.0.0.0", "listening_port": 10086 } - ], - "backend": { - "url": "http://localhost:8080" - }, - "logging": { - "path": "~/Downloads/unispy_server/log/", - "min_log_level": "verbose" } } \ No newline at end of file diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json index 7431a95a1..0bf30ad2f 100644 --- a/src/.vscode/launch.json +++ b/src/.vscode/launch.json @@ -13,7 +13,7 @@ "env": { "PYTHONPATH": "${workspaceFolder}" }, - "justMyCode": true + "justMyCode": false }, { "name": "pcm", diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index ee78415b9..0fd8299c5 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -1,4 +1,4 @@ { - "python.analysis.typeCheckingMode": "off", + "python.analysis.typeCheckingMode": "basic", "workbench.iconTheme": "material-icon-theme", } \ No newline at end of file diff --git a/src/library/src/abstractions/connections.py b/src/library/src/abstractions/connections.py index 45fe21525..57171b544 100644 --- a/src/library/src/abstractions/connections.py +++ b/src/library/src/abstractions/connections.py @@ -20,7 +20,7 @@ def __init__( self, handler: socketserver.BaseRequestHandler, config: ServerConfig, - t_client: ClientBase, + t_client: type[ClientBase], logger: LogWriter, ) -> None: super().__init__() diff --git a/src/library/src/abstractions/contracts.py b/src/library/src/abstractions/contracts.py index 83f96b388..672165095 100644 --- a/src/library/src/abstractions/contracts.py +++ b/src/library/src/abstractions/contracts.py @@ -4,6 +4,8 @@ import enum from uuid import UUID +from pydantic import BaseModel + class RequestBase(abc.ABC): command_name: object = None @@ -46,8 +48,7 @@ def to_serializable_dict(self) -> dict: return result -@dataclass -class ResultBase(abc.ABC): +class ResultBase(BaseModel, abc.ABC): pass diff --git a/src/library/src/unispy_server_config.py b/src/library/src/unispy_server_config.py index 6f25026ee..307bc48b5 100644 --- a/src/library/src/unispy_server_config.py +++ b/src/library/src/unispy_server_config.py @@ -1,111 +1,93 @@ -import json -from typing import Dict, Optional +from typing import Any, Literal, Optional from uuid import UUID import os -from library.src.exceptions.error import UniSpyException +from pydantic import BaseModel, Field, constr +from library.src.exceptions.error import UniSpyException -class PostgreSql: - url: str - def __init__( - self, - server, - port, - database, - username, - password, - ssl_mode, - trust_server_cert, - ssl_key, - ssl_password, - root_cert, - ) -> None: - self.server = server - self.port = int(port) - self.database = database - self.username = username - self.password = password - self.ssl_mode = ssl_mode - self.trust_server_cert = trust_server_cert - self.ssl_key = ssl_key - self.ssl_password = ssl_password - self.root_cert = root_cert +class PostgreSql(BaseModel): + server: str + port: int = Field(..., ge=1, le=65535) # Ensures port is between 1 and 65535 + database: str + username: str + password: str + ssl_mode: str # You might want to restrict this to specific values + trust_server_cert: bool + ssl_key: Optional[str] = None # Optional field for SSL key + ssl_password: Optional[str] = None # Optional field for SSL password + root_cert: Optional[str] = None # Optional field for root certificate + url: str = None # URL will be generated based on other fields + + def model_post_init(self, __context: Any) -> None: self.url = f"postgresql://{self.username}:{self.password}@{self.server}:{self.port}/{self.database}?sslmode={self.ssl_mode}" -class RedisConfig: - url: str +class RedisConfig(BaseModel): + server: str + port: int = Field(..., ge=1, le=65535) # Ensures port is between 1 and 65535 + user: str + password: str + ssl: bool # Use bool for SSL flag + ssl_host: Optional[str] = None # Optional field for SSL host + url: str = None # URL will be generated based on other fields - def __init__(self, server, port, user, password, ssl, ssl_host) -> None: - self.server = server - self.port = int(port) - self.user = user - self.password = password - self.ssl = ssl - self.ssl_host = ssl_host - if self.ssl == "true": + def model_post_init(self, __context: Any) -> None: + if self.ssl: self.url = ( f"rediss://{self.user}:{self.password}@{self.server}:{self.port}/0" ) + else: + self.url = ( + f"redis://{self.user}:{self.password}@{self.server}:{self.port}/0" + ) -class ServerConfig: - def __init__(self, server_id, server_name, public_address, listening_port) -> None: - self.server_id = UUID(server_id) - self.server_name = server_name - self.public_address = public_address - self.listening_port = int(listening_port) +class ServerConfig(BaseModel): + server_id: UUID + server_name: str = constr(min_length=1) # Ensures server_name is a non-empty string + public_address: str = constr( + min_length=1 + ) # Ensures public_address is a non-empty string + listening_port: int = Field( + ..., ge=1, le=65535 + ) # Ensures listening_port is between 1 and 65535 -class LoggingConfig: - def __init__(self, path: str, min_log_level: str) -> None: - self.path = path - self.min_log_level = min_log_level +class LoggingConfig(BaseModel): + path: str + min_log_level: Literal["debug", "info", "warning", "error"] -class BackendConfig: - def __init__(self, url: str) -> None: - self.url = url +class BackendConfig(BaseModel): + url: str -class MongoDbConfig: +class MongoDbConfig(BaseModel): server: str - port: int username: str password: str - database: str - url: str + port: int = Field(default=None) + database: str | None = Field(default=None) + url: str = Field(default=None) - def __init__(self, server, port, username, password, database) -> None: - self.server = server - self.port = port - self.username = username - self.password = password - self.database = database - self.url = f"mongodb+srv://{self.username}:{self.password}@{server}" - if port is not None: - self.url += f":{port}" - if database is not None: - self.url += f"/{database}" + def model_post_init(self, __context: Any) -> None: + url = f"mongodb+srv://{self.username}:{self.password}@{self.server}" + if self.port is not None: + url += f":{self.port}" + if self.database is not None: + url += f"/{self.database}" + return url -class UniSpyServerConfig: +class UniSpyServerConfig(BaseModel): postgresql: PostgreSql redis: RedisConfig backend: BackendConfig - servers: Dict[str, ServerConfig] = {} + servers: dict[str, ServerConfig] = Field(default_factory=dict) mongodb: MongoDbConfig - def __init__(self, config: Dict[str, str]) -> None: - self.mongodb = MongoDbConfig(**config["mongodb"]) - self.postgresql = PostgreSql(**config["postgresql"]) - self.redis = RedisConfig(**config["redis"]) - self.backend = BackendConfig(**config["backend"]) - self.logging = LoggingConfig(**config["logging"]) - for info in config["servers"]: - self.servers[info["server_name"]] = ServerConfig(**info) unispy_config = os.environ.get("UNISPY_CONFIG") @@ -114,8 +96,10 @@ def __init__(self, config: Dict[str, str]) -> None: "Unispy server config not found, you should set the UNISPY_CONFIG in the system enviroment." ) with open(unispy_config, "r") as f: + import json + config = json.load(f) - CONFIG = UniSpyServerConfig(config) + CONFIG = UniSpyServerConfig(**config) pass if __name__ == "__main__": diff --git a/src/library/tests/mock_objects/general.py b/src/library/tests/mock_objects/general.py index e41597a7d..8921bca15 100644 --- a/src/library/tests/mock_objects/general.py +++ b/src/library/tests/mock_objects/general.py @@ -11,7 +11,10 @@ def send(self, data: bytes) -> None: return print(data) -class RequestHandlerMock(): +class RequestHandlerMock(socketserver.BaseRequestHandler): + def __init__(self) -> None: + pass + client_address: tuple = ("192.168.0.1", 0) pass diff --git a/src/requirements.txt b/src/requirements.txt index 44735aacf..7dec0b862 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -8,8 +8,8 @@ email_validator == 2.1.1 attrs requests mongoengine == 0.28.2 -flask_socketio -socketio fastapi xmltodict -responses \ No newline at end of file +responses +pydantic +responses diff --git a/src/servers/chat/src/contracts/results/channel.py b/src/servers/chat/src/contracts/results/channel.py index f7f8e8e0b..17294593d 100644 --- a/src/servers/chat/src/contracts/results/channel.py +++ b/src/servers/chat/src/contracts/results/channel.py @@ -1,3 +1,4 @@ +from pydantic import BaseModel from servers.chat.src.abstractions.contract import ResultBase @@ -8,7 +9,11 @@ class GetChannelKeyResult(ResultBase): class GetCKeyResult(ResultBase): - infos: list[tuple] + class GetCKeyInfos(BaseModel): + nick_name: str + user_values: str + + infos: list[GetCKeyInfos] """ nick_name:str, user_values:str""" channel_name: str @@ -59,3 +64,21 @@ class TopicResult(ResultBase): class SetChannelKeyResult(ResultBase): channel_user_irc_prefix: str channel_name: str + + +if __name__ == "__main__": + dd = { + "infos": [ + { + "nick_name": "John", + "user_values": "12345" + }, + { + "nick_name": "Alice", + "user_values": "67890" + } + ], + "channel_name": "example_channel" + } + result = GetCKeyResult(**dd) + pass diff --git a/src/servers/chat/src/contracts/results/general.py b/src/servers/chat/src/contracts/results/general.py index 77a23afa0..966a5bf6a 100644 --- a/src/servers/chat/src/contracts/results/general.py +++ b/src/servers/chat/src/contracts/results/general.py @@ -1,4 +1,6 @@ from typing import List, Tuple + +from pydantic import BaseModel from servers.chat.src.abstractions.contract import ResultBase @@ -12,8 +14,12 @@ class GetKeyResult(ResultBase): class ListResult(ResultBase): + class ListInfo(BaseModel): + channel_name: str + total_channel_user: int + channel_topic: str user_irc_prefix: str - channel_info_list: List[Tuple[str, int, str]] = [] + channel_info_list: list[ListInfo] = [] """(channel_name:str,total_channel_user:int,channel_topic:str)""" @@ -31,8 +37,15 @@ class PingResult(ResultBase): class QuitResult(ResultBase): + class QuitInfo(BaseModel): + channel_name: str + is_peer_server: bool + is_channel_operator: bool + leave_reply_sending_buffer: str + kick_replay_sending_buffer: str + quiter_prefix: str - channel_infos: List[Tuple[str, bool, bool, str, str]] + channel_infos: list[QuitInfo] # (channel_name: str, is_peer_server: bool, is_channel_operator: bool,leave_reply_sending_buffer: str,kick_replay_sending_buffer: str) message: str @@ -50,11 +63,10 @@ class WhoIsResult(ResultBase): class WhoResult(ResultBase): - infos: list[tuple[str, str, str, str, str]] - """ - public string ChannelName { get; set; } - public string UserName { get; set; } - public string PublicIPAddress { get; set; } - public string NickName { get; set; } - public string Modes { get; set; } - """ + class WhoInfo(BaseModel): + channel_name: str + user_name: str + public_ip_addr: str + nick_name: str + modes: str + infos: list[WhoInfo] diff --git a/src/servers/natneg/src/abstractions/contracts.py b/src/servers/natneg/src/abstractions/contracts.py index d578aa11e..1e5313517 100644 --- a/src/servers/natneg/src/abstractions/contracts.py +++ b/src/servers/natneg/src/abstractions/contracts.py @@ -1,7 +1,7 @@ import abc import socket +from typing import Optional import library.src.abstractions.contracts -# from library.src.extentions.string_extentions import IPEndPoint from servers.natneg.src.enums.general import ( NatClientIndex, NatPortType, @@ -14,12 +14,16 @@ class RequestBase(library.src.abstractions.contracts.RequestBase): version: int - cookie: bytes + cookie: int + """ + byteorder: + big + """ port_type: NatPortType - command_name: bytes + command_name: RequestType raw_request: bytes - def __init__(self, raw_request: bytes = None): + def __init__(self, raw_request: Optional[bytes] = None): assert isinstance(raw_request, bytes) self.raw_request = raw_request @@ -27,9 +31,9 @@ def parse(self) -> None: if len(self.raw_request) < 12: return - self.version = self.raw_request[6] + self.version = int(self.raw_request[6]) self.command_name = RequestType(self.raw_request[7]) - self.cookie = self.raw_request[8:12] + self.cookie = int.from_bytes(self.raw_request[8:12]) self.port_type = NatPortType(self.raw_request[12]) @@ -53,7 +57,7 @@ def build(self) -> None: data += MAGIC_DATA data += self._request.version.to_bytes(1, "little") data += self._result.packet_type.value.to_bytes(1, "little") - data += self._request.cookie + data += self._request.cookie.to_bytes(4) self.sending_buffer = data diff --git a/src/servers/natneg/src/abstractions/handlers.py b/src/servers/natneg/src/abstractions/handlers.py index 312c2ba4f..a8d439a81 100644 --- a/src/servers/natneg/src/abstractions/handlers.py +++ b/src/servers/natneg/src/abstractions/handlers.py @@ -12,5 +12,5 @@ def __init__(self, client: Client, request: RequestBase) -> None: if __name__ == "__main__": - cmd = CmdHandlerBase(None, None) + # cmd = CmdHandlerBase(None, None) pass diff --git a/src/servers/natneg/src/aggregations/natneg_cookie.py b/src/servers/natneg/src/aggregations/natneg_cookie.py new file mode 100644 index 000000000..c1c8d9511 --- /dev/null +++ b/src/servers/natneg/src/aggregations/natneg_cookie.py @@ -0,0 +1,13 @@ + + +from pydantic import BaseModel + + +class NatNegCookie(BaseModel): + host_ip: str + host_port: int + heartbeat_ip: str + heartbeat_port: int + game_name: str + natneg_message: list + instant_key: int diff --git a/src/servers/natneg/src/contracts/requests.py b/src/servers/natneg/src/contracts/requests.py index a636f44fd..8d1cacbf5 100644 --- a/src/servers/natneg/src/contracts/requests.py +++ b/src/servers/natneg/src/contracts/requests.py @@ -1,5 +1,6 @@ from socket import inet_ntoa import struct + # from library.src.extentions.string_extentions import IPEndPoint from servers.natneg.src.abstractions.contracts import CommonRequestBase, RequestBase from servers.natneg.src.enums.general import ( @@ -37,9 +38,9 @@ class ErtAckRequest(CommonRequestBase): class InitRequest(CommonRequestBase): - game_name: str = None - private_ip: str = None - private_port: int = None + game_name: str + private_ip: str + private_port: int def parse(self) -> None: super().parse() @@ -61,17 +62,16 @@ class NatifyRequest(CommonRequestBase): class PreInitRequest(RequestBase): state: PreInitState - target_cookie: bytes + target_cookie: int def parse(self) -> None: super().parse() self.state = PreInitState(self.raw_request[12]) - self.target_cookie = int.from_bytes( - self.raw_request[13:17], byteorder="big") + self.target_cookie = int.from_bytes(self.raw_request[13:17]) class ReportRequest(CommonRequestBase): - is_nat_success: bool = None + is_nat_success: bool = False game_name: str nat_type: NatType mapping_scheme: NatPortMappingScheme @@ -90,4 +90,4 @@ def parse(self): self.mapping_scheme = NatPortMappingScheme(self.raw_request[17]) end_index = self.raw_request[23:].index(0) - self.game_name = self.raw_request[23: 23 + end_index].decode("ascii") + self.game_name = self.raw_request[23 : 23 + end_index].decode("ascii") diff --git a/src/servers/natneg/src/contracts/responses.py b/src/servers/natneg/src/contracts/responses.py index 7b41563f1..67e25d19e 100644 --- a/src/servers/natneg/src/contracts/responses.py +++ b/src/servers/natneg/src/contracts/responses.py @@ -1,8 +1,11 @@ -from servers.natneg.src.abstractions.contracts import ( - CommonResponseBase +from servers.natneg.src.abstractions.contracts import CommonResponseBase +from servers.natneg.src.contracts.requests import ( + AddressCheckRequest, + ErtAckRequest, + InitRequest, + NatifyRequest, ) -from servers.natneg.src.contracts.requests import InitRequest -from servers.natneg.src.contracts.results import InitResult +from servers.natneg.src.contracts.results import AddressCheckResult, ErtAckResult, InitResult, NatifyResult class InitResponse(CommonResponseBase): @@ -13,3 +16,36 @@ def __init__(self, request: "InitRequest", result: "InitResult") -> None: super().__init__(request, result) assert isinstance(request, InitRequest) assert isinstance(result, InitResult) + + +class ErcAckResponse(InitResponse): + _request: ErtAckRequest + _result: ErtAckResult + + def __init__(self, request: "ErtAckRequest", result: "ErtAckResult") -> None: + assert isinstance(request, InitRequest) + assert isinstance(result, InitResult) + self._request = request + self._result = result + + +class NatifyResponse(InitResponse): + _request: NatifyRequest + _result: NatifyResult + + def __init__(self, request: "NatifyRequest", result: "NatifyResult") -> None: + assert isinstance(request, NatifyRequest) + assert isinstance(result, NatifyResult) + self._request = request + self._result = result + + +class AddressCheckResponse(InitResponse): + _request: "AddressCheckRequest" + _result: "AddressCheckResult" + + def __init__(self, request: "AddressCheckRequest", result: "AddressCheckResult") -> None: + assert isinstance(request, AddressCheckRequest) + assert isinstance(result, AddressCheckResult) + self._request = request + self._result = result diff --git a/src/servers/natneg/src/contracts/results.py b/src/servers/natneg/src/contracts/results.py index 5f4abd43d..a4ba8b1a0 100644 --- a/src/servers/natneg/src/contracts/results.py +++ b/src/servers/natneg/src/contracts/results.py @@ -1,5 +1,10 @@ +from typing import Any from servers.natneg.src.abstractions.contracts import CommonResultBase, ResultBase -from servers.natneg.src.enums.general import ConnectPacketStatus, PreInitState, ResponseType +from servers.natneg.src.enums.general import ( + ConnectPacketStatus, + PreInitState, + ResponseType, +) class AddressCheckResult(CommonResultBase): @@ -13,22 +18,17 @@ class ConnectResult(ResultBase): port: int version: bytes cookie: bytes - - def __init__(self) -> None: - super().__init__() - self.packet_type = ResponseType.CONNECT - - -class ErtAckResult(CommonResultBase): - def __init__(self) -> None: - super().__init__() - self.packet_type = ResponseType.ERT_ACK + packet_type: ResponseType = ResponseType.CONNECT class InitResult(CommonResultBase): packet_type: ResponseType = ResponseType.INIT_ACK +class ErtAckResult(InitResult): + packet_type: ResponseType = ResponseType.ERT_ACK + + class NatifyResult(CommonResultBase): packet_type: ResponseType = ResponseType.ERT_TEST @@ -37,11 +37,8 @@ class PreInitResult(ResultBase): client_index: int state: PreInitState client_id: int - - def __init__(self) -> None: - super().__init__() - self.packet_type = ResponseType.PRE_INIT_ACK - self.state = PreInitState.READY + packet_type = ResponseType.PRE_INIT_ACK + state = PreInitState.READY class ReportResult(ResultBase): diff --git a/src/servers/natneg/src/handlers/handlers.py b/src/servers/natneg/src/handlers/handlers.py index 48714559f..82e239901 100644 --- a/src/servers/natneg/src/handlers/handlers.py +++ b/src/servers/natneg/src/handlers/handlers.py @@ -1,10 +1,31 @@ -from library.src.abstractions.contracts import RequestBase -# from library.src.extentions.string_extentions import IPEndPoint +from copy import copy + +from servers.natneg.src.abstractions.contracts import RequestBase from servers.natneg.src.abstractions.handlers import CmdHandlerBase from servers.natneg.src.applications.client import Client -from servers.natneg.src.contracts.requests import AddressCheckRequest, ConnectAckRequest, ConnectRequest, ErtAckRequest, InitRequest, NatifyRequest, ReportRequest -from servers.natneg.src.contracts.responses import InitResponse -from servers.natneg.src.contracts.results import AddressCheckResult, ConnectResult, ErtAckResult, InitResult, NatifyResult, ReportResult +from servers.natneg.src.contracts.requests import ( + AddressCheckRequest, + ConnectAckRequest, + ConnectRequest, + ErtAckRequest, + InitRequest, + NatifyRequest, + ReportRequest, +) +from servers.natneg.src.contracts.responses import ( + AddressCheckResponse, + ErcAckResponse, + InitResponse, + NatifyResponse, +) +from servers.natneg.src.contracts.results import ( + AddressCheckResult, + ConnectResult, + ErtAckResult, + InitResult, + NatifyResult, + ReportResult, +) class AddressCheckHandler(CmdHandlerBase): @@ -22,12 +43,16 @@ def _data_operate(self) -> None: address check did not require restapi backend, \n just send the remote ip back to the client """ - self._result = AddressCheckResult() + data = { + "public_ip_addr": copy(self._client.connection.remote_ip), + "public_port": copy(self._client.connection.remote_port), + } + self._result = AddressCheckResult(**data) self._result.public_ip_addr = self._client.connection.remote_ip self._result.public_port = self._client.connection.remote_port def _response_construct(self) -> None: - self._response = InitResponse(self._request, self._result) + self._response = AddressCheckResponse(self._request, self._result) class ConnectAckHandler(CmdHandlerBase): @@ -49,21 +74,23 @@ def __init__(self, client: Client, request: ConnectRequest) -> None: class ErtAckHandler(CmdHandlerBase): - _request: ErtAckRequest - _result: ErtAckResult = ErtAckResult() - _response: InitResponse + _request: "ErtAckRequest" + _result: "ErtAckResult" + _response: "ErcAckResponse" - def __init__(self, client: Client, request: ErtAckRequest) -> None: + def __init__(self, client: "Client", request: "ErtAckRequest") -> None: super().__init__(client, request) assert isinstance(request, ErtAckRequest) def _data_operate(self) -> None: - self._result.ip_endpoint = IPEndPoint( - self._client.connection.ip, self._client.connection.port - ) + data = { + "public_ip_addr": copy(self._client.connection.remote_ip), + "public_port": copy(self._client.connection.remote_port), + } + self._result = ErtAckResult(**data) def _response_construct(self) -> None: - self._response = InitResponse(self._request, self._result) + self._response = ErcAckResponse(self._request, self._result) class InitHandler(CmdHandlerBase): @@ -81,18 +108,24 @@ def __init__(self, client: Client, request: InitRequest) -> None: assert isinstance(request, InitRequest) def _data_operate(self) -> None: - super()._data_operate() - self._result = InitResult() - self._result.public_ip_addr = self._client.connection.remote_ip - self._result.public_port = self._client.connection.remote_port + data = { + "public_ip_addr": copy(self._client.connection.remote_ip), + "public_port": copy(self._client.connection.remote_port), + } + self._result = InitResult(**data) def _response_construct(self): self._response = InitResponse(self._request, self._result) + def _response_send(self) -> None: + """we need first to send the response to client in case of the time expire""" + super()._response_send() + super()._data_operate() + class NatifyHandler(CmdHandlerBase): _request: NatifyRequest - _result: NatifyResult = NatifyResult() + _result: NatifyResult _response: InitResponse def __init__(self, client: Client, request: NatifyRequest) -> None: @@ -100,12 +133,18 @@ def __init__(self, client: Client, request: NatifyRequest) -> None: assert isinstance(request, NatifyRequest) def _data_operate(self): - self._result.ip_endpoint = IPEndPoint( - self._client.connection.ip, self._client.connection.port - ) + data = { + "public_ip_addr": copy(self._client.connection.remote_ip), + "public_port": copy(self._client.connection.remote_port), + } + self._result = NatifyResult(**data) def _response_construct(self): - self._response = InitResponse(self._request, self._result) + self._response = NatifyResponse(self._request, self._result) + + def _response_send(self) -> None: + super()._response_send() + super()._data_operate() class PingHandler(CmdHandlerBase): diff --git a/src/servers/natneg/src/handlers/switcher.py b/src/servers/natneg/src/handlers/switcher.py index 16e70fff0..ee8cbcccd 100644 --- a/src/servers/natneg/src/handlers/switcher.py +++ b/src/servers/natneg/src/handlers/switcher.py @@ -8,7 +8,7 @@ class CmdSwitcher(SwitcherBase): def __init__(self, client: Client, rawRequest: bytes) -> None: super().__init__(client, rawRequest) - assert issubclass(client, Client) + assert issubclass(type(client), Client) assert isinstance(rawRequest, bytes) def _process_raw_request(self) -> None: diff --git a/src/servers/natneg/tests/handler_tests.py b/src/servers/natneg/tests/handler_tests.py index 8bb92bea1..e712e94a1 100644 --- a/src/servers/natneg/tests/handler_tests.py +++ b/src/servers/natneg/tests/handler_tests.py @@ -10,7 +10,7 @@ from servers.natneg.tests.mock_objects import ClientMock -def create_client(): +def create_client()->ClientMock: handler = RequestHandlerMock() logger = LogMock() conn = ConnectionMock( @@ -32,6 +32,7 @@ def init_test(self): ] ) # fmt: skip req = InitRequest(raw) + client = create_client() handler = InitHandler(client, req) diff --git a/src/servers/natneg/tests/redis.py b/src/servers/natneg/tests/redis.py index ff50b72a2..a2f002b61 100644 --- a/src/servers/natneg/tests/redis.py +++ b/src/servers/natneg/tests/redis.py @@ -1,37 +1,37 @@ -import datetime -from typing import Optional +# import datetime +# from typing import Optional -from pydantic.v1 import EmailStr +# from pydantic.v1 import EmailStr -from redis_om import Field, HashModel, Migrator +# from redis_om import Field, HashModel, Migrator -class Customer(HashModel): - first_name: str - last_name: str = Field(index=True) - email: EmailStr - join_date: datetime.date - age: int = Field(index=True) - bio: Optional[str] +# class Customer(HashModel): +# first_name: str +# last_name: str = Field(index=True) +# email: EmailStr +# join_date: datetime.date +# age: int = Field(index=True) +# bio: Optional[str] -# Now, if we use this model with a Redis deployment that has the -# RediSearch module installed, we can run queries like the following. +# # Now, if we use this model with a Redis deployment that has the +# # RediSearch module installed, we can run queries like the following. -# Before running queries, we need to run migrations to set up the -# indexes that Redis OM will use. You can also use the `migrate` -# CLI tool for this! -Migrator().run() +# # Before running queries, we need to run migrations to set up the +# # indexes that Redis OM will use. You can also use the `migrate` +# # CLI tool for this! +# Migrator().run() -# Find all customers with the last name "Brookins" -Customer.find(Customer.last_name == "Brookins").all() +# # Find all customers with the last name "Brookins" +# Customer.find(Customer.last_name == "Brookins").all() -# Find all customers that do NOT have the last name "Brookins" -Customer.find(Customer.last_name != "Brookins").all() +# # Find all customers that do NOT have the last name "Brookins" +# Customer.find(Customer.last_name != "Brookins").all() -# Find all customers whose last name is "Brookins" OR whose age is -# 100 AND whose last name is "Smith" -Customer.find( - (Customer.last_name == "Brookins") - | (Customer.age == 100) & (Customer.last_name == "Smith") -).all() +# # Find all customers whose last name is "Brookins" OR whose age is +# # 100 AND whose last name is "Smith" +# Customer.find( +# (Customer.last_name == "Brookins") +# | (Customer.age == 100) & (Customer.last_name == "Smith") +# ).all() diff --git a/src/servers/presence_connection_manager/src/applications/data.py b/src/servers/presence_connection_manager/src/applications/data.py index 6599ee82f..6a6fa91af 100644 --- a/src/servers/presence_connection_manager/src/applications/data.py +++ b/src/servers/presence_connection_manager/src/applications/data.py @@ -1,4 +1,3 @@ -from sqlalchemy import insert from library.src.database.pg_orm import ( Blocked, Friends, @@ -18,7 +17,8 @@ def is_email_exist(email: str) -> bool: def delete_friend_by_profile_id(profile_id: int): - friend = PG_SESSION.query(Friends).filter(Friends.friendid == profile_id).first() + friend = PG_SESSION.query(Friends).filter( + Friends.friendid == profile_id).first() if friend is None: raise GPDatabaseException( f"friend deletion have errors on profile id:{profile_id}" @@ -47,6 +47,16 @@ def get_friend_profile_id_list(profile_id: int, namespace_id: int) -> list[int]: def get_profile_info_list(profile_id: int, namespace_id: int): + """ + Retrieve profile information based on profile_id and namespace_id. + + Args: + profile_id (int): The ID of the profile to retrieve information for. + namespace_id (int): The ID of the namespace to filter the sub-profiles. + + Returns: + tuple: A tuple containing the profile information, sub-profile information, and user information. + """ result = ( PG_SESSION.query(Profiles, SubProfiles, Users) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) @@ -61,10 +71,19 @@ def get_profile_info_list(profile_id: int, namespace_id: int): def get_user_info_list(email: str, nick_name: str) -> list[tuple[int, int, int]]: """ - return (userid, profileid, subprofileid) + Retrieve the user information list based on the provided email and nickname. + + Args: + email (str): The email address of the user to search for. + nick_name (str): The nickname of the user to search for. + + Returns: + List[Tuple[int, int, int]]: A list of tuples containing the userid, profileid, and subprofileid + of users that match the provided email and nickname in the database. """ result = ( - PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) + PG_SESSION.query(Users.userid, Profiles.profileid, + SubProfiles.subprofileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .filter(Users.email == email, Profiles.nick == nick_name) @@ -75,7 +94,8 @@ def get_user_info_list(email: str, nick_name: str) -> list[tuple[int, int, int]] def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: result = ( - PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) + PG_SESSION.query(Users.userid, Profiles.profileid, + SubProfiles.subprofileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .filter( @@ -89,7 +109,8 @@ def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: def get_user_infos(unique_nick: str, namespace_id: int) -> list[tuple[int, int, int]]: result = ( - PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) + PG_SESSION.query(Users.userid, Profiles.profileid, + SubProfiles.subprofileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .filter( @@ -112,7 +133,8 @@ def update_block_info_list(target_id: int, profile_id: int, namespace_id: int) - .count() ) if result == 0: - b = Blocked(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) + b = Blocked(targetid=target_id, namespaceid=namespace_id, + profileid=profile_id) PG_SESSION.add(b) PG_SESSION.commit() @@ -127,7 +149,8 @@ def update_friend_info(target_id: int, profile_id: int, namespace_id: int): ) .count() ) - f = Friends(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) + f = Friends(targetid=target_id, namespaceid=namespace_id, + profileid=profile_id) if result == 0: PG_SESSION.add(f) diff --git a/src/servers/query_report/src/aggregates/natneg_cookie.py b/src/servers/query_report/src/aggregates/natneg_cookie.py deleted file mode 100644 index 74133f70b..000000000 --- a/src/servers/query_report/src/aggregates/natneg_cookie.py +++ /dev/null @@ -1,13 +0,0 @@ - -from attr import dataclass - - -@dataclass -class NatNegCookie: - host_ip:str - host_port:int - heartbeat_ip:str - heartbeat_port:int - game_name:str - natneg_message:bytes - instant_key:int \ No newline at end of file diff --git a/src/servers/query_report/src/v2/abstractions/cmd_handler_base.py b/src/servers/query_report/src/v2/abstractions/cmd_handler_base.py index 8ef6d1571..dd412403a 100644 --- a/src/servers/query_report/src/v2/abstractions/cmd_handler_base.py +++ b/src/servers/query_report/src/v2/abstractions/cmd_handler_base.py @@ -1,6 +1,6 @@ import abc from library.src.abstractions.handler import CmdHandlerBase as CHB -from servers.query_report.src.v2.abstractions.request_base import RequestBase +from servers.query_report.src.v2.abstractions.contracts import RequestBase from servers.query_report.src.applications.client import Client diff --git a/src/servers/query_report/src/v2/abstractions/request_base.py b/src/servers/query_report/src/v2/abstractions/contracts.py similarity index 62% rename from src/servers/query_report/src/v2/abstractions/request_base.py rename to src/servers/query_report/src/v2/abstractions/contracts.py index 00638aac8..dc8dd0b32 100644 --- a/src/servers/query_report/src/v2/abstractions/request_base.py +++ b/src/servers/query_report/src/v2/abstractions/contracts.py @@ -19,3 +19,23 @@ def parse(self): raise QRException self.command_name = RequestType(self.raw_request[0]) self.instant_key = int(self.raw_request[1:5]) + + + +import abc +import library.src.abstractions.contracts +from servers.query_report.src.v2.enums.general import PacketType + + +class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): + packet_type: PacketType = None + + + +import abc +import library.src.abstractions.contracts + + +class ResponseBase(library.src.abstractions.contracts.ResponseBase, abc.ABC): + _result: ResultBase + _request: RequestBase diff --git a/src/servers/query_report/src/v2/abstractions/response_base.py b/src/servers/query_report/src/v2/abstractions/response_base.py deleted file mode 100644 index 4add60583..000000000 --- a/src/servers/query_report/src/v2/abstractions/response_base.py +++ /dev/null @@ -1,9 +0,0 @@ -import abc -import library.src.abstractions.contracts -from servers.query_report.src.v2.contracts.requests import RequestBase -from servers.query_report.src.v2.contracts.results import ResultBase - - -class ResponseBase(library.src.abstractions.contracts.ResponseBase, abc.ABC): - _result: ResultBase - _request: RequestBase diff --git a/src/servers/query_report/src/v2/abstractions/result_base.py b/src/servers/query_report/src/v2/abstractions/result_base.py deleted file mode 100644 index c3ef79e0c..000000000 --- a/src/servers/query_report/src/v2/abstractions/result_base.py +++ /dev/null @@ -1,7 +0,0 @@ -import abc -import library.src.abstractions.contracts -from servers.query_report.src.v2.enums.general import PacketType - - -class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): - packet_type: PacketType = None diff --git a/src/servers/query_report/src/v2/aggregates/natneg_cookie.py b/src/servers/query_report/src/v2/aggregates/natneg_cookie.py deleted file mode 100644 index d02e72da8..000000000 --- a/src/servers/query_report/src/v2/aggregates/natneg_cookie.py +++ /dev/null @@ -1,12 +0,0 @@ -from attr import dataclass - - -@dataclass -class NatNegCookie: - ipaddress:str - host_port:int - heartbeat_ip:str - heartbeat_port:int - game_name:str - natneg_message:bytes - instant_key:int \ No newline at end of file diff --git a/src/servers/query_report/src/v2/contracts/requests.py b/src/servers/query_report/src/v2/contracts/requests.py index 8281d3576..92aa0963f 100644 --- a/src/servers/query_report/src/v2/contracts/requests.py +++ b/src/servers/query_report/src/v2/contracts/requests.py @@ -2,7 +2,7 @@ from library.src.extentions.encoding import get_string from library.src.log.log_manager import LogWriter from servers.query_report.src.exceptions.exceptions import QRException -from servers.query_report.src.v2.abstractions.request_base import RequestBase +from servers.query_report.src.v2.abstractions.contracts import RequestBase from servers.query_report.src.v2.enums.general import GameServerStatus diff --git a/src/servers/query_report/src/v2/contracts/responses.py b/src/servers/query_report/src/v2/contracts/responses.py index 47a5c4b71..9db0b4fd6 100644 --- a/src/servers/query_report/src/v2/contracts/responses.py +++ b/src/servers/query_report/src/v2/contracts/responses.py @@ -2,7 +2,7 @@ from servers.presence_connection_manager.src.contracts.requests.general import ( KeepAliveRequest, ) -from servers.query_report.src.v2.abstractions.response_base import ResponseBase +from servers.query_report.src.v2.abstractions.contracts import ResponseBase from servers.query_report.src.v2.contracts.requests import ( AvaliableRequest, ChallengeRequest, diff --git a/src/servers/query_report/src/v2/contracts/results.py b/src/servers/query_report/src/v2/contracts/results.py index 54351073e..fdc7ae361 100644 --- a/src/servers/query_report/src/v2/contracts/results.py +++ b/src/servers/query_report/src/v2/contracts/results.py @@ -1,5 +1,4 @@ -from library.src.extentions.string_extentions import IPEndPoint -from servers.query_report.src.v2.abstractions.result_base import ResultBase +from servers.query_report.src.v2.abstractions.contracts import ResultBase from servers.query_report.src.v2.enums.general import PacketType @@ -19,5 +18,6 @@ class EchoResult(ResultBase): class HeartBeatResult(ResultBase): - remote_ip_endpoint: IPEndPoint + remote_ip_address:str + remote_port:int packet_type: PacketType = PacketType.HEARTBEAT diff --git a/src/servers/server_browser/src/v2/handlers/handlers.py b/src/servers/server_browser/src/v2/handlers/handlers.py index b2c82df15..53ff6ce64 100644 --- a/src/servers/server_browser/src/v2/handlers/handlers.py +++ b/src/servers/server_browser/src/v2/handlers/handlers.py @@ -19,12 +19,19 @@ AdHocResult, ServerMainListResult, ) -from servers.server_browser.src.v2.enums.general import RequestType, ServerListUpdateOption +from servers.server_browser.src.v2.enums.general import ( + RequestType, + ServerListUpdateOption, +) from servers.server_browser.src.v2.abstractions.handlers import CmdHandlerBase from servers.server_browser.src.v2.applications.client import Client +def get_clients(ss): + raise NotImplementedError() + + class AdHocHandler(CmdHandlerBase): _message: GameServerInfo diff --git a/src/servers/webservices/src/modules/sake/contracts/requests.py b/src/servers/webservices/src/modules/sake/contracts/requests.py index 4db7fa916..881696421 100644 --- a/src/servers/webservices/src/modules/sake/contracts/requests.py +++ b/src/servers/webservices/src/modules/sake/contracts/requests.py @@ -1,4 +1,6 @@ from typing import OrderedDict + +from pydantic import BaseModel from servers.webservices.src.modules.sake.abstractions.general import ( RequestBase, NAMESPACE, @@ -66,6 +68,7 @@ class GetRecordLimitRequest(RequestBase): class GetSpecificRecordsRequest(RequestBase): + record_ids: list[tuple] """ [ diff --git a/src/servers/webservices/src/modules/sake/contracts/results.py b/src/servers/webservices/src/modules/sake/contracts/results.py index 7c77cedba..ec0cfca36 100644 --- a/src/servers/webservices/src/modules/sake/contracts/results.py +++ b/src/servers/webservices/src/modules/sake/contracts/results.py @@ -1,4 +1,6 @@ from typing import OrderedDict + +from pydantic import BaseModel from servers.webservices.src.modules.sake.abstractions.general import ResultBase @@ -9,16 +11,11 @@ class CreateRecordResult(ResultBase): class GetMyRecordsResult(ResultBase): - records: list[tuple] - """ - [ - (field_name,field_type,field_value), - (field_name,field_type,field_value), - (field_name,field_type,field_value), - ... - (field_name,field_type,field_value) - ] - """ + class GetMyRecordsInfo(BaseModel): + field_name: str + field_type: str + field_value: str + records: list[GetMyRecordsInfo] class SearchForRecordsResult(ResultBase): From 9c932f599c7f594a0dc721e049a4cd13b96809de Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 31 Jul 2024 08:50:06 +0000 Subject: [PATCH 085/231] fix(nn): type errors --- src/.vscode/launch.json | 2 +- src/library/src/abstractions/client.py | 30 +++++----- src/library/src/abstractions/handler.py | 13 ++-- src/library/src/abstractions/switcher.py | 12 ++-- src/library/src/encryption/encoding.py | 5 +- src/servers/chat/src/abstractions/contract.py | 10 ++-- src/servers/natneg/src/applications/client.py | 5 +- src/servers/natneg/src/contracts/requests.py | 4 ++ src/servers/natneg/src/contracts/results.py | 4 +- src/servers/natneg/src/enums/general.py | 3 + src/servers/natneg/src/handlers/handlers.py | 4 +- src/servers/natneg/src/handlers/switcher.py | 59 ++++++++++++++++--- src/servers/natneg/tests/handler_tests.py | 8 +-- 13 files changed, 106 insertions(+), 53 deletions(-) diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json index 0bf30ad2f..7431a95a1 100644 --- a/src/.vscode/launch.json +++ b/src/.vscode/launch.json @@ -13,7 +13,7 @@ "env": { "PYTHONPATH": "${workspaceFolder}" }, - "justMyCode": false + "justMyCode": true }, { "name": "pcm", diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index ed79f7f96..33c68f4d1 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -1,10 +1,11 @@ import abc from library.src.encryption.encoding import Encoding +from library.src.exceptions.error import UniSpyException from library.src.log.log_manager import LogWriter from library.src.log.log_manager import LogWriter from library.src.unispy_server_config import ServerConfig -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional if TYPE_CHECKING: from library.src.abstractions.handler import CmdHandlerBase @@ -17,9 +18,9 @@ class ClientBase(abc.ABC): server_config: "ServerConfig" - connection: object + connection: "ConnectionBase" logger: "LogWriter" - crypto: "EncryptBase" = None + crypto: "Optional[EncryptBase]" = None info: "ClientInfoBase" is_log_raw: "bool" = False @@ -29,9 +30,8 @@ def __init__( assert isinstance(server_config, ServerConfig) # assert isinstance(logger, LogWriter) self.server_config = server_config - from library.src.abstractions.connections import ConnectionBase - self.connection: ConnectionBase = connection + self.connection = connection self.logger = logger self.__log_prefix = f"[{self.connection.remote_ip}:{ self.connection.remote_port}]" @@ -40,13 +40,16 @@ def on_connected(self) -> None: pass def on_disconnected(self) -> None: - self.__del__() + pass @abc.abstractmethod def create_switcher(self, buffer) -> "SwitcherBase": pass def on_received(self, buffer) -> None: + if self.crypto is not None: + buffer = self.crypto.decrypt(buffer) + switcher: "SwitcherBase" = self.create_switcher(buffer) switcher.handle() @@ -57,13 +60,17 @@ def decrypt_message(self, buffer) -> bytes: return buffer def send(self, response: "ResponseBase") -> None: + from library.src.abstractions.contracts import ResponseBase + + assert issubclass(type(response),ResponseBase) response.build() sending_buffer = response.sending_buffer if isinstance(sending_buffer, str): - buffer = Encoding.get_bytes(sending_buffer) - else: + buffer:bytes = Encoding.get_bytes(sending_buffer) + if isinstance(sending_buffer, bytes): buffer = sending_buffer - + else: + raise UniSpyException("not supported buffer type") self.log_network_sending(buffer) if self.crypto is not None: @@ -71,11 +78,6 @@ def send(self, response: "ResponseBase") -> None: self.connection.send(buffer) - def test_received(self, buffer) -> None: - if self.crypto is not None: - self.crypto = None - - self.on_received(buffer) def log_debug(self, message: str) -> None: self.logger.debug(f"{self.__log_prefix}: {message}") diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index b68c30a27..afff024a1 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -15,13 +15,13 @@ class CmdHandlerBase(abc.ABC): _client: "ClientBase" _request: "RequestBase" - _result: "ResultBase" = None - _response: "ResponseBase" = None - _backend_url: "str" = None + _result: "ResultBase" + _response: "ResponseBase" + _backend_url: "str" """ store the backend url """ - _result_cls: "Type" = None + _result_cls: "Type[ResultBase]" """ the result class type """ @@ -76,11 +76,14 @@ def _data_operate(self) -> None: data = self._request.to_serializable_dict() data["server_id"] = str(self._client.server_config.server_id) - result = requests.post(url, json=data) + response = requests.post(url, json=data) + result = response.json() # if the result cls is not declared, we do not parse the response values if self._result_cls is None: return + if self._result is not None: + return self._result = self._result_cls(**result) pass diff --git a/src/library/src/abstractions/switcher.py b/src/library/src/abstractions/switcher.py index a8b3977ca..1ddf4edf8 100644 --- a/src/library/src/abstractions/switcher.py +++ b/src/library/src/abstractions/switcher.py @@ -1,15 +1,15 @@ import abc from library.src.abstractions.client import ClientBase from library.src.abstractions.handler import CmdHandlerBase -from typing import TYPE_CHECKING, List +from typing import TYPE_CHECKING, List, Optional class SwitcherBase(abc.ABC): _client: ClientBase _raw_request: object - _handlers: List[CmdHandlerBase] - _requests: List[tuple] + _handlers: List[CmdHandlerBase] = [] + _requests: List[tuple] = [] """ [ (request_name,raw_request), @@ -20,13 +20,11 @@ class SwitcherBase(abc.ABC): """ - def __init__(self, client: ClientBase, raw_request: object) -> None: + def __init__(self, client: ClientBase, raw_request: Optional[bytes | str]) -> None: assert isinstance(client, ClientBase) self._client = client self._raw_request = raw_request - self._handlers = [] - self._requests = [] def handle(self): from library.src.exceptions.error import UniSpyException @@ -54,5 +52,5 @@ def _process_raw_request(self) -> None: pass @abc.abstractmethod - def _create_cmd_handlers(self, name: object, rawRequest: object) -> CmdHandlerBase: + def _create_cmd_handlers(self, name: object, raw_request: object) -> CmdHandlerBase: pass diff --git a/src/library/src/encryption/encoding.py b/src/library/src/encryption/encoding.py index 7f1df25c7..3800639d2 100644 --- a/src/library/src/encryption/encoding.py +++ b/src/library/src/encryption/encoding.py @@ -7,6 +7,7 @@ def get_string(data: bytes) -> str: assert isinstance(data, bytes) return data.decode("ascii") - def get_bytes(data: str) -> List[int]: + @staticmethod + def get_bytes(data: str) -> bytes: assert isinstance(data, str) - return list(data.encode()) + return bytes(data.encode()) diff --git a/src/servers/chat/src/abstractions/contract.py b/src/servers/chat/src/abstractions/contract.py index 36ee329b5..5e4522f4d 100644 --- a/src/servers/chat/src/abstractions/contract.py +++ b/src/servers/chat/src/abstractions/contract.py @@ -3,11 +3,11 @@ class RequestBase(library.src.abstractions.contracts.RequestBase, abc.ABC): - raw_request: str = None - command_name: str = None - _prefix: str = None - _cmd_params: list = None - _longParam: str = None + raw_request: str + command_name: str + _prefix: str + _cmd_params: list + _longParam: str def __init__(self, raw_request: str) -> None: assert isinstance(raw_request, str) diff --git a/src/servers/natneg/src/applications/client.py b/src/servers/natneg/src/applications/client.py index 2c73cf923..da9362d85 100644 --- a/src/servers/natneg/src/applications/client.py +++ b/src/servers/natneg/src/applications/client.py @@ -9,6 +9,7 @@ def __init__(self, connection, server_config: ServerConfig, logger: LogWriter): self.is_log_raw = True def create_switcher(self, buffer: bytes): - # return super().create_switcher(buffer) assert isinstance(buffer, bytes) - return + from servers.natneg.src.handlers.switcher import CmdSwitcher + + return CmdSwitcher(self, buffer) diff --git a/src/servers/natneg/src/contracts/requests.py b/src/servers/natneg/src/contracts/requests.py index 8d1cacbf5..93fc378dc 100644 --- a/src/servers/natneg/src/contracts/requests.py +++ b/src/servers/natneg/src/contracts/requests.py @@ -17,6 +17,10 @@ class AddressCheckRequest(CommonRequestBase): pass +class PingRequest(RequestBase): + pass + + class ConnectAckRequest(RequestBase): client_index: NatClientIndex diff --git a/src/servers/natneg/src/contracts/results.py b/src/servers/natneg/src/contracts/results.py index a4ba8b1a0..b42119dd6 100644 --- a/src/servers/natneg/src/contracts/results.py +++ b/src/servers/natneg/src/contracts/results.py @@ -37,9 +37,9 @@ class PreInitResult(ResultBase): client_index: int state: PreInitState client_id: int - packet_type = ResponseType.PRE_INIT_ACK + packet_type: ResponseType = ResponseType.PRE_INIT_ACK state = PreInitState.READY class ReportResult(ResultBase): - packet_type = ResponseType.REPORT_ACK + packet_type: ResponseType = ResponseType.REPORT_ACK diff --git a/src/servers/natneg/src/enums/general.py b/src/servers/natneg/src/enums/general.py index e2281fbd3..86788f465 100644 --- a/src/servers/natneg/src/enums/general.py +++ b/src/servers/natneg/src/enums/general.py @@ -6,6 +6,9 @@ class RequestType(Enum): ERT_ACK = 3 CONNECT = 5 CONNECT_ACK = 6 + """ + only used in client, not used by server + """ PING = 7 ADDRESS_CHECK = 10 NATIFY_REQUEST = 12 diff --git a/src/servers/natneg/src/handlers/handlers.py b/src/servers/natneg/src/handlers/handlers.py index 82e239901..24747f403 100644 --- a/src/servers/natneg/src/handlers/handlers.py +++ b/src/servers/natneg/src/handlers/handlers.py @@ -10,6 +10,7 @@ ErtAckRequest, InitRequest, NatifyRequest, + PingRequest, ReportRequest, ) from servers.natneg.src.contracts.responses import ( @@ -102,6 +103,7 @@ class InitHandler(CmdHandlerBase): _result: InitResult _response: InitResponse _backend_url: str = "init" + _result_cls: type[InitResult] = InitResult def __init__(self, client: Client, request: InitRequest) -> None: super().__init__(client, request) @@ -148,7 +150,7 @@ def _response_send(self) -> None: class PingHandler(CmdHandlerBase): - def __init__(self, client: Client, request: RequestBase) -> None: + def __init__(self, client: Client, request: PingRequest) -> None: super().__init__(client, request) raise NotImplementedError() diff --git a/src/servers/natneg/src/handlers/switcher.py b/src/servers/natneg/src/handlers/switcher.py index ee8cbcccd..7fd4b0a37 100644 --- a/src/servers/natneg/src/handlers/switcher.py +++ b/src/servers/natneg/src/handlers/switcher.py @@ -1,20 +1,61 @@ +from typing import Optional from library.src.abstractions.switcher import SwitcherBase +from servers.natneg.src.abstractions.handlers import CmdHandlerBase from servers.natneg.src.applications.client import Client +from servers.natneg.src.contracts.requests import ( + AddressCheckRequest, + ConnectRequest, + ErtAckRequest, + InitRequest, + PingRequest, + ReportRequest, +) +from servers.natneg.src.enums.general import RequestType +from servers.natneg.src.handlers.handlers import ( + AddressCheckHandler, + ConnectHandler, + ErtAckHandler, + InitHandler, + PingHandler, + ReportHandler, +) class CmdSwitcher(SwitcherBase): - _rawRequest: bytes + _raw_request: bytes _client: Client - def __init__(self, client: Client, rawRequest: bytes) -> None: - super().__init__(client, rawRequest) + def __init__(self, client: Client, raw_request: bytes) -> None: + super().__init__(client, raw_request) assert issubclass(type(client), Client) - assert isinstance(rawRequest, bytes) + assert isinstance(raw_request, bytes) def _process_raw_request(self) -> None: - name = self._rawRequest[7] - self._requests.append((name, self._rawRequest)) + name = RequestType(self._raw_request[7]) + self._requests.append((name, self._raw_request)) - def _create_cmd_handlers(self, name: int, rawRequest: bytes) -> None: - assert isinstance(name, int) - assert isinstance(rawRequest, bytes) + def _create_cmd_handlers( + self, name: RequestType, raw_request: bytes + ) -> Optional[CmdHandlerBase | None]: + assert isinstance(name, RequestType) + assert isinstance(raw_request, bytes) + match name: + case RequestType.INIT: + return InitHandler(self._client, InitRequest(raw_request)) + # case RequestType. + case RequestType.ERT_ACK: + return ErtAckHandler(self._client, ErtAckRequest(raw_request)) + case RequestType.CONNECT: + return ConnectHandler(self._client, ConnectRequest(raw_request)) + case RequestType.PING: + return PingHandler(self._client, PingRequest(raw_request)) + case RequestType.ADDRESS_CHECK: + return AddressCheckHandler( + self._client, AddressCheckRequest(raw_request) + ) + case RequestType.REPORT: + return ReportHandler(self._client, ReportRequest(raw_request)) + case RequestType.PRE_INIT: + return None + case _: + return None \ No newline at end of file diff --git a/src/servers/natneg/tests/handler_tests.py b/src/servers/natneg/tests/handler_tests.py index e712e94a1..7726dca22 100644 --- a/src/servers/natneg/tests/handler_tests.py +++ b/src/servers/natneg/tests/handler_tests.py @@ -10,7 +10,7 @@ from servers.natneg.tests.mock_objects import ClientMock -def create_client()->ClientMock: +def create_client(): handler = RequestHandlerMock() logger = LogMock() conn = ConnectionMock( @@ -31,16 +31,14 @@ def init_test(self): 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] ) # fmt: skip - req = InitRequest(raw) client = create_client() - handler = InitHandler(client, req) url = f"{ - CONFIG.backend.url}/{handler._client.server_config.server_name}/{handler._backend_url}/" + CONFIG.backend.url}/{client.server_config.server_name}/{InitHandler._backend_url}/" responses.add(responses.POST, url, json={"message": "ok"}, status=200) + client.on_received(raw) - handler.handle() if __name__ == "__main__": From 2eaacf70cb67ccdd326bc169fd52f2b92266f884 Mon Sep 17 00:00:00 2001 From: Bildcraft1 Date: Fri, 2 Aug 2024 22:56:26 +0200 Subject: [PATCH 086/231] Implement Moke Object for PCM Tests --- .../presence_connection_manager/tests/game.py | 20 +++++++++++++++++-- .../tests/moke_object.py | 18 ----------------- 2 files changed, 18 insertions(+), 20 deletions(-) delete mode 100644 src/servers/presence_connection_manager/tests/moke_object.py diff --git a/src/servers/presence_connection_manager/tests/game.py b/src/servers/presence_connection_manager/tests/game.py index 9cbfb0d53..75437316d 100644 --- a/src/servers/presence_connection_manager/tests/game.py +++ b/src/servers/presence_connection_manager/tests/game.py @@ -1,7 +1,19 @@ import unittest +from library.src.unispy_server_config import CONFIG +from library.tests.mock_objects.general import RequestHandlerMock, LogMock, ConnectionMock from servers.presence_connection_manager.src.contracts.requests.buddy import StatusRequest +from servers.natneg.tests.mock_objects import ClientMock +def create_client(): + handler = RequestHandlerMock() + logger = LogMock() + conn = ConnectionMock( + handler=handler, + config=CONFIG.servers["PresenceConnectionManager"], t_client=ClientMock, + logger=logger) + + return conn._client class GameTest(unittest.TestCase): def test_civilization_4(self) -> None: @@ -9,8 +21,10 @@ def test_civilization_4(self) -> None: "\\newuser\\\\email\\civ4@unispy.org\\nick\\civ4-tk\\passwordenc\\JMHGwQ__\\productid\\10435\\gamename\\civ4\\namespaceid\\17\\uniquenick\\civ4-tk\\id\\1\\final\\", "\\login\\\\challenge\\xMsHUXuWNXL3KMwmhoQZJrP0RVsArCYT\\uniquenick\\civ4-tk\\userid\\25\\profileid\\26\\response\\7f2c9c6685570ea18b7207d2cbd72452\\firewall\\1\\port\\0\\productid\\10435\\gamename\\civ4\\namespaceid\\17\\sdkrevision\\1\\id\\1\\final\\", ] + client = create_client() + for x in raw_requests: - # TODO MokeObject Implementation + client.on_received(x) pass def test_conflict_global_storm(self) -> None: @@ -19,8 +33,10 @@ def test_conflict_global_storm(self) -> None: "\\login\\\\challenge\\KMylyQbZfqzKn9otxx32q4076sOUnKif\\user\\cgs1@cgs1@rs.de\\response\\c1a6638bbcfe130e4287bfe4aa792949\\port\\-15737\\productid\\10469\\gamename\\conflictsopc\\namespaceid\\1\\id\\1\\final\\", "\\inviteto\\\\sesskey\\58366\\products\\1038\\final\\", ] + client = create_client() + for x in raw_requests: - # TODO MokeObject Implementation + client.on_received(x) pass def test_sbwfrontps2(self) -> None: diff --git a/src/servers/presence_connection_manager/tests/moke_object.py b/src/servers/presence_connection_manager/tests/moke_object.py deleted file mode 100644 index ea89c0fd5..000000000 --- a/src/servers/presence_connection_manager/tests/moke_object.py +++ /dev/null @@ -1,18 +0,0 @@ -import unittest -from unittest.mock import Mock -from servers.presence_connection_manager.applications.client import Client - -class MokeObject: - client = None - - # TODO We first need Server implementation - @staticmethod - def create_client(ip_address="192.168.1.1", port=9990): - manager_mock = Mock(IConnectionManager) - connection_mock = Mock() - connection_mock.RemoteIPEndPoint = (ip_address, port) - connection_mock.Manager = manager_mock - connection_mock.ConnectionType = "Tcp" - server_mock = Server(manager_mock) - - return Client(connection_mock, server_mock) From ed4cdd53c48c21e592ddc33d8030209d882e7ce1 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 16 Aug 2024 06:49:43 +0000 Subject: [PATCH 087/231] fix(test): run time errors --- src/.vscode/launch.json | 2 +- src/.vscode/settings.json | 2 +- src/library/src/abstractions/client.py | 25 ++-- src/library/src/abstractions/connections.py | 16 ++- src/library/src/abstractions/contracts.py | 5 +- .../src/abstractions/server_launcher_base.py | 1 + src/library/src/encryption/gs_encryption.py | 52 ++++---- src/library/src/encryption/xor_encryption.py | 8 +- .../src/extentions/bytes_extentions.py | 17 +++ src/library/src/extentions/gamespy_ramdoms.py | 2 +- .../src/extentions/password_encoder.py | 5 +- src/library/src/extentions/redis_orm.py | 50 ++++---- .../src/extentions/string_extentions.py | 17 --- src/library/src/log/log_manager.py | 2 +- src/library/src/network/__init__.py | 2 +- src/library/src/network/http_handler.py | 19 +-- src/library/src/network/tcp_handler.py | 11 +- src/library/src/network/udp_handler.py | 19 +-- src/library/src/unispy_server_config.py | 30 ++--- .../src/applications/connection_listener.py | 16 ++- .../natneg/src/abstractions/contracts.py | 15 ++- src/servers/natneg/tests/handler_tests.py | 47 ++++--- src/servers/natneg/tests/mock_objects.py | 15 ++- .../src/abstractions/contracts.py | 6 +- .../src/abstractions/handler.py | 2 +- .../src/aggregates/user_status.py | 1 - .../src/aggregates/user_status_info.py | 4 +- .../src/applications/client.py | 6 +- .../src/applications/data.py | 18 +-- .../src/contracts/requests/buddy.py | 25 ++-- .../src/contracts/requests/general.py | 4 +- .../src/contracts/requests/profile.py | 59 ++++----- .../src/contracts/responses/general.py | 22 ++-- .../src/contracts/responses/profile.py | 83 ++++++------ .../src/contracts/results/profile.py | 59 ++++----- .../src/enums/general.py | 104 +-------------- .../src/handlers/buddy.py | 3 +- .../src/aggregates/game_server_info.py | 5 +- .../src/v2/abstractions/contracts.py | 7 +- .../query_report/src/v2/contracts/requests.py | 10 +- .../src/v2/contracts/responses.py | 12 +- .../src/v2/handlers/handlers.py | 1 + .../src/aggregations/soap_envelop.py | 8 +- src/servers/webservices/tests/auth.py | 118 ++++++++++++++++++ src/tests/library/encrypt_test.py | 7 +- 45 files changed, 509 insertions(+), 433 deletions(-) diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json index 7431a95a1..9e374c4f2 100644 --- a/src/.vscode/launch.json +++ b/src/.vscode/launch.json @@ -11,7 +11,7 @@ "program": "${file}", "console": "integratedTerminal", "env": { - "PYTHONPATH": "${workspaceFolder}" + "PYTHONPATH": "${workspaceFolder}", }, "justMyCode": true }, diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index 0fd8299c5..ee78415b9 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -1,4 +1,4 @@ { - "python.analysis.typeCheckingMode": "basic", + "python.analysis.typeCheckingMode": "off", "workbench.iconTheme": "material-icon-theme", } \ No newline at end of file diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index 33c68f4d1..fb1035320 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -14,18 +14,19 @@ from library.src.abstractions.enctypt_base import EncryptBase from library.src.abstractions.contracts import ResponseBase from library.src.abstractions.client import ClientInfoBase + from library.src.network.http_handler import HttpRequest class ClientBase(abc.ABC): - server_config: "ServerConfig" + server_config: ServerConfig connection: "ConnectionBase" - logger: "LogWriter" - crypto: "Optional[EncryptBase]" = None + logger: LogWriter + crypto: Optional["EncryptBase"] = None info: "ClientInfoBase" - is_log_raw: "bool" = False + is_log_raw: bool = False def __init__( - self, connection: "ConnectionBase", server_config: "ServerConfig", logger: "LogWriter" + self, connection: "ConnectionBase", server_config: ServerConfig, logger: LogWriter ): assert isinstance(server_config, ServerConfig) # assert isinstance(logger, LogWriter) @@ -46,10 +47,11 @@ def on_disconnected(self) -> None: def create_switcher(self, buffer) -> "SwitcherBase": pass - def on_received(self, buffer) -> None: - if self.crypto is not None: - buffer = self.crypto.decrypt(buffer) - + def on_received(self, buffer: "Optional[bytes | HttpRequest]") -> None: + if isinstance(buffer, bytes): + if self.crypto is not None: + buffer = self.crypto.decrypt(buffer) + switcher: "SwitcherBase" = self.create_switcher(buffer) switcher.handle() @@ -62,11 +64,11 @@ def decrypt_message(self, buffer) -> bytes: def send(self, response: "ResponseBase") -> None: from library.src.abstractions.contracts import ResponseBase - assert issubclass(type(response),ResponseBase) + assert issubclass(type(response), ResponseBase) response.build() sending_buffer = response.sending_buffer if isinstance(sending_buffer, str): - buffer:bytes = Encoding.get_bytes(sending_buffer) + buffer: bytes = Encoding.get_bytes(sending_buffer) if isinstance(sending_buffer, bytes): buffer = sending_buffer else: @@ -78,7 +80,6 @@ def send(self, response: "ResponseBase") -> None: self.connection.send(buffer) - def log_debug(self, message: str) -> None: self.logger.debug(f"{self.__log_prefix}: {message}") diff --git a/src/library/src/abstractions/connections.py b/src/library/src/abstractions/connections.py index 57171b544..57b206c74 100644 --- a/src/library/src/abstractions/connections.py +++ b/src/library/src/abstractions/connections.py @@ -1,10 +1,17 @@ import abc import socketserver +from typing import Optional from library.src.abstractions.client import ClientBase + # from library.src.extentions.string_extentions import IPEndPoint from library.src.log.log_manager import LogWriter from library.src.unispy_server_config import ServerConfig +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + from library.src.network.http_handler import HttpRequest + class ConnectionBase(abc.ABC): remote_ip: str @@ -35,7 +42,7 @@ def __init__( self.logger = logger self._client = self.t_client(self, self.config, self.logger) - def on_received(self, data: bytes) -> None: + def on_received(self, data: "Optional[bytes|HttpRequest]") -> None: self._client.on_received(data) @abc.abstractmethod @@ -44,7 +51,6 @@ def send(self, data: bytes) -> None: raise Exception("Server is not running.") assert isinstance(data, bytes) - class UcpConnectionBase(ConnectionBase, abc.ABC): pass @@ -69,7 +75,7 @@ class HttpConnectionBase(TcpConnectionBase, abc.ABC): class ServerBase(abc.ABC): _config: ServerConfig - _t_client: type[ClientBase] + _client_cls: type[ClientBase] _logger: LogWriter _server: socketserver.BaseServer @@ -80,10 +86,10 @@ def __init__( assert issubclass(t_client, ClientBase) # assert isinstance(logger, LogWriter) self._config = config - self._t_client = t_client + self._client_cls = t_client self._logger = logger - @abc.abstractclassmethod + @abc.abstractmethod def start(self): pass diff --git a/src/library/src/abstractions/contracts.py b/src/library/src/abstractions/contracts.py index 672165095..5bd672638 100644 --- a/src/library/src/abstractions/contracts.py +++ b/src/library/src/abstractions/contracts.py @@ -2,6 +2,7 @@ from copy import deepcopy from dataclasses import dataclass import enum +from typing import Optional from uuid import UUID from pydantic import BaseModel @@ -38,7 +39,7 @@ def to_serializable_dict(self) -> dict: result = deepcopy(self.__dict__) for key, value in result.items(): if isinstance(value, bytes): - result[key] = list(value) + result[key] = value.decode("utf-8") elif isinstance(value, enum.Enum): result[key] = value.value elif isinstance(value, enum.IntEnum): @@ -57,7 +58,7 @@ class ResponseBase(abc.ABC): _result: ResultBase _request: RequestBase - def __init__(self, request: RequestBase, result: ResultBase) -> None: + def __init__(self, request: RequestBase, result: Optional[ResultBase]) -> None: super().__init__() if request is not None: assert issubclass(type(request), RequestBase) diff --git a/src/library/src/abstractions/server_launcher_base.py b/src/library/src/abstractions/server_launcher_base.py index c3f9a93ec..6f6cb7de3 100644 --- a/src/library/src/abstractions/server_launcher_base.py +++ b/src/library/src/abstractions/server_launcher_base.py @@ -3,6 +3,7 @@ from library.src.unispy_server_config import CONFIG, ServerConfig import pyfiglet import requests + VERSION = 0.45 __server_name_mapping = { diff --git a/src/library/src/encryption/gs_encryption.py b/src/library/src/encryption/gs_encryption.py index 62ca28205..cded38fd1 100644 --- a/src/library/src/encryption/gs_encryption.py +++ b/src/library/src/encryption/gs_encryption.py @@ -53,30 +53,6 @@ def init(ctx: PeerChatCtx, challenge_key: str, secret_key: str): index1 += 1 -def handle(ctx: PeerChatCtx, data): - num1 = ctx.buffer1 - num2 = ctx.buffer2 - buffer = [] - size = len(data) - datapos = 0 - - while size > 0: - num1 = (num1 + 1) % 256 - num2 = (ctx.sbox[num1] + num2) % 256 - t = ctx.sbox[num1] - ctx.sbox[num1] = ctx.sbox[num2] - ctx.sbox[num2] = t - t = (ctx.sbox[num2] + ctx.sbox[num1]) % 256 - temp = data[datapos] ^ ctx.sbox[t] - buffer.append(temp) - datapos += 1 - size -= 1 - - ctx.buffer1 = num1 - ctx.buffer2 = num2 - return bytes(buffer) - - class ChatCrypt(EncryptBase): def __init__(self, game_secret_key): self.client_ctx = PeerChatCtx() @@ -84,13 +60,37 @@ def __init__(self, game_secret_key): init(self.client_ctx, CLIENT_KEY, game_secret_key) init(self.server_ctx, SERVER_KEY, game_secret_key) + @staticmethod + def handle(ctx: PeerChatCtx, data): + num1 = ctx.buffer1 + num2 = ctx.buffer2 + buffer = [] + size = len(data) + datapos = 0 + + while size > 0: + num1 = (num1 + 1) % 256 + num2 = (ctx.sbox[num1] + num2) % 256 + t = ctx.sbox[num1] + ctx.sbox[num1] = ctx.sbox[num2] + ctx.sbox[num2] = t + t = (ctx.sbox[num2] + ctx.sbox[num1]) % 256 + temp = data[datapos] ^ ctx.sbox[t] + buffer.append(temp) + datapos += 1 + size -= 1 + + ctx.buffer1 = num1 + ctx.buffer2 = num2 + return bytes(buffer) + def encrypt(self, data: bytes) -> bytes: super().encrypt(data) - return self.handle(self.server_ctx, data) + return ChatCrypt.handle(self.server_ctx, data) def decrypt(self, data: bytes) -> bytes: super().decrypt(data) - return self.handle(self.client_ctx, data) + return ChatCrypt.handle(self.client_ctx, data) if __name__ == "__main__": diff --git a/src/library/src/encryption/xor_encryption.py b/src/library/src/encryption/xor_encryption.py index 576aafe82..82ed425bd 100644 --- a/src/library/src/encryption/xor_encryption.py +++ b/src/library/src/encryption/xor_encryption.py @@ -23,8 +23,8 @@ def encode(plaintext: bytes, enc_type: XorType): seed_1 = b"GameSpy3D" seed_2 = b"Industries" seed_3 = b"ProjectAphex" - - length = len(plaintext) + temp_plaintext = list(plaintext) + length = len(temp_plaintext) index = 0 temp = seed_0 @@ -43,10 +43,10 @@ def encode(plaintext: bytes, enc_type: XorType): if i >= temp_length: i = 0 - plaintext[index] ^= temp[i] + temp_plaintext[index] ^= temp[i] index += 1 - return plaintext + return bytes(temp_plaintext) def encrypt(self, data: bytes): super().encrypt(data) diff --git a/src/library/src/extentions/bytes_extentions.py b/src/library/src/extentions/bytes_extentions.py index 7adda794a..e1fdaf918 100644 --- a/src/library/src/extentions/bytes_extentions.py +++ b/src/library/src/extentions/bytes_extentions.py @@ -1,7 +1,24 @@ +import socket + + def bytes_to_int(input: bytes) -> int: assert isinstance(input, bytes) return int.from_bytes(input, "little") + def int_to_bytes(input: int) -> bytes: assert isinstance(input, int) return input.to_bytes(4, "little", signed=False) + + +def ip_to_4_bytes(ip: str) -> bytes: + assert isinstance(ip, str) + return socket.inet_aton(ip) + + +def port_to_2_bytes(port: int) -> bytes: + """ + using for qr sb natneg to convert port to bytes + """ + assert isinstance(port, int) + return port.to_bytes(2, "little") diff --git a/src/library/src/extentions/gamespy_ramdoms.py b/src/library/src/extentions/gamespy_ramdoms.py index dfffeee51..23f1139dc 100644 --- a/src/library/src/extentions/gamespy_ramdoms.py +++ b/src/library/src/extentions/gamespy_ramdoms.py @@ -10,7 +10,7 @@ class StringType(IntEnum): def generate_random_string(count: int, type: StringType) -> str: - random.seed(datetime.datetime.now()) + random.seed(datetime.datetime.now().timestamp()) alpha_chars = [ "A", diff --git a/src/library/src/extentions/password_encoder.py b/src/library/src/extentions/password_encoder.py index 466e60cb7..3f1625ff6 100644 --- a/src/library/src/extentions/password_encoder.py +++ b/src/library/src/extentions/password_encoder.py @@ -40,11 +40,12 @@ def game_spy_encode_method(password_bytes: bytes): assert isinstance(password_bytes, bytes) a = 0 num = 0x79707367 # gamespy + temp_data = list(password_bytes) for i in range(len(password_bytes)): num = game_spy_byte_shift(num) a = num % 0xFF - password_bytes[i] ^= a - return password_bytes + temp_data[i] ^= a + return bytes(temp_data) def game_spy_byte_shift(num): diff --git a/src/library/src/extentions/redis_orm.py b/src/library/src/extentions/redis_orm.py index 79d4a0a03..9ffb98a6b 100644 --- a/src/library/src/extentions/redis_orm.py +++ b/src/library/src/extentions/redis_orm.py @@ -1,36 +1,36 @@ -import redis +# import redis -class RedisORM: - def __init__(self, host="localhost", port=6379, db=0): - # self.redis_conn = redis.StrictRedis(host=host, port=port, db=db) - pass +# class RedisORM: +# def __init__(self, host="localhost", port=6379, db=0): +# # self.redis_conn = redis.StrictRedis(host=host, port=port, db=db) +# pass - def query(self, table_class): - return QueryBuilder(self.redis_conn, table_class) +# def query(self, table_class): +# return QueryBuilder(self.redis_conn, table_class) -class QueryBuilder: - def __init__(self, redis_conn, table_class): - self.redis_conn = redis_conn - self.table_class = table_class +# class QueryBuilder: +# def __init__(self, redis_conn, table_class): +# self.redis_conn = redis_conn +# self.table_class = table_class - def filter_by(self, **kwargs): - self.filter_criteria = kwargs - return self +# def filter_by(self, **kwargs): +# self.filter_criteria = kwargs +# return self - def first(self): - key = f"{self.table_class.__name__}:{self.filter_criteria['url']}" - data = self.redis_conn.hgetall(key) - return data +# def first(self): +# key = f"{self.table_class.__name__}:{self.filter_criteria['url']}" +# data = self.redis_conn.hgetall(key) +# return data -# Example usage -class User: - pass +# # Example usage +# class User: +# pass -redis_orm = RedisORM() -query = QueryBuilder(None, None) -result = query.filter_by(url="example.com", name="hello").first() -print(result) +# redis_orm = RedisORM() +# query = QueryBuilder(None, None) +# result = query.filter_by(url="example.com", name="hello").first() +# print(result) diff --git a/src/library/src/extentions/string_extentions.py b/src/library/src/extentions/string_extentions.py index f204d3c9a..e81f72ce7 100644 --- a/src/library/src/extentions/string_extentions.py +++ b/src/library/src/extentions/string_extentions.py @@ -99,20 +99,3 @@ def format_network_message( tempLog = convert_nonprintable_bytes_to_hex_string(message) return f"[{type}] {tempLog}" - - -# class IPEndPoint: -# ip: str -# port: int - -# def __init__(self, ip: str, port: int) -> None: -# assert isinstance(ip, str) -# assert isinstance(port, int) -# self.ip = ip -# self.port = port - -# def get_ip_bytes(self) -> bytes: -# return socket.inet_aton(self.ip) - -# def get_port_bytes(self) -> bytes: -# return struct.pack("!H", self.port) diff --git a/src/library/src/log/log_manager.py b/src/library/src/log/log_manager.py index d5b1a7785..ce0ccac7c 100644 --- a/src/library/src/log/log_manager.py +++ b/src/library/src/log/log_manager.py @@ -34,7 +34,7 @@ def create_dir(path): class LogManager: @staticmethod - def create(log_file_path, logger_name) -> None: + def create(log_file_path: str, logger_name: str) -> "LogWriter": create_dir(log_file_path) logging.basicConfig( diff --git a/src/library/src/network/__init__.py b/src/library/src/network/__init__.py index e78e8f8f4..b2652cdb1 100644 --- a/src/library/src/network/__init__.py +++ b/src/library/src/network/__init__.py @@ -4,6 +4,6 @@ class Server(abc.ABC): - @abc.abstractclassmethod + @abc.abstractmethod def start(self): pass diff --git a/src/library/src/network/http_handler.py b/src/library/src/network/http_handler.py index 358a6f678..b57f099a3 100644 --- a/src/library/src/network/http_handler.py +++ b/src/library/src/network/http_handler.py @@ -7,10 +7,13 @@ class HttpRequest: url: str - headers: dict + headers: dict[str, str] content: str def __init__(self, url: str, headers: dict, content: str) -> None: + assert isinstance(url, str) + assert isinstance(headers, dict) + assert isinstance(content, str) self.url = url self.headers = headers self.content = content @@ -30,7 +33,7 @@ def get_content_bytes(self) -> bytes: class HttpConnection(ConnectionBase): handler: BaseHTTPRequestHandler - def send(self, data: bytes) -> None: + def send(self, data: HttpResponse) -> None: self.handler.send_response(200) self.handler.send_header("Content-type", "text/xml") self.handler.end_headers() @@ -41,12 +44,12 @@ class HttpHandler(BaseHTTPRequestHandler): conn: HttpConnection def do_POST(self) -> None: - parsed_url = urlparse(self.path) + parsed_url = urlparse(self.path).geturl() content_length = int(self.headers["Content-Length"]) data = self.rfile.read(content_length).decode() - request = HttpRequest(parsed_url, self.headers, data) + request = HttpRequest(parsed_url, dict(self.headers), data) if self.conn is None: - self.conn = HttpConnection(self, *self.server.handler_params) + self.conn = HttpConnection(self, *self.server.handler_params) # type: ignore self.conn.on_received(request) @@ -55,7 +58,7 @@ def start(self) -> None: self._server = ThreadingHTTPServer( (self._config.public_address, self._config.listening_port), HttpHandler ) - self._server.handler_params = (self._config, self._t_client, self._logger) + self._server.handler_params = (self._config, self._client_cls, self._logger) # type: ignore self._server.serve_forever() @@ -73,4 +76,6 @@ def on_connected(self) -> None: if __name__ == "__main__": # create_http_server(list(CONFIG.servers.values())[0], ClientBase) - s = HttpServer(list(CONFIG.servers.values())[0], TestClient, None) + from library.tests.mock_objects.general import LogMock + + s = HttpServer(list(CONFIG.servers.values())[0], TestClient, LogMock()) diff --git a/src/library/src/network/tcp_handler.py b/src/library/src/network/tcp_handler.py index 6650da5ab..5e319286c 100644 --- a/src/library/src/network/tcp_handler.py +++ b/src/library/src/network/tcp_handler.py @@ -1,5 +1,6 @@ import socket import socketserver +from typing import Optional from library.src.abstractions.client import ClientBase from library.src.abstractions.connections import ConnectionBase, ServerBase @@ -25,11 +26,11 @@ def disconnect(self) -> None: class TcpHandler(socketserver.BaseRequestHandler): request: socket.socket - conn: TcpConnection = None + conn: Optional[TcpConnection] = None def handle(self) -> None: if self.conn is None: - self.conn = TcpConnection(self, *self.server.handler_params) + self.conn = TcpConnection(self, *self.server.handler_params) # type: ignore self.conn.on_connected() while True: try: @@ -52,7 +53,7 @@ def start(self) -> None: TcpHandler, ) self._server.allow_reuse_address = True - self._server.handler_params = (self._config, self._t_client, self._logger) + self._server.handler_params = (self._config, self._client_cls, self._logger) # type: ignore self._server.serve_forever() @@ -69,6 +70,8 @@ def on_connected(self) -> None: if __name__ == "__main__": - s = TcpServer(list(CONFIG.servers.values())[0], TestClient, None) + from tests.mock_objects.general import LogMock + + s = TcpServer(list(CONFIG.servers.values())[0], TestClient, LogMock()) s.start() pass diff --git a/src/library/src/network/udp_handler.py b/src/library/src/network/udp_handler.py index 4c27a3f4b..ad46fabdd 100644 --- a/src/library/src/network/udp_handler.py +++ b/src/library/src/network/udp_handler.py @@ -3,9 +3,7 @@ from library.src.abstractions.client import ClientBase from library.src.abstractions.connections import ConnectionBase, ServerBase -from library.src.extentions.string_extentions import IPEndPoint -from library.src.log.log_manager import LogWriter -from library.src.unispy_server_config import CONFIG, ServerConfig +from library.src.unispy_server_config import CONFIG class UdpConnection(ConnectionBase): @@ -15,12 +13,12 @@ def send(self, data) -> None: class UdpHandler(socketserver.BaseRequestHandler): - request: socket.socket + request: tuple[bytes, socket.socket] conn: UdpConnection def handle(self) -> None: data = self.request[0] - conn = UdpConnection(self, *self.server.handler_params) + conn = UdpConnection(self, *self.server.handler_params) # type: ignore conn.on_received(data) def send(self, data: bytes) -> None: @@ -34,9 +32,14 @@ def start(self) -> None: (self._config.public_address, self._config.listening_port), UdpHandler, ) - self._server.handler_params = (self._config, self._t_client, self._logger) + # inject the handler params to ThreadingUDPServer + self._server.handler_params = (self._config, self._client_cls, self._logger) # type: ignore + self._server.serve_forever() + def __exit__(self, *args): + self._server.__exit__(*args) + class TestClient(ClientBase): def create_switcher(self, buffer) -> None: @@ -52,6 +55,8 @@ def on_connected(self) -> None: if __name__ == "__main__": # create_udp_server(list(CONFIG.servers.values())[0], ClientBase) - s = UdpServer(list(CONFIG.servers.values())[0], TestClient, None) + from tests.mock_objects.general import LogMock + + s = UdpServer(list(CONFIG.servers.values())[0], TestClient, LogMock()) s.start() pass diff --git a/src/library/src/unispy_server_config.py b/src/library/src/unispy_server_config.py index 307bc48b5..ba5f7a847 100644 --- a/src/library/src/unispy_server_config.py +++ b/src/library/src/unispy_server_config.py @@ -9,7 +9,8 @@ class PostgreSql(BaseModel): server: str - port: int = Field(..., ge=1, le=65535) # Ensures port is between 1 and 65535 + # Ensures port is between 1 and 65535 + port: int = Field(..., ge=1, le=65535) database: str username: str password: str @@ -18,38 +19,38 @@ class PostgreSql(BaseModel): ssl_key: Optional[str] = None # Optional field for SSL key ssl_password: Optional[str] = None # Optional field for SSL password root_cert: Optional[str] = None # Optional field for root certificate - url: str = None # URL will be generated based on other fields - def model_post_init(self, __context: Any) -> None: - self.url = f"postgresql://{self.username}:{self.password}@{self.server}:{self.port}/{self.database}?sslmode={self.ssl_mode}" + @property + def url(self) -> str: + # fmt ignore + return f"postgresql://{self.username}:{self.password}@{self.server}:{self.port}/{self.database}?sslmode={self.ssl_mode}" class RedisConfig(BaseModel): server: str - port: int = Field(..., ge=1, le=65535) # Ensures port is between 1 and 65535 + # Ensures port is between 1 and 65535 + port: int = Field(..., ge=1, le=65535) user: str password: str ssl: bool # Use bool for SSL flag ssl_host: Optional[str] = None # Optional field for SSL host - url: str = None # URL will be generated based on other fields - def model_post_init(self, __context: Any) -> None: + @property + def url(self) -> str: if self.ssl: - self.url = ( + return ( f"rediss://{self.user}:{self.password}@{self.server}:{self.port}/0" ) else: - self.url = ( + return ( f"redis://{self.user}:{self.password}@{self.server}:{self.port}/0" ) class ServerConfig(BaseModel): server_id: UUID - server_name: str = constr(min_length=1) # Ensures server_name is a non-empty string - public_address: str = constr( - min_length=1 - ) # Ensures public_address is a non-empty string + server_name: str = Field(..., min_length=1) + public_address: str = Field(..., min_length=1) listening_port: int = Field( ..., ge=1, le=65535 ) # Ensures listening_port is between 1 and 65535 @@ -78,7 +79,6 @@ def model_post_init(self, __context: Any) -> None: url += f":{self.port}" if self.database is not None: url += f"/{self.database}" - return url class UniSpyServerConfig(BaseModel): @@ -87,7 +87,7 @@ class UniSpyServerConfig(BaseModel): backend: BackendConfig servers: dict[str, ServerConfig] = Field(default_factory=dict) mongodb: MongoDbConfig - + logging: LoggingConfig unispy_config = os.environ.get("UNISPY_CONFIG") diff --git a/src/servers/game_traffic_relay/src/applications/connection_listener.py b/src/servers/game_traffic_relay/src/applications/connection_listener.py index 04eeae052..492a40143 100644 --- a/src/servers/game_traffic_relay/src/applications/connection_listener.py +++ b/src/servers/game_traffic_relay/src/applications/connection_listener.py @@ -1,17 +1,21 @@ import socketserver -from library.src.extentions.string_extentions import IPEndPoint + +from library.src.network.udp_handler import UdpHandler class ConnectionListener: cookie: bytes - ip_end_point: IPEndPoint + ip_addr: str + port: int - def __init__(self, ip_end_point: IPEndPoint) -> None: - assert isinstance(ip_end_point, IPEndPoint) - self.ip_end_point = ip_end_point + def __init__(self, ip_addr: str, port: int) -> None: + assert isinstance(ip_addr, str) + assert isinstance(port, int) + self.ip_addr = ip_addr + self.port = port def start(self): with socketserver.ThreadingUDPServer( - self.ip_end_point.ip, self.ip_end_point.port + (self.ip_addr, self.port), UdpHandler ) as s: s.serve_forever() diff --git a/src/servers/natneg/src/abstractions/contracts.py b/src/servers/natneg/src/abstractions/contracts.py index 1e5313517..537f7a24e 100644 --- a/src/servers/natneg/src/abstractions/contracts.py +++ b/src/servers/natneg/src/abstractions/contracts.py @@ -8,12 +8,14 @@ RequestType, ResponseType, ) +from library.src.extentions.bytes_extentions import ip_to_4_bytes MAGIC_DATA = bytes([0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2]) class RequestBase(library.src.abstractions.contracts.RequestBase): version: int + #! check bytes order cookie: int """ byteorder: @@ -33,7 +35,8 @@ def parse(self) -> None: self.version = int(self.raw_request[6]) self.command_name = RequestType(self.raw_request[7]) - self.cookie = int.from_bytes(self.raw_request[8:12]) + self.cookie = int.from_bytes( + self.raw_request[8:12], byteorder="little") self.port_type = NatPortType(self.raw_request[12]) @@ -55,8 +58,8 @@ def __init__(self, request: RequestBase, result: ResultBase) -> None: def build(self) -> None: data = bytes() data += MAGIC_DATA - data += self._request.version.to_bytes(1, "little") - data += self._result.packet_type.value.to_bytes(1, "little") + data += self._request.version.to_bytes() + data += self._result.packet_type.value.to_bytes() data += self._request.cookie.to_bytes(4) self.sending_buffer = data @@ -84,9 +87,9 @@ def build(self) -> None: super().build() data = bytes() data += self.sending_buffer - data += self._request.port_type.value.to_bytes(1, "little") - data += self._request.client_index.value.to_bytes(1, "little") + data += self._request.port_type.value.to_bytes() + data += self._request.client_index.value.to_bytes() data += bytes(self._request.use_game_port) - data += socket.inet_aton(self._result.public_ip_addr) + data += ip_to_4_bytes(self._result.public_ip_addr) data += self._result.public_port.to_bytes(2) self.sending_buffer = data diff --git a/src/servers/natneg/tests/handler_tests.py b/src/servers/natneg/tests/handler_tests.py index 7726dca22..60b4412ae 100644 --- a/src/servers/natneg/tests/handler_tests.py +++ b/src/servers/natneg/tests/handler_tests.py @@ -1,24 +1,24 @@ import unittest from library.src.unispy_server_config import CONFIG -from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock from servers.natneg.src.contracts.requests import InitRequest -from servers.natneg.src.contracts.results import InitResult from servers.natneg.src.handlers.handlers import InitHandler import responses - -from servers.natneg.tests.mock_objects import ClientMock - - -def create_client(): - handler = RequestHandlerMock() - logger = LogMock() - conn = ConnectionMock( - handler=handler, - config=CONFIG.servers["NatNegotiation"], t_client=ClientMock, - logger=logger) - - return conn._client +from servers.natneg.src.contracts.requests import ( + AddressCheckRequest, + ErtAckRequest, + InitRequest, + NatifyRequest, + PreInitRequest, +) +from servers.natneg.src.enums.general import ( + NatClientIndex, + NatPortType, + PreInitState, + RequestType, +) + +from servers.natneg.tests.mock_objects import ClientMock, create_client class HandlerTests(unittest.TestCase): @@ -37,8 +37,23 @@ def init_test(self): url = f"{ CONFIG.backend.url}/{client.server_config.server_name}/{InitHandler._backend_url}/" responses.add(responses.POST, url, json={"message": "ok"}, status=200) - client.on_received(raw) + # test request parsing + request = InitRequest(raw) + request.parse() + cookie = 151191552 + self.assertEqual(cookie, request.cookie) + self.assertEqual(RequestType.INIT, request.command_name) + self.assertEqual(NatClientIndex.GAME_CLIENT, request.client_index) + self.assertEqual(False, request.use_game_port) + self.assertEqual(3, request.version) + self.assertEqual(NatPortType.NN1, request.port_type) + handler = InitHandler(client, request) + handler.handle() + + # test response constructing + self.assertTrue(handler._response.sending_buffer == + b'\xfd\xfc\x1efj\xb2\x03\x01\x00\x00\x03\t\x01\x00\xc0\xa8\x00\x01\x00\x00') if __name__ == "__main__": diff --git a/src/servers/natneg/tests/mock_objects.py b/src/servers/natneg/tests/mock_objects.py index 689bc650d..fbf31c16f 100644 --- a/src/servers/natneg/tests/mock_objects.py +++ b/src/servers/natneg/tests/mock_objects.py @@ -1,6 +1,19 @@ +from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock +from library.src.unispy_server_config import CONFIG from servers.natneg.src.applications.client import Client class ClientMock(Client): - + pass + + +def create_client(): + handler = RequestHandlerMock() + logger = LogMock() + conn = ConnectionMock( + handler=handler, + config=CONFIG.servers["NatNegotiation"], t_client=ClientMock, + logger=logger) + + return conn._client diff --git a/src/servers/presence_connection_manager/src/abstractions/contracts.py b/src/servers/presence_connection_manager/src/abstractions/contracts.py index 3967050bb..004b8af5c 100644 --- a/src/servers/presence_connection_manager/src/abstractions/contracts.py +++ b/src/servers/presence_connection_manager/src/abstractions/contracts.py @@ -6,7 +6,7 @@ from servers.presence_search_player.src.exceptions.general import ( GPParseException, ) -from typing import Dict +from typing import Dict, Optional import library.src.abstractions.contracts @@ -26,7 +26,7 @@ class RequestBase(library.src.abstractions.contracts.RequestBase, abc.ABC): raw_request: str request_key_values: Dict[str, str] - def __init__(self, raw_request: "str") -> None: + def __init__(self, raw_request: str) -> None: assert isinstance(raw_request, str) super().__init__(raw_request) @@ -50,7 +50,7 @@ class ResponseBase(library.src.abstractions.contracts.ResponseBase, abc.ABC): _result: ResultBase sending_buffer: str - def __init__(self, request: RequestBase, result: ResultBase) -> None: + def __init__(self, request: RequestBase, result: Optional[ResultBase]) -> None: assert issubclass(type(request), RequestBase) assert issubclass(type(result), ResultBase) super().__init__(request, result) diff --git a/src/servers/presence_connection_manager/src/abstractions/handler.py b/src/servers/presence_connection_manager/src/abstractions/handler.py index f06a69494..f9646e368 100644 --- a/src/servers/presence_connection_manager/src/abstractions/handler.py +++ b/src/servers/presence_connection_manager/src/abstractions/handler.py @@ -8,7 +8,7 @@ RequestBase, ResultBase, ) -import library +import library.src.abstractions.handler class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase): diff --git a/src/servers/presence_connection_manager/src/aggregates/user_status.py b/src/servers/presence_connection_manager/src/aggregates/user_status.py index 077528dab..ef387b49a 100644 --- a/src/servers/presence_connection_manager/src/aggregates/user_status.py +++ b/src/servers/presence_connection_manager/src/aggregates/user_status.py @@ -3,7 +3,6 @@ from servers.presence_connection_manager.src.enums.general import GPStatusCode -@dataclass class UserStatus: status_string: str location_string: str diff --git a/src/servers/presence_connection_manager/src/aggregates/user_status_info.py b/src/servers/presence_connection_manager/src/aggregates/user_status_info.py index 5a6d03416..553659349 100644 --- a/src/servers/presence_connection_manager/src/aggregates/user_status_info.py +++ b/src/servers/presence_connection_manager/src/aggregates/user_status_info.py @@ -1,7 +1,6 @@ from dataclasses import dataclass -@dataclass class UserStatusInfo: status_state: str buddy_ip: str @@ -15,3 +14,6 @@ class UserStatusInfo: game_variant: str game_map_name: str quiet_mode_flags: str + + def __init__(self) -> None: + pass diff --git a/src/servers/presence_connection_manager/src/applications/client.py b/src/servers/presence_connection_manager/src/applications/client.py index e2439fc48..966007e88 100644 --- a/src/servers/presence_connection_manager/src/applications/client.py +++ b/src/servers/presence_connection_manager/src/applications/client.py @@ -49,9 +49,11 @@ def on_connected(self) -> None: "The server challenge has already been sent. Cannot send another login challenge." ) self.info.login_status = LoginStatus.PROCESSING - buffer = f"\\lc\\1\\challenge\\{SERVER_CHALLENGE}\\id\\1\\final\\" - self.send(buffer) + buffer = f"\\lc\\1\\challenge\\{SERVER_CHALLENGE}\\id\\1\\final\\".encode( + "ascii" + ) self.log_network_sending(buffer) + self.connection.send(buffer) def create_switcher(self, buffer) -> "SwitcherBase": return Switcher(self, buffer) diff --git a/src/servers/presence_connection_manager/src/applications/data.py b/src/servers/presence_connection_manager/src/applications/data.py index 6a6fa91af..da4cab584 100644 --- a/src/servers/presence_connection_manager/src/applications/data.py +++ b/src/servers/presence_connection_manager/src/applications/data.py @@ -17,8 +17,7 @@ def is_email_exist(email: str) -> bool: def delete_friend_by_profile_id(profile_id: int): - friend = PG_SESSION.query(Friends).filter( - Friends.friendid == profile_id).first() + friend = PG_SESSION.query(Friends).filter(Friends.friendid == profile_id).first() if friend is None: raise GPDatabaseException( f"friend deletion have errors on profile id:{profile_id}" @@ -82,8 +81,7 @@ def get_user_info_list(email: str, nick_name: str) -> list[tuple[int, int, int]] of users that match the provided email and nickname in the database. """ result = ( - PG_SESSION.query(Users.userid, Profiles.profileid, - SubProfiles.subprofileid) + PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .filter(Users.email == email, Profiles.nick == nick_name) @@ -94,8 +92,7 @@ def get_user_info_list(email: str, nick_name: str) -> list[tuple[int, int, int]] def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: result = ( - PG_SESSION.query(Users.userid, Profiles.profileid, - SubProfiles.subprofileid) + PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .filter( @@ -109,8 +106,7 @@ def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: def get_user_infos(unique_nick: str, namespace_id: int) -> list[tuple[int, int, int]]: result = ( - PG_SESSION.query(Users.userid, Profiles.profileid, - SubProfiles.subprofileid) + PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .filter( @@ -133,8 +129,7 @@ def update_block_info_list(target_id: int, profile_id: int, namespace_id: int) - .count() ) if result == 0: - b = Blocked(targetid=target_id, namespaceid=namespace_id, - profileid=profile_id) + b = Blocked(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) PG_SESSION.add(b) PG_SESSION.commit() @@ -149,8 +144,7 @@ def update_friend_info(target_id: int, profile_id: int, namespace_id: int): ) .count() ) - f = Friends(targetid=target_id, namespaceid=namespace_id, - profileid=profile_id) + f = Friends(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) if result == 0: PG_SESSION.add(f) diff --git a/src/servers/presence_connection_manager/src/contracts/requests/buddy.py b/src/servers/presence_connection_manager/src/contracts/requests/buddy.py index 53c109dec..00fc40da5 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests/buddy.py +++ b/src/servers/presence_connection_manager/src/contracts/requests/buddy.py @@ -1,6 +1,9 @@ +from typing import Optional from servers.presence_connection_manager.src.abstractions.contracts import RequestBase from servers.presence_connection_manager.src.aggregates.user_status import UserStatus -from servers.presence_connection_manager.src.aggregates.user_status_info import UserStatusInfo +from servers.presence_connection_manager.src.aggregates.user_status_info import ( + UserStatusInfo, +) from servers.presence_connection_manager.src.enums.general import GPStatusCode from servers.presence_search_player.src.exceptions.general import GPParseException @@ -66,12 +69,13 @@ def parse(self): class StatusInfoRequest(RequestBase): - def __init__(self): - super().__init__() - self.is_get_status_info = False - self.profile_id = 0 - self.namespace_id = None - self.status_info = UserStatusInfo() + namespace_id: Optional[int] = None + status_info: UserStatusInfo = UserStatusInfo() + profile_id: int = 0 + + def __init__(self, raw_request: Optional[str] = None) -> None: + if raw_request is not None: + self.raw_request = raw_request def parse(self): super().parse() @@ -97,7 +101,7 @@ def parse(self): try: self.status_info.query_report_port = int(self.request_key_values["qport"]) self.status_info.host_port = int(self.request_key_values["hport"]) - self.status_info.session_flags = int(self.request_key_values["sessflags"]) + self.status_info.session_flags = self.request_key_values["sessflags"] except ValueError: raise GPParseException("qport, hport, or sessflags format is incorrect.") @@ -108,10 +112,11 @@ def parse(self): class StatusRequest(RequestBase): + status: UserStatus + is_get_status: bool + def __init__(self, raw_request): super().__init__(raw_request) - self.status = UserStatus() - self.IsGetStatus = False def parse(self): super().parse() diff --git a/src/servers/presence_connection_manager/src/contracts/requests/general.py b/src/servers/presence_connection_manager/src/contracts/requests/general.py index dd12e80c9..ace4e90a2 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests/general.py +++ b/src/servers/presence_connection_manager/src/contracts/requests/general.py @@ -29,7 +29,7 @@ class LoginRequest(RequestBase): user_id: int profile_id: int partner_id: int - game_name: int + game_name: str quiet_mode_flags: int firewall: bool @@ -106,7 +106,7 @@ def parse_other_data(self): self.product_id = product_id if "firewall" in self.request_key_values: - self.firewall = self.request_key_values["firewall"] + self.firewall = bool(self.request_key_values["firewall"]) if "quiet" in self.request_key_values: quiet = int(self.request_key_values["quiet"]) diff --git a/src/servers/presence_connection_manager/src/contracts/requests/profile.py b/src/servers/presence_connection_manager/src/contracts/requests/profile.py index 11d85ab3a..12e67f103 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests/profile.py +++ b/src/servers/presence_connection_manager/src/contracts/requests/profile.py @@ -1,3 +1,4 @@ +from typing import Optional from library.src.extentions.gamespy_utils import is_valid_date from servers.presence_connection_manager.src.abstractions.contracts import RequestBase from servers.presence_connection_manager.src.enums.general import PublicMasks @@ -121,30 +122,30 @@ def parse(self): class UpdateProfileRequest(RequestBase): - has_public_mask_flag: bool = None - public_mask: PublicMasks = None - session_key: str = None - partner_id: int = None - nick: str = None - uniquenick: str = None - has_first_name_flag: bool = None - first_name: str = None - has_last_name_flag: bool = None - last_name: str = None - has_icq_flag: bool = None - icq_uin: int = None - has_home_page_flag: bool = None - home_page: str = None + has_public_mask_flag: Optional[bool] = None + public_mask: Optional[PublicMasks] = None + session_key: Optional[str] = None + partner_id: Optional[int] = None + nick: Optional[str] = None + uniquenick: Optional[str] = None + has_first_name_flag: Optional[bool] = None + first_name: Optional[str] = None + has_last_name_flag: Optional[bool] = None + last_name: Optional[str] = None + has_icq_flag: Optional[bool] = None + icq_uin: Optional[int] = None + has_home_page_flag: Optional[bool] = None + home_page: Optional[str] = None has_birthday_flag: bool = False - birth_day: int = None - birth_month: int = None - birth_year: int = None + birth_day: Optional[int] = None + birth_month: Optional[int] = None + birth_year: Optional[int] = None has_sex_flag: bool = False - sex: bool = None + sex: Optional[int] = None has_zip_code: bool = False - zip_code: str = None + zip_code: Optional[str] = None has_country_code: bool = False - country_code: str = None + country_code: Optional[str] = None def parse(self): super().parse() @@ -221,15 +222,15 @@ def parse(self): class UpdateUiRequest(RequestBase): - cpubrandid: str = None - cpuspeed: str = None - memory: str = None - videocard1ram: str = None - videocard2ram: str = None - connectionid: str = None - connectionspeed: str = None - hasnetwork: str = None - pic: str = None + cpubrandid: Optional[str] = None + cpuspeed: Optional[str] = None + memory: Optional[str] = None + videocard1ram: Optional[str] = None + videocard2ram: Optional[str] = None + connectionid: Optional[str] = None + connectionspeed: Optional[str] = None + hasnetwork: Optional[str] = None + pic: Optional[str] = None def parse(self): super().parse() diff --git a/src/servers/presence_connection_manager/src/contracts/responses/general.py b/src/servers/presence_connection_manager/src/contracts/responses/general.py index 9b4af2e5c..6c22a8683 100644 --- a/src/servers/presence_connection_manager/src/contracts/responses/general.py +++ b/src/servers/presence_connection_manager/src/contracts/responses/general.py @@ -1,5 +1,3 @@ -from servers.chat.src.contracts.requests.general import LoginRequest -from servers.chat.src.contracts.results.general import LoginResult from servers.presence_connection_manager.src.abstractions.contracts import ResponseBase from servers.presence_connection_manager.src.applications.client import ( LOGIN_TICKET, @@ -7,6 +5,10 @@ ) from servers.presence_connection_manager.src.contracts.requests.general import ( KeepAliveRequest, + LoginRequest, +) +from servers.presence_connection_manager.src.contracts.results.general import ( + LoginResult, ) from servers.presence_search_player.src.contracts.requests import ( NewUserRequest, @@ -27,24 +29,18 @@ def build(self) -> None: class LoginResponse(ResponseBase): _result: LoginResult + _request: LoginRequest def __init__(self, request: LoginRequest, result: LoginResult): super().__init__(request, result) + assert isinstance(request, LoginRequest) + assert isinstance(result, LoginResult) def build(self): # string checkSumStr = _result.DatabaseResults.Nick + _result.DatabaseResults.UniqueNick + _result.DatabaseResults.NamespaceID; # _connection.UserData.SessionKey = _crc.ComputeChecksum(checkSumStr); - self.sending_buffer = ( - "\\lc\\2\\sesskey\\" - + SESSION_KEY - + "\\proof\\" - + self._result.response_proof - + "\\userid\\" - + self._result.data.user_id - + "\\profileid\\" - + self._result.data.profile_id - ) + self.sending_buffer = f"\\lc\\2\\sesskey\\{SESSION_KEY}\\proof\\{self._result.response_proof}\\userid\\{self._result.data.user_id}\\profileid\\{self._result.data.profile_id}" if self._result.data.unique_nick is not None: self.sending_buffer += "\\uniquenick\\" + self._result.data.unique_nick @@ -58,4 +54,4 @@ class NewUserResponse(NUR): _request: NewUserRequest def build(self): - self.sending_buffer = f"\\nur\\userid\\{self._result.user.userid}\\profileid\\{self._result.sub_profile.profileid}\\id\\{self._request.operation_id}\\final\\" + self.sending_buffer = f"\\nur\\userid\\{self._result.user.userid}\\profileid\\{self._result.subprofiles.profileid}\\id\\{self._request.operation_id}\\final\\" diff --git a/src/servers/presence_connection_manager/src/contracts/responses/profile.py b/src/servers/presence_connection_manager/src/contracts/responses/profile.py index 00664f8e3..61a29edcc 100644 --- a/src/servers/presence_connection_manager/src/contracts/responses/profile.py +++ b/src/servers/presence_connection_manager/src/contracts/responses/profile.py @@ -1,7 +1,14 @@ from library.src.extentions.gamespy_ramdoms import StringType, generate_random_string from servers.presence_connection_manager.src.abstractions.contracts import ResponseBase -from servers.presence_connection_manager.src.contracts.requests.profile import GetProfileRequest, NewProfileRequest, RegisterNickRequest -from servers.presence_connection_manager.src.contracts.results.profile import GetProfileResult, NewProfileResult +from servers.presence_connection_manager.src.contracts.requests.profile import ( + GetProfileRequest, + NewProfileRequest, + RegisterNickRequest, +) +from servers.presence_connection_manager.src.contracts.results.profile import ( + GetProfileResult, + NewProfileResult, +) class GetProfileResponse(ResponseBase): @@ -15,32 +22,19 @@ def __init__(self, request: GetProfileRequest, result: GetProfileResult): def build(self): self.sending_buffer = ( - "\\pi\\profileid\\" - + str(self._result.user_profile.profile_id) - + "\\nick\\" - + self._result.user_profile.nick - + "\\uniquenick\\" - + self._result.user_profile.unique_nick - + "\\email\\" - + self._result.user_profile.email - + "\\firstname\\" - + self._result.user_profile.firstname - + "\\lastname\\" - + self._result.user_profile.lastname - + "\\icquin\\" - + self._result.user_profile.icquin - + "\\homepage\\" - + self._result.user_profile.homepage - + "\\zipcode\\" - + self._result.user_profile.zipcode - + "\\countrycode\\" - + self._result.user_profile.countrycode - + "\\lon\\" - + str(self._result.user_profile.longitude) - + "\\lat\\" - + str(self._result.user_profile.latitude) - + "\\loc\\" - + self._result.user_profile.location + f"\\pi\\profileid\\{self._result.user_profile.profile_id}" + + f"\\nick\\{self._result.user_profile.nick}" + + f"\\uniquenick\\{self._result.user_profile.unique_nick}" + + f"\\email\\{self._result.user_profile.email}" + + f"\\firstname\\{self._result.user_profile.firstname}" + + f"\\lastname\\{self._result.user_profile.lastname}" + + f"\\icquin\\{self._result.user_profile.icquin}" + + f"\\homepage\\{self._result.user_profile.homepage}" + + f"\\zipcode\\{self._result.user_profile.zipcode}" + + f"\\countrycode\\{self._result.user_profile.countrycode}" + + f"\\lon\\{self._result.user_profile.longitude}" + + f"\\lat\\{self._result.user_profile.latitude}" + + f"\\loc\\{self._result.user_profile.location}" ) birth_str = ( @@ -48,22 +42,23 @@ def build(self): | (self._result.user_profile.birthmonth << 16) | self._result.user_profile.birthyear ) - self.sending_buffer += "\\birthday\\" + str(birth_str) - - self.sending_buffer += "\\sex\\" + self._result.user_profile.sex - self.sending_buffer += "\\publicmask\\" + self._result.user_profile.publicmask - self.sending_buffer += "\\aim\\" + self._result.user_profile.aim - self.sending_buffer += "\\picture\\" + self._result.user_profile.picture - self.sending_buffer += "\\ooc" + str(self._result.user_profile.occupationid) - self.sending_buffer += "\\ind\\" + str(self._result.user_profile.industryid) - self.sending_buffer += "\\inc\\" + str(self._result.user_profile.incomeid) - self.sending_buffer += "\\mar\\" + str(self._result.user_profile.marriedid) - self.sending_buffer += "\\chc\\" + str(self._result.user_profile.childcount) - self.sending_buffer += "\\i1\\" + self._result.user_profile.interests1 - self.sending_buffer += "\\o1\\" + self._result.user_profile.ownership1 - self.sending_buffer += "\\conn\\" + self._result.user_profile.connectiontype - self.sending_buffer += "\\sig\\+" + generate_random_string(10, StringType.HEX) - self.sending_buffer += "\\id\\" + str(self._request.operation_id) + "\\final\\" + self.sending_buffer += ( + f"\\birthday\\{birth_str}" + f"\\sex\\{self._result.user_profile.sex}" + + f"\\publicmask\\{self._result.user_profile.publicmask}" + + f"\\aim\\{self._result.user_profile.aim}" + + f"\\picture\\{self._result.user_profile.picture}" + + f"\\ooc{self._result.user_profile.occupationid}" + + f"\\ind\\{self._result.user_profile.industryid}" + + f"\\inc\\{self._result.user_profile.incomeid}" + + f"\\mar\\{self._result.user_profile.marriedid}" + + f"\\chc\\{self._result.user_profile.childcount}" + + f"\\i1\\{self._result.user_profile.interests1}" + + f"\\o1\\{self._result.user_profile.ownership1}" + + f"\\conn\\{self._result.user_profile.connectiontype}" + + f"\\sig\\+{generate_random_string(10, StringType.HEX)}" + + f"\\id\\{self._request.operation_id}\\final\\" + ) class NewProfileResponse(ResponseBase): diff --git a/src/servers/presence_connection_manager/src/contracts/results/profile.py b/src/servers/presence_connection_manager/src/contracts/results/profile.py index 27876e6e3..3530b265e 100644 --- a/src/servers/presence_connection_manager/src/contracts/results/profile.py +++ b/src/servers/presence_connection_manager/src/contracts/results/profile.py @@ -1,39 +1,40 @@ +from typing import Optional from servers.presence_connection_manager.src.abstractions.contracts import ResultBase class GetProfileDataModel: - nick: str = None - profile_id: int = None - unique_nick: str = None - email: str = None - firstname: str = None - lastname: str = None - icquin: int = None - homepage: str = None - zipcode: str = None - countrycode: str = None - longitude: float = None - latitude: float = None - location: str = None - birthday: int = None - birthmonth: int = None - birthyear: int = None - sex: int = None - publicmask: int = None - aim: str = None - picture: int = None - occupationid: int = None - industryid: int = None - incomeid: int = None - marriedid: int = None - childcount: int = None - interests1: int = None - ownership1: int = None - connectiontype: int = None + nick: Optional[str] = None + profile_id: Optional[int] = None + unique_nick: Optional[str] = None + email: Optional[str] = None + firstname: Optional[str] = None + lastname: Optional[str] = None + icquin: Optional[int] = None + homepage: Optional[str] = None + zipcode: Optional[str] = None + countrycode: Optional[str] = None + longitude: Optional[float] = None + latitude: Optional[float] = None + location: Optional[str] = None + birthday: Optional[int] = None + birthmonth: Optional[int] = None + birthyear: Optional[int] = None + sex: Optional[int] = None + publicmask: Optional[int] = None + aim: Optional[str] = None + picture: Optional[int] = None + occupationid: Optional[int] = None + industryid: Optional[int] = None + incomeid: Optional[int] = None + marriedid: Optional[int] = None + childcount: Optional[int] = None + interests1: Optional[int] = None + ownership1: Optional[int] = None + connectiontype: Optional[int] = None class GetProfileResult(ResultBase): - user_profile: GetProfileDataModel = None + user_profile: Optional[GetProfileDataModel] = None class NewProfileResult(ResultBase): diff --git a/src/servers/presence_connection_manager/src/enums/general.py b/src/servers/presence_connection_manager/src/enums/general.py index 397c346ea..bad487837 100644 --- a/src/servers/presence_connection_manager/src/enums/general.py +++ b/src/servers/presence_connection_manager/src/enums/general.py @@ -59,97 +59,6 @@ class GenderType(IntEnum): PAT = 2 -class GPBasic(IntEnum): - INFO_CACHING = 0x0100 - SIMULATION = 0x0101 - INFO_CACHING_BUDDY_AND_BLOCK_ONLY = 0x0102 - BLOCKING = 0x0103 - NON_BLOCKING = 0x0104 - FIREWALL = 0x0105 - NO_FIREWALL = 0x0106 - CHECK_CACHE = 0x0107 - DONT_CHECK_CACHE = 0x0108 - EMAIL_VALID = 0x0109 - EMAIL_INVALID = 0x010A - FATAL = 0x010B - NON_FATAL = 0x010C - MALE = 0x0500 - FEMALE = 0x0501 - PAT = 0x0502 - MORE = 0x0600 - DONE = 0x0601 - NICK = 0x0700 - UNIQUENICK = 0x0701 - EMAIL = 0x0702 - PASSWORD = 0x0703 - FIRST_NAME = 0x0704 - LAST_NAME = 0x0705 - ICQ_UIN = 0x0706 - HOMEPAGE = 0x0707 - ZIP_CODE = 0x0708 - COUNTRY_CODE = 0x0709 - BIRTHDAY = 0x070A - SEX = 0x070B - CPU_BRAND = 0x070C - CPU_SPEED = 0x070D - MEMORY = 0x070E - VIDEO_CARD1_STRING = 0x070F - VIDEO_CARD1_RAM = 0x0710 - VIDEO_CARD2_STRING = 0x0711 - VIDEO_CARD2_RAM = 0x0712 - CONNECTION_ID = 0x0713 - CONNECTION_SPEED = 0x0714 - HAS_NETWORK = 0x0715 - OS_STRING = 0x0716 - AIM_NAME = 0x0717 - PIC = 0x0718 - OCCUPATION_ID = 0x0719 - INDUSTRY_ID = 0x071A - INCOME_ID = 0x071B - MARRIED_ID = 0x071C - CHILD_COUNT = 0x071D - INTEREST1 = 0x071E - REPLACE = 0x0800 - DONT_REPLACE = 0x0801 - CONNECTED = 0x0900 - NOT_CONNECTED = 0x0901 - MASK_NONE = 0x0A00 - MASK_HOMEPAGE = 0x0A01 - MASK_ZIP_CODE = 0x0A02 - MASK_COUNTRY_CODE = 0x0A03 - MASK_BIRTHDAY = 0x0A04 - MASK_SEX = 0x0A05 - MASK_EMAIL = 0x0A06 - MASK_ALL = 0x0A07 - SESS_IS_CLOSED = 0x0B00 - SESS_IS_OPEN = 0x0B01 - SESS_HAS_PASSWORD = 0x0B02 - SESS_IS_BEHIND_NAT = 0x0B03 - SESS_IS_RANKED = 0x0B04 - INTEL = 0x0C00 - AMD = 0x0C01 - CYRIX = 0x0C02 - MOTOROLA = 0x0C03 - ALPHA = 0x0C04 - MODEM = 0x0D00 - ISDN = 0x0D01 - CABLE_MODEM = 0x0D02 - DSL = 0x0D03 - SATELLITE = 0x0D04 - ETHERNET = 0x0D05 - WIRELESS = 0x0D06 - TRANSFER_SEND_REQUEST = 0x0E00 - TRANSFER_ACCEPTED = 0x0E01 - TRANSFER_REJECTED = 0x0E02 - TRANSFER_NOT_ACCEPTING = 0x0E03 - TRANSFER_NO_CONNECTION = 0x0E04 - TRANSFER_DONE = 0x0E05 - TRANSFER_CANCELLED = 0x0E06 - TRANSFER_LOST_CONNECTION = 0x0E07 - TRANSFER_ERROR = 0x0E08 - TRANSFER_THROTTLE = 0x0E09 - - class GPStatusCode(IntEnum): OFFLINE = 0 ONLINE = 1 @@ -172,17 +81,6 @@ class LoginType(IntEnum): AUTH_TOKEN = 2 -class PublicMasks(IntFlag): - NONE = 0x00000000 - HOMEPAGE = 0x00000001 - ZIP_CODE = 0x00000002 - COUNTRY_CODE = 0x00000004 - BIRTHDAY = 0x00000008 - SEX = 0x00000010 - EMAIL = 0x00000020 - ALL = 0xFFFFFFFF - - class QuietModeType(IntFlag): SILENCE_NONE = 0x00000000 SILENCE_MESSAGE = 0x00000001 @@ -327,7 +225,7 @@ class GPBasic(IntEnum): class GPPartnerId(IntEnum): - GAMESPY = (0,) + GAMESPY = 0 IGN = 10 diff --git a/src/servers/presence_connection_manager/src/handlers/buddy.py b/src/servers/presence_connection_manager/src/handlers/buddy.py index c08308e4e..04d38fd57 100644 --- a/src/servers/presence_connection_manager/src/handlers/buddy.py +++ b/src/servers/presence_connection_manager/src/handlers/buddy.py @@ -40,7 +40,8 @@ def _response_construct(self) -> None: class BuddyListHandler(LoginHandlerBase): - _result: BuddyListResult = BuddyListResult() + _result: BuddyListResult + _result_cls = BuddyListResult def __init__(self, client: Client): assert isinstance(client, Client) diff --git a/src/servers/query_report/src/aggregates/game_server_info.py b/src/servers/query_report/src/aggregates/game_server_info.py index 7f1c23905..75b1b04b2 100644 --- a/src/servers/query_report/src/aggregates/game_server_info.py +++ b/src/servers/query_report/src/aggregates/game_server_info.py @@ -2,6 +2,7 @@ import socket from uuid import UUID +from library.src.extentions.bytes_extentions import ip_to_4_bytes from servers.query_report.src.v2.enums.general import GameServerStatus @@ -17,9 +18,11 @@ class GameServerInfo: server_data: dict[str, str] player_data: list[dict[str, str]] team_data: list[dict[str, str]] + @property def query_report_port_bytes(self) -> bytes: return self.query_report_port.to_bytes(2, "big") + @property def host_ip_address_bytes(self) -> bytes: - return socket.inet_aton(self.host_ip_address) + return ip_to_4_bytes(self.host_ip_address) diff --git a/src/servers/query_report/src/v2/abstractions/contracts.py b/src/servers/query_report/src/v2/abstractions/contracts.py index dc8dd0b32..4dd3544c6 100644 --- a/src/servers/query_report/src/v2/abstractions/contracts.py +++ b/src/servers/query_report/src/v2/abstractions/contracts.py @@ -16,20 +16,18 @@ def __init__(self, raw_request: bytes) -> None: def parse(self): if len(self.raw_request) < 3: - raise QRException + raise QRException("request length not valid") self.command_name = RequestType(self.raw_request[0]) self.instant_key = int(self.raw_request[1:5]) - import abc import library.src.abstractions.contracts from servers.query_report.src.v2.enums.general import PacketType class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): - packet_type: PacketType = None - + packet_type: PacketType import abc @@ -39,3 +37,4 @@ class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): class ResponseBase(library.src.abstractions.contracts.ResponseBase, abc.ABC): _result: ResultBase _request: RequestBase + sending_buffer: bytes diff --git a/src/servers/query_report/src/v2/contracts/requests.py b/src/servers/query_report/src/v2/contracts/requests.py index 92aa0963f..8b0d6dfd3 100644 --- a/src/servers/query_report/src/v2/contracts/requests.py +++ b/src/servers/query_report/src/v2/contracts/requests.py @@ -33,14 +33,14 @@ class ClientMessageAckRequest(RequestBase): class ClientMessageRequest(RequestBase): server_browser_sender_id: UUID - natneg_message: bytes - target_ip_address: bytes - target_port: bytes + natneg_message: list[int] + target_ip_address: str + target_port: str message_key: int @property def cookie(self): - return self.natneg_message[6:10] + return int.from_bytes(self.natneg_message[6:10], "little") class HeartBeatRequest(RequestBase): @@ -110,7 +110,7 @@ def parse_server_data(self, server_data_str: str): temp_value = key_value_array[i + 1] if temp_key == "": - LogWriter.LogVerbose("Skipping empty key value") + LogWriter.debug("Skipping empty key value") continue if temp_key in self.server_data: diff --git a/src/servers/query_report/src/v2/contracts/responses.py b/src/servers/query_report/src/v2/contracts/responses.py index 9db0b4fd6..28a288d02 100644 --- a/src/servers/query_report/src/v2/contracts/responses.py +++ b/src/servers/query_report/src/v2/contracts/responses.py @@ -1,3 +1,4 @@ +import socket from library.src.extentions.encoding import get_bytes from servers.presence_connection_manager.src.contracts.requests.general import ( KeepAliveRequest, @@ -9,9 +10,12 @@ ClientMessageRequest, HeartBeatRequest, ) -from servers.query_report.src.v2.contracts.results import ChallengeResult, HeartBeatResult +from servers.query_report.src.v2.contracts.results import ( + ChallengeResult, + HeartBeatResult, +) from servers.query_report.src.v2.enums.general import ServerAvailability - +from library.src.extentions.bytes_extentions import ip_to_4_bytes, port_to_2_bytes RESPONSE_PREFIX = bytes([0xFE, 0xFD, 0x09, 0x00, 0x00, 0x00]) @@ -79,9 +83,9 @@ def build(self) -> None: data = bytearray() data.extend(self.sending_buffer) data.extend(CHALLENGE) - data.extend(self._result.remote_ip_endpoint.get_ip_bytes()) + data.extend(ip_to_4_bytes(self._result.remote_ip_address)) data.extend(SPLITER) - data.extend(self._result.remote_ip_endpoint.get_port_bytes()) + data.extend(port_to_2_bytes(self._result.remote_port)) self.sending_buffer = bytes(data) diff --git a/src/servers/server_browser/src/v2/handlers/handlers.py b/src/servers/server_browser/src/v2/handlers/handlers.py index 53ff6ce64..4f9eb4cb5 100644 --- a/src/servers/server_browser/src/v2/handlers/handlers.py +++ b/src/servers/server_browser/src/v2/handlers/handlers.py @@ -1,4 +1,5 @@ from concurrent.futures import ProcessPoolExecutor +import socket from servers.query_report.src.aggregates.game_server_info import GameServerInfo from servers.query_report.src.v2.contracts.requests import ClientMessageRequest from servers.query_report.src.v2.enums.general import GameServerStatus diff --git a/src/servers/webservices/src/aggregations/soap_envelop.py b/src/servers/webservices/src/aggregations/soap_envelop.py index 4b25e9d47..2c233258f 100644 --- a/src/servers/webservices/src/aggregations/soap_envelop.py +++ b/src/servers/webservices/src/aggregations/soap_envelop.py @@ -1,3 +1,4 @@ +from typing import Optional import xml.etree.ElementTree as ET @@ -15,12 +16,11 @@ def __init__(self, body_namespace: str): self.current_element = self.body def finish_add_sub_element(self): - self.current_element = ET.SubElement( - self.current_element, - ) + self.current_element = ET.SubElement(self.current_element,) def change_to_element(self, name: str): - self.current_element = self.body.find(f".//{{{self._body_namespace}}}{name}") + self.current_element = self.body.find( + f".//{{{self._body_namespace}}}{name}") def back_to_parent_element(self): self.current_element = self.body diff --git a/src/servers/webservices/tests/auth.py b/src/servers/webservices/tests/auth.py index e69de29bb..26e2cf3c1 100644 --- a/src/servers/webservices/tests/auth.py +++ b/src/servers/webservices/tests/auth.py @@ -0,0 +1,118 @@ +import unittest + +import responses + +from servers.webservices.src.modules.auth.contracts.requests import LoginProfileWithGameIdRequest, LoginUniqueNickRequest +from servers.webservices.src.modules.auth.handlers.general import LoginProfileWithGameIdHandler + +LOGIN_PROFILE = """ + + + + 1 + 0 + 0 + 0 + spyguy@unispy.org + spyguy + XXXXXXXXXXX + + XXXXXXXXXXX + + + + """ +LOGIN_PS3_CERT = """ + + + + 0 + 0 + 0 + 0001 + 0 + 0001 + + + """ + +LOGIN_REMOTE_AUTH = """ + + + + 1 + 0 + 0 + 0 + XXXXXXXXXXX + XXXXXXXXXXX + + + """ + +LOGIN_UNIQUENICK = """ + + + + 1 + 0 + 0 + spyguy + + XXXXXXXXXXX + + + + """ + + +class AuthTest(unittest.TestCase): + def CrysisAuth(self): + raw = """ + + + + 1 + 95 + 95 + spyguy + + 0000 + + + + """ + + request = LoginUniqueNickRequest(raw) + request.parse() + + @responses.activate + def login_profile(self): + request = LoginProfileWithGameIdRequest(LOGIN_PROFILE) + handler= LoginProfileWithGameIdHandler(request) + + +if __name__ == "__main__": + t = AuthTest() + t.CrysisAuth() diff --git a/src/tests/library/encrypt_test.py b/src/tests/library/encrypt_test.py index 3bdf6626d..c3b904901 100644 --- a/src/tests/library/encrypt_test.py +++ b/src/tests/library/encrypt_test.py @@ -5,7 +5,7 @@ class GSEncryptionTest(unittest.TestCase): def test_encryption(self): enc = ChatCrypt("123345") - result = enc.encrypt("hello") + result = enc.encrypt("hello".encode("ascii")) self.assertEqual(result, b"\xe9D\x91Q\xb9") def test_decryption(self): @@ -14,6 +14,5 @@ def test_decryption(self): self.assertEqual(result, "hello") - -if __name__ == '__main__': - unittest.main() \ No newline at end of file +if __name__ == "__main__": + unittest.main() From 58cdb61906d5eeceaba7738c8d7c65a2f15f6510 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 16 Aug 2024 07:31:33 +0000 Subject: [PATCH 088/231] fix(test): bytes convertion error --- src/library/src/abstractions/client.py | 16 ++++----- src/library/src/abstractions/contracts.py | 2 +- src/library/src/abstractions/handler.py | 10 +++--- src/library/tests/mock_objects/general.py | 12 ++++++- src/servers/natneg/src/contracts/requests.py | 2 +- src/servers/natneg/tests/handler_tests.py | 34 +++++++++++++++++--- src/servers/natneg/tests/mock_objects.py | 2 +- 7 files changed, 56 insertions(+), 22 deletions(-) diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index fb1035320..696a4e977 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -34,7 +34,7 @@ def __init__( self.connection = connection self.logger = logger - self.__log_prefix = f"[{self.connection.remote_ip}:{ + self._log_prefix = f"[{self.connection.remote_ip}:{ self.connection.remote_port}]" def on_connected(self) -> None: @@ -81,25 +81,25 @@ def send(self, response: "ResponseBase") -> None: self.connection.send(buffer) def log_debug(self, message: str) -> None: - self.logger.debug(f"{self.__log_prefix}: {message}") + self.logger.debug(f"{self._log_prefix}: {message}") def log_info(self, message: str) -> None: - self.logger.info(f"{self.__log_prefix}: {message}") + self.logger.info(f"{self._log_prefix}: {message}") def log_warn(self, message: str) -> None: - self.logger.warn(f"{self.__log_prefix}: {message}") + self.logger.warn(f"{self._log_prefix}: {message}") def log_error(self, message: str) -> None: - self.logger.error(f"{self.__log_prefix}: {message}") + self.logger.error(f"{self._log_prefix}: {message}") def log_network_sending(self, data: object) -> None: - self.logger.info(f"{self.__log_prefix} [send]: {data}") + self.logger.info(f"{self._log_prefix} [send]: {data}") def log_network_receving(self, data: object) -> None: - self.logger.info(f"{self.__log_prefix} [recv]: {data}") + self.logger.info(f"{self._log_prefix} [recv]: {data}") def log_current_class(self, object: "CmdHandlerBase") -> None: - self.logger.debug(f"{self.__log_prefix} [=>] <{ + self.logger.debug(f"{self._log_prefix} [=>] <{ object.__class__.__name__}>") diff --git a/src/library/src/abstractions/contracts.py b/src/library/src/abstractions/contracts.py index 5bd672638..f977b782e 100644 --- a/src/library/src/abstractions/contracts.py +++ b/src/library/src/abstractions/contracts.py @@ -39,7 +39,7 @@ def to_serializable_dict(self) -> dict: result = deepcopy(self.__dict__) for key, value in result.items(): if isinstance(value, bytes): - result[key] = value.decode("utf-8") + result[key] = list(value) elif isinstance(value, enum.Enum): result[key] = value.value elif isinstance(value, enum.IntEnum): diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index afff024a1..63e9e9e26 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -15,13 +15,13 @@ class CmdHandlerBase(abc.ABC): _client: "ClientBase" _request: "RequestBase" - _result: "ResultBase" - _response: "ResponseBase" - _backend_url: "str" + _result: "ResultBase" + _response: "ResponseBase" + _backend_url: "str" """ store the backend url """ - _result_cls: "Type[ResultBase]" + _result_cls: "Type[ResultBase]" """ the result class type """ @@ -72,7 +72,7 @@ def _data_operate(self) -> None: # get the http response and create it with this type url = f"{ - CONFIG.backend.url}/{self._client.server_config.server_name}/{self._backend_url}/" + CONFIG.backend.url}/{self._client.server_config.server_name}/{self.__class__.__name__}/" data = self._request.to_serializable_dict() data["server_id"] = str(self._client.server_config.server_id) diff --git a/src/library/tests/mock_objects/general.py b/src/library/tests/mock_objects/general.py index 8921bca15..7d354171b 100644 --- a/src/library/tests/mock_objects/general.py +++ b/src/library/tests/mock_objects/general.py @@ -1,14 +1,18 @@ import socketserver + +import responses from library.src.abstractions.client import ClientBase from library.src.abstractions.connections import ConnectionBase +from library.src.abstractions.handler import CmdHandlerBase from library.src.abstractions.switcher import SwitcherBase from library.src.log.log_manager import LogWriter +from library.src.unispy_server_config import CONFIG from servers.natneg.src.handlers.switcher import CmdSwitcher class ConnectionMock(ConnectionBase): def send(self, data: bytes) -> None: - return print(data) + pass class RequestHandlerMock(socketserver.BaseRequestHandler): @@ -34,3 +38,9 @@ def error(self, message): def warn(self, message): print(message) + + +def create_mock_url(client: ClientBase, handler: type[CmdHandlerBase], data: dict) -> None: + url = f"{ + CONFIG.backend.url}/{client.server_config.server_name}/{handler.__name__}/" + responses.add(responses.POST, url, json=data, status=200) diff --git a/src/servers/natneg/src/contracts/requests.py b/src/servers/natneg/src/contracts/requests.py index 93fc378dc..f52daa9aa 100644 --- a/src/servers/natneg/src/contracts/requests.py +++ b/src/servers/natneg/src/contracts/requests.py @@ -57,7 +57,7 @@ def parse(self) -> None: if len(self.raw_request) > 21 and self.raw_request[-1] == 0: game_name_bytes = self.raw_request[21:-1] - self.game_name = game_name_bytes.decode("ascii") + self.game_name = game_name_bytes.decode("ascii").replace("\x00","") class NatifyRequest(CommonRequestBase): diff --git a/src/servers/natneg/tests/handler_tests.py b/src/servers/natneg/tests/handler_tests.py index 60b4412ae..a565a4187 100644 --- a/src/servers/natneg/tests/handler_tests.py +++ b/src/servers/natneg/tests/handler_tests.py @@ -1,8 +1,9 @@ import unittest from library.src.unispy_server_config import CONFIG +from library.tests.mock_objects.general import create_mock_url from servers.natneg.src.contracts.requests import InitRequest -from servers.natneg.src.handlers.handlers import InitHandler +from servers.natneg.src.handlers.handlers import AddressCheckHandler, InitHandler import responses from servers.natneg.src.contracts.requests import ( AddressCheckRequest, @@ -34,9 +35,7 @@ def init_test(self): client = create_client() - url = f"{ - CONFIG.backend.url}/{client.server_config.server_name}/{InitHandler._backend_url}/" - responses.add(responses.POST, url, json={"message": "ok"}, status=200) + create_mock_url(client, InitHandler, {"message": "ok"}) # test request parsing request = InitRequest(raw) @@ -53,7 +52,32 @@ def init_test(self): # test response constructing self.assertTrue(handler._response.sending_buffer == - b'\xfd\xfc\x1efj\xb2\x03\x01\x00\x00\x03\t\x01\x00\xc0\xa8\x00\x01\x00\x00') + b'\xfd\xfc\x1efj\xb2\x03\x01\t\x03\x00\x00\x01\x00\xc0\xa8\x00\x01\x00\x00') + + @responses.activate + def address_check_test(self): + raw = bytes( + [ + 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, 0x0a, 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + ) # fmt: skip + + request = AddressCheckRequest(raw) + request.parse() + cookie = 151191552 + self.assertEqual(cookie, request.cookie) + self.assertEqual(RequestType.ADDRESS_CHECK, request.command_name) + self.assertEqual(NatClientIndex.GAME_CLIENT, request.client_index) + self.assertEqual(False, request.use_game_port) + self.assertEqual(3, request.version) + self.assertEqual(NatPortType.NN1, request.port_type) + + client = create_client() + create_mock_url(client, AddressCheckHandler,{"message": "ok"}) + handler = AddressCheckHandler(client, request) + handler.handle() + + self.assertTrue(handler._response.sending_buffer == b'') if __name__ == "__main__": diff --git a/src/servers/natneg/tests/mock_objects.py b/src/servers/natneg/tests/mock_objects.py index fbf31c16f..857201c4b 100644 --- a/src/servers/natneg/tests/mock_objects.py +++ b/src/servers/natneg/tests/mock_objects.py @@ -4,7 +4,7 @@ class ClientMock(Client): - + pass From 4995a3b4104a2f531c59d9fff0e64dfe09bc96da Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 16 Aug 2024 08:42:56 +0000 Subject: [PATCH 089/231] fix(test): added more tests --- src/library/src/abstractions/handler.py | 8 -- src/servers/natneg/src/contracts/requests.py | 6 +- src/servers/natneg/src/contracts/responses.py | 16 +-- src/servers/natneg/src/handlers/handlers.py | 15 +-- src/servers/natneg/tests/handler_tests.py | 90 +++++++++++-- src/servers/natneg/tests/requests_test.py | 118 ------------------ 6 files changed, 98 insertions(+), 155 deletions(-) delete mode 100644 src/servers/natneg/tests/requests_test.py diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index 63e9e9e26..683561a10 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -17,10 +17,6 @@ class CmdHandlerBase(abc.ABC): _request: "RequestBase" _result: "ResultBase" _response: "ResponseBase" - _backend_url: "str" - """ - store the backend url - """ _result_cls: "Type[ResultBase]" """ the result class type @@ -28,10 +24,6 @@ class CmdHandlerBase(abc.ABC): def __init__(self, client: "ClientBase", request: "RequestBase") -> None: - if self._backend_url is None: - raise UniSpyException( - "The backend url and result_cls should not be None or not None at same time") - assert issubclass(type(client), ClientBase) assert issubclass(type(request), RequestBase) # if some subclass do not need result, override the __init__() in that subclass diff --git a/src/servers/natneg/src/contracts/requests.py b/src/servers/natneg/src/contracts/requests.py index f52daa9aa..5a916177c 100644 --- a/src/servers/natneg/src/contracts/requests.py +++ b/src/servers/natneg/src/contracts/requests.py @@ -57,7 +57,9 @@ def parse(self) -> None: if len(self.raw_request) > 21 and self.raw_request[-1] == 0: game_name_bytes = self.raw_request[21:-1] - self.game_name = game_name_bytes.decode("ascii").replace("\x00","") + game_name = game_name_bytes.decode("ascii").replace("\x00", "") + if len(game_name) != 0: + self.game_name = game_name class NatifyRequest(CommonRequestBase): @@ -94,4 +96,4 @@ def parse(self): self.mapping_scheme = NatPortMappingScheme(self.raw_request[17]) end_index = self.raw_request[23:].index(0) - self.game_name = self.raw_request[23 : 23 + end_index].decode("ascii") + self.game_name = self.raw_request[23: 23 + end_index].decode("ascii") diff --git a/src/servers/natneg/src/contracts/responses.py b/src/servers/natneg/src/contracts/responses.py index 67e25d19e..77800b986 100644 --- a/src/servers/natneg/src/contracts/responses.py +++ b/src/servers/natneg/src/contracts/responses.py @@ -12,7 +12,7 @@ class InitResponse(CommonResponseBase): _request: InitRequest _result: InitResult - def __init__(self, request: "InitRequest", result: "InitResult") -> None: + def __init__(self, request: InitRequest, result: InitResult) -> None: super().__init__(request, result) assert isinstance(request, InitRequest) assert isinstance(result, InitResult) @@ -22,9 +22,9 @@ class ErcAckResponse(InitResponse): _request: ErtAckRequest _result: ErtAckResult - def __init__(self, request: "ErtAckRequest", result: "ErtAckResult") -> None: - assert isinstance(request, InitRequest) - assert isinstance(result, InitResult) + def __init__(self, request: ErtAckRequest, result: ErtAckResult) -> None: + assert isinstance(request, ErtAckRequest) + assert isinstance(result, ErtAckResult) self._request = request self._result = result @@ -33,7 +33,7 @@ class NatifyResponse(InitResponse): _request: NatifyRequest _result: NatifyResult - def __init__(self, request: "NatifyRequest", result: "NatifyResult") -> None: + def __init__(self, request: NatifyRequest, result: NatifyResult) -> None: assert isinstance(request, NatifyRequest) assert isinstance(result, NatifyResult) self._request = request @@ -41,10 +41,10 @@ def __init__(self, request: "NatifyRequest", result: "NatifyResult") -> None: class AddressCheckResponse(InitResponse): - _request: "AddressCheckRequest" - _result: "AddressCheckResult" + _request: AddressCheckRequest + _result: AddressCheckResult - def __init__(self, request: "AddressCheckRequest", result: "AddressCheckResult") -> None: + def __init__(self, request: AddressCheckRequest, result: AddressCheckResult) -> None: assert isinstance(request, AddressCheckRequest) assert isinstance(result, AddressCheckResult) self._request = request diff --git a/src/servers/natneg/src/handlers/handlers.py b/src/servers/natneg/src/handlers/handlers.py index 24747f403..d916d293e 100644 --- a/src/servers/natneg/src/handlers/handlers.py +++ b/src/servers/natneg/src/handlers/handlers.py @@ -1,6 +1,5 @@ from copy import copy -from servers.natneg.src.abstractions.contracts import RequestBase from servers.natneg.src.abstractions.handlers import CmdHandlerBase from servers.natneg.src.applications.client import Client from servers.natneg.src.contracts.requests import ( @@ -33,6 +32,7 @@ class AddressCheckHandler(CmdHandlerBase): _request: AddressCheckRequest _result: AddressCheckResult _response: InitResponse + _result_cls = None def __init__(self, client: Client, request: AddressCheckRequest) -> None: super().__init__(client, request) @@ -75,11 +75,12 @@ def __init__(self, client: Client, request: ConnectRequest) -> None: class ErtAckHandler(CmdHandlerBase): - _request: "ErtAckRequest" - _result: "ErtAckResult" - _response: "ErcAckResponse" + _request: ErtAckRequest + _result: ErtAckResult + _response: ErcAckResponse + _result_cls = None - def __init__(self, client: "Client", request: "ErtAckRequest") -> None: + def __init__(self, client: Client, request: ErtAckRequest) -> None: super().__init__(client, request) assert isinstance(request, ErtAckRequest) @@ -102,8 +103,7 @@ class InitHandler(CmdHandlerBase): _request: InitRequest _result: InitResult _response: InitResponse - _backend_url: str = "init" - _result_cls: type[InitResult] = InitResult + _result_cls = None def __init__(self, client: Client, request: InitRequest) -> None: super().__init__(client, request) @@ -129,6 +129,7 @@ class NatifyHandler(CmdHandlerBase): _request: NatifyRequest _result: NatifyResult _response: InitResponse + _result_cls = None def __init__(self, client: Client, request: NatifyRequest) -> None: super().__init__(client, request) diff --git a/src/servers/natneg/tests/handler_tests.py b/src/servers/natneg/tests/handler_tests.py index a565a4187..b40f971b2 100644 --- a/src/servers/natneg/tests/handler_tests.py +++ b/src/servers/natneg/tests/handler_tests.py @@ -1,9 +1,8 @@ import unittest -from library.src.unispy_server_config import CONFIG from library.tests.mock_objects.general import create_mock_url from servers.natneg.src.contracts.requests import InitRequest -from servers.natneg.src.handlers.handlers import AddressCheckHandler, InitHandler +from servers.natneg.src.handlers.handlers import AddressCheckHandler, ErtAckHandler, InitHandler, NatifyHandler import responses from servers.natneg.src.contracts.requests import ( AddressCheckRequest, @@ -19,12 +18,12 @@ RequestType, ) -from servers.natneg.tests.mock_objects import ClientMock, create_client +from servers.natneg.tests.mock_objects import create_client class HandlerTests(unittest.TestCase): @responses.activate - def init_test(self): + def test_init(self): raw = bytes( [ 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, @@ -52,10 +51,10 @@ def init_test(self): # test response constructing self.assertTrue(handler._response.sending_buffer == - b'\xfd\xfc\x1efj\xb2\x03\x01\t\x03\x00\x00\x01\x00\xc0\xa8\x00\x01\x00\x00') + b'\xfd\xfc\x1efj\xb2\x03\x01\t\x03\x00\x00\x01\x00\xc0\xa8\x00\x01\x00\x00') @responses.activate - def address_check_test(self): + def test_address_check(self): raw = bytes( [ 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, 0x0a, 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 @@ -64,8 +63,7 @@ def address_check_test(self): request = AddressCheckRequest(raw) request.parse() - cookie = 151191552 - self.assertEqual(cookie, request.cookie) + self.assertEqual(151191552, request.cookie) self.assertEqual(RequestType.ADDRESS_CHECK, request.command_name) self.assertEqual(NatClientIndex.GAME_CLIENT, request.client_index) self.assertEqual(False, request.use_game_port) @@ -73,13 +71,81 @@ def address_check_test(self): self.assertEqual(NatPortType.NN1, request.port_type) client = create_client() - create_mock_url(client, AddressCheckHandler,{"message": "ok"}) + create_mock_url(client, AddressCheckHandler, {"message": "ok"}) handler = AddressCheckHandler(client, request) handler.handle() - self.assertTrue(handler._response.sending_buffer == b'') + self.assertTrue(handler._response.sending_buffer == + b'\xfd\xfc\x1efj\xb2\x03\x0b\t\x03\x00\x00\x01\x00\xc0\xa8\x00\x01\x00\x00') + + @responses.activate + def test_ert_ack(self): + raw = bytes( + [ + 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, + 0x03, + 0x00, 0x00, 0x03, 0x09, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + ) # fmt: skip + request = ErtAckRequest(raw) + request.parse() + self.assertEqual(151191552, request.cookie) + self.assertEqual(RequestType.ERT_ACK, request.command_name) + self.assertEqual(NatClientIndex.GAME_CLIENT, request.client_index) + self.assertEqual(3, request.version) + self.assertEqual(False, request.use_game_port) + self.assertEqual(NatPortType.NN1, request.port_type) + client = create_client() + create_mock_url(client, AddressCheckHandler, {"message": "ok"}) + + handler = ErtAckHandler(client, request) + handler.handle() + self.assertTrue(handler._response.sending_buffer == + b'\xfd\xfc\x1efj\xb2\x03\x03\t\x03\x00\x00\x01\x00\xc0\xa8\x00\x01\x00\x00') + + @responses.activate + def test_natify(self): + raw = bytes( + [ + 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, + 0x0c, + 0x00, 0x00, 0x03, 0x09, + 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + ) # fmt: skip + request = NatifyRequest(raw) + request.parse() + self.assertEqual(151191552, request.cookie) + self.assertEqual(RequestType.NATIFY_REQUEST, request.command_name) + self.assertEqual(NatClientIndex.GAME_CLIENT, request.client_index) + self.assertEqual(3, request.version) + self.assertEqual(False, request.use_game_port) + self.assertEqual(NatPortType.NN1, request.port_type) + client = create_client() + create_mock_url(client, AddressCheckHandler, {"message": "ok"}) + + handler = NatifyHandler(client, request) + handler.handle() + self.assertTrue(handler._response.sending_buffer == + b'\xfd\xfc\x1efj\xb2\x03\x02\t\x03\x00\x00\x01\x00\xc0\xa8\x00\x01\x00\x00') + + @responses.activate + def test_preinit(self): + raw = bytes( + [ + 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x04, 0x0f, 0xb5, 0xe0, 0x95, 0x2a, 0x00, 0x24, 0x38, 0xb2, 0xb3, 0x5e + ] + ) # fmt: skip + + req = PreInitRequest(raw) + req.parse() + self.assertEqual(714465461, req.cookie) + self.assertEqual(RequestType.PRE_INIT, req.command_name) + self.assertEqual(4, req.version) + self.assertEqual(NatPortType.GP, req.port_type) + self.assertEqual(PreInitState.WAITING_FOR_CLIENT, req.state) if __name__ == "__main__": - test = HandlerTests() - test.init_test() + unittest.main() diff --git a/src/servers/natneg/tests/requests_test.py b/src/servers/natneg/tests/requests_test.py deleted file mode 100644 index 6032c97e9..000000000 --- a/src/servers/natneg/tests/requests_test.py +++ /dev/null @@ -1,118 +0,0 @@ -import unittest -from library.src.extentions.bytes_extentions import int_to_bytes -from servers.natneg.src.contracts.requests import ( - AddressCheckRequest, - ErtAckRequest, - InitRequest, - NatifyRequest, - PreInitRequest, -) -from servers.natneg.src.enums.general import ( - NatClientIndex, - NatPortType, - PreInitState, - RequestType, -) - - -class UnitTests(unittest.TestCase): - def test_init(self) -> None: - raw = bytes( - [ - 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, - 0x00, - 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - ] - ) # fmt: skip - req = InitRequest(raw) - req.parse() - cookie = 151191552 - self.assertEqual(cookie.to_bytes(4, "little"), req.cookie) - self.assertEqual(RequestType.INIT, req.command_name) - self.assertEqual(NatClientIndex.GAME_CLIENT, req.client_index) - self.assertEqual(False, req.use_game_port) - self.assertEqual(3, req.version) - self.assertEqual(NatPortType.NN1, req.port_type) - - def test_address_check(self) -> None: - raw = bytes( - [ - 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, 0x0a, 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - ] - ) # fmt: skip - - req = AddressCheckRequest(raw) - req.parse() - cookie = 151191552 - self.assertEqual(cookie.to_bytes(4, "little"), req.cookie) - self.assertEqual(RequestType.ADDRESS_CHECK, req.command_name) - self.assertEqual(NatClientIndex.GAME_CLIENT, req.client_index) - self.assertEqual(False, req.use_game_port) - self.assertEqual(3, req.version) - self.assertEqual(NatPortType.NN1, req.port_type) - - def test_ert_ack(self) -> None: - raw = bytes( - [ - 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, - 0x03, - 0x00, 0x00, 0x03, 0x09, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - ] - ) # fmt: skip - req = ErtAckRequest(raw) - req.parse() - cookie = 151191552 - self.assertEqual(cookie.to_bytes(4, "little"), req.cookie) - self.assertEqual(RequestType.ERT_ACK, req.command_name) - self.assertEqual(NatClientIndex.GAME_CLIENT, req.client_index) - self.assertEqual(3, req.version) - self.assertEqual(False, req.use_game_port) - self.assertEqual(NatPortType.NN1, req.port_type) - - def test_nattify(self) -> None: - raw = bytes( - [ - 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, - 0x0c, - 0x00, 0x00, 0x03, 0x09, - 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - ] - ) # fmt: skip - req = NatifyRequest(raw) - req.parse() - cookie = 151191552 - self.assertEqual(cookie.to_bytes(4, "little"), req.cookie) - self.assertEqual(RequestType.NATIFY_REQUEST, req.command_name) - self.assertEqual(NatClientIndex.GAME_CLIENT, req.client_index) - self.assertEqual(3, req.version) - self.assertEqual(False, req.use_game_port) - self.assertEqual(NatPortType.NN1, req.port_type) - - def test_preinit(self) -> None: - - raw = bytes( - [ - 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x04, 0x0f, 0xb5, 0xe0, 0x95, 0x2a, 0x00, 0x24, 0x38, 0xb2, 0xb3, 0x5e - ] - ) # fmt: skip - - req = PreInitRequest(raw) - req.parse() - b_cookie = bytes( - [ - 0xB5, - 0xE0, - 0x95, - 0x2A, - ] - ) - self.assertEqual(b_cookie, req.cookie) - self.assertEqual(RequestType.PRE_INIT, req.command_name) - self.assertEqual(4, req.version) - self.assertEqual(NatPortType.GP, req.port_type) - self.assertEqual(PreInitState.WAITING_FOR_CLIENT, req.state) - - -if __name__ == "__main__": - unittest.main() From 0df2b9109d31a1f5afe0f097fff0ac7b1a6bb800 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 20 Aug 2024 06:17:44 +0000 Subject: [PATCH 090/231] fix(web): added tests, fix parsing errors --- src/docker-compose.yaml | 2 +- .../src/modules/auth/abstractions/general.py | 4 +- .../src/modules/auth/contracts/requests.py | 76 ++++++----- src/servers/webservices/tests/auth.py | 119 ++++++++++++------ 4 files changed, 131 insertions(+), 70 deletions(-) diff --git a/src/docker-compose.yaml b/src/docker-compose.yaml index 759d08f22..173087e2e 100644 --- a/src/docker-compose.yaml +++ b/src/docker-compose.yaml @@ -27,4 +27,4 @@ services: POSTGRES_PASSWORD: 123456 POSTGRES_DB: unispy volumes: - - ../postgre_creation.sql:/docker-entrypoint-initdb.d/postgre_creation.sql + - ../common/postgre_creation.sql:/docker-entrypoint-initdb.d/postgre_creation.sql diff --git a/src/servers/webservices/src/modules/auth/abstractions/general.py b/src/servers/webservices/src/modules/auth/abstractions/general.py index ef4e64c2c..6b245aff4 100644 --- a/src/servers/webservices/src/modules/auth/abstractions/general.py +++ b/src/servers/webservices/src/modules/auth/abstractions/general.py @@ -21,12 +21,12 @@ def parse(self) -> None: raise AuthException("version is missing from the request.") self.version = int(version_node.text) partner_id_node = self._content_element.find( - f".//{{{NAMESPACE}}}version") + f".//{{{NAMESPACE}}}partnercode") if partner_id_node is None or partner_id_node.text is None: raise AuthException("partner id is missing from the request.") self.partner_code = int(partner_id_node.text) namespace_id_node = self._content_element.find( - f".//{{{NAMESPACE}}}version") + f".//{{{NAMESPACE}}}namespaceid") if namespace_id_node is None or namespace_id_node.text is None: raise AuthException("namespace id is missing from the request.") self.namespace_id = int(namespace_id_node.text) diff --git a/src/servers/webservices/src/modules/auth/contracts/requests.py b/src/servers/webservices/src/modules/auth/contracts/requests.py index a8cef4925..8ee99debc 100644 --- a/src/servers/webservices/src/modules/auth/contracts/requests.py +++ b/src/servers/webservices/src/modules/auth/contracts/requests.py @@ -13,23 +13,27 @@ class LoginProfileRequest(LoginRequestBase): def parse(self) -> None: super().parse() - self.email = self._content_element.find(f".//{{{NAMESPACE}}}email") - if self.email is None: + email = self._content_element.find(f".//{{{NAMESPACE}}}email") + if email is None: raise AuthException("email is missing from the request.") + self.email = email.text - self.uniquenick = self._content_element.find( + uniquenick = self._content_element.find( f".//{{{NAMESPACE}}}uniquenick") - if self.uniquenick is None: + if uniquenick is None: raise AuthException("uniquenick is missing from the request.") + self.uniquenick = uniquenick.text - self.cdkey = self._content_element.find(f".//{{{NAMESPACE}}}cdkey") - if self.cdkey is None: + cdkey = self._content_element.find(f".//{{{NAMESPACE}}}cdkey") + if cdkey is None: raise AuthException("cdkey is missing from the request.") + self.cdkey = cdkey.text - self.password = self._content_element.find( - f".//{{{NAMESPACE}}}password") - if self.password is None: + password = self._content_element.find( + f".//{{{NAMESPACE}}}password//{{{NAMESPACE}}}Value") + if password is None: raise AuthException("password is missing from the request.") + self.password = password.text class LoginProfileWithGameIdRequest(LoginProfileRequest): @@ -41,18 +45,31 @@ def parse(self) -> None: if game_id is None: raise AuthException("game id is missing from the request.") - self.game_id = int(game_id) + self.game_id = int(game_id.text) class LoginPs3CertRequest(LoginRequestBase): ps3_cert: str + game_id: int + npticket: str def parse(self) -> None: super().parse() - self.ps3_cert = self._content_element.find( - f".//{{{NAMESPACE}}}npticket") - if self.ps3_cert is None: + ps3_cert = self._content_element.find( + f".//{{{NAMESPACE}}}ps3sert") + if ps3_cert is None: raise AuthException("ps3cert is missing from the request") + self.ps3_cert = ps3_cert.text + + game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") + if game_id is None: + raise AuthException("game id is missing from the request.") + self.game_id = int(game_id.text) + + npticket = self._content_element.find(f".//{{{NAMESPACE}}}npticket") + if npticket is None: + raise AuthException("npticket is missing from the request.") + self.npticket = npticket.text class LoginPs3CertWithGameIdRequest(LoginPs3CertRequest): @@ -63,8 +80,7 @@ def parse(self) -> None: game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") if game_id is None: raise AuthException("game id is missing from the request.") - - self.game_id = int(game_id) + self.game_id = int(game_id.text) class LoginRemoteAuthRequest(LoginRequestBase): @@ -73,15 +89,22 @@ class LoginRemoteAuthRequest(LoginRequestBase): def parse(self) -> None: super().parse() - self.auth_token = self._content_element.find( + auth_token = self._content_element.find( f".//{{{NAMESPACE}}}authtoken") - if self.auth_token is None: + if auth_token is None: raise AuthException("authtoken is missing from the request.") + self.auth_token = auth_token.text - self.challenge = self._content_element.find( + challenge = self._content_element.find( f".//{{{NAMESPACE}}}challenge") - if self.challenge is None: + if challenge is None: raise AuthException("challenge is missing from the request.") + self.challenge = challenge.text + + game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") + if game_id is None: + raise AuthException("game id is missing from the request.") + self.game_id = int(game_id.text) class LoginRemoteAuthWithGameIdRequest(LoginRemoteAuthRequest): @@ -93,7 +116,7 @@ def parse(self) -> None: if game_id is None: raise AuthException("game id is missing from the request.") - self.game_id = int(game_id) + self.game_id = int(game_id.text) class LoginUniqueNickRequest(LoginRequestBase): @@ -108,14 +131,11 @@ def parse(self) -> None: raise AuthException("uniquenick is missing from the request.") self.uniquenick = unique_nick_node.text - password_node = self._content_element.find( - f".//{{{NAMESPACE}}}password") - if password_node is None: + password = self._content_element.find( + f".//{{{NAMESPACE}}}password//{{{NAMESPACE}}}Value") + if password is None: raise AuthException("password is missing from the request.") - password_value_node = password_node.find(f".//{{{NAMESPACE}}}Value") - if password_value_node is None or password_value_node.text is None: - raise AuthException("No password value found") - self.password = password_value_node.text + self.password = password.text class LoginUniqueNickWithGameIdRequest(LoginUniqueNickRequest): @@ -127,4 +147,4 @@ def parse(self) -> None: if game_id is None: raise AuthException("game id is missing from the request.") - self.game_id = int(game_id) + self.game_id = int(game_id.text) diff --git a/src/servers/webservices/tests/auth.py b/src/servers/webservices/tests/auth.py index 26e2cf3c1..625667252 100644 --- a/src/servers/webservices/tests/auth.py +++ b/src/servers/webservices/tests/auth.py @@ -2,15 +2,15 @@ import responses -from servers.webservices.src.modules.auth.contracts.requests import LoginProfileWithGameIdRequest, LoginUniqueNickRequest +from servers.webservices.src.modules.auth.contracts.requests import LoginProfileWithGameIdRequest, LoginPs3CertRequest, LoginRemoteAuthRequest, LoginUniqueNickRequest from servers.webservices.src.modules.auth.handlers.general import LoginProfileWithGameIdHandler -LOGIN_PROFILE = """ - +LOGIN_PROFILE = """ + 1 @@ -26,12 +26,12 @@ """ -LOGIN_PS3_CERT = """ - +LOGIN_PS3_CERT = """ + 0 @@ -44,12 +44,12 @@ """ -LOGIN_REMOTE_AUTH = """ - +LOGIN_REMOTE_AUTH = """ + 1 @@ -62,12 +62,12 @@ """ -LOGIN_UNIQUENICK = """ - +LOGIN_UNIQUENICK = """ + 1 @@ -81,16 +81,13 @@ """ - -class AuthTest(unittest.TestCase): - def CrysisAuth(self): - raw = """ +CRYSIS = """ + xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" + xmlns:SOAP-ENC="http://schemas.xmlsoap.org/soap/encoding/" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns:ns1="http://gamespy.net/AuthService/"> 1 @@ -104,15 +101,59 @@ def CrysisAuth(self): """ - request = LoginUniqueNickRequest(raw) + +class AuthTest(unittest.TestCase): + @responses.activate + def test_crysis_auth(self): + + request = LoginUniqueNickRequest(CRYSIS) request.parse() @responses.activate - def login_profile(self): + def test_login_profile(self): request = LoginProfileWithGameIdRequest(LOGIN_PROFILE) - handler= LoginProfileWithGameIdHandler(request) + request.parse() + self.assertEqual(1, request.version) + self.assertEqual(0, request.game_id) + self.assertEqual(0, request.partner_code) + self.assertEqual(0, request.namespace_id) + self.assertEqual("spyguy@unispy.org", request.email) + self.assertEqual("spyguy", request.uniquenick) + self.assertEqual("XXXXXXXXXXX", request.cdkey) + self.assertEqual("XXXXXXXXXXX", request.password) + + # handler = LoginProfileWithGameIdHandler(request) + # handler.handle() + + def test_login_ps3_cert(self): + request = LoginPs3CertRequest(LOGIN_PS3_CERT) + request.parse() + self.assertEqual(0, request.version) + self.assertEqual(0, request.game_id) + self.assertEqual(1, request.partner_code) + self.assertEqual(0, request.namespace_id) + self.assertEqual("0", request.ps3_cert) + self.assertEqual("0001", request.npticket) + + def test_remote_auth(self): + request = LoginRemoteAuthRequest(LOGIN_REMOTE_AUTH) + request.parse() + self.assertEqual(1, request.version) + self.assertEqual(0, request.game_id) + self.assertEqual(0, request.partner_code) + self.assertEqual(0, request.namespace_id) + self.assertEqual("XXXXXXXXXXX", request.auth_token) + self.assertEqual("XXXXXXXXXXX", request.challenge) + + def test_login_uniquenick(self): + request = LoginUniqueNickRequest(LOGIN_UNIQUENICK) + request.parse() + self.assertEqual(1, request.version) + self.assertEqual(0, request.partner_code) + self.assertEqual(0, request.namespace_id) + self.assertEqual("spyguy", request.uniquenick) + self.assertEqual("XXXXXXXXXXX", request.password) if __name__ == "__main__": - t = AuthTest() - t.CrysisAuth() + unittest.main() From d52740599ddc75f98ea4605dbc2f2a2813068432 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 23 Aug 2024 15:53:24 +0800 Subject: [PATCH 091/231] refactor(lib): remove abc class for performance --- src/library/src/abstractions/client.py | 2 +- src/library/src/abstractions/connections.py | 11 ++++++----- src/library/src/abstractions/contracts.py | 6 +++--- src/library/src/abstractions/enctypt_base.py | 8 ++++---- src/library/src/abstractions/handler.py | 2 +- src/library/src/abstractions/server_launcher_base.py | 2 +- src/library/src/abstractions/switcher.py | 4 ++-- 7 files changed, 18 insertions(+), 17 deletions(-) diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index 696a4e977..0590452d7 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -17,7 +17,7 @@ from library.src.network.http_handler import HttpRequest -class ClientBase(abc.ABC): +class ClientBase: server_config: ServerConfig connection: "ConnectionBase" logger: LogWriter diff --git a/src/library/src/abstractions/connections.py b/src/library/src/abstractions/connections.py index 57b206c74..737cfe93f 100644 --- a/src/library/src/abstractions/connections.py +++ b/src/library/src/abstractions/connections.py @@ -13,7 +13,7 @@ from library.src.network.http_handler import HttpRequest -class ConnectionBase(abc.ABC): +class ConnectionBase: remote_ip: str remote_port: int _is_started: bool = False @@ -51,11 +51,12 @@ def send(self, data: bytes) -> None: raise Exception("Server is not running.") assert isinstance(data, bytes) -class UcpConnectionBase(ConnectionBase, abc.ABC): + +class UcpConnectionBase(ConnectionBase): pass -class TcpConnectionBase(ConnectionBase, abc.ABC): +class TcpConnectionBase(ConnectionBase): @abc.abstractmethod def on_connected(self): pass @@ -69,11 +70,11 @@ def disconnect(self): pass -class HttpConnectionBase(TcpConnectionBase, abc.ABC): +class HttpConnectionBase(TcpConnectionBase): pass -class ServerBase(abc.ABC): +class ServerBase: _config: ServerConfig _client_cls: type[ClientBase] _logger: LogWriter diff --git a/src/library/src/abstractions/contracts.py b/src/library/src/abstractions/contracts.py index f977b782e..17d3c5ed4 100644 --- a/src/library/src/abstractions/contracts.py +++ b/src/library/src/abstractions/contracts.py @@ -8,7 +8,7 @@ from pydantic import BaseModel -class RequestBase(abc.ABC): +class RequestBase: command_name: object = None raw_request: object = None @@ -49,11 +49,11 @@ def to_serializable_dict(self) -> dict: return result -class ResultBase(BaseModel, abc.ABC): +class ResultBase(BaseModel): pass -class ResponseBase(abc.ABC): +class ResponseBase: sending_buffer: object _result: ResultBase _request: RequestBase diff --git a/src/library/src/abstractions/enctypt_base.py b/src/library/src/abstractions/enctypt_base.py index eb1f53b1e..efc1d6d7d 100644 --- a/src/library/src/abstractions/enctypt_base.py +++ b/src/library/src/abstractions/enctypt_base.py @@ -1,11 +1,11 @@ -import abc +from abc import abstractmethod -class EncryptBase(abc.ABC): - @abc.abstractmethod +class EncryptBase: + @abstractmethod def encrypt(self, data: bytes) -> bytes: assert isinstance(data, bytes) - @abc.abstractmethod + @abstractmethod def decrypt(self, data: bytes) -> bytes: assert isinstance(data, bytes) diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index 683561a10..8a2829ba6 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -11,7 +11,7 @@ from library.src.abstractions.contracts import RequestBase, ResultBase, ResponseBase -class CmdHandlerBase(abc.ABC): +class CmdHandlerBase: _client: "ClientBase" _request: "RequestBase" diff --git a/src/library/src/abstractions/server_launcher_base.py b/src/library/src/abstractions/server_launcher_base.py index 6f6cb7de3..2885de344 100644 --- a/src/library/src/abstractions/server_launcher_base.py +++ b/src/library/src/abstractions/server_launcher_base.py @@ -21,7 +21,7 @@ } -class ServerLauncherBase(abc.ABC): +class ServerLauncherBase: config: ServerConfig logger: LogWriter diff --git a/src/library/src/abstractions/switcher.py b/src/library/src/abstractions/switcher.py index 1ddf4edf8..fc163b5de 100644 --- a/src/library/src/abstractions/switcher.py +++ b/src/library/src/abstractions/switcher.py @@ -1,10 +1,10 @@ import abc from library.src.abstractions.client import ClientBase from library.src.abstractions.handler import CmdHandlerBase -from typing import TYPE_CHECKING, List, Optional +from typing import List, Optional -class SwitcherBase(abc.ABC): +class SwitcherBase: _client: ClientBase _raw_request: object From efb93a2bb8d53c67b5a44192df96d6953e9ceb97 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 23 Aug 2024 15:54:11 +0800 Subject: [PATCH 092/231] fix(web): d2g request parsing and tests --- .../src/modules/sake/abstractions/general.py | 27 +- .../src/modules/sake/contracts/requests.py | 118 +++-- src/servers/webservices/tests/direct2game.py | 472 ++++++++++++++++++ 3 files changed, 568 insertions(+), 49 deletions(-) diff --git a/src/servers/webservices/src/modules/sake/abstractions/general.py b/src/servers/webservices/src/modules/sake/abstractions/general.py index 044d2ea3e..852ca3fa5 100644 --- a/src/servers/webservices/src/modules/sake/abstractions/general.py +++ b/src/servers/webservices/src/modules/sake/abstractions/general.py @@ -1,3 +1,4 @@ +from xml.etree import ElementTree import servers.webservices.src.abstractions.handler as h import servers.webservices.src.abstractions.contracts as lib from servers.webservices.src.aggregations.soap_envelop import SoapEnvelop @@ -17,22 +18,34 @@ def parse(self) -> None: game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") if game_id is None: raise SakeException("gameid is missing from the request.") - self.game_id = int(game_id) + self.game_id = int(game_id.text) - self.secret_key = self._content_element.find( + secret_key = self._content_element.find( f".//{{{NAMESPACE}}}secretKey") - if self.secret_key is None: + if secret_key is None: raise SakeException("secretkey id is missing from the request.") + self.secret_key = secret_key.text - self.login_ticket = self._content_element.find( + login_ticket = self._content_element.find( f".//{{{NAMESPACE}}}loginTicket") - if self.login_ticket is None: + if login_ticket is None: raise SakeException("loginTicket is missing from the request.") + self.login_ticket = login_ticket.text - self.table_id = self._content_element.find( + table_id = self._content_element.find( f".//{{{NAMESPACE}}}tableid") - if self.table_id is None: + if table_id is None: raise SakeException("tableid is missing from the request.") + self.table_id = table_id.text + + @staticmethod + def remove_namespace(tree: ElementTree): + tree.tag = tree.tag.split('}', 1)[-1] + for elem in tree: + # Remove the namespace by splitting the tag + # Keep the part after the '}' + elem.tag = elem.tag.split('}', 1)[-1] + return tree class ResultBase(lib.ResultBase): diff --git a/src/servers/webservices/src/modules/sake/contracts/requests.py b/src/servers/webservices/src/modules/sake/contracts/requests.py index 881696421..b18b7807f 100644 --- a/src/servers/webservices/src/modules/sake/contracts/requests.py +++ b/src/servers/webservices/src/modules/sake/contracts/requests.py @@ -1,3 +1,5 @@ +from copy import copy, deepcopy +import xml.etree.ElementTree as ET from typing import OrderedDict from pydantic import BaseModel @@ -11,15 +13,28 @@ class CreateRecordRequest(RequestBase): - values: dict + values: list[tuple[str, str, str]] = [] + """ + (name,type,value) + """ def parse(self) -> None: super().parse() - value_node = self._content_element.find(f".//{{{NAMESPACE}}}values") - if value_node is None: + values = self._content_element.find(f".//{{{NAMESPACE}}}values") + if values is None: raise SakeException("values is missing from request") + record_fields = values.findall(f".//{{{NAMESPACE}}}RecordField") + for f in record_fields: + temp = [] + name = f.find(f".//{{{NAMESPACE}}}name") + temp.append(name.text) + value = f.find(f".//{{{NAMESPACE}}}value") + for v in value: + temp.append(v.tag.split("}")[1]) + for i in v: + temp.append(i.text) - self.values = xmltodict.parse(value_node) + self.values.append(tuple(temp)) class DeleteRecordRequest(RequestBase): @@ -32,35 +47,39 @@ def parse(self) -> None: if record_id is None: raise SakeException("recordid is missing from request") - self.record_id = int(record_id) + self.record_id = int(record_id.text) class GetMyRecordsRequest(RequestBase): - fields: dict + fields: list[tuple[str, str]] = [] def parse(self) -> None: super().parse() - fields_node = self._content_element.find(f".//{{{NAMESPACE}}}fields") - if fields_node is None: + fields = self._content_element.find(f".//{{{NAMESPACE}}}fields") + if fields is None: raise SakeException("fields is missing from request") - self.fields = xmltodict.parse(fields_node) + for e in fields: + data = (e.text, e.tag.split("}")[1]) + self.fields.append(data) class GetRandomRecordsRequest(RequestBase): - max: str - fields: dict + max: int + fields: list[tuple[str, str]] = [] def parse(self) -> None: super().parse() max = self._content_element.find(f".//{{{NAMESPACE}}}max") if max is None: raise SakeException("max is missing from request") - self.max = int(max) + self.max = int(max.text) - fields_node = self._content_element.find(f".//{{{NAMESPACE}}}fields") - if fields_node is None: + fields = self._content_element.find(f".//{{{NAMESPACE}}}fields") + if fields is None: raise SakeException("fields is missing from request") - self.fields = xmltodict.parse(fields_node) + for e in fields: + data = (e.text, e.tag.split("}")[1]) + self.fields.append(data) class GetRecordLimitRequest(RequestBase): @@ -69,7 +88,7 @@ class GetRecordLimitRequest(RequestBase): class GetSpecificRecordsRequest(RequestBase): - record_ids: list[tuple] + record_ids: list[tuple] = [] """ [ (field_name,field_type), @@ -79,7 +98,7 @@ class GetSpecificRecordsRequest(RequestBase): (field_name,field_type) ] """ - fields: list[tuple] + fields: list[tuple] = [] """ [ (field_name,field_type), @@ -92,17 +111,21 @@ class GetSpecificRecordsRequest(RequestBase): def parse(self) -> None: super().parse() - record_id_node = self._content_element.find( + record_ids = self._content_element.find( f".//{{{NAMESPACE}}}recordids") - if record_id_node is None: + if record_ids is None: raise SakeException("No record id found.") - self.record_ids = xmltodict.parse(str(record_id_node)) + for e in record_ids: + data = (e.text, e.tag.split("}")[1]) + self.record_ids.append(data) fields = self._content_element.find( - f".//{{{NAMESPACE}}}recordids") + f".//{{{NAMESPACE}}}fields") if fields is None: raise SakeException("No record id found.") - self.fields = xmltodict.parse(str(fields)) + for e in fields: + data = (e.text, e.tag.split("}")[1]) + self.fields.append(data) class RateRecordRequest(RequestBase): @@ -115,7 +138,7 @@ def parse(self) -> None: f".//{{{NAMESPACE}}}recordid") if record_id is None: raise SakeException("No record id found.") - self.record_id = record_id + self.record_id = record_id.text rating = self._content_element.find( f".//{{{NAMESPACE}}}rating") @@ -132,7 +155,7 @@ class SearchForRecordsRequest(RequestBase): surrounding: str owner_ids: str cache_flag: str - fields: OrderedDict[str, object] + fields: list[tuple[str, str]] = [] """ [ (field_name,field_type), @@ -145,46 +168,55 @@ class SearchForRecordsRequest(RequestBase): def parse(self) -> None: super().parse() - self.filter = self._content_element.find( + filter = self._content_element.find( f".//{{{NAMESPACE}}}filter") - if self.filter is None: + if filter is None: raise SakeException("No filter found.") + self.filter = filter.text - self.sort = self._content_element.find( + sort = self._content_element.find( f".//{{{NAMESPACE}}}sort") - if self.sort is None: + if sort is None: raise SakeException("No sort found.") + self.sort = sort.text - self.offset = self._content_element.find( + offset = self._content_element.find( f".//{{{NAMESPACE}}}offset") - if self.offset is None: + if offset is None: raise SakeException("No offset found.") + self.offset = offset.text - self.max = self._content_element.find( + max = self._content_element.find( f".//{{{NAMESPACE}}}max") - if self.max is None: + if max is None: raise SakeException("No max found.") + self.max = max.text - self.surrounding = self._content_element.find( + surrounding = self._content_element.find( f".//{{{NAMESPACE}}}surrounding") - if self.sort is None: + if surrounding is None: raise SakeException("No surrounding found.") - self.owner_ids = self._content_element.find( + self.surrounding = surrounding.text + + owner_ids = self._content_element.find( f".//{{{NAMESPACE}}}ownerids") - if self.owner_ids is None: + if owner_ids is None: raise SakeException("No ownderids found.") + self.owner_ids = owner_ids.text - self.cache_flag = self._content_element.find( + cache_flag = self._content_element.find( f".//{{{NAMESPACE}}}cacheFlag") - if self.cache_flag is None: + if cache_flag is None: raise SakeException("No cache flag found.") + self.cache_flag = cache_flag.text fields = self._content_element.find( f".//{{{NAMESPACE}}}fields") if fields is None: raise SakeException("No record id found.") - - self.fields = xmltodict.parse(str(fields)) + for e in fields: + data = (e.text, e.tag.split("}")[1]) + self.fields.append(data) class UpdateRecordRequest(RequestBase): @@ -206,7 +238,9 @@ def parse(self) -> None: f".//{{{NAMESPACE}}}recordid") if record_id is None: raise SakeException("No record id found.") - self.record_id = record_id + self.record_id = record_id.text values_node = self._content_element.find( f".//{{{NAMESPACE}}}values") - self.values = xmltodict.parse(str(values_node)) + temp_str = ET.tostring( + values_node, encoding="unicode").replace("ns0:", "") + self.values = xmltodict.parse(temp_str)['values']["RecordField"] diff --git a/src/servers/webservices/tests/direct2game.py b/src/servers/webservices/tests/direct2game.py index e69de29bb..23090d253 100644 --- a/src/servers/webservices/tests/direct2game.py +++ b/src/servers/webservices/tests/direct2game.py @@ -0,0 +1,472 @@ +import unittest + +import responses + +from servers.webservices.src.modules.sake.contracts.requests import CreateRecordRequest, DeleteRecordRequest, GetMyRecordsRequest, GetRandomRecordsRequest, GetRecordLimitRequest, GetSpecificRecordsRequest, RateRecordRequest, SearchForRecordsRequest, UpdateRecordRequest + + +CRYSIS_2_SAKE = """ + + + + 3300 + 8TTq4M + 0000000000000000000000__ + DEDICATEDSTATS + PROFILE =‵ + recordid + 0 + 1 + 0 + 2 + 0 + + DATA + recordid + + + + """ + +GET_RECORD_LIMIT = """ + + + + 0 + XXXXXX + xxxxxxxx_YYYYYYYYYY__ + nicks + + + """ + +RATE_RECORD = """ + + + + 0 + XXXXXX + xxxxxxxx_YYYYYYYYYY__ + test + 158 + 200 + + + """ + +GET_RANDOM_RECORDS = """ + + + + 0 + XXXXXX + xxxxxxxx_YYYYYYYYYY__ + levels + 1 + + recordid + score + + + + """ + +GET_SPECIFIC_RECORDS = """ + + + + 0 + XXXXXX + xxxxxxxx_YYYYYYYYYY__ + scores + + 1 + 2 + 4 + 5 + + + recordid + ownerid + score + + + + """ + +GET_MY_RECORDS = """ + + + + 0 + XXXXXX + xxxxxxxx_YYYYYYYYYY__ + test + + recordid + ownerid + MyByte + MyShort + MyInt + MyFloat + MyAsciiString + MyUnicodeString + MyBoolean + MyDateAndTime + MyBinaryData + MyFileID + num_ratings + average_rating + + + + """ + +SEARCH_FOR_RECORDS = """ + + + + 0 + XXXXXX + xxxxxxxx_YYYYYYYYYY__ + scores + + + 0 + 3 + 0 + + 0 + + score + recordid + + + + """ + +DELETE_RECORD = """ + + + + 0 + XXXXXX + xxxxxxxx_YYYYYYYYYY__ + test + 150 + + + """ + +UPDATE_RECORD = """ + + + + 0 + XXXXXX + xxxxxxxx_YYYYYYYYYY__ + test + 158 + + + MyByte + + + 123 + + + + + MyShort + + + 12345 + + + + + MyInt + + + 123456789 + + + + + MyFloat + + + 3.141593 + + + + + MyAsciiString + + + ascii + + + + + MyUnicodeString + + + unicode + + + + + MyBoolean + + + 1 + + + + + MyDateAndTime + + + 2020-05-21T11:13:41Z + + + + + MyBinaryData + + + EjRWq80= + + + + + + + """ + +CREATE_RECORD = """ + + + + 0 + XXXXXX + xxxxxxxx_YYYYYYYYYY__ + test + + + MyAsciiString + + + this is a record + + + + + + + """ + + +class Direct2GameTest(unittest.TestCase): + @responses.activate + def test_get_record_limit(self): + request = GetRecordLimitRequest(GET_RECORD_LIMIT) + request.parse() + self.assertEqual(0, request.game_id) + self.assertEqual("XXXXXX", request.secret_key) + self.assertEqual("xxxxxxxx_YYYYYYYYYY__", request.login_ticket) + self.assertEqual("nicks", request.table_id) + + @responses.activate + def test_rate_record(self): + request = RateRecordRequest(RATE_RECORD) + request.parse() + self.assertEqual(0, request.game_id) + self.assertEqual("XXXXXX", request.secret_key) + self.assertEqual("xxxxxxxx_YYYYYYYYYY__", request.login_ticket) + self.assertEqual("test", request.table_id) + self.assertEqual("158", request.record_id) + self.assertEqual("200", request.rating) + + @responses.activate + def test_get_random_records(self): + request = GetRandomRecordsRequest(GET_RANDOM_RECORDS) + request.parse() + self.assertEqual(0, request.game_id) + self.assertEqual("XXXXXX", request.secret_key) + self.assertEqual("xxxxxxxx_YYYYYYYYYY__", request.login_ticket) + self.assertEqual("levels", request.table_id) + self.assertEqual(1, request.max) + self.assertEqual("recordid", request.fields[0][0]) + self.assertEqual("string", request.fields[0][1]) + self.assertEqual("score", request.fields[1][0]) + self.assertEqual("string", request.fields[1][1]) + + @responses.activate + def test_get_specific_record(self): + request = GetSpecificRecordsRequest(GET_SPECIFIC_RECORDS) + request.parse() + self.assertEqual(0, request.game_id) + self.assertEqual("XXXXXX", request.secret_key) + self.assertEqual("xxxxxxxx_YYYYYYYYYY__", request.login_ticket) + self.assertEqual("scores", request.table_id) + self.assertEqual("1", request.record_ids[0][0]) + self.assertEqual("int", request.record_ids[0][1]) + self.assertEqual("2", request.record_ids[1][0]) + self.assertEqual("int", request.record_ids[1][1]) + self.assertEqual("4", request.record_ids[2][0]) + self.assertEqual("int", request.record_ids[2][1]) + self.assertEqual("5", request.record_ids[3][0]) + self.assertEqual("int", request.record_ids[3][1]) + self.assertEqual("recordid", request.fields[0][0]) + self.assertEqual("string", request.fields[0][1]) + self.assertEqual("ownerid", request.fields[1][0]) + self.assertEqual("string", request.fields[1][1]) + self.assertEqual("score", request.fields[2][0]) + self.assertEqual("string", request.fields[2][1]) + + @responses.activate + def test_get_my_record(self): + request = GetMyRecordsRequest(GET_MY_RECORDS) + request.parse() + self.assertEqual(0, request.game_id) + self.assertEqual("XXXXXX", request.secret_key) + self.assertEqual("xxxxxxxx_YYYYYYYYYY__", request.login_ticket) + self.assertEqual("test", request.table_id) + + self.assertEqual("recordid", request.fields[0][0]) + self.assertEqual("string", request.fields[0][1]) + self.assertEqual("ownerid", request.fields[1][0]) + self.assertEqual("string", request.fields[1][1]) + self.assertEqual("MyByte", request.fields[2][0]) + self.assertEqual("string", request.fields[2][1]) + self.assertEqual("MyShort", request.fields[3][0]) + self.assertEqual("string", request.fields[3][1]) + self.assertEqual("MyInt", request.fields[4][0]) + self.assertEqual("string", request.fields[4][1]) + self.assertEqual("MyFloat", request.fields[5][0]) + self.assertEqual("string", request.fields[5][1]) + self.assertEqual("MyAsciiString", request.fields[6][0]) + self.assertEqual("string", request.fields[6][1]) + self.assertEqual("MyUnicodeString", request.fields[7][0]) + self.assertEqual("string", request.fields[7][1]) + self.assertEqual("MyBoolean", request.fields[8][0]) + self.assertEqual("string", request.fields[8][1]) + self.assertEqual("MyDateAndTime", request.fields[9][0]) + self.assertEqual("string", request.fields[9][1]) + self.assertEqual("MyBinaryData", request.fields[10][0]) + self.assertEqual("string", request.fields[10][1]) + self.assertEqual("MyFileID", request.fields[11][0]) + self.assertEqual("string", request.fields[11][1]) + self.assertEqual("num_ratings", request.fields[12][0]) + self.assertEqual("string", request.fields[12][1]) + self.assertEqual("average_rating", request.fields[13][0]) + self.assertEqual("string", request.fields[13][1]) + + @responses.activate + def test_search_for_records(self): + request = SearchForRecordsRequest(SEARCH_FOR_RECORDS) + request.parse() + self.assertEqual(0, request.game_id) + self.assertEqual("XXXXXX", request.secret_key) + self.assertEqual("xxxxxxxx_YYYYYYYYYY__", request.login_ticket) + self.assertEqual("scores", request.table_id) + self.assertEqual(None, request.filter) + self.assertEqual(None, request.sort) + self.assertEqual("0", request.offset) + self.assertEqual("0", request.surrounding) + self.assertEqual(None, request.owner_ids) + self.assertEqual("0", request.cache_flag) + # todo check how to implement this + # self.assertEqual("score", request.fields[0][0]) + # self.assertEqual("string", request.fields[0][1]) + # self.assertEqual("recordid", request.fields[1][0]) + # self.assertEqual("string", request.fields[1][1]) + + @responses.activate + def test_delete_record(self): + request = DeleteRecordRequest(DELETE_RECORD) + request.parse() + + self.assertEqual(0, request.game_id) + self.assertEqual("XXXXXX", request.secret_key) + self.assertEqual("xxxxxxxx_YYYYYYYYYY__", request.login_ticket) + self.assertEqual("test", request.table_id) + self.assertEqual(150, request.record_id) + + @responses.activate + def test_update_record(self): + request = UpdateRecordRequest(UPDATE_RECORD) + request.parse() + + self.assertEqual(0, request.game_id) + self.assertEqual("XXXXXX", request.secret_key) + self.assertEqual("xxxxxxxx_YYYYYYYYYY__", request.login_ticket) + self.assertEqual("test", request.table_id) + self.assertEqual("158", request.record_id) + + # TODO: Deserialization of RecordFields + self.assertEqual("MyByte", request.values[0]["name"]) + self.assertEqual( + "123", request.values[0]["value"]["byteValue"]['value']) + + @responses.activate + def test_create_record(self): + request = CreateRecordRequest(CREATE_RECORD) + request.parse() + self.assertEqual(0, request.game_id) + self.assertEqual("XXXXXX", request.secret_key) + self.assertEqual("xxxxxxxx_YYYYYYYYYY__", request.login_ticket) + self.assertEqual("test", request.table_id) + + self.assertEqual("MyAsciiString",request.values[0][0]) + self.assertEqual("asciiStringValue",request.values[0][1]) + self.assertEqual("this is a record",request.values[0][2]) + + + + +if __name__ == "__main__": + unittest.main() From c6dfbfae2c39b7f2f9f3c0354472c8afde4267f1 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 23 Aug 2024 15:54:34 +0800 Subject: [PATCH 093/231] refactor(lib): remove abc lib using for performance --- .../gamespy/library/abstractions/cmd_handler_base.py | 2 +- .../protocols/presence_search_player/requests.py | 2 +- src/servers/chat/src/abstractions/contract.py | 6 +++--- src/servers/natneg/src/abstractions/contracts.py | 2 +- src/servers/natneg/src/abstractions/handlers.py | 2 +- .../src/abstractions/contracts.py | 4 ++-- .../src/abstractions/handler.py | 2 +- .../src/abstractions/contracts.py | 6 +++--- .../query_report/src/v1/abstractions/contracts.py | 4 ++-- .../src/v2/abstractions/cmd_handler_base.py | 3 +-- .../query_report/src/v2/abstractions/contracts.py | 4 ++-- .../server_browser/src/v2/abstractions/contracts.py | 12 ++++++------ .../server_browser/src/v2/abstractions/handlers.py | 2 +- 13 files changed, 25 insertions(+), 26 deletions(-) diff --git a/src/backends/gamespy/library/abstractions/cmd_handler_base.py b/src/backends/gamespy/library/abstractions/cmd_handler_base.py index a93274701..2615ea817 100644 --- a/src/backends/gamespy/library/abstractions/cmd_handler_base.py +++ b/src/backends/gamespy/library/abstractions/cmd_handler_base.py @@ -5,7 +5,7 @@ from library.src.abstractions.contracts import RequestBase -class CmdHandlerBase(abc.ABC): +class CmdHandlerBase: _request: RequestBase def handle(self): diff --git a/src/backends/gamespy/protocols/presence_search_player/requests.py b/src/backends/gamespy/protocols/presence_search_player/requests.py index 722704d37..b8a9eaa2f 100644 --- a/src/backends/gamespy/protocols/presence_search_player/requests.py +++ b/src/backends/gamespy/protocols/presence_search_player/requests.py @@ -4,7 +4,7 @@ from servers.presence_connection_manager.src.enums.general import LoginType, SdkRevisionType -class RequestBase(RB, abc.ABC): +class RequestBase(RB): operation_id: int = None diff --git a/src/servers/chat/src/abstractions/contract.py b/src/servers/chat/src/abstractions/contract.py index 5e4522f4d..9504a4d80 100644 --- a/src/servers/chat/src/abstractions/contract.py +++ b/src/servers/chat/src/abstractions/contract.py @@ -2,7 +2,7 @@ import library.src.abstractions.contracts -class RequestBase(library.src.abstractions.contracts.RequestBase, abc.ABC): +class RequestBase(library.src.abstractions.contracts.RequestBase): raw_request: str command_name: str _prefix: str @@ -46,14 +46,14 @@ def parse(self) -> None: self._cmd_params = dataFrag[1:] -class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): +class ResultBase(library.src.abstractions.contracts.ResultBase): pass SERVER_DOMAIN = "unispy.net" -class ResponseBase(library.src.abstractions.contracts.ResponseBase, abc.ABC): +class ResponseBase(library.src.abstractions.contracts.ResponseBase): sending_buffer: str _result: ResultBase _request: RequestBase diff --git a/src/servers/natneg/src/abstractions/contracts.py b/src/servers/natneg/src/abstractions/contracts.py index 537f7a24e..dd6d3e00d 100644 --- a/src/servers/natneg/src/abstractions/contracts.py +++ b/src/servers/natneg/src/abstractions/contracts.py @@ -74,7 +74,7 @@ def parse(self): self.use_game_port = bool(self.raw_request[14]) -class CommonResultBase(ResultBase, abc.ABC): +class CommonResultBase(ResultBase): public_ip_addr: str public_port: int diff --git a/src/servers/natneg/src/abstractions/handlers.py b/src/servers/natneg/src/abstractions/handlers.py index a8d439a81..0995c5018 100644 --- a/src/servers/natneg/src/abstractions/handlers.py +++ b/src/servers/natneg/src/abstractions/handlers.py @@ -4,7 +4,7 @@ from servers.natneg.src.abstractions.contracts import RequestBase -class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase, abc.ABC): +class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase): def __init__(self, client: Client, request: RequestBase) -> None: super().__init__(client, request) assert isinstance(client, Client) diff --git a/src/servers/presence_connection_manager/src/abstractions/contracts.py b/src/servers/presence_connection_manager/src/abstractions/contracts.py index 004b8af5c..26b05fff3 100644 --- a/src/servers/presence_connection_manager/src/abstractions/contracts.py +++ b/src/servers/presence_connection_manager/src/abstractions/contracts.py @@ -20,7 +20,7 @@ def normalize_request(message: str): return message -class RequestBase(library.src.abstractions.contracts.RequestBase, abc.ABC): +class RequestBase(library.src.abstractions.contracts.RequestBase): command_name: str operation_id: int raw_request: str @@ -45,7 +45,7 @@ class ResultBase(library.src.abstractions.contracts.ResultBase): pass -class ResponseBase(library.src.abstractions.contracts.ResponseBase, abc.ABC): +class ResponseBase(library.src.abstractions.contracts.ResponseBase): _request: RequestBase _result: ResultBase sending_buffer: str diff --git a/src/servers/presence_connection_manager/src/abstractions/handler.py b/src/servers/presence_connection_manager/src/abstractions/handler.py index f9646e368..500a811db 100644 --- a/src/servers/presence_connection_manager/src/abstractions/handler.py +++ b/src/servers/presence_connection_manager/src/abstractions/handler.py @@ -27,7 +27,7 @@ def _handle_exception(self, ex) -> None: super()._handle_exception(ex) -class LoginHandlerBase(CmdHandlerBase, abc.ABC): +class LoginHandlerBase(CmdHandlerBase): def _request_check(self) -> None: if self._client.info.login_status != LoginStatus.COMPLETED: diff --git a/src/servers/presence_search_player/src/abstractions/contracts.py b/src/servers/presence_search_player/src/abstractions/contracts.py index 5dd83d1bd..8eeb701ef 100644 --- a/src/servers/presence_search_player/src/abstractions/contracts.py +++ b/src/servers/presence_search_player/src/abstractions/contracts.py @@ -12,7 +12,7 @@ ) -class RequestBase(library.src.abstractions.contracts.RequestBase, abc.ABC): +class RequestBase(library.src.abstractions.contracts.RequestBase): request_dict: Dict[str, str] raw_request: str command_name: str @@ -39,10 +39,10 @@ def parse(self) -> None: raise GPParseException("namespaceid is incorrect.") -class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): +class ResultBase(library.src.abstractions.contracts.ResultBase): pass -class ResponseBase(library.src.abstractions.contracts.ResponseBase, abc.ABC): +class ResponseBase(library.src.abstractions.contracts.ResponseBase): _result: ResultBase _request: RequestBase diff --git a/src/servers/query_report/src/v1/abstractions/contracts.py b/src/servers/query_report/src/v1/abstractions/contracts.py index 8f05300e8..c0011c9c4 100644 --- a/src/servers/query_report/src/v1/abstractions/contracts.py +++ b/src/servers/query_report/src/v1/abstractions/contracts.py @@ -4,7 +4,7 @@ from library.src.extentions.gamespy_utils import convert_to_key_value -class RequestBase(library.src.abstractions.contracts.RequestBase, abc.ABC): +class RequestBase(library.src.abstractions.contracts.RequestBase): request_dict: dict[str, str] = {} def __init__(self, raw_request: str) -> None: @@ -16,7 +16,7 @@ def parse(self) -> None: self.command_name = self.request_dict.keys()[0] -class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): +class ResultBase(library.src.abstractions.contracts.ResultBase): pass diff --git a/src/servers/query_report/src/v2/abstractions/cmd_handler_base.py b/src/servers/query_report/src/v2/abstractions/cmd_handler_base.py index dd412403a..408ab1519 100644 --- a/src/servers/query_report/src/v2/abstractions/cmd_handler_base.py +++ b/src/servers/query_report/src/v2/abstractions/cmd_handler_base.py @@ -1,10 +1,9 @@ -import abc from library.src.abstractions.handler import CmdHandlerBase as CHB from servers.query_report.src.v2.abstractions.contracts import RequestBase from servers.query_report.src.applications.client import Client -class CmdHandlerBase(CHB, abc.ABC): +class CmdHandlerBase(CHB): def __init__(self, client: Client, request: RequestBase) -> None: assert issubclass(type(request), RequestBase) assert isinstance(client, Client) diff --git a/src/servers/query_report/src/v2/abstractions/contracts.py b/src/servers/query_report/src/v2/abstractions/contracts.py index 4dd3544c6..8f5a7a778 100644 --- a/src/servers/query_report/src/v2/abstractions/contracts.py +++ b/src/servers/query_report/src/v2/abstractions/contracts.py @@ -26,7 +26,7 @@ def parse(self): from servers.query_report.src.v2.enums.general import PacketType -class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): +class ResultBase(library.src.abstractions.contracts.ResultBase): packet_type: PacketType @@ -34,7 +34,7 @@ class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): import library.src.abstractions.contracts -class ResponseBase(library.src.abstractions.contracts.ResponseBase, abc.ABC): +class ResponseBase(library.src.abstractions.contracts.ResponseBase): _result: ResultBase _request: RequestBase sending_buffer: bytes diff --git a/src/servers/server_browser/src/v2/abstractions/contracts.py b/src/servers/server_browser/src/v2/abstractions/contracts.py index 70804174e..0bbefd704 100644 --- a/src/servers/server_browser/src/v2/abstractions/contracts.py +++ b/src/servers/server_browser/src/v2/abstractions/contracts.py @@ -17,7 +17,7 @@ QUERY_REPORT_DEFAULT_PORT = 6500 -class RequestBase(library.src.abstractions.contracts.RequestBase, abc.ABC): +class RequestBase(library.src.abstractions.contracts.RequestBase): request_length: int raw_request: bytes command_name: RequestType @@ -31,11 +31,11 @@ def parse(self) -> None: self.command_name = RequestType(self.raw_request[2]) -class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): +class ResultBase(library.src.abstractions.contracts.ResultBase): pass -class ResponseBase(library.src.abstractions.contracts.ResponseBase, abc.ABC): +class ResponseBase(library.src.abstractions.contracts.ResponseBase): _request: RequestBase _result: ResultBase sending_buffer: bytes @@ -46,7 +46,7 @@ def __init__(self, request: RequestBase, result: ResultBase) -> None: super().__init__(request, result) -class ServerListUpdateOptionRequestBase(RequestBase, abc.ABC): +class ServerListUpdateOptionRequestBase(RequestBase): def __init__(self): self.request_version: Optional[int] = None self.protocol_version: Optional[int] = None @@ -67,13 +67,13 @@ def __init__(self, raw_request: bytes): super().__init__(raw_request) -class ServerListUpdateOptionResultBase(ResultBase, abc.ABC): +class ServerListUpdateOptionResultBase(ResultBase): client_remote_ip: bytes flag: GameServerFlags game_secret_key: str -class ServerListUpdateOptionResponseBase(ResponseBase, abc.ABC): +class ServerListUpdateOptionResponseBase(ResponseBase): _request: ServerListUpdateOptionRequestBase _result: ServerListUpdateOptionResultBase _servers_info_buffers: list = [] diff --git a/src/servers/server_browser/src/v2/abstractions/handlers.py b/src/servers/server_browser/src/v2/abstractions/handlers.py index 37ba3f541..c58190ad3 100644 --- a/src/servers/server_browser/src/v2/abstractions/handlers.py +++ b/src/servers/server_browser/src/v2/abstractions/handlers.py @@ -15,7 +15,7 @@ ) -class CmdHandlerBase(CMB, abc.ABC): +class CmdHandlerBase(CMB): _client: Client _request: RequestBase _result: ResultBase From ec8a84462b25bce7863487c84acda3e38fcf261f Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 23 Aug 2024 17:13:20 +0800 Subject: [PATCH 094/231] fix: fix unittest discovery for vscode --- src/.vscode/settings.json | 10 ++ .../__init__.py | 0 .../tests/buddy_request_test.py | 42 ------ ...ddy_requests.py => buddy_request_tests.py} | 20 +-- .../tests/game_test.py | 31 ----- .../tests/{game.py => game_tests.py} | 18 +-- .../tests/general_request_test.py | 71 ---------- ...l_requests.py => general_request_tests.py} | 0 .../tests/mock_objects.py | 15 +++ .../tests/profile_request_test.py | 61 --------- ...e_requests.py => profile_request_tests.py} | 2 +- .../{src/modules/direct2game => }/__init__.py | 0 .../webservices/src/modules/auth/___init__.py | 0 .../modules/auth/abstractions/___init__.py | 0 .../direct2game/abstractions/__init__.py | 0 .../modules/direct2game/contracts/__init__.py | 0 .../src/modules/in_game_ad/__init__.py | 0 .../modules/patching_and_tracking/__init__.py | 0 .../src/modules/racing/__init__.py | 0 .../webservices/src/modules/sake/__init__.py | 0 src/servers/webservices/tests/altas_tests.py | 75 +++++++++++ .../tests/{auth.py => auth_tests.py} | 3 +- src/servers/webservices/tests/racing_tests.py | 123 ++++++++++++++++++ src/servers/webservices/tests/sake.py | 34 ----- .../tests/{direct2game.py => sake_tests.py} | 10 +- .../{encrypt_test.py => encrypt_tests.py} | 6 +- 26 files changed, 246 insertions(+), 275 deletions(-) rename src/servers/{webservices/src/modules => presence_connection_manager}/__init__.py (100%) delete mode 100644 src/servers/presence_connection_manager/tests/buddy_request_test.py rename src/servers/presence_connection_manager/tests/{buddy_requests.py => buddy_request_tests.py} (61%) delete mode 100644 src/servers/presence_connection_manager/tests/game_test.py rename src/servers/presence_connection_manager/tests/{game.py => game_tests.py} (79%) delete mode 100644 src/servers/presence_connection_manager/tests/general_request_test.py rename src/servers/presence_connection_manager/tests/{general_requests.py => general_request_tests.py} (100%) create mode 100644 src/servers/presence_connection_manager/tests/mock_objects.py delete mode 100644 src/servers/presence_connection_manager/tests/profile_request_test.py rename src/servers/presence_connection_manager/tests/{profile_requests.py => profile_request_tests.py} (99%) rename src/servers/webservices/{src/modules/direct2game => }/__init__.py (100%) delete mode 100644 src/servers/webservices/src/modules/auth/___init__.py delete mode 100644 src/servers/webservices/src/modules/auth/abstractions/___init__.py delete mode 100644 src/servers/webservices/src/modules/direct2game/abstractions/__init__.py delete mode 100644 src/servers/webservices/src/modules/direct2game/contracts/__init__.py delete mode 100644 src/servers/webservices/src/modules/in_game_ad/__init__.py delete mode 100644 src/servers/webservices/src/modules/patching_and_tracking/__init__.py delete mode 100644 src/servers/webservices/src/modules/racing/__init__.py delete mode 100644 src/servers/webservices/src/modules/sake/__init__.py create mode 100644 src/servers/webservices/tests/altas_tests.py rename src/servers/webservices/tests/{auth.py => auth_tests.py} (98%) create mode 100644 src/servers/webservices/tests/racing_tests.py delete mode 100644 src/servers/webservices/tests/sake.py rename src/servers/webservices/tests/{direct2game.py => sake_tests.py} (99%) rename src/tests/library/{encrypt_test.py => encrypt_tests.py} (71%) diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index ee78415b9..459e8e9f1 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -1,4 +1,14 @@ { "python.analysis.typeCheckingMode": "off", "workbench.iconTheme": "material-icon-theme", + "python.testing.unittestArgs": [ + "-v", + "-s", + ".", + "-p", + "*test*.py" + ], + "python.testing.pytestEnabled": false, + "python.testing.unittestEnabled": true, + "python.analysis.enablePytestSupport": false, } \ No newline at end of file diff --git a/src/servers/webservices/src/modules/__init__.py b/src/servers/presence_connection_manager/__init__.py similarity index 100% rename from src/servers/webservices/src/modules/__init__.py rename to src/servers/presence_connection_manager/__init__.py diff --git a/src/servers/presence_connection_manager/tests/buddy_request_test.py b/src/servers/presence_connection_manager/tests/buddy_request_test.py deleted file mode 100644 index 362cb2ed2..000000000 --- a/src/servers/presence_connection_manager/tests/buddy_request_test.py +++ /dev/null @@ -1,42 +0,0 @@ -import unittest - -from servers.presence_connection_manager.contracts.requests.buddy import ( - AddBuddyRequest, - DelBuddyRequest, - InviteToRequest, - StatusRequest, -) - - -class BuddyRequestTest(unittest.TestCase): - ADD_BUDDY = "\\addbuddy\\\\sesskey\\0\\newprofileid\\0\\reason\\test\\final\\" - DEL_BUDDY = "\\delbuddy\\\\sesskey\\0\\delprofileid\\0\\final\\" - INVITE_TO = "\\inviteto\\\\sesskey\\0\\productid\\0\\profileid\\0\\final\\" - STATUS = "\\status\\0\\statstring\\test\\locstring\\test\\final\\" - - def test_add_buddy(self) -> None: - request = AddBuddyRequest(BuddyRequestTest.ADD_BUDDY) - request.parse() - self.assertEqual(0, request.friend_profile_id) - self.assertEqual("test", request.reason) - - def test_del_buddy(self) -> None: - request = DelBuddyRequest(BuddyRequestTest.DEL_BUDDY) - request.parse() - self.assertEqual(0, request.friend_profile_id) - - def test_invite_to(self) -> None: - request = InviteToRequest(BuddyRequestTest.INVITE_TO) - request.parse() - self.assertEqual(0, request.product_id) - self.assertEqual(0, request.profile_id) - - def test_status_test(self) -> None: - request = StatusRequest(BuddyRequestTest.STATUS) - request.parse() - self.assertEqual("test", request.status.status_string) - self.assertEqual("test", request.status.location_string) - - -if __name__ == "__main__": - unittest.main() diff --git a/src/servers/presence_connection_manager/tests/buddy_requests.py b/src/servers/presence_connection_manager/tests/buddy_request_tests.py similarity index 61% rename from src/servers/presence_connection_manager/tests/buddy_requests.py rename to src/servers/presence_connection_manager/tests/buddy_request_tests.py index a9be50b80..a8a78da64 100644 --- a/src/servers/presence_connection_manager/tests/buddy_requests.py +++ b/src/servers/presence_connection_manager/tests/buddy_request_tests.py @@ -6,37 +6,37 @@ InviteToRequest, StatusRequest, ) - +ADD_BUDDY = "\\addbuddy\\\\sesskey\\0\\newprofileid\\0\\reason\\test\\final\\" +DEL_BUDDY = "\\delbuddy\\\\sesskey\\0\\delprofileid\\0\\final\\" +INVITE_TO = "\\inviteto\\\\sesskey\\0\\productid\\0\\profileid\\0\\final\\" +STATUS = "\\status\\0\\statstring\\test\\locstring\\test\\final\\" class BuddyRequestTest(unittest.TestCase): - ADD_BUDDY = "\\addbuddy\\\\sesskey\\0\\newprofileid\\0\\reason\\test\\final\\" - DEL_BUDDY = "\\delbuddy\\\\sesskey\\0\\delprofileid\\0\\final\\" - INVITE_TO = "\\inviteto\\\\sesskey\\0\\productid\\0\\profileid\\0\\final\\" - STATUS = "\\status\\0\\statstring\\test\\locstring\\test\\final\\" + def test_add_buddy(self) -> None: - request = AddBuddyRequest(BuddyRequestTest.ADD_BUDDY) + request = AddBuddyRequest(ADD_BUDDY) request.parse() self.assertEqual(0, request.friend_profile_id) self.assertEqual("test", request.reason) def test_del_buddy(self) -> None: - request = DelBuddyRequest(BuddyRequestTest.DEL_BUDDY) + request = DelBuddyRequest(DEL_BUDDY) request.parse() self.assertEqual(0, request.friend_profile_id) def test_invite_to(self) -> None: - request = InviteToRequest(BuddyRequestTest.INVITE_TO) + request = InviteToRequest(INVITE_TO) request.parse() self.assertEqual(0, request.product_id) self.assertEqual(0, request.profile_id) def test_status_test(self) -> None: - request = StatusRequest(BuddyRequestTest.STATUS) + request = StatusRequest(STATUS) request.parse() self.assertEqual("test", request.status.status_string) self.assertEqual("test", request.status.location_string) if __name__ == "__main__": - unittest.main() \ No newline at end of file + unittest.main() diff --git a/src/servers/presence_connection_manager/tests/game_test.py b/src/servers/presence_connection_manager/tests/game_test.py deleted file mode 100644 index a61c2411d..000000000 --- a/src/servers/presence_connection_manager/tests/game_test.py +++ /dev/null @@ -1,31 +0,0 @@ -import unittest - -from servers.presence_connection_manager.contracts.requests.buddy import StatusRequest - - -class GameTest(unittest.TestCase): - def test_civilization_4(self) -> None: - raw_requests = [ - "\\newuser\\\\email\\civ4@unispy.org\\nick\\civ4-tk\\passwordenc\\JMHGwQ__\\productid\\10435\\gamename\\civ4\\namespaceid\\17\\uniquenick\\civ4-tk\\id\\1\\final\\", - "\\login\\\\challenge\\xMsHUXuWNXL3KMwmhoQZJrP0RVsArCYT\\uniquenick\\civ4-tk\\userid\\25\\profileid\\26\\response\\7f2c9c6685570ea18b7207d2cbd72452\\firewall\\1\\port\\0\\productid\\10435\\gamename\\civ4\\namespaceid\\17\\sdkrevision\\1\\id\\1\\final\\", - ] - for x in raw_requests: - # TODO MokeObject Implementation - pass - - def test_conflict_global_storm(self) -> None: - raw_requests = [ - "\\lc\\1\\challenge\\NRNUJLZMLX\\id\\1\\final\\", - "\\login\\\\challenge\\KMylyQbZfqzKn9otxx32q4076sOUnKif\\user\\cgs1@cgs1@rs.de\\response\\c1a6638bbcfe130e4287bfe4aa792949\\port\\-15737\\productid\\10469\\gamename\\conflictsopc\\namespaceid\\1\\id\\1\\final\\", - "\\inviteto\\\\sesskey\\58366\\products\\1038\\final\\", - ] - for x in raw_requests: - # TODO MokeObject Implementation - pass - - def test_sbwfrontps2(self) -> None: - raw = "\\status\\1\\sesskey\\1111\\statstring\\EN LIGNE\\locstring\\\\final\\" - request = StatusRequest(raw) - request.parse() - self.assertTrue(request.status.location_string == "") - self.assertTrue(request.status.status_string == "EN LIGNE") diff --git a/src/servers/presence_connection_manager/tests/game.py b/src/servers/presence_connection_manager/tests/game_tests.py similarity index 79% rename from src/servers/presence_connection_manager/tests/game.py rename to src/servers/presence_connection_manager/tests/game_tests.py index 75437316d..0eec76f5e 100644 --- a/src/servers/presence_connection_manager/tests/game.py +++ b/src/servers/presence_connection_manager/tests/game_tests.py @@ -1,19 +1,9 @@ import unittest from library.src.unispy_server_config import CONFIG -from library.tests.mock_objects.general import RequestHandlerMock, LogMock, ConnectionMock from servers.presence_connection_manager.src.contracts.requests.buddy import StatusRequest -from servers.natneg.tests.mock_objects import ClientMock +from servers.presence_connection_manager.tests.mock_objects import create_client -def create_client(): - handler = RequestHandlerMock() - logger = LogMock() - conn = ConnectionMock( - handler=handler, - config=CONFIG.servers["PresenceConnectionManager"], t_client=ClientMock, - logger=logger) - - return conn._client class GameTest(unittest.TestCase): def test_civilization_4(self) -> None: @@ -22,7 +12,7 @@ def test_civilization_4(self) -> None: "\\login\\\\challenge\\xMsHUXuWNXL3KMwmhoQZJrP0RVsArCYT\\uniquenick\\civ4-tk\\userid\\25\\profileid\\26\\response\\7f2c9c6685570ea18b7207d2cbd72452\\firewall\\1\\port\\0\\productid\\10435\\gamename\\civ4\\namespaceid\\17\\sdkrevision\\1\\id\\1\\final\\", ] client = create_client() - + for x in raw_requests: client.on_received(x) pass @@ -34,7 +24,7 @@ def test_conflict_global_storm(self) -> None: "\\inviteto\\\\sesskey\\58366\\products\\1038\\final\\", ] client = create_client() - + for x in raw_requests: client.on_received(x) pass @@ -44,4 +34,4 @@ def test_sbwfrontps2(self) -> None: request = StatusRequest(raw) request.parse() self.assertTrue(request.status.location_string == "") - self.assertTrue(request.status.status_string == "EN LIGNE") \ No newline at end of file + self.assertTrue(request.status.status_string == "EN LIGNE") diff --git a/src/servers/presence_connection_manager/tests/general_request_test.py b/src/servers/presence_connection_manager/tests/general_request_test.py deleted file mode 100644 index baf30cb7a..000000000 --- a/src/servers/presence_connection_manager/tests/general_request_test.py +++ /dev/null @@ -1,71 +0,0 @@ -import unittest -from servers.presence_connection_manager.contracts.requests.general import LoginRequest -from servers.presence_connection_manager.enums.general import ( - LoginType, - QuietModeType, - SdkRevisionType, -) - - -class GeneralRequestTest(unittest.TestCase): - LOGIN_AUTH_TOKEN = "\\login\\\\challenge\\xxxx\\authtoken\\xxxx\\userid\\0\\profileid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\3\\quiet\\0\\id\\1\\final\\" - LOGIN_UNIQUE_NICK = "\\login\\\\challenge\\xxxx\\uniquenick\\spyguy\\userid\\0\\profileid\\0\\namespaceid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\3\\quiet\\0\\id\\1\\final\\" - LOGIN_USER = "\\login\\\\challenge\\xxxx\\user\\spyguy@spyguy@unispy.org\\userid\\0\\profileid\\0\\partnerid\\0\\namespaceid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\3\\quiet\\0\\id\\1\\final\\" - - def test_login_auth_token(self) -> None: - request = LoginRequest(GeneralRequestTest.LOGIN_AUTH_TOKEN) - request.parse() - self.assertEqual(LoginType.AUTH_TOKEN, request.type) - self.assertEqual("xxxx", request.user_challenge) - self.assertEqual("xxxx", request.auth_token) - self.assertEqual(0, request.user_id) - self.assertEqual(0, request.profile_id) - self.assertEqual(0, request.partner_id) - self.assertEqual("xxxxx", request.response) - self.assertEqual("1", request.firewall) - self.assertEqual(request.game_port, 0) - self.assertEqual(request.product_id, 0) - self.assertEqual("gmtest", request.game_name) - self.assertEqual(SdkRevisionType.GPINEW_STATUS_NOTIFICATION, request.sdk_revision_type) - self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) - - def test_login_unique_nick(self) -> None: - request = LoginRequest(GeneralRequestTest.LOGIN_UNIQUE_NICK) - request.parse() - self.assertEqual(LoginType.UNIQUENICK_NAMESPACE_ID, request.type) - self.assertEqual("xxxx", request.user_challenge) - self.assertEqual("spyguy", request.unique_nick) - self.assertEqual(0, request.namespace_id) - self.assertEqual(0, request.user_id) - self.assertEqual(0, request.profile_id) - self.assertEqual(0, request.partner_id) - self.assertEqual("xxxxx", request.response) - self.assertEqual("1", request.firewall) - self.assertEqual(0, request.game_port) - self.assertEqual(0, request.product_id) - self.assertEqual("gmtest", request.game_name) - self.assertEqual(SdkRevisionType.GPINEW_STATUS_NOTIFICATION, request.sdk_revision_type) - self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) - - def test_login_user(self) -> None: - request = LoginRequest(GeneralRequestTest.LOGIN_USER) - request.parse() - self.assertEqual(LoginType.NICK_EMAIL, request.type) - self.assertEqual("xxxx", request.user_challenge) - self.assertEqual("spyguy", request.nick) - self.assertEqual("spyguy@unispy.org", request.email) - self.assertEqual(0, request.namespace_id) - self.assertEqual(0, request.user_id) - self.assertEqual(0, request.profile_id) - self.assertEqual(0, request.partner_id) - self.assertEqual("xxxxx", request.response) - self.assertEqual("1", request.firewall) - self.assertEqual(0, request.game_port) - self.assertEqual(0, request.product_id) - self.assertEqual("gmtest", request.game_name) - self.assertEqual(SdkRevisionType.GPINEW_STATUS_NOTIFICATION, request.sdk_revision_type) - self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) - - -if __name__ == "__main__": - unittest.main() diff --git a/src/servers/presence_connection_manager/tests/general_requests.py b/src/servers/presence_connection_manager/tests/general_request_tests.py similarity index 100% rename from src/servers/presence_connection_manager/tests/general_requests.py rename to src/servers/presence_connection_manager/tests/general_request_tests.py diff --git a/src/servers/presence_connection_manager/tests/mock_objects.py b/src/servers/presence_connection_manager/tests/mock_objects.py new file mode 100644 index 000000000..e2d2e864e --- /dev/null +++ b/src/servers/presence_connection_manager/tests/mock_objects.py @@ -0,0 +1,15 @@ + +from library.src.unispy_server_config import CONFIG +from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock +from servers.natneg.tests.mock_objects import ClientMock + + +def create_client(): + handler = RequestHandlerMock() + logger = LogMock() + conn = ConnectionMock( + handler=handler, + config=CONFIG.servers["PresenceConnectionManager"], t_client=ClientMock, + logger=logger) + + return conn._client diff --git a/src/servers/presence_connection_manager/tests/profile_request_test.py b/src/servers/presence_connection_manager/tests/profile_request_test.py deleted file mode 100644 index d3843ef39..000000000 --- a/src/servers/presence_connection_manager/tests/profile_request_test.py +++ /dev/null @@ -1,61 +0,0 @@ -import unittest - -from servers.presence_connection_manager.contracts.requests.profile import ( - AddBlockRequest, - GetProfileRequest, - NewProfileRequest, - RegisterCDKeyRequest, - RegisterNickRequest, - UpdateProfileRequest, -) - - -class ProfileRequestTest(unittest.TestCase): - ADD_BLOCK = "\\addblock\\profileid\\0\\final\\" - GET_PROFILE = "\\getprofile\\sesskey\\xxxx\\profileid\\0\\final\\" - NEW_PROFILE = "\\newprofile\\sesskey\\xxxx\\nick\\spyguy\\id\\1\\final\\" - NEW_PROFILE_REPLACE = "\\newprofile\\sesskey\\xxxx\\nick\\spyguy2\\replace\\1\\oldnick\\spyguy\\id\\1\\final\\" - REGISTER_CD_KEY = "\\registercdkey\\sesskey\\xxxx\\cdkeyenc\\xxxx\\id\\1\\final\\" - REGISTER_NICK = "\\registernick\\sesskey\\xxxx\\uniquenick\\spyguy\\partnerid\\0\\id\\1\\final\\" - - def test_add_block(self) -> None: - request = AddBlockRequest(ProfileRequestTest.ADD_BLOCK) - request.parse() - self.assertEqual(0, request.taget_id) - - def test_get_profile(self) -> None: - request = GetProfileRequest(ProfileRequestTest.GET_PROFILE) - request.parse() - self.assertEqual("xxxx", request.session_key) - self.assertEqual(0, request.profile_id) - - def test_new_profile(self) -> None: - request = NewProfileRequest(ProfileRequestTest.NEW_PROFILE) - request.parse() - self.assertEqual("xxxx", request.session_key) - self.assertEqual("spyguy", request.new_nick) - - def test_new_profile_replace(self) -> None: - request = NewProfileRequest(ProfileRequestTest.NEW_PROFILE_REPLACE) - request.parse() - self.assertEqual("xxxx", request.session_key) - self.assertEqual("spyguy2", request.new_nick) - self.assertEqual("spyguy", request.old_nick) - - def test_register_cd_key(self) -> None: - request = RegisterCDKeyRequest(ProfileRequestTest.REGISTER_CD_KEY) - request.parse() - self.assertEqual("xxxx", request.session_key) - self.assertEqual("xxxx", request.cdkey_enc) - - def test_register_nick(self) -> None: - request = RegisterNickRequest(ProfileRequestTest.REGISTER_NICK) - request.parse() - self.assertEqual("xxxx", request.session_key) - self.assertEqual("spyguy", request.unique_nick) - self.assertEqual(0, request.partner_id) - - def test_update_profile(self) -> None: - crysisWarsRaw = "\\updatepro\\sesskey\\1111\\countrycode\\DE\\birthday\\168232912\\partnerid\\0\\final\\" - request = UpdateProfileRequest(crysisWarsRaw) - request.Parse() diff --git a/src/servers/presence_connection_manager/tests/profile_requests.py b/src/servers/presence_connection_manager/tests/profile_request_tests.py similarity index 99% rename from src/servers/presence_connection_manager/tests/profile_requests.py rename to src/servers/presence_connection_manager/tests/profile_request_tests.py index c20bc1cb5..b76f34e79 100644 --- a/src/servers/presence_connection_manager/tests/profile_requests.py +++ b/src/servers/presence_connection_manager/tests/profile_request_tests.py @@ -58,4 +58,4 @@ def test_register_nick(self) -> None: def test_update_profile(self) -> None: crysisWarsRaw = "\\updatepro\\sesskey\\1111\\countrycode\\DE\\birthday\\168232912\\partnerid\\0\\final\\" request = UpdateProfileRequest(crysisWarsRaw) - request.Parse() \ No newline at end of file + request.Parse() diff --git a/src/servers/webservices/src/modules/direct2game/__init__.py b/src/servers/webservices/__init__.py similarity index 100% rename from src/servers/webservices/src/modules/direct2game/__init__.py rename to src/servers/webservices/__init__.py diff --git a/src/servers/webservices/src/modules/auth/___init__.py b/src/servers/webservices/src/modules/auth/___init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/webservices/src/modules/auth/abstractions/___init__.py b/src/servers/webservices/src/modules/auth/abstractions/___init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/webservices/src/modules/direct2game/abstractions/__init__.py b/src/servers/webservices/src/modules/direct2game/abstractions/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/webservices/src/modules/direct2game/contracts/__init__.py b/src/servers/webservices/src/modules/direct2game/contracts/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/webservices/src/modules/in_game_ad/__init__.py b/src/servers/webservices/src/modules/in_game_ad/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/webservices/src/modules/patching_and_tracking/__init__.py b/src/servers/webservices/src/modules/patching_and_tracking/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/webservices/src/modules/racing/__init__.py b/src/servers/webservices/src/modules/racing/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/webservices/src/modules/sake/__init__.py b/src/servers/webservices/src/modules/sake/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/webservices/tests/altas_tests.py b/src/servers/webservices/tests/altas_tests.py new file mode 100644 index 000000000..91cdac0d5 --- /dev/null +++ b/src/servers/webservices/tests/altas_tests.py @@ -0,0 +1,75 @@ +import unittest + +CREATE_MATCHLESS_SESSION = """ + + + + XXXXXX + XXXXXX + 0 + + + """ + +CREATE_SESSION = """ + + + + XXXXXX + XXXXXX + 0 + + + """ + +SET_REPORT_INTENTION = """ + + + + XXXXXX + XXXXXX + 0 + 0 + 0 + XXXXXX + + + """ + +SUBMIT_REPORT = """ + + + + XXXXXX + XXXXXX + 0 + 0 + 0 + XXXXXX + + + """ + + +class AltasTests(unittest.TestCase): + pass + + +if __name__ == "__main__": + unittest.main() diff --git a/src/servers/webservices/tests/auth.py b/src/servers/webservices/tests/auth_tests.py similarity index 98% rename from src/servers/webservices/tests/auth.py rename to src/servers/webservices/tests/auth_tests.py index 625667252..608594932 100644 --- a/src/servers/webservices/tests/auth.py +++ b/src/servers/webservices/tests/auth_tests.py @@ -3,7 +3,6 @@ import responses from servers.webservices.src.modules.auth.contracts.requests import LoginProfileWithGameIdRequest, LoginPs3CertRequest, LoginRemoteAuthRequest, LoginUniqueNickRequest -from servers.webservices.src.modules.auth.handlers.general import LoginProfileWithGameIdHandler LOGIN_PROFILE = """ """ -class AuthTest(unittest.TestCase): +class AuthTests(unittest.TestCase): @responses.activate def test_crysis_auth(self): diff --git a/src/servers/webservices/tests/racing_tests.py b/src/servers/webservices/tests/racing_tests.py new file mode 100644 index 000000000..e9fb9fec1 --- /dev/null +++ b/src/servers/webservices/tests/racing_tests.py @@ -0,0 +1,123 @@ +import unittest + + +GET_CONTEST_DATA = """ + + + + 0 + 0 + 0 + + + """ + +GET_FRIEND_RANKINGS = """ + + + + + 0 + 0 + 0 + 0 + + + +""" + +GET_REGIONAL_DATA = """ + + + + 0 + 0 + + + """ + +GET_TEN_ABOVE_RANKINGS = """ + + + + 0 + 0 + 0 + 0 + + + """ + +GET_TOP_RANKINGS = """ + + + + 0 + 0 + 0 + + + +""" + +SUBMIT_GHOST = """ + + + + 0 + 0 + 0 + 0 + XXXXXX + 0 + + + """ + +SUBMIT_SCORES = """ + + + + 0 + 0 + 0 + 0 + 0 + XXXXXX + + + """ + + +class RacingTests(unittest.TestCase): + def test_get_centest_data(self): + # request = GetContestDataRequest + pass diff --git a/src/servers/webservices/tests/sake.py b/src/servers/webservices/tests/sake.py deleted file mode 100644 index 94206a747..000000000 --- a/src/servers/webservices/tests/sake.py +++ /dev/null @@ -1,34 +0,0 @@ -import unittest - -from servers.webservices.src.modules.auth.contracts.requests import LoginUniqueNickRequest - - -class Auth(unittest.TestCase): - def test_login_unique_nick(self) -> None: - raw = """ - - - - 1 - 95 - 95 - spyguy - - 0000 - - - - """ - r = LoginUniqueNickRequest(raw) - r.parse() - pass - - -if __name__ == "__main__": - a = Auth() - a.test_login_unique_nick() \ No newline at end of file diff --git a/src/servers/webservices/tests/direct2game.py b/src/servers/webservices/tests/sake_tests.py similarity index 99% rename from src/servers/webservices/tests/direct2game.py rename to src/servers/webservices/tests/sake_tests.py index 23090d253..23d6440a2 100644 --- a/src/servers/webservices/tests/direct2game.py +++ b/src/servers/webservices/tests/sake_tests.py @@ -309,7 +309,7 @@ """ -class Direct2GameTest(unittest.TestCase): +class SakeTests(unittest.TestCase): @responses.activate def test_get_record_limit(self): request = GetRecordLimitRequest(GET_RECORD_LIMIT) @@ -461,11 +461,9 @@ def test_create_record(self): self.assertEqual("xxxxxxxx_YYYYYYYYYY__", request.login_ticket) self.assertEqual("test", request.table_id) - self.assertEqual("MyAsciiString",request.values[0][0]) - self.assertEqual("asciiStringValue",request.values[0][1]) - self.assertEqual("this is a record",request.values[0][2]) - - + self.assertEqual("MyAsciiString", request.values[0][0]) + self.assertEqual("asciiStringValue", request.values[0][1]) + self.assertEqual("this is a record", request.values[0][2]) if __name__ == "__main__": diff --git a/src/tests/library/encrypt_test.py b/src/tests/library/encrypt_tests.py similarity index 71% rename from src/tests/library/encrypt_test.py rename to src/tests/library/encrypt_tests.py index c3b904901..a906820b8 100644 --- a/src/tests/library/encrypt_test.py +++ b/src/tests/library/encrypt_tests.py @@ -6,12 +6,12 @@ class GSEncryptionTest(unittest.TestCase): def test_encryption(self): enc = ChatCrypt("123345") result = enc.encrypt("hello".encode("ascii")) - self.assertEqual(result, b"\xe9D\x91Q\xb9") + self.assertEqual(result, b"\xda\xaek^d") def test_decryption(self): enc = ChatCrypt("123345") - result = enc.decrypt(b"\xe9D\x91Q\xb9") - self.assertEqual(result, "hello") + result = enc.decrypt(b"\xda\xaek^d") + self.assertEqual(result, b"hello") if __name__ == "__main__": From f3689f6fee418796da582b4c3de4b57a4b16093d Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 26 Aug 2024 16:16:04 +0800 Subject: [PATCH 095/231] fix: added new tests and fix testing errors --- src/backends/routers/gamespy/chat.py | 22 +-- src/library/src/abstractions/client.py | 4 +- src/library/src/abstractions/contracts.py | 1 - src/library/src/abstractions/handler.py | 26 ++-- src/library/src/abstractions/switcher.py | 2 +- src/library/src/encryption/encoding.py | 2 +- src/servers/chat/src/exceptions/general.py | 2 - src/servers/natneg/src/handlers/handlers.py | 30 ++-- .../src/abstractions/contracts.py | 19 +-- .../abstractions/{handler.py => handlers.py} | 3 +- .../src/applications/client.py | 5 +- .../src/contracts/requests/buddy.py | 96 ++++++------ .../src/contracts/requests/general.py | 138 +++++++++++++---- .../src/contracts/requests/profile.py | 146 +++++++++--------- .../src/contracts/responses/general.py | 28 ++-- .../src/contracts/results/general.py | 5 + .../src/handlers/buddy.py | 12 +- .../src/handlers/general.py | 34 ++-- .../src/handlers/profile.py | 2 +- .../src/handlers/switcher.py | 16 +- .../tests/game_tests.py | 9 +- .../tests/general_request_tests.py | 18 +-- .../tests/profile_request_tests.py | 20 ++- .../presence_search_player/__init__.py | 0 .../src/abstractions/contracts.py | 10 +- .../src/applications/client.py | 6 +- .../src/contracts/requests.py | 18 ++- .../src/contracts/responses.py | 10 +- .../src/contracts/results.py | 30 ++-- .../src/handlers/handlers.py | 55 ++++--- .../src/handlers/switcher.py | 34 ++-- .../tests/game_tests.py | 31 ++++ .../tests/handler_tests.py | 73 +++++++++ .../tests/mock_objects.py | 19 +++ 34 files changed, 580 insertions(+), 346 deletions(-) rename src/servers/presence_connection_manager/src/abstractions/{handler.py => handlers.py} (95%) create mode 100644 src/servers/presence_search_player/__init__.py create mode 100644 src/servers/presence_search_player/tests/game_tests.py create mode 100644 src/servers/presence_search_player/tests/handler_tests.py create mode 100644 src/servers/presence_search_player/tests/mock_objects.py diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 24dc6df25..72502fb63 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -1,41 +1,41 @@ -from flask import session -from flask_socketio import SocketIO, emit +# from backends.urls import CHAT +# from fastapi import FastAPI, WebSocket -import socketio -from backends.urls import CHAT +# app = FastAPI() -# @socketio.on(f"/{CHAT}/join") +# @app.websocket(f"/{CHAT}/join") # def join(request: "JoinRequest"): +# # directly send the irc chat raw message to the channel # raise NotImplementedError() -# @socketio.on(f"/{CHAT}/setckey") +# @app.websocket(f"/{CHAT}/setckey") # def setckey(request: "SetCKeyRequest"): # sender = session.get("nickname") # raise NotImplementedError() -# @socketio.on(f"/{CHAT}/setchankey") +# @app.websocket(f"/{CHAT}/setchankey") # def set_channel_key(request: "SetChannelKeyRequest"): # raise NotImplementedError() -# @socketio.on(f"/{CHAT}/atm") +# @app.websocket(f"/{CHAT}/atm") # def atm(request: "ATMRequest"): # raise NotImplementedError() -# @socketio.on(f"/{CHAT}/utm") +# @app.websocket(f"/{CHAT}/utm") # def utm(request: "UTMRequest"): # raise NotImplementedError() -# @socketio.on(f"/{CHAT}/notice") +# @app.websocket(f"/{CHAT}/notice") # def notice(request: "UTMRequest"): # raise NotImplementedError() -# @socketio.on(f"/{CHAT}/private") +# @app.websocket(f"/{CHAT}/private") # def notice(request: "PrivateRequest"): # raise NotImplementedError() diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index 0590452d7..a761337b9 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -1,4 +1,3 @@ -import abc from library.src.encryption.encoding import Encoding from library.src.exceptions.error import UniSpyException from library.src.log.log_manager import LogWriter @@ -43,7 +42,6 @@ def on_connected(self) -> None: def on_disconnected(self) -> None: pass - @abc.abstractmethod def create_switcher(self, buffer) -> "SwitcherBase": pass @@ -69,7 +67,7 @@ def send(self, response: "ResponseBase") -> None: sending_buffer = response.sending_buffer if isinstance(sending_buffer, str): buffer: bytes = Encoding.get_bytes(sending_buffer) - if isinstance(sending_buffer, bytes): + elif isinstance(sending_buffer, bytes): buffer = sending_buffer else: raise UniSpyException("not supported buffer type") diff --git a/src/library/src/abstractions/contracts.py b/src/library/src/abstractions/contracts.py index 17d3c5ed4..33c765738 100644 --- a/src/library/src/abstractions/contracts.py +++ b/src/library/src/abstractions/contracts.py @@ -1,6 +1,5 @@ import abc from copy import deepcopy -from dataclasses import dataclass import enum from typing import Optional from uuid import UUID diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index 8a2829ba6..47815fd29 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -1,7 +1,5 @@ -import abc from library.src.abstractions.client import ClientBase from library.src.exceptions.error import UniSpyException -from typing import TYPE_CHECKING from typing import Type import requests @@ -19,7 +17,15 @@ class CmdHandlerBase: _response: "ResponseBase" _result_cls: "Type[ResultBase]" """ - the result class type + the result type class, use to deserialize json data from backend + """ + _is_uploading = True + """ + whether need send data to backend + """ + _is_feaching = True + """ + whether need get data from backend """ def __init__(self, client: "ClientBase", request: "RequestBase") -> None: @@ -27,7 +33,7 @@ def __init__(self, client: "ClientBase", request: "RequestBase") -> None: assert issubclass(type(client), ClientBase) assert issubclass(type(request), RequestBase) # if some subclass do not need result, override the __init__() in that subclass - if self._result_cls is not None: + if self._is_feaching: assert issubclass(self._result_cls, ResultBase) self._client = client @@ -59,9 +65,11 @@ def _data_operate(self) -> None: """ virtual function, can be override """ + # we check whether we need fetch data + if not self._is_uploading: + return # default use restapi to access to our backend service - # get the http response and create it with this type url = f"{ CONFIG.backend.url}/{self._client.server_config.server_name}/{self.__class__.__name__}/" @@ -72,12 +80,8 @@ def _data_operate(self) -> None: result = response.json() # if the result cls is not declared, we do not parse the response values - if self._result_cls is None: - return - if self._result is not None: - return - self._result = self._result_cls(**result) - pass + if self._is_feaching: + self._result = self._result_cls(**result) def _response_construct(self) -> None: """construct response here in specific child class""" diff --git a/src/library/src/abstractions/switcher.py b/src/library/src/abstractions/switcher.py index fc163b5de..857504165 100644 --- a/src/library/src/abstractions/switcher.py +++ b/src/library/src/abstractions/switcher.py @@ -52,5 +52,5 @@ def _process_raw_request(self) -> None: pass @abc.abstractmethod - def _create_cmd_handlers(self, name: object, raw_request: object) -> CmdHandlerBase: + def _create_cmd_handlers(self, name: object, raw_request: object) -> Optional[CmdHandlerBase]: pass diff --git a/src/library/src/encryption/encoding.py b/src/library/src/encryption/encoding.py index 3800639d2..33739938a 100644 --- a/src/library/src/encryption/encoding.py +++ b/src/library/src/encryption/encoding.py @@ -10,4 +10,4 @@ def get_string(data: bytes) -> str: @staticmethod def get_bytes(data: str) -> bytes: assert isinstance(data, str) - return bytes(data.encode()) + return data.encode() diff --git a/src/servers/chat/src/exceptions/general.py b/src/servers/chat/src/exceptions/general.py index 8842a7601..0dd82b0e8 100644 --- a/src/servers/chat/src/exceptions/general.py +++ b/src/servers/chat/src/exceptions/general.py @@ -1,6 +1,5 @@ from servers.chat.src.abstractions.contract import SERVER_DOMAIN from servers.chat.src.enums.irc_error_code import IRCErrorCode -import abc from library.src.exceptions.error import UniSpyException as ER @@ -18,7 +17,6 @@ def __init__(self, message: "str", error_code: "IRCErrorCode") -> None: assert isinstance(error_code, IRCErrorCode) self.error_code = error_code - @abc.abstractedmethod def build(self): pass diff --git a/src/servers/natneg/src/handlers/handlers.py b/src/servers/natneg/src/handlers/handlers.py index d916d293e..ffef44a8d 100644 --- a/src/servers/natneg/src/handlers/handlers.py +++ b/src/servers/natneg/src/handlers/handlers.py @@ -1,5 +1,6 @@ from copy import copy +from servers.natneg.src.abstractions.contracts import RequestBase from servers.natneg.src.abstractions.handlers import CmdHandlerBase from servers.natneg.src.applications.client import Client from servers.natneg.src.contracts.requests import ( @@ -29,10 +30,7 @@ class AddressCheckHandler(CmdHandlerBase): - _request: AddressCheckRequest - _result: AddressCheckResult - _response: InitResponse - _result_cls = None + _is_feaching = False def __init__(self, client: Client, request: AddressCheckRequest) -> None: super().__init__(client, request) @@ -57,7 +55,11 @@ def _response_construct(self) -> None: class ConnectAckHandler(CmdHandlerBase): - _request: ConnectAckRequest + _is_feaching = False + + def __init__(self, client: Client, request: ConnectAckRequest) -> None: + super().__init__(client, request) + assert isinstance(request, ConnectAckRequest) def _data_operate(self) -> None: self._client.log_info( @@ -66,8 +68,7 @@ def _data_operate(self) -> None: class ConnectHandler(CmdHandlerBase): - _request: ConnectRequest - _result: ConnectResult + _is_feaching = False def __init__(self, client: Client, request: ConnectRequest) -> None: super().__init__(client, request) @@ -75,10 +76,7 @@ def __init__(self, client: Client, request: ConnectRequest) -> None: class ErtAckHandler(CmdHandlerBase): - _request: ErtAckRequest - _result: ErtAckResult - _response: ErcAckResponse - _result_cls = None + _is_feaching = False def __init__(self, client: Client, request: ErtAckRequest) -> None: super().__init__(client, request) @@ -100,10 +98,7 @@ class InitHandler(CmdHandlerBase): In init process, we need response the initresponse first to make client not timeout """ - _request: InitRequest - _result: InitResult - _response: InitResponse - _result_cls = None + _is_feaching = False def __init__(self, client: Client, request: InitRequest) -> None: super().__init__(client, request) @@ -126,10 +121,7 @@ def _response_send(self) -> None: class NatifyHandler(CmdHandlerBase): - _request: NatifyRequest - _result: NatifyResult - _response: InitResponse - _result_cls = None + _is_feaching = False def __init__(self, client: Client, request: NatifyRequest) -> None: super().__init__(client, request) diff --git a/src/servers/presence_connection_manager/src/abstractions/contracts.py b/src/servers/presence_connection_manager/src/abstractions/contracts.py index 26b05fff3..7be2a0b37 100644 --- a/src/servers/presence_connection_manager/src/abstractions/contracts.py +++ b/src/servers/presence_connection_manager/src/abstractions/contracts.py @@ -15,7 +15,7 @@ def normalize_request(message: str): if "login" in message: message = message.replace("\\-", "\\") pos = message.index("\\", message.index("\\") + 1) - if message[pos : pos + 2] != "\\\\": + if message[pos: pos + 2] != "\\\\": message = message[:pos] + "\\" + message[pos:] return message @@ -24,7 +24,7 @@ class RequestBase(library.src.abstractions.contracts.RequestBase): command_name: str operation_id: int raw_request: str - request_key_values: Dict[str, str] + request_dict: Dict[str, str] def __init__(self, raw_request: str) -> None: assert isinstance(raw_request, str) @@ -32,13 +32,14 @@ def __init__(self, raw_request: str) -> None: def parse(self): super().parse() - self.request_key_values = convert_to_key_value(self.raw_request) - self.command_name = list(self.request_key_values.keys())[0] - - if "id" in self.request_key_values: - self.operation_id = int(self.request_key_values["id"]) - else: - raise GPParseException("namespaceid is invalid.") + self.request_dict = convert_to_key_value(self.raw_request) + self.command_name = list(self.request_dict.keys())[0] + + if "id" in self.request_dict: + try: + self.operation_id = int(self.request_dict["id"]) + except: + raise GPParseException("namespaceid is invalid.") class ResultBase(library.src.abstractions.contracts.ResultBase): diff --git a/src/servers/presence_connection_manager/src/abstractions/handler.py b/src/servers/presence_connection_manager/src/abstractions/handlers.py similarity index 95% rename from src/servers/presence_connection_manager/src/abstractions/handler.py rename to src/servers/presence_connection_manager/src/abstractions/handlers.py index 500a811db..dacab4e2b 100644 --- a/src/servers/presence_connection_manager/src/abstractions/handler.py +++ b/src/servers/presence_connection_manager/src/abstractions/handlers.py @@ -1,4 +1,3 @@ -import abc from servers.presence_connection_manager.src.applications.client import Client from servers.presence_connection_manager.src.enums.general import LoginStatus @@ -27,7 +26,7 @@ def _handle_exception(self, ex) -> None: super()._handle_exception(ex) -class LoginHandlerBase(CmdHandlerBase): +class LoginedHandlerBase(CmdHandlerBase): def _request_check(self) -> None: if self._client.info.login_status != LoginStatus.COMPLETED: diff --git a/src/servers/presence_connection_manager/src/applications/client.py b/src/servers/presence_connection_manager/src/applications/client.py index 966007e88..0d343bd42 100644 --- a/src/servers/presence_connection_manager/src/applications/client.py +++ b/src/servers/presence_connection_manager/src/applications/client.py @@ -7,10 +7,8 @@ from servers.presence_connection_manager.src.aggregates.login_challenge import ( SERVER_CHALLENGE, ) -from servers.presence_connection_manager.src.handlers.switcher import Switcher from servers.presence_connection_manager.src.enums.general import LoginStatus -from typing import TYPE_CHECKING from servers.presence_connection_manager.src.aggregates.sdk_revision import SdkRevision from servers.presence_connection_manager.src.enums.general import LoginStatus @@ -55,5 +53,6 @@ def on_connected(self) -> None: self.log_network_sending(buffer) self.connection.send(buffer) - def create_switcher(self, buffer) -> "SwitcherBase": + def create_switcher(self, buffer) -> SwitcherBase: + from servers.presence_connection_manager.src.handlers.switcher import Switcher return Switcher(self, buffer) diff --git a/src/servers/presence_connection_manager/src/contracts/requests/buddy.py b/src/servers/presence_connection_manager/src/contracts/requests/buddy.py index 00fc40da5..8eded6150 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests/buddy.py +++ b/src/servers/presence_connection_manager/src/contracts/requests/buddy.py @@ -1,4 +1,5 @@ from typing import Optional +from library.src.extentions.gamespy_utils import convert_to_key_value from servers.presence_connection_manager.src.abstractions.contracts import RequestBase from servers.presence_connection_manager.src.aggregates.user_status import UserStatus from servers.presence_connection_manager.src.aggregates.user_status_info import ( @@ -15,17 +16,17 @@ class AddBuddyRequest(RequestBase): def parse(self): super().parse() if ( - ("sesskey" not in self.request_key_values) - or ("newprofileid" not in self.request_key_values) - or ("reason" not in self.request_key_values) + ("sesskey" not in self.request_dict) + or ("newprofileid" not in self.request_dict) + or ("reason" not in self.request_dict) ): raise GPParseException("addbuddy request is invalid.") - - self.friend_profile_id = int(self.request_key_values["newprofileid"]) - if self.friend_profile_id == 0: + try: + self.friend_profile_id = int(self.request_dict["newprofileid"]) + except: raise GPParseException("newprofileid format is incorrect.") - self.reason = self.request_key_values["reason"] + self.reason = self.request_dict["reason"] class DelBuddyRequest(RequestBase): @@ -33,11 +34,12 @@ class DelBuddyRequest(RequestBase): def parse(self): super().parse() - if "delprofileid" not in self.request_key_values: + if "delprofileid" not in self.request_dict: raise GPParseException("delprofileid is missing.") - self.friend_profile_id = int(self.request_key_values["delprofileid"]) - if self.friend_profile_id == 0: + try: + self.friend_profile_id = int(self.request_dict["delprofileid"]) + except: raise GPParseException("delprofileid format is incorrect.") @@ -49,23 +51,23 @@ class InviteToRequest(RequestBase): def parse(self): super().parse() - if "productid" not in self.request_key_values: + if "productid" not in self.request_dict: raise GPParseException("productid is missing.") - if "sesskey" not in self.request_key_values: + if "sesskey" not in self.request_dict: raise GPParseException("sesskey is missing.") try: - self.product_id = int(self.request_key_values["productid"]) + self.product_id = int(self.request_dict["productid"]) except ValueError: raise GPParseException("productid format is incorrect.") try: - self.profile_id = int(self.request_key_values["profileid"]) + self.profile_id = int(self.request_dict["profileid"]) except ValueError: raise GPParseException("profileid format is incorrect.") - self.session_key = self.request_key_values["sesskey"] + self.session_key = self.request_dict["sesskey"] class StatusInfoRequest(RequestBase): @@ -81,60 +83,60 @@ def parse(self): super().parse() if ( - "state" not in self.request_key_values - or "hostip" not in self.request_key_values - or "hprivip" not in self.request_key_values - or "qport" not in self.request_key_values - or "hport" not in self.request_key_values - or "sessflags" not in self.request_key_values - or "rechstatus" not in self.request_key_values - or "gametype" not in self.request_key_values - or "gamevariant" not in self.request_key_values - or "gamemapname" not in self.request_key_values + "state" not in self.request_dict + or "hostip" not in self.request_dict + or "hprivip" not in self.request_dict + or "qport" not in self.request_dict + or "hport" not in self.request_dict + or "sessflags" not in self.request_dict + or "rechstatus" not in self.request_dict + or "gametype" not in self.request_dict + or "gamevariant" not in self.request_dict + or "gamemapname" not in self.request_dict ): raise GPParseException("StatusInfo request is invalid.") - self.status_info.status_state = self.request_key_values["state"] - self.status_info.host_ip = self.request_key_values["hostip"] - self.status_info.host_private_ip = self.request_key_values["hprivip"] + self.status_info.status_state = self.request_dict["state"] + self.status_info.host_ip = self.request_dict["hostip"] + self.status_info.host_private_ip = self.request_dict["hprivip"] try: - self.status_info.query_report_port = int(self.request_key_values["qport"]) - self.status_info.host_port = int(self.request_key_values["hport"]) - self.status_info.session_flags = self.request_key_values["sessflags"] + self.status_info.query_report_port = int( + self.request_dict["qport"]) + self.status_info.host_port = int(self.request_dict["hport"]) + self.status_info.session_flags = self.request_dict["sessflags"] except ValueError: - raise GPParseException("qport, hport, or sessflags format is incorrect.") + raise GPParseException( + "qport, hport, or sessflags format is incorrect.") - self.status_info.rich_status = self.request_key_values["rechstatus"] - self.status_info.game_type = self.request_key_values["gametype"] - self.status_info.game_variant = self.request_key_values["gamevariant"] - self.status_info.game_map_name = self.request_key_values["gamemapname"] + self.status_info.rich_status = self.request_dict["rechstatus"] + self.status_info.game_type = self.request_dict["gametype"] + self.status_info.game_variant = self.request_dict["gamevariant"] + self.status_info.game_map_name = self.request_dict["gamemapname"] class StatusRequest(RequestBase): - status: UserStatus + status: UserStatus = UserStatus() is_get_status: bool - def __init__(self, raw_request): - super().__init__(raw_request) - def parse(self): - super().parse() + self.request_dict = convert_to_key_value(self.raw_request) + self.command_name = list(self.request_dict.keys())[0] - if "status" not in self.request_key_values: + if "status" not in self.request_dict: raise GPParseException("status is missing.") - if "statstring" not in self.request_key_values: + if "statstring" not in self.request_dict: raise GPParseException("statstring is missing.") - if "locstring" not in self.request_key_values: + if "locstring" not in self.request_dict: raise GPParseException("locstring is missing.") try: - status_code = int(self.request_key_values["status"]) + status_code = int(self.request_dict["status"]) self.status.current_status = GPStatusCode(status_code) except ValueError: raise GPParseException("status format is incorrect.") - self.status.location_string = self.request_key_values["locstring"] - self.status.status_string = self.request_key_values["statstring"] + self.status.location_string = self.request_dict["locstring"] + self.status.status_string = self.request_dict["statstring"] diff --git a/src/servers/presence_connection_manager/src/contracts/requests/general.py b/src/servers/presence_connection_manager/src/contracts/requests/general.py index ace4e90a2..59396e36a 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests/general.py +++ b/src/servers/presence_connection_manager/src/contracts/requests/general.py @@ -1,3 +1,5 @@ +from library.src.extentions.gamespy_utils import is_email_format_correct +from library.src.extentions.password_encoder import process_password from servers.presence_connection_manager.src.abstractions.contracts import RequestBase from servers.presence_connection_manager.src.enums.general import ( LoginType, @@ -39,38 +41,38 @@ def __init__(self, raw_request): def parse(self): super().parse() - if "challenge" not in self.request_key_values: + if "challenge" not in self.request_dict: raise GPParseException("challenge is missing") - if "response" not in self.request_key_values: + if "response" not in self.request_dict: raise GPParseException("response is missing") - self.user_challenge = self.request_key_values["challenge"] - self.response = self.request_key_values["response"] + self.user_challenge = self.request_dict["challenge"] + self.response = self.request_dict["response"] if ( - "uniquenick" in self.request_key_values - and "namespaceid" in self.request_key_values + "uniquenick" in self.request_dict + and "namespaceid" in self.request_dict ): - namespace_id = int(self.request_key_values["namespaceid"]) + namespace_id = int(self.request_dict["namespaceid"]) self.type = LoginType.UNIQUENICK_NAMESPACE_ID - self.unique_nick = self.request_key_values["uniquenick"] + self.unique_nick = self.request_dict["uniquenick"] self.user_data = self.unique_nick self.namespace_id = namespace_id - elif "authtoken" in self.request_key_values: + elif "authtoken" in self.request_dict: self.type = LoginType.AUTH_TOKEN - self.auth_token = self.request_key_values["authtoken"] + self.auth_token = self.request_dict["authtoken"] self.user_data = self.auth_token - elif "user" in self.request_key_values: + elif "user" in self.request_dict: self.type = LoginType.NICK_EMAIL - self.user_data = self.request_key_values["user"] + self.user_data = self.request_dict["user"] pos = self.user_data.index("@") if pos == -1 or pos < 1 or (pos + 1) >= len(self.user_data): raise GPParseException("user format is incorrect") self.nick = self.user_data[:pos] - self.email = self.user_data[pos + 1 :] - if "namespaceid" in self.request_key_values: - namespace_id = int(self.request_key_values["namespaceid"]) + self.email = self.user_data[pos + 1:] + if "namespaceid" in self.request_dict: + namespace_id = int(self.request_dict["namespaceid"]) self.namespace_id = namespace_id else: raise GPParseException("Unknown login method detected.") @@ -78,40 +80,110 @@ def parse(self): self.parse_other_data() def parse_other_data(self): - if "userid" in self.request_key_values: - user_id = int(self.request_key_values["userid"]) + if "userid" in self.request_dict: + user_id = int(self.request_dict["userid"]) self.user_id = user_id - if "profileid" in self.request_key_values: - profile_id = int(self.request_key_values["profileid"]) + if "profileid" in self.request_dict: + profile_id = int(self.request_dict["profileid"]) self.profile_id = profile_id - if "partnerid" in self.request_key_values: - partner_id = int(self.request_key_values["partnerid"]) + if "partnerid" in self.request_dict: + partner_id = int(self.request_dict["partnerid"]) self.partner_id = partner_id - if "sdkrevision" in self.request_key_values: - sdk_revision_type = int(self.request_key_values["sdkrevision"]) + if "sdkrevision" in self.request_dict: + sdk_revision_type = int(self.request_dict["sdkrevision"]) self.sdk_revision_type = SdkRevisionType(sdk_revision_type) - if "gamename" in self.request_key_values: - self.game_name = self.request_key_values["gamename"] + if "gamename" in self.request_dict: + self.game_name = self.request_dict["gamename"] - if "port" in self.request_key_values: - game_port = int(self.request_key_values["port"]) + if "port" in self.request_dict: + game_port = int(self.request_dict["port"]) self.game_port = game_port - if "productid" in self.request_key_values: - product_id = int(self.request_key_values["productid"]) + if "productid" in self.request_dict: + product_id = int(self.request_dict["productid"]) self.product_id = product_id - if "firewall" in self.request_key_values: - self.firewall = bool(self.request_key_values["firewall"]) + if "firewall" in self.request_dict: + self.firewall = bool(self.request_dict["firewall"]) - if "quiet" in self.request_key_values: - quiet = int(self.request_key_values["quiet"]) + if "quiet" in self.request_dict: + quiet = int(self.request_dict["quiet"]) self.quiet_mode_flags = QuietModeType(quiet) class LogoutRequest(RequestBase): pass + + +class NewUserRequest(RequestBase): + product_id: int + game_port: int + cd_key: str + has_game_name: bool + has_product_id: bool + has_cdkey: bool + has_partner_id: bool + has_game_port: bool + nick: str + email: str + password: str + partner_id: int + game_name: str + uniquenick: str + + def parse(self): + super().parse() + self.password = process_password(self.request_dict) + + if "nick" not in self.request_dict: + raise GPParseException("nickname is missing.") + if "email" not in self.request_dict: + raise GPParseException("email is missing.") + if not is_email_format_correct(self.request_dict["email"]): + raise GPParseException("email format is incorrect.") + self.nick = self.request_dict["nick"] + self.email = self.request_dict["email"] + + if "uniquenick" in self.request_dict and "namespaceid" in self.request_dict: + if "namespaceid" in self.request_dict: + try: + self.namespace_id = int(self.request_dict["namespaceid"]) + except ValueError: + raise GPParseException("namespaceid is incorrect.") + + self.uniquenick = self.request_dict["uniquenick"] + self.parse_other_info() + + def parse_other_info(self): + if "partnerid" in self.request_dict: + try: + self.partner_id = int(self.request_dict["partnerid"]) + self.has_partner_id_flag = True + except ValueError: + raise GPParseException("partnerid is incorrect.") + + if "productid" in self.request_dict: + try: + self.product_id = int(self.request_dict["productid"]) + self.has_product_id_flag = True + except ValueError: + raise GPParseException("productid is incorrect.") + + if "gamename" in self.request_dict: + self.has_game_name_flag = True + self.game_name = self.request_dict["gamename"] + + if "port" in self.request_dict: + try: + self.game_port = int(self.request_dict["port"]) + self.has_game_port_flag = True + except ValueError: + raise GPParseException("port is incorrect.") + + if "cdkey" in self.request_dict: + self.has_cd_key_enc_flag = True + self.cd_key = self.request_dict["cdkey"] diff --git a/src/servers/presence_connection_manager/src/contracts/requests/profile.py b/src/servers/presence_connection_manager/src/contracts/requests/profile.py index 12e67f103..28912313b 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests/profile.py +++ b/src/servers/presence_connection_manager/src/contracts/requests/profile.py @@ -13,11 +13,11 @@ class AddBlockRequest(RequestBase): def parse(self): super().parse() - if "profileid" not in self.request_key_values: + if "profileid" not in self.request_dict: raise GPParseException("profileid is missing") try: - self.taget_id = int(self.request_key_values["profileid"]) + self.taget_id = int(self.request_dict["profileid"]) except ValueError: raise GPParseException("profileid format is incorrect") @@ -29,18 +29,18 @@ class GetProfileRequest(RequestBase): def parse(self): super().parse() - if "profileid" not in self.request_key_values: + if "profileid" not in self.request_dict: raise GPParseException("profileid is missing") try: - self.profile_id = int(self.request_key_values["profileid"]) + self.profile_id = int(self.request_dict["profileid"]) except ValueError: raise GPParseException("profileid format is incorrect") - if "sesskey" not in self.request_key_values: + if "sesskey" not in self.request_dict: raise GPParseException("sesskey is missing") - self.session_key = self.request_key_values["sesskey"] + self.session_key = self.request_dict["sesskey"] class NewProfileRequest(RequestBase): @@ -52,29 +52,29 @@ class NewProfileRequest(RequestBase): def parse(self): super().parse() - if "sesskey" not in self.request_key_values: + if "sesskey" not in self.request_dict: raise GPParseException("sesskey is missing") - self.session_key = self.request_key_values["sesskey"] + self.session_key = self.request_dict["sesskey"] - if "replace" in self.request_key_values: + if "replace" in self.request_dict: if ( - "oldnick" not in self.request_key_values - and "nick" not in self.request_key_values + "oldnick" not in self.request_dict + and "nick" not in self.request_dict ): raise GPParseException("oldnick or nick is missing.") - if "oldnick" in self.request_key_values: - self.old_nick = self.request_key_values["oldnick"] - if "nick" in self.request_key_values: - self.new_nick = self.request_key_values["nick"] + if "oldnick" in self.request_dict: + self.old_nick = self.request_dict["oldnick"] + if "nick" in self.request_dict: + self.new_nick = self.request_dict["nick"] self.is_replace_nick_name = True else: - if "nick" not in self.request_key_values: + if "nick" not in self.request_dict: raise GPParseException("nick is missing.") - self.new_nick = self.request_key_values["nick"] + self.new_nick = self.request_dict["nick"] self.is_replace_nick_name = False @@ -85,15 +85,15 @@ class RegisterCDKeyRequest(RequestBase): def parse(self): super().parse() - if "sesskey" not in self.request_key_values: + if "sesskey" not in self.request_dict: raise GPParseException("sesskey is missing") - self.session_key = self.request_key_values["sesskey"] + self.session_key = self.request_dict["sesskey"] - if "cdkeyenc" not in self.request_key_values: + if "cdkeyenc" not in self.request_dict: raise GPParseException("cdkeyenc is missing") - self.cdkey_enc = self.request_key_values["cdkeyenc"] + self.cdkey_enc = self.request_dict["cdkeyenc"] class RegisterNickRequest(RequestBase): @@ -104,19 +104,19 @@ class RegisterNickRequest(RequestBase): def parse(self): super().parse() - if "sesskey" not in self.request_key_values: + if "sesskey" not in self.request_dict: raise GPParseException("sesskey is missing") - self.session_key = self.request_key_values["sesskey"] + self.session_key = self.request_dict["sesskey"] - if "uniquenick" not in self.request_key_values: + if "uniquenick" not in self.request_dict: raise GPParseException("uniquenick is missing") - self.unique_nick = self.request_key_values["uniquenick"] + self.unique_nick = self.request_dict["uniquenick"] - if "partnerid" in self.request_key_values: + if "partnerid" in self.request_dict: try: - self.partner_id = int(self.request_key_values["partnerid"]) + self.partner_id = int(self.request_dict["partnerid"]) except ValueError: raise GPParseException("partnerid is missing") @@ -150,38 +150,38 @@ class UpdateProfileRequest(RequestBase): def parse(self): super().parse() - if "publicmask" in self.request_key_values: - if not self.request_key_values["publicmask"].isdigit(): + if "publicmask" in self.request_dict: + if not self.request_dict["publicmask"].isdigit(): raise GPParseException("publicmask format is incorrect") self.has_public_mask_flag = True - self.public_mask = PublicMasks(int(self.request_key_values["publicmask"])) + self.public_mask = PublicMasks(int(self.request_dict["publicmask"])) - if "sesskey" not in self.request_key_values: + if "sesskey" not in self.request_dict: raise GPParseException("sesskey is missing") - self.session_key = self.request_key_values["sesskey"] + self.session_key = self.request_dict["sesskey"] - if "firstname" in self.request_key_values: - self.first_name = self.request_key_values["firstname"] + if "firstname" in self.request_dict: + self.first_name = self.request_dict["firstname"] self.has_first_name_flag = True - if "lastname" in self.request_key_values: - self.last_name = self.request_key_values["lastname"] + if "lastname" in self.request_dict: + self.last_name = self.request_dict["lastname"] self.has_last_name_flag = True - if "icquin" in self.request_key_values: - if not self.request_key_values["icquin"].isdigit(): + if "icquin" in self.request_dict: + if not self.request_dict["icquin"].isdigit(): raise GPParseException("icquin format is incorrect") self.has_icq_flag = True - self.icq_uin = int(self.request_key_values["icquin"]) + self.icq_uin = int(self.request_dict["icquin"]) # Remaining attribute assignments... - if "homepage" in self.request_key_values: - self.home_page = self.request_key_values["homepage"] + if "homepage" in self.request_dict: + self.home_page = self.request_dict["homepage"] self.has_home_page_flag = True - if "birthday" in self.request_key_values: + if "birthday" in self.request_dict: try: - date = int(self.request_key_values["birthday"]) + date = int(self.request_dict["birthday"]) d = (date >> 24) & 0xFF m = (date >> 16) & 0xFF y = date & 0xFFFF @@ -193,32 +193,32 @@ def parse(self): except ValueError: pass - if "sex" in self.request_key_values: + if "sex" in self.request_dict: try: - self.sex = int(self.request_key_values["sex"]) + self.sex = int(self.request_dict["sex"]) self.has_sex_flag = True except ValueError: raise GPParseException("sex format is incorrect") - if "zipcode" in self.request_key_values: - self.zip_code = self.request_key_values["zipcode"] + if "zipcode" in self.request_dict: + self.zip_code = self.request_dict["zipcode"] self.has_zip_code = True - if "countrycode" in self.request_key_values: - self.country_code = self.request_key_values["countrycode"] + if "countrycode" in self.request_dict: + self.country_code = self.request_dict["countrycode"] self.has_country_code = True - if "partnerid" in self.request_key_values: + if "partnerid" in self.request_dict: try: - self.partner_id = int(self.request_key_values["partnerid"]) + self.partner_id = int(self.request_dict["partnerid"]) except ValueError: raise GPParseException("partnerid is incorrect") - if "nick" in self.request_key_values: - self.nick = self.request_key_values["nick"] + if "nick" in self.request_dict: + self.nick = self.request_dict["nick"] - if "uniquenick" in self.request_key_values: - self.uniquenick = self.request_key_values["uniquenick"] + if "uniquenick" in self.request_dict: + self.uniquenick = self.request_dict["uniquenick"] class UpdateUiRequest(RequestBase): @@ -235,29 +235,29 @@ class UpdateUiRequest(RequestBase): def parse(self): super().parse() - if "cpubrandid" in self.request_key_values: - self.cpubrandid = self.request_key_values["cpubrandid"] + if "cpubrandid" in self.request_dict: + self.cpubrandid = self.request_dict["cpubrandid"] - if "cpuspeed" in self.request_key_values: - self.cpuspeed = self.request_key_values["cpuspeed"] + if "cpuspeed" in self.request_dict: + self.cpuspeed = self.request_dict["cpuspeed"] - if "memory" in self.request_key_values: - self.memory = self.request_key_values["memory"] + if "memory" in self.request_dict: + self.memory = self.request_dict["memory"] - if "videocard1ram" in self.request_key_values: - self.videocard1ram = self.request_key_values["videocard1ram"] + if "videocard1ram" in self.request_dict: + self.videocard1ram = self.request_dict["videocard1ram"] - if "videocard2ram" in self.request_key_values: - self.videocard2ram = self.request_key_values["videocard2ram"] + if "videocard2ram" in self.request_dict: + self.videocard2ram = self.request_dict["videocard2ram"] - if "connectionid" in self.request_key_values: - self.connectionid = self.request_key_values["connectionid"] + if "connectionid" in self.request_dict: + self.connectionid = self.request_dict["connectionid"] - if "connectionspeed" in self.request_key_values: - self.connectionspeed = self.request_key_values["connectionspeed"] + if "connectionspeed" in self.request_dict: + self.connectionspeed = self.request_dict["connectionspeed"] - if "hasnetwork" in self.request_key_values: - self.hasnetwork = self.request_key_values["hasnetwork"] + if "hasnetwork" in self.request_dict: + self.hasnetwork = self.request_dict["hasnetwork"] - if "pic" in self.request_key_values: - self.pic = self.request_key_values["pic"] + if "pic" in self.request_dict: + self.pic = self.request_dict["pic"] diff --git a/src/servers/presence_connection_manager/src/contracts/responses/general.py b/src/servers/presence_connection_manager/src/contracts/responses/general.py index 6c22a8683..f32a57a3d 100644 --- a/src/servers/presence_connection_manager/src/contracts/responses/general.py +++ b/src/servers/presence_connection_manager/src/contracts/responses/general.py @@ -1,4 +1,4 @@ -from servers.presence_connection_manager.src.abstractions.contracts import ResponseBase +from servers.presence_connection_manager.src.abstractions.contracts import RequestBase, ResponseBase, ResultBase from servers.presence_connection_manager.src.applications.client import ( LOGIN_TICKET, SESSION_KEY, @@ -6,17 +6,12 @@ from servers.presence_connection_manager.src.contracts.requests.general import ( KeepAliveRequest, LoginRequest, + NewUserRequest, ) from servers.presence_connection_manager.src.contracts.results.general import ( LoginResult, + NewUserResult, ) -from servers.presence_search_player.src.contracts.requests import ( - NewUserRequest, -) -from servers.presence_search_player.src.contracts.responses import ( - NewUserResponse as NUR, -) -from servers.presence_search_player.src.contracts.results import NewUserResult class KeepAliveResponse(ResponseBase): @@ -40,7 +35,8 @@ def build(self): # string checkSumStr = _result.DatabaseResults.Nick + _result.DatabaseResults.UniqueNick + _result.DatabaseResults.NamespaceID; # _connection.UserData.SessionKey = _crc.ComputeChecksum(checkSumStr); - self.sending_buffer = f"\\lc\\2\\sesskey\\{SESSION_KEY}\\proof\\{self._result.response_proof}\\userid\\{self._result.data.user_id}\\profileid\\{self._result.data.profile_id}" + self.sending_buffer = f"\\lc\\2\\sesskey\\{SESSION_KEY}\\proof\\{self._result.response_proof}\\userid\\{ + self._result.data.user_id}\\profileid\\{self._result.data.profile_id}" if self._result.data.unique_nick is not None: self.sending_buffer += "\\uniquenick\\" + self._result.data.unique_nick @@ -49,9 +45,17 @@ def build(self): self.sending_buffer += f"\\id\\{self._request.operation_id}\\final\\" -class NewUserResponse(NUR): - _result: NewUserResult +class NewUserResponse(ResponseBase): _request: NewUserRequest + _result: NewUserResult + + def __init__(self, request: NewUserRequest, result: NewUserResult) -> None: + super().__init__(request, result) + assert isinstance(request, NewUserRequest) + assert isinstance(result, NewUserResult) def build(self): - self.sending_buffer = f"\\nur\\userid\\{self._result.user.userid}\\profileid\\{self._result.subprofiles.profileid}\\id\\{self._request.operation_id}\\final\\" + # fmt: on + self.sending_buffer = f"\\nur\\userid\\{self._result.user_id}\\profileid\\{self._result.profile_id}\\id\\{self._request.operation_id}\\final\\" # fmt: off + + diff --git a/src/servers/presence_connection_manager/src/contracts/results/general.py b/src/servers/presence_connection_manager/src/contracts/results/general.py index b300724f2..60e65d498 100644 --- a/src/servers/presence_connection_manager/src/contracts/results/general.py +++ b/src/servers/presence_connection_manager/src/contracts/results/general.py @@ -17,3 +17,8 @@ class LoginDataModel: class LoginResult(ResultBase): response_proof: str data: LoginDataModel + + +class NewUserResult(ResultBase): + user_id: int + profile_id: int diff --git a/src/servers/presence_connection_manager/src/handlers/buddy.py b/src/servers/presence_connection_manager/src/handlers/buddy.py index 04d38fd57..6d1e4fb22 100644 --- a/src/servers/presence_connection_manager/src/handlers/buddy.py +++ b/src/servers/presence_connection_manager/src/handlers/buddy.py @@ -1,8 +1,8 @@ from multiprocessing.pool import Pool from servers.presence_connection_manager.src.abstractions.contracts import RequestBase -from servers.presence_connection_manager.src.abstractions.handler import ( +from servers.presence_connection_manager.src.abstractions.handlers import ( CmdHandlerBase, - LoginHandlerBase, + LoginedHandlerBase, ) from servers.presence_connection_manager.src.applications.client import Client from servers.presence_connection_manager.src.contracts.requests.buddy import ( @@ -39,7 +39,7 @@ def _response_construct(self) -> None: self._response = BlockListResponse(self._result) -class BuddyListHandler(LoginHandlerBase): +class BuddyListHandler(LoginedHandlerBase): _result: BuddyListResult _result_cls = BuddyListResult @@ -80,7 +80,7 @@ def __init__(self, client: Client, request: RequestBase) -> None: super().__init__(client, request) -class DelBuddyHandler(LoginHandlerBase): +class DelBuddyHandler(LoginedHandlerBase): _request: DelBuddyRequest def __init__(self, client: Client, request: DelBuddyRequest) -> None: @@ -88,7 +88,7 @@ def __init__(self, client: Client, request: DelBuddyRequest) -> None: super().__init__(client, request) -class InviteToHandler(LoginHandlerBase): +class InviteToHandler(LoginedHandlerBase): def __init__(self, client: Client, request: RequestBase) -> None: raise NotImplementedError() super().__init__(client, request) @@ -109,7 +109,7 @@ def _response_send(self) -> None: raise NotImplementedError() -class StatusInfoHandler(LoginHandlerBase): +class StatusInfoHandler(LoginedHandlerBase): _request: StatusInfoRequest _result: StatusInfoResult = StatusInfoResult() diff --git a/src/servers/presence_connection_manager/src/handlers/general.py b/src/servers/presence_connection_manager/src/handlers/general.py index da5cecca7..885291c84 100644 --- a/src/servers/presence_connection_manager/src/handlers/general.py +++ b/src/servers/presence_connection_manager/src/handlers/general.py @@ -1,28 +1,30 @@ -from servers.chat.src.contracts.requests.general import LoginRequest -from servers.chat.src.contracts.results.general import LoginResult -from servers.presence_connection_manager.src.abstractions.handler import ( - CmdHandlerBase, - LoginHandlerBase, -) + +from typing import TYPE_CHECKING +import servers.presence_connection_manager.src.abstractions.handlers from servers.presence_connection_manager.src.aggregates.sdk_revision import SdkRevision -from servers.presence_connection_manager.src.applications.client import Client from servers.presence_connection_manager.src.contracts.requests.general import ( KeepAliveRequest, + LoginRequest, LogoutRequest, ) from servers.presence_connection_manager.src.contracts.responses.general import ( KeepAliveResponse, LoginResponse, ) +from servers.presence_connection_manager.src.contracts.results.general import LoginResult from servers.presence_connection_manager.src.handlers.buddy import ( BlockListHandler, BuddyListHandler, ) from servers.presence_search_player.src.contracts.responses import NewUserResponse +import servers.presence_search_player.src.handlers.handlers + +if TYPE_CHECKING: + from servers.presence_connection_manager.src.applications.client import Client -class KeepAliveHandler(CmdHandlerBase): - def __init__(self, client: Client, request: KeepAliveRequest) -> None: +class KeepAliveHandler(servers.presence_connection_manager.src.abstractions.handlers.CmdHandlerBase): + def __init__(self, client: "Client", request: KeepAliveRequest) -> None: assert isinstance(request, KeepAliveRequest) super().__init__(client, request) @@ -30,7 +32,7 @@ def _response_construct(self) -> None: self._response = KeepAliveResponse(self._request) -class LoginHandelr(CmdHandlerBase): +class LoginHandler(servers.presence_connection_manager.src.abstractions.handlers.CmdHandlerBase): _request: LoginRequest _result: LoginResult @@ -43,7 +45,7 @@ def _response_construct(self) -> None: self._response = LoginResponse(self._request, self._result) -class LogoutHandler(LoginHandlerBase): +class LogoutHandler(servers.presence_connection_manager.src.abstractions.handlers.LoginedHandlerBase): _request: LogoutRequest def __init__(self, client: Client, request: LogoutRequest) -> None: @@ -51,16 +53,13 @@ def __init__(self, client: Client, request: LogoutRequest) -> None: super().__init__(client, request) -import servers.presence_search_player.src.handlers.handlers - - -class NewUserHandler(servers.presence_search_player.handlers.handlers.NewUserHandler): +class NewUserHandler(servers.presence_search_player.src.handlers.handlers.NewUserHandler): def _response_construct(self): self._response = NewUserResponse(self._request, self._response) -class SdkRevisionHandler(CmdHandlerBase): +class SdkRevisionHandler(servers.presence_connection_manager.src.abstractions.handlers.CmdHandlerBase): _request: LoginRequest def __init__(self, client: Client, request: LoginRequest) -> None: @@ -68,7 +67,8 @@ def __init__(self, client: Client, request: LoginRequest) -> None: super().__init__(client, request) def _response_construct(self) -> None: - self._client.info.sdk_revision = SdkRevision(self._request.sdk_revision_type) + self._client.info.sdk_revision = SdkRevision( + self._request.sdk_revision_type) if self._client.info.sdk_revision.is_support_gpi_new_status_notification: BuddyListHandler(self._client).handle() BlockListHandler(self._client).handle() diff --git a/src/servers/presence_connection_manager/src/handlers/profile.py b/src/servers/presence_connection_manager/src/handlers/profile.py index afdc5445f..451970920 100644 --- a/src/servers/presence_connection_manager/src/handlers/profile.py +++ b/src/servers/presence_connection_manager/src/handlers/profile.py @@ -1,6 +1,6 @@ from servers.chat.src.contracts.requests.general import RegisterNickRequest from servers.presence_connection_manager.src.abstractions.contracts import RequestBase -from servers.presence_connection_manager.src.abstractions.handler import CmdHandlerBase +from servers.presence_connection_manager.src.abstractions.handlers import CmdHandlerBase from servers.presence_connection_manager.src.applications.client import Client from servers.presence_connection_manager.src.contracts.requests.profile import ( AddBlockRequest, diff --git a/src/servers/presence_connection_manager/src/handlers/switcher.py b/src/servers/presence_connection_manager/src/handlers/switcher.py index 3f1d6e75f..ca9f9a0f5 100644 --- a/src/servers/presence_connection_manager/src/handlers/switcher.py +++ b/src/servers/presence_connection_manager/src/handlers/switcher.py @@ -1,7 +1,7 @@ from library.src.abstractions.switcher import SwitcherBase from servers.chat.src.contracts.requests.general import LoginRequest, RegisterNickRequest -from servers.presence_connection_manager.src.abstractions.handler import LoginHandlerBase -from servers.presence_connection_manager.src.applications.client import Client +from servers.presence_connection_manager.src.handlers.general import LoginHandler + from servers.presence_connection_manager.src.contracts.requests.buddy import StatusInfoRequest, StatusRequest from servers.presence_connection_manager.src.contracts.requests.general import KeepAliveRequest, LogoutRequest from servers.presence_connection_manager.src.contracts.requests.profile import AddBlockRequest, GetProfileRequest, NewProfileRequest, RegisterCDKeyRequest, UpdateProfileRequest @@ -12,6 +12,9 @@ from servers.presence_search_player.src.exceptions.general import ( GPParseException, ) +from servers.presence_search_player.src.abstractions.handler import CmdHandlerBase +from typing import Optional +from servers.presence_connection_manager.src.applications.client import Client class Switcher(SwitcherBase): @@ -19,22 +22,23 @@ class Switcher(SwitcherBase): def __init__(self, client: Client, raw_request: str) -> None: assert isinstance(client, Client) + assert isinstance(raw_request, str) super().__init__(client, raw_request) def _process_raw_request(self) -> None: if self._raw_request[0] != "\\": raise GPParseException("Request format is invalid") - raw_requests = self._raw_request.split("\\final\\") + raw_requests = [r for r in self._raw_request.split("\\final\\") if r] for raw_request in raw_requests: name = raw_request.strip("\\").split("\\")[0] self._requests.append((name, raw_request)) - def _create_cmd_handlers(self, name: str, raw_request: str) -> None: + def _create_cmd_handlers(self, name: str, raw_request: str) -> Optional[CmdHandlerBase]: match name: case "ka": return KeepAliveHandler(self._client, KeepAliveRequest(raw_request)) case "login": - return LoginHandlerBase(self._client, LoginRequest(raw_request)) + return LoginHandler(self._client, LoginRequest(raw_request)) case "logout": return LogoutHandler(self._client, LogoutRequest(raw_request)) case "newuser": @@ -56,4 +60,4 @@ def _create_cmd_handlers(self, name: str, raw_request: str) -> None: case "statusinfo": return StatusInfoHandler(self._client, StatusInfoRequest(raw_request)) case _: - return None \ No newline at end of file + return None diff --git a/src/servers/presence_connection_manager/tests/game_tests.py b/src/servers/presence_connection_manager/tests/game_tests.py index 0eec76f5e..880f2e300 100644 --- a/src/servers/presence_connection_manager/tests/game_tests.py +++ b/src/servers/presence_connection_manager/tests/game_tests.py @@ -1,6 +1,5 @@ import unittest -from library.src.unispy_server_config import CONFIG from servers.presence_connection_manager.src.contracts.requests.buddy import StatusRequest from servers.presence_connection_manager.tests.mock_objects import create_client @@ -14,7 +13,7 @@ def test_civilization_4(self) -> None: client = create_client() for x in raw_requests: - client.on_received(x) + client.on_received(x.encode("ascii")) pass def test_conflict_global_storm(self) -> None: @@ -26,7 +25,7 @@ def test_conflict_global_storm(self) -> None: client = create_client() for x in raw_requests: - client.on_received(x) + client.on_received(x.encode("ascii")) pass def test_sbwfrontps2(self) -> None: @@ -35,3 +34,7 @@ def test_sbwfrontps2(self) -> None: request.parse() self.assertTrue(request.status.location_string == "") self.assertTrue(request.status.status_string == "EN LIGNE") + + +if __name__ == "__main__": + unittest.main() diff --git a/src/servers/presence_connection_manager/tests/general_request_tests.py b/src/servers/presence_connection_manager/tests/general_request_tests.py index 82cbc1049..37050689d 100644 --- a/src/servers/presence_connection_manager/tests/general_request_tests.py +++ b/src/servers/presence_connection_manager/tests/general_request_tests.py @@ -8,9 +8,9 @@ class GeneralRequestTest(unittest.TestCase): - LOGIN_AUTH_TOKEN = "\\login\\\\challenge\\xxxx\\authtoken\\xxxx\\userid\\0\\profileid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\3\\quiet\\0\\id\\1\\final\\" - LOGIN_UNIQUE_NICK = "\\login\\\\challenge\\xxxx\\uniquenick\\spyguy\\userid\\0\\profileid\\0\\namespaceid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\3\\quiet\\0\\id\\1\\final\\" - LOGIN_USER = "\\login\\\\challenge\\xxxx\\user\\spyguy@spyguy@unispy.org\\userid\\0\\profileid\\0\\partnerid\\0\\namespaceid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\3\\quiet\\0\\id\\1\\final\\" + LOGIN_AUTH_TOKEN = "\\login\\\\challenge\\xxxx\\authtoken\\xxxx\\userid\\0\\profileid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" + LOGIN_UNIQUE_NICK = "\\login\\\\challenge\\xxxx\\uniquenick\\spyguy\\userid\\0\\profileid\\0\\namespaceid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" + LOGIN_USER = "\\login\\\\challenge\\xxxx\\user\\spyguy@spyguy@unispy.org\\userid\\0\\profileid\\0\\partnerid\\0\\namespaceid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" def test_login_auth_token(self) -> None: request = LoginRequest(GeneralRequestTest.LOGIN_AUTH_TOKEN) @@ -22,12 +22,12 @@ def test_login_auth_token(self) -> None: self.assertEqual(0, request.profile_id) self.assertEqual(0, request.partner_id) self.assertEqual("xxxxx", request.response) - self.assertEqual("1", request.firewall) + self.assertEqual(True, request.firewall) self.assertEqual(request.game_port, 0) self.assertEqual(request.product_id, 0) self.assertEqual("gmtest", request.game_name) self.assertEqual( - SdkRevisionType.GPINEW_STATUS_NOTIFICATION, request.sdk_revision_type) + SdkRevisionType.GPINEW_REVOKE_NOTIFICATION, request.sdk_revision_type) self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) def test_login_unique_nick(self) -> None: @@ -41,12 +41,12 @@ def test_login_unique_nick(self) -> None: self.assertEqual(0, request.profile_id) self.assertEqual(0, request.partner_id) self.assertEqual("xxxxx", request.response) - self.assertEqual("1", request.firewall) + self.assertEqual(True, request.firewall) self.assertEqual(0, request.game_port) self.assertEqual(0, request.product_id) self.assertEqual("gmtest", request.game_name) self.assertEqual( - SdkRevisionType.GPINEW_STATUS_NOTIFICATION, request.sdk_revision_type) + SdkRevisionType.GPINEW_REVOKE_NOTIFICATION, request.sdk_revision_type) self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) def test_login_user(self) -> None: @@ -61,12 +61,12 @@ def test_login_user(self) -> None: self.assertEqual(0, request.profile_id) self.assertEqual(0, request.partner_id) self.assertEqual("xxxxx", request.response) - self.assertEqual("1", request.firewall) + self.assertEqual(True, request.firewall) self.assertEqual(0, request.game_port) self.assertEqual(0, request.product_id) self.assertEqual("gmtest", request.game_name) self.assertEqual( - SdkRevisionType.GPINEW_STATUS_NOTIFICATION, request.sdk_revision_type) + SdkRevisionType.GPINEW_REVOKE_NOTIFICATION, request.sdk_revision_type) self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) diff --git a/src/servers/presence_connection_manager/tests/profile_request_tests.py b/src/servers/presence_connection_manager/tests/profile_request_tests.py index b76f34e79..cacf69950 100644 --- a/src/servers/presence_connection_manager/tests/profile_request_tests.py +++ b/src/servers/presence_connection_manager/tests/profile_request_tests.py @@ -11,12 +11,12 @@ class ProfileRequestTest(unittest.TestCase): - ADD_BLOCK = "\\addblock\\profileid\\0\\final\\" - GET_PROFILE = "\\getprofile\\sesskey\\xxxx\\profileid\\0\\final\\" - NEW_PROFILE = "\\newprofile\\sesskey\\xxxx\\nick\\spyguy\\id\\1\\final\\" - NEW_PROFILE_REPLACE = "\\newprofile\\sesskey\\xxxx\\nick\\spyguy2\\replace\\1\\oldnick\\spyguy\\id\\1\\final\\" - REGISTER_CD_KEY = "\\registercdkey\\sesskey\\xxxx\\cdkeyenc\\xxxx\\id\\1\\final\\" - REGISTER_NICK = "\\registernick\\sesskey\\xxxx\\uniquenick\\spyguy\\partnerid\\0\\id\\1\\final\\" + ADD_BLOCK = "\\addblock\\\\profileid\\0\\final\\" + GET_PROFILE = "\\getprofile\\\\sesskey\\xxxx\\profileid\\0\\final\\" + NEW_PROFILE = "\\newprofile\\\\sesskey\\xxxx\\nick\\spyguy\\id\\1\\final\\" + NEW_PROFILE_REPLACE = "\\newprofile\\\\sesskey\\xxxx\\nick\\spyguy2\\replace\\1\\oldnick\\spyguy\\id\\1\\final\\" + REGISTER_CD_KEY = "\\registercdkey\\\\sesskey\\xxxx\\cdkeyenc\\xxxx\\id\\1\\final\\" + REGISTER_NICK = "\\registernick\\\\sesskey\\xxxx\\uniquenick\\spyguy\\partnerid\\0\\id\\1\\final\\" def test_add_block(self) -> None: request = AddBlockRequest(ProfileRequestTest.ADD_BLOCK) @@ -56,6 +56,10 @@ def test_register_nick(self) -> None: self.assertEqual(0, request.partner_id) def test_update_profile(self) -> None: - crysisWarsRaw = "\\updatepro\\sesskey\\1111\\countrycode\\DE\\birthday\\168232912\\partnerid\\0\\final\\" + crysisWarsRaw = "\\updatepro\\\\sesskey\\1111\\countrycode\\DE\\birthday\\168232912\\partnerid\\0\\final\\" request = UpdateProfileRequest(crysisWarsRaw) - request.Parse() + request.parse() + + +if __name__ == "__main__": + unittest.main() diff --git a/src/servers/presence_search_player/__init__.py b/src/servers/presence_search_player/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/presence_search_player/src/abstractions/contracts.py b/src/servers/presence_search_player/src/abstractions/contracts.py index 8eeb701ef..12ea55a84 100644 --- a/src/servers/presence_search_player/src/abstractions/contracts.py +++ b/src/servers/presence_search_player/src/abstractions/contracts.py @@ -1,16 +1,10 @@ -import abc from typing import Dict -import library +import library.src.abstractions.contracts from library.src.extentions.gamespy_utils import convert_to_key_value from servers.presence_search_player.src.exceptions.general import ( GPParseException, ) -from servers.presence_search_player.src.abstractions.contracts import ( - RequestBase, - ResultBase, -) - class RequestBase(library.src.abstractions.contracts.RequestBase): request_dict: Dict[str, str] @@ -25,7 +19,7 @@ def __init__(self, raw_request: str) -> None: def parse(self) -> None: self.request_dict = convert_to_key_value(self.raw_request) - self.command_name = self.request_dict.keys()[0] + self.command_name = list(self.request_dict.keys())[0] if "id" in self.request_dict.keys(): try: self.operation_id = int(self.request_dict["id"]) diff --git a/src/servers/presence_search_player/src/applications/client.py b/src/servers/presence_search_player/src/applications/client.py index 08d5bde1e..af854d8b3 100644 --- a/src/servers/presence_search_player/src/applications/client.py +++ b/src/servers/presence_search_player/src/applications/client.py @@ -1,8 +1,10 @@ from library.src.abstractions.client import ClientBase -from servers.presence_search_player.src.handlers.switcher import CmdSwitcher + +from library.src.abstractions.switcher import SwitcherBase class Client(ClientBase): - def _create_switcher(self, buffer) -> CmdSwitcher: + def _create_switcher(self, buffer) -> SwitcherBase: + from servers.presence_search_player.src.handlers.switcher import CmdSwitcher return CmdSwitcher(self, buffer) diff --git a/src/servers/presence_search_player/src/contracts/requests.py b/src/servers/presence_search_player/src/contracts/requests.py index 188be1915..c1ab7cc5e 100644 --- a/src/servers/presence_search_player/src/contracts/requests.py +++ b/src/servers/presence_search_player/src/contracts/requests.py @@ -23,7 +23,7 @@ def parse(self): if "nick" not in self.request_dict or "email" not in self.request_dict: raise GPParseException("check request is incompelete.") - if is_email_format_correct(self.request_dict["email"]): + if not is_email_format_correct(self.request_dict["email"]): raise GPParseException(" email format is incorrect.") self.nick = self.request_dict["nick"] @@ -173,6 +173,10 @@ class SearchRequest(RequestBase): email: str nick: str uniquenick: str + session_key: str + firstname: str + lastname: str + icquin: str def parse(self) -> None: super().parse() @@ -183,9 +187,21 @@ def parse(self) -> None: and "email" not in self.request_dict and "namespaceid" not in self.request_dict and "gamename" not in self.request_dict + and "sesskey" not in self.request_dict ): raise GPParseException("Search request is incomplete.") + self.session_key = self.request_dict["sesskey"] + + if "firstname" in self.request_dict: + self.firstname = self.request_dict["firstname"] + + if "lastname" in self.request_dict: + self.lastname = self.request_dict["lastname"] + + if "icquin" in self.request_dict: + self.icquin = self.request_dict["icquin"] + if "gamename" in self.request_dict: self.game_name = self.request_dict["gamename"] diff --git a/src/servers/presence_search_player/src/contracts/responses.py b/src/servers/presence_search_player/src/contracts/responses.py index 576371213..dbc13b577 100644 --- a/src/servers/presence_search_player/src/contracts/responses.py +++ b/src/servers/presence_search_player/src/contracts/responses.py @@ -1,4 +1,4 @@ -from servers.presence_connection_manager.src.abstractions.contracts import ResponseBase +from servers.presence_search_player.src.abstractions.contracts import ResponseBase from servers.presence_search_player.src.contracts.requests import ( CheckRequest, NewUserRequest, @@ -31,7 +31,8 @@ def build(self): if self._result.profile_id is None: self.sending_buffer = f"\\cur\\1\\final\\" else: - self.sending_buffer = f"\\cur\\0\\pid\\{self._result.profile_id}\\final\\" + self.sending_buffer = f"\\cur\\0\\pid\\{ + self._result.profile_id}\\final\\" class NewUserResponse(ResponseBase): @@ -45,7 +46,7 @@ def __init__(self, request: NewUserRequest, result: NewUserResult) -> None: def build(self): self.sending_buffer = ( - f"\\nur\\\\pid\\{self._result.subprofiles.profileid}\\final\\" + f"\\nur\\\\pid\\{self._result.profile_id}\\final\\" ) @@ -174,5 +175,6 @@ def build(self): self.sending_buffer = "\\us\\1\\nick\\Choose another name\\usdone\\final\\" else: self.sending_buffer = ( - f"\\us\\1\\nick\\{self._request.preferred_nick}\\usdone\\final\\" + f"\\us\\1\\nick\\{ + self._request.preferred_nick}\\usdone\\final\\" ) diff --git a/src/servers/presence_search_player/src/contracts/results.py b/src/servers/presence_search_player/src/contracts/results.py index ff7f01715..1ec60261c 100644 --- a/src/servers/presence_search_player/src/contracts/results.py +++ b/src/servers/presence_search_player/src/contracts/results.py @@ -1,5 +1,6 @@ -from library.src.database.pg_orm import Profiles, SubProfiles, Users -from servers.presence_connection_manager.src.abstractions.contracts import ResultBase +from pydantic import BaseModel + +from servers.presence_search_player.src.abstractions.contracts import ResultBase class CheckResult(ResultBase): @@ -7,18 +8,17 @@ class CheckResult(ResultBase): class NewUserResult(ResultBase): - user: Users - profile: Profiles - subprofiles: SubProfiles + user_id: int + profile_id: int -class NickResultModel: +class NickResultData(BaseModel): nick: str uniquenick: str class NicksResult(ResultBase): - data: list[NickResultModel] = [] + data: list[NickResultData] """ [ (nick1, uniquenick1), (nick2, uniquenick2), @@ -29,13 +29,13 @@ class NicksResult(ResultBase): is_require_uniquenicks: bool = False -class OthersListModel: +class OthersListData(BaseModel): profile_id: int unique_nick: str class OthersListResult(ResultBase): - data: list[OthersListModel] = [] + data: list[OthersListData] = [] """ [ (prifileid1,uniquenick1), @@ -46,7 +46,7 @@ class OthersListResult(ResultBase): """ -class OthersResultModel: +class OthersResultData(BaseModel): profile_id: int nick: str uniquenick: str @@ -57,10 +57,10 @@ class OthersResultModel: class OthersResult(ResultBase): - data: list[OthersResultModel] = [] + data: list[OthersResultData] = [] -class SearchResultDataModel: +class SearchResultData(BaseModel): profile_id: int nick: str uniquenick: str @@ -71,10 +71,10 @@ class SearchResultDataModel: class SearchResult(ResultBase): - result: list[SearchResultDataModel] = [] + result: list[SearchResultData] -class SearchUniqueResultModel: +class SearchUniqueResultData(BaseModel): profile_id: int nick: str uniquenick: str @@ -85,7 +85,7 @@ class SearchUniqueResultModel: class SearchUniqueResult(ResultBase): - data: list[SearchUniqueResultModel] = [] + data: list[SearchUniqueResultData] class UniqueSearchResult(ResultBase): diff --git a/src/servers/presence_search_player/src/handlers/handlers.py b/src/servers/presence_search_player/src/handlers/handlers.py index 42328d901..69bbf90c7 100644 --- a/src/servers/presence_search_player/src/handlers/handlers.py +++ b/src/servers/presence_search_player/src/handlers/handlers.py @@ -1,16 +1,15 @@ -from servers.presence_connection_manager.src.abstractions.handler import CmdHandlerBase -from servers.presence_connection_manager.src.applications.client import Client -from servers.presence_connection_manager.src.contracts.responses.general import NewUserResponse + from servers.presence_search_player.src.contracts.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest -from servers.presence_search_player.src.contracts.responses import CheckResponse, NicksResponse, OthersListResponse, OthersResponse, SearchResponse, SearchUniqueResponse, UniqueSearchResponse, ValidResponse -from servers.presence_search_player.src.contracts.results import CheckResult, NewUserResult, NicksResult +from servers.presence_search_player.src.contracts.responses import CheckResponse, NewUserResponse, NicksResponse, OthersListResponse, OthersResponse, SearchResponse, SearchUniqueResponse, UniqueSearchResponse, ValidResponse +from servers.presence_search_player.src.contracts.results import CheckResult, NewUserResult, NicksResult, OthersListResult, OthersResult, SearchResult, SearchUniqueResult, UniqueSearchResult, ValidResult + +from servers.presence_search_player.src.abstractions.handler import CmdHandlerBase +from servers.presence_search_player.src.applications.client import Client class CheckHandler(CmdHandlerBase): - _request: CheckRequest - _result: CheckResult - _response: CheckResponse + _result_cls: type[CheckResult] = CheckResult def __init__(self, client: Client, request: CheckRequest) -> None: assert isinstance(request, CheckRequest) @@ -21,9 +20,7 @@ def _response_construct(self): class NewUserHandler(CmdHandlerBase): - _request: NewUserRequest - _result: NewUserResult - _response: NewUserResponse + _result_cls: type[NewUserResult] = NewUserResult def __init__(self, client: Client, request: NewUserRequest) -> None: assert isinstance(request, NewUserRequest) @@ -31,10 +28,10 @@ def __init__(self, client: Client, request: NewUserRequest) -> None: def _response_construct(self): self._response = NewUserResponse(self._request, self._result) + + class NicksHandler(CmdHandlerBase): - _request: NicksRequest - _result: NicksResult - _response: NicksResponse + _result_cls: type[NicksResult] = NicksResult def __init__(self, client: Client, request: NicksRequest) -> None: assert isinstance(request, NicksRequest) @@ -43,15 +40,22 @@ def __init__(self, client: Client, request: NicksRequest) -> None: def _response_construct(self): self._response = NicksResponse(self._request, self._result) + class OthersHandler(CmdHandlerBase): + _request: OthersRequest + _result_cls: type[OthersResult] = OthersResult + def __init__(self, client: Client, request: OthersRequest) -> None: assert isinstance(request, OthersRequest) super().__init__(client, request) def _response_construct(self): self._response = OthersResponse(self._result) - + + class OthersListHandler(CmdHandlerBase): + _result_cls: type[OthersListResult] = OthersListResult + def __init__(self, client: Client, request: OthersListRequest) -> None: assert isinstance(request, OthersListRequest) super().__init__(client, request) @@ -59,18 +63,21 @@ def __init__(self, client: Client, request: OthersListRequest) -> None: def _response_construct(self): self._response = OthersListResponse(self._result) + class SearchHandler(CmdHandlerBase): """ last one we search with email this may get few profile so we can not return GPErrorCode SearchWithEmail(client,dict ); - \search\\sesskey\0\profileid\0\namespaceid\1\partnerid\0\nick\mycrysis\uniquenick\xiaojiuwo\email\koujiangheng@live.cn\gamename\gmtest\final\ - \bsrdone\more\\final\ + \\search\\\\sesskey\\0\\profileid\\0\\namespaceid\\1\\partnerid\\0\\nick\\mycrysis\\uniquenick\\xiaojiuwo\\email\\koujiangheng@live.cn\\gamename\\gmtest\\final\\ + \\bsrdone\\more\\\\final\\ string sendingbuffer = - "\\bsr\\1\\nick\\mycrysis\\uniquenick\\1\\namespaceid\\0\\firstname\\jiangheng\\lastname\\kou\\email\\koujiangheng@live.cn\\bsrdone\\0\\final\\"; + "\\\\bsr\\\\1\\\\nick\\\\mycrysis\\\\uniquenick\\\\1\\\\namespaceid\\\\0\\\\firstname\\\\jiangheng\\\\lastname\\\\kou\\\\email\\\\koujiangheng@live.cn\\\\bsrdone\\\\0\\\\final\\\\"; client.Stream.SendAsync(sendingbuffer); - \more\\final\ - \search\sesskey\0\profileid\0\namespaceid\0\nick\gbr359_jordips\gamename\gbrome\final\ + \\more\\\\final\\ + \\search\\sesskey\\0\\profileid\\0\\namespaceid\\0\\nick\\gbr359_jordips\\gamename\\gbrome\\final\\ """ + _result_cls: type[SearchResult] = SearchResult + def __init__(self, client: Client, request: SearchRequest) -> None: assert isinstance(request, SearchRequest) super().__init__(client, request) @@ -80,20 +87,28 @@ def _response_construct(self): class SearchUniqueHandler(CmdHandlerBase): + _result_cls: type[SearchUniqueResult] = SearchUniqueResult + def __init__(self, client: Client, request: SearchUniqueRequest) -> None: assert isinstance(request, SearchUniqueRequest) super().__init__(client, request) def _response_construct(self): self._response = SearchUniqueResponse(self._result) + + class UniqueSearchHandler(CmdHandlerBase): + _result_cls: type[UniqueSearchResult] = UniqueSearchResult def __init__(self, client: Client, request: UniqueSearchRequest) -> None: assert isinstance(request, UniqueSearchRequest) super().__init__(client, request) def _response_construct(self): self._response = UniqueSearchResponse(self._request, self._result) + + class ValidHandler(CmdHandlerBase): + _result_cls: type[ValidResult] = ValidResult def __init__(self, client: Client, request: ValidRequest) -> None: assert isinstance(request, ValidRequest) super().__init__(client, request) diff --git a/src/servers/presence_search_player/src/handlers/switcher.py b/src/servers/presence_search_player/src/handlers/switcher.py index 3f1b8426d..ff2ad54b9 100644 --- a/src/servers/presence_search_player/src/handlers/switcher.py +++ b/src/servers/presence_search_player/src/handlers/switcher.py @@ -1,34 +1,32 @@ -from servers.presence_connection_manager.src.handlers.general import NewUserHandler -from servers.presence_search_player.src.abstractions.handler import CmdHandlerBase -from servers.presence_search_player.src.applications.client import Client +from typing import Optional +from library.src.abstractions.switcher import SwitcherBase from servers.presence_search_player.src.contracts.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest -from library.src.abstractions.switcher import SwitcherBase -from servers.presence_search_player.src.handlers.handlers import CheckHandler, NicksHandler, OthersHandler, OthersListHandler, SearchHandler, SearchUniqueHandler, UniqueSearchHandler, ValidHandler +from servers.presence_search_player.src.handlers.handlers import CheckHandler, NewUserHandler, NicksHandler, OthersHandler, OthersListHandler, SearchHandler, SearchUniqueHandler, UniqueSearchHandler, ValidHandler + +from servers.presence_search_player.src.abstractions.handler import CmdHandlerBase + +from servers.presence_search_player.src.applications.client import Client class CmdSwitcher(SwitcherBase): + _raw_request: str + def __init__(self, client: Client, raw_request: str): + assert isinstance(client, Client) super().__init__(client, raw_request) - @property - def _raw_request(self) -> str: - return super()._raw_request - - @property - def _client(self) -> Client: - return super()._client - - def process_raw_request(self): + def _process_raw_request(self): if self._raw_request[0] != "\\": self._client.log_info("Invalid request received!") return - raw_requests = self._raw_request.split("\\final\\") + raw_requests = [ + r+"\\final\\" for r in self._raw_request.split("\\final\\") if r] for raw_request in raw_requests: - name, request = raw_request.strip("\\").split("\\", 1) - self._requests.append((name, request)) + name = raw_request.strip("\\").split("\\", 1)[0] + self._requests.append((name, raw_request)) - def create_cmd_handlers(self, name: str, raw_request: str) -> CmdHandlerBase: + def _create_cmd_handlers(self, name: str, raw_request: str) -> Optional[CmdHandlerBase]: match name: case "check": return CheckHandler(self._client, CheckRequest(raw_request)) diff --git a/src/servers/presence_search_player/tests/game_tests.py b/src/servers/presence_search_player/tests/game_tests.py new file mode 100644 index 000000000..76ed80c79 --- /dev/null +++ b/src/servers/presence_search_player/tests/game_tests.py @@ -0,0 +1,31 @@ +import unittest + +from library.src.extentions.password_encoder import process_password +from library.tests.mock_objects.general import create_mock_url +from servers.presence_search_player.src.contracts.requests import CheckRequest +from servers.presence_search_player.src.handlers.handlers import CheckHandler +from servers.presence_search_player.src.handlers.switcher import CmdSwitcher +import responses + +from servers.presence_search_player.tests.mock_objects import create_client + + +class GameTest(unittest.TestCase): + @responses.activate + def test_check(self): + raw = "\\check\\\\nick\\spyguy\\email\\spyguy@gamespy.com\\pass\\0000\\final\\" + client = create_client() + create_mock_url(client, CheckHandler, {"profile_id": 0}) + + switcher = CmdSwitcher(client, raw) + switcher.handle() + request: CheckRequest = switcher._handlers[0]._request + response = switcher._handlers[0]._response + self.assertEqual("spyguy", request.nick) + self.assertEqual("spyguy@gamespy.com", request.email) + self.assertEqual("4a7d1ed414474e4033ac29ccb8653d9b", request.password) + self.assertEqual("\\cur\\0\\pid\\0\\final\\", response.sending_buffer) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/servers/presence_search_player/tests/handler_tests.py b/src/servers/presence_search_player/tests/handler_tests.py new file mode 100644 index 000000000..068a887f6 --- /dev/null +++ b/src/servers/presence_search_player/tests/handler_tests.py @@ -0,0 +1,73 @@ +import unittest + +import responses + +from library.tests.mock_objects.general import create_mock_url +from servers.presence_search_player.src.contracts.requests import SearchRequest +from servers.presence_search_player.src.handlers.handlers import SearchHandler +from servers.presence_search_player.tests.mock_objects import create_client + + +SEARCH_1 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\namespaceid\\0\\uniquenick\\spyguy\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" +SEARCH_2 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\nick\\spyguy\\email\\spyguy@unispy.org\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" +SEARCH_3 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\nick\\spyguy\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" +SEARCH_4 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\email\\spyguy@unispy.org\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" + +SEARCH_UNIQUENICK = "\\searchunique\\\\sesskey\\xxxx\\profileid\\0\\uniquenick\\spyguy\\namespaces\\1,2,3,4,5\\gamename\\gmtest\\final\\" + +VALID = "\\valid\\\\email\\spyguy@unispy.org\\partnerid\\1\\gamename\\gmtest\\final\\" + +NICKS = "\\nicks\\\\email\\spyguy@unispy.org\\passenc\\xxxxx\\namespaceid\\0\\partnerid\\0\\gamename\\gmtest\\final\\" + + +PMATCH = "\\pmatch\\\\sesskey\\123456\\profileid\\0\\productid\\0\\final\\" + +NEWUSER = "\\newuser\\\\nick\\xiaojiuwo\\email\\xiaojiuwo@gamespy.com\\passenc\\xxxx\\productID\\0\\namespaceid\\0\\uniquenick\\xiaojiuwo\\cdkey\\xxx-xxx-xxx-xxx\\partnerid\\0\\gamename\\gmtest\\final\\" + +OTHER_BUDDY = "\\others\\\\sesskey\\123456\\profileid\\0\\namespaceid\\0\\gamename\\gmtest\\final\\" + +OTHERS_BUDDY_LIST = "\\otherlist\\\\sesskey\\123456\\profileid\\0\\numopids\\2\\opids\\1|2\\namespaceid\\0\\gamename\\gmtest\\final\\" + +SUGGEST_UNIQUE = "\\uniquesearch\\\\preferrednick\\xiaojiuwo\\namespaceid\\0\\gamename\\gmtest\\final\\" + + +client = create_client() + + +class HandlerTests(unittest.TestCase): + + @responses.activate + def test_profile(self): + request = SearchRequest(SEARCH_1) + request.parse() + self.assertEqual("xxxx", request.session_key) + self.assertEqual("spyguy", request.uniquenick) + self.assertEqual(0, request.profile_id) + self.assertEqual(0, request.namespace_id) + self.assertEqual(0, request.skip_num) + self.assertEqual("spy", request.firstname) + self.assertEqual("guy", request.lastname) + self.assertEqual("123", request.icquin) + + request = SearchRequest(SEARCH_2) + request.parse() + self.assertEqual("spyguy", request.nick) + self.assertEqual("spyguy@unispy.org", request.email) + + request = SearchRequest(SEARCH_3) + request.parse() + self.assertEqual("spyguy", request.nick) + + request = SearchRequest(SEARCH_4) + request.parse() + self.assertEqual("spyguy@unispy.org", request.email) + + create_mock_url(client, SearchHandler, {"result": [{"profile_id": 0, "nick": "spyguy", "uniquenick": "spyguy", + "email": "spyguy@unispy.org", "firstname": "spy", "lastname": "guy", "namespace_id": 0}]}) + handler = SearchHandler(client, request) + handler.handle() + self.assertEqual("\\bsr\\0\\nick\\spyguy\\uniquenick\\spyguy\\namespaceid\\0\\firstname\\spy\\lastname\\guy\\email\\spyguy@unispy.org\\bsrdone\\\\more\\0\\final\\", handler._response.sending_buffer) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/servers/presence_search_player/tests/mock_objects.py b/src/servers/presence_search_player/tests/mock_objects.py new file mode 100644 index 000000000..0d8b8279f --- /dev/null +++ b/src/servers/presence_search_player/tests/mock_objects.py @@ -0,0 +1,19 @@ +from library.src.unispy_server_config import CONFIG +from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock +from servers.presence_search_player.src.applications.client import Client + + +class ClientMock(Client): + + pass + + +def create_client(): + handler = RequestHandlerMock() + logger = LogMock() + conn = ConnectionMock( + handler=handler, + config=CONFIG.servers["NatNegotiation"], t_client=ClientMock, + logger=logger) + + return conn._client From 6b1a358c3eddd3fd345b89c23262347ed5a174d8 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 28 Aug 2024 14:34:23 +0800 Subject: [PATCH 096/231] refactor(gs): update contracts --- src/library/src/abstractions/client.py | 2 +- src/library/src/abstractions/handler.py | 2 +- src/library/src/abstractions/switcher.py | 2 +- .../src/exceptions/{error.py => general.py} | 0 .../src/extentions/password_encoder.py | 2 +- src/library/src/unispy_server_config.py | 2 +- src/servers/chat/src/exceptions/general.py | 2 +- .../base_classes => }/__init__.py | 0 .../game_status/src/abstractions/contracts.py | 42 +++ .../game_status/src/abstractions/handlers.py | 0 .../game_status/src/aggregations/gscrypt.py | 21 ++ .../game_status/src/contracts/requests.py | 239 ++++++++++++++++++ .../game_status/src/contracts/responses.py | 0 .../game_status/src/contracts/results.py | 17 ++ src/servers/game_status/src/enums/general.py | 15 ++ .../game_status/src/exceptions/general.py | 5 + .../src/exceptions/general.py | 2 +- .../query_report/src/exceptions/exceptions.py | 2 +- .../src/v2/applications/switcher.py | 2 +- .../server_browser/src/exceptions/general.py | 2 +- .../webservices/src/abstractions/contracts.py | 2 +- .../webservices/src/exceptions/general.py | 2 +- 22 files changed, 351 insertions(+), 12 deletions(-) rename src/library/src/exceptions/{error.py => general.py} (100%) rename src/servers/game_status/{src/abstractions/base_classes => }/__init__.py (100%) create mode 100644 src/servers/game_status/src/abstractions/contracts.py create mode 100644 src/servers/game_status/src/abstractions/handlers.py create mode 100644 src/servers/game_status/src/aggregations/gscrypt.py create mode 100644 src/servers/game_status/src/contracts/requests.py create mode 100644 src/servers/game_status/src/contracts/responses.py create mode 100644 src/servers/game_status/src/contracts/results.py create mode 100644 src/servers/game_status/src/enums/general.py create mode 100644 src/servers/game_status/src/exceptions/general.py diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index a761337b9..81f92ef28 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -1,5 +1,5 @@ from library.src.encryption.encoding import Encoding -from library.src.exceptions.error import UniSpyException +from library.src.exceptions.general import UniSpyException from library.src.log.log_manager import LogWriter from library.src.log.log_manager import LogWriter from library.src.unispy_server_config import ServerConfig diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index 47815fd29..615bf4e9a 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -1,5 +1,5 @@ from library.src.abstractions.client import ClientBase -from library.src.exceptions.error import UniSpyException +from library.src.exceptions.general import UniSpyException from typing import Type import requests diff --git a/src/library/src/abstractions/switcher.py b/src/library/src/abstractions/switcher.py index 857504165..618a978e2 100644 --- a/src/library/src/abstractions/switcher.py +++ b/src/library/src/abstractions/switcher.py @@ -27,7 +27,7 @@ def __init__(self, client: ClientBase, raw_request: Optional[bytes | str]) -> No self._raw_request = raw_request def handle(self): - from library.src.exceptions.error import UniSpyException + from library.src.exceptions.general import UniSpyException try: self._process_raw_request() diff --git a/src/library/src/exceptions/error.py b/src/library/src/exceptions/general.py similarity index 100% rename from src/library/src/exceptions/error.py rename to src/library/src/exceptions/general.py diff --git a/src/library/src/extentions/password_encoder.py b/src/library/src/extentions/password_encoder.py index 3f1625ff6..b720675f9 100644 --- a/src/library/src/extentions/password_encoder.py +++ b/src/library/src/extentions/password_encoder.py @@ -1,7 +1,7 @@ import hashlib import base64 -from library.src.exceptions.error import UniSpyException +from library.src.exceptions.general import UniSpyException def process_password(request: dict): diff --git a/src/library/src/unispy_server_config.py b/src/library/src/unispy_server_config.py index ba5f7a847..a564d219a 100644 --- a/src/library/src/unispy_server_config.py +++ b/src/library/src/unispy_server_config.py @@ -4,7 +4,7 @@ from pydantic import BaseModel, Field, constr -from library.src.exceptions.error import UniSpyException +from library.src.exceptions.general import UniSpyException class PostgreSql(BaseModel): diff --git a/src/servers/chat/src/exceptions/general.py b/src/servers/chat/src/exceptions/general.py index 0dd82b0e8..cca15cada 100644 --- a/src/servers/chat/src/exceptions/general.py +++ b/src/servers/chat/src/exceptions/general.py @@ -1,7 +1,7 @@ from servers.chat.src.abstractions.contract import SERVER_DOMAIN from servers.chat.src.enums.irc_error_code import IRCErrorCode -from library.src.exceptions.error import UniSpyException as ER +from library.src.exceptions.general import UniSpyException as ER class ChatException(ER): diff --git a/src/servers/game_status/src/abstractions/base_classes/__init__.py b/src/servers/game_status/__init__.py similarity index 100% rename from src/servers/game_status/src/abstractions/base_classes/__init__.py rename to src/servers/game_status/__init__.py diff --git a/src/servers/game_status/src/abstractions/contracts.py b/src/servers/game_status/src/abstractions/contracts.py new file mode 100644 index 000000000..7700a54de --- /dev/null +++ b/src/servers/game_status/src/abstractions/contracts.py @@ -0,0 +1,42 @@ +from typing import Optional +import library.src.abstractions.contracts +from library.src.extentions.gamespy_utils import convert_to_key_value +from servers.game_status.src.exceptions.general import GSException + + +class RequestBase(library.src.abstractions.contracts.RequestBase): + command_name: str + raw_request: str + local_id: Optional[int] + request_dict: dict[str, str] + + @staticmethod + def convert_game_data_to_key_values(game_data: str): + assert isinstance(game_data, str) + game_data = game_data.replace("\u0001", "\\") + convert_to_key_value(game_data) + + def parse(self) -> None: + self.request_dict = convert_to_key_value(self.raw_request) + + if "lid" in self.request_dict: + try: + self.local_id = int(self.request_dict["lid"]) + except: + raise GSException("local id is not valid.") + + if "id" in self.request_dict: + try: + self.local_id = int(self.request_dict["id"]) + except: + raise GSException("local id is not valid.") + + +class ResultBase(library.src.abstractions.contracts.ResultBase): + pass + + +class ResponseBase(library.src.abstractions.contracts.ResponseBase): + _request: RequestBase + _result: ResultBase + sending_buffer: str diff --git a/src/servers/game_status/src/abstractions/handlers.py b/src/servers/game_status/src/abstractions/handlers.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/game_status/src/aggregations/gscrypt.py b/src/servers/game_status/src/aggregations/gscrypt.py new file mode 100644 index 000000000..9b512aa44 --- /dev/null +++ b/src/servers/game_status/src/aggregations/gscrypt.py @@ -0,0 +1,21 @@ + + +from library.src.abstractions.enctypt_base import EncryptBase +from library.src.encryption.xor_encryption import XorEncoding, XorType +from servers.game_status.src.exceptions.general import GSException + + +class GSCrypt(EncryptBase): + def decrypt(self, data: bytes) -> bytes: + if b"final" not in data: + raise GSException("Ciphertext must contains delimeter \\final\\") + cipher = data[:-7] + plain = XorEncoding.encode(cipher, XorType.TYPE_1) + return plain + b"\\final\\" + + def encrypt(self, data: bytes) -> bytes: + if b"final" not in data: + raise GSException("Ciphertext must contains delimeter \\final\\") + cipher = data[:-7] + plain = XorEncoding.encode(cipher, XorType.TYPE_1) + return plain + b"\\final\\" \ No newline at end of file diff --git a/src/servers/game_status/src/contracts/requests.py b/src/servers/game_status/src/contracts/requests.py new file mode 100644 index 000000000..0a912c307 --- /dev/null +++ b/src/servers/game_status/src/contracts/requests.py @@ -0,0 +1,239 @@ +from library.src.extentions.gamespy_utils import convert_to_key_value +from servers.game_status.src.abstractions.contracts import RequestBase +from servers.game_status.src.enums.general import AuthMethod, PersistStorageType +from servers.game_status.src.exceptions.general import GSException + + +class AuthGameRequest(RequestBase): + game_name: str + + def parse(self) -> None: + super().parse() + if "lid" not in self.request_dict and "id" not in self.request_dict: + raise GSException("localid is missing") + + if "gamename" not in self.request_dict: + raise GSException("gamename is missing") + self.game_name = self.request_dict["gamename"] + + if "response" not in self.request_dict: + raise GSException("response is missing") + self.response = self.request_dict["response"] + + if "port" in self.request_dict: + try: + self.port = int(self.request_dict["port"]) + except: + raise GSException("port format is incorrect") + + +class AuthPlayerRequest(RequestBase): + auth_type: AuthMethod + profile_id: int + + auth_token: str + response: str + cdkey_hash: str + nick: str + + def parse(self) -> None: + super().parse() + if "lid" not in self.request_dict and "id" not in self.request_dict: + raise GSException("localid is missing from auth game request") + if "pid" in self.request_dict and "resp" in self.request_dict: + try: + self.profile_id = int(self.request_dict["profile_id"]) + except: + raise GSException("profile id format is incorrect") + self.auth_type = AuthMethod.PROFILE_ID_AUTH + elif "authtoken" in self.request_dict and "response" in self.request_dict: + self.auth_token = self.request_dict["authtoken"] + self.response = self.request_dict["response"] + self.auth_type = AuthMethod.PARTNER_ID_AUTH + elif "keyhash" in self.request_dict and "nick" in self.request_dict: + self.cdkey_hash = self.request_dict["keyhash"] + self.nick = self.request_dict["nick"] + + else: + raise GSException("unknown authp request type") + + +class GetPlayerDataRequest(RequestBase): + profile_id: str + storage_type: PersistStorageType + data_index: int + is_get_all_data: bool = False + keys: list[str] = [] + + def parse(self) -> None: + super().parse() + + if "lid" not in self.request_dict and "id" not in self.request_dict: + raise GSException("localid is missing from auth game request") + + if "pid" in self.request_dict: + try: + self.profile_id = int(self.request_dict["profile_id"]) + except: + raise GSException("pid format is incorrect") + + if "ptype" in self.request_dict: + try: + self.storage_type = PersistStorageType( + int(self.request_dict["ptype"])) + except: + raise GSException("ptype format is incorrect") + + if "dindex" in self.request_dict: + try: + self.data_index = int(self.request_dict["dindex"]) + except: + raise GSException("dindex format is incorrect") + + if "keys" not in self.request_dict: + raise GSException("keys is missing") + + keys = self.request_dict["keys"] + if not keys: + self.is_get_all_data = True + else: + key_list = keys.split("\x1") + for key in key_list: + self.keys.append(key) + self.is_get_all_data = False + + +class GetProfileIdRequest(RequestBase): + nick: str + keyhash: str + + def parse(self) -> None: + super().parse() + if "lid" not in self.request_dict and "id" not in self.request_dict: + raise GSException("localid is missing from auth game request") + + if "nick" not in self.request_dict or "keyhash" not in self.request_dict: + raise GSException("nick or keyhash is missing") + + if "nick" in self.request_dict: + self.nick = self.request_dict["nick"] + if "keyhash" in self.request_dict: + self.keyhash = self.request_dict["keyhash"] + + +class NewGameRequest(RequestBase): + is_client_local_storage_available: bool + challenge: str = None + connection_id: int = None + session_key: str = None + + def parse(self) -> None: + super().parse() + if "sesskey" not in self.request_dict: + raise GSException("sesskey is missing") + + self.session_key = self.request_dict["sesskey"] + + if "connid" not in self.request_dict: + raise GSException("connid is missing") + try: + self.connection_id = int(self.request_dict["connid"]) + except: + raise GSException("connid format is incorrect") + + if "challenge" in self.request_dict: + self.challenge = self.request_dict["challenge"] + + +class SetPlayerDataRequest(RequestBase): + profile_id: int + storage_type: PersistStorageType + data_index: int + length: int + report: str + data: str + + def parse(self) -> None: + super().parse() + if "pid" not in self.request_dict: + raise GSException("pid is missing") + + if "ptype" not in self.request_dict: + raise GSException("ptype is missing") + + if "dindex" not in self.request_dict: + raise GSException("dindex is missing") + + if "length" not in self.request_dict: + raise GSException("length is missing") + + try: + self.profile_id = int(self.request_dict["pid"]) + except: + raise GSException("pid format is incorrect") + + try: + self.storage_type = PersistStorageType( + int(self.request_dict["ptype"])) + except: + raise GSException("ptype format is incorrect") + + try: + self.data_index = int(self.request_dict["dindex"]) + except: + raise GSException("dindex format is incorrect") + + try: + self.length = int(self.request_dict["length"]) + except: + raise GSException("length format is incorrect") + + if "report" in self.request_dict: + self.report = self.request_dict["report"] + + if "data" in self.request_dict: + self.data = self.request_dict["data"] + + +class UpdateGameRequest(RequestBase): + connection_id: int = None + is_done: bool + is_client_local_storage_available: bool + game_data: str + game_data_dict: dict[str, str] + session_key: str + + def parse(self) -> None: + super().parse() + + if "gamedata" not in self.request_dict: + raise GSException("gamedata is missing") + self.game_data = self.request_dict["gamedata"] + + self.game_data_dict = convert_to_key_value(self.game_data) + + if "dl" in self.request_dict: + self.is_client_local_storage_available = True + + if 'done' not in self.request_dict: + raise GSException("done is missing") + + done = self.request_dict["done"] + if done == "1": + self.is_done = True + + elif done == "0": + self.is_done = False + else: + raise GSException("done format is incorrect") + + if "sesskey" not in self.request_dict: + raise GSException("sesskey is missing") + + self.session_key = self.request_dict["sesskey"] + + if "connid" in self.request_dict: + try: + self.connection_id = int(self.request_dict["connid"]) + except: + raise GSException("connid format is incorrect") diff --git a/src/servers/game_status/src/contracts/responses.py b/src/servers/game_status/src/contracts/responses.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/game_status/src/contracts/results.py b/src/servers/game_status/src/contracts/results.py new file mode 100644 index 000000000..e0186f496 --- /dev/null +++ b/src/servers/game_status/src/contracts/results.py @@ -0,0 +1,17 @@ +from servers.game_status.src.abstractions.contracts import ResultBase + + +class AuthGameResult(ResultBase): + session_key: str + + +class AuthPlayerResult(ResultBase): + profile_id: int + + +class GetPlayerDataResult(ResultBase): + keyvalues: dict[str, str] + + +class GetProfileIdResult(ResultBase): + profile_id: int diff --git a/src/servers/game_status/src/enums/general.py b/src/servers/game_status/src/enums/general.py new file mode 100644 index 000000000..1b9357aa3 --- /dev/null +++ b/src/servers/game_status/src/enums/general.py @@ -0,0 +1,15 @@ +from enum import IntEnum + + +class AuthMethod(IntEnum): + UNKNOWN = 0 + PROFILE_ID_AUTH = 0 + PARTNER_ID_AUTH = 1 + CDKEY_AUTH = 2 + + +class PersistStorageType(IntEnum): + PRIVATE_READ_ONLY = 0 + PRIVATE_READ_WRITE = 1 + PUBLIC_READ_ONLY = 2 + PUBLIC_READ_WRITE = 3 diff --git a/src/servers/game_status/src/exceptions/general.py b/src/servers/game_status/src/exceptions/general.py new file mode 100644 index 000000000..c59852da0 --- /dev/null +++ b/src/servers/game_status/src/exceptions/general.py @@ -0,0 +1,5 @@ +from library.src.exceptions.general import UniSpyException + + +class GSException(UniSpyException): + pass diff --git a/src/servers/presence_search_player/src/exceptions/general.py b/src/servers/presence_search_player/src/exceptions/general.py index 07b08b646..590595180 100644 --- a/src/servers/presence_search_player/src/exceptions/general.py +++ b/src/servers/presence_search_player/src/exceptions/general.py @@ -1,4 +1,4 @@ -from library.src.exceptions.error import UniSpyException +from library.src.exceptions.general import UniSpyException from servers.presence_search_player.src.enums.error_codes import GPErrorCode from library.src.abstractions.contracts import ResponseBase diff --git a/src/servers/query_report/src/exceptions/exceptions.py b/src/servers/query_report/src/exceptions/exceptions.py index 91c3b0b75..3d29f873e 100644 --- a/src/servers/query_report/src/exceptions/exceptions.py +++ b/src/servers/query_report/src/exceptions/exceptions.py @@ -1,4 +1,4 @@ -from library.src.exceptions.error import UniSpyException +from library.src.exceptions.general import UniSpyException class QRException(UniSpyException): diff --git a/src/servers/query_report/src/v2/applications/switcher.py b/src/servers/query_report/src/v2/applications/switcher.py index 2a77ec83c..a34e3ed7f 100644 --- a/src/servers/query_report/src/v2/applications/switcher.py +++ b/src/servers/query_report/src/v2/applications/switcher.py @@ -1,5 +1,5 @@ from library.src.abstractions.switcher import SwitcherBase -from library.src.exceptions.error import UniSpyException +from library.src.exceptions.general import UniSpyException from servers.presence_connection_manager.src.contracts.requests.general import ( KeepAliveRequest, ) diff --git a/src/servers/server_browser/src/exceptions/general.py b/src/servers/server_browser/src/exceptions/general.py index 1dcab6e3e..cf3be195c 100644 --- a/src/servers/server_browser/src/exceptions/general.py +++ b/src/servers/server_browser/src/exceptions/general.py @@ -1,4 +1,4 @@ -from library.src.exceptions.error import UniSpyException +from library.src.exceptions.general import UniSpyException class ServerBrowserException(UniSpyException): diff --git a/src/servers/webservices/src/abstractions/contracts.py b/src/servers/webservices/src/abstractions/contracts.py index 3490c6f51..574e961d9 100644 --- a/src/servers/webservices/src/abstractions/contracts.py +++ b/src/servers/webservices/src/abstractions/contracts.py @@ -1,7 +1,7 @@ import library.src.abstractions.contracts as lib import xml.etree.ElementTree as ET -from library.src.exceptions.error import UniSpyException +from library.src.exceptions.general import UniSpyException from servers.webservices.src.aggregations.soap_envelop import SoapEnvelop diff --git a/src/servers/webservices/src/exceptions/general.py b/src/servers/webservices/src/exceptions/general.py index 5922c267b..b9b15802b 100644 --- a/src/servers/webservices/src/exceptions/general.py +++ b/src/servers/webservices/src/exceptions/general.py @@ -1,4 +1,4 @@ -from library.src.exceptions.error import UniSpyException +from library.src.exceptions.general import UniSpyException class WebExceptions(UniSpyException): From 2bf4e7253199e439a638d6d253af58d7bd2f2f36 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 28 Aug 2024 17:25:29 +0800 Subject: [PATCH 097/231] refactor(gs): update handlers --- src/library/src/abstractions/client.py | 10 +- src/library/src/unispy_server_config.py | 1 - .../game_status/src/abstractions/handlers.py | 16 ++++ .../game_status/src/applications/client.py | 44 +++++++++ .../game_status/src/contracts/responses.py | 52 +++++++++++ src/servers/game_status/src/enums/general.py | 7 ++ .../game_status/src/handlers/handlers.py | 92 +++++++++++++++++++ .../game_status/src/handlers/switcher.py | 5 + 8 files changed, 221 insertions(+), 6 deletions(-) create mode 100644 src/servers/game_status/src/applications/client.py create mode 100644 src/servers/game_status/src/handlers/handlers.py create mode 100644 src/servers/game_status/src/handlers/switcher.py diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index 81f92ef28..9ec7e0b62 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -16,6 +16,10 @@ from library.src.network.http_handler import HttpRequest +class ClientInfoBase: + pass + + class ClientBase: server_config: ServerConfig connection: "ConnectionBase" @@ -53,7 +57,7 @@ def on_received(self, buffer: "Optional[bytes | HttpRequest]") -> None: switcher: "SwitcherBase" = self.create_switcher(buffer) switcher.handle() - def decrypt_message(self, buffer) -> bytes: + def decrypt_message(self, buffer: bytes) -> bytes: if self.crypto is not None: return self.crypto.decrypt(buffer) else: @@ -118,7 +122,3 @@ def refresh_last_active_time(self) -> None: def dispose(self) -> None: pass - - -class ClientInfoBase: - pass diff --git a/src/library/src/unispy_server_config.py b/src/library/src/unispy_server_config.py index a564d219a..45b2a6f2d 100644 --- a/src/library/src/unispy_server_config.py +++ b/src/library/src/unispy_server_config.py @@ -22,7 +22,6 @@ class PostgreSql(BaseModel): @property def url(self) -> str: - # fmt ignore return f"postgresql://{self.username}:{self.password}@{self.server}:{self.port}/{self.database}?sslmode={self.ssl_mode}" diff --git a/src/servers/game_status/src/abstractions/handlers.py b/src/servers/game_status/src/abstractions/handlers.py index e69de29bb..103dc45e3 100644 --- a/src/servers/game_status/src/abstractions/handlers.py +++ b/src/servers/game_status/src/abstractions/handlers.py @@ -0,0 +1,16 @@ +from library.src.abstractions.contracts import ResponseBase +import library.src.abstractions.handler +from servers.game_status.src.abstractions.contracts import RequestBase, ResultBase +from servers.game_status.src.applications.client import Client + + +class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase): + _client: Client + _request: RequestBase + _result: ResultBase + _response: ResponseBase + + def __init__(self, client: Client, request: RequestBase) -> None: + super().__init__(client, request) + assert isinstance(client, Client) + assert issubclass(type(request), RequestBase) diff --git a/src/servers/game_status/src/applications/client.py b/src/servers/game_status/src/applications/client.py new file mode 100644 index 000000000..902c1726a --- /dev/null +++ b/src/servers/game_status/src/applications/client.py @@ -0,0 +1,44 @@ +from library.src.abstractions.client import ClientBase, ClientInfoBase +from library.src.abstractions.switcher import SwitcherBase +from servers.game_status.src.aggregations.gscrypt import GSCrypt + + +CHALLENGE_RESPONSE = "\\challenge\\00000000000000000000\\final\\" + + +class ClientInfo(ClientInfoBase): + session_key: str = None + game_name: str = None + is_user_authenticated: bool = False + is_player_authenticated: bool = False + is_game_authenticated: bool = False + profile_id: int = None + game_session_key: str = None + + +class Client(ClientBase): + info: ClientInfo + + def on_connected(self) -> None: + self.crypto = GSCrypt() + self.log_network_sending(CHALLENGE_RESPONSE) + self.connection.send(CHALLENGE_RESPONSE.encode("ascii")) + + def decrypt_message(self, buffer: bytes) -> bytes: + if self.crypto is None: + return buffer + + temp = buffer.decode("ascii").split("\\final\\")[0] + + if len(temp) > 1: + message = "" + for t in temp: + complete_buffer = (t+"\\final\\").encode() + message += self.crypto.decrypt(complete_buffer).decode() + return message.encode() + + return self.crypto.decrypt(buffer) + + def create_switcher(self, buffer) -> SwitcherBase: + from servers.game_status.src.handlers.switcher import Switcher + return Switcher(self, buffer) diff --git a/src/servers/game_status/src/contracts/responses.py b/src/servers/game_status/src/contracts/responses.py index e69de29bb..11639f5fc 100644 --- a/src/servers/game_status/src/contracts/responses.py +++ b/src/servers/game_status/src/contracts/responses.py @@ -0,0 +1,52 @@ + +from library.src.abstractions.contracts import ResponseBase +from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, SetPlayerDataRequest +from servers.game_status.src.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult + + +class AuthGameResponse(ResponseBase): + _request: AuthGameRequest + _result: AuthGameResult + + def build(self) -> None: + # fmt: off + self.sending_buffer = f"\\sesskey\\{self._result.session_key}\\lid\\{self._request.local_id}\\final\\" + # fmt: on + + +class AuthPlayerResponse(ResponseBase): + _request: AuthPlayerRequest + _result: AuthPlayerResult + + def build(self) -> None: + # fmt: off + self.sending_buffer = f"\\pauthr\\{self._result.profile_id}\\lid\\{self._request.local_id}\\final\\" + # fmt: on + + +class GetPlayerDataResponse(ResponseBase): + _request: GetPlayerDataRequest + _result: GetPlayerDataResult + + def build(self) -> None: + # fmt: off + self.sending_buffer = f"\\getpdr\\1\\pid\\{self._request.profile_id}\\lid\\{self._request.local_id}\\mod\\1234\\length\\5\\data\\mydata\\final\\" + # fmt: on + + +class GetProfileIdResponse(ResponseBase): + _request: GetProfileIdRequest + _result: GetProfileIdResult + + def build(self) -> None: + # fmt: off + self.sending_buffer = f"\\getpidr\\{self._result.profile_id}\\lid\\{self._request.local_id}\\final\\" + # fmt: on + + +class SetPlayerDataResponse(ResponseBase): + _request: SetPlayerDataRequest + _result: GetPlayerDataResult + + def build(self) -> None: + raise NotImplementedError() diff --git a/src/servers/game_status/src/enums/general.py b/src/servers/game_status/src/enums/general.py index 1b9357aa3..e78936ced 100644 --- a/src/servers/game_status/src/enums/general.py +++ b/src/servers/game_status/src/enums/general.py @@ -13,3 +13,10 @@ class PersistStorageType(IntEnum): PRIVATE_READ_WRITE = 1 PUBLIC_READ_ONLY = 2 PUBLIC_READ_WRITE = 3 + + +class GSErrorCode(IntEnum): + GENERAL = 0 + PARSE = 1 + DATABASE = 2 + NOERROR = 3 diff --git a/src/servers/game_status/src/handlers/handlers.py b/src/servers/game_status/src/handlers/handlers.py new file mode 100644 index 000000000..e18dfaa0e --- /dev/null +++ b/src/servers/game_status/src/handlers/handlers.py @@ -0,0 +1,92 @@ +from servers.game_status.src.abstractions.contracts import RequestBase +from servers.game_status.src.abstractions.handlers import CmdHandlerBase +from servers.game_status.src.applications.client import Client +from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest +from servers.game_status.src.contracts.responses import AuthGameResponse, AuthPlayerResponse, GetPlayerDataResponse, GetProfileIdResponse +from servers.game_status.src.contracts.results import AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult +from servers.game_status.src.exceptions.general import GSException + + +class AuthGameHandler(CmdHandlerBase): + _is_feaching = False + + def __init__(self, client: Client, request: AuthGameRequest) -> None: + super().__init__(client, request) + assert isinstance(request, AuthGameRequest) + + def _data_operate(self) -> None: + self._client.info.session_key = "2020" + self._client.info.game_name = self._request.game_name + self._client.info.is_game_authenticated = True + + def _response_construct(self) -> None: + self._response = AuthGameResponse(self._request, self._result) + + +class AuthPlayerHandler(CmdHandlerBase): + _result_cls: type[AuthPlayerResult] = AuthPlayerResult + + def __init__(self, client: Client, request: AuthPlayerRequest) -> None: + super().__init__(client, request) + assert isinstance(request, AuthPlayerRequest) + + def _response_construct(self) -> None: + self._response = AuthPlayerResponse(self._request, self._result) + + +class GetPlayerDataHandler(CmdHandlerBase): + _result_cls: type[GetPlayerDataResult] = GetPlayerDataResult + + def __init__(self, client: Client, request: GetPlayerDataRequest) -> None: + super().__init__(client, request) + assert isinstance(request, GetPlayerDataRequest) + + def _response_construct(self) -> None: + self._response = GetPlayerDataResponse(self._request, self._result) + + +class GetProfileIdHandler(CmdHandlerBase): + _result_cls: type[GetProfileIdResult] = GetProfileIdResult + + def __init__(self, client: Client, request: GetProfileIdRequest) -> None: + super().__init__(client, request) + assert isinstance(request, GetProfileIdRequest) + + def _response_construct(self) -> None: + self._response = GetProfileIdResponse(self._request, self._result) + + +class NewGameHandler(CmdHandlerBase): + _is_feaching = False + + def __init__(self, client: Client, request: NewGameRequest) -> None: + super().__init__(client, request) + assert isinstance(request, NewGameRequest) + + +class SetPlayerDataHandler(CmdHandlerBase): + _is_feaching = False + + def __init__(self, client: Client, request: SetPlayerDataRequest) -> None: + super().__init__(client, request) + assert isinstance(request, SetPlayerDataRequest) + raise NotImplementedError() + + +class UpdateGameHandler(CmdHandlerBase): + """ + old request "\updgame\\sesskey\%d\done\%d\gamedata\%s" + + new request "\updgame\\sesskey\%d\connid\%d\done\%d\gamedata\%s" + """ + _request: UpdateGameRequest + + def __init__(self, client: Client, request: UpdateGameRequest) -> None: + super().__init__(client, request) + assert isinstance(request, UpdateGameRequest) + + def _data_operate(self) -> None: + if not self._request.game_data: + return + super()._data_operate() + raise NotImplementedError() diff --git a/src/servers/game_status/src/handlers/switcher.py b/src/servers/game_status/src/handlers/switcher.py new file mode 100644 index 000000000..27575aa3f --- /dev/null +++ b/src/servers/game_status/src/handlers/switcher.py @@ -0,0 +1,5 @@ +from library.src.abstractions.switcher import SwitcherBase + + +class Switcher(SwitcherBase): + raise NotImplementedError() From a8ad499005d28a23b22cbe70c5c81143225a2c27 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 2 Sep 2024 09:16:01 +0800 Subject: [PATCH 098/231] refactor: added new tests --- src/library/src/abstractions/client.py | 6 +- src/library/src/abstractions/switcher.py | 3 +- src/library/src/exceptions/general.py | 12 +- src/requirements.txt | 2 +- src/servers/chat/src/aggregates/channel.py | 11 +- .../game_status/src/applications/client.py | 4 +- .../game_status/src/contracts/requests.py | 4 +- .../game_status/src/handlers/handlers.py | 4 +- .../game_status/src/handlers/switcher.py | 39 +++++- src/servers/game_status/tests/game_tests.py | 36 +++++ .../game_status/tests/handler_tests.py | 129 ++++++++++++++++++ src/servers/game_status/tests/mock_objects.py | 19 +++ .../src/applications/client.py | 4 +- .../src/handlers/switcher.py | 2 +- .../src/applications/client.py | 4 + .../src/handlers/switcher.py | 3 +- .../tests/mock_objects.py | 2 +- .../webservices/src/applications/client.py | 12 +- .../webservices/src/handlers/switcher.py | 114 ++++++++++++++++ 19 files changed, 385 insertions(+), 25 deletions(-) create mode 100644 src/servers/game_status/tests/game_tests.py create mode 100644 src/servers/game_status/tests/handler_tests.py create mode 100644 src/servers/game_status/tests/mock_objects.py create mode 100644 src/servers/webservices/src/handlers/switcher.py diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index 9ec7e0b62..db5928073 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -46,13 +46,17 @@ def on_connected(self) -> None: def on_disconnected(self) -> None: pass - def create_switcher(self, buffer) -> "SwitcherBase": + def create_switcher(self, buffer: "Optional[bytes | HttpRequest]") -> "SwitcherBase": pass def on_received(self, buffer: "Optional[bytes | HttpRequest]") -> None: if isinstance(buffer, bytes): if self.crypto is not None: buffer = self.crypto.decrypt(buffer) + elif isinstance(buffer, HttpRequest): + pass + else: + raise UniSpyException("buffer type is invalid") switcher: "SwitcherBase" = self.create_switcher(buffer) switcher.handle() diff --git a/src/library/src/abstractions/switcher.py b/src/library/src/abstractions/switcher.py index 618a978e2..2f9321810 100644 --- a/src/library/src/abstractions/switcher.py +++ b/src/library/src/abstractions/switcher.py @@ -21,7 +21,6 @@ class SwitcherBase: """ def __init__(self, client: ClientBase, raw_request: Optional[bytes | str]) -> None: - assert isinstance(client, ClientBase) self._client = client self._raw_request = raw_request @@ -45,7 +44,7 @@ def handle(self): for handler in self._handlers: handler.handle() except Exception as e: - UniSpyException.handle_exception(e) + UniSpyException.handle_exception(e, self._client) @abc.abstractmethod def _process_raw_request(self) -> None: diff --git a/src/library/src/exceptions/general.py b/src/library/src/exceptions/general.py index 012d2c2c7..d9b06190f 100644 --- a/src/library/src/exceptions/general.py +++ b/src/library/src/exceptions/general.py @@ -1,4 +1,9 @@ -# from library.src.abstractions.client_base import ClientBase + +from typing import TYPE_CHECKING + + +if TYPE_CHECKING: + from library.src.abstractions.client import ClientBase class UniSpyException(Exception): @@ -10,13 +15,14 @@ def __init__(self, message: str) -> None: @staticmethod # def handle_exception(e: Exception, client: ClientBase = None): - def handle_exception(e: Exception, client=None): + def handle_exception(e: Exception, client: "ClientBase" = None): if issubclass(type(e), UniSpyException): + ex: UniSpyException = e if client is None: # LogWriter.LogError(ex.Message); pass else: - # client.LogError(ex.Message); + client.log_error(ex.message) pass else: if client is None: diff --git a/src/requirements.txt b/src/requirements.txt index 7dec0b862..cd60d9d4f 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -2,7 +2,6 @@ pyfiglet psycopg2-binary sqlalchemy jsonpickle == 3.0.3 - aioredis == 2.0.1 email_validator == 2.1.1 attrs @@ -13,3 +12,4 @@ xmltodict responses pydantic responses +redis \ No newline at end of file diff --git a/src/servers/chat/src/aggregates/channel.py b/src/servers/chat/src/aggregates/channel.py index 943baca2e..fbff27467 100644 --- a/src/servers/chat/src/aggregates/channel.py +++ b/src/servers/chat/src/aggregates/channel.py @@ -16,7 +16,8 @@ class Channel: game_name: str name: str max_num_user: int = 200 - create_time: datetime.datetime = datetime.datetime.now(datetime.timezone.utc) + create_time: datetime.datetime = datetime.datetime.now( + datetime.timezone.utc) kv_manager: KeyValueManager = KeyValueManager() room_type: PeerRoomType password: str @@ -59,7 +60,8 @@ def get_group_id(self): def get_peer_room_name(self): if self.game_name in PEER_GROUP_LIST: grouplist = PEER_GROUP_LIST[self.game_name] - room = next((g for g in grouplist if g["group_id"] == self.group_id), None) + room = next( + (g for g in grouplist if g["group_id"] == self.group_id), None) if room is None: raise Exception(f"Invalid peer room: {self.name}") self.room_name = room["room_name"] @@ -175,8 +177,9 @@ def remove_local_channel(name: str) -> None: def add_message_broker(name: str) -> object: if name not in message_brokers: - message_brokers[name] = broker - return message_brokers[name] + brocker = MessageBrocker(name) + message_brokers[name] = brocker + return brocker def remove_message_brocker(name: str) -> None: diff --git a/src/servers/game_status/src/applications/client.py b/src/servers/game_status/src/applications/client.py index 902c1726a..0f364467e 100644 --- a/src/servers/game_status/src/applications/client.py +++ b/src/servers/game_status/src/applications/client.py @@ -39,6 +39,6 @@ def decrypt_message(self, buffer: bytes) -> bytes: return self.crypto.decrypt(buffer) - def create_switcher(self, buffer) -> SwitcherBase: + def create_switcher(self, buffer: bytes) -> SwitcherBase: from servers.game_status.src.handlers.switcher import Switcher - return Switcher(self, buffer) + return Switcher(self, buffer.decode()) diff --git a/src/servers/game_status/src/contracts/requests.py b/src/servers/game_status/src/contracts/requests.py index 0a912c307..8ef234454 100644 --- a/src/servers/game_status/src/contracts/requests.py +++ b/src/servers/game_status/src/contracts/requests.py @@ -73,7 +73,7 @@ def parse(self) -> None: if "pid" in self.request_dict: try: - self.profile_id = int(self.request_dict["profile_id"]) + self.profile_id = int(self.request_dict["pid"]) except: raise GSException("pid format is incorrect") @@ -97,7 +97,7 @@ def parse(self) -> None: if not keys: self.is_get_all_data = True else: - key_list = keys.split("\x1") + key_list = keys.split("\x01") for key in key_list: self.keys.append(key) self.is_get_all_data = False diff --git a/src/servers/game_status/src/handlers/handlers.py b/src/servers/game_status/src/handlers/handlers.py index e18dfaa0e..754831da5 100644 --- a/src/servers/game_status/src/handlers/handlers.py +++ b/src/servers/game_status/src/handlers/handlers.py @@ -75,9 +75,9 @@ def __init__(self, client: Client, request: SetPlayerDataRequest) -> None: class UpdateGameHandler(CmdHandlerBase): """ - old request "\updgame\\sesskey\%d\done\%d\gamedata\%s" + old request "\\updgame\\\\sesskey\\%d\\done\\%d\\gamedata\\%s" - new request "\updgame\\sesskey\%d\connid\%d\done\%d\gamedata\%s" + new request "\\updgame\\\\sesskey\\%d\\connid\\%d\\done\\%d\\gamedata\\%s" """ _request: UpdateGameRequest diff --git a/src/servers/game_status/src/handlers/switcher.py b/src/servers/game_status/src/handlers/switcher.py index 27575aa3f..3f61673c7 100644 --- a/src/servers/game_status/src/handlers/switcher.py +++ b/src/servers/game_status/src/handlers/switcher.py @@ -1,5 +1,42 @@ from library.src.abstractions.switcher import SwitcherBase +from servers.game_status.src.abstractions.handlers import CmdHandlerBase +from servers.game_status.src.applications.client import Client +from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest +from servers.game_status.src.handlers.handlers import AuthGameHandler, AuthPlayerHandler, GetPlayerDataHandler, NewGameHandler, SetPlayerDataHandler, UpdateGameHandler class Switcher(SwitcherBase): - raise NotImplementedError() + _raw_request: str + + def __init__(self, client: Client, raw_request: str) -> None: + super().__init__(client, raw_request) + assert isinstance(client, Client) + assert isinstance(raw_request, str) + + def _process_raw_request(self) -> None: + if self._raw_request[0] != "\\": + self._client.log_info("Invalid request received!") + return + raw_requests = [ + r+"\\final\\" for r in self._raw_request.split("\\final\\") if r] + for raw_request in raw_requests: + name = raw_request.strip("\\").split("\\", 1)[0] + self._requests.append((name, raw_request)) + + def _create_cmd_handlers(self, name: object, raw_request: str) -> CmdHandlerBase | None: + + match name: + case "auth": + return AuthGameHandler(self._client, AuthGameRequest(raw_request)) + case "authp": + return AuthPlayerHandler(self._client, AuthPlayerRequest(raw_request)) + case "newgame": + return NewGameHandler(self._client, NewGameRequest(raw_request)) + case "getpd": + return GetPlayerDataHandler(self._client, GetPlayerDataRequest(raw_request)) + case "setpd": + return SetPlayerDataHandler(self._client, SetPlayerDataRequest(raw_request)) + case "updgame": + return UpdateGameHandler(self._client, UpdateGameRequest(raw_request)) + case _: + return None diff --git a/src/servers/game_status/tests/game_tests.py b/src/servers/game_status/tests/game_tests.py new file mode 100644 index 000000000..a6b2049ba --- /dev/null +++ b/src/servers/game_status/tests/game_tests.py @@ -0,0 +1,36 @@ + + +import unittest + +from servers.game_status.tests.mock_objects import create_client + + +class GameTest(unittest.TestCase): + def test_worm3d_20230331(self): + raws1 = [ + "\\auth\\\\gamename\\worms3\\response\\bc3ca727a7825879eb9f13d9fd51bbb9\\port\\0\\id\\1\\final\\", + "\\newgame\\\\connid\\0\\sesskey\\144562\\final\\", + "\\authp\\\\pid\\1\\resp\\7b6658e99f448388fbeddc93654e6dd4\\lid\\2\\final\\", + "\\setpd\\\\pid\\1\\ptype\\1\\dindex\\0\\kv\\1\\lid\\2\\length\\111\\data\\\\report\\|title||victories|0|timestamp|66613|league|Team17|winner||crc|-1|player_0|spyguy|ip_0||pid_0|0|auth_0|[00]\\final\\", + ] + client = create_client() + for raw in raws1: + client.on_received(raw.encode()) + + def test_gmtest(self): + raws = [ + "\\auth\\\\gamename\\crysis2\\response\\xxxxx\\port\\30\\id\\1\\final\\", + "\\getpd\\\\pid\\0\\ptype\\0\\dindex\\1\\keys\\hello\\x1hi\\lid\\1\\final\\", + "\\getpid\\\\nick\\xiaojiuwo\\keyhash\\00000\\lid\\1\\final\\", + "\\newgame\\\\connid\\123\\sesskey\\123456\\lid\\1\\final\\", + "\\newgame\\\\connid\\123\\sesskey\\2020\\lid\\1\\final\\", + "\\newgame\\\\connid\\123\\sesskey\\123456\\challenge\\123456789\\lid\\1\\final\\", + "\\newgame\\\\connid\\123\\sesskey\\2020\\challenge\\123456789\\lid\\1\\final\\", + "\\setpd\\\\pid\\123\\ptype\\0\\dindex\\1\\kv\\%d\\lid\\1\\length\\5\\data\\11\\lid\\1\\final\\", + "\\updgame\\\\sesskey\\0\\done\\1\\gamedata\\hello\\lid\\1\\final\\", + "\\updgame\\\\sesskey\\2020\\done\\1\\gamedata\\hello\\lid\\1\\final\\", + "\\updgame\\\\sesskey\\2020\\connid\\1\\done\\1\\gamedata\\hello\\lid\\1\\final\\", + "\\updgame\\\\sesskey\\0\\connid\\1\\done\\1\\gamedata\\hello\\lid\\1\\final\\"] + client = create_client() + for raw in raws: + client.on_received(raw.encode()) diff --git a/src/servers/game_status/tests/handler_tests.py b/src/servers/game_status/tests/handler_tests.py new file mode 100644 index 000000000..924b1a52a --- /dev/null +++ b/src/servers/game_status/tests/handler_tests.py @@ -0,0 +1,129 @@ + +import unittest +import responses + +from library.tests.mock_objects.general import create_mock_url +from servers.game_status.src.aggregations.gscrypt import GSCrypt +from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest +from servers.game_status.src.enums.general import PersistStorageType +from servers.game_status.src.handlers.handlers import AuthPlayerHandler, SetPlayerDataHandler, UpdateGameHandler +from servers.game_status.src.handlers.switcher import Switcher +from servers.game_status.tests.mock_objects import create_client + + +class HandlerTests(unittest.TestCase): + + @responses.activate + @unittest.skip + def test_set_player_data_20230329(self): + raw = "\\setpd\\\\pid\\1\\ptype\\1\\dindex\\0\\kv\\1\\lid\\2\\length\\111\\data\\\\report\\|title||victories|0|timestamp|37155|league|Team17|winner||crc|-1|player_0|spyguy|ip_0||pid_0|0|auth_0|[00]\\final\\" + client = create_client() + + create_mock_url(client, SetPlayerDataHandler, {"message": "ok"}) + + request = SetPlayerDataRequest(raw) + request.parse() + self.assertEqual(1, request.profile_id) + self.assertEqual(PersistStorageType.PRIVATE_READ_WRITE, + request.storage_type) + self.assertEqual(0, request.data_index) + self.assertEqual("", request.data) + self.assertEqual(111, request.length) + self.assertEqual( + "|title||victories|0|timestamp|37155|league|Team17|winner||crc|-1|player_0|spyguy|ip_0||pid_0|0|auth_0|[00]", request.report) + + handler = SetPlayerDataHandler(client, request) + handler.handle() + + @unittest.skip + def test_gamespysdk_update_game_20230329(self): + raw1 = "\\updgame\\\\sesskey\\20298203\\connid\\0\\done\\0\\gamedata\\\u0001hostname\u0001My l33t Server\u0001mapname\u0001Level 33\u0001gametype\u0001hunter\u0001gamever\u00011.230000\u0001player_0\u0001Bob!\u0001points_0\u00014\u0001deaths_0\u00012\u0001pid_0\u000132432423\u0001auth_0\u00017cca8e60a13781eebc820a50754f57cd\u0001player_1\u0001Joey\u0001points_1\u00012\u0001deaths_1\u00014\u0001pid_1\u0001643423\u0001auth_1\u000119ea14d9d92a7fcc635cf5716944d9bc\\final\\" + raw2 = "\\updgame\\\\sesskey\\20298203\\connid\\0\\done\\1\\gamedata\\\u0001hostname\u0001My l33t Server\u0001mapname\u0001Level 33\u0001gametype\u0001hunter\u0001gamever\u00011.230000\u0001player_0\u0001Bob!\u0001points_0\u00016\u0001deaths_0\u00013\u0001pid_0\u000132432423\u0001auth_0\u00017cca8e60a13781eebc820a50754f57cd\u0001player_1\u0001Joey\u0001points_1\u00013\u0001deaths_1\u00016\u0001pid_1\u0001643423\u0001auth_1\u000119ea14d9d92a7fcc635cf5716944d9bc\\final\\" + client = create_client() + create_mock_url(client, UpdateGameHandler, {"message": "ok"}) + switcher = Switcher(client, raw1) + switcher.handle() + request: UpdateGameRequest = switcher._handlers[0]._request + response = switcher._handlers[0]._response + self.assertEqual("20298203", request.session_key) + self.assertEqual(1, request.connection_id) + self.assertEqual(False, request.is_done) + self.assertEqual("\u0001hostname\u0001My l33t Server\u0001mapname\u0001Level 33\u0001gametype\u0001hunter\u0001gamever\u00011.230000\u0001player_0\u0001Bob!\u0001points_0\u00014\u0001deaths_0\u00012\u0001pid_0\u000132432423\u0001auth_0\u00017cca8e60a13781eebc820a50754f57cd\u0001player_1\u0001Joey\u0001points_1\u00012\u0001deaths_1\u00014\u0001pid_1\u0001643423\u0001auth_1\u000119ea14d9d92a7fcc635cf5716944d9bc", request.game_data) + + @unittest.skip + def test_worm3d_auth_player(self): + raw = "2\x0F\x16\x10]%+=veKaB3a(UC`b$\x1CO\x11VZX\x09w\x1Cu\x08L@\x13=X!\x1E{\x0EL\x1DLf[qN \x04G\x130[#N'\x09(IC`b$\\final\\" + plaintext = GSCrypt().decrypt(raw.encode("ascii")) + request = AuthPlayerRequest(raw) + client = create_client() + create_mock_url(client, AuthPlayerHandler, {"profile_id": 1}) + handler = AuthPlayerHandler(client, request) + handler.handle() + + self.assertEqual() + + def test_auth(self): + raw = "\\auth\\\\gamename\\crysis2\\response\\xxxxx\\port\\30\\id\\1\\final\\" + request = AuthGameRequest(raw) + request.parse() + self.assertEqual("crysis2", request.game_name) + self.assertEqual(30, request.port) + self.assertEqual(1, request.local_id) + + def test_get_player_data(self): + raw = "\\getpd\\\\pid\\0\\ptype\\0\\dindex\\1\\keys\\hello\x01hi\\lid\\1\\final\\" + + request = GetPlayerDataRequest(raw) + request.parse() + self.assertEqual(0, request.profile_id) + self.assertEqual(PersistStorageType.PRIVATE_READ_ONLY, + request.storage_type) + self.assertEqual(1, request.data_index) + self.assertEqual(2, len(request.keys)) + self.assertEqual("hello", request.keys[0]) + self.assertEqual("hi", request.keys[1]) + + def test_get_profile_id(self): + raw = "\\getpid\\\\nick\\xiaojiuwo\\keyhash\\00000\\lid\\1\\final\\" + request = GetProfileIdRequest(raw) + request.parse() + self.assertEqual("xiaojiuwo", request.nick) + self.assertEqual("00000", request.keyhash) + self.assertEqual(1, request.local_id) + + def test_new_game(self): + raw1 = "\\newgame\\\\connid\\123\\sesskey\\123456\\lid\\1\\final\\" + request1 = NewGameRequest(raw1) + request1.parse() + + self.assertEqual(123, request1.connection_id) + self.assertEqual("123456", request1.session_key) + self.assertEqual(1, request1.local_id) + + raw2 = "\\newgame\\\\connid\\123\\sesskey\\123456\\challenge\\123456789\\lid\\1\\final\\" + request2 = NewGameRequest(raw2) + request2.parse() + self.assertEqual(123, request2.connection_id) + self.assertEqual("123456", request2.session_key) + self.assertEqual("123456789", request2.challenge) + self.assertEqual(1, request2.local_id) + + def test_update_game(self): + raw1 = "\\updgame\\\\sesskey\\0\\done\\1\\gamedata\\hello\\lid\\1\\final\\" + request1 = UpdateGameRequest(raw1) + request1.parse() + self.assertEqual("0", request1.session_key) + self.assertEqual(True, request1.is_done) + self.assertEqual("hello", request1.game_data) + self.assertEqual(None, request1.connection_id) + raw2 = "\\updgame\\\\sesskey\\0\\connid\\1\\done\\1\\gamedata\\hello\\lid\\1\\final\\" + request2 = UpdateGameRequest(raw2) + request2.parse() + self.assertEqual("0", request2.session_key) + self.assertEqual(True, request2.is_done) + self.assertEqual("hello", request2.game_data) + self.assertEqual(1, request2.connection_id) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/servers/game_status/tests/mock_objects.py b/src/servers/game_status/tests/mock_objects.py new file mode 100644 index 000000000..3d1487193 --- /dev/null +++ b/src/servers/game_status/tests/mock_objects.py @@ -0,0 +1,19 @@ +from library.src.unispy_server_config import CONFIG +from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock +from servers.game_status.src.applications.client import Client + + +class ClientMock(Client): + + pass + + +def create_client(): + handler = RequestHandlerMock() + logger = LogMock() + conn = ConnectionMock( + handler=handler, + config=CONFIG.servers["GameStatus"], t_client=ClientMock, + logger=logger) + + return conn._client diff --git a/src/servers/presence_connection_manager/src/applications/client.py b/src/servers/presence_connection_manager/src/applications/client.py index 0d343bd42..deeb71007 100644 --- a/src/servers/presence_connection_manager/src/applications/client.py +++ b/src/servers/presence_connection_manager/src/applications/client.py @@ -53,6 +53,6 @@ def on_connected(self) -> None: self.log_network_sending(buffer) self.connection.send(buffer) - def create_switcher(self, buffer) -> SwitcherBase: + def create_switcher(self, buffer: bytes) -> SwitcherBase: from servers.presence_connection_manager.src.handlers.switcher import Switcher - return Switcher(self, buffer) + return Switcher(self, buffer.decode()) diff --git a/src/servers/presence_connection_manager/src/handlers/switcher.py b/src/servers/presence_connection_manager/src/handlers/switcher.py index ca9f9a0f5..e78cce7e1 100644 --- a/src/servers/presence_connection_manager/src/handlers/switcher.py +++ b/src/servers/presence_connection_manager/src/handlers/switcher.py @@ -21,9 +21,9 @@ class Switcher(SwitcherBase): _raw_request: str def __init__(self, client: Client, raw_request: str) -> None: + super().__init__(client, raw_request) assert isinstance(client, Client) assert isinstance(raw_request, str) - super().__init__(client, raw_request) def _process_raw_request(self) -> None: if self._raw_request[0] != "\\": diff --git a/src/servers/presence_search_player/src/applications/client.py b/src/servers/presence_search_player/src/applications/client.py index af854d8b3..27724705c 100644 --- a/src/servers/presence_search_player/src/applications/client.py +++ b/src/servers/presence_search_player/src/applications/client.py @@ -8,3 +8,7 @@ class Client(ClientBase): def _create_switcher(self, buffer) -> SwitcherBase: from servers.presence_search_player.src.handlers.switcher import CmdSwitcher return CmdSwitcher(self, buffer) + + def create_switcher(self, buffer: bytes) -> SwitcherBase: + from servers.presence_search_player.src.handlers.switcher import CmdSwitcher + return CmdSwitcher(self, buffer.decode()) diff --git a/src/servers/presence_search_player/src/handlers/switcher.py b/src/servers/presence_search_player/src/handlers/switcher.py index ff2ad54b9..ba20cd721 100644 --- a/src/servers/presence_search_player/src/handlers/switcher.py +++ b/src/servers/presence_search_player/src/handlers/switcher.py @@ -13,8 +13,9 @@ class CmdSwitcher(SwitcherBase): _raw_request: str def __init__(self, client: Client, raw_request: str): - assert isinstance(client, Client) super().__init__(client, raw_request) + assert isinstance(client, Client) + assert isinstance(raw_request, str) def _process_raw_request(self): if self._raw_request[0] != "\\": diff --git a/src/servers/presence_search_player/tests/mock_objects.py b/src/servers/presence_search_player/tests/mock_objects.py index 0d8b8279f..644ddd6d5 100644 --- a/src/servers/presence_search_player/tests/mock_objects.py +++ b/src/servers/presence_search_player/tests/mock_objects.py @@ -13,7 +13,7 @@ def create_client(): logger = LogMock() conn = ConnectionMock( handler=handler, - config=CONFIG.servers["NatNegotiation"], t_client=ClientMock, + config=CONFIG.servers["PresenceSearchPlayer"], t_client=ClientMock, logger=logger) return conn._client diff --git a/src/servers/webservices/src/applications/client.py b/src/servers/webservices/src/applications/client.py index 3d6777da9..b8991c13e 100644 --- a/src/servers/webservices/src/applications/client.py +++ b/src/servers/webservices/src/applications/client.py @@ -1,4 +1,6 @@ from library.src.abstractions.client import ClientBase, ClientInfoBase +from library.src.abstractions.switcher import SwitcherBase +from library.src.network.http_handler import HttpRequest class ClientInfo(ClientInfoBase): @@ -16,12 +18,18 @@ class ClientInfo(ClientInfoBase): SERVER_DATA = "95980bf5011ce73f2866b995a272420c36f1e8b4ac946f0b5bfe87c9fef0811036da00cfa85e77e00af11c924d425ec06b1dd052feab1250376155272904cbf9da831b0ce3d52964424c0a426b869e2c0ad11ffa3e70496e27ea250adb707a96b3496bff190eafc0b6b9c99db75b02c2a822bb1b5b3d954e7b2c0f9b1487e3e1" SIGNATURE_PREFIX = "0001FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF003020300C06082A864886F70D020505000410" - PRIVATE_KEY_D = [0x96, 0xE2, 0xF4, 0xF9, 0x7C, 0x78, 0x81, 0x0C, 0x13, 0xE2, 0xA4, 0xCA, 0xC1, 0x12, 0x44, 0xBB, 0x5B, 0x37, 0xC5, 0x4E, 0x98, 0x20, 0xF1, 0x3F, 0xE9, 0xB5, 0x53, 0xDA, 0x10, 0xB1, 0xE8, 0xEF, 0x8E, 0xB0, 0x3F, 0x87, 0x68, 0x1B, 0x0E, 0x62, 0x4D, 0x1A, 0x8D, 0xE9, 0x17, 0x4C, 0xBE, 0xE5, 0xB8, 0xED, 0x92, 0xE4, 0xBE, 0x74, 0xF8, 0x6C, 0x30, 0x38, 0xCD, 0x7C, 0x1A, 0x20, 0xB9, 0xA3, 0xDB, 0x1D, 0x49, 0x22, 0x62, 0x87, 0x38, 0x68, 0xFB, 0xA0, 0x8E, 0x1E, 0xAB, 0x5C, 0xBA, 0x86, 0x3F, 0x8F, 0xDB, 0xF4, 0x5E, 0xEA, 0x61, 0x4B, 0xBF, 0x6C, 0xFC, 0x47, 0x00, 0x81, 0x44, 0x2A, 0x97, 0x78, 0x7E, 0xB6, 0xEC, 0xA7, 0x1C, 0x48, 0x96, 0x81, 0x6C, 0x2A, 0x62, 0x72, 0x4C, 0x0E, 0x8C, 0xAA, 0xEE, 0xAB, 0x72, 0x78, 0xC2, 0x55, 0x4A, 0x13, 0x80, 0x94, 0x6E, 0xED, 0x21, 0x29] # fmt: skip + PRIVATE_KEY_D = [0x96, 0xE2, 0xF4, 0xF9, 0x7C, 0x78, 0x81, 0x0C, 0x13, 0xE2, 0xA4, 0xCA, 0xC1, 0x12, 0x44, 0xBB, 0x5B, 0x37, 0xC5, 0x4E, 0x98, 0x20, 0xF1, 0x3F, 0xE9, 0xB5, 0x53, 0xDA, 0x10, 0xB1, 0xE8, 0xEF, 0x8E, 0xB0, 0x3F, 0x87, 0x68, 0x1B, 0x0E, 0x62, 0x4D, 0x1A, 0x8D, 0xE9, 0x17, 0x4C, 0xBE, 0xE5, 0xB8, 0xED, 0x92, 0xE4, 0xBE, 0x74, 0xF8, 0x6C, 0x30, 0x38, 0xCD, 0x7C, 0x1A, 0x20, 0xB9, 0xA3, + 0xDB, 0x1D, 0x49, 0x22, 0x62, 0x87, 0x38, 0x68, 0xFB, 0xA0, 0x8E, 0x1E, 0xAB, 0x5C, 0xBA, 0x86, 0x3F, 0x8F, 0xDB, 0xF4, 0x5E, 0xEA, 0x61, 0x4B, 0xBF, 0x6C, 0xFC, 0x47, 0x00, 0x81, 0x44, 0x2A, 0x97, 0x78, 0x7E, 0xB6, 0xEC, 0xA7, 0x1C, 0x48, 0x96, 0x81, 0x6C, 0x2A, 0x62, 0x72, 0x4C, 0x0E, 0x8C, 0xAA, 0xEE, 0xAB, 0x72, 0x78, 0xC2, 0x55, 0x4A, 0x13, 0x80, 0x94, 0x6E, 0xED, 0x21, 0x29] # fmt: skip - MODULUES = [ 0xA9, 0x3E, 0x00, 0x19, 0x2C, 0x4A, 0x98, 0x69, 0xD7, 0x41, 0x9A, 0xFF, 0x66, 0x2E, 0xCA, 0xD6, 0xC8, 0xB9, 0x99, 0x09, 0xFD, 0xD0, 0xE7, 0xF8, 0xCA, 0xDD, 0x15, 0x32, 0xE8, 0xE3, 0x59, 0x37, 0x40, 0x83, 0xDA, 0xB8, 0xBE, 0x71, 0x7F, 0x60, 0x91, 0x60, 0xCD, 0x6A, 0x54, 0x11, 0xBE, 0xD7, 0x92, 0x7A, 0xD3, 0xB5, 0xC0, 0x0C, 0x4C, 0x4B, 0x34, 0x76, 0x71, 0xF2, 0x3F, 0xE0, 0x1E, 0xBB, 0x2F, 0x83, 0x4B, 0xD8, 0xCA, 0x27, 0xC3, 0x55, 0x3E, 0x1E, 0x6B, 0xC2, 0x85, 0xAF, 0xC6, 0x3E, 0xC0, 0xE1, 0x1F, 0x59, 0xCA, 0xF6, 0xAC, 0x37, 0x5F, 0x4B, 0x0E, 0xB8, 0x2A, 0x4D, 0xA2, 0x2C, 0x0C, 0x10, 0x1D, 0x09, 0x13, 0x1A, 0x7C, 0x42, 0x0D, 0x4C, 0xC4, 0xD0, 0x95, 0x62, 0x4C, 0x42, 0xBC, 0xD8, 0xD7, 0x19, 0x16, 0xC8, 0xDC, 0x00, 0x48, 0x08, 0x6D, 0x74, 0x6A, 0x31, 0x55, 0xC7] # fmt: skip + MODULUES = [0xA9, 0x3E, 0x00, 0x19, 0x2C, 0x4A, 0x98, 0x69, 0xD7, 0x41, 0x9A, 0xFF, 0x66, 0x2E, 0xCA, 0xD6, 0xC8, 0xB9, 0x99, 0x09, 0xFD, 0xD0, 0xE7, 0xF8, 0xCA, 0xDD, 0x15, 0x32, 0xE8, 0xE3, 0x59, 0x37, 0x40, 0x83, 0xDA, 0xB8, 0xBE, 0x71, 0x7F, 0x60, 0x91, 0x60, 0xCD, 0x6A, 0x54, 0x11, 0xBE, 0xD7, 0x92, 0x7A, 0xD3, 0xB5, 0xC0, 0x0C, 0x4C, 0x4B, 0x34, 0x76, 0x71, 0xF2, 0x3F, 0xE0, 0x1E, 0xBB, + 0x2F, 0x83, 0x4B, 0xD8, 0xCA, 0x27, 0xC3, 0x55, 0x3E, 0x1E, 0x6B, 0xC2, 0x85, 0xAF, 0xC6, 0x3E, 0xC0, 0xE1, 0x1F, 0x59, 0xCA, 0xF6, 0xAC, 0x37, 0x5F, 0x4B, 0x0E, 0xB8, 0x2A, 0x4D, 0xA2, 0x2C, 0x0C, 0x10, 0x1D, 0x09, 0x13, 0x1A, 0x7C, 0x42, 0x0D, 0x4C, 0xC4, 0xD0, 0x95, 0x62, 0x4C, 0x42, 0xBC, 0xD8, 0xD7, 0x19, 0x16, 0xC8, 0xDC, 0x00, 0x48, 0x08, 0x6D, 0x74, 0x6A, 0x31, 0x55, 0xC7] # fmt: skip EXPONENT = [0x01, 0x00, 0x01] # fmt: skip class Client(ClientBase): info: ClientInfo + + def create_switcher(self, buffer: HttpRequest) -> SwitcherBase: + from servers.webservices.src.handlers.switcher import Switcher + return Switcher(self, buffer) diff --git a/src/servers/webservices/src/handlers/switcher.py b/src/servers/webservices/src/handlers/switcher.py new file mode 100644 index 000000000..abee45255 --- /dev/null +++ b/src/servers/webservices/src/handlers/switcher.py @@ -0,0 +1,114 @@ +from library.src.abstractions.handler import CmdHandlerBase +from library.src.abstractions.switcher import SwitcherBase +import xml.etree.ElementTree as ET + +from servers.webservices.src.exceptions.general import WebExceptions +from servers.webservices.src.modules.auth.contracts.requests import LoginProfileRequest, LoginProfileWithGameIdRequest, LoginRemoteAuthRequest, LoginRemoteAuthWithGameIdRequest, LoginUniqueNickRequest, LoginUniqueNickWithGameIdRequest +from servers.webservices.src.modules.auth.handlers.general import LoginProfileHandler, LoginProfileWithGameIdHandler, LoginRemoteAuthHandler, LoginRemoteAuthWithGameIdHandler, LoginUniqueNickHandler, LoginUniqueNickWithGameIdHandler +from servers.webservices.src.modules.direct2game.contracts.requests import GetPurchaseHistoryRequest, GetStoreAvailabilityRequest +from servers.webservices.src.modules.direct2game.handlers.general import GetPurchaseHistoryHandler, GetStoreAvailabilityHandler +from servers.webservices.src.modules.sake.contracts.requests import CreateRecordRequest, GetMyRecordsRequest, SearchForRecordsRequest +from servers.webservices.src.modules.sake.handlers.general import CreateRecordHandler, GetMyRecordsHandler, SearchForRecordsHandler + + +class Switcher(SwitcherBase): + def _process_raw_request(self) -> None: + name_node = ET.fromstring(self._raw_request)[0][0] + if name_node is None: + raise WebExceptions("name node is missing from soap request") + + name = name_node.text.split("}")[1] + + if len(name) < 4: + raise WebExceptions("request name invalid") + self._requests.append((name, self._raw_request)) + + def _create_cmd_handlers(self, name: str, raw_request: str) -> CmdHandlerBase | None: + assert isinstance(name, str) + assert isinstance(raw_request, str) + + match name: + # Altas services + case "CreateMatchlessSession": + raise NotImplementedError() + case "CreateSession": + raise NotImplementedError() + case "SetReportIntention": + raise NotImplementedError() + case "SubmitReport": + raise NotImplementedError() + + # Auth services + case "LoginProfile": + return LoginProfileHandler( + self._client, LoginProfileRequest(raw_request)) + case "LoginProfileWithGameId": + return LoginProfileWithGameIdHandler( + self._client, LoginProfileWithGameIdRequest(raw_request)) + case "LoginRemoteAuth": + return LoginRemoteAuthHandler(self._client, LoginRemoteAuthRequest(raw_request)) + + case "LoginRemoteAuthWithGameId": + return LoginRemoteAuthWithGameIdHandler(self._client, LoginRemoteAuthWithGameIdRequest) + + case "LoginUniqueNick": + return LoginUniqueNickHandler(self._client, LoginUniqueNickRequest(raw_request)) + case "LoginUniqueNickWithGameId": + return LoginUniqueNickWithGameIdHandler(self._client, LoginUniqueNickWithGameIdRequest(raw_request)) + + # Direct2Game services + + case "GetStoreAvailability": + return GetStoreAvailabilityHandler(self._client, GetStoreAvailabilityRequest(raw_request)) + + case "GetPurchaseHistory": + return GetPurchaseHistoryHandler(self._client, GetPurchaseHistoryRequest(raw_request)) + + case "GetTargettedAd": + raise NotImplementedError() + + # InGameAd services + case "ReportAdUsage": + raise NotImplementedError() + + # PatchingAndTracking + case "Motd": + raise NotImplementedError() + case "Vercheck": + raise NotImplementedError() + + # Racing + case "GetContestData": + raise NotImplementedError() + case "GetFriendRankings": + raise NotImplementedError() + case "GetRegionalData": + raise NotImplementedError() + case "GetTenAboveRankings": + raise NotImplementedError() + case "GetTopTenRankings": + raise NotImplementedError() + case "SubmitScores": + raise NotImplementedError() + + # SAKE + case "CreateRecord": + return CreateRecordHandler(self._client, CreateRecordRequest(raw_request)) + case "DeleteRecord": + raise NotImplementedError() + case "GetMyRecords": + raise GetMyRecordsHandler( + self._client, GetMyRecordsRequest(raw_request)) + case "GetRandomRecords": + raise NotImplementedError() + case "GetRecordLimit": + raise NotImplementedError() + case "RateRecord": + raise NotImplementedError() + case "SearchForRecords": + return SearchForRecordsHandler(self._client, SearchForRecordsRequest(raw_request)) + case "UpdateRecord": + raise NotImplementedError() + case _: + self._client.log_error(f"Unknown {name} request received") + return None From 3311a31d8d6b329bae082b14a82f0b34c456450f Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 3 Sep 2024 17:18:31 +0800 Subject: [PATCH 099/231] refactor: orgnized files --- .../contracts/__init__.py | 0 .../presence_connection_manager}/data.py | 0 .../{contracts => }/requests.py | 0 .../protocols/presence_search_player/data.py | 260 ++++++++++++++++++ .../gamespy/protocols/query_report}/data.py | 0 .../gamespy/presence_connection_manager.py | 4 + src/library/src/abstractions/brocker.py | 11 +- src/library/src/abstractions/contracts.py | 2 + src/library/src/abstractions/redis_channel.py | 100 +++++++ .../src/abstractions/server_launcher_base.py | 3 +- src/library/src/extentions/redis_orm.py | 2 + src/requirements.txt | 4 +- src/servers/chat/src/abstractions/channel.py | 50 ++-- src/servers/chat/src/abstractions/contract.py | 10 +- src/servers/chat/src/abstractions/handler.py | 2 + src/servers/chat/src/abstractions/message.py | 1 + src/servers/chat/src/aggregates/brockers.py | 43 ++- src/servers/chat/src/aggregates/channel.py | 60 ++-- .../chat/src/aggregates/storage_info.py | 14 + src/servers/chat/src/applications/client.py | 1 + .../{applications => handlers}/switcher.py | 0 .../src/aggregations/init_packet_info.py | 5 +- .../src/applications/data.py | 260 ------------------ .../query_report/src/applications/client.py | 2 +- .../query_report/src/v2/applications/data.py | 1 - .../v2/{applications => handlers}/switcher.py | 0 .../v2/aggregations/server_info_builder.py | 2 +- 27 files changed, 496 insertions(+), 341 deletions(-) delete mode 100644 src/backends/gamespy/protocols/presence_connection_manager/contracts/__init__.py rename src/{servers/presence_connection_manager/src/applications => backends/gamespy/protocols/presence_connection_manager}/data.py (100%) rename src/backends/gamespy/protocols/presence_connection_manager/{contracts => }/requests.py (100%) rename src/{servers/query_report/src/applications => backends/gamespy/protocols/query_report}/data.py (100%) rename src/servers/chat/src/{applications => handlers}/switcher.py (100%) delete mode 100644 src/servers/presence_search_player/src/applications/data.py delete mode 100644 src/servers/query_report/src/v2/applications/data.py rename src/servers/query_report/src/v2/{applications => handlers}/switcher.py (100%) diff --git a/src/backends/gamespy/protocols/presence_connection_manager/contracts/__init__.py b/src/backends/gamespy/protocols/presence_connection_manager/contracts/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/presence_connection_manager/src/applications/data.py b/src/backends/gamespy/protocols/presence_connection_manager/data.py similarity index 100% rename from src/servers/presence_connection_manager/src/applications/data.py rename to src/backends/gamespy/protocols/presence_connection_manager/data.py diff --git a/src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py b/src/backends/gamespy/protocols/presence_connection_manager/requests.py similarity index 100% rename from src/backends/gamespy/protocols/presence_connection_manager/contracts/requests.py rename to src/backends/gamespy/protocols/presence_connection_manager/requests.py diff --git a/src/backends/gamespy/protocols/presence_search_player/data.py b/src/backends/gamespy/protocols/presence_search_player/data.py index e69de29bb..869deb1da 100644 --- a/src/backends/gamespy/protocols/presence_search_player/data.py +++ b/src/backends/gamespy/protocols/presence_search_player/data.py @@ -0,0 +1,260 @@ +from sqlalchemy import insert +from library.src.database.pg_orm import ( + Friends, + Profiles, + SubProfiles, + Users, + PG_SESSION, +) + + +def verify_email(email: str): + if PG_SESSION.query(Users).filter(Users.email == email).count() == 1: + return True + else: + return False + + +def verify_email_and_password(email: str, password: str): + result = ( + PG_SESSION.query(Users) + .filter(Users.email == email, Users.password == password) + .count() + ) + if result == 1: + return True + return False + + +def get_profile_id(email: str, password: str, nick_name: str, partner_id: int): + result = ( + PG_SESSION.query(Profiles, SubProfiles, Users) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter( + Users.email == email, + Users.password == password, + Profiles.nick == nick_name, + SubProfiles.partnerid == partner_id, + ) + .first() + ) + + return result + + +def add_user(user: Users): + PG_SESSION.add(user) + PG_SESSION.commit() + + +def add_profile(profile: Profiles): + PG_SESSION.add(profile) + PG_SESSION.commit() + + +def add_sub_profile(subprofile: SubProfiles): + PG_SESSION.add(subprofile) + PG_SESSION.commit() + + +def update_user(user: Users): + PG_SESSION.merge(user) + PG_SESSION.commit() + + +def update_profile(profile: Profiles): + PG_SESSION.merge(profile) + PG_SESSION.commit() + + +def update_subprofile(subprofile: SubProfiles): + PG_SESSION.merge(subprofile) + PG_SESSION.commit() + + +def get_user(email: str): + result = PG_SESSION.query(Users).filter(Users.email == email).first() + return result + + +def get_profile(user_id: int, nick_name: str) -> Profiles: + result = PG_SESSION.query(Profiles).filter( + Profiles.userid == user_id, Profiles.nick == nick_name + ) + return result + + +def get_sub_profile(profile_id: int, namespace_id: int, product_id: int) -> SubProfiles: + PG_SESSION.query(SubProfiles).filter( + SubProfiles.profileid == profile_id, + SubProfiles.namespaceid == namespace_id, + SubProfiles.namespaceid == product_id, + ) + + +def get_nick_and_unique_nick_list(email: str, password: str, namespace_id: int): + """ + return [(nick, uniquenick)] + """ + result = ( + PG_SESSION.query(Profiles.nick, SubProfiles.uniquenick) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter( + Users.email == email, + Users.password == password, + SubProfiles.namespaceid == namespace_id, + ) + .all() + ) + return result + + +def get_friend_info_list(profile_id: int, namespace_id: int, game_name: str): + """ + return [(profileid, nick, uniquenick, lastname, firstname, userid, email)] + """ + + result = ( + PG_SESSION.query( + Profiles.profileid, + Profiles.nick, + SubProfiles.uniquenick, + Profiles.lastname, + Profiles.firstname, + Users.userid, + Users.email, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter( + Profiles.profileid.in_(PG_SESSION.query(Friends.profileid == profile_id)), + SubProfiles.namespaceid == namespace_id, + SubProfiles.gamename == game_name, + ) + .all() + ) + return result + + +def get_matched_profile_info_list( + profile_ids: list[int], namespace_id: int +) -> list[tuple[int, str]]: + """ + return [(profileid,uniquenick)] + + """ + result = ( + PG_SESSION.query(SubProfiles.profileid, SubProfiles.uniquenick) + .filter( + SubProfiles.profileid.in_(profile_ids), + SubProfiles.namespaceid == namespace_id, + ) + .all() + ) + return result + + +def get_matched_info_by_nick( + nick_name: str, +) -> list[tuple[int, str, str, str, str, int]]: + result = ( + PG_SESSION.query( + Profiles.profileid, + Profiles.nick, + Profiles.firstname, + Profiles.lastname, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter(Profiles.nick == nick_name) + .all() + ) + return result + + +def get_matched_info_by_email( + email: str, +) -> list[tuple[int, str, str, str, str, int]]: + result = ( + PG_SESSION.query( + Profiles.profileid, + Profiles.nick, + Profiles.firstname, + Profiles.lastname, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter(Users.email == email) + .all() + ) + return result + + +def get_matched_info_by_nick_and_email(nick_name: str, email: str): + result = ( + PG_SESSION.query( + Profiles.profileid, + Profiles.nick, + Profiles.firstname, + Profiles.lastname, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter(Users.email == email, Profiles.nick == nick_name) + .all() + ) + return result + + +def get_matched_info_by_uniquenick_and_namespaceid( + unique_nick: str, namespace_id: int +) -> list[tuple[int, str, str, str, str, int]]: + result = ( + PG_SESSION.query( + Profiles.profileid, + Profiles.nick, + Profiles.firstname, + Profiles.lastname, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter( + SubProfiles.uniquenick == unique_nick, + SubProfiles.namespaceid == namespace_id, + ) + .all() + ) + return result + + +def is_uniquenick_exist(unique_nick: str, namespace_id: int, game_name: str): + result = ( + PG_SESSION.query(Profiles) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter( + SubProfiles.uniquenick == unique_nick, + SubProfiles.gamename == game_name, + SubProfiles.namespaceid == namespace_id, + ) + .all() + ) + + return result + + +def is_email_exist(email: str): + result = PG_SESSION.query(Users.userid).filter(Users.email == email).count() + #! According to FSW partnerid is not nessesary + if result == 0: + return False + return True diff --git a/src/servers/query_report/src/applications/data.py b/src/backends/gamespy/protocols/query_report/data.py similarity index 100% rename from src/servers/query_report/src/applications/data.py rename to src/backends/gamespy/protocols/query_report/data.py diff --git a/src/backends/routers/gamespy/presence_connection_manager.py b/src/backends/routers/gamespy/presence_connection_manager.py index cd5767df9..98ccb88f7 100644 --- a/src/backends/routers/gamespy/presence_connection_manager.py +++ b/src/backends/routers/gamespy/presence_connection_manager.py @@ -2,6 +2,7 @@ from backends.gamespy.protocols.presence_search_player.requests import LoginRequest from backends.urls import * +import uvicorn app = FastAPI() @@ -10,3 +11,6 @@ def login(request: LoginRequest): pass + + + diff --git a/src/library/src/abstractions/brocker.py b/src/library/src/abstractions/brocker.py index 96268e251..1a9298a2d 100644 --- a/src/library/src/abstractions/brocker.py +++ b/src/library/src/abstractions/brocker.py @@ -1,5 +1,8 @@ import abc +from servers.chat.src.aggregates.channel import MIN_CHANNEL_NAME_LENGTH +from servers.chat.src.exceptions.general import ChatException + class BrockerBase: _subscriber: object @@ -12,10 +15,13 @@ class BrockerBase: def __init__(self, name: str) -> None: assert isinstance(name, str) + if len(name) < MIN_CHANNEL_NAME_LENGTH: + raise ChatException(f"The channel name length must larget than { + MIN_CHANNEL_NAME_LENGTH}") self._name = name @abc.abstractmethod - def _subscribe(self): + def subscribe(self): """ define the brocker event binding """ @@ -33,6 +39,3 @@ def publish_message(self, message): @abc.abstractmethod def unsubscribe(self): pass - - def __del__(self): - self.unsubscribe() diff --git a/src/library/src/abstractions/contracts.py b/src/library/src/abstractions/contracts.py index 33c765738..e532fcf80 100644 --- a/src/library/src/abstractions/contracts.py +++ b/src/library/src/abstractions/contracts.py @@ -70,3 +70,5 @@ def __init__(self, request: RequestBase, result: Optional[ResultBase]) -> None: @abc.abstractmethod def build(self) -> None: pass + + diff --git a/src/library/src/abstractions/redis_channel.py b/src/library/src/abstractions/redis_channel.py index 505c059fb..8d31d2d78 100644 --- a/src/library/src/abstractions/redis_channel.py +++ b/src/library/src/abstractions/redis_channel.py @@ -1,2 +1,102 @@ +from dataclasses import dataclass +from typing import Optional + +from library.src.exceptions.general import UniSpyException + + class RedisChannelBase: pass + + +# class RedisKey: +# is_required = False +# value: str | int + +# def __init__(self, value: str | int) -> None: +# if (not isinstance(value, str)) or (not isinstance(value, int)): +# raise UniSpyException("Redis key must be int or str") +# self.value = value + + +DELIMETER = ":" + + +class RedisKeyValueObject: + """ + The base class of redis keyvalue object + + ------- + create the redis key with RedisKey to identity that is a redis key + """ + pass + _keys: list[str] + + def _define_keys(self): + """ + Override this function to define keys + """ + if self._keys is None: + raise UniSpyException( + "You must set keys before creating class instance") + if not all(isinstance(key, str) for key in self._keys): + raise UniSpyException("all key must be str") + + def __init__(self): + self._define_keys() + + def to_json(self) -> dict: + """ + convert object to json serializable dict + """ + import json + try: + output_dict = json.dumps(self) + except: + raise UniSpyException("all value must be python basic type") + return output_dict + + # @staticmethod + # def _check_is_basic_type(value): + # if not isinstance(value, str) or not isinstance(value, int) or not isinstance(value, float) or not isinstance(value, list) or isinstance(): + + def get_full_key(self) -> str: + """ + key format: + key1 = value1; key2 = value2; key3=value3 + every property must have + """ + full_key = "" + for key in self._keys: + if key not in self.__dict__: + # fmt: off + raise UniSpyException(f"key: {key} do not have value, in order to build full key every key must have value") + # fmt: on + if self.__dict__[key] is None: + # fmt: off + raise UniSpyException(f"key: {key} can not be none, in order to build full key every key must have value") + # fmt: on + value = self.__dict__[key] + full_key += f"{key}={value}" + if key != self._keys[-1]: + full_key += DELIMETER + return full_key + + def get_search_key(self) -> str: + """ + get keys using to search + key format: + key1=value1;key2=*;key3=value3 + """ + search_key = "" + for key in self._keys: + if key not in self.__dict__: + search_key += f"{key}=*" + + if self.__dict__[key] is None: + search_key += f"{key}=*" + + value = self.__dict__[key] + search_key += f"{key}={value}" + if key != self._keys[-1]: + search_key += DELIMETER + return search_key diff --git a/src/library/src/abstractions/server_launcher_base.py b/src/library/src/abstractions/server_launcher_base.py index 2885de344..f627b48d7 100644 --- a/src/library/src/abstractions/server_launcher_base.py +++ b/src/library/src/abstractions/server_launcher_base.py @@ -1,4 +1,5 @@ import abc +from library.src.exceptions.general import UniSpyException from library.src.log.log_manager import LogManager, LogWriter from library.src.unispy_server_config import CONFIG, ServerConfig import pyfiglet @@ -52,7 +53,7 @@ def _connect_to_backend(self): ) except: # fmt: off - raise Exception(f"backend server: {CONFIG.backend.url} not available.") + raise UniSpyException(f"backend server: {CONFIG.backend.url} not available.") # fmt: on def _create_logger(self): diff --git a/src/library/src/extentions/redis_orm.py b/src/library/src/extentions/redis_orm.py index 9ffb98a6b..ab469f50e 100644 --- a/src/library/src/extentions/redis_orm.py +++ b/src/library/src/extentions/redis_orm.py @@ -34,3 +34,5 @@ # query = QueryBuilder(None, None) # result = query.filter_by(url="example.com", name="hello").first() # print(result) +import ast + diff --git a/src/requirements.txt b/src/requirements.txt index cd60d9d4f..07348c04a 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -12,4 +12,6 @@ xmltodict responses pydantic responses -redis \ No newline at end of file +redis +websocket-client +uvicorn \ No newline at end of file diff --git a/src/servers/chat/src/abstractions/channel.py b/src/servers/chat/src/abstractions/channel.py index a8a7ec0dc..9878b3b29 100644 --- a/src/servers/chat/src/abstractions/channel.py +++ b/src/servers/chat/src/abstractions/channel.py @@ -1,9 +1,30 @@ from servers.chat.src.abstractions.contract import * from servers.chat.src.abstractions.handler import PostLoginHandlerBase +from servers.chat.src.aggregates.channel import Channel, ChannelManager +from servers.chat.src.aggregates.channel_user import ChannelUser from servers.chat.src.exceptions.channel import NoSuchChannelException from servers.chat.src.exceptions.general import ChatException, NoSuchNickException +class ChannelRequestBase(RequestBase): + channel_name: str = None + + def parse(self) -> None: + super().parse() + if self._cmd_params is None or len(self._cmd_params) < 1: + raise ChatException("Channel name is missing.") + self.channel_name = self._cmd_params[0] + + +class ChannelResponseBase(ResponseBase): + _request: ChannelRequestBase + + def __init__(self, request: RequestBase, result: ResultBase) -> None: + super().__init__(request, result) + assert isinstance(request, RequestBase) + assert isinstance(result, ResultBase) + + class ChannelHandlerBase(PostLoginHandlerBase): _channel: Channel _user: ChannelUser @@ -14,7 +35,7 @@ def _request_check(self) -> None: return super()._request_check() if self._channel is None: - self._channel = self._client.info.get_local_channel( + self._channel = ChannelManager.get_local_channel( self._request.channel_name ) if self._channel is None: @@ -28,7 +49,8 @@ def _request_check(self) -> None: if self._user is None: raise NoSuchNickException( - f"Can not find user with nickname: {self._client.info.nickname} username: {self._client.info.username}" + f"Can not find user with nickname: { + self._client.info.nick_name} user_name: {self._client.info.user_name}" ) def handle(self) -> None: @@ -43,33 +65,15 @@ def handle(self) -> None: if self.request.raw_request is None: return - publish_message() - update_channel_cache() + self.publish_message() + self.update_channel_cache() except Exception as e: self._handle_exception(e) def publish_message(self): + raise NotImplementedError() meg = RemoteMessage(self._request, self._client.get_remote_client()) self._channel.broker.publish_message(msg) def update_channel_cache(self): pass - - -class ChannelRequestBase(RequestBase): - channel_name: str - - def parse(self) -> None: - super().parse() - if self._cmd_params is None or len(self._cmd_params) < 1: - raise ChatException("Channel name is missing.") - - self.channel_name = self._cmd_params[0] - - -class ChannelResponseBase(ResponseBase): - _request: ChannelRequestBase - def __init__(self, request: RequestBase, result: ResultBase) -> None: - super().__init__(request, result) - assert isinstance(request, RequestBase) - assert isinstance(result, ResultBase) diff --git a/src/servers/chat/src/abstractions/contract.py b/src/servers/chat/src/abstractions/contract.py index 9504a4d80..c522ceece 100644 --- a/src/servers/chat/src/abstractions/contract.py +++ b/src/servers/chat/src/abstractions/contract.py @@ -1,5 +1,6 @@ import abc import library.src.abstractions.contracts +from servers.chat.src.exceptions.general import ChatException class RequestBase(library.src.abstractions.contracts.RequestBase): @@ -34,7 +35,7 @@ def parse(self) -> None: indexOfColon = rawRequest.index(":") if indexOfColon != 0 and indexOfColon != -1: - self._longParam = rawRequest[indexOfColon + 1 :] + self._longParam = rawRequest[indexOfColon + 1:] # reset the request string rawRequest = rawRequest[:indexOfColon] @@ -58,6 +59,13 @@ class ResponseBase(library.src.abstractions.contracts.ResponseBase): _result: ResultBase _request: RequestBase + def __init__(self, request: RequestBase, result: ResultBase | None) -> None: + super().__init__(request, result) + if result is not None: + assert issubclass(type(result), ResultBase) + assert issubclass(request, RequestBase) + + if __name__ == "__main__": # Example usage: diff --git a/src/servers/chat/src/abstractions/handler.py b/src/servers/chat/src/abstractions/handler.py index ef7c5a7a6..9bea518a1 100644 --- a/src/servers/chat/src/abstractions/handler.py +++ b/src/servers/chat/src/abstractions/handler.py @@ -1,11 +1,13 @@ from library.src.abstractions.client import ClientBase from servers.chat.src.abstractions.contract import RequestBase +from servers.chat.src.applications.client import Client from servers.chat.src.exceptions.general import IRCException import library.src.abstractions.handler class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase): _request: RequestBase + _client: Client def __init__(self, client: ClientBase, request: RequestBase): super().__init__(client, request) diff --git a/src/servers/chat/src/abstractions/message.py b/src/servers/chat/src/abstractions/message.py index bb1272121..948f97463 100644 --- a/src/servers/chat/src/abstractions/message.py +++ b/src/servers/chat/src/abstractions/message.py @@ -1,6 +1,7 @@ from library.src.abstractions.client import ClientBase from servers.chat.src.abstractions.channel import ChannelHandlerBase, ChannelRequestBase from servers.chat.src.abstractions.contract import ResultBase +from servers.chat.src.aggregates.channel_user import ChannelUser from servers.chat.src.enums.general import MessageType diff --git a/src/servers/chat/src/aggregates/brockers.py b/src/servers/chat/src/aggregates/brockers.py index 9ce0ed744..9eec99d12 100644 --- a/src/servers/chat/src/aggregates/brockers.py +++ b/src/servers/chat/src/aggregates/brockers.py @@ -1,11 +1,31 @@ -import threading +# import threading import redis +import redis.client from library.src.abstractions.brocker import BrockerBase from library.src.unispy_server_config import CONFIG +import websocket -class SocketIOBrocker(BrockerBase): - pass +class WebSocketBrocker(BrockerBase): + _subscriber: websocket.WebSocketApp + + def __init__(self, name: str) -> None: + super().__init__(name) + url = f"{CONFIG.backend.url}/{name}" + self._subscriber = \ + websocket.WebSocketApp(self._backend_url, + on_message=self.receive_message, + on_error=None, on_close=None) + + def subscribe(self): + if not self.is_started: + self._subscriber.run_forever(reconnect=5) + + def receive_message(self, message): + return super().receive_message(message) + + def unsubscribe(self): + self._subscriber.close() class RedisBrocker(BrockerBase): @@ -13,25 +33,22 @@ class RedisBrocker(BrockerBase): def __init__(self, name: str) -> None: super().__init__(name) - self.__redis = redis.from_url(CONFIG.redis.url) - self._subscriber = self.__redis.pubsub() - self.sub_thread = threading.Thread(target=self._subscribe) - self.sub_thread.daemon = True + self._redis = redis.from_url(CONFIG.redis.url) + self._subscriber = self._redis.pubsub() - def _subscribe(self): + def subscribe(self): self._subscriber.subscribe(self._name) for message in self._subscriber.listen(): if message["type"] == "message": print(message["data"]) def publish_message(self, message): - self.__redis.publish(self._name, message) + self._redis.publish(self._name, message) - def start(self): - self._subscribe() - # self.sub_thread.start() + def unsubscribe(self): + self._subscriber.unsubscribe() if __name__ == "__main__": b = RedisBrocker("hello") - b.start() + b.subscribe() diff --git a/src/servers/chat/src/aggregates/channel.py b/src/servers/chat/src/aggregates/channel.py index fbff27467..ff4f7fe46 100644 --- a/src/servers/chat/src/aggregates/channel.py +++ b/src/servers/chat/src/aggregates/channel.py @@ -1,6 +1,7 @@ import datetime from uuid import UUID from servers.chat.src.abstractions.contract import ResponseBase +from servers.chat.src.aggregates.brockers import RedisBrocker from servers.chat.src.aggregates.channel_user import ChannelUser from servers.chat.src.aggregates.key_value_manager import KeyValueManager from servers.chat.src.aggregates.peer_room import PeerRoom @@ -10,8 +11,13 @@ from servers.chat.src.exceptions.general import ChatException from servers.server_browser.src.v2.aggregations.server_info_builder import PEER_GROUP_LIST +MIN_CHANNEL_NAME_LENGTH = 4 + class Channel: + """ + The channel class, every channel class manage a brocker + """ server_id: UUID game_name: str name: str @@ -85,7 +91,7 @@ def creator(self) -> ChannelUser: else: return None - def __add_ban_user(self, request: ModeRequest): + def _add_ban_user(self, request: ModeRequest): assert isinstance(request, ModeRequest) if request.nick_name not in self.users: raise ChatException( @@ -95,11 +101,11 @@ def __add_ban_user(self, request: ModeRequest): self.ban_list[request.nick_name] = user - def __remove_ban_user(self, nick_name: str): + def _remove_ban_user(self, nick_name: str): if nick_name in self.ban_list: del self.ban_list[nick_name:str] - def __add_channel_operator(self, nick_name: str): + def _add_channel_operator(self, nick_name: str): if nick_name not in self.users: return @@ -107,14 +113,14 @@ def __add_channel_operator(self, nick_name: str): if not user.is_channel_creator: user.is_channel_creator = True - def __remove_channel_operator(self, nick_name: str): + def _remove_channel_operator(self, nick_name: str): if nick_name not in self.users: return user = self.users[nick_name] user.is_channel_creator = False - def __user_voice_permission(self, nick_name: str, enable: bool = True): + def _user_voice_permission(self, nick_name: str, enable: bool = True): if nick_name not in self.users: return user = self.users[nick_name] @@ -151,37 +157,23 @@ def multicast(self, sender: Client, message: ResponseBase, is_skip_snder=False): user.client.send(message) def remove_user(self, user: ChannelUser): - user.client.info.previously_joined_channel = self.name - - -# channel_manager = Manager() -# brocker_manager = Manager() -# local_channels: dict = channel_manager.dict() -# message_brokers: dict = brocker_manager.dict() -local_channels: dict = {} -message_brokers: dict = {} - - -"""The code blow is for channel manage""" - - -def get_local_channel(name: str) -> Channel: - if name in local_channels: - return local_channels[name] - + user.client.info.previously_joined_channel -def remove_local_channel(name: str) -> None: - if name in local_channels: - del local_channels[name] +class ChannelManager: + local_channels: dict = {} + """The code blow is for channel manage""" -def add_message_broker(name: str) -> object: - if name not in message_brokers: - brocker = MessageBrocker(name) - message_brokers[name] = brocker - return brocker + @staticmethod + def get_local_channel(name: str) -> Channel: + if name in ChannelManager.local_channels: + return ChannelManager.local_channels[name] + def add_local_channel(channel: Channel): + if channel.name not in ChannelManager.local_channels: + ChannelManager.local_channels[channel.name] = channel -def remove_message_brocker(name: str) -> None: - if name in message_brokers: - del message_brokers[name] + @staticmethod + def remove_local_channel(name: str) -> None: + if name in ChannelManager.local_channels: + del ChannelManager.local_channels[name] diff --git a/src/servers/chat/src/aggregates/storage_info.py b/src/servers/chat/src/aggregates/storage_info.py index 13983615b..be490bfb7 100644 --- a/src/servers/chat/src/aggregates/storage_info.py +++ b/src/servers/chat/src/aggregates/storage_info.py @@ -5,16 +5,30 @@ UUIDField, BooleanField, DictField, + DateTimeField ) class ChannelInfo(Document): game_name = StringField(required=True) channel_name = StringField(required=True) + key_values = DictField(reqired=False) + max_num_user = IntField(max_value=200) + room_name = StringField(required=False) + topic = StringField(required=False) + password = StringField(required=False) + group_id = IntField(required=False) + create_time = DateTimeField(required=True) class ChannelUser(Document): + """ + We can use one of the info below to search the channel info + """ server_id = UUIDField(binary=False, required=True) + channel_name = StringField(required=True) + user_name = StringField(required=True) + nick_name = StringField(required=True) is_voiceable = BooleanField(required=True) is_channel_operator = BooleanField(required=True) is_channel_creator = BooleanField(required=True) diff --git a/src/servers/chat/src/applications/client.py b/src/servers/chat/src/applications/client.py index 6d1724386..15dd97812 100644 --- a/src/servers/chat/src/applications/client.py +++ b/src/servers/chat/src/applications/client.py @@ -10,6 +10,7 @@ class ClientInfo: joined_channels: list["Channel"] nick_name: str gamename: str + user_name: str class Client(ClientBase): diff --git a/src/servers/chat/src/applications/switcher.py b/src/servers/chat/src/handlers/switcher.py similarity index 100% rename from src/servers/chat/src/applications/switcher.py rename to src/servers/chat/src/handlers/switcher.py diff --git a/src/servers/natneg/src/aggregations/init_packet_info.py b/src/servers/natneg/src/aggregations/init_packet_info.py index 2246a9952..b5f485bd0 100644 --- a/src/servers/natneg/src/aggregations/init_packet_info.py +++ b/src/servers/natneg/src/aggregations/init_packet_info.py @@ -10,7 +10,6 @@ from servers.natneg.src.enums.general import NatClientIndex, NatPortType import datetime - class InitPacketInfo(Document): server_id = UUIDField(binary=False, required=True) cookie = IntField(required=True) @@ -33,3 +32,7 @@ class NatFailInfo(Document): meta = {"expireAfterSeconds": int( datetime.timedelta(days=1).total_seconds())} """expire after 1 day""" + + +if __name__ == "__main__": + InitPacketInfo.objects \ No newline at end of file diff --git a/src/servers/presence_search_player/src/applications/data.py b/src/servers/presence_search_player/src/applications/data.py deleted file mode 100644 index 869deb1da..000000000 --- a/src/servers/presence_search_player/src/applications/data.py +++ /dev/null @@ -1,260 +0,0 @@ -from sqlalchemy import insert -from library.src.database.pg_orm import ( - Friends, - Profiles, - SubProfiles, - Users, - PG_SESSION, -) - - -def verify_email(email: str): - if PG_SESSION.query(Users).filter(Users.email == email).count() == 1: - return True - else: - return False - - -def verify_email_and_password(email: str, password: str): - result = ( - PG_SESSION.query(Users) - .filter(Users.email == email, Users.password == password) - .count() - ) - if result == 1: - return True - return False - - -def get_profile_id(email: str, password: str, nick_name: str, partner_id: int): - result = ( - PG_SESSION.query(Profiles, SubProfiles, Users) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter( - Users.email == email, - Users.password == password, - Profiles.nick == nick_name, - SubProfiles.partnerid == partner_id, - ) - .first() - ) - - return result - - -def add_user(user: Users): - PG_SESSION.add(user) - PG_SESSION.commit() - - -def add_profile(profile: Profiles): - PG_SESSION.add(profile) - PG_SESSION.commit() - - -def add_sub_profile(subprofile: SubProfiles): - PG_SESSION.add(subprofile) - PG_SESSION.commit() - - -def update_user(user: Users): - PG_SESSION.merge(user) - PG_SESSION.commit() - - -def update_profile(profile: Profiles): - PG_SESSION.merge(profile) - PG_SESSION.commit() - - -def update_subprofile(subprofile: SubProfiles): - PG_SESSION.merge(subprofile) - PG_SESSION.commit() - - -def get_user(email: str): - result = PG_SESSION.query(Users).filter(Users.email == email).first() - return result - - -def get_profile(user_id: int, nick_name: str) -> Profiles: - result = PG_SESSION.query(Profiles).filter( - Profiles.userid == user_id, Profiles.nick == nick_name - ) - return result - - -def get_sub_profile(profile_id: int, namespace_id: int, product_id: int) -> SubProfiles: - PG_SESSION.query(SubProfiles).filter( - SubProfiles.profileid == profile_id, - SubProfiles.namespaceid == namespace_id, - SubProfiles.namespaceid == product_id, - ) - - -def get_nick_and_unique_nick_list(email: str, password: str, namespace_id: int): - """ - return [(nick, uniquenick)] - """ - result = ( - PG_SESSION.query(Profiles.nick, SubProfiles.uniquenick) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter( - Users.email == email, - Users.password == password, - SubProfiles.namespaceid == namespace_id, - ) - .all() - ) - return result - - -def get_friend_info_list(profile_id: int, namespace_id: int, game_name: str): - """ - return [(profileid, nick, uniquenick, lastname, firstname, userid, email)] - """ - - result = ( - PG_SESSION.query( - Profiles.profileid, - Profiles.nick, - SubProfiles.uniquenick, - Profiles.lastname, - Profiles.firstname, - Users.userid, - Users.email, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter( - Profiles.profileid.in_(PG_SESSION.query(Friends.profileid == profile_id)), - SubProfiles.namespaceid == namespace_id, - SubProfiles.gamename == game_name, - ) - .all() - ) - return result - - -def get_matched_profile_info_list( - profile_ids: list[int], namespace_id: int -) -> list[tuple[int, str]]: - """ - return [(profileid,uniquenick)] - - """ - result = ( - PG_SESSION.query(SubProfiles.profileid, SubProfiles.uniquenick) - .filter( - SubProfiles.profileid.in_(profile_ids), - SubProfiles.namespaceid == namespace_id, - ) - .all() - ) - return result - - -def get_matched_info_by_nick( - nick_name: str, -) -> list[tuple[int, str, str, str, str, int]]: - result = ( - PG_SESSION.query( - Profiles.profileid, - Profiles.nick, - Profiles.firstname, - Profiles.lastname, - SubProfiles.uniquenick, - SubProfiles.namespaceid, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter(Profiles.nick == nick_name) - .all() - ) - return result - - -def get_matched_info_by_email( - email: str, -) -> list[tuple[int, str, str, str, str, int]]: - result = ( - PG_SESSION.query( - Profiles.profileid, - Profiles.nick, - Profiles.firstname, - Profiles.lastname, - SubProfiles.uniquenick, - SubProfiles.namespaceid, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter(Users.email == email) - .all() - ) - return result - - -def get_matched_info_by_nick_and_email(nick_name: str, email: str): - result = ( - PG_SESSION.query( - Profiles.profileid, - Profiles.nick, - Profiles.firstname, - Profiles.lastname, - SubProfiles.uniquenick, - SubProfiles.namespaceid, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter(Users.email == email, Profiles.nick == nick_name) - .all() - ) - return result - - -def get_matched_info_by_uniquenick_and_namespaceid( - unique_nick: str, namespace_id: int -) -> list[tuple[int, str, str, str, str, int]]: - result = ( - PG_SESSION.query( - Profiles.profileid, - Profiles.nick, - Profiles.firstname, - Profiles.lastname, - SubProfiles.uniquenick, - SubProfiles.namespaceid, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter( - SubProfiles.uniquenick == unique_nick, - SubProfiles.namespaceid == namespace_id, - ) - .all() - ) - return result - - -def is_uniquenick_exist(unique_nick: str, namespace_id: int, game_name: str): - result = ( - PG_SESSION.query(Profiles) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter( - SubProfiles.uniquenick == unique_nick, - SubProfiles.gamename == game_name, - SubProfiles.namespaceid == namespace_id, - ) - .all() - ) - - return result - - -def is_email_exist(email: str): - result = PG_SESSION.query(Users.userid).filter(Users.email == email).count() - #! According to FSW partnerid is not nessesary - if result == 0: - return False - return True diff --git a/src/servers/query_report/src/applications/client.py b/src/servers/query_report/src/applications/client.py index 52c9ba41a..febef8beb 100644 --- a/src/servers/query_report/src/applications/client.py +++ b/src/servers/query_report/src/applications/client.py @@ -1,7 +1,7 @@ from library.src.abstractions.client import ClientBase # import servers.query_report.v1 -from servers.query_report.src.v2.applications.switcher import CmdSwitcher as V2CmdSwitcher +from servers.query_report.src.v2.handlers.switcher import CmdSwitcher as V2CmdSwitcher class Client(ClientBase): diff --git a/src/servers/query_report/src/v2/applications/data.py b/src/servers/query_report/src/v2/applications/data.py deleted file mode 100644 index 8b1378917..000000000 --- a/src/servers/query_report/src/v2/applications/data.py +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/servers/query_report/src/v2/applications/switcher.py b/src/servers/query_report/src/v2/handlers/switcher.py similarity index 100% rename from src/servers/query_report/src/v2/applications/switcher.py rename to src/servers/query_report/src/v2/handlers/switcher.py diff --git a/src/servers/server_browser/src/v2/aggregations/server_info_builder.py b/src/servers/server_browser/src/v2/aggregations/server_info_builder.py index 42db03743..a41b15247 100644 --- a/src/servers/server_browser/src/v2/aggregations/server_info_builder.py +++ b/src/servers/server_browser/src/v2/aggregations/server_info_builder.py @@ -2,7 +2,7 @@ from servers.query_report.src.aggregates.game_server_info import GameServerInfo -from servers.query_report.src.applications.data import get_all_groups +from backends.gamespy.protocols.query_report.data import get_all_groups from servers.server_browser.src.v2.abstractions.contracts import QUERY_REPORT_DEFAULT_PORT from servers.server_browser.src.v2.enums.general import GameServerFlags From a18c4733a7af8550fbcb70b64c1b283a9aeb75ac Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 4 Sep 2024 11:34:36 +0800 Subject: [PATCH 100/231] refactor: added requests for backend --- .../{request_base.py => contracts.py} | 6 +- .../gamespy/protocols/chat/requests.py | 121 ++++++++++++++++-- .../gamespy/protocols/game_status/data.py} | 0 .../gamespy/protocols/game_status/requests.py | 62 +++++++++ .../protocols/game_traffic_relay/data.py} | 0 src/backends/gamespy/protocols/natneg/data.py | 2 +- .../protocols/natneg}/init_packet_info.py | 0 .../protocols/natneg}/relay_server_info.py | 0 .../gamespy/protocols/natneg/requests.py | 3 +- .../presence_connection_manager/requests.py | 2 +- .../presence_search_player/requests.py | 64 +++++---- .../protocols/query_report/requests.py | 48 +++++++ .../gamespy/protocols/server_browser/data.py} | 0 .../protocols/server_browser/requests.py | 43 +++++++ .../gamespy/protocols/web_services/data.py} | 0 .../protocols/web_services/requests.py | 90 +++++++++++++ .../gamespy/presence_connection_manager.py | 2 + src/backends/routers/gamespy/webservices.py | 12 ++ src/backends/urls.py | 20 +-- src/library/src/abstractions/handler.py | 9 +- .../chat/src/contracts/requests/general.py | 7 +- .../src/contracts/requests/profile.py | 5 +- .../query_report/src/v2/contracts/requests.py | 12 +- .../src/v2/abstractions/contracts.py | 28 ++-- .../tests => web_services}/__init__.py | 0 src/servers/web_services/src/__init__.py | 0 .../src/abstractions/contracts.py | 2 +- .../src/abstractions/handler.py | 4 +- .../src/aggregations/soap_envelop.py | 1 - .../src/applications/client.py | 2 +- .../src/exceptions/general.py | 0 .../src/handlers/switcher.py | 14 +- .../src/modules/altas/___init__.py | 0 .../src/modules/auth/abstractions/general.py | 8 +- .../src/modules/auth/contracts/requests.py | 4 +- .../src/modules/auth/contracts/responses.py | 6 +- .../src/modules/auth/contracts/results.py | 2 +- .../src/modules/auth/exceptions/general.py | 5 + .../src/modules/auth/handlers/general.py | 10 +- .../direct2game/abstractions/contracts.py | 4 +- .../direct2game/abstractions/handler.py | 0 .../modules/direct2game/contracts/requests.py | 4 +- .../direct2game/contracts/responses.py | 4 +- .../modules/direct2game/contracts/results.py | 2 +- .../modules/direct2game/handlers/general.py | 12 +- .../src/modules/sake/abstractions/general.py | 8 +- .../src/modules/sake/contracts/requests.py | 4 +- .../src/modules/sake/contracts/responses.py | 6 +- .../src/modules/sake/contracts/results.py | 2 +- .../src/modules/sake/exceptions/general.py | 5 + .../src/modules/sake/handlers/general.py | 8 +- src/servers/web_services/tests/__init__.py | 0 .../tests/altas_tests.py | 0 .../tests/auth_tests.py | 2 +- .../tests/racing_tests.py | 0 .../tests/sake_tests.py | 2 +- .../src/modules/auth/exceptions/general.py | 5 - .../src/modules/sake/exceptions/general.py | 5 - 58 files changed, 523 insertions(+), 144 deletions(-) rename src/backends/gamespy/library/abstractions/{request_base.py => contracts.py} (63%) rename src/{servers/webservices/__init__.py => backends/gamespy/protocols/game_status/data.py} (100%) create mode 100644 src/backends/gamespy/protocols/game_status/requests.py rename src/{servers/webservices/src/__init__.py => backends/gamespy/protocols/game_traffic_relay/data.py} (100%) rename src/{servers/natneg/src/aggregations => backends/gamespy/protocols/natneg}/init_packet_info.py (100%) rename src/{servers/natneg/src/aggregations => backends/gamespy/protocols/natneg}/relay_server_info.py (100%) create mode 100644 src/backends/gamespy/protocols/query_report/requests.py rename src/{servers/webservices/src/modules/altas/___init__.py => backends/gamespy/protocols/server_browser/data.py} (100%) create mode 100644 src/backends/gamespy/protocols/server_browser/requests.py rename src/{servers/webservices/src/modules/direct2game/abstractions/handler.py => backends/gamespy/protocols/web_services/data.py} (100%) create mode 100644 src/backends/gamespy/protocols/web_services/requests.py rename src/servers/{webservices/tests => web_services}/__init__.py (100%) create mode 100644 src/servers/web_services/src/__init__.py rename src/servers/{webservices => web_services}/src/abstractions/contracts.py (94%) rename src/servers/{webservices => web_services}/src/abstractions/handler.py (70%) rename src/servers/{webservices => web_services}/src/aggregations/soap_envelop.py (98%) rename src/servers/{webservices => web_services}/src/applications/client.py (97%) rename src/servers/{webservices => web_services}/src/exceptions/general.py (100%) rename src/servers/{webservices => web_services}/src/handlers/switcher.py (79%) create mode 100644 src/servers/web_services/src/modules/altas/___init__.py rename src/servers/{webservices => web_services}/src/modules/auth/abstractions/general.py (93%) rename src/servers/{webservices => web_services}/src/modules/auth/contracts/requests.py (96%) rename src/servers/{webservices => web_services}/src/modules/auth/contracts/responses.py (90%) rename src/servers/{webservices => web_services}/src/modules/auth/contracts/results.py (74%) create mode 100644 src/servers/web_services/src/modules/auth/exceptions/general.py rename src/servers/{webservices => web_services}/src/modules/auth/handlers/general.py (85%) rename src/servers/{webservices => web_services}/src/modules/direct2game/abstractions/contracts.py (63%) create mode 100644 src/servers/web_services/src/modules/direct2game/abstractions/handler.py rename src/servers/{webservices => web_services}/src/modules/direct2game/contracts/requests.py (92%) rename src/servers/{webservices => web_services}/src/modules/direct2game/contracts/responses.py (86%) rename src/servers/{webservices => web_services}/src/modules/direct2game/contracts/results.py (80%) rename src/servers/{webservices => web_services}/src/modules/direct2game/handlers/general.py (71%) rename src/servers/{webservices => web_services}/src/modules/sake/abstractions/general.py (86%) rename src/servers/{webservices => web_services}/src/modules/sake/contracts/requests.py (97%) rename src/servers/{webservices => web_services}/src/modules/sake/contracts/responses.py (76%) rename src/servers/{webservices => web_services}/src/modules/sake/contracts/results.py (83%) create mode 100644 src/servers/web_services/src/modules/sake/exceptions/general.py rename src/servers/{webservices => web_services}/src/modules/sake/handlers/general.py (68%) create mode 100644 src/servers/web_services/tests/__init__.py rename src/servers/{webservices => web_services}/tests/altas_tests.py (100%) rename src/servers/{webservices => web_services}/tests/auth_tests.py (97%) rename src/servers/{webservices => web_services}/tests/racing_tests.py (100%) rename src/servers/{webservices => web_services}/tests/sake_tests.py (98%) delete mode 100644 src/servers/webservices/src/modules/auth/exceptions/general.py delete mode 100644 src/servers/webservices/src/modules/sake/exceptions/general.py diff --git a/src/backends/gamespy/library/abstractions/request_base.py b/src/backends/gamespy/library/abstractions/contracts.py similarity index 63% rename from src/backends/gamespy/library/abstractions/request_base.py rename to src/backends/gamespy/library/abstractions/contracts.py index 82a1f0883..acc46d470 100644 --- a/src/backends/gamespy/library/abstractions/request_base.py +++ b/src/backends/gamespy/library/abstractions/contracts.py @@ -1,6 +1,6 @@ -from dataclasses import dataclass -from pydantic import BaseModel, Field,UUID4 -@dataclass +from pydantic import BaseModel, UUID4 + + class RequestBase(BaseModel): """ The ultimate request base class of all gamespy requests diff --git a/src/backends/gamespy/protocols/chat/requests.py b/src/backends/gamespy/protocols/chat/requests.py index e47014d9e..3bcfe87c4 100644 --- a/src/backends/gamespy/protocols/chat/requests.py +++ b/src/backends/gamespy/protocols/chat/requests.py @@ -1,12 +1,109 @@ -from fastapi import BaseModel - -# import abc -# from dataclasses import dataclass -# from servers.chat.enum -# @dataclass -# class RequestBase(abc.ABC): -# raw_request:str -# command_name:str - -# class LoginRequest(RequestBase): -# request_type:LoginRequestType \ No newline at end of file +import backends.gamespy.library.abstractions.contracts as lib +from servers.chat.src.enums.general import LoginRequestType, WhoRequestType + + +class RequestBase(lib.RequestBase): + raw_request: str + command_name: str + _prefix: str + _cmd_params: list + _longParam: str + + +class CdkeyRequest(RequestBase): + cdkey: str + + +class CryptRequest(RequestBase): + version_id: str + gamename: str + + +class GetUdpRelayRequest(RequestBase): + pass + + +class InviteRequest(RequestBase): + channel_name: str + nick_name: str + + +class ListLimitRequest(RequestBase): + max_number_of_channels: int + filter: str + + +class ListRequest(RequestBase): + is_searching_channel: bool + is_searching_user: bool + filter: str + + +class LoginPreAuth(RequestBase): + auth_token: str + partner_challenge: str + + +class LoginRequest(RequestBase): + request_type: LoginRequestType + namespace_id: int + nick_name: str + email: str + unique_nick: str + password_hash: str + + +class NickRequest(RequestBase): + nick_name: str + + +class PingRequest(RequestBase): + pass + + +class PongRequest(RequestBase): + echo_message: str + + +class QuitRequest(RequestBase): + reason: str + + +class RegisterNickRequest(RequestBase): + namespace_id: int + unique_nick: str + cdkey: str + + +class SetKeyRequest(RequestBase): + key_values: dict[str, str] + + +class UserIPRequest(RequestBase): + remote_ip_address: str + + +class UserRequest(RequestBase): + user_name: str + host_name: str + server_name: str + nick_name: str + name: str + + +class WhoIsRequest(RequestBase): + nick_name: str + + +class WhoRequest(RequestBase): + request_type: WhoRequestType + channel_name: str + nick_name: str + + +class GetKeyRequest(RequestBase): + is_get_all_user: bool + nick_name: str + cookie: str + unknown_cmd_param: str + keys: list[str] diff --git a/src/servers/webservices/__init__.py b/src/backends/gamespy/protocols/game_status/data.py similarity index 100% rename from src/servers/webservices/__init__.py rename to src/backends/gamespy/protocols/game_status/data.py diff --git a/src/backends/gamespy/protocols/game_status/requests.py b/src/backends/gamespy/protocols/game_status/requests.py new file mode 100644 index 000000000..f1f73b358 --- /dev/null +++ b/src/backends/gamespy/protocols/game_status/requests.py @@ -0,0 +1,62 @@ +from typing import Optional +import backends.gamespy.library.abstractions.contracts as lib +from servers.game_status.src.enums.general import AuthMethod, PersistStorageType + + +class RequestBase(lib.RequestBase): + command_name: str + raw_request: str + local_id: Optional[int] + request_dict: dict[str, str] + + +class AuthGameRequest(RequestBase): + game_name: str + + +class AuthPlayerRequest(RequestBase): + auth_type: AuthMethod + profile_id: int + + auth_token: str + response: str + cdkey_hash: str + nick: str + + +class GetPlayerDataRequest(RequestBase): + profile_id: str + storage_type: PersistStorageType + data_index: int + is_get_all_data: bool = False + keys: list[str] + + +class GetProfileIdRequest(RequestBase): + nick: str + keyhash: str + + +class NewGameRequest(RequestBase): + is_client_local_storage_available: bool + challenge: str + connection_id: int + session_key: str + + +class SetPlayerDataRequest(RequestBase): + profile_id: int + storage_type: PersistStorageType + data_index: int + length: int + report: str + data: str + + +class UpdateGameRequest(RequestBase): + connection_id: int + is_done: bool + is_client_local_storage_available: bool + game_data: str + game_data_dict: dict[str, str] + session_key: str diff --git a/src/servers/webservices/src/__init__.py b/src/backends/gamespy/protocols/game_traffic_relay/data.py similarity index 100% rename from src/servers/webservices/src/__init__.py rename to src/backends/gamespy/protocols/game_traffic_relay/data.py diff --git a/src/backends/gamespy/protocols/natneg/data.py b/src/backends/gamespy/protocols/natneg/data.py index a62151f11..c72e85105 100644 --- a/src/backends/gamespy/protocols/natneg/data.py +++ b/src/backends/gamespy/protocols/natneg/data.py @@ -1,6 +1,6 @@ # from servers.natneg.contracts.requests import InitRequest from backends.gamespy.protocols.natneg.requests import InitRequest -from servers.natneg.src.aggregations.init_packet_info import InitPacketInfo +from backends.gamespy.protocols.natneg.init_packet_info import InitPacketInfo from mongoengine import QuerySet def store_init_packet(request: InitRequest) -> None: diff --git a/src/servers/natneg/src/aggregations/init_packet_info.py b/src/backends/gamespy/protocols/natneg/init_packet_info.py similarity index 100% rename from src/servers/natneg/src/aggregations/init_packet_info.py rename to src/backends/gamespy/protocols/natneg/init_packet_info.py diff --git a/src/servers/natneg/src/aggregations/relay_server_info.py b/src/backends/gamespy/protocols/natneg/relay_server_info.py similarity index 100% rename from src/servers/natneg/src/aggregations/relay_server_info.py rename to src/backends/gamespy/protocols/natneg/relay_server_info.py diff --git a/src/backends/gamespy/protocols/natneg/requests.py b/src/backends/gamespy/protocols/natneg/requests.py index a5e22bcd8..a71f4e729 100644 --- a/src/backends/gamespy/protocols/natneg/requests.py +++ b/src/backends/gamespy/protocols/natneg/requests.py @@ -1,4 +1,3 @@ -from dataclasses import dataclass from servers.natneg.src.enums.general import ( NatClientIndex, NatPortMappingScheme, @@ -9,7 +8,7 @@ ) from typing import Union -import backends.gamespy.library.abstractions.request_base as lib +import backends.gamespy.library.abstractions.contracts as lib class RequestBase(lib.RequestBase): diff --git a/src/backends/gamespy/protocols/presence_connection_manager/requests.py b/src/backends/gamespy/protocols/presence_connection_manager/requests.py index 5c55ba95d..4d0046522 100644 --- a/src/backends/gamespy/protocols/presence_connection_manager/requests.py +++ b/src/backends/gamespy/protocols/presence_connection_manager/requests.py @@ -11,7 +11,7 @@ SdkRevisionType, ) -import backends.gamespy.library.abstractions.request_base as lib +import backends.gamespy.library.abstractions.contracts as lib class RequestBase(BaseModel): diff --git a/src/backends/gamespy/protocols/presence_search_player/requests.py b/src/backends/gamespy/protocols/presence_search_player/requests.py index b8a9eaa2f..09924bbc3 100644 --- a/src/backends/gamespy/protocols/presence_search_player/requests.py +++ b/src/backends/gamespy/protocols/presence_search_player/requests.py @@ -1,14 +1,16 @@ -import abc -from dataclasses import dataclass -from backends.gamespy.library.abstractions.request_base import RequestBase as RB -from servers.presence_connection_manager.src.enums.general import LoginType, SdkRevisionType +from typing import Optional +from backends.gamespy.library.abstractions.contracts import RequestBase as RB +from servers.presence_search_player.src.enums.general import SearchType class RequestBase(RB): - operation_id: int = None + operation_id: int +# general # we just need to recreate the requests and just put the property inside it. The result we can use the results inside servers. + + class CheckRequest(RequestBase): nick: str password: str @@ -39,22 +41,40 @@ class NicksRequest(RequestBase): is_require_uniquenicks: bool -class LoginRequest(RequestBase): - user_challenge: str - response: str - unique_nick: str - user_data: str - namespace_id: int - auth_token: str - nick: str - email: str - product_id: int - type: LoginType - sdk_revision_type: SdkRevisionType - game_port: int - user_id: int +class OthersListRequest(RequestBase): + profile_ids: list[int] = [] + namespace_id: int = 0 + + +class OthersRequest(RequestBase): + profile_id: Optional[int] + game_name: int + + +class SearchRequest(RequestBase): + skip_num: int + request_type: SearchType + game_name: str profile_id: int partner_id: int - game_name: int - quiet_mode_flags: int - firewall: bool + email: str + nick: str + uniquenick: str + session_key: str + firstname: str + lastname: str + icquin: str + + +class SearchUniqueRequest(RequestBase): + uniquenick: str + namespace_ids: list[int] + + +class UniqueSearchRequest(RequestBase): + preferred_nick: str + game_name: str + + +class ValidRequest(RequestBase): + email: str diff --git a/src/backends/gamespy/protocols/query_report/requests.py b/src/backends/gamespy/protocols/query_report/requests.py new file mode 100644 index 000000000..ea24e64a1 --- /dev/null +++ b/src/backends/gamespy/protocols/query_report/requests.py @@ -0,0 +1,48 @@ + +from pydantic import UUID4 +import backends.gamespy.library.abstractions.contracts as lib + +from servers.query_report.src.v2.enums.general import GameServerStatus, RequestType + + +class RequestBase(lib.RequestBase): + instant_key: int + command_name: RequestType + raw_request: bytes + + +class AvaliableRequest(RequestBase): + pass + + +class ChallengeRequest(RequestBase): + pass + + +class ClientMessageAckRequest(RequestBase): + pass + + +class ClientMessageRequest(RequestBase): + server_browser_sender_id: UUID4 + natneg_message: list[int] + target_ip_address: str + target_port: str + message_key: int + cookie: int + + +class HeartBeatRequest(RequestBase): + server_data: dict[str, str] + player_data: list[dict[str, str]] + team_data: list[dict[str, str]] + server_status: GameServerStatus + group_id: int + + +class EchoRequest(RequestBase): + pass + + +class KeepAliveRequest(RequestBase): + pass diff --git a/src/servers/webservices/src/modules/altas/___init__.py b/src/backends/gamespy/protocols/server_browser/data.py similarity index 100% rename from src/servers/webservices/src/modules/altas/___init__.py rename to src/backends/gamespy/protocols/server_browser/data.py diff --git a/src/backends/gamespy/protocols/server_browser/requests.py b/src/backends/gamespy/protocols/server_browser/requests.py new file mode 100644 index 000000000..bbeb50da5 --- /dev/null +++ b/src/backends/gamespy/protocols/server_browser/requests.py @@ -0,0 +1,43 @@ +from typing import List, Optional +import backends.gamespy.library.abstractions.contracts as lib +from servers.server_browser.src.v2.enums.general import RequestType, ServerListUpdateOption + + +class RequestBase(lib.RequestBase): + request_length: int + raw_request: bytes + command_name: RequestType + + +class ServerListUpdateOptionRequestBase(RequestBase): + source_ip: str + request_version: Optional[int] = None + protocol_version: Optional[int] = None + encoding_version: Optional[int] = None + game_version: Optional[int] = None + query_options: Optional[int] = None + dev_game_name: Optional[str] = None + game_name: Optional[str] = None + client_challenge: Optional[str] = None + update_option: Optional[ServerListUpdateOption] = None + keys: Optional[List[str]] = None + filter: Optional[str] = None + max_servers: Optional[int] = None + + +class ServerListRequest(ServerListUpdateOptionRequestBase): + pass + + +class AdHocRequestBase(RequestBase): + game_server_public_ip: list[int] + game_server_public_port: list[int] + + +class SendMessageRequest(AdHocRequestBase): + prefix_message: list[int] + client_message: list[int] + + +class ServerInfoRequest(AdHocRequestBase): + pass diff --git a/src/servers/webservices/src/modules/direct2game/abstractions/handler.py b/src/backends/gamespy/protocols/web_services/data.py similarity index 100% rename from src/servers/webservices/src/modules/direct2game/abstractions/handler.py rename to src/backends/gamespy/protocols/web_services/data.py diff --git a/src/backends/gamespy/protocols/web_services/requests.py b/src/backends/gamespy/protocols/web_services/requests.py new file mode 100644 index 000000000..50d45ec06 --- /dev/null +++ b/src/backends/gamespy/protocols/web_services/requests.py @@ -0,0 +1,90 @@ +from pydantic import BaseModel +import backends.gamespy.library.abstractions.contracts as lib + + +class RequestBase(lib.RequestBase): + game_id: int + secret_key: str + login_ticket: str + table_id: str + + +class CreateRecordData(BaseModel): + name: str + type: str + value: str + + +class CreateRecordRequest(RequestBase): + values: list[CreateRecordData] + + +class DeleteRecordRequest(RequestBase): + record_id: int + + +class GetMyRecordsData(BaseModel): + name: str + value: str + + +class GetMyRecordsRequest(RequestBase): + fields: list[GetMyRecordsData] + + +class GetRandomRecordsData(BaseModel): + name: str + value: str + + +class GetRandomRecordsRequest(RequestBase): + max: int + fields: list[GetRandomRecordsData] + + +class GetRecordLimitRequest(RequestBase): + pass + + +class GetSpecificRecordsData(BaseModel): + name: str + type: str + + +class GetSpecificRecordsRequest(RequestBase): + + record_ids: list[GetSpecificRecordsData] + fields: list[GetSpecificRecordsData] + + + +class RateRecordRequest(RequestBase): + record_id: str + rating: str + + +class SearchForRecordsData(BaseModel): + name: str + type: str + + +class SearchForRecordsRequest(RequestBase): + filter: str + sort: str + offset: str + max: str + surrounding: str + owner_ids: str + cache_flag: str + fields: list[SearchForRecordsData] + + +class UpdateRecordData(BaseModel): + name: str + type: str + value: str + + +class UpdateRecordRequest(RequestBase): + record_id: str + values: list[UpdateRecordData] diff --git a/src/backends/routers/gamespy/presence_connection_manager.py b/src/backends/routers/gamespy/presence_connection_manager.py index 98ccb88f7..ef5a70f03 100644 --- a/src/backends/routers/gamespy/presence_connection_manager.py +++ b/src/backends/routers/gamespy/presence_connection_manager.py @@ -4,6 +4,8 @@ from backends.urls import * import uvicorn + + app = FastAPI() diff --git a/src/backends/routers/gamespy/webservices.py b/src/backends/routers/gamespy/webservices.py index b594b9a81..1123b9495 100644 --- a/src/backends/routers/gamespy/webservices.py +++ b/src/backends/routers/gamespy/webservices.py @@ -1 +1,13 @@ from fastapi import FastAPI + +from backends.urls import GAMESPY_PREFIX, WEB_SERVICES + +URL = f"{GAMESPY_PREFIX}/{WEB_SERVICES}" +app = FastAPI() + +# SAKE services + + +@app.post(f"/{URL}/CreateRecord") +def create_record(request): + raise NotImplementedError() diff --git a/src/backends/urls.py b/src/backends/urls.py index f7eac3c60..f9a8167aa 100644 --- a/src/backends/urls.py +++ b/src/backends/urls.py @@ -1,9 +1,11 @@ -PRESENCE_CONNECTION_MANAGER = "PCM" -PRESENCE_SEARCH_PLAYER = "PSP" -SERVER_BROWSER_V1 = "SB_V1" -SERVER_BROWSER_V2 = "SB_V2" -QUERY_REPORT = "QR" -NATNEG = "NN" -GAMESTATUS = "GS" -CHAT = "Chat" -SOAP_SERVICE = "SOAP" +GAMESPY_PREFIX = "GameSpy" + +PRESENCE_CONNECTION_MANAGER = f"{GAMESPY_PREFIX}/PCM/" +PRESENCE_SEARCH_PLAYER = f"{GAMESPY_PREFIX}/PSP/" +SERVER_BROWSER_V1 = f"{GAMESPY_PREFIX}/SB_V1/" +SERVER_BROWSER_V2 = f"{GAMESPY_PREFIX}/SB_V2/" +QUERY_REPORT = f"{GAMESPY_PREFIX}/QR/" +NATNEG = f"{GAMESPY_PREFIX}/NN/" +GAMESTATUS = f"{GAMESPY_PREFIX}/GS/" +CHAT = f"{GAMESPY_PREFIX}/Chat/" +WEB_SERVICES = f"{GAMESPY_PREFIX}/WEB/" diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index 615bf4e9a..e495f4fdf 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -71,8 +71,13 @@ def _data_operate(self) -> None: # default use restapi to access to our backend service # get the http response and create it with this type - url = f"{ - CONFIG.backend.url}/{self._client.server_config.server_name}/{self.__class__.__name__}/" + # http://127.0.0.1:8080/gamespy/pcm/login/ + + # fmt: off + + url = f"{CONFIG.backend.url}/gamespy/{self._client.server_config.server_name}/{self.__class__.__name__[:-len("Handler")]}/".lower() + + # fmt: on data = self._request.to_serializable_dict() data["server_id"] = str(self._client.server_config.server_id) diff --git a/src/servers/chat/src/contracts/requests/general.py b/src/servers/chat/src/contracts/requests/general.py index a2a36c160..3086f9dcd 100644 --- a/src/servers/chat/src/contracts/requests/general.py +++ b/src/servers/chat/src/contracts/requests/general.py @@ -119,7 +119,7 @@ def parse(self): profile_nick_index = self._longParam.index("@") self.nick_name = self._longParam[0:profile_nick_index] - self.email = self._longParam[profile_nick_index + 1 :] + self.email = self._longParam[profile_nick_index + 1:] return self.request_type = LoginRequestType.UNIQUE_NICK_LOGIN @@ -145,7 +145,8 @@ def parse(self): raise NickNameInUseException( self.nick_name, self.nick_name, - f"The nick name: {self.nick_name} contains invalid character.", + f"The nick name: { + self.nick_name} contains invalid character.", ) @@ -187,7 +188,7 @@ def parse(self): class SetKeyRequest(RequestBase): - key_values: Dict[str, str] + key_values: dict[str, str] def parse(self): super().parse() diff --git a/src/servers/presence_connection_manager/src/contracts/requests/profile.py b/src/servers/presence_connection_manager/src/contracts/requests/profile.py index 28912313b..ddac02992 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests/profile.py +++ b/src/servers/presence_connection_manager/src/contracts/requests/profile.py @@ -154,7 +154,8 @@ def parse(self): if not self.request_dict["publicmask"].isdigit(): raise GPParseException("publicmask format is incorrect") self.has_public_mask_flag = True - self.public_mask = PublicMasks(int(self.request_dict["publicmask"])) + self.public_mask = PublicMasks( + int(self.request_dict["publicmask"])) if "sesskey" not in self.request_dict: raise GPParseException("sesskey is missing") @@ -221,7 +222,7 @@ def parse(self): self.uniquenick = self.request_dict["uniquenick"] -class UpdateUiRequest(RequestBase): +class UpdateUserInfoRequest(RequestBase): cpubrandid: Optional[str] = None cpuspeed: Optional[str] = None memory: Optional[str] = None diff --git a/src/servers/query_report/src/v2/contracts/requests.py b/src/servers/query_report/src/v2/contracts/requests.py index 8b0d6dfd3..fff8bcb0e 100644 --- a/src/servers/query_report/src/v2/contracts/requests.py +++ b/src/servers/query_report/src/v2/contracts/requests.py @@ -39,7 +39,7 @@ class ClientMessageRequest(RequestBase): message_key: int @property - def cookie(self): + def cookie(self) -> int: return int.from_bytes(self.natneg_message[6:10], "little") @@ -67,12 +67,12 @@ def parse(self): self.parse_server_data(server_data_str) player_data_str = self.data_partition[ - player_pos - 1 : player_pos - 1 + player_length - 2 + player_pos - 1: player_pos - 1 + player_length - 2 ] self.parse_player_data(player_data_str) team_data_str = self.data_partition[ - team_pos - 1 : team_pos - 1 + team_length + team_pos - 1: team_pos - 1 + team_length ] self.parse_team_data(team_data_str) @@ -82,7 +82,7 @@ def parse(self): server_data_str = self.data_partition[: player_pos - 4] self.parse_server_data(server_data_str) - player_data_str = self.data_partition[player_pos - 1 :] + player_data_str = self.data_partition[player_pos - 1:] self.parse_player_data(player_data_str) elif player_pos == -1 and team_pos == -1: @@ -132,7 +132,7 @@ def parse_player_data(self, player_data_str: str): key_str = player_data_str[:index_of_key] keys = key_str.split("\0") - values_str = player_data_str[index_of_key + 2 :] + values_str = player_data_str[index_of_key + 2:] values = values_str.split("\0") for player_index in range(player_count): @@ -158,7 +158,7 @@ def parse_team_data(self, team_data_str: str): key_str = team_data_str[:end_key_index] keys = key_str.split("\0") - value_str = team_data_str[end_key_index + 2 :] + value_str = team_data_str[end_key_index + 2:] values = value_str.split("\0") for team_index in range(team_count): diff --git a/src/servers/server_browser/src/v2/abstractions/contracts.py b/src/servers/server_browser/src/v2/abstractions/contracts.py index 0bbefd704..65e0e9b63 100644 --- a/src/servers/server_browser/src/v2/abstractions/contracts.py +++ b/src/servers/server_browser/src/v2/abstractions/contracts.py @@ -1,7 +1,6 @@ from socket import inet_ntoa from typing import List, Optional import library.src.abstractions.contracts -import abc from library.src.extentions.encoding import get_bytes from servers.server_browser.src.v2.aggregations.encryption import SERVER_CHALLENGE @@ -47,20 +46,19 @@ def __init__(self, request: RequestBase, result: ResultBase) -> None: class ServerListUpdateOptionRequestBase(RequestBase): - def __init__(self): - self.request_version: Optional[int] = None - self.protocol_version: Optional[int] = None - self.encoding_version: Optional[int] = None - self.game_version: Optional[int] = None - self.query_options: Optional[int] = None - self.dev_game_name: Optional[str] = None - self.game_name: Optional[str] = None - self.client_challenge: Optional[str] = None - self.update_option: Optional[ServerListUpdateOption] = None - self.keys: Optional[List[str]] = None - self.filter: Optional[str] = None - self.source_ip: str - self.max_servers: Optional[int] = None + request_version: Optional[int] = None + protocol_version: Optional[int] = None + encoding_version: Optional[int] = None + game_version: Optional[int] = None + query_options: Optional[int] = None + dev_game_name: Optional[str] = None + game_name: Optional[str] = None + client_challenge: Optional[str] = None + update_option: Optional[ServerListUpdateOption] = None + keys: Optional[List[str]] = None + filter: Optional[str] = None + source_ip: str + max_servers: Optional[int] = None def __init__(self, raw_request: bytes): assert isinstance(raw_request, bytes) diff --git a/src/servers/webservices/tests/__init__.py b/src/servers/web_services/__init__.py similarity index 100% rename from src/servers/webservices/tests/__init__.py rename to src/servers/web_services/__init__.py diff --git a/src/servers/web_services/src/__init__.py b/src/servers/web_services/src/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/src/abstractions/contracts.py b/src/servers/web_services/src/abstractions/contracts.py similarity index 94% rename from src/servers/webservices/src/abstractions/contracts.py rename to src/servers/web_services/src/abstractions/contracts.py index 574e961d9..b495c48d8 100644 --- a/src/servers/webservices/src/abstractions/contracts.py +++ b/src/servers/web_services/src/abstractions/contracts.py @@ -2,7 +2,7 @@ import xml.etree.ElementTree as ET from library.src.exceptions.general import UniSpyException -from servers.webservices.src.aggregations.soap_envelop import SoapEnvelop +from servers.web_services.src.aggregations.soap_envelop import SoapEnvelop class RequestBase(lib.RequestBase): diff --git a/src/servers/webservices/src/abstractions/handler.py b/src/servers/web_services/src/abstractions/handler.py similarity index 70% rename from src/servers/webservices/src/abstractions/handler.py rename to src/servers/web_services/src/abstractions/handler.py index 0be598b93..974ff3697 100644 --- a/src/servers/webservices/src/abstractions/handler.py +++ b/src/servers/web_services/src/abstractions/handler.py @@ -1,6 +1,6 @@ import library.src.abstractions.handler as lib -from servers.webservices.src.applications.client import Client -from servers.webservices.src.abstractions.contracts import RequestBase +from servers.web_services.src.applications.client import Client +from servers.web_services.src.abstractions.contracts import RequestBase class CmdHandlerBase(lib.CmdHandlerBase): diff --git a/src/servers/webservices/src/aggregations/soap_envelop.py b/src/servers/web_services/src/aggregations/soap_envelop.py similarity index 98% rename from src/servers/webservices/src/aggregations/soap_envelop.py rename to src/servers/web_services/src/aggregations/soap_envelop.py index 2c233258f..84381236f 100644 --- a/src/servers/webservices/src/aggregations/soap_envelop.py +++ b/src/servers/web_services/src/aggregations/soap_envelop.py @@ -1,4 +1,3 @@ -from typing import Optional import xml.etree.ElementTree as ET diff --git a/src/servers/webservices/src/applications/client.py b/src/servers/web_services/src/applications/client.py similarity index 97% rename from src/servers/webservices/src/applications/client.py rename to src/servers/web_services/src/applications/client.py index b8991c13e..975866a85 100644 --- a/src/servers/webservices/src/applications/client.py +++ b/src/servers/web_services/src/applications/client.py @@ -31,5 +31,5 @@ class Client(ClientBase): info: ClientInfo def create_switcher(self, buffer: HttpRequest) -> SwitcherBase: - from servers.webservices.src.handlers.switcher import Switcher + from servers.web_services.src.handlers.switcher import Switcher return Switcher(self, buffer) diff --git a/src/servers/webservices/src/exceptions/general.py b/src/servers/web_services/src/exceptions/general.py similarity index 100% rename from src/servers/webservices/src/exceptions/general.py rename to src/servers/web_services/src/exceptions/general.py diff --git a/src/servers/webservices/src/handlers/switcher.py b/src/servers/web_services/src/handlers/switcher.py similarity index 79% rename from src/servers/webservices/src/handlers/switcher.py rename to src/servers/web_services/src/handlers/switcher.py index abee45255..843481598 100644 --- a/src/servers/webservices/src/handlers/switcher.py +++ b/src/servers/web_services/src/handlers/switcher.py @@ -2,13 +2,13 @@ from library.src.abstractions.switcher import SwitcherBase import xml.etree.ElementTree as ET -from servers.webservices.src.exceptions.general import WebExceptions -from servers.webservices.src.modules.auth.contracts.requests import LoginProfileRequest, LoginProfileWithGameIdRequest, LoginRemoteAuthRequest, LoginRemoteAuthWithGameIdRequest, LoginUniqueNickRequest, LoginUniqueNickWithGameIdRequest -from servers.webservices.src.modules.auth.handlers.general import LoginProfileHandler, LoginProfileWithGameIdHandler, LoginRemoteAuthHandler, LoginRemoteAuthWithGameIdHandler, LoginUniqueNickHandler, LoginUniqueNickWithGameIdHandler -from servers.webservices.src.modules.direct2game.contracts.requests import GetPurchaseHistoryRequest, GetStoreAvailabilityRequest -from servers.webservices.src.modules.direct2game.handlers.general import GetPurchaseHistoryHandler, GetStoreAvailabilityHandler -from servers.webservices.src.modules.sake.contracts.requests import CreateRecordRequest, GetMyRecordsRequest, SearchForRecordsRequest -from servers.webservices.src.modules.sake.handlers.general import CreateRecordHandler, GetMyRecordsHandler, SearchForRecordsHandler +from servers.web_services.src.exceptions.general import WebExceptions +from servers.web_services.src.modules.auth.contracts.requests import LoginProfileRequest, LoginProfileWithGameIdRequest, LoginRemoteAuthRequest, LoginRemoteAuthWithGameIdRequest, LoginUniqueNickRequest, LoginUniqueNickWithGameIdRequest +from servers.web_services.src.modules.auth.handlers.general import LoginProfileHandler, LoginProfileWithGameIdHandler, LoginRemoteAuthHandler, LoginRemoteAuthWithGameIdHandler, LoginUniqueNickHandler, LoginUniqueNickWithGameIdHandler +from servers.web_services.src.modules.direct2game.contracts.requests import GetPurchaseHistoryRequest, GetStoreAvailabilityRequest +from servers.web_services.src.modules.direct2game.handlers.general import GetPurchaseHistoryHandler, GetStoreAvailabilityHandler +from servers.web_services.src.modules.sake.contracts.requests import CreateRecordRequest, GetMyRecordsRequest, SearchForRecordsRequest +from servers.web_services.src.modules.sake.handlers.general import CreateRecordHandler, GetMyRecordsHandler, SearchForRecordsHandler class Switcher(SwitcherBase): diff --git a/src/servers/web_services/src/modules/altas/___init__.py b/src/servers/web_services/src/modules/altas/___init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/src/modules/auth/abstractions/general.py b/src/servers/web_services/src/modules/auth/abstractions/general.py similarity index 93% rename from src/servers/webservices/src/modules/auth/abstractions/general.py rename to src/servers/web_services/src/modules/auth/abstractions/general.py index 6b245aff4..a843be40b 100644 --- a/src/servers/webservices/src/modules/auth/abstractions/general.py +++ b/src/servers/web_services/src/modules/auth/abstractions/general.py @@ -1,8 +1,8 @@ import hashlib -import servers.webservices.src.abstractions.contracts as lib -from servers.webservices.src.aggregations.soap_envelop import SoapEnvelop -from servers.webservices.src.applications.client import ClientInfo -from servers.webservices.src.modules.auth.exceptions.general import AuthException +import servers.web_services.src.abstractions.contracts as lib +from servers.web_services.src.aggregations.soap_envelop import SoapEnvelop +from servers.web_services.src.applications.client import ClientInfo +from servers.web_services.src.modules.auth.exceptions.general import AuthException import datetime NAMESPACE = "http://gamespy.net/AuthService/" diff --git a/src/servers/webservices/src/modules/auth/contracts/requests.py b/src/servers/web_services/src/modules/auth/contracts/requests.py similarity index 96% rename from src/servers/webservices/src/modules/auth/contracts/requests.py rename to src/servers/web_services/src/modules/auth/contracts/requests.py index 8ee99debc..189c4d34b 100644 --- a/src/servers/webservices/src/modules/auth/contracts/requests.py +++ b/src/servers/web_services/src/modules/auth/contracts/requests.py @@ -1,8 +1,8 @@ -from servers.webservices.src.modules.auth.abstractions.general import ( +from servers.web_services.src.modules.auth.abstractions.general import ( NAMESPACE, LoginRequestBase, ) -from servers.webservices.src.modules.auth.exceptions.general import AuthException +from servers.web_services.src.modules.auth.exceptions.general import AuthException class LoginProfileRequest(LoginRequestBase): diff --git a/src/servers/webservices/src/modules/auth/contracts/responses.py b/src/servers/web_services/src/modules/auth/contracts/responses.py similarity index 90% rename from src/servers/webservices/src/modules/auth/contracts/responses.py rename to src/servers/web_services/src/modules/auth/contracts/responses.py index 90584c7a6..d22f5df2f 100644 --- a/src/servers/webservices/src/modules/auth/contracts/responses.py +++ b/src/servers/web_services/src/modules/auth/contracts/responses.py @@ -1,9 +1,9 @@ -from servers.webservices.src.modules.auth.abstractions.general import LoginResponseBase -from servers.webservices.src.modules.auth.contracts.requests import ( +from servers.web_services.src.modules.auth.abstractions.general import LoginResponseBase +from servers.web_services.src.modules.auth.contracts.requests import ( LoginProfileRequest, LoginProfileWithGameIdRequest, ) -from servers.webservices.src.modules.auth.contracts.results import ( +from servers.web_services.src.modules.auth.contracts.results import ( LoginProfileResult, LoginPs3CertResult, ) diff --git a/src/servers/webservices/src/modules/auth/contracts/results.py b/src/servers/web_services/src/modules/auth/contracts/results.py similarity index 74% rename from src/servers/webservices/src/modules/auth/contracts/results.py rename to src/servers/web_services/src/modules/auth/contracts/results.py index 6d1181f16..ac289868b 100644 --- a/src/servers/webservices/src/modules/auth/contracts/results.py +++ b/src/servers/web_services/src/modules/auth/contracts/results.py @@ -1,4 +1,4 @@ -from servers.webservices.src.modules.auth.abstractions.general import LoginResultBase +from servers.web_services.src.modules.auth.abstractions.general import LoginResultBase class LoginProfileResult(LoginResultBase): diff --git a/src/servers/web_services/src/modules/auth/exceptions/general.py b/src/servers/web_services/src/modules/auth/exceptions/general.py new file mode 100644 index 000000000..79233b574 --- /dev/null +++ b/src/servers/web_services/src/modules/auth/exceptions/general.py @@ -0,0 +1,5 @@ +from servers.web_services.src.exceptions.general import WebExceptions + + +class AuthException(WebExceptions): + pass diff --git a/src/servers/webservices/src/modules/auth/handlers/general.py b/src/servers/web_services/src/modules/auth/handlers/general.py similarity index 85% rename from src/servers/webservices/src/modules/auth/handlers/general.py rename to src/servers/web_services/src/modules/auth/handlers/general.py index 6aaaded33..83de30723 100644 --- a/src/servers/webservices/src/modules/auth/handlers/general.py +++ b/src/servers/web_services/src/modules/auth/handlers/general.py @@ -1,6 +1,6 @@ -from servers.webservices.src.abstractions.handler import CmdHandlerBase -from servers.webservices.src.modules.auth.abstractions.general import LoginResultBase -from servers.webservices.src.modules.auth.contracts.requests import ( +from servers.web_services.src.abstractions.handler import CmdHandlerBase +from servers.web_services.src.modules.auth.abstractions.general import LoginResultBase +from servers.web_services.src.modules.auth.contracts.requests import ( LoginProfileRequest, LoginProfileWithGameIdRequest, LoginPs3CertRequest, @@ -10,7 +10,7 @@ LoginUniqueNickRequest, LoginUniqueNickWithGameIdRequest, ) -from servers.webservices.src.modules.auth.contracts.responses import ( +from servers.web_services.src.modules.auth.contracts.responses import ( LoginProfileResponse, LoginProfileWithGameIdResponse, LoginRemoteAuthResponse, @@ -18,7 +18,7 @@ LoginUniqueNickResponse, LoginUniqueNickWithGameIdResponse, ) -from servers.webservices.src.modules.auth.contracts.results import ( +from servers.web_services.src.modules.auth.contracts.results import ( LoginProfileResult, LoginPs3CertResult, LoginRemoteAuthResult, diff --git a/src/servers/webservices/src/modules/direct2game/abstractions/contracts.py b/src/servers/web_services/src/modules/direct2game/abstractions/contracts.py similarity index 63% rename from src/servers/webservices/src/modules/direct2game/abstractions/contracts.py rename to src/servers/web_services/src/modules/direct2game/abstractions/contracts.py index efae5a815..3d0e7431c 100644 --- a/src/servers/webservices/src/modules/direct2game/abstractions/contracts.py +++ b/src/servers/web_services/src/modules/direct2game/abstractions/contracts.py @@ -1,5 +1,5 @@ -import servers.webservices.src.abstractions.contracts as lib -from servers.webservices.src.aggregations.soap_envelop import SoapEnvelop +import servers.web_services.src.abstractions.contracts as lib +from servers.web_services.src.aggregations.soap_envelop import SoapEnvelop NAMESPACE = "http://gamespy.net/commerce/" diff --git a/src/servers/web_services/src/modules/direct2game/abstractions/handler.py b/src/servers/web_services/src/modules/direct2game/abstractions/handler.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/src/modules/direct2game/contracts/requests.py b/src/servers/web_services/src/modules/direct2game/contracts/requests.py similarity index 92% rename from src/servers/webservices/src/modules/direct2game/contracts/requests.py rename to src/servers/web_services/src/modules/direct2game/contracts/requests.py index 8740957e8..c6a5f40fd 100644 --- a/src/servers/webservices/src/modules/direct2game/contracts/requests.py +++ b/src/servers/web_services/src/modules/direct2game/contracts/requests.py @@ -1,5 +1,5 @@ -from servers.webservices.src.exceptions.general import WebExceptions -from servers.webservices.src.modules.direct2game.abstractions.contracts import ( +from servers.web_services.src.exceptions.general import WebExceptions +from servers.web_services.src.modules.direct2game.abstractions.contracts import ( NAMESPACE, RequestBase, ) diff --git a/src/servers/webservices/src/modules/direct2game/contracts/responses.py b/src/servers/web_services/src/modules/direct2game/contracts/responses.py similarity index 86% rename from src/servers/webservices/src/modules/direct2game/contracts/responses.py rename to src/servers/web_services/src/modules/direct2game/contracts/responses.py index 240e9f627..0d015a58f 100644 --- a/src/servers/webservices/src/modules/direct2game/contracts/responses.py +++ b/src/servers/web_services/src/modules/direct2game/contracts/responses.py @@ -1,5 +1,5 @@ -from servers.webservices.src.modules.direct2game.abstractions.contracts import ResponseBase -from servers.webservices.src.modules.direct2game.contracts.results import ( +from servers.web_services.src.modules.direct2game.abstractions.contracts import ResponseBase +from servers.web_services.src.modules.direct2game.contracts.results import ( GetPurchaseHistoryResult, GetStoreAvailabilityResult, ) diff --git a/src/servers/webservices/src/modules/direct2game/contracts/results.py b/src/servers/web_services/src/modules/direct2game/contracts/results.py similarity index 80% rename from src/servers/webservices/src/modules/direct2game/contracts/results.py rename to src/servers/web_services/src/modules/direct2game/contracts/results.py index 7f4ce7b60..5215630ee 100644 --- a/src/servers/webservices/src/modules/direct2game/contracts/results.py +++ b/src/servers/web_services/src/modules/direct2game/contracts/results.py @@ -1,6 +1,6 @@ from enum import IntEnum -from servers.webservices.src.modules.direct2game.abstractions.contracts import ResultBase +from servers.web_services.src.modules.direct2game.abstractions.contracts import ResultBase class GetPurchaseHistoryResult(ResultBase): diff --git a/src/servers/webservices/src/modules/direct2game/handlers/general.py b/src/servers/web_services/src/modules/direct2game/handlers/general.py similarity index 71% rename from src/servers/webservices/src/modules/direct2game/handlers/general.py rename to src/servers/web_services/src/modules/direct2game/handlers/general.py index e35ecd633..581bc04bd 100644 --- a/src/servers/webservices/src/modules/direct2game/handlers/general.py +++ b/src/servers/web_services/src/modules/direct2game/handlers/general.py @@ -1,15 +1,15 @@ -from servers.webservices.src.abstractions.contracts import RequestBase -from servers.webservices.src.abstractions.handler import CmdHandlerBase -from servers.webservices.src.applications.client import Client -from servers.webservices.src.modules.direct2game.contracts.requests import ( +from servers.web_services.src.abstractions.contracts import RequestBase +from servers.web_services.src.abstractions.handler import CmdHandlerBase +from servers.web_services.src.applications.client import Client +from servers.web_services.src.modules.direct2game.contracts.requests import ( GetPurchaseHistoryRequest, GetStoreAvailabilityRequest, ) -from servers.webservices.src.modules.direct2game.contracts.responses import ( +from servers.web_services.src.modules.direct2game.contracts.responses import ( GetPurchaseHistoryResponse, GetStoreAvailabilityResponse, ) -from servers.webservices.src.modules.direct2game.contracts.results import ( +from servers.web_services.src.modules.direct2game.contracts.results import ( GetPurchaseHistoryResult, GetStoreAvailabilityResult, ) diff --git a/src/servers/webservices/src/modules/sake/abstractions/general.py b/src/servers/web_services/src/modules/sake/abstractions/general.py similarity index 86% rename from src/servers/webservices/src/modules/sake/abstractions/general.py rename to src/servers/web_services/src/modules/sake/abstractions/general.py index 852ca3fa5..b9516b406 100644 --- a/src/servers/webservices/src/modules/sake/abstractions/general.py +++ b/src/servers/web_services/src/modules/sake/abstractions/general.py @@ -1,8 +1,8 @@ from xml.etree import ElementTree -import servers.webservices.src.abstractions.handler as h -import servers.webservices.src.abstractions.contracts as lib -from servers.webservices.src.aggregations.soap_envelop import SoapEnvelop -from servers.webservices.src.modules.sake.exceptions.general import SakeException +import servers.web_services.src.abstractions.handler as h +import servers.web_services.src.abstractions.contracts as lib +from servers.web_services.src.aggregations.soap_envelop import SoapEnvelop +from servers.web_services.src.modules.sake.exceptions.general import SakeException NAMESPACE = "http://gamespy.net/sake" diff --git a/src/servers/webservices/src/modules/sake/contracts/requests.py b/src/servers/web_services/src/modules/sake/contracts/requests.py similarity index 97% rename from src/servers/webservices/src/modules/sake/contracts/requests.py rename to src/servers/web_services/src/modules/sake/contracts/requests.py index b18b7807f..630db1b91 100644 --- a/src/servers/webservices/src/modules/sake/contracts/requests.py +++ b/src/servers/web_services/src/modules/sake/contracts/requests.py @@ -3,13 +3,13 @@ from typing import OrderedDict from pydantic import BaseModel -from servers.webservices.src.modules.sake.abstractions.general import ( +from servers.web_services.src.modules.sake.abstractions.general import ( RequestBase, NAMESPACE, ) import xmltodict -from servers.webservices.src.modules.sake.exceptions.general import SakeException +from servers.web_services.src.modules.sake.exceptions.general import SakeException class CreateRecordRequest(RequestBase): diff --git a/src/servers/webservices/src/modules/sake/contracts/responses.py b/src/servers/web_services/src/modules/sake/contracts/responses.py similarity index 76% rename from src/servers/webservices/src/modules/sake/contracts/responses.py rename to src/servers/web_services/src/modules/sake/contracts/responses.py index e75d5de79..220e8d7b4 100644 --- a/src/servers/webservices/src/modules/sake/contracts/responses.py +++ b/src/servers/web_services/src/modules/sake/contracts/responses.py @@ -1,6 +1,6 @@ -from servers.webservices.src.modules.sake.abstractions.general import ResponseBase -from servers.webservices.src.modules.sake.contracts.requests import CreateRecordRequest, SearchForRecordsRequest -from servers.webservices.src.modules.sake.contracts.results import CreateRecordResult, SearchForRecordsResult +from servers.web_services.src.modules.sake.abstractions.general import ResponseBase +from servers.web_services.src.modules.sake.contracts.requests import CreateRecordRequest, SearchForRecordsRequest +from servers.web_services.src.modules.sake.contracts.results import CreateRecordResult, SearchForRecordsResult class CreateRecordResponse(ResponseBase): diff --git a/src/servers/webservices/src/modules/sake/contracts/results.py b/src/servers/web_services/src/modules/sake/contracts/results.py similarity index 83% rename from src/servers/webservices/src/modules/sake/contracts/results.py rename to src/servers/web_services/src/modules/sake/contracts/results.py index ec0cfca36..7942c08bd 100644 --- a/src/servers/webservices/src/modules/sake/contracts/results.py +++ b/src/servers/web_services/src/modules/sake/contracts/results.py @@ -1,7 +1,7 @@ from typing import OrderedDict from pydantic import BaseModel -from servers.webservices.src.modules.sake.abstractions.general import ResultBase +from servers.web_services.src.modules.sake.abstractions.general import ResultBase class CreateRecordResult(ResultBase): diff --git a/src/servers/web_services/src/modules/sake/exceptions/general.py b/src/servers/web_services/src/modules/sake/exceptions/general.py new file mode 100644 index 000000000..9cc3e22bc --- /dev/null +++ b/src/servers/web_services/src/modules/sake/exceptions/general.py @@ -0,0 +1,5 @@ +from servers.web_services.src.exceptions.general import WebExceptions + + +class SakeException(WebExceptions): + pass diff --git a/src/servers/webservices/src/modules/sake/handlers/general.py b/src/servers/web_services/src/modules/sake/handlers/general.py similarity index 68% rename from src/servers/webservices/src/modules/sake/handlers/general.py rename to src/servers/web_services/src/modules/sake/handlers/general.py index b3ffb18d5..fb10ca4c5 100644 --- a/src/servers/webservices/src/modules/sake/handlers/general.py +++ b/src/servers/web_services/src/modules/sake/handlers/general.py @@ -1,7 +1,7 @@ -from servers.webservices.src.applications.client import Client -from servers.webservices.src.modules.sake.abstractions.general import CmdHandlerBase -from servers.webservices.src.modules.sake.contracts.requests import CreateRecordRequest, GetMyRecordsRequest, SearchForRecordsRequest -from servers.webservices.src.modules.sake.contracts.results import CreateRecordResult, GetMyRecordsResult, SearchForRecordsResult +from servers.web_services.src.applications.client import Client +from servers.web_services.src.modules.sake.abstractions.general import CmdHandlerBase +from servers.web_services.src.modules.sake.contracts.requests import CreateRecordRequest, GetMyRecordsRequest, SearchForRecordsRequest +from servers.web_services.src.modules.sake.contracts.results import CreateRecordResult, GetMyRecordsResult, SearchForRecordsResult class CreateRecordHandler(CmdHandlerBase): diff --git a/src/servers/web_services/tests/__init__.py b/src/servers/web_services/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/webservices/tests/altas_tests.py b/src/servers/web_services/tests/altas_tests.py similarity index 100% rename from src/servers/webservices/tests/altas_tests.py rename to src/servers/web_services/tests/altas_tests.py diff --git a/src/servers/webservices/tests/auth_tests.py b/src/servers/web_services/tests/auth_tests.py similarity index 97% rename from src/servers/webservices/tests/auth_tests.py rename to src/servers/web_services/tests/auth_tests.py index 608594932..25daeff2a 100644 --- a/src/servers/webservices/tests/auth_tests.py +++ b/src/servers/web_services/tests/auth_tests.py @@ -2,7 +2,7 @@ import responses -from servers.webservices.src.modules.auth.contracts.requests import LoginProfileWithGameIdRequest, LoginPs3CertRequest, LoginRemoteAuthRequest, LoginUniqueNickRequest +from servers.web_services.src.modules.auth.contracts.requests import LoginProfileWithGameIdRequest, LoginPs3CertRequest, LoginRemoteAuthRequest, LoginUniqueNickRequest LOGIN_PROFILE = """ diff --git a/src/servers/webservices/src/modules/auth/exceptions/general.py b/src/servers/webservices/src/modules/auth/exceptions/general.py deleted file mode 100644 index 16994567a..000000000 --- a/src/servers/webservices/src/modules/auth/exceptions/general.py +++ /dev/null @@ -1,5 +0,0 @@ -from servers.webservices.src.exceptions.general import WebExceptions - - -class AuthException(WebExceptions): - pass diff --git a/src/servers/webservices/src/modules/sake/exceptions/general.py b/src/servers/webservices/src/modules/sake/exceptions/general.py deleted file mode 100644 index 010a7bca1..000000000 --- a/src/servers/webservices/src/modules/sake/exceptions/general.py +++ /dev/null @@ -1,5 +0,0 @@ -from servers.webservices.src.exceptions.general import WebExceptions - - -class SakeException(WebExceptions): - pass From e5cda5e4d9219a9d1ef11a81caa4fac7357ee75e Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 4 Sep 2024 07:12:36 +0000 Subject: [PATCH 101/231] refactor: added database fetching functions --- .../gamespy/library/contracts/chat.py | 0 src/backends/gamespy/protocols/natneg/data.py | 16 ---- .../gamespy/protocols/query_report/data.py | 31 ------- .../gamespy/protocols/web_services/data.py | 0 .../library/abstractions/cmd_handler_base.py | 0 .../library/abstractions/contracts.py | 0 .../{gamespy => }/protocols/__init__.py | 0 .../gamespy}/chat/__init__.py | 0 .../gamespy}/chat/requests.py | 2 +- .../protocols/gamespy/game_status/data.py | 41 +++++++++ .../gamespy}/game_status/requests.py | 2 +- .../gamespy/game_traffic_relay}/data.py | 0 .../gamespy}/natneg/__init__.py | 0 src/backends/protocols/gamespy/natneg/data.py | 50 +++++++++++ .../gamespy}/natneg/init_packet_info.py | 3 +- .../gamespy}/natneg/relay_server_info.py | 0 .../gamespy}/natneg/requests.py | 2 +- .../presence_connection_manager/data.py | 0 .../presence_connection_manager/requests.py | 2 +- .../presence_search_player/__init__.py | 0 .../gamespy}/presence_search_player/data.py | 0 .../presence_search_player/requests.py | 2 +- .../protocols/gamespy/query_report/data.py | 84 +++++++++++++++++++ .../gamespy}/query_report/requests.py | 2 +- .../gamespy/query_report}/storage_info.py | 11 +-- .../gamespy/server_browser}/data.py | 0 .../gamespy}/server_browser/requests.py | 2 +- .../gamespy/web_services}/data.py | 0 .../gamespy}/web_services/requests.py | 2 +- .../gamespy/presence_connection_manager.py | 2 +- .../v2/aggregations/server_info_builder.py | 2 +- 31 files changed, 193 insertions(+), 63 deletions(-) delete mode 100644 src/backends/gamespy/library/contracts/chat.py delete mode 100644 src/backends/gamespy/protocols/natneg/data.py delete mode 100644 src/backends/gamespy/protocols/query_report/data.py delete mode 100644 src/backends/gamespy/protocols/web_services/data.py rename src/backends/{gamespy => }/library/abstractions/cmd_handler_base.py (100%) rename src/backends/{gamespy => }/library/abstractions/contracts.py (100%) rename src/backends/{gamespy => }/protocols/__init__.py (100%) rename src/backends/{gamespy/protocols => protocols/gamespy}/chat/__init__.py (100%) rename src/backends/{gamespy/protocols => protocols/gamespy}/chat/requests.py (96%) create mode 100644 src/backends/protocols/gamespy/game_status/data.py rename src/backends/{gamespy/protocols => protocols/gamespy}/game_status/requests.py (95%) rename src/backends/{gamespy/protocols/game_status => protocols/gamespy/game_traffic_relay}/data.py (100%) rename src/backends/{gamespy/protocols => protocols/gamespy}/natneg/__init__.py (100%) create mode 100644 src/backends/protocols/gamespy/natneg/data.py rename src/backends/{gamespy/protocols => protocols/gamespy}/natneg/init_packet_info.py (97%) rename src/backends/{gamespy/protocols => protocols/gamespy}/natneg/relay_server_info.py (100%) rename src/backends/{gamespy/protocols => protocols/gamespy}/natneg/requests.py (95%) rename src/backends/{gamespy/protocols => protocols/gamespy}/presence_connection_manager/data.py (100%) rename src/backends/{gamespy/protocols => protocols/gamespy}/presence_connection_manager/requests.py (98%) rename src/backends/{gamespy/protocols => protocols/gamespy}/presence_search_player/__init__.py (100%) rename src/backends/{gamespy/protocols => protocols/gamespy}/presence_search_player/data.py (100%) rename src/backends/{gamespy/protocols => protocols/gamespy}/presence_search_player/requests.py (94%) create mode 100644 src/backends/protocols/gamespy/query_report/data.py rename src/backends/{gamespy/protocols => protocols/gamespy}/query_report/requests.py (93%) rename src/{servers/chat/src/aggregates => backends/protocols/gamespy/query_report}/storage_info.py (81%) rename src/backends/{gamespy/protocols/game_traffic_relay => protocols/gamespy/server_browser}/data.py (100%) rename src/backends/{gamespy/protocols => protocols/gamespy}/server_browser/requests.py (94%) rename src/backends/{gamespy/protocols/server_browser => protocols/gamespy/web_services}/data.py (100%) rename src/backends/{gamespy/protocols => protocols/gamespy}/web_services/requests.py (95%) diff --git a/src/backends/gamespy/library/contracts/chat.py b/src/backends/gamespy/library/contracts/chat.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/backends/gamespy/protocols/natneg/data.py b/src/backends/gamespy/protocols/natneg/data.py deleted file mode 100644 index c72e85105..000000000 --- a/src/backends/gamespy/protocols/natneg/data.py +++ /dev/null @@ -1,16 +0,0 @@ -# from servers.natneg.contracts.requests import InitRequest -from backends.gamespy.protocols.natneg.requests import InitRequest -from backends.gamespy.protocols.natneg.init_packet_info import InitPacketInfo -from mongoengine import QuerySet - -def store_init_packet(request: InitRequest) -> None: - info = InitPacketInfo(request.model_dump()) - # InitPacketInfo.objects(server_id=info.server_id, cookie=info.cookie, version=info.version, port_type=info.port_type, - # client_index=info.client_index, game_name=info.game_name, use_game_port=info.use_game_port, - # public_ip=info.public_ip, public_port=info.public_port, private_ip=info.private_ip, private_port=info.private_port) - info.update(upsert=True) - - -def count_inif_info(cookie: int, version: int) -> int: - result = InitPacketInfo.objects(cookie=cookie, version=version).count() - return result diff --git a/src/backends/gamespy/protocols/query_report/data.py b/src/backends/gamespy/protocols/query_report/data.py deleted file mode 100644 index 3d55c2e63..000000000 --- a/src/backends/gamespy/protocols/query_report/data.py +++ /dev/null @@ -1,31 +0,0 @@ -from library.src.database.pg_orm import PG_SESSION, GroupList, Games - - -def get_all_groups(): - result: list[tuple[Games, GroupList]] = ( - PG_SESSION.query(Games, GroupList) - .join(GroupList, Games.gameid == GroupList.gameid) - .all() - ) - - # Group the results by Game name - grouped_result = {} - for game, group in result: - if game.gamename not in grouped_result: - grouped_result[game.gamename] = [] - grouped_result[game.gamename].append( - { - "game_id": group.gameid, - "game_name": game.gamename, - "group_id": group.groupid, - "room_name": group.roomname, - "secret_key": game.secretkey, - } - ) - - # Convert the grouped result to the desired format - return grouped_result - - -if __name__ == "__main__": - get_all_groups() diff --git a/src/backends/gamespy/protocols/web_services/data.py b/src/backends/gamespy/protocols/web_services/data.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/backends/gamespy/library/abstractions/cmd_handler_base.py b/src/backends/library/abstractions/cmd_handler_base.py similarity index 100% rename from src/backends/gamespy/library/abstractions/cmd_handler_base.py rename to src/backends/library/abstractions/cmd_handler_base.py diff --git a/src/backends/gamespy/library/abstractions/contracts.py b/src/backends/library/abstractions/contracts.py similarity index 100% rename from src/backends/gamespy/library/abstractions/contracts.py rename to src/backends/library/abstractions/contracts.py diff --git a/src/backends/gamespy/protocols/__init__.py b/src/backends/protocols/__init__.py similarity index 100% rename from src/backends/gamespy/protocols/__init__.py rename to src/backends/protocols/__init__.py diff --git a/src/backends/gamespy/protocols/chat/__init__.py b/src/backends/protocols/gamespy/chat/__init__.py similarity index 100% rename from src/backends/gamespy/protocols/chat/__init__.py rename to src/backends/protocols/gamespy/chat/__init__.py diff --git a/src/backends/gamespy/protocols/chat/requests.py b/src/backends/protocols/gamespy/chat/requests.py similarity index 96% rename from src/backends/gamespy/protocols/chat/requests.py rename to src/backends/protocols/gamespy/chat/requests.py index 3bcfe87c4..1e27bee1f 100644 --- a/src/backends/gamespy/protocols/chat/requests.py +++ b/src/backends/protocols/gamespy/chat/requests.py @@ -1,4 +1,4 @@ -import backends.gamespy.library.abstractions.contracts as lib +import backends.library.abstractions.contracts as lib from servers.chat.src.enums.general import LoginRequestType, WhoRequestType diff --git a/src/backends/protocols/gamespy/game_status/data.py b/src/backends/protocols/gamespy/game_status/data.py new file mode 100644 index 000000000..f2032a7af --- /dev/null +++ b/src/backends/protocols/gamespy/game_status/data.py @@ -0,0 +1,41 @@ +from typing import Optional +from library.src.database.pg_orm import PG_SESSION, PStorage, Profiles, SubProfiles +from servers.game_status.src.enums.general import PersistStorageType +from servers.game_status.src.exceptions.general import GSException + + +def create_new_game_data(): + raise NotImplementedError() + + +def create_new_player_data(): + raise NotImplementedError() + + +def update_player_data(): + raise NotImplementedError() + + +def get_profile_id(token: str) -> int: + result = PG_SESSION.query(SubProfiles.profileid).filter( + SubProfiles.authtoken == token).one() + if result is None: + raise GSException("No records found in database") + return result + + +def get_profile_id(cdkey: str, nick_name: str) -> Optional[int]: + result = PG_SESSION.query(SubProfiles.profileid).join( + SubProfiles, Profiles.profileid == SubProfiles.profileid).filter(SubProfiles.cdkeyenc == cdkey, Profiles.nick == nick_name).one() + if result is None: + raise GSException("No record found in database") + return result + + +def get_player_data(profile_id: int, storage_type: PersistStorageType, data_index: int) -> dict[str, str]: + result = PG_SESSION.query(PStorage.data).filter(PStorage.ptype == storage_type.value, + PStorage.dindex == data_index, + PStorage.profileid == profile_id) + if result is None: + raise GSException("No records found in database") + return result diff --git a/src/backends/gamespy/protocols/game_status/requests.py b/src/backends/protocols/gamespy/game_status/requests.py similarity index 95% rename from src/backends/gamespy/protocols/game_status/requests.py rename to src/backends/protocols/gamespy/game_status/requests.py index f1f73b358..2d1b6ab20 100644 --- a/src/backends/gamespy/protocols/game_status/requests.py +++ b/src/backends/protocols/gamespy/game_status/requests.py @@ -1,5 +1,5 @@ from typing import Optional -import backends.gamespy.library.abstractions.contracts as lib +import backends.library.abstractions.contracts as lib from servers.game_status.src.enums.general import AuthMethod, PersistStorageType diff --git a/src/backends/gamespy/protocols/game_status/data.py b/src/backends/protocols/gamespy/game_traffic_relay/data.py similarity index 100% rename from src/backends/gamespy/protocols/game_status/data.py rename to src/backends/protocols/gamespy/game_traffic_relay/data.py diff --git a/src/backends/gamespy/protocols/natneg/__init__.py b/src/backends/protocols/gamespy/natneg/__init__.py similarity index 100% rename from src/backends/gamespy/protocols/natneg/__init__.py rename to src/backends/protocols/gamespy/natneg/__init__.py diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py new file mode 100644 index 000000000..05a759140 --- /dev/null +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -0,0 +1,50 @@ +# from servers.natneg.contracts.requests import InitRequest +from uuid import UUID +from backends.protocols.gamespy.natneg.relay_server_info import RelayServerInfo +from backends.protocols.gamespy.natneg.requests import InitRequest +from backends.protocols.gamespy.natneg.init_packet_info import InitPacketInfo, NatFailInfo +from mongoengine import QuerySet + + +def store_init_packet(request: InitRequest) -> None: + info = InitPacketInfo(request.model_dump()) + info.update(upsert=True) + + +def count_init_info(cookie: int, version: int) -> int: + result = InitPacketInfo.objects(cookie=cookie, version=version).count() + return result + + +def get_available_relay_serves() -> list[RelayServerInfo]: + """ + Return + ------ + list of ip:port + """ + result: list[RelayServerInfo] = list(RelayServerInfo.objects) + return result + + +def get_init_infos(server_id: UUID, cookie: int) -> list[InitPacketInfo]: + result: list[InitPacketInfo] = InitPacketInfo.objects( + server_id=server_id, cookie=cookie) + return result + + +def update_init_info(info: InitPacketInfo) -> None: + info.save() + + +def remove_init_info(info: InitPacketInfo) -> None: + info.delete() + + +def update_nat_fail_info(info: NatFailInfo) -> None: + info.save() + + +def get_nat_fail_info(public_ip1: str, public_ip2: str) -> list[NatFailInfo]: + result = NatFailInfo.objects(public_ip_address1=public_ip1, + public_ip_address2=public_ip2) + return result diff --git a/src/backends/gamespy/protocols/natneg/init_packet_info.py b/src/backends/protocols/gamespy/natneg/init_packet_info.py similarity index 97% rename from src/backends/gamespy/protocols/natneg/init_packet_info.py rename to src/backends/protocols/gamespy/natneg/init_packet_info.py index b5f485bd0..2a6eb264b 100644 --- a/src/backends/gamespy/protocols/natneg/init_packet_info.py +++ b/src/backends/protocols/gamespy/natneg/init_packet_info.py @@ -10,6 +10,7 @@ from servers.natneg.src.enums.general import NatClientIndex, NatPortType import datetime + class InitPacketInfo(Document): server_id = UUIDField(binary=False, required=True) cookie = IntField(required=True) @@ -35,4 +36,4 @@ class NatFailInfo(Document): if __name__ == "__main__": - InitPacketInfo.objects \ No newline at end of file + InitPacketInfo.objects() diff --git a/src/backends/gamespy/protocols/natneg/relay_server_info.py b/src/backends/protocols/gamespy/natneg/relay_server_info.py similarity index 100% rename from src/backends/gamespy/protocols/natneg/relay_server_info.py rename to src/backends/protocols/gamespy/natneg/relay_server_info.py diff --git a/src/backends/gamespy/protocols/natneg/requests.py b/src/backends/protocols/gamespy/natneg/requests.py similarity index 95% rename from src/backends/gamespy/protocols/natneg/requests.py rename to src/backends/protocols/gamespy/natneg/requests.py index a71f4e729..df08a4100 100644 --- a/src/backends/gamespy/protocols/natneg/requests.py +++ b/src/backends/protocols/gamespy/natneg/requests.py @@ -8,7 +8,7 @@ ) from typing import Union -import backends.gamespy.library.abstractions.contracts as lib +import backends.library.abstractions.contracts as lib class RequestBase(lib.RequestBase): diff --git a/src/backends/gamespy/protocols/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py similarity index 100% rename from src/backends/gamespy/protocols/presence_connection_manager/data.py rename to src/backends/protocols/gamespy/presence_connection_manager/data.py diff --git a/src/backends/gamespy/protocols/presence_connection_manager/requests.py b/src/backends/protocols/gamespy/presence_connection_manager/requests.py similarity index 98% rename from src/backends/gamespy/protocols/presence_connection_manager/requests.py rename to src/backends/protocols/gamespy/presence_connection_manager/requests.py index 4d0046522..8a0564f91 100644 --- a/src/backends/gamespy/protocols/presence_connection_manager/requests.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/requests.py @@ -11,7 +11,7 @@ SdkRevisionType, ) -import backends.gamespy.library.abstractions.contracts as lib +import backends.library.abstractions.contracts as lib class RequestBase(BaseModel): diff --git a/src/backends/gamespy/protocols/presence_search_player/__init__.py b/src/backends/protocols/gamespy/presence_search_player/__init__.py similarity index 100% rename from src/backends/gamespy/protocols/presence_search_player/__init__.py rename to src/backends/protocols/gamespy/presence_search_player/__init__.py diff --git a/src/backends/gamespy/protocols/presence_search_player/data.py b/src/backends/protocols/gamespy/presence_search_player/data.py similarity index 100% rename from src/backends/gamespy/protocols/presence_search_player/data.py rename to src/backends/protocols/gamespy/presence_search_player/data.py diff --git a/src/backends/gamespy/protocols/presence_search_player/requests.py b/src/backends/protocols/gamespy/presence_search_player/requests.py similarity index 94% rename from src/backends/gamespy/protocols/presence_search_player/requests.py rename to src/backends/protocols/gamespy/presence_search_player/requests.py index 09924bbc3..4bab223ce 100644 --- a/src/backends/gamespy/protocols/presence_search_player/requests.py +++ b/src/backends/protocols/gamespy/presence_search_player/requests.py @@ -1,5 +1,5 @@ from typing import Optional -from backends.gamespy.library.abstractions.contracts import RequestBase as RB +from backends.library.abstractions.contracts import RequestBase as RB from servers.presence_search_player.src.enums.general import SearchType diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py new file mode 100644 index 000000000..92f15f3e5 --- /dev/null +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -0,0 +1,84 @@ +from backends.protocols.gamespy.query_report.storage_info import ChannelInfo +from library.src.database.pg_orm import PG_SESSION, GroupList, Games +from servers.chat.src.aggregates.peer_room import PeerRoom +from servers.query_report.src.v2.aggregates.game_server_info_v2 import GameServerInfoV2 + + +def get_all_groups(): + result: list[tuple[Games, GroupList]] = ( + PG_SESSION.query(Games, GroupList) + .join(GroupList, Games.gameid == GroupList.gameid) + .all() + ) + + # Group the results by Game name + grouped_result = {} + for game, group in result: + if game.gamename not in grouped_result: + temp_list = [] + grouped_result[game.gamename] = temp_list + temp_list.append( + { + "game_id": group.gameid, + "game_name": game.gamename, + "group_id": group.groupid, + "room_name": group.roomname, + "secret_key": game.secretkey, + } + ) + + # Convert the grouped result to the desired format + return grouped_result + + +def get_peer_staging_channels(game_name: str, group_id: int): + assert isinstance(game_name, str) + assert isinstance(group_id, int) + staging_name = f"{PeerRoom.StagingRoomPrefix}!{game_name}!*" + + result: list[ChannelInfo] = ChannelInfo.objects(channel_name=staging_name) + return result + + +def get_peer_group_channel(group_id: int): + assert isinstance(group_id, int) + group_name = f"{PeerRoom.GroupRoomPrefix}!{group_id}" + result: list[ChannelInfo] = ChannelInfo.objects(channel_name=group_name) + return result + + +def get_server_info_with_instant_key(instant_key: int) -> GameServerInfoV2: + assert isinstance(instant_key, int) + + result = GameServerInfoV2.objects(instant_key=instant_key) + + return result + + +def get_server_info_with_game_name(game_name: str) -> GameServerInfoV2: + assert isinstance(game_name, str) + + result = GameServerInfoV2.objects(game_name=game_name).one() + + return result + + +def get_server_info_with_ip_and_port(ip: str, port: int) -> GameServerInfoV2: + assert isinstance(ip, str) + assert isinstance(port, int) + + result = GameServerInfoV2.objects( + host_ip_address=ip, query_report_port=port).one() + + return result + +def remove_server_info(info: GameServerInfoV2) -> None: + info.delete() + + +def update_game_server(info: GameServerInfoV2) -> None: + info.update() + + +if __name__ == "__main__": + get_all_groups() diff --git a/src/backends/gamespy/protocols/query_report/requests.py b/src/backends/protocols/gamespy/query_report/requests.py similarity index 93% rename from src/backends/gamespy/protocols/query_report/requests.py rename to src/backends/protocols/gamespy/query_report/requests.py index ea24e64a1..8b60edae6 100644 --- a/src/backends/gamespy/protocols/query_report/requests.py +++ b/src/backends/protocols/gamespy/query_report/requests.py @@ -1,6 +1,6 @@ from pydantic import UUID4 -import backends.gamespy.library.abstractions.contracts as lib +import backends.library.abstractions.contracts as lib from servers.query_report.src.v2.enums.general import GameServerStatus, RequestType diff --git a/src/servers/chat/src/aggregates/storage_info.py b/src/backends/protocols/gamespy/query_report/storage_info.py similarity index 81% rename from src/servers/chat/src/aggregates/storage_info.py rename to src/backends/protocols/gamespy/query_report/storage_info.py index be490bfb7..34d456790 100644 --- a/src/servers/chat/src/aggregates/storage_info.py +++ b/src/backends/protocols/gamespy/query_report/storage_info.py @@ -12,13 +12,14 @@ class ChannelInfo(Document): game_name = StringField(required=True) channel_name = StringField(required=True) - key_values = DictField(reqired=False) + key_values = DictField() max_num_user = IntField(max_value=200) - room_name = StringField(required=False) - topic = StringField(required=False) - password = StringField(required=False) - group_id = IntField(required=False) + room_name = StringField() + topic = StringField() + password = StringField() + group_id = IntField() create_time = DateTimeField(required=True) + previously_joined_channel = StringField() class ChannelUser(Document): diff --git a/src/backends/gamespy/protocols/game_traffic_relay/data.py b/src/backends/protocols/gamespy/server_browser/data.py similarity index 100% rename from src/backends/gamespy/protocols/game_traffic_relay/data.py rename to src/backends/protocols/gamespy/server_browser/data.py diff --git a/src/backends/gamespy/protocols/server_browser/requests.py b/src/backends/protocols/gamespy/server_browser/requests.py similarity index 94% rename from src/backends/gamespy/protocols/server_browser/requests.py rename to src/backends/protocols/gamespy/server_browser/requests.py index bbeb50da5..e6b1db385 100644 --- a/src/backends/gamespy/protocols/server_browser/requests.py +++ b/src/backends/protocols/gamespy/server_browser/requests.py @@ -1,5 +1,5 @@ from typing import List, Optional -import backends.gamespy.library.abstractions.contracts as lib +import backends.library.abstractions.contracts as lib from servers.server_browser.src.v2.enums.general import RequestType, ServerListUpdateOption diff --git a/src/backends/gamespy/protocols/server_browser/data.py b/src/backends/protocols/gamespy/web_services/data.py similarity index 100% rename from src/backends/gamespy/protocols/server_browser/data.py rename to src/backends/protocols/gamespy/web_services/data.py diff --git a/src/backends/gamespy/protocols/web_services/requests.py b/src/backends/protocols/gamespy/web_services/requests.py similarity index 95% rename from src/backends/gamespy/protocols/web_services/requests.py rename to src/backends/protocols/gamespy/web_services/requests.py index 50d45ec06..d5ba9e927 100644 --- a/src/backends/gamespy/protocols/web_services/requests.py +++ b/src/backends/protocols/gamespy/web_services/requests.py @@ -1,5 +1,5 @@ from pydantic import BaseModel -import backends.gamespy.library.abstractions.contracts as lib +import backends.library.abstractions.contracts as lib class RequestBase(lib.RequestBase): diff --git a/src/backends/routers/gamespy/presence_connection_manager.py b/src/backends/routers/gamespy/presence_connection_manager.py index ef5a70f03..94424ea52 100644 --- a/src/backends/routers/gamespy/presence_connection_manager.py +++ b/src/backends/routers/gamespy/presence_connection_manager.py @@ -1,6 +1,6 @@ from fastapi import FastAPI -from backends.gamespy.protocols.presence_search_player.requests import LoginRequest +from backends.protocols.gamespy.presence_search_player.requests import LoginRequest from backends.urls import * import uvicorn diff --git a/src/servers/server_browser/src/v2/aggregations/server_info_builder.py b/src/servers/server_browser/src/v2/aggregations/server_info_builder.py index a41b15247..17fe5eccb 100644 --- a/src/servers/server_browser/src/v2/aggregations/server_info_builder.py +++ b/src/servers/server_browser/src/v2/aggregations/server_info_builder.py @@ -2,7 +2,7 @@ from servers.query_report.src.aggregates.game_server_info import GameServerInfo -from backends.gamespy.protocols.query_report.data import get_all_groups +from backends.protocols.gamespy.query_report.data import get_all_groups from servers.server_browser.src.v2.abstractions.contracts import QUERY_REPORT_DEFAULT_PORT from servers.server_browser.src.v2.enums.general import GameServerFlags From 0fa817af91b35b9528717e508cfa1fcf15dafb34 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 4 Sep 2024 16:18:40 +0800 Subject: [PATCH 102/231] refactor: fix unittest backendurl construct error --- src/library/src/abstractions/handler.py | 2 +- src/library/src/unispy_server_config.py | 8 +++++--- src/library/tests/mock_objects/general.py | 5 +++-- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index e495f4fdf..a03cf3747 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -75,7 +75,7 @@ def _data_operate(self) -> None: # fmt: off - url = f"{CONFIG.backend.url}/gamespy/{self._client.server_config.server_name}/{self.__class__.__name__[:-len("Handler")]}/".lower() + url = f"{CONFIG.backend.url}/GameSpy/{self._client.server_config.server_name}/{self.__class__.__name__}/" # fmt: on data = self._request.to_serializable_dict() diff --git a/src/library/src/unispy_server_config.py b/src/library/src/unispy_server_config.py index 45b2a6f2d..d955f7638 100644 --- a/src/library/src/unispy_server_config.py +++ b/src/library/src/unispy_server_config.py @@ -1,12 +1,14 @@ +import os from typing import Any, Literal, Optional from uuid import UUID -import os - -from pydantic import BaseModel, Field, constr +from pydantic import BaseModel, Field from library.src.exceptions.general import UniSpyException + + + class PostgreSql(BaseModel): server: str # Ensures port is between 1 and 65535 diff --git a/src/library/tests/mock_objects/general.py b/src/library/tests/mock_objects/general.py index 7d354171b..25662d663 100644 --- a/src/library/tests/mock_objects/general.py +++ b/src/library/tests/mock_objects/general.py @@ -41,6 +41,7 @@ def warn(self, message): def create_mock_url(client: ClientBase, handler: type[CmdHandlerBase], data: dict) -> None: - url = f"{ - CONFIG.backend.url}/{client.server_config.server_name}/{handler.__name__}/" + # fmt: off + url = f"{CONFIG.backend.url}/GameSpy/{client.server_config.server_name}/{handler.__name__}/" responses.add(responses.POST, url, json=data, status=200) + # fmt: on From 2e718be6f8d6b3716c915a54faf9ab162031af51 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 5 Sep 2024 10:21:30 +0800 Subject: [PATCH 103/231] refactor: fix import error, created Contribution doc --- src/Contribute.md | 16 ++++++ .../library/abstractions/cmd_handler_base.py | 7 ++- src/library/src/abstractions/client.py | 20 ++++--- src/library/src/abstractions/connections.py | 1 - src/library/src/abstractions/handler.py | 9 ++- src/library/src/abstractions/switcher.py | 39 +++++++------ src/library/src/exceptions/general.py | 21 +++---- .../game_status/src/applications/client.py | 2 +- .../game_status/src/handlers/handlers.py | 23 ++++---- src/servers/natneg/src/handlers/handlers.py | 57 +++++++------------ .../src/aggregates/user_status.py | 9 +-- .../src/aggregates/user_status_info.py | 5 +- .../src/contracts/requests/buddy.py | 13 +++-- .../src/contracts/requests/general.py | 5 ++ .../src/contracts/requests/profile.py | 9 ++- .../src/contracts/responses/general.py | 2 +- .../src/contracts/results/general.py | 3 +- .../src/contracts/results/profile.py | 4 +- .../src/handlers/buddy.py | 4 +- .../src/handlers/general.py | 8 +-- .../src/handlers/profile.py | 20 +++++-- .../src/handlers/switcher.py | 16 ++---- .../tests/game_tests.py | 2 +- .../tests/mock_objects.py | 6 +- .../src/handlers/switcher.py | 1 - 25 files changed, 171 insertions(+), 131 deletions(-) create mode 100644 src/Contribute.md diff --git a/src/Contribute.md b/src/Contribute.md new file mode 100644 index 000000000..0feece733 --- /dev/null +++ b/src/Contribute.md @@ -0,0 +1,16 @@ +```python + +class BaseClass: + """ + We use class static member to type hint the class instance member + Do not initialize the class static member + """ + _property1:type1 + _property2:type2 + + + def __init__(self): + # In the base class we have to check whether the _property has been initialized, if not we init it + if not hasattr(self,"_property1"): + self._property1 = value1 +``` \ No newline at end of file diff --git a/src/backends/library/abstractions/cmd_handler_base.py b/src/backends/library/abstractions/cmd_handler_base.py index 2615ea817..9242fea72 100644 --- a/src/backends/library/abstractions/cmd_handler_base.py +++ b/src/backends/library/abstractions/cmd_handler_base.py @@ -1,4 +1,3 @@ -import abc from typing import TYPE_CHECKING if TYPE_CHECKING: @@ -13,3 +12,9 @@ def handle(self): def request_check(self): pass + + def data_fetch(self): + pass + + def result_construct(self): + pass diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index db5928073..b4187889e 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -24,21 +24,23 @@ class ClientBase: server_config: ServerConfig connection: "ConnectionBase" logger: LogWriter - crypto: Optional["EncryptBase"] = None + crypto: Optional["EncryptBase"] info: "ClientInfoBase" - is_log_raw: bool = False + is_log_raw: bool def __init__( self, connection: "ConnectionBase", server_config: ServerConfig, logger: LogWriter ): + # fmt: off assert isinstance(server_config, ServerConfig) - # assert isinstance(logger, LogWriter) + assert isinstance(logger, LogWriter) self.server_config = server_config - self.connection = connection self.logger = logger - self._log_prefix = f"[{self.connection.remote_ip}:{ - self.connection.remote_port}]" + self._log_prefix = f"[{self.connection.remote_ip}:{self.connection.remote_port}]" + self.crypto = None + self.is_log_raw = False + # fmt: on def on_connected(self) -> None: pass @@ -46,10 +48,10 @@ def on_connected(self) -> None: def on_disconnected(self) -> None: pass - def create_switcher(self, buffer: "Optional[bytes | HttpRequest]") -> "SwitcherBase": - pass + def create_switcher(self, buffer: "bytes | HttpRequest") -> "SwitcherBase": + assert isinstance(buffer, bytes) or isinstance(buffer, HttpRequest) - def on_received(self, buffer: "Optional[bytes | HttpRequest]") -> None: + def on_received(self, buffer: "bytes | HttpRequest") -> None: if isinstance(buffer, bytes): if self.crypto is not None: buffer = self.crypto.decrypt(buffer) diff --git a/src/library/src/abstractions/connections.py b/src/library/src/abstractions/connections.py index 737cfe93f..b145de77c 100644 --- a/src/library/src/abstractions/connections.py +++ b/src/library/src/abstractions/connections.py @@ -3,7 +3,6 @@ from typing import Optional from library.src.abstractions.client import ClientBase -# from library.src.extentions.string_extentions import IPEndPoint from library.src.log.log_manager import LogWriter from library.src.unispy_server_config import ServerConfig diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index a03cf3747..75d22d6c4 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -10,7 +10,6 @@ class CmdHandlerBase: - _client: "ClientBase" _request: "RequestBase" _result: "ResultBase" @@ -19,11 +18,11 @@ class CmdHandlerBase: """ the result type class, use to deserialize json data from backend """ - _is_uploading = True + _is_uploading: bool """ whether need send data to backend """ - _is_feaching = True + _is_feaching: bool """ whether need get data from backend """ @@ -33,6 +32,10 @@ def __init__(self, client: "ClientBase", request: "RequestBase") -> None: assert issubclass(type(client), ClientBase) assert issubclass(type(request), RequestBase) # if some subclass do not need result, override the __init__() in that subclass + if not hasattr(self, "_is_feaching"): + self._is_feaching = True + if not hasattr(self, "_is_uploading"): + self._is_uploading = True if self._is_feaching: assert issubclass(self._result_cls, ResultBase) diff --git a/src/library/src/abstractions/switcher.py b/src/library/src/abstractions/switcher.py index 2f9321810..a21deb5a9 100644 --- a/src/library/src/abstractions/switcher.py +++ b/src/library/src/abstractions/switcher.py @@ -1,29 +1,32 @@ -import abc +from abc import abstractmethod from library.src.abstractions.client import ClientBase from library.src.abstractions.handler import CmdHandlerBase -from typing import List, Optional +from typing import Optional class SwitcherBase: - - _client: ClientBase - _raw_request: object - _handlers: List[CmdHandlerBase] = [] - _requests: List[tuple] = [] """ - [ - (request_name,raw_request), - (request_name,raw_request), - (request_name,raw_request), - ... - ] - + class member type hint can use class static member, but you can not initialize any class static member here! Init it in the __init__() function """ + _handlers: list[CmdHandlerBase] + _requests: list[tuple] + _raw_request: object def __init__(self, client: ClientBase, raw_request: Optional[bytes | str]) -> None: assert isinstance(client, ClientBase) - self._client = client - self._raw_request = raw_request + self._client: ClientBase = client + self._raw_request: object = raw_request + self._handlers: list[CmdHandlerBase] = [] + self._requests: list[tuple[object, object]] = [] + """ + [ + (request_name,raw_request), + (request_name,raw_request), + (request_name,raw_request), + ... + ] + + """ def handle(self): from library.src.exceptions.general import UniSpyException @@ -46,10 +49,10 @@ def handle(self): except Exception as e: UniSpyException.handle_exception(e, self._client) - @abc.abstractmethod + @abstractmethod def _process_raw_request(self) -> None: pass - @abc.abstractmethod + @abstractmethod def _create_cmd_handlers(self, name: object, raw_request: object) -> Optional[CmdHandlerBase]: pass diff --git a/src/library/src/exceptions/general.py b/src/library/src/exceptions/general.py index d9b06190f..de1372d0c 100644 --- a/src/library/src/exceptions/general.py +++ b/src/library/src/exceptions/general.py @@ -16,21 +16,16 @@ def __init__(self, message: str) -> None: @staticmethod # def handle_exception(e: Exception, client: ClientBase = None): def handle_exception(e: Exception, client: "ClientBase" = None): - if issubclass(type(e), UniSpyException): - ex: UniSpyException = e - if client is None: - # LogWriter.LogError(ex.Message); - pass - else: - client.log_error(ex.message) - pass + if client is None: + # LogWriter.LogError(ex.Message); + pass else: - if client is None: - # LogWriter.LogError(ex.ToString()); - pass + if issubclass(type(e), UniSpyException): + ex: UniSpyException = e + client.log_error(ex.message) else: - # client.LogError(ex.ToString()); - pass + client.log_error(e) + pass def __repr__(self) -> str: # return super().__repr__() diff --git a/src/servers/game_status/src/applications/client.py b/src/servers/game_status/src/applications/client.py index 0f364467e..7b7e008ff 100644 --- a/src/servers/game_status/src/applications/client.py +++ b/src/servers/game_status/src/applications/client.py @@ -17,7 +17,7 @@ class ClientInfo(ClientInfoBase): class Client(ClientBase): - info: ClientInfo + info: ClientInfo = ClientInfo() def on_connected(self) -> None: self.crypto = GSCrypt() diff --git a/src/servers/game_status/src/handlers/handlers.py b/src/servers/game_status/src/handlers/handlers.py index 754831da5..6b9b41d8d 100644 --- a/src/servers/game_status/src/handlers/handlers.py +++ b/src/servers/game_status/src/handlers/handlers.py @@ -8,9 +8,10 @@ class AuthGameHandler(CmdHandlerBase): - _is_feaching = False + _request: AuthGameRequest def __init__(self, client: Client, request: AuthGameRequest) -> None: + self._is_feaching = False super().__init__(client, request) assert isinstance(request, AuthGameRequest) @@ -24,20 +25,22 @@ def _response_construct(self) -> None: class AuthPlayerHandler(CmdHandlerBase): - _result_cls: type[AuthPlayerResult] = AuthPlayerResult + _result_cls: type[AuthPlayerResult] def __init__(self, client: Client, request: AuthPlayerRequest) -> None: - super().__init__(client, request) assert isinstance(request, AuthPlayerRequest) + self._result_cls = AuthPlayerResult + super().__init__(client, request) def _response_construct(self) -> None: self._response = AuthPlayerResponse(self._request, self._result) class GetPlayerDataHandler(CmdHandlerBase): - _result_cls: type[GetPlayerDataResult] = GetPlayerDataResult + _result_cls: type[GetPlayerDataResult] def __init__(self, client: Client, request: GetPlayerDataRequest) -> None: + self._result_cls = GetPlayerDataResult super().__init__(client, request) assert isinstance(request, GetPlayerDataRequest) @@ -46,28 +49,27 @@ def _response_construct(self) -> None: class GetProfileIdHandler(CmdHandlerBase): - _result_cls: type[GetProfileIdResult] = GetProfileIdResult + _result_cls: type[GetProfileIdResult] def __init__(self, client: Client, request: GetProfileIdRequest) -> None: - super().__init__(client, request) assert isinstance(request, GetProfileIdRequest) + self._result_cls = GetProfileIdResult + super().__init__(client, request) def _response_construct(self) -> None: self._response = GetProfileIdResponse(self._request, self._result) class NewGameHandler(CmdHandlerBase): - _is_feaching = False - def __init__(self, client: Client, request: NewGameRequest) -> None: + self._is_feaching = False super().__init__(client, request) assert isinstance(request, NewGameRequest) class SetPlayerDataHandler(CmdHandlerBase): - _is_feaching = False - def __init__(self, client: Client, request: SetPlayerDataRequest) -> None: + self._is_feaching = False super().__init__(client, request) assert isinstance(request, SetPlayerDataRequest) raise NotImplementedError() @@ -80,7 +82,6 @@ class UpdateGameHandler(CmdHandlerBase): new request "\\updgame\\\\sesskey\\%d\\connid\\%d\\done\\%d\\gamedata\\%s" """ _request: UpdateGameRequest - def __init__(self, client: Client, request: UpdateGameRequest) -> None: super().__init__(client, request) assert isinstance(request, UpdateGameRequest) diff --git a/src/servers/natneg/src/handlers/handlers.py b/src/servers/natneg/src/handlers/handlers.py index ffef44a8d..4d0210e96 100644 --- a/src/servers/natneg/src/handlers/handlers.py +++ b/src/servers/natneg/src/handlers/handlers.py @@ -1,6 +1,3 @@ -from copy import copy - -from servers.natneg.src.abstractions.contracts import RequestBase from servers.natneg.src.abstractions.handlers import CmdHandlerBase from servers.natneg.src.applications.client import Client from servers.natneg.src.contracts.requests import ( @@ -30,9 +27,8 @@ class AddressCheckHandler(CmdHandlerBase): - _is_feaching = False - def __init__(self, client: Client, request: AddressCheckRequest) -> None: + self._is_feaching = False super().__init__(client, request) assert isinstance(client, Client) assert isinstance(request, AddressCheckRequest) @@ -42,11 +38,9 @@ def _data_operate(self) -> None: address check did not require restapi backend, \n just send the remote ip back to the client """ - data = { - "public_ip_addr": copy(self._client.connection.remote_ip), - "public_port": copy(self._client.connection.remote_port), - } - self._result = AddressCheckResult(**data) + self._result = AddressCheckResult( + public_ip_addr=self._client.connection.remote_ip, + public_port=self._client.connection.remote_port) self._result.public_ip_addr = self._client.connection.remote_ip self._result.public_port = self._client.connection.remote_port @@ -55,39 +49,38 @@ def _response_construct(self) -> None: class ConnectAckHandler(CmdHandlerBase): - _is_feaching = False + _request: ConnectAckRequest def __init__(self, client: Client, request: ConnectAckRequest) -> None: + self._is_feaching = False super().__init__(client, request) assert isinstance(request, ConnectAckRequest) def _data_operate(self) -> None: self._client.log_info( - f"client:{self._request.client_index} aknowledged connect request." - ) + f"client:{self._request.client_index} aknowledged connect request.") class ConnectHandler(CmdHandlerBase): - _is_feaching = False + _result_cls: type[ConnectResult] def __init__(self, client: Client, request: ConnectRequest) -> None: + self._is_feaching = False super().__init__(client, request) assert isinstance(request, ConnectRequest) + self._result_cls = ConnectResult class ErtAckHandler(CmdHandlerBase): - _is_feaching = False - def __init__(self, client: Client, request: ErtAckRequest) -> None: + self._is_feaching = False super().__init__(client, request) assert isinstance(request, ErtAckRequest) def _data_operate(self) -> None: - data = { - "public_ip_addr": copy(self._client.connection.remote_ip), - "public_port": copy(self._client.connection.remote_port), - } - self._result = ErtAckResult(**data) + self._result = ErtAckResult( + public_ip_addr=self._client.connection.remote_ip, + public_port=self._client.connection.remote_port) def _response_construct(self) -> None: self._response = ErcAckResponse(self._request, self._result) @@ -98,18 +91,15 @@ class InitHandler(CmdHandlerBase): In init process, we need response the initresponse first to make client not timeout """ - _is_feaching = False - def __init__(self, client: Client, request: InitRequest) -> None: + self._is_feaching = False super().__init__(client, request) assert isinstance(request, InitRequest) def _data_operate(self) -> None: - data = { - "public_ip_addr": copy(self._client.connection.remote_ip), - "public_port": copy(self._client.connection.remote_port), - } - self._result = InitResult(**data) + self._result = InitResult( + public_ip_addr=self._client.connection.remote_ip, + public_port=self._client.connection.remote_port) def _response_construct(self): self._response = InitResponse(self._request, self._result) @@ -121,18 +111,15 @@ def _response_send(self) -> None: class NatifyHandler(CmdHandlerBase): - _is_feaching = False - def __init__(self, client: Client, request: NatifyRequest) -> None: + self._is_feaching = False super().__init__(client, request) assert isinstance(request, NatifyRequest) def _data_operate(self): - data = { - "public_ip_addr": copy(self._client.connection.remote_ip), - "public_port": copy(self._client.connection.remote_port), - } - self._result = NatifyResult(**data) + self._result = NatifyResult( + public_ip_addr=self._client.connection.remote_ip, + public_port=self._client.connection.remote_port) def _response_construct(self): self._response = NatifyResponse(self._request, self._result) diff --git a/src/servers/presence_connection_manager/src/aggregates/user_status.py b/src/servers/presence_connection_manager/src/aggregates/user_status.py index ef387b49a..4d21ca870 100644 --- a/src/servers/presence_connection_manager/src/aggregates/user_status.py +++ b/src/servers/presence_connection_manager/src/aggregates/user_status.py @@ -1,9 +1,10 @@ -from dataclasses import dataclass from typing import Union + +from pydantic import BaseModel from servers.presence_connection_manager.src.enums.general import GPStatusCode -class UserStatus: - status_string: str - location_string: str +class UserStatus(BaseModel): + status_string: str = None + location_string: str = None current_status: Union[GPStatusCode, int] = GPStatusCode.OFFLINE diff --git a/src/servers/presence_connection_manager/src/aggregates/user_status_info.py b/src/servers/presence_connection_manager/src/aggregates/user_status_info.py index 553659349..59ed82a48 100644 --- a/src/servers/presence_connection_manager/src/aggregates/user_status_info.py +++ b/src/servers/presence_connection_manager/src/aggregates/user_status_info.py @@ -1,7 +1,8 @@ -from dataclasses import dataclass +from pydantic import BaseModel -class UserStatusInfo: + +class UserStatusInfo(BaseModel): status_state: str buddy_ip: str host_ip: str diff --git a/src/servers/presence_connection_manager/src/contracts/requests/buddy.py b/src/servers/presence_connection_manager/src/contracts/requests/buddy.py index 8eded6150..1d9932b13 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests/buddy.py +++ b/src/servers/presence_connection_manager/src/contracts/requests/buddy.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, final from library.src.extentions.gamespy_utils import convert_to_key_value from servers.presence_connection_manager.src.abstractions.contracts import RequestBase from servers.presence_connection_manager.src.aggregates.user_status import UserStatus @@ -9,6 +9,7 @@ from servers.presence_search_player.src.exceptions.general import GPParseException +@final class AddBuddyRequest(RequestBase): friend_profile_id: int reason: str @@ -29,6 +30,7 @@ def parse(self): self.reason = self.request_dict["reason"] +@final class DelBuddyRequest(RequestBase): friend_profile_id: int @@ -43,6 +45,7 @@ def parse(self): raise GPParseException("delprofileid format is incorrect.") +@final class InviteToRequest(RequestBase): product_id: int profile_id: int @@ -70,9 +73,10 @@ def parse(self): self.session_key = self.request_dict["sesskey"] +@final class StatusInfoRequest(RequestBase): namespace_id: Optional[int] = None - status_info: UserStatusInfo = UserStatusInfo() + status_info: UserStatusInfo profile_id: int = 0 def __init__(self, raw_request: Optional[str] = None) -> None: @@ -115,8 +119,9 @@ def parse(self): self.status_info.game_map_name = self.request_dict["gamemapname"] +@final class StatusRequest(RequestBase): - status: UserStatus = UserStatus() + status: UserStatus is_get_status: bool def parse(self): @@ -134,7 +139,7 @@ def parse(self): try: status_code = int(self.request_dict["status"]) - self.status.current_status = GPStatusCode(status_code) + self.status = UserStatus(current_status=GPStatusCode(status_code)) except ValueError: raise GPParseException("status format is incorrect.") diff --git a/src/servers/presence_connection_manager/src/contracts/requests/general.py b/src/servers/presence_connection_manager/src/contracts/requests/general.py index 59396e36a..049752f06 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests/general.py +++ b/src/servers/presence_connection_manager/src/contracts/requests/general.py @@ -1,3 +1,4 @@ +from typing import final from library.src.extentions.gamespy_utils import is_email_format_correct from library.src.extentions.password_encoder import process_password from servers.presence_connection_manager.src.abstractions.contracts import RequestBase @@ -11,10 +12,12 @@ ) +@final class KeepAliveRequest(RequestBase): pass +@final class LoginRequest(RequestBase): user_challenge: str response: str @@ -115,10 +118,12 @@ def parse_other_data(self): self.quiet_mode_flags = QuietModeType(quiet) +@final class LogoutRequest(RequestBase): pass +@final class NewUserRequest(RequestBase): product_id: int game_port: int diff --git a/src/servers/presence_connection_manager/src/contracts/requests/profile.py b/src/servers/presence_connection_manager/src/contracts/requests/profile.py index ddac02992..eb3d88e85 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests/profile.py +++ b/src/servers/presence_connection_manager/src/contracts/requests/profile.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import Optional, final from library.src.extentions.gamespy_utils import is_valid_date from servers.presence_connection_manager.src.abstractions.contracts import RequestBase from servers.presence_connection_manager.src.enums.general import PublicMasks @@ -7,6 +7,7 @@ ) +@final class AddBlockRequest(RequestBase): taget_id: int @@ -22,6 +23,7 @@ def parse(self): raise GPParseException("profileid format is incorrect") +@final class GetProfileRequest(RequestBase): profile_id: int session_key: str @@ -43,6 +45,7 @@ def parse(self): self.session_key = self.request_dict["sesskey"] +@final class NewProfileRequest(RequestBase): is_replace_nick_name: bool session_key: str @@ -78,6 +81,7 @@ def parse(self): self.is_replace_nick_name = False +@final class RegisterCDKeyRequest(RequestBase): session_key: str cdkey_enc: str @@ -96,6 +100,7 @@ def parse(self): self.cdkey_enc = self.request_dict["cdkeyenc"] +@final class RegisterNickRequest(RequestBase): unique_nick: str session_key: str @@ -121,6 +126,7 @@ def parse(self): raise GPParseException("partnerid is missing") +@final class UpdateProfileRequest(RequestBase): has_public_mask_flag: Optional[bool] = None public_mask: Optional[PublicMasks] = None @@ -222,6 +228,7 @@ def parse(self): self.uniquenick = self.request_dict["uniquenick"] +@final class UpdateUserInfoRequest(RequestBase): cpubrandid: Optional[str] = None cpuspeed: Optional[str] = None diff --git a/src/servers/presence_connection_manager/src/contracts/responses/general.py b/src/servers/presence_connection_manager/src/contracts/responses/general.py index f32a57a3d..7387a4623 100644 --- a/src/servers/presence_connection_manager/src/contracts/responses/general.py +++ b/src/servers/presence_connection_manager/src/contracts/responses/general.py @@ -1,4 +1,4 @@ -from servers.presence_connection_manager.src.abstractions.contracts import RequestBase, ResponseBase, ResultBase +from servers.presence_connection_manager.src.abstractions.contracts import ResponseBase from servers.presence_connection_manager.src.applications.client import ( LOGIN_TICKET, SESSION_KEY, diff --git a/src/servers/presence_connection_manager/src/contracts/results/general.py b/src/servers/presence_connection_manager/src/contracts/results/general.py index 60e65d498..05a130e44 100644 --- a/src/servers/presence_connection_manager/src/contracts/results/general.py +++ b/src/servers/presence_connection_manager/src/contracts/results/general.py @@ -1,7 +1,8 @@ +from pydantic import BaseModel from servers.presence_connection_manager.src.abstractions.contracts import ResultBase -class LoginDataModel: +class LoginDataModel(BaseModel): user_id: int profile_id: int nick: str diff --git a/src/servers/presence_connection_manager/src/contracts/results/profile.py b/src/servers/presence_connection_manager/src/contracts/results/profile.py index 3530b265e..e21aafe66 100644 --- a/src/servers/presence_connection_manager/src/contracts/results/profile.py +++ b/src/servers/presence_connection_manager/src/contracts/results/profile.py @@ -1,8 +1,10 @@ from typing import Optional + +from pydantic import BaseModel from servers.presence_connection_manager.src.abstractions.contracts import ResultBase -class GetProfileDataModel: +class GetProfileDataModel(BaseModel): nick: Optional[str] = None profile_id: Optional[int] = None unique_nick: Optional[str] = None diff --git a/src/servers/presence_connection_manager/src/handlers/buddy.py b/src/servers/presence_connection_manager/src/handlers/buddy.py index 6d1e4fb22..30de4e86a 100644 --- a/src/servers/presence_connection_manager/src/handlers/buddy.py +++ b/src/servers/presence_connection_manager/src/handlers/buddy.py @@ -98,7 +98,7 @@ def __init__(self, client: Client, request: RequestBase) -> None: class StatusHandler(CmdHandlerBase): _request: StatusRequest - _result: StatusResult = StatusResult() + _result: StatusResult def __init__(self, client: Client, request: StatusRequest) -> None: assert isinstance(request, StatusRequest) @@ -111,7 +111,7 @@ def _response_send(self) -> None: class StatusInfoHandler(LoginedHandlerBase): _request: StatusInfoRequest - _result: StatusInfoResult = StatusInfoResult() + _result: StatusInfoResult def __init__(self, client: Client, request: StatusInfoRequest) -> None: assert isinstance(request, StatusInfoRequest) diff --git a/src/servers/presence_connection_manager/src/handlers/general.py b/src/servers/presence_connection_manager/src/handlers/general.py index 885291c84..7f7a4346e 100644 --- a/src/servers/presence_connection_manager/src/handlers/general.py +++ b/src/servers/presence_connection_manager/src/handlers/general.py @@ -35,9 +35,9 @@ def _response_construct(self) -> None: class LoginHandler(servers.presence_connection_manager.src.abstractions.handlers.CmdHandlerBase): _request: LoginRequest - _result: LoginResult + _result_cls: type[LoginResult] = LoginResult - def __init__(self, client: Client, request: LoginRequest) -> None: + def __init__(self, client: "Client", request: LoginRequest) -> None: assert isinstance(request, LoginRequest) super().__init__(client, request) @@ -48,7 +48,7 @@ def _response_construct(self) -> None: class LogoutHandler(servers.presence_connection_manager.src.abstractions.handlers.LoginedHandlerBase): _request: LogoutRequest - def __init__(self, client: Client, request: LogoutRequest) -> None: + def __init__(self, client: "Client", request: LogoutRequest) -> None: assert isinstance(request, LogoutRequest) super().__init__(client, request) @@ -62,7 +62,7 @@ def _response_construct(self): class SdkRevisionHandler(servers.presence_connection_manager.src.abstractions.handlers.CmdHandlerBase): _request: LoginRequest - def __init__(self, client: Client, request: LoginRequest) -> None: + def __init__(self, client: "Client", request: LoginRequest) -> None: assert isinstance(request, LoginRequest) super().__init__(client, request) diff --git a/src/servers/presence_connection_manager/src/handlers/profile.py b/src/servers/presence_connection_manager/src/handlers/profile.py index 451970920..19e2ffcb2 100644 --- a/src/servers/presence_connection_manager/src/handlers/profile.py +++ b/src/servers/presence_connection_manager/src/handlers/profile.py @@ -1,4 +1,4 @@ -from servers.chat.src.contracts.requests.general import RegisterNickRequest +from typing import final from servers.presence_connection_manager.src.abstractions.contracts import RequestBase from servers.presence_connection_manager.src.abstractions.handlers import CmdHandlerBase from servers.presence_connection_manager.src.applications.client import Client @@ -7,6 +7,7 @@ GetProfileRequest, NewProfileRequest, RegisterCDKeyRequest, + RegisterNickRequest, UpdateProfileRequest, ) from servers.presence_connection_manager.src.contracts.responses.profile import ( @@ -16,7 +17,7 @@ ) from servers.presence_connection_manager.src.contracts.results.profile import NewProfileResult - +@final class AddBlockHandler(CmdHandlerBase): _request: AddBlockRequest @@ -25,6 +26,7 @@ def __init__(self, client: Client, request: AddBlockRequest) -> None: super().__init__(client, request) +@final class GetProfileHandler(CmdHandlerBase): _request: GetProfileRequest _result: GetProfileResponse @@ -37,6 +39,7 @@ def _response_construct(self) -> None: self._response = GetProfileResponse(self._request, self._result) +@final class NewProfileHandler(CmdHandlerBase): _request: NewProfileRequest _result: NewProfileResult @@ -49,6 +52,7 @@ def _response_construct(self) -> None: self._response = NewProfileResponse(self._request, self._result) +@final class RegisterCDKeyHandler(CmdHandlerBase): _request: RegisterCDKeyRequest @@ -57,8 +61,8 @@ def __init__(self, client: Client, request: RegisterCDKeyRequest) -> None: super().__init__(client, request) +@final class RegisterNickHandler(CmdHandlerBase): - _request: RegisterNickRequest def __init__(self, client: Client, request: RegisterNickRequest) -> None: @@ -69,13 +73,14 @@ def _response_construct(self) -> None: self._response = RegisterNickResponse(self._request, self._result) +@final class RemoveBlockHandler(CmdHandlerBase): def __init__(self, client: Client, request: RequestBase) -> None: - raise NotImplementedError() - super().__init__(client, request) + raise NotImplementedError() +@final class UpdateProfileHandler(CmdHandlerBase): _request: UpdateProfileRequest @@ -84,5 +89,8 @@ def __init__(self, client: Client, request: UpdateProfileRequest) -> None: super().__init__(client, request) +@final class UpdateUserInfoHandler(CmdHandlerBase): - raise NotImplementedError() + def __init__(self, client: Client, request: RequestBase) -> None: + super().__init__(client, request) + raise NotImplementedError() diff --git a/src/servers/presence_connection_manager/src/handlers/switcher.py b/src/servers/presence_connection_manager/src/handlers/switcher.py index e78cce7e1..f859bfd9b 100644 --- a/src/servers/presence_connection_manager/src/handlers/switcher.py +++ b/src/servers/presence_connection_manager/src/handlers/switcher.py @@ -1,17 +1,13 @@ from library.src.abstractions.switcher import SwitcherBase -from servers.chat.src.contracts.requests.general import LoginRequest, RegisterNickRequest -from servers.presence_connection_manager.src.handlers.general import LoginHandler - from servers.presence_connection_manager.src.contracts.requests.buddy import StatusInfoRequest, StatusRequest -from servers.presence_connection_manager.src.contracts.requests.general import KeepAliveRequest, LogoutRequest -from servers.presence_connection_manager.src.contracts.requests.profile import AddBlockRequest, GetProfileRequest, NewProfileRequest, RegisterCDKeyRequest, UpdateProfileRequest +from servers.presence_connection_manager.src.contracts.requests.general import KeepAliveRequest, LoginRequest, LogoutRequest +from servers.presence_connection_manager.src.contracts.requests.profile import AddBlockRequest, GetProfileRequest, NewProfileRequest, RegisterCDKeyRequest, RegisterNickRequest, UpdateProfileRequest from servers.presence_connection_manager.src.handlers.buddy import StatusHandler, StatusInfoHandler -from servers.presence_connection_manager.src.handlers.general import KeepAliveHandler, LogoutHandler, NewUserHandler +from servers.presence_connection_manager.src.handlers.general import KeepAliveHandler, LoginHandler, LogoutHandler, NewUserHandler from servers.presence_connection_manager.src.handlers.profile import AddBlockHandler, GetProfileHandler, NewProfileHandler, RegisterCDKeyHandler, RegisterNickHandler, UpdateProfileHandler from servers.presence_search_player.src.contracts.requests import NewUserRequest -from servers.presence_search_player.src.exceptions.general import ( - GPParseException, -) +from servers.presence_search_player.src.exceptions.general import GPParseException + from servers.presence_search_player.src.abstractions.handler import CmdHandlerBase from typing import Optional from servers.presence_connection_manager.src.applications.client import Client @@ -28,7 +24,7 @@ def __init__(self, client: Client, raw_request: str) -> None: def _process_raw_request(self) -> None: if self._raw_request[0] != "\\": raise GPParseException("Request format is invalid") - raw_requests = [r for r in self._raw_request.split("\\final\\") if r] + raw_requests = [r+"\\final\\" for r in self._raw_request.split("\\final\\") if r] for raw_request in raw_requests: name = raw_request.strip("\\").split("\\")[0] self._requests.append((name, raw_request)) diff --git a/src/servers/presence_connection_manager/tests/game_tests.py b/src/servers/presence_connection_manager/tests/game_tests.py index 880f2e300..99ba22cec 100644 --- a/src/servers/presence_connection_manager/tests/game_tests.py +++ b/src/servers/presence_connection_manager/tests/game_tests.py @@ -17,8 +17,8 @@ def test_civilization_4(self) -> None: pass def test_conflict_global_storm(self) -> None: + # "\\lc\\1\\challenge\\NRNUJLZMLX\\id\\1\\final\\", raw_requests = [ - "\\lc\\1\\challenge\\NRNUJLZMLX\\id\\1\\final\\", "\\login\\\\challenge\\KMylyQbZfqzKn9otxx32q4076sOUnKif\\user\\cgs1@cgs1@rs.de\\response\\c1a6638bbcfe130e4287bfe4aa792949\\port\\-15737\\productid\\10469\\gamename\\conflictsopc\\namespaceid\\1\\id\\1\\final\\", "\\inviteto\\\\sesskey\\58366\\products\\1038\\final\\", ] diff --git a/src/servers/presence_connection_manager/tests/mock_objects.py b/src/servers/presence_connection_manager/tests/mock_objects.py index e2d2e864e..d9ca5243f 100644 --- a/src/servers/presence_connection_manager/tests/mock_objects.py +++ b/src/servers/presence_connection_manager/tests/mock_objects.py @@ -1,7 +1,11 @@ from library.src.unispy_server_config import CONFIG from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock -from servers.natneg.tests.mock_objects import ClientMock +from servers.presence_connection_manager.src.applications.client import Client + + +class ClientMock(Client): + pass def create_client(): diff --git a/src/servers/presence_search_player/src/handlers/switcher.py b/src/servers/presence_search_player/src/handlers/switcher.py index ba20cd721..f664379f3 100644 --- a/src/servers/presence_search_player/src/handlers/switcher.py +++ b/src/servers/presence_search_player/src/handlers/switcher.py @@ -10,7 +10,6 @@ class CmdSwitcher(SwitcherBase): - _raw_request: str def __init__(self, client: Client, raw_request: str): super().__init__(client, raw_request) From 2d4554e5854876c82bc944fd4e5d18bc98942f85 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 5 Sep 2024 06:36:43 +0000 Subject: [PATCH 104/231] refactor: added unimplemented routers --- src/backends/routers/gamespy/chat.py | 33 --------- src/backends/routers/gamespy/gstats.py | 46 ++++++++----- src/backends/routers/gamespy/natneg.py | 44 ++++++++++++ .../gamespy/presence_connection_manager.py | 69 +++++++++++++++++-- .../routers/gamespy/presence_search_player.py | 61 ++++++++++++++++ src/backends/routers/gamespy/query_report.py | 42 +++++++++++ .../routers/gamespy/server_browser.py | 28 ++++++++ src/backends/routers/gamespy/webservices.py | 64 +++++++++++++++-- src/backends/urls.py | 20 +++--- .../src/v2/handlers/handlers.py | 15 ++-- 10 files changed, 346 insertions(+), 76 deletions(-) diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 72502fb63..49f12ea8b 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -4,38 +4,5 @@ # app = FastAPI() -# @app.websocket(f"/{CHAT}/join") -# def join(request: "JoinRequest"): -# # directly send the irc chat raw message to the channel -# raise NotImplementedError() -# @app.websocket(f"/{CHAT}/setckey") -# def setckey(request: "SetCKeyRequest"): -# sender = session.get("nickname") -# raise NotImplementedError() - - -# @app.websocket(f"/{CHAT}/setchankey") -# def set_channel_key(request: "SetChannelKeyRequest"): -# raise NotImplementedError() - - -# @app.websocket(f"/{CHAT}/atm") -# def atm(request: "ATMRequest"): -# raise NotImplementedError() - - -# @app.websocket(f"/{CHAT}/utm") -# def utm(request: "UTMRequest"): -# raise NotImplementedError() - - -# @app.websocket(f"/{CHAT}/notice") -# def notice(request: "UTMRequest"): -# raise NotImplementedError() - - -# @app.websocket(f"/{CHAT}/private") -# def notice(request: "PrivateRequest"): -# raise NotImplementedError() diff --git a/src/backends/routers/gamespy/gstats.py b/src/backends/routers/gamespy/gstats.py index 81873390f..5fecf1da0 100644 --- a/src/backends/routers/gamespy/gstats.py +++ b/src/backends/routers/gamespy/gstats.py @@ -1,24 +1,38 @@ -from typing import Annotated -import uvicorn +from fastapi import FastAPI +from backends.protocols.gamespy.game_status.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest +from backends.urls import GAMESTATUS +app = FastAPI() -from fastapi import Body, FastAPI -from pydantic import BaseModel, Field -app = FastAPI() +@app.post(f"{GAMESTATUS}/AuthGameHandler/") +async def update_item(request: AuthGameRequest): + raise NotImplementedError() + + +@app.post(f"{GAMESTATUS}/AuthPlayerHandler/") +async def update_item(request: AuthPlayerRequest): + raise NotImplementedError() + + +@app.post(f"{GAMESTATUS}/NewGameHandler/") +async def update_item(request: NewGameRequest): + raise NotImplementedError() + + +@app.post(f"{GAMESTATUS}/GetPlayerDataHandler/") +async def update_item(request: GetPlayerDataRequest): + raise NotImplementedError() -class Item(BaseModel): - name: str - description: str | None = None - price: float - tax: float | None = None - tags: list = [] +@app.post(f"{GAMESTATUS}/SetPlayerDataHandler/") +async def update_item(request: SetPlayerDataRequest): + raise NotImplementedError() -@app.put("/items/{item_id}") -async def update_item(item_id: int, item: Annotated[Item, Body(embed=True)]): - results = {"item_id": item_id, "item": item} - return results +@app.post(f"{GAMESTATUS}/UpdateGameHandler/") +async def update_item(request: UpdateGameRequest): + raise NotImplementedError() if __name__ == "__main__": - uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) diff --git a/src/backends/routers/gamespy/natneg.py b/src/backends/routers/gamespy/natneg.py index b594b9a81..8f0f21303 100644 --- a/src/backends/routers/gamespy/natneg.py +++ b/src/backends/routers/gamespy/natneg.py @@ -1 +1,45 @@ from fastapi import FastAPI + +from backends.protocols.gamespy.chat.requests import PingRequest +from backends.protocols.gamespy.natneg.requests import ConnectRequest, ErtAckRequest, InitRequest, ReportRequest +from backends.urls import NATNEG +from servers.natneg.src.contracts.requests import AddressCheckRequest + + +app = FastAPI() + + +@app.post(f"{NATNEG}/AddressCheckHandler/") +async def address_check(request: AddressCheckRequest): + raise NotImplementedError() + + +@app.post(f"{NATNEG}/ConnectHandler/") +async def connect(request: ConnectRequest): + raise NotImplementedError() + + +@app.post(f"{NATNEG}/ErtAckHandler/") +async def ert_ack(request: ErtAckRequest): + raise NotImplementedError() + + +@app.post(f"{NATNEG}/InitHandler/") +async def init(request: InitRequest): + raise NotImplementedError() + + +@app.post(f"{NATNEG}/PingHandler/") +async def ping(request: PingRequest): + raise NotImplementedError() + + +@app.post(f"{NATNEG}/ReportHandler/") +async def report(request: ReportRequest): + raise NotImplementedError() + + + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/src/backends/routers/gamespy/presence_connection_manager.py b/src/backends/routers/gamespy/presence_connection_manager.py index 94424ea52..d0dd511ae 100644 --- a/src/backends/routers/gamespy/presence_connection_manager.py +++ b/src/backends/routers/gamespy/presence_connection_manager.py @@ -1,18 +1,79 @@ from fastapi import FastAPI -from backends.protocols.gamespy.presence_search_player.requests import LoginRequest +from backends.protocols.gamespy.chat.requests import RegisterNickRequest +from backends.protocols.gamespy.presence_connection_manager.requests import GetProfileRequest, LoginRequest, LogoutRequest, NewProfileRequest, RegisterCDKeyRequest, StatusInfoRequest, StatusRequest, UpdateProfileRequest from backends.urls import * -import uvicorn +from servers.presence_connection_manager.src.contracts.requests.general import KeepAliveRequest +from servers.presence_connection_manager.src.contracts.requests.profile import AddBlockRequest +from servers.presence_search_player.src.contracts.requests import NewUserRequest app = FastAPI() -@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/login") +@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/LoginHandler") def login(request: LoginRequest): + raise NotImplementedError() - pass +@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/LogoutHandler") +def logout(request: LogoutRequest): + raise NotImplementedError() +@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/KeepAliveHandler") +def keep_alive(request: KeepAliveRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/NewUserHandler") +def new_user(request: NewUserRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/AddBlockHandler") +def add_block(request: AddBlockRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/GetProfileHandler") +def get_profile(request: GetProfileRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/NewProfileHandler") +def new_proflie(request: NewProfileRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/RegisterCDKeyHandler") +def register_cdkey(request: RegisterCDKeyRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/RegisterNickHandler") +def register_nick(request: RegisterNickRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/UpdateProfileHandler") +def update_profile(request: UpdateProfileRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/StatusHandler") +def status(request: StatusRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/StatusInfoHandler") +def status_info(request: StatusInfoRequest): + raise NotImplementedError() + + + + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/src/backends/routers/gamespy/presence_search_player.py b/src/backends/routers/gamespy/presence_search_player.py index b594b9a81..fd6bd298b 100644 --- a/src/backends/routers/gamespy/presence_search_player.py +++ b/src/backends/routers/gamespy/presence_search_player.py @@ -1 +1,62 @@ from fastapi import FastAPI + +from backends.protocols.gamespy.presence_search_player.requests import CheckRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest +from backends.urls import PRESENCE_SEARCH_PLAYER +from servers.presence_search_player.src.contracts.requests import NewUserRequest + +app = FastAPI() + + +@app.post(f"/{PRESENCE_SEARCH_PLAYER}/CheckHandler") +def check(request: CheckRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_SEARCH_PLAYER}/NewUserHandler") +def new_user(request: NewUserRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_SEARCH_PLAYER}/NicksHandler") +def nicks(request: NicksRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_SEARCH_PLAYER}/OthersHandler") +def others(request: OthersRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_SEARCH_PLAYER}/OthersListHandler") +def others_list(request: OthersListRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_SEARCH_PLAYER}/PMatchHandler") +def player_match(request: object): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_SEARCH_PLAYER}/SearchHandler") +def search(request: SearchRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_SEARCH_PLAYER}/SearchUniqueHandler") +def search_unique(request: SearchUniqueRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_SEARCH_PLAYER}/UniqueSearchHandler") +def unique_search(request: UniqueSearchRequest): + raise NotImplementedError() + + +@app.post(f"/{PRESENCE_SEARCH_PLAYER}/ValidHandler") +def valid(request: ValidRequest): + raise NotImplementedError() + + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index b594b9a81..f43c54db5 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -1 +1,43 @@ from fastapi import FastAPI + +from backends.protocols.gamespy.query_report.requests import ChallengeRequest, ClientMessageRequest, EchoRequest, HeartBeatRequest +from backends.urls import QUERY_REPORT +from servers.presence_connection_manager.src.contracts.requests.general import KeepAliveRequest +from servers.query_report.src.v2.contracts.requests import AvaliableRequest + +app = FastAPI() + + +@app.post(f"/{QUERY_REPORT}/HeartBeatHandler") +def heartbeat(request: HeartBeatRequest): + raise NotImplementedError() + + +@app.post(f"/{QUERY_REPORT}/ChallengeHanler") +def challenge(request: ChallengeRequest): + raise NotImplementedError() + + +@app.post(f"/{QUERY_REPORT}/AvailableHandler") +def available(request: AvaliableRequest): + raise NotImplementedError() + + +@app.post(f"/{QUERY_REPORT}/ClientMessageAckHandler") +def client_message(request: ClientMessageRequest): + raise NotImplementedError() + + +@app.post(f"/{QUERY_REPORT}/EchoHandler") +def echo(request: EchoRequest): + raise NotImplementedError() + + +@app.post(f"/{QUERY_REPORT}/KeepAliveHandler") +def keep_alive(request: KeepAliveRequest): + raise NotImplementedError() + + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) diff --git a/src/backends/routers/gamespy/server_browser.py b/src/backends/routers/gamespy/server_browser.py index b594b9a81..205028b3d 100644 --- a/src/backends/routers/gamespy/server_browser.py +++ b/src/backends/routers/gamespy/server_browser.py @@ -1 +1,29 @@ from fastapi import FastAPI +from backends.protocols.gamespy.server_browser.requests import SendMessageRequest, ServerInfoRequest, ServerListRequest +from backends.urls import SERVER_BROWSER_V1, SERVER_BROWSER_V2 +app = FastAPI + +# todo maybe implement this in websocket way +# @app.post(f"/{SERVER_BROWSER_V2}/AdHocHandler") +# def check(request: ADHocRequest): +# raise NotImplementedError() + + +@app.post(f"/{SERVER_BROWSER_V2}/SendMessageHandler") +def send_message(request: SendMessageRequest): + raise NotImplementedError() + + +@app.post(f"/{SERVER_BROWSER_V2}/ServerInfoHandler") +def server_info(request: ServerInfoRequest): + raise NotImplementedError() + + +@app.post(f"/{SERVER_BROWSER_V2}/ServerListHandler") +def server_list(request: ServerListRequest): + raise NotImplementedError() + + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) diff --git a/src/backends/routers/gamespy/webservices.py b/src/backends/routers/gamespy/webservices.py index 1123b9495..2d5d3fc3f 100644 --- a/src/backends/routers/gamespy/webservices.py +++ b/src/backends/routers/gamespy/webservices.py @@ -1,13 +1,69 @@ from fastapi import FastAPI -from backends.urls import GAMESPY_PREFIX, WEB_SERVICES +from backends.urls import WEB_SERVICES -URL = f"{GAMESPY_PREFIX}/{WEB_SERVICES}" app = FastAPI() -# SAKE services +# Altas services + + +@app.post(f"{WEB_SERVICES}/Altas/CreateRecordHandler") +def create_matchless_session(request): + raise NotImplementedError() + + +@app.post(f"{WEB_SERVICES}/Altas/CreateSessionHandler") +def create_session(request): + raise NotImplementedError() + + +@app.post(f"{WEB_SERVICES}/Altas/SetReportIntentionHandler") +def set_report_intention(request): + raise NotImplementedError() + + +@app.post(f"{WEB_SERVICES}/Altas/SubmitReportHandler") +def submit_report(request): + raise NotImplementedError() + + +# Auth services +@app.post(f"{WEB_SERVICES}/Auth/LoginProfileHandler") +def submit_report(request: LoginProfileRequest): + raise NotImplementedError() + + +@app.post(f"{WEB_SERVICES}/Auth/LoginProfileWithGameIdHandler") +def submit_report(request:LoginProfileWithGameIdRequest): + raise NotImplementedError() + + +@app.post(f"{WEB_SERVICES}/Auth/LoginRemoteAuthHandler") +def submit_report(request:LoginRemoteAuthRequest): + raise NotImplementedError() -@app.post(f"/{URL}/CreateRecord") +@app.post(f"{WEB_SERVICES}/Auth/LoginRemoteAuthWithGameIdHandler") +def submit_report(request: LoginRemoteAuthWithGameIdRequest): + raise NotImplementedError() + + +@app.post(f"{WEB_SERVICES}/Auth/LoginUniqueNickHandler") +def submit_report(request:LoginUniqueNickRequest): + raise NotImplementedError() + + +@app.post(f"{WEB_SERVICES}/Auth/LoginUniqueNickWithGameIdHandler") +def submit_report(request:LoginUniqueNickWithGameIdRequest): + raise NotImplementedError() + + +# SAKE services +@app.post(f"{WEB_SERVICES}/Sake/CreateRecordHandler") def create_record(request): raise NotImplementedError() + + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8000) diff --git a/src/backends/urls.py b/src/backends/urls.py index f9a8167aa..5bb82553c 100644 --- a/src/backends/urls.py +++ b/src/backends/urls.py @@ -1,11 +1,9 @@ -GAMESPY_PREFIX = "GameSpy" - -PRESENCE_CONNECTION_MANAGER = f"{GAMESPY_PREFIX}/PCM/" -PRESENCE_SEARCH_PLAYER = f"{GAMESPY_PREFIX}/PSP/" -SERVER_BROWSER_V1 = f"{GAMESPY_PREFIX}/SB_V1/" -SERVER_BROWSER_V2 = f"{GAMESPY_PREFIX}/SB_V2/" -QUERY_REPORT = f"{GAMESPY_PREFIX}/QR/" -NATNEG = f"{GAMESPY_PREFIX}/NN/" -GAMESTATUS = f"{GAMESPY_PREFIX}/GS/" -CHAT = f"{GAMESPY_PREFIX}/Chat/" -WEB_SERVICES = f"{GAMESPY_PREFIX}/WEB/" +PRESENCE_CONNECTION_MANAGER = "/GameSpy/PCM" +PRESENCE_SEARCH_PLAYER = "/GameSpy/PSP" +SERVER_BROWSER_V1 = "/GameSpy/SB_V1" +SERVER_BROWSER_V2 = "/GameSpy/SB_V2" +QUERY_REPORT = "/GameSpy/QR" +NATNEG = "/GameSpy/NN" +GAMESTATUS = "/GameSpy/GS" +CHAT = "/GameSpy/Chat" +WEB_SERVICES = "/GameSpy/WEB" diff --git a/src/servers/server_browser/src/v2/handlers/handlers.py b/src/servers/server_browser/src/v2/handlers/handlers.py index 4f9eb4cb5..805774649 100644 --- a/src/servers/server_browser/src/v2/handlers/handlers.py +++ b/src/servers/server_browser/src/v2/handlers/handlers.py @@ -29,10 +29,6 @@ from servers.server_browser.src.v2.applications.client import Client -def get_clients(ss): - raise NotImplementedError() - - class AdHocHandler(CmdHandlerBase): _message: GameServerInfo @@ -69,7 +65,7 @@ def send_message(self, client: Client): ) ): client.log_info( - f"Sending AdHoc message { self._message.status} to client" + f"Sending AdHoc message {self._message.status} to client" ) client.send(self.response) @@ -121,12 +117,15 @@ def response_construct(self): ServerListUpdateOption.LIMIT_RESULT_COUNT, ServerListUpdateOption.SERVER_FULL_INFO_LIST, ]: - self._response = ServerMainListResponse(self._request, self._result) + self._response = ServerMainListResponse( + self._request, self._result) case ServerListUpdateOption.P2P_GROUP_ROOM_LIST: - self._response = P2PGroupRoomListResponse(self._request, self._result) + self._response = P2PGroupRoomListResponse( + self._request, self._result) case ServerListUpdateOption.SERVER_FULL_INFO_LIST: self._response = ServerNetworkInfoListResponse( self._request, self._result ) case _: - raise ServerBrowserException("unknown serverlist update option type") + raise ServerBrowserException( + "unknown serverlist update option type") From 881752b0b121a3d5f3dba8063293db0d59b11adb Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 5 Sep 2024 08:54:42 +0000 Subject: [PATCH 105/231] refactor: added server launcher --- .../src/applications/server_launcher.py | 20 ++++++++++++++++++ .../src/applications/server_launcher.py | 20 ++++++++++++++++++ .../src/applications/server_launcher.py | 19 +++++++++++++++++ .../src/applications/server_launcher.py | 19 +++++++++++++++++ .../src/applications/server_launcher.py | 19 +++++++++++++++++ .../src/v2/applications/__init__.py | 0 .../src/v2/applications/server_launcher.py | 19 +++++++++++++++++ .../src/v2/handlers/handlers.py | 1 + .../web_services/src/applications/client.py | 7 ++++++- .../src/applications/server_launcher.py | 21 +++++++++++++++++++ .../web_services/src/handlers/switcher.py | 8 +++++++ 11 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 src/servers/game_status/src/applications/server_launcher.py create mode 100644 src/servers/natneg/src/applications/server_launcher.py create mode 100644 src/servers/presence_connection_manager/src/applications/server_launcher.py create mode 100644 src/servers/presence_search_player/src/applications/server_launcher.py create mode 100644 src/servers/query_report/src/applications/server_launcher.py delete mode 100644 src/servers/server_browser/src/v2/applications/__init__.py create mode 100644 src/servers/server_browser/src/v2/applications/server_launcher.py create mode 100644 src/servers/web_services/src/applications/server_launcher.py diff --git a/src/servers/game_status/src/applications/server_launcher.py b/src/servers/game_status/src/applications/server_launcher.py new file mode 100644 index 000000000..638d1d909 --- /dev/null +++ b/src/servers/game_status/src/applications/server_launcher.py @@ -0,0 +1,20 @@ +from library.src.abstractions.server_launcher_base import ServerLauncherBase +from library.src.network.tcp_handler import TcpServer +from library.src.unispy_server_config import CONFIG +from servers.chat.src.applications.client import Client + + +class ServerLauncher(ServerLauncherBase): + server: "TcpServer" + + def __init__(self) -> None: + super().__init__() + self.config = CONFIG.servers["GameStatus"] + + def _launch_server(self): + TcpServer(self.config, Client).start() + + +if __name__ == "__main__": + s = ServerLauncher() + s.start() diff --git a/src/servers/natneg/src/applications/server_launcher.py b/src/servers/natneg/src/applications/server_launcher.py new file mode 100644 index 000000000..6e41d481f --- /dev/null +++ b/src/servers/natneg/src/applications/server_launcher.py @@ -0,0 +1,20 @@ +from library.src.abstractions.server_launcher_base import ServerLauncherBase +from library.src.network.udp_handler import UdpServer +from library.src.unispy_server_config import CONFIG +from servers.natneg.src.applications.client import Client + + +class ServerLauncher(ServerLauncherBase): + server: UdpServer + + def __init__(self) -> None: + super().__init__() + self.config = CONFIG.servers["NatNegotiation"] + + def _launch_server(self): + UdpServer(self.config, Client).start() + + +if __name__ == "__main__": + s = ServerLauncher() + s.start() diff --git a/src/servers/presence_connection_manager/src/applications/server_launcher.py b/src/servers/presence_connection_manager/src/applications/server_launcher.py new file mode 100644 index 000000000..da7a97135 --- /dev/null +++ b/src/servers/presence_connection_manager/src/applications/server_launcher.py @@ -0,0 +1,19 @@ +from library.src.abstractions.server_launcher_base import ServerLauncherBase +from library.src.network.udp_handler import UdpServer +from library.src.unispy_server_config import CONFIG +from servers.presence_connection_manager.src.applications.client import Client + + +class ServerLauncher(ServerLauncherBase): + + def __init__(self) -> None: + super().__init__() + self.config = CONFIG.servers["PresenceConnectionManager"] + + def _launch_server(self): + UdpServer(self.config, Client).start() + + +if __name__ == "__main__": + s = ServerLauncher() + s.start() diff --git a/src/servers/presence_search_player/src/applications/server_launcher.py b/src/servers/presence_search_player/src/applications/server_launcher.py new file mode 100644 index 000000000..7983067ed --- /dev/null +++ b/src/servers/presence_search_player/src/applications/server_launcher.py @@ -0,0 +1,19 @@ +from library.src.abstractions.server_launcher_base import ServerLauncherBase +from library.src.network.tcp_handler import TcpServer +from library.src.unispy_server_config import CONFIG +from servers.presence_search_player.src.applications.client import Client + + +class ServerLauncher(ServerLauncherBase): + + def __init__(self) -> None: + super().__init__() + self.config = CONFIG.servers["PresenceSearchPlayer"] + + def _launch_server(self): + TcpServer(self.config, Client).start() + + +if __name__ == "__main__": + s = ServerLauncher() + s.start() diff --git a/src/servers/query_report/src/applications/server_launcher.py b/src/servers/query_report/src/applications/server_launcher.py new file mode 100644 index 000000000..9735086cf --- /dev/null +++ b/src/servers/query_report/src/applications/server_launcher.py @@ -0,0 +1,19 @@ +from library.src.abstractions.server_launcher_base import ServerLauncherBase +from library.src.network.udp_handler import UdpServer +from library.src.unispy_server_config import CONFIG +from servers.presence_search_player.src.applications.client import Client + + +class ServerLauncher(ServerLauncherBase): + + def __init__(self) -> None: + super().__init__() + self.config = CONFIG.servers["QueryReport"] + + def _launch_server(self): + UdpServer(self.config, Client).start() + + +if __name__ == "__main__": + s = ServerLauncher() + s.start() diff --git a/src/servers/server_browser/src/v2/applications/__init__.py b/src/servers/server_browser/src/v2/applications/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/server_browser/src/v2/applications/server_launcher.py b/src/servers/server_browser/src/v2/applications/server_launcher.py new file mode 100644 index 000000000..e78074d4a --- /dev/null +++ b/src/servers/server_browser/src/v2/applications/server_launcher.py @@ -0,0 +1,19 @@ +from library.src.abstractions.server_launcher_base import ServerLauncherBase +from library.src.network.tcp_handler import TcpServer +from library.src.unispy_server_config import CONFIG +from servers.server_browser.src.v2.applications.client import Client + + +class ServerLauncher(ServerLauncherBase): + + def __init__(self) -> None: + super().__init__() + self.config = CONFIG.servers["ServerBrowserV2"] + + def _launch_server(self): + TcpServer(self.config, Client).start() + + +if __name__ == "__main__": + s = ServerLauncher() + s.start() diff --git a/src/servers/server_browser/src/v2/handlers/handlers.py b/src/servers/server_browser/src/v2/handlers/handlers.py index 805774649..d109fe6f7 100644 --- a/src/servers/server_browser/src/v2/handlers/handlers.py +++ b/src/servers/server_browser/src/v2/handlers/handlers.py @@ -31,6 +31,7 @@ class AdHocHandler(CmdHandlerBase): _message: GameServerInfo + # !fix this def __init__(self, message: GameServerInfo) -> None: self._log_current_class() diff --git a/src/servers/web_services/src/applications/client.py b/src/servers/web_services/src/applications/client.py index 975866a85..ff92df23b 100644 --- a/src/servers/web_services/src/applications/client.py +++ b/src/servers/web_services/src/applications/client.py @@ -30,6 +30,11 @@ class ClientInfo(ClientInfoBase): class Client(ClientBase): info: ClientInfo - def create_switcher(self, buffer: HttpRequest) -> SwitcherBase: + def on_received(self, buffer: str) -> None: + assert isinstance(buffer, str) + super().on_received(buffer) + + def create_switcher(self, buffer: str) -> SwitcherBase: + assert isinstance(buffer, str) from servers.web_services.src.handlers.switcher import Switcher return Switcher(self, buffer) diff --git a/src/servers/web_services/src/applications/server_launcher.py b/src/servers/web_services/src/applications/server_launcher.py new file mode 100644 index 000000000..fa7ee5369 --- /dev/null +++ b/src/servers/web_services/src/applications/server_launcher.py @@ -0,0 +1,21 @@ +from library.src.abstractions.server_launcher_base import ServerLauncherBase +from library.src.network.http_handler import HttpServer +from library.src.unispy_server_config import CONFIG +from servers.web_services.src.applications import client + + +class ServerLauncher(ServerLauncherBase): + server: "HttpServer" + + def __init__(self) -> None: + super().__init__() + self.config = CONFIG.servers["WebServices"] + + def _launch_server(self): + HttpServer(self.config, client).start() + + +if __name__ == "__main__": + + s = ServerLauncher() + s.start() diff --git a/src/servers/web_services/src/handlers/switcher.py b/src/servers/web_services/src/handlers/switcher.py index 843481598..736206c28 100644 --- a/src/servers/web_services/src/handlers/switcher.py +++ b/src/servers/web_services/src/handlers/switcher.py @@ -1,3 +1,4 @@ +from library.src.abstractions.client import ClientBase from library.src.abstractions.handler import CmdHandlerBase from library.src.abstractions.switcher import SwitcherBase import xml.etree.ElementTree as ET @@ -12,6 +13,13 @@ class Switcher(SwitcherBase): + _raw_request: str + + def __init__(self, client: ClientBase, raw_request: str) -> None: + assert isinstance(raw_request, str) + # assert isinstance(client,Client) + super().__init__(client, raw_request) + def _process_raw_request(self) -> None: name_node = ET.fromstring(self._raw_request)[0][0] if name_node is None: From 726bf6bfcc19c6f06e2feec4e76f81ed0126bdfd Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 5 Sep 2024 08:55:09 +0000 Subject: [PATCH 106/231] refactor: added backends home router --- src/backends/routers/gamespy/gstats.py | 18 +++--- src/backends/routers/gamespy/natneg.py | 19 +++---- .../gamespy/presence_connection_manager.py | 56 +++++++++---------- .../routers/gamespy/presence_search_player.py | 46 +++++++-------- src/backends/routers/gamespy/query_report.py | 30 +++++----- .../routers/gamespy/server_browser.py | 33 +++++++---- src/backends/routers/gamespy/webservices.py | 50 ++++++++--------- src/backends/routers/home.py | 20 +++++++ src/backends/urls.py | 16 +++--- 9 files changed, 157 insertions(+), 131 deletions(-) create mode 100644 src/backends/routers/home.py diff --git a/src/backends/routers/gamespy/gstats.py b/src/backends/routers/gamespy/gstats.py index 5fecf1da0..524c59ef4 100644 --- a/src/backends/routers/gamespy/gstats.py +++ b/src/backends/routers/gamespy/gstats.py @@ -1,38 +1,38 @@ -from fastapi import FastAPI +from fastapi import APIRouter from backends.protocols.gamespy.game_status.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest from backends.urls import GAMESTATUS -app = FastAPI() +router = APIRouter() -@app.post(f"{GAMESTATUS}/AuthGameHandler/") +@router.post(f"{GAMESTATUS}/AuthGameHandler/") async def update_item(request: AuthGameRequest): raise NotImplementedError() -@app.post(f"{GAMESTATUS}/AuthPlayerHandler/") +@router.post(f"{GAMESTATUS}/AuthPlayerHandler/") async def update_item(request: AuthPlayerRequest): raise NotImplementedError() -@app.post(f"{GAMESTATUS}/NewGameHandler/") +@router.post(f"{GAMESTATUS}/NewGameHandler/") async def update_item(request: NewGameRequest): raise NotImplementedError() -@app.post(f"{GAMESTATUS}/GetPlayerDataHandler/") +@router.post(f"{GAMESTATUS}/GetPlayerDataHandler/") async def update_item(request: GetPlayerDataRequest): raise NotImplementedError() -@app.post(f"{GAMESTATUS}/SetPlayerDataHandler/") +@router.post(f"{GAMESTATUS}/SetPlayerDataHandler/") async def update_item(request: SetPlayerDataRequest): raise NotImplementedError() -@app.post(f"{GAMESTATUS}/UpdateGameHandler/") +@router.post(f"{GAMESTATUS}/UpdateGameHandler/") async def update_item(request: UpdateGameRequest): raise NotImplementedError() if __name__ == "__main__": import uvicorn - uvicorn.run(app, host="0.0.0.0", port=8000) + uvicorn.run(router, host="0.0.0.0", port=8000) diff --git a/src/backends/routers/gamespy/natneg.py b/src/backends/routers/gamespy/natneg.py index 8f0f21303..a7b401f10 100644 --- a/src/backends/routers/gamespy/natneg.py +++ b/src/backends/routers/gamespy/natneg.py @@ -1,4 +1,4 @@ -from fastapi import FastAPI +from fastapi import APIRouter from backends.protocols.gamespy.chat.requests import PingRequest from backends.protocols.gamespy.natneg.requests import ConnectRequest, ErtAckRequest, InitRequest, ReportRequest @@ -6,40 +6,39 @@ from servers.natneg.src.contracts.requests import AddressCheckRequest -app = FastAPI() +router = APIRouter() -@app.post(f"{NATNEG}/AddressCheckHandler/") +@router.post(f"{NATNEG}/AddressCheckHandler/") async def address_check(request: AddressCheckRequest): raise NotImplementedError() -@app.post(f"{NATNEG}/ConnectHandler/") +@router.post(f"{NATNEG}/ConnectHandler/") async def connect(request: ConnectRequest): raise NotImplementedError() -@app.post(f"{NATNEG}/ErtAckHandler/") +@router.post(f"{NATNEG}/ErtAckHandler/") async def ert_ack(request: ErtAckRequest): raise NotImplementedError() -@app.post(f"{NATNEG}/InitHandler/") +@router.post(f"{NATNEG}/InitHandler/") async def init(request: InitRequest): raise NotImplementedError() -@app.post(f"{NATNEG}/PingHandler/") +@router.post(f"{NATNEG}/PingHandler/") async def ping(request: PingRequest): raise NotImplementedError() -@app.post(f"{NATNEG}/ReportHandler/") +@router.post(f"{NATNEG}/ReportHandler/") async def report(request: ReportRequest): raise NotImplementedError() - if __name__ == "__main__": import uvicorn - uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file + uvicorn.run(router, host="0.0.0.0", port=8000) diff --git a/src/backends/routers/gamespy/presence_connection_manager.py b/src/backends/routers/gamespy/presence_connection_manager.py index d0dd511ae..2a0a5f814 100644 --- a/src/backends/routers/gamespy/presence_connection_manager.py +++ b/src/backends/routers/gamespy/presence_connection_manager.py @@ -1,4 +1,4 @@ -from fastapi import FastAPI +from fastapi import APIRouter from backends.protocols.gamespy.chat.requests import RegisterNickRequest from backends.protocols.gamespy.presence_connection_manager.requests import GetProfileRequest, LoginRequest, LogoutRequest, NewProfileRequest, RegisterCDKeyRequest, StatusInfoRequest, StatusRequest, UpdateProfileRequest @@ -9,71 +9,69 @@ from servers.presence_search_player.src.contracts.requests import NewUserRequest -app = FastAPI() +router = APIRouter() -@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/LoginHandler") -def login(request: LoginRequest): +@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/LoginHandler") +async def login(request: LoginRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/LogoutHandler") -def logout(request: LogoutRequest): +@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/LogoutHandler") +async def logout(request: LogoutRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/KeepAliveHandler") -def keep_alive(request: KeepAliveRequest): +@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/KeepAliveHandler") +async def keep_alive(request: KeepAliveRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/NewUserHandler") -def new_user(request: NewUserRequest): +@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/NewUserHandler") +async def new_user(request: NewUserRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/AddBlockHandler") -def add_block(request: AddBlockRequest): +@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/AddBlockHandler") +async def add_block(request: AddBlockRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/GetProfileHandler") -def get_profile(request: GetProfileRequest): +@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/GetProfileHandler") +async def get_profile(request: GetProfileRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/NewProfileHandler") -def new_proflie(request: NewProfileRequest): +@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/NewProfileHandler") +async def new_proflie(request: NewProfileRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/RegisterCDKeyHandler") -def register_cdkey(request: RegisterCDKeyRequest): +@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/RegisterCDKeyHandler") +async def register_cdkey(request: RegisterCDKeyRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/RegisterNickHandler") -def register_nick(request: RegisterNickRequest): +@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/RegisterNickHandler") +async def register_nick(request: RegisterNickRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/UpdateProfileHandler") -def update_profile(request: UpdateProfileRequest): +@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/UpdateProfileHandler") +async def update_profile(request: UpdateProfileRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/StatusHandler") -def status(request: StatusRequest): +@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/StatusHandler") +async def status(request: StatusRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_CONNECTION_MANAGER}/StatusInfoHandler") -def status_info(request: StatusInfoRequest): +@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/StatusInfoHandler") +async def status_info(request: StatusInfoRequest): raise NotImplementedError() - - if __name__ == "__main__": import uvicorn - uvicorn.run(app, host="0.0.0.0", port=8000) \ No newline at end of file + uvicorn.run(router, host="0.0.0.0", port=8000) diff --git a/src/backends/routers/gamespy/presence_search_player.py b/src/backends/routers/gamespy/presence_search_player.py index fd6bd298b..bb489c8c4 100644 --- a/src/backends/routers/gamespy/presence_search_player.py +++ b/src/backends/routers/gamespy/presence_search_player.py @@ -1,62 +1,62 @@ -from fastapi import FastAPI +from fastapi import APIRouter from backends.protocols.gamespy.presence_search_player.requests import CheckRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest from backends.urls import PRESENCE_SEARCH_PLAYER from servers.presence_search_player.src.contracts.requests import NewUserRequest -app = FastAPI() +router = APIRouter() -@app.post(f"/{PRESENCE_SEARCH_PLAYER}/CheckHandler") -def check(request: CheckRequest): +@router.post(f"/{PRESENCE_SEARCH_PLAYER}/CheckHandler") +async def check(request: CheckRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_SEARCH_PLAYER}/NewUserHandler") -def new_user(request: NewUserRequest): +@router.post(f"/{PRESENCE_SEARCH_PLAYER}/NewUserHandler") +async def new_user(request: NewUserRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_SEARCH_PLAYER}/NicksHandler") -def nicks(request: NicksRequest): +@router.post(f"/{PRESENCE_SEARCH_PLAYER}/NicksHandler") +async def nicks(request: NicksRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_SEARCH_PLAYER}/OthersHandler") -def others(request: OthersRequest): +@router.post(f"/{PRESENCE_SEARCH_PLAYER}/OthersHandler") +async def others(request: OthersRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_SEARCH_PLAYER}/OthersListHandler") -def others_list(request: OthersListRequest): +@router.post(f"/{PRESENCE_SEARCH_PLAYER}/OthersListHandler") +async def others_list(request: OthersListRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_SEARCH_PLAYER}/PMatchHandler") -def player_match(request: object): +@router.post(f"/{PRESENCE_SEARCH_PLAYER}/PMatchHandler") +async def player_match(request: object): raise NotImplementedError() -@app.post(f"/{PRESENCE_SEARCH_PLAYER}/SearchHandler") -def search(request: SearchRequest): +@router.post(f"/{PRESENCE_SEARCH_PLAYER}/SearchHandler") +async def search(request: SearchRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_SEARCH_PLAYER}/SearchUniqueHandler") -def search_unique(request: SearchUniqueRequest): +@router.post(f"/{PRESENCE_SEARCH_PLAYER}/SearchUniqueHandler") +async def search_unique(request: SearchUniqueRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_SEARCH_PLAYER}/UniqueSearchHandler") -def unique_search(request: UniqueSearchRequest): +@router.post(f"/{PRESENCE_SEARCH_PLAYER}/UniqueSearchHandler") +async def unique_search(request: UniqueSearchRequest): raise NotImplementedError() -@app.post(f"/{PRESENCE_SEARCH_PLAYER}/ValidHandler") -def valid(request: ValidRequest): +@router.post(f"/{PRESENCE_SEARCH_PLAYER}/ValidHandler") +async def valid(request: ValidRequest): raise NotImplementedError() if __name__ == "__main__": import uvicorn - uvicorn.run(app, host="0.0.0.0", port=8000) + uvicorn.run(router, host="0.0.0.0", port=8000) diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index f43c54db5..0e5fcf5d3 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -1,43 +1,43 @@ -from fastapi import FastAPI +from fastapi import APIRouter from backends.protocols.gamespy.query_report.requests import ChallengeRequest, ClientMessageRequest, EchoRequest, HeartBeatRequest from backends.urls import QUERY_REPORT from servers.presence_connection_manager.src.contracts.requests.general import KeepAliveRequest from servers.query_report.src.v2.contracts.requests import AvaliableRequest -app = FastAPI() +router = APIRouter() -@app.post(f"/{QUERY_REPORT}/HeartBeatHandler") -def heartbeat(request: HeartBeatRequest): +@router.post(f"/{QUERY_REPORT}/HeartBeatHandler") +async def heartbeat(request: HeartBeatRequest): raise NotImplementedError() -@app.post(f"/{QUERY_REPORT}/ChallengeHanler") -def challenge(request: ChallengeRequest): +@router.post(f"/{QUERY_REPORT}/ChallengeHanler") +async def challenge(request: ChallengeRequest): raise NotImplementedError() -@app.post(f"/{QUERY_REPORT}/AvailableHandler") -def available(request: AvaliableRequest): +@router.post(f"/{QUERY_REPORT}/AvailableHandler") +async def available(request: AvaliableRequest): raise NotImplementedError() -@app.post(f"/{QUERY_REPORT}/ClientMessageAckHandler") -def client_message(request: ClientMessageRequest): +@router.post(f"/{QUERY_REPORT}/ClientMessageAckHandler") +async def client_message(request: ClientMessageRequest): raise NotImplementedError() -@app.post(f"/{QUERY_REPORT}/EchoHandler") -def echo(request: EchoRequest): +@router.post(f"/{QUERY_REPORT}/EchoHandler") +async def echo(request: EchoRequest): raise NotImplementedError() -@app.post(f"/{QUERY_REPORT}/KeepAliveHandler") -def keep_alive(request: KeepAliveRequest): +@router.post(f"/{QUERY_REPORT}/KeepAliveHandler") +async def keep_alive(request: KeepAliveRequest): raise NotImplementedError() if __name__ == "__main__": import uvicorn - uvicorn.run(app, host="0.0.0.0", port=8000) + uvicorn.run(router, host="0.0.0.0", port=8000) diff --git a/src/backends/routers/gamespy/server_browser.py b/src/backends/routers/gamespy/server_browser.py index 205028b3d..44eafc965 100644 --- a/src/backends/routers/gamespy/server_browser.py +++ b/src/backends/routers/gamespy/server_browser.py @@ -1,29 +1,38 @@ -from fastapi import FastAPI +from fastapi import APIRouter, WebSocket from backends.protocols.gamespy.server_browser.requests import SendMessageRequest, ServerInfoRequest, ServerListRequest from backends.urls import SERVER_BROWSER_V1, SERVER_BROWSER_V2 -app = FastAPI +router = APIRouter() # todo maybe implement this in websocket way -# @app.post(f"/{SERVER_BROWSER_V2}/AdHocHandler") -# def check(request: ADHocRequest): -# raise NotImplementedError() -@app.post(f"/{SERVER_BROWSER_V2}/SendMessageHandler") -def send_message(request: SendMessageRequest): +@router.websocket(f"/{SERVER_BROWSER_V2}/AdHocHandler") +async def check(websocket: WebSocket): + """ + notify every server browser to send message to its client + """ + await websocket.accept() + while True: + data = await websocket.receive_text() + await websocket.send_text(f"Message text was: {data}") raise NotImplementedError() -@app.post(f"/{SERVER_BROWSER_V2}/ServerInfoHandler") -def server_info(request: ServerInfoRequest): +@router.post(f"/{SERVER_BROWSER_V2}/SendMessageHandler") +async def send_message(request: SendMessageRequest): raise NotImplementedError() -@app.post(f"/{SERVER_BROWSER_V2}/ServerListHandler") -def server_list(request: ServerListRequest): +@router.post(f"/{SERVER_BROWSER_V2}/ServerInfoHandler") +async def server_info(request: ServerInfoRequest): + raise NotImplementedError() + + +@router.post(f"/{SERVER_BROWSER_V2}/ServerListHandler") +async def server_list(request: ServerListRequest): raise NotImplementedError() if __name__ == "__main__": import uvicorn - uvicorn.run(app, host="0.0.0.0", port=8000) + uvicorn.run(router, host="0.0.0.0", port=8000) diff --git a/src/backends/routers/gamespy/webservices.py b/src/backends/routers/gamespy/webservices.py index 2d5d3fc3f..624e53ef5 100644 --- a/src/backends/routers/gamespy/webservices.py +++ b/src/backends/routers/gamespy/webservices.py @@ -1,69 +1,69 @@ -from fastapi import FastAPI +from fastapi import APIRouter from backends.urls import WEB_SERVICES -app = FastAPI() +router = APIRouter() # Altas services -@app.post(f"{WEB_SERVICES}/Altas/CreateRecordHandler") -def create_matchless_session(request): +@router.post(f"{WEB_SERVICES}/Altas/CreateRecordHandler") +async def create_matchless_session(request): raise NotImplementedError() -@app.post(f"{WEB_SERVICES}/Altas/CreateSessionHandler") -def create_session(request): +@router.post(f"{WEB_SERVICES}/Altas/CreateSessionHandler") +async def create_session(request): raise NotImplementedError() -@app.post(f"{WEB_SERVICES}/Altas/SetReportIntentionHandler") -def set_report_intention(request): +@router.post(f"{WEB_SERVICES}/Altas/SetReportIntentionHandler") +async def set_report_intention(request): raise NotImplementedError() -@app.post(f"{WEB_SERVICES}/Altas/SubmitReportHandler") -def submit_report(request): +@router.post(f"{WEB_SERVICES}/Altas/SubmitReportHandler") +async def submit_report(request): raise NotImplementedError() # Auth services -@app.post(f"{WEB_SERVICES}/Auth/LoginProfileHandler") -def submit_report(request: LoginProfileRequest): +@router.post(f"{WEB_SERVICES}/Auth/LoginProfileHandler") +async def submit_report(request: LoginProfileRequest): raise NotImplementedError() -@app.post(f"{WEB_SERVICES}/Auth/LoginProfileWithGameIdHandler") -def submit_report(request:LoginProfileWithGameIdRequest): +@router.post(f"{WEB_SERVICES}/Auth/LoginProfileWithGameIdHandler") +async def submit_report(request: LoginProfileWithGameIdRequest): raise NotImplementedError() -@app.post(f"{WEB_SERVICES}/Auth/LoginRemoteAuthHandler") -def submit_report(request:LoginRemoteAuthRequest): +@router.post(f"{WEB_SERVICES}/Auth/LoginRemoteAuthHandler") +async def submit_report(request: LoginRemoteAuthRequest): raise NotImplementedError() -@app.post(f"{WEB_SERVICES}/Auth/LoginRemoteAuthWithGameIdHandler") -def submit_report(request: LoginRemoteAuthWithGameIdRequest): +@router.post(f"{WEB_SERVICES}/Auth/LoginRemoteAuthWithGameIdHandler") +async def submit_report(request: LoginRemoteAuthWithGameIdRequest): raise NotImplementedError() -@app.post(f"{WEB_SERVICES}/Auth/LoginUniqueNickHandler") -def submit_report(request:LoginUniqueNickRequest): +@router.post(f"{WEB_SERVICES}/Auth/LoginUniqueNickHandler") +async def submit_report(request: LoginUniqueNickRequest): raise NotImplementedError() -@app.post(f"{WEB_SERVICES}/Auth/LoginUniqueNickWithGameIdHandler") -def submit_report(request:LoginUniqueNickWithGameIdRequest): +@router.post(f"{WEB_SERVICES}/Auth/LoginUniqueNickWithGameIdHandler") +async def submit_report(request: LoginUniqueNickWithGameIdRequest): raise NotImplementedError() # SAKE services -@app.post(f"{WEB_SERVICES}/Sake/CreateRecordHandler") -def create_record(request): +@router.post(f"{WEB_SERVICES}/Sake/CreateRecordHandler") +async def create_record(request): raise NotImplementedError() if __name__ == "__main__": import uvicorn - uvicorn.run(app, host="0.0.0.0", port=8000) + uvicorn.run(router, host="0.0.0.0", port=8000) diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py new file mode 100644 index 000000000..11d6d6e6e --- /dev/null +++ b/src/backends/routers/home.py @@ -0,0 +1,20 @@ +from fastapi import FastAPI + +from library.src.unispy_server_config import ServerConfig +from backends.routers.gamespy import chat, gstats, natneg, presence_connection_manager, presence_search_player, query_report, server_browser, webservices +app = FastAPI() + +# app.include_router(chat.router) +app.include_router(gstats.router) +app.include_router(natneg.router) +app.include_router(presence_connection_manager.router) +app.include_router(presence_search_player.router) +app.include_router(query_report.router) +app.include_router(server_browser.router) +app.include_router(webservices.router) + + +@app.post("/") +def home(request: ServerConfig): + # todo add the server config to our database + return {"status": "online"} diff --git a/src/backends/urls.py b/src/backends/urls.py index 5bb82553c..0f94b6e2f 100644 --- a/src/backends/urls.py +++ b/src/backends/urls.py @@ -1,9 +1,9 @@ -PRESENCE_CONNECTION_MANAGER = "/GameSpy/PCM" -PRESENCE_SEARCH_PLAYER = "/GameSpy/PSP" -SERVER_BROWSER_V1 = "/GameSpy/SB_V1" -SERVER_BROWSER_V2 = "/GameSpy/SB_V2" -QUERY_REPORT = "/GameSpy/QR" -NATNEG = "/GameSpy/NN" -GAMESTATUS = "/GameSpy/GS" +PRESENCE_CONNECTION_MANAGER = "/GameSpy/PresenceConnectionManager" +PRESENCE_SEARCH_PLAYER = "/GameSpy/PresenceSearchPlayer" +SERVER_BROWSER_V1 = "/GameSpy/ServerBrowserV1" +SERVER_BROWSER_V2 = "/GameSpy/ServerBrowser_V2" +QUERY_REPORT = "/GameSpy/QueryReport" +NATNEG = "/GameSpy/NatNegotiation" +GAMESTATUS = "/GameSpy/GameStatus" CHAT = "/GameSpy/Chat" -WEB_SERVICES = "/GameSpy/WEB" +WEB_SERVICES = "/GameSpy/WebServices" From d7f28dfcb7b0c60ab44cb5feda5f13ffc38caebf Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 5 Sep 2024 08:55:24 +0000 Subject: [PATCH 107/231] refactor: update docker related files --- src/Dockerfile | 17 ++++++ ...se.yaml => docker-compose-unispy-env.yaml} | 0 src/docker-compose-unispy-server.yml | 57 +++++++++++++++++++ src/library/src/abstractions/client.py | 6 +- .../src/abstractions/server_launcher_base.py | 9 ++- src/library/src/abstractions/switcher.py | 5 +- src/library/src/network/http_handler.py | 10 ++-- src/library/src/network/tcp_handler.py | 4 +- src/library/src/network/udp_handler.py | 4 +- 9 files changed, 95 insertions(+), 17 deletions(-) create mode 100644 src/Dockerfile rename src/{docker-compose.yaml => docker-compose-unispy-env.yaml} (100%) create mode 100644 src/docker-compose-unispy-server.yml diff --git a/src/Dockerfile b/src/Dockerfile new file mode 100644 index 000000000..56d45abaa --- /dev/null +++ b/src/Dockerfile @@ -0,0 +1,17 @@ +# Use the official Python image from the Docker Hub +FROM python:3.12-slim + +# Set the working directory in the container +WORKDIR /unispy-server + +# Copy the requirements file into the container +COPY requirements.txt . + +# Install the dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the rest of the application code into the container +COPY . . + +# Specify the command to run the application +CMD ["python", "app.py"] \ No newline at end of file diff --git a/src/docker-compose.yaml b/src/docker-compose-unispy-env.yaml similarity index 100% rename from src/docker-compose.yaml rename to src/docker-compose-unispy-env.yaml diff --git a/src/docker-compose-unispy-server.yml b/src/docker-compose-unispy-server.yml new file mode 100644 index 000000000..7b2eefbc3 --- /dev/null +++ b/src/docker-compose-unispy-server.yml @@ -0,0 +1,57 @@ +version: '3' + +services: + backends: + image: unispy-python + restart: always + ports: + - "8000:8000" + command: python backends/home.py + pcm: + image: unispy-python + restart: always + ports: + - "29900:29900" + command: python servers/presence_connection_manager/application/server_launcher.py + psp: + image: unispy-python + restart: always + ports: + - "29901:29901" + command: python servers/presence_search_player/application/server_launcher.py + natneg: + image: unispy-python + restart: always + ports: + - "27901:27901" + command: python servers/natneg/application/server_launcher.py + chat: + image: unispy-python + restart: always + ports: + - "6667:6667" + command: python servers/chat/application/server_launcher.py + gstats: + image: unispy-python + restart: always + ports: + - "29920:29920" + command: python servers/game_status/application/server_launcher.py + qr: + image: unispy-python + restart: always + ports: + - "27900:27900" + command: python servers/query_report/application/server_launcher.py + sbv2: + image: unispy-python + restart: always + ports: + - "28900:28900" + command: python servers/server_browser/application/server_launcher.py + web: + image: unispy-python + restart: always + ports: + - "80:80" + command: python servers/web_services/application/server_launcher.py \ No newline at end of file diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index b4187889e..ff279e22e 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -48,14 +48,14 @@ def on_connected(self) -> None: def on_disconnected(self) -> None: pass - def create_switcher(self, buffer: "bytes | HttpRequest") -> "SwitcherBase": + def create_switcher(self, buffer: bytes | str) -> "SwitcherBase": assert isinstance(buffer, bytes) or isinstance(buffer, HttpRequest) - def on_received(self, buffer: "bytes | HttpRequest") -> None: + def on_received(self, buffer: bytes | str) -> None: if isinstance(buffer, bytes): if self.crypto is not None: buffer = self.crypto.decrypt(buffer) - elif isinstance(buffer, HttpRequest): + elif isinstance(buffer, str): pass else: raise UniSpyException("buffer type is invalid") diff --git a/src/library/src/abstractions/server_launcher_base.py b/src/library/src/abstractions/server_launcher_base.py index f627b48d7..488ab9502 100644 --- a/src/library/src/abstractions/server_launcher_base.py +++ b/src/library/src/abstractions/server_launcher_base.py @@ -7,7 +7,7 @@ VERSION = 0.45 -__server_name_mapping = { +__SERVER_FULL_SHORT_NAME_MAPPING = { "PresenceConnectionManager": "PCM", "PresenceSearchPlayer": "PSP", "CDKey": "CDKey", @@ -44,7 +44,10 @@ def _launch_server(self) -> None: def _connect_to_backend(self): try: - resp: requests.Response = requests.get(url=CONFIG.backend.url) + # post our server config to backends to register + resp: requests.Response = requests.post( + url=CONFIG.backend.url, + data=self.config.model_dump_json()) if resp.status_code == 200: data = resp.json() if data["status"] != "online": @@ -57,5 +60,5 @@ def _connect_to_backend(self): # fmt: on def _create_logger(self): - short_name = __server_name_mapping[self.config.server_name] + short_name = __SERVER_FULL_SHORT_NAME_MAPPING[self.config.server_name] self.logger = LogManager.create(CONFIG.logging.path, short_name) diff --git a/src/library/src/abstractions/switcher.py b/src/library/src/abstractions/switcher.py index a21deb5a9..40437b6d2 100644 --- a/src/library/src/abstractions/switcher.py +++ b/src/library/src/abstractions/switcher.py @@ -9,11 +9,12 @@ class SwitcherBase: class member type hint can use class static member, but you can not initialize any class static member here! Init it in the __init__() function """ _handlers: list[CmdHandlerBase] - _requests: list[tuple] + _requests: list[tuple[object, object]] _raw_request: object - def __init__(self, client: ClientBase, raw_request: Optional[bytes | str]) -> None: + def __init__(self, client: ClientBase, raw_request: bytes | str) -> None: assert isinstance(client, ClientBase) + assert isinstance(raw_request, bytes) or isinstance(raw_request, str) self._client: ClientBase = client self._raw_request: object = raw_request self._handlers: list[CmdHandlerBase] = [] diff --git a/src/library/src/network/http_handler.py b/src/library/src/network/http_handler.py index b57f099a3..63884b621 100644 --- a/src/library/src/network/http_handler.py +++ b/src/library/src/network/http_handler.py @@ -44,13 +44,13 @@ class HttpHandler(BaseHTTPRequestHandler): conn: HttpConnection def do_POST(self) -> None: - parsed_url = urlparse(self.path).geturl() + # parsed_url = urlparse(self.path).geturl() content_length = int(self.headers["Content-Length"]) data = self.rfile.read(content_length).decode() - request = HttpRequest(parsed_url, dict(self.headers), data) + # request = HttpRequest(parsed_url, dict(self.headers), data) if self.conn is None: - self.conn = HttpConnection(self, *self.server.handler_params) # type: ignore - self.conn.on_received(request) + self.conn = HttpConnection(self, *self.server.unispy_params) # type: ignore + self.conn.on_received(data) class HttpServer(ServerBase): @@ -58,7 +58,7 @@ def start(self) -> None: self._server = ThreadingHTTPServer( (self._config.public_address, self._config.listening_port), HttpHandler ) - self._server.handler_params = (self._config, self._client_cls, self._logger) # type: ignore + self._server.unispy_params = (self._config, self._client_cls, self._logger) # type: ignore self._server.serve_forever() diff --git a/src/library/src/network/tcp_handler.py b/src/library/src/network/tcp_handler.py index 5e319286c..e7816e530 100644 --- a/src/library/src/network/tcp_handler.py +++ b/src/library/src/network/tcp_handler.py @@ -30,7 +30,7 @@ class TcpHandler(socketserver.BaseRequestHandler): def handle(self) -> None: if self.conn is None: - self.conn = TcpConnection(self, *self.server.handler_params) # type: ignore + self.conn = TcpConnection(self, *self.server.unispy_params) # type: ignore self.conn.on_connected() while True: try: @@ -53,7 +53,7 @@ def start(self) -> None: TcpHandler, ) self._server.allow_reuse_address = True - self._server.handler_params = (self._config, self._client_cls, self._logger) # type: ignore + self._server.unispy_params = (self._config, self._client_cls, self._logger) # type: ignore self._server.serve_forever() diff --git a/src/library/src/network/udp_handler.py b/src/library/src/network/udp_handler.py index ad46fabdd..fb4db9ece 100644 --- a/src/library/src/network/udp_handler.py +++ b/src/library/src/network/udp_handler.py @@ -18,7 +18,7 @@ class UdpHandler(socketserver.BaseRequestHandler): def handle(self) -> None: data = self.request[0] - conn = UdpConnection(self, *self.server.handler_params) # type: ignore + conn = UdpConnection(self, *self.server.unispy_params) # type: ignore conn.on_received(data) def send(self, data: bytes) -> None: @@ -33,7 +33,7 @@ def start(self) -> None: UdpHandler, ) # inject the handler params to ThreadingUDPServer - self._server.handler_params = (self._config, self._client_cls, self._logger) # type: ignore + self._server.unispy_params = (self._config, self._client_cls, self._logger) # type: ignore self._server.serve_forever() From c73220e4e383594f203581ff2fc21f100ce1bcd2 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 6 Sep 2024 03:32:24 +0000 Subject: [PATCH 108/231] refactor: added client pool for global use --- .../gamespy/web_services/requests.py | 86 ++++++++++++++++--- src/backends/routers/gamespy/chat.py | 7 ++ src/backends/routers/gamespy/gstats.py | 5 ++ src/backends/routers/gamespy/natneg.py | 6 +- .../gamespy/presence_connection_manager.py | 6 +- .../routers/gamespy/presence_search_player.py | 6 +- src/backends/routers/gamespy/query_report.py | 6 +- .../routers/gamespy/server_browser.py | 6 +- src/backends/routers/gamespy/webservices.py | 39 +++++++++ src/library/src/abstractions/client.py | 12 ++- src/library/src/abstractions/connections.py | 10 ++- src/library/tests/mock_objects/general.py | 5 +- src/servers/chat/src/applications/client.py | 12 ++- .../game_status/src/applications/client.py | 20 ++++- src/servers/natneg/src/applications/client.py | 5 +- .../src/applications/client.py | 13 ++- .../src/applications/client.py | 7 ++ .../server_browser/src/v2/enums/general.py | 15 ++-- .../src/v2/handlers/handlers.py | 23 +++-- .../web_services/src/applications/client.py | 9 +- 20 files changed, 251 insertions(+), 47 deletions(-) diff --git a/src/backends/protocols/gamespy/web_services/requests.py b/src/backends/protocols/gamespy/web_services/requests.py index d5ba9e927..03e1a5d13 100644 --- a/src/backends/protocols/gamespy/web_services/requests.py +++ b/src/backends/protocols/gamespy/web_services/requests.py @@ -2,7 +2,7 @@ import backends.library.abstractions.contracts as lib -class RequestBase(lib.RequestBase): +class SakeRequestBase(lib.RequestBase): game_id: int secret_key: str login_ticket: str @@ -15,11 +15,11 @@ class CreateRecordData(BaseModel): value: str -class CreateRecordRequest(RequestBase): +class CreateRecordRequest(SakeRequestBase): values: list[CreateRecordData] -class DeleteRecordRequest(RequestBase): +class DeleteRecordRequest(SakeRequestBase): record_id: int @@ -28,7 +28,7 @@ class GetMyRecordsData(BaseModel): value: str -class GetMyRecordsRequest(RequestBase): +class GetMyRecordsRequest(SakeRequestBase): fields: list[GetMyRecordsData] @@ -37,12 +37,12 @@ class GetRandomRecordsData(BaseModel): value: str -class GetRandomRecordsRequest(RequestBase): +class GetRandomRecordsRequest(SakeRequestBase): max: int fields: list[GetRandomRecordsData] -class GetRecordLimitRequest(RequestBase): +class GetRecordLimitRequest(SakeRequestBase): pass @@ -51,14 +51,13 @@ class GetSpecificRecordsData(BaseModel): type: str -class GetSpecificRecordsRequest(RequestBase): +class GetSpecificRecordsRequest(SakeRequestBase): record_ids: list[GetSpecificRecordsData] fields: list[GetSpecificRecordsData] - -class RateRecordRequest(RequestBase): +class RateRecordRequest(SakeRequestBase): record_id: str rating: str @@ -68,7 +67,7 @@ class SearchForRecordsData(BaseModel): type: str -class SearchForRecordsRequest(RequestBase): +class SearchForRecordsRequest(SakeRequestBase): filter: str sort: str offset: str @@ -85,6 +84,71 @@ class UpdateRecordData(BaseModel): value: str -class UpdateRecordRequest(RequestBase): +class UpdateRecordRequest(SakeRequestBase): record_id: str values: list[UpdateRecordData] + + +# Auth + +class AuthRequestBase(lib.RequestBase): + version: int + partner_code: int + namespace_id: int + + +class LoginProfileRequest(AuthRequestBase): + email: str + uniquenick: str + cdkey: str + password: str + + +class LoginProfileWithGameIdRequest(LoginProfileRequest): + game_id: int + + +class LoginPs3CertRequest(AuthRequestBase): + ps3_cert: str + game_id: int + npticket: str + + +class LoginPs3CertWithGameIdRequest(LoginPs3CertRequest): + game_id: int + + +class LoginRemoteAuthRequest(AuthRequestBase): + auth_token: str + challenge: str + + +class LoginRemoteAuthWithGameIdRequest(LoginRemoteAuthRequest): + game_id: int + + +class LoginUniqueNickRequest(AuthRequestBase): + uniquenick: str + password: str + + +class LoginUniqueNickWithGameIdRequest(LoginUniqueNickRequest): + game_id: int + + +# D2G + +class Direct2GameRequestBase(lib.RequestBase): + pass + +class GetPurchaseHistoryRequest(Direct2GameRequestBase): + game_id: int + access_token: str + proof: str + certificate: str + +class GetStoreAvailabilityRequest(Direct2GameRequestBase): + game_id: int + version: int + region: str + access_token: str \ No newline at end of file diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 49f12ea8b..60f41e5ac 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -6,3 +6,10 @@ +# if __name__ == "__main__": +# import uvicorn +# from fastapi import FastAPI + +# app = FastAPI() +# app.include_router(router) +# uvicorn.run(router, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/src/backends/routers/gamespy/gstats.py b/src/backends/routers/gamespy/gstats.py index 524c59ef4..0aabd70aa 100644 --- a/src/backends/routers/gamespy/gstats.py +++ b/src/backends/routers/gamespy/gstats.py @@ -33,6 +33,11 @@ async def update_item(request: SetPlayerDataRequest): async def update_item(request: UpdateGameRequest): raise NotImplementedError() + if __name__ == "__main__": import uvicorn + from fastapi import FastAPI + + app = FastAPI() + app.include_router(router) uvicorn.run(router, host="0.0.0.0", port=8000) diff --git a/src/backends/routers/gamespy/natneg.py b/src/backends/routers/gamespy/natneg.py index a7b401f10..a9782f461 100644 --- a/src/backends/routers/gamespy/natneg.py +++ b/src/backends/routers/gamespy/natneg.py @@ -41,4 +41,8 @@ async def report(request: ReportRequest): if __name__ == "__main__": import uvicorn - uvicorn.run(router, host="0.0.0.0", port=8000) + from fastapi import FastAPI + + app = FastAPI() + app.include_router(router) + uvicorn.run(router, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/src/backends/routers/gamespy/presence_connection_manager.py b/src/backends/routers/gamespy/presence_connection_manager.py index 2a0a5f814..8431a1b92 100644 --- a/src/backends/routers/gamespy/presence_connection_manager.py +++ b/src/backends/routers/gamespy/presence_connection_manager.py @@ -74,4 +74,8 @@ async def status_info(request: StatusInfoRequest): if __name__ == "__main__": import uvicorn - uvicorn.run(router, host="0.0.0.0", port=8000) + from fastapi import FastAPI + + app = FastAPI() + app.include_router(router) + uvicorn.run(router, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/src/backends/routers/gamespy/presence_search_player.py b/src/backends/routers/gamespy/presence_search_player.py index bb489c8c4..ca81f8449 100644 --- a/src/backends/routers/gamespy/presence_search_player.py +++ b/src/backends/routers/gamespy/presence_search_player.py @@ -59,4 +59,8 @@ async def valid(request: ValidRequest): if __name__ == "__main__": import uvicorn - uvicorn.run(router, host="0.0.0.0", port=8000) + from fastapi import FastAPI + + app = FastAPI() + app.include_router(router) + uvicorn.run(router, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index 0e5fcf5d3..8ec5944d5 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -40,4 +40,8 @@ async def keep_alive(request: KeepAliveRequest): if __name__ == "__main__": import uvicorn - uvicorn.run(router, host="0.0.0.0", port=8000) + from fastapi import FastAPI + + app = FastAPI() + app.include_router(router) + uvicorn.run(router, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/src/backends/routers/gamespy/server_browser.py b/src/backends/routers/gamespy/server_browser.py index 44eafc965..1599a0299 100644 --- a/src/backends/routers/gamespy/server_browser.py +++ b/src/backends/routers/gamespy/server_browser.py @@ -35,4 +35,8 @@ async def server_list(request: ServerListRequest): if __name__ == "__main__": import uvicorn - uvicorn.run(router, host="0.0.0.0", port=8000) + from fastapi import FastAPI + + app = FastAPI() + app.include_router(router) + uvicorn.run(router, host="0.0.0.0", port=8000) \ No newline at end of file diff --git a/src/backends/routers/gamespy/webservices.py b/src/backends/routers/gamespy/webservices.py index 624e53ef5..36b1584ee 100644 --- a/src/backends/routers/gamespy/webservices.py +++ b/src/backends/routers/gamespy/webservices.py @@ -1,6 +1,7 @@ from fastapi import APIRouter from backends.urls import WEB_SERVICES +from backends.protocols.gamespy.web_services.requests import CreateRecordRequest, GetMyRecordsRequest, LoginProfileRequest, LoginProfileWithGameIdRequest, LoginRemoteAuthRequest, LoginRemoteAuthWithGameIdRequest, LoginUniqueNickRequest, LoginUniqueNickWithGameIdRequest, SearchForRecordsRequest router = APIRouter() @@ -60,10 +61,48 @@ async def submit_report(request: LoginUniqueNickWithGameIdRequest): # SAKE services @router.post(f"{WEB_SERVICES}/Sake/CreateRecordHandler") +async def create_record(request: CreateRecordRequest): + raise NotImplementedError() + + +@router.post(f"{WEB_SERVICES}/Sake/DeleteRecordHandler") async def create_record(request): raise NotImplementedError() +@router.post(f"{WEB_SERVICES}/Sake/GetMyRecordsHandler") +async def create_record(request: GetMyRecordsRequest): + raise NotImplementedError() + + +@router.post(f"{WEB_SERVICES}/Sake/GetRandomRecordsHandler") +async def create_record(request): + raise NotImplementedError() + + +@router.post(f"{WEB_SERVICES}/Sake/GetRecordLimitHandler") +async def create_record(request): + raise NotImplementedError() + + +@router.post(f"{WEB_SERVICES}/Sake/RateRecordHandler") +async def create_record(request): + raise NotImplementedError() + + +@router.post(f"{WEB_SERVICES}/Sake/SearchForRecordsHandler") +async def create_record(request: SearchForRecordsRequest): + raise NotImplementedError() + + +@router.post(f"{WEB_SERVICES}/Sake/UpdateRecordHandler") +async def create_record(request): + raise NotImplementedError() + if __name__ == "__main__": import uvicorn + from fastapi import FastAPI + + app = FastAPI() + app.include_router(router) uvicorn.run(router, host="0.0.0.0", port=8000) diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index ff279e22e..fb5d09258 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -7,8 +7,8 @@ from typing import TYPE_CHECKING, Optional if TYPE_CHECKING: - from library.src.abstractions.handler import CmdHandlerBase from library.src.abstractions.connections import ConnectionBase + from library.src.abstractions.handler import CmdHandlerBase from library.src.abstractions.switcher import SwitcherBase from library.src.abstractions.enctypt_base import EncryptBase from library.src.abstractions.contracts import ResponseBase @@ -27,25 +27,31 @@ class ClientBase: crypto: Optional["EncryptBase"] info: "ClientInfoBase" is_log_raw: bool + pool: dict[str, "ClientBase"] def __init__( self, connection: "ConnectionBase", server_config: ServerConfig, logger: LogWriter ): - # fmt: off assert isinstance(server_config, ServerConfig) assert isinstance(logger, LogWriter) + from library.src.abstractions.connections import ConnectionBase + assert issubclass(type(connection), ConnectionBase) self.server_config = server_config self.connection = connection self.logger = logger - self._log_prefix = f"[{self.connection.remote_ip}:{self.connection.remote_port}]" self.crypto = None self.is_log_raw = False + # fmt: off + self._log_prefix = self.connection.ip_endpoint # fmt: on def on_connected(self) -> None: + # this operation will append the child client instance to the static member of childclass, if child class is overide the static member ClientBase.pool + self.pool[self.connection.ip_endpoint] = self pass def on_disconnected(self) -> None: + del self.pool[self.connection.ip_endpoint] pass def create_switcher(self, buffer: bytes | str) -> "SwitcherBase": diff --git a/src/library/src/abstractions/connections.py b/src/library/src/abstractions/connections.py index b145de77c..7bec592dd 100644 --- a/src/library/src/abstractions/connections.py +++ b/src/library/src/abstractions/connections.py @@ -15,13 +15,16 @@ class ConnectionBase: remote_ip: str remote_port: int - _is_started: bool = False + _is_started: bool config: ServerConfig t_client: type[ClientBase] logger: LogWriter handler: socketserver.BaseRequestHandler _client: ClientBase - + ip_endpoint: str + """ + ip endpoint format str \ + """ def __init__( self, handler: socketserver.BaseRequestHandler, @@ -36,10 +39,13 @@ def __init__( # assert issubclass(type(handler), socketserver.BaseRequestHandler) self.remote_ip = handler.client_address[0] self.remote_port = int(handler.client_address[1]) + self.ip_endpoint = f"{self.remote_ip}:{self.remote_port}" + self.config = config self.t_client = t_client self.logger = logger self._client = self.t_client(self, self.config, self.logger) + self._is_started = False def on_received(self, data: "Optional[bytes|HttpRequest]") -> None: self._client.on_received(data) diff --git a/src/library/tests/mock_objects/general.py b/src/library/tests/mock_objects/general.py index 25662d663..a58b0122e 100644 --- a/src/library/tests/mock_objects/general.py +++ b/src/library/tests/mock_objects/general.py @@ -4,13 +4,12 @@ from library.src.abstractions.client import ClientBase from library.src.abstractions.connections import ConnectionBase from library.src.abstractions.handler import CmdHandlerBase -from library.src.abstractions.switcher import SwitcherBase from library.src.log.log_manager import LogWriter -from library.src.unispy_server_config import CONFIG -from servers.natneg.src.handlers.switcher import CmdSwitcher +from library.src.unispy_server_config import CONFIG, ServerConfig class ConnectionMock(ConnectionBase): + def send(self, data: bytes) -> None: pass diff --git a/src/servers/chat/src/applications/client.py b/src/servers/chat/src/applications/client.py index 15dd97812..fb98bc89f 100644 --- a/src/servers/chat/src/applications/client.py +++ b/src/servers/chat/src/applications/client.py @@ -1,6 +1,10 @@ from library.src.abstractions.client import ClientBase from typing import TYPE_CHECKING +from library.src.log.log_manager import LogWriter +from library.src.network.tcp_handler import TcpConnection +from library.src.unispy_server_config import ServerConfig + if TYPE_CHECKING: from servers.chat.src.aggregates.channel import Channel @@ -14,5 +18,9 @@ class ClientInfo: class Client(ClientBase): - info: ClientInfo = ClientInfo() - pass + info: ClientInfo + client_pool: dict[str, "Client"] = {} + + def __init__(self, connection: TcpConnection, server_config: ServerConfig, logger: LogWriter): + super().__init__(connection, server_config, logger) + self.info = ClientInfo() diff --git a/src/servers/game_status/src/applications/client.py b/src/servers/game_status/src/applications/client.py index 7b7e008ff..2cd72cea2 100644 --- a/src/servers/game_status/src/applications/client.py +++ b/src/servers/game_status/src/applications/client.py @@ -1,5 +1,8 @@ from library.src.abstractions.client import ClientBase, ClientInfoBase from library.src.abstractions.switcher import SwitcherBase +from library.src.log.log_manager import LogWriter +from library.src.network.tcp_handler import TcpConnection +from library.src.unispy_server_config import ServerConfig from servers.game_status.src.aggregations.gscrypt import GSCrypt @@ -15,9 +18,24 @@ class ClientInfo(ClientInfoBase): profile_id: int = None game_session_key: str = None + def __init__(self) -> None: + super().__init__() + self.session_key: str = None + self.game_name: str = None + self.is_user_authenticated: bool = False + self.is_player_authenticated: bool = False + self.is_game_authenticated: bool = False + self.profile_id: int = None + self.game_session_key: str = None + class Client(ClientBase): - info: ClientInfo = ClientInfo() + info: ClientInfo + client_pool: dict[str, "Client"] = {} + + def __init__(self, connection: TcpConnection, server_config: ServerConfig, logger: LogWriter): + super().__init__(connection, server_config, logger) + self.info = ClientInfo() def on_connected(self) -> None: self.crypto = GSCrypt() diff --git a/src/servers/natneg/src/applications/client.py b/src/servers/natneg/src/applications/client.py index da9362d85..045f8834c 100644 --- a/src/servers/natneg/src/applications/client.py +++ b/src/servers/natneg/src/applications/client.py @@ -1,10 +1,13 @@ from library.src.abstractions.client import ClientBase from library.src.log.log_manager import LogWriter +from library.src.network.udp_handler import UdpConnection from library.src.unispy_server_config import ServerConfig class Client(ClientBase): - def __init__(self, connection, server_config: ServerConfig, logger: LogWriter): + client_pool: dict[str, "Client"] = [] + + def __init__(self, connection: UdpConnection, server_config: ServerConfig, logger: LogWriter): super().__init__(connection, server_config, logger) self.is_log_raw = True diff --git a/src/servers/presence_connection_manager/src/applications/client.py b/src/servers/presence_connection_manager/src/applications/client.py index deeb71007..3ece52730 100644 --- a/src/servers/presence_connection_manager/src/applications/client.py +++ b/src/servers/presence_connection_manager/src/applications/client.py @@ -1,8 +1,8 @@ from library.src.abstractions.client import ClientBase, ClientInfoBase -from library.src.abstractions.connections import TcpConnectionBase from library.src.abstractions.switcher import SwitcherBase from library.src.log.log_manager import LogWriter +from library.src.network.tcp_handler import TcpConnection from library.src.unispy_server_config import ServerConfig from servers.presence_connection_manager.src.aggregates.login_challenge import ( SERVER_CHALLENGE, @@ -21,18 +21,23 @@ class ClientInfo(ClientInfoBase): user_id: int profile_id: int sub_profile_id: int - login_status: LoginStatus = LoginStatus.CONNECTED + login_status: LoginStatus namespace_id: int sdk_revision: SdkRevision + def __init__(self) -> None: + super().__init__() + self.login_status = LoginStatus.CONNECTED + class Client(ClientBase): info: ClientInfo - connection: TcpConnectionBase + client_pool: dict[str, "Client"] = {} + connection: TcpConnection def __init__( self, - connection: TcpConnectionBase, + connection: TcpConnection, server_config: ServerConfig, logger: LogWriter, ): diff --git a/src/servers/presence_search_player/src/applications/client.py b/src/servers/presence_search_player/src/applications/client.py index 27724705c..93cbcc185 100644 --- a/src/servers/presence_search_player/src/applications/client.py +++ b/src/servers/presence_search_player/src/applications/client.py @@ -1,9 +1,16 @@ from library.src.abstractions.client import ClientBase from library.src.abstractions.switcher import SwitcherBase +from library.src.log.log_manager import LogWriter +from library.src.network.tcp_handler import TcpConnection +from library.src.unispy_server_config import ServerConfig class Client(ClientBase): + client_pool: dict[str, "Client"] = {} + + def __init__(self, connection: TcpConnection, server_config: ServerConfig, logger: LogWriter): + super().__init__(connection, server_config, logger) def _create_switcher(self, buffer) -> SwitcherBase: from servers.presence_search_player.src.handlers.switcher import CmdSwitcher diff --git a/src/servers/server_browser/src/v2/enums/general.py b/src/servers/server_browser/src/v2/enums/general.py index 1f6252bc7..5a517798d 100644 --- a/src/servers/server_browser/src/v2/enums/general.py +++ b/src/servers/server_browser/src/v2/enums/general.py @@ -21,13 +21,12 @@ class DataKeyType(IntEnum): class RequestType(IntEnum): - SERVER_LIST_REQUEST = 0 - SERVER_INFO_REQUEST = 1 - SEND_MESSAGE_REQUEST = 2 - KEEP_ALIVE_REPLY = 3 - MAP_LOOP_REQUEST = 4 - PLAYER_SEARCH_REQUEST = 5 - + SERVER_LIST_REQUEST = 0x00 + SERVER_INFO_REQUEST = 0x01 + SEND_MESSAGE_REQUEST = 0x02 + KEEP_ALIVE_REPLY = 0x03 + MAP_LOOP_REQUEST = 0x04 + PLAYER_SEARCH_REQUEST = 0x05 class ResponseType(IntEnum): PUSH_KEYS_MESSAGE = 1 @@ -67,4 +66,4 @@ class GameServerFlags(IntEnum): if __name__ == "__main__": GameServerFlags.PRIVATE_IP_FLAG.value - pass \ No newline at end of file + pass diff --git a/src/servers/server_browser/src/v2/handlers/handlers.py b/src/servers/server_browser/src/v2/handlers/handlers.py index d109fe6f7..aab2d0120 100644 --- a/src/servers/server_browser/src/v2/handlers/handlers.py +++ b/src/servers/server_browser/src/v2/handlers/handlers.py @@ -1,8 +1,7 @@ from concurrent.futures import ProcessPoolExecutor -import socket from servers.query_report.src.aggregates.game_server_info import GameServerInfo from servers.query_report.src.v2.contracts.requests import ClientMessageRequest -from servers.query_report.src.v2.enums.general import GameServerStatus +from servers.query_report.src.v2.enums.general import GameServerStatus, RequestType from servers.server_browser.src.exceptions.general import ServerBrowserException from servers.server_browser.src.v2.contracts.requests import ( SendMessageRequest, @@ -21,7 +20,7 @@ ServerMainListResult, ) from servers.server_browser.src.v2.enums.general import ( - RequestType, + # RequestType, ServerListUpdateOption, ) from servers.server_browser.src.v2.abstractions.handlers import CmdHandlerBase @@ -29,6 +28,17 @@ from servers.server_browser.src.v2.applications.client import Client +def get_clients(game_name: str): + client_list = [] + assert isinstance(game_name, str) + for ip, c in Client.pool.items(): + client: Client = c + if client.info.game_name == game_name: + client_list.append(client) + + return client_list + + class AdHocHandler(CmdHandlerBase): _message: GameServerInfo # !fix this @@ -43,7 +53,9 @@ def handle(self) -> None: match (self._message.status): case ( status - ) if status == GameServerStatus.NORMAL or status == GameServerStatus.UPDATE or status == GameServerStatus.PLAYING: + ) if status == GameServerStatus.NORMAL \ + or status == GameServerStatus.UPDATE \ + or status == GameServerStatus.PLAYING: self.response = UpdateServerInfoResponse(result) case GameServerStatus.SHUTDOWN: self.response = DeleteServerInfoResponse(result) @@ -61,8 +73,7 @@ def send_message(self, client: Client): and client.crypto is not None and ( client.info.search_type == ServerListUpdateOption.SERVER_MAIN_LIST - or client.info.search_type - == ServerListUpdateOption.P2P_SERVER_MAIN_LIST + or client.info.search_type == ServerListUpdateOption.P2P_SERVER_MAIN_LIST ) ): client.log_info( diff --git a/src/servers/web_services/src/applications/client.py b/src/servers/web_services/src/applications/client.py index ff92df23b..f1a8de2c5 100644 --- a/src/servers/web_services/src/applications/client.py +++ b/src/servers/web_services/src/applications/client.py @@ -1,6 +1,8 @@ from library.src.abstractions.client import ClientBase, ClientInfoBase from library.src.abstractions.switcher import SwitcherBase -from library.src.network.http_handler import HttpRequest +from library.src.log.log_manager import LogWriter +from library.src.network.http_handler import HttpConnection +from library.src.unispy_server_config import ServerConfig class ClientInfo(ClientInfoBase): @@ -29,6 +31,11 @@ class ClientInfo(ClientInfoBase): class Client(ClientBase): info: ClientInfo + client_pool: dict[str, "Client"] = {} + + def __init__(self, connection: HttpConnection, server_config: ServerConfig, logger: LogWriter): + super().__init__(connection, server_config, logger) + self.info = ClientInfo() def on_received(self, buffer: str) -> None: assert isinstance(buffer, str) From d120873f432171240d437a25c7c91470bd98e2ba Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 6 Sep 2024 07:29:48 +0000 Subject: [PATCH 109/231] refactor: added global logger --- .../library/abstractions/cmd_handler_base.py | 45 +++++++++++++++---- src/backends/routers/home.py | 5 ++- src/library/src/abstractions/client.py | 3 ++ ...er_launcher_base.py => server_launcher.py} | 6 +++ src/library/src/log/log_manager.py | 2 +- .../chat/src/applications/server_launcher.py | 2 +- .../src/applications/server_launcher.py | 2 +- .../src/applications/server_launcher.py | 2 +- .../src/applications/server_launcher.py | 2 +- .../src/applications/server_launcher.py | 2 +- .../src/applications/server_launcher.py | 2 +- .../src/v2/applications/server_launcher.py | 2 +- .../src/applications/server_launcher.py | 2 +- 13 files changed, 58 insertions(+), 19 deletions(-) rename src/library/src/abstractions/{server_launcher_base.py => server_launcher.py} (94%) diff --git a/src/backends/library/abstractions/cmd_handler_base.py b/src/backends/library/abstractions/cmd_handler_base.py index 9242fea72..bb4e073f1 100644 --- a/src/backends/library/abstractions/cmd_handler_base.py +++ b/src/backends/library/abstractions/cmd_handler_base.py @@ -1,20 +1,47 @@ -from typing import TYPE_CHECKING +from abc import abstractmethod +from backends.library.abstractions.contracts import RequestBase +from library.src.abstractions.contracts import ResultBase -if TYPE_CHECKING: - from library.src.abstractions.contracts import RequestBase +import logging +from library.src.exceptions.general import UniSpyException -class CmdHandlerBase: + +class HandlerBase: + """ + The ultimate handler base of backend service + """ _request: RequestBase + _result: ResultBase + _response: dict + """ + the dict response which send to client + """ + + def __init__(self, request: RequestBase) -> None: + assert issubclass(type(request), RequestBase) + self._request = request + # decoupling the logging in home.py + self.logger = logging.getLogger("backend") - def handle(self): - self.request_check() + async def handle(self) -> None: + try: + await self.request_check() + await self.data_fetch() + await self.result_construct() + except UniSpyException as ex: + self.logger.error(ex.message) + except Exception as ex: + self.logger.error(ex) - def request_check(self): + @abstractmethod + async def request_check(self) -> None: pass - def data_fetch(self): + @abstractmethod + async def data_fetch(self) -> None: pass - def result_construct(self): + @abstractmethod + async def result_construct(self) -> None: pass diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index 11d6d6e6e..c10509a4a 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -1,6 +1,7 @@ from fastapi import FastAPI -from library.src.unispy_server_config import ServerConfig +from library.src.log.log_manager import LogManager +from library.src.unispy_server_config import CONFIG, ServerConfig from backends.routers.gamespy import chat, gstats, natneg, presence_connection_manager, presence_search_player, query_report, server_browser, webservices app = FastAPI() @@ -13,6 +14,8 @@ app.include_router(server_browser.router) app.include_router(webservices.router) +LogManager.create(CONFIG.logging.path, "backend") + @app.post("/") def home(request: ServerConfig): diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index fb5d09258..b1942c115 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -28,6 +28,9 @@ class ClientBase: info: "ClientInfoBase" is_log_raw: bool pool: dict[str, "ClientBase"] + """ + Note: initialize in child class as class static member + """ def __init__( self, connection: "ConnectionBase", server_config: ServerConfig, logger: LogWriter diff --git a/src/library/src/abstractions/server_launcher_base.py b/src/library/src/abstractions/server_launcher.py similarity index 94% rename from src/library/src/abstractions/server_launcher_base.py rename to src/library/src/abstractions/server_launcher.py index 488ab9502..ed3a7d0e7 100644 --- a/src/library/src/abstractions/server_launcher_base.py +++ b/src/library/src/abstractions/server_launcher.py @@ -22,6 +22,12 @@ } +GLOBAL_LOGGER = LogManager.create(CONFIG.logging.path, "unispy") +""" +the global logger of unispy +""" + + class ServerLauncherBase: config: ServerConfig logger: LogWriter diff --git a/src/library/src/log/log_manager.py b/src/library/src/log/log_manager.py index ce0ccac7c..363084018 100644 --- a/src/library/src/log/log_manager.py +++ b/src/library/src/log/log_manager.py @@ -2,7 +2,6 @@ from logging.handlers import TimedRotatingFileHandler import os - class LogWriter: original_logger: logging.Logger @@ -69,3 +68,4 @@ def create(log_file_path: str, logger_name: str) -> "LogWriter": logger.addHandler(file_handler) logger.addHandler(console_handler) return LogWriter(logger) + diff --git a/src/servers/chat/src/applications/server_launcher.py b/src/servers/chat/src/applications/server_launcher.py index 67a1d4c77..441f29ced 100644 --- a/src/servers/chat/src/applications/server_launcher.py +++ b/src/servers/chat/src/applications/server_launcher.py @@ -1,4 +1,4 @@ -from library.src.abstractions.server_launcher_base import ServerLauncherBase +from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.tcp_handler import TcpServer from library.src.unispy_server_config import CONFIG from servers.chat.src.applications.client import Client diff --git a/src/servers/game_status/src/applications/server_launcher.py b/src/servers/game_status/src/applications/server_launcher.py index 638d1d909..3a199f63e 100644 --- a/src/servers/game_status/src/applications/server_launcher.py +++ b/src/servers/game_status/src/applications/server_launcher.py @@ -1,4 +1,4 @@ -from library.src.abstractions.server_launcher_base import ServerLauncherBase +from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.tcp_handler import TcpServer from library.src.unispy_server_config import CONFIG from servers.chat.src.applications.client import Client diff --git a/src/servers/natneg/src/applications/server_launcher.py b/src/servers/natneg/src/applications/server_launcher.py index 6e41d481f..606370248 100644 --- a/src/servers/natneg/src/applications/server_launcher.py +++ b/src/servers/natneg/src/applications/server_launcher.py @@ -1,4 +1,4 @@ -from library.src.abstractions.server_launcher_base import ServerLauncherBase +from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.udp_handler import UdpServer from library.src.unispy_server_config import CONFIG from servers.natneg.src.applications.client import Client diff --git a/src/servers/presence_connection_manager/src/applications/server_launcher.py b/src/servers/presence_connection_manager/src/applications/server_launcher.py index da7a97135..2f8d12bb4 100644 --- a/src/servers/presence_connection_manager/src/applications/server_launcher.py +++ b/src/servers/presence_connection_manager/src/applications/server_launcher.py @@ -1,4 +1,4 @@ -from library.src.abstractions.server_launcher_base import ServerLauncherBase +from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.udp_handler import UdpServer from library.src.unispy_server_config import CONFIG from servers.presence_connection_manager.src.applications.client import Client diff --git a/src/servers/presence_search_player/src/applications/server_launcher.py b/src/servers/presence_search_player/src/applications/server_launcher.py index 7983067ed..77350c836 100644 --- a/src/servers/presence_search_player/src/applications/server_launcher.py +++ b/src/servers/presence_search_player/src/applications/server_launcher.py @@ -1,4 +1,4 @@ -from library.src.abstractions.server_launcher_base import ServerLauncherBase +from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.tcp_handler import TcpServer from library.src.unispy_server_config import CONFIG from servers.presence_search_player.src.applications.client import Client diff --git a/src/servers/query_report/src/applications/server_launcher.py b/src/servers/query_report/src/applications/server_launcher.py index 9735086cf..2384db12e 100644 --- a/src/servers/query_report/src/applications/server_launcher.py +++ b/src/servers/query_report/src/applications/server_launcher.py @@ -1,4 +1,4 @@ -from library.src.abstractions.server_launcher_base import ServerLauncherBase +from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.udp_handler import UdpServer from library.src.unispy_server_config import CONFIG from servers.presence_search_player.src.applications.client import Client diff --git a/src/servers/server_browser/src/v2/applications/server_launcher.py b/src/servers/server_browser/src/v2/applications/server_launcher.py index e78074d4a..d9e600094 100644 --- a/src/servers/server_browser/src/v2/applications/server_launcher.py +++ b/src/servers/server_browser/src/v2/applications/server_launcher.py @@ -1,4 +1,4 @@ -from library.src.abstractions.server_launcher_base import ServerLauncherBase +from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.tcp_handler import TcpServer from library.src.unispy_server_config import CONFIG from servers.server_browser.src.v2.applications.client import Client diff --git a/src/servers/web_services/src/applications/server_launcher.py b/src/servers/web_services/src/applications/server_launcher.py index fa7ee5369..735713d2b 100644 --- a/src/servers/web_services/src/applications/server_launcher.py +++ b/src/servers/web_services/src/applications/server_launcher.py @@ -1,4 +1,4 @@ -from library.src.abstractions.server_launcher_base import ServerLauncherBase +from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.http_handler import HttpServer from library.src.unispy_server_config import CONFIG from servers.web_services.src.applications import client From b6b96388b6160cef9bc9724de002e9c5025de137 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 6 Sep 2024 08:15:41 +0000 Subject: [PATCH 110/231] refactor: change raw request encoding --- src/backends/library/abstractions/contracts.py | 9 ++++++++- .../{cmd_handler_base.py => handler_base.py} | 4 +++- .../protocols/gamespy/chat/handlers.py | 0 .../protocols/gamespy/game_status/handlers.py | 0 .../gamespy/game_traffic_relay/handlers.py | 0 src/backends/protocols/gamespy/natneg/data.py | 9 +++++---- .../protocols/gamespy/natneg/handlers.py | 18 ++++++++++++++++++ src/backends/routers/gamespy/natneg.py | 7 +++++-- src/library/src/abstractions/contracts.py | 6 ++---- src/library/src/abstractions/handler.py | 2 +- 10 files changed, 42 insertions(+), 13 deletions(-) rename src/backends/library/abstractions/{cmd_handler_base.py => handler_base.py} (90%) create mode 100644 src/backends/protocols/gamespy/chat/handlers.py create mode 100644 src/backends/protocols/gamespy/game_status/handlers.py create mode 100644 src/backends/protocols/gamespy/game_traffic_relay/handlers.py create mode 100644 src/backends/protocols/gamespy/natneg/handlers.py diff --git a/src/backends/library/abstractions/contracts.py b/src/backends/library/abstractions/contracts.py index acc46d470..4c678ffb3 100644 --- a/src/backends/library/abstractions/contracts.py +++ b/src/backends/library/abstractions/contracts.py @@ -7,4 +7,11 @@ class RequestBase(BaseModel): """ server_id: UUID4 - raw_request: object + raw_request: str + """ + if the raw_request is bytes, we decode it to decode("ascii","backslashreplace") str + """ + + +class GeneralResponse(BaseModel): + message: str diff --git a/src/backends/library/abstractions/cmd_handler_base.py b/src/backends/library/abstractions/handler_base.py similarity index 90% rename from src/backends/library/abstractions/cmd_handler_base.py rename to src/backends/library/abstractions/handler_base.py index bb4e073f1..5644c6fee 100644 --- a/src/backends/library/abstractions/cmd_handler_base.py +++ b/src/backends/library/abstractions/handler_base.py @@ -13,7 +13,7 @@ class HandlerBase: """ _request: RequestBase _result: ResultBase - _response: dict + response: dict """ the dict response which send to client """ @@ -31,8 +31,10 @@ async def handle(self) -> None: await self.result_construct() except UniSpyException as ex: self.logger.error(ex.message) + self.response = {"message": ex.message} except Exception as ex: self.logger.error(ex) + self.response = {"message": str(ex)} @abstractmethod async def request_check(self) -> None: diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/protocols/gamespy/game_status/handlers.py b/src/backends/protocols/gamespy/game_status/handlers.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/protocols/gamespy/game_traffic_relay/handlers.py b/src/backends/protocols/gamespy/game_traffic_relay/handlers.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py index 05a759140..e27dd961e 100644 --- a/src/backends/protocols/gamespy/natneg/data.py +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -26,14 +26,15 @@ def get_available_relay_serves() -> list[RelayServerInfo]: return result -def get_init_infos(server_id: UUID, cookie: int) -> list[InitPacketInfo]: - result: list[InitPacketInfo] = InitPacketInfo.objects( +async def get_init_infos(server_id: UUID, cookie: int) -> list[InitPacketInfo]: + result: list[InitPacketInfo] = await InitPacketInfo.objects( server_id=server_id, cookie=cookie) return result -def update_init_info(info: InitPacketInfo) -> None: - info.save() +async def update_init_info(info: InitPacketInfo) -> None: + assert isinstance(info, InitPacketInfo) + await info.save() def remove_init_info(info: InitPacketInfo) -> None: diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py new file mode 100644 index 000000000..a33ceebe8 --- /dev/null +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -0,0 +1,18 @@ +from typing import Any, Coroutine +from backends.library.abstractions.handler_base import HandlerBase +from backends.protocols.gamespy.natneg.data import update_init_info +from backends.protocols.gamespy.natneg.init_packet_info import InitPacketInfo +from backends.protocols.gamespy.natneg.requests import InitRequest + + +class InitHandler(HandlerBase): + def __init__(self, request: InitRequest) -> None: + super().__init__(request) + assert isinstance(request, InitRequest) + + async def data_fetch(self) -> None: + info = InitPacketInfo(**self._request.model_dump_json()) + await update_init_info(info) + + async def result_construct(self) -> None: + self.response = {"message", "ok"} diff --git a/src/backends/routers/gamespy/natneg.py b/src/backends/routers/gamespy/natneg.py index a9782f461..db912aed1 100644 --- a/src/backends/routers/gamespy/natneg.py +++ b/src/backends/routers/gamespy/natneg.py @@ -1,6 +1,7 @@ from fastapi import APIRouter from backends.protocols.gamespy.chat.requests import PingRequest +from backends.protocols.gamespy.natneg.handlers import InitHandler from backends.protocols.gamespy.natneg.requests import ConnectRequest, ErtAckRequest, InitRequest, ReportRequest from backends.urls import NATNEG from servers.natneg.src.contracts.requests import AddressCheckRequest @@ -26,7 +27,9 @@ async def ert_ack(request: ErtAckRequest): @router.post(f"{NATNEG}/InitHandler/") async def init(request: InitRequest): - raise NotImplementedError() + handler = InitHandler(request) + await handler.handle() + return handler.response @router.post(f"{NATNEG}/PingHandler/") @@ -45,4 +48,4 @@ async def report(request: ReportRequest): app = FastAPI() app.include_router(router) - uvicorn.run(router, host="0.0.0.0", port=8000) \ No newline at end of file + uvicorn.run(router, host="0.0.0.0", port=8000) diff --git a/src/library/src/abstractions/contracts.py b/src/library/src/abstractions/contracts.py index e532fcf80..e2df960ca 100644 --- a/src/library/src/abstractions/contracts.py +++ b/src/library/src/abstractions/contracts.py @@ -31,14 +31,14 @@ def __init__(self, raw_request: object) -> None: def parse(self) -> None: pass - def to_serializable_dict(self) -> dict: + def to_json(self) -> dict: """ create a json serializable dict of this class """ result = deepcopy(self.__dict__) for key, value in result.items(): if isinstance(value, bytes): - result[key] = list(value) + result[key] = value.decode("ascii", "backslashreplace") elif isinstance(value, enum.Enum): result[key] = value.value elif isinstance(value, enum.IntEnum): @@ -70,5 +70,3 @@ def __init__(self, request: RequestBase, result: Optional[ResultBase]) -> None: @abc.abstractmethod def build(self) -> None: pass - - diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index 75d22d6c4..7c3096ec1 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -81,7 +81,7 @@ def _data_operate(self) -> None: url = f"{CONFIG.backend.url}/GameSpy/{self._client.server_config.server_name}/{self.__class__.__name__}/" # fmt: on - data = self._request.to_serializable_dict() + data = self._request.to_json() data["server_id"] = str(self._client.server_config.server_id) response = requests.post(url, json=data) From 6ce28a2c6e9250c58bc3d201a4a251afcec74635 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 6 Sep 2024 08:51:35 +0000 Subject: [PATCH 111/231] fix: backend router url --- src/backends/routers/gamespy/gstats.py | 12 ++++++------ src/backends/routers/gamespy/natneg.py | 17 ++++++++--------- 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/backends/routers/gamespy/gstats.py b/src/backends/routers/gamespy/gstats.py index 0aabd70aa..168a1243f 100644 --- a/src/backends/routers/gamespy/gstats.py +++ b/src/backends/routers/gamespy/gstats.py @@ -4,32 +4,32 @@ router = APIRouter() -@router.post(f"{GAMESTATUS}/AuthGameHandler/") +@router.post(f"{GAMESTATUS}/AuthGameHandler") async def update_item(request: AuthGameRequest): raise NotImplementedError() -@router.post(f"{GAMESTATUS}/AuthPlayerHandler/") +@router.post(f"{GAMESTATUS}/AuthPlayerHandler") async def update_item(request: AuthPlayerRequest): raise NotImplementedError() -@router.post(f"{GAMESTATUS}/NewGameHandler/") +@router.post(f"{GAMESTATUS}/NewGameHandler") async def update_item(request: NewGameRequest): raise NotImplementedError() -@router.post(f"{GAMESTATUS}/GetPlayerDataHandler/") +@router.post(f"{GAMESTATUS}/GetPlayerDataHandler") async def update_item(request: GetPlayerDataRequest): raise NotImplementedError() -@router.post(f"{GAMESTATUS}/SetPlayerDataHandler/") +@router.post(f"{GAMESTATUS}/SetPlayerDataHandler") async def update_item(request: SetPlayerDataRequest): raise NotImplementedError() -@router.post(f"{GAMESTATUS}/UpdateGameHandler/") +@router.post(f"{GAMESTATUS}/UpdateGameHandler") async def update_item(request: UpdateGameRequest): raise NotImplementedError() diff --git a/src/backends/routers/gamespy/natneg.py b/src/backends/routers/gamespy/natneg.py index db912aed1..77ef3e7a0 100644 --- a/src/backends/routers/gamespy/natneg.py +++ b/src/backends/routers/gamespy/natneg.py @@ -2,42 +2,41 @@ from backends.protocols.gamespy.chat.requests import PingRequest from backends.protocols.gamespy.natneg.handlers import InitHandler -from backends.protocols.gamespy.natneg.requests import ConnectRequest, ErtAckRequest, InitRequest, ReportRequest +from backends.protocols.gamespy.natneg.requests import AddressCheckRequest, ConnectRequest, ErtAckRequest, InitRequest, ReportRequest from backends.urls import NATNEG -from servers.natneg.src.contracts.requests import AddressCheckRequest router = APIRouter() -@router.post(f"{NATNEG}/AddressCheckHandler/") -async def address_check(request: AddressCheckRequest): +@router.post(f"{NATNEG}/AddressCheckHandler") +async def address_check(request: AddressCheckRequest) -> dict: raise NotImplementedError() -@router.post(f"{NATNEG}/ConnectHandler/") +@router.post(f"{NATNEG}/ConnectHandler") async def connect(request: ConnectRequest): raise NotImplementedError() -@router.post(f"{NATNEG}/ErtAckHandler/") +@router.post(f"{NATNEG}/ErtAckHandler") async def ert_ack(request: ErtAckRequest): raise NotImplementedError() -@router.post(f"{NATNEG}/InitHandler/") +@router.post(f"{NATNEG}/InitHandler") async def init(request: InitRequest): handler = InitHandler(request) await handler.handle() return handler.response -@router.post(f"{NATNEG}/PingHandler/") +@router.post(f"{NATNEG}/PingHandler") async def ping(request: PingRequest): raise NotImplementedError() -@router.post(f"{NATNEG}/ReportHandler/") +@router.post(f"{NATNEG}/ReportHandler") async def report(request: ReportRequest): raise NotImplementedError() From c8b9673aa656aa9fe6c1e7caae7e01a903a0b1a9 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 7 Oct 2024 01:02:47 +0800 Subject: [PATCH 112/231] refactor: fixed and tested docker compose file --- Dockerfile | 17 ++++++++++ docker-compose-unispy-env.yml | 31 +++++++++++++++++ docker-compose-unispy-server.yml | 58 ++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 Dockerfile create mode 100644 docker-compose-unispy-env.yml create mode 100644 docker-compose-unispy-server.yml diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 000000000..56d45abaa --- /dev/null +++ b/Dockerfile @@ -0,0 +1,17 @@ +# Use the official Python image from the Docker Hub +FROM python:3.12-slim + +# Set the working directory in the container +WORKDIR /unispy-server + +# Copy the requirements file into the container +COPY requirements.txt . + +# Install the dependencies +RUN pip install --no-cache-dir -r requirements.txt + +# Copy the rest of the application code into the container +COPY . . + +# Specify the command to run the application +CMD ["python", "app.py"] \ No newline at end of file diff --git a/docker-compose-unispy-env.yml b/docker-compose-unispy-env.yml new file mode 100644 index 000000000..9940edb98 --- /dev/null +++ b/docker-compose-unispy-env.yml @@ -0,0 +1,31 @@ +version: '3' + +services: + redis: + image: redis + restart: always + ports: + - "6379:6379" + command: redis-server --requirepass 123456 + + mongodb: + image: mongo + restart: always + ports: + - "27017:27017" + environment: + MONGO_INITDB_ROOT_USERNAME: unispy + MONGO_INITDB_ROOT_PASSWORD: 123456 + + postgresql: + image: postgres:14 + container_name: unispy_postgresql + ports: + - "5432:5432" + restart: always + environment: + POSTGRES_USER: unispy + POSTGRES_PASSWORD: 123456 + POSTGRES_DB: unispy + volumes: + - ./common/UniSpy_pg.sql:/docker-entrypoint-initdb.d/init.sql \ No newline at end of file diff --git a/docker-compose-unispy-server.yml b/docker-compose-unispy-server.yml new file mode 100644 index 000000000..fff680e2a --- /dev/null +++ b/docker-compose-unispy-server.yml @@ -0,0 +1,58 @@ +version: '3.8' + + +services: + backends: + image: unispy-python + ports: + - "8000:8000" + command: python backends/home.py + restart: always + pcm: + image: unispy-python + restart: always + ports: + - "29900:29900" + command: python servers/presence_connection_manager/application/server_launcher.py + psp: + image: unispy-python + restart: always + ports: + - "29901:29901" + command: python servers/presence_search_player/application/server_launcher.py + natneg: + image: unispy-python + restart: always + ports: + - "27901:27901" + command: python servers/natneg/application/server_launcher.py + chat: + image: unispy-python + restart: always + ports: + - "6667:6667" + command: python servers/chat/application/server_launcher.py + gstats: + image: unispy-python + restart: always + ports: + - "29920:29920" + command: python servers/game_status/application/server_launcher.py + qr: + image: unispy-python + restart: always + ports: + - "27900:27900" + command: python servers/query_report/application/server_launcher.py + sbv2: + image: unispy-python + restart: always + ports: + - "28900:28900" + command: python servers/server_browser/application/server_launcher.py + web: + image: unispy-python + restart: always + ports: + - "80:80" + command: python servers/web_services/application/server_launcher.py \ No newline at end of file From 444f6f2fa1d16cb54898fa6a5dc00efc78c3c392 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sun, 6 Oct 2024 17:06:13 +0000 Subject: [PATCH 113/231] refactor: removed aux files --- .DS_Store | Bin 6148 -> 0 bytes src/.DS_Store | Bin 6148 -> 0 bytes src/.github/dependabot.yml | 12 ---- src/.vscode/launch.json | 2 +- src/Dockerfile | 17 ------ src/README | 35 ----------- src/docker-compose-unispy-env.yaml | 30 --------- src/docker-compose-unispy-server.yml | 57 ------------------ src/library/src/abstractions/contracts.py | 6 +- src/library/src/database/pg_orm.py | 2 +- src/servers/chat/src/aggregates/brockers.py | 2 +- .../src/v2/abstractions/contrancts.py | 41 +++++++++++++ 12 files changed, 48 insertions(+), 156 deletions(-) delete mode 100644 .DS_Store delete mode 100644 src/.DS_Store delete mode 100644 src/.github/dependabot.yml delete mode 100644 src/Dockerfile delete mode 100644 src/README delete mode 100644 src/docker-compose-unispy-env.yaml delete mode 100644 src/docker-compose-unispy-server.yml create mode 100644 src/servers/query_report/src/v2/abstractions/contrancts.py diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 7af9a8e28a2b3dd413ff6d5cea925321bc28f625..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKy>8nu5I)9^OEn1Mp+GWn>88P|oG2Lr2#z~+E1;qs+9Hu6BN3p8fhEUi92a_x z_Ko@keVrc3i{Za6849D}8OM)0@+f>CMA8QUtT#$MfHnYdbcM~A*sL)%u6)I|DAGb? zdW|$0m$ykSla;wPU=^?mY@PyY?`}f^Bgi0!`ThGVxy`e(>U4gytwwXZ^@4N0!*}`D z=vt1WDy~LlFCKnYPw(UEI-&jtDf3xBPCjOX@p=2znJlU}D+WWIoTUSt^5s)jq;lMo zqarQ!I*}Q09EUsS?Ssk0KRI%R7j&ntm;^z`72OkmI(7K|Ywz^k#m%?-$;0&f52Gr> zBS_mG;UoOS&K$+NXjtU3xWcH)Xu%~&xWS)PDyiZ|ZzjQ*h)Jc<4S$t3|A zP{I%{H;j64+;xQ?1T*Sg|L957iQW?F$8Y0VBW)PI^Q={{gE)f5Oe&&DWmIA?la6sg^FqSfph*Wt#RsD|GpbOS zemkx&^mJgMLEBmdtOAP)G|XdL-T!a@UjHv9*(a-jRp7r;fHnJ3zlW6QZe1a#?plF< sfv!x=s}1TDZ0I`H9d#8Splic1p*n~mVQmm2X!ehQmccewfq$yNACn!{hyVZp diff --git a/src/.DS_Store b/src/.DS_Store deleted file mode 100644 index 193262bcbdff0866577cc1e23ba5718c6feed83a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeHKI|>3p3{6x}u(7n9D|mxJ^aOhW3&lnh6s@=NTprDr52CC#f{naD@@6u5v+OH2 z8xhg@c|8-Eh{yg>muLQd8@Ilv zF-rxg02QDDRDcS6setuf*k~NcNCl_>6?iCM--iM>tch))e>xC+1ON_@cEj3d31G1V zuqL*F$iOtHz@Tc57#eisOV-uIHZbU-Iech7S#v^Be>(0jUM^Y#8L0pjcvYYu+llr6 zCH%wu|4QPH3Q&Q+Qa}fbW--SrWpC}gob}oQU%{>B1~a>v35N5qNppj Y#(7O_1D%e%(}DaMFkNU=;MWS=0Vkdn#{d8T diff --git a/src/.github/dependabot.yml b/src/.github/dependabot.yml deleted file mode 100644 index f33a02cd1..000000000 --- a/src/.github/dependabot.yml +++ /dev/null @@ -1,12 +0,0 @@ -# To get started with Dependabot version updates, you'll need to specify which -# package ecosystems to update and where the package manifests are located. -# Please see the documentation for more information: -# https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates -# https://containers.dev/guide/dependabot - -version: 2 -updates: - - package-ecosystem: "devcontainers" - directory: "/" - schedule: - interval: weekly diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json index 9e374c4f2..438deacb6 100644 --- a/src/.vscode/launch.json +++ b/src/.vscode/launch.json @@ -13,7 +13,7 @@ "env": { "PYTHONPATH": "${workspaceFolder}", }, - "justMyCode": true + "justMyCode": false }, { "name": "pcm", diff --git a/src/Dockerfile b/src/Dockerfile deleted file mode 100644 index 56d45abaa..000000000 --- a/src/Dockerfile +++ /dev/null @@ -1,17 +0,0 @@ -# Use the official Python image from the Docker Hub -FROM python:3.12-slim - -# Set the working directory in the container -WORKDIR /unispy-server - -# Copy the requirements file into the container -COPY requirements.txt . - -# Install the dependencies -RUN pip install --no-cache-dir -r requirements.txt - -# Copy the rest of the application code into the container -COPY . . - -# Specify the command to run the application -CMD ["python", "app.py"] \ No newline at end of file diff --git a/src/README b/src/README deleted file mode 100644 index c3499728f..000000000 --- a/src/README +++ /dev/null @@ -1,35 +0,0 @@ -[![license](https://img.shields.io/github/license/GameProgressive/UniSpyServer.svg)](../LICENSE) -![CIPass](https://github.com/GameProgressive/UniSpyServer/workflows/CI/badge.svg)\ -![platforms](https://img.shields.io/badge/platform-win32%20%7C%20win64%20%7C%20linux%20%7C%20osx-brightgreen.svg)\ -[![discord-banner](https://discord.com/api/guilds/512314008079171615/widget.png?style=banner2)](https://discord.gg/NpggYaD) - -UniSpy is a GameSpy Project that aims to create GameSpy services. - -The server is written in C# and is inspired by the old OpenSpy project. - -## Information & Documentation -See the [wiki](https://github.com/GameProgressive/UniSpyServer/wiki) for more information about project and the UniSpy Server. -You can also use our Software Development Kit to create games or learn about the [UniSpySDK](https://github.com/GameProgressive/UniSpySDK). - -Technical papers and documentation about the GameSpy protocol and the games that use it, [GameSpyDocs](https://github.com/GameProgressive/GameSpyDocs). - -## Credits -* The [contributors](https://github.com/GameProgressive/UniSpyServer/graphs/contributors) of the project -* [Luigi Auriemma](https://aluigi.altervista.org/papers.htm#distrust) for his gamespy papers that were used as a reference -* [BattleSpy](https://github.com/BF2Statistics/BattleSpy) for their library, that we used as a base for UniSpy -* [NetCoreServer](https://github.com/chronoxor/NetCoreServer) for their TCP and UDP server - - -## License -This project is licensed under the [GNU Affero General Public License v3.0](../LICENSE). - - -## Why rewrite C# to python -* The vscode extensions for C# development is become more and more hard to use, and microsoft abandoned the open-source OmniSharp project, replacing it with ites own closed source language server. -* The c# project seems hard to run by users, it require a lot of deploy knowledge and hard for collaborations, for the future of the gamespy emulator, I choose to rewrite this into a opensource and easy high level language - python. - -## Setup instructions - -1. Install postgresql and redis -2. Python version > 3.10 -3. export UNISPY_CONFIG= \ No newline at end of file diff --git a/src/docker-compose-unispy-env.yaml b/src/docker-compose-unispy-env.yaml deleted file mode 100644 index 173087e2e..000000000 --- a/src/docker-compose-unispy-env.yaml +++ /dev/null @@ -1,30 +0,0 @@ -version: '3' - -services: - redis: - image: redis - restart: always - ports: - - "6379:6379" - command: redis-server --requirepass 123456 - - mongodb: - image: mongo - restart: always - ports: - - "27017:27017" - environment: - MONGO_INITDB_ROOT_USERNAME: unispy - MONGO_INITDB_ROOT_PASSWORD: 123456 - - postgresql: - image: postgres:14 - ports: - - "5432:5432" - restart: always - environment: - POSTGRES_USER: unispy - POSTGRES_PASSWORD: 123456 - POSTGRES_DB: unispy - volumes: - - ../common/postgre_creation.sql:/docker-entrypoint-initdb.d/postgre_creation.sql diff --git a/src/docker-compose-unispy-server.yml b/src/docker-compose-unispy-server.yml deleted file mode 100644 index 7b2eefbc3..000000000 --- a/src/docker-compose-unispy-server.yml +++ /dev/null @@ -1,57 +0,0 @@ -version: '3' - -services: - backends: - image: unispy-python - restart: always - ports: - - "8000:8000" - command: python backends/home.py - pcm: - image: unispy-python - restart: always - ports: - - "29900:29900" - command: python servers/presence_connection_manager/application/server_launcher.py - psp: - image: unispy-python - restart: always - ports: - - "29901:29901" - command: python servers/presence_search_player/application/server_launcher.py - natneg: - image: unispy-python - restart: always - ports: - - "27901:27901" - command: python servers/natneg/application/server_launcher.py - chat: - image: unispy-python - restart: always - ports: - - "6667:6667" - command: python servers/chat/application/server_launcher.py - gstats: - image: unispy-python - restart: always - ports: - - "29920:29920" - command: python servers/game_status/application/server_launcher.py - qr: - image: unispy-python - restart: always - ports: - - "27900:27900" - command: python servers/query_report/application/server_launcher.py - sbv2: - image: unispy-python - restart: always - ports: - - "28900:28900" - command: python servers/server_browser/application/server_launcher.py - web: - image: unispy-python - restart: always - ports: - - "80:80" - command: python servers/web_services/application/server_launcher.py \ No newline at end of file diff --git a/src/library/src/abstractions/contracts.py b/src/library/src/abstractions/contracts.py index e2df960ca..c99c4db95 100644 --- a/src/library/src/abstractions/contracts.py +++ b/src/library/src/abstractions/contracts.py @@ -8,8 +8,8 @@ class RequestBase: - command_name: object = None - raw_request: object = None + command_name: object + raw_request: object def __init__(self, raw_request: object) -> None: """ @@ -27,6 +27,8 @@ def __init__(self, raw_request: object) -> None: raise Exception("Unsupported raw_request type") self.raw_request = raw_request return + # self.command_name = None + # self.raw_request = None def parse(self) -> None: pass diff --git a/src/library/src/database/pg_orm.py b/src/library/src/database/pg_orm.py index 6ed014807..7412a9e60 100644 --- a/src/library/src/database/pg_orm.py +++ b/src/library/src/database/pg_orm.py @@ -200,5 +200,5 @@ def connect_to_db() -> Session: if __name__ == "__main__": session = connect_to_db() - # session.query().filter + session.query(Users.userid == 0) pass diff --git a/src/servers/chat/src/aggregates/brockers.py b/src/servers/chat/src/aggregates/brockers.py index 9eec99d12..2c60ec803 100644 --- a/src/servers/chat/src/aggregates/brockers.py +++ b/src/servers/chat/src/aggregates/brockers.py @@ -13,7 +13,7 @@ def __init__(self, name: str) -> None: super().__init__(name) url = f"{CONFIG.backend.url}/{name}" self._subscriber = \ - websocket.WebSocketApp(self._backend_url, + websocket.WebSocketApp(url=url, on_message=self.receive_message, on_error=None, on_close=None) diff --git a/src/servers/query_report/src/v2/abstractions/contrancts.py b/src/servers/query_report/src/v2/abstractions/contrancts.py new file mode 100644 index 000000000..dc8dd0b32 --- /dev/null +++ b/src/servers/query_report/src/v2/abstractions/contrancts.py @@ -0,0 +1,41 @@ +import library.src.abstractions.contracts +from servers.query_report.src.exceptions.exceptions import QRException +from servers.query_report.src.v2.enums.general import RequestType + +MAGIC_DATA = [0xFE, 0xFD] + + +class RequestBase(library.src.abstractions.contracts.RequestBase): + instant_key: int + command_name: RequestType + raw_request: bytes + + def __init__(self, raw_request: bytes) -> None: + assert isinstance(raw_request, bytes) + super().__init__(raw_request) + + def parse(self): + if len(self.raw_request) < 3: + raise QRException + self.command_name = RequestType(self.raw_request[0]) + self.instant_key = int(self.raw_request[1:5]) + + + +import abc +import library.src.abstractions.contracts +from servers.query_report.src.v2.enums.general import PacketType + + +class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): + packet_type: PacketType = None + + + +import abc +import library.src.abstractions.contracts + + +class ResponseBase(library.src.abstractions.contracts.ResponseBase, abc.ABC): + _result: ResultBase + _request: RequestBase From bdd19922c2905a7492b6a10446ce7d0fbdd9ba39 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 16 Oct 2024 02:28:13 +0000 Subject: [PATCH 114/231] refactor(lib): created and tested websocket brocker --- src/.vscode/launch.json | 2 +- .../gamespy/game_traffic_relay/data.py | 23 ++++++ .../relay_server_info.py | 0 src/backends/protocols/gamespy/natneg/data.py | 22 ++---- src/backends/routers/gamespy/chat.py | 33 +++++--- .../routers/gamespy/server_browser.py | 16 ++-- src/backends/tests/http_tests/chat.http | 0 src/backends/tests/http_tests/game_stats.http | 0 src/backends/tests/http_tests/natneg.http | 0 .../presence_connection_manager.http | 0 .../http_tests/presence_search_player.http | 0 .../tests/http_tests/query_report.http | 0 .../tests/http_tests/server_browser.http | 0 .../tests/http_tests/web_services.http | 0 src/library/src/abstractions/brocker.py | 18 ++--- src/library/src/network/brockers.py | 79 +++++++++++++++++++ src/library/src/unispy_server_config.py | 70 ++++++++++------ .../game_status/src/contracts/requests.py | 9 ++- .../game_status/src/contracts/responses.py | 6 ++ .../game_status/src/contracts/results.py | 5 ++ 20 files changed, 214 insertions(+), 69 deletions(-) rename src/backends/protocols/gamespy/{natneg => game_traffic_relay}/relay_server_info.py (100%) create mode 100644 src/backends/tests/http_tests/chat.http create mode 100644 src/backends/tests/http_tests/game_stats.http create mode 100644 src/backends/tests/http_tests/natneg.http create mode 100644 src/backends/tests/http_tests/presence_connection_manager.http create mode 100644 src/backends/tests/http_tests/presence_search_player.http create mode 100644 src/backends/tests/http_tests/query_report.http create mode 100644 src/backends/tests/http_tests/server_browser.http create mode 100644 src/backends/tests/http_tests/web_services.http create mode 100644 src/library/src/network/brockers.py diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json index 438deacb6..9e374c4f2 100644 --- a/src/.vscode/launch.json +++ b/src/.vscode/launch.json @@ -13,7 +13,7 @@ "env": { "PYTHONPATH": "${workspaceFolder}", }, - "justMyCode": false + "justMyCode": true }, { "name": "pcm", diff --git a/src/backends/protocols/gamespy/game_traffic_relay/data.py b/src/backends/protocols/gamespy/game_traffic_relay/data.py index e69de29bb..d30032c5b 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/data.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/data.py @@ -0,0 +1,23 @@ + +from uuid import UUID +from backends.protocols.gamespy.game_traffic_relay.relay_server_info import RelayServerInfo + + +def get_available_relay_serves() -> list[RelayServerInfo]: + """ + Return + ------ + list of ip:port + """ + result: list[RelayServerInfo] = list(RelayServerInfo.objects) + return result + + +def update_relay_server(info: RelayServerInfo): + info.save() + + +def delete_relay_server(server_id: UUID, ip_address: str, port: int): + info = RelayServerInfo.object( + server_id=server_id, public_ip_address=ip_address, public_port=port) + info.delete() diff --git a/src/backends/protocols/gamespy/natneg/relay_server_info.py b/src/backends/protocols/gamespy/game_traffic_relay/relay_server_info.py similarity index 100% rename from src/backends/protocols/gamespy/natneg/relay_server_info.py rename to src/backends/protocols/gamespy/game_traffic_relay/relay_server_info.py diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py index e27dd961e..c2c138449 100644 --- a/src/backends/protocols/gamespy/natneg/data.py +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -1,9 +1,7 @@ -# from servers.natneg.contracts.requests import InitRequest from uuid import UUID -from backends.protocols.gamespy.natneg.relay_server_info import RelayServerInfo +from backends.protocols.gamespy.game_traffic_relay.relay_server_info import RelayServerInfo from backends.protocols.gamespy.natneg.requests import InitRequest from backends.protocols.gamespy.natneg.init_packet_info import InitPacketInfo, NatFailInfo -from mongoengine import QuerySet def store_init_packet(request: InitRequest) -> None: @@ -16,25 +14,15 @@ def count_init_info(cookie: int, version: int) -> int: return result -def get_available_relay_serves() -> list[RelayServerInfo]: - """ - Return - ------ - list of ip:port - """ - result: list[RelayServerInfo] = list(RelayServerInfo.objects) - return result - - -async def get_init_infos(server_id: UUID, cookie: int) -> list[InitPacketInfo]: - result: list[InitPacketInfo] = await InitPacketInfo.objects( +def get_init_infos(server_id: UUID, cookie: int) -> list[InitPacketInfo]: + result: list[InitPacketInfo] = InitPacketInfo.objects( server_id=server_id, cookie=cookie) return result -async def update_init_info(info: InitPacketInfo) -> None: +def update_init_info(info: InitPacketInfo) -> None: assert isinstance(info, InitPacketInfo) - await info.save() + info.save() def remove_init_info(info: InitPacketInfo) -> None: diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 60f41e5ac..0fc0c3916 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -1,15 +1,30 @@ -# from backends.urls import CHAT -# from fastapi import FastAPI, WebSocket +from backends.urls import CHAT +from fastapi import APIRouter, FastAPI, WebSocket, WebSocketDisconnect -# app = FastAPI() +router = APIRouter() +clients: list[WebSocket] = [] +@router.websocket("/chat") +async def websocket_endpoint(websocket: WebSocket): + await websocket.accept() + if isinstance(websocket,WebSocket): + clients.append(websocket) + try: + while True: + message = await websocket.receive_text() + print(message) + for client in clients: + await client.send_text(message) + except WebSocketDisconnect: + clients.remove(websocket) + print("Client disconnected") -# if __name__ == "__main__": -# import uvicorn -# from fastapi import FastAPI +if __name__ == "__main__": + import uvicorn + from fastapi import FastAPI -# app = FastAPI() -# app.include_router(router) -# uvicorn.run(router, host="0.0.0.0", port=8000) \ No newline at end of file + app = FastAPI() + app.include_router(router) + uvicorn.run(router, host="0.0.0.0", port=8000) diff --git a/src/backends/routers/gamespy/server_browser.py b/src/backends/routers/gamespy/server_browser.py index 1599a0299..959798bb2 100644 --- a/src/backends/routers/gamespy/server_browser.py +++ b/src/backends/routers/gamespy/server_browser.py @@ -1,21 +1,27 @@ -from fastapi import APIRouter, WebSocket +from fastapi import APIRouter, WebSocket, WebSocketDisconnect from backends.protocols.gamespy.server_browser.requests import SendMessageRequest, ServerInfoRequest, ServerListRequest from backends.urls import SERVER_BROWSER_V1, SERVER_BROWSER_V2 router = APIRouter() # todo maybe implement this in websocket way +client_pool: dict[str, WebSocket] = {} + @router.websocket(f"/{SERVER_BROWSER_V2}/AdHocHandler") async def check(websocket: WebSocket): """ notify every server browser to send message to its client """ + raise NotImplementedError() await websocket.accept() while True: - data = await websocket.receive_text() - await websocket.send_text(f"Message text was: {data}") - raise NotImplementedError() + try: + data = await websocket.receive_text() + client_pool[websocket.client.host] = websocket + await websocket.send_text(f"Message text was: {data}") + except WebSocketDisconnect: + del client_pool[websocket.client.host] @router.post(f"/{SERVER_BROWSER_V2}/SendMessageHandler") @@ -39,4 +45,4 @@ async def server_list(request: ServerListRequest): app = FastAPI() app.include_router(router) - uvicorn.run(router, host="0.0.0.0", port=8000) \ No newline at end of file + uvicorn.run(router, host="0.0.0.0", port=8000) diff --git a/src/backends/tests/http_tests/chat.http b/src/backends/tests/http_tests/chat.http new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/http_tests/game_stats.http b/src/backends/tests/http_tests/game_stats.http new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/http_tests/natneg.http b/src/backends/tests/http_tests/natneg.http new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/http_tests/presence_connection_manager.http b/src/backends/tests/http_tests/presence_connection_manager.http new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/http_tests/presence_search_player.http b/src/backends/tests/http_tests/presence_search_player.http new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/http_tests/query_report.http b/src/backends/tests/http_tests/query_report.http new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/http_tests/server_browser.http b/src/backends/tests/http_tests/server_browser.http new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/http_tests/web_services.http b/src/backends/tests/http_tests/web_services.http new file mode 100644 index 000000000..e69de29bb diff --git a/src/library/src/abstractions/brocker.py b/src/library/src/abstractions/brocker.py index 1a9298a2d..50b5c109d 100644 --- a/src/library/src/abstractions/brocker.py +++ b/src/library/src/abstractions/brocker.py @@ -1,24 +1,22 @@ import abc - -from servers.chat.src.aggregates.channel import MIN_CHANNEL_NAME_LENGTH -from servers.chat.src.exceptions.general import ChatException +from typing import final class BrockerBase: _subscriber: object is_started: bool = False _name: str - call_backs: list + _call_back_func: "function" """ brocker subscribe name """ - def __init__(self, name: str) -> None: + def __init__(self, name: str, call_back_func: "function") -> None: assert isinstance(name, str) - if len(name) < MIN_CHANNEL_NAME_LENGTH: - raise ChatException(f"The channel name length must larget than { - MIN_CHANNEL_NAME_LENGTH}") + assert callable(call_back_func) + self._name = name + self._call_back_func = call_back_func @abc.abstractmethod def subscribe(self): @@ -27,13 +25,13 @@ def subscribe(self): """ pass - @abc.abstractmethod + @final def receive_message(self, message): + self._call_back_func(message) pass @abc.abstractmethod def publish_message(self, message): - pass @abc.abstractmethod diff --git a/src/library/src/network/brockers.py b/src/library/src/network/brockers.py new file mode 100644 index 000000000..41af61f53 --- /dev/null +++ b/src/library/src/network/brockers.py @@ -0,0 +1,79 @@ + +import threading +from time import sleep +import websocket +from redis import Redis +from library.src.abstractions.brocker import BrockerBase +from redis.client import PubSub +websocket.enableTrace(True) + + +class RedisBrocker(BrockerBase): + _client: Redis + _subscriber: PubSub + + def __init__(self, name: str, call_back_func: "function") -> None: + super().__init__(name, call_back_func) + self._subscriber = self._client.pubsub() + + def subscribe(self): + self.is_started = True + self._subscriber.subscribe(self._name) + threading.Thread(target=self.get_message).start() + + def get_message(self): + for message in self._subscriber.get_message(): + if not self.is_started: + break + if message["type"] == "message": + self.receive_message(message['data'].decode('utf-8')) + + def unsubscribe(self): + self.is_started = False + self._subscriber.unsubscribe(self._name) + self._subscriber.close() + + def publish_message(self, message): + self._client.publish(self._name, message) + + +class WebsocketBrocker(BrockerBase): + _publisher: websocket.WebSocket = None + _subscriber: websocket.WebSocketApp + + def __init__(self, name: str, url: str, call_back_func: "function") -> None: + super().__init__(name, call_back_func) + self._subscriber = websocket.WebSocketApp( + url, + on_message=lambda _, m: self.receive_message(m), + on_error=print, + on_close=print, + on_open=self._on_open) + + def _on_open(self, ws): + self._publisher: websocket.WebSocket = ws + + def _on_message(self, _, message): + self.receive_message(message) + + def subscribe(self): + threading.Thread(target=self._subscriber.run_forever).start() + # wait for connection establish + while self._publisher is not None: + break + + def unsubscribe(self): + self._subscriber.close() + + def publish_message(self, message): + self._publisher.send(message) + + +if __name__ == "__main__": + + ws = WebsocketBrocker(name="test_channel", + url="ws://127.0.0.1:8000/chat", call_back_func=print) + ws.subscribe() + ws.publish_message("hello") + while True: + pass diff --git a/src/library/src/unispy_server_config.py b/src/library/src/unispy_server_config.py index d955f7638..4bd22ddf4 100644 --- a/src/library/src/unispy_server_config.py +++ b/src/library/src/unispy_server_config.py @@ -1,18 +1,15 @@ +from dataclasses import dataclass import os -from typing import Any, Literal, Optional +from typing import Literal, Optional from uuid import UUID -from pydantic import BaseModel, Field from library.src.exceptions.general import UniSpyException - - - -class PostgreSql(BaseModel): +@dataclass +class PostgreSql: server: str - # Ensures port is between 1 and 65535 - port: int = Field(..., ge=1, le=65535) + port: int database: str username: str password: str @@ -22,15 +19,18 @@ class PostgreSql(BaseModel): ssl_password: Optional[str] = None # Optional field for SSL password root_cert: Optional[str] = None # Optional field for root certificate + def __post_init__(self): + pass + @property def url(self) -> str: return f"postgresql://{self.username}:{self.password}@{self.server}:{self.port}/{self.database}?sslmode={self.ssl_mode}" -class RedisConfig(BaseModel): +@dataclass +class RedisConfig: server: str - # Ensures port is between 1 and 65535 - port: int = Field(..., ge=1, le=65535) + port: int user: str password: str ssl: bool # Use bool for SSL flag @@ -48,51 +48,69 @@ def url(self) -> str: ) -class ServerConfig(BaseModel): +@dataclass +class ServerConfig: server_id: UUID - server_name: str = Field(..., min_length=1) - public_address: str = Field(..., min_length=1) - listening_port: int = Field( - ..., ge=1, le=65535 - ) # Ensures listening_port is between 1 and 65535 + server_name: str + public_address: str + listening_port: int # Ensures listening_port is between 1 and 65535 -class LoggingConfig(BaseModel): +@dataclass +class LoggingConfig: path: str min_log_level: Literal["debug", "info", "warning", "error"] + def __post_init__(self): + if "~" in self.path: + self.path = os.path.expanduser(self.path) + -class BackendConfig(BaseModel): +@dataclass +class BackendConfig: url: str -class MongoDbConfig(BaseModel): +@dataclass +class MongoDbConfig: server: str username: str password: str - port: int = Field(default=None) - database: str | None = Field(default=None) - url: str = Field(default=None) + port: int + database: str - def model_post_init(self, __context: Any) -> None: + @property + def url(self): url = f"mongodb+srv://{self.username}:{self.password}@{self.server}" if self.port is not None: url += f":{self.port}" if self.database is not None: url += f"/{self.database}" + return url -class UniSpyServerConfig(BaseModel): +@dataclass +class UniSpyServerConfig: postgresql: PostgreSql redis: RedisConfig backend: BackendConfig - servers: dict[str, ServerConfig] = Field(default_factory=dict) + servers: dict[str, ServerConfig] mongodb: MongoDbConfig logging: LoggingConfig + def __post_init__(self): + self.postgresql = PostgreSql(**self.postgresql) + self.redis = RedisConfig(**self.redis) + self.backend = BackendConfig(**self.backend) + for key, value in self.servers.items(): + self.servers[key] = ServerConfig(**value) + self.mongodb = MongoDbConfig(**self.mongodb) + self.logging = LoggingConfig(**self.logging) + unispy_config = os.environ.get("UNISPY_CONFIG") if unispy_config is None: + raise UniSpyException( "Unispy server config not found, you should set the UNISPY_CONFIG in the system enviroment." ) diff --git a/src/servers/game_status/src/contracts/requests.py b/src/servers/game_status/src/contracts/requests.py index 8ef234454..6755230f9 100644 --- a/src/servers/game_status/src/contracts/requests.py +++ b/src/servers/game_status/src/contracts/requests.py @@ -1,9 +1,10 @@ +from typing import final from library.src.extentions.gamespy_utils import convert_to_key_value from servers.game_status.src.abstractions.contracts import RequestBase from servers.game_status.src.enums.general import AuthMethod, PersistStorageType from servers.game_status.src.exceptions.general import GSException - +@final class AuthGameRequest(RequestBase): game_name: str @@ -27,6 +28,7 @@ def parse(self) -> None: raise GSException("port format is incorrect") +@final class AuthPlayerRequest(RequestBase): auth_type: AuthMethod profile_id: int @@ -58,6 +60,7 @@ def parse(self) -> None: raise GSException("unknown authp request type") +@final class GetPlayerDataRequest(RequestBase): profile_id: str storage_type: PersistStorageType @@ -103,6 +106,7 @@ def parse(self) -> None: self.is_get_all_data = False +@final class GetProfileIdRequest(RequestBase): nick: str keyhash: str @@ -121,6 +125,7 @@ def parse(self) -> None: self.keyhash = self.request_dict["keyhash"] +@final class NewGameRequest(RequestBase): is_client_local_storage_available: bool challenge: str = None @@ -145,6 +150,7 @@ def parse(self) -> None: self.challenge = self.request_dict["challenge"] +@final class SetPlayerDataRequest(RequestBase): profile_id: int storage_type: PersistStorageType @@ -195,6 +201,7 @@ def parse(self) -> None: self.data = self.request_dict["data"] +@final class UpdateGameRequest(RequestBase): connection_id: int = None is_done: bool diff --git a/src/servers/game_status/src/contracts/responses.py b/src/servers/game_status/src/contracts/responses.py index 11639f5fc..c6c74851f 100644 --- a/src/servers/game_status/src/contracts/responses.py +++ b/src/servers/game_status/src/contracts/responses.py @@ -1,9 +1,11 @@ +from typing import final from library.src.abstractions.contracts import ResponseBase from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, SetPlayerDataRequest from servers.game_status.src.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult +@final class AuthGameResponse(ResponseBase): _request: AuthGameRequest _result: AuthGameResult @@ -14,6 +16,7 @@ def build(self) -> None: # fmt: on +@final class AuthPlayerResponse(ResponseBase): _request: AuthPlayerRequest _result: AuthPlayerResult @@ -24,6 +27,7 @@ def build(self) -> None: # fmt: on +@final class GetPlayerDataResponse(ResponseBase): _request: GetPlayerDataRequest _result: GetPlayerDataResult @@ -34,6 +38,7 @@ def build(self) -> None: # fmt: on +@final class GetProfileIdResponse(ResponseBase): _request: GetProfileIdRequest _result: GetProfileIdResult @@ -44,6 +49,7 @@ def build(self) -> None: # fmt: on +@final class SetPlayerDataResponse(ResponseBase): _request: SetPlayerDataRequest _result: GetPlayerDataResult diff --git a/src/servers/game_status/src/contracts/results.py b/src/servers/game_status/src/contracts/results.py index e0186f496..66a24b65d 100644 --- a/src/servers/game_status/src/contracts/results.py +++ b/src/servers/game_status/src/contracts/results.py @@ -1,17 +1,22 @@ +from typing import final from servers.game_status.src.abstractions.contracts import ResultBase +@final class AuthGameResult(ResultBase): session_key: str +@final class AuthPlayerResult(ResultBase): profile_id: int +@final class GetPlayerDataResult(ResultBase): keyvalues: dict[str, str] +@final class GetProfileIdResult(ResultBase): profile_id: int From 91bb6ac5fa0892cb0217524cf54ce212d231fbbd Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 16 Oct 2024 09:27:40 +0000 Subject: [PATCH 115/231] refactor: small code change --- src/backends/routers/gamespy/chat.py | 78 ++++++++++++--- src/library/src/abstractions/handler.py | 7 ++ src/library/src/network/brockers.py | 4 +- src/servers/chat/src/abstractions/channel.py | 29 +++--- src/servers/chat/src/aggregates/brockers.py | 54 ----------- src/servers/chat/src/aggregates/channel.py | 36 +++++-- .../chat/src/aggregates/channel_broker.py | 19 ++++ .../chat/src/applications/server_launcher.py | 2 +- src/servers/chat/src/handlers/channel.py | 94 +++++++++++++++---- .../src/applications/server_launcher.py | 2 +- 10 files changed, 211 insertions(+), 114 deletions(-) delete mode 100644 src/servers/chat/src/aggregates/brockers.py create mode 100644 src/servers/chat/src/aggregates/channel_broker.py diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 0fc0c3916..443bea7c3 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -1,23 +1,79 @@ +from dataclasses import dataclass +import json +from typing import Optional +import uuid from backends.urls import CHAT from fastapi import APIRouter, FastAPI, WebSocket, WebSocketDisconnect router = APIRouter() -clients: list[WebSocket] = [] +channels: dict[str, list[WebSocket]] = {} +""" +{"channel_name" : "list of WebSocket"} +""" +clients: dict[str, WebSocket] = [] +""" +{"client ip and port" : WebSocket} +""" -@router.websocket("/chat") -async def websocket_endpoint(websocket: WebSocket): - await websocket.accept() - if isinstance(websocket,WebSocket): - clients.append(websocket) +@dataclass +class ChannelMessage: + channel_name: str + message: str + + def __post_init__(self): + if self.channel_name is None or len(self.channel_name < 3): + raise ValueError("channel name is not valid") + if self.message is None or len(self.message) < 3: + raise ValueError("message length is not valid") + if self.channel_name not in channels: + raise ValueError("channel is not registered") + + +@router.post(f"{CHAT}/add_channel") +def add_channel(channel_name: str, server_id: uuid.UUID, server_ip: str): + # first validate the server_id server_ip etc. info + # if server is valid we initialize the channel + + # we initialize the channel + if channel_name not in channels: + channels[channel_name] = [] + + +def check_request(request: str) -> Optional[ChannelMessage]: + ch_msg = None + try: + request_dict = json.loads(request) + ch_msg = ChannelMessage(**request_dict) + except: + return None + return ch_msg + + +@router.websocket(f"{CHAT}/channel") +async def websocket_endpoint(ws: WebSocket): + await ws.accept() + if isinstance(ws, WebSocket): + client_key = f"{ws.client.host}:{ws.client.port}" + clients[client_key](ws) try: while True: - message = await websocket.receive_text() - print(message) - for client in clients: - await client.send_text(message) + request = await ws.receive_text() + msg = check_request(request) + if msg is None: + return + channel_clients = channels[msg.channel_name] + for client in channel_clients: + # we do not send data to the publisher + if client == ws: + continue + + await client.send_text(msg.message) + except WebSocketDisconnect: - clients.remove(websocket) + client_key = f"{ws.client.host}:{ws.client.port}" + del clients[client_key] + channels[msg.channel_name].remove(ws) print("Client disconnected") diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index 7c3096ec1..de6573622 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -26,6 +26,7 @@ class CmdHandlerBase: """ whether need get data from backend """ + _is_debugging: bool = False def __init__(self, client: "ClientBase", request: "RequestBase") -> None: @@ -103,7 +104,13 @@ def _response_send(self) -> None: self._client.send(self._response) def _handle_exception(self, ex) -> None: + """ + override in child class if there are different exception handling behavior + """ UniSpyException.handle_exception(ex, self._client) + # if we are debugging the app we re-raise the exception + if CmdHandlerBase._is_debugging: + raise ex def _log_current_class(self) -> None: if self._client is None: diff --git a/src/library/src/network/brockers.py b/src/library/src/network/brockers.py index 41af61f53..1f4d1fa60 100644 --- a/src/library/src/network/brockers.py +++ b/src/library/src/network/brockers.py @@ -1,6 +1,4 @@ - import threading -from time import sleep import websocket from redis import Redis from library.src.abstractions.brocker import BrockerBase @@ -72,7 +70,7 @@ def publish_message(self, message): if __name__ == "__main__": ws = WebsocketBrocker(name="test_channel", - url="ws://127.0.0.1:8000/chat", call_back_func=print) + url="ws://127.0.0.1:8000/channel", call_back_func=print) ws.subscribe() ws.publish_message("hello") while True: diff --git a/src/servers/chat/src/abstractions/channel.py b/src/servers/chat/src/abstractions/channel.py index 9878b3b29..e02903744 100644 --- a/src/servers/chat/src/abstractions/channel.py +++ b/src/servers/chat/src/abstractions/channel.py @@ -1,4 +1,6 @@ +from library.src.abstractions.client import ClientBase from servers.chat.src.abstractions.contract import * +from servers.chat.src.abstractions.contract import RequestBase from servers.chat.src.abstractions.handler import PostLoginHandlerBase from servers.chat.src.aggregates.channel import Channel, ChannelManager from servers.chat.src.aggregates.channel_user import ChannelUser @@ -30,12 +32,15 @@ class ChannelHandlerBase(PostLoginHandlerBase): _user: ChannelUser _request: ChannelRequestBase + def __init__(self, client: ClientBase, request: RequestBase): + super().__init__(client, request) + def _request_check(self) -> None: if self._request.raw_request is None: return super()._request_check() if self._channel is None: - self._channel = ChannelManager.get_local_channel( + self._channel = ChannelManager.get_channel( self._request.channel_name ) if self._channel is None: @@ -56,24 +61,14 @@ def _request_check(self) -> None: def handle(self) -> None: super().handle() try: - # we do not publish message when the message is received from remote client - if self._client.is_remote_client: - return - if self._channel is None: - return - - if self.request.raw_request is None: - return - - self.publish_message() - self.update_channel_cache() + # todo check whether the broadcast message is same as responses + self._publish_message() + self._update_channel_cache() except Exception as e: self._handle_exception(e) - def publish_message(self): - raise NotImplementedError() - meg = RemoteMessage(self._request, self._client.get_remote_client()) - self._channel.broker.publish_message(msg) + def _publish_message(self): + self._channel.send_message_to_brocker(self._response.sending_buffer) - def update_channel_cache(self): + def _update_channel_cache(self): pass diff --git a/src/servers/chat/src/aggregates/brockers.py b/src/servers/chat/src/aggregates/brockers.py deleted file mode 100644 index 2c60ec803..000000000 --- a/src/servers/chat/src/aggregates/brockers.py +++ /dev/null @@ -1,54 +0,0 @@ -# import threading -import redis -import redis.client -from library.src.abstractions.brocker import BrockerBase -from library.src.unispy_server_config import CONFIG -import websocket - - -class WebSocketBrocker(BrockerBase): - _subscriber: websocket.WebSocketApp - - def __init__(self, name: str) -> None: - super().__init__(name) - url = f"{CONFIG.backend.url}/{name}" - self._subscriber = \ - websocket.WebSocketApp(url=url, - on_message=self.receive_message, - on_error=None, on_close=None) - - def subscribe(self): - if not self.is_started: - self._subscriber.run_forever(reconnect=5) - - def receive_message(self, message): - return super().receive_message(message) - - def unsubscribe(self): - self._subscriber.close() - - -class RedisBrocker(BrockerBase): - _subscriber: redis.client.PubSub - - def __init__(self, name: str) -> None: - super().__init__(name) - self._redis = redis.from_url(CONFIG.redis.url) - self._subscriber = self._redis.pubsub() - - def subscribe(self): - self._subscriber.subscribe(self._name) - for message in self._subscriber.listen(): - if message["type"] == "message": - print(message["data"]) - - def publish_message(self, message): - self._redis.publish(self._name, message) - - def unsubscribe(self): - self._subscriber.unsubscribe() - - -if __name__ == "__main__": - b = RedisBrocker("hello") - b.subscribe() diff --git a/src/servers/chat/src/aggregates/channel.py b/src/servers/chat/src/aggregates/channel.py index ff4f7fe46..e3d5468cd 100644 --- a/src/servers/chat/src/aggregates/channel.py +++ b/src/servers/chat/src/aggregates/channel.py @@ -1,7 +1,9 @@ import datetime +from typing import overload from uuid import UUID +from library.src.unispy_server_config import CONFIG from servers.chat.src.abstractions.contract import ResponseBase -from servers.chat.src.aggregates.brockers import RedisBrocker +from servers.chat.src.aggregates.channel_broker import ChannelBrocker from servers.chat.src.aggregates.channel_user import ChannelUser from servers.chat.src.aggregates.key_value_manager import KeyValueManager from servers.chat.src.aggregates.peer_room import PeerRoom @@ -44,7 +46,9 @@ def __init__(self, name: str, client: Client, password: str = None) -> None: self.previously_join_channel = client.info.previously_joined_channel self.room_type = PeerRoom.get_room_type(name) # setup the message broker - self._broker = None + self._broker = ChannelBrocker( + self.name, CONFIG.backend.url, self.get_message_from_brocker) + self._broker.subscribe() match self.room_type: case PeerRoomType.Group: @@ -78,8 +82,6 @@ def get_staging_room_name(self): def get_title_room_name(self): self.get_staging_room_name() - # from multiprocessing import Manager - ban_list: dict[str, ChannelUser] = {} users: dict[str, ChannelUser] = {} _creator_nick_name: str @@ -126,11 +128,12 @@ def _user_voice_permission(self, nick_name: str, enable: bool = True): user = self.users[nick_name] user.is_voiceable = enable + @overload def get_user(self, nick_name: str) -> ChannelUser: if nick_name in self.users: return self.users[nick_name] return None - + @overload def get_user(self, client: Client) -> ChannelUser: for user in self.users.values(): if ( @@ -148,14 +151,27 @@ def remove_bind_on_user_and_channel(leaver: ChannelUser): del leaver.channel.users[leaver.client.info.nick_name] del leaver.client.info.joined_channels[leaver.channel.name] - def multicast(self, sender: Client, message: ResponseBase, is_skip_snder=False): + def multicast(self, sender: Client, message: ResponseBase, is_skip_sender=False): for nick, user in self.users.items(): - if is_skip_snder: + if is_skip_sender: if sender.info.nick_name == nick: continue else: user.client.send(message) + def get_message_from_brocker(self, message: str): + """ + we directly send the message from brocker to all channel local user + """ + for nick, user in self.users.items(): + user.client.send(message) + + def send_message_to_brocker(self, message: str): + data = {"channel_name": self.name, "message": message} + import json + data_str = json.dumps(data) + self._broker.publish_message(data_str) + def remove_user(self, user: ChannelUser): user.client.info.previously_joined_channel @@ -165,15 +181,15 @@ class ChannelManager: """The code blow is for channel manage""" @staticmethod - def get_local_channel(name: str) -> Channel: + def get_channel(name: str) -> Channel: if name in ChannelManager.local_channels: return ChannelManager.local_channels[name] - def add_local_channel(channel: Channel): + def add_channel(channel: Channel): if channel.name not in ChannelManager.local_channels: ChannelManager.local_channels[channel.name] = channel @staticmethod - def remove_local_channel(name: str) -> None: + def remove_channel(name: str) -> None: if name in ChannelManager.local_channels: del ChannelManager.local_channels[name] diff --git a/src/servers/chat/src/aggregates/channel_broker.py b/src/servers/chat/src/aggregates/channel_broker.py new file mode 100644 index 000000000..5e7ed40f8 --- /dev/null +++ b/src/servers/chat/src/aggregates/channel_broker.py @@ -0,0 +1,19 @@ +from library.src.network.brockers import WebsocketBrocker +import requests + +from library.src.unispy_server_config import CONFIG +from servers.chat.src.applications.server_launcher import ServerLauncher +from servers.chat.src.exceptions.general import ChatException + + + +class ChannelBrocker(WebsocketBrocker): + def __init__(self, name: str, url: str, call_back_func: function) -> None: + super().__init__(name, url, call_back_func) + req = {"channel_name": name, "server_id": ServerLauncher.config.server_id, + "server_ip": ServerLauncher.config.public_address} + try: + requests.post( + f"{CONFIG.backend.url}/GameSpy/Chat/add_channel", req) + except Exception as e: + raise ChatException("Channel register on backend failed") diff --git a/src/servers/chat/src/applications/server_launcher.py b/src/servers/chat/src/applications/server_launcher.py index 441f29ced..c9af625cb 100644 --- a/src/servers/chat/src/applications/server_launcher.py +++ b/src/servers/chat/src/applications/server_launcher.py @@ -1,6 +1,6 @@ from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.tcp_handler import TcpServer -from library.src.unispy_server_config import CONFIG +from library.src.unispy_server_config import CONFIG, ServerConfig from servers.chat.src.applications.client import Client diff --git a/src/servers/chat/src/handlers/channel.py b/src/servers/chat/src/handlers/channel.py index d5fed05de..c724d9c4d 100644 --- a/src/servers/chat/src/handlers/channel.py +++ b/src/servers/chat/src/handlers/channel.py @@ -1,6 +1,8 @@ -from library.src.abstractions.client import ClientBase from servers.chat.src.abstractions.channel import ChannelHandlerBase +from servers.chat.src.aggregates.channel import Channel, ChannelManager +from servers.chat.src.aggregates.channel_user import ChannelUser from servers.chat.src.aggregates.response_name import * +from servers.chat.src.applications.client import Client from servers.chat.src.contracts.requests.channel import ( GetCKeyRequest, GetChannelKeyRequest, @@ -43,19 +45,32 @@ class GetChannelKeyHandler(ChannelHandlerBase): _request: GetChannelKeyRequest _result: GetChannelKeyResult - def __init__(self, client: ClientBase, request: GetChannelKeyRequest): + def __init__(self, client: Client, request: GetChannelKeyRequest): assert isinstance(request, GetChannelKeyRequest) + self._is_fetching_data = True super().__init__(client, request) + def _publish_message(self): + pass + + def _update_channel_cache(self): + pass + class GetCKeyHandler(ChannelHandlerBase): _request: GetCKeyRequest _result: GetCKeyResult - def __init__(self, client: ClientBase, request: GetCKeyRequest): + def __init__(self, client: Client, request: GetCKeyRequest): assert isinstance(request, GetCKeyRequest) super().__init__(client, request) + def _publish_message(self): + pass + + def _update_channel_cache(self): + pass + def _response_construct(self): self._response = GetCKeyResponse(self._request, self._result) @@ -64,31 +79,66 @@ class JoinHandler(ChannelHandlerBase): _request: JoinRequest _result: JoinResult - def __init__(self, client: ClientBase, request: JoinRequest): + def __init__(self, client: Client, request: JoinRequest): assert isinstance(request, JoinRequest) super().__init__(client, request) + def _check_user_in_remote(self): + """ + todo maybe do not need because there are nick handler? + """ + pass + + def _check_user_in_local(self): + self._channel = ChannelManager.get_channel( + self._request.channel_name) + if self._channel is not None: + if self._client.info.nick_name in self._channel.users: + raise ChatException("user is already in channel") + # if channel still none we create the channel + if self._channel is None: + self._channel = Channel( + self._request.channel_name, self._client, self._request.password) + ChannelManager.add_channel(self._channel) + + def _request_check(self) -> None: + # todo check if user already in local channel + # self._check_user_in_remote() + self._check_user_in_local() + + self._user = ChannelUser(self._client, self._channel) + self._channel.add_bind_on_user_and_channel(self._user) + + super()._request_check() + def _response_construct(self): self._response = JoinResponse(self._request, self._result) + def _response_send(self): + # for join request we need to send to our self + self._channel.multicast(self._user.client, self._response, False) + class KickHandler(ChannelHandlerBase): _request: KickRequest _result: KickResult - def __init__(self, client: ClientBase, request: KickRequest): + def __init__(self, client: Client, request: KickRequest): assert isinstance(request, KickRequest) super().__init__(client, request) def _response_construct(self): self._response = KickResponse(self._request, self._result) + def _response_send(self): + self._channel.multicast(self._user.client, self._response, True) + class ModeHandler(ChannelHandlerBase): _request: ModeRequest _result: ModeResult - def __init__(self, client: ClientBase, request: ModeRequest): + def __init__(self, client: Client, request: ModeRequest): assert isinstance(request, ModeRequest) super().__init__(client, request) @@ -104,13 +154,25 @@ def _response_construct(self): case _: raise ChatException("Unknown mode request type") + def _publish_message(self): + if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: + super()._publish_message() + + def _update_channel_cache(self): + if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: + super()._update_channel_cache() + + def _response_send(self): + self._channel.multicast(self._user.client, self._response, True) + class NamesHandler(ChannelHandlerBase): _request: NamesRequest _result: NamesResult - def __init__(self, client: ClientBase, request: NamesRequest): + def __init__(self, client: Client, request: NamesRequest): assert isinstance(request, NamesRequest) + self._is_fetching_data = True super().__init__(client, request) def _response_construct(self): @@ -121,7 +183,7 @@ class PartHandler(ChannelHandlerBase): _request: PartRequest _result: PartResult - def __init__(self, client: ClientBase, request: PartRequest): + def __init__(self, client: Client, request: PartRequest): assert isinstance(request, PartRequest) super().__init__(client, request) @@ -136,7 +198,7 @@ class SetChannelKeyHandler(ChannelHandlerBase): _request: SetChannelKeyRequest _result: SetChannelKeyResult - def __init__(self, client: ClientBase, request: SetChannelKeyRequest): + def __init__(self, client: Client, request: SetChannelKeyRequest): assert isinstance(self._request, SetChannelKeyRequest) super().__init__(client, request) @@ -144,28 +206,28 @@ def _response_construct(self): self._response = SetChannelKeyResponse(self._request, self._result) def _response_send(self): - self._channel.multicast(self._client, self._response, True) + self._channel.multicast(self._user.client, self._response, True) class SetCKeyHandler(ChannelHandlerBase): _request: SetCKeyRequest - def __init__(self, client: ClientBase, request: SetCKeyRequest): + def __init__(self, client: Client, request: SetCKeyRequest): assert isinstance(request, SetCKeyRequest) super().__init__(client, request) def _response_construct(self) -> None: self._response = SetCKeyResponse(self._request) - def _response_send(self) -> None: - self._channel.multicast(self._client, self._response) + def _response_send(self): + self._channel.multicast(self._user.client, self._response, True) class TopicHandler(ChannelHandlerBase): _request: TopicRequest _result: TopicResult - def __init__(self, client: ClientBase, request: TopicRequest): + def __init__(self, client: Client, request: TopicRequest): assert isinstance(request, TopicRequest) super().__init__(client, request) @@ -175,8 +237,6 @@ def _response_construct(self) -> None: def _response_send(self) -> None: match self._request.request_type: case TopicRequestType.GET_CHANNEL_TOPIC: - super()._response_send() + self._client.send(self._response) case TopicRequestType.SET_CHANNEL_TOPIC: self._channel.multicast(self._client, self._response) - - diff --git a/src/servers/natneg/src/applications/server_launcher.py b/src/servers/natneg/src/applications/server_launcher.py index 606370248..c46ebcc06 100644 --- a/src/servers/natneg/src/applications/server_launcher.py +++ b/src/servers/natneg/src/applications/server_launcher.py @@ -1,6 +1,6 @@ from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.udp_handler import UdpServer -from library.src.unispy_server_config import CONFIG +from library.src.unispy_server_config import CONFIG, ServerConfig from servers.natneg.src.applications.client import Client From 9db06ac65aedf4b5be8b9c0c19c3dbaff5034c6c Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 18 Oct 2024 07:48:04 +0000 Subject: [PATCH 116/231] fix: brocker error handling --- src/backends/routers/gamespy/chat.py | 41 ++++++++----------- src/library/src/network/brockers.py | 22 ++++++---- src/servers/chat/src/aggregates/channel.py | 24 ++++++++++- .../chat/src/aggregates/channel_broker.py | 19 --------- .../chat/src/contracts/requests/channel.py | 14 +++++-- 5 files changed, 64 insertions(+), 56 deletions(-) delete mode 100644 src/servers/chat/src/aggregates/channel_broker.py diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 443bea7c3..07760dc98 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -5,30 +5,19 @@ from backends.urls import CHAT from fastapi import APIRouter, FastAPI, WebSocket, WebSocketDisconnect +from servers.chat.src.aggregates.channel import BrockerMessage + router = APIRouter() -channels: dict[str, list[WebSocket]] = {} +channels: dict[str, list[WebSocket]] = {"test": []} """ {"channel_name" : "list of WebSocket"} """ -clients: dict[str, WebSocket] = [] +clients: dict[str, WebSocket] = {} """ {"client ip and port" : WebSocket} """ -@dataclass -class ChannelMessage: - channel_name: str - message: str - - def __post_init__(self): - if self.channel_name is None or len(self.channel_name < 3): - raise ValueError("channel name is not valid") - if self.message is None or len(self.message) < 3: - raise ValueError("message length is not valid") - if self.channel_name not in channels: - raise ValueError("channel is not registered") - @router.post(f"{CHAT}/add_channel") def add_channel(channel_name: str, server_id: uuid.UUID, server_ip: str): @@ -40,35 +29,41 @@ def add_channel(channel_name: str, server_id: uuid.UUID, server_ip: str): channels[channel_name] = [] -def check_request(request: str) -> Optional[ChannelMessage]: +def check_request(request: str) -> Optional[BrockerMessage]: ch_msg = None try: request_dict = json.loads(request) - ch_msg = ChannelMessage(**request_dict) - except: + ch_msg = BrockerMessage(**request_dict) + except Exception as e: + print(e) return None return ch_msg -@router.websocket(f"{CHAT}/channel") +async def multicast_message(ws: WebSocket): + pass + + +@router.websocket(f"{CHAT}/Channel") async def websocket_endpoint(ws: WebSocket): await ws.accept() if isinstance(ws, WebSocket): client_key = f"{ws.client.host}:{ws.client.port}" - clients[client_key](ws) + clients[client_key] = ws try: while True: request = await ws.receive_text() msg = check_request(request) if msg is None: return - channel_clients = channels[msg.channel_name] + channels[msg.channel_name].append(ws) + channel_clients: list[WebSocket] = channels[msg.channel_name] + for client in channel_clients: # we do not send data to the publisher if client == ws: continue - - await client.send_text(msg.message) + await client.send_text(request) except WebSocketDisconnect: client_key = f"{ws.client.host}:{ws.client.port}" diff --git a/src/library/src/network/brockers.py b/src/library/src/network/brockers.py index 1f4d1fa60..3fdff5ac6 100644 --- a/src/library/src/network/brockers.py +++ b/src/library/src/network/brockers.py @@ -45,8 +45,8 @@ def __init__(self, name: str, url: str, call_back_func: "function") -> None: url, on_message=lambda _, m: self.receive_message(m), on_error=print, - on_close=print, - on_open=self._on_open) + on_close=print) + self._subscriber.on_open = self._on_open def _on_open(self, ws): self._publisher: websocket.WebSocket = ws @@ -55,23 +55,29 @@ def _on_message(self, _, message): self.receive_message(message) def subscribe(self): - threading.Thread(target=self._subscriber.run_forever).start() - # wait for connection establish - while self._publisher is not None: - break + t = threading.Thread(target=self._subscriber.run_forever) + t.start() + # # wait for connection establish + while True: + if self._publisher is not None: + break def unsubscribe(self): self._subscriber.close() def publish_message(self, message): + if self._publisher is None: + raise ValueError("websocket connection is not established") self._publisher.send(message) if __name__ == "__main__": ws = WebsocketBrocker(name="test_channel", - url="ws://127.0.0.1:8000/channel", call_back_func=print) + url="ws://127.0.0.1:8000/GameSpy/Chat/Channel", call_back_func=print) ws.subscribe() - ws.publish_message("hello") + import json + ws.publish_message(json.dumps( + {"channel_name": "test", "message": "hello"})) while True: pass diff --git a/src/servers/chat/src/aggregates/channel.py b/src/servers/chat/src/aggregates/channel.py index e3d5468cd..fa9594837 100644 --- a/src/servers/chat/src/aggregates/channel.py +++ b/src/servers/chat/src/aggregates/channel.py @@ -1,9 +1,12 @@ +from dataclasses import dataclass import datetime from typing import overload from uuid import UUID + +from pydantic import BaseModel, field_validator +from library.src.network.brockers import WebsocketBrocker from library.src.unispy_server_config import CONFIG from servers.chat.src.abstractions.contract import ResponseBase -from servers.chat.src.aggregates.channel_broker import ChannelBrocker from servers.chat.src.aggregates.channel_user import ChannelUser from servers.chat.src.aggregates.key_value_manager import KeyValueManager from servers.chat.src.aggregates.peer_room import PeerRoom @@ -46,7 +49,7 @@ def __init__(self, name: str, client: Client, password: str = None) -> None: self.previously_join_channel = client.info.previously_joined_channel self.room_type = PeerRoom.get_room_type(name) # setup the message broker - self._broker = ChannelBrocker( + self._broker = WebsocketBrocker( self.name, CONFIG.backend.url, self.get_message_from_brocker) self._broker.subscribe() @@ -133,6 +136,7 @@ def get_user(self, nick_name: str) -> ChannelUser: if nick_name in self.users: return self.users[nick_name] return None + @overload def get_user(self, client: Client) -> ChannelUser: for user in self.users.values(): @@ -193,3 +197,19 @@ def add_channel(channel: Channel): def remove_channel(name: str) -> None: if name in ChannelManager.local_channels: del ChannelManager.local_channels[name] + + +class BrockerMessage(BaseModel): + channel_name: str + message: str + + @field_validator("channel_name") + def validate_channel_name(cls, value): + if value is None or len(value) < 3: + raise ValueError("channel name is not valid") + return value + + @field_validator("message") + def validate_message(cls, value): + if value is None or len(value) < 3: + raise ValueError("message length is not valid") diff --git a/src/servers/chat/src/aggregates/channel_broker.py b/src/servers/chat/src/aggregates/channel_broker.py deleted file mode 100644 index 5e7ed40f8..000000000 --- a/src/servers/chat/src/aggregates/channel_broker.py +++ /dev/null @@ -1,19 +0,0 @@ -from library.src.network.brockers import WebsocketBrocker -import requests - -from library.src.unispy_server_config import CONFIG -from servers.chat.src.applications.server_launcher import ServerLauncher -from servers.chat.src.exceptions.general import ChatException - - - -class ChannelBrocker(WebsocketBrocker): - def __init__(self, name: str, url: str, call_back_func: function) -> None: - super().__init__(name, url, call_back_func) - req = {"channel_name": name, "server_id": ServerLauncher.config.server_id, - "server_ip": ServerLauncher.config.public_address} - try: - requests.post( - f"{CONFIG.backend.url}/GameSpy/Chat/add_channel", req) - except Exception as e: - raise ChatException("Channel register on backend failed") diff --git a/src/servers/chat/src/contracts/requests/channel.py b/src/servers/chat/src/contracts/requests/channel.py index e502b22da..d98104e33 100644 --- a/src/servers/chat/src/contracts/requests/channel.py +++ b/src/servers/chat/src/contracts/requests/channel.py @@ -121,7 +121,8 @@ def parse(self): elif len(self._cmd_params) == 2 or len(self._cmd_params) == 3: self.request_type = ModeRequestType.SET_CHANNEL_MODES self.mode_flag = self._cmd_params[1] - modeFlags = [s for s in re.split(r"(?=\+|\-)", self.mode_flag) if s.strip()] + modeFlags = [s for s in re.split( + r"(?=\+|\-)", self.mode_flag) if s.strip()] modeFlags = list(filter(None, modeFlags)) for flag in modeFlags: match flag: @@ -167,7 +168,8 @@ def parse(self): ModeOperationType.REMOVE_SECRET_CHANNEL_FLAG ) case "+i": - self.mode_operations.append(ModeOperationType.SET_INVITED_ONLY) + self.mode_operations.append( + ModeOperationType.SET_INVITED_ONLY) case "-i": self.mode_operations.append( ModeOperationType.REMOVE_INVITED_ONLY @@ -308,10 +310,12 @@ class SetCKeyRequest(ChannelRequestBase): def parse(self) -> None: super().parse() if self._cmd_params is None: - raise ChatException("The cmdParams from SETCKEY request are missing.") + raise ChatException( + "The cmdParams from SETCKEY request are missing.") if self._longParam is None: - raise ChatException("The longParam from SETCKEY request is missing.") + raise ChatException( + "The longParam from SETCKEY request is missing.") self.channel_name = self._cmd_params[0] self.nick_name = self._cmd_params[1] @@ -346,3 +350,5 @@ def parse(self) -> None: else: self.request_type = TopicRequestType.SET_CHANNEL_TOPIC self.channel_topic = self._longParam + + From 0086268002ce60ec975db88af766063a5d4db6bc Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 18 Oct 2024 10:44:57 +0000 Subject: [PATCH 117/231] fix: natify test error --- src/backends/routers/gamespy/chat.py | 4 ++-- src/backends/routers/home.py | 2 +- src/library/src/abstractions/client.py | 3 ++- src/library/src/abstractions/connections.py | 2 +- src/library/src/abstractions/handler.py | 9 ++++++--- src/library/src/abstractions/server_launcher.py | 2 +- src/library/src/{unispy_server_config.py => configs.py} | 0 src/library/src/database/mongodb_orm.py | 2 +- src/library/src/database/pg_orm.py | 2 +- src/library/src/database/redis.py | 2 +- src/library/src/network/http_handler.py | 2 +- src/library/src/network/tcp_handler.py | 2 +- src/library/src/network/udp_handler.py | 2 +- src/library/tests/mock_objects/general.py | 2 +- src/servers/chat/src/aggregates/channel.py | 2 +- src/servers/chat/src/applications/client.py | 2 +- src/servers/chat/src/applications/server_launcher.py | 2 +- src/servers/game_status/src/applications/client.py | 2 +- .../game_status/src/applications/server_launcher.py | 2 +- src/servers/game_status/tests/mock_objects.py | 2 +- src/servers/natneg/src/applications/client.py | 2 +- src/servers/natneg/src/applications/server_launcher.py | 2 +- src/servers/natneg/tests/handler_tests.py | 4 +++- src/servers/natneg/tests/mock_objects.py | 2 +- .../src/applications/client.py | 2 +- .../src/applications/server_launcher.py | 2 +- .../presence_connection_manager/tests/mock_objects.py | 2 +- .../presence_search_player/src/applications/client.py | 2 +- .../src/applications/server_launcher.py | 2 +- src/servers/presence_search_player/tests/mock_objects.py | 2 +- .../query_report/src/applications/server_launcher.py | 2 +- .../src/v2/applications/server_launcher.py | 2 +- src/servers/web_services/src/applications/client.py | 2 +- .../web_services/src/applications/server_launcher.py | 2 +- 34 files changed, 42 insertions(+), 36 deletions(-) rename src/library/src/{unispy_server_config.py => configs.py} (100%) diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 07760dc98..07ca37fc8 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -5,6 +5,7 @@ from backends.urls import CHAT from fastapi import APIRouter, FastAPI, WebSocket, WebSocketDisconnect +from library.src.configs import ServerConfig from servers.chat.src.aggregates.channel import BrockerMessage router = APIRouter() @@ -18,9 +19,8 @@ """ - @router.post(f"{CHAT}/add_channel") -def add_channel(channel_name: str, server_id: uuid.UUID, server_ip: str): +def add_channel(channel_name: str, config: ServerConfig): # first validate the server_id server_ip etc. info # if server is valid we initialize the channel diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index c10509a4a..874bfe414 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -1,7 +1,7 @@ from fastapi import FastAPI from library.src.log.log_manager import LogManager -from library.src.unispy_server_config import CONFIG, ServerConfig +from library.src.configs import CONFIG, ServerConfig from backends.routers.gamespy import chat, gstats, natneg, presence_connection_manager, presence_search_player, query_report, server_browser, webservices app = FastAPI() diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index b1942c115..e07a51dde 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -2,7 +2,7 @@ from library.src.exceptions.general import UniSpyException from library.src.log.log_manager import LogWriter from library.src.log.log_manager import LogWriter -from library.src.unispy_server_config import ServerConfig +from library.src.configs import ServerConfig from typing import TYPE_CHECKING, Optional @@ -127,6 +127,7 @@ def __init__(self, interval, refresh_interval) -> None: self.is_expired = False def elapsed(self, s, e) -> None: + print() pass def start(self) -> None: diff --git a/src/library/src/abstractions/connections.py b/src/library/src/abstractions/connections.py index 7bec592dd..24ff84657 100644 --- a/src/library/src/abstractions/connections.py +++ b/src/library/src/abstractions/connections.py @@ -4,7 +4,7 @@ from library.src.abstractions.client import ClientBase from library.src.log.log_manager import LogWriter -from library.src.unispy_server_config import ServerConfig +from library.src.configs import ServerConfig from typing import TYPE_CHECKING diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index de6573622..5aee4e35a 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -3,7 +3,7 @@ from typing import Type import requests -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG # if TYPE_CHECKING: from library.src.abstractions.contracts import RequestBase, ResultBase, ResponseBase @@ -26,7 +26,10 @@ class CmdHandlerBase: """ whether need get data from backend """ - _is_debugging: bool = False + _debug: bool = False + """ + whether is in debug mode, if in debug mode exception will raise from handler + """ def __init__(self, client: "ClientBase", request: "RequestBase") -> None: @@ -109,7 +112,7 @@ def _handle_exception(self, ex) -> None: """ UniSpyException.handle_exception(ex, self._client) # if we are debugging the app we re-raise the exception - if CmdHandlerBase._is_debugging: + if CmdHandlerBase._debug: raise ex def _log_current_class(self) -> None: diff --git a/src/library/src/abstractions/server_launcher.py b/src/library/src/abstractions/server_launcher.py index ed3a7d0e7..2697f16ca 100644 --- a/src/library/src/abstractions/server_launcher.py +++ b/src/library/src/abstractions/server_launcher.py @@ -1,7 +1,7 @@ import abc from library.src.exceptions.general import UniSpyException from library.src.log.log_manager import LogManager, LogWriter -from library.src.unispy_server_config import CONFIG, ServerConfig +from library.src.configs import CONFIG, ServerConfig import pyfiglet import requests diff --git a/src/library/src/unispy_server_config.py b/src/library/src/configs.py similarity index 100% rename from src/library/src/unispy_server_config.py rename to src/library/src/configs.py diff --git a/src/library/src/database/mongodb_orm.py b/src/library/src/database/mongodb_orm.py index d797e8752..e9012d54b 100644 --- a/src/library/src/database/mongodb_orm.py +++ b/src/library/src/database/mongodb_orm.py @@ -1,6 +1,6 @@ from mongoengine import connect -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG def connect_to_db(): diff --git a/src/library/src/database/pg_orm.py b/src/library/src/database/pg_orm.py index 7412a9e60..82fa2e359 100644 --- a/src/library/src/database/pg_orm.py +++ b/src/library/src/database/pg_orm.py @@ -184,7 +184,7 @@ class SakeStorage(Base): from sqlalchemy import create_engine -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG def connect_to_db() -> Session: diff --git a/src/library/src/database/redis.py b/src/library/src/database/redis.py index 77813587b..bc6693078 100644 --- a/src/library/src/database/redis.py +++ b/src/library/src/database/redis.py @@ -2,7 +2,7 @@ # import redis import aioredis -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG # SESSION = redis.Redis.from_url(CONFIG.redis.url) diff --git a/src/library/src/network/http_handler.py b/src/library/src/network/http_handler.py index 63884b621..bf7cbe313 100644 --- a/src/library/src/network/http_handler.py +++ b/src/library/src/network/http_handler.py @@ -2,7 +2,7 @@ from library.src.abstractions.client import ClientBase from library.src.abstractions.connections import ConnectionBase, ServerBase from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG class HttpRequest: diff --git a/src/library/src/network/tcp_handler.py b/src/library/src/network/tcp_handler.py index e7816e530..3b200a791 100644 --- a/src/library/src/network/tcp_handler.py +++ b/src/library/src/network/tcp_handler.py @@ -5,7 +5,7 @@ from library.src.abstractions.connections import ConnectionBase, ServerBase from library.src.network import DATA_SIZE -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG class TcpConnection(ConnectionBase): diff --git a/src/library/src/network/udp_handler.py b/src/library/src/network/udp_handler.py index fb4db9ece..814a1ba33 100644 --- a/src/library/src/network/udp_handler.py +++ b/src/library/src/network/udp_handler.py @@ -3,7 +3,7 @@ from library.src.abstractions.client import ClientBase from library.src.abstractions.connections import ConnectionBase, ServerBase -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG class UdpConnection(ConnectionBase): diff --git a/src/library/tests/mock_objects/general.py b/src/library/tests/mock_objects/general.py index a58b0122e..74809095c 100644 --- a/src/library/tests/mock_objects/general.py +++ b/src/library/tests/mock_objects/general.py @@ -5,7 +5,7 @@ from library.src.abstractions.connections import ConnectionBase from library.src.abstractions.handler import CmdHandlerBase from library.src.log.log_manager import LogWriter -from library.src.unispy_server_config import CONFIG, ServerConfig +from library.src.configs import CONFIG, ServerConfig class ConnectionMock(ConnectionBase): diff --git a/src/servers/chat/src/aggregates/channel.py b/src/servers/chat/src/aggregates/channel.py index fa9594837..11bc583ea 100644 --- a/src/servers/chat/src/aggregates/channel.py +++ b/src/servers/chat/src/aggregates/channel.py @@ -5,7 +5,7 @@ from pydantic import BaseModel, field_validator from library.src.network.brockers import WebsocketBrocker -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG from servers.chat.src.abstractions.contract import ResponseBase from servers.chat.src.aggregates.channel_user import ChannelUser from servers.chat.src.aggregates.key_value_manager import KeyValueManager diff --git a/src/servers/chat/src/applications/client.py b/src/servers/chat/src/applications/client.py index fb98bc89f..2771b167d 100644 --- a/src/servers/chat/src/applications/client.py +++ b/src/servers/chat/src/applications/client.py @@ -3,7 +3,7 @@ from library.src.log.log_manager import LogWriter from library.src.network.tcp_handler import TcpConnection -from library.src.unispy_server_config import ServerConfig +from library.src.configs import ServerConfig if TYPE_CHECKING: from servers.chat.src.aggregates.channel import Channel diff --git a/src/servers/chat/src/applications/server_launcher.py b/src/servers/chat/src/applications/server_launcher.py index c9af625cb..0dbdd3713 100644 --- a/src/servers/chat/src/applications/server_launcher.py +++ b/src/servers/chat/src/applications/server_launcher.py @@ -1,6 +1,6 @@ from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.tcp_handler import TcpServer -from library.src.unispy_server_config import CONFIG, ServerConfig +from library.src.configs import CONFIG, ServerConfig from servers.chat.src.applications.client import Client diff --git a/src/servers/game_status/src/applications/client.py b/src/servers/game_status/src/applications/client.py index 2cd72cea2..0512cfc23 100644 --- a/src/servers/game_status/src/applications/client.py +++ b/src/servers/game_status/src/applications/client.py @@ -2,7 +2,7 @@ from library.src.abstractions.switcher import SwitcherBase from library.src.log.log_manager import LogWriter from library.src.network.tcp_handler import TcpConnection -from library.src.unispy_server_config import ServerConfig +from library.src.configs import ServerConfig from servers.game_status.src.aggregations.gscrypt import GSCrypt diff --git a/src/servers/game_status/src/applications/server_launcher.py b/src/servers/game_status/src/applications/server_launcher.py index 3a199f63e..c50b76bf2 100644 --- a/src/servers/game_status/src/applications/server_launcher.py +++ b/src/servers/game_status/src/applications/server_launcher.py @@ -1,6 +1,6 @@ from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.tcp_handler import TcpServer -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG from servers.chat.src.applications.client import Client diff --git a/src/servers/game_status/tests/mock_objects.py b/src/servers/game_status/tests/mock_objects.py index 3d1487193..e79a20501 100644 --- a/src/servers/game_status/tests/mock_objects.py +++ b/src/servers/game_status/tests/mock_objects.py @@ -1,4 +1,4 @@ -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock from servers.game_status.src.applications.client import Client diff --git a/src/servers/natneg/src/applications/client.py b/src/servers/natneg/src/applications/client.py index 045f8834c..ab253fb6a 100644 --- a/src/servers/natneg/src/applications/client.py +++ b/src/servers/natneg/src/applications/client.py @@ -1,7 +1,7 @@ from library.src.abstractions.client import ClientBase from library.src.log.log_manager import LogWriter from library.src.network.udp_handler import UdpConnection -from library.src.unispy_server_config import ServerConfig +from library.src.configs import ServerConfig class Client(ClientBase): diff --git a/src/servers/natneg/src/applications/server_launcher.py b/src/servers/natneg/src/applications/server_launcher.py index c46ebcc06..5d03c6554 100644 --- a/src/servers/natneg/src/applications/server_launcher.py +++ b/src/servers/natneg/src/applications/server_launcher.py @@ -1,6 +1,6 @@ from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.udp_handler import UdpServer -from library.src.unispy_server_config import CONFIG, ServerConfig +from library.src.configs import CONFIG, ServerConfig from servers.natneg.src.applications.client import Client diff --git a/src/servers/natneg/tests/handler_tests.py b/src/servers/natneg/tests/handler_tests.py index b40f971b2..64dfdd48c 100644 --- a/src/servers/natneg/tests/handler_tests.py +++ b/src/servers/natneg/tests/handler_tests.py @@ -1,5 +1,6 @@ import unittest +from library.src.abstractions.handler import CmdHandlerBase from library.tests.mock_objects.general import create_mock_url from servers.natneg.src.contracts.requests import InitRequest from servers.natneg.src.handlers.handlers import AddressCheckHandler, ErtAckHandler, InitHandler, NatifyHandler @@ -19,6 +20,7 @@ ) from servers.natneg.tests.mock_objects import create_client +CmdHandlerBase._debug = True class HandlerTests(unittest.TestCase): @@ -123,7 +125,7 @@ def test_natify(self): self.assertEqual(False, request.use_game_port) self.assertEqual(NatPortType.NN1, request.port_type) client = create_client() - create_mock_url(client, AddressCheckHandler, {"message": "ok"}) + create_mock_url(client, NatifyHandler, {"message": "ok"}) handler = NatifyHandler(client, request) handler.handle() diff --git a/src/servers/natneg/tests/mock_objects.py b/src/servers/natneg/tests/mock_objects.py index 857201c4b..8d760874d 100644 --- a/src/servers/natneg/tests/mock_objects.py +++ b/src/servers/natneg/tests/mock_objects.py @@ -1,5 +1,5 @@ from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG from servers.natneg.src.applications.client import Client diff --git a/src/servers/presence_connection_manager/src/applications/client.py b/src/servers/presence_connection_manager/src/applications/client.py index 3ece52730..ce60d2ef3 100644 --- a/src/servers/presence_connection_manager/src/applications/client.py +++ b/src/servers/presence_connection_manager/src/applications/client.py @@ -3,7 +3,7 @@ from library.src.abstractions.switcher import SwitcherBase from library.src.log.log_manager import LogWriter from library.src.network.tcp_handler import TcpConnection -from library.src.unispy_server_config import ServerConfig +from library.src.configs import ServerConfig from servers.presence_connection_manager.src.aggregates.login_challenge import ( SERVER_CHALLENGE, ) diff --git a/src/servers/presence_connection_manager/src/applications/server_launcher.py b/src/servers/presence_connection_manager/src/applications/server_launcher.py index 2f8d12bb4..572c36271 100644 --- a/src/servers/presence_connection_manager/src/applications/server_launcher.py +++ b/src/servers/presence_connection_manager/src/applications/server_launcher.py @@ -1,6 +1,6 @@ from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.udp_handler import UdpServer -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG from servers.presence_connection_manager.src.applications.client import Client diff --git a/src/servers/presence_connection_manager/tests/mock_objects.py b/src/servers/presence_connection_manager/tests/mock_objects.py index d9ca5243f..f414b79f6 100644 --- a/src/servers/presence_connection_manager/tests/mock_objects.py +++ b/src/servers/presence_connection_manager/tests/mock_objects.py @@ -1,5 +1,5 @@ -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock from servers.presence_connection_manager.src.applications.client import Client diff --git a/src/servers/presence_search_player/src/applications/client.py b/src/servers/presence_search_player/src/applications/client.py index 93cbcc185..0cb246373 100644 --- a/src/servers/presence_search_player/src/applications/client.py +++ b/src/servers/presence_search_player/src/applications/client.py @@ -3,7 +3,7 @@ from library.src.abstractions.switcher import SwitcherBase from library.src.log.log_manager import LogWriter from library.src.network.tcp_handler import TcpConnection -from library.src.unispy_server_config import ServerConfig +from library.src.configs import ServerConfig class Client(ClientBase): diff --git a/src/servers/presence_search_player/src/applications/server_launcher.py b/src/servers/presence_search_player/src/applications/server_launcher.py index 77350c836..0d3894d34 100644 --- a/src/servers/presence_search_player/src/applications/server_launcher.py +++ b/src/servers/presence_search_player/src/applications/server_launcher.py @@ -1,6 +1,6 @@ from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.tcp_handler import TcpServer -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG from servers.presence_search_player.src.applications.client import Client diff --git a/src/servers/presence_search_player/tests/mock_objects.py b/src/servers/presence_search_player/tests/mock_objects.py index 644ddd6d5..5a51a7028 100644 --- a/src/servers/presence_search_player/tests/mock_objects.py +++ b/src/servers/presence_search_player/tests/mock_objects.py @@ -1,4 +1,4 @@ -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock from servers.presence_search_player.src.applications.client import Client diff --git a/src/servers/query_report/src/applications/server_launcher.py b/src/servers/query_report/src/applications/server_launcher.py index 2384db12e..f50f9462a 100644 --- a/src/servers/query_report/src/applications/server_launcher.py +++ b/src/servers/query_report/src/applications/server_launcher.py @@ -1,6 +1,6 @@ from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.udp_handler import UdpServer -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG from servers.presence_search_player.src.applications.client import Client diff --git a/src/servers/server_browser/src/v2/applications/server_launcher.py b/src/servers/server_browser/src/v2/applications/server_launcher.py index d9e600094..c1d47bc40 100644 --- a/src/servers/server_browser/src/v2/applications/server_launcher.py +++ b/src/servers/server_browser/src/v2/applications/server_launcher.py @@ -1,6 +1,6 @@ from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.tcp_handler import TcpServer -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG from servers.server_browser.src.v2.applications.client import Client diff --git a/src/servers/web_services/src/applications/client.py b/src/servers/web_services/src/applications/client.py index f1a8de2c5..a9f2ac312 100644 --- a/src/servers/web_services/src/applications/client.py +++ b/src/servers/web_services/src/applications/client.py @@ -2,7 +2,7 @@ from library.src.abstractions.switcher import SwitcherBase from library.src.log.log_manager import LogWriter from library.src.network.http_handler import HttpConnection -from library.src.unispy_server_config import ServerConfig +from library.src.configs import ServerConfig class ClientInfo(ClientInfoBase): diff --git a/src/servers/web_services/src/applications/server_launcher.py b/src/servers/web_services/src/applications/server_launcher.py index 735713d2b..6e4a0f7e2 100644 --- a/src/servers/web_services/src/applications/server_launcher.py +++ b/src/servers/web_services/src/applications/server_launcher.py @@ -1,6 +1,6 @@ from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.http_handler import HttpServer -from library.src.unispy_server_config import CONFIG +from library.src.configs import CONFIG from servers.web_services.src.applications import client From c5aacdfbe469171192c0c4ee5291531aab0cdcf1 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 23 Oct 2024 07:01:27 +0000 Subject: [PATCH 118/231] fix: type hint error --- docker-compose-unispy-env.yml | 2 +- src/.vscode/settings.json | 2 +- src/backends/library/database/mongodb_orm.py | 45 +++ .../library}/database/pg_orm.py | 97 ++++++- .../library}/database/redis.py | 0 .../protocols/gamespy/chat/__init__.py | 0 src/backends/protocols/gamespy/chat/data.py | 81 ++++++ .../protocols/gamespy/chat/handlers.py | 6 + .../storage_info.py => chat/storage_infos.py} | 0 .../protocols/gamespy/game_status/data.py | 8 +- .../protocols/gamespy/natneg/__init__.py | 0 .../protocols/gamespy/natneg/handlers.py | 2 +- .../protocols/gamespy/natneg/requests.py | 6 +- .../presence_search_player/__init__.py | 0 .../protocols/gamespy/query_report/data.py | 2 +- src/backends/routers/gamespy/chat.py | 10 +- src/backends/routers/gamespy/gstats.py | 12 +- src/backends/routers/gamespy/webservices.py | 28 +- src/library/src/abstractions/client.py | 7 +- src/library/src/abstractions/connections.py | 9 +- src/library/src/abstractions/handler.py | 6 +- src/library/src/abstractions/redis_channel.py | 102 ------- src/library/src/abstractions/redis_objects.py | 262 ++++++++++++++++++ .../src/abstractions/server_launcher.py | 17 +- src/library/src/configs.py | 13 +- src/library/src/database/__init__.py | 0 src/library/src/database/mongodb_orm.py | 12 - src/library/src/exceptions/general.py | 12 +- src/library/src/log/log_manager.py | 17 +- src/library/src/network/brockers.py | 7 +- src/library/src/network/http_handler.py | 4 +- src/library/src/network/tcp_handler.py | 4 +- src/library/src/network/udp_handler.py | 4 +- src/servers/chat/src/abstractions/channel.py | 40 ++- src/servers/chat/src/abstractions/contract.py | 8 +- src/servers/chat/src/abstractions/handler.py | 12 +- src/servers/chat/src/abstractions/message.py | 1 - src/servers/chat/src/aggregates/channel.py | 34 +-- .../chat/src/aggregates/key_value_manager.py | 2 +- src/servers/chat/src/applications/client.py | 4 +- .../chat/src/applications/server_launcher.py | 2 +- .../chat/src/contracts/requests/channel.py | 10 +- .../chat/src/contracts/requests/general.py | 2 +- .../chat/src/contracts/responses/channel.py | 7 +- .../chat/src/contracts/responses/general.py | 51 ++-- .../chat/src/contracts/results/channel.py | 6 - src/servers/chat/src/exceptions/general.py | 11 +- src/servers/chat/src/handlers/channel.py | 35 ++- src/servers/chat/src/handlers/general.py | 12 +- src/servers/chat/src/handlers/switcher.py | 7 +- .../game_status/src/applications/client.py | 29 +- .../src/applications/server_launcher.py | 2 +- .../game_status/src/contracts/requests.py | 13 +- .../game_status/src/handlers/handlers.py | 2 - .../game_status/src/handlers/switcher.py | 5 +- .../game_status/tests/handler_tests.py | 12 +- src/servers/game_status/tests/mock_objects.py | 5 +- src/servers/natneg/src/applications/client.py | 2 +- .../src/applications/server_launcher.py | 2 +- src/servers/natneg/src/handlers/handlers.py | 11 + src/servers/natneg/tests/mock_objects.py | 7 +- .../src/aggregates/user_status.py | 12 +- .../src/applications/server_launcher.py | 2 +- .../src/contracts/requests/buddy.py | 10 +- .../src/contracts/responses/profile.py | 10 +- .../src/contracts/results/profile.py | 58 ++-- .../src/handlers/buddy.py | 8 +- .../src/handlers/general.py | 19 +- .../src/handlers/profile.py | 8 +- .../src/handlers/switcher.py | 11 +- .../src/applications/server_launcher.py | 2 +- .../src/contracts/requests.py | 8 +- .../src/contracts/responses.py | 8 +- .../src/exceptions/general.py | 11 +- .../src/handlers/handlers.py | 43 ++- .../src/handlers/switcher.py | 6 +- .../tests/game_tests.py | 4 +- .../tests/mock_objects.py | 5 +- .../src/aggregates/peer_room_info.py | 6 +- .../src/applications/server_launcher.py | 2 +- .../src/v1/abstractions/contracts.py | 6 +- .../src/v2/abstractions/contracts.py | 10 +- .../src/v2/abstractions/contrancts.py | 41 --- .../src/v2/aggregates/game_server_info_v2.py | 2 +- .../query_report/src/v2/contracts/requests.py | 4 - .../query_report/src/v2/handlers/handlers.py | 16 +- .../query_report/src/v2/handlers/switcher.py | 14 +- 87 files changed, 921 insertions(+), 516 deletions(-) create mode 100644 src/backends/library/database/mongodb_orm.py rename src/{library/src => backends/library}/database/pg_orm.py (63%) rename src/{library/src => backends/library}/database/redis.py (100%) delete mode 100644 src/backends/protocols/gamespy/chat/__init__.py create mode 100644 src/backends/protocols/gamespy/chat/data.py rename src/backends/protocols/gamespy/{query_report/storage_info.py => chat/storage_infos.py} (100%) delete mode 100644 src/backends/protocols/gamespy/natneg/__init__.py delete mode 100644 src/backends/protocols/gamespy/presence_search_player/__init__.py delete mode 100644 src/library/src/abstractions/redis_channel.py create mode 100644 src/library/src/abstractions/redis_objects.py delete mode 100644 src/library/src/database/__init__.py delete mode 100644 src/library/src/database/mongodb_orm.py delete mode 100644 src/servers/query_report/src/v2/abstractions/contrancts.py diff --git a/docker-compose-unispy-env.yml b/docker-compose-unispy-env.yml index 9940edb98..0b9d7d14e 100644 --- a/docker-compose-unispy-env.yml +++ b/docker-compose-unispy-env.yml @@ -9,7 +9,7 @@ services: command: redis-server --requirepass 123456 mongodb: - image: mongo + image: mongodb/mongodb-community-server:latest restart: always ports: - "27017:27017" diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index 459e8e9f1..cbe3eecc2 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -1,5 +1,5 @@ { - "python.analysis.typeCheckingMode": "off", + "python.analysis.typeCheckingMode": "basic", "workbench.iconTheme": "material-icon-theme", "python.testing.unittestArgs": [ "-v", diff --git a/src/backends/library/database/mongodb_orm.py b/src/backends/library/database/mongodb_orm.py new file mode 100644 index 000000000..98bcae66c --- /dev/null +++ b/src/backends/library/database/mongodb_orm.py @@ -0,0 +1,45 @@ +from mongoengine import connect + +from library.src.configs import CONFIG + + +def connect_to_db(): + connect(host=CONFIG.mongodb.url) + + +def get_ttl_param(seconds: int): + assert isinstance(seconds, int) + return {"indexes": [{"fields": ["created"], "expireAfterSeconds": seconds}]} + + +if __name__ == "__main__": + from pymongo import MongoClient + + # Connect to the database + client = MongoClient('mongodb://unispy:123456@172.17.0.1:27017/') + # Select the database + db = client['mydatabase'] + + # Select the collection + collection = db['mycollection'] + + # Insert a document + document = {'name': 'John Doe', 'age': 30} + collection.insert_one(document) + + # Find all documents + documents = collection.find() + for document in documents: + print(document) + + # Update a document + filter = {'name': 'John Doe'} + update = {'$set': {'age': 31}} + collection.update_one(filter, update) + + # Delete a document + filter = {'name': 'John Doe'} + collection.delete_one(filter) + + # Close the client + client.close() diff --git a/src/library/src/database/pg_orm.py b/src/backends/library/database/pg_orm.py similarity index 63% rename from src/library/src/database/pg_orm.py rename to src/backends/library/database/pg_orm.py index 82fa2e359..af3e6fb49 100644 --- a/src/library/src/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -1,3 +1,6 @@ +from library.src.configs import CONFIG +from sqlalchemy import Enum, create_engine +from sqlalchemy.orm.session import Session from datetime import datetime from sqlalchemy import ( Boolean, @@ -11,13 +14,16 @@ ForeignKey, DateTime, text, + UUID ) from sqlalchemy.dialects.postgresql import JSONB, INET from sqlalchemy.ext.declarative import DeclarativeMeta from sqlalchemy.orm import sessionmaker, declarative_base +from servers.natneg.src.enums.general import NatClientIndex, NatPortType +from servers.query_report.src.v2.enums.general import GameServerStatus + Base: DeclarativeMeta = declarative_base() -from sqlalchemy.orm.session import Session class Users(Base): @@ -104,9 +110,11 @@ class AddRequest(Base): __tablename__ = "addrequests" addrequestid = Column(Integer, primary_key=True, autoincrement=True) - profileid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) + profileid = Column(Integer, ForeignKey( + "profiles.profileid"), nullable=False) namespaceid = Column(Integer, nullable=False) - targetid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) + targetid = Column(Integer, ForeignKey( + "profiles.profileid"), nullable=False) reason = Column(String, nullable=False) syncrequested = Column(String, nullable=False) @@ -115,7 +123,8 @@ class Blocked(Base): __tablename__ = "blocked" blockid = Column(Integer, primary_key=True, autoincrement=True) - profileid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) + profileid = Column(Integer, ForeignKey( + "profiles.profileid"), nullable=False) namespaceid = Column(Integer, nullable=False) targetid = Column(Integer, nullable=False) @@ -124,7 +133,8 @@ class Friends(Base): __tablename__ = "friends" friendid = Column(Integer, primary_key=True, autoincrement=True) - profileid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) + profileid = Column(Integer, ForeignKey( + "profiles.profileid"), nullable=False) namespaceid = Column(Integer, nullable=False) targetid = Column(Integer, nullable=False) @@ -170,7 +180,8 @@ class PStorage(Base): __tablename__ = "pstorage" pstorageid = Column(Integer, primary_key=True, autoincrement=True) - profileid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) + profileid = Column(Integer, ForeignKey( + "profiles.profileid"), nullable=False) ptype = Column(Integer, nullable=False) dindex = Column(Integer, nullable=False) data = Column(JSONB) @@ -183,8 +194,78 @@ class SakeStorage(Base): tableid = Column(String, nullable=False) -from sqlalchemy import create_engine -from library.src.configs import CONFIG +class InitPacketCaches(Base): + __tablename__ = "init_packet_caches" + + server_id = Column(UUID, nullable=False) + cookie = Column(Integer, nullable=False) + version = Column(Integer, nullable=False) + port_type = Column(Enum(NatPortType), nullable=False) + client_index = Column(Enum(NatClientIndex), nullable=False) + game_name = Column(String, nullable=False) + use_game_port = Column(Boolean, nullable=False) + public_ip = Column(String, nullable=False) + public_port = Column(Integer, nullable=False) + private_ip = Column(String, nullable=False) + private_port = Column(Integer, nullable=False) + update_time = Column(DateTime, nullable=False) + + +class NatFailCaches(Base): + __tablename__ = "nat_fail_Cachess" + public_ip_address1 = Column(INET, nullable=False) + public_ip_address2 = Column(INET, nullable=False) + update_time = Column(DateTime, nullable=False) + +# class ReportPackets(Base): +# __tablename__ = "report_packets" +# public_ip_address1 = Column(String, nullable=False) +# public_ip_address2 = Column(String, nullable=False) +# update_time = Column(DateTime, nullable=False) + + +class ChatChannelCaches(Base): + __tablename__ = "chat_channel_Caches" + server_id = Column(UUID, nullable=False) + channel_name = Column(String, primary_key=True, nullable=False) + game_name = Column(String, nullable=False) + room_name = Column(String, nullable=False) + topic = Column(String, nullable=False) + password = Column(String, nullable=True) + group_id = Column(Integer, nullable=False) + max_num_user = Column(Integer, nullable=False) + key_values = Column(JSONB) + update_time = Column(DateTime, nullable=False) + + +class ChatUserCaches(Base): + __tablename__ = "chat_user_caches" + nick_name = Column(String, primary_key=True, nullable=False) + channel_name = Column(String, ForeignKey( + "chat_channel_caches.channel_name"), nullable=False) + server_id = Column(UUID, nullable=False) + user_name = Column(String, nullable=False) + update_time = Column(DateTime, nullable=False) + is_voiceable = Column(Boolean, nullable=False) + is_channel_operator = Column(Boolean, nullable=False) + is_channel_creator = Column(Boolean, nullable=False) + remote_ip_address = Column(INET, nullable=False) + remote_port = Column(Integer, nullable=False) + key_values = Column(JSONB) + + +class GameServerCaches(Base): + __tablename__ = "game_server_caches" + server_id = Column(UUID, nullable=False) + host_ip_address = Column(INET, nullable=False) + instant_key = Column(Integer, nullable=False) + game_name = Column(String, nullable=False) + query_report_port = Column(Integer, nullable=False) + update_time = Column(DateTime, nullable=False) + status = Column(Enum(GameServerStatus)) + player_data = Column(JSONB, nullable=False) + server_data = Column(JSONB, nullable=False) + team_data = Column(JSONB, nullable=False) def connect_to_db() -> Session: diff --git a/src/library/src/database/redis.py b/src/backends/library/database/redis.py similarity index 100% rename from src/library/src/database/redis.py rename to src/backends/library/database/redis.py diff --git a/src/backends/protocols/gamespy/chat/__init__.py b/src/backends/protocols/gamespy/chat/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py new file mode 100644 index 000000000..6cc59338f --- /dev/null +++ b/src/backends/protocols/gamespy/chat/data.py @@ -0,0 +1,81 @@ +from backends.protocols.gamespy.chat.storage_infos import ChannelInfo, ChannelUser +from library.src.database.pg_orm import PG_SESSION, Users, Profiles, SubProfiles +from servers.chat.src.aggregates.channel import Channel +from servers.chat.src.exceptions.general import ChatException + + +def nick_and_email_login(nick_name: str, email: str, password_hash: str) -> tuple[int, int, bool, bool]: + """ + return + userid, profileid, emailverified, banned + """ + result = PG_SESSION.query(Users.userid, Profiles.profileid, + Users.emailverified, Users.banned).join(Profiles, (Users.userid, Profiles.userid)).where( + Users.email == email, + Profiles.nick == nick_name, + Users.password == password_hash + ).first() + if result is None: + # fmt: off + raise ChatException(f"Can not find user with nickname:{nick_name} in database.") + # fmt on + + return result + +def uniquenick_login(uniquenick:str,namespace_id:int)-> tuple[int, int, bool, bool]: + """ + return + userid, profileid, emailverified, banned + """ + result = PG_SESSION.query(Users.userid, Profiles.profileid,Users.emailverified, Users.banned).join(Profiles,(Users.userid,Profiles.userid)).join(Profiles,(Profiles.profileid,SubProfiles.profileid)).where(SubProfiles.namespaceid == namespace_id,SubProfiles.uniquenick == uniquenick).first() + if result is None: + # fmt: off + raise ChatException(f"Can not find user with uniquenick:{uniquenick} in database.") + # fmt on + return result + + +def is_channel_exist(channel_name:str,game_name:str)->bool: + channel_count = ChannelInfo.objects.filter(channel_name = channel_name,game_name = game_name).count() + if channel_count == 1: + return True + else: + return False + +def update_channel(channel:Channel): + info = ChannelInfo( + channel_name=channel.name, game_name=channel.game_name, key_values =channel.kv_manager.data, max_num_user=channel.max_num_user, room_name=channel.room_name, topic=channel.topic, password=channel.password, group_id=channel.group_id, create_time=channel.create_time, previously_joined_channel=channel.previously_join_channel + ) + info.save() + +def remove_channel(channel_name:str)->None: + info = ChannelInfo.objects(channel_name=channel_name).first() + info.delete() + +def remove_user(nick_name:str): + user = ChannelUser.objects(nick_name == nick_name).first() + user.delete() + +def is_user_exist(nick_name:str)->bool: + user_count = ChannelUser.objects.filter(nick_name == nick_name).count() + if user_count ==1: + return True + else: + return False + + +def update_client(user:ChannelUser): + user.save() + + +def remove_user(nick_name:str): + user = ChannelUser.objects(nick_name == nick_name).first() + user.delete() + +def is_user_exist(nick_name:str)->bool: + user_count = ChannelUser.objects.filter(nick_name == nick_name).count() + if user_count ==1: + return True + else: + return False + diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index e69de29bb..10559fd74 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -0,0 +1,6 @@ +from backends.library.abstractions.handler_base import HandlerBase + + +class JoinHandler(HandlerBase): + async def data_fetch(self) -> None: + pass diff --git a/src/backends/protocols/gamespy/query_report/storage_info.py b/src/backends/protocols/gamespy/chat/storage_infos.py similarity index 100% rename from src/backends/protocols/gamespy/query_report/storage_info.py rename to src/backends/protocols/gamespy/chat/storage_infos.py diff --git a/src/backends/protocols/gamespy/game_status/data.py b/src/backends/protocols/gamespy/game_status/data.py index f2032a7af..2558ff7d0 100644 --- a/src/backends/protocols/gamespy/game_status/data.py +++ b/src/backends/protocols/gamespy/game_status/data.py @@ -16,17 +16,17 @@ def update_player_data(): raise NotImplementedError() -def get_profile_id(token: str) -> int: +def get_profile_id_by_token(token: str) -> int: result = PG_SESSION.query(SubProfiles.profileid).filter( - SubProfiles.authtoken == token).one() + SubProfiles.authtoken == token).first() if result is None: raise GSException("No records found in database") return result -def get_profile_id(cdkey: str, nick_name: str) -> Optional[int]: +def get_profile_id(cdkey: str, nick_name: str) -> int: result = PG_SESSION.query(SubProfiles.profileid).join( - SubProfiles, Profiles.profileid == SubProfiles.profileid).filter(SubProfiles.cdkeyenc == cdkey, Profiles.nick == nick_name).one() + SubProfiles, Profiles.profileid == SubProfiles.profileid).filter(SubProfiles.cdkeyenc == cdkey, Profiles.nick == nick_name).first() if result is None: raise GSException("No record found in database") return result diff --git a/src/backends/protocols/gamespy/natneg/__init__.py b/src/backends/protocols/gamespy/natneg/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py index a33ceebe8..2cb7213c7 100644 --- a/src/backends/protocols/gamespy/natneg/handlers.py +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -11,7 +11,7 @@ def __init__(self, request: InitRequest) -> None: assert isinstance(request, InitRequest) async def data_fetch(self) -> None: - info = InitPacketInfo(**self._request.model_dump_json()) + info = InitPacketInfo(**self._request.model_dump()) await update_init_info(info) async def result_construct(self) -> None: diff --git a/src/backends/protocols/gamespy/natneg/requests.py b/src/backends/protocols/gamespy/natneg/requests.py index df08a4100..cffbf6957 100644 --- a/src/backends/protocols/gamespy/natneg/requests.py +++ b/src/backends/protocols/gamespy/natneg/requests.py @@ -6,7 +6,7 @@ PreInitState, RequestType, ) -from typing import Union +from typing import Optional, Union import backends.library.abstractions.contracts as lib @@ -45,8 +45,8 @@ class ErtAckRequest(CommonRequestBase): class InitRequest(CommonRequestBase): - game_name: str = None - private_ip_address: str = None + game_name: Optional[str] + private_ip_address: Optional[str] class NatifyRequest(CommonRequestBase): diff --git a/src/backends/protocols/gamespy/presence_search_player/__init__.py b/src/backends/protocols/gamespy/presence_search_player/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index 92f15f3e5..0651da956 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -1,4 +1,4 @@ -from backends.protocols.gamespy.query_report.storage_info import ChannelInfo +from backends.protocols.gamespy.chat.storage_infos import ChannelInfo from library.src.database.pg_orm import PG_SESSION, GroupList, Games from servers.chat.src.aggregates.peer_room import PeerRoom from servers.query_report.src.v2.aggregates.game_server_info_v2 import GameServerInfoV2 diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 07ca37fc8..6429d1480 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -47,7 +47,7 @@ async def multicast_message(ws: WebSocket): @router.websocket(f"{CHAT}/Channel") async def websocket_endpoint(ws: WebSocket): await ws.accept() - if isinstance(ws, WebSocket): + if isinstance(ws, WebSocket) and ws.client is not None: client_key = f"{ws.client.host}:{ws.client.port}" clients[client_key] = ws try: @@ -66,9 +66,11 @@ async def websocket_endpoint(ws: WebSocket): await client.send_text(request) except WebSocketDisconnect: - client_key = f"{ws.client.host}:{ws.client.port}" - del clients[client_key] - channels[msg.channel_name].remove(ws) + if ws.client is not None: + client_key = f"{ws.client.host}:{ws.client.port}" + del clients[client_key] + if msg is not None: + channels[msg.channel_name].remove(ws) print("Client disconnected") diff --git a/src/backends/routers/gamespy/gstats.py b/src/backends/routers/gamespy/gstats.py index 168a1243f..aa7fa9185 100644 --- a/src/backends/routers/gamespy/gstats.py +++ b/src/backends/routers/gamespy/gstats.py @@ -5,32 +5,32 @@ @router.post(f"{GAMESTATUS}/AuthGameHandler") -async def update_item(request: AuthGameRequest): +async def auth_game(request: AuthGameRequest): raise NotImplementedError() @router.post(f"{GAMESTATUS}/AuthPlayerHandler") -async def update_item(request: AuthPlayerRequest): +async def auth_player(request: AuthPlayerRequest): raise NotImplementedError() @router.post(f"{GAMESTATUS}/NewGameHandler") -async def update_item(request: NewGameRequest): +async def new_game(request: NewGameRequest): raise NotImplementedError() @router.post(f"{GAMESTATUS}/GetPlayerDataHandler") -async def update_item(request: GetPlayerDataRequest): +async def get_player_data(request: GetPlayerDataRequest): raise NotImplementedError() @router.post(f"{GAMESTATUS}/SetPlayerDataHandler") -async def update_item(request: SetPlayerDataRequest): +async def set_player_data(request: SetPlayerDataRequest): raise NotImplementedError() @router.post(f"{GAMESTATUS}/UpdateGameHandler") -async def update_item(request: UpdateGameRequest): +async def updaet_game(request: UpdateGameRequest): raise NotImplementedError() diff --git a/src/backends/routers/gamespy/webservices.py b/src/backends/routers/gamespy/webservices.py index 36b1584ee..e67e4f49e 100644 --- a/src/backends/routers/gamespy/webservices.py +++ b/src/backends/routers/gamespy/webservices.py @@ -6,8 +6,6 @@ router = APIRouter() # Altas services - - @router.post(f"{WEB_SERVICES}/Altas/CreateRecordHandler") async def create_matchless_session(request): raise NotImplementedError() @@ -30,32 +28,32 @@ async def submit_report(request): # Auth services @router.post(f"{WEB_SERVICES}/Auth/LoginProfileHandler") -async def submit_report(request: LoginProfileRequest): +async def login_profile(request: LoginProfileRequest): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Auth/LoginProfileWithGameIdHandler") -async def submit_report(request: LoginProfileWithGameIdRequest): +async def login_profile_with_game_id(request: LoginProfileWithGameIdRequest): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Auth/LoginRemoteAuthHandler") -async def submit_report(request: LoginRemoteAuthRequest): +async def login_remote_auth(request: LoginRemoteAuthRequest): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Auth/LoginRemoteAuthWithGameIdHandler") -async def submit_report(request: LoginRemoteAuthWithGameIdRequest): +async def login_remote_auth_with_game_id(request: LoginRemoteAuthWithGameIdRequest): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Auth/LoginUniqueNickHandler") -async def submit_report(request: LoginUniqueNickRequest): +async def login_uniquenick(request: LoginUniqueNickRequest): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Auth/LoginUniqueNickWithGameIdHandler") -async def submit_report(request: LoginUniqueNickWithGameIdRequest): +async def login_uniquenick_with_game_id(request: LoginUniqueNickWithGameIdRequest): raise NotImplementedError() @@ -66,37 +64,37 @@ async def create_record(request: CreateRecordRequest): @router.post(f"{WEB_SERVICES}/Sake/DeleteRecordHandler") -async def create_record(request): +async def delete_record(request): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Sake/GetMyRecordsHandler") -async def create_record(request: GetMyRecordsRequest): +async def get_my_records(request: GetMyRecordsRequest): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Sake/GetRandomRecordsHandler") -async def create_record(request): +async def get_random_records(request): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Sake/GetRecordLimitHandler") -async def create_record(request): +async def get_record_limit(request): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Sake/RateRecordHandler") -async def create_record(request): +async def rate_record(request): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Sake/SearchForRecordsHandler") -async def create_record(request: SearchForRecordsRequest): +async def search_for_records(request: SearchForRecordsRequest): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Sake/UpdateRecordHandler") -async def create_record(request): +async def update_record(request): raise NotImplementedError() if __name__ == "__main__": diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index e07a51dde..cb2675632 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -57,8 +57,11 @@ def on_disconnected(self) -> None: del self.pool[self.connection.ip_endpoint] pass - def create_switcher(self, buffer: bytes | str) -> "SwitcherBase": - assert isinstance(buffer, bytes) or isinstance(buffer, HttpRequest) + def create_switcher(self, buffer: bytes | str) -> "SwitcherBase": # type: ignore + """ + virtual method helps verify buffer type + """ + assert isinstance(buffer, bytes) or isinstance(buffer, str) def on_received(self, buffer: bytes | str) -> None: if isinstance(buffer, bytes): diff --git a/src/library/src/abstractions/connections.py b/src/library/src/abstractions/connections.py index 24ff84657..307d2278e 100644 --- a/src/library/src/abstractions/connections.py +++ b/src/library/src/abstractions/connections.py @@ -23,8 +23,9 @@ class ConnectionBase: _client: ClientBase ip_endpoint: str """ - ip endpoint format str \ + ip endpoint format str \\ """ + def __init__( self, handler: socketserver.BaseRequestHandler, @@ -40,14 +41,14 @@ def __init__( self.remote_ip = handler.client_address[0] self.remote_port = int(handler.client_address[1]) self.ip_endpoint = f"{self.remote_ip}:{self.remote_port}" - + self.config = config self.t_client = t_client self.logger = logger self._client = self.t_client(self, self.config, self.logger) self._is_started = False - def on_received(self, data: "Optional[bytes|HttpRequest]") -> None: + def on_received(self, data: "bytes|str") -> None: self._client.on_received(data) @abc.abstractmethod @@ -79,7 +80,7 @@ class HttpConnectionBase(TcpConnectionBase): pass -class ServerBase: +class NetworkServerBase: _config: ServerConfig _client_cls: type[ClientBase] _logger: LogWriter diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index 5aee4e35a..fe423873d 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -14,6 +14,9 @@ class CmdHandlerBase: _request: "RequestBase" _result: "ResultBase" _response: "ResponseBase" + """ + the response instance, initialize as None in __init__ + """ _result_cls: "Type[ResultBase]" """ the result type class, use to deserialize json data from backend @@ -106,7 +109,7 @@ def _response_send(self) -> None: """ self._client.send(self._response) - def _handle_exception(self, ex) -> None: + def _handle_exception(self, ex: Exception) -> None: """ override in child class if there are different exception handling behavior """ @@ -118,7 +121,6 @@ def _handle_exception(self, ex) -> None: def _log_current_class(self) -> None: if self._client is None: # todo - # self._client.log_current_class(self) print(self) else: self._client.log_current_class(self) diff --git a/src/library/src/abstractions/redis_channel.py b/src/library/src/abstractions/redis_channel.py deleted file mode 100644 index 8d31d2d78..000000000 --- a/src/library/src/abstractions/redis_channel.py +++ /dev/null @@ -1,102 +0,0 @@ -from dataclasses import dataclass -from typing import Optional - -from library.src.exceptions.general import UniSpyException - - -class RedisChannelBase: - pass - - -# class RedisKey: -# is_required = False -# value: str | int - -# def __init__(self, value: str | int) -> None: -# if (not isinstance(value, str)) or (not isinstance(value, int)): -# raise UniSpyException("Redis key must be int or str") -# self.value = value - - -DELIMETER = ":" - - -class RedisKeyValueObject: - """ - The base class of redis keyvalue object - - ------- - create the redis key with RedisKey to identity that is a redis key - """ - pass - _keys: list[str] - - def _define_keys(self): - """ - Override this function to define keys - """ - if self._keys is None: - raise UniSpyException( - "You must set keys before creating class instance") - if not all(isinstance(key, str) for key in self._keys): - raise UniSpyException("all key must be str") - - def __init__(self): - self._define_keys() - - def to_json(self) -> dict: - """ - convert object to json serializable dict - """ - import json - try: - output_dict = json.dumps(self) - except: - raise UniSpyException("all value must be python basic type") - return output_dict - - # @staticmethod - # def _check_is_basic_type(value): - # if not isinstance(value, str) or not isinstance(value, int) or not isinstance(value, float) or not isinstance(value, list) or isinstance(): - - def get_full_key(self) -> str: - """ - key format: - key1 = value1; key2 = value2; key3=value3 - every property must have - """ - full_key = "" - for key in self._keys: - if key not in self.__dict__: - # fmt: off - raise UniSpyException(f"key: {key} do not have value, in order to build full key every key must have value") - # fmt: on - if self.__dict__[key] is None: - # fmt: off - raise UniSpyException(f"key: {key} can not be none, in order to build full key every key must have value") - # fmt: on - value = self.__dict__[key] - full_key += f"{key}={value}" - if key != self._keys[-1]: - full_key += DELIMETER - return full_key - - def get_search_key(self) -> str: - """ - get keys using to search - key format: - key1=value1;key2=*;key3=value3 - """ - search_key = "" - for key in self._keys: - if key not in self.__dict__: - search_key += f"{key}=*" - - if self.__dict__[key] is None: - search_key += f"{key}=*" - - value = self.__dict__[key] - search_key += f"{key}={value}" - if key != self._keys[-1]: - search_key += DELIMETER - return search_key diff --git a/src/library/src/abstractions/redis_objects.py b/src/library/src/abstractions/redis_objects.py new file mode 100644 index 000000000..032aa6f2b --- /dev/null +++ b/src/library/src/abstractions/redis_objects.py @@ -0,0 +1,262 @@ +# from dataclasses import dataclass, field +# from datetime import datetime +# # import threading +# import asyncio +# from typing import Optional + +# from redis import Redis + + +# from library.src.exceptions.general import UniSpyException + +# import json + + +# class RedisJsonEncoder(json.JSONEncoder): +# def default(self, obj): +# if isinstance(obj, datetime): +# return str(obj) + + +# class RedisJsonDecoder(json.JSONDecoder): +# pass + + +# @dataclass +# class RedisQuery: +# key: str +# value: object + + +# @dataclass +# class RedisKey: +# type: "type" +# name: str + +# def __eq__(self, value: object) -> RedisQuery: +# if self.type is not type(value): +# raise ValueError("The value type is not equal to redis key type") +# # return (self.name, value) +# return RedisQuery(self.name, value) + +# def __ne__(self, value: object) -> bool: +# raise UniSpyException( +# "Redis key attribute do not have this method") + +# def __add__(self, other): +# raise UniSpyException( +# "Redis key attribute do not have this method") + +# def __lt__(self, other): +# raise UniSpyException( +# "Redis key attribute do not have this method") + +# def __gt__(self, other): +# raise UniSpyException( +# "Redis key attribute do not have this method") + +# def __ge__(self, other): +# raise UniSpyException( +# "Redis key attribute do not have this method") + +# def __le__(self, other): +# raise UniSpyException( +# "Redis key attribute do not have this method") + + +# class RedisKeyValueObject: +# _DELIMETER = ":" +# _VALID_TYPE = [int, float, str, datetime] +# """ +# The base class of redis keyvalue object + +# ------- +# create the redis key with RedisKey to identity that is a redis key +# """ +# _database: int +# """ +# Init _database in child class __post_init__(self) +# """ +# _redis_client: Redis +# _expire_time: int +# """ +# expire time seconds +# """ + +# def __post_init__(self): +# if not hasattr(self, "_database"): +# raise UniSpyException( +# "You have to initailize _database in child class") +# self._get_all_redis_key() + +# @property +# def _keys(self): +# keys = [] +# for k, v in type(self).__dict__.items(): +# if type(v) is RedisKey: +# keys.append(k) +# return keys + +# def _get_all_redis_key(self): +# for k, v in type(self).__dict__["__annotations__"].items(): +# if v not in RedisKeyValueObject._VALID_TYPE: +# raise UniSpyException(f"type: {v} is not allowed") +# setattr(type(self), k, RedisKey(type=v, name=k)) + +# def to_json_str(self) -> str: +# """ +# convert object to RedisKeyValueObjectjson serializable dict +# """ +# import json +# try: +# output_dict = json.dumps(self) +# except: +# raise UniSpyException("all value must be python basic type") +# return output_dict + +# def build_full_key(self) -> str: +# """ +# key format: +# key1 = value1; key2 = value2; key3=value3 +# every property mus__init__t have +# """ +# full_key = f"db={self._database}:" + +# for index in range(len(self._keys)): +# key = self._keys[index] +# if key not in self.__dict__: +# raise ValueError( +# f"key: {key} is not initialized, in order to build full key every key must have value") +# value = self.__dict__[key] +# if value is None: +# # fmt: r.keys('*')off +# raise UniSpyException( +# f"key: {key} can not be none, in order to build full key every key must have value") +# # fmt: on +# full_key += f"{key}={value}" +# if index != len(self._keys)-1: +# full_key += RedisKeyValueObject._DELIMETER +# return full_key + +# def build_search_key(self) -> str: +# """ +# get keys using to search +# key format: +# key1=value1;key2=*;key3=value3 +# """ +# search_key = f"db={self._database}:" +# for index in range(len(self._keys)): +# key = self._keys[index] +# if key not in self.__dict__: +# search_key += f"{key}=*" + +# if self.__dict__[key] is None: +# search_key += f"{key}=*" +# else: +# value = self.__dict__[key] +# search_key += f"{key}={value}" + +# if index != len(self._keys)-1: +# search_key += RedisKeyValueObject._DELIMETER +# return search_key + +# def _build_search_object(self, query_dict: dict[str, object]) -> "RedisKeyValueObject": +# param_dict = {} +# for k in self._keys: +# if k not in query_dict: +# param_dict[k] = None +# else: +# param_dict[k] = query_dict[k] +# q_obj = type(self)(**param_dict) +# return q_obj + +# def is_all_instance(self, data: list): +# for d in data: +# if not isinstance(d, RedisQuery): +# raise UniSpyException( +# "The queries list contain non RedisQuery object") + +# self._database = 0 + +# def convert_query_to_dict(self, queries: list[RedisQuery]) -> dict[str, object]: +# query_dict = {} +# for q in queries: +# if q.key not in query_dict.keys(): +# query_dict[q.key] = q.value +# else: +# raise UniSpyException( +# f"The query: {q.key} is duplicated, check your query syntax") + +# return query_dict + +# def query(self, queries: list[RedisQuery]) -> list["RedisKeyValueObject"]: +# self.is_all_instance(queries) +# q_dict = self.convert_query_to_dict(queries) +# obj = self._build_search_object(q_dict) +# search_key = obj.build_search_key() +# db_keys = self._redis_client.keys(search_key) +# results: list = self._redis_client.mget(db_keys) # type: ignore + +# objs: list["RedisKeyValueObject"] = [] +# if results is not None: +# for r in results: +# r_dict = json.loads(r) +# objs.append(type(self)(**r_dict)) +# return objs + +# def count(self, queries: list[RedisQuery]) -> int: +# self.is_all_instance(queries) +# param_dict = {} +# for q in queries: +# param_dict[q.key] = q.value +# obj = type(self)(**param_dict) +# search_key = obj.build_search_key() +# db_keys: list = self._redis_client.keys(search_key) # type: ignore +# return len(db_keys) + +# def first(self, queries: list[RedisQuery]) -> Optional["RedisKeyValueObject"]: +# self.is_all_instance(queries) +# param_dict = {} +# for q in queries: +# param_dict[q.key] = q.value +# obj = type(self)(**param_dict) +# search_key = obj.build_search_key() +# db_keys = self._redis_client.keys(search_key) +# if len(db_keys) == 0: +# result = None +# else: +# result = self._redis_client.get(db_keys[0]) +# return result + +# async def async_count(self, queries: list[RedisQuery]) -> int: +# loop = asyncio.get_event_loop() +# result = await loop.run_in_executor(None, self.count, queries) +# return result + +# async def async_query(self, queries: list[RedisQuery]) -> list["RedisKeyValueObject"]: +# loop = asyncio.get_event_loop() +# result = await loop.run_in_executor(None, self.query, queries) +# return result + +# async def async_first(self, queries: list[RedisQuery]) -> Optional["RedisKeyValueObject"]: +# loop = asyncio.get_event_loop() +# result = await loop.run_in_executor(None, self.first, queries) +# return result + + +# @dataclass +# class TestKvObject(RedisKeyValueObject): +# param1: int +# param2: int + +# def __post_init__(self): +# self._database = 0 +# return super().__post_init__() + + +# t = TestKvObject(param1=1, param2=2) +# t.query([TestKvObject.param1 == 1]) +# t.build_full_key() +# t.build_search_key() +# t2 = TestKvObject(param1=1, param2=None) +# t2.build_search_key() diff --git a/src/library/src/abstractions/server_launcher.py b/src/library/src/abstractions/server_launcher.py index 2697f16ca..68d1a98b3 100644 --- a/src/library/src/abstractions/server_launcher.py +++ b/src/library/src/abstractions/server_launcher.py @@ -1,4 +1,5 @@ import abc +from types import MappingProxyType from library.src.exceptions.general import UniSpyException from library.src.log.log_manager import LogManager, LogWriter from library.src.configs import CONFIG, ServerConfig @@ -7,7 +8,7 @@ VERSION = 0.45 -__SERVER_FULL_SHORT_NAME_MAPPING = { +__SERVER_FULL_SHORT_NAME_MAPPING = MappingProxyType({ "PresenceConnectionManager": "PCM", "PresenceSearchPlayer": "PSP", "CDKey": "CDKey", @@ -19,19 +20,13 @@ "Chat": "Chat", "WebServices": "Web", "GameTrafficReplay": "GTR", -} - - -GLOBAL_LOGGER = LogManager.create(CONFIG.logging.path, "unispy") -""" -the global logger of unispy -""" +}) class ServerLauncherBase: config: ServerConfig logger: LogWriter - + def start(self): self.__show_unispy_logo() self._connect_to_backend() @@ -53,7 +48,7 @@ def _connect_to_backend(self): # post our server config to backends to register resp: requests.Response = requests.post( url=CONFIG.backend.url, - data=self.config.model_dump_json()) + data=self.config.__dict__) if resp.status_code == 200: data = resp.json() if data["status"] != "online": @@ -67,4 +62,4 @@ def _connect_to_backend(self): def _create_logger(self): short_name = __SERVER_FULL_SHORT_NAME_MAPPING[self.config.server_name] - self.logger = LogManager.create(CONFIG.logging.path, short_name) + self.logger = LogManager.create(short_name) diff --git a/src/library/src/configs.py b/src/library/src/configs.py index 4bd22ddf4..6ebb77544 100644 --- a/src/library/src/configs.py +++ b/src/library/src/configs.py @@ -99,13 +99,14 @@ class UniSpyServerConfig: logging: LoggingConfig def __post_init__(self): - self.postgresql = PostgreSql(**self.postgresql) - self.redis = RedisConfig(**self.redis) - self.backend = BackendConfig(**self.backend) + + self.postgresql = PostgreSql(**self.postgresql) # type: ignore + self.redis = RedisConfig(**self.redis) # type: ignore + self.backend = BackendConfig(**self.backend) # type: ignore for key, value in self.servers.items(): - self.servers[key] = ServerConfig(**value) - self.mongodb = MongoDbConfig(**self.mongodb) - self.logging = LoggingConfig(**self.logging) + self.servers[key] = ServerConfig(**value) # type: ignore + self.mongodb = MongoDbConfig(**self.mongodb) # type: ignore + self.logging = LoggingConfig(**self.logging) # type: ignore unispy_config = os.environ.get("UNISPY_CONFIG") diff --git a/src/library/src/database/__init__.py b/src/library/src/database/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/library/src/database/mongodb_orm.py b/src/library/src/database/mongodb_orm.py deleted file mode 100644 index e9012d54b..000000000 --- a/src/library/src/database/mongodb_orm.py +++ /dev/null @@ -1,12 +0,0 @@ -from mongoengine import connect - -from library.src.configs import CONFIG - - -def connect_to_db(): - connect(host=CONFIG.mongodb.url) - - -def get_ttl_param(seconds: int): - assert isinstance(seconds, int) - return {"indexes": [{"fields": ["created"], "expireAfterSeconds": seconds}]} diff --git a/src/library/src/exceptions/general.py b/src/library/src/exceptions/general.py index de1372d0c..361a76330 100644 --- a/src/library/src/exceptions/general.py +++ b/src/library/src/exceptions/general.py @@ -1,5 +1,7 @@ -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional + +from library.src.log.log_manager import GLOBAL_LOGGER if TYPE_CHECKING: @@ -15,16 +17,16 @@ def __init__(self, message: str) -> None: @staticmethod # def handle_exception(e: Exception, client: ClientBase = None): - def handle_exception(e: Exception, client: "ClientBase" = None): + def handle_exception(e: Exception, client: Optional["ClientBase"] = None): if client is None: - # LogWriter.LogError(ex.Message); + GLOBAL_LOGGER.info(str(e)) pass else: if issubclass(type(e), UniSpyException): - ex: UniSpyException = e + ex: UniSpyException = e # type:ignore client.log_error(ex.message) else: - client.log_error(e) + client.log_error(str(e)) pass def __repr__(self) -> str: diff --git a/src/library/src/log/log_manager.py b/src/library/src/log/log_manager.py index 363084018..935853410 100644 --- a/src/library/src/log/log_manager.py +++ b/src/library/src/log/log_manager.py @@ -2,6 +2,9 @@ from logging.handlers import TimedRotatingFileHandler import os +from library.src.configs import CONFIG + + class LogWriter: original_logger: logging.Logger @@ -33,18 +36,19 @@ def create_dir(path): class LogManager: @staticmethod - def create(log_file_path: str, logger_name: str) -> "LogWriter": + def create(logger_name: str) -> "LogWriter": + log_file_path = CONFIG.logging.path create_dir(log_file_path) - + file_name = f"{log_file_path}/{logger_name}.log" logging.basicConfig( - filename=log_file_path, + filename=file_name, level=logging.INFO, format=f"%(asctime)s [{logger_name}] [%(levelname)s]: %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) # 滚动日志文件 file_handler = TimedRotatingFileHandler( - log_file_path, + file_name, when="midnight", interval=1, backupCount=7, @@ -69,3 +73,8 @@ def create(log_file_path: str, logger_name: str) -> "LogWriter": logger.addHandler(console_handler) return LogWriter(logger) + +GLOBAL_LOGGER = LogManager.create("unispy") +""" +the global logger of unispy +""" diff --git a/src/library/src/network/brockers.py b/src/library/src/network/brockers.py index 3fdff5ac6..0b23c3f65 100644 --- a/src/library/src/network/brockers.py +++ b/src/library/src/network/brockers.py @@ -1,4 +1,5 @@ import threading +from typing import Optional import websocket from redis import Redis from library.src.abstractions.brocker import BrockerBase @@ -20,7 +21,7 @@ def subscribe(self): threading.Thread(target=self.get_message).start() def get_message(self): - for message in self._subscriber.get_message(): + for message in self._subscriber.listen(): if not self.is_started: break if message["type"] == "message": @@ -36,8 +37,8 @@ def publish_message(self, message): class WebsocketBrocker(BrockerBase): - _publisher: websocket.WebSocket = None _subscriber: websocket.WebSocketApp + _publisher: Optional[websocket.WebSocket] = None def __init__(self, name: str, url: str, call_back_func: "function") -> None: super().__init__(name, call_back_func) @@ -49,7 +50,7 @@ def __init__(self, name: str, url: str, call_back_func: "function") -> None: self._subscriber.on_open = self._on_open def _on_open(self, ws): - self._publisher: websocket.WebSocket = ws + self._publisher = ws def _on_message(self, _, message): self.receive_message(message) diff --git a/src/library/src/network/http_handler.py b/src/library/src/network/http_handler.py index bf7cbe313..4d9401b88 100644 --- a/src/library/src/network/http_handler.py +++ b/src/library/src/network/http_handler.py @@ -1,6 +1,6 @@ from urllib.parse import urlparse from library.src.abstractions.client import ClientBase -from library.src.abstractions.connections import ConnectionBase, ServerBase +from library.src.abstractions.connections import ConnectionBase, NetworkServerBase from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer from library.src.configs import CONFIG @@ -53,7 +53,7 @@ def do_POST(self) -> None: self.conn.on_received(data) -class HttpServer(ServerBase): +class HttpServer(NetworkServerBase): def start(self) -> None: self._server = ThreadingHTTPServer( (self._config.public_address, self._config.listening_port), HttpHandler diff --git a/src/library/src/network/tcp_handler.py b/src/library/src/network/tcp_handler.py index 3b200a791..4615960d0 100644 --- a/src/library/src/network/tcp_handler.py +++ b/src/library/src/network/tcp_handler.py @@ -2,7 +2,7 @@ import socketserver from typing import Optional from library.src.abstractions.client import ClientBase -from library.src.abstractions.connections import ConnectionBase, ServerBase +from library.src.abstractions.connections import ConnectionBase, NetworkServerBase from library.src.network import DATA_SIZE from library.src.configs import CONFIG @@ -46,7 +46,7 @@ def handle(self) -> None: pass -class TcpServer(ServerBase): +class TcpServer(NetworkServerBase): def start(self) -> None: self._server = socketserver.ThreadingTCPServer( (self._config.public_address, self._config.listening_port), diff --git a/src/library/src/network/udp_handler.py b/src/library/src/network/udp_handler.py index 814a1ba33..7b6e9d2a1 100644 --- a/src/library/src/network/udp_handler.py +++ b/src/library/src/network/udp_handler.py @@ -2,7 +2,7 @@ import socketserver from library.src.abstractions.client import ClientBase -from library.src.abstractions.connections import ConnectionBase, ServerBase +from library.src.abstractions.connections import ConnectionBase, NetworkServerBase from library.src.configs import CONFIG @@ -26,7 +26,7 @@ def send(self, data: bytes) -> None: conn.sendto(data, self.client_address) -class UdpServer(ServerBase): +class UdpServer(NetworkServerBase): def start(self) -> None: self._server = socketserver.ThreadingUDPServer( (self._config.public_address, self._config.listening_port), diff --git a/src/servers/chat/src/abstractions/channel.py b/src/servers/chat/src/abstractions/channel.py index e02903744..cd8e42821 100644 --- a/src/servers/chat/src/abstractions/channel.py +++ b/src/servers/chat/src/abstractions/channel.py @@ -1,3 +1,4 @@ +from typing import Optional from library.src.abstractions.client import ClientBase from servers.chat.src.abstractions.contract import * from servers.chat.src.abstractions.contract import RequestBase @@ -9,7 +10,7 @@ class ChannelRequestBase(RequestBase): - channel_name: str = None + channel_name: str def parse(self) -> None: super().parse() @@ -31,32 +32,36 @@ class ChannelHandlerBase(PostLoginHandlerBase): _channel: Channel _user: ChannelUser _request: ChannelRequestBase + _response: ResponseBase def __init__(self, client: ClientBase, request: RequestBase): super().__init__(client, request) + # self._channel = None + # self._response = None def _request_check(self) -> None: if self._request.raw_request is None: return super()._request_check() if self._channel is None: - self._channel = ChannelManager.get_channel( + channel = ChannelManager.get_channel( self._request.channel_name ) - if self._channel is None: - raise NoSuchChannelException( - f"No such channel {self._request.channel_name}", - self._request.channel_name, - ) - + if channel is None: + raise NoSuchChannelException( + f"No such channel {self._request.channel_name}", + ) + self._channel = channel if self._user is None: - self._user = self._channel.get_user(self._client) + user = self._channel.get_user_by_nick( + self._client.info.nick_name) - if self._user is None: - raise NoSuchNickException( - f"Can not find user with nickname: { - self._client.info.nick_name} user_name: {self._client.info.user_name}" - ) + if user is None: + raise NoSuchNickException( + f"Can not find user with nickname: { + self._client.info.nick_name} user_name: {self._client.info.user_name}" + ) + self._user = user def handle(self) -> None: super().handle() @@ -66,8 +71,15 @@ def handle(self) -> None: self._update_channel_cache() except Exception as e: self._handle_exception(e) + if ChannelHandlerBase._debug: + raise e def _publish_message(self): + if self._response is None: + self._client.log_warn("response is not constructed.") + if self._channel is None: + self._client.log_warn("channel is not assined") + return self._channel.send_message_to_brocker(self._response.sending_buffer) def _update_channel_cache(self): diff --git a/src/servers/chat/src/abstractions/contract.py b/src/servers/chat/src/abstractions/contract.py index c522ceece..be7a92288 100644 --- a/src/servers/chat/src/abstractions/contract.py +++ b/src/servers/chat/src/abstractions/contract.py @@ -1,4 +1,5 @@ import abc +from typing import Optional import library.src.abstractions.contracts from servers.chat.src.exceptions.general import ChatException @@ -59,17 +60,16 @@ class ResponseBase(library.src.abstractions.contracts.ResponseBase): _result: ResultBase _request: RequestBase - def __init__(self, request: RequestBase, result: ResultBase | None) -> None: + def __init__(self, request: RequestBase, result: Optional[ResultBase]) -> None: super().__init__(request, result) if result is not None: assert issubclass(type(result), ResultBase) - assert issubclass(request, RequestBase) - + assert issubclass(type(request), RequestBase) if __name__ == "__main__": # Example usage: - request = RequestBase() + request = RequestBase("") request.raw_request = "your_raw_request_here" request.parse() print(request.command_name) diff --git a/src/servers/chat/src/abstractions/handler.py b/src/servers/chat/src/abstractions/handler.py index 9bea518a1..1a5540e38 100644 --- a/src/servers/chat/src/abstractions/handler.py +++ b/src/servers/chat/src/abstractions/handler.py @@ -1,23 +1,25 @@ from library.src.abstractions.client import ClientBase -from servers.chat.src.abstractions.contract import RequestBase +from servers.chat.src.abstractions.contract import RequestBase, ResultBase from servers.chat.src.applications.client import Client from servers.chat.src.exceptions.general import IRCException import library.src.abstractions.handler +from typing import cast class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase): _request: RequestBase _client: Client + _result: ResultBase def __init__(self, client: ClientBase, request: RequestBase): super().__init__(client, request) - assert issubclass(request, RequestBase) + assert issubclass(type(request), RequestBase) - def _handle_exception(self, ex) -> None: + def _handle_exception(self, ex: Exception) -> None: t_ex = type(ex) if t_ex is IRCException: - self._client.send() - + ex = cast(IRCException, ex) + self._client.connection.send(ex.message.encode()) super()._handle_exception(ex) diff --git a/src/servers/chat/src/abstractions/message.py b/src/servers/chat/src/abstractions/message.py index 948f97463..d8fe878cb 100644 --- a/src/servers/chat/src/abstractions/message.py +++ b/src/servers/chat/src/abstractions/message.py @@ -16,7 +16,6 @@ def parse(self): self.type = MessageType.CHANNEL_MESSAGE else: self.type = MessageType.USER_MESSAGE - self.channel_name = None self.nick_name = self._cmd_params[0] self.message = self._longParam diff --git a/src/servers/chat/src/aggregates/channel.py b/src/servers/chat/src/aggregates/channel.py index 11bc583ea..30ea0d930 100644 --- a/src/servers/chat/src/aggregates/channel.py +++ b/src/servers/chat/src/aggregates/channel.py @@ -1,6 +1,6 @@ from dataclasses import dataclass import datetime -from typing import overload +from typing import Optional, overload from uuid import UUID from pydantic import BaseModel, field_validator @@ -31,17 +31,17 @@ class Channel: datetime.timezone.utc) kv_manager: KeyValueManager = KeyValueManager() room_type: PeerRoomType - password: str - topic: str = None - group_id: int = None - room_name: str = None - previously_join_channel: str = None + password: Optional[str] + topic: Optional[str] = None + group_id: Optional[int] = None + room_name: Optional[str] = None + previously_join_channel: Optional[str] = None @property def is_valid_peer_room(self) -> bool: return self.group_id is not None and self.room_name is not None - def __init__(self, name: str, client: Client, password: str = None) -> None: + def __init__(self, name: str, client: Client, password: Optional[str] = None) -> None: self.server_id = client.server_config.server_id self.name = name self.password = password @@ -90,7 +90,7 @@ def get_title_room_name(self): _creator_nick_name: str @property - def creator(self) -> ChannelUser: + def creator(self) -> Optional[ChannelUser]: if self._creator_nick_name in self.users: return self.users[self._creator_nick_name] else: @@ -108,7 +108,7 @@ def _add_ban_user(self, request: ModeRequest): def _remove_ban_user(self, nick_name: str): if nick_name in self.ban_list: - del self.ban_list[nick_name:str] + del self.ban_list[nick_name] def _add_channel_operator(self, nick_name: str): if nick_name not in self.users: @@ -131,14 +131,12 @@ def _user_voice_permission(self, nick_name: str, enable: bool = True): user = self.users[nick_name] user.is_voiceable = enable - @overload - def get_user(self, nick_name: str) -> ChannelUser: + def get_user_by_nick(self, nick_name: str) -> Optional[ChannelUser]: if nick_name in self.users: return self.users[nick_name] return None - @overload - def get_user(self, client: Client) -> ChannelUser: + def get_user_by_client(self, client: Client) -> Optional[ChannelUser]: for user in self.users.values(): if ( client.connection.remote_ip == user.remote_ip @@ -147,11 +145,11 @@ def get_user(self, client: Client) -> ChannelUser: return user return None - def add_bind_on_user_and_channel(joiner: ChannelUser): + def add_bind_on_user_and_channel(self, joiner: ChannelUser): joiner.channel.users[joiner.client.info.nick_name] joiner.client.info.joined_channels[joiner.channel.name] = joiner.channel - def remove_bind_on_user_and_channel(leaver: ChannelUser): + def remove_bind_on_user_and_channel(self, leaver: ChannelUser): del leaver.channel.users[leaver.client.info.nick_name] del leaver.client.info.joined_channels[leaver.channel.name] @@ -168,7 +166,7 @@ def get_message_from_brocker(self, message: str): we directly send the message from brocker to all channel local user """ for nick, user in self.users.items(): - user.client.send(message) + user.client.connection.send(message.encode()) def send_message_to_brocker(self, message: str): data = {"channel_name": self.name, "message": message} @@ -185,10 +183,12 @@ class ChannelManager: """The code blow is for channel manage""" @staticmethod - def get_channel(name: str) -> Channel: + def get_channel(name: str) -> Optional[Channel]: if name in ChannelManager.local_channels: return ChannelManager.local_channels[name] + return None + @staticmethod def add_channel(channel: Channel): if channel.name not in ChannelManager.local_channels: ChannelManager.local_channels[channel.name] = channel diff --git a/src/servers/chat/src/aggregates/key_value_manager.py b/src/servers/chat/src/aggregates/key_value_manager.py index cc0cfed9f..4569ba2da 100644 --- a/src/servers/chat/src/aggregates/key_value_manager.py +++ b/src/servers/chat/src/aggregates/key_value_manager.py @@ -17,7 +17,7 @@ def build_key_value_string(self, key_values: dict): flags += f"\\{key}\\{value}" return flags - def get_value_string(self, keys: list[str]) -> list[str]: + def get_value_string(self, keys: list[str]) -> str: values = "" for key in keys: if key in self.data: diff --git a/src/servers/chat/src/applications/client.py b/src/servers/chat/src/applications/client.py index 2771b167d..ebdc508be 100644 --- a/src/servers/chat/src/applications/client.py +++ b/src/servers/chat/src/applications/client.py @@ -10,8 +10,8 @@ class ClientInfo: - previously_joined_channel: "Channel" - joined_channels: list["Channel"] + previously_joined_channel: str + joined_channels: dict[str, "Channel"] nick_name: str gamename: str user_name: str diff --git a/src/servers/chat/src/applications/server_launcher.py b/src/servers/chat/src/applications/server_launcher.py index 0dbdd3713..357b3f821 100644 --- a/src/servers/chat/src/applications/server_launcher.py +++ b/src/servers/chat/src/applications/server_launcher.py @@ -12,7 +12,7 @@ def __init__(self) -> None: self.config = CONFIG.servers["Chat"] def _launch_server(self): - TcpServer(self.config, Client).start() + TcpServer(self.config, Client, self.logger).start() if __name__ == "__main__": diff --git a/src/servers/chat/src/contracts/requests/channel.py b/src/servers/chat/src/contracts/requests/channel.py index d98104e33..29fc98e0d 100644 --- a/src/servers/chat/src/contracts/requests/channel.py +++ b/src/servers/chat/src/contracts/requests/channel.py @@ -1,5 +1,5 @@ import re -from typing import List +from typing import List, Optional from servers.chat.src.abstractions.channel import ChannelRequestBase from servers.chat.src.enums.general import ( GetKeyRequestType, @@ -117,7 +117,7 @@ def parse(self): return super().parse() if len(self._cmd_params) == 1: - self.request_type == ModeRequestType.GET_CHANNEL_MODES + self.request_type = ModeRequestType.GET_CHANNEL_MODES elif len(self._cmd_params) == 2 or len(self._cmd_params) == 3: self.request_type = ModeRequestType.SET_CHANNEL_MODES self.mode_flag = self._cmd_params[1] @@ -266,7 +266,7 @@ def parse(self): class NamesRequest(ChannelRequestBase): channel_name: str - def __init__(self, raw_request: str = None) -> None: + def __init__(self, raw_request: Optional[str] = None) -> None: if raw_request is not None: super().__init__(raw_request) @@ -275,7 +275,7 @@ class PartRequest(ChannelRequestBase): channel_name: str reason: str = "Unknown reason" - def __init__(self, raw_request: str = None) -> None: + def __init__(self, raw_request: Optional[str] = None) -> None: if raw_request is not None: super().__init__(raw_request) @@ -350,5 +350,3 @@ def parse(self) -> None: else: self.request_type = TopicRequestType.SET_CHANNEL_TOPIC self.channel_topic = self._longParam - - diff --git a/src/servers/chat/src/contracts/requests/general.py b/src/servers/chat/src/contracts/requests/general.py index 3086f9dcd..1334ead9d 100644 --- a/src/servers/chat/src/contracts/requests/general.py +++ b/src/servers/chat/src/contracts/requests/general.py @@ -78,7 +78,7 @@ class ListRequest(RequestBase): def parse(self): super().parse() - if self._cmd_params is None or self._cmd_params.count() == 0: + if self._cmd_params is None or len(self._cmd_params) == 0: raise ChatException("The Search filter is missing.") self.is_searching_channel = True diff --git a/src/servers/chat/src/contracts/responses/channel.py b/src/servers/chat/src/contracts/responses/channel.py index 2d37bcd1f..0c76d8e06 100644 --- a/src/servers/chat/src/contracts/responses/channel.py +++ b/src/servers/chat/src/contracts/responses/channel.py @@ -27,6 +27,7 @@ ModeResult, NamesResult, PartResult, + SetChannelKeyResult, TopicResult, ) from servers.chat.src.enums.general import ModeRequestType @@ -129,13 +130,13 @@ def build(self) -> None: class SetChannelKeyResponse(ChannelResponseBase): _request: SetChannelKeyRequest - _result: GetChannelKeyResult + _result: SetChannelKeyResult def __init__( - self, request: SetChannelKeyRequest, result: GetChannelKeyResult + self, request: SetChannelKeyRequest, result: SetChannelKeyResult ) -> None: assert isinstance(request, SetChannelKeyRequest) - assert isinstance(result, GetChannelKeyResult) + assert isinstance(result, SetChannelKeyResult) super().__init__(request, result) def build(self) -> None: diff --git a/src/servers/chat/src/contracts/responses/general.py b/src/servers/chat/src/contracts/responses/general.py index 6dc095d01..498106c01 100644 --- a/src/servers/chat/src/contracts/responses/general.py +++ b/src/servers/chat/src/contracts/responses/general.py @@ -18,7 +18,7 @@ class CdKeyResponse(ResponseBase): def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {CD_KEY} * 1 :Authenticated.\r\n" + self.sending_buffer = f":{SERVER_DOMAIN} {CD_KEY} * 1 :Authenticated.\r\n" # noqa class CryptResponse(ResponseBase): @@ -26,9 +26,7 @@ def __init__(self) -> None: pass def build(self) -> None: - self.sending_buffer = ( - f":{SERVER_DOMAIN} {SECURE_KEY} * {CLIENT_KEY} {SERVER_KEY}\r\n" - ) + self.sending_buffer = f":{SERVER_DOMAIN} {SECURE_KEY} * {CLIENT_KEY} {SERVER_KEY}\r\n" # noqa class GetKeyResponse(ResponseBase): @@ -44,9 +42,9 @@ def build(self) -> None: self.sending_buffer = "" for value in self._result.values: - self.sending_buffer += f":{SERVER_DOMAIN} {GET_KEY} * {self._result.nick_name} {self._request.cookie} {value}\r\n" + self.sending_buffer += f":{SERVER_DOMAIN} {GET_KEY} * {self._result.nick_name} {self._request.cookie} {value}\r\n" # noqa - self.sending_buffer += f":{SERVER_DOMAIN} {END_GET_KEY} * {self._request.cookie} * :End Of GETKEY.\r\n" + self.sending_buffer += f":{SERVER_DOMAIN} {END_GET_KEY} * {self._request.cookie} * :End Of GETKEY.\r\n" # noqa class ListResponse(ResponseBase): @@ -54,7 +52,7 @@ class ListResponse(ResponseBase): def __init__(self, result: ListResult) -> None: assert isinstance(result, ListResult) - super().__init__(None, result) + self._result = result def build(self) -> None: self.sending_buffer = "" @@ -63,8 +61,8 @@ def build(self) -> None: total_channel_user, channel_topic, ) in self._result.channel_info_list: - self.sending_buffer += f":{self._result.user_irc_prefix} {LIST_START} * {channel_name} {total_channel_user} {channel_topic}\r\n" - self.sending_buffer += f":{self._result.user_irc_prefix} {LIST_END}\r\n" + self.sending_buffer += f":{self._result.user_irc_prefix} {LIST_START} * {channel_name} {total_channel_user} {channel_topic}\r\n" # noqa + self.sending_buffer += f":{self._result.user_irc_prefix} {LIST_END}\r\n" # noqa class LoginResponse(ResponseBase): @@ -72,10 +70,10 @@ class LoginResponse(ResponseBase): def __init__(self, result: LoginResult) -> None: assert isinstance(result, LoginResult) - super().__init__() + self._result = result def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {LOGIN} * {self._result.user_id} {self._result.profile_id}\r\n" + self.sending_buffer = f":{SERVER_DOMAIN} {LOGIN} * {self._result.user_id} {self._result.profile_id}\r\n" # noqa class NickResponse(ResponseBase): @@ -83,10 +81,10 @@ class NickResponse(ResponseBase): def __init__(self, result: NickResult) -> None: assert isinstance(result, NickResult) - super().__init__(None, result) + self._result = result def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {WELCOME} {self._result.nick_name} :Welcome to UniSpy!\r\n" + self.sending_buffer = f":{SERVER_DOMAIN} {WELCOME} {self._result.nick_name} :Welcome to UniSpy!\r\n" # noqa class PingResponse(ResponseBase): @@ -94,10 +92,11 @@ class PingResponse(ResponseBase): def __init__(self, result: PingResult) -> None: assert isinstance(result, PingResult) - super().__init__(None, result) + self._result = result def build(self) -> None: - self.sending_buffer = f":{self._result.requester_irc_prefix} {PONG}\r\n" + self.sending_buffer = f":{ + self._result.requester_irc_prefix} {PONG}\r\n" class UserIPResponse(ResponseBase): @@ -105,12 +104,10 @@ class UserIPResponse(ResponseBase): def __init__(self, result: UserIPResult) -> None: assert isinstance(result, UserIPResult) - super().__init__(None, result) + self._result = result def build(self) -> None: - self.sending_buffer = ( - f":{SERVER_DOMAIN} {USER_IP} :@{self._result.remote_ip_address}\r\n" - ) + self.sending_buffer = f":{SERVER_DOMAIN} {USER_IP} :@{self._result.remote_ip_address}\r\n" # noqa class WhoIsResponse(ResponseBase): @@ -118,19 +115,19 @@ class WhoIsResponse(ResponseBase): def __init__(self, result: WhoIsResult) -> None: assert isinstance(result, UserIPResult) - super().__init__(None, result) + self._result = result def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {WHO_IS_USER} {self._result.nick_name} {self._result.name} {self._result.user_name} {self._result.public_ip_address} * :{self._result.user_name}\r\n" + self.sending_buffer = f":{SERVER_DOMAIN} {WHO_IS_USER} {self._result.nick_name} {self._result.name} {self._result.user_name} {self._result.public_ip_address} * :{self._result.user_name}\r\n" # noqa if len(self._result.joined_channel_name) != 0: channel_name = "" for name in self._result.joined_channel_name: - channel_name += f"@{name} " + channel_name += f"@{name} " # noqa - self.sending_buffer += f":{SERVER_DOMAIN} {WHO_IS_CHANNELS} {self._result.nick_name} {self._result.name} :{channel_name}\r\n" + self.sending_buffer += f":{SERVER_DOMAIN} {WHO_IS_CHANNELS} {self._result.nick_name} {self._result.name} :{channel_name}\r\n" # noqa - self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO_IS} {self._result.nick_name} {self._result.name} :End of WHOIS list. \r\n" + self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO_IS} {self._result.nick_name} {self._result.name} :End of WHOIS list. \r\n" # noqa class WhoResponse(ResponseBase): @@ -151,11 +148,11 @@ def build(self): nick_name, modes, ) in self._result.infos: - self.sending_buffer += f":{SERVER_DOMAIN} {WHO_REPLY} * {channel_name} {user_name} {public_ip_address} * {nick_name} {modes} *\r\n" + self.sending_buffer += f":{SERVER_DOMAIN} {WHO_REPLY} * {channel_name} {user_name} {public_ip_address} * {nick_name} {modes} *\r\n" # noqa if self._request.request_type == WhoRequestType.GET_CHANNEL_USER_INFO: if len(self._result.infos) > 0: - self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.channel_name} * :End of WHO.\r\n" + self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.channel_name} * :End of WHO.\r\n" # noqa elif self._request.request_type == WhoRequestType.GET_USER_INFO: if len(self._result.infos) > 0: - self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.nick_name} * :End of WHO.\r\n" + self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.nick_name} * :End of WHO.\r\n" # noqa diff --git a/src/servers/chat/src/contracts/results/channel.py b/src/servers/chat/src/contracts/results/channel.py index 17294593d..ebfc7d915 100644 --- a/src/servers/chat/src/contracts/results/channel.py +++ b/src/servers/chat/src/contracts/results/channel.py @@ -50,12 +50,6 @@ class PartResult(ResultBase): channel_name: str -class GetChannelKeyResult(ResultBase): - channel_user_irc_prefix: str - channel_name: str - values: str - - class TopicResult(ResultBase): channel_name: str channel_topic: str diff --git a/src/servers/chat/src/exceptions/general.py b/src/servers/chat/src/exceptions/general.py index cca15cada..7ba9cdf51 100644 --- a/src/servers/chat/src/exceptions/general.py +++ b/src/servers/chat/src/exceptions/general.py @@ -18,7 +18,7 @@ def __init__(self, message: "str", error_code: "IRCErrorCode") -> None: self.error_code = error_code def build(self): - pass + raise ChatException("IRCException is abstracted class, should not be initialized") class IRCChannelException(IRCException): @@ -28,7 +28,7 @@ def __init__(self, message: str, error_code: IRCErrorCode) -> None: super().__init__(message, error_code) def build(self): - self.sending_buffer = f":{SERVER_DOMAIN} {self.error_code} * {self.channel_name} :{self.message}\r\n" + self.sending_buffer = f":{SERVER_DOMAIN} {self.error_code} * {self.channel_name} :{self.message}\r\n" # noqa: E501 class ErrOneUSNickNameException(IRCException): @@ -74,9 +74,7 @@ def __init__( self.new_nick = new_nick def build(self): - self.sending_buffer = ( - f"{SERVER_DOMAIN} {self.error_code} {self.old_nick} {self.new_nick} *\r\n" - ) + self.sending_buffer = f"{SERVER_DOMAIN} {self.error_code} {self.old_nick} {self.new_nick} *\r\n" # noqa: E501 class NoSuchNickException(IRCException): @@ -92,7 +90,6 @@ def __init__( ) -> None: super().__init__(message, error_code) - class RegisterNickFaildException(IRCException): def __init__( self, message: str, error_code: IRCErrorCode = IRCErrorCode.REGISTER_NICK_FAILED @@ -102,7 +99,7 @@ def __init__( class TooManyChannelsException(IRCException): def __init__( - self, message: str, error_code: IRCErrorCode.TOO_MANY_CHANNELS + self, message: str, error_code: IRCErrorCode = IRCErrorCode.TOO_MANY_CHANNELS ) -> None: super().__init__(message, error_code) diff --git a/src/servers/chat/src/handlers/channel.py b/src/servers/chat/src/handlers/channel.py index c724d9c4d..87ca26ddc 100644 --- a/src/servers/chat/src/handlers/channel.py +++ b/src/servers/chat/src/handlers/channel.py @@ -1,8 +1,8 @@ +from library.src.abstractions.client import ClientBase from servers.chat.src.abstractions.channel import ChannelHandlerBase from servers.chat.src.aggregates.channel import Channel, ChannelManager from servers.chat.src.aggregates.channel_user import ChannelUser from servers.chat.src.aggregates.response_name import * -from servers.chat.src.applications.client import Client from servers.chat.src.contracts.requests.channel import ( GetCKeyRequest, GetChannelKeyRequest, @@ -45,7 +45,7 @@ class GetChannelKeyHandler(ChannelHandlerBase): _request: GetChannelKeyRequest _result: GetChannelKeyResult - def __init__(self, client: Client, request: GetChannelKeyRequest): + def __init__(self, client: ClientBase, request: GetChannelKeyRequest): assert isinstance(request, GetChannelKeyRequest) self._is_fetching_data = True super().__init__(client, request) @@ -61,7 +61,7 @@ class GetCKeyHandler(ChannelHandlerBase): _request: GetCKeyRequest _result: GetCKeyResult - def __init__(self, client: Client, request: GetCKeyRequest): + def __init__(self, client: ClientBase, request: GetCKeyRequest): assert isinstance(request, GetCKeyRequest) super().__init__(client, request) @@ -79,7 +79,7 @@ class JoinHandler(ChannelHandlerBase): _request: JoinRequest _result: JoinResult - def __init__(self, client: Client, request: JoinRequest): + def __init__(self, client: ClientBase, request: JoinRequest): assert isinstance(request, JoinRequest) super().__init__(client, request) @@ -90,22 +90,21 @@ def _check_user_in_remote(self): pass def _check_user_in_local(self): - self._channel = ChannelManager.get_channel( + channel = ChannelManager.get_channel( self._request.channel_name) - if self._channel is not None: - if self._client.info.nick_name in self._channel.users: - raise ChatException("user is already in channel") - # if channel still none we create the channel - if self._channel is None: + if channel is None: self._channel = Channel( self._request.channel_name, self._client, self._request.password) ChannelManager.add_channel(self._channel) + else: + self._channel = channel + if self._client.info.nick_name in self._channel.users: + raise ChatException("user is already in channel") def _request_check(self) -> None: # todo check if user already in local channel # self._check_user_in_remote() self._check_user_in_local() - self._user = ChannelUser(self._client, self._channel) self._channel.add_bind_on_user_and_channel(self._user) @@ -123,7 +122,7 @@ class KickHandler(ChannelHandlerBase): _request: KickRequest _result: KickResult - def __init__(self, client: Client, request: KickRequest): + def __init__(self, client: ClientBase, request: KickRequest): assert isinstance(request, KickRequest) super().__init__(client, request) @@ -138,7 +137,7 @@ class ModeHandler(ChannelHandlerBase): _request: ModeRequest _result: ModeResult - def __init__(self, client: Client, request: ModeRequest): + def __init__(self, client: ClientBase, request: ModeRequest): assert isinstance(request, ModeRequest) super().__init__(client, request) @@ -170,7 +169,7 @@ class NamesHandler(ChannelHandlerBase): _request: NamesRequest _result: NamesResult - def __init__(self, client: Client, request: NamesRequest): + def __init__(self, client: ClientBase, request: NamesRequest): assert isinstance(request, NamesRequest) self._is_fetching_data = True super().__init__(client, request) @@ -183,7 +182,7 @@ class PartHandler(ChannelHandlerBase): _request: PartRequest _result: PartResult - def __init__(self, client: Client, request: PartRequest): + def __init__(self, client: ClientBase, request: PartRequest): assert isinstance(request, PartRequest) super().__init__(client, request) @@ -198,7 +197,7 @@ class SetChannelKeyHandler(ChannelHandlerBase): _request: SetChannelKeyRequest _result: SetChannelKeyResult - def __init__(self, client: Client, request: SetChannelKeyRequest): + def __init__(self, client: ClientBase, request: SetChannelKeyRequest): assert isinstance(self._request, SetChannelKeyRequest) super().__init__(client, request) @@ -212,7 +211,7 @@ def _response_send(self): class SetCKeyHandler(ChannelHandlerBase): _request: SetCKeyRequest - def __init__(self, client: Client, request: SetCKeyRequest): + def __init__(self, client: ClientBase, request: SetCKeyRequest): assert isinstance(request, SetCKeyRequest) super().__init__(client, request) @@ -227,7 +226,7 @@ class TopicHandler(ChannelHandlerBase): _request: TopicRequest _result: TopicResult - def __init__(self, client: Client, request: TopicRequest): + def __init__(self, client: ClientBase, request: TopicRequest): assert isinstance(request, TopicRequest) super().__init__(client, request) diff --git a/src/servers/chat/src/handlers/general.py b/src/servers/chat/src/handlers/general.py index be24ed273..2f5992c6a 100644 --- a/src/servers/chat/src/handlers/general.py +++ b/src/servers/chat/src/handlers/general.py @@ -2,6 +2,7 @@ from library.src.abstractions.client import ClientBase from servers.chat.src.abstractions.contract import RequestBase from servers.chat.src.abstractions.handler import CmdHandlerBase, PostLoginHandlerBase +from servers.chat.src.applications.client import Client from servers.chat.src.contracts.requests.channel import GetUdpRelayRequest from servers.chat.src.contracts.requests.general import ( CdkeyRequest, @@ -15,6 +16,7 @@ QuitRequest, SetKeyRequest, UserIPRequest, + UserRequest, WhoIsRequest, WhoRequest, ) @@ -103,7 +105,7 @@ def __init__(self, client: ClientBase, request: ListRequest): super().__init__(client, request) def _response_construct(self) -> None: - self._response = ListResponse(self._request, self._result) + self._response = ListResponse(self._result) class LoginHandler(CmdHandlerBase): @@ -127,7 +129,7 @@ def __init__(self, client: ClientBase, request: NickRequest): super().__init__(client, request) def _response_construct(self) -> None: - self._response = NickResponse(self._request, self._result) + self._response = NickResponse(self._result) class PingHandler(CmdHandlerBase): @@ -159,10 +161,10 @@ def __init__(self, client: ClientBase, request: SetKeyRequest): class UserHandler(CmdHandlerBase): - _request: SetKeyRequest + _request: UserRequest - def __init__(self, client: ClientBase, request: SetKeyRequest): - assert isinstance(request, SetKeyRequest) + def __init__(self, client: ClientBase, request: UserRequest): + assert isinstance(request, UserRequest) super().__init__(client, request) diff --git a/src/servers/chat/src/handlers/switcher.py b/src/servers/chat/src/handlers/switcher.py index adee52ec0..d6c7adaa3 100644 --- a/src/servers/chat/src/handlers/switcher.py +++ b/src/servers/chat/src/handlers/switcher.py @@ -1,3 +1,4 @@ +from typing import Optional from library.src.abstractions.client import ClientBase from library.src.abstractions.handler import CmdHandlerBase from library.src.abstractions.switcher import SwitcherBase @@ -83,7 +84,7 @@ def _process_raw_request(self) -> None: name = raw_request.strip(" ").split(" ")[0] self._requests.append((name, raw_request)) - def _create_cmd_handlers(self, name: str, rawRequest: str) -> CmdHandlerBase: + def _create_cmd_handlers(self, name: str, rawRequest: str) -> Optional[CmdHandlerBase]: request = rawRequest assert isinstance(name, str) match name: @@ -114,7 +115,7 @@ def _create_cmd_handlers(self, name: str, rawRequest: str) -> CmdHandlerBase: return WhoHandler(self._client, WhoRequest(request)) case "WHOIS": return WhoIsHandler(self._client, WhoIsRequest(request)) - # endregion + # endregionCmdHandlerBase # region Channel case "GETCHANKEY": @@ -149,4 +150,4 @@ def _create_cmd_handlers(self, name: str, rawRequest: str) -> CmdHandlerBase: return UTMHandler(self._client, UTMRequest(request)) case _: return None - # endregion + # endregion diff --git a/src/servers/game_status/src/applications/client.py b/src/servers/game_status/src/applications/client.py index 0512cfc23..4870648c9 100644 --- a/src/servers/game_status/src/applications/client.py +++ b/src/servers/game_status/src/applications/client.py @@ -1,3 +1,4 @@ +from typing import Optional from library.src.abstractions.client import ClientBase, ClientInfoBase from library.src.abstractions.switcher import SwitcherBase from library.src.log.log_manager import LogWriter @@ -10,23 +11,23 @@ class ClientInfo(ClientInfoBase): - session_key: str = None - game_name: str = None - is_user_authenticated: bool = False - is_player_authenticated: bool = False - is_game_authenticated: bool = False - profile_id: int = None - game_session_key: str = None + session_key: Optional[str] + game_name: Optional[str] + is_user_authenticated: bool + is_player_authenticated: bool + is_game_authenticated: bool + profile_id: Optional[int] + game_session_key: Optional[str] def __init__(self) -> None: super().__init__() - self.session_key: str = None - self.game_name: str = None - self.is_user_authenticated: bool = False - self.is_player_authenticated: bool = False - self.is_game_authenticated: bool = False - self.profile_id: int = None - self.game_session_key: str = None + self.session_key = None + self.game_name = None + self.profile_id = None + self.game_session_key = None + self.is_user_authenticated = False + self.is_player_authenticated = False + self.is_game_authenticated = False class Client(ClientBase): diff --git a/src/servers/game_status/src/applications/server_launcher.py b/src/servers/game_status/src/applications/server_launcher.py index c50b76bf2..2b2c7e524 100644 --- a/src/servers/game_status/src/applications/server_launcher.py +++ b/src/servers/game_status/src/applications/server_launcher.py @@ -12,7 +12,7 @@ def __init__(self) -> None: self.config = CONFIG.servers["GameStatus"] def _launch_server(self): - TcpServer(self.config, Client).start() + TcpServer(self.config, Client, self.logger).start() if __name__ == "__main__": diff --git a/src/servers/game_status/src/contracts/requests.py b/src/servers/game_status/src/contracts/requests.py index 6755230f9..d4674c63f 100644 --- a/src/servers/game_status/src/contracts/requests.py +++ b/src/servers/game_status/src/contracts/requests.py @@ -1,9 +1,10 @@ -from typing import final +from typing import Optional, final from library.src.extentions.gamespy_utils import convert_to_key_value from servers.game_status.src.abstractions.contracts import RequestBase from servers.game_status.src.enums.general import AuthMethod, PersistStorageType from servers.game_status.src.exceptions.general import GSException + @final class AuthGameRequest(RequestBase): game_name: str @@ -62,7 +63,7 @@ def parse(self) -> None: @final class GetPlayerDataRequest(RequestBase): - profile_id: str + profile_id: int storage_type: PersistStorageType data_index: int is_get_all_data: bool = False @@ -128,9 +129,9 @@ def parse(self) -> None: @final class NewGameRequest(RequestBase): is_client_local_storage_available: bool - challenge: str = None - connection_id: int = None - session_key: str = None + challenge: str + connection_id: int + session_key: str def parse(self) -> None: super().parse() @@ -203,7 +204,7 @@ def parse(self) -> None: @final class UpdateGameRequest(RequestBase): - connection_id: int = None + connection_id: int is_done: bool is_client_local_storage_available: bool game_data: str diff --git a/src/servers/game_status/src/handlers/handlers.py b/src/servers/game_status/src/handlers/handlers.py index 6b9b41d8d..d3d2fd60e 100644 --- a/src/servers/game_status/src/handlers/handlers.py +++ b/src/servers/game_status/src/handlers/handlers.py @@ -1,10 +1,8 @@ -from servers.game_status.src.abstractions.contracts import RequestBase from servers.game_status.src.abstractions.handlers import CmdHandlerBase from servers.game_status.src.applications.client import Client from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest from servers.game_status.src.contracts.responses import AuthGameResponse, AuthPlayerResponse, GetPlayerDataResponse, GetProfileIdResponse from servers.game_status.src.contracts.results import AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult -from servers.game_status.src.exceptions.general import GSException class AuthGameHandler(CmdHandlerBase): diff --git a/src/servers/game_status/src/handlers/switcher.py b/src/servers/game_status/src/handlers/switcher.py index 3f61673c7..376c9a05a 100644 --- a/src/servers/game_status/src/handlers/switcher.py +++ b/src/servers/game_status/src/handlers/switcher.py @@ -1,3 +1,4 @@ +from typing import Optional, cast from library.src.abstractions.switcher import SwitcherBase from servers.game_status.src.abstractions.handlers import CmdHandlerBase from servers.game_status.src.applications.client import Client @@ -23,8 +24,8 @@ def _process_raw_request(self) -> None: name = raw_request.strip("\\").split("\\", 1)[0] self._requests.append((name, raw_request)) - def _create_cmd_handlers(self, name: object, raw_request: str) -> CmdHandlerBase | None: - + def _create_cmd_handlers(self, name: object, raw_request: str) -> Optional[CmdHandlerBase]: + self._client = cast(Client, self._client) match name: case "auth": return AuthGameHandler(self._client, AuthGameRequest(raw_request)) diff --git a/src/servers/game_status/tests/handler_tests.py b/src/servers/game_status/tests/handler_tests.py index 924b1a52a..182b65a88 100644 --- a/src/servers/game_status/tests/handler_tests.py +++ b/src/servers/game_status/tests/handler_tests.py @@ -1,4 +1,5 @@ +from typing import cast import unittest import responses @@ -14,7 +15,7 @@ class HandlerTests(unittest.TestCase): @responses.activate - @unittest.skip + @unittest.skip("skiped") def test_set_player_data_20230329(self): raw = "\\setpd\\\\pid\\1\\ptype\\1\\dindex\\0\\kv\\1\\lid\\2\\length\\111\\data\\\\report\\|title||victories|0|timestamp|37155|league|Team17|winner||crc|-1|player_0|spyguy|ip_0||pid_0|0|auth_0|[00]\\final\\" client = create_client() @@ -35,7 +36,7 @@ def test_set_player_data_20230329(self): handler = SetPlayerDataHandler(client, request) handler.handle() - @unittest.skip + @unittest.skip("skiped") def test_gamespysdk_update_game_20230329(self): raw1 = "\\updgame\\\\sesskey\\20298203\\connid\\0\\done\\0\\gamedata\\\u0001hostname\u0001My l33t Server\u0001mapname\u0001Level 33\u0001gametype\u0001hunter\u0001gamever\u00011.230000\u0001player_0\u0001Bob!\u0001points_0\u00014\u0001deaths_0\u00012\u0001pid_0\u000132432423\u0001auth_0\u00017cca8e60a13781eebc820a50754f57cd\u0001player_1\u0001Joey\u0001points_1\u00012\u0001deaths_1\u00014\u0001pid_1\u0001643423\u0001auth_1\u000119ea14d9d92a7fcc635cf5716944d9bc\\final\\" raw2 = "\\updgame\\\\sesskey\\20298203\\connid\\0\\done\\1\\gamedata\\\u0001hostname\u0001My l33t Server\u0001mapname\u0001Level 33\u0001gametype\u0001hunter\u0001gamever\u00011.230000\u0001player_0\u0001Bob!\u0001points_0\u00016\u0001deaths_0\u00013\u0001pid_0\u000132432423\u0001auth_0\u00017cca8e60a13781eebc820a50754f57cd\u0001player_1\u0001Joey\u0001points_1\u00013\u0001deaths_1\u00016\u0001pid_1\u0001643423\u0001auth_1\u000119ea14d9d92a7fcc635cf5716944d9bc\\final\\" @@ -43,14 +44,15 @@ def test_gamespysdk_update_game_20230329(self): create_mock_url(client, UpdateGameHandler, {"message": "ok"}) switcher = Switcher(client, raw1) switcher.handle() - request: UpdateGameRequest = switcher._handlers[0]._request + request: UpdateGameRequest = cast( + UpdateGameRequest, switcher._handlers[0]._request) response = switcher._handlers[0]._response self.assertEqual("20298203", request.session_key) self.assertEqual(1, request.connection_id) self.assertEqual(False, request.is_done) self.assertEqual("\u0001hostname\u0001My l33t Server\u0001mapname\u0001Level 33\u0001gametype\u0001hunter\u0001gamever\u00011.230000\u0001player_0\u0001Bob!\u0001points_0\u00014\u0001deaths_0\u00012\u0001pid_0\u000132432423\u0001auth_0\u00017cca8e60a13781eebc820a50754f57cd\u0001player_1\u0001Joey\u0001points_1\u00012\u0001deaths_1\u00014\u0001pid_1\u0001643423\u0001auth_1\u000119ea14d9d92a7fcc635cf5716944d9bc", request.game_data) - @unittest.skip + @unittest.skip("") def test_worm3d_auth_player(self): raw = "2\x0F\x16\x10]%+=veKaB3a(UC`b$\x1CO\x11VZX\x09w\x1Cu\x08L@\x13=X!\x1E{\x0EL\x1DLf[qN \x04G\x130[#N'\x09(IC`b$\\final\\" plaintext = GSCrypt().decrypt(raw.encode("ascii")) @@ -60,7 +62,7 @@ def test_worm3d_auth_player(self): handler = AuthPlayerHandler(client, request) handler.handle() - self.assertEqual() + # self.assertEqual() def test_auth(self): raw = "\\auth\\\\gamename\\crysis2\\response\\xxxxx\\port\\30\\id\\1\\final\\" diff --git a/src/servers/game_status/tests/mock_objects.py b/src/servers/game_status/tests/mock_objects.py index e79a20501..d53520ae9 100644 --- a/src/servers/game_status/tests/mock_objects.py +++ b/src/servers/game_status/tests/mock_objects.py @@ -1,3 +1,4 @@ +from typing import cast from library.src.configs import CONFIG from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock from servers.game_status.src.applications.client import Client @@ -8,7 +9,7 @@ class ClientMock(Client): pass -def create_client(): +def create_client() -> Client: handler = RequestHandlerMock() logger = LogMock() conn = ConnectionMock( @@ -16,4 +17,4 @@ def create_client(): config=CONFIG.servers["GameStatus"], t_client=ClientMock, logger=logger) - return conn._client + return cast(Client, conn._client) diff --git a/src/servers/natneg/src/applications/client.py b/src/servers/natneg/src/applications/client.py index ab253fb6a..d28cb2a38 100644 --- a/src/servers/natneg/src/applications/client.py +++ b/src/servers/natneg/src/applications/client.py @@ -5,7 +5,7 @@ class Client(ClientBase): - client_pool: dict[str, "Client"] = [] + client_pool: dict[str, "Client"] = {} def __init__(self, connection: UdpConnection, server_config: ServerConfig, logger: LogWriter): super().__init__(connection, server_config, logger) diff --git a/src/servers/natneg/src/applications/server_launcher.py b/src/servers/natneg/src/applications/server_launcher.py index 5d03c6554..ebae9d03b 100644 --- a/src/servers/natneg/src/applications/server_launcher.py +++ b/src/servers/natneg/src/applications/server_launcher.py @@ -12,7 +12,7 @@ def __init__(self) -> None: self.config = CONFIG.servers["NatNegotiation"] def _launch_server(self): - UdpServer(self.config, Client).start() + UdpServer(self.config, Client, self.logger).start() if __name__ == "__main__": diff --git a/src/servers/natneg/src/handlers/handlers.py b/src/servers/natneg/src/handlers/handlers.py index 4d0210e96..bd0c3aa89 100644 --- a/src/servers/natneg/src/handlers/handlers.py +++ b/src/servers/natneg/src/handlers/handlers.py @@ -27,6 +27,9 @@ class AddressCheckHandler(CmdHandlerBase): + _request: AddressCheckRequest + _result: AddressCheckResult + def __init__(self, client: Client, request: AddressCheckRequest) -> None: self._is_feaching = False super().__init__(client, request) @@ -72,6 +75,9 @@ def __init__(self, client: Client, request: ConnectRequest) -> None: class ErtAckHandler(CmdHandlerBase): + _request: ErtAckRequest + _result: ErtAckResult + def __init__(self, client: Client, request: ErtAckRequest) -> None: self._is_feaching = False super().__init__(client, request) @@ -90,6 +96,8 @@ class InitHandler(CmdHandlerBase): """ In init process, we need response the initresponse first to make client not timeout """ + _request: InitRequest + _result: InitResult def __init__(self, client: Client, request: InitRequest) -> None: self._is_feaching = False @@ -111,6 +119,9 @@ def _response_send(self) -> None: class NatifyHandler(CmdHandlerBase): + _request: NatifyRequest + _result: NatifyResult + def __init__(self, client: Client, request: NatifyRequest) -> None: self._is_feaching = False super().__init__(client, request) diff --git a/src/servers/natneg/tests/mock_objects.py b/src/servers/natneg/tests/mock_objects.py index 8d760874d..0e813a8b1 100644 --- a/src/servers/natneg/tests/mock_objects.py +++ b/src/servers/natneg/tests/mock_objects.py @@ -1,14 +1,15 @@ from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock from library.src.configs import CONFIG from servers.natneg.src.applications.client import Client +from typing import cast class ClientMock(Client): - + pass -def create_client(): +def create_client() -> Client: handler = RequestHandlerMock() logger = LogMock() conn = ConnectionMock( @@ -16,4 +17,4 @@ def create_client(): config=CONFIG.servers["NatNegotiation"], t_client=ClientMock, logger=logger) - return conn._client + return cast(Client, conn._client) diff --git a/src/servers/presence_connection_manager/src/aggregates/user_status.py b/src/servers/presence_connection_manager/src/aggregates/user_status.py index 4d21ca870..2319df5e2 100644 --- a/src/servers/presence_connection_manager/src/aggregates/user_status.py +++ b/src/servers/presence_connection_manager/src/aggregates/user_status.py @@ -1,10 +1,10 @@ -from typing import Union -from pydantic import BaseModel +from dataclasses import dataclass from servers.presence_connection_manager.src.enums.general import GPStatusCode -class UserStatus(BaseModel): - status_string: str = None - location_string: str = None - current_status: Union[GPStatusCode, int] = GPStatusCode.OFFLINE +@dataclass +class UserStatus: + status_string: str + location_string: str + current_status: GPStatusCode = GPStatusCode.OFFLINE diff --git a/src/servers/presence_connection_manager/src/applications/server_launcher.py b/src/servers/presence_connection_manager/src/applications/server_launcher.py index 572c36271..f3c37db29 100644 --- a/src/servers/presence_connection_manager/src/applications/server_launcher.py +++ b/src/servers/presence_connection_manager/src/applications/server_launcher.py @@ -11,7 +11,7 @@ def __init__(self) -> None: self.config = CONFIG.servers["PresenceConnectionManager"] def _launch_server(self): - UdpServer(self.config, Client).start() + UdpServer(self.config, Client, self.logger).start() if __name__ == "__main__": diff --git a/src/servers/presence_connection_manager/src/contracts/requests/buddy.py b/src/servers/presence_connection_manager/src/contracts/requests/buddy.py index 1d9932b13..f1bb9478a 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests/buddy.py +++ b/src/servers/presence_connection_manager/src/contracts/requests/buddy.py @@ -75,9 +75,9 @@ def parse(self): @final class StatusInfoRequest(RequestBase): - namespace_id: Optional[int] = None + namespace_id: Optional[int] status_info: UserStatusInfo - profile_id: int = 0 + profile_id: int def __init__(self, raw_request: Optional[str] = None) -> None: if raw_request is not None: @@ -139,9 +139,7 @@ def parse(self): try: status_code = int(self.request_dict["status"]) - self.status = UserStatus(current_status=GPStatusCode(status_code)) + self.status = UserStatus( + location_string=self.request_dict["locstring"], status_string=self.request_dict['statstring'], current_status=GPStatusCode(status_code)) except ValueError: raise GPParseException("status format is incorrect.") - - self.status.location_string = self.request_dict["locstring"] - self.status.status_string = self.request_dict["statstring"] diff --git a/src/servers/presence_connection_manager/src/contracts/responses/profile.py b/src/servers/presence_connection_manager/src/contracts/responses/profile.py index 61a29edcc..cc658fb50 100644 --- a/src/servers/presence_connection_manager/src/contracts/responses/profile.py +++ b/src/servers/presence_connection_manager/src/contracts/responses/profile.py @@ -39,8 +39,8 @@ def build(self): birth_str = ( (self._result.user_profile.birthday << 24) - | (self._result.user_profile.birthmonth << 16) - | self._result.user_profile.birthyear + or (self._result.user_profile.birthmonth << 16) + or self._result.user_profile.birthyear ) self.sending_buffer += ( f"\\birthday\\{birth_str}" @@ -71,7 +71,9 @@ def __init__(self, request: NewProfileRequest, result: NewProfileResult): super().__init__(request, result) def build(self): + # fmt: off self.sending_buffer = f"\\npr\\\\profileid\\{self.sending_buffer}\\id\\{self._request.operation_id}\\final\\" + # fmt: on class RegisterCDKeyResposne(ResponseBase): @@ -90,4 +92,6 @@ def __init__(self, request: RegisterNickRequest) -> None: self._request = request def build(self) -> None: - self.sending_buffer = f"\\rn\\\\id\\{self._request.operation_id}\\final\\" + # fmt: off + self.sending_buffer = f"\\rn\\\\id\\{self._request.operation_id}\\final\\" + # fmt: on diff --git a/src/servers/presence_connection_manager/src/contracts/results/profile.py b/src/servers/presence_connection_manager/src/contracts/results/profile.py index e21aafe66..378d9da26 100644 --- a/src/servers/presence_connection_manager/src/contracts/results/profile.py +++ b/src/servers/presence_connection_manager/src/contracts/results/profile.py @@ -5,38 +5,38 @@ class GetProfileDataModel(BaseModel): - nick: Optional[str] = None - profile_id: Optional[int] = None - unique_nick: Optional[str] = None - email: Optional[str] = None - firstname: Optional[str] = None - lastname: Optional[str] = None - icquin: Optional[int] = None - homepage: Optional[str] = None - zipcode: Optional[str] = None - countrycode: Optional[str] = None - longitude: Optional[float] = None - latitude: Optional[float] = None - location: Optional[str] = None - birthday: Optional[int] = None - birthmonth: Optional[int] = None - birthyear: Optional[int] = None - sex: Optional[int] = None - publicmask: Optional[int] = None - aim: Optional[str] = None - picture: Optional[int] = None - occupationid: Optional[int] = None - industryid: Optional[int] = None - incomeid: Optional[int] = None - marriedid: Optional[int] = None - childcount: Optional[int] = None - interests1: Optional[int] = None - ownership1: Optional[int] = None - connectiontype: Optional[int] = None + nick: str + profile_id: int + unique_nick: str + email: str + firstname: str + lastname: str + icquin: int + homepage: str + zipcode: str + countrycode: str + longitude: float + latitude: float + location: str + birthday: int + birthmonth: int + birthyear: int + sex: int + publicmask: int + aim: str + picture: int + occupationid: int + industryid: int + incomeid: int + marriedid: int + childcount: int + interests1: int + ownership1: int + connectiontype: int class GetProfileResult(ResultBase): - user_profile: Optional[GetProfileDataModel] = None + user_profile: GetProfileDataModel class NewProfileResult(ResultBase): diff --git a/src/servers/presence_connection_manager/src/handlers/buddy.py b/src/servers/presence_connection_manager/src/handlers/buddy.py index 30de4e86a..a3cf08c7b 100644 --- a/src/servers/presence_connection_manager/src/handlers/buddy.py +++ b/src/servers/presence_connection_manager/src/handlers/buddy.py @@ -54,7 +54,7 @@ def handle_status_info(self, profile_id): request = StatusInfoRequest() request.profile_id = profile_id request.namespace_id = int(self._client.info.namespace_id) - request.is_get_status_info = True + # request.is_get_status_info = True StatusInfoHandler(self._client, request).handle() @@ -65,7 +65,7 @@ def _response_send(self) -> None: return with Pool() as pool: - pool.map(self.handle_status_info, self._result.profile_id_list) + pool.map(self.handle_status_info, self._result.profile_ids) class BuddyStatusInfoHandler(CmdHandlerBase): @@ -118,6 +118,6 @@ def __init__(self, client: Client, request: StatusInfoRequest) -> None: super().__init__(client, request) def _response_send(self) -> None: - if self._request.is_get_status_info: + if self._request is not None: self._response = StatusInfoResponse(self._request, self._result) - super()._response_send() + super()._response_send() diff --git a/src/servers/presence_connection_manager/src/handlers/general.py b/src/servers/presence_connection_manager/src/handlers/general.py index 7f7a4346e..98ef2b566 100644 --- a/src/servers/presence_connection_manager/src/handlers/general.py +++ b/src/servers/presence_connection_manager/src/handlers/general.py @@ -6,6 +6,7 @@ KeepAliveRequest, LoginRequest, LogoutRequest, + # NewUserRequest, ) from servers.presence_connection_manager.src.contracts.responses.general import ( KeepAliveResponse, @@ -16,7 +17,9 @@ BlockListHandler, BuddyListHandler, ) +from servers.presence_search_player.src.contracts.requests import NewUserRequest from servers.presence_search_player.src.contracts.responses import NewUserResponse +from servers.presence_search_player.src.contracts.results import NewUserResult import servers.presence_search_player.src.handlers.handlers if TYPE_CHECKING: @@ -24,6 +27,8 @@ class KeepAliveHandler(servers.presence_connection_manager.src.abstractions.handlers.CmdHandlerBase): + _request: KeepAliveRequest + def __init__(self, client: "Client", request: KeepAliveRequest) -> None: assert isinstance(request, KeepAliveRequest) super().__init__(client, request) @@ -35,11 +40,13 @@ def _response_construct(self) -> None: class LoginHandler(servers.presence_connection_manager.src.abstractions.handlers.CmdHandlerBase): _request: LoginRequest - _result_cls: type[LoginResult] = LoginResult + _result_cls: type[LoginResult] + _result: LoginResult def __init__(self, client: "Client", request: LoginRequest) -> None: assert isinstance(request, LoginRequest) super().__init__(client, request) + self._result_cls = LoginResult def _response_construct(self) -> None: self._response = LoginResponse(self._request, self._result) @@ -53,10 +60,16 @@ def __init__(self, client: "Client", request: LogoutRequest) -> None: super().__init__(client, request) -class NewUserHandler(servers.presence_search_player.src.handlers.handlers.NewUserHandler): + +# todo create new handler +class NewUserHandler(servers.presence_connection_manager.src.abstractions.handlers.CmdHandlerBase): + _request: NewUserRequest + _result_cls: type[NewUserResult] + _result: NewUserResult + # todo create seperate request and result def _response_construct(self): - self._response = NewUserResponse(self._request, self._response) + self._response = NewUserResponse(self._request, self._result) class SdkRevisionHandler(servers.presence_connection_manager.src.abstractions.handlers.CmdHandlerBase): diff --git a/src/servers/presence_connection_manager/src/handlers/profile.py b/src/servers/presence_connection_manager/src/handlers/profile.py index 19e2ffcb2..3d22d09d0 100644 --- a/src/servers/presence_connection_manager/src/handlers/profile.py +++ b/src/servers/presence_connection_manager/src/handlers/profile.py @@ -15,7 +15,8 @@ NewProfileResponse, RegisterNickResponse, ) -from servers.presence_connection_manager.src.contracts.results.profile import NewProfileResult +from servers.presence_connection_manager.src.contracts.results.profile import GetProfileResult, NewProfileResult + @final class AddBlockHandler(CmdHandlerBase): @@ -29,7 +30,7 @@ def __init__(self, client: Client, request: AddBlockRequest) -> None: @final class GetProfileHandler(CmdHandlerBase): _request: GetProfileRequest - _result: GetProfileResponse + _result: GetProfileResult def __init__(self, client: Client, request: GetProfileRequest) -> None: assert isinstance(request, GetProfileRequest) @@ -64,13 +65,12 @@ def __init__(self, client: Client, request: RegisterCDKeyRequest) -> None: @final class RegisterNickHandler(CmdHandlerBase): _request: RegisterNickRequest - def __init__(self, client: Client, request: RegisterNickRequest) -> None: assert isinstance(request, RegisterNickRequest) super().__init__(client, request) def _response_construct(self) -> None: - self._response = RegisterNickResponse(self._request, self._result) + self._response = RegisterNickResponse(self._request) @final diff --git a/src/servers/presence_connection_manager/src/handlers/switcher.py b/src/servers/presence_connection_manager/src/handlers/switcher.py index f859bfd9b..544027b95 100644 --- a/src/servers/presence_connection_manager/src/handlers/switcher.py +++ b/src/servers/presence_connection_manager/src/handlers/switcher.py @@ -5,11 +5,11 @@ from servers.presence_connection_manager.src.handlers.buddy import StatusHandler, StatusInfoHandler from servers.presence_connection_manager.src.handlers.general import KeepAliveHandler, LoginHandler, LogoutHandler, NewUserHandler from servers.presence_connection_manager.src.handlers.profile import AddBlockHandler, GetProfileHandler, NewProfileHandler, RegisterCDKeyHandler, RegisterNickHandler, UpdateProfileHandler -from servers.presence_search_player.src.contracts.requests import NewUserRequest +from servers.presence_connection_manager.src.contracts.requests import NewUserRequest from servers.presence_search_player.src.exceptions.general import GPParseException -from servers.presence_search_player.src.abstractions.handler import CmdHandlerBase -from typing import Optional +from servers.presence_connection_manager.src.abstractions.handlers import CmdHandlerBase +from typing import TYPE_CHECKING, Optional, cast from servers.presence_connection_manager.src.applications.client import Client @@ -24,12 +24,15 @@ def __init__(self, client: Client, raw_request: str) -> None: def _process_raw_request(self) -> None: if self._raw_request[0] != "\\": raise GPParseException("Request format is invalid") - raw_requests = [r+"\\final\\" for r in self._raw_request.split("\\final\\") if r] + raw_requests = [ + r+"\\final\\" for r in self._raw_request.split("\\final\\") if r] for raw_request in raw_requests: name = raw_request.strip("\\").split("\\")[0] self._requests.append((name, raw_request)) def _create_cmd_handlers(self, name: str, raw_request: str) -> Optional[CmdHandlerBase]: + if TYPE_CHECKING: + self._client = cast(Client, self._client) match name: case "ka": return KeepAliveHandler(self._client, KeepAliveRequest(raw_request)) diff --git a/src/servers/presence_search_player/src/applications/server_launcher.py b/src/servers/presence_search_player/src/applications/server_launcher.py index 0d3894d34..26cdc60f0 100644 --- a/src/servers/presence_search_player/src/applications/server_launcher.py +++ b/src/servers/presence_search_player/src/applications/server_launcher.py @@ -11,7 +11,7 @@ def __init__(self) -> None: self.config = CONFIG.servers["PresenceSearchPlayer"] def _launch_server(self): - TcpServer(self.config, Client).start() + TcpServer(self.config, Client, self.logger).start() if __name__ == "__main__": diff --git a/src/servers/presence_search_player/src/contracts/requests.py b/src/servers/presence_search_player/src/contracts/requests.py index c1ab7cc5e..7dfef098b 100644 --- a/src/servers/presence_search_player/src/contracts/requests.py +++ b/src/servers/presence_search_player/src/contracts/requests.py @@ -133,13 +133,13 @@ def parse(self) -> None: self.profile_ids = [ int(opid) for opid in self.request_dict["opids"].strip("|").split("|") ] - except Exception as e: - raise GPParseException("opids is incorrect", e) + except: + raise GPParseException("opids is incorrect") class OthersRequest(RequestBase): - profile_id: int = None - game_name: int + profile_id: int + game_name: str def parse(self): super().parse() diff --git a/src/servers/presence_search_player/src/contracts/responses.py b/src/servers/presence_search_player/src/contracts/responses.py index dbc13b577..5bdb52f76 100644 --- a/src/servers/presence_search_player/src/contracts/responses.py +++ b/src/servers/presence_search_player/src/contracts/responses.py @@ -74,7 +74,7 @@ class OthersListResponse(ResponseBase): def __init__(self, result: OthersListResult) -> None: assert isinstance(result, OthersListResult) - super().__init__(None, result) + self._result = result def build(self): self.sending_buffer = "\\otherslist\\" @@ -89,7 +89,7 @@ class OthersResponse(ResponseBase): def __init__(self, result: OthersResult) -> None: assert isinstance(result, OthersResult) - super().__init__(None, result) + self._result = result def build(self): self.sending_buffer = "\\others\\" @@ -108,7 +108,7 @@ class SearchResponse(ResponseBase): def __init__(self, result: SearchResult) -> None: assert isinstance(result, SearchResult) - super().__init__(None, result) + self._result = result def build(self): self.sending_buffer = f"\\bsr\\" @@ -128,7 +128,7 @@ class SearchUniqueResponse(ResponseBase): def __init__(self, result: SearchUniqueResult) -> None: assert isinstance(result, SearchUniqueResult) - super().__init__(None, result) + self._result = result def build(self): self.sending_buffer = "\\bsr" diff --git a/src/servers/presence_search_player/src/exceptions/general.py b/src/servers/presence_search_player/src/exceptions/general.py index 590595180..744e79a4b 100644 --- a/src/servers/presence_search_player/src/exceptions/general.py +++ b/src/servers/presence_search_player/src/exceptions/general.py @@ -15,8 +15,9 @@ def __init__( UniSpyException.__init__(self, message) self.error_code = error_code - def build(self) -> str: - self.sending_buffer = f"\\error\\\\err\\{int(self.error_code)}\\fatal\\\\errmsg\\{self.message}\\final\\" + def build(self) -> None: + self.sending_buffer = f"\\error\\\\err\\{int(self.error_code)}\\fatal\\\\errmsg\\{ + self.message}\\final\\" class GPParseException(GPException): @@ -189,7 +190,7 @@ def __init__( ) -> None: super().__init__(message, error_code) - def build(self) -> str: + def build(self) -> None: self.sending_buffer = f"\\cur\\{int(self.error_code)}\\final\\" @@ -428,6 +429,6 @@ def __init__( MAPPING = { - "GPException":GPException, - + "GPException": GPException, + } diff --git a/src/servers/presence_search_player/src/handlers/handlers.py b/src/servers/presence_search_player/src/handlers/handlers.py index 69bbf90c7..74d27c29f 100644 --- a/src/servers/presence_search_player/src/handlers/handlers.py +++ b/src/servers/presence_search_player/src/handlers/handlers.py @@ -9,33 +9,42 @@ class CheckHandler(CmdHandlerBase): - _result_cls: type[CheckResult] = CheckResult + _result_cls: type[CheckResult] + _request: CheckRequest + _result: CheckResult def __init__(self, client: Client, request: CheckRequest) -> None: assert isinstance(request, CheckRequest) super().__init__(client, request) + self._result_cls = CheckResult def _response_construct(self): self._response = CheckResponse(self._request, self._result) class NewUserHandler(CmdHandlerBase): - _result_cls: type[NewUserResult] = NewUserResult + _result_cls: type[NewUserResult] + _request: NewUserRequest + _result: NewUserResult def __init__(self, client: Client, request: NewUserRequest) -> None: assert isinstance(request, NewUserRequest) super().__init__(client, request) + self._result_cls = NewUserResult def _response_construct(self): self._response = NewUserResponse(self._request, self._result) class NicksHandler(CmdHandlerBase): - _result_cls: type[NicksResult] = NicksResult + _result_cls: type[NicksResult] + _request: NicksRequest + _result: NicksResult def __init__(self, client: Client, request: NicksRequest) -> None: assert isinstance(request, NicksRequest) super().__init__(client, request) + self._result_cls = NicksResult def _response_construct(self): self._response = NicksResponse(self._request, self._result) @@ -43,22 +52,26 @@ def _response_construct(self): class OthersHandler(CmdHandlerBase): _request: OthersRequest - _result_cls: type[OthersResult] = OthersResult + _result_cls: type[OthersResult] + _result: OthersResult def __init__(self, client: Client, request: OthersRequest) -> None: assert isinstance(request, OthersRequest) super().__init__(client, request) + self._result_cls = OthersResult def _response_construct(self): self._response = OthersResponse(self._result) class OthersListHandler(CmdHandlerBase): - _result_cls: type[OthersListResult] = OthersListResult + _result_cls: type[OthersListResult] + _result: OthersListResult def __init__(self, client: Client, request: OthersListRequest) -> None: assert isinstance(request, OthersListRequest) super().__init__(client, request) + self._result_cls = OthersListResult def _response_construct(self): self._response = OthersListResponse(self._result) @@ -76,42 +89,54 @@ class SearchHandler(CmdHandlerBase): \\more\\\\final\\ \\search\\sesskey\\0\\profileid\\0\\namespaceid\\0\\nick\\gbr359_jordips\\gamename\\gbrome\\final\\ """ - _result_cls: type[SearchResult] = SearchResult + _result_cls: type[SearchResult] + _result: SearchResult def __init__(self, client: Client, request: SearchRequest) -> None: assert isinstance(request, SearchRequest) super().__init__(client, request) + self._result_cls = SearchResult def _response_construct(self): self._response = SearchResponse(self._result) class SearchUniqueHandler(CmdHandlerBase): - _result_cls: type[SearchUniqueResult] = SearchUniqueResult + _result_cls: type[SearchUniqueResult] + _result: SearchUniqueResult def __init__(self, client: Client, request: SearchUniqueRequest) -> None: assert isinstance(request, SearchUniqueRequest) super().__init__(client, request) + self._result_cls = SearchUniqueResult def _response_construct(self): self._response = SearchUniqueResponse(self._result) class UniqueSearchHandler(CmdHandlerBase): - _result_cls: type[UniqueSearchResult] = UniqueSearchResult + _result_cls: type[UniqueSearchResult] + _request: UniqueSearchRequest + _result: UniqueSearchResult + def __init__(self, client: Client, request: UniqueSearchRequest) -> None: assert isinstance(request, UniqueSearchRequest) super().__init__(client, request) + self._result_cls = UniqueSearchResult def _response_construct(self): self._response = UniqueSearchResponse(self._request, self._result) class ValidHandler(CmdHandlerBase): - _result_cls: type[ValidResult] = ValidResult + _result_cls: type[ValidResult] + _result: ValidResult + _request: ValidRequest + def __init__(self, client: Client, request: ValidRequest) -> None: assert isinstance(request, ValidRequest) super().__init__(client, request) + self._result_cls = ValidResult def _response_construct(self): self._response = ValidResponse(self._request, self._result) diff --git a/src/servers/presence_search_player/src/handlers/switcher.py b/src/servers/presence_search_player/src/handlers/switcher.py index f664379f3..2a1aa4c12 100644 --- a/src/servers/presence_search_player/src/handlers/switcher.py +++ b/src/servers/presence_search_player/src/handlers/switcher.py @@ -1,4 +1,4 @@ -from typing import Optional +from typing import TYPE_CHECKING, Optional, cast from library.src.abstractions.switcher import SwitcherBase from servers.presence_search_player.src.contracts.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest @@ -10,6 +10,7 @@ class CmdSwitcher(SwitcherBase): + _raw_request: str def __init__(self, client: Client, raw_request: str): super().__init__(client, raw_request) @@ -27,6 +28,9 @@ def _process_raw_request(self): self._requests.append((name, raw_request)) def _create_cmd_handlers(self, name: str, raw_request: str) -> Optional[CmdHandlerBase]: + if TYPE_CHECKING: + self._client = cast(Client, self._client) + match name: case "check": return CheckHandler(self._client, CheckRequest(raw_request)) diff --git a/src/servers/presence_search_player/tests/game_tests.py b/src/servers/presence_search_player/tests/game_tests.py index 76ed80c79..06634d102 100644 --- a/src/servers/presence_search_player/tests/game_tests.py +++ b/src/servers/presence_search_player/tests/game_tests.py @@ -1,3 +1,4 @@ +from typing import cast import unittest from library.src.extentions.password_encoder import process_password @@ -19,7 +20,8 @@ def test_check(self): switcher = CmdSwitcher(client, raw) switcher.handle() - request: CheckRequest = switcher._handlers[0]._request + request: CheckRequest = cast( + CheckRequest, switcher._handlers[0]._request) response = switcher._handlers[0]._response self.assertEqual("spyguy", request.nick) self.assertEqual("spyguy@gamespy.com", request.email) diff --git a/src/servers/presence_search_player/tests/mock_objects.py b/src/servers/presence_search_player/tests/mock_objects.py index 5a51a7028..782e24bda 100644 --- a/src/servers/presence_search_player/tests/mock_objects.py +++ b/src/servers/presence_search_player/tests/mock_objects.py @@ -1,3 +1,4 @@ +from typing import cast from library.src.configs import CONFIG from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock from servers.presence_search_player.src.applications.client import Client @@ -8,7 +9,7 @@ class ClientMock(Client): pass -def create_client(): +def create_client() -> Client: handler = RequestHandlerMock() logger = LogMock() conn = ConnectionMock( @@ -16,4 +17,4 @@ def create_client(): config=CONFIG.servers["PresenceSearchPlayer"], t_client=ClientMock, logger=logger) - return conn._client + return cast(Client, conn._client) diff --git a/src/servers/query_report/src/aggregates/peer_room_info.py b/src/servers/query_report/src/aggregates/peer_room_info.py index 3e2933d16..5014ddb8c 100644 --- a/src/servers/query_report/src/aggregates/peer_room_info.py +++ b/src/servers/query_report/src/aggregates/peer_room_info.py @@ -1,7 +1,7 @@ from uuid import UUID -{ +dd = { "groupid": "groupid", "hostname": "hostname", "number_of_waiting_player": "numwaiting", @@ -20,8 +20,8 @@ class PeerRoomInfo: game_name: str group_id: int room_name: str - - raw_key_values: dict[str, str] + # todo change to dict[str, object] + raw_key_values: dict def __init__(self, game_name, group_id, room_name) -> None: self.game_name = game_name diff --git a/src/servers/query_report/src/applications/server_launcher.py b/src/servers/query_report/src/applications/server_launcher.py index f50f9462a..7cad2e97d 100644 --- a/src/servers/query_report/src/applications/server_launcher.py +++ b/src/servers/query_report/src/applications/server_launcher.py @@ -11,7 +11,7 @@ def __init__(self) -> None: self.config = CONFIG.servers["QueryReport"] def _launch_server(self): - UdpServer(self.config, Client).start() + UdpServer(self.config, Client, self.logger).start() if __name__ == "__main__": diff --git a/src/servers/query_report/src/v1/abstractions/contracts.py b/src/servers/query_report/src/v1/abstractions/contracts.py index c0011c9c4..5c222f59f 100644 --- a/src/servers/query_report/src/v1/abstractions/contracts.py +++ b/src/servers/query_report/src/v1/abstractions/contracts.py @@ -5,15 +5,17 @@ class RequestBase(library.src.abstractions.contracts.RequestBase): - request_dict: dict[str, str] = {} + request_dict: dict[str, str] + raw_request: str def __init__(self, raw_request: str) -> None: assert isinstance(raw_request, str) super().__init__(raw_request) + self.request_dict = {} def parse(self) -> None: self.request_dict = convert_to_key_value(self.raw_request) - self.command_name = self.request_dict.keys()[0] + self.command_name = list(self.request_dict.keys())[0] class ResultBase(library.src.abstractions.contracts.ResultBase): diff --git a/src/servers/query_report/src/v2/abstractions/contracts.py b/src/servers/query_report/src/v2/abstractions/contracts.py index 8f5a7a778..db577a0ac 100644 --- a/src/servers/query_report/src/v2/abstractions/contracts.py +++ b/src/servers/query_report/src/v2/abstractions/contracts.py @@ -1,3 +1,4 @@ +from servers.query_report.src.v2.enums.general import PacketType import library.src.abstractions.contracts from servers.query_report.src.exceptions.exceptions import QRException from servers.query_report.src.v2.enums.general import RequestType @@ -21,19 +22,10 @@ def parse(self): self.instant_key = int(self.raw_request[1:5]) -import abc -import library.src.abstractions.contracts -from servers.query_report.src.v2.enums.general import PacketType - - class ResultBase(library.src.abstractions.contracts.ResultBase): packet_type: PacketType -import abc -import library.src.abstractions.contracts - - class ResponseBase(library.src.abstractions.contracts.ResponseBase): _result: ResultBase _request: RequestBase diff --git a/src/servers/query_report/src/v2/abstractions/contrancts.py b/src/servers/query_report/src/v2/abstractions/contrancts.py deleted file mode 100644 index dc8dd0b32..000000000 --- a/src/servers/query_report/src/v2/abstractions/contrancts.py +++ /dev/null @@ -1,41 +0,0 @@ -import library.src.abstractions.contracts -from servers.query_report.src.exceptions.exceptions import QRException -from servers.query_report.src.v2.enums.general import RequestType - -MAGIC_DATA = [0xFE, 0xFD] - - -class RequestBase(library.src.abstractions.contracts.RequestBase): - instant_key: int - command_name: RequestType - raw_request: bytes - - def __init__(self, raw_request: bytes) -> None: - assert isinstance(raw_request, bytes) - super().__init__(raw_request) - - def parse(self): - if len(self.raw_request) < 3: - raise QRException - self.command_name = RequestType(self.raw_request[0]) - self.instant_key = int(self.raw_request[1:5]) - - - -import abc -import library.src.abstractions.contracts -from servers.query_report.src.v2.enums.general import PacketType - - -class ResultBase(library.src.abstractions.contracts.ResultBase, abc.ABC): - packet_type: PacketType = None - - - -import abc -import library.src.abstractions.contracts - - -class ResponseBase(library.src.abstractions.contracts.ResponseBase, abc.ABC): - _result: ResultBase - _request: RequestBase diff --git a/src/servers/query_report/src/v2/aggregates/game_server_info_v2.py b/src/servers/query_report/src/v2/aggregates/game_server_info_v2.py index 182cfcac2..ef1d25b35 100644 --- a/src/servers/query_report/src/v2/aggregates/game_server_info_v2.py +++ b/src/servers/query_report/src/v2/aggregates/game_server_info_v2.py @@ -11,7 +11,7 @@ DictField, ) -from library.src.database.mongodb_orm import connect_to_db, get_ttl_param +# from library.src.database.mongodb_orm import connect_to_db, get_ttl_param from servers.query_report.src.v2.enums.general import GameServerStatus diff --git a/src/servers/query_report/src/v2/contracts/requests.py b/src/servers/query_report/src/v2/contracts/requests.py index fff8bcb0e..5c5c0293b 100644 --- a/src/servers/query_report/src/v2/contracts/requests.py +++ b/src/servers/query_report/src/v2/contracts/requests.py @@ -109,10 +109,6 @@ def parse_server_data(self, server_data_str: str): temp_key = key_value_array[i] temp_value = key_value_array[i + 1] - if temp_key == "": - LogWriter.debug("Skipping empty key value") - continue - if temp_key in self.server_data: self.server_data[temp_key] = temp_value else: diff --git a/src/servers/query_report/src/v2/handlers/handlers.py b/src/servers/query_report/src/v2/handlers/handlers.py index ffe0d6f12..30034927f 100644 --- a/src/servers/query_report/src/v2/handlers/handlers.py +++ b/src/servers/query_report/src/v2/handlers/handlers.py @@ -1,6 +1,4 @@ -from servers.presence_connection_manager.src.contracts.requests.general import ( - KeepAliveRequest, -) + from servers.query_report.src.applications.client import Client from servers.query_report.src.v2.abstractions.cmd_handler_base import CmdHandlerBase from servers.query_report.src.v2.contracts.requests import ( @@ -10,6 +8,7 @@ ClientMessageRequest, EchoRequest, HeartBeatRequest, + KeepAliveRequest, ) from servers.query_report.src.v2.contracts.responses import ( AvaliableResponse, @@ -17,7 +16,7 @@ ClientMessageResponse, HeartBeatResponse, ) -from servers.query_report.src.v2.contracts.results import ChallengeResult +from servers.query_report.src.v2.contracts.results import ChallengeResult, HeartBeatResult class AvailableHandler(CmdHandlerBase): @@ -53,6 +52,8 @@ def _response_construct(self): class ClientMessageHandler(CmdHandlerBase): + _request: ClientMessageRequest + def __init__(self, client: Client, request: ClientMessageRequest) -> None: assert isinstance(request, ClientMessageRequest) super().__init__(client, request) @@ -68,15 +69,22 @@ def __init__(self, client: Client, request: EchoRequest) -> None: class HeartBeatHandler(CmdHandlerBase): + _request: HeartBeatRequest + _result: HeartBeatResult + _result_cls: type[HeartBeatResult] + def __init__(self, client: Client, request: HeartBeatRequest) -> None: assert isinstance(request, HeartBeatRequest) super().__init__(client, request) + self._result_cls = HeartBeatResult def _response_construct(self) -> None: self._response = HeartBeatResponse(self._request, self._result) class KeepAliveHandler(CmdHandlerBase): + _request: KeepAliveRequest + def __init__(self, client: Client, request: KeepAliveRequest) -> None: assert isinstance(request, KeepAliveRequest) super().__init__(client, request) diff --git a/src/servers/query_report/src/v2/handlers/switcher.py b/src/servers/query_report/src/v2/handlers/switcher.py index a34e3ed7f..bf892078e 100644 --- a/src/servers/query_report/src/v2/handlers/switcher.py +++ b/src/servers/query_report/src/v2/handlers/switcher.py @@ -1,17 +1,17 @@ +from typing import TYPE_CHECKING, Optional, cast from library.src.abstractions.switcher import SwitcherBase from library.src.exceptions.general import UniSpyException -from servers.presence_connection_manager.src.contracts.requests.general import ( - KeepAliveRequest, -) from servers.query_report.src.applications.client import Client from servers.query_report.src.v2.abstractions.cmd_handler_base import CmdHandlerBase from servers.query_report.src.v2.contracts.requests import ( AvaliableRequest, ChallengeRequest, + ClientMessageAckRequest, ClientMessageRequest, EchoRequest, HeartBeatRequest, + KeepAliveRequest, ) from servers.query_report.src.v2.enums.general import RequestType from servers.query_report.src.v2.handlers.handlers import ( @@ -25,6 +25,8 @@ class CmdSwitcher(SwitcherBase): + _raw_request: bytes + def __init__(self, client: Client, raw_request: bytes): super().__init__(client, raw_request) @@ -36,8 +38,10 @@ def _process_raw_request(self) -> None: raw_request = self._raw_request self._requests.append((name, raw_request)) - def _create_cmd_handlers(self, name: int, raw_request: bytes) -> CmdHandlerBase: + def _create_cmd_handlers(self, name: int, raw_request: bytes) -> Optional[CmdHandlerBase]: req = raw_request + if TYPE_CHECKING: + self._client = cast(Client, self._client) match name: case RequestType.HEARTBEAT: return HeartBeatHandler(self._client, HeartBeatRequest(req)) @@ -46,7 +50,7 @@ def _create_cmd_handlers(self, name: int, raw_request: bytes) -> CmdHandlerBase: case RequestType.AVALIABLE_CHECK: return AvailableHandler(self._client, AvaliableRequest(req)) case RequestType.CLIENT_MESSAGE_ACK: - return ClientMessageAckHandler(self._client, ClientMessageRequest(req)) + return ClientMessageAckHandler(self._client, ClientMessageAckRequest(req)) case RequestType.ECHO: return EchoHandler(self._client, EchoRequest(req)) case RequestType.KEEP_ALIVE: From 0fd9fc38112245831c6c1313200aae8fc6072c8f Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 23 Oct 2024 08:37:59 +0000 Subject: [PATCH 119/231] fix(web): type hint errors --- src/library/src/configs.py | 4 +- .../src/aggregations/soap_envelop.py | 12 +++- .../src/applications/server_launcher.py | 4 +- .../web_services/src/exceptions/general.py | 2 +- .../web_services/src/handlers/switcher.py | 22 ++++--- .../src/modules/auth/contracts/requests.py | 30 ++++----- .../src/modules/auth/exceptions/general.py | 4 +- .../src/modules/auth/handlers/general.py | 14 ++-- .../modules/direct2game/contracts/requests.py | 64 +++++++++++-------- .../src/modules/sake/abstractions/general.py | 12 ++-- .../src/modules/sake/contracts/requests.py | 33 +++++----- .../src/modules/sake/exceptions/general.py | 4 +- 12 files changed, 118 insertions(+), 87 deletions(-) diff --git a/src/library/src/configs.py b/src/library/src/configs.py index 6ebb77544..1a79c053f 100644 --- a/src/library/src/configs.py +++ b/src/library/src/configs.py @@ -3,7 +3,7 @@ from typing import Literal, Optional from uuid import UUID -from library.src.exceptions.general import UniSpyException +# from library.src.exceptions.general import UniSpyException @dataclass @@ -112,7 +112,7 @@ def __post_init__(self): unispy_config = os.environ.get("UNISPY_CONFIG") if unispy_config is None: - raise UniSpyException( + raise Exception( "Unispy server config not found, you should set the UNISPY_CONFIG in the system enviroment." ) with open(unispy_config, "r") as f: diff --git a/src/servers/web_services/src/aggregations/soap_envelop.py b/src/servers/web_services/src/aggregations/soap_envelop.py index 84381236f..ce78d93da 100644 --- a/src/servers/web_services/src/aggregations/soap_envelop.py +++ b/src/servers/web_services/src/aggregations/soap_envelop.py @@ -1,5 +1,7 @@ import xml.etree.ElementTree as ET +from servers.web_services.src.exceptions.general import WebException + class SoapEnvelop: soap_envelop_namespace = "http://schemas.xmlsoap.org/soap/envelope/" @@ -15,11 +17,15 @@ def __init__(self, body_namespace: str): self.current_element = self.body def finish_add_sub_element(self): - self.current_element = ET.SubElement(self.current_element,) + self.current_element = ET.SubElement( + self.current_element, self._body_namespace) def change_to_element(self, name: str): - self.current_element = self.body.find( + current_element = self.body.find( f".//{{{self._body_namespace}}}{name}") + if current_element is None: + raise WebException("can not find the node") + self.current_element = current_element def back_to_parent_element(self): self.current_element = self.body @@ -30,7 +36,7 @@ def add(self, name: str, value: object = None): ) if value is not None: - new_element.text = value + new_element.text = str(value) self.current_element = new_element def __str__(self) -> str: diff --git a/src/servers/web_services/src/applications/server_launcher.py b/src/servers/web_services/src/applications/server_launcher.py index 6e4a0f7e2..32fb5402d 100644 --- a/src/servers/web_services/src/applications/server_launcher.py +++ b/src/servers/web_services/src/applications/server_launcher.py @@ -1,7 +1,7 @@ from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.http_handler import HttpServer from library.src.configs import CONFIG -from servers.web_services.src.applications import client +from servers.web_services.src.applications.client import Client class ServerLauncher(ServerLauncherBase): @@ -12,7 +12,7 @@ def __init__(self) -> None: self.config = CONFIG.servers["WebServices"] def _launch_server(self): - HttpServer(self.config, client).start() + HttpServer(self.config, Client, self.logger).start() if __name__ == "__main__": diff --git a/src/servers/web_services/src/exceptions/general.py b/src/servers/web_services/src/exceptions/general.py index b9b15802b..424661137 100644 --- a/src/servers/web_services/src/exceptions/general.py +++ b/src/servers/web_services/src/exceptions/general.py @@ -1,6 +1,6 @@ from library.src.exceptions.general import UniSpyException -class WebExceptions(UniSpyException): +class WebException(UniSpyException): pass diff --git a/src/servers/web_services/src/handlers/switcher.py b/src/servers/web_services/src/handlers/switcher.py index 736206c28..4859f32c1 100644 --- a/src/servers/web_services/src/handlers/switcher.py +++ b/src/servers/web_services/src/handlers/switcher.py @@ -1,9 +1,11 @@ +from typing import TYPE_CHECKING, Optional, cast from library.src.abstractions.client import ClientBase from library.src.abstractions.handler import CmdHandlerBase from library.src.abstractions.switcher import SwitcherBase import xml.etree.ElementTree as ET -from servers.web_services.src.exceptions.general import WebExceptions +from servers.web_services.src.applications.client import Client +from servers.web_services.src.exceptions.general import WebException from servers.web_services.src.modules.auth.contracts.requests import LoginProfileRequest, LoginProfileWithGameIdRequest, LoginRemoteAuthRequest, LoginRemoteAuthWithGameIdRequest, LoginUniqueNickRequest, LoginUniqueNickWithGameIdRequest from servers.web_services.src.modules.auth.handlers.general import LoginProfileHandler, LoginProfileWithGameIdHandler, LoginRemoteAuthHandler, LoginRemoteAuthWithGameIdHandler, LoginUniqueNickHandler, LoginUniqueNickWithGameIdHandler from servers.web_services.src.modules.direct2game.contracts.requests import GetPurchaseHistoryRequest, GetStoreAvailabilityRequest @@ -23,17 +25,19 @@ def __init__(self, client: ClientBase, raw_request: str) -> None: def _process_raw_request(self) -> None: name_node = ET.fromstring(self._raw_request)[0][0] if name_node is None: - raise WebExceptions("name node is missing from soap request") - + raise WebException("name node is missing from soap request") + if name_node.text is None: + raise WebException( + "name node text field is missing from soap request") name = name_node.text.split("}")[1] if len(name) < 4: - raise WebExceptions("request name invalid") + raise WebException("request name invalid") self._requests.append((name, self._raw_request)) - def _create_cmd_handlers(self, name: str, raw_request: str) -> CmdHandlerBase | None: - assert isinstance(name, str) - assert isinstance(raw_request, str) + def _create_cmd_handlers(self, name: str, raw_request: str) -> Optional[CmdHandlerBase]: + if TYPE_CHECKING: + self._client = cast(Client, self._client) match name: # Altas services @@ -57,7 +61,7 @@ def _create_cmd_handlers(self, name: str, raw_request: str) -> CmdHandlerBase | return LoginRemoteAuthHandler(self._client, LoginRemoteAuthRequest(raw_request)) case "LoginRemoteAuthWithGameId": - return LoginRemoteAuthWithGameIdHandler(self._client, LoginRemoteAuthWithGameIdRequest) + return LoginRemoteAuthWithGameIdHandler(self._client, LoginRemoteAuthWithGameIdRequest(raw_request)) case "LoginUniqueNick": return LoginUniqueNickHandler(self._client, LoginUniqueNickRequest(raw_request)) @@ -105,7 +109,7 @@ def _create_cmd_handlers(self, name: str, raw_request: str) -> CmdHandlerBase | case "DeleteRecord": raise NotImplementedError() case "GetMyRecords": - raise GetMyRecordsHandler( + return GetMyRecordsHandler( self._client, GetMyRecordsRequest(raw_request)) case "GetRandomRecords": raise NotImplementedError() diff --git a/src/servers/web_services/src/modules/auth/contracts/requests.py b/src/servers/web_services/src/modules/auth/contracts/requests.py index 189c4d34b..6dc8dd3e1 100644 --- a/src/servers/web_services/src/modules/auth/contracts/requests.py +++ b/src/servers/web_services/src/modules/auth/contracts/requests.py @@ -14,24 +14,24 @@ class LoginProfileRequest(LoginRequestBase): def parse(self) -> None: super().parse() email = self._content_element.find(f".//{{{NAMESPACE}}}email") - if email is None: + if email is None or email.text is None: raise AuthException("email is missing from the request.") self.email = email.text uniquenick = self._content_element.find( f".//{{{NAMESPACE}}}uniquenick") - if uniquenick is None: + if uniquenick is None or uniquenick.text is None: raise AuthException("uniquenick is missing from the request.") self.uniquenick = uniquenick.text cdkey = self._content_element.find(f".//{{{NAMESPACE}}}cdkey") - if cdkey is None: + if cdkey is None or cdkey.text is None: raise AuthException("cdkey is missing from the request.") self.cdkey = cdkey.text password = self._content_element.find( f".//{{{NAMESPACE}}}password//{{{NAMESPACE}}}Value") - if password is None: + if password is None or password.text is None: raise AuthException("password is missing from the request.") self.password = password.text @@ -42,7 +42,7 @@ class LoginProfileWithGameIdRequest(LoginProfileRequest): def parse(self) -> None: super().parse() game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") - if game_id is None: + if game_id is None or game_id.text is None: raise AuthException("game id is missing from the request.") self.game_id = int(game_id.text) @@ -57,17 +57,17 @@ def parse(self) -> None: super().parse() ps3_cert = self._content_element.find( f".//{{{NAMESPACE}}}ps3sert") - if ps3_cert is None: + if ps3_cert is None or ps3_cert.text is None: raise AuthException("ps3cert is missing from the request") self.ps3_cert = ps3_cert.text game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") - if game_id is None: + if game_id is None or game_id.text is None: raise AuthException("game id is missing from the request.") self.game_id = int(game_id.text) npticket = self._content_element.find(f".//{{{NAMESPACE}}}npticket") - if npticket is None: + if npticket is None or npticket.text is None: raise AuthException("npticket is missing from the request.") self.npticket = npticket.text @@ -78,7 +78,7 @@ class LoginPs3CertWithGameIdRequest(LoginPs3CertRequest): def parse(self) -> None: super().parse() game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") - if game_id is None: + if game_id is None or game_id.text is None: raise AuthException("game id is missing from the request.") self.game_id = int(game_id.text) @@ -91,18 +91,18 @@ def parse(self) -> None: super().parse() auth_token = self._content_element.find( f".//{{{NAMESPACE}}}authtoken") - if auth_token is None: + if auth_token is None or auth_token.text is None: raise AuthException("authtoken is missing from the request.") self.auth_token = auth_token.text challenge = self._content_element.find( f".//{{{NAMESPACE}}}challenge") - if challenge is None: + if challenge is None or challenge.text is None: raise AuthException("challenge is missing from the request.") self.challenge = challenge.text game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") - if game_id is None: + if game_id is None or game_id.text is None: raise AuthException("game id is missing from the request.") self.game_id = int(game_id.text) @@ -113,7 +113,7 @@ class LoginRemoteAuthWithGameIdRequest(LoginRemoteAuthRequest): def parse(self) -> None: super().parse() game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") - if game_id is None: + if game_id is None or game_id.text is None: raise AuthException("game id is missing from the request.") self.game_id = int(game_id.text) @@ -133,7 +133,7 @@ def parse(self) -> None: password = self._content_element.find( f".//{{{NAMESPACE}}}password//{{{NAMESPACE}}}Value") - if password is None: + if password is None or password.text is None: raise AuthException("password is missing from the request.") self.password = password.text @@ -144,7 +144,7 @@ class LoginUniqueNickWithGameIdRequest(LoginUniqueNickRequest): def parse(self) -> None: super().parse() game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") - if game_id is None: + if game_id is None or game_id.text is None: raise AuthException("game id is missing from the request.") self.game_id = int(game_id.text) diff --git a/src/servers/web_services/src/modules/auth/exceptions/general.py b/src/servers/web_services/src/modules/auth/exceptions/general.py index 79233b574..2e262963b 100644 --- a/src/servers/web_services/src/modules/auth/exceptions/general.py +++ b/src/servers/web_services/src/modules/auth/exceptions/general.py @@ -1,5 +1,5 @@ -from servers.web_services.src.exceptions.general import WebExceptions +from servers.web_services.src.exceptions.general import WebException -class AuthException(WebExceptions): +class AuthException(WebException): pass diff --git a/src/servers/web_services/src/modules/auth/handlers/general.py b/src/servers/web_services/src/modules/auth/handlers/general.py index 83de30723..ee650691b 100644 --- a/src/servers/web_services/src/modules/auth/handlers/general.py +++ b/src/servers/web_services/src/modules/auth/handlers/general.py @@ -22,6 +22,7 @@ LoginProfileResult, LoginPs3CertResult, LoginRemoteAuthResult, + LoginUniqueNickResult, ) @@ -38,7 +39,8 @@ class LoginProfileWithGameIdHandler(CmdHandlerBase): _result: LoginProfileResult def _response_construct(self) -> None: - self._response = LoginProfileWithGameIdResponse(self._request, self._result) + self._response = LoginProfileWithGameIdResponse( + self._request, self._result) class LoginPs3CertHandler(CmdHandlerBase): @@ -61,13 +63,16 @@ def _response_construct(self) -> None: class LoginRemoteAuthWithGameIdHandler(CmdHandlerBase): _request: LoginRemoteAuthWithGameIdRequest + _result: LoginRemoteAuthResult def _response_construct(self) -> None: - self._response = LoginRemoteAuthWithGameIdResponse(self._request, self._result) + self._response = LoginRemoteAuthWithGameIdResponse( + self._request, self._result) class LoginUniqueNickHandler(CmdHandlerBase): _request: LoginUniqueNickRequest + _result: LoginUniqueNickResult def _response_construct(self) -> None: self._response = LoginUniqueNickResponse(self._request, self._result) @@ -75,6 +80,7 @@ def _response_construct(self) -> None: class LoginUniqueNickWithGameIdHandler(CmdHandlerBase): _request: LoginUniqueNickWithGameIdRequest - + _result: LoginUniqueNickResult def _response_construct(self) -> None: - self._response = LoginUniqueNickWithGameIdResponse(self._request, self._result) + self._response = LoginUniqueNickWithGameIdResponse( + self._request, self._result) diff --git a/src/servers/web_services/src/modules/direct2game/contracts/requests.py b/src/servers/web_services/src/modules/direct2game/contracts/requests.py index c6a5f40fd..aa38205f9 100644 --- a/src/servers/web_services/src/modules/direct2game/contracts/requests.py +++ b/src/servers/web_services/src/modules/direct2game/contracts/requests.py @@ -1,4 +1,4 @@ -from servers.web_services.src.exceptions.general import WebExceptions +from servers.web_services.src.exceptions.general import WebException from servers.web_services.src.modules.direct2game.abstractions.contracts import ( NAMESPACE, RequestBase, @@ -14,21 +14,28 @@ class GetPurchaseHistoryRequest(RequestBase): def parse(self) -> None: super().parse() game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") - if game_id is None: - raise WebExceptions("gameid is missing from the request.") - self.game_id = int(game_id) - - self.proof = self._content_element.find(f".//{{{NAMESPACE}}}proof") - if self.proof is None: - raise WebExceptions("proof is missing from the request.") - self.access_token = self._content_element.find( + if game_id is None or game_id.text is None: + raise WebException("gameid is missing from the request.") + self.game_id = int(game_id.text) + + proof = self._content_element.find(f".//{{{NAMESPACE}}}proof") + if proof is None or proof.text is None: + raise WebException("proof is missing from the request.") + self.proof = proof.text + + access_token = self._content_element.find( f".//{{{NAMESPACE}}}access_token" ) - if self.access_token is None: - raise WebExceptions("access_token is missing from the request.") - self.certificate = self._content_element.find(f".//{{{NAMESPACE}}}certificate") - if self.certificate is None: - raise WebExceptions("certificate is missing from the request.") + + if access_token is None or access_token.text is None: + raise WebException("access_token is missing from the request.") + self.access_token = access_token.text + + certificate = self._content_element.find( + f".//{{{NAMESPACE}}}certificate") + if certificate is None or certificate.text is None: + raise WebException("certificate is missing from the request.") + self.certificate = certificate.text class GetStoreAvailabilityRequest(RequestBase): @@ -40,16 +47,21 @@ class GetStoreAvailabilityRequest(RequestBase): def parse(self) -> None: super().parse() game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") - if game_id is None: - raise WebExceptions("gameid is missing from the request.") - self.game_id = int(game_id) + if game_id is None or game_id.text is None: + raise WebException("gameid is missing from the request.") + self.game_id = int(game_id.text) version = self._content_element.find(f".//{{{NAMESPACE}}}version") - if version is None: - raise WebExceptions("version is missing from the request.") - self.version = int(version) - self.region = self._content_element.find(f".//{{{NAMESPACE}}}region") - if self.region is None: - raise WebExceptions("region is missing from the request.") - self.access_token = self._content_element.find(f".//{{{NAMESPACE}}}accesstoken") - if self.access_token is None: - raise WebExceptions("accesstoken is missing from the request.") + if version is None or version.text is None: + raise WebException("version is missing from the request.") + self.version = int(version.text) + region = self._content_element.find(f".//{{{NAMESPACE}}}region") + if region is None or region.text is None: + raise WebException("region is missing from the request.") + self.region = region.text + + access_token = self._content_element.find( + f".//{{{NAMESPACE}}}accesstoken") + + if access_token is None or access_token.text is None: + raise WebException("accesstoken is missing from the request.") + self.access_token = access_token.text diff --git a/src/servers/web_services/src/modules/sake/abstractions/general.py b/src/servers/web_services/src/modules/sake/abstractions/general.py index b9516b406..26c0d8e15 100644 --- a/src/servers/web_services/src/modules/sake/abstractions/general.py +++ b/src/servers/web_services/src/modules/sake/abstractions/general.py @@ -1,8 +1,8 @@ -from xml.etree import ElementTree import servers.web_services.src.abstractions.handler as h import servers.web_services.src.abstractions.contracts as lib from servers.web_services.src.aggregations.soap_envelop import SoapEnvelop from servers.web_services.src.modules.sake.exceptions.general import SakeException +import xml.etree.ElementTree as ET NAMESPACE = "http://gamespy.net/sake" @@ -16,30 +16,30 @@ class RequestBase(lib.RequestBase): def parse(self) -> None: super().parse() game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") - if game_id is None: + if game_id is None or game_id.text is None: raise SakeException("gameid is missing from the request.") self.game_id = int(game_id.text) secret_key = self._content_element.find( f".//{{{NAMESPACE}}}secretKey") - if secret_key is None: + if secret_key is None or secret_key.text is None: raise SakeException("secretkey id is missing from the request.") self.secret_key = secret_key.text login_ticket = self._content_element.find( f".//{{{NAMESPACE}}}loginTicket") - if login_ticket is None: + if login_ticket is None or login_ticket.text is None: raise SakeException("loginTicket is missing from the request.") self.login_ticket = login_ticket.text table_id = self._content_element.find( f".//{{{NAMESPACE}}}tableid") - if table_id is None: + if table_id is None or table_id.text is None: raise SakeException("tableid is missing from the request.") self.table_id = table_id.text @staticmethod - def remove_namespace(tree: ElementTree): + def remove_namespace(tree: ET.Element): tree.tag = tree.tag.split('}', 1)[-1] for elem in tree: # Remove the namespace by splitting the tag diff --git a/src/servers/web_services/src/modules/sake/contracts/requests.py b/src/servers/web_services/src/modules/sake/contracts/requests.py index 630db1b91..444fce539 100644 --- a/src/servers/web_services/src/modules/sake/contracts/requests.py +++ b/src/servers/web_services/src/modules/sake/contracts/requests.py @@ -136,7 +136,7 @@ def parse(self) -> None: super().parse() record_id = self._content_element.find( f".//{{{NAMESPACE}}}recordid") - if record_id is None: + if record_id is None or record_id.text is None: raise SakeException("No record id found.") self.record_id = record_id.text @@ -155,7 +155,7 @@ class SearchForRecordsRequest(RequestBase): surrounding: str owner_ids: str cache_flag: str - fields: list[tuple[str, str]] = [] + fields: list[tuple[str, str]] """ [ (field_name,field_type), @@ -170,58 +170,60 @@ def parse(self) -> None: super().parse() filter = self._content_element.find( f".//{{{NAMESPACE}}}filter") - if filter is None: + if filter is None or filter.text is None: raise SakeException("No filter found.") self.filter = filter.text sort = self._content_element.find( f".//{{{NAMESPACE}}}sort") - if sort is None: + if sort is None or sort.text is None: raise SakeException("No sort found.") self.sort = sort.text offset = self._content_element.find( f".//{{{NAMESPACE}}}offset") - if offset is None: + if offset is None or offset.text is None: raise SakeException("No offset found.") self.offset = offset.text - max = self._content_element.find( + vmax = self._content_element.find( f".//{{{NAMESPACE}}}max") - if max is None: + if vmax is None or vmax.text is None: raise SakeException("No max found.") - self.max = max.text + self.max = vmax.text surrounding = self._content_element.find( f".//{{{NAMESPACE}}}surrounding") - if surrounding is None: + if surrounding is None or surrounding.text is None: raise SakeException("No surrounding found.") self.surrounding = surrounding.text owner_ids = self._content_element.find( f".//{{{NAMESPACE}}}ownerids") - if owner_ids is None: + if owner_ids is None or owner_ids.text is None: raise SakeException("No ownderids found.") self.owner_ids = owner_ids.text cache_flag = self._content_element.find( f".//{{{NAMESPACE}}}cacheFlag") - if cache_flag is None: + if cache_flag is None or cache_flag.text is None: raise SakeException("No cache flag found.") self.cache_flag = cache_flag.text fields = self._content_element.find( f".//{{{NAMESPACE}}}fields") + self.fields = [] if fields is None: raise SakeException("No record id found.") for e in fields: data = (e.text, e.tag.split("}")[1]) - self.fields.append(data) + if data is not None: + self.fields.append(data) class UpdateRecordRequest(RequestBase): record_id: str - values: OrderedDict[str, object] + values: list """ [ (field_name,field_type,field_value), @@ -236,11 +238,12 @@ def parse(self) -> None: super().parse() record_id = self._content_element.find( f".//{{{NAMESPACE}}}recordid") - if record_id is None: + if record_id is None or record_id.text is None: raise SakeException("No record id found.") self.record_id = record_id.text values_node = self._content_element.find( f".//{{{NAMESPACE}}}values") + # todo fix this temp_str = ET.tostring( - values_node, encoding="unicode").replace("ns0:", "") + element=values_node, encoding="unicode").replace("ns0:", "") self.values = xmltodict.parse(temp_str)['values']["RecordField"] diff --git a/src/servers/web_services/src/modules/sake/exceptions/general.py b/src/servers/web_services/src/modules/sake/exceptions/general.py index 9cc3e22bc..589966e55 100644 --- a/src/servers/web_services/src/modules/sake/exceptions/general.py +++ b/src/servers/web_services/src/modules/sake/exceptions/general.py @@ -1,5 +1,5 @@ -from servers.web_services.src.exceptions.general import WebExceptions +from servers.web_services.src.exceptions.general import WebException -class SakeException(WebExceptions): +class SakeException(WebException): pass From caaf5dbd33a048130eaa36ac5c076f9da61eba47 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 24 Oct 2024 02:26:49 +0000 Subject: [PATCH 120/231] fix: type hint erros --- src/backends/library/database/pg_orm.py | 2 +- src/backends/protocols/gamespy/chat/data.py | 6 +- .../protocols/gamespy/game_status/data.py | 3 +- .../presence_connection_manager/data.py | 2 +- .../presence_connection_manager/requests.py | 2 +- .../presence_search_player/requests.py | 2 +- .../protocols/gamespy/query_report/data.py | 2 +- .../query_report}/game_server_info_v2.py | 3 +- .../gamespy/query_report/requests.py | 2 +- .../gamespy/server_browser/requests.py | 2 +- .../gamespy/presence_connection_manager.py | 8 +- src/backends/routers/gamespy/query_report.py | 2 +- src/backends/routers/home.py | 2 +- src/library/src/configs.py | 4 + .../src/abstractions/contracts.py | 2 +- .../src/abstractions/handlers.py | 4 +- .../{enums/general.py => aggregates/enums.py} | 0 .../src/aggregates/login_challenge.py | 2 +- .../src/aggregates/sdk_revision.py | 2 +- .../src/aggregates/user_status.py | 2 +- .../src/applications/client.py | 8 +- .../src/applications/handlers.py | 299 +++++++++ .../{handlers => applications}/switcher.py | 15 +- .../src/contracts/requests.py | 611 ++++++++++++++++++ .../src/contracts/requests/buddy.py | 145 ----- .../src/contracts/requests/general.py | 194 ------ .../src/contracts/requests/profile.py | 271 -------- .../src/contracts/responses.py | 242 +++++++ .../src/contracts/responses/buddy.py | 80 --- .../src/contracts/responses/general.py | 61 -- .../src/contracts/responses/profile.py | 97 --- .../src/contracts/results.py | 101 +++ .../src/contracts/results/buddy.py | 30 - .../src/contracts/results/general.py | 25 - .../src/contracts/results/profile.py | 43 -- .../src/handlers/buddy.py | 123 ---- .../src/handlers/general.py | 87 --- .../src/handlers/profile.py | 96 --- .../tests/buddy_request_tests.py | 2 +- .../tests/game_tests.py | 2 +- .../tests/general_request_tests.py | 4 +- .../tests/profile_request_tests.py | 2 +- .../src/abstractions/contracts.py | 2 +- .../src/abstractions/handler.py | 2 +- .../error_codes.py => aggregates/enums.py} | 10 +- .../general.py => aggregates/exceptions.py} | 94 +-- .../src/contracts/requests.py | 4 +- .../src/enums/general.py | 8 - .../src/aggregates/game_server_info.py | 2 +- .../src/v2/abstractions/contracts.py | 4 +- .../{enums/general.py => aggregates/enums.py} | 0 .../query_report/src/v2/contracts/requests.py | 11 +- .../src/v2/contracts/responses.py | 6 +- .../query_report/src/v2/contracts/results.py | 2 +- .../query_report/src/v2/handlers/switcher.py | 2 +- .../src/v2/abstractions/contracts.py | 40 +- .../src/v2/abstractions/handlers.py | 2 +- .../general.py => aggregations/enums.py} | 0 .../v2/aggregations/server_info_builder.py | 2 +- .../src/v2/aggregations/string_flags.py | 6 +- .../src/v2/applications/client.py | 4 +- .../v2/{handlers => applications}/handlers.py | 13 +- .../src/v2/applications/server_launcher.py | 2 +- .../src/v2/contracts/requests.py | 15 +- .../src/v2/contracts/responses.py | 40 +- .../src/modules/sake/contracts/requests.py | 28 +- 66 files changed, 1459 insertions(+), 1432 deletions(-) rename src/{servers/query_report/src/v2/aggregates => backends/protocols/gamespy/query_report}/game_server_info_v2.py (90%) rename src/servers/presence_connection_manager/src/{enums/general.py => aggregates/enums.py} (100%) create mode 100644 src/servers/presence_connection_manager/src/applications/handlers.py rename src/servers/presence_connection_manager/src/{handlers => applications}/switcher.py (70%) create mode 100644 src/servers/presence_connection_manager/src/contracts/requests.py delete mode 100644 src/servers/presence_connection_manager/src/contracts/requests/buddy.py delete mode 100644 src/servers/presence_connection_manager/src/contracts/requests/general.py delete mode 100644 src/servers/presence_connection_manager/src/contracts/requests/profile.py create mode 100644 src/servers/presence_connection_manager/src/contracts/responses.py delete mode 100644 src/servers/presence_connection_manager/src/contracts/responses/buddy.py delete mode 100644 src/servers/presence_connection_manager/src/contracts/responses/general.py delete mode 100644 src/servers/presence_connection_manager/src/contracts/responses/profile.py create mode 100644 src/servers/presence_connection_manager/src/contracts/results.py delete mode 100644 src/servers/presence_connection_manager/src/contracts/results/buddy.py delete mode 100644 src/servers/presence_connection_manager/src/contracts/results/general.py delete mode 100644 src/servers/presence_connection_manager/src/contracts/results/profile.py delete mode 100644 src/servers/presence_connection_manager/src/handlers/buddy.py delete mode 100644 src/servers/presence_connection_manager/src/handlers/general.py delete mode 100644 src/servers/presence_connection_manager/src/handlers/profile.py rename src/servers/presence_search_player/src/{enums/error_codes.py => aggregates/enums.py} (94%) rename src/servers/presence_search_player/src/{exceptions/general.py => aggregates/exceptions.py} (76%) delete mode 100644 src/servers/presence_search_player/src/enums/general.py rename src/servers/query_report/src/v2/{enums/general.py => aggregates/enums.py} (100%) rename src/servers/server_browser/src/v2/{enums/general.py => aggregations/enums.py} (100%) rename src/servers/server_browser/src/v2/{handlers => applications}/handlers.py (93%) diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index af3e6fb49..4945d9866 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -21,7 +21,7 @@ from sqlalchemy.orm import sessionmaker, declarative_base from servers.natneg.src.enums.general import NatClientIndex, NatPortType -from servers.query_report.src.v2.enums.general import GameServerStatus +from servers.query_report.src.v2.aggregates.enums import GameServerStatus Base: DeclarativeMeta = declarative_base() diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index 6cc59338f..1e2172be1 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -1,5 +1,5 @@ from backends.protocols.gamespy.chat.storage_infos import ChannelInfo, ChannelUser -from library.src.database.pg_orm import PG_SESSION, Users, Profiles, SubProfiles +from backends.library.database.pg_orm import PG_SESSION, Users, Profiles, SubProfiles from servers.chat.src.aggregates.channel import Channel from servers.chat.src.exceptions.general import ChatException @@ -10,7 +10,7 @@ def nick_and_email_login(nick_name: str, email: str, password_hash: str) -> tupl userid, profileid, emailverified, banned """ result = PG_SESSION.query(Users.userid, Profiles.profileid, - Users.emailverified, Users.banned).join(Profiles, (Users.userid, Profiles.userid)).where( + Users.emailverified, Users.banned).join(Profiles, (Users.userid == Profiles.userid)).where( Users.email == email, Profiles.nick == nick_name, Users.password == password_hash @@ -27,7 +27,7 @@ def uniquenick_login(uniquenick:str,namespace_id:int)-> tuple[int, int, bool, bo return userid, profileid, emailverified, banned """ - result = PG_SESSION.query(Users.userid, Profiles.profileid,Users.emailverified, Users.banned).join(Profiles,(Users.userid,Profiles.userid)).join(Profiles,(Profiles.profileid,SubProfiles.profileid)).where(SubProfiles.namespaceid == namespace_id,SubProfiles.uniquenick == uniquenick).first() + result = PG_SESSION.query(Users.userid, Profiles.profileid,Users.emailverified, Users.banned).join(Profiles,(Users.userid == Profiles.userid)).join(Profiles,(Profiles.profileid == SubProfiles.profileid)).where(SubProfiles.namespaceid == namespace_id,SubProfiles.uniquenick == uniquenick).first() if result is None: # fmt: off raise ChatException(f"Can not find user with uniquenick:{uniquenick} in database.") diff --git a/src/backends/protocols/gamespy/game_status/data.py b/src/backends/protocols/gamespy/game_status/data.py index 2558ff7d0..dce9210eb 100644 --- a/src/backends/protocols/gamespy/game_status/data.py +++ b/src/backends/protocols/gamespy/game_status/data.py @@ -1,5 +1,4 @@ -from typing import Optional -from library.src.database.pg_orm import PG_SESSION, PStorage, Profiles, SubProfiles +from backends.library.database.pg_orm import PG_SESSION, PStorage, Profiles, SubProfiles from servers.game_status.src.enums.general import PersistStorageType from servers.game_status.src.exceptions.general import GSException diff --git a/src/backends/protocols/gamespy/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py index da4cab584..211e9cb2e 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/data.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/data.py @@ -6,7 +6,7 @@ Users, PG_SESSION, ) -from servers.presence_search_player.src.exceptions.general import GPDatabaseException +from servers.presence_search_player.src.aggregates.exceptions import GPDatabaseException def is_email_exist(email: str) -> bool: diff --git a/src/backends/protocols/gamespy/presence_connection_manager/requests.py b/src/backends/protocols/gamespy/presence_connection_manager/requests.py index 8a0564f91..fcc3aeba2 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/requests.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/requests.py @@ -5,7 +5,7 @@ from servers.presence_connection_manager.src.aggregates.user_status_info import ( UserStatusInfo, ) -from servers.presence_connection_manager.src.enums.general import ( +from servers.presence_connection_manager.src.aggregates.enums import ( LoginType, PublicMasks, SdkRevisionType, diff --git a/src/backends/protocols/gamespy/presence_search_player/requests.py b/src/backends/protocols/gamespy/presence_search_player/requests.py index 4bab223ce..8c18494d8 100644 --- a/src/backends/protocols/gamespy/presence_search_player/requests.py +++ b/src/backends/protocols/gamespy/presence_search_player/requests.py @@ -1,6 +1,6 @@ from typing import Optional from backends.library.abstractions.contracts import RequestBase as RB -from servers.presence_search_player.src.enums.general import SearchType +from servers.presence_search_player.src.aggregates.enums import SearchType class RequestBase(RB): diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index 0651da956..74030f6e2 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -1,7 +1,7 @@ from backends.protocols.gamespy.chat.storage_infos import ChannelInfo from library.src.database.pg_orm import PG_SESSION, GroupList, Games from servers.chat.src.aggregates.peer_room import PeerRoom -from servers.query_report.src.v2.aggregates.game_server_info_v2 import GameServerInfoV2 +from backends.protocols.gamespy.query_report.game_server_info_v2 import GameServerInfoV2 def get_all_groups(): diff --git a/src/servers/query_report/src/v2/aggregates/game_server_info_v2.py b/src/backends/protocols/gamespy/query_report/game_server_info_v2.py similarity index 90% rename from src/servers/query_report/src/v2/aggregates/game_server_info_v2.py rename to src/backends/protocols/gamespy/query_report/game_server_info_v2.py index ef1d25b35..179ae29fc 100644 --- a/src/servers/query_report/src/v2/aggregates/game_server_info_v2.py +++ b/src/backends/protocols/gamespy/query_report/game_server_info_v2.py @@ -12,7 +12,8 @@ ) # from library.src.database.mongodb_orm import connect_to_db, get_ttl_param -from servers.query_report.src.v2.enums.general import GameServerStatus +from backends.library.database.mongodb_orm import get_ttl_param +from servers.query_report.src.v2.aggregates.enums import GameServerStatus class GameServerInfoV2(Document): diff --git a/src/backends/protocols/gamespy/query_report/requests.py b/src/backends/protocols/gamespy/query_report/requests.py index 8b60edae6..5c5722d9c 100644 --- a/src/backends/protocols/gamespy/query_report/requests.py +++ b/src/backends/protocols/gamespy/query_report/requests.py @@ -2,7 +2,7 @@ from pydantic import UUID4 import backends.library.abstractions.contracts as lib -from servers.query_report.src.v2.enums.general import GameServerStatus, RequestType +from servers.query_report.src.v2.aggregates.enums import GameServerStatus, RequestType class RequestBase(lib.RequestBase): diff --git a/src/backends/protocols/gamespy/server_browser/requests.py b/src/backends/protocols/gamespy/server_browser/requests.py index e6b1db385..697fdd97b 100644 --- a/src/backends/protocols/gamespy/server_browser/requests.py +++ b/src/backends/protocols/gamespy/server_browser/requests.py @@ -1,6 +1,6 @@ from typing import List, Optional import backends.library.abstractions.contracts as lib -from servers.server_browser.src.v2.enums.general import RequestType, ServerListUpdateOption +from servers.server_browser.src.v2.aggregations.enums import RequestType, ServerListUpdateOption class RequestBase(lib.RequestBase): diff --git a/src/backends/routers/gamespy/presence_connection_manager.py b/src/backends/routers/gamespy/presence_connection_manager.py index 8431a1b92..e03808454 100644 --- a/src/backends/routers/gamespy/presence_connection_manager.py +++ b/src/backends/routers/gamespy/presence_connection_manager.py @@ -1,13 +1,9 @@ from fastapi import APIRouter from backends.protocols.gamespy.chat.requests import RegisterNickRequest -from backends.protocols.gamespy.presence_connection_manager.requests import GetProfileRequest, LoginRequest, LogoutRequest, NewProfileRequest, RegisterCDKeyRequest, StatusInfoRequest, StatusRequest, UpdateProfileRequest +from backends.protocols.gamespy.presence_connection_manager.requests import GetProfileRequest, LoginRequest, LogoutRequest, NewProfileRequest, RegisterCDKeyRequest, StatusInfoRequest, StatusRequest, UpdateProfileRequest, KeepAliveRequest, NewUserRequest, AddBlockRequest from backends.urls import * -from servers.presence_connection_manager.src.contracts.requests.general import KeepAliveRequest -from servers.presence_connection_manager.src.contracts.requests.profile import AddBlockRequest -from servers.presence_search_player.src.contracts.requests import NewUserRequest - router = APIRouter() @@ -78,4 +74,4 @@ async def status_info(request: StatusInfoRequest): app = FastAPI() app.include_router(router) - uvicorn.run(router, host="0.0.0.0", port=8000) \ No newline at end of file + uvicorn.run(router, host="0.0.0.0", port=8000) diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index 8ec5944d5..5b7c57dcf 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -2,7 +2,7 @@ from backends.protocols.gamespy.query_report.requests import ChallengeRequest, ClientMessageRequest, EchoRequest, HeartBeatRequest from backends.urls import QUERY_REPORT -from servers.presence_connection_manager.src.contracts.requests.general import KeepAliveRequest +from servers.presence_connection_manager.src.contracts.requests import KeepAliveRequest from servers.query_report.src.v2.contracts.requests import AvaliableRequest router = APIRouter() diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index 874bfe414..506d445df 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -14,7 +14,7 @@ app.include_router(server_browser.router) app.include_router(webservices.router) -LogManager.create(CONFIG.logging.path, "backend") +LogManager.create("backend") @app.post("/") diff --git a/src/library/src/configs.py b/src/library/src/configs.py index 1a79c053f..354c45543 100644 --- a/src/library/src/configs.py +++ b/src/library/src/configs.py @@ -115,6 +115,10 @@ def __post_init__(self): raise Exception( "Unispy server config not found, you should set the UNISPY_CONFIG in the system enviroment." ) +if not os.path.exists(unispy_config): + raise Exception( + "Unispy server config file not exist, check UNISPY_CONFIG path." + ) with open(unispy_config, "r") as f: import json diff --git a/src/servers/presence_connection_manager/src/abstractions/contracts.py b/src/servers/presence_connection_manager/src/abstractions/contracts.py index 7be2a0b37..215447bdc 100644 --- a/src/servers/presence_connection_manager/src/abstractions/contracts.py +++ b/src/servers/presence_connection_manager/src/abstractions/contracts.py @@ -3,7 +3,7 @@ import library.src.abstractions import library.src.abstractions.contracts from library.src.extentions.gamespy_utils import convert_to_key_value -from servers.presence_search_player.src.exceptions.general import ( +from servers.presence_search_player.src.aggregates.exceptions import ( GPParseException, ) from typing import Dict, Optional diff --git a/src/servers/presence_connection_manager/src/abstractions/handlers.py b/src/servers/presence_connection_manager/src/abstractions/handlers.py index dacab4e2b..e3b025c1b 100644 --- a/src/servers/presence_connection_manager/src/abstractions/handlers.py +++ b/src/servers/presence_connection_manager/src/abstractions/handlers.py @@ -1,7 +1,7 @@ from servers.presence_connection_manager.src.applications.client import Client -from servers.presence_connection_manager.src.enums.general import LoginStatus -from servers.presence_search_player.src.exceptions.general import GPException +from servers.presence_connection_manager.src.aggregates.enums import LoginStatus +from servers.presence_search_player.src.aggregates.exceptions import GPException from servers.presence_connection_manager.src.abstractions.contracts import ( RequestBase, diff --git a/src/servers/presence_connection_manager/src/enums/general.py b/src/servers/presence_connection_manager/src/aggregates/enums.py similarity index 100% rename from src/servers/presence_connection_manager/src/enums/general.py rename to src/servers/presence_connection_manager/src/aggregates/enums.py diff --git a/src/servers/presence_connection_manager/src/aggregates/login_challenge.py b/src/servers/presence_connection_manager/src/aggregates/login_challenge.py index 9a2039cfe..0790b0217 100644 --- a/src/servers/presence_connection_manager/src/aggregates/login_challenge.py +++ b/src/servers/presence_connection_manager/src/aggregates/login_challenge.py @@ -1,6 +1,6 @@ import hashlib -from servers.presence_connection_manager.src.enums.general import GPPartnerId, LoginType +from servers.presence_connection_manager.src.aggregates.enums import GPPartnerId, LoginType SERVER_CHALLENGE = "0000000000" diff --git a/src/servers/presence_connection_manager/src/aggregates/sdk_revision.py b/src/servers/presence_connection_manager/src/aggregates/sdk_revision.py index d58a6b6c7..b7eba2796 100644 --- a/src/servers/presence_connection_manager/src/aggregates/sdk_revision.py +++ b/src/servers/presence_connection_manager/src/aggregates/sdk_revision.py @@ -1,4 +1,4 @@ -from servers.presence_connection_manager.src.enums.general import SdkRevisionType +from servers.presence_connection_manager.src.aggregates.enums import SdkRevisionType class SdkRevision: diff --git a/src/servers/presence_connection_manager/src/aggregates/user_status.py b/src/servers/presence_connection_manager/src/aggregates/user_status.py index 2319df5e2..592be000e 100644 --- a/src/servers/presence_connection_manager/src/aggregates/user_status.py +++ b/src/servers/presence_connection_manager/src/aggregates/user_status.py @@ -1,6 +1,6 @@ from dataclasses import dataclass -from servers.presence_connection_manager.src.enums.general import GPStatusCode +from servers.presence_connection_manager.src.aggregates.enums import GPStatusCode @dataclass diff --git a/src/servers/presence_connection_manager/src/applications/client.py b/src/servers/presence_connection_manager/src/applications/client.py index ce60d2ef3..1753275ae 100644 --- a/src/servers/presence_connection_manager/src/applications/client.py +++ b/src/servers/presence_connection_manager/src/applications/client.py @@ -7,11 +7,9 @@ from servers.presence_connection_manager.src.aggregates.login_challenge import ( SERVER_CHALLENGE, ) -from servers.presence_connection_manager.src.enums.general import LoginStatus - - +from servers.presence_connection_manager.src.aggregates.enums import LoginStatus from servers.presence_connection_manager.src.aggregates.sdk_revision import SdkRevision -from servers.presence_connection_manager.src.enums.general import LoginStatus +from servers.presence_connection_manager.src.aggregates.enums import LoginStatus LOGIN_TICKET = "0000000000000000000000__" SESSION_KEY = 1111 @@ -59,5 +57,5 @@ def on_connected(self) -> None: self.connection.send(buffer) def create_switcher(self, buffer: bytes) -> SwitcherBase: - from servers.presence_connection_manager.src.handlers.switcher import Switcher + from servers.presence_connection_manager.src.applications.switcher import Switcher return Switcher(self, buffer.decode()) diff --git a/src/servers/presence_connection_manager/src/applications/handlers.py b/src/servers/presence_connection_manager/src/applications/handlers.py new file mode 100644 index 000000000..bad103dea --- /dev/null +++ b/src/servers/presence_connection_manager/src/applications/handlers.py @@ -0,0 +1,299 @@ +from typing import final + +from servers.presence_connection_manager.src.contracts.requests import ( + AddBlockRequest, + GetProfileRequest, + NewProfileRequest, + NewUserRequest, + RegisterCDKeyRequest, + RegisterNickRequest, + UpdateProfileRequest, + DelBuddyRequest, + StatusInfoRequest, + StatusRequest, + KeepAliveRequest, + LoginRequest, + LogoutRequest, +) +from servers.presence_connection_manager.src.abstractions.handlers import CmdHandlerBase +from servers.presence_connection_manager.src.contracts.results import ( + BlockListResult, + BuddyListResult, + NewUserResult, + StatusInfoResult, + StatusResult, + GetProfileResult, + NewProfileResult, + LoginResult +) +from servers.presence_connection_manager.src.contracts.responses import ( + BlockListResponse, + BuddyListResponse, + NewUserResponse, + StatusInfoResponse, + GetProfileResponse, + NewProfileResponse, + RegisterNickResponse, + KeepAliveResponse, + LoginResponse, +) + +from servers.presence_connection_manager.src.applications.client import Client +from servers.presence_connection_manager.src.abstractions.handlers import ( + CmdHandlerBase, + LoginedHandlerBase, +) +from servers.presence_connection_manager.src.abstractions.contracts import RequestBase +from multiprocessing.pool import Pool +from typing import TYPE_CHECKING +from servers.presence_connection_manager.src.aggregates.sdk_revision import SdkRevision + + +if TYPE_CHECKING: + from servers.presence_connection_manager.src.applications.client import Client + +# region General + + +class KeepAliveHandler(CmdHandlerBase): + _request: KeepAliveRequest + + def __init__(self, client: "Client", request: KeepAliveRequest) -> None: + assert isinstance(request, KeepAliveRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = KeepAliveResponse(self._request) + + +class LoginHandler(CmdHandlerBase): + + _request: LoginRequest + _result_cls: type[LoginResult] + _result: LoginResult + + def __init__(self, client: "Client", request: LoginRequest) -> None: + assert isinstance(request, LoginRequest) + super().__init__(client, request) + self._result_cls = LoginResult + + def _response_construct(self) -> None: + self._response = LoginResponse(self._request, self._result) + + +class LogoutHandler(LoginedHandlerBase): + _request: LogoutRequest + + def __init__(self, client: "Client", request: LogoutRequest) -> None: + assert isinstance(request, LogoutRequest) + super().__init__(client, request) + + +# todo create new handler +class NewUserHandler(CmdHandlerBase): + _request: NewUserRequest + _result_cls: type[NewUserResult] + _result: NewUserResult + # todo create seperate request and result + + def _response_construct(self): + self._response = NewUserResponse(self._request, self._result) + + +class SdkRevisionHandler(CmdHandlerBase): + _request: LoginRequest + + def __init__(self, client: "Client", request: LoginRequest) -> None: + assert isinstance(request, LoginRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._client.info.sdk_revision = SdkRevision( + self._request.sdk_revision_type) + if self._client.info.sdk_revision.is_support_gpi_new_status_notification: + BuddyListHandler(self._client).handle() + BlockListHandler(self._client).handle() + +# region Buddy + + +class AddBuddyHandler(CmdHandlerBase): + def __init__(self, client: Client, request: RequestBase) -> None: + raise NotImplementedError() + super().__init__(client, request) + + +class BlockListHandler(CmdHandlerBase): + _result: BlockListResult + + def __init__(self, client: Client) -> None: + assert isinstance(client, Client) + + def _response_construct(self) -> None: + self._response = BlockListResponse(self._result) + + +class BuddyListHandler(LoginedHandlerBase): + _result: BuddyListResult + _result_cls = BuddyListResult + + def __init__(self, client: Client): + assert isinstance(client, Client) + self._client = client + + def response_construct(self): + self._response = BuddyListResponse(self._request, self._result) + + def handle_status_info(self, profile_id): + request = StatusInfoRequest() + request.profile_id = profile_id + request.namespace_id = int(self._client.info.namespace_id) + # request.is_get_status_info = True + + StatusInfoHandler(self._client, request).handle() + + def _response_send(self) -> None: + super()._response_send() + + if not self._client.info.sdk_revision.is_support_gpi_new_status_notification: + return + + with Pool() as pool: + pool.map(self.handle_status_info, self._result.profile_ids) + + +class BuddyStatusInfoHandler(CmdHandlerBase): + """ + This is what the message should look like. Its broken up for easy viewing. + + \bsi\\state\\profile\\bip\\bport\\hostip\\hprivip\\qport\\hport\\sessflags\\rstatus\\gameType\\gameVnt\\gameMn\\product\\qmodeflags\ + """ + + def __init__(self, client: Client, request: RequestBase) -> None: + raise NotImplementedError() + super().__init__(client, request) + + +class DelBuddyHandler(LoginedHandlerBase): + _request: DelBuddyRequest + + def __init__(self, client: Client, request: DelBuddyRequest) -> None: + assert isinstance(request, DelBuddyRequest) + super().__init__(client, request) + + +class InviteToHandler(LoginedHandlerBase): + def __init__(self, client: Client, request: RequestBase) -> None: + raise NotImplementedError() + super().__init__(client, request) + + pass + + +class StatusHandler(CmdHandlerBase): + _request: StatusRequest + _result: StatusResult + + def __init__(self, client: Client, request: StatusRequest) -> None: + assert isinstance(request, StatusRequest) + super().__init__(client, request) + + def _response_send(self) -> None: + # TODO check if statushandler need send response + raise NotImplementedError() + + +class StatusInfoHandler(LoginedHandlerBase): + _request: StatusInfoRequest + _result: StatusInfoResult + + def __init__(self, client: Client, request: StatusInfoRequest) -> None: + assert isinstance(request, StatusInfoRequest) + super().__init__(client, request) + + def _response_send(self) -> None: + if self._request is not None: + self._response = StatusInfoResponse(self._request, self._result) + super()._response_send() + + +# region Profile + + +@final +class AddBlockHandler(CmdHandlerBase): + _request: AddBlockRequest + + def __init__(self, client: Client, request: AddBlockRequest) -> None: + assert isinstance(request, AddBlockRequest) + super().__init__(client, request) + + +@final +class GetProfileHandler(CmdHandlerBase): + _request: GetProfileRequest + _result: GetProfileResult + + def __init__(self, client: Client, request: GetProfileRequest) -> None: + assert isinstance(request, GetProfileRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = GetProfileResponse(self._request, self._result) + + +@final +class NewProfileHandler(CmdHandlerBase): + _request: NewProfileRequest + _result: NewProfileResult + + def __init__(self, client: Client, request: NewProfileRequest) -> None: + assert isinstance(request, NewProfileRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = NewProfileResponse(self._request, self._result) + + +@final +class RegisterCDKeyHandler(CmdHandlerBase): + _request: RegisterCDKeyRequest + + def __init__(self, client: Client, request: RegisterCDKeyRequest) -> None: + assert isinstance(request, RegisterCDKeyRequest) + super().__init__(client, request) + + +@final +class RegisterNickHandler(CmdHandlerBase): + _request: RegisterNickRequest + + def __init__(self, client: Client, request: RegisterNickRequest) -> None: + assert isinstance(request, RegisterNickRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = RegisterNickResponse(self._request) + + +@final +class RemoveBlockHandler(CmdHandlerBase): + def __init__(self, client: Client, request: RequestBase) -> None: + super().__init__(client, request) + raise NotImplementedError() + + +@final +class UpdateProfileHandler(CmdHandlerBase): + _request: UpdateProfileRequest + + def __init__(self, client: Client, request: UpdateProfileRequest) -> None: + assert isinstance(request, UpdateProfileRequest) + super().__init__(client, request) + + +@final +class UpdateUserInfoHandler(CmdHandlerBase): + def __init__(self, client: Client, request: RequestBase) -> None: + super().__init__(client, request) + raise NotImplementedError() diff --git a/src/servers/presence_connection_manager/src/handlers/switcher.py b/src/servers/presence_connection_manager/src/applications/switcher.py similarity index 70% rename from src/servers/presence_connection_manager/src/handlers/switcher.py rename to src/servers/presence_connection_manager/src/applications/switcher.py index 544027b95..3f9f03899 100644 --- a/src/servers/presence_connection_manager/src/handlers/switcher.py +++ b/src/servers/presence_connection_manager/src/applications/switcher.py @@ -1,16 +1,13 @@ from library.src.abstractions.switcher import SwitcherBase -from servers.presence_connection_manager.src.contracts.requests.buddy import StatusInfoRequest, StatusRequest -from servers.presence_connection_manager.src.contracts.requests.general import KeepAliveRequest, LoginRequest, LogoutRequest -from servers.presence_connection_manager.src.contracts.requests.profile import AddBlockRequest, GetProfileRequest, NewProfileRequest, RegisterCDKeyRequest, RegisterNickRequest, UpdateProfileRequest -from servers.presence_connection_manager.src.handlers.buddy import StatusHandler, StatusInfoHandler -from servers.presence_connection_manager.src.handlers.general import KeepAliveHandler, LoginHandler, LogoutHandler, NewUserHandler -from servers.presence_connection_manager.src.handlers.profile import AddBlockHandler, GetProfileHandler, NewProfileHandler, RegisterCDKeyHandler, RegisterNickHandler, UpdateProfileHandler -from servers.presence_connection_manager.src.contracts.requests import NewUserRequest -from servers.presence_search_player.src.exceptions.general import GPParseException +from servers.presence_connection_manager.src.contracts.requests import KeepAliveRequest, LoginRequest, LogoutRequest, StatusInfoRequest, StatusRequest, AddBlockRequest, GetProfileRequest, NewProfileRequest, RegisterCDKeyRequest, NewUserRequest, RegisterNickRequest, UpdateProfileRequest +from servers.presence_connection_manager.src.applications.handlers import AddBlockHandler, GetProfileHandler, KeepAliveHandler, LoginHandler, LogoutHandler, NewProfileHandler, NewUserHandler, RegisterCDKeyHandler, RegisterNickHandler, StatusHandler, StatusInfoHandler, UpdateProfileHandler +from servers.presence_search_player.src.aggregates.exceptions import GPParseException from servers.presence_connection_manager.src.abstractions.handlers import CmdHandlerBase from typing import TYPE_CHECKING, Optional, cast -from servers.presence_connection_manager.src.applications.client import Client + +if TYPE_CHECKING: + from servers.presence_connection_manager.src.applications.client import Client class Switcher(SwitcherBase): diff --git a/src/servers/presence_connection_manager/src/contracts/requests.py b/src/servers/presence_connection_manager/src/contracts/requests.py new file mode 100644 index 000000000..cd74c2095 --- /dev/null +++ b/src/servers/presence_connection_manager/src/contracts/requests.py @@ -0,0 +1,611 @@ +from servers.presence_connection_manager.src.aggregates.enums import PublicMasks +from library.src.extentions.gamespy_utils import is_valid_date +from typing import Optional, final +from library.src.extentions.gamespy_utils import convert_to_key_value +from servers.presence_connection_manager.src.abstractions.contracts import RequestBase +from servers.presence_connection_manager.src.aggregates.user_status import UserStatus +from servers.presence_connection_manager.src.aggregates.user_status_info import ( + UserStatusInfo, +) +from servers.presence_connection_manager.src.aggregates.enums import GPStatusCode +from servers.presence_search_player.src.aggregates.exceptions import GPParseException +from typing import final +from library.src.extentions.gamespy_utils import is_email_format_correct +from library.src.extentions.password_encoder import process_password +from servers.presence_connection_manager.src.abstractions.contracts import RequestBase +from servers.presence_connection_manager.src.aggregates.enums import ( + LoginType, + QuietModeType, + SdkRevisionType, +) +from servers.presence_search_player.src.aggregates.exceptions import ( + GPParseException, +) + +# region General + + +@final +class KeepAliveRequest(RequestBase): + pass + + +@final +class LoginRequest(RequestBase): + user_challenge: str + response: str + unique_nick: str + user_data: str + namespace_id: int + auth_token: str + nick: str + email: str + product_id: int + type: LoginType + sdk_revision_type: SdkRevisionType + game_port: int + user_id: int + profile_id: int + partner_id: int + game_name: str + quiet_mode_flags: int + firewall: bool + + def __init__(self, raw_request): + super().__init__(raw_request) + + def parse(self): + super().parse() + + if "challenge" not in self.request_dict: + raise GPParseException("challenge is missing") + + if "response" not in self.request_dict: + raise GPParseException("response is missing") + + self.user_challenge = self.request_dict["challenge"] + self.response = self.request_dict["response"] + + if ( + "uniquenick" in self.request_dict + and "namespaceid" in self.request_dict + ): + namespace_id = int(self.request_dict["namespaceid"]) + self.type = LoginType.UNIQUENICK_NAMESPACE_ID + self.unique_nick = self.request_dict["uniquenick"] + self.user_data = self.unique_nick + self.namespace_id = namespace_id + elif "authtoken" in self.request_dict: + self.type = LoginType.AUTH_TOKEN + self.auth_token = self.request_dict["authtoken"] + self.user_data = self.auth_token + elif "user" in self.request_dict: + self.type = LoginType.NICK_EMAIL + self.user_data = self.request_dict["user"] + pos = self.user_data.index("@") + if pos == -1 or pos < 1 or (pos + 1) >= len(self.user_data): + raise GPParseException("user format is incorrect") + self.nick = self.user_data[:pos] + self.email = self.user_data[pos + 1:] + if "namespaceid" in self.request_dict: + namespace_id = int(self.request_dict["namespaceid"]) + self.namespace_id = namespace_id + else: + raise GPParseException("Unknown login method detected.") + + self.parse_other_data() + + def parse_other_data(self): + if "userid" in self.request_dict: + user_id = int(self.request_dict["userid"]) + self.user_id = user_id + + if "profileid" in self.request_dict: + profile_id = int(self.request_dict["profileid"]) + self.profile_id = profile_id + + if "partnerid" in self.request_dict: + partner_id = int(self.request_dict["partnerid"]) + self.partner_id = partner_id + + if "sdkrevision" in self.request_dict: + sdk_revision_type = int(self.request_dict["sdkrevision"]) + self.sdk_revision_type = SdkRevisionType(sdk_revision_type) + + if "gamename" in self.request_dict: + self.game_name = self.request_dict["gamename"] + + if "port" in self.request_dict: + game_port = int(self.request_dict["port"]) + self.game_port = game_port + + if "productid" in self.request_dict: + product_id = int(self.request_dict["productid"]) + self.product_id = product_id + + if "firewall" in self.request_dict: + self.firewall = bool(self.request_dict["firewall"]) + + if "quiet" in self.request_dict: + quiet = int(self.request_dict["quiet"]) + self.quiet_mode_flags = QuietModeType(quiet) + + +@final +class LogoutRequest(RequestBase): + pass + + +@final +class NewUserRequest(RequestBase): + product_id: int + game_port: int + cd_key: str + has_game_name: bool + has_product_id: bool + has_cdkey: bool + has_partner_id: bool + has_game_port: bool + nick: str + email: str + password: str + partner_id: int + game_name: str + uniquenick: str + + def parse(self): + super().parse() + self.password = process_password(self.request_dict) + + if "nick" not in self.request_dict: + raise GPParseException("nickname is missing.") + if "email" not in self.request_dict: + raise GPParseException("email is missing.") + if not is_email_format_correct(self.request_dict["email"]): + raise GPParseException("email format is incorrect.") + self.nick = self.request_dict["nick"] + self.email = self.request_dict["email"] + + if "uniquenick" in self.request_dict and "namespaceid" in self.request_dict: + if "namespaceid" in self.request_dict: + try: + self.namespace_id = int(self.request_dict["namespaceid"]) + except ValueError: + raise GPParseException("namespaceid is incorrect.") + + self.uniquenick = self.request_dict["uniquenick"] + self.parse_other_info() + + def parse_other_info(self): + if "partnerid" in self.request_dict: + try: + self.partner_id = int(self.request_dict["partnerid"]) + self.has_partner_id_flag = True + except ValueError: + raise GPParseException("partnerid is incorrect.") + + if "productid" in self.request_dict: + try: + self.product_id = int(self.request_dict["productid"]) + self.has_product_id_flag = True + except ValueError: + raise GPParseException("productid is incorrect.") + + if "gamename" in self.request_dict: + self.has_game_name_flag = True + self.game_name = self.request_dict["gamename"] + + if "port" in self.request_dict: + try: + self.game_port = int(self.request_dict["port"]) + self.has_game_port_flag = True + except ValueError: + raise GPParseException("port is incorrect.") + + if "cdkey" in self.request_dict: + self.has_cd_key_enc_flag = True + self.cd_key = self.request_dict["cdkey"] + +# region Buddy + + +@final +class AddBuddyRequest(RequestBase): + friend_profile_id: int + reason: str + + def parse(self): + super().parse() + if ( + ("sesskey" not in self.request_dict) + or ("newprofileid" not in self.request_dict) + or ("reason" not in self.request_dict) + ): + raise GPParseException("addbuddy request is invalid.") + try: + self.friend_profile_id = int(self.request_dict["newprofileid"]) + except: + raise GPParseException("newprofileid format is incorrect.") + + self.reason = self.request_dict["reason"] + + +@final +class DelBuddyRequest(RequestBase): + friend_profile_id: int + + def parse(self): + super().parse() + if "delprofileid" not in self.request_dict: + raise GPParseException("delprofileid is missing.") + + try: + self.friend_profile_id = int(self.request_dict["delprofileid"]) + except: + raise GPParseException("delprofileid format is incorrect.") + + +@final +class InviteToRequest(RequestBase): + product_id: int + profile_id: int + session_key: str + """the invite target profile id""" + + def parse(self): + super().parse() + if "productid" not in self.request_dict: + raise GPParseException("productid is missing.") + + if "sesskey" not in self.request_dict: + raise GPParseException("sesskey is missing.") + + try: + self.product_id = int(self.request_dict["productid"]) + except ValueError: + raise GPParseException("productid format is incorrect.") + + try: + self.profile_id = int(self.request_dict["profileid"]) + except ValueError: + raise GPParseException("profileid format is incorrect.") + + self.session_key = self.request_dict["sesskey"] + + +@final +class StatusInfoRequest(RequestBase): + namespace_id: Optional[int] + status_info: UserStatusInfo + profile_id: int + + def __init__(self, raw_request: Optional[str] = None) -> None: + if raw_request is not None: + self.raw_request = raw_request + + def parse(self): + super().parse() + + if ( + "state" not in self.request_dict + or "hostip" not in self.request_dict + or "hprivip" not in self.request_dict + or "qport" not in self.request_dict + or "hport" not in self.request_dict + or "sessflags" not in self.request_dict + or "rechstatus" not in self.request_dict + or "gametype" not in self.request_dict + or "gamevariant" not in self.request_dict + or "gamemapname" not in self.request_dict + ): + raise GPParseException("StatusInfo request is invalid.") + + self.status_info.status_state = self.request_dict["state"] + self.status_info.host_ip = self.request_dict["hostip"] + self.status_info.host_private_ip = self.request_dict["hprivip"] + + try: + self.status_info.query_report_port = int( + self.request_dict["qport"]) + self.status_info.host_port = int(self.request_dict["hport"]) + self.status_info.session_flags = self.request_dict["sessflags"] + except ValueError: + raise GPParseException( + "qport, hport, or sessflags format is incorrect.") + + self.status_info.rich_status = self.request_dict["rechstatus"] + self.status_info.game_type = self.request_dict["gametype"] + self.status_info.game_variant = self.request_dict["gamevariant"] + self.status_info.game_map_name = self.request_dict["gamemapname"] + + +@final +class StatusRequest(RequestBase): + status: UserStatus + is_get_status: bool + + def parse(self): + self.request_dict = convert_to_key_value(self.raw_request) + self.command_name = list(self.request_dict.keys())[0] + + if "status" not in self.request_dict: + raise GPParseException("status is missing.") + + if "statstring" not in self.request_dict: + raise GPParseException("statstring is missing.") + + if "locstring" not in self.request_dict: + raise GPParseException("locstring is missing.") + + try: + status_code = int(self.request_dict["status"]) + self.status = UserStatus( + location_string=self.request_dict["locstring"], status_string=self.request_dict['statstring'], current_status=GPStatusCode(status_code)) + except ValueError: + raise GPParseException("status format is incorrect.") + + +# region Profile + +@final +class AddBlockRequest(RequestBase): + taget_id: int + + def parse(self): + super().parse() + + if "profileid" not in self.request_dict: + raise GPParseException("profileid is missing") + + try: + self.taget_id = int(self.request_dict["profileid"]) + except ValueError: + raise GPParseException("profileid format is incorrect") + + +@final +class GetProfileRequest(RequestBase): + profile_id: int + session_key: str + + def parse(self): + super().parse() + + if "profileid" not in self.request_dict: + raise GPParseException("profileid is missing") + + try: + self.profile_id = int(self.request_dict["profileid"]) + except ValueError: + raise GPParseException("profileid format is incorrect") + + if "sesskey" not in self.request_dict: + raise GPParseException("sesskey is missing") + + self.session_key = self.request_dict["sesskey"] + + +@final +class NewProfileRequest(RequestBase): + is_replace_nick_name: bool + session_key: str + new_nick: str + old_nick: str + + def parse(self): + super().parse() + + if "sesskey" not in self.request_dict: + raise GPParseException("sesskey is missing") + + self.session_key = self.request_dict["sesskey"] + + if "replace" in self.request_dict: + if ( + "oldnick" not in self.request_dict + and "nick" not in self.request_dict + ): + raise GPParseException("oldnick or nick is missing.") + + if "oldnick" in self.request_dict: + self.old_nick = self.request_dict["oldnick"] + if "nick" in self.request_dict: + self.new_nick = self.request_dict["nick"] + + self.is_replace_nick_name = True + else: + if "nick" not in self.request_dict: + raise GPParseException("nick is missing.") + + self.new_nick = self.request_dict["nick"] + self.is_replace_nick_name = False + + +@final +class RegisterCDKeyRequest(RequestBase): + session_key: str + cdkey_enc: str + + def parse(self): + super().parse() + + if "sesskey" not in self.request_dict: + raise GPParseException("sesskey is missing") + + self.session_key = self.request_dict["sesskey"] + + if "cdkeyenc" not in self.request_dict: + raise GPParseException("cdkeyenc is missing") + + self.cdkey_enc = self.request_dict["cdkeyenc"] + + +@final +class RegisterNickRequest(RequestBase): + unique_nick: str + session_key: str + partner_id: int + + def parse(self): + super().parse() + + if "sesskey" not in self.request_dict: + raise GPParseException("sesskey is missing") + + self.session_key = self.request_dict["sesskey"] + + if "uniquenick" not in self.request_dict: + raise GPParseException("uniquenick is missing") + + self.unique_nick = self.request_dict["uniquenick"] + + if "partnerid" in self.request_dict: + try: + self.partner_id = int(self.request_dict["partnerid"]) + except ValueError: + raise GPParseException("partnerid is missing") + + +@final +class UpdateProfileRequest(RequestBase): + has_public_mask_flag: Optional[bool] = None + public_mask: Optional[PublicMasks] = None + session_key: Optional[str] = None + partner_id: Optional[int] = None + nick: Optional[str] = None + uniquenick: Optional[str] = None + has_first_name_flag: Optional[bool] = None + first_name: Optional[str] = None + has_last_name_flag: Optional[bool] = None + last_name: Optional[str] = None + has_icq_flag: Optional[bool] = None + icq_uin: Optional[int] = None + has_home_page_flag: Optional[bool] = None + home_page: Optional[str] = None + has_birthday_flag: bool = False + birth_day: Optional[int] = None + birth_month: Optional[int] = None + birth_year: Optional[int] = None + has_sex_flag: bool = False + sex: Optional[int] = None + has_zip_code: bool = False + zip_code: Optional[str] = None + has_country_code: bool = False + country_code: Optional[str] = None + + def parse(self): + super().parse() + + if "publicmask" in self.request_dict: + if not self.request_dict["publicmask"].isdigit(): + raise GPParseException("publicmask format is incorrect") + self.has_public_mask_flag = True + self.public_mask = PublicMasks( + int(self.request_dict["publicmask"])) + + if "sesskey" not in self.request_dict: + raise GPParseException("sesskey is missing") + self.session_key = self.request_dict["sesskey"] + + if "firstname" in self.request_dict: + self.first_name = self.request_dict["firstname"] + self.has_first_name_flag = True + + if "lastname" in self.request_dict: + self.last_name = self.request_dict["lastname"] + self.has_last_name_flag = True + + if "icquin" in self.request_dict: + if not self.request_dict["icquin"].isdigit(): + raise GPParseException("icquin format is incorrect") + self.has_icq_flag = True + self.icq_uin = int(self.request_dict["icquin"]) + + # Remaining attribute assignments... + if "homepage" in self.request_dict: + self.home_page = self.request_dict["homepage"] + self.has_home_page_flag = True + + if "birthday" in self.request_dict: + try: + date = int(self.request_dict["birthday"]) + d = (date >> 24) & 0xFF + m = (date >> 16) & 0xFF + y = date & 0xFFFF + if is_valid_date(d, m, y): + self.birth_day = d + self.birth_month = m + self.birth_year = y + self.has_birthday_flag = True + except ValueError: + pass + + if "sex" in self.request_dict: + try: + self.sex = int(self.request_dict["sex"]) + self.has_sex_flag = True + except ValueError: + raise GPParseException("sex format is incorrect") + + if "zipcode" in self.request_dict: + self.zip_code = self.request_dict["zipcode"] + self.has_zip_code = True + + if "countrycode" in self.request_dict: + self.country_code = self.request_dict["countrycode"] + self.has_country_code = True + + if "partnerid" in self.request_dict: + try: + self.partner_id = int(self.request_dict["partnerid"]) + except ValueError: + raise GPParseException("partnerid is incorrect") + + if "nick" in self.request_dict: + self.nick = self.request_dict["nick"] + + if "uniquenick" in self.request_dict: + self.uniquenick = self.request_dict["uniquenick"] + + +@final +class UpdateUserInfoRequest(RequestBase): + cpubrandid: Optional[str] = None + cpuspeed: Optional[str] = None + memory: Optional[str] = None + videocard1ram: Optional[str] = None + videocard2ram: Optional[str] = None + connectionid: Optional[str] = None + connectionspeed: Optional[str] = None + hasnetwork: Optional[str] = None + pic: Optional[str] = None + + def parse(self): + super().parse() + + if "cpubrandid" in self.request_dict: + self.cpubrandid = self.request_dict["cpubrandid"] + + if "cpuspeed" in self.request_dict: + self.cpuspeed = self.request_dict["cpuspeed"] + + if "memory" in self.request_dict: + self.memory = self.request_dict["memory"] + + if "videocard1ram" in self.request_dict: + self.videocard1ram = self.request_dict["videocard1ram"] + + if "videocard2ram" in self.request_dict: + self.videocard2ram = self.request_dict["videocard2ram"] + + if "connectionid" in self.request_dict: + self.connectionid = self.request_dict["connectionid"] + + if "connectionspeed" in self.request_dict: + self.connectionspeed = self.request_dict["connectionspeed"] + + if "hasnetwork" in self.request_dict: + self.hasnetwork = self.request_dict["hasnetwork"] + + if "pic" in self.request_dict: + self.pic = self.request_dict["pic"] diff --git a/src/servers/presence_connection_manager/src/contracts/requests/buddy.py b/src/servers/presence_connection_manager/src/contracts/requests/buddy.py deleted file mode 100644 index f1bb9478a..000000000 --- a/src/servers/presence_connection_manager/src/contracts/requests/buddy.py +++ /dev/null @@ -1,145 +0,0 @@ -from typing import Optional, final -from library.src.extentions.gamespy_utils import convert_to_key_value -from servers.presence_connection_manager.src.abstractions.contracts import RequestBase -from servers.presence_connection_manager.src.aggregates.user_status import UserStatus -from servers.presence_connection_manager.src.aggregates.user_status_info import ( - UserStatusInfo, -) -from servers.presence_connection_manager.src.enums.general import GPStatusCode -from servers.presence_search_player.src.exceptions.general import GPParseException - - -@final -class AddBuddyRequest(RequestBase): - friend_profile_id: int - reason: str - - def parse(self): - super().parse() - if ( - ("sesskey" not in self.request_dict) - or ("newprofileid" not in self.request_dict) - or ("reason" not in self.request_dict) - ): - raise GPParseException("addbuddy request is invalid.") - try: - self.friend_profile_id = int(self.request_dict["newprofileid"]) - except: - raise GPParseException("newprofileid format is incorrect.") - - self.reason = self.request_dict["reason"] - - -@final -class DelBuddyRequest(RequestBase): - friend_profile_id: int - - def parse(self): - super().parse() - if "delprofileid" not in self.request_dict: - raise GPParseException("delprofileid is missing.") - - try: - self.friend_profile_id = int(self.request_dict["delprofileid"]) - except: - raise GPParseException("delprofileid format is incorrect.") - - -@final -class InviteToRequest(RequestBase): - product_id: int - profile_id: int - session_key: str - """the invite target profile id""" - - def parse(self): - super().parse() - if "productid" not in self.request_dict: - raise GPParseException("productid is missing.") - - if "sesskey" not in self.request_dict: - raise GPParseException("sesskey is missing.") - - try: - self.product_id = int(self.request_dict["productid"]) - except ValueError: - raise GPParseException("productid format is incorrect.") - - try: - self.profile_id = int(self.request_dict["profileid"]) - except ValueError: - raise GPParseException("profileid format is incorrect.") - - self.session_key = self.request_dict["sesskey"] - - -@final -class StatusInfoRequest(RequestBase): - namespace_id: Optional[int] - status_info: UserStatusInfo - profile_id: int - - def __init__(self, raw_request: Optional[str] = None) -> None: - if raw_request is not None: - self.raw_request = raw_request - - def parse(self): - super().parse() - - if ( - "state" not in self.request_dict - or "hostip" not in self.request_dict - or "hprivip" not in self.request_dict - or "qport" not in self.request_dict - or "hport" not in self.request_dict - or "sessflags" not in self.request_dict - or "rechstatus" not in self.request_dict - or "gametype" not in self.request_dict - or "gamevariant" not in self.request_dict - or "gamemapname" not in self.request_dict - ): - raise GPParseException("StatusInfo request is invalid.") - - self.status_info.status_state = self.request_dict["state"] - self.status_info.host_ip = self.request_dict["hostip"] - self.status_info.host_private_ip = self.request_dict["hprivip"] - - try: - self.status_info.query_report_port = int( - self.request_dict["qport"]) - self.status_info.host_port = int(self.request_dict["hport"]) - self.status_info.session_flags = self.request_dict["sessflags"] - except ValueError: - raise GPParseException( - "qport, hport, or sessflags format is incorrect.") - - self.status_info.rich_status = self.request_dict["rechstatus"] - self.status_info.game_type = self.request_dict["gametype"] - self.status_info.game_variant = self.request_dict["gamevariant"] - self.status_info.game_map_name = self.request_dict["gamemapname"] - - -@final -class StatusRequest(RequestBase): - status: UserStatus - is_get_status: bool - - def parse(self): - self.request_dict = convert_to_key_value(self.raw_request) - self.command_name = list(self.request_dict.keys())[0] - - if "status" not in self.request_dict: - raise GPParseException("status is missing.") - - if "statstring" not in self.request_dict: - raise GPParseException("statstring is missing.") - - if "locstring" not in self.request_dict: - raise GPParseException("locstring is missing.") - - try: - status_code = int(self.request_dict["status"]) - self.status = UserStatus( - location_string=self.request_dict["locstring"], status_string=self.request_dict['statstring'], current_status=GPStatusCode(status_code)) - except ValueError: - raise GPParseException("status format is incorrect.") diff --git a/src/servers/presence_connection_manager/src/contracts/requests/general.py b/src/servers/presence_connection_manager/src/contracts/requests/general.py deleted file mode 100644 index 049752f06..000000000 --- a/src/servers/presence_connection_manager/src/contracts/requests/general.py +++ /dev/null @@ -1,194 +0,0 @@ -from typing import final -from library.src.extentions.gamespy_utils import is_email_format_correct -from library.src.extentions.password_encoder import process_password -from servers.presence_connection_manager.src.abstractions.contracts import RequestBase -from servers.presence_connection_manager.src.enums.general import ( - LoginType, - QuietModeType, - SdkRevisionType, -) -from servers.presence_search_player.src.exceptions.general import ( - GPParseException, -) - - -@final -class KeepAliveRequest(RequestBase): - pass - - -@final -class LoginRequest(RequestBase): - user_challenge: str - response: str - unique_nick: str - user_data: str - namespace_id: int - auth_token: str - nick: str - email: str - product_id: int - type: LoginType - sdk_revision_type: SdkRevisionType - game_port: int - user_id: int - profile_id: int - partner_id: int - game_name: str - quiet_mode_flags: int - firewall: bool - - def __init__(self, raw_request): - super().__init__(raw_request) - - def parse(self): - super().parse() - - if "challenge" not in self.request_dict: - raise GPParseException("challenge is missing") - - if "response" not in self.request_dict: - raise GPParseException("response is missing") - - self.user_challenge = self.request_dict["challenge"] - self.response = self.request_dict["response"] - - if ( - "uniquenick" in self.request_dict - and "namespaceid" in self.request_dict - ): - namespace_id = int(self.request_dict["namespaceid"]) - self.type = LoginType.UNIQUENICK_NAMESPACE_ID - self.unique_nick = self.request_dict["uniquenick"] - self.user_data = self.unique_nick - self.namespace_id = namespace_id - elif "authtoken" in self.request_dict: - self.type = LoginType.AUTH_TOKEN - self.auth_token = self.request_dict["authtoken"] - self.user_data = self.auth_token - elif "user" in self.request_dict: - self.type = LoginType.NICK_EMAIL - self.user_data = self.request_dict["user"] - pos = self.user_data.index("@") - if pos == -1 or pos < 1 or (pos + 1) >= len(self.user_data): - raise GPParseException("user format is incorrect") - self.nick = self.user_data[:pos] - self.email = self.user_data[pos + 1:] - if "namespaceid" in self.request_dict: - namespace_id = int(self.request_dict["namespaceid"]) - self.namespace_id = namespace_id - else: - raise GPParseException("Unknown login method detected.") - - self.parse_other_data() - - def parse_other_data(self): - if "userid" in self.request_dict: - user_id = int(self.request_dict["userid"]) - self.user_id = user_id - - if "profileid" in self.request_dict: - profile_id = int(self.request_dict["profileid"]) - self.profile_id = profile_id - - if "partnerid" in self.request_dict: - partner_id = int(self.request_dict["partnerid"]) - self.partner_id = partner_id - - if "sdkrevision" in self.request_dict: - sdk_revision_type = int(self.request_dict["sdkrevision"]) - self.sdk_revision_type = SdkRevisionType(sdk_revision_type) - - if "gamename" in self.request_dict: - self.game_name = self.request_dict["gamename"] - - if "port" in self.request_dict: - game_port = int(self.request_dict["port"]) - self.game_port = game_port - - if "productid" in self.request_dict: - product_id = int(self.request_dict["productid"]) - self.product_id = product_id - - if "firewall" in self.request_dict: - self.firewall = bool(self.request_dict["firewall"]) - - if "quiet" in self.request_dict: - quiet = int(self.request_dict["quiet"]) - self.quiet_mode_flags = QuietModeType(quiet) - - -@final -class LogoutRequest(RequestBase): - pass - - -@final -class NewUserRequest(RequestBase): - product_id: int - game_port: int - cd_key: str - has_game_name: bool - has_product_id: bool - has_cdkey: bool - has_partner_id: bool - has_game_port: bool - nick: str - email: str - password: str - partner_id: int - game_name: str - uniquenick: str - - def parse(self): - super().parse() - self.password = process_password(self.request_dict) - - if "nick" not in self.request_dict: - raise GPParseException("nickname is missing.") - if "email" not in self.request_dict: - raise GPParseException("email is missing.") - if not is_email_format_correct(self.request_dict["email"]): - raise GPParseException("email format is incorrect.") - self.nick = self.request_dict["nick"] - self.email = self.request_dict["email"] - - if "uniquenick" in self.request_dict and "namespaceid" in self.request_dict: - if "namespaceid" in self.request_dict: - try: - self.namespace_id = int(self.request_dict["namespaceid"]) - except ValueError: - raise GPParseException("namespaceid is incorrect.") - - self.uniquenick = self.request_dict["uniquenick"] - self.parse_other_info() - - def parse_other_info(self): - if "partnerid" in self.request_dict: - try: - self.partner_id = int(self.request_dict["partnerid"]) - self.has_partner_id_flag = True - except ValueError: - raise GPParseException("partnerid is incorrect.") - - if "productid" in self.request_dict: - try: - self.product_id = int(self.request_dict["productid"]) - self.has_product_id_flag = True - except ValueError: - raise GPParseException("productid is incorrect.") - - if "gamename" in self.request_dict: - self.has_game_name_flag = True - self.game_name = self.request_dict["gamename"] - - if "port" in self.request_dict: - try: - self.game_port = int(self.request_dict["port"]) - self.has_game_port_flag = True - except ValueError: - raise GPParseException("port is incorrect.") - - if "cdkey" in self.request_dict: - self.has_cd_key_enc_flag = True - self.cd_key = self.request_dict["cdkey"] diff --git a/src/servers/presence_connection_manager/src/contracts/requests/profile.py b/src/servers/presence_connection_manager/src/contracts/requests/profile.py deleted file mode 100644 index eb3d88e85..000000000 --- a/src/servers/presence_connection_manager/src/contracts/requests/profile.py +++ /dev/null @@ -1,271 +0,0 @@ -from typing import Optional, final -from library.src.extentions.gamespy_utils import is_valid_date -from servers.presence_connection_manager.src.abstractions.contracts import RequestBase -from servers.presence_connection_manager.src.enums.general import PublicMasks -from servers.presence_search_player.src.exceptions.general import ( - GPParseException, -) - - -@final -class AddBlockRequest(RequestBase): - taget_id: int - - def parse(self): - super().parse() - - if "profileid" not in self.request_dict: - raise GPParseException("profileid is missing") - - try: - self.taget_id = int(self.request_dict["profileid"]) - except ValueError: - raise GPParseException("profileid format is incorrect") - - -@final -class GetProfileRequest(RequestBase): - profile_id: int - session_key: str - - def parse(self): - super().parse() - - if "profileid" not in self.request_dict: - raise GPParseException("profileid is missing") - - try: - self.profile_id = int(self.request_dict["profileid"]) - except ValueError: - raise GPParseException("profileid format is incorrect") - - if "sesskey" not in self.request_dict: - raise GPParseException("sesskey is missing") - - self.session_key = self.request_dict["sesskey"] - - -@final -class NewProfileRequest(RequestBase): - is_replace_nick_name: bool - session_key: str - new_nick: str - old_nick: str - - def parse(self): - super().parse() - - if "sesskey" not in self.request_dict: - raise GPParseException("sesskey is missing") - - self.session_key = self.request_dict["sesskey"] - - if "replace" in self.request_dict: - if ( - "oldnick" not in self.request_dict - and "nick" not in self.request_dict - ): - raise GPParseException("oldnick or nick is missing.") - - if "oldnick" in self.request_dict: - self.old_nick = self.request_dict["oldnick"] - if "nick" in self.request_dict: - self.new_nick = self.request_dict["nick"] - - self.is_replace_nick_name = True - else: - if "nick" not in self.request_dict: - raise GPParseException("nick is missing.") - - self.new_nick = self.request_dict["nick"] - self.is_replace_nick_name = False - - -@final -class RegisterCDKeyRequest(RequestBase): - session_key: str - cdkey_enc: str - - def parse(self): - super().parse() - - if "sesskey" not in self.request_dict: - raise GPParseException("sesskey is missing") - - self.session_key = self.request_dict["sesskey"] - - if "cdkeyenc" not in self.request_dict: - raise GPParseException("cdkeyenc is missing") - - self.cdkey_enc = self.request_dict["cdkeyenc"] - - -@final -class RegisterNickRequest(RequestBase): - unique_nick: str - session_key: str - partner_id: int - - def parse(self): - super().parse() - - if "sesskey" not in self.request_dict: - raise GPParseException("sesskey is missing") - - self.session_key = self.request_dict["sesskey"] - - if "uniquenick" not in self.request_dict: - raise GPParseException("uniquenick is missing") - - self.unique_nick = self.request_dict["uniquenick"] - - if "partnerid" in self.request_dict: - try: - self.partner_id = int(self.request_dict["partnerid"]) - except ValueError: - raise GPParseException("partnerid is missing") - - -@final -class UpdateProfileRequest(RequestBase): - has_public_mask_flag: Optional[bool] = None - public_mask: Optional[PublicMasks] = None - session_key: Optional[str] = None - partner_id: Optional[int] = None - nick: Optional[str] = None - uniquenick: Optional[str] = None - has_first_name_flag: Optional[bool] = None - first_name: Optional[str] = None - has_last_name_flag: Optional[bool] = None - last_name: Optional[str] = None - has_icq_flag: Optional[bool] = None - icq_uin: Optional[int] = None - has_home_page_flag: Optional[bool] = None - home_page: Optional[str] = None - has_birthday_flag: bool = False - birth_day: Optional[int] = None - birth_month: Optional[int] = None - birth_year: Optional[int] = None - has_sex_flag: bool = False - sex: Optional[int] = None - has_zip_code: bool = False - zip_code: Optional[str] = None - has_country_code: bool = False - country_code: Optional[str] = None - - def parse(self): - super().parse() - - if "publicmask" in self.request_dict: - if not self.request_dict["publicmask"].isdigit(): - raise GPParseException("publicmask format is incorrect") - self.has_public_mask_flag = True - self.public_mask = PublicMasks( - int(self.request_dict["publicmask"])) - - if "sesskey" not in self.request_dict: - raise GPParseException("sesskey is missing") - self.session_key = self.request_dict["sesskey"] - - if "firstname" in self.request_dict: - self.first_name = self.request_dict["firstname"] - self.has_first_name_flag = True - - if "lastname" in self.request_dict: - self.last_name = self.request_dict["lastname"] - self.has_last_name_flag = True - - if "icquin" in self.request_dict: - if not self.request_dict["icquin"].isdigit(): - raise GPParseException("icquin format is incorrect") - self.has_icq_flag = True - self.icq_uin = int(self.request_dict["icquin"]) - - # Remaining attribute assignments... - if "homepage" in self.request_dict: - self.home_page = self.request_dict["homepage"] - self.has_home_page_flag = True - - if "birthday" in self.request_dict: - try: - date = int(self.request_dict["birthday"]) - d = (date >> 24) & 0xFF - m = (date >> 16) & 0xFF - y = date & 0xFFFF - if is_valid_date(d, m, y): - self.birth_day = d - self.birth_month = m - self.birth_year = y - self.has_birthday_flag = True - except ValueError: - pass - - if "sex" in self.request_dict: - try: - self.sex = int(self.request_dict["sex"]) - self.has_sex_flag = True - except ValueError: - raise GPParseException("sex format is incorrect") - - if "zipcode" in self.request_dict: - self.zip_code = self.request_dict["zipcode"] - self.has_zip_code = True - - if "countrycode" in self.request_dict: - self.country_code = self.request_dict["countrycode"] - self.has_country_code = True - - if "partnerid" in self.request_dict: - try: - self.partner_id = int(self.request_dict["partnerid"]) - except ValueError: - raise GPParseException("partnerid is incorrect") - - if "nick" in self.request_dict: - self.nick = self.request_dict["nick"] - - if "uniquenick" in self.request_dict: - self.uniquenick = self.request_dict["uniquenick"] - - -@final -class UpdateUserInfoRequest(RequestBase): - cpubrandid: Optional[str] = None - cpuspeed: Optional[str] = None - memory: Optional[str] = None - videocard1ram: Optional[str] = None - videocard2ram: Optional[str] = None - connectionid: Optional[str] = None - connectionspeed: Optional[str] = None - hasnetwork: Optional[str] = None - pic: Optional[str] = None - - def parse(self): - super().parse() - - if "cpubrandid" in self.request_dict: - self.cpubrandid = self.request_dict["cpubrandid"] - - if "cpuspeed" in self.request_dict: - self.cpuspeed = self.request_dict["cpuspeed"] - - if "memory" in self.request_dict: - self.memory = self.request_dict["memory"] - - if "videocard1ram" in self.request_dict: - self.videocard1ram = self.request_dict["videocard1ram"] - - if "videocard2ram" in self.request_dict: - self.videocard2ram = self.request_dict["videocard2ram"] - - if "connectionid" in self.request_dict: - self.connectionid = self.request_dict["connectionid"] - - if "connectionspeed" in self.request_dict: - self.connectionspeed = self.request_dict["connectionspeed"] - - if "hasnetwork" in self.request_dict: - self.hasnetwork = self.request_dict["hasnetwork"] - - if "pic" in self.request_dict: - self.pic = self.request_dict["pic"] diff --git a/src/servers/presence_connection_manager/src/contracts/responses.py b/src/servers/presence_connection_manager/src/contracts/responses.py new file mode 100644 index 000000000..9b526b11d --- /dev/null +++ b/src/servers/presence_connection_manager/src/contracts/responses.py @@ -0,0 +1,242 @@ +from servers.presence_connection_manager.src.abstractions.contracts import ResponseBase +from servers.presence_connection_manager.src.applications.client import ( + LOGIN_TICKET, + SESSION_KEY, +) +from servers.presence_connection_manager.src.contracts.requests import ( + KeepAliveRequest, + LoginRequest, + NewUserRequest, +) +from servers.presence_connection_manager.src.contracts.results import ( + LoginResult, + NewUserResult, +) + +# region General + + +class KeepAliveResponse(ResponseBase): + def __init__(self, request: KeepAliveRequest) -> None: + super().__init__(request, None) + + def build(self) -> None: + self.sending_buffer = "\\ka\\final\\" + + +class LoginResponse(ResponseBase): + _result: LoginResult + _request: LoginRequest + + def __init__(self, request: LoginRequest, result: LoginResult): + super().__init__(request, result) + assert isinstance(request, LoginRequest) + assert isinstance(result, LoginResult) + + def build(self): + # string checkSumStr = _result.DatabaseResults.Nick + _result.DatabaseResults.UniqueNick + _result.DatabaseResults.NamespaceID; + # _connection.UserData.SessionKey = _crc.ComputeChecksum(checkSumStr); + + self.sending_buffer = f"\\lc\\2\\sesskey\\{SESSION_KEY}\\proof\\{self._result.response_proof}\\userid\\{ + self._result.data.user_id}\\profileid\\{self._result.data.profile_id}" + + if self._result.data.unique_nick is not None: + self.sending_buffer += "\\uniquenick\\" + self._result.data.unique_nick + + self.sending_buffer += f"\\lt\\{LOGIN_TICKET}" + self.sending_buffer += f"\\id\\{self._request.operation_id}\\final\\" + + +class NewUserResponse(ResponseBase): + _request: NewUserRequest + _result: NewUserResult + + def __init__(self, request: NewUserRequest, result: NewUserResult) -> None: + super().__init__(request, result) + assert isinstance(request, NewUserRequest) + assert isinstance(result, NewUserResult) + + def build(self): + # fmt: on + self.sending_buffer = f"\\nur\\userid\\{self._result.user_id}\\profileid\\{self._result.profile_id}\\id\\{self._request.operation_id}\\final\\" # fmt: off + + +# region Buddy + +from servers.presence_connection_manager.src.abstractions.contracts import ( + RequestBase, + ResponseBase, +) +from servers.presence_connection_manager.src.contracts.requests import AddBuddyRequest, StatusInfoRequest +from servers.presence_connection_manager.src.contracts.results import ( + AddBuddyResult, + BlockListResult, + BuddyListResult, + StatusInfoResult, +) + +class AddBuddyResponse(ResponseBase): + def __init__(self, request: AddBuddyRequest, result: AddBuddyResult) -> None: + assert issubclass(type(request), AddBuddyRequest) + assert issubclass(type(result), AddBuddyResult) + super().__init__(request, result) + + def build(self) -> None: + # return super().build() + raise NotImplementedError() + # \bm\\f\\date\ + # GPI_BM_MESSAGE: \msg\\ + # GPI_BM_UTM:\msg\\ + # GPI_BM_REQUEST:\msg\|signed|\ + # GPI_BM_AUTH: + # GPI_BM_REVOKE: + # GPI_BM_STATUS:\msg\|s|\ or \msg\|ss||ls||ip||p||qm| + # GPI_BM_INVITE:\msg\|p||l| + # GPI_BM_PING:\msg\\ + + +class BlockListResponse(ResponseBase): + _result: BlockListResult + + def __init__(self, result: BlockListResult): + assert isinstance(result, BlockListResult) + self._result = result + + def build(self): + # \blk\< num in list >\list\< profileid list - comma delimited >\final\ + self.sending_buffer = f"\\blk\\{len(self._result.profile_ids)}\\list\\" + self.sending_buffer += ",".join(str(pid) for pid in self._result.profile_ids) + self.sending_buffer += "\\final\\" + + +class BuddyListResponse(ResponseBase): + _result: BuddyListResult + + def __init__(self, request: RequestBase, result: BuddyListResult): + super().__init__(request, result) + + def build(self): + # \bdy\< num in list >\list\< profileid list - comma delimited >\final\ + self.sending_buffer = f"\\bdy\\{len(self._result.profile_ids)}\\list\\" + self.sending_buffer += ",".join(str(pid) for pid in self._result.profile_ids) + self.sending_buffer += "\\final\\" + + +class StatusInfoResponse(ResponseBase): + _result: StatusInfoResult + + def __init__(self, request: StatusInfoRequest, result: StatusInfoResult): + assert isinstance(request, StatusInfoRequest) + assert isinstance(result, StatusInfoResult) + super().__init__(request, result) + + def build(self): + # \bsi\\state\\profile\\bip\\bport\\hostip\\hprivip\\qport\\hport\\sessflags\\rstatus\\gameType\\gameVnt\\gameMn\\product\\qmodeflags\ + self.sending_buffer = ( + f"\\bsi\\state\\{self._result.status_info.status_state}\\" + f"profile\\{self._result.profile_id}\\bip\\{self._result.status_info.buddy_ip}\\" + f"hostIp\\{self._result.status_info.host_ip}\\hprivIp\\{self._result.status_info.host_private_ip}\\" + f"qport\\{self._result.status_info.query_report_port}\\hport\\{self._result.status_info.host_port}\\" + f"sessflags\\{self._result.status_info.session_flags}\\rstatus\\{self._result.status_info.rich_status}\\" + f"gameType\\{self._result.status_info.game_type}\\gameVnt\\{self._result.status_info.game_variant}\\" + f"gameMn\\{self._result.status_info.game_map_name}\\product\\{self._result.product_id}\\" + f"qmodeflags\\{self._result.status_info.quiet_mode_flags}\\final\\" + ) + +# region Profile +from library.src.extentions.gamespy_ramdoms import StringType, generate_random_string +from servers.presence_connection_manager.src.abstractions.contracts import ResponseBase +from servers.presence_connection_manager.src.contracts.requests import ( + GetProfileRequest, + NewProfileRequest, + RegisterNickRequest, +) +from servers.presence_connection_manager.src.contracts.results import ( + GetProfileResult, + NewProfileResult, +) + +class GetProfileResponse(ResponseBase): + _result: GetProfileResult + _request: GetProfileRequest + + def __init__(self, request: GetProfileRequest, result: GetProfileResult): + assert isinstance(request, GetProfileRequest) + assert isinstance(result, GetProfileResult) + super().__init__(request, result) + + def build(self): + self.sending_buffer = ( + f"\\pi\\profileid\\{self._result.user_profile.profile_id}" + + f"\\nick\\{self._result.user_profile.nick}" + + f"\\uniquenick\\{self._result.user_profile.unique_nick}" + + f"\\email\\{self._result.user_profile.email}" + + f"\\firstname\\{self._result.user_profile.firstname}" + + f"\\lastname\\{self._result.user_profile.lastname}" + + f"\\icquin\\{self._result.user_profile.icquin}" + + f"\\homepage\\{self._result.user_profile.homepage}" + + f"\\zipcode\\{self._result.user_profile.zipcode}" + + f"\\countrycode\\{self._result.user_profile.countrycode}" + + f"\\lon\\{self._result.user_profile.longitude}" + + f"\\lat\\{self._result.user_profile.latitude}" + + f"\\loc\\{self._result.user_profile.location}" + ) + + birth_str = ( + (self._result.user_profile.birthday << 24) + or (self._result.user_profile.birthmonth << 16) + or self._result.user_profile.birthyear + ) + self.sending_buffer += ( + f"\\birthday\\{birth_str}" + f"\\sex\\{self._result.user_profile.sex}" + + f"\\publicmask\\{self._result.user_profile.publicmask}" + + f"\\aim\\{self._result.user_profile.aim}" + + f"\\picture\\{self._result.user_profile.picture}" + + f"\\ooc{self._result.user_profile.occupationid}" + + f"\\ind\\{self._result.user_profile.industryid}" + + f"\\inc\\{self._result.user_profile.incomeid}" + + f"\\mar\\{self._result.user_profile.marriedid}" + + f"\\chc\\{self._result.user_profile.childcount}" + + f"\\i1\\{self._result.user_profile.interests1}" + + f"\\o1\\{self._result.user_profile.ownership1}" + + f"\\conn\\{self._result.user_profile.connectiontype}" + + f"\\sig\\+{generate_random_string(10, StringType.HEX)}" + + f"\\id\\{self._request.operation_id}\\final\\" + ) + + +class NewProfileResponse(ResponseBase): + _request: NewProfileRequest + _result: NewProfileResult + + def __init__(self, request: NewProfileRequest, result: NewProfileResult): + assert isinstance(request, NewProfileRequest) + assert isinstance(result, NewProfileResult) + super().__init__(request, result) + + def build(self): + # fmt: off + self.sending_buffer = f"\\npr\\\\profileid\\{self.sending_buffer}\\id\\{self._request.operation_id}\\final\\" + # fmt: on + + +class RegisterCDKeyResposne(ResponseBase): + def __init__(self) -> None: + pass + + def build(self): + self.sending_buffer = "\\rc\\\\final\\" + + +class RegisterNickResponse(ResponseBase): + _request: RegisterNickRequest + + def __init__(self, request: RegisterNickRequest) -> None: + assert isinstance(request, RegisterNickRequest) + self._request = request + + def build(self) -> None: + # fmt: off + self.sending_buffer = f"\\rn\\\\id\\{self._request.operation_id}\\final\\" + # fmt: on diff --git a/src/servers/presence_connection_manager/src/contracts/responses/buddy.py b/src/servers/presence_connection_manager/src/contracts/responses/buddy.py deleted file mode 100644 index f8636da18..000000000 --- a/src/servers/presence_connection_manager/src/contracts/responses/buddy.py +++ /dev/null @@ -1,80 +0,0 @@ -from servers.presence_connection_manager.src.abstractions.contracts import ( - RequestBase, - ResponseBase, -) -from servers.presence_connection_manager.src.contracts.requests.buddy import AddBuddyRequest, StatusInfoRequest -from servers.presence_connection_manager.src.contracts.results.buddy import ( - AddBuddyResult, - BlockListResult, - BuddyListResult, - StatusInfoResult, -) - - -class AddBuddyResponse(ResponseBase): - def __init__(self, request: AddBuddyRequest, result: AddBuddyResult) -> None: - assert issubclass(type(request), AddBuddyRequest) - assert issubclass(type(result), AddBuddyResult) - super().__init__(request, result) - - def build(self) -> None: - # return super().build() - raise NotImplementedError() - # \bm\\f\\date\ - # GPI_BM_MESSAGE: \msg\\ - # GPI_BM_UTM:\msg\\ - # GPI_BM_REQUEST:\msg\|signed|\ - # GPI_BM_AUTH: - # GPI_BM_REVOKE: - # GPI_BM_STATUS:\msg\|s|\ or \msg\|ss||ls||ip||p||qm| - # GPI_BM_INVITE:\msg\|p||l| - # GPI_BM_PING:\msg\\ - - -class BlockListResponse(ResponseBase): - _result: BlockListResult - - def __init__(self, result: BlockListResult): - assert isinstance(result, BlockListResult) - self._result = result - - def build(self): - # \blk\< num in list >\list\< profileid list - comma delimited >\final\ - self.sending_buffer = f"\\blk\\{len(self._result.profile_ids)}\\list\\" - self.sending_buffer += ",".join(str(pid) for pid in self._result.profile_ids) - self.sending_buffer += "\\final\\" - - -class BuddyListResponse(ResponseBase): - _result: BuddyListResult - - def __init__(self, request: RequestBase, result: BuddyListResult): - super().__init__(request, result) - - def build(self): - # \bdy\< num in list >\list\< profileid list - comma delimited >\final\ - self.sending_buffer = f"\\bdy\\{len(self._result.profile_ids)}\\list\\" - self.sending_buffer += ",".join(str(pid) for pid in self._result.profile_ids) - self.sending_buffer += "\\final\\" - - -class StatusInfoResponse(ResponseBase): - _result: StatusInfoResult - - def __init__(self, request: StatusInfoRequest, result: StatusInfoResult): - assert isinstance(request, StatusInfoRequest) - assert isinstance(result, StatusInfoResult) - super().__init__(request, result) - - def build(self): - # \bsi\\state\\profile\\bip\\bport\\hostip\\hprivip\\qport\\hport\\sessflags\\rstatus\\gameType\\gameVnt\\gameMn\\product\\qmodeflags\ - self.sending_buffer = ( - f"\\bsi\\state\\{self._result.status_info.status_state}\\" - f"profile\\{self._result.profile_id}\\bip\\{self._result.status_info.buddy_ip}\\" - f"hostIp\\{self._result.status_info.host_ip}\\hprivIp\\{self._result.status_info.host_private_ip}\\" - f"qport\\{self._result.status_info.query_report_port}\\hport\\{self._result.status_info.host_port}\\" - f"sessflags\\{self._result.status_info.session_flags}\\rstatus\\{self._result.status_info.rich_status}\\" - f"gameType\\{self._result.status_info.game_type}\\gameVnt\\{self._result.status_info.game_variant}\\" - f"gameMn\\{self._result.status_info.game_map_name}\\product\\{self._result.product_id}\\" - f"qmodeflags\\{self._result.status_info.quiet_mode_flags}\\final\\" - ) diff --git a/src/servers/presence_connection_manager/src/contracts/responses/general.py b/src/servers/presence_connection_manager/src/contracts/responses/general.py deleted file mode 100644 index 7387a4623..000000000 --- a/src/servers/presence_connection_manager/src/contracts/responses/general.py +++ /dev/null @@ -1,61 +0,0 @@ -from servers.presence_connection_manager.src.abstractions.contracts import ResponseBase -from servers.presence_connection_manager.src.applications.client import ( - LOGIN_TICKET, - SESSION_KEY, -) -from servers.presence_connection_manager.src.contracts.requests.general import ( - KeepAliveRequest, - LoginRequest, - NewUserRequest, -) -from servers.presence_connection_manager.src.contracts.results.general import ( - LoginResult, - NewUserResult, -) - - -class KeepAliveResponse(ResponseBase): - def __init__(self, request: KeepAliveRequest) -> None: - super().__init__(request, None) - - def build(self) -> None: - self.sending_buffer = "\\ka\\final\\" - - -class LoginResponse(ResponseBase): - _result: LoginResult - _request: LoginRequest - - def __init__(self, request: LoginRequest, result: LoginResult): - super().__init__(request, result) - assert isinstance(request, LoginRequest) - assert isinstance(result, LoginResult) - - def build(self): - # string checkSumStr = _result.DatabaseResults.Nick + _result.DatabaseResults.UniqueNick + _result.DatabaseResults.NamespaceID; - # _connection.UserData.SessionKey = _crc.ComputeChecksum(checkSumStr); - - self.sending_buffer = f"\\lc\\2\\sesskey\\{SESSION_KEY}\\proof\\{self._result.response_proof}\\userid\\{ - self._result.data.user_id}\\profileid\\{self._result.data.profile_id}" - - if self._result.data.unique_nick is not None: - self.sending_buffer += "\\uniquenick\\" + self._result.data.unique_nick - - self.sending_buffer += f"\\lt\\{LOGIN_TICKET}" - self.sending_buffer += f"\\id\\{self._request.operation_id}\\final\\" - - -class NewUserResponse(ResponseBase): - _request: NewUserRequest - _result: NewUserResult - - def __init__(self, request: NewUserRequest, result: NewUserResult) -> None: - super().__init__(request, result) - assert isinstance(request, NewUserRequest) - assert isinstance(result, NewUserResult) - - def build(self): - # fmt: on - self.sending_buffer = f"\\nur\\userid\\{self._result.user_id}\\profileid\\{self._result.profile_id}\\id\\{self._request.operation_id}\\final\\" # fmt: off - - diff --git a/src/servers/presence_connection_manager/src/contracts/responses/profile.py b/src/servers/presence_connection_manager/src/contracts/responses/profile.py deleted file mode 100644 index cc658fb50..000000000 --- a/src/servers/presence_connection_manager/src/contracts/responses/profile.py +++ /dev/null @@ -1,97 +0,0 @@ -from library.src.extentions.gamespy_ramdoms import StringType, generate_random_string -from servers.presence_connection_manager.src.abstractions.contracts import ResponseBase -from servers.presence_connection_manager.src.contracts.requests.profile import ( - GetProfileRequest, - NewProfileRequest, - RegisterNickRequest, -) -from servers.presence_connection_manager.src.contracts.results.profile import ( - GetProfileResult, - NewProfileResult, -) - - -class GetProfileResponse(ResponseBase): - _result: GetProfileResult - _request: GetProfileRequest - - def __init__(self, request: GetProfileRequest, result: GetProfileResult): - assert isinstance(request, GetProfileRequest) - assert isinstance(result, GetProfileResult) - super().__init__(request, result) - - def build(self): - self.sending_buffer = ( - f"\\pi\\profileid\\{self._result.user_profile.profile_id}" - + f"\\nick\\{self._result.user_profile.nick}" - + f"\\uniquenick\\{self._result.user_profile.unique_nick}" - + f"\\email\\{self._result.user_profile.email}" - + f"\\firstname\\{self._result.user_profile.firstname}" - + f"\\lastname\\{self._result.user_profile.lastname}" - + f"\\icquin\\{self._result.user_profile.icquin}" - + f"\\homepage\\{self._result.user_profile.homepage}" - + f"\\zipcode\\{self._result.user_profile.zipcode}" - + f"\\countrycode\\{self._result.user_profile.countrycode}" - + f"\\lon\\{self._result.user_profile.longitude}" - + f"\\lat\\{self._result.user_profile.latitude}" - + f"\\loc\\{self._result.user_profile.location}" - ) - - birth_str = ( - (self._result.user_profile.birthday << 24) - or (self._result.user_profile.birthmonth << 16) - or self._result.user_profile.birthyear - ) - self.sending_buffer += ( - f"\\birthday\\{birth_str}" - f"\\sex\\{self._result.user_profile.sex}" - + f"\\publicmask\\{self._result.user_profile.publicmask}" - + f"\\aim\\{self._result.user_profile.aim}" - + f"\\picture\\{self._result.user_profile.picture}" - + f"\\ooc{self._result.user_profile.occupationid}" - + f"\\ind\\{self._result.user_profile.industryid}" - + f"\\inc\\{self._result.user_profile.incomeid}" - + f"\\mar\\{self._result.user_profile.marriedid}" - + f"\\chc\\{self._result.user_profile.childcount}" - + f"\\i1\\{self._result.user_profile.interests1}" - + f"\\o1\\{self._result.user_profile.ownership1}" - + f"\\conn\\{self._result.user_profile.connectiontype}" - + f"\\sig\\+{generate_random_string(10, StringType.HEX)}" - + f"\\id\\{self._request.operation_id}\\final\\" - ) - - -class NewProfileResponse(ResponseBase): - _request: NewProfileRequest - _result: NewProfileResult - - def __init__(self, request: NewProfileRequest, result: NewProfileResult): - assert isinstance(request, NewProfileRequest) - assert isinstance(result, NewProfileResult) - super().__init__(request, result) - - def build(self): - # fmt: off - self.sending_buffer = f"\\npr\\\\profileid\\{self.sending_buffer}\\id\\{self._request.operation_id}\\final\\" - # fmt: on - - -class RegisterCDKeyResposne(ResponseBase): - def __init__(self) -> None: - pass - - def build(self): - self.sending_buffer = "\\rc\\\\final\\" - - -class RegisterNickResponse(ResponseBase): - _request: RegisterNickRequest - - def __init__(self, request: RegisterNickRequest) -> None: - assert isinstance(request, RegisterNickRequest) - self._request = request - - def build(self) -> None: - # fmt: off - self.sending_buffer = f"\\rn\\\\id\\{self._request.operation_id}\\final\\" - # fmt: on diff --git a/src/servers/presence_connection_manager/src/contracts/results.py b/src/servers/presence_connection_manager/src/contracts/results.py new file mode 100644 index 000000000..c467d95a4 --- /dev/null +++ b/src/servers/presence_connection_manager/src/contracts/results.py @@ -0,0 +1,101 @@ +from typing import Optional +from servers.presence_connection_manager.src.aggregates.user_status_info import ( + UserStatusInfo, +) +from servers.presence_connection_manager.src.aggregates.user_status import UserStatus +from pydantic import BaseModel +from servers.presence_connection_manager.src.abstractions.contracts import ResultBase + +# region General + + +class LoginDataModel(BaseModel): + user_id: int + profile_id: int + nick: str + email: str + unique_nick: str + password_hash: str + email_verified_flag: bool + banned_flag: bool + namespace_id: int + sub_profile_id: int + + +class LoginResult(ResultBase): + response_proof: str + data: LoginDataModel + + +class NewUserResult(ResultBase): + user_id: int + profile_id: int + + +# region Buddy + + +class AddBuddyResult(ResultBase): + pass + + +class BlockListResult(ResultBase): + profile_ids: list[int] + + +class BuddyListResult(ResultBase): + profile_ids: list[int] + + +class StatusInfoResult(ResultBase): + profile_id: int + product_id: int + status_info: UserStatusInfo + + +class StatusResult(ResultBase): + status: UserStatus + + +# class NewUserResult() + + +# region Profile + +class GetProfileDataModel(BaseModel): + nick: str + profile_id: int + unique_nick: str + email: str + firstname: str + lastname: str + icquin: int + homepage: str + zipcode: str + countrycode: str + longitude: float + latitude: float + location: str + birthday: int + birthmonth: int + birthyear: int + sex: int + publicmask: int + aim: str + picture: int + occupationid: int + industryid: int + incomeid: int + marriedid: int + childcount: int + interests1: int + ownership1: int + connectiontype: int + + +class GetProfileResult(ResultBase): + user_profile: GetProfileDataModel + + +class NewProfileResult(ResultBase): + profile_id: int diff --git a/src/servers/presence_connection_manager/src/contracts/results/buddy.py b/src/servers/presence_connection_manager/src/contracts/results/buddy.py deleted file mode 100644 index 97d08f34c..000000000 --- a/src/servers/presence_connection_manager/src/contracts/results/buddy.py +++ /dev/null @@ -1,30 +0,0 @@ -from servers.presence_connection_manager.src.abstractions.contracts import ResultBase -from servers.presence_connection_manager.src.aggregates.user_status import UserStatus -from servers.presence_connection_manager.src.aggregates.user_status_info import ( - UserStatusInfo, -) - - -class AddBuddyResult(ResultBase): - pass - - -class BlockListResult(ResultBase): - profile_ids: list[int] - - -class BuddyListResult(ResultBase): - profile_ids: list[int] - - -class StatusInfoResult(ResultBase): - profile_id: int - product_id: int - status_info: UserStatusInfo - - -class StatusResult(ResultBase): - status: UserStatus - - -# class NewUserResult() \ No newline at end of file diff --git a/src/servers/presence_connection_manager/src/contracts/results/general.py b/src/servers/presence_connection_manager/src/contracts/results/general.py deleted file mode 100644 index 05a130e44..000000000 --- a/src/servers/presence_connection_manager/src/contracts/results/general.py +++ /dev/null @@ -1,25 +0,0 @@ -from pydantic import BaseModel -from servers.presence_connection_manager.src.abstractions.contracts import ResultBase - - -class LoginDataModel(BaseModel): - user_id: int - profile_id: int - nick: str - email: str - unique_nick: str - password_hash: str - email_verified_flag: bool - banned_flag: bool - namespace_id: int - sub_profile_id: int - - -class LoginResult(ResultBase): - response_proof: str - data: LoginDataModel - - -class NewUserResult(ResultBase): - user_id: int - profile_id: int diff --git a/src/servers/presence_connection_manager/src/contracts/results/profile.py b/src/servers/presence_connection_manager/src/contracts/results/profile.py deleted file mode 100644 index 378d9da26..000000000 --- a/src/servers/presence_connection_manager/src/contracts/results/profile.py +++ /dev/null @@ -1,43 +0,0 @@ -from typing import Optional - -from pydantic import BaseModel -from servers.presence_connection_manager.src.abstractions.contracts import ResultBase - - -class GetProfileDataModel(BaseModel): - nick: str - profile_id: int - unique_nick: str - email: str - firstname: str - lastname: str - icquin: int - homepage: str - zipcode: str - countrycode: str - longitude: float - latitude: float - location: str - birthday: int - birthmonth: int - birthyear: int - sex: int - publicmask: int - aim: str - picture: int - occupationid: int - industryid: int - incomeid: int - marriedid: int - childcount: int - interests1: int - ownership1: int - connectiontype: int - - -class GetProfileResult(ResultBase): - user_profile: GetProfileDataModel - - -class NewProfileResult(ResultBase): - profile_id: int diff --git a/src/servers/presence_connection_manager/src/handlers/buddy.py b/src/servers/presence_connection_manager/src/handlers/buddy.py deleted file mode 100644 index a3cf08c7b..000000000 --- a/src/servers/presence_connection_manager/src/handlers/buddy.py +++ /dev/null @@ -1,123 +0,0 @@ -from multiprocessing.pool import Pool -from servers.presence_connection_manager.src.abstractions.contracts import RequestBase -from servers.presence_connection_manager.src.abstractions.handlers import ( - CmdHandlerBase, - LoginedHandlerBase, -) -from servers.presence_connection_manager.src.applications.client import Client -from servers.presence_connection_manager.src.contracts.requests.buddy import ( - DelBuddyRequest, - StatusInfoRequest, - StatusRequest, -) -from servers.presence_connection_manager.src.contracts.responses.buddy import ( - BlockListResponse, - BuddyListResponse, - StatusInfoResponse, -) -from servers.presence_connection_manager.src.contracts.results.buddy import ( - BlockListResult, - BuddyListResult, - StatusInfoResult, - StatusResult, -) - - -class AddBuddyHandler(CmdHandlerBase): - def __init__(self, client: Client, request: RequestBase) -> None: - raise NotImplementedError() - super().__init__(client, request) - - -class BlockListHandler(CmdHandlerBase): - _result: BlockListResult - - def __init__(self, client: Client) -> None: - assert isinstance(client, Client) - - def _response_construct(self) -> None: - self._response = BlockListResponse(self._result) - - -class BuddyListHandler(LoginedHandlerBase): - _result: BuddyListResult - _result_cls = BuddyListResult - - def __init__(self, client: Client): - assert isinstance(client, Client) - self._client = client - - def response_construct(self): - self._response = BuddyListResponse(self._request, self._result) - - def handle_status_info(self, profile_id): - request = StatusInfoRequest() - request.profile_id = profile_id - request.namespace_id = int(self._client.info.namespace_id) - # request.is_get_status_info = True - - StatusInfoHandler(self._client, request).handle() - - def _response_send(self) -> None: - super()._response_send() - - if not self._client.info.sdk_revision.is_support_gpi_new_status_notification: - return - - with Pool() as pool: - pool.map(self.handle_status_info, self._result.profile_ids) - - -class BuddyStatusInfoHandler(CmdHandlerBase): - """ - This is what the message should look like. Its broken up for easy viewing. - - \bsi\\state\\profile\\bip\\bport\\hostip\\hprivip\\qport\\hport\\sessflags\\rstatus\\gameType\\gameVnt\\gameMn\\product\\qmodeflags\ - """ - - def __init__(self, client: Client, request: RequestBase) -> None: - raise NotImplementedError() - super().__init__(client, request) - - -class DelBuddyHandler(LoginedHandlerBase): - _request: DelBuddyRequest - - def __init__(self, client: Client, request: DelBuddyRequest) -> None: - assert isinstance(request, DelBuddyRequest) - super().__init__(client, request) - - -class InviteToHandler(LoginedHandlerBase): - def __init__(self, client: Client, request: RequestBase) -> None: - raise NotImplementedError() - super().__init__(client, request) - - pass - - -class StatusHandler(CmdHandlerBase): - _request: StatusRequest - _result: StatusResult - - def __init__(self, client: Client, request: StatusRequest) -> None: - assert isinstance(request, StatusRequest) - super().__init__(client, request) - - def _response_send(self) -> None: - # TODO check if statushandler need send response - raise NotImplementedError() - - -class StatusInfoHandler(LoginedHandlerBase): - _request: StatusInfoRequest - _result: StatusInfoResult - - def __init__(self, client: Client, request: StatusInfoRequest) -> None: - assert isinstance(request, StatusInfoRequest) - super().__init__(client, request) - - def _response_send(self) -> None: - if self._request is not None: - self._response = StatusInfoResponse(self._request, self._result) - super()._response_send() diff --git a/src/servers/presence_connection_manager/src/handlers/general.py b/src/servers/presence_connection_manager/src/handlers/general.py deleted file mode 100644 index 98ef2b566..000000000 --- a/src/servers/presence_connection_manager/src/handlers/general.py +++ /dev/null @@ -1,87 +0,0 @@ - -from typing import TYPE_CHECKING -import servers.presence_connection_manager.src.abstractions.handlers -from servers.presence_connection_manager.src.aggregates.sdk_revision import SdkRevision -from servers.presence_connection_manager.src.contracts.requests.general import ( - KeepAliveRequest, - LoginRequest, - LogoutRequest, - # NewUserRequest, -) -from servers.presence_connection_manager.src.contracts.responses.general import ( - KeepAliveResponse, - LoginResponse, -) -from servers.presence_connection_manager.src.contracts.results.general import LoginResult -from servers.presence_connection_manager.src.handlers.buddy import ( - BlockListHandler, - BuddyListHandler, -) -from servers.presence_search_player.src.contracts.requests import NewUserRequest -from servers.presence_search_player.src.contracts.responses import NewUserResponse -from servers.presence_search_player.src.contracts.results import NewUserResult -import servers.presence_search_player.src.handlers.handlers - -if TYPE_CHECKING: - from servers.presence_connection_manager.src.applications.client import Client - - -class KeepAliveHandler(servers.presence_connection_manager.src.abstractions.handlers.CmdHandlerBase): - _request: KeepAliveRequest - - def __init__(self, client: "Client", request: KeepAliveRequest) -> None: - assert isinstance(request, KeepAliveRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = KeepAliveResponse(self._request) - - -class LoginHandler(servers.presence_connection_manager.src.abstractions.handlers.CmdHandlerBase): - - _request: LoginRequest - _result_cls: type[LoginResult] - _result: LoginResult - - def __init__(self, client: "Client", request: LoginRequest) -> None: - assert isinstance(request, LoginRequest) - super().__init__(client, request) - self._result_cls = LoginResult - - def _response_construct(self) -> None: - self._response = LoginResponse(self._request, self._result) - - -class LogoutHandler(servers.presence_connection_manager.src.abstractions.handlers.LoginedHandlerBase): - _request: LogoutRequest - - def __init__(self, client: "Client", request: LogoutRequest) -> None: - assert isinstance(request, LogoutRequest) - super().__init__(client, request) - - - -# todo create new handler -class NewUserHandler(servers.presence_connection_manager.src.abstractions.handlers.CmdHandlerBase): - _request: NewUserRequest - _result_cls: type[NewUserResult] - _result: NewUserResult - # todo create seperate request and result - - def _response_construct(self): - self._response = NewUserResponse(self._request, self._result) - - -class SdkRevisionHandler(servers.presence_connection_manager.src.abstractions.handlers.CmdHandlerBase): - _request: LoginRequest - - def __init__(self, client: "Client", request: LoginRequest) -> None: - assert isinstance(request, LoginRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._client.info.sdk_revision = SdkRevision( - self._request.sdk_revision_type) - if self._client.info.sdk_revision.is_support_gpi_new_status_notification: - BuddyListHandler(self._client).handle() - BlockListHandler(self._client).handle() diff --git a/src/servers/presence_connection_manager/src/handlers/profile.py b/src/servers/presence_connection_manager/src/handlers/profile.py deleted file mode 100644 index 3d22d09d0..000000000 --- a/src/servers/presence_connection_manager/src/handlers/profile.py +++ /dev/null @@ -1,96 +0,0 @@ -from typing import final -from servers.presence_connection_manager.src.abstractions.contracts import RequestBase -from servers.presence_connection_manager.src.abstractions.handlers import CmdHandlerBase -from servers.presence_connection_manager.src.applications.client import Client -from servers.presence_connection_manager.src.contracts.requests.profile import ( - AddBlockRequest, - GetProfileRequest, - NewProfileRequest, - RegisterCDKeyRequest, - RegisterNickRequest, - UpdateProfileRequest, -) -from servers.presence_connection_manager.src.contracts.responses.profile import ( - GetProfileResponse, - NewProfileResponse, - RegisterNickResponse, -) -from servers.presence_connection_manager.src.contracts.results.profile import GetProfileResult, NewProfileResult - - -@final -class AddBlockHandler(CmdHandlerBase): - _request: AddBlockRequest - - def __init__(self, client: Client, request: AddBlockRequest) -> None: - assert isinstance(request, AddBlockRequest) - super().__init__(client, request) - - -@final -class GetProfileHandler(CmdHandlerBase): - _request: GetProfileRequest - _result: GetProfileResult - - def __init__(self, client: Client, request: GetProfileRequest) -> None: - assert isinstance(request, GetProfileRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = GetProfileResponse(self._request, self._result) - - -@final -class NewProfileHandler(CmdHandlerBase): - _request: NewProfileRequest - _result: NewProfileResult - - def __init__(self, client: Client, request: NewProfileRequest) -> None: - assert isinstance(request, NewProfileRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = NewProfileResponse(self._request, self._result) - - -@final -class RegisterCDKeyHandler(CmdHandlerBase): - _request: RegisterCDKeyRequest - - def __init__(self, client: Client, request: RegisterCDKeyRequest) -> None: - assert isinstance(request, RegisterCDKeyRequest) - super().__init__(client, request) - - -@final -class RegisterNickHandler(CmdHandlerBase): - _request: RegisterNickRequest - def __init__(self, client: Client, request: RegisterNickRequest) -> None: - assert isinstance(request, RegisterNickRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = RegisterNickResponse(self._request) - - -@final -class RemoveBlockHandler(CmdHandlerBase): - def __init__(self, client: Client, request: RequestBase) -> None: - super().__init__(client, request) - raise NotImplementedError() - - -@final -class UpdateProfileHandler(CmdHandlerBase): - _request: UpdateProfileRequest - - def __init__(self, client: Client, request: UpdateProfileRequest) -> None: - assert isinstance(request, UpdateProfileRequest) - super().__init__(client, request) - - -@final -class UpdateUserInfoHandler(CmdHandlerBase): - def __init__(self, client: Client, request: RequestBase) -> None: - super().__init__(client, request) - raise NotImplementedError() diff --git a/src/servers/presence_connection_manager/tests/buddy_request_tests.py b/src/servers/presence_connection_manager/tests/buddy_request_tests.py index a8a78da64..cf8c9122e 100644 --- a/src/servers/presence_connection_manager/tests/buddy_request_tests.py +++ b/src/servers/presence_connection_manager/tests/buddy_request_tests.py @@ -1,6 +1,6 @@ import unittest -from servers.presence_connection_manager.src.contracts.requests.buddy import ( +from servers.presence_connection_manager.src.contracts.requests import ( AddBuddyRequest, DelBuddyRequest, InviteToRequest, diff --git a/src/servers/presence_connection_manager/tests/game_tests.py b/src/servers/presence_connection_manager/tests/game_tests.py index 99ba22cec..4c8fdce32 100644 --- a/src/servers/presence_connection_manager/tests/game_tests.py +++ b/src/servers/presence_connection_manager/tests/game_tests.py @@ -1,6 +1,6 @@ import unittest -from servers.presence_connection_manager.src.contracts.requests.buddy import StatusRequest +from servers.presence_connection_manager.src.contracts.requests import StatusRequest from servers.presence_connection_manager.tests.mock_objects import create_client diff --git a/src/servers/presence_connection_manager/tests/general_request_tests.py b/src/servers/presence_connection_manager/tests/general_request_tests.py index 37050689d..9455f89a3 100644 --- a/src/servers/presence_connection_manager/tests/general_request_tests.py +++ b/src/servers/presence_connection_manager/tests/general_request_tests.py @@ -1,6 +1,6 @@ import unittest -from servers.presence_connection_manager.src.contracts.requests.general import LoginRequest -from servers.presence_connection_manager.src.enums.general import ( +from servers.presence_connection_manager.src.contracts.requests import LoginRequest +from servers.presence_connection_manager.src.aggregates.enums import ( LoginType, QuietModeType, SdkRevisionType, diff --git a/src/servers/presence_connection_manager/tests/profile_request_tests.py b/src/servers/presence_connection_manager/tests/profile_request_tests.py index cacf69950..14d4097d1 100644 --- a/src/servers/presence_connection_manager/tests/profile_request_tests.py +++ b/src/servers/presence_connection_manager/tests/profile_request_tests.py @@ -1,6 +1,6 @@ import unittest -from servers.presence_connection_manager.src.contracts.requests.profile import ( +from servers.presence_connection_manager.src.contracts.requests import ( AddBlockRequest, GetProfileRequest, NewProfileRequest, diff --git a/src/servers/presence_search_player/src/abstractions/contracts.py b/src/servers/presence_search_player/src/abstractions/contracts.py index 12ea55a84..21507f813 100644 --- a/src/servers/presence_search_player/src/abstractions/contracts.py +++ b/src/servers/presence_search_player/src/abstractions/contracts.py @@ -1,7 +1,7 @@ from typing import Dict import library.src.abstractions.contracts from library.src.extentions.gamespy_utils import convert_to_key_value -from servers.presence_search_player.src.exceptions.general import ( +from servers.presence_search_player.src.aggregates.exceptions import ( GPParseException, ) diff --git a/src/servers/presence_search_player/src/abstractions/handler.py b/src/servers/presence_search_player/src/abstractions/handler.py index 0c96c4344..373ce3598 100644 --- a/src/servers/presence_search_player/src/abstractions/handler.py +++ b/src/servers/presence_search_player/src/abstractions/handler.py @@ -1,7 +1,7 @@ from library.src.abstractions.handler import CmdHandlerBase as CHB from servers.presence_search_player.src.abstractions.contracts import RequestBase from servers.presence_search_player.src.applications.client import Client -from servers.presence_search_player.src.exceptions.general import GPException +from servers.presence_search_player.src.aggregates.exceptions import GPException class CmdHandlerBase(CHB): diff --git a/src/servers/presence_search_player/src/enums/error_codes.py b/src/servers/presence_search_player/src/aggregates/enums.py similarity index 94% rename from src/servers/presence_search_player/src/enums/error_codes.py rename to src/servers/presence_search_player/src/aggregates/enums.py index ee8d75879..4530eaee7 100644 --- a/src/servers/presence_search_player/src/enums/error_codes.py +++ b/src/servers/presence_search_player/src/aggregates/enums.py @@ -1,7 +1,13 @@ -from enum import Enum +from enum import IntEnum -from enum import IntEnum +class SearchType(IntEnum): + NICK_SEARCH = 0 + NICK_EMAIL_SEARCH = 1 + UNIQUENICK_NAMESPACEID_SEARCH = 2 + EMAIL_SEARCH = 3 + +# region Error code class GPErrorCode(IntEnum): diff --git a/src/servers/presence_search_player/src/exceptions/general.py b/src/servers/presence_search_player/src/aggregates/exceptions.py similarity index 76% rename from src/servers/presence_search_player/src/exceptions/general.py rename to src/servers/presence_search_player/src/aggregates/exceptions.py index 744e79a4b..ddede71a0 100644 --- a/src/servers/presence_search_player/src/exceptions/general.py +++ b/src/servers/presence_search_player/src/aggregates/exceptions.py @@ -1,5 +1,5 @@ from library.src.exceptions.general import UniSpyException -from servers.presence_search_player.src.enums.error_codes import GPErrorCode +from servers.presence_search_player.src.aggregates.enums import GPErrorCode from library.src.abstractions.contracts import ResponseBase @@ -10,7 +10,7 @@ class GPException(UniSpyException, ResponseBase): def __init__( self, message: str = "General error.", - error_code: GPErrorCode = GPErrorCode.GENERAL, + error_code=GPErrorCode.GENERAL, ) -> None: UniSpyException.__init__(self, message) self.error_code = error_code @@ -24,7 +24,7 @@ class GPParseException(GPException): def __init__( self, message: str = "Request parsing error.", - error_code: GPErrorCode = GPErrorCode.PARSE, + error_code=GPErrorCode.PARSE, ) -> None: super().__init__(message, error_code) @@ -33,7 +33,7 @@ class GPUdpLayerException(GPException): def __init__( self, message: str = "Unknown UDP layer error.", - error_code: GPErrorCode = GPErrorCode.UDP_LAYER, + error_code=GPErrorCode.UDP_LAYER, ) -> None: super().__init__(message, error_code) @@ -42,7 +42,7 @@ class GPNotLoginException(GPException): def __init__( self, message: str = "You are not logged in, please login first.", - error_code: GPErrorCode = GPErrorCode.NOT_LOGGED_IN, + error_code=GPErrorCode.NOT_LOGGED_IN, ) -> None: super().__init__(message, error_code) @@ -51,7 +51,7 @@ class GPNetworkException(GPException): def __init__( self, message: str = "Unknown network error.", - error_code: GPErrorCode = GPErrorCode.NETWORK, + error_code=GPErrorCode.NETWORK, ) -> None: super().__init__(message, error_code) @@ -60,7 +60,7 @@ class GPForceDisconnectException(GPException): def __init__( self, message: str = "Client is forced to disconnect.", - error_code: GPErrorCode = GPErrorCode.FORCED_DISCONNECT, + error_code=GPErrorCode.FORCED_DISCONNECT, ) -> None: super().__init__(message, error_code) @@ -69,7 +69,7 @@ class GPDatabaseException(GPException): def __init__( self, message: str = "Database error.", - error_code: GPErrorCode = GPErrorCode.DATABASE_ERROR, + error_code=GPErrorCode.DATABASE_ERROR, ) -> None: super().__init__(message, error_code) @@ -78,7 +78,7 @@ class GPConnectionCloseException(GPException): def __init__( self, message: str = "Client connection accidently closed.", - error_code: GPErrorCode = GPErrorCode.CONNECTION_CLOSE, + error_code=GPErrorCode.CONNECTION_CLOSE, ) -> None: super().__init__(message, error_code) @@ -87,7 +87,7 @@ class GPBadSessionKeyException(GPException): def __init__( self, message: str = "Session key is invalid.", - error_code: GPErrorCode = GPErrorCode.BAD_SESSION_KEY, + error_code=GPErrorCode.BAD_SESSION_KEY, ) -> None: super().__init__(message, error_code) @@ -96,7 +96,7 @@ class GPAddBuddyException(GPException): def __init__( self, message: str = "Unknown error occur at add buddy.", - error_code: GPErrorCode = GPErrorCode.ADD_BUDDY, + error_code=GPErrorCode.ADD_BUDDY, ) -> None: super().__init__(message, error_code) @@ -105,7 +105,7 @@ class GPAddBuddyAlreadyException(GPAddBuddyException): def __init__( self, message: str = "The buddy you are adding is already in your buddy list.", - error_code: GPErrorCode = GPErrorCode.ADD_BUDDY_ALREADY_BUDDY, + error_code=GPErrorCode.ADD_BUDDY_ALREADY_BUDDY, ) -> None: super().__init__(message, error_code) @@ -114,7 +114,7 @@ class GPAddBuddyBadFormatException(GPAddBuddyException): def __init__( self, message: str = "Add buddy format invalid.", - error_code: GPErrorCode = GPErrorCode.ADD_BUDDY_BAD_FORM, + error_code=GPErrorCode.ADD_BUDDY_BAD_FORM, ) -> None: super().__init__(message, error_code) @@ -123,7 +123,7 @@ class GPAddBuddyBadNewException(GPAddBuddyException): def __init__( self, message: str = "The buddy name provided is invalid.", - error_code: GPErrorCode = GPErrorCode.ADD_BUDDY_BAD_NEW, + error_code=GPErrorCode.ADD_BUDDY_BAD_NEW, ) -> None: super().__init__(message, error_code) @@ -132,7 +132,7 @@ class AuthAddException(GPException): def __init__( self, message: str = "The adding of authentication failed.", - error_code: GPErrorCode = GPErrorCode.AUTH_ADD, + error_code=GPErrorCode.AUTH_ADD, ) -> None: super().__init__(message, error_code) @@ -141,7 +141,7 @@ class AuthAddBadFormatException(AuthAddException): def __init__( self, message: str = "The authentication is in bad form.", - error_code: GPErrorCode = GPErrorCode.AUTH_ADD_BAD_FORM, + error_code=GPErrorCode.AUTH_ADD_BAD_FORM, ) -> None: super().__init__(message, error_code) @@ -150,7 +150,7 @@ class AuthAddBadSigException(AuthAddException): def __init__( self, message: str = "The signature in authentication is invalid.", - error_code: GPErrorCode = GPErrorCode.AUTH_ADD_BAD_SIG, + error_code=GPErrorCode.AUTH_ADD_BAD_SIG, ) -> None: super().__init__(message, error_code) @@ -159,7 +159,7 @@ class GPBuddyMsgException(GPException): def __init__( self, message: str = "Unknown error occur when processing buddy message.", - error_code: GPErrorCode = GPErrorCode.BM, + error_code=GPErrorCode.BM, ) -> None: super().__init__(message, error_code) @@ -168,7 +168,7 @@ class GPBuddyMsgExtInfoNotSupportedException(GPBuddyMsgException): def __init__( self, message: str = "Buddy message is not supported.", - error_code: GPErrorCode = GPErrorCode.BM_EXT_INFO_NOT_SUPPORTED, + error_code=GPErrorCode.BM_EXT_INFO_NOT_SUPPORTED, ) -> None: super().__init__(message, error_code) @@ -177,7 +177,7 @@ class GPBuddyMsgNotBuddyException(GPBuddyMsgException): def __init__( self, message: str = "The message receiver is not your buddy.", - error_code: GPErrorCode = GPErrorCode.BM_NOT_BUDDY, + error_code=GPErrorCode.BM_NOT_BUDDY, ) -> None: super().__init__(message, error_code) @@ -186,7 +186,7 @@ class CheckException(GPException): def __init__( self, message: str = "There was an error checking the user account.", - error_code: GPErrorCode = GPErrorCode.CHECK, + error_code=GPErrorCode.CHECK, ) -> None: super().__init__(message, error_code) @@ -198,7 +198,7 @@ class GPLoginException(GPException): def __init__( self, message: str = "Unknown login error.", - error_code: GPErrorCode = GPErrorCode.LOGIN, + error_code=GPErrorCode.LOGIN, ) -> None: super().__init__(message, error_code) @@ -207,7 +207,7 @@ class GPLoginBadEmailException(GPLoginException): def __init__( self, message: str = "Email provided is invalid.", - error_code: GPErrorCode = GPErrorCode.LOGIN_BAD_EMAIL, + error_code=GPErrorCode.LOGIN_BAD_EMAIL, ) -> None: super().__init__(message, error_code) @@ -216,7 +216,7 @@ class GPLoginBadLoginTicketException(GPLoginException): def __init__( self, message: str = "The login ticket is invalid.", - error_code: GPErrorCode = GPErrorCode.LOGIN_TICKET_EXPIRED, + error_code=GPErrorCode.LOGIN_TICKET_EXPIRED, ) -> None: super().__init__(message, error_code) @@ -225,7 +225,7 @@ class GPLoginBadNickException(GPLoginException): def __init__( self, message: str = "Nickname is in valid.", - error_code: GPErrorCode = GPErrorCode.LOGIN_BAD_NICK, + error_code=GPErrorCode.LOGIN_BAD_NICK, ) -> None: super().__init__(message, error_code) @@ -234,7 +234,7 @@ class GPLoginBadPasswordException(GPLoginException): def __init__( self, message: str = "Password provided is invalid.", - error_code: GPErrorCode = GPErrorCode.LOGIN_BAD_PASSWORD, + error_code=GPErrorCode.LOGIN_BAD_PASSWORD, ) -> None: super().__init__(message, error_code) @@ -243,7 +243,7 @@ class GPLoginBadPreAuthException(GPLoginException): def __init__( self, message: str = "Login pre-authentication failed.", - error_code: GPErrorCode = GPErrorCode.LOGIN_BAD_PRE_AUTH, + error_code=GPErrorCode.LOGIN_BAD_PRE_AUTH, ) -> None: super().__init__(message, error_code) @@ -252,7 +252,7 @@ class GPLoginBadProfileException(GPLoginException): def __init__( self, message: str = "User profile is damaged.", - error_code: GPErrorCode = GPErrorCode.LOGIN_BAD_PROFILE, + error_code=GPErrorCode.LOGIN_BAD_PROFILE, ) -> None: super().__init__(message, error_code) @@ -261,7 +261,7 @@ class GPLoginBadUniquenickException(GPLoginException): def __init__( self, message: str = "The uniquenick provided is invalid.", - error_code: GPErrorCode = GPErrorCode.LOGIN_BAD_UNIQUENICK, + error_code=GPErrorCode.LOGIN_BAD_UNIQUENICK, ) -> None: super().__init__(message, error_code) @@ -270,7 +270,7 @@ class GPLoginConnectionFailedException(GPLoginException): def __init__( self, message: str = "Login connection failed.", - error_code: GPErrorCode = GPErrorCode.LOGIN_CONNECTION_FAILED, + error_code=GPErrorCode.LOGIN_CONNECTION_FAILED, ) -> None: super().__init__(message, error_code) @@ -279,7 +279,7 @@ class GPLoginProfileDeletedException(GPLoginException): def __init__( self, message: str = "Login connection failed.", - error_code: GPErrorCode = GPErrorCode.LOGIN_PROFILE_DELETED, + error_code=GPErrorCode.LOGIN_PROFILE_DELETED, ) -> None: super().__init__(message, error_code) @@ -288,7 +288,7 @@ class GPLoginServerAuthFailedException(GPLoginException): def __init__( self, message: str = "Login server authentication failed.", - error_code: GPErrorCode = GPErrorCode.LOGIN_SERVER_AUTH_FAILED, + error_code=GPErrorCode.LOGIN_SERVER_AUTH_FAILED, ) -> None: super().__init__(message, error_code) @@ -297,7 +297,7 @@ class GPLoginTicketExpiredException(GPLoginException): def __init__( self, message: str = "The login ticket have expired.", - error_code: GPErrorCode = GPErrorCode.LOGIN_TICKET_EXPIRED, + error_code=GPErrorCode.LOGIN_TICKET_EXPIRED, ) -> None: super().__init__(message, error_code) @@ -306,7 +306,7 @@ class GPLoginTimeOutException(GPLoginException): def __init__( self, message: str = "Login timeout.", - error_code: GPErrorCode = GPErrorCode.LOGIN_TIME_OUT, + error_code=GPErrorCode.LOGIN_TIME_OUT, ) -> None: super().__init__(message, error_code) @@ -315,7 +315,7 @@ class GPNewProfileException(GPException): def __init__( self, message: str = "An unknown error occur when creating new profile.", - error_code: GPErrorCode = GPErrorCode.NEW_PROFILE, + error_code=GPErrorCode.NEW_PROFILE, ) -> None: super().__init__(message, error_code) @@ -324,7 +324,7 @@ class GPNewProfileBadNickException(GPNewProfileException): def __init__( self, message: str = "Nickname is invalid at creating new profile.", - error_code: GPErrorCode = GPErrorCode.NEW_PROFILE_BAD_NICK, + error_code=GPErrorCode.NEW_PROFILE_BAD_NICK, ) -> None: super().__init__(message, error_code) @@ -333,7 +333,7 @@ class GPNewProfileBadOldNickException(GPNewProfileException): def __init__( self, message: str = "There is an already exist nickname.", - error_code: GPErrorCode = GPErrorCode.NEW_PROFILE_BAD_OLD_NICK, + error_code=GPErrorCode.NEW_PROFILE_BAD_OLD_NICK, ) -> None: super().__init__(message, error_code) @@ -342,7 +342,7 @@ class GPNewUserException(GPException): def __init__( self, message: str = "There was an unknown error creating user account.", - error_code: GPErrorCode = GPErrorCode.NEW_USER, + error_code=GPErrorCode.NEW_USER, ) -> None: super().__init__(message, error_code) @@ -351,7 +351,7 @@ class GPNewUserBadNickException(GPException): def __init__( self, message: str = "The nickname provided is invalid.", - error_code: GPErrorCode = GPErrorCode.NEW_USER_BAD_NICK, + error_code=GPErrorCode.NEW_USER_BAD_NICK, ) -> None: super().__init__(message, error_code) @@ -360,7 +360,7 @@ class GPNewUserBadPasswordException(GPException): def __init__( self, message: str = "Password is invalid.", - error_code: GPErrorCode = GPErrorCode.NEW_USER_BAD_PASSWORDS, + error_code=GPErrorCode.NEW_USER_BAD_PASSWORDS, ) -> None: super().__init__(message, error_code) @@ -369,7 +369,7 @@ class GPNewUserUniquenickInUseException(GPException): def __init__( self, message: str = "Uniquenick is in use.", - error_code: GPErrorCode = GPErrorCode.NEW_USER_UNIQUENICK_IN_USE, + error_code=GPErrorCode.NEW_USER_UNIQUENICK_IN_USE, ) -> None: super().__init__(message, error_code) @@ -378,7 +378,7 @@ class GPNewUserUniquenickInvalidException(GPException): def __init__( self, message: str = "Uniquenick is invalid.", - error_code: GPErrorCode = GPErrorCode.NEW_USER_UNIQUENICK_INVALID, + error_code=GPErrorCode.NEW_USER_UNIQUENICK_INVALID, ) -> None: super().__init__(message, error_code) @@ -387,7 +387,7 @@ class GPStatusException(GPException): def __init__( self, message: str = "Unknown error happen when processing player status.", - error_code: GPErrorCode = GPErrorCode.STATUS, + error_code=GPErrorCode.STATUS, ) -> None: super().__init__(message, error_code) @@ -396,7 +396,7 @@ class GPUpdateProfileException(GPException): def __init__( self, message: str = "Update profile unknown error.", - error_code: GPErrorCode = GPErrorCode.UPDATE_PRO, + error_code=GPErrorCode.UPDATE_PRO, ) -> None: super().__init__(message, error_code) @@ -405,7 +405,7 @@ class GPUpdateProBadNickException(GPUpdateProfileException): def __init__( self, message: str = "Nickname is invalid for updating profile.", - error_code: GPErrorCode = GPErrorCode.UPDATE_PRO_BAD_NICK, + error_code=GPErrorCode.UPDATE_PRO_BAD_NICK, ) -> None: super().__init__(message, error_code) @@ -414,7 +414,7 @@ class GPUpdateUIException(GPException): def __init__( self, message: str = "Update user info unknown error.", - error_code: GPErrorCode = GPErrorCode.UPDATE_UI, + error_code=GPErrorCode.UPDATE_UI, ) -> None: super().__init__(message, error_code) @@ -423,7 +423,7 @@ class GPUpdateUIBadEmailException(GPException): def __init__( self, message: str = "Email is invalid.", - error_code: GPErrorCode = GPErrorCode.UPDATE_UI_BAD_EMAIL, + error_code=GPErrorCode.UPDATE_UI_BAD_EMAIL, ) -> None: super().__init__(message, error_code) diff --git a/src/servers/presence_search_player/src/contracts/requests.py b/src/servers/presence_search_player/src/contracts/requests.py index 7dfef098b..2d0371bf6 100644 --- a/src/servers/presence_search_player/src/contracts/requests.py +++ b/src/servers/presence_search_player/src/contracts/requests.py @@ -1,8 +1,8 @@ from library.src.extentions.gamespy_utils import is_email_format_correct from library.src.extentions.password_encoder import process_password from servers.presence_search_player.src.abstractions.contracts import RequestBase -from servers.presence_search_player.src.enums.general import SearchType -from servers.presence_search_player.src.exceptions.general import ( +from servers.presence_search_player.src.aggregates.enums import SearchType +from servers.presence_search_player.src.aggregates.exceptions import ( GPParseException, ) diff --git a/src/servers/presence_search_player/src/enums/general.py b/src/servers/presence_search_player/src/enums/general.py deleted file mode 100644 index 2e2108f0b..000000000 --- a/src/servers/presence_search_player/src/enums/general.py +++ /dev/null @@ -1,8 +0,0 @@ -from enum import IntEnum - - -class SearchType(IntEnum): - NICK_SEARCH = 0 - NICK_EMAIL_SEARCH = 1 - UNIQUENICK_NAMESPACEID_SEARCH = 2 - EMAIL_SEARCH = 3 diff --git a/src/servers/query_report/src/aggregates/game_server_info.py b/src/servers/query_report/src/aggregates/game_server_info.py index 75b1b04b2..5b53f467f 100644 --- a/src/servers/query_report/src/aggregates/game_server_info.py +++ b/src/servers/query_report/src/aggregates/game_server_info.py @@ -3,7 +3,7 @@ from uuid import UUID from library.src.extentions.bytes_extentions import ip_to_4_bytes -from servers.query_report.src.v2.enums.general import GameServerStatus +from servers.query_report.src.v2.aggregates.enums import GameServerStatus class GameServerInfo: diff --git a/src/servers/query_report/src/v2/abstractions/contracts.py b/src/servers/query_report/src/v2/abstractions/contracts.py index db577a0ac..7565effe3 100644 --- a/src/servers/query_report/src/v2/abstractions/contracts.py +++ b/src/servers/query_report/src/v2/abstractions/contracts.py @@ -1,7 +1,7 @@ -from servers.query_report.src.v2.enums.general import PacketType +from servers.query_report.src.v2.aggregates.enums import PacketType import library.src.abstractions.contracts from servers.query_report.src.exceptions.exceptions import QRException -from servers.query_report.src.v2.enums.general import RequestType +from servers.query_report.src.v2.aggregates.enums import RequestType MAGIC_DATA = [0xFE, 0xFD] diff --git a/src/servers/query_report/src/v2/enums/general.py b/src/servers/query_report/src/v2/aggregates/enums.py similarity index 100% rename from src/servers/query_report/src/v2/enums/general.py rename to src/servers/query_report/src/v2/aggregates/enums.py diff --git a/src/servers/query_report/src/v2/contracts/requests.py b/src/servers/query_report/src/v2/contracts/requests.py index 5c5c0293b..0afe44047 100644 --- a/src/servers/query_report/src/v2/contracts/requests.py +++ b/src/servers/query_report/src/v2/contracts/requests.py @@ -1,9 +1,10 @@ +from typing import Optional from uuid import UUID from library.src.extentions.encoding import get_string from library.src.log.log_manager import LogWriter from servers.query_report.src.exceptions.exceptions import QRException from servers.query_report.src.v2.abstractions.contracts import RequestBase -from servers.query_report.src.v2.enums.general import GameServerStatus +from servers.query_report.src.v2.aggregates.enums import GameServerStatus PREFIX = bytes([0x09, 0x00, 0x00, 0x00, 0x00]) @@ -33,15 +34,19 @@ class ClientMessageAckRequest(RequestBase): class ClientMessageRequest(RequestBase): server_browser_sender_id: UUID - natneg_message: list[int] + natneg_message: bytes target_ip_address: str - target_port: str + target_port: int message_key: int @property def cookie(self) -> int: return int.from_bytes(self.natneg_message[6:10], "little") + def __init__(self, raw_request: Optional[bytes] = None) -> None: + if raw_request is not None: + super().__init__(raw_request) + class HeartBeatRequest(RequestBase): server_data: dict[str, str] diff --git a/src/servers/query_report/src/v2/contracts/responses.py b/src/servers/query_report/src/v2/contracts/responses.py index 28a288d02..48e1b16e8 100644 --- a/src/servers/query_report/src/v2/contracts/responses.py +++ b/src/servers/query_report/src/v2/contracts/responses.py @@ -1,20 +1,18 @@ import socket from library.src.extentions.encoding import get_bytes -from servers.presence_connection_manager.src.contracts.requests.general import ( - KeepAliveRequest, -) from servers.query_report.src.v2.abstractions.contracts import ResponseBase from servers.query_report.src.v2.contracts.requests import ( AvaliableRequest, ChallengeRequest, ClientMessageRequest, HeartBeatRequest, + KeepAliveRequest ) from servers.query_report.src.v2.contracts.results import ( ChallengeResult, HeartBeatResult, ) -from servers.query_report.src.v2.enums.general import ServerAvailability +from servers.query_report.src.v2.aggregates.enums import ServerAvailability from library.src.extentions.bytes_extentions import ip_to_4_bytes, port_to_2_bytes RESPONSE_PREFIX = bytes([0xFE, 0xFD, 0x09, 0x00, 0x00, 0x00]) diff --git a/src/servers/query_report/src/v2/contracts/results.py b/src/servers/query_report/src/v2/contracts/results.py index fdc7ae361..3a83f0a22 100644 --- a/src/servers/query_report/src/v2/contracts/results.py +++ b/src/servers/query_report/src/v2/contracts/results.py @@ -1,5 +1,5 @@ from servers.query_report.src.v2.abstractions.contracts import ResultBase -from servers.query_report.src.v2.enums.general import PacketType +from servers.query_report.src.v2.aggregates.enums import PacketType class ChallengeResult(ResultBase): diff --git a/src/servers/query_report/src/v2/handlers/switcher.py b/src/servers/query_report/src/v2/handlers/switcher.py index bf892078e..fefe52664 100644 --- a/src/servers/query_report/src/v2/handlers/switcher.py +++ b/src/servers/query_report/src/v2/handlers/switcher.py @@ -13,7 +13,7 @@ HeartBeatRequest, KeepAliveRequest, ) -from servers.query_report.src.v2.enums.general import RequestType +from servers.query_report.src.v2.aggregates.enums import RequestType from servers.query_report.src.v2.handlers.handlers import ( AvailableHandler, ChallengeHanler, diff --git a/src/servers/server_browser/src/v2/abstractions/contracts.py b/src/servers/server_browser/src/v2/abstractions/contracts.py index 65e0e9b63..3db3a8e8b 100644 --- a/src/servers/server_browser/src/v2/abstractions/contracts.py +++ b/src/servers/server_browser/src/v2/abstractions/contracts.py @@ -6,7 +6,7 @@ from servers.server_browser.src.v2.aggregations.encryption import SERVER_CHALLENGE from servers.server_browser.src.v2.aggregations.string_flags import STRING_SPLITER from servers.server_browser.src.v2.contracts.results import AdHocResult -from servers.server_browser.src.v2.enums.general import ( +from servers.server_browser.src.v2.aggregations.enums import ( DataKeyType, GameServerFlags, RequestType, @@ -26,7 +26,8 @@ def __init__(self, raw_request: bytes) -> None: super().__init__(raw_request) def parse(self) -> None: - self.request_length = int.from_bytes(self.raw_request[:2], byteorder="little") + self.request_length = int.from_bytes( + self.raw_request[:2], byteorder="little") self.command_name = RequestType(self.raw_request[2]) @@ -46,19 +47,19 @@ def __init__(self, request: RequestBase, result: ResultBase) -> None: class ServerListUpdateOptionRequestBase(RequestBase): - request_version: Optional[int] = None - protocol_version: Optional[int] = None - encoding_version: Optional[int] = None - game_version: Optional[int] = None - query_options: Optional[int] = None - dev_game_name: Optional[str] = None - game_name: Optional[str] = None - client_challenge: Optional[str] = None - update_option: Optional[ServerListUpdateOption] = None - keys: Optional[List[str]] = None - filter: Optional[str] = None + request_version: int + protocol_version: int + encoding_version: int + game_version: int + query_options: int + dev_game_name: str + game_name: str + client_challenge: str + update_option: ServerListUpdateOption + keys: list[str] + filter: str source_ip: str - max_servers: Optional[int] = None + max_servers: int def __init__(self, raw_request: bytes): assert isinstance(raw_request, bytes) @@ -74,7 +75,7 @@ class ServerListUpdateOptionResultBase(ResultBase): class ServerListUpdateOptionResponseBase(ResponseBase): _request: ServerListUpdateOptionRequestBase _result: ServerListUpdateOptionResultBase - _servers_info_buffers: list = [] + _servers_info_buffers: bytearray def __init__( self, @@ -84,6 +85,7 @@ def __init__( assert issubclass(type(request), ServerListUpdateOptionRequestBase) assert issubclass(type(result), ServerListUpdateOptionResultBase) super().__init__(request, result) + self._servers_info_buffers = bytearray() def build(self) -> None: crypt_header = self.build_crypt_header() @@ -107,15 +109,15 @@ def build_server_keys(self) -> None: for key in self._request.keys: self._servers_info_buffers.append(DataKeyType.STRING) self._servers_info_buffers.extend(get_bytes(key)) - self._servers_info_buffers.append(STRING_SPLITER) + self._servers_info_buffers.extend(STRING_SPLITER) def build_unique_value(self): self._servers_info_buffers.append(0) class AdHocRequestBase(RequestBase): - game_server_public_ip: bytes - game_server_public_port: bytes + game_server_public_ip: str + game_server_public_port: int def parse(self) -> None: super().parse() @@ -127,4 +129,4 @@ def parse(self) -> None: class AdHocResponseBase(ResponseBase): _result: AdHocResult - _buffer: list[int] = [] + _buffer: bytearray diff --git a/src/servers/server_browser/src/v2/abstractions/handlers.py b/src/servers/server_browser/src/v2/abstractions/handlers.py index c58190ad3..0d568d5d9 100644 --- a/src/servers/server_browser/src/v2/abstractions/handlers.py +++ b/src/servers/server_browser/src/v2/abstractions/handlers.py @@ -51,4 +51,4 @@ def _response_send(self) -> None: encrypted_body_buffer = self._client.crypto.encrypt(body_buffer) buffer = head_buffer + encrypted_body_buffer self._client.log_network_sending(buffer) - self._client.send(buffer) + self._client.connection.send(buffer) diff --git a/src/servers/server_browser/src/v2/enums/general.py b/src/servers/server_browser/src/v2/aggregations/enums.py similarity index 100% rename from src/servers/server_browser/src/v2/enums/general.py rename to src/servers/server_browser/src/v2/aggregations/enums.py diff --git a/src/servers/server_browser/src/v2/aggregations/server_info_builder.py b/src/servers/server_browser/src/v2/aggregations/server_info_builder.py index 17fe5eccb..572ae3ff7 100644 --- a/src/servers/server_browser/src/v2/aggregations/server_info_builder.py +++ b/src/servers/server_browser/src/v2/aggregations/server_info_builder.py @@ -4,7 +4,7 @@ from backends.protocols.gamespy.query_report.data import get_all_groups from servers.server_browser.src.v2.abstractions.contracts import QUERY_REPORT_DEFAULT_PORT -from servers.server_browser.src.v2.enums.general import GameServerFlags +from servers.server_browser.src.v2.aggregations.enums import GameServerFlags PEER_GROUP_LIST = get_all_groups() diff --git a/src/servers/server_browser/src/v2/aggregations/string_flags.py b/src/servers/server_browser/src/v2/aggregations/string_flags.py index 181200db8..a1b38a6ff 100644 --- a/src/servers/server_browser/src/v2/aggregations/string_flags.py +++ b/src/servers/server_browser/src/v2/aggregations/string_flags.py @@ -1,3 +1,3 @@ -ALL_SERVER_END_FLAG = bytes([0, 255, 255, 255, 255]) -STRING_SPLITER = bytes([0]) -NTS_STRING_FLAG = bytes([0xFF]) +ALL_SERVER_END_FLAG = bytearray([0, 255, 255, 255, 255]) +STRING_SPLITER = b"\x00" +NTS_STRING_FLAG = b"0xFF" diff --git a/src/servers/server_browser/src/v2/applications/client.py b/src/servers/server_browser/src/v2/applications/client.py index beb74bdcc..ff1ce5e89 100644 --- a/src/servers/server_browser/src/v2/applications/client.py +++ b/src/servers/server_browser/src/v2/applications/client.py @@ -1,5 +1,6 @@ from library.src.abstractions.client import ClientBase, ClientInfoBase -from servers.server_browser.src.v2.enums.general import ServerListUpdateOption +from library.src.abstractions.enctypt_base import EncryptBase +from servers.server_browser.src.v2.aggregations.enums import ServerListUpdateOption class ClientInfo(ClientInfoBase): @@ -12,6 +13,7 @@ class ClientInfo(ClientInfoBase): class Client(ClientBase): is_log_raw: bool = True info: ClientInfo + crypto: EncryptBase def create_switcher(self, buffer) -> None: pass diff --git a/src/servers/server_browser/src/v2/handlers/handlers.py b/src/servers/server_browser/src/v2/applications/handlers.py similarity index 93% rename from src/servers/server_browser/src/v2/handlers/handlers.py rename to src/servers/server_browser/src/v2/applications/handlers.py index aab2d0120..e9fcbea83 100644 --- a/src/servers/server_browser/src/v2/handlers/handlers.py +++ b/src/servers/server_browser/src/v2/applications/handlers.py @@ -1,7 +1,8 @@ from concurrent.futures import ProcessPoolExecutor +from typing import TYPE_CHECKING, cast from servers.query_report.src.aggregates.game_server_info import GameServerInfo from servers.query_report.src.v2.contracts.requests import ClientMessageRequest -from servers.query_report.src.v2.enums.general import GameServerStatus, RequestType +from servers.query_report.src.v2.aggregates.enums import GameServerStatus, RequestType from servers.server_browser.src.exceptions.general import ServerBrowserException from servers.server_browser.src.v2.contracts.requests import ( SendMessageRequest, @@ -19,7 +20,7 @@ AdHocResult, ServerMainListResult, ) -from servers.server_browser.src.v2.enums.general import ( +from servers.server_browser.src.v2.aggregations.enums import ( # RequestType, ServerListUpdateOption, ) @@ -31,8 +32,9 @@ def get_clients(game_name: str): client_list = [] assert isinstance(game_name, str) - for ip, c in Client.pool.items(): - client: Client = c + for ip, client in Client.pool.items(): + if TYPE_CHECKING: + client = cast(Client, client) if client.info.game_name == game_name: client_list.append(client) @@ -48,8 +50,7 @@ def __init__(self, message: GameServerInfo) -> None: self._message = message def handle(self) -> None: - result = AdHocResult() - result.game_server_info = self._message + result = AdHocResult(game_server_info=self._message) match (self._message.status): case ( status diff --git a/src/servers/server_browser/src/v2/applications/server_launcher.py b/src/servers/server_browser/src/v2/applications/server_launcher.py index c1d47bc40..b77c7938d 100644 --- a/src/servers/server_browser/src/v2/applications/server_launcher.py +++ b/src/servers/server_browser/src/v2/applications/server_launcher.py @@ -11,7 +11,7 @@ def __init__(self) -> None: self.config = CONFIG.servers["ServerBrowserV2"] def _launch_server(self): - TcpServer(self.config, Client).start() + TcpServer(self.config, Client, self.logger).start() if __name__ == "__main__": diff --git a/src/servers/server_browser/src/v2/contracts/requests.py b/src/servers/server_browser/src/v2/contracts/requests.py index d0dcf7263..de15ad283 100644 --- a/src/servers/server_browser/src/v2/contracts/requests.py +++ b/src/servers/server_browser/src/v2/contracts/requests.py @@ -1,9 +1,10 @@ from socket import inet_ntoa +from typing import Optional from servers.server_browser.src.v2.abstractions.contracts import ( AdHocRequestBase, ServerListUpdateOptionRequestBase, ) -from servers.server_browser.src.v2.enums.general import RequestType, ServerListUpdateOption +from servers.server_browser.src.v2.aggregations.enums import RequestType, ServerListUpdateOption class ServerListRequest(ServerListUpdateOptionRequestBase): @@ -18,21 +19,21 @@ def parse(self) -> None: remain_data = self.raw_request[9:] dev_game_name_index = remain_data.index(0) self.dev_game_name = remain_data[:dev_game_name_index].decode() - remain_data = remain_data[dev_game_name_index + 1 :] + remain_data = remain_data[dev_game_name_index + 1:] game_name_index = remain_data.index(0) self.game_name = remain_data[:game_name_index].decode() - remain_data = remain_data[game_name_index + 1 :] + remain_data = remain_data[game_name_index + 1:] self.client_challenge = remain_data[:8].decode() remain_data = remain_data[8:] filter_index = remain_data.index(0) if filter_index > 0: self.filter = remain_data[:filter_index].decode() - remain_data = remain_data[filter_index + 1 :] + remain_data = remain_data[filter_index + 1:] keys_index = remain_data.index(0) self.keys = remain_data[:keys_index].decode().split("\\") - remain_data = remain_data[keys_index + 1 :] + remain_data = remain_data[keys_index + 1:] byte_update_options = remain_data[:4][::-1] self.update_option = ServerListUpdateOption(int(byte_update_options)) @@ -52,6 +53,10 @@ class SendMessageRequest(AdHocRequestBase): prefix_message: bytes client_message: bytes + def __init__(self, raw_request: Optional[bytes] = None) -> None: + if raw_request is not None: + super().__init__(raw_request) + def parse(self) -> None: super().parse() self.client_message = self.raw_request[9:] diff --git a/src/servers/server_browser/src/v2/contracts/responses.py b/src/servers/server_browser/src/v2/contracts/responses.py index 41c05c231..0bbfa9fd9 100644 --- a/src/servers/server_browser/src/v2/contracts/responses.py +++ b/src/servers/server_browser/src/v2/contracts/responses.py @@ -14,7 +14,7 @@ ServerMainListResult, ) -from servers.server_browser.src.v2.enums.general import GameServerFlags, ResponseType +from servers.server_browser.src.v2.aggregations.enums import GameServerFlags, ResponseType class DeleteServerInfoResponse(AdHocResponseBase): @@ -22,7 +22,7 @@ class DeleteServerInfoResponse(AdHocResponseBase): def __init__(self, result: AdHocResult) -> None: assert isinstance(result, AdHocResult) - super().__init__(None, result) + self._result = result def build(self): buffer = bytearray() @@ -34,13 +34,14 @@ def build(self): class UpdateServerInfoResponse(AdHocResponseBase): def __init__(self, result: AdHocResult) -> None: assert isinstance(result, AdHocResult) - super().__init__(None, result) + self._result = result def build(self) -> None: self._buffer.append(ResponseType.PUSH_SERVER_MESSAGE) self.__build_single_server_full_info() - msg_leng = len(self._buffer).to_bytes(2, "big") - self._buffer.insert(0, msg_leng) + msg_leng = bytearray(len(self._buffer).to_bytes(2, "big")) + # make sure the msg_leng is at first 2 bytes + self._buffer = msg_leng+self._buffer self.sending_buffer = bytes(self._buffer) def __build_single_server_full_info(self): @@ -49,15 +50,19 @@ def __build_single_server_full_info(self): ) self._buffer.extend(header) if self._result.game_server_info.server_data is not None: - server_data = UpdateServerInfoResponse._build_kv() + server_data = UpdateServerInfoResponse._build_kv( + self._result.game_server_info.server_data) self._buffer.extend(server_data) if self._result.game_server_info.player_data is not None: - server_data = UpdateServerInfoResponse._build_kv() - self._buffer.extend(server_data) + for pd in self._result.game_server_info.player_data: + player_data = UpdateServerInfoResponse._build_kv(pd) + self._buffer.extend(player_data) if self._result.game_server_info.team_data is not None: - server_data = UpdateServerInfoResponse._build_kv() - self._buffer.extend(server_data) + for td in self._result.game_server_info.team_data: + team_data = UpdateServerInfoResponse._build_kv(td) + self._buffer.extend(team_data) + @staticmethod def _build_kv(data: dict): buffer = [] for k, v in data.items(): @@ -82,18 +87,18 @@ def build(self) -> None: def _build_servers_full_info(self): for room in self._result.peer_room_infos: self._servers_info_buffers.append(GameServerFlags.HAS_KEYS_FLAG) - group_id_bytes = room.group_id.to_bytes("big") + group_id_bytes = room.group_id.to_bytes() self._servers_info_buffers.extend(group_id_bytes) for key in self._request.keys: - self._servers_info_buffers.append(NTS_STRING_FLAG) + self._servers_info_buffers.extend(NTS_STRING_FLAG) value = ( room.raw_key_values[key] if key in room.raw_key_values.keys() else "" ) self._servers_info_buffers.extend(get_bytes(value)) - self._servers_info_buffers.append(STRING_SPLITER) - end_flag = int(0).to_bytes("little") + self._servers_info_buffers.extend(STRING_SPLITER) + end_flag = b"\x00" self._servers_info_buffers.extend(end_flag) @@ -106,10 +111,11 @@ def __build_servers_full_info(self): header = build_server_info_header(self._result.flag, info) self._servers_info_buffers.extend(header) for key in self._request.keys: - self._servers_info_buffers.append(NTS_STRING_FLAG) + self._servers_info_buffers.extend(NTS_STRING_FLAG) if key in info.server_data.keys(): - self._servers_info_buffers.extend(get_bytes(info.server_data[key])) - self._servers_info_buffers.append(STRING_SPLITER) + self._servers_info_buffers.extend( + get_bytes(info.server_data[key])) + self._servers_info_buffers.extend(STRING_SPLITER) self._servers_info_buffers.extend(ALL_SERVER_END_FLAG) diff --git a/src/servers/web_services/src/modules/sake/contracts/requests.py b/src/servers/web_services/src/modules/sake/contracts/requests.py index 444fce539..5acb1ca2a 100644 --- a/src/servers/web_services/src/modules/sake/contracts/requests.py +++ b/src/servers/web_services/src/modules/sake/contracts/requests.py @@ -1,8 +1,8 @@ from copy import copy, deepcopy +from typing import Optional import xml.etree.ElementTree as ET -from typing import OrderedDict -from pydantic import BaseModel +from servers.web_services.src.exceptions.general import WebException from servers.web_services.src.modules.sake.abstractions.general import ( RequestBase, NAMESPACE, @@ -27,8 +27,12 @@ def parse(self) -> None: for f in record_fields: temp = [] name = f.find(f".//{{{NAMESPACE}}}name") + if name is None or name.text is None: + raise WebException("name can not be None") temp.append(name.text) value = f.find(f".//{{{NAMESPACE}}}value") + if value is None or value.text is None: + raise WebException("value can not be None") for v in value: temp.append(v.tag.split("}")[1]) for i in v: @@ -44,14 +48,18 @@ def parse(self) -> None: super().parse() record_id = self._content_element.find(f".//{{{NAMESPACE}}}recordid") - if record_id is None: + if record_id is None or record_id.text is None: raise SakeException("recordid is missing from request") self.record_id = int(record_id.text) class GetMyRecordsRequest(RequestBase): - fields: list[tuple[str, str]] = [] + fields: list[tuple[Optional[str], str]] + + def __init__(self, raw_request: str) -> None: + super().__init__(raw_request) + self.fields = [] def parse(self) -> None: super().parse() @@ -60,17 +68,19 @@ def parse(self) -> None: raise SakeException("fields is missing from request") for e in fields: data = (e.text, e.tag.split("}")[1]) + if data is None: + raise WebException("data can not be None") self.fields.append(data) class GetRandomRecordsRequest(RequestBase): max: int - fields: list[tuple[str, str]] = [] + fields: list[tuple[Optional[str], str]] = [] def parse(self) -> None: super().parse() max = self._content_element.find(f".//{{{NAMESPACE}}}max") - if max is None: + if max is None or max.text is None: raise SakeException("max is missing from request") self.max = int(max.text) @@ -79,6 +89,8 @@ def parse(self) -> None: raise SakeException("fields is missing from request") for e in fields: data = (e.text, e.tag.split("}")[1]) + if data is None: + raise WebException("data can not be None") self.fields.append(data) @@ -155,7 +167,7 @@ class SearchForRecordsRequest(RequestBase): surrounding: str owner_ids: str cache_flag: str - fields: list[tuple[str, str]] + fields: list[tuple[Optional[str], str]] """ [ (field_name,field_type), @@ -244,6 +256,8 @@ def parse(self) -> None: values_node = self._content_element.find( f".//{{{NAMESPACE}}}values") # todo fix this + if values_node is None: + raise WebException("value node can not be none") temp_str = ET.tostring( element=values_node, encoding="unicode").replace("ns0:", "") self.values = xmltodict.parse(temp_str)['values']["RecordField"] From 0d03ffda43f54958a6d277b0278ee8d2ce143a4c Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 24 Oct 2024 03:54:42 +0000 Subject: [PATCH 121/231] fix: type hint errors --- src/backends/library/database/pg_orm.py | 12 ++-- src/backends/protocols/gamespy/chat/data.py | 3 + src/backends/protocols/gamespy/natneg/data.py | 67 ++++++++++++++----- .../protocols/gamespy/natneg/requests.py | 8 +-- .../presence_connection_manager/data.py | 39 ++++++++--- .../presence_connection_manager/requests.py | 23 ++++++- .../gamespy/presence_search_player/data.py | 42 ++++++++---- .../protocols/gamespy/query_report/data.py | 60 ++++++++++------- .../query_report/game_server_info_v2.py | 47 ------------- 9 files changed, 175 insertions(+), 126 deletions(-) delete mode 100644 src/backends/protocols/gamespy/query_report/game_server_info_v2.py diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 4945d9866..93b2544cd 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -1,3 +1,4 @@ +from typing import Optional from library.src.configs import CONFIG from sqlalchemy import Enum, create_engine from sqlalchemy.orm.session import Session @@ -197,8 +198,8 @@ class SakeStorage(Base): class InitPacketCaches(Base): __tablename__ = "init_packet_caches" + cookie = Column(Integer, primary_key=True, nullable=False) server_id = Column(UUID, nullable=False) - cookie = Column(Integer, nullable=False) version = Column(Integer, nullable=False) port_type = Column(Enum(NatPortType), nullable=False) client_index = Column(Enum(NatClientIndex), nullable=False) @@ -212,7 +213,8 @@ class InitPacketCaches(Base): class NatFailCaches(Base): - __tablename__ = "nat_fail_Cachess" + __tablename__ = "nat_fail_cachess" + record_id = Column(Integer, primary_key=True, autoincrement=True) public_ip_address1 = Column(INET, nullable=False) public_ip_address2 = Column(INET, nullable=False) update_time = Column(DateTime, nullable=False) @@ -225,9 +227,9 @@ class NatFailCaches(Base): class ChatChannelCaches(Base): - __tablename__ = "chat_channel_Caches" - server_id = Column(UUID, nullable=False) + __tablename__ = "chat_channel_caches" channel_name = Column(String, primary_key=True, nullable=False) + server_id = Column(UUID, nullable=False) game_name = Column(String, nullable=False) room_name = Column(String, nullable=False) topic = Column(String, nullable=False) @@ -256,9 +258,9 @@ class ChatUserCaches(Base): class GameServerCaches(Base): __tablename__ = "game_server_caches" + instant_key = Column(Integer, primary_key=True, nullable=False) server_id = Column(UUID, nullable=False) host_ip_address = Column(INET, nullable=False) - instant_key = Column(Integer, nullable=False) game_name = Column(String, nullable=False) query_report_port = Column(Integer, nullable=False) update_time = Column(DateTime, nullable=False) diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index 1e2172be1..8359c67e2 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -1,3 +1,4 @@ +from typing import TYPE_CHECKING, cast from backends.protocols.gamespy.chat.storage_infos import ChannelInfo, ChannelUser from backends.library.database.pg_orm import PG_SESSION, Users, Profiles, SubProfiles from servers.chat.src.aggregates.channel import Channel @@ -15,6 +16,8 @@ def nick_and_email_login(nick_name: str, email: str, password_hash: str) -> tupl Profiles.nick == nick_name, Users.password == password_hash ).first() + if TYPE_CHECKING: + result = cast(tuple[int, int, bool, bool], result) if result is None: # fmt: off raise ChatException(f"Can not find user with nickname:{nick_name} in database.") diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py index c2c138449..bf96774a9 100644 --- a/src/backends/protocols/gamespy/natneg/data.py +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -1,39 +1,70 @@ +from typing import overload from uuid import UUID +from backends.library.database.pg_orm import PG_SESSION, InitPacketCaches, NatFailCaches from backends.protocols.gamespy.game_traffic_relay.relay_server_info import RelayServerInfo from backends.protocols.gamespy.natneg.requests import InitRequest -from backends.protocols.gamespy.natneg.init_packet_info import InitPacketInfo, NatFailInfo +from servers.natneg.src.enums.general import NatClientIndex +# from backends.protocols.gamespy.natneg.init_packet_info import InitPacketInfo, NatFailInfo -def store_init_packet(request: InitRequest) -> None: - info = InitPacketInfo(request.model_dump()) - info.update(upsert=True) +def store_init_packet(info: InitPacketCaches) -> None: + assert isinstance(info, InitPacketCaches) + PG_SESSION.add(info) + PG_SESSION.commit() def count_init_info(cookie: int, version: int) -> int: - result = InitPacketInfo.objects(cookie=cookie, version=version).count() + result = PG_SESSION.query(InitPacketCaches).filter( + InitPacketCaches.cookie == cookie, InitPacketCaches.version == version,).count() return result -def get_init_infos(server_id: UUID, cookie: int) -> list[InitPacketInfo]: - result: list[InitPacketInfo] = InitPacketInfo.objects( - server_id=server_id, cookie=cookie) +def get_init_infos(server_id: UUID, cookie: int, client_index: NatClientIndex) -> list[InitPacketCaches]: + result = PG_SESSION.query(InitPacketCaches).filter( + InitPacketCaches.server_id == server_id, InitPacketCaches.cookie == cookie, InitPacketCaches.client_index == client_index).all() return result -def update_init_info(info: InitPacketInfo) -> None: - assert isinstance(info, InitPacketInfo) - info.save() +def update_init_info(info: InitPacketCaches) -> None: + assert isinstance(info, InitPacketCaches) + remove_init_info(info) + store_init_packet(info) -def remove_init_info(info: InitPacketInfo) -> None: - info.delete() +def remove_init_info(info: InitPacketCaches) -> None: + assert isinstance(info, InitPacketCaches) + PG_SESSION.delete(info) + PG_SESSION.commit() -def update_nat_fail_info(info: NatFailInfo) -> None: - info.save() +def store_nat_fail_info(info: NatFailCaches) -> None: + assert isinstance(info, NatFailCaches) + PG_SESSION.add(info) + PG_SESSION.commit() -def get_nat_fail_info(public_ip1: str, public_ip2: str) -> list[NatFailInfo]: - result = NatFailInfo.objects(public_ip_address1=public_ip1, - public_ip_address2=public_ip2) +def update_nat_fail_info(info: NatFailCaches) -> None: + assert isinstance(info, NatFailCaches) + result = get_nat_fail_info(info) + if result is not None: + remove_nat_fail_info(result) + store_nat_fail_info(info) + + +def remove_nat_fail_info(info: NatFailCaches) -> None: + assert isinstance(info, NatFailCaches) + PG_SESSION.delete(info) + PG_SESSION.commit() + + +def get_nat_fail_info(info: NatFailCaches): + + result = get_nat_fail_info_by_ip( + str(info.public_ip_address1), str(info.public_ip_address2)) + return result + + +def get_nat_fail_info_by_ip(public_ip1: str, public_ip2: str) -> list[NatFailCaches]: + result = PG_SESSION.query(NatFailCaches).filter(NatFailCaches.public_ip_address1 == public_ip1, + NatFailCaches.public_ip_address2 == public_ip2).all() return result diff --git a/src/backends/protocols/gamespy/natneg/requests.py b/src/backends/protocols/gamespy/natneg/requests.py index cffbf6957..4e1c1ef60 100644 --- a/src/backends/protocols/gamespy/natneg/requests.py +++ b/src/backends/protocols/gamespy/natneg/requests.py @@ -16,11 +16,11 @@ class RequestBase(lib.RequestBase): version: int cookie: int port_type: NatPortType - command_name: Union[RequestType, int] + command_name: RequestType class CommonRequestBase(RequestBase): - client_index: Union[NatClientIndex, int] + client_index: NatClientIndex use_game_port: bool @@ -29,7 +29,7 @@ class AddressCheckRequest(CommonRequestBase): class ConnectAckRequest(RequestBase): - client_index: Union[NatClientIndex, int] + client_index: NatClientIndex class ConnectRequest(RequestBase): @@ -37,7 +37,7 @@ class ConnectRequest(RequestBase): Server will send this request to client to let them connect to each other """ - client_index: Union[NatClientIndex, int] + client_index: NatClientIndex class ErtAckRequest(CommonRequestBase): diff --git a/src/backends/protocols/gamespy/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py index 211e9cb2e..840f7b01e 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/data.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/data.py @@ -1,4 +1,5 @@ -from library.src.database.pg_orm import ( +from typing import TYPE_CHECKING, cast +from backends.library.database.pg_orm import ( Blocked, Friends, Profiles, @@ -17,7 +18,8 @@ def is_email_exist(email: str) -> bool: def delete_friend_by_profile_id(profile_id: int): - friend = PG_SESSION.query(Friends).filter(Friends.friendid == profile_id).first() + friend = PG_SESSION.query(Friends).filter( + Friends.friendid == profile_id).first() if friend is None: raise GPDatabaseException( f"friend deletion have errors on profile id:{profile_id}" @@ -33,6 +35,8 @@ def get_blocked_profile_id_list(profile_id: int, namespace_id: int) -> list[int] .filter(Blocked.profileid == profile_id, Blocked.namespaceid == namespace_id) .all() ) + if TYPE_CHECKING: + result = cast(list[int], result) return result @@ -42,6 +46,8 @@ def get_friend_profile_id_list(profile_id: int, namespace_id: int) -> list[int]: .filter(Friends.profileid == profile_id, Friends.namespaceid == namespace_id) .all() ) + if TYPE_CHECKING: + result = cast(list[int], result) return result @@ -81,18 +87,22 @@ def get_user_info_list(email: str, nick_name: str) -> list[tuple[int, int, int]] of users that match the provided email and nickname in the database. """ result = ( - PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) + PG_SESSION.query(Users.userid, Profiles.profileid, + SubProfiles.subprofileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .filter(Users.email == email, Profiles.nick == nick_name) .all() ) + if TYPE_CHECKING: + result = cast(list[tuple[int, int, int]], result) return result def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: result = ( - PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) + PG_SESSION.query(Users.userid, Profiles.profileid, + SubProfiles.subprofileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .filter( @@ -101,12 +111,15 @@ def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: ) .first() ) + if TYPE_CHECKING: + result = cast(tuple[int, int, int], result) return result def get_user_infos(unique_nick: str, namespace_id: int) -> list[tuple[int, int, int]]: result = ( - PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) + PG_SESSION.query(Users.userid, Profiles.profileid, + SubProfiles.subprofileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .filter( @@ -115,6 +128,8 @@ def get_user_infos(unique_nick: str, namespace_id: int) -> list[tuple[int, int, ) .all() ) + if TYPE_CHECKING: + result = cast(list[tuple[int, int, int]], result) return result @@ -129,7 +144,8 @@ def update_block_info_list(target_id: int, profile_id: int, namespace_id: int) - .count() ) if result == 0: - b = Blocked(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) + b = Blocked(targetid=target_id, namespaceid=namespace_id, + profileid=profile_id) PG_SESSION.add(b) PG_SESSION.commit() @@ -144,7 +160,8 @@ def update_friend_info(target_id: int, profile_id: int, namespace_id: int): ) .count() ) - f = Friends(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) + f = Friends(targetid=target_id, namespaceid=namespace_id, + profileid=profile_id) if result == 0: PG_SESSION.add(f) @@ -152,7 +169,9 @@ def update_friend_info(target_id: int, profile_id: int, namespace_id: int): def add_nick_name(profile_id: int, old_nick: str, new_nick: str): - + assert isinstance(profile_id, int) + assert isinstance(old_nick, str) + assert isinstance(new_nick, str) result = ( PG_SESSION.query(Profiles) .filter(Profiles.profileid == profile_id, Profiles.nick == old_nick) @@ -162,7 +181,7 @@ def add_nick_name(profile_id: int, old_nick: str, new_nick: str): if result is None: raise GPDatabaseException("No user infomation found in database.") - result.nick = new_nick + result.nick = new_nick # type:ignore PG_SESSION.commit() @@ -177,7 +196,7 @@ def update_unique_nick(subprofile_id: int, unique_nick: str): .filter(SubProfiles.subprofileid == subprofile_id) .first() ) - result.uniquenick = unique_nick + result.uniquenick = unique_nick # type:ignore PG_SESSION.commit() diff --git a/src/backends/protocols/gamespy/presence_connection_manager/requests.py b/src/backends/protocols/gamespy/presence_connection_manager/requests.py index fcc3aeba2..d273298c0 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/requests.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/requests.py @@ -1,4 +1,4 @@ -from typing import Union +from typing import Optional, Union from pydantic import UUID4, BaseModel from servers.presence_connection_manager.src.aggregates.user_status import UserStatus @@ -58,6 +58,23 @@ class KeepAliveRequest(RequestBase): pass +class NewUserRequest(RequestBase): + product_id: int + game_port: int + cd_key: str + has_game_name: bool + has_product_id: bool + has_cdkey: bool + has_partner_id: bool + has_game_port: bool + nick: str + email: str + password: str + partner_id: int + game_name: str + uniquenick: str + + class LoginRequest(RequestBase): user_challenge: str response: str @@ -114,8 +131,8 @@ class RegisterNickRequest(RequestBase): class UpdateProfileRequest(RequestBase): - has_public_mask_flag: bool = None - public_mask: Union[PublicMasks, int] = None + has_public_mask_flag: Optional[bool] = None + public_mask: Optional[PublicMasks] = None session_key: str = None partner_id: int = None nick: str = None diff --git a/src/backends/protocols/gamespy/presence_search_player/data.py b/src/backends/protocols/gamespy/presence_search_player/data.py index 869deb1da..145a91bd7 100644 --- a/src/backends/protocols/gamespy/presence_search_player/data.py +++ b/src/backends/protocols/gamespy/presence_search_player/data.py @@ -1,5 +1,6 @@ +from typing import TYPE_CHECKING, Optional, cast from sqlalchemy import insert -from library.src.database.pg_orm import ( +from backends.library.database.pg_orm import ( Friends, Profiles, SubProfiles, @@ -81,16 +82,17 @@ def get_user(email: str): def get_profile(user_id: int, nick_name: str) -> Profiles: result = PG_SESSION.query(Profiles).filter( Profiles.userid == user_id, Profiles.nick == nick_name - ) + ).first() return result def get_sub_profile(profile_id: int, namespace_id: int, product_id: int) -> SubProfiles: - PG_SESSION.query(SubProfiles).filter( + result = PG_SESSION.query(SubProfiles).filter( SubProfiles.profileid == profile_id, SubProfiles.namespaceid == namespace_id, SubProfiles.namespaceid == product_id, - ) + ).first() + return result def get_nick_and_unique_nick_list(email: str, password: str, namespace_id: int): @@ -129,7 +131,8 @@ def get_friend_info_list(profile_id: int, namespace_id: int, game_name: str): .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .filter( - Profiles.profileid.in_(PG_SESSION.query(Friends.profileid == profile_id)), + Profiles.profileid.in_(PG_SESSION.query( + Friends.profileid == profile_id)), SubProfiles.namespaceid == namespace_id, SubProfiles.gamename == game_name, ) @@ -140,7 +143,7 @@ def get_friend_info_list(profile_id: int, namespace_id: int, game_name: str): def get_matched_profile_info_list( profile_ids: list[int], namespace_id: int -) -> list[tuple[int, str]]: +) -> Optional[list[tuple[int, str]]]: """ return [(profileid,uniquenick)] @@ -153,12 +156,14 @@ def get_matched_profile_info_list( ) .all() ) + if TYPE_CHECKING: + result = cast(Optional[list[tuple[int, str]]], result) return result def get_matched_info_by_nick( nick_name: str, -) -> list[tuple[int, str, str, str, str, int]]: +) -> Optional[list[tuple[int, str, str, str, str, int]]]: result = ( PG_SESSION.query( Profiles.profileid, @@ -173,6 +178,9 @@ def get_matched_info_by_nick( .filter(Profiles.nick == nick_name) .all() ) + if TYPE_CHECKING: + result = cast( + Optional[list[tuple[int, str, str, str, str, int]]], result) return result @@ -193,6 +201,8 @@ def get_matched_info_by_email( .filter(Users.email == email) .all() ) + if TYPE_CHECKING: + result = cast(list[tuple[int, str, str, str, str, int]], result) return result @@ -234,10 +244,12 @@ def get_matched_info_by_uniquenick_and_namespaceid( ) .all() ) + if TYPE_CHECKING: + result = cast(list[tuple[int, str, str, str, str, int]], result) return result -def is_uniquenick_exist(unique_nick: str, namespace_id: int, game_name: str): +def is_uniquenick_exist(unique_nick: str, namespace_id: int, game_name: str) -> bool: result = ( PG_SESSION.query(Profiles) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) @@ -246,15 +258,19 @@ def is_uniquenick_exist(unique_nick: str, namespace_id: int, game_name: str): SubProfiles.gamename == game_name, SubProfiles.namespaceid == namespace_id, ) - .all() + .count() ) - return result + if result == 0: + return False + else: + return True -def is_email_exist(email: str): - result = PG_SESSION.query(Users.userid).filter(Users.email == email).count() - #! According to FSW partnerid is not nessesary +def is_email_exist(email: str) -> bool: + result = PG_SESSION.query(Users.userid).filter( + Users.email == email).count() + # According to game partnerid is not nessesary if result == 0: return False return True diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index 74030f6e2..df576576b 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -1,15 +1,17 @@ +from typing import TYPE_CHECKING, Optional, cast from backends.protocols.gamespy.chat.storage_infos import ChannelInfo -from library.src.database.pg_orm import PG_SESSION, GroupList, Games +from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, GroupList, Games, GameServerCaches from servers.chat.src.aggregates.peer_room import PeerRoom -from backends.protocols.gamespy.query_report.game_server_info_v2 import GameServerInfoV2 -def get_all_groups(): - result: list[tuple[Games, GroupList]] = ( +def get_all_groups() -> dict: + result = ( PG_SESSION.query(Games, GroupList) - .join(GroupList, Games.gameid == GroupList.gameid) + .join(GroupList, (Games.gameid == GroupList.gameid)) .all() ) + if TYPE_CHECKING: + result = cast(list[tuple[Games, GroupList]], result) # Group the results by Game name grouped_result = {} @@ -31,53 +33,59 @@ def get_all_groups(): return grouped_result -def get_peer_staging_channels(game_name: str, group_id: int): +def get_peer_staging_channels(game_name: str, group_id: int) -> list[ChatChannelCaches]: assert isinstance(game_name, str) assert isinstance(group_id, int) staging_name = f"{PeerRoom.StagingRoomPrefix}!{game_name}!*" - - result: list[ChannelInfo] = ChannelInfo.objects(channel_name=staging_name) + result = PG_SESSION.query(ChatChannelCaches).filter( + ChatChannelCaches.channel_name == staging_name).all() return result -def get_peer_group_channel(group_id: int): +def get_peer_group_channel(group_id: int) -> list[ChatChannelCaches]: assert isinstance(group_id, int) group_name = f"{PeerRoom.GroupRoomPrefix}!{group_id}" - result: list[ChannelInfo] = ChannelInfo.objects(channel_name=group_name) + + result = PG_SESSION.query(ChatChannelCaches).filter( + ChatChannelCaches.channel_name == group_name).all() return result -def get_server_info_with_instant_key(instant_key: int) -> GameServerInfoV2: +def get_server_info_with_instant_key(instant_key: int) -> Optional[GameServerCaches]: assert isinstance(instant_key, int) - - result = GameServerInfoV2.objects(instant_key=instant_key) - + result = PG_SESSION.query(GameServerCaches).filter( + GameServerCaches.instant_key == instant_key).first() return result -def get_server_info_with_game_name(game_name: str) -> GameServerInfoV2: +def get_server_info_with_game_name(game_name: str) -> Optional[GameServerCaches]: assert isinstance(game_name, str) - - result = GameServerInfoV2.objects(game_name=game_name).one() - + result = PG_SESSION.query(GameServerCaches).filter( + GameServerCaches.game_name == game_name).first() return result -def get_server_info_with_ip_and_port(ip: str, port: int) -> GameServerInfoV2: +def get_server_info_with_ip_and_port(ip: str, port: int) -> GameServerCaches: assert isinstance(ip, str) assert isinstance(port, int) - - result = GameServerInfoV2.objects( - host_ip_address=ip, query_report_port=port).one() + result = PG_SESSION.query(GameServerCaches).filter( + GameServerCaches.host_ip_address == ip, GameServerCaches.query_report_port == port).first() return result -def remove_server_info(info: GameServerInfoV2) -> None: - info.delete() + +def remove_server_info(info: GameServerCaches) -> None: + PG_SESSION.delete(info) + PG_SESSION.commit() + +# todo finish the GameServerCaches creation -def update_game_server(info: GameServerInfoV2) -> None: - info.update() +def update_game_server(info: GameServerCaches) -> None: + from datetime import datetime + info.update_time = datetime.now() # type:ignore + PG_SESSION.add(info) + PG_SESSION.commit() if __name__ == "__main__": diff --git a/src/backends/protocols/gamespy/query_report/game_server_info_v2.py b/src/backends/protocols/gamespy/query_report/game_server_info_v2.py deleted file mode 100644 index 179ae29fc..000000000 --- a/src/backends/protocols/gamespy/query_report/game_server_info_v2.py +++ /dev/null @@ -1,47 +0,0 @@ -from datetime import datetime, timezone -import uuid -from mongoengine import ( - Document, - StringField, - IntField, - UUIDField, - DateTimeField, - EnumField, - ListField, - DictField, -) - -# from library.src.database.mongodb_orm import connect_to_db, get_ttl_param -from backends.library.database.mongodb_orm import get_ttl_param -from servers.query_report.src.v2.aggregates.enums import GameServerStatus - - -class GameServerInfoV2(Document): - created = DateTimeField(default=datetime.now(timezone.utc)) - server_id = UUIDField(binary=False, required=True) - host_ip_address = StringField(required=True) - instant_key = IntField(required=True) - last_package_received_time = DateTimeField(required=True) - server_status = EnumField(GameServerStatus, required=True) - server_data = DictField(required=True) - query_report_port = IntField() - player_data = ListField(DictField()) - team_data = ListField(DictField()) - meta = get_ttl_param(30) - """expire after 30 seconds""" - - -if __name__ == "__main__": - connect_to_db() - result = GameServerInfoV2.objects().first() - if result is None: - dd = GameServerInfoV2( - server_id=uuid.UUID("212b7218-1758-11ef-94cc-70a8d36da155"), - host_ip_address="192.168.0.1", - instant_key=123, - last_package_received_time=datetime.now(timezone.utc), - server_status=GameServerStatus.NORMAL, - server_data={"hello": "hi"}, - ) - dd.save() - pass From 4dcd1bb81ee01ed9139ffa26755ae2ba8ade86aa Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 24 Oct 2024 03:55:13 +0000 Subject: [PATCH 122/231] refactor: docker compose config --- docker-compose-unispy-env.yml | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/docker-compose-unispy-env.yml b/docker-compose-unispy-env.yml index 0b9d7d14e..0834f26d4 100644 --- a/docker-compose-unispy-env.yml +++ b/docker-compose-unispy-env.yml @@ -3,19 +3,20 @@ version: '3' services: redis: image: redis + container_name: unispy_redis restart: always ports: - "6379:6379" command: redis-server --requirepass 123456 - mongodb: - image: mongodb/mongodb-community-server:latest - restart: always - ports: - - "27017:27017" - environment: - MONGO_INITDB_ROOT_USERNAME: unispy - MONGO_INITDB_ROOT_PASSWORD: 123456 + # mongodb: + # image: mongodb/mongodb-community-server:latest + # restart: always + # ports: + # - "27017:27017" + # environment: + # MONGO_INITDB_ROOT_USERNAME: unispy + # MONGO_INITDB_ROOT_PASSWORD: 123456 postgresql: image: postgres:14 @@ -28,4 +29,4 @@ services: POSTGRES_PASSWORD: 123456 POSTGRES_DB: unispy volumes: - - ./common/UniSpy_pg.sql:/docker-entrypoint-initdb.d/init.sql \ No newline at end of file + - ./common/UniSpy_pg.sql:/docker-entrypoint-initdb.d/init.sql From 99ef502d078f0fcfea5987aa4fc791dfb14cb3c7 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 24 Oct 2024 06:50:33 +0000 Subject: [PATCH 123/231] fix: type hint errors --- src/backends/library/database/mongodb_orm.py | 45 ------------- src/backends/library/database/pg_orm.py | 8 +++ src/backends/protocols/gamespy/chat/data.py | 64 +++++++++++-------- .../protocols/gamespy/chat/storage_infos.py | 38 ----------- .../protocols/gamespy/game_status/data.py | 16 ++++- .../gamespy/game_traffic_relay/data.py | 21 +++--- .../game_traffic_relay/relay_server_info.py | 11 ---- src/backends/protocols/gamespy/natneg/data.py | 20 +++--- .../protocols/gamespy/natneg/handlers.py | 9 ++- .../gamespy/natneg/init_packet_info.py | 39 ----------- .../presence_connection_manager/requests.py | 62 +++++++++--------- .../protocols/gamespy/query_report/data.py | 1 - 12 files changed, 120 insertions(+), 214 deletions(-) delete mode 100644 src/backends/library/database/mongodb_orm.py delete mode 100644 src/backends/protocols/gamespy/chat/storage_infos.py delete mode 100644 src/backends/protocols/gamespy/game_traffic_relay/relay_server_info.py delete mode 100644 src/backends/protocols/gamespy/natneg/init_packet_info.py diff --git a/src/backends/library/database/mongodb_orm.py b/src/backends/library/database/mongodb_orm.py deleted file mode 100644 index 98bcae66c..000000000 --- a/src/backends/library/database/mongodb_orm.py +++ /dev/null @@ -1,45 +0,0 @@ -from mongoengine import connect - -from library.src.configs import CONFIG - - -def connect_to_db(): - connect(host=CONFIG.mongodb.url) - - -def get_ttl_param(seconds: int): - assert isinstance(seconds, int) - return {"indexes": [{"fields": ["created"], "expireAfterSeconds": seconds}]} - - -if __name__ == "__main__": - from pymongo import MongoClient - - # Connect to the database - client = MongoClient('mongodb://unispy:123456@172.17.0.1:27017/') - # Select the database - db = client['mydatabase'] - - # Select the collection - collection = db['mycollection'] - - # Insert a document - document = {'name': 'John Doe', 'age': 30} - collection.insert_one(document) - - # Find all documents - documents = collection.find() - for document in documents: - print(document) - - # Update a document - filter = {'name': 'John Doe'} - update = {'$set': {'age': 31}} - collection.update_one(filter, update) - - # Delete a document - filter = {'name': 'John Doe'} - collection.delete_one(filter) - - # Close the client - client.close() diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 93b2544cd..2cacbacc9 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -226,6 +226,14 @@ class NatFailCaches(Base): # update_time = Column(DateTime, nullable=False) +class RelayServerCaches(Base): + __tablename__ = "relay_server_caches" + server_id = Column(UUID, primary_key=True, nullable=False) + public_ip_address = Column(String, nullable=False) + public_port = Column(Integer, nullable=False) + client_count = Column(Integer, nullable=False) + + class ChatChannelCaches(Base): __tablename__ = "chat_channel_caches" channel_name = Column(String, primary_key=True, nullable=False) diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index 8359c67e2..4cfdb0991 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -1,6 +1,5 @@ from typing import TYPE_CHECKING, cast -from backends.protocols.gamespy.chat.storage_infos import ChannelInfo, ChannelUser -from backends.library.database.pg_orm import PG_SESSION, Users, Profiles, SubProfiles +from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatUserCaches, Users, Profiles, SubProfiles from servers.chat.src.aggregates.channel import Channel from servers.chat.src.exceptions.general import ChatException @@ -35,50 +34,65 @@ def uniquenick_login(uniquenick:str,namespace_id:int)-> tuple[int, int, bool, bo # fmt: off raise ChatException(f"Can not find user with uniquenick:{uniquenick} in database.") # fmt on + if TYPE_CHECKING: + result = cast(tuple[int, int, bool, bool],result) return result def is_channel_exist(channel_name:str,game_name:str)->bool: - channel_count = ChannelInfo.objects.filter(channel_name = channel_name,game_name = game_name).count() + channel_count = PG_SESSION.query(ChatChannelCaches)\ + .filter(ChatChannelCaches.channel_name == channel_name, + ChatChannelCaches.game_name == game_name)\ + .count() if channel_count == 1: return True else: return False - -def update_channel(channel:Channel): - info = ChannelInfo( +def add_channel(channel:Channel): + info = ChatChannelCaches( channel_name=channel.name, game_name=channel.game_name, key_values =channel.kv_manager.data, max_num_user=channel.max_num_user, room_name=channel.room_name, topic=channel.topic, password=channel.password, group_id=channel.group_id, create_time=channel.create_time, previously_joined_channel=channel.previously_join_channel ) - info.save() + PG_SESSION.add(info) + PG_SESSION.commit() -def remove_channel(channel_name:str)->None: - info = ChannelInfo.objects(channel_name=channel_name).first() - info.delete() +def get_channel_cache(channel_name:str,game_name:str)->ChatChannelCaches: + channel = PG_SESSION.query(ChatChannelCaches)\ + .filter(ChatChannelCaches.channel_name == channel_name, + ChatChannelCaches.game_name == game_name)\ + .first() + return channel -def remove_user(nick_name:str): - user = ChannelUser.objects(nick_name == nick_name).first() - user.delete() +def update_channel(channel:Channel): -def is_user_exist(nick_name:str)->bool: - user_count = ChannelUser.objects.filter(nick_name == nick_name).count() - if user_count ==1: - return True - else: - return False + info = ChatChannelCaches( + channel_name=channel.name, game_name=channel.game_name, key_values =channel.kv_manager.data, max_num_user=channel.max_num_user, room_name=channel.room_name, topic=channel.topic, password=channel.password, group_id=channel.group_id, create_time=channel.create_time, previously_joined_channel=channel.previously_join_channel + ) + PG_SESSION.add(info) -def update_client(user:ChannelUser): - user.save() +def get_user_cache_by_nick_name(nick_name:str)->ChatUserCaches: + result = PG_SESSION.query(ChatUserCaches).filter(ChatUserCaches.nick_name == nick_name).first() + return result +def remove_channel(cache:ChatChannelCaches)->None: + assert isinstance(cache,ChatChannelCaches) + PG_SESSION.delete(cache) + PG_SESSION.commit() -def remove_user(nick_name:str): - user = ChannelUser.objects(nick_name == nick_name).first() - user.delete() +def remove_user(cache:ChatUserCaches): + assert isinstance(cache,ChatUserCaches) + PG_SESSION.delete(cache) + PG_SESSION.commit() def is_user_exist(nick_name:str)->bool: - user_count = ChannelUser.objects.filter(nick_name == nick_name).count() + user_count= PG_SESSION.query(ChatUserCaches).filter(ChatUserCaches.nick_name ==nick_name).count() if user_count ==1: return True else: return False +def update_client(cache:ChatUserCaches): + assert isinstance(cache,ChatUserCaches) + PG_SESSION.commit() + + diff --git a/src/backends/protocols/gamespy/chat/storage_infos.py b/src/backends/protocols/gamespy/chat/storage_infos.py deleted file mode 100644 index 34d456790..000000000 --- a/src/backends/protocols/gamespy/chat/storage_infos.py +++ /dev/null @@ -1,38 +0,0 @@ -from mongoengine import ( - Document, - StringField, - IntField, - UUIDField, - BooleanField, - DictField, - DateTimeField -) - - -class ChannelInfo(Document): - game_name = StringField(required=True) - channel_name = StringField(required=True) - key_values = DictField() - max_num_user = IntField(max_value=200) - room_name = StringField() - topic = StringField() - password = StringField() - group_id = IntField() - create_time = DateTimeField(required=True) - previously_joined_channel = StringField() - - -class ChannelUser(Document): - """ - We can use one of the info below to search the channel info - """ - server_id = UUIDField(binary=False, required=True) - channel_name = StringField(required=True) - user_name = StringField(required=True) - nick_name = StringField(required=True) - is_voiceable = BooleanField(required=True) - is_channel_operator = BooleanField(required=True) - is_channel_creator = BooleanField(required=True) - remote_ip_address = StringField(required=True) - remote_port = IntField(required=True) - key_values = DictField(required=True) diff --git a/src/backends/protocols/gamespy/game_status/data.py b/src/backends/protocols/gamespy/game_status/data.py index dce9210eb..5ddbe3fa1 100644 --- a/src/backends/protocols/gamespy/game_status/data.py +++ b/src/backends/protocols/gamespy/game_status/data.py @@ -1,3 +1,4 @@ +from typing import TYPE_CHECKING, cast from backends.library.database.pg_orm import PG_SESSION, PStorage, Profiles, SubProfiles from servers.game_status.src.enums.general import PersistStorageType from servers.game_status.src.exceptions.general import GSException @@ -20,21 +21,30 @@ def get_profile_id_by_token(token: str) -> int: SubProfiles.authtoken == token).first() if result is None: raise GSException("No records found in database") + if TYPE_CHECKING: + result = cast(int, result) return result def get_profile_id(cdkey: str, nick_name: str) -> int: result = PG_SESSION.query(SubProfiles.profileid).join( - SubProfiles, Profiles.profileid == SubProfiles.profileid).filter(SubProfiles.cdkeyenc == cdkey, Profiles.nick == nick_name).first() + SubProfiles, Profiles.profileid == SubProfiles.profileid)\ + .filter(SubProfiles.cdkeyenc == cdkey, + Profiles.nick == nick_name)\ + .first() if result is None: raise GSException("No record found in database") + if TYPE_CHECKING: + result = cast(int, result) return result -def get_player_data(profile_id: int, storage_type: PersistStorageType, data_index: int) -> dict[str, str]: +def get_player_data(profile_id: int, storage_type: PersistStorageType, data_index: int) -> dict: result = PG_SESSION.query(PStorage.data).filter(PStorage.ptype == storage_type.value, PStorage.dindex == data_index, - PStorage.profileid == profile_id) + PStorage.profileid == profile_id).first() if result is None: raise GSException("No records found in database") + if TYPE_CHECKING: + result = cast(dict, result) return result diff --git a/src/backends/protocols/gamespy/game_traffic_relay/data.py b/src/backends/protocols/gamespy/game_traffic_relay/data.py index d30032c5b..ce2ee5543 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/data.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/data.py @@ -1,23 +1,28 @@ from uuid import UUID -from backends.protocols.gamespy.game_traffic_relay.relay_server_info import RelayServerInfo +from backends.library.database.pg_orm import PG_SESSION, RelayServerCaches -def get_available_relay_serves() -> list[RelayServerInfo]: +def get_available_relay_serves() -> list[RelayServerCaches]: """ Return ------ list of ip:port """ - result: list[RelayServerInfo] = list(RelayServerInfo.objects) + result: list[RelayServerCaches] = PG_SESSION.query(RelayServerCaches).all() return result -def update_relay_server(info: RelayServerInfo): - info.save() +def update_relay_server(info: RelayServerCaches): + PG_SESSION.add(info) + PG_SESSION.commit() def delete_relay_server(server_id: UUID, ip_address: str, port: int): - info = RelayServerInfo.object( - server_id=server_id, public_ip_address=ip_address, public_port=port) - info.delete() + assert isinstance(server_id, UUID) + assert isinstance(ip_address, str) + assert isinstance(port, int) + info = PG_SESSION.query(RelayServerCaches).filter( + RelayServerCaches.server_id == server_id, RelayServerCaches.public_ip_address == ip_address, RelayServerCaches.public_port == port).first() + PG_SESSION.delete(info) + PG_SESSION.commit() diff --git a/src/backends/protocols/gamespy/game_traffic_relay/relay_server_info.py b/src/backends/protocols/gamespy/game_traffic_relay/relay_server_info.py deleted file mode 100644 index 118633004..000000000 --- a/src/backends/protocols/gamespy/game_traffic_relay/relay_server_info.py +++ /dev/null @@ -1,11 +0,0 @@ -from mongoengine import Document, StringField, IntField, UUIDField - - -class RelayServerInfo(Document): - server_id = UUIDField(binary=False, required=True) - public_ip_address = StringField(required=True) - public_port = IntField(required=True) - client_count = IntField(required=True) - # report how many client are connect to this relay server - - diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py index bf96774a9..4b4dce424 100644 --- a/src/backends/protocols/gamespy/natneg/data.py +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -1,10 +1,7 @@ -from typing import overload +from datetime import datetime, timedelta from uuid import UUID from backends.library.database.pg_orm import PG_SESSION, InitPacketCaches, NatFailCaches -from backends.protocols.gamespy.game_traffic_relay.relay_server_info import RelayServerInfo -from backends.protocols.gamespy.natneg.requests import InitRequest from servers.natneg.src.enums.general import NatClientIndex -# from backends.protocols.gamespy.natneg.init_packet_info import InitPacketInfo, NatFailInfo def store_init_packet(info: InitPacketCaches) -> None: @@ -14,14 +11,22 @@ def store_init_packet(info: InitPacketCaches) -> None: def count_init_info(cookie: int, version: int) -> int: + time = datetime.now()-timedelta(seconds=30) result = PG_SESSION.query(InitPacketCaches).filter( - InitPacketCaches.cookie == cookie, InitPacketCaches.version == version,).count() + InitPacketCaches.cookie == cookie, + InitPacketCaches.version == version, + InitPacketCaches.update_time >= time).count() return result def get_init_infos(server_id: UUID, cookie: int, client_index: NatClientIndex) -> list[InitPacketCaches]: + # query the latest init info with in 30 seconds + time = datetime.now()-timedelta(seconds=30) result = PG_SESSION.query(InitPacketCaches).filter( - InitPacketCaches.server_id == server_id, InitPacketCaches.cookie == cookie, InitPacketCaches.client_index == client_index).all() + InitPacketCaches.server_id == server_id, + InitPacketCaches.cookie == cookie, + InitPacketCaches.client_index == client_index, + InitPacketCaches.update_time >= time).all() return result @@ -47,7 +52,7 @@ def update_nat_fail_info(info: NatFailCaches) -> None: assert isinstance(info, NatFailCaches) result = get_nat_fail_info(info) if result is not None: - remove_nat_fail_info(result) + PG_SESSION.delete(result) store_nat_fail_info(info) @@ -58,7 +63,6 @@ def remove_nat_fail_info(info: NatFailCaches) -> None: def get_nat_fail_info(info: NatFailCaches): - result = get_nat_fail_info_by_ip( str(info.public_ip_address1), str(info.public_ip_address2)) return result diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py index 2cb7213c7..40ad739d4 100644 --- a/src/backends/protocols/gamespy/natneg/handlers.py +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -1,7 +1,6 @@ -from typing import Any, Coroutine from backends.library.abstractions.handler_base import HandlerBase +from backends.library.database.pg_orm import InitPacketCaches from backends.protocols.gamespy.natneg.data import update_init_info -from backends.protocols.gamespy.natneg.init_packet_info import InitPacketInfo from backends.protocols.gamespy.natneg.requests import InitRequest @@ -11,8 +10,8 @@ def __init__(self, request: InitRequest) -> None: assert isinstance(request, InitRequest) async def data_fetch(self) -> None: - info = InitPacketInfo(**self._request.model_dump()) - await update_init_info(info) + info = InitPacketCaches(**self._request.model_dump()) + update_init_info(info) async def result_construct(self) -> None: - self.response = {"message", "ok"} + self.response = {"message": "ok"} diff --git a/src/backends/protocols/gamespy/natneg/init_packet_info.py b/src/backends/protocols/gamespy/natneg/init_packet_info.py deleted file mode 100644 index 2a6eb264b..000000000 --- a/src/backends/protocols/gamespy/natneg/init_packet_info.py +++ /dev/null @@ -1,39 +0,0 @@ -from mongoengine import ( - Document, - StringField, - IntField, - UUIDField, - EnumField, - BooleanField, -) - -from servers.natneg.src.enums.general import NatClientIndex, NatPortType -import datetime - - -class InitPacketInfo(Document): - server_id = UUIDField(binary=False, required=True) - cookie = IntField(required=True) - version = IntField(required=True) - port_type = EnumField(NatPortType, required=True) - client_index = EnumField(NatClientIndex, required=True) - game_name = StringField(required=True) - use_game_port = BooleanField(required=True) - public_ip = StringField(required=True) - public_port = IntField(required=True) - private_ip = StringField(required=True) - private_port = IntField(required=True) - meta = {"expireAfterSeconds": int( - datetime.timedelta(minutes=3).total_seconds())} - - -class NatFailInfo(Document): - public_ip_address1 = StringField(required=True) - public_ip_address2 = StringField(required=True) - meta = {"expireAfterSeconds": int( - datetime.timedelta(days=1).total_seconds())} - """expire after 1 day""" - - -if __name__ == "__main__": - InitPacketInfo.objects() diff --git a/src/backends/protocols/gamespy/presence_connection_manager/requests.py b/src/backends/protocols/gamespy/presence_connection_manager/requests.py index d273298c0..5e5ce3477 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/requests.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/requests.py @@ -133,40 +133,40 @@ class RegisterNickRequest(RequestBase): class UpdateProfileRequest(RequestBase): has_public_mask_flag: Optional[bool] = None public_mask: Optional[PublicMasks] = None - session_key: str = None - partner_id: int = None - nick: str = None - uniquenick: str = None - has_first_name_flag: bool = None - first_name: str = None - has_last_name_flag: bool = None - last_name: str = None - has_icq_flag: bool = None - icq_uin: int = None - has_home_page_flag: bool = None - home_page: str = None - has_birthday_flag: bool = False - birth_day: int = None - birth_month: int = None - birth_year: int = None - has_sex_flag: bool = False - sex: bool = None - has_zip_code: bool = False - zip_code: str = None - has_country_code: bool = False - country_code: str = None + session_key: Optional[str] = None + partner_id: Optional[int] = None + nick: Optional[str] = None + uniquenick: Optional[str] = None + has_first_name_flag: Optional[bool] = None + first_name: Optional[str] = None + has_last_name_flag: Optional[bool] = None + last_name: Optional[str] = None + has_icq_flag: Optional[bool] = None + icq_uin: Optional[int] = None + has_home_page_flag: Optional[bool] = None + home_page: Optional[str] = None + has_birthday_flag: Optional[bool] = False + birth_day: Optional[int] = None + birth_month: Optional[int] = None + birth_year: Optional[int] = None + has_sex_flag: Optional[bool] = False + sex: Optional[bool] = None + has_zip_code: Optional[bool] = False + zip_code: Optional[str] = None + has_country_code: Optional[bool] = False + country_code: Optional[str] = None class UpdateUiRequest(RequestBase): - cpubrandid: str = None - cpuspeed: str = None - memory: str = None - videocard1ram: str = None - videocard2ram: str = None - connectionid: str = None - connectionspeed: str = None - hasnetwork: str = None - pic: str = None + cpubrandid: Optional[str] = None + cpuspeed: Optional[str] = None + memory: Optional[str] = None + videocard1ram: Optional[str] = None + videocard2ram: Optional[str] = None + connectionid: Optional[str] = None + connectionspeed: Optional[str] = None + hasnetwork: Optional[str] = None + pic: Optional[str] = None if __name__ == "__main__": diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index df576576b..68b65a67b 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -1,5 +1,4 @@ from typing import TYPE_CHECKING, Optional, cast -from backends.protocols.gamespy.chat.storage_infos import ChannelInfo from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, GroupList, Games, GameServerCaches from servers.chat.src.aggregates.peer_room import PeerRoom From 85b5292dd398ef4f491ef82aa27a993c15c1ef3e Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 24 Oct 2024 08:29:59 +0000 Subject: [PATCH 124/231] fix: unit-test errors --- .../game_status/src/contracts/requests.py | 7 ++-- .../src/applications/handlers.py | 6 +++- .../src/applications/switcher.py | 7 ++-- .../tests/game_tests.py | 36 ++++++++++++++++--- .../src/handlers/handlers.py | 18 +++++----- .../tests/game_tests.py | 7 ++-- .../src/modules/sake/contracts/requests.py | 12 +++---- 7 files changed, 65 insertions(+), 28 deletions(-) diff --git a/src/servers/game_status/src/contracts/requests.py b/src/servers/game_status/src/contracts/requests.py index d4674c63f..4d49b1612 100644 --- a/src/servers/game_status/src/contracts/requests.py +++ b/src/servers/game_status/src/contracts/requests.py @@ -204,16 +204,19 @@ def parse(self) -> None: @final class UpdateGameRequest(RequestBase): - connection_id: int + connection_id: Optional[int] is_done: bool is_client_local_storage_available: bool game_data: str game_data_dict: dict[str, str] session_key: str + def __init__(self, raw_request: object) -> None: + super().__init__(raw_request) + self.connection_id = None + def parse(self) -> None: super().parse() - if "gamedata" not in self.request_dict: raise GSException("gamedata is missing") self.game_data = self.request_dict["gamedata"] diff --git a/src/servers/presence_connection_manager/src/applications/handlers.py b/src/servers/presence_connection_manager/src/applications/handlers.py index bad103dea..34a3c9101 100644 --- a/src/servers/presence_connection_manager/src/applications/handlers.py +++ b/src/servers/presence_connection_manager/src/applications/handlers.py @@ -74,8 +74,8 @@ class LoginHandler(CmdHandlerBase): def __init__(self, client: "Client", request: LoginRequest) -> None: assert isinstance(request, LoginRequest) - super().__init__(client, request) self._result_cls = LoginResult + super().__init__(client, request) def _response_construct(self) -> None: self._response = LoginResponse(self._request, self._result) @@ -96,6 +96,10 @@ class NewUserHandler(CmdHandlerBase): _result: NewUserResult # todo create seperate request and result + def __init__(self, client: Client, request: RequestBase) -> None: + self._result_cls = NewUserResult + super().__init__(client, request) + def _response_construct(self): self._response = NewUserResponse(self._request, self._result) diff --git a/src/servers/presence_connection_manager/src/applications/switcher.py b/src/servers/presence_connection_manager/src/applications/switcher.py index 3f9f03899..374f50cb1 100644 --- a/src/servers/presence_connection_manager/src/applications/switcher.py +++ b/src/servers/presence_connection_manager/src/applications/switcher.py @@ -6,17 +6,16 @@ from servers.presence_connection_manager.src.abstractions.handlers import CmdHandlerBase from typing import TYPE_CHECKING, Optional, cast -if TYPE_CHECKING: - from servers.presence_connection_manager.src.applications.client import Client +from servers.presence_connection_manager.src.applications.client import Client class Switcher(SwitcherBase): _raw_request: str def __init__(self, client: Client, raw_request: str) -> None: - super().__init__(client, raw_request) assert isinstance(client, Client) assert isinstance(raw_request, str) + super().__init__(client, raw_request) def _process_raw_request(self) -> None: if self._raw_request[0] != "\\": @@ -55,5 +54,7 @@ def _create_cmd_handlers(self, name: str, raw_request: str) -> Optional[CmdHandl return StatusHandler(self._client, StatusRequest(raw_request)) case "statusinfo": return StatusInfoHandler(self._client, StatusInfoRequest(raw_request)) + case "inviteto": + raise NotImplementedError() case _: return None diff --git a/src/servers/presence_connection_manager/tests/game_tests.py b/src/servers/presence_connection_manager/tests/game_tests.py index 4c8fdce32..0ba2c7e50 100644 --- a/src/servers/presence_connection_manager/tests/game_tests.py +++ b/src/servers/presence_connection_manager/tests/game_tests.py @@ -1,29 +1,57 @@ import unittest +from library.tests.mock_objects.general import create_mock_url +from servers.presence_connection_manager.src.applications.handlers import LoginHandler, NewUserHandler from servers.presence_connection_manager.src.contracts.requests import StatusRequest from servers.presence_connection_manager.tests.mock_objects import create_client - +import responses class GameTest(unittest.TestCase): + @responses.activate def test_civilization_4(self) -> None: raw_requests = [ "\\newuser\\\\email\\civ4@unispy.org\\nick\\civ4-tk\\passwordenc\\JMHGwQ__\\productid\\10435\\gamename\\civ4\\namespaceid\\17\\uniquenick\\civ4-tk\\id\\1\\final\\", "\\login\\\\challenge\\xMsHUXuWNXL3KMwmhoQZJrP0RVsArCYT\\uniquenick\\civ4-tk\\userid\\25\\profileid\\26\\response\\7f2c9c6685570ea18b7207d2cbd72452\\firewall\\1\\port\\0\\productid\\10435\\gamename\\civ4\\namespaceid\\17\\sdkrevision\\1\\id\\1\\final\\", ] client = create_client() + create_mock_url(client, NewUserHandler, { + "user_id": 0, "profile_id": 0}) + create_mock_url(client, LoginHandler, {"response_proof": "7f2c9c6685570ea18b7207d2cbd72452", "data": { + "user_id": 0, + "profile_id": 0, + "nick": "test", + "email": "test@gamespy.com", + "unique_nick": "test_unique", + "password_hash": "password", + "email_verified_flag": True, + "namespace_id": 0, + "sub_profile_id": 0, + "banned_flag": False + }}) for x in raw_requests: client.on_received(x.encode("ascii")) pass - + @responses.activate def test_conflict_global_storm(self) -> None: - # "\\lc\\1\\challenge\\NRNUJLZMLX\\id\\1\\final\\", + # "\\lc\\1\\challenge\\NRNUJLZMLX\\id\\1\\final\\", raw_requests = [ "\\login\\\\challenge\\KMylyQbZfqzKn9otxx32q4076sOUnKif\\user\\cgs1@cgs1@rs.de\\response\\c1a6638bbcfe130e4287bfe4aa792949\\port\\-15737\\productid\\10469\\gamename\\conflictsopc\\namespaceid\\1\\id\\1\\final\\", "\\inviteto\\\\sesskey\\58366\\products\\1038\\final\\", ] client = create_client() - + create_mock_url(client, LoginHandler, {"response_proof": "7f2c9c6685570ea18b7207d2cbd72452", "data": { + "user_id": 0, + "profile_id": 0, + "nick": "test", + "email": "test@gamespy.com", + "unique_nick": "test_unique", + "password_hash": "password", + "email_verified_flag": True, + "namespace_id": 0, + "sub_profile_id": 0, + "banned_flag": False + }}) for x in raw_requests: client.on_received(x.encode("ascii")) pass diff --git a/src/servers/presence_search_player/src/handlers/handlers.py b/src/servers/presence_search_player/src/handlers/handlers.py index 74d27c29f..06dd88700 100644 --- a/src/servers/presence_search_player/src/handlers/handlers.py +++ b/src/servers/presence_search_player/src/handlers/handlers.py @@ -15,8 +15,8 @@ class CheckHandler(CmdHandlerBase): def __init__(self, client: Client, request: CheckRequest) -> None: assert isinstance(request, CheckRequest) - super().__init__(client, request) self._result_cls = CheckResult + super().__init__(client, request) def _response_construct(self): self._response = CheckResponse(self._request, self._result) @@ -29,8 +29,8 @@ class NewUserHandler(CmdHandlerBase): def __init__(self, client: Client, request: NewUserRequest) -> None: assert isinstance(request, NewUserRequest) - super().__init__(client, request) self._result_cls = NewUserResult + super().__init__(client, request) def _response_construct(self): self._response = NewUserResponse(self._request, self._result) @@ -43,8 +43,8 @@ class NicksHandler(CmdHandlerBase): def __init__(self, client: Client, request: NicksRequest) -> None: assert isinstance(request, NicksRequest) - super().__init__(client, request) self._result_cls = NicksResult + super().__init__(client, request) def _response_construct(self): self._response = NicksResponse(self._request, self._result) @@ -57,8 +57,8 @@ class OthersHandler(CmdHandlerBase): def __init__(self, client: Client, request: OthersRequest) -> None: assert isinstance(request, OthersRequest) - super().__init__(client, request) self._result_cls = OthersResult + super().__init__(client, request) def _response_construct(self): self._response = OthersResponse(self._result) @@ -70,8 +70,8 @@ class OthersListHandler(CmdHandlerBase): def __init__(self, client: Client, request: OthersListRequest) -> None: assert isinstance(request, OthersListRequest) - super().__init__(client, request) self._result_cls = OthersListResult + super().__init__(client, request) def _response_construct(self): self._response = OthersListResponse(self._result) @@ -94,8 +94,8 @@ class SearchHandler(CmdHandlerBase): def __init__(self, client: Client, request: SearchRequest) -> None: assert isinstance(request, SearchRequest) - super().__init__(client, request) self._result_cls = SearchResult + super().__init__(client, request) def _response_construct(self): self._response = SearchResponse(self._result) @@ -107,8 +107,8 @@ class SearchUniqueHandler(CmdHandlerBase): def __init__(self, client: Client, request: SearchUniqueRequest) -> None: assert isinstance(request, SearchUniqueRequest) - super().__init__(client, request) self._result_cls = SearchUniqueResult + super().__init__(client, request) def _response_construct(self): self._response = SearchUniqueResponse(self._result) @@ -121,8 +121,8 @@ class UniqueSearchHandler(CmdHandlerBase): def __init__(self, client: Client, request: UniqueSearchRequest) -> None: assert isinstance(request, UniqueSearchRequest) - super().__init__(client, request) self._result_cls = UniqueSearchResult + super().__init__(client, request) def _response_construct(self): self._response = UniqueSearchResponse(self._request, self._result) @@ -135,8 +135,8 @@ class ValidHandler(CmdHandlerBase): def __init__(self, client: Client, request: ValidRequest) -> None: assert isinstance(request, ValidRequest) - super().__init__(client, request) self._result_cls = ValidResult + super().__init__(client, request) def _response_construct(self): self._response = ValidResponse(self._request, self._result) diff --git a/src/servers/presence_search_player/tests/game_tests.py b/src/servers/presence_search_player/tests/game_tests.py index 06634d102..5cd6bfade 100644 --- a/src/servers/presence_search_player/tests/game_tests.py +++ b/src/servers/presence_search_player/tests/game_tests.py @@ -1,4 +1,4 @@ -from typing import cast +from typing import TYPE_CHECKING, cast import unittest from library.src.extentions.password_encoder import process_password @@ -20,8 +20,9 @@ def test_check(self): switcher = CmdSwitcher(client, raw) switcher.handle() - request: CheckRequest = cast( - CheckRequest, switcher._handlers[0]._request) + request = switcher._handlers[0]._request + if TYPE_CHECKING: + request = cast(CheckRequest, request) response = switcher._handlers[0]._response self.assertEqual("spyguy", request.nick) self.assertEqual("spyguy@gamespy.com", request.email) diff --git a/src/servers/web_services/src/modules/sake/contracts/requests.py b/src/servers/web_services/src/modules/sake/contracts/requests.py index 5acb1ca2a..54f91be87 100644 --- a/src/servers/web_services/src/modules/sake/contracts/requests.py +++ b/src/servers/web_services/src/modules/sake/contracts/requests.py @@ -160,12 +160,12 @@ def parse(self) -> None: class SearchForRecordsRequest(RequestBase): - filter: str - sort: str + filter: Optional[str] + sort: Optional[str] offset: str max: str surrounding: str - owner_ids: str + owner_ids: Optional[str] cache_flag: str fields: list[tuple[Optional[str], str]] """ @@ -182,13 +182,13 @@ def parse(self) -> None: super().parse() filter = self._content_element.find( f".//{{{NAMESPACE}}}filter") - if filter is None or filter.text is None: + if filter is None: raise SakeException("No filter found.") self.filter = filter.text sort = self._content_element.find( f".//{{{NAMESPACE}}}sort") - if sort is None or sort.text is None: + if sort is None: raise SakeException("No sort found.") self.sort = sort.text @@ -212,7 +212,7 @@ def parse(self) -> None: owner_ids = self._content_element.find( f".//{{{NAMESPACE}}}ownerids") - if owner_ids is None or owner_ids.text is None: + if owner_ids is None: raise SakeException("No ownderids found.") self.owner_ids = owner_ids.text From 4d528ca1c79a85bd7670e0e050705352a70282ae Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 25 Oct 2024 04:00:17 +0000 Subject: [PATCH 125/231] fix: recursive import --- src/.devcontainer/devcontainer.json | 9 ++- src/.vscode/launch.json | 43 +++++++++-- src/backends/routers/gamespy/chat.py | 1 - .../routers/gamespy/presence_search_player.py | 3 +- src/backends/routers/gamespy/query_report.py | 5 +- src/backends/routers/home.py | 8 ++- src/library/src/abstractions/client.py | 2 +- src/library/src/abstractions/handler.py | 3 +- .../src/abstractions/server_launcher.py | 23 +++--- src/library/src/abstractions/switcher.py | 2 + src/library/src/configs.py | 71 +++++-------------- src/requirements.txt | 6 +- src/servers/chat/src/abstractions/channel.py | 14 ++-- src/servers/chat/src/abstractions/contract.py | 3 - src/servers/chat/src/aggregates/channel.py | 25 +------ .../chat/src/aggregates/channel_user.py | 2 +- .../{key_value_manager.py => managers.py} | 28 ++++++++ src/servers/chat/src/handlers/channel.py | 3 +- .../src/applications/handlers.py | 3 +- .../v1/aggregations/game_server_info_v1.py | 18 ----- .../src/v2/abstractions/contracts.py | 1 - .../v2/aggregations/server_info_builder.py | 2 +- 22 files changed, 141 insertions(+), 134 deletions(-) rename src/servers/chat/src/aggregates/{key_value_manager.py => managers.py} (54%) delete mode 100644 src/servers/query_report/src/v1/aggregations/game_server_info_v1.py diff --git a/src/.devcontainer/devcontainer.json b/src/.devcontainer/devcontainer.json index 0fec28679..98f9afb34 100644 --- a/src/.devcontainer/devcontainer.json +++ b/src/.devcontainer/devcontainer.json @@ -3,6 +3,12 @@ { "name": "Python 3", // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile + // "initializeCommand":"docker build --network unispy --tag mcr.microsoft.com/devcontainers/python:1-3.12-bullseye .", + // "containerName": "unispy_server_dev", + "runArgs": [ + "--network=unispy", + "--name=unispy_server_dev" + ], "image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye", // Features to add to the dev container. More info: https://containers.dev/features. // "features": {}, @@ -21,7 +27,8 @@ 80 ], // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "pip3 install --user -r requirements.txt" + "postCreateCommand": "pip3 config set global.index-url https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple && pip3 install --user -r requirements.txt" + // "postCreateCommand": "pip3 install --user -r requirements.txt" // Configure tool-specific properties. // "customizations": {}, // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json index 9e374c4f2..c743262ff 100644 --- a/src/.vscode/launch.json +++ b/src/.vscode/launch.json @@ -15,12 +15,47 @@ }, "justMyCode": true }, + { + "name": "backend", + "type": "debugpy", + "request": "launch", + "module": "uvicorn", + "args": [ + "backends.routers.home:app", // Replace with your module name if different + "--host", + "127.0.0.1", + "--port", + "8080", + "--reload" // Optional: enables auto-reload on code changes + ], + "console": "integratedTerminal", + "env": { + "PYTHONPATH": "${workspaceFolder}", + }, + "justMyCode": true + }, { "name": "pcm", "type": "debugpy", "request": "launch", - "program": "${workSpace}/servers/presence_connection_manager.py", - "console": "integratedTerminal" - } - ] + "program": "servers/presence_connection_manager/src/applications/server_launcher.py", + "console": "integratedTerminal", + "env": { + "PYTHONPATH": "${workspaceFolder}", + }, + "justMyCode": true + }, + { + "name": "psp", + "type": "debugpy", + "request": "launch", + "program": "servers/presence_search_player/src/applications/server_launcher.py", + "console": "integratedTerminal", + "env": { + "PYTHONPATH": "${workspaceFolder}", + }, + "justMyCode": true + }, + ], + "compounds": [] } \ No newline at end of file diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 6429d1480..e01b37805 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -1,7 +1,6 @@ from dataclasses import dataclass import json from typing import Optional -import uuid from backends.urls import CHAT from fastapi import APIRouter, FastAPI, WebSocket, WebSocketDisconnect diff --git a/src/backends/routers/gamespy/presence_search_player.py b/src/backends/routers/gamespy/presence_search_player.py index ca81f8449..958b01b64 100644 --- a/src/backends/routers/gamespy/presence_search_player.py +++ b/src/backends/routers/gamespy/presence_search_player.py @@ -1,8 +1,7 @@ from fastapi import APIRouter -from backends.protocols.gamespy.presence_search_player.requests import CheckRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest +from backends.protocols.gamespy.presence_search_player.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest from backends.urls import PRESENCE_SEARCH_PLAYER -from servers.presence_search_player.src.contracts.requests import NewUserRequest router = APIRouter() diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index 5b7c57dcf..2c2f924b8 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -1,9 +1,8 @@ from fastapi import APIRouter -from backends.protocols.gamespy.query_report.requests import ChallengeRequest, ClientMessageRequest, EchoRequest, HeartBeatRequest +from backends.protocols.gamespy.presence_connection_manager.requests import KeepAliveRequest +from backends.protocols.gamespy.query_report.requests import AvaliableRequest, ChallengeRequest, ClientMessageRequest, EchoRequest, HeartBeatRequest from backends.urls import QUERY_REPORT -from servers.presence_connection_manager.src.contracts.requests import KeepAliveRequest -from servers.query_report.src.v2.contracts.requests import AvaliableRequest router = APIRouter() diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index 506d445df..3813df52e 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -1,7 +1,8 @@ from fastapi import FastAPI +import uvicorn from library.src.log.log_manager import LogManager -from library.src.configs import CONFIG, ServerConfig +from library.src.configs import ServerConfig from backends.routers.gamespy import chat, gstats, natneg, presence_connection_manager, presence_search_player, query_report, server_browser, webservices app = FastAPI() @@ -21,3 +22,8 @@ def home(request: ServerConfig): # todo add the server config to our database return {"status": "online"} + + +if __name__ == "__main__": + uvicorn.run("backends.routers.home:app", + host="127.0.0.1", port=8080, reload=True) diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index cb2675632..cfa6bf45f 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -71,7 +71,7 @@ def on_received(self, buffer: bytes | str) -> None: pass else: raise UniSpyException("buffer type is invalid") - + self.log_network_receving(buffer) switcher: "SwitcherBase" = self.create_switcher(buffer) switcher.handle() diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index fe423873d..244b50a4c 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -19,7 +19,8 @@ class CmdHandlerBase: """ _result_cls: "Type[ResultBase]" """ - the result type class, use to deserialize json data from backend + the result type class, use to deserialize json data from backend\n + the initialization of _result_cls must before call super().__init__() """ _is_uploading: bool """ diff --git a/src/library/src/abstractions/server_launcher.py b/src/library/src/abstractions/server_launcher.py index 68d1a98b3..043cb5632 100644 --- a/src/library/src/abstractions/server_launcher.py +++ b/src/library/src/abstractions/server_launcher.py @@ -5,10 +5,10 @@ from library.src.configs import CONFIG, ServerConfig import pyfiglet import requests - +from prettytable import PrettyTable VERSION = 0.45 -__SERVER_FULL_SHORT_NAME_MAPPING = MappingProxyType({ +_SERVER_FULL_SHORT_NAME_MAPPING = MappingProxyType({ "PresenceConnectionManager": "PCM", "PresenceSearchPlayer": "PSP", "CDKey": "CDKey", @@ -26,7 +26,7 @@ class ServerLauncherBase: config: ServerConfig logger: LogWriter - + def start(self): self.__show_unispy_logo() self._connect_to_backend() @@ -38,17 +38,22 @@ def __show_unispy_logo(self): print(pyfiglet.Figlet().renderText("UniSpy.Server")) # display version info print(f"version {VERSION}") + table = PrettyTable() + table.field_names = ["Server Name", + "Listening Address", "Listening Port"] + table.add_row([self.config.server_name, + self.config.public_address, self.config.listening_port]) + print(table) - @abc.abstractmethod def _launch_server(self) -> None: - pass + raise NotImplementedError("Override this function in child class") def _connect_to_backend(self): try: # post our server config to backends to register - resp: requests.Response = requests.post( - url=CONFIG.backend.url, - data=self.config.__dict__) + resp = requests.post( + url=CONFIG.backend.url+"/", + data=self.config.model_dump_json()) if resp.status_code == 200: data = resp.json() if data["status"] != "online": @@ -61,5 +66,5 @@ def _connect_to_backend(self): # fmt: on def _create_logger(self): - short_name = __SERVER_FULL_SHORT_NAME_MAPPING[self.config.server_name] + short_name = _SERVER_FULL_SHORT_NAME_MAPPING[self.config.server_name] self.logger = LogManager.create(short_name) diff --git a/src/library/src/abstractions/switcher.py b/src/library/src/abstractions/switcher.py index 40437b6d2..9d177dee0 100644 --- a/src/library/src/abstractions/switcher.py +++ b/src/library/src/abstractions/switcher.py @@ -49,6 +49,8 @@ def handle(self): handler.handle() except Exception as e: UniSpyException.handle_exception(e, self._client) + if CmdHandlerBase._debug: + raise e @abstractmethod def _process_raw_request(self) -> None: diff --git a/src/library/src/configs.py b/src/library/src/configs.py index 354c45543..701fff446 100644 --- a/src/library/src/configs.py +++ b/src/library/src/configs.py @@ -1,13 +1,12 @@ -from dataclasses import dataclass import os from typing import Literal, Optional from uuid import UUID -# from library.src.exceptions.general import UniSpyException +from pydantic import BaseModel,field_validator -@dataclass -class PostgreSql: + +class PostgreSql(BaseModel): server: str port: int database: str @@ -19,16 +18,12 @@ class PostgreSql: ssl_password: Optional[str] = None # Optional field for SSL password root_cert: Optional[str] = None # Optional field for root certificate - def __post_init__(self): - pass - @property def url(self) -> str: return f"postgresql://{self.username}:{self.password}@{self.server}:{self.port}/{self.database}?sslmode={self.ssl_mode}" -@dataclass -class RedisConfig: +class RedisConfig(BaseModel): server: str port: int user: str @@ -48,73 +43,43 @@ def url(self) -> str: ) -@dataclass -class ServerConfig: +class ServerConfig(BaseModel): server_id: UUID server_name: str public_address: str listening_port: int # Ensures listening_port is between 1 and 65535 -@dataclass -class LoggingConfig: +class LoggingConfig(BaseModel): path: str min_log_level: Literal["debug", "info", "warning", "error"] - def __post_init__(self): - if "~" in self.path: - self.path = os.path.expanduser(self.path) + @field_validator('path', mode='before') + def expand_user_path(cls, value): + if "~" in value: + return os.path.expanduser(value) + return value -@dataclass -class BackendConfig: +class BackendConfig(BaseModel): url: str -@dataclass -class MongoDbConfig: - server: str - username: str - password: str - port: int - database: str - - @property - def url(self): - url = f"mongodb+srv://{self.username}:{self.password}@{self.server}" - if self.port is not None: - url += f":{self.port}" - if self.database is not None: - url += f"/{self.database}" - return url - -@dataclass -class UniSpyServerConfig: +class UniSpyServerConfig(BaseModel): postgresql: PostgreSql redis: RedisConfig backend: BackendConfig servers: dict[str, ServerConfig] - mongodb: MongoDbConfig logging: LoggingConfig - def __post_init__(self): - - self.postgresql = PostgreSql(**self.postgresql) # type: ignore - self.redis = RedisConfig(**self.redis) # type: ignore - self.backend = BackendConfig(**self.backend) # type: ignore - for key, value in self.servers.items(): - self.servers[key] = ServerConfig(**value) # type: ignore - self.mongodb = MongoDbConfig(**self.mongodb) # type: ignore - self.logging = LoggingConfig(**self.logging) # type: ignore - - unispy_config = os.environ.get("UNISPY_CONFIG") +default_config = "../common/config.json" if unispy_config is None: - - raise Exception( - "Unispy server config not found, you should set the UNISPY_CONFIG in the system enviroment." - ) + unispy_config = default_config + # raise Exception( + # "Unispy server config not found, you should set the UNISPY_CONFIG in the system enviroment." + # ) if not os.path.exists(unispy_config): raise Exception( "Unispy server config file not exist, check UNISPY_CONFIG path." diff --git a/src/requirements.txt b/src/requirements.txt index 07348c04a..e4d978948 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -2,16 +2,14 @@ pyfiglet psycopg2-binary sqlalchemy jsonpickle == 3.0.3 -aioredis == 2.0.1 email_validator == 2.1.1 attrs requests -mongoengine == 0.28.2 fastapi xmltodict responses pydantic -responses redis websocket-client -uvicorn \ No newline at end of file +uvicorn +prettytable \ No newline at end of file diff --git a/src/servers/chat/src/abstractions/channel.py b/src/servers/chat/src/abstractions/channel.py index cd8e42821..523b205ab 100644 --- a/src/servers/chat/src/abstractions/channel.py +++ b/src/servers/chat/src/abstractions/channel.py @@ -1,13 +1,17 @@ -from typing import Optional +from typing import TYPE_CHECKING from library.src.abstractions.client import ClientBase from servers.chat.src.abstractions.contract import * from servers.chat.src.abstractions.contract import RequestBase from servers.chat.src.abstractions.handler import PostLoginHandlerBase -from servers.chat.src.aggregates.channel import Channel, ChannelManager -from servers.chat.src.aggregates.channel_user import ChannelUser +from servers.chat.src.aggregates.managers import ChannelManager + from servers.chat.src.exceptions.channel import NoSuchChannelException from servers.chat.src.exceptions.general import ChatException, NoSuchNickException +if TYPE_CHECKING: + from servers.chat.src.aggregates.channel import Channel + from servers.chat.src.aggregates.channel_user import ChannelUser + class ChannelRequestBase(RequestBase): channel_name: str @@ -29,8 +33,8 @@ def __init__(self, request: RequestBase, result: ResultBase) -> None: class ChannelHandlerBase(PostLoginHandlerBase): - _channel: Channel - _user: ChannelUser + _channel: "Channel" + _user: "ChannelUser" _request: ChannelRequestBase _response: ResponseBase diff --git a/src/servers/chat/src/abstractions/contract.py b/src/servers/chat/src/abstractions/contract.py index be7a92288..0df8a3c94 100644 --- a/src/servers/chat/src/abstractions/contract.py +++ b/src/servers/chat/src/abstractions/contract.py @@ -1,7 +1,5 @@ -import abc from typing import Optional import library.src.abstractions.contracts -from servers.chat.src.exceptions.general import ChatException class RequestBase(library.src.abstractions.contracts.RequestBase): @@ -15,7 +13,6 @@ def __init__(self, raw_request: str) -> None: assert isinstance(raw_request, str) super().__init__(raw_request) - @abc.abstractmethod def parse(self) -> None: # at most 2 colon character # we do not sure about all command diff --git a/src/servers/chat/src/aggregates/channel.py b/src/servers/chat/src/aggregates/channel.py index 30ea0d930..dd682e72e 100644 --- a/src/servers/chat/src/aggregates/channel.py +++ b/src/servers/chat/src/aggregates/channel.py @@ -1,6 +1,5 @@ -from dataclasses import dataclass import datetime -from typing import Optional, overload +from typing import Optional from uuid import UUID from pydantic import BaseModel, field_validator @@ -8,7 +7,7 @@ from library.src.configs import CONFIG from servers.chat.src.abstractions.contract import ResponseBase from servers.chat.src.aggregates.channel_user import ChannelUser -from servers.chat.src.aggregates.key_value_manager import KeyValueManager +from servers.chat.src.aggregates.managers import KeyValueManager from servers.chat.src.aggregates.peer_room import PeerRoom from servers.chat.src.applications.client import Client from servers.chat.src.contracts.requests.channel import ModeRequest @@ -178,26 +177,6 @@ def remove_user(self, user: ChannelUser): user.client.info.previously_joined_channel -class ChannelManager: - local_channels: dict = {} - """The code blow is for channel manage""" - - @staticmethod - def get_channel(name: str) -> Optional[Channel]: - if name in ChannelManager.local_channels: - return ChannelManager.local_channels[name] - return None - - @staticmethod - def add_channel(channel: Channel): - if channel.name not in ChannelManager.local_channels: - ChannelManager.local_channels[channel.name] = channel - - @staticmethod - def remove_channel(name: str) -> None: - if name in ChannelManager.local_channels: - del ChannelManager.local_channels[name] - class BrockerMessage(BaseModel): channel_name: str diff --git a/src/servers/chat/src/aggregates/channel_user.py b/src/servers/chat/src/aggregates/channel_user.py index bf6217555..1bb1db732 100644 --- a/src/servers/chat/src/aggregates/channel_user.py +++ b/src/servers/chat/src/aggregates/channel_user.py @@ -1,6 +1,6 @@ from uuid import UUID -from servers.chat.src.aggregates.key_value_manager import KeyValueManager +from servers.chat.src.aggregates.managers import KeyValueManager from servers.chat.src.applications.client import Client from typing import TYPE_CHECKING diff --git a/src/servers/chat/src/aggregates/key_value_manager.py b/src/servers/chat/src/aggregates/managers.py similarity index 54% rename from src/servers/chat/src/aggregates/key_value_manager.py rename to src/servers/chat/src/aggregates/managers.py index 4569ba2da..14fc78466 100644 --- a/src/servers/chat/src/aggregates/key_value_manager.py +++ b/src/servers/chat/src/aggregates/managers.py @@ -1,3 +1,6 @@ +from typing import TYPE_CHECKING, Optional + + class KeyValueManager: data: dict """ @@ -30,3 +33,28 @@ def get_value_string(self, keys: list[str]) -> str: def is_contain_all_key(self, keys: list[str]): return all(key in self.data for key in keys) + + +if TYPE_CHECKING: + from servers.chat.src.aggregates.channel import Channel + + +class ChannelManager: + local_channels: dict = {} + """The code blow is for channel manage""" + + @staticmethod + def get_channel(name: str) -> Optional["Channel"]: + if name in ChannelManager.local_channels: + return ChannelManager.local_channels[name] + return None + + @staticmethod + def add_channel(channel: "Channel"): + if channel.name not in ChannelManager.local_channels: + ChannelManager.local_channels[channel.name] = channel + + @staticmethod + def remove_channel(name: str) -> None: + if name in ChannelManager.local_channels: + del ChannelManager.local_channels[name] diff --git a/src/servers/chat/src/handlers/channel.py b/src/servers/chat/src/handlers/channel.py index 87ca26ddc..a9d84bcbd 100644 --- a/src/servers/chat/src/handlers/channel.py +++ b/src/servers/chat/src/handlers/channel.py @@ -1,7 +1,8 @@ from library.src.abstractions.client import ClientBase from servers.chat.src.abstractions.channel import ChannelHandlerBase -from servers.chat.src.aggregates.channel import Channel, ChannelManager +from servers.chat.src.aggregates.channel import Channel from servers.chat.src.aggregates.channel_user import ChannelUser +from servers.chat.src.aggregates.managers import ChannelManager from servers.chat.src.aggregates.response_name import * from servers.chat.src.contracts.requests.channel import ( GetCKeyRequest, diff --git a/src/servers/presence_connection_manager/src/applications/handlers.py b/src/servers/presence_connection_manager/src/applications/handlers.py index 34a3c9101..6af45f25e 100644 --- a/src/servers/presence_connection_manager/src/applications/handlers.py +++ b/src/servers/presence_connection_manager/src/applications/handlers.py @@ -170,7 +170,7 @@ class BuddyStatusInfoHandler(CmdHandlerBase): """ This is what the message should look like. Its broken up for easy viewing. - \bsi\\state\\profile\\bip\\bport\\hostip\\hprivip\\qport\\hport\\sessflags\\rstatus\\gameType\\gameVnt\\gameMn\\product\\qmodeflags\ + \\bsi\\state\\profile\\bip\\bport\\hostip\\hprivip\\qport\\hport\\sessflags\\rstatus\\gameType\\gameVnt\\gameMn\\product\\qmodeflags\ """ def __init__(self, client: Client, request: RequestBase) -> None: @@ -183,6 +183,7 @@ class DelBuddyHandler(LoginedHandlerBase): def __init__(self, client: Client, request: DelBuddyRequest) -> None: assert isinstance(request, DelBuddyRequest) + self._is_uploading = False super().__init__(client, request) diff --git a/src/servers/query_report/src/v1/aggregations/game_server_info_v1.py b/src/servers/query_report/src/v1/aggregations/game_server_info_v1.py deleted file mode 100644 index da5578cef..000000000 --- a/src/servers/query_report/src/v1/aggregations/game_server_info_v1.py +++ /dev/null @@ -1,18 +0,0 @@ -from mongoengine import ( - Document, - StringField, - IntField, - UUIDField, - BooleanField, - DictField, -) - - -class GameServerInfoV1(Document): - server_id = UUIDField(binary=False, required=True) - host_ip_address = StringField(required=True) - host_port = IntField(required=True) - game_name = StringField(required=True) - is_validated = BooleanField(required=True) - server_data = DictField(required=True) - meta = {"expireAfterSeconds": 30} diff --git a/src/servers/server_browser/src/v2/abstractions/contracts.py b/src/servers/server_browser/src/v2/abstractions/contracts.py index 3db3a8e8b..e56593acd 100644 --- a/src/servers/server_browser/src/v2/abstractions/contracts.py +++ b/src/servers/server_browser/src/v2/abstractions/contracts.py @@ -13,7 +13,6 @@ ServerListUpdateOption, ) -QUERY_REPORT_DEFAULT_PORT = 6500 class RequestBase(library.src.abstractions.contracts.RequestBase): diff --git a/src/servers/server_browser/src/v2/aggregations/server_info_builder.py b/src/servers/server_browser/src/v2/aggregations/server_info_builder.py index 572ae3ff7..134c1fbc9 100644 --- a/src/servers/server_browser/src/v2/aggregations/server_info_builder.py +++ b/src/servers/server_browser/src/v2/aggregations/server_info_builder.py @@ -3,10 +3,10 @@ from servers.query_report.src.aggregates.game_server_info import GameServerInfo from backends.protocols.gamespy.query_report.data import get_all_groups -from servers.server_browser.src.v2.abstractions.contracts import QUERY_REPORT_DEFAULT_PORT from servers.server_browser.src.v2.aggregations.enums import GameServerFlags PEER_GROUP_LIST = get_all_groups() +QUERY_REPORT_DEFAULT_PORT = 6500 def build_server_info_header( From d2e40ce48bfbba2daac9b0fb2c25f861f83bda55 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 28 Oct 2024 02:12:40 +0000 Subject: [PATCH 126/231] refactor: simplify files --- common/config.json | 8 +- common/pg_certs/root/root.crt | 77 +++ common/pg_certs/root/root.csr | 62 +++ common/pg_certs/root/root.key | 28 + common/pg_certs/root/root.srl | 1 + common/pg_certs/root/server.csr | 62 +++ common/pg_certs/server.crt | 68 +++ common/pg_certs/server.key | 28 + docker-compose-unispy-env.yml | 20 +- src/.vscode/launch.json | 110 +++- src/backends/library/database/pg_orm.py | 2 +- src/backends/protocols/gamespy/chat/data.py | 2 +- .../protocols/gamespy/chat/requests.py | 2 +- .../protocols/gamespy/game_status/data.py | 4 +- .../protocols/gamespy/game_status/requests.py | 2 +- src/backends/protocols/gamespy/natneg/data.py | 2 +- .../protocols/gamespy/natneg/requests.py | 2 +- src/backends/routers/gamespy/chat.py | 1 - src/backends/routers/home.py | 2 +- src/servers/chat/src/abstractions/channel.py | 3 +- src/servers/chat/src/abstractions/handler.py | 2 +- src/servers/chat/src/abstractions/message.py | 2 +- src/servers/chat/src/aggregates/channel.py | 7 +- .../{enums/general.py => aggregates/enums.py} | 30 +- .../general.py => aggregates/exceptions.py} | 52 +- src/servers/chat/src/aggregates/peer_room.py | 6 +- src/servers/chat/src/applications/handlers.py | 512 ++++++++++++++++++ .../{handlers => applications}/switcher.py | 12 +- src/servers/chat/src/contracts/__init__.py | 0 .../{requests/channel.py => requests.py} | 316 ++++++++++- .../chat/src/contracts/requests/__init__.py | 0 .../chat/src/contracts/requests/general.py | 286 ---------- .../chat/src/contracts/requests/message.py | 17 - src/servers/chat/src/contracts/responses.py | 411 ++++++++++++++ .../chat/src/contracts/responses/__init__.py | 0 .../chat/src/contracts/responses/channel.py | 174 ------ .../chat/src/contracts/responses/general.py | 158 ------ .../chat/src/contracts/responses/message.py | 68 --- src/servers/chat/src/contracts/results.py | 172 ++++++ .../chat/src/contracts/results/__init__.py | 0 .../chat/src/contracts/results/channel.py | 78 --- .../chat/src/contracts/results/general.py | 72 --- .../chat/src/contracts/results/message.py | 17 - src/servers/chat/src/enums/irc_error_code.py | 19 - src/servers/chat/src/enums/peer_room.py | 8 - src/servers/chat/src/exceptions/channel.py | 44 -- src/servers/chat/src/handlers/channel.py | 242 --------- src/servers/chat/src/handlers/general.py | 211 -------- src/servers/chat/src/handlers/message.py | 68 --- .../game_status/src/abstractions/contracts.py | 2 +- .../general.py => aggregations/enums.py} | 0 .../general.py => aggregations/exceptions.py} | 0 .../game_status/src/aggregations/gscrypt.py | 2 +- .../game_status/src/applications/client.py | 2 +- .../{handlers => applications}/handlers.py | 12 +- .../{handlers => applications}/switcher.py | 2 +- .../game_status/src/contracts/requests.py | 4 +- .../game_status/tests/handler_tests.py | 6 +- .../src/contracts/general.py | 2 +- .../natneg/src/abstractions/contracts.py | 2 +- .../general.py => aggregations/enums.py} | 0 src/servers/natneg/src/applications/client.py | 2 +- .../{handlers => applications}/handlers.py | 0 .../{handlers => applications}/switcher.py | 4 +- src/servers/natneg/src/contracts/requests.py | 2 +- src/servers/natneg/src/contracts/results.py | 2 +- src/servers/natneg/tests/handler_tests.py | 4 +- .../tests/game_tests.py | 3 + .../src/applications/client.py | 6 +- .../{handlers => applications}/handlers.py | 0 .../{handlers => applications}/switcher.py | 2 +- .../src/contracts/__init__.py | 0 .../tests/game_tests.py | 4 +- .../tests/handler_tests.py | 5 +- .../{exceptions => aggregates}/exceptions.py | 0 .../query_report/src/applications/client.py | 2 +- .../src/v2/abstractions/contracts.py | 2 +- .../v2/{handlers => applications}/handlers.py | 0 .../v2/{handlers => applications}/switcher.py | 2 +- .../query_report/src/v2/contracts/requests.py | 2 +- .../general.py => aggregates/exceptions.py} | 0 src/servers/server_browser/src/v2/__init__.py | 0 .../src/v2/applications/handlers.py | 2 +- .../general.py => aggregations/exceptions.py} | 0 .../src/aggregations/soap_envelop.py | 2 +- .../web_services/src/applications/client.py | 2 +- .../{handlers => applications}/switcher.py | 4 +- .../src/modules/auth/exceptions/general.py | 2 +- .../modules/direct2game/contracts/requests.py | 2 +- .../abstractions/{general.py => generals.py} | 0 .../general.py => applications/handlers.py} | 2 +- .../src/modules/sake/contracts/requests.py | 4 +- .../src/modules/sake/contracts/responses.py | 2 +- .../src/modules/sake/contracts/results.py | 2 +- .../src/modules/sake/exceptions/general.py | 2 +- 95 files changed, 2003 insertions(+), 1569 deletions(-) create mode 100644 common/pg_certs/root/root.crt create mode 100644 common/pg_certs/root/root.csr create mode 100644 common/pg_certs/root/root.key create mode 100644 common/pg_certs/root/root.srl create mode 100644 common/pg_certs/root/server.csr create mode 100644 common/pg_certs/server.crt create mode 100644 common/pg_certs/server.key rename src/servers/chat/src/{enums/general.py => aggregates/enums.py} (71%) rename src/servers/chat/src/{exceptions/general.py => aggregates/exceptions.py} (66%) create mode 100644 src/servers/chat/src/applications/handlers.py rename src/servers/chat/src/{handlers => applications}/switcher.py (93%) delete mode 100644 src/servers/chat/src/contracts/__init__.py rename src/servers/chat/src/contracts/{requests/channel.py => requests.py} (61%) delete mode 100644 src/servers/chat/src/contracts/requests/__init__.py delete mode 100644 src/servers/chat/src/contracts/requests/general.py delete mode 100644 src/servers/chat/src/contracts/requests/message.py create mode 100644 src/servers/chat/src/contracts/responses.py delete mode 100644 src/servers/chat/src/contracts/responses/__init__.py delete mode 100644 src/servers/chat/src/contracts/responses/channel.py delete mode 100644 src/servers/chat/src/contracts/responses/general.py delete mode 100644 src/servers/chat/src/contracts/responses/message.py create mode 100644 src/servers/chat/src/contracts/results.py delete mode 100644 src/servers/chat/src/contracts/results/__init__.py delete mode 100644 src/servers/chat/src/contracts/results/channel.py delete mode 100644 src/servers/chat/src/contracts/results/general.py delete mode 100644 src/servers/chat/src/contracts/results/message.py delete mode 100644 src/servers/chat/src/enums/irc_error_code.py delete mode 100644 src/servers/chat/src/enums/peer_room.py delete mode 100644 src/servers/chat/src/exceptions/channel.py delete mode 100644 src/servers/chat/src/handlers/channel.py delete mode 100644 src/servers/chat/src/handlers/general.py delete mode 100644 src/servers/chat/src/handlers/message.py rename src/servers/game_status/src/{enums/general.py => aggregations/enums.py} (100%) rename src/servers/game_status/src/{exceptions/general.py => aggregations/exceptions.py} (100%) rename src/servers/game_status/src/{handlers => applications}/handlers.py (92%) rename src/servers/game_status/src/{handlers => applications}/switcher.py (91%) rename src/servers/natneg/src/{enums/general.py => aggregations/enums.py} (100%) rename src/servers/natneg/src/{handlers => applications}/handlers.py (100%) rename src/servers/natneg/src/{handlers => applications}/switcher.py (94%) rename src/servers/presence_search_player/src/{handlers => applications}/handlers.py (100%) rename src/servers/presence_search_player/src/{handlers => applications}/switcher.py (92%) delete mode 100644 src/servers/presence_search_player/src/contracts/__init__.py rename src/servers/query_report/src/{exceptions => aggregates}/exceptions.py (100%) rename src/servers/query_report/src/v2/{handlers => applications}/handlers.py (100%) rename src/servers/query_report/src/v2/{handlers => applications}/switcher.py (97%) rename src/servers/server_browser/src/{exceptions/general.py => aggregates/exceptions.py} (100%) delete mode 100644 src/servers/server_browser/src/v2/__init__.py rename src/servers/web_services/src/{exceptions/general.py => aggregations/exceptions.py} (100%) rename src/servers/web_services/src/{handlers => applications}/switcher.py (96%) rename src/servers/web_services/src/modules/sake/abstractions/{general.py => generals.py} (100%) rename src/servers/web_services/src/modules/sake/{handlers/general.py => applications/handlers.py} (93%) diff --git a/common/config.json b/common/config.json index 8aabdb7e5..7e5ba1a2d 100644 --- a/common/config.json +++ b/common/config.json @@ -7,12 +7,12 @@ "min_log_level": "debug" }, "postgresql": { - "server": "127.0.0.1", + "server": "unispy_postgresql", "port": 5432, - "database": "unispy-db", + "database": "unispy", "username": "unispy", "password": "123456", - "ssl_mode": "require", + "ssl_mode": "allow", "trust_server_cert": "false", "ssl_key": "", "ssl_password": "", @@ -26,7 +26,7 @@ "database": null }, "redis": { - "server": "127.0.0.1", + "server": "unispy_redis", "port": 5678, "user": "", "password": "password", diff --git a/common/pg_certs/root/root.crt b/common/pg_certs/root/root.crt new file mode 100644 index 000000000..d8dcf6b84 --- /dev/null +++ b/common/pg_certs/root/root.crt @@ -0,0 +1,77 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: + 36:d7:c5:3a:fe:85:75:9d:1e:ed:e2:c3:70:34:af:ed:22:57:cf:2d + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN = unispy.net + Validity + Not Before: Oct 24 02:51:56 2024 GMT + Not After : Oct 22 02:51:56 2034 GMT + Subject: CN = unispy.net + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c6:25:5d:9a:ba:2f:a4:b7:5f:95:cd:d1:b3:51: + 36:69:69:42:05:2b:f2:32:e0:d1:b1:73:38:dc:df: + 54:49:bd:a2:34:e3:9b:5e:1b:bc:7b:54:87:28:9f: + e6:02:93:4d:62:bf:7b:01:f4:75:ea:6c:cc:50:f9: + 03:70:79:37:61:b6:06:1f:a7:23:e5:60:d5:7a:3b: + 87:5d:9c:f3:e1:9a:5e:4b:49:9c:05:70:4c:9c:b2: + f2:af:22:7d:34:da:53:a6:5e:21:73:1a:87:67:4f: + 2c:c7:65:eb:05:44:b6:97:fe:54:7d:16:61:cb:44: + d2:2f:03:6e:a6:3f:3c:19:92:75:8b:80:b0:61:10: + d5:ab:99:16:0e:cf:5d:66:f8:e5:e3:b2:08:78:03: + af:86:c0:07:3d:b0:d2:33:a2:45:6c:96:18:4a:e7: + e5:bd:ac:23:ea:7c:55:f9:15:9d:62:35:19:02:d1: + b1:c3:0b:f5:2b:f8:06:a7:53:10:42:75:c6:44:57: + ad:b9:3b:37:f1:01:f6:16:81:bb:cc:24:62:8d:da: + 53:d3:8d:9e:42:ec:33:b3:4b:29:88:b1:e4:31:03: + f5:10:51:7e:54:a0:11:92:2e:d2:b6:6b:09:8e:aa: + 17:05:b5:61:0b:93:a0:25:99:39:5a:df:31:4e:60: + f7:6b + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + B9:42:89:52:5A:0B:41:68:D2:8B:B7:DD:D4:EB:B0:31:A8:01:1D:81 + X509v3 Authority Key Identifier: + B9:42:89:52:5A:0B:41:68:D2:8B:B7:DD:D4:EB:B0:31:A8:01:1D:81 + X509v3 Basic Constraints: critical + CA:TRUE + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 8e:73:42:21:f4:3c:67:98:31:b3:39:1a:0e:42:5d:fb:0f:ef: + 7d:d6:d9:86:63:29:4d:d9:ce:f6:54:49:af:4e:42:c7:f7:5e: + f4:28:39:9d:19:fc:8c:fc:61:e1:37:bf:10:29:81:aa:c4:3a: + 4a:a5:bd:92:c5:f6:51:6f:ca:4e:12:a9:ce:cd:77:75:ae:72: + a3:72:f4:19:f4:58:3a:65:6f:eb:55:a1:f2:05:04:a8:5c:6a: + 2b:d0:7e:94:a9:3b:f8:a6:68:f2:67:05:e9:79:64:6a:c2:c2: + 31:00:e3:af:95:34:6c:f9:99:88:0b:62:1a:c8:c3:31:f9:73: + 46:9b:20:6a:14:3f:ef:70:07:72:7e:4e:4e:e6:46:16:d1:f5: + 52:6b:0a:7e:7d:ed:fb:f1:31:3b:be:a9:77:06:a0:eb:e1:8a: + cb:fa:67:ef:df:2c:ef:9e:89:d8:5b:46:40:90:ab:3a:9a:69: + 5b:3d:45:8a:63:a0:6d:3b:46:69:b1:9a:60:0a:83:5a:02:53: + e3:b6:90:ec:79:90:1b:a4:13:e8:5f:b6:e2:45:fb:4b:28:a6: + 01:86:ff:a2:2e:37:cb:a0:8c:3c:8d:1b:ff:a6:0a:c7:06:01: + 8a:20:77:9b:dd:e8:bb:72:e5:d3:9c:e6:bc:92:ca:2c:19:f3: + 31:bd:1b:e7 +-----BEGIN CERTIFICATE----- +MIIDCzCCAfOgAwIBAgIUNtfFOv6FdZ0e7eLDcDSv7SJXzy0wDQYJKoZIhvcNAQEL +BQAwFTETMBEGA1UEAwwKdW5pc3B5Lm5ldDAeFw0yNDEwMjQwMjUxNTZaFw0zNDEw +MjIwMjUxNTZaMBUxEzARBgNVBAMMCnVuaXNweS5uZXQwggEiMA0GCSqGSIb3DQEB +AQUAA4IBDwAwggEKAoIBAQDGJV2aui+kt1+VzdGzUTZpaUIFK/Iy4NGxczjc31RJ +vaI045teG7x7VIcon+YCk01iv3sB9HXqbMxQ+QNweTdhtgYfpyPlYNV6O4ddnPPh +ml5LSZwFcEycsvKvIn002lOmXiFzGodnTyzHZesFRLaX/lR9FmHLRNIvA26mPzwZ +knWLgLBhENWrmRYOz11m+OXjsgh4A6+GwAc9sNIzokVslhhK5+W9rCPqfFX5FZ1i +NRkC0bHDC/Ur+AanUxBCdcZEV625OzfxAfYWgbvMJGKN2lPTjZ5C7DOzSymIseQx +A/UQUX5UoBGSLtK2awmOqhcFtWELk6AlmTla3zFOYPdrAgMBAAGjUzBRMB0GA1Ud +DgQWBBS5QolSWgtBaNKLt93U67AxqAEdgTAfBgNVHSMEGDAWgBS5QolSWgtBaNKL +t93U67AxqAEdgTAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQCO +c0Ih9DxnmDGzORoOQl37D+991tmGYylN2c72VEmvTkLH9170KDmdGfyM/GHhN78Q +KYGqxDpKpb2SxfZRb8pOEqnOzXd1rnKjcvQZ9Fg6ZW/rVaHyBQSoXGor0H6UqTv4 +pmjyZwXpeWRqwsIxAOOvlTRs+ZmIC2IayMMx+XNGmyBqFD/vcAdyfk5O5kYW0fVS +awp+fe378TE7vql3BqDr4YrL+mfv3yzvnonYW0ZAkKs6mmlbPUWKY6BtO0ZpsZpg +CoNaAlPjtpDseZAbpBPoX7biRftLKKYBhv+iLjfLoIw8jRv/pgrHBgGKIHeb3ei7 +cuXTnOa8ksosGfMxvRvn +-----END CERTIFICATE----- diff --git a/common/pg_certs/root/root.csr b/common/pg_certs/root/root.csr new file mode 100644 index 000000000..8e246683d --- /dev/null +++ b/common/pg_certs/root/root.csr @@ -0,0 +1,62 @@ +Certificate Request: + Data: + Version: 1 (0x0) + Subject: CN = unispy.net + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:c6:25:5d:9a:ba:2f:a4:b7:5f:95:cd:d1:b3:51: + 36:69:69:42:05:2b:f2:32:e0:d1:b1:73:38:dc:df: + 54:49:bd:a2:34:e3:9b:5e:1b:bc:7b:54:87:28:9f: + e6:02:93:4d:62:bf:7b:01:f4:75:ea:6c:cc:50:f9: + 03:70:79:37:61:b6:06:1f:a7:23:e5:60:d5:7a:3b: + 87:5d:9c:f3:e1:9a:5e:4b:49:9c:05:70:4c:9c:b2: + f2:af:22:7d:34:da:53:a6:5e:21:73:1a:87:67:4f: + 2c:c7:65:eb:05:44:b6:97:fe:54:7d:16:61:cb:44: + d2:2f:03:6e:a6:3f:3c:19:92:75:8b:80:b0:61:10: + d5:ab:99:16:0e:cf:5d:66:f8:e5:e3:b2:08:78:03: + af:86:c0:07:3d:b0:d2:33:a2:45:6c:96:18:4a:e7: + e5:bd:ac:23:ea:7c:55:f9:15:9d:62:35:19:02:d1: + b1:c3:0b:f5:2b:f8:06:a7:53:10:42:75:c6:44:57: + ad:b9:3b:37:f1:01:f6:16:81:bb:cc:24:62:8d:da: + 53:d3:8d:9e:42:ec:33:b3:4b:29:88:b1:e4:31:03: + f5:10:51:7e:54:a0:11:92:2e:d2:b6:6b:09:8e:aa: + 17:05:b5:61:0b:93:a0:25:99:39:5a:df:31:4e:60: + f7:6b + Exponent: 65537 (0x10001) + Attributes: + (none) + Requested Extensions: + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 1c:c4:9d:64:4e:ad:a8:a5:cd:c2:cf:1f:59:ec:3e:37:3c:0b: + 56:00:4e:1d:d1:71:cd:ba:76:cc:88:28:5c:15:2c:01:71:ca: + d6:b2:93:38:9e:67:ff:da:a9:cd:86:b1:be:0c:02:f9:1b:d3: + 7a:19:82:37:f6:32:1b:7b:55:1f:67:91:f1:04:d0:13:41:b5: + c8:62:c3:10:1b:45:ee:6f:3b:bf:58:5d:c0:f7:a6:c7:c9:01: + e5:8e:6b:e6:d3:40:08:51:ee:c3:07:42:bb:c6:cf:5c:74:23: + 3b:f9:e1:89:2f:a6:bb:ab:74:b8:e2:fe:76:5f:bf:b8:0d:58: + 7c:c2:76:2e:97:60:25:d3:c9:a9:1e:be:fc:2c:e9:6d:36:4c: + b3:2f:15:c1:2d:f0:6b:f4:aa:49:ea:3d:ef:0e:50:74:5f:e2: + a0:bf:a6:a0:a0:ad:0a:75:0b:c3:9b:4c:a8:d8:a2:d9:d7:ef: + 2e:2b:27:37:80:a6:da:4b:1c:8e:a4:fa:79:81:66:cd:29:58: + dc:e5:3a:67:b6:d5:3c:e0:f8:0b:10:4c:71:c0:93:95:9a:f2: + 60:4e:8d:e7:2d:8b:2e:81:a8:96:9c:fd:a9:46:44:87:eb:52: + 68:3f:d7:b8:74:6a:57:50:c2:57:0e:2a:48:78:6a:2e:6b:da: + 4e:40:db:34 +-----BEGIN CERTIFICATE REQUEST----- +MIICWjCCAUICAQAwFTETMBEGA1UEAwwKdW5pc3B5Lm5ldDCCASIwDQYJKoZIhvcN +AQEBBQADggEPADCCAQoCggEBAMYlXZq6L6S3X5XN0bNRNmlpQgUr8jLg0bFzONzf +VEm9ojTjm14bvHtUhyif5gKTTWK/ewH0depszFD5A3B5N2G2Bh+nI+Vg1Xo7h12c +8+GaXktJnAVwTJyy8q8ifTTaU6ZeIXMah2dPLMdl6wVEtpf+VH0WYctE0i8DbqY/ +PBmSdYuAsGEQ1auZFg7PXWb45eOyCHgDr4bABz2w0jOiRWyWGErn5b2sI+p8VfkV +nWI1GQLRscML9Sv4BqdTEEJ1xkRXrbk7N/EB9haBu8wkYo3aU9ONnkLsM7NLKYix +5DED9RBRflSgEZIu0rZrCY6qFwW1YQuToCWZOVrfMU5g92sCAwEAAaAAMA0GCSqG +SIb3DQEBCwUAA4IBAQAcxJ1kTq2opc3Czx9Z7D43PAtWAE4d0XHNunbMiChcFSwB +ccrWspM4nmf/2qnNhrG+DAL5G9N6GYI39jIbe1UfZ5HxBNATQbXIYsMQG0Xubzu/ +WF3A96bHyQHljmvm00AIUe7DB0K7xs9cdCM7+eGJL6a7q3S44v52X7+4DVh8wnYu +l2Al08mpHr78LOltNkyzLxXBLfBr9KpJ6j3vDlB0X+Kgv6agoK0KdQvDm0yo2KLZ +1+8uKyc3gKbaSxyOpPp5gWbNKVjc5TpnttU84PgLEExxwJOVmvJgTo3nLYsugaiW +nP2pRkSH61JoP9e4dGpXUMJXDipIeGoua9pOQNs0 +-----END CERTIFICATE REQUEST----- diff --git a/common/pg_certs/root/root.key b/common/pg_certs/root/root.key new file mode 100644 index 000000000..fd4b499e6 --- /dev/null +++ b/common/pg_certs/root/root.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQDGJV2aui+kt1+V +zdGzUTZpaUIFK/Iy4NGxczjc31RJvaI045teG7x7VIcon+YCk01iv3sB9HXqbMxQ ++QNweTdhtgYfpyPlYNV6O4ddnPPhml5LSZwFcEycsvKvIn002lOmXiFzGodnTyzH +ZesFRLaX/lR9FmHLRNIvA26mPzwZknWLgLBhENWrmRYOz11m+OXjsgh4A6+GwAc9 +sNIzokVslhhK5+W9rCPqfFX5FZ1iNRkC0bHDC/Ur+AanUxBCdcZEV625OzfxAfYW +gbvMJGKN2lPTjZ5C7DOzSymIseQxA/UQUX5UoBGSLtK2awmOqhcFtWELk6AlmTla +3zFOYPdrAgMBAAECggEAH2LZ0eF+HHxDcso4Vkwd71KR95m/cpmz/YS/1BS4GDom +kHQhLyX7lBmOkzvIxk2o62RjSqr7Zpe3QXhAODerMxoPHEJwaCwPhJ4bUhPDec/m +8cwGH8JrEEM0N9Ohu5Z9u5Obfs0L96xN7oPRV3NL7QWHQo0iDn2nQXUsuL65eV83 +Tgv0eK8GlVoESGgnE9/w5U2gHMEvrKD3Zxax5i8HknSSLKS235iNnhlX1anNC34H +PQfWxCqapzRv+FB7t+AtlZx8RU/ky4Q6HkzeuXAZQ1qEk7+tblnt5B3DpHTf3l7w +EDQSvCKeKDj6ryiu+/TWwfzJFGuFVarkv8yllUFHUQKBgQDj+Qd0zytpLHkGzTpF +53iYpLyOclNd3FXTuV1L7XQB1mwKmXvL807j9tonF4repBKiLWR64SzY81sRcNAr +8SZF/C7AFtTuDJYjB81LLdUcVC7Hmen0hjQxa0OiVGck9yOlfZCcd3Ww9NFp1ycL +MnbIRWI8J8T9Qh843hMxLPpdCQKBgQDegZlze+lWlrwxn1SkYeh/bIS+WM/MwB/4 ++L7rx40W49EDhxVCM4fO91VkZN4nZPXvIDhF/8A3FMEuAxVGgbJnG9fln+3xbNeZ +fKqAS3rIXzVtDDuwsNLVX6NQZ0CagCnJyXJqDvI+N6Mgg9cdPnCL6dbthanZVYzV +Cxo9F+9B0wKBgB95SiY+U+f5U9w0iU8NXgD0/XNNJWVX/iF0/gR1jAaU6+WquwS1 +WrbuZb/v6CRE0q3BRpYQcHijYHdP8+2dJYOUBYBPpqYW5sN/WECA22NF3A+CmGJC +BQKtpHDM5lCcLjey1jxD4ePEaQULx0Asf2m26pETjIbKkjTvtAaeBxLBAoGBAJmS +V4gmgPFbjj6tmqzuSpsQGjqKb7oA7NBZVuTDUTT4Pj2yEVEk4dpOSWjGWbJU8418 +7noZv+AEeiS4yglk4O5bgFKjZIYaOmBcdA2iivcbB3PhWp1kHdBZdw26hhNc2/rD +CC39bOLWYcfCV0l+3A0lc0ty0r0HV/F+/TgneeIzAoGBANpri83xFrYQzS6fjm9U +SB/4mTzdRRVqh44UEYKciSnMhD8I8XYKV7vV3Ix9L+yYGxTOUDfskDLrQ1t66oqV +o7mmG5MFkSoTdMB1v6hZXVYhSQ5J5Q9QNdSppU8NLxXiaFScJdhZpF2wcINkbxPm +j9TqJ4zJ7H+FP0RP9Kn+zK23 +-----END PRIVATE KEY----- diff --git a/common/pg_certs/root/root.srl b/common/pg_certs/root/root.srl new file mode 100644 index 000000000..12500797a --- /dev/null +++ b/common/pg_certs/root/root.srl @@ -0,0 +1 @@ +45BD6FD72E54134CD40A3B242DC3A69B964E17BF diff --git a/common/pg_certs/root/server.csr b/common/pg_certs/root/server.csr new file mode 100644 index 000000000..bf42c3a2b --- /dev/null +++ b/common/pg_certs/root/server.csr @@ -0,0 +1,62 @@ +Certificate Request: + Data: + Version: 1 (0x0) + Subject: CN = dbhost.yourdomain.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:9c:da:6e:a6:3d:77:67:6d:b8:88:d5:2e:d9:2d: + 21:ca:9b:e4:3d:f9:a7:ba:c6:60:12:85:0c:66:64: + 2b:ac:10:a4:2a:69:a7:78:0e:0f:d8:c7:b8:83:ed: + 5d:2e:9b:30:0f:27:1e:ca:b1:0c:34:4f:e3:25:57: + 7a:5c:38:ee:ba:5a:1c:f7:d3:c7:92:df:ef:9f:26: + 2c:01:50:bf:63:df:7d:e6:0e:d7:08:49:33:de:aa: + 2d:42:3b:8f:81:c2:87:14:6c:5a:cf:c3:b6:06:59: + 3a:f6:f0:ec:ff:2b:ec:7c:e1:9b:c3:d6:5d:50:34: + 55:37:c6:77:be:88:15:0a:a9:43:0d:8c:b4:b9:9f: + 8f:8a:03:70:b7:a6:82:cb:cd:ba:ae:cd:11:b7:f2: + e1:56:16:8c:67:f9:73:c1:0d:b3:50:1f:76:3a:f9: + 91:50:bb:4c:ed:67:56:d6:c0:3f:1f:24:a3:98:20: + 55:14:6b:1c:89:f7:fe:97:04:ca:fb:50:54:1f:47: + 5a:31:4b:2d:48:86:ab:cc:c3:ca:38:b0:1f:72:8a: + 60:40:bc:e8:31:39:f4:a3:20:c4:4c:93:7c:ef:10: + 16:37:4d:9e:10:60:28:f2:89:55:bf:3d:04:cf:8d: + 58:45:3f:d5:4a:06:be:fc:5a:74:db:8d:8f:ca:98: + 3b:c1 + Exponent: 65537 (0x10001) + Attributes: + (none) + Requested Extensions: + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 7d:54:f9:9a:3c:79:64:e3:5b:9a:93:3a:36:d8:72:4d:fb:cd: + d2:a6:19:e6:d9:e0:d0:de:39:b1:a9:eb:f4:a0:eb:88:b7:0a: + 5b:c8:09:25:4a:6b:26:05:8f:3f:aa:da:75:a8:02:b5:09:ec: + e8:b9:ec:2e:55:b8:61:12:ae:5b:e9:41:68:08:cd:22:e6:e2: + d9:e9:cc:6e:df:c3:5e:2f:20:7a:39:b1:24:3a:f5:9a:9d:d1: + 39:2a:f4:c1:16:e9:64:84:1f:cd:4a:ac:91:82:e8:69:00:0b: + 2f:49:2d:15:4c:85:0f:a3:68:d1:98:aa:90:23:db:84:7a:98: + f5:fb:03:c1:d1:ae:ef:e3:67:54:f0:91:ce:29:ac:ae:dd:3f: + ac:74:59:0e:69:69:97:08:24:1b:27:77:23:b8:97:28:53:69: + ab:50:cf:ae:00:06:1e:6b:8f:62:ce:bb:9d:49:0f:c2:65:0a: + a6:5c:05:ac:b4:de:7c:72:55:9e:aa:01:0c:ff:14:46:c0:d6: + 44:eb:43:b8:25:d8:a5:e7:8b:49:c3:dd:d0:66:58:be:92:06: + 9a:dd:44:6f:c9:01:b9:5d:1e:09:fa:ad:e7:ab:8a:67:d5:21: + 00:d7:a8:c7:f1:1b:a2:f5:6a:64:c7:64:23:f2:df:f8:e0:9e: + ba:46:40:0a +-----BEGIN CERTIFICATE REQUEST----- +MIICZTCCAU0CAQAwIDEeMBwGA1UEAwwVZGJob3N0LnlvdXJkb21haW4uY29tMIIB +IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAnNpupj13Z224iNUu2S0hypvk +PfmnusZgEoUMZmQrrBCkKmmneA4P2Me4g+1dLpswDyceyrEMNE/jJVd6XDjuuloc +99PHkt/vnyYsAVC/Y9995g7XCEkz3qotQjuPgcKHFGxaz8O2Blk69vDs/yvsfOGb +w9ZdUDRVN8Z3vogVCqlDDYy0uZ+PigNwt6aCy826rs0Rt/LhVhaMZ/lzwQ2zUB92 +OvmRULtM7WdW1sA/HySjmCBVFGsciff+lwTK+1BUH0daMUstSIarzMPKOLAfcopg +QLzoMTn0oyDETJN87xAWN02eEGAo8olVvz0Ez41YRT/VSga+/Fp0242Pypg7wQID +AQABoAAwDQYJKoZIhvcNAQELBQADggEBAH1U+Zo8eWTjW5qTOjbYck37zdKmGebZ +4NDeObGp6/Sg64i3ClvICSVKayYFjz+q2nWoArUJ7Oi57C5VuGESrlvpQWgIzSLm +4tnpzG7fw14vIHo5sSQ69Zqd0Tkq9MEW6WSEH81KrJGC6GkACy9JLRVMhQ+jaNGY +qpAj24R6mPX7A8HRru/jZ1Twkc4prK7dP6x0WQ5paZcIJBsndyO4lyhTaatQz64A +Bh5rj2LOu51JD8JlCqZcBay03nxyVZ6qAQz/FEbA1kTrQ7gl2KXni0nD3dBmWL6S +BprdRG/JAbldHgn6reerimfVIQDXqMfxG6L1amTHZCPy3/jgnrpGQAo= +-----END CERTIFICATE REQUEST----- diff --git a/common/pg_certs/server.crt b/common/pg_certs/server.crt new file mode 100644 index 000000000..47d047a90 --- /dev/null +++ b/common/pg_certs/server.crt @@ -0,0 +1,68 @@ +Certificate: + Data: + Version: 1 (0x0) + Serial Number: + 45:bd:6f:d7:2e:54:13:4c:d4:0a:3b:24:2d:c3:a6:9b:96:4e:17:bf + Signature Algorithm: sha256WithRSAEncryption + Issuer: CN = unispy.net + Validity + Not Before: Oct 24 02:52:10 2024 GMT + Not After : Oct 24 02:52:10 2025 GMT + Subject: CN = dbhost.yourdomain.com + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:9c:da:6e:a6:3d:77:67:6d:b8:88:d5:2e:d9:2d: + 21:ca:9b:e4:3d:f9:a7:ba:c6:60:12:85:0c:66:64: + 2b:ac:10:a4:2a:69:a7:78:0e:0f:d8:c7:b8:83:ed: + 5d:2e:9b:30:0f:27:1e:ca:b1:0c:34:4f:e3:25:57: + 7a:5c:38:ee:ba:5a:1c:f7:d3:c7:92:df:ef:9f:26: + 2c:01:50:bf:63:df:7d:e6:0e:d7:08:49:33:de:aa: + 2d:42:3b:8f:81:c2:87:14:6c:5a:cf:c3:b6:06:59: + 3a:f6:f0:ec:ff:2b:ec:7c:e1:9b:c3:d6:5d:50:34: + 55:37:c6:77:be:88:15:0a:a9:43:0d:8c:b4:b9:9f: + 8f:8a:03:70:b7:a6:82:cb:cd:ba:ae:cd:11:b7:f2: + e1:56:16:8c:67:f9:73:c1:0d:b3:50:1f:76:3a:f9: + 91:50:bb:4c:ed:67:56:d6:c0:3f:1f:24:a3:98:20: + 55:14:6b:1c:89:f7:fe:97:04:ca:fb:50:54:1f:47: + 5a:31:4b:2d:48:86:ab:cc:c3:ca:38:b0:1f:72:8a: + 60:40:bc:e8:31:39:f4:a3:20:c4:4c:93:7c:ef:10: + 16:37:4d:9e:10:60:28:f2:89:55:bf:3d:04:cf:8d: + 58:45:3f:d5:4a:06:be:fc:5a:74:db:8d:8f:ca:98: + 3b:c1 + Exponent: 65537 (0x10001) + Signature Algorithm: sha256WithRSAEncryption + Signature Value: + 4c:40:14:e7:6d:0e:2a:0e:9f:3e:a9:9a:ad:b1:3e:8a:dd:7a: + a4:fb:4a:11:0d:3d:8f:9f:a8:be:eb:41:1d:64:2d:d2:77:11: + 3c:1c:9f:f6:39:99:6a:08:5c:62:7e:9e:40:92:58:d9:0a:14: + 1b:fe:16:dc:48:93:2c:ab:3b:49:c6:5a:f5:01:16:ad:46:92: + a8:63:4a:21:b9:23:f5:f2:ad:4f:41:09:8c:6b:a5:4d:c9:ab: + 8e:8f:4d:dd:b4:fd:f0:5e:6c:a6:d4:0b:ad:5b:86:58:e2:48: + 72:65:ab:0b:a2:de:f4:f5:da:a5:66:16:fb:71:f0:41:90:e6: + 90:4a:73:c0:31:f2:1f:90:ab:7d:b0:e6:f0:15:76:a0:69:17: + 4a:04:cc:3d:8c:a6:dd:11:b8:e0:09:c5:5a:f8:39:d6:bf:d1: + bb:39:e5:43:04:93:fb:ed:c6:4d:16:22:eb:69:ff:ea:e5:66: + 42:9c:ce:a1:b2:84:37:d8:55:c6:ca:51:93:59:06:b3:11:23: + 5d:f1:66:5d:7e:9b:2b:f2:4d:b3:2f:37:66:5c:4a:24:14:ec: + 29:78:97:f9:7e:0f:38:20:23:f7:e2:3c:49:e4:f7:76:a8:ea: + 5b:73:03:32:f4:cc:99:dd:3f:74:0e:13:05:11:60:71:31:5a: + ef:88:22:46 +-----BEGIN CERTIFICATE----- +MIICvDCCAaQCFEW9b9cuVBNM1Ao7JC3DppuWThe/MA0GCSqGSIb3DQEBCwUAMBUx +EzARBgNVBAMMCnVuaXNweS5uZXQwHhcNMjQxMDI0MDI1MjEwWhcNMjUxMDI0MDI1 +MjEwWjAgMR4wHAYDVQQDDBVkYmhvc3QueW91cmRvbWFpbi5jb20wggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCc2m6mPXdnbbiI1S7ZLSHKm+Q9+ae6xmAS +hQxmZCusEKQqaad4Dg/Yx7iD7V0umzAPJx7KsQw0T+MlV3pcOO66Whz308eS3++f +JiwBUL9j333mDtcISTPeqi1CO4+BwocUbFrPw7YGWTr28Oz/K+x84ZvD1l1QNFU3 +xne+iBUKqUMNjLS5n4+KA3C3poLLzbquzRG38uFWFoxn+XPBDbNQH3Y6+ZFQu0zt +Z1bWwD8fJKOYIFUUaxyJ9/6XBMr7UFQfR1oxSy1IhqvMw8o4sB9yimBAvOgxOfSj +IMRMk3zvEBY3TZ4QYCjyiVW/PQTPjVhFP9VKBr78WnTbjY/KmDvBAgMBAAEwDQYJ +KoZIhvcNAQELBQADggEBAExAFOdtDioOnz6pmq2xPordeqT7ShENPY+fqL7rQR1k +LdJ3ETwcn/Y5mWoIXGJ+nkCSWNkKFBv+FtxIkyyrO0nGWvUBFq1GkqhjSiG5I/Xy +rU9BCYxrpU3Jq46PTd20/fBebKbUC61bhljiSHJlqwui3vT12qVmFvtx8EGQ5pBK +c8Ax8h+Qq32w5vAVdqBpF0oEzD2Mpt0RuOAJxVr4Oda/0bs55UMEk/vtxk0WIutp +/+rlZkKczqGyhDfYVcbKUZNZBrMRI13xZl1+myvyTbMvN2ZcSiQU7Cl4l/l+Dzgg +I/fiPEnk93ao6ltzAzL0zJndP3QOEwURYHExWu+IIkY= +-----END CERTIFICATE----- diff --git a/common/pg_certs/server.key b/common/pg_certs/server.key new file mode 100644 index 000000000..0960f0e1e --- /dev/null +++ b/common/pg_certs/server.key @@ -0,0 +1,28 @@ +-----BEGIN PRIVATE KEY----- +MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQCc2m6mPXdnbbiI +1S7ZLSHKm+Q9+ae6xmAShQxmZCusEKQqaad4Dg/Yx7iD7V0umzAPJx7KsQw0T+Ml +V3pcOO66Whz308eS3++fJiwBUL9j333mDtcISTPeqi1CO4+BwocUbFrPw7YGWTr2 +8Oz/K+x84ZvD1l1QNFU3xne+iBUKqUMNjLS5n4+KA3C3poLLzbquzRG38uFWFoxn ++XPBDbNQH3Y6+ZFQu0ztZ1bWwD8fJKOYIFUUaxyJ9/6XBMr7UFQfR1oxSy1IhqvM +w8o4sB9yimBAvOgxOfSjIMRMk3zvEBY3TZ4QYCjyiVW/PQTPjVhFP9VKBr78WnTb +jY/KmDvBAgMBAAECggEADdazQ7lBVUefy/nEEP7rf+7WWbXyu5a4P/YLYKbGmB8d +Ps00zHGwKfv//mrFKYEhYbrdu3wYo65nL+KajfOrc1dTPjXKAj4969SQNhr/0dHU +b6VgSS+9MvBgfxsWZ5hINu/y6Kj/oKqDemlJ/Z7sVd3JUoNRlwuQ97Nr2exzb83Z +ByArsrrYGhApckgJ5lO5KUoEWX0ciVb3t4rQftNYSGrJUl+AKkpNrsCzhzprfJ9Y +q79/lJZ9ZBspYemF9goRtFcQ0ZuYgg05fEKNIbjm5SrNhmT0GDCFbQXcus4+aNT/ +YvTtwE9O4NJ8pfEsm6BFHpmfubu8xCbX33nYSFhE+QKBgQDWYjou0HL+7KJoqzhw +23UZCvIoH7cBqtjd5+PcpiSAEuLtPT9UJ67ZoEN4zzC8gA3KenoOVkzY9teT/CYZ +3wGzJFLLYLroQ+4pFvDNreNeT72RzlF7MYu51f7n0EEv7OrKZ/KdnxtmBqj0HH6p +ZbSkdknKz+wN/MgEMoYmpuepyQKBgQC7TTxtSaEllHwcPudGNIX4CRHNEKreU4RA +o3ggcvlmV6YKnSlwFzaF+Lgzox2J2OyswoDSgIHfBfpCCQjE6NY6/48XLuTW8JWh +kIyOaJuBS3UQDYfEdNpnpjgMU+ZwW62aC3opZCe8nK/JOWw1lCrJirZh0INM+1xy +RfG+WR7+OQKBgC+rxf5U8c1H91FJCZLm6eH4siJD8yDWycSGZP/Snfkwue9BGEzx +SgswfPBnOhIgc3CbzXpUrF/ue793aU2Fbk5UfGinCMjPGi1e4YsK6K03FBNRCoNX +YBehwz3u7B/pEciSVru//oqwXm9xyqSGbiXH+96yX2440I1GYthDcu4pAoGARrPy +IoMPzKLPcs4f+XVsOOQbjyBCj+hQ3SGYAA/Gq2ZcrFcFRGXO1CW+SufBB78WIGTP +wiZ2X9zeyjykzcfizqSXvDWcdrKcmT96f2tngBge2W9yF6vQoh2xvJ2TOEizMJoy +hBtlkKJJDRmbCmKjAC9Xh3bxiYa9L/nNNoBn3akCgYBrcFhAfzxR8uYOCD7p1blK +xz/Q72K8wTA6IHvpwFh89oby0hRTiuulOtyHs08dC2OU2sEzJvYEyK665Q8k6ttC +CVgt1nC9s/UTt7piZ1cURQhnmbw1T5wzBXyTArRXoXj7CphDY/9QxaFdSFp5pFUP +2BMWzhRZm/Q9S7oKz9TOjw== +-----END PRIVATE KEY----- diff --git a/docker-compose-unispy-env.yml b/docker-compose-unispy-env.yml index 0834f26d4..e3debd06d 100644 --- a/docker-compose-unispy-env.yml +++ b/docker-compose-unispy-env.yml @@ -1,5 +1,3 @@ -version: '3' - services: redis: image: redis @@ -8,15 +6,8 @@ services: ports: - "6379:6379" command: redis-server --requirepass 123456 - - # mongodb: - # image: mongodb/mongodb-community-server:latest - # restart: always - # ports: - # - "27017:27017" - # environment: - # MONGO_INITDB_ROOT_USERNAME: unispy - # MONGO_INITDB_ROOT_PASSWORD: 123456 + networks: + - unispy postgresql: image: postgres:14 @@ -30,3 +21,10 @@ services: POSTGRES_DB: unispy volumes: - ./common/UniSpy_pg.sql:/docker-entrypoint-initdb.d/init.sql + networks: + - unispy + +networks: + unispy: + name: unispy + driver: bridge \ No newline at end of file diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json index c743262ff..bc27b859e 100644 --- a/src/.vscode/launch.json +++ b/src/.vscode/launch.json @@ -56,6 +56,114 @@ }, "justMyCode": true }, + { + "name": "chat", + "type": "debugpy", + "request": "launch", + "program": "servers/chat/src/applications/server_launcher.py", + "console": "integratedTerminal", + "env": { + "PYTHONPATH": "${workspaceFolder}", + }, + "justMyCode": true + }, + { + "name": "gs", + "type": "debugpy", + "request": "launch", + "program": "servers/game_status/src/applications/server_launcher.py", + "console": "integratedTerminal", + "env": { + "PYTHONPATH": "${workspaceFolder}", + }, + "justMyCode": true + }, + { + "name": "natneg", + "type": "debugpy", + "request": "launch", + "program": "servers/natneg/src/applications/server_launcher.py", + "console": "integratedTerminal", + "env": { + "PYTHONPATH": "${workspaceFolder}", + }, + "justMyCode": true + }, + { + "name": "qr", + "type": "debugpy", + "request": "launch", + "program": "servers/query_report/src/applications/server_launcher.py", + "console": "integratedTerminal", + "env": { + "PYTHONPATH": "${workspaceFolder}", + }, + "justMyCode": true + }, + { + "name": "sb", + "type": "debugpy", + "request": "launch", + "program": "servers/server_browser/src/v2/applications/server_launcher.py", + "console": "integratedTerminal", + "env": { + "PYTHONPATH": "${workspaceFolder}", + }, + "justMyCode": true + }, + { + "name": "web", + "type": "debugpy", + "request": "launch", + "program": "servers/web_services/src/applications/server_launcher.py", + "console": "integratedTerminal", + "env": { + "PYTHONPATH": "${workspaceFolder}", + }, + "justMyCode": true + }, ], - "compounds": [] + // "compounds": [ + // { + // "name": "pcm", + // "configurations": [ + // "backend", + // "_pcm" + // ] + // }, + // { + // "name": "psp", + // "configurations": [ + // "backend", + // "_psp" + // ] + // }, + // { + // "name": "qr", + // "configurations": [ + // "backend", + // "_qr" + // ] + // }, + // { + // "name": "sb", + // "configurations": [ + // "backend", + // "_sb" + // ] + // }, + // { + // "name": "all", + // "configurations": [ + // "_psp", + // "_pcm", + // "backend", + // "_natneg", + // "_gs", + // "_sb", + // "_qr", + // "_web" + // ] + // }, + // ] } \ No newline at end of file diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 2cacbacc9..d50e2d1b1 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -21,7 +21,7 @@ from sqlalchemy.ext.declarative import DeclarativeMeta from sqlalchemy.orm import sessionmaker, declarative_base -from servers.natneg.src.enums.general import NatClientIndex, NatPortType +from servers.natneg.src.aggregations.enums import NatClientIndex, NatPortType from servers.query_report.src.v2.aggregates.enums import GameServerStatus Base: DeclarativeMeta = declarative_base() diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index 4cfdb0991..906c3a746 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING, cast from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatUserCaches, Users, Profiles, SubProfiles from servers.chat.src.aggregates.channel import Channel -from servers.chat.src.exceptions.general import ChatException +from servers.chat.src.aggregates.exceptions import ChatException def nick_and_email_login(nick_name: str, email: str, password_hash: str) -> tuple[int, int, bool, bool]: diff --git a/src/backends/protocols/gamespy/chat/requests.py b/src/backends/protocols/gamespy/chat/requests.py index 1e27bee1f..0434dbb1c 100644 --- a/src/backends/protocols/gamespy/chat/requests.py +++ b/src/backends/protocols/gamespy/chat/requests.py @@ -1,5 +1,5 @@ import backends.library.abstractions.contracts as lib -from servers.chat.src.enums.general import LoginRequestType, WhoRequestType +from servers.chat.src.aggregates.enums import LoginRequestType, WhoRequestType class RequestBase(lib.RequestBase): diff --git a/src/backends/protocols/gamespy/game_status/data.py b/src/backends/protocols/gamespy/game_status/data.py index 5ddbe3fa1..d1a8d8a8c 100644 --- a/src/backends/protocols/gamespy/game_status/data.py +++ b/src/backends/protocols/gamespy/game_status/data.py @@ -1,7 +1,7 @@ from typing import TYPE_CHECKING, cast from backends.library.database.pg_orm import PG_SESSION, PStorage, Profiles, SubProfiles -from servers.game_status.src.enums.general import PersistStorageType -from servers.game_status.src.exceptions.general import GSException +from servers.game_status.src.aggregations.enums import PersistStorageType +from servers.game_status.src.aggregations.exceptions import GSException def create_new_game_data(): diff --git a/src/backends/protocols/gamespy/game_status/requests.py b/src/backends/protocols/gamespy/game_status/requests.py index 2d1b6ab20..712dc3313 100644 --- a/src/backends/protocols/gamespy/game_status/requests.py +++ b/src/backends/protocols/gamespy/game_status/requests.py @@ -1,6 +1,6 @@ from typing import Optional import backends.library.abstractions.contracts as lib -from servers.game_status.src.enums.general import AuthMethod, PersistStorageType +from servers.game_status.src.aggregations.enums import AuthMethod, PersistStorageType class RequestBase(lib.RequestBase): diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py index 4b4dce424..632553a8d 100644 --- a/src/backends/protocols/gamespy/natneg/data.py +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -1,7 +1,7 @@ from datetime import datetime, timedelta from uuid import UUID from backends.library.database.pg_orm import PG_SESSION, InitPacketCaches, NatFailCaches -from servers.natneg.src.enums.general import NatClientIndex +from servers.natneg.src.aggregations.enums import NatClientIndex def store_init_packet(info: InitPacketCaches) -> None: diff --git a/src/backends/protocols/gamespy/natneg/requests.py b/src/backends/protocols/gamespy/natneg/requests.py index 4e1c1ef60..180d7a228 100644 --- a/src/backends/protocols/gamespy/natneg/requests.py +++ b/src/backends/protocols/gamespy/natneg/requests.py @@ -1,4 +1,4 @@ -from servers.natneg.src.enums.general import ( +from servers.natneg.src.aggregations.enums import ( NatClientIndex, NatPortMappingScheme, NatPortType, diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index e01b37805..d9825083d 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -1,4 +1,3 @@ -from dataclasses import dataclass import json from typing import Optional from backends.urls import CHAT diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index 3813df52e..ec0c9fc80 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -19,7 +19,7 @@ @app.post("/") -def home(request: ServerConfig): +async def home(request: ServerConfig) -> dict: # todo add the server config to our database return {"status": "online"} diff --git a/src/servers/chat/src/abstractions/channel.py b/src/servers/chat/src/abstractions/channel.py index 523b205ab..4cce9d326 100644 --- a/src/servers/chat/src/abstractions/channel.py +++ b/src/servers/chat/src/abstractions/channel.py @@ -5,8 +5,7 @@ from servers.chat.src.abstractions.handler import PostLoginHandlerBase from servers.chat.src.aggregates.managers import ChannelManager -from servers.chat.src.exceptions.channel import NoSuchChannelException -from servers.chat.src.exceptions.general import ChatException, NoSuchNickException +from servers.chat.src.aggregates.exceptions import ChatException, NoSuchNickException, NoSuchChannelException if TYPE_CHECKING: from servers.chat.src.aggregates.channel import Channel diff --git a/src/servers/chat/src/abstractions/handler.py b/src/servers/chat/src/abstractions/handler.py index 1a5540e38..61ccca7cf 100644 --- a/src/servers/chat/src/abstractions/handler.py +++ b/src/servers/chat/src/abstractions/handler.py @@ -1,7 +1,7 @@ from library.src.abstractions.client import ClientBase from servers.chat.src.abstractions.contract import RequestBase, ResultBase from servers.chat.src.applications.client import Client -from servers.chat.src.exceptions.general import IRCException +from servers.chat.src.aggregates.exceptions import IRCException import library.src.abstractions.handler from typing import cast diff --git a/src/servers/chat/src/abstractions/message.py b/src/servers/chat/src/abstractions/message.py index d8fe878cb..4ca81e6f9 100644 --- a/src/servers/chat/src/abstractions/message.py +++ b/src/servers/chat/src/abstractions/message.py @@ -2,7 +2,7 @@ from servers.chat.src.abstractions.channel import ChannelHandlerBase, ChannelRequestBase from servers.chat.src.abstractions.contract import ResultBase from servers.chat.src.aggregates.channel_user import ChannelUser -from servers.chat.src.enums.general import MessageType +from servers.chat.src.aggregates.enums import MessageType class MessageRequestBase(ChannelRequestBase): diff --git a/src/servers/chat/src/aggregates/channel.py b/src/servers/chat/src/aggregates/channel.py index dd682e72e..f2fc7a84a 100644 --- a/src/servers/chat/src/aggregates/channel.py +++ b/src/servers/chat/src/aggregates/channel.py @@ -10,9 +10,9 @@ from servers.chat.src.aggregates.managers import KeyValueManager from servers.chat.src.aggregates.peer_room import PeerRoom from servers.chat.src.applications.client import Client -from servers.chat.src.contracts.requests.channel import ModeRequest -from servers.chat.src.enums.peer_room import PeerRoomType -from servers.chat.src.exceptions.general import ChatException +from servers.chat.src.contracts.requests import ModeRequest +from servers.chat.src.aggregates.enums import PeerRoomType +from servers.chat.src.aggregates.exceptions import ChatException from servers.server_browser.src.v2.aggregations.server_info_builder import PEER_GROUP_LIST MIN_CHANNEL_NAME_LENGTH = 4 @@ -177,7 +177,6 @@ def remove_user(self, user: ChannelUser): user.client.info.previously_joined_channel - class BrockerMessage(BaseModel): channel_name: str message: str diff --git a/src/servers/chat/src/enums/general.py b/src/servers/chat/src/aggregates/enums.py similarity index 71% rename from src/servers/chat/src/enums/general.py rename to src/servers/chat/src/aggregates/enums.py index 42cbfc5e9..f997dc2c4 100644 --- a/src/servers/chat/src/enums/general.py +++ b/src/servers/chat/src/aggregates/enums.py @@ -1,4 +1,4 @@ -from enum import IntEnum +from enum import Enum, IntEnum class LoginRequestType(IntEnum): @@ -60,3 +60,31 @@ class ModeRequestType(IntEnum): class TopicRequestType(IntEnum): GET_CHANNEL_TOPIC = 0 SET_CHANNEL_TOPIC = 1 + + +# region IRC error code +class IRCErrorCode(Enum): + NO_SUCH_NICK = "401" + NO_SUCH_CHANNEL = "403" + TOO_MANY_CHANNELS = "405" + ERR_ONE_US_NICK_NAME = "432" + NICK_NAME_IN_USE = "433" + MORE_PARAMETERS = "461" + CHANNEL_IS_FULL = "471" + INVITE_ONLY_CHAN = "473" + BANNED_FROM_CHAN = "474" + BAD_CHANNEL_KEY = "475" + BAD_CHAN_MASK = "476" + LOGIN_FAILED = "708" + NO_UNIQUE_NICK = "709" + UNIQUE_NICK_EXPIRED = "710" + REGISTER_NICK_FAILED = "711" + +# region Peer room + + +class PeerRoomType(IntEnum): + Group = 0 + Staging = 1 + Title = 2 + Normal = 3 diff --git a/src/servers/chat/src/exceptions/general.py b/src/servers/chat/src/aggregates/exceptions.py similarity index 66% rename from src/servers/chat/src/exceptions/general.py rename to src/servers/chat/src/aggregates/exceptions.py index 7ba9cdf51..32d4ccd04 100644 --- a/src/servers/chat/src/exceptions/general.py +++ b/src/servers/chat/src/aggregates/exceptions.py @@ -1,5 +1,6 @@ +from servers.chat.src.aggregates.exceptions import IRCChannelException from servers.chat.src.abstractions.contract import SERVER_DOMAIN -from servers.chat.src.enums.irc_error_code import IRCErrorCode +from servers.chat.src.aggregates.enums import IRCErrorCode from library.src.exceptions.general import UniSpyException as ER @@ -18,7 +19,8 @@ def __init__(self, message: "str", error_code: "IRCErrorCode") -> None: self.error_code = error_code def build(self): - raise ChatException("IRCException is abstracted class, should not be initialized") + raise ChatException( + "IRCException is abstracted class, should not be initialized") class IRCChannelException(IRCException): @@ -90,6 +92,7 @@ def __init__( ) -> None: super().__init__(message, error_code) + class RegisterNickFaildException(IRCException): def __init__( self, message: str, error_code: IRCErrorCode = IRCErrorCode.REGISTER_NICK_FAILED @@ -109,3 +112,48 @@ def __init__( self, message: str, error_code: IRCErrorCode = IRCErrorCode.UNIQUE_NICK_EXPIRED ) -> None: super().__init__(message, error_code) + + +# region Channel Exceptions + + +class BadChannelMaskException(IRCChannelException): + def __init__( + self, message: str, error_code: IRCErrorCode = IRCErrorCode.BAD_CHAN_MASK + ) -> None: + super().__init__(message, error_code) + + +class BadChannelKeyException(IRCChannelException): + def __init__( + self, message: str, error_code: IRCErrorCode = IRCErrorCode.BAD_CHANNEL_KEY + ) -> None: + super().__init__(message, error_code) + + +class BannedFromChanException(IRCChannelException): + def __init__( + self, message: str, error_code: IRCErrorCode = IRCErrorCode.BANNED_FROM_CHAN + ) -> None: + super().__init__(message, error_code) + + +class ChannelIsFullException(IRCChannelException): + def __init__( + self, message: str, error_code: IRCErrorCode = IRCErrorCode.CHANNEL_IS_FULL + ) -> None: + super().__init__(message, error_code) + + +class InviteOnlyChanException(IRCChannelException): + def __init__( + self, message: str, error_code: IRCErrorCode = IRCErrorCode.INVITE_ONLY_CHAN + ) -> None: + super().__init__(message, error_code) + + +class NoSuchChannelException(IRCChannelException): + def __init__( + self, message: str, error_code: IRCErrorCode = IRCErrorCode.NO_SUCH_CHANNEL + ) -> None: + super().__init__(message, error_code) diff --git a/src/servers/chat/src/aggregates/peer_room.py b/src/servers/chat/src/aggregates/peer_room.py index 1e197f52c..d8dc04c21 100644 --- a/src/servers/chat/src/aggregates/peer_room.py +++ b/src/servers/chat/src/aggregates/peer_room.py @@ -1,8 +1,8 @@ -from servers.chat.src.enums.peer_room import PeerRoomType +from servers.chat.src.aggregates.enums import PeerRoomType class PeerRoom: - + TitleRoomPrefix = "#GSP" """ When game connects to the server, the player will enter the default channel for communicating with other players.""" StagingRoomPrefix = "#GSP" @@ -61,4 +61,4 @@ def is_group_room(channel_name: str) -> bool: if __name__ == "__main__": result = PeerRoom.get_room_type("#GSP!worms3!Ml4lz344lM") - pass \ No newline at end of file + pass diff --git a/src/servers/chat/src/applications/handlers.py b/src/servers/chat/src/applications/handlers.py new file mode 100644 index 000000000..54454348a --- /dev/null +++ b/src/servers/chat/src/applications/handlers.py @@ -0,0 +1,512 @@ +from servers.chat.src.contracts.results import ( + ATMResult, + NoticeResult, + PrivateResult, + UTMResult, + GetCKeyResult, + GetChannelKeyResult, + JoinResult, + KickResult, + ModeResult, + NamesResult, + PartResult, + SetChannelKeyResult, + TopicResult, + CryptResult, + GetKeyResult, + ListResult, + LoginResult, + NickResult, + PingResult, + UserIPResult, + WhoIsResult, + WhoResult, +) +from servers.chat.src.contracts.responses import ( + ATMResponse, + NoticeResponse, + PrivateResponse, + UTMResponse, + GetCKeyResponse, + JoinResponse, + KickResponse, + ModeResponse, + NamesResponse, + PartResponse, + SetCKeyResponse, + SetChannelKeyResponse, + TopicResponse, + CdKeyResponse, + CryptResponse, + GetKeyResponse, + ListResponse, + LoginResponse, + NickResponse, + PingResponse, + UserIPResponse, + WhoIsResponse, + WhoResponse, + +) +from servers.chat.src.contracts.requests import ( + ATMRequest, + NoticeRequest, + PrivateRequest, + UTMRequest, + GetCKeyRequest, + GetChannelKeyRequest, + JoinRequest, + KickRequest, + ModeRequest, + NamesRequest, + PartRequest, + SetCKeyRequest, + SetChannelKeyRequest, + TopicRequest, + CdkeyRequest, + CryptRequest, + GetKeyRequest, + InviteRequest, + ListRequest, + LoginRequest, + NickRequest, + PingRequest, + QuitRequest, + SetKeyRequest, + UserIPRequest, + UserRequest, + WhoIsRequest, + WhoRequest, + GetUdpRelayRequest +) +from servers.chat.src.abstractions.message import MessageHandlerBase, MessageRequestBase +from servers.chat.src.aggregates.exceptions import ChatException +from servers.chat.src.aggregates.enums import ModeRequestType, TopicRequestType +from servers.chat.src.aggregates.response_name import * +from servers.chat.src.aggregates.managers import ChannelManager +from servers.chat.src.aggregates.channel_user import ChannelUser +from servers.chat.src.aggregates.channel import Channel +from servers.chat.src.abstractions.channel import ChannelHandlerBase +from typing import Type +from library.src.abstractions.client import ClientBase +from servers.chat.src.abstractions.contract import RequestBase +from servers.chat.src.abstractions.handler import CmdHandlerBase, PostLoginHandlerBase +# region General + + +class CdKeyHandler(CmdHandlerBase): + _request: CdkeyRequest + + def __init__(self, client: ClientBase, request: CdkeyRequest): + assert isinstance(request, CdkeyRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = CdKeyResponse(self._request, self._result) + + +class CryptHandler(CmdHandlerBase): + _request: CryptRequest + _result: CryptResult + + def __init__(self, client: ClientBase, request: RequestBase): + assert isinstance(request, CryptRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = CryptResponse() + + +class GetKeyHandler(CmdHandlerBase): + _request: GetKeyRequest + _result: GetKeyResult + + def __init__(self, client: ClientBase, request: GetKeyRequest): + assert isinstance(request, GetKeyRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = GetKeyResponse(self._request, self._result) + + +class GetUdpRelayHandler(CmdHandlerBase): + _request: GetUdpRelayRequest + + def __init__(self, client: ClientBase, request: GetUdpRelayRequest): + assert isinstance(request, GetUdpRelayRequest) + super().__init__(client, request) + + +class InviteHandler(CmdHandlerBase): + _request: InviteRequest + + def __init__(self, client: ClientBase, request: InviteRequest): + assert isinstance(request, InviteRequest) + super().__init__(client, request) + + +class ListHandler(PostLoginHandlerBase): + _request: ListRequest + _result: ListResult + + def __init__(self, client: ClientBase, request: ListRequest): + assert isinstance(request, ListRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = ListResponse(self._result) + + +class LoginHandler(CmdHandlerBase): + _request: LoginRequest + _result: LoginResult + + def __init__(self, client: ClientBase, request: LoginRequest): + assert isinstance(request, LoginRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = LoginResponse(self._result) + + +class NickHandler(CmdHandlerBase): + _request: NickRequest + _result: NickResult + + def __init__(self, client: ClientBase, request: NickRequest): + assert isinstance(request, NickRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = NickResponse(self._result) + + +class PingHandler(CmdHandlerBase): + _request: PingRequest + _result: PingResult + + def __init__(self, client: ClientBase, request: PingRequest): + assert isinstance(request, PingRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = PingResponse(self._result) + + +class QuitHandler(CmdHandlerBase): + _request: QuitRequest + + def __init__(self, client: ClientBase, request: QuitRequest): + assert isinstance(request, QuitHandler) + super().__init__(client, request) + + +class SetKeyHandler(PostLoginHandlerBase): + _request: SetKeyRequest + + def __init__(self, client: ClientBase, request: SetKeyRequest): + assert isinstance(request, SetKeyRequest) + super().__init__(client, request) + + +class UserHandler(CmdHandlerBase): + _request: UserRequest + + def __init__(self, client: ClientBase, request: UserRequest): + assert isinstance(request, UserRequest) + super().__init__(client, request) + + +class UserIPHandler(CmdHandlerBase): + _request: UserIPRequest + _result: UserIPResult + + def __init__(self, client: ClientBase, request: UserIPRequest): + assert isinstance(request, UserIPRequest) + super().__init__(client, request) + + def _request_check(self) -> None: + super()._request_check() + self._request.remote_ip_address = ( + f"{self._client.connection.remote_ip}:{ + self._client.connection.remote_port}" + ) + + def _response_construct(self) -> None: + self._response = UserIPResponse(self._result) + + +class WhoHandler(CmdHandlerBase): + _request: WhoRequest + _result: WhoResult + + def __init__(self, client: ClientBase, request: WhoRequest): + assert isinstance(request, WhoRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = WhoResponse(self._request, self._result) + + +class WhoIsHandler(CmdHandlerBase): + _request: WhoIsRequest + _result: WhoIsResult + _result_type: Type = WhoIsResult + + def __init__(self, client: ClientBase, request: WhoIsRequest): + assert isinstance(request, WhoIsRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = WhoIsResponse(self._result) + +# region channel + + +class GetChannelKeyHandler(ChannelHandlerBase): + _request: GetChannelKeyRequest + _result: GetChannelKeyResult + + def __init__(self, client: ClientBase, request: GetChannelKeyRequest): + assert isinstance(request, GetChannelKeyRequest) + self._is_fetching_data = True + super().__init__(client, request) + + def _publish_message(self): + pass + + def _update_channel_cache(self): + pass + + +class GetCKeyHandler(ChannelHandlerBase): + _request: GetCKeyRequest + _result: GetCKeyResult + + def __init__(self, client: ClientBase, request: GetCKeyRequest): + assert isinstance(request, GetCKeyRequest) + super().__init__(client, request) + + def _publish_message(self): + pass + + def _update_channel_cache(self): + pass + + def _response_construct(self): + self._response = GetCKeyResponse(self._request, self._result) + + +class JoinHandler(ChannelHandlerBase): + _request: JoinRequest + _result: JoinResult + + def __init__(self, client: ClientBase, request: JoinRequest): + assert isinstance(request, JoinRequest) + super().__init__(client, request) + + def _check_user_in_remote(self): + """ + todo maybe do not need because there are nick handler? + """ + pass + + def _check_user_in_local(self): + channel = ChannelManager.get_channel( + self._request.channel_name) + if channel is None: + self._channel = Channel( + self._request.channel_name, self._client, self._request.password) + ChannelManager.add_channel(self._channel) + else: + self._channel = channel + if self._client.info.nick_name in self._channel.users: + raise ChatException("user is already in channel") + + def _request_check(self) -> None: + # todo check if user already in local channel + # self._check_user_in_remote() + self._check_user_in_local() + self._user = ChannelUser(self._client, self._channel) + self._channel.add_bind_on_user_and_channel(self._user) + + super()._request_check() + + def _response_construct(self): + self._response = JoinResponse(self._request, self._result) + + def _response_send(self): + # for join request we need to send to our self + self._channel.multicast(self._user.client, self._response, False) + + +class KickHandler(ChannelHandlerBase): + _request: KickRequest + _result: KickResult + + def __init__(self, client: ClientBase, request: KickRequest): + assert isinstance(request, KickRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = KickResponse(self._request, self._result) + + def _response_send(self): + self._channel.multicast(self._user.client, self._response, True) + + +class ModeHandler(ChannelHandlerBase): + _request: ModeRequest + _result: ModeResult + + def __init__(self, client: ClientBase, request: ModeRequest): + assert isinstance(request, ModeRequest) + super().__init__(client, request) + + def _response_construct(self): + match self._request.request_type: + case ( + ModeRequestType.GET_CHANNEL_AND_USER_MODES, + ModeRequestType.GET_CHANNEL_MODES, + ): + self._response = ModeResponse(self._request, self._result) + case ModeRequestType.SET_CHANNEL_MODES: + pass + case _: + raise ChatException("Unknown mode request type") + + def _publish_message(self): + if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: + super()._publish_message() + + def _update_channel_cache(self): + if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: + super()._update_channel_cache() + + def _response_send(self): + self._channel.multicast(self._user.client, self._response, True) + + +class NamesHandler(ChannelHandlerBase): + _request: NamesRequest + _result: NamesResult + + def __init__(self, client: ClientBase, request: NamesRequest): + assert isinstance(request, NamesRequest) + self._is_fetching_data = True + super().__init__(client, request) + + def _response_construct(self): + self._response = NamesResponse(self._request, self._result) + + +class PartHandler(ChannelHandlerBase): + _request: PartRequest + _result: PartResult + + def __init__(self, client: ClientBase, request: PartRequest): + assert isinstance(request, PartRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = PartResponse(self._request, self._result) + + def _response_send(self): + self._channel.multicast(self._user.client, self._response, True) + + +class SetChannelKeyHandler(ChannelHandlerBase): + _request: SetChannelKeyRequest + _result: SetChannelKeyResult + + def __init__(self, client: ClientBase, request: SetChannelKeyRequest): + assert isinstance(self._request, SetChannelKeyRequest) + super().__init__(client, request) + + def _response_construct(self): + self._response = SetChannelKeyResponse(self._request, self._result) + + def _response_send(self): + self._channel.multicast(self._user.client, self._response, True) + + +class SetCKeyHandler(ChannelHandlerBase): + _request: SetCKeyRequest + + def __init__(self, client: ClientBase, request: SetCKeyRequest): + assert isinstance(request, SetCKeyRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = SetCKeyResponse(self._request) + + def _response_send(self): + self._channel.multicast(self._user.client, self._response, True) + + +class TopicHandler(ChannelHandlerBase): + _request: TopicRequest + _result: TopicResult + + def __init__(self, client: ClientBase, request: TopicRequest): + assert isinstance(request, TopicRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = TopicResponse(self._request, self._result) + + def _response_send(self) -> None: + match self._request.request_type: + case TopicRequestType.GET_CHANNEL_TOPIC: + self._client.send(self._response) + case TopicRequestType.SET_CHANNEL_TOPIC: + self._channel.multicast(self._client, self._response) +# region Message + + +class ATMHandler(MessageHandlerBase): + _request: ATMRequest + _result: ATMResult + + def __init__(self, client: ClientBase, request: ATMRequest): + assert isinstance(request, ATMRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = ATMResponse(self._request, self._result) + + +class UTMHandler(MessageHandlerBase): + _request: UTMRequest + _result: UTMResult + + def __init__(self, client: ClientBase, request: UTMRequest): + assert isinstance(request, UTMRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = UTMResponse(self._request, self._result) + + +class NoticeHandler(MessageHandlerBase): + _request: NoticeRequest + _result: NoticeResult + + def __init__(self, client: ClientBase, request: NoticeRequest): + assert isinstance(request, NoticeRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = NoticeResponse(self._request, self._result) + + +class PrivateHandler(MessageHandlerBase): + _request: PrivateRequest + _result: PrivateResult + + def __init__(self, client: ClientBase, request: PrivateRequest): + assert isinstance(request, PrivateRequest) + super().__init__(client, request) + + def _response_construct(self) -> None: + self._response = PrivateResponse(self._request, self._result) diff --git a/src/servers/chat/src/handlers/switcher.py b/src/servers/chat/src/applications/switcher.py similarity index 93% rename from src/servers/chat/src/handlers/switcher.py rename to src/servers/chat/src/applications/switcher.py index d6c7adaa3..fe3cc24fe 100644 --- a/src/servers/chat/src/handlers/switcher.py +++ b/src/servers/chat/src/applications/switcher.py @@ -2,7 +2,7 @@ from library.src.abstractions.client import ClientBase from library.src.abstractions.handler import CmdHandlerBase from library.src.abstractions.switcher import SwitcherBase -from servers.chat.src.contracts.requests.channel import ( +from servers.chat.src.contracts.requests import ( GetCKeyRequest, GetChannelKeyRequest, JoinRequest, @@ -13,8 +13,6 @@ SetCKeyRequest, SetChannelKeyRequest, TopicRequest, -) -from servers.chat.src.contracts.requests.general import ( CdkeyRequest, CryptRequest, GetKeyRequest, @@ -28,14 +26,12 @@ UserRequest, WhoIsRequest, WhoRequest, -) -from servers.chat.src.contracts.requests.message import ( ATMRequest, NoticeRequest, PrivateRequest, UTMRequest, ) -from servers.chat.src.handlers.channel import ( +from servers.chat.src.applications.handlers import ( GetCKeyHandler, GetChannelKeyHandler, JoinHandler, @@ -46,8 +42,6 @@ SetCKeyHandler, SetChannelKeyHandler, TopicHandler, -) -from servers.chat.src.handlers.general import ( CdKeyHandler, CryptHandler, GetKeyHandler, @@ -61,8 +55,6 @@ UserIPHandler, WhoHandler, WhoIsHandler, -) -from servers.chat.src.handlers.message import ( ATMHandler, NoticeHandler, PrivateHandler, diff --git a/src/servers/chat/src/contracts/__init__.py b/src/servers/chat/src/contracts/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/chat/src/contracts/requests/channel.py b/src/servers/chat/src/contracts/requests.py similarity index 61% rename from src/servers/chat/src/contracts/requests/channel.py rename to src/servers/chat/src/contracts/requests.py index 29fc98e0d..a858b5e52 100644 --- a/src/servers/chat/src/contracts/requests/channel.py +++ b/src/servers/chat/src/contracts/requests.py @@ -1,14 +1,300 @@ -import re -from typing import List, Optional -from servers.chat.src.abstractions.channel import ChannelRequestBase -from servers.chat.src.enums.general import ( +from servers.chat.src.abstractions.message import MessageRequestBase +from library.src.extentions.string_extentions import convert_kvstring_to_dictionary +from servers.chat.src.aggregates.enums import ( GetKeyRequestType, ModeOperationType, ModeRequestType, TopicRequestType, ) -from servers.chat.src.exceptions.general import ChatException -from library.src.extentions.string_extentions import convert_kvstring_to_dictionary +from servers.chat.src.abstractions.channel import ChannelRequestBase +from typing import List, Optional +import re +from library.src.extentions.string_extentions import ( + convert_keystr_to_list, + convert_kvstring_to_dictionary, +) +from servers.chat.src.abstractions.contract import RequestBase +from servers.chat.src.aggregates.enums import LoginRequestType, WhoRequestType +from servers.chat.src.aggregates.exceptions import ChatException +from servers.chat.src.aggregates.exceptions import NickNameInUseException + +# General + + +class CdkeyRequest(RequestBase): + cdkey: str + + def parse(self): + super().parse() + if len(self._cmd_params) < 1: + raise ChatException("The number of IRC cmdParams are incorrect.") + + self.cdkey = self._cmd_params[0] + + +class CryptRequest(RequestBase): + version_id: str + gamename: str + + def parse(self): + super().parse() + if len(self._cmd_params) < 3: + raise ChatException( + "The number of IRC params in CRYPT request is incorrect." + ) + self.version_id = self._cmd_params[1] + self.gamename = self._cmd_params[2] + + +class InviteRequest(RequestBase): + channel_name: str + nick_name: str + + def parse(self): + super().parse() + if len(self._cmd_params) > 2: + raise ChatException( + "The number of IRC cmd params in Invite request is incorrect." + ) + self.channel_name = self._cmd_params[0] + self.nick_name = self._cmd_params[1] + + +class ListLimitRequest(RequestBase): + max_number_of_channels: int + filter: str + + def parse(self): + super().parse() + if len(self._cmd_params) != 2: + raise ChatException( + "The number of IRC cmd params in ListLimit request is incorrect." + ) + try: + self.max_number_of_channels = int(self._cmd_params[0]) + + except Exception as e: + raise ChatException("The max number format is incorrect.") + + self.filter = self._cmd_params[1] + + +class ListRequest(RequestBase): + is_searching_channel: bool = False + is_searching_user: bool = False + filter: str + + def parse(self): + super().parse() + if self._cmd_params is None or len(self._cmd_params) == 0: + raise ChatException("The Search filter is missing.") + + self.is_searching_channel = True + self.filter = self._cmd_params[0] + + +class LoginPreAuth(RequestBase): + auth_token: str + partner_challenge: str + + def parse(self): + super().parse() + self.auth_token = self._cmd_params[0] + self.partner_challenge = self._cmd_params[1] + + +class LoginRequest(RequestBase): + request_type: LoginRequestType + namespace_id: int + nick_name: str + email: str + unique_nick: str + password_hash: str + + def parse(self): + super().parse() + try: + self.namespace_id = int(self._cmd_params[0]) + + except Exception as e: + raise ChatException("The namespaceid format is incorrect.") + + if self._cmd_params[1] == "*": + self.request_type = LoginRequestType.NICK_AND_EMAIL_LOGIN + self.password_hash = self._cmd_params[2] + if self._longParam.count("@") != 2: + raise ChatException("The profile nick format is incorrect.") + + profile_nick_index = self._longParam.index("@") + self.nick_name = self._longParam[0:profile_nick_index] + self.email = self._longParam[profile_nick_index + 1:] + return + + self.request_type = LoginRequestType.UNIQUE_NICK_LOGIN + self.unique_nick = self._cmd_params[1] + self.password_hash = self._cmd_params[2] + + +class NickRequest(RequestBase): + _invalid_chars = "#@$%^&*()~" + nick_name: str + + def parse(self): + super().parse() + if len(self._cmd_params) == 1: + self.nick_name = self._cmd_params[0] + elif self._longParam is None: + self.nick_name = self._longParam + else: + raise ChatException("NICK request is invalid.") + + for c in self._invalid_chars: + if c in self.nick_name: + raise NickNameInUseException( + self.nick_name, + self.nick_name, + f"The nick name: { + self.nick_name} contains invalid character.", + ) + + +class PingRequest(RequestBase): + pass + + +class PongRequest(RequestBase): + echo_message: str + + def parse(self): + super().parse() + if self._longParam is None: + raise ChatException("Echo message is missing.") + self.echo_message = self._longParam + + +class QuitRequest(RequestBase): + reason: str + + def parse(self): + super().parse() + if self._longParam is None: + raise ChatException("Quit reason is missing.") + + self.reason = self._longParam + + +class RegisterNickRequest(RequestBase): + namespace_id: int + unique_nick: str + cdkey: str + + def parse(self): + super().parse() + self.namespace_id = self._cmd_params[0] + self.unique_nick = self._cmd_params[1] + self.cdkey = self._cmd_params[2] + + +class SetKeyRequest(RequestBase): + key_values: dict[str, str] + + def parse(self): + super().parse() + if self._longParam is None: + raise ChatException("The keys and values are missing.") + + self.key_values = convert_kvstring_to_dictionary(self._longParam) + + +class UserIPRequest(RequestBase): + remote_ip_address: str + + +class UserRequest(RequestBase): + user_name: str + host_name: str + server_name: str + nick_name: str + name: str + + def parse(self): + super().parse() + if len(self._cmd_params) == 3: + self.user_name = self._cmd_params[0] + self.host_name = self._cmd_params[1] + self.server_name = self._cmd_params[2] + else: + self.host_name = self._cmd_params[0] + self.server_name = self._cmd_params[1] + + self.name = self._longParam + + +class WhoIsRequest(RequestBase): + nick_name: str + + def parse(self): + super().parse() + if len(self._cmd_params) != 1: + raise ChatException( + "The number of IRC cmd params in WHOIS request is incorrect." + ) + + self.nick_name = self._cmd_params[0] + + +class WhoRequest(RequestBase): + request_type: WhoRequestType + channel_name: str + nick_name: str + + def parse(self): + super().parse() + if len(self._cmd_params) != 1: + raise ChatException( + "The number of IRC cmd params in WHO request is incorrect." + ) + + if "#" in self._cmd_params[0]: + self.request_type = WhoRequestType.GET_CHANNEL_USER_INFO + self.channel_name = self._cmd_params[0] + return + + self.request_type = WhoRequestType.GET_USER_INFO + self.nick_name = self._cmd_params[0] + + +class GetKeyRequest(RequestBase): + is_get_all_user: bool = False + nick_name: str + cookie: str + unknown_cmd_param: str + keys: list[str] + + def parse(self) -> None: + super().parse() + if len(self._cmd_params) < 2: + raise ChatException( + "The number of IRC cmd params in GETKEY request is incorrect." + ) + + if self._longParam is None: + raise ChatException( + "The number of IRC cmd params in GETKEY request is incorrect." + ) + + self.nick_name = self._cmd_params[0] + self.cookie = self._cmd_params[1] + self.unknown_cmd_param = self._cmd_params[2] + + self._longParam = self._longParam[: len(self._longParam)] + if self.nick_name == "*": + self.is_get_all_user = True + + self.keys = convert_keystr_to_list(self._longParam) + + +# region Channel class GetChannelKeyRequest(ChannelRequestBase): @@ -350,3 +636,21 @@ def parse(self) -> None: else: self.request_type = TopicRequestType.SET_CHANNEL_TOPIC self.channel_topic = self._longParam + +# region Message + + +class ATMRequest(MessageRequestBase): + pass + + +class NoticeRequest(MessageRequestBase): + pass + + +class PrivateRequest(MessageRequestBase): + pass + + +class UTMRequest(MessageRequestBase): + pass diff --git a/src/servers/chat/src/contracts/requests/__init__.py b/src/servers/chat/src/contracts/requests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/chat/src/contracts/requests/general.py b/src/servers/chat/src/contracts/requests/general.py deleted file mode 100644 index 1334ead9d..000000000 --- a/src/servers/chat/src/contracts/requests/general.py +++ /dev/null @@ -1,286 +0,0 @@ -from typing import Dict -from library.src.extentions.string_extentions import ( - convert_keystr_to_list, - convert_kvstring_to_dictionary, -) -from servers.chat.src.abstractions.contract import RequestBase -from servers.chat.src.enums.general import LoginRequestType, WhoRequestType -from servers.chat.src.exceptions.general import ChatException -from servers.chat.src.exceptions.general import NickNameInUseException - - -class CdkeyRequest(RequestBase): - cdkey: str - - def parse(self): - super().parse() - if len(self._cmd_params) < 1: - raise ChatException("The number of IRC cmdParams are incorrect.") - - self.cdkey = self._cmd_params[0] - - -class CryptRequest(RequestBase): - version_id: str - gamename: str - - def parse(self): - super().parse() - if len(self._cmd_params) < 3: - raise ChatException( - "The number of IRC params in CRYPT request is incorrect." - ) - self.version_id = self._cmd_params[1] - self.gamename = self._cmd_params[2] - - -class GetUdpRelayRequest(RequestBase): - pass - - -class InviteRequest(RequestBase): - channel_name: str - nick_name: str - - def parse(self): - super().parse() - if len(self._cmd_params) > 2: - raise ChatException( - "The number of IRC cmd params in Invite request is incorrect." - ) - self.channel_name = self._cmd_params[0] - self.nick_name = self._cmd_params[1] - - -class ListLimitRequest(RequestBase): - max_number_of_channels: int - filter: str - - def parse(self): - super().parse() - if len(self._cmd_params) != 2: - raise ChatException( - "The number of IRC cmd params in ListLimit request is incorrect." - ) - try: - self.max_number_of_channels = int(self._cmd_params[0]) - - except Exception as e: - raise ChatException("The max number format is incorrect.") - - self.filter = self._cmd_params[1] - - -class ListRequest(RequestBase): - is_searching_channel: bool = False - is_searching_user: bool = False - filter: str - - def parse(self): - super().parse() - if self._cmd_params is None or len(self._cmd_params) == 0: - raise ChatException("The Search filter is missing.") - - self.is_searching_channel = True - self.filter = self._cmd_params[0] - - -class LoginPreAuth(RequestBase): - auth_token: str - partner_challenge: str - - def parse(self): - super().parse() - self.auth_token = self._cmd_params[0] - self.partner_challenge = self._cmd_params[1] - - -class LoginRequest(RequestBase): - request_type: LoginRequestType - namespace_id: int - nick_name: str - email: str - unique_nick: str - password_hash: str - - def parse(self): - super().parse() - try: - self.namespace_id = int(self._cmd_params[0]) - - except Exception as e: - raise ChatException("The namespaceid format is incorrect.") - - if self._cmd_params[1] == "*": - self.request_type = LoginRequestType.NICK_AND_EMAIL_LOGIN - self.password_hash = self._cmd_params[2] - if self._longParam.count("@") != 2: - raise ChatException("The profile nick format is incorrect.") - - profile_nick_index = self._longParam.index("@") - self.nick_name = self._longParam[0:profile_nick_index] - self.email = self._longParam[profile_nick_index + 1:] - return - - self.request_type = LoginRequestType.UNIQUE_NICK_LOGIN - self.unique_nick = self._cmd_params[1] - self.password_hash = self._cmd_params[2] - - -class NickRequest(RequestBase): - _invalid_chars = "#@$%^&*()~" - nick_name: str - - def parse(self): - super().parse() - if len(self._cmd_params) == 1: - self.nick_name = self._cmd_params[0] - elif self._longParam is None: - self.nick_name = self._longParam - else: - raise ChatException("NICK request is invalid.") - - for c in self._invalid_chars: - if c in self.nick_name: - raise NickNameInUseException( - self.nick_name, - self.nick_name, - f"The nick name: { - self.nick_name} contains invalid character.", - ) - - -class PingRequest(RequestBase): - pass - - -class PongRequest(RequestBase): - echo_message: str - - def parse(self): - super().parse() - if self._longParam is None: - raise ChatException("Echo message is missing.") - self.echo_message = self._longParam - - -class QuitRequest(RequestBase): - reason: str - - def parse(self): - super().parse() - if self._longParam is None: - raise ChatException("Quit reason is missing.") - - self.reason = self._longParam - - -class RegisterNickRequest(RequestBase): - namespace_id: int - unique_nick: str - cdkey: str - - def parse(self): - super().parse() - self.namespace_id = self._cmd_params[0] - self.unique_nick = self._cmd_params[1] - self.cdkey = self._cmd_params[2] - - -class SetKeyRequest(RequestBase): - key_values: dict[str, str] - - def parse(self): - super().parse() - if self._longParam is None: - raise ChatException("The keys and values are missing.") - - self.key_values = convert_kvstring_to_dictionary(self._longParam) - - -class UserIPRequest(RequestBase): - remote_ip_address: str - - -class UserRequest(RequestBase): - user_name: str - host_name: str - server_name: str - nick_name: str - name: str - - def parse(self): - super().parse() - if len(self._cmd_params) == 3: - self.user_name = self._cmd_params[0] - self.host_name = self._cmd_params[1] - self.server_name = self._cmd_params[2] - else: - self.host_name = self._cmd_params[0] - self.server_name = self._cmd_params[1] - - self.name = self._longParam - - -class WhoIsRequest(RequestBase): - nick_name: str - - def parse(self): - super().parse() - if len(self._cmd_params) != 1: - raise ChatException( - "The number of IRC cmd params in WHOIS request is incorrect." - ) - - self.nick_name = self._cmd_params[0] - - -class WhoRequest(RequestBase): - request_type: WhoRequestType - channel_name: str - nick_name: str - - def parse(self): - super().parse() - if len(self._cmd_params) != 1: - raise ChatException( - "The number of IRC cmd params in WHO request is incorrect." - ) - - if "#" in self._cmd_params[0]: - self.request_type = WhoRequestType.GET_CHANNEL_USER_INFO - self.channel_name = self._cmd_params[0] - return - - self.request_type = WhoRequestType.GET_USER_INFO - self.nick_name = self._cmd_params[0] - - -class GetKeyRequest(RequestBase): - is_get_all_user: bool = False - nick_name: str - cookie: str - unknown_cmd_param: str - keys: list[str] - - def parse(self) -> None: - super().parse() - if len(self._cmd_params) < 2: - raise ChatException( - "The number of IRC cmd params in GETKEY request is incorrect." - ) - - if self._longParam is None: - raise ChatException( - "The number of IRC cmd params in GETKEY request is incorrect." - ) - - self.nick_name = self._cmd_params[0] - self.cookie = self._cmd_params[1] - self.unknown_cmd_param = self._cmd_params[2] - - self._longParam = self._longParam[: len(self._longParam)] - if self.nick_name == "*": - self.is_get_all_user = True - - self.keys = convert_keystr_to_list(self._longParam) diff --git a/src/servers/chat/src/contracts/requests/message.py b/src/servers/chat/src/contracts/requests/message.py deleted file mode 100644 index 7870af701..000000000 --- a/src/servers/chat/src/contracts/requests/message.py +++ /dev/null @@ -1,17 +0,0 @@ -from servers.chat.src.abstractions.message import MessageRequestBase - - -class ATMRequest(MessageRequestBase): - pass - - -class NoticeRequest(MessageRequestBase): - pass - - -class PrivateRequest(MessageRequestBase): - pass - - -class UTMRequest(MessageRequestBase): - pass diff --git a/src/servers/chat/src/contracts/responses.py b/src/servers/chat/src/contracts/responses.py new file mode 100644 index 000000000..a6c8eae03 --- /dev/null +++ b/src/servers/chat/src/contracts/responses.py @@ -0,0 +1,411 @@ +from servers.chat.src.aggregates.enums import ModeRequestType, WhoRequestType +from servers.chat.src.contracts.results import ( + GetCKeyResult, + GetChannelKeyResult, + JoinResult, + KickResult, + ModeResult, + NamesResult, + PartResult, + SetChannelKeyResult, + TopicResult, + ATMResult, + NoticeResult, + PrivateResult, + UTMResult, + GetKeyResult, + ListResult, + LoginResult, + NickResult, + PingResult, + UserIPResult, + WhoIsResult, + WhoResult, +) +from servers.chat.src.contracts.requests import ( + GetCKeyRequest, + GetChannelKeyRequest, + JoinRequest, + KickRequest, + ModeRequest, + NamesRequest, + PartRequest, + SetCKeyRequest, + SetChannelKeyRequest, + TopicRequest, + ATMRequest, + NoticeRequest, + PrivateRequest, + UTMRequest, + GetKeyRequest, + WhoRequest + +) +from servers.chat.src.abstractions.contract import ( + SERVER_DOMAIN, + ResponseBase, +) +from servers.chat.src.abstractions.channel import ChannelResponseBase +from library.src.encryption.gs_encryption import CLIENT_KEY, SERVER_KEY +from servers.chat.src.aggregates.response_name import * + +# region General + + +class CdKeyResponse(ResponseBase): + def build(self) -> None: + self.sending_buffer = f":{SERVER_DOMAIN} {CD_KEY} * 1 :Authenticated.\r\n" # noqa + + +class CryptResponse(ResponseBase): + def __init__(self) -> None: + pass + + def build(self) -> None: + self.sending_buffer = f":{SERVER_DOMAIN} {SECURE_KEY} * {CLIENT_KEY} {SERVER_KEY}\r\n" # noqa + + +class GetKeyResponse(ResponseBase): + _request: GetKeyRequest + _result: GetKeyResult + + def __init__(self, request: GetKeyRequest, result: GetKeyResult) -> None: + assert isinstance(request, GetKeyRequest) + assert isinstance(result, GetKeyResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = "" + + for value in self._result.values: + self.sending_buffer += f":{SERVER_DOMAIN} {GET_KEY} * {self._result.nick_name} {self._request.cookie} {value}\r\n" # noqa + + self.sending_buffer += f":{SERVER_DOMAIN} {END_GET_KEY} * {self._request.cookie} * :End Of GETKEY.\r\n" # noqa + + +class ListResponse(ResponseBase): + _result: ListResult + + def __init__(self, result: ListResult) -> None: + assert isinstance(result, ListResult) + self._result = result + + def build(self) -> None: + self.sending_buffer = "" + for ( + channel_name, + total_channel_user, + channel_topic, + ) in self._result.channel_info_list: + self.sending_buffer += f":{self._result.user_irc_prefix} {LIST_START} * {channel_name} {total_channel_user} {channel_topic}\r\n" # noqa + self.sending_buffer += f":{self._result.user_irc_prefix} {LIST_END}\r\n" # noqa + + +class LoginResponse(ResponseBase): + _result: LoginResult + + def __init__(self, result: LoginResult) -> None: + assert isinstance(result, LoginResult) + self._result = result + + def build(self) -> None: + self.sending_buffer = f":{SERVER_DOMAIN} {LOGIN} * {self._result.user_id} {self._result.profile_id}\r\n" # noqa + + +class NickResponse(ResponseBase): + _result: NickResult + + def __init__(self, result: NickResult) -> None: + assert isinstance(result, NickResult) + self._result = result + + def build(self) -> None: + self.sending_buffer = f":{SERVER_DOMAIN} {WELCOME} {self._result.nick_name} :Welcome to UniSpy!\r\n" # noqa + + +class PingResponse(ResponseBase): + _result: PingResult + + def __init__(self, result: PingResult) -> None: + assert isinstance(result, PingResult) + self._result = result + + def build(self) -> None: + self.sending_buffer = f":{ + self._result.requester_irc_prefix} {PONG}\r\n" + + +class UserIPResponse(ResponseBase): + _result: UserIPResult + + def __init__(self, result: UserIPResult) -> None: + assert isinstance(result, UserIPResult) + self._result = result + + def build(self) -> None: + self.sending_buffer = f":{SERVER_DOMAIN} {USER_IP} :@{self._result.remote_ip_address}\r\n" # noqa + + +class WhoIsResponse(ResponseBase): + _result: WhoIsResult + + def __init__(self, result: WhoIsResult) -> None: + assert isinstance(result, UserIPResult) + self._result = result + + def build(self) -> None: + self.sending_buffer = f":{SERVER_DOMAIN} {WHO_IS_USER} {self._result.nick_name} {self._result.name} {self._result.user_name} {self._result.public_ip_address} * :{self._result.user_name}\r\n" # noqa + + if len(self._result.joined_channel_name) != 0: + channel_name = "" + for name in self._result.joined_channel_name: + channel_name += f"@{name} " # noqa + + self.sending_buffer += f":{SERVER_DOMAIN} {WHO_IS_CHANNELS} {self._result.nick_name} {self._result.name} :{channel_name}\r\n" # noqa + + self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO_IS} {self._result.nick_name} {self._result.name} :End of WHOIS list. \r\n" # noqa + + +class WhoResponse(ResponseBase): + _request: WhoRequest + _result: WhoResult + + def __init__(self, request: WhoRequest, result: WhoResult) -> None: + assert isinstance(request, WhoRequest) + assert isinstance(result, WhoResult) + super().__init__(request, result) + + def build(self): + self.sending_buffer = "" + for ( + channel_name, + user_name, + public_ip_address, + nick_name, + modes, + ) in self._result.infos: + self.sending_buffer += f":{SERVER_DOMAIN} {WHO_REPLY} * {channel_name} {user_name} {public_ip_address} * {nick_name} {modes} *\r\n" # noqa + + if self._request.request_type == WhoRequestType.GET_CHANNEL_USER_INFO: + if len(self._result.infos) > 0: + self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.channel_name} * :End of WHO.\r\n" # noqa + elif self._request.request_type == WhoRequestType.GET_USER_INFO: + if len(self._result.infos) > 0: + self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.nick_name} * :End of WHO.\r\n" # noqa + + +# region Channel + + +class GetChannelKeyResponse(ChannelResponseBase): + _request: GetChannelKeyRequest + _result: GetChannelKeyResult + + def build(self) -> None: + self.sending_buffer = f":{self._result.channel_user_irc_prefix} { + GET_CHAN_KEY} * {self._result.channel_name} {self._request.cookie} {self._result.values}\r\n" + + +class GetCKeyResponse(ResponseBase): + _request: GetCKeyRequest + _result: GetCKeyResult + + def __init__(self, request: GetCKeyRequest, result: GetCKeyResult) -> None: + assert isinstance(request, GetCKeyRequest) + assert isinstance(result, GetCKeyResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = "" + for nick_name, user_values in self._result.infos: + self.sending_buffer += f":{SERVER_DOMAIN} {GET_CKEY} * { + self._request.channel_name} {nick_name} {self._request.cookie} {user_values}\r\n" + + self.sending_buffer += f"{SERVER_DOMAIN} {END_GET_CKEY} * { + self._request.channel_name} {self._request.cookie} :End Of GETCKEY.\r\n" + + +class JoinResponse(ResponseBase): + _request: JoinRequest + _result: JoinResult + + def __init__(self, request: JoinRequest, result: JoinResult) -> None: + assert isinstance(request, JoinRequest) + assert isinstance(result, JoinResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = ( + f"{self._result.joiner_prefix} {JOIN} { + self._request.channel_name}\r\n" + ) + + +class KickResponse(ResponseBase): + _request: KickRequest + _result: KickResult + + def __init__(self, request: KickRequest, result: KickResult) -> None: + assert isinstance(request, KickRequest) + assert isinstance(result, KickResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = f"{self._result.kicker_irc_prefix} {KICK} { + self._result.channel_name} {self._result.kickee_nick_name} :{self._request.reason}\r\n" + + +class ModeResponse(ResponseBase): + _request: ModeRequest + _result: ModeResult + + def __init__(self, request: ModeRequest, result: ModeResult) -> None: + assert isinstance(request, ModeRequest) + assert isinstance(result, ModeResult) + super().__init__(request, result) + + def build(self) -> None: + if self._request.request_type == ModeRequestType.GET_CHANNEL_MODES: + self.sending_buffer = f":{SERVER_DOMAIN} { + CHANNEL_MODELS} * {self._result.channel_modes} {self._result.channel_modes}\r\n" + + +class NamesResponse(ChannelResponseBase): + _request: NamesRequest + _result: NamesResult + + def __init__(self, request: NamesRequest, result: NamesResult) -> None: + assert isinstance(request, NamesRequest) + assert isinstance(result, NamesResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = f":{SERVER_DOMAIN} {NAME_REPLY} {self._result.requester_nick_name} = { + self._result.channel_name} :{self._result.all_channel_user_nicks}\r\n" + + self.sending_buffer = f":{SERVER_DOMAIN} {END_OF_NAMES} { + self._result.requester_nick_name} {self._result.channel_name} :End of NAMES list. \r\n" + + +class PartResponse(ResponseBase): + _request: PartRequest + _result: PartResult + + def __init__(self, request: PartRequest, result: PartResult) -> None: + assert isinstance(request, PartRequest) + assert isinstance(result, PartResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = f":{self._result.leaver_irc_prefix} { + PART} {self._result.channel_name} :{self._request.reason}\r\n" + + +class SetChannelKeyResponse(ChannelResponseBase): + _request: SetChannelKeyRequest + _result: SetChannelKeyResult + + def __init__( + self, request: SetChannelKeyRequest, result: SetChannelKeyResult + ) -> None: + assert isinstance(request, SetChannelKeyRequest) + assert isinstance(result, SetChannelKeyResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = f":{self._result.channel_user_irc_prefix} { + GET_CHAN_KEY} * {self._request.channel_name} BCAST {self._request.key_value_string}\r\n" + + +class SetCKeyResponse(ResponseBase): + _request: SetCKeyRequest + + def __init__(self, request: SetCKeyRequest) -> None: + assert isinstance(request, SetCKeyRequest) + super().__init__(request, None) + + def build(self) -> None: + self.sending_buffer = f":{SERVER_DOMAIN} {GET_CKEY} * {self._request.channel_name} { + self._request.nick_name} {self._request.cookie} {self._request.key_value_string}\r\n" + + self.sending_buffer = f":{SERVER_DOMAIN} {END_GET_CKEY} * {self._request.channel_name} { + self._request.nick_name} {self._request.cookie} :End Of SETCKEY.\r\n" + + +class TopicResponse(ResponseBase): + _request: TopicRequest + _result: TopicResult + + def __init__(self, request: TopicRequest, result: TopicResult) -> None: + assert isinstance(request, TopicRequest) + assert isinstance(result, TopicResult) + super().__init__(request, result) + + def build(self) -> None: + if self._result.channel_topic == "" or self._result.channel_topic is None: + self.sending_buffer = ( + f":{SERVER_DOMAIN} {NO_TOPIC} * {self._result.channel_name}\r\n" + ) + else: + self.sending_buffer = f":{SERVER_DOMAIN} { + TOPIC} * {self._result.channel_name} :{self._result.channel_topic}\r\n" + + +# region Message + + +class ATMResponse(ResponseBase): + _request: ATMRequest + _result: ATMResult + + def __init__(self, request: ATMRequest, result: ATMResult) -> None: + assert isinstance(request, ATMRequest) + assert isinstance(result, ATMResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = f":{self._result.user_irc_prefix} {ABOVE_THE_TABLE_MSG} { + self._result.target_name} :{self._request.message}\r\n" + + +class NoticeResponse(ResponseBase): + _request: NoticeRequest + _result: NoticeResult + + def __init__(self, request: NoticeRequest, result: NoticeResult) -> None: + assert isinstance(result, NoticeResult) + assert isinstance(request, NoticeRequest) + + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = f":{self._result.user_irc_prefix} {NOTICE} { + self._result.target_name} {self._request.message}\r\n" + + +class PrivateResponse(ResponseBase): + _request: PrivateRequest + _result: PrivateResult + + def __init__(self, request: PrivateRequest, result: PrivateResult) -> None: + assert isinstance(result, PrivateRequest) + assert isinstance(request, PrivateResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = f":{self._result.user_irc_prefix} {PRIVATE_MSG} { + self._result.target_name} :{self._request.message}\r\n" + + +class UTMResponse(ResponseBase): + _request: UTMRequest + _result: UTMResult + + def __init__(self, request: UTMRequest, result: UTMResult) -> None: + assert isinstance(request, UTMRequest) + assert isinstance(result, UTMResult) + super().__init__(request, result) + + def build(self) -> None: + self.sending_buffer = f":{self._result.user_irc_prefix} { + UNDER_THE_TABLE_MSG} {self._result.target_name} :{self._request.message}" diff --git a/src/servers/chat/src/contracts/responses/__init__.py b/src/servers/chat/src/contracts/responses/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/chat/src/contracts/responses/channel.py b/src/servers/chat/src/contracts/responses/channel.py deleted file mode 100644 index 0c76d8e06..000000000 --- a/src/servers/chat/src/contracts/responses/channel.py +++ /dev/null @@ -1,174 +0,0 @@ -from library.src.abstractions.contracts import RequestBase, ResultBase -from servers.chat.src.abstractions.channel import ChannelResponseBase -from servers.chat.src.abstractions.contract import ( - SERVER_DOMAIN, - RequestBase, - ResponseBase, - ResultBase, -) -from servers.chat.src.aggregates.response_name import * -from servers.chat.src.contracts.requests.channel import ( - GetCKeyRequest, - GetChannelKeyRequest, - JoinRequest, - KickRequest, - ModeRequest, - NamesRequest, - PartRequest, - SetCKeyRequest, - SetChannelKeyRequest, - TopicRequest, -) -from servers.chat.src.contracts.results.channel import ( - GetCKeyResult, - GetChannelKeyResult, - JoinResult, - KickResult, - ModeResult, - NamesResult, - PartResult, - SetChannelKeyResult, - TopicResult, -) -from servers.chat.src.enums.general import ModeRequestType - - -class GetChannelKeyResponse(ChannelResponseBase): - _request: GetChannelKeyRequest - _result: GetChannelKeyResult - - def build(self) -> None: - self.sending_buffer = f":{self._result.channel_user_irc_prefix} {GET_CHAN_KEY} * {self._result.channel_name} {self._request.cookie} {self._result.values}\r\n" - - -class GetCKeyResponse(ResponseBase): - _request: GetCKeyRequest - _result: GetCKeyResult - - def __init__(self, request: GetCKeyRequest, result: GetCKeyResult) -> None: - assert isinstance(request, GetCKeyRequest) - assert isinstance(result, GetCKeyResult) - super().__init__(request, result) - - def build(self) -> None: - self.sending_buffer = "" - for nick_name, user_values in self._result.infos: - self.sending_buffer += f":{SERVER_DOMAIN} {GET_CKEY} * {self._request.channel_name} {nick_name} {self._request.cookie} {user_values}\r\n" - - self.sending_buffer += f"{SERVER_DOMAIN} {END_GET_CKEY} * {self._request.channel_name} {self._request.cookie} :End Of GETCKEY.\r\n" - - -class JoinResponse(ResponseBase): - _request: JoinRequest - _result: JoinResult - - def __init__(self, request: JoinRequest, result: JoinResult) -> None: - assert isinstance(request, JoinRequest) - assert isinstance(result, JoinResult) - super().__init__(request, result) - - def build(self) -> None: - self.sending_buffer = ( - f"{self._result.joiner_prefix} {JOIN} {self._request.channel_name}\r\n" - ) - - -class KickResponse(ResponseBase): - _request: KickRequest - _result: KickResult - - def __init__(self, request: KickRequest, result: KickResult) -> None: - assert isinstance(request, KickRequest) - assert isinstance(result, KickResult) - super().__init__(request, result) - - def build(self) -> None: - self.sending_buffer = f"{self._result.kicker_irc_prefix} {KICK} {self._result.channel_name} {self._result.kickee_nick_name} :{self._request.reason}\r\n" - - -class ModeResponse(ResponseBase): - _request: ModeRequest - _result: ModeResult - - def __init__(self, request: ModeRequest, result: ModeResult) -> None: - assert isinstance(request, ModeRequest) - assert isinstance(result, ModeResult) - super().__init__(request, result) - - def build(self) -> None: - if self._request.request_type == ModeRequestType.GET_CHANNEL_MODES: - self.sending_buffer = f":{SERVER_DOMAIN} {CHANNEL_MODELS} * {self._result.channel_modes} {self._result.channel_modes}\r\n" - - -class NamesResponse(ChannelResponseBase): - _request: NamesRequest - _result: NamesResult - - def __init__(self, request: NamesRequest, result: NamesResult) -> None: - assert isinstance(request, NamesRequest) - assert isinstance(result, NamesResult) - super().__init__(request, result) - - def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {NAME_REPLY} {self._result.requester_nick_name} = {self._result.channel_name} :{self._result.all_channel_user_nicks}\r\n" - - self.sending_buffer = f":{SERVER_DOMAIN} {END_OF_NAMES} {self._result.requester_nick_name} {self._result.channel_name} :End of NAMES list. \r\n" - - -class PartResponse(ResponseBase): - _request: PartRequest - _result: PartResult - - def __init__(self, request: PartRequest, result: PartResult) -> None: - assert isinstance(request, PartRequest) - assert isinstance(result, PartResult) - super().__init__(request, result) - - def build(self) -> None: - self.sending_buffer = f":{self._result.leaver_irc_prefix} {PART} {self._result.channel_name} :{self._request.reason}\r\n" - - -class SetChannelKeyResponse(ChannelResponseBase): - _request: SetChannelKeyRequest - _result: SetChannelKeyResult - - def __init__( - self, request: SetChannelKeyRequest, result: SetChannelKeyResult - ) -> None: - assert isinstance(request, SetChannelKeyRequest) - assert isinstance(result, SetChannelKeyResult) - super().__init__(request, result) - - def build(self) -> None: - self.sending_buffer = f":{self._result.channel_user_irc_prefix} {GET_CHAN_KEY} * {self._request.channel_name} BCAST {self._request.key_value_string}\r\n" - - -class SetCKeyResponse(ResponseBase): - _request: SetCKeyRequest - - def __init__(self, request: SetCKeyRequest) -> None: - assert isinstance(request, SetCKeyRequest) - super().__init__(request, None) - - def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {GET_CKEY} * {self._request.channel_name} {self._request.nick_name} {self._request.cookie} {self._request.key_value_string}\r\n" - - self.sending_buffer = f":{SERVER_DOMAIN} {END_GET_CKEY} * {self._request.channel_name} {self._request.nick_name} {self._request.cookie} :End Of SETCKEY.\r\n" - - -class TopicResponse(ResponseBase): - _request: TopicRequest - _result: TopicResult - - def __init__(self, request: TopicRequest, result: TopicResult) -> None: - assert isinstance(request, TopicRequest) - assert isinstance(result, TopicResult) - super().__init__(request, result) - - def build(self) -> None: - if self._result.channel_topic == "" or self._result.channel_topic is None: - self.sending_buffer = ( - f":{SERVER_DOMAIN} {NO_TOPIC} * {self._result.channel_name}\r\n" - ) - else: - self.sending_buffer = f":{SERVER_DOMAIN} {TOPIC} * {self._result.channel_name} :{self._result.channel_topic}\r\n" diff --git a/src/servers/chat/src/contracts/responses/general.py b/src/servers/chat/src/contracts/responses/general.py deleted file mode 100644 index 498106c01..000000000 --- a/src/servers/chat/src/contracts/responses/general.py +++ /dev/null @@ -1,158 +0,0 @@ -from library.src.abstractions.contracts import RequestBase, ResultBase -from library.src.encryption.gs_encryption import CLIENT_KEY, SERVER_KEY -from servers.chat.src.abstractions.contract import SERVER_DOMAIN, ResponseBase -from servers.chat.src.aggregates.response_name import * -from servers.chat.src.contracts.requests.general import GetKeyRequest, WhoRequest -from servers.chat.src.contracts.results.general import ( - GetKeyResult, - ListResult, - LoginResult, - NickResult, - PingResult, - UserIPResult, - WhoIsResult, - WhoResult, -) -from servers.chat.src.enums.general import WhoRequestType - - -class CdKeyResponse(ResponseBase): - def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {CD_KEY} * 1 :Authenticated.\r\n" # noqa - - -class CryptResponse(ResponseBase): - def __init__(self) -> None: - pass - - def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {SECURE_KEY} * {CLIENT_KEY} {SERVER_KEY}\r\n" # noqa - - -class GetKeyResponse(ResponseBase): - _request: GetKeyRequest - _result: GetKeyResult - - def __init__(self, request: GetKeyRequest, result: GetKeyResult) -> None: - assert isinstance(request, GetKeyRequest) - assert isinstance(result, GetKeyResult) - super().__init__(request, result) - - def build(self) -> None: - self.sending_buffer = "" - - for value in self._result.values: - self.sending_buffer += f":{SERVER_DOMAIN} {GET_KEY} * {self._result.nick_name} {self._request.cookie} {value}\r\n" # noqa - - self.sending_buffer += f":{SERVER_DOMAIN} {END_GET_KEY} * {self._request.cookie} * :End Of GETKEY.\r\n" # noqa - - -class ListResponse(ResponseBase): - _result: ListResult - - def __init__(self, result: ListResult) -> None: - assert isinstance(result, ListResult) - self._result = result - - def build(self) -> None: - self.sending_buffer = "" - for ( - channel_name, - total_channel_user, - channel_topic, - ) in self._result.channel_info_list: - self.sending_buffer += f":{self._result.user_irc_prefix} {LIST_START} * {channel_name} {total_channel_user} {channel_topic}\r\n" # noqa - self.sending_buffer += f":{self._result.user_irc_prefix} {LIST_END}\r\n" # noqa - - -class LoginResponse(ResponseBase): - _result: LoginResult - - def __init__(self, result: LoginResult) -> None: - assert isinstance(result, LoginResult) - self._result = result - - def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {LOGIN} * {self._result.user_id} {self._result.profile_id}\r\n" # noqa - - -class NickResponse(ResponseBase): - _result: NickResult - - def __init__(self, result: NickResult) -> None: - assert isinstance(result, NickResult) - self._result = result - - def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {WELCOME} {self._result.nick_name} :Welcome to UniSpy!\r\n" # noqa - - -class PingResponse(ResponseBase): - _result: PingResult - - def __init__(self, result: PingResult) -> None: - assert isinstance(result, PingResult) - self._result = result - - def build(self) -> None: - self.sending_buffer = f":{ - self._result.requester_irc_prefix} {PONG}\r\n" - - -class UserIPResponse(ResponseBase): - _result: UserIPResult - - def __init__(self, result: UserIPResult) -> None: - assert isinstance(result, UserIPResult) - self._result = result - - def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {USER_IP} :@{self._result.remote_ip_address}\r\n" # noqa - - -class WhoIsResponse(ResponseBase): - _result: WhoIsResult - - def __init__(self, result: WhoIsResult) -> None: - assert isinstance(result, UserIPResult) - self._result = result - - def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {WHO_IS_USER} {self._result.nick_name} {self._result.name} {self._result.user_name} {self._result.public_ip_address} * :{self._result.user_name}\r\n" # noqa - - if len(self._result.joined_channel_name) != 0: - channel_name = "" - for name in self._result.joined_channel_name: - channel_name += f"@{name} " # noqa - - self.sending_buffer += f":{SERVER_DOMAIN} {WHO_IS_CHANNELS} {self._result.nick_name} {self._result.name} :{channel_name}\r\n" # noqa - - self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO_IS} {self._result.nick_name} {self._result.name} :End of WHOIS list. \r\n" # noqa - - -class WhoResponse(ResponseBase): - _request: WhoRequest - _result: WhoResult - - def __init__(self, request: WhoRequest, result: WhoResult) -> None: - assert isinstance(request, WhoRequest) - assert isinstance(result, WhoResult) - super().__init__(request, result) - - def build(self): - self.sending_buffer = "" - for ( - channel_name, - user_name, - public_ip_address, - nick_name, - modes, - ) in self._result.infos: - self.sending_buffer += f":{SERVER_DOMAIN} {WHO_REPLY} * {channel_name} {user_name} {public_ip_address} * {nick_name} {modes} *\r\n" # noqa - - if self._request.request_type == WhoRequestType.GET_CHANNEL_USER_INFO: - if len(self._result.infos) > 0: - self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.channel_name} * :End of WHO.\r\n" # noqa - elif self._request.request_type == WhoRequestType.GET_USER_INFO: - if len(self._result.infos) > 0: - self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.nick_name} * :End of WHO.\r\n" # noqa diff --git a/src/servers/chat/src/contracts/responses/message.py b/src/servers/chat/src/contracts/responses/message.py deleted file mode 100644 index 8fa6c0663..000000000 --- a/src/servers/chat/src/contracts/responses/message.py +++ /dev/null @@ -1,68 +0,0 @@ -from library.src.abstractions.contracts import RequestBase, ResultBase -from servers.chat.src.abstractions.contract import ResponseBase -from servers.chat.src.aggregates.response_name import * -from servers.chat.src.contracts.requests.message import ( - ATMRequest, - NoticeRequest, - PrivateRequest, - UTMRequest, -) -from servers.chat.src.contracts.results.message import ( - ATMResult, - NoticeResult, - PrivateResult, - UTMResult, -) - - -class ATMResponse(ResponseBase): - _request: ATMRequest - _result: ATMResult - - def __init__(self, request: ATMRequest, result: ATMResult) -> None: - assert isinstance(request, ATMRequest) - assert isinstance(result, ATMResult) - super().__init__(request, result) - - def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} {ABOVE_THE_TABLE_MSG} {self._result.target_name} :{self._request.message}\r\n" - - -class NoticeResponse(ResponseBase): - _request: NoticeRequest - _result: NoticeResult - - def __init__(self, request: NoticeRequest, result: NoticeResult) -> None: - assert isinstance(result, NoticeResult) - assert isinstance(request, NoticeRequest) - - super().__init__(request, result) - - def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} {NOTICE} {self._result.target_name} {self._request.message}\r\n" - - -class PrivateResponse(ResponseBase): - _request: PrivateRequest - _result: PrivateResult - - def __init__(self, request: PrivateRequest, result: PrivateResult) -> None: - assert isinstance(result, PrivateRequest) - assert isinstance(request, PrivateResult) - super().__init__(request, result) - - def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} {PRIVATE_MSG} {self._result.target_name} :{self._request.message}\r\n" - - -class UTMResponse(ResponseBase): - _request: UTMRequest - _result: UTMResult - - def __init__(self, request: UTMRequest, result: UTMResult) -> None: - assert isinstance(request, UTMRequest) - assert isinstance(result, UTMResult) - super().__init__(request, result) - - def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} {UNDER_THE_TABLE_MSG} {self._result.target_name} :{self._request.message}" diff --git a/src/servers/chat/src/contracts/results.py b/src/servers/chat/src/contracts/results.py new file mode 100644 index 000000000..f4ce9e799 --- /dev/null +++ b/src/servers/chat/src/contracts/results.py @@ -0,0 +1,172 @@ +from typing import List, Tuple + +from pydantic import BaseModel +from servers.chat.src.abstractions.contract import ResultBase + +# region General + + +class CryptResult(ResultBase): + pass + + +class GetKeyResult(ResultBase): + nick_name: str + values: list = [] + + +class ListResult(ResultBase): + class ListInfo(BaseModel): + channel_name: str + total_channel_user: int + channel_topic: str + user_irc_prefix: str + channel_info_list: list[ListInfo] = [] + """(channel_name:str,total_channel_user:int,channel_topic:str)""" + + +class LoginResult(ResultBase): + profile_id: int + user_id: int + + +class NickResult(ResultBase): + nick_name: str + + +class PingResult(ResultBase): + requester_irc_prefix: str + + +class QuitResult(ResultBase): + class QuitInfo(BaseModel): + channel_name: str + is_peer_server: bool + is_channel_operator: bool + leave_reply_sending_buffer: str + kick_replay_sending_buffer: str + + quiter_prefix: str + channel_infos: list[QuitInfo] + # (channel_name: str, is_peer_server: bool, is_channel_operator: bool,leave_reply_sending_buffer: str,kick_replay_sending_buffer: str) + message: str + + +class UserIPResult(ResultBase): + remote_ip_address: str + + +class WhoIsResult(ResultBase): + nick_name: str + user_name: str + name: str + public_ip_address: str + joined_channel_name: list[str] = [] + + +class WhoResult(ResultBase): + class WhoInfo(BaseModel): + channel_name: str + user_name: str + public_ip_addr: str + nick_name: str + modes: str + infos: list[WhoInfo] + + +# region Channel + + +class GetChannelKeyResult(ResultBase): + channel_user_irc_prefix: str + channel_name: str + values: str + + +class GetCKeyResult(ResultBase): + class GetCKeyInfos(BaseModel): + nick_name: str + user_values: str + + infos: list[GetCKeyInfos] + """ nick_name:str, user_values:str""" + channel_name: str + + +class JoinResult(ResultBase): + joiner_prefix: str + joiner_nick_name: str + all_channel_user_nicks: str + channel_modes: str + + +class KickResult(ResultBase): + channel_name: str + kicker_irc_prefix: str + kicker_nick_name: str + kickee_nick_name: str + + +class ModeResult(ResultBase): + channel_name: str + channel_modes: str + joiner_nick_name: str + + +class NamesResult(ResultBase): + all_channel_user_nicks: str + channel_name: str + requester_nick_name: str + + +class PartResult(ResultBase): + leaver_irc_prefix: str + is_channel_creator: bool + channel_name: str + + +class TopicResult(ResultBase): + channel_name: str + channel_topic: str + + +class SetChannelKeyResult(ResultBase): + channel_user_irc_prefix: str + channel_name: str + + +if __name__ == "__main__": + dd = { + "infos": [ + { + "nick_name": "John", + "user_values": "12345" + }, + { + "nick_name": "Alice", + "user_values": "67890" + } + ], + "channel_name": "example_channel" + } + result = GetCKeyResult(**dd) + pass + +# region Message +from servers.chat.src.abstractions.message import MessageResultBase + + +class ATMResult(MessageResultBase): + pass + + +class NoticeResult(MessageResultBase): + pass + + +class PrivateResult(MessageResultBase): + is_broadcast_message: bool = False + + +class UTMResult(MessageResultBase): + pass diff --git a/src/servers/chat/src/contracts/results/__init__.py b/src/servers/chat/src/contracts/results/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/chat/src/contracts/results/channel.py b/src/servers/chat/src/contracts/results/channel.py deleted file mode 100644 index ebfc7d915..000000000 --- a/src/servers/chat/src/contracts/results/channel.py +++ /dev/null @@ -1,78 +0,0 @@ -from pydantic import BaseModel -from servers.chat.src.abstractions.contract import ResultBase - - -class GetChannelKeyResult(ResultBase): - channel_user_irc_prefix: str - channel_name: str - values: str - - -class GetCKeyResult(ResultBase): - class GetCKeyInfos(BaseModel): - nick_name: str - user_values: str - - infos: list[GetCKeyInfos] - """ nick_name:str, user_values:str""" - channel_name: str - - -class JoinResult(ResultBase): - joiner_prefix: str - joiner_nick_name: str - all_channel_user_nicks: str - channel_modes: str - - -class KickResult(ResultBase): - channel_name: str - kicker_irc_prefix: str - kicker_nick_name: str - kickee_nick_name: str - - -class ModeResult(ResultBase): - channel_name: str - channel_modes: str - joiner_nick_name: str - - -class NamesResult(ResultBase): - all_channel_user_nicks: str - channel_name: str - requester_nick_name: str - - -class PartResult(ResultBase): - leaver_irc_prefix: str - is_channel_creator: bool - channel_name: str - - -class TopicResult(ResultBase): - channel_name: str - channel_topic: str - - -class SetChannelKeyResult(ResultBase): - channel_user_irc_prefix: str - channel_name: str - - -if __name__ == "__main__": - dd = { - "infos": [ - { - "nick_name": "John", - "user_values": "12345" - }, - { - "nick_name": "Alice", - "user_values": "67890" - } - ], - "channel_name": "example_channel" - } - result = GetCKeyResult(**dd) - pass diff --git a/src/servers/chat/src/contracts/results/general.py b/src/servers/chat/src/contracts/results/general.py deleted file mode 100644 index 966a5bf6a..000000000 --- a/src/servers/chat/src/contracts/results/general.py +++ /dev/null @@ -1,72 +0,0 @@ -from typing import List, Tuple - -from pydantic import BaseModel -from servers.chat.src.abstractions.contract import ResultBase - - -class CryptResult(ResultBase): - pass - - -class GetKeyResult(ResultBase): - nick_name: str - values: list = [] - - -class ListResult(ResultBase): - class ListInfo(BaseModel): - channel_name: str - total_channel_user: int - channel_topic: str - user_irc_prefix: str - channel_info_list: list[ListInfo] = [] - """(channel_name:str,total_channel_user:int,channel_topic:str)""" - - -class LoginResult(ResultBase): - profile_id: int - user_id: int - - -class NickResult(ResultBase): - nick_name: str - - -class PingResult(ResultBase): - requester_irc_prefix: str - - -class QuitResult(ResultBase): - class QuitInfo(BaseModel): - channel_name: str - is_peer_server: bool - is_channel_operator: bool - leave_reply_sending_buffer: str - kick_replay_sending_buffer: str - - quiter_prefix: str - channel_infos: list[QuitInfo] - # (channel_name: str, is_peer_server: bool, is_channel_operator: bool,leave_reply_sending_buffer: str,kick_replay_sending_buffer: str) - message: str - - -class UserIPResult(ResultBase): - remote_ip_address: str - - -class WhoIsResult(ResultBase): - nick_name: str - user_name: str - name: str - public_ip_address: str - joined_channel_name: list[str] = [] - - -class WhoResult(ResultBase): - class WhoInfo(BaseModel): - channel_name: str - user_name: str - public_ip_addr: str - nick_name: str - modes: str - infos: list[WhoInfo] diff --git a/src/servers/chat/src/contracts/results/message.py b/src/servers/chat/src/contracts/results/message.py deleted file mode 100644 index 35c4c81c4..000000000 --- a/src/servers/chat/src/contracts/results/message.py +++ /dev/null @@ -1,17 +0,0 @@ -from servers.chat.src.abstractions.message import MessageResultBase - - -class ATMResult(MessageResultBase): - pass - - -class NoticeResult(MessageResultBase): - pass - - -class PrivateResult(MessageResultBase): - is_broadcast_message: bool = False - - -class UTMResult(MessageResultBase): - pass diff --git a/src/servers/chat/src/enums/irc_error_code.py b/src/servers/chat/src/enums/irc_error_code.py deleted file mode 100644 index 48d3e7e0c..000000000 --- a/src/servers/chat/src/enums/irc_error_code.py +++ /dev/null @@ -1,19 +0,0 @@ -from enum import Enum - - -class IRCErrorCode(Enum): - NO_SUCH_NICK = "401" - NO_SUCH_CHANNEL = "403" - TOO_MANY_CHANNELS = "405" - ERR_ONE_US_NICK_NAME = "432" - NICK_NAME_IN_USE = "433" - MORE_PARAMETERS = "461" - CHANNEL_IS_FULL = "471" - INVITE_ONLY_CHAN = "473" - BANNED_FROM_CHAN = "474" - BAD_CHANNEL_KEY = "475" - BAD_CHAN_MASK = "476" - LOGIN_FAILED = "708" - NO_UNIQUE_NICK = "709" - UNIQUE_NICK_EXPIRED = "710" - REGISTER_NICK_FAILED = "711" diff --git a/src/servers/chat/src/enums/peer_room.py b/src/servers/chat/src/enums/peer_room.py deleted file mode 100644 index 421fb05a2..000000000 --- a/src/servers/chat/src/enums/peer_room.py +++ /dev/null @@ -1,8 +0,0 @@ -from enum import IntEnum - - -class PeerRoomType(IntEnum): - Group = 0 - Staging = 1 - Title = 2 - Normal = 3 diff --git a/src/servers/chat/src/exceptions/channel.py b/src/servers/chat/src/exceptions/channel.py deleted file mode 100644 index 9fbf4270a..000000000 --- a/src/servers/chat/src/exceptions/channel.py +++ /dev/null @@ -1,44 +0,0 @@ -from servers.chat.src.enums.irc_error_code import IRCErrorCode -from servers.chat.src.exceptions.general import IRCChannelException - - -class BadChannelMaskException(IRCChannelException): - def __init__( - self, message: str, error_code: IRCErrorCode = IRCErrorCode.BAD_CHAN_MASK - ) -> None: - super().__init__(message, error_code) - - -class BadChannelKeyException(IRCChannelException): - def __init__( - self, message: str, error_code: IRCErrorCode = IRCErrorCode.BAD_CHANNEL_KEY - ) -> None: - super().__init__(message, error_code) - - -class BannedFromChanException(IRCChannelException): - def __init__( - self, message: str, error_code: IRCErrorCode = IRCErrorCode.BANNED_FROM_CHAN - ) -> None: - super().__init__(message, error_code) - - -class ChannelIsFullException(IRCChannelException): - def __init__( - self, message: str, error_code: IRCErrorCode = IRCErrorCode.CHANNEL_IS_FULL - ) -> None: - super().__init__(message, error_code) - - -class InviteOnlyChanException(IRCChannelException): - def __init__( - self, message: str, error_code: IRCErrorCode = IRCErrorCode.INVITE_ONLY_CHAN - ) -> None: - super().__init__(message, error_code) - - -class NoSuchChannelException(IRCChannelException): - def __init__( - self, message: str, error_code: IRCErrorCode = IRCErrorCode.NO_SUCH_CHANNEL - ) -> None: - super().__init__(message, error_code) diff --git a/src/servers/chat/src/handlers/channel.py b/src/servers/chat/src/handlers/channel.py deleted file mode 100644 index a9d84bcbd..000000000 --- a/src/servers/chat/src/handlers/channel.py +++ /dev/null @@ -1,242 +0,0 @@ -from library.src.abstractions.client import ClientBase -from servers.chat.src.abstractions.channel import ChannelHandlerBase -from servers.chat.src.aggregates.channel import Channel -from servers.chat.src.aggregates.channel_user import ChannelUser -from servers.chat.src.aggregates.managers import ChannelManager -from servers.chat.src.aggregates.response_name import * -from servers.chat.src.contracts.requests.channel import ( - GetCKeyRequest, - GetChannelKeyRequest, - JoinRequest, - KickRequest, - ModeRequest, - NamesRequest, - PartRequest, - SetCKeyRequest, - SetChannelKeyRequest, - TopicRequest, -) -from servers.chat.src.contracts.responses.channel import ( - GetCKeyResponse, - JoinResponse, - KickResponse, - ModeResponse, - NamesResponse, - PartResponse, - SetCKeyResponse, - SetChannelKeyResponse, - TopicResponse, -) -from servers.chat.src.contracts.results.channel import ( - GetCKeyResult, - GetChannelKeyResult, - JoinResult, - KickResult, - ModeResult, - NamesResult, - PartResult, - SetChannelKeyResult, - TopicResult, -) -from servers.chat.src.enums.general import ModeRequestType, TopicRequestType -from servers.chat.src.exceptions.general import ChatException - - -class GetChannelKeyHandler(ChannelHandlerBase): - _request: GetChannelKeyRequest - _result: GetChannelKeyResult - - def __init__(self, client: ClientBase, request: GetChannelKeyRequest): - assert isinstance(request, GetChannelKeyRequest) - self._is_fetching_data = True - super().__init__(client, request) - - def _publish_message(self): - pass - - def _update_channel_cache(self): - pass - - -class GetCKeyHandler(ChannelHandlerBase): - _request: GetCKeyRequest - _result: GetCKeyResult - - def __init__(self, client: ClientBase, request: GetCKeyRequest): - assert isinstance(request, GetCKeyRequest) - super().__init__(client, request) - - def _publish_message(self): - pass - - def _update_channel_cache(self): - pass - - def _response_construct(self): - self._response = GetCKeyResponse(self._request, self._result) - - -class JoinHandler(ChannelHandlerBase): - _request: JoinRequest - _result: JoinResult - - def __init__(self, client: ClientBase, request: JoinRequest): - assert isinstance(request, JoinRequest) - super().__init__(client, request) - - def _check_user_in_remote(self): - """ - todo maybe do not need because there are nick handler? - """ - pass - - def _check_user_in_local(self): - channel = ChannelManager.get_channel( - self._request.channel_name) - if channel is None: - self._channel = Channel( - self._request.channel_name, self._client, self._request.password) - ChannelManager.add_channel(self._channel) - else: - self._channel = channel - if self._client.info.nick_name in self._channel.users: - raise ChatException("user is already in channel") - - def _request_check(self) -> None: - # todo check if user already in local channel - # self._check_user_in_remote() - self._check_user_in_local() - self._user = ChannelUser(self._client, self._channel) - self._channel.add_bind_on_user_and_channel(self._user) - - super()._request_check() - - def _response_construct(self): - self._response = JoinResponse(self._request, self._result) - - def _response_send(self): - # for join request we need to send to our self - self._channel.multicast(self._user.client, self._response, False) - - -class KickHandler(ChannelHandlerBase): - _request: KickRequest - _result: KickResult - - def __init__(self, client: ClientBase, request: KickRequest): - assert isinstance(request, KickRequest) - super().__init__(client, request) - - def _response_construct(self): - self._response = KickResponse(self._request, self._result) - - def _response_send(self): - self._channel.multicast(self._user.client, self._response, True) - - -class ModeHandler(ChannelHandlerBase): - _request: ModeRequest - _result: ModeResult - - def __init__(self, client: ClientBase, request: ModeRequest): - assert isinstance(request, ModeRequest) - super().__init__(client, request) - - def _response_construct(self): - match self._request.request_type: - case ( - ModeRequestType.GET_CHANNEL_AND_USER_MODES, - ModeRequestType.GET_CHANNEL_MODES, - ): - self._response = ModeResponse(self._request, self._result) - case ModeRequestType.SET_CHANNEL_MODES: - pass - case _: - raise ChatException("Unknown mode request type") - - def _publish_message(self): - if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: - super()._publish_message() - - def _update_channel_cache(self): - if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: - super()._update_channel_cache() - - def _response_send(self): - self._channel.multicast(self._user.client, self._response, True) - - -class NamesHandler(ChannelHandlerBase): - _request: NamesRequest - _result: NamesResult - - def __init__(self, client: ClientBase, request: NamesRequest): - assert isinstance(request, NamesRequest) - self._is_fetching_data = True - super().__init__(client, request) - - def _response_construct(self): - self._response = NamesResponse(self._request, self._result) - - -class PartHandler(ChannelHandlerBase): - _request: PartRequest - _result: PartResult - - def __init__(self, client: ClientBase, request: PartRequest): - assert isinstance(request, PartRequest) - super().__init__(client, request) - - def _response_construct(self): - self._response = PartResponse(self._request, self._result) - - def _response_send(self): - self._channel.multicast(self._user.client, self._response, True) - - -class SetChannelKeyHandler(ChannelHandlerBase): - _request: SetChannelKeyRequest - _result: SetChannelKeyResult - - def __init__(self, client: ClientBase, request: SetChannelKeyRequest): - assert isinstance(self._request, SetChannelKeyRequest) - super().__init__(client, request) - - def _response_construct(self): - self._response = SetChannelKeyResponse(self._request, self._result) - - def _response_send(self): - self._channel.multicast(self._user.client, self._response, True) - - -class SetCKeyHandler(ChannelHandlerBase): - _request: SetCKeyRequest - - def __init__(self, client: ClientBase, request: SetCKeyRequest): - assert isinstance(request, SetCKeyRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = SetCKeyResponse(self._request) - - def _response_send(self): - self._channel.multicast(self._user.client, self._response, True) - - -class TopicHandler(ChannelHandlerBase): - _request: TopicRequest - _result: TopicResult - - def __init__(self, client: ClientBase, request: TopicRequest): - assert isinstance(request, TopicRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = TopicResponse(self._request, self._result) - - def _response_send(self) -> None: - match self._request.request_type: - case TopicRequestType.GET_CHANNEL_TOPIC: - self._client.send(self._response) - case TopicRequestType.SET_CHANNEL_TOPIC: - self._channel.multicast(self._client, self._response) diff --git a/src/servers/chat/src/handlers/general.py b/src/servers/chat/src/handlers/general.py deleted file mode 100644 index 2f5992c6a..000000000 --- a/src/servers/chat/src/handlers/general.py +++ /dev/null @@ -1,211 +0,0 @@ -from typing import Type -from library.src.abstractions.client import ClientBase -from servers.chat.src.abstractions.contract import RequestBase -from servers.chat.src.abstractions.handler import CmdHandlerBase, PostLoginHandlerBase -from servers.chat.src.applications.client import Client -from servers.chat.src.contracts.requests.channel import GetUdpRelayRequest -from servers.chat.src.contracts.requests.general import ( - CdkeyRequest, - CryptRequest, - GetKeyRequest, - InviteRequest, - ListRequest, - LoginRequest, - NickRequest, - PingRequest, - QuitRequest, - SetKeyRequest, - UserIPRequest, - UserRequest, - WhoIsRequest, - WhoRequest, -) -from servers.chat.src.contracts.responses.general import ( - CdKeyResponse, - CryptResponse, - GetKeyResponse, - ListResponse, - LoginResponse, - NickResponse, - PingResponse, - UserIPResponse, - WhoIsResponse, - WhoResponse, -) -from servers.chat.src.contracts.results.general import ( - CryptResult, - GetKeyResult, - ListResult, - LoginResult, - NickResult, - PingResult, - UserIPResult, - WhoIsResult, - WhoResult, -) - - -class CdKeyHandler(CmdHandlerBase): - _request: CdkeyRequest - - def __init__(self, client: ClientBase, request: CdkeyRequest): - assert isinstance(request, CdkeyRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = CdKeyResponse(self._request, self._result) - - -class CryptHandler(CmdHandlerBase): - _request: CryptRequest - _result: CryptResult - - def __init__(self, client: ClientBase, request: RequestBase): - assert isinstance(request, CryptRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = CryptResponse() - - -class GetKeyHandler(CmdHandlerBase): - _request: GetKeyRequest - _result: GetKeyResult - - def __init__(self, client: ClientBase, request: GetKeyRequest): - assert isinstance(request, GetKeyRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = GetKeyResponse(self._request, self._result) - - -class GetUdpRelayHandler(CmdHandlerBase): - _request: GetUdpRelayRequest - - def __init__(self, client: ClientBase, request: GetUdpRelayRequest): - assert isinstance(request, GetUdpRelayRequest) - super().__init__(client, request) - - -class InviteHandler(CmdHandlerBase): - _request: InviteRequest - - def __init__(self, client: ClientBase, request: InviteRequest): - assert isinstance(request, InviteRequest) - super().__init__(client, request) - - -class ListHandler(PostLoginHandlerBase): - _request: ListRequest - _result: ListResult - - def __init__(self, client: ClientBase, request: ListRequest): - assert isinstance(request, ListRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = ListResponse(self._result) - - -class LoginHandler(CmdHandlerBase): - _request: LoginRequest - _result: LoginResult - - def __init__(self, client: ClientBase, request: LoginRequest): - assert isinstance(request, LoginRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = LoginResponse(self._result) - - -class NickHandler(CmdHandlerBase): - _request: NickRequest - _result: NickResult - - def __init__(self, client: ClientBase, request: NickRequest): - assert isinstance(request, NickRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = NickResponse(self._result) - - -class PingHandler(CmdHandlerBase): - _request: PingRequest - _result: PingResult - - def __init__(self, client: ClientBase, request: PingRequest): - assert isinstance(request, PingRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = PingResponse(self._result) - - -class QuitHandler(CmdHandlerBase): - _request: QuitRequest - - def __init__(self, client: ClientBase, request: QuitRequest): - assert isinstance(request, QuitHandler) - super().__init__(client, request) - - -class SetKeyHandler(PostLoginHandlerBase): - _request: SetKeyRequest - - def __init__(self, client: ClientBase, request: SetKeyRequest): - assert isinstance(request, SetKeyRequest) - super().__init__(client, request) - - -class UserHandler(CmdHandlerBase): - _request: UserRequest - - def __init__(self, client: ClientBase, request: UserRequest): - assert isinstance(request, UserRequest) - super().__init__(client, request) - - -class UserIPHandler(CmdHandlerBase): - _request: UserIPRequest - _result: UserIPResult - - def __init__(self, client: ClientBase, request: UserIPRequest): - assert isinstance(request, UserIPRequest) - super().__init__(client, request) - - def _request_check(self) -> None: - super()._request_check() - self._request.remote_ip_address = ( - f"{self._client.connection.remote_ip}:{self._client.connection.remote_port}" - ) - - def _response_construct(self) -> None: - self._response = UserIPResponse(self._result) - - -class WhoHandler(CmdHandlerBase): - _request: WhoRequest - _result: WhoResult - - def __init__(self, client: ClientBase, request: WhoRequest): - assert isinstance(request, WhoRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = WhoResponse(self._request, self._result) - - -class WhoIsHandler(CmdHandlerBase): - _request: WhoIsRequest - _result: WhoIsResult - _result_type: Type = WhoIsResult - - def __init__(self, client: ClientBase, request: WhoIsRequest): - assert isinstance(request, WhoIsRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = WhoIsResponse(self._result) diff --git a/src/servers/chat/src/handlers/message.py b/src/servers/chat/src/handlers/message.py deleted file mode 100644 index dffde5c72..000000000 --- a/src/servers/chat/src/handlers/message.py +++ /dev/null @@ -1,68 +0,0 @@ -from library.src.abstractions.client import ClientBase -from servers.chat.src.abstractions.message import MessageHandlerBase, MessageRequestBase -from servers.chat.src.contracts.requests.message import ( - ATMRequest, - NoticeRequest, - PrivateRequest, - UTMRequest, -) -from servers.chat.src.contracts.responses.message import ( - ATMResponse, - NoticeResponse, - PrivateResponse, - UTMResponse, -) -from servers.chat.src.contracts.results.message import ( - ATMResult, - NoticeResult, - PrivateResult, - UTMResult, -) - - -class ATMHandler(MessageHandlerBase): - _request: ATMRequest - _result: ATMResult - - def __init__(self, client: ClientBase, request: ATMRequest): - assert isinstance(request, ATMRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = ATMResponse(self._request, self._result) - - -class UTMHandler(MessageHandlerBase): - _request: UTMRequest - _result: UTMResult - - def __init__(self, client: ClientBase, request: UTMRequest): - assert isinstance(request, UTMRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = UTMResponse(self._request, self._result) - - -class NoticeHandler(MessageHandlerBase): - _request: NoticeRequest - _result: NoticeResult - - def __init__(self, client: ClientBase, request: NoticeRequest): - assert isinstance(request, NoticeRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = NoticeResponse(self._request, self._result) - - -class PrivateHandler(MessageHandlerBase): - _request: PrivateRequest - _result: PrivateResult - - def __init__(self, client: ClientBase, request: PrivateRequest): - assert isinstance(request, PrivateRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = PrivateResponse(self._request, self._result) diff --git a/src/servers/game_status/src/abstractions/contracts.py b/src/servers/game_status/src/abstractions/contracts.py index 7700a54de..c2e91f20d 100644 --- a/src/servers/game_status/src/abstractions/contracts.py +++ b/src/servers/game_status/src/abstractions/contracts.py @@ -1,7 +1,7 @@ from typing import Optional import library.src.abstractions.contracts from library.src.extentions.gamespy_utils import convert_to_key_value -from servers.game_status.src.exceptions.general import GSException +from servers.game_status.src.aggregations.exceptions import GSException class RequestBase(library.src.abstractions.contracts.RequestBase): diff --git a/src/servers/game_status/src/enums/general.py b/src/servers/game_status/src/aggregations/enums.py similarity index 100% rename from src/servers/game_status/src/enums/general.py rename to src/servers/game_status/src/aggregations/enums.py diff --git a/src/servers/game_status/src/exceptions/general.py b/src/servers/game_status/src/aggregations/exceptions.py similarity index 100% rename from src/servers/game_status/src/exceptions/general.py rename to src/servers/game_status/src/aggregations/exceptions.py diff --git a/src/servers/game_status/src/aggregations/gscrypt.py b/src/servers/game_status/src/aggregations/gscrypt.py index 9b512aa44..d25a87194 100644 --- a/src/servers/game_status/src/aggregations/gscrypt.py +++ b/src/servers/game_status/src/aggregations/gscrypt.py @@ -2,7 +2,7 @@ from library.src.abstractions.enctypt_base import EncryptBase from library.src.encryption.xor_encryption import XorEncoding, XorType -from servers.game_status.src.exceptions.general import GSException +from servers.game_status.src.aggregations.exceptions import GSException class GSCrypt(EncryptBase): diff --git a/src/servers/game_status/src/applications/client.py b/src/servers/game_status/src/applications/client.py index 4870648c9..ac0ce863b 100644 --- a/src/servers/game_status/src/applications/client.py +++ b/src/servers/game_status/src/applications/client.py @@ -59,5 +59,5 @@ def decrypt_message(self, buffer: bytes) -> bytes: return self.crypto.decrypt(buffer) def create_switcher(self, buffer: bytes) -> SwitcherBase: - from servers.game_status.src.handlers.switcher import Switcher + from servers.game_status.src.applications.switcher import Switcher return Switcher(self, buffer.decode()) diff --git a/src/servers/game_status/src/handlers/handlers.py b/src/servers/game_status/src/applications/handlers.py similarity index 92% rename from src/servers/game_status/src/handlers/handlers.py rename to src/servers/game_status/src/applications/handlers.py index d3d2fd60e..6e598da53 100644 --- a/src/servers/game_status/src/handlers/handlers.py +++ b/src/servers/game_status/src/applications/handlers.py @@ -2,23 +2,22 @@ from servers.game_status.src.applications.client import Client from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest from servers.game_status.src.contracts.responses import AuthGameResponse, AuthPlayerResponse, GetPlayerDataResponse, GetProfileIdResponse -from servers.game_status.src.contracts.results import AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult +from servers.game_status.src.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult class AuthGameHandler(CmdHandlerBase): _request: AuthGameRequest + _result: AuthGameResult def __init__(self, client: Client, request: AuthGameRequest) -> None: - self._is_feaching = False + self._result_cls = AuthGameResult super().__init__(client, request) assert isinstance(request, AuthGameRequest) - def _data_operate(self) -> None: - self._client.info.session_key = "2020" + def _response_construct(self) -> None: + self._client.info.session_key = self._result.session_key self._client.info.game_name = self._request.game_name self._client.info.is_game_authenticated = True - - def _response_construct(self) -> None: self._response = AuthGameResponse(self._request, self._result) @@ -80,6 +79,7 @@ class UpdateGameHandler(CmdHandlerBase): new request "\\updgame\\\\sesskey\\%d\\connid\\%d\\done\\%d\\gamedata\\%s" """ _request: UpdateGameRequest + def __init__(self, client: Client, request: UpdateGameRequest) -> None: super().__init__(client, request) assert isinstance(request, UpdateGameRequest) diff --git a/src/servers/game_status/src/handlers/switcher.py b/src/servers/game_status/src/applications/switcher.py similarity index 91% rename from src/servers/game_status/src/handlers/switcher.py rename to src/servers/game_status/src/applications/switcher.py index 376c9a05a..d12f6e723 100644 --- a/src/servers/game_status/src/handlers/switcher.py +++ b/src/servers/game_status/src/applications/switcher.py @@ -3,7 +3,7 @@ from servers.game_status.src.abstractions.handlers import CmdHandlerBase from servers.game_status.src.applications.client import Client from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest -from servers.game_status.src.handlers.handlers import AuthGameHandler, AuthPlayerHandler, GetPlayerDataHandler, NewGameHandler, SetPlayerDataHandler, UpdateGameHandler +from servers.game_status.src.applications.handlers import AuthGameHandler, AuthPlayerHandler, GetPlayerDataHandler, NewGameHandler, SetPlayerDataHandler, UpdateGameHandler class Switcher(SwitcherBase): diff --git a/src/servers/game_status/src/contracts/requests.py b/src/servers/game_status/src/contracts/requests.py index 4d49b1612..a01e8d5f1 100644 --- a/src/servers/game_status/src/contracts/requests.py +++ b/src/servers/game_status/src/contracts/requests.py @@ -1,8 +1,8 @@ from typing import Optional, final from library.src.extentions.gamespy_utils import convert_to_key_value from servers.game_status.src.abstractions.contracts import RequestBase -from servers.game_status.src.enums.general import AuthMethod, PersistStorageType -from servers.game_status.src.exceptions.general import GSException +from servers.game_status.src.aggregations.enums import AuthMethod, PersistStorageType +from servers.game_status.src.aggregations.exceptions import GSException @final diff --git a/src/servers/game_status/tests/handler_tests.py b/src/servers/game_status/tests/handler_tests.py index 182b65a88..db834c005 100644 --- a/src/servers/game_status/tests/handler_tests.py +++ b/src/servers/game_status/tests/handler_tests.py @@ -6,9 +6,9 @@ from library.tests.mock_objects.general import create_mock_url from servers.game_status.src.aggregations.gscrypt import GSCrypt from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest -from servers.game_status.src.enums.general import PersistStorageType -from servers.game_status.src.handlers.handlers import AuthPlayerHandler, SetPlayerDataHandler, UpdateGameHandler -from servers.game_status.src.handlers.switcher import Switcher +from servers.game_status.src.aggregations.enums import PersistStorageType +from servers.game_status.src.applications.handlers import AuthPlayerHandler, SetPlayerDataHandler, UpdateGameHandler +from servers.game_status.src.applications.switcher import Switcher from servers.game_status.tests.mock_objects import create_client diff --git a/src/servers/game_traffic_relay/src/contracts/general.py b/src/servers/game_traffic_relay/src/contracts/general.py index b656242fe..53a21da4a 100644 --- a/src/servers/game_traffic_relay/src/contracts/general.py +++ b/src/servers/game_traffic_relay/src/contracts/general.py @@ -1,6 +1,6 @@ from pydantic import BaseModel, UUID4 -from servers.natneg.src.enums.general import NatClientIndex, NatPortType +from servers.natneg.src.aggregations.enums import NatClientIndex, NatPortType class InitPacketInfo(BaseModel): diff --git a/src/servers/natneg/src/abstractions/contracts.py b/src/servers/natneg/src/abstractions/contracts.py index dd6d3e00d..2b320c3bd 100644 --- a/src/servers/natneg/src/abstractions/contracts.py +++ b/src/servers/natneg/src/abstractions/contracts.py @@ -2,7 +2,7 @@ import socket from typing import Optional import library.src.abstractions.contracts -from servers.natneg.src.enums.general import ( +from servers.natneg.src.aggregations.enums import ( NatClientIndex, NatPortType, RequestType, diff --git a/src/servers/natneg/src/enums/general.py b/src/servers/natneg/src/aggregations/enums.py similarity index 100% rename from src/servers/natneg/src/enums/general.py rename to src/servers/natneg/src/aggregations/enums.py diff --git a/src/servers/natneg/src/applications/client.py b/src/servers/natneg/src/applications/client.py index d28cb2a38..da599cac1 100644 --- a/src/servers/natneg/src/applications/client.py +++ b/src/servers/natneg/src/applications/client.py @@ -13,6 +13,6 @@ def __init__(self, connection: UdpConnection, server_config: ServerConfig, logge def create_switcher(self, buffer: bytes): assert isinstance(buffer, bytes) - from servers.natneg.src.handlers.switcher import CmdSwitcher + from servers.natneg.src.applications.switcher import CmdSwitcher return CmdSwitcher(self, buffer) diff --git a/src/servers/natneg/src/handlers/handlers.py b/src/servers/natneg/src/applications/handlers.py similarity index 100% rename from src/servers/natneg/src/handlers/handlers.py rename to src/servers/natneg/src/applications/handlers.py diff --git a/src/servers/natneg/src/handlers/switcher.py b/src/servers/natneg/src/applications/switcher.py similarity index 94% rename from src/servers/natneg/src/handlers/switcher.py rename to src/servers/natneg/src/applications/switcher.py index 7fd4b0a37..1bbf4099a 100644 --- a/src/servers/natneg/src/handlers/switcher.py +++ b/src/servers/natneg/src/applications/switcher.py @@ -10,8 +10,8 @@ PingRequest, ReportRequest, ) -from servers.natneg.src.enums.general import RequestType -from servers.natneg.src.handlers.handlers import ( +from servers.natneg.src.aggregations.enums import RequestType +from servers.natneg.src.applications.handlers import ( AddressCheckHandler, ConnectHandler, ErtAckHandler, diff --git a/src/servers/natneg/src/contracts/requests.py b/src/servers/natneg/src/contracts/requests.py index 5a916177c..11e4ad753 100644 --- a/src/servers/natneg/src/contracts/requests.py +++ b/src/servers/natneg/src/contracts/requests.py @@ -3,7 +3,7 @@ # from library.src.extentions.string_extentions import IPEndPoint from servers.natneg.src.abstractions.contracts import CommonRequestBase, RequestBase -from servers.natneg.src.enums.general import ( +from servers.natneg.src.aggregations.enums import ( NatClientIndex, NatPortMappingScheme, NatPortType, diff --git a/src/servers/natneg/src/contracts/results.py b/src/servers/natneg/src/contracts/results.py index b42119dd6..724c86036 100644 --- a/src/servers/natneg/src/contracts/results.py +++ b/src/servers/natneg/src/contracts/results.py @@ -1,6 +1,6 @@ from typing import Any from servers.natneg.src.abstractions.contracts import CommonResultBase, ResultBase -from servers.natneg.src.enums.general import ( +from servers.natneg.src.aggregations.enums import ( ConnectPacketStatus, PreInitState, ResponseType, diff --git a/src/servers/natneg/tests/handler_tests.py b/src/servers/natneg/tests/handler_tests.py index 64dfdd48c..1bf7e64d4 100644 --- a/src/servers/natneg/tests/handler_tests.py +++ b/src/servers/natneg/tests/handler_tests.py @@ -3,7 +3,7 @@ from library.src.abstractions.handler import CmdHandlerBase from library.tests.mock_objects.general import create_mock_url from servers.natneg.src.contracts.requests import InitRequest -from servers.natneg.src.handlers.handlers import AddressCheckHandler, ErtAckHandler, InitHandler, NatifyHandler +from servers.natneg.src.applications.handlers import AddressCheckHandler, ErtAckHandler, InitHandler, NatifyHandler import responses from servers.natneg.src.contracts.requests import ( AddressCheckRequest, @@ -12,7 +12,7 @@ NatifyRequest, PreInitRequest, ) -from servers.natneg.src.enums.general import ( +from servers.natneg.src.aggregations.enums import ( NatClientIndex, NatPortType, PreInitState, diff --git a/src/servers/presence_connection_manager/tests/game_tests.py b/src/servers/presence_connection_manager/tests/game_tests.py index 0ba2c7e50..09ca35087 100644 --- a/src/servers/presence_connection_manager/tests/game_tests.py +++ b/src/servers/presence_connection_manager/tests/game_tests.py @@ -6,6 +6,7 @@ from servers.presence_connection_manager.tests.mock_objects import create_client import responses + class GameTest(unittest.TestCase): @responses.activate def test_civilization_4(self) -> None: @@ -32,6 +33,8 @@ def test_civilization_4(self) -> None: for x in raw_requests: client.on_received(x.encode("ascii")) pass + + @unittest.skip("not finished handler") @responses.activate def test_conflict_global_storm(self) -> None: # "\\lc\\1\\challenge\\NRNUJLZMLX\\id\\1\\final\\", diff --git a/src/servers/presence_search_player/src/applications/client.py b/src/servers/presence_search_player/src/applications/client.py index 0cb246373..6687f8d1d 100644 --- a/src/servers/presence_search_player/src/applications/client.py +++ b/src/servers/presence_search_player/src/applications/client.py @@ -13,9 +13,5 @@ def __init__(self, connection: TcpConnection, server_config: ServerConfig, logge super().__init__(connection, server_config, logger) def _create_switcher(self, buffer) -> SwitcherBase: - from servers.presence_search_player.src.handlers.switcher import CmdSwitcher + from servers.presence_search_player.src.applications.switcher import CmdSwitcher return CmdSwitcher(self, buffer) - - def create_switcher(self, buffer: bytes) -> SwitcherBase: - from servers.presence_search_player.src.handlers.switcher import CmdSwitcher - return CmdSwitcher(self, buffer.decode()) diff --git a/src/servers/presence_search_player/src/handlers/handlers.py b/src/servers/presence_search_player/src/applications/handlers.py similarity index 100% rename from src/servers/presence_search_player/src/handlers/handlers.py rename to src/servers/presence_search_player/src/applications/handlers.py diff --git a/src/servers/presence_search_player/src/handlers/switcher.py b/src/servers/presence_search_player/src/applications/switcher.py similarity index 92% rename from src/servers/presence_search_player/src/handlers/switcher.py rename to src/servers/presence_search_player/src/applications/switcher.py index 2a1aa4c12..0520f0406 100644 --- a/src/servers/presence_search_player/src/handlers/switcher.py +++ b/src/servers/presence_search_player/src/applications/switcher.py @@ -2,7 +2,7 @@ from library.src.abstractions.switcher import SwitcherBase from servers.presence_search_player.src.contracts.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest -from servers.presence_search_player.src.handlers.handlers import CheckHandler, NewUserHandler, NicksHandler, OthersHandler, OthersListHandler, SearchHandler, SearchUniqueHandler, UniqueSearchHandler, ValidHandler +from servers.presence_search_player.src.applications.handlers import CheckHandler, NewUserHandler, NicksHandler, OthersHandler, OthersListHandler, SearchHandler, SearchUniqueHandler, UniqueSearchHandler, ValidHandler from servers.presence_search_player.src.abstractions.handler import CmdHandlerBase diff --git a/src/servers/presence_search_player/src/contracts/__init__.py b/src/servers/presence_search_player/src/contracts/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/presence_search_player/tests/game_tests.py b/src/servers/presence_search_player/tests/game_tests.py index 5cd6bfade..715479ee1 100644 --- a/src/servers/presence_search_player/tests/game_tests.py +++ b/src/servers/presence_search_player/tests/game_tests.py @@ -4,8 +4,8 @@ from library.src.extentions.password_encoder import process_password from library.tests.mock_objects.general import create_mock_url from servers.presence_search_player.src.contracts.requests import CheckRequest -from servers.presence_search_player.src.handlers.handlers import CheckHandler -from servers.presence_search_player.src.handlers.switcher import CmdSwitcher +from servers.presence_search_player.src.applications.handlers import CheckHandler +from servers.presence_search_player.src.applications.switcher import CmdSwitcher import responses from servers.presence_search_player.tests.mock_objects import create_client diff --git a/src/servers/presence_search_player/tests/handler_tests.py b/src/servers/presence_search_player/tests/handler_tests.py index 068a887f6..ab283918d 100644 --- a/src/servers/presence_search_player/tests/handler_tests.py +++ b/src/servers/presence_search_player/tests/handler_tests.py @@ -4,7 +4,7 @@ from library.tests.mock_objects.general import create_mock_url from servers.presence_search_player.src.contracts.requests import SearchRequest -from servers.presence_search_player.src.handlers.handlers import SearchHandler +from servers.presence_search_player.src.applications.handlers import SearchHandler from servers.presence_search_player.tests.mock_objects import create_client @@ -66,7 +66,8 @@ def test_profile(self): "email": "spyguy@unispy.org", "firstname": "spy", "lastname": "guy", "namespace_id": 0}]}) handler = SearchHandler(client, request) handler.handle() - self.assertEqual("\\bsr\\0\\nick\\spyguy\\uniquenick\\spyguy\\namespaceid\\0\\firstname\\spy\\lastname\\guy\\email\\spyguy@unispy.org\\bsrdone\\\\more\\0\\final\\", handler._response.sending_buffer) + self.assertEqual("\\bsr\\0\\nick\\spyguy\\uniquenick\\spyguy\\namespaceid\\0\\firstname\\spy\\lastname\\guy\\email\\spyguy@unispy.org\\bsrdone\\\\more\\0\\final\\", + handler._response.sending_buffer) if __name__ == "__main__": diff --git a/src/servers/query_report/src/exceptions/exceptions.py b/src/servers/query_report/src/aggregates/exceptions.py similarity index 100% rename from src/servers/query_report/src/exceptions/exceptions.py rename to src/servers/query_report/src/aggregates/exceptions.py diff --git a/src/servers/query_report/src/applications/client.py b/src/servers/query_report/src/applications/client.py index febef8beb..52c9ba41a 100644 --- a/src/servers/query_report/src/applications/client.py +++ b/src/servers/query_report/src/applications/client.py @@ -1,7 +1,7 @@ from library.src.abstractions.client import ClientBase # import servers.query_report.v1 -from servers.query_report.src.v2.handlers.switcher import CmdSwitcher as V2CmdSwitcher +from servers.query_report.src.v2.applications.switcher import CmdSwitcher as V2CmdSwitcher class Client(ClientBase): diff --git a/src/servers/query_report/src/v2/abstractions/contracts.py b/src/servers/query_report/src/v2/abstractions/contracts.py index 7565effe3..9e961f5e5 100644 --- a/src/servers/query_report/src/v2/abstractions/contracts.py +++ b/src/servers/query_report/src/v2/abstractions/contracts.py @@ -1,6 +1,6 @@ from servers.query_report.src.v2.aggregates.enums import PacketType import library.src.abstractions.contracts -from servers.query_report.src.exceptions.exceptions import QRException +from servers.query_report.src.aggregates.exceptions import QRException from servers.query_report.src.v2.aggregates.enums import RequestType MAGIC_DATA = [0xFE, 0xFD] diff --git a/src/servers/query_report/src/v2/handlers/handlers.py b/src/servers/query_report/src/v2/applications/handlers.py similarity index 100% rename from src/servers/query_report/src/v2/handlers/handlers.py rename to src/servers/query_report/src/v2/applications/handlers.py diff --git a/src/servers/query_report/src/v2/handlers/switcher.py b/src/servers/query_report/src/v2/applications/switcher.py similarity index 97% rename from src/servers/query_report/src/v2/handlers/switcher.py rename to src/servers/query_report/src/v2/applications/switcher.py index fefe52664..3550b83ee 100644 --- a/src/servers/query_report/src/v2/handlers/switcher.py +++ b/src/servers/query_report/src/v2/applications/switcher.py @@ -14,7 +14,7 @@ KeepAliveRequest, ) from servers.query_report.src.v2.aggregates.enums import RequestType -from servers.query_report.src.v2.handlers.handlers import ( +from servers.query_report.src.v2.applications.handlers import ( AvailableHandler, ChallengeHanler, ClientMessageAckHandler, diff --git a/src/servers/query_report/src/v2/contracts/requests.py b/src/servers/query_report/src/v2/contracts/requests.py index 0afe44047..a26ff3934 100644 --- a/src/servers/query_report/src/v2/contracts/requests.py +++ b/src/servers/query_report/src/v2/contracts/requests.py @@ -2,7 +2,7 @@ from uuid import UUID from library.src.extentions.encoding import get_string from library.src.log.log_manager import LogWriter -from servers.query_report.src.exceptions.exceptions import QRException +from servers.query_report.src.aggregates.exceptions import QRException from servers.query_report.src.v2.abstractions.contracts import RequestBase from servers.query_report.src.v2.aggregates.enums import GameServerStatus diff --git a/src/servers/server_browser/src/exceptions/general.py b/src/servers/server_browser/src/aggregates/exceptions.py similarity index 100% rename from src/servers/server_browser/src/exceptions/general.py rename to src/servers/server_browser/src/aggregates/exceptions.py diff --git a/src/servers/server_browser/src/v2/__init__.py b/src/servers/server_browser/src/v2/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/server_browser/src/v2/applications/handlers.py b/src/servers/server_browser/src/v2/applications/handlers.py index e9fcbea83..d4e644ab9 100644 --- a/src/servers/server_browser/src/v2/applications/handlers.py +++ b/src/servers/server_browser/src/v2/applications/handlers.py @@ -3,7 +3,7 @@ from servers.query_report.src.aggregates.game_server_info import GameServerInfo from servers.query_report.src.v2.contracts.requests import ClientMessageRequest from servers.query_report.src.v2.aggregates.enums import GameServerStatus, RequestType -from servers.server_browser.src.exceptions.general import ServerBrowserException +from servers.server_browser.src.aggregates.exceptions import ServerBrowserException from servers.server_browser.src.v2.contracts.requests import ( SendMessageRequest, ServerInfoRequest, diff --git a/src/servers/web_services/src/exceptions/general.py b/src/servers/web_services/src/aggregations/exceptions.py similarity index 100% rename from src/servers/web_services/src/exceptions/general.py rename to src/servers/web_services/src/aggregations/exceptions.py diff --git a/src/servers/web_services/src/aggregations/soap_envelop.py b/src/servers/web_services/src/aggregations/soap_envelop.py index ce78d93da..d34bef1ed 100644 --- a/src/servers/web_services/src/aggregations/soap_envelop.py +++ b/src/servers/web_services/src/aggregations/soap_envelop.py @@ -1,6 +1,6 @@ import xml.etree.ElementTree as ET -from servers.web_services.src.exceptions.general import WebException +from servers.web_services.src.aggregations.exceptions import WebException class SoapEnvelop: diff --git a/src/servers/web_services/src/applications/client.py b/src/servers/web_services/src/applications/client.py index a9f2ac312..a1f474a42 100644 --- a/src/servers/web_services/src/applications/client.py +++ b/src/servers/web_services/src/applications/client.py @@ -43,5 +43,5 @@ def on_received(self, buffer: str) -> None: def create_switcher(self, buffer: str) -> SwitcherBase: assert isinstance(buffer, str) - from servers.web_services.src.handlers.switcher import Switcher + from servers.web_services.src.applications.switcher import Switcher return Switcher(self, buffer) diff --git a/src/servers/web_services/src/handlers/switcher.py b/src/servers/web_services/src/applications/switcher.py similarity index 96% rename from src/servers/web_services/src/handlers/switcher.py rename to src/servers/web_services/src/applications/switcher.py index 4859f32c1..12da40859 100644 --- a/src/servers/web_services/src/handlers/switcher.py +++ b/src/servers/web_services/src/applications/switcher.py @@ -5,13 +5,13 @@ import xml.etree.ElementTree as ET from servers.web_services.src.applications.client import Client -from servers.web_services.src.exceptions.general import WebException +from servers.web_services.src.aggregations.exceptions import WebException from servers.web_services.src.modules.auth.contracts.requests import LoginProfileRequest, LoginProfileWithGameIdRequest, LoginRemoteAuthRequest, LoginRemoteAuthWithGameIdRequest, LoginUniqueNickRequest, LoginUniqueNickWithGameIdRequest from servers.web_services.src.modules.auth.handlers.general import LoginProfileHandler, LoginProfileWithGameIdHandler, LoginRemoteAuthHandler, LoginRemoteAuthWithGameIdHandler, LoginUniqueNickHandler, LoginUniqueNickWithGameIdHandler from servers.web_services.src.modules.direct2game.contracts.requests import GetPurchaseHistoryRequest, GetStoreAvailabilityRequest from servers.web_services.src.modules.direct2game.handlers.general import GetPurchaseHistoryHandler, GetStoreAvailabilityHandler from servers.web_services.src.modules.sake.contracts.requests import CreateRecordRequest, GetMyRecordsRequest, SearchForRecordsRequest -from servers.web_services.src.modules.sake.handlers.general import CreateRecordHandler, GetMyRecordsHandler, SearchForRecordsHandler +from servers.web_services.src.modules.sake.applications.handlers import CreateRecordHandler, GetMyRecordsHandler, SearchForRecordsHandler class Switcher(SwitcherBase): diff --git a/src/servers/web_services/src/modules/auth/exceptions/general.py b/src/servers/web_services/src/modules/auth/exceptions/general.py index 2e262963b..6c5137645 100644 --- a/src/servers/web_services/src/modules/auth/exceptions/general.py +++ b/src/servers/web_services/src/modules/auth/exceptions/general.py @@ -1,4 +1,4 @@ -from servers.web_services.src.exceptions.general import WebException +from servers.web_services.src.aggregations.exceptions import WebException class AuthException(WebException): diff --git a/src/servers/web_services/src/modules/direct2game/contracts/requests.py b/src/servers/web_services/src/modules/direct2game/contracts/requests.py index aa38205f9..cb8fd70ba 100644 --- a/src/servers/web_services/src/modules/direct2game/contracts/requests.py +++ b/src/servers/web_services/src/modules/direct2game/contracts/requests.py @@ -1,4 +1,4 @@ -from servers.web_services.src.exceptions.general import WebException +from servers.web_services.src.aggregations.exceptions import WebException from servers.web_services.src.modules.direct2game.abstractions.contracts import ( NAMESPACE, RequestBase, diff --git a/src/servers/web_services/src/modules/sake/abstractions/general.py b/src/servers/web_services/src/modules/sake/abstractions/generals.py similarity index 100% rename from src/servers/web_services/src/modules/sake/abstractions/general.py rename to src/servers/web_services/src/modules/sake/abstractions/generals.py diff --git a/src/servers/web_services/src/modules/sake/handlers/general.py b/src/servers/web_services/src/modules/sake/applications/handlers.py similarity index 93% rename from src/servers/web_services/src/modules/sake/handlers/general.py rename to src/servers/web_services/src/modules/sake/applications/handlers.py index fb10ca4c5..59dc05c7e 100644 --- a/src/servers/web_services/src/modules/sake/handlers/general.py +++ b/src/servers/web_services/src/modules/sake/applications/handlers.py @@ -1,5 +1,5 @@ from servers.web_services.src.applications.client import Client -from servers.web_services.src.modules.sake.abstractions.general import CmdHandlerBase +from servers.web_services.src.modules.sake.abstractions.generals import CmdHandlerBase from servers.web_services.src.modules.sake.contracts.requests import CreateRecordRequest, GetMyRecordsRequest, SearchForRecordsRequest from servers.web_services.src.modules.sake.contracts.results import CreateRecordResult, GetMyRecordsResult, SearchForRecordsResult diff --git a/src/servers/web_services/src/modules/sake/contracts/requests.py b/src/servers/web_services/src/modules/sake/contracts/requests.py index 54f91be87..effd87eb4 100644 --- a/src/servers/web_services/src/modules/sake/contracts/requests.py +++ b/src/servers/web_services/src/modules/sake/contracts/requests.py @@ -2,8 +2,8 @@ from typing import Optional import xml.etree.ElementTree as ET -from servers.web_services.src.exceptions.general import WebException -from servers.web_services.src.modules.sake.abstractions.general import ( +from servers.web_services.src.aggregations.exceptions import WebException +from servers.web_services.src.modules.sake.abstractions.generals import ( RequestBase, NAMESPACE, ) diff --git a/src/servers/web_services/src/modules/sake/contracts/responses.py b/src/servers/web_services/src/modules/sake/contracts/responses.py index 220e8d7b4..7432f7d95 100644 --- a/src/servers/web_services/src/modules/sake/contracts/responses.py +++ b/src/servers/web_services/src/modules/sake/contracts/responses.py @@ -1,4 +1,4 @@ -from servers.web_services.src.modules.sake.abstractions.general import ResponseBase +from servers.web_services.src.modules.sake.abstractions.generals import ResponseBase from servers.web_services.src.modules.sake.contracts.requests import CreateRecordRequest, SearchForRecordsRequest from servers.web_services.src.modules.sake.contracts.results import CreateRecordResult, SearchForRecordsResult diff --git a/src/servers/web_services/src/modules/sake/contracts/results.py b/src/servers/web_services/src/modules/sake/contracts/results.py index 7942c08bd..1b3ab4049 100644 --- a/src/servers/web_services/src/modules/sake/contracts/results.py +++ b/src/servers/web_services/src/modules/sake/contracts/results.py @@ -1,7 +1,7 @@ from typing import OrderedDict from pydantic import BaseModel -from servers.web_services.src.modules.sake.abstractions.general import ResultBase +from servers.web_services.src.modules.sake.abstractions.generals import ResultBase class CreateRecordResult(ResultBase): diff --git a/src/servers/web_services/src/modules/sake/exceptions/general.py b/src/servers/web_services/src/modules/sake/exceptions/general.py index 589966e55..cf0bc14b9 100644 --- a/src/servers/web_services/src/modules/sake/exceptions/general.py +++ b/src/servers/web_services/src/modules/sake/exceptions/general.py @@ -1,4 +1,4 @@ -from servers.web_services.src.exceptions.general import WebException +from servers.web_services.src.aggregations.exceptions import WebException class SakeException(WebException): From 8d276fb75cc30bf45e090e1b7a2361ab56e6f968 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 28 Oct 2024 08:35:14 +0000 Subject: [PATCH 127/231] fix: unit test errors --- src/backends/library/database/redis.py | 13 +++----- src/{tests => }/library/__init__.py | 0 src/library/src/abstractions/client.py | 2 +- src/library/src/abstractions/handler.py | 18 +++++------ src/library/src/abstractions/switcher.py | 3 +- src/library/src/encryption/xor_encryption.py | 30 ++++++++----------- src/library/src/network/http_handler.py | 2 +- src/library/src/network/tcp_handler.py | 2 +- src/library/src/network/udp_handler.py | 2 +- src/library/tests/encrypt_tests.py | 26 ++++++++++++++++ .../general.py => mock_objects.py} | 4 +-- .../game_status/src/abstractions/handlers.py | 3 +- .../game_status/src/applications/handlers.py | 27 +++++++++-------- .../game_status/src/applications/switcher.py | 6 ++-- .../game_status/src/contracts/requests.py | 2 +- src/servers/game_status/tests/game_tests.py | 6 +++- .../game_status/tests/handler_tests.py | 21 ++++++------- src/servers/game_status/tests/mock_objects.py | 19 ++++++++++-- .../natneg/src/applications/handlers.py | 24 +++++++-------- src/servers/natneg/tests/handler_tests.py | 6 +--- src/servers/natneg/tests/mock_objects.py | 11 ++++++- .../tests/game_tests.py | 29 ++---------------- .../tests/mock_objects.py | 20 ++++++++++++- .../tests/game_tests.py | 3 +- .../tests/handler_tests.py | 5 ++-- .../tests/mock_objects.py | 9 +++++- src/tests/library/encrypt_tests.py | 18 ----------- 27 files changed, 166 insertions(+), 145 deletions(-) rename src/{tests => }/library/__init__.py (100%) create mode 100644 src/library/tests/encrypt_tests.py rename src/library/tests/{mock_objects/general.py => mock_objects.py} (83%) delete mode 100644 src/tests/library/encrypt_tests.py diff --git a/src/backends/library/database/redis.py b/src/backends/library/database/redis.py index bc6693078..662f2e7bf 100644 --- a/src/backends/library/database/redis.py +++ b/src/backends/library/database/redis.py @@ -1,16 +1,11 @@ -import asyncio -# import redis -import aioredis +import redis from library.src.configs import CONFIG # SESSION = redis.Redis.from_url(CONFIG.redis.url) -pool = aioredis.from_url(CONFIG.redis.url) -loop=asyncio.get_event_loop() -loop.run_until_complete(pool.set("hello","hi")) -data = loop.run_until_complete(pool.get("hello")) +client = redis.from_url(CONFIG.redis.url) +client.set("hello", "hi") +data = client.get("hello") pass - - diff --git a/src/tests/library/__init__.py b/src/library/__init__.py similarity index 100% rename from src/tests/library/__init__.py rename to src/library/__init__.py diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index cfa6bf45f..04e5e58df 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -45,7 +45,7 @@ def __init__( self.crypto = None self.is_log_raw = False # fmt: off - self._log_prefix = self.connection.ip_endpoint + self._log_prefix = f"[{self.connection.ip_endpoint}]" # fmt: on def on_connected(self) -> None: diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index 244b50a4c..dc18ac920 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -1,6 +1,6 @@ from library.src.abstractions.client import ClientBase from library.src.exceptions.general import UniSpyException -from typing import Type +from typing import Optional, Type import requests from library.src.configs import CONFIG @@ -13,7 +13,7 @@ class CmdHandlerBase: _client: "ClientBase" _request: "RequestBase" _result: "ResultBase" - _response: "ResponseBase" + _response: Optional["ResponseBase"] """ the response instance, initialize as None in __init__ """ @@ -40,13 +40,8 @@ def __init__(self, client: "ClientBase", request: "RequestBase") -> None: assert issubclass(type(client), ClientBase) assert issubclass(type(request), RequestBase) # if some subclass do not need result, override the __init__() in that subclass - if not hasattr(self, "_is_feaching"): - self._is_feaching = True - if not hasattr(self, "_is_uploading"): - self._is_uploading = True - if self._is_feaching: - assert issubclass(self._result_cls, ResultBase) - + self._is_feaching = True + self._is_uploading = True self._client = client self._request = request @@ -79,7 +74,8 @@ def _data_operate(self) -> None: # we check whether we need fetch data if not self._is_uploading: return - + if self._is_feaching: + assert issubclass(self._result_cls, ResultBase) # default use restapi to access to our backend service # get the http response and create it with this type # http://127.0.0.1:8080/gamespy/pcm/login/ @@ -108,6 +104,8 @@ def _response_send(self) -> None: virtual function, can be override Send response back to client, this is a virtual function which can be override only by child class """ + if self._response is None: + return self._client.send(self._response) def _handle_exception(self, ex: Exception) -> None: diff --git a/src/library/src/abstractions/switcher.py b/src/library/src/abstractions/switcher.py index 9d177dee0..00c943711 100644 --- a/src/library/src/abstractions/switcher.py +++ b/src/library/src/abstractions/switcher.py @@ -39,7 +39,8 @@ def handle(self): for request in self._requests: handler = self._create_cmd_handlers(request[0], request[1]) if handler is None: - self._client.log_warn("Request ignored.") + self._client.log_warn( + f"Request: <{request[0]}> is ignored.") continue self._handlers.append(handler) if len(self._handlers) == 0: diff --git a/src/library/src/encryption/xor_encryption.py b/src/library/src/encryption/xor_encryption.py index 82ed425bd..6958b9ea1 100644 --- a/src/library/src/encryption/xor_encryption.py +++ b/src/library/src/encryption/xor_encryption.py @@ -24,29 +24,23 @@ def encode(plaintext: bytes, enc_type: XorType): seed_2 = b"Industries" seed_3 = b"ProjectAphex" temp_plaintext = list(plaintext) - length = len(temp_plaintext) index = 0 - temp = seed_0 - + key = seed_0 if enc_type == XorType.TYPE_0: - temp = seed_0 + key = seed_0 elif enc_type == XorType.TYPE_1: - temp = seed_1 + key = seed_1 elif enc_type == XorType.TYPE_2: - temp = seed_2 + key = seed_2 elif enc_type == XorType.TYPE_3: - temp = seed_3 - - temp_length = len(temp) - - for i in range(length): - if i >= temp_length: - i = 0 - - temp_plaintext[index] ^= temp[i] - index += 1 - - return bytes(temp_plaintext) + key = seed_3 + result = [] + for index in range(len(plaintext)): + key_index = index % len(key) + enc_byte = ((plaintext[index] ^ key[key_index])) % 255 + result.append(enc_byte) + + return bytes(result) def encrypt(self, data: bytes): super().encrypt(data) diff --git a/src/library/src/network/http_handler.py b/src/library/src/network/http_handler.py index 4d9401b88..75d7eb4e3 100644 --- a/src/library/src/network/http_handler.py +++ b/src/library/src/network/http_handler.py @@ -76,6 +76,6 @@ def on_connected(self) -> None: if __name__ == "__main__": # create_http_server(list(CONFIG.servers.values())[0], ClientBase) - from library.tests.mock_objects.general import LogMock + from library.tests.mock_objects import LogMock s = HttpServer(list(CONFIG.servers.values())[0], TestClient, LogMock()) diff --git a/src/library/src/network/tcp_handler.py b/src/library/src/network/tcp_handler.py index 4615960d0..f94de6faf 100644 --- a/src/library/src/network/tcp_handler.py +++ b/src/library/src/network/tcp_handler.py @@ -70,7 +70,7 @@ def on_connected(self) -> None: if __name__ == "__main__": - from tests.mock_objects.general import LogMock + from library.tests.mock_objects import LogMock s = TcpServer(list(CONFIG.servers.values())[0], TestClient, LogMock()) s.start() diff --git a/src/library/src/network/udp_handler.py b/src/library/src/network/udp_handler.py index 7b6e9d2a1..db862546e 100644 --- a/src/library/src/network/udp_handler.py +++ b/src/library/src/network/udp_handler.py @@ -55,7 +55,7 @@ def on_connected(self) -> None: if __name__ == "__main__": # create_udp_server(list(CONFIG.servers.values())[0], ClientBase) - from tests.mock_objects.general import LogMock + from library.tests.mock_objects import LogMock s = UdpServer(list(CONFIG.servers.values())[0], TestClient, LogMock()) s.start() diff --git a/src/library/tests/encrypt_tests.py b/src/library/tests/encrypt_tests.py new file mode 100644 index 000000000..4ee440e72 --- /dev/null +++ b/src/library/tests/encrypt_tests.py @@ -0,0 +1,26 @@ +import unittest +from library.src.encryption.gs_encryption import ChatCrypt +from library.src.encryption.xor_encryption import XorEncoding, XorType +from servers.game_status.src.aggregations.gscrypt import GSCrypt + + +class EncryptionTest(unittest.TestCase): + def test_chat_encryption(self): + enc = ChatCrypt("123345") + result = enc.encrypt("hello".encode("ascii")) + self.assertEqual(result, b"\xda\xaek^d") + + def test_chat_decryption(self): + enc = ChatCrypt("123345") + result = enc.decrypt(b"\xda\xaek^d") + self.assertEqual(result, b"hello") + + def test_xor_encoding(self): + raw = b"abcdefghijklmnopqrstuvwxyz" + plaintext = XorEncoding.encode(raw, XorType.TYPE_1) + self.assertEqual( + b"&\x03\x0e\x016\x16\x1e[--\n\x01\x08=\x1f\tB64\x15\x18\x13$\x08\x00I", plaintext) + + +if __name__ == "__main__": + unittest.main() diff --git a/src/library/tests/mock_objects/general.py b/src/library/tests/mock_objects.py similarity index 83% rename from src/library/tests/mock_objects/general.py rename to src/library/tests/mock_objects.py index 74809095c..245b9975c 100644 --- a/src/library/tests/mock_objects/general.py +++ b/src/library/tests/mock_objects.py @@ -39,8 +39,8 @@ def warn(self, message): print(message) -def create_mock_url(client: ClientBase, handler: type[CmdHandlerBase], data: dict) -> None: +def create_mock_url(config: ServerConfig, handler: type[CmdHandlerBase], data: dict) -> None: # fmt: off - url = f"{CONFIG.backend.url}/GameSpy/{client.server_config.server_name}/{handler.__name__}/" + url = f"{CONFIG.backend.url}/GameSpy/{config.server_name}/{handler.__name__}/" responses.add(responses.POST, url, json=data, status=200) # fmt: on diff --git a/src/servers/game_status/src/abstractions/handlers.py b/src/servers/game_status/src/abstractions/handlers.py index 103dc45e3..bd40ec7c5 100644 --- a/src/servers/game_status/src/abstractions/handlers.py +++ b/src/servers/game_status/src/abstractions/handlers.py @@ -1,3 +1,4 @@ +from typing import Optional from library.src.abstractions.contracts import ResponseBase import library.src.abstractions.handler from servers.game_status.src.abstractions.contracts import RequestBase, ResultBase @@ -8,7 +9,7 @@ class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase): _client: Client _request: RequestBase _result: ResultBase - _response: ResponseBase + _response: Optional[ResponseBase] def __init__(self, client: Client, request: RequestBase) -> None: super().__init__(client, request) diff --git a/src/servers/game_status/src/applications/handlers.py b/src/servers/game_status/src/applications/handlers.py index 6e598da53..61c433207 100644 --- a/src/servers/game_status/src/applications/handlers.py +++ b/src/servers/game_status/src/applications/handlers.py @@ -50,8 +50,8 @@ class GetProfileIdHandler(CmdHandlerBase): def __init__(self, client: Client, request: GetProfileIdRequest) -> None: assert isinstance(request, GetProfileIdRequest) - self._result_cls = GetProfileIdResult super().__init__(client, request) + self._result_cls = GetProfileIdResult def _response_construct(self) -> None: self._response = GetProfileIdResponse(self._request, self._result) @@ -59,17 +59,22 @@ def _response_construct(self) -> None: class NewGameHandler(CmdHandlerBase): def __init__(self, client: Client, request: NewGameRequest) -> None: - self._is_feaching = False - super().__init__(client, request) assert isinstance(request, NewGameRequest) + super().__init__(client, request) + self._is_feaching = False + + def _response_construct(self) -> None: + self._response = None class SetPlayerDataHandler(CmdHandlerBase): def __init__(self, client: Client, request: SetPlayerDataRequest) -> None: - self._is_feaching = False - super().__init__(client, request) assert isinstance(request, SetPlayerDataRequest) - raise NotImplementedError() + super().__init__(client, request) + self._is_feaching = False + + def _response_construct(self) -> None: + self._response = None class UpdateGameHandler(CmdHandlerBase): @@ -81,11 +86,9 @@ class UpdateGameHandler(CmdHandlerBase): _request: UpdateGameRequest def __init__(self, client: Client, request: UpdateGameRequest) -> None: - super().__init__(client, request) assert isinstance(request, UpdateGameRequest) + super().__init__(client, request) + self._is_feaching = False - def _data_operate(self) -> None: - if not self._request.game_data: - return - super()._data_operate() - raise NotImplementedError() + def _response_construct(self) -> None: + self._response = None diff --git a/src/servers/game_status/src/applications/switcher.py b/src/servers/game_status/src/applications/switcher.py index d12f6e723..fd26a4824 100644 --- a/src/servers/game_status/src/applications/switcher.py +++ b/src/servers/game_status/src/applications/switcher.py @@ -2,8 +2,8 @@ from library.src.abstractions.switcher import SwitcherBase from servers.game_status.src.abstractions.handlers import CmdHandlerBase from servers.game_status.src.applications.client import Client -from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest -from servers.game_status.src.applications.handlers import AuthGameHandler, AuthPlayerHandler, GetPlayerDataHandler, NewGameHandler, SetPlayerDataHandler, UpdateGameHandler +from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest +from servers.game_status.src.applications.handlers import AuthGameHandler, AuthPlayerHandler, GetPlayerDataHandler, GetProfileIdHandler, NewGameHandler, SetPlayerDataHandler, UpdateGameHandler class Switcher(SwitcherBase): @@ -39,5 +39,7 @@ def _create_cmd_handlers(self, name: object, raw_request: str) -> Optional[CmdHa return SetPlayerDataHandler(self._client, SetPlayerDataRequest(raw_request)) case "updgame": return UpdateGameHandler(self._client, UpdateGameRequest(raw_request)) + case "getpid": + return GetProfileIdHandler(self._client, GetProfileIdRequest(raw_request)) case _: return None diff --git a/src/servers/game_status/src/contracts/requests.py b/src/servers/game_status/src/contracts/requests.py index a01e8d5f1..61516de1e 100644 --- a/src/servers/game_status/src/contracts/requests.py +++ b/src/servers/game_status/src/contracts/requests.py @@ -45,7 +45,7 @@ def parse(self) -> None: raise GSException("localid is missing from auth game request") if "pid" in self.request_dict and "resp" in self.request_dict: try: - self.profile_id = int(self.request_dict["profile_id"]) + self.profile_id = int(self.request_dict["pid"]) except: raise GSException("profile id format is incorrect") self.auth_type = AuthMethod.PROFILE_ID_AUTH diff --git a/src/servers/game_status/tests/game_tests.py b/src/servers/game_status/tests/game_tests.py index a6b2049ba..fc0e74dce 100644 --- a/src/servers/game_status/tests/game_tests.py +++ b/src/servers/game_status/tests/game_tests.py @@ -2,10 +2,13 @@ import unittest +import responses + from servers.game_status.tests.mock_objects import create_client class GameTest(unittest.TestCase): + @responses.activate def test_worm3d_20230331(self): raws1 = [ "\\auth\\\\gamename\\worms3\\response\\bc3ca727a7825879eb9f13d9fd51bbb9\\port\\0\\id\\1\\final\\", @@ -17,10 +20,11 @@ def test_worm3d_20230331(self): for raw in raws1: client.on_received(raw.encode()) + @responses.activate def test_gmtest(self): raws = [ "\\auth\\\\gamename\\crysis2\\response\\xxxxx\\port\\30\\id\\1\\final\\", - "\\getpd\\\\pid\\0\\ptype\\0\\dindex\\1\\keys\\hello\\x1hi\\lid\\1\\final\\", + "\\getpd\\\\pid\\0\\ptype\\0\\dindex\\1\\keys\\hello\x01hi\\lid\\1\\final\\", "\\getpid\\\\nick\\xiaojiuwo\\keyhash\\00000\\lid\\1\\final\\", "\\newgame\\\\connid\\123\\sesskey\\123456\\lid\\1\\final\\", "\\newgame\\\\connid\\123\\sesskey\\2020\\lid\\1\\final\\", diff --git a/src/servers/game_status/tests/handler_tests.py b/src/servers/game_status/tests/handler_tests.py index db834c005..6fea3bb45 100644 --- a/src/servers/game_status/tests/handler_tests.py +++ b/src/servers/game_status/tests/handler_tests.py @@ -3,7 +3,7 @@ import unittest import responses -from library.tests.mock_objects.general import create_mock_url +from library.tests.mock_objects import create_mock_url from servers.game_status.src.aggregations.gscrypt import GSCrypt from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest from servers.game_status.src.aggregations.enums import PersistStorageType @@ -15,13 +15,10 @@ class HandlerTests(unittest.TestCase): @responses.activate - @unittest.skip("skiped") + # @unittest.skip("not implemented") def test_set_player_data_20230329(self): raw = "\\setpd\\\\pid\\1\\ptype\\1\\dindex\\0\\kv\\1\\lid\\2\\length\\111\\data\\\\report\\|title||victories|0|timestamp|37155|league|Team17|winner||crc|-1|player_0|spyguy|ip_0||pid_0|0|auth_0|[00]\\final\\" client = create_client() - - create_mock_url(client, SetPlayerDataHandler, {"message": "ok"}) - request = SetPlayerDataRequest(raw) request.parse() self.assertEqual(1, request.profile_id) @@ -36,29 +33,29 @@ def test_set_player_data_20230329(self): handler = SetPlayerDataHandler(client, request) handler.handle() - @unittest.skip("skiped") + @responses.activate + # @unittest.skip("not implemented") def test_gamespysdk_update_game_20230329(self): raw1 = "\\updgame\\\\sesskey\\20298203\\connid\\0\\done\\0\\gamedata\\\u0001hostname\u0001My l33t Server\u0001mapname\u0001Level 33\u0001gametype\u0001hunter\u0001gamever\u00011.230000\u0001player_0\u0001Bob!\u0001points_0\u00014\u0001deaths_0\u00012\u0001pid_0\u000132432423\u0001auth_0\u00017cca8e60a13781eebc820a50754f57cd\u0001player_1\u0001Joey\u0001points_1\u00012\u0001deaths_1\u00014\u0001pid_1\u0001643423\u0001auth_1\u000119ea14d9d92a7fcc635cf5716944d9bc\\final\\" raw2 = "\\updgame\\\\sesskey\\20298203\\connid\\0\\done\\1\\gamedata\\\u0001hostname\u0001My l33t Server\u0001mapname\u0001Level 33\u0001gametype\u0001hunter\u0001gamever\u00011.230000\u0001player_0\u0001Bob!\u0001points_0\u00016\u0001deaths_0\u00013\u0001pid_0\u000132432423\u0001auth_0\u00017cca8e60a13781eebc820a50754f57cd\u0001player_1\u0001Joey\u0001points_1\u00013\u0001deaths_1\u00016\u0001pid_1\u0001643423\u0001auth_1\u000119ea14d9d92a7fcc635cf5716944d9bc\\final\\" client = create_client() - create_mock_url(client, UpdateGameHandler, {"message": "ok"}) switcher = Switcher(client, raw1) switcher.handle() request: UpdateGameRequest = cast( UpdateGameRequest, switcher._handlers[0]._request) response = switcher._handlers[0]._response self.assertEqual("20298203", request.session_key) - self.assertEqual(1, request.connection_id) + self.assertEqual(0, request.connection_id) self.assertEqual(False, request.is_done) self.assertEqual("\u0001hostname\u0001My l33t Server\u0001mapname\u0001Level 33\u0001gametype\u0001hunter\u0001gamever\u00011.230000\u0001player_0\u0001Bob!\u0001points_0\u00014\u0001deaths_0\u00012\u0001pid_0\u000132432423\u0001auth_0\u00017cca8e60a13781eebc820a50754f57cd\u0001player_1\u0001Joey\u0001points_1\u00012\u0001deaths_1\u00014\u0001pid_1\u0001643423\u0001auth_1\u000119ea14d9d92a7fcc635cf5716944d9bc", request.game_data) - @unittest.skip("") + @responses.activate + @unittest.skip("Encrypted request is not correct") def test_worm3d_auth_player(self): - raw = "2\x0F\x16\x10]%+=veKaB3a(UC`b$\x1CO\x11VZX\x09w\x1Cu\x08L@\x13=X!\x1E{\x0EL\x1DLf[qN \x04G\x130[#N'\x09(IC`b$\\final\\" - plaintext = GSCrypt().decrypt(raw.encode("ascii")) + raw = b"2\x0F\x16\x10]%+=veKaB3a(UC`b$\x1CO\x11VZX\x09w\x1Cu\x08L@\x13=X!\x1E{\x0EL\x1DLf[qN \x04G\x130[#N'\x09(IC`b$\\final\\" + plaintext = GSCrypt().decrypt(raw) request = AuthPlayerRequest(raw) client = create_client() - create_mock_url(client, AuthPlayerHandler, {"profile_id": 1}) handler = AuthPlayerHandler(client, request) handler.handle() diff --git a/src/servers/game_status/tests/mock_objects.py b/src/servers/game_status/tests/mock_objects.py index d53520ae9..b94f11c3d 100644 --- a/src/servers/game_status/tests/mock_objects.py +++ b/src/servers/game_status/tests/mock_objects.py @@ -1,11 +1,12 @@ from typing import cast from library.src.configs import CONFIG -from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock +from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url from servers.game_status.src.applications.client import Client +from servers.game_status.src.applications.handlers import AuthGameHandler, AuthPlayerHandler, GetPlayerDataHandler, GetProfileIdHandler, NewGameHandler, SetPlayerDataHandler, UpdateGameHandler +from servers.game_status.src.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult class ClientMock(Client): - pass @@ -18,3 +19,17 @@ def create_client() -> Client: logger=logger) return cast(Client, conn._client) + + +config = CONFIG.servers["GameStatus"] +create_mock_url(config, SetPlayerDataHandler, {"message": "ok"}) +create_mock_url(config, GetPlayerDataHandler, GetPlayerDataResult( + **{"keyvalues": {"hello": "hello_value", "hi": "hi_value"}}).model_dump()) +create_mock_url(config, GetProfileIdHandler, + GetProfileIdResult(**{"profile_id": 1}).model_dump()) +create_mock_url(config, UpdateGameHandler, {"message": "ok"}) +create_mock_url(config, AuthPlayerHandler, + AuthPlayerResult(**{"profile_id": 1}).model_dump()) +create_mock_url(config, NewGameHandler, {"message": "ok"}) +create_mock_url(config, AuthGameHandler, AuthGameResult( + **{"session_key": "123456"}).model_dump()) diff --git a/src/servers/natneg/src/applications/handlers.py b/src/servers/natneg/src/applications/handlers.py index bd0c3aa89..d33648066 100644 --- a/src/servers/natneg/src/applications/handlers.py +++ b/src/servers/natneg/src/applications/handlers.py @@ -31,10 +31,10 @@ class AddressCheckHandler(CmdHandlerBase): _result: AddressCheckResult def __init__(self, client: Client, request: AddressCheckRequest) -> None: - self._is_feaching = False - super().__init__(client, request) assert isinstance(client, Client) assert isinstance(request, AddressCheckRequest) + super().__init__(client, request) + self._is_feaching = False def _data_operate(self) -> None: """ @@ -55,9 +55,9 @@ class ConnectAckHandler(CmdHandlerBase): _request: ConnectAckRequest def __init__(self, client: Client, request: ConnectAckRequest) -> None: - self._is_feaching = False - super().__init__(client, request) assert isinstance(request, ConnectAckRequest) + super().__init__(client, request) + self._is_feaching = False def _data_operate(self) -> None: self._client.log_info( @@ -68,10 +68,10 @@ class ConnectHandler(CmdHandlerBase): _result_cls: type[ConnectResult] def __init__(self, client: Client, request: ConnectRequest) -> None: - self._is_feaching = False - super().__init__(client, request) assert isinstance(request, ConnectRequest) + super().__init__(client, request) self._result_cls = ConnectResult + self._is_feaching = False class ErtAckHandler(CmdHandlerBase): @@ -79,9 +79,9 @@ class ErtAckHandler(CmdHandlerBase): _result: ErtAckResult def __init__(self, client: Client, request: ErtAckRequest) -> None: - self._is_feaching = False - super().__init__(client, request) assert isinstance(request, ErtAckRequest) + super().__init__(client, request) + self._is_feaching = False def _data_operate(self) -> None: self._result = ErtAckResult( @@ -100,9 +100,9 @@ class InitHandler(CmdHandlerBase): _result: InitResult def __init__(self, client: Client, request: InitRequest) -> None: - self._is_feaching = False - super().__init__(client, request) assert isinstance(request, InitRequest) + super().__init__(client, request) + self._is_feaching = False def _data_operate(self) -> None: self._result = InitResult( @@ -123,9 +123,9 @@ class NatifyHandler(CmdHandlerBase): _result: NatifyResult def __init__(self, client: Client, request: NatifyRequest) -> None: - self._is_feaching = False - super().__init__(client, request) assert isinstance(request, NatifyRequest) + super().__init__(client, request) + self._is_feaching = False def _data_operate(self): self._result = NatifyResult( diff --git a/src/servers/natneg/tests/handler_tests.py b/src/servers/natneg/tests/handler_tests.py index 1bf7e64d4..d22d433a8 100644 --- a/src/servers/natneg/tests/handler_tests.py +++ b/src/servers/natneg/tests/handler_tests.py @@ -1,7 +1,7 @@ import unittest from library.src.abstractions.handler import CmdHandlerBase -from library.tests.mock_objects.general import create_mock_url +from library.tests.mock_objects import create_mock_url from servers.natneg.src.contracts.requests import InitRequest from servers.natneg.src.applications.handlers import AddressCheckHandler, ErtAckHandler, InitHandler, NatifyHandler import responses @@ -36,7 +36,6 @@ def test_init(self): client = create_client() - create_mock_url(client, InitHandler, {"message": "ok"}) # test request parsing request = InitRequest(raw) @@ -73,7 +72,6 @@ def test_address_check(self): self.assertEqual(NatPortType.NN1, request.port_type) client = create_client() - create_mock_url(client, AddressCheckHandler, {"message": "ok"}) handler = AddressCheckHandler(client, request) handler.handle() @@ -99,7 +97,6 @@ def test_ert_ack(self): self.assertEqual(False, request.use_game_port) self.assertEqual(NatPortType.NN1, request.port_type) client = create_client() - create_mock_url(client, AddressCheckHandler, {"message": "ok"}) handler = ErtAckHandler(client, request) handler.handle() @@ -125,7 +122,6 @@ def test_natify(self): self.assertEqual(False, request.use_game_port) self.assertEqual(NatPortType.NN1, request.port_type) client = create_client() - create_mock_url(client, NatifyHandler, {"message": "ok"}) handler = NatifyHandler(client, request) handler.handle() diff --git a/src/servers/natneg/tests/mock_objects.py b/src/servers/natneg/tests/mock_objects.py index 0e813a8b1..4d077214e 100644 --- a/src/servers/natneg/tests/mock_objects.py +++ b/src/servers/natneg/tests/mock_objects.py @@ -1,8 +1,10 @@ -from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock +from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url from library.src.configs import CONFIG from servers.natneg.src.applications.client import Client from typing import cast +from servers.natneg.src.applications.handlers import AddressCheckHandler, InitHandler, NatifyHandler + class ClientMock(Client): @@ -18,3 +20,10 @@ def create_client() -> Client: logger=logger) return cast(Client, conn._client) + + +config = CONFIG.servers["NatNegotiation"] +create_mock_url(config, InitHandler, {"message": "ok"}) +create_mock_url(config, AddressCheckHandler, {"message": "ok"}) +create_mock_url(config, AddressCheckHandler, {"message": "ok"}) +create_mock_url(config, NatifyHandler, {"message": "ok"}) diff --git a/src/servers/presence_connection_manager/tests/game_tests.py b/src/servers/presence_connection_manager/tests/game_tests.py index 09ca35087..3de477d46 100644 --- a/src/servers/presence_connection_manager/tests/game_tests.py +++ b/src/servers/presence_connection_manager/tests/game_tests.py @@ -1,6 +1,6 @@ import unittest -from library.tests.mock_objects.general import create_mock_url +from library.tests.mock_objects import create_mock_url from servers.presence_connection_manager.src.applications.handlers import LoginHandler, NewUserHandler from servers.presence_connection_manager.src.contracts.requests import StatusRequest from servers.presence_connection_manager.tests.mock_objects import create_client @@ -15,20 +15,7 @@ def test_civilization_4(self) -> None: "\\login\\\\challenge\\xMsHUXuWNXL3KMwmhoQZJrP0RVsArCYT\\uniquenick\\civ4-tk\\userid\\25\\profileid\\26\\response\\7f2c9c6685570ea18b7207d2cbd72452\\firewall\\1\\port\\0\\productid\\10435\\gamename\\civ4\\namespaceid\\17\\sdkrevision\\1\\id\\1\\final\\", ] client = create_client() - create_mock_url(client, NewUserHandler, { - "user_id": 0, "profile_id": 0}) - create_mock_url(client, LoginHandler, {"response_proof": "7f2c9c6685570ea18b7207d2cbd72452", "data": { - "user_id": 0, - "profile_id": 0, - "nick": "test", - "email": "test@gamespy.com", - "unique_nick": "test_unique", - "password_hash": "password", - "email_verified_flag": True, - "namespace_id": 0, - "sub_profile_id": 0, - "banned_flag": False - }}) + for x in raw_requests: client.on_received(x.encode("ascii")) @@ -43,18 +30,6 @@ def test_conflict_global_storm(self) -> None: "\\inviteto\\\\sesskey\\58366\\products\\1038\\final\\", ] client = create_client() - create_mock_url(client, LoginHandler, {"response_proof": "7f2c9c6685570ea18b7207d2cbd72452", "data": { - "user_id": 0, - "profile_id": 0, - "nick": "test", - "email": "test@gamespy.com", - "unique_nick": "test_unique", - "password_hash": "password", - "email_verified_flag": True, - "namespace_id": 0, - "sub_profile_id": 0, - "banned_flag": False - }}) for x in raw_requests: client.on_received(x.encode("ascii")) pass diff --git a/src/servers/presence_connection_manager/tests/mock_objects.py b/src/servers/presence_connection_manager/tests/mock_objects.py index f414b79f6..417a5177e 100644 --- a/src/servers/presence_connection_manager/tests/mock_objects.py +++ b/src/servers/presence_connection_manager/tests/mock_objects.py @@ -1,7 +1,8 @@ from library.src.configs import CONFIG -from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock +from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url from servers.presence_connection_manager.src.applications.client import Client +from servers.presence_connection_manager.src.applications.handlers import LoginHandler, NewUserHandler class ClientMock(Client): @@ -17,3 +18,20 @@ def create_client(): logger=logger) return conn._client + + +config = CONFIG.servers["PresenceConnectionManager"] +create_mock_url(config, NewUserHandler, { + "user_id": 0, "profile_id": 0}) +create_mock_url(config, LoginHandler, {"response_proof": "7f2c9c6685570ea18b7207d2cbd72452", "data": { + "user_id": 0, + "profile_id": 0, + "nick": "test", + "email": "test@gamespy.com", + "unique_nick": "test_unique", + "password_hash": "password", + "email_verified_flag": True, + "namespace_id": 0, + "sub_profile_id": 0, + "banned_flag": False +}}) diff --git a/src/servers/presence_search_player/tests/game_tests.py b/src/servers/presence_search_player/tests/game_tests.py index 715479ee1..3db2a83aa 100644 --- a/src/servers/presence_search_player/tests/game_tests.py +++ b/src/servers/presence_search_player/tests/game_tests.py @@ -2,7 +2,7 @@ import unittest from library.src.extentions.password_encoder import process_password -from library.tests.mock_objects.general import create_mock_url +from library.tests.mock_objects import create_mock_url from servers.presence_search_player.src.contracts.requests import CheckRequest from servers.presence_search_player.src.applications.handlers import CheckHandler from servers.presence_search_player.src.applications.switcher import CmdSwitcher @@ -16,7 +16,6 @@ class GameTest(unittest.TestCase): def test_check(self): raw = "\\check\\\\nick\\spyguy\\email\\spyguy@gamespy.com\\pass\\0000\\final\\" client = create_client() - create_mock_url(client, CheckHandler, {"profile_id": 0}) switcher = CmdSwitcher(client, raw) switcher.handle() diff --git a/src/servers/presence_search_player/tests/handler_tests.py b/src/servers/presence_search_player/tests/handler_tests.py index ab283918d..8ee7d0bb5 100644 --- a/src/servers/presence_search_player/tests/handler_tests.py +++ b/src/servers/presence_search_player/tests/handler_tests.py @@ -2,7 +2,7 @@ import responses -from library.tests.mock_objects.general import create_mock_url +from library.tests.mock_objects import create_mock_url from servers.presence_search_player.src.contracts.requests import SearchRequest from servers.presence_search_player.src.applications.handlers import SearchHandler from servers.presence_search_player.tests.mock_objects import create_client @@ -62,8 +62,7 @@ def test_profile(self): request.parse() self.assertEqual("spyguy@unispy.org", request.email) - create_mock_url(client, SearchHandler, {"result": [{"profile_id": 0, "nick": "spyguy", "uniquenick": "spyguy", - "email": "spyguy@unispy.org", "firstname": "spy", "lastname": "guy", "namespace_id": 0}]}) + handler = SearchHandler(client, request) handler.handle() self.assertEqual("\\bsr\\0\\nick\\spyguy\\uniquenick\\spyguy\\namespaceid\\0\\firstname\\spy\\lastname\\guy\\email\\spyguy@unispy.org\\bsrdone\\\\more\\0\\final\\", diff --git a/src/servers/presence_search_player/tests/mock_objects.py b/src/servers/presence_search_player/tests/mock_objects.py index 782e24bda..5201c1eea 100644 --- a/src/servers/presence_search_player/tests/mock_objects.py +++ b/src/servers/presence_search_player/tests/mock_objects.py @@ -1,7 +1,8 @@ from typing import cast from library.src.configs import CONFIG -from library.tests.mock_objects.general import ConnectionMock, LogMock, RequestHandlerMock +from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url from servers.presence_search_player.src.applications.client import Client +from servers.presence_search_player.src.applications.handlers import CheckHandler, SearchHandler class ClientMock(Client): @@ -18,3 +19,9 @@ def create_client() -> Client: logger=logger) return cast(Client, conn._client) + + +config = CONFIG.servers["PresenceSearchPlayer"] +create_mock_url(config, CheckHandler, {"profile_id": 0}) +create_mock_url(config, SearchHandler, {"result": [{"profile_id": 0, "nick": "spyguy", "uniquenick": "spyguy", + "email": "spyguy@unispy.org", "firstname": "spy", "lastname": "guy", "namespace_id": 0}]}) diff --git a/src/tests/library/encrypt_tests.py b/src/tests/library/encrypt_tests.py deleted file mode 100644 index a906820b8..000000000 --- a/src/tests/library/encrypt_tests.py +++ /dev/null @@ -1,18 +0,0 @@ -import unittest -from library.src.encryption.gs_encryption import ChatCrypt - - -class GSEncryptionTest(unittest.TestCase): - def test_encryption(self): - enc = ChatCrypt("123345") - result = enc.encrypt("hello".encode("ascii")) - self.assertEqual(result, b"\xda\xaek^d") - - def test_decryption(self): - enc = ChatCrypt("123345") - result = enc.decrypt(b"\xda\xaek^d") - self.assertEqual(result, b"hello") - - -if __name__ == "__main__": - unittest.main() From ba4846d14eeed0126184de67c60af2b3d3efcc6b Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 29 Oct 2024 01:38:53 +0000 Subject: [PATCH 128/231] refactor: change handler feach data logic --- src/library/src/abstractions/handler.py | 56 +++++++++---------- src/library/src/encryption/xor_encryption.py | 1 - src/servers/chat/src/abstractions/channel.py | 1 - src/servers/chat/src/aggregates/exceptions.py | 1 - src/servers/chat/src/applications/handlers.py | 2 - .../game_status/src/applications/handlers.py | 17 +++--- .../game_status/src/contracts/requests.py | 6 +- .../game_status/tests/handler_tests.py | 1 - src/servers/game_status/tests/mock_objects.py | 26 ++++----- .../natneg/src/applications/handlers.py | 24 ++++++-- src/servers/natneg/tests/handler_tests.py | 1 + src/servers/natneg/tests/mock_objects.py | 14 ++--- .../tests/mock_objects.py | 32 +++++------ .../tests/game_tests.py | 2 - .../tests/handler_tests.py | 2 +- .../tests/mock_objects.py | 11 ++-- 16 files changed, 99 insertions(+), 98 deletions(-) diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index dc18ac920..bfb3a2777 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -13,7 +13,7 @@ class CmdHandlerBase: _client: "ClientBase" _request: "RequestBase" _result: "ResultBase" - _response: Optional["ResponseBase"] + _response: "ResponseBase" """ the response instance, initialize as None in __init__ """ @@ -23,13 +23,9 @@ class CmdHandlerBase: the initialization of _result_cls must before call super().__init__() """ _is_uploading: bool - """ - whether need send data to backend - """ + _is_feaching: bool - """ - whether need get data from backend - """ + _debug: bool = False """ whether is in debug mode, if in debug mode exception will raise from handler @@ -39,9 +35,6 @@ def __init__(self, client: "ClientBase", request: "RequestBase") -> None: assert issubclass(type(client), ClientBase) assert issubclass(type(request), RequestBase) - # if some subclass do not need result, override the __init__() in that subclass - self._is_feaching = True - self._is_uploading = True self._client = client self._request = request @@ -53,7 +46,7 @@ def handle(self) -> None: self._request_check() self._data_operate() self._response_construct() - if self._response is None: + if not hasattr(self, "_response"): return self._response_send() except Exception as ex: @@ -71,29 +64,38 @@ def _data_operate(self) -> None: """ virtual function, can be override """ - # we check whether we need fetch data - if not self._is_uploading: - return - if self._is_feaching: - assert issubclass(self._result_cls, ResultBase) - # default use restapi to access to our backend service - # get the http response and create it with this type - # http://127.0.0.1:8080/gamespy/pcm/login/ - - # fmt: off + self._upload_data() + self._feach_data() - url = f"{CONFIG.backend.url}/GameSpy/{self._client.server_config.server_name}/{self.__class__.__name__}/" + def _upload_data(self): + """ + whether need send data to backend + if child class do not require feach, overide this function to do nothing + """ + url = f"{CONFIG.backend.url}/GameSpy/{ + self._client.server_config.server_name}/{self.__class__.__name__}/" # fmt: on data = self._request.to_json() data["server_id"] = str(self._client.server_config.server_id) response = requests.post(url, json=data) - result = response.json() - # if the result cls is not declared, we do not parse the response values + self._http_result = response.json() + + def _feach_data(self): + """ + whether need get data from backend. + if child class do not require feach, overide this function to do nothing + """ + if not hasattr(self, "_result_cls"): + raise UniSpyException( + "_result should be initialized when feach data") + + if self._result_cls is None: + raise UniSpyException("_result should not be null when feach data") - if self._is_feaching: - self._result = self._result_cls(**result) + assert issubclass(self._result_cls, ResultBase) + self._result = self._result_cls(**self._http_result) def _response_construct(self) -> None: """construct response here in specific child class""" @@ -104,8 +106,6 @@ def _response_send(self) -> None: virtual function, can be override Send response back to client, this is a virtual function which can be override only by child class """ - if self._response is None: - return self._client.send(self._response) def _handle_exception(self, ex: Exception) -> None: diff --git a/src/library/src/encryption/xor_encryption.py b/src/library/src/encryption/xor_encryption.py index 6958b9ea1..1436389b0 100644 --- a/src/library/src/encryption/xor_encryption.py +++ b/src/library/src/encryption/xor_encryption.py @@ -23,7 +23,6 @@ def encode(plaintext: bytes, enc_type: XorType): seed_1 = b"GameSpy3D" seed_2 = b"Industries" seed_3 = b"ProjectAphex" - temp_plaintext = list(plaintext) index = 0 key = seed_0 if enc_type == XorType.TYPE_0: diff --git a/src/servers/chat/src/abstractions/channel.py b/src/servers/chat/src/abstractions/channel.py index 4cce9d326..5f16a3bee 100644 --- a/src/servers/chat/src/abstractions/channel.py +++ b/src/servers/chat/src/abstractions/channel.py @@ -40,7 +40,6 @@ class ChannelHandlerBase(PostLoginHandlerBase): def __init__(self, client: ClientBase, request: RequestBase): super().__init__(client, request) # self._channel = None - # self._response = None def _request_check(self) -> None: if self._request.raw_request is None: diff --git a/src/servers/chat/src/aggregates/exceptions.py b/src/servers/chat/src/aggregates/exceptions.py index 32d4ccd04..f04a7cdb0 100644 --- a/src/servers/chat/src/aggregates/exceptions.py +++ b/src/servers/chat/src/aggregates/exceptions.py @@ -1,4 +1,3 @@ -from servers.chat.src.aggregates.exceptions import IRCChannelException from servers.chat.src.abstractions.contract import SERVER_DOMAIN from servers.chat.src.aggregates.enums import IRCErrorCode diff --git a/src/servers/chat/src/applications/handlers.py b/src/servers/chat/src/applications/handlers.py index 54454348a..6ca605ff9 100644 --- a/src/servers/chat/src/applications/handlers.py +++ b/src/servers/chat/src/applications/handlers.py @@ -269,7 +269,6 @@ class GetChannelKeyHandler(ChannelHandlerBase): def __init__(self, client: ClientBase, request: GetChannelKeyRequest): assert isinstance(request, GetChannelKeyRequest) - self._is_fetching_data = True super().__init__(client, request) def _publish_message(self): @@ -393,7 +392,6 @@ class NamesHandler(ChannelHandlerBase): def __init__(self, client: ClientBase, request: NamesRequest): assert isinstance(request, NamesRequest) - self._is_fetching_data = True super().__init__(client, request) def _response_construct(self): diff --git a/src/servers/game_status/src/applications/handlers.py b/src/servers/game_status/src/applications/handlers.py index 61c433207..ea387a20b 100644 --- a/src/servers/game_status/src/applications/handlers.py +++ b/src/servers/game_status/src/applications/handlers.py @@ -61,20 +61,19 @@ class NewGameHandler(CmdHandlerBase): def __init__(self, client: Client, request: NewGameRequest) -> None: assert isinstance(request, NewGameRequest) super().__init__(client, request) - self._is_feaching = False - def _response_construct(self) -> None: - self._response = None + def _feach_data(self): + pass + class SetPlayerDataHandler(CmdHandlerBase): def __init__(self, client: Client, request: SetPlayerDataRequest) -> None: assert isinstance(request, SetPlayerDataRequest) super().__init__(client, request) - self._is_feaching = False - def _response_construct(self) -> None: - self._response = None + def _feach_data(self): + pass class UpdateGameHandler(CmdHandlerBase): @@ -88,7 +87,7 @@ class UpdateGameHandler(CmdHandlerBase): def __init__(self, client: Client, request: UpdateGameRequest) -> None: assert isinstance(request, UpdateGameRequest) super().__init__(client, request) - self._is_feaching = False - def _response_construct(self) -> None: - self._response = None + def _feach_data(self): + pass + diff --git a/src/servers/game_status/src/contracts/requests.py b/src/servers/game_status/src/contracts/requests.py index 61516de1e..53cb5cebb 100644 --- a/src/servers/game_status/src/contracts/requests.py +++ b/src/servers/game_status/src/contracts/requests.py @@ -67,7 +67,11 @@ class GetPlayerDataRequest(RequestBase): storage_type: PersistStorageType data_index: int is_get_all_data: bool = False - keys: list[str] = [] + keys: list[str] + + def __init__(self, raw_request: object) -> None: + super().__init__(raw_request) + self.keys = [] def parse(self) -> None: super().parse() diff --git a/src/servers/game_status/tests/handler_tests.py b/src/servers/game_status/tests/handler_tests.py index 6fea3bb45..f7996eb59 100644 --- a/src/servers/game_status/tests/handler_tests.py +++ b/src/servers/game_status/tests/handler_tests.py @@ -43,7 +43,6 @@ def test_gamespysdk_update_game_20230329(self): switcher.handle() request: UpdateGameRequest = cast( UpdateGameRequest, switcher._handlers[0]._request) - response = switcher._handlers[0]._response self.assertEqual("20298203", request.session_key) self.assertEqual(0, request.connection_id) self.assertEqual(False, request.is_done) diff --git a/src/servers/game_status/tests/mock_objects.py b/src/servers/game_status/tests/mock_objects.py index b94f11c3d..e8ddc0968 100644 --- a/src/servers/game_status/tests/mock_objects.py +++ b/src/servers/game_status/tests/mock_objects.py @@ -17,19 +17,17 @@ def create_client() -> Client: handler=handler, config=CONFIG.servers["GameStatus"], t_client=ClientMock, logger=logger) + config = CONFIG.servers["GameStatus"] + create_mock_url(config, SetPlayerDataHandler, {"message": "ok"}) + create_mock_url(config, GetPlayerDataHandler, GetPlayerDataResult( + **{"keyvalues": {"hello": "hello_value", "hi": "hi_value"}}).model_dump()) + create_mock_url(config, GetProfileIdHandler, + GetProfileIdResult(**{"profile_id": 1}).model_dump()) + create_mock_url(config, UpdateGameHandler, {"message": "ok"}) + create_mock_url(config, AuthPlayerHandler, + AuthPlayerResult(**{"profile_id": 1}).model_dump()) + create_mock_url(config, NewGameHandler, {"message": "ok"}) + create_mock_url(config, AuthGameHandler, AuthGameResult( + **{"session_key": "123456"}).model_dump()) return cast(Client, conn._client) - - -config = CONFIG.servers["GameStatus"] -create_mock_url(config, SetPlayerDataHandler, {"message": "ok"}) -create_mock_url(config, GetPlayerDataHandler, GetPlayerDataResult( - **{"keyvalues": {"hello": "hello_value", "hi": "hi_value"}}).model_dump()) -create_mock_url(config, GetProfileIdHandler, - GetProfileIdResult(**{"profile_id": 1}).model_dump()) -create_mock_url(config, UpdateGameHandler, {"message": "ok"}) -create_mock_url(config, AuthPlayerHandler, - AuthPlayerResult(**{"profile_id": 1}).model_dump()) -create_mock_url(config, NewGameHandler, {"message": "ok"}) -create_mock_url(config, AuthGameHandler, AuthGameResult( - **{"session_key": "123456"}).model_dump()) diff --git a/src/servers/natneg/src/applications/handlers.py b/src/servers/natneg/src/applications/handlers.py index d33648066..b290e50c2 100644 --- a/src/servers/natneg/src/applications/handlers.py +++ b/src/servers/natneg/src/applications/handlers.py @@ -34,7 +34,9 @@ def __init__(self, client: Client, request: AddressCheckRequest) -> None: assert isinstance(client, Client) assert isinstance(request, AddressCheckRequest) super().__init__(client, request) - self._is_feaching = False + + def _feach_data(self): + pass def _data_operate(self) -> None: """ @@ -57,7 +59,9 @@ class ConnectAckHandler(CmdHandlerBase): def __init__(self, client: Client, request: ConnectAckRequest) -> None: assert isinstance(request, ConnectAckRequest) super().__init__(client, request) - self._is_feaching = False + + def _feach_data(self): + pass def _data_operate(self) -> None: self._client.log_info( @@ -71,7 +75,9 @@ def __init__(self, client: Client, request: ConnectRequest) -> None: assert isinstance(request, ConnectRequest) super().__init__(client, request) self._result_cls = ConnectResult - self._is_feaching = False + + def _feach_data(self): + pass class ErtAckHandler(CmdHandlerBase): @@ -81,7 +87,9 @@ class ErtAckHandler(CmdHandlerBase): def __init__(self, client: Client, request: ErtAckRequest) -> None: assert isinstance(request, ErtAckRequest) super().__init__(client, request) - self._is_feaching = False + + def _feach_data(self): + pass def _data_operate(self) -> None: self._result = ErtAckResult( @@ -102,7 +110,9 @@ class InitHandler(CmdHandlerBase): def __init__(self, client: Client, request: InitRequest) -> None: assert isinstance(request, InitRequest) super().__init__(client, request) - self._is_feaching = False + + def _feach_data(self): + pass def _data_operate(self) -> None: self._result = InitResult( @@ -125,7 +135,9 @@ class NatifyHandler(CmdHandlerBase): def __init__(self, client: Client, request: NatifyRequest) -> None: assert isinstance(request, NatifyRequest) super().__init__(client, request) - self._is_feaching = False + + def _feach_data(self): + pass def _data_operate(self): self._result = NatifyResult( diff --git a/src/servers/natneg/tests/handler_tests.py b/src/servers/natneg/tests/handler_tests.py index d22d433a8..7a5b02e94 100644 --- a/src/servers/natneg/tests/handler_tests.py +++ b/src/servers/natneg/tests/handler_tests.py @@ -1,6 +1,7 @@ import unittest from library.src.abstractions.handler import CmdHandlerBase +from library.src.configs import CONFIG from library.tests.mock_objects import create_mock_url from servers.natneg.src.contracts.requests import InitRequest from servers.natneg.src.applications.handlers import AddressCheckHandler, ErtAckHandler, InitHandler, NatifyHandler diff --git a/src/servers/natneg/tests/mock_objects.py b/src/servers/natneg/tests/mock_objects.py index 4d077214e..b4bc9ce7b 100644 --- a/src/servers/natneg/tests/mock_objects.py +++ b/src/servers/natneg/tests/mock_objects.py @@ -6,6 +6,7 @@ from servers.natneg.src.applications.handlers import AddressCheckHandler, InitHandler, NatifyHandler + class ClientMock(Client): pass @@ -18,12 +19,11 @@ def create_client() -> Client: handler=handler, config=CONFIG.servers["NatNegotiation"], t_client=ClientMock, logger=logger) + + config = CONFIG.servers["NatNegotiation"] + create_mock_url(config, InitHandler, {"message": "ok"}) + create_mock_url(config, AddressCheckHandler, {"message": "ok"}) + create_mock_url(config, AddressCheckHandler, {"message": "ok"}) + create_mock_url(config, NatifyHandler, {"message": "ok"}) return cast(Client, conn._client) - - -config = CONFIG.servers["NatNegotiation"] -create_mock_url(config, InitHandler, {"message": "ok"}) -create_mock_url(config, AddressCheckHandler, {"message": "ok"}) -create_mock_url(config, AddressCheckHandler, {"message": "ok"}) -create_mock_url(config, NatifyHandler, {"message": "ok"}) diff --git a/src/servers/presence_connection_manager/tests/mock_objects.py b/src/servers/presence_connection_manager/tests/mock_objects.py index 417a5177e..eb3ce4711 100644 --- a/src/servers/presence_connection_manager/tests/mock_objects.py +++ b/src/servers/presence_connection_manager/tests/mock_objects.py @@ -16,22 +16,20 @@ def create_client(): handler=handler, config=CONFIG.servers["PresenceConnectionManager"], t_client=ClientMock, logger=logger) + config = CONFIG.servers["PresenceConnectionManager"] + create_mock_url(config, NewUserHandler, { + "user_id": 0, "profile_id": 0}) + create_mock_url(config, LoginHandler, {"response_proof": "7f2c9c6685570ea18b7207d2cbd72452", "data": { + "user_id": 0, + "profile_id": 0, + "nick": "test", + "email": "test@gamespy.com", + "unique_nick": "test_unique", + "password_hash": "password", + "email_verified_flag": True, + "namespace_id": 0, + "sub_profile_id": 0, + "banned_flag": False + }}) return conn._client - - -config = CONFIG.servers["PresenceConnectionManager"] -create_mock_url(config, NewUserHandler, { - "user_id": 0, "profile_id": 0}) -create_mock_url(config, LoginHandler, {"response_proof": "7f2c9c6685570ea18b7207d2cbd72452", "data": { - "user_id": 0, - "profile_id": 0, - "nick": "test", - "email": "test@gamespy.com", - "unique_nick": "test_unique", - "password_hash": "password", - "email_verified_flag": True, - "namespace_id": 0, - "sub_profile_id": 0, - "banned_flag": False -}}) diff --git a/src/servers/presence_search_player/tests/game_tests.py b/src/servers/presence_search_player/tests/game_tests.py index 3db2a83aa..8c6429096 100644 --- a/src/servers/presence_search_player/tests/game_tests.py +++ b/src/servers/presence_search_player/tests/game_tests.py @@ -1,8 +1,6 @@ from typing import TYPE_CHECKING, cast import unittest -from library.src.extentions.password_encoder import process_password -from library.tests.mock_objects import create_mock_url from servers.presence_search_player.src.contracts.requests import CheckRequest from servers.presence_search_player.src.applications.handlers import CheckHandler from servers.presence_search_player.src.applications.switcher import CmdSwitcher diff --git a/src/servers/presence_search_player/tests/handler_tests.py b/src/servers/presence_search_player/tests/handler_tests.py index 8ee7d0bb5..acfa66998 100644 --- a/src/servers/presence_search_player/tests/handler_tests.py +++ b/src/servers/presence_search_player/tests/handler_tests.py @@ -31,13 +31,13 @@ SUGGEST_UNIQUE = "\\uniquesearch\\\\preferrednick\\xiaojiuwo\\namespaceid\\0\\gamename\\gmtest\\final\\" -client = create_client() class HandlerTests(unittest.TestCase): @responses.activate def test_profile(self): + client = create_client() request = SearchRequest(SEARCH_1) request.parse() self.assertEqual("xxxx", request.session_key) diff --git a/src/servers/presence_search_player/tests/mock_objects.py b/src/servers/presence_search_player/tests/mock_objects.py index 5201c1eea..43bda1e93 100644 --- a/src/servers/presence_search_player/tests/mock_objects.py +++ b/src/servers/presence_search_player/tests/mock_objects.py @@ -6,7 +6,6 @@ class ClientMock(Client): - pass @@ -17,11 +16,9 @@ def create_client() -> Client: handler=handler, config=CONFIG.servers["PresenceSearchPlayer"], t_client=ClientMock, logger=logger) + config = CONFIG.servers["PresenceSearchPlayer"] + create_mock_url(config, CheckHandler, {"profile_id": 0}) + create_mock_url(config, SearchHandler, {"result": [{"profile_id": 0, "nick": "spyguy", "uniquenick": "spyguy", + "email": "spyguy@unispy.org", "firstname": "spy", "lastname": "guy", "namespace_id": 0}]}) return cast(Client, conn._client) - - -config = CONFIG.servers["PresenceSearchPlayer"] -create_mock_url(config, CheckHandler, {"profile_id": 0}) -create_mock_url(config, SearchHandler, {"result": [{"profile_id": 0, "nick": "spyguy", "uniquenick": "spyguy", - "email": "spyguy@unispy.org", "firstname": "spy", "lastname": "guy", "namespace_id": 0}]}) From 920bae6044fab5d7ab23d788ad287e4c0acea296 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 29 Oct 2024 07:13:10 +0000 Subject: [PATCH 129/231] refactor: added tests for qr and sb --- src/library/src/abstractions/client.py | 4 +- src/library/src/abstractions/connections.py | 4 -- src/library/src/log/log_manager.py | 4 +- src/library/src/network/http_handler.py | 2 +- src/library/src/network/tcp_handler.py | 2 +- src/library/src/network/udp_handler.py | 2 +- src/requirements.txt | 22 ++++---- .../game_status/src/applications/client.py | 2 +- src/servers/natneg/src/applications/client.py | 2 +- .../src/applications/client.py | 2 +- .../src/applications/client.py | 7 +-- .../src/applications/switcher.py | 2 +- .../tests/mock_objects.py | 9 ++-- src/servers/query_report/__init__.py | 0 .../src/aggregates/game_server_info.py | 5 +- .../src/aggregates/peer_room_info.py | 4 +- .../query_report/src/applications/client.py | 5 +- .../src/v2/abstractions/contracts.py | 9 +++- .../src/v2/applications/handlers.py | 11 +++- .../query_report/src/v2/contracts/requests.py | 32 ++++++++---- src/servers/query_report/tests/game_tests.py | 18 +++++++ .../query_report/tests/mock_objects.py | 24 +++++++++ .../query_report/tests/request_tests.py | 52 +++++++++++++++++++ src/servers/server_browser/__init__.py | 0 .../src/v2/abstractions/contracts.py | 8 +-- .../src/v2/applications/client.py | 8 ++- .../src/v2/applications/switcher.py | 32 ++++++++++++ .../server_browser/tests/game_tests.py | 17 ++++++ .../server_browser/tests/mock_objects.py | 38 ++++++++++++++ .../web_services/src/applications/client.py | 2 +- src/unispy | 3 ++ 31 files changed, 277 insertions(+), 55 deletions(-) create mode 100644 src/servers/query_report/__init__.py create mode 100644 src/servers/query_report/tests/game_tests.py create mode 100644 src/servers/query_report/tests/mock_objects.py create mode 100644 src/servers/query_report/tests/request_tests.py create mode 100644 src/servers/server_browser/__init__.py create mode 100644 src/servers/server_browser/src/v2/applications/switcher.py create mode 100644 src/servers/server_browser/tests/game_tests.py create mode 100644 src/servers/server_browser/tests/mock_objects.py create mode 100644 src/unispy diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index 04e5e58df..5b294a994 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -57,7 +57,7 @@ def on_disconnected(self) -> None: del self.pool[self.connection.ip_endpoint] pass - def create_switcher(self, buffer: bytes | str) -> "SwitcherBase": # type: ignore + def _create_switcher(self, buffer: bytes | str) -> "SwitcherBase": # type: ignore """ virtual method helps verify buffer type """ @@ -72,7 +72,7 @@ def on_received(self, buffer: bytes | str) -> None: else: raise UniSpyException("buffer type is invalid") self.log_network_receving(buffer) - switcher: "SwitcherBase" = self.create_switcher(buffer) + switcher: "SwitcherBase" = self._create_switcher(buffer) switcher.handle() def decrypt_message(self, buffer: bytes) -> bytes: diff --git a/src/library/src/abstractions/connections.py b/src/library/src/abstractions/connections.py index 307d2278e..9bdcd0d0e 100644 --- a/src/library/src/abstractions/connections.py +++ b/src/library/src/abstractions/connections.py @@ -21,10 +21,6 @@ class ConnectionBase: logger: LogWriter handler: socketserver.BaseRequestHandler _client: ClientBase - ip_endpoint: str - """ - ip endpoint format str \\ - """ def __init__( self, diff --git a/src/library/src/log/log_manager.py b/src/library/src/log/log_manager.py index 935853410..32fc4a893 100644 --- a/src/library/src/log/log_manager.py +++ b/src/library/src/log/log_manager.py @@ -41,7 +41,7 @@ def create(logger_name: str) -> "LogWriter": create_dir(log_file_path) file_name = f"{log_file_path}/{logger_name}.log" logging.basicConfig( - filename=file_name, + filename=logger_name, level=logging.INFO, format=f"%(asctime)s [{logger_name}] [%(levelname)s]: %(message)s", datefmt="%Y-%m-%d %H:%M:%S", @@ -68,7 +68,7 @@ def create(logger_name: str) -> "LogWriter": console_handler.setLevel(logging.DEBUG) console_handler.setFormatter(formatter) - logger = logging.getLogger(log_file_path) + logger = logging.getLogger(logger_name) logger.addHandler(file_handler) logger.addHandler(console_handler) return LogWriter(logger) diff --git a/src/library/src/network/http_handler.py b/src/library/src/network/http_handler.py index 75d7eb4e3..c08ef0946 100644 --- a/src/library/src/network/http_handler.py +++ b/src/library/src/network/http_handler.py @@ -63,7 +63,7 @@ def start(self) -> None: class TestClient(ClientBase): - def create_switcher(self, buffer) -> None: + def _create_switcher(self, buffer) -> None: # return super().create_switcher(buffer) print(buffer) pass diff --git a/src/library/src/network/tcp_handler.py b/src/library/src/network/tcp_handler.py index f94de6faf..866be421c 100644 --- a/src/library/src/network/tcp_handler.py +++ b/src/library/src/network/tcp_handler.py @@ -58,7 +58,7 @@ def start(self) -> None: class TestClient(ClientBase): - def create_switcher(self, buffer) -> None: + def _create_switcher(self, buffer) -> None: # return super().create_switcher(buffer) print(buffer) pass diff --git a/src/library/src/network/udp_handler.py b/src/library/src/network/udp_handler.py index db862546e..6c7507dbd 100644 --- a/src/library/src/network/udp_handler.py +++ b/src/library/src/network/udp_handler.py @@ -42,7 +42,7 @@ def __exit__(self, *args): class TestClient(ClientBase): - def create_switcher(self, buffer) -> None: + def _create_switcher(self, buffer) -> None: # return super().create_switcher(buffer) print(buffer) pass diff --git a/src/requirements.txt b/src/requirements.txt index e4d978948..7663f3023 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,15 +1,15 @@ pyfiglet -psycopg2-binary -sqlalchemy +psycopg2-binary == 2.9.10 +sqlalchemy == 2.0.36 jsonpickle == 3.0.3 email_validator == 2.1.1 attrs -requests -fastapi -xmltodict -responses -pydantic -redis -websocket-client -uvicorn -prettytable \ No newline at end of file +requests == 2.32.3 +fastapi == 0.115.4 +xmltodict == 0.14.2 +responses == 0.25.3 +pydantic == 2.9.2 +redis == 5.2.0 +websocket-client ==1.8.0 +uvicorn == 0.32.0 +prettytable == 3.11.0 \ No newline at end of file diff --git a/src/servers/game_status/src/applications/client.py b/src/servers/game_status/src/applications/client.py index ac0ce863b..2222173d8 100644 --- a/src/servers/game_status/src/applications/client.py +++ b/src/servers/game_status/src/applications/client.py @@ -58,6 +58,6 @@ def decrypt_message(self, buffer: bytes) -> bytes: return self.crypto.decrypt(buffer) - def create_switcher(self, buffer: bytes) -> SwitcherBase: + def _create_switcher(self, buffer: bytes) -> SwitcherBase: from servers.game_status.src.applications.switcher import Switcher return Switcher(self, buffer.decode()) diff --git a/src/servers/natneg/src/applications/client.py b/src/servers/natneg/src/applications/client.py index da599cac1..9f446b17f 100644 --- a/src/servers/natneg/src/applications/client.py +++ b/src/servers/natneg/src/applications/client.py @@ -11,7 +11,7 @@ def __init__(self, connection: UdpConnection, server_config: ServerConfig, logge super().__init__(connection, server_config, logger) self.is_log_raw = True - def create_switcher(self, buffer: bytes): + def _create_switcher(self, buffer: bytes): assert isinstance(buffer, bytes) from servers.natneg.src.applications.switcher import CmdSwitcher diff --git a/src/servers/presence_connection_manager/src/applications/client.py b/src/servers/presence_connection_manager/src/applications/client.py index 1753275ae..cfcefcd37 100644 --- a/src/servers/presence_connection_manager/src/applications/client.py +++ b/src/servers/presence_connection_manager/src/applications/client.py @@ -56,6 +56,6 @@ def on_connected(self) -> None: self.log_network_sending(buffer) self.connection.send(buffer) - def create_switcher(self, buffer: bytes) -> SwitcherBase: + def _create_switcher(self, buffer: bytes) -> SwitcherBase: from servers.presence_connection_manager.src.applications.switcher import Switcher return Switcher(self, buffer.decode()) diff --git a/src/servers/presence_search_player/src/applications/client.py b/src/servers/presence_search_player/src/applications/client.py index 6687f8d1d..442f59b25 100644 --- a/src/servers/presence_search_player/src/applications/client.py +++ b/src/servers/presence_search_player/src/applications/client.py @@ -11,7 +11,8 @@ class Client(ClientBase): def __init__(self, connection: TcpConnection, server_config: ServerConfig, logger: LogWriter): super().__init__(connection, server_config, logger) - - def _create_switcher(self, buffer) -> SwitcherBase: + + def _create_switcher(self, buffer: bytes) -> SwitcherBase: from servers.presence_search_player.src.applications.switcher import CmdSwitcher - return CmdSwitcher(self, buffer) + temp_buffer = buffer.decode() + return CmdSwitcher(self, temp_buffer) diff --git a/src/servers/presence_search_player/src/applications/switcher.py b/src/servers/presence_search_player/src/applications/switcher.py index 0520f0406..ad8c5a960 100644 --- a/src/servers/presence_search_player/src/applications/switcher.py +++ b/src/servers/presence_search_player/src/applications/switcher.py @@ -13,9 +13,9 @@ class CmdSwitcher(SwitcherBase): _raw_request: str def __init__(self, client: Client, raw_request: str): - super().__init__(client, raw_request) assert isinstance(client, Client) assert isinstance(raw_request, str) + super().__init__(client, raw_request) def _process_raw_request(self): if self._raw_request[0] != "\\": diff --git a/src/servers/presence_search_player/tests/mock_objects.py b/src/servers/presence_search_player/tests/mock_objects.py index 43bda1e93..64f4fb1b3 100644 --- a/src/servers/presence_search_player/tests/mock_objects.py +++ b/src/servers/presence_search_player/tests/mock_objects.py @@ -3,6 +3,7 @@ from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url from servers.presence_search_player.src.applications.client import Client from servers.presence_search_player.src.applications.handlers import CheckHandler, SearchHandler +from servers.presence_search_player.src.contracts.results import CheckResult, SearchResult class ClientMock(Client): @@ -17,8 +18,10 @@ def create_client() -> Client: config=CONFIG.servers["PresenceSearchPlayer"], t_client=ClientMock, logger=logger) config = CONFIG.servers["PresenceSearchPlayer"] - create_mock_url(config, CheckHandler, {"profile_id": 0}) - create_mock_url(config, SearchHandler, {"result": [{"profile_id": 0, "nick": "spyguy", "uniquenick": "spyguy", - "email": "spyguy@unispy.org", "firstname": "spy", "lastname": "guy", "namespace_id": 0}]}) + create_mock_url(config, CheckHandler, CheckResult.model_validate( + {"profile_id": 0}).model_dump()) + + create_mock_url(config, SearchHandler, SearchResult.model_validate({"result": [{"profile_id": 0, "nick": "spyguy", "uniquenick": "spyguy", + "email": "spyguy@unispy.org", "firstname": "spy", "lastname": "guy", "namespace_id": 0}]}).model_dump()) return cast(Client, conn._client) diff --git a/src/servers/query_report/__init__.py b/src/servers/query_report/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/query_report/src/aggregates/game_server_info.py b/src/servers/query_report/src/aggregates/game_server_info.py index 5b53f467f..b0ba822cf 100644 --- a/src/servers/query_report/src/aggregates/game_server_info.py +++ b/src/servers/query_report/src/aggregates/game_server_info.py @@ -1,12 +1,13 @@ from datetime import datetime -import socket from uuid import UUID +from pydantic import BaseModel + from library.src.extentions.bytes_extentions import ip_to_4_bytes from servers.query_report.src.v2.aggregates.enums import GameServerStatus -class GameServerInfo: +class GameServerInfo(BaseModel): server_id: UUID host_ip_address: str instant_key: int diff --git a/src/servers/query_report/src/aggregates/peer_room_info.py b/src/servers/query_report/src/aggregates/peer_room_info.py index 5014ddb8c..ea6d636a6 100644 --- a/src/servers/query_report/src/aggregates/peer_room_info.py +++ b/src/servers/query_report/src/aggregates/peer_room_info.py @@ -1,5 +1,7 @@ from uuid import UUID +from pydantic import BaseModel + dd = { "groupid": "groupid", @@ -15,7 +17,7 @@ } -class PeerRoomInfo: +class PeerRoomInfo(BaseModel): server_id: UUID game_name: str group_id: int diff --git a/src/servers/query_report/src/applications/client.py b/src/servers/query_report/src/applications/client.py index 52c9ba41a..588f23993 100644 --- a/src/servers/query_report/src/applications/client.py +++ b/src/servers/query_report/src/applications/client.py @@ -1,13 +1,14 @@ from library.src.abstractions.client import ClientBase # import servers.query_report.v1 -from servers.query_report.src.v2.applications.switcher import CmdSwitcher as V2CmdSwitcher class Client(ClientBase): is_log_raw: bool = True - def _create_switcher(self, buffer): + def _create_switcher(self, buffer: bytes): + from servers.query_report.src.v2.applications.switcher import CmdSwitcher as V2CmdSwitcher + assert isinstance(buffer, bytes) if buffer[0] == ord("\\"): raise NotImplementedError("v1 protocol not implemented") return V1CmdSwitcher(self, (buffer)) diff --git a/src/servers/query_report/src/v2/abstractions/contracts.py b/src/servers/query_report/src/v2/abstractions/contracts.py index 9e961f5e5..ed039108a 100644 --- a/src/servers/query_report/src/v2/abstractions/contracts.py +++ b/src/servers/query_report/src/v2/abstractions/contracts.py @@ -19,7 +19,7 @@ def parse(self): if len(self.raw_request) < 3: raise QRException("request length not valid") self.command_name = RequestType(self.raw_request[0]) - self.instant_key = int(self.raw_request[1:5]) + self.instant_key = int.from_bytes(self.raw_request[1:5]) class ResultBase(library.src.abstractions.contracts.ResultBase): @@ -30,3 +30,10 @@ class ResponseBase(library.src.abstractions.contracts.ResponseBase): _result: ResultBase _request: RequestBase sending_buffer: bytes + + def build(self) -> None: + data = bytearray() + data.extend(MAGIC_DATA) + data.append(self._request.command_name.value) + data.extend(self._request.instant_key.to_bytes(4)) + self.sending_buffer = bytes(data) diff --git a/src/servers/query_report/src/v2/applications/handlers.py b/src/servers/query_report/src/v2/applications/handlers.py index 30034927f..d31e324ce 100644 --- a/src/servers/query_report/src/v2/applications/handlers.py +++ b/src/servers/query_report/src/v2/applications/handlers.py @@ -76,7 +76,16 @@ class HeartBeatHandler(CmdHandlerBase): def __init__(self, client: Client, request: HeartBeatRequest) -> None: assert isinstance(request, HeartBeatRequest) super().__init__(client, request) - self._result_cls = HeartBeatResult + + def _request_check(self) -> None: + super()._request_check() + self._request.remote_ip_address = self._client.connection.remote_ip + self._request.remote_port = self._client.connection.remote_port + + def _feach_data(self): + self._result = HeartBeatResult( + remote_ip_address=self._request.remote_ip_address, + remote_port=self._request.remote_port) def _response_construct(self) -> None: self._response = HeartBeatResponse(self._request, self._result) diff --git a/src/servers/query_report/src/v2/contracts/requests.py b/src/servers/query_report/src/v2/contracts/requests.py index a26ff3934..cfc9e37f0 100644 --- a/src/servers/query_report/src/v2/contracts/requests.py +++ b/src/servers/query_report/src/v2/contracts/requests.py @@ -15,12 +15,11 @@ class AvaliableRequest(RequestBase): def parse(self): super().parse() - for i in range(len(PREFIX)): - if self.raw_request[i] != PREFIX[i]: - raise QRException("Avaliable request prefix is invalid.") + if self.raw_request[:len(PREFIX)] != PREFIX: + raise QRException("Avaliable request prefix is invalid.") # postfix check - if self.raw_request[len(self.raw_request) - 1] != POSTFIX: + if self.raw_request[len(self.raw_request)-1].to_bytes() != POSTFIX: raise QRException("Avaliable request postfix is invalid.") @@ -54,6 +53,9 @@ class HeartBeatRequest(RequestBase): team_data: list[dict[str, str]] server_status: GameServerStatus group_id: int + remote_ip_address: str + remote_port: int + game_name: str def parse(self): super().parse() @@ -61,8 +63,19 @@ def parse(self): player_length, team_length = 0, 0 self.data_partition = get_string(self.raw_request[5:]) - player_pos = self.data_partition.index("player_\0", 0) - team_pos = self.data_partition.index("team_t\0", 0) + game_key_val = self.data_partition.split("\x00") + index_gn = game_key_val.index("gamename") + self.game_name = game_key_val[index_gn+1] + + try: + player_pos = self.data_partition.index("player_\0", 0) + except ValueError: + player_pos = -1 + + try: + team_pos = self.data_partition.index("team_t\0", 0) + except ValueError: + team_pos = -1 if player_pos != -1 and team_pos != -1: player_length = team_pos - player_pos @@ -122,11 +135,12 @@ def parse_server_data(self, server_data_str: str): if "statechanged" not in self.server_data: self.server_status = GameServerStatus.NORMAL else: - self.server_status = GameServerStatus[self.server_data["statechanged"]] + self.server_status = GameServerStatus( + int(self.server_data["statechanged"])) def parse_player_data(self, player_data_str: str): self.player_data = [] - player_count = int(player_data_str[0]) + player_count = int.from_bytes(player_data_str[0].encode()) player_data_str = player_data_str[1:] index_of_key = player_data_str.index("\0\0", 0) @@ -152,7 +166,7 @@ def parse_player_data(self, player_data_str: str): def parse_team_data(self, team_data_str: str): self.team_data = [] - team_count = int(team_data_str[0]) + team_count = int.from_bytes(team_data_str[0].encode()) team_data_str = team_data_str[1:] end_key_index = team_data_str.index("\0\0", 0) diff --git a/src/servers/query_report/tests/game_tests.py b/src/servers/query_report/tests/game_tests.py new file mode 100644 index 000000000..a711aaa18 --- /dev/null +++ b/src/servers/query_report/tests/game_tests.py @@ -0,0 +1,18 @@ +import unittest + +import responses +from servers.query_report.tests.mock_objects import create_client + + +class GameTests(unittest.TestCase): + @responses.activate + def test_faltout2(self): + raw = b'\x03\xc2\x94\x1c\xe7localip0\x00192.168.0.50\x00localip1\x00172.16.74.1\x00localip2\x00172.17.0.1\x00localip3\x00192.168.122.1\x00localip4\x00172.16.65.1\x00localport\x0023756\x00natneg\x001\x00statechanged\x001\x00gamename\x00flatout2pc\x00publicip\x000\x00publicport\x000\x00hostkey\x00-820966322\x00hostname\x00Spore\x00gamever\x00FO14\x00gametype\x00race\x00gamevariant\x00normal_race\x00gamemode\x00openwaiting\x00numplayers\x001\x00maxplayers\x008\x00mapname\x00Timberlands_1\x00timelimit\x000\x00password\x000\x00car_type\x000\x00car_class\x000\x00races_p\x00100\x00derbies_p\x000\x00stunts_p\x000\x00normal_race_p\x00100\x00pong_race_p\x000\x00wreck_derby_p\x000\x00survivor_derby_p\x000\x00frag_derby_p\x000\x00tag_p\x000\x00upgrades\x002\x00nitro_regeneration\x002\x00damage_level\x002\x00derby_damage_level\x001\x00next_race_type\x00normal_race\x00laps_or_timelimit\x004\x00num_races\x001\x00num_derbies\x000\x00num_stunts\x000\x00datachecksum\x003546d58093237eb33b2a96bb813370d846ffcec8\x00\x00\x00\x00\x00\x00\x00\x00' + client = create_client() + client.on_received(raw) + + @responses.activate + def test_worm3d(self): + raw = b'\x03Q]\xa0\xe8localip0\x00192.168.0.60\x00localport\x006500\x00natneg\x001\x00statechanged\x003\x00gamename\x00worms3\x00hostname\x00test\x00gamemode\x00openstaging\x00groupid\x00622\x00numplayers\x001\x00maxplayers\x002\x00hostname\x00test\x00hostport\x00\x00maxplayers\x002\x00numplayers\x001\x00SchemeChanging\x000\x00gamever\x001073\x00gametype\x00\x00mapname\x00\x00firewall\x000\x00publicip\x00255.255.255.255\x00privateip\x00192.168.0.60\x00gamemode\x00openstaging\x00val\x000\x00password\x000\x00\x00\x00\x01player_\x00ping_\x00hostname\x00hostport\x00maxplayers\x00numplayers\x00SchemeChanging\x00gamever\x00gametype\x00mapname\x00firewall\x00publicip\x00privateip\x00gamemode\x00val\x00password\x00\x00worms10\x000\x00\x00\x00\x00\x00\x001073\x00\x00\x001\x00255.255.255.255\x00192.168.0.60\x00\x000\x00\x00\x00\x00hostname\x00hostport\x00maxplayers\x00numplayers\x00SchemeChanging\x00gamever\x00gametype\x00mapname\x00firewall\x00publicip\x00privateip\x00gamemode\x00val\x00password\x00\x00' + client = create_client() + client.on_received(raw) diff --git a/src/servers/query_report/tests/mock_objects.py b/src/servers/query_report/tests/mock_objects.py new file mode 100644 index 000000000..190af65d8 --- /dev/null +++ b/src/servers/query_report/tests/mock_objects.py @@ -0,0 +1,24 @@ +from typing import cast +from library.src.configs import CONFIG +from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url +from servers.query_report.src.applications.client import Client +from servers.query_report.src.v2.applications.handlers import HeartBeatHandler +from servers.query_report.src.v2.contracts.results import HeartBeatResult + + +class ClientMock(Client): + pass + + +def create_client() -> Client: + handler = RequestHandlerMock() + logger = LogMock() + conn = ConnectionMock( + handler=handler, + config=CONFIG.servers["QueryReport"], t_client=ClientMock, + logger=logger) + config = CONFIG.servers["QueryReport"] + create_mock_url(config, HeartBeatHandler, HeartBeatResult.model_validate( + {"remote_ip_address": conn.remote_ip, "remote_port": conn.remote_port}).model_dump()) + + return cast(Client, conn._client) diff --git a/src/servers/query_report/tests/request_tests.py b/src/servers/query_report/tests/request_tests.py new file mode 100644 index 000000000..95b4536b5 --- /dev/null +++ b/src/servers/query_report/tests/request_tests.py @@ -0,0 +1,52 @@ +import unittest + +from servers.query_report.src.v2.aggregates.enums import RequestType +from servers.query_report.src.v2.contracts.requests import AvaliableRequest, ChallengeRequest, EchoRequest, HeartBeatRequest + + +class RequestTest(unittest.TestCase): + def test_available(self): + raw = bytes([0x09, # packet type + 0x00, 0x00, 0x00, 0x00, # instant key + 0x09, 0x00, 0x00, 0x00, 0x00, # prefix + 0x67, 0x61, 0x6D, 0x65, 0x73, 0x70, 0x79, # gamename + 0x00]) + request = AvaliableRequest(raw) + request.parse() + self.assertEqual(RequestType.AVALIABLE_CHECK, request.command_name) + self.assertEqual(0, request.instant_key) + + def test_challenge(self): + raw = bytes([ + 0x01, # packet type + 0x00, 0x00, 0x00, 0x00, # instant key + 0x67, 0x61, 0x6D, 0x65, 0x73, 0x70, 0x79, # gamename + 0x00 + ]) + request = ChallengeRequest(raw) + request.parse() + self.assertEqual(RequestType.CHALLENGE, request.command_name) + self.assertEqual(0, request.instant_key) + + def test_echo_request(self): + raw = bytes([ + 0x02, # packet type + 0x00, 0x00, 0x00, 0x00, # instant key + 0x67, 0x61, 0x6D, 0x65, 0x73, 0x70, 0x79, # gamename + 0x00 + ]) + request = EchoRequest(raw) + request.parse() + self.assertEqual(RequestType.ECHO, request.command_name) + self.assertEqual(0, request.instant_key) + + def test_heartbeat(self): + raw = bytes([ + 0x03, 0xae, 0x1f, 0x77, 0x64, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x70, 0x30, 0x00, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x39, 0x00, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x31, 0x31, 0x31, 0x31, 0x31, 0x00, 0x6e, 0x61, 0x74, 0x6e, 0x65, 0x67, 0x00, 0x31, 0x00, 0x73, 0x74, 0x61, 0x74, 0x65, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x00, 0x33, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x67, 0x6d, 0x74, 0x65, 0x73, 0x74, 0x00, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x70, 0x79, 0x20, 0x51, 0x52, 0x32, 0x20, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x76, 0x65, 0x72, 0x00, 0x32, 0x2e, 0x30, 0x30, 0x00, 0x68, 0x6f, 0x73, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x32, 0x35, 0x30, 0x30, 0x30, 0x00, 0x6d, 0x61, 0x70, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x67, 0x6d, 0x74, 0x6d, 0x61, 0x70, 0x31, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x00, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x00, 0x6e, 0x75, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00, 0x36, 0x00, 0x6e, 0x75, 0x6d, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x00, 0x32, 0x00, 0x6d, 0x61, 0x78, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00, 0x33, 0x32, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x00, 0x6f, 0x70, 0x65, 0x6e, 0x70, 0x6c, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x00, 0x74, 0x65, 0x61, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x31, 0x00, 0x66, 0x72, 0x61, 0x67, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00, 0x30, 0x00, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00, 0x34, 0x30, 0x00, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x79, 0x00, 0x38, 0x30, 0x30, 0x00, 0x72, 0x61, 0x6e, 0x6b, 0x69, 0x6e, 0x67, 0x6f, 0x6e, 0x00, 0x31, 0x00, 0x00, 0x00, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x00, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x00, 0x64, 0x65, 0x61, 0x74, 0x68, 0x73, 0x5f, 0x00, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x00, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x00, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x00, 0x00, 0x4a, 0x6f, 0x65, 0x20, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x00, 0x32, 0x37, 0x00, 0x36, 0x00, 0x31, 0x32, 0x33, 0x00, 0x31, 0x00, 0x34, 0x30, 0x39, 0x00, 0x4c, 0x33, 0x33, 0x74, 0x20, 0x30, 0x6e, 0x33, 0x00, 0x36, 0x00, 0x32, 0x33, 0x00, 0x32, 0x37, 0x37, 0x00, 0x30, 0x00, 0x36, 0x37, 0x33, 0x00, 0x52, 0x61, 0x70, 0x74, 0x6f, 0x72, 0x00, 0x33, 0x30, 0x00, 0x31, 0x00, 0x31, 0x34, 0x36, 0x00, 0x31, 0x00, 0x37, 0x30, 0x31, 0x00, 0x47, 0x72, 0x38, 0x31, 0x00, 0x32, 0x31, 0x00, 0x31, 0x36, 0x00, 0x31, 0x32, 0x35, 0x00, 0x31, 0x00, 0x35, 0x38, 0x32, 0x00, 0x46, 0x6c, 0x75, 0x62, 0x62, 0x65, 0x72, 0x00, 0x33, 0x00, 0x32, 0x31, 0x00, 0x31, 0x31, 0x30, 0x00, 0x30, 0x00, 0x32, 0x39, 0x38, 0x00, 0x53, 0x61, 0x72, 0x67, 0x65, 0x00, 0x33, 0x00, 0x32, 0x38, 0x00, 0x31, 0x32, 0x35, 0x00, 0x31, 0x00, 0x35, 0x39, 0x30, 0x00, 0x00, 0x02, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x74, 0x00, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x00, 0x61, 0x76, 0x67, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x00, 0x00, 0x52, 0x65, 0x64, 0x00, 0x32, 0x39, 0x34, 0x00, 0x33, 0x37, 0x39, 0x00, 0x42, 0x6c, 0x75, 0x65, 0x00, 0x38, 0x39, 0x00, 0x33, 0x38, 0x33, 0x00]) + request = HeartBeatRequest(raw) + request.parse() + self.assertEqual("gmtest", request.game_name) + self.assertEqual(2921297764, request.instant_key) + self.assertEqual(6, len(request.player_data)) + self.assertEqual(19, len(request.server_data)) + self.assertEqual(2, len(request.team_data)) diff --git a/src/servers/server_browser/__init__.py b/src/servers/server_browser/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/server_browser/src/v2/abstractions/contracts.py b/src/servers/server_browser/src/v2/abstractions/contracts.py index e56593acd..f04c382a6 100644 --- a/src/servers/server_browser/src/v2/abstractions/contracts.py +++ b/src/servers/server_browser/src/v2/abstractions/contracts.py @@ -1,18 +1,18 @@ from socket import inet_ntoa -from typing import List, Optional +from typing import TYPE_CHECKING import library.src.abstractions.contracts from library.src.extentions.encoding import get_bytes from servers.server_browser.src.v2.aggregations.encryption import SERVER_CHALLENGE from servers.server_browser.src.v2.aggregations.string_flags import STRING_SPLITER -from servers.server_browser.src.v2.contracts.results import AdHocResult from servers.server_browser.src.v2.aggregations.enums import ( DataKeyType, GameServerFlags, RequestType, ServerListUpdateOption, ) - +if TYPE_CHECKING: + from servers.server_browser.src.v2.contracts.results import AdHocResult class RequestBase(library.src.abstractions.contracts.RequestBase): @@ -127,5 +127,5 @@ def parse(self) -> None: class AdHocResponseBase(ResponseBase): - _result: AdHocResult + _result: "AdHocResult" _buffer: bytearray diff --git a/src/servers/server_browser/src/v2/applications/client.py b/src/servers/server_browser/src/v2/applications/client.py index ff1ce5e89..c6e479eba 100644 --- a/src/servers/server_browser/src/v2/applications/client.py +++ b/src/servers/server_browser/src/v2/applications/client.py @@ -1,6 +1,9 @@ +from typing import TYPE_CHECKING from library.src.abstractions.client import ClientBase, ClientInfoBase from library.src.abstractions.enctypt_base import EncryptBase from servers.server_browser.src.v2.aggregations.enums import ServerListUpdateOption +if TYPE_CHECKING: + from library.src.abstractions.switcher import SwitcherBase class ClientInfo(ClientInfoBase): @@ -15,5 +18,6 @@ class Client(ClientBase): info: ClientInfo crypto: EncryptBase - def create_switcher(self, buffer) -> None: - pass + def _create_switcher(self, buffer: bytes) -> "SwitcherBase": + from servers.server_browser.src.v2.applications.switcher import CmdSwitcher + return CmdSwitcher(self, buffer) diff --git a/src/servers/server_browser/src/v2/applications/switcher.py b/src/servers/server_browser/src/v2/applications/switcher.py new file mode 100644 index 000000000..7f5e60d34 --- /dev/null +++ b/src/servers/server_browser/src/v2/applications/switcher.py @@ -0,0 +1,32 @@ +from typing import TYPE_CHECKING, Optional, cast +from library.src.abstractions.switcher import SwitcherBase +from library.src.exceptions.general import UniSpyException +from servers.server_browser.src.v2.abstractions.handlers import CmdHandlerBase +from servers.server_browser.src.v2.aggregations.enums import RequestType +from servers.server_browser.src.v2.applications.client import Client +from servers.server_browser.src.v2.applications.handlers import SendMessageHandler, ServerInfoHandler, ServerListHandler +from servers.server_browser.src.v2.contracts.requests import SendMessageRequest, ServerInfoRequest, ServerListRequest + + +class CmdSwitcher(SwitcherBase): + _raw_request: bytes + + def _process_raw_request(self) -> None: + if len(self._raw_request) < 4: + raise UniSpyException("Invalid request") + name = RequestType(self._raw_request[2]) + self._requests.append((name, self._raw_request)) + + def _create_cmd_handlers(self, name: int, raw_request: bytes) -> Optional[CmdHandlerBase]: + req = raw_request + if TYPE_CHECKING: + self._client = cast(Client, self._client) + match name: + case RequestType.SERVER_LIST_REQUEST: + return ServerListHandler(self._client, ServerListRequest(req)) + case RequestType.SERVER_INFO_REQUEST: + return ServerInfoHandler(self._client, ServerInfoRequest(req)) + case RequestType.SEND_MESSAGE_REQUEST: + return SendMessageHandler(self._client, SendMessageRequest(req)) + case _: + return None diff --git a/src/servers/server_browser/tests/game_tests.py b/src/servers/server_browser/tests/game_tests.py new file mode 100644 index 000000000..6e1f15bba --- /dev/null +++ b/src/servers/server_browser/tests/game_tests.py @@ -0,0 +1,17 @@ +import unittest + +import responses +# from servers.server_browser.tests.mock_objects import create_v2_client +from servers.query_report.tests.mock_objects import create_client +from servers.server_browser.tests.mock_objects import create_v2_client + + +class GameTest(unittest.TestCase): + @responses.activate + def test_gmtest_20200309(self): + qr_raw = b'\x03\xea+\xafPlocalip0\x00192.168.122.226\x00localport\x0011111\x00natneg\x001\x00statechanged\x003\x00gamename\x00gmtest\x00hostname\x00GameSpy QR2 Sample\x00gamever\x002.00\x00hostport\x0025000\x00mapname\x00gmtmap1\x00gametype\x00arena\x00numplayers\x0010\x00numteams\x002\x00maxplayers\x0032\x00gamemode\x00openplaying\x00teamplay\x001\x00fraglimit\x000\x00timelimit\x0040\x00gravity\x00800\x00rankingon\x001\x00\x00\x00\nplayer_\x00score_\x00deaths_\x00ping_\x00team_\x00time_\x00\x00Joe Player\x004\x002\x0077\x000\x00185\x00L33t 0n3\x006\x0024\x0068\x001\x00820\x00Raptor\x0010\x0029\x00216\x001\x00664\x00Gr81\x008\x006\x00327\x001\x00697\x00Flubber\x0015\x002\x00179\x000\x0048\x00Sarge\x009\x0012\x00337\x000\x00296\x00Void\x0027\x0029\x0045\x000\x00355\x00runaway\x0024\x004\x00197\x001\x00428\x00Ph3ar\x0030\x0030\x00339\x001\x00525\x00wh00t\x0031\x0028\x00269\x001\x0077\x00\x00\x02team_t\x00score_t\x00avgping_t\x00\x00Red\x00487\x00336\x00Blue\x0082\x00458\x00' + qr_client = create_client() + qr_client.on_received(qr_raw) + sb_raw = b'\x00\t\x01\xc0\xa8z\xe2+g' + sb_client = create_v2_client() + sb_client.on_received(sb_raw) diff --git a/src/servers/server_browser/tests/mock_objects.py b/src/servers/server_browser/tests/mock_objects.py new file mode 100644 index 000000000..fd727ca9d --- /dev/null +++ b/src/servers/server_browser/tests/mock_objects.py @@ -0,0 +1,38 @@ +from typing import cast +from library.src.configs import CONFIG +from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url +from servers.query_report.src.v2.applications.handlers import HeartBeatHandler +from servers.query_report.src.v2.contracts.results import HeartBeatResult +from servers.server_browser.src.v2.applications.client import Client + + +class ClientMock(Client): + pass + + +def create_v2_client() -> Client: + handler = RequestHandlerMock() + logger = LogMock() + conn = ConnectionMock( + handler=handler, + config=CONFIG.servers["ServerBrowserV2"], t_client=ClientMock, + logger=logger) + config = CONFIG.servers["ServerBrowserV2"] + create_mock_url(config, HeartBeatHandler, HeartBeatResult.model_validate( + {"remote_ip_address": conn.remote_ip, "remote_port": conn.remote_port}).model_dump()) + + return cast(Client, conn._client) + + +def create_v1_client() -> Client: + handler = RequestHandlerMock() + logger = LogMock() + conn = ConnectionMock( + handler=handler, + config=CONFIG.servers["ServerBrowserV1"], t_client=ClientMock, + logger=logger) + config = CONFIG.servers["ServerBrowserV1"] + create_mock_url(config, HeartBeatHandler, HeartBeatResult.model_validate( + {"remote_ip_address": conn.remote_ip, "remote_port": conn.remote_port}).model_dump()) + + return cast(Client, conn._client) diff --git a/src/servers/web_services/src/applications/client.py b/src/servers/web_services/src/applications/client.py index a1f474a42..71d228c00 100644 --- a/src/servers/web_services/src/applications/client.py +++ b/src/servers/web_services/src/applications/client.py @@ -41,7 +41,7 @@ def on_received(self, buffer: str) -> None: assert isinstance(buffer, str) super().on_received(buffer) - def create_switcher(self, buffer: str) -> SwitcherBase: + def _create_switcher(self, buffer: str) -> SwitcherBase: assert isinstance(buffer, str) from servers.web_services.src.applications.switcher import Switcher return Switcher(self, buffer) diff --git a/src/unispy b/src/unispy new file mode 100644 index 000000000..2d124b195 --- /dev/null +++ b/src/unispy @@ -0,0 +1,3 @@ +2024-10-29 02:29:48 [unispy] [INFO]: [172.19.0.5:35003] [recv]: b'Hello, UDP!\n' +2024-10-29 02:33:53 [unispy] [INFO]: [172.19.0.5:47697] [recv]: b'Hello, UDP!\n' +2024-10-29 02:33:59 [unispy] [INFO]: [172.19.0.5:47697]: Invalid request received! From 7c130bca94c0074cadfee07b872f5b91840b26f3 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 29 Oct 2024 09:24:31 +0000 Subject: [PATCH 130/231] refactor: adding sb tests --- .../src/aggregates/game_server_info.py | 4 ++-- .../query_report/src/v2/contracts/results.py | 2 +- .../src/v2/abstractions/contracts.py | 13 +++++++++--- .../src/v2/applications/handlers.py | 12 ++++++++--- .../src/v2/contracts/responses.py | 21 ++++++++++--------- .../src/v2/contracts/results.py | 5 +++-- .../server_browser/tests/mock_objects.py | 15 ++++++++----- 7 files changed, 46 insertions(+), 26 deletions(-) diff --git a/src/servers/query_report/src/aggregates/game_server_info.py b/src/servers/query_report/src/aggregates/game_server_info.py index b0ba822cf..cab8dc42c 100644 --- a/src/servers/query_report/src/aggregates/game_server_info.py +++ b/src/servers/query_report/src/aggregates/game_server_info.py @@ -14,7 +14,7 @@ class GameServerInfo(BaseModel): game_name: str query_report_port: int - last_heart_beart_received_time: datetime + last_heart_beat_received_time: datetime status: GameServerStatus server_data: dict[str, str] player_data: list[dict[str, str]] @@ -22,7 +22,7 @@ class GameServerInfo(BaseModel): @property def query_report_port_bytes(self) -> bytes: - return self.query_report_port.to_bytes(2, "big") + return self.query_report_port.to_bytes(2) @property def host_ip_address_bytes(self) -> bytes: diff --git a/src/servers/query_report/src/v2/contracts/results.py b/src/servers/query_report/src/v2/contracts/results.py index 3a83f0a22..b852911b4 100644 --- a/src/servers/query_report/src/v2/contracts/results.py +++ b/src/servers/query_report/src/v2/contracts/results.py @@ -18,6 +18,6 @@ class EchoResult(ResultBase): class HeartBeatResult(ResultBase): + packet_type: PacketType = PacketType.HEARTBEAT remote_ip_address:str remote_port:int - packet_type: PacketType = PacketType.HEARTBEAT diff --git a/src/servers/server_browser/src/v2/abstractions/contracts.py b/src/servers/server_browser/src/v2/abstractions/contracts.py index f04c382a6..b6396658c 100644 --- a/src/servers/server_browser/src/v2/abstractions/contracts.py +++ b/src/servers/server_browser/src/v2/abstractions/contracts.py @@ -3,6 +3,7 @@ import library.src.abstractions.contracts from library.src.extentions.encoding import get_bytes +from servers.query_report.src.aggregates.game_server_info import GameServerInfo from servers.server_browser.src.v2.aggregations.encryption import SERVER_CHALLENGE from servers.server_browser.src.v2.aggregations.string_flags import STRING_SPLITER from servers.server_browser.src.v2.aggregations.enums import ( @@ -11,8 +12,6 @@ RequestType, ServerListUpdateOption, ) -if TYPE_CHECKING: - from servers.server_browser.src.v2.contracts.results import AdHocResult class RequestBase(library.src.abstractions.contracts.RequestBase): @@ -114,6 +113,10 @@ def build_unique_value(self): self._servers_info_buffers.append(0) +class AdHocResultBase(ResultBase): + game_server_info: GameServerInfo + + class AdHocRequestBase(RequestBase): game_server_public_ip: str game_server_public_port: int @@ -127,5 +130,9 @@ def parse(self) -> None: class AdHocResponseBase(ResponseBase): - _result: "AdHocResult" + _result: AdHocResultBase _buffer: bytearray + + def __init__(self, request: RequestBase, result: ResultBase) -> None: + super().__init__(request, result) + self._buffer = bytearray() diff --git a/src/servers/server_browser/src/v2/applications/handlers.py b/src/servers/server_browser/src/v2/applications/handlers.py index d4e644ab9..ec0645c95 100644 --- a/src/servers/server_browser/src/v2/applications/handlers.py +++ b/src/servers/server_browser/src/v2/applications/handlers.py @@ -4,6 +4,7 @@ from servers.query_report.src.v2.contracts.requests import ClientMessageRequest from servers.query_report.src.v2.aggregates.enums import GameServerStatus, RequestType from servers.server_browser.src.aggregates.exceptions import ServerBrowserException +from servers.server_browser.src.v2.abstractions.contracts import RequestBase from servers.server_browser.src.v2.contracts.requests import ( SendMessageRequest, ServerInfoRequest, @@ -17,7 +18,7 @@ UpdateServerInfoResponse, ) from servers.server_browser.src.v2.contracts.results import ( - AdHocResult, + ServerInfoResult, ServerMainListResult, ) from servers.server_browser.src.v2.aggregations.enums import ( @@ -44,13 +45,14 @@ def get_clients(game_name: str): class AdHocHandler(CmdHandlerBase): _message: GameServerInfo # !fix this + _result: ServerInfoResult def __init__(self, message: GameServerInfo) -> None: self._log_current_class() self._message = message def handle(self) -> None: - result = AdHocResult(game_server_info=self._message) + result = ServerInfoResult(game_server_info=self._message) match (self._message.status): case ( status @@ -111,7 +113,11 @@ def _response_send(self) -> None: class ServerInfoHandler(CmdHandlerBase): _request: ServerInfoRequest - _result: AdHocResult + _result: ServerInfoResult + + def __init__(self, client: Client, request: RequestBase) -> None: + super().__init__(client, request) + self._result_cls = ServerInfoResult def _response_construct(self) -> None: if self._result.game_server_info is not None: diff --git a/src/servers/server_browser/src/v2/contracts/responses.py b/src/servers/server_browser/src/v2/contracts/responses.py index 0bbfa9fd9..1e7c4ad0e 100644 --- a/src/servers/server_browser/src/v2/contracts/responses.py +++ b/src/servers/server_browser/src/v2/contracts/responses.py @@ -9,8 +9,8 @@ from servers.server_browser.src.v2.aggregations.string_flags import * from servers.server_browser.src.v2.contracts.requests import ServerListRequest from servers.server_browser.src.v2.contracts.results import ( - AdHocResult, P2PGroupRoomListResult, + ServerInfoResult, ServerMainListResult, ) @@ -18,10 +18,10 @@ class DeleteServerInfoResponse(AdHocResponseBase): - _result: AdHocResult + _result: ServerInfoResult - def __init__(self, result: AdHocResult) -> None: - assert isinstance(result, AdHocResult) + def __init__(self, result: ServerInfoResult) -> None: + assert isinstance(result, ServerInfoResult) self._result = result def build(self): @@ -32,9 +32,10 @@ def build(self): class UpdateServerInfoResponse(AdHocResponseBase): - def __init__(self, result: AdHocResult) -> None: - assert isinstance(result, AdHocResult) + def __init__(self, result: ServerInfoResult) -> None: + assert isinstance(result, ServerInfoResult) self._result = result + self._buffer = bytearray() def build(self) -> None: self._buffer.append(ResponseType.PUSH_SERVER_MESSAGE) @@ -63,13 +64,13 @@ def __build_single_server_full_info(self): self._buffer.extend(team_data) @staticmethod - def _build_kv(data: dict): - buffer = [] + def _build_kv(data: dict)->bytearray: + buffer = bytearray() for k, v in data.items(): buffer.extend(get_bytes(k)) - buffer.append(STRING_SPLITER) + buffer.extend(STRING_SPLITER) buffer.extend(get_bytes(v)) - buffer.append(STRING_SPLITER) + buffer.extend(STRING_SPLITER) return buffer diff --git a/src/servers/server_browser/src/v2/contracts/results.py b/src/servers/server_browser/src/v2/contracts/results.py index e51763289..d998f38d1 100644 --- a/src/servers/server_browser/src/v2/contracts/results.py +++ b/src/servers/server_browser/src/v2/contracts/results.py @@ -1,13 +1,14 @@ from servers.query_report.src.aggregates.game_server_info import GameServerInfo from servers.query_report.src.aggregates.peer_room_info import PeerRoomInfo from servers.server_browser.src.v2.abstractions.contracts import ( + AdHocResultBase, ResultBase, ServerListUpdateOptionResultBase, ) -class AdHocResult(ResultBase): - game_server_info: GameServerInfo +class ServerInfoResult(AdHocResultBase): + pass class P2PGroupRoomListResult(ResultBase): diff --git a/src/servers/server_browser/tests/mock_objects.py b/src/servers/server_browser/tests/mock_objects.py index fd727ca9d..4f64ed451 100644 --- a/src/servers/server_browser/tests/mock_objects.py +++ b/src/servers/server_browser/tests/mock_objects.py @@ -1,9 +1,10 @@ from typing import cast from library.src.configs import CONFIG from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url -from servers.query_report.src.v2.applications.handlers import HeartBeatHandler -from servers.query_report.src.v2.contracts.results import HeartBeatResult from servers.server_browser.src.v2.applications.client import Client +from servers.server_browser.src.v2.applications.handlers import ServerInfoHandler, ServerListHandler +from servers.server_browser.src.v2.contracts.results import ServerInfoResult, ServerMainListResult +import json class ClientMock(Client): @@ -18,8 +19,12 @@ def create_v2_client() -> Client: config=CONFIG.servers["ServerBrowserV2"], t_client=ClientMock, logger=logger) config = CONFIG.servers["ServerBrowserV2"] - create_mock_url(config, HeartBeatHandler, HeartBeatResult.model_validate( - {"remote_ip_address": conn.remote_ip, "remote_port": conn.remote_port}).model_dump()) + + create_mock_url(config, ServerListHandler, json.loads( + ServerMainListResult.model_validate({"client_remote_ip": "127.0.0.1", "flag": 64, + "game_secret_key": "123567", "servers_info": []}).model_dump_json())) + create_mock_url(config, ServerInfoHandler, json.loads(ServerInfoResult.model_validate({"game_server_info": {"server_id": "550e8400-e29b-41d4-a716-446655440000", "host_ip_address": "192.168.1.1", "instant_key": 123456, "game_name": "Example Game", "query_report_port": 8080, "last_heart_beat_received_time": "2023-10-01T12:00:00Z", "status": 3, "server_data": { + "max_players": "100", "current_players": "50", "region": "US-East"}, "player_data": [{"player_id": "player1", "player_name": "Player One"}, {"player_id": "player2", "player_name": "Player Two"}], "team_data": [{"team_id": "team1", "team_name": "Team Alpha"}, {"team_id": "team2", "team_name": "Team Beta"}]}}).model_dump_json())) return cast(Client, conn._client) @@ -32,7 +37,7 @@ def create_v1_client() -> Client: config=CONFIG.servers["ServerBrowserV1"], t_client=ClientMock, logger=logger) config = CONFIG.servers["ServerBrowserV1"] - create_mock_url(config, HeartBeatHandler, HeartBeatResult.model_validate( + create_mock_url(config, ServerListHandler, ServerMainListResult.model_validate( {"remote_ip_address": conn.remote_ip, "remote_port": conn.remote_port}).model_dump()) return cast(Client, conn._client) From ee2ef5875abffdabfc687971a264006222ac53f4 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 30 Oct 2024 03:26:34 +0000 Subject: [PATCH 131/231] refactor: added sb unit tests --- .../src/aggregates/peer_room_info.py | 105 +++--------------- .../src/v2/abstractions/contracts.py | 11 +- .../src/v2/applications/handlers.py | 7 +- .../src/v2/contracts/requests.py | 7 +- .../src/v2/contracts/responses.py | 8 +- .../server_browser/tests/game_tests.py | 46 ++++++++ 6 files changed, 87 insertions(+), 97 deletions(-) diff --git a/src/servers/query_report/src/aggregates/peer_room_info.py b/src/servers/query_report/src/aggregates/peer_room_info.py index ea6d636a6..9315f4061 100644 --- a/src/servers/query_report/src/aggregates/peer_room_info.py +++ b/src/servers/query_report/src/aggregates/peer_room_info.py @@ -1,103 +1,34 @@ from uuid import UUID -from pydantic import BaseModel +from pydantic import BaseModel, Field - -dd = { - "groupid": "groupid", - "hostname": "hostname", - "number_of_waiting_player": "numwaiting", - "max_number_of_waiting_players": "maxwaiting", - "number_of_servers": "numservers", - "number_of_players": "numplayers", - "max_number_of_players": "maxplayers", - "password": "password", - "number_of_games": "numgames", - "number_of_playing_players": "numplaying", -} +from types import MappingProxyType class PeerRoomInfo(BaseModel): server_id: UUID game_name: str - group_id: int - room_name: str - # todo change to dict[str, object] - raw_key_values: dict + group_id: int = Field(..., alias='groupid') + room_name: str = Field(alias="hostname") + number_of_waiting: int = Field(default=0, alias="numwaiting") + max_waiting: int = Field(default=200, alias='maxwaiting') + number_of_servers: int = Field(default=0, alias="numservers") + number_of_players: int = Field(default=0, alias="numplayers") + max_players: int = Field(200, alias="maxplayers") + password: str = Field(default="", alias="password") + number_of_games: int = Field(default=0, alias="numgames") + number_of_playing: int = Field(default=0, alias="numplaying") def __init__(self, game_name, group_id, room_name) -> None: self.game_name = game_name self.group_id = group_id self.room_name = room_name - self.raw_key_values = { - "groupid": group_id, - "hostname": room_name, - "numwaiting": 0, - "maxwaiting": 200, - "numservers": 0, - "numplayers": 0, - "maxplayers": 200, - "password": "", - "numgames": 0, - "numplaying": 0, - } - - @property - def number_of_servers(self) -> int: - return int(self.raw_key_values["numservers"]) - - @number_of_servers.setter - def number_of_servers(self, value: int): - assert isinstance(value, int) - self.raw_key_values["numservers"] = value - - @property - def number_of_players(self) -> int: - return int(self.raw_key_values["numplayers"]) - - @number_of_players.setter - def number_of_players(self, value: int): - assert isinstance(value, int) - self.raw_key_values["numplayers"] = value - - @property - def max_number_of_waiting_players(self) -> int: - return int(self.raw_key_values["maxwaiting"]) - - @max_number_of_waiting_players.setter - def max_number_of_waiting_players(self, value: int): - assert isinstance(value, int) - self.raw_key_values["maxwaiting"] = value - - @property - def max_number_of_players(self) -> int: - return int(self.raw_key_values["maxplayers"]) - - @max_number_of_players.setter - def max_number_of_players(self, value: int): - assert isinstance(value, int) - self.raw_key_values["maxplayers"] = value - - @property - def number_of_games(self) -> int: - return int(self.raw_key_values["numgames"]) - - @number_of_games.setter - def number_of_games(self, value: int): - assert isinstance(value, int) - self.raw_key_values["numgames"] = value - - @property - def number_of_playing_players(self) -> int: - return self.raw_key_values["numplaying"] - - @number_of_playing_players.setter - def number_of_playing_players(self, value: int): - assert isinstance(value, int) - self.raw_key_values["numplaying"] = value - def get_gamespy_format_data(self): + def get_gamespy_dict(self) -> MappingProxyType: """ - convert everything to string + return a immutable dict """ - return {key: str(value) for key, value in self.raw_key_values.items()} + data = self.model_dump() + del data["server_id"] + del data["game_name"] + return MappingProxyType(data) diff --git a/src/servers/server_browser/src/v2/abstractions/contracts.py b/src/servers/server_browser/src/v2/abstractions/contracts.py index b6396658c..ac7808dcc 100644 --- a/src/servers/server_browser/src/v2/abstractions/contracts.py +++ b/src/servers/server_browser/src/v2/abstractions/contracts.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING import library.src.abstractions.contracts +from library.src.extentions.bytes_extentions import ip_to_4_bytes from library.src.extentions.encoding import get_bytes from servers.query_report.src.aggregates.game_server_info import GameServerInfo from servers.server_browser.src.v2.aggregations.encryption import SERVER_CHALLENGE @@ -13,6 +14,8 @@ ServerListUpdateOption, ) +QUERY_REPORT_DEFAULT_PORT: int = int(6500) + class RequestBase(library.src.abstractions.contracts.RequestBase): request_length: int @@ -65,7 +68,7 @@ def __init__(self, raw_request: bytes): class ServerListUpdateOptionResultBase(ResultBase): - client_remote_ip: bytes + client_remote_ip: str flag: GameServerFlags game_secret_key: str @@ -88,8 +91,10 @@ def __init__( def build(self) -> None: crypt_header = self.build_crypt_header() self._servers_info_buffers.extend(crypt_header) - self._servers_info_buffers.extend(self._result.client_remote_ip) - self._servers_info_buffers.extend(bytes(6500)) + self._servers_info_buffers.extend( + ip_to_4_bytes(self._result.client_remote_ip)) + self._servers_info_buffers.extend( + QUERY_REPORT_DEFAULT_PORT.to_bytes(4)) def build_crypt_header(self) -> list: # cryptHeader have 14 bytes, when we encrypt data we need skip the first 14 bytes diff --git a/src/servers/server_browser/src/v2/applications/handlers.py b/src/servers/server_browser/src/v2/applications/handlers.py index ec0645c95..3851605a3 100644 --- a/src/servers/server_browser/src/v2/applications/handlers.py +++ b/src/servers/server_browser/src/v2/applications/handlers.py @@ -127,8 +127,13 @@ def _response_construct(self) -> None: class ServerListHandler(CmdHandlerBase): _request: ServerListRequest _result: ServerMainListResult + _result_cls: type[ServerMainListResult] - def response_construct(self): + def __init__(self, client: Client, request: RequestBase) -> None: + super().__init__(client, request) + self._result_cls = ServerMainListResult + + def _response_construct(self) -> None: match self._request.update_option: case option if option in [ ServerListUpdateOption.SERVER_MAIN_LIST, diff --git a/src/servers/server_browser/src/v2/contracts/requests.py b/src/servers/server_browser/src/v2/contracts/requests.py index de15ad283..f4d0b0291 100644 --- a/src/servers/server_browser/src/v2/contracts/requests.py +++ b/src/servers/server_browser/src/v2/contracts/requests.py @@ -14,7 +14,7 @@ def parse(self) -> None: self.request_version = int(self.raw_request[2]) self.protocol_version = int(self.raw_request[3]) self.encoding_version = int(self.raw_request[4]) - self.game_version = int(self.raw_request[5:9]) + self.game_version = int.from_bytes(self.raw_request[5:9], "little") remain_data = self.raw_request[9:] dev_game_name_index = remain_data.index(0) @@ -35,8 +35,9 @@ def parse(self) -> None: self.keys = remain_data[:keys_index].decode().split("\\") remain_data = remain_data[keys_index + 1:] - byte_update_options = remain_data[:4][::-1] - self.update_option = ServerListUpdateOption(int(byte_update_options)) + byte_update_options = remain_data[:4] + self.update_option = ServerListUpdateOption( + int.from_bytes(byte_update_options)) remain_data = remain_data[4:] if self.update_option & ServerListUpdateOption.ALTERNATE_SOURCE_IP: diff --git a/src/servers/server_browser/src/v2/contracts/responses.py b/src/servers/server_browser/src/v2/contracts/responses.py index 1e7c4ad0e..10712faed 100644 --- a/src/servers/server_browser/src/v2/contracts/responses.py +++ b/src/servers/server_browser/src/v2/contracts/responses.py @@ -64,7 +64,7 @@ def __build_single_server_full_info(self): self._buffer.extend(team_data) @staticmethod - def _build_kv(data: dict)->bytearray: + def _build_kv(data: dict) -> bytearray: buffer = bytearray() for k, v in data.items(): buffer.extend(get_bytes(k)) @@ -90,11 +90,13 @@ def _build_servers_full_info(self): self._servers_info_buffers.append(GameServerFlags.HAS_KEYS_FLAG) group_id_bytes = room.group_id.to_bytes() self._servers_info_buffers.extend(group_id_bytes) + # get gamespy format dict + gamespy_dict = room.get_gamespy_dict() for key in self._request.keys: self._servers_info_buffers.extend(NTS_STRING_FLAG) value = ( - room.raw_key_values[key] - if key in room.raw_key_values.keys() + gamespy_dict[key] + if key in gamespy_dict.keys() else "" ) self._servers_info_buffers.extend(get_bytes(value)) diff --git a/src/servers/server_browser/tests/game_tests.py b/src/servers/server_browser/tests/game_tests.py index 6e1f15bba..8bfcd4ea1 100644 --- a/src/servers/server_browser/tests/game_tests.py +++ b/src/servers/server_browser/tests/game_tests.py @@ -15,3 +15,49 @@ def test_gmtest_20200309(self): sb_raw = b'\x00\t\x01\xc0\xa8z\xe2+g' sb_client = create_v2_client() sb_client.on_received(sb_raw) + + def test_anno1701_20220620(self): + qr_raws = [ + # available request + b'\t\x00\x00\x00\x00anno1701\x00', + b"\x03\x1dU\xcc\xcalocalip0\x00192.168.0.80\x00localport\x0021701\x00natneg\x001\x00statechanged\x003\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00377563076\x00numaiplayers\x00\x00openslots\x00\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00anno1701_220\x000\x000\x00\x00\x01\x00", + b"\x03\x1dU\xcc\xcalocalip0\x00192.168.0.80\x00localport\x0021701\x00natneg\x001\x00statechanged\x003\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00377563076\x00numaiplayers\x00\x00openslots\x00\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00anno1701_220\x000\x000\x00\x00\x01\x00", + b'\x03\x1dU\xcc\xcalocalip0\x00192.168.0.80\x00localport\x0021701\x00natneg\x001\x00statechanged\x001\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00109127620\x00numaiplayers\x000\x00openslots\x004\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00anno1701_220\x000\x000\x00\x00\x01\x00', + b"\x03\x1dU\xcc\xcalocalip0\x00192.168.0.80\x00localport\x0021701\x00natneg\x001\x00statechanged\x001\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00109127620\x00numaiplayers\x000\x00openslots\x004\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00anno1701_220\x000\x000\x00\x00\x01\x00", + b"\x03\x1dU\xcc\xcalocalip0\x00192.168.0.80\x00localport\x0021701\x00natneg\x001\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00109127620\x00numaiplayers\x000\x00openslots\x004\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00anno1701_220\x000\x000\x00\x00\x01\x00", + b'\x03\x1dU\xcc\xcalocalip0\x00192.168.0.80\x00localport\x0021701\x00natneg\x001\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00109127620\x00numaiplayers\x000\x00openslots\x004\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00anno1701_220\x000\x000\x00\x00\x01\x00', + b"\x03\x1dU\xcc\xcalocalip0\x00192.168.0.80\x00localport\x0021701\x00natneg\x001\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00109127620\x00numaiplayers\x000\x00openslots\x004\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00anno1701_220\x000\x000\x00\x00\x01\x00", + # client message + b"\xfe\xfd\x03\x1dU\xcc\xcaTTT\x00\x00[+2\xba\x00\x00\x00\x00\xc5T\x00\x00", + # keep alive + b"\x08\x1dU\xcc\xca" + ] + sb_raws = [ + b"\x00\x9a\x00\x01\x03\x8fU\x00\x00anno1701\x00anno1701\x00D:@o)Okhgroupid is null\x00\\hostname\\gamemode\\gamever\\gametype\\password\\mapname\\numplayers\\numaiplayers\\openslots\\gamevariant\x00\x00\x00\x00\x04", + b"\x00\x9a\x00\x01\x03\x8fU\x00\x00anno1701\x00anno1701\x00AHl='lhIgroupid is null\x00\\hostname\\gamemode\\gamever\\gametype\\password\\mapname\\numplayers\\numaiplayers\\openslots\\gamevariant\x00\x00\x00\x00\x04", + b"\x00\x9a\x00\x01\x03\x8fU\x00\x00anno1701\x00anno1701\x00TsFhHjvQgroupid is null\x00\\hostname\\gamemode\\gamever\\gametype\\password\\mapname\\numplayers\\numaiplayers\\openslots\\gamevariant\x00\x00\x00\x00\x04", + b"\x00\t\x01[+2\xbaT\xc5", + b"\xfd\xfc\x1efj\xb2\x00\x00\x171" + ] + + qr_client = create_client() + for raw in qr_raws: + qr_client.on_received(raw) + + sb_client = create_v2_client() + for raw in sb_raws: + sb_client.on_received(raw) + + def test_anno1701_20221104(self): + qr_raw = {"qr1": + b"\t\x00\x00\x00\x00anno1701\x00", + "qr2": b"\x03\x98\x92%\xa0localip0\x00192.168.0.50\x00localip1\x00192.168.122.1\x00localport\x0021701\x00natneg\x001\x00statechanged\x003\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00369174468\x00numaiplayers\x00\x00openslots\x00\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00sporesirius\x000\x000\x00\x00\x01\x00", + "sb1": b"\x00\x9a\x00\x01\x03\x8fU\x00\x00anno1701\x00anno1701\x00RcX;M({Ggroupid is null\x00\\hostname\\gamemode\\gamever\\gametype\\password\\mapname\\numplayers\\numaiplayers\\openslots\\gamevariant\x00\x00\x00\x00\x04", + "qr3": b"\x07\x98\x92%\xa0\x00\x00\x00\x00" + } + + @responses.activate + def test_aarts_20230618(self): + raw = b"\x00\xb8\x00\x01\x03\x00\x00\x00\x00aarts\x00aarts\x00F|Cy9!&w\x00\\hostname\\gamemode\\hostport\\hostname\\gamename\\gametype\\gamever\\mapname\\numplayers\\maxplayers\\gamemode\\password\\groupid\\mapsessiontype\\mapids\\internet\x00\x00\x00\x00\x04" + client = create_v2_client() + client.on_received(raw) From d373c098e112fca0a7cb54a6453cca50d5112044 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 30 Oct 2024 09:29:45 +0000 Subject: [PATCH 132/231] refactor: added more tests --- .../gamespy/query_report/requests.py | 2 +- src/backends/routers/gamespy/chat.py | 2 +- src/library/src/abstractions/brocker.py | 3 +- src/library/src/abstractions/client.py | 16 ++--- src/library/src/abstractions/connections.py | 1 - .../src/abstractions/server_launcher.py | 3 +- src/library/src/network/brockers.py | 26 ++++---- src/library/tests/mock_objects.py | 11 ++++ src/servers/chat/__init__.py | 0 src/servers/chat/src/aggregates/channel.py | 49 +++++++++------ src/servers/chat/src/applications/client.py | 10 +++- src/servers/chat/src/applications/handlers.py | 4 ++ src/servers/chat/tests/game_tests.py | 26 ++++++++ src/servers/chat/tests/mock_objects.py | 21 +++++++ src/servers/chat/tests/room_tests.py | 18 ++++++ .../src/aggregates/natneg_channel.py | 41 +++++++++++++ .../query_report/src/applications/client.py | 1 + .../src/applications/server_launcher.py | 4 +- .../src/v2/applications/handlers.py | 6 ++ .../query_report/src/v2/contracts/requests.py | 5 +- .../src/v2/contracts/responses.py | 1 - .../query_report/tests/mock_objects.py | 4 +- .../server_browser/tests/game_tests.py | 2 +- src/unispy | 60 +++++++++++++++++++ 24 files changed, 267 insertions(+), 49 deletions(-) create mode 100644 src/servers/chat/__init__.py create mode 100644 src/servers/chat/tests/game_tests.py create mode 100644 src/servers/chat/tests/mock_objects.py create mode 100644 src/servers/chat/tests/room_tests.py create mode 100644 src/servers/query_report/src/aggregates/natneg_channel.py diff --git a/src/backends/protocols/gamespy/query_report/requests.py b/src/backends/protocols/gamespy/query_report/requests.py index 5c5722d9c..0f83eaef3 100644 --- a/src/backends/protocols/gamespy/query_report/requests.py +++ b/src/backends/protocols/gamespy/query_report/requests.py @@ -25,7 +25,7 @@ class ClientMessageAckRequest(RequestBase): class ClientMessageRequest(RequestBase): server_browser_sender_id: UUID4 - natneg_message: list[int] + natneg_message: bytes target_ip_address: str target_port: str message_key: int diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index d9825083d..6b01e8ec5 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -78,4 +78,4 @@ async def websocket_endpoint(ws: WebSocket): app = FastAPI() app.include_router(router) - uvicorn.run(router, host="0.0.0.0", port=8000) + uvicorn.run(router, host="0.0.0.0", port=8080) diff --git a/src/library/src/abstractions/brocker.py b/src/library/src/abstractions/brocker.py index 50b5c109d..8d5628b95 100644 --- a/src/library/src/abstractions/brocker.py +++ b/src/library/src/abstractions/brocker.py @@ -11,12 +11,13 @@ class BrockerBase: brocker subscribe name """ - def __init__(self, name: str, call_back_func: "function") -> None: + def __init__(self, name: str, url: str, call_back_func: "function") -> None: assert isinstance(name, str) assert callable(call_back_func) self._name = name self._call_back_func = call_back_func + self.url = url @abc.abstractmethod def subscribe(self): diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index 5b294a994..797495b8c 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -3,7 +3,7 @@ from library.src.log.log_manager import LogWriter from library.src.log.log_manager import LogWriter from library.src.configs import ServerConfig - +import threading from typing import TYPE_CHECKING, Optional if TYPE_CHECKING: @@ -13,7 +13,6 @@ from library.src.abstractions.enctypt_base import EncryptBase from library.src.abstractions.contracts import ResponseBase from library.src.abstractions.client import ClientInfoBase - from library.src.network.http_handler import HttpRequest class ClientInfoBase: @@ -27,7 +26,7 @@ class ClientBase: crypto: Optional["EncryptBase"] info: "ClientInfoBase" is_log_raw: bool - pool: dict[str, "ClientBase"] + pool: dict[str, "ClientBase"] = {} """ Note: initialize in child class as class static member """ @@ -49,13 +48,14 @@ def __init__( # fmt: on def on_connected(self) -> None: - # this operation will append the child client instance to the static member of childclass, if child class is overide the static member ClientBase.pool - self.pool[self.connection.ip_endpoint] = self - pass + lock = threading.Lock() + with lock: + ClientBase.pool[self.connection.ip_endpoint] = self def on_disconnected(self) -> None: - del self.pool[self.connection.ip_endpoint] - pass + lock = threading.Lock() + with lock: + del ClientBase.pool[self.connection.ip_endpoint] def _create_switcher(self, buffer: bytes | str) -> "SwitcherBase": # type: ignore """ diff --git a/src/library/src/abstractions/connections.py b/src/library/src/abstractions/connections.py index 9bdcd0d0e..0bb2978fc 100644 --- a/src/library/src/abstractions/connections.py +++ b/src/library/src/abstractions/connections.py @@ -21,7 +21,6 @@ class ConnectionBase: logger: LogWriter handler: socketserver.BaseRequestHandler _client: ClientBase - def __init__( self, handler: socketserver.BaseRequestHandler, diff --git a/src/library/src/abstractions/server_launcher.py b/src/library/src/abstractions/server_launcher.py index 043cb5632..932e76a62 100644 --- a/src/library/src/abstractions/server_launcher.py +++ b/src/library/src/abstractions/server_launcher.py @@ -1,5 +1,5 @@ -import abc from types import MappingProxyType +from library.src.abstractions.connections import NetworkServerBase from library.src.exceptions.general import UniSpyException from library.src.log.log_manager import LogManager, LogWriter from library.src.configs import CONFIG, ServerConfig @@ -26,6 +26,7 @@ class ServerLauncherBase: config: ServerConfig logger: LogWriter + server: NetworkServerBase def start(self): self.__show_unispy_logo() diff --git a/src/library/src/network/brockers.py b/src/library/src/network/brockers.py index 0b23c3f65..ad308bd46 100644 --- a/src/library/src/network/brockers.py +++ b/src/library/src/network/brockers.py @@ -1,9 +1,12 @@ +import asyncio import threading from typing import Optional import websocket from redis import Redis from library.src.abstractions.brocker import BrockerBase from redis.client import PubSub + +from servers.chat.src.aggregates.exceptions import ChatException websocket.enableTrace(True) @@ -11,8 +14,9 @@ class RedisBrocker(BrockerBase): _client: Redis _subscriber: PubSub - def __init__(self, name: str, call_back_func: "function") -> None: - super().__init__(name, call_back_func) + def __init__(self, name: str, url: str, call_back_func: "function") -> None: + super().__init__(name, url, call_back_func) + self._client = Redis.from_url(url) self._subscriber = self._client.pubsub() def subscribe(self): @@ -25,7 +29,9 @@ def get_message(self): if not self.is_started: break if message["type"] == "message": - self.receive_message(message['data'].decode('utf-8')) + msg = message['data'].decode('utf-8') + # run receive message in background do not block receiving + threading.Thread(target=self.receive_message, args=msg) def unsubscribe(self): self.is_started = False @@ -41,7 +47,7 @@ class WebsocketBrocker(BrockerBase): _publisher: Optional[websocket.WebSocket] = None def __init__(self, name: str, url: str, call_back_func: "function") -> None: - super().__init__(name, call_back_func) + super().__init__(name, url, call_back_func) self._subscriber = websocket.WebSocketApp( url, on_message=lambda _, m: self.receive_message(m), @@ -53,15 +59,13 @@ def _on_open(self, ws): self._publisher = ws def _on_message(self, _, message): - self.receive_message(message) + threading.Thread(target=self.receive_message, args=message).start() def subscribe(self): - t = threading.Thread(target=self._subscriber.run_forever) - t.start() + threading.Thread(target=self._subscriber.run_forever).start() # # wait for connection establish - while True: - if self._publisher is not None: - break + if self._publisher is None: + raise ChatException("brocker backend is not available") def unsubscribe(self): self._subscriber.close() @@ -75,7 +79,7 @@ def publish_message(self, message): if __name__ == "__main__": ws = WebsocketBrocker(name="test_channel", - url="ws://127.0.0.1:8000/GameSpy/Chat/Channel", call_back_func=print) + url="ws://127.0.0.1:8080/GameSpy/Chat/Channel", call_back_func=print) ws.subscribe() import json ws.publish_message(json.dumps( diff --git a/src/library/tests/mock_objects.py b/src/library/tests/mock_objects.py index 245b9975c..98d876520 100644 --- a/src/library/tests/mock_objects.py +++ b/src/library/tests/mock_objects.py @@ -1,6 +1,7 @@ import socketserver import responses +from library.src.abstractions.brocker import BrockerBase from library.src.abstractions.client import ClientBase from library.src.abstractions.connections import ConnectionBase from library.src.abstractions.handler import CmdHandlerBase @@ -39,6 +40,16 @@ def warn(self, message): print(message) +class BrokerMock(BrockerBase): + def subscribe(self): + pass + + def publish_message(self, message): + pass + + def unsubscribe(self): + pass + def create_mock_url(config: ServerConfig, handler: type[CmdHandlerBase], data: dict) -> None: # fmt: off url = f"{CONFIG.backend.url}/GameSpy/{config.server_name}/{handler.__name__}/" diff --git a/src/servers/chat/__init__.py b/src/servers/chat/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/servers/chat/src/aggregates/channel.py b/src/servers/chat/src/aggregates/channel.py index f2fc7a84a..974f9951e 100644 --- a/src/servers/chat/src/aggregates/channel.py +++ b/src/servers/chat/src/aggregates/channel.py @@ -3,6 +3,7 @@ from uuid import UUID from pydantic import BaseModel, field_validator +from library.src.abstractions.brocker import BrockerBase from library.src.network.brockers import WebsocketBrocker from library.src.configs import CONFIG from servers.chat.src.abstractions.contract import ResponseBase @@ -25,32 +26,46 @@ class Channel: server_id: UUID game_name: str name: str - max_num_user: int = 200 - create_time: datetime.datetime = datetime.datetime.now( - datetime.timezone.utc) - kv_manager: KeyValueManager = KeyValueManager() + max_num_user: int + create_time: datetime.datetime + kv_manager: KeyValueManager room_type: PeerRoomType password: Optional[str] - topic: Optional[str] = None - group_id: Optional[int] = None - room_name: Optional[str] = None - previously_join_channel: Optional[str] = None + topic: Optional[str] + group_id: Optional[int] + room_name: Optional[str] + previously_join_channel: Optional[str] + ban_list: dict[str, ChannelUser] + users: dict[str, ChannelUser] + _creator_nick_name: str @property def is_valid_peer_room(self) -> bool: return self.group_id is not None and self.room_name is not None - def __init__(self, name: str, client: Client, password: Optional[str] = None) -> None: + def __init__(self, name: str, client: Client, password: Optional[str] = None, brocker_cls: type[BrockerBase] = WebsocketBrocker) -> None: + # region channel init self.server_id = client.server_config.server_id self.name = name self.password = password self.game_name = client.info.gamename self.previously_join_channel = client.info.previously_joined_channel self.room_type = PeerRoom.get_room_type(name) + self.create_time = datetime.datetime.now() + self.topic = None + self.group_id = None + self.room_name = None + self.previously_join_channel = None + self.kv_manager = KeyValueManager() + self.max_num_user = 200 # setup the message broker - self._broker = WebsocketBrocker( + self._broker = brocker_cls( self.name, CONFIG.backend.url, self.get_message_from_brocker) + # channel user init self._broker.subscribe() + self.ban_list = {} + self.users = {} + self._creator_nick_name = client.info.nick_name match self.room_type: case PeerRoomType.Group: @@ -84,10 +99,6 @@ def get_staging_room_name(self): def get_title_room_name(self): self.get_staging_room_name() - ban_list: dict[str, ChannelUser] = {} - users: dict[str, ChannelUser] = {} - _creator_nick_name: str - @property def creator(self) -> Optional[ChannelUser]: if self._creator_nick_name in self.users: @@ -145,12 +156,14 @@ def get_user_by_client(self, client: Client) -> Optional[ChannelUser]: return None def add_bind_on_user_and_channel(self, joiner: ChannelUser): - joiner.channel.users[joiner.client.info.nick_name] - joiner.client.info.joined_channels[joiner.channel.name] = joiner.channel + if joiner.client.info.nick_name not in joiner.channel.users: + joiner.client.info.joined_channels[joiner.channel.name] = joiner.channel def remove_bind_on_user_and_channel(self, leaver: ChannelUser): - del leaver.channel.users[leaver.client.info.nick_name] - del leaver.client.info.joined_channels[leaver.channel.name] + if leaver.client.info.nick_name in leaver.channel.users: + del leaver.channel.users[leaver.client.info.nick_name] + if leaver.channel.name in leaver.client.info.joined_channels: + del leaver.client.info.joined_channels[leaver.channel.name] def multicast(self, sender: Client, message: ResponseBase, is_skip_sender=False): for nick, user in self.users.items(): diff --git a/src/servers/chat/src/applications/client.py b/src/servers/chat/src/applications/client.py index ebdc508be..dc1fe94dd 100644 --- a/src/servers/chat/src/applications/client.py +++ b/src/servers/chat/src/applications/client.py @@ -1,6 +1,7 @@ from library.src.abstractions.client import ClientBase from typing import TYPE_CHECKING +from library.src.abstractions.switcher import SwitcherBase from library.src.log.log_manager import LogWriter from library.src.network.tcp_handler import TcpConnection from library.src.configs import ServerConfig @@ -16,11 +17,18 @@ class ClientInfo: gamename: str user_name: str + def __init__(self) -> None: + self.joined_channels = {} + class Client(ClientBase): info: ClientInfo - client_pool: dict[str, "Client"] = {} + client_pool: dict[str, "Client"] def __init__(self, connection: TcpConnection, server_config: ServerConfig, logger: LogWriter): super().__init__(connection, server_config, logger) self.info = ClientInfo() + + def _create_switcher(self, buffer: bytes) -> SwitcherBase: + from servers.chat.src.applications.switcher import Switcher + return Switcher(self, buffer.decode()) diff --git a/src/servers/chat/src/applications/handlers.py b/src/servers/chat/src/applications/handlers.py index 6ca605ff9..8a674eb7a 100644 --- a/src/servers/chat/src/applications/handlers.py +++ b/src/servers/chat/src/applications/handlers.py @@ -176,6 +176,7 @@ class NickHandler(CmdHandlerBase): def __init__(self, client: ClientBase, request: NickRequest): assert isinstance(request, NickRequest) super().__init__(client, request) + self._result_cls = NickResult def _response_construct(self) -> None: self._response = NickResponse(self._result) @@ -232,6 +233,9 @@ def _request_check(self) -> None: self._client.connection.remote_port}" ) + def _feach_data(self): + pass + def _response_construct(self) -> None: self._response = UserIPResponse(self._result) diff --git a/src/servers/chat/tests/game_tests.py b/src/servers/chat/tests/game_tests.py new file mode 100644 index 000000000..ef0126099 --- /dev/null +++ b/src/servers/chat/tests/game_tests.py @@ -0,0 +1,26 @@ +import unittest + +from servers.chat.tests.mock_objects import create_client + + +class GameTests(unittest.TestCase): + def test_civilization4(self): + raws = ["USRIP", + "USER X419pGl4sX|18 127.0.0.1 peerchat.gamespy.com :aa3041ada9385b28fc4d4e47db288769", + "NICK a1701-5", + "CDKEY 81123-67814-77652-27631-11723-47707-22638-10701", + "JOIN #GSP!anno1701 ", + "MODE #GSP!anno1701", + "GETCKEY #GSP!anno1701 * 008 0 :\\b_flags", "WHO a1701-5", + "JOIN #GSP!anno1701!M9zK0KJaKM ", + "MODE #GSP!anno1701!M9zK0KJaKM", + "SETCKEY #GSP!anno1701 a1701-5 :\\b_flags\\s", + "SETCKEY #GSP!anno1701!M9zK0KJaKM a1701-5 :\\b_flags\\sh", + "GETCKEY #GSP!anno1701!M9zK0KJaKM * 009 0 :\\b_flags", + "TOPIC #GSP!anno1701!M9zK0KJaKM :test", + "MODE #GSP!anno1701!M9zK0KJaKM +l 4", + "MODE #GSP!anno1701!M9zK0KJaKM -i-p-s+m+n+t+l+e 4", + "PART #GSP!anno1701 :"] + client = create_client() + for raw in raws: + client.on_received(raw) diff --git a/src/servers/chat/tests/mock_objects.py b/src/servers/chat/tests/mock_objects.py new file mode 100644 index 000000000..393048a96 --- /dev/null +++ b/src/servers/chat/tests/mock_objects.py @@ -0,0 +1,21 @@ +from typing import TYPE_CHECKING, cast +from library.src.configs import CONFIG +from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock +from servers.chat.src.applications.client import Client + + +class ClientMock(Client): + pass + + +def create_client() -> Client: + handler = RequestHandlerMock() + logger = LogMock() + config = CONFIG.servers["Chat"] + conn = ConnectionMock( + handler=handler, + config=config, t_client=ClientMock, + logger=logger) + if TYPE_CHECKING: + conn._client = cast(Client, conn._client) + return conn._client diff --git a/src/servers/chat/tests/room_tests.py b/src/servers/chat/tests/room_tests.py new file mode 100644 index 000000000..42f080ce8 --- /dev/null +++ b/src/servers/chat/tests/room_tests.py @@ -0,0 +1,18 @@ +import unittest + +from library.tests.mock_objects import BrokerMock +from servers.chat.src.aggregates.channel import Channel +from servers.chat.src.aggregates.channel_user import ChannelUser +from servers.chat.tests.mock_objects import create_client + + +class RoomTests(unittest.TestCase): + def test_peer_room(self): + client = create_client() + client.info.gamename = "test" + client.info.previously_joined_channel = "stagging" + client.info.nick_name = "unispy" + channel = Channel("test", client, brocker_cls=BrokerMock) + user = ChannelUser(client, channel) + channel.add_bind_on_user_and_channel(user) + pass diff --git a/src/servers/query_report/src/aggregates/natneg_channel.py b/src/servers/query_report/src/aggregates/natneg_channel.py new file mode 100644 index 000000000..60095a1ff --- /dev/null +++ b/src/servers/query_report/src/aggregates/natneg_channel.py @@ -0,0 +1,41 @@ +from typing import TYPE_CHECKING +from library.src.abstractions.brocker import BrockerBase +from library.src.configs import CONFIG +from library.src.log.log_manager import GLOBAL_LOGGER +from library.src.network.brockers import WebsocketBrocker +from servers.query_report.src.v2.applications.handlers import ClientMessageHandler +from servers.query_report.src.v2.contracts.requests import ClientMessageRequest +from types import MappingProxyType + +if TYPE_CHECKING: + from servers.query_report.src.applications.client import Client + + +class NatNegChannel: + """ + todo send data to the ip endpoint when receive data, not find client from pool to save memory + """ + broker: BrockerBase + pool: MappingProxyType[str, "Client"] + + def __init__(self, broker_cls: type[BrockerBase] = WebsocketBrocker) -> None: + self.broker = broker_cls( + "natneg", f"{CONFIG.backend.url}/QueryReport/Channel", self.recieve_message) + self.pool = MappingProxyType(Client.pool) + + def recieve_message(self, request: bytes): + message = ClientMessageRequest(request) + message.parse() + client = None + if message.target_ip_endpoint in self.pool: + client = self.pool[message.target_ip_endpoint] + + if client is None: + GLOBAL_LOGGER.warn(f"Client:{message.target_ip_address}:{ + message.target_port} not found, we ignore natneg message from SB: {message.server_browser_sender_id}") + return + + GLOBAL_LOGGER.info(f"Get client message from server browser: { + message.server_browser_sender_id} [{message.natneg_message}]") + handler = ClientMessageHandler(client, message) + handler.handle() diff --git a/src/servers/query_report/src/applications/client.py b/src/servers/query_report/src/applications/client.py index 588f23993..c6ff8216b 100644 --- a/src/servers/query_report/src/applications/client.py +++ b/src/servers/query_report/src/applications/client.py @@ -4,6 +4,7 @@ class Client(ClientBase): + pool: dict[str, "Client"] is_log_raw: bool = True def _create_switcher(self, buffer: bytes): diff --git a/src/servers/query_report/src/applications/server_launcher.py b/src/servers/query_report/src/applications/server_launcher.py index 7cad2e97d..50330b9ee 100644 --- a/src/servers/query_report/src/applications/server_launcher.py +++ b/src/servers/query_report/src/applications/server_launcher.py @@ -5,13 +5,15 @@ class ServerLauncher(ServerLauncherBase): + natneg_channel: object def __init__(self) -> None: super().__init__() self.config = CONFIG.servers["QueryReport"] def _launch_server(self): - UdpServer(self.config, Client, self.logger).start() + self.server = UdpServer(self.config, Client, self.logger) + self.server.start() if __name__ == "__main__": diff --git a/src/servers/query_report/src/v2/applications/handlers.py b/src/servers/query_report/src/v2/applications/handlers.py index d31e324ce..20a19a216 100644 --- a/src/servers/query_report/src/v2/applications/handlers.py +++ b/src/servers/query_report/src/v2/applications/handlers.py @@ -26,6 +26,9 @@ def __init__(self, client: Client, request: AvaliableRequest) -> None: assert isinstance(request, AvaliableRequest) super().__init__(client, request) + def _feach_data(self): + pass + def _response_construct(self): self._response = AvaliableResponse(self._request) @@ -58,6 +61,9 @@ def __init__(self, client: Client, request: ClientMessageRequest) -> None: assert isinstance(request, ClientMessageRequest) super().__init__(client, request) + def _request_check(self) -> None: + pass + def _response_construct(self): self._response = ClientMessageResponse(self._request) diff --git a/src/servers/query_report/src/v2/contracts/requests.py b/src/servers/query_report/src/v2/contracts/requests.py index cfc9e37f0..cce4b4380 100644 --- a/src/servers/query_report/src/v2/contracts/requests.py +++ b/src/servers/query_report/src/v2/contracts/requests.py @@ -1,7 +1,6 @@ from typing import Optional from uuid import UUID from library.src.extentions.encoding import get_string -from library.src.log.log_manager import LogWriter from servers.query_report.src.aggregates.exceptions import QRException from servers.query_report.src.v2.abstractions.contracts import RequestBase from servers.query_report.src.v2.aggregates.enums import GameServerStatus @@ -42,6 +41,10 @@ class ClientMessageRequest(RequestBase): def cookie(self) -> int: return int.from_bytes(self.natneg_message[6:10], "little") + @property + def target_ip_endpoint(self) -> str: + return f"{self.target_ip_address}:{self.target_port}" + def __init__(self, raw_request: Optional[bytes] = None) -> None: if raw_request is not None: super().__init__(raw_request) diff --git a/src/servers/query_report/src/v2/contracts/responses.py b/src/servers/query_report/src/v2/contracts/responses.py index 48e1b16e8..a1ed5d608 100644 --- a/src/servers/query_report/src/v2/contracts/responses.py +++ b/src/servers/query_report/src/v2/contracts/responses.py @@ -1,4 +1,3 @@ -import socket from library.src.extentions.encoding import get_bytes from servers.query_report.src.v2.abstractions.contracts import ResponseBase from servers.query_report.src.v2.contracts.requests import ( diff --git a/src/servers/query_report/tests/mock_objects.py b/src/servers/query_report/tests/mock_objects.py index 190af65d8..7d395c95e 100644 --- a/src/servers/query_report/tests/mock_objects.py +++ b/src/servers/query_report/tests/mock_objects.py @@ -2,7 +2,7 @@ from library.src.configs import CONFIG from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url from servers.query_report.src.applications.client import Client -from servers.query_report.src.v2.applications.handlers import HeartBeatHandler +from servers.query_report.src.v2.applications.handlers import AvailableHandler, HeartBeatHandler from servers.query_report.src.v2.contracts.results import HeartBeatResult @@ -20,5 +20,5 @@ def create_client() -> Client: config = CONFIG.servers["QueryReport"] create_mock_url(config, HeartBeatHandler, HeartBeatResult.model_validate( {"remote_ip_address": conn.remote_ip, "remote_port": conn.remote_port}).model_dump()) - + create_mock_url(config, AvailableHandler, {"message": "ok"}) return cast(Client, conn._client) diff --git a/src/servers/server_browser/tests/game_tests.py b/src/servers/server_browser/tests/game_tests.py index 8bfcd4ea1..be4984b3c 100644 --- a/src/servers/server_browser/tests/game_tests.py +++ b/src/servers/server_browser/tests/game_tests.py @@ -1,7 +1,6 @@ import unittest import responses -# from servers.server_browser.tests.mock_objects import create_v2_client from servers.query_report.tests.mock_objects import create_client from servers.server_browser.tests.mock_objects import create_v2_client @@ -16,6 +15,7 @@ def test_gmtest_20200309(self): sb_client = create_v2_client() sb_client.on_received(sb_raw) + @responses.activate def test_anno1701_20220620(self): qr_raws = [ # available request diff --git a/src/unispy b/src/unispy index 2d124b195..dd0095b58 100644 --- a/src/unispy +++ b/src/unispy @@ -1,3 +1,63 @@ 2024-10-29 02:29:48 [unispy] [INFO]: [172.19.0.5:35003] [recv]: b'Hello, UDP!\n' 2024-10-29 02:33:53 [unispy] [INFO]: [172.19.0.5:47697] [recv]: b'Hello, UDP!\n' 2024-10-29 02:33:59 [unispy] [INFO]: [172.19.0.5:47697]: Invalid request received! +2024-10-30 05:48:28 [unispy] [ERROR]: [Errno 111] Connection refused - goodbye +2024-10-30 05:48:58 [unispy] [DEBUG]: --- request header --- +2024-10-30 05:48:58 [unispy] [DEBUG]: GET /GameSpy/Chat/Channel HTTP/1.1 +Upgrade: websocket +Host: 127.0.0.1:8080 +Origin: http://127.0.0.1:8080 +Sec-WebSocket-Key: PG0nOCrh8mpemVL2zR+POg== +Sec-WebSocket-Version: 13 +Connection: Upgrade + + +2024-10-30 05:48:58 [unispy] [DEBUG]: ----------------------- +2024-10-30 05:48:58 [unispy] [DEBUG]: --- response header --- +2024-10-30 05:48:58 [unispy] [DEBUG]: HTTP/1.1 404 Not Found +2024-10-30 05:48:58 [unispy] [DEBUG]: date: Wed, 30 Oct 2024 05:48:57 GMT +2024-10-30 05:48:58 [unispy] [DEBUG]: server: uvicorn +2024-10-30 05:48:58 [unispy] [DEBUG]: content-length: 22 +2024-10-30 05:48:58 [unispy] [DEBUG]: content-type: application/json +2024-10-30 05:48:58 [unispy] [DEBUG]: ----------------------- +2024-10-30 05:48:58 [unispy] [ERROR]: Handshake status 404 Not Found -+-+- {'date': 'Wed, 30 Oct 2024 05:48:57 GMT', 'server': 'uvicorn', 'content-length': '22', 'content-type': 'application/json'} -+-+- b'{"detail":"Not Found"}' - goodbye +2024-10-30 05:49:45 [unispy] [ERROR]: [Errno 111] Connection refused - goodbye +2024-10-30 05:50:23 [unispy] [DEBUG]: --- request header --- +2024-10-30 05:50:23 [unispy] [DEBUG]: GET /GameSpy/Chat/Channel HTTP/1.1 +Upgrade: websocket +Host: 127.0.0.1:8080 +Origin: http://127.0.0.1:8080 +Sec-WebSocket-Key: 0usGdjTC0jytMHzSo/EsKA== +Sec-WebSocket-Version: 13 +Connection: Upgrade + + +2024-10-30 05:50:23 [unispy] [DEBUG]: ----------------------- +2024-10-30 05:50:23 [unispy] [DEBUG]: --- response header --- +2024-10-30 05:50:23 [unispy] [DEBUG]: HTTP/1.1 404 Not Found +2024-10-30 05:50:23 [unispy] [DEBUG]: date: Wed, 30 Oct 2024 05:50:22 GMT +2024-10-30 05:50:23 [unispy] [DEBUG]: server: uvicorn +2024-10-30 05:50:23 [unispy] [DEBUG]: content-length: 9 +2024-10-30 05:50:23 [unispy] [DEBUG]: content-type: text/plain; charset=utf-8 +2024-10-30 05:50:23 [unispy] [DEBUG]: ----------------------- +2024-10-30 05:50:23 [unispy] [ERROR]: Handshake status 404 Not Found -+-+- {'date': 'Wed, 30 Oct 2024 05:50:22 GMT', 'server': 'uvicorn', 'content-length': '9', 'content-type': 'text/plain; charset=utf-8'} -+-+- b'Not Found' - goodbye +2024-10-30 05:51:24 [unispy] [DEBUG]: --- request header --- +2024-10-30 05:51:24 [unispy] [DEBUG]: GET /GameSpy/Chat/Channel HTTP/1.1 +Upgrade: websocket +Host: 127.0.0.1:8080 +Origin: http://127.0.0.1:8080 +Sec-WebSocket-Key: U/5/wTk/QDGKhj1GM+g/tA== +Sec-WebSocket-Version: 13 +Connection: Upgrade + + +2024-10-30 05:51:24 [unispy] [DEBUG]: ----------------------- +2024-10-30 05:51:24 [unispy] [DEBUG]: --- response header --- +2024-10-30 05:51:24 [unispy] [DEBUG]: HTTP/1.1 101 Switching Protocols +2024-10-30 05:51:24 [unispy] [DEBUG]: Upgrade: websocket +2024-10-30 05:51:24 [unispy] [DEBUG]: Connection: Upgrade +2024-10-30 05:51:24 [unispy] [DEBUG]: Sec-WebSocket-Accept: ybgaKobanY3o8V/gxbVYsaX79wE= +2024-10-30 05:51:24 [unispy] [DEBUG]: date: Wed, 30 Oct 2024 05:51:24 GMT +2024-10-30 05:51:24 [unispy] [DEBUG]: server: uvicorn +2024-10-30 05:51:24 [unispy] [DEBUG]: ----------------------- +2024-10-30 05:51:24 [unispy] [INFO]: Websocket connected From 15060f924348bdd91c5e844b036eed2e6588bcb3 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 1 Nov 2024 02:46:09 +0000 Subject: [PATCH 133/231] refactor: request type validation --- .../protocols/gamespy/chat/requests.py | 95 +++++++++++- src/backends/routers/gamespy/chat.py | 137 ++++++++++++++++++ src/library/src/abstractions/client.py | 16 +- src/library/src/abstractions/connections.py | 3 +- src/library/src/abstractions/contracts.py | 3 +- src/library/src/abstractions/handler.py | 17 ++- src/library/src/network/http_handler.py | 9 +- src/servers/chat/src/abstractions/channel.py | 88 ----------- src/servers/chat/src/abstractions/contract.py | 36 +++-- src/servers/chat/src/abstractions/handler.py | 103 +++++++++++++ src/servers/chat/src/abstractions/message.py | 42 ------ src/servers/chat/src/aggregates/channel.py | 10 +- src/servers/chat/src/aggregates/enums.py | 30 ++++ src/servers/chat/src/applications/client.py | 2 +- src/servers/chat/src/applications/handlers.py | 26 ++-- src/servers/chat/src/applications/switcher.py | 130 +++++++++-------- src/servers/chat/src/contracts/requests.py | 18 +-- src/servers/chat/src/contracts/responses.py | 2 +- src/servers/chat/src/contracts/results.py | 2 +- src/servers/chat/tests/game_tests.py | 43 +++++- src/servers/chat/tests/mock_objects.py | 5 +- src/servers/chat/tests/request_tests.py | 66 +++++++++ .../game_status/src/aggregations/enums.py | 12 +- .../game_status/src/applications/switcher.py | 24 +-- .../natneg/src/applications/switcher.py | 11 +- .../src/aggregates/enums.py | 18 ++- .../src/applications/switcher.py | 40 +++-- .../tests/game_tests.py | 2 +- .../src/aggregates/enums.py | 16 +- .../src/applications/switcher.py | 41 +++--- .../src/v2/applications/handlers.py | 12 +- .../src/v2/applications/switcher.py | 28 ++-- .../query_report/tests/mock_objects.py | 3 +- .../src/v2/applications/switcher.py | 9 +- .../web_services/src/applications/client.py | 9 +- 35 files changed, 758 insertions(+), 350 deletions(-) delete mode 100644 src/servers/chat/src/abstractions/channel.py delete mode 100644 src/servers/chat/src/abstractions/message.py create mode 100644 src/servers/chat/tests/request_tests.py diff --git a/src/backends/protocols/gamespy/chat/requests.py b/src/backends/protocols/gamespy/chat/requests.py index 0434dbb1c..2c64e74cd 100644 --- a/src/backends/protocols/gamespy/chat/requests.py +++ b/src/backends/protocols/gamespy/chat/requests.py @@ -1,5 +1,5 @@ import backends.library.abstractions.contracts as lib -from servers.chat.src.aggregates.enums import LoginRequestType, WhoRequestType +from servers.chat.src.aggregates.enums import GetKeyRequestType, LoginRequestType, MessageType, ModeRequestType, TopicRequestType, WhoRequestType class RequestBase(lib.RequestBase): @@ -9,6 +9,8 @@ class RequestBase(lib.RequestBase): _cmd_params: list _longParam: str +# region General + class CdkeyRequest(RequestBase): cdkey: str @@ -107,3 +109,94 @@ class GetKeyRequest(RequestBase): cookie: str unknown_cmd_param: str keys: list[str] + +# region Channel + + +class ChannelRequestBase(RequestBase): + channel_name: str + + +class GetChannelKeyRequest(ChannelRequestBase): + cookie: str + keys: list + + +class GetCKeyRequest(ChannelRequestBase): + nick_name: str + cookie: str + keys: list + request_type: GetKeyRequestType + + +class JoinRequest(ChannelRequestBase): + password: str + + +class KickRequest(ChannelRequestBase): + kickee_nick_name: str + reason: str + + +class ModeRequest(ChannelRequestBase): + request_type: ModeRequestType + mode_operations: list = [] + nick_name: str + user_name: str + limit_number: int + mode_flag: str + password: str + + +class NamesRequest(ChannelRequestBase): + pass + + +class PartRequest(ChannelRequestBase): + reason: str + + +class SetChannelKeyRequest(ChannelRequestBase): + key_value_string: str + key_values: dict[str, str] + + +class SetCKeyRequest(ChannelRequestBase): + nick_name: str + cookie: str + is_broadcast: bool + key_value_string: str + key_values: dict[str, str] + + +class SetGroupRequest(ChannelRequestBase): + group_name: str + + +class TopicRequest(ChannelRequestBase): + channel_topic: str + request_type: TopicRequestType + + +class MessageRequestBase(ChannelRequestBase): + type: MessageType + nick_name: str + message: str + +# region Message + + +class ATMRequest(MessageRequestBase): + pass + + +class NoticeRequest(MessageRequestBase): + pass + + +class PrivateRequest(MessageRequestBase): + pass + + +class UTMRequest(MessageRequestBase): + pass diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 6b01e8ec5..07982511d 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -1,5 +1,7 @@ import json from typing import Optional +from backends.protocols.gamespy.chat.requests import (ATMRequest, CdkeyRequest, GetCKeyRequest, GetChannelKeyRequest, GetKeyRequest, GetUdpRelayRequest, InviteRequest, JoinRequest, KickRequest, + ListRequest, LoginRequest, ModeRequest, NamesRequest, NickRequest, NoticeRequest, PartRequest, PrivateRequest, QuitRequest, SetChannelKeyRequest, SetGroupRequest, SetKeyRequest, TopicRequest, UTMRequest, UserIPRequest, UserRequest, WhoIsRequest, WhoRequest) from backends.urls import CHAT from fastapi import APIRouter, FastAPI, WebSocket, WebSocketDisconnect @@ -72,6 +74,141 @@ async def websocket_endpoint(ws: WebSocket): print("Client disconnected") +@router.post(f"{CHAT}/CdKeyHandler") +def cdkey(request: CdkeyRequest): + pass + + +@router.post(f"{CHAT}/GetKeyHandler") +def getkey(request: GetKeyRequest): + pass + + +@router.post(f"{CHAT}/GetUdpRelayHandler") +def get_udp_relay(request: GetUdpRelayRequest): + pass + +@router.post(f"{CHAT}/InviteHandler") +def invite(request: InviteRequest): + pass + + +@router.post(f"{CHAT}/ListHandler") +def list_data(request: ListRequest): + pass + + +@router.post(f"{CHAT}/LoginHandler") +def login(request: LoginRequest): + pass + + +@router.post(f"{CHAT}/NickHandler") +def nick(request: NickRequest): + pass + + +@router.post(f"{CHAT}/QuitHandler") +def quit(request: QuitRequest): + pass + + +@router.post(f"{CHAT}/SetKeyHandler") +def set_key(request: SetKeyRequest): + pass + + +@router.post(f"{CHAT}/UserHandler") +def user(request: UserRequest): + pass + + +@router.post(f"{CHAT}/UserIPHandler") +def user_ip(request: UserIPRequest): + pass + + +@router.post(f"{CHAT}/WhoHandler") +def who(request: WhoRequest): + pass + + +@router.post(f"{CHAT}/WhoIsHandler") +def whois(request: WhoIsRequest): + pass + + +# region channel +@router.post(f"{CHAT}/GetChannelKeyHandler") +def get_channel_key(request: GetChannelKeyRequest): + pass + + +@router.post(f"{CHAT}/GetCKeyHandler") +def get_ckey(request: GetCKeyRequest): + pass + + +@router.post(f"{CHAT}/JoinHandler") +def join(request: JoinRequest): + pass + + +@router.post(f"{CHAT}/KickHandler") +def kick(request: KickRequest): + pass + + +@router.post(f"{CHAT}/ModeHandler") +def mode(request: ModeRequest): + pass + + +@router.post(f"{CHAT}/NamesHandler") +def names(request: NamesRequest): + pass + + +@router.post(f"{CHAT}/PartHandler") +def part(request: PartRequest): + pass + + +@router.post(f"{CHAT}/SetChannelKeyHandler") +def set_channel_key(request: SetChannelKeyRequest): + pass + + +@router.post(f"{CHAT}/SetGroupHandler") +def set_group(request: SetGroupRequest): + pass + + +@router.post(f"{CHAT}/TopicHandler") +def topic(request: TopicRequest): + pass + + +@router.post(f"{CHAT}/ATMHandler") +def atm(request: ATMRequest): + pass + + +@router.post(f"{CHAT}/NoticeHandler") +def notice(request: NoticeRequest): + pass + + +@router.post(f"{CHAT}/PrivateHandler") +def private(request: PrivateRequest): + pass + + +@router.post(f"{CHAT}/UTMHandler") +def utm(request: UTMRequest): + pass + + if __name__ == "__main__": import uvicorn from fastapi import FastAPI diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index 797495b8c..228c1280b 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -57,22 +57,20 @@ def on_disconnected(self) -> None: with lock: del ClientBase.pool[self.connection.ip_endpoint] - def _create_switcher(self, buffer: bytes | str) -> "SwitcherBase": # type: ignore + def _create_switcher(self, buffer: bytes) -> "SwitcherBase": # type: ignore """ virtual method helps verify buffer type """ assert isinstance(buffer, bytes) or isinstance(buffer, str) - def on_received(self, buffer: bytes | str) -> None: - if isinstance(buffer, bytes): - if self.crypto is not None: - buffer = self.crypto.decrypt(buffer) - elif isinstance(buffer, str): - pass - else: + def on_received(self, buffer: bytes) -> None: + if not isinstance(buffer, bytes): raise UniSpyException("buffer type is invalid") + + if self.crypto is not None: + buffer = self.crypto.decrypt(buffer) self.log_network_receving(buffer) - switcher: "SwitcherBase" = self._create_switcher(buffer) + switcher = self._create_switcher(buffer) switcher.handle() def decrypt_message(self, buffer: bytes) -> bytes: diff --git a/src/library/src/abstractions/connections.py b/src/library/src/abstractions/connections.py index 0bb2978fc..b3ff2c657 100644 --- a/src/library/src/abstractions/connections.py +++ b/src/library/src/abstractions/connections.py @@ -21,6 +21,7 @@ class ConnectionBase: logger: LogWriter handler: socketserver.BaseRequestHandler _client: ClientBase + def __init__( self, handler: socketserver.BaseRequestHandler, @@ -43,7 +44,7 @@ def __init__( self._client = self.t_client(self, self.config, self.logger) self._is_started = False - def on_received(self, data: "bytes|str") -> None: + def on_received(self, data: bytes) -> None: self._client.on_received(data) @abc.abstractmethod diff --git a/src/library/src/abstractions/contracts.py b/src/library/src/abstractions/contracts.py index c99c4db95..736593a56 100644 --- a/src/library/src/abstractions/contracts.py +++ b/src/library/src/abstractions/contracts.py @@ -2,7 +2,6 @@ from copy import deepcopy import enum from typing import Optional -from uuid import UUID from pydantic import BaseModel @@ -45,7 +44,7 @@ def to_json(self) -> dict: result[key] = value.value elif isinstance(value, enum.IntEnum): result[key] = value.value - elif isinstance(value, UUID): + else: result[key] = str(value) return result diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index bfb3a2777..cfbd6b694 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -1,6 +1,6 @@ from library.src.abstractions.client import ClientBase from library.src.exceptions.general import UniSpyException -from typing import Optional, Type +from typing import Type import requests from library.src.configs import CONFIG @@ -64,9 +64,16 @@ def _data_operate(self) -> None: """ virtual function, can be override """ + self._prepare_data() self._upload_data() self._feach_data() + def _prepare_data(self): + self._temp_data = self._request.to_json() + self._temp_data["server_id"] = str( + self._client.server_config.server_id) + self._temp_data["client_ip_endpoint"] = self._client.connection.ip_endpoint + def _upload_data(self): """ whether need send data to backend @@ -75,11 +82,9 @@ def _upload_data(self): url = f"{CONFIG.backend.url}/GameSpy/{ self._client.server_config.server_name}/{self.__class__.__name__}/" - # fmt: on - data = self._request.to_json() - data["server_id"] = str(self._client.server_config.server_id) - - response = requests.post(url, json=data) + response = requests.post(url, json=self._temp_data) + if response.status_code != 200: + raise UniSpyException("Upload data to background failed.") self._http_result = response.json() def _feach_data(self): diff --git a/src/library/src/network/http_handler.py b/src/library/src/network/http_handler.py index c08ef0946..72d83f384 100644 --- a/src/library/src/network/http_handler.py +++ b/src/library/src/network/http_handler.py @@ -49,8 +49,9 @@ def do_POST(self) -> None: data = self.rfile.read(content_length).decode() # request = HttpRequest(parsed_url, dict(self.headers), data) if self.conn is None: - self.conn = HttpConnection(self, *self.server.unispy_params) # type: ignore - self.conn.on_received(data) + self.conn = HttpConnection( + self, *self.server.unispy_params) # type: ignore + self.conn.on_received(data.encode()) class HttpServer(NetworkServerBase): @@ -58,7 +59,9 @@ def start(self) -> None: self._server = ThreadingHTTPServer( (self._config.public_address, self._config.listening_port), HttpHandler ) - self._server.unispy_params = (self._config, self._client_cls, self._logger) # type: ignore + # fmt: off + self._server.unispy_params = (self._config, self._client_cls, self._logger)# type: ignore + # fmt: on self._server.serve_forever() diff --git a/src/servers/chat/src/abstractions/channel.py b/src/servers/chat/src/abstractions/channel.py deleted file mode 100644 index 5f16a3bee..000000000 --- a/src/servers/chat/src/abstractions/channel.py +++ /dev/null @@ -1,88 +0,0 @@ -from typing import TYPE_CHECKING -from library.src.abstractions.client import ClientBase -from servers.chat.src.abstractions.contract import * -from servers.chat.src.abstractions.contract import RequestBase -from servers.chat.src.abstractions.handler import PostLoginHandlerBase -from servers.chat.src.aggregates.managers import ChannelManager - -from servers.chat.src.aggregates.exceptions import ChatException, NoSuchNickException, NoSuchChannelException - -if TYPE_CHECKING: - from servers.chat.src.aggregates.channel import Channel - from servers.chat.src.aggregates.channel_user import ChannelUser - - -class ChannelRequestBase(RequestBase): - channel_name: str - - def parse(self) -> None: - super().parse() - if self._cmd_params is None or len(self._cmd_params) < 1: - raise ChatException("Channel name is missing.") - self.channel_name = self._cmd_params[0] - - -class ChannelResponseBase(ResponseBase): - _request: ChannelRequestBase - - def __init__(self, request: RequestBase, result: ResultBase) -> None: - super().__init__(request, result) - assert isinstance(request, RequestBase) - assert isinstance(result, ResultBase) - - -class ChannelHandlerBase(PostLoginHandlerBase): - _channel: "Channel" - _user: "ChannelUser" - _request: ChannelRequestBase - _response: ResponseBase - - def __init__(self, client: ClientBase, request: RequestBase): - super().__init__(client, request) - # self._channel = None - - def _request_check(self) -> None: - if self._request.raw_request is None: - return super()._request_check() - - if self._channel is None: - channel = ChannelManager.get_channel( - self._request.channel_name - ) - if channel is None: - raise NoSuchChannelException( - f"No such channel {self._request.channel_name}", - ) - self._channel = channel - if self._user is None: - user = self._channel.get_user_by_nick( - self._client.info.nick_name) - - if user is None: - raise NoSuchNickException( - f"Can not find user with nickname: { - self._client.info.nick_name} user_name: {self._client.info.user_name}" - ) - self._user = user - - def handle(self) -> None: - super().handle() - try: - # todo check whether the broadcast message is same as responses - self._publish_message() - self._update_channel_cache() - except Exception as e: - self._handle_exception(e) - if ChannelHandlerBase._debug: - raise e - - def _publish_message(self): - if self._response is None: - self._client.log_warn("response is not constructed.") - if self._channel is None: - self._client.log_warn("channel is not assined") - return - self._channel.send_message_to_brocker(self._response.sending_buffer) - - def _update_channel_cache(self): - pass diff --git a/src/servers/chat/src/abstractions/contract.py b/src/servers/chat/src/abstractions/contract.py index 0df8a3c94..203bb8bde 100644 --- a/src/servers/chat/src/abstractions/contract.py +++ b/src/servers/chat/src/abstractions/contract.py @@ -22,22 +22,30 @@ def parse(self) -> None: if self.raw_request.count(":") > 2: raise Exception(f"IRC request is invalid {self.raw_request}") - - indexOfColon = self.raw_request.index(":") - - rawRequest = self.raw_request - if indexOfColon == 0 and indexOfColon != -1: - prefixIndex = rawRequest.index(" ") - self._prefix = rawRequest[indexOfColon:prefixIndex] - rawRequest = rawRequest[prefixIndex:] - - indexOfColon = rawRequest.index(":") - if indexOfColon != 0 and indexOfColon != -1: - self._longParam = rawRequest[indexOfColon + 1:] + if ":" not in self.raw_request: + index_of_colon = 0 + else: + index_of_colon = self.raw_request.index(":") + + raw = self.raw_request + if index_of_colon == 0 and index_of_colon != -1: + if " " not in raw: + prefixIndex = 0 + else: + prefixIndex = raw.index(" ") + self._prefix = raw[index_of_colon:prefixIndex] + raw = raw[prefixIndex:] + + if ":" not in self.raw_request: + index_of_colon = 0 + else: + index_of_colon = self.raw_request.index(":") + if index_of_colon != 0 and index_of_colon != -1: + self._longParam = raw[index_of_colon + 1:] # reset the request string - rawRequest = rawRequest[:indexOfColon] + raw = raw[:index_of_colon] - dataFrag = rawRequest.strip(" ").split(" ") + dataFrag = raw.strip(" ").split(" ") self.command_name = dataFrag[0] diff --git a/src/servers/chat/src/abstractions/handler.py b/src/servers/chat/src/abstractions/handler.py index 61ccca7cf..dbda3bae6 100644 --- a/src/servers/chat/src/abstractions/handler.py +++ b/src/servers/chat/src/abstractions/handler.py @@ -1,3 +1,11 @@ +from servers.chat.src.aggregates.enums import MessageType +from servers.chat.src.abstractions.contract import ResultBase +from typing import TYPE_CHECKING +from servers.chat.src.aggregates.exceptions import ChatException, NoSuchNickException, NoSuchChannelException +from servers.chat.src.aggregates.managers import ChannelManager +from servers.chat.src.abstractions.handler import PostLoginHandlerBase +from servers.chat.src.abstractions.contract import RequestBase +from servers.chat.src.abstractions.contract import * from library.src.abstractions.client import ClientBase from servers.chat.src.abstractions.contract import RequestBase, ResultBase from servers.chat.src.applications.client import Client @@ -25,3 +33,98 @@ def _handle_exception(self, ex: Exception) -> None: class PostLoginHandlerBase(CmdHandlerBase): pass + + +if TYPE_CHECKING: + from servers.chat.src.aggregates.channel import Channel + from servers.chat.src.aggregates.channel_user import ChannelUser + +# region Channel + + +class ChannelRequestBase(RequestBase): + channel_name: str + + def parse(self) -> None: + super().parse() + if self._cmd_params is None or len(self._cmd_params) < 1: + raise ChatException("Channel name is missing.") + self.channel_name = self._cmd_params[0] + + +class ChannelResponseBase(ResponseBase): + _request: ChannelRequestBase + + def __init__(self, request: RequestBase, result: ResultBase) -> None: + super().__init__(request, result) + assert isinstance(request, RequestBase) + assert isinstance(result, ResultBase) + + +class ChannelHandlerBase(PostLoginHandlerBase): + _channel: Channel + _user: ChannelUser + _request: ChannelRequestBase + _response: ResponseBase + + def __init__(self, client: ClientBase, request: RequestBase): + super().__init__(client, request) + # self._channel = None + + def _request_check(self) -> None: + if self._request.raw_request is None: + return super()._request_check() + + if self._channel is None: + channel = ChannelManager.get_channel( + self._request.channel_name + ) + if channel is None: + raise NoSuchChannelException( + f"No such channel {self._request.channel_name}", + ) + self._channel = channel + if self._user is None: + user = self._channel.get_user_by_nick( + self._client.info.nick_name) + + if user is None: + raise NoSuchNickException(f"Can not find user with nickname: {self._client.info.nick_name} user_name: {self._client.info.user_name}") # noqa + self._user = user + +# region Message + + +class MessageRequestBase(ChannelRequestBase): + type: MessageType + nick_name: str + message: str + + def parse(self): + super().parse() + if "#" in self.channel_name: + self.type = MessageType.CHANNEL_MESSAGE + else: + self.type = MessageType.USER_MESSAGE + self.nick_name = self._cmd_params[0] + + self.message = self._longParam + + +class MessageResultBase(ResultBase): + user_irc_prefix: str + target_name: str + + +class MessageHandlerBase(ChannelHandlerBase): + _request: MessageRequestBase + _result: MessageResultBase + _receiver: ChannelUser + + def __init__(self, client: ClientBase, request: MessageRequestBase): + assert isinstance(request, MessageRequestBase) + super().__init__(client, request) + + def _update_channel_cache(self): + """we do nothing here, channel message do not need to update channel cache""" + pass diff --git a/src/servers/chat/src/abstractions/message.py b/src/servers/chat/src/abstractions/message.py deleted file mode 100644 index 4ca81e6f9..000000000 --- a/src/servers/chat/src/abstractions/message.py +++ /dev/null @@ -1,42 +0,0 @@ -from library.src.abstractions.client import ClientBase -from servers.chat.src.abstractions.channel import ChannelHandlerBase, ChannelRequestBase -from servers.chat.src.abstractions.contract import ResultBase -from servers.chat.src.aggregates.channel_user import ChannelUser -from servers.chat.src.aggregates.enums import MessageType - - -class MessageRequestBase(ChannelRequestBase): - type: MessageType - nick_name: str - message: str - - def parse(self): - super().parse() - if "#" in self.channel_name: - self.type = MessageType.CHANNEL_MESSAGE - else: - self.type = MessageType.USER_MESSAGE - self.nick_name = self._cmd_params[0] - - self.message = self._longParam - - -class MessageResultBase(ResultBase): - user_irc_prefix: str - target_name: str - - -class MessageHandlerBase(ChannelHandlerBase): - _request: MessageRequestBase - _result: MessageResultBase - _receiver: ChannelUser - - def __init__(self, client: ClientBase, request: MessageRequestBase): - assert isinstance(request, MessageRequestBase) - super().__init__(client, request) - - def _update_channel_cache(self): - """we do nothing here, channel message do not need to update channel cache""" - pass - - diff --git a/src/servers/chat/src/aggregates/channel.py b/src/servers/chat/src/aggregates/channel.py index 974f9951e..d67051d79 100644 --- a/src/servers/chat/src/aggregates/channel.py +++ b/src/servers/chat/src/aggregates/channel.py @@ -60,7 +60,7 @@ def __init__(self, name: str, client: Client, password: Optional[str] = None, br self.max_num_user = 200 # setup the message broker self._broker = brocker_cls( - self.name, CONFIG.backend.url, self.get_message_from_brocker) + self.name, CONFIG.backend.url, self._get_message_from_brocker) # channel user init self._broker.subscribe() self.ban_list = {} @@ -173,19 +173,13 @@ def multicast(self, sender: Client, message: ResponseBase, is_skip_sender=False) else: user.client.send(message) - def get_message_from_brocker(self, message: str): + def _get_message_from_brocker(self, message: str): """ we directly send the message from brocker to all channel local user """ for nick, user in self.users.items(): user.client.connection.send(message.encode()) - def send_message_to_brocker(self, message: str): - data = {"channel_name": self.name, "message": message} - import json - data_str = json.dumps(data) - self._broker.publish_message(data_str) - def remove_user(self, user: ChannelUser): user.client.info.previously_joined_channel diff --git a/src/servers/chat/src/aggregates/enums.py b/src/servers/chat/src/aggregates/enums.py index f997dc2c4..96d203982 100644 --- a/src/servers/chat/src/aggregates/enums.py +++ b/src/servers/chat/src/aggregates/enums.py @@ -88,3 +88,33 @@ class PeerRoomType(IntEnum): Staging = 1 Title = 2 Normal = 3 + + +class RequestType(Enum): + CRYPT = "CRYPT" + CDKEY = "CDKEY" + GETKEY = "GETKEY" + LIST = "LIST" + LOGIN = "LOGIN" + NICK = "NICK" + PING = "PING" + QUIT = "QUIT" + SETKEY = "SETKEY" + USER = "USER" + USRIP = "USRIP" + WHO = "WHO" + WHOIS = "WHOIS" + GETCHANKEY = "GETCHANKEY" + GETCKEY = "GETCKEY" + JOIN = "JOIN" + KICK = "KICK" + MODE = "MODE" + NAMES = "NAMES" + PART = "PART" + SETCHANKEY = "SETCHANKEY" + SETCKEY = "SETCKEY" + TOPIC = "TOPIC" + ATM = "ATM" + NOTICE = "NOTICE" + PRIVMSG = "PRIVMSG" + UTM = "UTM" diff --git a/src/servers/chat/src/applications/client.py b/src/servers/chat/src/applications/client.py index dc1fe94dd..0598fc647 100644 --- a/src/servers/chat/src/applications/client.py +++ b/src/servers/chat/src/applications/client.py @@ -1,11 +1,11 @@ from library.src.abstractions.client import ClientBase -from typing import TYPE_CHECKING from library.src.abstractions.switcher import SwitcherBase from library.src.log.log_manager import LogWriter from library.src.network.tcp_handler import TcpConnection from library.src.configs import ServerConfig +from typing import TYPE_CHECKING if TYPE_CHECKING: from servers.chat.src.aggregates.channel import Channel diff --git a/src/servers/chat/src/applications/handlers.py b/src/servers/chat/src/applications/handlers.py index 8a674eb7a..98955e658 100644 --- a/src/servers/chat/src/applications/handlers.py +++ b/src/servers/chat/src/applications/handlers.py @@ -79,18 +79,16 @@ WhoRequest, GetUdpRelayRequest ) -from servers.chat.src.abstractions.message import MessageHandlerBase, MessageRequestBase from servers.chat.src.aggregates.exceptions import ChatException from servers.chat.src.aggregates.enums import ModeRequestType, TopicRequestType from servers.chat.src.aggregates.response_name import * from servers.chat.src.aggregates.managers import ChannelManager from servers.chat.src.aggregates.channel_user import ChannelUser from servers.chat.src.aggregates.channel import Channel -from servers.chat.src.abstractions.channel import ChannelHandlerBase from typing import Type from library.src.abstractions.client import ClientBase from servers.chat.src.abstractions.contract import RequestBase -from servers.chat.src.abstractions.handler import CmdHandlerBase, PostLoginHandlerBase +from servers.chat.src.abstractions.handler import ChannelHandlerBase, CmdHandlerBase, MessageHandlerBase, PostLoginHandlerBase # region General @@ -226,15 +224,9 @@ def __init__(self, client: ClientBase, request: UserIPRequest): assert isinstance(request, UserIPRequest) super().__init__(client, request) - def _request_check(self) -> None: - super()._request_check() - self._request.remote_ip_address = ( - f"{self._client.connection.remote_ip}:{ - self._client.connection.remote_port}" - ) - def _feach_data(self): - pass + self._result = UserIPResult( + remote_ip_address=self._client.connection.remote_ip) def _response_construct(self) -> None: self._response = UserIPResponse(self._result) @@ -378,13 +370,13 @@ def _response_construct(self): case _: raise ChatException("Unknown mode request type") - def _publish_message(self): - if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: - super()._publish_message() + # def _publish_message(self): + # if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: + # super()._publish_message() - def _update_channel_cache(self): - if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: - super()._update_channel_cache() + # def _update_channel_cache(self): + # if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: + # super()._update_channel_cache() def _response_send(self): self._channel.multicast(self._user.client, self._response, True) diff --git a/src/servers/chat/src/applications/switcher.py b/src/servers/chat/src/applications/switcher.py index fe3cc24fe..7089c207b 100644 --- a/src/servers/chat/src/applications/switcher.py +++ b/src/servers/chat/src/applications/switcher.py @@ -2,6 +2,7 @@ from library.src.abstractions.client import ClientBase from library.src.abstractions.handler import CmdHandlerBase from library.src.abstractions.switcher import SwitcherBase +from servers.chat.src.aggregates.enums import RequestType from servers.chat.src.contracts.requests import ( GetCKeyRequest, GetChannelKeyRequest, @@ -74,72 +75,75 @@ def _process_raw_request(self) -> None: splited_raw_requests = self._raw_request.replace("\r", "").split("\n") for raw_request in splited_raw_requests: name = raw_request.strip(" ").split(" ")[0] - self._requests.append((name, raw_request)) + if name not in RequestType: + self._client.log_debug( + f"Request: {name} is not a valid request.") + continue + self._requests.append((RequestType(name), raw_request)) - def _create_cmd_handlers(self, name: str, rawRequest: str) -> Optional[CmdHandlerBase]: - request = rawRequest - assert isinstance(name, str) - match name: + def _create_cmd_handlers(self, name: RequestType, raw_request: str) -> Optional[CmdHandlerBase]: + assert isinstance(name, RequestType) + r_type = RequestType(name) + match r_type: # region General - case "CRYPT": - return CryptHandler(self._client, CryptRequest(request)) - case "CDKEY": - return CdKeyHandler(self._client, CdkeyRequest(request)) - case "GETKEY": - return GetKeyHandler(self._client, GetKeyRequest(request)) - case "LIST": - return ListHandler(self._client, ListRequest(request)) - case "LOGIN": - return LoginHandler(self._client, LoginRequest(request)) - case "NICK": - return NickHandler(self._client, NickRequest(request)) - case "PING": - return PingHandler(self._client, PingRequest(request)) - case "QUIT": - return QuitHandler(self._client, QuitRequest(request)) - case "SETKEY": - return SetKeyHandler(self._client, SetKeyRequest(request)) - case "USER": - return UserHandler(self._client, UserRequest(request)) - case "USRIP": - return UserIPHandler(self._client, UserIPRequest(request)) - case "WHO": - return WhoHandler(self._client, WhoRequest(request)) - case "WHOIS": - return WhoIsHandler(self._client, WhoIsRequest(request)) - # endregionCmdHandlerBase + case RequestType.CRYPT: + return CryptHandler(self._client, CryptRequest(raw_request)) + case RequestType.CDKEY: + return CdKeyHandler(self._client, CdkeyRequest(raw_request)) + case RequestType.GETKEY: + return GetKeyHandler(self._client, GetKeyRequest(raw_request)) + case RequestType.LIST: + return ListHandler(self._client, ListRequest(raw_request)) + case RequestType.LOGIN: + return LoginHandler(self._client, LoginRequest(raw_request)) + case RequestType.NICK: + return NickHandler(self._client, NickRequest(raw_request)) + case RequestType.PING: + return PingHandler(self._client, PingRequest(raw_request)) + case RequestType.QUIT: + return QuitHandler(self._client, QuitRequest(raw_request)) + case RequestType.SETKEY: + return SetKeyHandler(self._client, SetKeyRequest(raw_request)) + case RequestType.USER: + return UserHandler(self._client, UserRequest(raw_request)) + case RequestType.USRIP: + return UserIPHandler(self._client, UserIPRequest(raw_request)) + case RequestType.WHO: + return WhoHandler(self._client, WhoRequest(raw_request)) + case RequestType.WHOIS: + return WhoIsHandler(self._client, WhoIsRequest(raw_request)) - # region Channel - case "GETCHANKEY": - return GetChannelKeyHandler(self._client, GetChannelKeyRequest(request)) - case "GETCKEY": - return GetCKeyHandler(self._client, GetCKeyRequest(request)) - case "JOIN": - return JoinHandler(self._client, JoinRequest(request)) - case "KICK": - return KickHandler(self._client, KickRequest(request)) - case "MODE": - return ModeHandler(self._client, ModeRequest(request)) - case "NAMES": - return NamesHandler(self._client, NamesRequest(request)) - case "PART": - return PartHandler(self._client, PartRequest(request)) - case "SETCHANKEY": - return SetChannelKeyHandler(self._client, SetChannelKeyRequest(request)) - case "SETCKEY": - return SetCKeyHandler(self._client, SetCKeyRequest(request)) - case "TOPIC": - return TopicHandler(self._client, TopicRequest(request)) - # endregion - # region Message - case "ATM": - return ATMHandler(self._client, ATMRequest(request)) - case "NOTICE": - return NoticeHandler(self._client, NoticeRequest(request)) - case "PRIVMSG": - return PrivateHandler(self._client, PrivateRequest(request)) - case "UTM": - return UTMHandler(self._client, UTMRequest(request)) + # Channel commands + case RequestType.GETCHANKEY: + return GetChannelKeyHandler(self._client, GetChannelKeyRequest(raw_request)) + case RequestType.GETCKEY: + return GetCKeyHandler(self._client, GetCKeyRequest(raw_request)) + case RequestType.JOIN: + return JoinHandler(self._client, JoinRequest(raw_request)) + case RequestType.KICK: + return KickHandler(self._client, KickRequest(raw_request)) + case RequestType.MODE: + return ModeHandler(self._client, ModeRequest(raw_request)) + case RequestType.NAMES: + return NamesHandler(self._client, NamesRequest(raw_request)) + case RequestType.PART: + return PartHandler(self._client, PartRequest(raw_request)) + case RequestType.SETCHANKEY: + return SetChannelKeyHandler(self._client, SetChannelKeyRequest(raw_request)) + case RequestType.SETCKEY: + return SetCKeyHandler(self._client, SetCKeyRequest(raw_request)) + case RequestType.TOPIC: + return TopicHandler(self._client, TopicRequest(raw_request)) + + # Message commands + case RequestType.ATM: + return ATMHandler(self._client, ATMRequest(raw_request)) + case RequestType.NOTICE: + return NoticeHandler(self._client, NoticeRequest(raw_request)) + case RequestType.PRIVMSG: + return PrivateHandler(self._client, PrivateRequest(raw_request)) + case RequestType.UTM: + return UTMHandler(self._client, UTMRequest(raw_request)) case _: return None # endregion diff --git a/src/servers/chat/src/contracts/requests.py b/src/servers/chat/src/contracts/requests.py index a858b5e52..8318699ba 100644 --- a/src/servers/chat/src/contracts/requests.py +++ b/src/servers/chat/src/contracts/requests.py @@ -1,13 +1,12 @@ -from servers.chat.src.abstractions.message import MessageRequestBase from library.src.extentions.string_extentions import convert_kvstring_to_dictionary +from servers.chat.src.abstractions.handler import ChannelRequestBase, MessageRequestBase from servers.chat.src.aggregates.enums import ( GetKeyRequestType, ModeOperationType, ModeRequestType, TopicRequestType, ) -from servers.chat.src.abstractions.channel import ChannelRequestBase -from typing import List, Optional +from typing import Optional import re from library.src.extentions.string_extentions import ( convert_keystr_to_list, @@ -299,7 +298,7 @@ def parse(self) -> None: class GetChannelKeyRequest(ChannelRequestBase): cookie: str - keys: List + keys: list def parse(self): super().parse() @@ -316,7 +315,7 @@ def parse(self): class GetCKeyRequest(ChannelRequestBase): nick_name: str cookie: str - keys: List + keys: list request_type: GetKeyRequestType def parse(self): @@ -391,13 +390,17 @@ class ModeRequest(ChannelRequestBase): # "MODE " # "MODE " request_type: ModeRequestType - mode_operations: list = [] + mode_operations: list nick_name: str user_name: str limit_number: int mode_flag: str password: str + def __init__(self, raw_request: str) -> None: + super().__init__(raw_request) + self.mode_operations = [] + def parse(self): if self.raw_request is None: return @@ -550,15 +553,12 @@ def parse(self): class NamesRequest(ChannelRequestBase): - channel_name: str - def __init__(self, raw_request: Optional[str] = None) -> None: if raw_request is not None: super().__init__(raw_request) class PartRequest(ChannelRequestBase): - channel_name: str reason: str = "Unknown reason" def __init__(self, raw_request: Optional[str] = None) -> None: diff --git a/src/servers/chat/src/contracts/responses.py b/src/servers/chat/src/contracts/responses.py index a6c8eae03..fb53e34b5 100644 --- a/src/servers/chat/src/contracts/responses.py +++ b/src/servers/chat/src/contracts/responses.py @@ -1,3 +1,4 @@ +from servers.chat.src.abstractions.handler import ChannelResponseBase from servers.chat.src.aggregates.enums import ModeRequestType, WhoRequestType from servers.chat.src.contracts.results import ( GetCKeyResult, @@ -45,7 +46,6 @@ SERVER_DOMAIN, ResponseBase, ) -from servers.chat.src.abstractions.channel import ChannelResponseBase from library.src.encryption.gs_encryption import CLIENT_KEY, SERVER_KEY from servers.chat.src.aggregates.response_name import * diff --git a/src/servers/chat/src/contracts/results.py b/src/servers/chat/src/contracts/results.py index f4ce9e799..f33dd57cb 100644 --- a/src/servers/chat/src/contracts/results.py +++ b/src/servers/chat/src/contracts/results.py @@ -2,6 +2,7 @@ from pydantic import BaseModel from servers.chat.src.abstractions.contract import ResultBase +from servers.chat.src.abstractions.handler import MessageResultBase # region General @@ -153,7 +154,6 @@ class SetChannelKeyResult(ResultBase): pass # region Message -from servers.chat.src.abstractions.message import MessageResultBase class ATMResult(MessageResultBase): diff --git a/src/servers/chat/tests/game_tests.py b/src/servers/chat/tests/game_tests.py index ef0126099..3cca011e7 100644 --- a/src/servers/chat/tests/game_tests.py +++ b/src/servers/chat/tests/game_tests.py @@ -1,9 +1,13 @@ import unittest +import responses + from servers.chat.tests.mock_objects import create_client class GameTests(unittest.TestCase): + responses.activate + def test_civilization4(self): raws = ["USRIP", "USER X419pGl4sX|18 127.0.0.1 peerchat.gamespy.com :aa3041ada9385b28fc4d4e47db288769", @@ -23,4 +27,41 @@ def test_civilization4(self): "PART #GSP!anno1701 :"] client = create_client() for raw in raws: - client.on_received(raw) + client.on_received(raw.encode()) + + def test_worm3d(self): + raws = [ + "CRYPT des 1 worms3\r\n", + "USRIP\r\n", + "USER X419pGl4sX|6 127.0.0.1 peerchat.gamespy.com :aa3041ada9385b28fc4d4e47db288769\r\n", + "NICK worms10\r\n", + "JOIN #GPG!622\r\n", + "MODE #GPG!622\r\n", + "GETCKEY #GPG!622 * 024 0 :\\username\\b_flags\r\n", + "JOIN #GSP!worms3!Ml4lz344lM\r\n", + "MODE #GSP!worms3!Ml4lz344lM\r\n", + "SETCKEY #GPG!622 worms10 :\\b_flags\\s"+"\r\n", + "SETCKEY #GSP!worms3!Ml4lz344lM worms10 :\\b_flags\\sh"+"\r\n", + "GETCKEY #GSP!worms3!Ml4lz344lM * 025 0 :\\username\\b_flags\r\n", + "TOPIC #GSP!worms3!Ml4lz344lM :tesr\r\n", + "MODE #GSP!worms3!Ml4lz344lM +l 2\r\n", + "PART #GPG!622 :Joined staging room\r\n", + "SETCKEY #GSP!worms3!Ml4lz344lM worms10 :\\b_firewall\\1\\b_profileid\\6\\b_ipaddress\\b_publicip\\255.255.255.255\\b_privateip\\192.168.0.60\b_authresponse\\b_gamever\\1073\\b_val\\0\r\n", + "WHO worms10\r\n", + "SETCHANKEY #GSP!worms3!Ml4lz344lM :\\b_hostname\\test\\b_hostport\\b_MaxPlayers\\2\\b_NumPlayers\\1\\b_SchemeChanging\\0\\b_gamever\\1073\\b_gametype\\b_mapname\\Random\\b_firewall\\1\\b_publicip\\255.255.255.255\\b_privateip\\192.168.0.60\\b_gamemode\\openstaging\\b_val\\0\\b_password\\1\r\n", + "GETKEY worms20 026 0 :\\b_firewall\b_profileid\\b_ipaddress\\b_publicip\\b_privateip\\b_authresponse\\b_gamever\\b_val\r\n", + "GETCKEY #GSP!worms3!Ml4lz344lM worms20 027 0 :\\b_firewall\\b_profileid\\b_ipaddress\\b_publicip\\b_privateip\\b_authresponse\\b_gamever\\b_val\r\n", + "SETCHANKEY #GSP!worms3!Ml4lz344lM :\\b_hostname\\test\\b_hostport\\b_MaxPlayers\\2\\b_NumPlayers\\1\\b_SchemeChanging\\0\\b_gamever\\1073\\b_gametype\\b_mapname\\Random\\b_firewall\\1\\b_publicip\\255.255.255.255\\b_privateip\\192.168.0.60\\b_gamemode\\openstaging\\b_val\0\\b_password\\1\r\n", + "SETCHANKEY #GSP!worms3!Ml4lz344lM :\\b_hostname\\test\\b_hostport\\b_MaxPlayers\\2\\b_NumPlayers\\1\\b_SchemeChanging\\0\\b_gamever\\1073\\b_gametype\\b_mapname\\Random\\b_firewall\\1\\b_publicip\\255.255.255.255\\b_privateip\\192.168.0.60\\b_gamemode\\openstaging\\b_val\\0\\b_password\\1\r\n", + "UTM #GSP!worms3!Ml4lz344lM :MDM |Obj|3|Land.Time|0|LogicalSeed|3891226431|GraphicalSeed|3269271590|Land.RealSeed|3281489942|Land.Theme|Pirate.Lumps|LevelToUse|FE.Level.RandomLand|Land.Ind|0|Wormpot.Reel1|17|Wormpot.Reel2|17|Wormpot.Reel3|17|TimeStamp|6206364\r\n", + "UTM #GSP!worms3!Ml4lz344lM :TDM aA\r\n", + "UTM #GSP!worms3!Ml4lz344lM :SDM ASFE.Scheme.StandardCUnAACADCBBCACBBFFBKBB8C/C3C!A!A*C*C Client: logger=logger) if TYPE_CHECKING: conn._client = cast(Client, conn._client) + create_mock_url(config, UserIPHandler, {"message": "ok"}) + return conn._client diff --git a/src/servers/chat/tests/request_tests.py b/src/servers/chat/tests/request_tests.py new file mode 100644 index 000000000..680423f93 --- /dev/null +++ b/src/servers/chat/tests/request_tests.py @@ -0,0 +1,66 @@ +import unittest +# region General +CD_KEY = "CDKEY XXXX-XXXX-XXXX-XXXX\r\n" +CRYPT = "CRYPT des 1 gmtest\r\n" +GET_KEY = "GETKEY spyguy 004 0 :\\b_firewall\\b_profileid\\b_ipaddress\\b_publicip\\b_privateip\\b_authresponse\\b_gamever\\b_val\r\n" # CRLF +GET_UDP_RELAY = "GETUDPRELAY\r\n" +INVITE = "INVITE test spyguy\r\n" +LIST_LIMIT = "LISTLIMIT 5 test\r\n" +LIST = "LIST test\r\n" +LOGIN_PRE_AUTH = "LOGINPREAUTH xxxxx yyyyy\r\n" +# TODO: add binary data test [0D][0A] +LOGIN_NICK_AND_EMAIL = "LOGIN 0 * xxxxx :spyguy@spyguy@unispy.org\r\n" +LOGIN_UNIQUE_NICK = "LOGIN 0 spyguy xxxxx\r\n" +NAMES = "NAMES\r\n" +NICK = "NICK :spyguy\r\n" +PING = "PING\r\n" # TODO: add binary data test [0D][0A] +PONG = "PONG :Pong!\r\n" +QUIT = "QUIT :Later!\r\n" # TODO: add binary data test [0D][0A] +REGISTER_NICK = "REGISTERNICK 0 spyguy XXXX-XXXX-XXXX-XXXX\r\n" +SET_GROUP = "SETGROUP test\r\n" +SET_KEY = "SETKEY :test\r\n" +USER_IP = "USRIP\r\n" +USER = "USER spyguy 127.0.0.1 peerchat.unispy.org :spyguy2\r\n" +WHO_IS = "WHOIS spyguy\r\n" +WHO_CHANNEL_USERS_INFO = "WHO #room\r\n" +WHO_USER_INFO = "WHO spyguy\r\n" + + +class GeneralRequestTests(unittest.TestCase): + def test_get_chann_key(self): + pass + + +# region Channel +GET_CHANNEL_KEY = "GETCHANKEY #GSP!room!test 0000 0 :\\username\\nickname\0\r\n" +GET_CKEY_CHANNEL_SPECIFIC_USER = "GETCKEY #GSP!room!test spyguy 0000 0 :\\username\\nickname\0\r\n" +GET_CKEY_CHANNEL_ALL_USER = "GETCKEY #GSP!room!test * 0000 0 :\\username\\nickname\0\r\n" +JOIN = "JOIN #GSP!room!test\r\n" +JOIN_WITH_PASS = "JOIN #GSP!room!test pass123\r\n" +KICK = "KICK #islabul spyguy :Spam\r\n" +MODE_CHANNEL = "MODE #GSP!room!test +l 2\r\n" +MODE_USER = "MODE spyguy +s\r\n" +PART = "PART #GSP!room!test :test\r\n" +SET_CHANNEL_KEY = "SETCHANNELKEY #GSP!room!test 0000 0:\\b_flags\\sh\0\r\n" +SET_CKEY = "SETCKEY #GSP!room!test spyguy 0000 0:\\b_flags\\sh\0\r\n" +TOPIC_GET_CHANNEL_TOPIC = "TOPIC #GSP!room!test\r\n" +TOPIC_SET_CHANNEL_TOPIC = "TOPIC #GSP!room!test :This is a topic message.\r\n" + + +class ChannelRequestTests(unittest.TestCase): + def test_get_chann_key(self): + pass + +# region Message + + +ABOVE_THE_TABLE_MSG = "ATM #GSP!room!test :hello this is a test.\r\n" +NOTICE = "NOTICE #GSP!room!test :hello this is a test.\r\n" +PRIVATE_MSG = "PRIVMSG #GSP!room!test :hello this is a test.\r\n" +UNDER_THE_TABLE_MSG = "UTM #GSP!room!test :hello this is a test.\r\n" +ACTION_MSG = "PRIVMSG #GSP!room!test :\001ACTION hello this is a test.\001\r\n" + + +class MessageRequestTests(unittest.TestCase): + def test_get_chann_key(self): + pass diff --git a/src/servers/game_status/src/aggregations/enums.py b/src/servers/game_status/src/aggregations/enums.py index e78936ced..d3ef30f2c 100644 --- a/src/servers/game_status/src/aggregations/enums.py +++ b/src/servers/game_status/src/aggregations/enums.py @@ -1,4 +1,4 @@ -from enum import IntEnum +from enum import Enum, IntEnum class AuthMethod(IntEnum): @@ -20,3 +20,13 @@ class GSErrorCode(IntEnum): PARSE = 1 DATABASE = 2 NOERROR = 3 + + +class RequestType(Enum): + AUTH = "auth" + AUTHP = "authp" + NEWGAME = "newgame" + GETPD = "getpd" + SETPD = "setpd" + UPDGAME = "updgame" + GETPID = "getpid" \ No newline at end of file diff --git a/src/servers/game_status/src/applications/switcher.py b/src/servers/game_status/src/applications/switcher.py index fd26a4824..4e6f74900 100644 --- a/src/servers/game_status/src/applications/switcher.py +++ b/src/servers/game_status/src/applications/switcher.py @@ -1,6 +1,7 @@ from typing import Optional, cast from library.src.abstractions.switcher import SwitcherBase from servers.game_status.src.abstractions.handlers import CmdHandlerBase +from servers.game_status.src.aggregations.enums import RequestType from servers.game_status.src.applications.client import Client from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest from servers.game_status.src.applications.handlers import AuthGameHandler, AuthPlayerHandler, GetPlayerDataHandler, GetProfileIdHandler, NewGameHandler, SetPlayerDataHandler, UpdateGameHandler @@ -22,24 +23,29 @@ def _process_raw_request(self) -> None: r+"\\final\\" for r in self._raw_request.split("\\final\\") if r] for raw_request in raw_requests: name = raw_request.strip("\\").split("\\", 1)[0] - self._requests.append((name, raw_request)) + if name not in RequestType: + self._client.log_debug( + f"Request: {name} is not a valid request.") + return + self._requests.append((RequestType(name), raw_request)) - def _create_cmd_handlers(self, name: object, raw_request: str) -> Optional[CmdHandlerBase]: + def _create_cmd_handlers(self, name: RequestType, raw_request: str) -> Optional[CmdHandlerBase]: + assert isinstance(name, RequestType) self._client = cast(Client, self._client) match name: - case "auth": + case RequestType.AUTH: return AuthGameHandler(self._client, AuthGameRequest(raw_request)) - case "authp": + case RequestType.AUTHP: return AuthPlayerHandler(self._client, AuthPlayerRequest(raw_request)) - case "newgame": + case RequestType.NEWGAME: return NewGameHandler(self._client, NewGameRequest(raw_request)) - case "getpd": + case RequestType.GETPD: return GetPlayerDataHandler(self._client, GetPlayerDataRequest(raw_request)) - case "setpd": + case RequestType.SETPD: return SetPlayerDataHandler(self._client, SetPlayerDataRequest(raw_request)) - case "updgame": + case RequestType.UPDGAME: return UpdateGameHandler(self._client, UpdateGameRequest(raw_request)) - case "getpid": + case RequestType.GETPID: return GetProfileIdHandler(self._client, GetProfileIdRequest(raw_request)) case _: return None diff --git a/src/servers/natneg/src/applications/switcher.py b/src/servers/natneg/src/applications/switcher.py index 1bbf4099a..67d6c5c51 100644 --- a/src/servers/natneg/src/applications/switcher.py +++ b/src/servers/natneg/src/applications/switcher.py @@ -31,8 +31,13 @@ def __init__(self, client: Client, raw_request: bytes) -> None: assert isinstance(raw_request, bytes) def _process_raw_request(self) -> None: - name = RequestType(self._raw_request[7]) - self._requests.append((name, self._raw_request)) + + name = self._raw_request[7] + if name not in RequestType: + self._client.log_debug( + f"Request: {name} is not a valid request.") + return + self._requests.append((RequestType(name), self._raw_request)) def _create_cmd_handlers( self, name: RequestType, raw_request: bytes @@ -58,4 +63,4 @@ def _create_cmd_handlers( case RequestType.PRE_INIT: return None case _: - return None \ No newline at end of file + return None diff --git a/src/servers/presence_connection_manager/src/aggregates/enums.py b/src/servers/presence_connection_manager/src/aggregates/enums.py index bad487837..134bd808f 100644 --- a/src/servers/presence_connection_manager/src/aggregates/enums.py +++ b/src/servers/presence_connection_manager/src/aggregates/enums.py @@ -1,4 +1,4 @@ -from enum import IntEnum, IntFlag +from enum import Enum, IntEnum, IntFlag class BuddyMessageType(IntEnum): @@ -238,3 +238,19 @@ class PublicMasks(IntEnum): SEX = 0x00000010 EMAIL = 0x00000020 ALL = 0xFFFFFFFF + + +class RequestType(Enum): + KA = "ka" + LOGIN = "login" + LOGOUT = "logout" + NEWUSER = "newuser" + ADDBLOCK = "addblock" + GETPROFILE = "getprofile" + NEWPROFILE = "newprofile" + REGISTERCDKEY = "registercdkey" + REGISTERNICK = "registernick" + UPDATEPRO = "updatepro" + STATUS = "status" + STATUSINFO = "statusinfo" + INVITETO = "inviteto" diff --git a/src/servers/presence_connection_manager/src/applications/switcher.py b/src/servers/presence_connection_manager/src/applications/switcher.py index 374f50cb1..b75f0afcb 100644 --- a/src/servers/presence_connection_manager/src/applications/switcher.py +++ b/src/servers/presence_connection_manager/src/applications/switcher.py @@ -1,4 +1,5 @@ from library.src.abstractions.switcher import SwitcherBase +from servers.presence_connection_manager.src.aggregates.enums import RequestType from servers.presence_connection_manager.src.contracts.requests import KeepAliveRequest, LoginRequest, LogoutRequest, StatusInfoRequest, StatusRequest, AddBlockRequest, GetProfileRequest, NewProfileRequest, RegisterCDKeyRequest, NewUserRequest, RegisterNickRequest, UpdateProfileRequest from servers.presence_connection_manager.src.applications.handlers import AddBlockHandler, GetProfileHandler, KeepAliveHandler, LoginHandler, LogoutHandler, NewProfileHandler, NewUserHandler, RegisterCDKeyHandler, RegisterNickHandler, StatusHandler, StatusInfoHandler, UpdateProfileHandler from servers.presence_search_player.src.aggregates.exceptions import GPParseException @@ -24,37 +25,44 @@ def _process_raw_request(self) -> None: r+"\\final\\" for r in self._raw_request.split("\\final\\") if r] for raw_request in raw_requests: name = raw_request.strip("\\").split("\\")[0] - self._requests.append((name, raw_request)) + if name not in RequestType: + self._client.log_debug( + f"Request: {name} is not a valid request.") + continue + self._requests.append((RequestType(name), raw_request)) - def _create_cmd_handlers(self, name: str, raw_request: str) -> Optional[CmdHandlerBase]: + def _create_cmd_handlers(self, name: RequestType, raw_request: str) -> Optional[CmdHandlerBase]: + assert isinstance(name, RequestType) + assert isinstance(raw_request, str) if TYPE_CHECKING: self._client = cast(Client, self._client) match name: - case "ka": + case RequestType.KA: return KeepAliveHandler(self._client, KeepAliveRequest(raw_request)) - case "login": + case RequestType.LOGIN: return LoginHandler(self._client, LoginRequest(raw_request)) - case "logout": + case RequestType.LOGOUT: return LogoutHandler(self._client, LogoutRequest(raw_request)) - case "newuser": + case RequestType.NEWUSER: return NewUserHandler(self._client, NewUserRequest(raw_request)) - case "addblock": + case RequestType.ADDBLOCK: return AddBlockHandler(self._client, AddBlockRequest(raw_request)) - case "getprofile": + case RequestType.GETPROFILE: return GetProfileHandler(self._client, GetProfileRequest(raw_request)) - case "newprofile": + case RequestType.NEWPROFILE: return NewProfileHandler(self._client, NewProfileRequest(raw_request)) - case "registercdkey": + case RequestType.REGISTERCDKEY: return RegisterCDKeyHandler(self._client, RegisterCDKeyRequest(raw_request)) - case "registernick": + case RequestType.REGISTERNICK: return RegisterNickHandler(self._client, RegisterNickRequest(raw_request)) - case "updatepro": + case RequestType.UPDATEPRO: return UpdateProfileHandler(self._client, UpdateProfileRequest(raw_request)) - case "status": + case RequestType.STATUS: return StatusHandler(self._client, StatusRequest(raw_request)) - case "statusinfo": + case RequestType.STATUSINFO: return StatusInfoHandler(self._client, StatusInfoRequest(raw_request)) - case "inviteto": - raise NotImplementedError() + case RequestType.INVITETO: + raise NotImplementedError( + "InviteToHandler is not implemented.") case _: return None diff --git a/src/servers/presence_connection_manager/tests/game_tests.py b/src/servers/presence_connection_manager/tests/game_tests.py index 3de477d46..78660a903 100644 --- a/src/servers/presence_connection_manager/tests/game_tests.py +++ b/src/servers/presence_connection_manager/tests/game_tests.py @@ -18,7 +18,7 @@ def test_civilization_4(self) -> None: for x in raw_requests: - client.on_received(x.encode("ascii")) + client.on_received(x.encode()) pass @unittest.skip("not finished handler") diff --git a/src/servers/presence_search_player/src/aggregates/enums.py b/src/servers/presence_search_player/src/aggregates/enums.py index 4530eaee7..573c51d28 100644 --- a/src/servers/presence_search_player/src/aggregates/enums.py +++ b/src/servers/presence_search_player/src/aggregates/enums.py @@ -1,4 +1,4 @@ -from enum import IntEnum +from enum import Enum, IntEnum class SearchType(IntEnum): @@ -123,3 +123,17 @@ class GPErrorCode(IntEnum): # RemoveBlock. REMOVE_BLOCK = 0x1300 REMOVE_BLOCK_NOT_BLOCKED = 0x1301 + + + +class RequestType(Enum): + CHECK = "check" + NEWUSER = "newuser" + NICKS = "nicks" + OTHERS = "others" + OTHERSLIST = "otherslist" + PMATCH = "pmatch" + SEARCH = "search" + SEARCHUNIQUE = "searchunique" + UNIQUESearch = "uniquesearch" + VALID = "valid" \ No newline at end of file diff --git a/src/servers/presence_search_player/src/applications/switcher.py b/src/servers/presence_search_player/src/applications/switcher.py index ad8c5a960..c1038a576 100644 --- a/src/servers/presence_search_player/src/applications/switcher.py +++ b/src/servers/presence_search_player/src/applications/switcher.py @@ -1,5 +1,6 @@ from typing import TYPE_CHECKING, Optional, cast from library.src.abstractions.switcher import SwitcherBase +from servers.presence_search_player.src.aggregates.enums import RequestType from servers.presence_search_player.src.contracts.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest from servers.presence_search_player.src.applications.handlers import CheckHandler, NewUserHandler, NicksHandler, OthersHandler, OthersListHandler, SearchHandler, SearchUniqueHandler, UniqueSearchHandler, ValidHandler @@ -25,37 +26,39 @@ def _process_raw_request(self): r+"\\final\\" for r in self._raw_request.split("\\final\\") if r] for raw_request in raw_requests: name = raw_request.strip("\\").split("\\", 1)[0] - self._requests.append((name, raw_request)) + if name not in RequestType: + self._client.log_debug( + f"Request: {name} is not a valid request.") + continue + self._requests.append((RequestType(name), raw_request)) - def _create_cmd_handlers(self, name: str, raw_request: str) -> Optional[CmdHandlerBase]: + def _create_cmd_handlers(self, name: RequestType, raw_request: str) -> Optional[CmdHandlerBase]: + assert isinstance(name, RequestType) if TYPE_CHECKING: self._client = cast(Client, self._client) match name: - case "check": + case RequestType.CHECK: return CheckHandler(self._client, CheckRequest(raw_request)) - case "newuser": + case RequestType.NEWUSER: return NewUserHandler(self._client, NewUserRequest(raw_request)) - case "nicks": + case RequestType.NICKS: return NicksHandler(self._client, NicksRequest(raw_request)) - case "others": + case RequestType.OTHERS: return OthersHandler(self._client, OthersRequest(raw_request)) - case "otherslist": + case RequestType.OTHERSLIST: return OthersListHandler(self._client, OthersListRequest(raw_request)) - case "pmatch": + case RequestType.PMATCH: + # Uncomment the line below when PMatchHandler is implemented # return PMatchHandler(self._client, PMatchRequest(raw_request)) - raise NotImplementedError() - case "search": + raise NotImplementedError("PMatchHandler is not implemented.") + case RequestType.SEARCH: return SearchHandler(self._client, SearchRequest(raw_request)) - case "searchunique": - return SearchUniqueHandler( - self._client, SearchUniqueRequest(raw_request) - ) - case "uniquesearch": - return UniqueSearchHandler( - self._client, UniqueSearchRequest(raw_request) - ) - case "valid": + case RequestType.SEARCHUNIQUE: + return SearchUniqueHandler(self._client, SearchUniqueRequest(raw_request)) + case RequestType.UNIQUESearch: + return UniqueSearchHandler(self._client, UniqueSearchRequest(raw_request)) + case RequestType.VALID: return ValidHandler(self._client, ValidRequest(raw_request)) case _: return None diff --git a/src/servers/query_report/src/v2/applications/handlers.py b/src/servers/query_report/src/v2/applications/handlers.py index 20a19a216..899e70f75 100644 --- a/src/servers/query_report/src/v2/applications/handlers.py +++ b/src/servers/query_report/src/v2/applications/handlers.py @@ -83,15 +83,10 @@ def __init__(self, client: Client, request: HeartBeatRequest) -> None: assert isinstance(request, HeartBeatRequest) super().__init__(client, request) - def _request_check(self) -> None: - super()._request_check() - self._request.remote_ip_address = self._client.connection.remote_ip - self._request.remote_port = self._client.connection.remote_port - def _feach_data(self): self._result = HeartBeatResult( - remote_ip_address=self._request.remote_ip_address, - remote_port=self._request.remote_port) + remote_ip_address=self._client.connection.remote_ip, + remote_port=self._client.connection.remote_port) def _response_construct(self) -> None: self._response = HeartBeatResponse(self._request, self._result) @@ -103,3 +98,6 @@ class KeepAliveHandler(CmdHandlerBase): def __init__(self, client: Client, request: KeepAliveRequest) -> None: assert isinstance(request, KeepAliveRequest) super().__init__(client, request) + + def _feach_data(self): + pass diff --git a/src/servers/query_report/src/v2/applications/switcher.py b/src/servers/query_report/src/v2/applications/switcher.py index 3550b83ee..0e5785cbe 100644 --- a/src/servers/query_report/src/v2/applications/switcher.py +++ b/src/servers/query_report/src/v2/applications/switcher.py @@ -27,33 +27,33 @@ class CmdSwitcher(SwitcherBase): _raw_request: bytes - def __init__(self, client: Client, raw_request: bytes): - super().__init__(client, raw_request) - def _process_raw_request(self) -> None: if len(self._raw_request) < 4: raise UniSpyException("Invalid request") - - name = RequestType(self._raw_request[0]) + name = self._raw_request[0] + if name not in RequestType: + self._client.log_debug( + f"Request: {name} is not a valid request.") + return raw_request = self._raw_request - self._requests.append((name, raw_request)) + self._requests.append((RequestType(name), raw_request)) - def _create_cmd_handlers(self, name: int, raw_request: bytes) -> Optional[CmdHandlerBase]: - req = raw_request + def _create_cmd_handlers(self, name: RequestType, raw_request: bytes) -> Optional[CmdHandlerBase]: + assert isinstance(name, RequestType) if TYPE_CHECKING: self._client = cast(Client, self._client) match name: case RequestType.HEARTBEAT: - return HeartBeatHandler(self._client, HeartBeatRequest(req)) + return HeartBeatHandler(self._client, HeartBeatRequest(raw_request)) case RequestType.CHALLENGE: - return ChallengeHanler(self._client, ChallengeRequest(req)) + return ChallengeHanler(self._client, ChallengeRequest(raw_request)) case RequestType.AVALIABLE_CHECK: - return AvailableHandler(self._client, AvaliableRequest(req)) + return AvailableHandler(self._client, AvaliableRequest(raw_request)) case RequestType.CLIENT_MESSAGE_ACK: - return ClientMessageAckHandler(self._client, ClientMessageAckRequest(req)) + return ClientMessageAckHandler(self._client, ClientMessageAckRequest(raw_request)) case RequestType.ECHO: - return EchoHandler(self._client, EchoRequest(req)) + return EchoHandler(self._client, EchoRequest(raw_request)) case RequestType.KEEP_ALIVE: - return KeepAliveHandler(self._client, KeepAliveRequest(req)) + return KeepAliveHandler(self._client, KeepAliveRequest(raw_request)) case _: return None diff --git a/src/servers/query_report/tests/mock_objects.py b/src/servers/query_report/tests/mock_objects.py index 7d395c95e..befc971f3 100644 --- a/src/servers/query_report/tests/mock_objects.py +++ b/src/servers/query_report/tests/mock_objects.py @@ -2,7 +2,7 @@ from library.src.configs import CONFIG from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url from servers.query_report.src.applications.client import Client -from servers.query_report.src.v2.applications.handlers import AvailableHandler, HeartBeatHandler +from servers.query_report.src.v2.applications.handlers import AvailableHandler, HeartBeatHandler, KeepAliveHandler from servers.query_report.src.v2.contracts.results import HeartBeatResult @@ -21,4 +21,5 @@ def create_client() -> Client: create_mock_url(config, HeartBeatHandler, HeartBeatResult.model_validate( {"remote_ip_address": conn.remote_ip, "remote_port": conn.remote_port}).model_dump()) create_mock_url(config, AvailableHandler, {"message": "ok"}) + create_mock_url(config, KeepAliveHandler, {"message": "ok"}) return cast(Client, conn._client) diff --git a/src/servers/server_browser/src/v2/applications/switcher.py b/src/servers/server_browser/src/v2/applications/switcher.py index 7f5e60d34..db303a39a 100644 --- a/src/servers/server_browser/src/v2/applications/switcher.py +++ b/src/servers/server_browser/src/v2/applications/switcher.py @@ -14,8 +14,13 @@ class CmdSwitcher(SwitcherBase): def _process_raw_request(self) -> None: if len(self._raw_request) < 4: raise UniSpyException("Invalid request") - name = RequestType(self._raw_request[2]) - self._requests.append((name, self._raw_request)) + name = self._raw_request[2] + if name not in RequestType: + self._client.log_debug( + f"Request: {name} is not a valid request.") + return + + self._requests.append((RequestType(name), self._raw_request)) def _create_cmd_handlers(self, name: int, raw_request: bytes) -> Optional[CmdHandlerBase]: req = raw_request diff --git a/src/servers/web_services/src/applications/client.py b/src/servers/web_services/src/applications/client.py index 71d228c00..befde86c4 100644 --- a/src/servers/web_services/src/applications/client.py +++ b/src/servers/web_services/src/applications/client.py @@ -37,11 +37,6 @@ def __init__(self, connection: HttpConnection, server_config: ServerConfig, logg super().__init__(connection, server_config, logger) self.info = ClientInfo() - def on_received(self, buffer: str) -> None: - assert isinstance(buffer, str) - super().on_received(buffer) - - def _create_switcher(self, buffer: str) -> SwitcherBase: - assert isinstance(buffer, str) + def _create_switcher(self, buffer: bytes) -> SwitcherBase: from servers.web_services.src.applications.switcher import Switcher - return Switcher(self, buffer) + return Switcher(self, buffer.decode()) From e443740e71031a859c9ff79537d6bd1e25bf021d Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 1 Nov 2024 03:15:03 +0000 Subject: [PATCH 134/231] fix: router url double slash --- .../library/abstractions/contracts.py | 2 +- .../library/abstractions/handler_base.py | 20 +- .../protocols/gamespy/chat/handlers.py | 9 +- .../protocols/gamespy/chat/requests.py | 1 + .../protocols/gamespy/natneg/handlers.py | 4 +- .../gamespy/presence_connection_manager.py | 24 +- .../routers/gamespy/presence_search_player.py | 20 +- src/backends/routers/gamespy/query_report.py | 12 +- .../routers/gamespy/server_browser.py | 8 +- src/servers/chat/src/abstractions/handler.py | 13 +- src/unispy | 350 ++++++++++++++++++ 11 files changed, 411 insertions(+), 52 deletions(-) diff --git a/src/backends/library/abstractions/contracts.py b/src/backends/library/abstractions/contracts.py index 4c678ffb3..4f60748b8 100644 --- a/src/backends/library/abstractions/contracts.py +++ b/src/backends/library/abstractions/contracts.py @@ -13,5 +13,5 @@ class RequestBase(BaseModel): """ -class GeneralResponse(BaseModel): +class ErrorResponse(BaseModel): message: str diff --git a/src/backends/library/abstractions/handler_base.py b/src/backends/library/abstractions/handler_base.py index 5644c6fee..0a6118b04 100644 --- a/src/backends/library/abstractions/handler_base.py +++ b/src/backends/library/abstractions/handler_base.py @@ -1,5 +1,5 @@ -from abc import abstractmethod -from backends.library.abstractions.contracts import RequestBase +from abc import abstractmethod, ABC +from backends.library.abstractions.contracts import ErrorResponse, RequestBase from library.src.abstractions.contracts import ResultBase import logging @@ -26,24 +26,24 @@ def __init__(self, request: RequestBase) -> None: async def handle(self) -> None: try: - await self.request_check() - await self.data_fetch() - await self.result_construct() + await self._request_check() + await self._data_fetch() + await self._result_construct() except UniSpyException as ex: self.logger.error(ex.message) - self.response = {"message": ex.message} + self.response = ErrorResponse(message=ex.message).model_dump() except Exception as ex: self.logger.error(ex) - self.response = {"message": str(ex)} + self.response = ErrorResponse(message=str(ex)).model_dump() @abstractmethod - async def request_check(self) -> None: + async def _request_check(self) -> None: pass @abstractmethod - async def data_fetch(self) -> None: + async def _data_fetch(self) -> None: pass @abstractmethod - async def result_construct(self) -> None: + async def _result_construct(self) -> None: pass diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index 10559fd74..9b61dd42e 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -1,6 +1,11 @@ from backends.library.abstractions.handler_base import HandlerBase +import backends.protocols.gamespy.chat.data as data +from backends.protocols.gamespy.chat.requests import JoinRequest class JoinHandler(HandlerBase): - async def data_fetch(self) -> None: - pass + _request: JoinRequest + + async def _data_fetch(self) -> None: + data.is_channel_exist(self._request.channel_name, + self._request.game_name) diff --git a/src/backends/protocols/gamespy/chat/requests.py b/src/backends/protocols/gamespy/chat/requests.py index 2c64e74cd..b8e427e47 100644 --- a/src/backends/protocols/gamespy/chat/requests.py +++ b/src/backends/protocols/gamespy/chat/requests.py @@ -131,6 +131,7 @@ class GetCKeyRequest(ChannelRequestBase): class JoinRequest(ChannelRequestBase): password: str + game_name: str class KickRequest(ChannelRequestBase): diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py index 40ad739d4..4b520df94 100644 --- a/src/backends/protocols/gamespy/natneg/handlers.py +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -9,9 +9,9 @@ def __init__(self, request: InitRequest) -> None: super().__init__(request) assert isinstance(request, InitRequest) - async def data_fetch(self) -> None: + async def _data_fetch(self) -> None: info = InitPacketCaches(**self._request.model_dump()) update_init_info(info) - async def result_construct(self) -> None: + async def _result_construct(self) -> None: self.response = {"message": "ok"} diff --git a/src/backends/routers/gamespy/presence_connection_manager.py b/src/backends/routers/gamespy/presence_connection_manager.py index e03808454..2dfc0a550 100644 --- a/src/backends/routers/gamespy/presence_connection_manager.py +++ b/src/backends/routers/gamespy/presence_connection_manager.py @@ -8,62 +8,62 @@ router = APIRouter() -@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/LoginHandler") +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/LoginHandler") async def login(request: LoginRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/LogoutHandler") +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/LogoutHandler") async def logout(request: LogoutRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/KeepAliveHandler") +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/KeepAliveHandler") async def keep_alive(request: KeepAliveRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/NewUserHandler") +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/NewUserHandler") async def new_user(request: NewUserRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/AddBlockHandler") +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/AddBlockHandler") async def add_block(request: AddBlockRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/GetProfileHandler") +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/GetProfileHandler") async def get_profile(request: GetProfileRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/NewProfileHandler") +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/NewProfileHandler") async def new_proflie(request: NewProfileRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/RegisterCDKeyHandler") +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/RegisterCDKeyHandler") async def register_cdkey(request: RegisterCDKeyRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/RegisterNickHandler") +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/RegisterNickHandler") async def register_nick(request: RegisterNickRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/UpdateProfileHandler") +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/UpdateProfileHandler") async def update_profile(request: UpdateProfileRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/StatusHandler") +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/StatusHandler") async def status(request: StatusRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_CONNECTION_MANAGER}/StatusInfoHandler") +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/StatusInfoHandler") async def status_info(request: StatusInfoRequest): raise NotImplementedError() diff --git a/src/backends/routers/gamespy/presence_search_player.py b/src/backends/routers/gamespy/presence_search_player.py index 958b01b64..18ea536c3 100644 --- a/src/backends/routers/gamespy/presence_search_player.py +++ b/src/backends/routers/gamespy/presence_search_player.py @@ -6,52 +6,52 @@ router = APIRouter() -@router.post(f"/{PRESENCE_SEARCH_PLAYER}/CheckHandler") +@router.post(f"{PRESENCE_SEARCH_PLAYER}/CheckHandler") async def check(request: CheckRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_SEARCH_PLAYER}/NewUserHandler") +@router.post(f"{PRESENCE_SEARCH_PLAYER}/NewUserHandler") async def new_user(request: NewUserRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_SEARCH_PLAYER}/NicksHandler") +@router.post(f"{PRESENCE_SEARCH_PLAYER}/NicksHandler") async def nicks(request: NicksRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_SEARCH_PLAYER}/OthersHandler") +@router.post(f"{PRESENCE_SEARCH_PLAYER}/OthersHandler") async def others(request: OthersRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_SEARCH_PLAYER}/OthersListHandler") +@router.post(f"{PRESENCE_SEARCH_PLAYER}/OthersListHandler") async def others_list(request: OthersListRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_SEARCH_PLAYER}/PMatchHandler") +@router.post(f"{PRESENCE_SEARCH_PLAYER}/PMatchHandler") async def player_match(request: object): raise NotImplementedError() -@router.post(f"/{PRESENCE_SEARCH_PLAYER}/SearchHandler") +@router.post(f"{PRESENCE_SEARCH_PLAYER}/SearchHandler") async def search(request: SearchRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_SEARCH_PLAYER}/SearchUniqueHandler") +@router.post(f"{PRESENCE_SEARCH_PLAYER}/SearchUniqueHandler") async def search_unique(request: SearchUniqueRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_SEARCH_PLAYER}/UniqueSearchHandler") +@router.post(f"{PRESENCE_SEARCH_PLAYER}/UniqueSearchHandler") async def unique_search(request: UniqueSearchRequest): raise NotImplementedError() -@router.post(f"/{PRESENCE_SEARCH_PLAYER}/ValidHandler") +@router.post(f"{PRESENCE_SEARCH_PLAYER}/ValidHandler") async def valid(request: ValidRequest): raise NotImplementedError() diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index 2c2f924b8..8bbd78615 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -7,32 +7,32 @@ router = APIRouter() -@router.post(f"/{QUERY_REPORT}/HeartBeatHandler") +@router.post(f"{QUERY_REPORT}/HeartBeatHandler") async def heartbeat(request: HeartBeatRequest): raise NotImplementedError() -@router.post(f"/{QUERY_REPORT}/ChallengeHanler") +@router.post(f"{QUERY_REPORT}/ChallengeHanler") async def challenge(request: ChallengeRequest): raise NotImplementedError() -@router.post(f"/{QUERY_REPORT}/AvailableHandler") +@router.post(f"{QUERY_REPORT}/AvailableHandler") async def available(request: AvaliableRequest): raise NotImplementedError() -@router.post(f"/{QUERY_REPORT}/ClientMessageAckHandler") +@router.post(f"{QUERY_REPORT}/ClientMessageAckHandler") async def client_message(request: ClientMessageRequest): raise NotImplementedError() -@router.post(f"/{QUERY_REPORT}/EchoHandler") +@router.post(f"{QUERY_REPORT}/EchoHandler") async def echo(request: EchoRequest): raise NotImplementedError() -@router.post(f"/{QUERY_REPORT}/KeepAliveHandler") +@router.post(f"{QUERY_REPORT}/KeepAliveHandler") async def keep_alive(request: KeepAliveRequest): raise NotImplementedError() diff --git a/src/backends/routers/gamespy/server_browser.py b/src/backends/routers/gamespy/server_browser.py index 959798bb2..a3a6c2041 100644 --- a/src/backends/routers/gamespy/server_browser.py +++ b/src/backends/routers/gamespy/server_browser.py @@ -8,7 +8,7 @@ client_pool: dict[str, WebSocket] = {} -@router.websocket(f"/{SERVER_BROWSER_V2}/AdHocHandler") +@router.websocket(f"{SERVER_BROWSER_V2}/AdHocHandler") async def check(websocket: WebSocket): """ notify every server browser to send message to its client @@ -24,17 +24,17 @@ async def check(websocket: WebSocket): del client_pool[websocket.client.host] -@router.post(f"/{SERVER_BROWSER_V2}/SendMessageHandler") +@router.post(f"{SERVER_BROWSER_V2}/SendMessageHandler") async def send_message(request: SendMessageRequest): raise NotImplementedError() -@router.post(f"/{SERVER_BROWSER_V2}/ServerInfoHandler") +@router.post(f"{SERVER_BROWSER_V2}/ServerInfoHandler") async def server_info(request: ServerInfoRequest): raise NotImplementedError() -@router.post(f"/{SERVER_BROWSER_V2}/ServerListHandler") +@router.post(f"{SERVER_BROWSER_V2}/ServerListHandler") async def server_list(request: ServerListRequest): raise NotImplementedError() diff --git a/src/servers/chat/src/abstractions/handler.py b/src/servers/chat/src/abstractions/handler.py index dbda3bae6..6524f4114 100644 --- a/src/servers/chat/src/abstractions/handler.py +++ b/src/servers/chat/src/abstractions/handler.py @@ -3,7 +3,6 @@ from typing import TYPE_CHECKING from servers.chat.src.aggregates.exceptions import ChatException, NoSuchNickException, NoSuchChannelException from servers.chat.src.aggregates.managers import ChannelManager -from servers.chat.src.abstractions.handler import PostLoginHandlerBase from servers.chat.src.abstractions.contract import RequestBase from servers.chat.src.abstractions.contract import * from library.src.abstractions.client import ClientBase @@ -13,10 +12,14 @@ import library.src.abstractions.handler from typing import cast +if TYPE_CHECKING: + from servers.chat.src.aggregates.channel import Channel + from servers.chat.src.aggregates.channel_user import ChannelUser + class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase): - _request: RequestBase _client: Client + _request: RequestBase _result: ResultBase def __init__(self, client: ClientBase, request: RequestBase): @@ -62,8 +65,8 @@ def __init__(self, request: RequestBase, result: ResultBase) -> None: class ChannelHandlerBase(PostLoginHandlerBase): - _channel: Channel - _user: ChannelUser + _channel: "Channel" + _user: "ChannelUser" _request: ChannelRequestBase _response: ResponseBase @@ -119,7 +122,7 @@ class MessageResultBase(ResultBase): class MessageHandlerBase(ChannelHandlerBase): _request: MessageRequestBase _result: MessageResultBase - _receiver: ChannelUser + _receiver: "ChannelUser" def __init__(self, client: ClientBase, request: MessageRequestBase): assert isinstance(request, MessageRequestBase) diff --git a/src/unispy b/src/unispy index dd0095b58..2601530ef 100644 --- a/src/unispy +++ b/src/unispy @@ -61,3 +61,353 @@ Connection: Upgrade 2024-10-30 05:51:24 [unispy] [DEBUG]: server: uvicorn 2024-10-30 05:51:24 [unispy] [DEBUG]: ----------------------- 2024-10-30 05:51:24 [unispy] [INFO]: Websocket connected +2024-11-01 03:12:23 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:23 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:24 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:24 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:24 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:25 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:25 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:25 [unispy] [INFO]: 4 changes detected +2024-11-01 03:12:26 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:26 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:26 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:27 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:27 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:27 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:28 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:28 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:28 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:29 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:29 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:30 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:30 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:30 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:31 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:31 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:31 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:32 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:32 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:32 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:33 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:33 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:33 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:34 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:34 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:34 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:35 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:35 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:36 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:36 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:36 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:37 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:37 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:37 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:38 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:38 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:38 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:39 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:39 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:39 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:40 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:40 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:40 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:41 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:41 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:41 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:42 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:42 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:43 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:43 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:43 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:44 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:44 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:44 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:45 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:45 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:45 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:46 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:46 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:46 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:47 [unispy] [INFO]: 4 changes detected +2024-11-01 03:12:47 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:48 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:48 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:48 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:49 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:49 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:49 [unispy] [INFO]: 10 changes detected +2024-11-01 03:12:50 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:50 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:50 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:51 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:51 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:51 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:52 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:52 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:52 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:53 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:53 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:54 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:54 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:54 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:55 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:55 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:55 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:56 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:56 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:56 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:57 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:57 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:57 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:58 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:58 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:58 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:59 [unispy] [INFO]: 1 change detected +2024-11-01 03:12:59 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:00 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:00 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:00 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:01 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:01 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:01 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:02 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:02 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:02 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:03 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:03 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:03 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:04 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:04 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:04 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:05 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:05 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:05 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:06 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:06 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:07 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:07 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:07 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:08 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:08 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:08 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:09 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:09 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:09 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:10 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:10 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:10 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:11 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:11 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:11 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:12 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:12 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:12 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:13 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:13 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:14 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:14 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:14 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:15 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:15 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:15 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:16 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:16 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:16 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:17 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:17 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:17 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:18 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:18 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:18 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:19 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:19 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:19 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:20 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:20 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:21 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:21 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:21 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:22 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:22 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:22 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:23 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:23 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:23 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:24 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:24 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:24 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:25 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:25 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:25 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:26 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:26 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:27 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:27 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:27 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:28 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:28 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:28 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:29 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:29 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:29 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:30 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:30 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:30 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:31 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:31 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:31 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:32 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:32 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:32 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:33 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:33 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:34 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:34 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:34 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:35 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:35 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:35 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:36 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:36 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:36 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:37 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:37 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:37 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:38 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:38 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:38 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:39 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:39 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:39 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:40 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:40 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:41 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:41 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:41 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:42 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:42 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:42 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:43 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:43 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:43 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:44 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:44 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:44 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:45 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:45 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:45 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:46 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:46 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:47 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:47 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:47 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:48 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:48 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:48 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:49 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:49 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:49 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:50 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:50 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:50 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:51 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:51 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:51 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:52 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:52 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:52 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:53 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:53 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:54 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:54 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:54 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:55 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:55 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:55 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:56 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:56 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:56 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:57 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:57 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:57 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:58 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:58 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:58 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:59 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:59 [unispy] [INFO]: 1 change detected +2024-11-01 03:13:59 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:00 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:00 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:01 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:01 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:01 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:02 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:02 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:02 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:03 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:03 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:03 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:04 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:04 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:04 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:05 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:05 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:05 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:06 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:06 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:06 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:07 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:07 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:08 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:08 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:08 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:09 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:09 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:09 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:10 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:10 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:10 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:11 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:11 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:11 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:12 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:12 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:12 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:13 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:13 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:14 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:14 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:14 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:15 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:15 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:15 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:16 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:16 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:16 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:17 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:17 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:17 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:18 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:18 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:18 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:19 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:19 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:19 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:20 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:20 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:21 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:21 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:21 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:22 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:22 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:22 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:23 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:23 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:23 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:24 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:24 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:24 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:25 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:25 [unispy] [INFO]: 1 change detected +2024-11-01 03:14:25 [unispy] [INFO]: 1 change detected From b6558484dc041dfef43762ced9b55332212e2882 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 1 Nov 2024 07:39:12 +0000 Subject: [PATCH 135/231] refactor: added more chat tests --- .../library/abstractions/contracts.py | 4 + src/library/src/abstractions/contracts.py | 19 +-- src/library/src/abstractions/handler.py | 19 ++- src/library/src/extentions/encoding.py | 20 ++- .../src/extentions/string_extentions.py | 10 +- src/servers/chat/src/abstractions/contract.py | 12 +- src/servers/chat/src/abstractions/handler.py | 7 +- src/servers/chat/src/contracts/requests.py | 70 +++++----- src/servers/chat/tests/request_tests.py | 120 +++++++++++++++++- 9 files changed, 210 insertions(+), 71 deletions(-) diff --git a/src/backends/library/abstractions/contracts.py b/src/backends/library/abstractions/contracts.py index 4f60748b8..bbffab6fd 100644 --- a/src/backends/library/abstractions/contracts.py +++ b/src/backends/library/abstractions/contracts.py @@ -11,6 +11,10 @@ class RequestBase(BaseModel): """ if the raw_request is bytes, we decode it to decode("ascii","backslashreplace") str """ + client_ip_endpoint: str + """ + ip:port + """ class ErrorResponse(BaseModel): diff --git a/src/library/src/abstractions/contracts.py b/src/library/src/abstractions/contracts.py index 736593a56..7d75ea50c 100644 --- a/src/library/src/abstractions/contracts.py +++ b/src/library/src/abstractions/contracts.py @@ -1,8 +1,9 @@ import abc from copy import deepcopy +from datetime import datetime import enum from typing import Optional - +from uuid import UUID from pydantic import BaseModel @@ -32,20 +33,14 @@ def __init__(self, raw_request: object) -> None: def parse(self) -> None: pass - def to_json(self) -> dict: + def to_dict(self) -> dict: """ create a json serializable dict of this class """ - result = deepcopy(self.__dict__) - for key, value in result.items(): - if isinstance(value, bytes): - result[key] = value.decode("ascii", "backslashreplace") - elif isinstance(value, enum.Enum): - result[key] = value.value - elif isinstance(value, enum.IntEnum): - result[key] = value.value - else: - result[key] = str(value) + result = {} + for key, value in self.__dict__.items(): + if key[0] != "_": + result[key] = value return result diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index cfbd6b694..d34012769 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -1,3 +1,4 @@ +import json from library.src.abstractions.client import ClientBase from library.src.exceptions.general import UniSpyException from typing import Type @@ -7,6 +8,7 @@ # if TYPE_CHECKING: from library.src.abstractions.contracts import RequestBase, ResultBase, ResponseBase +from library.src.extentions.encoding import UniSpyJsonEncoder class CmdHandlerBase: @@ -69,9 +71,12 @@ def _data_operate(self) -> None: self._feach_data() def _prepare_data(self): - self._temp_data = self._request.to_json() - self._temp_data["server_id"] = str( - self._client.server_config.server_id) + self._temp_data = self._request.to_dict() + if "server_id" in self._temp_data: + raise UniSpyException("server_id name collision in dict") + self._temp_data["server_id"] = self._client.server_config.server_id + if "client_ip_endpoint" in self._temp_data: + raise UniSpyException("client_ip_endpoint name collision in dict") self._temp_data["client_ip_endpoint"] = self._client.connection.ip_endpoint def _upload_data(self): @@ -81,8 +86,10 @@ def _upload_data(self): """ url = f"{CONFIG.backend.url}/GameSpy/{ self._client.server_config.server_name}/{self.__class__.__name__}/" - - response = requests.post(url, json=self._temp_data) + json_str = json.dumps( + self._temp_data, cls=UniSpyJsonEncoder, ensure_ascii=False) + response = requests.post(url, data=json_str, headers={ + "Content-Type": "application/json"}) if response.status_code != 200: raise UniSpyException("Upload data to background failed.") self._http_result = response.json() @@ -98,8 +105,8 @@ def _feach_data(self): if self._result_cls is None: raise UniSpyException("_result should not be null when feach data") - assert issubclass(self._result_cls, ResultBase) + self._result = self._result_cls(**self._http_result) def _response_construct(self) -> None: diff --git a/src/library/src/extentions/encoding.py b/src/library/src/extentions/encoding.py index 2d4df5d10..88cdf31f2 100644 --- a/src/library/src/extentions/encoding.py +++ b/src/library/src/extentions/encoding.py @@ -1,3 +1,8 @@ +import enum +import json +from uuid import UUID + + def get_string(data: bytes) -> str: return data.decode("ascii") @@ -6,4 +11,17 @@ def get_bytes(data: str) -> bytes: return data.encode("ascii") - +class UniSpyJsonEncoder(json.JSONEncoder): + def default(self, obj): + # Handle bytes + if isinstance(obj, bytes): + return obj.decode("ascii", "backslashreplace") + # Handle enum and IntEnum + elif isinstance(obj, enum.Enum): + return obj.value + elif isinstance(obj, enum.IntEnum): + return obj.value + elif isinstance(obj, UUID): + return str(obj) + # Fallback to the default method for other types + return super().default(obj) diff --git a/src/library/src/extentions/string_extentions.py b/src/library/src/extentions/string_extentions.py index e81f72ce7..6d84bf2b5 100644 --- a/src/library/src/extentions/string_extentions.py +++ b/src/library/src/extentions/string_extentions.py @@ -36,7 +36,7 @@ def convert_kvstring_to_dictionary(kv_str: str): if len(key_value_list) < i + 2: dic[key_value_list[i]] = "" else: - dic[key_value_list[i]] = key_value_list[i + 1] + dic[key_value_list[i]] = key_value_list[i + 1].replace("\x00", "") return dic @@ -45,7 +45,8 @@ def convert_keystr_to_list(key_str: str): assert isinstance(key_str, str) data = key_str.split("\\") - data = [item for item in data if item] # Remove empty strings from the list + # Remove empty strings from the list + data = [item for item in data if item] return data @@ -61,7 +62,7 @@ def from_hex_string_to_bytes(hex_str: str): use in webservice auth public key convertion """ assert isinstance(hex_str, str) - data = [int(hex_str[i : i + 2], 16) for i in range(0, len(hex_str), 2)] + data = [int(hex_str[i: i + 2], 16) for i in range(0, len(hex_str), 2)] return bytes(data) @@ -94,7 +95,8 @@ def format_network_message( ): assert type in ["recv", "send"] if is_log_raw: - tempLog = f"{convert_printable_bytes_to_string(message)} [{convert_byte_to_hex_string(message)}]" + tempLog = f"{convert_printable_bytes_to_string( + message)} [{convert_byte_to_hex_string(message)}]" else: tempLog = convert_nonprintable_bytes_to_hex_string(message) diff --git a/src/servers/chat/src/abstractions/contract.py b/src/servers/chat/src/abstractions/contract.py index 203bb8bde..72e6c5a10 100644 --- a/src/servers/chat/src/abstractions/contract.py +++ b/src/servers/chat/src/abstractions/contract.py @@ -7,7 +7,7 @@ class RequestBase(library.src.abstractions.contracts.RequestBase): command_name: str _prefix: str _cmd_params: list - _longParam: str + _long_param: str def __init__(self, raw_request: str) -> None: assert isinstance(raw_request, str) @@ -23,25 +23,25 @@ def parse(self) -> None: if self.raw_request.count(":") > 2: raise Exception(f"IRC request is invalid {self.raw_request}") if ":" not in self.raw_request: - index_of_colon = 0 + index_of_colon = -1 else: index_of_colon = self.raw_request.index(":") raw = self.raw_request if index_of_colon == 0 and index_of_colon != -1: if " " not in raw: - prefixIndex = 0 + prefixIndex = -1 else: prefixIndex = raw.index(" ") self._prefix = raw[index_of_colon:prefixIndex] raw = raw[prefixIndex:] - if ":" not in self.raw_request: + if ":" not in raw: index_of_colon = 0 else: - index_of_colon = self.raw_request.index(":") + index_of_colon = raw.index(":") if index_of_colon != 0 and index_of_colon != -1: - self._longParam = raw[index_of_colon + 1:] + self._long_param = raw[index_of_colon + 1:] # reset the request string raw = raw[:index_of_colon] diff --git a/src/servers/chat/src/abstractions/handler.py b/src/servers/chat/src/abstractions/handler.py index 6524f4114..b403ad7a0 100644 --- a/src/servers/chat/src/abstractions/handler.py +++ b/src/servers/chat/src/abstractions/handler.py @@ -50,7 +50,7 @@ class ChannelRequestBase(RequestBase): def parse(self) -> None: super().parse() - if self._cmd_params is None or len(self._cmd_params) < 1: + if not hasattr(self, "_cmd_params") or len(self._cmd_params) < 1: raise ChatException("Channel name is missing.") self.channel_name = self._cmd_params[0] @@ -105,13 +105,16 @@ class MessageRequestBase(ChannelRequestBase): def parse(self): super().parse() + if not hasattr(self, "channel_name"): + raise NoSuchNickException( + "the channel name is missing from the request") if "#" in self.channel_name: self.type = MessageType.CHANNEL_MESSAGE else: self.type = MessageType.USER_MESSAGE self.nick_name = self._cmd_params[0] - self.message = self._longParam + self.message = self._long_param class MessageResultBase(ResultBase): diff --git a/src/servers/chat/src/contracts/requests.py b/src/servers/chat/src/contracts/requests.py index 8318699ba..210205e32 100644 --- a/src/servers/chat/src/contracts/requests.py +++ b/src/servers/chat/src/contracts/requests.py @@ -121,12 +121,12 @@ def parse(self): if self._cmd_params[1] == "*": self.request_type = LoginRequestType.NICK_AND_EMAIL_LOGIN self.password_hash = self._cmd_params[2] - if self._longParam.count("@") != 2: + if self._long_param.count("@") != 2: raise ChatException("The profile nick format is incorrect.") - profile_nick_index = self._longParam.index("@") - self.nick_name = self._longParam[0:profile_nick_index] - self.email = self._longParam[profile_nick_index + 1:] + profile_nick_index = self._long_param.index("@") + self.nick_name = self._long_param[0:profile_nick_index] + self.email = self._long_param[profile_nick_index + 1:] return self.request_type = LoginRequestType.UNIQUE_NICK_LOGIN @@ -142,8 +142,8 @@ def parse(self): super().parse() if len(self._cmd_params) == 1: self.nick_name = self._cmd_params[0] - elif self._longParam is None: - self.nick_name = self._longParam + elif self._long_param is None: + self.nick_name = self._long_param else: raise ChatException("NICK request is invalid.") @@ -166,9 +166,9 @@ class PongRequest(RequestBase): def parse(self): super().parse() - if self._longParam is None: + if self._long_param is None: raise ChatException("Echo message is missing.") - self.echo_message = self._longParam + self.echo_message = self._long_param class QuitRequest(RequestBase): @@ -176,10 +176,10 @@ class QuitRequest(RequestBase): def parse(self): super().parse() - if self._longParam is None: + if self._long_param is None: raise ChatException("Quit reason is missing.") - self.reason = self._longParam + self.reason = self._long_param class RegisterNickRequest(RequestBase): @@ -199,10 +199,10 @@ class SetKeyRequest(RequestBase): def parse(self): super().parse() - if self._longParam is None: + if self._long_param is None: raise ChatException("The keys and values are missing.") - self.key_values = convert_kvstring_to_dictionary(self._longParam) + self.key_values = convert_kvstring_to_dictionary(self._long_param) class UserIPRequest(RequestBase): @@ -226,7 +226,7 @@ def parse(self): self.host_name = self._cmd_params[0] self.server_name = self._cmd_params[1] - self.name = self._longParam + self.name = self._long_param class WhoIsRequest(RequestBase): @@ -277,7 +277,7 @@ def parse(self) -> None: "The number of IRC cmd params in GETKEY request is incorrect." ) - if self._longParam is None: + if self._long_param is None: raise ChatException( "The number of IRC cmd params in GETKEY request is incorrect." ) @@ -286,11 +286,11 @@ def parse(self) -> None: self.cookie = self._cmd_params[1] self.unknown_cmd_param = self._cmd_params[2] - self._longParam = self._longParam[: len(self._longParam)] + self._long_param = self._long_param[: len(self._long_param)] if self.nick_name == "*": self.is_get_all_user = True - self.keys = convert_keystr_to_list(self._longParam) + self.keys = convert_keystr_to_list(self._long_param) # region Channel @@ -306,14 +306,14 @@ def parse(self): if len(self._cmd_params) != 3: raise ChatException("The cmdParams number is invalid.") - if self._longParam is None or self._longParam[-1] != "\0": + if self._long_param is None or self._long_param[-1] != "\0": raise ChatException("The longParam number is invalid.") - self.cookie = self._cmd_params[0] - self.keys = self._longParam.strip("\\").rstrip("\0").split("\\") + self.cookie = self._cmd_params[1] + self.keys = self._long_param.strip("\\").rstrip("\0").split("\\") class GetCKeyRequest(ChannelRequestBase): - nick_name: str + nick_name: Optional[str] cookie: str keys: list request_type: GetKeyRequestType @@ -322,7 +322,7 @@ def parse(self): super().parse() if len(self._cmd_params) != 4: raise ChatException("The number of IRC parameters are incorrect.") - if self._longParam is None: + if self._long_param is None: raise ChatException("The IRC long parameter is incorrect.") self.nick_name = self._cmd_params[1] @@ -333,11 +333,11 @@ def parse(self): self.cookie = self._cmd_params[2] - if "\0" not in self._longParam and "\\" not in self._longParam: + if "\0" not in self._long_param and "\\" not in self._long_param: raise ChatException("The key provide is incorrect.") - self.keys = self._longParam.strip("\\").rstrip("\0").split("\\") + self.keys = self._long_param.strip("\\").rstrip("\0").split("\\") class GetUdpRelayRequest(ChannelRequestBase): @@ -366,10 +366,10 @@ def parse(self): raise ChatException("The number of IRC parameters are incorrect.") self.kickee_nick_name = self._cmd_params[1] - if self._longParam is None: + if self._long_param is None: raise ChatException("The IRC long parameters is missing.") - self.reason = self._longParam + self.reason = self._long_param class ModeRequest(ChannelRequestBase): @@ -390,7 +390,7 @@ class ModeRequest(ChannelRequestBase): # "MODE " # "MODE " request_type: ModeRequestType - mode_operations: list + mode_operations: list[ModeOperationType] nick_name: str user_name: str limit_number: int @@ -568,9 +568,9 @@ def __init__(self, raw_request: Optional[str] = None) -> None: def parse(self): super().parse() - if self._longParam is None: + if self._long_param is None: raise ChatException("The reason of living channel is missing.") - self.reason = self._longParam + self.reason = self._long_param class SetChannelKeyRequest(ChannelRequestBase): @@ -579,10 +579,10 @@ class SetChannelKeyRequest(ChannelRequestBase): def parse(self): super().parse() - if self._longParam is None: + if self._long_param is None: raise ChatException("Channel keys and values are missing.") - self._longParam = self._longParam[1:] - self.key_value_string = self._longParam + self._long_param = self._long_param[1:] + self.key_value_string = self._long_param self.key_values = convert_kvstring_to_dictionary(self.key_value_string) @@ -599,13 +599,13 @@ def parse(self) -> None: raise ChatException( "The cmdParams from SETCKEY request are missing.") - if self._longParam is None: + if self._long_param is None: raise ChatException( "The longParam from SETCKEY request is missing.") self.channel_name = self._cmd_params[0] self.nick_name = self._cmd_params[1] - self.key_value_string = self._longParam[1:] + self.key_value_string = self._long_param[1:] self.key_values = convert_kvstring_to_dictionary(self.key_value_string) if "b_" in self.key_values: @@ -631,11 +631,11 @@ class TopicRequest(ChannelRequestBase): def parse(self) -> None: super().parse() - if self._longParam is None: + if not hasattr(self, "_long_param"): self.request_type = TopicRequestType.GET_CHANNEL_TOPIC else: self.request_type = TopicRequestType.SET_CHANNEL_TOPIC - self.channel_topic = self._longParam + self.channel_topic = self._long_param # region Message diff --git a/src/servers/chat/tests/request_tests.py b/src/servers/chat/tests/request_tests.py index 680423f93..234fb719c 100644 --- a/src/servers/chat/tests/request_tests.py +++ b/src/servers/chat/tests/request_tests.py @@ -1,4 +1,7 @@ import unittest + +from servers.chat.src.aggregates.enums import MessageType +from servers.chat.src.contracts.requests import * # region General CD_KEY = "CDKEY XXXX-XXXX-XXXX-XXXX\r\n" CRYPT = "CRYPT des 1 gmtest\r\n" @@ -48,19 +51,126 @@ def test_get_chann_key(self): class ChannelRequestTests(unittest.TestCase): - def test_get_chann_key(self): - pass + def test_get_channel_key(self): + request = GetChannelKeyRequest(GET_CHANNEL_KEY) + request.parse() + self.assertEqual(request.channel_name, "#GSP!room!test") + self.assertEqual(request.cookie, "0000") + self.assertEqual(request.keys[0], "username") + self.assertEqual(request.keys[1], "nickname") + + def test_get_ckey_channel_specific_user(self): + request = GetCKeyRequest(GET_CKEY_CHANNEL_SPECIFIC_USER) + request.parse() + self.assertEqual(request.request_type, + GetKeyRequestType.GET_CHANNEL_SPECIFIC_USER_KEY_VALUE) + self.assertEqual(request.channel_name, "#GSP!room!test") + self.assertEqual(request.nick_name, "spyguy") + self.assertEqual(request.cookie, "0000") + self.assertEqual(request.keys[0], "username") + self.assertEqual(request.keys[1], "nickname") + + def test_get_ckey_channel_all_user(self): + request = GetCKeyRequest(GET_CKEY_CHANNEL_ALL_USER) + request.parse() + self.assertEqual(request.request_type, + GetKeyRequestType.GET_CHANNEL_ALL_USER_KEY_VALUE) + self.assertEqual(request.channel_name, "#GSP!room!test") + self.assertEqual(request.cookie, "0000") + self.assertEqual(request.keys[0], "username") + self.assertEqual(request.keys[1], "nickname") + + def test_join(self): + request = JoinRequest(JOIN) + request.parse() + self.assertEqual(request.channel_name, "#GSP!room!test") + + request = JoinRequest(JOIN_WITH_PASS) + request.parse() + self.assertEqual(request.channel_name, "#GSP!room!test") + self.assertEqual(request.password, "pass123") + + def test_kick(self): + request = KickRequest(KICK) + request.parse() + self.assertEqual(request.kickee_nick_name, "spyguy") + self.assertEqual(request.reason, "Spam") + + def test_mode(self): + request = ModeRequest(MODE_CHANNEL) + request.parse() + self.assertEqual( + request.mode_operations[0], ModeOperationType.REMOVE_CHANNEL_PASSWORD) + self.assertEqual(request.channel_name, "#GSP!room!test") + self.assertEqual(request.mode_flag, "+l") + self.assertEqual(request.limit_number, 2) + + request = ModeRequest("MODE #GSP!gmtest!MlNK4q4l1M -i-p-s+m-n+t+l+e 2") + request.parse() + + def test_part(self): + request = PartRequest(PART) + request.parse() + self.assertEqual(request.reason, "test") + + def test_set_channel_key(self): + request = SetChannelKeyRequest(SET_CHANNEL_KEY) + request.parse() + # Add assertions as needed + + def test_set_ckey(self): + request = SetCKeyRequest(SET_CKEY) + request.parse() + self.assertEqual(request.channel_name, "#GSP!room!test") + self.assertEqual(request.nick_name, "spyguy") + self.assertEqual(request.key_values, {"b_flags": "sh"}) + + def test_topic_get_channel_topic(self): + request = TopicRequest(TOPIC_GET_CHANNEL_TOPIC) + request.parse() + self.assertEqual(request.channel_name, "#GSP!room!test") + + def test_topic_set_channel_topic(self): + request = TopicRequest(TOPIC_SET_CHANNEL_TOPIC) + request.parse() + self.assertEqual(request.channel_name, "#GSP!room!test") + self.assertEqual(request.channel_topic, "This is a topic message.") # region Message ABOVE_THE_TABLE_MSG = "ATM #GSP!room!test :hello this is a test.\r\n" -NOTICE = "NOTICE #GSP!room!test :hello this is a test.\r\n" +NOTICE_MSG = "NOTICE #GSP!room!test :hello this is a test.\r\n" PRIVATE_MSG = "PRIVMSG #GSP!room!test :hello this is a test.\r\n" UNDER_THE_TABLE_MSG = "UTM #GSP!room!test :hello this is a test.\r\n" ACTION_MSG = "PRIVMSG #GSP!room!test :\001ACTION hello this is a test.\001\r\n" class MessageRequestTests(unittest.TestCase): - def test_get_chann_key(self): - pass + def test_atm(self): + request = ATMRequest(ABOVE_THE_TABLE_MSG) + request.parse() + self.assertEqual(MessageType.CHANNEL_MESSAGE, request.type) + self.assertEqual(False, hasattr(request, "nick_name")) + self.assertEqual("#GSP!room!test", request.channel_name) + + def test_notice(self): + request = NoticeRequest(NOTICE_MSG) + request.parse() + self.assertEqual(MessageType.CHANNEL_MESSAGE, request.type) + self.assertEqual(False, hasattr(request, "nick_name")) + self.assertEqual("#GSP!room!test", request.channel_name) + + def test_private(self): + request = PrivateRequest(PRIVATE_MSG) + request.parse() + self.assertEqual(MessageType.CHANNEL_MESSAGE, request.type) + self.assertEqual(False, hasattr(request, "nick_name")) + self.assertEqual("#GSP!room!test", request.channel_name) + + def test_utm(self): + request = UTMRequest(UNDER_THE_TABLE_MSG) + request.parse() + self.assertEqual(MessageType.CHANNEL_MESSAGE, request.type) + self.assertEqual(False, hasattr(request, "nick_name")) + self.assertEqual("#GSP!room!test", request.channel_name) From b0847e174b71d35cb4f4152a4374ca0d905cd50a Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sun, 3 Nov 2024 00:46:07 +0000 Subject: [PATCH 136/231] refactor: README, added how to run section --- .github/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/README.md b/.github/README.md index ad1e790e9..886ef1f04 100644 --- a/.github/README.md +++ b/.github/README.md @@ -28,3 +28,8 @@ This project is licensed under the [GNU Affero General Public License v3.0](../L ## Why rewrite C# to python * The vscode extensions for C# development is become more and more hard to use, and microsoft abandoned the open-source OmniSharp project and replacing it with it's own closed source language server. * The c# project seems hard to run by users, it require a lot of deploy knowledge and hard for collaborations, for the future of the gamespy emulator, I choose to rewrite this into a opensource and easy high level language - python. + + +## How to run +* Run ```docker compose up -f docker-compose-unispy-env.yml -d``` +* Use vscode to open the src folder and reopen in devcontainer (make sure your vscode have devcontainer extension) \ No newline at end of file From e80877cd6f4b18d1681151feb7bd5a482a8315bc Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 2 Nov 2024 13:05:07 +0000 Subject: [PATCH 137/231] refactor: init class member with none in __init__ --- src/.vscode/settings.json | 2 +- src/library/src/abstractions/handler.py | 23 ++++++++----------- src/servers/chat/src/abstractions/contract.py | 9 +++++--- src/servers/chat/src/abstractions/handler.py | 15 ++++++++---- src/servers/chat/src/applications/handlers.py | 3 +++ src/servers/chat/src/contracts/requests.py | 2 +- src/servers/chat/tests/room_tests.py | 4 ++++ .../game_status/src/applications/handlers.py | 10 ++++---- .../src/applications/handlers.py | 18 +++++++++------ .../src/applications/handlers.py | 18 +++++++-------- .../src/contracts/requests.py | 3 --- 11 files changed, 60 insertions(+), 47 deletions(-) diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index cbe3eecc2..459e8e9f1 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -1,5 +1,5 @@ { - "python.analysis.typeCheckingMode": "basic", + "python.analysis.typeCheckingMode": "off", "workbench.iconTheme": "material-icon-theme", "python.testing.unittestArgs": [ "-v", diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index d34012769..70ff27d6f 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -1,7 +1,7 @@ import json from library.src.abstractions.client import ClientBase from library.src.exceptions.general import UniSpyException -from typing import Type +from typing import Optional, Type import requests from library.src.configs import CONFIG @@ -14,20 +14,16 @@ class CmdHandlerBase: _client: "ClientBase" _request: "RequestBase" - _result: "ResultBase" - _response: "ResponseBase" + _result: Optional["ResultBase"] + _response: Optional["ResponseBase"] """ the response instance, initialize as None in __init__ """ - _result_cls: "Type[ResultBase]" + _result_cls: Optional["Type[ResultBase]"] """ the result type class, use to deserialize json data from backend\n - the initialization of _result_cls must before call super().__init__() + the initialization of _result_cls must after call super().__init__() """ - _is_uploading: bool - - _is_feaching: bool - _debug: bool = False """ whether is in debug mode, if in debug mode exception will raise from handler @@ -39,6 +35,9 @@ def __init__(self, client: "ClientBase", request: "RequestBase") -> None: assert issubclass(type(request), RequestBase) self._client = client self._request = request + self._response = None + self._result_cls = None + self._result = None def handle(self) -> None: try: @@ -48,7 +47,7 @@ def handle(self) -> None: self._request_check() self._data_operate() self._response_construct() - if not hasattr(self, "_response"): + if self._response is None: return self._response_send() except Exception as ex: @@ -99,10 +98,6 @@ def _feach_data(self): whether need get data from backend. if child class do not require feach, overide this function to do nothing """ - if not hasattr(self, "_result_cls"): - raise UniSpyException( - "_result should be initialized when feach data") - if self._result_cls is None: raise UniSpyException("_result should not be null when feach data") assert issubclass(self._result_cls, ResultBase) diff --git a/src/servers/chat/src/abstractions/contract.py b/src/servers/chat/src/abstractions/contract.py index 72e6c5a10..b06511c0d 100644 --- a/src/servers/chat/src/abstractions/contract.py +++ b/src/servers/chat/src/abstractions/contract.py @@ -5,13 +5,16 @@ class RequestBase(library.src.abstractions.contracts.RequestBase): raw_request: str command_name: str - _prefix: str - _cmd_params: list - _long_param: str + _prefix: Optional[str] + _cmd_params: Optional[list[str]] + _long_param: Optional[str] def __init__(self, raw_request: str) -> None: assert isinstance(raw_request, str) super().__init__(raw_request) + self._prefix = None + self._cmd_params = None + self._long_param = None def parse(self) -> None: # at most 2 colon character diff --git a/src/servers/chat/src/abstractions/handler.py b/src/servers/chat/src/abstractions/handler.py index b403ad7a0..b16d3c23b 100644 --- a/src/servers/chat/src/abstractions/handler.py +++ b/src/servers/chat/src/abstractions/handler.py @@ -46,11 +46,15 @@ class PostLoginHandlerBase(CmdHandlerBase): class ChannelRequestBase(RequestBase): - channel_name: str + channel_name: Optional[str] + + def __init__(self, raw_request: str) -> None: + super().__init__(raw_request) + self.channel_name = None def parse(self) -> None: super().parse() - if not hasattr(self, "_cmd_params") or len(self._cmd_params) < 1: + if self._cmd_params is None or len(self._cmd_params) < 1: raise ChatException("Channel name is missing.") self.channel_name = self._cmd_params[0] @@ -105,15 +109,18 @@ class MessageRequestBase(ChannelRequestBase): def parse(self): super().parse() - if not hasattr(self, "channel_name"): + if self.channel_name is None: raise NoSuchNickException( "the channel name is missing from the request") if "#" in self.channel_name: self.type = MessageType.CHANNEL_MESSAGE else: + if self._cmd_params is None or len(self._cmd_params) < 1: + raise ChatException("cmd params is invalid") self.type = MessageType.USER_MESSAGE self.nick_name = self._cmd_params[0] - + if self._long_param is None: + raise ChatException("long param is invalid") self.message = self._long_param diff --git a/src/servers/chat/src/applications/handlers.py b/src/servers/chat/src/applications/handlers.py index 98955e658..b376cd451 100644 --- a/src/servers/chat/src/applications/handlers.py +++ b/src/servers/chat/src/applications/handlers.py @@ -111,6 +111,9 @@ def __init__(self, client: ClientBase, request: RequestBase): assert isinstance(request, CryptRequest) super().__init__(client, request) + def _data_operate(self): + pass + def _response_construct(self) -> None: self._response = CryptResponse() diff --git a/src/servers/chat/src/contracts/requests.py b/src/servers/chat/src/contracts/requests.py index 210205e32..220dd97fe 100644 --- a/src/servers/chat/src/contracts/requests.py +++ b/src/servers/chat/src/contracts/requests.py @@ -631,7 +631,7 @@ class TopicRequest(ChannelRequestBase): def parse(self) -> None: super().parse() - if not hasattr(self, "_long_param"): + if self._long_param is None: self.request_type = TopicRequestType.GET_CHANNEL_TOPIC else: self.request_type = TopicRequestType.SET_CHANNEL_TOPIC diff --git a/src/servers/chat/tests/room_tests.py b/src/servers/chat/tests/room_tests.py index 42f080ce8..6ec2d1a65 100644 --- a/src/servers/chat/tests/room_tests.py +++ b/src/servers/chat/tests/room_tests.py @@ -1,3 +1,4 @@ +from typing import Optional import unittest from library.tests.mock_objects import BrokerMock @@ -16,3 +17,6 @@ def test_peer_room(self): user = ChannelUser(client, channel) channel.add_bind_on_user_and_channel(user) pass + + def test_single_join(self, user_name="unispy", nick_name="unispy", channel_name="#GSP!room!test"): + pass diff --git a/src/servers/game_status/src/applications/handlers.py b/src/servers/game_status/src/applications/handlers.py index ea387a20b..67e7e9f6e 100644 --- a/src/servers/game_status/src/applications/handlers.py +++ b/src/servers/game_status/src/applications/handlers.py @@ -10,9 +10,9 @@ class AuthGameHandler(CmdHandlerBase): _result: AuthGameResult def __init__(self, client: Client, request: AuthGameRequest) -> None: - self._result_cls = AuthGameResult - super().__init__(client, request) assert isinstance(request, AuthGameRequest) + super().__init__(client, request) + self._result_cls = AuthGameResult def _response_construct(self) -> None: self._client.info.session_key = self._result.session_key @@ -26,8 +26,8 @@ class AuthPlayerHandler(CmdHandlerBase): def __init__(self, client: Client, request: AuthPlayerRequest) -> None: assert isinstance(request, AuthPlayerRequest) - self._result_cls = AuthPlayerResult super().__init__(client, request) + self._result_cls = AuthPlayerResult def _response_construct(self) -> None: self._response = AuthPlayerResponse(self._request, self._result) @@ -37,9 +37,9 @@ class GetPlayerDataHandler(CmdHandlerBase): _result_cls: type[GetPlayerDataResult] def __init__(self, client: Client, request: GetPlayerDataRequest) -> None: - self._result_cls = GetPlayerDataResult - super().__init__(client, request) assert isinstance(request, GetPlayerDataRequest) + super().__init__(client, request) + self._result_cls = GetPlayerDataResult def _response_construct(self) -> None: self._response = GetPlayerDataResponse(self._request, self._result) diff --git a/src/servers/presence_connection_manager/src/applications/handlers.py b/src/servers/presence_connection_manager/src/applications/handlers.py index 6af45f25e..24809efb1 100644 --- a/src/servers/presence_connection_manager/src/applications/handlers.py +++ b/src/servers/presence_connection_manager/src/applications/handlers.py @@ -74,8 +74,8 @@ class LoginHandler(CmdHandlerBase): def __init__(self, client: "Client", request: LoginRequest) -> None: assert isinstance(request, LoginRequest) - self._result_cls = LoginResult super().__init__(client, request) + self._result_cls = LoginResult def _response_construct(self) -> None: self._response = LoginResponse(self._request, self._result) @@ -96,9 +96,10 @@ class NewUserHandler(CmdHandlerBase): _result: NewUserResult # todo create seperate request and result - def __init__(self, client: Client, request: RequestBase) -> None: - self._result_cls = NewUserResult + def __init__(self, client: Client, request: NewUserRequest) -> None: + assert isinstance(request, NewUserRequest) super().__init__(client, request) + self._result_cls = NewUserResult def _response_construct(self): self._response = NewUserResponse(self._request, self._result) @@ -111,6 +112,9 @@ def __init__(self, client: "Client", request: LoginRequest) -> None: assert isinstance(request, LoginRequest) super().__init__(client, request) + def _data_operate(self): + pass + def _response_construct(self) -> None: self._client.info.sdk_revision = SdkRevision( self._request.sdk_revision_type) @@ -139,11 +143,11 @@ def _response_construct(self) -> None: class BuddyListHandler(LoginedHandlerBase): _result: BuddyListResult - _result_cls = BuddyListResult + _result_cls: type[BuddyListResult] - def __init__(self, client: Client): - assert isinstance(client, Client) - self._client = client + def __init__(self, client, request): + super().__init__(client, request) + def response_construct(self): self._response = BuddyListResponse(self._request, self._result) diff --git a/src/servers/presence_search_player/src/applications/handlers.py b/src/servers/presence_search_player/src/applications/handlers.py index 06dd88700..74d27c29f 100644 --- a/src/servers/presence_search_player/src/applications/handlers.py +++ b/src/servers/presence_search_player/src/applications/handlers.py @@ -15,8 +15,8 @@ class CheckHandler(CmdHandlerBase): def __init__(self, client: Client, request: CheckRequest) -> None: assert isinstance(request, CheckRequest) - self._result_cls = CheckResult super().__init__(client, request) + self._result_cls = CheckResult def _response_construct(self): self._response = CheckResponse(self._request, self._result) @@ -29,8 +29,8 @@ class NewUserHandler(CmdHandlerBase): def __init__(self, client: Client, request: NewUserRequest) -> None: assert isinstance(request, NewUserRequest) - self._result_cls = NewUserResult super().__init__(client, request) + self._result_cls = NewUserResult def _response_construct(self): self._response = NewUserResponse(self._request, self._result) @@ -43,8 +43,8 @@ class NicksHandler(CmdHandlerBase): def __init__(self, client: Client, request: NicksRequest) -> None: assert isinstance(request, NicksRequest) - self._result_cls = NicksResult super().__init__(client, request) + self._result_cls = NicksResult def _response_construct(self): self._response = NicksResponse(self._request, self._result) @@ -57,8 +57,8 @@ class OthersHandler(CmdHandlerBase): def __init__(self, client: Client, request: OthersRequest) -> None: assert isinstance(request, OthersRequest) - self._result_cls = OthersResult super().__init__(client, request) + self._result_cls = OthersResult def _response_construct(self): self._response = OthersResponse(self._result) @@ -70,8 +70,8 @@ class OthersListHandler(CmdHandlerBase): def __init__(self, client: Client, request: OthersListRequest) -> None: assert isinstance(request, OthersListRequest) - self._result_cls = OthersListResult super().__init__(client, request) + self._result_cls = OthersListResult def _response_construct(self): self._response = OthersListResponse(self._result) @@ -94,8 +94,8 @@ class SearchHandler(CmdHandlerBase): def __init__(self, client: Client, request: SearchRequest) -> None: assert isinstance(request, SearchRequest) - self._result_cls = SearchResult super().__init__(client, request) + self._result_cls = SearchResult def _response_construct(self): self._response = SearchResponse(self._result) @@ -107,8 +107,8 @@ class SearchUniqueHandler(CmdHandlerBase): def __init__(self, client: Client, request: SearchUniqueRequest) -> None: assert isinstance(request, SearchUniqueRequest) - self._result_cls = SearchUniqueResult super().__init__(client, request) + self._result_cls = SearchUniqueResult def _response_construct(self): self._response = SearchUniqueResponse(self._result) @@ -121,8 +121,8 @@ class UniqueSearchHandler(CmdHandlerBase): def __init__(self, client: Client, request: UniqueSearchRequest) -> None: assert isinstance(request, UniqueSearchRequest) - self._result_cls = UniqueSearchResult super().__init__(client, request) + self._result_cls = UniqueSearchResult def _response_construct(self): self._response = UniqueSearchResponse(self._request, self._result) @@ -135,8 +135,8 @@ class ValidHandler(CmdHandlerBase): def __init__(self, client: Client, request: ValidRequest) -> None: assert isinstance(request, ValidRequest) - self._result_cls = ValidResult super().__init__(client, request) + self._result_cls = ValidResult def _response_construct(self): self._response = ValidResponse(self._request, self._result) diff --git a/src/servers/presence_search_player/src/contracts/requests.py b/src/servers/presence_search_player/src/contracts/requests.py index 2d0371bf6..04bef48e7 100644 --- a/src/servers/presence_search_player/src/contracts/requests.py +++ b/src/servers/presence_search_player/src/contracts/requests.py @@ -14,9 +14,6 @@ class CheckRequest(RequestBase): email: str partner_id: int - def __init__(self, raw_request: str) -> None: - super().__init__(raw_request) - def parse(self): super().parse() self.password = process_password(self.request_dict) From 4257ba474eb2aebbb469b37e3c45d436f59b6594 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 4 Nov 2024 09:14:57 +0000 Subject: [PATCH 138/231] refactor: change chat broadcast logic --- .gitignore | 5 +- src/.vscode/settings.json | 2 +- src/library/src/abstractions/handler.py | 11 +- .../src/abstractions/server_launcher.py | 31 +- src/library/src/network/brockers.py | 2 +- src/servers/chat/src/abstractions/contract.py | 4 +- src/servers/chat/src/abstractions/handler.py | 62 ++- src/servers/chat/src/applications/client.py | 16 +- src/servers/chat/src/applications/handlers.py | 111 ++--- .../chat/src/applications/server_launcher.py | 5 +- src/servers/chat/src/contracts/requests.py | 18 +- src/servers/chat/src/contracts/responses.py | 11 +- src/servers/chat/tests/game_tests.py | 3 +- src/servers/chat/tests/mock_objects.py | 19 +- .../game_status/src/applications/handlers.py | 13 +- .../src/applications/server_launcher.py | 6 +- .../natneg/src/applications/handlers.py | 30 +- .../src/applications/server_launcher.py | 5 +- src/servers/natneg/tests/handler_tests.py | 2 +- src/servers/natneg/tests/mock_objects.py | 7 +- .../src/applications/handlers.py | 6 +- .../src/applications/server_launcher.py | 7 +- .../src/applications/handlers.py | 1 + .../src/applications/server_launcher.py | 5 +- .../tests/game_tests.py | 3 + .../src/applications/server_launcher.py | 5 +- .../src/v2/applications/handlers.py | 8 +- .../src/v2/applications/server_launcher.py | 5 +- .../src/applications/server_launcher.py | 5 +- src/unispy | 413 ------------------ 30 files changed, 235 insertions(+), 586 deletions(-) delete mode 100644 src/unispy diff --git a/.gitignore b/.gitignore index e390fdba9..8b0e0c52e 100644 --- a/.gitignore +++ b/.gitignore @@ -164,4 +164,7 @@ cython_debug/ *.pyc *.txt -!requirements.txt \ No newline at end of file +!requirements.txt + + +src/unispy \ No newline at end of file diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index 459e8e9f1..cbe3eecc2 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -1,5 +1,5 @@ { - "python.analysis.typeCheckingMode": "off", + "python.analysis.typeCheckingMode": "basic", "workbench.iconTheme": "material-icon-theme", "python.testing.unittestArgs": [ "-v", diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index 70ff27d6f..851889bbf 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -28,6 +28,8 @@ class CmdHandlerBase: """ whether is in debug mode, if in debug mode exception will raise from handler """ + _is_uploading: bool + _is_fetching: bool def __init__(self, client: "ClientBase", request: "RequestBase") -> None: @@ -38,6 +40,8 @@ def __init__(self, client: "ClientBase", request: "RequestBase") -> None: self._response = None self._result_cls = None self._result = None + self._is_uploading = True + self._is_fetching = True def handle(self) -> None: try: @@ -66,8 +70,10 @@ def _data_operate(self) -> None: virtual function, can be override """ self._prepare_data() - self._upload_data() - self._feach_data() + if self._is_uploading: + self._upload_data() + if self._is_fetching: + self._feach_data() def _prepare_data(self): self._temp_data = self._request.to_dict() @@ -113,6 +119,7 @@ def _response_send(self) -> None: virtual function, can be override Send response back to client, this is a virtual function which can be override only by child class """ + assert isinstance(self._response, ResponseBase) self._client.send(self._response) def _handle_exception(self, ex: Exception) -> None: diff --git a/src/library/src/abstractions/server_launcher.py b/src/library/src/abstractions/server_launcher.py index 932e76a62..68f9433df 100644 --- a/src/library/src/abstractions/server_launcher.py +++ b/src/library/src/abstractions/server_launcher.py @@ -1,4 +1,6 @@ +import signal from types import MappingProxyType +from typing import Optional from library.src.abstractions.connections import NetworkServerBase from library.src.exceptions.general import UniSpyException from library.src.log.log_manager import LogManager, LogWriter @@ -6,6 +8,8 @@ import pyfiglet import requests from prettytable import PrettyTable +import keyboard + VERSION = 0.45 _SERVER_FULL_SHORT_NAME_MAPPING = MappingProxyType({ @@ -24,15 +28,22 @@ class ServerLauncherBase: - config: ServerConfig - logger: LogWriter - server: NetworkServerBase + config: Optional[ServerConfig] + logger: Optional[LogWriter] + server: Optional[NetworkServerBase] + + def __init__(self): + self.server = None + self.logger = None + self.config = None def start(self): self.__show_unispy_logo() self._connect_to_backend() self._create_logger() self._launch_server() + print("Server successfully launched.") + self.__keep_running() def __show_unispy_logo(self): # display logo @@ -42,16 +53,24 @@ def __show_unispy_logo(self): table = PrettyTable() table.field_names = ["Server Name", "Listening Address", "Listening Port"] + assert self.config is not None table.add_row([self.config.server_name, self.config.public_address, self.config.listening_port]) print(table) def _launch_server(self) -> None: - raise NotImplementedError("Override this function in child class") + if self.server is None: + raise UniSpyException("Create network server in child class") + import threading + th = threading.Thread(target=self.server.start) + key = input('Press Q to Quit') + th.join() + def _connect_to_backend(self): try: # post our server config to backends to register + assert self.config is not None resp = requests.post( url=CONFIG.backend.url+"/", data=self.config.model_dump_json()) @@ -67,5 +86,9 @@ def _connect_to_backend(self): # fmt: on def _create_logger(self): + assert self.config is not None short_name = _SERVER_FULL_SHORT_NAME_MAPPING[self.config.server_name] self.logger = LogManager.create(short_name) + + def __keep_running(self): + key = input('Press Q to Quit') diff --git a/src/library/src/network/brockers.py b/src/library/src/network/brockers.py index ad308bd46..cfbd82436 100644 --- a/src/library/src/network/brockers.py +++ b/src/library/src/network/brockers.py @@ -70,7 +70,7 @@ def subscribe(self): def unsubscribe(self): self._subscriber.close() - def publish_message(self, message): + def publish_message(self, message: str): if self._publisher is None: raise ValueError("websocket connection is not established") self._publisher.send(message) diff --git a/src/servers/chat/src/abstractions/contract.py b/src/servers/chat/src/abstractions/contract.py index b06511c0d..a1f44a8e9 100644 --- a/src/servers/chat/src/abstractions/contract.py +++ b/src/servers/chat/src/abstractions/contract.py @@ -6,14 +6,14 @@ class RequestBase(library.src.abstractions.contracts.RequestBase): raw_request: str command_name: str _prefix: Optional[str] - _cmd_params: Optional[list[str]] + _cmd_params: list[str] _long_param: Optional[str] def __init__(self, raw_request: str) -> None: assert isinstance(raw_request, str) super().__init__(raw_request) self._prefix = None - self._cmd_params = None + self._cmd_params = [] self._long_param = None def parse(self) -> None: diff --git a/src/servers/chat/src/abstractions/handler.py b/src/servers/chat/src/abstractions/handler.py index b16d3c23b..f19ad2c94 100644 --- a/src/servers/chat/src/abstractions/handler.py +++ b/src/servers/chat/src/abstractions/handler.py @@ -1,3 +1,6 @@ +from library.src.configs import CONFIG +from library.src.extentions.encoding import UniSpyJsonEncoder +from library.src.network.brockers import WebsocketBrocker from servers.chat.src.aggregates.enums import MessageType from servers.chat.src.abstractions.contract import ResultBase from typing import TYPE_CHECKING @@ -12,6 +15,8 @@ import library.src.abstractions.handler from typing import cast +from servers.chat.src.applications.server_launcher import ServerLauncher + if TYPE_CHECKING: from servers.chat.src.aggregates.channel import Channel from servers.chat.src.aggregates.channel_user import ChannelUser @@ -68,36 +73,49 @@ def __init__(self, request: RequestBase, result: ResultBase) -> None: assert isinstance(result, ResultBase) +def handle_brocker_message(message: str): + raise NotImplementedError() + + class ChannelHandlerBase(PostLoginHandlerBase): - _channel: "Channel" - _user: "ChannelUser" _request: ChannelRequestBase _response: ResponseBase + _result: ResultBase + _b_msg: dict + """ + broadcast message + """ + _brocker: WebsocketBrocker = WebsocketBrocker( + "channel", CONFIG.backend.url, handle_brocker_message) def __init__(self, client: ClientBase, request: RequestBase): super().__init__(client, request) # self._channel = None - def _request_check(self) -> None: - if self._request.raw_request is None: - return super()._request_check() - - if self._channel is None: - channel = ChannelManager.get_channel( - self._request.channel_name - ) - if channel is None: - raise NoSuchChannelException( - f"No such channel {self._request.channel_name}", - ) - self._channel = channel - if self._user is None: - user = self._channel.get_user_by_nick( - self._client.info.nick_name) - - if user is None: - raise NoSuchNickException(f"Can not find user with nickname: {self._client.info.nick_name} user_name: {self._client.info.user_name}") # noqa - self._user = user + def _message_construct(self): + """ + broadcast message construct + """ + self._b_msg = { + "server_id": self._client.server_config.server_id, + "sender_ip_end_point": self._client.connection.ip_endpoint, + "message": self._response.sending_buffer, + "channel_name": self._request.channel_name, + } + + def _publish_to_brocker(self): + """ + send message to backend, let backend to broadcast for us + """ + import json + self._message_construct() + j_str = json.dumps(self._b_msg, cls=UniSpyJsonEncoder) + ChannelHandlerBase._brocker.publish_message(j_str) + + def _response_send(self) -> None: + super()._response_send() + self._publish_to_brocker() + # region Message diff --git a/src/servers/chat/src/applications/client.py b/src/servers/chat/src/applications/client.py index 0598fc647..f6126a3a8 100644 --- a/src/servers/chat/src/applications/client.py +++ b/src/servers/chat/src/applications/client.py @@ -5,21 +5,25 @@ from library.src.network.tcp_handler import TcpConnection from library.src.configs import ServerConfig -from typing import TYPE_CHECKING +from typing import TYPE_CHECKING, Optional if TYPE_CHECKING: from servers.chat.src.aggregates.channel import Channel class ClientInfo: - previously_joined_channel: str + previously_joined_channel: Optional[str] joined_channels: dict[str, "Channel"] - nick_name: str - gamename: str - user_name: str + nick_name: Optional[str] + gamename: Optional[str] + user_name: Optional[str] def __init__(self) -> None: self.joined_channels = {} - + self.nick_name = None + self.gamename = None + self.user_name = None + self.previously_joined_channel = None + class Client(ClientBase): info: ClientInfo diff --git a/src/servers/chat/src/applications/handlers.py b/src/servers/chat/src/applications/handlers.py index b376cd451..0c9b02d6a 100644 --- a/src/servers/chat/src/applications/handlers.py +++ b/src/servers/chat/src/applications/handlers.py @@ -98,9 +98,10 @@ class CdKeyHandler(CmdHandlerBase): def __init__(self, client: ClientBase, request: CdkeyRequest): assert isinstance(request, CdkeyRequest) super().__init__(client, request) + self._is_fetching = False def _response_construct(self) -> None: - self._response = CdKeyResponse(self._request, self._result) + self._response = CdKeyResponse() class CryptHandler(CmdHandlerBase): @@ -217,6 +218,7 @@ class UserHandler(CmdHandlerBase): def __init__(self, client: ClientBase, request: UserRequest): assert isinstance(request, UserRequest) super().__init__(client, request) + self._is_fetching = False class UserIPHandler(CmdHandlerBase): @@ -285,12 +287,6 @@ def __init__(self, client: ClientBase, request: GetCKeyRequest): assert isinstance(request, GetCKeyRequest) super().__init__(client, request) - def _publish_message(self): - pass - - def _update_channel_cache(self): - pass - def _response_construct(self): self._response = GetCKeyResponse(self._request, self._result) @@ -303,39 +299,41 @@ def __init__(self, client: ClientBase, request: JoinRequest): assert isinstance(request, JoinRequest) super().__init__(client, request) - def _check_user_in_remote(self): - """ - todo maybe do not need because there are nick handler? - """ - pass - - def _check_user_in_local(self): - channel = ChannelManager.get_channel( - self._request.channel_name) - if channel is None: - self._channel = Channel( - self._request.channel_name, self._client, self._request.password) - ChannelManager.add_channel(self._channel) - else: - self._channel = channel - if self._client.info.nick_name in self._channel.users: - raise ChatException("user is already in channel") - - def _request_check(self) -> None: - # todo check if user already in local channel - # self._check_user_in_remote() - self._check_user_in_local() - self._user = ChannelUser(self._client, self._channel) - self._channel.add_bind_on_user_and_channel(self._user) - - super()._request_check() + # def _check_user_in_remote(self): + # """ + # todo maybe do not need because there are nick handler? + # """ + # pass + + # def _check_user_in_local(self): + # channel = ChannelManager.get_channel( + # self._request.channel_name) + # if channel is None: + # self._channel = Channel( + # self._request.channel_name, self._client, self._request.password) + # ChannelManager.add_channel(self._channel) + # else: + # self._channel = channel + # if self._client.info.nick_name in self._channel.users: + # raise ChatException("user is already in channel") + + # def _request_check(self) -> None: + # # todo check if user already in local channel + # # self._check_user_in_remote() + # self._request.parse() + # self._check_user_in_local() + # self._user = ChannelUser(self._client, self._channel) + # self._channel.add_bind_on_user_and_channel(self._user) + # super()._request_check() def _response_construct(self): self._response = JoinResponse(self._request, self._result) + # def _response_send(self): + # # for join request we need to send to our self + # self._channel.multicast(self._user.client, self._response, False) def _response_send(self): - # for join request we need to send to our self - self._channel.multicast(self._user.client, self._response, False) + super()._response_send() class KickHandler(ChannelHandlerBase): @@ -349,9 +347,6 @@ def __init__(self, client: ClientBase, request: KickRequest): def _response_construct(self): self._response = KickResponse(self._request, self._result) - def _response_send(self): - self._channel.multicast(self._user.client, self._response, True) - class ModeHandler(ChannelHandlerBase): _request: ModeRequest @@ -361,28 +356,15 @@ def __init__(self, client: ClientBase, request: ModeRequest): assert isinstance(request, ModeRequest) super().__init__(client, request) - def _response_construct(self): - match self._request.request_type: - case ( - ModeRequestType.GET_CHANNEL_AND_USER_MODES, - ModeRequestType.GET_CHANNEL_MODES, - ): - self._response = ModeResponse(self._request, self._result) - case ModeRequestType.SET_CHANNEL_MODES: - pass - case _: - raise ChatException("Unknown mode request type") - - # def _publish_message(self): - # if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: - # super()._publish_message() - - # def _update_channel_cache(self): - # if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: - # super()._update_channel_cache() + def _request_check(self) -> None: + super()._request_check() + if self._request.type == ModeRequestType.SET_CHANNEL_MODES: + self._is_fetching = False - def _response_send(self): - self._channel.multicast(self._user.client, self._response, True) + def _response_construct(self): + if self._request.type in [ModeRequestType.GET_CHANNEL_AND_USER_MODES, + ModeRequestType.GET_CHANNEL_MODES]: + self._response = ModeResponse(self._request, self._result) class NamesHandler(ChannelHandlerBase): @@ -408,9 +390,6 @@ def __init__(self, client: ClientBase, request: PartRequest): def _response_construct(self): self._response = PartResponse(self._request, self._result) - def _response_send(self): - self._channel.multicast(self._user.client, self._response, True) - class SetChannelKeyHandler(ChannelHandlerBase): _request: SetChannelKeyRequest @@ -423,9 +402,6 @@ def __init__(self, client: ClientBase, request: SetChannelKeyRequest): def _response_construct(self): self._response = SetChannelKeyResponse(self._request, self._result) - def _response_send(self): - self._channel.multicast(self._user.client, self._response, True) - class SetCKeyHandler(ChannelHandlerBase): _request: SetCKeyRequest @@ -437,9 +413,6 @@ def __init__(self, client: ClientBase, request: SetCKeyRequest): def _response_construct(self) -> None: self._response = SetCKeyResponse(self._request) - def _response_send(self): - self._channel.multicast(self._user.client, self._response, True) - class TopicHandler(ChannelHandlerBase): _request: TopicRequest @@ -457,7 +430,7 @@ def _response_send(self) -> None: case TopicRequestType.GET_CHANNEL_TOPIC: self._client.send(self._response) case TopicRequestType.SET_CHANNEL_TOPIC: - self._channel.multicast(self._client, self._response) + self._publish_to_brocker() # region Message diff --git a/src/servers/chat/src/applications/server_launcher.py b/src/servers/chat/src/applications/server_launcher.py index 357b3f821..166390fc7 100644 --- a/src/servers/chat/src/applications/server_launcher.py +++ b/src/servers/chat/src/applications/server_launcher.py @@ -12,7 +12,10 @@ def __init__(self) -> None: self.config = CONFIG.servers["Chat"] def _launch_server(self): - TcpServer(self.config, Client, self.logger).start() + assert self.config is not None + assert self.logger is not None + self.server = TcpServer(self.config, Client, self.logger) + self._launch_server() if __name__ == "__main__": diff --git a/src/servers/chat/src/contracts/requests.py b/src/servers/chat/src/contracts/requests.py index 220dd97fe..483ad2cd4 100644 --- a/src/servers/chat/src/contracts/requests.py +++ b/src/servers/chat/src/contracts/requests.py @@ -121,6 +121,7 @@ def parse(self): if self._cmd_params[1] == "*": self.request_type = LoginRequestType.NICK_AND_EMAIL_LOGIN self.password_hash = self._cmd_params[2] + assert isinstance(self._long_param, str) if self._long_param.count("@") != 2: raise ChatException("The profile nick format is incorrect.") @@ -143,6 +144,7 @@ def parse(self): if len(self._cmd_params) == 1: self.nick_name = self._cmd_params[0] elif self._long_param is None: + assert isinstance(self._long_param, str) self.nick_name = self._long_param else: raise ChatException("NICK request is invalid.") @@ -189,7 +191,8 @@ class RegisterNickRequest(RequestBase): def parse(self): super().parse() - self.namespace_id = self._cmd_params[0] + assert isinstance(self._cmd_params, list) + self.namespace_id = int(self._cmd_params[0]) self.unique_nick = self._cmd_params[1] self.cdkey = self._cmd_params[2] @@ -226,6 +229,7 @@ def parse(self): self.host_name = self._cmd_params[0] self.server_name = self._cmd_params[1] + assert isinstance(self._long_param, str) self.name = self._long_param @@ -345,7 +349,11 @@ class GetUdpRelayRequest(ChannelRequestBase): class JoinRequest(ChannelRequestBase): - password: str + password: Optional[str] + + def __init__(self, raw_request): + super().__init__(raw_request) + self.password = None def parse(self): super().parse() @@ -389,7 +397,7 @@ class ModeRequest(ChannelRequestBase): # "MODE " # "MODE " - request_type: ModeRequestType + type: ModeRequestType mode_operations: list[ModeOperationType] nick_name: str user_name: str @@ -406,9 +414,9 @@ def parse(self): return super().parse() if len(self._cmd_params) == 1: - self.request_type = ModeRequestType.GET_CHANNEL_MODES + self.type = ModeRequestType.GET_CHANNEL_MODES elif len(self._cmd_params) == 2 or len(self._cmd_params) == 3: - self.request_type = ModeRequestType.SET_CHANNEL_MODES + self.type = ModeRequestType.SET_CHANNEL_MODES self.mode_flag = self._cmd_params[1] modeFlags = [s for s in re.split( r"(?=\+|\-)", self.mode_flag) if s.strip()] diff --git a/src/servers/chat/src/contracts/responses.py b/src/servers/chat/src/contracts/responses.py index fb53e34b5..60f0ca2b4 100644 --- a/src/servers/chat/src/contracts/responses.py +++ b/src/servers/chat/src/contracts/responses.py @@ -53,6 +53,9 @@ class CdKeyResponse(ResponseBase): + def __init__(self) -> None: + pass + def build(self) -> None: self.sending_buffer = f":{SERVER_DOMAIN} {CD_KEY} * 1 :Authenticated.\r\n" # noqa @@ -218,11 +221,9 @@ def __init__(self, request: GetCKeyRequest, result: GetCKeyResult) -> None: def build(self) -> None: self.sending_buffer = "" for nick_name, user_values in self._result.infos: - self.sending_buffer += f":{SERVER_DOMAIN} {GET_CKEY} * { - self._request.channel_name} {nick_name} {self._request.cookie} {user_values}\r\n" + self.sending_buffer += f":{SERVER_DOMAIN} {GET_CKEY} * {self._request.channel_name} {nick_name} {self._request.cookie} {user_values}\r\n" # noqa - self.sending_buffer += f"{SERVER_DOMAIN} {END_GET_CKEY} * { - self._request.channel_name} {self._request.cookie} :End Of GETCKEY.\r\n" + self.sending_buffer += f"{SERVER_DOMAIN} {END_GET_CKEY} * {self._request.channel_name} {self._request.cookie} :End Of GETCKEY.\r\n" # noqa class JoinResponse(ResponseBase): @@ -265,7 +266,7 @@ def __init__(self, request: ModeRequest, result: ModeResult) -> None: super().__init__(request, result) def build(self) -> None: - if self._request.request_type == ModeRequestType.GET_CHANNEL_MODES: + if self._request.type == ModeRequestType.GET_CHANNEL_MODES: self.sending_buffer = f":{SERVER_DOMAIN} { CHANNEL_MODELS} * {self._result.channel_modes} {self._result.channel_modes}\r\n" diff --git a/src/servers/chat/tests/game_tests.py b/src/servers/chat/tests/game_tests.py index 3cca011e7..abe5560cc 100644 --- a/src/servers/chat/tests/game_tests.py +++ b/src/servers/chat/tests/game_tests.py @@ -6,8 +6,8 @@ class GameTests(unittest.TestCase): - responses.activate + @responses.activate def test_civilization4(self): raws = ["USRIP", "USER X419pGl4sX|18 127.0.0.1 peerchat.gamespy.com :aa3041ada9385b28fc4d4e47db288769", @@ -29,6 +29,7 @@ def test_civilization4(self): for raw in raws: client.on_received(raw.encode()) + @responses.activate def test_worm3d(self): raws = [ "CRYPT des 1 worms3\r\n", diff --git a/src/servers/chat/tests/mock_objects.py b/src/servers/chat/tests/mock_objects.py index 39536b4af..4248ca366 100644 --- a/src/servers/chat/tests/mock_objects.py +++ b/src/servers/chat/tests/mock_objects.py @@ -2,7 +2,7 @@ from library.src.configs import CONFIG from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url from servers.chat.src.applications.client import Client -from servers.chat.src.applications.handlers import UserIPHandler +from servers.chat.src.applications.handlers import * class ClientMock(Client): @@ -17,8 +17,21 @@ def create_client() -> Client: handler=handler, config=config, t_client=ClientMock, logger=logger) + create_mock_url(config, UserIPHandler, {"message": "ok"}) + create_mock_url(config, JoinHandler, {"message": "ok"}) + create_mock_url(config, UserHandler, {"message": "ok"}) + create_mock_url(config, CdKeyHandler, {"message": "ok"}) + create_mock_url(config, GetCKeyHandler, GetCKeyResult.model_validate( + {"channel_name": "test", "infos": [{"nick_name": "test_nick", "user_values": "/data/key/value/data"}]}).model_dump()) + create_mock_url(config, ModeHandler, ModeResult.model_validate( + {"channel_name": "test", "channel_modes": "+n", "joiner_nick_name": "test_nick"}).model_dump()) + create_mock_url(config, SetCKeyHandler, {"message": "ok"}) + create_mock_url(config, TopicHandler, TopicResult( + channel_name="test_chan", channel_topic="test").model_dump()) + create_mock_url(config, PartHandler, PartResult( + leaver_irc_prefix="test_prefix", is_channel_creator=False, channel_name="test_chan").model_dump()) + create_mock_url(config, NickHandler, NickResult( + nick_name="test").model_dump()) if TYPE_CHECKING: conn._client = cast(Client, conn._client) - create_mock_url(config, UserIPHandler, {"message": "ok"}) - return conn._client diff --git a/src/servers/game_status/src/applications/handlers.py b/src/servers/game_status/src/applications/handlers.py index 67e7e9f6e..a743cb6ee 100644 --- a/src/servers/game_status/src/applications/handlers.py +++ b/src/servers/game_status/src/applications/handlers.py @@ -61,19 +61,16 @@ class NewGameHandler(CmdHandlerBase): def __init__(self, client: Client, request: NewGameRequest) -> None: assert isinstance(request, NewGameRequest) super().__init__(client, request) - - def _feach_data(self): - pass - + self._is_fetching = False class SetPlayerDataHandler(CmdHandlerBase): def __init__(self, client: Client, request: SetPlayerDataRequest) -> None: assert isinstance(request, SetPlayerDataRequest) super().__init__(client, request) + self._is_fetching = False + - def _feach_data(self): - pass class UpdateGameHandler(CmdHandlerBase): @@ -87,7 +84,5 @@ class UpdateGameHandler(CmdHandlerBase): def __init__(self, client: Client, request: UpdateGameRequest) -> None: assert isinstance(request, UpdateGameRequest) super().__init__(client, request) - - def _feach_data(self): - pass + self._is_fetching = False diff --git a/src/servers/game_status/src/applications/server_launcher.py b/src/servers/game_status/src/applications/server_launcher.py index 2b2c7e524..8eb8b853f 100644 --- a/src/servers/game_status/src/applications/server_launcher.py +++ b/src/servers/game_status/src/applications/server_launcher.py @@ -12,7 +12,11 @@ def __init__(self) -> None: self.config = CONFIG.servers["GameStatus"] def _launch_server(self): - TcpServer(self.config, Client, self.logger).start() + assert self.config is not None + assert self.logger is not None + self.server = TcpServer(self.config, Client, self.logger) + super()._launch_server() + if __name__ == "__main__": diff --git a/src/servers/natneg/src/applications/handlers.py b/src/servers/natneg/src/applications/handlers.py index b290e50c2..df0577404 100644 --- a/src/servers/natneg/src/applications/handlers.py +++ b/src/servers/natneg/src/applications/handlers.py @@ -29,14 +29,13 @@ class AddressCheckHandler(CmdHandlerBase): _request: AddressCheckRequest _result: AddressCheckResult + _response: AddressCheckResponse def __init__(self, client: Client, request: AddressCheckRequest) -> None: assert isinstance(client, Client) assert isinstance(request, AddressCheckRequest) super().__init__(client, request) - - def _feach_data(self): - pass + self._is_fetching = False def _data_operate(self) -> None: """ @@ -59,9 +58,7 @@ class ConnectAckHandler(CmdHandlerBase): def __init__(self, client: Client, request: ConnectAckRequest) -> None: assert isinstance(request, ConnectAckRequest) super().__init__(client, request) - - def _feach_data(self): - pass + self._is_fetching = False def _data_operate(self) -> None: self._client.log_info( @@ -74,24 +71,19 @@ class ConnectHandler(CmdHandlerBase): def __init__(self, client: Client, request: ConnectRequest) -> None: assert isinstance(request, ConnectRequest) super().__init__(client, request) - self._result_cls = ConnectResult - - def _feach_data(self): - pass + self._is_fetching = False class ErtAckHandler(CmdHandlerBase): _request: ErtAckRequest _result: ErtAckResult + _response: ErcAckResponse def __init__(self, client: Client, request: ErtAckRequest) -> None: assert isinstance(request, ErtAckRequest) super().__init__(client, request) - def _feach_data(self): - pass - - def _data_operate(self) -> None: + def _feach_data(self) -> None: self._result = ErtAckResult( public_ip_addr=self._client.connection.remote_ip, public_port=self._client.connection.remote_port) @@ -106,15 +98,13 @@ class InitHandler(CmdHandlerBase): """ _request: InitRequest _result: InitResult + _response: InitResponse def __init__(self, client: Client, request: InitRequest) -> None: assert isinstance(request, InitRequest) super().__init__(client, request) - def _feach_data(self): - pass - - def _data_operate(self) -> None: + def _feach_data(self) -> None: self._result = InitResult( public_ip_addr=self._client.connection.remote_ip, public_port=self._client.connection.remote_port) @@ -131,15 +121,13 @@ def _response_send(self) -> None: class NatifyHandler(CmdHandlerBase): _request: NatifyRequest _result: NatifyResult + _response: NatifyResponse def __init__(self, client: Client, request: NatifyRequest) -> None: assert isinstance(request, NatifyRequest) super().__init__(client, request) def _feach_data(self): - pass - - def _data_operate(self): self._result = NatifyResult( public_ip_addr=self._client.connection.remote_ip, public_port=self._client.connection.remote_port) diff --git a/src/servers/natneg/src/applications/server_launcher.py b/src/servers/natneg/src/applications/server_launcher.py index ebae9d03b..a8fa9990c 100644 --- a/src/servers/natneg/src/applications/server_launcher.py +++ b/src/servers/natneg/src/applications/server_launcher.py @@ -12,7 +12,10 @@ def __init__(self) -> None: self.config = CONFIG.servers["NatNegotiation"] def _launch_server(self): - UdpServer(self.config, Client, self.logger).start() + assert self.config is not None + assert self.logger is not None + self.server = UdpServer(self.config, Client, self.logger) + super()._launch_server() if __name__ == "__main__": diff --git a/src/servers/natneg/tests/handler_tests.py b/src/servers/natneg/tests/handler_tests.py index 7a5b02e94..f773b3b7a 100644 --- a/src/servers/natneg/tests/handler_tests.py +++ b/src/servers/natneg/tests/handler_tests.py @@ -20,6 +20,7 @@ RequestType, ) +from servers.natneg.src.contracts.responses import InitResponse from servers.natneg.tests.mock_objects import create_client CmdHandlerBase._debug = True @@ -37,7 +38,6 @@ def test_init(self): client = create_client() - # test request parsing request = InitRequest(raw) request.parse() diff --git a/src/servers/natneg/tests/mock_objects.py b/src/servers/natneg/tests/mock_objects.py index b4bc9ce7b..97b8d247c 100644 --- a/src/servers/natneg/tests/mock_objects.py +++ b/src/servers/natneg/tests/mock_objects.py @@ -3,8 +3,7 @@ from servers.natneg.src.applications.client import Client from typing import cast -from servers.natneg.src.applications.handlers import AddressCheckHandler, InitHandler, NatifyHandler - +from servers.natneg.src.applications.handlers import AddressCheckHandler, ErtAckHandler, InitHandler, NatifyHandler class ClientMock(Client): @@ -19,11 +18,11 @@ def create_client() -> Client: handler=handler, config=CONFIG.servers["NatNegotiation"], t_client=ClientMock, logger=logger) - + config = CONFIG.servers["NatNegotiation"] create_mock_url(config, InitHandler, {"message": "ok"}) create_mock_url(config, AddressCheckHandler, {"message": "ok"}) create_mock_url(config, AddressCheckHandler, {"message": "ok"}) create_mock_url(config, NatifyHandler, {"message": "ok"}) - + create_mock_url(config, ErtAckHandler, {"message": "ok"}) return cast(Client, conn._client) diff --git a/src/servers/presence_connection_manager/src/applications/handlers.py b/src/servers/presence_connection_manager/src/applications/handlers.py index 24809efb1..951feef76 100644 --- a/src/servers/presence_connection_manager/src/applications/handlers.py +++ b/src/servers/presence_connection_manager/src/applications/handlers.py @@ -145,9 +145,9 @@ class BuddyListHandler(LoginedHandlerBase): _result: BuddyListResult _result_cls: type[BuddyListResult] - def __init__(self, client, request): - super().__init__(client, request) - + def __init__(self, client): + assert isinstance(client, Client) + self._client = client def response_construct(self): self._response = BuddyListResponse(self._request, self._result) diff --git a/src/servers/presence_connection_manager/src/applications/server_launcher.py b/src/servers/presence_connection_manager/src/applications/server_launcher.py index f3c37db29..204a75d6c 100644 --- a/src/servers/presence_connection_manager/src/applications/server_launcher.py +++ b/src/servers/presence_connection_manager/src/applications/server_launcher.py @@ -1,5 +1,5 @@ from library.src.abstractions.server_launcher import ServerLauncherBase -from library.src.network.udp_handler import UdpServer +from library.src.network.tcp_handler import TcpServer from library.src.configs import CONFIG from servers.presence_connection_manager.src.applications.client import Client @@ -11,7 +11,10 @@ def __init__(self) -> None: self.config = CONFIG.servers["PresenceConnectionManager"] def _launch_server(self): - UdpServer(self.config, Client, self.logger).start() + assert self.config is not None + assert self.logger is not None + self.server = TcpServer(self.config, Client, self.logger) + super()._launch_server() if __name__ == "__main__": diff --git a/src/servers/presence_search_player/src/applications/handlers.py b/src/servers/presence_search_player/src/applications/handlers.py index 74d27c29f..d63bcb9bf 100644 --- a/src/servers/presence_search_player/src/applications/handlers.py +++ b/src/servers/presence_search_player/src/applications/handlers.py @@ -91,6 +91,7 @@ class SearchHandler(CmdHandlerBase): """ _result_cls: type[SearchResult] _result: SearchResult + _response: SearchResponse def __init__(self, client: Client, request: SearchRequest) -> None: assert isinstance(request, SearchRequest) diff --git a/src/servers/presence_search_player/src/applications/server_launcher.py b/src/servers/presence_search_player/src/applications/server_launcher.py index 26cdc60f0..32fccfddd 100644 --- a/src/servers/presence_search_player/src/applications/server_launcher.py +++ b/src/servers/presence_search_player/src/applications/server_launcher.py @@ -11,7 +11,10 @@ def __init__(self) -> None: self.config = CONFIG.servers["PresenceSearchPlayer"] def _launch_server(self): - TcpServer(self.config, Client, self.logger).start() + assert self.config is not None + assert self.logger is not None + self.server = TcpServer(self.config, Client, self.logger) + super()._launch_server() if __name__ == "__main__": diff --git a/src/servers/presence_search_player/tests/game_tests.py b/src/servers/presence_search_player/tests/game_tests.py index 8c6429096..974028388 100644 --- a/src/servers/presence_search_player/tests/game_tests.py +++ b/src/servers/presence_search_player/tests/game_tests.py @@ -6,6 +6,7 @@ from servers.presence_search_player.src.applications.switcher import CmdSwitcher import responses +from servers.presence_search_player.src.contracts.responses import CheckResponse from servers.presence_search_player.tests.mock_objects import create_client @@ -21,6 +22,8 @@ def test_check(self): if TYPE_CHECKING: request = cast(CheckRequest, request) response = switcher._handlers[0]._response + if TYPE_CHECKING: + response = cast(CheckResponse, response) self.assertEqual("spyguy", request.nick) self.assertEqual("spyguy@gamespy.com", request.email) self.assertEqual("4a7d1ed414474e4033ac29ccb8653d9b", request.password) diff --git a/src/servers/query_report/src/applications/server_launcher.py b/src/servers/query_report/src/applications/server_launcher.py index 50330b9ee..da8e8aedf 100644 --- a/src/servers/query_report/src/applications/server_launcher.py +++ b/src/servers/query_report/src/applications/server_launcher.py @@ -12,8 +12,11 @@ def __init__(self) -> None: self.config = CONFIG.servers["QueryReport"] def _launch_server(self): + assert self.config is not None + assert self.logger is not None self.server = UdpServer(self.config, Client, self.logger) - self.server.start() + super()._launch_server() + if __name__ == "__main__": diff --git a/src/servers/query_report/src/v2/applications/handlers.py b/src/servers/query_report/src/v2/applications/handlers.py index 899e70f75..56afac398 100644 --- a/src/servers/query_report/src/v2/applications/handlers.py +++ b/src/servers/query_report/src/v2/applications/handlers.py @@ -25,9 +25,9 @@ class AvailableHandler(CmdHandlerBase): def __init__(self, client: Client, request: AvaliableRequest) -> None: assert isinstance(request, AvaliableRequest) super().__init__(client, request) + self._is_fetching = False + - def _feach_data(self): - pass def _response_construct(self): self._response = AvaliableResponse(self._request) @@ -98,6 +98,6 @@ class KeepAliveHandler(CmdHandlerBase): def __init__(self, client: Client, request: KeepAliveRequest) -> None: assert isinstance(request, KeepAliveRequest) super().__init__(client, request) + self._is_fetching = False + - def _feach_data(self): - pass diff --git a/src/servers/server_browser/src/v2/applications/server_launcher.py b/src/servers/server_browser/src/v2/applications/server_launcher.py index b77c7938d..3eb9a58b4 100644 --- a/src/servers/server_browser/src/v2/applications/server_launcher.py +++ b/src/servers/server_browser/src/v2/applications/server_launcher.py @@ -11,7 +11,10 @@ def __init__(self) -> None: self.config = CONFIG.servers["ServerBrowserV2"] def _launch_server(self): - TcpServer(self.config, Client, self.logger).start() + assert self.config is not None + assert self.logger is not None + self.server = TcpServer(self.config, Client, self.logger) + super()._launch_server() if __name__ == "__main__": diff --git a/src/servers/web_services/src/applications/server_launcher.py b/src/servers/web_services/src/applications/server_launcher.py index 32fb5402d..aa996d580 100644 --- a/src/servers/web_services/src/applications/server_launcher.py +++ b/src/servers/web_services/src/applications/server_launcher.py @@ -12,7 +12,10 @@ def __init__(self) -> None: self.config = CONFIG.servers["WebServices"] def _launch_server(self): - HttpServer(self.config, Client, self.logger).start() + assert self.config is not None + assert self.logger is not None + self.server = HttpServer(self.config, Client, self.logger) + super()._launch_server() if __name__ == "__main__": diff --git a/src/unispy b/src/unispy deleted file mode 100644 index 2601530ef..000000000 --- a/src/unispy +++ /dev/null @@ -1,413 +0,0 @@ -2024-10-29 02:29:48 [unispy] [INFO]: [172.19.0.5:35003] [recv]: b'Hello, UDP!\n' -2024-10-29 02:33:53 [unispy] [INFO]: [172.19.0.5:47697] [recv]: b'Hello, UDP!\n' -2024-10-29 02:33:59 [unispy] [INFO]: [172.19.0.5:47697]: Invalid request received! -2024-10-30 05:48:28 [unispy] [ERROR]: [Errno 111] Connection refused - goodbye -2024-10-30 05:48:58 [unispy] [DEBUG]: --- request header --- -2024-10-30 05:48:58 [unispy] [DEBUG]: GET /GameSpy/Chat/Channel HTTP/1.1 -Upgrade: websocket -Host: 127.0.0.1:8080 -Origin: http://127.0.0.1:8080 -Sec-WebSocket-Key: PG0nOCrh8mpemVL2zR+POg== -Sec-WebSocket-Version: 13 -Connection: Upgrade - - -2024-10-30 05:48:58 [unispy] [DEBUG]: ----------------------- -2024-10-30 05:48:58 [unispy] [DEBUG]: --- response header --- -2024-10-30 05:48:58 [unispy] [DEBUG]: HTTP/1.1 404 Not Found -2024-10-30 05:48:58 [unispy] [DEBUG]: date: Wed, 30 Oct 2024 05:48:57 GMT -2024-10-30 05:48:58 [unispy] [DEBUG]: server: uvicorn -2024-10-30 05:48:58 [unispy] [DEBUG]: content-length: 22 -2024-10-30 05:48:58 [unispy] [DEBUG]: content-type: application/json -2024-10-30 05:48:58 [unispy] [DEBUG]: ----------------------- -2024-10-30 05:48:58 [unispy] [ERROR]: Handshake status 404 Not Found -+-+- {'date': 'Wed, 30 Oct 2024 05:48:57 GMT', 'server': 'uvicorn', 'content-length': '22', 'content-type': 'application/json'} -+-+- b'{"detail":"Not Found"}' - goodbye -2024-10-30 05:49:45 [unispy] [ERROR]: [Errno 111] Connection refused - goodbye -2024-10-30 05:50:23 [unispy] [DEBUG]: --- request header --- -2024-10-30 05:50:23 [unispy] [DEBUG]: GET /GameSpy/Chat/Channel HTTP/1.1 -Upgrade: websocket -Host: 127.0.0.1:8080 -Origin: http://127.0.0.1:8080 -Sec-WebSocket-Key: 0usGdjTC0jytMHzSo/EsKA== -Sec-WebSocket-Version: 13 -Connection: Upgrade - - -2024-10-30 05:50:23 [unispy] [DEBUG]: ----------------------- -2024-10-30 05:50:23 [unispy] [DEBUG]: --- response header --- -2024-10-30 05:50:23 [unispy] [DEBUG]: HTTP/1.1 404 Not Found -2024-10-30 05:50:23 [unispy] [DEBUG]: date: Wed, 30 Oct 2024 05:50:22 GMT -2024-10-30 05:50:23 [unispy] [DEBUG]: server: uvicorn -2024-10-30 05:50:23 [unispy] [DEBUG]: content-length: 9 -2024-10-30 05:50:23 [unispy] [DEBUG]: content-type: text/plain; charset=utf-8 -2024-10-30 05:50:23 [unispy] [DEBUG]: ----------------------- -2024-10-30 05:50:23 [unispy] [ERROR]: Handshake status 404 Not Found -+-+- {'date': 'Wed, 30 Oct 2024 05:50:22 GMT', 'server': 'uvicorn', 'content-length': '9', 'content-type': 'text/plain; charset=utf-8'} -+-+- b'Not Found' - goodbye -2024-10-30 05:51:24 [unispy] [DEBUG]: --- request header --- -2024-10-30 05:51:24 [unispy] [DEBUG]: GET /GameSpy/Chat/Channel HTTP/1.1 -Upgrade: websocket -Host: 127.0.0.1:8080 -Origin: http://127.0.0.1:8080 -Sec-WebSocket-Key: U/5/wTk/QDGKhj1GM+g/tA== -Sec-WebSocket-Version: 13 -Connection: Upgrade - - -2024-10-30 05:51:24 [unispy] [DEBUG]: ----------------------- -2024-10-30 05:51:24 [unispy] [DEBUG]: --- response header --- -2024-10-30 05:51:24 [unispy] [DEBUG]: HTTP/1.1 101 Switching Protocols -2024-10-30 05:51:24 [unispy] [DEBUG]: Upgrade: websocket -2024-10-30 05:51:24 [unispy] [DEBUG]: Connection: Upgrade -2024-10-30 05:51:24 [unispy] [DEBUG]: Sec-WebSocket-Accept: ybgaKobanY3o8V/gxbVYsaX79wE= -2024-10-30 05:51:24 [unispy] [DEBUG]: date: Wed, 30 Oct 2024 05:51:24 GMT -2024-10-30 05:51:24 [unispy] [DEBUG]: server: uvicorn -2024-10-30 05:51:24 [unispy] [DEBUG]: ----------------------- -2024-10-30 05:51:24 [unispy] [INFO]: Websocket connected -2024-11-01 03:12:23 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:23 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:24 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:24 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:24 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:25 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:25 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:25 [unispy] [INFO]: 4 changes detected -2024-11-01 03:12:26 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:26 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:26 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:27 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:27 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:27 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:28 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:28 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:28 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:29 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:29 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:30 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:30 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:30 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:31 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:31 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:31 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:32 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:32 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:32 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:33 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:33 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:33 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:34 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:34 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:34 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:35 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:35 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:36 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:36 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:36 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:37 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:37 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:37 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:38 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:38 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:38 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:39 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:39 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:39 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:40 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:40 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:40 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:41 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:41 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:41 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:42 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:42 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:43 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:43 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:43 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:44 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:44 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:44 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:45 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:45 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:45 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:46 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:46 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:46 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:47 [unispy] [INFO]: 4 changes detected -2024-11-01 03:12:47 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:48 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:48 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:48 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:49 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:49 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:49 [unispy] [INFO]: 10 changes detected -2024-11-01 03:12:50 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:50 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:50 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:51 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:51 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:51 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:52 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:52 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:52 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:53 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:53 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:54 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:54 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:54 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:55 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:55 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:55 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:56 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:56 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:56 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:57 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:57 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:57 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:58 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:58 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:58 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:59 [unispy] [INFO]: 1 change detected -2024-11-01 03:12:59 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:00 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:00 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:00 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:01 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:01 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:01 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:02 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:02 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:02 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:03 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:03 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:03 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:04 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:04 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:04 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:05 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:05 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:05 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:06 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:06 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:07 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:07 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:07 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:08 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:08 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:08 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:09 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:09 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:09 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:10 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:10 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:10 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:11 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:11 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:11 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:12 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:12 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:12 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:13 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:13 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:14 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:14 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:14 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:15 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:15 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:15 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:16 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:16 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:16 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:17 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:17 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:17 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:18 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:18 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:18 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:19 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:19 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:19 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:20 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:20 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:21 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:21 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:21 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:22 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:22 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:22 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:23 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:23 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:23 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:24 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:24 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:24 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:25 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:25 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:25 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:26 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:26 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:27 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:27 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:27 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:28 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:28 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:28 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:29 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:29 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:29 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:30 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:30 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:30 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:31 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:31 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:31 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:32 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:32 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:32 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:33 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:33 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:34 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:34 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:34 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:35 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:35 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:35 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:36 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:36 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:36 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:37 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:37 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:37 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:38 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:38 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:38 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:39 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:39 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:39 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:40 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:40 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:41 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:41 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:41 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:42 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:42 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:42 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:43 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:43 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:43 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:44 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:44 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:44 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:45 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:45 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:45 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:46 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:46 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:47 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:47 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:47 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:48 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:48 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:48 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:49 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:49 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:49 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:50 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:50 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:50 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:51 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:51 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:51 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:52 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:52 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:52 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:53 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:53 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:54 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:54 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:54 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:55 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:55 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:55 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:56 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:56 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:56 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:57 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:57 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:57 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:58 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:58 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:58 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:59 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:59 [unispy] [INFO]: 1 change detected -2024-11-01 03:13:59 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:00 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:00 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:01 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:01 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:01 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:02 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:02 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:02 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:03 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:03 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:03 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:04 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:04 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:04 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:05 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:05 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:05 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:06 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:06 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:06 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:07 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:07 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:08 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:08 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:08 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:09 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:09 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:09 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:10 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:10 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:10 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:11 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:11 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:11 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:12 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:12 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:12 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:13 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:13 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:14 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:14 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:14 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:15 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:15 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:15 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:16 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:16 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:16 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:17 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:17 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:17 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:18 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:18 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:18 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:19 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:19 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:19 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:20 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:20 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:21 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:21 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:21 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:22 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:22 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:22 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:23 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:23 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:23 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:24 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:24 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:24 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:25 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:25 [unispy] [INFO]: 1 change detected -2024-11-01 03:14:25 [unispy] [INFO]: 1 change detected From ff273b946e5cd1721b2f59544891cbadb7d0fd1d Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 4 Nov 2024 13:21:49 +0000 Subject: [PATCH 139/231] refactor: added mock websocket server --- src/.devcontainer/devcontainer.json | 2 +- src/library/src/abstractions/handler.py | 3 +- .../src/abstractions/server_launcher.py | 1 - src/library/tests/mock_objects.py | 6 +++- src/servers/chat/src/abstractions/handler.py | 3 +- src/servers/chat/src/applications/handlers.py | 36 ++----------------- src/servers/chat/tests/mock_objects.py | 14 ++++++-- 7 files changed, 25 insertions(+), 40 deletions(-) diff --git a/src/.devcontainer/devcontainer.json b/src/.devcontainer/devcontainer.json index 98f9afb34..b8c8c34a8 100644 --- a/src/.devcontainer/devcontainer.json +++ b/src/.devcontainer/devcontainer.json @@ -27,7 +27,7 @@ 80 ], // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "pip3 config set global.index-url https://mirrors.tuna.tsinghua.edu.cn/pypi/web/simple && pip3 install --user -r requirements.txt" + "postCreateCommand": "pip3 install --user -r requirements.txt" // "postCreateCommand": "pip3 install --user -r requirements.txt" // Configure tool-specific properties. // "customizations": {}, diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index 851889bbf..b4bc8e5ee 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -105,7 +105,8 @@ def _feach_data(self): if child class do not require feach, overide this function to do nothing """ if self._result_cls is None: - raise UniSpyException("_result should not be null when feach data") + raise UniSpyException( + f"_result in {self.__class__.__name__} should not be null when feach data") assert issubclass(self._result_cls, ResultBase) self._result = self._result_cls(**self._http_result) diff --git a/src/library/src/abstractions/server_launcher.py b/src/library/src/abstractions/server_launcher.py index 68f9433df..41ac066f4 100644 --- a/src/library/src/abstractions/server_launcher.py +++ b/src/library/src/abstractions/server_launcher.py @@ -8,7 +8,6 @@ import pyfiglet import requests from prettytable import PrettyTable -import keyboard VERSION = 0.45 diff --git a/src/library/tests/mock_objects.py b/src/library/tests/mock_objects.py index 98d876520..d99d0f839 100644 --- a/src/library/tests/mock_objects.py +++ b/src/library/tests/mock_objects.py @@ -41,15 +41,19 @@ def warn(self, message): class BrokerMock(BrockerBase): + def __init__(self) -> None: + pass + def subscribe(self): pass def publish_message(self, message): - pass + print(f"[multi] {message}") def unsubscribe(self): pass + def create_mock_url(config: ServerConfig, handler: type[CmdHandlerBase], data: dict) -> None: # fmt: off url = f"{CONFIG.backend.url}/GameSpy/{config.server_name}/{handler.__name__}/" diff --git a/src/servers/chat/src/abstractions/handler.py b/src/servers/chat/src/abstractions/handler.py index f19ad2c94..3bf020a10 100644 --- a/src/servers/chat/src/abstractions/handler.py +++ b/src/servers/chat/src/abstractions/handler.py @@ -1,3 +1,4 @@ +from library.src.abstractions.brocker import BrockerBase from library.src.configs import CONFIG from library.src.extentions.encoding import UniSpyJsonEncoder from library.src.network.brockers import WebsocketBrocker @@ -85,7 +86,7 @@ class ChannelHandlerBase(PostLoginHandlerBase): """ broadcast message """ - _brocker: WebsocketBrocker = WebsocketBrocker( + _brocker: BrockerBase = WebsocketBrocker( "channel", CONFIG.backend.url, handle_brocker_message) def __init__(self, client: ClientBase, request: RequestBase): diff --git a/src/servers/chat/src/applications/handlers.py b/src/servers/chat/src/applications/handlers.py index 0c9b02d6a..2b1ee0072 100644 --- a/src/servers/chat/src/applications/handlers.py +++ b/src/servers/chat/src/applications/handlers.py @@ -298,43 +298,11 @@ class JoinHandler(ChannelHandlerBase): def __init__(self, client: ClientBase, request: JoinRequest): assert isinstance(request, JoinRequest) super().__init__(client, request) - - # def _check_user_in_remote(self): - # """ - # todo maybe do not need because there are nick handler? - # """ - # pass - - # def _check_user_in_local(self): - # channel = ChannelManager.get_channel( - # self._request.channel_name) - # if channel is None: - # self._channel = Channel( - # self._request.channel_name, self._client, self._request.password) - # ChannelManager.add_channel(self._channel) - # else: - # self._channel = channel - # if self._client.info.nick_name in self._channel.users: - # raise ChatException("user is already in channel") - - # def _request_check(self) -> None: - # # todo check if user already in local channel - # # self._check_user_in_remote() - # self._request.parse() - # self._check_user_in_local() - # self._user = ChannelUser(self._client, self._channel) - # self._channel.add_bind_on_user_and_channel(self._user) - # super()._request_check() + self._result_cls = JoinResult def _response_construct(self): self._response = JoinResponse(self._request, self._result) - # def _response_send(self): - # # for join request we need to send to our self - # self._channel.multicast(self._user.client, self._response, False) - def _response_send(self): - super()._response_send() - class KickHandler(ChannelHandlerBase): _request: KickRequest @@ -343,6 +311,7 @@ class KickHandler(ChannelHandlerBase): def __init__(self, client: ClientBase, request: KickRequest): assert isinstance(request, KickRequest) super().__init__(client, request) + self._result_cls = KickResult def _response_construct(self): self._response = KickResponse(self._request, self._result) @@ -355,6 +324,7 @@ class ModeHandler(ChannelHandlerBase): def __init__(self, client: ClientBase, request: ModeRequest): assert isinstance(request, ModeRequest) super().__init__(client, request) + self._result_cls = ModeResult def _request_check(self) -> None: super()._request_check() diff --git a/src/servers/chat/tests/mock_objects.py b/src/servers/chat/tests/mock_objects.py index 4248ca366..12a9b33ec 100644 --- a/src/servers/chat/tests/mock_objects.py +++ b/src/servers/chat/tests/mock_objects.py @@ -1,6 +1,9 @@ +import asyncio +import threading +from time import sleep from typing import TYPE_CHECKING, cast from library.src.configs import CONFIG -from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url +from library.tests.mock_objects import BrokerMock, ConnectionMock, LogMock, RequestHandlerMock, create_mock_url from servers.chat.src.applications.client import Client from servers.chat.src.applications.handlers import * @@ -18,7 +21,8 @@ def create_client() -> Client: config=config, t_client=ClientMock, logger=logger) create_mock_url(config, UserIPHandler, {"message": "ok"}) - create_mock_url(config, JoinHandler, {"message": "ok"}) + create_mock_url(config, JoinHandler, JoinResult( + joiner_nick_name="unispy", joiner_prefix="xiaojiuwo!unispy@UNISPYSERVER", all_channel_user_nicks="test", channel_modes="+q").model_dump()) create_mock_url(config, UserHandler, {"message": "ok"}) create_mock_url(config, CdKeyHandler, {"message": "ok"}) create_mock_url(config, GetCKeyHandler, GetCKeyResult.model_validate( @@ -32,6 +36,12 @@ def create_client() -> Client: leaver_irc_prefix="test_prefix", is_channel_creator=False, channel_name="test_chan").model_dump()) create_mock_url(config, NickHandler, NickResult( nick_name="test").model_dump()) + ChannelHandlerBase._brocker = BrokerMock() if TYPE_CHECKING: conn._client = cast(Client, conn._client) + return conn._client + + +if __name__ == "__main__": + create_client() From 9ed1fe1ce807651a6fe04979136000c4ae381f63 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 5 Nov 2024 02:36:55 +0000 Subject: [PATCH 140/231] fix: chat unit-test --- .../protocols/gamespy/chat/channel.py | 200 ++++++++++++++++++ .../protocols/gamespy/chat/channel_user.py | 36 ++++ src/backends/protocols/gamespy/chat/data.py | 21 +- .../protocols/gamespy/chat/managers.py | 60 ++++++ .../protocols/gamespy/chat/requests.py | 3 - src/backends/routers/gamespy/chat.py | 3 +- src/backends/tests/gamespy/chat/room_tests.py | 22 ++ src/library/src/abstractions/client.py | 3 + src/library/tests/mock_objects.py | 2 +- src/servers/chat/src/abstractions/contract.py | 12 ++ src/servers/chat/src/abstractions/handler.py | 30 +-- src/servers/chat/src/aggregates/channel.py | 200 ------------------ .../chat/src/aggregates/channel_user.py | 36 ---- src/servers/chat/src/aggregates/managers.py | 60 ------ src/servers/chat/src/applications/client.py | 8 +- src/servers/chat/src/applications/handlers.py | 16 +- src/servers/chat/src/applications/switcher.py | 3 +- src/servers/chat/src/contracts/requests.py | 8 +- src/servers/chat/src/contracts/responses.py | 4 +- src/servers/chat/src/contracts/results.py | 2 +- src/servers/chat/tests/game_tests.py | 3 + src/servers/chat/tests/mock_objects.py | 8 + src/servers/chat/tests/room_tests.py | 22 -- 23 files changed, 387 insertions(+), 375 deletions(-) create mode 100644 src/backends/protocols/gamespy/chat/channel.py create mode 100644 src/backends/protocols/gamespy/chat/channel_user.py create mode 100644 src/backends/protocols/gamespy/chat/managers.py create mode 100644 src/backends/tests/gamespy/chat/room_tests.py delete mode 100644 src/servers/chat/src/aggregates/channel.py delete mode 100644 src/servers/chat/src/aggregates/channel_user.py delete mode 100644 src/servers/chat/src/aggregates/managers.py delete mode 100644 src/servers/chat/tests/room_tests.py diff --git a/src/backends/protocols/gamespy/chat/channel.py b/src/backends/protocols/gamespy/chat/channel.py new file mode 100644 index 000000000..c47c558ee --- /dev/null +++ b/src/backends/protocols/gamespy/chat/channel.py @@ -0,0 +1,200 @@ +# import datetime +# from typing import Optional +# from uuid import UUID + +# from pydantic import BaseModel, field_validator +# from library.src.abstractions.brocker import BrockerBase +# from library.src.network.brockers import WebsocketBrocker +# from library.src.configs import CONFIG +# from servers.chat.src.abstractions.contract import ResponseBase +# from servers.chat.src.aggregates.channel_user import ChannelUser +# from backends.protocols.gamespy.chat.managers import KeyValueManager +# from servers.chat.src.aggregates.peer_room import PeerRoom +# from servers.chat.src.applications.client import Client +# from servers.chat.src.contracts.requests import ModeRequest +# from servers.chat.src.aggregates.enums import PeerRoomType +# from servers.chat.src.aggregates.exceptions import ChatException +# from servers.server_browser.src.v2.aggregations.server_info_builder import PEER_GROUP_LIST + +# MIN_CHANNEL_NAME_LENGTH = 4 + + +# class Channel: +# """ +# The channel class, every channel class manage a brocker +# """ +# server_id: UUID +# game_name: str +# name: str +# max_num_user: int +# create_time: datetime.datetime +# kv_manager: KeyValueManager +# room_type: PeerRoomType +# password: Optional[str] +# topic: Optional[str] +# group_id: Optional[int] +# room_name: Optional[str] +# previously_join_channel: Optional[str] +# ban_list: dict[str, ChannelUser] +# users: dict[str, ChannelUser] +# _creator_nick_name: str + +# @property +# def is_valid_peer_room(self) -> bool: +# return self.group_id is not None and self.room_name is not None + +# def __init__(self, name: str, client: Client, password: Optional[str] = None, brocker_cls: type[BrockerBase] = WebsocketBrocker) -> None: +# # region channel init +# self.server_id = client.server_config.server_id +# self.name = name +# self.password = password +# self.game_name = client.info.gamename +# self.previously_join_channel = client.info.previously_joined_channel +# self.room_type = PeerRoom.get_room_type(name) +# self.create_time = datetime.datetime.now() +# self.topic = None +# self.group_id = None +# self.room_name = None +# self.previously_join_channel = None +# self.kv_manager = KeyValueManager() +# self.max_num_user = 200 +# # setup the message broker +# self._broker = brocker_cls( +# self.name, CONFIG.backend.url, self._get_message_from_brocker) +# # channel user init +# self._broker.subscribe() +# self.ban_list = {} +# self.users = {} +# self._creator_nick_name = client.info.nick_name + +# match self.room_type: +# case PeerRoomType.Group: +# self.get_group_id() +# self.get_peer_room_name() +# case PeerRoomType.Staging: +# self.get_staging_room_name() +# case PeerRoomType.Title: +# self.get_title_room_name() + +# def get_group_id(self): +# group_id_str = self.name.split("!")[1] +# try: +# group_id = int(group_id_str) +# except ValueError: +# raise Exception("Peer room group id is incorrect") +# self.group_id = group_id + +# def get_peer_room_name(self): +# if self.game_name in PEER_GROUP_LIST: +# grouplist = PEER_GROUP_LIST[self.game_name] +# room = next( +# (g for g in grouplist if g["group_id"] == self.group_id), None) +# if room is None: +# raise Exception(f"Invalid peer room: {self.name}") +# self.room_name = room["room_name"] + +# def get_staging_room_name(self): +# self.room_name = self.name.split("!")[-1] + +# def get_title_room_name(self): +# self.get_staging_room_name() + +# @property +# def creator(self) -> Optional[ChannelUser]: +# if self._creator_nick_name in self.users: +# return self.users[self._creator_nick_name] +# else: +# return None + +# def _add_ban_user(self, request: ModeRequest): +# assert isinstance(request, ModeRequest) +# if request.nick_name not in self.users: +# raise ChatException( +# f"user:{request.nick_name} did not connected to this server" +# ) +# user = self.users[request.nick_name] + +# self.ban_list[request.nick_name] = user + +# def _remove_ban_user(self, nick_name: str): +# if nick_name in self.ban_list: +# del self.ban_list[nick_name] + +# def _add_channel_operator(self, nick_name: str): +# if nick_name not in self.users: +# return + +# user = self.users[nick_name] +# if not user.is_channel_creator: +# user.is_channel_creator = True + +# def _remove_channel_operator(self, nick_name: str): +# if nick_name not in self.users: +# return + +# user = self.users[nick_name] +# user.is_channel_creator = False + +# def _user_voice_permission(self, nick_name: str, enable: bool = True): +# if nick_name not in self.users: +# return +# user = self.users[nick_name] +# user.is_voiceable = enable + +# def get_user_by_nick(self, nick_name: str) -> Optional[ChannelUser]: +# if nick_name in self.users: +# return self.users[nick_name] +# return None + +# def get_user_by_client(self, client: Client) -> Optional[ChannelUser]: +# for user in self.users.values(): +# if ( +# client.connection.remote_ip == user.remote_ip +# and client.connection.remote_port == user.remote_port +# ): +# return user +# return None + +# def add_bind_on_user_and_channel(self, joiner: ChannelUser): +# if joiner.client.info.nick_name not in joiner.channel.users: +# joiner.client.info.joined_channels[joiner.channel.name] = joiner.channel + +# def remove_bind_on_user_and_channel(self, leaver: ChannelUser): +# if leaver.client.info.nick_name in leaver.channel.users: +# del leaver.channel.users[leaver.client.info.nick_name] +# if leaver.channel.name in leaver.client.info.joined_channels: +# del leaver.client.info.joined_channels[leaver.channel.name] + +# def multicast(self, sender: Client, message: ResponseBase, is_skip_sender=False): +# for nick, user in self.users.items(): +# if is_skip_sender: +# if sender.info.nick_name == nick: +# continue +# else: +# user.client.send(message) + +# def _get_message_from_brocker(self, message: str): +# """ +# we directly send the message from brocker to all channel local user +# """ +# for nick, user in self.users.items(): +# user.client.connection.send(message.encode()) + +# def remove_user(self, user: ChannelUser): +# user.client.info.previously_joined_channel + + +# class BrockerMessage(BaseModel): +# channel_name: str +# message: str + +# @field_validator("channel_name") +# def validate_channel_name(cls, value): +# if value is None or len(value) < 3: +# raise ValueError("channel name is not valid") +# return value + +# @field_validator("message") +# def validate_message(cls, value): +# if value is None or len(value) < 3: +# raise ValueError("message length is not valid") diff --git a/src/backends/protocols/gamespy/chat/channel_user.py b/src/backends/protocols/gamespy/chat/channel_user.py new file mode 100644 index 000000000..2ad66f8c8 --- /dev/null +++ b/src/backends/protocols/gamespy/chat/channel_user.py @@ -0,0 +1,36 @@ +# from uuid import UUID + +# from backends.protocols.gamespy.chat.managers import KeyValueManager +# from servers.chat.src.applications.client import Client +# from typing import TYPE_CHECKING + +# if TYPE_CHECKING: +# from servers.chat.src.aggregates.channel import Channel + + +# class ChannelUser: +# server_id: UUID +# is_voiceable: bool = True +# is_channel_operator: bool = False +# is_channel_creator: bool = False +# remote_ip: str +# remote_port: int +# client: Client +# kv_manager: KeyValueManager = KeyValueManager() +# channel: "Channel" + +# @property +# def modes(self): +# buffer = "" +# if self.is_channel_creator: +# buffer += "@" +# if self.is_voiceable: +# buffer += "+" +# return buffer + +# def __init__(self, client: Client, channel: "Channel") -> None: +# self.client = client +# self.channel = channel +# self.server_id = client.server_config.server_id +# self.remote_ip = client.connection.remote_ip +# self.remote_port = client.connection.remote_port diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index 906c3a746..484f17538 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -1,6 +1,6 @@ +from datetime import datetime, timedelta from typing import TYPE_CHECKING, cast from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatUserCaches, Users, Profiles, SubProfiles -from servers.chat.src.aggregates.channel import Channel from servers.chat.src.aggregates.exceptions import ChatException @@ -42,17 +42,15 @@ def uniquenick_login(uniquenick:str,namespace_id:int)-> tuple[int, int, bool, bo def is_channel_exist(channel_name:str,game_name:str)->bool: channel_count = PG_SESSION.query(ChatChannelCaches)\ .filter(ChatChannelCaches.channel_name == channel_name, - ChatChannelCaches.game_name == game_name)\ + ChatChannelCaches.game_name == game_name, + ChatChannelCaches.update_time >= datetime.now()-timedelta(minutes=10))\ .count() if channel_count == 1: return True else: return False -def add_channel(channel:Channel): - info = ChatChannelCaches( - channel_name=channel.name, game_name=channel.game_name, key_values =channel.kv_manager.data, max_num_user=channel.max_num_user, room_name=channel.room_name, topic=channel.topic, password=channel.password, group_id=channel.group_id, create_time=channel.create_time, previously_joined_channel=channel.previously_join_channel - ) - PG_SESSION.add(info) +def add_channel(channel:ChatChannelCaches): + PG_SESSION.add(channel) PG_SESSION.commit() def get_channel_cache(channel_name:str,game_name:str)->ChatChannelCaches: @@ -62,12 +60,9 @@ def get_channel_cache(channel_name:str,game_name:str)->ChatChannelCaches: .first() return channel -def update_channel(channel:Channel): - - info = ChatChannelCaches( - channel_name=channel.name, game_name=channel.game_name, key_values =channel.kv_manager.data, max_num_user=channel.max_num_user, room_name=channel.room_name, topic=channel.topic, password=channel.password, group_id=channel.group_id, create_time=channel.create_time, previously_joined_channel=channel.previously_join_channel - ) - PG_SESSION.add(info) +def update_channel(channel:ChatChannelCaches): + channel.update_time = datetime.now() # type: ignore + PG_SESSION.commit() def get_user_cache_by_nick_name(nick_name:str)->ChatUserCaches: diff --git a/src/backends/protocols/gamespy/chat/managers.py b/src/backends/protocols/gamespy/chat/managers.py new file mode 100644 index 000000000..0b0492ea7 --- /dev/null +++ b/src/backends/protocols/gamespy/chat/managers.py @@ -0,0 +1,60 @@ +# from typing import TYPE_CHECKING, Optional + + +# class KeyValueManager: +# data: dict +# """ +# store the key and values +# """ + +# def __init__(self): +# self.data = {} + +# def update(self, data: dict): +# for key, value in data.items(): +# self.data[key] = value + +# def build_key_value_string(self, key_values: dict): +# flags = "" +# for key, value in key_values.items(): +# flags += f"\\{key}\\{value}" +# return flags + +# def get_value_string(self, keys: list[str]) -> str: +# values = "" +# for key in keys: +# if key in self.data: +# values += f"\\{self.data[key]}" +# else: +# values += "\\" +# # Uncomment the line below to raise an exception if key is not found +# # raise Exception(f"Can not find key: {key}") +# return values + +# def is_contain_all_key(self, keys: list[str]): +# return all(key in self.data for key in keys) + + +# if TYPE_CHECKING: +# from servers.chat.src.aggregates.channel import Channel + + +# class ChannelManager: +# local_channels: dict = {} +# """The code blow is for channel manage""" + +# @staticmethod +# def get_channel(name: str) -> Optional["Channel"]: +# if name in ChannelManager.local_channels: +# return ChannelManager.local_channels[name] +# return None + +# @staticmethod +# def add_channel(channel: "Channel"): +# if channel.name not in ChannelManager.local_channels: +# ChannelManager.local_channels[channel.name] = channel + +# @staticmethod +# def remove_channel(name: str) -> None: +# if name in ChannelManager.local_channels: +# del ChannelManager.local_channels[name] diff --git a/src/backends/protocols/gamespy/chat/requests.py b/src/backends/protocols/gamespy/chat/requests.py index b8e427e47..8b814c64f 100644 --- a/src/backends/protocols/gamespy/chat/requests.py +++ b/src/backends/protocols/gamespy/chat/requests.py @@ -5,9 +5,6 @@ class RequestBase(lib.RequestBase): raw_request: str command_name: str - _prefix: str - _cmd_params: list - _longParam: str # region General diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 07982511d..29e16b63a 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -6,7 +6,7 @@ from fastapi import APIRouter, FastAPI, WebSocket, WebSocketDisconnect from library.src.configs import ServerConfig -from servers.chat.src.aggregates.channel import BrockerMessage +from servers.chat.src.abstractions.contract import BrockerMessage router = APIRouter() channels: dict[str, list[WebSocket]] = {"test": []} @@ -88,6 +88,7 @@ def getkey(request: GetKeyRequest): def get_udp_relay(request: GetUdpRelayRequest): pass + @router.post(f"{CHAT}/InviteHandler") def invite(request: InviteRequest): pass diff --git a/src/backends/tests/gamespy/chat/room_tests.py b/src/backends/tests/gamespy/chat/room_tests.py new file mode 100644 index 000000000..c7064d32e --- /dev/null +++ b/src/backends/tests/gamespy/chat/room_tests.py @@ -0,0 +1,22 @@ +# from typing import Optional +# import unittest + +# from library.tests.mock_objects import BrokerMock +# from servers.chat.src.aggregates.channel import Channel +# from servers.chat.src.aggregates.channel_user import ChannelUser +# from servers.chat.tests.mock_objects import create_client + + +# class RoomTests(unittest.TestCase): +# def test_peer_room(self): +# client = create_client() +# client.info.gamename = "test" +# client.info.previously_joined_channel = "stagging" +# client.info.nick_name = "unispy" +# channel = Channel("test", client, brocker_cls=BrokerMock) +# user = ChannelUser(client, channel) +# channel.add_bind_on_user_and_channel(user) +# pass + +# def test_single_join(self, user_name="unispy", nick_name="unispy", channel_name="#GSP!room!test"): +# pass diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index 228c1280b..b1ac1b689 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -113,6 +113,9 @@ def log_error(self, message: str) -> None: def log_network_sending(self, data: object) -> None: self.logger.info(f"{self._log_prefix} [send]: {data}") + def log_network_broadcast(self, data: object) -> None: + self.logger.info(f"{self._log_prefix} [cast]: {data}") + def log_network_receving(self, data: object) -> None: self.logger.info(f"{self._log_prefix} [recv]: {data}") diff --git a/src/library/tests/mock_objects.py b/src/library/tests/mock_objects.py index d99d0f839..24c5f7083 100644 --- a/src/library/tests/mock_objects.py +++ b/src/library/tests/mock_objects.py @@ -48,7 +48,7 @@ def subscribe(self): pass def publish_message(self, message): - print(f"[multi] {message}") + pass def unsubscribe(self): pass diff --git a/src/servers/chat/src/abstractions/contract.py b/src/servers/chat/src/abstractions/contract.py index a1f44a8e9..62c079d37 100644 --- a/src/servers/chat/src/abstractions/contract.py +++ b/src/servers/chat/src/abstractions/contract.py @@ -1,4 +1,7 @@ from typing import Optional +from uuid import UUID + +from pydantic import BaseModel import library.src.abstractions.contracts @@ -74,6 +77,15 @@ def __init__(self, request: RequestBase, result: Optional[ResultBase]) -> None: assert issubclass(type(result), ResultBase) assert issubclass(type(request), RequestBase) +# region Brocker + + +class BrockerMessage(BaseModel): + server_id: UUID + channel_name: str + sender_ip_end_point: str + message: str + if __name__ == "__main__": # Example usage: diff --git a/src/servers/chat/src/abstractions/handler.py b/src/servers/chat/src/abstractions/handler.py index 3bf020a10..39cc32416 100644 --- a/src/servers/chat/src/abstractions/handler.py +++ b/src/servers/chat/src/abstractions/handler.py @@ -1,12 +1,9 @@ from library.src.abstractions.brocker import BrockerBase from library.src.configs import CONFIG -from library.src.extentions.encoding import UniSpyJsonEncoder from library.src.network.brockers import WebsocketBrocker from servers.chat.src.aggregates.enums import MessageType from servers.chat.src.abstractions.contract import ResultBase -from typing import TYPE_CHECKING from servers.chat.src.aggregates.exceptions import ChatException, NoSuchNickException, NoSuchChannelException -from servers.chat.src.aggregates.managers import ChannelManager from servers.chat.src.abstractions.contract import RequestBase from servers.chat.src.abstractions.contract import * from library.src.abstractions.client import ClientBase @@ -16,12 +13,6 @@ import library.src.abstractions.handler from typing import cast -from servers.chat.src.applications.server_launcher import ServerLauncher - -if TYPE_CHECKING: - from servers.chat.src.aggregates.channel import Channel - from servers.chat.src.aggregates.channel_user import ChannelUser - class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase): _client: Client @@ -44,10 +35,6 @@ class PostLoginHandlerBase(CmdHandlerBase): pass -if TYPE_CHECKING: - from servers.chat.src.aggregates.channel import Channel - from servers.chat.src.aggregates.channel_user import ChannelUser - # region Channel @@ -82,7 +69,7 @@ class ChannelHandlerBase(PostLoginHandlerBase): _request: ChannelRequestBase _response: ResponseBase _result: ResultBase - _b_msg: dict + _b_msg: BrockerMessage """ broadcast message """ @@ -97,20 +84,18 @@ def _message_construct(self): """ broadcast message construct """ - self._b_msg = { - "server_id": self._client.server_config.server_id, - "sender_ip_end_point": self._client.connection.ip_endpoint, - "message": self._response.sending_buffer, - "channel_name": self._request.channel_name, - } + assert self._request.channel_name is not None + self._response.build() + self._b_msg = BrockerMessage(server_id=self._client.server_config.server_id, channel_name=self._request.channel_name, + sender_ip_end_point=self._client.connection.ip_endpoint, message=self._response.sending_buffer) def _publish_to_brocker(self): """ send message to backend, let backend to broadcast for us """ - import json self._message_construct() - j_str = json.dumps(self._b_msg, cls=UniSpyJsonEncoder) + j_str = self._b_msg.model_dump_json() + self._client.log_network_broadcast(j_str) ChannelHandlerBase._brocker.publish_message(j_str) def _response_send(self) -> None: @@ -151,7 +136,6 @@ class MessageResultBase(ResultBase): class MessageHandlerBase(ChannelHandlerBase): _request: MessageRequestBase _result: MessageResultBase - _receiver: "ChannelUser" def __init__(self, client: ClientBase, request: MessageRequestBase): assert isinstance(request, MessageRequestBase) diff --git a/src/servers/chat/src/aggregates/channel.py b/src/servers/chat/src/aggregates/channel.py deleted file mode 100644 index d67051d79..000000000 --- a/src/servers/chat/src/aggregates/channel.py +++ /dev/null @@ -1,200 +0,0 @@ -import datetime -from typing import Optional -from uuid import UUID - -from pydantic import BaseModel, field_validator -from library.src.abstractions.brocker import BrockerBase -from library.src.network.brockers import WebsocketBrocker -from library.src.configs import CONFIG -from servers.chat.src.abstractions.contract import ResponseBase -from servers.chat.src.aggregates.channel_user import ChannelUser -from servers.chat.src.aggregates.managers import KeyValueManager -from servers.chat.src.aggregates.peer_room import PeerRoom -from servers.chat.src.applications.client import Client -from servers.chat.src.contracts.requests import ModeRequest -from servers.chat.src.aggregates.enums import PeerRoomType -from servers.chat.src.aggregates.exceptions import ChatException -from servers.server_browser.src.v2.aggregations.server_info_builder import PEER_GROUP_LIST - -MIN_CHANNEL_NAME_LENGTH = 4 - - -class Channel: - """ - The channel class, every channel class manage a brocker - """ - server_id: UUID - game_name: str - name: str - max_num_user: int - create_time: datetime.datetime - kv_manager: KeyValueManager - room_type: PeerRoomType - password: Optional[str] - topic: Optional[str] - group_id: Optional[int] - room_name: Optional[str] - previously_join_channel: Optional[str] - ban_list: dict[str, ChannelUser] - users: dict[str, ChannelUser] - _creator_nick_name: str - - @property - def is_valid_peer_room(self) -> bool: - return self.group_id is not None and self.room_name is not None - - def __init__(self, name: str, client: Client, password: Optional[str] = None, brocker_cls: type[BrockerBase] = WebsocketBrocker) -> None: - # region channel init - self.server_id = client.server_config.server_id - self.name = name - self.password = password - self.game_name = client.info.gamename - self.previously_join_channel = client.info.previously_joined_channel - self.room_type = PeerRoom.get_room_type(name) - self.create_time = datetime.datetime.now() - self.topic = None - self.group_id = None - self.room_name = None - self.previously_join_channel = None - self.kv_manager = KeyValueManager() - self.max_num_user = 200 - # setup the message broker - self._broker = brocker_cls( - self.name, CONFIG.backend.url, self._get_message_from_brocker) - # channel user init - self._broker.subscribe() - self.ban_list = {} - self.users = {} - self._creator_nick_name = client.info.nick_name - - match self.room_type: - case PeerRoomType.Group: - self.get_group_id() - self.get_peer_room_name() - case PeerRoomType.Staging: - self.get_staging_room_name() - case PeerRoomType.Title: - self.get_title_room_name() - - def get_group_id(self): - group_id_str = self.name.split("!")[1] - try: - group_id = int(group_id_str) - except ValueError: - raise Exception("Peer room group id is incorrect") - self.group_id = group_id - - def get_peer_room_name(self): - if self.game_name in PEER_GROUP_LIST: - grouplist = PEER_GROUP_LIST[self.game_name] - room = next( - (g for g in grouplist if g["group_id"] == self.group_id), None) - if room is None: - raise Exception(f"Invalid peer room: {self.name}") - self.room_name = room["room_name"] - - def get_staging_room_name(self): - self.room_name = self.name.split("!")[-1] - - def get_title_room_name(self): - self.get_staging_room_name() - - @property - def creator(self) -> Optional[ChannelUser]: - if self._creator_nick_name in self.users: - return self.users[self._creator_nick_name] - else: - return None - - def _add_ban_user(self, request: ModeRequest): - assert isinstance(request, ModeRequest) - if request.nick_name not in self.users: - raise ChatException( - f"user:{request.nick_name} did not connected to this server" - ) - user = self.users[request.nick_name] - - self.ban_list[request.nick_name] = user - - def _remove_ban_user(self, nick_name: str): - if nick_name in self.ban_list: - del self.ban_list[nick_name] - - def _add_channel_operator(self, nick_name: str): - if nick_name not in self.users: - return - - user = self.users[nick_name] - if not user.is_channel_creator: - user.is_channel_creator = True - - def _remove_channel_operator(self, nick_name: str): - if nick_name not in self.users: - return - - user = self.users[nick_name] - user.is_channel_creator = False - - def _user_voice_permission(self, nick_name: str, enable: bool = True): - if nick_name not in self.users: - return - user = self.users[nick_name] - user.is_voiceable = enable - - def get_user_by_nick(self, nick_name: str) -> Optional[ChannelUser]: - if nick_name in self.users: - return self.users[nick_name] - return None - - def get_user_by_client(self, client: Client) -> Optional[ChannelUser]: - for user in self.users.values(): - if ( - client.connection.remote_ip == user.remote_ip - and client.connection.remote_port == user.remote_port - ): - return user - return None - - def add_bind_on_user_and_channel(self, joiner: ChannelUser): - if joiner.client.info.nick_name not in joiner.channel.users: - joiner.client.info.joined_channels[joiner.channel.name] = joiner.channel - - def remove_bind_on_user_and_channel(self, leaver: ChannelUser): - if leaver.client.info.nick_name in leaver.channel.users: - del leaver.channel.users[leaver.client.info.nick_name] - if leaver.channel.name in leaver.client.info.joined_channels: - del leaver.client.info.joined_channels[leaver.channel.name] - - def multicast(self, sender: Client, message: ResponseBase, is_skip_sender=False): - for nick, user in self.users.items(): - if is_skip_sender: - if sender.info.nick_name == nick: - continue - else: - user.client.send(message) - - def _get_message_from_brocker(self, message: str): - """ - we directly send the message from brocker to all channel local user - """ - for nick, user in self.users.items(): - user.client.connection.send(message.encode()) - - def remove_user(self, user: ChannelUser): - user.client.info.previously_joined_channel - - -class BrockerMessage(BaseModel): - channel_name: str - message: str - - @field_validator("channel_name") - def validate_channel_name(cls, value): - if value is None or len(value) < 3: - raise ValueError("channel name is not valid") - return value - - @field_validator("message") - def validate_message(cls, value): - if value is None or len(value) < 3: - raise ValueError("message length is not valid") diff --git a/src/servers/chat/src/aggregates/channel_user.py b/src/servers/chat/src/aggregates/channel_user.py deleted file mode 100644 index 1bb1db732..000000000 --- a/src/servers/chat/src/aggregates/channel_user.py +++ /dev/null @@ -1,36 +0,0 @@ -from uuid import UUID - -from servers.chat.src.aggregates.managers import KeyValueManager -from servers.chat.src.applications.client import Client -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from servers.chat.src.aggregates.channel import Channel - - -class ChannelUser: - server_id: UUID - is_voiceable: bool = True - is_channel_operator: bool = False - is_channel_creator: bool = False - remote_ip: str - remote_port: int - client: Client - kv_manager: KeyValueManager = KeyValueManager() - channel: "Channel" - - @property - def modes(self): - buffer = "" - if self.is_channel_creator: - buffer += "@" - if self.is_voiceable: - buffer += "+" - return buffer - - def __init__(self, client: Client, channel: "Channel") -> None: - self.client = client - self.channel = channel - self.server_id = client.server_config.server_id - self.remote_ip = client.connection.remote_ip - self.remote_port = client.connection.remote_port diff --git a/src/servers/chat/src/aggregates/managers.py b/src/servers/chat/src/aggregates/managers.py deleted file mode 100644 index 14fc78466..000000000 --- a/src/servers/chat/src/aggregates/managers.py +++ /dev/null @@ -1,60 +0,0 @@ -from typing import TYPE_CHECKING, Optional - - -class KeyValueManager: - data: dict - """ - store the key and values - """ - - def __init__(self): - self.data = {} - - def update(self, data: dict): - for key, value in data.items(): - self.data[key] = value - - def build_key_value_string(self, key_values: dict): - flags = "" - for key, value in key_values.items(): - flags += f"\\{key}\\{value}" - return flags - - def get_value_string(self, keys: list[str]) -> str: - values = "" - for key in keys: - if key in self.data: - values += f"\\{self.data[key]}" - else: - values += "\\" - # Uncomment the line below to raise an exception if key is not found - # raise Exception(f"Can not find key: {key}") - return values - - def is_contain_all_key(self, keys: list[str]): - return all(key in self.data for key in keys) - - -if TYPE_CHECKING: - from servers.chat.src.aggregates.channel import Channel - - -class ChannelManager: - local_channels: dict = {} - """The code blow is for channel manage""" - - @staticmethod - def get_channel(name: str) -> Optional["Channel"]: - if name in ChannelManager.local_channels: - return ChannelManager.local_channels[name] - return None - - @staticmethod - def add_channel(channel: "Channel"): - if channel.name not in ChannelManager.local_channels: - ChannelManager.local_channels[channel.name] = channel - - @staticmethod - def remove_channel(name: str) -> None: - if name in ChannelManager.local_channels: - del ChannelManager.local_channels[name] diff --git a/src/servers/chat/src/applications/client.py b/src/servers/chat/src/applications/client.py index f6126a3a8..2a9b5f7bd 100644 --- a/src/servers/chat/src/applications/client.py +++ b/src/servers/chat/src/applications/client.py @@ -6,24 +6,22 @@ from library.src.configs import ServerConfig from typing import TYPE_CHECKING, Optional -if TYPE_CHECKING: - from servers.chat.src.aggregates.channel import Channel class ClientInfo: previously_joined_channel: Optional[str] - joined_channels: dict[str, "Channel"] + joined_channels: list[str] nick_name: Optional[str] gamename: Optional[str] user_name: Optional[str] def __init__(self) -> None: - self.joined_channels = {} + self.joined_channels = [] self.nick_name = None self.gamename = None self.user_name = None self.previously_joined_channel = None - + class Client(ClientBase): info: ClientInfo diff --git a/src/servers/chat/src/applications/handlers.py b/src/servers/chat/src/applications/handlers.py index 2b1ee0072..52a0a0c49 100644 --- a/src/servers/chat/src/applications/handlers.py +++ b/src/servers/chat/src/applications/handlers.py @@ -79,12 +79,8 @@ WhoRequest, GetUdpRelayRequest ) -from servers.chat.src.aggregates.exceptions import ChatException from servers.chat.src.aggregates.enums import ModeRequestType, TopicRequestType from servers.chat.src.aggregates.response_name import * -from servers.chat.src.aggregates.managers import ChannelManager -from servers.chat.src.aggregates.channel_user import ChannelUser -from servers.chat.src.aggregates.channel import Channel from typing import Type from library.src.abstractions.client import ClientBase from servers.chat.src.abstractions.contract import RequestBase @@ -126,6 +122,7 @@ class GetKeyHandler(CmdHandlerBase): def __init__(self, client: ClientBase, request: GetKeyRequest): assert isinstance(request, GetKeyRequest) super().__init__(client, request) + self._result_cls = GetKeyResult def _response_construct(self) -> None: self._response = GetKeyResponse(self._request, self._result) @@ -244,6 +241,7 @@ class WhoHandler(CmdHandlerBase): def __init__(self, client: ClientBase, request: WhoRequest): assert isinstance(request, WhoRequest) super().__init__(client, request) + self._result_cls = WhoResult def _response_construct(self) -> None: self._response = WhoResponse(self._request, self._result) @@ -286,6 +284,7 @@ class GetCKeyHandler(ChannelHandlerBase): def __init__(self, client: ClientBase, request: GetCKeyRequest): assert isinstance(request, GetCKeyRequest) super().__init__(client, request) + self._result_cls = GetCKeyResult def _response_construct(self): self._response = GetCKeyResponse(self._request, self._result) @@ -356,6 +355,7 @@ class PartHandler(ChannelHandlerBase): def __init__(self, client: ClientBase, request: PartRequest): assert isinstance(request, PartRequest) super().__init__(client, request) + self._result_cls = PartResult def _response_construct(self): self._response = PartResponse(self._request, self._result) @@ -366,8 +366,9 @@ class SetChannelKeyHandler(ChannelHandlerBase): _result: SetChannelKeyResult def __init__(self, client: ClientBase, request: SetChannelKeyRequest): - assert isinstance(self._request, SetChannelKeyRequest) + assert isinstance(request, SetChannelKeyRequest) super().__init__(client, request) + self._result_cls = SetChannelKeyResult def _response_construct(self): self._response = SetChannelKeyResponse(self._request, self._result) @@ -379,6 +380,7 @@ class SetCKeyHandler(ChannelHandlerBase): def __init__(self, client: ClientBase, request: SetCKeyRequest): assert isinstance(request, SetCKeyRequest) super().__init__(client, request) + self._is_fetching = False def _response_construct(self) -> None: self._response = SetCKeyResponse(self._request) @@ -391,6 +393,7 @@ class TopicHandler(ChannelHandlerBase): def __init__(self, client: ClientBase, request: TopicRequest): assert isinstance(request, TopicRequest) super().__init__(client, request) + self._result_cls = TopicResult def _response_construct(self) -> None: self._response = TopicResponse(self._request, self._result) @@ -423,6 +426,7 @@ class UTMHandler(MessageHandlerBase): def __init__(self, client: ClientBase, request: UTMRequest): assert isinstance(request, UTMRequest) super().__init__(client, request) + self._result_cls = UTMResult def _response_construct(self) -> None: self._response = UTMResponse(self._request, self._result) @@ -435,6 +439,7 @@ class NoticeHandler(MessageHandlerBase): def __init__(self, client: ClientBase, request: NoticeRequest): assert isinstance(request, NoticeRequest) super().__init__(client, request) + self._result_cls = NoticeResult def _response_construct(self) -> None: self._response = NoticeResponse(self._request, self._result) @@ -447,6 +452,7 @@ class PrivateHandler(MessageHandlerBase): def __init__(self, client: ClientBase, request: PrivateRequest): assert isinstance(request, PrivateRequest) super().__init__(client, request) + self._result_cls = PrivateResult def _response_construct(self) -> None: self._response = PrivateResponse(self._request, self._result) diff --git a/src/servers/chat/src/applications/switcher.py b/src/servers/chat/src/applications/switcher.py index 7089c207b..f26319ba8 100644 --- a/src/servers/chat/src/applications/switcher.py +++ b/src/servers/chat/src/applications/switcher.py @@ -72,7 +72,8 @@ def __init__(self, client: ClientBase, raw_request: str) -> None: super().__init__(client, raw_request) def _process_raw_request(self) -> None: - splited_raw_requests = self._raw_request.replace("\r", "").split("\n") + splited_raw_requests = [ + req for req in self._raw_request.replace("\r", "").split("\n") if req] for raw_request in splited_raw_requests: name = raw_request.strip(" ").split(" ")[0] if name not in RequestType: diff --git a/src/servers/chat/src/contracts/requests.py b/src/servers/chat/src/contracts/requests.py index 483ad2cd4..202f2644b 100644 --- a/src/servers/chat/src/contracts/requests.py +++ b/src/servers/chat/src/contracts/requests.py @@ -615,8 +615,12 @@ def parse(self) -> None: self.nick_name = self._cmd_params[1] self.key_value_string = self._long_param[1:] self.key_values = convert_kvstring_to_dictionary(self.key_value_string) - - if "b_" in self.key_values: + is_broadcast = False + for key in self.key_values: + if "b_" in key: + is_broadcast = True + break + if is_broadcast: self.cookie = "BCAST" self.is_broadcast = True diff --git a/src/servers/chat/src/contracts/responses.py b/src/servers/chat/src/contracts/responses.py index 60f0ca2b4..d2bcd51d1 100644 --- a/src/servers/chat/src/contracts/responses.py +++ b/src/servers/chat/src/contracts/responses.py @@ -190,10 +190,10 @@ def build(self): self.sending_buffer += f":{SERVER_DOMAIN} {WHO_REPLY} * {channel_name} {user_name} {public_ip_address} * {nick_name} {modes} *\r\n" # noqa if self._request.request_type == WhoRequestType.GET_CHANNEL_USER_INFO: - if len(self._result.infos) > 0: + # if len(self._result.infos) > 0: self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.channel_name} * :End of WHO.\r\n" # noqa elif self._request.request_type == WhoRequestType.GET_USER_INFO: - if len(self._result.infos) > 0: + # if len(self._result.infos) > 0: self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.nick_name} * :End of WHO.\r\n" # noqa diff --git a/src/servers/chat/src/contracts/results.py b/src/servers/chat/src/contracts/results.py index f33dd57cb..30b21a361 100644 --- a/src/servers/chat/src/contracts/results.py +++ b/src/servers/chat/src/contracts/results.py @@ -13,7 +13,7 @@ class CryptResult(ResultBase): class GetKeyResult(ResultBase): nick_name: str - values: list = [] + values: list class ListResult(ResultBase): diff --git a/src/servers/chat/tests/game_tests.py b/src/servers/chat/tests/game_tests.py index abe5560cc..bdac81545 100644 --- a/src/servers/chat/tests/game_tests.py +++ b/src/servers/chat/tests/game_tests.py @@ -59,6 +59,9 @@ def test_worm3d(self): "UTM #GSP!worms3!Ml4lz344lM :SDM ASFE.Scheme.StandardCUnAACADCBBCACBBFFBKBB8C/C3C!A!A*C*C Client: leaver_irc_prefix="test_prefix", is_channel_creator=False, channel_name="test_chan").model_dump()) create_mock_url(config, NickHandler, NickResult( nick_name="test").model_dump()) + create_mock_url(config, WhoHandler, WhoResult(infos=[]).model_dump()) + create_mock_url(config, SetChannelKeyHandler, SetChannelKeyResult( + channel_user_irc_prefix="unispy!unispy@unispyserver", channel_name="test").model_dump()) + create_mock_url(config, GetKeyHandler, GetKeyResult( + nick_name="unispy", values=[]).model_dump()) + create_mock_url(config, UTMHandler, UTMResult( + user_irc_prefix="unispy!unispy@unispy", target_name="spyguy").model_dump()) + ChannelHandlerBase._brocker = BrokerMock() if TYPE_CHECKING: conn._client = cast(Client, conn._client) diff --git a/src/servers/chat/tests/room_tests.py b/src/servers/chat/tests/room_tests.py deleted file mode 100644 index 6ec2d1a65..000000000 --- a/src/servers/chat/tests/room_tests.py +++ /dev/null @@ -1,22 +0,0 @@ -from typing import Optional -import unittest - -from library.tests.mock_objects import BrokerMock -from servers.chat.src.aggregates.channel import Channel -from servers.chat.src.aggregates.channel_user import ChannelUser -from servers.chat.tests.mock_objects import create_client - - -class RoomTests(unittest.TestCase): - def test_peer_room(self): - client = create_client() - client.info.gamename = "test" - client.info.previously_joined_channel = "stagging" - client.info.nick_name = "unispy" - channel = Channel("test", client, brocker_cls=BrokerMock) - user = ChannelUser(client, channel) - channel.add_bind_on_user_and_channel(user) - pass - - def test_single_join(self, user_name="unispy", nick_name="unispy", channel_name="#GSP!room!test"): - pass From 75b2f45bfb3d37e1e8dea59f2b645b74178a4332 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sun, 10 Nov 2024 09:37:13 +0800 Subject: [PATCH 141/231] Update README.md Added how to run in markdown --- .github/README.md | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/.github/README.md b/.github/README.md index 886ef1f04..e60ac2f6e 100644 --- a/.github/README.md +++ b/.github/README.md @@ -31,5 +31,10 @@ This project is licensed under the [GNU Affero General Public License v3.0](../L ## How to run -* Run ```docker compose up -f docker-compose-unispy-env.yml -d``` -* Use vscode to open the src folder and reopen in devcontainer (make sure your vscode have devcontainer extension) \ No newline at end of file +* Run ```docker compose up -f docker-compose-unispy-env.yml -d``` to setup postgresql and redis +* Run ```export UNISPY_CONFIG=''``` to setup env config file path, remember replace symbol `````` with config file path +* Open with vscode: + * Use vscode to open the src folder and reopen in devcontainer (make sure your vscode have devcontainer extension) +* open with github codespace: + * add ```unispy_config = ""``` after the line of ```unispy_config = os.environ.get("UNISPY_CONFIG")``` in config.py (for unittest working) + * github codespace vscode web can not install devcontainer, the project can not running in docker network ```unispy```. Therefore, replace ```"server": "unispy_postgresql"``` and ```"server": "unispy_redis"``` to ```"server": "localhost"``` in config.json to use localhost postgres and redis in docker (we already setup) From 7f158bb5f3c797724e94d5f36a281cc29153cc3c Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 12 Nov 2024 07:11:37 +0000 Subject: [PATCH 142/231] refactor: added backends handlers --- .../library/abstractions/contracts.py | 6 +- .../library/abstractions/handler_base.py | 35 +++-- src/backends/library/database/pg_orm.py | 147 ++++++++++-------- src/backends/protocols/gamespy/chat/data.py | 55 ++++++- .../protocols/gamespy/chat/handlers.py | 108 ++++++++++++- .../protocols/gamespy/game_status/data.py | 16 +- .../protocols/gamespy/natneg/handlers.py | 2 +- .../presence_connection_manager/data.py | 39 +++++ .../gamespy/presence_search_player/data.py | 114 +++++++++++++- .../presence_search_player/handlers.py | 96 ++++++++++++ .../presence_search_player/requests.py | 2 + .../protocols/gamespy/query_report/data.py | 2 + .../gamespy/query_report/handlers.py | 68 ++++++++ .../gamespy/query_report/requests.py | 1 + src/backends/routers/gamespy/query_report.py | 7 +- src/library/src/abstractions/handler.py | 11 +- src/library/tests/mock_objects.py | 1 - src/servers/chat/src/abstractions/contract.py | 3 +- src/servers/chat/src/abstractions/handler.py | 7 +- .../query_report/src/v2/contracts/results.py | 12 +- .../v2/aggregations/server_info_builder.py | 8 +- 21 files changed, 627 insertions(+), 113 deletions(-) create mode 100644 src/backends/protocols/gamespy/presence_search_player/handlers.py create mode 100644 src/backends/protocols/gamespy/query_report/handlers.py diff --git a/src/backends/library/abstractions/contracts.py b/src/backends/library/abstractions/contracts.py index bbffab6fd..4f1fabd08 100644 --- a/src/backends/library/abstractions/contracts.py +++ b/src/backends/library/abstractions/contracts.py @@ -11,10 +11,8 @@ class RequestBase(BaseModel): """ if the raw_request is bytes, we decode it to decode("ascii","backslashreplace") str """ - client_ip_endpoint: str - """ - ip:port - """ + client_ip: str + client_port: int class ErrorResponse(BaseModel): diff --git a/src/backends/library/abstractions/handler_base.py b/src/backends/library/abstractions/handler_base.py index 0a6118b04..33a2897b8 100644 --- a/src/backends/library/abstractions/handler_base.py +++ b/src/backends/library/abstractions/handler_base.py @@ -1,5 +1,9 @@ from abc import abstractmethod, ABC +from typing import Optional, final + +from pydantic import BaseModel from backends.library.abstractions.contracts import ErrorResponse, RequestBase +from backends.library.database.pg_orm import PG_SESSION from library.src.abstractions.contracts import ResultBase import logging @@ -7,13 +11,17 @@ from library.src.exceptions.general import UniSpyException +class OkResponse(BaseModel): + message: str = "ok" + + class HandlerBase: """ The ultimate handler base of backend service """ _request: RequestBase - _result: ResultBase - response: dict + _result: Optional[ResultBase] + response: Optional[dict] """ the dict response which send to client """ @@ -23,12 +31,15 @@ def __init__(self, request: RequestBase) -> None: self._request = request # decoupling the logging in home.py self.logger = logging.getLogger("backend") + self._result = None + self.response = None async def handle(self) -> None: try: await self._request_check() - await self._data_fetch() + await self._data_operate() await self._result_construct() + await self._response_construct() except UniSpyException as ex: self.logger.error(ex.message) self.response = ErrorResponse(message=ex.message).model_dump() @@ -36,14 +47,18 @@ async def handle(self) -> None: self.logger.error(ex) self.response = ErrorResponse(message=str(ex)).model_dump() - @abstractmethod async def _request_check(self) -> None: - pass + """virtual method""" - @abstractmethod - async def _data_fetch(self) -> None: - pass + async def _data_operate(self) -> None: + """virtual method""" - @abstractmethod async def _result_construct(self) -> None: - pass + """virtual method""" + @final + async def _response_construct(self) -> None: + # if there are no result, we send ok response + if self._result is None: + self.response = OkResponse().model_dump() + else: + self.response = self._result.model_dump() diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index d50e2d1b1..6b50f600f 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -30,63 +30,68 @@ class Users(Base): __tablename__ = "users" - userid = Column(Integer, primary_key=True, autoincrement=True) - email = Column(String, nullable=False) - password = Column(String, nullable=False) - emailverified = Column(Boolean, default=True, nullable=False) - lastip = Column(INET) - lastonline = Column(DateTime, default=datetime.now()) - createddate = Column(DateTime, default=datetime.now(), nullable=False) - banned = Column(Boolean, default=False, nullable=False) - deleted = Column(Boolean, default=False, nullable=False) + userid: Column | int = Column( + Integer, primary_key=True, autoincrement=True) + email: Column | str = Column(String, nullable=False) + password: Column | str = Column(String, nullable=False) + emailverified: Column | bool = Column( + Boolean, default=True, nullable=False) + lastip: Column | str = Column(INET) + lastonline: Column | datetime = Column(DateTime, default=datetime.now()) + createddate: Column | datetime = Column( + DateTime, default=datetime.now(), nullable=False) + banned: Column | bool = Column(Boolean, default=False, nullable=False) + deleted: Column | bool = Column(Boolean, default=False, nullable=False) class Profiles(Base): __tablename__ = "profiles" - profileid = Column(Integer, primary_key=True, autoincrement=True) - userid = Column(Integer, ForeignKey("users.userid"), nullable=False) - nick = Column(String, nullable=False) - serverflag = Column(Integer, nullable=False, default=0) - status = Column(SmallInteger, default=0) - statstring = Column(String, default="I love UniSpy") - location = Column(String) - firstname = Column(String) - lastname = Column(String) - publicmask = Column(Integer, default=0) - latitude = Column(Double, default=0) - longitude = Column(Double, default=0) - aim = Column(String, default="") - picture = Column(Integer, default=0) - occupationid = Column(Integer, default=0) - incomeid = Column(Integer, default=0) - industryid = Column(Integer, default=0) - marriedid = Column(Integer, default=0) - childcount = Column(Integer, default=0) - interests1 = Column(Integer, default=0) - ownership1 = Column(Integer, default=0) - connectiontype = Column(Integer, default=0) - sex = Column(SmallInteger, default=0) - zipcode = Column(String, default="00000") - countrycode = Column(String, default="1") - homepage = Column(String, default="unispy.org") - birthday = Column(Integer, default=0) - birthmonth = Column(Integer, default=0) - birthyear = Column(Integer, default=0) - icquin = Column(Integer, default=0) - quietflags = Column(SmallInteger, nullable=False, default=0) - streetaddr = Column(Text) - streeaddr = Column(Text) - city = Column(Text) - cpubrandid = Column(Integer, default=0) - cpuspeed = Column(Integer, default=0) - memory = Column(SmallInteger, default=0) - videocard1string = Column(Text) - videocard1ram = Column(SmallInteger, default=0) - videocard2string = Column(Text) - videocard2ram = Column(SmallInteger, default=0) - subscription = Column(Integer, default=0) - adminrights = Column(Integer, default=0) + profileid: Column | int = Column( + Integer, primary_key=True, autoincrement=True) + userid: Column | int = Column( + Integer, ForeignKey("users.userid"), nullable=False) + nick: Column | str = Column(String, nullable=False) + serverflag: Column | int = Column(Integer, nullable=False, default=0) + status: Column | int = Column(SmallInteger, default=0) + statstring: Column | str = Column(String, default="I love UniSpy") + location: Column | str = Column(String) + firstname: Column | str = Column(String) + lastname: Column | str = Column(String) + publicmask: Column | int = Column(Integer, default=0) + latitude: Column | float = Column(Double, default=0) + longitude: Column | float = Column(Double, default=0) + aim: Column | str = Column(String, default="") + picture: Column | int = Column(Integer, default=0) + occupationid: Column | int = Column(Integer, default=0) + incomeid: Column | int = Column(Integer, default=0) + industryid: Column | int = Column(Integer, default=0) + marriedid: Column | int = Column(Integer, default=0) + childcount: Column | int = Column(Integer, default=0) + interests1: Column | int = Column(Integer, default=0) + ownership1: Column | int = Column(Integer, default=0) + connectiontype: Column | int = Column(Integer, default=0) + sex: Column | int = Column(SmallInteger, default=0) + zipcode: Column | str = Column(String, default="00000") + countrycode: Column | str = Column(String, default="1") + homepage: Column | str = Column(String, default="unispy.org") + birthday: Column | int = Column(Integer, default=0) + birthmonth: Column | int = Column(Integer, default=0) + birthyear: Column | int = Column(Integer, default=0) + icquin: Column | int = Column(Integer, default=0) + quietflags: Column | int = Column(SmallInteger, nullable=False, default=0) + streetaddr: Column | str = Column(Text) + streeaddr: Column | str = Column(Text) + city: Column | str = Column(Text) + cpubrandid: Column | int = Column(Integer, default=0) + cpuspeed: Column | int = Column(Integer, default=0) + memory: Column | int = Column(SmallInteger, default=0) + videocard1string: Column | str = Column(Text) + videocard1ram: Column | int = Column(SmallInteger, default=0) + videocard2string: Column | str = Column(Text) + videocard2ram: Column | int = Column(SmallInteger, default=0) + subscription: Column | int = Column(Integer, default=0) + adminrights: Column | int = Column(Integer, default=0) class SubProfiles(Base): @@ -95,16 +100,16 @@ class SubProfiles(Base): subprofileid = Column( Integer, ForeignKey("profiles.profileid"), primary_key=True, autoincrement=True ) - profileid = Column(Integer, nullable=False) - uniquenick = Column(String) - namespaceid = Column(Integer, nullable=False, default=0) - partnerid = Column(Integer, nullable=False, default=0) - productid = Column(Integer) - gamename = Column(Text) - cdkeyenc = Column(String) - firewall = Column(SmallInteger, default=0) - port = Column(Integer, default=0) - authtoken = Column(String) + profileid: Column | int = Column(Integer, nullable=False) + uniquenick: Column | str = Column(String) + namespaceid: Column | int = Column(Integer, nullable=False, default=0) + partnerid: Column | int = Column(Integer, nullable=False, default=0) + productid: Column | int = Column(Integer) + gamename: Column | str = Column(Text) + cdkeyenc: Column | str = Column(String) + firewall: Column | int = Column(SmallInteger, default=0) + port: Column | int = Column(Integer, default=0) + authtoken: Column | str = Column(String) class AddRequest(Base): @@ -240,11 +245,24 @@ class ChatChannelCaches(Base): server_id = Column(UUID, nullable=False) game_name = Column(String, nullable=False) room_name = Column(String, nullable=False) - topic = Column(String, nullable=False) + topic = Column(String, nullable=True) password = Column(String, nullable=True) group_id = Column(Integer, nullable=False) max_num_user = Column(Integer, nullable=False) key_values = Column(JSONB) + invited_nicks = Column(JSONB) + update_time = Column(DateTime, nullable=False) + + +class ChatNickCaches(Base): + __tablename__ = "chat_nick_caches" + server_id = Column(UUID, nullable=False) + nick_name = Column(String, primary_key=True, nullable=False) + game_name = Column(String, nullable=True) + user_name = Column(String, nullable=True) + remote_ip_address = Column(INET, nullable=False) + remote_port = Column(Integer, nullable=False) + key_value = Column(JSONB) update_time = Column(DateTime, nullable=False) @@ -276,6 +294,7 @@ class GameServerCaches(Base): player_data = Column(JSONB, nullable=False) server_data = Column(JSONB, nullable=False) team_data = Column(JSONB, nullable=False) + avaliable = Column(Boolean, nullable=True) def connect_to_db() -> Session: @@ -291,5 +310,5 @@ def connect_to_db() -> Session: if __name__ == "__main__": session = connect_to_db() - session.query(Users.userid == 0) + session.query(Users.userid == 0) # type:ignore pass diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index 484f17538..5f862b7be 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -1,14 +1,51 @@ from datetime import datetime, timedelta from typing import TYPE_CHECKING, cast -from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatUserCaches, Users, Profiles, SubProfiles + +from sqlalchemy import Column +from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatNickCaches, ChatUserCaches, Users, Profiles, SubProfiles from servers.chat.src.aggregates.exceptions import ChatException +def is_cdkey_valid(cdkey: str) -> bool: + if TYPE_CHECKING: + assert isinstance(SubProfiles.cdkeyenc, Column) + result = PG_SESSION.query(SubProfiles).where( + SubProfiles.cdkeyenc == cdkey).count() + if result == 0: + return False + + else: + return True + + +def is_nick_exist(nick_name: str) -> bool: + c = PG_SESSION.query(ChatNickCaches.nick_name).count() + if c == 1: + return True + else: + return False + + +def add_nick_cache(cache: ChatNickCaches): + PG_SESSION.add(cache) + PG_SESSION.commit() + + def nick_and_email_login(nick_name: str, email: str, password_hash: str) -> tuple[int, int, bool, bool]: """ return userid, profileid, emailverified, banned """ + if TYPE_CHECKING: + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(Users.userid, Column) + assert isinstance(Users.email, Column) + assert isinstance(Profiles.nick, Column) + assert isinstance(Users.emailverified, Column) + assert isinstance(Users.banned, Column) + assert isinstance(Users.password, Column) + result = PG_SESSION.query(Users.userid, Profiles.profileid, Users.emailverified, Users.banned).join(Profiles, (Users.userid == Profiles.userid)).where( Users.email == email, @@ -29,6 +66,17 @@ def uniquenick_login(uniquenick:str,namespace_id:int)-> tuple[int, int, bool, bo return userid, profileid, emailverified, banned """ + if TYPE_CHECKING: + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(Users.userid, Column) + assert isinstance(Users.email, Column) + assert isinstance(Profiles.nick, Column) + assert isinstance(Users.emailverified, Column) + assert isinstance(Users.banned, Column) + assert isinstance(Users.password, Column) + assert isinstance(SubProfiles.namespaceid, Column) + assert isinstance(SubProfiles.uniquenick ,Column) result = PG_SESSION.query(Users.userid, Profiles.profileid,Users.emailverified, Users.banned).join(Profiles,(Users.userid == Profiles.userid)).join(Profiles,(Profiles.profileid == SubProfiles.profileid)).where(SubProfiles.namespaceid == namespace_id,SubProfiles.uniquenick == uniquenick).first() if result is None: # fmt: off @@ -79,8 +127,9 @@ def remove_user(cache:ChatUserCaches): PG_SESSION.delete(cache) PG_SESSION.commit() -def is_user_exist(nick_name:str)->bool: - user_count= PG_SESSION.query(ChatUserCaches).filter(ChatUserCaches.nick_name ==nick_name).count() +def is_user_exist(ip:str,port:int)->bool: + user_count= PG_SESSION.query(ChatUserCaches).filter(ChatUserCaches.remote_ip_address==ip, + ChatUserCaches.remote_port==port).count() if user_count ==1: return True else: diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index 9b61dd42e..ed7b1c398 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -1,11 +1,107 @@ +from datetime import datetime +from typing import TYPE_CHECKING, cast +from backends.library.abstractions.contracts import RequestBase from backends.library.abstractions.handler_base import HandlerBase +from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatNickCaches, ChatUserCaches import backends.protocols.gamespy.chat.data as data -from backends.protocols.gamespy.chat.requests import JoinRequest +from backends.protocols.gamespy.chat.requests import * +from servers.chat.src.aggregates.exceptions import LoginFailedException, NickNameInUseException, NoSuchChannelException, NoSuchNickException +from servers.chat.src.contracts.results import CryptResult, GetKeyResult, NickResult -class JoinHandler(HandlerBase): - _request: JoinRequest +class CdKeyHandler(HandlerBase): + _request: CdkeyRequest - async def _data_fetch(self) -> None: - data.is_channel_exist(self._request.channel_name, - self._request.game_name) + async def _data_operate(self) -> None: + is_valid = data.is_cdkey_valid(self._request.cdkey) + if not is_valid: + raise LoginFailedException("cdkey not matched") + + +class CryptHandler(HandlerBase): + _request: CryptRequest + + async def _data_operate(self) -> None: + result = PG_SESSION.query(ChatNickCaches).where(ChatNickCaches.remote_ip_address == + self._request.client_ip, ChatNickCaches.remote_port == self._request.client_port).first() + if result is None: + raise NoSuchNickException( + f"No nick found for {self._request.client_ip}") + result.game_name = self._request.gamename # type: ignore + PG_SESSION.commit() + + async def _result_construct(self) -> None: + self._result = CryptResult() + + +class GetKeyHandler(HandlerBase): + _request: GetKeyRequest + + async def _data_operate(self) -> None: + caches = PG_SESSION.query(ChatNickCaches.key_value).where( + ChatNickCaches.nick_name == self._request.nick_name).first() + + if caches is None: + raise NoSuchNickException("nick not found") + if TYPE_CHECKING: + self.caches = cast(list, caches) + + async def _result_construct(self) -> None: + self._result = GetKeyResult( + nick_name=self._request.nick_name, values=self.caches) + + +class GetUdpRelayHandler(HandlerBase): + _request: GetUdpRelayRequest + + def __init__(self, request: RequestBase) -> None: + super().__init__(request) + raise NotImplementedError() + + +class InviteHandler(HandlerBase): + _request: InviteRequest + + async def _data_operate(self) -> None: + chann = PG_SESSION.query(ChatChannelCaches).join(ChatUserCaches).where( + ChatUserCaches.channel_name == self._request.channel_name, ChatUserCaches.remote_ip_address == self._request.client_ip, ChatUserCaches.remote_port == self._request.client_port).first() + if chann is None: + raise NoSuchChannelException( + "you have to be in this channel to invite your friends") + + chann.invited_nicks.append(self._request.nick_name) + + +class NickHandler(HandlerBase): + _request: NickRequest + + async def _data_operate(self) -> None: + is_nick = data.is_nick_exist(self._request.nick_name) + if is_nick: + raise NickNameInUseException( + old_nick=self._request.nick_name, new_nick="", message="nick name in use") + else: + cache = ChatNickCaches(nick_name=self._request.nick_name, + server_id=self._request.server_id, + update_time=datetime.now()) + data.add_nick_cache(cache) + + async def _result_construct(self) -> None: + self._result = NickResult(nick_name=self._request.nick_name) + +# class JoinHandler(HandlerBase): +# _request: JoinRequest + +# async def _data_fetch(self) -> None: +# is_chan_exist = data.is_channel_exist(self._request.channel_name, +# self._request.game_name) +# # group_id = +# if is_chan_exist: +# is_user_exist = data.is_user_exist( +# self._request.client_ip, self._request.client_port) +# else: +# # create channel +# # create user +# is_peer_room = +# chan = ChatChannelCaches(channel_name=self._request.channel_name, server_id=self._request.server_id, game_name, room_name, topic, +# password=self._request.password, group_id, max_num_user=200, key_values=None, update_time=datetime.now()) diff --git a/src/backends/protocols/gamespy/game_status/data.py b/src/backends/protocols/gamespy/game_status/data.py index d1a8d8a8c..a9036ce13 100644 --- a/src/backends/protocols/gamespy/game_status/data.py +++ b/src/backends/protocols/gamespy/game_status/data.py @@ -1,5 +1,7 @@ from typing import TYPE_CHECKING, cast -from backends.library.database.pg_orm import PG_SESSION, PStorage, Profiles, SubProfiles + +from sqlalchemy import Column +from backends.library.database.pg_orm import PG_SESSION, PStorage, Profiles, SubProfiles, Users from servers.game_status.src.aggregations.enums import PersistStorageType from servers.game_status.src.aggregations.exceptions import GSException @@ -17,6 +19,10 @@ def update_player_data(): def get_profile_id_by_token(token: str) -> int: + if TYPE_CHECKING: + assert isinstance(SubProfiles.profileid, Column) + assert isinstance(SubProfiles.authtoken, Column) + result = PG_SESSION.query(SubProfiles.profileid).filter( SubProfiles.authtoken == token).first() if result is None: @@ -27,6 +33,14 @@ def get_profile_id_by_token(token: str) -> int: def get_profile_id(cdkey: str, nick_name: str) -> int: + if TYPE_CHECKING: + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(Users.userid, Column) + assert isinstance(Users.email, Column) + assert isinstance(Profiles.nick, Column) + assert isinstance(SubProfiles.profileid, Column) + assert isinstance(SubProfiles.cdkeyenc, Column) result = PG_SESSION.query(SubProfiles.profileid).join( SubProfiles, Profiles.profileid == SubProfiles.profileid)\ .filter(SubProfiles.cdkeyenc == cdkey, diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py index 4b520df94..673b37207 100644 --- a/src/backends/protocols/gamespy/natneg/handlers.py +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -9,7 +9,7 @@ def __init__(self, request: InitRequest) -> None: super().__init__(request) assert isinstance(request, InitRequest) - async def _data_fetch(self) -> None: + async def _data_operate(self) -> None: info = InitPacketCaches(**self._request.model_dump()) update_init_info(info) diff --git a/src/backends/protocols/gamespy/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py index 840f7b01e..949f5f9d0 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/data.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/data.py @@ -1,4 +1,6 @@ from typing import TYPE_CHECKING, cast + +from sqlalchemy import Column from backends.library.database.pg_orm import ( Blocked, Friends, @@ -11,6 +13,8 @@ def is_email_exist(email: str) -> bool: + if TYPE_CHECKING: + assert isinstance(Users.email, Column) if PG_SESSION.query(Users).filter(Users.email == email).count() == 1: return True else: @@ -62,6 +66,10 @@ def get_profile_info_list(profile_id: int, namespace_id: int): Returns: tuple: A tuple containing the profile information, sub-profile information, and user information. """ + if TYPE_CHECKING: + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(SubProfiles.namespaceid, Column) result = ( PG_SESSION.query(Profiles, SubProfiles, Users) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) @@ -86,6 +94,13 @@ def get_user_info_list(email: str, nick_name: str) -> list[tuple[int, int, int]] List[Tuple[int, int, int]]: A list of tuples containing the userid, profileid, and subprofileid of users that match the provided email and nickname in the database. """ + if TYPE_CHECKING: + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(Users.userid, Column) + assert isinstance(Users.email, Column) + assert isinstance(Profiles.nick, Column) + result = ( PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) @@ -100,6 +115,15 @@ def get_user_info_list(email: str, nick_name: str) -> list[tuple[int, int, int]] def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: + if TYPE_CHECKING: + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(Users.userid, Column) + assert isinstance(Users.email, Column) + assert isinstance(Profiles.nick, Column) + assert isinstance(SubProfiles.uniquenick, Column) + assert isinstance(SubProfiles.namespaceid, Column) + result = ( PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) @@ -117,6 +141,15 @@ def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: def get_user_infos(unique_nick: str, namespace_id: int) -> list[tuple[int, int, int]]: + if TYPE_CHECKING: + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(Users.userid, Column) + assert isinstance(Users.email, Column) + assert isinstance(Profiles.nick, Column) + assert isinstance(SubProfiles.uniquenick, Column) + assert isinstance(SubProfiles.namespaceid, Column) + result = ( PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) @@ -172,6 +205,12 @@ def add_nick_name(profile_id: int, old_nick: str, new_nick: str): assert isinstance(profile_id, int) assert isinstance(old_nick, str) assert isinstance(new_nick, str) + if TYPE_CHECKING: + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(Users.userid, Column) + assert isinstance(Users.email, Column) + assert isinstance(Profiles.nick, Column) result = ( PG_SESSION.query(Profiles) .filter(Profiles.profileid == profile_id, Profiles.nick == old_nick) diff --git a/src/backends/protocols/gamespy/presence_search_player/data.py b/src/backends/protocols/gamespy/presence_search_player/data.py index 145a91bd7..7cda46bd6 100644 --- a/src/backends/protocols/gamespy/presence_search_player/data.py +++ b/src/backends/protocols/gamespy/presence_search_player/data.py @@ -1,5 +1,5 @@ from typing import TYPE_CHECKING, Optional, cast -from sqlalchemy import insert +from sqlalchemy import Column from backends.library.database.pg_orm import ( Friends, Profiles, @@ -10,6 +10,9 @@ def verify_email(email: str): + if TYPE_CHECKING: + Users.email = cast(Column, Users.email) + Users.password = cast(Column, Users.password) if PG_SESSION.query(Users).filter(Users.email == email).count() == 1: return True else: @@ -17,6 +20,9 @@ def verify_email(email: str): def verify_email_and_password(email: str, password: str): + if TYPE_CHECKING: + assert isinstance(Users.email, Column) + assert isinstance(Users.password, Column) result = ( PG_SESSION.query(Users) .filter(Users.email == email, Users.password == password) @@ -27,9 +33,18 @@ def verify_email_and_password(email: str, password: str): return False -def get_profile_id(email: str, password: str, nick_name: str, partner_id: int): - result = ( - PG_SESSION.query(Profiles, SubProfiles, Users) +def get_profile_id(email: str, password: str, nick_name: str, partner_id: int) -> int: + if TYPE_CHECKING: + assert isinstance(Users.email, Column) + assert isinstance(Users.password, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(Users.userid, Column) + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.nick, Column) + assert isinstance(SubProfiles.partnerid, Column) + + pid = ( + PG_SESSION.query(Profiles.profileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .filter( @@ -40,8 +55,9 @@ def get_profile_id(email: str, password: str, nick_name: str, partner_id: int): ) .first() ) - - return result + if TYPE_CHECKING: + pid = cast(int, pid) + return pid def add_user(user: Users): @@ -75,11 +91,16 @@ def update_subprofile(subprofile: SubProfiles): def get_user(email: str): + if TYPE_CHECKING: + Users.email = cast(Column, Users.email) result = PG_SESSION.query(Users).filter(Users.email == email).first() return result def get_profile(user_id: int, nick_name: str) -> Profiles: + if TYPE_CHECKING: + Profiles.userid = cast(Column, Profiles.userid) + Profiles.nick = cast(Column, Profiles.nick) result = PG_SESSION.query(Profiles).filter( Profiles.userid == user_id, Profiles.nick == nick_name ).first() @@ -87,6 +108,10 @@ def get_profile(user_id: int, nick_name: str) -> Profiles: def get_sub_profile(profile_id: int, namespace_id: int, product_id: int) -> SubProfiles: + if TYPE_CHECKING: + assert isinstance(SubProfiles.profileid, Column) + assert isinstance(SubProfiles.namespaceid, Column) + assert isinstance(SubProfiles.namespaceid, Column) result = PG_SESSION.query(SubProfiles).filter( SubProfiles.profileid == profile_id, SubProfiles.namespaceid == namespace_id, @@ -95,10 +120,21 @@ def get_sub_profile(profile_id: int, namespace_id: int, product_id: int) -> SubP return result -def get_nick_and_unique_nick_list(email: str, password: str, namespace_id: int): +def get_nick_and_unique_nick_list(email: str, password: str, namespace_id: int) -> list[tuple[str, str]]: """ return [(nick, uniquenick)] """ + if TYPE_CHECKING: + assert isinstance(Profiles.nick, Column) + assert isinstance(SubProfiles.uniquenick, Column) + assert isinstance(Users.email, Column) + assert isinstance(Users.password, Column) + assert isinstance(SubProfiles.namespaceid, Column) + assert isinstance(Profiles.nick, Column) + assert isinstance(SubProfiles.uniquenick, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(Profiles.profileid, Column) + assert isinstance(SubProfiles.namespaceid, Column) result = ( PG_SESSION.query(Profiles.nick, SubProfiles.uniquenick) .join(Users, Profiles.userid == Users.userid) @@ -110,6 +146,8 @@ def get_nick_and_unique_nick_list(email: str, password: str, namespace_id: int): ) .all() ) + if TYPE_CHECKING: + result = cast(list, result) return result @@ -117,7 +155,19 @@ def get_friend_info_list(profile_id: int, namespace_id: int, game_name: str): """ return [(profileid, nick, uniquenick, lastname, firstname, userid, email)] """ - + if TYPE_CHECKING: + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.nick, Column) + assert isinstance(SubProfiles.uniquenick, Column) + assert isinstance(Profiles.lastname, Column) + assert isinstance(Profiles.firstname, Column) + assert isinstance(Users.userid, Column) + assert isinstance(Users.email, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(Profiles.profileid, Column) + assert isinstance(SubProfiles.namespaceid, Column) + assert isinstance(SubProfiles.gamename, Column) + assert isinstance(Friends.profileid, Column) result = ( PG_SESSION.query( Profiles.profileid, @@ -148,6 +198,10 @@ def get_matched_profile_info_list( return [(profileid,uniquenick)] """ + if TYPE_CHECKING: + assert isinstance(SubProfiles.profileid, Column) + assert isinstance(SubProfiles.uniquenick, Column) + assert isinstance(SubProfiles.namespaceid, Column) result = ( PG_SESSION.query(SubProfiles.profileid, SubProfiles.uniquenick) .filter( @@ -164,6 +218,15 @@ def get_matched_profile_info_list( def get_matched_info_by_nick( nick_name: str, ) -> Optional[list[tuple[int, str, str, str, str, int]]]: + if TYPE_CHECKING: + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.nick, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(Profiles.firstname, Column) + assert isinstance(Profiles.lastname, Column) + assert isinstance(SubProfiles.uniquenick, Column) + assert isinstance(SubProfiles.namespaceid, Column) + assert isinstance(Users.userid, Column) result = ( PG_SESSION.query( Profiles.profileid, @@ -187,6 +250,15 @@ def get_matched_info_by_nick( def get_matched_info_by_email( email: str, ) -> list[tuple[int, str, str, str, str, int]]: + if TYPE_CHECKING: + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.nick, Column) + assert isinstance(Profiles.firstname, Column) + assert isinstance(Profiles.lastname, Column) + assert isinstance(SubProfiles.uniquenick, Column) + assert isinstance(SubProfiles.namespaceid, Column) + assert isinstance(Users.email, Column) + assert isinstance(Profiles.userid, Column) result = ( PG_SESSION.query( Profiles.profileid, @@ -207,6 +279,15 @@ def get_matched_info_by_email( def get_matched_info_by_nick_and_email(nick_name: str, email: str): + if TYPE_CHECKING: + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.nick, Column) + assert isinstance(Profiles.firstname, Column) + assert isinstance(Profiles.lastname, Column) + assert isinstance(SubProfiles.uniquenick, Column) + assert isinstance(SubProfiles.namespaceid, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(Users.email, Column) result = ( PG_SESSION.query( Profiles.profileid, @@ -227,6 +308,15 @@ def get_matched_info_by_nick_and_email(nick_name: str, email: str): def get_matched_info_by_uniquenick_and_namespaceid( unique_nick: str, namespace_id: int ) -> list[tuple[int, str, str, str, str, int]]: + if TYPE_CHECKING: + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.nick, Column) + assert isinstance(Profiles.firstname, Column) + assert isinstance(Profiles.lastname, Column) + assert isinstance(SubProfiles.uniquenick, Column) + assert isinstance(SubProfiles.namespaceid, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(Users.email, Column) result = ( PG_SESSION.query( Profiles.profileid, @@ -250,6 +340,11 @@ def get_matched_info_by_uniquenick_and_namespaceid( def is_uniquenick_exist(unique_nick: str, namespace_id: int, game_name: str) -> bool: + if TYPE_CHECKING: + assert isinstance(Profiles.profileid, Column) + assert isinstance(SubProfiles.uniquenick, Column) + assert isinstance(SubProfiles.gamename, Column) + assert isinstance(SubProfiles.namespaceid, Column) result = ( PG_SESSION.query(Profiles) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) @@ -268,6 +363,9 @@ def is_uniquenick_exist(unique_nick: str, namespace_id: int, game_name: str) -> def is_email_exist(email: str) -> bool: + if TYPE_CHECKING: + Users.userid = cast(Column, Users.userid) + Users.email = cast(Column, Users.email) result = PG_SESSION.query(Users.userid).filter( Users.email == email).count() # According to game partnerid is not nessesary diff --git a/src/backends/protocols/gamespy/presence_search_player/handlers.py b/src/backends/protocols/gamespy/presence_search_player/handlers.py new file mode 100644 index 000000000..89efe1950 --- /dev/null +++ b/src/backends/protocols/gamespy/presence_search_player/handlers.py @@ -0,0 +1,96 @@ +from typing import TYPE_CHECKING, cast +from backends.library.abstractions.handler_base import HandlerBase +from backends.library.database.pg_orm import PG_SESSION, Users, Profiles, SubProfiles +import backends.protocols.gamespy.presence_search_player.data as data +from backends.protocols.gamespy.presence_search_player.requests import * +from servers.presence_search_player.src.aggregates.exceptions import CheckException +from servers.presence_search_player.src.contracts.results import CheckResult, NewUserResult, NickResultData, NicksResult + + +class CheckHandler(HandlerBase): + _request: CheckRequest + + async def _data_operate(self) -> None: + if data.verify_email(self._request.email): + raise CheckException("The email is not existed") + if data.verify_email_and_password(self._request.email, self._request.password): + raise CheckException("The password is incorrect") + profile_id = data.get_profile_id( + self._request.email, self._request.password, self._request.nick, self._request.partner_id) + + self._result = CheckResult(profile_id=profile_id) + + +class NewUserHandler(HandlerBase): + _request: NewUserRequest + + async def _data_operate(self) -> None: + + # check if user exist + self.user = data.get_user(self._request.email) + if self.user is None: + self._create_user() + + if TYPE_CHECKING: + assert self.user + self.user.userid = cast(int, self.user.userid) + + self.profile = data.get_profile(self.user.userid, self._request.nick) + if self.profile is None: + self._create_profile() + + if TYPE_CHECKING: + self.profile.profileid = cast(int, self.profile.profileid) + + self.subprofile = data.get_sub_profile( + profile_id=self.profile.profileid, namespace_id=self._request.namespace_id, product_id=self._request.product_id) + if self.subprofile is None: + self._create_subprofile() + + async def _result_construct(self) -> None: + assert self.user is not None + assert isinstance(self.user.userid, int) + assert self.profile is not None + assert isinstance(self.profile.profileid, int) + self._result = NewUserResult( + user_id=self.user.userid, profile_id=self.profile.profileid) + + def _create_user(self) -> None: + user_dict = {} + for key, value in self._request.__dict__.items(): + if key in Users.__dict__: + user_dict[key] = value + self.user = Users(**user_dict) + PG_SESSION.commit() + + def _create_profile(self) -> None: + + profile_dict = {} + for key, value in self._request.__dict__.items(): + if key in Profiles.__dict__: + profile_dict[key] = value + self.profile = Profiles(**profile_dict) + PG_SESSION.commit() + + def _create_subprofile(self) -> None: + subprofile_dict = {} + for key, value in self._request.__dict__.items(): + if key in SubProfiles.__dict__: + subprofile_dict[key] = value + self.subprofile = SubProfiles(**subprofile_dict) + PG_SESSION.commit() + + +class NicksHandler(HandlerBase): + _request: NicksRequest + + async def _data_operate(self) -> None: + self.temp_list = data.get_nick_and_unique_nick_list( + self._request.email, self._request.password, self._request.namespace_id) + self.result_data = [] + for nick, unique in self.temp_list: + self.result_data.append( + NickResultData(nick=nick, uniquenick=unique)) + + async def _result_construct(self) -> None: + self._result = NicksResult(data=self.result_data) diff --git a/src/backends/protocols/gamespy/presence_search_player/requests.py b/src/backends/protocols/gamespy/presence_search_player/requests.py index 8c18494d8..8ff4deb77 100644 --- a/src/backends/protocols/gamespy/presence_search_player/requests.py +++ b/src/backends/protocols/gamespy/presence_search_player/requests.py @@ -33,12 +33,14 @@ class NewUserRequest(RequestBase): partner_id: int game_name: str uniquenick: str + namespace_id: int class NicksRequest(RequestBase): password: str email: str is_require_uniquenicks: bool + namespace_id: int class OthersListRequest(RequestBase): diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index 68b65a67b..4f5963d0a 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -3,6 +3,7 @@ from servers.chat.src.aggregates.peer_room import PeerRoom + def get_all_groups() -> dict: result = ( PG_SESSION.query(Games, GroupList) @@ -31,6 +32,7 @@ def get_all_groups() -> dict: # Convert the grouped result to the desired format return grouped_result +PEER_GROUP_LIST = get_all_groups() def get_peer_staging_channels(game_name: str, group_id: int) -> list[ChatChannelCaches]: assert isinstance(game_name, str) diff --git a/src/backends/protocols/gamespy/query_report/handlers.py b/src/backends/protocols/gamespy/query_report/handlers.py new file mode 100644 index 000000000..f7653e2b8 --- /dev/null +++ b/src/backends/protocols/gamespy/query_report/handlers.py @@ -0,0 +1,68 @@ +from datetime import datetime +from backends.library.abstractions.handler_base import HandlerBase +from backends.library.database.pg_orm import PG_SESSION, GameServerCaches +from backends.protocols.gamespy.query_report.requests import * +from servers.query_report.src.aggregates.exceptions import QRException + + +class AvaliableHandler(HandlerBase): + _request: AvaliableRequest + + +class ChallengeHandler(HandlerBase): + _request: HeartBeatRequest + + async def _data_operate(self) -> None: + cache = PG_SESSION.query(GameServerCaches).where( + GameServerCaches.instant_key == self._request.instant_key).first() + if cache is None: + raise QRException( + "No server found, please make sure there is a server.") + cache.avaliable = True # type: ignore + PG_SESSION.commit() + + +class Heartbeathandler(HandlerBase): + _request: HeartBeatRequest + + async def _data_operate(self) -> None: + cache = PG_SESSION.query(GameServerCaches).where( + GameServerCaches.instant_key == self._request.instant_key).first() + if cache is None: + cache = GameServerCaches(instant_key=self._request.instant_key, + server_id=self._request.server_id, + host_ip_address=self._request.client_ip, + game_name=self._request.game_name, + query_report_port=self._request.client_port, + update_time=datetime.now(), + status=self._request.server_status, + player_data=self._request.player_data, + server_data=self._request.server_data, + team_data=self._request.team_data, + avaliable=True) + else: + cache.instant_key = self._request.instant_key # type: ignore + cache.server_id = self._request.server_id # type: ignore + cache.host_ip_address = self._request.client_ip # type: ignore + cache.game_name = self._request.game_name # type: ignore + cache.query_report_port = self._request.client_port # type: ignore + cache.update_time = datetime.now() # type: ignore + cache.status = self._request.server_status # type: ignore + cache.player_data = self._request.player_data # type: ignore + cache.server_data = self._request.server_data # type: ignore + cache.team_data = self._request.team_data # type: ignore + cache.avaliable = True # type: ignore + + PG_SESSION.commit() + + +class KeepAliveHandler(HandlerBase): + _request: KeepAliveRequest + + async def _data_operate(self) -> None: + cache = PG_SESSION.query(GameServerCaches).where( + GameServerCaches.instant_key == self._request.instant_key).first() + # update heartbeat time + cache.update_time = datetime.now() # type: ignore + + PG_SESSION.commit() diff --git a/src/backends/protocols/gamespy/query_report/requests.py b/src/backends/protocols/gamespy/query_report/requests.py index 0f83eaef3..0a3dddb4f 100644 --- a/src/backends/protocols/gamespy/query_report/requests.py +++ b/src/backends/protocols/gamespy/query_report/requests.py @@ -38,6 +38,7 @@ class HeartBeatRequest(RequestBase): team_data: list[dict[str, str]] server_status: GameServerStatus group_id: int + game_name: str class EchoRequest(RequestBase): diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index 8bbd78615..b085b6efe 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -1,6 +1,7 @@ from fastapi import APIRouter from backends.protocols.gamespy.presence_connection_manager.requests import KeepAliveRequest +from backends.protocols.gamespy.query_report.handlers import AvaliableHandler from backends.protocols.gamespy.query_report.requests import AvaliableRequest, ChallengeRequest, ClientMessageRequest, EchoRequest, HeartBeatRequest from backends.urls import QUERY_REPORT @@ -19,7 +20,9 @@ async def challenge(request: ChallengeRequest): @router.post(f"{QUERY_REPORT}/AvailableHandler") async def available(request: AvaliableRequest): - raise NotImplementedError() + handler = AvaliableHandler(request) + await handler.handle() + return handler.response @router.post(f"{QUERY_REPORT}/ClientMessageAckHandler") @@ -43,4 +46,4 @@ async def keep_alive(request: KeepAliveRequest): app = FastAPI() app.include_router(router) - uvicorn.run(router, host="0.0.0.0", port=8000) \ No newline at end of file + uvicorn.run(router, host="0.0.0.0", port=8000) diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index b4bc8e5ee..36fc82ca4 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -80,10 +80,13 @@ def _prepare_data(self): if "server_id" in self._temp_data: raise UniSpyException("server_id name collision in dict") self._temp_data["server_id"] = self._client.server_config.server_id - if "client_ip_endpoint" in self._temp_data: - raise UniSpyException("client_ip_endpoint name collision in dict") - self._temp_data["client_ip_endpoint"] = self._client.connection.ip_endpoint - + if "client_ip" in self._temp_data: + raise UniSpyException("client_ip name collision in dict") + self._temp_data["client_ip"] = self._client.connection.remote_ip + if "client_port" in self._temp_data: + raise UniSpyException("client_port name collision in dict") + self._temp_data["client_port"] = self._client.connection.remote_port + def _upload_data(self): """ whether need send data to backend diff --git a/src/library/tests/mock_objects.py b/src/library/tests/mock_objects.py index 24c5f7083..a76513f88 100644 --- a/src/library/tests/mock_objects.py +++ b/src/library/tests/mock_objects.py @@ -2,7 +2,6 @@ import responses from library.src.abstractions.brocker import BrockerBase -from library.src.abstractions.client import ClientBase from library.src.abstractions.connections import ConnectionBase from library.src.abstractions.handler import CmdHandlerBase from library.src.log.log_manager import LogWriter diff --git a/src/servers/chat/src/abstractions/contract.py b/src/servers/chat/src/abstractions/contract.py index 62c079d37..1eaacc9ec 100644 --- a/src/servers/chat/src/abstractions/contract.py +++ b/src/servers/chat/src/abstractions/contract.py @@ -83,7 +83,8 @@ def __init__(self, request: RequestBase, result: Optional[ResultBase]) -> None: class BrockerMessage(BaseModel): server_id: UUID channel_name: str - sender_ip_end_point: str + sender_ip_address: str + sender_port: int message: str diff --git a/src/servers/chat/src/abstractions/handler.py b/src/servers/chat/src/abstractions/handler.py index 39cc32416..3064af458 100644 --- a/src/servers/chat/src/abstractions/handler.py +++ b/src/servers/chat/src/abstractions/handler.py @@ -86,8 +86,11 @@ def _message_construct(self): """ assert self._request.channel_name is not None self._response.build() - self._b_msg = BrockerMessage(server_id=self._client.server_config.server_id, channel_name=self._request.channel_name, - sender_ip_end_point=self._client.connection.ip_endpoint, message=self._response.sending_buffer) + self._b_msg = BrockerMessage(server_id=self._client.server_config.server_id, + channel_name=self._request.channel_name, + sender_ip_address=self._client.connection.remote_ip, + sender_port=self._client.connection.remote_port, + message=self._response.sending_buffer) def _publish_to_brocker(self): """ diff --git a/src/servers/query_report/src/v2/contracts/results.py b/src/servers/query_report/src/v2/contracts/results.py index b852911b4..91a233a54 100644 --- a/src/servers/query_report/src/v2/contracts/results.py +++ b/src/servers/query_report/src/v2/contracts/results.py @@ -1,23 +1,31 @@ +from typing import final from servers.query_report.src.v2.abstractions.contracts import ResultBase from servers.query_report.src.v2.aggregates.enums import PacketType +@final class ChallengeResult(ResultBase): packet_type: PacketType = PacketType.CHALLENGE +@final class ClientMessageResult(ResultBase): natneg_message: bytes message_key: int packet_type: PacketType = PacketType.CLIENT_MESSAGE +@final class EchoResult(ResultBase): info: dict packet_type: PacketType = PacketType.ECHO +@final class HeartBeatResult(ResultBase): + """ + this result is replied in unispy server + """ packet_type: PacketType = PacketType.HEARTBEAT - remote_ip_address:str - remote_port:int + remote_ip_address: str + remote_port: int diff --git a/src/servers/server_browser/src/v2/aggregations/server_info_builder.py b/src/servers/server_browser/src/v2/aggregations/server_info_builder.py index 134c1fbc9..3185395f5 100644 --- a/src/servers/server_browser/src/v2/aggregations/server_info_builder.py +++ b/src/servers/server_browser/src/v2/aggregations/server_info_builder.py @@ -2,10 +2,10 @@ from servers.query_report.src.aggregates.game_server_info import GameServerInfo -from backends.protocols.gamespy.query_report.data import get_all_groups +# from backends.protocols.gamespy.query_report.data import get_all_groups from servers.server_browser.src.v2.aggregations.enums import GameServerFlags -PEER_GROUP_LIST = get_all_groups() +# PEER_GROUP_LIST = get_all_groups() QUERY_REPORT_DEFAULT_PORT = 6500 @@ -51,8 +51,8 @@ def check_unsolicited_udp(header: list[int], server_info: GameServerInfo): def check_private_ip(header: list[int], server_info: GameServerInfo): #!when game create a channel chat, it will use both the public ip and private ip to build the name. #!known game: Worm3d - - if server_info.game_name in PEER_GROUP_LIST: + # todo + # if server_info.game_name in PEER_GROUP_LIST: if "localip0" in server_info.server_data: header[0] ^= GameServerFlags.PRIVATE_IP_FLAG.value bytes_address = inet_aton(server_info.server_data["localip0"]) From 52951e5f64f19f255f8b33bf4fd71a27bfc329d4 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 16 Nov 2024 18:30:50 +0800 Subject: [PATCH 143/231] Update README.md Fix: docker compose command --- .github/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index e60ac2f6e..4f850c76c 100644 --- a/.github/README.md +++ b/.github/README.md @@ -31,7 +31,7 @@ This project is licensed under the [GNU Affero General Public License v3.0](../L ## How to run -* Run ```docker compose up -f docker-compose-unispy-env.yml -d``` to setup postgresql and redis +* Run ```docker compose -f docker-compose-unispy-env.yml up -d``` to setup postgresql and redis * Run ```export UNISPY_CONFIG=''``` to setup env config file path, remember replace symbol `````` with config file path * Open with vscode: * Use vscode to open the src folder and reopen in devcontainer (make sure your vscode have devcontainer extension) From d5c1641c0097eff09bce5d0f4ea63137a86fa4e8 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 26 Nov 2024 23:22:00 +0000 Subject: [PATCH 144/231] refactor: update data featch functions --- common/UniSpy_pg.sql | 38 +--- src/backends/library/database/pg_orm.py | 78 ++++---- .../presence_connection_manager/data.py | 120 ++++++++++- .../presence_connection_manager/handlers.py | 56 ++++++ .../presence_connection_manager/requests.py | 14 +- .../gamespy/presence_search_player/data.py | 87 ++++++-- .../presence_search_player/handlers.py | 91 ++++++++- .../presence_search_player/requests.py | 8 +- .../precence_search_player/db_tests.py | 1 + .../src/aggregates/login_challenge.py | 28 ++- .../src/applications/handlers.py | 6 + .../src/contracts/requests.py | 186 +++++++----------- .../src/contracts/responses.py | 11 +- .../src/contracts/results.py | 7 +- .../src/contracts/results.py | 12 +- 15 files changed, 484 insertions(+), 259 deletions(-) create mode 100644 src/backends/protocols/gamespy/presence_connection_manager/handlers.py create mode 100644 src/backends/tests/gamespy/precence_search_player/db_tests.py diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 3a8add655..0c529496a 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -300,43 +300,7 @@ CREATE TABLE unispy.profiles ( serverflag integer DEFAULT 0 NOT NULL, status smallint DEFAULT 0, statstring character varying DEFAULT 'I love UniSpy'::character varying, - location character varying, - firstname character varying, - lastname character varying, - publicmask integer DEFAULT 0, - latitude double precision DEFAULT 0, - longitude double precision DEFAULT 0, - aim character varying DEFAULT ''::character varying, - picture integer DEFAULT 0, - occupationid integer DEFAULT 0, - incomeid integer DEFAULT 0, - industryid integer DEFAULT 0, - marriedid integer DEFAULT 0, - childcount integer DEFAULT 0, - interests1 integer DEFAULT 0, - ownership1 integer DEFAULT 0, - connectiontype integer DEFAULT 0, - sex smallint DEFAULT 0, - zipcode character varying DEFAULT '00000'::character varying, - countrycode character varying DEFAULT 1, - homepage character varying DEFAULT 'unispy.org'::character varying, - birthday integer DEFAULT 0, - birthmonth integer DEFAULT 0, - birthyear integer DEFAULT 0, - icquin integer DEFAULT 0, - quietflags smallint DEFAULT 0 NOT NULL, - streetaddr text, - streeaddr text, - city text, - cpubrandid integer DEFAULT 0, - cpuspeed integer DEFAULT 0, - memory smallint DEFAULT 0, - videocard1string text, - videocard1ram smallint DEFAULT 0, - videocard2string text, - videocard2ram smallint DEFAULT 0, - subscription integer DEFAULT 0, - adminrights integer DEFAULT 0 + extra_info jsonb ); diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 6b50f600f..b775d2a61 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -23,6 +23,9 @@ from servers.natneg.src.aggregations.enums import NatClientIndex, NatPortType from servers.query_report.src.v2.aggregates.enums import GameServerStatus +from sqlalchemy.orm.decl_api import DeclarativeBase + + Base: DeclarativeMeta = declarative_base() @@ -56,42 +59,43 @@ class Profiles(Base): status: Column | int = Column(SmallInteger, default=0) statstring: Column | str = Column(String, default="I love UniSpy") location: Column | str = Column(String) - firstname: Column | str = Column(String) - lastname: Column | str = Column(String) - publicmask: Column | int = Column(Integer, default=0) - latitude: Column | float = Column(Double, default=0) - longitude: Column | float = Column(Double, default=0) - aim: Column | str = Column(String, default="") - picture: Column | int = Column(Integer, default=0) - occupationid: Column | int = Column(Integer, default=0) - incomeid: Column | int = Column(Integer, default=0) - industryid: Column | int = Column(Integer, default=0) - marriedid: Column | int = Column(Integer, default=0) - childcount: Column | int = Column(Integer, default=0) - interests1: Column | int = Column(Integer, default=0) - ownership1: Column | int = Column(Integer, default=0) - connectiontype: Column | int = Column(Integer, default=0) - sex: Column | int = Column(SmallInteger, default=0) - zipcode: Column | str = Column(String, default="00000") - countrycode: Column | str = Column(String, default="1") - homepage: Column | str = Column(String, default="unispy.org") - birthday: Column | int = Column(Integer, default=0) - birthmonth: Column | int = Column(Integer, default=0) - birthyear: Column | int = Column(Integer, default=0) - icquin: Column | int = Column(Integer, default=0) - quietflags: Column | int = Column(SmallInteger, nullable=False, default=0) - streetaddr: Column | str = Column(Text) - streeaddr: Column | str = Column(Text) - city: Column | str = Column(Text) - cpubrandid: Column | int = Column(Integer, default=0) - cpuspeed: Column | int = Column(Integer, default=0) - memory: Column | int = Column(SmallInteger, default=0) - videocard1string: Column | str = Column(Text) - videocard1ram: Column | int = Column(SmallInteger, default=0) - videocard2string: Column | str = Column(Text) - videocard2ram: Column | int = Column(SmallInteger, default=0) - subscription: Column | int = Column(Integer, default=0) - adminrights: Column | int = Column(Integer, default=0) + extra_info = Column(JSONB) + # firstname: Column | str = Column(String) + # lastname: Column | str = Column(String) + # publicmask: Column | int = Column(Integer, default=0) + # latitude: Column | float = Column(Double, default=0) + # longitude: Column | float = Column(Double, default=0) + # aim: Column | str = Column(String, default="") + # picture: Column | int = Column(Integer, default=0) + # occupationid: Column | int = Column(Integer, default=0) + # incomeid: Column | int = Column(Integer, default=0) + # industryid: Column | int = Column(Integer, default=0) + # marriedid: Column | int = Column(Integer, default=0) + # childcount: Column | int = Column(Integer, default=0) + # interests1: Column | int = Column(Integer, default=0) + # ownership1: Column | int = Column(Integer, default=0) + # connectiontype: Column | int = Column(Integer, default=0) + # sex: Column | int = Column(SmallInteger, default=0) + # zipcode: Column | str = Column(String, default="00000") + # countrycode: Column | str = Column(String, default="1") + # homepage: Column | str = Column(String, default="unispy.org") + # birthday: Column | int = Column(Integer, default=0) + # birthmonth: Column | int = Column(Integer, default=0) + # birthyear: Column | int = Column(Integer, default=0) + # icquin: Column | int = Column(Integer, default=0) + # quietflags: Column | int = Column(SmallInteger, nullable=False, default=0) + # streetaddr: Column | str = Column(Text) + # streeaddr: Column | str = Column(Text) + # city: Column | str = Column(Text) + # cpubrandid: Column | int = Column(Integer, default=0) + # cpuspeed: Column | int = Column(Integer, default=0) + # memory: Column | int = Column(SmallInteger, default=0) + # videocard1string: Column | str = Column(Text) + # videocard1ram: Column | int = Column(SmallInteger, default=0) + # videocard2string: Column | str = Column(Text) + # videocard2ram: Column | int = Column(SmallInteger, default=0) + # subscription: Column | int = Column(Integer, default=0) + # adminrights: Column | int = Column(Integer, default=0) class SubProfiles(Base): @@ -300,7 +304,7 @@ class GameServerCaches(Base): def connect_to_db() -> Session: ENGINE = create_engine(CONFIG.postgresql.url) session = sessionmaker(bind=ENGINE)() - Base.metadata.create_all(ENGINE) + # Base.metadata.create_all(ENGINE) with ENGINE.connect() as conn: conn.execute(text("SELECT 1")) return session diff --git a/src/backends/protocols/gamespy/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py index 949f5f9d0..80428b4b5 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/data.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/data.py @@ -1,3 +1,4 @@ +from datetime import datetime from typing import TYPE_CHECKING, cast from sqlalchemy import Column @@ -9,9 +10,22 @@ Users, PG_SESSION, ) +from servers.presence_connection_manager.src.aggregates.enums import LoginStatus +from servers.presence_connection_manager.src.contracts.results import LoginData from servers.presence_search_player.src.aggregates.exceptions import GPDatabaseException +def update_online_time(ip: str, port: int): + if TYPE_CHECKING: + assert isinstance(Users.lastip, Column) + + result = PG_SESSION.query(Users).where(Users.lastip == ip).first() + if result is None: + return False + result.lastonline = datetime.now() + PG_SESSION.commit() + + def is_email_exist(email: str) -> bool: if TYPE_CHECKING: assert isinstance(Users.email, Column) @@ -140,7 +154,7 @@ def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: return result -def get_user_infos(unique_nick: str, namespace_id: int) -> list[tuple[int, int, int]]: +def get_user_infos_by_uniquenick_namespace_id(unique_nick: str, namespace_id: int) -> LoginData | None: if TYPE_CHECKING: assert isinstance(Profiles.profileid, Column) assert isinstance(Profiles.userid, Column) @@ -149,23 +163,117 @@ def get_user_infos(unique_nick: str, namespace_id: int) -> list[tuple[int, int, assert isinstance(Profiles.nick, Column) assert isinstance(SubProfiles.uniquenick, Column) assert isinstance(SubProfiles.namespaceid, Column) + assert isinstance(Users.password, Column) + assert isinstance(Users.emailverified, Column) + assert isinstance(Users.banned, Column) result = ( - PG_SESSION.query(Users.userid, Profiles.profileid, - SubProfiles.subprofileid) + PG_SESSION.query(Users.userid, + Profiles.profileid, + SubProfiles.subprofileid, + Profiles.nick, + Users.email, + SubProfiles.uniquenick, + Users.password, + Users.emailverified, + Users.banned, + SubProfiles.namespaceid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .filter( SubProfiles.uniquenick == unique_nick, SubProfiles.namespaceid == namespace_id, ) - .all() + .first() ) - if TYPE_CHECKING: - result = cast(list[tuple[int, int, int]], result) + return result +def get_user_infos_by_nick_email(nick: str, email: str) -> LoginData | None: + if TYPE_CHECKING: + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(Users.userid, Column) + assert isinstance(Users.email, Column) + assert isinstance(Users.password, Column) + assert isinstance(Users.emailverified, Column) + assert isinstance(Users.banned, Column) + assert isinstance(Profiles.nick, Column) + assert isinstance(SubProfiles.uniquenick, Column) + assert isinstance(SubProfiles.namespaceid, Column) + + result = ( + PG_SESSION.query(Users.userid, + Profiles.profileid, + SubProfiles.subprofileid, + Profiles.nick, + Users.email, + SubProfiles.uniquenick, + Users.password, + Users.emailverified, + Users.banned, + SubProfiles.namespaceid + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter( + Users.email == email, + Profiles.nick == nick + ) + .first() + ) + if result is not None: + return LoginData(*result) # type: ignore + else: + return None + + +def update_online_status(user_id: int, status: LoginStatus): + if TYPE_CHECKING: + assert isinstance(Users.userid, Column) + result = PG_SESSION.query(Users).where(Users.userid == user_id).first() + raise NotImplementedError("implement sesskey") + + +def get_user_infos_by_authtoken(auth_token: str) -> LoginData | None: + if TYPE_CHECKING: + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(Users.userid, Column) + assert isinstance(Users.email, Column) + assert isinstance(Users.password, Column) + assert isinstance(Users.emailverified, Column) + assert isinstance(Users.banned, Column) + assert isinstance(Profiles.nick, Column) + assert isinstance(SubProfiles.uniquenick, Column) + assert isinstance(SubProfiles.namespaceid, Column) + assert isinstance(SubProfiles.authtoken, Column) + + result = ( + PG_SESSION.query(Users.userid, + Profiles.profileid, + SubProfiles.subprofileid, + Profiles.nick, + Users.email, + SubProfiles.uniquenick, + Users.password, + Users.emailverified, + Users.banned, + SubProfiles.namespaceid) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter( + SubProfiles.authtoken == auth_token + ) + .first() + ) + if result is not None: + return LoginData(*result) # type: ignore + else: + return None + + def update_block_info_list(target_id: int, profile_id: int, namespace_id: int) -> None: result = ( PG_SESSION.query(Blocked) diff --git a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py new file mode 100644 index 000000000..dacafc18c --- /dev/null +++ b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py @@ -0,0 +1,56 @@ +from backends.library.abstractions.handler_base import HandlerBase +from backends.protocols.gamespy.presence_connection_manager.requests import * +import backends.protocols.gamespy.presence_connection_manager.data as data +from servers.presence_connection_manager.src.aggregates.enums import LoginStatus +from servers.presence_connection_manager.src.contracts.results import LoginResult +from servers.presence_search_player.src.aggregates.exceptions import GPLoginBadEmailException + + +class KeepAliveHandler(HandlerBase): + _request: KeepAliveRequest + + async def _data_operate(self) -> None: + data.update_online_time(self._request.client_ip, + self._request.client_port) + + +class LoginHandler(HandlerBase): + _request: LoginRequest + + async def _data_operate(self) -> None: + if self._request.type == LoginType.NICK_EMAIL: + self._nick_email_login() + elif self._request.type == LoginType.UNIQUENICK_NAMESPACE_ID: + self._unique_nick_login() + elif self._request.type == LoginType.AUTH_TOKEN: + self._auth_token_login() + else: + raise ValueError("Request type not valid") + + def _nick_email_login(self) -> None: + is_exsit = data.is_email_exist(self._request.email) + if not is_exsit: + raise GPLoginBadEmailException( + f"email: {self._request.email} is invalid.") + self._data = data.get_user_infos_by_nick_email( + self._request.nick, self._request.email) + + def _unique_nick_login(self) -> None: + self.data = data.get_user_infos_by_uniquenick_namespace_id( + self._request.unique_nick, self._request.namespace_id) + + def _auth_token_login(self) -> None: + self._data = data.get_user_infos_by_authtoken( + self._request.auth_token) + + async def _result_construct(self) -> None: + if self.data is not None: + self._result = LoginResult(data=self.data) + + +class LogoutHandler(HandlerBase): + _request: LogoutRequest + + async def _data_operate(self) -> None: + # data.update_online_status(user_id=, status=LoginStatus.DISCONNECTED) + raise NotImplementedError() diff --git a/src/backends/protocols/gamespy/presence_connection_manager/requests.py b/src/backends/protocols/gamespy/presence_connection_manager/requests.py index 5e5ce3477..d6b0d0bc0 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/requests.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/requests.py @@ -55,6 +55,8 @@ class StatusRequest(RequestBase): class KeepAliveRequest(RequestBase): + client_ip: str + client_port: int pass @@ -157,16 +159,8 @@ class UpdateProfileRequest(RequestBase): country_code: Optional[str] = None -class UpdateUiRequest(RequestBase): - cpubrandid: Optional[str] = None - cpuspeed: Optional[str] = None - memory: Optional[str] = None - videocard1ram: Optional[str] = None - videocard2ram: Optional[str] = None - connectionid: Optional[str] = None - connectionspeed: Optional[str] = None - hasnetwork: Optional[str] = None - pic: Optional[str] = None +class UpdateUIRequest(RequestBase): + extra_info: dict if __name__ == "__main__": diff --git a/src/backends/protocols/gamespy/presence_search_player/data.py b/src/backends/protocols/gamespy/presence_search_player/data.py index 7cda46bd6..5256f37c5 100644 --- a/src/backends/protocols/gamespy/presence_search_player/data.py +++ b/src/backends/protocols/gamespy/presence_search_player/data.py @@ -7,6 +7,7 @@ Users, PG_SESSION, ) +from servers.presence_search_player.src.contracts.results import * def verify_email(email: str): @@ -151,7 +152,7 @@ def get_nick_and_unique_nick_list(email: str, password: str, namespace_id: int) return result -def get_friend_info_list(profile_id: int, namespace_id: int, game_name: str): +def get_friend_info_list(profile_id: int, namespace_id: int, game_name: str) -> list: """ return [(profileid, nick, uniquenick, lastname, firstname, userid, email)] """ @@ -193,7 +194,7 @@ def get_friend_info_list(profile_id: int, namespace_id: int, game_name: str): def get_matched_profile_info_list( profile_ids: list[int], namespace_id: int -) -> Optional[list[tuple[int, str]]]: +) -> list[tuple[int, str]]: """ return [(profileid,uniquenick)] @@ -210,15 +211,18 @@ def get_matched_profile_info_list( ) .all() ) + if result is None: + result = [] if TYPE_CHECKING: - result = cast(Optional[list[tuple[int, str]]], result) + result = cast(list[tuple[int, str]], result) return result def get_matched_info_by_nick( nick_name: str, -) -> Optional[list[tuple[int, str, str, str, str, int]]]: +) -> list[SearchResultData]: if TYPE_CHECKING: + assert isinstance(Users.email, Column) assert isinstance(Profiles.profileid, Column) assert isinstance(Profiles.nick, Column) assert isinstance(Profiles.userid, Column) @@ -229,6 +233,7 @@ def get_matched_info_by_nick( assert isinstance(Users.userid, Column) result = ( PG_SESSION.query( + Users.email, Profiles.profileid, Profiles.nick, Profiles.firstname, @@ -241,15 +246,17 @@ def get_matched_info_by_nick( .filter(Profiles.nick == nick_name) .all() ) - if TYPE_CHECKING: - result = cast( - Optional[list[tuple[int, str, str, str, str, int]]], result) - return result + temp: list[SearchResultData] = [] + for email, profile_id, nick, first_name, last_name, uniquenick, namespace_id in result: + temp.append(SearchResultData(profile_id=profile_id, nick=nick, uniquenick=uniquenick, + email=email, namespace_id=namespace_id, firstname=first_name, lastname=last_name)) + + return temp def get_matched_info_by_email( email: str, -) -> list[tuple[int, str, str, str, str, int]]: +) -> list[SearchResultData]: if TYPE_CHECKING: assert isinstance(Profiles.profileid, Column) assert isinstance(Profiles.nick, Column) @@ -273,9 +280,11 @@ def get_matched_info_by_email( .filter(Users.email == email) .all() ) - if TYPE_CHECKING: - result = cast(list[tuple[int, str, str, str, str, int]], result) - return result + temp: list[SearchResultData] = [] + for email, profile_id, nick, first_name, last_name, uniquenick, namespace_id in result: + temp.append(SearchResultData(profile_id=profile_id, nick=nick, uniquenick=uniquenick, + email=email, namespace_id=namespace_id, firstname=first_name, lastname=last_name)) + return temp def get_matched_info_by_nick_and_email(nick_name: str, email: str): @@ -290,6 +299,7 @@ def get_matched_info_by_nick_and_email(nick_name: str, email: str): assert isinstance(Users.email, Column) result = ( PG_SESSION.query( + Users.email, Profiles.profileid, Profiles.nick, Profiles.firstname, @@ -302,12 +312,16 @@ def get_matched_info_by_nick_and_email(nick_name: str, email: str): .filter(Users.email == email, Profiles.nick == nick_name) .all() ) - return result + temp: list[SearchResultData] = [] + for email, profile_id, nick, first_name, last_name, uniquenick, namespace_id in result: + temp.append(SearchResultData(profile_id=profile_id, nick=nick, uniquenick=uniquenick, + email=email, namespace_id=namespace_id, firstname=first_name, lastname=last_name)) + return temp def get_matched_info_by_uniquenick_and_namespaceid( unique_nick: str, namespace_id: int -) -> list[tuple[int, str, str, str, str, int]]: +) -> list[SearchResultData]: if TYPE_CHECKING: assert isinstance(Profiles.profileid, Column) assert isinstance(Profiles.nick, Column) @@ -334,9 +348,50 @@ def get_matched_info_by_uniquenick_and_namespaceid( ) .all() ) + temp: list[SearchResultData] = [] + for email, profile_id, nick, first_name, last_name, uniquenick, namespace_id in result: + temp.append(SearchResultData(profile_id=profile_id, nick=nick, uniquenick=uniquenick, + email=email, namespace_id=namespace_id, firstname=first_name, lastname=last_name)) + + return temp + + +def get_matched_info_by_uniquenick_and_namespaceids( + unique_nick: str, namespace_ids: list[int] +) -> list[SearchResultData]: if TYPE_CHECKING: - result = cast(list[tuple[int, str, str, str, str, int]], result) - return result + assert isinstance(Profiles.profileid, Column) + assert isinstance(Profiles.nick, Column) + assert isinstance(Profiles.firstname, Column) + assert isinstance(Profiles.lastname, Column) + assert isinstance(SubProfiles.uniquenick, Column) + assert isinstance(SubProfiles.namespaceid, Column) + assert isinstance(Profiles.userid, Column) + assert isinstance(Users.email, Column) + + result = ( + PG_SESSION.query( + Profiles.profileid, + Profiles.nick, + Profiles.firstname, + Profiles.lastname, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .filter( + SubProfiles.uniquenick == unique_nick, + SubProfiles.namespaceid.in_(namespace_ids) + ) + .all() + ) + data: list[SearchResultData] = [] + for email, profile_id, nick, first_name, last_name, uniquenick, namespace_id in result: + data.append(SearchResultData(profile_id=profile_id, nick=nick, uniquenick=uniquenick, + email=email, namespace_id=namespace_id, firstname=first_name, lastname=last_name)) + + return data def is_uniquenick_exist(unique_nick: str, namespace_id: int, game_name: str) -> bool: diff --git a/src/backends/protocols/gamespy/presence_search_player/handlers.py b/src/backends/protocols/gamespy/presence_search_player/handlers.py index 89efe1950..6f5726d2c 100644 --- a/src/backends/protocols/gamespy/presence_search_player/handlers.py +++ b/src/backends/protocols/gamespy/presence_search_player/handlers.py @@ -3,8 +3,9 @@ from backends.library.database.pg_orm import PG_SESSION, Users, Profiles, SubProfiles import backends.protocols.gamespy.presence_search_player.data as data from backends.protocols.gamespy.presence_search_player.requests import * +from library.src.exceptions.general import UniSpyException from servers.presence_search_player.src.aggregates.exceptions import CheckException -from servers.presence_search_player.src.contracts.results import CheckResult, NewUserResult, NickResultData, NicksResult +from servers.presence_search_player.src.contracts.results import CheckResult, NewUserResult, NickResultData, NicksResult, OthersListData, OthersListResult, OthersResult, OthersResultData, SearchResult, SearchUniqueResult, UniqueSearchResult, ValidResult class CheckHandler(HandlerBase): @@ -94,3 +95,91 @@ async def _data_operate(self) -> None: async def _result_construct(self) -> None: self._result = NicksResult(data=self.result_data) + + +class OthersHandler(HandlerBase): + _request: OthersRequest + + async def _data_operate(self) -> None: + self._data = data.get_friend_info_list( + self._request.profile_id, self._request.namespace_id, self._request.game_name) + + async def _result_construct(self) -> None: + temp_list = [] + for item in self._data: + temp_list.append(OthersResultData( + profile_id=item[0], nick=item[1], uniquenick=item[2], lastname=item[3], firstname=item[4], user_id=item[5], email=item[6] + )) + self._result = OthersResult(data=temp_list) + + +class OthersListHandler(HandlerBase): + _request: OthersListRequest + + async def _data_operate(self) -> None: + self._data = data.get_matched_profile_info_list( + self._request.profile_ids, self._request.namespace_id) + + async def _result_construct(self) -> None: + temp = [] + for profile_id, uniquenick in self._data: + temp.append(OthersListData( + profile_id=profile_id, unique_nick=uniquenick)) + self._result = OthersListResult(data=temp) + + +# class PlayerMatchHandler(HandlerBase): +# _request: playermatchrequest + +class SearchHandler(HandlerBase): + _request: SearchRequest + + async def _data_operate(self) -> None: + if self._request.request_type == SearchType.NICK_SEARCH: + self._data = data.get_matched_info_by_nick(self._request.nick) + + elif self._request.request_type == SearchType.NICK_EMAIL_SEARCH: + data.get_matched_info_by_nick_and_email( + self._request.nick, self._request.email) + elif self._request.request_type == SearchType.UNIQUENICK_NAMESPACEID_SEARCH: + self._data = data.get_matched_info_by_uniquenick_and_namespaceid( + self._request.uniquenick, self._request.namespace_id) + elif self._request.request_type == SearchType.EMAIL_SEARCH: + self._data = data.get_matched_info_by_email(self._request.email) + else: + raise UniSpyException("search type invalid") + + async def _result_construct(self) -> None: + self._result = SearchResult(result=self._data) + + +class SearchUniqueHandler(HandlerBase): + _request: SearchUniqueRequest + + async def _data_operate(self) -> None: + self._data = data.get_matched_info_by_uniquenick_and_namespaceids( + self._request.uniquenick, self._request.namespace_ids) + + async def _result_construct(self) -> None: + self._result = SearchUniqueResult(data=self._data) + + +class UniqueSearchHandler(HandlerBase): + _request: UniqueSearchRequest + + async def _data_operate(self) -> None: + self._is_exist = data.is_uniquenick_exist( + self._request.preferred_nick, self._request.namespace_id, self._request.game_name) + + async def _result_construct(self) -> None: + self._result = UniqueSearchResult(is_uniquenick_exist=self._is_exist) + + +class ValidHandler(HandlerBase): + _request: ValidRequest + + async def _data_operate(self) -> None: + self._is_exist = data.is_email_exist(self._request.email) + + async def _result_construct(self) -> None: + self._result = ValidResult(is_account_valid=self._is_exist) diff --git a/src/backends/protocols/gamespy/presence_search_player/requests.py b/src/backends/protocols/gamespy/presence_search_player/requests.py index 8ff4deb77..a915143b8 100644 --- a/src/backends/protocols/gamespy/presence_search_player/requests.py +++ b/src/backends/protocols/gamespy/presence_search_player/requests.py @@ -1,4 +1,3 @@ -from typing import Optional from backends.library.abstractions.contracts import RequestBase as RB from servers.presence_search_player.src.aggregates.enums import SearchType @@ -49,8 +48,9 @@ class OthersListRequest(RequestBase): class OthersRequest(RequestBase): - profile_id: Optional[int] - game_name: int + profile_id: int + game_name: str + namespace_id: int class SearchRequest(RequestBase): @@ -66,6 +66,7 @@ class SearchRequest(RequestBase): firstname: str lastname: str icquin: str + namespace_id: int class SearchUniqueRequest(RequestBase): @@ -76,6 +77,7 @@ class SearchUniqueRequest(RequestBase): class UniqueSearchRequest(RequestBase): preferred_nick: str game_name: str + namespace_id: int class ValidRequest(RequestBase): diff --git a/src/backends/tests/gamespy/precence_search_player/db_tests.py b/src/backends/tests/gamespy/precence_search_player/db_tests.py new file mode 100644 index 000000000..e0f6fed08 --- /dev/null +++ b/src/backends/tests/gamespy/precence_search_player/db_tests.py @@ -0,0 +1 @@ +# the tests related to database operations diff --git a/src/servers/presence_connection_manager/src/aggregates/login_challenge.py b/src/servers/presence_connection_manager/src/aggregates/login_challenge.py index 0790b0217..91efa97c5 100644 --- a/src/servers/presence_connection_manager/src/aggregates/login_challenge.py +++ b/src/servers/presence_connection_manager/src/aggregates/login_challenge.py @@ -8,7 +8,7 @@ class LoginChallengeProof: def __init__( - self, userData, loginType, partnerID, challenge1, challenge2, passwordHash + self, userData: str, loginType: LoginType, partnerID: int, challenge1: str, challenge2: str, passwordHash: str ): self.userData = userData self.loginType = loginType @@ -17,19 +17,17 @@ def __init__( self.challenge2 = challenge2 self.passwordHash = passwordHash + def generate_proof(self): + tempUserData = self.userData -@staticmethod -def generate_proof(data: LoginChallengeProof): - tempUserData = data.userData + if self.partnerID is not None: + if ( + self.partnerID != GPPartnerId.GAMESPY.value + and self.loginType != LoginType.AUTH_TOKEN + ): + tempUserData = f"{self.partnerID}@{self.userData}" - if data.partnerID is not None: - if ( - data.partnerID != GPPartnerId.GAMESPY.value - and data.loginType != LoginType.AUTH_TOKEN - ): - tempUserData = f"{data.partnerID}@{data.userData}" - - responseString = f"{data.passwordHash} {' ' * 48}{tempUserData}{data.challenge1}{data.challenge2}{data.passwordHash}" - hashString = hashlib.md5(responseString.encode()).hexdigest() - - return hashString + responseString = f"{self.passwordHash} { + ' ' * 48}{tempUserData}{self.challenge1}{self.challenge2}{self.passwordHash}" + hashString = hashlib.md5(responseString.encode()).hexdigest() + return hashString diff --git a/src/servers/presence_connection_manager/src/applications/handlers.py b/src/servers/presence_connection_manager/src/applications/handlers.py index 951feef76..453a57d2a 100644 --- a/src/servers/presence_connection_manager/src/applications/handlers.py +++ b/src/servers/presence_connection_manager/src/applications/handlers.py @@ -62,6 +62,12 @@ def __init__(self, client: "Client", request: KeepAliveRequest) -> None: assert isinstance(request, KeepAliveRequest) super().__init__(client, request) + def _data_operate(self) -> None: + # we set ip and data to request + self._request.client_ip = self._client.connection.remote_ip + self._request.client_port = self._client.connection.remote_port + super()._data_operate() + def _response_construct(self) -> None: self._response = KeepAliveResponse(self._request) diff --git a/src/servers/presence_connection_manager/src/contracts/requests.py b/src/servers/presence_connection_manager/src/contracts/requests.py index cd74c2095..d49736dc7 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests.py +++ b/src/servers/presence_connection_manager/src/contracts/requests.py @@ -1,5 +1,5 @@ +from library.src.exceptions.general import UniSpyException from servers.presence_connection_manager.src.aggregates.enums import PublicMasks -from library.src.extentions.gamespy_utils import is_valid_date from typing import Optional, final from library.src.extentions.gamespy_utils import convert_to_key_value from servers.presence_connection_manager.src.abstractions.contracts import RequestBase @@ -23,10 +23,70 @@ ) # region General +EXTRA_INFO_DICT: dict[str, type] = { + "firstname": str, + "lastname": str, + "publicmask": int, + "latitude": float, + "longitude": float, + "aim": str, + "picture": int, + "occupationid": int, + "incomeid": int, + "industryid": int, + "marriedid": int, + "childcount": int, + "interests1": int, + "ownership1": int, + "connectiontype": int, + "sex": int, + "zipcode": str, + "countrycode": str, + "homepage": str, + "birthday": int, + "birthmonth": int, + "birthyear": int, + "icquin": int, + "quietflags": int, + "streetaddr": str, + "streeaddr": str, + "city": str, + "cpubrandid": int, + "cpuspeed": int, + "memory": int, + "videocard1string": str, + "videocard1ram": int, + "videocard2string": str, + "videocard2ram": int, + "subscription": int, + "adminrights": int +} + + +def validate_extra_infos(info_dict: dict) -> dict: + """ + validate and create a dict of extra info + """ + assert isinstance(info_dict, dict) + extra_infos = {} + for key, value in info_dict.values(): + if key in EXTRA_INFO_DICT and key not in extra_infos: + if not isinstance(value, EXTRA_INFO_DICT[key]): + converted_value = value + else: + try: + value_type = EXTRA_INFO_DICT[key] + converted_value = value_type(value) + except Exception as e: + raise UniSpyException(f"value conversion failed: {e}") + extra_infos[key] = converted_value + return extra_infos @final class KeepAliveRequest(RequestBase): + client_ip: str + client_port: int pass @@ -468,93 +528,19 @@ def parse(self): @final class UpdateProfileRequest(RequestBase): - has_public_mask_flag: Optional[bool] = None - public_mask: Optional[PublicMasks] = None - session_key: Optional[str] = None - partner_id: Optional[int] = None - nick: Optional[str] = None - uniquenick: Optional[str] = None - has_first_name_flag: Optional[bool] = None - first_name: Optional[str] = None - has_last_name_flag: Optional[bool] = None - last_name: Optional[str] = None - has_icq_flag: Optional[bool] = None - icq_uin: Optional[int] = None - has_home_page_flag: Optional[bool] = None - home_page: Optional[str] = None - has_birthday_flag: bool = False - birth_day: Optional[int] = None - birth_month: Optional[int] = None - birth_year: Optional[int] = None - has_sex_flag: bool = False - sex: Optional[int] = None - has_zip_code: bool = False - zip_code: Optional[str] = None - has_country_code: bool = False - country_code: Optional[str] = None + session_key: str + partner_id: int + nick: str + uniquenick: str + extra_infos: dict def parse(self): super().parse() - if "publicmask" in self.request_dict: - if not self.request_dict["publicmask"].isdigit(): - raise GPParseException("publicmask format is incorrect") - self.has_public_mask_flag = True - self.public_mask = PublicMasks( - int(self.request_dict["publicmask"])) - if "sesskey" not in self.request_dict: raise GPParseException("sesskey is missing") self.session_key = self.request_dict["sesskey"] - if "firstname" in self.request_dict: - self.first_name = self.request_dict["firstname"] - self.has_first_name_flag = True - - if "lastname" in self.request_dict: - self.last_name = self.request_dict["lastname"] - self.has_last_name_flag = True - - if "icquin" in self.request_dict: - if not self.request_dict["icquin"].isdigit(): - raise GPParseException("icquin format is incorrect") - self.has_icq_flag = True - self.icq_uin = int(self.request_dict["icquin"]) - - # Remaining attribute assignments... - if "homepage" in self.request_dict: - self.home_page = self.request_dict["homepage"] - self.has_home_page_flag = True - - if "birthday" in self.request_dict: - try: - date = int(self.request_dict["birthday"]) - d = (date >> 24) & 0xFF - m = (date >> 16) & 0xFF - y = date & 0xFFFF - if is_valid_date(d, m, y): - self.birth_day = d - self.birth_month = m - self.birth_year = y - self.has_birthday_flag = True - except ValueError: - pass - - if "sex" in self.request_dict: - try: - self.sex = int(self.request_dict["sex"]) - self.has_sex_flag = True - except ValueError: - raise GPParseException("sex format is incorrect") - - if "zipcode" in self.request_dict: - self.zip_code = self.request_dict["zipcode"] - self.has_zip_code = True - - if "countrycode" in self.request_dict: - self.country_code = self.request_dict["countrycode"] - self.has_country_code = True - if "partnerid" in self.request_dict: try: self.partner_id = int(self.request_dict["partnerid"]) @@ -567,45 +553,13 @@ def parse(self): if "uniquenick" in self.request_dict: self.uniquenick = self.request_dict["uniquenick"] + self.extra_info = validate_extra_infos(self.request_dict) + @final class UpdateUserInfoRequest(RequestBase): - cpubrandid: Optional[str] = None - cpuspeed: Optional[str] = None - memory: Optional[str] = None - videocard1ram: Optional[str] = None - videocard2ram: Optional[str] = None - connectionid: Optional[str] = None - connectionspeed: Optional[str] = None - hasnetwork: Optional[str] = None - pic: Optional[str] = None + extra_info: dict[str, str] def parse(self): super().parse() - - if "cpubrandid" in self.request_dict: - self.cpubrandid = self.request_dict["cpubrandid"] - - if "cpuspeed" in self.request_dict: - self.cpuspeed = self.request_dict["cpuspeed"] - - if "memory" in self.request_dict: - self.memory = self.request_dict["memory"] - - if "videocard1ram" in self.request_dict: - self.videocard1ram = self.request_dict["videocard1ram"] - - if "videocard2ram" in self.request_dict: - self.videocard2ram = self.request_dict["videocard2ram"] - - if "connectionid" in self.request_dict: - self.connectionid = self.request_dict["connectionid"] - - if "connectionspeed" in self.request_dict: - self.connectionspeed = self.request_dict["connectionspeed"] - - if "hasnetwork" in self.request_dict: - self.hasnetwork = self.request_dict["hasnetwork"] - - if "pic" in self.request_dict: - self.pic = self.request_dict["pic"] + self.extra_info = validate_extra_infos(self.request_dict) diff --git a/src/servers/presence_connection_manager/src/contracts/responses.py b/src/servers/presence_connection_manager/src/contracts/responses.py index 9b526b11d..ba7cf3833 100644 --- a/src/servers/presence_connection_manager/src/contracts/responses.py +++ b/src/servers/presence_connection_manager/src/contracts/responses.py @@ -1,4 +1,5 @@ from servers.presence_connection_manager.src.abstractions.contracts import ResponseBase +from servers.presence_connection_manager.src.aggregates.login_challenge import SERVER_CHALLENGE, LoginChallengeProof from servers.presence_connection_manager.src.applications.client import ( LOGIN_TICKET, SESSION_KEY, @@ -34,10 +35,14 @@ def __init__(self, request: LoginRequest, result: LoginResult): assert isinstance(result, LoginResult) def build(self): - # string checkSumStr = _result.DatabaseResults.Nick + _result.DatabaseResults.UniqueNick + _result.DatabaseResults.NamespaceID; - # _connection.UserData.SessionKey = _crc.ComputeChecksum(checkSumStr); - self.sending_buffer = f"\\lc\\2\\sesskey\\{SESSION_KEY}\\proof\\{self._result.response_proof}\\userid\\{ + response_proof = LoginChallengeProof(self._request.user_data, + self._request.type, + self._request.partner_id, + self._request.user_challenge, + SERVER_CHALLENGE, + self._result.data.password_hash).generate_proof() + self.sending_buffer = f"\\lc\\2\\sesskey\\{SESSION_KEY}\\proof\\{response_proof}\\userid\\{ self._result.data.user_id}\\profileid\\{self._result.data.profile_id}" if self._result.data.unique_nick is not None: diff --git a/src/servers/presence_connection_manager/src/contracts/results.py b/src/servers/presence_connection_manager/src/contracts/results.py index c467d95a4..817558cbd 100644 --- a/src/servers/presence_connection_manager/src/contracts/results.py +++ b/src/servers/presence_connection_manager/src/contracts/results.py @@ -9,9 +9,10 @@ # region General -class LoginDataModel(BaseModel): +class LoginData(BaseModel): user_id: int profile_id: int + sub_profile_id: int nick: str email: str unique_nick: str @@ -19,12 +20,10 @@ class LoginDataModel(BaseModel): email_verified_flag: bool banned_flag: bool namespace_id: int - sub_profile_id: int class LoginResult(ResultBase): - response_proof: str - data: LoginDataModel + data: LoginData class NewUserResult(ResultBase): diff --git a/src/servers/presence_search_player/src/contracts/results.py b/src/servers/presence_search_player/src/contracts/results.py index 1ec60261c..c6b8c9c5f 100644 --- a/src/servers/presence_search_player/src/contracts/results.py +++ b/src/servers/presence_search_player/src/contracts/results.py @@ -74,18 +74,8 @@ class SearchResult(ResultBase): result: list[SearchResultData] -class SearchUniqueResultData(BaseModel): - profile_id: int - nick: str - uniquenick: str - email: str - firstname: str - lastname: str - namespace_id: int - - class SearchUniqueResult(ResultBase): - data: list[SearchUniqueResultData] + data: list[SearchResultData] class UniqueSearchResult(ResultBase): From bd25e2f72068375a0e7e0cb26a1ffc17ee97f179 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 10 Dec 2024 03:57:31 +0000 Subject: [PATCH 145/231] refactor: update backend handlers --- common/UniSpy_pg.sql | 3 +- src/backends/library/database/pg_orm.py | 72 +++------- .../presence_connection_manager/data.py | 130 ++++++++++++++++-- .../presence_connection_manager/handlers.py | 126 ++++++++++++++++- .../presence_connection_manager/requests.py | 46 ++++--- .../src/abstractions/contracts.py | 3 +- .../src/aggregates/enums.py | 6 + .../src/aggregates/user_status.py | 20 ++- .../src/aggregates/user_status_info.py | 20 --- .../src/contracts/requests.py | 21 ++- .../src/contracts/responses.py | 31 +---- .../src/contracts/results.py | 32 +---- 12 files changed, 340 insertions(+), 170 deletions(-) delete mode 100644 src/servers/presence_connection_manager/src/aggregates/user_status_info.py diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 0c529496a..505c20ee0 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -441,7 +441,8 @@ CREATE TABLE unispy.subprofiles ( cdkeyenc character varying, firewall smallint DEFAULT 0, port integer DEFAULT 0, - authtoken character varying + authtoken character varying, + session_key character varying ); diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index b775d2a61..8a7498cee 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -22,11 +22,11 @@ from sqlalchemy.orm import sessionmaker, declarative_base from servers.natneg.src.aggregations.enums import NatClientIndex, NatPortType +from servers.presence_connection_manager.src.aggregates.enums import FriendRequestStatus, GPStatusCode from servers.query_report.src.v2.aggregates.enums import GameServerStatus from sqlalchemy.orm.decl_api import DeclarativeBase - Base: DeclarativeMeta = declarative_base() @@ -56,46 +56,10 @@ class Profiles(Base): Integer, ForeignKey("users.userid"), nullable=False) nick: Column | str = Column(String, nullable=False) serverflag: Column | int = Column(Integer, nullable=False, default=0) - status: Column | int = Column(SmallInteger, default=0) + status: Column | GPStatusCode = Column(Enum(GPStatusCode), default=0) statstring: Column | str = Column(String, default="I love UniSpy") location: Column | str = Column(String) extra_info = Column(JSONB) - # firstname: Column | str = Column(String) - # lastname: Column | str = Column(String) - # publicmask: Column | int = Column(Integer, default=0) - # latitude: Column | float = Column(Double, default=0) - # longitude: Column | float = Column(Double, default=0) - # aim: Column | str = Column(String, default="") - # picture: Column | int = Column(Integer, default=0) - # occupationid: Column | int = Column(Integer, default=0) - # incomeid: Column | int = Column(Integer, default=0) - # industryid: Column | int = Column(Integer, default=0) - # marriedid: Column | int = Column(Integer, default=0) - # childcount: Column | int = Column(Integer, default=0) - # interests1: Column | int = Column(Integer, default=0) - # ownership1: Column | int = Column(Integer, default=0) - # connectiontype: Column | int = Column(Integer, default=0) - # sex: Column | int = Column(SmallInteger, default=0) - # zipcode: Column | str = Column(String, default="00000") - # countrycode: Column | str = Column(String, default="1") - # homepage: Column | str = Column(String, default="unispy.org") - # birthday: Column | int = Column(Integer, default=0) - # birthmonth: Column | int = Column(Integer, default=0) - # birthyear: Column | int = Column(Integer, default=0) - # icquin: Column | int = Column(Integer, default=0) - # quietflags: Column | int = Column(SmallInteger, nullable=False, default=0) - # streetaddr: Column | str = Column(Text) - # streeaddr: Column | str = Column(Text) - # city: Column | str = Column(Text) - # cpubrandid: Column | int = Column(Integer, default=0) - # cpuspeed: Column | int = Column(Integer, default=0) - # memory: Column | int = Column(SmallInteger, default=0) - # videocard1string: Column | str = Column(Text) - # videocard1ram: Column | int = Column(SmallInteger, default=0) - # videocard2string: Column | str = Column(Text) - # videocard2ram: Column | int = Column(SmallInteger, default=0) - # subscription: Column | int = Column(Integer, default=0) - # adminrights: Column | int = Column(Integer, default=0) class SubProfiles(Base): @@ -114,19 +78,7 @@ class SubProfiles(Base): firewall: Column | int = Column(SmallInteger, default=0) port: Column | int = Column(Integer, default=0) authtoken: Column | str = Column(String) - - -class AddRequest(Base): - __tablename__ = "addrequests" - - addrequestid = Column(Integer, primary_key=True, autoincrement=True) - profileid = Column(Integer, ForeignKey( - "profiles.profileid"), nullable=False) - namespaceid = Column(Integer, nullable=False) - targetid = Column(Integer, ForeignKey( - "profiles.profileid"), nullable=False) - reason = Column(String, nullable=False) - syncrequested = Column(String, nullable=False) + session_key: Column | str = Column(String) class Blocked(Base): @@ -145,8 +97,22 @@ class Friends(Base): friendid = Column(Integer, primary_key=True, autoincrement=True) profileid = Column(Integer, ForeignKey( "profiles.profileid"), nullable=False) - namespaceid = Column(Integer, nullable=False) targetid = Column(Integer, nullable=False) + namespaceid = Column(Integer, nullable=False) + + +class FriendAddRequest(Base): + __tablename__ = "addrequests" + + addrequestid = Column(Integer, primary_key=True, autoincrement=True) + profileid = Column(Integer, ForeignKey( + "profiles.profileid"), nullable=False) + targetid = Column(Integer, ForeignKey( + "profiles.profileid"), nullable=False) + namespaceid = Column(Integer, nullable=False) + reason = Column(String, nullable=False) + status = Column(Enum(FriendRequestStatus), nullable=False, + default=FriendRequestStatus.PENDING) class Games(Base): @@ -171,7 +137,7 @@ class Messages(Base): __tablename__ = "messages" messageid = Column(Integer, primary_key=True, autoincrement=True) - namespaceid = Column(Integer) + namespaceid = Column(Integer, nullable=False) type = Column(Integer) from_user = Column(Integer, nullable=False) to_user = Column(Integer, nullable=False) diff --git a/src/backends/protocols/gamespy/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py index 80428b4b5..dc1653cc4 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/data.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/data.py @@ -4,15 +4,17 @@ from sqlalchemy import Column from backends.library.database.pg_orm import ( Blocked, + FriendAddRequest, Friends, Profiles, SubProfiles, Users, PG_SESSION, ) -from servers.presence_connection_manager.src.aggregates.enums import LoginStatus -from servers.presence_connection_manager.src.contracts.results import LoginData -from servers.presence_search_player.src.aggregates.exceptions import GPDatabaseException +from servers.presence_connection_manager.src.aggregates.enums import GPStatusCode, LoginStatus +from servers.presence_connection_manager.src.aggregates.user_status import UserStatus +from servers.presence_connection_manager.src.contracts.results import GetProfileData, LoginData +from servers.presence_search_player.src.aggregates.exceptions import GPAddBuddyException, GPDatabaseException, GPStatusException, GPException def update_online_time(ip: str, port: int): @@ -69,7 +71,7 @@ def get_friend_profile_id_list(profile_id: int, namespace_id: int) -> list[int]: return result -def get_profile_info_list(profile_id: int, namespace_id: int): +def get_profile_infos(profile_id: int, session_key: str) -> GetProfileData: """ Retrieve profile information based on profile_id and namespace_id. @@ -84,16 +86,42 @@ def get_profile_info_list(profile_id: int, namespace_id: int): assert isinstance(Profiles.profileid, Column) assert isinstance(Profiles.userid, Column) assert isinstance(SubProfiles.namespaceid, Column) + assert isinstance(SubProfiles.session_key, Column) + + namespace_id = PG_SESSION.query(SubProfiles.namespaceid).where( + SubProfiles.session_key == session_key).first() + if namespace_id is None: + raise GPException("namespace not found") + result = ( - PG_SESSION.query(Profiles, SubProfiles, Users) + PG_SESSION.query(Users, Profiles, SubProfiles) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .join(Users, Profiles.userid == Users.userid) - .filter( + .where( Profiles.profileid == profile_id, SubProfiles.namespaceid == namespace_id ) .first() ) - return result + if result is None: + raise GPException("no profile found") + + if TYPE_CHECKING: + result = cast(tuple, result) + + user: Users = result[0] + profile: Profiles = result[1] + subprofile: SubProfiles = result[3] + if TYPE_CHECKING: + assert isinstance(profile.nick, str) + assert isinstance(profile.profileid, int) + assert isinstance(subprofile.uniquenick, str) + assert isinstance(user.email, str) + assert isinstance(Profiles.extra_info, dict) + + data = GetProfileData(nick=profile.nick, profile_id=profile.profileid, + unique_nick=subprofile.uniquenick, email=user.email, extra_infos=Profiles.extra_info) + + return data def get_user_info_list(email: str, nick_name: str) -> list[tuple[int, int, int]]: @@ -274,10 +302,41 @@ def get_user_infos_by_authtoken(auth_token: str) -> LoginData | None: return None -def update_block_info_list(target_id: int, profile_id: int, namespace_id: int) -> None: +def get_block_list(profile_id: int, namespace_id: int) -> list[int]: result = ( - PG_SESSION.query(Blocked) + PG_SESSION.query(Blocked.targetid) + .filter( + Blocked.namespaceid == namespace_id, + Blocked.profileid == profile_id, + ).all() + ) + if TYPE_CHECKING: + result = cast(list[int], result) + return result + + +def get_buddy_list(profile_id: int, namespace_id: int) -> list[int]: + result = ( + PG_SESSION.query(Friends.targetid) .filter( + Blocked.namespaceid == namespace_id, + Blocked.profileid == profile_id, + ).all() + ) + # assert isinstance(result, list) + if TYPE_CHECKING: + result = cast(list[int], result) + return result + + +def update_block(profile_id: int, target_id: int, session_key: str) -> None: + if TYPE_CHECKING: + assert isinstance(SubProfiles.session_key, Column) + namespace_id = PG_SESSION.query(SubProfiles).where( + SubProfiles.session_key == session_key).first() + result = ( + PG_SESSION.query(Blocked) + .where( Blocked.targetid == target_id, Blocked.namespaceid == namespace_id, Blocked.profileid == profile_id, @@ -350,3 +409,56 @@ def update_unique_nick(subprofile_id: int, unique_nick: str): def update_subprofile_info(subprofile: SubProfiles): PG_SESSION.add(subprofile) PG_SESSION.commit() + + +def add_friend_request(profileid: int, targetid: int, namespace_id: int, reason: str) -> None: + data = PG_SESSION.query(FriendAddRequest).where(FriendAddRequest.profileid == + profileid, + FriendAddRequest.targetid == targetid, + FriendAddRequest.namespaceid == namespace_id).first() + if data is not None: + raise GPAddBuddyException("Request is existed, add friend ignored") + request = FriendAddRequest(profileid=profileid, targetid=targetid, + namespaceid=namespace_id, reason=reason) + PG_SESSION.add(request) + PG_SESSION.commit() + + +def get_status(session_key: str) -> UserStatus: + if TYPE_CHECKING: + assert isinstance(SubProfiles.session_key, Column) + + result = PG_SESSION.query(Profiles).join(SubProfiles).where( + SubProfiles.session_key == session_key).first() + if result is None: + raise GPStatusException( + "No profile found with the provided session key") + + if TYPE_CHECKING: + assert isinstance(result.statstring, str) + assert isinstance(result.location, str) + assert isinstance(result.status, GPStatusCode) + + data = UserStatus(status_string=result.statstring, + location_string=result.location, current_status=result.status) + return data + + +def update_status(session_key: str, status: UserStatus): + if TYPE_CHECKING: + assert isinstance(SubProfiles.session_key, Column) + result = PG_SESSION.query(Profiles).join(SubProfiles).where( + SubProfiles.session_key == session_key).first() + if result is None: + raise GPStatusException( + "No profile found with the provided session key") + + result.statstring = status.status_string + result.status = status.current_status + result.location = status.location_string + + PG_SESSION.commit() + + +if __name__ == "__main__": + result = get_block_list(1, 1) diff --git a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py index dacafc18c..63c3b2917 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py @@ -1,9 +1,10 @@ +from backends.library.abstractions.contracts import RequestBase from backends.library.abstractions.handler_base import HandlerBase from backends.protocols.gamespy.presence_connection_manager.requests import * import backends.protocols.gamespy.presence_connection_manager.data as data -from servers.presence_connection_manager.src.aggregates.enums import LoginStatus -from servers.presence_connection_manager.src.contracts.results import LoginResult +from servers.presence_connection_manager.src.contracts.results import BlockListResult, BuddyListResult, GetProfileResult, LoginResult, StatusResult from servers.presence_search_player.src.aggregates.exceptions import GPLoginBadEmailException +# region General class KeepAliveHandler(HandlerBase): @@ -54,3 +55,124 @@ class LogoutHandler(HandlerBase): async def _data_operate(self) -> None: # data.update_online_status(user_id=, status=LoginStatus.DISCONNECTED) raise NotImplementedError() + + +class NewUserHandler(HandlerBase): + def __init__(self, request: NewUserRequest) -> None: + raise NotImplementedError("Use presence search player newuser router") + super().__init__(request) + # region Profile + + # region Buddy + + +class BuddyListHandler(HandlerBase): + _request: BuddyListRequest + + async def _data_operate(self) -> None: + self.data = data.get_buddy_list( + self._request.profile_id, self._request.namespace_id) + + async def _result_construct(self) -> None: + self._result = BuddyListResult(profile_ids=self.data) + + +class BlockListHandler(HandlerBase): + _request: BlockListRequest + + async def _data_operate(self) -> None: + self.data = data.get_block_list( + self._request.profile_id, self._request.namespace_id) + + async def _result_construct(self) -> None: + self._result = BlockListResult(profile_ids=self.data) + + +class BuddyStatusInfoHandler(HandlerBase): + """ + This is what the message should look like. Its broken up for easy viewing. + + "\\bsi\\\\state\\\\profile\\\\bip\\\\bport\\\\hostip\\\\hprivip\\" + "\\qport\\\\hport\\\\sessflags\\\\rstatus\\\\gameType\\" + "\\gameVnt\\\\gameMn\\\\product\\\\qmodeflags\\" + """ + + def __init__(self, request: RequestBase) -> None: + super().__init__(request) + raise NotImplementedError() + + +class DelBuddyHandler(HandlerBase): + _request: DelBuddyRequest + + async def _data_operate(self) -> None: + self.data = data.delete_friend_by_profile_id( + self._request.target_id) + + +class AddBuddyHandler(HandlerBase): + _request: AddBuddyRequest + + async def _data_operate(self) -> None: + data.add_friend_request( + self._request.profile_id, + self._request.target_id, + self._request.namespace_id, + self._request.reason) + + +class AddBlockHandler(HandlerBase): + _request: AddBlockRequest + + async def _data_operate(self) -> None: + data.update_block(self._request.profile_id, + self._request.taget_id, self._request.session_key) + + +class InviteToHandler(HandlerBase): + _request: InviteToRequest + + async def _data_operate(self) -> None: + # user is offline + # if (client is null) + # { + # return; + # } + # else + # { + + # } + # TODO + # parse user to buddy message system + raise NotImplementedError() + + +class StatusHandler(HandlerBase): + _request: StatusRequest + + async def _data_operate(self) -> None: + if self._request.is_get: + self.data = data.get_status( + self._request.session_key) + else: + data.update_status(self._request.session_key, self._request.status) + + async def _result_construct(self) -> None: + + if self._request.is_get: + assert isinstance(self.data, UserStatus) + self._result = StatusResult(status=self.data) + + +# region Profile + +class GetProfileHandler(HandlerBase): + _request: GetProfileRequest + + async def _data_operate(self) -> None: + self.data = data.get_profile_infos( + profile_id=self._request.profile_id, session_key=self._request.session_key) + + async def _result_construct(self) -> None: + self._result = GetProfileResult(user_profile=self.data) + diff --git a/src/backends/protocols/gamespy/presence_connection_manager/requests.py b/src/backends/protocols/gamespy/presence_connection_manager/requests.py index d6b0d0bc0..829c6aa64 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/requests.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/requests.py @@ -1,35 +1,50 @@ from typing import Optional, Union from pydantic import UUID4, BaseModel -from servers.presence_connection_manager.src.aggregates.user_status import UserStatus -from servers.presence_connection_manager.src.aggregates.user_status_info import ( - UserStatusInfo, -) + from servers.presence_connection_manager.src.aggregates.enums import ( LoginType, PublicMasks, SdkRevisionType, ) -import backends.library.abstractions.contracts as lib - - -class RequestBase(BaseModel): - server_id: UUID4 +from backends.library.abstractions.contracts import RequestBase +from servers.presence_connection_manager.src.aggregates.user_status import UserStatus, UserStatusInfo class ErrorOnParse(RequestBase): raw_request: str +class AddBlockRequest(RequestBase): + taget_id: int + profile_id: int + session_key: str + # region buddy + + +class BuddyListRequest(RequestBase): + profile_id: int + namespace_id: int + + +class BlockListRequest(RequestBase): + profile_id: int + namespace_id: int + + class AddBuddyRequest(RequestBase): - friend_profile_id: int + profile_id: int + target_id: int + namespace_id: int reason: str class DelBuddyRequest(RequestBase): - friend_profile_id: int + profile_id: int + target_id: int + namespace_id: int class InviteToRequest(RequestBase): @@ -40,15 +55,16 @@ class InviteToRequest(RequestBase): class StatusInfoRequest(RequestBase): - is_get_status_info: bool + is_get: bool profile_id: int namespace_id: int status_info: UserStatusInfo class StatusRequest(RequestBase): + session_key: str status: UserStatus - is_get_status: bool + is_get: bool # region general @@ -105,10 +121,6 @@ class LogoutRequest(RequestBase): # region profile -class AddBlockRequest(RequestBase): - taget_id: int - - class GetProfileRequest(RequestBase): profile_id: int session_key: str diff --git a/src/servers/presence_connection_manager/src/abstractions/contracts.py b/src/servers/presence_connection_manager/src/abstractions/contracts.py index 215447bdc..7acc124aa 100644 --- a/src/servers/presence_connection_manager/src/abstractions/contracts.py +++ b/src/servers/presence_connection_manager/src/abstractions/contracts.py @@ -40,8 +40,7 @@ def parse(self): self.operation_id = int(self.request_dict["id"]) except: raise GPParseException("namespaceid is invalid.") - - + class ResultBase(library.src.abstractions.contracts.ResultBase): pass diff --git a/src/servers/presence_connection_manager/src/aggregates/enums.py b/src/servers/presence_connection_manager/src/aggregates/enums.py index 134bd808f..d25982492 100644 --- a/src/servers/presence_connection_manager/src/aggregates/enums.py +++ b/src/servers/presence_connection_manager/src/aggregates/enums.py @@ -254,3 +254,9 @@ class RequestType(Enum): STATUS = "status" STATUSINFO = "statusinfo" INVITETO = "inviteto" + + +class FriendRequestStatus(Enum): + PENDING = 0 + ACCEPTED = 1 + REJECTED = 2 diff --git a/src/servers/presence_connection_manager/src/aggregates/user_status.py b/src/servers/presence_connection_manager/src/aggregates/user_status.py index 592be000e..d4c142524 100644 --- a/src/servers/presence_connection_manager/src/aggregates/user_status.py +++ b/src/servers/presence_connection_manager/src/aggregates/user_status.py @@ -1,10 +1,24 @@ +from pydantic import BaseModel -from dataclasses import dataclass from servers.presence_connection_manager.src.aggregates.enums import GPStatusCode -@dataclass -class UserStatus: +class UserStatus(BaseModel): status_string: str location_string: str current_status: GPStatusCode = GPStatusCode.OFFLINE + + +class UserStatusInfo(BaseModel): + status_state: str + buddy_ip: str + host_ip: str + host_private_ip: str + query_report_port: int + host_port: int + session_flags: str + rich_status: str + game_type: str + game_variant: str + game_map_name: str + quiet_mode_flags: str diff --git a/src/servers/presence_connection_manager/src/aggregates/user_status_info.py b/src/servers/presence_connection_manager/src/aggregates/user_status_info.py deleted file mode 100644 index 59ed82a48..000000000 --- a/src/servers/presence_connection_manager/src/aggregates/user_status_info.py +++ /dev/null @@ -1,20 +0,0 @@ - -from pydantic import BaseModel - - -class UserStatusInfo(BaseModel): - status_state: str - buddy_ip: str - host_ip: str - host_private_ip: str - query_report_port: int - host_port: int - session_flags: str - rich_status: str - game_type: str - game_variant: str - game_map_name: str - quiet_mode_flags: str - - def __init__(self) -> None: - pass diff --git a/src/servers/presence_connection_manager/src/contracts/requests.py b/src/servers/presence_connection_manager/src/contracts/requests.py index d49736dc7..efe81f282 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests.py +++ b/src/servers/presence_connection_manager/src/contracts/requests.py @@ -1,12 +1,10 @@ +from pydantic import BaseModel from library.src.exceptions.general import UniSpyException from servers.presence_connection_manager.src.aggregates.enums import PublicMasks from typing import Optional, final from library.src.extentions.gamespy_utils import convert_to_key_value from servers.presence_connection_manager.src.abstractions.contracts import RequestBase -from servers.presence_connection_manager.src.aggregates.user_status import UserStatus -from servers.presence_connection_manager.src.aggregates.user_status_info import ( - UserStatusInfo, -) +from servers.presence_connection_manager.src.aggregates.user_status import UserStatus, UserStatusInfo from servers.presence_connection_manager.src.aggregates.enums import GPStatusCode from servers.presence_search_player.src.aggregates.exceptions import GPParseException from typing import final @@ -69,7 +67,7 @@ def validate_extra_infos(info_dict: dict) -> dict: """ assert isinstance(info_dict, dict) extra_infos = {} - for key, value in info_dict.values(): + for key, value in info_dict.items(): if key in EXTRA_INFO_DICT and key not in extra_infos: if not isinstance(value, EXTRA_INFO_DICT[key]): converted_value = value @@ -167,6 +165,8 @@ def parse_other_data(self): if "partnerid" in self.request_dict: partner_id = int(self.request_dict["partnerid"]) self.partner_id = partner_id + else: + self.partner_id = 0 if "sdkrevision" in self.request_dict: sdk_revision_type = int(self.request_dict["sdkrevision"]) @@ -243,6 +243,8 @@ def parse_other_info(self): self.has_partner_id_flag = True except ValueError: raise GPParseException("partnerid is incorrect.") + else: + self.partner_id = 0 # set default partner id to 0 means gamespy if "productid" in self.request_dict: try: @@ -335,7 +337,7 @@ def parse(self): @final class StatusInfoRequest(RequestBase): - namespace_id: Optional[int] + namespace_id: int status_info: UserStatusInfo profile_id: int @@ -373,6 +375,11 @@ def parse(self): raise GPParseException( "qport, hport, or sessflags format is incorrect.") + if "namespace_id" in self.request_dict: + self.namespace_id = int(self.request_dict["namespaceid"]) + else: + self.namespace_id = 0 + self.status_info.rich_status = self.request_dict["rechstatus"] self.status_info.game_type = self.request_dict["gametype"] self.status_info.game_variant = self.request_dict["gamevariant"] @@ -382,7 +389,7 @@ def parse(self): @final class StatusRequest(RequestBase): status: UserStatus - is_get_status: bool + is_get: bool def parse(self): self.request_dict = convert_to_key_value(self.raw_request) diff --git a/src/servers/presence_connection_manager/src/contracts/responses.py b/src/servers/presence_connection_manager/src/contracts/responses.py index ba7cf3833..445e1589c 100644 --- a/src/servers/presence_connection_manager/src/contracts/responses.py +++ b/src/servers/presence_connection_manager/src/contracts/responses.py @@ -176,37 +176,12 @@ def build(self): + f"\\nick\\{self._result.user_profile.nick}" + f"\\uniquenick\\{self._result.user_profile.unique_nick}" + f"\\email\\{self._result.user_profile.email}" - + f"\\firstname\\{self._result.user_profile.firstname}" - + f"\\lastname\\{self._result.user_profile.lastname}" - + f"\\icquin\\{self._result.user_profile.icquin}" - + f"\\homepage\\{self._result.user_profile.homepage}" - + f"\\zipcode\\{self._result.user_profile.zipcode}" - + f"\\countrycode\\{self._result.user_profile.countrycode}" - + f"\\lon\\{self._result.user_profile.longitude}" - + f"\\lat\\{self._result.user_profile.latitude}" - + f"\\loc\\{self._result.user_profile.location}" ) + for key,value in self._result.user_profile.extra_infos.items(): + self.sending_buffer += f"\\{key}\\{value}" - birth_str = ( - (self._result.user_profile.birthday << 24) - or (self._result.user_profile.birthmonth << 16) - or self._result.user_profile.birthyear - ) self.sending_buffer += ( - f"\\birthday\\{birth_str}" - f"\\sex\\{self._result.user_profile.sex}" - + f"\\publicmask\\{self._result.user_profile.publicmask}" - + f"\\aim\\{self._result.user_profile.aim}" - + f"\\picture\\{self._result.user_profile.picture}" - + f"\\ooc{self._result.user_profile.occupationid}" - + f"\\ind\\{self._result.user_profile.industryid}" - + f"\\inc\\{self._result.user_profile.incomeid}" - + f"\\mar\\{self._result.user_profile.marriedid}" - + f"\\chc\\{self._result.user_profile.childcount}" - + f"\\i1\\{self._result.user_profile.interests1}" - + f"\\o1\\{self._result.user_profile.ownership1}" - + f"\\conn\\{self._result.user_profile.connectiontype}" - + f"\\sig\\+{generate_random_string(10, StringType.HEX)}" + f"\\sig\\+{generate_random_string(10, StringType.HEX)}" + f"\\id\\{self._request.operation_id}\\final\\" ) diff --git a/src/servers/presence_connection_manager/src/contracts/results.py b/src/servers/presence_connection_manager/src/contracts/results.py index 817558cbd..b18c9ebc9 100644 --- a/src/servers/presence_connection_manager/src/contracts/results.py +++ b/src/servers/presence_connection_manager/src/contracts/results.py @@ -1,5 +1,4 @@ -from typing import Optional -from servers.presence_connection_manager.src.aggregates.user_status_info import ( +from servers.presence_connection_manager.src.aggregates.user_status import ( UserStatusInfo, ) from servers.presence_connection_manager.src.aggregates.user_status import UserStatus @@ -61,39 +60,16 @@ class StatusResult(ResultBase): # region Profile -class GetProfileDataModel(BaseModel): +class GetProfileData(BaseModel): nick: str profile_id: int unique_nick: str email: str - firstname: str - lastname: str - icquin: int - homepage: str - zipcode: str - countrycode: str - longitude: float - latitude: float - location: str - birthday: int - birthmonth: int - birthyear: int - sex: int - publicmask: int - aim: str - picture: int - occupationid: int - industryid: int - incomeid: int - marriedid: int - childcount: int - interests1: int - ownership1: int - connectiontype: int + extra_infos: dict class GetProfileResult(ResultBase): - user_profile: GetProfileDataModel + user_profile: GetProfileData class NewProfileResult(ResultBase): From c9ddb307dc04112d17beb38ef4a10ce6c74db5dd Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 18 Dec 2024 10:36:11 +0000 Subject: [PATCH 146/231] refactor: update backends handlers --- common/UniSpy_pg.sql | 9 +- src/.vscode/settings.json | 2 +- src/backends/library/database/pg_orm.py | 9 +- .../protocols/gamespy/game_status/data.py | 14 +- .../protocols/gamespy/game_status/handlers.py | 79 ++++++++++++ .../protocols/gamespy/game_status/requests.py | 2 +- .../presence_connection_manager/data.py | 60 ++++++++- .../presence_connection_manager/handlers.py | 49 +++++++ .../presence_connection_manager/requests.py | 35 +---- .../protocols/gamespy/query_report/data.py | 43 ++++-- .../protocols/gamespy/server_browser/data.py | 7 + .../gamespy/server_browser/handlers.py | 79 ++++++++++++ .../gamespy/server_browser/requests.py | 32 ++--- .../protocols/gamespy/web_services/data.py | 112 ++++++++++++++++ .../gamespy/web_services/handlers.py | 122 ++++++++++++++++++ .../gamespy/web_services/requests.py | 25 +--- src/backends/routers/gamespy/gstats.py | 4 +- src/backends/routers/gamespy/webservices.py | 18 +-- .../src/contracts/requests.py | 4 +- .../src/v2/contracts/responses.py | 2 +- .../src/v2/contracts/results.py | 17 ++- 21 files changed, 611 insertions(+), 113 deletions(-) create mode 100644 src/backends/protocols/gamespy/server_browser/handlers.py create mode 100644 src/backends/protocols/gamespy/web_services/handlers.py diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 505c20ee0..8c3cb3ab1 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -300,7 +300,7 @@ CREATE TABLE unispy.profiles ( serverflag integer DEFAULT 0 NOT NULL, status smallint DEFAULT 0, statstring character varying DEFAULT 'I love UniSpy'::character varying, - extra_info jsonb + extra_info JSONB ); @@ -346,7 +346,7 @@ CREATE TABLE unispy.pstorage ( profileid integer NOT NULL, ptype integer NOT NULL, dindex integer NOT NULL, - data jsonb + data JSONB ); @@ -388,8 +388,9 @@ ALTER SEQUENCE unispy.pstorage_pstorageid_seq OWNED BY unispy.pstorage.pstoragei -- CREATE TABLE unispy.sakestorage ( - sakestorageid integer NOT NULL, - tableid character varying NOT NULL + sakestorageid integer NOT NULL + tableid integer NOT NULL + data JSONB ); diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index cbe3eecc2..459e8e9f1 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -1,5 +1,5 @@ { - "python.analysis.typeCheckingMode": "basic", + "python.analysis.typeCheckingMode": "off", "workbench.iconTheme": "material-icon-theme", "python.testing.unittestArgs": [ "-v", diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 8a7498cee..5b3add527 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -59,7 +59,7 @@ class Profiles(Base): status: Column | GPStatusCode = Column(Enum(GPStatusCode), default=0) statstring: Column | str = Column(String, default="I love UniSpy") location: Column | str = Column(String) - extra_info = Column(JSONB) + extra_info: Column[JSONB] = Column(JSONB) class SubProfiles(Base): @@ -68,8 +68,8 @@ class SubProfiles(Base): subprofileid = Column( Integer, ForeignKey("profiles.profileid"), primary_key=True, autoincrement=True ) - profileid: Column | int = Column(Integer, nullable=False) - uniquenick: Column | str = Column(String) + profileid: Column[Integer] = Column(Integer, nullable=False) + uniquenick: Column[str] = Column(String) namespaceid: Column | int = Column(Integer, nullable=False, default=0) partnerid: Column | int = Column(Integer, nullable=False, default=0) productid: Column | int = Column(Integer) @@ -167,7 +167,8 @@ class SakeStorage(Base): __tablename__ = "sakestorage" sakestorageid = Column(Integer, primary_key=True, autoincrement=True) - tableid = Column(String, nullable=False) + tableid = Column(Integer, nullable=False) + data: Column[JSONB] = Column(JSONB) class InitPacketCaches(Base): diff --git a/src/backends/protocols/gamespy/game_status/data.py b/src/backends/protocols/gamespy/game_status/data.py index a9036ce13..eff6cc899 100644 --- a/src/backends/protocols/gamespy/game_status/data.py +++ b/src/backends/protocols/gamespy/game_status/data.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, cast +from typing import TYPE_CHECKING, cast, overload from sqlalchemy import Column from backends.library.database.pg_orm import PG_SESSION, PStorage, Profiles, SubProfiles, Users @@ -18,7 +18,8 @@ def update_player_data(): raise NotImplementedError() -def get_profile_id_by_token(token: str) -> int: +@overload +def get_profile_id(token: str) -> int: if TYPE_CHECKING: assert isinstance(SubProfiles.profileid, Column) assert isinstance(SubProfiles.authtoken, Column) @@ -32,6 +33,15 @@ def get_profile_id_by_token(token: str) -> int: return result +@overload +def get_profile_id(profile_id: int): + result = PG_SESSION.query(SubProfiles.profileid).where( + SubProfiles.profileid == profile_id).count() + if result != 1: + raise GSException(f"There is no profile_id {profile_id} existed") + + +@overload def get_profile_id(cdkey: str, nick_name: str) -> int: if TYPE_CHECKING: assert isinstance(Profiles.profileid, Column) diff --git a/src/backends/protocols/gamespy/game_status/handlers.py b/src/backends/protocols/gamespy/game_status/handlers.py index e69de29bb..1be56a619 100644 --- a/src/backends/protocols/gamespy/game_status/handlers.py +++ b/src/backends/protocols/gamespy/game_status/handlers.py @@ -0,0 +1,79 @@ + + +from backends.library.abstractions.handler_base import HandlerBase +from backends.protocols.gamespy.game_status.requests import * +import backends.protocols.gamespy.game_status.data as data +from servers.game_status.src.aggregations.exceptions import GSException +from servers.game_status.src.contracts.results import AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult + + +class AuthGameHandler(HandlerBase): + _request: AuthGameRequest + + +class AuthPlayerHandler(HandlerBase): + _request: AuthPlayerRequest + + async def _data_operate(self): + match self._request.auth_type: + case AuthMethod.PARTNER_ID_AUTH: + self.data = data.get_profile_id(token=self._request.auth_token) + case AuthMethod.PROFILE_ID_AUTH: + self.data = data.get_profile_id( + profile_id=self._request.profile_id) + case AuthMethod.CDKEY_AUTH: + self.data = data.get_profile_id( + cdkey=self._request.cdkey_hash, nick_name=self._request.nick) + case _: + raise GSException("Invalid auth type") + + async def _result_construct(self): + self._result = AuthPlayerResult(profile_id=self.data) + + +class GetPlayerDataHandler(HandlerBase): + _request: GetPlayerDataRequest + + async def _data_operate(self): + self.data = data.get_player_data( + self._request.profile_id, + self._request.storage_type, + self._request.data_index) + + async def _result_construct(self): + self._result = GetPlayerDataResult(keyvalues=self.data) + + +class GetProfileIdHandler(HandlerBase): + _request: GetProfileIdRequest + + async def _data_operate(self): + self.data = data.get_profile_id( + cdkey=self._request.cdkey, nick_name=self._request.nick) + + async def _result_construct(self): + self._result = GetProfileIdResult(profile_id=self.data) + + +class NewGameHandler(HandlerBase): + _request: NewGameRequest + """ + find game based on the session key, and create a space for the game data + """ + + async def _data_operate(self): + self.data = data.create_new_game_data() + + +class SetPlayerDataHandler(HandlerBase): + _request: SetPlayerDataRequest + + async def _data_operate(self): + raise NotImplementedError() + + +class UpdateGameHandler(HandlerBase): + _request: SetPlayerDataRequest + + async def _data_operate(self): + raise NotImplementedError() diff --git a/src/backends/protocols/gamespy/game_status/requests.py b/src/backends/protocols/gamespy/game_status/requests.py index 712dc3313..b6e03c203 100644 --- a/src/backends/protocols/gamespy/game_status/requests.py +++ b/src/backends/protocols/gamespy/game_status/requests.py @@ -34,7 +34,7 @@ class GetPlayerDataRequest(RequestBase): class GetProfileIdRequest(RequestBase): nick: str - keyhash: str + cdkey: str class NewGameRequest(RequestBase): diff --git a/src/backends/protocols/gamespy/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py index dc1653cc4..b6ce5fb3b 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/data.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/data.py @@ -1,3 +1,4 @@ +# type:ignore from datetime import datetime from typing import TYPE_CHECKING, cast @@ -16,6 +17,8 @@ from servers.presence_connection_manager.src.contracts.results import GetProfileData, LoginData from servers.presence_search_player.src.aggregates.exceptions import GPAddBuddyException, GPDatabaseException, GPStatusException, GPException +# region General + def update_online_time(ip: str, port: int): if TYPE_CHECKING: @@ -70,6 +73,8 @@ def get_friend_profile_id_list(profile_id: int, namespace_id: int) -> list[int]: result = cast(list[int], result) return result +# region Profile + def get_profile_infos(profile_id: int, session_key: str) -> GetProfileData: """ @@ -314,6 +319,8 @@ def get_block_list(profile_id: int, namespace_id: int) -> list[int]: result = cast(list[int], result) return result +# region Buddy + def get_buddy_list(profile_id: int, namespace_id: int) -> list[int]: result = ( @@ -391,9 +398,9 @@ def add_nick_name(profile_id: int, old_nick: str, new_nick: str): PG_SESSION.commit() -def update_profile_info(profile: Profiles): - PG_SESSION.add(profile) - PG_SESSION.commit() +# def update_profile_info(profile: Profiles): +# PG_SESSION.add(profile) +# PG_SESSION.commit() def update_unique_nick(subprofile_id: int, unique_nick: str): @@ -460,5 +467,52 @@ def update_status(session_key: str, status: UserStatus): PG_SESSION.commit() +def update_new_nick(session_key: str, old_nick: str, new_nick: str): + result = PG_SESSION.query(Profiles).join(SubProfiles).where( + SubProfiles.session_key == session_key).first() + if result.nick == old_nick and result.nick != new_nick: + result.nick = new_nick + PG_SESSION.commit() + + +def update_cdkey(session_key: str, cdkey: str): + subprofile = PG_SESSION.query(SubProfiles).where( + SubProfiles.session_key == session_key).first() + if subprofile is None: + raise GPDatabaseException( + f"no subprofile found with session key:{session_key}") + + subprofile.cdkeyenc = cdkey + + PG_SESSION.commit() + + +def update_uniquenick(session_key: str, uniquenick: str): + subprofile = PG_SESSION.query(SubProfiles).where( + SubProfiles.session_key == session_key).first() + if subprofile is None: + raise GPDatabaseException( + f"no subprofile found with session key:{session_key}") + + subprofile.uniquenick = uniquenick + PG_SESSION.commit() + + +def update_profiles(session_key: str, extra_info: dict): + profile = PG_SESSION.query(Profiles).join(SubProfiles).where( + SubProfiles.session_key == session_key).first() + if profile is None: + raise GPDatabaseException( + f"no profile found with session key:{session_key}") + for key, value in extra_info.items(): + profile.extra_info[key] = value + + PG_SESSION.commit() + + +def update_user(session_key): + raise NotImplementedError() + + if __name__ == "__main__": result = get_block_list(1, 1) diff --git a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py index 63c3b2917..2e15bd963 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py @@ -176,3 +176,52 @@ async def _data_operate(self) -> None: async def _result_construct(self) -> None: self._result = GetProfileResult(user_profile=self.data) + +class NewProfileHandler(HandlerBase): + """ + update a exist profile + """ + _request: NewProfileRequest + + async def _data_operate(self) -> None: + data.update_new_nick( + self._request.session_key, self._request.old_nick, self._request.new_nick) + + +class RegisterCDKeyHandler(HandlerBase): + _request: RegisterCDKeyRequest + + async def _data_operate(self): + data.update_cdkey(self._request.session_key, self._request.cdkey_enc) + + +class RegisterNickHandler(HandlerBase): + """ + some game will not register uniquenick when create a new account, it will update its uniquenick later + """ + _request: RegisterNickRequest + + async def _data_operate(self): + data.update_uniquenick(self._request.session_key, + self._request.unique_nick) + + +class RemoveBlockHandler(HandlerBase): + async def _data_operate(self): + raise NotImplementedError() + + +class UpdateProfileHandler(HandlerBase): + _request: UpdateProfileRequest + + async def _data_operate(self): + data.update_profiles(self._request.session_key, + self._request.extra_infos) + + +class UpdateUserInfoHandler(HandlerBase): + _request: UpdateUserInfoRequest + + async def _data_operate(self): + data.update_profiles(self._request.session_key, + self._request.extra_infos) diff --git a/src/backends/protocols/gamespy/presence_connection_manager/requests.py b/src/backends/protocols/gamespy/presence_connection_manager/requests.py index 829c6aa64..dcac9f15b 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/requests.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/requests.py @@ -145,34 +145,13 @@ class RegisterNickRequest(RequestBase): class UpdateProfileRequest(RequestBase): - has_public_mask_flag: Optional[bool] = None - public_mask: Optional[PublicMasks] = None - session_key: Optional[str] = None - partner_id: Optional[int] = None - nick: Optional[str] = None - uniquenick: Optional[str] = None - has_first_name_flag: Optional[bool] = None - first_name: Optional[str] = None - has_last_name_flag: Optional[bool] = None - last_name: Optional[str] = None - has_icq_flag: Optional[bool] = None - icq_uin: Optional[int] = None - has_home_page_flag: Optional[bool] = None - home_page: Optional[str] = None - has_birthday_flag: Optional[bool] = False - birth_day: Optional[int] = None - birth_month: Optional[int] = None - birth_year: Optional[int] = None - has_sex_flag: Optional[bool] = False - sex: Optional[bool] = None - has_zip_code: Optional[bool] = False - zip_code: Optional[str] = None - has_country_code: Optional[bool] = False - country_code: Optional[str] = None - - -class UpdateUIRequest(RequestBase): - extra_info: dict + session_key: str + extra_infos: dict + + +class UpdateUserInfoRequest(RequestBase): + session_key: str + extra_infos: dict if __name__ == "__main__": diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index 4f5963d0a..cb8b499c2 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -1,7 +1,9 @@ from typing import TYPE_CHECKING, Optional, cast from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, GroupList, Games, GameServerCaches from servers.chat.src.aggregates.peer_room import PeerRoom - +from servers.query_report.src.aggregates.game_server_info import GameServerInfo +from servers.query_report.src.aggregates.peer_room_info import PeerRoomInfo +from servers.server_browser.src.aggregates.exceptions import ServerBrowserException def get_all_groups() -> dict: @@ -32,47 +34,66 @@ def get_all_groups() -> dict: # Convert the grouped result to the desired format return grouped_result + PEER_GROUP_LIST = get_all_groups() -def get_peer_staging_channels(game_name: str, group_id: int) -> list[ChatChannelCaches]: + +def get_peer_staging_channels(game_name: str, group_id: int) -> list[GameServerInfo]: assert isinstance(game_name, str) assert isinstance(group_id, int) staging_name = f"{PeerRoom.StagingRoomPrefix}!{game_name}!*" result = PG_SESSION.query(ChatChannelCaches).filter( ChatChannelCaches.channel_name == staging_name).all() - return result + data = [] + for s in result: + data.append(GameServerInfo(**s)) + return data -def get_peer_group_channel(group_id: int) -> list[ChatChannelCaches]: +def get_peer_group_channel(group_id: int) -> list[GameServerInfo]: assert isinstance(group_id, int) group_name = f"{PeerRoom.GroupRoomPrefix}!{group_id}" result = PG_SESSION.query(ChatChannelCaches).filter( ChatChannelCaches.channel_name == group_name).all() - return result + data = [] + for s in result: + data.append(PeerRoomInfo(**s)) + return data -def get_server_info_with_instant_key(instant_key: int) -> Optional[GameServerCaches]: +def get_server_info_with_instant_key(instant_key: int) -> Optional[GameServerInfo]: assert isinstance(instant_key, int) result = PG_SESSION.query(GameServerCaches).filter( GameServerCaches.instant_key == instant_key).first() return result -def get_server_info_with_game_name(game_name: str) -> Optional[GameServerCaches]: +def get_server_info_with_game_name(game_name: str) -> Optional[GameServerInfo]: assert isinstance(game_name, str) - result = PG_SESSION.query(GameServerCaches).filter( + result = PG_SESSION.query(GameServerCaches).where( GameServerCaches.game_name == game_name).first() return result -def get_server_info_with_ip_and_port(ip: str, port: int) -> GameServerCaches: +def get_server_info_list_with_game_name(game_name: str) -> list[GameServerInfo]: + result = PG_SESSION.query(GameServerCaches).where( + GameServerCaches.game_name == game_name).all() + data = [] + for s in result: + data.append(GameServerInfo(**s)) + return data + + +def get_server_info_with_ip_and_port(ip: str, port: int) -> GameServerInfo: assert isinstance(ip, str) assert isinstance(port, int) result = PG_SESSION.query(GameServerCaches).filter( GameServerCaches.host_ip_address == ip, GameServerCaches.query_report_port == port).first() - - return result + if result is None: + raise ServerBrowserException("game server not found") + data = GameServerInfo(**result) + return data def remove_server_info(info: GameServerCaches) -> None: diff --git a/src/backends/protocols/gamespy/server_browser/data.py b/src/backends/protocols/gamespy/server_browser/data.py index e69de29bb..765d6fca8 100644 --- a/src/backends/protocols/gamespy/server_browser/data.py +++ b/src/backends/protocols/gamespy/server_browser/data.py @@ -0,0 +1,7 @@ + + +# region V1 + + +# region V2 + diff --git a/src/backends/protocols/gamespy/server_browser/handlers.py b/src/backends/protocols/gamespy/server_browser/handlers.py new file mode 100644 index 000000000..5fc97274d --- /dev/null +++ b/src/backends/protocols/gamespy/server_browser/handlers.py @@ -0,0 +1,79 @@ +from backends.library.abstractions.handler_base import HandlerBase +from backends.protocols.gamespy.server_browser.requests import * +import backends.protocols.gamespy.query_report.data as data +from servers.query_report.src.aggregates.game_server_info import GameServerInfo +from servers.query_report.src.aggregates.peer_room_info import PeerRoomInfo +from servers.server_browser.src.aggregates.exceptions import ServerBrowserException +from servers.server_browser.src.v2.aggregations.enums import GameServerFlags, ServerListUpdateOption +from servers.server_browser.src.v2.contracts.results import P2PGroupRoomListResult, SendMessageResult, ServerInfoResult, ServerMainListResult + +# region Server list + + +class ServerListHandler(HandlerBase): + _request: ServerListRequest + + async def _data_operate(self): + if self._request.update_option in\ + [ServerListUpdateOption.SERVER_MAIN_LIST, + ServerListUpdateOption.P2P_SERVER_MAIN_LIST, + ServerListUpdateOption.LIMIT_RESULT_COUNT, + ServerListUpdateOption.SERVER_FULL_INFO_LIST,]: + + self.data = data.get_server_info_list_with_game_name( + self._request.game_name) + + elif self._request.update_option == ServerListUpdateOption.P2P_GROUP_ROOM_LIST: + self.data = data.get_peer_group_channel + else: + raise ServerBrowserException( + "invalid server browser update option") + + async def _result_construct(self): + if self._request.update_option in\ + [ServerListUpdateOption.SERVER_MAIN_LIST, + ServerListUpdateOption.P2P_SERVER_MAIN_LIST, + ServerListUpdateOption.LIMIT_RESULT_COUNT, + ServerListUpdateOption.SERVER_FULL_INFO_LIST,]: + assert isinstance(self.data, GameServerInfo) + self._result = ServerMainListResult(client_remote_ip=self._request.client_ip, + flag=GameServerFlags.HAS_KEYS_FLAG, game_secret_key="", servers_info=self.data) + elif self._request.update_option == ServerListUpdateOption.P2P_GROUP_ROOM_LIST: + assert isinstance(self.data, PeerRoomInfo) + self._result = P2PGroupRoomListResult( + client_remote_ip=self._request.client_ip, flag=GameServerFlags.HAS_KEYS_FLAG, game_secret_key="", peer_room_info=self.data) + else: + raise ServerBrowserException( + "invalid server browser update option") + +# region Adhoc + + +class AdHocHandler(HandlerBase): + _request: AdHocRequestBase + + async def _data_operate(self): + raise NotImplementedError() + + +class SendMessageHandler(HandlerBase): + _request: SendMessageRequest + + async def _data_operate(self): + self.data = data.get_server_info_with_ip_and_port( + self._request.game_server_public_ip, self._request.game_server_public_port) + + async def _result_construct(self): + self._result = SendMessageResult(sb_sender_id=self._request.server_id, + natneg_message=self._request.client_message, server_info=self.data) + + +class ServerInfoHandler(HandlerBase): + _request: ServerInfoRequest + + async def _data_operate(self) -> None: + self.data = data.get_server_info_with_ip_and_port( + self._request.game_server_public_ip, self._request.game_server_public_port) + + async def _result_construct(self) -> None: + self._result = ServerInfoResult(game_server_info=self.data) diff --git a/src/backends/protocols/gamespy/server_browser/requests.py b/src/backends/protocols/gamespy/server_browser/requests.py index 697fdd97b..c75bdd152 100644 --- a/src/backends/protocols/gamespy/server_browser/requests.py +++ b/src/backends/protocols/gamespy/server_browser/requests.py @@ -11,18 +11,18 @@ class RequestBase(lib.RequestBase): class ServerListUpdateOptionRequestBase(RequestBase): source_ip: str - request_version: Optional[int] = None - protocol_version: Optional[int] = None - encoding_version: Optional[int] = None - game_version: Optional[int] = None - query_options: Optional[int] = None - dev_game_name: Optional[str] = None - game_name: Optional[str] = None - client_challenge: Optional[str] = None - update_option: Optional[ServerListUpdateOption] = None - keys: Optional[List[str]] = None - filter: Optional[str] = None - max_servers: Optional[int] = None + request_version: int + protocol_version: int + encoding_version: int + game_version: int + query_options: int + dev_game_name: str + game_name: str + client_challenge: str + update_option: ServerListUpdateOption + keys: list[str] + filter: list[str] + max_servers: int class ServerListRequest(ServerListUpdateOptionRequestBase): @@ -30,13 +30,13 @@ class ServerListRequest(ServerListUpdateOptionRequestBase): class AdHocRequestBase(RequestBase): - game_server_public_ip: list[int] - game_server_public_port: list[int] + game_server_public_ip: str + game_server_public_port: int class SendMessageRequest(AdHocRequestBase): - prefix_message: list[int] - client_message: list[int] + prefix_message: str + client_message: str class ServerInfoRequest(AdHocRequestBase): diff --git a/src/backends/protocols/gamespy/web_services/data.py b/src/backends/protocols/gamespy/web_services/data.py index e69de29bb..cc70137d2 100644 --- a/src/backends/protocols/gamespy/web_services/data.py +++ b/src/backends/protocols/gamespy/web_services/data.py @@ -0,0 +1,112 @@ +# region altas + +# region auth + +from typing import TYPE_CHECKING, cast, overload +from backends.library.database.pg_orm import PG_SESSION, Profiles, SubProfiles, Users, SakeStorage +from servers.web_services.src.aggregations.exceptions import WebException +from servers.web_services.src.modules.auth.exceptions.general import AuthException +from servers.web_services.src.modules.sake.exceptions.general import SakeException + + +def is_user_exist(uniquenick: str, cdkey: str, partner_id: int, namespace_id: int, email: str, password: str) -> None: + result = PG_SESSION.query(Profiles).join(Users).join(SubProfiles).where(SubProfiles.uniquenick == uniquenick, + SubProfiles.cdkeyenc == cdkey, SubProfiles.partnerid == partner_id, SubProfiles.namespaceid == namespace_id, Users.email == email, Users.password == password).first() + if result is None: + raise AuthException( + "No account exists with the provided email address.") + + +@overload +def get_info(uniquenick: str, namespace_id: int, cdkey: str, email: str) -> tuple[int, int, str, str, str]: + """ + return [user_id,profile_id,profile_nick,unique_nick,cdkey_hash] + """ + result = PG_SESSION.query(Users, Profiles, SubProfiles).join(Users, (Users.userid, Profiles.userid)).join( + Profiles, (Profiles.profileid, SubProfiles.profileid)).where(SubProfiles.uniquenick == uniquenick, SubProfiles.namespaceid == namespace_id, SubProfiles.cdkeyenc == cdkey, Users.email == email).first() + + if result is None: + raise AuthException( + "No account exists with the provided uniquenick and namespace id.") + user: Users = result[0] + profile: Profiles = result[1] + subprofile: SubProfiles = result[2] + return user.userid, profile.profileid, profile.nick, subprofile.uniquenick, subprofile.cdkeyenc + + +@overload +def get_info(auth_token: str) -> tuple[int, int, str, str, str]: + """ + return [user_id,profile_id,profile_nick,unique_nick,cdkey_hash] + """ + result = PG_SESSION.query(Users, Profiles, SubProfiles).join(Users, (Users.userid, Profiles.userid)).join( + Profiles, (Profiles.profileid, SubProfiles.profileid)).where(SubProfiles.authtoken == auth_token).first() + if result is None: + raise AuthException( + "No account exists with the provided authtoken.") + user: Users = result[0] + profile: Profiles = result[1] + subprofile: SubProfiles = result[2] + return user.userid, profile.profileid, profile.nick, subprofile.uniquenick, subprofile.cdkeyenc + + +@overload +def get_info(uniquenick: str, namespace_id: int) -> tuple[int, int, str, str, str]: + """ + return [user_id,profile_id,profile_nick,unique_nick,cdkey_hash] + """ + result = PG_SESSION.query(Users, Profiles, SubProfiles).join(Users, (Users.userid, Profiles.userid)).join( + Profiles, (Profiles.profileid, SubProfiles.profileid)).where(SubProfiles.uniquenick == uniquenick, SubProfiles.namespaceid == namespace_id).first() + + if result is None: + raise AuthException( + "No account exists with the provided uniquenick and namespace id.") + user: Users = result[0] + profile: Profiles = result[1] + subprofile: SubProfiles = result[2] + return user.userid, profile.profileid, profile.nick, subprofile.uniquenick, subprofile.cdkeyenc + +# region d2g + +# region ingamead + +# region patching and tracking + +# region racing + +# region sake + + +def get_user_data(table_id: int,) -> dict: + result = PG_SESSION.query(SakeStorage.data).where( + SakeStorage.tableid == table_id).first() + + if TYPE_CHECKING: + result = cast(dict, result) + return result + + +def update_user_data(table_id: int, data: dict) -> None: + result = PG_SESSION.query(SakeStorage).where( + SakeStorage.tableid == table_id).first() + + for key, value in result.data.items(): + if key in data: + if data[key] is None or data[key] is "": + raise SakeException(f"the value of {key} should not be None.") + if value == data[key]: + continue + result.data[key] = data[key] + + +def create_records(table_id: int, data: dict) -> None: + result = PG_SESSION.query(SakeStorage).where( + SakeStorage.tableid == table_id).count() + + if result != 0: + raise SakeException("Records already existed") + + sake = SakeStorage(table_id=table_id, data=data) + + PG_SESSION.add(sake) + PG_SESSION.commit() diff --git a/src/backends/protocols/gamespy/web_services/handlers.py b/src/backends/protocols/gamespy/web_services/handlers.py new file mode 100644 index 000000000..550fc4d3d --- /dev/null +++ b/src/backends/protocols/gamespy/web_services/handlers.py @@ -0,0 +1,122 @@ +# region altas + +# region auth +from backends.library.abstractions.contracts import RequestBase +from backends.library.abstractions.handler_base import HandlerBase +from backends.protocols.gamespy.web_services.requests import * +import backends.protocols.gamespy.web_services.data as data +from servers.web_services.src.modules.auth.contracts.results import LoginProfileResult +from servers.web_services.src.modules.direct2game.contracts.results import GetPurchaseHistoryResult, GetStoreAvailabilityResult + + +class LoginProfileHandler(HandlerBase): + _request: LoginProfileRequest + + async def _data_operate(self) -> None: + self.data = data.get_info(uniquenick=self._request.uniquenick, + namespace_id=self._request.namespace_id, + cdkey=self._request.cdkey, + email=self._request.email) + + async def _result_construct(self) -> None: + self._result = LoginProfileResult( + user_id=self.data[0], + profile_id=self.data[1], + profile_nick=self.data[2], + unique_nick=self.data[3], + cdkey_hash=self.data[4]) + + +class LoginPS3CertHandler(HandlerBase): + _request: LoginPS3CertRequest + + async def _data_operate(self) -> None: + raise NotImplementedError() + + +class LoginRemoteAuthHandler(HandlerBase): + _request: LoginRemoteAuthRequest + + async def _data_operate(self) -> None: + self.data = data.get_info(auth_token=self._request.auth_token) + + async def _result_construct(self) -> None: + self._result = LoginProfileResult( + user_id=self.data[0], + profile_id=self.data[1], + profile_nick=self.data[2], + unique_nick=self.data[3], + cdkey_hash=self.data[4]) + + +class LoginUniqueNickHandler(HandlerBase): + _request: LoginUniqueNickRequest + + async def _data_operate(self) -> None: + self.data = data.get_info(uniquenick=self._request.uniquenick, + namespace_id=self._request.namespace_id) + + async def _result_construct(self) -> None: + self._result = LoginProfileResult( + user_id=self.data[0], + profile_id=self.data[1], + profile_nick=self.data[2], + unique_nick=self.data[3], + cdkey_hash=self.data[4]) +# region d2g + + +class GetPurchaceHistoryHandler(HandlerBase): + _request: GetPurchaseHistoryRequest + + async def _result_construct(self) -> None: + self._result = GetPurchaseHistoryResult() + + +class GetStoreAvailabilityHandler(HandlerBase): + _request: GetStoreAvailabilityRequest + + async def _result_construct(self) -> None: + self._result = GetStoreAvailabilityResult() + +# region ingamead + + +class GetTargettedAdHandler(HandlerBase): + def __init__(self, request: RequestBase) -> None: + super().__init__(request) + raise NotImplementedError() + + +class ReportAdUsageRequest(HandlerBase): + def __init__(self, request: RequestBase) -> None: + super().__init__(request) + raise NotImplementedError() + +# region patching and tracking + +# region racing + +# region sake + + +class CreateRecordHandler(HandlerBase): + _request: CreateRecordRequest + + async def _data_operate(self) -> None: + raise NotImplementedError() + + +class GetMyRecordsHandler(HandlerBase): + _request: GetMyRecordsRequest + + async def _data_operate(self): + self.data = data.get_user_data(self._request.table_id) + raise NotImplementedError() + + +class SearchForRecordsHandler(HandlerBase): + _request: SearchForRecordsRequest + + async def _data_operate(self) -> None: + return await super()._data_operate() diff --git a/src/backends/protocols/gamespy/web_services/requests.py b/src/backends/protocols/gamespy/web_services/requests.py index 03e1a5d13..9c1588279 100644 --- a/src/backends/protocols/gamespy/web_services/requests.py +++ b/src/backends/protocols/gamespy/web_services/requests.py @@ -1,3 +1,4 @@ +from typing import Optional from pydantic import BaseModel import backends.library.abstractions.contracts as lib @@ -95,6 +96,7 @@ class AuthRequestBase(lib.RequestBase): version: int partner_code: int namespace_id: int + game_id: Optional[int] = None class LoginProfileRequest(AuthRequestBase): @@ -104,51 +106,36 @@ class LoginProfileRequest(AuthRequestBase): password: str -class LoginProfileWithGameIdRequest(LoginProfileRequest): - game_id: int - - -class LoginPs3CertRequest(AuthRequestBase): +class LoginPS3CertRequest(AuthRequestBase): ps3_cert: str - game_id: int npticket: str -class LoginPs3CertWithGameIdRequest(LoginPs3CertRequest): - game_id: int - - class LoginRemoteAuthRequest(AuthRequestBase): auth_token: str challenge: str -class LoginRemoteAuthWithGameIdRequest(LoginRemoteAuthRequest): - game_id: int - - class LoginUniqueNickRequest(AuthRequestBase): uniquenick: str password: str -class LoginUniqueNickWithGameIdRequest(LoginUniqueNickRequest): - game_id: int - - # D2G class Direct2GameRequestBase(lib.RequestBase): pass + class GetPurchaseHistoryRequest(Direct2GameRequestBase): game_id: int access_token: str proof: str certificate: str + class GetStoreAvailabilityRequest(Direct2GameRequestBase): game_id: int version: int region: str - access_token: str \ No newline at end of file + access_token: str diff --git a/src/backends/routers/gamespy/gstats.py b/src/backends/routers/gamespy/gstats.py index aa7fa9185..4d019514f 100644 --- a/src/backends/routers/gamespy/gstats.py +++ b/src/backends/routers/gamespy/gstats.py @@ -1,6 +1,7 @@ from fastapi import APIRouter -from backends.protocols.gamespy.game_status.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest +from backends.protocols.gamespy.game_status.requests import * from backends.urls import GAMESTATUS + router = APIRouter() @@ -8,7 +9,6 @@ async def auth_game(request: AuthGameRequest): raise NotImplementedError() - @router.post(f"{GAMESTATUS}/AuthPlayerHandler") async def auth_player(request: AuthPlayerRequest): raise NotImplementedError() diff --git a/src/backends/routers/gamespy/webservices.py b/src/backends/routers/gamespy/webservices.py index e67e4f49e..51305a6b4 100644 --- a/src/backends/routers/gamespy/webservices.py +++ b/src/backends/routers/gamespy/webservices.py @@ -1,11 +1,12 @@ from fastapi import APIRouter from backends.urls import WEB_SERVICES -from backends.protocols.gamespy.web_services.requests import CreateRecordRequest, GetMyRecordsRequest, LoginProfileRequest, LoginProfileWithGameIdRequest, LoginRemoteAuthRequest, LoginRemoteAuthWithGameIdRequest, LoginUniqueNickRequest, LoginUniqueNickWithGameIdRequest, SearchForRecordsRequest +from backends.protocols.gamespy.web_services.requests import CreateRecordRequest, GetMyRecordsRequest, LoginProfileRequest, LoginRemoteAuthRequest, LoginUniqueNickRequest, SearchForRecordsRequest router = APIRouter() # Altas services + @router.post(f"{WEB_SERVICES}/Altas/CreateRecordHandler") async def create_matchless_session(request): raise NotImplementedError() @@ -32,31 +33,16 @@ async def login_profile(request: LoginProfileRequest): raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/Auth/LoginProfileWithGameIdHandler") -async def login_profile_with_game_id(request: LoginProfileWithGameIdRequest): - raise NotImplementedError() - - @router.post(f"{WEB_SERVICES}/Auth/LoginRemoteAuthHandler") async def login_remote_auth(request: LoginRemoteAuthRequest): raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/Auth/LoginRemoteAuthWithGameIdHandler") -async def login_remote_auth_with_game_id(request: LoginRemoteAuthWithGameIdRequest): - raise NotImplementedError() - - @router.post(f"{WEB_SERVICES}/Auth/LoginUniqueNickHandler") async def login_uniquenick(request: LoginUniqueNickRequest): raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/Auth/LoginUniqueNickWithGameIdHandler") -async def login_uniquenick_with_game_id(request: LoginUniqueNickWithGameIdRequest): - raise NotImplementedError() - - # SAKE services @router.post(f"{WEB_SERVICES}/Sake/CreateRecordHandler") async def create_record(request: CreateRecordRequest): diff --git a/src/servers/presence_connection_manager/src/contracts/requests.py b/src/servers/presence_connection_manager/src/contracts/requests.py index efe81f282..fbdeb3b61 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests.py +++ b/src/servers/presence_connection_manager/src/contracts/requests.py @@ -565,8 +565,8 @@ def parse(self): @final class UpdateUserInfoRequest(RequestBase): - extra_info: dict[str, str] + extra_infos: dict[str, str] def parse(self): super().parse() - self.extra_info = validate_extra_infos(self.request_dict) + self.extra_infos = validate_extra_infos(self.request_dict) diff --git a/src/servers/server_browser/src/v2/contracts/responses.py b/src/servers/server_browser/src/v2/contracts/responses.py index 10712faed..d53b23ea7 100644 --- a/src/servers/server_browser/src/v2/contracts/responses.py +++ b/src/servers/server_browser/src/v2/contracts/responses.py @@ -86,7 +86,7 @@ def build(self) -> None: self.sending_buffer = bytes(self._servers_info_buffers) def _build_servers_full_info(self): - for room in self._result.peer_room_infos: + for room in self._result.peer_room_info: self._servers_info_buffers.append(GameServerFlags.HAS_KEYS_FLAG) group_id_bytes = room.group_id.to_bytes() self._servers_info_buffers.extend(group_id_bytes) diff --git a/src/servers/server_browser/src/v2/contracts/results.py b/src/servers/server_browser/src/v2/contracts/results.py index d998f38d1..c966da522 100644 --- a/src/servers/server_browser/src/v2/contracts/results.py +++ b/src/servers/server_browser/src/v2/contracts/results.py @@ -1,5 +1,10 @@ +from datetime import datetime +from uuid import UUID + +from pydantic import BaseModel from servers.query_report.src.aggregates.game_server_info import GameServerInfo from servers.query_report.src.aggregates.peer_room_info import PeerRoomInfo +from servers.query_report.src.v2.aggregates.enums import GameServerStatus from servers.server_browser.src.v2.abstractions.contracts import ( AdHocResultBase, ResultBase, @@ -11,9 +16,15 @@ class ServerInfoResult(AdHocResultBase): pass -class P2PGroupRoomListResult(ResultBase): - peer_room_infos: list[PeerRoomInfo] +class P2PGroupRoomListResult(ServerListUpdateOptionResultBase): + peer_room_info: list[PeerRoomInfo] class ServerMainListResult(ServerListUpdateOptionResultBase): - servers_info: list[GameServerInfo] = [] + servers_info: list[GameServerInfo] + + +class SendMessageResult(ResultBase): + sb_sender_id: UUID + natneg_message: str + server_info: GameServerInfo From 4baea2d902e1763f2550cfa0136e399f5755f38e Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 3 Jan 2025 12:05:52 +0800 Subject: [PATCH 147/231] update(db): postgresql creation script --- common/UniSpy_pg.sql | 921 +++++++++++++++++++++++++++---------------- common/config.json | 5 +- 2 files changed, 582 insertions(+), 344 deletions(-) diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 8c3cb3ab1..cb5f13739 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -2,10 +2,8 @@ -- PostgreSQL database dump -- --- Dumped from database version 14.2 (Debian 14.2-1.pgdg110+1) --- Dumped by pg_dump version 14.1 - --- Started on 2022-03-02 20:15:24 CET +-- Dumped from database version 14.13 (Debian 14.13-1.pgdg120+1) +-- Dumped by pg_dump version 14.13 (Debian 14.13-1.pgdg120+1) SET statement_timeout = 0; SET lock_timeout = 0; @@ -19,53 +17,112 @@ SET client_min_messages = warning; SET row_security = off; -- --- TOC entry 5 (class 2615 OID 16385) --- Name: unispy; Type: SCHEMA; Schema: -; Owner: - +-- Name: unispy; Type: SCHEMA; Schema: -; Owner: unispy -- CREATE SCHEMA unispy; +ALTER SCHEMA unispy OWNER TO unispy; + -- --- TOC entry 3466 (class 0 OID 0) --- Dependencies: 5 --- Name: SCHEMA unispy; Type: COMMENT; Schema: -; Owner: - +-- Name: SCHEMA unispy; Type: COMMENT; Schema: -; Owner: unispy -- COMMENT ON SCHEMA unispy IS 'standard public schema'; +-- +-- Name: friendrequeststatus; Type: TYPE; Schema: unispy; Owner: unispy +-- + +CREATE TYPE unispy.friendrequeststatus AS ENUM ( + 'PENDING', + 'ACCEPTED', + 'REJECTED' +); + + +ALTER TYPE unispy.friendrequeststatus OWNER TO unispy; + +-- +-- Name: gameserverstatus; Type: TYPE; Schema: unispy; Owner: unispy +-- + +CREATE TYPE unispy.gameserverstatus AS ENUM ( + 'NORMAL', + 'UPDATE', + 'SHUTDOWN', + 'PLAYING' +); + + +ALTER TYPE unispy.gameserverstatus OWNER TO unispy; + +-- +-- Name: gpstatuscode; Type: TYPE; Schema: unispy; Owner: unispy +-- + +CREATE TYPE unispy.gpstatuscode AS ENUM ( + 'OFFLINE', + 'ONLINE', + 'PLAYING', + 'STAGING', + 'CHATTING', + 'AWAY' +); + + +ALTER TYPE unispy.gpstatuscode OWNER TO unispy; + +-- +-- Name: natclientindex; Type: TYPE; Schema: unispy; Owner: unispy +-- + +CREATE TYPE unispy.natclientindex AS ENUM ( + 'GAME_CLIENT', + 'GAME_SERVER' +); + + +ALTER TYPE unispy.natclientindex OWNER TO unispy; + +-- +-- Name: natporttype; Type: TYPE; Schema: unispy; Owner: unispy +-- + +CREATE TYPE unispy.natporttype AS ENUM ( + 'GP', + 'NN1', + 'NN2', + 'NN3' +); + + +ALTER TYPE unispy.natporttype OWNER TO unispy; + SET default_tablespace = ''; SET default_table_access_method = heap; -- --- TOC entry 209 (class 1259 OID 16386) --- Name: addrequests; Type: TABLE; Schema: unispy; Owner: - +-- Name: addrequests; Type: TABLE; Schema: unispy; Owner: unispy -- CREATE TABLE unispy.addrequests ( addrequestid integer NOT NULL, profileid integer NOT NULL, - namespaceid integer NOT NULL, targetid integer NOT NULL, + namespaceid integer NOT NULL, reason character varying NOT NULL, - syncrequested character varying NOT NULL + status unispy.friendrequeststatus NOT NULL ); --- --- TOC entry 3467 (class 0 OID 0) --- Dependencies: 209 --- Name: TABLE addrequests; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.addrequests IS 'Friend request.'; - +ALTER TABLE unispy.addrequests OWNER TO unispy; -- --- TOC entry 210 (class 1259 OID 16391) --- Name: addrequests_addrequestid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- Name: addrequests_addrequestid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- CREATE SEQUENCE unispy.addrequests_addrequestid_seq @@ -77,18 +134,17 @@ CREATE SEQUENCE unispy.addrequests_addrequestid_seq CACHE 1; +ALTER TABLE unispy.addrequests_addrequestid_seq OWNER TO unispy; + -- --- TOC entry 3468 (class 0 OID 0) --- Dependencies: 210 --- Name: addrequests_addrequestid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- Name: addrequests_addrequestid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy -- ALTER SEQUENCE unispy.addrequests_addrequestid_seq OWNED BY unispy.addrequests.addrequestid; -- --- TOC entry 211 (class 1259 OID 16392) --- Name: blocked; Type: TABLE; Schema: unispy; Owner: - +-- Name: blocked; Type: TABLE; Schema: unispy; Owner: unispy -- CREATE TABLE unispy.blocked ( @@ -99,18 +155,10 @@ CREATE TABLE unispy.blocked ( ); --- --- TOC entry 3469 (class 0 OID 0) --- Dependencies: 211 --- Name: TABLE blocked; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.blocked IS 'Block list.'; - +ALTER TABLE unispy.blocked OWNER TO unispy; -- --- TOC entry 212 (class 1259 OID 16395) --- Name: blocked_blockid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- Name: blocked_blockid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- CREATE SEQUENCE unispy.blocked_blockid_seq @@ -122,43 +170,137 @@ CREATE SEQUENCE unispy.blocked_blockid_seq CACHE 1; +ALTER TABLE unispy.blocked_blockid_seq OWNER TO unispy; + -- --- TOC entry 3470 (class 0 OID 0) --- Dependencies: 212 --- Name: blocked_blockid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- Name: blocked_blockid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy -- ALTER SEQUENCE unispy.blocked_blockid_seq OWNED BY unispy.blocked.blockid; -- --- TOC entry 213 (class 1259 OID 16396) --- Name: friends; Type: TABLE; Schema: unispy; Owner: - +-- Name: chat_channel_caches; Type: TABLE; Schema: unispy; Owner: unispy +-- + +CREATE TABLE unispy.chat_channel_caches ( + channel_name character varying NOT NULL, + server_id uuid NOT NULL, + game_name character varying NOT NULL, + room_name character varying NOT NULL, + topic character varying, + password character varying, + group_id integer NOT NULL, + max_num_user integer NOT NULL, + key_values jsonb, + invited_nicks jsonb, + update_time timestamp without time zone NOT NULL +); + + +ALTER TABLE unispy.chat_channel_caches OWNER TO unispy; + +-- +-- Name: chat_nick_caches; Type: TABLE; Schema: unispy; Owner: unispy +-- + +CREATE TABLE unispy.chat_nick_caches ( + server_id uuid NOT NULL, + nick_name character varying NOT NULL, + game_name character varying, + user_name character varying, + remote_ip_address inet NOT NULL, + remote_port integer NOT NULL, + key_value jsonb, + update_time timestamp without time zone NOT NULL +); + + +ALTER TABLE unispy.chat_nick_caches OWNER TO unispy; + +-- +-- Name: chat_user_caches; Type: TABLE; Schema: unispy; Owner: unispy +-- + +CREATE TABLE unispy.chat_user_caches ( + nick_name character varying NOT NULL, + channel_name character varying NOT NULL, + server_id uuid NOT NULL, + user_name character varying NOT NULL, + update_time timestamp without time zone NOT NULL, + is_voiceable boolean NOT NULL, + is_channel_operator boolean NOT NULL, + is_channel_creator boolean NOT NULL, + remote_ip_address inet NOT NULL, + remote_port integer NOT NULL, + key_values jsonb +); + + +ALTER TABLE unispy.chat_user_caches OWNER TO unispy; + +-- +-- Name: friends; Type: TABLE; Schema: unispy; Owner: unispy -- CREATE TABLE unispy.friends ( friendid integer NOT NULL, profileid integer NOT NULL, - namespaceid integer NOT NULL, - targetid integer NOT NULL + targetid integer NOT NULL, + namespaceid integer NOT NULL ); +ALTER TABLE unispy.friends OWNER TO unispy; + +-- +-- Name: friends_friendid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy +-- + +CREATE SEQUENCE unispy.friends_friendid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE unispy.friends_friendid_seq OWNER TO unispy; + +-- +-- Name: friends_friendid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy +-- + +ALTER SEQUENCE unispy.friends_friendid_seq OWNED BY unispy.friends.friendid; + + -- --- TOC entry 3471 (class 0 OID 0) --- Dependencies: 213 --- Name: TABLE friends; Type: COMMENT; Schema: unispy; Owner: - +-- Name: game_server_caches; Type: TABLE; Schema: unispy; Owner: unispy -- -COMMENT ON TABLE unispy.friends IS 'Friend list.'; +CREATE TABLE unispy.game_server_caches ( + instant_key integer NOT NULL, + server_id uuid NOT NULL, + host_ip_address inet NOT NULL, + game_name character varying NOT NULL, + query_report_port integer NOT NULL, + update_time timestamp without time zone NOT NULL, + status unispy.gameserverstatus, + player_data jsonb NOT NULL, + server_data jsonb NOT NULL, + team_data jsonb NOT NULL, + avaliable boolean +); + +ALTER TABLE unispy.game_server_caches OWNER TO unispy; -- --- TOC entry 214 (class 1259 OID 16399) --- Name: friends_friendid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- Name: game_server_caches_instant_key_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- -CREATE SEQUENCE unispy.friends_friendid_seq +CREATE SEQUENCE unispy.game_server_caches_instant_key_seq AS integer START WITH 1 INCREMENT BY 1 @@ -167,18 +309,17 @@ CREATE SEQUENCE unispy.friends_friendid_seq CACHE 1; +ALTER TABLE unispy.game_server_caches_instant_key_seq OWNER TO unispy; + -- --- TOC entry 3472 (class 0 OID 0) --- Dependencies: 214 --- Name: friends_friendid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- Name: game_server_caches_instant_key_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy -- -ALTER SEQUENCE unispy.friends_friendid_seq OWNED BY unispy.friends.friendid; +ALTER SEQUENCE unispy.game_server_caches_instant_key_seq OWNED BY unispy.game_server_caches.instant_key; -- --- TOC entry 215 (class 1259 OID 16400) --- Name: games; Type: TABLE; Schema: unispy; Owner: - +-- Name: games; Type: TABLE; Schema: unispy; Owner: unispy -- CREATE TABLE unispy.games ( @@ -190,18 +331,17 @@ CREATE TABLE unispy.games ( ); +ALTER TABLE unispy.games OWNER TO unispy; + -- --- TOC entry 3473 (class 0 OID 0) --- Dependencies: 215 --- Name: TABLE games; Type: COMMENT; Schema: unispy; Owner: - +-- Name: TABLE games; Type: COMMENT; Schema: unispy; Owner: unispy -- COMMENT ON TABLE unispy.games IS 'Game list.'; -- --- TOC entry 216 (class 1259 OID 16405) --- Name: grouplist; Type: TABLE; Schema: unispy; Owner: - +-- Name: grouplist; Type: TABLE; Schema: unispy; Owner: unispy -- CREATE TABLE unispy.grouplist ( @@ -211,46 +351,117 @@ CREATE TABLE unispy.grouplist ( ); +ALTER TABLE unispy.grouplist OWNER TO unispy; + -- --- TOC entry 3474 (class 0 OID 0) --- Dependencies: 216 --- Name: TABLE grouplist; Type: COMMENT; Schema: unispy; Owner: - +-- Name: TABLE grouplist; Type: COMMENT; Schema: unispy; Owner: unispy -- COMMENT ON TABLE unispy.grouplist IS 'Old games use grouplist to create their game rooms.'; -- --- TOC entry 217 (class 1259 OID 16410) --- Name: messages; Type: TABLE; Schema: unispy; Owner: - +-- Name: init_packet_caches; Type: TABLE; Schema: unispy; Owner: unispy +-- + +CREATE TABLE unispy.init_packet_caches ( + cookie integer NOT NULL, + server_id uuid NOT NULL, + version integer NOT NULL, + port_type unispy.natporttype NOT NULL, + client_index unispy.natclientindex NOT NULL, + game_name character varying NOT NULL, + use_game_port boolean NOT NULL, + public_ip character varying NOT NULL, + public_port integer NOT NULL, + private_ip character varying NOT NULL, + private_port integer NOT NULL, + update_time timestamp without time zone NOT NULL +); + + +ALTER TABLE unispy.init_packet_caches OWNER TO unispy; + +-- +-- Name: init_packet_caches_cookie_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy +-- + +CREATE SEQUENCE unispy.init_packet_caches_cookie_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +ALTER TABLE unispy.init_packet_caches_cookie_seq OWNER TO unispy; + +-- +-- Name: init_packet_caches_cookie_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy +-- + +ALTER SEQUENCE unispy.init_packet_caches_cookie_seq OWNED BY unispy.init_packet_caches.cookie; + + +-- +-- Name: messages; Type: TABLE; Schema: unispy; Owner: unispy -- CREATE TABLE unispy.messages ( messageid integer NOT NULL, - namespaceid integer, + namespaceid integer NOT NULL, type integer, - "from" integer NOT NULL, - "to" integer NOT NULL, - date timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL, - message character varying NOT NULL + from_user integer NOT NULL, + to_user integer NOT NULL, + date timestamp without time zone NOT NULL, + message text NOT NULL ); +ALTER TABLE unispy.messages OWNER TO unispy; + -- --- TOC entry 3475 (class 0 OID 0) --- Dependencies: 217 --- Name: TABLE messages; Type: COMMENT; Schema: unispy; Owner: - +-- Name: messages_messageid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- -COMMENT ON TABLE unispy.messages IS 'Friend messages.'; +CREATE SEQUENCE unispy.messages_messageid_seq + AS integer + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + +ALTER TABLE unispy.messages_messageid_seq OWNER TO unispy; -- --- TOC entry 218 (class 1259 OID 16416) --- Name: messages_messageid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- Name: messages_messageid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy -- -CREATE SEQUENCE unispy.messages_messageid_seq +ALTER SEQUENCE unispy.messages_messageid_seq OWNED BY unispy.messages.messageid; + + +-- +-- Name: nat_fail_cachess; Type: TABLE; Schema: unispy; Owner: unispy +-- + +CREATE TABLE unispy.nat_fail_cachess ( + record_id integer NOT NULL, + public_ip_address1 inet NOT NULL, + public_ip_address2 inet NOT NULL, + update_time timestamp without time zone NOT NULL +); + + +ALTER TABLE unispy.nat_fail_cachess OWNER TO unispy; + +-- +-- Name: nat_fail_cachess_record_id_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy +-- + +CREATE SEQUENCE unispy.nat_fail_cachess_record_id_seq AS integer START WITH 1 INCREMENT BY 1 @@ -259,18 +470,17 @@ CREATE SEQUENCE unispy.messages_messageid_seq CACHE 1; +ALTER TABLE unispy.nat_fail_cachess_record_id_seq OWNER TO unispy; + -- --- TOC entry 3476 (class 0 OID 0) --- Dependencies: 218 --- Name: messages_messageid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- Name: nat_fail_cachess_record_id_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy -- -ALTER SEQUENCE unispy.messages_messageid_seq OWNED BY unispy.messages.messageid; +ALTER SEQUENCE unispy.nat_fail_cachess_record_id_seq OWNED BY unispy.nat_fail_cachess.record_id; -- --- TOC entry 219 (class 1259 OID 16417) --- Name: partner; Type: TABLE; Schema: unispy; Owner: - +-- Name: partner; Type: TABLE; Schema: unispy; Owner: unispy -- CREATE TABLE unispy.partner ( @@ -279,43 +489,34 @@ CREATE TABLE unispy.partner ( ); +ALTER TABLE unispy.partner OWNER TO unispy; + -- --- TOC entry 3477 (class 0 OID 0) --- Dependencies: 219 --- Name: TABLE partner; Type: COMMENT; Schema: unispy; Owner: - +-- Name: TABLE partner; Type: COMMENT; Schema: unispy; Owner: unispy -- COMMENT ON TABLE unispy.partner IS 'Partner information, these information are used for authentication and login.'; -- --- TOC entry 220 (class 1259 OID 16422) --- Name: profiles; Type: TABLE; Schema: unispy; Owner: - +-- Name: profiles; Type: TABLE; Schema: unispy; Owner: unispy -- CREATE TABLE unispy.profiles ( profileid integer NOT NULL, userid integer NOT NULL, nick character varying NOT NULL, - serverflag integer DEFAULT 0 NOT NULL, - status smallint DEFAULT 0, - statstring character varying DEFAULT 'I love UniSpy'::character varying, - extra_info JSONB + serverflag integer NOT NULL, + status integer, + statstring character varying, + extra_info jsonb ); --- --- TOC entry 3478 (class 0 OID 0) --- Dependencies: 220 --- Name: TABLE profiles; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.profiles IS 'User profiles.'; - +ALTER TABLE unispy.profiles OWNER TO unispy; -- --- TOC entry 221 (class 1259 OID 16459) --- Name: profiles_profileid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- Name: profiles_profileid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- CREATE SEQUENCE unispy.profiles_profileid_seq @@ -327,18 +528,17 @@ CREATE SEQUENCE unispy.profiles_profileid_seq CACHE 1; +ALTER TABLE unispy.profiles_profileid_seq OWNER TO unispy; + -- --- TOC entry 3479 (class 0 OID 0) --- Dependencies: 221 --- Name: profiles_profileid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- Name: profiles_profileid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy -- ALTER SEQUENCE unispy.profiles_profileid_seq OWNED BY unispy.profiles.profileid; -- --- TOC entry 222 (class 1259 OID 16460) --- Name: pstorage; Type: TABLE; Schema: unispy; Owner: - +-- Name: pstorage; Type: TABLE; Schema: unispy; Owner: unispy -- CREATE TABLE unispy.pstorage ( @@ -346,22 +546,14 @@ CREATE TABLE unispy.pstorage ( profileid integer NOT NULL, ptype integer NOT NULL, dindex integer NOT NULL, - data JSONB + data jsonb ); --- --- TOC entry 3480 (class 0 OID 0) --- Dependencies: 222 --- Name: TABLE pstorage; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.pstorage IS 'Old games use pstorage to store game data.'; - +ALTER TABLE unispy.pstorage OWNER TO unispy; -- --- TOC entry 223 (class 1259 OID 16465) --- Name: pstorage_pstorageid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- Name: pstorage_pstorageid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- CREATE SEQUENCE unispy.pstorage_pstorageid_seq @@ -373,39 +565,51 @@ CREATE SEQUENCE unispy.pstorage_pstorageid_seq CACHE 1; +ALTER TABLE unispy.pstorage_pstorageid_seq OWNER TO unispy; + -- --- TOC entry 3481 (class 0 OID 0) --- Dependencies: 223 --- Name: pstorage_pstorageid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- Name: pstorage_pstorageid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy -- ALTER SEQUENCE unispy.pstorage_pstorageid_seq OWNED BY unispy.pstorage.pstorageid; -- --- TOC entry 224 (class 1259 OID 16466) --- Name: sakestorage; Type: TABLE; Schema: unispy; Owner: - +-- Name: relay_server_caches; Type: TABLE; Schema: unispy; Owner: unispy +-- + +CREATE TABLE unispy.relay_server_caches ( + server_id uuid NOT NULL, + public_ip_address character varying NOT NULL, + public_port integer NOT NULL, + client_count integer NOT NULL +); + + +ALTER TABLE unispy.relay_server_caches OWNER TO unispy; + +-- +-- Name: sakestorage; Type: TABLE; Schema: unispy; Owner: unispy -- CREATE TABLE unispy.sakestorage ( - sakestorageid integer NOT NULL - tableid integer NOT NULL - data JSONB + sakestorageid integer NOT NULL, + tableid integer NOT NULL, + data jsonb ); +ALTER TABLE unispy.sakestorage OWNER TO unispy; + -- --- TOC entry 3482 (class 0 OID 0) --- Dependencies: 224 --- Name: TABLE sakestorage; Type: COMMENT; Schema: unispy; Owner: - +-- Name: TABLE sakestorage; Type: COMMENT; Schema: unispy; Owner: unispy -- COMMENT ON TABLE unispy.sakestorage IS 'Sake storage system.'; -- --- TOC entry 225 (class 1259 OID 16471) --- Name: sakestorage_sakestorageid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- Name: sakestorage_sakestorageid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- CREATE SEQUENCE unispy.sakestorage_sakestorageid_seq @@ -417,48 +621,39 @@ CREATE SEQUENCE unispy.sakestorage_sakestorageid_seq CACHE 1; +ALTER TABLE unispy.sakestorage_sakestorageid_seq OWNER TO unispy; + -- --- TOC entry 3483 (class 0 OID 0) --- Dependencies: 225 --- Name: sakestorage_sakestorageid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- Name: sakestorage_sakestorageid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy -- ALTER SEQUENCE unispy.sakestorage_sakestorageid_seq OWNED BY unispy.sakestorage.sakestorageid; -- --- TOC entry 226 (class 1259 OID 16472) --- Name: subprofiles; Type: TABLE; Schema: unispy; Owner: - +-- Name: subprofiles; Type: TABLE; Schema: unispy; Owner: unispy -- CREATE TABLE unispy.subprofiles ( subprofileid integer NOT NULL, profileid integer NOT NULL, uniquenick character varying, - namespaceid integer DEFAULT 0 NOT NULL, - partnerid integer DEFAULT 0 NOT NULL, + namespaceid integer NOT NULL, + partnerid integer NOT NULL, productid integer, gamename text, cdkeyenc character varying, - firewall smallint DEFAULT 0, - port integer DEFAULT 0, + firewall smallint, + port integer, authtoken character varying, session_key character varying ); --- --- TOC entry 3484 (class 0 OID 0) --- Dependencies: 226 --- Name: TABLE subprofiles; Type: COMMENT; Schema: unispy; Owner: - --- - -COMMENT ON TABLE unispy.subprofiles IS 'User subprofiles.'; - +ALTER TABLE unispy.subprofiles OWNER TO unispy; -- --- TOC entry 227 (class 1259 OID 16481) --- Name: subprofiles_subprofileid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- Name: subprofiles_subprofileid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- CREATE SEQUENCE unispy.subprofiles_subprofileid_seq @@ -470,18 +665,17 @@ CREATE SEQUENCE unispy.subprofiles_subprofileid_seq CACHE 1; +ALTER TABLE unispy.subprofiles_subprofileid_seq OWNER TO unispy; + -- --- TOC entry 3485 (class 0 OID 0) --- Dependencies: 227 --- Name: subprofiles_subprofileid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- Name: subprofiles_subprofileid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy -- ALTER SEQUENCE unispy.subprofiles_subprofileid_seq OWNED BY unispy.subprofiles.subprofileid; -- --- TOC entry 228 (class 1259 OID 16482) --- Name: users; Type: TABLE; Schema: unispy; Owner: - +-- Name: users; Type: TABLE; Schema: unispy; Owner: unispy -- CREATE TABLE unispy.users ( @@ -497,18 +691,17 @@ CREATE TABLE unispy.users ( ); +ALTER TABLE unispy.users OWNER TO unispy; + -- --- TOC entry 3486 (class 0 OID 0) --- Dependencies: 228 --- Name: TABLE users; Type: COMMENT; Schema: unispy; Owner: - +-- Name: TABLE users; Type: COMMENT; Schema: unispy; Owner: unispy -- COMMENT ON TABLE unispy.users IS 'User account information.'; -- --- TOC entry 229 (class 1259 OID 16492) --- Name: users_userid_seq; Type: SEQUENCE; Schema: unispy; Owner: - +-- Name: users_userid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- CREATE SEQUENCE unispy.users_userid_seq @@ -520,101 +713,109 @@ CREATE SEQUENCE unispy.users_userid_seq CACHE 1; +ALTER TABLE unispy.users_userid_seq OWNER TO unispy; + -- --- TOC entry 3487 (class 0 OID 0) --- Dependencies: 229 --- Name: users_userid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: - +-- Name: users_userid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy -- ALTER SEQUENCE unispy.users_userid_seq OWNED BY unispy.users.userid; -- --- TOC entry 3219 (class 2604 OID 16493) --- Name: addrequests addrequestid; Type: DEFAULT; Schema: unispy; Owner: - +-- Name: addrequests addrequestid; Type: DEFAULT; Schema: unispy; Owner: unispy -- ALTER TABLE ONLY unispy.addrequests ALTER COLUMN addrequestid SET DEFAULT nextval('unispy.addrequests_addrequestid_seq'::regclass); -- --- TOC entry 3220 (class 2604 OID 16494) --- Name: blocked blockid; Type: DEFAULT; Schema: unispy; Owner: - +-- Name: blocked blockid; Type: DEFAULT; Schema: unispy; Owner: unispy -- ALTER TABLE ONLY unispy.blocked ALTER COLUMN blockid SET DEFAULT nextval('unispy.blocked_blockid_seq'::regclass); -- --- TOC entry 3221 (class 2604 OID 16495) --- Name: friends friendid; Type: DEFAULT; Schema: unispy; Owner: - +-- Name: friends friendid; Type: DEFAULT; Schema: unispy; Owner: unispy -- ALTER TABLE ONLY unispy.friends ALTER COLUMN friendid SET DEFAULT nextval('unispy.friends_friendid_seq'::regclass); -- --- TOC entry 3223 (class 2604 OID 16496) --- Name: messages messageid; Type: DEFAULT; Schema: unispy; Owner: - +-- Name: game_server_caches instant_key; Type: DEFAULT; Schema: unispy; Owner: unispy +-- + +ALTER TABLE ONLY unispy.game_server_caches ALTER COLUMN instant_key SET DEFAULT nextval('unispy.game_server_caches_instant_key_seq'::regclass); + + +-- +-- Name: init_packet_caches cookie; Type: DEFAULT; Schema: unispy; Owner: unispy +-- + +ALTER TABLE ONLY unispy.init_packet_caches ALTER COLUMN cookie SET DEFAULT nextval('unispy.init_packet_caches_cookie_seq'::regclass); + + +-- +-- Name: messages messageid; Type: DEFAULT; Schema: unispy; Owner: unispy -- ALTER TABLE ONLY unispy.messages ALTER COLUMN messageid SET DEFAULT nextval('unispy.messages_messageid_seq'::regclass); -- --- TOC entry 3256 (class 2604 OID 16497) --- Name: profiles profileid; Type: DEFAULT; Schema: unispy; Owner: - +-- Name: nat_fail_cachess record_id; Type: DEFAULT; Schema: unispy; Owner: unispy +-- + +ALTER TABLE ONLY unispy.nat_fail_cachess ALTER COLUMN record_id SET DEFAULT nextval('unispy.nat_fail_cachess_record_id_seq'::regclass); + + +-- +-- Name: profiles profileid; Type: DEFAULT; Schema: unispy; Owner: unispy -- ALTER TABLE ONLY unispy.profiles ALTER COLUMN profileid SET DEFAULT nextval('unispy.profiles_profileid_seq'::regclass); -- --- TOC entry 3257 (class 2604 OID 16498) --- Name: pstorage pstorageid; Type: DEFAULT; Schema: unispy; Owner: - +-- Name: pstorage pstorageid; Type: DEFAULT; Schema: unispy; Owner: unispy -- ALTER TABLE ONLY unispy.pstorage ALTER COLUMN pstorageid SET DEFAULT nextval('unispy.pstorage_pstorageid_seq'::regclass); -- --- TOC entry 3258 (class 2604 OID 16499) --- Name: sakestorage sakestorageid; Type: DEFAULT; Schema: unispy; Owner: - +-- Name: sakestorage sakestorageid; Type: DEFAULT; Schema: unispy; Owner: unispy -- ALTER TABLE ONLY unispy.sakestorage ALTER COLUMN sakestorageid SET DEFAULT nextval('unispy.sakestorage_sakestorageid_seq'::regclass); -- --- TOC entry 3263 (class 2604 OID 16500) --- Name: subprofiles subprofileid; Type: DEFAULT; Schema: unispy; Owner: - +-- Name: subprofiles subprofileid; Type: DEFAULT; Schema: unispy; Owner: unispy -- ALTER TABLE ONLY unispy.subprofiles ALTER COLUMN subprofileid SET DEFAULT nextval('unispy.subprofiles_subprofileid_seq'::regclass); -- --- TOC entry 3269 (class 2604 OID 16501) --- Name: users userid; Type: DEFAULT; Schema: unispy; Owner: - +-- Name: users userid; Type: DEFAULT; Schema: unispy; Owner: unispy -- ALTER TABLE ONLY unispy.users ALTER COLUMN userid SET DEFAULT nextval('unispy.users_userid_seq'::regclass); -- --- TOC entry 3440 (class 0 OID 16386) --- Dependencies: 209 --- Data for Name: addrequests; Type: TABLE DATA; Schema: unispy; Owner: - +-- Data for Name: addrequests; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.addrequests (addrequestid, profileid, namespaceid, targetid, reason, syncrequested) FROM stdin; +COPY unispy.addrequests (addrequestid, profileid, targetid, namespaceid, reason, status) FROM stdin; \. -- --- TOC entry 3442 (class 0 OID 16392) --- Dependencies: 211 --- Data for Name: blocked; Type: TABLE DATA; Schema: unispy; Owner: - +-- Data for Name: blocked; Type: TABLE DATA; Schema: unispy; Owner: unispy -- COPY unispy.blocked (blockid, profileid, namespaceid, targetid) FROM stdin; @@ -622,19 +823,47 @@ COPY unispy.blocked (blockid, profileid, namespaceid, targetid) FROM stdin; -- --- TOC entry 3444 (class 0 OID 16396) --- Dependencies: 213 --- Data for Name: friends; Type: TABLE DATA; Schema: unispy; Owner: - +-- Data for Name: chat_channel_caches; Type: TABLE DATA; Schema: unispy; Owner: unispy +-- + +COPY unispy.chat_channel_caches (channel_name, server_id, game_name, room_name, topic, password, group_id, max_num_user, key_values, invited_nicks, update_time) FROM stdin; +\. + + +-- +-- Data for Name: chat_nick_caches; Type: TABLE DATA; Schema: unispy; Owner: unispy +-- + +COPY unispy.chat_nick_caches (server_id, nick_name, game_name, user_name, remote_ip_address, remote_port, key_value, update_time) FROM stdin; +\. + + +-- +-- Data for Name: chat_user_caches; Type: TABLE DATA; Schema: unispy; Owner: unispy +-- + +COPY unispy.chat_user_caches (nick_name, channel_name, server_id, user_name, update_time, is_voiceable, is_channel_operator, is_channel_creator, remote_ip_address, remote_port, key_values) FROM stdin; +\. + + +-- +-- Data for Name: friends; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.friends (friendid, profileid, namespaceid, targetid) FROM stdin; +COPY unispy.friends (friendid, profileid, targetid, namespaceid) FROM stdin; \. -- --- TOC entry 3446 (class 0 OID 16400) --- Dependencies: 215 --- Data for Name: games; Type: TABLE DATA; Schema: unispy; Owner: - +-- Data for Name: game_server_caches; Type: TABLE DATA; Schema: unispy; Owner: unispy +-- + +COPY unispy.game_server_caches (instant_key, server_id, host_ip_address, game_name, query_report_port, update_time, status, player_data, server_data, team_data, avaliable) FROM stdin; +\. + + +-- +-- Data for Name: games; Type: TABLE DATA; Schema: unispy; Owner: unispy -- COPY unispy.games (gameid, gamename, secretkey, description, disabled) FROM stdin; @@ -1637,6 +1866,7 @@ COPY unispy.games (gameid, gamename, secretkey, description, disabled) FROM stdi 1313 marvlegpc eAMh9M Marvel Legends (PC) f 1315 marvlegpcd \N Marvel Legends Demo (PC) f 1318 hustleps2am \N Hustle: Detroit Streets Automatch (PS2) f +2832 bädmasterid \N bädmasterid f 1317 hustleps2 ni9hdV Hustle: Detroit Streets (PS2) f 1342 ffurtdriftps2am \N The Fast and the Furious: Tokyo Drift Automatch (PS2) f 1320 koshien2ds UKdPFf PowerPro Pocket Koshien 2 (DS) f @@ -2237,6 +2467,7 @@ COPY unispy.games (gameid, gamename, secretkey, description, disabled) FROM stdi 2018 cvania08ds SwO9Jn Castlevania 2008 (DS) f 2019 nplusds qX9Muy N+ (DS) f 2020 gauntletds wUq7fL Gauntlet (DS) f +2935 DeathtoSpies LOhgNO Death to Spies f 2021 finertiaps3 3vEcPe Fatal Inertia (PS3) f 2029 tpfolEUpcd \N Turning Point: Fall of Liberty Demo (EU) (PC) f 2023 topspin3usds 8R4LgD Top Spin 3 (US) (DS) f @@ -2927,7 +3158,6 @@ COPY unispy.games (gameid, gamename, secretkey, description, disabled) FROM stdi 2817 maxpayne3x360am \N Max Payne 3 Automatch (360) f 2816 maxpayne3x360 28xd4T Max Payne 3 (360) f 2818 maxpayne3x360d \N Max Payne 3 Demo (360) f -2832 bädmasterid \N bädmasterid f 2819 wordjongeuds 3rwTkL Wordjong EU (DS) f 2820 sengo3wii Esqv7G Sengokumuso 3 f 2821 bewarewii iTHrhz Beware (WiiWare) f @@ -3028,7 +3258,6 @@ COPY unispy.games (gameid, gamename, secretkey, description, disabled) FROM stdi 2937 svsr11x360devam \N Smackdown vs Raw 2011 DEV Automatch (x360) f 2932 girlskoreads QiFGmi Girls_Korea (DS) f 2934 protocolwii Hd4g3T Protocol (WiiWare) f -2935 DeathtoSpies LOhgNO Death to Spies f 2936 svsr11x360dev h5DZhP Smackdown vs Raw 2011 DEV (x360) f 2939 svsr11ps3devam \N Smackdown vs Raw 2011 DEV Automatch (PS3) f 2938 svsr11ps3dev gSTArg Smackdown vs Raw 2011 DEV (PS3) f @@ -3441,9 +3670,7 @@ COPY unispy.games (gameid, gamename, secretkey, description, disabled) FROM stdi -- --- TOC entry 3447 (class 0 OID 16405) --- Dependencies: 216 --- Data for Name: grouplist; Type: TABLE DATA; Schema: unispy; Owner: - +-- Data for Name: grouplist; Type: TABLE DATA; Schema: unispy; Owner: unispy -- COPY unispy.grouplist (groupid, gameid, roomname) FROM stdin; @@ -5142,19 +5369,31 @@ COPY unispy.grouplist (groupid, gameid, roomname) FROM stdin; -- --- TOC entry 3448 (class 0 OID 16410) --- Dependencies: 217 --- Data for Name: messages; Type: TABLE DATA; Schema: unispy; Owner: - +-- Data for Name: init_packet_caches; Type: TABLE DATA; Schema: unispy; Owner: unispy +-- + +COPY unispy.init_packet_caches (cookie, server_id, version, port_type, client_index, game_name, use_game_port, public_ip, public_port, private_ip, private_port, update_time) FROM stdin; +\. + + +-- +-- Data for Name: messages; Type: TABLE DATA; Schema: unispy; Owner: unispy +-- + +COPY unispy.messages (messageid, namespaceid, type, from_user, to_user, date, message) FROM stdin; +\. + + +-- +-- Data for Name: nat_fail_cachess; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.messages (messageid, namespaceid, type, "from", "to", date, message) FROM stdin; +COPY unispy.nat_fail_cachess (record_id, public_ip_address1, public_ip_address2, update_time) FROM stdin; \. -- --- TOC entry 3450 (class 0 OID 16417) --- Dependencies: 219 --- Data for Name: partner; Type: TABLE DATA; Schema: unispy; Owner: - +-- Data for Name: partner; Type: TABLE DATA; Schema: unispy; Owner: unispy -- COPY unispy.partner (partnerid, partnername) FROM stdin; @@ -5164,24 +5403,16 @@ COPY unispy.partner (partnerid, partnername) FROM stdin; -- --- TOC entry 3451 (class 0 OID 16422) --- Dependencies: 220 --- Data for Name: profiles; Type: TABLE DATA; Schema: unispy; Owner: - +-- Data for Name: profiles; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.profiles (profileid, userid, nick, serverflag, status, statstring, location, firstname, lastname, publicmask, latitude, longitude, aim, picture, occupationid, incomeid, industryid, marriedid, childcount, interests1, ownership1, connectiontype, sex, zipcode, countrycode, homepage, birthday, birthmonth, birthyear, icquin, quietflags, streetaddr, streeaddr, city, cpubrandid, cpuspeed, memory, videocard1string, videocard1ram, videocard2string, videocard2ram, subscription, adminrights) FROM stdin; -1 1 spyguy 0 0 I love UniSpy earth spy guy 0 0 0 0 0 0 0 0 0 0 0 0 0 00000 1 unispy.org 0 0 0 0 0 \N \N \N 0 0 0 \N 0 \N 0 0 0 -2 2 uniguy 0 0 I love UniSpy earth uni guy 0 0 0 0 0 0 0 0 0 0 0 0 0 00000 1 unispy.org 0 0 0 0 0 \N \N \N 0 0 0 \N 0 \N 0 0 0 -3 3 gptest1 0 0 I love UniSpy \N \N \N 0 0 0 0 0 0 0 0 0 0 0 0 0 00000 1 unispy.org 0 0 0 0 0 \N \N \N 0 0 0 \N 0 \N 0 0 0 -4 4 gptest2 0 0 I love UniSpy \N \N \N 0 0 0 0 0 0 0 0 0 0 0 0 0 00000 1 unispy.org 0 0 0 0 0 \N \N \N 0 0 0 \N 0 \N 0 0 0 -5 5 gptest3 0 0 I love UniSpy \N \N \N 0 0 0 0 0 0 0 0 0 0 0 0 0 00000 1 unispy.org 0 0 0 0 0 \N \N \N 0 0 0 \N 0 \N 0 0 0 +COPY unispy.profiles (profileid, userid, nick, serverflag, status, statstring, extra_info) FROM stdin; +1 1 spyguy 0 0 I love UniSpy {} \. -- --- TOC entry 3453 (class 0 OID 16460) --- Dependencies: 222 --- Data for Name: pstorage; Type: TABLE DATA; Schema: unispy; Owner: - +-- Data for Name: pstorage; Type: TABLE DATA; Schema: unispy; Owner: unispy -- COPY unispy.pstorage (pstorageid, profileid, ptype, dindex, data) FROM stdin; @@ -5189,34 +5420,31 @@ COPY unispy.pstorage (pstorageid, profileid, ptype, dindex, data) FROM stdin; -- --- TOC entry 3455 (class 0 OID 16466) --- Dependencies: 224 --- Data for Name: sakestorage; Type: TABLE DATA; Schema: unispy; Owner: - +-- Data for Name: relay_server_caches; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.sakestorage (sakestorageid, tableid) FROM stdin; +COPY unispy.relay_server_caches (server_id, public_ip_address, public_port, client_count) FROM stdin; \. -- --- TOC entry 3457 (class 0 OID 16472) --- Dependencies: 226 --- Data for Name: subprofiles; Type: TABLE DATA; Schema: unispy; Owner: - +-- Data for Name: sakestorage; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.subprofiles (subprofileid, profileid, uniquenick, namespaceid, partnerid, productid, gamename, cdkeyenc, firewall, port, authtoken) FROM stdin; -1 1 spyguy 1 0 0 gmtest \N 0 0 example_token -2 2 uniguy 1 0 0 gmtest \N 0 0 \N -3 3 gptest1 1 0 0 gmtest \N 0 0 \N -4 4 gptest2 1 0 0 gmtest \N 0 0 \N -5 5 gptest3 1 0 0 gmtest \N 0 0 \N +COPY unispy.sakestorage (sakestorageid, tableid, data) FROM stdin; \. -- --- TOC entry 3459 (class 0 OID 16482) --- Dependencies: 228 --- Data for Name: users; Type: TABLE DATA; Schema: unispy; Owner: - +-- Data for Name: subprofiles; Type: TABLE DATA; Schema: unispy; Owner: unispy +-- + +COPY unispy.subprofiles (subprofileid, profileid, uniquenick, namespaceid, partnerid, productid, gamename, cdkeyenc, firewall, port, authtoken, session_key) FROM stdin; +\. + + +-- +-- Data for Name: users; Type: TABLE DATA; Schema: unispy; Owner: unispy -- COPY unispy.users (userid, email, password, emailverified, lastip, lastonline, createddate, banned, deleted) FROM stdin; @@ -5229,259 +5457,266 @@ COPY unispy.users (userid, email, password, emailverified, lastip, lastonline, c -- --- TOC entry 3488 (class 0 OID 0) --- Dependencies: 210 --- Name: addrequests_addrequestid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- Name: addrequests_addrequestid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy -- SELECT pg_catalog.setval('unispy.addrequests_addrequestid_seq', 1, false); -- --- TOC entry 3489 (class 0 OID 0) --- Dependencies: 212 --- Name: blocked_blockid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- Name: blocked_blockid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy -- SELECT pg_catalog.setval('unispy.blocked_blockid_seq', 1, false); -- --- TOC entry 3490 (class 0 OID 0) --- Dependencies: 214 --- Name: friends_friendid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- Name: friends_friendid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy -- SELECT pg_catalog.setval('unispy.friends_friendid_seq', 1, false); -- --- TOC entry 3491 (class 0 OID 0) --- Dependencies: 218 --- Name: messages_messageid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- Name: game_server_caches_instant_key_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy +-- + +SELECT pg_catalog.setval('unispy.game_server_caches_instant_key_seq', 1, false); + + +-- +-- Name: init_packet_caches_cookie_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy +-- + +SELECT pg_catalog.setval('unispy.init_packet_caches_cookie_seq', 1, false); + + +-- +-- Name: messages_messageid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy -- SELECT pg_catalog.setval('unispy.messages_messageid_seq', 1, false); -- --- TOC entry 3492 (class 0 OID 0) --- Dependencies: 221 --- Name: profiles_profileid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- Name: nat_fail_cachess_record_id_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy -- -SELECT pg_catalog.setval('unispy.profiles_profileid_seq', 5, true); +SELECT pg_catalog.setval('unispy.nat_fail_cachess_record_id_seq', 1, false); -- --- TOC entry 3493 (class 0 OID 0) --- Dependencies: 223 --- Name: pstorage_pstorageid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- Name: profiles_profileid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy -- -SELECT pg_catalog.setval('unispy.pstorage_pstorageid_seq', 1, true); +SELECT pg_catalog.setval('unispy.profiles_profileid_seq', 2, true); -- --- TOC entry 3494 (class 0 OID 0) --- Dependencies: 225 --- Name: sakestorage_sakestorageid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- Name: pstorage_pstorageid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy +-- + +SELECT pg_catalog.setval('unispy.pstorage_pstorageid_seq', 1, false); + + +-- +-- Name: sakestorage_sakestorageid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy -- SELECT pg_catalog.setval('unispy.sakestorage_sakestorageid_seq', 1, false); -- --- TOC entry 3495 (class 0 OID 0) --- Dependencies: 227 --- Name: subprofiles_subprofileid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- Name: subprofiles_subprofileid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy -- -SELECT pg_catalog.setval('unispy.subprofiles_subprofileid_seq', 5, true); +SELECT pg_catalog.setval('unispy.subprofiles_subprofileid_seq', 1, false); -- --- TOC entry 3496 (class 0 OID 0) --- Dependencies: 229 --- Name: users_userid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: - +-- Name: users_userid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy -- SELECT pg_catalog.setval('unispy.users_userid_seq', 5, true); -- --- TOC entry 3271 (class 2606 OID 16503) --- Name: addrequests addrequests_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- Name: addrequests addrequests_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- ALTER TABLE ONLY unispy.addrequests - ADD CONSTRAINT addrequests_pk PRIMARY KEY (addrequestid); + ADD CONSTRAINT addrequests_pkey PRIMARY KEY (addrequestid); -- --- TOC entry 3273 (class 2606 OID 16505) --- Name: blocked blocked_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- Name: blocked blocked_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- ALTER TABLE ONLY unispy.blocked - ADD CONSTRAINT blocked_pk PRIMARY KEY (blockid); + ADD CONSTRAINT blocked_pkey PRIMARY KEY (blockid); -- --- TOC entry 3275 (class 2606 OID 16507) --- Name: friends friends_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- Name: chat_channel_caches chat_channel_caches_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.friends - ADD CONSTRAINT friends_pk PRIMARY KEY (friendid); +ALTER TABLE ONLY unispy.chat_channel_caches + ADD CONSTRAINT chat_channel_caches_pkey PRIMARY KEY (channel_name); -- --- TOC entry 3277 (class 2606 OID 16509) --- Name: games games_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- Name: chat_nick_caches chat_nick_caches_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.games - ADD CONSTRAINT games_pk PRIMARY KEY (gameid); +ALTER TABLE ONLY unispy.chat_nick_caches + ADD CONSTRAINT chat_nick_caches_pkey PRIMARY KEY (nick_name); -- --- TOC entry 3279 (class 2606 OID 16511) --- Name: grouplist grouplist_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- Name: chat_user_caches chat_user_caches_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.grouplist - ADD CONSTRAINT grouplist_pk PRIMARY KEY (groupid); +ALTER TABLE ONLY unispy.chat_user_caches + ADD CONSTRAINT chat_user_caches_pkey PRIMARY KEY (nick_name); -- --- TOC entry 3281 (class 2606 OID 16513) --- Name: messages messages_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- Name: friends friends_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.messages - ADD CONSTRAINT messages_pk PRIMARY KEY (messageid); +ALTER TABLE ONLY unispy.friends + ADD CONSTRAINT friends_pkey PRIMARY KEY (friendid); -- --- TOC entry 3283 (class 2606 OID 16515) --- Name: partner partner_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- Name: game_server_caches game_server_caches_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.partner - ADD CONSTRAINT partner_pk PRIMARY KEY (partnerid); +ALTER TABLE ONLY unispy.game_server_caches + ADD CONSTRAINT game_server_caches_pkey PRIMARY KEY (instant_key); -- --- TOC entry 3285 (class 2606 OID 16517) --- Name: profiles profiles_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- Name: games games_pk; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.profiles - ADD CONSTRAINT profiles_pk PRIMARY KEY (profileid); +ALTER TABLE ONLY unispy.games + ADD CONSTRAINT games_pk PRIMARY KEY (gameid); -- --- TOC entry 3287 (class 2606 OID 16519) --- Name: pstorage pstorage_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- Name: grouplist grouplist_pk; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.pstorage - ADD CONSTRAINT pstorage_pk PRIMARY KEY (pstorageid); +ALTER TABLE ONLY unispy.grouplist + ADD CONSTRAINT grouplist_pk PRIMARY KEY (groupid); -- --- TOC entry 3289 (class 2606 OID 16521) --- Name: sakestorage sakestorage_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- Name: init_packet_caches init_packet_caches_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.sakestorage - ADD CONSTRAINT sakestorage_pk PRIMARY KEY (sakestorageid); +ALTER TABLE ONLY unispy.init_packet_caches + ADD CONSTRAINT init_packet_caches_pkey PRIMARY KEY (cookie); -- --- TOC entry 3291 (class 2606 OID 16523) --- Name: subprofiles subprofiles_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- Name: messages messages_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.subprofiles - ADD CONSTRAINT subprofiles_pk PRIMARY KEY (subprofileid); +ALTER TABLE ONLY unispy.messages + ADD CONSTRAINT messages_pkey PRIMARY KEY (messageid); -- --- TOC entry 3293 (class 2606 OID 16525) --- Name: users users_pk; Type: CONSTRAINT; Schema: unispy; Owner: - +-- Name: nat_fail_cachess nat_fail_cachess_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.users - ADD CONSTRAINT users_pk PRIMARY KEY (userid); +ALTER TABLE ONLY unispy.nat_fail_cachess + ADD CONSTRAINT nat_fail_cachess_pkey PRIMARY KEY (record_id); -- --- TOC entry 3294 (class 2606 OID 16526) --- Name: addrequests addrequests_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - +-- Name: partner partner_pk; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.addrequests - ADD CONSTRAINT addrequests_fk FOREIGN KEY (profileid) REFERENCES unispy.profiles(profileid); +ALTER TABLE ONLY unispy.partner + ADD CONSTRAINT partner_pk PRIMARY KEY (partnerid); -- --- TOC entry 3295 (class 2606 OID 16531) --- Name: blocked blocked_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - +-- Name: profiles profiles_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.blocked - ADD CONSTRAINT blocked_fk FOREIGN KEY (profileid) REFERENCES unispy.profiles(profileid); +ALTER TABLE ONLY unispy.profiles + ADD CONSTRAINT profiles_pkey PRIMARY KEY (profileid); -- --- TOC entry 3296 (class 2606 OID 16536) --- Name: friends friends_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - +-- Name: pstorage pstorage_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.friends - ADD CONSTRAINT friends_fk FOREIGN KEY (profileid) REFERENCES unispy.profiles(profileid); +ALTER TABLE ONLY unispy.pstorage + ADD CONSTRAINT pstorage_pkey PRIMARY KEY (pstorageid); -- --- TOC entry 3297 (class 2606 OID 16541) --- Name: grouplist grouplist_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - +-- Name: relay_server_caches relay_server_caches_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.grouplist - ADD CONSTRAINT grouplist_fk FOREIGN KEY (gameid) REFERENCES unispy.games(gameid); +ALTER TABLE ONLY unispy.relay_server_caches + ADD CONSTRAINT relay_server_caches_pkey PRIMARY KEY (server_id); -- --- TOC entry 3298 (class 2606 OID 16546) --- Name: profiles profiles_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - +-- Name: sakestorage sakestorage_pk; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.profiles - ADD CONSTRAINT profiles_fk FOREIGN KEY (userid) REFERENCES unispy.users(userid); +ALTER TABLE ONLY unispy.sakestorage + ADD CONSTRAINT sakestorage_pk PRIMARY KEY (sakestorageid); -- --- TOC entry 3299 (class 2606 OID 16551) --- Name: pstorage pstorage_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - +-- Name: subprofiles subprofiles_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.pstorage - ADD CONSTRAINT pstorage_fk FOREIGN KEY (profileid) REFERENCES unispy.profiles(profileid); +ALTER TABLE ONLY unispy.subprofiles + ADD CONSTRAINT subprofiles_pkey PRIMARY KEY (subprofileid); -- --- TOC entry 3300 (class 2606 OID 16556) --- Name: subprofiles subprofiles_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: - +-- Name: users users_pk; Type: CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.subprofiles - ADD CONSTRAINT subprofiles_fk FOREIGN KEY (profileid) REFERENCES unispy.profiles(profileid); +ALTER TABLE ONLY unispy.users + ADD CONSTRAINT users_pk PRIMARY KEY (userid); + + +-- +-- Name: chat_user_caches chat_user_caches_channel_name_fkey; Type: FK CONSTRAINT; Schema: unispy; Owner: unispy +-- +ALTER TABLE ONLY unispy.chat_user_caches + ADD CONSTRAINT chat_user_caches_channel_name_fkey FOREIGN KEY (channel_name) REFERENCES unispy.chat_channel_caches(channel_name); + + +-- +-- Name: grouplist grouplist_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: unispy +-- + +ALTER TABLE ONLY unispy.grouplist + ADD CONSTRAINT grouplist_fk FOREIGN KEY (gameid) REFERENCES unispy.games(gameid); + + +-- +-- Name: profiles profiles_userid_fkey; Type: FK CONSTRAINT; Schema: unispy; Owner: unispy +-- + +ALTER TABLE ONLY unispy.profiles + ADD CONSTRAINT profiles_userid_fkey FOREIGN KEY (userid) REFERENCES unispy.users(userid); --- Completed on 2022-03-02 20:15:24 CET -- -- PostgreSQL database dump complete -- + diff --git a/common/config.json b/common/config.json index 7e5ba1a2d..4e205379e 100644 --- a/common/config.json +++ b/common/config.json @@ -1,6 +1,9 @@ { "backend": { - "url": "http://localhost:8080" + "url": "http://localhost:8080", + "token_secret_key": "I love UniSpy", + "token_algorithm": "HS256", + "token_expire_time": 30 }, "logging": { "path": "~/Downloads/unispy_server/log/", From eb2be437bb8e45f19767d9c9bc41ac14acc8bd90 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 3 Jan 2025 04:12:40 +0000 Subject: [PATCH 148/231] refactor: added backends functions --- src/backends/library/database/pg_orm.py | 96 +++++++++++-------- .../protocols/gamespy/game_status/data.py | 23 ++--- .../protocols/gamespy/game_status/handlers.py | 8 +- .../protocols/gamespy/game_status/requests.py | 3 +- .../gamespy/presence_search_player/data.py | 10 +- .../presence_search_player/handlers.py | 8 +- .../protocols/gamespy/web_services/data.py | 79 +++++++++++---- .../gamespy/web_services/handlers.py | 15 +-- .../gamespy/web_services/requests.py | 2 +- src/backends/routers/gamespy/gstats.py | 7 +- src/backends/routers/home.py | 12 +++ 11 files changed, 167 insertions(+), 96 deletions(-) diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 5b3add527..3c09e0b0a 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -1,11 +1,7 @@ -from typing import Optional from library.src.configs import CONFIG -from sqlalchemy import Enum, create_engine -from sqlalchemy.orm.session import Session from datetime import datetime from sqlalchemy import ( Boolean, - Double, SmallInteger, Text, create_engine, @@ -15,16 +11,33 @@ ForeignKey, DateTime, text, - UUID + UUID, + create_engine ) +from sqlalchemy.orm.session import Session from sqlalchemy.dialects.postgresql import JSONB, INET from sqlalchemy.ext.declarative import DeclarativeMeta from sqlalchemy.orm import sessionmaker, declarative_base - +from sqlalchemy.types import TypeDecorator from servers.natneg.src.aggregations.enums import NatClientIndex, NatPortType from servers.presence_connection_manager.src.aggregates.enums import FriendRequestStatus, GPStatusCode from servers.query_report.src.v2.aggregates.enums import GameServerStatus -from sqlalchemy.orm.decl_api import DeclarativeBase +import sqlalchemy as sa +import enum + + +class IntEnum(TypeDecorator): + impl = sa.Integer + + def __init__(self, enumtype, *args, **kwargs): + super().__init__(*args, **kwargs) + self._enumtype = enumtype + + def process_bind_param(self, value: enum.Enum, dialect): + return value.value + + def process_result_value(self, value, dialect): + return self._enumtype(value) Base: DeclarativeMeta = declarative_base() @@ -33,32 +46,32 @@ class Users(Base): __tablename__ = "users" - userid: Column | int = Column( + userid: Column[int] = Column( Integer, primary_key=True, autoincrement=True) - email: Column | str = Column(String, nullable=False) - password: Column | str = Column(String, nullable=False) - emailverified: Column | bool = Column( + email: Column[str] = Column(String, nullable=False) + password: Column[str] = Column(String, nullable=False) + emailverified: Column[bool] = Column( Boolean, default=True, nullable=False) - lastip: Column | str = Column(INET) - lastonline: Column | datetime = Column(DateTime, default=datetime.now()) - createddate: Column | datetime = Column( + lastip: Column[str] = Column(INET) + lastonline: Column[datetime] = Column(DateTime, default=datetime.now()) + createddate: Column[datetime] = Column( DateTime, default=datetime.now(), nullable=False) - banned: Column | bool = Column(Boolean, default=False, nullable=False) - deleted: Column | bool = Column(Boolean, default=False, nullable=False) + banned: Column[bool] = Column(Boolean, default=False, nullable=False) + deleted: Column[bool] = Column(Boolean, default=False, nullable=False) class Profiles(Base): __tablename__ = "profiles" - profileid: Column | int = Column( + profileid: Column[int] = Column( Integer, primary_key=True, autoincrement=True) - userid: Column | int = Column( + userid: Column[int] = Column( Integer, ForeignKey("users.userid"), nullable=False) - nick: Column | str = Column(String, nullable=False) - serverflag: Column | int = Column(Integer, nullable=False, default=0) - status: Column | GPStatusCode = Column(Enum(GPStatusCode), default=0) - statstring: Column | str = Column(String, default="I love UniSpy") - location: Column | str = Column(String) + nick: Column[str] = Column(String, nullable=False) + serverflag: Column[int] = Column(Integer, nullable=False, default=0) + status = Column( + IntEnum(GPStatusCode), default=GPStatusCode.OFFLINE) + statstring: Column[str] = Column(String, default="I love UniSpy") extra_info: Column[JSONB] = Column(JSONB) @@ -68,17 +81,17 @@ class SubProfiles(Base): subprofileid = Column( Integer, ForeignKey("profiles.profileid"), primary_key=True, autoincrement=True ) - profileid: Column[Integer] = Column(Integer, nullable=False) + profileid: Column[int] = Column(Integer, nullable=False) uniquenick: Column[str] = Column(String) - namespaceid: Column | int = Column(Integer, nullable=False, default=0) - partnerid: Column | int = Column(Integer, nullable=False, default=0) - productid: Column | int = Column(Integer) - gamename: Column | str = Column(Text) - cdkeyenc: Column | str = Column(String) - firewall: Column | int = Column(SmallInteger, default=0) - port: Column | int = Column(Integer, default=0) - authtoken: Column | str = Column(String) - session_key: Column | str = Column(String) + namespaceid: Column[int] = Column(Integer, nullable=False, default=0) + partnerid: Column[int] = Column(Integer, nullable=False, default=0) + productid: Column[int] = Column(Integer) + gamename: Column[str] = Column(Text) + cdkeyenc: Column[str] = Column(String) + firewall: Column[int] = Column(SmallInteger, default=0) + port: Column[int] = Column(Integer, default=0) + authtoken: Column[str] = Column(String) + session_key: Column[str] = Column(String) class Blocked(Base): @@ -111,7 +124,7 @@ class FriendAddRequest(Base): "profiles.profileid"), nullable=False) namespaceid = Column(Integer, nullable=False) reason = Column(String, nullable=False) - status = Column(Enum(FriendRequestStatus), nullable=False, + status = Column(IntEnum(FriendRequestStatus), nullable=False, default=FriendRequestStatus.PENDING) @@ -168,7 +181,7 @@ class SakeStorage(Base): sakestorageid = Column(Integer, primary_key=True, autoincrement=True) tableid = Column(Integer, nullable=False) - data: Column[JSONB] = Column(JSONB) + data: Column[JSONB] = Column(JSONB, nullable=False) class InitPacketCaches(Base): @@ -177,8 +190,8 @@ class InitPacketCaches(Base): cookie = Column(Integer, primary_key=True, nullable=False) server_id = Column(UUID, nullable=False) version = Column(Integer, nullable=False) - port_type = Column(Enum(NatPortType), nullable=False) - client_index = Column(Enum(NatClientIndex), nullable=False) + port_type = Column(IntEnum(NatPortType), nullable=False) + client_index = Column(IntEnum(NatClientIndex), nullable=False) game_name = Column(String, nullable=False) use_game_port = Column(Boolean, nullable=False) public_ip = Column(String, nullable=False) @@ -261,7 +274,7 @@ class GameServerCaches(Base): game_name = Column(String, nullable=False) query_report_port = Column(Integer, nullable=False) update_time = Column(DateTime, nullable=False) - status = Column(Enum(GameServerStatus)) + status = Column(IntEnum(GameServerStatus)) player_data = Column(JSONB, nullable=False) server_data = Column(JSONB, nullable=False) team_data = Column(JSONB, nullable=False) @@ -271,9 +284,9 @@ class GameServerCaches(Base): def connect_to_db() -> Session: ENGINE = create_engine(CONFIG.postgresql.url) session = sessionmaker(bind=ENGINE)() - # Base.metadata.create_all(ENGINE) + Base.metadata.create_all(ENGINE) with ENGINE.connect() as conn: - conn.execute(text("SELECT 1")) + conn.execute(text("SELECT 1")).first() return session @@ -282,4 +295,7 @@ def connect_to_db() -> Session: if __name__ == "__main__": session = connect_to_db() session.query(Users.userid == 0) # type:ignore + profile = Profiles(userid=1,nick="spyguy",extra_info={},status=GPStatusCode.OFFLINE) + PG_SESSION.add(profile) + PG_SESSION.commit() pass diff --git a/src/backends/protocols/gamespy/game_status/data.py b/src/backends/protocols/gamespy/game_status/data.py index eff6cc899..7fc8fd1cb 100644 --- a/src/backends/protocols/gamespy/game_status/data.py +++ b/src/backends/protocols/gamespy/game_status/data.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, cast, overload +from typing import TYPE_CHECKING, cast from sqlalchemy import Column from backends.library.database.pg_orm import PG_SESSION, PStorage, Profiles, SubProfiles, Users @@ -18,31 +18,28 @@ def update_player_data(): raise NotImplementedError() -@overload -def get_profile_id(token: str) -> int: - if TYPE_CHECKING: - assert isinstance(SubProfiles.profileid, Column) - assert isinstance(SubProfiles.authtoken, Column) - +def get_profile_id_by_token(token: str) -> int: + assert isinstance(token, str) result = PG_SESSION.query(SubProfiles.profileid).filter( SubProfiles.authtoken == token).first() if result is None: raise GSException("No records found in database") - if TYPE_CHECKING: - result = cast(int, result) + assert isinstance(result, int) return result -@overload -def get_profile_id(profile_id: int): +def get_profile_id_by_profile_id(profile_id: int) -> int: + assert isinstance(profile_id, int) + result = PG_SESSION.query(SubProfiles.profileid).where( SubProfiles.profileid == profile_id).count() if result != 1: raise GSException(f"There is no profile_id {profile_id} existed") + assert isinstance(result, int) + return result -@overload -def get_profile_id(cdkey: str, nick_name: str) -> int: +def get_profile_id_by_cdkey(cdkey: str, nick_name: str) -> int: if TYPE_CHECKING: assert isinstance(Profiles.profileid, Column) assert isinstance(Profiles.userid, Column) diff --git a/src/backends/protocols/gamespy/game_status/handlers.py b/src/backends/protocols/gamespy/game_status/handlers.py index 1be56a619..b4da005da 100644 --- a/src/backends/protocols/gamespy/game_status/handlers.py +++ b/src/backends/protocols/gamespy/game_status/handlers.py @@ -17,12 +17,12 @@ class AuthPlayerHandler(HandlerBase): async def _data_operate(self): match self._request.auth_type: case AuthMethod.PARTNER_ID_AUTH: - self.data = data.get_profile_id(token=self._request.auth_token) + self.data = data.get_profile_id_by_token(token=self._request.auth_token) case AuthMethod.PROFILE_ID_AUTH: - self.data = data.get_profile_id( + self.data = data.get_profile_id_by_profile_id( profile_id=self._request.profile_id) case AuthMethod.CDKEY_AUTH: - self.data = data.get_profile_id( + self.data = data.get_profile_id_by_cdkey( cdkey=self._request.cdkey_hash, nick_name=self._request.nick) case _: raise GSException("Invalid auth type") @@ -48,7 +48,7 @@ class GetProfileIdHandler(HandlerBase): _request: GetProfileIdRequest async def _data_operate(self): - self.data = data.get_profile_id( + self.data = data.get_profile_id_by_cdkey( cdkey=self._request.cdkey, nick_name=self._request.nick) async def _result_construct(self): diff --git a/src/backends/protocols/gamespy/game_status/requests.py b/src/backends/protocols/gamespy/game_status/requests.py index b6e03c203..7eadfdec5 100644 --- a/src/backends/protocols/gamespy/game_status/requests.py +++ b/src/backends/protocols/gamespy/game_status/requests.py @@ -17,7 +17,6 @@ class AuthGameRequest(RequestBase): class AuthPlayerRequest(RequestBase): auth_type: AuthMethod profile_id: int - auth_token: str response: str cdkey_hash: str @@ -25,7 +24,7 @@ class AuthPlayerRequest(RequestBase): class GetPlayerDataRequest(RequestBase): - profile_id: str + profile_id: int storage_type: PersistStorageType data_index: int is_get_all_data: bool = False diff --git a/src/backends/protocols/gamespy/presence_search_player/data.py b/src/backends/protocols/gamespy/presence_search_player/data.py index 5256f37c5..2db3ccd05 100644 --- a/src/backends/protocols/gamespy/presence_search_player/data.py +++ b/src/backends/protocols/gamespy/presence_search_player/data.py @@ -91,17 +91,15 @@ def update_subprofile(subprofile: SubProfiles): PG_SESSION.commit() -def get_user(email: str): - if TYPE_CHECKING: - Users.email = cast(Column, Users.email) +def get_user(email: str) -> Users | None: + assert isinstance(email, str) result = PG_SESSION.query(Users).filter(Users.email == email).first() return result def get_profile(user_id: int, nick_name: str) -> Profiles: - if TYPE_CHECKING: - Profiles.userid = cast(Column, Profiles.userid) - Profiles.nick = cast(Column, Profiles.nick) + assert isinstance(user_id, int) + assert isinstance(nick_name, str) result = PG_SESSION.query(Profiles).filter( Profiles.userid == user_id, Profiles.nick == nick_name ).first() diff --git a/src/backends/protocols/gamespy/presence_search_player/handlers.py b/src/backends/protocols/gamespy/presence_search_player/handlers.py index 6f5726d2c..e035b2c91 100644 --- a/src/backends/protocols/gamespy/presence_search_player/handlers.py +++ b/src/backends/protocols/gamespy/presence_search_player/handlers.py @@ -32,16 +32,14 @@ async def _data_operate(self) -> None: if self.user is None: self._create_user() - if TYPE_CHECKING: - assert self.user - self.user.userid = cast(int, self.user.userid) + assert self.user != None + assert isinstance(self.user.userid, int) self.profile = data.get_profile(self.user.userid, self._request.nick) if self.profile is None: self._create_profile() - if TYPE_CHECKING: - self.profile.profileid = cast(int, self.profile.profileid) + assert isinstance(self.profile.profileid, int) self.subprofile = data.get_sub_profile( profile_id=self.profile.profileid, namespace_id=self._request.namespace_id, product_id=self._request.product_id) diff --git a/src/backends/protocols/gamespy/web_services/data.py b/src/backends/protocols/gamespy/web_services/data.py index cc70137d2..9b573a8fa 100644 --- a/src/backends/protocols/gamespy/web_services/data.py +++ b/src/backends/protocols/gamespy/web_services/data.py @@ -10,20 +10,37 @@ def is_user_exist(uniquenick: str, cdkey: str, partner_id: int, namespace_id: int, email: str, password: str) -> None: - result = PG_SESSION.query(Profiles).join(Users).join(SubProfiles).where(SubProfiles.uniquenick == uniquenick, - SubProfiles.cdkeyenc == cdkey, SubProfiles.partnerid == partner_id, SubProfiles.namespaceid == namespace_id, Users.email == email, Users.password == password).first() + result = PG_SESSION.query(Profiles)\ + .join(Users)\ + .join(SubProfiles)\ + .where(SubProfiles.uniquenick == uniquenick, + SubProfiles.cdkeyenc == cdkey, + SubProfiles.partnerid == partner_id, + SubProfiles.namespaceid == namespace_id, + Users.email == email, + Users.password == password).first() if result is None: raise AuthException( "No account exists with the provided email address.") -@overload -def get_info(uniquenick: str, namespace_id: int, cdkey: str, email: str) -> tuple[int, int, str, str, str]: +def get_info_by_cdkey_email(uniquenick: str, namespace_id: int, cdkey: str, email: str) -> tuple[int, int, str, str, str]: """ return [user_id,profile_id,profile_nick,unique_nick,cdkey_hash] """ - result = PG_SESSION.query(Users, Profiles, SubProfiles).join(Users, (Users.userid, Profiles.userid)).join( - Profiles, (Profiles.profileid, SubProfiles.profileid)).where(SubProfiles.uniquenick == uniquenick, SubProfiles.namespaceid == namespace_id, SubProfiles.cdkeyenc == cdkey, Users.email == email).first() + assert isinstance(uniquenick, str) + assert isinstance(namespace_id, int) + assert isinstance(cdkey, str) + assert isinstance(email, str) + + result = PG_SESSION.query(Users, Profiles, SubProfiles)\ + .join(Users)\ + .join(Profiles,)\ + .join(SubProfiles)\ + .where(SubProfiles.uniquenick == uniquenick, + SubProfiles.namespaceid == namespace_id, + SubProfiles.cdkeyenc == cdkey, + Users.email == email).first() if result is None: raise AuthException( @@ -31,32 +48,45 @@ def get_info(uniquenick: str, namespace_id: int, cdkey: str, email: str) -> tupl user: Users = result[0] profile: Profiles = result[1] subprofile: SubProfiles = result[2] + assert isinstance(user.userid, int) + assert isinstance(profile.profileid, int) + assert isinstance(profile.nick, str) + assert isinstance(subprofile.uniquenick, str) + assert isinstance(subprofile.cdkeyenc, str) + return user.userid, profile.profileid, profile.nick, subprofile.uniquenick, subprofile.cdkeyenc -@overload -def get_info(auth_token: str) -> tuple[int, int, str, str, str]: +def get_info_by_authtoken(auth_token: str) -> tuple[int, int, str, str, str]: """ return [user_id,profile_id,profile_nick,unique_nick,cdkey_hash] """ - result = PG_SESSION.query(Users, Profiles, SubProfiles).join(Users, (Users.userid, Profiles.userid)).join( - Profiles, (Profiles.profileid, SubProfiles.profileid)).where(SubProfiles.authtoken == auth_token).first() + result = PG_SESSION.query(Users, Profiles, SubProfiles)\ + .join(Users, Users.userid == Profiles.userid).join( + Profiles, Profiles.profileid == SubProfiles.profileid)\ + .where(SubProfiles.authtoken == auth_token).first() if result is None: raise AuthException( "No account exists with the provided authtoken.") user: Users = result[0] profile: Profiles = result[1] subprofile: SubProfiles = result[2] + assert isinstance(user.userid, int) + assert isinstance(profile.profileid, int) + assert isinstance(profile.nick, str) + assert isinstance(subprofile.uniquenick, str) + assert isinstance(subprofile.cdkeyenc, str) return user.userid, profile.profileid, profile.nick, subprofile.uniquenick, subprofile.cdkeyenc -@overload -def get_info(uniquenick: str, namespace_id: int) -> tuple[int, int, str, str, str]: +def get_info_by_uniquenick(uniquenick: str, namespace_id: int) -> tuple[int, int, str, str, str]: """ return [user_id,profile_id,profile_nick,unique_nick,cdkey_hash] """ - result = PG_SESSION.query(Users, Profiles, SubProfiles).join(Users, (Users.userid, Profiles.userid)).join( - Profiles, (Profiles.profileid, SubProfiles.profileid)).where(SubProfiles.uniquenick == uniquenick, SubProfiles.namespaceid == namespace_id).first() + result = PG_SESSION.query(Users, Profiles, SubProfiles)\ + .join(Users, Users.userid == Profiles.userid)\ + .join(Profiles, Profiles.profileid == SubProfiles.profileid)\ + .where(SubProfiles.uniquenick == uniquenick, SubProfiles.namespaceid == namespace_id).first() if result is None: raise AuthException( @@ -64,6 +94,12 @@ def get_info(uniquenick: str, namespace_id: int) -> tuple[int, int, str, str, st user: Users = result[0] profile: Profiles = result[1] subprofile: SubProfiles = result[2] + assert isinstance(user.userid, int) + assert isinstance(profile.profileid, int) + assert isinstance(profile.nick, str) + assert isinstance(subprofile.uniquenick, str) + assert isinstance(subprofile.cdkeyenc, str) + return user.userid, profile.profileid, profile.nick, subprofile.uniquenick, subprofile.cdkeyenc # region d2g @@ -89,10 +125,12 @@ def get_user_data(table_id: int,) -> dict: def update_user_data(table_id: int, data: dict) -> None: result = PG_SESSION.query(SakeStorage).where( SakeStorage.tableid == table_id).first() - + if result is None: + raise SakeException("user data not found") + assert isinstance(result.data, dict) for key, value in result.data.items(): if key in data: - if data[key] is None or data[key] is "": + if data[key] is None or data[key] == "": raise SakeException(f"the value of {key} should not be None.") if value == data[key]: continue @@ -100,6 +138,8 @@ def update_user_data(table_id: int, data: dict) -> None: def create_records(table_id: int, data: dict) -> None: + assert isinstance(table_id, int) + assert isinstance(data, dict) result = PG_SESSION.query(SakeStorage).where( SakeStorage.tableid == table_id).count() @@ -110,3 +150,10 @@ def create_records(table_id: int, data: dict) -> None: PG_SESSION.add(sake) PG_SESSION.commit() + + +if __name__ == "__main__": + result = PG_SESSION.query(Users, Profiles, SubProfiles)\ + .join(Profiles, Users.userid == Profiles.userid)\ + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid)\ + .where(Users.userid == 1).first() diff --git a/src/backends/protocols/gamespy/web_services/handlers.py b/src/backends/protocols/gamespy/web_services/handlers.py index 550fc4d3d..6d6b1089c 100644 --- a/src/backends/protocols/gamespy/web_services/handlers.py +++ b/src/backends/protocols/gamespy/web_services/handlers.py @@ -13,10 +13,10 @@ class LoginProfileHandler(HandlerBase): _request: LoginProfileRequest async def _data_operate(self) -> None: - self.data = data.get_info(uniquenick=self._request.uniquenick, - namespace_id=self._request.namespace_id, - cdkey=self._request.cdkey, - email=self._request.email) + self.data = data.get_info_by_cdkey_email(uniquenick=self._request.uniquenick, + namespace_id=self._request.namespace_id, + cdkey=self._request.cdkey, + email=self._request.email) async def _result_construct(self) -> None: self._result = LoginProfileResult( @@ -38,7 +38,8 @@ class LoginRemoteAuthHandler(HandlerBase): _request: LoginRemoteAuthRequest async def _data_operate(self) -> None: - self.data = data.get_info(auth_token=self._request.auth_token) + self.data = data.get_info_by_authtoken( + auth_token=self._request.auth_token) async def _result_construct(self) -> None: self._result = LoginProfileResult( @@ -53,8 +54,8 @@ class LoginUniqueNickHandler(HandlerBase): _request: LoginUniqueNickRequest async def _data_operate(self) -> None: - self.data = data.get_info(uniquenick=self._request.uniquenick, - namespace_id=self._request.namespace_id) + self.data = data.get_info_by_uniquenick(uniquenick=self._request.uniquenick, + namespace_id=self._request.namespace_id) async def _result_construct(self) -> None: self._result = LoginProfileResult( diff --git a/src/backends/protocols/gamespy/web_services/requests.py b/src/backends/protocols/gamespy/web_services/requests.py index 9c1588279..aeac55158 100644 --- a/src/backends/protocols/gamespy/web_services/requests.py +++ b/src/backends/protocols/gamespy/web_services/requests.py @@ -7,7 +7,7 @@ class SakeRequestBase(lib.RequestBase): game_id: int secret_key: str login_ticket: str - table_id: str + table_id: int class CreateRecordData(BaseModel): diff --git a/src/backends/routers/gamespy/gstats.py b/src/backends/routers/gamespy/gstats.py index 4d019514f..988c1239d 100644 --- a/src/backends/routers/gamespy/gstats.py +++ b/src/backends/routers/gamespy/gstats.py @@ -1,5 +1,5 @@ from fastapi import APIRouter -from backends.protocols.gamespy.game_status.requests import * +from backends.protocols.gamespy.game_status.handlers import * from backends.urls import GAMESTATUS router = APIRouter() @@ -7,7 +7,10 @@ @router.post(f"{GAMESTATUS}/AuthGameHandler") async def auth_game(request: AuthGameRequest): - raise NotImplementedError() + handler = AuthGameHandler(request) + await handler.handle() + return handler.response + @router.post(f"{GAMESTATUS}/AuthPlayerHandler") async def auth_player(request: AuthPlayerRequest): diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index ec0c9fc80..501d8649a 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -1,4 +1,7 @@ +from ipaddress import IPv4Address +from uuid import UUID from fastapi import FastAPI +from pydantic import BaseModel import uvicorn from library.src.log.log_manager import LogManager @@ -24,6 +27,15 @@ async def home(request: ServerConfig) -> dict: return {"status": "online"} +class RegisterRequest(BaseModel): + server_id: UUID + client_ip: IPv4Address + + +@app.post("/token") +async def get_auth_token(request: RegisterRequest): + + pass if __name__ == "__main__": uvicorn.run("backends.routers.home:app", host="127.0.0.1", port=8080, reload=True) From 253ad5cdd94105c6a840bfebd1d8cae794112e73 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 3 Jan 2025 04:13:18 +0000 Subject: [PATCH 149/231] refactor: aux files --- src/.vscode/settings.json | 2 +- src/library/src/configs.py | 8 +++++--- .../presence_connection_manager/src/aggregates/enums.py | 7 ++++++- .../presence_connection_manager/src/contracts/requests.py | 1 + 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index 459e8e9f1..cbe3eecc2 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -1,5 +1,5 @@ { - "python.analysis.typeCheckingMode": "off", + "python.analysis.typeCheckingMode": "basic", "workbench.iconTheme": "material-icon-theme", "python.testing.unittestArgs": [ "-v", diff --git a/src/library/src/configs.py b/src/library/src/configs.py index 701fff446..8fec572d0 100644 --- a/src/library/src/configs.py +++ b/src/library/src/configs.py @@ -2,8 +2,7 @@ from typing import Literal, Optional from uuid import UUID -from pydantic import BaseModel,field_validator - +from pydantic import BaseModel, field_validator class PostgreSql(BaseModel): @@ -63,7 +62,9 @@ def expand_user_path(cls, value): class BackendConfig(BaseModel): url: str - + token_secret_key: str + token_algorithm: str + token_expire_time: int = 30 class UniSpyServerConfig(BaseModel): @@ -73,6 +74,7 @@ class UniSpyServerConfig(BaseModel): servers: dict[str, ServerConfig] logging: LoggingConfig + unispy_config = os.environ.get("UNISPY_CONFIG") default_config = "../common/config.json" if unispy_config is None: diff --git a/src/servers/presence_connection_manager/src/aggregates/enums.py b/src/servers/presence_connection_manager/src/aggregates/enums.py index d25982492..bd1706ae7 100644 --- a/src/servers/presence_connection_manager/src/aggregates/enums.py +++ b/src/servers/presence_connection_manager/src/aggregates/enums.py @@ -256,7 +256,12 @@ class RequestType(Enum): INVITETO = "inviteto" -class FriendRequestStatus(Enum): +class FriendRequestStatus(IntEnum): PENDING = 0 ACCEPTED = 1 REJECTED = 2 + + +if __name__ == "__main__": + + pass \ No newline at end of file diff --git a/src/servers/presence_connection_manager/src/contracts/requests.py b/src/servers/presence_connection_manager/src/contracts/requests.py index fbdeb3b61..0d72a1910 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests.py +++ b/src/servers/presence_connection_manager/src/contracts/requests.py @@ -22,6 +22,7 @@ # region General EXTRA_INFO_DICT: dict[str, type] = { + "location": str, "firstname": str, "lastname": str, "publicmask": int, From 1cdc6aba68bbeb9221841aed73e966fe74f29af2 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 4 Jan 2025 09:31:39 +0800 Subject: [PATCH 150/231] refactor: added database unittests --- common/UniSpy_pg.sql | 1 + src/backends/protocols/gamespy/chat/data.py | 8 +- .../protocols/gamespy/game_status/data.py | 6 +- .../gamespy/game_traffic_relay/data.py | 2 +- src/backends/protocols/gamespy/natneg/data.py | 6 +- .../presence_connection_manager/data.py | 28 +++---- .../gamespy/presence_search_player/data.py | 80 +++++++------------ .../presence_search_player/handlers.py | 6 ++ .../protocols/gamespy/query_report/data.py | 8 +- src/backends/tests/__init__.py | 0 src/backends/tests/gamespy/__init__.py | 0 src/backends/tests/gamespy/chat/room_tests.py | 5 ++ .../precence_search_player/__init__.py | 0 .../data_fetch_tests.py | 43 ++++++++++ .../precence_search_player/db_tests.py | 1 - .../src/contracts/requests.py | 3 + 16 files changed, 117 insertions(+), 80 deletions(-) create mode 100644 src/backends/tests/__init__.py create mode 100644 src/backends/tests/gamespy/__init__.py create mode 100644 src/backends/tests/gamespy/precence_search_player/__init__.py create mode 100644 src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py delete mode 100644 src/backends/tests/gamespy/precence_search_player/db_tests.py diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index cb5f13739..9200562df 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -5440,6 +5440,7 @@ COPY unispy.sakestorage (sakestorageid, tableid, data) FROM stdin; -- COPY unispy.subprofiles (subprofileid, profileid, uniquenick, namespaceid, partnerid, productid, gamename, cdkeyenc, firewall, port, authtoken, session_key) FROM stdin; +1 1 spyguy_test 0 1 1 gmtests encrypted_cdkey 0 8080 auth_token_example session_key_example \. diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index 5f862b7be..3d928fb2e 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -89,7 +89,7 @@ def uniquenick_login(uniquenick:str,namespace_id:int)-> tuple[int, int, bool, bo def is_channel_exist(channel_name:str,game_name:str)->bool: channel_count = PG_SESSION.query(ChatChannelCaches)\ - .filter(ChatChannelCaches.channel_name == channel_name, + .where(ChatChannelCaches.channel_name == channel_name, ChatChannelCaches.game_name == game_name, ChatChannelCaches.update_time >= datetime.now()-timedelta(minutes=10))\ .count() @@ -103,7 +103,7 @@ def add_channel(channel:ChatChannelCaches): def get_channel_cache(channel_name:str,game_name:str)->ChatChannelCaches: channel = PG_SESSION.query(ChatChannelCaches)\ - .filter(ChatChannelCaches.channel_name == channel_name, + .where(ChatChannelCaches.channel_name == channel_name, ChatChannelCaches.game_name == game_name)\ .first() return channel @@ -114,7 +114,7 @@ def update_channel(channel:ChatChannelCaches): def get_user_cache_by_nick_name(nick_name:str)->ChatUserCaches: - result = PG_SESSION.query(ChatUserCaches).filter(ChatUserCaches.nick_name == nick_name).first() + result = PG_SESSION.query(ChatUserCaches).where(ChatUserCaches.nick_name == nick_name).first() return result def remove_channel(cache:ChatChannelCaches)->None: @@ -128,7 +128,7 @@ def remove_user(cache:ChatUserCaches): PG_SESSION.commit() def is_user_exist(ip:str,port:int)->bool: - user_count= PG_SESSION.query(ChatUserCaches).filter(ChatUserCaches.remote_ip_address==ip, + user_count= PG_SESSION.query(ChatUserCaches).where(ChatUserCaches.remote_ip_address==ip, ChatUserCaches.remote_port==port).count() if user_count ==1: return True diff --git a/src/backends/protocols/gamespy/game_status/data.py b/src/backends/protocols/gamespy/game_status/data.py index 7fc8fd1cb..bef87e216 100644 --- a/src/backends/protocols/gamespy/game_status/data.py +++ b/src/backends/protocols/gamespy/game_status/data.py @@ -20,7 +20,7 @@ def update_player_data(): def get_profile_id_by_token(token: str) -> int: assert isinstance(token, str) - result = PG_SESSION.query(SubProfiles.profileid).filter( + result = PG_SESSION.query(SubProfiles.profileid).where( SubProfiles.authtoken == token).first() if result is None: raise GSException("No records found in database") @@ -50,7 +50,7 @@ def get_profile_id_by_cdkey(cdkey: str, nick_name: str) -> int: assert isinstance(SubProfiles.cdkeyenc, Column) result = PG_SESSION.query(SubProfiles.profileid).join( SubProfiles, Profiles.profileid == SubProfiles.profileid)\ - .filter(SubProfiles.cdkeyenc == cdkey, + .where(SubProfiles.cdkeyenc == cdkey, Profiles.nick == nick_name)\ .first() if result is None: @@ -61,7 +61,7 @@ def get_profile_id_by_cdkey(cdkey: str, nick_name: str) -> int: def get_player_data(profile_id: int, storage_type: PersistStorageType, data_index: int) -> dict: - result = PG_SESSION.query(PStorage.data).filter(PStorage.ptype == storage_type.value, + result = PG_SESSION.query(PStorage.data).where(PStorage.ptype == storage_type.value, PStorage.dindex == data_index, PStorage.profileid == profile_id).first() if result is None: diff --git a/src/backends/protocols/gamespy/game_traffic_relay/data.py b/src/backends/protocols/gamespy/game_traffic_relay/data.py index ce2ee5543..8750794d6 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/data.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/data.py @@ -22,7 +22,7 @@ def delete_relay_server(server_id: UUID, ip_address: str, port: int): assert isinstance(server_id, UUID) assert isinstance(ip_address, str) assert isinstance(port, int) - info = PG_SESSION.query(RelayServerCaches).filter( + info = PG_SESSION.query(RelayServerCaches).where( RelayServerCaches.server_id == server_id, RelayServerCaches.public_ip_address == ip_address, RelayServerCaches.public_port == port).first() PG_SESSION.delete(info) PG_SESSION.commit() diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py index 632553a8d..1150da7b0 100644 --- a/src/backends/protocols/gamespy/natneg/data.py +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -12,7 +12,7 @@ def store_init_packet(info: InitPacketCaches) -> None: def count_init_info(cookie: int, version: int) -> int: time = datetime.now()-timedelta(seconds=30) - result = PG_SESSION.query(InitPacketCaches).filter( + result = PG_SESSION.query(InitPacketCaches).where( InitPacketCaches.cookie == cookie, InitPacketCaches.version == version, InitPacketCaches.update_time >= time).count() @@ -22,7 +22,7 @@ def count_init_info(cookie: int, version: int) -> int: def get_init_infos(server_id: UUID, cookie: int, client_index: NatClientIndex) -> list[InitPacketCaches]: # query the latest init info with in 30 seconds time = datetime.now()-timedelta(seconds=30) - result = PG_SESSION.query(InitPacketCaches).filter( + result = PG_SESSION.query(InitPacketCaches).where( InitPacketCaches.server_id == server_id, InitPacketCaches.cookie == cookie, InitPacketCaches.client_index == client_index, @@ -69,6 +69,6 @@ def get_nat_fail_info(info: NatFailCaches): def get_nat_fail_info_by_ip(public_ip1: str, public_ip2: str) -> list[NatFailCaches]: - result = PG_SESSION.query(NatFailCaches).filter(NatFailCaches.public_ip_address1 == public_ip1, + result = PG_SESSION.query(NatFailCaches).where(NatFailCaches.public_ip_address1 == public_ip1, NatFailCaches.public_ip_address2 == public_ip2).all() return result diff --git a/src/backends/protocols/gamespy/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py index b6ce5fb3b..a346c4dc0 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/data.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/data.py @@ -34,14 +34,14 @@ def update_online_time(ip: str, port: int): def is_email_exist(email: str) -> bool: if TYPE_CHECKING: assert isinstance(Users.email, Column) - if PG_SESSION.query(Users).filter(Users.email == email).count() == 1: + if PG_SESSION.query(Users).where(Users.email == email).count() == 1: return True else: return False def delete_friend_by_profile_id(profile_id: int): - friend = PG_SESSION.query(Friends).filter( + friend = PG_SESSION.query(Friends).where( Friends.friendid == profile_id).first() if friend is None: raise GPDatabaseException( @@ -55,7 +55,7 @@ def delete_friend_by_profile_id(profile_id: int): def get_blocked_profile_id_list(profile_id: int, namespace_id: int) -> list[int]: result = ( PG_SESSION.query(Blocked.targetid) - .filter(Blocked.profileid == profile_id, Blocked.namespaceid == namespace_id) + .where(Blocked.profileid == profile_id, Blocked.namespaceid == namespace_id) .all() ) if TYPE_CHECKING: @@ -66,7 +66,7 @@ def get_blocked_profile_id_list(profile_id: int, namespace_id: int) -> list[int] def get_friend_profile_id_list(profile_id: int, namespace_id: int) -> list[int]: result = ( PG_SESSION.query(Friends.targetid) - .filter(Friends.profileid == profile_id, Friends.namespaceid == namespace_id) + .where(Friends.profileid == profile_id, Friends.namespaceid == namespace_id) .all() ) if TYPE_CHECKING: @@ -153,7 +153,7 @@ def get_user_info_list(email: str, nick_name: str) -> list[tuple[int, int, int]] SubProfiles.subprofileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter(Users.email == email, Profiles.nick == nick_name) + .where(Users.email == email, Profiles.nick == nick_name) .all() ) if TYPE_CHECKING: @@ -176,7 +176,7 @@ def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: SubProfiles.subprofileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter( + .where( SubProfiles.uniquenick == unique_nick, SubProfiles.namespaceid == namespace_id, ) @@ -213,7 +213,7 @@ def get_user_infos_by_uniquenick_namespace_id(unique_nick: str, namespace_id: in SubProfiles.namespaceid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter( + .where( SubProfiles.uniquenick == unique_nick, SubProfiles.namespaceid == namespace_id, ) @@ -250,7 +250,7 @@ def get_user_infos_by_nick_email(nick: str, email: str) -> LoginData | None: ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter( + .where( Users.email == email, Profiles.nick == nick ) @@ -296,7 +296,7 @@ def get_user_infos_by_authtoken(auth_token: str) -> LoginData | None: SubProfiles.namespaceid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter( + .where( SubProfiles.authtoken == auth_token ) .first() @@ -310,7 +310,7 @@ def get_user_infos_by_authtoken(auth_token: str) -> LoginData | None: def get_block_list(profile_id: int, namespace_id: int) -> list[int]: result = ( PG_SESSION.query(Blocked.targetid) - .filter( + .where( Blocked.namespaceid == namespace_id, Blocked.profileid == profile_id, ).all() @@ -325,7 +325,7 @@ def get_block_list(profile_id: int, namespace_id: int) -> list[int]: def get_buddy_list(profile_id: int, namespace_id: int) -> list[int]: result = ( PG_SESSION.query(Friends.targetid) - .filter( + .where( Blocked.namespaceid == namespace_id, Blocked.profileid == profile_id, ).all() @@ -360,7 +360,7 @@ def update_block(profile_id: int, target_id: int, session_key: str) -> None: def update_friend_info(target_id: int, profile_id: int, namespace_id: int): result = ( PG_SESSION.query(Friends) - .filter( + .where( Friends.targetid == target_id, Friends.namespaceid == namespace_id, Friends.profileid == profile_id, @@ -387,7 +387,7 @@ def add_nick_name(profile_id: int, old_nick: str, new_nick: str): assert isinstance(Profiles.nick, Column) result = ( PG_SESSION.query(Profiles) - .filter(Profiles.profileid == profile_id, Profiles.nick == old_nick) + .where(Profiles.profileid == profile_id, Profiles.nick == old_nick) .first() ) @@ -406,7 +406,7 @@ def add_nick_name(profile_id: int, old_nick: str, new_nick: str): def update_unique_nick(subprofile_id: int, unique_nick: str): result = ( PG_SESSION.query(SubProfiles) - .filter(SubProfiles.subprofileid == subprofile_id) + .where(SubProfiles.subprofileid == subprofile_id) .first() ) result.uniquenick = unique_nick # type:ignore diff --git a/src/backends/protocols/gamespy/presence_search_player/data.py b/src/backends/protocols/gamespy/presence_search_player/data.py index 2db3ccd05..4ba6ce746 100644 --- a/src/backends/protocols/gamespy/presence_search_player/data.py +++ b/src/backends/protocols/gamespy/presence_search_player/data.py @@ -7,26 +7,25 @@ Users, PG_SESSION, ) +from servers.presence_search_player.src.aggregates.exceptions import CheckException from servers.presence_search_player.src.contracts.results import * def verify_email(email: str): - if TYPE_CHECKING: - Users.email = cast(Column, Users.email) - Users.password = cast(Column, Users.password) - if PG_SESSION.query(Users).filter(Users.email == email).count() == 1: + assert isinstance(email, str) + if PG_SESSION.query(Users).where(Users.email == email).count() == 1: return True else: return False def verify_email_and_password(email: str, password: str): - if TYPE_CHECKING: - assert isinstance(Users.email, Column) - assert isinstance(Users.password, Column) + assert isinstance(email, str) + assert isinstance(password, str) + result = ( PG_SESSION.query(Users) - .filter(Users.email == email, Users.password == password) + .where(Users.email == email, Users.password == password) .count() ) if result == 1: @@ -34,31 +33,22 @@ def verify_email_and_password(email: str, password: str): return False -def get_profile_id(email: str, password: str, nick_name: str, partner_id: int) -> int: - if TYPE_CHECKING: - assert isinstance(Users.email, Column) - assert isinstance(Users.password, Column) - assert isinstance(Profiles.userid, Column) - assert isinstance(Users.userid, Column) - assert isinstance(Profiles.profileid, Column) - assert isinstance(Profiles.nick, Column) - assert isinstance(SubProfiles.partnerid, Column) - - pid = ( +def get_profile_id(email: str, password: str, nick_name: str, partner_id: int) -> int | None: + result = ( PG_SESSION.query(Profiles.profileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter( + .where( Users.email == email, Users.password == password, Profiles.nick == nick_name, SubProfiles.partnerid == partner_id, - ) - .first() + ).first() ) - if TYPE_CHECKING: - pid = cast(int, pid) - return pid + if result is not None: + result = result[0] + assert isinstance(result, int) + return result def add_user(user: Users): @@ -93,14 +83,14 @@ def update_subprofile(subprofile: SubProfiles): def get_user(email: str) -> Users | None: assert isinstance(email, str) - result = PG_SESSION.query(Users).filter(Users.email == email).first() + result = PG_SESSION.query(Users).where(Users.email == email).first() return result def get_profile(user_id: int, nick_name: str) -> Profiles: assert isinstance(user_id, int) assert isinstance(nick_name, str) - result = PG_SESSION.query(Profiles).filter( + result = PG_SESSION.query(Profiles).where( Profiles.userid == user_id, Profiles.nick == nick_name ).first() return result @@ -111,7 +101,7 @@ def get_sub_profile(profile_id: int, namespace_id: int, product_id: int) -> SubP assert isinstance(SubProfiles.profileid, Column) assert isinstance(SubProfiles.namespaceid, Column) assert isinstance(SubProfiles.namespaceid, Column) - result = PG_SESSION.query(SubProfiles).filter( + result = PG_SESSION.query(SubProfiles).where( SubProfiles.profileid == profile_id, SubProfiles.namespaceid == namespace_id, SubProfiles.namespaceid == product_id, @@ -123,30 +113,20 @@ def get_nick_and_unique_nick_list(email: str, password: str, namespace_id: int) """ return [(nick, uniquenick)] """ - if TYPE_CHECKING: - assert isinstance(Profiles.nick, Column) - assert isinstance(SubProfiles.uniquenick, Column) - assert isinstance(Users.email, Column) - assert isinstance(Users.password, Column) - assert isinstance(SubProfiles.namespaceid, Column) - assert isinstance(Profiles.nick, Column) - assert isinstance(SubProfiles.uniquenick, Column) - assert isinstance(Profiles.userid, Column) - assert isinstance(Profiles.profileid, Column) - assert isinstance(SubProfiles.namespaceid, Column) result = ( PG_SESSION.query(Profiles.nick, SubProfiles.uniquenick) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter( + .where( Users.email == email, Users.password == password, SubProfiles.namespaceid == namespace_id, ) .all() ) + assert isinstance(result, list) if TYPE_CHECKING: - result = cast(list, result) + result = cast(list[tuple[str, str]], result) return result @@ -179,7 +159,7 @@ def get_friend_info_list(profile_id: int, namespace_id: int, game_name: str) -> ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter( + .where( Profiles.profileid.in_(PG_SESSION.query( Friends.profileid == profile_id)), SubProfiles.namespaceid == namespace_id, @@ -203,7 +183,7 @@ def get_matched_profile_info_list( assert isinstance(SubProfiles.namespaceid, Column) result = ( PG_SESSION.query(SubProfiles.profileid, SubProfiles.uniquenick) - .filter( + .where( SubProfiles.profileid.in_(profile_ids), SubProfiles.namespaceid == namespace_id, ) @@ -241,7 +221,7 @@ def get_matched_info_by_nick( ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter(Profiles.nick == nick_name) + .where(Profiles.nick == nick_name) .all() ) temp: list[SearchResultData] = [] @@ -275,7 +255,7 @@ def get_matched_info_by_email( ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter(Users.email == email) + .where(Users.email == email) .all() ) temp: list[SearchResultData] = [] @@ -307,7 +287,7 @@ def get_matched_info_by_nick_and_email(nick_name: str, email: str): ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter(Users.email == email, Profiles.nick == nick_name) + .where(Users.email == email, Profiles.nick == nick_name) .all() ) temp: list[SearchResultData] = [] @@ -340,7 +320,7 @@ def get_matched_info_by_uniquenick_and_namespaceid( ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter( + .where( SubProfiles.uniquenick == unique_nick, SubProfiles.namespaceid == namespace_id, ) @@ -378,7 +358,7 @@ def get_matched_info_by_uniquenick_and_namespaceids( ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter( + .where( SubProfiles.uniquenick == unique_nick, SubProfiles.namespaceid.in_(namespace_ids) ) @@ -401,7 +381,7 @@ def is_uniquenick_exist(unique_nick: str, namespace_id: int, game_name: str) -> result = ( PG_SESSION.query(Profiles) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .filter( + .where( SubProfiles.uniquenick == unique_nick, SubProfiles.gamename == game_name, SubProfiles.namespaceid == namespace_id, @@ -419,7 +399,7 @@ def is_email_exist(email: str) -> bool: if TYPE_CHECKING: Users.userid = cast(Column, Users.userid) Users.email = cast(Column, Users.email) - result = PG_SESSION.query(Users.userid).filter( + result = PG_SESSION.query(Users.userid).where( Users.email == email).count() # According to game partnerid is not nessesary if result == 0: diff --git a/src/backends/protocols/gamespy/presence_search_player/handlers.py b/src/backends/protocols/gamespy/presence_search_player/handlers.py index e035b2c91..e82648542 100644 --- a/src/backends/protocols/gamespy/presence_search_player/handlers.py +++ b/src/backends/protocols/gamespy/presence_search_player/handlers.py @@ -9,6 +9,9 @@ class CheckHandler(HandlerBase): + """ + todo: whether need check the partner id, which means whether we need to check subprofiles + """ _request: CheckRequest async def _data_operate(self) -> None: @@ -18,6 +21,9 @@ async def _data_operate(self) -> None: raise CheckException("The password is incorrect") profile_id = data.get_profile_id( self._request.email, self._request.password, self._request.nick, self._request.partner_id) + if profile_id is None: + raise CheckException(f"No pid found with email{ + self._request.email}") self._result = CheckResult(profile_id=profile_id) diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index cb8b499c2..035c72782 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -42,7 +42,7 @@ def get_peer_staging_channels(game_name: str, group_id: int) -> list[GameServerI assert isinstance(game_name, str) assert isinstance(group_id, int) staging_name = f"{PeerRoom.StagingRoomPrefix}!{game_name}!*" - result = PG_SESSION.query(ChatChannelCaches).filter( + result = PG_SESSION.query(ChatChannelCaches).where( ChatChannelCaches.channel_name == staging_name).all() data = [] for s in result: @@ -54,7 +54,7 @@ def get_peer_group_channel(group_id: int) -> list[GameServerInfo]: assert isinstance(group_id, int) group_name = f"{PeerRoom.GroupRoomPrefix}!{group_id}" - result = PG_SESSION.query(ChatChannelCaches).filter( + result = PG_SESSION.query(ChatChannelCaches).where( ChatChannelCaches.channel_name == group_name).all() data = [] for s in result: @@ -64,7 +64,7 @@ def get_peer_group_channel(group_id: int) -> list[GameServerInfo]: def get_server_info_with_instant_key(instant_key: int) -> Optional[GameServerInfo]: assert isinstance(instant_key, int) - result = PG_SESSION.query(GameServerCaches).filter( + result = PG_SESSION.query(GameServerCaches).where( GameServerCaches.instant_key == instant_key).first() return result @@ -88,7 +88,7 @@ def get_server_info_list_with_game_name(game_name: str) -> list[GameServerInfo]: def get_server_info_with_ip_and_port(ip: str, port: int) -> GameServerInfo: assert isinstance(ip, str) assert isinstance(port, int) - result = PG_SESSION.query(GameServerCaches).filter( + result = PG_SESSION.query(GameServerCaches).where( GameServerCaches.host_ip_address == ip, GameServerCaches.query_report_port == port).first() if result is None: raise ServerBrowserException("game server not found") diff --git a/src/backends/tests/__init__.py b/src/backends/tests/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/gamespy/__init__.py b/src/backends/tests/gamespy/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/gamespy/chat/room_tests.py b/src/backends/tests/gamespy/chat/room_tests.py index c7064d32e..c0c9da40a 100644 --- a/src/backends/tests/gamespy/chat/room_tests.py +++ b/src/backends/tests/gamespy/chat/room_tests.py @@ -20,3 +20,8 @@ # def test_single_join(self, user_name="unispy", nick_name="unispy", channel_name="#GSP!room!test"): # pass + + +from fastapi import testclient + +# testclient.TestClient.get() \ No newline at end of file diff --git a/src/backends/tests/gamespy/precence_search_player/__init__.py b/src/backends/tests/gamespy/precence_search_player/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py b/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py new file mode 100644 index 000000000..f1216ec97 --- /dev/null +++ b/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py @@ -0,0 +1,43 @@ +# the tests related to database operations +from unittest import TestCase +from backends.library.database.pg_orm import Profiles, Users +import backends.protocols.gamespy.presence_search_player.data as data + + +class DataFetchTests(TestCase): + def test_verify_email(self): + result1 = data.verify_email("spyguy@unispy.net") + self.assertFalse(result1) + result2 = data.verify_email("spyguy@gamespy.com") + self.assertTrue(result2) + + def test_verify_email_and_password(self): + result1 = data.verify_email_and_password( + "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d91") + self.assertFalse(result1) + result2 = data.verify_email_and_password( + "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b") + self.assertTrue(result2) + + def test_get_profile_id(self): + result1 = data.get_profile_id( + "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b", "spyguy1", 1) + self.assertIsNone(result1) + result2 = data.get_profile_id( + "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b", "spyguy", 1) + self.assertIsNotNone(result2) + self.assertEqual(result2, 1) + + def test_get_users(self): + result1 = data.get_user("spyguy@gamespy.com") + self.assertIsNotNone(result1) + self.assertEqual(type(result1), Users) + result2 = data.get_user("spyguy_not_user@gamespy.com") + self.assertIsNone(result2) + + def test_get_profile(self): + result1 = data.get_profile(1, "spyguy") + self.assertEqual(type(result1), Profiles) + + result2 = data.get_profile(1, "spyguy_not_profile") + self.assertIsNone(result2) diff --git a/src/backends/tests/gamespy/precence_search_player/db_tests.py b/src/backends/tests/gamespy/precence_search_player/db_tests.py deleted file mode 100644 index e0f6fed08..000000000 --- a/src/backends/tests/gamespy/precence_search_player/db_tests.py +++ /dev/null @@ -1 +0,0 @@ -# the tests related to database operations diff --git a/src/servers/presence_search_player/src/contracts/requests.py b/src/servers/presence_search_player/src/contracts/requests.py index 04bef48e7..a809fc873 100644 --- a/src/servers/presence_search_player/src/contracts/requests.py +++ b/src/servers/presence_search_player/src/contracts/requests.py @@ -28,6 +28,9 @@ def parse(self): if "partner_id" in self.request_dict.keys(): self.partner_id = int(self.request_dict["partner_id"]) + else: + raise GPParseException( + "no partner id found, check whether need implement the default partnerid") class NewUserRequest(RequestBase): From 5f8251e0dfe8b5c5b151ea7495835eed45260fe6 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 7 Jan 2025 15:44:45 +0800 Subject: [PATCH 151/231] fix: database insersion errors --- common/UniSpy_pg.sql | 75 +------- common/config.json | 22 +-- src/backends/library/database/pg_orm.py | 9 +- .../presence_connection_manager/data.py | 11 +- .../gamespy/presence_search_player/data.py | 175 ++++++++---------- .../protocols/gamespy/query_report/data.py | 22 ++- .../gamespy/query_report/handlers.py | 9 +- .../gamespy/query_report/requests.py | 14 +- src/backends/routers/gamespy/query_report.py | 6 +- .../data_fetch_tests.py | 37 ++++ .../tests/gamespy/query_report/__init__.py | 0 .../tests/gamespy/query_report/api_tests.py | 14 ++ .../gamespy/query_report/data_fetch_tests.py | 20 ++ src/library/src/abstractions/connections.py | 2 +- src/library/src/abstractions/handler.py | 6 +- .../src/abstractions/server_launcher.py | 5 +- .../src/applications/server_launcher.py | 3 +- .../query_report/src/v2/aggregates/enums.py | 16 +- .../query_report/src/v2/contracts/requests.py | 4 +- 19 files changed, 217 insertions(+), 233 deletions(-) create mode 100644 src/backends/tests/gamespy/query_report/__init__.py create mode 100644 src/backends/tests/gamespy/query_report/api_tests.py create mode 100644 src/backends/tests/gamespy/query_report/data_fetch_tests.py diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 9200562df..546d2e6ad 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -32,73 +32,6 @@ ALTER SCHEMA unispy OWNER TO unispy; COMMENT ON SCHEMA unispy IS 'standard public schema'; --- --- Name: friendrequeststatus; Type: TYPE; Schema: unispy; Owner: unispy --- - -CREATE TYPE unispy.friendrequeststatus AS ENUM ( - 'PENDING', - 'ACCEPTED', - 'REJECTED' -); - - -ALTER TYPE unispy.friendrequeststatus OWNER TO unispy; - --- --- Name: gameserverstatus; Type: TYPE; Schema: unispy; Owner: unispy --- - -CREATE TYPE unispy.gameserverstatus AS ENUM ( - 'NORMAL', - 'UPDATE', - 'SHUTDOWN', - 'PLAYING' -); - - -ALTER TYPE unispy.gameserverstatus OWNER TO unispy; - --- --- Name: gpstatuscode; Type: TYPE; Schema: unispy; Owner: unispy --- - -CREATE TYPE unispy.gpstatuscode AS ENUM ( - 'OFFLINE', - 'ONLINE', - 'PLAYING', - 'STAGING', - 'CHATTING', - 'AWAY' -); - - -ALTER TYPE unispy.gpstatuscode OWNER TO unispy; - --- --- Name: natclientindex; Type: TYPE; Schema: unispy; Owner: unispy --- - -CREATE TYPE unispy.natclientindex AS ENUM ( - 'GAME_CLIENT', - 'GAME_SERVER' -); - - -ALTER TYPE unispy.natclientindex OWNER TO unispy; - --- --- Name: natporttype; Type: TYPE; Schema: unispy; Owner: unispy --- - -CREATE TYPE unispy.natporttype AS ENUM ( - 'GP', - 'NN1', - 'NN2', - 'NN3' -); - - ALTER TYPE unispy.natporttype OWNER TO unispy; SET default_tablespace = ''; @@ -115,7 +48,7 @@ CREATE TABLE unispy.addrequests ( targetid integer NOT NULL, namespaceid integer NOT NULL, reason character varying NOT NULL, - status unispy.friendrequeststatus NOT NULL + status smallint NOT NULL ); @@ -280,13 +213,13 @@ ALTER SEQUENCE unispy.friends_friendid_seq OWNED BY unispy.friends.friendid; -- CREATE TABLE unispy.game_server_caches ( - instant_key integer NOT NULL, + instant_key character varying(10) NOT NULL, server_id uuid NOT NULL, host_ip_address inet NOT NULL, game_name character varying NOT NULL, query_report_port integer NOT NULL, update_time timestamp without time zone NOT NULL, - status unispy.gameserverstatus, + status smallint not NULL, player_data jsonb NOT NULL, server_data jsonb NOT NULL, team_data jsonb NOT NULL, @@ -507,7 +440,7 @@ CREATE TABLE unispy.profiles ( userid integer NOT NULL, nick character varying NOT NULL, serverflag integer NOT NULL, - status integer, + status smallint not NULL, statstring character varying, extra_info jsonb ); diff --git a/common/config.json b/common/config.json index 4e205379e..6086089da 100644 --- a/common/config.json +++ b/common/config.json @@ -38,67 +38,67 @@ }, "servers": { "PresenceConnectionManager": { - "server_id": "00000000-0000-0000-0000-000000000000", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "PresenceConnectionManager", "public_address": "0.0.0.0", "listening_port": 29900 }, "PresenceSearchPlayer": { - "server_id": "00000000-0000-0000-0000-000000000000", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "PresenceSearchPlayer", "public_address": "0.0.0.0", "listening_port": 29901 }, "CDKey": { - "server_id": "00000000-0000-0000-0000-000000000000", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "CDKey", "public_address": "0.0.0.0", "listening_port": 29910 }, "ServerBrowserV1": { - "server_id": "00000000-0000-0000-0000-000000000000", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "ServerBrowserV1", "public_address": "0.0.0.0", "listening_port": 28900 }, "ServerBrowserV2": { - "server_id": "00000000-0000-0000-0000-000000000000", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "ServerBrowserV2", "public_address": "0.0.0.0", "listening_port": 28910 }, "QueryReport": { - "server_id": "00000000-0000-0000-0000-000000000000", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "QueryReport", "public_address": "0.0.0.0", "listening_port": 27900 }, "NatNegotiation": { - "server_id": "00000000-0000-0000-0000-000000000000", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "NatNegotiation", "public_address": "0.0.0.0", "listening_port": 27901 }, "GameStatus": { - "server_id": "00000000-0000-0000-0000-000000000000", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "GameStatus", "public_address": "0.0.0.0", "listening_port": 29920 }, "Chat": { - "server_id": "00000000-0000-0000-0000-000000000000", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "Chat", "public_address": "0.0.0.0", "listening_port": 6667 }, "WebServices": { - "server_id": "00000000-0000-0000-0000-000000000000", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "WebServices", "public_address": "0.0.0.0", "listening_port": 80 }, "GameTrafficRelay": { - "server_id": "00000000-0000-0000-0000-000000000000", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "GameTrafficRelay", "public_address": "0.0.0.0", "listening_port": 10086 diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 3c09e0b0a..6163473e7 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -233,8 +233,8 @@ class ChatChannelCaches(Base): password = Column(String, nullable=True) group_id = Column(Integer, nullable=False) max_num_user = Column(Integer, nullable=False) - key_values = Column(JSONB) - invited_nicks = Column(JSONB) + key_values = Column(JSONB, default={}) + invited_nicks = Column(JSONB, default=[]) update_time = Column(DateTime, nullable=False) @@ -268,7 +268,7 @@ class ChatUserCaches(Base): class GameServerCaches(Base): __tablename__ = "game_server_caches" - instant_key = Column(Integer, primary_key=True, nullable=False) + instant_key = Column(String, primary_key=True, nullable=False) server_id = Column(UUID, nullable=False) host_ip_address = Column(INET, nullable=False) game_name = Column(String, nullable=False) @@ -295,7 +295,8 @@ def connect_to_db() -> Session: if __name__ == "__main__": session = connect_to_db() session.query(Users.userid == 0) # type:ignore - profile = Profiles(userid=1,nick="spyguy",extra_info={},status=GPStatusCode.OFFLINE) + profile = Profiles(userid=1, nick="spyguy", + extra_info={}, status=GPStatusCode.OFFLINE) PG_SESSION.add(profile) PG_SESSION.commit() pass diff --git a/src/backends/protocols/gamespy/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py index a346c4dc0..d18439adb 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/data.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/data.py @@ -16,7 +16,7 @@ from servers.presence_connection_manager.src.aggregates.user_status import UserStatus from servers.presence_connection_manager.src.contracts.results import GetProfileData, LoginData from servers.presence_search_player.src.aggregates.exceptions import GPAddBuddyException, GPDatabaseException, GPStatusException, GPException - +from backends.protocols.gamespy.presence_search_player.data import is_email_exist # region General @@ -31,15 +31,6 @@ def update_online_time(ip: str, port: int): PG_SESSION.commit() -def is_email_exist(email: str) -> bool: - if TYPE_CHECKING: - assert isinstance(Users.email, Column) - if PG_SESSION.query(Users).where(Users.email == email).count() == 1: - return True - else: - return False - - def delete_friend_by_profile_id(profile_id: int): friend = PG_SESSION.query(Friends).where( Friends.friendid == profile_id).first() diff --git a/src/backends/protocols/gamespy/presence_search_player/data.py b/src/backends/protocols/gamespy/presence_search_player/data.py index 4ba6ce746..2acc96e7d 100644 --- a/src/backends/protocols/gamespy/presence_search_player/data.py +++ b/src/backends/protocols/gamespy/presence_search_player/data.py @@ -125,35 +125,22 @@ def get_nick_and_unique_nick_list(email: str, password: str, namespace_id: int) .all() ) assert isinstance(result, list) - if TYPE_CHECKING: - result = cast(list[tuple[str, str]], result) - return result + data = [] + for r in result: + # convert to tuple + data.append(tuple(r)) + return data def get_friend_info_list(profile_id: int, namespace_id: int, game_name: str) -> list: """ return [(profileid, nick, uniquenick, lastname, firstname, userid, email)] """ - if TYPE_CHECKING: - assert isinstance(Profiles.profileid, Column) - assert isinstance(Profiles.nick, Column) - assert isinstance(SubProfiles.uniquenick, Column) - assert isinstance(Profiles.lastname, Column) - assert isinstance(Profiles.firstname, Column) - assert isinstance(Users.userid, Column) - assert isinstance(Users.email, Column) - assert isinstance(Profiles.userid, Column) - assert isinstance(Profiles.profileid, Column) - assert isinstance(SubProfiles.namespaceid, Column) - assert isinstance(SubProfiles.gamename, Column) - assert isinstance(Friends.profileid, Column) result = ( PG_SESSION.query( Profiles.profileid, Profiles.nick, SubProfiles.uniquenick, - Profiles.lastname, - Profiles.firstname, Users.userid, Users.email, ) @@ -189,35 +176,23 @@ def get_matched_profile_info_list( ) .all() ) - if result is None: - result = [] - if TYPE_CHECKING: - result = cast(list[tuple[int, str]], result) - return result + data = [] + for r in result: + data.append(tuple(r)) + return data def get_matched_info_by_nick( nick_name: str, ) -> list[SearchResultData]: - if TYPE_CHECKING: - assert isinstance(Users.email, Column) - assert isinstance(Profiles.profileid, Column) - assert isinstance(Profiles.nick, Column) - assert isinstance(Profiles.userid, Column) - assert isinstance(Profiles.firstname, Column) - assert isinstance(Profiles.lastname, Column) - assert isinstance(SubProfiles.uniquenick, Column) - assert isinstance(SubProfiles.namespaceid, Column) - assert isinstance(Users.userid, Column) result = ( PG_SESSION.query( Users.email, Profiles.profileid, Profiles.nick, - Profiles.firstname, - Profiles.lastname, SubProfiles.uniquenick, SubProfiles.namespaceid, + Profiles.extra_info ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) @@ -225,9 +200,18 @@ def get_matched_info_by_nick( .all() ) temp: list[SearchResultData] = [] - for email, profile_id, nick, first_name, last_name, uniquenick, namespace_id in result: - temp.append(SearchResultData(profile_id=profile_id, nick=nick, uniquenick=uniquenick, - email=email, namespace_id=namespace_id, firstname=first_name, lastname=last_name)) + for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: + if TYPE_CHECKING: + extra_info = cast(dict, extra_info) + firstname = extra_info.get("firstname", "") + lastname = extra_info.get("lastname", "") + t = SearchResultData(profile_id=profile_id, + nick=nick, + uniquenick=uniquenick, + email=email, + namespace_id=namespace_id, firstname=firstname, + lastname=lastname) + temp.append(t) return temp @@ -235,23 +219,14 @@ def get_matched_info_by_nick( def get_matched_info_by_email( email: str, ) -> list[SearchResultData]: - if TYPE_CHECKING: - assert isinstance(Profiles.profileid, Column) - assert isinstance(Profiles.nick, Column) - assert isinstance(Profiles.firstname, Column) - assert isinstance(Profiles.lastname, Column) - assert isinstance(SubProfiles.uniquenick, Column) - assert isinstance(SubProfiles.namespaceid, Column) - assert isinstance(Users.email, Column) - assert isinstance(Profiles.userid, Column) result = ( PG_SESSION.query( + Users.email, Profiles.profileid, Profiles.nick, - Profiles.firstname, - Profiles.lastname, SubProfiles.uniquenick, SubProfiles.namespaceid, + Profiles.extra_info ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) @@ -259,31 +234,31 @@ def get_matched_info_by_email( .all() ) temp: list[SearchResultData] = [] - for email, profile_id, nick, first_name, last_name, uniquenick, namespace_id in result: - temp.append(SearchResultData(profile_id=profile_id, nick=nick, uniquenick=uniquenick, - email=email, namespace_id=namespace_id, firstname=first_name, lastname=last_name)) + for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: + if TYPE_CHECKING: + extra_info = cast(dict, extra_info) + firstname = extra_info.get("firstname", "") + lastname = extra_info.get("lastname", "") + t = SearchResultData(profile_id=profile_id, + nick=nick, + uniquenick=uniquenick, + email=email, + namespace_id=namespace_id, firstname=firstname, + lastname=lastname) + temp.append(t) return temp def get_matched_info_by_nick_and_email(nick_name: str, email: str): - if TYPE_CHECKING: - assert isinstance(Profiles.profileid, Column) - assert isinstance(Profiles.nick, Column) - assert isinstance(Profiles.firstname, Column) - assert isinstance(Profiles.lastname, Column) - assert isinstance(SubProfiles.uniquenick, Column) - assert isinstance(SubProfiles.namespaceid, Column) - assert isinstance(Profiles.userid, Column) - assert isinstance(Users.email, Column) + result = ( PG_SESSION.query( Users.email, Profiles.profileid, Profiles.nick, - Profiles.firstname, - Profiles.lastname, SubProfiles.uniquenick, SubProfiles.namespaceid, + Profiles.extra_info ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) @@ -291,32 +266,31 @@ def get_matched_info_by_nick_and_email(nick_name: str, email: str): .all() ) temp: list[SearchResultData] = [] - for email, profile_id, nick, first_name, last_name, uniquenick, namespace_id in result: - temp.append(SearchResultData(profile_id=profile_id, nick=nick, uniquenick=uniquenick, - email=email, namespace_id=namespace_id, firstname=first_name, lastname=last_name)) + for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: + if TYPE_CHECKING: + extra_info = cast(dict, extra_info) + firstname = extra_info.get("firstname", "") + lastname = extra_info.get("lastname", "") + t = SearchResultData(profile_id=profile_id, + nick=nick, + uniquenick=uniquenick, + email=email, + namespace_id=namespace_id, firstname=firstname, + lastname=lastname) + temp.append(t) return temp def get_matched_info_by_uniquenick_and_namespaceid( unique_nick: str, namespace_id: int ) -> list[SearchResultData]: - if TYPE_CHECKING: - assert isinstance(Profiles.profileid, Column) - assert isinstance(Profiles.nick, Column) - assert isinstance(Profiles.firstname, Column) - assert isinstance(Profiles.lastname, Column) - assert isinstance(SubProfiles.uniquenick, Column) - assert isinstance(SubProfiles.namespaceid, Column) - assert isinstance(Profiles.userid, Column) - assert isinstance(Users.email, Column) result = ( PG_SESSION.query( Profiles.profileid, Profiles.nick, - Profiles.firstname, - Profiles.lastname, SubProfiles.uniquenick, SubProfiles.namespaceid, + Profiles.extra_info ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) @@ -327,9 +301,18 @@ def get_matched_info_by_uniquenick_and_namespaceid( .all() ) temp: list[SearchResultData] = [] - for email, profile_id, nick, first_name, last_name, uniquenick, namespace_id in result: - temp.append(SearchResultData(profile_id=profile_id, nick=nick, uniquenick=uniquenick, - email=email, namespace_id=namespace_id, firstname=first_name, lastname=last_name)) + for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: + if TYPE_CHECKING: + extra_info = cast(dict, extra_info) + firstname = extra_info.get("firstname", "") + lastname = extra_info.get("lastname", "") + t = SearchResultData(profile_id=profile_id, + nick=nick, + uniquenick=uniquenick, + email=email, + namespace_id=namespace_id, firstname=firstname, + lastname=lastname) + temp.append(t) return temp @@ -337,22 +320,10 @@ def get_matched_info_by_uniquenick_and_namespaceid( def get_matched_info_by_uniquenick_and_namespaceids( unique_nick: str, namespace_ids: list[int] ) -> list[SearchResultData]: - if TYPE_CHECKING: - assert isinstance(Profiles.profileid, Column) - assert isinstance(Profiles.nick, Column) - assert isinstance(Profiles.firstname, Column) - assert isinstance(Profiles.lastname, Column) - assert isinstance(SubProfiles.uniquenick, Column) - assert isinstance(SubProfiles.namespaceid, Column) - assert isinstance(Profiles.userid, Column) - assert isinstance(Users.email, Column) - result = ( PG_SESSION.query( Profiles.profileid, Profiles.nick, - Profiles.firstname, - Profiles.lastname, SubProfiles.uniquenick, SubProfiles.namespaceid, ) @@ -365,19 +336,23 @@ def get_matched_info_by_uniquenick_and_namespaceids( .all() ) data: list[SearchResultData] = [] - for email, profile_id, nick, first_name, last_name, uniquenick, namespace_id in result: - data.append(SearchResultData(profile_id=profile_id, nick=nick, uniquenick=uniquenick, - email=email, namespace_id=namespace_id, firstname=first_name, lastname=last_name)) + for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: + if TYPE_CHECKING: + extra_info = cast(dict, extra_info) + firstname = extra_info.get("firstname", "") + lastname = extra_info.get("lastname", "") + t = SearchResultData(profile_id=profile_id, + nick=nick, + uniquenick=uniquenick, + email=email, + namespace_id=namespace_id, firstname=firstname, + lastname=lastname) + data.append(t) return data def is_uniquenick_exist(unique_nick: str, namespace_id: int, game_name: str) -> bool: - if TYPE_CHECKING: - assert isinstance(Profiles.profileid, Column) - assert isinstance(SubProfiles.uniquenick, Column) - assert isinstance(SubProfiles.gamename, Column) - assert isinstance(SubProfiles.namespaceid, Column) result = ( PG_SESSION.query(Profiles) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index 035c72782..c7a01c2e1 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -39,6 +39,9 @@ def get_all_groups() -> dict: def get_peer_staging_channels(game_name: str, group_id: int) -> list[GameServerInfo]: + """ + todo check where use this function + """ assert isinstance(game_name, str) assert isinstance(group_id, int) staging_name = f"{PeerRoom.StagingRoomPrefix}!{game_name}!*" @@ -46,11 +49,12 @@ def get_peer_staging_channels(game_name: str, group_id: int) -> list[GameServerI ChatChannelCaches.channel_name == staging_name).all() data = [] for s in result: - data.append(GameServerInfo(**s)) + t = {k: v for k, v in s.__dict__.items() if k != '_sa_instance_state'} + data.append(GameServerInfo(**t)) return data -def get_peer_group_channel(group_id: int) -> list[GameServerInfo]: +def get_peer_group_channel(group_id: int) -> list[PeerRoomInfo]: assert isinstance(group_id, int) group_name = f"{PeerRoom.GroupRoomPrefix}!{group_id}" @@ -62,8 +66,8 @@ def get_peer_group_channel(group_id: int) -> list[GameServerInfo]: return data -def get_server_info_with_instant_key(instant_key: int) -> Optional[GameServerInfo]: - assert isinstance(instant_key, int) +def get_server_info_with_instant_key(instant_key: str) -> Optional[GameServerInfo]: + assert isinstance(instant_key, str) result = PG_SESSION.query(GameServerCaches).where( GameServerCaches.instant_key == instant_key).first() return result @@ -103,10 +107,14 @@ def remove_server_info(info: GameServerCaches) -> None: # todo finish the GameServerCaches creation -def update_game_server(info: GameServerCaches) -> None: - from datetime import datetime - info.update_time = datetime.now() # type:ignore +def create_game_server(info: GameServerCaches) -> None: PG_SESSION.add(info) + update_game_server() + + +def update_game_server() -> None: + from datetime import datetime + # info.update_time = datetime.now() # type:ignore PG_SESSION.commit() diff --git a/src/backends/protocols/gamespy/query_report/handlers.py b/src/backends/protocols/gamespy/query_report/handlers.py index f7653e2b8..ec99ef100 100644 --- a/src/backends/protocols/gamespy/query_report/handlers.py +++ b/src/backends/protocols/gamespy/query_report/handlers.py @@ -3,6 +3,7 @@ from backends.library.database.pg_orm import PG_SESSION, GameServerCaches from backends.protocols.gamespy.query_report.requests import * from servers.query_report.src.aggregates.exceptions import QRException +import backends.protocols.gamespy.query_report.data as data class AvaliableHandler(HandlerBase): @@ -26,8 +27,8 @@ class Heartbeathandler(HandlerBase): _request: HeartBeatRequest async def _data_operate(self) -> None: - cache = PG_SESSION.query(GameServerCaches).where( - GameServerCaches.instant_key == self._request.instant_key).first() + cache = data.get_server_info_with_instant_key( + self._request.instant_key) if cache is None: cache = GameServerCaches(instant_key=self._request.instant_key, server_id=self._request.server_id, @@ -40,6 +41,7 @@ async def _data_operate(self) -> None: server_data=self._request.server_data, team_data=self._request.team_data, avaliable=True) + data.create_game_server(cache) else: cache.instant_key = self._request.instant_key # type: ignore cache.server_id = self._request.server_id # type: ignore @@ -52,8 +54,7 @@ async def _data_operate(self) -> None: cache.server_data = self._request.server_data # type: ignore cache.team_data = self._request.team_data # type: ignore cache.avaliable = True # type: ignore - - PG_SESSION.commit() + data.update_game_server() class KeepAliveHandler(HandlerBase): diff --git a/src/backends/protocols/gamespy/query_report/requests.py b/src/backends/protocols/gamespy/query_report/requests.py index 0a3dddb4f..68163578c 100644 --- a/src/backends/protocols/gamespy/query_report/requests.py +++ b/src/backends/protocols/gamespy/query_report/requests.py @@ -1,14 +1,14 @@ -from pydantic import UUID4 +from pydantic import UUID4, Field, constr import backends.library.abstractions.contracts as lib from servers.query_report.src.v2.aggregates.enums import GameServerStatus, RequestType class RequestBase(lib.RequestBase): - instant_key: int + instant_key: str = Field(..., max_length=10) command_name: RequestType - raw_request: bytes + raw_request: str class AvaliableRequest(RequestBase): @@ -33,11 +33,11 @@ class ClientMessageRequest(RequestBase): class HeartBeatRequest(RequestBase): - server_data: dict[str, str] - player_data: list[dict[str, str]] - team_data: list[dict[str, str]] + server_data: dict[str, object] + player_data: list[dict[str, object]] + team_data: list[dict[str, object]] server_status: GameServerStatus - group_id: int + group_id: int | None game_name: str diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index b085b6efe..ada05a400 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -1,7 +1,7 @@ from fastapi import APIRouter from backends.protocols.gamespy.presence_connection_manager.requests import KeepAliveRequest -from backends.protocols.gamespy.query_report.handlers import AvaliableHandler +from backends.protocols.gamespy.query_report.handlers import AvaliableHandler, Heartbeathandler from backends.protocols.gamespy.query_report.requests import AvaliableRequest, ChallengeRequest, ClientMessageRequest, EchoRequest, HeartBeatRequest from backends.urls import QUERY_REPORT @@ -10,7 +10,9 @@ @router.post(f"{QUERY_REPORT}/HeartBeatHandler") async def heartbeat(request: HeartBeatRequest): - raise NotImplementedError() + handler = Heartbeathandler(request) + await handler.handle() + return handler.response @router.post(f"{QUERY_REPORT}/ChallengeHanler") diff --git a/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py b/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py index f1216ec97..e6f0ffce1 100644 --- a/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py +++ b/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py @@ -41,3 +41,40 @@ def test_get_profile(self): result2 = data.get_profile(1, "spyguy_not_profile") self.assertIsNone(result2) + + def test_get_sub_profile(self): + pass + + def test_get_nick_and_unique_nick_list(self): + result = data.get_nick_and_unique_nick_list( + "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b", 0) + self.assertIsInstance(result, list) + self.assertEqual(len(result), 1) + self.assertIsInstance(result[0], tuple) + + def test_get_matched_profile_info_list(self): + result = data.get_matched_profile_info_list([1], 0) + self.assertIsInstance(result, list) + self.assertNotEqual(len(result), 0) + + def test_get_matched_info_by_nick(self): + result = data.get_matched_info_by_nick("spyguy") + self.assertIsNotNone(result) + self.assertNotEqual(len(result), 0) + + def test_get_matched_info_by_email(self): + result = data.get_matched_info_by_email("spyguy@gamespy.com") + + def test_is_uniquenick_exist(self): + result1 = data.is_uniquenick_exist("spyguy_test", 0, "gmtests") + self.assertTrue(result1) + + result2 = data.is_uniquenick_exist( + "spyguy_not_uniquenick", 0, "gmtests") + self.assertFalse(result2) + + def test_is_email_exist(self): + result1 = data.is_email_exist("spyguy@gamespy.com") + self.assertTrue(result1) + result2 = data.is_email_exist("spyguy@gamespy.net") + self.assertFalse(result2) diff --git a/src/backends/tests/gamespy/query_report/__init__.py b/src/backends/tests/gamespy/query_report/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/gamespy/query_report/api_tests.py b/src/backends/tests/gamespy/query_report/api_tests.py new file mode 100644 index 000000000..79af9f2cf --- /dev/null +++ b/src/backends/tests/gamespy/query_report/api_tests.py @@ -0,0 +1,14 @@ +import unittest + +from backends.protocols.gamespy.query_report.handlers import Heartbeathandler +from backends.protocols.gamespy.query_report.requests import HeartBeatRequest + + +class ApiTests(unittest.IsolatedAsyncioTestCase): + async def test_heartbeat(self): + request = {"server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "raw_request": "\\u0003\\\\xe5\\\\xcfaZlocalip0\\u0000172.19.0.5\\u0000localport\\u000011111\\u0000natneg\\u00001\\u0000statechanged\\u00003\\u0000gamename\\u0000gmtest\\u0000hostname\\u0000GameSpy QR2 Sample\\u0000gamever\\u00002.00\\u0000hostport\\u000025000\\u0000mapname\\u0000gmtmap1\\u0000gametype\\u0000arena\\u0000numplayers\\u00005\\u0000numteams\\u00002\\u0000maxplayers\\u000032\\u0000gamemode\\u0000openplaying\\u0000teamplay\\u00001\\u0000fraglimit\\u00000\\u0000timelimit\\u000040\\u0000gravity\\u0000800\\u0000rankingon\\u00001\\u0000\\u0000\\u0000\\u0005player_\\u0000score_\\u0000deaths_\\u0000ping_\\u0000team_\\u0000time_\\u0000\\u0000Joe Player\\u000030\\u000012\\u0000411\\u00000\\u000010\\u0000L33t 0n3\\u00000\\u00006\\u0000233\\u00001\\u0000325\\u0000Raptor\\u000015\\u000025\\u000063\\u00001\\u0000462\\u0000Gr81\\u00000\\u000016\\u0000294\\u00000\\u0000870\\u0000Flubber\\u000017\\u000012\\u0000232\\u00001\\u0000384\\u0000\\u0000\\u0002team_t\\u0000score_t\\u0000avgping_t\\u0000\\u0000Red\\u0000294\\u0000357\\u0000Blue\\u0000498\\u0000454\\u0000", "client_ip": "172.19.0.5", "client_port": 11111, + "instant_key": "3855573338", "command_name": 3, "server_data": {"localip0": "172.19.0.5", "localport": "11111", "natneg": "1", "statechanged": "3", "gamename": "gmtest", "hostname": "GameSpy QR2 Sample", "gamever": "2.00", "hostport": "25000", "mapname": "gmtmap1", "gametype": "arena", "numplayers": "5", "numteams": "2", "maxplayers": "32", "gamemode": "openplaying", "teamplay": "1", "fraglimit": "0", "timelimit": "40", "gravity": "800", "rankingon": "1"}, "player_data": [{"player_0": "Joe Player", "score_0": "30", "deaths_0": "12", "ping_0": "411", "team_0": "0", "time_0": "10"}, {"player_1": "L33t 0n3", "score_1": "0", "deaths_1": "6", "ping_1": "233", "team_1": "1", "time_1": "325"}, {"player_2": "Raptor", "score_2": "15", "deaths_2": "25", "ping_2": "63", "team_2": "1", "time_2": "462"}, {"player_3": "Gr81", "score_3": "0", "deaths_3": "16", "ping_3": "294", "team_3": "0", "time_3": "870"}, {"player_4": "Flubber", "score_4": "17", "deaths_4": "12", "ping_4": "232", "team_4": "1", "time_4": "384"}], "team_data": [{"team_t0": "Red", "score_t0": "294", "avgping_t0": "357"}, {"team_t1": "Blue", "score_t1": "498", "avgping_t1": "454"}], "server_status": 3, "group_id": None, "game_name": "gmtest"} + req = HeartBeatRequest(**request) + handler = Heartbeathandler(req) + await handler.handle() + handler.response diff --git a/src/backends/tests/gamespy/query_report/data_fetch_tests.py b/src/backends/tests/gamespy/query_report/data_fetch_tests.py new file mode 100644 index 000000000..02d7e0d46 --- /dev/null +++ b/src/backends/tests/gamespy/query_report/data_fetch_tests.py @@ -0,0 +1,20 @@ +from datetime import datetime, timezone +import unittest + +from pydantic import ValidationError +from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches +import backends.protocols.gamespy.query_report.data as data +from servers.query_report.src.aggregates.game_server_info import GameServerInfo + + +class DataFetchTests(unittest.TestCase): + def test_get_all_groups(self): + self.assertIsNotNone(data.PEER_GROUP_LIST) + self.assertIsInstance(data.PEER_GROUP_LIST, dict) + + def test_get_peer_staging_channels(self): + cache = ChatChannelCaches(channel_name="#GSP!unispy_test_game_name!*", server_id="b6480a17-5e3d-4da0-aeec-c421620bff71", game_name="unispy_test_game_name", + room_name="unispy_test_room_name", group_id=0, max_num_user=100, key_values={}, invited_nicks={}, update_time=datetime.now(timezone.utc)) + # PG_SESSION.add(cache) + # PG_SESSION.commit() + self.assertRaises(ValidationError,data.get_peer_staging_channels,"unispy_test_game_name", 0) diff --git a/src/library/src/abstractions/connections.py b/src/library/src/abstractions/connections.py index b3ff2c657..0b913fe04 100644 --- a/src/library/src/abstractions/connections.py +++ b/src/library/src/abstractions/connections.py @@ -34,10 +34,10 @@ def __init__( assert issubclass(t_client, ClientBase) # assert isinstance(logger, LogWriter) # assert issubclass(type(handler), socketserver.BaseRequestHandler) + self.handler = handler self.remote_ip = handler.client_address[0] self.remote_port = int(handler.client_address[1]) self.ip_endpoint = f"{self.remote_ip}:{self.remote_port}" - self.config = config self.t_client = t_client self.logger = logger diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index 36fc82ca4..40a4f8940 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -93,13 +93,15 @@ def _upload_data(self): if child class do not require feach, overide this function to do nothing """ url = f"{CONFIG.backend.url}/GameSpy/{ - self._client.server_config.server_name}/{self.__class__.__name__}/" + self._client.server_config.server_name}/{self.__class__.__name__}" json_str = json.dumps( self._temp_data, cls=UniSpyJsonEncoder, ensure_ascii=False) + self._client.log_info(f"Send json to {url}") response = requests.post(url, data=json_str, headers={ "Content-Type": "application/json"}) + if response.status_code != 200: - raise UniSpyException("Upload data to background failed.") + raise UniSpyException("failed to upload data to background.") self._http_result = response.json() def _feach_data(self): diff --git a/src/library/src/abstractions/server_launcher.py b/src/library/src/abstractions/server_launcher.py index 41ac066f4..d89acdddd 100644 --- a/src/library/src/abstractions/server_launcher.py +++ b/src/library/src/abstractions/server_launcher.py @@ -61,9 +61,8 @@ def _launch_server(self) -> None: if self.server is None: raise UniSpyException("Create network server in child class") import threading - th = threading.Thread(target=self.server.start) - key = input('Press Q to Quit') - th.join() + print("Press Ctrl+C to Quit") + self.server.start() def _connect_to_backend(self): diff --git a/src/servers/query_report/src/applications/server_launcher.py b/src/servers/query_report/src/applications/server_launcher.py index da8e8aedf..ec23c1b06 100644 --- a/src/servers/query_report/src/applications/server_launcher.py +++ b/src/servers/query_report/src/applications/server_launcher.py @@ -1,7 +1,7 @@ from library.src.abstractions.server_launcher import ServerLauncherBase from library.src.network.udp_handler import UdpServer from library.src.configs import CONFIG -from servers.presence_search_player.src.applications.client import Client +from servers.query_report.src.applications.client import Client class ServerLauncher(ServerLauncherBase): @@ -18,7 +18,6 @@ def _launch_server(self): super()._launch_server() - if __name__ == "__main__": s = ServerLauncher() s.start() diff --git a/src/servers/query_report/src/v2/aggregates/enums.py b/src/servers/query_report/src/v2/aggregates/enums.py index fe02ea866..6721506d3 100644 --- a/src/servers/query_report/src/v2/aggregates/enums.py +++ b/src/servers/query_report/src/v2/aggregates/enums.py @@ -1,7 +1,7 @@ -from enum import IntEnum +from enum import Enum -class RequestType(IntEnum): +class RequestType(Enum): CHALLENGE = 0x01 HEARTBEAT = 0x03 CLIENT_MESSAGE = 0x06 @@ -12,7 +12,7 @@ class RequestType(IntEnum): AVALIABLE_CHECK = 0x09 -class ResponseType(IntEnum): +class ResponseType(Enum): QUERY = 0x00 ECHO = 0x02 ADD_ERROR = 0x04 @@ -21,7 +21,7 @@ class ResponseType(IntEnum): CLIENT_REGISTERED = 0x0A -class PacketType(IntEnum): +class PacketType(Enum): QUERY = 0x00 CHALLENGE = 0x01 ECHO = 0x02 @@ -36,27 +36,27 @@ class PacketType(IntEnum): AVALIABLE_CHECK = 0x09 -class QRStateChange(IntEnum): +class QRStateChange(Enum): NORMAL_HEARTBEAT = 0 GAME_MODE_CHANGE = 1 SERVER_SHUTDOWN = 2 CANNOT_RECIEVE_CHALLENGE = 3 -class HeartBeatReportType(IntEnum): +class HeartBeatReportType(Enum): SERVER_TEAM_PLAYER_DATA = 1 SERVER_PLAYER_DATA = 2 SERVER_DATA = 3 -class GameServerStatus(IntEnum): +class GameServerStatus(Enum): NORMAL = 0 UPDATE = 1 SHUTDOWN = 2 PLAYING = 3 -class ServerAvailability(IntEnum): +class ServerAvailability(Enum): AVAILABLE = 0 WAITING = 1 PERMANENT_UNAVAILABLE = 2 diff --git a/src/servers/query_report/src/v2/contracts/requests.py b/src/servers/query_report/src/v2/contracts/requests.py index cce4b4380..3d9c9fddd 100644 --- a/src/servers/query_report/src/v2/contracts/requests.py +++ b/src/servers/query_report/src/v2/contracts/requests.py @@ -55,7 +55,7 @@ class HeartBeatRequest(RequestBase): player_data: list[dict[str, str]] team_data: list[dict[str, str]] server_status: GameServerStatus - group_id: int + group_id: int | None remote_ip_address: str remote_port: int game_name: str @@ -118,6 +118,8 @@ def parse(self): if not int(self.server_data["groupid"], group_id): raise QRException("GroupId is invalid.") self.group_id = group_id + else: + self.group_id = None def parse_server_data(self, server_data_str: str): self.server_data = {} From 2d39cbd64bec0517653b296a96a43225e4a243a0 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 10 Jan 2025 03:41:02 +0000 Subject: [PATCH 152/231] fix: backend search request parsing error --- .../presence_search_player/requests.py | 20 +++++++++---------- .../gamespy/query_report/handlers.py | 2 +- .../gamespy/query_report/requests.py | 2 +- .../routers/gamespy/presence_search_player.py | 2 +- src/backends/routers/home.py | 11 +++++++++- .../gamespy/query_report/data_fetch_tests.py | 2 ++ .../{api_tests.py => handler_test.py} | 0 .../gamespy/query_report/integration_tests.py | 2 ++ src/library/src/abstractions/client.py | 3 +++ src/library/src/abstractions/handler.py | 6 +++--- .../src/contracts/requests.py | 6 +++--- .../src/abstractions/contracts.py | 2 ++ .../src/contracts/requests.py | 16 +++++++++++---- .../src/aggregates/game_server_info.py | 2 +- .../src/v2/abstractions/contracts.py | 6 +++--- .../src/v2/contracts/responses.py | 2 +- 16 files changed, 55 insertions(+), 29 deletions(-) rename src/backends/tests/gamespy/query_report/{api_tests.py => handler_test.py} (100%) create mode 100644 src/backends/tests/gamespy/query_report/integration_tests.py diff --git a/src/backends/protocols/gamespy/presence_search_player/requests.py b/src/backends/protocols/gamespy/presence_search_player/requests.py index a915143b8..6dbdc71d6 100644 --- a/src/backends/protocols/gamespy/presence_search_player/requests.py +++ b/src/backends/protocols/gamespy/presence_search_player/requests.py @@ -43,8 +43,8 @@ class NicksRequest(RequestBase): class OthersListRequest(RequestBase): - profile_ids: list[int] = [] - namespace_id: int = 0 + profile_ids: list[int] + namespace_id: int class OthersRequest(RequestBase): @@ -57,15 +57,15 @@ class SearchRequest(RequestBase): skip_num: int request_type: SearchType game_name: str - profile_id: int + profile_id: int| None = None partner_id: int - email: str - nick: str - uniquenick: str - session_key: str - firstname: str - lastname: str - icquin: str + email: str | None = None + nick: str | None = None + uniquenick: str | None = None + session_key: str | None = None + firstname: str | None = None + lastname: str | None = None + icquin: str | None = None namespace_id: int diff --git a/src/backends/protocols/gamespy/query_report/handlers.py b/src/backends/protocols/gamespy/query_report/handlers.py index ec99ef100..8b0db59d9 100644 --- a/src/backends/protocols/gamespy/query_report/handlers.py +++ b/src/backends/protocols/gamespy/query_report/handlers.py @@ -28,7 +28,7 @@ class Heartbeathandler(HandlerBase): async def _data_operate(self) -> None: cache = data.get_server_info_with_instant_key( - self._request.instant_key) + str(self._request.instant_key)) if cache is None: cache = GameServerCaches(instant_key=self._request.instant_key, server_id=self._request.server_id, diff --git a/src/backends/protocols/gamespy/query_report/requests.py b/src/backends/protocols/gamespy/query_report/requests.py index 68163578c..e938e5db9 100644 --- a/src/backends/protocols/gamespy/query_report/requests.py +++ b/src/backends/protocols/gamespy/query_report/requests.py @@ -6,7 +6,7 @@ class RequestBase(lib.RequestBase): - instant_key: str = Field(..., max_length=10) + instant_key: str command_name: RequestType raw_request: str diff --git a/src/backends/routers/gamespy/presence_search_player.py b/src/backends/routers/gamespy/presence_search_player.py index 18ea536c3..29ccb246f 100644 --- a/src/backends/routers/gamespy/presence_search_player.py +++ b/src/backends/routers/gamespy/presence_search_player.py @@ -32,7 +32,7 @@ async def others_list(request: OthersListRequest): @router.post(f"{PRESENCE_SEARCH_PLAYER}/PMatchHandler") -async def player_match(request: object): +async def player_match(request: dict): raise NotImplementedError() diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index 501d8649a..b32b5ebb6 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -1,6 +1,7 @@ from ipaddress import IPv4Address from uuid import UUID from fastapi import FastAPI +from fastapi.exceptions import RequestValidationError from pydantic import BaseModel import uvicorn @@ -18,7 +19,15 @@ app.include_router(server_browser.router) app.include_router(webservices.router) -LogManager.create("backend") +logger = LogManager.create("backend") + +@app.exception_handler(RequestValidationError) +async def validation_exception_handler(request, exc): + logger.error(str(exc)) + +@app.exception_handler(Exception) +async def handle_unispy_exception(request, exc): + logger.error(str(exc)) @app.post("/") diff --git a/src/backends/tests/gamespy/query_report/data_fetch_tests.py b/src/backends/tests/gamespy/query_report/data_fetch_tests.py index 02d7e0d46..6945de7e3 100644 --- a/src/backends/tests/gamespy/query_report/data_fetch_tests.py +++ b/src/backends/tests/gamespy/query_report/data_fetch_tests.py @@ -18,3 +18,5 @@ def test_get_peer_staging_channels(self): # PG_SESSION.add(cache) # PG_SESSION.commit() self.assertRaises(ValidationError,data.get_peer_staging_channels,"unispy_test_game_name", 0) + + \ No newline at end of file diff --git a/src/backends/tests/gamespy/query_report/api_tests.py b/src/backends/tests/gamespy/query_report/handler_test.py similarity index 100% rename from src/backends/tests/gamespy/query_report/api_tests.py rename to src/backends/tests/gamespy/query_report/handler_test.py diff --git a/src/backends/tests/gamespy/query_report/integration_tests.py b/src/backends/tests/gamespy/query_report/integration_tests.py new file mode 100644 index 000000000..2c0edaca7 --- /dev/null +++ b/src/backends/tests/gamespy/query_report/integration_tests.py @@ -0,0 +1,2 @@ + +class IntegrationTest: \ No newline at end of file diff --git a/src/library/src/abstractions/client.py b/src/library/src/abstractions/client.py index b1ac1b689..dacfaf65a 100644 --- a/src/library/src/abstractions/client.py +++ b/src/library/src/abstractions/client.py @@ -119,6 +119,9 @@ def log_network_broadcast(self, data: object) -> None: def log_network_receving(self, data: object) -> None: self.logger.info(f"{self._log_prefix} [recv]: {data}") + def log_network_upload(self, data: object) -> None: + self.logger.info(f"{self._log_prefix} [upload]: {data}") + def log_current_class(self, object: "CmdHandlerBase") -> None: self.logger.debug(f"{self._log_prefix} [=>] <{ object.__class__.__name__}>") diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index 40a4f8940..288b61da5 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -86,7 +86,7 @@ def _prepare_data(self): if "client_port" in self._temp_data: raise UniSpyException("client_port name collision in dict") self._temp_data["client_port"] = self._client.connection.remote_port - + def _upload_data(self): """ whether need send data to backend @@ -96,10 +96,10 @@ def _upload_data(self): self._client.server_config.server_name}/{self.__class__.__name__}" json_str = json.dumps( self._temp_data, cls=UniSpyJsonEncoder, ensure_ascii=False) - self._client.log_info(f"Send json to {url}") + self._client.log_network_upload(f"[{url}] {json_str}") response = requests.post(url, data=json_str, headers={ "Content-Type": "application/json"}) - + if response.status_code != 200: raise UniSpyException("failed to upload data to background.") self._http_result = response.json() diff --git a/src/servers/presence_connection_manager/src/contracts/requests.py b/src/servers/presence_connection_manager/src/contracts/requests.py index 0d72a1910..9d3039e4c 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests.py +++ b/src/servers/presence_connection_manager/src/contracts/requests.py @@ -106,13 +106,13 @@ class LoginRequest(RequestBase): user_id: int profile_id: int partner_id: int + """ + partner id default is 0 + """ game_name: str quiet_mode_flags: int firewall: bool - def __init__(self, raw_request): - super().__init__(raw_request) - def parse(self): super().parse() diff --git a/src/servers/presence_search_player/src/abstractions/contracts.py b/src/servers/presence_search_player/src/abstractions/contracts.py index 21507f813..dfa1f1021 100644 --- a/src/servers/presence_search_player/src/abstractions/contracts.py +++ b/src/servers/presence_search_player/src/abstractions/contracts.py @@ -16,6 +16,8 @@ class RequestBase(library.src.abstractions.contracts.RequestBase): def __init__(self, raw_request: str) -> None: assert isinstance(raw_request, str) super().__init__(raw_request) + self.operation_id = 0 + self.namespace_id = 0 def parse(self) -> None: self.request_dict = convert_to_key_value(self.raw_request) diff --git a/src/servers/presence_search_player/src/contracts/requests.py b/src/servers/presence_search_player/src/contracts/requests.py index a809fc873..0999a9ff5 100644 --- a/src/servers/presence_search_player/src/contracts/requests.py +++ b/src/servers/presence_search_player/src/contracts/requests.py @@ -27,10 +27,13 @@ def parse(self): self.email = self.request_dict["email"] if "partner_id" in self.request_dict.keys(): - self.partner_id = int(self.request_dict["partner_id"]) + try: + self.partner_id = int(self.request_dict["partner_id"]) + except: + raise GPParseException( + "no partner id found, check whether need implement the default partnerid") else: - raise GPParseException( - "no partner id found, check whether need implement the default partnerid") + self.partner_id = 0 class NewUserRequest(RequestBase): @@ -79,7 +82,8 @@ def parse_other_info(self): self.has_partner_id_flag = True except ValueError: raise GPParseException("partnerid is incorrect.") - + else: + self.partner_id = 0 if "productid" in self.request_dict: try: self.product_id = int(self.request_dict["productid"]) @@ -178,6 +182,10 @@ class SearchRequest(RequestBase): lastname: str icquin: str + def __init__(self, raw_request: str) -> None: + super().__init__(raw_request) + self.skip_num = 0 + def parse(self) -> None: super().parse() diff --git a/src/servers/query_report/src/aggregates/game_server_info.py b/src/servers/query_report/src/aggregates/game_server_info.py index cab8dc42c..f7bbc578c 100644 --- a/src/servers/query_report/src/aggregates/game_server_info.py +++ b/src/servers/query_report/src/aggregates/game_server_info.py @@ -10,7 +10,7 @@ class GameServerInfo(BaseModel): server_id: UUID host_ip_address: str - instant_key: int + instant_key: str game_name: str query_report_port: int diff --git a/src/servers/query_report/src/v2/abstractions/contracts.py b/src/servers/query_report/src/v2/abstractions/contracts.py index ed039108a..94573b54d 100644 --- a/src/servers/query_report/src/v2/abstractions/contracts.py +++ b/src/servers/query_report/src/v2/abstractions/contracts.py @@ -7,7 +7,7 @@ class RequestBase(library.src.abstractions.contracts.RequestBase): - instant_key: int + instant_key: str command_name: RequestType raw_request: bytes @@ -19,7 +19,7 @@ def parse(self): if len(self.raw_request) < 3: raise QRException("request length not valid") self.command_name = RequestType(self.raw_request[0]) - self.instant_key = int.from_bytes(self.raw_request[1:5]) + self.instant_key = str(int.from_bytes(self.raw_request[1:5])) class ResultBase(library.src.abstractions.contracts.ResultBase): @@ -35,5 +35,5 @@ def build(self) -> None: data = bytearray() data.extend(MAGIC_DATA) data.append(self._request.command_name.value) - data.extend(self._request.instant_key.to_bytes(4)) + data.extend(int(self._request.instant_key).to_bytes(4)) self.sending_buffer = bytes(data) diff --git a/src/servers/query_report/src/v2/contracts/responses.py b/src/servers/query_report/src/v2/contracts/responses.py index a1ed5d608..68d6e8181 100644 --- a/src/servers/query_report/src/v2/contracts/responses.py +++ b/src/servers/query_report/src/v2/contracts/responses.py @@ -25,7 +25,7 @@ def __init__(self, request: AvaliableRequest) -> None: def build(self): data = bytearray() data.extend(RESPONSE_PREFIX) - data.append(ServerAvailability.AVAILABLE) + data.append(ServerAvailability.AVAILABLE.value) self.sending_buffer = bytes(data) From 2c7022b1d98d220a1763548eb9b99eea9e11715a Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 24 Jan 2025 02:53:01 +0000 Subject: [PATCH 153/231] fix: database creation errors --- common/UniSpy_pg.sql | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 546d2e6ad..2f9f65752 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -32,8 +32,6 @@ ALTER SCHEMA unispy OWNER TO unispy; COMMENT ON SCHEMA unispy IS 'standard public schema'; -ALTER TYPE unispy.natporttype OWNER TO unispy; - SET default_tablespace = ''; SET default_table_access_method = heap; @@ -157,7 +155,7 @@ ALTER TABLE unispy.chat_nick_caches OWNER TO unispy; CREATE TABLE unispy.chat_user_caches ( nick_name character varying NOT NULL, - channel_name character varying NOT NULL, + channel_name character varying NOT NULL UNIQUE, server_id uuid NOT NULL, user_name character varying NOT NULL, update_time timestamp without time zone NOT NULL, @@ -301,8 +299,8 @@ CREATE TABLE unispy.init_packet_caches ( cookie integer NOT NULL, server_id uuid NOT NULL, version integer NOT NULL, - port_type unispy.natporttype NOT NULL, - client_index unispy.natclientindex NOT NULL, + port_type smallint NOT NULL, + client_index smallint NOT NULL, game_name character varying NOT NULL, use_game_port boolean NOT NULL, public_ip character varying NOT NULL, @@ -417,7 +415,7 @@ ALTER SEQUENCE unispy.nat_fail_cachess_record_id_seq OWNED BY unispy.nat_fail_ca -- CREATE TABLE unispy.partner ( - partnerid integer NOT NULL, + partnerid serial primary key, partnername character varying NOT NULL ); @@ -436,9 +434,9 @@ COMMENT ON TABLE unispy.partner IS 'Partner information, these information are u -- CREATE TABLE unispy.profiles ( - profileid integer NOT NULL, + profileid SERIAL PRIMARY KEY NOT NULL, userid integer NOT NULL, - nick character varying NOT NULL, + nick character varying NOT NULL UNIQUE, serverflag integer NOT NULL, status smallint not NULL, statstring character varying, @@ -612,8 +610,8 @@ ALTER SEQUENCE unispy.subprofiles_subprofileid_seq OWNED BY unispy.subprofiles.s -- CREATE TABLE unispy.users ( - userid integer NOT NULL, - email character varying NOT NULL, + userid serial primary key, + email character varying NOT NULL UNIQUE, password character varying NOT NULL, emailverified boolean DEFAULT true NOT NULL, lastip inet, From 1c04028d7b1a076ed8a1dd4a4751d46d4edd097a Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 24 Jan 2025 02:53:15 +0000 Subject: [PATCH 154/231] fix instant id type error --- .../presence_search_player/handlers.py | 6 +- .../presence_search_player/requests.py | 2 +- .../precence_search_player/requests_tests.py | 67 +++++++++++++++++++ .../gamespy/query_report/data_fetch_tests.py | 1 + .../gamespy/query_report/integration_tests.py | 3 +- src/library/src/abstractions/handler.py | 5 +- .../src/extentions/password_encoder.py | 2 +- src/library/tests/mock_objects.py | 2 +- .../src/contracts/requests.py | 8 ++- .../tests/handler_tests.py | 5 +- .../query_report/tests/mock_objects.py | 2 +- .../query_report/tests/request_tests.py | 8 +-- .../server_browser/tests/mock_objects.py | 8 +-- 13 files changed, 96 insertions(+), 23 deletions(-) create mode 100644 src/backends/tests/gamespy/precence_search_player/requests_tests.py diff --git a/src/backends/protocols/gamespy/presence_search_player/handlers.py b/src/backends/protocols/gamespy/presence_search_player/handlers.py index e82648542..4b06ab86e 100644 --- a/src/backends/protocols/gamespy/presence_search_player/handlers.py +++ b/src/backends/protocols/gamespy/presence_search_player/handlers.py @@ -140,15 +140,19 @@ class SearchHandler(HandlerBase): async def _data_operate(self) -> None: if self._request.request_type == SearchType.NICK_SEARCH: + assert self._request.nick self._data = data.get_matched_info_by_nick(self._request.nick) - elif self._request.request_type == SearchType.NICK_EMAIL_SEARCH: + assert self._request.email + assert self._request.nick data.get_matched_info_by_nick_and_email( self._request.nick, self._request.email) elif self._request.request_type == SearchType.UNIQUENICK_NAMESPACEID_SEARCH: + assert self._request.uniquenick self._data = data.get_matched_info_by_uniquenick_and_namespaceid( self._request.uniquenick, self._request.namespace_id) elif self._request.request_type == SearchType.EMAIL_SEARCH: + assert self._request.email self._data = data.get_matched_info_by_email(self._request.email) else: raise UniSpyException("search type invalid") diff --git a/src/backends/protocols/gamespy/presence_search_player/requests.py b/src/backends/protocols/gamespy/presence_search_player/requests.py index 6dbdc71d6..4580259de 100644 --- a/src/backends/protocols/gamespy/presence_search_player/requests.py +++ b/src/backends/protocols/gamespy/presence_search_player/requests.py @@ -38,8 +38,8 @@ class NewUserRequest(RequestBase): class NicksRequest(RequestBase): password: str email: str - is_require_uniquenicks: bool namespace_id: int + is_require_uniquenicks: bool class OthersListRequest(RequestBase): diff --git a/src/backends/tests/gamespy/precence_search_player/requests_tests.py b/src/backends/tests/gamespy/precence_search_player/requests_tests.py new file mode 100644 index 000000000..bb87b4a04 --- /dev/null +++ b/src/backends/tests/gamespy/precence_search_player/requests_tests.py @@ -0,0 +1,67 @@ +# the total requests tests +import unittest + +import servers.presence_search_player.src.contracts.requests as psp +from servers.presence_search_player.tests.handler_tests import ( + NICKS, SEARCH_1, SEARCH_2, SEARCH_3, SEARCH_4, CHECK1, NEWUSER, SEARCH_UNIQUENICK, SUGGEST_UNIQUE, VALID) +import backends.protocols.gamespy.presence_search_player.requests as bk + + +def add_nessesary_info(request) -> dict: + request.parse() + data = request.to_dict() + data["client_ip"] = "192.168.0.1" + data["server_id"] = "950b7638-a90d-469b-ac1f-861e63c8c613" + data["client_port"] = 1234 + return data + + +class RequestsCompatibleTests(unittest.TestCase): + """ + test backend and server request compability + """ + # region PCM + # region PSP + + def test_search(self): + for raw in [SEARCH_1, SEARCH_2, SEARCH_3, SEARCH_4]: + r = psp.SearchRequest(raw) + data = add_nessesary_info(r) + bk.SearchRequest(**data) + + def test_chenck(self): + r = psp.CheckRequest(CHECK1) + data = add_nessesary_info(r) + bk.CheckRequest(**data) + pass + + def test_new_user(self): + pass + r = psp.NewUserRequest(NEWUSER) + data = add_nessesary_info(r) + bk.NewUserRequest(**data) + + def test_nick(self): + r = psp.NicksRequest(NICKS) + data = add_nessesary_info(r) + bk.NicksRequest(**data) + pass + + def test_others_list(self): + # r = psp.OthersListRequest() + pass + + def test_others(self): + pass + + def test_search_unique(self): + r = psp.SearchUniqueRequest(SEARCH_UNIQUENICK) + data = add_nessesary_info(r) + bk.SearchUniqueRequest(**data) + pass + + def test_valid(self): + r = psp.ValidRequest(VALID) + data = add_nessesary_info(r) + bk.ValidRequest(**data) + pass diff --git a/src/backends/tests/gamespy/query_report/data_fetch_tests.py b/src/backends/tests/gamespy/query_report/data_fetch_tests.py index 6945de7e3..fa05d21de 100644 --- a/src/backends/tests/gamespy/query_report/data_fetch_tests.py +++ b/src/backends/tests/gamespy/query_report/data_fetch_tests.py @@ -19,4 +19,5 @@ def test_get_peer_staging_channels(self): # PG_SESSION.commit() self.assertRaises(ValidationError,data.get_peer_staging_channels,"unispy_test_game_name", 0) + \ No newline at end of file diff --git a/src/backends/tests/gamespy/query_report/integration_tests.py b/src/backends/tests/gamespy/query_report/integration_tests.py index 2c0edaca7..cf8ec12a8 100644 --- a/src/backends/tests/gamespy/query_report/integration_tests.py +++ b/src/backends/tests/gamespy/query_report/integration_tests.py @@ -1,2 +1,3 @@ -class IntegrationTest: \ No newline at end of file +class IntegrationTest: + pass diff --git a/src/library/src/abstractions/handler.py b/src/library/src/abstractions/handler.py index 288b61da5..96a49b2ae 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/library/src/abstractions/handler.py @@ -79,12 +79,13 @@ def _prepare_data(self): self._temp_data = self._request.to_dict() if "server_id" in self._temp_data: raise UniSpyException("server_id name collision in dict") - self._temp_data["server_id"] = self._client.server_config.server_id if "client_ip" in self._temp_data: raise UniSpyException("client_ip name collision in dict") - self._temp_data["client_ip"] = self._client.connection.remote_ip if "client_port" in self._temp_data: raise UniSpyException("client_port name collision in dict") + # add the server info to json request dict + self._temp_data["client_ip"] = self._client.connection.remote_ip + self._temp_data["server_id"] = self._client.server_config.server_id self._temp_data["client_port"] = self._client.connection.remote_port def _upload_data(self): diff --git a/src/library/src/extentions/password_encoder.py b/src/library/src/extentions/password_encoder.py index b720675f9..92424e978 100644 --- a/src/library/src/extentions/password_encoder.py +++ b/src/library/src/extentions/password_encoder.py @@ -10,7 +10,7 @@ def process_password(request: dict): if "passwordenc" in request: md5_password = get_md5_hash(decode(request["passwordenc"])) elif "passenc" in request: - md5_password = get_md5_hash(decode(request["passenc"])) + md5_password = get_md5_hash(request["passenc"]) elif "pass" in request: md5_password = get_md5_hash(request["pass"]) elif "password" in request: diff --git a/src/library/tests/mock_objects.py b/src/library/tests/mock_objects.py index a76513f88..7c0ac5802 100644 --- a/src/library/tests/mock_objects.py +++ b/src/library/tests/mock_objects.py @@ -55,6 +55,6 @@ def unsubscribe(self): def create_mock_url(config: ServerConfig, handler: type[CmdHandlerBase], data: dict) -> None: # fmt: off - url = f"{CONFIG.backend.url}/GameSpy/{config.server_name}/{handler.__name__}/" + url = f"{CONFIG.backend.url}/GameSpy/{config.server_name}/{handler.__name__}" responses.add(responses.POST, url, json=data, status=200) # fmt: on diff --git a/src/servers/presence_search_player/src/contracts/requests.py b/src/servers/presence_search_player/src/contracts/requests.py index 0999a9ff5..0716369c4 100644 --- a/src/servers/presence_search_player/src/contracts/requests.py +++ b/src/servers/presence_search_player/src/contracts/requests.py @@ -110,10 +110,11 @@ def parse_other_info(self): class NicksRequest(RequestBase): password: str email: str - is_require_uniquenicks: bool = True + is_require_uniquenicks: bool def parse(self): super().parse() + self.is_require_uniquenicks = True self.password = process_password(self.request_dict) if "email" not in self.request_dict.keys(): raise GPParseException("email is missing.") @@ -185,6 +186,7 @@ class SearchRequest(RequestBase): def __init__(self, raw_request: str) -> None: super().__init__(raw_request) self.skip_num = 0 + self.partner_id = 0 def parse(self) -> None: super().parse() @@ -250,11 +252,11 @@ def parse(self) -> None: class SearchUniqueRequest(RequestBase): uniquenick: str - namespace_ids: list[int] = [] + namespace_ids: list[int] def parse(self): super().parse() - + self.namespace_ids = [] if ( "uniquenick" not in self.request_dict or "namespaces" not in self.request_dict diff --git a/src/servers/presence_search_player/tests/handler_tests.py b/src/servers/presence_search_player/tests/handler_tests.py index acfa66998..4a3e38a44 100644 --- a/src/servers/presence_search_player/tests/handler_tests.py +++ b/src/servers/presence_search_player/tests/handler_tests.py @@ -6,7 +6,7 @@ from servers.presence_search_player.src.contracts.requests import SearchRequest from servers.presence_search_player.src.applications.handlers import SearchHandler from servers.presence_search_player.tests.mock_objects import create_client - +CHECK1 = "\\check\\\\nick\\spyguy\\email\\spyguy@gamespy.com\\pass\\0000\\final\\" SEARCH_1 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\namespaceid\\0\\uniquenick\\spyguy\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" SEARCH_2 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\nick\\spyguy\\email\\spyguy@unispy.org\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" @@ -31,8 +31,6 @@ SUGGEST_UNIQUE = "\\uniquesearch\\\\preferrednick\\xiaojiuwo\\namespaceid\\0\\gamename\\gmtest\\final\\" - - class HandlerTests(unittest.TestCase): @responses.activate @@ -62,7 +60,6 @@ def test_profile(self): request.parse() self.assertEqual("spyguy@unispy.org", request.email) - handler = SearchHandler(client, request) handler.handle() self.assertEqual("\\bsr\\0\\nick\\spyguy\\uniquenick\\spyguy\\namespaceid\\0\\firstname\\spy\\lastname\\guy\\email\\spyguy@unispy.org\\bsrdone\\\\more\\0\\final\\", diff --git a/src/servers/query_report/tests/mock_objects.py b/src/servers/query_report/tests/mock_objects.py index befc971f3..23f57437a 100644 --- a/src/servers/query_report/tests/mock_objects.py +++ b/src/servers/query_report/tests/mock_objects.py @@ -19,7 +19,7 @@ def create_client() -> Client: logger=logger) config = CONFIG.servers["QueryReport"] create_mock_url(config, HeartBeatHandler, HeartBeatResult.model_validate( - {"remote_ip_address": conn.remote_ip, "remote_port": conn.remote_port}).model_dump()) + {"remote_ip_address": conn.remote_ip, "remote_port": conn.remote_port}).model_dump(mode='json')) create_mock_url(config, AvailableHandler, {"message": "ok"}) create_mock_url(config, KeepAliveHandler, {"message": "ok"}) return cast(Client, conn._client) diff --git a/src/servers/query_report/tests/request_tests.py b/src/servers/query_report/tests/request_tests.py index 95b4536b5..b34dd1fd0 100644 --- a/src/servers/query_report/tests/request_tests.py +++ b/src/servers/query_report/tests/request_tests.py @@ -14,7 +14,7 @@ def test_available(self): request = AvaliableRequest(raw) request.parse() self.assertEqual(RequestType.AVALIABLE_CHECK, request.command_name) - self.assertEqual(0, request.instant_key) + self.assertEqual("0", request.instant_key) def test_challenge(self): raw = bytes([ @@ -26,7 +26,7 @@ def test_challenge(self): request = ChallengeRequest(raw) request.parse() self.assertEqual(RequestType.CHALLENGE, request.command_name) - self.assertEqual(0, request.instant_key) + self.assertEqual("0", request.instant_key) def test_echo_request(self): raw = bytes([ @@ -38,7 +38,7 @@ def test_echo_request(self): request = EchoRequest(raw) request.parse() self.assertEqual(RequestType.ECHO, request.command_name) - self.assertEqual(0, request.instant_key) + self.assertEqual("0", request.instant_key) def test_heartbeat(self): raw = bytes([ @@ -46,7 +46,7 @@ def test_heartbeat(self): request = HeartBeatRequest(raw) request.parse() self.assertEqual("gmtest", request.game_name) - self.assertEqual(2921297764, request.instant_key) + self.assertEqual("2921297764", request.instant_key) self.assertEqual(6, len(request.player_data)) self.assertEqual(19, len(request.server_data)) self.assertEqual(2, len(request.team_data)) diff --git a/src/servers/server_browser/tests/mock_objects.py b/src/servers/server_browser/tests/mock_objects.py index 4f64ed451..df0ac5799 100644 --- a/src/servers/server_browser/tests/mock_objects.py +++ b/src/servers/server_browser/tests/mock_objects.py @@ -20,11 +20,11 @@ def create_v2_client() -> Client: logger=logger) config = CONFIG.servers["ServerBrowserV2"] - create_mock_url(config, ServerListHandler, json.loads( + create_mock_url(config, ServerListHandler, ServerMainListResult.model_validate({"client_remote_ip": "127.0.0.1", "flag": 64, - "game_secret_key": "123567", "servers_info": []}).model_dump_json())) - create_mock_url(config, ServerInfoHandler, json.loads(ServerInfoResult.model_validate({"game_server_info": {"server_id": "550e8400-e29b-41d4-a716-446655440000", "host_ip_address": "192.168.1.1", "instant_key": 123456, "game_name": "Example Game", "query_report_port": 8080, "last_heart_beat_received_time": "2023-10-01T12:00:00Z", "status": 3, "server_data": { - "max_players": "100", "current_players": "50", "region": "US-East"}, "player_data": [{"player_id": "player1", "player_name": "Player One"}, {"player_id": "player2", "player_name": "Player Two"}], "team_data": [{"team_id": "team1", "team_name": "Team Alpha"}, {"team_id": "team2", "team_name": "Team Beta"}]}}).model_dump_json())) + "game_secret_key": "123567", "servers_info": []}).model_dump(mode="json")) + create_mock_url(config, ServerInfoHandler, ServerInfoResult.model_validate({"game_server_info": {"server_id": "550e8400-e29b-41d4-a716-446655440000", "host_ip_address": "192.168.1.1", "instant_key": "123456", "game_name": "Example Game", "query_report_port": 8080, "last_heart_beat_received_time": "2023-10-01T12:00:00Z", "status": 3, "server_data": { + "max_players": "100", "current_players": "50", "region": "US-East"}, "player_data": [{"player_id": "player1", "player_name": "Player One"}, {"player_id": "player2", "player_name": "Player Two"}], "team_data": [{"team_id": "team1", "team_name": "Team Alpha"}, {"team_id": "team2", "team_name": "Team Beta"}]}}).model_dump(mode="json")) return cast(Client, conn._client) From 894cc98d3eb26543bc27ad98f30ae9cbde87046d Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 24 Jan 2025 07:22:00 +0000 Subject: [PATCH 155/231] update sql scripts --- common/UniSpy_pg.sql | 6 +++--- src/backends/tests/gamespy/query_report/data_fetch_tests.py | 6 ++++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 2f9f65752..1b43ea0dc 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -115,7 +115,7 @@ ALTER SEQUENCE unispy.blocked_blockid_seq OWNED BY unispy.blocked.blockid; -- CREATE TABLE unispy.chat_channel_caches ( - channel_name character varying NOT NULL, + channel_name character varying NOT NULL UNIQUE, server_id uuid NOT NULL, game_name character varying NOT NULL, room_name character varying NOT NULL, @@ -137,7 +137,7 @@ ALTER TABLE unispy.chat_channel_caches OWNER TO unispy; CREATE TABLE unispy.chat_nick_caches ( server_id uuid NOT NULL, - nick_name character varying NOT NULL, + nick_name character varying NOT NULL UNIQUE, game_name character varying, user_name character varying, remote_ip_address inet NOT NULL, @@ -154,7 +154,7 @@ ALTER TABLE unispy.chat_nick_caches OWNER TO unispy; -- CREATE TABLE unispy.chat_user_caches ( - nick_name character varying NOT NULL, + nick_name character varying NOT NULL UNIQUE, channel_name character varying NOT NULL UNIQUE, server_id uuid NOT NULL, user_name character varying NOT NULL, diff --git a/src/backends/tests/gamespy/query_report/data_fetch_tests.py b/src/backends/tests/gamespy/query_report/data_fetch_tests.py index fa05d21de..a84312cd3 100644 --- a/src/backends/tests/gamespy/query_report/data_fetch_tests.py +++ b/src/backends/tests/gamespy/query_report/data_fetch_tests.py @@ -15,9 +15,11 @@ def test_get_all_groups(self): def test_get_peer_staging_channels(self): cache = ChatChannelCaches(channel_name="#GSP!unispy_test_game_name!*", server_id="b6480a17-5e3d-4da0-aeec-c421620bff71", game_name="unispy_test_game_name", room_name="unispy_test_room_name", group_id=0, max_num_user=100, key_values={}, invited_nicks={}, update_time=datetime.now(timezone.utc)) - # PG_SESSION.add(cache) - # PG_SESSION.commit() + PG_SESSION.add(cache) + PG_SESSION.commit() self.assertRaises(ValidationError,data.get_peer_staging_channels,"unispy_test_game_name", 0) + PG_SESSION.delete(cache) + PG_SESSION.commit() \ No newline at end of file From 479293e5c76f443811a79cc6a606f9cef714d9d3 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 24 Jan 2025 09:10:09 +0000 Subject: [PATCH 156/231] fix sb logic --- common/UniSpy_pg.sql | 367 +++++------------- .../protocols/gamespy/query_report/data.py | 31 +- .../gamespy/server_browser/handlers.py | 22 +- 3 files changed, 146 insertions(+), 274 deletions(-) diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 1b43ea0dc..04156f0dd 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -41,7 +41,7 @@ SET default_table_access_method = heap; -- CREATE TABLE unispy.addrequests ( - addrequestid integer NOT NULL, + addrequestid SERIAL PRIMARY KEY NOT NULL, profileid integer NOT NULL, targetid integer NOT NULL, namespaceid integer NOT NULL, @@ -56,13 +56,13 @@ ALTER TABLE unispy.addrequests OWNER TO unispy; -- Name: addrequests_addrequestid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- -CREATE SEQUENCE unispy.addrequests_addrequestid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; +-- CREATE SEQUENCE unispy.addrequests_addrequestid_seq +-- AS integer +-- START WITH 1 +-- INCREMENT BY 1 +-- NO MINVALUE +-- NO MAXVALUE +-- CACHE 1; ALTER TABLE unispy.addrequests_addrequestid_seq OWNER TO unispy; @@ -79,7 +79,7 @@ ALTER SEQUENCE unispy.addrequests_addrequestid_seq OWNED BY unispy.addrequests.a -- CREATE TABLE unispy.blocked ( - blockid integer NOT NULL, + blockid SERIAL PRIMARY KEY NOT NULL, profileid integer NOT NULL, namespaceid integer NOT NULL, targetid integer NOT NULL @@ -92,13 +92,13 @@ ALTER TABLE unispy.blocked OWNER TO unispy; -- Name: blocked_blockid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- -CREATE SEQUENCE unispy.blocked_blockid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; +-- CREATE SEQUENCE unispy.blocked_blockid_seq +-- AS integer +-- START WITH 1 +-- INCREMENT BY 1 +-- NO MINVALUE +-- NO MAXVALUE +-- CACHE 1; ALTER TABLE unispy.blocked_blockid_seq OWNER TO unispy; @@ -175,7 +175,7 @@ ALTER TABLE unispy.chat_user_caches OWNER TO unispy; -- CREATE TABLE unispy.friends ( - friendid integer NOT NULL, + friendid SERIAL PRIMARY KEY NOT NULL, profileid integer NOT NULL, targetid integer NOT NULL, namespaceid integer NOT NULL @@ -188,13 +188,13 @@ ALTER TABLE unispy.friends OWNER TO unispy; -- Name: friends_friendid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- -CREATE SEQUENCE unispy.friends_friendid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; +-- CREATE SEQUENCE unispy.friends_friendid_seq +-- AS integer +-- START WITH 1 +-- INCREMENT BY 1 +-- NO MINVALUE +-- NO MAXVALUE +-- CACHE 1; ALTER TABLE unispy.friends_friendid_seq OWNER TO unispy; @@ -211,7 +211,7 @@ ALTER SEQUENCE unispy.friends_friendid_seq OWNED BY unispy.friends.friendid; -- CREATE TABLE unispy.game_server_caches ( - instant_key character varying(10) NOT NULL, + instant_key character varying(10) PRIMARY KEY NOT NULL UNIQUE, server_id uuid NOT NULL, host_ip_address inet NOT NULL, game_name character varying NOT NULL, @@ -231,22 +231,22 @@ ALTER TABLE unispy.game_server_caches OWNER TO unispy; -- Name: game_server_caches_instant_key_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- -CREATE SEQUENCE unispy.game_server_caches_instant_key_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; +-- CREATE SEQUENCE unispy.game_server_caches_instant_key_seq +-- AS integer +-- START WITH 1 +-- INCREMENT BY 1 +-- NO MINVALUE +-- NO MAXVALUE +-- CACHE 1; -ALTER TABLE unispy.game_server_caches_instant_key_seq OWNER TO unispy; +-- ALTER TABLE unispy.game_server_caches_instant_key_seq OWNER TO unispy; -- -- Name: game_server_caches_instant_key_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy -- -ALTER SEQUENCE unispy.game_server_caches_instant_key_seq OWNED BY unispy.game_server_caches.instant_key; +-- ALTER SEQUENCE unispy.game_server_caches_instant_key_seq OWNED BY unispy.game_server_caches.instant_key; -- @@ -276,7 +276,7 @@ COMMENT ON TABLE unispy.games IS 'Game list.'; -- CREATE TABLE unispy.grouplist ( - groupid integer NOT NULL, + groupid integer NOT NULL UNIQUE, gameid integer NOT NULL, roomname text NOT NULL ); @@ -317,22 +317,22 @@ ALTER TABLE unispy.init_packet_caches OWNER TO unispy; -- Name: init_packet_caches_cookie_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- -CREATE SEQUENCE unispy.init_packet_caches_cookie_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; +-- CREATE SEQUENCE unispy.init_packet_caches_cookie_seq +-- AS integer +-- START WITH 1 +-- INCREMENT BY 1 +-- NO MINVALUE +-- NO MAXVALUE +-- CACHE 1; -ALTER TABLE unispy.init_packet_caches_cookie_seq OWNER TO unispy; +-- ALTER TABLE unispy.init_packet_caches_cookie_seq OWNER TO unispy; -- -- Name: init_packet_caches_cookie_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy -- -ALTER SEQUENCE unispy.init_packet_caches_cookie_seq OWNED BY unispy.init_packet_caches.cookie; +-- ALTER SEQUENCE unispy.init_packet_caches_cookie_seq OWNED BY unispy.init_packet_caches.cookie; -- @@ -340,7 +340,7 @@ ALTER SEQUENCE unispy.init_packet_caches_cookie_seq OWNED BY unispy.init_packet_ -- CREATE TABLE unispy.messages ( - messageid integer NOT NULL, + messageid SERIAL PRIMARY KEY NOT NULL, namespaceid integer NOT NULL, type integer, from_user integer NOT NULL, @@ -356,13 +356,13 @@ ALTER TABLE unispy.messages OWNER TO unispy; -- Name: messages_messageid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- -CREATE SEQUENCE unispy.messages_messageid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; +-- CREATE SEQUENCE unispy.messages_messageid_seq +-- AS integer +-- START WITH 1 +-- INCREMENT BY 1 +-- NO MINVALUE +-- NO MAXVALUE +-- CACHE 1; ALTER TABLE unispy.messages_messageid_seq OWNER TO unispy; @@ -379,7 +379,7 @@ ALTER SEQUENCE unispy.messages_messageid_seq OWNED BY unispy.messages.messageid; -- CREATE TABLE unispy.nat_fail_cachess ( - record_id integer NOT NULL, + record_id SERIAL NOT NULL, public_ip_address1 inet NOT NULL, public_ip_address2 inet NOT NULL, update_time timestamp without time zone NOT NULL @@ -392,13 +392,13 @@ ALTER TABLE unispy.nat_fail_cachess OWNER TO unispy; -- Name: nat_fail_cachess_record_id_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- -CREATE SEQUENCE unispy.nat_fail_cachess_record_id_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; +-- CREATE SEQUENCE unispy.nat_fail_cachess_record_id_seq +-- AS integer +-- START WITH 1 +-- INCREMENT BY 1 +-- NO MINVALUE +-- NO MAXVALUE +-- CACHE 1; ALTER TABLE unispy.nat_fail_cachess_record_id_seq OWNER TO unispy; @@ -415,7 +415,7 @@ ALTER SEQUENCE unispy.nat_fail_cachess_record_id_seq OWNED BY unispy.nat_fail_ca -- CREATE TABLE unispy.partner ( - partnerid serial primary key, + partnerid SERIAL PRIMARY KEY, partnername character varying NOT NULL ); @@ -450,13 +450,13 @@ ALTER TABLE unispy.profiles OWNER TO unispy; -- Name: profiles_profileid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- -CREATE SEQUENCE unispy.profiles_profileid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; +-- CREATE SEQUENCE unispy.profiles_profileid_seq +-- AS integer +-- START WITH 1 +-- INCREMENT BY 1 +-- NO MINVALUE +-- NO MAXVALUE +-- CACHE 1; ALTER TABLE unispy.profiles_profileid_seq OWNER TO unispy; @@ -473,7 +473,7 @@ ALTER SEQUENCE unispy.profiles_profileid_seq OWNED BY unispy.profiles.profileid; -- CREATE TABLE unispy.pstorage ( - pstorageid integer NOT NULL, + pstorageid SERIAL PRIMARY KEY NOT NULL, profileid integer NOT NULL, ptype integer NOT NULL, dindex integer NOT NULL, @@ -487,13 +487,13 @@ ALTER TABLE unispy.pstorage OWNER TO unispy; -- Name: pstorage_pstorageid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- -CREATE SEQUENCE unispy.pstorage_pstorageid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; +-- CREATE SEQUENCE unispy.pstorage_pstorageid_seq +-- AS integer +-- START WITH 1 +-- INCREMENT BY 1 +-- NO MINVALUE +-- NO MAXVALUE +-- CACHE 1; ALTER TABLE unispy.pstorage_pstorageid_seq OWNER TO unispy; @@ -524,7 +524,7 @@ ALTER TABLE unispy.relay_server_caches OWNER TO unispy; -- CREATE TABLE unispy.sakestorage ( - sakestorageid integer NOT NULL, + sakestorageid SERIAL PRIMARY KEY NOT NULL, tableid integer NOT NULL, data jsonb ); @@ -543,13 +543,13 @@ COMMENT ON TABLE unispy.sakestorage IS 'Sake storage system.'; -- Name: sakestorage_sakestorageid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- -CREATE SEQUENCE unispy.sakestorage_sakestorageid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; +-- CREATE SEQUENCE unispy.sakestorage_sakestorageid_seq +-- AS integer +-- START WITH 1 +-- INCREMENT BY 1 +-- NO MINVALUE +-- NO MAXVALUE +-- CACHE 1; ALTER TABLE unispy.sakestorage_sakestorageid_seq OWNER TO unispy; @@ -566,7 +566,7 @@ ALTER SEQUENCE unispy.sakestorage_sakestorageid_seq OWNED BY unispy.sakestorage. -- CREATE TABLE unispy.subprofiles ( - subprofileid integer NOT NULL, + subprofileid SERIAL PRIMARY KEY NOT NULL, profileid integer NOT NULL, uniquenick character varying, namespaceid integer NOT NULL, @@ -587,13 +587,13 @@ ALTER TABLE unispy.subprofiles OWNER TO unispy; -- Name: subprofiles_subprofileid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- -CREATE SEQUENCE unispy.subprofiles_subprofileid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; +-- CREATE SEQUENCE unispy.subprofiles_subprofileid_seq +-- AS integer +-- START WITH 1 +-- INCREMENT BY 1 +-- NO MINVALUE +-- NO MAXVALUE +-- CACHE 1; ALTER TABLE unispy.subprofiles_subprofileid_seq OWNER TO unispy; @@ -635,13 +635,13 @@ COMMENT ON TABLE unispy.users IS 'User account information.'; -- Name: users_userid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- -CREATE SEQUENCE unispy.users_userid_seq - AS integer - START WITH 1 - INCREMENT BY 1 - NO MINVALUE - NO MAXVALUE - CACHE 1; +-- CREATE SEQUENCE unispy.users_userid_seq +-- AS integer +-- START WITH 1 +-- INCREMENT BY 1 +-- NO MINVALUE +-- NO MAXVALUE +-- CACHE 1; ALTER TABLE unispy.users_userid_seq OWNER TO unispy; @@ -671,21 +671,21 @@ ALTER TABLE ONLY unispy.blocked ALTER COLUMN blockid SET DEFAULT nextval('unispy -- Name: friends friendid; Type: DEFAULT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.friends ALTER COLUMN friendid SET DEFAULT nextval('unispy.friends_friendid_seq'::regclass); +-- ALTER TABLE ONLY unispy.friends ALTER COLUMN friendid SET DEFAULT nextval('unispy.friends_friendid_seq'::regclass); -- -- Name: game_server_caches instant_key; Type: DEFAULT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.game_server_caches ALTER COLUMN instant_key SET DEFAULT nextval('unispy.game_server_caches_instant_key_seq'::regclass); +-- ALTER TABLE ONLY unispy.game_server_caches ALTER COLUMN instant_key SET DEFAULT nextval('unispy.game_server_caches_instant_key_seq'::regclass); -- -- Name: init_packet_caches cookie; Type: DEFAULT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.init_packet_caches ALTER COLUMN cookie SET DEFAULT nextval('unispy.init_packet_caches_cookie_seq'::regclass); +-- ALTER TABLE ONLY unispy.init_packet_caches ALTER COLUMN cookie SET DEFAULT nextval('unispy.init_packet_caches_cookie_seq'::regclass); -- @@ -5413,14 +5413,14 @@ SELECT pg_catalog.setval('unispy.friends_friendid_seq', 1, false); -- Name: game_server_caches_instant_key_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy -- -SELECT pg_catalog.setval('unispy.game_server_caches_instant_key_seq', 1, false); +-- SELECT pg_catalog.setval('unispy.game_server_caches_instant_key_seq', 1, false); -- -- Name: init_packet_caches_cookie_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy -- -SELECT pg_catalog.setval('unispy.init_packet_caches_cookie_seq', 1, false); +-- SELECT pg_catalog.setval('unispy.init_packet_caches_cookie_seq', 1, false); -- @@ -5471,159 +5471,6 @@ SELECT pg_catalog.setval('unispy.subprofiles_subprofileid_seq', 1, false); SELECT pg_catalog.setval('unispy.users_userid_seq', 5, true); - --- --- Name: addrequests addrequests_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.addrequests - ADD CONSTRAINT addrequests_pkey PRIMARY KEY (addrequestid); - - --- --- Name: blocked blocked_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.blocked - ADD CONSTRAINT blocked_pkey PRIMARY KEY (blockid); - - --- --- Name: chat_channel_caches chat_channel_caches_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.chat_channel_caches - ADD CONSTRAINT chat_channel_caches_pkey PRIMARY KEY (channel_name); - - --- --- Name: chat_nick_caches chat_nick_caches_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.chat_nick_caches - ADD CONSTRAINT chat_nick_caches_pkey PRIMARY KEY (nick_name); - - --- --- Name: chat_user_caches chat_user_caches_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.chat_user_caches - ADD CONSTRAINT chat_user_caches_pkey PRIMARY KEY (nick_name); - - --- --- Name: friends friends_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.friends - ADD CONSTRAINT friends_pkey PRIMARY KEY (friendid); - - --- --- Name: game_server_caches game_server_caches_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.game_server_caches - ADD CONSTRAINT game_server_caches_pkey PRIMARY KEY (instant_key); - - --- --- Name: games games_pk; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.games - ADD CONSTRAINT games_pk PRIMARY KEY (gameid); - - --- --- Name: grouplist grouplist_pk; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.grouplist - ADD CONSTRAINT grouplist_pk PRIMARY KEY (groupid); - - --- --- Name: init_packet_caches init_packet_caches_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.init_packet_caches - ADD CONSTRAINT init_packet_caches_pkey PRIMARY KEY (cookie); - - --- --- Name: messages messages_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.messages - ADD CONSTRAINT messages_pkey PRIMARY KEY (messageid); - - --- --- Name: nat_fail_cachess nat_fail_cachess_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.nat_fail_cachess - ADD CONSTRAINT nat_fail_cachess_pkey PRIMARY KEY (record_id); - - --- --- Name: partner partner_pk; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.partner - ADD CONSTRAINT partner_pk PRIMARY KEY (partnerid); - - --- --- Name: profiles profiles_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.profiles - ADD CONSTRAINT profiles_pkey PRIMARY KEY (profileid); - - --- --- Name: pstorage pstorage_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.pstorage - ADD CONSTRAINT pstorage_pkey PRIMARY KEY (pstorageid); - - --- --- Name: relay_server_caches relay_server_caches_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.relay_server_caches - ADD CONSTRAINT relay_server_caches_pkey PRIMARY KEY (server_id); - - --- --- Name: sakestorage sakestorage_pk; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.sakestorage - ADD CONSTRAINT sakestorage_pk PRIMARY KEY (sakestorageid); - - --- --- Name: subprofiles subprofiles_pkey; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.subprofiles - ADD CONSTRAINT subprofiles_pkey PRIMARY KEY (subprofileid); - - --- --- Name: users users_pk; Type: CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.users - ADD CONSTRAINT users_pk PRIMARY KEY (userid); - - -- -- Name: chat_user_caches chat_user_caches_channel_name_fkey; Type: FK CONSTRAINT; Schema: unispy; Owner: unispy -- @@ -5648,7 +5495,7 @@ ALTER TABLE ONLY unispy.profiles ADD CONSTRAINT profiles_userid_fkey FOREIGN KEY (userid) REFERENCES unispy.users(userid); --- --- PostgreSQL database dump complete --- + +PostgreSQL database dump complete + diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index c7a01c2e1..a3e2ff519 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -54,15 +54,30 @@ def get_peer_staging_channels(game_name: str, group_id: int) -> list[GameServerI return data -def get_peer_group_channel(group_id: int) -> list[PeerRoomInfo]: - assert isinstance(group_id, int) - group_name = f"{PeerRoom.GroupRoomPrefix}!{group_id}" +def get_group_data_list_by_gamename(game_name: str) -> list[dict]: + assert isinstance(game_name, str) + if game_name not in PEER_GROUP_LIST: + raise ValueError(f"game name: {game_name} not in PEER_GROUP_LIST") + # the group id list length can not be 0 + result = PEER_GROUP_LIST[game_name] + assert len(result) != 0 + return result - result = PG_SESSION.query(ChatChannelCaches).where( - ChatChannelCaches.channel_name == group_name).all() - data = [] - for s in result: - data.append(PeerRoomInfo(**s)) + +def get_peer_group_channel(group_data: list[dict]) -> list[PeerRoomInfo]: + assert isinstance(group_data, list) and all( + isinstance(id, dict) for id in group_data) + # Construct the group names based on the provided group_ids + group_names = [f"{PeerRoom.GroupRoomPrefix}!{item['group_id']}" for item in group_data] + + # Query the database for channels matching the constructed group names + result = PG_SESSION.query(ChatChannelCaches).filter( + ChatChannelCaches.channel_name.in_(group_names) + ).all() + + # Convert the result to a list of PeerRoomInfo objects + data = [PeerRoomInfo(**s) for s in result] + return data diff --git a/src/backends/protocols/gamespy/server_browser/handlers.py b/src/backends/protocols/gamespy/server_browser/handlers.py index 5fc97274d..c769feb02 100644 --- a/src/backends/protocols/gamespy/server_browser/handlers.py +++ b/src/backends/protocols/gamespy/server_browser/handlers.py @@ -1,3 +1,4 @@ +from typing import TYPE_CHECKING, cast from backends.library.abstractions.handler_base import HandlerBase from backends.protocols.gamespy.server_browser.requests import * import backends.protocols.gamespy.query_report.data as data @@ -12,6 +13,7 @@ class ServerListHandler(HandlerBase): _request: ServerListRequest + _caches: list[PeerRoomInfo] | list[GameServerInfo] async def _data_operate(self): if self._request.update_option in\ @@ -20,11 +22,13 @@ async def _data_operate(self): ServerListUpdateOption.LIMIT_RESULT_COUNT, ServerListUpdateOption.SERVER_FULL_INFO_LIST,]: - self.data = data.get_server_info_list_with_game_name( + self._caches = data.get_server_info_list_with_game_name( self._request.game_name) elif self._request.update_option == ServerListUpdateOption.P2P_GROUP_ROOM_LIST: - self.data = data.get_peer_group_channel + group_data = data.get_group_data_list_by_gamename( + self._request.game_name) + self._caches = data.get_peer_group_channel(group_data) else: raise ServerBrowserException( "invalid server browser update option") @@ -35,13 +39,19 @@ async def _result_construct(self): ServerListUpdateOption.P2P_SERVER_MAIN_LIST, ServerListUpdateOption.LIMIT_RESULT_COUNT, ServerListUpdateOption.SERVER_FULL_INFO_LIST,]: - assert isinstance(self.data, GameServerInfo) + assert isinstance(self._caches, list) and all( + isinstance(item, GameServerInfo) for item in self._caches) + if TYPE_CHECKING: + self._caches = cast(list[GameServerInfo], self._caches) self._result = ServerMainListResult(client_remote_ip=self._request.client_ip, - flag=GameServerFlags.HAS_KEYS_FLAG, game_secret_key="", servers_info=self.data) + flag=GameServerFlags.HAS_KEYS_FLAG, game_secret_key="", servers_info=self._caches) elif self._request.update_option == ServerListUpdateOption.P2P_GROUP_ROOM_LIST: - assert isinstance(self.data, PeerRoomInfo) + assert isinstance(self._caches, list) and all( + isinstance(item, PeerRoomInfo) for item in self._caches) + if TYPE_CHECKING: + self._caches = cast(list[PeerRoomInfo], self._caches) self._result = P2PGroupRoomListResult( - client_remote_ip=self._request.client_ip, flag=GameServerFlags.HAS_KEYS_FLAG, game_secret_key="", peer_room_info=self.data) + client_remote_ip=self._request.client_ip, flag=GameServerFlags.HAS_KEYS_FLAG, game_secret_key="", peer_room_info=self._caches) else: raise ServerBrowserException( "invalid server browser update option") From 5cb094c51c363719b1595c304bc9e7f717631c35 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 25 Jan 2025 03:38:07 +0000 Subject: [PATCH 157/231] fix: new user contract params --- .../gamespy/presence_search_player/requests.py | 18 +++++++----------- .../src/contracts/requests.py | 13 +++---------- 2 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/backends/protocols/gamespy/presence_search_player/requests.py b/src/backends/protocols/gamespy/presence_search_player/requests.py index 4580259de..0e6df9e8e 100644 --- a/src/backends/protocols/gamespy/presence_search_player/requests.py +++ b/src/backends/protocols/gamespy/presence_search_player/requests.py @@ -1,3 +1,4 @@ +from typing import Optional from backends.library.abstractions.contracts import RequestBase as RB from servers.presence_search_player.src.aggregates.enums import SearchType @@ -18,21 +19,16 @@ class CheckRequest(RequestBase): class NewUserRequest(RequestBase): - product_id: int - game_port: int - cd_key: str - has_game_name: bool - has_product_id: bool - has_cdkey: bool - has_partner_id: bool - has_game_port: bool nick: str email: str password: str - partner_id: int - game_name: str uniquenick: str namespace_id: int + product_id: int + game_port: Optional[int] = None + cd_key: Optional[str] = None + partner_id: Optional[int] = None + game_name: Optional[str] = None class NicksRequest(RequestBase): @@ -57,7 +53,7 @@ class SearchRequest(RequestBase): skip_num: int request_type: SearchType game_name: str - profile_id: int| None = None + profile_id: int | None = None partner_id: int email: str | None = None nick: str | None = None diff --git a/src/servers/presence_search_player/src/contracts/requests.py b/src/servers/presence_search_player/src/contracts/requests.py index 0716369c4..2074f9f10 100644 --- a/src/servers/presence_search_player/src/contracts/requests.py +++ b/src/servers/presence_search_player/src/contracts/requests.py @@ -40,11 +40,6 @@ class NewUserRequest(RequestBase): product_id: int game_port: int cd_key: str - has_game_name: bool - has_product_id: bool - has_cdkey: bool - has_partner_id: bool - has_game_port: bool nick: str email: str password: str @@ -79,7 +74,6 @@ def parse_other_info(self): if "partnerid" in self.request_dict: try: self.partner_id = int(self.request_dict["partnerid"]) - self.has_partner_id_flag = True except ValueError: raise GPParseException("partnerid is incorrect.") else: @@ -87,23 +81,22 @@ def parse_other_info(self): if "productid" in self.request_dict: try: self.product_id = int(self.request_dict["productid"]) - self.has_product_id_flag = True except ValueError: raise GPParseException("productid is incorrect.") + else: + # we give default product id here + self.product_id = 0 if "gamename" in self.request_dict: - self.has_game_name_flag = True self.game_name = self.request_dict["gamename"] if "port" in self.request_dict: try: self.game_port = int(self.request_dict["port"]) - self.has_game_port_flag = True except ValueError: raise GPParseException("port is incorrect.") if "cdkey" in self.request_dict: - self.has_cd_key_enc_flag = True self.cd_key = self.request_dict["cdkey"] From fb5f0d8ec6189558fd5ad624fe736eafb26dd7f4 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sun, 26 Jan 2025 14:47:17 +0000 Subject: [PATCH 158/231] added handlers to routers --- .../library/abstractions/contracts.py | 20 ++++++- .../library/abstractions/handler_base.py | 45 +++++++++------ .../protocols/gamespy/natneg/handlers.py | 14 +++-- .../presence_connection_manager/handlers.py | 8 +++ .../gamespy/presence_search_player/data.py | 10 ++-- .../protocols/gamespy/query_report/data.py | 7 ++- src/backends/routers/gamespy/chat.py | 3 +- src/backends/routers/gamespy/gstats.py | 25 ++++++--- src/backends/routers/gamespy/natneg.py | 3 +- .../gamespy/presence_connection_manager.py | 55 +++++++++++++------ .../routers/gamespy/presence_search_player.py | 21 +++++-- src/backends/routers/gamespy/query_report.py | 7 +-- .../routers/gamespy/server_browser.py | 3 +- src/backends/routers/gamespy/webservices.py | 3 +- .../gamespy/query_report/handler_test.py | 2 +- 15 files changed, 152 insertions(+), 74 deletions(-) diff --git a/src/backends/library/abstractions/contracts.py b/src/backends/library/abstractions/contracts.py index 4f1fabd08..e1e62a867 100644 --- a/src/backends/library/abstractions/contracts.py +++ b/src/backends/library/abstractions/contracts.py @@ -1,3 +1,4 @@ +from typing import Literal from pydantic import BaseModel, UUID4 @@ -15,5 +16,22 @@ class RequestBase(BaseModel): client_port: int -class ErrorResponse(BaseModel): +class Response(BaseModel): message: str + + def to_json_dict(self) -> dict: + return self.model_dump(mode="json") + + +class OKResponse(Response): + message: str = "ok" + + +class DataResponse(OKResponse): + result: object + pass + + +class ErrorResponse(Response): + # code: int + pass diff --git a/src/backends/library/abstractions/handler_base.py b/src/backends/library/abstractions/handler_base.py index 33a2897b8..8fb7c3b24 100644 --- a/src/backends/library/abstractions/handler_base.py +++ b/src/backends/library/abstractions/handler_base.py @@ -1,8 +1,5 @@ -from abc import abstractmethod, ABC from typing import Optional, final - -from pydantic import BaseModel -from backends.library.abstractions.contracts import ErrorResponse, RequestBase +from backends.library.abstractions.contracts import DataResponse, ErrorResponse, OKResponse, RequestBase, Response from backends.library.database.pg_orm import PG_SESSION from library.src.abstractions.contracts import ResultBase @@ -11,20 +8,22 @@ from library.src.exceptions.general import UniSpyException -class OkResponse(BaseModel): - message: str = "ok" - - class HandlerBase: """ The ultimate handler base of backend service """ _request: RequestBase _result: Optional[ResultBase] - response: Optional[dict] + _response: Response """ - the dict response which send to client + the response using to wrap data """ + @property + def response(self) -> dict: + """ + the dict response which send to client + """ + return self._response.to_json_dict() def __init__(self, request: RequestBase) -> None: assert issubclass(type(request), RequestBase) @@ -32,7 +31,7 @@ def __init__(self, request: RequestBase) -> None: # decoupling the logging in home.py self.logger = logging.getLogger("backend") self._result = None - self.response = None + self._response = OKResponse() async def handle(self) -> None: try: @@ -42,23 +41,35 @@ async def handle(self) -> None: await self._response_construct() except UniSpyException as ex: self.logger.error(ex.message) - self.response = ErrorResponse(message=ex.message).model_dump() + self._response = ErrorResponse(message=ex.message) except Exception as ex: self.logger.error(ex) - self.response = ErrorResponse(message=str(ex)).model_dump() + self._response = ErrorResponse(message=str(ex)) async def _request_check(self) -> None: """virtual method""" async def _data_operate(self) -> None: - """virtual method""" + """virtual method\n + override by child class to perform database operations + """ async def _result_construct(self) -> None: - """virtual method""" + """virtual method\n + can override by child class to create self._result + """ @final async def _response_construct(self) -> None: + """ + _response_construct can not be overrided + """ # if there are no result, we send ok response if self._result is None: - self.response = OkResponse().model_dump() + self._response = OKResponse() + self.logger.info( + f"[{self.__class__.__name__}] use default OKResponse") else: - self.response = self._result.model_dump() + self._response = DataResponse( + result=self._result.model_dump(mode="json")) + self.logger.info( + f"[{self.__class__.__name__}] use default DataResponse") diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py index 673b37207..63ad6a0b1 100644 --- a/src/backends/protocols/gamespy/natneg/handlers.py +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -1,17 +1,21 @@ +from backends.library.abstractions.contracts import OKResponse, RequestBase from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import InitPacketCaches from backends.protocols.gamespy.natneg.data import update_init_info -from backends.protocols.gamespy.natneg.requests import InitRequest +from backends.protocols.gamespy.natneg.requests import ConnectRequest, InitRequest class InitHandler(HandlerBase): def __init__(self, request: InitRequest) -> None: - super().__init__(request) assert isinstance(request, InitRequest) + super().__init__(request) async def _data_operate(self) -> None: - info = InitPacketCaches(**self._request.model_dump()) + info = InitPacketCaches(**self._request.model_dump(mode="json")) update_init_info(info) - async def _result_construct(self) -> None: - self.response = {"message": "ok"} + +class ConnectHandler(HandlerBase): + def __init__(self, request: RequestBase) -> None: + super().__init__(request) + assert isinstance(request, ConnectRequest) diff --git a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py index 2e15bd963..aef65fd95 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py @@ -164,8 +164,16 @@ async def _result_construct(self) -> None: self._result = StatusResult(status=self.data) +class StatusInfoHandler(HandlerBase): + _request: StatusInfoRequest + + async def _data_operate(self) -> None: + raise NotImplementedError() + + # region Profile + class GetProfileHandler(HandlerBase): _request: GetProfileRequest diff --git a/src/backends/protocols/gamespy/presence_search_player/data.py b/src/backends/protocols/gamespy/presence_search_player/data.py index 2acc96e7d..134f69a5c 100644 --- a/src/backends/protocols/gamespy/presence_search_player/data.py +++ b/src/backends/protocols/gamespy/presence_search_player/data.py @@ -1,5 +1,5 @@ from typing import TYPE_CHECKING, Optional, cast -from sqlalchemy import Column +from sqlalchemy import Column, and_ from backends.library.database.pg_orm import ( Friends, Profiles, @@ -146,12 +146,12 @@ def get_friend_info_list(profile_id: int, namespace_id: int, game_name: str) -> ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .join(Friends, Profiles.profileid == Friends.friendid) + # todo check whether friends table join is correct .where( - Profiles.profileid.in_(PG_SESSION.query( - Friends.profileid == profile_id)), + Friends.profileid == profile_id, SubProfiles.namespaceid == namespace_id, - SubProfiles.gamename == game_name, - ) + SubProfiles.gamename == game_name,) .all() ) return result diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index a3e2ff519..8560a1dcc 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -68,7 +68,8 @@ def get_peer_group_channel(group_data: list[dict]) -> list[PeerRoomInfo]: assert isinstance(group_data, list) and all( isinstance(id, dict) for id in group_data) # Construct the group names based on the provided group_ids - group_names = [f"{PeerRoom.GroupRoomPrefix}!{item['group_id']}" for item in group_data] + group_names = [f"{PeerRoom.GroupRoomPrefix}!{ + item['group_id']}" for item in group_data] # Query the database for channels matching the constructed group names result = PG_SESSION.query(ChatChannelCaches).filter( @@ -77,11 +78,11 @@ def get_peer_group_channel(group_data: list[dict]) -> list[PeerRoomInfo]: # Convert the result to a list of PeerRoomInfo objects data = [PeerRoomInfo(**s) for s in result] - + return data -def get_server_info_with_instant_key(instant_key: str) -> Optional[GameServerInfo]: +def get_server_info_with_instant_key(instant_key: str) -> Optional[GameServerCaches]: assert isinstance(instant_key, str) result = PG_SESSION.query(GameServerCaches).where( GameServerCaches.instant_key == instant_key).first() diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 29e16b63a..b2823c1e3 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -213,7 +213,6 @@ def utm(request: UTMRequest): if __name__ == "__main__": import uvicorn from fastapi import FastAPI - app = FastAPI() app.include_router(router) - uvicorn.run(router, host="0.0.0.0", port=8080) + uvicorn.run(app, host="0.0.0.0", port=8080) diff --git a/src/backends/routers/gamespy/gstats.py b/src/backends/routers/gamespy/gstats.py index 988c1239d..20c3d2f35 100644 --- a/src/backends/routers/gamespy/gstats.py +++ b/src/backends/routers/gamespy/gstats.py @@ -9,38 +9,47 @@ async def auth_game(request: AuthGameRequest): handler = AuthGameHandler(request) await handler.handle() - return handler.response + return handler._response @router.post(f"{GAMESTATUS}/AuthPlayerHandler") async def auth_player(request: AuthPlayerRequest): - raise NotImplementedError() + handler = AuthPlayerHandler(request) + await handler.handle() + return handler._response @router.post(f"{GAMESTATUS}/NewGameHandler") async def new_game(request: NewGameRequest): - raise NotImplementedError() + handler = NewGameHandler(request) + await handler.handle() + return handler._response @router.post(f"{GAMESTATUS}/GetPlayerDataHandler") async def get_player_data(request: GetPlayerDataRequest): - raise NotImplementedError() + handler = GetPlayerDataHandler(request) + await handler.handle() + return handler._response @router.post(f"{GAMESTATUS}/SetPlayerDataHandler") async def set_player_data(request: SetPlayerDataRequest): - raise NotImplementedError() + handler = SetPlayerDataHandler(request) + await handler.handle() + return handler._response @router.post(f"{GAMESTATUS}/UpdateGameHandler") async def updaet_game(request: UpdateGameRequest): - raise NotImplementedError() + handler = UpdateGameHandler(request) + await handler.handle() + return handler._response if __name__ == "__main__": import uvicorn from fastapi import FastAPI - app = FastAPI() app.include_router(router) - uvicorn.run(router, host="0.0.0.0", port=8000) + uvicorn.run(app, host="0.0.0.0", port=8080) diff --git a/src/backends/routers/gamespy/natneg.py b/src/backends/routers/gamespy/natneg.py index 77ef3e7a0..182a7b7f4 100644 --- a/src/backends/routers/gamespy/natneg.py +++ b/src/backends/routers/gamespy/natneg.py @@ -44,7 +44,6 @@ async def report(request: ReportRequest): if __name__ == "__main__": import uvicorn from fastapi import FastAPI - app = FastAPI() app.include_router(router) - uvicorn.run(router, host="0.0.0.0", port=8000) + uvicorn.run(app, host="0.0.0.0", port=8080) diff --git a/src/backends/routers/gamespy/presence_connection_manager.py b/src/backends/routers/gamespy/presence_connection_manager.py index 2dfc0a550..3681b39cc 100644 --- a/src/backends/routers/gamespy/presence_connection_manager.py +++ b/src/backends/routers/gamespy/presence_connection_manager.py @@ -1,8 +1,9 @@ from fastapi import APIRouter from backends.protocols.gamespy.chat.requests import RegisterNickRequest +from backends.protocols.gamespy.presence_connection_manager.handlers import AddBlockHandler, GetProfileHandler, KeepAliveHandler, LoginHandler, LogoutHandler, NewProfileHandler, NewUserHandler, RegisterCDKeyHandler, RegisterNickHandler, StatusHandler, StatusInfoHandler, UpdateProfileHandler from backends.protocols.gamespy.presence_connection_manager.requests import GetProfileRequest, LoginRequest, LogoutRequest, NewProfileRequest, RegisterCDKeyRequest, StatusInfoRequest, StatusRequest, UpdateProfileRequest, KeepAliveRequest, NewUserRequest, AddBlockRequest -from backends.urls import * +from backends.urls import PRESENCE_CONNECTION_MANAGER router = APIRouter() @@ -10,68 +11,90 @@ @router.post(f"{PRESENCE_CONNECTION_MANAGER}/LoginHandler") async def login(request: LoginRequest): - raise NotImplementedError() + handler = LoginHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/LogoutHandler") async def logout(request: LogoutRequest): - raise NotImplementedError() + handler = LogoutHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/KeepAliveHandler") async def keep_alive(request: KeepAliveRequest): - raise NotImplementedError() + handler = KeepAliveHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/NewUserHandler") async def new_user(request: NewUserRequest): - raise NotImplementedError() + handler = NewUserHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/AddBlockHandler") async def add_block(request: AddBlockRequest): - raise NotImplementedError() + handler = AddBlockHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/GetProfileHandler") async def get_profile(request: GetProfileRequest): - raise NotImplementedError() + handler = GetProfileHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/NewProfileHandler") async def new_proflie(request: NewProfileRequest): - raise NotImplementedError() + handler = NewProfileHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/RegisterCDKeyHandler") async def register_cdkey(request: RegisterCDKeyRequest): - raise NotImplementedError() + handler = RegisterCDKeyHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/RegisterNickHandler") async def register_nick(request: RegisterNickRequest): - raise NotImplementedError() + handler = RegisterNickHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/UpdateProfileHandler") async def update_profile(request: UpdateProfileRequest): - raise NotImplementedError() + handler = UpdateProfileHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/StatusHandler") async def status(request: StatusRequest): - raise NotImplementedError() + handler = StatusHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/StatusInfoHandler") async def status_info(request: StatusInfoRequest): - raise NotImplementedError() - + handler = StatusInfoHandler(request) + await handler.handle() + return handler.response if __name__ == "__main__": import uvicorn from fastapi import FastAPI - app = FastAPI() app.include_router(router) - uvicorn.run(router, host="0.0.0.0", port=8000) + uvicorn.run(app, host="0.0.0.0", port=8080) diff --git a/src/backends/routers/gamespy/presence_search_player.py b/src/backends/routers/gamespy/presence_search_player.py index 29ccb246f..6ded1bc14 100644 --- a/src/backends/routers/gamespy/presence_search_player.py +++ b/src/backends/routers/gamespy/presence_search_player.py @@ -1,5 +1,7 @@ from fastapi import APIRouter +from backends.library.abstractions.contracts import Response +from backends.protocols.gamespy.presence_search_player.handlers import CheckHandler, NewUserHandler, NicksHandler, OthersHandler from backends.protocols.gamespy.presence_search_player.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest from backends.urls import PRESENCE_SEARCH_PLAYER @@ -8,22 +10,30 @@ @router.post(f"{PRESENCE_SEARCH_PLAYER}/CheckHandler") async def check(request: CheckRequest): - raise NotImplementedError() + handler = CheckHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_SEARCH_PLAYER}/NewUserHandler") async def new_user(request: NewUserRequest): - raise NotImplementedError() + handler = NewUserHandler(request) + await handler.handle() + handler.response @router.post(f"{PRESENCE_SEARCH_PLAYER}/NicksHandler") async def nicks(request: NicksRequest): - raise NotImplementedError() + handler = NicksHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_SEARCH_PLAYER}/OthersHandler") async def others(request: OthersRequest): - raise NotImplementedError() + handler = OthersHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_SEARCH_PLAYER}/OthersListHandler") @@ -59,7 +69,6 @@ async def valid(request: ValidRequest): if __name__ == "__main__": import uvicorn from fastapi import FastAPI - app = FastAPI() app.include_router(router) - uvicorn.run(router, host="0.0.0.0", port=8000) \ No newline at end of file + uvicorn.run(app, host="0.0.0.0", port=8080) diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index ada05a400..9635639cf 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -12,7 +12,7 @@ async def heartbeat(request: HeartBeatRequest): handler = Heartbeathandler(request) await handler.handle() - return handler.response + return handler._response @router.post(f"{QUERY_REPORT}/ChallengeHanler") @@ -24,7 +24,7 @@ async def challenge(request: ChallengeRequest): async def available(request: AvaliableRequest): handler = AvaliableHandler(request) await handler.handle() - return handler.response + return handler._response @router.post(f"{QUERY_REPORT}/ClientMessageAckHandler") @@ -45,7 +45,6 @@ async def keep_alive(request: KeepAliveRequest): if __name__ == "__main__": import uvicorn from fastapi import FastAPI - app = FastAPI() app.include_router(router) - uvicorn.run(router, host="0.0.0.0", port=8000) + uvicorn.run(app, host="0.0.0.0", port=8080) diff --git a/src/backends/routers/gamespy/server_browser.py b/src/backends/routers/gamespy/server_browser.py index a3a6c2041..5db83813b 100644 --- a/src/backends/routers/gamespy/server_browser.py +++ b/src/backends/routers/gamespy/server_browser.py @@ -42,7 +42,6 @@ async def server_list(request: ServerListRequest): if __name__ == "__main__": import uvicorn from fastapi import FastAPI - app = FastAPI() app.include_router(router) - uvicorn.run(router, host="0.0.0.0", port=8000) + uvicorn.run(app, host="0.0.0.0", port=8080) diff --git a/src/backends/routers/gamespy/webservices.py b/src/backends/routers/gamespy/webservices.py index 51305a6b4..e0ab2985a 100644 --- a/src/backends/routers/gamespy/webservices.py +++ b/src/backends/routers/gamespy/webservices.py @@ -86,7 +86,6 @@ async def update_record(request): if __name__ == "__main__": import uvicorn from fastapi import FastAPI - app = FastAPI() app.include_router(router) - uvicorn.run(router, host="0.0.0.0", port=8000) + uvicorn.run(app, host="0.0.0.0", port=8080) diff --git a/src/backends/tests/gamespy/query_report/handler_test.py b/src/backends/tests/gamespy/query_report/handler_test.py index 79af9f2cf..3f5ca569b 100644 --- a/src/backends/tests/gamespy/query_report/handler_test.py +++ b/src/backends/tests/gamespy/query_report/handler_test.py @@ -11,4 +11,4 @@ async def test_heartbeat(self): req = HeartBeatRequest(**request) handler = Heartbeathandler(req) await handler.handle() - handler.response + handler._response From 158839aaa913146dc8d77771b11d2c4d2d942759 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 5 Feb 2025 13:37:27 +0000 Subject: [PATCH 159/231] orgnized files --- common/UniSpy_pg.sql | 27 ++-- src/.vscode/settings.json | 3 + .../library/abstractions/handler_base.py | 8 +- src/backends/library/database/pg_orm.py | 21 +-- src/backends/library/database/redis.py | 2 +- .../protocols/gamespy/chat/channel.py | 22 +-- .../protocols/gamespy/chat/channel_user.py | 4 +- src/backends/protocols/gamespy/chat/data.py | 150 ++++++++++++------ .../protocols/gamespy/chat/handlers.py | 135 ++++++++++++++-- .../protocols/gamespy/chat/managers.py | 2 +- .../protocols/gamespy/chat/requests.py | 6 +- .../protocols/gamespy/game_status/data.py | 4 +- .../protocols/gamespy/game_status/handlers.py | 4 +- .../protocols/gamespy/game_status/requests.py | 2 +- src/backends/protocols/gamespy/natneg/data.py | 2 +- .../protocols/gamespy/natneg/requests.py | 2 +- .../presence_connection_manager/data.py | 8 +- .../presence_connection_manager/handlers.py | 4 +- .../presence_connection_manager/requests.py | 4 +- .../gamespy/presence_search_player/data.py | 101 ++++++------ .../presence_search_player/handlers.py | 32 ++-- .../presence_search_player/requests.py | 2 +- .../protocols/gamespy/query_report/data.py | 8 +- .../gamespy/query_report/handlers.py | 2 +- .../gamespy/query_report/requests.py | 2 +- .../gamespy/server_browser/handlers.py | 10 +- .../gamespy/server_browser/requests.py | 2 +- .../protocols/gamespy/web_services/data.py | 6 +- .../gamespy/web_services/handlers.py | 4 +- src/backends/routers/gamespy/chat.py | 80 ++++++---- src/backends/routers/gamespy/natneg.py | 8 +- .../routers/gamespy/presence_search_player.py | 22 ++- .../routers/gamespy/server_browser.py | 9 +- src/backends/routers/gamespy/webservices.py | 26 ++- src/backends/routers/home.py | 4 +- src/backends/tests/gamespy/chat/room_tests.py | 8 +- .../precence_search_player/requests_tests.py | 4 +- .../gamespy/query_report/data_fetch_tests.py | 8 +- src/{library => frontends}/__init__.py | 0 .../gamespy/library/abstractions}/__init__.py | 0 .../gamespy/library}/abstractions/brocker.py | 0 .../gamespy/library}/abstractions/client.py | 26 +-- .../library}/abstractions/connections.py | 8 +- .../library}/abstractions/contracts.py | 0 .../library}/abstractions/enctypt_base.py | 0 .../gamespy/library}/abstractions/handler.py | 10 +- .../library}/abstractions/redis_objects.py | 2 +- .../library}/abstractions/server_launcher.py | 8 +- .../gamespy/library}/abstractions/switcher.py | 6 +- .../gamespy/library}/configs.py | 0 .../gamespy/library/encryption}/__init__.py | 0 .../gamespy/library}/encryption/encoding.py | 0 .../library}/encryption/gs_encryption.py | 2 +- .../library}/encryption/xor_encryption.py | 2 +- .../gamespy/library/exceptions}/__init__.py | 0 .../gamespy/library}/exceptions/general.py | 4 +- .../gamespy/library/extentions}/__init__.py | 0 .../library}/extentions/bytes_extentions.py | 0 .../gamespy/library}/extentions/encoding.py | 0 .../library}/extentions/gamespy_ramdoms.py | 0 .../library}/extentions/gamespy_utils.py | 0 .../library}/extentions/password_encoder.py | 2 +- .../gamespy/library}/extentions/redis_orm.py | 0 .../library}/extentions/string_extentions.py | 0 .../gamespy/library/log}/__init__.py | 0 .../gamespy/library}/log/log_manager.py | 2 +- .../gamespy/library}/network/__init__.py | 0 .../gamespy/library}/network/brockers.py | 4 +- .../gamespy/library}/network/http_handler.py | 8 +- .../gamespy/library}/network/tcp_handler.py | 10 +- .../gamespy/library}/network/udp_handler.py | 8 +- .../gamespy/protocols}/__init__.py | 0 .../gamespy/protocols/chat}/__init__.py | 0 .../protocols/chat}/abstractions/contract.py | 8 +- .../protocols/chat}/abstractions/handler.py | 28 ++-- .../protocols/chat}/aggregates/enums.py | 0 .../protocols/chat}/aggregates/exceptions.py | 6 +- .../protocols/chat}/aggregates/peer_room.py | 2 +- .../chat}/aggregates/response_name.py | 0 .../protocols/chat}/applications/client.py | 12 +- .../protocols/chat}/applications/handlers.py | 16 +- .../chat}/applications/server_launcher.py | 8 +- .../protocols/chat}/applications/switcher.py | 12 +- .../protocols/chat}/contracts/requests.py | 16 +- .../protocols/chat}/contracts/responses.py | 14 +- .../protocols/chat}/contracts/results.py | 7 +- .../protocols/game_status}/__init__.py | 0 .../game_status}/abstractions/contracts.py | 12 +- .../game_status/abstractions/handlers.py | 17 ++ .../game_status}/aggregations/enums.py | 0 .../game_status/aggregations/exceptions.py | 5 + .../game_status}/aggregations/gscrypt.py | 6 +- .../game_status}/applications/client.py | 14 +- .../game_status}/applications/handlers.py | 10 +- .../applications/server_launcher.py | 8 +- .../game_status}/applications/switcher.py | 12 +- .../game_status}/contracts/requests.py | 8 +- .../game_status}/contracts/responses.py | 6 +- .../game_status}/contracts/results.py | 2 +- .../protocols/game_traffic_relay}/__init__.py | 0 .../applications/connection_listener.py | 2 +- .../applications/router.py | 2 +- .../game_traffic_relay}/contracts/general.py | 2 +- .../gamespy/protocols/natneg}/__init__.py | 0 .../natneg}/abstractions/contracts.py | 12 +- .../natneg}/abstractions/handlers.py | 9 +- .../protocols/natneg}/aggregations/enums.py | 0 .../natneg}/aggregations/natneg_cookie.py | 0 .../protocols/natneg}/applications/client.py | 10 +- .../natneg}/applications/handlers.py | 10 +- .../natneg}/applications/server_launcher.py | 8 +- .../natneg}/applications/switcher.py | 12 +- .../protocols/natneg}/contracts/requests.py | 6 +- .../protocols/natneg}/contracts/responses.py | 6 +- .../protocols/natneg}/contracts/results.py | 4 +- .../presence_connection_manager}/__init__.py | 0 .../abstractions/contracts.py | 14 +- .../abstractions/handlers.py | 12 +- .../aggregates/enums.py | 0 .../aggregates/login_challenge.py | 2 +- .../aggregates/sdk_revision.py | 2 +- .../aggregates/user_status.py | 2 +- .../applications/client.py | 20 +-- .../applications/handlers.py | 18 +-- .../applications/server_launcher.py | 8 +- .../applications/switcher.py | 14 +- .../contracts/requests.py | 24 +-- .../contracts/responses.py | 24 +-- .../contracts/results.py | 6 +- .../presence_search_player}/__init__.py | 0 .../abstractions/contracts.py | 12 +- .../abstractions/handler.py | 8 +- .../aggregates/enums.py | 0 .../aggregates/exceptions.py | 6 +- .../applications/client.py | 18 +++ .../applications/handlers.py | 10 +- .../applications/server_launcher.py | 8 +- .../applications/switcher.py | 12 +- .../contracts/requests.py | 10 +- .../contracts/responses.py | 8 +- .../contracts/results.py | 4 +- .../protocols/query_report}/__init__.py | 0 .../query_report/aggregates/exceptions.py | 5 + .../aggregates/game_server_info.py | 4 +- .../aggregates/natneg_channel.py | 14 +- .../aggregates/peer_room_info.py | 0 .../query_report}/applications/client.py | 4 +- .../applications/server_launcher.py | 8 +- .../protocols/query_report/v1}/__init__.py | 0 .../query_report/v1/abstractions}/__init__.py | 0 .../v1/abstractions/contracts.py | 10 +- .../query_report/v1/abstractions/handlers.py | 10 ++ .../v2/abstractions/cmd_handler_base.py | 10 ++ .../v2/abstractions/contracts.py | 14 +- .../query_report}/v2/aggregates/enums.py | 0 .../query_report}/v2/applications/handlers.py | 10 +- .../query_report}/v2/applications/switcher.py | 14 +- .../query_report}/v2/contracts/requests.py | 8 +- .../query_report}/v2/contracts/responses.py | 12 +- .../query_report}/v2/contracts/results.py | 4 +- .../protocols/server_browser}/__init__.py | 0 .../server_browser}/aggregates/exceptions.py | 2 +- .../v2/abstractions}/__init__.py | 0 .../v2/abstractions/contracts.py | 23 ++- .../v2/abstractions/handlers.py | 10 +- .../v2/aggregations/encryption.py | 2 +- .../server_browser}/v2/aggregations/enums.py | 0 .../v2/aggregations/server_info_builder.py | 4 +- .../v2/aggregations/string_flags.py | 0 .../server_browser/v2/applications/client.py | 23 +++ .../v2/applications/handlers.py | 22 +-- .../v2/applications/server_launcher.py | 8 +- .../v2/applications/switcher.py | 14 +- .../server_browser/v2/contracts}/__init__.py | 0 .../server_browser}/v2/contracts/requests.py | 4 +- .../server_browser}/v2/contracts/responses.py | 14 +- .../server_browser}/v2/contracts/results.py | 8 +- .../protocols/web_services}/__init__.py | 0 .../web_services}/abstractions/contracts.py | 6 +- .../web_services}/abstractions/handler.py | 6 +- .../web_services/aggregations/exceptions.py | 6 + .../aggregations/soap_envelop.py | 2 +- .../web_services}/applications/client.py | 12 +- .../applications/server_launcher.py | 8 +- .../web_services}/applications/switcher.py | 22 +-- .../web_services}/modules/altas/___init__.py | 0 .../modules/auth/abstractions/general.py | 8 +- .../modules/auth/contracts/requests.py | 4 +- .../modules/auth/contracts/responses.py | 6 +- .../modules/auth/contracts/results.py | 2 +- .../modules/auth/exceptions/general.py | 5 + .../modules/auth/handlers/general.py | 10 +- .../direct2game/abstractions/contracts.py | 4 +- .../direct2game/abstractions/handler.py | 0 .../modules/direct2game/contracts/requests.py | 4 +- .../direct2game/contracts/responses.py | 4 +- .../modules/direct2game/contracts/results.py | 2 +- .../modules/direct2game/handlers/general.py | 12 +- .../modules/sake/abstractions/generals.py | 8 +- .../modules/sake/applications/handlers.py | 8 +- .../modules/sake/contracts/requests.py | 6 +- .../modules/sake/contracts/responses.py | 6 +- .../modules/sake/contracts/results.py | 2 +- .../modules/sake/exceptions/general.py | 5 + .../src => frontends/tests}/__init__.py | 0 .../tests/gamespy}/__init__.py | 0 .../tests/gamespy/chat}/__init__.py | 0 .../tests/gamespy/chat}/game_tests.py | 2 +- .../tests/gamespy/chat}/mock_objects.py | 8 +- .../tests/gamespy/chat}/request_tests.py | 4 +- .../tests/gamespy/game_status}/__init__.py | 0 .../tests/gamespy/game_status}/game_tests.py | 2 +- .../gamespy/game_status}/handler_tests.py | 13 +- .../gamespy/game_status}/mock_objects.py | 10 +- .../tests/gamespy/library}/__init__.py | 0 .../tests/gamespy/library}/encrypt_tests.py | 6 +- .../tests/gamespy/library}/mock_objects.py | 10 +- .../tests/gamespy/natneg}/__init__.py | 0 .../tests/gamespy/natneg}/handler_tests.py | 17 +- .../tests/gamespy/natneg}/mock_objects.py | 8 +- .../tests/gamespy/natneg}/redis.py | 0 .../presence_connection_manager}/__init__.py | 0 .../buddy_request_tests.py | 2 +- .../game_tests.py | 6 +- .../general_request_tests.py | 4 +- .../mock_objects.py | 8 +- .../profile_request_tests.py | 2 +- .../presence_search_player}/__init__.py | 0 .../presence_search_player}/game_tests.py | 10 +- .../presence_search_player}/handler_tests.py | 7 +- .../presence_search_player}/mock_objects.py | 10 +- .../tests/gamespy/query_report}/__init__.py | 0 .../tests/gamespy/query_report}/game_tests.py | 2 +- .../gamespy/query_report}/mock_objects.py | 10 +- .../gamespy/query_report}/request_tests.py | 4 +- .../tests/gamespy/server_browser}/__init__.py | 0 .../gamespy/server_browser}/game_tests.py | 4 +- .../gamespy/server_browser}/mock_objects.py | 10 +- .../tests/gamespy/web_services}/__init__.py | 0 .../gamespy/web_services}/altas_tests.py | 0 .../tests/gamespy/web_services}/auth_tests.py | 2 +- .../gamespy/web_services}/racing_tests.py | 0 .../tests/gamespy/web_services}/sake_tests.py | 2 +- .../game_status/src/abstractions/handlers.py | 17 -- .../src/aggregations/exceptions.py | 5 - .../src/applications/client.py | 18 --- .../query_report/src/aggregates/exceptions.py | 5 - .../src/v1/abstractions/handlers.py | 10 -- .../src/v2/abstractions/cmd_handler_base.py | 10 -- src/servers/server_browser/src/__init__.py | 0 .../src/v2/abstractions/__init__.py | 0 .../src/v2/applications/client.py | 23 --- .../src/v2/contracts/__init__.py | 0 src/servers/server_browser/tests/__init__.py | 0 src/servers/web_services/__init__.py | 0 src/servers/web_services/src/__init__.py | 0 .../src/aggregations/exceptions.py | 6 - .../src/modules/auth/exceptions/general.py | 5 - .../src/modules/sake/exceptions/general.py | 5 - src/servers/web_services/tests/__init__.py | 0 260 files changed, 1168 insertions(+), 922 deletions(-) rename src/{library => frontends}/__init__.py (100%) rename src/{library/src => frontends/gamespy/library/abstractions}/__init__.py (100%) rename src/{library/src => frontends/gamespy/library}/abstractions/brocker.py (100%) rename src/{library/src => frontends/gamespy/library}/abstractions/client.py (81%) rename src/{library/src => frontends/gamespy/library}/abstractions/connections.py (90%) rename src/{library/src => frontends/gamespy/library}/abstractions/contracts.py (100%) rename src/{library/src => frontends/gamespy/library}/abstractions/enctypt_base.py (100%) rename src/{library/src => frontends/gamespy/library}/abstractions/handler.py (92%) rename src/{library/src => frontends/gamespy/library}/abstractions/redis_objects.py (99%) rename src/{library/src => frontends/gamespy/library}/abstractions/server_launcher.py (90%) rename src/{library/src => frontends/gamespy/library}/abstractions/switcher.py (89%) rename src/{library/src => frontends/gamespy/library}/configs.py (100%) rename src/{library/src/abstractions => frontends/gamespy/library/encryption}/__init__.py (100%) rename src/{library/src => frontends/gamespy/library}/encryption/encoding.py (100%) rename src/{library/src => frontends/gamespy/library}/encryption/gs_encryption.py (97%) rename src/{library/src => frontends/gamespy/library}/encryption/xor_encryption.py (94%) rename src/{library/src/encryption => frontends/gamespy/library/exceptions}/__init__.py (100%) rename src/{library/src => frontends/gamespy/library}/exceptions/general.py (89%) rename src/{library/src/exceptions => frontends/gamespy/library/extentions}/__init__.py (100%) rename src/{library/src => frontends/gamespy/library}/extentions/bytes_extentions.py (100%) rename src/{library/src => frontends/gamespy/library}/extentions/encoding.py (100%) rename src/{library/src => frontends/gamespy/library}/extentions/gamespy_ramdoms.py (100%) rename src/{library/src => frontends/gamespy/library}/extentions/gamespy_utils.py (100%) rename src/{library/src => frontends/gamespy/library}/extentions/password_encoder.py (96%) rename src/{library/src => frontends/gamespy/library}/extentions/redis_orm.py (100%) rename src/{library/src => frontends/gamespy/library}/extentions/string_extentions.py (100%) rename src/{library/src/extentions => frontends/gamespy/library/log}/__init__.py (100%) rename src/{library/src => frontends/gamespy/library}/log/log_manager.py (97%) rename src/{library/src => frontends/gamespy/library}/network/__init__.py (100%) rename src/{library/src => frontends/gamespy/library}/network/brockers.py (94%) rename src/{library/src => frontends/gamespy/library}/network/http_handler.py (89%) rename src/{library/src => frontends/gamespy/library}/network/tcp_handler.py (85%) rename src/{library/src => frontends/gamespy/library}/network/udp_handler.py (84%) rename src/{library/src/log => frontends/gamespy/protocols}/__init__.py (100%) rename src/{library/tests => frontends/gamespy/protocols/chat}/__init__.py (100%) rename src/{servers/chat/src => frontends/gamespy/protocols/chat}/abstractions/contract.py (90%) rename src/{servers/chat/src => frontends/gamespy/protocols/chat}/abstractions/handler.py (79%) rename src/{servers/chat/src => frontends/gamespy/protocols/chat}/aggregates/enums.py (100%) rename src/{servers/chat/src => frontends/gamespy/protocols/chat}/aggregates/exceptions.py (94%) rename src/{servers/chat/src => frontends/gamespy/protocols/chat}/aggregates/peer_room.py (96%) rename src/{servers/chat/src => frontends/gamespy/protocols/chat}/aggregates/response_name.py (100%) rename src/{servers/chat/src => frontends/gamespy/protocols/chat}/applications/client.py (66%) rename src/{servers/chat/src => frontends/gamespy/protocols/chat}/applications/handlers.py (95%) rename src/{servers/chat/src => frontends/gamespy/protocols/chat}/applications/server_launcher.py (60%) rename src/{servers/chat/src => frontends/gamespy/protocols/chat}/applications/switcher.py (92%) rename src/{servers/chat/src => frontends/gamespy/protocols/chat}/contracts/requests.py (96%) rename src/{servers/chat/src => frontends/gamespy/protocols/chat}/contracts/responses.py (96%) rename src/{servers/chat/src => frontends/gamespy/protocols/chat}/contracts/results.py (95%) rename src/{servers => frontends/gamespy/protocols/game_status}/__init__.py (100%) rename src/{servers/game_status/src => frontends/gamespy/protocols/game_status}/abstractions/contracts.py (70%) create mode 100644 src/frontends/gamespy/protocols/game_status/abstractions/handlers.py rename src/{servers/game_status/src => frontends/gamespy/protocols/game_status}/aggregations/enums.py (100%) create mode 100644 src/frontends/gamespy/protocols/game_status/aggregations/exceptions.py rename src/{servers/game_status/src => frontends/gamespy/protocols/game_status}/aggregations/gscrypt.py (70%) rename src/{servers/game_status/src => frontends/gamespy/protocols/game_status}/applications/client.py (76%) rename src/{servers/game_status/src => frontends/gamespy/protocols/game_status}/applications/handlers.py (80%) rename src/{servers/game_status/src => frontends/gamespy/protocols/game_status}/applications/server_launcher.py (61%) rename src/{servers/game_status/src => frontends/gamespy/protocols/game_status}/applications/switcher.py (73%) rename src/{servers/game_status/src => frontends/gamespy/protocols/game_status}/contracts/requests.py (95%) rename src/{servers/game_status/src => frontends/gamespy/protocols/game_status}/contracts/responses.py (78%) rename src/{servers/game_status/src => frontends/gamespy/protocols/game_status}/contracts/results.py (77%) rename src/{servers/chat => frontends/gamespy/protocols/game_traffic_relay}/__init__.py (100%) rename src/{servers/game_traffic_relay/src => frontends/gamespy/protocols/game_traffic_relay}/applications/connection_listener.py (86%) rename src/{servers/game_traffic_relay/src => frontends/gamespy/protocols/game_traffic_relay}/applications/router.py (67%) rename src/{servers/game_traffic_relay/src => frontends/gamespy/protocols/game_traffic_relay}/contracts/general.py (76%) rename src/{servers/chat/src => frontends/gamespy/protocols/natneg}/__init__.py (100%) rename src/{servers/natneg/src => frontends/gamespy/protocols/natneg}/abstractions/contracts.py (86%) rename src/{servers/natneg/src => frontends/gamespy/protocols/natneg}/abstractions/handlers.py (52%) rename src/{servers/natneg/src => frontends/gamespy/protocols/natneg}/aggregations/enums.py (100%) rename src/{servers/natneg/src => frontends/gamespy/protocols/natneg}/aggregations/natneg_cookie.py (100%) rename src/{servers/natneg/src => frontends/gamespy/protocols/natneg}/applications/client.py (52%) rename src/{servers/natneg/src => frontends/gamespy/protocols/natneg}/applications/handlers.py (92%) rename src/{servers/natneg/src => frontends/gamespy/protocols/natneg}/applications/server_launcher.py (60%) rename src/{servers/natneg/src => frontends/gamespy/protocols/natneg}/applications/switcher.py (81%) rename src/{servers/natneg/src => frontends/gamespy/protocols/natneg}/contracts/requests.py (91%) rename src/{servers/natneg/src => frontends/gamespy/protocols/natneg}/contracts/responses.py (83%) rename src/{servers/natneg/src => frontends/gamespy/protocols/natneg}/contracts/results.py (85%) rename src/{servers/chat/tests => frontends/gamespy/protocols/presence_connection_manager}/__init__.py (100%) rename src/{servers/presence_connection_manager/src => frontends/gamespy/protocols/presence_connection_manager}/abstractions/contracts.py (73%) rename src/{servers/presence_connection_manager/src => frontends/gamespy/protocols/presence_connection_manager}/abstractions/handlers.py (59%) rename src/{servers/presence_connection_manager/src => frontends/gamespy/protocols/presence_connection_manager}/aggregates/enums.py (100%) rename src/{servers/presence_connection_manager/src => frontends/gamespy/protocols/presence_connection_manager}/aggregates/login_challenge.py (90%) rename src/{servers/presence_connection_manager/src => frontends/gamespy/protocols/presence_connection_manager}/aggregates/sdk_revision.py (93%) rename src/{servers/presence_connection_manager/src => frontends/gamespy/protocols/presence_connection_manager}/aggregates/user_status.py (82%) rename src/{servers/presence_connection_manager/src => frontends/gamespy/protocols/presence_connection_manager}/applications/client.py (62%) rename src/{servers/presence_connection_manager/src => frontends/gamespy/protocols/presence_connection_manager}/applications/handlers.py (91%) rename src/{servers/presence_connection_manager/src => frontends/gamespy/protocols/presence_connection_manager}/applications/server_launcher.py (59%) rename src/{servers/presence_connection_manager/src => frontends/gamespy/protocols/presence_connection_manager}/applications/switcher.py (72%) rename src/{servers/presence_connection_manager/src => frontends/gamespy/protocols/presence_connection_manager}/contracts/requests.py (93%) rename src/{servers/presence_connection_manager/src => frontends/gamespy/protocols/presence_connection_manager}/contracts/responses.py (86%) rename src/{servers/presence_connection_manager/src => frontends/gamespy/protocols/presence_connection_manager}/contracts/results.py (78%) rename src/{servers/game_status => frontends/gamespy/protocols/presence_search_player}/__init__.py (100%) rename src/{servers/presence_search_player/src => frontends/gamespy/protocols/presence_search_player}/abstractions/contracts.py (73%) rename src/{servers/presence_search_player/src => frontends/gamespy/protocols/presence_search_player}/abstractions/handler.py (51%) rename src/{servers/presence_search_player/src => frontends/gamespy/protocols/presence_search_player}/aggregates/enums.py (100%) rename src/{servers/presence_search_player/src => frontends/gamespy/protocols/presence_search_player}/aggregates/exceptions.py (98%) create mode 100644 src/frontends/gamespy/protocols/presence_search_player/applications/client.py rename src/{servers/presence_search_player/src => frontends/gamespy/protocols/presence_search_player}/applications/handlers.py (84%) rename src/{servers/presence_search_player/src => frontends/gamespy/protocols/presence_search_player}/applications/server_launcher.py (59%) rename src/{servers/presence_search_player/src => frontends/gamespy/protocols/presence_search_player}/applications/switcher.py (75%) rename src/{servers/presence_search_player/src => frontends/gamespy/protocols/presence_search_player}/contracts/requests.py (95%) rename src/{servers/presence_search_player/src => frontends/gamespy/protocols/presence_search_player}/contracts/responses.py (95%) rename src/{servers/presence_search_player/src => frontends/gamespy/protocols/presence_search_player}/contracts/results.py (91%) rename src/{servers/game_status/tests => frontends/gamespy/protocols/query_report}/__init__.py (100%) create mode 100644 src/frontends/gamespy/protocols/query_report/aggregates/exceptions.py rename src/{servers/query_report/src => frontends/gamespy/protocols/query_report}/aggregates/game_server_info.py (78%) rename src/{servers/query_report/src => frontends/gamespy/protocols/query_report}/aggregates/natneg_channel.py (70%) rename src/{servers/query_report/src => frontends/gamespy/protocols/query_report}/aggregates/peer_room_info.py (100%) rename src/{servers/query_report/src => frontends/gamespy/protocols/query_report}/applications/client.py (69%) rename src/{servers/query_report/src => frontends/gamespy/protocols/query_report}/applications/server_launcher.py (61%) rename src/{servers/game_traffic_relay/src => frontends/gamespy/protocols/query_report/v1}/__init__.py (100%) rename src/{servers/game_traffic_relay/tests => frontends/gamespy/protocols/query_report/v1/abstractions}/__init__.py (100%) rename src/{servers/query_report/src => frontends/gamespy/protocols/query_report}/v1/abstractions/contracts.py (67%) create mode 100644 src/frontends/gamespy/protocols/query_report/v1/abstractions/handlers.py create mode 100644 src/frontends/gamespy/protocols/query_report/v2/abstractions/cmd_handler_base.py rename src/{servers/query_report/src => frontends/gamespy/protocols/query_report}/v2/abstractions/contracts.py (65%) rename src/{servers/query_report/src => frontends/gamespy/protocols/query_report}/v2/aggregates/enums.py (100%) rename src/{servers/query_report/src => frontends/gamespy/protocols/query_report}/v2/applications/handlers.py (86%) rename src/{servers/query_report/src => frontends/gamespy/protocols/query_report}/v2/applications/switcher.py (76%) rename src/{servers/query_report/src => frontends/gamespy/protocols/query_report}/v2/contracts/requests.py (94%) rename src/{servers/query_report/src => frontends/gamespy/protocols/query_report}/v2/contracts/responses.py (83%) rename src/{servers/query_report/src => frontends/gamespy/protocols/query_report}/v2/contracts/results.py (76%) rename src/{servers/natneg => frontends/gamespy/protocols/server_browser}/__init__.py (100%) rename src/{servers/server_browser/src => frontends/gamespy/protocols/server_browser}/aggregates/exceptions.py (63%) rename src/{servers/natneg/src => frontends/gamespy/protocols/server_browser/v2/abstractions}/__init__.py (100%) rename src/{servers/server_browser/src => frontends/gamespy/protocols/server_browser}/v2/abstractions/contracts.py (84%) rename src/{servers/server_browser/src => frontends/gamespy/protocols/server_browser}/v2/abstractions/handlers.py (78%) rename src/{servers/server_browser/src => frontends/gamespy/protocols/server_browser}/v2/aggregations/encryption.py (98%) rename src/{servers/server_browser/src => frontends/gamespy/protocols/server_browser}/v2/aggregations/enums.py (100%) rename src/{servers/server_browser/src => frontends/gamespy/protocols/server_browser}/v2/aggregations/server_info_builder.py (94%) rename src/{servers/server_browser/src => frontends/gamespy/protocols/server_browser}/v2/aggregations/string_flags.py (100%) create mode 100644 src/frontends/gamespy/protocols/server_browser/v2/applications/client.py rename src/{servers/server_browser/src => frontends/gamespy/protocols/server_browser}/v2/applications/handlers.py (83%) rename src/{servers/server_browser/src => frontends/gamespy/protocols/server_browser}/v2/applications/server_launcher.py (59%) rename src/{servers/server_browser/src => frontends/gamespy/protocols/server_browser}/v2/applications/switcher.py (62%) rename src/{servers/natneg/tests => frontends/gamespy/protocols/server_browser/v2/contracts}/__init__.py (100%) rename src/{servers/server_browser/src => frontends/gamespy/protocols/server_browser}/v2/contracts/requests.py (92%) rename src/{servers/server_browser/src => frontends/gamespy/protocols/server_browser}/v2/contracts/responses.py (88%) rename src/{servers/server_browser/src => frontends/gamespy/protocols/server_browser}/v2/contracts/results.py (59%) rename src/{servers/presence_connection_manager => frontends/gamespy/protocols/web_services}/__init__.py (100%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/abstractions/contracts.py (83%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/abstractions/handler.py (54%) create mode 100644 src/frontends/gamespy/protocols/web_services/aggregations/exceptions.py rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/aggregations/soap_envelop.py (95%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/applications/client.py (87%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/applications/server_launcher.py (61%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/applications/switcher.py (75%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/altas/___init__.py (100%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/auth/abstractions/general.py (92%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/auth/contracts/requests.py (96%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/auth/contracts/responses.py (87%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/auth/contracts/results.py (71%) create mode 100644 src/frontends/gamespy/protocols/web_services/modules/auth/exceptions/general.py rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/auth/handlers/general.py (83%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/direct2game/abstractions/contracts.py (60%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/direct2game/abstractions/handler.py (100%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/direct2game/contracts/requests.py (92%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/direct2game/contracts/responses.py (83%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/direct2game/contracts/results.py (77%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/direct2game/handlers/general.py (67%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/sake/abstractions/generals.py (84%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/sake/applications/handlers.py (65%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/sake/contracts/requests.py (96%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/sake/contracts/responses.py (73%) rename src/{servers/web_services/src => frontends/gamespy/protocols/web_services}/modules/sake/contracts/results.py (81%) create mode 100644 src/frontends/gamespy/protocols/web_services/modules/sake/exceptions/general.py rename src/{servers/presence_connection_manager/src => frontends/tests}/__init__.py (100%) rename src/{servers/presence_connection_manager/tests => frontends/tests/gamespy}/__init__.py (100%) rename src/{servers/presence_search_player => frontends/tests/gamespy/chat}/__init__.py (100%) rename src/{servers/chat/tests => frontends/tests/gamespy/chat}/game_tests.py (98%) rename src/{servers/chat/tests => frontends/tests/gamespy/chat}/mock_objects.py (87%) rename src/{servers/chat/tests => frontends/tests/gamespy/chat}/request_tests.py (98%) rename src/{servers/presence_search_player/src => frontends/tests/gamespy/game_status}/__init__.py (100%) rename src/{servers/game_status/tests => frontends/tests/gamespy/game_status}/game_tests.py (96%) rename src/{servers/game_status/tests => frontends/tests/gamespy/game_status}/handler_tests.py (90%) rename src/{servers/game_status/tests => frontends/tests/gamespy/game_status}/mock_objects.py (64%) rename src/{servers/presence_search_player/tests => frontends/tests/gamespy/library}/__init__.py (100%) rename src/{library/tests => frontends/tests/gamespy/library}/encrypt_tests.py (74%) rename src/{library/tests => frontends/tests/gamespy/library}/mock_objects.py (75%) rename src/{servers/query_report => frontends/tests/gamespy/natneg}/__init__.py (100%) rename src/{servers/natneg/tests => frontends/tests/gamespy/natneg}/handler_tests.py (90%) rename src/{servers/natneg/tests => frontends/tests/gamespy/natneg}/mock_objects.py (65%) rename src/{servers/natneg/tests => frontends/tests/gamespy/natneg}/redis.py (100%) rename src/{servers/query_report/src => frontends/tests/gamespy/presence_connection_manager}/__init__.py (100%) rename src/{servers/presence_connection_manager/tests => frontends/tests/gamespy/presence_connection_manager}/buddy_request_tests.py (93%) rename src/{servers/presence_connection_manager/tests => frontends/tests/gamespy/presence_connection_manager}/game_tests.py (84%) rename src/{servers/presence_connection_manager/tests => frontends/tests/gamespy/presence_connection_manager}/general_request_tests.py (95%) rename src/{servers/presence_connection_manager/tests => frontends/tests/gamespy/presence_connection_manager}/mock_objects.py (69%) rename src/{servers/presence_connection_manager/tests => frontends/tests/gamespy/presence_connection_manager}/profile_request_tests.py (96%) rename src/{servers/query_report/src/v1 => frontends/tests/gamespy/presence_search_player}/__init__.py (100%) rename src/{servers/presence_search_player/tests => frontends/tests/gamespy/presence_search_player}/game_tests.py (66%) rename src/{servers/presence_search_player/tests => frontends/tests/gamespy/presence_search_player}/handler_tests.py (91%) rename src/{servers/presence_search_player/tests => frontends/tests/gamespy/presence_search_player}/mock_objects.py (63%) rename src/{servers/query_report/src/v1/abstractions => frontends/tests/gamespy/query_report}/__init__.py (100%) rename src/{servers/query_report/tests => frontends/tests/gamespy/query_report}/game_tests.py (96%) rename src/{servers/query_report/tests => frontends/tests/gamespy/query_report}/mock_objects.py (59%) rename src/{servers/query_report/tests => frontends/tests/gamespy/query_report}/request_tests.py (95%) rename src/{servers/query_report/tests => frontends/tests/gamespy/server_browser}/__init__.py (100%) rename src/{servers/server_browser/tests => frontends/tests/gamespy/server_browser}/game_tests.py (98%) rename src/{servers/server_browser/tests => frontends/tests/gamespy/server_browser}/mock_objects.py (79%) rename src/{servers/server_browser => frontends/tests/gamespy/web_services}/__init__.py (100%) rename src/{servers/web_services/tests => frontends/tests/gamespy/web_services}/altas_tests.py (100%) rename src/{servers/web_services/tests => frontends/tests/gamespy/web_services}/auth_tests.py (97%) rename src/{servers/web_services/tests => frontends/tests/gamespy/web_services}/racing_tests.py (100%) rename src/{servers/web_services/tests => frontends/tests/gamespy/web_services}/sake_tests.py (98%) delete mode 100644 src/servers/game_status/src/abstractions/handlers.py delete mode 100644 src/servers/game_status/src/aggregations/exceptions.py delete mode 100644 src/servers/presence_search_player/src/applications/client.py delete mode 100644 src/servers/query_report/src/aggregates/exceptions.py delete mode 100644 src/servers/query_report/src/v1/abstractions/handlers.py delete mode 100644 src/servers/query_report/src/v2/abstractions/cmd_handler_base.py delete mode 100644 src/servers/server_browser/src/__init__.py delete mode 100644 src/servers/server_browser/src/v2/abstractions/__init__.py delete mode 100644 src/servers/server_browser/src/v2/applications/client.py delete mode 100644 src/servers/server_browser/src/v2/contracts/__init__.py delete mode 100644 src/servers/server_browser/tests/__init__.py delete mode 100644 src/servers/web_services/__init__.py delete mode 100644 src/servers/web_services/src/__init__.py delete mode 100644 src/servers/web_services/src/aggregations/exceptions.py delete mode 100644 src/servers/web_services/src/modules/auth/exceptions/general.py delete mode 100644 src/servers/web_services/src/modules/sake/exceptions/general.py delete mode 100644 src/servers/web_services/tests/__init__.py diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 04156f0dd..c0426d2f3 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -135,29 +135,29 @@ ALTER TABLE unispy.chat_channel_caches OWNER TO unispy; -- Name: chat_nick_caches; Type: TABLE; Schema: unispy; Owner: unispy -- -CREATE TABLE unispy.chat_nick_caches ( +CREATE TABLE unispy.chat_user_caches ( + user_id SERIAL PRIMARY KEY, server_id uuid NOT NULL, nick_name character varying NOT NULL UNIQUE, game_name character varying, user_name character varying, remote_ip_address inet NOT NULL, - remote_port integer NOT NULL, + remote_port INTEGER NOT NULL, key_value jsonb, update_time timestamp without time zone NOT NULL ); -ALTER TABLE unispy.chat_nick_caches OWNER TO unispy; +ALTER TABLE unispy.chat_user_caches OWNER TO unispy; -- -- Name: chat_user_caches; Type: TABLE; Schema: unispy; Owner: unispy -- -CREATE TABLE unispy.chat_user_caches ( - nick_name character varying NOT NULL UNIQUE, - channel_name character varying NOT NULL UNIQUE, +CREATE TABLE unispy.chat_channel_user_caches ( + userid INTEGER NOT NULL, + channel_name character varying NOT NULL, server_id uuid NOT NULL, - user_name character varying NOT NULL, update_time timestamp without time zone NOT NULL, is_voiceable boolean NOT NULL, is_channel_operator boolean NOT NULL, @@ -168,7 +168,7 @@ CREATE TABLE unispy.chat_user_caches ( ); -ALTER TABLE unispy.chat_user_caches OWNER TO unispy; +ALTER TABLE unispy.chat_channel_user_caches OWNER TO unispy; -- -- Name: friends; Type: TABLE; Schema: unispy; Owner: unispy @@ -441,6 +441,7 @@ CREATE TABLE unispy.profiles ( status smallint not NULL, statstring character varying, extra_info jsonb + FOREIGN key (userid) REFERENCES unispy.users (user_id) on delete cascade ); @@ -765,7 +766,7 @@ COPY unispy.chat_channel_caches (channel_name, server_id, game_name, room_name, -- Data for Name: chat_nick_caches; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.chat_nick_caches (server_id, nick_name, game_name, user_name, remote_ip_address, remote_port, key_value, update_time) FROM stdin; +COPY unispy.chat_user_caches (server_id, nick_name, game_name, user_name, remote_ip_address, remote_port, key_value, update_time) FROM stdin; \. @@ -773,7 +774,7 @@ COPY unispy.chat_nick_caches (server_id, nick_name, game_name, user_name, remote -- Data for Name: chat_user_caches; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.chat_user_caches (nick_name, channel_name, server_id, user_name, update_time, is_voiceable, is_channel_operator, is_channel_creator, remote_ip_address, remote_port, key_values) FROM stdin; +COPY unispy.chat_channel_user_caches (nick_name, channel_name, server_id, user_name, update_time, is_voiceable, is_channel_operator, is_channel_creator, remote_ip_address, remote_port, key_values) FROM stdin; \. @@ -5475,7 +5476,7 @@ SELECT pg_catalog.setval('unispy.users_userid_seq', 5, true); -- Name: chat_user_caches chat_user_caches_channel_name_fkey; Type: FK CONSTRAINT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.chat_user_caches +ALTER TABLE ONLY unispy.chat_channel_user_caches ADD CONSTRAINT chat_user_caches_channel_name_fkey FOREIGN KEY (channel_name) REFERENCES unispy.chat_channel_caches(channel_name); @@ -5494,7 +5495,11 @@ ALTER TABLE ONLY unispy.grouplist ALTER TABLE ONLY unispy.profiles ADD CONSTRAINT profiles_userid_fkey FOREIGN KEY (userid) REFERENCES unispy.users(userid); +ALTER TABLE ONLY unispy.chat_channel_user_caches + ADD CONSTRAINT chat_channel_user_caches_userid_fkey FOREGIN KEY(userid) unispy.chat_user_caches(userid) +ALTER TABLE ONLY unispy.chat_channel_user_caches + ADD CONSTRAINT chat_channel_user_caches_channel_name_fkey FOREGIN KEY(channel_name) unispy.chat_channel_caches(channel_name) PostgreSQL database dump complete diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index cbe3eecc2..a0fe3d0fd 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -11,4 +11,7 @@ "python.testing.pytestEnabled": false, "python.testing.unittestEnabled": true, "python.analysis.enablePytestSupport": false, + "python.analysis.extraPaths": [ + "./frontends/gamespy" + ], } \ No newline at end of file diff --git a/src/backends/library/abstractions/handler_base.py b/src/backends/library/abstractions/handler_base.py index 8fb7c3b24..ce55053ab 100644 --- a/src/backends/library/abstractions/handler_base.py +++ b/src/backends/library/abstractions/handler_base.py @@ -1,11 +1,11 @@ from typing import Optional, final from backends.library.abstractions.contracts import DataResponse, ErrorResponse, OKResponse, RequestBase, Response from backends.library.database.pg_orm import PG_SESSION -from library.src.abstractions.contracts import ResultBase +from frontends.gamespy.library.abstractions.contracts import ResultBase import logging -from library.src.exceptions.general import UniSpyException +from frontends.gamespy.library.exceptions.general import UniSpyException class HandlerBase: @@ -18,6 +18,10 @@ class HandlerBase: """ the response using to wrap data """ + # _data: object + """ + the data get from database, can be any type + """ @property def response(self) -> dict: """ diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 6163473e7..df2e45455 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -1,4 +1,4 @@ -from library.src.configs import CONFIG +from frontends.gamespy.library.configs import CONFIG from datetime import datetime from sqlalchemy import ( Boolean, @@ -19,9 +19,9 @@ from sqlalchemy.ext.declarative import DeclarativeMeta from sqlalchemy.orm import sessionmaker, declarative_base from sqlalchemy.types import TypeDecorator -from servers.natneg.src.aggregations.enums import NatClientIndex, NatPortType -from servers.presence_connection_manager.src.aggregates.enums import FriendRequestStatus, GPStatusCode -from servers.query_report.src.v2.aggregates.enums import GameServerStatus +from frontends.gamespy.protocols.natneg.aggregations.enums import NatClientIndex, NatPortType +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import FriendRequestStatus, GPStatusCode +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus import sqlalchemy as sa import enum @@ -238,8 +238,12 @@ class ChatChannelCaches(Base): update_time = Column(DateTime, nullable=False) -class ChatNickCaches(Base): +class ChatUserCaches(Base): + """ + each user only have a unique nick caches, but have multiple user caches + """ __tablename__ = "chat_nick_caches" + user_id = Column(Integer, primary_key=True, nullable=False) server_id = Column(UUID, nullable=False) nick_name = Column(String, primary_key=True, nullable=False) game_name = Column(String, nullable=True) @@ -250,13 +254,12 @@ class ChatNickCaches(Base): update_time = Column(DateTime, nullable=False) -class ChatUserCaches(Base): +class ChatChannelUserCaches(Base): __tablename__ = "chat_user_caches" - nick_name = Column(String, primary_key=True, nullable=False) + user_id = Column(Integer, ForeignKey( + "chat_channel_caches.user_id"), primary_key=True, nullable=False) channel_name = Column(String, ForeignKey( "chat_channel_caches.channel_name"), nullable=False) - server_id = Column(UUID, nullable=False) - user_name = Column(String, nullable=False) update_time = Column(DateTime, nullable=False) is_voiceable = Column(Boolean, nullable=False) is_channel_operator = Column(Boolean, nullable=False) diff --git a/src/backends/library/database/redis.py b/src/backends/library/database/redis.py index 662f2e7bf..fc6c78d9b 100644 --- a/src/backends/library/database/redis.py +++ b/src/backends/library/database/redis.py @@ -1,6 +1,6 @@ import redis -from library.src.configs import CONFIG +from frontends.gamespy.library.configs import CONFIG # SESSION = redis.Redis.from_url(CONFIG.redis.url) diff --git a/src/backends/protocols/gamespy/chat/channel.py b/src/backends/protocols/gamespy/chat/channel.py index c47c558ee..cc7a2519c 100644 --- a/src/backends/protocols/gamespy/chat/channel.py +++ b/src/backends/protocols/gamespy/chat/channel.py @@ -3,18 +3,18 @@ # from uuid import UUID # from pydantic import BaseModel, field_validator -# from library.src.abstractions.brocker import BrockerBase -# from library.src.network.brockers import WebsocketBrocker -# from library.src.configs import CONFIG -# from servers.chat.src.abstractions.contract import ResponseBase -# from servers.chat.src.aggregates.channel_user import ChannelUser +# from frontends.gamespy.library.abstractions.brocker import BrockerBase +# from frontends.gamespy.library.network.brockers import WebsocketBrocker +# from frontends.gamespy.library.configs import CONFIG +# from frontends.gamespy.protocols.chat.abstractions.contract import ResponseBase +# from frontends.gamespy.protocols.chat.aggregates.channel_user import ChannelUser # from backends.protocols.gamespy.chat.managers import KeyValueManager -# from servers.chat.src.aggregates.peer_room import PeerRoom -# from servers.chat.src.applications.client import Client -# from servers.chat.src.contracts.requests import ModeRequest -# from servers.chat.src.aggregates.enums import PeerRoomType -# from servers.chat.src.aggregates.exceptions import ChatException -# from servers.server_browser.src.v2.aggregations.server_info_builder import PEER_GROUP_LIST +# from frontends.gamespy.protocols.chat.aggregates.peer_room import PeerRoom +# from frontends.gamespy.protocols.chat.applications.client import Client +# from frontends.gamespy.protocols.chat.contracts.requests import ModeRequest +# from frontends.gamespy.protocols.chat.aggregates.enums import PeerRoomType +# from frontends.gamespy.protocols.chat.aggregates.exceptions import ChatException +# from frontends.gamespy.protocols.server_browser.v2.aggregations.server_info_builder import PEER_GROUP_LIST # MIN_CHANNEL_NAME_LENGTH = 4 diff --git a/src/backends/protocols/gamespy/chat/channel_user.py b/src/backends/protocols/gamespy/chat/channel_user.py index 2ad66f8c8..8b74361ee 100644 --- a/src/backends/protocols/gamespy/chat/channel_user.py +++ b/src/backends/protocols/gamespy/chat/channel_user.py @@ -1,11 +1,11 @@ # from uuid import UUID # from backends.protocols.gamespy.chat.managers import KeyValueManager -# from servers.chat.src.applications.client import Client +# from frontends.gamespy.protocols.chat.applications.client import Client # from typing import TYPE_CHECKING # if TYPE_CHECKING: -# from servers.chat.src.aggregates.channel import Channel +# from frontends.gamespy.protocols.chat.aggregates.channel import Channel # class ChannelUser: diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index 3d928fb2e..b8b90fc08 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -1,32 +1,21 @@ from datetime import datetime, timedelta from typing import TYPE_CHECKING, cast -from sqlalchemy import Column -from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatNickCaches, ChatUserCaches, Users, Profiles, SubProfiles -from servers.chat.src.aggregates.exceptions import ChatException - - -def is_cdkey_valid(cdkey: str) -> bool: - if TYPE_CHECKING: - assert isinstance(SubProfiles.cdkeyenc, Column) - result = PG_SESSION.query(SubProfiles).where( - SubProfiles.cdkeyenc == cdkey).count() - if result == 0: - return False - - else: - return True +from sqlalchemy import Column, func +from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatUserCaches, ChatChannelUserCaches, Users, Profiles, SubProfiles +from frontends.gamespy.protocols.chat.aggregates.exceptions import ChatException, NoSuchNickException +from frontends.gamespy.protocols.chat.contracts.results import ListResult def is_nick_exist(nick_name: str) -> bool: - c = PG_SESSION.query(ChatNickCaches.nick_name).count() + c = PG_SESSION.query(ChatUserCaches.nick_name).count() if c == 1: return True else: return False -def add_nick_cache(cache: ChatNickCaches): +def add_nick_cache(cache: ChatUserCaches): PG_SESSION.add(cache) PG_SESSION.commit() @@ -36,15 +25,9 @@ def nick_and_email_login(nick_name: str, email: str, password_hash: str) -> tupl return userid, profileid, emailverified, banned """ - if TYPE_CHECKING: - assert isinstance(Profiles.profileid, Column) - assert isinstance(Profiles.userid, Column) - assert isinstance(Users.userid, Column) - assert isinstance(Users.email, Column) - assert isinstance(Profiles.nick, Column) - assert isinstance(Users.emailverified, Column) - assert isinstance(Users.banned, Column) - assert isinstance(Users.password, Column) + assert isinstance(nick_name, str) + assert isinstance(email, str) + assert isinstance(password_hash, str) result = PG_SESSION.query(Users.userid, Profiles.profileid, Users.emailverified, Users.banned).join(Profiles, (Users.userid == Profiles.userid)).where( @@ -66,17 +49,8 @@ def uniquenick_login(uniquenick:str,namespace_id:int)-> tuple[int, int, bool, bo return userid, profileid, emailverified, banned """ - if TYPE_CHECKING: - assert isinstance(Profiles.profileid, Column) - assert isinstance(Profiles.userid, Column) - assert isinstance(Users.userid, Column) - assert isinstance(Users.email, Column) - assert isinstance(Profiles.nick, Column) - assert isinstance(Users.emailverified, Column) - assert isinstance(Users.banned, Column) - assert isinstance(Users.password, Column) - assert isinstance(SubProfiles.namespaceid, Column) - assert isinstance(SubProfiles.uniquenick ,Column) + assert isinstance(uniquenick, str) + assert isinstance(namespace_id, int) result = PG_SESSION.query(Users.userid, Profiles.profileid,Users.emailverified, Users.banned).join(Profiles,(Users.userid == Profiles.userid)).join(Profiles,(Profiles.profileid == SubProfiles.profileid)).where(SubProfiles.namespaceid == namespace_id,SubProfiles.uniquenick == uniquenick).first() if result is None: # fmt: off @@ -87,6 +61,22 @@ def uniquenick_login(uniquenick:str,namespace_id:int)-> tuple[int, int, bool, bo return result +# region User + + +def is_cdkey_valid(cdkey: str) -> bool: + if TYPE_CHECKING: + assert isinstance(SubProfiles.cdkeyenc, Column) + result = PG_SESSION.query(SubProfiles).where( + SubProfiles.cdkeyenc == cdkey).count() + if result == 0: + return False + + else: + return True + +# region Channel + def is_channel_exist(channel_name:str,game_name:str)->bool: channel_count = PG_SESSION.query(ChatChannelCaches)\ .where(ChatChannelCaches.channel_name == channel_name, @@ -101,42 +91,112 @@ def add_channel(channel:ChatChannelCaches): PG_SESSION.add(channel) PG_SESSION.commit() -def get_channel_cache(channel_name:str,game_name:str)->ChatChannelCaches: +def get_channel_by_name_and_game(channel_name:str,game_name:str)->ChatChannelCaches: channel = PG_SESSION.query(ChatChannelCaches)\ .where(ChatChannelCaches.channel_name == channel_name, ChatChannelCaches.game_name == game_name)\ .first() return channel -def update_channel(channel:ChatChannelCaches): +def get_channel_by_name_and_ip_port(channel_name:str,ip:str,port:int)->ChatChannelCaches|None: + assert isinstance(channel_name,str) + assert isinstance(ip,str) + assert isinstance(port,int) + result = PG_SESSION.query(ChatChannelCaches).join(ChatChannelUserCaches).where( + ChatChannelUserCaches.channel_name == channel_name, ChatChannelUserCaches.remote_ip_address == ip, ChatChannelUserCaches.remote_port == port).first() + return result +def update_channel_time(channel:ChatChannelCaches): channel.update_time = datetime.now() # type: ignore PG_SESSION.commit() +def db_commit(): + PG_SESSION.commit() def get_user_cache_by_nick_name(nick_name:str)->ChatUserCaches: result = PG_SESSION.query(ChatUserCaches).where(ChatUserCaches.nick_name == nick_name).first() return result +def get_user_cache_by_ip_port(ip:str,port:int)->ChatUserCaches|None: + result = PG_SESSION.query(ChatUserCaches).where(ChatUserCaches.remote_ip_address == ip, ChatUserCaches.remote_port == port).first() + assert isinstance(result,ChatUserCaches) + return result + +def get_whois_result(nick:str)->tuple: + """ + nick is unique in chat + """ + info = PG_SESSION.query(ChatUserCaches).first() + + if info is None: + raise NoSuchNickException(f"User not find by nick name:{nick}.") + channels = PG_SESSION.query(ChatChannelUserCaches.channel_name).join(ChatUserCaches,ChatChannelUserCaches.user_id == ChatUserCaches.user_id).where(ChatChannelUserCaches.user_id == info.user_id).all() + return info.nick_name,info.user_name,info.nick_name,info.remote_ip_address,channels # type:ignore + + + +def remove_user_caches_by_ip_port(ip:str,port:int): + assert isinstance(ip,str) + assert isinstance(port,int) + PG_SESSION.query(ChatChannelUserCaches).where(ChatChannelUserCaches.remote_ip_address==ip,ChatChannelUserCaches.remote_port == port).delete() + def remove_channel(cache:ChatChannelCaches)->None: assert isinstance(cache,ChatChannelCaches) PG_SESSION.delete(cache) PG_SESSION.commit() -def remove_user(cache:ChatUserCaches): - assert isinstance(cache,ChatUserCaches) +def remove_user(cache:ChatChannelUserCaches): + assert isinstance(cache,ChatChannelUserCaches) PG_SESSION.delete(cache) PG_SESSION.commit() def is_user_exist(ip:str,port:int)->bool: - user_count= PG_SESSION.query(ChatUserCaches).where(ChatUserCaches.remote_ip_address==ip, - ChatUserCaches.remote_port==port).count() + user_count= PG_SESSION.query(ChatChannelUserCaches).where(ChatChannelUserCaches.remote_ip_address==ip, + ChatChannelUserCaches.remote_port==port).count() if user_count ==1: return True else: return False -def update_client(cache:ChatUserCaches): - assert isinstance(cache,ChatUserCaches) +def update_client(cache:ChatChannelUserCaches): + assert isinstance(cache,ChatChannelUserCaches) PG_SESSION.commit() +def add_invited(channel_name:str,client_ip:str,client_port:int): + pass + + + +def find_channel_by_substring(channel_name:str)->list[dict]: + assert isinstance(channel_name,str) + + names,topics = PG_SESSION.query(ChatChannelCaches.channel_name,ChatChannelCaches.topic).where(ChatChannelCaches.channel_name.like(f"%{channel_name}%")).all() + users = PG_SESSION.query(ChatChannelUserCaches).where(ChatChannelUserCaches.channel_name.like(f"%{channel_name}%")).all() + data: list[dict] =[] + for name,topic,count in zip(names,topics,users): + d = { + "channel_name":name, + "total_channel_user":count, + "channel_topic":topic + } + data.append(d) + return data + +def find_user_by_substring(user_name:str)->list[dict]: + assert isinstance(user_name,str) + names,topics,users = PG_SESSION.query(ChatChannelCaches.channel_name,ChatChannelCaches.topic,func.count(ChatChannelUserCaches.channel_name)).join(ChatUserCaches,ChatUserCaches.user_id==ChatChannelUserCaches.user_id).join(ChatChannelCaches,ChatChannelCaches.channel_name==ChatChannelUserCaches.channel_name).where(ChatUserCaches.user_name.like(f"%{user_name}%")).all() + data: list[dict] =[] + + for name,topic,count in zip(names,topics,users): + d = { + "channel_name":name, + "total_channel_user":count, + "channel_topic":topic + } + data.append(d) + return data + +def get_channel_user_cache(channel_name:str)->list[dict]: + pass +def get_user_cache(user_name:str)->list[dict]: + pass \ No newline at end of file diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index ed7b1c398..92c82f2a8 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -2,11 +2,13 @@ from typing import TYPE_CHECKING, cast from backends.library.abstractions.contracts import RequestBase from backends.library.abstractions.handler_base import HandlerBase -from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatNickCaches, ChatUserCaches +from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatUserCaches, ChatChannelUserCaches import backends.protocols.gamespy.chat.data as data from backends.protocols.gamespy.chat.requests import * -from servers.chat.src.aggregates.exceptions import LoginFailedException, NickNameInUseException, NoSuchChannelException, NoSuchNickException -from servers.chat.src.contracts.results import CryptResult, GetKeyResult, NickResult +from frontends.gamespy.protocols.chat.aggregates.exceptions import ChatException, LoginFailedException, NickNameInUseException, NoSuchChannelException, NoSuchNickException +from frontends.gamespy.protocols.chat.contracts.results import CryptResult, GetKeyResult, ListResult, NickResult, WhoIsResult + +# region General class CdKeyHandler(HandlerBase): @@ -22,8 +24,8 @@ class CryptHandler(HandlerBase): _request: CryptRequest async def _data_operate(self) -> None: - result = PG_SESSION.query(ChatNickCaches).where(ChatNickCaches.remote_ip_address == - self._request.client_ip, ChatNickCaches.remote_port == self._request.client_port).first() + result = PG_SESSION.query(ChatUserCaches).where(ChatUserCaches.remote_ip_address == + self._request.client_ip, ChatUserCaches.remote_port == self._request.client_port).first() if result is None: raise NoSuchNickException( f"No nick found for {self._request.client_ip}") @@ -38,8 +40,8 @@ class GetKeyHandler(HandlerBase): _request: GetKeyRequest async def _data_operate(self) -> None: - caches = PG_SESSION.query(ChatNickCaches.key_value).where( - ChatNickCaches.nick_name == self._request.nick_name).first() + caches = PG_SESSION.query(ChatUserCaches.key_value).where( + ChatUserCaches.nick_name == self._request.nick_name).first() if caches is None: raise NoSuchNickException("nick not found") @@ -63,13 +65,55 @@ class InviteHandler(HandlerBase): _request: InviteRequest async def _data_operate(self) -> None: - chann = PG_SESSION.query(ChatChannelCaches).join(ChatUserCaches).where( - ChatUserCaches.channel_name == self._request.channel_name, ChatUserCaches.remote_ip_address == self._request.client_ip, ChatUserCaches.remote_port == self._request.client_port).first() + chann = data.get_channel_by_name_and_ip_port( + self._request.channel_name, self._request.client_ip, self._request.client_port) if chann is None: raise NoSuchChannelException( "you have to be in this channel to invite your friends") + assert isinstance(chann.invited_nicks, list) chann.invited_nicks.append(self._request.nick_name) + data.db_commit() + + +class ListHandler(HandlerBase): + _request: ListRequest + + async def _request_check(self) -> None: + if self._request.is_searching_channel: + pass + elif self._request.is_searching_user: + pass + else: + raise ChatException("request is invalid") + + async def _data_operate(self) -> None: + if self._request.is_searching_channel: + # get the channel names with the substring + self._data = data.find_channel_by_substring(self._request.filter) + return + if self._request.is_searching_user: + # get the user names with the substring + self._data = data.find_user_by_substring(self._request.filter) + + async def _result_construct(self) -> None: + data = [] + for d in self._data: + dd = ListResult.ListInfo(**d) + data.append(dd) + self._result = ListResult(user_irc_prefix="", channel_info_list=data) + + +class LoginPreAuthHandler(HandlerBase): + _request: LoginPreAuthRequest + + async def _data_operate(self) -> None: + raise NotImplementedError("should this access to PCM's api?") + + +class LoginHandler(HandlerBase): + async def _data_operate(self) -> None: + raise NotImplementedError("should this access to PCM's api?") class NickHandler(HandlerBase): @@ -81,7 +125,7 @@ async def _data_operate(self) -> None: raise NickNameInUseException( old_nick=self._request.nick_name, new_nick="", message="nick name in use") else: - cache = ChatNickCaches(nick_name=self._request.nick_name, + cache = ChatUserCaches(nick_name=self._request.nick_name, server_id=self._request.server_id, update_time=datetime.now()) data.add_nick_cache(cache) @@ -89,6 +133,72 @@ async def _data_operate(self) -> None: async def _result_construct(self) -> None: self._result = NickResult(nick_name=self._request.nick_name) + +class QuitHandler(HandlerBase): + _request: QuitRequest + + async def _data_operate(self) -> None: + data.remove_user_caches_by_ip_port( + self._request.client_ip, self._request.client_port) + raise NotImplementedError() + + +class RegisterNickHandler(HandlerBase): + async def _data_operate(self) -> None: + raise NotImplementedError( + "we do not know which unique nick should be updated") + + +class SetKeyHandler(HandlerBase): + _request: SetKeyRequest + + async def _data_operate(self) -> None: + user = data.get_user_cache_by_ip_port( + self._request.client_ip, self._request.client_port) + if user is None: + raise NoSuchNickException( + "The ip and port is not find in database") + + user.key_value = self._request.key_values # type:ignore + data.db_commit() + + +class UserHandler(HandlerBase): + _request: UserRequest + + async def _data_operate(self) -> None: + raise NotImplementedError("maybe update the user caches") + + +class WhoIsHandler(HandlerBase): + _request: WhoIsRequest + + async def _data_operate(self) -> None: + self._data: tuple = data.get_whois_result(self._request.nick_name) + + async def _result_construct(self) -> None: + self._result = WhoIsResult(nick_name=self._data[0], + user_name=self._data[1], + name=self._data[2], + public_ip_address=self._data[3], + joined_channel_name=self._data[4]) + + +class WhoHandler(HandlerBase): + _request: WhoRequest + + async def _data_operate(self) -> None: + if self._request.request_type == WhoRequestType.GET_CHANNEL_USER_INFO: + self._get_channel_user_info() + else: + self._get_user_info() + + def _get_channel_user_info(self) -> None: + pass + + def _get_user_info(self) -> None: + pass + # class JoinHandler(HandlerBase): # _request: JoinRequest @@ -105,3 +215,8 @@ async def _result_construct(self) -> None: # is_peer_room = # chan = ChatChannelCaches(channel_name=self._request.channel_name, server_id=self._request.server_id, game_name, room_name, topic, # password=self._request.password, group_id, max_num_user=200, key_values=None, update_time=datetime.now()) + + +# region Channel + +# region Message diff --git a/src/backends/protocols/gamespy/chat/managers.py b/src/backends/protocols/gamespy/chat/managers.py index 0b0492ea7..fa2bbe982 100644 --- a/src/backends/protocols/gamespy/chat/managers.py +++ b/src/backends/protocols/gamespy/chat/managers.py @@ -36,7 +36,7 @@ # if TYPE_CHECKING: -# from servers.chat.src.aggregates.channel import Channel +# from frontends.gamespy.protocols.chat.aggregates.channel import Channel # class ChannelManager: diff --git a/src/backends/protocols/gamespy/chat/requests.py b/src/backends/protocols/gamespy/chat/requests.py index 8b814c64f..f0ba7100c 100644 --- a/src/backends/protocols/gamespy/chat/requests.py +++ b/src/backends/protocols/gamespy/chat/requests.py @@ -1,5 +1,5 @@ import backends.library.abstractions.contracts as lib -from servers.chat.src.aggregates.enums import GetKeyRequestType, LoginRequestType, MessageType, ModeRequestType, TopicRequestType, WhoRequestType +from frontends.gamespy.protocols.chat.aggregates.enums import GetKeyRequestType, LoginRequestType, MessageType, ModeRequestType, TopicRequestType, WhoRequestType class RequestBase(lib.RequestBase): @@ -24,7 +24,7 @@ class GetUdpRelayRequest(RequestBase): class InviteRequest(RequestBase): channel_name: str - nick_name: str + nick_name:str class ListLimitRequest(RequestBase): @@ -38,7 +38,7 @@ class ListRequest(RequestBase): filter: str -class LoginPreAuth(RequestBase): +class LoginPreAuthRequest(RequestBase): auth_token: str partner_challenge: str diff --git a/src/backends/protocols/gamespy/game_status/data.py b/src/backends/protocols/gamespy/game_status/data.py index bef87e216..cb50bccc3 100644 --- a/src/backends/protocols/gamespy/game_status/data.py +++ b/src/backends/protocols/gamespy/game_status/data.py @@ -2,8 +2,8 @@ from sqlalchemy import Column from backends.library.database.pg_orm import PG_SESSION, PStorage, Profiles, SubProfiles, Users -from servers.game_status.src.aggregations.enums import PersistStorageType -from servers.game_status.src.aggregations.exceptions import GSException +from frontends.gamespy.protocols.game_status.aggregations.enums import PersistStorageType +from frontends.gamespy.protocols.game_status.aggregations.exceptions import GSException def create_new_game_data(): diff --git a/src/backends/protocols/gamespy/game_status/handlers.py b/src/backends/protocols/gamespy/game_status/handlers.py index b4da005da..f2f57813d 100644 --- a/src/backends/protocols/gamespy/game_status/handlers.py +++ b/src/backends/protocols/gamespy/game_status/handlers.py @@ -3,8 +3,8 @@ from backends.library.abstractions.handler_base import HandlerBase from backends.protocols.gamespy.game_status.requests import * import backends.protocols.gamespy.game_status.data as data -from servers.game_status.src.aggregations.exceptions import GSException -from servers.game_status.src.contracts.results import AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult +from frontends.gamespy.protocols.game_status.aggregations.exceptions import GSException +from frontends.gamespy.protocols.game_status.contracts.results import AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult class AuthGameHandler(HandlerBase): diff --git a/src/backends/protocols/gamespy/game_status/requests.py b/src/backends/protocols/gamespy/game_status/requests.py index 7eadfdec5..0c0dff8da 100644 --- a/src/backends/protocols/gamespy/game_status/requests.py +++ b/src/backends/protocols/gamespy/game_status/requests.py @@ -1,6 +1,6 @@ from typing import Optional import backends.library.abstractions.contracts as lib -from servers.game_status.src.aggregations.enums import AuthMethod, PersistStorageType +from frontends.gamespy.protocols.game_status.aggregations.enums import AuthMethod, PersistStorageType class RequestBase(lib.RequestBase): diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py index 1150da7b0..9f9bbfcb7 100644 --- a/src/backends/protocols/gamespy/natneg/data.py +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -1,7 +1,7 @@ from datetime import datetime, timedelta from uuid import UUID from backends.library.database.pg_orm import PG_SESSION, InitPacketCaches, NatFailCaches -from servers.natneg.src.aggregations.enums import NatClientIndex +from frontends.gamespy.protocols.natneg.aggregations.enums import NatClientIndex def store_init_packet(info: InitPacketCaches) -> None: diff --git a/src/backends/protocols/gamespy/natneg/requests.py b/src/backends/protocols/gamespy/natneg/requests.py index 180d7a228..500b6b7b6 100644 --- a/src/backends/protocols/gamespy/natneg/requests.py +++ b/src/backends/protocols/gamespy/natneg/requests.py @@ -1,4 +1,4 @@ -from servers.natneg.src.aggregations.enums import ( +from frontends.gamespy.protocols.natneg.aggregations.enums import ( NatClientIndex, NatPortMappingScheme, NatPortType, diff --git a/src/backends/protocols/gamespy/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py index d18439adb..979ffeeae 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/data.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/data.py @@ -12,10 +12,10 @@ Users, PG_SESSION, ) -from servers.presence_connection_manager.src.aggregates.enums import GPStatusCode, LoginStatus -from servers.presence_connection_manager.src.aggregates.user_status import UserStatus -from servers.presence_connection_manager.src.contracts.results import GetProfileData, LoginData -from servers.presence_search_player.src.aggregates.exceptions import GPAddBuddyException, GPDatabaseException, GPStatusException, GPException +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import GPStatusCode, LoginStatus +from frontends.gamespy.protocols.presence_connection_manager.aggregates.user_status import UserStatus +from frontends.gamespy.protocols.presence_connection_manager.contracts.results import GetProfileData, LoginData +from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import GPAddBuddyException, GPDatabaseException, GPStatusException, GPException from backends.protocols.gamespy.presence_search_player.data import is_email_exist # region General diff --git a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py index aef65fd95..8e1c361e1 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py @@ -2,8 +2,8 @@ from backends.library.abstractions.handler_base import HandlerBase from backends.protocols.gamespy.presence_connection_manager.requests import * import backends.protocols.gamespy.presence_connection_manager.data as data -from servers.presence_connection_manager.src.contracts.results import BlockListResult, BuddyListResult, GetProfileResult, LoginResult, StatusResult -from servers.presence_search_player.src.aggregates.exceptions import GPLoginBadEmailException +from frontends.gamespy.protocols.presence_connection_manager.contracts.results import BlockListResult, BuddyListResult, GetProfileResult, LoginResult, StatusResult +from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import GPLoginBadEmailException # region General diff --git a/src/backends/protocols/gamespy/presence_connection_manager/requests.py b/src/backends/protocols/gamespy/presence_connection_manager/requests.py index dcac9f15b..786694eac 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/requests.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/requests.py @@ -2,14 +2,14 @@ from pydantic import UUID4, BaseModel -from servers.presence_connection_manager.src.aggregates.enums import ( +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( LoginType, PublicMasks, SdkRevisionType, ) from backends.library.abstractions.contracts import RequestBase -from servers.presence_connection_manager.src.aggregates.user_status import UserStatus, UserStatusInfo +from frontends.gamespy.protocols.presence_connection_manager.aggregates.user_status import UserStatus, UserStatusInfo class ErrorOnParse(RequestBase): diff --git a/src/backends/protocols/gamespy/presence_search_player/data.py b/src/backends/protocols/gamespy/presence_search_player/data.py index 134f69a5c..9df27fefb 100644 --- a/src/backends/protocols/gamespy/presence_search_player/data.py +++ b/src/backends/protocols/gamespy/presence_search_player/data.py @@ -7,8 +7,12 @@ Users, PG_SESSION, ) -from servers.presence_search_player.src.aggregates.exceptions import CheckException -from servers.presence_search_player.src.contracts.results import * +from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import CheckException +from frontends.gamespy.protocols.presence_search_player.contracts.results import * + + +def db_commit() -> None: + PG_SESSION.commit() def verify_email(email: str): @@ -184,7 +188,7 @@ def get_matched_profile_info_list( def get_matched_info_by_nick( nick_name: str, -) -> list[SearchResultData]: +) -> list[dict]: result = ( PG_SESSION.query( Users.email, @@ -199,18 +203,19 @@ def get_matched_info_by_nick( .where(Profiles.nick == nick_name) .all() ) - temp: list[SearchResultData] = [] + temp: list[dict] = [] for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: if TYPE_CHECKING: extra_info = cast(dict, extra_info) firstname = extra_info.get("firstname", "") lastname = extra_info.get("lastname", "") - t = SearchResultData(profile_id=profile_id, - nick=nick, - uniquenick=uniquenick, - email=email, - namespace_id=namespace_id, firstname=firstname, - lastname=lastname) + t = {"profile_id": profile_id, + "nick": nick, + "uniquenick": uniquenick, + "email": email, + "namespace_id": namespace_id, + "firstname": firstname, + "lastname": lastname} temp.append(t) return temp @@ -218,7 +223,7 @@ def get_matched_info_by_nick( def get_matched_info_by_email( email: str, -) -> list[SearchResultData]: +) -> list[dict]: result = ( PG_SESSION.query( Users.email, @@ -233,23 +238,24 @@ def get_matched_info_by_email( .where(Users.email == email) .all() ) - temp: list[SearchResultData] = [] + temp: list[dict] = [] for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: if TYPE_CHECKING: extra_info = cast(dict, extra_info) firstname = extra_info.get("firstname", "") lastname = extra_info.get("lastname", "") - t = SearchResultData(profile_id=profile_id, - nick=nick, - uniquenick=uniquenick, - email=email, - namespace_id=namespace_id, firstname=firstname, - lastname=lastname) + t = {"profile_id": profile_id, + "nick": nick, + "uniquenick": uniquenick, + "email": email, + "namespace_id": namespace_id, + "firstname": firstname, + "lastname": lastname} temp.append(t) return temp -def get_matched_info_by_nick_and_email(nick_name: str, email: str): +def get_matched_info_by_nick_and_email(nick_name: str, email: str)->list[dict]: result = ( PG_SESSION.query( @@ -265,25 +271,26 @@ def get_matched_info_by_nick_and_email(nick_name: str, email: str): .where(Users.email == email, Profiles.nick == nick_name) .all() ) - temp: list[SearchResultData] = [] + data: list[dict] = [] for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: if TYPE_CHECKING: extra_info = cast(dict, extra_info) firstname = extra_info.get("firstname", "") lastname = extra_info.get("lastname", "") - t = SearchResultData(profile_id=profile_id, - nick=nick, - uniquenick=uniquenick, - email=email, - namespace_id=namespace_id, firstname=firstname, - lastname=lastname) - temp.append(t) - return temp + t = {"profile_id": profile_id, + "nick": nick, + "uniquenick": uniquenick, + "email": email, + "namespace_id": namespace_id, + "firstname": firstname, + "lastname": lastname} + data.append(t) + return data def get_matched_info_by_uniquenick_and_namespaceid( unique_nick: str, namespace_id: int -) -> list[SearchResultData]: +) -> list[dict]: result = ( PG_SESSION.query( Profiles.profileid, @@ -300,26 +307,27 @@ def get_matched_info_by_uniquenick_and_namespaceid( ) .all() ) - temp: list[SearchResultData] = [] + data: list[dict] = [] for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: if TYPE_CHECKING: extra_info = cast(dict, extra_info) firstname = extra_info.get("firstname", "") lastname = extra_info.get("lastname", "") - t = SearchResultData(profile_id=profile_id, - nick=nick, - uniquenick=uniquenick, - email=email, - namespace_id=namespace_id, firstname=firstname, - lastname=lastname) - temp.append(t) + t = {"profile_id": profile_id, + "nick": nick, + "uniquenick": uniquenick, + "email": email, + "namespace_id": namespace_id, + "firstname": firstname, + "lastname": lastname} + data.append(t) - return temp + return data def get_matched_info_by_uniquenick_and_namespaceids( unique_nick: str, namespace_ids: list[int] -) -> list[SearchResultData]: +) -> list[dict]: result = ( PG_SESSION.query( Profiles.profileid, @@ -335,18 +343,19 @@ def get_matched_info_by_uniquenick_and_namespaceids( ) .all() ) - data: list[SearchResultData] = [] + data: list[dict] = [] for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: if TYPE_CHECKING: extra_info = cast(dict, extra_info) firstname = extra_info.get("firstname", "") lastname = extra_info.get("lastname", "") - t = SearchResultData(profile_id=profile_id, - nick=nick, - uniquenick=uniquenick, - email=email, - namespace_id=namespace_id, firstname=firstname, - lastname=lastname) + t = {"profile_id": profile_id, + "nick": nick, + "uniquenick": uniquenick, + "email": email, + "namespace_id": namespace_id, + "firstname": firstname, + "lastname": lastname} data.append(t) return data diff --git a/src/backends/protocols/gamespy/presence_search_player/handlers.py b/src/backends/protocols/gamespy/presence_search_player/handlers.py index 4b06ab86e..53352c987 100644 --- a/src/backends/protocols/gamespy/presence_search_player/handlers.py +++ b/src/backends/protocols/gamespy/presence_search_player/handlers.py @@ -3,9 +3,9 @@ from backends.library.database.pg_orm import PG_SESSION, Users, Profiles, SubProfiles import backends.protocols.gamespy.presence_search_player.data as data from backends.protocols.gamespy.presence_search_player.requests import * -from library.src.exceptions.general import UniSpyException -from servers.presence_search_player.src.aggregates.exceptions import CheckException -from servers.presence_search_player.src.contracts.results import CheckResult, NewUserResult, NickResultData, NicksResult, OthersListData, OthersListResult, OthersResult, OthersResultData, SearchResult, SearchUniqueResult, UniqueSearchResult, ValidResult +from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import CheckException +from frontends.gamespy.protocols.presence_search_player.contracts.results import CheckResult, NewUserResult, NickResultData, NicksResult, OthersListData, OthersListResult, OthersResult, OthersResultData, SearchResult, SearchResultData, SearchUniqueResult, UniqueSearchResult, ValidResult class CheckHandler(HandlerBase): @@ -19,13 +19,15 @@ async def _data_operate(self) -> None: raise CheckException("The email is not existed") if data.verify_email_and_password(self._request.email, self._request.password): raise CheckException("The password is incorrect") - profile_id = data.get_profile_id( + self._profile_id = data.get_profile_id( self._request.email, self._request.password, self._request.nick, self._request.partner_id) - if profile_id is None: + if self._profile_id is None: raise CheckException(f"No pid found with email{ self._request.email}") - self._result = CheckResult(profile_id=profile_id) + async def _result_construct(self) -> None: + assert self._profile_id is not None + self._result = CheckResult(profile_id=self._profile_id) class NewUserHandler(HandlerBase): @@ -105,7 +107,7 @@ class OthersHandler(HandlerBase): _request: OthersRequest async def _data_operate(self) -> None: - self._data = data.get_friend_info_list( + self._data: list = data.get_friend_info_list( self._request.profile_id, self._request.namespace_id, self._request.game_name) async def _result_construct(self) -> None: @@ -121,7 +123,7 @@ class OthersListHandler(HandlerBase): _request: OthersListRequest async def _data_operate(self) -> None: - self._data = data.get_matched_profile_info_list( + self._data: list = data.get_matched_profile_info_list( self._request.profile_ids, self._request.namespace_id) async def _result_construct(self) -> None: @@ -145,7 +147,7 @@ async def _data_operate(self) -> None: elif self._request.request_type == SearchType.NICK_EMAIL_SEARCH: assert self._request.email assert self._request.nick - data.get_matched_info_by_nick_and_email( + self._data = data.get_matched_info_by_nick_and_email( self._request.nick, self._request.email) elif self._request.request_type == SearchType.UNIQUENICK_NAMESPACEID_SEARCH: assert self._request.uniquenick @@ -158,7 +160,11 @@ async def _data_operate(self) -> None: raise UniSpyException("search type invalid") async def _result_construct(self) -> None: - self._result = SearchResult(result=self._data) + data = [] + for d in self._data: + dd = SearchResultData(**d) + data.append(dd) + self._result = SearchResult(data=data) class SearchUniqueHandler(HandlerBase): @@ -169,7 +175,11 @@ async def _data_operate(self) -> None: self._request.uniquenick, self._request.namespace_ids) async def _result_construct(self) -> None: - self._result = SearchUniqueResult(data=self._data) + data = [] + for d in self._data: + dd = SearchResultData(**d) + data.append(dd) + self._result = SearchUniqueResult(data=data) class UniqueSearchHandler(HandlerBase): diff --git a/src/backends/protocols/gamespy/presence_search_player/requests.py b/src/backends/protocols/gamespy/presence_search_player/requests.py index 0e6df9e8e..a8c8b0faa 100644 --- a/src/backends/protocols/gamespy/presence_search_player/requests.py +++ b/src/backends/protocols/gamespy/presence_search_player/requests.py @@ -1,6 +1,6 @@ from typing import Optional from backends.library.abstractions.contracts import RequestBase as RB -from servers.presence_search_player.src.aggregates.enums import SearchType +from frontends.gamespy.protocols.presence_search_player.aggregates.enums import SearchType class RequestBase(RB): diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index 8560a1dcc..bead1fe9e 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -1,9 +1,9 @@ from typing import TYPE_CHECKING, Optional, cast from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, GroupList, Games, GameServerCaches -from servers.chat.src.aggregates.peer_room import PeerRoom -from servers.query_report.src.aggregates.game_server_info import GameServerInfo -from servers.query_report.src.aggregates.peer_room_info import PeerRoomInfo -from servers.server_browser.src.aggregates.exceptions import ServerBrowserException +from frontends.gamespy.protocols.chat.aggregates.peer_room import PeerRoom +from frontends.gamespy.protocols.query_report.aggregates.game_server_info import GameServerInfo +from frontends.gamespy.protocols.query_report.aggregates.peer_room_info import PeerRoomInfo +from frontends.gamespy.protocols.server_browser.aggregates.exceptions import ServerBrowserException def get_all_groups() -> dict: diff --git a/src/backends/protocols/gamespy/query_report/handlers.py b/src/backends/protocols/gamespy/query_report/handlers.py index 8b0db59d9..b58b99d00 100644 --- a/src/backends/protocols/gamespy/query_report/handlers.py +++ b/src/backends/protocols/gamespy/query_report/handlers.py @@ -2,7 +2,7 @@ from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import PG_SESSION, GameServerCaches from backends.protocols.gamespy.query_report.requests import * -from servers.query_report.src.aggregates.exceptions import QRException +from frontends.gamespy.protocols.query_report.aggregates.exceptions import QRException import backends.protocols.gamespy.query_report.data as data diff --git a/src/backends/protocols/gamespy/query_report/requests.py b/src/backends/protocols/gamespy/query_report/requests.py index e938e5db9..7c4e57e1d 100644 --- a/src/backends/protocols/gamespy/query_report/requests.py +++ b/src/backends/protocols/gamespy/query_report/requests.py @@ -2,7 +2,7 @@ from pydantic import UUID4, Field, constr import backends.library.abstractions.contracts as lib -from servers.query_report.src.v2.aggregates.enums import GameServerStatus, RequestType +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus, RequestType class RequestBase(lib.RequestBase): diff --git a/src/backends/protocols/gamespy/server_browser/handlers.py b/src/backends/protocols/gamespy/server_browser/handlers.py index c769feb02..bc824dbf4 100644 --- a/src/backends/protocols/gamespy/server_browser/handlers.py +++ b/src/backends/protocols/gamespy/server_browser/handlers.py @@ -2,11 +2,11 @@ from backends.library.abstractions.handler_base import HandlerBase from backends.protocols.gamespy.server_browser.requests import * import backends.protocols.gamespy.query_report.data as data -from servers.query_report.src.aggregates.game_server_info import GameServerInfo -from servers.query_report.src.aggregates.peer_room_info import PeerRoomInfo -from servers.server_browser.src.aggregates.exceptions import ServerBrowserException -from servers.server_browser.src.v2.aggregations.enums import GameServerFlags, ServerListUpdateOption -from servers.server_browser.src.v2.contracts.results import P2PGroupRoomListResult, SendMessageResult, ServerInfoResult, ServerMainListResult +from frontends.gamespy.protocols.query_report.aggregates.game_server_info import GameServerInfo +from frontends.gamespy.protocols.query_report.aggregates.peer_room_info import PeerRoomInfo +from frontends.gamespy.protocols.server_browser.aggregates.exceptions import ServerBrowserException +from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import GameServerFlags, ServerListUpdateOption +from frontends.gamespy.protocols.server_browser.v2.contracts.results import P2PGroupRoomListResult, SendMessageResult, ServerInfoResult, ServerMainListResult # region Server list diff --git a/src/backends/protocols/gamespy/server_browser/requests.py b/src/backends/protocols/gamespy/server_browser/requests.py index c75bdd152..3a03ff2fc 100644 --- a/src/backends/protocols/gamespy/server_browser/requests.py +++ b/src/backends/protocols/gamespy/server_browser/requests.py @@ -1,6 +1,6 @@ from typing import List, Optional import backends.library.abstractions.contracts as lib -from servers.server_browser.src.v2.aggregations.enums import RequestType, ServerListUpdateOption +from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import RequestType, ServerListUpdateOption class RequestBase(lib.RequestBase): diff --git a/src/backends/protocols/gamespy/web_services/data.py b/src/backends/protocols/gamespy/web_services/data.py index 9b573a8fa..8fd64dcf3 100644 --- a/src/backends/protocols/gamespy/web_services/data.py +++ b/src/backends/protocols/gamespy/web_services/data.py @@ -4,9 +4,9 @@ from typing import TYPE_CHECKING, cast, overload from backends.library.database.pg_orm import PG_SESSION, Profiles, SubProfiles, Users, SakeStorage -from servers.web_services.src.aggregations.exceptions import WebException -from servers.web_services.src.modules.auth.exceptions.general import AuthException -from servers.web_services.src.modules.sake.exceptions.general import SakeException +from frontends.gamespy.protocols.web_services.aggregations.exceptions import WebException +from frontends.gamespy.protocols.web_services.modules.auth.exceptions.general import AuthException +from frontends.gamespy.protocols.web_services.modules.sake.exceptions.general import SakeException def is_user_exist(uniquenick: str, cdkey: str, partner_id: int, namespace_id: int, email: str, password: str) -> None: diff --git a/src/backends/protocols/gamespy/web_services/handlers.py b/src/backends/protocols/gamespy/web_services/handlers.py index 6d6b1089c..0ca740443 100644 --- a/src/backends/protocols/gamespy/web_services/handlers.py +++ b/src/backends/protocols/gamespy/web_services/handlers.py @@ -5,8 +5,8 @@ from backends.library.abstractions.handler_base import HandlerBase from backends.protocols.gamespy.web_services.requests import * import backends.protocols.gamespy.web_services.data as data -from servers.web_services.src.modules.auth.contracts.results import LoginProfileResult -from servers.web_services.src.modules.direct2game.contracts.results import GetPurchaseHistoryResult, GetStoreAvailabilityResult +from frontends.gamespy.protocols.web_services.modules.auth.contracts.results import LoginProfileResult +from frontends.gamespy.protocols.web_services.modules.direct2game.contracts.results import GetPurchaseHistoryResult, GetStoreAvailabilityResult class LoginProfileHandler(HandlerBase): diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index b2823c1e3..c92e37fcb 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -1,12 +1,13 @@ import json from typing import Optional +from backends.protocols.gamespy.chat.handlers import CdKeyHandler, GetKeyHandler, GetUdpRelayHandler, InviteHandler from backends.protocols.gamespy.chat.requests import (ATMRequest, CdkeyRequest, GetCKeyRequest, GetChannelKeyRequest, GetKeyRequest, GetUdpRelayRequest, InviteRequest, JoinRequest, KickRequest, ListRequest, LoginRequest, ModeRequest, NamesRequest, NickRequest, NoticeRequest, PartRequest, PrivateRequest, QuitRequest, SetChannelKeyRequest, SetGroupRequest, SetKeyRequest, TopicRequest, UTMRequest, UserIPRequest, UserRequest, WhoIsRequest, WhoRequest) from backends.urls import CHAT from fastapi import APIRouter, FastAPI, WebSocket, WebSocketDisconnect -from library.src.configs import ServerConfig -from servers.chat.src.abstractions.contract import BrockerMessage +from frontends.gamespy.library.configs import ServerConfig +from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage router = APIRouter() channels: dict[str, list[WebSocket]] = {"test": []} @@ -73,125 +74,138 @@ async def websocket_endpoint(ws: WebSocket): channels[msg.channel_name].remove(ws) print("Client disconnected") +# region General + @router.post(f"{CHAT}/CdKeyHandler") -def cdkey(request: CdkeyRequest): - pass +async def cdkey(request: CdkeyRequest): + handler = CdKeyHandler(request) + await handler.handle() + return handler.response @router.post(f"{CHAT}/GetKeyHandler") -def getkey(request: GetKeyRequest): - pass +async def getkey(request: GetKeyRequest): + handler = GetKeyHandler(request) + await handler.handle() + return handler.response @router.post(f"{CHAT}/GetUdpRelayHandler") -def get_udp_relay(request: GetUdpRelayRequest): - pass +async def get_udp_relay(request: GetUdpRelayRequest): + handler = GetUdpRelayHandler(request) + await handler.handle() + return handler.response @router.post(f"{CHAT}/InviteHandler") -def invite(request: InviteRequest): - pass +async def invite(request: InviteRequest): + handler = InviteHandler(request) + await handler.handle() + return handler.response @router.post(f"{CHAT}/ListHandler") -def list_data(request: ListRequest): - pass +async def list_data(request: ListRequest): + # handler = ListHandler + raise NotImplementedError() @router.post(f"{CHAT}/LoginHandler") -def login(request: LoginRequest): +async def login(request: LoginRequest): pass @router.post(f"{CHAT}/NickHandler") -def nick(request: NickRequest): +async def nick(request: NickRequest): pass @router.post(f"{CHAT}/QuitHandler") -def quit(request: QuitRequest): +async def quit(request: QuitRequest): pass @router.post(f"{CHAT}/SetKeyHandler") -def set_key(request: SetKeyRequest): +async def set_key(request: SetKeyRequest): pass @router.post(f"{CHAT}/UserHandler") -def user(request: UserRequest): +async def user(request: UserRequest): pass @router.post(f"{CHAT}/UserIPHandler") -def user_ip(request: UserIPRequest): +async def user_ip(request: UserIPRequest): pass @router.post(f"{CHAT}/WhoHandler") -def who(request: WhoRequest): +async def who(request: WhoRequest): pass @router.post(f"{CHAT}/WhoIsHandler") -def whois(request: WhoIsRequest): +async def whois(request: WhoIsRequest): pass # region channel @router.post(f"{CHAT}/GetChannelKeyHandler") -def get_channel_key(request: GetChannelKeyRequest): +async def get_channel_key(request: GetChannelKeyRequest): pass @router.post(f"{CHAT}/GetCKeyHandler") -def get_ckey(request: GetCKeyRequest): +async def get_ckey(request: GetCKeyRequest): pass @router.post(f"{CHAT}/JoinHandler") -def join(request: JoinRequest): +async def join(request: JoinRequest): pass @router.post(f"{CHAT}/KickHandler") -def kick(request: KickRequest): +async def kick(request: KickRequest): pass @router.post(f"{CHAT}/ModeHandler") -def mode(request: ModeRequest): +async def mode(request: ModeRequest): pass @router.post(f"{CHAT}/NamesHandler") -def names(request: NamesRequest): +async def names(request: NamesRequest): pass @router.post(f"{CHAT}/PartHandler") -def part(request: PartRequest): +async def part(request: PartRequest): pass @router.post(f"{CHAT}/SetChannelKeyHandler") -def set_channel_key(request: SetChannelKeyRequest): +async def set_channel_key(request: SetChannelKeyRequest): pass @router.post(f"{CHAT}/SetGroupHandler") -def set_group(request: SetGroupRequest): +async def set_group(request: SetGroupRequest): pass @router.post(f"{CHAT}/TopicHandler") -def topic(request: TopicRequest): +async def topic(request: TopicRequest): pass +# region Message + @router.post(f"{CHAT}/ATMHandler") -def atm(request: ATMRequest): +async def atm(request: ATMRequest): pass @@ -201,12 +215,12 @@ def notice(request: NoticeRequest): @router.post(f"{CHAT}/PrivateHandler") -def private(request: PrivateRequest): +async def private(request: PrivateRequest): pass @router.post(f"{CHAT}/UTMHandler") -def utm(request: UTMRequest): +async def utm(request: UTMRequest): pass diff --git a/src/backends/routers/gamespy/natneg.py b/src/backends/routers/gamespy/natneg.py index 182a7b7f4..aea6c40a0 100644 --- a/src/backends/routers/gamespy/natneg.py +++ b/src/backends/routers/gamespy/natneg.py @@ -1,7 +1,7 @@ from fastapi import APIRouter from backends.protocols.gamespy.chat.requests import PingRequest -from backends.protocols.gamespy.natneg.handlers import InitHandler +from backends.protocols.gamespy.natneg.handlers import ConnectHandler, InitHandler from backends.protocols.gamespy.natneg.requests import AddressCheckRequest, ConnectRequest, ErtAckRequest, InitRequest, ReportRequest from backends.urls import NATNEG @@ -10,13 +10,15 @@ @router.post(f"{NATNEG}/AddressCheckHandler") -async def address_check(request: AddressCheckRequest) -> dict: +async def address_check(request: AddressCheckRequest): raise NotImplementedError() @router.post(f"{NATNEG}/ConnectHandler") async def connect(request: ConnectRequest): - raise NotImplementedError() + handler = ConnectHandler(request) + await handler.handle() + return handler.response @router.post(f"{NATNEG}/ErtAckHandler") diff --git a/src/backends/routers/gamespy/presence_search_player.py b/src/backends/routers/gamespy/presence_search_player.py index 6ded1bc14..e7a44fd16 100644 --- a/src/backends/routers/gamespy/presence_search_player.py +++ b/src/backends/routers/gamespy/presence_search_player.py @@ -1,7 +1,7 @@ from fastapi import APIRouter from backends.library.abstractions.contracts import Response -from backends.protocols.gamespy.presence_search_player.handlers import CheckHandler, NewUserHandler, NicksHandler, OthersHandler +from backends.protocols.gamespy.presence_search_player.handlers import CheckHandler, NewUserHandler, NicksHandler, OthersHandler, OthersListHandler, SearchHandler, SearchUniqueHandler, UniqueSearchHandler, ValidHandler from backends.protocols.gamespy.presence_search_player.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest from backends.urls import PRESENCE_SEARCH_PLAYER @@ -38,7 +38,9 @@ async def others(request: OthersRequest): @router.post(f"{PRESENCE_SEARCH_PLAYER}/OthersListHandler") async def others_list(request: OthersListRequest): - raise NotImplementedError() + handler = OthersListHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_SEARCH_PLAYER}/PMatchHandler") @@ -48,22 +50,30 @@ async def player_match(request: dict): @router.post(f"{PRESENCE_SEARCH_PLAYER}/SearchHandler") async def search(request: SearchRequest): - raise NotImplementedError() + handler = SearchHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_SEARCH_PLAYER}/SearchUniqueHandler") async def search_unique(request: SearchUniqueRequest): - raise NotImplementedError() + handler = SearchUniqueHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_SEARCH_PLAYER}/UniqueSearchHandler") async def unique_search(request: UniqueSearchRequest): - raise NotImplementedError() + handler = UniqueSearchHandler(request) + await handler.handle() + return handler.response @router.post(f"{PRESENCE_SEARCH_PLAYER}/ValidHandler") async def valid(request: ValidRequest): - raise NotImplementedError() + handler = ValidHandler(request) + await handler.handle() + return handler.response if __name__ == "__main__": diff --git a/src/backends/routers/gamespy/server_browser.py b/src/backends/routers/gamespy/server_browser.py index 5db83813b..fe1b54cee 100644 --- a/src/backends/routers/gamespy/server_browser.py +++ b/src/backends/routers/gamespy/server_browser.py @@ -1,4 +1,5 @@ from fastapi import APIRouter, WebSocket, WebSocketDisconnect +from backends.protocols.gamespy.server_browser.handlers import ServerInfoHandler, ServerListHandler from backends.protocols.gamespy.server_browser.requests import SendMessageRequest, ServerInfoRequest, ServerListRequest from backends.urls import SERVER_BROWSER_V1, SERVER_BROWSER_V2 @@ -31,12 +32,16 @@ async def send_message(request: SendMessageRequest): @router.post(f"{SERVER_BROWSER_V2}/ServerInfoHandler") async def server_info(request: ServerInfoRequest): - raise NotImplementedError() + handler = ServerInfoHandler(request) + await handler.handle() + return handler.response @router.post(f"{SERVER_BROWSER_V2}/ServerListHandler") async def server_list(request: ServerListRequest): - raise NotImplementedError() + handler = ServerListHandler(request) + await handler.handle() + return handler.response if __name__ == "__main__": diff --git a/src/backends/routers/gamespy/webservices.py b/src/backends/routers/gamespy/webservices.py index e0ab2985a..36b2b5020 100644 --- a/src/backends/routers/gamespy/webservices.py +++ b/src/backends/routers/gamespy/webservices.py @@ -1,5 +1,6 @@ from fastapi import APIRouter +from backends.protocols.gamespy.web_services.handlers import CreateRecordHandler, GetMyRecordsHandler, LoginProfileHandler, LoginRemoteAuthHandler, LoginUniqueNickHandler, SearchForRecordsHandler from backends.urls import WEB_SERVICES from backends.protocols.gamespy.web_services.requests import CreateRecordRequest, GetMyRecordsRequest, LoginProfileRequest, LoginRemoteAuthRequest, LoginUniqueNickRequest, SearchForRecordsRequest @@ -7,6 +8,7 @@ # Altas services + @router.post(f"{WEB_SERVICES}/Altas/CreateRecordHandler") async def create_matchless_session(request): raise NotImplementedError() @@ -30,23 +32,31 @@ async def submit_report(request): # Auth services @router.post(f"{WEB_SERVICES}/Auth/LoginProfileHandler") async def login_profile(request: LoginProfileRequest): - raise NotImplementedError() + handler = LoginProfileHandler(request) + await handler.handle() + return handler.response @router.post(f"{WEB_SERVICES}/Auth/LoginRemoteAuthHandler") async def login_remote_auth(request: LoginRemoteAuthRequest): - raise NotImplementedError() + handler = LoginRemoteAuthHandler(request) + await handler.handle() + return handler.response @router.post(f"{WEB_SERVICES}/Auth/LoginUniqueNickHandler") async def login_uniquenick(request: LoginUniqueNickRequest): - raise NotImplementedError() + handler = LoginUniqueNickHandler(request) + await handler.handle() + return handler.response # SAKE services @router.post(f"{WEB_SERVICES}/Sake/CreateRecordHandler") async def create_record(request: CreateRecordRequest): - raise NotImplementedError() + handler = CreateRecordHandler(request) + await handler.handle() + return handler.response @router.post(f"{WEB_SERVICES}/Sake/DeleteRecordHandler") @@ -56,7 +66,9 @@ async def delete_record(request): @router.post(f"{WEB_SERVICES}/Sake/GetMyRecordsHandler") async def get_my_records(request: GetMyRecordsRequest): - raise NotImplementedError() + handler = GetMyRecordsHandler(request) + await handler.handle() + return handler.response @router.post(f"{WEB_SERVICES}/Sake/GetRandomRecordsHandler") @@ -76,7 +88,9 @@ async def rate_record(request): @router.post(f"{WEB_SERVICES}/Sake/SearchForRecordsHandler") async def search_for_records(request: SearchForRecordsRequest): - raise NotImplementedError() + handler = SearchForRecordsHandler(request) + await handler.handle() + return handler.response @router.post(f"{WEB_SERVICES}/Sake/UpdateRecordHandler") diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index b32b5ebb6..61ee7e71e 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -5,8 +5,8 @@ from pydantic import BaseModel import uvicorn -from library.src.log.log_manager import LogManager -from library.src.configs import ServerConfig +from frontends.gamespy.library.log.log_manager import LogManager +from frontends.gamespy.library.configs import ServerConfig from backends.routers.gamespy import chat, gstats, natneg, presence_connection_manager, presence_search_player, query_report, server_browser, webservices app = FastAPI() diff --git a/src/backends/tests/gamespy/chat/room_tests.py b/src/backends/tests/gamespy/chat/room_tests.py index c0c9da40a..9b6061122 100644 --- a/src/backends/tests/gamespy/chat/room_tests.py +++ b/src/backends/tests/gamespy/chat/room_tests.py @@ -1,10 +1,10 @@ # from typing import Optional # import unittest -# from library.tests.mock_objects import BrokerMock -# from servers.chat.src.aggregates.channel import Channel -# from servers.chat.src.aggregates.channel_user import ChannelUser -# from servers.chat.tests.mock_objects import create_client +# from frontends.gamespy.library.tests.mock_objects import BrokerMock +# from frontends.gamespy.protocols.chat.aggregates.channel import Channel +# from frontends.gamespy.protocols.chat.aggregates.channel_user import ChannelUser +# from frontends.gamespy.protocols.chat.tests.mock_objects import create_client # class RoomTests(unittest.TestCase): diff --git a/src/backends/tests/gamespy/precence_search_player/requests_tests.py b/src/backends/tests/gamespy/precence_search_player/requests_tests.py index bb87b4a04..5e85f3304 100644 --- a/src/backends/tests/gamespy/precence_search_player/requests_tests.py +++ b/src/backends/tests/gamespy/precence_search_player/requests_tests.py @@ -1,8 +1,8 @@ # the total requests tests import unittest -import servers.presence_search_player.src.contracts.requests as psp -from servers.presence_search_player.tests.handler_tests import ( +import frontends.gamespy.protocols.presence_search_player.contracts.requests as psp +from frontends.tests.gamespy.presence_search_player.handler_tests import ( NICKS, SEARCH_1, SEARCH_2, SEARCH_3, SEARCH_4, CHECK1, NEWUSER, SEARCH_UNIQUENICK, SUGGEST_UNIQUE, VALID) import backends.protocols.gamespy.presence_search_player.requests as bk diff --git a/src/backends/tests/gamespy/query_report/data_fetch_tests.py b/src/backends/tests/gamespy/query_report/data_fetch_tests.py index a84312cd3..67aa2cdb6 100644 --- a/src/backends/tests/gamespy/query_report/data_fetch_tests.py +++ b/src/backends/tests/gamespy/query_report/data_fetch_tests.py @@ -4,7 +4,7 @@ from pydantic import ValidationError from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches import backends.protocols.gamespy.query_report.data as data -from servers.query_report.src.aggregates.game_server_info import GameServerInfo +from frontends.gamespy.protocols.query_report.aggregates.game_server_info import GameServerInfo class DataFetchTests(unittest.TestCase): @@ -17,9 +17,7 @@ def test_get_peer_staging_channels(self): room_name="unispy_test_room_name", group_id=0, max_num_user=100, key_values={}, invited_nicks={}, update_time=datetime.now(timezone.utc)) PG_SESSION.add(cache) PG_SESSION.commit() - self.assertRaises(ValidationError,data.get_peer_staging_channels,"unispy_test_game_name", 0) + self.assertRaises( + ValidationError, data.get_peer_staging_channels, "unispy_test_game_name", 0) PG_SESSION.delete(cache) PG_SESSION.commit() - - - \ No newline at end of file diff --git a/src/library/__init__.py b/src/frontends/__init__.py similarity index 100% rename from src/library/__init__.py rename to src/frontends/__init__.py diff --git a/src/library/src/__init__.py b/src/frontends/gamespy/library/abstractions/__init__.py similarity index 100% rename from src/library/src/__init__.py rename to src/frontends/gamespy/library/abstractions/__init__.py diff --git a/src/library/src/abstractions/brocker.py b/src/frontends/gamespy/library/abstractions/brocker.py similarity index 100% rename from src/library/src/abstractions/brocker.py rename to src/frontends/gamespy/library/abstractions/brocker.py diff --git a/src/library/src/abstractions/client.py b/src/frontends/gamespy/library/abstractions/client.py similarity index 81% rename from src/library/src/abstractions/client.py rename to src/frontends/gamespy/library/abstractions/client.py index dacfaf65a..ac637d0ff 100644 --- a/src/library/src/abstractions/client.py +++ b/src/frontends/gamespy/library/abstractions/client.py @@ -1,18 +1,18 @@ -from library.src.encryption.encoding import Encoding -from library.src.exceptions.general import UniSpyException -from library.src.log.log_manager import LogWriter -from library.src.log.log_manager import LogWriter -from library.src.configs import ServerConfig +from frontends.gamespy.library.encryption.encoding import Encoding +from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.library.log.log_manager import LogWriter +from frontends.gamespy.library.log.log_manager import LogWriter +from frontends.gamespy.library.configs import ServerConfig import threading from typing import TYPE_CHECKING, Optional if TYPE_CHECKING: - from library.src.abstractions.connections import ConnectionBase - from library.src.abstractions.handler import CmdHandlerBase - from library.src.abstractions.switcher import SwitcherBase - from library.src.abstractions.enctypt_base import EncryptBase - from library.src.abstractions.contracts import ResponseBase - from library.src.abstractions.client import ClientInfoBase + from frontends.gamespy.library.abstractions.connections import ConnectionBase + from frontends.gamespy.library.abstractions.handler import CmdHandlerBase + from frontends.gamespy.library.abstractions.switcher import SwitcherBase + from frontends.gamespy.library.abstractions.enctypt_base import EncryptBase + from frontends.gamespy.library.abstractions.contracts import ResponseBase + from frontends.gamespy.library.abstractions.client import ClientInfoBase class ClientInfoBase: @@ -36,7 +36,7 @@ def __init__( ): assert isinstance(server_config, ServerConfig) assert isinstance(logger, LogWriter) - from library.src.abstractions.connections import ConnectionBase + from frontends.gamespy.library.abstractions.connections import ConnectionBase assert issubclass(type(connection), ConnectionBase) self.server_config = server_config self.connection = connection @@ -80,7 +80,7 @@ def decrypt_message(self, buffer: bytes) -> bytes: return buffer def send(self, response: "ResponseBase") -> None: - from library.src.abstractions.contracts import ResponseBase + from frontends.gamespy.library.abstractions.contracts import ResponseBase assert issubclass(type(response), ResponseBase) response.build() diff --git a/src/library/src/abstractions/connections.py b/src/frontends/gamespy/library/abstractions/connections.py similarity index 90% rename from src/library/src/abstractions/connections.py rename to src/frontends/gamespy/library/abstractions/connections.py index 0b913fe04..47cd22020 100644 --- a/src/library/src/abstractions/connections.py +++ b/src/frontends/gamespy/library/abstractions/connections.py @@ -1,15 +1,15 @@ import abc import socketserver from typing import Optional -from library.src.abstractions.client import ClientBase +from frontends.gamespy.library.abstractions.client import ClientBase -from library.src.log.log_manager import LogWriter -from library.src.configs import ServerConfig +from frontends.gamespy.library.log.log_manager import LogWriter +from frontends.gamespy.library.configs import ServerConfig from typing import TYPE_CHECKING if TYPE_CHECKING: - from library.src.network.http_handler import HttpRequest + from frontends.gamespy.library.network.http_handler import HttpRequest class ConnectionBase: diff --git a/src/library/src/abstractions/contracts.py b/src/frontends/gamespy/library/abstractions/contracts.py similarity index 100% rename from src/library/src/abstractions/contracts.py rename to src/frontends/gamespy/library/abstractions/contracts.py diff --git a/src/library/src/abstractions/enctypt_base.py b/src/frontends/gamespy/library/abstractions/enctypt_base.py similarity index 100% rename from src/library/src/abstractions/enctypt_base.py rename to src/frontends/gamespy/library/abstractions/enctypt_base.py diff --git a/src/library/src/abstractions/handler.py b/src/frontends/gamespy/library/abstractions/handler.py similarity index 92% rename from src/library/src/abstractions/handler.py rename to src/frontends/gamespy/library/abstractions/handler.py index 96a49b2ae..49680bc94 100644 --- a/src/library/src/abstractions/handler.py +++ b/src/frontends/gamespy/library/abstractions/handler.py @@ -1,14 +1,14 @@ import json -from library.src.abstractions.client import ClientBase -from library.src.exceptions.general import UniSpyException +from frontends.gamespy.library.abstractions.client import ClientBase +from frontends.gamespy.library.exceptions.general import UniSpyException from typing import Optional, Type import requests -from library.src.configs import CONFIG +from frontends.gamespy.library.configs import CONFIG # if TYPE_CHECKING: -from library.src.abstractions.contracts import RequestBase, ResultBase, ResponseBase -from library.src.extentions.encoding import UniSpyJsonEncoder +from frontends.gamespy.library.abstractions.contracts import RequestBase, ResultBase, ResponseBase +from frontends.gamespy.library.extentions.encoding import UniSpyJsonEncoder class CmdHandlerBase: diff --git a/src/library/src/abstractions/redis_objects.py b/src/frontends/gamespy/library/abstractions/redis_objects.py similarity index 99% rename from src/library/src/abstractions/redis_objects.py rename to src/frontends/gamespy/library/abstractions/redis_objects.py index 032aa6f2b..b3301f530 100644 --- a/src/library/src/abstractions/redis_objects.py +++ b/src/frontends/gamespy/library/abstractions/redis_objects.py @@ -7,7 +7,7 @@ # from redis import Redis -# from library.src.exceptions.general import UniSpyException +# from frontends.gamespy.library.exceptions.general import UniSpyException # import json diff --git a/src/library/src/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py similarity index 90% rename from src/library/src/abstractions/server_launcher.py rename to src/frontends/gamespy/library/abstractions/server_launcher.py index d89acdddd..5967e8c14 100644 --- a/src/library/src/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -1,10 +1,10 @@ import signal from types import MappingProxyType from typing import Optional -from library.src.abstractions.connections import NetworkServerBase -from library.src.exceptions.general import UniSpyException -from library.src.log.log_manager import LogManager, LogWriter -from library.src.configs import CONFIG, ServerConfig +from frontends.gamespy.library.abstractions.connections import NetworkServerBase +from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.library.log.log_manager import LogManager, LogWriter +from frontends.gamespy.library.configs import CONFIG, ServerConfig import pyfiglet import requests from prettytable import PrettyTable diff --git a/src/library/src/abstractions/switcher.py b/src/frontends/gamespy/library/abstractions/switcher.py similarity index 89% rename from src/library/src/abstractions/switcher.py rename to src/frontends/gamespy/library/abstractions/switcher.py index 00c943711..27044a9df 100644 --- a/src/library/src/abstractions/switcher.py +++ b/src/frontends/gamespy/library/abstractions/switcher.py @@ -1,6 +1,6 @@ from abc import abstractmethod -from library.src.abstractions.client import ClientBase -from library.src.abstractions.handler import CmdHandlerBase +from frontends.gamespy.library.abstractions.client import ClientBase +from frontends.gamespy.library.abstractions.handler import CmdHandlerBase from typing import Optional @@ -30,7 +30,7 @@ def __init__(self, client: ClientBase, raw_request: bytes | str) -> None: """ def handle(self): - from library.src.exceptions.general import UniSpyException + from frontends.gamespy.library.exceptions.general import UniSpyException try: self._process_raw_request() diff --git a/src/library/src/configs.py b/src/frontends/gamespy/library/configs.py similarity index 100% rename from src/library/src/configs.py rename to src/frontends/gamespy/library/configs.py diff --git a/src/library/src/abstractions/__init__.py b/src/frontends/gamespy/library/encryption/__init__.py similarity index 100% rename from src/library/src/abstractions/__init__.py rename to src/frontends/gamespy/library/encryption/__init__.py diff --git a/src/library/src/encryption/encoding.py b/src/frontends/gamespy/library/encryption/encoding.py similarity index 100% rename from src/library/src/encryption/encoding.py rename to src/frontends/gamespy/library/encryption/encoding.py diff --git a/src/library/src/encryption/gs_encryption.py b/src/frontends/gamespy/library/encryption/gs_encryption.py similarity index 97% rename from src/library/src/encryption/gs_encryption.py rename to src/frontends/gamespy/library/encryption/gs_encryption.py index cded38fd1..7177543a5 100644 --- a/src/library/src/encryption/gs_encryption.py +++ b/src/frontends/gamespy/library/encryption/gs_encryption.py @@ -1,4 +1,4 @@ -from library.src.abstractions.enctypt_base import EncryptBase +from frontends.gamespy.library.abstractions.enctypt_base import EncryptBase import hashlib DIGITS_HEX = "0123456789abcdef" diff --git a/src/library/src/encryption/xor_encryption.py b/src/frontends/gamespy/library/encryption/xor_encryption.py similarity index 94% rename from src/library/src/encryption/xor_encryption.py rename to src/frontends/gamespy/library/encryption/xor_encryption.py index 1436389b0..ae144eee5 100644 --- a/src/library/src/encryption/xor_encryption.py +++ b/src/frontends/gamespy/library/encryption/xor_encryption.py @@ -1,7 +1,7 @@ from enum import IntEnum import base64 -from library.src.abstractions.enctypt_base import EncryptBase +from frontends.gamespy.library.abstractions.enctypt_base import EncryptBase class XorType(IntEnum): diff --git a/src/library/src/encryption/__init__.py b/src/frontends/gamespy/library/exceptions/__init__.py similarity index 100% rename from src/library/src/encryption/__init__.py rename to src/frontends/gamespy/library/exceptions/__init__.py diff --git a/src/library/src/exceptions/general.py b/src/frontends/gamespy/library/exceptions/general.py similarity index 89% rename from src/library/src/exceptions/general.py rename to src/frontends/gamespy/library/exceptions/general.py index 361a76330..1e9c09ca7 100644 --- a/src/library/src/exceptions/general.py +++ b/src/frontends/gamespy/library/exceptions/general.py @@ -1,11 +1,11 @@ from typing import TYPE_CHECKING, Optional -from library.src.log.log_manager import GLOBAL_LOGGER +from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER if TYPE_CHECKING: - from library.src.abstractions.client import ClientBase + from frontends.gamespy.library.abstractions.client import ClientBase class UniSpyException(Exception): diff --git a/src/library/src/exceptions/__init__.py b/src/frontends/gamespy/library/extentions/__init__.py similarity index 100% rename from src/library/src/exceptions/__init__.py rename to src/frontends/gamespy/library/extentions/__init__.py diff --git a/src/library/src/extentions/bytes_extentions.py b/src/frontends/gamespy/library/extentions/bytes_extentions.py similarity index 100% rename from src/library/src/extentions/bytes_extentions.py rename to src/frontends/gamespy/library/extentions/bytes_extentions.py diff --git a/src/library/src/extentions/encoding.py b/src/frontends/gamespy/library/extentions/encoding.py similarity index 100% rename from src/library/src/extentions/encoding.py rename to src/frontends/gamespy/library/extentions/encoding.py diff --git a/src/library/src/extentions/gamespy_ramdoms.py b/src/frontends/gamespy/library/extentions/gamespy_ramdoms.py similarity index 100% rename from src/library/src/extentions/gamespy_ramdoms.py rename to src/frontends/gamespy/library/extentions/gamespy_ramdoms.py diff --git a/src/library/src/extentions/gamespy_utils.py b/src/frontends/gamespy/library/extentions/gamespy_utils.py similarity index 100% rename from src/library/src/extentions/gamespy_utils.py rename to src/frontends/gamespy/library/extentions/gamespy_utils.py diff --git a/src/library/src/extentions/password_encoder.py b/src/frontends/gamespy/library/extentions/password_encoder.py similarity index 96% rename from src/library/src/extentions/password_encoder.py rename to src/frontends/gamespy/library/extentions/password_encoder.py index 92424e978..bb1769d13 100644 --- a/src/library/src/extentions/password_encoder.py +++ b/src/frontends/gamespy/library/extentions/password_encoder.py @@ -1,7 +1,7 @@ import hashlib import base64 -from library.src.exceptions.general import UniSpyException +from frontends.gamespy.library.exceptions.general import UniSpyException def process_password(request: dict): diff --git a/src/library/src/extentions/redis_orm.py b/src/frontends/gamespy/library/extentions/redis_orm.py similarity index 100% rename from src/library/src/extentions/redis_orm.py rename to src/frontends/gamespy/library/extentions/redis_orm.py diff --git a/src/library/src/extentions/string_extentions.py b/src/frontends/gamespy/library/extentions/string_extentions.py similarity index 100% rename from src/library/src/extentions/string_extentions.py rename to src/frontends/gamespy/library/extentions/string_extentions.py diff --git a/src/library/src/extentions/__init__.py b/src/frontends/gamespy/library/log/__init__.py similarity index 100% rename from src/library/src/extentions/__init__.py rename to src/frontends/gamespy/library/log/__init__.py diff --git a/src/library/src/log/log_manager.py b/src/frontends/gamespy/library/log/log_manager.py similarity index 97% rename from src/library/src/log/log_manager.py rename to src/frontends/gamespy/library/log/log_manager.py index 32fc4a893..55e44807e 100644 --- a/src/library/src/log/log_manager.py +++ b/src/frontends/gamespy/library/log/log_manager.py @@ -2,7 +2,7 @@ from logging.handlers import TimedRotatingFileHandler import os -from library.src.configs import CONFIG +from frontends.gamespy.library.configs import CONFIG class LogWriter: diff --git a/src/library/src/network/__init__.py b/src/frontends/gamespy/library/network/__init__.py similarity index 100% rename from src/library/src/network/__init__.py rename to src/frontends/gamespy/library/network/__init__.py diff --git a/src/library/src/network/brockers.py b/src/frontends/gamespy/library/network/brockers.py similarity index 94% rename from src/library/src/network/brockers.py rename to src/frontends/gamespy/library/network/brockers.py index cfbd82436..bc9d5a4f6 100644 --- a/src/library/src/network/brockers.py +++ b/src/frontends/gamespy/library/network/brockers.py @@ -3,10 +3,10 @@ from typing import Optional import websocket from redis import Redis -from library.src.abstractions.brocker import BrockerBase +from frontends.gamespy.library.abstractions.brocker import BrockerBase from redis.client import PubSub -from servers.chat.src.aggregates.exceptions import ChatException +from frontends.gamespy.protocols.chat.aggregates.exceptions import ChatException websocket.enableTrace(True) diff --git a/src/library/src/network/http_handler.py b/src/frontends/gamespy/library/network/http_handler.py similarity index 89% rename from src/library/src/network/http_handler.py rename to src/frontends/gamespy/library/network/http_handler.py index 72d83f384..df3d497cf 100644 --- a/src/library/src/network/http_handler.py +++ b/src/frontends/gamespy/library/network/http_handler.py @@ -1,8 +1,8 @@ from urllib.parse import urlparse -from library.src.abstractions.client import ClientBase -from library.src.abstractions.connections import ConnectionBase, NetworkServerBase +from frontends.gamespy.library.abstractions.client import ClientBase +from frontends.gamespy.library.abstractions.connections import ConnectionBase, NetworkServerBase from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer -from library.src.configs import CONFIG +from frontends.gamespy.library.configs import CONFIG class HttpRequest: @@ -79,6 +79,6 @@ def on_connected(self) -> None: if __name__ == "__main__": # create_http_server(list(CONFIG.servers.values())[0], ClientBase) - from library.tests.mock_objects import LogMock + from frontends.tests.gamespy.library.mock_objects import LogMock s = HttpServer(list(CONFIG.servers.values())[0], TestClient, LogMock()) diff --git a/src/library/src/network/tcp_handler.py b/src/frontends/gamespy/library/network/tcp_handler.py similarity index 85% rename from src/library/src/network/tcp_handler.py rename to src/frontends/gamespy/library/network/tcp_handler.py index 866be421c..a967cab1d 100644 --- a/src/library/src/network/tcp_handler.py +++ b/src/frontends/gamespy/library/network/tcp_handler.py @@ -1,11 +1,11 @@ import socket import socketserver from typing import Optional -from library.src.abstractions.client import ClientBase -from library.src.abstractions.connections import ConnectionBase, NetworkServerBase +from frontends.gamespy.library.abstractions.client import ClientBase +from frontends.gamespy.library.abstractions.connections import ConnectionBase, NetworkServerBase -from library.src.network import DATA_SIZE -from library.src.configs import CONFIG +from frontends.gamespy.library.network import DATA_SIZE +from frontends.gamespy.library.configs import CONFIG class TcpConnection(ConnectionBase): @@ -70,7 +70,7 @@ def on_connected(self) -> None: if __name__ == "__main__": - from library.tests.mock_objects import LogMock + from frontends.tests.gamespy.library.mock_objects import LogMock s = TcpServer(list(CONFIG.servers.values())[0], TestClient, LogMock()) s.start() diff --git a/src/library/src/network/udp_handler.py b/src/frontends/gamespy/library/network/udp_handler.py similarity index 84% rename from src/library/src/network/udp_handler.py rename to src/frontends/gamespy/library/network/udp_handler.py index 6c7507dbd..d24854720 100644 --- a/src/library/src/network/udp_handler.py +++ b/src/frontends/gamespy/library/network/udp_handler.py @@ -1,9 +1,9 @@ import socket import socketserver -from library.src.abstractions.client import ClientBase -from library.src.abstractions.connections import ConnectionBase, NetworkServerBase -from library.src.configs import CONFIG +from frontends.gamespy.library.abstractions.client import ClientBase +from frontends.gamespy.library.abstractions.connections import ConnectionBase, NetworkServerBase +from frontends.gamespy.library.configs import CONFIG class UdpConnection(ConnectionBase): @@ -55,7 +55,7 @@ def on_connected(self) -> None: if __name__ == "__main__": # create_udp_server(list(CONFIG.servers.values())[0], ClientBase) - from library.tests.mock_objects import LogMock + from frontends.tests.gamespy.library.mock_objects import LogMock s = UdpServer(list(CONFIG.servers.values())[0], TestClient, LogMock()) s.start() diff --git a/src/library/src/log/__init__.py b/src/frontends/gamespy/protocols/__init__.py similarity index 100% rename from src/library/src/log/__init__.py rename to src/frontends/gamespy/protocols/__init__.py diff --git a/src/library/tests/__init__.py b/src/frontends/gamespy/protocols/chat/__init__.py similarity index 100% rename from src/library/tests/__init__.py rename to src/frontends/gamespy/protocols/chat/__init__.py diff --git a/src/servers/chat/src/abstractions/contract.py b/src/frontends/gamespy/protocols/chat/abstractions/contract.py similarity index 90% rename from src/servers/chat/src/abstractions/contract.py rename to src/frontends/gamespy/protocols/chat/abstractions/contract.py index 1eaacc9ec..34598cf92 100644 --- a/src/servers/chat/src/abstractions/contract.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/contract.py @@ -2,10 +2,10 @@ from uuid import UUID from pydantic import BaseModel -import library.src.abstractions.contracts +import frontends.gamespy.library.abstractions.contracts as lib -class RequestBase(library.src.abstractions.contracts.RequestBase): +class RequestBase(lib.RequestBase): raw_request: str command_name: str _prefix: Optional[str] @@ -59,14 +59,14 @@ def parse(self) -> None: self._cmd_params = dataFrag[1:] -class ResultBase(library.src.abstractions.contracts.ResultBase): +class ResultBase(lib.ResultBase): pass SERVER_DOMAIN = "unispy.net" -class ResponseBase(library.src.abstractions.contracts.ResponseBase): +class ResponseBase(lib.ResponseBase): sending_buffer: str _result: ResultBase _request: RequestBase diff --git a/src/servers/chat/src/abstractions/handler.py b/src/frontends/gamespy/protocols/chat/abstractions/handler.py similarity index 79% rename from src/servers/chat/src/abstractions/handler.py rename to src/frontends/gamespy/protocols/chat/abstractions/handler.py index 3064af458..f18a38666 100644 --- a/src/servers/chat/src/abstractions/handler.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/handler.py @@ -1,20 +1,20 @@ -from library.src.abstractions.brocker import BrockerBase -from library.src.configs import CONFIG -from library.src.network.brockers import WebsocketBrocker -from servers.chat.src.aggregates.enums import MessageType -from servers.chat.src.abstractions.contract import ResultBase -from servers.chat.src.aggregates.exceptions import ChatException, NoSuchNickException, NoSuchChannelException -from servers.chat.src.abstractions.contract import RequestBase -from servers.chat.src.abstractions.contract import * -from library.src.abstractions.client import ClientBase -from servers.chat.src.abstractions.contract import RequestBase, ResultBase -from servers.chat.src.applications.client import Client -from servers.chat.src.aggregates.exceptions import IRCException -import library.src.abstractions.handler +from frontends.gamespy.library.abstractions.brocker import BrockerBase +from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.library.network.brockers import WebsocketBrocker +from frontends.gamespy.protocols.chat.aggregates.enums import MessageType +from frontends.gamespy.protocols.chat.abstractions.contract import ResultBase +from frontends.gamespy.protocols.chat.aggregates.exceptions import ChatException, NoSuchNickException, NoSuchChannelException +from frontends.gamespy.protocols.chat.abstractions.contract import RequestBase +from frontends.gamespy.protocols.chat.abstractions.contract import * +from frontends.gamespy.library.abstractions.client import ClientBase +from frontends.gamespy.protocols.chat.abstractions.contract import RequestBase, ResultBase +from frontends.gamespy.protocols.chat.applications.client import Client +from frontends.gamespy.protocols.chat.aggregates.exceptions import IRCException +import frontends.gamespy.library.abstractions.handler as lib from typing import cast -class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase): +class CmdHandlerBase(lib.CmdHandlerBase): _client: Client _request: RequestBase _result: ResultBase diff --git a/src/servers/chat/src/aggregates/enums.py b/src/frontends/gamespy/protocols/chat/aggregates/enums.py similarity index 100% rename from src/servers/chat/src/aggregates/enums.py rename to src/frontends/gamespy/protocols/chat/aggregates/enums.py diff --git a/src/servers/chat/src/aggregates/exceptions.py b/src/frontends/gamespy/protocols/chat/aggregates/exceptions.py similarity index 94% rename from src/servers/chat/src/aggregates/exceptions.py rename to src/frontends/gamespy/protocols/chat/aggregates/exceptions.py index f04a7cdb0..e8cf2b469 100644 --- a/src/servers/chat/src/aggregates/exceptions.py +++ b/src/frontends/gamespy/protocols/chat/aggregates/exceptions.py @@ -1,7 +1,7 @@ -from servers.chat.src.abstractions.contract import SERVER_DOMAIN -from servers.chat.src.aggregates.enums import IRCErrorCode +from frontends.gamespy.protocols.chat.abstractions.contract import SERVER_DOMAIN +from frontends.gamespy.protocols.chat.aggregates.enums import IRCErrorCode -from library.src.exceptions.general import UniSpyException as ER +from frontends.gamespy.library.exceptions.general import UniSpyException as ER class ChatException(ER): diff --git a/src/servers/chat/src/aggregates/peer_room.py b/src/frontends/gamespy/protocols/chat/aggregates/peer_room.py similarity index 96% rename from src/servers/chat/src/aggregates/peer_room.py rename to src/frontends/gamespy/protocols/chat/aggregates/peer_room.py index d8dc04c21..782442ac7 100644 --- a/src/servers/chat/src/aggregates/peer_room.py +++ b/src/frontends/gamespy/protocols/chat/aggregates/peer_room.py @@ -1,4 +1,4 @@ -from servers.chat.src.aggregates.enums import PeerRoomType +from frontends.gamespy.protocols.chat.aggregates.enums import PeerRoomType class PeerRoom: diff --git a/src/servers/chat/src/aggregates/response_name.py b/src/frontends/gamespy/protocols/chat/aggregates/response_name.py similarity index 100% rename from src/servers/chat/src/aggregates/response_name.py rename to src/frontends/gamespy/protocols/chat/aggregates/response_name.py diff --git a/src/servers/chat/src/applications/client.py b/src/frontends/gamespy/protocols/chat/applications/client.py similarity index 66% rename from src/servers/chat/src/applications/client.py rename to src/frontends/gamespy/protocols/chat/applications/client.py index 2a9b5f7bd..556f0f1d4 100644 --- a/src/servers/chat/src/applications/client.py +++ b/src/frontends/gamespy/protocols/chat/applications/client.py @@ -1,9 +1,9 @@ -from library.src.abstractions.client import ClientBase +from frontends.gamespy.library.abstractions.client import ClientBase -from library.src.abstractions.switcher import SwitcherBase -from library.src.log.log_manager import LogWriter -from library.src.network.tcp_handler import TcpConnection -from library.src.configs import ServerConfig +from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.library.log.log_manager import LogWriter +from frontends.gamespy.library.network.tcp_handler import TcpConnection +from frontends.gamespy.library.configs import ServerConfig from typing import TYPE_CHECKING, Optional @@ -32,5 +32,5 @@ def __init__(self, connection: TcpConnection, server_config: ServerConfig, logge self.info = ClientInfo() def _create_switcher(self, buffer: bytes) -> SwitcherBase: - from servers.chat.src.applications.switcher import Switcher + from frontends.gamespy.protocols.chat.applications.switcher import Switcher return Switcher(self, buffer.decode()) diff --git a/src/servers/chat/src/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py similarity index 95% rename from src/servers/chat/src/applications/handlers.py rename to src/frontends/gamespy/protocols/chat/applications/handlers.py index 52a0a0c49..6a85f915c 100644 --- a/src/servers/chat/src/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -1,4 +1,4 @@ -from servers.chat.src.contracts.results import ( +from frontends.gamespy.protocols.chat.contracts.results import ( ATMResult, NoticeResult, PrivateResult, @@ -22,7 +22,7 @@ WhoIsResult, WhoResult, ) -from servers.chat.src.contracts.responses import ( +from frontends.gamespy.protocols.chat.contracts.responses import ( ATMResponse, NoticeResponse, PrivateResponse, @@ -48,7 +48,7 @@ WhoResponse, ) -from servers.chat.src.contracts.requests import ( +from frontends.gamespy.protocols.chat.contracts.requests import ( ATMRequest, NoticeRequest, PrivateRequest, @@ -79,12 +79,12 @@ WhoRequest, GetUdpRelayRequest ) -from servers.chat.src.aggregates.enums import ModeRequestType, TopicRequestType -from servers.chat.src.aggregates.response_name import * +from frontends.gamespy.protocols.chat.aggregates.enums import ModeRequestType, TopicRequestType +from frontends.gamespy.protocols.chat.aggregates.response_name import * from typing import Type -from library.src.abstractions.client import ClientBase -from servers.chat.src.abstractions.contract import RequestBase -from servers.chat.src.abstractions.handler import ChannelHandlerBase, CmdHandlerBase, MessageHandlerBase, PostLoginHandlerBase +from frontends.gamespy.library.abstractions.client import ClientBase +from frontends.gamespy.protocols.chat.abstractions.contract import RequestBase +from frontends.gamespy.protocols.chat.abstractions.handler import ChannelHandlerBase, CmdHandlerBase, MessageHandlerBase, PostLoginHandlerBase # region General diff --git a/src/servers/chat/src/applications/server_launcher.py b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py similarity index 60% rename from src/servers/chat/src/applications/server_launcher.py rename to src/frontends/gamespy/protocols/chat/applications/server_launcher.py index 166390fc7..dbfc8959f 100644 --- a/src/servers/chat/src/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py @@ -1,7 +1,7 @@ -from library.src.abstractions.server_launcher import ServerLauncherBase -from library.src.network.tcp_handler import TcpServer -from library.src.configs import CONFIG, ServerConfig -from servers.chat.src.applications.client import Client +from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.network.tcp_handler import TcpServer +from frontends.gamespy.library.configs import CONFIG, ServerConfig +from frontends.gamespy.protocols.chat.applications.client import Client class ServerLauncher(ServerLauncherBase): diff --git a/src/servers/chat/src/applications/switcher.py b/src/frontends/gamespy/protocols/chat/applications/switcher.py similarity index 92% rename from src/servers/chat/src/applications/switcher.py rename to src/frontends/gamespy/protocols/chat/applications/switcher.py index f26319ba8..743c98b60 100644 --- a/src/servers/chat/src/applications/switcher.py +++ b/src/frontends/gamespy/protocols/chat/applications/switcher.py @@ -1,9 +1,9 @@ from typing import Optional -from library.src.abstractions.client import ClientBase -from library.src.abstractions.handler import CmdHandlerBase -from library.src.abstractions.switcher import SwitcherBase -from servers.chat.src.aggregates.enums import RequestType -from servers.chat.src.contracts.requests import ( +from frontends.gamespy.library.abstractions.client import ClientBase +from frontends.gamespy.library.abstractions.handler import CmdHandlerBase +from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.protocols.chat.aggregates.enums import RequestType +from frontends.gamespy.protocols.chat.contracts.requests import ( GetCKeyRequest, GetChannelKeyRequest, JoinRequest, @@ -32,7 +32,7 @@ PrivateRequest, UTMRequest, ) -from servers.chat.src.applications.handlers import ( +from frontends.gamespy.protocols.chat.applications.handlers import ( GetCKeyHandler, GetChannelKeyHandler, JoinHandler, diff --git a/src/servers/chat/src/contracts/requests.py b/src/frontends/gamespy/protocols/chat/contracts/requests.py similarity index 96% rename from src/servers/chat/src/contracts/requests.py rename to src/frontends/gamespy/protocols/chat/contracts/requests.py index 202f2644b..89abd19e8 100644 --- a/src/servers/chat/src/contracts/requests.py +++ b/src/frontends/gamespy/protocols/chat/contracts/requests.py @@ -1,6 +1,6 @@ -from library.src.extentions.string_extentions import convert_kvstring_to_dictionary -from servers.chat.src.abstractions.handler import ChannelRequestBase, MessageRequestBase -from servers.chat.src.aggregates.enums import ( +from frontends.gamespy.library.extentions.string_extentions import convert_kvstring_to_dictionary +from frontends.gamespy.protocols.chat.abstractions.handler import ChannelRequestBase, MessageRequestBase +from frontends.gamespy.protocols.chat.aggregates.enums import ( GetKeyRequestType, ModeOperationType, ModeRequestType, @@ -8,14 +8,14 @@ ) from typing import Optional import re -from library.src.extentions.string_extentions import ( +from frontends.gamespy.library.extentions.string_extentions import ( convert_keystr_to_list, convert_kvstring_to_dictionary, ) -from servers.chat.src.abstractions.contract import RequestBase -from servers.chat.src.aggregates.enums import LoginRequestType, WhoRequestType -from servers.chat.src.aggregates.exceptions import ChatException -from servers.chat.src.aggregates.exceptions import NickNameInUseException +from frontends.gamespy.protocols.chat.abstractions.contract import RequestBase +from frontends.gamespy.protocols.chat.aggregates.enums import LoginRequestType, WhoRequestType +from frontends.gamespy.protocols.chat.aggregates.exceptions import ChatException +from frontends.gamespy.protocols.chat.aggregates.exceptions import NickNameInUseException # General diff --git a/src/servers/chat/src/contracts/responses.py b/src/frontends/gamespy/protocols/chat/contracts/responses.py similarity index 96% rename from src/servers/chat/src/contracts/responses.py rename to src/frontends/gamespy/protocols/chat/contracts/responses.py index d2bcd51d1..4dd87bd88 100644 --- a/src/servers/chat/src/contracts/responses.py +++ b/src/frontends/gamespy/protocols/chat/contracts/responses.py @@ -1,6 +1,6 @@ -from servers.chat.src.abstractions.handler import ChannelResponseBase -from servers.chat.src.aggregates.enums import ModeRequestType, WhoRequestType -from servers.chat.src.contracts.results import ( +from frontends.gamespy.protocols.chat.abstractions.handler import ChannelResponseBase +from frontends.gamespy.protocols.chat.aggregates.enums import ModeRequestType, WhoRequestType +from frontends.gamespy.protocols.chat.contracts.results import ( GetCKeyResult, GetChannelKeyResult, JoinResult, @@ -23,7 +23,7 @@ WhoIsResult, WhoResult, ) -from servers.chat.src.contracts.requests import ( +from frontends.gamespy.protocols.chat.contracts.requests import ( GetCKeyRequest, GetChannelKeyRequest, JoinRequest, @@ -42,12 +42,12 @@ WhoRequest ) -from servers.chat.src.abstractions.contract import ( +from frontends.gamespy.protocols.chat.abstractions.contract import ( SERVER_DOMAIN, ResponseBase, ) -from library.src.encryption.gs_encryption import CLIENT_KEY, SERVER_KEY -from servers.chat.src.aggregates.response_name import * +from frontends.gamespy.library.encryption.gs_encryption import CLIENT_KEY, SERVER_KEY +from frontends.gamespy.protocols.chat.aggregates.response_name import * # region General diff --git a/src/servers/chat/src/contracts/results.py b/src/frontends/gamespy/protocols/chat/contracts/results.py similarity index 95% rename from src/servers/chat/src/contracts/results.py rename to src/frontends/gamespy/protocols/chat/contracts/results.py index 30b21a361..d31f924b2 100644 --- a/src/servers/chat/src/contracts/results.py +++ b/src/frontends/gamespy/protocols/chat/contracts/results.py @@ -1,8 +1,8 @@ from typing import List, Tuple from pydantic import BaseModel -from servers.chat.src.abstractions.contract import ResultBase -from servers.chat.src.abstractions.handler import MessageResultBase +from frontends.gamespy.protocols.chat.abstractions.contract import ResultBase +from frontends.gamespy.protocols.chat.abstractions.handler import MessageResultBase # region General @@ -16,6 +16,9 @@ class GetKeyResult(ResultBase): values: list + + + class ListResult(ResultBase): class ListInfo(BaseModel): channel_name: str diff --git a/src/servers/__init__.py b/src/frontends/gamespy/protocols/game_status/__init__.py similarity index 100% rename from src/servers/__init__.py rename to src/frontends/gamespy/protocols/game_status/__init__.py diff --git a/src/servers/game_status/src/abstractions/contracts.py b/src/frontends/gamespy/protocols/game_status/abstractions/contracts.py similarity index 70% rename from src/servers/game_status/src/abstractions/contracts.py rename to src/frontends/gamespy/protocols/game_status/abstractions/contracts.py index c2e91f20d..ffbb1903b 100644 --- a/src/servers/game_status/src/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/game_status/abstractions/contracts.py @@ -1,10 +1,10 @@ from typing import Optional -import library.src.abstractions.contracts -from library.src.extentions.gamespy_utils import convert_to_key_value -from servers.game_status.src.aggregations.exceptions import GSException +import frontends.gamespy.library.abstractions.contracts as lib +from frontends.gamespy.library.extentions.gamespy_utils import convert_to_key_value +from frontends.gamespy.protocols.game_status.aggregations.exceptions import GSException -class RequestBase(library.src.abstractions.contracts.RequestBase): +class RequestBase(lib.RequestBase): command_name: str raw_request: str local_id: Optional[int] @@ -32,11 +32,11 @@ def parse(self) -> None: raise GSException("local id is not valid.") -class ResultBase(library.src.abstractions.contracts.ResultBase): +class ResultBase(lib.ResultBase): pass -class ResponseBase(library.src.abstractions.contracts.ResponseBase): +class ResponseBase(lib.ResponseBase): _request: RequestBase _result: ResultBase sending_buffer: str diff --git a/src/frontends/gamespy/protocols/game_status/abstractions/handlers.py b/src/frontends/gamespy/protocols/game_status/abstractions/handlers.py new file mode 100644 index 000000000..938f0b3ea --- /dev/null +++ b/src/frontends/gamespy/protocols/game_status/abstractions/handlers.py @@ -0,0 +1,17 @@ +from typing import Optional +from frontends.gamespy.library.abstractions.contracts import ResponseBase +import frontends.gamespy.library.abstractions.handler as lib +from frontends.gamespy.protocols.game_status.abstractions.contracts import RequestBase, ResultBase +from frontends.gamespy.protocols.game_status.applications.client import Client + + +class CmdHandlerBase(lib.CmdHandlerBase): + _client: Client + _request: RequestBase + _result: ResultBase + _response: Optional[ResponseBase] + + def __init__(self, client: Client, request: RequestBase) -> None: + super().__init__(client, request) + assert isinstance(client, Client) + assert issubclass(type(request), RequestBase) diff --git a/src/servers/game_status/src/aggregations/enums.py b/src/frontends/gamespy/protocols/game_status/aggregations/enums.py similarity index 100% rename from src/servers/game_status/src/aggregations/enums.py rename to src/frontends/gamespy/protocols/game_status/aggregations/enums.py diff --git a/src/frontends/gamespy/protocols/game_status/aggregations/exceptions.py b/src/frontends/gamespy/protocols/game_status/aggregations/exceptions.py new file mode 100644 index 000000000..3a85892ff --- /dev/null +++ b/src/frontends/gamespy/protocols/game_status/aggregations/exceptions.py @@ -0,0 +1,5 @@ +from frontends.gamespy.library.exceptions.general import UniSpyException + + +class GSException(UniSpyException): + pass diff --git a/src/servers/game_status/src/aggregations/gscrypt.py b/src/frontends/gamespy/protocols/game_status/aggregations/gscrypt.py similarity index 70% rename from src/servers/game_status/src/aggregations/gscrypt.py rename to src/frontends/gamespy/protocols/game_status/aggregations/gscrypt.py index d25a87194..14002a3d7 100644 --- a/src/servers/game_status/src/aggregations/gscrypt.py +++ b/src/frontends/gamespy/protocols/game_status/aggregations/gscrypt.py @@ -1,8 +1,8 @@ -from library.src.abstractions.enctypt_base import EncryptBase -from library.src.encryption.xor_encryption import XorEncoding, XorType -from servers.game_status.src.aggregations.exceptions import GSException +from frontends.gamespy.library.abstractions.enctypt_base import EncryptBase +from frontends.gamespy.library.encryption.xor_encryption import XorEncoding, XorType +from frontends.gamespy.protocols.game_status.aggregations.exceptions import GSException class GSCrypt(EncryptBase): diff --git a/src/servers/game_status/src/applications/client.py b/src/frontends/gamespy/protocols/game_status/applications/client.py similarity index 76% rename from src/servers/game_status/src/applications/client.py rename to src/frontends/gamespy/protocols/game_status/applications/client.py index 2222173d8..f31450343 100644 --- a/src/servers/game_status/src/applications/client.py +++ b/src/frontends/gamespy/protocols/game_status/applications/client.py @@ -1,10 +1,10 @@ from typing import Optional -from library.src.abstractions.client import ClientBase, ClientInfoBase -from library.src.abstractions.switcher import SwitcherBase -from library.src.log.log_manager import LogWriter -from library.src.network.tcp_handler import TcpConnection -from library.src.configs import ServerConfig -from servers.game_status.src.aggregations.gscrypt import GSCrypt +from frontends.gamespy.library.abstractions.client import ClientBase, ClientInfoBase +from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.library.log.log_manager import LogWriter +from frontends.gamespy.library.network.tcp_handler import TcpConnection +from frontends.gamespy.library.configs import ServerConfig +from frontends.gamespy.protocols.game_status.aggregations.gscrypt import GSCrypt CHALLENGE_RESPONSE = "\\challenge\\00000000000000000000\\final\\" @@ -59,5 +59,5 @@ def decrypt_message(self, buffer: bytes) -> bytes: return self.crypto.decrypt(buffer) def _create_switcher(self, buffer: bytes) -> SwitcherBase: - from servers.game_status.src.applications.switcher import Switcher + from frontends.gamespy.protocols.game_status.applications.switcher import Switcher return Switcher(self, buffer.decode()) diff --git a/src/servers/game_status/src/applications/handlers.py b/src/frontends/gamespy/protocols/game_status/applications/handlers.py similarity index 80% rename from src/servers/game_status/src/applications/handlers.py rename to src/frontends/gamespy/protocols/game_status/applications/handlers.py index a743cb6ee..ef91e9de3 100644 --- a/src/servers/game_status/src/applications/handlers.py +++ b/src/frontends/gamespy/protocols/game_status/applications/handlers.py @@ -1,8 +1,8 @@ -from servers.game_status.src.abstractions.handlers import CmdHandlerBase -from servers.game_status.src.applications.client import Client -from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest -from servers.game_status.src.contracts.responses import AuthGameResponse, AuthPlayerResponse, GetPlayerDataResponse, GetProfileIdResponse -from servers.game_status.src.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult +from frontends.gamespy.protocols.game_status.abstractions.handlers import CmdHandlerBase +from frontends.gamespy.protocols.game_status.applications.client import Client +from frontends.gamespy.protocols.game_status.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest +from frontends.gamespy.protocols.game_status.contracts.responses import AuthGameResponse, AuthPlayerResponse, GetPlayerDataResponse, GetProfileIdResponse +from frontends.gamespy.protocols.game_status.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult class AuthGameHandler(CmdHandlerBase): diff --git a/src/servers/game_status/src/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py similarity index 61% rename from src/servers/game_status/src/applications/server_launcher.py rename to src/frontends/gamespy/protocols/game_status/applications/server_launcher.py index 8eb8b853f..94543332b 100644 --- a/src/servers/game_status/src/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py @@ -1,7 +1,7 @@ -from library.src.abstractions.server_launcher import ServerLauncherBase -from library.src.network.tcp_handler import TcpServer -from library.src.configs import CONFIG -from servers.chat.src.applications.client import Client +from frontends.gamespy.protocols.game_status.applications.client import Client +from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.network.tcp_handler import TcpServer +from frontends.gamespy.library.configs import CONFIG class ServerLauncher(ServerLauncherBase): diff --git a/src/servers/game_status/src/applications/switcher.py b/src/frontends/gamespy/protocols/game_status/applications/switcher.py similarity index 73% rename from src/servers/game_status/src/applications/switcher.py rename to src/frontends/gamespy/protocols/game_status/applications/switcher.py index 4e6f74900..0aa7090f4 100644 --- a/src/servers/game_status/src/applications/switcher.py +++ b/src/frontends/gamespy/protocols/game_status/applications/switcher.py @@ -1,10 +1,10 @@ from typing import Optional, cast -from library.src.abstractions.switcher import SwitcherBase -from servers.game_status.src.abstractions.handlers import CmdHandlerBase -from servers.game_status.src.aggregations.enums import RequestType -from servers.game_status.src.applications.client import Client -from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest -from servers.game_status.src.applications.handlers import AuthGameHandler, AuthPlayerHandler, GetPlayerDataHandler, GetProfileIdHandler, NewGameHandler, SetPlayerDataHandler, UpdateGameHandler +from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.protocols.game_status.abstractions.handlers import CmdHandlerBase +from frontends.gamespy.protocols.game_status.aggregations.enums import RequestType +from frontends.gamespy.protocols.game_status.applications.client import Client +from frontends.gamespy.protocols.game_status.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest +from frontends.gamespy.protocols.game_status.applications.handlers import AuthGameHandler, AuthPlayerHandler, GetPlayerDataHandler, GetProfileIdHandler, NewGameHandler, SetPlayerDataHandler, UpdateGameHandler class Switcher(SwitcherBase): diff --git a/src/servers/game_status/src/contracts/requests.py b/src/frontends/gamespy/protocols/game_status/contracts/requests.py similarity index 95% rename from src/servers/game_status/src/contracts/requests.py rename to src/frontends/gamespy/protocols/game_status/contracts/requests.py index 53cb5cebb..06c65ca77 100644 --- a/src/servers/game_status/src/contracts/requests.py +++ b/src/frontends/gamespy/protocols/game_status/contracts/requests.py @@ -1,8 +1,8 @@ from typing import Optional, final -from library.src.extentions.gamespy_utils import convert_to_key_value -from servers.game_status.src.abstractions.contracts import RequestBase -from servers.game_status.src.aggregations.enums import AuthMethod, PersistStorageType -from servers.game_status.src.aggregations.exceptions import GSException +from frontends.gamespy.library.extentions.gamespy_utils import convert_to_key_value +from frontends.gamespy.protocols.game_status.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.game_status.aggregations.enums import AuthMethod, PersistStorageType +from frontends.gamespy.protocols.game_status.aggregations.exceptions import GSException @final diff --git a/src/servers/game_status/src/contracts/responses.py b/src/frontends/gamespy/protocols/game_status/contracts/responses.py similarity index 78% rename from src/servers/game_status/src/contracts/responses.py rename to src/frontends/gamespy/protocols/game_status/contracts/responses.py index c6c74851f..d16e0b860 100644 --- a/src/servers/game_status/src/contracts/responses.py +++ b/src/frontends/gamespy/protocols/game_status/contracts/responses.py @@ -1,8 +1,8 @@ from typing import final -from library.src.abstractions.contracts import ResponseBase -from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, SetPlayerDataRequest -from servers.game_status.src.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult +from frontends.gamespy.library.abstractions.contracts import ResponseBase +from frontends.gamespy.protocols.game_status.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, SetPlayerDataRequest +from frontends.gamespy.protocols.game_status.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult @final diff --git a/src/servers/game_status/src/contracts/results.py b/src/frontends/gamespy/protocols/game_status/contracts/results.py similarity index 77% rename from src/servers/game_status/src/contracts/results.py rename to src/frontends/gamespy/protocols/game_status/contracts/results.py index 66a24b65d..863aed137 100644 --- a/src/servers/game_status/src/contracts/results.py +++ b/src/frontends/gamespy/protocols/game_status/contracts/results.py @@ -1,5 +1,5 @@ from typing import final -from servers.game_status.src.abstractions.contracts import ResultBase +from frontends.gamespy.protocols.game_status.abstractions.contracts import ResultBase @final diff --git a/src/servers/chat/__init__.py b/src/frontends/gamespy/protocols/game_traffic_relay/__init__.py similarity index 100% rename from src/servers/chat/__init__.py rename to src/frontends/gamespy/protocols/game_traffic_relay/__init__.py diff --git a/src/servers/game_traffic_relay/src/applications/connection_listener.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/connection_listener.py similarity index 86% rename from src/servers/game_traffic_relay/src/applications/connection_listener.py rename to src/frontends/gamespy/protocols/game_traffic_relay/applications/connection_listener.py index 492a40143..f09b94f8f 100644 --- a/src/servers/game_traffic_relay/src/applications/connection_listener.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/connection_listener.py @@ -1,6 +1,6 @@ import socketserver -from library.src.network.udp_handler import UdpHandler +from frontends.gamespy.library.network.udp_handler import UdpHandler class ConnectionListener: diff --git a/src/servers/game_traffic_relay/src/applications/router.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/router.py similarity index 67% rename from src/servers/game_traffic_relay/src/applications/router.py rename to src/frontends/gamespy/protocols/game_traffic_relay/applications/router.py index f85742006..ac55ddaf9 100644 --- a/src/servers/game_traffic_relay/src/applications/router.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/router.py @@ -1,6 +1,6 @@ from fastapi import FastAPI from backends.urls import * -from servers.game_traffic_relay.src.contracts.general import InitPacketInfo +from frontends.gamespy.protocols.game_traffic_relay.contracts.general import InitPacketInfo app = FastAPI() diff --git a/src/servers/game_traffic_relay/src/contracts/general.py b/src/frontends/gamespy/protocols/game_traffic_relay/contracts/general.py similarity index 76% rename from src/servers/game_traffic_relay/src/contracts/general.py rename to src/frontends/gamespy/protocols/game_traffic_relay/contracts/general.py index 53a21da4a..fe09da232 100644 --- a/src/servers/game_traffic_relay/src/contracts/general.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/contracts/general.py @@ -1,6 +1,6 @@ from pydantic import BaseModel, UUID4 -from servers.natneg.src.aggregations.enums import NatClientIndex, NatPortType +from frontends.gamespy.protocols.natneg.aggregations.enums import NatClientIndex, NatPortType class InitPacketInfo(BaseModel): diff --git a/src/servers/chat/src/__init__.py b/src/frontends/gamespy/protocols/natneg/__init__.py similarity index 100% rename from src/servers/chat/src/__init__.py rename to src/frontends/gamespy/protocols/natneg/__init__.py diff --git a/src/servers/natneg/src/abstractions/contracts.py b/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py similarity index 86% rename from src/servers/natneg/src/abstractions/contracts.py rename to src/frontends/gamespy/protocols/natneg/abstractions/contracts.py index 2b320c3bd..f3f5987c7 100644 --- a/src/servers/natneg/src/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py @@ -1,19 +1,19 @@ import abc import socket from typing import Optional -import library.src.abstractions.contracts -from servers.natneg.src.aggregations.enums import ( +import frontends.gamespy.library.abstractions.contracts as lib +from frontends.gamespy.protocols.natneg.aggregations.enums import ( NatClientIndex, NatPortType, RequestType, ResponseType, ) -from library.src.extentions.bytes_extentions import ip_to_4_bytes +from frontends.gamespy.library.extentions.bytes_extentions import ip_to_4_bytes MAGIC_DATA = bytes([0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2]) -class RequestBase(library.src.abstractions.contracts.RequestBase): +class RequestBase(lib.RequestBase): version: int #! check bytes order cookie: int @@ -40,12 +40,12 @@ def parse(self) -> None: self.port_type = NatPortType(self.raw_request[12]) -class ResultBase(library.src.abstractions.contracts.ResultBase): +class ResultBase(lib.ResultBase): packet_type: ResponseType pass -class ResponseBase(library.src.abstractions.contracts.ResponseBase): +class ResponseBase(lib.ResponseBase): _request: RequestBase _result: ResultBase sending_buffer: bytes diff --git a/src/servers/natneg/src/abstractions/handlers.py b/src/frontends/gamespy/protocols/natneg/abstractions/handlers.py similarity index 52% rename from src/servers/natneg/src/abstractions/handlers.py rename to src/frontends/gamespy/protocols/natneg/abstractions/handlers.py index 0995c5018..e90326062 100644 --- a/src/servers/natneg/src/abstractions/handlers.py +++ b/src/frontends/gamespy/protocols/natneg/abstractions/handlers.py @@ -1,10 +1,9 @@ -import abc -from servers.natneg.src.applications.client import Client -import library.src.abstractions.handler -from servers.natneg.src.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.natneg.applications.client import Client +from frontends.gamespy.protocols.natneg.abstractions.contracts import RequestBase +import frontends.gamespy.library.abstractions.handler as lib -class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase): +class CmdHandlerBase(lib.CmdHandlerBase): def __init__(self, client: Client, request: RequestBase) -> None: super().__init__(client, request) assert isinstance(client, Client) diff --git a/src/servers/natneg/src/aggregations/enums.py b/src/frontends/gamespy/protocols/natneg/aggregations/enums.py similarity index 100% rename from src/servers/natneg/src/aggregations/enums.py rename to src/frontends/gamespy/protocols/natneg/aggregations/enums.py diff --git a/src/servers/natneg/src/aggregations/natneg_cookie.py b/src/frontends/gamespy/protocols/natneg/aggregations/natneg_cookie.py similarity index 100% rename from src/servers/natneg/src/aggregations/natneg_cookie.py rename to src/frontends/gamespy/protocols/natneg/aggregations/natneg_cookie.py diff --git a/src/servers/natneg/src/applications/client.py b/src/frontends/gamespy/protocols/natneg/applications/client.py similarity index 52% rename from src/servers/natneg/src/applications/client.py rename to src/frontends/gamespy/protocols/natneg/applications/client.py index 9f446b17f..27b364d0e 100644 --- a/src/servers/natneg/src/applications/client.py +++ b/src/frontends/gamespy/protocols/natneg/applications/client.py @@ -1,7 +1,7 @@ -from library.src.abstractions.client import ClientBase -from library.src.log.log_manager import LogWriter -from library.src.network.udp_handler import UdpConnection -from library.src.configs import ServerConfig +from frontends.gamespy.library.abstractions.client import ClientBase +from frontends.gamespy.library.log.log_manager import LogWriter +from frontends.gamespy.library.network.udp_handler import UdpConnection +from frontends.gamespy.library.configs import ServerConfig class Client(ClientBase): @@ -13,6 +13,6 @@ def __init__(self, connection: UdpConnection, server_config: ServerConfig, logge def _create_switcher(self, buffer: bytes): assert isinstance(buffer, bytes) - from servers.natneg.src.applications.switcher import CmdSwitcher + from frontends.gamespy.protocols.natneg.applications.switcher import CmdSwitcher return CmdSwitcher(self, buffer) diff --git a/src/servers/natneg/src/applications/handlers.py b/src/frontends/gamespy/protocols/natneg/applications/handlers.py similarity index 92% rename from src/servers/natneg/src/applications/handlers.py rename to src/frontends/gamespy/protocols/natneg/applications/handlers.py index df0577404..ed13e7b32 100644 --- a/src/servers/natneg/src/applications/handlers.py +++ b/src/frontends/gamespy/protocols/natneg/applications/handlers.py @@ -1,6 +1,6 @@ -from servers.natneg.src.abstractions.handlers import CmdHandlerBase -from servers.natneg.src.applications.client import Client -from servers.natneg.src.contracts.requests import ( +from frontends.gamespy.protocols.natneg.abstractions.handlers import CmdHandlerBase +from frontends.gamespy.protocols.natneg.applications.client import Client +from frontends.gamespy.protocols.natneg.contracts.requests import ( AddressCheckRequest, ConnectAckRequest, ConnectRequest, @@ -10,13 +10,13 @@ PingRequest, ReportRequest, ) -from servers.natneg.src.contracts.responses import ( +from frontends.gamespy.protocols.natneg.contracts.responses import ( AddressCheckResponse, ErcAckResponse, InitResponse, NatifyResponse, ) -from servers.natneg.src.contracts.results import ( +from frontends.gamespy.protocols.natneg.contracts.results import ( AddressCheckResult, ConnectResult, ErtAckResult, diff --git a/src/servers/natneg/src/applications/server_launcher.py b/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py similarity index 60% rename from src/servers/natneg/src/applications/server_launcher.py rename to src/frontends/gamespy/protocols/natneg/applications/server_launcher.py index a8fa9990c..5db7fa15b 100644 --- a/src/servers/natneg/src/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py @@ -1,7 +1,7 @@ -from library.src.abstractions.server_launcher import ServerLauncherBase -from library.src.network.udp_handler import UdpServer -from library.src.configs import CONFIG, ServerConfig -from servers.natneg.src.applications.client import Client +from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.network.udp_handler import UdpServer +from frontends.gamespy.library.configs import CONFIG, ServerConfig +from frontends.gamespy.protocols.natneg.applications.client import Client class ServerLauncher(ServerLauncherBase): diff --git a/src/servers/natneg/src/applications/switcher.py b/src/frontends/gamespy/protocols/natneg/applications/switcher.py similarity index 81% rename from src/servers/natneg/src/applications/switcher.py rename to src/frontends/gamespy/protocols/natneg/applications/switcher.py index 67d6c5c51..c04fc1d60 100644 --- a/src/servers/natneg/src/applications/switcher.py +++ b/src/frontends/gamespy/protocols/natneg/applications/switcher.py @@ -1,8 +1,8 @@ from typing import Optional -from library.src.abstractions.switcher import SwitcherBase -from servers.natneg.src.abstractions.handlers import CmdHandlerBase -from servers.natneg.src.applications.client import Client -from servers.natneg.src.contracts.requests import ( +from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.protocols.natneg.abstractions.handlers import CmdHandlerBase +from frontends.gamespy.protocols.natneg.applications.client import Client +from frontends.gamespy.protocols.natneg.contracts.requests import ( AddressCheckRequest, ConnectRequest, ErtAckRequest, @@ -10,8 +10,8 @@ PingRequest, ReportRequest, ) -from servers.natneg.src.aggregations.enums import RequestType -from servers.natneg.src.applications.handlers import ( +from frontends.gamespy.protocols.natneg.aggregations.enums import RequestType +from frontends.gamespy.protocols.natneg.applications.handlers import ( AddressCheckHandler, ConnectHandler, ErtAckHandler, diff --git a/src/servers/natneg/src/contracts/requests.py b/src/frontends/gamespy/protocols/natneg/contracts/requests.py similarity index 91% rename from src/servers/natneg/src/contracts/requests.py rename to src/frontends/gamespy/protocols/natneg/contracts/requests.py index 11e4ad753..662fc79d3 100644 --- a/src/servers/natneg/src/contracts/requests.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/requests.py @@ -1,9 +1,9 @@ from socket import inet_ntoa import struct -# from library.src.extentions.string_extentions import IPEndPoint -from servers.natneg.src.abstractions.contracts import CommonRequestBase, RequestBase -from servers.natneg.src.aggregations.enums import ( +# from frontends.gamespy.library.extentions.string_extentions import IPEndPoint +from frontends.gamespy.protocols.natneg.abstractions.contracts import CommonRequestBase, RequestBase +from frontends.gamespy.protocols.natneg.aggregations.enums import ( NatClientIndex, NatPortMappingScheme, NatPortType, diff --git a/src/servers/natneg/src/contracts/responses.py b/src/frontends/gamespy/protocols/natneg/contracts/responses.py similarity index 83% rename from src/servers/natneg/src/contracts/responses.py rename to src/frontends/gamespy/protocols/natneg/contracts/responses.py index 77800b986..5271dff18 100644 --- a/src/servers/natneg/src/contracts/responses.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/responses.py @@ -1,11 +1,11 @@ -from servers.natneg.src.abstractions.contracts import CommonResponseBase -from servers.natneg.src.contracts.requests import ( +from frontends.gamespy.protocols.natneg.abstractions.contracts import CommonResponseBase +from frontends.gamespy.protocols.natneg.contracts.requests import ( AddressCheckRequest, ErtAckRequest, InitRequest, NatifyRequest, ) -from servers.natneg.src.contracts.results import AddressCheckResult, ErtAckResult, InitResult, NatifyResult +from frontends.gamespy.protocols.natneg.contracts.results import AddressCheckResult, ErtAckResult, InitResult, NatifyResult class InitResponse(CommonResponseBase): diff --git a/src/servers/natneg/src/contracts/results.py b/src/frontends/gamespy/protocols/natneg/contracts/results.py similarity index 85% rename from src/servers/natneg/src/contracts/results.py rename to src/frontends/gamespy/protocols/natneg/contracts/results.py index 724c86036..5c608091a 100644 --- a/src/servers/natneg/src/contracts/results.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/results.py @@ -1,6 +1,6 @@ from typing import Any -from servers.natneg.src.abstractions.contracts import CommonResultBase, ResultBase -from servers.natneg.src.aggregations.enums import ( +from frontends.gamespy.protocols.natneg.abstractions.contracts import CommonResultBase, ResultBase +from frontends.gamespy.protocols.natneg.aggregations.enums import ( ConnectPacketStatus, PreInitState, ResponseType, diff --git a/src/servers/chat/tests/__init__.py b/src/frontends/gamespy/protocols/presence_connection_manager/__init__.py similarity index 100% rename from src/servers/chat/tests/__init__.py rename to src/frontends/gamespy/protocols/presence_connection_manager/__init__.py diff --git a/src/servers/presence_connection_manager/src/abstractions/contracts.py b/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py similarity index 73% rename from src/servers/presence_connection_manager/src/abstractions/contracts.py rename to src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py index 7acc124aa..18a16e5be 100644 --- a/src/servers/presence_connection_manager/src/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py @@ -1,14 +1,12 @@ import abc -import library.src.abstractions -import library.src.abstractions.contracts -from library.src.extentions.gamespy_utils import convert_to_key_value -from servers.presence_search_player.src.aggregates.exceptions import ( +from frontends.gamespy.library.extentions.gamespy_utils import convert_to_key_value +from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import ( GPParseException, ) from typing import Dict, Optional +import frontends.gamespy.library.abstractions.contracts as lib -import library.src.abstractions.contracts def normalize_request(message: str): @@ -20,7 +18,7 @@ def normalize_request(message: str): return message -class RequestBase(library.src.abstractions.contracts.RequestBase): +class RequestBase(lib.RequestBase): command_name: str operation_id: int raw_request: str @@ -41,11 +39,11 @@ def parse(self): except: raise GPParseException("namespaceid is invalid.") -class ResultBase(library.src.abstractions.contracts.ResultBase): +class ResultBase(lib.ResultBase): pass -class ResponseBase(library.src.abstractions.contracts.ResponseBase): +class ResponseBase(lib.ResponseBase): _request: RequestBase _result: ResultBase sending_buffer: str diff --git a/src/servers/presence_connection_manager/src/abstractions/handlers.py b/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/handlers.py similarity index 59% rename from src/servers/presence_connection_manager/src/abstractions/handlers.py rename to src/frontends/gamespy/protocols/presence_connection_manager/abstractions/handlers.py index e3b025c1b..0ad72f4ac 100644 --- a/src/servers/presence_connection_manager/src/abstractions/handlers.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/handlers.py @@ -1,16 +1,16 @@ -from servers.presence_connection_manager.src.applications.client import Client -from servers.presence_connection_manager.src.aggregates.enums import LoginStatus -from servers.presence_search_player.src.aggregates.exceptions import GPException +from frontends.gamespy.protocols.presence_connection_manager.applications.client import Client +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import LoginStatus +from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import GPException -from servers.presence_connection_manager.src.abstractions.contracts import ( +from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import ( RequestBase, ResultBase, ) -import library.src.abstractions.handler +import frontends.gamespy.library.abstractions.handler as lib -class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase): +class CmdHandlerBase(lib.CmdHandlerBase): _client: Client _request: RequestBase _result: ResultBase diff --git a/src/servers/presence_connection_manager/src/aggregates/enums.py b/src/frontends/gamespy/protocols/presence_connection_manager/aggregates/enums.py similarity index 100% rename from src/servers/presence_connection_manager/src/aggregates/enums.py rename to src/frontends/gamespy/protocols/presence_connection_manager/aggregates/enums.py diff --git a/src/servers/presence_connection_manager/src/aggregates/login_challenge.py b/src/frontends/gamespy/protocols/presence_connection_manager/aggregates/login_challenge.py similarity index 90% rename from src/servers/presence_connection_manager/src/aggregates/login_challenge.py rename to src/frontends/gamespy/protocols/presence_connection_manager/aggregates/login_challenge.py index 91efa97c5..09bf8ab1d 100644 --- a/src/servers/presence_connection_manager/src/aggregates/login_challenge.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/aggregates/login_challenge.py @@ -1,6 +1,6 @@ import hashlib -from servers.presence_connection_manager.src.aggregates.enums import GPPartnerId, LoginType +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import GPPartnerId, LoginType SERVER_CHALLENGE = "0000000000" diff --git a/src/servers/presence_connection_manager/src/aggregates/sdk_revision.py b/src/frontends/gamespy/protocols/presence_connection_manager/aggregates/sdk_revision.py similarity index 93% rename from src/servers/presence_connection_manager/src/aggregates/sdk_revision.py rename to src/frontends/gamespy/protocols/presence_connection_manager/aggregates/sdk_revision.py index b7eba2796..018c50ed0 100644 --- a/src/servers/presence_connection_manager/src/aggregates/sdk_revision.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/aggregates/sdk_revision.py @@ -1,4 +1,4 @@ -from servers.presence_connection_manager.src.aggregates.enums import SdkRevisionType +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import SdkRevisionType class SdkRevision: diff --git a/src/servers/presence_connection_manager/src/aggregates/user_status.py b/src/frontends/gamespy/protocols/presence_connection_manager/aggregates/user_status.py similarity index 82% rename from src/servers/presence_connection_manager/src/aggregates/user_status.py rename to src/frontends/gamespy/protocols/presence_connection_manager/aggregates/user_status.py index d4c142524..c9b175a41 100644 --- a/src/servers/presence_connection_manager/src/aggregates/user_status.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/aggregates/user_status.py @@ -1,6 +1,6 @@ from pydantic import BaseModel -from servers.presence_connection_manager.src.aggregates.enums import GPStatusCode +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import GPStatusCode class UserStatus(BaseModel): diff --git a/src/servers/presence_connection_manager/src/applications/client.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/client.py similarity index 62% rename from src/servers/presence_connection_manager/src/applications/client.py rename to src/frontends/gamespy/protocols/presence_connection_manager/applications/client.py index cfcefcd37..e501fba7c 100644 --- a/src/servers/presence_connection_manager/src/applications/client.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/client.py @@ -1,15 +1,15 @@ -from library.src.abstractions.client import ClientBase, ClientInfoBase +from frontends.gamespy.library.abstractions.client import ClientBase, ClientInfoBase -from library.src.abstractions.switcher import SwitcherBase -from library.src.log.log_manager import LogWriter -from library.src.network.tcp_handler import TcpConnection -from library.src.configs import ServerConfig -from servers.presence_connection_manager.src.aggregates.login_challenge import ( +from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.library.log.log_manager import LogWriter +from frontends.gamespy.library.network.tcp_handler import TcpConnection +from frontends.gamespy.library.configs import ServerConfig +from frontends.gamespy.protocols.presence_connection_manager.aggregates.login_challenge import ( SERVER_CHALLENGE, ) -from servers.presence_connection_manager.src.aggregates.enums import LoginStatus -from servers.presence_connection_manager.src.aggregates.sdk_revision import SdkRevision -from servers.presence_connection_manager.src.aggregates.enums import LoginStatus +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import LoginStatus +from frontends.gamespy.protocols.presence_connection_manager.aggregates.sdk_revision import SdkRevision +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import LoginStatus LOGIN_TICKET = "0000000000000000000000__" SESSION_KEY = 1111 @@ -57,5 +57,5 @@ def on_connected(self) -> None: self.connection.send(buffer) def _create_switcher(self, buffer: bytes) -> SwitcherBase: - from servers.presence_connection_manager.src.applications.switcher import Switcher + from frontends.gamespy.protocols.presence_connection_manager.applications.switcher import Switcher return Switcher(self, buffer.decode()) diff --git a/src/servers/presence_connection_manager/src/applications/handlers.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py similarity index 91% rename from src/servers/presence_connection_manager/src/applications/handlers.py rename to src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py index 453a57d2a..e67b05e8b 100644 --- a/src/servers/presence_connection_manager/src/applications/handlers.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py @@ -1,6 +1,6 @@ from typing import final -from servers.presence_connection_manager.src.contracts.requests import ( +from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import ( AddBlockRequest, GetProfileRequest, NewProfileRequest, @@ -15,8 +15,8 @@ LoginRequest, LogoutRequest, ) -from servers.presence_connection_manager.src.abstractions.handlers import CmdHandlerBase -from servers.presence_connection_manager.src.contracts.results import ( +from frontends.gamespy.protocols.presence_connection_manager.abstractions.handlers import CmdHandlerBase +from frontends.gamespy.protocols.presence_connection_manager.contracts.results import ( BlockListResult, BuddyListResult, NewUserResult, @@ -26,7 +26,7 @@ NewProfileResult, LoginResult ) -from servers.presence_connection_manager.src.contracts.responses import ( +from frontends.gamespy.protocols.presence_connection_manager.contracts.responses import ( BlockListResponse, BuddyListResponse, NewUserResponse, @@ -38,19 +38,19 @@ LoginResponse, ) -from servers.presence_connection_manager.src.applications.client import Client -from servers.presence_connection_manager.src.abstractions.handlers import ( +from frontends.gamespy.protocols.presence_connection_manager.applications.client import Client +from frontends.gamespy.protocols.presence_connection_manager.abstractions.handlers import ( CmdHandlerBase, LoginedHandlerBase, ) -from servers.presence_connection_manager.src.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import RequestBase from multiprocessing.pool import Pool from typing import TYPE_CHECKING -from servers.presence_connection_manager.src.aggregates.sdk_revision import SdkRevision +from frontends.gamespy.protocols.presence_connection_manager.aggregates.sdk_revision import SdkRevision if TYPE_CHECKING: - from servers.presence_connection_manager.src.applications.client import Client + from frontends.gamespy.protocols.presence_connection_manager.applications.client import Client # region General diff --git a/src/servers/presence_connection_manager/src/applications/server_launcher.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py similarity index 59% rename from src/servers/presence_connection_manager/src/applications/server_launcher.py rename to src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py index 204a75d6c..a6b894262 100644 --- a/src/servers/presence_connection_manager/src/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py @@ -1,7 +1,7 @@ -from library.src.abstractions.server_launcher import ServerLauncherBase -from library.src.network.tcp_handler import TcpServer -from library.src.configs import CONFIG -from servers.presence_connection_manager.src.applications.client import Client +from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.network.tcp_handler import TcpServer +from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.protocols.presence_connection_manager.applications.client import Client class ServerLauncher(ServerLauncherBase): diff --git a/src/servers/presence_connection_manager/src/applications/switcher.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/switcher.py similarity index 72% rename from src/servers/presence_connection_manager/src/applications/switcher.py rename to src/frontends/gamespy/protocols/presence_connection_manager/applications/switcher.py index b75f0afcb..47db40a37 100644 --- a/src/servers/presence_connection_manager/src/applications/switcher.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/switcher.py @@ -1,13 +1,13 @@ -from library.src.abstractions.switcher import SwitcherBase -from servers.presence_connection_manager.src.aggregates.enums import RequestType -from servers.presence_connection_manager.src.contracts.requests import KeepAliveRequest, LoginRequest, LogoutRequest, StatusInfoRequest, StatusRequest, AddBlockRequest, GetProfileRequest, NewProfileRequest, RegisterCDKeyRequest, NewUserRequest, RegisterNickRequest, UpdateProfileRequest -from servers.presence_connection_manager.src.applications.handlers import AddBlockHandler, GetProfileHandler, KeepAliveHandler, LoginHandler, LogoutHandler, NewProfileHandler, NewUserHandler, RegisterCDKeyHandler, RegisterNickHandler, StatusHandler, StatusInfoHandler, UpdateProfileHandler -from servers.presence_search_player.src.aggregates.exceptions import GPParseException +from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import RequestType +from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import KeepAliveRequest, LoginRequest, LogoutRequest, StatusInfoRequest, StatusRequest, AddBlockRequest, GetProfileRequest, NewProfileRequest, RegisterCDKeyRequest, NewUserRequest, RegisterNickRequest, UpdateProfileRequest +from frontends.gamespy.protocols.presence_connection_manager.applications.handlers import AddBlockHandler, GetProfileHandler, KeepAliveHandler, LoginHandler, LogoutHandler, NewProfileHandler, NewUserHandler, RegisterCDKeyHandler, RegisterNickHandler, StatusHandler, StatusInfoHandler, UpdateProfileHandler +from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import GPParseException -from servers.presence_connection_manager.src.abstractions.handlers import CmdHandlerBase +from frontends.gamespy.protocols.presence_connection_manager.abstractions.handlers import CmdHandlerBase from typing import TYPE_CHECKING, Optional, cast -from servers.presence_connection_manager.src.applications.client import Client +from frontends.gamespy.protocols.presence_connection_manager.applications.client import Client class Switcher(SwitcherBase): diff --git a/src/servers/presence_connection_manager/src/contracts/requests.py b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py similarity index 93% rename from src/servers/presence_connection_manager/src/contracts/requests.py rename to src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py index 9d3039e4c..af71b231d 100644 --- a/src/servers/presence_connection_manager/src/contracts/requests.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py @@ -1,22 +1,22 @@ from pydantic import BaseModel -from library.src.exceptions.general import UniSpyException -from servers.presence_connection_manager.src.aggregates.enums import PublicMasks +from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import PublicMasks from typing import Optional, final -from library.src.extentions.gamespy_utils import convert_to_key_value -from servers.presence_connection_manager.src.abstractions.contracts import RequestBase -from servers.presence_connection_manager.src.aggregates.user_status import UserStatus, UserStatusInfo -from servers.presence_connection_manager.src.aggregates.enums import GPStatusCode -from servers.presence_search_player.src.aggregates.exceptions import GPParseException +from frontends.gamespy.library.extentions.gamespy_utils import convert_to_key_value +from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.presence_connection_manager.aggregates.user_status import UserStatus, UserStatusInfo +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import GPStatusCode +from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import GPParseException from typing import final -from library.src.extentions.gamespy_utils import is_email_format_correct -from library.src.extentions.password_encoder import process_password -from servers.presence_connection_manager.src.abstractions.contracts import RequestBase -from servers.presence_connection_manager.src.aggregates.enums import ( +from frontends.gamespy.library.extentions.gamespy_utils import is_email_format_correct +from frontends.gamespy.library.extentions.password_encoder import process_password +from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( LoginType, QuietModeType, SdkRevisionType, ) -from servers.presence_search_player.src.aggregates.exceptions import ( +from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import ( GPParseException, ) diff --git a/src/servers/presence_connection_manager/src/contracts/responses.py b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/responses.py similarity index 86% rename from src/servers/presence_connection_manager/src/contracts/responses.py rename to src/frontends/gamespy/protocols/presence_connection_manager/contracts/responses.py index 445e1589c..d113f0d46 100644 --- a/src/servers/presence_connection_manager/src/contracts/responses.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/responses.py @@ -1,15 +1,15 @@ -from servers.presence_connection_manager.src.abstractions.contracts import ResponseBase -from servers.presence_connection_manager.src.aggregates.login_challenge import SERVER_CHALLENGE, LoginChallengeProof -from servers.presence_connection_manager.src.applications.client import ( +from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import ResponseBase +from frontends.gamespy.protocols.presence_connection_manager.aggregates.login_challenge import SERVER_CHALLENGE, LoginChallengeProof +from frontends.gamespy.protocols.presence_connection_manager.applications.client import ( LOGIN_TICKET, SESSION_KEY, ) -from servers.presence_connection_manager.src.contracts.requests import ( +from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import ( KeepAliveRequest, LoginRequest, NewUserRequest, ) -from servers.presence_connection_manager.src.contracts.results import ( +from frontends.gamespy.protocols.presence_connection_manager.contracts.results import ( LoginResult, NewUserResult, ) @@ -68,12 +68,12 @@ def build(self): # region Buddy -from servers.presence_connection_manager.src.abstractions.contracts import ( +from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import ( RequestBase, ResponseBase, ) -from servers.presence_connection_manager.src.contracts.requests import AddBuddyRequest, StatusInfoRequest -from servers.presence_connection_manager.src.contracts.results import ( +from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import AddBuddyRequest, StatusInfoRequest +from frontends.gamespy.protocols.presence_connection_manager.contracts.results import ( AddBuddyResult, BlockListResult, BuddyListResult, @@ -149,14 +149,14 @@ def build(self): ) # region Profile -from library.src.extentions.gamespy_ramdoms import StringType, generate_random_string -from servers.presence_connection_manager.src.abstractions.contracts import ResponseBase -from servers.presence_connection_manager.src.contracts.requests import ( +from frontends.gamespy.library.extentions.gamespy_ramdoms import StringType, generate_random_string +from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import ResponseBase +from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import ( GetProfileRequest, NewProfileRequest, RegisterNickRequest, ) -from servers.presence_connection_manager.src.contracts.results import ( +from frontends.gamespy.protocols.presence_connection_manager.contracts.results import ( GetProfileResult, NewProfileResult, ) diff --git a/src/servers/presence_connection_manager/src/contracts/results.py b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/results.py similarity index 78% rename from src/servers/presence_connection_manager/src/contracts/results.py rename to src/frontends/gamespy/protocols/presence_connection_manager/contracts/results.py index b18c9ebc9..3ba40de53 100644 --- a/src/servers/presence_connection_manager/src/contracts/results.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/results.py @@ -1,9 +1,9 @@ -from servers.presence_connection_manager.src.aggregates.user_status import ( +from frontends.gamespy.protocols.presence_connection_manager.aggregates.user_status import ( UserStatusInfo, ) -from servers.presence_connection_manager.src.aggregates.user_status import UserStatus +from frontends.gamespy.protocols.presence_connection_manager.aggregates.user_status import UserStatus from pydantic import BaseModel -from servers.presence_connection_manager.src.abstractions.contracts import ResultBase +from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import ResultBase # region General diff --git a/src/servers/game_status/__init__.py b/src/frontends/gamespy/protocols/presence_search_player/__init__.py similarity index 100% rename from src/servers/game_status/__init__.py rename to src/frontends/gamespy/protocols/presence_search_player/__init__.py diff --git a/src/servers/presence_search_player/src/abstractions/contracts.py b/src/frontends/gamespy/protocols/presence_search_player/abstractions/contracts.py similarity index 73% rename from src/servers/presence_search_player/src/abstractions/contracts.py rename to src/frontends/gamespy/protocols/presence_search_player/abstractions/contracts.py index dfa1f1021..652ac58a3 100644 --- a/src/servers/presence_search_player/src/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/presence_search_player/abstractions/contracts.py @@ -1,12 +1,12 @@ from typing import Dict -import library.src.abstractions.contracts -from library.src.extentions.gamespy_utils import convert_to_key_value -from servers.presence_search_player.src.aggregates.exceptions import ( +import frontends.gamespy.library.abstractions.contracts as lib +from frontends.gamespy.library.extentions.gamespy_utils import convert_to_key_value +from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import ( GPParseException, ) -class RequestBase(library.src.abstractions.contracts.RequestBase): +class RequestBase(lib.RequestBase): request_dict: Dict[str, str] raw_request: str command_name: str @@ -35,10 +35,10 @@ def parse(self) -> None: raise GPParseException("namespaceid is incorrect.") -class ResultBase(library.src.abstractions.contracts.ResultBase): +class ResultBase(lib.ResultBase): pass -class ResponseBase(library.src.abstractions.contracts.ResponseBase): +class ResponseBase(lib.ResponseBase): _result: ResultBase _request: RequestBase diff --git a/src/servers/presence_search_player/src/abstractions/handler.py b/src/frontends/gamespy/protocols/presence_search_player/abstractions/handler.py similarity index 51% rename from src/servers/presence_search_player/src/abstractions/handler.py rename to src/frontends/gamespy/protocols/presence_search_player/abstractions/handler.py index 373ce3598..84b5c5688 100644 --- a/src/servers/presence_search_player/src/abstractions/handler.py +++ b/src/frontends/gamespy/protocols/presence_search_player/abstractions/handler.py @@ -1,7 +1,7 @@ -from library.src.abstractions.handler import CmdHandlerBase as CHB -from servers.presence_search_player.src.abstractions.contracts import RequestBase -from servers.presence_search_player.src.applications.client import Client -from servers.presence_search_player.src.aggregates.exceptions import GPException +from frontends.gamespy.library.abstractions.handler import CmdHandlerBase as CHB +from frontends.gamespy.protocols.presence_search_player.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.presence_search_player.applications.client import Client +from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import GPException class CmdHandlerBase(CHB): diff --git a/src/servers/presence_search_player/src/aggregates/enums.py b/src/frontends/gamespy/protocols/presence_search_player/aggregates/enums.py similarity index 100% rename from src/servers/presence_search_player/src/aggregates/enums.py rename to src/frontends/gamespy/protocols/presence_search_player/aggregates/enums.py diff --git a/src/servers/presence_search_player/src/aggregates/exceptions.py b/src/frontends/gamespy/protocols/presence_search_player/aggregates/exceptions.py similarity index 98% rename from src/servers/presence_search_player/src/aggregates/exceptions.py rename to src/frontends/gamespy/protocols/presence_search_player/aggregates/exceptions.py index ddede71a0..f0e8c9782 100644 --- a/src/servers/presence_search_player/src/aggregates/exceptions.py +++ b/src/frontends/gamespy/protocols/presence_search_player/aggregates/exceptions.py @@ -1,6 +1,6 @@ -from library.src.exceptions.general import UniSpyException -from servers.presence_search_player.src.aggregates.enums import GPErrorCode -from library.src.abstractions.contracts import ResponseBase +from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.protocols.presence_search_player.aggregates.enums import GPErrorCode +from frontends.gamespy.library.abstractions.contracts import ResponseBase class GPException(UniSpyException, ResponseBase): diff --git a/src/frontends/gamespy/protocols/presence_search_player/applications/client.py b/src/frontends/gamespy/protocols/presence_search_player/applications/client.py new file mode 100644 index 000000000..a37234fae --- /dev/null +++ b/src/frontends/gamespy/protocols/presence_search_player/applications/client.py @@ -0,0 +1,18 @@ +from frontends.gamespy.library.abstractions.client import ClientBase + +from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.library.log.log_manager import LogWriter +from frontends.gamespy.library.network.tcp_handler import TcpConnection +from frontends.gamespy.library.configs import ServerConfig + + +class Client(ClientBase): + client_pool: dict[str, "Client"] = {} + + def __init__(self, connection: TcpConnection, server_config: ServerConfig, logger: LogWriter): + super().__init__(connection, server_config, logger) + + def _create_switcher(self, buffer: bytes) -> SwitcherBase: + from frontends.gamespy.protocols.presence_search_player.applications.switcher import CmdSwitcher + temp_buffer = buffer.decode() + return CmdSwitcher(self, temp_buffer) diff --git a/src/servers/presence_search_player/src/applications/handlers.py b/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py similarity index 84% rename from src/servers/presence_search_player/src/applications/handlers.py rename to src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py index d63bcb9bf..403bbb185 100644 --- a/src/servers/presence_search_player/src/applications/handlers.py +++ b/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py @@ -1,11 +1,11 @@ -from servers.presence_search_player.src.contracts.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest +from frontends.gamespy.protocols.presence_search_player.contracts.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest -from servers.presence_search_player.src.contracts.responses import CheckResponse, NewUserResponse, NicksResponse, OthersListResponse, OthersResponse, SearchResponse, SearchUniqueResponse, UniqueSearchResponse, ValidResponse -from servers.presence_search_player.src.contracts.results import CheckResult, NewUserResult, NicksResult, OthersListResult, OthersResult, SearchResult, SearchUniqueResult, UniqueSearchResult, ValidResult +from frontends.gamespy.protocols.presence_search_player.contracts.responses import CheckResponse, NewUserResponse, NicksResponse, OthersListResponse, OthersResponse, SearchResponse, SearchUniqueResponse, UniqueSearchResponse, ValidResponse +from frontends.gamespy.protocols.presence_search_player.contracts.results import CheckResult, NewUserResult, NicksResult, OthersListResult, OthersResult, SearchResult, SearchUniqueResult, UniqueSearchResult, ValidResult -from servers.presence_search_player.src.abstractions.handler import CmdHandlerBase -from servers.presence_search_player.src.applications.client import Client +from frontends.gamespy.protocols.presence_search_player.abstractions.handler import CmdHandlerBase +from frontends.gamespy.protocols.presence_search_player.applications.client import Client class CheckHandler(CmdHandlerBase): diff --git a/src/servers/presence_search_player/src/applications/server_launcher.py b/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py similarity index 59% rename from src/servers/presence_search_player/src/applications/server_launcher.py rename to src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py index 32fccfddd..ff345544c 100644 --- a/src/servers/presence_search_player/src/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py @@ -1,7 +1,7 @@ -from library.src.abstractions.server_launcher import ServerLauncherBase -from library.src.network.tcp_handler import TcpServer -from library.src.configs import CONFIG -from servers.presence_search_player.src.applications.client import Client +from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.network.tcp_handler import TcpServer +from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.protocols.presence_search_player.applications.client import Client class ServerLauncher(ServerLauncherBase): diff --git a/src/servers/presence_search_player/src/applications/switcher.py b/src/frontends/gamespy/protocols/presence_search_player/applications/switcher.py similarity index 75% rename from src/servers/presence_search_player/src/applications/switcher.py rename to src/frontends/gamespy/protocols/presence_search_player/applications/switcher.py index c1038a576..4829f927c 100644 --- a/src/servers/presence_search_player/src/applications/switcher.py +++ b/src/frontends/gamespy/protocols/presence_search_player/applications/switcher.py @@ -1,13 +1,13 @@ from typing import TYPE_CHECKING, Optional, cast -from library.src.abstractions.switcher import SwitcherBase -from servers.presence_search_player.src.aggregates.enums import RequestType -from servers.presence_search_player.src.contracts.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest +from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.protocols.presence_search_player.aggregates.enums import RequestType +from frontends.gamespy.protocols.presence_search_player.contracts.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest -from servers.presence_search_player.src.applications.handlers import CheckHandler, NewUserHandler, NicksHandler, OthersHandler, OthersListHandler, SearchHandler, SearchUniqueHandler, UniqueSearchHandler, ValidHandler +from frontends.gamespy.protocols.presence_search_player.applications.handlers import CheckHandler, NewUserHandler, NicksHandler, OthersHandler, OthersListHandler, SearchHandler, SearchUniqueHandler, UniqueSearchHandler, ValidHandler -from servers.presence_search_player.src.abstractions.handler import CmdHandlerBase +from frontends.gamespy.protocols.presence_search_player.abstractions.handler import CmdHandlerBase -from servers.presence_search_player.src.applications.client import Client +from frontends.gamespy.protocols.presence_search_player.applications.client import Client class CmdSwitcher(SwitcherBase): diff --git a/src/servers/presence_search_player/src/contracts/requests.py b/src/frontends/gamespy/protocols/presence_search_player/contracts/requests.py similarity index 95% rename from src/servers/presence_search_player/src/contracts/requests.py rename to src/frontends/gamespy/protocols/presence_search_player/contracts/requests.py index 2074f9f10..b75baaaee 100644 --- a/src/servers/presence_search_player/src/contracts/requests.py +++ b/src/frontends/gamespy/protocols/presence_search_player/contracts/requests.py @@ -1,8 +1,8 @@ -from library.src.extentions.gamespy_utils import is_email_format_correct -from library.src.extentions.password_encoder import process_password -from servers.presence_search_player.src.abstractions.contracts import RequestBase -from servers.presence_search_player.src.aggregates.enums import SearchType -from servers.presence_search_player.src.aggregates.exceptions import ( +from frontends.gamespy.library.extentions.gamespy_utils import is_email_format_correct +from frontends.gamespy.library.extentions.password_encoder import process_password +from frontends.gamespy.protocols.presence_search_player.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.presence_search_player.aggregates.enums import SearchType +from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import ( GPParseException, ) diff --git a/src/servers/presence_search_player/src/contracts/responses.py b/src/frontends/gamespy/protocols/presence_search_player/contracts/responses.py similarity index 95% rename from src/servers/presence_search_player/src/contracts/responses.py rename to src/frontends/gamespy/protocols/presence_search_player/contracts/responses.py index 5bdb52f76..b886eb9c6 100644 --- a/src/servers/presence_search_player/src/contracts/responses.py +++ b/src/frontends/gamespy/protocols/presence_search_player/contracts/responses.py @@ -1,12 +1,12 @@ -from servers.presence_search_player.src.abstractions.contracts import ResponseBase -from servers.presence_search_player.src.contracts.requests import ( +from frontends.gamespy.protocols.presence_search_player.abstractions.contracts import ResponseBase +from frontends.gamespy.protocols.presence_search_player.contracts.requests import ( CheckRequest, NewUserRequest, NicksRequest, UniqueSearchRequest, ValidRequest, ) -from servers.presence_search_player.src.contracts.results import ( +from frontends.gamespy.protocols.presence_search_player.contracts.results import ( CheckResult, NewUserResult, NicksResult, @@ -112,7 +112,7 @@ def __init__(self, result: SearchResult) -> None: def build(self): self.sending_buffer = f"\\bsr\\" - for info in self._result.result: + for info in self._result.data: self.sending_buffer += str(info.profile_id) self.sending_buffer += f"\\nick\\{info.nick}" self.sending_buffer += f"\\uniquenick\\{info.uniquenick}" diff --git a/src/servers/presence_search_player/src/contracts/results.py b/src/frontends/gamespy/protocols/presence_search_player/contracts/results.py similarity index 91% rename from src/servers/presence_search_player/src/contracts/results.py rename to src/frontends/gamespy/protocols/presence_search_player/contracts/results.py index c6b8c9c5f..2ec0cb20a 100644 --- a/src/servers/presence_search_player/src/contracts/results.py +++ b/src/frontends/gamespy/protocols/presence_search_player/contracts/results.py @@ -1,6 +1,6 @@ from pydantic import BaseModel -from servers.presence_search_player.src.abstractions.contracts import ResultBase +from frontends.gamespy.protocols.presence_search_player.abstractions.contracts import ResultBase class CheckResult(ResultBase): @@ -71,7 +71,7 @@ class SearchResultData(BaseModel): class SearchResult(ResultBase): - result: list[SearchResultData] + data: list[SearchResultData] class SearchUniqueResult(ResultBase): diff --git a/src/servers/game_status/tests/__init__.py b/src/frontends/gamespy/protocols/query_report/__init__.py similarity index 100% rename from src/servers/game_status/tests/__init__.py rename to src/frontends/gamespy/protocols/query_report/__init__.py diff --git a/src/frontends/gamespy/protocols/query_report/aggregates/exceptions.py b/src/frontends/gamespy/protocols/query_report/aggregates/exceptions.py new file mode 100644 index 000000000..14d7ba343 --- /dev/null +++ b/src/frontends/gamespy/protocols/query_report/aggregates/exceptions.py @@ -0,0 +1,5 @@ +from frontends.gamespy.library.exceptions.general import UniSpyException + + +class QRException(UniSpyException): + pass diff --git a/src/servers/query_report/src/aggregates/game_server_info.py b/src/frontends/gamespy/protocols/query_report/aggregates/game_server_info.py similarity index 78% rename from src/servers/query_report/src/aggregates/game_server_info.py rename to src/frontends/gamespy/protocols/query_report/aggregates/game_server_info.py index f7bbc578c..50820c6a9 100644 --- a/src/servers/query_report/src/aggregates/game_server_info.py +++ b/src/frontends/gamespy/protocols/query_report/aggregates/game_server_info.py @@ -3,8 +3,8 @@ from pydantic import BaseModel -from library.src.extentions.bytes_extentions import ip_to_4_bytes -from servers.query_report.src.v2.aggregates.enums import GameServerStatus +from frontends.gamespy.library.extentions.bytes_extentions import ip_to_4_bytes +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus class GameServerInfo(BaseModel): diff --git a/src/servers/query_report/src/aggregates/natneg_channel.py b/src/frontends/gamespy/protocols/query_report/aggregates/natneg_channel.py similarity index 70% rename from src/servers/query_report/src/aggregates/natneg_channel.py rename to src/frontends/gamespy/protocols/query_report/aggregates/natneg_channel.py index 60095a1ff..018a3e100 100644 --- a/src/servers/query_report/src/aggregates/natneg_channel.py +++ b/src/frontends/gamespy/protocols/query_report/aggregates/natneg_channel.py @@ -1,14 +1,14 @@ from typing import TYPE_CHECKING -from library.src.abstractions.brocker import BrockerBase -from library.src.configs import CONFIG -from library.src.log.log_manager import GLOBAL_LOGGER -from library.src.network.brockers import WebsocketBrocker -from servers.query_report.src.v2.applications.handlers import ClientMessageHandler -from servers.query_report.src.v2.contracts.requests import ClientMessageRequest +from frontends.gamespy.library.abstractions.brocker import BrockerBase +from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER +from frontends.gamespy.library.network.brockers import WebsocketBrocker +from frontends.gamespy.protocols.query_report.v2.applications.handlers import ClientMessageHandler +from frontends.gamespy.protocols.query_report.v2.contracts.requests import ClientMessageRequest from types import MappingProxyType if TYPE_CHECKING: - from servers.query_report.src.applications.client import Client + from frontends.gamespy.protocols.query_report.applications.client import Client class NatNegChannel: diff --git a/src/servers/query_report/src/aggregates/peer_room_info.py b/src/frontends/gamespy/protocols/query_report/aggregates/peer_room_info.py similarity index 100% rename from src/servers/query_report/src/aggregates/peer_room_info.py rename to src/frontends/gamespy/protocols/query_report/aggregates/peer_room_info.py diff --git a/src/servers/query_report/src/applications/client.py b/src/frontends/gamespy/protocols/query_report/applications/client.py similarity index 69% rename from src/servers/query_report/src/applications/client.py rename to src/frontends/gamespy/protocols/query_report/applications/client.py index c6ff8216b..198650f4f 100644 --- a/src/servers/query_report/src/applications/client.py +++ b/src/frontends/gamespy/protocols/query_report/applications/client.py @@ -1,4 +1,4 @@ -from library.src.abstractions.client import ClientBase +from frontends.gamespy.library.abstractions.client import ClientBase # import servers.query_report.v1 @@ -8,7 +8,7 @@ class Client(ClientBase): is_log_raw: bool = True def _create_switcher(self, buffer: bytes): - from servers.query_report.src.v2.applications.switcher import CmdSwitcher as V2CmdSwitcher + from frontends.gamespy.protocols.query_report.v2.applications.switcher import CmdSwitcher as V2CmdSwitcher assert isinstance(buffer, bytes) if buffer[0] == ord("\\"): raise NotImplementedError("v1 protocol not implemented") diff --git a/src/servers/query_report/src/applications/server_launcher.py b/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py similarity index 61% rename from src/servers/query_report/src/applications/server_launcher.py rename to src/frontends/gamespy/protocols/query_report/applications/server_launcher.py index ec23c1b06..26d47c8fd 100644 --- a/src/servers/query_report/src/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py @@ -1,7 +1,7 @@ -from library.src.abstractions.server_launcher import ServerLauncherBase -from library.src.network.udp_handler import UdpServer -from library.src.configs import CONFIG -from servers.query_report.src.applications.client import Client +from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.network.udp_handler import UdpServer +from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.protocols.query_report.applications.client import Client class ServerLauncher(ServerLauncherBase): diff --git a/src/servers/game_traffic_relay/src/__init__.py b/src/frontends/gamespy/protocols/query_report/v1/__init__.py similarity index 100% rename from src/servers/game_traffic_relay/src/__init__.py rename to src/frontends/gamespy/protocols/query_report/v1/__init__.py diff --git a/src/servers/game_traffic_relay/tests/__init__.py b/src/frontends/gamespy/protocols/query_report/v1/abstractions/__init__.py similarity index 100% rename from src/servers/game_traffic_relay/tests/__init__.py rename to src/frontends/gamespy/protocols/query_report/v1/abstractions/__init__.py diff --git a/src/servers/query_report/src/v1/abstractions/contracts.py b/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py similarity index 67% rename from src/servers/query_report/src/v1/abstractions/contracts.py rename to src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py index 5c222f59f..3b7757248 100644 --- a/src/servers/query_report/src/v1/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py @@ -1,10 +1,10 @@ -import library.src.abstractions.contracts +import frontends.gamespy.library.abstractions.contracts as lib import abc -from library.src.extentions.gamespy_utils import convert_to_key_value +from frontends.gamespy.library.extentions.gamespy_utils import convert_to_key_value -class RequestBase(library.src.abstractions.contracts.RequestBase): +class RequestBase(lib.RequestBase): request_dict: dict[str, str] raw_request: str @@ -18,11 +18,11 @@ def parse(self) -> None: self.command_name = list(self.request_dict.keys())[0] -class ResultBase(library.src.abstractions.contracts.ResultBase): +class ResultBase(lib.ResultBase): pass -class ResponseBase(library.src.abstractions.contracts.ResponseBase): +class ResponseBase(lib.ResponseBase): sending_buffer: str def __init__(self, request: RequestBase, result: ResultBase) -> None: diff --git a/src/frontends/gamespy/protocols/query_report/v1/abstractions/handlers.py b/src/frontends/gamespy/protocols/query_report/v1/abstractions/handlers.py new file mode 100644 index 000000000..dae695ee1 --- /dev/null +++ b/src/frontends/gamespy/protocols/query_report/v1/abstractions/handlers.py @@ -0,0 +1,10 @@ +import frontends.gamespy.library.abstractions.handler as lib +from frontends.gamespy.protocols.query_report.v1.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.query_report.applications.client import Client + + +class CmdHandlerBase(lib.CmdHandlerBase): + def __init__(self, client: Client, request: RequestBase) -> None: + assert issubclass(type(request), RequestBase) + assert isinstance(client, Client) + super().__init__(client, request) diff --git a/src/frontends/gamespy/protocols/query_report/v2/abstractions/cmd_handler_base.py b/src/frontends/gamespy/protocols/query_report/v2/abstractions/cmd_handler_base.py new file mode 100644 index 000000000..ba7bc9eb8 --- /dev/null +++ b/src/frontends/gamespy/protocols/query_report/v2/abstractions/cmd_handler_base.py @@ -0,0 +1,10 @@ +from frontends.gamespy.library.abstractions.handler import CmdHandlerBase as CHB +from frontends.gamespy.protocols.query_report.v2.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.query_report.applications.client import Client + + +class CmdHandlerBase(CHB): + def __init__(self, client: Client, request: RequestBase) -> None: + assert issubclass(type(request), RequestBase) + assert isinstance(client, Client) + super().__init__(client, request) diff --git a/src/servers/query_report/src/v2/abstractions/contracts.py b/src/frontends/gamespy/protocols/query_report/v2/abstractions/contracts.py similarity index 65% rename from src/servers/query_report/src/v2/abstractions/contracts.py rename to src/frontends/gamespy/protocols/query_report/v2/abstractions/contracts.py index 94573b54d..7bd9d310d 100644 --- a/src/servers/query_report/src/v2/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/query_report/v2/abstractions/contracts.py @@ -1,12 +1,12 @@ -from servers.query_report.src.v2.aggregates.enums import PacketType -import library.src.abstractions.contracts -from servers.query_report.src.aggregates.exceptions import QRException -from servers.query_report.src.v2.aggregates.enums import RequestType +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import PacketType +from frontends.gamespy.protocols.query_report.aggregates.exceptions import QRException +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import RequestType +import frontends.gamespy.library.abstractions.contracts as lib MAGIC_DATA = [0xFE, 0xFD] -class RequestBase(library.src.abstractions.contracts.RequestBase): +class RequestBase(lib.RequestBase): instant_key: str command_name: RequestType raw_request: bytes @@ -22,11 +22,11 @@ def parse(self): self.instant_key = str(int.from_bytes(self.raw_request[1:5])) -class ResultBase(library.src.abstractions.contracts.ResultBase): +class ResultBase(lib.ResultBase): packet_type: PacketType -class ResponseBase(library.src.abstractions.contracts.ResponseBase): +class ResponseBase(lib.ResponseBase): _result: ResultBase _request: RequestBase sending_buffer: bytes diff --git a/src/servers/query_report/src/v2/aggregates/enums.py b/src/frontends/gamespy/protocols/query_report/v2/aggregates/enums.py similarity index 100% rename from src/servers/query_report/src/v2/aggregates/enums.py rename to src/frontends/gamespy/protocols/query_report/v2/aggregates/enums.py diff --git a/src/servers/query_report/src/v2/applications/handlers.py b/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py similarity index 86% rename from src/servers/query_report/src/v2/applications/handlers.py rename to src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py index 56afac398..7011d4a6e 100644 --- a/src/servers/query_report/src/v2/applications/handlers.py +++ b/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py @@ -1,7 +1,7 @@ -from servers.query_report.src.applications.client import Client -from servers.query_report.src.v2.abstractions.cmd_handler_base import CmdHandlerBase -from servers.query_report.src.v2.contracts.requests import ( +from frontends.gamespy.protocols.query_report.applications.client import Client +from frontends.gamespy.protocols.query_report.v2.abstractions.cmd_handler_base import CmdHandlerBase +from frontends.gamespy.protocols.query_report.v2.contracts.requests import ( AvaliableRequest, ChallengeRequest, ClientMessageAckRequest, @@ -10,13 +10,13 @@ HeartBeatRequest, KeepAliveRequest, ) -from servers.query_report.src.v2.contracts.responses import ( +from frontends.gamespy.protocols.query_report.v2.contracts.responses import ( AvaliableResponse, ChallengeResponse, ClientMessageResponse, HeartBeatResponse, ) -from servers.query_report.src.v2.contracts.results import ChallengeResult, HeartBeatResult +from frontends.gamespy.protocols.query_report.v2.contracts.results import ChallengeResult, HeartBeatResult class AvailableHandler(CmdHandlerBase): diff --git a/src/servers/query_report/src/v2/applications/switcher.py b/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py similarity index 76% rename from src/servers/query_report/src/v2/applications/switcher.py rename to src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py index 0e5785cbe..fccf367f9 100644 --- a/src/servers/query_report/src/v2/applications/switcher.py +++ b/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py @@ -1,10 +1,10 @@ from typing import TYPE_CHECKING, Optional, cast -from library.src.abstractions.switcher import SwitcherBase -from library.src.exceptions.general import UniSpyException -from servers.query_report.src.applications.client import Client -from servers.query_report.src.v2.abstractions.cmd_handler_base import CmdHandlerBase +from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.protocols.query_report.applications.client import Client +from frontends.gamespy.protocols.query_report.v2.abstractions.cmd_handler_base import CmdHandlerBase -from servers.query_report.src.v2.contracts.requests import ( +from frontends.gamespy.protocols.query_report.v2.contracts.requests import ( AvaliableRequest, ChallengeRequest, ClientMessageAckRequest, @@ -13,8 +13,8 @@ HeartBeatRequest, KeepAliveRequest, ) -from servers.query_report.src.v2.aggregates.enums import RequestType -from servers.query_report.src.v2.applications.handlers import ( +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import RequestType +from frontends.gamespy.protocols.query_report.v2.applications.handlers import ( AvailableHandler, ChallengeHanler, ClientMessageAckHandler, diff --git a/src/servers/query_report/src/v2/contracts/requests.py b/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py similarity index 94% rename from src/servers/query_report/src/v2/contracts/requests.py rename to src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py index 3d9c9fddd..87c349ef1 100644 --- a/src/servers/query_report/src/v2/contracts/requests.py +++ b/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py @@ -1,9 +1,9 @@ from typing import Optional from uuid import UUID -from library.src.extentions.encoding import get_string -from servers.query_report.src.aggregates.exceptions import QRException -from servers.query_report.src.v2.abstractions.contracts import RequestBase -from servers.query_report.src.v2.aggregates.enums import GameServerStatus +from frontends.gamespy.library.extentions.encoding import get_string +from frontends.gamespy.protocols.query_report.aggregates.exceptions import QRException +from frontends.gamespy.protocols.query_report.v2.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus PREFIX = bytes([0x09, 0x00, 0x00, 0x00, 0x00]) diff --git a/src/servers/query_report/src/v2/contracts/responses.py b/src/frontends/gamespy/protocols/query_report/v2/contracts/responses.py similarity index 83% rename from src/servers/query_report/src/v2/contracts/responses.py rename to src/frontends/gamespy/protocols/query_report/v2/contracts/responses.py index 68d6e8181..19bed6668 100644 --- a/src/servers/query_report/src/v2/contracts/responses.py +++ b/src/frontends/gamespy/protocols/query_report/v2/contracts/responses.py @@ -1,18 +1,18 @@ -from library.src.extentions.encoding import get_bytes -from servers.query_report.src.v2.abstractions.contracts import ResponseBase -from servers.query_report.src.v2.contracts.requests import ( +from frontends.gamespy.library.extentions.encoding import get_bytes +from frontends.gamespy.protocols.query_report.v2.abstractions.contracts import ResponseBase +from frontends.gamespy.protocols.query_report.v2.contracts.requests import ( AvaliableRequest, ChallengeRequest, ClientMessageRequest, HeartBeatRequest, KeepAliveRequest ) -from servers.query_report.src.v2.contracts.results import ( +from frontends.gamespy.protocols.query_report.v2.contracts.results import ( ChallengeResult, HeartBeatResult, ) -from servers.query_report.src.v2.aggregates.enums import ServerAvailability -from library.src.extentions.bytes_extentions import ip_to_4_bytes, port_to_2_bytes +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import ServerAvailability +from frontends.gamespy.library.extentions.bytes_extentions import ip_to_4_bytes, port_to_2_bytes RESPONSE_PREFIX = bytes([0xFE, 0xFD, 0x09, 0x00, 0x00, 0x00]) diff --git a/src/servers/query_report/src/v2/contracts/results.py b/src/frontends/gamespy/protocols/query_report/v2/contracts/results.py similarity index 76% rename from src/servers/query_report/src/v2/contracts/results.py rename to src/frontends/gamespy/protocols/query_report/v2/contracts/results.py index 91a233a54..e4b723f3f 100644 --- a/src/servers/query_report/src/v2/contracts/results.py +++ b/src/frontends/gamespy/protocols/query_report/v2/contracts/results.py @@ -1,6 +1,6 @@ from typing import final -from servers.query_report.src.v2.abstractions.contracts import ResultBase -from servers.query_report.src.v2.aggregates.enums import PacketType +from frontends.gamespy.protocols.query_report.v2.abstractions.contracts import ResultBase +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import PacketType @final diff --git a/src/servers/natneg/__init__.py b/src/frontends/gamespy/protocols/server_browser/__init__.py similarity index 100% rename from src/servers/natneg/__init__.py rename to src/frontends/gamespy/protocols/server_browser/__init__.py diff --git a/src/servers/server_browser/src/aggregates/exceptions.py b/src/frontends/gamespy/protocols/server_browser/aggregates/exceptions.py similarity index 63% rename from src/servers/server_browser/src/aggregates/exceptions.py rename to src/frontends/gamespy/protocols/server_browser/aggregates/exceptions.py index cf3be195c..2d20e15e9 100644 --- a/src/servers/server_browser/src/aggregates/exceptions.py +++ b/src/frontends/gamespy/protocols/server_browser/aggregates/exceptions.py @@ -1,4 +1,4 @@ -from library.src.exceptions.general import UniSpyException +from frontends.gamespy.library.exceptions.general import UniSpyException class ServerBrowserException(UniSpyException): diff --git a/src/servers/natneg/src/__init__.py b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/__init__.py similarity index 100% rename from src/servers/natneg/src/__init__.py rename to src/frontends/gamespy/protocols/server_browser/v2/abstractions/__init__.py diff --git a/src/servers/server_browser/src/v2/abstractions/contracts.py b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py similarity index 84% rename from src/servers/server_browser/src/v2/abstractions/contracts.py rename to src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py index ac7808dcc..d01c2e7be 100644 --- a/src/servers/server_browser/src/v2/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py @@ -1,13 +1,12 @@ from socket import inet_ntoa -from typing import TYPE_CHECKING -import library.src.abstractions.contracts - -from library.src.extentions.bytes_extentions import ip_to_4_bytes -from library.src.extentions.encoding import get_bytes -from servers.query_report.src.aggregates.game_server_info import GameServerInfo -from servers.server_browser.src.v2.aggregations.encryption import SERVER_CHALLENGE -from servers.server_browser.src.v2.aggregations.string_flags import STRING_SPLITER -from servers.server_browser.src.v2.aggregations.enums import ( +import frontends.gamespy.library.abstractions.contracts as lib + +from frontends.gamespy.library.extentions.bytes_extentions import ip_to_4_bytes +from frontends.gamespy.library.extentions.encoding import get_bytes +from frontends.gamespy.protocols.query_report.aggregates.game_server_info import GameServerInfo +from frontends.gamespy.protocols.server_browser.v2.aggregations.encryption import SERVER_CHALLENGE +from frontends.gamespy.protocols.server_browser.v2.aggregations.string_flags import STRING_SPLITER +from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import ( DataKeyType, GameServerFlags, RequestType, @@ -17,7 +16,7 @@ QUERY_REPORT_DEFAULT_PORT: int = int(6500) -class RequestBase(library.src.abstractions.contracts.RequestBase): +class RequestBase(lib.RequestBase): request_length: int raw_request: bytes command_name: RequestType @@ -32,11 +31,11 @@ def parse(self) -> None: self.command_name = RequestType(self.raw_request[2]) -class ResultBase(library.src.abstractions.contracts.ResultBase): +class ResultBase(lib.ResultBase): pass -class ResponseBase(library.src.abstractions.contracts.ResponseBase): +class ResponseBase(lib.ResponseBase): _request: RequestBase _result: ResultBase sending_buffer: bytes diff --git a/src/servers/server_browser/src/v2/abstractions/handlers.py b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/handlers.py similarity index 78% rename from src/servers/server_browser/src/v2/abstractions/handlers.py rename to src/frontends/gamespy/protocols/server_browser/v2/abstractions/handlers.py index 0d568d5d9..e55db0f53 100644 --- a/src/servers/server_browser/src/v2/abstractions/handlers.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/handlers.py @@ -1,14 +1,14 @@ -from library.src.abstractions.handler import CmdHandlerBase as CMB +from frontends.gamespy.library.abstractions.handler import CmdHandlerBase as CMB import abc -from servers.server_browser.src.v2.abstractions.contracts import ( +from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import ( ServerListUpdateOptionRequestBase, ServerListUpdateOptionResponseBase, ServerListUpdateOptionResultBase, ) -from servers.server_browser.src.v2.aggregations.encryption import EnctypeX -from servers.server_browser.src.v2.applications.client import Client -from servers.server_browser.src.v2.abstractions.contracts import ( +from frontends.gamespy.protocols.server_browser.v2.aggregations.encryption import EnctypeX +from frontends.gamespy.protocols.server_browser.v2.applications.client import Client +from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import ( RequestBase, ResultBase, ResponseBase, diff --git a/src/servers/server_browser/src/v2/aggregations/encryption.py b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/encryption.py similarity index 98% rename from src/servers/server_browser/src/v2/aggregations/encryption.py rename to src/frontends/gamespy/protocols/server_browser/v2/aggregations/encryption.py index b0f83014c..b2b03640f 100644 --- a/src/servers/server_browser/src/v2/aggregations/encryption.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/encryption.py @@ -1,4 +1,4 @@ -from library.src.abstractions.enctypt_base import EncryptBase +from frontends.gamespy.library.abstractions.enctypt_base import EncryptBase SERVER_CHALLENGE = "0000000000" diff --git a/src/servers/server_browser/src/v2/aggregations/enums.py b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/enums.py similarity index 100% rename from src/servers/server_browser/src/v2/aggregations/enums.py rename to src/frontends/gamespy/protocols/server_browser/v2/aggregations/enums.py diff --git a/src/servers/server_browser/src/v2/aggregations/server_info_builder.py b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/server_info_builder.py similarity index 94% rename from src/servers/server_browser/src/v2/aggregations/server_info_builder.py rename to src/frontends/gamespy/protocols/server_browser/v2/aggregations/server_info_builder.py index 3185395f5..9af3ef6b3 100644 --- a/src/servers/server_browser/src/v2/aggregations/server_info_builder.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/server_info_builder.py @@ -1,9 +1,9 @@ from socket import inet_aton -from servers.query_report.src.aggregates.game_server_info import GameServerInfo +from frontends.gamespy.protocols.query_report.aggregates.game_server_info import GameServerInfo # from backends.protocols.gamespy.query_report.data import get_all_groups -from servers.server_browser.src.v2.aggregations.enums import GameServerFlags +from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import GameServerFlags # PEER_GROUP_LIST = get_all_groups() QUERY_REPORT_DEFAULT_PORT = 6500 diff --git a/src/servers/server_browser/src/v2/aggregations/string_flags.py b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/string_flags.py similarity index 100% rename from src/servers/server_browser/src/v2/aggregations/string_flags.py rename to src/frontends/gamespy/protocols/server_browser/v2/aggregations/string_flags.py diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/client.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/client.py new file mode 100644 index 000000000..881da507f --- /dev/null +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/client.py @@ -0,0 +1,23 @@ +from typing import TYPE_CHECKING +from frontends.gamespy.library.abstractions.client import ClientBase, ClientInfoBase +from frontends.gamespy.library.abstractions.enctypt_base import EncryptBase +from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import ServerListUpdateOption +if TYPE_CHECKING: + from frontends.gamespy.library.abstractions.switcher import SwitcherBase + + +class ClientInfo(ClientInfoBase): + game_secret_key: str + client_challenge: str + search_type: ServerListUpdateOption + game_name: str + + +class Client(ClientBase): + is_log_raw: bool = True + info: ClientInfo + crypto: EncryptBase + + def _create_switcher(self, buffer: bytes) -> "SwitcherBase": + from frontends.gamespy.protocols.server_browser.v2.applications.switcher import CmdSwitcher + return CmdSwitcher(self, buffer) diff --git a/src/servers/server_browser/src/v2/applications/handlers.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py similarity index 83% rename from src/servers/server_browser/src/v2/applications/handlers.py rename to src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py index 3851605a3..5bf3301aa 100644 --- a/src/servers/server_browser/src/v2/applications/handlers.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py @@ -1,33 +1,33 @@ from concurrent.futures import ProcessPoolExecutor from typing import TYPE_CHECKING, cast -from servers.query_report.src.aggregates.game_server_info import GameServerInfo -from servers.query_report.src.v2.contracts.requests import ClientMessageRequest -from servers.query_report.src.v2.aggregates.enums import GameServerStatus, RequestType -from servers.server_browser.src.aggregates.exceptions import ServerBrowserException -from servers.server_browser.src.v2.abstractions.contracts import RequestBase -from servers.server_browser.src.v2.contracts.requests import ( +from frontends.gamespy.protocols.query_report.aggregates.game_server_info import GameServerInfo +from frontends.gamespy.protocols.query_report.v2.contracts.requests import ClientMessageRequest +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus, RequestType +from frontends.gamespy.protocols.server_browser.aggregates.exceptions import ServerBrowserException +from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.server_browser.v2.contracts.requests import ( SendMessageRequest, ServerInfoRequest, ServerListRequest, ) -from servers.server_browser.src.v2.contracts.responses import ( +from frontends.gamespy.protocols.server_browser.v2.contracts.responses import ( DeleteServerInfoResponse, P2PGroupRoomListResponse, ServerMainListResponse, ServerNetworkInfoListResponse, UpdateServerInfoResponse, ) -from servers.server_browser.src.v2.contracts.results import ( +from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( ServerInfoResult, ServerMainListResult, ) -from servers.server_browser.src.v2.aggregations.enums import ( +from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import ( # RequestType, ServerListUpdateOption, ) -from servers.server_browser.src.v2.abstractions.handlers import CmdHandlerBase +from frontends.gamespy.protocols.server_browser.v2.abstractions.handlers import CmdHandlerBase -from servers.server_browser.src.v2.applications.client import Client +from frontends.gamespy.protocols.server_browser.v2.applications.client import Client def get_clients(game_name: str): diff --git a/src/servers/server_browser/src/v2/applications/server_launcher.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/server_launcher.py similarity index 59% rename from src/servers/server_browser/src/v2/applications/server_launcher.py rename to src/frontends/gamespy/protocols/server_browser/v2/applications/server_launcher.py index 3eb9a58b4..7fba830fb 100644 --- a/src/servers/server_browser/src/v2/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/server_launcher.py @@ -1,7 +1,7 @@ -from library.src.abstractions.server_launcher import ServerLauncherBase -from library.src.network.tcp_handler import TcpServer -from library.src.configs import CONFIG -from servers.server_browser.src.v2.applications.client import Client +from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.network.tcp_handler import TcpServer +from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.protocols.server_browser.v2.applications.client import Client class ServerLauncher(ServerLauncherBase): diff --git a/src/servers/server_browser/src/v2/applications/switcher.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py similarity index 62% rename from src/servers/server_browser/src/v2/applications/switcher.py rename to src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py index db303a39a..6366ca197 100644 --- a/src/servers/server_browser/src/v2/applications/switcher.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py @@ -1,11 +1,11 @@ from typing import TYPE_CHECKING, Optional, cast -from library.src.abstractions.switcher import SwitcherBase -from library.src.exceptions.general import UniSpyException -from servers.server_browser.src.v2.abstractions.handlers import CmdHandlerBase -from servers.server_browser.src.v2.aggregations.enums import RequestType -from servers.server_browser.src.v2.applications.client import Client -from servers.server_browser.src.v2.applications.handlers import SendMessageHandler, ServerInfoHandler, ServerListHandler -from servers.server_browser.src.v2.contracts.requests import SendMessageRequest, ServerInfoRequest, ServerListRequest +from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.protocols.server_browser.v2.abstractions.handlers import CmdHandlerBase +from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import RequestType +from frontends.gamespy.protocols.server_browser.v2.applications.client import Client +from frontends.gamespy.protocols.server_browser.v2.applications.handlers import SendMessageHandler, ServerInfoHandler, ServerListHandler +from frontends.gamespy.protocols.server_browser.v2.contracts.requests import SendMessageRequest, ServerInfoRequest, ServerListRequest class CmdSwitcher(SwitcherBase): diff --git a/src/servers/natneg/tests/__init__.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/__init__.py similarity index 100% rename from src/servers/natneg/tests/__init__.py rename to src/frontends/gamespy/protocols/server_browser/v2/contracts/__init__.py diff --git a/src/servers/server_browser/src/v2/contracts/requests.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py similarity index 92% rename from src/servers/server_browser/src/v2/contracts/requests.py rename to src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py index f4d0b0291..201fccbd5 100644 --- a/src/servers/server_browser/src/v2/contracts/requests.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py @@ -1,10 +1,10 @@ from socket import inet_ntoa from typing import Optional -from servers.server_browser.src.v2.abstractions.contracts import ( +from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import ( AdHocRequestBase, ServerListUpdateOptionRequestBase, ) -from servers.server_browser.src.v2.aggregations.enums import RequestType, ServerListUpdateOption +from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import RequestType, ServerListUpdateOption class ServerListRequest(ServerListUpdateOptionRequestBase): diff --git a/src/servers/server_browser/src/v2/contracts/responses.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py similarity index 88% rename from src/servers/server_browser/src/v2/contracts/responses.py rename to src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py index d53b23ea7..c2ab8d473 100644 --- a/src/servers/server_browser/src/v2/contracts/responses.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py @@ -1,20 +1,20 @@ -from library.src.extentions.encoding import get_bytes -from servers.server_browser.src.v2.abstractions.contracts import ( +from frontends.gamespy.library.extentions.encoding import get_bytes +from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import ( AdHocResponseBase, ServerListUpdateOptionResponseBase, ) -from servers.server_browser.src.v2.aggregations.server_info_builder import ( +from frontends.gamespy.protocols.server_browser.v2.aggregations.server_info_builder import ( build_server_info_header, ) -from servers.server_browser.src.v2.aggregations.string_flags import * -from servers.server_browser.src.v2.contracts.requests import ServerListRequest -from servers.server_browser.src.v2.contracts.results import ( +from frontends.gamespy.protocols.server_browser.v2.aggregations.string_flags import * +from frontends.gamespy.protocols.server_browser.v2.contracts.requests import ServerListRequest +from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( P2PGroupRoomListResult, ServerInfoResult, ServerMainListResult, ) -from servers.server_browser.src.v2.aggregations.enums import GameServerFlags, ResponseType +from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import GameServerFlags, ResponseType class DeleteServerInfoResponse(AdHocResponseBase): diff --git a/src/servers/server_browser/src/v2/contracts/results.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py similarity index 59% rename from src/servers/server_browser/src/v2/contracts/results.py rename to src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py index c966da522..6383d49c4 100644 --- a/src/servers/server_browser/src/v2/contracts/results.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py @@ -2,10 +2,10 @@ from uuid import UUID from pydantic import BaseModel -from servers.query_report.src.aggregates.game_server_info import GameServerInfo -from servers.query_report.src.aggregates.peer_room_info import PeerRoomInfo -from servers.query_report.src.v2.aggregates.enums import GameServerStatus -from servers.server_browser.src.v2.abstractions.contracts import ( +from frontends.gamespy.protocols.query_report.aggregates.game_server_info import GameServerInfo +from frontends.gamespy.protocols.query_report.aggregates.peer_room_info import PeerRoomInfo +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus +from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import ( AdHocResultBase, ResultBase, ServerListUpdateOptionResultBase, diff --git a/src/servers/presence_connection_manager/__init__.py b/src/frontends/gamespy/protocols/web_services/__init__.py similarity index 100% rename from src/servers/presence_connection_manager/__init__.py rename to src/frontends/gamespy/protocols/web_services/__init__.py diff --git a/src/servers/web_services/src/abstractions/contracts.py b/src/frontends/gamespy/protocols/web_services/abstractions/contracts.py similarity index 83% rename from src/servers/web_services/src/abstractions/contracts.py rename to src/frontends/gamespy/protocols/web_services/abstractions/contracts.py index b495c48d8..aec2c8fd6 100644 --- a/src/servers/web_services/src/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/web_services/abstractions/contracts.py @@ -1,8 +1,8 @@ -import library.src.abstractions.contracts as lib +import frontends.gamespy.library.abstractions.contracts as lib import xml.etree.ElementTree as ET -from library.src.exceptions.general import UniSpyException -from servers.web_services.src.aggregations.soap_envelop import SoapEnvelop +from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.protocols.web_services.aggregations.soap_envelop import SoapEnvelop class RequestBase(lib.RequestBase): diff --git a/src/servers/web_services/src/abstractions/handler.py b/src/frontends/gamespy/protocols/web_services/abstractions/handler.py similarity index 54% rename from src/servers/web_services/src/abstractions/handler.py rename to src/frontends/gamespy/protocols/web_services/abstractions/handler.py index 974ff3697..3fa1bb01d 100644 --- a/src/servers/web_services/src/abstractions/handler.py +++ b/src/frontends/gamespy/protocols/web_services/abstractions/handler.py @@ -1,6 +1,6 @@ -import library.src.abstractions.handler as lib -from servers.web_services.src.applications.client import Client -from servers.web_services.src.abstractions.contracts import RequestBase +import frontends.gamespy.library.abstractions.handler as lib +from frontends.gamespy.protocols.web_services.applications.client import Client +from frontends.gamespy.protocols.web_services.abstractions.contracts import RequestBase class CmdHandlerBase(lib.CmdHandlerBase): diff --git a/src/frontends/gamespy/protocols/web_services/aggregations/exceptions.py b/src/frontends/gamespy/protocols/web_services/aggregations/exceptions.py new file mode 100644 index 000000000..b4f839817 --- /dev/null +++ b/src/frontends/gamespy/protocols/web_services/aggregations/exceptions.py @@ -0,0 +1,6 @@ +from frontends.gamespy.library.exceptions.general import UniSpyException + + +class WebException(UniSpyException): + pass + diff --git a/src/servers/web_services/src/aggregations/soap_envelop.py b/src/frontends/gamespy/protocols/web_services/aggregations/soap_envelop.py similarity index 95% rename from src/servers/web_services/src/aggregations/soap_envelop.py rename to src/frontends/gamespy/protocols/web_services/aggregations/soap_envelop.py index d34bef1ed..57949dffe 100644 --- a/src/servers/web_services/src/aggregations/soap_envelop.py +++ b/src/frontends/gamespy/protocols/web_services/aggregations/soap_envelop.py @@ -1,6 +1,6 @@ import xml.etree.ElementTree as ET -from servers.web_services.src.aggregations.exceptions import WebException +from frontends.gamespy.protocols.web_services.aggregations.exceptions import WebException class SoapEnvelop: diff --git a/src/servers/web_services/src/applications/client.py b/src/frontends/gamespy/protocols/web_services/applications/client.py similarity index 87% rename from src/servers/web_services/src/applications/client.py rename to src/frontends/gamespy/protocols/web_services/applications/client.py index befde86c4..7c80e4e58 100644 --- a/src/servers/web_services/src/applications/client.py +++ b/src/frontends/gamespy/protocols/web_services/applications/client.py @@ -1,8 +1,8 @@ -from library.src.abstractions.client import ClientBase, ClientInfoBase -from library.src.abstractions.switcher import SwitcherBase -from library.src.log.log_manager import LogWriter -from library.src.network.http_handler import HttpConnection -from library.src.configs import ServerConfig +from frontends.gamespy.library.abstractions.client import ClientBase, ClientInfoBase +from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.library.log.log_manager import LogWriter +from frontends.gamespy.library.network.http_handler import HttpConnection +from frontends.gamespy.library.configs import ServerConfig class ClientInfo(ClientInfoBase): @@ -38,5 +38,5 @@ def __init__(self, connection: HttpConnection, server_config: ServerConfig, logg self.info = ClientInfo() def _create_switcher(self, buffer: bytes) -> SwitcherBase: - from servers.web_services.src.applications.switcher import Switcher + from frontends.gamespy.protocols.web_services.applications.switcher import Switcher return Switcher(self, buffer.decode()) diff --git a/src/servers/web_services/src/applications/server_launcher.py b/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py similarity index 61% rename from src/servers/web_services/src/applications/server_launcher.py rename to src/frontends/gamespy/protocols/web_services/applications/server_launcher.py index aa996d580..89fdd6f88 100644 --- a/src/servers/web_services/src/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py @@ -1,7 +1,7 @@ -from library.src.abstractions.server_launcher import ServerLauncherBase -from library.src.network.http_handler import HttpServer -from library.src.configs import CONFIG -from servers.web_services.src.applications.client import Client +from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.network.http_handler import HttpServer +from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.protocols.web_services.applications.client import Client class ServerLauncher(ServerLauncherBase): diff --git a/src/servers/web_services/src/applications/switcher.py b/src/frontends/gamespy/protocols/web_services/applications/switcher.py similarity index 75% rename from src/servers/web_services/src/applications/switcher.py rename to src/frontends/gamespy/protocols/web_services/applications/switcher.py index 12da40859..acc1fce11 100644 --- a/src/servers/web_services/src/applications/switcher.py +++ b/src/frontends/gamespy/protocols/web_services/applications/switcher.py @@ -1,17 +1,17 @@ from typing import TYPE_CHECKING, Optional, cast -from library.src.abstractions.client import ClientBase -from library.src.abstractions.handler import CmdHandlerBase -from library.src.abstractions.switcher import SwitcherBase +from frontends.gamespy.library.abstractions.client import ClientBase +from frontends.gamespy.library.abstractions.handler import CmdHandlerBase +from frontends.gamespy.library.abstractions.switcher import SwitcherBase import xml.etree.ElementTree as ET -from servers.web_services.src.applications.client import Client -from servers.web_services.src.aggregations.exceptions import WebException -from servers.web_services.src.modules.auth.contracts.requests import LoginProfileRequest, LoginProfileWithGameIdRequest, LoginRemoteAuthRequest, LoginRemoteAuthWithGameIdRequest, LoginUniqueNickRequest, LoginUniqueNickWithGameIdRequest -from servers.web_services.src.modules.auth.handlers.general import LoginProfileHandler, LoginProfileWithGameIdHandler, LoginRemoteAuthHandler, LoginRemoteAuthWithGameIdHandler, LoginUniqueNickHandler, LoginUniqueNickWithGameIdHandler -from servers.web_services.src.modules.direct2game.contracts.requests import GetPurchaseHistoryRequest, GetStoreAvailabilityRequest -from servers.web_services.src.modules.direct2game.handlers.general import GetPurchaseHistoryHandler, GetStoreAvailabilityHandler -from servers.web_services.src.modules.sake.contracts.requests import CreateRecordRequest, GetMyRecordsRequest, SearchForRecordsRequest -from servers.web_services.src.modules.sake.applications.handlers import CreateRecordHandler, GetMyRecordsHandler, SearchForRecordsHandler +from frontends.gamespy.protocols.web_services.applications.client import Client +from frontends.gamespy.protocols.web_services.aggregations.exceptions import WebException +from frontends.gamespy.protocols.web_services.modules.auth.contracts.requests import LoginProfileRequest, LoginProfileWithGameIdRequest, LoginRemoteAuthRequest, LoginRemoteAuthWithGameIdRequest, LoginUniqueNickRequest, LoginUniqueNickWithGameIdRequest +from frontends.gamespy.protocols.web_services.modules.auth.handlers.general import LoginProfileHandler, LoginProfileWithGameIdHandler, LoginRemoteAuthHandler, LoginRemoteAuthWithGameIdHandler, LoginUniqueNickHandler, LoginUniqueNickWithGameIdHandler +from frontends.gamespy.protocols.web_services.modules.direct2game.contracts.requests import GetPurchaseHistoryRequest, GetStoreAvailabilityRequest +from frontends.gamespy.protocols.web_services.modules.direct2game.handlers.general import GetPurchaseHistoryHandler, GetStoreAvailabilityHandler +from frontends.gamespy.protocols.web_services.modules.sake.contracts.requests import CreateRecordRequest, GetMyRecordsRequest, SearchForRecordsRequest +from frontends.gamespy.protocols.web_services.modules.sake.applications.handlers import CreateRecordHandler, GetMyRecordsHandler, SearchForRecordsHandler class Switcher(SwitcherBase): diff --git a/src/servers/web_services/src/modules/altas/___init__.py b/src/frontends/gamespy/protocols/web_services/modules/altas/___init__.py similarity index 100% rename from src/servers/web_services/src/modules/altas/___init__.py rename to src/frontends/gamespy/protocols/web_services/modules/altas/___init__.py diff --git a/src/servers/web_services/src/modules/auth/abstractions/general.py b/src/frontends/gamespy/protocols/web_services/modules/auth/abstractions/general.py similarity index 92% rename from src/servers/web_services/src/modules/auth/abstractions/general.py rename to src/frontends/gamespy/protocols/web_services/modules/auth/abstractions/general.py index a843be40b..214f8ccd4 100644 --- a/src/servers/web_services/src/modules/auth/abstractions/general.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/abstractions/general.py @@ -1,8 +1,8 @@ import hashlib -import servers.web_services.src.abstractions.contracts as lib -from servers.web_services.src.aggregations.soap_envelop import SoapEnvelop -from servers.web_services.src.applications.client import ClientInfo -from servers.web_services.src.modules.auth.exceptions.general import AuthException +import frontends.gamespy.protocols.web_services.abstractions.contracts as lib +from frontends.gamespy.protocols.web_services.aggregations.soap_envelop import SoapEnvelop +from frontends.gamespy.protocols.web_services.applications.client import ClientInfo +from frontends.gamespy.protocols.web_services.modules.auth.exceptions.general import AuthException import datetime NAMESPACE = "http://gamespy.net/AuthService/" diff --git a/src/servers/web_services/src/modules/auth/contracts/requests.py b/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/requests.py similarity index 96% rename from src/servers/web_services/src/modules/auth/contracts/requests.py rename to src/frontends/gamespy/protocols/web_services/modules/auth/contracts/requests.py index 6dc8dd3e1..c4688c5d8 100644 --- a/src/servers/web_services/src/modules/auth/contracts/requests.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/requests.py @@ -1,8 +1,8 @@ -from servers.web_services.src.modules.auth.abstractions.general import ( +from frontends.gamespy.protocols.web_services.modules.auth.abstractions.general import ( NAMESPACE, LoginRequestBase, ) -from servers.web_services.src.modules.auth.exceptions.general import AuthException +from frontends.gamespy.protocols.web_services.modules.auth.exceptions.general import AuthException class LoginProfileRequest(LoginRequestBase): diff --git a/src/servers/web_services/src/modules/auth/contracts/responses.py b/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/responses.py similarity index 87% rename from src/servers/web_services/src/modules/auth/contracts/responses.py rename to src/frontends/gamespy/protocols/web_services/modules/auth/contracts/responses.py index d22f5df2f..26f4a62b5 100644 --- a/src/servers/web_services/src/modules/auth/contracts/responses.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/responses.py @@ -1,9 +1,9 @@ -from servers.web_services.src.modules.auth.abstractions.general import LoginResponseBase -from servers.web_services.src.modules.auth.contracts.requests import ( +from frontends.gamespy.protocols.web_services.modules.auth.abstractions.general import LoginResponseBase +from frontends.gamespy.protocols.web_services.modules.auth.contracts.requests import ( LoginProfileRequest, LoginProfileWithGameIdRequest, ) -from servers.web_services.src.modules.auth.contracts.results import ( +from frontends.gamespy.protocols.web_services.modules.auth.contracts.results import ( LoginProfileResult, LoginPs3CertResult, ) diff --git a/src/servers/web_services/src/modules/auth/contracts/results.py b/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/results.py similarity index 71% rename from src/servers/web_services/src/modules/auth/contracts/results.py rename to src/frontends/gamespy/protocols/web_services/modules/auth/contracts/results.py index ac289868b..1e1b985ea 100644 --- a/src/servers/web_services/src/modules/auth/contracts/results.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/results.py @@ -1,4 +1,4 @@ -from servers.web_services.src.modules.auth.abstractions.general import LoginResultBase +from frontends.gamespy.protocols.web_services.modules.auth.abstractions.general import LoginResultBase class LoginProfileResult(LoginResultBase): diff --git a/src/frontends/gamespy/protocols/web_services/modules/auth/exceptions/general.py b/src/frontends/gamespy/protocols/web_services/modules/auth/exceptions/general.py new file mode 100644 index 000000000..7bed127fd --- /dev/null +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/exceptions/general.py @@ -0,0 +1,5 @@ +from frontends.gamespy.protocols.web_services.aggregations.exceptions import WebException + + +class AuthException(WebException): + pass diff --git a/src/servers/web_services/src/modules/auth/handlers/general.py b/src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py similarity index 83% rename from src/servers/web_services/src/modules/auth/handlers/general.py rename to src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py index ee650691b..87252a6d1 100644 --- a/src/servers/web_services/src/modules/auth/handlers/general.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py @@ -1,6 +1,6 @@ -from servers.web_services.src.abstractions.handler import CmdHandlerBase -from servers.web_services.src.modules.auth.abstractions.general import LoginResultBase -from servers.web_services.src.modules.auth.contracts.requests import ( +from frontends.gamespy.protocols.web_services.abstractions.handler import CmdHandlerBase +from frontends.gamespy.protocols.web_services.modules.auth.abstractions.general import LoginResultBase +from frontends.gamespy.protocols.web_services.modules.auth.contracts.requests import ( LoginProfileRequest, LoginProfileWithGameIdRequest, LoginPs3CertRequest, @@ -10,7 +10,7 @@ LoginUniqueNickRequest, LoginUniqueNickWithGameIdRequest, ) -from servers.web_services.src.modules.auth.contracts.responses import ( +from frontends.gamespy.protocols.web_services.modules.auth.contracts.responses import ( LoginProfileResponse, LoginProfileWithGameIdResponse, LoginRemoteAuthResponse, @@ -18,7 +18,7 @@ LoginUniqueNickResponse, LoginUniqueNickWithGameIdResponse, ) -from servers.web_services.src.modules.auth.contracts.results import ( +from frontends.gamespy.protocols.web_services.modules.auth.contracts.results import ( LoginProfileResult, LoginPs3CertResult, LoginRemoteAuthResult, diff --git a/src/servers/web_services/src/modules/direct2game/abstractions/contracts.py b/src/frontends/gamespy/protocols/web_services/modules/direct2game/abstractions/contracts.py similarity index 60% rename from src/servers/web_services/src/modules/direct2game/abstractions/contracts.py rename to src/frontends/gamespy/protocols/web_services/modules/direct2game/abstractions/contracts.py index 3d0e7431c..07ada7d07 100644 --- a/src/servers/web_services/src/modules/direct2game/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/web_services/modules/direct2game/abstractions/contracts.py @@ -1,5 +1,5 @@ -import servers.web_services.src.abstractions.contracts as lib -from servers.web_services.src.aggregations.soap_envelop import SoapEnvelop +import protocols.web_services.abstractions.contracts as lib +from frontends.gamespy.protocols.web_services.aggregations.soap_envelop import SoapEnvelop NAMESPACE = "http://gamespy.net/commerce/" diff --git a/src/servers/web_services/src/modules/direct2game/abstractions/handler.py b/src/frontends/gamespy/protocols/web_services/modules/direct2game/abstractions/handler.py similarity index 100% rename from src/servers/web_services/src/modules/direct2game/abstractions/handler.py rename to src/frontends/gamespy/protocols/web_services/modules/direct2game/abstractions/handler.py diff --git a/src/servers/web_services/src/modules/direct2game/contracts/requests.py b/src/frontends/gamespy/protocols/web_services/modules/direct2game/contracts/requests.py similarity index 92% rename from src/servers/web_services/src/modules/direct2game/contracts/requests.py rename to src/frontends/gamespy/protocols/web_services/modules/direct2game/contracts/requests.py index cb8fd70ba..6db32b4a2 100644 --- a/src/servers/web_services/src/modules/direct2game/contracts/requests.py +++ b/src/frontends/gamespy/protocols/web_services/modules/direct2game/contracts/requests.py @@ -1,5 +1,5 @@ -from servers.web_services.src.aggregations.exceptions import WebException -from servers.web_services.src.modules.direct2game.abstractions.contracts import ( +from frontends.gamespy.protocols.web_services.aggregations.exceptions import WebException +from frontends.gamespy.protocols.web_services.modules.direct2game.abstractions.contracts import ( NAMESPACE, RequestBase, ) diff --git a/src/servers/web_services/src/modules/direct2game/contracts/responses.py b/src/frontends/gamespy/protocols/web_services/modules/direct2game/contracts/responses.py similarity index 83% rename from src/servers/web_services/src/modules/direct2game/contracts/responses.py rename to src/frontends/gamespy/protocols/web_services/modules/direct2game/contracts/responses.py index 0d015a58f..1d083c07c 100644 --- a/src/servers/web_services/src/modules/direct2game/contracts/responses.py +++ b/src/frontends/gamespy/protocols/web_services/modules/direct2game/contracts/responses.py @@ -1,5 +1,5 @@ -from servers.web_services.src.modules.direct2game.abstractions.contracts import ResponseBase -from servers.web_services.src.modules.direct2game.contracts.results import ( +from frontends.gamespy.protocols.web_services.modules.direct2game.abstractions.contracts import ResponseBase +from frontends.gamespy.protocols.web_services.modules.direct2game.contracts.results import ( GetPurchaseHistoryResult, GetStoreAvailabilityResult, ) diff --git a/src/servers/web_services/src/modules/direct2game/contracts/results.py b/src/frontends/gamespy/protocols/web_services/modules/direct2game/contracts/results.py similarity index 77% rename from src/servers/web_services/src/modules/direct2game/contracts/results.py rename to src/frontends/gamespy/protocols/web_services/modules/direct2game/contracts/results.py index 5215630ee..97cf1e2b4 100644 --- a/src/servers/web_services/src/modules/direct2game/contracts/results.py +++ b/src/frontends/gamespy/protocols/web_services/modules/direct2game/contracts/results.py @@ -1,6 +1,6 @@ from enum import IntEnum -from servers.web_services.src.modules.direct2game.abstractions.contracts import ResultBase +from frontends.gamespy.protocols.web_services.modules.direct2game.abstractions.contracts import ResultBase class GetPurchaseHistoryResult(ResultBase): diff --git a/src/servers/web_services/src/modules/direct2game/handlers/general.py b/src/frontends/gamespy/protocols/web_services/modules/direct2game/handlers/general.py similarity index 67% rename from src/servers/web_services/src/modules/direct2game/handlers/general.py rename to src/frontends/gamespy/protocols/web_services/modules/direct2game/handlers/general.py index 581bc04bd..15057f678 100644 --- a/src/servers/web_services/src/modules/direct2game/handlers/general.py +++ b/src/frontends/gamespy/protocols/web_services/modules/direct2game/handlers/general.py @@ -1,15 +1,15 @@ -from servers.web_services.src.abstractions.contracts import RequestBase -from servers.web_services.src.abstractions.handler import CmdHandlerBase -from servers.web_services.src.applications.client import Client -from servers.web_services.src.modules.direct2game.contracts.requests import ( +from frontends.gamespy.protocols.web_services.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.web_services.abstractions.handler import CmdHandlerBase +from frontends.gamespy.protocols.web_services.applications.client import Client +from frontends.gamespy.protocols.web_services.modules.direct2game.contracts.requests import ( GetPurchaseHistoryRequest, GetStoreAvailabilityRequest, ) -from servers.web_services.src.modules.direct2game.contracts.responses import ( +from frontends.gamespy.protocols.web_services.modules.direct2game.contracts.responses import ( GetPurchaseHistoryResponse, GetStoreAvailabilityResponse, ) -from servers.web_services.src.modules.direct2game.contracts.results import ( +from frontends.gamespy.protocols.web_services.modules.direct2game.contracts.results import ( GetPurchaseHistoryResult, GetStoreAvailabilityResult, ) diff --git a/src/servers/web_services/src/modules/sake/abstractions/generals.py b/src/frontends/gamespy/protocols/web_services/modules/sake/abstractions/generals.py similarity index 84% rename from src/servers/web_services/src/modules/sake/abstractions/generals.py rename to src/frontends/gamespy/protocols/web_services/modules/sake/abstractions/generals.py index 26c0d8e15..b3ed391d6 100644 --- a/src/servers/web_services/src/modules/sake/abstractions/generals.py +++ b/src/frontends/gamespy/protocols/web_services/modules/sake/abstractions/generals.py @@ -1,7 +1,7 @@ -import servers.web_services.src.abstractions.handler as h -import servers.web_services.src.abstractions.contracts as lib -from servers.web_services.src.aggregations.soap_envelop import SoapEnvelop -from servers.web_services.src.modules.sake.exceptions.general import SakeException +import frontends.gamespy.protocols.web_services.abstractions.handler as h +import frontends.gamespy.protocols.web_services.abstractions.contracts as lib +from frontends.gamespy.protocols.web_services.aggregations.soap_envelop import SoapEnvelop +from frontends.gamespy.protocols.web_services.modules.sake.exceptions.general import SakeException import xml.etree.ElementTree as ET NAMESPACE = "http://gamespy.net/sake" diff --git a/src/servers/web_services/src/modules/sake/applications/handlers.py b/src/frontends/gamespy/protocols/web_services/modules/sake/applications/handlers.py similarity index 65% rename from src/servers/web_services/src/modules/sake/applications/handlers.py rename to src/frontends/gamespy/protocols/web_services/modules/sake/applications/handlers.py index 59dc05c7e..34abb223b 100644 --- a/src/servers/web_services/src/modules/sake/applications/handlers.py +++ b/src/frontends/gamespy/protocols/web_services/modules/sake/applications/handlers.py @@ -1,7 +1,7 @@ -from servers.web_services.src.applications.client import Client -from servers.web_services.src.modules.sake.abstractions.generals import CmdHandlerBase -from servers.web_services.src.modules.sake.contracts.requests import CreateRecordRequest, GetMyRecordsRequest, SearchForRecordsRequest -from servers.web_services.src.modules.sake.contracts.results import CreateRecordResult, GetMyRecordsResult, SearchForRecordsResult +from frontends.gamespy.protocols.web_services.applications.client import Client +from frontends.gamespy.protocols.web_services.modules.sake.abstractions.generals import CmdHandlerBase +from frontends.gamespy.protocols.web_services.modules.sake.contracts.requests import CreateRecordRequest, GetMyRecordsRequest, SearchForRecordsRequest +from frontends.gamespy.protocols.web_services.modules.sake.contracts.results import CreateRecordResult, GetMyRecordsResult, SearchForRecordsResult class CreateRecordHandler(CmdHandlerBase): diff --git a/src/servers/web_services/src/modules/sake/contracts/requests.py b/src/frontends/gamespy/protocols/web_services/modules/sake/contracts/requests.py similarity index 96% rename from src/servers/web_services/src/modules/sake/contracts/requests.py rename to src/frontends/gamespy/protocols/web_services/modules/sake/contracts/requests.py index effd87eb4..92352935a 100644 --- a/src/servers/web_services/src/modules/sake/contracts/requests.py +++ b/src/frontends/gamespy/protocols/web_services/modules/sake/contracts/requests.py @@ -2,14 +2,14 @@ from typing import Optional import xml.etree.ElementTree as ET -from servers.web_services.src.aggregations.exceptions import WebException -from servers.web_services.src.modules.sake.abstractions.generals import ( +from frontends.gamespy.protocols.web_services.aggregations.exceptions import WebException +from frontends.gamespy.protocols.web_services.modules.sake.abstractions.generals import ( RequestBase, NAMESPACE, ) import xmltodict -from servers.web_services.src.modules.sake.exceptions.general import SakeException +from frontends.gamespy.protocols.web_services.modules.sake.exceptions.general import SakeException class CreateRecordRequest(RequestBase): diff --git a/src/servers/web_services/src/modules/sake/contracts/responses.py b/src/frontends/gamespy/protocols/web_services/modules/sake/contracts/responses.py similarity index 73% rename from src/servers/web_services/src/modules/sake/contracts/responses.py rename to src/frontends/gamespy/protocols/web_services/modules/sake/contracts/responses.py index 7432f7d95..0c105f6db 100644 --- a/src/servers/web_services/src/modules/sake/contracts/responses.py +++ b/src/frontends/gamespy/protocols/web_services/modules/sake/contracts/responses.py @@ -1,6 +1,6 @@ -from servers.web_services.src.modules.sake.abstractions.generals import ResponseBase -from servers.web_services.src.modules.sake.contracts.requests import CreateRecordRequest, SearchForRecordsRequest -from servers.web_services.src.modules.sake.contracts.results import CreateRecordResult, SearchForRecordsResult +from frontends.gamespy.protocols.web_services.modules.sake.abstractions.generals import ResponseBase +from frontends.gamespy.protocols.web_services.modules.sake.contracts.requests import CreateRecordRequest, SearchForRecordsRequest +from frontends.gamespy.protocols.web_services.modules.sake.contracts.results import CreateRecordResult, SearchForRecordsResult class CreateRecordResponse(ResponseBase): diff --git a/src/servers/web_services/src/modules/sake/contracts/results.py b/src/frontends/gamespy/protocols/web_services/modules/sake/contracts/results.py similarity index 81% rename from src/servers/web_services/src/modules/sake/contracts/results.py rename to src/frontends/gamespy/protocols/web_services/modules/sake/contracts/results.py index 1b3ab4049..71c3c5360 100644 --- a/src/servers/web_services/src/modules/sake/contracts/results.py +++ b/src/frontends/gamespy/protocols/web_services/modules/sake/contracts/results.py @@ -1,7 +1,7 @@ from typing import OrderedDict from pydantic import BaseModel -from servers.web_services.src.modules.sake.abstractions.generals import ResultBase +from frontends.gamespy.protocols.web_services.modules.sake.abstractions.generals import ResultBase class CreateRecordResult(ResultBase): diff --git a/src/frontends/gamespy/protocols/web_services/modules/sake/exceptions/general.py b/src/frontends/gamespy/protocols/web_services/modules/sake/exceptions/general.py new file mode 100644 index 000000000..9dba83e7b --- /dev/null +++ b/src/frontends/gamespy/protocols/web_services/modules/sake/exceptions/general.py @@ -0,0 +1,5 @@ +from frontends.gamespy.protocols.web_services.aggregations.exceptions import WebException + + +class SakeException(WebException): + pass diff --git a/src/servers/presence_connection_manager/src/__init__.py b/src/frontends/tests/__init__.py similarity index 100% rename from src/servers/presence_connection_manager/src/__init__.py rename to src/frontends/tests/__init__.py diff --git a/src/servers/presence_connection_manager/tests/__init__.py b/src/frontends/tests/gamespy/__init__.py similarity index 100% rename from src/servers/presence_connection_manager/tests/__init__.py rename to src/frontends/tests/gamespy/__init__.py diff --git a/src/servers/presence_search_player/__init__.py b/src/frontends/tests/gamespy/chat/__init__.py similarity index 100% rename from src/servers/presence_search_player/__init__.py rename to src/frontends/tests/gamespy/chat/__init__.py diff --git a/src/servers/chat/tests/game_tests.py b/src/frontends/tests/gamespy/chat/game_tests.py similarity index 98% rename from src/servers/chat/tests/game_tests.py rename to src/frontends/tests/gamespy/chat/game_tests.py index bdac81545..4150a6a93 100644 --- a/src/servers/chat/tests/game_tests.py +++ b/src/frontends/tests/gamespy/chat/game_tests.py @@ -2,7 +2,7 @@ import responses -from servers.chat.tests.mock_objects import create_client +from frontends.tests.gamespy.chat.mock_objects import create_client class GameTests(unittest.TestCase): diff --git a/src/servers/chat/tests/mock_objects.py b/src/frontends/tests/gamespy/chat/mock_objects.py similarity index 87% rename from src/servers/chat/tests/mock_objects.py rename to src/frontends/tests/gamespy/chat/mock_objects.py index 8c9eb1804..9f74f9acd 100644 --- a/src/servers/chat/tests/mock_objects.py +++ b/src/frontends/tests/gamespy/chat/mock_objects.py @@ -2,10 +2,10 @@ import threading from time import sleep from typing import TYPE_CHECKING, cast -from library.src.configs import CONFIG -from library.tests.mock_objects import BrokerMock, ConnectionMock, LogMock, RequestHandlerMock, create_mock_url -from servers.chat.src.applications.client import Client -from servers.chat.src.applications.handlers import * +from frontends.gamespy.library.configs import CONFIG +from frontends.tests.gamespy.library.mock_objects import BrokerMock, ConnectionMock, LogMock, RequestHandlerMock, create_mock_url +from frontends.gamespy.protocols.chat.applications.client import Client +from frontends.gamespy.protocols.chat.applications.handlers import * class ClientMock(Client): diff --git a/src/servers/chat/tests/request_tests.py b/src/frontends/tests/gamespy/chat/request_tests.py similarity index 98% rename from src/servers/chat/tests/request_tests.py rename to src/frontends/tests/gamespy/chat/request_tests.py index 234fb719c..19d39a6e1 100644 --- a/src/servers/chat/tests/request_tests.py +++ b/src/frontends/tests/gamespy/chat/request_tests.py @@ -1,7 +1,7 @@ import unittest -from servers.chat.src.aggregates.enums import MessageType -from servers.chat.src.contracts.requests import * +from frontends.gamespy.protocols.chat.aggregates.enums import MessageType +from frontends.gamespy.protocols.chat.contracts.requests import * # region General CD_KEY = "CDKEY XXXX-XXXX-XXXX-XXXX\r\n" CRYPT = "CRYPT des 1 gmtest\r\n" diff --git a/src/servers/presence_search_player/src/__init__.py b/src/frontends/tests/gamespy/game_status/__init__.py similarity index 100% rename from src/servers/presence_search_player/src/__init__.py rename to src/frontends/tests/gamespy/game_status/__init__.py diff --git a/src/servers/game_status/tests/game_tests.py b/src/frontends/tests/gamespy/game_status/game_tests.py similarity index 96% rename from src/servers/game_status/tests/game_tests.py rename to src/frontends/tests/gamespy/game_status/game_tests.py index fc0e74dce..1ed007786 100644 --- a/src/servers/game_status/tests/game_tests.py +++ b/src/frontends/tests/gamespy/game_status/game_tests.py @@ -4,7 +4,7 @@ import responses -from servers.game_status.tests.mock_objects import create_client +from frontends.tests.gamespy.game_status.mock_objects import create_client class GameTest(unittest.TestCase): diff --git a/src/servers/game_status/tests/handler_tests.py b/src/frontends/tests/gamespy/game_status/handler_tests.py similarity index 90% rename from src/servers/game_status/tests/handler_tests.py rename to src/frontends/tests/gamespy/game_status/handler_tests.py index f7996eb59..f5d7db3cd 100644 --- a/src/servers/game_status/tests/handler_tests.py +++ b/src/frontends/tests/gamespy/game_status/handler_tests.py @@ -3,13 +3,12 @@ import unittest import responses -from library.tests.mock_objects import create_mock_url -from servers.game_status.src.aggregations.gscrypt import GSCrypt -from servers.game_status.src.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest -from servers.game_status.src.aggregations.enums import PersistStorageType -from servers.game_status.src.applications.handlers import AuthPlayerHandler, SetPlayerDataHandler, UpdateGameHandler -from servers.game_status.src.applications.switcher import Switcher -from servers.game_status.tests.mock_objects import create_client +from frontends.gamespy.protocols.game_status.aggregations.gscrypt import GSCrypt +from frontends.gamespy.protocols.game_status.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest +from frontends.gamespy.protocols.game_status.aggregations.enums import PersistStorageType +from frontends.gamespy.protocols.game_status.applications.handlers import AuthPlayerHandler, SetPlayerDataHandler, UpdateGameHandler +from frontends.gamespy.protocols.game_status.applications.switcher import Switcher +from frontends.tests.gamespy.game_status.mock_objects import create_client class HandlerTests(unittest.TestCase): diff --git a/src/servers/game_status/tests/mock_objects.py b/src/frontends/tests/gamespy/game_status/mock_objects.py similarity index 64% rename from src/servers/game_status/tests/mock_objects.py rename to src/frontends/tests/gamespy/game_status/mock_objects.py index e8ddc0968..1025050bc 100644 --- a/src/servers/game_status/tests/mock_objects.py +++ b/src/frontends/tests/gamespy/game_status/mock_objects.py @@ -1,9 +1,9 @@ from typing import cast -from library.src.configs import CONFIG -from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url -from servers.game_status.src.applications.client import Client -from servers.game_status.src.applications.handlers import AuthGameHandler, AuthPlayerHandler, GetPlayerDataHandler, GetProfileIdHandler, NewGameHandler, SetPlayerDataHandler, UpdateGameHandler -from servers.game_status.src.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult +from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.protocols.game_status.applications.client import Client +from frontends.gamespy.protocols.game_status.applications.handlers import AuthGameHandler, AuthPlayerHandler, GetPlayerDataHandler, GetProfileIdHandler, NewGameHandler, SetPlayerDataHandler, UpdateGameHandler +from frontends.gamespy.protocols.game_status.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult +from frontends.tests.gamespy.library.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url class ClientMock(Client): diff --git a/src/servers/presence_search_player/tests/__init__.py b/src/frontends/tests/gamespy/library/__init__.py similarity index 100% rename from src/servers/presence_search_player/tests/__init__.py rename to src/frontends/tests/gamespy/library/__init__.py diff --git a/src/library/tests/encrypt_tests.py b/src/frontends/tests/gamespy/library/encrypt_tests.py similarity index 74% rename from src/library/tests/encrypt_tests.py rename to src/frontends/tests/gamespy/library/encrypt_tests.py index 4ee440e72..c9d75f37b 100644 --- a/src/library/tests/encrypt_tests.py +++ b/src/frontends/tests/gamespy/library/encrypt_tests.py @@ -1,7 +1,7 @@ import unittest -from library.src.encryption.gs_encryption import ChatCrypt -from library.src.encryption.xor_encryption import XorEncoding, XorType -from servers.game_status.src.aggregations.gscrypt import GSCrypt +from frontends.gamespy.library.encryption.gs_encryption import ChatCrypt +from frontends.gamespy.library.encryption.xor_encryption import XorEncoding, XorType +from frontends.gamespy.protocols.game_status.aggregations.gscrypt import GSCrypt class EncryptionTest(unittest.TestCase): diff --git a/src/library/tests/mock_objects.py b/src/frontends/tests/gamespy/library/mock_objects.py similarity index 75% rename from src/library/tests/mock_objects.py rename to src/frontends/tests/gamespy/library/mock_objects.py index 7c0ac5802..f8c92a049 100644 --- a/src/library/tests/mock_objects.py +++ b/src/frontends/tests/gamespy/library/mock_objects.py @@ -1,11 +1,11 @@ import socketserver import responses -from library.src.abstractions.brocker import BrockerBase -from library.src.abstractions.connections import ConnectionBase -from library.src.abstractions.handler import CmdHandlerBase -from library.src.log.log_manager import LogWriter -from library.src.configs import CONFIG, ServerConfig +from frontends.gamespy.library.abstractions.brocker import BrockerBase +from frontends.gamespy.library.abstractions.connections import ConnectionBase +from frontends.gamespy.library.abstractions.handler import CmdHandlerBase +from frontends.gamespy.library.log.log_manager import LogWriter +from frontends.gamespy.library.configs import CONFIG, ServerConfig class ConnectionMock(ConnectionBase): diff --git a/src/servers/query_report/__init__.py b/src/frontends/tests/gamespy/natneg/__init__.py similarity index 100% rename from src/servers/query_report/__init__.py rename to src/frontends/tests/gamespy/natneg/__init__.py diff --git a/src/servers/natneg/tests/handler_tests.py b/src/frontends/tests/gamespy/natneg/handler_tests.py similarity index 90% rename from src/servers/natneg/tests/handler_tests.py rename to src/frontends/tests/gamespy/natneg/handler_tests.py index f773b3b7a..e8661cab8 100644 --- a/src/servers/natneg/tests/handler_tests.py +++ b/src/frontends/tests/gamespy/natneg/handler_tests.py @@ -1,27 +1,26 @@ import unittest -from library.src.abstractions.handler import CmdHandlerBase -from library.src.configs import CONFIG -from library.tests.mock_objects import create_mock_url -from servers.natneg.src.contracts.requests import InitRequest -from servers.natneg.src.applications.handlers import AddressCheckHandler, ErtAckHandler, InitHandler, NatifyHandler +from frontends.gamespy.library.abstractions.handler import CmdHandlerBase +from frontends.tests.gamespy.library.mock_objects import create_mock_url +from frontends.gamespy.protocols.natneg.contracts.requests import InitRequest +from frontends.gamespy.protocols.natneg.applications.handlers import AddressCheckHandler, ErtAckHandler, InitHandler, NatifyHandler import responses -from servers.natneg.src.contracts.requests import ( +from frontends.gamespy.protocols.natneg.contracts.requests import ( AddressCheckRequest, ErtAckRequest, InitRequest, NatifyRequest, PreInitRequest, ) -from servers.natneg.src.aggregations.enums import ( +from frontends.gamespy.protocols.natneg.aggregations.enums import ( NatClientIndex, NatPortType, PreInitState, RequestType, ) -from servers.natneg.src.contracts.responses import InitResponse -from servers.natneg.tests.mock_objects import create_client +from frontends.gamespy.protocols.natneg.contracts.responses import InitResponse +from frontends.tests.gamespy.natneg.mock_objects import create_client CmdHandlerBase._debug = True diff --git a/src/servers/natneg/tests/mock_objects.py b/src/frontends/tests/gamespy/natneg/mock_objects.py similarity index 65% rename from src/servers/natneg/tests/mock_objects.py rename to src/frontends/tests/gamespy/natneg/mock_objects.py index 97b8d247c..b0282c876 100644 --- a/src/servers/natneg/tests/mock_objects.py +++ b/src/frontends/tests/gamespy/natneg/mock_objects.py @@ -1,9 +1,9 @@ -from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url -from library.src.configs import CONFIG -from servers.natneg.src.applications.client import Client +from frontends.tests.gamespy.library.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url +from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.protocols.natneg.applications.client import Client from typing import cast -from servers.natneg.src.applications.handlers import AddressCheckHandler, ErtAckHandler, InitHandler, NatifyHandler +from frontends.gamespy.protocols.natneg.applications.handlers import AddressCheckHandler, ErtAckHandler, InitHandler, NatifyHandler class ClientMock(Client): diff --git a/src/servers/natneg/tests/redis.py b/src/frontends/tests/gamespy/natneg/redis.py similarity index 100% rename from src/servers/natneg/tests/redis.py rename to src/frontends/tests/gamespy/natneg/redis.py diff --git a/src/servers/query_report/src/__init__.py b/src/frontends/tests/gamespy/presence_connection_manager/__init__.py similarity index 100% rename from src/servers/query_report/src/__init__.py rename to src/frontends/tests/gamespy/presence_connection_manager/__init__.py diff --git a/src/servers/presence_connection_manager/tests/buddy_request_tests.py b/src/frontends/tests/gamespy/presence_connection_manager/buddy_request_tests.py similarity index 93% rename from src/servers/presence_connection_manager/tests/buddy_request_tests.py rename to src/frontends/tests/gamespy/presence_connection_manager/buddy_request_tests.py index cf8c9122e..c327e580c 100644 --- a/src/servers/presence_connection_manager/tests/buddy_request_tests.py +++ b/src/frontends/tests/gamespy/presence_connection_manager/buddy_request_tests.py @@ -1,6 +1,6 @@ import unittest -from servers.presence_connection_manager.src.contracts.requests import ( +from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import ( AddBuddyRequest, DelBuddyRequest, InviteToRequest, diff --git a/src/servers/presence_connection_manager/tests/game_tests.py b/src/frontends/tests/gamespy/presence_connection_manager/game_tests.py similarity index 84% rename from src/servers/presence_connection_manager/tests/game_tests.py rename to src/frontends/tests/gamespy/presence_connection_manager/game_tests.py index 78660a903..82ab361f8 100644 --- a/src/servers/presence_connection_manager/tests/game_tests.py +++ b/src/frontends/tests/gamespy/presence_connection_manager/game_tests.py @@ -1,9 +1,7 @@ import unittest -from library.tests.mock_objects import create_mock_url -from servers.presence_connection_manager.src.applications.handlers import LoginHandler, NewUserHandler -from servers.presence_connection_manager.src.contracts.requests import StatusRequest -from servers.presence_connection_manager.tests.mock_objects import create_client +from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import StatusRequest +from frontends.tests.gamespy.presence_connection_manager.mock_objects import create_client import responses diff --git a/src/servers/presence_connection_manager/tests/general_request_tests.py b/src/frontends/tests/gamespy/presence_connection_manager/general_request_tests.py similarity index 95% rename from src/servers/presence_connection_manager/tests/general_request_tests.py rename to src/frontends/tests/gamespy/presence_connection_manager/general_request_tests.py index 9455f89a3..d85f4f805 100644 --- a/src/servers/presence_connection_manager/tests/general_request_tests.py +++ b/src/frontends/tests/gamespy/presence_connection_manager/general_request_tests.py @@ -1,6 +1,6 @@ import unittest -from servers.presence_connection_manager.src.contracts.requests import LoginRequest -from servers.presence_connection_manager.src.aggregates.enums import ( +from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import LoginRequest +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( LoginType, QuietModeType, SdkRevisionType, diff --git a/src/servers/presence_connection_manager/tests/mock_objects.py b/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py similarity index 69% rename from src/servers/presence_connection_manager/tests/mock_objects.py rename to src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py index eb3ce4711..76cc8ba61 100644 --- a/src/servers/presence_connection_manager/tests/mock_objects.py +++ b/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py @@ -1,8 +1,8 @@ -from library.src.configs import CONFIG -from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url -from servers.presence_connection_manager.src.applications.client import Client -from servers.presence_connection_manager.src.applications.handlers import LoginHandler, NewUserHandler +from frontends.gamespy.library.configs import CONFIG +from frontends.tests.gamespy.library.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url +from frontends.gamespy.protocols.presence_connection_manager.applications.client import Client +from frontends.gamespy.protocols.presence_connection_manager.applications.handlers import LoginHandler, NewUserHandler class ClientMock(Client): diff --git a/src/servers/presence_connection_manager/tests/profile_request_tests.py b/src/frontends/tests/gamespy/presence_connection_manager/profile_request_tests.py similarity index 96% rename from src/servers/presence_connection_manager/tests/profile_request_tests.py rename to src/frontends/tests/gamespy/presence_connection_manager/profile_request_tests.py index 14d4097d1..14e22f2ef 100644 --- a/src/servers/presence_connection_manager/tests/profile_request_tests.py +++ b/src/frontends/tests/gamespy/presence_connection_manager/profile_request_tests.py @@ -1,6 +1,6 @@ import unittest -from servers.presence_connection_manager.src.contracts.requests import ( +from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import ( AddBlockRequest, GetProfileRequest, NewProfileRequest, diff --git a/src/servers/query_report/src/v1/__init__.py b/src/frontends/tests/gamespy/presence_search_player/__init__.py similarity index 100% rename from src/servers/query_report/src/v1/__init__.py rename to src/frontends/tests/gamespy/presence_search_player/__init__.py diff --git a/src/servers/presence_search_player/tests/game_tests.py b/src/frontends/tests/gamespy/presence_search_player/game_tests.py similarity index 66% rename from src/servers/presence_search_player/tests/game_tests.py rename to src/frontends/tests/gamespy/presence_search_player/game_tests.py index 974028388..1920bc444 100644 --- a/src/servers/presence_search_player/tests/game_tests.py +++ b/src/frontends/tests/gamespy/presence_search_player/game_tests.py @@ -1,13 +1,13 @@ from typing import TYPE_CHECKING, cast import unittest -from servers.presence_search_player.src.contracts.requests import CheckRequest -from servers.presence_search_player.src.applications.handlers import CheckHandler -from servers.presence_search_player.src.applications.switcher import CmdSwitcher +from frontends.gamespy.protocols.presence_search_player.contracts.requests import CheckRequest +from frontends.gamespy.protocols.presence_search_player.applications.handlers import CheckHandler +from frontends.gamespy.protocols.presence_search_player.applications.switcher import CmdSwitcher import responses -from servers.presence_search_player.src.contracts.responses import CheckResponse -from servers.presence_search_player.tests.mock_objects import create_client +from frontends.gamespy.protocols.presence_search_player.contracts.responses import CheckResponse +from frontends.tests.gamespy.presence_search_player.mock_objects import create_client class GameTest(unittest.TestCase): diff --git a/src/servers/presence_search_player/tests/handler_tests.py b/src/frontends/tests/gamespy/presence_search_player/handler_tests.py similarity index 91% rename from src/servers/presence_search_player/tests/handler_tests.py rename to src/frontends/tests/gamespy/presence_search_player/handler_tests.py index 4a3e38a44..e3a9bba50 100644 --- a/src/servers/presence_search_player/tests/handler_tests.py +++ b/src/frontends/tests/gamespy/presence_search_player/handler_tests.py @@ -2,10 +2,9 @@ import responses -from library.tests.mock_objects import create_mock_url -from servers.presence_search_player.src.contracts.requests import SearchRequest -from servers.presence_search_player.src.applications.handlers import SearchHandler -from servers.presence_search_player.tests.mock_objects import create_client +from frontends.gamespy.protocols.presence_search_player.contracts.requests import SearchRequest +from frontends.gamespy.protocols.presence_search_player.applications.handlers import SearchHandler +from frontends.tests.gamespy.presence_search_player.mock_objects import create_client CHECK1 = "\\check\\\\nick\\spyguy\\email\\spyguy@gamespy.com\\pass\\0000\\final\\" SEARCH_1 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\namespaceid\\0\\uniquenick\\spyguy\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" diff --git a/src/servers/presence_search_player/tests/mock_objects.py b/src/frontends/tests/gamespy/presence_search_player/mock_objects.py similarity index 63% rename from src/servers/presence_search_player/tests/mock_objects.py rename to src/frontends/tests/gamespy/presence_search_player/mock_objects.py index 64f4fb1b3..fdce425f9 100644 --- a/src/servers/presence_search_player/tests/mock_objects.py +++ b/src/frontends/tests/gamespy/presence_search_player/mock_objects.py @@ -1,9 +1,9 @@ from typing import cast -from library.src.configs import CONFIG -from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url -from servers.presence_search_player.src.applications.client import Client -from servers.presence_search_player.src.applications.handlers import CheckHandler, SearchHandler -from servers.presence_search_player.src.contracts.results import CheckResult, SearchResult +from frontends.gamespy.library.configs import CONFIG +from frontends.tests.gamespy.library.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url +from frontends.gamespy.protocols.presence_search_player.applications.client import Client +from frontends.gamespy.protocols.presence_search_player.applications.handlers import CheckHandler, SearchHandler +from frontends.gamespy.protocols.presence_search_player.contracts.results import CheckResult, SearchResult class ClientMock(Client): diff --git a/src/servers/query_report/src/v1/abstractions/__init__.py b/src/frontends/tests/gamespy/query_report/__init__.py similarity index 100% rename from src/servers/query_report/src/v1/abstractions/__init__.py rename to src/frontends/tests/gamespy/query_report/__init__.py diff --git a/src/servers/query_report/tests/game_tests.py b/src/frontends/tests/gamespy/query_report/game_tests.py similarity index 96% rename from src/servers/query_report/tests/game_tests.py rename to src/frontends/tests/gamespy/query_report/game_tests.py index a711aaa18..00490ae00 100644 --- a/src/servers/query_report/tests/game_tests.py +++ b/src/frontends/tests/gamespy/query_report/game_tests.py @@ -1,7 +1,7 @@ import unittest import responses -from servers.query_report.tests.mock_objects import create_client +from frontends.tests.gamespy.query_report.mock_objects import create_client class GameTests(unittest.TestCase): diff --git a/src/servers/query_report/tests/mock_objects.py b/src/frontends/tests/gamespy/query_report/mock_objects.py similarity index 59% rename from src/servers/query_report/tests/mock_objects.py rename to src/frontends/tests/gamespy/query_report/mock_objects.py index 23f57437a..aeb5c41ad 100644 --- a/src/servers/query_report/tests/mock_objects.py +++ b/src/frontends/tests/gamespy/query_report/mock_objects.py @@ -1,9 +1,9 @@ from typing import cast -from library.src.configs import CONFIG -from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url -from servers.query_report.src.applications.client import Client -from servers.query_report.src.v2.applications.handlers import AvailableHandler, HeartBeatHandler, KeepAliveHandler -from servers.query_report.src.v2.contracts.results import HeartBeatResult +from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.protocols.query_report.applications.client import Client +from frontends.gamespy.protocols.query_report.v2.applications.handlers import AvailableHandler, HeartBeatHandler, KeepAliveHandler +from frontends.gamespy.protocols.query_report.v2.contracts.results import HeartBeatResult +from frontends.tests.gamespy.library.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url class ClientMock(Client): diff --git a/src/servers/query_report/tests/request_tests.py b/src/frontends/tests/gamespy/query_report/request_tests.py similarity index 95% rename from src/servers/query_report/tests/request_tests.py rename to src/frontends/tests/gamespy/query_report/request_tests.py index b34dd1fd0..a3b1e76ac 100644 --- a/src/servers/query_report/tests/request_tests.py +++ b/src/frontends/tests/gamespy/query_report/request_tests.py @@ -1,7 +1,7 @@ import unittest -from servers.query_report.src.v2.aggregates.enums import RequestType -from servers.query_report.src.v2.contracts.requests import AvaliableRequest, ChallengeRequest, EchoRequest, HeartBeatRequest +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import RequestType +from frontends.gamespy.protocols.query_report.v2.contracts.requests import AvaliableRequest, ChallengeRequest, EchoRequest, HeartBeatRequest class RequestTest(unittest.TestCase): diff --git a/src/servers/query_report/tests/__init__.py b/src/frontends/tests/gamespy/server_browser/__init__.py similarity index 100% rename from src/servers/query_report/tests/__init__.py rename to src/frontends/tests/gamespy/server_browser/__init__.py diff --git a/src/servers/server_browser/tests/game_tests.py b/src/frontends/tests/gamespy/server_browser/game_tests.py similarity index 98% rename from src/servers/server_browser/tests/game_tests.py rename to src/frontends/tests/gamespy/server_browser/game_tests.py index be4984b3c..8201dac66 100644 --- a/src/servers/server_browser/tests/game_tests.py +++ b/src/frontends/tests/gamespy/server_browser/game_tests.py @@ -1,8 +1,8 @@ import unittest import responses -from servers.query_report.tests.mock_objects import create_client -from servers.server_browser.tests.mock_objects import create_v2_client +from frontends.tests.gamespy.query_report.mock_objects import create_client +from frontends.tests.gamespy.server_browser.mock_objects import create_v2_client class GameTest(unittest.TestCase): diff --git a/src/servers/server_browser/tests/mock_objects.py b/src/frontends/tests/gamespy/server_browser/mock_objects.py similarity index 79% rename from src/servers/server_browser/tests/mock_objects.py rename to src/frontends/tests/gamespy/server_browser/mock_objects.py index df0ac5799..a2207e652 100644 --- a/src/servers/server_browser/tests/mock_objects.py +++ b/src/frontends/tests/gamespy/server_browser/mock_objects.py @@ -1,9 +1,9 @@ from typing import cast -from library.src.configs import CONFIG -from library.tests.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url -from servers.server_browser.src.v2.applications.client import Client -from servers.server_browser.src.v2.applications.handlers import ServerInfoHandler, ServerListHandler -from servers.server_browser.src.v2.contracts.results import ServerInfoResult, ServerMainListResult +from frontends.gamespy.library.configs import CONFIG +from frontends.tests.gamespy.library.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url +from frontends.gamespy.protocols.server_browser.v2.applications.client import Client +from frontends.gamespy.protocols.server_browser.v2.applications.handlers import ServerInfoHandler, ServerListHandler +from frontends.gamespy.protocols.server_browser.v2.contracts.results import ServerInfoResult, ServerMainListResult import json diff --git a/src/servers/server_browser/__init__.py b/src/frontends/tests/gamespy/web_services/__init__.py similarity index 100% rename from src/servers/server_browser/__init__.py rename to src/frontends/tests/gamespy/web_services/__init__.py diff --git a/src/servers/web_services/tests/altas_tests.py b/src/frontends/tests/gamespy/web_services/altas_tests.py similarity index 100% rename from src/servers/web_services/tests/altas_tests.py rename to src/frontends/tests/gamespy/web_services/altas_tests.py diff --git a/src/servers/web_services/tests/auth_tests.py b/src/frontends/tests/gamespy/web_services/auth_tests.py similarity index 97% rename from src/servers/web_services/tests/auth_tests.py rename to src/frontends/tests/gamespy/web_services/auth_tests.py index 25daeff2a..7893d7442 100644 --- a/src/servers/web_services/tests/auth_tests.py +++ b/src/frontends/tests/gamespy/web_services/auth_tests.py @@ -2,7 +2,7 @@ import responses -from servers.web_services.src.modules.auth.contracts.requests import LoginProfileWithGameIdRequest, LoginPs3CertRequest, LoginRemoteAuthRequest, LoginUniqueNickRequest +from frontends.gamespy.protocols.web_services.modules.auth.contracts.requests import LoginProfileWithGameIdRequest, LoginPs3CertRequest, LoginRemoteAuthRequest, LoginUniqueNickRequest LOGIN_PROFILE = """ diff --git a/src/servers/game_status/src/abstractions/handlers.py b/src/servers/game_status/src/abstractions/handlers.py deleted file mode 100644 index bd40ec7c5..000000000 --- a/src/servers/game_status/src/abstractions/handlers.py +++ /dev/null @@ -1,17 +0,0 @@ -from typing import Optional -from library.src.abstractions.contracts import ResponseBase -import library.src.abstractions.handler -from servers.game_status.src.abstractions.contracts import RequestBase, ResultBase -from servers.game_status.src.applications.client import Client - - -class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase): - _client: Client - _request: RequestBase - _result: ResultBase - _response: Optional[ResponseBase] - - def __init__(self, client: Client, request: RequestBase) -> None: - super().__init__(client, request) - assert isinstance(client, Client) - assert issubclass(type(request), RequestBase) diff --git a/src/servers/game_status/src/aggregations/exceptions.py b/src/servers/game_status/src/aggregations/exceptions.py deleted file mode 100644 index c59852da0..000000000 --- a/src/servers/game_status/src/aggregations/exceptions.py +++ /dev/null @@ -1,5 +0,0 @@ -from library.src.exceptions.general import UniSpyException - - -class GSException(UniSpyException): - pass diff --git a/src/servers/presence_search_player/src/applications/client.py b/src/servers/presence_search_player/src/applications/client.py deleted file mode 100644 index 442f59b25..000000000 --- a/src/servers/presence_search_player/src/applications/client.py +++ /dev/null @@ -1,18 +0,0 @@ -from library.src.abstractions.client import ClientBase - -from library.src.abstractions.switcher import SwitcherBase -from library.src.log.log_manager import LogWriter -from library.src.network.tcp_handler import TcpConnection -from library.src.configs import ServerConfig - - -class Client(ClientBase): - client_pool: dict[str, "Client"] = {} - - def __init__(self, connection: TcpConnection, server_config: ServerConfig, logger: LogWriter): - super().__init__(connection, server_config, logger) - - def _create_switcher(self, buffer: bytes) -> SwitcherBase: - from servers.presence_search_player.src.applications.switcher import CmdSwitcher - temp_buffer = buffer.decode() - return CmdSwitcher(self, temp_buffer) diff --git a/src/servers/query_report/src/aggregates/exceptions.py b/src/servers/query_report/src/aggregates/exceptions.py deleted file mode 100644 index 3d29f873e..000000000 --- a/src/servers/query_report/src/aggregates/exceptions.py +++ /dev/null @@ -1,5 +0,0 @@ -from library.src.exceptions.general import UniSpyException - - -class QRException(UniSpyException): - pass diff --git a/src/servers/query_report/src/v1/abstractions/handlers.py b/src/servers/query_report/src/v1/abstractions/handlers.py deleted file mode 100644 index 7a615966a..000000000 --- a/src/servers/query_report/src/v1/abstractions/handlers.py +++ /dev/null @@ -1,10 +0,0 @@ -import library.src.abstractions.handler -from servers.query_report.src.v1.abstractions.contracts import RequestBase -from servers.query_report.src.applications.client import Client - - -class CmdHandlerBase(library.src.abstractions.handler.CmdHandlerBase): - def __init__(self, client: Client, request: RequestBase) -> None: - assert issubclass(type(request), RequestBase) - assert isinstance(client, Client) - super().__init__(client, request) diff --git a/src/servers/query_report/src/v2/abstractions/cmd_handler_base.py b/src/servers/query_report/src/v2/abstractions/cmd_handler_base.py deleted file mode 100644 index 408ab1519..000000000 --- a/src/servers/query_report/src/v2/abstractions/cmd_handler_base.py +++ /dev/null @@ -1,10 +0,0 @@ -from library.src.abstractions.handler import CmdHandlerBase as CHB -from servers.query_report.src.v2.abstractions.contracts import RequestBase -from servers.query_report.src.applications.client import Client - - -class CmdHandlerBase(CHB): - def __init__(self, client: Client, request: RequestBase) -> None: - assert issubclass(type(request), RequestBase) - assert isinstance(client, Client) - super().__init__(client, request) diff --git a/src/servers/server_browser/src/__init__.py b/src/servers/server_browser/src/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/server_browser/src/v2/abstractions/__init__.py b/src/servers/server_browser/src/v2/abstractions/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/server_browser/src/v2/applications/client.py b/src/servers/server_browser/src/v2/applications/client.py deleted file mode 100644 index c6e479eba..000000000 --- a/src/servers/server_browser/src/v2/applications/client.py +++ /dev/null @@ -1,23 +0,0 @@ -from typing import TYPE_CHECKING -from library.src.abstractions.client import ClientBase, ClientInfoBase -from library.src.abstractions.enctypt_base import EncryptBase -from servers.server_browser.src.v2.aggregations.enums import ServerListUpdateOption -if TYPE_CHECKING: - from library.src.abstractions.switcher import SwitcherBase - - -class ClientInfo(ClientInfoBase): - game_secret_key: str - client_challenge: str - search_type: ServerListUpdateOption - game_name: str - - -class Client(ClientBase): - is_log_raw: bool = True - info: ClientInfo - crypto: EncryptBase - - def _create_switcher(self, buffer: bytes) -> "SwitcherBase": - from servers.server_browser.src.v2.applications.switcher import CmdSwitcher - return CmdSwitcher(self, buffer) diff --git a/src/servers/server_browser/src/v2/contracts/__init__.py b/src/servers/server_browser/src/v2/contracts/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/server_browser/tests/__init__.py b/src/servers/server_browser/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/web_services/__init__.py b/src/servers/web_services/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/web_services/src/__init__.py b/src/servers/web_services/src/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/src/servers/web_services/src/aggregations/exceptions.py b/src/servers/web_services/src/aggregations/exceptions.py deleted file mode 100644 index 424661137..000000000 --- a/src/servers/web_services/src/aggregations/exceptions.py +++ /dev/null @@ -1,6 +0,0 @@ -from library.src.exceptions.general import UniSpyException - - -class WebException(UniSpyException): - pass - diff --git a/src/servers/web_services/src/modules/auth/exceptions/general.py b/src/servers/web_services/src/modules/auth/exceptions/general.py deleted file mode 100644 index 6c5137645..000000000 --- a/src/servers/web_services/src/modules/auth/exceptions/general.py +++ /dev/null @@ -1,5 +0,0 @@ -from servers.web_services.src.aggregations.exceptions import WebException - - -class AuthException(WebException): - pass diff --git a/src/servers/web_services/src/modules/sake/exceptions/general.py b/src/servers/web_services/src/modules/sake/exceptions/general.py deleted file mode 100644 index cf0bc14b9..000000000 --- a/src/servers/web_services/src/modules/sake/exceptions/general.py +++ /dev/null @@ -1,5 +0,0 @@ -from servers.web_services.src.aggregations.exceptions import WebException - - -class SakeException(WebException): - pass diff --git a/src/servers/web_services/tests/__init__.py b/src/servers/web_services/tests/__init__.py deleted file mode 100644 index e69de29bb..000000000 From 0296e8c8bb60fe4a16dadb012d9a6305a4e598d0 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 5 Mar 2025 09:03:56 +0000 Subject: [PATCH 160/231] fix: orm classes typehit problem --- src/backends/library/database/pg_orm.py | 37 +++++++++++++++---------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index df2e45455..02d9ce3fa 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -1,3 +1,4 @@ +from typing import TYPE_CHECKING, cast from frontends.gamespy.library.configs import CONFIG from datetime import datetime from sqlalchemy import ( @@ -24,6 +25,7 @@ from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus import sqlalchemy as sa import enum +from sqlalchemy.orm.decl_api import DeclarativeBase class IntEnum(TypeDecorator): @@ -40,8 +42,11 @@ def process_result_value(self, value, dialect): return self._enumtype(value) -Base: DeclarativeMeta = declarative_base() - +# Base: DeclarativeMeta = declarative_base() +# if TYPE_CHECKING: +# Base = cast(DeclarativeMeta, Base) +class Base(DeclarativeBase): + pass class Users(Base): __tablename__ = "users" @@ -225,8 +230,8 @@ class RelayServerCaches(Base): class ChatChannelCaches(Base): __tablename__ = "chat_channel_caches" - channel_name = Column(String, primary_key=True, nullable=False) server_id = Column(UUID, nullable=False) + channel_name = Column(String, primary_key=True, nullable=False) game_name = Column(String, nullable=False) room_name = Column(String, nullable=False) topic = Column(String, nullable=True) @@ -242,12 +247,11 @@ class ChatUserCaches(Base): """ each user only have a unique nick caches, but have multiple user caches """ - __tablename__ = "chat_nick_caches" - user_id = Column(Integer, primary_key=True, nullable=False) + __tablename__ = "chat_user_caches" server_id = Column(UUID, nullable=False) nick_name = Column(String, primary_key=True, nullable=False) - game_name = Column(String, nullable=True) user_name = Column(String, nullable=True) + game_name = Column(String, nullable=True) remote_ip_address = Column(INET, nullable=False) remote_port = Column(Integer, nullable=False) key_value = Column(JSONB) @@ -255,18 +259,22 @@ class ChatUserCaches(Base): class ChatChannelUserCaches(Base): - __tablename__ = "chat_user_caches" - user_id = Column(Integer, ForeignKey( - "chat_channel_caches.user_id"), primary_key=True, nullable=False) + __tablename__ = "chat_channel_user_caches" + nick_name = Column(String, ForeignKey( + "chat_user_caches.nick_name"), primary_key=True, nullable=False) + user_name = Column(String, ForeignKey( + "chat_user_caches.user_name"), primary_key=True, nullable=False) channel_name = Column(String, ForeignKey( "chat_channel_caches.channel_name"), nullable=False) update_time = Column(DateTime, nullable=False) + # can we directly store the flags? is_voiceable = Column(Boolean, nullable=False) is_channel_operator = Column(Boolean, nullable=False) is_channel_creator = Column(Boolean, nullable=False) remote_ip_address = Column(INET, nullable=False) remote_port = Column(Integer, nullable=False) key_values = Column(JSONB) + modes = Column(String) class GameServerCaches(Base): @@ -296,10 +304,9 @@ def connect_to_db() -> Session: PG_SESSION = connect_to_db() if __name__ == "__main__": - session = connect_to_db() - session.query(Users.userid == 0) # type:ignore - profile = Profiles(userid=1, nick="spyguy", - extra_info={}, status=GPStatusCode.OFFLINE) - PG_SESSION.add(profile) - PG_SESSION.commit() + PG_SESSION.query(Users.userid == 0).all() # type:ignore + # profile = Profiles(userid=1, nick="spyguy", + # extra_info={}, status=GPStatusCode.OFFLINE) + # PG_SESSION.add(profile) + # PG_SESSION.commit() pass From 0874c8a5975b12e214df83f735d0aa445f2b0ae0 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 5 Mar 2025 09:04:35 +0000 Subject: [PATCH 161/231] refactored database operations --- common/UniSpy_pg.sql | 50 +++++++-------- docker-compose-unispy-env.yml | 1 + .../protocols/gamespy/chat/abstractions.py | 58 ++++++++++++++++++ src/backends/protocols/gamespy/chat/data.py | 61 +++++++++++++++---- .../protocols/gamespy/chat/handlers.py | 49 +++++++++------ .../gamespy/presence_search_player/data.py | 6 +- .../presence_search_player/handlers.py | 3 +- .../protocols/gamespy/query_report/data.py | 8 +-- .../query_report/aggregates/peer_room_info.py | 8 +-- src/requirements.txt | 5 +- 10 files changed, 176 insertions(+), 73 deletions(-) create mode 100644 src/backends/protocols/gamespy/chat/abstractions.py diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index c0426d2f3..649b87e3d 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -110,6 +110,21 @@ ALTER TABLE unispy.blocked_blockid_seq OWNER TO unispy; ALTER SEQUENCE unispy.blocked_blockid_seq OWNED BY unispy.blocked.blockid; +-- +-- Name: chat_nick_caches; Type: TABLE; Schema: unispy; Owner: unispy +-- + +CREATE TABLE unispy.chat_user_caches ( + server_id uuid NOT NULL, + nick_name character varying primary key NOT NULL UNIQUE , + game_name character varying, + user_name character varying, + remote_ip_address inet NOT NULL, + remote_port INTEGER NOT NULL, + key_value jsonb, + update_time timestamp without time zone NOT NULL +); + -- -- Name: chat_channel_caches; Type: TABLE; Schema: unispy; Owner: unispy -- @@ -131,21 +146,6 @@ CREATE TABLE unispy.chat_channel_caches ( ALTER TABLE unispy.chat_channel_caches OWNER TO unispy; --- --- Name: chat_nick_caches; Type: TABLE; Schema: unispy; Owner: unispy --- - -CREATE TABLE unispy.chat_user_caches ( - user_id SERIAL PRIMARY KEY, - server_id uuid NOT NULL, - nick_name character varying NOT NULL UNIQUE, - game_name character varying, - user_name character varying, - remote_ip_address inet NOT NULL, - remote_port INTEGER NOT NULL, - key_value jsonb, - update_time timestamp without time zone NOT NULL -); ALTER TABLE unispy.chat_user_caches OWNER TO unispy; @@ -155,7 +155,8 @@ ALTER TABLE unispy.chat_user_caches OWNER TO unispy; -- CREATE TABLE unispy.chat_channel_user_caches ( - userid INTEGER NOT NULL, + nick_name character varying NOT NULL + user_name character varying NOT NULL channel_name character varying NOT NULL, server_id uuid NOT NULL, update_time timestamp without time zone NOT NULL, @@ -441,9 +442,9 @@ CREATE TABLE unispy.profiles ( status smallint not NULL, statstring character varying, extra_info jsonb - FOREIGN key (userid) REFERENCES unispy.users (user_id) on delete cascade ); +-- FOREIGN key (userid) REFERENCES unispy.users (user_id) on delete cascade ALTER TABLE unispy.profiles OWNER TO unispy; @@ -5472,13 +5473,6 @@ SELECT pg_catalog.setval('unispy.subprofiles_subprofileid_seq', 1, false); SELECT pg_catalog.setval('unispy.users_userid_seq', 5, true); --- --- Name: chat_user_caches chat_user_caches_channel_name_fkey; Type: FK CONSTRAINT; Schema: unispy; Owner: unispy --- - -ALTER TABLE ONLY unispy.chat_channel_user_caches - ADD CONSTRAINT chat_user_caches_channel_name_fkey FOREIGN KEY (channel_name) REFERENCES unispy.chat_channel_caches(channel_name); - -- -- Name: grouplist grouplist_fk; Type: FK CONSTRAINT; Schema: unispy; Owner: unispy @@ -5495,11 +5489,13 @@ ALTER TABLE ONLY unispy.grouplist ALTER TABLE ONLY unispy.profiles ADD CONSTRAINT profiles_userid_fkey FOREIGN KEY (userid) REFERENCES unispy.users(userid); -ALTER TABLE ONLY unispy.chat_channel_user_caches - ADD CONSTRAINT chat_channel_user_caches_userid_fkey FOREGIN KEY(userid) unispy.chat_user_caches(userid) +-- +-- Name: chat_user_caches chat_user_caches_channel_name_fkey; Type: FK CONSTRAINT; Schema: unispy; Owner: unispy +-- ALTER TABLE ONLY unispy.chat_channel_user_caches - ADD CONSTRAINT chat_channel_user_caches_channel_name_fkey FOREGIN KEY(channel_name) unispy.chat_channel_caches(channel_name) + ADD CONSTRAINT chat_channel_user_caches_channel_name_fkey FOREIGN KEY(channel_name) unispy.chat_channel_caches(channel_name) + ADD CONSTRAINT chat_channel_user_caches_nick_name_fkey FOREIGN KEY(nick_name) unispy.chat_user_caches(nick_name) PostgreSQL database dump complete diff --git a/docker-compose-unispy-env.yml b/docker-compose-unispy-env.yml index e3debd06d..efcb689a3 100644 --- a/docker-compose-unispy-env.yml +++ b/docker-compose-unispy-env.yml @@ -26,5 +26,6 @@ services: networks: unispy: + external: true name: unispy driver: bridge \ No newline at end of file diff --git a/src/backends/protocols/gamespy/chat/abstractions.py b/src/backends/protocols/gamespy/chat/abstractions.py new file mode 100644 index 000000000..caf1ad562 --- /dev/null +++ b/src/backends/protocols/gamespy/chat/abstractions.py @@ -0,0 +1,58 @@ + +from backends.library.abstractions.handler_base import HandlerBase +from backends.library.database.pg_orm import ChatChannelCaches, ChatChannelUserCaches, ChatUserCaches +from backends.protocols.gamespy.chat.requests import ChannelRequestBase +from protocols.chat.aggregates.exceptions import NoSuchChannelException, NoSuchNickException +import backends.protocols.gamespy.chat.data as data + + +class ChannelHandlerBase(HandlerBase): + _request: ChannelRequestBase + _user: ChatUserCaches | None + _channel: ChatChannelCaches | None + _channel_user: ChatChannelUserCaches | None + + def _get_user(self): + self._user = data.get_user_cache_by_ip_port( + self._request.client_ip, self._request.client_port) + + def _get_channel(self): + self._channel = data.get_channel_by_name_and_ip_port( + self._request.channel_name, self._request.client_ip, self._request.client_port) + + def _get_channel_user(self): + self._channel_user = data.get_channel_user_cache_by_name_and_ip_port( + self._request.channel_name, + self._request.client_ip, + self._request.client_port) + + def _check_user(self): + if self._user is None: + raise NoSuchNickException( + f"Can not find user with ip address: {self._request.client_ip}:{self._request.client_port}") + + def _check_channel(self): + if self._channel is None: + raise NoSuchChannelException( + f"Can not find channel with name: {self._request.channel_name}") + + def _check_channel_user(self): + if self._channel_user is None: + raise NoSuchNickException( + f"Can not find channel user with channel name: {self._request.channel_name}, ip address: {self._request.client_ip}:{self._request.client_port}") + + async def _request_check(self) -> None: + self._get_user() + self._check_user() + + self._get_channel() + self._check_channel() + + self._get_channel_user() + self._check_channel_user() + + +class GetChannelKeyHandler(ChannelHandlerBase): + def _get_key_values(self): + assert isinstance(self._channel, ChatChannelCaches) + self._key_values = self._channel.key_values diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index b8b90fc08..83273bc7b 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -4,7 +4,6 @@ from sqlalchemy import Column, func from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatUserCaches, ChatChannelUserCaches, Users, Profiles, SubProfiles from frontends.gamespy.protocols.chat.aggregates.exceptions import ChatException, NoSuchNickException -from frontends.gamespy.protocols.chat.contracts.results import ListResult def is_nick_exist(nick_name: str) -> bool: @@ -91,7 +90,7 @@ def add_channel(channel:ChatChannelCaches): PG_SESSION.add(channel) PG_SESSION.commit() -def get_channel_by_name_and_game(channel_name:str,game_name:str)->ChatChannelCaches: +def get_channel_by_name_and_game(channel_name:str,game_name:str)->ChatChannelCaches|None: channel = PG_SESSION.query(ChatChannelCaches)\ .where(ChatChannelCaches.channel_name == channel_name, ChatChannelCaches.game_name == game_name)\ @@ -103,8 +102,23 @@ def get_channel_by_name_and_ip_port(channel_name:str,ip:str,port:int)->ChatChann assert isinstance(ip,str) assert isinstance(port,int) result = PG_SESSION.query(ChatChannelCaches).join(ChatChannelUserCaches).where( - ChatChannelUserCaches.channel_name == channel_name, ChatChannelUserCaches.remote_ip_address == ip, ChatChannelUserCaches.remote_port == port).first() + ChatChannelUserCaches.channel_name == channel_name, + ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_port == port).first() return result + +def get_channel_user_cache_by_name(channel_name:str,nick_name:str)->ChatChannelUserCaches|None: + assert isinstance(channel_name,str) + assert isinstance(nick_name,str) + result = PG_SESSION.query(ChatChannelUserCaches).where(ChatChannelUserCaches.channel_name == channel_name,ChatChannelUserCaches.nick_name == nick_name).first() + return result + +def get_channel_user_cache_by_name_and_ip_port(channel_name:str,ip:str,port:int)->ChatChannelUserCaches|None: + result = PG_SESSION.query(ChatChannelUserCaches).where(ChatChannelUserCaches.channel_name == channel_name, + ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_port == port).first() + return result + def update_channel_time(channel:ChatChannelCaches): channel.update_time = datetime.now() # type: ignore PG_SESSION.commit() @@ -112,10 +126,11 @@ def update_channel_time(channel:ChatChannelCaches): def db_commit(): PG_SESSION.commit() -def get_user_cache_by_nick_name(nick_name:str)->ChatUserCaches: +def get_user_cache_by_nick_name(nick_name:str)->ChatUserCaches|None: result = PG_SESSION.query(ChatUserCaches).where(ChatUserCaches.nick_name == nick_name).first() return result -def get_user_cache_by_ip_port(ip:str,port:int)->ChatUserCaches|None: + +def get_user_cache_by_ip_port(ip:str,port:int)->ChatUserCaches: result = PG_SESSION.query(ChatUserCaches).where(ChatUserCaches.remote_ip_address == ip, ChatUserCaches.remote_port == port).first() assert isinstance(result,ChatUserCaches) return result @@ -128,7 +143,7 @@ def get_whois_result(nick:str)->tuple: if info is None: raise NoSuchNickException(f"User not find by nick name:{nick}.") - channels = PG_SESSION.query(ChatChannelUserCaches.channel_name).join(ChatUserCaches,ChatChannelUserCaches.user_id == ChatUserCaches.user_id).where(ChatChannelUserCaches.user_id == info.user_id).all() + channels = PG_SESSION.query(ChatChannelUserCaches.channel_name).join(ChatUserCaches,ChatChannelUserCaches.nick_name == ChatUserCaches.nick_name).where(ChatChannelUserCaches.nick_name == info.nick_name).all() return info.nick_name,info.user_name,info.nick_name,info.remote_ip_address,channels # type:ignore @@ -184,7 +199,9 @@ def find_channel_by_substring(channel_name:str)->list[dict]: def find_user_by_substring(user_name:str)->list[dict]: assert isinstance(user_name,str) - names,topics,users = PG_SESSION.query(ChatChannelCaches.channel_name,ChatChannelCaches.topic,func.count(ChatChannelUserCaches.channel_name)).join(ChatUserCaches,ChatUserCaches.user_id==ChatChannelUserCaches.user_id).join(ChatChannelCaches,ChatChannelCaches.channel_name==ChatChannelUserCaches.channel_name).where(ChatUserCaches.user_name.like(f"%{user_name}%")).all() + names,topics,users = PG_SESSION.query( + ChatChannelCaches.channel_name, + ChatChannelCaches.topic,func.count(ChatChannelUserCaches.channel_name)).join(ChatUserCaches,ChatUserCaches.nick_name==ChatChannelUserCaches.nick_name).join(ChatChannelCaches,ChatChannelCaches.channel_name==ChatChannelUserCaches.channel_name).where(ChatUserCaches.user_name.like(f"%{user_name}%")).all() data: list[dict] =[] for name,topic,count in zip(names,topics,users): @@ -196,7 +213,29 @@ def find_user_by_substring(user_name:str)->list[dict]: data.append(d) return data -def get_channel_user_cache(channel_name:str)->list[dict]: - pass -def get_user_cache(user_name:str)->list[dict]: - pass \ No newline at end of file +def get_channel_user_caches(channel_name:str)->list[dict]: + result:list[ChatChannelUserCaches] = PG_SESSION.query(ChatChannelUserCaches).join(ChatChannelCaches,ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name).join(ChatUserCaches,ChatUserCaches.user_name == ChatChannelUserCaches.user_name).where(ChatChannelUserCaches.channel_name == channel_name).all() + data = [] + for r in result: + temp = {} + temp["channel_name"] = r.channel_name + temp["user_name"] = r.user_name + temp["public_ip_addr"] = r.remote_ip_address + temp["nick_name"] = r.nick_name + temp["modes"] = r.modes + data.append(temp) + return data + +def get_channel_user_cache_by_ip(ip:str,port:int)->list[dict]: + + result:list[ChatChannelUserCaches] = PG_SESSION.query(ChatChannelUserCaches).join(ChatChannelCaches,ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name).join(ChatUserCaches,ChatUserCaches.user_name == ChatChannelUserCaches.user_name).where(ChatUserCaches.remote_ip_address==ip,ChatUserCaches.remote_port == port).all() + data = [] + for r in result: + temp = {} + temp["channel_name"] = r.channel_name + temp["user_name"] = r.user_name + temp["public_ip_addr"] = r.remote_ip_address + temp["nick_name"] = r.nick_name + temp["modes"] = r.modes + data.append(temp) + return data \ No newline at end of file diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index 92c82f2a8..616ac2b72 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -6,7 +6,7 @@ import backends.protocols.gamespy.chat.data as data from backends.protocols.gamespy.chat.requests import * from frontends.gamespy.protocols.chat.aggregates.exceptions import ChatException, LoginFailedException, NickNameInUseException, NoSuchChannelException, NoSuchNickException -from frontends.gamespy.protocols.chat.contracts.results import CryptResult, GetKeyResult, ListResult, NickResult, WhoIsResult +from frontends.gamespy.protocols.chat.contracts.results import CryptResult, GetKeyResult, ListResult, NickResult, WhoIsResult, WhoResult # region General @@ -24,8 +24,8 @@ class CryptHandler(HandlerBase): _request: CryptRequest async def _data_operate(self) -> None: - result = PG_SESSION.query(ChatUserCaches).where(ChatUserCaches.remote_ip_address == - self._request.client_ip, ChatUserCaches.remote_port == self._request.client_port).first() + result = data.get_user_cache_by_ip_port( + self._request.client_ip, self._request.client_port) if result is None: raise NoSuchNickException( f"No nick found for {self._request.client_ip}") @@ -40,8 +40,7 @@ class GetKeyHandler(HandlerBase): _request: GetKeyRequest async def _data_operate(self) -> None: - caches = PG_SESSION.query(ChatUserCaches.key_value).where( - ChatUserCaches.nick_name == self._request.nick_name).first() + caches = data.get_user_cache_by_nick_name(self._request.nick_name) if caches is None: raise NoSuchNickException("nick not found") @@ -128,6 +127,7 @@ async def _data_operate(self) -> None: cache = ChatUserCaches(nick_name=self._request.nick_name, server_id=self._request.server_id, update_time=datetime.now()) + ChatUserCaches() data.add_nick_cache(cache) async def _result_construct(self) -> None: @@ -170,6 +170,30 @@ async def _data_operate(self) -> None: raise NotImplementedError("maybe update the user caches") +class WhoHandler(HandlerBase): + _request: WhoRequest + + async def _data_operate(self) -> None: + if self._request.request_type == WhoRequestType.GET_CHANNEL_USER_INFO: + self._get_channel_user_info() + else: + self._get_user_info() + + def _get_channel_user_info(self) -> None: + self._data = data.get_channel_user_caches(self._request.channel_name) + + def _get_user_info(self) -> None: + self._data = data.get_channel_user_cache_by_ip( + self._request.client_ip, self._request.client_port) + + async def _result_construct(self) -> None: + infos = [] + for d in self._data: + info = WhoResult.WhoInfo(**d) + infos.append(info) + self._result = WhoResult(infos=infos) + + class WhoIsHandler(HandlerBase): _request: WhoIsRequest @@ -184,21 +208,6 @@ async def _result_construct(self) -> None: joined_channel_name=self._data[4]) -class WhoHandler(HandlerBase): - _request: WhoRequest - - async def _data_operate(self) -> None: - if self._request.request_type == WhoRequestType.GET_CHANNEL_USER_INFO: - self._get_channel_user_info() - else: - self._get_user_info() - - def _get_channel_user_info(self) -> None: - pass - - def _get_user_info(self) -> None: - pass - # class JoinHandler(HandlerBase): # _request: JoinRequest diff --git a/src/backends/protocols/gamespy/presence_search_player/data.py b/src/backends/protocols/gamespy/presence_search_player/data.py index 9df27fefb..b32051399 100644 --- a/src/backends/protocols/gamespy/presence_search_player/data.py +++ b/src/backends/protocols/gamespy/presence_search_player/data.py @@ -91,7 +91,7 @@ def get_user(email: str) -> Users | None: return result -def get_profile(user_id: int, nick_name: str) -> Profiles: +def get_profile(user_id: int, nick_name: str) -> Profiles | None: assert isinstance(user_id, int) assert isinstance(nick_name, str) result = PG_SESSION.query(Profiles).where( @@ -100,7 +100,7 @@ def get_profile(user_id: int, nick_name: str) -> Profiles: return result -def get_sub_profile(profile_id: int, namespace_id: int, product_id: int) -> SubProfiles: +def get_sub_profile(profile_id: int, namespace_id: int, product_id: int) -> SubProfiles | None: if TYPE_CHECKING: assert isinstance(SubProfiles.profileid, Column) assert isinstance(SubProfiles.namespaceid, Column) @@ -255,7 +255,7 @@ def get_matched_info_by_email( return temp -def get_matched_info_by_nick_and_email(nick_name: str, email: str)->list[dict]: +def get_matched_info_by_nick_and_email(nick_name: str, email: str) -> list[dict]: result = ( PG_SESSION.query( diff --git a/src/backends/protocols/gamespy/presence_search_player/handlers.py b/src/backends/protocols/gamespy/presence_search_player/handlers.py index 53352c987..63143cd59 100644 --- a/src/backends/protocols/gamespy/presence_search_player/handlers.py +++ b/src/backends/protocols/gamespy/presence_search_player/handlers.py @@ -46,9 +46,8 @@ async def _data_operate(self) -> None: self.profile = data.get_profile(self.user.userid, self._request.nick) if self.profile is None: self._create_profile() - + assert self.profile is not None assert isinstance(self.profile.profileid, int) - self.subprofile = data.get_sub_profile( profile_id=self.profile.profileid, namespace_id=self._request.namespace_id, product_id=self._request.product_id) if self.subprofile is None: diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index bead1fe9e..b294e9a85 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -77,7 +77,7 @@ def get_peer_group_channel(group_data: list[dict]) -> list[PeerRoomInfo]: ).all() # Convert the result to a list of PeerRoomInfo objects - data = [PeerRoomInfo(**s) for s in result] + data = [PeerRoomInfo(**s.__dict__) for s in result] return data @@ -89,7 +89,7 @@ def get_server_info_with_instant_key(instant_key: str) -> Optional[GameServerCac return result -def get_server_info_with_game_name(game_name: str) -> Optional[GameServerInfo]: +def get_server_info_with_game_name(game_name: str) -> Optional[GameServerCaches]: assert isinstance(game_name, str) result = PG_SESSION.query(GameServerCaches).where( GameServerCaches.game_name == game_name).first() @@ -101,7 +101,7 @@ def get_server_info_list_with_game_name(game_name: str) -> list[GameServerInfo]: GameServerCaches.game_name == game_name).all() data = [] for s in result: - data.append(GameServerInfo(**s)) + data.append(GameServerInfo(**s.__dict__)) return data @@ -112,7 +112,7 @@ def get_server_info_with_ip_and_port(ip: str, port: int) -> GameServerInfo: GameServerCaches.host_ip_address == ip, GameServerCaches.query_report_port == port).first() if result is None: raise ServerBrowserException("game server not found") - data = GameServerInfo(**result) + data = GameServerInfo(**result.__dict__) return data diff --git a/src/frontends/gamespy/protocols/query_report/aggregates/peer_room_info.py b/src/frontends/gamespy/protocols/query_report/aggregates/peer_room_info.py index 9315f4061..11fcc127d 100644 --- a/src/frontends/gamespy/protocols/query_report/aggregates/peer_room_info.py +++ b/src/frontends/gamespy/protocols/query_report/aggregates/peer_room_info.py @@ -19,10 +19,10 @@ class PeerRoomInfo(BaseModel): number_of_games: int = Field(default=0, alias="numgames") number_of_playing: int = Field(default=0, alias="numplaying") - def __init__(self, game_name, group_id, room_name) -> None: - self.game_name = game_name - self.group_id = group_id - self.room_name = room_name + # def __init__(self, game_name, group_id, room_name) -> None: + # self.game_name = game_name + # self.group_id = group_id + # self.room_name = room_name def get_gamespy_dict(self) -> MappingProxyType: """ diff --git a/src/requirements.txt b/src/requirements.txt index 7663f3023..0bb497d6f 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -3,7 +3,6 @@ psycopg2-binary == 2.9.10 sqlalchemy == 2.0.36 jsonpickle == 3.0.3 email_validator == 2.1.1 -attrs requests == 2.32.3 fastapi == 0.115.4 xmltodict == 0.14.2 @@ -12,4 +11,6 @@ pydantic == 2.9.2 redis == 5.2.0 websocket-client ==1.8.0 uvicorn == 0.32.0 -prettytable == 3.11.0 \ No newline at end of file +prettytable == 3.11.0 +responses == 0.25.6 +pydantic ==2.10.6 \ No newline at end of file From e5bc1b19d0b56ba335e5230f8273ec39da6675a4 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 7 Mar 2025 09:34:21 +0000 Subject: [PATCH 162/231] update chat apis --- common/UniSpy_pg.sql | 4 +- src/backends/library/database/pg_orm.py | 7 +- .../protocols/gamespy/chat/abstractions.py | 10 +- .../protocols/gamespy/chat/channel.py | 351 ++++++++---------- src/backends/protocols/gamespy/chat/data.py | 45 ++- .../protocols/gamespy/chat/handlers.py | 96 ++++- .../protocols/gamespy/chat/managers.py | 63 ++-- .../gamespy/presence_search_player/data.py | 7 +- .../protocols/chat/aggregates/enums.py | 56 +-- .../protocols/chat/contracts/requests.py | 8 +- .../protocols/chat/contracts/results.py | 5 +- .../tests/gamespy/chat/mock_objects.py | 2 +- .../tests/gamespy/chat/request_tests.py | 2 +- .../presence_search_player/mock_objects.py | 2 +- 14 files changed, 341 insertions(+), 317 deletions(-) diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 649b87e3d..613d59dad 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -140,7 +140,9 @@ CREATE TABLE unispy.chat_channel_caches ( max_num_user integer NOT NULL, key_values jsonb, invited_nicks jsonb, - update_time timestamp without time zone NOT NULL + update_time timestamp without time zone NOT NULL, + modes character varying NOT NULL, + banned_nicks jsonb ); diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 02d9ce3fa..33c3e7b59 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -48,6 +48,7 @@ def process_result_value(self, value, dialect): class Base(DeclarativeBase): pass + class Users(Base): __tablename__ = "users" @@ -241,6 +242,9 @@ class ChatChannelCaches(Base): key_values = Column(JSONB, default={}) invited_nicks = Column(JSONB, default=[]) update_time = Column(DateTime, nullable=False) + creator = Column(String, nullable=True) + modes = Column(JSONB, default=[]) + banned_nicks = Column(JSONB, default=[]) class ChatUserCaches(Base): @@ -273,8 +277,7 @@ class ChatChannelUserCaches(Base): is_channel_creator = Column(Boolean, nullable=False) remote_ip_address = Column(INET, nullable=False) remote_port = Column(Integer, nullable=False) - key_values = Column(JSONB) - modes = Column(String) + key_values = Column(JSONB, default={}) class GameServerCaches(Base): diff --git a/src/backends/protocols/gamespy/chat/abstractions.py b/src/backends/protocols/gamespy/chat/abstractions.py index caf1ad562..b113e3746 100644 --- a/src/backends/protocols/gamespy/chat/abstractions.py +++ b/src/backends/protocols/gamespy/chat/abstractions.py @@ -17,8 +17,8 @@ def _get_user(self): self._request.client_ip, self._request.client_port) def _get_channel(self): - self._channel = data.get_channel_by_name_and_ip_port( - self._request.channel_name, self._request.client_ip, self._request.client_port) + self._channel = data.get_channel_by_name( + self._request.channel_name) def _get_channel_user(self): self._channel_user = data.get_channel_user_cache_by_name_and_ip_port( @@ -50,9 +50,3 @@ async def _request_check(self) -> None: self._get_channel_user() self._check_channel_user() - - -class GetChannelKeyHandler(ChannelHandlerBase): - def _get_key_values(self): - assert isinstance(self._channel, ChatChannelCaches) - self._key_values = self._channel.key_values diff --git a/src/backends/protocols/gamespy/chat/channel.py b/src/backends/protocols/gamespy/chat/channel.py index cc7a2519c..6fb723cb7 100644 --- a/src/backends/protocols/gamespy/chat/channel.py +++ b/src/backends/protocols/gamespy/chat/channel.py @@ -1,200 +1,151 @@ -# import datetime -# from typing import Optional -# from uuid import UUID - -# from pydantic import BaseModel, field_validator -# from frontends.gamespy.library.abstractions.brocker import BrockerBase -# from frontends.gamespy.library.network.brockers import WebsocketBrocker -# from frontends.gamespy.library.configs import CONFIG -# from frontends.gamespy.protocols.chat.abstractions.contract import ResponseBase -# from frontends.gamespy.protocols.chat.aggregates.channel_user import ChannelUser -# from backends.protocols.gamespy.chat.managers import KeyValueManager -# from frontends.gamespy.protocols.chat.aggregates.peer_room import PeerRoom -# from frontends.gamespy.protocols.chat.applications.client import Client -# from frontends.gamespy.protocols.chat.contracts.requests import ModeRequest -# from frontends.gamespy.protocols.chat.aggregates.enums import PeerRoomType -# from frontends.gamespy.protocols.chat.aggregates.exceptions import ChatException -# from frontends.gamespy.protocols.server_browser.v2.aggregations.server_info_builder import PEER_GROUP_LIST - -# MIN_CHANNEL_NAME_LENGTH = 4 - - -# class Channel: -# """ -# The channel class, every channel class manage a brocker -# """ -# server_id: UUID -# game_name: str -# name: str -# max_num_user: int -# create_time: datetime.datetime -# kv_manager: KeyValueManager -# room_type: PeerRoomType -# password: Optional[str] -# topic: Optional[str] -# group_id: Optional[int] -# room_name: Optional[str] -# previously_join_channel: Optional[str] -# ban_list: dict[str, ChannelUser] -# users: dict[str, ChannelUser] -# _creator_nick_name: str - -# @property -# def is_valid_peer_room(self) -> bool: -# return self.group_id is not None and self.room_name is not None - -# def __init__(self, name: str, client: Client, password: Optional[str] = None, brocker_cls: type[BrockerBase] = WebsocketBrocker) -> None: -# # region channel init -# self.server_id = client.server_config.server_id -# self.name = name -# self.password = password -# self.game_name = client.info.gamename -# self.previously_join_channel = client.info.previously_joined_channel -# self.room_type = PeerRoom.get_room_type(name) -# self.create_time = datetime.datetime.now() -# self.topic = None -# self.group_id = None -# self.room_name = None -# self.previously_join_channel = None -# self.kv_manager = KeyValueManager() -# self.max_num_user = 200 -# # setup the message broker -# self._broker = brocker_cls( -# self.name, CONFIG.backend.url, self._get_message_from_brocker) -# # channel user init -# self._broker.subscribe() -# self.ban_list = {} -# self.users = {} -# self._creator_nick_name = client.info.nick_name - -# match self.room_type: -# case PeerRoomType.Group: -# self.get_group_id() -# self.get_peer_room_name() -# case PeerRoomType.Staging: -# self.get_staging_room_name() -# case PeerRoomType.Title: -# self.get_title_room_name() - -# def get_group_id(self): -# group_id_str = self.name.split("!")[1] -# try: -# group_id = int(group_id_str) -# except ValueError: -# raise Exception("Peer room group id is incorrect") -# self.group_id = group_id - -# def get_peer_room_name(self): -# if self.game_name in PEER_GROUP_LIST: -# grouplist = PEER_GROUP_LIST[self.game_name] -# room = next( -# (g for g in grouplist if g["group_id"] == self.group_id), None) -# if room is None: -# raise Exception(f"Invalid peer room: {self.name}") -# self.room_name = room["room_name"] - -# def get_staging_room_name(self): -# self.room_name = self.name.split("!")[-1] - -# def get_title_room_name(self): -# self.get_staging_room_name() - -# @property -# def creator(self) -> Optional[ChannelUser]: -# if self._creator_nick_name in self.users: -# return self.users[self._creator_nick_name] -# else: -# return None - -# def _add_ban_user(self, request: ModeRequest): -# assert isinstance(request, ModeRequest) -# if request.nick_name not in self.users: -# raise ChatException( -# f"user:{request.nick_name} did not connected to this server" -# ) -# user = self.users[request.nick_name] - -# self.ban_list[request.nick_name] = user - -# def _remove_ban_user(self, nick_name: str): -# if nick_name in self.ban_list: -# del self.ban_list[nick_name] - -# def _add_channel_operator(self, nick_name: str): -# if nick_name not in self.users: -# return - -# user = self.users[nick_name] -# if not user.is_channel_creator: -# user.is_channel_creator = True - -# def _remove_channel_operator(self, nick_name: str): -# if nick_name not in self.users: -# return - -# user = self.users[nick_name] -# user.is_channel_creator = False - -# def _user_voice_permission(self, nick_name: str, enable: bool = True): -# if nick_name not in self.users: -# return -# user = self.users[nick_name] -# user.is_voiceable = enable - -# def get_user_by_nick(self, nick_name: str) -> Optional[ChannelUser]: -# if nick_name in self.users: -# return self.users[nick_name] -# return None - -# def get_user_by_client(self, client: Client) -> Optional[ChannelUser]: -# for user in self.users.values(): -# if ( -# client.connection.remote_ip == user.remote_ip -# and client.connection.remote_port == user.remote_port -# ): -# return user -# return None - -# def add_bind_on_user_and_channel(self, joiner: ChannelUser): -# if joiner.client.info.nick_name not in joiner.channel.users: -# joiner.client.info.joined_channels[joiner.channel.name] = joiner.channel - -# def remove_bind_on_user_and_channel(self, leaver: ChannelUser): -# if leaver.client.info.nick_name in leaver.channel.users: -# del leaver.channel.users[leaver.client.info.nick_name] -# if leaver.channel.name in leaver.client.info.joined_channels: -# del leaver.client.info.joined_channels[leaver.channel.name] - -# def multicast(self, sender: Client, message: ResponseBase, is_skip_sender=False): -# for nick, user in self.users.items(): -# if is_skip_sender: -# if sender.info.nick_name == nick: -# continue -# else: -# user.client.send(message) - -# def _get_message_from_brocker(self, message: str): -# """ -# we directly send the message from brocker to all channel local user -# """ -# for nick, user in self.users.items(): -# user.client.connection.send(message.encode()) - -# def remove_user(self, user: ChannelUser): -# user.client.info.previously_joined_channel - - -# class BrockerMessage(BaseModel): -# channel_name: str -# message: str - -# @field_validator("channel_name") -# def validate_channel_name(cls, value): -# if value is None or len(value) < 3: -# raise ValueError("channel name is not valid") -# return value - -# @field_validator("message") -# def validate_message(cls, value): -# if value is None or len(value) < 3: -# raise ValueError("message length is not valid") +from datetime import datetime +from typing import TYPE_CHECKING +from uuid import UUID + +from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatChannelUserCaches, ChatUserCaches +import backends.protocols.gamespy.chat.data as data +from protocols.chat.aggregates.enums import ModeOperationType +from protocols.chat.aggregates.exceptions import InviteOnlyChanException, NoSuchChannelException + + +class ChannelUserHelper: + @staticmethod + def get_mode_string(user: ChatChannelUserCaches): + assert isinstance(user.is_channel_operator, bool) + assert isinstance(user.is_voiceable, bool) + buffer = "" + if user.is_channel_operator: + buffer += "@" + if user.is_voiceable: + buffer += "+" + return buffer + + + +class ChannelHelper: + @staticmethod + def join(channel: ChatChannelCaches, user: ChatUserCaches) -> None: + # 1 check if is a invited channel + # 1.1 check if user is in a invited list + assert isinstance(channel.modes, list) + # assert isinstance(channel.invited_nicks,list) + if ModeOperationType.SET_INVITED_ONLY in channel.modes: + if user.nick_name not in channel.invited_nicks: + raise InviteOnlyChanException( + f"You can only join channel: {channel.channel_name} when you are in invite list") + + # 2 check if user is in ban list, if it is user can not join + raise NotImplementedError() + + @staticmethod + def quit(channel: ChatChannelCaches, user: ChatUserCaches) -> None: + raise NotImplementedError() + + @staticmethod + def kick(channel: ChatChannelCaches, kicker: ChatUserCaches, kickee: ChatUserCaches) -> None: + raise NotImplementedError() + + @staticmethod + def invite(channel: ChatChannelCaches, inviter: ChatChannelUserCaches, invitee: ChatUserCaches) -> None: + + if str(inviter.channel_name) != str(channel.channel_name): + raise InviteOnlyChanException( + f"The inviter:{inviter.nick_name} is not a user in channel:{channel.channel_name}.") + + assert isinstance(channel.invited_nicks, list) + channel.invited_nicks.append(invitee.nick_name) + PG_SESSION.commit() + + @staticmethod + def create(server_id: UUID, + channel_name: str, + password: str, + game_name: str, + room_name: str, + topic: str, + group_id: int, + max_num_user: int, + key_values: dict, + update_time: datetime, + modes: str, + creator: str | None = None) -> ChatChannelCaches: + # check whether if channel exist, if not user is creator + is_exist = data.is_channel_exist(channel_name, game_name) + if is_exist: + raise NoSuchChannelException( + f"Channel: {channel_name} is already exist, can not create a new one") + cache = ChatChannelCaches(server_id=server_id, + channel_name=channel_name, + password=password, + game_name=game_name, + room_name=room_name, + topic=topic, + group_id=group_id, + max_num_user=max_num_user, + key_values=key_values, + update_time=update_time, + creator=creator, + modes=modes) + PG_SESSION.add(cache) + PG_SESSION.commit() + + return cache + + @staticmethod + def change_modes(channel: ChatChannelCaches, changer: ChatChannelUserCaches, modes: list[ModeOperationType], args: list): + assert isinstance(channel, ChatChannelCaches) + assert isinstance(changer, ChatChannelUserCaches) + assert isinstance(channel.modes, list) + assert all(isinstance(m, ModeOperationType) for m in modes) + + for flag in modes: + if flag not in channel.modes: + channel.modes.append(flag) + match flag: + case ModeOperationType.ENABLE_USER_QUIET_FLAG: + if changer.is_channel_operator: # type:ignore + if ModeOperationType.ENABLE_USER_QUIET_FLAG not in channel.modes: + channel.modes.append( + ModeOperationType.ENABLE_USER_QUIET_FLAG) + case ModeOperationType.DISABLE_USER_QUIET_FLAG: + if changer.is_channel_operator: # type:ignore + if ModeOperationType.ENABLE_USER_QUIET_FLAG not in channel.modes: + channel.modes.remove( + ModeOperationType.ENABLE_USER_QUIET_FLAG) + case ModeOperationType.ADD_CHANNEL_PASSWORD: + assert isinstance(args[0], str) + if changer.is_channel_operator: # type:ignore + channel.password = args[0] # type:ignore + case ModeOperationType.REMOVE_CHANNEL_PASSWORD: + if changer.is_channel_operator: # type:ignore + channel.password = "" # type:ignore + case ModeOperationType.ADD_CHANNEL_USER_LIMITS: + channel.max_num_user = args[0] # type: ignore + case ModeOperationType.REMOVE_CHANNEL_USER_LIMITS: + channel.max_num_user = 200 # type: ignore + case ModeOperationType.ADD_BAN_ON_USER: + assert isinstance(channel.banned_nicks, list) + if args[0] not in list(channel.banned_nicks): # type: ignore + channel.banned_nicks.append(args[0]) + case ModeOperationType.REMOVE_BAN_ON_USER: + assert isinstance(channel.banned_nicks, list) + if args[0] in list(channel.banned_nicks): + channel.banned_nicks.remove(args[0]) + case ModeOperationType.ADD_CHANNEL_OPERATOR: + assert isinstance(args[0], ChatChannelUserCaches) + user: ChatChannelUserCaches = args[0] + user.is_channel_operator = True # type: ignore + case ModeOperationType.REMOVE_CHANNEL_OPERATOR: + assert isinstance(args[0], ChatChannelUserCaches) + user: ChatChannelUserCaches = args[0] + user.is_channel_operator = False # type: ignore + case ModeOperationType.ENABLE_USER_VOICE_PERMISSION: + assert isinstance(args[0], ChatChannelUserCaches) + user: ChatChannelUserCaches = args[0] + user.is_voiceable = True # type: ignore + case ModeOperationType.DISABLE_USER_VOICE_PERMISSION: + assert isinstance(args[0], ChatChannelUserCaches) + user: ChatChannelUserCaches = args[0] + user.is_voiceable = False # type: ignore + + PG_SESSION.commit() diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index 83273bc7b..de906ed70 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -29,11 +29,13 @@ def nick_and_email_login(nick_name: str, email: str, password_hash: str) -> tupl assert isinstance(password_hash, str) result = PG_SESSION.query(Users.userid, Profiles.profileid, - Users.emailverified, Users.banned).join(Profiles, (Users.userid == Profiles.userid)).where( - Users.email == email, - Profiles.nick == nick_name, - Users.password == password_hash - ).first() + Users.emailverified, Users.banned)\ + .join(Profiles, (Users.userid == Profiles.userid))\ + .where( + Users.email == email, + Profiles.nick == nick_name, + Users.password == password_hash + ).first() if TYPE_CHECKING: result = cast(tuple[int, int, bool, bool], result) if result is None: @@ -90,6 +92,7 @@ def add_channel(channel:ChatChannelCaches): PG_SESSION.add(channel) PG_SESSION.commit() + def get_channel_by_name_and_game(channel_name:str,game_name:str)->ChatChannelCaches|None: channel = PG_SESSION.query(ChatChannelCaches)\ .where(ChatChannelCaches.channel_name == channel_name, @@ -97,6 +100,12 @@ def get_channel_by_name_and_game(channel_name:str,game_name:str)->ChatChannelCac .first() return channel +def get_channel_by_name(channel_name:str)->ChatChannelCaches|None: + channel = PG_SESSION.query(ChatChannelCaches)\ + .where(ChatChannelCaches.channel_name == channel_name)\ + .first() + return channel + def get_channel_by_name_and_ip_port(channel_name:str,ip:str,port:int)->ChatChannelCaches|None: assert isinstance(channel_name,str) assert isinstance(ip,str) @@ -119,6 +128,11 @@ def get_channel_user_cache_by_name_and_ip_port(channel_name:str,ip:str,port:int) ChatChannelUserCaches.remote_port == port).first() return result +def get_channel_user_caches_by_name(channel_name:str)->list: + assert isinstance(channel_name,str) + result = PG_SESSION.query(ChatChannelUserCaches.key_values).where(ChatChannelUserCaches.channel_name == channel_name).all() + return result + def update_channel_time(channel:ChatChannelCaches): channel.update_time = datetime.now() # type: ignore PG_SESSION.commit() @@ -185,8 +199,10 @@ def add_invited(channel_name:str,client_ip:str,client_port:int): def find_channel_by_substring(channel_name:str)->list[dict]: assert isinstance(channel_name,str) - names,topics = PG_SESSION.query(ChatChannelCaches.channel_name,ChatChannelCaches.topic).where(ChatChannelCaches.channel_name.like(f"%{channel_name}%")).all() - users = PG_SESSION.query(ChatChannelUserCaches).where(ChatChannelUserCaches.channel_name.like(f"%{channel_name}%")).all() + names,topics = PG_SESSION.query(ChatChannelCaches.channel_name,ChatChannelCaches.topic)\ + .where(ChatChannelCaches.channel_name.like(f"%{channel_name}%")).all() + users = PG_SESSION.query(ChatChannelUserCaches)\ + .where(ChatChannelUserCaches.channel_name.like(f"%{channel_name}%")).all() data: list[dict] =[] for name,topic,count in zip(names,topics,users): d = { @@ -201,7 +217,10 @@ def find_user_by_substring(user_name:str)->list[dict]: assert isinstance(user_name,str) names,topics,users = PG_SESSION.query( ChatChannelCaches.channel_name, - ChatChannelCaches.topic,func.count(ChatChannelUserCaches.channel_name)).join(ChatUserCaches,ChatUserCaches.nick_name==ChatChannelUserCaches.nick_name).join(ChatChannelCaches,ChatChannelCaches.channel_name==ChatChannelUserCaches.channel_name).where(ChatUserCaches.user_name.like(f"%{user_name}%")).all() + ChatChannelCaches.topic,func.count(ChatChannelUserCaches.channel_name))\ + .join(ChatUserCaches,ChatUserCaches.nick_name==ChatChannelUserCaches.nick_name)\ + .join(ChatChannelCaches,ChatChannelCaches.channel_name==ChatChannelUserCaches.channel_name)\ + .where(ChatUserCaches.user_name.like(f"%{user_name}%")).all() data: list[dict] =[] for name,topic,count in zip(names,topics,users): @@ -213,8 +232,14 @@ def find_user_by_substring(user_name:str)->list[dict]: data.append(d) return data +def create_channel_user_caches(chan_user:ChatChannelUserCaches): + PG_SESSION.add(chan_user) + PG_SESSION.commit() + def get_channel_user_caches(channel_name:str)->list[dict]: - result:list[ChatChannelUserCaches] = PG_SESSION.query(ChatChannelUserCaches).join(ChatChannelCaches,ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name).join(ChatUserCaches,ChatUserCaches.user_name == ChatChannelUserCaches.user_name).where(ChatChannelUserCaches.channel_name == channel_name).all() + result:list[ChatChannelUserCaches] = PG_SESSION.query(ChatChannelUserCaches).join(ChatChannelCaches,ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name)\ + .join(ChatUserCaches,ChatUserCaches.user_name == ChatChannelUserCaches.user_name)\ + .where(ChatChannelUserCaches.channel_name == channel_name).all() data = [] for r in result: temp = {} @@ -222,7 +247,6 @@ def get_channel_user_caches(channel_name:str)->list[dict]: temp["user_name"] = r.user_name temp["public_ip_addr"] = r.remote_ip_address temp["nick_name"] = r.nick_name - temp["modes"] = r.modes data.append(temp) return data @@ -236,6 +260,5 @@ def get_channel_user_cache_by_ip(ip:str,port:int)->list[dict]: temp["user_name"] = r.user_name temp["public_ip_addr"] = r.remote_ip_address temp["nick_name"] = r.nick_name - temp["modes"] = r.modes data.append(temp) return data \ No newline at end of file diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index 616ac2b72..5015bb750 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -3,10 +3,13 @@ from backends.library.abstractions.contracts import RequestBase from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatUserCaches, ChatChannelUserCaches +from backends.protocols.gamespy.chat.abstractions import ChannelHandlerBase +from backends.protocols.gamespy.chat.channel import ChannelUserHelper import backends.protocols.gamespy.chat.data as data +from backends.protocols.gamespy.chat.managers import KeyValueManager from backends.protocols.gamespy.chat.requests import * from frontends.gamespy.protocols.chat.aggregates.exceptions import ChatException, LoginFailedException, NickNameInUseException, NoSuchChannelException, NoSuchNickException -from frontends.gamespy.protocols.chat.contracts.results import CryptResult, GetKeyResult, ListResult, NickResult, WhoIsResult, WhoResult +from frontends.gamespy.protocols.chat.contracts.results import CryptResult, GetCKeyResult, GetKeyResult, ListResult, NickResult, WhoIsResult, WhoResult # region General @@ -208,24 +211,83 @@ async def _result_construct(self) -> None: joined_channel_name=self._data[4]) -# class JoinHandler(HandlerBase): -# _request: JoinRequest +# region Channel +class GetChannelKeyHandler(ChannelHandlerBase): + def _get_key_values(self): + assert isinstance(self._channel, ChatChannelCaches) + self._key_values = self._channel.key_values -# async def _data_fetch(self) -> None: -# is_chan_exist = data.is_channel_exist(self._request.channel_name, -# self._request.game_name) -# # group_id = -# if is_chan_exist: -# is_user_exist = data.is_user_exist( -# self._request.client_ip, self._request.client_port) -# else: -# # create channel -# # create user -# is_peer_room = -# chan = ChatChannelCaches(channel_name=self._request.channel_name, server_id=self._request.server_id, game_name, room_name, topic, -# password=self._request.password, group_id, max_num_user=200, key_values=None, update_time=datetime.now()) + async def _request_check(self) -> None: + await super()._request_check() + self._get_key_values() -# region Channel +class GetCKeyHandler(ChannelHandlerBase): + _request: GetCKeyRequest + + async def _data_operate(self) -> None: + match self._request.request_type: + case GetKeyRequestType.GET_CHANNEL_ALL_USER_KEY_VALUE: + self.get_channel_all_user_key_value() + case GetKeyRequestType.GET_CHANNEL_SPECIFIC_USER_KEY_VALUE: + self.get_channel_specific_user_key_value() + + def get_channel_all_user_key_value(self): + self._data = data.get_channel_user_caches_by_name( + self._request.channel_name) + + def get_channel_specific_user_key_value(self): + d = data.get_channel_user_cache_by_name( + self._request.channel_name, self._request.nick_name) + if d is not None: + self._data = [d] + + async def _result_construct(self) -> None: + if self._data is None: + return + infos = [] + for d in self._data: + assert isinstance(d, ChatChannelUserCaches) + assert isinstance(d.nick_name, str) + assert isinstance(d.key_values, dict) + info = GetCKeyResult.GetCKeyInfos( + nick_name=d.nick_name, + user_values=list(d.key_values.values())) + infos.append(info) + + self._result = GetCKeyResult(infos=infos, + channel_name=self._request.channel_name) + + +class JoinHandler(ChannelHandlerBase): + _request: JoinRequest + + async def _request_check(self) -> None: + self._get_user() + self._check_user() + + self._get_channel() + self._check_channel() + + self._get_channel_user() + + async def _data_operate(self) -> None: + assert self._user is not None + assert isinstance(self._user.nick_name, str) + assert self._channel is not None + assert isinstance(self._channel.channel_name, str) + + if self._channel_user is None: + cache = ChatChannelUserCaches( + nick_name=self._user.nick_name, + user_name=self._user.user_name, + channel_name=self._channel.channel_name, + update_time=datetime.now(), + is_voiceable=True, + is_channel_operator=False, + is_channel_creator=False, + remote_ip_address=self._user.remote_ip_address, + remote_port=self._user.remote_port, + key_values={}) # region Message diff --git a/src/backends/protocols/gamespy/chat/managers.py b/src/backends/protocols/gamespy/chat/managers.py index fa2bbe982..d853c6e13 100644 --- a/src/backends/protocols/gamespy/chat/managers.py +++ b/src/backends/protocols/gamespy/chat/managers.py @@ -1,38 +1,37 @@ # from typing import TYPE_CHECKING, Optional -# class KeyValueManager: -# data: dict -# """ -# store the key and values -# """ - -# def __init__(self): -# self.data = {} - -# def update(self, data: dict): -# for key, value in data.items(): -# self.data[key] = value - -# def build_key_value_string(self, key_values: dict): -# flags = "" -# for key, value in key_values.items(): -# flags += f"\\{key}\\{value}" -# return flags - -# def get_value_string(self, keys: list[str]) -> str: -# values = "" -# for key in keys: -# if key in self.data: -# values += f"\\{self.data[key]}" -# else: -# values += "\\" -# # Uncomment the line below to raise an exception if key is not found -# # raise Exception(f"Can not find key: {key}") -# return values - -# def is_contain_all_key(self, keys: list[str]): -# return all(key in self.data for key in keys) +class KeyValueManager: + """ + Handle key value string + """ + @staticmethod + def update(data: dict): + for key, value in data.items(): + data[key] = value + + @staticmethod + def build_key_value_string(key_values: dict): + flags = "" + for key, value in key_values.items(): + flags += f"\\{key}\\{value}" + return flags + + @staticmethod + def get_value_string(data: dict, keys: list[str]) -> str: + values = "" + for key in keys: + if key in data: + values += f"\\{data[key]}" + else: + values += "\\" + # Uncomment the line below to raise an exception if key is not found + # raise Exception(f"Can not find key: {key}") + return values + + @staticmethod + def is_contain_all_key(data: dict, keys: list[str]): + return all(key in data for key in keys) # if TYPE_CHECKING: diff --git a/src/backends/protocols/gamespy/presence_search_player/data.py b/src/backends/protocols/gamespy/presence_search_player/data.py index b32051399..0bf105966 100644 --- a/src/backends/protocols/gamespy/presence_search_player/data.py +++ b/src/backends/protocols/gamespy/presence_search_player/data.py @@ -101,10 +101,9 @@ def get_profile(user_id: int, nick_name: str) -> Profiles | None: def get_sub_profile(profile_id: int, namespace_id: int, product_id: int) -> SubProfiles | None: - if TYPE_CHECKING: - assert isinstance(SubProfiles.profileid, Column) - assert isinstance(SubProfiles.namespaceid, Column) - assert isinstance(SubProfiles.namespaceid, Column) + assert isinstance(SubProfiles.profileid, Column) + assert isinstance(SubProfiles.namespaceid, Column) + assert isinstance(SubProfiles.namespaceid, Column) result = PG_SESSION.query(SubProfiles).where( SubProfiles.profileid == profile_id, SubProfiles.namespaceid == namespace_id, diff --git a/src/frontends/gamespy/protocols/chat/aggregates/enums.py b/src/frontends/gamespy/protocols/chat/aggregates/enums.py index 96d203982..1f0646fd6 100644 --- a/src/frontends/gamespy/protocols/chat/aggregates/enums.py +++ b/src/frontends/gamespy/protocols/chat/aggregates/enums.py @@ -21,34 +21,34 @@ class GetKeyRequestType(IntEnum): GET_CHANNEL_SPECIFIC_USER_KEY_VALUE = 1 -class ModeOperationType(IntEnum): - ENABLE_USER_QUIET_FLAG = 0 - DISABLE_USER_QUIET_FLAG = 1 - ADD_CHANNEL_PASSWORD = 2 - REMOVE_CHANNEL_PASSWORD = 3 - ADD_CHANNEL_USER_LIMITS = 4 - REMOVE_CHANNEL_USER_LIMITS = 5 - ADD_BAN_ON_USER = 6 - GET_BANNED_USERS = 7 - REMOVE_BAN_ON_USER = 8 - ADD_CHANNEL_OPERATOR = 9 - REMOVE_CHANNEL_OPERATOR = 10 - ENABLE_USER_VOICE_PERMISSION = 11 - DISABLE_USER_VOICE_PERMISSION = 12 - SET_INVITED_ONLY = 13 - REMOVE_INVITED_ONLY = 14 - SET_PRIVATE_CHANNEL_FLAG = 15 - REMOVE_PRIVATE_CHANNEL_FLAG = 16 - SET_SECRET_CHANNEL_FLAG = 17 - REMOVE_SECRET_CHANNEL_FLAG = 18 - SET_MODERATED_CHANNEL_FLAG = 19 - REMOVE_MODERATED_CHANNEL_FLAG = 20 - ENABLE_EXTERNAL_MESSAGES_FLAG = 21 - DISABLE_EXTERNAL_MESSAGES_FLAG = 22 - SET_TOPIC_CHANGE_BY_OPERATOR_FLAG = 23 - REMOVE_TOPIC_CHANGE_BY_OPERATOR_FLAG = 24 - SET_OPERATOR_ABEY_CHANNEL_LIMITS = 25 - REMOVE_OPERATOR_ABEY_CHANNEL_LIMITS = 26 +class ModeOperationType(Enum): + ENABLE_USER_QUIET_FLAG = "+q" + DISABLE_USER_QUIET_FLAG = "-q" + ADD_CHANNEL_PASSWORD = "+k" + REMOVE_CHANNEL_PASSWORD = "-k" + ADD_CHANNEL_USER_LIMITS = "+l" + REMOVE_CHANNEL_USER_LIMITS = "-l" + ADD_BAN_ON_USER = "+b" + GET_BANNED_USERS = "+b" + REMOVE_BAN_ON_USER = "-b" + ADD_CHANNEL_OPERATOR = "+co" + REMOVE_CHANNEL_OPERATOR = "-co" + ENABLE_USER_VOICE_PERMISSION = "+cv" + DISABLE_USER_VOICE_PERMISSION = "-cv" + SET_INVITED_ONLY = "+i" + REMOVE_INVITED_ONLY = "-i" + SET_PRIVATE_CHANNEL_FLAG = "+p" + REMOVE_PRIVATE_CHANNEL_FLAG = "-p" + SET_SECRET_CHANNEL_FLAG = "+s" + REMOVE_SECRET_CHANNEL_FLAG = "-s" + SET_MODERATED_CHANNEL_FLAG = "+m" + REMOVE_MODERATED_CHANNEL_FLAG = "-m" + ENABLE_EXTERNAL_MESSAGES_FLAG = "+n" + DISABLE_EXTERNAL_MESSAGES_FLAG = "-n" + SET_TOPIC_CHANGE_BY_OPERATOR_FLAG = "+t" + REMOVE_TOPIC_CHANGE_BY_OPERATOR_FLAG = "-t" + SET_OPERATOR_ABEY_CHANNEL_LIMITS = "+e" + REMOVE_OPERATOR_ABEY_CHANNEL_LIMITS = "-e" class ModeRequestType(IntEnum): diff --git a/src/frontends/gamespy/protocols/chat/contracts/requests.py b/src/frontends/gamespy/protocols/chat/contracts/requests.py index 89abd19e8..782bfeb5d 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/requests.py +++ b/src/frontends/gamespy/protocols/chat/contracts/requests.py @@ -475,7 +475,7 @@ def parse(self): self.mode_operations.append( ModeOperationType.REMOVE_PRIVATE_CHANNEL_FLAG ) - case "-p": + case "+p": self.mode_operations.append( ModeOperationType.SET_PRIVATE_CHANNEL_FLAG ) @@ -495,12 +495,6 @@ def parse(self): self.mode_operations.append( ModeOperationType.REMOVE_CHANNEL_PASSWORD ) - case "+l": - self.channel_name = self._cmd_params[0] - self.limit_number = int(self._cmd_params[2]) - self.mode_operations.append( - ModeOperationType.REMOVE_CHANNEL_PASSWORD - ) case "+l": self.channel_name = self._cmd_params[0] diff --git a/src/frontends/gamespy/protocols/chat/contracts/results.py b/src/frontends/gamespy/protocols/chat/contracts/results.py index d31f924b2..c785293c4 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/results.py +++ b/src/frontends/gamespy/protocols/chat/contracts/results.py @@ -16,9 +16,6 @@ class GetKeyResult(ResultBase): values: list - - - class ListResult(ResultBase): class ListInfo(BaseModel): channel_name: str @@ -90,7 +87,7 @@ class GetChannelKeyResult(ResultBase): class GetCKeyResult(ResultBase): class GetCKeyInfos(BaseModel): nick_name: str - user_values: str + user_values: list infos: list[GetCKeyInfos] """ nick_name:str, user_values:str""" diff --git a/src/frontends/tests/gamespy/chat/mock_objects.py b/src/frontends/tests/gamespy/chat/mock_objects.py index 9f74f9acd..4d01444b8 100644 --- a/src/frontends/tests/gamespy/chat/mock_objects.py +++ b/src/frontends/tests/gamespy/chat/mock_objects.py @@ -26,7 +26,7 @@ def create_client() -> Client: create_mock_url(config, UserHandler, {"message": "ok"}) create_mock_url(config, CdKeyHandler, {"message": "ok"}) create_mock_url(config, GetCKeyHandler, GetCKeyResult.model_validate( - {"channel_name": "test", "infos": [{"nick_name": "test_nick", "user_values": "/data/key/value/data"}]}).model_dump()) + {"channel_name": "test", "infos": [{"nick_name": "test_nick", "user_values": ["data", "key", "value", "data"]}]}).model_dump()) create_mock_url(config, ModeHandler, ModeResult.model_validate( {"channel_name": "test", "channel_modes": "+n", "joiner_nick_name": "test_nick"}).model_dump()) create_mock_url(config, SetCKeyHandler, {"message": "ok"}) diff --git a/src/frontends/tests/gamespy/chat/request_tests.py b/src/frontends/tests/gamespy/chat/request_tests.py index 19d39a6e1..98ee74e00 100644 --- a/src/frontends/tests/gamespy/chat/request_tests.py +++ b/src/frontends/tests/gamespy/chat/request_tests.py @@ -100,7 +100,7 @@ def test_mode(self): request = ModeRequest(MODE_CHANNEL) request.parse() self.assertEqual( - request.mode_operations[0], ModeOperationType.REMOVE_CHANNEL_PASSWORD) + request.mode_operations[0], ModeOperationType.ADD_CHANNEL_USER_LIMITS) self.assertEqual(request.channel_name, "#GSP!room!test") self.assertEqual(request.mode_flag, "+l") self.assertEqual(request.limit_number, 2) diff --git a/src/frontends/tests/gamespy/presence_search_player/mock_objects.py b/src/frontends/tests/gamespy/presence_search_player/mock_objects.py index fdce425f9..6dd37432f 100644 --- a/src/frontends/tests/gamespy/presence_search_player/mock_objects.py +++ b/src/frontends/tests/gamespy/presence_search_player/mock_objects.py @@ -21,7 +21,7 @@ def create_client() -> Client: create_mock_url(config, CheckHandler, CheckResult.model_validate( {"profile_id": 0}).model_dump()) - create_mock_url(config, SearchHandler, SearchResult.model_validate({"result": [{"profile_id": 0, "nick": "spyguy", "uniquenick": "spyguy", + create_mock_url(config, SearchHandler, SearchResult.model_validate({"data": [{"profile_id": 0, "nick": "spyguy", "uniquenick": "spyguy", "email": "spyguy@unispy.org", "firstname": "spy", "lastname": "guy", "namespace_id": 0}]}).model_dump()) return cast(Client, conn._client) From fa48c0edac364ba067dfddce0d127e61f02842d0 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Thu, 13 Mar 2025 12:12:19 +0000 Subject: [PATCH 163/231] refactor(chat): added helper class --- .../protocols/gamespy/chat/abstractions.py | 7 + .../protocols/gamespy/chat/channel_user.py | 36 ----- src/backends/protocols/gamespy/chat/data.py | 16 +- .../protocols/gamespy/chat/handlers.py | 80 +++++++--- .../gamespy/chat/{channel.py => helper.py} | 139 +++++++++++++----- .../protocols/gamespy/chat/requests.py | 6 +- 6 files changed, 185 insertions(+), 99 deletions(-) delete mode 100644 src/backends/protocols/gamespy/chat/channel_user.py rename src/backends/protocols/gamespy/chat/{channel.py => helper.py} (51%) diff --git a/src/backends/protocols/gamespy/chat/abstractions.py b/src/backends/protocols/gamespy/chat/abstractions.py index b113e3746..808c30fcb 100644 --- a/src/backends/protocols/gamespy/chat/abstractions.py +++ b/src/backends/protocols/gamespy/chat/abstractions.py @@ -1,4 +1,5 @@ +from backends.library.abstractions.contracts import RequestBase from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import ChatChannelCaches, ChatChannelUserCaches, ChatUserCaches from backends.protocols.gamespy.chat.requests import ChannelRequestBase @@ -12,6 +13,12 @@ class ChannelHandlerBase(HandlerBase): _channel: ChatChannelCaches | None _channel_user: ChatChannelUserCaches | None + def __init__(self, request: RequestBase) -> None: + super().__init__(request) + self._user = None + self._channel = None + self._channel_user = None + def _get_user(self): self._user = data.get_user_cache_by_ip_port( self._request.client_ip, self._request.client_port) diff --git a/src/backends/protocols/gamespy/chat/channel_user.py b/src/backends/protocols/gamespy/chat/channel_user.py deleted file mode 100644 index 8b74361ee..000000000 --- a/src/backends/protocols/gamespy/chat/channel_user.py +++ /dev/null @@ -1,36 +0,0 @@ -# from uuid import UUID - -# from backends.protocols.gamespy.chat.managers import KeyValueManager -# from frontends.gamespy.protocols.chat.applications.client import Client -# from typing import TYPE_CHECKING - -# if TYPE_CHECKING: -# from frontends.gamespy.protocols.chat.aggregates.channel import Channel - - -# class ChannelUser: -# server_id: UUID -# is_voiceable: bool = True -# is_channel_operator: bool = False -# is_channel_creator: bool = False -# remote_ip: str -# remote_port: int -# client: Client -# kv_manager: KeyValueManager = KeyValueManager() -# channel: "Channel" - -# @property -# def modes(self): -# buffer = "" -# if self.is_channel_creator: -# buffer += "@" -# if self.is_voiceable: -# buffer += "+" -# return buffer - -# def __init__(self, client: Client, channel: "Channel") -> None: -# self.client = client -# self.channel = channel -# self.server_id = client.server_config.server_id -# self.remote_ip = client.connection.remote_ip -# self.remote_port = client.connection.remote_port diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index de906ed70..b8ee2543b 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -30,12 +30,12 @@ def nick_and_email_login(nick_name: str, email: str, password_hash: str) -> tupl result = PG_SESSION.query(Users.userid, Profiles.profileid, Users.emailverified, Users.banned)\ - .join(Profiles, (Users.userid == Profiles.userid))\ - .where( - Users.email == email, - Profiles.nick == nick_name, - Users.password == password_hash - ).first() + .join(Profiles, (Users.userid == Profiles.userid))\ + .where( + Users.email == email, + Profiles.nick == nick_name, + Users.password == password_hash + ).first() if TYPE_CHECKING: result = cast(tuple[int, int, bool, bool], result) if result is None: @@ -128,9 +128,9 @@ def get_channel_user_cache_by_name_and_ip_port(channel_name:str,ip:str,port:int) ChatChannelUserCaches.remote_port == port).first() return result -def get_channel_user_caches_by_name(channel_name:str)->list: +def get_channel_user_caches_by_name(channel_name:str)->list[ChatChannelUserCaches]: assert isinstance(channel_name,str) - result = PG_SESSION.query(ChatChannelUserCaches.key_values).where(ChatChannelUserCaches.channel_name == channel_name).all() + result:list[ChatChannelUserCaches] = PG_SESSION.query(ChatChannelUserCaches.key_values).where(ChatChannelUserCaches.channel_name == channel_name).all()#type:ignore return result def update_channel_time(channel:ChatChannelCaches): diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index 5015bb750..efaeb3785 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -4,11 +4,11 @@ from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatUserCaches, ChatChannelUserCaches from backends.protocols.gamespy.chat.abstractions import ChannelHandlerBase -from backends.protocols.gamespy.chat.channel import ChannelUserHelper +from backends.protocols.gamespy.chat.helper import ChannelHelper, ChannelUserHelper import backends.protocols.gamespy.chat.data as data from backends.protocols.gamespy.chat.managers import KeyValueManager from backends.protocols.gamespy.chat.requests import * -from frontends.gamespy.protocols.chat.aggregates.exceptions import ChatException, LoginFailedException, NickNameInUseException, NoSuchChannelException, NoSuchNickException +from frontends.gamespy.protocols.chat.aggregates.exceptions import BadChannelKeyException, ChatException, LoginFailedException, NickNameInUseException, NoSuchChannelException, NoSuchNickException from frontends.gamespy.protocols.chat.contracts.results import CryptResult, GetCKeyResult, GetKeyResult, ListResult, NickResult, WhoIsResult, WhoResult # region General @@ -267,27 +267,69 @@ async def _request_check(self) -> None: self._check_user() self._get_channel() - self._check_channel() - - self._get_channel_user() async def _data_operate(self) -> None: assert self._user is not None assert isinstance(self._user.nick_name, str) assert self._channel is not None assert isinstance(self._channel.channel_name, str) - - if self._channel_user is None: - cache = ChatChannelUserCaches( - nick_name=self._user.nick_name, - user_name=self._user.user_name, - channel_name=self._channel.channel_name, + if self._channel is None: + self._channel = ChannelHelper.create( + server_id=self._request.server_id, + channel_name=self._request.channel_name, + password=self._request.password, + game_name=self._request.game_name, + room_name="", + topic="", + key_values={}, update_time=datetime.now(), - is_voiceable=True, - is_channel_operator=False, - is_channel_creator=False, - remote_ip_address=self._user.remote_ip_address, - remote_port=self._user.remote_port, - key_values={}) - -# region Message + modes=[], + creator=self._user.nick_name, + group_id=0, + max_num_user=100 + ) + ChannelHelper.join(self._channel, self._user) + + +class KickHandler(ChannelHandlerBase): + _kickee: ChatChannelUserCaches | None + _request: KickRequest + + def __init__(self, request: RequestBase) -> None: + super().__init__(request) + self._kickee = None + + async def _request_check(self) -> None: + await super()._request_check() + assert isinstance(self._channel, ChatChannelCaches) + assert isinstance(self._channel.channel_name, str) + self._kickee = data.get_channel_user_cache_by_name( + self._channel.channel_name, self._request.kickee_nick_name) + if self._kickee is None: + raise BadChannelKeyException( + f"kickee is not a user of channel:{self._channel.channel_name}") + + async def _data_operate(self) -> None: + assert self._channel + assert self._channel_user + assert self._kickee + ChannelHelper.kick(self._channel, self._channel_user, self._kickee) + + +class ModeHandler(ChannelHandlerBase): + _request: ModeRequest + + async def _data_operate(self) -> None: + assert self._channel + assert self._channel_user + ChannelHelper.change_modes( + self._channel, self._channel_user, self._request) + + async def _result_construct(self) -> None: + raise NotImplementedError() + return await super()._result_construct() + + +class NamesHandler(ChannelHandlerBase): + + # region Message diff --git a/src/backends/protocols/gamespy/chat/channel.py b/src/backends/protocols/gamespy/chat/helper.py similarity index 51% rename from src/backends/protocols/gamespy/chat/channel.py rename to src/backends/protocols/gamespy/chat/helper.py index 6fb723cb7..cfc272f96 100644 --- a/src/backends/protocols/gamespy/chat/channel.py +++ b/src/backends/protocols/gamespy/chat/helper.py @@ -1,14 +1,22 @@ from datetime import datetime -from typing import TYPE_CHECKING +from enum import Enum +from typing import TYPE_CHECKING, cast from uuid import UUID from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatChannelUserCaches, ChatUserCaches import backends.protocols.gamespy.chat.data as data +from backends.protocols.gamespy.chat.requests import ModeRequest from protocols.chat.aggregates.enums import ModeOperationType -from protocols.chat.aggregates.exceptions import InviteOnlyChanException, NoSuchChannelException +from protocols.chat.aggregates.exceptions import BadChannelKeyException, BannedFromChanException, InviteOnlyChanException, NoSuchChannelException + + +class ChannelUserProperty(Enum): + CHANNEL_CREATOR = "channel_creator" + CHANNEL_OPERATOR = "channel_operator" class ChannelUserHelper: + @staticmethod def get_mode_string(user: ChatChannelUserCaches): assert isinstance(user.is_channel_operator, bool) @@ -21,29 +29,75 @@ def get_mode_string(user: ChatChannelUserCaches): return buffer - class ChannelHelper: @staticmethod def join(channel: ChatChannelCaches, user: ChatUserCaches) -> None: + + assert isinstance(channel, ChatChannelCaches) + assert isinstance(channel.modes, list) + assert isinstance(user.nick_name, str) + assert isinstance(channel.banned_nicks, list) # 1 check if is a invited channel # 1.1 check if user is in a invited list - assert isinstance(channel.modes, list) - # assert isinstance(channel.invited_nicks,list) + if ModeOperationType.SET_INVITED_ONLY in channel.modes: if user.nick_name not in channel.invited_nicks: raise InviteOnlyChanException( f"You can only join channel: {channel.channel_name} when you are in invite list") # 2 check if user is in ban list, if it is user can not join - raise NotImplementedError() + if user.nick_name in channel.banned_nicks: + raise BannedFromChanException( + "can not join channel, because you are in ban list") + if channel.creator == user.nick_name: # type:ignore + is_creator = True + else: + is_creator = False + chan_user = ChatChannelUserCaches( + nick_name=user.nick_name, + user_name=user.user_name, + channel_name=channel.channel_name, + update_time=datetime.now(), + is_voiceable=True, + is_channel_operator=False, + is_channel_creator=is_creator, + remote_ip_address=user.remote_ip_address, + remote_port=user.remote_port) + PG_SESSION.add(chan_user) + PG_SESSION.commit() @staticmethod - def quit(channel: ChatChannelCaches, user: ChatUserCaches) -> None: - raise NotImplementedError() + def quit(channel: ChatChannelCaches, quiter: ChatChannelUserCaches) -> None: + assert isinstance(quiter, ChatChannelUserCaches) + assert isinstance(channel, ChatChannelCaches) + assert isinstance(quiter.channel_name, str) + assert isinstance(channel.channel_name, str) + + if quiter.channel_name != channel.channel_name: # type:ignore + print("user is not in channel, so can not quit") + return + PG_SESSION.delete(quiter) + PG_SESSION.commit() @staticmethod - def kick(channel: ChatChannelCaches, kicker: ChatUserCaches, kickee: ChatUserCaches) -> None: - raise NotImplementedError() + def kick(channel: ChatChannelCaches, kicker: ChatChannelUserCaches, kickee: ChatChannelUserCaches) -> None: + assert isinstance(channel, ChatChannelCaches) + assert isinstance(kicker, ChatChannelUserCaches) + assert isinstance(kickee, ChatChannelUserCaches) + assert isinstance(kicker.channel_name, str) + assert isinstance(channel.channel_name, str) + assert isinstance(kickee.channel_name, str) + if kicker.channel_name != channel.channel_name: # type:ignore + raise BadChannelKeyException( + f"kicker is not in channel: {channel.channel_name}") + if kickee.channel_name != channel.channel_name: # type:ignore + raise BadChannelKeyException( + f"kickee is not in channel: {channel.channel_name}") + if not kicker.is_channel_operator: # type:ignore + raise BadChannelKeyException( + "kick failed, kicker is not channel operator") + PG_SESSION.delete(kickee) + PG_SESSION.commit() @staticmethod def invite(channel: ChatChannelCaches, inviter: ChatChannelUserCaches, invitee: ChatUserCaches) -> None: @@ -67,7 +121,7 @@ def create(server_id: UUID, max_num_user: int, key_values: dict, update_time: datetime, - modes: str, + modes: list, creator: str | None = None) -> ChatChannelCaches: # check whether if channel exist, if not user is creator is_exist = data.is_channel_exist(channel_name, game_name) @@ -92,13 +146,17 @@ def create(server_id: UUID, return cache @staticmethod - def change_modes(channel: ChatChannelCaches, changer: ChatChannelUserCaches, modes: list[ModeOperationType], args: list): + def change_modes(channel: ChatChannelCaches, + changer: ChatChannelUserCaches, + request: ModeRequest, + ): assert isinstance(channel, ChatChannelCaches) assert isinstance(changer, ChatChannelUserCaches) assert isinstance(channel.modes, list) - assert all(isinstance(m, ModeOperationType) for m in modes) + assert all(isinstance(m, ModeOperationType) + for m in request.mode_operations) - for flag in modes: + for flag in request.mode_operations: if flag not in channel.modes: channel.modes.append(flag) match flag: @@ -113,39 +171,54 @@ def change_modes(channel: ChatChannelCaches, changer: ChatChannelUserCaches, mod channel.modes.remove( ModeOperationType.ENABLE_USER_QUIET_FLAG) case ModeOperationType.ADD_CHANNEL_PASSWORD: - assert isinstance(args[0], str) + assert isinstance(request.password, str) if changer.is_channel_operator: # type:ignore - channel.password = args[0] # type:ignore + channel.password = request.password # type:ignore case ModeOperationType.REMOVE_CHANNEL_PASSWORD: if changer.is_channel_operator: # type:ignore channel.password = "" # type:ignore case ModeOperationType.ADD_CHANNEL_USER_LIMITS: - channel.max_num_user = args[0] # type: ignore + channel.max_num_user = request.limit_number # type: ignore case ModeOperationType.REMOVE_CHANNEL_USER_LIMITS: channel.max_num_user = 200 # type: ignore case ModeOperationType.ADD_BAN_ON_USER: assert isinstance(channel.banned_nicks, list) - if args[0] not in list(channel.banned_nicks): # type: ignore - channel.banned_nicks.append(args[0]) + # type: ignore + if request.nick_name not in list(channel.banned_nicks): + channel.banned_nicks.append(request.nick_name) case ModeOperationType.REMOVE_BAN_ON_USER: assert isinstance(channel.banned_nicks, list) - if args[0] in list(channel.banned_nicks): - channel.banned_nicks.remove(args[0]) + if request.nick_name in list(channel.banned_nicks): + channel.banned_nicks.remove(request.nick_name) case ModeOperationType.ADD_CHANNEL_OPERATOR: - assert isinstance(args[0], ChatChannelUserCaches) - user: ChatChannelUserCaches = args[0] - user.is_channel_operator = True # type: ignore + u = data.get_channel_user_cache_by_name( + request.channel_name, request.nick_name) + if u is None: + raise BadChannelKeyException( + f"no user found with nick name:{request.nick_name}") + + u.is_channel_operator = True # type: ignore + PG_SESSION.commit() case ModeOperationType.REMOVE_CHANNEL_OPERATOR: - assert isinstance(args[0], ChatChannelUserCaches) - user: ChatChannelUserCaches = args[0] - user.is_channel_operator = False # type: ignore + u = data.get_channel_user_cache_by_name( + request.channel_name, request.nick_name) + u.is_channel_operator = False # type: ignore + PG_SESSION.commit() + case ModeOperationType.ENABLE_USER_VOICE_PERMISSION: - assert isinstance(args[0], ChatChannelUserCaches) - user: ChatChannelUserCaches = args[0] - user.is_voiceable = True # type: ignore + u = data.get_channel_user_cache_by_name( + request.channel_name, request.nick_name) + u.is_voiceable = True # type: ignore case ModeOperationType.DISABLE_USER_VOICE_PERMISSION: - assert isinstance(args[0], ChatChannelUserCaches) - user: ChatChannelUserCaches = args[0] - user.is_voiceable = False # type: ignore + u = data.get_channel_user_cache_by_name( + request.channel_name, request.nick_name) + u.is_voiceable = False # type: ignore PG_SESSION.commit() + + @staticmethod + def get_all_user_nick_string(channel: ChatChannelCaches): + assert isinstance(channel, ChatChannelCaches) + assert isinstance(channel.channel_name, str) + users = data.get_channel_user_caches_by_name(channel.channel_name) + \ No newline at end of file diff --git a/src/backends/protocols/gamespy/chat/requests.py b/src/backends/protocols/gamespy/chat/requests.py index f0ba7100c..bff4dab47 100644 --- a/src/backends/protocols/gamespy/chat/requests.py +++ b/src/backends/protocols/gamespy/chat/requests.py @@ -1,5 +1,5 @@ import backends.library.abstractions.contracts as lib -from frontends.gamespy.protocols.chat.aggregates.enums import GetKeyRequestType, LoginRequestType, MessageType, ModeRequestType, TopicRequestType, WhoRequestType +from frontends.gamespy.protocols.chat.aggregates.enums import GetKeyRequestType, LoginRequestType, MessageType, ModeOperationType, ModeRequestType, TopicRequestType, WhoRequestType class RequestBase(lib.RequestBase): @@ -24,7 +24,7 @@ class GetUdpRelayRequest(RequestBase): class InviteRequest(RequestBase): channel_name: str - nick_name:str + nick_name: str class ListLimitRequest(RequestBase): @@ -138,7 +138,7 @@ class KickRequest(ChannelRequestBase): class ModeRequest(ChannelRequestBase): request_type: ModeRequestType - mode_operations: list = [] + mode_operations: list[ModeOperationType] nick_name: str user_name: str limit_number: int From cd1dc9a74134c3174b48d08e42e7945a9f17ff8a Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 14 Mar 2025 12:47:16 +0000 Subject: [PATCH 164/231] refactor: added channel handler functions --- .../protocols/gamespy/chat/abstractions.py | 29 ++++- .../protocols/gamespy/chat/handlers.py | 102 +++++++++++++++++- src/backends/protocols/gamespy/chat/helper.py | 21 +++- 3 files changed, 146 insertions(+), 6 deletions(-) diff --git a/src/backends/protocols/gamespy/chat/abstractions.py b/src/backends/protocols/gamespy/chat/abstractions.py index 808c30fcb..6be881549 100644 --- a/src/backends/protocols/gamespy/chat/abstractions.py +++ b/src/backends/protocols/gamespy/chat/abstractions.py @@ -1,8 +1,9 @@ -from backends.library.abstractions.contracts import RequestBase +from backends.library.abstractions.contracts import ErrorResponse, RequestBase from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import ChatChannelCaches, ChatChannelUserCaches, ChatUserCaches from backends.protocols.gamespy.chat.requests import ChannelRequestBase +from library.exceptions.general import UniSpyException from protocols.chat.aggregates.exceptions import NoSuchChannelException, NoSuchNickException import backends.protocols.gamespy.chat.data as data @@ -12,12 +13,14 @@ class ChannelHandlerBase(HandlerBase): _user: ChatUserCaches | None _channel: ChatChannelCaches | None _channel_user: ChatChannelUserCaches | None + _is_broadcast: bool def __init__(self, request: RequestBase) -> None: super().__init__(request) self._user = None self._channel = None self._channel_user = None + self._is_broadcast = False def _get_user(self): self._user = data.get_user_cache_by_ip_port( @@ -57,3 +60,27 @@ async def _request_check(self) -> None: self._get_channel_user() self._check_channel_user() + + async def _boradcast(self) -> None: + # todo boradcast message here + raise NotImplementedError() + pass + + async def handle(self) -> None: + try: + await self._request_check() + await self._data_operate() + await self._result_construct() + await self._response_construct() + + await self._boradcast() + except UniSpyException as ex: + self.logger.error(ex.message) + self._response = ErrorResponse(message=ex.message) + except Exception as ex: + self.logger.error(ex) + self._response = ErrorResponse(message=str(ex)) + + +class MessageHandlerBase(ChannelHandlerBase): + pass diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index efaeb3785..0727d9d60 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -9,7 +9,7 @@ from backends.protocols.gamespy.chat.managers import KeyValueManager from backends.protocols.gamespy.chat.requests import * from frontends.gamespy.protocols.chat.aggregates.exceptions import BadChannelKeyException, ChatException, LoginFailedException, NickNameInUseException, NoSuchChannelException, NoSuchNickException -from frontends.gamespy.protocols.chat.contracts.results import CryptResult, GetCKeyResult, GetKeyResult, ListResult, NickResult, WhoIsResult, WhoResult +from frontends.gamespy.protocols.chat.contracts.results import CryptResult, GetCKeyResult, GetKeyResult, ListResult, NamesResult, NickResult, PartResult, SetChannelKeyResult, TopicResult, WhoIsResult, WhoResult # region General @@ -331,5 +331,101 @@ async def _result_construct(self) -> None: class NamesHandler(ChannelHandlerBase): - - # region Message + _request: NamesRequest + + async def _request_check(self) -> None: + self._get_user() + self._check_user() + + self._get_channel() + self._check_channel() + + async def _data_operate(self) -> None: + assert self._channel + self._data = ChannelHelper.get_all_user_nick_string(self._channel) + + async def _result_construct(self) -> None: + assert self._user + assert isinstance(self._user.nick_name, str) + self._result = NamesResult( + all_channel_user_nicks=self._data, + channel_name=self._request.channel_name, + requester_nick_name=self._user.nick_name) + + +class PartHandler(ChannelHandlerBase): + _request: PartRequest + + async def _data_operate(self) -> None: + assert self._channel + assert self._channel_user + ChannelHelper.quit(self._channel, self._channel_user) + + async def _result_construct(self) -> None: + assert self._channel_user + assert self._channel + assert isinstance(self._channel_user.is_channel_creator, bool) + assert isinstance(self._channel_user.is_channel_operator, bool) + assert isinstance(self._channel.channel_name, str) + + irc = ChannelUserHelper.get_user_irc_prefix(self._channel_user) + self._result = PartResult(leaver_irc_prefix=irc, is_channel_creator=self._channel_user.is_channel_creator, + channel_name=self._channel.channel_name) + + +class SetChannelKeyHandler(ChannelHandlerBase): + _request: SetChannelKeyRequest + + async def _request_check(self) -> None: + await super()._request_check() + assert self._channel_user + assert isinstance(self._channel_user.is_channel_operator, bool) + if self._channel_user.is_channel_operator: + self._channel.key_values = self._request.key_values # type:ignore + data.db_commit() + + async def _result_construct(self) -> None: + assert self._channel_user + irc = ChannelUserHelper.get_user_irc_prefix(self._channel_user) + self._result = SetChannelKeyResult( + channel_user_irc_prefix=irc, channel_name=self._request.channel_name) + + +class SetCkeyHandler(ChannelHandlerBase): + """ + todo check if set channel_user or user keyvalue or set for other channeluser keyvalue + """ + _request: SetCKeyRequest + + async def _data_operate(self) -> None: + self._channel_user.key_values = self._request.key_values # type:ignore + data.db_commit() + self._is_broadcast = True + + async def _result_construct(self) -> None: + # todo think how to broadcast message + raise NotImplementedError() + + +class TopicHandler(ChannelHandlerBase): + _request: TopicRequest + + async def _data_operate(self) -> None: + assert self._channel_user + assert isinstance(self._channel_user.is_channel_operator, bool) + if self._request.request_type is TopicRequestType.GET_CHANNEL_TOPIC: + self._data: str = self._channel.topic # type:ignore + else: + if not self._channel_user.is_channel_operator: + raise NoSuchChannelException( + "inorder to set channel topic, you have to be channel operator") + self._channel.topic = self._request.channel_topic # type:ignore + self._data: str = self._request.channel_topic + + async def _result_construct(self) -> None: + self._result = TopicResult( + channel_name=self._request.channel_name, channel_topic=self._data) + +# region Message + +# class AtmHandler() \ No newline at end of file diff --git a/src/backends/protocols/gamespy/chat/helper.py b/src/backends/protocols/gamespy/chat/helper.py index cfc272f96..35744674c 100644 --- a/src/backends/protocols/gamespy/chat/helper.py +++ b/src/backends/protocols/gamespy/chat/helper.py @@ -6,6 +6,7 @@ from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatChannelUserCaches, ChatUserCaches import backends.protocols.gamespy.chat.data as data from backends.protocols.gamespy.chat.requests import ModeRequest +from protocols.chat.abstractions.contract import SERVER_DOMAIN from protocols.chat.aggregates.enums import ModeOperationType from protocols.chat.aggregates.exceptions import BadChannelKeyException, BannedFromChanException, InviteOnlyChanException, NoSuchChannelException @@ -28,6 +29,11 @@ def get_mode_string(user: ChatChannelUserCaches): buffer += "+" return buffer + @staticmethod + def get_user_irc_prefix(user: ChatChannelUserCaches): + irc = f"{user.nick_name}!{user.user_name}@{SERVER_DOMAIN}" + return irc + class ChannelHelper: @staticmethod @@ -217,8 +223,19 @@ def change_modes(channel: ChatChannelCaches, PG_SESSION.commit() @staticmethod - def get_all_user_nick_string(channel: ChatChannelCaches): + def get_all_user_nick_string(channel: ChatChannelCaches) -> str: assert isinstance(channel, ChatChannelCaches) assert isinstance(channel.channel_name, str) users = data.get_channel_user_caches_by_name(channel.channel_name) - \ No newline at end of file + nicks = "" + for user in users: + assert isinstance(user.is_channel_creator, bool) + assert isinstance(user.nick_name, str) + if user.is_channel_creator: + nicks += f"@{user.nick_name}" + else: + nicks += user.nick_name + # use space as seperator + if user != users[-1]: + nicks += " " + return nicks From 4457e15bab86fdeefd5626e7e911cba1e362212c Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 14 Mar 2025 20:48:45 +0800 Subject: [PATCH 165/231] Update README.md Added docker network creation cmd --- .github/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/README.md b/.github/README.md index 4f850c76c..74d205c58 100644 --- a/.github/README.md +++ b/.github/README.md @@ -31,6 +31,7 @@ This project is licensed under the [GNU Affero General Public License v3.0](../L ## How to run +* RUN ```docker network create unispy``` to setup a network for unispy * Run ```docker compose -f docker-compose-unispy-env.yml up -d``` to setup postgresql and redis * Run ```export UNISPY_CONFIG=''``` to setup env config file path, remember replace symbol `````` with config file path * Open with vscode: From 5b8ae5713f24f830251cb01392453669fc1412ca Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Fri, 14 Mar 2025 20:49:02 +0800 Subject: [PATCH 166/231] Update README.md --- .github/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/README.md b/.github/README.md index 74d205c58..1f019c1e5 100644 --- a/.github/README.md +++ b/.github/README.md @@ -31,7 +31,7 @@ This project is licensed under the [GNU Affero General Public License v3.0](../L ## How to run -* RUN ```docker network create unispy``` to setup a network for unispy +* Run ```docker network create unispy``` to setup a network for unispy * Run ```docker compose -f docker-compose-unispy-env.yml up -d``` to setup postgresql and redis * Run ```export UNISPY_CONFIG=''``` to setup env config file path, remember replace symbol `````` with config file path * Open with vscode: From a9104037d316fd96c8b868c8ab7613d947e430ff Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 18 Mar 2025 02:48:03 +0000 Subject: [PATCH 167/231] fix database creation error --- common/UniSpy_pg.sql | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 613d59dad..d6373e7e6 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -116,7 +116,7 @@ ALTER SEQUENCE unispy.blocked_blockid_seq OWNED BY unispy.blocked.blockid; CREATE TABLE unispy.chat_user_caches ( server_id uuid NOT NULL, - nick_name character varying primary key NOT NULL UNIQUE , + nick_name character varying PRIMARY KEY NOT NULL, game_name character varying, user_name character varying, remote_ip_address inet NOT NULL, @@ -130,7 +130,7 @@ CREATE TABLE unispy.chat_user_caches ( -- CREATE TABLE unispy.chat_channel_caches ( - channel_name character varying NOT NULL UNIQUE, + channel_name character varying NOT NULL PRIMARY KEY, server_id uuid NOT NULL, game_name character varying NOT NULL, room_name character varying NOT NULL, @@ -157,8 +157,8 @@ ALTER TABLE unispy.chat_user_caches OWNER TO unispy; -- CREATE TABLE unispy.chat_channel_user_caches ( - nick_name character varying NOT NULL - user_name character varying NOT NULL + nick_name character varying NOT NULL, + user_name character varying NOT NULL, channel_name character varying NOT NULL, server_id uuid NOT NULL, update_time timestamp without time zone NOT NULL, @@ -257,7 +257,7 @@ ALTER TABLE unispy.game_server_caches OWNER TO unispy; -- CREATE TABLE unispy.games ( - gameid integer NOT NULL, + gameid integer PRIMARY KEY NOT NULL , gamename character varying NOT NULL, secretkey character varying, description character varying(4095) NOT NULL, @@ -5496,9 +5496,9 @@ ALTER TABLE ONLY unispy.profiles -- Name: chat_user_caches chat_user_caches_channel_name_fkey; Type: FK CONSTRAINT; Schema: unispy; Owner: unispy -- ALTER TABLE ONLY unispy.chat_channel_user_caches - ADD CONSTRAINT chat_channel_user_caches_channel_name_fkey FOREIGN KEY(channel_name) unispy.chat_channel_caches(channel_name) - ADD CONSTRAINT chat_channel_user_caches_nick_name_fkey FOREIGN KEY(nick_name) unispy.chat_user_caches(nick_name) - -PostgreSQL database dump complete + ADD CONSTRAINT chat_channel_user_caches_channel_name_fkey FOREIGN KEY(channel_name) REFERENCES unispy.chat_channel_caches(channel_name); +ALTER TABLE ONLY unispy.chat_channel_user_caches + ADD CONSTRAINT chat_channel_user_caches_nick_name_fkey FOREIGN KEY(nick_name) REFERENCES unispy.chat_user_caches(nick_name); +-- PostgreSQL database dump complete \ No newline at end of file From 16dca28469931d03f0287abecc9be1a22b94a0c3 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 18 Mar 2025 02:48:53 +0000 Subject: [PATCH 168/231] update contribute file --- src/Contribute.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Contribute.md b/src/Contribute.md index 0feece733..002848b89 100644 --- a/src/Contribute.md +++ b/src/Contribute.md @@ -2,7 +2,7 @@ class BaseClass: """ - We use class static member to type hint the class instance member + We use class static member only to type hint the class instance member Do not initialize the class static member """ _property1:type1 @@ -10,7 +10,9 @@ class BaseClass: def __init__(self): + # if the property do not have default value it must be initialized as None + self._property1 = None # In the base class we have to check whether the _property has been initialized, if not we init it if not hasattr(self,"_property1"): - self._property1 = value1 + self._property2 = value2 ``` \ No newline at end of file From 766dc289dfae5d1af496abb39f54e0dc2a5c143f Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Tue, 18 Mar 2025 09:02:50 +0000 Subject: [PATCH 169/231] fix data fetch tests --- src/backends/library/brockers/chat.py | 23 +++++++++++ src/backends/library/database/pg_orm.py | 1 - src/backends/library/database/redis.py | 11 ----- .../protocols/gamespy/chat/abstractions.py | 2 +- .../protocols/gamespy/chat/handlers.py | 20 +++++++-- .../protocols/gamespy/chat/requests.py | 4 +- src/backends/routers/gamespy/chat.py | 41 ++++--------------- .../data_fetch_tests.py | 2 +- .../gamespy/query_report/data_fetch_tests.py | 10 ++++- .../gamespy/library/network/brockers.py | 3 +- src/redis_test.py | 18 ++++++++ 11 files changed, 80 insertions(+), 55 deletions(-) create mode 100644 src/backends/library/brockers/chat.py delete mode 100644 src/backends/library/database/redis.py create mode 100644 src/redis_test.py diff --git a/src/backends/library/brockers/chat.py b/src/backends/library/brockers/chat.py new file mode 100644 index 000000000..f88fbba17 --- /dev/null +++ b/src/backends/library/brockers/chat.py @@ -0,0 +1,23 @@ + + +from library.network.brockers import RedisBrocker +from fastapi import WebSocket +# init in fast api routers +REDIS_BROCKER = None + +FRONTENDS: dict[str, WebSocket] = {} +""" +{"client ip and port" : WebSocket} +store the channel name and server websockets +""" +CHANNELS: dict[str, list[WebSocket]] = {} +""" +{"channel_name" : "list of WebSocket"} +store the frontends server websockets +""" + + +async def channel_publish(channel_name: str, message: dict): + if channel_name in CHANNELS: + for client in CHANNELS[channel_name]: + await client.send(message) diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 33c3e7b59..1ce8751ee 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -242,7 +242,6 @@ class ChatChannelCaches(Base): key_values = Column(JSONB, default={}) invited_nicks = Column(JSONB, default=[]) update_time = Column(DateTime, nullable=False) - creator = Column(String, nullable=True) modes = Column(JSONB, default=[]) banned_nicks = Column(JSONB, default=[]) diff --git a/src/backends/library/database/redis.py b/src/backends/library/database/redis.py deleted file mode 100644 index fc6c78d9b..000000000 --- a/src/backends/library/database/redis.py +++ /dev/null @@ -1,11 +0,0 @@ -import redis - -from frontends.gamespy.library.configs import CONFIG - - -# SESSION = redis.Redis.from_url(CONFIG.redis.url) - -client = redis.from_url(CONFIG.redis.url) -client.set("hello", "hi") -data = client.get("hello") -pass diff --git a/src/backends/protocols/gamespy/chat/abstractions.py b/src/backends/protocols/gamespy/chat/abstractions.py index 6be881549..8ff8d917c 100644 --- a/src/backends/protocols/gamespy/chat/abstractions.py +++ b/src/backends/protocols/gamespy/chat/abstractions.py @@ -64,7 +64,7 @@ async def _request_check(self) -> None: async def _boradcast(self) -> None: # todo boradcast message here raise NotImplementedError() - pass + async def handle(self) -> None: try: diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index 0727d9d60..234b0dabf 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -3,7 +3,7 @@ from backends.library.abstractions.contracts import RequestBase from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatUserCaches, ChatChannelUserCaches -from backends.protocols.gamespy.chat.abstractions import ChannelHandlerBase +from backends.protocols.gamespy.chat.abstractions import ChannelHandlerBase, MessageHandlerBase from backends.protocols.gamespy.chat.helper import ChannelHelper, ChannelUserHelper import backends.protocols.gamespy.chat.data as data from backends.protocols.gamespy.chat.managers import KeyValueManager @@ -425,7 +425,21 @@ async def _data_operate(self) -> None: async def _result_construct(self) -> None: self._result = TopicResult( channel_name=self._request.channel_name, channel_topic=self._data) - + # region Message -# class AtmHandler() \ No newline at end of file + +class AtmHandler(MessageHandlerBase): + _request: AtmRequest + + +class UtmHandler(MessageHandlerBase): + _request: UtmRequest + + +class NoticeHandler(MessageHandlerBase): + _request: NoticeRequest + + +class PrivateHandler(MessageHandlerBase): + _request: NoticeRequest diff --git a/src/backends/protocols/gamespy/chat/requests.py b/src/backends/protocols/gamespy/chat/requests.py index bff4dab47..fd51ca2c0 100644 --- a/src/backends/protocols/gamespy/chat/requests.py +++ b/src/backends/protocols/gamespy/chat/requests.py @@ -184,7 +184,7 @@ class MessageRequestBase(ChannelRequestBase): # region Message -class ATMRequest(MessageRequestBase): +class AtmRequest(MessageRequestBase): pass @@ -196,5 +196,5 @@ class PrivateRequest(MessageRequestBase): pass -class UTMRequest(MessageRequestBase): +class UtmRequest(MessageRequestBase): pass diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index c92e37fcb..9671ad846 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -1,33 +1,14 @@ import json from typing import Optional from backends.protocols.gamespy.chat.handlers import CdKeyHandler, GetKeyHandler, GetUdpRelayHandler, InviteHandler -from backends.protocols.gamespy.chat.requests import (ATMRequest, CdkeyRequest, GetCKeyRequest, GetChannelKeyRequest, GetKeyRequest, GetUdpRelayRequest, InviteRequest, JoinRequest, KickRequest, - ListRequest, LoginRequest, ModeRequest, NamesRequest, NickRequest, NoticeRequest, PartRequest, PrivateRequest, QuitRequest, SetChannelKeyRequest, SetGroupRequest, SetKeyRequest, TopicRequest, UTMRequest, UserIPRequest, UserRequest, WhoIsRequest, WhoRequest) +from backends.protocols.gamespy.chat.requests import AtmRequest, CdkeyRequest, GetCKeyRequest, GetChannelKeyRequest, GetKeyRequest, GetUdpRelayRequest, InviteRequest, JoinRequest, KickRequest, ListRequest, LoginRequest, ModeRequest, NamesRequest, NickRequest, NoticeRequest, PartRequest, PrivateRequest, QuitRequest, SetChannelKeyRequest, SetGroupRequest, SetKeyRequest, TopicRequest, UtmRequest, UserIPRequest, UserRequest, WhoIsRequest, WhoRequest from backends.urls import CHAT from fastapi import APIRouter, FastAPI, WebSocket, WebSocketDisconnect -from frontends.gamespy.library.configs import ServerConfig from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage +from backends.library.brockers.chat import CHANNELS, FRONTENDS router = APIRouter() -channels: dict[str, list[WebSocket]] = {"test": []} -""" -{"channel_name" : "list of WebSocket"} -""" -clients: dict[str, WebSocket] = {} -""" -{"client ip and port" : WebSocket} -""" - - -@router.post(f"{CHAT}/add_channel") -def add_channel(channel_name: str, config: ServerConfig): - # first validate the server_id server_ip etc. info - # if server is valid we initialize the channel - - # we initialize the channel - if channel_name not in channels: - channels[channel_name] = [] def check_request(request: str) -> Optional[BrockerMessage]: @@ -41,24 +22,20 @@ def check_request(request: str) -> Optional[BrockerMessage]: return ch_msg -async def multicast_message(ws: WebSocket): - pass - - @router.websocket(f"{CHAT}/Channel") async def websocket_endpoint(ws: WebSocket): await ws.accept() if isinstance(ws, WebSocket) and ws.client is not None: client_key = f"{ws.client.host}:{ws.client.port}" - clients[client_key] = ws + FRONTENDS[client_key] = ws try: while True: request = await ws.receive_text() msg = check_request(request) if msg is None: return - channels[msg.channel_name].append(ws) - channel_clients: list[WebSocket] = channels[msg.channel_name] + CHANNELS[msg.channel_name].append(ws) + channel_clients: list[WebSocket] = CHANNELS[msg.channel_name] for client in channel_clients: # we do not send data to the publisher @@ -69,9 +46,9 @@ async def websocket_endpoint(ws: WebSocket): except WebSocketDisconnect: if ws.client is not None: client_key = f"{ws.client.host}:{ws.client.port}" - del clients[client_key] + del FRONTENDS[client_key] if msg is not None: - channels[msg.channel_name].remove(ws) + CHANNELS[msg.channel_name].remove(ws) print("Client disconnected") # region General @@ -205,7 +182,7 @@ async def topic(request: TopicRequest): @router.post(f"{CHAT}/ATMHandler") -async def atm(request: ATMRequest): +async def atm(request: AtmRequest): pass @@ -220,7 +197,7 @@ async def private(request: PrivateRequest): @router.post(f"{CHAT}/UTMHandler") -async def utm(request: UTMRequest): +async def utm(request: UtmRequest): pass diff --git a/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py b/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py index e6f0ffce1..c2ab4bdec 100644 --- a/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py +++ b/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py @@ -5,7 +5,7 @@ class DataFetchTests(TestCase): - def test_verify_email(self): + def test_verify_email(self) -> None: result1 = data.verify_email("spyguy@unispy.net") self.assertFalse(result1) result2 = data.verify_email("spyguy@gamespy.com") diff --git a/src/backends/tests/gamespy/query_report/data_fetch_tests.py b/src/backends/tests/gamespy/query_report/data_fetch_tests.py index 67aa2cdb6..8fdb2460a 100644 --- a/src/backends/tests/gamespy/query_report/data_fetch_tests.py +++ b/src/backends/tests/gamespy/query_report/data_fetch_tests.py @@ -13,8 +13,14 @@ def test_get_all_groups(self): self.assertIsInstance(data.PEER_GROUP_LIST, dict) def test_get_peer_staging_channels(self): - cache = ChatChannelCaches(channel_name="#GSP!unispy_test_game_name!*", server_id="b6480a17-5e3d-4da0-aeec-c421620bff71", game_name="unispy_test_game_name", - room_name="unispy_test_room_name", group_id=0, max_num_user=100, key_values={}, invited_nicks={}, update_time=datetime.now(timezone.utc)) + cache = ChatChannelCaches( + channel_name="#GSP!unispy_test_game_name!*", + server_id="b6480a17-5e3d-4da0-aeec-c421620bff71", + game_name="unispy_test_game_name", + room_name="unispy_test_room_name", + group_id=0, + max_num_user=100, + update_time=datetime.now(timezone.utc)) PG_SESSION.add(cache) PG_SESSION.commit() self.assertRaises( diff --git a/src/frontends/gamespy/library/network/brockers.py b/src/frontends/gamespy/library/network/brockers.py index bc9d5a4f6..a04591f15 100644 --- a/src/frontends/gamespy/library/network/brockers.py +++ b/src/frontends/gamespy/library/network/brockers.py @@ -1,8 +1,7 @@ -import asyncio import threading from typing import Optional -import websocket from redis import Redis +import websocket from frontends.gamespy.library.abstractions.brocker import BrockerBase from redis.client import PubSub diff --git a/src/redis_test.py b/src/redis_test.py new file mode 100644 index 000000000..f3999c142 --- /dev/null +++ b/src/redis_test.py @@ -0,0 +1,18 @@ + +# import redis +# from frontends.gamespy.library.configs import CONFIG + + +# # SESSION = redis.Redis.from_url(CONFIG.redis.url) + +# client = redis.from_url(CONFIG.redis.url) +# pubsub = client.pubsub() +# pubsub.subscribe("test") + +# for message in pubsub.listen(): +# if message['type'] == 'message': +# print(f"Received: {message['data'].decode('utf-8')}") + +# client.set("hello", "hi") +# data = client.get("hello") +# pass From 282c330b41302b56845c34793b8abafee588c789 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sun, 30 Mar 2025 11:46:26 +0000 Subject: [PATCH 170/231] Fix pip package conflictions --- src/requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/requirements.txt b/src/requirements.txt index 0bb497d6f..6e23d0348 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -12,5 +12,5 @@ redis == 5.2.0 websocket-client ==1.8.0 uvicorn == 0.32.0 prettytable == 3.11.0 -responses == 0.25.6 -pydantic ==2.10.6 \ No newline at end of file +responses == 0.25.3 +pydantic == 2.9.2 \ No newline at end of file From a4fa82b26ba464f2151e32dd9ee974d0146e10a0 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sun, 30 Mar 2025 12:28:21 +0000 Subject: [PATCH 171/231] Update test case --- src/backends/library/database/pg_orm.py | 2 +- .../{handler_test.py => handler_tests.py} | 14 +++-- .../gamespy/query_report/integration_tests.py | 3 -- .../gamespy/library/abstractions/handler.py | 2 +- .../gamespy/query_report/request_tests.py | 53 +++++++++++-------- 5 files changed, 43 insertions(+), 31 deletions(-) rename src/backends/tests/gamespy/query_report/{handler_test.py => handler_tests.py} (83%) delete mode 100644 src/backends/tests/gamespy/query_report/integration_tests.py diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 1ce8751ee..6a83a5083 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -297,7 +297,7 @@ class GameServerCaches(Base): def connect_to_db() -> Session: ENGINE = create_engine(CONFIG.postgresql.url) session = sessionmaker(bind=ENGINE)() - Base.metadata.create_all(ENGINE) + # Base.metadata.create_all(ENGINE) with ENGINE.connect() as conn: conn.execute(text("SELECT 1")).first() return session diff --git a/src/backends/tests/gamespy/query_report/handler_test.py b/src/backends/tests/gamespy/query_report/handler_tests.py similarity index 83% rename from src/backends/tests/gamespy/query_report/handler_test.py rename to src/backends/tests/gamespy/query_report/handler_tests.py index 3f5ca569b..32b71d2c4 100644 --- a/src/backends/tests/gamespy/query_report/handler_test.py +++ b/src/backends/tests/gamespy/query_report/handler_tests.py @@ -1,10 +1,10 @@ import unittest -from backends.protocols.gamespy.query_report.handlers import Heartbeathandler -from backends.protocols.gamespy.query_report.requests import HeartBeatRequest +from backends.protocols.gamespy.query_report.handlers import Heartbeathandler, AvaliableHandler +from backends.protocols.gamespy.query_report.requests import HeartBeatRequest, AvaliableRequest -class ApiTests(unittest.IsolatedAsyncioTestCase): +class HandlerTests(unittest.IsolatedAsyncioTestCase): async def test_heartbeat(self): request = {"server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "raw_request": "\\u0003\\\\xe5\\\\xcfaZlocalip0\\u0000172.19.0.5\\u0000localport\\u000011111\\u0000natneg\\u00001\\u0000statechanged\\u00003\\u0000gamename\\u0000gmtest\\u0000hostname\\u0000GameSpy QR2 Sample\\u0000gamever\\u00002.00\\u0000hostport\\u000025000\\u0000mapname\\u0000gmtmap1\\u0000gametype\\u0000arena\\u0000numplayers\\u00005\\u0000numteams\\u00002\\u0000maxplayers\\u000032\\u0000gamemode\\u0000openplaying\\u0000teamplay\\u00001\\u0000fraglimit\\u00000\\u0000timelimit\\u000040\\u0000gravity\\u0000800\\u0000rankingon\\u00001\\u0000\\u0000\\u0000\\u0005player_\\u0000score_\\u0000deaths_\\u0000ping_\\u0000team_\\u0000time_\\u0000\\u0000Joe Player\\u000030\\u000012\\u0000411\\u00000\\u000010\\u0000L33t 0n3\\u00000\\u00006\\u0000233\\u00001\\u0000325\\u0000Raptor\\u000015\\u000025\\u000063\\u00001\\u0000462\\u0000Gr81\\u00000\\u000016\\u0000294\\u00000\\u0000870\\u0000Flubber\\u000017\\u000012\\u0000232\\u00001\\u0000384\\u0000\\u0000\\u0002team_t\\u0000score_t\\u0000avgping_t\\u0000\\u0000Red\\u0000294\\u0000357\\u0000Blue\\u0000498\\u0000454\\u0000", "client_ip": "172.19.0.5", "client_port": 11111, "instant_key": "3855573338", "command_name": 3, "server_data": {"localip0": "172.19.0.5", "localport": "11111", "natneg": "1", "statechanged": "3", "gamename": "gmtest", "hostname": "GameSpy QR2 Sample", "gamever": "2.00", "hostport": "25000", "mapname": "gmtmap1", "gametype": "arena", "numplayers": "5", "numteams": "2", "maxplayers": "32", "gamemode": "openplaying", "teamplay": "1", "fraglimit": "0", "timelimit": "40", "gravity": "800", "rankingon": "1"}, "player_data": [{"player_0": "Joe Player", "score_0": "30", "deaths_0": "12", "ping_0": "411", "team_0": "0", "time_0": "10"}, {"player_1": "L33t 0n3", "score_1": "0", "deaths_1": "6", "ping_1": "233", "team_1": "1", "time_1": "325"}, {"player_2": "Raptor", "score_2": "15", "deaths_2": "25", "ping_2": "63", "team_2": "1", "time_2": "462"}, {"player_3": "Gr81", "score_3": "0", "deaths_3": "16", "ping_3": "294", "team_3": "0", "time_3": "870"}, {"player_4": "Flubber", "score_4": "17", "deaths_4": "12", "ping_4": "232", "team_4": "1", "time_4": "384"}], "team_data": [{"team_t0": "Red", "score_t0": "294", "avgping_t0": "357"}, {"team_t1": "Blue", "score_t1": "498", "avgping_t1": "454"}], "server_status": 3, "group_id": None, "game_name": "gmtest"} @@ -12,3 +12,11 @@ async def test_heartbeat(self): handler = Heartbeathandler(req) await handler.handle() handler._response + + async def test_available(self): + request = {"raw_request": "\\t\\u0000\\u0000\\u0000\\u0000\\t\\u0000\\u0000\\u0000\\u0000gamespy\\u0000", "command_name": 9, + "instant_key": "0", "client_ip": "127.0.0.1", "server_id": "a8893d8a-664e-4302-bb55-41b3a9229bd1", "client_port": "1234"} + new_req = AvaliableRequest(**request) + handler = AvaliableHandler(new_req) + await handler.handle() + pass diff --git a/src/backends/tests/gamespy/query_report/integration_tests.py b/src/backends/tests/gamespy/query_report/integration_tests.py deleted file mode 100644 index cf8ec12a8..000000000 --- a/src/backends/tests/gamespy/query_report/integration_tests.py +++ /dev/null @@ -1,3 +0,0 @@ - -class IntegrationTest: - pass diff --git a/src/frontends/gamespy/library/abstractions/handler.py b/src/frontends/gamespy/library/abstractions/handler.py index 49680bc94..1c48624ca 100644 --- a/src/frontends/gamespy/library/abstractions/handler.py +++ b/src/frontends/gamespy/library/abstractions/handler.py @@ -87,7 +87,7 @@ def _prepare_data(self): self._temp_data["client_ip"] = self._client.connection.remote_ip self._temp_data["server_id"] = self._client.server_config.server_id self._temp_data["client_port"] = self._client.connection.remote_port - + def _upload_data(self): """ whether need send data to backend diff --git a/src/frontends/tests/gamespy/query_report/request_tests.py b/src/frontends/tests/gamespy/query_report/request_tests.py index a3b1e76ac..e0e4da7fa 100644 --- a/src/frontends/tests/gamespy/query_report/request_tests.py +++ b/src/frontends/tests/gamespy/query_report/request_tests.py @@ -3,47 +3,54 @@ from frontends.gamespy.protocols.query_report.v2.aggregates.enums import RequestType from frontends.gamespy.protocols.query_report.v2.contracts.requests import AvaliableRequest, ChallengeRequest, EchoRequest, HeartBeatRequest +AVALIABLE_REQUEST = bytes([0x09, # packet type + 0x00, 0x00, 0x00, 0x00, # instant key + 0x09, 0x00, 0x00, 0x00, 0x00, # prefix + 0x67, 0x61, 0x6D, 0x65, 0x73, 0x70, 0x79, # gamename + 0x00]) + +CHALLENGE_REQUEST = bytes([ + 0x01, # packet type + 0x00, 0x00, 0x00, 0x00, # instant key + 0x67, 0x61, 0x6D, 0x65, 0x73, 0x70, 0x79, # gamename + 0x00 +]) + +ECHO_REQUEST = bytes([ + 0x02, # packet type + 0x00, 0x00, 0x00, 0x00, # instant key + 0x67, 0x61, 0x6D, 0x65, 0x73, 0x70, 0x79, # gamename + 0x00 +]) + +HEARTBEAT_REQUEST = bytes([ + 0x03, 0xae, 0x1f, 0x77, 0x64, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x70, 0x30, 0x00, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x39, 0x00, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x31, 0x31, 0x31, 0x31, 0x31, 0x00, 0x6e, 0x61, 0x74, 0x6e, 0x65, 0x67, 0x00, 0x31, 0x00, 0x73, 0x74, 0x61, 0x74, 0x65, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x00, 0x33, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x67, 0x6d, 0x74, 0x65, 0x73, 0x74, 0x00, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x70, 0x79, 0x20, 0x51, 0x52, 0x32, 0x20, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x76, 0x65, 0x72, 0x00, 0x32, 0x2e, 0x30, 0x30, 0x00, 0x68, 0x6f, 0x73, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x32, 0x35, 0x30, 0x30, 0x30, 0x00, 0x6d, 0x61, 0x70, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x67, 0x6d, 0x74, 0x6d, 0x61, 0x70, 0x31, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x00, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x00, 0x6e, 0x75, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00, 0x36, 0x00, 0x6e, 0x75, 0x6d, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x00, 0x32, 0x00, 0x6d, 0x61, 0x78, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00, 0x33, 0x32, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x00, 0x6f, 0x70, 0x65, 0x6e, 0x70, 0x6c, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x00, 0x74, 0x65, 0x61, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x31, 0x00, 0x66, 0x72, 0x61, 0x67, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00, 0x30, 0x00, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00, 0x34, 0x30, 0x00, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x79, 0x00, 0x38, 0x30, 0x30, 0x00, 0x72, 0x61, 0x6e, 0x6b, 0x69, 0x6e, 0x67, 0x6f, 0x6e, 0x00, 0x31, 0x00, 0x00, 0x00, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x00, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x00, 0x64, 0x65, 0x61, 0x74, 0x68, 0x73, 0x5f, 0x00, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x00, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x00, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x00, 0x00, 0x4a, 0x6f, 0x65, 0x20, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x00, 0x32, 0x37, 0x00, 0x36, 0x00, 0x31, 0x32, 0x33, 0x00, 0x31, 0x00, 0x34, 0x30, 0x39, 0x00, 0x4c, 0x33, 0x33, 0x74, 0x20, 0x30, 0x6e, 0x33, 0x00, 0x36, 0x00, 0x32, 0x33, 0x00, 0x32, 0x37, 0x37, 0x00, 0x30, 0x00, 0x36, 0x37, 0x33, 0x00, 0x52, 0x61, 0x70, 0x74, 0x6f, 0x72, 0x00, 0x33, 0x30, 0x00, 0x31, 0x00, 0x31, 0x34, 0x36, 0x00, 0x31, 0x00, 0x37, 0x30, 0x31, 0x00, 0x47, 0x72, 0x38, 0x31, 0x00, 0x32, 0x31, 0x00, 0x31, 0x36, 0x00, 0x31, 0x32, 0x35, 0x00, 0x31, 0x00, 0x35, 0x38, 0x32, 0x00, 0x46, 0x6c, 0x75, 0x62, 0x62, 0x65, 0x72, 0x00, 0x33, 0x00, 0x32, 0x31, 0x00, 0x31, 0x31, 0x30, 0x00, 0x30, 0x00, 0x32, 0x39, 0x38, 0x00, 0x53, 0x61, 0x72, 0x67, 0x65, 0x00, 0x33, 0x00, 0x32, 0x38, 0x00, 0x31, 0x32, 0x35, 0x00, 0x31, 0x00, 0x35, 0x39, 0x30, 0x00, 0x00, 0x02, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x74, 0x00, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x00, 0x61, 0x76, 0x67, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x00, 0x00, 0x52, 0x65, 0x64, 0x00, 0x32, 0x39, 0x34, 0x00, 0x33, 0x37, 0x39, 0x00, 0x42, 0x6c, 0x75, 0x65, 0x00, 0x38, 0x39, 0x00, 0x33, 0x38, 0x33, 0x00]) + class RequestTest(unittest.TestCase): def test_available(self): - raw = bytes([0x09, # packet type - 0x00, 0x00, 0x00, 0x00, # instant key - 0x09, 0x00, 0x00, 0x00, 0x00, # prefix - 0x67, 0x61, 0x6D, 0x65, 0x73, 0x70, 0x79, # gamename - 0x00]) - request = AvaliableRequest(raw) + request = AvaliableRequest(AVALIABLE_REQUEST) request.parse() self.assertEqual(RequestType.AVALIABLE_CHECK, request.command_name) self.assertEqual("0", request.instant_key) def test_challenge(self): - raw = bytes([ - 0x01, # packet type - 0x00, 0x00, 0x00, 0x00, # instant key - 0x67, 0x61, 0x6D, 0x65, 0x73, 0x70, 0x79, # gamename - 0x00 - ]) - request = ChallengeRequest(raw) + + request = ChallengeRequest(CHALLENGE_REQUEST) request.parse() self.assertEqual(RequestType.CHALLENGE, request.command_name) self.assertEqual("0", request.instant_key) def test_echo_request(self): - raw = bytes([ - 0x02, # packet type - 0x00, 0x00, 0x00, 0x00, # instant key - 0x67, 0x61, 0x6D, 0x65, 0x73, 0x70, 0x79, # gamename - 0x00 - ]) - request = EchoRequest(raw) + + request = EchoRequest(ECHO_REQUEST) request.parse() self.assertEqual(RequestType.ECHO, request.command_name) self.assertEqual("0", request.instant_key) def test_heartbeat(self): - raw = bytes([ - 0x03, 0xae, 0x1f, 0x77, 0x64, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x69, 0x70, 0x30, 0x00, 0x31, 0x39, 0x32, 0x2e, 0x31, 0x36, 0x38, 0x2e, 0x30, 0x2e, 0x31, 0x30, 0x39, 0x00, 0x6c, 0x6f, 0x63, 0x61, 0x6c, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x31, 0x31, 0x31, 0x31, 0x31, 0x00, 0x6e, 0x61, 0x74, 0x6e, 0x65, 0x67, 0x00, 0x31, 0x00, 0x73, 0x74, 0x61, 0x74, 0x65, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x64, 0x00, 0x33, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x67, 0x6d, 0x74, 0x65, 0x73, 0x74, 0x00, 0x68, 0x6f, 0x73, 0x74, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x47, 0x61, 0x6d, 0x65, 0x53, 0x70, 0x79, 0x20, 0x51, 0x52, 0x32, 0x20, 0x53, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x76, 0x65, 0x72, 0x00, 0x32, 0x2e, 0x30, 0x30, 0x00, 0x68, 0x6f, 0x73, 0x74, 0x70, 0x6f, 0x72, 0x74, 0x00, 0x32, 0x35, 0x30, 0x30, 0x30, 0x00, 0x6d, 0x61, 0x70, 0x6e, 0x61, 0x6d, 0x65, 0x00, 0x67, 0x6d, 0x74, 0x6d, 0x61, 0x70, 0x31, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x74, 0x79, 0x70, 0x65, 0x00, 0x61, 0x72, 0x65, 0x6e, 0x61, 0x00, 0x6e, 0x75, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00, 0x36, 0x00, 0x6e, 0x75, 0x6d, 0x74, 0x65, 0x61, 0x6d, 0x73, 0x00, 0x32, 0x00, 0x6d, 0x61, 0x78, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x73, 0x00, 0x33, 0x32, 0x00, 0x67, 0x61, 0x6d, 0x65, 0x6d, 0x6f, 0x64, 0x65, 0x00, 0x6f, 0x70, 0x65, 0x6e, 0x70, 0x6c, 0x61, 0x79, 0x69, 0x6e, 0x67, 0x00, 0x74, 0x65, 0x61, 0x6d, 0x70, 0x6c, 0x61, 0x79, 0x00, 0x31, 0x00, 0x66, 0x72, 0x61, 0x67, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00, 0x30, 0x00, 0x74, 0x69, 0x6d, 0x65, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x00, 0x34, 0x30, 0x00, 0x67, 0x72, 0x61, 0x76, 0x69, 0x74, 0x79, 0x00, 0x38, 0x30, 0x30, 0x00, 0x72, 0x61, 0x6e, 0x6b, 0x69, 0x6e, 0x67, 0x6f, 0x6e, 0x00, 0x31, 0x00, 0x00, 0x00, 0x06, 0x70, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x5f, 0x00, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x00, 0x64, 0x65, 0x61, 0x74, 0x68, 0x73, 0x5f, 0x00, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x00, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x00, 0x74, 0x69, 0x6d, 0x65, 0x5f, 0x00, 0x00, 0x4a, 0x6f, 0x65, 0x20, 0x50, 0x6c, 0x61, 0x79, 0x65, 0x72, 0x00, 0x32, 0x37, 0x00, 0x36, 0x00, 0x31, 0x32, 0x33, 0x00, 0x31, 0x00, 0x34, 0x30, 0x39, 0x00, 0x4c, 0x33, 0x33, 0x74, 0x20, 0x30, 0x6e, 0x33, 0x00, 0x36, 0x00, 0x32, 0x33, 0x00, 0x32, 0x37, 0x37, 0x00, 0x30, 0x00, 0x36, 0x37, 0x33, 0x00, 0x52, 0x61, 0x70, 0x74, 0x6f, 0x72, 0x00, 0x33, 0x30, 0x00, 0x31, 0x00, 0x31, 0x34, 0x36, 0x00, 0x31, 0x00, 0x37, 0x30, 0x31, 0x00, 0x47, 0x72, 0x38, 0x31, 0x00, 0x32, 0x31, 0x00, 0x31, 0x36, 0x00, 0x31, 0x32, 0x35, 0x00, 0x31, 0x00, 0x35, 0x38, 0x32, 0x00, 0x46, 0x6c, 0x75, 0x62, 0x62, 0x65, 0x72, 0x00, 0x33, 0x00, 0x32, 0x31, 0x00, 0x31, 0x31, 0x30, 0x00, 0x30, 0x00, 0x32, 0x39, 0x38, 0x00, 0x53, 0x61, 0x72, 0x67, 0x65, 0x00, 0x33, 0x00, 0x32, 0x38, 0x00, 0x31, 0x32, 0x35, 0x00, 0x31, 0x00, 0x35, 0x39, 0x30, 0x00, 0x00, 0x02, 0x74, 0x65, 0x61, 0x6d, 0x5f, 0x74, 0x00, 0x73, 0x63, 0x6f, 0x72, 0x65, 0x5f, 0x74, 0x00, 0x61, 0x76, 0x67, 0x70, 0x69, 0x6e, 0x67, 0x5f, 0x74, 0x00, 0x00, 0x52, 0x65, 0x64, 0x00, 0x32, 0x39, 0x34, 0x00, 0x33, 0x37, 0x39, 0x00, 0x42, 0x6c, 0x75, 0x65, 0x00, 0x38, 0x39, 0x00, 0x33, 0x38, 0x33, 0x00]) - request = HeartBeatRequest(raw) + + request = HeartBeatRequest(HEARTBEAT_REQUEST) request.parse() self.assertEqual("gmtest", request.game_name) self.assertEqual("2921297764", request.instant_key) From 69203edc35195400f35872287929aa19725fe988 Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Fri, 18 Apr 2025 08:40:01 +0000 Subject: [PATCH 172/231] Update: add unittest functions. --- .../presence_connection_manager/handlers.py | 16 ++- .../presence_connection_manager/requests.py | 39 +++++-- .../presence_search_player/handlers.py | 11 +- src/backends/routers/home.py | 11 +- .../precence_search_player/handler_tests.py | 75 ++++++++++++ .../precence_search_player/requests_tests.py | 67 ----------- .../presence_conection_manager/__init__.py | 0 .../handler_tests.py | 107 ++++++++++++++++++ src/backends/tests/utils.py | 10 ++ .../contracts/requests.py | 32 ++++++ .../general_request_tests.py | 12 +- src/requirements.txt | 3 +- 12 files changed, 290 insertions(+), 93 deletions(-) create mode 100644 src/backends/tests/gamespy/precence_search_player/handler_tests.py delete mode 100644 src/backends/tests/gamespy/precence_search_player/requests_tests.py create mode 100644 src/backends/tests/gamespy/presence_conection_manager/__init__.py create mode 100644 src/backends/tests/gamespy/presence_conection_manager/handler_tests.py create mode 100644 src/backends/tests/utils.py diff --git a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py index 8e1c361e1..3bc590670 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py @@ -29,6 +29,8 @@ async def _data_operate(self) -> None: raise ValueError("Request type not valid") def _nick_email_login(self) -> None: + assert self._request.email is not None + assert self._request.nick is not None is_exsit = data.is_email_exist(self._request.email) if not is_exsit: raise GPLoginBadEmailException( @@ -37,16 +39,20 @@ def _nick_email_login(self) -> None: self._request.nick, self._request.email) def _unique_nick_login(self) -> None: + assert self._request.unique_nick is not None + assert self._request.namespace_id is not None self.data = data.get_user_infos_by_uniquenick_namespace_id( - self._request.unique_nick, self._request.namespace_id) + self._request.unique_nick, + self._request.namespace_id) def _auth_token_login(self) -> None: + assert self._request.auth_token is not None self._data = data.get_user_infos_by_authtoken( self._request.auth_token) async def _result_construct(self) -> None: - if self.data is not None: - self._result = LoginResult(data=self.data) + assert self._data + self._result = LoginResult(data=self._data) class LogoutHandler(HandlerBase): @@ -169,8 +175,8 @@ class StatusInfoHandler(HandlerBase): async def _data_operate(self) -> None: raise NotImplementedError() - - + + # region Profile diff --git a/src/backends/protocols/gamespy/presence_connection_manager/requests.py b/src/backends/protocols/gamespy/presence_connection_manager/requests.py index 786694eac..f89ef89a3 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/requests.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/requests.py @@ -1,6 +1,6 @@ -from typing import Optional, Union +from typing import Any, Optional, Union -from pydantic import UUID4, BaseModel +from pydantic import ValidationError from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( LoginType, @@ -16,17 +16,18 @@ class ErrorOnParse(RequestBase): raw_request: str +# region buddy + class AddBlockRequest(RequestBase): taget_id: int profile_id: int session_key: str -# region buddy - class BuddyListRequest(RequestBase): profile_id: int namespace_id: int + raw_request: Optional[str] = None class BlockListRequest(RequestBase): @@ -96,12 +97,12 @@ class NewUserRequest(RequestBase): class LoginRequest(RequestBase): user_challenge: str response: str - unique_nick: str user_data: str - namespace_id: int - auth_token: str - nick: str - email: str + unique_nick: Optional[str] = None + namespace_id: Optional[int] = None + auth_token: Optional[str] = None + nick: Optional[str] = None + email: Optional[str] = None product_id: int type: Union[LoginType, int] sdk_revision_type: Union[SdkRevisionType, int] @@ -109,10 +110,28 @@ class LoginRequest(RequestBase): user_id: int profile_id: int partner_id: int - game_name: int + game_name: Optional[str] quiet_mode_flags: int firewall: bool + def model_post_init(self, __context: Any) -> None: + super().model_post_init(__context) + if self.type == LoginType.AUTH_TOKEN: + if self.auth_token is None: + raise ValidationError("authtoken is missing.") + elif self.type == LoginType.UNIQUENICK_NAMESPACE_ID: + if self.unique_nick is None: + raise ValidationError("unique nick is missing.") + if self.namespace_id is None: + raise ValidationError("namespace is missing.") + elif self.type == LoginType.NICK_EMAIL: + if self.nick is None: + raise ValidationError("nick name is missing.") + if self.email is None: + raise ValidationError("email is missing.") + else: + raise ValidationError(f"request type {self.type} not found.") + class LogoutRequest(RequestBase): pass diff --git a/src/backends/protocols/gamespy/presence_search_player/handlers.py b/src/backends/protocols/gamespy/presence_search_player/handlers.py index 63143cd59..1a3606788 100644 --- a/src/backends/protocols/gamespy/presence_search_player/handlers.py +++ b/src/backends/protocols/gamespy/presence_search_player/handlers.py @@ -13,6 +13,7 @@ class CheckHandler(HandlerBase): todo: whether need check the partner id, which means whether we need to check subprofiles """ _request: CheckRequest + _result: CheckResult async def _data_operate(self) -> None: if data.verify_email(self._request.email): @@ -32,6 +33,7 @@ async def _result_construct(self) -> None: class NewUserHandler(HandlerBase): _request: NewUserRequest + _result: NewUserResult async def _data_operate(self) -> None: @@ -89,6 +91,7 @@ def _create_subprofile(self) -> None: class NicksHandler(HandlerBase): _request: NicksRequest + _result: NicksResult async def _data_operate(self) -> None: self.temp_list = data.get_nick_and_unique_nick_list( @@ -104,6 +107,7 @@ async def _result_construct(self) -> None: class OthersHandler(HandlerBase): _request: OthersRequest + _result: OthersResult async def _data_operate(self) -> None: self._data: list = data.get_friend_info_list( @@ -120,6 +124,7 @@ async def _result_construct(self) -> None: class OthersListHandler(HandlerBase): _request: OthersListRequest + result: OthersListResult async def _data_operate(self) -> None: self._data: list = data.get_matched_profile_info_list( @@ -138,6 +143,7 @@ async def _result_construct(self) -> None: class SearchHandler(HandlerBase): _request: SearchRequest + _result: SearchResult async def _data_operate(self) -> None: if self._request.request_type == SearchType.NICK_SEARCH: @@ -162,12 +168,13 @@ async def _result_construct(self) -> None: data = [] for d in self._data: dd = SearchResultData(**d) - data.append(dd) + data.append(dd) self._result = SearchResult(data=data) class SearchUniqueHandler(HandlerBase): _request: SearchUniqueRequest + _result: SearchUniqueResult async def _data_operate(self) -> None: self._data = data.get_matched_info_by_uniquenick_and_namespaceids( @@ -183,6 +190,7 @@ async def _result_construct(self) -> None: class UniqueSearchHandler(HandlerBase): _request: UniqueSearchRequest + _result: UniqueSearchResult async def _data_operate(self) -> None: self._is_exist = data.is_uniquenick_exist( @@ -194,6 +202,7 @@ async def _result_construct(self) -> None: class ValidHandler(HandlerBase): _request: ValidRequest + _result: ValidResult async def _data_operate(self) -> None: self._is_exist = data.is_email_exist(self._request.email) diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index 61ee7e71e..5ff3862e6 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -2,6 +2,7 @@ from uuid import UUID from fastapi import FastAPI from fastapi.exceptions import RequestValidationError +from fastapi.responses import JSONResponse from pydantic import BaseModel import uvicorn @@ -21,13 +22,19 @@ logger = LogManager.create("backend") + @app.exception_handler(RequestValidationError) async def validation_exception_handler(request, exc): - logger.error(str(exc)) + str_error = str(exc) + logger.error(str_error) + return JSONResponse({"error": str_error}) + @app.exception_handler(Exception) async def handle_unispy_exception(request, exc): - logger.error(str(exc)) + str_error = str(exc) + logger.error(str_error) + return JSONResponse({"error": str_error}) @app.post("/") diff --git a/src/backends/tests/gamespy/precence_search_player/handler_tests.py b/src/backends/tests/gamespy/precence_search_player/handler_tests.py new file mode 100644 index 000000000..da7bf787b --- /dev/null +++ b/src/backends/tests/gamespy/precence_search_player/handler_tests.py @@ -0,0 +1,75 @@ +# the total requests tests +import asyncio +import unittest + +from backends.tests.utils import add_headers +import frontends.gamespy.protocols.presence_search_player.contracts.requests as psp +from frontends.tests.gamespy.presence_search_player.handler_tests import ( + NICKS, SEARCH_1, SEARCH_2, SEARCH_3, SEARCH_4, CHECK1, NEWUSER, SEARCH_UNIQUENICK, SUGGEST_UNIQUE, VALID) +import backends.protocols.gamespy.presence_search_player.requests as bkr +import backends.protocols.gamespy.presence_search_player.handlers as bkh + + +class HandlerTest(unittest.TestCase): + """ + test backend and server request compability + """ + # region PCM + # region PSP + + def test_search(self): + for raw in [SEARCH_1, SEARCH_2, SEARCH_3, SEARCH_4]: + r = psp.SearchRequest(raw) + data = add_headers(r) + request = bkr.SearchRequest(**data) + handler = bkh.SearchHandler(request) + asyncio.run(handler.handle()) + pass + + def test_chenck(self): + r = psp.CheckRequest(CHECK1) + data = add_headers(r) + request = bkr.CheckRequest(**data) + handler = bkh.CheckHandler(request) + asyncio.run(handler.handle()) + pass + + def test_new_user(self): + r = psp.NewUserRequest(NEWUSER) + data = add_headers(r) + request = bkr.NewUserRequest(**data) + handler = bkh.NewUserHandler(request) + asyncio.run(handler.handle()) + pass + + def test_nick(self): + r = psp.NicksRequest(NICKS) + data = add_headers(r) + request = bkr.NicksRequest(**data) + handler = bkh.NicksHandler(request) + asyncio.run(handler.handle()) + pass + + def test_others_list(self): + # r = psp.OthersListRequest() + pass + + def test_others(self): + pass + + def test_search_unique(self): + r = psp.SearchUniqueRequest(SEARCH_UNIQUENICK) + data = add_headers(r) + request = bkr.SearchUniqueRequest(**data) + handler = bkh.SearchUniqueHandler(request) + asyncio.run(handler.handle()) + pass + + def test_valid(self): + r = psp.ValidRequest(VALID) + data = add_headers(r) + request = bkr.ValidRequest(**data) + handler = bkh.ValidHandler(request) + asyncio.run(handler.handle()) + self.assertTrue(handler._result.is_account_valid, True) + pass diff --git a/src/backends/tests/gamespy/precence_search_player/requests_tests.py b/src/backends/tests/gamespy/precence_search_player/requests_tests.py deleted file mode 100644 index 5e85f3304..000000000 --- a/src/backends/tests/gamespy/precence_search_player/requests_tests.py +++ /dev/null @@ -1,67 +0,0 @@ -# the total requests tests -import unittest - -import frontends.gamespy.protocols.presence_search_player.contracts.requests as psp -from frontends.tests.gamespy.presence_search_player.handler_tests import ( - NICKS, SEARCH_1, SEARCH_2, SEARCH_3, SEARCH_4, CHECK1, NEWUSER, SEARCH_UNIQUENICK, SUGGEST_UNIQUE, VALID) -import backends.protocols.gamespy.presence_search_player.requests as bk - - -def add_nessesary_info(request) -> dict: - request.parse() - data = request.to_dict() - data["client_ip"] = "192.168.0.1" - data["server_id"] = "950b7638-a90d-469b-ac1f-861e63c8c613" - data["client_port"] = 1234 - return data - - -class RequestsCompatibleTests(unittest.TestCase): - """ - test backend and server request compability - """ - # region PCM - # region PSP - - def test_search(self): - for raw in [SEARCH_1, SEARCH_2, SEARCH_3, SEARCH_4]: - r = psp.SearchRequest(raw) - data = add_nessesary_info(r) - bk.SearchRequest(**data) - - def test_chenck(self): - r = psp.CheckRequest(CHECK1) - data = add_nessesary_info(r) - bk.CheckRequest(**data) - pass - - def test_new_user(self): - pass - r = psp.NewUserRequest(NEWUSER) - data = add_nessesary_info(r) - bk.NewUserRequest(**data) - - def test_nick(self): - r = psp.NicksRequest(NICKS) - data = add_nessesary_info(r) - bk.NicksRequest(**data) - pass - - def test_others_list(self): - # r = psp.OthersListRequest() - pass - - def test_others(self): - pass - - def test_search_unique(self): - r = psp.SearchUniqueRequest(SEARCH_UNIQUENICK) - data = add_nessesary_info(r) - bk.SearchUniqueRequest(**data) - pass - - def test_valid(self): - r = psp.ValidRequest(VALID) - data = add_nessesary_info(r) - bk.ValidRequest(**data) - pass diff --git a/src/backends/tests/gamespy/presence_conection_manager/__init__.py b/src/backends/tests/gamespy/presence_conection_manager/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/gamespy/presence_conection_manager/handler_tests.py b/src/backends/tests/gamespy/presence_conection_manager/handler_tests.py new file mode 100644 index 000000000..fc9151f0a --- /dev/null +++ b/src/backends/tests/gamespy/presence_conection_manager/handler_tests.py @@ -0,0 +1,107 @@ +import asyncio +import unittest +from backends.tests.utils import add_headers +import frontends.gamespy.protocols.presence_connection_manager.contracts.requests as pcm +from frontends.tests.gamespy.presence_connection_manager.general_request_tests import LOGIN_AUTH_TOKEN, LOGIN_UNIQUE_NICK, LOGIN_USER +import backends.protocols.gamespy.presence_connection_manager.requests as bkr +import backends.protocols.gamespy.presence_connection_manager.handlers as bkh + + +class HandlerTest(unittest.TestCase): + + # region General + def test_login_authtoken(self): + r = pcm.LoginRequest(LOGIN_AUTH_TOKEN) + data = add_headers(r) + request = bkr.LoginRequest(**data) + handler = bkh.LoginHandler(request) + asyncio.run(handler.handle()) + pass + + def test_login_uniquenick(self): + r = pcm.LoginRequest(LOGIN_UNIQUE_NICK) + data = add_headers(r) + request = bkr.LoginRequest(**data) + handler = bkh.LoginHandler(request) + asyncio.run(handler.handle()) + pass + + def test_login_user(self): + r = pcm.LoginRequest(LOGIN_USER) + data = add_headers(r) + request = bkr.LoginRequest(**data) + handler = bkh.LoginHandler(request) + asyncio.run(handler.handle()) + pass + + def test_new_user(self): + raise NotImplementedError() + + def test_logout(self): + raise NotImplementedError() + + # region Buddy + + def test_buddy_list(self): + r = pcm.BuddyListRequest(profile_id=1, namespace_id=0) + data = add_headers(r) + request = bkr.BuddyListRequest(**data) + handler = bkh.BuddyListHandler(request) + asyncio.run(handler.handle()) + pass + + def test_block_list(self): + r = pcm.BlockListRequest(profile_id=1, namespace_id=0) + data = add_headers(r) + request = bkr.BlockListRequest(**data) + handler = bkh.BlockListHandler(request) + asyncio.run(handler.handle()) + pass + + def test_add_buddy(self): + # r = pcm.AddBuddyRequest() + raise NotImplementedError() + pass + + def test_del_buddy(self): + raise NotImplementedError() + pass + + def test_add_block(self): + raise NotImplementedError() + pass + + def test_del_block(self): + raise NotImplementedError() + pass + + def test_invite_to(self): + raise NotImplementedError() + pass + + def test_status_info(self): + raise NotImplementedError() + pass + + def test_statue(self): + raise NotImplementedError() + pass + +# region Profile + def test_get_profile(self): + raise NotImplementedError() + + def test_new_profile(self): + raise NotImplementedError() + + def test_register_cdkey(self): + raise NotImplementedError() + + def test_register_nick(self): + raise NotImplementedError() + + def test_update_profile(self): + raise NotImplementedError() + + def test_update_user_info(self): + raise NotImplementedError() diff --git a/src/backends/tests/utils.py b/src/backends/tests/utils.py new file mode 100644 index 000000000..7ad0831f4 --- /dev/null +++ b/src/backends/tests/utils.py @@ -0,0 +1,10 @@ +from frontends.gamespy.library.abstractions.contracts import RequestBase + + +def add_headers(request: RequestBase) -> dict: + request.parse() + data = request.to_dict() + data["client_ip"] = "192.168.0.1" + data["server_id"] = "950b7638-a90d-469b-ac1f-861e63c8c613" + data["client_port"] = 1234 + return data diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py index af71b231d..29cd97edd 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py @@ -272,6 +272,38 @@ def parse_other_info(self): # region Buddy +class BuddyListRequest(RequestBase): + profile_id: int + namespace_id: int + + def __init__(self, + profile_id: int, + namespace_id: int) -> None: + assert isinstance(profile_id, int) + assert isinstance(namespace_id, int) + self.profile_id = profile_id + self.namespace_id = namespace_id + + def parse(self): + pass + + +class BlockListRequest(RequestBase): + profile_id: int + namespace_id: int + + def __init__(self, + profile_id: int, + namespace_id: int) -> None: + assert isinstance(profile_id, int) + assert isinstance(namespace_id, int) + self.profile_id = profile_id + self.namespace_id = namespace_id + + def parse(self): + pass + + @final class AddBuddyRequest(RequestBase): friend_profile_id: int diff --git a/src/frontends/tests/gamespy/presence_connection_manager/general_request_tests.py b/src/frontends/tests/gamespy/presence_connection_manager/general_request_tests.py index d85f4f805..24ee397ab 100644 --- a/src/frontends/tests/gamespy/presence_connection_manager/general_request_tests.py +++ b/src/frontends/tests/gamespy/presence_connection_manager/general_request_tests.py @@ -5,15 +5,15 @@ QuietModeType, SdkRevisionType, ) +LOGIN_AUTH_TOKEN = "\\login\\\\challenge\\xxxx\\authtoken\\xxxx\\userid\\0\\profileid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" +LOGIN_UNIQUE_NICK = "\\login\\\\challenge\\xxxx\\uniquenick\\spyguy\\userid\\0\\profileid\\0\\namespaceid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" +LOGIN_USER = "\\login\\\\challenge\\xxxx\\user\\spyguy@spyguy@unispy.org\\userid\\0\\profileid\\0\\partnerid\\0\\namespaceid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" class GeneralRequestTest(unittest.TestCase): - LOGIN_AUTH_TOKEN = "\\login\\\\challenge\\xxxx\\authtoken\\xxxx\\userid\\0\\profileid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" - LOGIN_UNIQUE_NICK = "\\login\\\\challenge\\xxxx\\uniquenick\\spyguy\\userid\\0\\profileid\\0\\namespaceid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" - LOGIN_USER = "\\login\\\\challenge\\xxxx\\user\\spyguy@spyguy@unispy.org\\userid\\0\\profileid\\0\\partnerid\\0\\namespaceid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" def test_login_auth_token(self) -> None: - request = LoginRequest(GeneralRequestTest.LOGIN_AUTH_TOKEN) + request = LoginRequest(LOGIN_AUTH_TOKEN) request.parse() self.assertEqual(LoginType.AUTH_TOKEN, request.type) self.assertEqual("xxxx", request.user_challenge) @@ -31,7 +31,7 @@ def test_login_auth_token(self) -> None: self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) def test_login_unique_nick(self) -> None: - request = LoginRequest(GeneralRequestTest.LOGIN_UNIQUE_NICK) + request = LoginRequest(LOGIN_UNIQUE_NICK) request.parse() self.assertEqual(LoginType.UNIQUENICK_NAMESPACE_ID, request.type) self.assertEqual("xxxx", request.user_challenge) @@ -50,7 +50,7 @@ def test_login_unique_nick(self) -> None: self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) def test_login_user(self) -> None: - request = LoginRequest(GeneralRequestTest.LOGIN_USER) + request = LoginRequest(LOGIN_USER) request.parse() self.assertEqual(LoginType.NICK_EMAIL, request.type) self.assertEqual("xxxx", request.user_challenge) diff --git a/src/requirements.txt b/src/requirements.txt index 6e23d0348..274dbe776 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -7,10 +7,9 @@ requests == 2.32.3 fastapi == 0.115.4 xmltodict == 0.14.2 responses == 0.25.3 -pydantic == 2.9.2 redis == 5.2.0 websocket-client ==1.8.0 uvicorn == 0.32.0 prettytable == 3.11.0 responses == 0.25.3 -pydantic == 2.9.2 \ No newline at end of file +pydantic ==2.10.6 From 0a12d64cc0b5a0a78a1b9876d4e74adba5fb2ace Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 5 May 2025 09:42:35 +0000 Subject: [PATCH 173/231] Remove unused lib. --- src/requirements.txt | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/src/requirements.txt b/src/requirements.txt index 6e23d0348..7085768c2 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -1,16 +1,11 @@ -pyfiglet +pyfiglet == 1.0.2 +prettytable == 3.11.0 psycopg2-binary == 2.9.10 sqlalchemy == 2.0.36 -jsonpickle == 3.0.3 email_validator == 2.1.1 requests == 2.32.3 -fastapi == 0.115.4 +fastapi[standard] == 0.115.4 xmltodict == 0.14.2 responses == 0.25.3 -pydantic == 2.9.2 redis == 5.2.0 -websocket-client ==1.8.0 -uvicorn == 0.32.0 -prettytable == 3.11.0 -responses == 0.25.3 -pydantic == 2.9.2 \ No newline at end of file +websocket-client ==1.8.0 \ No newline at end of file From bf57661f1f80ebc99be5d1fa523b5615cfe21394 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 5 May 2025 17:44:25 +0800 Subject: [PATCH 174/231] Added file ignore on ruff. --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8b0e0c52e..18630a37c 100644 --- a/.gitignore +++ b/.gitignore @@ -5,7 +5,7 @@ *.pyc # C extensions *.so - +.ruff_cache/ # Distribution / packaging .Python build/ From 1156a38b1aeacb8ae9af99dfc92ae26d6c1e0563 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 5 May 2025 09:45:26 +0000 Subject: [PATCH 175/231] Formated code style. --- src/backends/routers/gamespy/chat.py | 42 ++++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 9671ad846..4a3514d83 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -1,7 +1,40 @@ import json from typing import Optional -from backends.protocols.gamespy.chat.handlers import CdKeyHandler, GetKeyHandler, GetUdpRelayHandler, InviteHandler -from backends.protocols.gamespy.chat.requests import AtmRequest, CdkeyRequest, GetCKeyRequest, GetChannelKeyRequest, GetKeyRequest, GetUdpRelayRequest, InviteRequest, JoinRequest, KickRequest, ListRequest, LoginRequest, ModeRequest, NamesRequest, NickRequest, NoticeRequest, PartRequest, PrivateRequest, QuitRequest, SetChannelKeyRequest, SetGroupRequest, SetKeyRequest, TopicRequest, UtmRequest, UserIPRequest, UserRequest, WhoIsRequest, WhoRequest +from backends.protocols.gamespy.chat.handlers import ( + CdKeyHandler, + GetKeyHandler, + GetUdpRelayHandler, + InviteHandler, +) +from backends.protocols.gamespy.chat.requests import ( + AtmRequest, + CdkeyRequest, + GetCKeyRequest, + GetChannelKeyRequest, + GetKeyRequest, + GetUdpRelayRequest, + InviteRequest, + JoinRequest, + KickRequest, + ListRequest, + LoginRequest, + ModeRequest, + NamesRequest, + NickRequest, + NoticeRequest, + PartRequest, + PrivateRequest, + QuitRequest, + SetChannelKeyRequest, + SetGroupRequest, + SetKeyRequest, + TopicRequest, + UtmRequest, + UserIPRequest, + UserRequest, + WhoIsRequest, + WhoRequest, +) from backends.urls import CHAT from fastapi import APIRouter, FastAPI, WebSocket, WebSocketDisconnect @@ -22,7 +55,7 @@ def check_request(request: str) -> Optional[BrockerMessage]: return ch_msg -@router.websocket(f"{CHAT}/Channel") +@router.websocket(f"{CHAT}/Brocker") async def websocket_endpoint(ws: WebSocket): await ws.accept() if isinstance(ws, WebSocket) and ws.client is not None: @@ -51,6 +84,7 @@ async def websocket_endpoint(ws: WebSocket): CHANNELS[msg.channel_name].remove(ws) print("Client disconnected") + # region General @@ -178,6 +212,7 @@ async def set_group(request: SetGroupRequest): async def topic(request: TopicRequest): pass + # region Message @@ -204,6 +239,7 @@ async def utm(request: UtmRequest): if __name__ == "__main__": import uvicorn from fastapi import FastAPI + app = FastAPI() app.include_router(router) uvicorn.run(app, host="0.0.0.0", port=8080) From 9946fedc30540c9ac75b6ee1fab269dd257ea8bb Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 5 May 2025 09:45:53 +0000 Subject: [PATCH 176/231] Create websocket manager. --- src/backends/library/brockers/chat.py | 8 +-- src/backends/library/brockers/ws_manager.py | 75 +++++++++++++++++++++ 2 files changed, 79 insertions(+), 4 deletions(-) create mode 100644 src/backends/library/brockers/ws_manager.py diff --git a/src/backends/library/brockers/chat.py b/src/backends/library/brockers/chat.py index f88fbba17..793297d51 100644 --- a/src/backends/library/brockers/chat.py +++ b/src/backends/library/brockers/chat.py @@ -17,7 +17,7 @@ """ -async def channel_publish(channel_name: str, message: dict): - if channel_name in CHANNELS: - for client in CHANNELS[channel_name]: - await client.send(message) +# async def channel_publish(channel_name: str, message: dict): +# if channel_name in CHANNELS: +# for client in CHANNELS[channel_name]: +# await client.send(message) diff --git a/src/backends/library/brockers/ws_manager.py b/src/backends/library/brockers/ws_manager.py new file mode 100644 index 000000000..dcea32635 --- /dev/null +++ b/src/backends/library/brockers/ws_manager.py @@ -0,0 +1,75 @@ +import asyncio +from fastapi import WebSocket + + +class WebsocketManager: + ws_pool: dict[str, WebSocket] + + def __init__(self) -> None: + self.ws_pool = {} + + @staticmethod + def _get_ip_port_str(ws: WebSocket): + ip, port = ws.client # type:ignore + ip_port = f"{ip}:{port}" + return ip_port + + def add_websocket(self, ws: WebSocket): + assert ws + WebsocketManager._add_websocket_to_dict(self.ws_pool, ws) + + def remove_websocket(self, ws: WebSocket): + assert ws + WebsocketManager._remove_websocket_from_dict(self.ws_pool, ws) + + @staticmethod + def _add_websocket_to_dict(pool: dict, ws: WebSocket): + ip_port = WebsocketManager._get_ip_port_str(ws) + if ip_port not in pool: + pool[ip_port] = ws + else: + print("client already in dict, so not add.") + + @staticmethod + def _remove_websocket_from_dict(pool: dict, ws: WebSocket): + ip_port = WebsocketManager._get_ip_port_str(ws) + if ip_port in pool: + del pool[ip_port] + else: + print("client not found in dict, so not removed.") + + def broadcast(self, message: dict): + WebsocketManager._broadcast(self.ws_pool, message) + + @staticmethod + def _broadcast(pool: dict[str, WebSocket], message: dict): + loop = asyncio.get_event_loop() + for ws in pool.values(): + loop.create_task(ws.send(message)) + + +class ChatWebSocketManager(WebsocketManager): + irc_channels: dict[str, dict[str, WebSocket]] + + def __init__(self) -> None: + super().__init__() + self.irc_channels = {} + + def add_client_to_channel(self, channel_name: str, ws: WebSocket): + if channel_name not in self.irc_channels: + self.irc_channels[channel_name] = {} + + channel_ws_pool = self.irc_channels[channel_name] + WebsocketManager._add_websocket_to_dict(channel_ws_pool, ws) + + def remove_client_from_channel(self, channel_name: str, ws: WebSocket): + if channel_name not in self.irc_channels: + return + channel_ws_pool = self.irc_channels[channel_name] + WebsocketManager._remove_websocket_from_dict(channel_ws_pool, ws) + + def broadcast_channel(self, name: str, message: dict): + if name not in self.irc_channels: + return + channel_pool = self.irc_channels[name] + WebsocketManager._broadcast(channel_pool, message) From 6a65bcf23dfc65347e5278e4bc09701640fcac5e Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Mon, 5 May 2025 09:46:42 +0000 Subject: [PATCH 177/231] Fix server name. --- src/backends/urls.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/backends/urls.py b/src/backends/urls.py index 0f94b6e2f..bd9e1c242 100644 --- a/src/backends/urls.py +++ b/src/backends/urls.py @@ -1,7 +1,7 @@ PRESENCE_CONNECTION_MANAGER = "/GameSpy/PresenceConnectionManager" PRESENCE_SEARCH_PLAYER = "/GameSpy/PresenceSearchPlayer" SERVER_BROWSER_V1 = "/GameSpy/ServerBrowserV1" -SERVER_BROWSER_V2 = "/GameSpy/ServerBrowser_V2" +SERVER_BROWSER_V2 = "/GameSpy/ServerBrowserV2" QUERY_REPORT = "/GameSpy/QueryReport" NATNEG = "/GameSpy/NatNegotiation" GAMESTATUS = "/GameSpy/GameStatus" From b906fbacf11dfdcfb46a8901debb2931a2d34284 Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Tue, 6 May 2025 08:01:32 +0000 Subject: [PATCH 178/231] Update websocket management functions. --- src/.vscode/settings.json | 3 - .../library/abstractions/contracts.py | 1 - .../library/abstractions/websocket_manager.py | 63 ++++++++ src/backends/library/brockers/chat.py | 90 ++++++++--- src/backends/library/brockers/ws_manager.py | 75 --------- .../protocols/gamespy/chat/abstractions.py | 33 ++-- .../protocols/gamespy/chat/handlers.py | 1 - src/backends/protocols/gamespy/chat/helper.py | 153 +++++++++++------- .../protocols/gamespy/chat/managers.py | 59 ------- src/backends/routers/gamespy/chat.py | 42 ++--- .../gamespy/library/abstractions/contracts.py | 3 - 11 files changed, 261 insertions(+), 262 deletions(-) create mode 100644 src/backends/library/abstractions/websocket_manager.py delete mode 100644 src/backends/library/brockers/ws_manager.py delete mode 100644 src/backends/protocols/gamespy/chat/managers.py diff --git a/src/.vscode/settings.json b/src/.vscode/settings.json index a0fe3d0fd..cbe3eecc2 100644 --- a/src/.vscode/settings.json +++ b/src/.vscode/settings.json @@ -11,7 +11,4 @@ "python.testing.pytestEnabled": false, "python.testing.unittestEnabled": true, "python.analysis.enablePytestSupport": false, - "python.analysis.extraPaths": [ - "./frontends/gamespy" - ], } \ No newline at end of file diff --git a/src/backends/library/abstractions/contracts.py b/src/backends/library/abstractions/contracts.py index e1e62a867..a06a67b64 100644 --- a/src/backends/library/abstractions/contracts.py +++ b/src/backends/library/abstractions/contracts.py @@ -1,4 +1,3 @@ -from typing import Literal from pydantic import BaseModel, UUID4 diff --git a/src/backends/library/abstractions/websocket_manager.py b/src/backends/library/abstractions/websocket_manager.py new file mode 100644 index 000000000..203ed18a6 --- /dev/null +++ b/src/backends/library/abstractions/websocket_manager.py @@ -0,0 +1,63 @@ +import asyncio +from uuid import UUID +from fastapi import WebSocket + + +class WebSocketClient: + server_id: UUID + ip: str + port: int + ws: WebSocket + + def __init__(self, ws: WebSocket) -> None: + assert ws.client + ip, port = ws.client + self.ip = ip + self.port = port + self.ws = ws + + @property + def ip_port(self): + return f"{self.ip}:{self.port}" + + @staticmethod + def get_ip_port_str(ws: WebSocket): + assert ws.client + ip, port = ws.client + return f"{ip}:{port}" + + +class WebSocketManager: + client_pool: dict[str, WebSocketClient] + + def __init__(self) -> None: + self.client_pool = {} + + def create_client(self, ws: WebSocket) -> WebSocketClient: + client = WebSocketClient(ws) + return client + + def get_client(self, ws: WebSocket) -> WebSocketClient | None: + ip_port = WebSocketClient.get_ip_port_str(ws) + client = None + if ip_port in self.client_pool: + client = self.client_pool[ip_port] + return client + + def connect(self, ws: WebSocket): + client = self.create_client(ws) + if client.ip_port not in self.client_pool: + self.client_pool[client.ip_port] = client + + def disconnect(self, ws: WebSocket): + """ + call at last in inherited classs + """ + temp = self.create_client(ws) + if temp.ip_port in self.client_pool: + del self.client_pool[temp.ip_port] + + def broadcast(self, message: dict): + loop = asyncio.get_event_loop() + for client in self.client_pool.values(): + loop.create_task(client.ws.send_json(message)) diff --git a/src/backends/library/brockers/chat.py b/src/backends/library/brockers/chat.py index 793297d51..62f00b57d 100644 --- a/src/backends/library/brockers/chat.py +++ b/src/backends/library/brockers/chat.py @@ -1,23 +1,73 @@ +from backends.library.abstractions.websocket_manager import WebSocketClient, WebSocketManager - -from library.network.brockers import RedisBrocker +# from library.network.brockers import RedisBrocker from fastapi import WebSocket + # init in fast api routers -REDIS_BROCKER = None - -FRONTENDS: dict[str, WebSocket] = {} -""" -{"client ip and port" : WebSocket} -store the channel name and server websockets -""" -CHANNELS: dict[str, list[WebSocket]] = {} -""" -{"channel_name" : "list of WebSocket"} -store the frontends server websockets -""" - - -# async def channel_publish(channel_name: str, message: dict): -# if channel_name in CHANNELS: -# for client in CHANNELS[channel_name]: -# await client.send(message) +class ChatWebSocketClient(WebSocketClient): + channels: list[str] + + def __init__(self, ws: WebSocket) -> None: + super().__init__(ws) + self.channels = [] + + +class ChatWebSocketManager(WebSocketManager): + channel_info: dict[str, dict[str, ChatWebSocketClient]] + """ + [channel_name,[ip_port,WebSocket]] + """ + client_pool: dict[str, ChatWebSocketClient] + """ + [ip_port,ChatWebSocketClient] + """ + + def __init__(self) -> None: + super().__init__() + self.channel_info = {} + + def create_client(self, ws: WebSocket) -> WebSocketClient: + client = ChatWebSocketClient(ws) + return client + + def add_to_channel(self, channel_name: str, ws: WebSocket): + client = self.get_client(ws) + assert isinstance(client, ChatWebSocketClient) + # add channel to client.channels + if channel_name not in client.channels: + client.channels.append(channel_name) + # add client to channel_info + if channel_name not in self.channel_info: + self.channel_info[channel_name] = {} + channel = self.channel_info[channel_name] + channel[client.ip_port] = client + + def remove_from_channel(self, channel_name, ws: WebSocket): + # remove from channel_info + if channel_name not in self.channel_info: + return + client = self.get_client(ws) + assert isinstance(client, ChatWebSocketClient) + # remove from client.channels + if channel_name not in client.channels: + return + client.channels.remove(channel_name) + + def disconnect(self, ws: WebSocket): + client = self.get_client(ws) + assert isinstance(client, ChatWebSocketClient) + for channel_name in client.channels: + if channel_name in self.channel_info: + channel_dict = self.channel_info[channel_name] + if client.ip_port in channel_dict: + del channel_dict[client.ip_port] + super().disconnect(ws) + + def channel_broad_cast(self): + pass + + +MANAGER = ChatWebSocketManager() + +# create redis pubsub to share message cross all backends +# currently we simply implement without redis pubsub diff --git a/src/backends/library/brockers/ws_manager.py b/src/backends/library/brockers/ws_manager.py deleted file mode 100644 index dcea32635..000000000 --- a/src/backends/library/brockers/ws_manager.py +++ /dev/null @@ -1,75 +0,0 @@ -import asyncio -from fastapi import WebSocket - - -class WebsocketManager: - ws_pool: dict[str, WebSocket] - - def __init__(self) -> None: - self.ws_pool = {} - - @staticmethod - def _get_ip_port_str(ws: WebSocket): - ip, port = ws.client # type:ignore - ip_port = f"{ip}:{port}" - return ip_port - - def add_websocket(self, ws: WebSocket): - assert ws - WebsocketManager._add_websocket_to_dict(self.ws_pool, ws) - - def remove_websocket(self, ws: WebSocket): - assert ws - WebsocketManager._remove_websocket_from_dict(self.ws_pool, ws) - - @staticmethod - def _add_websocket_to_dict(pool: dict, ws: WebSocket): - ip_port = WebsocketManager._get_ip_port_str(ws) - if ip_port not in pool: - pool[ip_port] = ws - else: - print("client already in dict, so not add.") - - @staticmethod - def _remove_websocket_from_dict(pool: dict, ws: WebSocket): - ip_port = WebsocketManager._get_ip_port_str(ws) - if ip_port in pool: - del pool[ip_port] - else: - print("client not found in dict, so not removed.") - - def broadcast(self, message: dict): - WebsocketManager._broadcast(self.ws_pool, message) - - @staticmethod - def _broadcast(pool: dict[str, WebSocket], message: dict): - loop = asyncio.get_event_loop() - for ws in pool.values(): - loop.create_task(ws.send(message)) - - -class ChatWebSocketManager(WebsocketManager): - irc_channels: dict[str, dict[str, WebSocket]] - - def __init__(self) -> None: - super().__init__() - self.irc_channels = {} - - def add_client_to_channel(self, channel_name: str, ws: WebSocket): - if channel_name not in self.irc_channels: - self.irc_channels[channel_name] = {} - - channel_ws_pool = self.irc_channels[channel_name] - WebsocketManager._add_websocket_to_dict(channel_ws_pool, ws) - - def remove_client_from_channel(self, channel_name: str, ws: WebSocket): - if channel_name not in self.irc_channels: - return - channel_ws_pool = self.irc_channels[channel_name] - WebsocketManager._remove_websocket_from_dict(channel_ws_pool, ws) - - def broadcast_channel(self, name: str, message: dict): - if name not in self.irc_channels: - return - channel_pool = self.irc_channels[name] - WebsocketManager._broadcast(channel_pool, message) diff --git a/src/backends/protocols/gamespy/chat/abstractions.py b/src/backends/protocols/gamespy/chat/abstractions.py index 8ff8d917c..387c67f7a 100644 --- a/src/backends/protocols/gamespy/chat/abstractions.py +++ b/src/backends/protocols/gamespy/chat/abstractions.py @@ -1,10 +1,16 @@ - from backends.library.abstractions.contracts import ErrorResponse, RequestBase from backends.library.abstractions.handler_base import HandlerBase -from backends.library.database.pg_orm import ChatChannelCaches, ChatChannelUserCaches, ChatUserCaches +from backends.library.database.pg_orm import ( + ChatChannelCaches, + ChatChannelUserCaches, + ChatUserCaches, +) from backends.protocols.gamespy.chat.requests import ChannelRequestBase -from library.exceptions.general import UniSpyException -from protocols.chat.aggregates.exceptions import NoSuchChannelException, NoSuchNickException +from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.protocols.chat.aggregates.exceptions import ( + NoSuchChannelException, + NoSuchNickException, +) import backends.protocols.gamespy.chat.data as data @@ -24,32 +30,36 @@ def __init__(self, request: RequestBase) -> None: def _get_user(self): self._user = data.get_user_cache_by_ip_port( - self._request.client_ip, self._request.client_port) + self._request.client_ip, self._request.client_port + ) def _get_channel(self): - self._channel = data.get_channel_by_name( - self._request.channel_name) + self._channel = data.get_channel_by_name(self._request.channel_name) def _get_channel_user(self): self._channel_user = data.get_channel_user_cache_by_name_and_ip_port( self._request.channel_name, self._request.client_ip, - self._request.client_port) + self._request.client_port, + ) def _check_user(self): if self._user is None: raise NoSuchNickException( - f"Can not find user with ip address: {self._request.client_ip}:{self._request.client_port}") + f"Can not find user with ip address: {self._request.client_ip}:{self._request.client_port}" + ) def _check_channel(self): if self._channel is None: raise NoSuchChannelException( - f"Can not find channel with name: {self._request.channel_name}") + f"Can not find channel with name: {self._request.channel_name}" + ) def _check_channel_user(self): if self._channel_user is None: raise NoSuchNickException( - f"Can not find channel user with channel name: {self._request.channel_name}, ip address: {self._request.client_ip}:{self._request.client_port}") + f"Can not find channel user with channel name: {self._request.channel_name}, ip address: {self._request.client_ip}:{self._request.client_port}" + ) async def _request_check(self) -> None: self._get_user() @@ -64,7 +74,6 @@ async def _request_check(self) -> None: async def _boradcast(self) -> None: # todo boradcast message here raise NotImplementedError() - async def handle(self) -> None: try: diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index 234b0dabf..559be1782 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -6,7 +6,6 @@ from backends.protocols.gamespy.chat.abstractions import ChannelHandlerBase, MessageHandlerBase from backends.protocols.gamespy.chat.helper import ChannelHelper, ChannelUserHelper import backends.protocols.gamespy.chat.data as data -from backends.protocols.gamespy.chat.managers import KeyValueManager from backends.protocols.gamespy.chat.requests import * from frontends.gamespy.protocols.chat.aggregates.exceptions import BadChannelKeyException, ChatException, LoginFailedException, NickNameInUseException, NoSuchChannelException, NoSuchNickException from frontends.gamespy.protocols.chat.contracts.results import CryptResult, GetCKeyResult, GetKeyResult, ListResult, NamesResult, NickResult, PartResult, SetChannelKeyResult, TopicResult, WhoIsResult, WhoResult diff --git a/src/backends/protocols/gamespy/chat/helper.py b/src/backends/protocols/gamespy/chat/helper.py index 35744674c..170638837 100644 --- a/src/backends/protocols/gamespy/chat/helper.py +++ b/src/backends/protocols/gamespy/chat/helper.py @@ -1,14 +1,23 @@ from datetime import datetime from enum import Enum -from typing import TYPE_CHECKING, cast from uuid import UUID -from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatChannelUserCaches, ChatUserCaches +from backends.library.database.pg_orm import ( + PG_SESSION, + ChatChannelCaches, + ChatChannelUserCaches, + ChatUserCaches, +) import backends.protocols.gamespy.chat.data as data from backends.protocols.gamespy.chat.requests import ModeRequest -from protocols.chat.abstractions.contract import SERVER_DOMAIN -from protocols.chat.aggregates.enums import ModeOperationType -from protocols.chat.aggregates.exceptions import BadChannelKeyException, BannedFromChanException, InviteOnlyChanException, NoSuchChannelException +from frontends.gamespy.protocols.chat.abstractions.contract import SERVER_DOMAIN +from frontends.gamespy.protocols.chat.aggregates.enums import ModeOperationType +from frontends.gamespy.protocols.chat.aggregates.exceptions import ( + BadChannelKeyException, + BannedFromChanException, + InviteOnlyChanException, + NoSuchChannelException, +) class ChannelUserProperty(Enum): @@ -17,7 +26,6 @@ class ChannelUserProperty(Enum): class ChannelUserHelper: - @staticmethod def get_mode_string(user: ChatChannelUserCaches): assert isinstance(user.is_channel_operator, bool) @@ -38,7 +46,6 @@ def get_user_irc_prefix(user: ChatChannelUserCaches): class ChannelHelper: @staticmethod def join(channel: ChatChannelCaches, user: ChatUserCaches) -> None: - assert isinstance(channel, ChatChannelCaches) assert isinstance(channel.modes, list) assert isinstance(user.nick_name, str) @@ -49,12 +56,14 @@ def join(channel: ChatChannelCaches, user: ChatUserCaches) -> None: if ModeOperationType.SET_INVITED_ONLY in channel.modes: if user.nick_name not in channel.invited_nicks: raise InviteOnlyChanException( - f"You can only join channel: {channel.channel_name} when you are in invite list") + f"You can only join channel: {channel.channel_name} when you are in invite list" + ) # 2 check if user is in ban list, if it is user can not join if user.nick_name in channel.banned_nicks: raise BannedFromChanException( - "can not join channel, because you are in ban list") + "can not join channel, because you are in ban list" + ) if channel.creator == user.nick_name: # type:ignore is_creator = True else: @@ -68,7 +77,8 @@ def join(channel: ChatChannelCaches, user: ChatUserCaches) -> None: is_channel_operator=False, is_channel_creator=is_creator, remote_ip_address=user.remote_ip_address, - remote_port=user.remote_port) + remote_port=user.remote_port, + ) PG_SESSION.add(chan_user) PG_SESSION.commit() @@ -86,7 +96,11 @@ def quit(channel: ChatChannelCaches, quiter: ChatChannelUserCaches) -> None: PG_SESSION.commit() @staticmethod - def kick(channel: ChatChannelCaches, kicker: ChatChannelUserCaches, kickee: ChatChannelUserCaches) -> None: + def kick( + channel: ChatChannelCaches, + kicker: ChatChannelUserCaches, + kickee: ChatChannelUserCaches, + ) -> None: assert isinstance(channel, ChatChannelCaches) assert isinstance(kicker, ChatChannelUserCaches) assert isinstance(kickee, ChatChannelUserCaches) @@ -95,72 +109,82 @@ def kick(channel: ChatChannelCaches, kicker: ChatChannelUserCaches, kickee: Chat assert isinstance(kickee.channel_name, str) if kicker.channel_name != channel.channel_name: # type:ignore raise BadChannelKeyException( - f"kicker is not in channel: {channel.channel_name}") + f"kicker is not in channel: {channel.channel_name}" + ) if kickee.channel_name != channel.channel_name: # type:ignore raise BadChannelKeyException( - f"kickee is not in channel: {channel.channel_name}") + f"kickee is not in channel: {channel.channel_name}" + ) if not kicker.is_channel_operator: # type:ignore - raise BadChannelKeyException( - "kick failed, kicker is not channel operator") + raise BadChannelKeyException("kick failed, kicker is not channel operator") PG_SESSION.delete(kickee) PG_SESSION.commit() @staticmethod - def invite(channel: ChatChannelCaches, inviter: ChatChannelUserCaches, invitee: ChatUserCaches) -> None: - + def invite( + channel: ChatChannelCaches, + inviter: ChatChannelUserCaches, + invitee: ChatUserCaches, + ) -> None: if str(inviter.channel_name) != str(channel.channel_name): raise InviteOnlyChanException( - f"The inviter:{inviter.nick_name} is not a user in channel:{channel.channel_name}.") + f"The inviter:{inviter.nick_name} is not a user in channel:{channel.channel_name}." + ) assert isinstance(channel.invited_nicks, list) channel.invited_nicks.append(invitee.nick_name) PG_SESSION.commit() @staticmethod - def create(server_id: UUID, - channel_name: str, - password: str, - game_name: str, - room_name: str, - topic: str, - group_id: int, - max_num_user: int, - key_values: dict, - update_time: datetime, - modes: list, - creator: str | None = None) -> ChatChannelCaches: + def create( + server_id: UUID, + channel_name: str, + password: str, + game_name: str, + room_name: str, + topic: str, + group_id: int, + max_num_user: int, + key_values: dict, + update_time: datetime, + modes: list, + creator: str | None = None, + ) -> ChatChannelCaches: # check whether if channel exist, if not user is creator is_exist = data.is_channel_exist(channel_name, game_name) if is_exist: raise NoSuchChannelException( - f"Channel: {channel_name} is already exist, can not create a new one") - cache = ChatChannelCaches(server_id=server_id, - channel_name=channel_name, - password=password, - game_name=game_name, - room_name=room_name, - topic=topic, - group_id=group_id, - max_num_user=max_num_user, - key_values=key_values, - update_time=update_time, - creator=creator, - modes=modes) + f"Channel: {channel_name} is already exist, can not create a new one" + ) + cache = ChatChannelCaches( + server_id=server_id, + channel_name=channel_name, + password=password, + game_name=game_name, + room_name=room_name, + topic=topic, + group_id=group_id, + max_num_user=max_num_user, + key_values=key_values, + update_time=update_time, + creator=creator, + modes=modes, + ) PG_SESSION.add(cache) PG_SESSION.commit() return cache @staticmethod - def change_modes(channel: ChatChannelCaches, - changer: ChatChannelUserCaches, - request: ModeRequest, - ): + def change_modes( + channel: ChatChannelCaches, + changer: ChatChannelUserCaches, + request: ModeRequest, + ): assert isinstance(channel, ChatChannelCaches) assert isinstance(changer, ChatChannelUserCaches) assert isinstance(channel.modes, list) - assert all(isinstance(m, ModeOperationType) - for m in request.mode_operations) + assert all(isinstance(m, ModeOperationType) for m in request.mode_operations) for flag in request.mode_operations: if flag not in channel.modes: @@ -168,14 +192,22 @@ def change_modes(channel: ChatChannelCaches, match flag: case ModeOperationType.ENABLE_USER_QUIET_FLAG: if changer.is_channel_operator: # type:ignore - if ModeOperationType.ENABLE_USER_QUIET_FLAG not in channel.modes: + if ( + ModeOperationType.ENABLE_USER_QUIET_FLAG + not in channel.modes + ): channel.modes.append( - ModeOperationType.ENABLE_USER_QUIET_FLAG) + ModeOperationType.ENABLE_USER_QUIET_FLAG + ) case ModeOperationType.DISABLE_USER_QUIET_FLAG: if changer.is_channel_operator: # type:ignore - if ModeOperationType.ENABLE_USER_QUIET_FLAG not in channel.modes: + if ( + ModeOperationType.ENABLE_USER_QUIET_FLAG + not in channel.modes + ): channel.modes.remove( - ModeOperationType.ENABLE_USER_QUIET_FLAG) + ModeOperationType.ENABLE_USER_QUIET_FLAG + ) case ModeOperationType.ADD_CHANNEL_PASSWORD: assert isinstance(request.password, str) if changer.is_channel_operator: # type:ignore @@ -198,26 +230,31 @@ def change_modes(channel: ChatChannelCaches, channel.banned_nicks.remove(request.nick_name) case ModeOperationType.ADD_CHANNEL_OPERATOR: u = data.get_channel_user_cache_by_name( - request.channel_name, request.nick_name) + request.channel_name, request.nick_name + ) if u is None: raise BadChannelKeyException( - f"no user found with nick name:{request.nick_name}") + f"no user found with nick name:{request.nick_name}" + ) u.is_channel_operator = True # type: ignore PG_SESSION.commit() case ModeOperationType.REMOVE_CHANNEL_OPERATOR: u = data.get_channel_user_cache_by_name( - request.channel_name, request.nick_name) + request.channel_name, request.nick_name + ) u.is_channel_operator = False # type: ignore PG_SESSION.commit() case ModeOperationType.ENABLE_USER_VOICE_PERMISSION: u = data.get_channel_user_cache_by_name( - request.channel_name, request.nick_name) + request.channel_name, request.nick_name + ) u.is_voiceable = True # type: ignore case ModeOperationType.DISABLE_USER_VOICE_PERMISSION: u = data.get_channel_user_cache_by_name( - request.channel_name, request.nick_name) + request.channel_name, request.nick_name + ) u.is_voiceable = False # type: ignore PG_SESSION.commit() diff --git a/src/backends/protocols/gamespy/chat/managers.py b/src/backends/protocols/gamespy/chat/managers.py deleted file mode 100644 index d853c6e13..000000000 --- a/src/backends/protocols/gamespy/chat/managers.py +++ /dev/null @@ -1,59 +0,0 @@ -# from typing import TYPE_CHECKING, Optional - - -class KeyValueManager: - """ - Handle key value string - """ - @staticmethod - def update(data: dict): - for key, value in data.items(): - data[key] = value - - @staticmethod - def build_key_value_string(key_values: dict): - flags = "" - for key, value in key_values.items(): - flags += f"\\{key}\\{value}" - return flags - - @staticmethod - def get_value_string(data: dict, keys: list[str]) -> str: - values = "" - for key in keys: - if key in data: - values += f"\\{data[key]}" - else: - values += "\\" - # Uncomment the line below to raise an exception if key is not found - # raise Exception(f"Can not find key: {key}") - return values - - @staticmethod - def is_contain_all_key(data: dict, keys: list[str]): - return all(key in data for key in keys) - - -# if TYPE_CHECKING: -# from frontends.gamespy.protocols.chat.aggregates.channel import Channel - - -# class ChannelManager: -# local_channels: dict = {} -# """The code blow is for channel manage""" - -# @staticmethod -# def get_channel(name: str) -> Optional["Channel"]: -# if name in ChannelManager.local_channels: -# return ChannelManager.local_channels[name] -# return None - -# @staticmethod -# def add_channel(channel: "Channel"): -# if channel.name not in ChannelManager.local_channels: -# ChannelManager.local_channels[channel.name] = channel - -# @staticmethod -# def remove_channel(name: str) -> None: -# if name in ChannelManager.local_channels: -# del ChannelManager.local_channels[name] diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 4a3514d83..87dd1490e 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -1,5 +1,4 @@ -import json -from typing import Optional +from backends.library.brockers.chat import MANAGER from backends.protocols.gamespy.chat.handlers import ( CdKeyHandler, GetKeyHandler, @@ -39,49 +38,32 @@ from fastapi import APIRouter, FastAPI, WebSocket, WebSocketDisconnect from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage -from backends.library.brockers.chat import CHANNELS, FRONTENDS router = APIRouter() -def check_request(request: str) -> Optional[BrockerMessage]: - ch_msg = None - try: - request_dict = json.loads(request) - ch_msg = BrockerMessage(**request_dict) - except Exception as e: - print(e) - return None - return ch_msg +def check_request(request: dict) -> BrockerMessage: + msg = BrockerMessage(**request) + return msg -@router.websocket(f"{CHAT}/Brocker") +@router.websocket(f"{CHAT}/Broker") async def websocket_endpoint(ws: WebSocket): await ws.accept() if isinstance(ws, WebSocket) and ws.client is not None: - client_key = f"{ws.client.host}:{ws.client.port}" - FRONTENDS[client_key] = ws + MANAGER.connect(ws) try: while True: - request = await ws.receive_text() - msg = check_request(request) - if msg is None: + request = await ws.receive_json() + r = check_request(request) + if r.message is None: return - CHANNELS[msg.channel_name].append(ws) - channel_clients: list[WebSocket] = CHANNELS[msg.channel_name] - - for client in channel_clients: - # we do not send data to the publisher - if client == ws: - continue - await client.send_text(request) + # currently we broadcast all message + MANAGER.broadcast(r.model_dump()) except WebSocketDisconnect: if ws.client is not None: - client_key = f"{ws.client.host}:{ws.client.port}" - del FRONTENDS[client_key] - if msg is not None: - CHANNELS[msg.channel_name].remove(ws) + MANAGER.disconnect(ws) print("Client disconnected") diff --git a/src/frontends/gamespy/library/abstractions/contracts.py b/src/frontends/gamespy/library/abstractions/contracts.py index 7d75ea50c..b4dc52e30 100644 --- a/src/frontends/gamespy/library/abstractions/contracts.py +++ b/src/frontends/gamespy/library/abstractions/contracts.py @@ -1,9 +1,6 @@ import abc from copy import deepcopy -from datetime import datetime -import enum from typing import Optional -from uuid import UUID from pydantic import BaseModel From 69fa56bc8be00d70138300beab2fabb4230bd8fc Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Wed, 7 May 2025 17:30:32 +0800 Subject: [PATCH 179/231] Added more tests. --- .../library/abstractions/handler_base.py | 21 ++- .../library/abstractions/websocket_manager.py | 10 +- src/backends/library/brockers/chat.py | 21 ++- .../protocols/gamespy/chat/handlers.py | 175 +++++++++++++----- .../gamespy/presence_search_player/data.py | 140 ++++++++------ src/backends/tests/gamespy/chat/__init__.py | 0 src/backends/tests/gamespy/chat/lib_tests.py | 45 +++++ .../data_fetch_tests.py | 20 +- .../precence_search_player/handler_tests.py | 2 +- .../gamespy/library/abstractions/brocker.py | 6 +- .../gamespy/library/abstractions/client.py | 14 +- .../gamespy/library/abstractions/handler.py | 38 ++-- .../library/abstractions/server_launcher.py | 52 +++--- .../gamespy/library/exceptions/general.py | 7 +- .../gamespy/library/network/brockers.py | 31 ++-- .../protocols/chat/abstractions/contract.py | 3 +- .../protocols/chat/abstractions/handler.py | 56 ++---- .../protocols/chat/applications/client.py | 40 +++- .../protocols/chat/applications/handlers.py | 34 ++-- .../protocols/chat/contracts/requests.py | 1 - .../protocols/chat/contracts/results.py | 2 - .../protocols/natneg/abstractions/handlers.py | 15 ++ .../protocols/natneg/applications/handlers.py | 56 +++--- .../natneg/applications/server_launcher.py | 2 +- .../applications/handlers.py | 37 ++-- .../applications/handlers.py | 6 +- .../query_report/aggregates/natneg_channel.py | 4 +- .../query_report/v2/applications/handlers.py | 26 +-- .../tests/gamespy/chat/game_tests.py | 4 + .../tests/gamespy/chat/mock_objects.py | 140 +++++++++++--- .../tests/gamespy/chat/request_tests.py | 2 +- .../tests/gamespy/game_status/mock_objects.py | 2 + .../tests/gamespy/library/mock_objects.py | 7 +- .../tests/gamespy/natneg/handler_tests.py | 7 +- .../tests/gamespy/natneg/mock_objects.py | 23 ++- .../general_request_tests.py | 4 +- .../mock_objects.py | 64 ++++--- .../presence_search_player/handler_tests.py | 14 +- .../presence_search_player/mock_objects.py | 4 +- .../gamespy/query_report/mock_objects.py | 2 + .../tests/gamespy/web_services/auth_tests.py | 4 +- 41 files changed, 746 insertions(+), 395 deletions(-) create mode 100644 src/backends/tests/gamespy/chat/__init__.py create mode 100644 src/backends/tests/gamespy/chat/lib_tests.py diff --git a/src/backends/library/abstractions/handler_base.py b/src/backends/library/abstractions/handler_base.py index ce55053ab..401a46777 100644 --- a/src/backends/library/abstractions/handler_base.py +++ b/src/backends/library/abstractions/handler_base.py @@ -1,6 +1,11 @@ from typing import Optional, final -from backends.library.abstractions.contracts import DataResponse, ErrorResponse, OKResponse, RequestBase, Response -from backends.library.database.pg_orm import PG_SESSION +from backends.library.abstractions.contracts import ( + DataResponse, + ErrorResponse, + OKResponse, + RequestBase, + Response, +) from frontends.gamespy.library.abstractions.contracts import ResultBase import logging @@ -12,6 +17,7 @@ class HandlerBase: """ The ultimate handler base of backend service """ + _request: RequestBase _result: Optional[ResultBase] _response: Response @@ -22,6 +28,7 @@ class HandlerBase: """ the data get from database, can be any type """ + @property def response(self) -> dict: """ @@ -62,6 +69,7 @@ async def _result_construct(self) -> None: """virtual method\n can override by child class to create self._result """ + @final async def _response_construct(self) -> None: """ @@ -70,10 +78,7 @@ async def _response_construct(self) -> None: # if there are no result, we send ok response if self._result is None: self._response = OKResponse() - self.logger.info( - f"[{self.__class__.__name__}] use default OKResponse") + self.logger.info(f"[{self.__class__.__name__}] use default OKResponse") else: - self._response = DataResponse( - result=self._result.model_dump(mode="json")) - self.logger.info( - f"[{self.__class__.__name__}] use default DataResponse") + self._response = DataResponse(result=self._result.model_dump(mode="json")) + self.logger.info(f"[{self.__class__.__name__}] use default DataResponse") diff --git a/src/backends/library/abstractions/websocket_manager.py b/src/backends/library/abstractions/websocket_manager.py index 203ed18a6..9187024ed 100644 --- a/src/backends/library/abstractions/websocket_manager.py +++ b/src/backends/library/abstractions/websocket_manager.py @@ -2,6 +2,8 @@ from uuid import UUID from fastapi import WebSocket +from frontends.gamespy.library.exceptions.general import UniSpyException + class WebSocketClient: server_id: UUID @@ -17,7 +19,7 @@ def __init__(self, ws: WebSocket) -> None: self.ws = ws @property - def ip_port(self): + def ip_port(self) -> str: return f"{self.ip}:{self.port}" @staticmethod @@ -37,11 +39,15 @@ def create_client(self, ws: WebSocket) -> WebSocketClient: client = WebSocketClient(ws) return client - def get_client(self, ws: WebSocket) -> WebSocketClient | None: + def get_client(self, ws: WebSocket) -> WebSocketClient: ip_port = WebSocketClient.get_ip_port_str(ws) client = None if ip_port in self.client_pool: client = self.client_pool[ip_port] + if client is None: + raise UniSpyException( + "client is not existed in client pool. skip deleting." + ) return client def connect(self, ws: WebSocket): diff --git a/src/backends/library/brockers/chat.py b/src/backends/library/brockers/chat.py index 62f00b57d..bb1d098e4 100644 --- a/src/backends/library/brockers/chat.py +++ b/src/backends/library/brockers/chat.py @@ -1,9 +1,13 @@ -from backends.library.abstractions.websocket_manager import WebSocketClient, WebSocketManager +from typing import cast +from backends.library.abstractions.websocket_manager import ( + WebSocketClient, + WebSocketManager, +) # from library.network.brockers import RedisBrocker from fastapi import WebSocket -# init in fast api routers + class ChatWebSocketClient(WebSocketClient): channels: list[str] @@ -32,7 +36,7 @@ def create_client(self, ws: WebSocket) -> WebSocketClient: def add_to_channel(self, channel_name: str, ws: WebSocket): client = self.get_client(ws) - assert isinstance(client, ChatWebSocketClient) + client = cast(ChatWebSocketClient, client) # add channel to client.channels if channel_name not in client.channels: client.channels.append(channel_name) @@ -46,8 +50,15 @@ def remove_from_channel(self, channel_name, ws: WebSocket): # remove from channel_info if channel_name not in self.channel_info: return + client = self.get_client(ws) - assert isinstance(client, ChatWebSocketClient) + client = cast(ChatWebSocketClient, client) + + if client.ip_port in self.channel_info[channel_name]: + del self.channel_info[channel_name][client.ip_port] + # when channel do not have player we remove it + if len(self.channel_info[channel_name]) == 0: + del self.channel_info[channel_name] # remove from client.channels if channel_name not in client.channels: return @@ -55,7 +66,7 @@ def remove_from_channel(self, channel_name, ws: WebSocket): def disconnect(self, ws: WebSocket): client = self.get_client(ws) - assert isinstance(client, ChatWebSocketClient) + client = cast(ChatWebSocketClient, client) for channel_name in client.channels: if channel_name in self.channel_info: channel_dict = self.channel_info[channel_name] diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index 559be1782..31e4267e4 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -2,13 +2,72 @@ from typing import TYPE_CHECKING, cast from backends.library.abstractions.contracts import RequestBase from backends.library.abstractions.handler_base import HandlerBase -from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatUserCaches, ChatChannelUserCaches -from backends.protocols.gamespy.chat.abstractions import ChannelHandlerBase, MessageHandlerBase +from backends.library.database.pg_orm import ( + PG_SESSION, + ChatChannelCaches, + ChatUserCaches, + ChatChannelUserCaches, +) +from backends.protocols.gamespy.chat.abstractions import ( + ChannelHandlerBase, + MessageHandlerBase, +) from backends.protocols.gamespy.chat.helper import ChannelHelper, ChannelUserHelper import backends.protocols.gamespy.chat.data as data -from backends.protocols.gamespy.chat.requests import * -from frontends.gamespy.protocols.chat.aggregates.exceptions import BadChannelKeyException, ChatException, LoginFailedException, NickNameInUseException, NoSuchChannelException, NoSuchNickException -from frontends.gamespy.protocols.chat.contracts.results import CryptResult, GetCKeyResult, GetKeyResult, ListResult, NamesResult, NickResult, PartResult, SetChannelKeyResult, TopicResult, WhoIsResult, WhoResult +from backends.protocols.gamespy.chat.requests import ( + AtmRequest, + CdkeyRequest, + CryptRequest, + GetCKeyRequest, + GetKeyRequest, + GetUdpRelayRequest, + InviteRequest, + JoinRequest, + KickRequest, + ListRequest, + LoginPreAuthRequest, + ModeRequest, + NamesRequest, + NickRequest, + NoticeRequest, + PartRequest, + PrivateRequest, + QuitRequest, + SetCKeyRequest, + SetChannelKeyRequest, + SetKeyRequest, + TopicRequest, + UserRequest, + UtmRequest, + WhoIsRequest, + WhoRequest, +) +from frontends.gamespy.protocols.chat.aggregates.enums import ( + GetKeyRequestType, + TopicRequestType, + WhoRequestType, +) +from frontends.gamespy.protocols.chat.aggregates.exceptions import ( + BadChannelKeyException, + ChatException, + LoginFailedException, + NickNameInUseException, + NoSuchChannelException, + NoSuchNickException, +) +from frontends.gamespy.protocols.chat.contracts.results import ( + CryptResult, + GetCKeyResult, + GetKeyResult, + ListResult, + NamesResult, + NickResult, + PartResult, + SetChannelKeyResult, + TopicResult, + WhoIsResult, + WhoResult, +) # region General @@ -27,10 +86,10 @@ class CryptHandler(HandlerBase): async def _data_operate(self) -> None: result = data.get_user_cache_by_ip_port( - self._request.client_ip, self._request.client_port) + self._request.client_ip, self._request.client_port + ) if result is None: - raise NoSuchNickException( - f"No nick found for {self._request.client_ip}") + raise NoSuchNickException(f"No nick found for {self._request.client_ip}") result.game_name = self._request.gamename # type: ignore PG_SESSION.commit() @@ -51,7 +110,8 @@ async def _data_operate(self) -> None: async def _result_construct(self) -> None: self._result = GetKeyResult( - nick_name=self._request.nick_name, values=self.caches) + nick_name=self._request.nick_name, values=self.caches + ) class GetUdpRelayHandler(HandlerBase): @@ -67,10 +127,14 @@ class InviteHandler(HandlerBase): async def _data_operate(self) -> None: chann = data.get_channel_by_name_and_ip_port( - self._request.channel_name, self._request.client_ip, self._request.client_port) + self._request.channel_name, + self._request.client_ip, + self._request.client_port, + ) if chann is None: raise NoSuchChannelException( - "you have to be in this channel to invite your friends") + "you have to be in this channel to invite your friends" + ) assert isinstance(chann.invited_nicks, list) chann.invited_nicks.append(self._request.nick_name) @@ -124,11 +188,16 @@ async def _data_operate(self) -> None: is_nick = data.is_nick_exist(self._request.nick_name) if is_nick: raise NickNameInUseException( - old_nick=self._request.nick_name, new_nick="", message="nick name in use") + old_nick=self._request.nick_name, + new_nick="", + message="nick name in use", + ) else: - cache = ChatUserCaches(nick_name=self._request.nick_name, - server_id=self._request.server_id, - update_time=datetime.now()) + cache = ChatUserCaches( + nick_name=self._request.nick_name, + server_id=self._request.server_id, + update_time=datetime.now(), + ) ChatUserCaches() data.add_nick_cache(cache) @@ -141,14 +210,14 @@ class QuitHandler(HandlerBase): async def _data_operate(self) -> None: data.remove_user_caches_by_ip_port( - self._request.client_ip, self._request.client_port) + self._request.client_ip, self._request.client_port + ) raise NotImplementedError() class RegisterNickHandler(HandlerBase): async def _data_operate(self) -> None: - raise NotImplementedError( - "we do not know which unique nick should be updated") + raise NotImplementedError("we do not know which unique nick should be updated") class SetKeyHandler(HandlerBase): @@ -156,10 +225,10 @@ class SetKeyHandler(HandlerBase): async def _data_operate(self) -> None: user = data.get_user_cache_by_ip_port( - self._request.client_ip, self._request.client_port) + self._request.client_ip, self._request.client_port + ) if user is None: - raise NoSuchNickException( - "The ip and port is not find in database") + raise NoSuchNickException("The ip and port is not find in database") user.key_value = self._request.key_values # type:ignore data.db_commit() @@ -186,7 +255,8 @@ def _get_channel_user_info(self) -> None: def _get_user_info(self) -> None: self._data = data.get_channel_user_cache_by_ip( - self._request.client_ip, self._request.client_port) + self._request.client_ip, self._request.client_port + ) async def _result_construct(self) -> None: infos = [] @@ -203,11 +273,13 @@ async def _data_operate(self) -> None: self._data: tuple = data.get_whois_result(self._request.nick_name) async def _result_construct(self) -> None: - self._result = WhoIsResult(nick_name=self._data[0], - user_name=self._data[1], - name=self._data[2], - public_ip_address=self._data[3], - joined_channel_name=self._data[4]) + self._result = WhoIsResult( + nick_name=self._data[0], + user_name=self._data[1], + name=self._data[2], + public_ip_address=self._data[3], + joined_channel_name=self._data[4], + ) # region Channel @@ -232,12 +304,12 @@ async def _data_operate(self) -> None: self.get_channel_specific_user_key_value() def get_channel_all_user_key_value(self): - self._data = data.get_channel_user_caches_by_name( - self._request.channel_name) + self._data = data.get_channel_user_caches_by_name(self._request.channel_name) def get_channel_specific_user_key_value(self): d = data.get_channel_user_cache_by_name( - self._request.channel_name, self._request.nick_name) + self._request.channel_name, self._request.nick_name + ) if d is not None: self._data = [d] @@ -250,12 +322,13 @@ async def _result_construct(self) -> None: assert isinstance(d.nick_name, str) assert isinstance(d.key_values, dict) info = GetCKeyResult.GetCKeyInfos( - nick_name=d.nick_name, - user_values=list(d.key_values.values())) + nick_name=d.nick_name, user_values=list(d.key_values.values()) + ) infos.append(info) - self._result = GetCKeyResult(infos=infos, - channel_name=self._request.channel_name) + self._result = GetCKeyResult( + infos=infos, channel_name=self._request.channel_name + ) class JoinHandler(ChannelHandlerBase): @@ -285,7 +358,7 @@ async def _data_operate(self) -> None: modes=[], creator=self._user.nick_name, group_id=0, - max_num_user=100 + max_num_user=100, ) ChannelHelper.join(self._channel, self._user) @@ -303,10 +376,12 @@ async def _request_check(self) -> None: assert isinstance(self._channel, ChatChannelCaches) assert isinstance(self._channel.channel_name, str) self._kickee = data.get_channel_user_cache_by_name( - self._channel.channel_name, self._request.kickee_nick_name) + self._channel.channel_name, self._request.kickee_nick_name + ) if self._kickee is None: raise BadChannelKeyException( - f"kickee is not a user of channel:{self._channel.channel_name}") + f"kickee is not a user of channel:{self._channel.channel_name}" + ) async def _data_operate(self) -> None: assert self._channel @@ -321,8 +396,7 @@ class ModeHandler(ChannelHandlerBase): async def _data_operate(self) -> None: assert self._channel assert self._channel_user - ChannelHelper.change_modes( - self._channel, self._channel_user, self._request) + ChannelHelper.change_modes(self._channel, self._channel_user, self._request) async def _result_construct(self) -> None: raise NotImplementedError() @@ -349,7 +423,8 @@ async def _result_construct(self) -> None: self._result = NamesResult( all_channel_user_nicks=self._data, channel_name=self._request.channel_name, - requester_nick_name=self._user.nick_name) + requester_nick_name=self._user.nick_name, + ) class PartHandler(ChannelHandlerBase): @@ -368,8 +443,11 @@ async def _result_construct(self) -> None: assert isinstance(self._channel.channel_name, str) irc = ChannelUserHelper.get_user_irc_prefix(self._channel_user) - self._result = PartResult(leaver_irc_prefix=irc, is_channel_creator=self._channel_user.is_channel_creator, - channel_name=self._channel.channel_name) + self._result = PartResult( + leaver_irc_prefix=irc, + is_channel_creator=self._channel_user.is_channel_creator, + channel_name=self._channel.channel_name, + ) class SetChannelKeyHandler(ChannelHandlerBase): @@ -387,13 +465,15 @@ async def _result_construct(self) -> None: assert self._channel_user irc = ChannelUserHelper.get_user_irc_prefix(self._channel_user) self._result = SetChannelKeyResult( - channel_user_irc_prefix=irc, channel_name=self._request.channel_name) + channel_user_irc_prefix=irc, channel_name=self._request.channel_name + ) class SetCkeyHandler(ChannelHandlerBase): """ todo check if set channel_user or user keyvalue or set for other channeluser keyvalue """ + _request: SetCKeyRequest async def _data_operate(self) -> None: @@ -417,13 +497,16 @@ async def _data_operate(self) -> None: else: if not self._channel_user.is_channel_operator: raise NoSuchChannelException( - "inorder to set channel topic, you have to be channel operator") + "inorder to set channel topic, you have to be channel operator" + ) self._channel.topic = self._request.channel_topic # type:ignore self._data: str = self._request.channel_topic async def _result_construct(self) -> None: self._result = TopicResult( - channel_name=self._request.channel_name, channel_topic=self._data) + channel_name=self._request.channel_name, channel_topic=self._data + ) + # region Message @@ -441,4 +524,4 @@ class NoticeHandler(MessageHandlerBase): class PrivateHandler(MessageHandlerBase): - _request: NoticeRequest + _request: PrivateRequest diff --git a/src/backends/protocols/gamespy/presence_search_player/data.py b/src/backends/protocols/gamespy/presence_search_player/data.py index 0bf105966..2608e7d70 100644 --- a/src/backends/protocols/gamespy/presence_search_player/data.py +++ b/src/backends/protocols/gamespy/presence_search_player/data.py @@ -1,5 +1,5 @@ -from typing import TYPE_CHECKING, Optional, cast -from sqlalchemy import Column, and_ +from typing import TYPE_CHECKING, cast +from sqlalchemy import Column from backends.library.database.pg_orm import ( Friends, Profiles, @@ -7,8 +7,6 @@ Users, PG_SESSION, ) -from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import CheckException -from frontends.gamespy.protocols.presence_search_player.contracts.results import * def db_commit() -> None: @@ -37,7 +35,9 @@ def verify_email_and_password(email: str, password: str): return False -def get_profile_id(email: str, password: str, nick_name: str, partner_id: int) -> int | None: +def get_profile_id( + email: str, password: str, nick_name: str, partner_id: int +) -> int | None: result = ( PG_SESSION.query(Profiles.profileid) .join(Users, Profiles.userid == Users.userid) @@ -47,7 +47,8 @@ def get_profile_id(email: str, password: str, nick_name: str, partner_id: int) - Users.password == password, Profiles.nick == nick_name, SubProfiles.partnerid == partner_id, - ).first() + ) + .first() ) if result is not None: result = result[0] @@ -94,25 +95,35 @@ def get_user(email: str) -> Users | None: def get_profile(user_id: int, nick_name: str) -> Profiles | None: assert isinstance(user_id, int) assert isinstance(nick_name, str) - result = PG_SESSION.query(Profiles).where( - Profiles.userid == user_id, Profiles.nick == nick_name - ).first() + result = ( + PG_SESSION.query(Profiles) + .where(Profiles.userid == user_id, Profiles.nick == nick_name) + .first() + ) return result -def get_sub_profile(profile_id: int, namespace_id: int, product_id: int) -> SubProfiles | None: +def get_sub_profile( + profile_id: int, namespace_id: int, product_id: int +) -> SubProfiles | None: assert isinstance(SubProfiles.profileid, Column) assert isinstance(SubProfiles.namespaceid, Column) assert isinstance(SubProfiles.namespaceid, Column) - result = PG_SESSION.query(SubProfiles).where( - SubProfiles.profileid == profile_id, - SubProfiles.namespaceid == namespace_id, - SubProfiles.namespaceid == product_id, - ).first() + result = ( + PG_SESSION.query(SubProfiles) + .where( + SubProfiles.profileid == profile_id, + SubProfiles.namespaceid == namespace_id, + SubProfiles.namespaceid == product_id, + ) + .first() + ) return result -def get_nick_and_unique_nick_list(email: str, password: str, namespace_id: int) -> list[tuple[str, str]]: +def get_nick_and_unique_nick_list( + email: str, password: str, namespace_id: int +) -> list[tuple[str, str]]: """ return [(nick, uniquenick)] """ @@ -154,7 +165,8 @@ def get_friend_info_list(profile_id: int, namespace_id: int, game_name: str) -> .where( Friends.profileid == profile_id, SubProfiles.namespaceid == namespace_id, - SubProfiles.gamename == game_name,) + SubProfiles.gamename == game_name, + ) .all() ) return result @@ -195,7 +207,7 @@ def get_matched_info_by_nick( Profiles.nick, SubProfiles.uniquenick, SubProfiles.namespaceid, - Profiles.extra_info + Profiles.extra_info, ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) @@ -208,13 +220,15 @@ def get_matched_info_by_nick( extra_info = cast(dict, extra_info) firstname = extra_info.get("firstname", "") lastname = extra_info.get("lastname", "") - t = {"profile_id": profile_id, - "nick": nick, - "uniquenick": uniquenick, - "email": email, - "namespace_id": namespace_id, - "firstname": firstname, - "lastname": lastname} + t = { + "profile_id": profile_id, + "nick": nick, + "uniquenick": uniquenick, + "email": email, + "namespace_id": namespace_id, + "firstname": firstname, + "lastname": lastname, + } temp.append(t) return temp @@ -230,7 +244,7 @@ def get_matched_info_by_email( Profiles.nick, SubProfiles.uniquenick, SubProfiles.namespaceid, - Profiles.extra_info + Profiles.extra_info, ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) @@ -243,19 +257,20 @@ def get_matched_info_by_email( extra_info = cast(dict, extra_info) firstname = extra_info.get("firstname", "") lastname = extra_info.get("lastname", "") - t = {"profile_id": profile_id, - "nick": nick, - "uniquenick": uniquenick, - "email": email, - "namespace_id": namespace_id, - "firstname": firstname, - "lastname": lastname} + t = { + "profile_id": profile_id, + "nick": nick, + "uniquenick": uniquenick, + "email": email, + "namespace_id": namespace_id, + "firstname": firstname, + "lastname": lastname, + } temp.append(t) return temp def get_matched_info_by_nick_and_email(nick_name: str, email: str) -> list[dict]: - result = ( PG_SESSION.query( Users.email, @@ -263,7 +278,7 @@ def get_matched_info_by_nick_and_email(nick_name: str, email: str) -> list[dict] Profiles.nick, SubProfiles.uniquenick, SubProfiles.namespaceid, - Profiles.extra_info + Profiles.extra_info, ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) @@ -276,13 +291,15 @@ def get_matched_info_by_nick_and_email(nick_name: str, email: str) -> list[dict] extra_info = cast(dict, extra_info) firstname = extra_info.get("firstname", "") lastname = extra_info.get("lastname", "") - t = {"profile_id": profile_id, - "nick": nick, - "uniquenick": uniquenick, - "email": email, - "namespace_id": namespace_id, - "firstname": firstname, - "lastname": lastname} + t = { + "profile_id": profile_id, + "nick": nick, + "uniquenick": uniquenick, + "email": email, + "namespace_id": namespace_id, + "firstname": firstname, + "lastname": lastname, + } data.append(t) return data @@ -296,7 +313,7 @@ def get_matched_info_by_uniquenick_and_namespaceid( Profiles.nick, SubProfiles.uniquenick, SubProfiles.namespaceid, - Profiles.extra_info + Profiles.extra_info, ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) @@ -312,13 +329,15 @@ def get_matched_info_by_uniquenick_and_namespaceid( extra_info = cast(dict, extra_info) firstname = extra_info.get("firstname", "") lastname = extra_info.get("lastname", "") - t = {"profile_id": profile_id, - "nick": nick, - "uniquenick": uniquenick, - "email": email, - "namespace_id": namespace_id, - "firstname": firstname, - "lastname": lastname} + t = { + "profile_id": profile_id, + "nick": nick, + "uniquenick": uniquenick, + "email": email, + "namespace_id": namespace_id, + "firstname": firstname, + "lastname": lastname, + } data.append(t) return data @@ -338,7 +357,7 @@ def get_matched_info_by_uniquenick_and_namespaceids( .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .where( SubProfiles.uniquenick == unique_nick, - SubProfiles.namespaceid.in_(namespace_ids) + SubProfiles.namespaceid.in_(namespace_ids), ) .all() ) @@ -348,13 +367,15 @@ def get_matched_info_by_uniquenick_and_namespaceids( extra_info = cast(dict, extra_info) firstname = extra_info.get("firstname", "") lastname = extra_info.get("lastname", "") - t = {"profile_id": profile_id, - "nick": nick, - "uniquenick": uniquenick, - "email": email, - "namespace_id": namespace_id, - "firstname": firstname, - "lastname": lastname} + t = { + "profile_id": profile_id, + "nick": nick, + "uniquenick": uniquenick, + "email": email, + "namespace_id": namespace_id, + "firstname": firstname, + "lastname": lastname, + } data.append(t) return data @@ -382,8 +403,7 @@ def is_email_exist(email: str) -> bool: if TYPE_CHECKING: Users.userid = cast(Column, Users.userid) Users.email = cast(Column, Users.email) - result = PG_SESSION.query(Users.userid).where( - Users.email == email).count() + result = PG_SESSION.query(Users.userid).where(Users.email == email).count() # According to game partnerid is not nessesary if result == 0: return False diff --git a/src/backends/tests/gamespy/chat/__init__.py b/src/backends/tests/gamespy/chat/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/gamespy/chat/lib_tests.py b/src/backends/tests/gamespy/chat/lib_tests.py new file mode 100644 index 000000000..322ea9c39 --- /dev/null +++ b/src/backends/tests/gamespy/chat/lib_tests.py @@ -0,0 +1,45 @@ +from typing import cast +import unittest + +from fastapi import WebSocket +from fastapi.datastructures import Address + +from backends.library.brockers.chat import ChatWebSocketClient, ChatWebSocketManager + + +class WebSocketMock: + client: Address = Address("127.0.0.1", 123) + + +class LibTests(unittest.TestCase): + def test_ws_manager(self): + ws = WebSocketMock() + manager = ChatWebSocketManager() + ws = cast(WebSocket, ws) + manager.connect(ws) + self.assertEqual(list(manager.client_pool.values())[0].ws, ws) + manager.disconnect(ws) + self.assertEqual(len(manager.client_pool.values()), 0) + + def test_chat_ws_manager(self): + ws = WebSocketMock() + manager = ChatWebSocketManager() + ws = cast(WebSocket, ws) + manager.connect(ws) + self.assertEqual(list(manager.client_pool.values())[0].ws, ws) + + channel_name = "gmtest" + manager.add_to_channel(channel_name, ws) + client = manager.get_client(ws) + client = cast(ChatWebSocketClient, client) + manager.channel_info + self.assertTrue(channel_name in manager.channel_info) + self.assertTrue(len(manager.channel_info.values()) != 0) + self.assertTrue(channel_name in client.channels) + manager.remove_from_channel(channel_name, ws) + self.assertTrue(channel_name not in manager.channel_info) + self.assertTrue(len(manager.channel_info.values()) == 0) + self.assertTrue(channel_name not in client.channels) + + manager.disconnect(ws) + self.assertEqual(len(manager.client_pool.values()), 0) diff --git a/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py b/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py index c2ab4bdec..a3c7f3afb 100644 --- a/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py +++ b/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py @@ -13,18 +13,22 @@ def test_verify_email(self) -> None: def test_verify_email_and_password(self): result1 = data.verify_email_and_password( - "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d91") + "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d91" + ) self.assertFalse(result1) result2 = data.verify_email_and_password( - "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b") + "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b" + ) self.assertTrue(result2) def test_get_profile_id(self): result1 = data.get_profile_id( - "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b", "spyguy1", 1) + "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b", "spyguy1", 1 + ) self.assertIsNone(result1) result2 = data.get_profile_id( - "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b", "spyguy", 1) + "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b", "spyguy", 1 + ) self.assertIsNotNone(result2) self.assertEqual(result2, 1) @@ -47,7 +51,8 @@ def test_get_sub_profile(self): def test_get_nick_and_unique_nick_list(self): result = data.get_nick_and_unique_nick_list( - "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b", 0) + "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b", 0 + ) self.assertIsInstance(result, list) self.assertEqual(len(result), 1) self.assertIsInstance(result[0], tuple) @@ -64,13 +69,14 @@ def test_get_matched_info_by_nick(self): def test_get_matched_info_by_email(self): result = data.get_matched_info_by_email("spyguy@gamespy.com") + self.assertEqual(result[0]["nick"], "spyguy") + self.assertEqual(result[0]["profile_id"], 1) def test_is_uniquenick_exist(self): result1 = data.is_uniquenick_exist("spyguy_test", 0, "gmtests") self.assertTrue(result1) - result2 = data.is_uniquenick_exist( - "spyguy_not_uniquenick", 0, "gmtests") + result2 = data.is_uniquenick_exist("spyguy_not_uniquenick", 0, "gmtests") self.assertFalse(result2) def test_is_email_exist(self): diff --git a/src/backends/tests/gamespy/precence_search_player/handler_tests.py b/src/backends/tests/gamespy/precence_search_player/handler_tests.py index da7bf787b..2e0224f66 100644 --- a/src/backends/tests/gamespy/precence_search_player/handler_tests.py +++ b/src/backends/tests/gamespy/precence_search_player/handler_tests.py @@ -5,7 +5,7 @@ from backends.tests.utils import add_headers import frontends.gamespy.protocols.presence_search_player.contracts.requests as psp from frontends.tests.gamespy.presence_search_player.handler_tests import ( - NICKS, SEARCH_1, SEARCH_2, SEARCH_3, SEARCH_4, CHECK1, NEWUSER, SEARCH_UNIQUENICK, SUGGEST_UNIQUE, VALID) + NICKS, SEARCH_1, SEARCH_2, SEARCH_3, SEARCH_4, CHECK1, NEWUSER, SEARCH_UNIQUENICK, VALID) import backends.protocols.gamespy.presence_search_player.requests as bkr import backends.protocols.gamespy.presence_search_player.handlers as bkh diff --git a/src/frontends/gamespy/library/abstractions/brocker.py b/src/frontends/gamespy/library/abstractions/brocker.py index 8d5628b95..53b2f852b 100644 --- a/src/frontends/gamespy/library/abstractions/brocker.py +++ b/src/frontends/gamespy/library/abstractions/brocker.py @@ -1,17 +1,17 @@ import abc -from typing import final +from typing import final, Callable class BrockerBase: _subscriber: object is_started: bool = False _name: str - _call_back_func: "function" + _call_back_func: Callable """ brocker subscribe name """ - def __init__(self, name: str, url: str, call_back_func: "function") -> None: + def __init__(self, name: str, url: str, call_back_func: Callable) -> None: assert isinstance(name, str) assert callable(call_back_func) diff --git a/src/frontends/gamespy/library/abstractions/client.py b/src/frontends/gamespy/library/abstractions/client.py index ac637d0ff..3bb56c5d5 100644 --- a/src/frontends/gamespy/library/abstractions/client.py +++ b/src/frontends/gamespy/library/abstractions/client.py @@ -1,7 +1,6 @@ from frontends.gamespy.library.encryption.encoding import Encoding from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.library.log.log_manager import LogWriter -from frontends.gamespy.library.log.log_manager import LogWriter from frontends.gamespy.library.configs import ServerConfig import threading from typing import TYPE_CHECKING, Optional @@ -26,26 +25,30 @@ class ClientBase: crypto: Optional["EncryptBase"] info: "ClientInfoBase" is_log_raw: bool + # class static property pool: dict[str, "ClientBase"] = {} + """ Note: initialize in child class as class static member """ def __init__( - self, connection: "ConnectionBase", server_config: ServerConfig, logger: LogWriter + self, + connection: "ConnectionBase", + server_config: ServerConfig, + logger: LogWriter, ): assert isinstance(server_config, ServerConfig) assert isinstance(logger, LogWriter) from frontends.gamespy.library.abstractions.connections import ConnectionBase + assert issubclass(type(connection), ConnectionBase) self.server_config = server_config self.connection = connection self.logger = logger self.crypto = None self.is_log_raw = False - # fmt: off self._log_prefix = f"[{self.connection.ip_endpoint}]" - # fmt: on def on_connected(self) -> None: lock = threading.Lock() @@ -123,8 +126,7 @@ def log_network_upload(self, data: object) -> None: self.logger.info(f"{self._log_prefix} [upload]: {data}") def log_current_class(self, object: "CmdHandlerBase") -> None: - self.logger.debug(f"{self._log_prefix} [=>] <{ - object.__class__.__name__}>") + self.logger.debug(f"{self._log_prefix} [=>] <{object.__class__.__name__}>") class EasyTimer: diff --git a/src/frontends/gamespy/library/abstractions/handler.py b/src/frontends/gamespy/library/abstractions/handler.py index 1c48624ca..575f081e1 100644 --- a/src/frontends/gamespy/library/abstractions/handler.py +++ b/src/frontends/gamespy/library/abstractions/handler.py @@ -7,7 +7,11 @@ from frontends.gamespy.library.configs import CONFIG # if TYPE_CHECKING: -from frontends.gamespy.library.abstractions.contracts import RequestBase, ResultBase, ResponseBase +from frontends.gamespy.library.abstractions.contracts import ( + RequestBase, + ResultBase, + ResponseBase, +) from frontends.gamespy.library.extentions.encoding import UniSpyJsonEncoder @@ -32,7 +36,6 @@ class CmdHandlerBase: _is_fetching: bool def __init__(self, client: "ClientBase", request: "RequestBase") -> None: - assert issubclass(type(client), ClientBase) assert issubclass(type(request), RequestBase) self._client = client @@ -87,22 +90,33 @@ def _prepare_data(self): self._temp_data["client_ip"] = self._client.connection.remote_ip self._temp_data["server_id"] = self._client.server_config.server_id self._temp_data["client_port"] = self._client.connection.remote_port - + def _upload_data(self): """ whether need send data to backend if child class do not require feach, overide this function to do nothing """ - url = f"{CONFIG.backend.url}/GameSpy/{ - self._client.server_config.server_name}/{self.__class__.__name__}" + url = f"{CONFIG.backend.url}/GameSpy/{self._client.server_config.server_name}/{ + self.__class__.__name__ + }" json_str = json.dumps( - self._temp_data, cls=UniSpyJsonEncoder, ensure_ascii=False) + self._temp_data, cls=UniSpyJsonEncoder, ensure_ascii=False + ) self._client.log_network_upload(f"[{url}] {json_str}") - response = requests.post(url, data=json_str, headers={ - "Content-Type": "application/json"}) + try: + response = requests.post( + url, data=json_str, headers={"Content-Type": "application/json"} + ) + except requests.exceptions.ConnectionError: + if UniSpyException._is_unittesting: + raise UniSpyException( + f"backends api for {[self.__class__.__name__]} is not mocked" + ) + else: + raise UniSpyException("backends is not avaliable") if response.status_code != 200: - raise UniSpyException("failed to upload data to background.") + raise UniSpyException("failed to upload data to backends.") self._http_result = response.json() def _feach_data(self): @@ -112,7 +126,8 @@ def _feach_data(self): """ if self._result_cls is None: raise UniSpyException( - f"_result in {self.__class__.__name__} should not be null when feach data") + "_result_cls can not be null when feach data." + ) assert issubclass(self._result_cls, ResultBase) self._result = self._result_cls(**self._http_result) @@ -134,9 +149,6 @@ def _handle_exception(self, ex: Exception) -> None: override in child class if there are different exception handling behavior """ UniSpyException.handle_exception(ex, self._client) - # if we are debugging the app we re-raise the exception - if CmdHandlerBase._debug: - raise ex def _log_current_class(self) -> None: if self._client is None: diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index 5967e8c14..ff4fdccc1 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -1,4 +1,3 @@ -import signal from types import MappingProxyType from typing import Optional from frontends.gamespy.library.abstractions.connections import NetworkServerBase @@ -10,20 +9,21 @@ from prettytable import PrettyTable VERSION = 0.45 - -_SERVER_FULL_SHORT_NAME_MAPPING = MappingProxyType({ - "PresenceConnectionManager": "PCM", - "PresenceSearchPlayer": "PSP", - "CDKey": "CDKey", - "ServerBrwoserV1": "SBv1", - "ServerBrowserV2": "SBv2", - "QueryReport": "QR", - "NatNegotiation": "NN", - "GameStatus": "GS", - "Chat": "Chat", - "WebServices": "Web", - "GameTrafficReplay": "GTR", -}) +_SERVER_FULL_SHORT_NAME_MAPPING = MappingProxyType( + { + "PresenceConnectionManager": "PCM", + "PresenceSearchPlayer": "PSP", + "CDKey": "CDKey", + "ServerBrwoserV1": "SBv1", + "ServerBrowserV2": "SBv2", + "QueryReport": "QR", + "NatNegotiation": "NN", + "GameStatus": "GS", + "Chat": "Chat", + "WebServices": "Web", + "GameTrafficReplay": "GTR", + } +) class ServerLauncherBase: @@ -50,35 +50,37 @@ def __show_unispy_logo(self): # display version info print(f"version {VERSION}") table = PrettyTable() - table.field_names = ["Server Name", - "Listening Address", "Listening Port"] + table.field_names = ["Server Name", "Listening Address", "Listening Port"] assert self.config is not None - table.add_row([self.config.server_name, - self.config.public_address, self.config.listening_port]) + table.add_row( + [ + self.config.server_name, + self.config.public_address, + self.config.listening_port, + ] + ) print(table) def _launch_server(self) -> None: if self.server is None: raise UniSpyException("Create network server in child class") - import threading print("Press Ctrl+C to Quit") self.server.start() - def _connect_to_backend(self): try: # post our server config to backends to register assert self.config is not None resp = requests.post( - url=CONFIG.backend.url+"/", - data=self.config.model_dump_json()) + url=CONFIG.backend.url + "/", data=self.config.model_dump_json() + ) if resp.status_code == 200: data = resp.json() if data["status"] != "online": raise Exception( f"backend server: {CONFIG.backend.url} not available." ) - except: + except Exception: # fmt: off raise UniSpyException(f"backend server: {CONFIG.backend.url} not available.") # fmt: on @@ -89,4 +91,4 @@ def _create_logger(self): self.logger = LogManager.create(short_name) def __keep_running(self): - key = input('Press Q to Quit') + _ = input("Press Q to Quit") diff --git a/src/frontends/gamespy/library/exceptions/general.py b/src/frontends/gamespy/library/exceptions/general.py index 1e9c09ca7..17e12c245 100644 --- a/src/frontends/gamespy/library/exceptions/general.py +++ b/src/frontends/gamespy/library/exceptions/general.py @@ -1,4 +1,3 @@ - from typing import TYPE_CHECKING, Optional from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER @@ -9,6 +8,7 @@ class UniSpyException(Exception): + _is_unittesting: bool = False message: str """the error message""" @@ -18,16 +18,17 @@ def __init__(self, message: str) -> None: @staticmethod # def handle_exception(e: Exception, client: ClientBase = None): def handle_exception(e: Exception, client: Optional["ClientBase"] = None): + # if we are unittesting we raise the exception out + if UniSpyException._is_unittesting: + raise e if client is None: GLOBAL_LOGGER.info(str(e)) - pass else: if issubclass(type(e), UniSpyException): ex: UniSpyException = e # type:ignore client.log_error(ex.message) else: client.log_error(str(e)) - pass def __repr__(self) -> str: # return super().__repr__() diff --git a/src/frontends/gamespy/library/network/brockers.py b/src/frontends/gamespy/library/network/brockers.py index a04591f15..fdd6db173 100644 --- a/src/frontends/gamespy/library/network/brockers.py +++ b/src/frontends/gamespy/library/network/brockers.py @@ -1,11 +1,12 @@ import threading -from typing import Optional +from typing import Optional, Callable from redis import Redis import websocket from frontends.gamespy.library.abstractions.brocker import BrockerBase from redis.client import PubSub -from frontends.gamespy.protocols.chat.aggregates.exceptions import ChatException +from frontends.gamespy.library.exceptions.general import UniSpyException + websocket.enableTrace(True) @@ -13,7 +14,7 @@ class RedisBrocker(BrockerBase): _client: Redis _subscriber: PubSub - def __init__(self, name: str, url: str, call_back_func: "function") -> None: + def __init__(self, name: str, url: str, call_back_func: Callable) -> None: super().__init__(name, url, call_back_func) self._client = Redis.from_url(url) self._subscriber = self._client.pubsub() @@ -28,7 +29,7 @@ def get_message(self): if not self.is_started: break if message["type"] == "message": - msg = message['data'].decode('utf-8') + msg = message["data"].decode("utf-8") # run receive message in background do not block receiving threading.Thread(target=self.receive_message, args=msg) @@ -41,17 +42,18 @@ def publish_message(self, message): self._client.publish(self._name, message) -class WebsocketBrocker(BrockerBase): +class WebSocketBrocker(BrockerBase): _subscriber: websocket.WebSocketApp _publisher: Optional[websocket.WebSocket] = None - def __init__(self, name: str, url: str, call_back_func: "function") -> None: + def __init__(self, name: str, url: str, call_back_func: Callable) -> None: super().__init__(name, url, call_back_func) self._subscriber = websocket.WebSocketApp( url, on_message=lambda _, m: self.receive_message(m), on_error=print, - on_close=print) + on_close=print, + ) self._subscriber.on_open = self._on_open def _on_open(self, ws): @@ -64,24 +66,27 @@ def subscribe(self): threading.Thread(target=self._subscriber.run_forever).start() # # wait for connection establish if self._publisher is None: - raise ChatException("brocker backend is not available") + raise UniSpyException("brocker backend is not available") def unsubscribe(self): self._subscriber.close() def publish_message(self, message: str): + raise NotImplementedError("Websocket brocker only use to listen to message.") if self._publisher is None: raise ValueError("websocket connection is not established") self._publisher.send(message) if __name__ == "__main__": - - ws = WebsocketBrocker(name="test_channel", - url="ws://127.0.0.1:8080/GameSpy/Chat/Channel", call_back_func=print) + ws = WebSocketBrocker( + name="test_channel", + url="ws://127.0.0.1:8080/GameSpy/Chat/Channel", + call_back_func=print, + ) ws.subscribe() import json - ws.publish_message(json.dumps( - {"channel_name": "test", "message": "hello"})) + + ws.publish_message(json.dumps({"channel_name": "test", "message": "hello"})) while True: pass diff --git a/src/frontends/gamespy/protocols/chat/abstractions/contract.py b/src/frontends/gamespy/protocols/chat/abstractions/contract.py index 34598cf92..0d34c8414 100644 --- a/src/frontends/gamespy/protocols/chat/abstractions/contract.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/contract.py @@ -47,7 +47,7 @@ def parse(self) -> None: else: index_of_colon = raw.index(":") if index_of_colon != 0 and index_of_colon != -1: - self._long_param = raw[index_of_colon + 1:] + self._long_param = raw[index_of_colon + 1 :] # reset the request string raw = raw[:index_of_colon] @@ -77,6 +77,7 @@ def __init__(self, request: RequestBase, result: Optional[ResultBase]) -> None: assert issubclass(type(result), ResultBase) assert issubclass(type(request), RequestBase) + # region Brocker diff --git a/src/frontends/gamespy/protocols/chat/abstractions/handler.py b/src/frontends/gamespy/protocols/chat/abstractions/handler.py index f18a38666..c55574f0d 100644 --- a/src/frontends/gamespy/protocols/chat/abstractions/handler.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/handler.py @@ -1,17 +1,18 @@ -from frontends.gamespy.library.abstractions.brocker import BrockerBase -from frontends.gamespy.library.configs import CONFIG -from frontends.gamespy.library.network.brockers import WebsocketBrocker from frontends.gamespy.protocols.chat.aggregates.enums import MessageType -from frontends.gamespy.protocols.chat.abstractions.contract import ResultBase -from frontends.gamespy.protocols.chat.aggregates.exceptions import ChatException, NoSuchNickException, NoSuchChannelException -from frontends.gamespy.protocols.chat.abstractions.contract import RequestBase -from frontends.gamespy.protocols.chat.abstractions.contract import * +from frontends.gamespy.protocols.chat.abstractions.contract import ( + RequestBase, + ResponseBase, + ResultBase, +) +from frontends.gamespy.protocols.chat.aggregates.exceptions import ( + ChatException, + NoSuchNickException, +) from frontends.gamespy.library.abstractions.client import ClientBase -from frontends.gamespy.protocols.chat.abstractions.contract import RequestBase, ResultBase from frontends.gamespy.protocols.chat.applications.client import Client from frontends.gamespy.protocols.chat.aggregates.exceptions import IRCException import frontends.gamespy.library.abstractions.handler as lib -from typing import cast +from typing import Optional, cast class CmdHandlerBase(lib.CmdHandlerBase): @@ -69,46 +70,16 @@ class ChannelHandlerBase(PostLoginHandlerBase): _request: ChannelRequestBase _response: ResponseBase _result: ResultBase - _b_msg: BrockerMessage - """ - broadcast message - """ - _brocker: BrockerBase = WebsocketBrocker( - "channel", CONFIG.backend.url, handle_brocker_message) def __init__(self, client: ClientBase, request: RequestBase): super().__init__(client, request) - # self._channel = None - - def _message_construct(self): - """ - broadcast message construct - """ - assert self._request.channel_name is not None - self._response.build() - self._b_msg = BrockerMessage(server_id=self._client.server_config.server_id, - channel_name=self._request.channel_name, - sender_ip_address=self._client.connection.remote_ip, - sender_port=self._client.connection.remote_port, - message=self._response.sending_buffer) - - def _publish_to_brocker(self): """ - send message to backend, let backend to broadcast for us + we handle message broadcasting in backend api + frontends -> backends -> backends_api -> websocket broadcast. -> frontends.client.brocker.receive """ - self._message_construct() - j_str = self._b_msg.model_dump_json() - self._client.log_network_broadcast(j_str) - ChannelHandlerBase._brocker.publish_message(j_str) - - def _response_send(self) -> None: - super()._response_send() - self._publish_to_brocker() # region Message - - class MessageRequestBase(ChannelRequestBase): type: MessageType nick_name: str @@ -117,8 +88,7 @@ class MessageRequestBase(ChannelRequestBase): def parse(self): super().parse() if self.channel_name is None: - raise NoSuchNickException( - "the channel name is missing from the request") + raise NoSuchNickException("the channel name is missing from the request") if "#" in self.channel_name: self.type = MessageType.CHANNEL_MESSAGE else: diff --git a/src/frontends/gamespy/protocols/chat/applications/client.py b/src/frontends/gamespy/protocols/chat/applications/client.py index 556f0f1d4..a550f40aa 100644 --- a/src/frontends/gamespy/protocols/chat/applications/client.py +++ b/src/frontends/gamespy/protocols/chat/applications/client.py @@ -2,10 +2,13 @@ from frontends.gamespy.library.abstractions.switcher import SwitcherBase from frontends.gamespy.library.log.log_manager import LogWriter +from frontends.gamespy.library.network.brockers import WebSocketBrocker from frontends.gamespy.library.network.tcp_handler import TcpConnection -from frontends.gamespy.library.configs import ServerConfig +from frontends.gamespy.library.configs import CONFIG, ServerConfig -from typing import TYPE_CHECKING, Optional +from typing import Optional + +from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage class ClientInfo: @@ -25,12 +28,41 @@ def __init__(self) -> None: class Client(ClientBase): info: ClientInfo - client_pool: dict[str, "Client"] + brocker: Optional[WebSocketBrocker] - def __init__(self, connection: TcpConnection, server_config: ServerConfig, logger: LogWriter): + def __init__( + self, connection: TcpConnection, server_config: ServerConfig, logger: LogWriter + ): super().__init__(connection, server_config, logger) self.info = ClientInfo() + self.brocker = None def _create_switcher(self, buffer: bytes) -> SwitcherBase: from frontends.gamespy.protocols.chat.applications.switcher import Switcher + return Switcher(self, buffer.decode()) + + def on_connected(self) -> None: + self.start_brocker() + super().on_connected() + + def on_disconnected(self) -> None: + self.stop_brocker() + super().on_disconnected() + + def start_brocker(self): + self.brocker = WebSocketBrocker( + name=self.server_config.server_name, + url=f"{CONFIG.backend.url}/Chat/ws", + call_back_func=self._process_brocker_message, + ) + self.brocker.subscribe() + + def stop_brocker(self): + assert self.brocker is not None + self.brocker.unsubscribe() + + def _process_brocker_message(self, message: str): + # responsible for receive message and send out + assert message + self.connection.send(message.encode()) diff --git a/src/frontends/gamespy/protocols/chat/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py index 6a85f915c..9b6ab28f8 100644 --- a/src/frontends/gamespy/protocols/chat/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -46,7 +46,6 @@ UserIPResponse, WhoIsResponse, WhoResponse, - ) from frontends.gamespy.protocols.chat.contracts.requests import ( ATMRequest, @@ -77,14 +76,18 @@ UserRequest, WhoIsRequest, WhoRequest, - GetUdpRelayRequest + GetUdpRelayRequest, ) -from frontends.gamespy.protocols.chat.aggregates.enums import ModeRequestType, TopicRequestType -from frontends.gamespy.protocols.chat.aggregates.response_name import * +from frontends.gamespy.protocols.chat.aggregates.enums import ModeRequestType from typing import Type from frontends.gamespy.library.abstractions.client import ClientBase from frontends.gamespy.protocols.chat.abstractions.contract import RequestBase -from frontends.gamespy.protocols.chat.abstractions.handler import ChannelHandlerBase, CmdHandlerBase, MessageHandlerBase, PostLoginHandlerBase +from frontends.gamespy.protocols.chat.abstractions.handler import ( + ChannelHandlerBase, + CmdHandlerBase, + MessageHandlerBase, + PostLoginHandlerBase, +) # region General @@ -163,6 +166,7 @@ class LoginHandler(CmdHandlerBase): def __init__(self, client: ClientBase, request: LoginRequest): assert isinstance(request, LoginRequest) super().__init__(client, request) + self._result_cls = LoginResult def _response_construct(self) -> None: self._response = LoginResponse(self._result) @@ -225,12 +229,10 @@ class UserIPHandler(CmdHandlerBase): def __init__(self, client: ClientBase, request: UserIPRequest): assert isinstance(request, UserIPRequest) super().__init__(client, request) - - def _feach_data(self): - self._result = UserIPResult( - remote_ip_address=self._client.connection.remote_ip) + self._is_fetching = False def _response_construct(self) -> None: + self._result = UserIPResult(remote_ip_address=self._client.connection.remote_ip) self._response = UserIPResponse(self._result) @@ -259,6 +261,7 @@ def __init__(self, client: ClientBase, request: WhoIsRequest): def _response_construct(self) -> None: self._response = WhoIsResponse(self._result) + # region channel @@ -331,8 +334,10 @@ def _request_check(self) -> None: self._is_fetching = False def _response_construct(self): - if self._request.type in [ModeRequestType.GET_CHANNEL_AND_USER_MODES, - ModeRequestType.GET_CHANNEL_MODES]: + if self._request.type in [ + ModeRequestType.GET_CHANNEL_AND_USER_MODES, + ModeRequestType.GET_CHANNEL_MODES, + ]: self._response = ModeResponse(self._request, self._result) @@ -398,12 +403,7 @@ def __init__(self, client: ClientBase, request: TopicRequest): def _response_construct(self) -> None: self._response = TopicResponse(self._request, self._result) - def _response_send(self) -> None: - match self._request.request_type: - case TopicRequestType.GET_CHANNEL_TOPIC: - self._client.send(self._response) - case TopicRequestType.SET_CHANNEL_TOPIC: - self._publish_to_brocker() + # region Message diff --git a/src/frontends/gamespy/protocols/chat/contracts/requests.py b/src/frontends/gamespy/protocols/chat/contracts/requests.py index 782bfeb5d..22cc4be31 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/requests.py +++ b/src/frontends/gamespy/protocols/chat/contracts/requests.py @@ -1,4 +1,3 @@ -from frontends.gamespy.library.extentions.string_extentions import convert_kvstring_to_dictionary from frontends.gamespy.protocols.chat.abstractions.handler import ChannelRequestBase, MessageRequestBase from frontends.gamespy.protocols.chat.aggregates.enums import ( GetKeyRequestType, diff --git a/src/frontends/gamespy/protocols/chat/contracts/results.py b/src/frontends/gamespy/protocols/chat/contracts/results.py index c785293c4..ba8290ca3 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/results.py +++ b/src/frontends/gamespy/protocols/chat/contracts/results.py @@ -1,5 +1,3 @@ -from typing import List, Tuple - from pydantic import BaseModel from frontends.gamespy.protocols.chat.abstractions.contract import ResultBase from frontends.gamespy.protocols.chat.abstractions.handler import MessageResultBase diff --git a/src/frontends/gamespy/protocols/natneg/abstractions/handlers.py b/src/frontends/gamespy/protocols/natneg/abstractions/handlers.py index e90326062..0f42c43fe 100644 --- a/src/frontends/gamespy/protocols/natneg/abstractions/handlers.py +++ b/src/frontends/gamespy/protocols/natneg/abstractions/handlers.py @@ -9,6 +9,21 @@ def __init__(self, client: Client, request: RequestBase) -> None: assert isinstance(client, Client) assert issubclass(type(request), RequestBase) + def handle(self) -> None: + try: + # we first log this class + self._log_current_class() + # then we handle it + self._request_check() + self._response_construct() + # first send the response + if self._response is not None: + self._response_send() + # then send to backends + self._data_operate() + except Exception as ex: + self._handle_exception(ex) + if __name__ == "__main__": # cmd = CmdHandlerBase(None, None) diff --git a/src/frontends/gamespy/protocols/natneg/applications/handlers.py b/src/frontends/gamespy/protocols/natneg/applications/handlers.py index ed13e7b32..da045028a 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/handlers.py +++ b/src/frontends/gamespy/protocols/natneg/applications/handlers.py @@ -37,18 +37,17 @@ def __init__(self, client: Client, request: AddressCheckRequest) -> None: super().__init__(client, request) self._is_fetching = False - def _data_operate(self) -> None: + def _response_construct(self) -> None: """ address check did not require restapi backend, \n just send the remote ip back to the client """ self._result = AddressCheckResult( public_ip_addr=self._client.connection.remote_ip, - public_port=self._client.connection.remote_port) + public_port=self._client.connection.remote_port, + ) self._result.public_ip_addr = self._client.connection.remote_ip self._result.public_port = self._client.connection.remote_port - - def _response_construct(self) -> None: self._response = AddressCheckResponse(self._request, self._result) @@ -60,10 +59,6 @@ def __init__(self, client: Client, request: ConnectAckRequest) -> None: super().__init__(client, request) self._is_fetching = False - def _data_operate(self) -> None: - self._client.log_info( - f"client:{self._request.client_index} aknowledged connect request.") - class ConnectHandler(CmdHandlerBase): _result_cls: type[ConnectResult] @@ -82,13 +77,16 @@ class ErtAckHandler(CmdHandlerBase): def __init__(self, client: Client, request: ErtAckRequest) -> None: assert isinstance(request, ErtAckRequest) super().__init__(client, request) + self._is_fetching = False - def _feach_data(self) -> None: + def _response_construct(self) -> None: + """ + Natneg require fast response, so we do not wait for upload data. + """ self._result = ErtAckResult( public_ip_addr=self._client.connection.remote_ip, - public_port=self._client.connection.remote_port) - - def _response_construct(self) -> None: + public_port=self._client.connection.remote_port, + ) self._response = ErcAckResponse(self._request, self._result) @@ -96,6 +94,7 @@ class InitHandler(CmdHandlerBase): """ In init process, we need response the initresponse first to make client not timeout """ + _request: InitRequest _result: InitResult _response: InitResponse @@ -103,20 +102,19 @@ class InitHandler(CmdHandlerBase): def __init__(self, client: Client, request: InitRequest) -> None: assert isinstance(request, InitRequest) super().__init__(client, request) + self._is_fetching = False - def _feach_data(self) -> None: + def _response_construct(self): + """ + Natneg require fast response, so we do not wait for upload data. + """ self._result = InitResult( public_ip_addr=self._client.connection.remote_ip, - public_port=self._client.connection.remote_port) + public_port=self._client.connection.remote_port, + ) - def _response_construct(self): self._response = InitResponse(self._request, self._result) - def _response_send(self) -> None: - """we need first to send the response to client in case of the time expire""" - super()._response_send() - super()._data_operate() - class NatifyHandler(CmdHandlerBase): _request: NatifyRequest @@ -126,19 +124,18 @@ class NatifyHandler(CmdHandlerBase): def __init__(self, client: Client, request: NatifyRequest) -> None: assert isinstance(request, NatifyRequest) super().__init__(client, request) + self._is_fetching = False - def _feach_data(self): + def _response_construct(self): + """ + Natneg require fast response, so we do not wait for upload data. + """ self._result = NatifyResult( public_ip_addr=self._client.connection.remote_ip, - public_port=self._client.connection.remote_port) - - def _response_construct(self): + public_port=self._client.connection.remote_port, + ) self._response = NatifyResponse(self._request, self._result) - def _response_send(self) -> None: - super()._response_send() - super()._data_operate() - class PingHandler(CmdHandlerBase): def __init__(self, client: Client, request: PingRequest) -> None: @@ -151,5 +148,6 @@ class ReportHandler(CmdHandlerBase): _result: ReportResult def __init__(self, client: Client, request: ReportRequest) -> None: - super().__init__(client, request) assert isinstance(request, ReportRequest) + super().__init__(client, request) + self._is_fetching = False diff --git a/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py b/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py index 5db7fa15b..3d29676ea 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py @@ -1,6 +1,6 @@ from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase from frontends.gamespy.library.network.udp_handler import UdpServer -from frontends.gamespy.library.configs import CONFIG, ServerConfig +from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.protocols.natneg.applications.client import Client diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py index e67b05e8b..75d1a26bf 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py @@ -15,7 +15,6 @@ LoginRequest, LogoutRequest, ) -from frontends.gamespy.protocols.presence_connection_manager.abstractions.handlers import CmdHandlerBase from frontends.gamespy.protocols.presence_connection_manager.contracts.results import ( BlockListResult, BuddyListResult, @@ -24,7 +23,7 @@ StatusResult, GetProfileResult, NewProfileResult, - LoginResult + LoginResult, ) from frontends.gamespy.protocols.presence_connection_manager.contracts.responses import ( BlockListResponse, @@ -38,19 +37,27 @@ LoginResponse, ) -from frontends.gamespy.protocols.presence_connection_manager.applications.client import Client +from frontends.gamespy.protocols.presence_connection_manager.applications.client import ( + Client, +) from frontends.gamespy.protocols.presence_connection_manager.abstractions.handlers import ( CmdHandlerBase, LoginedHandlerBase, ) -from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import ( + RequestBase, +) from multiprocessing.pool import Pool from typing import TYPE_CHECKING -from frontends.gamespy.protocols.presence_connection_manager.aggregates.sdk_revision import SdkRevision +from frontends.gamespy.protocols.presence_connection_manager.aggregates.sdk_revision import ( + SdkRevision, +) if TYPE_CHECKING: - from frontends.gamespy.protocols.presence_connection_manager.applications.client import Client + from frontends.gamespy.protocols.presence_connection_manager.applications.client import ( + Client, + ) # region General @@ -61,6 +68,7 @@ class KeepAliveHandler(CmdHandlerBase): def __init__(self, client: "Client", request: KeepAliveRequest) -> None: assert isinstance(request, KeepAliveRequest) super().__init__(client, request) + self._is_fetching = False def _data_operate(self) -> None: # we set ip and data to request @@ -73,7 +81,6 @@ def _response_construct(self) -> None: class LoginHandler(CmdHandlerBase): - _request: LoginRequest _result_cls: type[LoginResult] _result: LoginResult @@ -122,12 +129,12 @@ def _data_operate(self): pass def _response_construct(self) -> None: - self._client.info.sdk_revision = SdkRevision( - self._request.sdk_revision_type) + self._client.info.sdk_revision = SdkRevision(self._request.sdk_revision_type) if self._client.info.sdk_revision.is_support_gpi_new_status_notification: BuddyListHandler(self._client).handle() BlockListHandler(self._client).handle() + # region Buddy @@ -142,6 +149,7 @@ class BlockListHandler(CmdHandlerBase): def __init__(self, client: Client) -> None: assert isinstance(client, Client) + self._is_fetching = False def _response_construct(self) -> None: self._response = BlockListResponse(self._result) @@ -152,8 +160,8 @@ class BuddyListHandler(LoginedHandlerBase): _result_cls: type[BuddyListResult] def __init__(self, client): - assert isinstance(client, Client) self._client = client + assert isinstance(client, Client) def response_construct(self): self._response = BuddyListResponse(self._request, self._result) @@ -193,8 +201,8 @@ class DelBuddyHandler(LoginedHandlerBase): def __init__(self, client: Client, request: DelBuddyRequest) -> None: assert isinstance(request, DelBuddyRequest) - self._is_uploading = False super().__init__(client, request) + self._is_uploading = False class InviteToHandler(LoginedHandlerBase): @@ -222,6 +230,7 @@ class StatusInfoHandler(LoginedHandlerBase): _request: StatusInfoRequest _result: StatusInfoResult + # TODO: check if this implement is correct def __init__(self, client: Client, request: StatusInfoRequest) -> None: assert isinstance(request, StatusInfoRequest) super().__init__(client, request) @@ -242,6 +251,7 @@ class AddBlockHandler(CmdHandlerBase): def __init__(self, client: Client, request: AddBlockRequest) -> None: assert isinstance(request, AddBlockRequest) super().__init__(client, request) + self._is_fetching = False @final @@ -252,6 +262,7 @@ class GetProfileHandler(CmdHandlerBase): def __init__(self, client: Client, request: GetProfileRequest) -> None: assert isinstance(request, GetProfileRequest) super().__init__(client, request) + self._result_cls = GetProfileResult def _response_construct(self) -> None: self._response = GetProfileResponse(self._request, self._result) @@ -265,6 +276,7 @@ class NewProfileHandler(CmdHandlerBase): def __init__(self, client: Client, request: NewProfileRequest) -> None: assert isinstance(request, NewProfileRequest) super().__init__(client, request) + self._result_cls = NewProfileResult def _response_construct(self) -> None: self._response = NewProfileResponse(self._request, self._result) @@ -277,6 +289,7 @@ class RegisterCDKeyHandler(CmdHandlerBase): def __init__(self, client: Client, request: RegisterCDKeyRequest) -> None: assert isinstance(request, RegisterCDKeyRequest) super().__init__(client, request) + self._is_fetching = False @final @@ -286,6 +299,7 @@ class RegisterNickHandler(CmdHandlerBase): def __init__(self, client: Client, request: RegisterNickRequest) -> None: assert isinstance(request, RegisterNickRequest) super().__init__(client, request) + self._is_fetching = False def _response_construct(self) -> None: self._response = RegisterNickResponse(self._request) @@ -305,6 +319,7 @@ class UpdateProfileHandler(CmdHandlerBase): def __init__(self, client: Client, request: UpdateProfileRequest) -> None: assert isinstance(request, UpdateProfileRequest) super().__init__(client, request) + raise NotImplementedError() @final diff --git a/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py b/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py index 403bbb185..279b2ef6e 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py +++ b/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py @@ -103,8 +103,8 @@ def _response_construct(self): class SearchUniqueHandler(CmdHandlerBase): - _result_cls: type[SearchUniqueResult] _result: SearchUniqueResult + _result_cls: type[SearchUniqueResult] def __init__(self, client: Client, request: SearchUniqueRequest) -> None: assert isinstance(request, SearchUniqueRequest) @@ -116,9 +116,9 @@ def _response_construct(self): class UniqueSearchHandler(CmdHandlerBase): - _result_cls: type[UniqueSearchResult] _request: UniqueSearchRequest _result: UniqueSearchResult + _result_cls: type[UniqueSearchResult] def __init__(self, client: Client, request: UniqueSearchRequest) -> None: assert isinstance(request, UniqueSearchRequest) @@ -130,9 +130,9 @@ def _response_construct(self): class ValidHandler(CmdHandlerBase): - _result_cls: type[ValidResult] _result: ValidResult _request: ValidRequest + _result_cls: type[ValidResult] def __init__(self, client: Client, request: ValidRequest) -> None: assert isinstance(request, ValidRequest) diff --git a/src/frontends/gamespy/protocols/query_report/aggregates/natneg_channel.py b/src/frontends/gamespy/protocols/query_report/aggregates/natneg_channel.py index 018a3e100..084e77386 100644 --- a/src/frontends/gamespy/protocols/query_report/aggregates/natneg_channel.py +++ b/src/frontends/gamespy/protocols/query_report/aggregates/natneg_channel.py @@ -2,7 +2,7 @@ from frontends.gamespy.library.abstractions.brocker import BrockerBase from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER -from frontends.gamespy.library.network.brockers import WebsocketBrocker +from frontends.gamespy.library.network.brockers import WebSocketBrocker from frontends.gamespy.protocols.query_report.v2.applications.handlers import ClientMessageHandler from frontends.gamespy.protocols.query_report.v2.contracts.requests import ClientMessageRequest from types import MappingProxyType @@ -18,7 +18,7 @@ class NatNegChannel: broker: BrockerBase pool: MappingProxyType[str, "Client"] - def __init__(self, broker_cls: type[BrockerBase] = WebsocketBrocker) -> None: + def __init__(self, broker_cls: type[BrockerBase] = WebSocketBrocker) -> None: self.broker = broker_cls( "natneg", f"{CONFIG.backend.url}/QueryReport/Channel", self.recieve_message) self.pool = MappingProxyType(Client.pool) diff --git a/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py b/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py index 7011d4a6e..ef99189d3 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py +++ b/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py @@ -1,6 +1,7 @@ - from frontends.gamespy.protocols.query_report.applications.client import Client -from frontends.gamespy.protocols.query_report.v2.abstractions.cmd_handler_base import CmdHandlerBase +from frontends.gamespy.protocols.query_report.v2.abstractions.cmd_handler_base import ( + CmdHandlerBase, +) from frontends.gamespy.protocols.query_report.v2.contracts.requests import ( AvaliableRequest, ChallengeRequest, @@ -16,7 +17,10 @@ ClientMessageResponse, HeartBeatResponse, ) -from frontends.gamespy.protocols.query_report.v2.contracts.results import ChallengeResult, HeartBeatResult +from frontends.gamespy.protocols.query_report.v2.contracts.results import ( + ChallengeResult, + HeartBeatResult, +) class AvailableHandler(CmdHandlerBase): @@ -27,8 +31,6 @@ def __init__(self, client: Client, request: AvaliableRequest) -> None: super().__init__(client, request) self._is_fetching = False - - def _response_construct(self): self._response = AvaliableResponse(self._request) @@ -40,6 +42,7 @@ class ChallengeHanler(CmdHandlerBase): def __init__(self, client: Client, request: ChallengeRequest) -> None: assert isinstance(request, ChallengeRequest) super().__init__(client, request) + self._is_fetching = False def _response_construct(self): self._response = ChallengeResponse(self._request, self._result) @@ -49,6 +52,7 @@ class ClientMessageAckHandler(CmdHandlerBase): def __init__(self, client: Client, request: ClientMessageAckRequest) -> None: assert isinstance(request, ClientMessageAckRequest) super().__init__(client, request) + self._is_fetching = False def _response_construct(self): self._client.log_info("Get client message ack") @@ -64,7 +68,7 @@ def __init__(self, client: Client, request: ClientMessageRequest) -> None: def _request_check(self) -> None: pass - def _response_construct(self): + def _response_construct(self) -> None: self._response = ClientMessageResponse(self._request) @@ -82,13 +86,13 @@ class HeartBeatHandler(CmdHandlerBase): def __init__(self, client: Client, request: HeartBeatRequest) -> None: assert isinstance(request, HeartBeatRequest) super().__init__(client, request) + self._is_fetching = False - def _feach_data(self): + def _response_construct(self) -> None: self._result = HeartBeatResult( remote_ip_address=self._client.connection.remote_ip, - remote_port=self._client.connection.remote_port) - - def _response_construct(self) -> None: + remote_port=self._client.connection.remote_port, + ) self._response = HeartBeatResponse(self._request, self._result) @@ -99,5 +103,3 @@ def __init__(self, client: Client, request: KeepAliveRequest) -> None: assert isinstance(request, KeepAliveRequest) super().__init__(client, request) self._is_fetching = False - - diff --git a/src/frontends/tests/gamespy/chat/game_tests.py b/src/frontends/tests/gamespy/chat/game_tests.py index 4150a6a93..ac63a924d 100644 --- a/src/frontends/tests/gamespy/chat/game_tests.py +++ b/src/frontends/tests/gamespy/chat/game_tests.py @@ -63,9 +63,13 @@ def test_worm3d(self): for raw in raws: client.on_received(raw.encode()) + @responses.activate def test_crysis2_20230926(self): raws = ["CRYPT des 1 capricorn\r\n", "LOGIN 95 Sporesirius baec04ae6862e941b948c8a1a361c096\r\n", "USRIP\r\n", "USER XpaGflff1X|39 127.0.0.1 peerchat.unispy.dev :e51130924b08de265d9ae69113103f78\r\nNICK *\r\n", "QUIT :Later!\r\n"] + client = create_client() + for raw in raws: + client.on_received(raw.encode()) diff --git a/src/frontends/tests/gamespy/chat/mock_objects.py b/src/frontends/tests/gamespy/chat/mock_objects.py index 4d01444b8..cc01a659e 100644 --- a/src/frontends/tests/gamespy/chat/mock_objects.py +++ b/src/frontends/tests/gamespy/chat/mock_objects.py @@ -1,11 +1,43 @@ -import asyncio -import threading -from time import sleep from typing import TYPE_CHECKING, cast from frontends.gamespy.library.configs import CONFIG -from frontends.tests.gamespy.library.mock_objects import BrokerMock, ConnectionMock, LogMock, RequestHandlerMock, create_mock_url +from frontends.gamespy.protocols.chat.applications.handlers import ( + CdKeyHandler, + GetCKeyHandler, + GetKeyHandler, + JoinHandler, + ModeHandler, + NickHandler, + PartHandler, + SetCKeyHandler, + SetChannelKeyHandler, + TopicHandler, + UTMHandler, + UserHandler, + UserIPHandler, + WhoHandler, + LoginHandler +) +from frontends.gamespy.protocols.chat.contracts.results import ( + GetCKeyResult, + GetKeyResult, + JoinResult, + LoginResult, + ModeResult, + NickResult, + PartResult, + SetChannelKeyResult, + TopicResult, + UTMResult, + WhoResult, +) +from frontends.tests.gamespy.library.mock_objects import ( + ConnectionMock, + LogMock, + RequestHandlerMock, + create_mock_url, +) from frontends.gamespy.protocols.chat.applications.client import Client -from frontends.gamespy.protocols.chat.applications.handlers import * +from frontends.gamespy.library.exceptions.general import UniSpyException class ClientMock(Client): @@ -13,38 +45,90 @@ class ClientMock(Client): def create_client() -> Client: + UniSpyException._is_unittesting = True handler = RequestHandlerMock() logger = LogMock() config = CONFIG.servers["Chat"] conn = ConnectionMock( - handler=handler, - config=config, t_client=ClientMock, - logger=logger) + handler=handler, config=config, t_client=ClientMock, logger=logger + ) + create_mock_url( + config, LoginHandler, LoginResult(profile_id=1, user_id=1).model_dump() + ) create_mock_url(config, UserIPHandler, {"message": "ok"}) - create_mock_url(config, JoinHandler, JoinResult( - joiner_nick_name="unispy", joiner_prefix="xiaojiuwo!unispy@UNISPYSERVER", all_channel_user_nicks="test", channel_modes="+q").model_dump()) + create_mock_url( + config, + JoinHandler, + JoinResult( + joiner_nick_name="unispy", + joiner_prefix="xiaojiuwo!unispy@UNISPYSERVER", + all_channel_user_nicks="test", + channel_modes="+q", + ).model_dump(), + ) create_mock_url(config, UserHandler, {"message": "ok"}) create_mock_url(config, CdKeyHandler, {"message": "ok"}) - create_mock_url(config, GetCKeyHandler, GetCKeyResult.model_validate( - {"channel_name": "test", "infos": [{"nick_name": "test_nick", "user_values": ["data", "key", "value", "data"]}]}).model_dump()) - create_mock_url(config, ModeHandler, ModeResult.model_validate( - {"channel_name": "test", "channel_modes": "+n", "joiner_nick_name": "test_nick"}).model_dump()) + create_mock_url( + config, + GetCKeyHandler, + GetCKeyResult.model_validate( + { + "channel_name": "test", + "infos": [ + { + "nick_name": "test_nick", + "user_values": ["data", "key", "value", "data"], + } + ], + } + ).model_dump(), + ) + create_mock_url( + config, + ModeHandler, + ModeResult.model_validate( + { + "channel_name": "test", + "channel_modes": "+n", + "joiner_nick_name": "test_nick", + } + ).model_dump(), + ) create_mock_url(config, SetCKeyHandler, {"message": "ok"}) - create_mock_url(config, TopicHandler, TopicResult( - channel_name="test_chan", channel_topic="test").model_dump()) - create_mock_url(config, PartHandler, PartResult( - leaver_irc_prefix="test_prefix", is_channel_creator=False, channel_name="test_chan").model_dump()) - create_mock_url(config, NickHandler, NickResult( - nick_name="test").model_dump()) + create_mock_url( + config, + TopicHandler, + TopicResult(channel_name="test_chan", channel_topic="test").model_dump(), + ) + create_mock_url( + config, + PartHandler, + PartResult( + leaver_irc_prefix="test_prefix", + is_channel_creator=False, + channel_name="test_chan", + ).model_dump(), + ) + create_mock_url(config, NickHandler, NickResult(nick_name="test").model_dump()) create_mock_url(config, WhoHandler, WhoResult(infos=[]).model_dump()) - create_mock_url(config, SetChannelKeyHandler, SetChannelKeyResult( - channel_user_irc_prefix="unispy!unispy@unispyserver", channel_name="test").model_dump()) - create_mock_url(config, GetKeyHandler, GetKeyResult( - nick_name="unispy", values=[]).model_dump()) - create_mock_url(config, UTMHandler, UTMResult( - user_irc_prefix="unispy!unispy@unispy", target_name="spyguy").model_dump()) - - ChannelHandlerBase._brocker = BrokerMock() + create_mock_url( + config, + SetChannelKeyHandler, + SetChannelKeyResult( + channel_user_irc_prefix="unispy!unispy@unispyserver", channel_name="test" + ).model_dump(), + ) + create_mock_url( + config, GetKeyHandler, GetKeyResult(nick_name="unispy", values=[]).model_dump() + ) + create_mock_url( + config, + UTMHandler, + UTMResult( + user_irc_prefix="unispy!unispy@unispy", target_name="spyguy" + ).model_dump(), + ) + if TYPE_CHECKING: conn._client = cast(Client, conn._client) diff --git a/src/frontends/tests/gamespy/chat/request_tests.py b/src/frontends/tests/gamespy/chat/request_tests.py index 98ee74e00..fcfa09f83 100644 --- a/src/frontends/tests/gamespy/chat/request_tests.py +++ b/src/frontends/tests/gamespy/chat/request_tests.py @@ -12,7 +12,7 @@ LIST = "LIST test\r\n" LOGIN_PRE_AUTH = "LOGINPREAUTH xxxxx yyyyy\r\n" # TODO: add binary data test [0D][0A] -LOGIN_NICK_AND_EMAIL = "LOGIN 0 * xxxxx :spyguy@spyguy@unispy.org\r\n" +LOGIN_NICK_AND_EMAIL = "LOGIN 0 * xxxxx :spyguy@spyguy@gamespy.com\r\n" LOGIN_UNIQUE_NICK = "LOGIN 0 spyguy xxxxx\r\n" NAMES = "NAMES\r\n" NICK = "NICK :spyguy\r\n" diff --git a/src/frontends/tests/gamespy/game_status/mock_objects.py b/src/frontends/tests/gamespy/game_status/mock_objects.py index 1025050bc..3892084d0 100644 --- a/src/frontends/tests/gamespy/game_status/mock_objects.py +++ b/src/frontends/tests/gamespy/game_status/mock_objects.py @@ -5,12 +5,14 @@ from frontends.gamespy.protocols.game_status.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult from frontends.tests.gamespy.library.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url +from frontends.gamespy.library.exceptions.general import UniSpyException class ClientMock(Client): pass def create_client() -> Client: + UniSpyException._is_unittesting = True handler = RequestHandlerMock() logger = LogMock() conn = ConnectionMock( diff --git a/src/frontends/tests/gamespy/library/mock_objects.py b/src/frontends/tests/gamespy/library/mock_objects.py index f8c92a049..c5d75a870 100644 --- a/src/frontends/tests/gamespy/library/mock_objects.py +++ b/src/frontends/tests/gamespy/library/mock_objects.py @@ -9,7 +9,6 @@ class ConnectionMock(ConnectionBase): - def send(self, data: bytes) -> None: pass @@ -53,8 +52,8 @@ def unsubscribe(self): pass -def create_mock_url(config: ServerConfig, handler: type[CmdHandlerBase], data: dict) -> None: - # fmt: off +def create_mock_url( + config: ServerConfig, handler: type[CmdHandlerBase], data: dict +) -> None: url = f"{CONFIG.backend.url}/GameSpy/{config.server_name}/{handler.__name__}" responses.add(responses.POST, url, json=data, status=200) - # fmt: on diff --git a/src/frontends/tests/gamespy/natneg/handler_tests.py b/src/frontends/tests/gamespy/natneg/handler_tests.py index e8661cab8..ed8c73a6b 100644 --- a/src/frontends/tests/gamespy/natneg/handler_tests.py +++ b/src/frontends/tests/gamespy/natneg/handler_tests.py @@ -1,8 +1,5 @@ import unittest -from frontends.gamespy.library.abstractions.handler import CmdHandlerBase -from frontends.tests.gamespy.library.mock_objects import create_mock_url -from frontends.gamespy.protocols.natneg.contracts.requests import InitRequest from frontends.gamespy.protocols.natneg.applications.handlers import AddressCheckHandler, ErtAckHandler, InitHandler, NatifyHandler import responses from frontends.gamespy.protocols.natneg.contracts.requests import ( @@ -18,10 +15,10 @@ PreInitState, RequestType, ) +from frontends.gamespy.library.exceptions.general import UniSpyException -from frontends.gamespy.protocols.natneg.contracts.responses import InitResponse from frontends.tests.gamespy.natneg.mock_objects import create_client -CmdHandlerBase._debug = True +UniSpyException._is_unittesting = True class HandlerTests(unittest.TestCase): diff --git a/src/frontends/tests/gamespy/natneg/mock_objects.py b/src/frontends/tests/gamespy/natneg/mock_objects.py index b0282c876..e0416fa72 100644 --- a/src/frontends/tests/gamespy/natneg/mock_objects.py +++ b/src/frontends/tests/gamespy/natneg/mock_objects.py @@ -1,23 +1,36 @@ -from frontends.tests.gamespy.library.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url +from frontends.tests.gamespy.library.mock_objects import ( + ConnectionMock, + LogMock, + RequestHandlerMock, + create_mock_url, +) from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.protocols.natneg.applications.client import Client from typing import cast -from frontends.gamespy.protocols.natneg.applications.handlers import AddressCheckHandler, ErtAckHandler, InitHandler, NatifyHandler +from frontends.gamespy.protocols.natneg.applications.handlers import ( + AddressCheckHandler, + ErtAckHandler, + InitHandler, + NatifyHandler, +) +from frontends.gamespy.library.exceptions.general import UniSpyException class ClientMock(Client): - pass def create_client() -> Client: + UniSpyException._is_unittesting = True handler = RequestHandlerMock() logger = LogMock() conn = ConnectionMock( handler=handler, - config=CONFIG.servers["NatNegotiation"], t_client=ClientMock, - logger=logger) + config=CONFIG.servers["NatNegotiation"], + t_client=ClientMock, + logger=logger, + ) config = CONFIG.servers["NatNegotiation"] create_mock_url(config, InitHandler, {"message": "ok"}) diff --git a/src/frontends/tests/gamespy/presence_connection_manager/general_request_tests.py b/src/frontends/tests/gamespy/presence_connection_manager/general_request_tests.py index 24ee397ab..6ff6aa8e7 100644 --- a/src/frontends/tests/gamespy/presence_connection_manager/general_request_tests.py +++ b/src/frontends/tests/gamespy/presence_connection_manager/general_request_tests.py @@ -7,7 +7,7 @@ ) LOGIN_AUTH_TOKEN = "\\login\\\\challenge\\xxxx\\authtoken\\xxxx\\userid\\0\\profileid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" LOGIN_UNIQUE_NICK = "\\login\\\\challenge\\xxxx\\uniquenick\\spyguy\\userid\\0\\profileid\\0\\namespaceid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" -LOGIN_USER = "\\login\\\\challenge\\xxxx\\user\\spyguy@spyguy@unispy.org\\userid\\0\\profileid\\0\\partnerid\\0\\namespaceid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" +LOGIN_USER = "\\login\\\\challenge\\xxxx\\user\\spyguy@spyguy@gamespy.com\\userid\\0\\profileid\\0\\partnerid\\0\\namespaceid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" class GeneralRequestTest(unittest.TestCase): @@ -55,7 +55,7 @@ def test_login_user(self) -> None: self.assertEqual(LoginType.NICK_EMAIL, request.type) self.assertEqual("xxxx", request.user_challenge) self.assertEqual("spyguy", request.nick) - self.assertEqual("spyguy@unispy.org", request.email) + self.assertEqual("spyguy@gamespy.com", request.email) self.assertEqual(0, request.namespace_id) self.assertEqual(0, request.user_id) self.assertEqual(0, request.profile_id) diff --git a/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py b/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py index 76cc8ba61..f24f69866 100644 --- a/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py +++ b/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py @@ -1,35 +1,55 @@ - +from typing import cast from frontends.gamespy.library.configs import CONFIG -from frontends.tests.gamespy.library.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url -from frontends.gamespy.protocols.presence_connection_manager.applications.client import Client -from frontends.gamespy.protocols.presence_connection_manager.applications.handlers import LoginHandler, NewUserHandler +from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.tests.gamespy.library.mock_objects import ( + ConnectionMock, + LogMock, + RequestHandlerMock, + create_mock_url, +) +from frontends.gamespy.protocols.presence_connection_manager.applications.client import ( + Client, +) +from frontends.gamespy.protocols.presence_connection_manager.applications.handlers import ( + LoginHandler, + NewUserHandler, +) class ClientMock(Client): pass -def create_client(): +def create_client() -> Client: + UniSpyException._is_unittesting = True handler = RequestHandlerMock() logger = LogMock() conn = ConnectionMock( handler=handler, - config=CONFIG.servers["PresenceConnectionManager"], t_client=ClientMock, - logger=logger) + config=CONFIG.servers["PresenceConnectionManager"], + t_client=ClientMock, + logger=logger, + ) config = CONFIG.servers["PresenceConnectionManager"] - create_mock_url(config, NewUserHandler, { - "user_id": 0, "profile_id": 0}) - create_mock_url(config, LoginHandler, {"response_proof": "7f2c9c6685570ea18b7207d2cbd72452", "data": { - "user_id": 0, - "profile_id": 0, - "nick": "test", - "email": "test@gamespy.com", - "unique_nick": "test_unique", - "password_hash": "password", - "email_verified_flag": True, - "namespace_id": 0, - "sub_profile_id": 0, - "banned_flag": False - }}) + create_mock_url(config, NewUserHandler, {"user_id": 0, "profile_id": 0}) + create_mock_url( + config, + LoginHandler, + { + "response_proof": "7f2c9c6685570ea18b7207d2cbd72452", + "data": { + "user_id": 0, + "profile_id": 0, + "nick": "test", + "email": "test@gamespy.com", + "unique_nick": "test_unique", + "password_hash": "password", + "email_verified_flag": True, + "namespace_id": 0, + "sub_profile_id": 0, + "banned_flag": False, + }, + }, + ) - return conn._client + return cast(Client, conn._client) diff --git a/src/frontends/tests/gamespy/presence_search_player/handler_tests.py b/src/frontends/tests/gamespy/presence_search_player/handler_tests.py index e3a9bba50..11af9560b 100644 --- a/src/frontends/tests/gamespy/presence_search_player/handler_tests.py +++ b/src/frontends/tests/gamespy/presence_search_player/handler_tests.py @@ -8,15 +8,15 @@ CHECK1 = "\\check\\\\nick\\spyguy\\email\\spyguy@gamespy.com\\pass\\0000\\final\\" SEARCH_1 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\namespaceid\\0\\uniquenick\\spyguy\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" -SEARCH_2 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\nick\\spyguy\\email\\spyguy@unispy.org\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" +SEARCH_2 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\nick\\spyguy\\email\\spyguy@gamespy.com\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" SEARCH_3 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\nick\\spyguy\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" -SEARCH_4 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\email\\spyguy@unispy.org\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" +SEARCH_4 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\email\\spyguy@gamespy.com\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" SEARCH_UNIQUENICK = "\\searchunique\\\\sesskey\\xxxx\\profileid\\0\\uniquenick\\spyguy\\namespaces\\1,2,3,4,5\\gamename\\gmtest\\final\\" -VALID = "\\valid\\\\email\\spyguy@unispy.org\\partnerid\\1\\gamename\\gmtest\\final\\" +VALID = "\\valid\\\\email\\spyguy@gamespy.com\\partnerid\\1\\gamename\\gmtest\\final\\" -NICKS = "\\nicks\\\\email\\spyguy@unispy.org\\passenc\\xxxxx\\namespaceid\\0\\partnerid\\0\\gamename\\gmtest\\final\\" +NICKS = "\\nicks\\\\email\\spyguy@gamespy.com\\passenc\\xxxxx\\namespaceid\\0\\partnerid\\0\\gamename\\gmtest\\final\\" PMATCH = "\\pmatch\\\\sesskey\\123456\\profileid\\0\\productid\\0\\final\\" @@ -49,7 +49,7 @@ def test_profile(self): request = SearchRequest(SEARCH_2) request.parse() self.assertEqual("spyguy", request.nick) - self.assertEqual("spyguy@unispy.org", request.email) + self.assertEqual("spyguy@gamespy.com", request.email) request = SearchRequest(SEARCH_3) request.parse() @@ -57,11 +57,11 @@ def test_profile(self): request = SearchRequest(SEARCH_4) request.parse() - self.assertEqual("spyguy@unispy.org", request.email) + self.assertEqual("spyguy@gamespy.com", request.email) handler = SearchHandler(client, request) handler.handle() - self.assertEqual("\\bsr\\0\\nick\\spyguy\\uniquenick\\spyguy\\namespaceid\\0\\firstname\\spy\\lastname\\guy\\email\\spyguy@unispy.org\\bsrdone\\\\more\\0\\final\\", + self.assertEqual("\\bsr\\0\\nick\\spyguy\\uniquenick\\spyguy\\namespaceid\\0\\firstname\\spy\\lastname\\guy\\email\\spyguy@gamespy.com\\bsrdone\\\\more\\0\\final\\", handler._response.sending_buffer) diff --git a/src/frontends/tests/gamespy/presence_search_player/mock_objects.py b/src/frontends/tests/gamespy/presence_search_player/mock_objects.py index 6dd37432f..924f487b3 100644 --- a/src/frontends/tests/gamespy/presence_search_player/mock_objects.py +++ b/src/frontends/tests/gamespy/presence_search_player/mock_objects.py @@ -5,12 +5,14 @@ from frontends.gamespy.protocols.presence_search_player.applications.handlers import CheckHandler, SearchHandler from frontends.gamespy.protocols.presence_search_player.contracts.results import CheckResult, SearchResult +from frontends.gamespy.library.exceptions.general import UniSpyException class ClientMock(Client): pass def create_client() -> Client: + UniSpyException._is_unittesting = True handler = RequestHandlerMock() logger = LogMock() conn = ConnectionMock( @@ -22,6 +24,6 @@ def create_client() -> Client: {"profile_id": 0}).model_dump()) create_mock_url(config, SearchHandler, SearchResult.model_validate({"data": [{"profile_id": 0, "nick": "spyguy", "uniquenick": "spyguy", - "email": "spyguy@unispy.org", "firstname": "spy", "lastname": "guy", "namespace_id": 0}]}).model_dump()) + "email": "spyguy@gamespy.com", "firstname": "spy", "lastname": "guy", "namespace_id": 0}]}).model_dump()) return cast(Client, conn._client) diff --git a/src/frontends/tests/gamespy/query_report/mock_objects.py b/src/frontends/tests/gamespy/query_report/mock_objects.py index aeb5c41ad..d4ca1a0c7 100644 --- a/src/frontends/tests/gamespy/query_report/mock_objects.py +++ b/src/frontends/tests/gamespy/query_report/mock_objects.py @@ -4,6 +4,7 @@ from frontends.gamespy.protocols.query_report.v2.applications.handlers import AvailableHandler, HeartBeatHandler, KeepAliveHandler from frontends.gamespy.protocols.query_report.v2.contracts.results import HeartBeatResult from frontends.tests.gamespy.library.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url +from frontends.gamespy.library.exceptions.general import UniSpyException class ClientMock(Client): @@ -11,6 +12,7 @@ class ClientMock(Client): def create_client() -> Client: + UniSpyException._is_unittesting = True handler = RequestHandlerMock() logger = LogMock() conn = ConnectionMock( diff --git a/src/frontends/tests/gamespy/web_services/auth_tests.py b/src/frontends/tests/gamespy/web_services/auth_tests.py index 7893d7442..bac197327 100644 --- a/src/frontends/tests/gamespy/web_services/auth_tests.py +++ b/src/frontends/tests/gamespy/web_services/auth_tests.py @@ -16,7 +16,7 @@ 0 0 0 - spyguy@unispy.org + spyguy@gamespy.com spyguy XXXXXXXXXXX @@ -116,7 +116,7 @@ def test_login_profile(self): self.assertEqual(0, request.game_id) self.assertEqual(0, request.partner_code) self.assertEqual(0, request.namespace_id) - self.assertEqual("spyguy@unispy.org", request.email) + self.assertEqual("spyguy@gamespy.com", request.email) self.assertEqual("spyguy", request.uniquenick) self.assertEqual("XXXXXXXXXXX", request.cdkey) self.assertEqual("XXXXXXXXXXX", request.password) From bfaeb3ad784b52ec221343a789ef9fcae3a1ec91 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Wed, 7 May 2025 14:48:27 +0000 Subject: [PATCH 180/231] Update channel message broadcast logic. --- src/backends/library/database/pg_orm.py | 82 ++++++++------ .../protocols/gamespy/chat/handlers.py | 19 +++- src/backends/protocols/gamespy/chat/helper.py | 10 ++ .../library/abstractions/connections.py | 1 - .../gamespy/library/abstractions/contracts.py | 1 - .../gamespy/library/network/brockers.py | 19 ++-- .../protocols/chat/abstractions/handler.py | 21 ++++ .../protocols/chat/applications/handlers.py | 2 +- .../protocols/chat/contracts/requests.py | 41 +++---- .../protocols/chat/contracts/responses.py | 106 +++++++++++------- .../protocols/chat/contracts/results.py | 24 ++-- .../tests/gamespy/chat/mock_objects.py | 10 +- 12 files changed, 208 insertions(+), 128 deletions(-) diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 6a83a5083..82062ced4 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -1,4 +1,3 @@ -from typing import TYPE_CHECKING, cast from frontends.gamespy.library.configs import CONFIG from datetime import datetime from sqlalchemy import ( @@ -13,16 +12,22 @@ DateTime, text, UUID, - create_engine ) from sqlalchemy.orm.session import Session from sqlalchemy.dialects.postgresql import JSONB, INET -from sqlalchemy.ext.declarative import DeclarativeMeta -from sqlalchemy.orm import sessionmaker, declarative_base +from sqlalchemy.orm import sessionmaker from sqlalchemy.types import TypeDecorator -from frontends.gamespy.protocols.natneg.aggregations.enums import NatClientIndex, NatPortType -from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import FriendRequestStatus, GPStatusCode -from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus +from frontends.gamespy.protocols.natneg.aggregations.enums import ( + NatClientIndex, + NatPortType, +) +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( + FriendRequestStatus, + GPStatusCode, +) +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import ( + GameServerStatus, +) import sqlalchemy as sa import enum from sqlalchemy.orm.decl_api import DeclarativeBase @@ -52,16 +57,15 @@ class Base(DeclarativeBase): class Users(Base): __tablename__ = "users" - userid: Column[int] = Column( - Integer, primary_key=True, autoincrement=True) + userid: Column[int] = Column(Integer, primary_key=True, autoincrement=True) email: Column[str] = Column(String, nullable=False) password: Column[str] = Column(String, nullable=False) - emailverified: Column[bool] = Column( - Boolean, default=True, nullable=False) + emailverified: Column[bool] = Column(Boolean, default=True, nullable=False) lastip: Column[str] = Column(INET) lastonline: Column[datetime] = Column(DateTime, default=datetime.now()) createddate: Column[datetime] = Column( - DateTime, default=datetime.now(), nullable=False) + DateTime, default=datetime.now(), nullable=False + ) banned: Column[bool] = Column(Boolean, default=False, nullable=False) deleted: Column[bool] = Column(Boolean, default=False, nullable=False) @@ -69,14 +73,11 @@ class Users(Base): class Profiles(Base): __tablename__ = "profiles" - profileid: Column[int] = Column( - Integer, primary_key=True, autoincrement=True) - userid: Column[int] = Column( - Integer, ForeignKey("users.userid"), nullable=False) + profileid: Column[int] = Column(Integer, primary_key=True, autoincrement=True) + userid: Column[int] = Column(Integer, ForeignKey("users.userid"), nullable=False) nick: Column[str] = Column(String, nullable=False) serverflag: Column[int] = Column(Integer, nullable=False, default=0) - status = Column( - IntEnum(GPStatusCode), default=GPStatusCode.OFFLINE) + status = Column(IntEnum(GPStatusCode), default=GPStatusCode.OFFLINE) statstring: Column[str] = Column(String, default="I love UniSpy") extra_info: Column[JSONB] = Column(JSONB) @@ -104,8 +105,7 @@ class Blocked(Base): __tablename__ = "blocked" blockid = Column(Integer, primary_key=True, autoincrement=True) - profileid = Column(Integer, ForeignKey( - "profiles.profileid"), nullable=False) + profileid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) namespaceid = Column(Integer, nullable=False) targetid = Column(Integer, nullable=False) @@ -114,8 +114,7 @@ class Friends(Base): __tablename__ = "friends" friendid = Column(Integer, primary_key=True, autoincrement=True) - profileid = Column(Integer, ForeignKey( - "profiles.profileid"), nullable=False) + profileid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) targetid = Column(Integer, nullable=False) namespaceid = Column(Integer, nullable=False) @@ -124,14 +123,15 @@ class FriendAddRequest(Base): __tablename__ = "addrequests" addrequestid = Column(Integer, primary_key=True, autoincrement=True) - profileid = Column(Integer, ForeignKey( - "profiles.profileid"), nullable=False) - targetid = Column(Integer, ForeignKey( - "profiles.profileid"), nullable=False) + profileid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) + targetid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) namespaceid = Column(Integer, nullable=False) reason = Column(String, nullable=False) - status = Column(IntEnum(FriendRequestStatus), nullable=False, - default=FriendRequestStatus.PENDING) + status = Column( + IntEnum(FriendRequestStatus), + nullable=False, + default=FriendRequestStatus.PENDING, + ) class Games(Base): @@ -175,8 +175,7 @@ class PStorage(Base): __tablename__ = "pstorage" pstorageid = Column(Integer, primary_key=True, autoincrement=True) - profileid = Column(Integer, ForeignKey( - "profiles.profileid"), nullable=False) + profileid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) ptype = Column(Integer, nullable=False) dindex = Column(Integer, nullable=False) data = Column(JSONB) @@ -214,6 +213,7 @@ class NatFailCaches(Base): public_ip_address2 = Column(INET, nullable=False) update_time = Column(DateTime, nullable=False) + # class ReportPackets(Base): # __tablename__ = "report_packets" # public_ip_address1 = Column(String, nullable=False) @@ -250,6 +250,7 @@ class ChatUserCaches(Base): """ each user only have a unique nick caches, but have multiple user caches """ + __tablename__ = "chat_user_caches" server_id = Column(UUID, nullable=False) nick_name = Column(String, primary_key=True, nullable=False) @@ -263,12 +264,21 @@ class ChatUserCaches(Base): class ChatChannelUserCaches(Base): __tablename__ = "chat_channel_user_caches" - nick_name = Column(String, ForeignKey( - "chat_user_caches.nick_name"), primary_key=True, nullable=False) - user_name = Column(String, ForeignKey( - "chat_user_caches.user_name"), primary_key=True, nullable=False) - channel_name = Column(String, ForeignKey( - "chat_channel_caches.channel_name"), nullable=False) + nick_name = Column( + String, + ForeignKey("chat_user_caches.nick_name"), + primary_key=True, + nullable=False, + ) + user_name = Column( + String, + ForeignKey("chat_user_caches.user_name"), + primary_key=True, + nullable=False, + ) + channel_name = Column( + String, ForeignKey("chat_channel_caches.channel_name"), nullable=False + ) update_time = Column(DateTime, nullable=False) # can we directly store the flags? is_voiceable = Column(Boolean, nullable=False) diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index 31e4267e4..705c9cc89 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -59,6 +59,7 @@ CryptResult, GetCKeyResult, GetKeyResult, + JoinResult, ListResult, NamesResult, NickResult, @@ -361,6 +362,20 @@ async def _data_operate(self) -> None: max_num_user=100, ) ChannelHelper.join(self._channel, self._user) + self._nicks = ChannelHelper.get_channel_all_nicks(self._channel) + + async def _result_construct(self) -> None: + assert self._user is not None + assert isinstance(self._user.user_name, str) + assert isinstance(self._user.nick_name, str) + assert self._channel is not None + assert isinstance(self._channel.modes, list) + self._result = JoinResult( + joiner_user_name=self._user.user_name, + joiner_nick_name=self._user.nick_name, + all_channel_user_nicks=self._nicks, + channel_modes=self._channel.modes, + ) class KickHandler(ChannelHandlerBase): @@ -415,13 +430,13 @@ async def _request_check(self) -> None: async def _data_operate(self) -> None: assert self._channel - self._data = ChannelHelper.get_all_user_nick_string(self._channel) + self._nicks = ChannelHelper.get_channel_all_nicks(self._channel) async def _result_construct(self) -> None: assert self._user assert isinstance(self._user.nick_name, str) self._result = NamesResult( - all_channel_user_nicks=self._data, + all_channel_nicks=self._nicks, channel_name=self._request.channel_name, requester_nick_name=self._user.nick_name, ) diff --git a/src/backends/protocols/gamespy/chat/helper.py b/src/backends/protocols/gamespy/chat/helper.py index 170638837..29990c3d3 100644 --- a/src/backends/protocols/gamespy/chat/helper.py +++ b/src/backends/protocols/gamespy/chat/helper.py @@ -276,3 +276,13 @@ def get_all_user_nick_string(channel: ChatChannelCaches) -> str: if user != users[-1]: nicks += " " return nicks + + @staticmethod + def get_channel_all_nicks(channel: ChatChannelCaches) -> list[str]: + assert channel is not None + assert isinstance(channel.channel_name, str) + users = data.get_channel_user_caches_by_name(channel.channel_name) + nicks = [] + for user in users: + nicks.append(user.nick_name) + return nicks diff --git a/src/frontends/gamespy/library/abstractions/connections.py b/src/frontends/gamespy/library/abstractions/connections.py index 47cd22020..2b3f5bbdf 100644 --- a/src/frontends/gamespy/library/abstractions/connections.py +++ b/src/frontends/gamespy/library/abstractions/connections.py @@ -1,6 +1,5 @@ import abc import socketserver -from typing import Optional from frontends.gamespy.library.abstractions.client import ClientBase from frontends.gamespy.library.log.log_manager import LogWriter diff --git a/src/frontends/gamespy/library/abstractions/contracts.py b/src/frontends/gamespy/library/abstractions/contracts.py index b4dc52e30..fddcabe5b 100644 --- a/src/frontends/gamespy/library/abstractions/contracts.py +++ b/src/frontends/gamespy/library/abstractions/contracts.py @@ -1,5 +1,4 @@ import abc -from copy import deepcopy from typing import Optional from pydantic import BaseModel diff --git a/src/frontends/gamespy/library/network/brockers.py b/src/frontends/gamespy/library/network/brockers.py index fdd6db173..b4c63f2e5 100644 --- a/src/frontends/gamespy/library/network/brockers.py +++ b/src/frontends/gamespy/library/network/brockers.py @@ -1,11 +1,13 @@ import threading from typing import Optional, Callable +from uuid import UUID from redis import Redis import websocket from frontends.gamespy.library.abstractions.brocker import BrockerBase from redis.client import PubSub from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage websocket.enableTrace(True) @@ -71,11 +73,10 @@ def subscribe(self): def unsubscribe(self): self._subscriber.close() - def publish_message(self, message: str): - raise NotImplementedError("Websocket brocker only use to listen to message.") + def publish_message(self, message: BrockerMessage): if self._publisher is None: raise ValueError("websocket connection is not established") - self._publisher.send(message) + self._publisher.send(message.model_dump_json()) if __name__ == "__main__": @@ -85,8 +86,12 @@ def publish_message(self, message: str): call_back_func=print, ) ws.subscribe() - import json + msg = BrockerMessage( + server_id=UUID("08ed7859-1d9e-448b-8fda-dabb845d85f9"), + channel_name="gmtest", + sender_ip_address="192.168.0.1", + sender_port=80, + message="hello", + ) + ws.publish_message(msg) - ws.publish_message(json.dumps({"channel_name": "test", "message": "hello"})) - while True: - pass diff --git a/src/frontends/gamespy/protocols/chat/abstractions/handler.py b/src/frontends/gamespy/protocols/chat/abstractions/handler.py index c55574f0d..5ac761817 100644 --- a/src/frontends/gamespy/protocols/chat/abstractions/handler.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/handler.py @@ -1,5 +1,7 @@ +from backends.library.database.pg_orm import ChatChannelCaches from frontends.gamespy.protocols.chat.aggregates.enums import MessageType from frontends.gamespy.protocols.chat.abstractions.contract import ( + BrockerMessage, RequestBase, ResponseBase, ResultBase, @@ -70,6 +72,7 @@ class ChannelHandlerBase(PostLoginHandlerBase): _request: ChannelRequestBase _response: ResponseBase _result: ResultBase + _channel: ChatChannelCaches def __init__(self, client: ClientBase, request: RequestBase): super().__init__(client, request) @@ -78,6 +81,24 @@ def __init__(self, client: ClientBase, request: RequestBase): frontends -> backends -> backends_api -> websocket broadcast. -> frontends.client.brocker.receive """ + def _response_send(self) -> None: + super()._response_send() + # send message to backend websocket + self.broadcast_message() + + def broadcast_message(self): + assert self._client.brocker + assert self._channel + assert isinstance(self._channel.channel_name, str) + msg = BrockerMessage( + server_id=self._client.server_config.server_id, + channel_name=self._channel.channel_name, + sender_ip_address=self._client.connection.remote_ip, + sender_port=self._client.connection.remote_port, + message=self._response.sending_buffer, + ) + self._client.brocker.publish_message(msg) + # region Message class MessageRequestBase(ChannelRequestBase): diff --git a/src/frontends/gamespy/protocols/chat/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py index 9b6ab28f8..88442ca9d 100644 --- a/src/frontends/gamespy/protocols/chat/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -201,7 +201,7 @@ class QuitHandler(CmdHandlerBase): _request: QuitRequest def __init__(self, client: ClientBase, request: QuitRequest): - assert isinstance(request, QuitHandler) + assert isinstance(request, QuitRequest) super().__init__(client, request) diff --git a/src/frontends/gamespy/protocols/chat/contracts/requests.py b/src/frontends/gamespy/protocols/chat/contracts/requests.py index 22cc4be31..9892e7087 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/requests.py +++ b/src/frontends/gamespy/protocols/chat/contracts/requests.py @@ -1,4 +1,7 @@ -from frontends.gamespy.protocols.chat.abstractions.handler import ChannelRequestBase, MessageRequestBase +from frontends.gamespy.protocols.chat.abstractions.handler import ( + ChannelRequestBase, + MessageRequestBase, +) from frontends.gamespy.protocols.chat.aggregates.enums import ( GetKeyRequestType, ModeOperationType, @@ -12,9 +15,14 @@ convert_kvstring_to_dictionary, ) from frontends.gamespy.protocols.chat.abstractions.contract import RequestBase -from frontends.gamespy.protocols.chat.aggregates.enums import LoginRequestType, WhoRequestType +from frontends.gamespy.protocols.chat.aggregates.enums import ( + LoginRequestType, + WhoRequestType, +) from frontends.gamespy.protocols.chat.aggregates.exceptions import ChatException -from frontends.gamespy.protocols.chat.aggregates.exceptions import NickNameInUseException +from frontends.gamespy.protocols.chat.aggregates.exceptions import ( + NickNameInUseException, +) # General @@ -70,8 +78,7 @@ def parse(self): ) try: self.max_number_of_channels = int(self._cmd_params[0]) - - except Exception as e: + except Exception: raise ChatException("The max number format is incorrect.") self.filter = self._cmd_params[1] @@ -113,8 +120,7 @@ def parse(self): super().parse() try: self.namespace_id = int(self._cmd_params[0]) - - except Exception as e: + except Exception: raise ChatException("The namespaceid format is incorrect.") if self._cmd_params[1] == "*": @@ -126,7 +132,7 @@ def parse(self): profile_nick_index = self._long_param.index("@") self.nick_name = self._long_param[0:profile_nick_index] - self.email = self._long_param[profile_nick_index + 1:] + self.email = self._long_param[profile_nick_index + 1 :] return self.request_type = LoginRequestType.UNIQUE_NICK_LOGIN @@ -135,7 +141,7 @@ def parse(self): class NickRequest(RequestBase): - _invalid_chars = "#@$%^&*()~" + _invalid_chars = "#@$%^&()~" nick_name: str def parse(self): @@ -153,8 +159,7 @@ def parse(self): raise NickNameInUseException( self.nick_name, self.nick_name, - f"The nick name: { - self.nick_name} contains invalid character.", + f"The nick name: {self.nick_name} contains invalid character.", ) @@ -337,7 +342,6 @@ def parse(self): self.cookie = self._cmd_params[2] if "\0" not in self._long_param and "\\" not in self._long_param: - raise ChatException("The key provide is incorrect.") self.keys = self._long_param.strip("\\").rstrip("\0").split("\\") @@ -417,8 +421,7 @@ def parse(self): elif len(self._cmd_params) == 2 or len(self._cmd_params) == 3: self.type = ModeRequestType.SET_CHANNEL_MODES self.mode_flag = self._cmd_params[1] - modeFlags = [s for s in re.split( - r"(?=\+|\-)", self.mode_flag) if s.strip()] + modeFlags = [s for s in re.split(r"(?=\+|\-)", self.mode_flag) if s.strip()] modeFlags = list(filter(None, modeFlags)) for flag in modeFlags: match flag: @@ -464,8 +467,7 @@ def parse(self): ModeOperationType.REMOVE_SECRET_CHANNEL_FLAG ) case "+i": - self.mode_operations.append( - ModeOperationType.SET_INVITED_ONLY) + self.mode_operations.append(ModeOperationType.SET_INVITED_ONLY) case "-i": self.mode_operations.append( ModeOperationType.REMOVE_INVITED_ONLY @@ -597,12 +599,10 @@ class SetCKeyRequest(ChannelRequestBase): def parse(self) -> None: super().parse() if self._cmd_params is None: - raise ChatException( - "The cmdParams from SETCKEY request are missing.") + raise ChatException("The cmdParams from SETCKEY request are missing.") if self._long_param is None: - raise ChatException( - "The longParam from SETCKEY request is missing.") + raise ChatException("The longParam from SETCKEY request is missing.") self.channel_name = self._cmd_params[0] self.nick_name = self._cmd_params[1] @@ -642,6 +642,7 @@ def parse(self) -> None: self.request_type = TopicRequestType.SET_CHANNEL_TOPIC self.channel_topic = self._long_param + # region Message diff --git a/src/frontends/gamespy/protocols/chat/contracts/responses.py b/src/frontends/gamespy/protocols/chat/contracts/responses.py index 4dd87bd88..74b2c8cb1 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/responses.py +++ b/src/frontends/gamespy/protocols/chat/contracts/responses.py @@ -1,5 +1,39 @@ from frontends.gamespy.protocols.chat.abstractions.handler import ChannelResponseBase -from frontends.gamespy.protocols.chat.aggregates.enums import ModeRequestType, WhoRequestType +from frontends.gamespy.protocols.chat.aggregates.enums import ( + ModeRequestType, + WhoRequestType, +) +from frontends.gamespy.protocols.chat.aggregates.response_name import ( + ABOVE_THE_TABLE_MSG, + CD_KEY, + CHANNEL_MODELS, + END_GET_CKEY, + END_GET_KEY, + END_OF_NAMES, + END_OF_WHO, + END_OF_WHO_IS, + GET_CHAN_KEY, + GET_CKEY, + GET_KEY, + JOIN, + KICK, + LIST_END, + LIST_START, + LOGIN, + NAME_REPLY, + NO_TOPIC, + NOTICE, + PART, + PONG, + PRIVATE_MSG, + SECURE_KEY, + TOPIC, + UNDER_THE_TABLE_MSG, + WELCOME, + WHO_IS_CHANNELS, + WHO_IS_USER, + WHO_REPLY, +) from frontends.gamespy.protocols.chat.contracts.results import ( GetCKeyResult, GetChannelKeyResult, @@ -39,15 +73,14 @@ PrivateRequest, UTMRequest, GetKeyRequest, - WhoRequest - + WhoRequest, ) from frontends.gamespy.protocols.chat.abstractions.contract import ( SERVER_DOMAIN, ResponseBase, ) from frontends.gamespy.library.encryption.gs_encryption import CLIENT_KEY, SERVER_KEY -from frontends.gamespy.protocols.chat.aggregates.response_name import * +from frontends.tests.gamespy.chat.request_tests import USER_IP # region General @@ -65,7 +98,9 @@ def __init__(self) -> None: pass def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {SECURE_KEY} * {CLIENT_KEY} {SERVER_KEY}\r\n" # noqa + self.sending_buffer = ( + f":{SERVER_DOMAIN} {SECURE_KEY} * {CLIENT_KEY} {SERVER_KEY}\r\n" # noqa + ) class GetKeyResponse(ResponseBase): @@ -134,8 +169,7 @@ def __init__(self, result: PingResult) -> None: self._result = result def build(self) -> None: - self.sending_buffer = f":{ - self._result.requester_irc_prefix} {PONG}\r\n" + self.sending_buffer = f":{self._result.requester_irc_prefix} {PONG}\r\n" class UserIPResponse(ResponseBase): @@ -146,7 +180,9 @@ def __init__(self, result: UserIPResult) -> None: self._result = result def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {USER_IP} :@{self._result.remote_ip_address}\r\n" # noqa + self.sending_buffer = ( + f":{SERVER_DOMAIN} {USER_IP} :@{self._result.remote_ip_address}\r\n" + ) class WhoIsResponse(ResponseBase): @@ -191,10 +227,10 @@ def build(self): if self._request.request_type == WhoRequestType.GET_CHANNEL_USER_INFO: # if len(self._result.infos) > 0: - self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.channel_name} * :End of WHO.\r\n" # noqa + self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.channel_name} * :End of WHO.\r\n" # noqa elif self._request.request_type == WhoRequestType.GET_USER_INFO: # if len(self._result.infos) > 0: - self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.nick_name} * :End of WHO.\r\n" # noqa + self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.nick_name} * :End of WHO.\r\n" # noqa # region Channel @@ -205,8 +241,7 @@ class GetChannelKeyResponse(ChannelResponseBase): _result: GetChannelKeyResult def build(self) -> None: - self.sending_buffer = f":{self._result.channel_user_irc_prefix} { - GET_CHAN_KEY} * {self._result.channel_name} {self._request.cookie} {self._result.values}\r\n" + self.sending_buffer = f":{self._result.channel_user_irc_prefix} {GET_CHAN_KEY} * {self._result.channel_name} {self._request.cookie} {self._result.values}\r\n" class GetCKeyResponse(ResponseBase): @@ -236,9 +271,9 @@ def __init__(self, request: JoinRequest, result: JoinResult) -> None: super().__init__(request, result) def build(self) -> None: + joiner_irc_prefix = f"{self._result.joiner_nick_name}!{self._result.joiner_user_name}@{SERVER_DOMAIN}" self.sending_buffer = ( - f"{self._result.joiner_prefix} {JOIN} { - self._request.channel_name}\r\n" + f"{joiner_irc_prefix} {JOIN} {self._request.channel_name}\r\n" ) @@ -252,8 +287,8 @@ def __init__(self, request: KickRequest, result: KickResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f"{self._result.kicker_irc_prefix} {KICK} { - self._result.channel_name} {self._result.kickee_nick_name} :{self._request.reason}\r\n" + kicker_irc_prefix = f"{self._result.kicker_nick_name}!{self._result.kicker_user_name}@{SERVER_DOMAIN}" + self.sending_buffer = f"{kicker_irc_prefix} {KICK} {self._result.channel_name} {self._result.kickee_nick_name} :{self._request.reason}\r\n" class ModeResponse(ResponseBase): @@ -267,8 +302,7 @@ def __init__(self, request: ModeRequest, result: ModeResult) -> None: def build(self) -> None: if self._request.type == ModeRequestType.GET_CHANNEL_MODES: - self.sending_buffer = f":{SERVER_DOMAIN} { - CHANNEL_MODELS} * {self._result.channel_modes} {self._result.channel_modes}\r\n" + self.sending_buffer = f":{SERVER_DOMAIN} {CHANNEL_MODELS} * {self._result.channel_modes} {self._result.channel_modes}\r\n" class NamesResponse(ChannelResponseBase): @@ -281,11 +315,8 @@ def __init__(self, request: NamesRequest, result: NamesResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {NAME_REPLY} {self._result.requester_nick_name} = { - self._result.channel_name} :{self._result.all_channel_user_nicks}\r\n" - - self.sending_buffer = f":{SERVER_DOMAIN} {END_OF_NAMES} { - self._result.requester_nick_name} {self._result.channel_name} :End of NAMES list. \r\n" + self.sending_buffer = f":{SERVER_DOMAIN} {NAME_REPLY} {self._result.requester_nick_name} = {self._result.channel_name} :{self._result.all_channel_nicks}\r\n" + self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_NAMES} {self._result.requester_nick_name} {self._result.channel_name} :End of NAMES list. \r\n" class PartResponse(ResponseBase): @@ -298,8 +329,7 @@ def __init__(self, request: PartRequest, result: PartResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.leaver_irc_prefix} { - PART} {self._result.channel_name} :{self._request.reason}\r\n" + self.sending_buffer = f":{self._result.leaver_irc_prefix} {PART} {self._result.channel_name} :{self._request.reason}\r\n" class SetChannelKeyResponse(ChannelResponseBase): @@ -314,8 +344,7 @@ def __init__( super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.channel_user_irc_prefix} { - GET_CHAN_KEY} * {self._request.channel_name} BCAST {self._request.key_value_string}\r\n" + self.sending_buffer = f":{self._result.channel_user_irc_prefix} {GET_CHAN_KEY} * {self._request.channel_name} BCAST {self._request.key_value_string}\r\n" class SetCKeyResponse(ResponseBase): @@ -326,11 +355,9 @@ def __init__(self, request: SetCKeyRequest) -> None: super().__init__(request, None) def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {GET_CKEY} * {self._request.channel_name} { - self._request.nick_name} {self._request.cookie} {self._request.key_value_string}\r\n" + self.sending_buffer = f":{SERVER_DOMAIN} {GET_CKEY} * {self._request.channel_name} {self._request.nick_name} {self._request.cookie} {self._request.key_value_string}\r\n" - self.sending_buffer = f":{SERVER_DOMAIN} {END_GET_CKEY} * {self._request.channel_name} { - self._request.nick_name} {self._request.cookie} :End Of SETCKEY.\r\n" + self.sending_buffer += f":{SERVER_DOMAIN} {END_GET_CKEY} * {self._request.channel_name} {self._request.nick_name} {self._request.cookie} :End Of SETCKEY.\r\n" class TopicResponse(ResponseBase): @@ -348,8 +375,7 @@ def build(self) -> None: f":{SERVER_DOMAIN} {NO_TOPIC} * {self._result.channel_name}\r\n" ) else: - self.sending_buffer = f":{SERVER_DOMAIN} { - TOPIC} * {self._result.channel_name} :{self._result.channel_topic}\r\n" + self.sending_buffer = f":{SERVER_DOMAIN} {TOPIC} * {self._result.channel_name} :{self._result.channel_topic}\r\n" # region Message @@ -365,8 +391,7 @@ def __init__(self, request: ATMRequest, result: ATMResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} {ABOVE_THE_TABLE_MSG} { - self._result.target_name} :{self._request.message}\r\n" + self.sending_buffer = f":{self._result.user_irc_prefix} {ABOVE_THE_TABLE_MSG} {self._result.target_name} :{self._request.message}\r\n" class NoticeResponse(ResponseBase): @@ -380,8 +405,7 @@ def __init__(self, request: NoticeRequest, result: NoticeResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} {NOTICE} { - self._result.target_name} {self._request.message}\r\n" + self.sending_buffer = f":{self._result.user_irc_prefix} {NOTICE} {self._result.target_name} {self._request.message}\r\n" class PrivateResponse(ResponseBase): @@ -394,8 +418,7 @@ def __init__(self, request: PrivateRequest, result: PrivateResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} {PRIVATE_MSG} { - self._result.target_name} :{self._request.message}\r\n" + self.sending_buffer = f":{self._result.user_irc_prefix} {PRIVATE_MSG} {self._result.target_name} :{self._request.message}\r\n" class UTMResponse(ResponseBase): @@ -408,5 +431,6 @@ def __init__(self, request: UTMRequest, result: UTMResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} { - UNDER_THE_TABLE_MSG} {self._result.target_name} :{self._request.message}" + self.sending_buffer = f":{self._result.user_irc_prefix} {UNDER_THE_TABLE_MSG} { + self._result.target_name + } :{self._request.message}" diff --git a/src/frontends/gamespy/protocols/chat/contracts/results.py b/src/frontends/gamespy/protocols/chat/contracts/results.py index ba8290ca3..cea9e6782 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/results.py +++ b/src/frontends/gamespy/protocols/chat/contracts/results.py @@ -19,6 +19,7 @@ class ListInfo(BaseModel): channel_name: str total_channel_user: int channel_topic: str + user_irc_prefix: str channel_info_list: list[ListInfo] = [] """(channel_name:str,total_channel_user:int,channel_topic:str)""" @@ -70,6 +71,7 @@ class WhoInfo(BaseModel): public_ip_addr: str nick_name: str modes: str + infos: list[WhoInfo] @@ -93,16 +95,16 @@ class GetCKeyInfos(BaseModel): class JoinResult(ResultBase): - joiner_prefix: str + joiner_user_name: str joiner_nick_name: str - all_channel_user_nicks: str - channel_modes: str + all_channel_user_nicks: list[str] + channel_modes: list[str] class KickResult(ResultBase): channel_name: str - kicker_irc_prefix: str kicker_nick_name: str + kicker_user_name: str kickee_nick_name: str @@ -113,7 +115,7 @@ class ModeResult(ResultBase): class NamesResult(ResultBase): - all_channel_user_nicks: str + all_channel_nicks: list[str] channel_name: str requester_nick_name: str @@ -137,16 +139,10 @@ class SetChannelKeyResult(ResultBase): if __name__ == "__main__": dd = { "infos": [ - { - "nick_name": "John", - "user_values": "12345" - }, - { - "nick_name": "Alice", - "user_values": "67890" - } + {"nick_name": "John", "user_values": "12345"}, + {"nick_name": "Alice", "user_values": "67890"}, ], - "channel_name": "example_channel" + "channel_name": "example_channel", } result = GetCKeyResult(**dd) pass diff --git a/src/frontends/tests/gamespy/chat/mock_objects.py b/src/frontends/tests/gamespy/chat/mock_objects.py index cc01a659e..4bd2c994a 100644 --- a/src/frontends/tests/gamespy/chat/mock_objects.py +++ b/src/frontends/tests/gamespy/chat/mock_objects.py @@ -15,7 +15,7 @@ UserHandler, UserIPHandler, WhoHandler, - LoginHandler + LoginHandler, ) from frontends.gamespy.protocols.chat.contracts.results import ( GetCKeyResult, @@ -60,10 +60,10 @@ def create_client() -> Client: config, JoinHandler, JoinResult( - joiner_nick_name="unispy", - joiner_prefix="xiaojiuwo!unispy@UNISPYSERVER", - all_channel_user_nicks="test", - channel_modes="+q", + joiner_nick_name="xiaojiuwo", + joiner_user_name="unispy", + all_channel_user_nicks=["unispy1", "unispy2"], + channel_modes=["+q"], ).model_dump(), ) create_mock_url(config, UserHandler, {"message": "ok"}) From 9508aca01d6ea0295ae39712e336fad6dfe79f63 Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Thu, 8 May 2025 09:05:27 +0000 Subject: [PATCH 181/231] Update mocked objects. --- .../library/abstractions/connections.py | 8 ------- .../gamespy/library/abstractions/switcher.py | 2 -- .../protocols/chat/abstractions/handler.py | 14 ++++------- .../protocols/chat/applications/handlers.py | 1 + .../tests/gamespy/chat/mock_objects.py | 23 +++++++++++++++++-- 5 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/frontends/gamespy/library/abstractions/connections.py b/src/frontends/gamespy/library/abstractions/connections.py index 2b3f5bbdf..763d55604 100644 --- a/src/frontends/gamespy/library/abstractions/connections.py +++ b/src/frontends/gamespy/library/abstractions/connections.py @@ -5,11 +5,6 @@ from frontends.gamespy.library.log.log_manager import LogWriter from frontends.gamespy.library.configs import ServerConfig -from typing import TYPE_CHECKING - -if TYPE_CHECKING: - from frontends.gamespy.library.network.http_handler import HttpRequest - class ConnectionBase: remote_ip: str @@ -30,9 +25,6 @@ def __init__( ) -> None: super().__init__() assert isinstance(config, ServerConfig) - assert issubclass(t_client, ClientBase) - # assert isinstance(logger, LogWriter) - # assert issubclass(type(handler), socketserver.BaseRequestHandler) self.handler = handler self.remote_ip = handler.client_address[0] self.remote_port = int(handler.client_address[1]) diff --git a/src/frontends/gamespy/library/abstractions/switcher.py b/src/frontends/gamespy/library/abstractions/switcher.py index 27044a9df..9b4e8526d 100644 --- a/src/frontends/gamespy/library/abstractions/switcher.py +++ b/src/frontends/gamespy/library/abstractions/switcher.py @@ -50,8 +50,6 @@ def handle(self): handler.handle() except Exception as e: UniSpyException.handle_exception(e, self._client) - if CmdHandlerBase._debug: - raise e @abstractmethod def _process_raw_request(self) -> None: diff --git a/src/frontends/gamespy/protocols/chat/abstractions/handler.py b/src/frontends/gamespy/protocols/chat/abstractions/handler.py index 5ac761817..82f927d31 100644 --- a/src/frontends/gamespy/protocols/chat/abstractions/handler.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/handler.py @@ -42,11 +42,10 @@ class PostLoginHandlerBase(CmdHandlerBase): class ChannelRequestBase(RequestBase): - channel_name: Optional[str] + channel_name: str def __init__(self, raw_request: str) -> None: super().__init__(raw_request) - self.channel_name = None def parse(self) -> None: super().parse() @@ -64,10 +63,6 @@ def __init__(self, request: RequestBase, result: ResultBase) -> None: assert isinstance(result, ResultBase) -def handle_brocker_message(message: str): - raise NotImplementedError() - - class ChannelHandlerBase(PostLoginHandlerBase): _request: ChannelRequestBase _response: ResponseBase @@ -87,16 +82,15 @@ def _response_send(self) -> None: self.broadcast_message() def broadcast_message(self): - assert self._client.brocker - assert self._channel - assert isinstance(self._channel.channel_name, str) + assert self._request.channel_name msg = BrockerMessage( server_id=self._client.server_config.server_id, - channel_name=self._channel.channel_name, + channel_name=self._request.channel_name, sender_ip_address=self._client.connection.remote_ip, sender_port=self._client.connection.remote_port, message=self._response.sending_buffer, ) + assert self._client.brocker self._client.brocker.publish_message(msg) diff --git a/src/frontends/gamespy/protocols/chat/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py index 88442ca9d..fda7dd8cd 100644 --- a/src/frontends/gamespy/protocols/chat/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -203,6 +203,7 @@ class QuitHandler(CmdHandlerBase): def __init__(self, client: ClientBase, request: QuitRequest): assert isinstance(request, QuitRequest) super().__init__(client, request) + self._is_fetching = False class SetKeyHandler(PostLoginHandlerBase): diff --git a/src/frontends/tests/gamespy/chat/mock_objects.py b/src/frontends/tests/gamespy/chat/mock_objects.py index 4bd2c994a..8f9c360bc 100644 --- a/src/frontends/tests/gamespy/chat/mock_objects.py +++ b/src/frontends/tests/gamespy/chat/mock_objects.py @@ -1,4 +1,5 @@ from typing import TYPE_CHECKING, cast +from frontends.gamespy.library.abstractions.brocker import BrockerBase from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.protocols.chat.applications.handlers import ( CdKeyHandler, @@ -8,6 +9,7 @@ ModeHandler, NickHandler, PartHandler, + QuitHandler, SetCKeyHandler, SetChannelKeyHandler, TopicHandler, @@ -40,8 +42,24 @@ from frontends.gamespy.library.exceptions.general import UniSpyException +class WebSocketBrockerMock(BrockerBase): + def __init__(self) -> None: + super().__init__("test", "ws://test.com", print) + + def subscribe(self): + pass + + def publish_message(self, message): + pass + + def unsubscribe(self): + pass + + class ClientMock(Client): - pass + def start_brocker(self): + self.brocker = WebSocketBrockerMock() # type:ignore + self.brocker.subscribe() # type:ignore def create_client() -> Client: @@ -55,6 +73,7 @@ def create_client() -> Client: create_mock_url( config, LoginHandler, LoginResult(profile_id=1, user_id=1).model_dump() ) + create_mock_url(config, QuitHandler, {"message": "ok"}) create_mock_url(config, UserIPHandler, {"message": "ok"}) create_mock_url( config, @@ -131,7 +150,7 @@ def create_client() -> Client: if TYPE_CHECKING: conn._client = cast(Client, conn._client) - + conn._client.on_connected() return conn._client From d79d2a00dd60d85181c979ea585fa364953934b3 Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Fri, 9 May 2025 03:18:21 +0000 Subject: [PATCH 182/231] Convert async to sync for debugging. --- .../library/abstractions/handler_base.py | 18 ++-- .../protocols/gamespy/chat/abstractions.py | 16 ++-- .../protocols/gamespy/chat/handlers.py | 92 +++++++++---------- .../protocols/gamespy/game_status/handlers.py | 18 ++-- .../protocols/gamespy/natneg/handlers.py | 2 +- .../presence_connection_manager/handlers.py | 46 +++++----- .../presence_search_player/handlers.py | 36 ++++---- .../gamespy/query_report/handlers.py | 6 +- .../gamespy/server_browser/handlers.py | 14 +-- .../gamespy/web_services/handlers.py | 78 ++++++++++------ src/backends/routers/gamespy/chat.py | 60 ++++++------ src/backends/routers/gamespy/gstats.py | 24 ++--- src/backends/routers/gamespy/natneg.py | 16 ++-- .../gamespy/presence_connection_manager.py | 48 +++++----- .../routers/gamespy/presence_search_player.py | 38 ++++---- src/backends/routers/gamespy/query_report.py | 16 ++-- .../routers/gamespy/server_browser.py | 18 ++-- src/backends/routers/gamespy/webservices.py | 42 ++++----- src/backends/routers/home.py | 8 +- .../precence_search_player/handler_tests.py | 13 ++- .../handler_tests.py | 37 ++++++-- .../gamespy/query_report/handler_tests.py | 8 +- .../library/abstractions/redis_objects.py | 12 +-- .../protocols/chat/abstractions/handler.py | 2 +- .../game_traffic_relay/applications/router.py | 2 +- 25 files changed, 356 insertions(+), 314 deletions(-) diff --git a/src/backends/library/abstractions/handler_base.py b/src/backends/library/abstractions/handler_base.py index 401a46777..d84eb9530 100644 --- a/src/backends/library/abstractions/handler_base.py +++ b/src/backends/library/abstractions/handler_base.py @@ -44,12 +44,12 @@ def __init__(self, request: RequestBase) -> None: self._result = None self._response = OKResponse() - async def handle(self) -> None: + def handle(self) -> None: try: - await self._request_check() - await self._data_operate() - await self._result_construct() - await self._response_construct() + self._request_check() + self._data_operate() + self._result_construct() + self._response_construct() except UniSpyException as ex: self.logger.error(ex.message) self._response = ErrorResponse(message=ex.message) @@ -57,21 +57,21 @@ async def handle(self) -> None: self.logger.error(ex) self._response = ErrorResponse(message=str(ex)) - async def _request_check(self) -> None: + def _request_check(self) -> None: """virtual method""" - async def _data_operate(self) -> None: + def _data_operate(self) -> None: """virtual method\n override by child class to perform database operations """ - async def _result_construct(self) -> None: + def _result_construct(self) -> None: """virtual method\n can override by child class to create self._result """ @final - async def _response_construct(self) -> None: + def _response_construct(self) -> None: """ _response_construct can not be overrided """ diff --git a/src/backends/protocols/gamespy/chat/abstractions.py b/src/backends/protocols/gamespy/chat/abstractions.py index 387c67f7a..659839462 100644 --- a/src/backends/protocols/gamespy/chat/abstractions.py +++ b/src/backends/protocols/gamespy/chat/abstractions.py @@ -61,7 +61,7 @@ def _check_channel_user(self): f"Can not find channel user with channel name: {self._request.channel_name}, ip address: {self._request.client_ip}:{self._request.client_port}" ) - async def _request_check(self) -> None: + def _request_check(self) -> None: self._get_user() self._check_user() @@ -71,18 +71,18 @@ async def _request_check(self) -> None: self._get_channel_user() self._check_channel_user() - async def _boradcast(self) -> None: + def _boradcast(self) -> None: # todo boradcast message here raise NotImplementedError() - async def handle(self) -> None: + def handle(self) -> None: try: - await self._request_check() - await self._data_operate() - await self._result_construct() - await self._response_construct() + self._request_check() + self._data_operate() + self._result_construct() + self._response_construct() - await self._boradcast() + self._boradcast() except UniSpyException as ex: self.logger.error(ex.message) self._response = ErrorResponse(message=ex.message) diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index 705c9cc89..3f893a369 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -76,7 +76,7 @@ class CdKeyHandler(HandlerBase): _request: CdkeyRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: is_valid = data.is_cdkey_valid(self._request.cdkey) if not is_valid: raise LoginFailedException("cdkey not matched") @@ -85,7 +85,7 @@ async def _data_operate(self) -> None: class CryptHandler(HandlerBase): _request: CryptRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: result = data.get_user_cache_by_ip_port( self._request.client_ip, self._request.client_port ) @@ -94,14 +94,14 @@ async def _data_operate(self) -> None: result.game_name = self._request.gamename # type: ignore PG_SESSION.commit() - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = CryptResult() class GetKeyHandler(HandlerBase): _request: GetKeyRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: caches = data.get_user_cache_by_nick_name(self._request.nick_name) if caches is None: @@ -109,7 +109,7 @@ async def _data_operate(self) -> None: if TYPE_CHECKING: self.caches = cast(list, caches) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = GetKeyResult( nick_name=self._request.nick_name, values=self.caches ) @@ -126,7 +126,7 @@ def __init__(self, request: RequestBase) -> None: class InviteHandler(HandlerBase): _request: InviteRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: chann = data.get_channel_by_name_and_ip_port( self._request.channel_name, self._request.client_ip, @@ -145,7 +145,7 @@ async def _data_operate(self) -> None: class ListHandler(HandlerBase): _request: ListRequest - async def _request_check(self) -> None: + def _request_check(self) -> None: if self._request.is_searching_channel: pass elif self._request.is_searching_user: @@ -153,7 +153,7 @@ async def _request_check(self) -> None: else: raise ChatException("request is invalid") - async def _data_operate(self) -> None: + def _data_operate(self) -> None: if self._request.is_searching_channel: # get the channel names with the substring self._data = data.find_channel_by_substring(self._request.filter) @@ -162,7 +162,7 @@ async def _data_operate(self) -> None: # get the user names with the substring self._data = data.find_user_by_substring(self._request.filter) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: data = [] for d in self._data: dd = ListResult.ListInfo(**d) @@ -173,19 +173,19 @@ async def _result_construct(self) -> None: class LoginPreAuthHandler(HandlerBase): _request: LoginPreAuthRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: raise NotImplementedError("should this access to PCM's api?") class LoginHandler(HandlerBase): - async def _data_operate(self) -> None: + def _data_operate(self) -> None: raise NotImplementedError("should this access to PCM's api?") class NickHandler(HandlerBase): _request: NickRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: is_nick = data.is_nick_exist(self._request.nick_name) if is_nick: raise NickNameInUseException( @@ -202,14 +202,14 @@ async def _data_operate(self) -> None: ChatUserCaches() data.add_nick_cache(cache) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = NickResult(nick_name=self._request.nick_name) class QuitHandler(HandlerBase): _request: QuitRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: data.remove_user_caches_by_ip_port( self._request.client_ip, self._request.client_port ) @@ -217,14 +217,14 @@ async def _data_operate(self) -> None: class RegisterNickHandler(HandlerBase): - async def _data_operate(self) -> None: + def _data_operate(self) -> None: raise NotImplementedError("we do not know which unique nick should be updated") class SetKeyHandler(HandlerBase): _request: SetKeyRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: user = data.get_user_cache_by_ip_port( self._request.client_ip, self._request.client_port ) @@ -238,14 +238,14 @@ async def _data_operate(self) -> None: class UserHandler(HandlerBase): _request: UserRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: raise NotImplementedError("maybe update the user caches") class WhoHandler(HandlerBase): _request: WhoRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: if self._request.request_type == WhoRequestType.GET_CHANNEL_USER_INFO: self._get_channel_user_info() else: @@ -259,7 +259,7 @@ def _get_user_info(self) -> None: self._request.client_ip, self._request.client_port ) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: infos = [] for d in self._data: info = WhoResult.WhoInfo(**d) @@ -270,10 +270,10 @@ async def _result_construct(self) -> None: class WhoIsHandler(HandlerBase): _request: WhoIsRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: self._data: tuple = data.get_whois_result(self._request.nick_name) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = WhoIsResult( nick_name=self._data[0], user_name=self._data[1], @@ -289,15 +289,15 @@ def _get_key_values(self): assert isinstance(self._channel, ChatChannelCaches) self._key_values = self._channel.key_values - async def _request_check(self) -> None: - await super()._request_check() + def _request_check(self) -> None: + super()._request_check() self._get_key_values() class GetCKeyHandler(ChannelHandlerBase): _request: GetCKeyRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: match self._request.request_type: case GetKeyRequestType.GET_CHANNEL_ALL_USER_KEY_VALUE: self.get_channel_all_user_key_value() @@ -314,7 +314,7 @@ def get_channel_specific_user_key_value(self): if d is not None: self._data = [d] - async def _result_construct(self) -> None: + def _result_construct(self) -> None: if self._data is None: return infos = [] @@ -335,13 +335,13 @@ async def _result_construct(self) -> None: class JoinHandler(ChannelHandlerBase): _request: JoinRequest - async def _request_check(self) -> None: + def _request_check(self) -> None: self._get_user() self._check_user() self._get_channel() - async def _data_operate(self) -> None: + def _data_operate(self) -> None: assert self._user is not None assert isinstance(self._user.nick_name, str) assert self._channel is not None @@ -364,7 +364,7 @@ async def _data_operate(self) -> None: ChannelHelper.join(self._channel, self._user) self._nicks = ChannelHelper.get_channel_all_nicks(self._channel) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: assert self._user is not None assert isinstance(self._user.user_name, str) assert isinstance(self._user.nick_name, str) @@ -386,8 +386,8 @@ def __init__(self, request: RequestBase) -> None: super().__init__(request) self._kickee = None - async def _request_check(self) -> None: - await super()._request_check() + def _request_check(self) -> None: + super()._request_check() assert isinstance(self._channel, ChatChannelCaches) assert isinstance(self._channel.channel_name, str) self._kickee = data.get_channel_user_cache_by_name( @@ -398,7 +398,7 @@ async def _request_check(self) -> None: f"kickee is not a user of channel:{self._channel.channel_name}" ) - async def _data_operate(self) -> None: + def _data_operate(self) -> None: assert self._channel assert self._channel_user assert self._kickee @@ -408,31 +408,31 @@ async def _data_operate(self) -> None: class ModeHandler(ChannelHandlerBase): _request: ModeRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: assert self._channel assert self._channel_user ChannelHelper.change_modes(self._channel, self._channel_user, self._request) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: raise NotImplementedError() - return await super()._result_construct() + return super()._result_construct() class NamesHandler(ChannelHandlerBase): _request: NamesRequest - async def _request_check(self) -> None: + def _request_check(self) -> None: self._get_user() self._check_user() self._get_channel() self._check_channel() - async def _data_operate(self) -> None: + def _data_operate(self) -> None: assert self._channel self._nicks = ChannelHelper.get_channel_all_nicks(self._channel) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: assert self._user assert isinstance(self._user.nick_name, str) self._result = NamesResult( @@ -445,12 +445,12 @@ async def _result_construct(self) -> None: class PartHandler(ChannelHandlerBase): _request: PartRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: assert self._channel assert self._channel_user ChannelHelper.quit(self._channel, self._channel_user) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: assert self._channel_user assert self._channel assert isinstance(self._channel_user.is_channel_creator, bool) @@ -468,15 +468,15 @@ async def _result_construct(self) -> None: class SetChannelKeyHandler(ChannelHandlerBase): _request: SetChannelKeyRequest - async def _request_check(self) -> None: - await super()._request_check() + def _request_check(self) -> None: + super()._request_check() assert self._channel_user assert isinstance(self._channel_user.is_channel_operator, bool) if self._channel_user.is_channel_operator: self._channel.key_values = self._request.key_values # type:ignore data.db_commit() - async def _result_construct(self) -> None: + def _result_construct(self) -> None: assert self._channel_user irc = ChannelUserHelper.get_user_irc_prefix(self._channel_user) self._result = SetChannelKeyResult( @@ -491,12 +491,12 @@ class SetCkeyHandler(ChannelHandlerBase): _request: SetCKeyRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: self._channel_user.key_values = self._request.key_values # type:ignore data.db_commit() self._is_broadcast = True - async def _result_construct(self) -> None: + def _result_construct(self) -> None: # todo think how to broadcast message raise NotImplementedError() @@ -504,7 +504,7 @@ async def _result_construct(self) -> None: class TopicHandler(ChannelHandlerBase): _request: TopicRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: assert self._channel_user assert isinstance(self._channel_user.is_channel_operator, bool) if self._request.request_type is TopicRequestType.GET_CHANNEL_TOPIC: @@ -517,7 +517,7 @@ async def _data_operate(self) -> None: self._channel.topic = self._request.channel_topic # type:ignore self._data: str = self._request.channel_topic - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = TopicResult( channel_name=self._request.channel_name, channel_topic=self._data ) diff --git a/src/backends/protocols/gamespy/game_status/handlers.py b/src/backends/protocols/gamespy/game_status/handlers.py index f2f57813d..32a14dffd 100644 --- a/src/backends/protocols/gamespy/game_status/handlers.py +++ b/src/backends/protocols/gamespy/game_status/handlers.py @@ -14,7 +14,7 @@ class AuthGameHandler(HandlerBase): class AuthPlayerHandler(HandlerBase): _request: AuthPlayerRequest - async def _data_operate(self): + def _data_operate(self): match self._request.auth_type: case AuthMethod.PARTNER_ID_AUTH: self.data = data.get_profile_id_by_token(token=self._request.auth_token) @@ -27,31 +27,31 @@ async def _data_operate(self): case _: raise GSException("Invalid auth type") - async def _result_construct(self): + def _result_construct(self): self._result = AuthPlayerResult(profile_id=self.data) class GetPlayerDataHandler(HandlerBase): _request: GetPlayerDataRequest - async def _data_operate(self): + def _data_operate(self): self.data = data.get_player_data( self._request.profile_id, self._request.storage_type, self._request.data_index) - async def _result_construct(self): + def _result_construct(self): self._result = GetPlayerDataResult(keyvalues=self.data) class GetProfileIdHandler(HandlerBase): _request: GetProfileIdRequest - async def _data_operate(self): + def _data_operate(self): self.data = data.get_profile_id_by_cdkey( cdkey=self._request.cdkey, nick_name=self._request.nick) - async def _result_construct(self): + def _result_construct(self): self._result = GetProfileIdResult(profile_id=self.data) @@ -61,19 +61,19 @@ class NewGameHandler(HandlerBase): find game based on the session key, and create a space for the game data """ - async def _data_operate(self): + def _data_operate(self): self.data = data.create_new_game_data() class SetPlayerDataHandler(HandlerBase): _request: SetPlayerDataRequest - async def _data_operate(self): + def _data_operate(self): raise NotImplementedError() class UpdateGameHandler(HandlerBase): _request: SetPlayerDataRequest - async def _data_operate(self): + def _data_operate(self): raise NotImplementedError() diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py index 63ad6a0b1..7903fab50 100644 --- a/src/backends/protocols/gamespy/natneg/handlers.py +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -10,7 +10,7 @@ def __init__(self, request: InitRequest) -> None: assert isinstance(request, InitRequest) super().__init__(request) - async def _data_operate(self) -> None: + def _data_operate(self) -> None: info = InitPacketCaches(**self._request.model_dump(mode="json")) update_init_info(info) diff --git a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py index 3bc590670..8625a5917 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py @@ -10,7 +10,7 @@ class KeepAliveHandler(HandlerBase): _request: KeepAliveRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: data.update_online_time(self._request.client_ip, self._request.client_port) @@ -18,7 +18,7 @@ async def _data_operate(self) -> None: class LoginHandler(HandlerBase): _request: LoginRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: if self._request.type == LoginType.NICK_EMAIL: self._nick_email_login() elif self._request.type == LoginType.UNIQUENICK_NAMESPACE_ID: @@ -50,7 +50,7 @@ def _auth_token_login(self) -> None: self._data = data.get_user_infos_by_authtoken( self._request.auth_token) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: assert self._data self._result = LoginResult(data=self._data) @@ -58,7 +58,7 @@ async def _result_construct(self) -> None: class LogoutHandler(HandlerBase): _request: LogoutRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: # data.update_online_status(user_id=, status=LoginStatus.DISCONNECTED) raise NotImplementedError() @@ -75,22 +75,22 @@ def __init__(self, request: NewUserRequest) -> None: class BuddyListHandler(HandlerBase): _request: BuddyListRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: self.data = data.get_buddy_list( self._request.profile_id, self._request.namespace_id) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = BuddyListResult(profile_ids=self.data) class BlockListHandler(HandlerBase): _request: BlockListRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: self.data = data.get_block_list( self._request.profile_id, self._request.namespace_id) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = BlockListResult(profile_ids=self.data) @@ -111,7 +111,7 @@ def __init__(self, request: RequestBase) -> None: class DelBuddyHandler(HandlerBase): _request: DelBuddyRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: self.data = data.delete_friend_by_profile_id( self._request.target_id) @@ -119,7 +119,7 @@ async def _data_operate(self) -> None: class AddBuddyHandler(HandlerBase): _request: AddBuddyRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: data.add_friend_request( self._request.profile_id, self._request.target_id, @@ -130,7 +130,7 @@ async def _data_operate(self) -> None: class AddBlockHandler(HandlerBase): _request: AddBlockRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: data.update_block(self._request.profile_id, self._request.taget_id, self._request.session_key) @@ -138,7 +138,7 @@ async def _data_operate(self) -> None: class InviteToHandler(HandlerBase): _request: InviteToRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: # user is offline # if (client is null) # { @@ -156,14 +156,14 @@ async def _data_operate(self) -> None: class StatusHandler(HandlerBase): _request: StatusRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: if self._request.is_get: self.data = data.get_status( self._request.session_key) else: data.update_status(self._request.session_key, self._request.status) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: if self._request.is_get: assert isinstance(self.data, UserStatus) @@ -173,7 +173,7 @@ async def _result_construct(self) -> None: class StatusInfoHandler(HandlerBase): _request: StatusInfoRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: raise NotImplementedError() @@ -183,11 +183,11 @@ async def _data_operate(self) -> None: class GetProfileHandler(HandlerBase): _request: GetProfileRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: self.data = data.get_profile_infos( profile_id=self._request.profile_id, session_key=self._request.session_key) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = GetProfileResult(user_profile=self.data) @@ -197,7 +197,7 @@ class NewProfileHandler(HandlerBase): """ _request: NewProfileRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: data.update_new_nick( self._request.session_key, self._request.old_nick, self._request.new_nick) @@ -205,7 +205,7 @@ async def _data_operate(self) -> None: class RegisterCDKeyHandler(HandlerBase): _request: RegisterCDKeyRequest - async def _data_operate(self): + def _data_operate(self): data.update_cdkey(self._request.session_key, self._request.cdkey_enc) @@ -215,20 +215,20 @@ class RegisterNickHandler(HandlerBase): """ _request: RegisterNickRequest - async def _data_operate(self): + def _data_operate(self): data.update_uniquenick(self._request.session_key, self._request.unique_nick) class RemoveBlockHandler(HandlerBase): - async def _data_operate(self): + def _data_operate(self): raise NotImplementedError() class UpdateProfileHandler(HandlerBase): _request: UpdateProfileRequest - async def _data_operate(self): + def _data_operate(self): data.update_profiles(self._request.session_key, self._request.extra_infos) @@ -236,6 +236,6 @@ async def _data_operate(self): class UpdateUserInfoHandler(HandlerBase): _request: UpdateUserInfoRequest - async def _data_operate(self): + def _data_operate(self): data.update_profiles(self._request.session_key, self._request.extra_infos) diff --git a/src/backends/protocols/gamespy/presence_search_player/handlers.py b/src/backends/protocols/gamespy/presence_search_player/handlers.py index 1a3606788..89e92d01b 100644 --- a/src/backends/protocols/gamespy/presence_search_player/handlers.py +++ b/src/backends/protocols/gamespy/presence_search_player/handlers.py @@ -15,7 +15,7 @@ class CheckHandler(HandlerBase): _request: CheckRequest _result: CheckResult - async def _data_operate(self) -> None: + def _data_operate(self) -> None: if data.verify_email(self._request.email): raise CheckException("The email is not existed") if data.verify_email_and_password(self._request.email, self._request.password): @@ -26,7 +26,7 @@ async def _data_operate(self) -> None: raise CheckException(f"No pid found with email{ self._request.email}") - async def _result_construct(self) -> None: + def _result_construct(self) -> None: assert self._profile_id is not None self._result = CheckResult(profile_id=self._profile_id) @@ -35,7 +35,7 @@ class NewUserHandler(HandlerBase): _request: NewUserRequest _result: NewUserResult - async def _data_operate(self) -> None: + def _data_operate(self) -> None: # check if user exist self.user = data.get_user(self._request.email) @@ -55,7 +55,7 @@ async def _data_operate(self) -> None: if self.subprofile is None: self._create_subprofile() - async def _result_construct(self) -> None: + def _result_construct(self) -> None: assert self.user is not None assert isinstance(self.user.userid, int) assert self.profile is not None @@ -93,7 +93,7 @@ class NicksHandler(HandlerBase): _request: NicksRequest _result: NicksResult - async def _data_operate(self) -> None: + def _data_operate(self) -> None: self.temp_list = data.get_nick_and_unique_nick_list( self._request.email, self._request.password, self._request.namespace_id) self.result_data = [] @@ -101,7 +101,7 @@ async def _data_operate(self) -> None: self.result_data.append( NickResultData(nick=nick, uniquenick=unique)) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = NicksResult(data=self.result_data) @@ -109,11 +109,11 @@ class OthersHandler(HandlerBase): _request: OthersRequest _result: OthersResult - async def _data_operate(self) -> None: + def _data_operate(self) -> None: self._data: list = data.get_friend_info_list( self._request.profile_id, self._request.namespace_id, self._request.game_name) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: temp_list = [] for item in self._data: temp_list.append(OthersResultData( @@ -126,11 +126,11 @@ class OthersListHandler(HandlerBase): _request: OthersListRequest result: OthersListResult - async def _data_operate(self) -> None: + def _data_operate(self) -> None: self._data: list = data.get_matched_profile_info_list( self._request.profile_ids, self._request.namespace_id) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: temp = [] for profile_id, uniquenick in self._data: temp.append(OthersListData( @@ -145,7 +145,7 @@ class SearchHandler(HandlerBase): _request: SearchRequest _result: SearchResult - async def _data_operate(self) -> None: + def _data_operate(self) -> None: if self._request.request_type == SearchType.NICK_SEARCH: assert self._request.nick self._data = data.get_matched_info_by_nick(self._request.nick) @@ -164,7 +164,7 @@ async def _data_operate(self) -> None: else: raise UniSpyException("search type invalid") - async def _result_construct(self) -> None: + def _result_construct(self) -> None: data = [] for d in self._data: dd = SearchResultData(**d) @@ -176,11 +176,11 @@ class SearchUniqueHandler(HandlerBase): _request: SearchUniqueRequest _result: SearchUniqueResult - async def _data_operate(self) -> None: + def _data_operate(self) -> None: self._data = data.get_matched_info_by_uniquenick_and_namespaceids( self._request.uniquenick, self._request.namespace_ids) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: data = [] for d in self._data: dd = SearchResultData(**d) @@ -192,11 +192,11 @@ class UniqueSearchHandler(HandlerBase): _request: UniqueSearchRequest _result: UniqueSearchResult - async def _data_operate(self) -> None: + def _data_operate(self) -> None: self._is_exist = data.is_uniquenick_exist( self._request.preferred_nick, self._request.namespace_id, self._request.game_name) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = UniqueSearchResult(is_uniquenick_exist=self._is_exist) @@ -204,8 +204,8 @@ class ValidHandler(HandlerBase): _request: ValidRequest _result: ValidResult - async def _data_operate(self) -> None: + def _data_operate(self) -> None: self._is_exist = data.is_email_exist(self._request.email) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = ValidResult(is_account_valid=self._is_exist) diff --git a/src/backends/protocols/gamespy/query_report/handlers.py b/src/backends/protocols/gamespy/query_report/handlers.py index b58b99d00..2450e35b3 100644 --- a/src/backends/protocols/gamespy/query_report/handlers.py +++ b/src/backends/protocols/gamespy/query_report/handlers.py @@ -13,7 +13,7 @@ class AvaliableHandler(HandlerBase): class ChallengeHandler(HandlerBase): _request: HeartBeatRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: cache = PG_SESSION.query(GameServerCaches).where( GameServerCaches.instant_key == self._request.instant_key).first() if cache is None: @@ -26,7 +26,7 @@ async def _data_operate(self) -> None: class Heartbeathandler(HandlerBase): _request: HeartBeatRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: cache = data.get_server_info_with_instant_key( str(self._request.instant_key)) if cache is None: @@ -60,7 +60,7 @@ async def _data_operate(self) -> None: class KeepAliveHandler(HandlerBase): _request: KeepAliveRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: cache = PG_SESSION.query(GameServerCaches).where( GameServerCaches.instant_key == self._request.instant_key).first() # update heartbeat time diff --git a/src/backends/protocols/gamespy/server_browser/handlers.py b/src/backends/protocols/gamespy/server_browser/handlers.py index bc824dbf4..eea61552e 100644 --- a/src/backends/protocols/gamespy/server_browser/handlers.py +++ b/src/backends/protocols/gamespy/server_browser/handlers.py @@ -15,7 +15,7 @@ class ServerListHandler(HandlerBase): _request: ServerListRequest _caches: list[PeerRoomInfo] | list[GameServerInfo] - async def _data_operate(self): + def _data_operate(self): if self._request.update_option in\ [ServerListUpdateOption.SERVER_MAIN_LIST, ServerListUpdateOption.P2P_SERVER_MAIN_LIST, @@ -33,7 +33,7 @@ async def _data_operate(self): raise ServerBrowserException( "invalid server browser update option") - async def _result_construct(self): + def _result_construct(self): if self._request.update_option in\ [ServerListUpdateOption.SERVER_MAIN_LIST, ServerListUpdateOption.P2P_SERVER_MAIN_LIST, @@ -62,18 +62,18 @@ async def _result_construct(self): class AdHocHandler(HandlerBase): _request: AdHocRequestBase - async def _data_operate(self): + def _data_operate(self): raise NotImplementedError() class SendMessageHandler(HandlerBase): _request: SendMessageRequest - async def _data_operate(self): + def _data_operate(self): self.data = data.get_server_info_with_ip_and_port( self._request.game_server_public_ip, self._request.game_server_public_port) - async def _result_construct(self): + def _result_construct(self): self._result = SendMessageResult(sb_sender_id=self._request.server_id, natneg_message=self._request.client_message, server_info=self.data) @@ -81,9 +81,9 @@ async def _result_construct(self): class ServerInfoHandler(HandlerBase): _request: ServerInfoRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: self.data = data.get_server_info_with_ip_and_port( self._request.game_server_public_ip, self._request.game_server_public_port) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = ServerInfoResult(game_server_info=self.data) diff --git a/src/backends/protocols/gamespy/web_services/handlers.py b/src/backends/protocols/gamespy/web_services/handlers.py index 0ca740443..854a73353 100644 --- a/src/backends/protocols/gamespy/web_services/handlers.py +++ b/src/backends/protocols/gamespy/web_services/handlers.py @@ -3,83 +3,106 @@ # region auth from backends.library.abstractions.contracts import RequestBase from backends.library.abstractions.handler_base import HandlerBase -from backends.protocols.gamespy.web_services.requests import * import backends.protocols.gamespy.web_services.data as data -from frontends.gamespy.protocols.web_services.modules.auth.contracts.results import LoginProfileResult -from frontends.gamespy.protocols.web_services.modules.direct2game.contracts.results import GetPurchaseHistoryResult, GetStoreAvailabilityResult +from backends.protocols.gamespy.web_services.requests import ( + CreateRecordRequest, + GetMyRecordsRequest, + GetPurchaseHistoryRequest, + GetStoreAvailabilityRequest, + LoginPS3CertRequest, + LoginProfileRequest, + LoginRemoteAuthRequest, + LoginUniqueNickRequest, + SearchForRecordsRequest, +) +from frontends.gamespy.protocols.web_services.modules.auth.contracts.results import ( + LoginProfileResult, +) +from frontends.gamespy.protocols.web_services.modules.direct2game.contracts.results import ( + GetPurchaseHistoryResult, + GetStoreAvailabilityResult, +) class LoginProfileHandler(HandlerBase): _request: LoginProfileRequest - async def _data_operate(self) -> None: - self.data = data.get_info_by_cdkey_email(uniquenick=self._request.uniquenick, - namespace_id=self._request.namespace_id, - cdkey=self._request.cdkey, - email=self._request.email) + def _data_operate(self) -> None: + self.data = data.get_info_by_cdkey_email( + uniquenick=self._request.uniquenick, + namespace_id=self._request.namespace_id, + cdkey=self._request.cdkey, + email=self._request.email, + ) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = LoginProfileResult( user_id=self.data[0], profile_id=self.data[1], profile_nick=self.data[2], unique_nick=self.data[3], - cdkey_hash=self.data[4]) + cdkey_hash=self.data[4], + ) class LoginPS3CertHandler(HandlerBase): _request: LoginPS3CertRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: raise NotImplementedError() class LoginRemoteAuthHandler(HandlerBase): _request: LoginRemoteAuthRequest - async def _data_operate(self) -> None: - self.data = data.get_info_by_authtoken( - auth_token=self._request.auth_token) + def _data_operate(self) -> None: + self.data = data.get_info_by_authtoken(auth_token=self._request.auth_token) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = LoginProfileResult( user_id=self.data[0], profile_id=self.data[1], profile_nick=self.data[2], unique_nick=self.data[3], - cdkey_hash=self.data[4]) + cdkey_hash=self.data[4], + ) class LoginUniqueNickHandler(HandlerBase): _request: LoginUniqueNickRequest - async def _data_operate(self) -> None: - self.data = data.get_info_by_uniquenick(uniquenick=self._request.uniquenick, - namespace_id=self._request.namespace_id) + def _data_operate(self) -> None: + self.data = data.get_info_by_uniquenick( + uniquenick=self._request.uniquenick, namespace_id=self._request.namespace_id + ) - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = LoginProfileResult( user_id=self.data[0], profile_id=self.data[1], profile_nick=self.data[2], unique_nick=self.data[3], - cdkey_hash=self.data[4]) + cdkey_hash=self.data[4], + ) + + # region d2g class GetPurchaceHistoryHandler(HandlerBase): _request: GetPurchaseHistoryRequest - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = GetPurchaseHistoryResult() class GetStoreAvailabilityHandler(HandlerBase): _request: GetStoreAvailabilityRequest - async def _result_construct(self) -> None: + def _result_construct(self) -> None: self._result = GetStoreAvailabilityResult() + # region ingamead @@ -94,6 +117,7 @@ def __init__(self, request: RequestBase) -> None: super().__init__(request) raise NotImplementedError() + # region patching and tracking # region racing @@ -104,14 +128,14 @@ def __init__(self, request: RequestBase) -> None: class CreateRecordHandler(HandlerBase): _request: CreateRecordRequest - async def _data_operate(self) -> None: + def _data_operate(self) -> None: raise NotImplementedError() class GetMyRecordsHandler(HandlerBase): _request: GetMyRecordsRequest - async def _data_operate(self): + def _data_operate(self): self.data = data.get_user_data(self._request.table_id) raise NotImplementedError() @@ -119,5 +143,5 @@ async def _data_operate(self): class SearchForRecordsHandler(HandlerBase): _request: SearchForRecordsRequest - async def _data_operate(self) -> None: - return await super()._data_operate() + def _data_operate(self) -> None: + return super()._data_operate() diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 87dd1490e..5ca5fac6d 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -71,127 +71,127 @@ async def websocket_endpoint(ws: WebSocket): @router.post(f"{CHAT}/CdKeyHandler") -async def cdkey(request: CdkeyRequest): +def cdkey(request: CdkeyRequest): handler = CdKeyHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{CHAT}/GetKeyHandler") -async def getkey(request: GetKeyRequest): +def getkey(request: GetKeyRequest): handler = GetKeyHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{CHAT}/GetUdpRelayHandler") -async def get_udp_relay(request: GetUdpRelayRequest): +def get_udp_relay(request: GetUdpRelayRequest): handler = GetUdpRelayHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{CHAT}/InviteHandler") -async def invite(request: InviteRequest): +def invite(request: InviteRequest): handler = InviteHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{CHAT}/ListHandler") -async def list_data(request: ListRequest): +def list_data(request: ListRequest): # handler = ListHandler raise NotImplementedError() @router.post(f"{CHAT}/LoginHandler") -async def login(request: LoginRequest): +def login(request: LoginRequest): pass @router.post(f"{CHAT}/NickHandler") -async def nick(request: NickRequest): +def nick(request: NickRequest): pass @router.post(f"{CHAT}/QuitHandler") -async def quit(request: QuitRequest): +def quit(request: QuitRequest): pass @router.post(f"{CHAT}/SetKeyHandler") -async def set_key(request: SetKeyRequest): +def set_key(request: SetKeyRequest): pass @router.post(f"{CHAT}/UserHandler") -async def user(request: UserRequest): +def user(request: UserRequest): pass @router.post(f"{CHAT}/UserIPHandler") -async def user_ip(request: UserIPRequest): +def user_ip(request: UserIPRequest): pass @router.post(f"{CHAT}/WhoHandler") -async def who(request: WhoRequest): +def who(request: WhoRequest): pass @router.post(f"{CHAT}/WhoIsHandler") -async def whois(request: WhoIsRequest): +def whois(request: WhoIsRequest): pass # region channel @router.post(f"{CHAT}/GetChannelKeyHandler") -async def get_channel_key(request: GetChannelKeyRequest): +def get_channel_key(request: GetChannelKeyRequest): pass @router.post(f"{CHAT}/GetCKeyHandler") -async def get_ckey(request: GetCKeyRequest): +def get_ckey(request: GetCKeyRequest): pass @router.post(f"{CHAT}/JoinHandler") -async def join(request: JoinRequest): +def join(request: JoinRequest): pass @router.post(f"{CHAT}/KickHandler") -async def kick(request: KickRequest): +def kick(request: KickRequest): pass @router.post(f"{CHAT}/ModeHandler") -async def mode(request: ModeRequest): +def mode(request: ModeRequest): pass @router.post(f"{CHAT}/NamesHandler") -async def names(request: NamesRequest): +def names(request: NamesRequest): pass @router.post(f"{CHAT}/PartHandler") -async def part(request: PartRequest): +def part(request: PartRequest): pass @router.post(f"{CHAT}/SetChannelKeyHandler") -async def set_channel_key(request: SetChannelKeyRequest): +def set_channel_key(request: SetChannelKeyRequest): pass @router.post(f"{CHAT}/SetGroupHandler") -async def set_group(request: SetGroupRequest): +def set_group(request: SetGroupRequest): pass @router.post(f"{CHAT}/TopicHandler") -async def topic(request: TopicRequest): +def topic(request: TopicRequest): pass @@ -199,7 +199,7 @@ async def topic(request: TopicRequest): @router.post(f"{CHAT}/ATMHandler") -async def atm(request: AtmRequest): +def atm(request: AtmRequest): pass @@ -209,12 +209,12 @@ def notice(request: NoticeRequest): @router.post(f"{CHAT}/PrivateHandler") -async def private(request: PrivateRequest): +def private(request: PrivateRequest): pass @router.post(f"{CHAT}/UTMHandler") -async def utm(request: UtmRequest): +def utm(request: UtmRequest): pass diff --git a/src/backends/routers/gamespy/gstats.py b/src/backends/routers/gamespy/gstats.py index 20c3d2f35..647314ce5 100644 --- a/src/backends/routers/gamespy/gstats.py +++ b/src/backends/routers/gamespy/gstats.py @@ -6,44 +6,44 @@ @router.post(f"{GAMESTATUS}/AuthGameHandler") -async def auth_game(request: AuthGameRequest): +def auth_game(request: AuthGameRequest): handler = AuthGameHandler(request) - await handler.handle() + handler.handle() return handler._response @router.post(f"{GAMESTATUS}/AuthPlayerHandler") -async def auth_player(request: AuthPlayerRequest): +def auth_player(request: AuthPlayerRequest): handler = AuthPlayerHandler(request) - await handler.handle() + handler.handle() return handler._response @router.post(f"{GAMESTATUS}/NewGameHandler") -async def new_game(request: NewGameRequest): +def new_game(request: NewGameRequest): handler = NewGameHandler(request) - await handler.handle() + handler.handle() return handler._response @router.post(f"{GAMESTATUS}/GetPlayerDataHandler") -async def get_player_data(request: GetPlayerDataRequest): +def get_player_data(request: GetPlayerDataRequest): handler = GetPlayerDataHandler(request) - await handler.handle() + handler.handle() return handler._response @router.post(f"{GAMESTATUS}/SetPlayerDataHandler") -async def set_player_data(request: SetPlayerDataRequest): +def set_player_data(request: SetPlayerDataRequest): handler = SetPlayerDataHandler(request) - await handler.handle() + handler.handle() return handler._response @router.post(f"{GAMESTATUS}/UpdateGameHandler") -async def updaet_game(request: UpdateGameRequest): +def updaet_game(request: UpdateGameRequest): handler = UpdateGameHandler(request) - await handler.handle() + handler.handle() return handler._response diff --git a/src/backends/routers/gamespy/natneg.py b/src/backends/routers/gamespy/natneg.py index aea6c40a0..0622fe241 100644 --- a/src/backends/routers/gamespy/natneg.py +++ b/src/backends/routers/gamespy/natneg.py @@ -10,36 +10,36 @@ @router.post(f"{NATNEG}/AddressCheckHandler") -async def address_check(request: AddressCheckRequest): +def address_check(request: AddressCheckRequest): raise NotImplementedError() @router.post(f"{NATNEG}/ConnectHandler") -async def connect(request: ConnectRequest): +def connect(request: ConnectRequest): handler = ConnectHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{NATNEG}/ErtAckHandler") -async def ert_ack(request: ErtAckRequest): +def ert_ack(request: ErtAckRequest): raise NotImplementedError() @router.post(f"{NATNEG}/InitHandler") -async def init(request: InitRequest): +def init(request: InitRequest): handler = InitHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{NATNEG}/PingHandler") -async def ping(request: PingRequest): +def ping(request: PingRequest): raise NotImplementedError() @router.post(f"{NATNEG}/ReportHandler") -async def report(request: ReportRequest): +def report(request: ReportRequest): raise NotImplementedError() diff --git a/src/backends/routers/gamespy/presence_connection_manager.py b/src/backends/routers/gamespy/presence_connection_manager.py index 3681b39cc..2fc13b128 100644 --- a/src/backends/routers/gamespy/presence_connection_manager.py +++ b/src/backends/routers/gamespy/presence_connection_manager.py @@ -10,86 +10,86 @@ @router.post(f"{PRESENCE_CONNECTION_MANAGER}/LoginHandler") -async def login(request: LoginRequest): +def login(request: LoginRequest): handler = LoginHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/LogoutHandler") -async def logout(request: LogoutRequest): +def logout(request: LogoutRequest): handler = LogoutHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/KeepAliveHandler") -async def keep_alive(request: KeepAliveRequest): +def keep_alive(request: KeepAliveRequest): handler = KeepAliveHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/NewUserHandler") -async def new_user(request: NewUserRequest): +def new_user(request: NewUserRequest): handler = NewUserHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/AddBlockHandler") -async def add_block(request: AddBlockRequest): +def add_block(request: AddBlockRequest): handler = AddBlockHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/GetProfileHandler") -async def get_profile(request: GetProfileRequest): +def get_profile(request: GetProfileRequest): handler = GetProfileHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/NewProfileHandler") -async def new_proflie(request: NewProfileRequest): +def new_proflie(request: NewProfileRequest): handler = NewProfileHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/RegisterCDKeyHandler") -async def register_cdkey(request: RegisterCDKeyRequest): +def register_cdkey(request: RegisterCDKeyRequest): handler = RegisterCDKeyHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/RegisterNickHandler") -async def register_nick(request: RegisterNickRequest): +def register_nick(request: RegisterNickRequest): handler = RegisterNickHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/UpdateProfileHandler") -async def update_profile(request: UpdateProfileRequest): +def update_profile(request: UpdateProfileRequest): handler = UpdateProfileHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/StatusHandler") -async def status(request: StatusRequest): +def status(request: StatusRequest): handler = StatusHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_CONNECTION_MANAGER}/StatusInfoHandler") -async def status_info(request: StatusInfoRequest): +def status_info(request: StatusInfoRequest): handler = StatusInfoHandler(request) - await handler.handle() + handler.handle() return handler.response if __name__ == "__main__": diff --git a/src/backends/routers/gamespy/presence_search_player.py b/src/backends/routers/gamespy/presence_search_player.py index e7a44fd16..8194ca06f 100644 --- a/src/backends/routers/gamespy/presence_search_player.py +++ b/src/backends/routers/gamespy/presence_search_player.py @@ -9,70 +9,70 @@ @router.post(f"{PRESENCE_SEARCH_PLAYER}/CheckHandler") -async def check(request: CheckRequest): +def check(request: CheckRequest): handler = CheckHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_SEARCH_PLAYER}/NewUserHandler") -async def new_user(request: NewUserRequest): +def new_user(request: NewUserRequest): handler = NewUserHandler(request) - await handler.handle() + handler.handle() handler.response @router.post(f"{PRESENCE_SEARCH_PLAYER}/NicksHandler") -async def nicks(request: NicksRequest): +def nicks(request: NicksRequest): handler = NicksHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_SEARCH_PLAYER}/OthersHandler") -async def others(request: OthersRequest): +def others(request: OthersRequest): handler = OthersHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_SEARCH_PLAYER}/OthersListHandler") -async def others_list(request: OthersListRequest): +def others_list(request: OthersListRequest): handler = OthersListHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_SEARCH_PLAYER}/PMatchHandler") -async def player_match(request: dict): +def player_match(request: dict): raise NotImplementedError() @router.post(f"{PRESENCE_SEARCH_PLAYER}/SearchHandler") -async def search(request: SearchRequest): +def search(request: SearchRequest): handler = SearchHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_SEARCH_PLAYER}/SearchUniqueHandler") -async def search_unique(request: SearchUniqueRequest): +def search_unique(request: SearchUniqueRequest): handler = SearchUniqueHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_SEARCH_PLAYER}/UniqueSearchHandler") -async def unique_search(request: UniqueSearchRequest): +def unique_search(request: UniqueSearchRequest): handler = UniqueSearchHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{PRESENCE_SEARCH_PLAYER}/ValidHandler") -async def valid(request: ValidRequest): +def valid(request: ValidRequest): handler = ValidHandler(request) - await handler.handle() + handler.handle() return handler.response diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index 9635639cf..e62742393 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -9,36 +9,36 @@ @router.post(f"{QUERY_REPORT}/HeartBeatHandler") -async def heartbeat(request: HeartBeatRequest): +def heartbeat(request: HeartBeatRequest): handler = Heartbeathandler(request) - await handler.handle() + handler.handle() return handler._response @router.post(f"{QUERY_REPORT}/ChallengeHanler") -async def challenge(request: ChallengeRequest): +def challenge(request: ChallengeRequest): raise NotImplementedError() @router.post(f"{QUERY_REPORT}/AvailableHandler") -async def available(request: AvaliableRequest): +def available(request: AvaliableRequest): handler = AvaliableHandler(request) - await handler.handle() + handler.handle() return handler._response @router.post(f"{QUERY_REPORT}/ClientMessageAckHandler") -async def client_message(request: ClientMessageRequest): +def client_message(request: ClientMessageRequest): raise NotImplementedError() @router.post(f"{QUERY_REPORT}/EchoHandler") -async def echo(request: EchoRequest): +def echo(request: EchoRequest): raise NotImplementedError() @router.post(f"{QUERY_REPORT}/KeepAliveHandler") -async def keep_alive(request: KeepAliveRequest): +def keep_alive(request: KeepAliveRequest): raise NotImplementedError() diff --git a/src/backends/routers/gamespy/server_browser.py b/src/backends/routers/gamespy/server_browser.py index fe1b54cee..731dbca76 100644 --- a/src/backends/routers/gamespy/server_browser.py +++ b/src/backends/routers/gamespy/server_browser.py @@ -10,37 +10,37 @@ @router.websocket(f"{SERVER_BROWSER_V2}/AdHocHandler") -async def check(websocket: WebSocket): +def check(websocket: WebSocket): """ notify every server browser to send message to its client """ raise NotImplementedError() - await websocket.accept() + websocket.accept() while True: try: - data = await websocket.receive_text() + data = websocket.receive_text() client_pool[websocket.client.host] = websocket - await websocket.send_text(f"Message text was: {data}") + websocket.send_text(f"Message text was: {data}") except WebSocketDisconnect: del client_pool[websocket.client.host] @router.post(f"{SERVER_BROWSER_V2}/SendMessageHandler") -async def send_message(request: SendMessageRequest): +def send_message(request: SendMessageRequest): raise NotImplementedError() @router.post(f"{SERVER_BROWSER_V2}/ServerInfoHandler") -async def server_info(request: ServerInfoRequest): +def server_info(request: ServerInfoRequest): handler = ServerInfoHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{SERVER_BROWSER_V2}/ServerListHandler") -async def server_list(request: ServerListRequest): +def server_list(request: ServerListRequest): handler = ServerListHandler(request) - await handler.handle() + handler.handle() return handler.response diff --git a/src/backends/routers/gamespy/webservices.py b/src/backends/routers/gamespy/webservices.py index 36b2b5020..05192a1c9 100644 --- a/src/backends/routers/gamespy/webservices.py +++ b/src/backends/routers/gamespy/webservices.py @@ -10,91 +10,91 @@ @router.post(f"{WEB_SERVICES}/Altas/CreateRecordHandler") -async def create_matchless_session(request): +def create_matchless_session(request): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Altas/CreateSessionHandler") -async def create_session(request): +def create_session(request): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Altas/SetReportIntentionHandler") -async def set_report_intention(request): +def set_report_intention(request): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Altas/SubmitReportHandler") -async def submit_report(request): +def submit_report(request): raise NotImplementedError() # Auth services @router.post(f"{WEB_SERVICES}/Auth/LoginProfileHandler") -async def login_profile(request: LoginProfileRequest): +def login_profile(request: LoginProfileRequest): handler = LoginProfileHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{WEB_SERVICES}/Auth/LoginRemoteAuthHandler") -async def login_remote_auth(request: LoginRemoteAuthRequest): +def login_remote_auth(request: LoginRemoteAuthRequest): handler = LoginRemoteAuthHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{WEB_SERVICES}/Auth/LoginUniqueNickHandler") -async def login_uniquenick(request: LoginUniqueNickRequest): +def login_uniquenick(request: LoginUniqueNickRequest): handler = LoginUniqueNickHandler(request) - await handler.handle() + handler.handle() return handler.response # SAKE services @router.post(f"{WEB_SERVICES}/Sake/CreateRecordHandler") -async def create_record(request: CreateRecordRequest): +def create_record(request: CreateRecordRequest): handler = CreateRecordHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{WEB_SERVICES}/Sake/DeleteRecordHandler") -async def delete_record(request): +def delete_record(request): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Sake/GetMyRecordsHandler") -async def get_my_records(request: GetMyRecordsRequest): +def get_my_records(request: GetMyRecordsRequest): handler = GetMyRecordsHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{WEB_SERVICES}/Sake/GetRandomRecordsHandler") -async def get_random_records(request): +def get_random_records(request): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Sake/GetRecordLimitHandler") -async def get_record_limit(request): +def get_record_limit(request): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Sake/RateRecordHandler") -async def rate_record(request): +def rate_record(request): raise NotImplementedError() @router.post(f"{WEB_SERVICES}/Sake/SearchForRecordsHandler") -async def search_for_records(request: SearchForRecordsRequest): +def search_for_records(request: SearchForRecordsRequest): handler = SearchForRecordsHandler(request) - await handler.handle() + handler.handle() return handler.response @router.post(f"{WEB_SERVICES}/Sake/UpdateRecordHandler") -async def update_record(request): +def update_record(request): raise NotImplementedError() if __name__ == "__main__": diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index 5ff3862e6..7758fc837 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -24,21 +24,21 @@ @app.exception_handler(RequestValidationError) -async def validation_exception_handler(request, exc): +def validation_exception_handler(request, exc): str_error = str(exc) logger.error(str_error) return JSONResponse({"error": str_error}) @app.exception_handler(Exception) -async def handle_unispy_exception(request, exc): +def handle_unispy_exception(request, exc): str_error = str(exc) logger.error(str_error) return JSONResponse({"error": str_error}) @app.post("/") -async def home(request: ServerConfig) -> dict: +def home(request: ServerConfig) -> dict: # todo add the server config to our database return {"status": "online"} @@ -49,7 +49,7 @@ class RegisterRequest(BaseModel): @app.post("/token") -async def get_auth_token(request: RegisterRequest): +def get_auth_token(request: RegisterRequest): pass if __name__ == "__main__": diff --git a/src/backends/tests/gamespy/precence_search_player/handler_tests.py b/src/backends/tests/gamespy/precence_search_player/handler_tests.py index 2e0224f66..3db990632 100644 --- a/src/backends/tests/gamespy/precence_search_player/handler_tests.py +++ b/src/backends/tests/gamespy/precence_search_player/handler_tests.py @@ -1,5 +1,4 @@ # the total requests tests -import asyncio import unittest from backends.tests.utils import add_headers @@ -23,7 +22,7 @@ def test_search(self): data = add_headers(r) request = bkr.SearchRequest(**data) handler = bkh.SearchHandler(request) - asyncio.run(handler.handle()) + handler.handle() pass def test_chenck(self): @@ -31,7 +30,7 @@ def test_chenck(self): data = add_headers(r) request = bkr.CheckRequest(**data) handler = bkh.CheckHandler(request) - asyncio.run(handler.handle()) + handler.handle() pass def test_new_user(self): @@ -39,7 +38,7 @@ def test_new_user(self): data = add_headers(r) request = bkr.NewUserRequest(**data) handler = bkh.NewUserHandler(request) - asyncio.run(handler.handle()) + handler.handle() pass def test_nick(self): @@ -47,7 +46,7 @@ def test_nick(self): data = add_headers(r) request = bkr.NicksRequest(**data) handler = bkh.NicksHandler(request) - asyncio.run(handler.handle()) + handler.handle() pass def test_others_list(self): @@ -62,7 +61,7 @@ def test_search_unique(self): data = add_headers(r) request = bkr.SearchUniqueRequest(**data) handler = bkh.SearchUniqueHandler(request) - asyncio.run(handler.handle()) + handler.handle() pass def test_valid(self): @@ -70,6 +69,6 @@ def test_valid(self): data = add_headers(r) request = bkr.ValidRequest(**data) handler = bkh.ValidHandler(request) - asyncio.run(handler.handle()) + handler.handle() self.assertTrue(handler._result.is_account_valid, True) pass diff --git a/src/backends/tests/gamespy/presence_conection_manager/handler_tests.py b/src/backends/tests/gamespy/presence_conection_manager/handler_tests.py index fc9151f0a..f88e017a1 100644 --- a/src/backends/tests/gamespy/presence_conection_manager/handler_tests.py +++ b/src/backends/tests/gamespy/presence_conection_manager/handler_tests.py @@ -1,21 +1,23 @@ -import asyncio import unittest from backends.tests.utils import add_headers import frontends.gamespy.protocols.presence_connection_manager.contracts.requests as pcm -from frontends.tests.gamespy.presence_connection_manager.general_request_tests import LOGIN_AUTH_TOKEN, LOGIN_UNIQUE_NICK, LOGIN_USER +from frontends.tests.gamespy.presence_connection_manager.general_request_tests import ( + LOGIN_AUTH_TOKEN, + LOGIN_UNIQUE_NICK, + LOGIN_USER, +) import backends.protocols.gamespy.presence_connection_manager.requests as bkr import backends.protocols.gamespy.presence_connection_manager.handlers as bkh class HandlerTest(unittest.TestCase): - # region General def test_login_authtoken(self): r = pcm.LoginRequest(LOGIN_AUTH_TOKEN) data = add_headers(r) request = bkr.LoginRequest(**data) handler = bkh.LoginHandler(request) - asyncio.run(handler.handle()) + handler.handle() pass def test_login_uniquenick(self): @@ -23,7 +25,7 @@ def test_login_uniquenick(self): data = add_headers(r) request = bkr.LoginRequest(**data) handler = bkh.LoginHandler(request) - asyncio.run(handler.handle()) + handler.handle() pass def test_login_user(self): @@ -31,77 +33,94 @@ def test_login_user(self): data = add_headers(r) request = bkr.LoginRequest(**data) handler = bkh.LoginHandler(request) - asyncio.run(handler.handle()) + handler.handle() pass + @unittest.skip("not implemented") def test_new_user(self): raise NotImplementedError() + @unittest.skip("not implemented") def test_logout(self): raise NotImplementedError() # region Buddy + @unittest.skip("not implemented") def test_buddy_list(self): r = pcm.BuddyListRequest(profile_id=1, namespace_id=0) data = add_headers(r) request = bkr.BuddyListRequest(**data) handler = bkh.BuddyListHandler(request) - asyncio.run(handler.handle()) + handler.handle() pass + @unittest.skip("not implemented") def test_block_list(self): r = pcm.BlockListRequest(profile_id=1, namespace_id=0) data = add_headers(r) request = bkr.BlockListRequest(**data) handler = bkh.BlockListHandler(request) - asyncio.run(handler.handle()) + handler.handle() pass + @unittest.skip("not implemented") def test_add_buddy(self): # r = pcm.AddBuddyRequest() raise NotImplementedError() pass + @unittest.skip("not implemented") def test_del_buddy(self): raise NotImplementedError() pass + @unittest.skip("not implemented") def test_add_block(self): raise NotImplementedError() pass + @unittest.skip("not implemented") def test_del_block(self): raise NotImplementedError() pass + @unittest.skip("not implemented") def test_invite_to(self): raise NotImplementedError() pass + @unittest.skip("not implemented") def test_status_info(self): raise NotImplementedError() pass + @unittest.skip("not implemented") def test_statue(self): raise NotImplementedError() pass -# region Profile + # region Profile + @unittest.skip("not implemented") def test_get_profile(self): raise NotImplementedError() + @unittest.skip("not implemented") def test_new_profile(self): raise NotImplementedError() + @unittest.skip("not implemented") def test_register_cdkey(self): raise NotImplementedError() + @unittest.skip("not implemented") def test_register_nick(self): raise NotImplementedError() + @unittest.skip("not implemented") def test_update_profile(self): raise NotImplementedError() + @unittest.skip("not implemented") def test_update_user_info(self): raise NotImplementedError() diff --git a/src/backends/tests/gamespy/query_report/handler_tests.py b/src/backends/tests/gamespy/query_report/handler_tests.py index 32b71d2c4..eeec4fc41 100644 --- a/src/backends/tests/gamespy/query_report/handler_tests.py +++ b/src/backends/tests/gamespy/query_report/handler_tests.py @@ -5,18 +5,18 @@ class HandlerTests(unittest.IsolatedAsyncioTestCase): - async def test_heartbeat(self): + def test_heartbeat(self): request = {"server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "raw_request": "\\u0003\\\\xe5\\\\xcfaZlocalip0\\u0000172.19.0.5\\u0000localport\\u000011111\\u0000natneg\\u00001\\u0000statechanged\\u00003\\u0000gamename\\u0000gmtest\\u0000hostname\\u0000GameSpy QR2 Sample\\u0000gamever\\u00002.00\\u0000hostport\\u000025000\\u0000mapname\\u0000gmtmap1\\u0000gametype\\u0000arena\\u0000numplayers\\u00005\\u0000numteams\\u00002\\u0000maxplayers\\u000032\\u0000gamemode\\u0000openplaying\\u0000teamplay\\u00001\\u0000fraglimit\\u00000\\u0000timelimit\\u000040\\u0000gravity\\u0000800\\u0000rankingon\\u00001\\u0000\\u0000\\u0000\\u0005player_\\u0000score_\\u0000deaths_\\u0000ping_\\u0000team_\\u0000time_\\u0000\\u0000Joe Player\\u000030\\u000012\\u0000411\\u00000\\u000010\\u0000L33t 0n3\\u00000\\u00006\\u0000233\\u00001\\u0000325\\u0000Raptor\\u000015\\u000025\\u000063\\u00001\\u0000462\\u0000Gr81\\u00000\\u000016\\u0000294\\u00000\\u0000870\\u0000Flubber\\u000017\\u000012\\u0000232\\u00001\\u0000384\\u0000\\u0000\\u0002team_t\\u0000score_t\\u0000avgping_t\\u0000\\u0000Red\\u0000294\\u0000357\\u0000Blue\\u0000498\\u0000454\\u0000", "client_ip": "172.19.0.5", "client_port": 11111, "instant_key": "3855573338", "command_name": 3, "server_data": {"localip0": "172.19.0.5", "localport": "11111", "natneg": "1", "statechanged": "3", "gamename": "gmtest", "hostname": "GameSpy QR2 Sample", "gamever": "2.00", "hostport": "25000", "mapname": "gmtmap1", "gametype": "arena", "numplayers": "5", "numteams": "2", "maxplayers": "32", "gamemode": "openplaying", "teamplay": "1", "fraglimit": "0", "timelimit": "40", "gravity": "800", "rankingon": "1"}, "player_data": [{"player_0": "Joe Player", "score_0": "30", "deaths_0": "12", "ping_0": "411", "team_0": "0", "time_0": "10"}, {"player_1": "L33t 0n3", "score_1": "0", "deaths_1": "6", "ping_1": "233", "team_1": "1", "time_1": "325"}, {"player_2": "Raptor", "score_2": "15", "deaths_2": "25", "ping_2": "63", "team_2": "1", "time_2": "462"}, {"player_3": "Gr81", "score_3": "0", "deaths_3": "16", "ping_3": "294", "team_3": "0", "time_3": "870"}, {"player_4": "Flubber", "score_4": "17", "deaths_4": "12", "ping_4": "232", "team_4": "1", "time_4": "384"}], "team_data": [{"team_t0": "Red", "score_t0": "294", "avgping_t0": "357"}, {"team_t1": "Blue", "score_t1": "498", "avgping_t1": "454"}], "server_status": 3, "group_id": None, "game_name": "gmtest"} req = HeartBeatRequest(**request) handler = Heartbeathandler(req) - await handler.handle() + handler.handle() handler._response - async def test_available(self): + def test_available(self): request = {"raw_request": "\\t\\u0000\\u0000\\u0000\\u0000\\t\\u0000\\u0000\\u0000\\u0000gamespy\\u0000", "command_name": 9, "instant_key": "0", "client_ip": "127.0.0.1", "server_id": "a8893d8a-664e-4302-bb55-41b3a9229bd1", "client_port": "1234"} new_req = AvaliableRequest(**request) handler = AvaliableHandler(new_req) - await handler.handle() + handler.handle() pass diff --git a/src/frontends/gamespy/library/abstractions/redis_objects.py b/src/frontends/gamespy/library/abstractions/redis_objects.py index b3301f530..a6d26ba84 100644 --- a/src/frontends/gamespy/library/abstractions/redis_objects.py +++ b/src/frontends/gamespy/library/abstractions/redis_objects.py @@ -228,19 +228,19 @@ # result = self._redis_client.get(db_keys[0]) # return result -# async def async_count(self, queries: list[RedisQuery]) -> int: +# def async_count(self, queries: list[RedisQuery]) -> int: # loop = asyncio.get_event_loop() -# result = await loop.run_in_executor(None, self.count, queries) +# result = loop.run_in_executor(None, self.count, queries) # return result -# async def async_query(self, queries: list[RedisQuery]) -> list["RedisKeyValueObject"]: +# def async_query(self, queries: list[RedisQuery]) -> list["RedisKeyValueObject"]: # loop = asyncio.get_event_loop() -# result = await loop.run_in_executor(None, self.query, queries) +# result = loop.run_in_executor(None, self.query, queries) # return result -# async def async_first(self, queries: list[RedisQuery]) -> Optional["RedisKeyValueObject"]: +# def async_first(self, queries: list[RedisQuery]) -> Optional["RedisKeyValueObject"]: # loop = asyncio.get_event_loop() -# result = await loop.run_in_executor(None, self.first, queries) +# result = loop.run_in_executor(None, self.first, queries) # return result diff --git a/src/frontends/gamespy/protocols/chat/abstractions/handler.py b/src/frontends/gamespy/protocols/chat/abstractions/handler.py index 82f927d31..6483965bf 100644 --- a/src/frontends/gamespy/protocols/chat/abstractions/handler.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/handler.py @@ -14,7 +14,7 @@ from frontends.gamespy.protocols.chat.applications.client import Client from frontends.gamespy.protocols.chat.aggregates.exceptions import IRCException import frontends.gamespy.library.abstractions.handler as lib -from typing import Optional, cast +from typing import cast class CmdHandlerBase(lib.CmdHandlerBase): diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/router.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/router.py index ac55ddaf9..a2062c0ba 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/router.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/router.py @@ -6,5 +6,5 @@ @app.post(f"/GetNatNegotiationInfo") -async def get_natneg_info(request: InitPacketInfo): +def get_natneg_info(request: InitPacketInfo): data = request.json From ff8f800f508fa333f8579cbf1778d9d2401bfd4c Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Tue, 13 May 2025 02:59:52 +0000 Subject: [PATCH 183/231] Fix issues on psp. --- common/config.json | 4 + src/.vscode/launch.json | 16 +- .../library/abstractions/contracts.py | 2 +- src/backends/protocols/gamespy/chat/data.py | 390 ++++++++++++------ .../presence_search_player/handlers.py | 102 +++-- .../routers/gamespy/presence_search_player.py | 1 - .../gamespy/library/abstractions/client.py | 2 + .../gamespy/library/abstractions/handler.py | 28 +- .../library/abstractions/server_launcher.py | 6 +- src/frontends/gamespy/library/configs.py | 30 +- .../library/encryption/xor_encryption.py | 4 +- .../gamespy/library/exceptions/general.py | 9 +- .../gamespy/library/log/log_manager.py | 13 +- .../contracts/responses.py | 24 +- .../{v2 => }/applications/server_launcher.py | 2 +- .../direct2game/abstractions/contracts.py | 2 +- .../tests/gamespy/chat/mock_objects.py | 3 +- .../gamespy/game_status/handler_tests.py | 57 ++- .../tests/gamespy/game_status/mock_objects.py | 63 ++- .../tests/gamespy/library/mock_objects.py | 6 +- .../tests/gamespy/natneg/handler_tests.py | 4 +- .../tests/gamespy/natneg/mock_objects.py | 2 +- .../mock_objects.py | 2 +- .../presence_search_player/mock_objects.py | 2 +- .../gamespy/query_report/mock_objects.py | 2 +- 25 files changed, 527 insertions(+), 249 deletions(-) rename src/frontends/gamespy/protocols/server_browser/{v2 => }/applications/server_launcher.py (95%) diff --git a/common/config.json b/common/config.json index 6086089da..28006feff 100644 --- a/common/config.json +++ b/common/config.json @@ -103,5 +103,9 @@ "public_address": "0.0.0.0", "listening_port": 10086 } + }, + "unittest": { + "is_raise_except": false, + "is_collect_request": false } } \ No newline at end of file diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json index bc27b859e..ed6303f5c 100644 --- a/src/.vscode/launch.json +++ b/src/.vscode/launch.json @@ -38,7 +38,7 @@ "name": "pcm", "type": "debugpy", "request": "launch", - "program": "servers/presence_connection_manager/src/applications/server_launcher.py", + "program": "frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py", "console": "integratedTerminal", "env": { "PYTHONPATH": "${workspaceFolder}", @@ -49,7 +49,7 @@ "name": "psp", "type": "debugpy", "request": "launch", - "program": "servers/presence_search_player/src/applications/server_launcher.py", + "program": "frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py", "console": "integratedTerminal", "env": { "PYTHONPATH": "${workspaceFolder}", @@ -60,7 +60,7 @@ "name": "chat", "type": "debugpy", "request": "launch", - "program": "servers/chat/src/applications/server_launcher.py", + "program": "frontends/gamespy/protocols/chat/applications/server_launcher.py", "console": "integratedTerminal", "env": { "PYTHONPATH": "${workspaceFolder}", @@ -71,7 +71,7 @@ "name": "gs", "type": "debugpy", "request": "launch", - "program": "servers/game_status/src/applications/server_launcher.py", + "program": "frontends/gamespy/protocols/game_status/applications/server_launcher.py", "console": "integratedTerminal", "env": { "PYTHONPATH": "${workspaceFolder}", @@ -82,7 +82,7 @@ "name": "natneg", "type": "debugpy", "request": "launch", - "program": "servers/natneg/src/applications/server_launcher.py", + "program": "frontends/gamespy/protocols/natneg/applications/server_launcher.py", "console": "integratedTerminal", "env": { "PYTHONPATH": "${workspaceFolder}", @@ -93,7 +93,7 @@ "name": "qr", "type": "debugpy", "request": "launch", - "program": "servers/query_report/src/applications/server_launcher.py", + "program": "frontends/gamespy/protocols/query_report/applications/server_launcher.py", "console": "integratedTerminal", "env": { "PYTHONPATH": "${workspaceFolder}", @@ -104,7 +104,7 @@ "name": "sb", "type": "debugpy", "request": "launch", - "program": "servers/server_browser/src/v2/applications/server_launcher.py", + "program": "frontends/gamespy/protocols/server_browser/applications/server_launcher.py", "console": "integratedTerminal", "env": { "PYTHONPATH": "${workspaceFolder}", @@ -115,7 +115,7 @@ "name": "web", "type": "debugpy", "request": "launch", - "program": "servers/web_services/src/applications/server_launcher.py", + "program": "frontends/gamespy/protocols/web_services/applications/server_launcher.py", "console": "integratedTerminal", "env": { "PYTHONPATH": "${workspaceFolder}", diff --git a/src/backends/library/abstractions/contracts.py b/src/backends/library/abstractions/contracts.py index a06a67b64..16c8d441f 100644 --- a/src/backends/library/abstractions/contracts.py +++ b/src/backends/library/abstractions/contracts.py @@ -18,7 +18,7 @@ class RequestBase(BaseModel): class Response(BaseModel): message: str - def to_json_dict(self) -> dict: + def to_json_dict(self) -> dict[str, object]: return self.model_dump(mode="json") diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index b8ee2543b..dfa894e39 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -2,8 +2,19 @@ from typing import TYPE_CHECKING, cast from sqlalchemy import Column, func -from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, ChatUserCaches, ChatChannelUserCaches, Users, Profiles, SubProfiles -from frontends.gamespy.protocols.chat.aggregates.exceptions import ChatException, NoSuchNickException +from backends.library.database.pg_orm import ( + PG_SESSION, + ChatChannelCaches, + ChatUserCaches, + ChatChannelUserCaches, + Users, + Profiles, + SubProfiles, +) +from frontends.gamespy.protocols.chat.aggregates.exceptions import ( + ChatException, + NoSuchNickException, +) def is_nick_exist(nick_name: str) -> bool: @@ -19,7 +30,9 @@ def add_nick_cache(cache: ChatUserCaches): PG_SESSION.commit() -def nick_and_email_login(nick_name: str, email: str, password_hash: str) -> tuple[int, int, bool, bool]: +def nick_and_email_login( + nick_name: str, email: str, password_hash: str +) -> tuple[int, int, bool, bool]: """ return userid, profileid, emailverified, banned @@ -28,14 +41,18 @@ def nick_and_email_login(nick_name: str, email: str, password_hash: str) -> tupl assert isinstance(email, str) assert isinstance(password_hash, str) - result = PG_SESSION.query(Users.userid, Profiles.profileid, - Users.emailverified, Users.banned)\ - .join(Profiles, (Users.userid == Profiles.userid))\ + result = ( + PG_SESSION.query( + Users.userid, Profiles.profileid, Users.emailverified, Users.banned + ) + .join(Profiles, (Users.userid == Profiles.userid)) .where( - Users.email == email, - Profiles.nick == nick_name, - Users.password == password_hash - ).first() + Users.email == email, + Profiles.nick == nick_name, + Users.password == password_hash, + ) + .first() + ) if TYPE_CHECKING: result = cast(tuple[int, int, bool, bool], result) if result is None: @@ -45,20 +62,32 @@ def nick_and_email_login(nick_name: str, email: str, password_hash: str) -> tupl return result -def uniquenick_login(uniquenick:str,namespace_id:int)-> tuple[int, int, bool, bool]: + +def uniquenick_login(uniquenick: str, namespace_id: int) -> tuple[int, int, bool, bool]: """ return userid, profileid, emailverified, banned """ assert isinstance(uniquenick, str) assert isinstance(namespace_id, int) - result = PG_SESSION.query(Users.userid, Profiles.profileid,Users.emailverified, Users.banned).join(Profiles,(Users.userid == Profiles.userid)).join(Profiles,(Profiles.profileid == SubProfiles.profileid)).where(SubProfiles.namespaceid == namespace_id,SubProfiles.uniquenick == uniquenick).first() + result = ( + PG_SESSION.query( + Users.userid, Profiles.profileid, Users.emailverified, Users.banned + ) + .join(Profiles, (Users.userid == Profiles.userid)) + .join(Profiles, (Profiles.profileid == SubProfiles.profileid)) + .where( + SubProfiles.namespaceid == namespace_id, + SubProfiles.uniquenick == uniquenick, + ) + .first() + ) if result is None: # fmt: off raise ChatException(f"Can not find user with uniquenick:{uniquenick} in database.") # fmt on if TYPE_CHECKING: - result = cast(tuple[int, int, bool, bool],result) + result = cast(tuple[int, int, bool, bool], result) return result @@ -68,88 +97,152 @@ def uniquenick_login(uniquenick:str,namespace_id:int)-> tuple[int, int, bool, bo def is_cdkey_valid(cdkey: str) -> bool: if TYPE_CHECKING: assert isinstance(SubProfiles.cdkeyenc, Column) - result = PG_SESSION.query(SubProfiles).where( - SubProfiles.cdkeyenc == cdkey).count() + result = PG_SESSION.query(SubProfiles).where(SubProfiles.cdkeyenc == cdkey).count() if result == 0: return False else: return True + # region Channel -def is_channel_exist(channel_name:str,game_name:str)->bool: - channel_count = PG_SESSION.query(ChatChannelCaches)\ - .where(ChatChannelCaches.channel_name == channel_name, - ChatChannelCaches.game_name == game_name, - ChatChannelCaches.update_time >= datetime.now()-timedelta(minutes=10))\ - .count() + +def is_channel_exist(channel_name: str, game_name: str) -> bool: + channel_count = ( + PG_SESSION.query(ChatChannelCaches) + .where( + ChatChannelCaches.channel_name == channel_name, + ChatChannelCaches.game_name == game_name, + ChatChannelCaches.update_time >= datetime.now() - timedelta(minutes=10), + ) + .count() + ) if channel_count == 1: return True else: return False -def add_channel(channel:ChatChannelCaches): + + +def add_channel(channel: ChatChannelCaches): PG_SESSION.add(channel) PG_SESSION.commit() -def get_channel_by_name_and_game(channel_name:str,game_name:str)->ChatChannelCaches|None: - channel = PG_SESSION.query(ChatChannelCaches)\ - .where(ChatChannelCaches.channel_name == channel_name, - ChatChannelCaches.game_name == game_name)\ - .first() +def get_channel_by_name_and_game( + channel_name: str, game_name: str +) -> ChatChannelCaches | None: + channel = ( + PG_SESSION.query(ChatChannelCaches) + .where( + ChatChannelCaches.channel_name == channel_name, + ChatChannelCaches.game_name == game_name, + ) + .first() + ) return channel -def get_channel_by_name(channel_name:str)->ChatChannelCaches|None: - channel = PG_SESSION.query(ChatChannelCaches)\ - .where(ChatChannelCaches.channel_name == channel_name)\ - .first() + +def get_channel_by_name(channel_name: str) -> ChatChannelCaches | None: + channel = ( + PG_SESSION.query(ChatChannelCaches) + .where(ChatChannelCaches.channel_name == channel_name) + .first() + ) return channel -def get_channel_by_name_and_ip_port(channel_name:str,ip:str,port:int)->ChatChannelCaches|None: - assert isinstance(channel_name,str) - assert isinstance(ip,str) - assert isinstance(port,int) - result = PG_SESSION.query(ChatChannelCaches).join(ChatChannelUserCaches).where( - ChatChannelUserCaches.channel_name == channel_name, - ChatChannelUserCaches.remote_ip_address == ip, - ChatChannelUserCaches.remote_port == port).first() + +def get_channel_by_name_and_ip_port( + channel_name: str, ip: str, port: int +) -> ChatChannelCaches | None: + assert isinstance(channel_name, str) + assert isinstance(ip, str) + assert isinstance(port, int) + result = ( + PG_SESSION.query(ChatChannelCaches) + .join(ChatChannelUserCaches) + .where( + ChatChannelUserCaches.channel_name == channel_name, + ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_port == port, + ) + .first() + ) return result -def get_channel_user_cache_by_name(channel_name:str,nick_name:str)->ChatChannelUserCaches|None: - assert isinstance(channel_name,str) - assert isinstance(nick_name,str) - result = PG_SESSION.query(ChatChannelUserCaches).where(ChatChannelUserCaches.channel_name == channel_name,ChatChannelUserCaches.nick_name == nick_name).first() + +def get_channel_user_cache_by_name( + channel_name: str, nick_name: str +) -> ChatChannelUserCaches | None: + assert isinstance(channel_name, str) + assert isinstance(nick_name, str) + result = ( + PG_SESSION.query(ChatChannelUserCaches) + .where( + ChatChannelUserCaches.channel_name == channel_name, + ChatChannelUserCaches.nick_name == nick_name, + ) + .first() + ) return result -def get_channel_user_cache_by_name_and_ip_port(channel_name:str,ip:str,port:int)->ChatChannelUserCaches|None: - result = PG_SESSION.query(ChatChannelUserCaches).where(ChatChannelUserCaches.channel_name == channel_name, - ChatChannelUserCaches.remote_ip_address == ip, - ChatChannelUserCaches.remote_port == port).first() + +def get_channel_user_cache_by_name_and_ip_port( + channel_name: str, ip: str, port: int +) -> ChatChannelUserCaches | None: + result = ( + PG_SESSION.query(ChatChannelUserCaches) + .where( + ChatChannelUserCaches.channel_name == channel_name, + ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_port == port, + ) + .first() + ) return result -def get_channel_user_caches_by_name(channel_name:str)->list[ChatChannelUserCaches]: - assert isinstance(channel_name,str) - result:list[ChatChannelUserCaches] = PG_SESSION.query(ChatChannelUserCaches.key_values).where(ChatChannelUserCaches.channel_name == channel_name).all()#type:ignore + +def get_channel_user_caches_by_name(channel_name: str) -> list[ChatChannelUserCaches]: + assert isinstance(channel_name, str) + result: list[ChatChannelUserCaches] = ( + PG_SESSION.query(ChatChannelUserCaches.key_values) + .where(ChatChannelUserCaches.channel_name == channel_name) + .all() + ) # type:ignore return result -def update_channel_time(channel:ChatChannelCaches): - channel.update_time = datetime.now() # type: ignore + +def update_channel_time(channel: ChatChannelCaches): + channel.update_time = datetime.now() # type: ignore PG_SESSION.commit() + def db_commit(): PG_SESSION.commit() -def get_user_cache_by_nick_name(nick_name:str)->ChatUserCaches|None: - result = PG_SESSION.query(ChatUserCaches).where(ChatUserCaches.nick_name == nick_name).first() + +def get_user_cache_by_nick_name(nick_name: str) -> ChatUserCaches | None: + result = ( + PG_SESSION.query(ChatUserCaches) + .where(ChatUserCaches.nick_name == nick_name) + .first() + ) return result -def get_user_cache_by_ip_port(ip:str,port:int)->ChatUserCaches: - result = PG_SESSION.query(ChatUserCaches).where(ChatUserCaches.remote_ip_address == ip, ChatUserCaches.remote_port == port).first() - assert isinstance(result,ChatUserCaches) + +def get_user_cache_by_ip_port(ip: str, port: int) -> ChatUserCaches: + result = ( + PG_SESSION.query(ChatUserCaches) + .where( + ChatUserCaches.remote_ip_address == ip, ChatUserCaches.remote_port == port + ) + .first() + ) + assert isinstance(result, ChatUserCaches) return result -def get_whois_result(nick:str)->tuple: + +def get_whois_result(nick: str) -> tuple: """ nick is unique in chat """ @@ -157,89 +250,135 @@ def get_whois_result(nick:str)->tuple: if info is None: raise NoSuchNickException(f"User not find by nick name:{nick}.") - channels = PG_SESSION.query(ChatChannelUserCaches.channel_name).join(ChatUserCaches,ChatChannelUserCaches.nick_name == ChatUserCaches.nick_name).where(ChatChannelUserCaches.nick_name == info.nick_name).all() - return info.nick_name,info.user_name,info.nick_name,info.remote_ip_address,channels # type:ignore - - - -def remove_user_caches_by_ip_port(ip:str,port:int): - assert isinstance(ip,str) - assert isinstance(port,int) - PG_SESSION.query(ChatChannelUserCaches).where(ChatChannelUserCaches.remote_ip_address==ip,ChatChannelUserCaches.remote_port == port).delete() - - -def remove_channel(cache:ChatChannelCaches)->None: - assert isinstance(cache,ChatChannelCaches) + channels = ( + PG_SESSION.query(ChatChannelUserCaches.channel_name) + .join( + ChatUserCaches, ChatChannelUserCaches.nick_name == ChatUserCaches.nick_name + ) + .where(ChatChannelUserCaches.nick_name == info.nick_name) + .all() + ) + return ( + info.nick_name, + info.user_name, + info.nick_name, + info.remote_ip_address, + channels, + ) # type:ignore + + +def remove_user_caches_by_ip_port(ip: str, port: int): + assert isinstance(ip, str) + assert isinstance(port, int) + PG_SESSION.query(ChatChannelUserCaches).where( + ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_port == port, + ).delete() + + +def remove_channel(cache: ChatChannelCaches) -> None: + assert isinstance(cache, ChatChannelCaches) PG_SESSION.delete(cache) PG_SESSION.commit() -def remove_user(cache:ChatChannelUserCaches): - assert isinstance(cache,ChatChannelUserCaches) + +def remove_user(cache: ChatChannelUserCaches): + assert isinstance(cache, ChatChannelUserCaches) PG_SESSION.delete(cache) PG_SESSION.commit() -def is_user_exist(ip:str,port:int)->bool: - user_count= PG_SESSION.query(ChatChannelUserCaches).where(ChatChannelUserCaches.remote_ip_address==ip, - ChatChannelUserCaches.remote_port==port).count() - if user_count ==1: + +def is_user_exist(ip: str, port: int) -> bool: + user_count = ( + PG_SESSION.query(ChatChannelUserCaches) + .where( + ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_port == port, + ) + .count() + ) + if user_count == 1: return True else: return False -def update_client(cache:ChatChannelUserCaches): - assert isinstance(cache,ChatChannelUserCaches) + +def update_client(cache: ChatChannelUserCaches): + assert isinstance(cache, ChatChannelUserCaches) PG_SESSION.commit() -def add_invited(channel_name:str,client_ip:str,client_port:int): +def add_invited(channel_name: str, client_ip: str, client_port: int): pass - -def find_channel_by_substring(channel_name:str)->list[dict]: - assert isinstance(channel_name,str) - - names,topics = PG_SESSION.query(ChatChannelCaches.channel_name,ChatChannelCaches.topic)\ - .where(ChatChannelCaches.channel_name.like(f"%{channel_name}%")).all() - users = PG_SESSION.query(ChatChannelUserCaches)\ - .where(ChatChannelUserCaches.channel_name.like(f"%{channel_name}%")).all() - data: list[dict] =[] - for name,topic,count in zip(names,topics,users): - d = { - "channel_name":name, - "total_channel_user":count, - "channel_topic":topic - } +def find_channel_by_substring(channel_name: str) -> list[dict]: + assert isinstance(channel_name, str) + + names, topics = ( + PG_SESSION.query(ChatChannelCaches.channel_name, ChatChannelCaches.topic) + .where(ChatChannelCaches.channel_name.like(f"%{channel_name}%")) + .all() + ) + users = ( + PG_SESSION.query(ChatChannelUserCaches) + .where(ChatChannelUserCaches.channel_name.like(f"%{channel_name}%")) + .all() + ) + data: list[dict] = [] + assert isinstance(names, list) + assert isinstance(topics, list) + assert isinstance(users, list) + for name, topic, count in zip(names, topics, users): + d = {"channel_name": name, "total_channel_user": count, "channel_topic": topic} data.append(d) return data -def find_user_by_substring(user_name:str)->list[dict]: - assert isinstance(user_name,str) - names,topics,users = PG_SESSION.query( - ChatChannelCaches.channel_name, - ChatChannelCaches.topic,func.count(ChatChannelUserCaches.channel_name))\ - .join(ChatUserCaches,ChatUserCaches.nick_name==ChatChannelUserCaches.nick_name)\ - .join(ChatChannelCaches,ChatChannelCaches.channel_name==ChatChannelUserCaches.channel_name)\ - .where(ChatUserCaches.user_name.like(f"%{user_name}%")).all() - data: list[dict] =[] - - for name,topic,count in zip(names,topics,users): - d = { - "channel_name":name, - "total_channel_user":count, - "channel_topic":topic - } + +def find_user_by_substring(user_name: str) -> list[dict]: + assert isinstance(user_name, str) + names, topics, users = ( + PG_SESSION.query( + ChatChannelCaches.channel_name, + ChatChannelCaches.topic, + func.count(ChatChannelUserCaches.channel_name), + ) + .join( + ChatUserCaches, ChatUserCaches.nick_name == ChatChannelUserCaches.nick_name + ) + .join( + ChatChannelCaches, + ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name, + ) + .where(ChatUserCaches.user_name.like(f"%{user_name}%")) + .all() + ) + data: list[dict] = [] + + for name, topic, count in zip(names, topics, users): + d = {"channel_name": name, "total_channel_user": count, "channel_topic": topic} data.append(d) return data -def create_channel_user_caches(chan_user:ChatChannelUserCaches): + +def create_channel_user_caches(chan_user: ChatChannelUserCaches): PG_SESSION.add(chan_user) PG_SESSION.commit() -def get_channel_user_caches(channel_name:str)->list[dict]: - result:list[ChatChannelUserCaches] = PG_SESSION.query(ChatChannelUserCaches).join(ChatChannelCaches,ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name)\ - .join(ChatUserCaches,ChatUserCaches.user_name == ChatChannelUserCaches.user_name)\ - .where(ChatChannelUserCaches.channel_name == channel_name).all() + +def get_channel_user_caches(channel_name: str) -> list[dict]: + result: list[ChatChannelUserCaches] = ( + PG_SESSION.query(ChatChannelUserCaches) + .join( + ChatChannelCaches, + ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name, + ) + .join( + ChatUserCaches, ChatUserCaches.user_name == ChatChannelUserCaches.user_name + ) + .where(ChatChannelUserCaches.channel_name == channel_name) + .all() + ) data = [] for r in result: temp = {} @@ -250,9 +389,22 @@ def get_channel_user_caches(channel_name:str)->list[dict]: data.append(temp) return data -def get_channel_user_cache_by_ip(ip:str,port:int)->list[dict]: - result:list[ChatChannelUserCaches] = PG_SESSION.query(ChatChannelUserCaches).join(ChatChannelCaches,ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name).join(ChatUserCaches,ChatUserCaches.user_name == ChatChannelUserCaches.user_name).where(ChatUserCaches.remote_ip_address==ip,ChatUserCaches.remote_port == port).all() +def get_channel_user_cache_by_ip(ip: str, port: int) -> list[dict]: + result: list[ChatChannelUserCaches] = ( + PG_SESSION.query(ChatChannelUserCaches) + .join( + ChatChannelCaches, + ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name, + ) + .join( + ChatUserCaches, ChatUserCaches.user_name == ChatChannelUserCaches.user_name + ) + .where( + ChatUserCaches.remote_ip_address == ip, ChatUserCaches.remote_port == port + ) + .all() + ) data = [] for r in result: temp = {} @@ -261,4 +413,4 @@ def get_channel_user_cache_by_ip(ip:str,port:int)->list[dict]: temp["public_ip_addr"] = r.remote_ip_address temp["nick_name"] = r.nick_name data.append(temp) - return data \ No newline at end of file + return data diff --git a/src/backends/protocols/gamespy/presence_search_player/handlers.py b/src/backends/protocols/gamespy/presence_search_player/handlers.py index 89e92d01b..d5c8bd07d 100644 --- a/src/backends/protocols/gamespy/presence_search_player/handlers.py +++ b/src/backends/protocols/gamespy/presence_search_player/handlers.py @@ -1,17 +1,44 @@ -from typing import TYPE_CHECKING, cast from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import PG_SESSION, Users, Profiles, SubProfiles import backends.protocols.gamespy.presence_search_player.data as data -from backends.protocols.gamespy.presence_search_player.requests import * +from backends.protocols.gamespy.presence_search_player.requests import ( + CheckRequest, + NewUserRequest, + NicksRequest, + OthersListRequest, + OthersRequest, + SearchRequest, + SearchUniqueRequest, + UniqueSearchRequest, + ValidRequest, +) from frontends.gamespy.library.exceptions.general import UniSpyException -from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import CheckException -from frontends.gamespy.protocols.presence_search_player.contracts.results import CheckResult, NewUserResult, NickResultData, NicksResult, OthersListData, OthersListResult, OthersResult, OthersResultData, SearchResult, SearchResultData, SearchUniqueResult, UniqueSearchResult, ValidResult +from frontends.gamespy.protocols.presence_search_player.aggregates.enums import SearchType +from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import ( + CheckException, +) +from frontends.gamespy.protocols.presence_search_player.contracts.results import ( + CheckResult, + NewUserResult, + NickResultData, + NicksResult, + OthersListData, + OthersListResult, + OthersResult, + OthersResultData, + SearchResult, + SearchResultData, + SearchUniqueResult, + UniqueSearchResult, + ValidResult, +) class CheckHandler(HandlerBase): """ todo: whether need check the partner id, which means whether we need to check subprofiles """ + _request: CheckRequest _result: CheckResult @@ -21,10 +48,13 @@ def _data_operate(self) -> None: if data.verify_email_and_password(self._request.email, self._request.password): raise CheckException("The password is incorrect") self._profile_id = data.get_profile_id( - self._request.email, self._request.password, self._request.nick, self._request.partner_id) + self._request.email, + self._request.password, + self._request.nick, + self._request.partner_id, + ) if self._profile_id is None: - raise CheckException(f"No pid found with email{ - self._request.email}") + raise CheckException(f"No pid found with email{self._request.email}") def _result_construct(self) -> None: assert self._profile_id is not None @@ -36,13 +66,12 @@ class NewUserHandler(HandlerBase): _result: NewUserResult def _data_operate(self) -> None: - # check if user exist self.user = data.get_user(self._request.email) if self.user is None: self._create_user() - assert self.user != None + assert self.user assert isinstance(self.user.userid, int) self.profile = data.get_profile(self.user.userid, self._request.nick) @@ -51,7 +80,10 @@ def _data_operate(self) -> None: assert self.profile is not None assert isinstance(self.profile.profileid, int) self.subprofile = data.get_sub_profile( - profile_id=self.profile.profileid, namespace_id=self._request.namespace_id, product_id=self._request.product_id) + profile_id=self.profile.profileid, + namespace_id=self._request.namespace_id, + product_id=self._request.product_id, + ) if self.subprofile is None: self._create_subprofile() @@ -61,7 +93,8 @@ def _result_construct(self) -> None: assert self.profile is not None assert isinstance(self.profile.profileid, int) self._result = NewUserResult( - user_id=self.user.userid, profile_id=self.profile.profileid) + user_id=self.user.userid, profile_id=self.profile.profileid + ) def _create_user(self) -> None: user_dict = {} @@ -72,7 +105,6 @@ def _create_user(self) -> None: PG_SESSION.commit() def _create_profile(self) -> None: - profile_dict = {} for key, value in self._request.__dict__.items(): if key in Profiles.__dict__: @@ -95,11 +127,11 @@ class NicksHandler(HandlerBase): def _data_operate(self) -> None: self.temp_list = data.get_nick_and_unique_nick_list( - self._request.email, self._request.password, self._request.namespace_id) + self._request.email, self._request.password, self._request.namespace_id + ) self.result_data = [] for nick, unique in self.temp_list: - self.result_data.append( - NickResultData(nick=nick, uniquenick=unique)) + self.result_data.append(NickResultData(nick=nick, uniquenick=unique)) def _result_construct(self) -> None: self._result = NicksResult(data=self.result_data) @@ -111,14 +143,25 @@ class OthersHandler(HandlerBase): def _data_operate(self) -> None: self._data: list = data.get_friend_info_list( - self._request.profile_id, self._request.namespace_id, self._request.game_name) + self._request.profile_id, + self._request.namespace_id, + self._request.game_name, + ) def _result_construct(self) -> None: temp_list = [] for item in self._data: - temp_list.append(OthersResultData( - profile_id=item[0], nick=item[1], uniquenick=item[2], lastname=item[3], firstname=item[4], user_id=item[5], email=item[6] - )) + temp_list.append( + OthersResultData( + profile_id=item[0], + nick=item[1], + uniquenick=item[2], + lastname=item[3], + firstname=item[4], + user_id=item[5], + email=item[6], + ) + ) self._result = OthersResult(data=temp_list) @@ -128,19 +171,20 @@ class OthersListHandler(HandlerBase): def _data_operate(self) -> None: self._data: list = data.get_matched_profile_info_list( - self._request.profile_ids, self._request.namespace_id) + self._request.profile_ids, self._request.namespace_id + ) def _result_construct(self) -> None: temp = [] for profile_id, uniquenick in self._data: - temp.append(OthersListData( - profile_id=profile_id, unique_nick=uniquenick)) + temp.append(OthersListData(profile_id=profile_id, unique_nick=uniquenick)) self._result = OthersListResult(data=temp) # class PlayerMatchHandler(HandlerBase): # _request: playermatchrequest + class SearchHandler(HandlerBase): _request: SearchRequest _result: SearchResult @@ -153,11 +197,13 @@ def _data_operate(self) -> None: assert self._request.email assert self._request.nick self._data = data.get_matched_info_by_nick_and_email( - self._request.nick, self._request.email) + self._request.nick, self._request.email + ) elif self._request.request_type == SearchType.UNIQUENICK_NAMESPACEID_SEARCH: assert self._request.uniquenick self._data = data.get_matched_info_by_uniquenick_and_namespaceid( - self._request.uniquenick, self._request.namespace_id) + self._request.uniquenick, self._request.namespace_id + ) elif self._request.request_type == SearchType.EMAIL_SEARCH: assert self._request.email self._data = data.get_matched_info_by_email(self._request.email) @@ -178,7 +224,8 @@ class SearchUniqueHandler(HandlerBase): def _data_operate(self) -> None: self._data = data.get_matched_info_by_uniquenick_and_namespaceids( - self._request.uniquenick, self._request.namespace_ids) + self._request.uniquenick, self._request.namespace_ids + ) def _result_construct(self) -> None: data = [] @@ -194,7 +241,10 @@ class UniqueSearchHandler(HandlerBase): def _data_operate(self) -> None: self._is_exist = data.is_uniquenick_exist( - self._request.preferred_nick, self._request.namespace_id, self._request.game_name) + self._request.preferred_nick, + self._request.namespace_id, + self._request.game_name, + ) def _result_construct(self) -> None: self._result = UniqueSearchResult(is_uniquenick_exist=self._is_exist) diff --git a/src/backends/routers/gamespy/presence_search_player.py b/src/backends/routers/gamespy/presence_search_player.py index 8194ca06f..d44a8b006 100644 --- a/src/backends/routers/gamespy/presence_search_player.py +++ b/src/backends/routers/gamespy/presence_search_player.py @@ -1,6 +1,5 @@ from fastapi import APIRouter -from backends.library.abstractions.contracts import Response from backends.protocols.gamespy.presence_search_player.handlers import CheckHandler, NewUserHandler, NicksHandler, OthersHandler, OthersListHandler, SearchHandler, SearchUniqueHandler, UniqueSearchHandler, ValidHandler from backends.protocols.gamespy.presence_search_player.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest from backends.urls import PRESENCE_SEARCH_PLAYER diff --git a/src/frontends/gamespy/library/abstractions/client.py b/src/frontends/gamespy/library/abstractions/client.py index 3bb56c5d5..c29eab14f 100644 --- a/src/frontends/gamespy/library/abstractions/client.py +++ b/src/frontends/gamespy/library/abstractions/client.py @@ -124,6 +124,8 @@ def log_network_receving(self, data: object) -> None: def log_network_upload(self, data: object) -> None: self.logger.info(f"{self._log_prefix} [upload]: {data}") + def log_network_fetch(self, data: object) -> None: + self.logger.info(f"{self._log_prefix} [fetch]: {data}") def log_current_class(self, object: "CmdHandlerBase") -> None: self.logger.debug(f"{self._log_prefix} [=>] <{object.__class__.__name__}>") diff --git a/src/frontends/gamespy/library/abstractions/handler.py b/src/frontends/gamespy/library/abstractions/handler.py index 575f081e1..2223964eb 100644 --- a/src/frontends/gamespy/library/abstractions/handler.py +++ b/src/frontends/gamespy/library/abstractions/handler.py @@ -13,6 +13,7 @@ ResponseBase, ) from frontends.gamespy.library.extentions.encoding import UniSpyJsonEncoder +from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER class CmdHandlerBase: @@ -52,6 +53,8 @@ def handle(self) -> None: self._log_current_class() # then we handle it self._request_check() + if CONFIG.unittest.is_collect_request: + return self._data_operate() self._response_construct() if self._response is None: @@ -91,24 +94,28 @@ def _prepare_data(self): self._temp_data["server_id"] = self._client.server_config.server_id self._temp_data["client_port"] = self._client.connection.remote_port + def __get_url(self) -> str: + url = f"{CONFIG.backend.url}/GameSpy/{self._client.server_config.server_name}/{ + self.__class__.__name__ + }" + return url + def _upload_data(self): """ whether need send data to backend if child class do not require feach, overide this function to do nothing """ - url = f"{CONFIG.backend.url}/GameSpy/{self._client.server_config.server_name}/{ - self.__class__.__name__ - }" + self.__url = self.__get_url() json_str = json.dumps( self._temp_data, cls=UniSpyJsonEncoder, ensure_ascii=False ) - self._client.log_network_upload(f"[{url}] {json_str}") + self._client.log_network_upload(f"[{self.__url}] {json_str}") try: response = requests.post( - url, data=json_str, headers={"Content-Type": "application/json"} + self.__url, data=json_str, headers={"Content-Type": "application/json"} ) except requests.exceptions.ConnectionError: - if UniSpyException._is_unittesting: + if CONFIG.unittest.is_raise_except: raise UniSpyException( f"backends api for {[self.__class__.__name__]} is not mocked" ) @@ -125,12 +132,11 @@ def _feach_data(self): if child class do not require feach, overide this function to do nothing """ if self._result_cls is None: - raise UniSpyException( - "_result_cls can not be null when feach data." - ) + raise UniSpyException("_result_cls can not be null when feach data.") assert issubclass(self._result_cls, ResultBase) + self._client.log_network_fetch(f"[{self.__url}] {self._http_result}") - self._result = self._result_cls(**self._http_result) + self._result = self._result_cls(**self._http_result["result"]) def _response_construct(self) -> None: """construct response here in specific child class""" @@ -153,6 +159,6 @@ def _handle_exception(self, ex: Exception) -> None: def _log_current_class(self) -> None: if self._client is None: # todo - print(self) + GLOBAL_LOGGER.debug(f"=> <{self.__class__.__name__}>") else: self._client.log_current_class(self) diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index ff4fdccc1..3963916f5 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -77,13 +77,11 @@ def _connect_to_backend(self): if resp.status_code == 200: data = resp.json() if data["status"] != "online": - raise Exception( + raise UniSpyException( f"backend server: {CONFIG.backend.url} not available." ) - except Exception: - # fmt: off + except requests.ConnectionError: raise UniSpyException(f"backend server: {CONFIG.backend.url} not available.") - # fmt: on def _create_logger(self): assert self.config is not None diff --git a/src/frontends/gamespy/library/configs.py b/src/frontends/gamespy/library/configs.py index 8fec572d0..c79ba0539 100644 --- a/src/frontends/gamespy/library/configs.py +++ b/src/frontends/gamespy/library/configs.py @@ -33,13 +33,9 @@ class RedisConfig(BaseModel): @property def url(self) -> str: if self.ssl: - return ( - f"rediss://{self.user}:{self.password}@{self.server}:{self.port}/0" - ) + return f"rediss://{self.user}:{self.password}@{self.server}:{self.port}/0" else: - return ( - f"redis://{self.user}:{self.password}@{self.server}:{self.port}/0" - ) + return f"redis://{self.user}:{self.password}@{self.server}:{self.port}/0" class ServerConfig(BaseModel): @@ -53,7 +49,7 @@ class LoggingConfig(BaseModel): path: str min_log_level: Literal["debug", "info", "warning", "error"] - @field_validator('path', mode='before') + @field_validator("path", mode="before") def expand_user_path(cls, value): if "~" in value: return os.path.expanduser(value) @@ -67,12 +63,28 @@ class BackendConfig(BaseModel): token_expire_time: int = 30 +class UnittestConfig(BaseModel): + """ + unittest related config + """ + + is_raise_except: bool + """ + whether raise exception after log it + """ + is_collect_request: bool + """ + only connect the request do not actually do anything + """ + + class UniSpyServerConfig(BaseModel): postgresql: PostgreSql redis: RedisConfig backend: BackendConfig servers: dict[str, ServerConfig] logging: LoggingConfig + unittest: UnittestConfig unispy_config = os.environ.get("UNISPY_CONFIG") @@ -83,9 +95,7 @@ class UniSpyServerConfig(BaseModel): # "Unispy server config not found, you should set the UNISPY_CONFIG in the system enviroment." # ) if not os.path.exists(unispy_config): - raise Exception( - "Unispy server config file not exist, check UNISPY_CONFIG path." - ) + raise Exception("Unispy server config file not exist, check UNISPY_CONFIG path.") with open(unispy_config, "r") as f: import json diff --git a/src/frontends/gamespy/library/encryption/xor_encryption.py b/src/frontends/gamespy/library/encryption/xor_encryption.py index ae144eee5..f536eb815 100644 --- a/src/frontends/gamespy/library/encryption/xor_encryption.py +++ b/src/frontends/gamespy/library/encryption/xor_encryption.py @@ -34,9 +34,11 @@ def encode(plaintext: bytes, enc_type: XorType): elif enc_type == XorType.TYPE_3: key = seed_3 result = [] + key_index = 0 for index in range(len(plaintext)): key_index = index % len(key) - enc_byte = ((plaintext[index] ^ key[key_index])) % 255 + print(key_index) + enc_byte = (plaintext[index] ^ key[key_index]) % 255 result.append(enc_byte) return bytes(result) diff --git a/src/frontends/gamespy/library/exceptions/general.py b/src/frontends/gamespy/library/exceptions/general.py index 17e12c245..c7b545819 100644 --- a/src/frontends/gamespy/library/exceptions/general.py +++ b/src/frontends/gamespy/library/exceptions/general.py @@ -1,5 +1,6 @@ from typing import TYPE_CHECKING, Optional +from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER @@ -8,7 +9,6 @@ class UniSpyException(Exception): - _is_unittesting: bool = False message: str """the error message""" @@ -18,9 +18,7 @@ def __init__(self, message: str) -> None: @staticmethod # def handle_exception(e: Exception, client: ClientBase = None): def handle_exception(e: Exception, client: Optional["ClientBase"] = None): - # if we are unittesting we raise the exception out - if UniSpyException._is_unittesting: - raise e + # first log the exception if client is None: GLOBAL_LOGGER.info(str(e)) else: @@ -29,6 +27,9 @@ def handle_exception(e: Exception, client: Optional["ClientBase"] = None): client.log_error(ex.message) else: client.log_error(str(e)) + # if we are unittesting we raise the exception out + if CONFIG.unittest.is_raise_except: + raise e def __repr__(self) -> str: # return super().__repr__() diff --git a/src/frontends/gamespy/library/log/log_manager.py b/src/frontends/gamespy/library/log/log_manager.py index 55e44807e..23f607a0a 100644 --- a/src/frontends/gamespy/library/log/log_manager.py +++ b/src/frontends/gamespy/library/log/log_manager.py @@ -34,14 +34,13 @@ def create_dir(path): class LogManager: - @staticmethod def create(logger_name: str) -> "LogWriter": log_file_path = CONFIG.logging.path create_dir(log_file_path) file_name = f"{log_file_path}/{logger_name}.log" logging.basicConfig( - filename=logger_name, + filename=file_name, level=logging.INFO, format=f"%(asctime)s [{logger_name}] [%(levelname)s]: %(message)s", datefmt="%Y-%m-%d %H:%M:%S", @@ -63,16 +62,22 @@ def create(logger_name: str) -> "LogWriter": ) # Set the desired log level for the console file_handler.setFormatter(formatter) - # 控制台日志输出 + # create console log handler console_handler = logging.StreamHandler() console_handler.setLevel(logging.DEBUG) console_handler.setFormatter(formatter) - + # create logger logger = logging.getLogger(logger_name) logger.addHandler(file_handler) logger.addHandler(console_handler) return LogWriter(logger) + @staticmethod + def logger_exists(name) -> bool: + logger = logging.getLogger(name) + is_exist = len(logger.handlers) > 0 + return is_exist + GLOBAL_LOGGER = LogManager.create("unispy") """ diff --git a/src/frontends/gamespy/protocols/presence_search_player/contracts/responses.py b/src/frontends/gamespy/protocols/presence_search_player/contracts/responses.py index b886eb9c6..74cd4b5c8 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/contracts/responses.py +++ b/src/frontends/gamespy/protocols/presence_search_player/contracts/responses.py @@ -1,4 +1,6 @@ -from frontends.gamespy.protocols.presence_search_player.abstractions.contracts import ResponseBase +from frontends.gamespy.protocols.presence_search_player.abstractions.contracts import ( + ResponseBase, +) from frontends.gamespy.protocols.presence_search_player.contracts.requests import ( CheckRequest, NewUserRequest, @@ -29,10 +31,9 @@ def __init__(self, request: CheckRequest, result: CheckResult) -> None: def build(self): if self._result.profile_id is None: - self.sending_buffer = f"\\cur\\1\\final\\" + self.sending_buffer = "\\cur\\1\\final\\" else: - self.sending_buffer = f"\\cur\\0\\pid\\{ - self._result.profile_id}\\final\\" + self.sending_buffer = f"\\cur\\0\\pid\\{self._result.profile_id}\\final\\" class NewUserResponse(ResponseBase): @@ -45,9 +46,7 @@ def __init__(self, request: NewUserRequest, result: NewUserResult) -> None: super().__init__(request, result) def build(self): - self.sending_buffer = ( - f"\\nur\\\\pid\\{self._result.profile_id}\\final\\" - ) + self.sending_buffer = f"\\nur\\\\pid\\{self._result.profile_id}\\final\\" class NicksResponse(ResponseBase): @@ -111,7 +110,7 @@ def __init__(self, result: SearchResult) -> None: self._result = result def build(self): - self.sending_buffer = f"\\bsr\\" + self.sending_buffer = "\\bsr\\" for info in self._result.data: self.sending_buffer += str(info.profile_id) self.sending_buffer += f"\\nick\\{info.nick}" @@ -120,7 +119,7 @@ def build(self): self.sending_buffer += f"\\firstname\\{info.firstname}" self.sending_buffer += f"\\lastname\\{info.lastname}" self.sending_buffer += f"\\email\\{info.email}" - self.sending_buffer += f"\\bsrdone\\\\more\\0\\final\\" + self.sending_buffer += "\\bsrdone\\\\more\\0\\final\\" class SearchUniqueResponse(ResponseBase): @@ -131,7 +130,7 @@ def __init__(self, result: SearchUniqueResult) -> None: self._result = result def build(self): - self.sending_buffer = "\\bsr" + self.sending_buffer = "\\bsr\\" for info in self._result.data: self.sending_buffer += str(info.profile_id) self.sending_buffer += f"\\nick\\{info.nick}" @@ -140,7 +139,7 @@ def build(self): self.sending_buffer += f"\\firstname\\{info.firstname}" self.sending_buffer += f"\\lastname\\{info.lastname}" self.sending_buffer += f"\\email\\{info.email}" - self.sending_buffer += "\\bsrdone\\\\more\\0" + self.sending_buffer += "\\bsrdone\\\\more\\0\\final\\" class ValidResponse(ResponseBase): @@ -175,6 +174,5 @@ def build(self): self.sending_buffer = "\\us\\1\\nick\\Choose another name\\usdone\\final\\" else: self.sending_buffer = ( - f"\\us\\1\\nick\\{ - self._request.preferred_nick}\\usdone\\final\\" + f"\\us\\1\\nick\\{self._request.preferred_nick}\\usdone\\final\\" ) diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/server_launcher.py b/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py similarity index 95% rename from src/frontends/gamespy/protocols/server_browser/v2/applications/server_launcher.py rename to src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py index 7fba830fb..ebd02d5d4 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py @@ -5,7 +5,6 @@ class ServerLauncher(ServerLauncherBase): - def __init__(self) -> None: super().__init__() self.config = CONFIG.servers["ServerBrowserV2"] @@ -13,6 +12,7 @@ def __init__(self) -> None: def _launch_server(self): assert self.config is not None assert self.logger is not None + # todo: add v1 server here self.server = TcpServer(self.config, Client, self.logger) super()._launch_server() diff --git a/src/frontends/gamespy/protocols/web_services/modules/direct2game/abstractions/contracts.py b/src/frontends/gamespy/protocols/web_services/modules/direct2game/abstractions/contracts.py index 07ada7d07..f07625095 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/direct2game/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/web_services/modules/direct2game/abstractions/contracts.py @@ -1,4 +1,4 @@ -import protocols.web_services.abstractions.contracts as lib +import frontends.gamespy.protocols.web_services.abstractions.contracts as lib from frontends.gamespy.protocols.web_services.aggregations.soap_envelop import SoapEnvelop NAMESPACE = "http://gamespy.net/commerce/" diff --git a/src/frontends/tests/gamespy/chat/mock_objects.py b/src/frontends/tests/gamespy/chat/mock_objects.py index 8f9c360bc..85210b088 100644 --- a/src/frontends/tests/gamespy/chat/mock_objects.py +++ b/src/frontends/tests/gamespy/chat/mock_objects.py @@ -39,7 +39,6 @@ create_mock_url, ) from frontends.gamespy.protocols.chat.applications.client import Client -from frontends.gamespy.library.exceptions.general import UniSpyException class WebSocketBrockerMock(BrockerBase): @@ -63,7 +62,7 @@ def start_brocker(self): def create_client() -> Client: - UniSpyException._is_unittesting = True + CONFIG.unittest.is_raise_except = True handler = RequestHandlerMock() logger = LogMock() config = CONFIG.servers["Chat"] diff --git a/src/frontends/tests/gamespy/game_status/handler_tests.py b/src/frontends/tests/gamespy/game_status/handler_tests.py index f5d7db3cd..b632cf55b 100644 --- a/src/frontends/tests/gamespy/game_status/handler_tests.py +++ b/src/frontends/tests/gamespy/game_status/handler_tests.py @@ -1,18 +1,28 @@ - -from typing import cast import unittest import responses from frontends.gamespy.protocols.game_status.aggregations.gscrypt import GSCrypt -from frontends.gamespy.protocols.game_status.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest -from frontends.gamespy.protocols.game_status.aggregations.enums import PersistStorageType -from frontends.gamespy.protocols.game_status.applications.handlers import AuthPlayerHandler, SetPlayerDataHandler, UpdateGameHandler -from frontends.gamespy.protocols.game_status.applications.switcher import Switcher +from frontends.gamespy.protocols.game_status.contracts.requests import ( + AuthGameRequest, + AuthPlayerRequest, + GetPlayerDataRequest, + GetProfileIdRequest, + NewGameRequest, + SetPlayerDataRequest, + UpdateGameRequest, +) +from frontends.gamespy.protocols.game_status.aggregations.enums import ( + PersistStorageType, +) +from frontends.gamespy.protocols.game_status.applications.handlers import ( + AuthPlayerHandler, + SetPlayerDataHandler, + UpdateGameHandler, +) from frontends.tests.gamespy.game_status.mock_objects import create_client class HandlerTests(unittest.TestCase): - @responses.activate # @unittest.skip("not implemented") def test_set_player_data_20230329(self): @@ -21,13 +31,14 @@ def test_set_player_data_20230329(self): request = SetPlayerDataRequest(raw) request.parse() self.assertEqual(1, request.profile_id) - self.assertEqual(PersistStorageType.PRIVATE_READ_WRITE, - request.storage_type) + self.assertEqual(PersistStorageType.PRIVATE_READ_WRITE, request.storage_type) self.assertEqual(0, request.data_index) self.assertEqual("", request.data) self.assertEqual(111, request.length) self.assertEqual( - "|title||victories|0|timestamp|37155|league|Team17|winner||crc|-1|player_0|spyguy|ip_0||pid_0|0|auth_0|[00]", request.report) + "|title||victories|0|timestamp|37155|league|Team17|winner||crc|-1|player_0|spyguy|ip_0||pid_0|0|auth_0|[00]", + request.report, + ) handler = SetPlayerDataHandler(client, request) handler.handle() @@ -38,21 +49,26 @@ def test_gamespysdk_update_game_20230329(self): raw1 = "\\updgame\\\\sesskey\\20298203\\connid\\0\\done\\0\\gamedata\\\u0001hostname\u0001My l33t Server\u0001mapname\u0001Level 33\u0001gametype\u0001hunter\u0001gamever\u00011.230000\u0001player_0\u0001Bob!\u0001points_0\u00014\u0001deaths_0\u00012\u0001pid_0\u000132432423\u0001auth_0\u00017cca8e60a13781eebc820a50754f57cd\u0001player_1\u0001Joey\u0001points_1\u00012\u0001deaths_1\u00014\u0001pid_1\u0001643423\u0001auth_1\u000119ea14d9d92a7fcc635cf5716944d9bc\\final\\" raw2 = "\\updgame\\\\sesskey\\20298203\\connid\\0\\done\\1\\gamedata\\\u0001hostname\u0001My l33t Server\u0001mapname\u0001Level 33\u0001gametype\u0001hunter\u0001gamever\u00011.230000\u0001player_0\u0001Bob!\u0001points_0\u00016\u0001deaths_0\u00013\u0001pid_0\u000132432423\u0001auth_0\u00017cca8e60a13781eebc820a50754f57cd\u0001player_1\u0001Joey\u0001points_1\u00013\u0001deaths_1\u00016\u0001pid_1\u0001643423\u0001auth_1\u000119ea14d9d92a7fcc635cf5716944d9bc\\final\\" client = create_client() - switcher = Switcher(client, raw1) - switcher.handle() - request: UpdateGameRequest = cast( - UpdateGameRequest, switcher._handlers[0]._request) + request = UpdateGameRequest(raw1) + handler = UpdateGameHandler(client, request) + handler.handle() self.assertEqual("20298203", request.session_key) self.assertEqual(0, request.connection_id) self.assertEqual(False, request.is_done) - self.assertEqual("\u0001hostname\u0001My l33t Server\u0001mapname\u0001Level 33\u0001gametype\u0001hunter\u0001gamever\u00011.230000\u0001player_0\u0001Bob!\u0001points_0\u00014\u0001deaths_0\u00012\u0001pid_0\u000132432423\u0001auth_0\u00017cca8e60a13781eebc820a50754f57cd\u0001player_1\u0001Joey\u0001points_1\u00012\u0001deaths_1\u00014\u0001pid_1\u0001643423\u0001auth_1\u000119ea14d9d92a7fcc635cf5716944d9bc", request.game_data) + self.assertEqual( + "\u0001hostname\u0001My l33t Server\u0001mapname\u0001Level 33\u0001gametype\u0001hunter\u0001gamever\u00011.230000\u0001player_0\u0001Bob!\u0001points_0\u00014\u0001deaths_0\u00012\u0001pid_0\u000132432423\u0001auth_0\u00017cca8e60a13781eebc820a50754f57cd\u0001player_1\u0001Joey\u0001points_1\u00012\u0001deaths_1\u00014\u0001pid_1\u0001643423\u0001auth_1\u000119ea14d9d92a7fcc635cf5716944d9bc", + request.game_data, + ) + request = UpdateGameRequest(raw2) + handler = UpdateGameHandler(client, request) + handler.handle() @responses.activate @unittest.skip("Encrypted request is not correct") def test_worm3d_auth_player(self): - raw = b"2\x0F\x16\x10]%+=veKaB3a(UC`b$\x1CO\x11VZX\x09w\x1Cu\x08L@\x13=X!\x1E{\x0EL\x1DLf[qN \x04G\x130[#N'\x09(IC`b$\\final\\" + raw = b"2\\x0F\\x16\\x10]%+=veKaB3a(UC`b$\\x1CO\\x11VZX\\x09w\\x1Cu\\x08L@\\x13=X!\\x1E{\\x0EL\\x1DLf[qN \\x04G\\x130[#N'\\x09(IC`b$\\final\\" plaintext = GSCrypt().decrypt(raw) - request = AuthPlayerRequest(raw) + request = AuthPlayerRequest(plaintext) client = create_client() handler = AuthPlayerHandler(client, request) handler.handle() @@ -68,13 +84,14 @@ def test_auth(self): self.assertEqual(1, request.local_id) def test_get_player_data(self): - raw = "\\getpd\\\\pid\\0\\ptype\\0\\dindex\\1\\keys\\hello\x01hi\\lid\\1\\final\\" + raw = ( + "\\getpd\\\\pid\\0\\ptype\\0\\dindex\\1\\keys\\hello\x01hi\\lid\\1\\final\\" + ) request = GetPlayerDataRequest(raw) request.parse() self.assertEqual(0, request.profile_id) - self.assertEqual(PersistStorageType.PRIVATE_READ_ONLY, - request.storage_type) + self.assertEqual(PersistStorageType.PRIVATE_READ_ONLY, request.storage_type) self.assertEqual(1, request.data_index) self.assertEqual(2, len(request.keys)) self.assertEqual("hello", request.keys[0]) diff --git a/src/frontends/tests/gamespy/game_status/mock_objects.py b/src/frontends/tests/gamespy/game_status/mock_objects.py index 3892084d0..bdd0e135f 100644 --- a/src/frontends/tests/gamespy/game_status/mock_objects.py +++ b/src/frontends/tests/gamespy/game_status/mock_objects.py @@ -1,35 +1,66 @@ from typing import cast from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.protocols.game_status.applications.client import Client -from frontends.gamespy.protocols.game_status.applications.handlers import AuthGameHandler, AuthPlayerHandler, GetPlayerDataHandler, GetProfileIdHandler, NewGameHandler, SetPlayerDataHandler, UpdateGameHandler -from frontends.gamespy.protocols.game_status.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult -from frontends.tests.gamespy.library.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url +from frontends.gamespy.protocols.game_status.applications.handlers import ( + AuthGameHandler, + AuthPlayerHandler, + GetPlayerDataHandler, + GetProfileIdHandler, + NewGameHandler, + SetPlayerDataHandler, + UpdateGameHandler, +) +from frontends.gamespy.protocols.game_status.contracts.results import ( + AuthGameResult, + AuthPlayerResult, + GetPlayerDataResult, + GetProfileIdResult, +) +from frontends.tests.gamespy.library.mock_objects import ( + ConnectionMock, + LogMock, + RequestHandlerMock, + create_mock_url, +) -from frontends.gamespy.library.exceptions.general import UniSpyException class ClientMock(Client): pass def create_client() -> Client: - UniSpyException._is_unittesting = True + CONFIG.unittest.is_raise_except = True handler = RequestHandlerMock() logger = LogMock() conn = ConnectionMock( handler=handler, - config=CONFIG.servers["GameStatus"], t_client=ClientMock, - logger=logger) + config=CONFIG.servers["GameStatus"], + t_client=ClientMock, + logger=logger, + ) config = CONFIG.servers["GameStatus"] create_mock_url(config, SetPlayerDataHandler, {"message": "ok"}) - create_mock_url(config, GetPlayerDataHandler, GetPlayerDataResult( - **{"keyvalues": {"hello": "hello_value", "hi": "hi_value"}}).model_dump()) - create_mock_url(config, GetProfileIdHandler, - GetProfileIdResult(**{"profile_id": 1}).model_dump()) + create_mock_url( + config, + GetPlayerDataHandler, + GetPlayerDataResult( + **{"keyvalues": {"hello": "hello_value", "hi": "hi_value"}} + ).model_dump(), + ) + create_mock_url( + config, + GetProfileIdHandler, + GetProfileIdResult(**{"profile_id": 1}).model_dump(), + ) create_mock_url(config, UpdateGameHandler, {"message": "ok"}) - create_mock_url(config, AuthPlayerHandler, - AuthPlayerResult(**{"profile_id": 1}).model_dump()) - create_mock_url(config, NewGameHandler, {"message": "ok"}) - create_mock_url(config, AuthGameHandler, AuthGameResult( - **{"session_key": "123456"}).model_dump()) + create_mock_url( + config, AuthPlayerHandler, AuthPlayerResult(**{"profile_id": 1}).model_dump() + ) + create_mock_url(config, NewGameHandler, {"message": "ok"}) + create_mock_url( + config, + AuthGameHandler, + AuthGameResult(**{"session_key": "123456"}).model_dump(), + ) return cast(Client, conn._client) diff --git a/src/frontends/tests/gamespy/library/mock_objects.py b/src/frontends/tests/gamespy/library/mock_objects.py index c5d75a870..15f84cde7 100644 --- a/src/frontends/tests/gamespy/library/mock_objects.py +++ b/src/frontends/tests/gamespy/library/mock_objects.py @@ -4,7 +4,7 @@ from frontends.gamespy.library.abstractions.brocker import BrockerBase from frontends.gamespy.library.abstractions.connections import ConnectionBase from frontends.gamespy.library.abstractions.handler import CmdHandlerBase -from frontends.gamespy.library.log.log_manager import LogWriter +from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER, LogWriter from frontends.gamespy.library.configs import CONFIG, ServerConfig @@ -27,15 +27,19 @@ def __init__(self) -> None: def debug(self, message): print(message) + GLOBAL_LOGGER.debug(message) def info(self, message): print(message) + GLOBAL_LOGGER.info(message) def error(self, message): print(message) + GLOBAL_LOGGER.error(message) def warn(self, message): print(message) + GLOBAL_LOGGER.warn(message) class BrokerMock(BrockerBase): diff --git a/src/frontends/tests/gamespy/natneg/handler_tests.py b/src/frontends/tests/gamespy/natneg/handler_tests.py index ed8c73a6b..dd4c578ee 100644 --- a/src/frontends/tests/gamespy/natneg/handler_tests.py +++ b/src/frontends/tests/gamespy/natneg/handler_tests.py @@ -1,5 +1,6 @@ import unittest +from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.protocols.natneg.applications.handlers import AddressCheckHandler, ErtAckHandler, InitHandler, NatifyHandler import responses from frontends.gamespy.protocols.natneg.contracts.requests import ( @@ -15,10 +16,9 @@ PreInitState, RequestType, ) -from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.tests.gamespy.natneg.mock_objects import create_client -UniSpyException._is_unittesting = True +CONFIG.unittest.is_raise_except = True class HandlerTests(unittest.TestCase): diff --git a/src/frontends/tests/gamespy/natneg/mock_objects.py b/src/frontends/tests/gamespy/natneg/mock_objects.py index e0416fa72..1135d3e70 100644 --- a/src/frontends/tests/gamespy/natneg/mock_objects.py +++ b/src/frontends/tests/gamespy/natneg/mock_objects.py @@ -22,7 +22,7 @@ class ClientMock(Client): def create_client() -> Client: - UniSpyException._is_unittesting = True + CONFIG.unittest.is_raise_except = True handler = RequestHandlerMock() logger = LogMock() conn = ConnectionMock( diff --git a/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py b/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py index f24f69866..b7deb8990 100644 --- a/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py +++ b/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py @@ -21,7 +21,7 @@ class ClientMock(Client): def create_client() -> Client: - UniSpyException._is_unittesting = True + CONFIG.unittest.is_raise_except = True handler = RequestHandlerMock() logger = LogMock() conn = ConnectionMock( diff --git a/src/frontends/tests/gamespy/presence_search_player/mock_objects.py b/src/frontends/tests/gamespy/presence_search_player/mock_objects.py index 924f487b3..46c8962fc 100644 --- a/src/frontends/tests/gamespy/presence_search_player/mock_objects.py +++ b/src/frontends/tests/gamespy/presence_search_player/mock_objects.py @@ -12,7 +12,7 @@ class ClientMock(Client): def create_client() -> Client: - UniSpyException._is_unittesting = True + CONFIG.unittest.is_raise_except = True handler = RequestHandlerMock() logger = LogMock() conn = ConnectionMock( diff --git a/src/frontends/tests/gamespy/query_report/mock_objects.py b/src/frontends/tests/gamespy/query_report/mock_objects.py index d4ca1a0c7..037335d52 100644 --- a/src/frontends/tests/gamespy/query_report/mock_objects.py +++ b/src/frontends/tests/gamespy/query_report/mock_objects.py @@ -12,7 +12,7 @@ class ClientMock(Client): def create_client() -> Client: - UniSpyException._is_unittesting = True + CONFIG.unittest.is_raise_except = True handler = RequestHandlerMock() logger = LogMock() conn = ConnectionMock( From 2afde5e2637b6a214506e9be93fa67d88e8fa1bd Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Tue, 13 May 2025 03:05:00 +0000 Subject: [PATCH 184/231] Add unitest condition. --- .../gamespy/library/abstractions/server_launcher.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index 3963916f5..6d34a9654 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -68,6 +68,8 @@ def _launch_server(self) -> None: self.server.start() def _connect_to_backend(self): + if CONFIG.unittest.is_collect_request: + return try: # post our server config to backends to register assert self.config is not None @@ -81,7 +83,9 @@ def _connect_to_backend(self): f"backend server: {CONFIG.backend.url} not available." ) except requests.ConnectionError: - raise UniSpyException(f"backend server: {CONFIG.backend.url} not available.") + raise UniSpyException( + f"backend server: {CONFIG.backend.url} not available." + ) def _create_logger(self): assert self.config is not None From 3a691b87482ab90c907d8b9c3fb943d92ba3bc5d Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Tue, 13 May 2025 08:52:41 +0000 Subject: [PATCH 185/231] Fix issues with gptest.c --- .../library/abstractions/handler_base.py | 17 +- .../presence_connection_manager/data.py | 289 +++++++++++------- .../presence_connection_manager/handlers.py | 101 +++--- .../presence_connection_manager/requests.py | 26 +- src/backends/routers/home.py | 31 +- .../handler_tests.py | 2 +- .../library/abstractions/server_launcher.py | 2 +- .../gamespy/library/extentions/encoding.py | 4 + .../gamespy/library/network/http_handler.py | 19 +- .../gamespy/library/network/tcp_handler.py | 8 +- .../gamespy/library/network/udp_handler.py | 14 +- .../abstractions/contracts.py | 8 +- .../aggregates/user_status.py | 24 -- .../applications/client.py | 6 +- .../applications/handlers.py | 45 +-- .../contracts/requests.py | 119 +++++--- .../contracts/responses.py | 94 +++--- .../contracts/results.py | 24 +- .../buddy_request_tests.py | 42 --- .../presence_connection_manager/game_tests.py | 4 +- .../general_request_tests.py | 74 ----- .../handler_tests.py | 5 + .../profile_request_tests.py | 65 ---- .../request_tests.py | 174 +++++++++++ 24 files changed, 673 insertions(+), 524 deletions(-) delete mode 100644 src/frontends/tests/gamespy/presence_connection_manager/buddy_request_tests.py delete mode 100644 src/frontends/tests/gamespy/presence_connection_manager/general_request_tests.py create mode 100644 src/frontends/tests/gamespy/presence_connection_manager/handler_tests.py delete mode 100644 src/frontends/tests/gamespy/presence_connection_manager/profile_request_tests.py create mode 100644 src/frontends/tests/gamespy/presence_connection_manager/request_tests.py diff --git a/src/backends/library/abstractions/handler_base.py b/src/backends/library/abstractions/handler_base.py index d84eb9530..5c58c71e8 100644 --- a/src/backends/library/abstractions/handler_base.py +++ b/src/backends/library/abstractions/handler_base.py @@ -1,7 +1,6 @@ from typing import Optional, final from backends.library.abstractions.contracts import ( DataResponse, - ErrorResponse, OKResponse, RequestBase, Response, @@ -10,7 +9,6 @@ import logging -from frontends.gamespy.library.exceptions.general import UniSpyException class HandlerBase: @@ -45,17 +43,10 @@ def __init__(self, request: RequestBase) -> None: self._response = OKResponse() def handle(self) -> None: - try: - self._request_check() - self._data_operate() - self._result_construct() - self._response_construct() - except UniSpyException as ex: - self.logger.error(ex.message) - self._response = ErrorResponse(message=ex.message) - except Exception as ex: - self.logger.error(ex) - self._response = ErrorResponse(message=str(ex)) + self._request_check() + self._data_operate() + self._result_construct() + self._response_construct() def _request_check(self) -> None: """virtual method""" diff --git a/src/backends/protocols/gamespy/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py index 979ffeeae..e7356c506 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/data.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/data.py @@ -12,14 +12,29 @@ Users, PG_SESSION, ) -from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import GPStatusCode, LoginStatus -from frontends.gamespy.protocols.presence_connection_manager.aggregates.user_status import UserStatus -from frontends.gamespy.protocols.presence_connection_manager.contracts.results import GetProfileData, LoginData -from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import GPAddBuddyException, GPDatabaseException, GPStatusException, GPException -from backends.protocols.gamespy.presence_search_player.data import is_email_exist +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( + GPStatusCode, + LoginStatus, +) + +from frontends.gamespy.protocols.presence_connection_manager.contracts.results import ( + GetProfileData, + LoginData, +) +from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import ( + GPAddBuddyException, + GPDatabaseException, + GPStatusException, + GPException, +) +import backends.protocols.gamespy.presence_search_player.data as psp # region General +def is_email_exist(email: str): + return psp.is_email_exist(email) + + def update_online_time(ip: str, port: int): if TYPE_CHECKING: assert isinstance(Users.lastip, Column) @@ -32,8 +47,7 @@ def update_online_time(ip: str, port: int): def delete_friend_by_profile_id(profile_id: int): - friend = PG_SESSION.query(Friends).where( - Friends.friendid == profile_id).first() + friend = PG_SESSION.query(Friends).where(Friends.friendid == profile_id).first() if friend is None: raise GPDatabaseException( f"friend deletion have errors on profile id:{profile_id}" @@ -64,6 +78,7 @@ def get_friend_profile_id_list(profile_id: int, namespace_id: int) -> list[int]: result = cast(list[int], result) return result + # region Profile @@ -84,8 +99,11 @@ def get_profile_infos(profile_id: int, session_key: str) -> GetProfileData: assert isinstance(SubProfiles.namespaceid, Column) assert isinstance(SubProfiles.session_key, Column) - namespace_id = PG_SESSION.query(SubProfiles.namespaceid).where( - SubProfiles.session_key == session_key).first() + namespace_id = ( + PG_SESSION.query(SubProfiles.namespaceid) + .where(SubProfiles.session_key == session_key) + .first() + ) if namespace_id is None: raise GPException("namespace not found") @@ -114,8 +132,13 @@ def get_profile_infos(profile_id: int, session_key: str) -> GetProfileData: assert isinstance(user.email, str) assert isinstance(Profiles.extra_info, dict) - data = GetProfileData(nick=profile.nick, profile_id=profile.profileid, - unique_nick=subprofile.uniquenick, email=user.email, extra_infos=Profiles.extra_info) + data = GetProfileData( + nick=profile.nick, + profile_id=profile.profileid, + unique_nick=subprofile.uniquenick, + email=user.email, + extra_infos=Profiles.extra_info, + ) return data @@ -140,8 +163,7 @@ def get_user_info_list(email: str, nick_name: str) -> list[tuple[int, int, int]] assert isinstance(Profiles.nick, Column) result = ( - PG_SESSION.query(Users.userid, Profiles.profileid, - SubProfiles.subprofileid) + PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .where(Users.email == email, Profiles.nick == nick_name) @@ -163,8 +185,7 @@ def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: assert isinstance(SubProfiles.namespaceid, Column) result = ( - PG_SESSION.query(Users.userid, Profiles.profileid, - SubProfiles.subprofileid) + PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .where( @@ -178,7 +199,9 @@ def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: return result -def get_user_infos_by_uniquenick_namespace_id(unique_nick: str, namespace_id: int) -> LoginData | None: +def get_user_infos_by_uniquenick_namespace_id( + unique_nick: str, namespace_id: int +) -> LoginData | None: if TYPE_CHECKING: assert isinstance(Profiles.profileid, Column) assert isinstance(Profiles.userid, Column) @@ -192,16 +215,18 @@ def get_user_infos_by_uniquenick_namespace_id(unique_nick: str, namespace_id: in assert isinstance(Users.banned, Column) result = ( - PG_SESSION.query(Users.userid, - Profiles.profileid, - SubProfiles.subprofileid, - Profiles.nick, - Users.email, - SubProfiles.uniquenick, - Users.password, - Users.emailverified, - Users.banned, - SubProfiles.namespaceid) + PG_SESSION.query( + Users.userid, + Profiles.profileid, + SubProfiles.subprofileid, + Profiles.nick, + Users.email, + SubProfiles.uniquenick, + Users.password, + Users.emailverified, + Users.banned, + SubProfiles.namespaceid, + ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .where( @@ -228,27 +253,37 @@ def get_user_infos_by_nick_email(nick: str, email: str) -> LoginData | None: assert isinstance(SubProfiles.namespaceid, Column) result = ( - PG_SESSION.query(Users.userid, - Profiles.profileid, - SubProfiles.subprofileid, - Profiles.nick, - Users.email, - SubProfiles.uniquenick, - Users.password, - Users.emailverified, - Users.banned, - SubProfiles.namespaceid - ) + PG_SESSION.query( + Users.userid, + Profiles.profileid, + SubProfiles.subprofileid, + Profiles.nick, + Users.email, + SubProfiles.uniquenick, + Users.password, + Users.emailverified, + Users.banned, + SubProfiles.namespaceid, + ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where( - Users.email == email, - Profiles.nick == nick - ) + .where(Users.email == email, Profiles.nick == nick) .first() ) + data = { + "user_id": result[0], + "profile_id": result[1], + "sub_profile_id": result[2], + "nick": result[3], + "email": result[4], + "unique_nick": result[5], + "password_hash": result[6], + "email_verified_flag": result[7], + "banned_flag": result[8], + "namespace_id": result[9], + } if result is not None: - return LoginData(*result) # type: ignore + return LoginData(**data) # type: ignore else: return None @@ -275,25 +310,37 @@ def get_user_infos_by_authtoken(auth_token: str) -> LoginData | None: assert isinstance(SubProfiles.authtoken, Column) result = ( - PG_SESSION.query(Users.userid, - Profiles.profileid, - SubProfiles.subprofileid, - Profiles.nick, - Users.email, - SubProfiles.uniquenick, - Users.password, - Users.emailverified, - Users.banned, - SubProfiles.namespaceid) + PG_SESSION.query( + Users.userid, + Profiles.profileid, + SubProfiles.subprofileid, + Profiles.nick, + Users.email, + SubProfiles.uniquenick, + Users.password, + Users.emailverified, + Users.banned, + SubProfiles.namespaceid, + ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where( - SubProfiles.authtoken == auth_token - ) + .where(SubProfiles.authtoken == auth_token) .first() ) + data = { + "user_id": result[0], + "profile_id": result[1], + "sub_profile_id": result[2], + "nick": result[3], + "email": result[4], + "unique_nick": result[5], + "password_hash": result[6], + "email_verified_flag": result[7], + "banned_flag": result[8], + "namespace_id": result[9], + } if result is not None: - return LoginData(*result) # type: ignore + return LoginData(**data) # type: ignore else: return None @@ -304,12 +351,14 @@ def get_block_list(profile_id: int, namespace_id: int) -> list[int]: .where( Blocked.namespaceid == namespace_id, Blocked.profileid == profile_id, - ).all() + ) + .all() ) if TYPE_CHECKING: result = cast(list[int], result) return result + # region Buddy @@ -319,7 +368,8 @@ def get_buddy_list(profile_id: int, namespace_id: int) -> list[int]: .where( Blocked.namespaceid == namespace_id, Blocked.profileid == profile_id, - ).all() + ) + .all() ) # assert isinstance(result, list) if TYPE_CHECKING: @@ -330,8 +380,11 @@ def get_buddy_list(profile_id: int, namespace_id: int) -> list[int]: def update_block(profile_id: int, target_id: int, session_key: str) -> None: if TYPE_CHECKING: assert isinstance(SubProfiles.session_key, Column) - namespace_id = PG_SESSION.query(SubProfiles).where( - SubProfiles.session_key == session_key).first() + namespace_id = ( + PG_SESSION.query(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) result = ( PG_SESSION.query(Blocked) .where( @@ -342,8 +395,7 @@ def update_block(profile_id: int, target_id: int, session_key: str) -> None: .count() ) if result == 0: - b = Blocked(targetid=target_id, namespaceid=namespace_id, - profileid=profile_id) + b = Blocked(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) PG_SESSION.add(b) PG_SESSION.commit() @@ -358,8 +410,7 @@ def update_friend_info(target_id: int, profile_id: int, namespace_id: int): ) .count() ) - f = Friends(targetid=target_id, namespaceid=namespace_id, - profileid=profile_id) + f = Friends(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) if result == 0: PG_SESSION.add(f) @@ -409,69 +460,98 @@ def update_subprofile_info(subprofile: SubProfiles): PG_SESSION.commit() -def add_friend_request(profileid: int, targetid: int, namespace_id: int, reason: str) -> None: - data = PG_SESSION.query(FriendAddRequest).where(FriendAddRequest.profileid == - profileid, - FriendAddRequest.targetid == targetid, - FriendAddRequest.namespaceid == namespace_id).first() +def add_friend_request( + profileid: int, targetid: int, namespace_id: int, reason: str +) -> None: + data = ( + PG_SESSION.query(FriendAddRequest) + .where( + FriendAddRequest.profileid == profileid, + FriendAddRequest.targetid == targetid, + FriendAddRequest.namespaceid == namespace_id, + ) + .first() + ) if data is not None: raise GPAddBuddyException("Request is existed, add friend ignored") - request = FriendAddRequest(profileid=profileid, targetid=targetid, - namespaceid=namespace_id, reason=reason) + request = FriendAddRequest( + profileid=profileid, targetid=targetid, namespaceid=namespace_id, reason=reason + ) PG_SESSION.add(request) PG_SESSION.commit() -def get_status(session_key: str) -> UserStatus: +def get_status(session_key: str) -> dict: if TYPE_CHECKING: assert isinstance(SubProfiles.session_key, Column) - result = PG_SESSION.query(Profiles).join(SubProfiles).where( - SubProfiles.session_key == session_key).first() + result = ( + PG_SESSION.query(Profiles) + .join(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) if result is None: - raise GPStatusException( - "No profile found with the provided session key") + raise GPStatusException("No profile found with the provided session key") if TYPE_CHECKING: assert isinstance(result.statstring, str) - assert isinstance(result.location, str) assert isinstance(result.status, GPStatusCode) - - data = UserStatus(status_string=result.statstring, - location_string=result.location, current_status=result.status) + if "location" not in result.extra_info: + result.extra_info["location"] = "" + data = { + "status_string": result.statstring, + "location_string": result.extra_info["location"], + "current_status": result.status, + } return data -def update_status(session_key: str, status: UserStatus): +def update_status( + session_key: str, + current_status: GPStatusCode, + location_string: str, + status_string: str, +): if TYPE_CHECKING: assert isinstance(SubProfiles.session_key, Column) - result = PG_SESSION.query(Profiles).join(SubProfiles).where( - SubProfiles.session_key == session_key).first() + result = ( + PG_SESSION.query(Profiles) + .join(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) if result is None: - raise GPStatusException( - "No profile found with the provided session key") + raise GPStatusException("No profile found with the provided session key") - result.statstring = status.status_string - result.status = status.current_status - result.location = status.location_string + result.statstring = status_string + result.status = current_status + assert isinstance(result.extra_info, list) + result.extra_info.append(location_string) PG_SESSION.commit() def update_new_nick(session_key: str, old_nick: str, new_nick: str): - result = PG_SESSION.query(Profiles).join(SubProfiles).where( - SubProfiles.session_key == session_key).first() + result = ( + PG_SESSION.query(Profiles) + .join(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) if result.nick == old_nick and result.nick != new_nick: result.nick = new_nick PG_SESSION.commit() def update_cdkey(session_key: str, cdkey: str): - subprofile = PG_SESSION.query(SubProfiles).where( - SubProfiles.session_key == session_key).first() + subprofile = ( + PG_SESSION.query(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) if subprofile is None: - raise GPDatabaseException( - f"no subprofile found with session key:{session_key}") + raise GPDatabaseException(f"no subprofile found with session key:{session_key}") subprofile.cdkeyenc = cdkey @@ -479,22 +559,27 @@ def update_cdkey(session_key: str, cdkey: str): def update_uniquenick(session_key: str, uniquenick: str): - subprofile = PG_SESSION.query(SubProfiles).where( - SubProfiles.session_key == session_key).first() + subprofile = ( + PG_SESSION.query(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) if subprofile is None: - raise GPDatabaseException( - f"no subprofile found with session key:{session_key}") + raise GPDatabaseException(f"no subprofile found with session key:{session_key}") subprofile.uniquenick = uniquenick PG_SESSION.commit() def update_profiles(session_key: str, extra_info: dict): - profile = PG_SESSION.query(Profiles).join(SubProfiles).where( - SubProfiles.session_key == session_key).first() + profile = ( + PG_SESSION.query(Profiles) + .join(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) if profile is None: - raise GPDatabaseException( - f"no profile found with session key:{session_key}") + raise GPDatabaseException(f"no profile found with session key:{session_key}") for key, value in extra_info.items(): profile.extra_info[key] = value diff --git a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py index 8625a5917..2e2513400 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py @@ -1,9 +1,38 @@ from backends.library.abstractions.contracts import RequestBase from backends.library.abstractions.handler_base import HandlerBase -from backends.protocols.gamespy.presence_connection_manager.requests import * import backends.protocols.gamespy.presence_connection_manager.data as data -from frontends.gamespy.protocols.presence_connection_manager.contracts.results import BlockListResult, BuddyListResult, GetProfileResult, LoginResult, StatusResult -from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import GPLoginBadEmailException +from backends.protocols.gamespy.presence_connection_manager.requests import ( + AddBlockRequest, + AddBuddyRequest, + BlockListRequest, + BuddyListRequest, + DelBuddyRequest, + GetProfileRequest, + InviteToRequest, + KeepAliveRequest, + LoginRequest, + LogoutRequest, + NewProfileRequest, + NewUserRequest, + RegisterCDKeyRequest, + RegisterNickRequest, + StatusInfoRequest, + StatusRequest, + UpdateProfileRequest, + UpdateUserInfoRequest, +) +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( + LoginType, +) +from frontends.gamespy.protocols.presence_connection_manager.contracts.results import ( + BlockListResult, + BuddyListResult, + GetProfileResult, + LoginResult, +) +from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import ( + GPLoginBadEmailException, +) # region General @@ -11,8 +40,7 @@ class KeepAliveHandler(HandlerBase): _request: KeepAliveRequest def _data_operate(self) -> None: - data.update_online_time(self._request.client_ip, - self._request.client_port) + data.update_online_time(self._request.client_ip, self._request.client_port) class LoginHandler(HandlerBase): @@ -33,22 +61,21 @@ def _nick_email_login(self) -> None: assert self._request.nick is not None is_exsit = data.is_email_exist(self._request.email) if not is_exsit: - raise GPLoginBadEmailException( - f"email: {self._request.email} is invalid.") + raise GPLoginBadEmailException(f"email: {self._request.email} is invalid.") self._data = data.get_user_infos_by_nick_email( - self._request.nick, self._request.email) + self._request.nick, self._request.email + ) def _unique_nick_login(self) -> None: assert self._request.unique_nick is not None assert self._request.namespace_id is not None self.data = data.get_user_infos_by_uniquenick_namespace_id( - self._request.unique_nick, - self._request.namespace_id) + self._request.unique_nick, self._request.namespace_id + ) def _auth_token_login(self) -> None: assert self._request.auth_token is not None - self._data = data.get_user_infos_by_authtoken( - self._request.auth_token) + self._data = data.get_user_infos_by_authtoken(self._request.auth_token) def _result_construct(self) -> None: assert self._data @@ -77,7 +104,8 @@ class BuddyListHandler(HandlerBase): def _data_operate(self) -> None: self.data = data.get_buddy_list( - self._request.profile_id, self._request.namespace_id) + self._request.profile_id, self._request.namespace_id + ) def _result_construct(self) -> None: self._result = BuddyListResult(profile_ids=self.data) @@ -88,7 +116,8 @@ class BlockListHandler(HandlerBase): def _data_operate(self) -> None: self.data = data.get_block_list( - self._request.profile_id, self._request.namespace_id) + self._request.profile_id, self._request.namespace_id + ) def _result_construct(self) -> None: self._result = BlockListResult(profile_ids=self.data) @@ -112,8 +141,7 @@ class DelBuddyHandler(HandlerBase): _request: DelBuddyRequest def _data_operate(self) -> None: - self.data = data.delete_friend_by_profile_id( - self._request.target_id) + self.data = data.delete_friend_by_profile_id(self._request.target_id) class AddBuddyHandler(HandlerBase): @@ -124,15 +152,17 @@ def _data_operate(self) -> None: self._request.profile_id, self._request.target_id, self._request.namespace_id, - self._request.reason) + self._request.reason, + ) class AddBlockHandler(HandlerBase): _request: AddBlockRequest def _data_operate(self) -> None: - data.update_block(self._request.profile_id, - self._request.taget_id, self._request.session_key) + data.update_block( + self._request.profile_id, self._request.taget_id, self._request.session_key + ) class InviteToHandler(HandlerBase): @@ -157,17 +187,13 @@ class StatusHandler(HandlerBase): _request: StatusRequest def _data_operate(self) -> None: - if self._request.is_get: - self.data = data.get_status( - self._request.session_key) - else: - data.update_status(self._request.session_key, self._request.status) - - def _result_construct(self) -> None: + data.update_status( + self._request.session_key, + self._request.current_status, + self._request.location_string, + self._request.status_string, + ) - if self._request.is_get: - assert isinstance(self.data, UserStatus) - self._result = StatusResult(status=self.data) class StatusInfoHandler(HandlerBase): @@ -185,7 +211,8 @@ class GetProfileHandler(HandlerBase): def _data_operate(self) -> None: self.data = data.get_profile_infos( - profile_id=self._request.profile_id, session_key=self._request.session_key) + profile_id=self._request.profile_id, session_key=self._request.session_key + ) def _result_construct(self) -> None: self._result = GetProfileResult(user_profile=self.data) @@ -195,11 +222,13 @@ class NewProfileHandler(HandlerBase): """ update a exist profile """ + _request: NewProfileRequest def _data_operate(self) -> None: data.update_new_nick( - self._request.session_key, self._request.old_nick, self._request.new_nick) + self._request.session_key, self._request.old_nick, self._request.new_nick + ) class RegisterCDKeyHandler(HandlerBase): @@ -213,11 +242,11 @@ class RegisterNickHandler(HandlerBase): """ some game will not register uniquenick when create a new account, it will update its uniquenick later """ + _request: RegisterNickRequest def _data_operate(self): - data.update_uniquenick(self._request.session_key, - self._request.unique_nick) + data.update_uniquenick(self._request.session_key, self._request.unique_nick) class RemoveBlockHandler(HandlerBase): @@ -229,13 +258,11 @@ class UpdateProfileHandler(HandlerBase): _request: UpdateProfileRequest def _data_operate(self): - data.update_profiles(self._request.session_key, - self._request.extra_infos) + data.update_profiles(self._request.session_key, self._request.extra_infos) class UpdateUserInfoHandler(HandlerBase): _request: UpdateUserInfoRequest def _data_operate(self): - data.update_profiles(self._request.session_key, - self._request.extra_infos) + data.update_profiles(self._request.session_key, self._request.extra_infos) diff --git a/src/backends/protocols/gamespy/presence_connection_manager/requests.py b/src/backends/protocols/gamespy/presence_connection_manager/requests.py index f89ef89a3..26b41fadb 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/requests.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/requests.py @@ -3,13 +3,12 @@ from pydantic import ValidationError from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( + GPStatusCode, LoginType, - PublicMasks, SdkRevisionType, ) from backends.library.abstractions.contracts import RequestBase -from frontends.gamespy.protocols.presence_connection_manager.aggregates.user_status import UserStatus, UserStatusInfo class ErrorOnParse(RequestBase): @@ -18,6 +17,7 @@ class ErrorOnParse(RequestBase): # region buddy + class AddBlockRequest(RequestBase): taget_id: int profile_id: int @@ -59,13 +59,25 @@ class StatusInfoRequest(RequestBase): is_get: bool profile_id: int namespace_id: int - status_info: UserStatusInfo + status_state: str + buddy_ip: str + host_ip: str + host_private_ip: str + query_report_port: int + host_port: int + session_flags: str + rich_status: str + game_type: str + game_variant: str + game_map_name: str + quiet_mode_flags: str class StatusRequest(RequestBase): session_key: str - status: UserStatus - is_get: bool + status_string: str + location_string: str + current_status: GPStatusCode # region general @@ -105,10 +117,8 @@ class LoginRequest(RequestBase): email: Optional[str] = None product_id: int type: Union[LoginType, int] - sdk_revision_type: Union[SdkRevisionType, int] + sdk_revision_type: list[SdkRevisionType] game_port: int - user_id: int - profile_id: int partner_id: int game_name: Optional[str] quiet_mode_flags: int diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index 7758fc837..61878610f 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -6,9 +6,20 @@ from pydantic import BaseModel import uvicorn +from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.library.log.log_manager import LogManager from frontends.gamespy.library.configs import ServerConfig -from backends.routers.gamespy import chat, gstats, natneg, presence_connection_manager, presence_search_player, query_report, server_browser, webservices +from backends.routers.gamespy import ( + chat, + gstats, + natneg, + presence_connection_manager, + presence_search_player, + query_report, + server_browser, + webservices, +) + app = FastAPI() # app.include_router(chat.router) @@ -23,15 +34,21 @@ logger = LogManager.create("backend") +@app.exception_handler(UniSpyException) +def unispy_exception_handler(_, exc: UniSpyException): + logger.error(exc.message) + return JSONResponse({"error": exc.message}) + + @app.exception_handler(RequestValidationError) -def validation_exception_handler(request, exc): - str_error = str(exc) +def validation_exception_handler(_, exc: RequestValidationError): + str_error = str(exc.args) logger.error(str_error) return JSONResponse({"error": str_error}) @app.exception_handler(Exception) -def handle_unispy_exception(request, exc): +def handle_unispy_exception(_, exc: Exception): str_error = str(exc) logger.error(str_error) return JSONResponse({"error": str_error}) @@ -50,8 +67,8 @@ class RegisterRequest(BaseModel): @app.post("/token") def get_auth_token(request: RegisterRequest): - pass + + if __name__ == "__main__": - uvicorn.run("backends.routers.home:app", - host="127.0.0.1", port=8080, reload=True) + uvicorn.run("backends.routers.home:app", host="127.0.0.1", port=8080, reload=True) diff --git a/src/backends/tests/gamespy/presence_conection_manager/handler_tests.py b/src/backends/tests/gamespy/presence_conection_manager/handler_tests.py index f88e017a1..71284dfbd 100644 --- a/src/backends/tests/gamespy/presence_conection_manager/handler_tests.py +++ b/src/backends/tests/gamespy/presence_conection_manager/handler_tests.py @@ -1,7 +1,7 @@ import unittest from backends.tests.utils import add_headers import frontends.gamespy.protocols.presence_connection_manager.contracts.requests as pcm -from frontends.tests.gamespy.presence_connection_manager.general_request_tests import ( +from frontends.tests.gamespy.presence_connection_manager.request_tests import ( LOGIN_AUTH_TOKEN, LOGIN_UNIQUE_NICK, LOGIN_USER, diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index 6d34a9654..4619147b6 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -37,9 +37,9 @@ def __init__(self): self.config = None def start(self): - self.__show_unispy_logo() self._connect_to_backend() self._create_logger() + self.__show_unispy_logo() self._launch_server() print("Server successfully launched.") self.__keep_running() diff --git a/src/frontends/gamespy/library/extentions/encoding.py b/src/frontends/gamespy/library/extentions/encoding.py index 88cdf31f2..559536463 100644 --- a/src/frontends/gamespy/library/extentions/encoding.py +++ b/src/frontends/gamespy/library/extentions/encoding.py @@ -2,6 +2,8 @@ import json from uuid import UUID +from pydantic import BaseModel + def get_string(data: bytes) -> str: return data.decode("ascii") @@ -23,5 +25,7 @@ def default(self, obj): return obj.value elif isinstance(obj, UUID): return str(obj) + elif isinstance(obj,BaseModel): + return obj.model_dump_json() # Fallback to the default method for other types return super().default(obj) diff --git a/src/frontends/gamespy/library/network/http_handler.py b/src/frontends/gamespy/library/network/http_handler.py index df3d497cf..d36495e29 100644 --- a/src/frontends/gamespy/library/network/http_handler.py +++ b/src/frontends/gamespy/library/network/http_handler.py @@ -1,8 +1,11 @@ -from urllib.parse import urlparse from frontends.gamespy.library.abstractions.client import ClientBase -from frontends.gamespy.library.abstractions.connections import ConnectionBase, NetworkServerBase +from frontends.gamespy.library.abstractions.connections import ( + ConnectionBase, + NetworkServerBase, +) from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer -from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.library.configs import CONFIG, ServerConfig +from frontends.gamespy.library.log.log_manager import LogWriter class HttpRequest: @@ -49,19 +52,23 @@ def do_POST(self) -> None: data = self.rfile.read(content_length).decode() # request = HttpRequest(parsed_url, dict(self.headers), data) if self.conn is None: - self.conn = HttpConnection( - self, *self.server.unispy_params) # type: ignore + self.conn = HttpConnection(self, *self.server.unispy_params) # type: ignore self.conn.on_received(data.encode()) class HttpServer(NetworkServerBase): - def start(self) -> None: + def __init__( + self, config: ServerConfig, t_client: type[ClientBase], logger: LogWriter + ) -> None: + super().__init__(config, t_client, logger) self._server = ThreadingHTTPServer( (self._config.public_address, self._config.listening_port), HttpHandler ) # fmt: off self._server.unispy_params = (self._config, self._client_cls, self._logger)# type: ignore # fmt: on + + def start(self) -> None: self._server.serve_forever() diff --git a/src/frontends/gamespy/library/network/tcp_handler.py b/src/frontends/gamespy/library/network/tcp_handler.py index a967cab1d..9ecfd8b99 100644 --- a/src/frontends/gamespy/library/network/tcp_handler.py +++ b/src/frontends/gamespy/library/network/tcp_handler.py @@ -4,8 +4,9 @@ from frontends.gamespy.library.abstractions.client import ClientBase from frontends.gamespy.library.abstractions.connections import ConnectionBase, NetworkServerBase +from frontends.gamespy.library.log.log_manager import LogWriter from frontends.gamespy.library.network import DATA_SIZE -from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.library.configs import CONFIG, ServerConfig class TcpConnection(ConnectionBase): @@ -47,11 +48,14 @@ def handle(self) -> None: class TcpServer(NetworkServerBase): - def start(self) -> None: + def __init__(self, config: ServerConfig, t_client: type[ClientBase], logger: LogWriter) -> None: + super().__init__(config, t_client, logger) self._server = socketserver.ThreadingTCPServer( (self._config.public_address, self._config.listening_port), TcpHandler, ) + def start(self) -> None: + self._server.allow_reuse_address = True self._server.unispy_params = (self._config, self._client_cls, self._logger) # type: ignore self._server.serve_forever() diff --git a/src/frontends/gamespy/library/network/udp_handler.py b/src/frontends/gamespy/library/network/udp_handler.py index d24854720..23fa9098e 100644 --- a/src/frontends/gamespy/library/network/udp_handler.py +++ b/src/frontends/gamespy/library/network/udp_handler.py @@ -2,8 +2,12 @@ import socketserver from frontends.gamespy.library.abstractions.client import ClientBase -from frontends.gamespy.library.abstractions.connections import ConnectionBase, NetworkServerBase -from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.library.abstractions.connections import ( + ConnectionBase, + NetworkServerBase, +) +from frontends.gamespy.library.configs import CONFIG, ServerConfig +from frontends.gamespy.library.log.log_manager import LogWriter class UdpConnection(ConnectionBase): @@ -27,7 +31,10 @@ def send(self, data: bytes) -> None: class UdpServer(NetworkServerBase): - def start(self) -> None: + def __init__( + self, config: ServerConfig, t_client: type[ClientBase], logger: LogWriter + ) -> None: + super().__init__(config, t_client, logger) self._server = socketserver.ThreadingUDPServer( (self._config.public_address, self._config.listening_port), UdpHandler, @@ -35,6 +42,7 @@ def start(self) -> None: # inject the handler params to ThreadingUDPServer self._server.unispy_params = (self._config, self._client_cls, self._logger) # type: ignore + def start(self) -> None: self._server.serve_forever() def __exit__(self, *args): diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py b/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py index 18a16e5be..fd2c7752d 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py @@ -8,12 +8,11 @@ import frontends.gamespy.library.abstractions.contracts as lib - def normalize_request(message: str): if "login" in message: message = message.replace("\\-", "\\") pos = message.index("\\", message.index("\\") + 1) - if message[pos: pos + 2] != "\\\\": + if message[pos : pos + 2] != "\\\\": message = message[:pos] + "\\" + message[pos:] return message @@ -36,9 +35,10 @@ def parse(self): if "id" in self.request_dict: try: self.operation_id = int(self.request_dict["id"]) - except: + except Exception: raise GPParseException("namespaceid is invalid.") - + + class ResultBase(lib.ResultBase): pass diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/aggregates/user_status.py b/src/frontends/gamespy/protocols/presence_connection_manager/aggregates/user_status.py index c9b175a41..e69de29bb 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/aggregates/user_status.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/aggregates/user_status.py @@ -1,24 +0,0 @@ -from pydantic import BaseModel - -from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import GPStatusCode - - -class UserStatus(BaseModel): - status_string: str - location_string: str - current_status: GPStatusCode = GPStatusCode.OFFLINE - - -class UserStatusInfo(BaseModel): - status_state: str - buddy_ip: str - host_ip: str - host_private_ip: str - query_report_port: int - host_port: int - session_flags: str - rich_status: str - game_type: str - game_variant: str - game_map_name: str - quiet_mode_flags: str diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/applications/client.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/client.py index e501fba7c..cc6dbd4ba 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/applications/client.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/client.py @@ -7,9 +7,7 @@ from frontends.gamespy.protocols.presence_connection_manager.aggregates.login_challenge import ( SERVER_CHALLENGE, ) -from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import LoginStatus -from frontends.gamespy.protocols.presence_connection_manager.aggregates.sdk_revision import SdkRevision -from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import LoginStatus +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import LoginStatus, SdkRevisionType LOGIN_TICKET = "0000000000000000000000__" SESSION_KEY = 1111 @@ -21,7 +19,7 @@ class ClientInfo(ClientInfoBase): sub_profile_id: int login_status: LoginStatus namespace_id: int - sdk_revision: SdkRevision + sdk_revision: list[SdkRevisionType] def __init__(self) -> None: super().__init__() diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py index 75d1a26bf..fc52649af 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py @@ -1,5 +1,8 @@ from typing import final +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( + SdkRevisionType, +) from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import ( AddBlockRequest, GetProfileRequest, @@ -47,11 +50,7 @@ from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import ( RequestBase, ) -from multiprocessing.pool import Pool from typing import TYPE_CHECKING -from frontends.gamespy.protocols.presence_connection_manager.aggregates.sdk_revision import ( - SdkRevision, -) if TYPE_CHECKING: @@ -129,10 +128,16 @@ def _data_operate(self): pass def _response_construct(self) -> None: - self._client.info.sdk_revision = SdkRevision(self._request.sdk_revision_type) - if self._client.info.sdk_revision.is_support_gpi_new_status_notification: - BuddyListHandler(self._client).handle() - BlockListHandler(self._client).handle() + self._client.info.sdk_revision = self._request.sdk_revision_type + for operation in self._client.info.sdk_revision: + if operation == SdkRevisionType.GPINEW_LIST_RETRIEVAL_ON_LOGIN: + BuddyListHandler(self._client).handle() + BlockListHandler(self._client).handle() + request = StatusInfoRequest() + request.profile_id = self._client.info.profile_id + request.namespace_id = int(self._client.info.namespace_id) + StatusInfoHandler(self._client, request).handle() + # todo: add other revision operations # region Buddy @@ -166,23 +171,6 @@ def __init__(self, client): def response_construct(self): self._response = BuddyListResponse(self._request, self._result) - def handle_status_info(self, profile_id): - request = StatusInfoRequest() - request.profile_id = profile_id - request.namespace_id = int(self._client.info.namespace_id) - # request.is_get_status_info = True - - StatusInfoHandler(self._client, request).handle() - - def _response_send(self) -> None: - super()._response_send() - - if not self._client.info.sdk_revision.is_support_gpi_new_status_notification: - return - - with Pool() as pool: - pool.map(self.handle_status_info, self._result.profile_ids) - class BuddyStatusInfoHandler(CmdHandlerBase): """ @@ -220,10 +208,7 @@ class StatusHandler(CmdHandlerBase): def __init__(self, client: Client, request: StatusRequest) -> None: assert isinstance(request, StatusRequest) super().__init__(client, request) - - def _response_send(self) -> None: - # TODO check if statushandler need send response - raise NotImplementedError() + self._is_fetching = False class StatusInfoHandler(LoginedHandlerBase): @@ -234,8 +219,10 @@ class StatusInfoHandler(LoginedHandlerBase): def __init__(self, client: Client, request: StatusInfoRequest) -> None: assert isinstance(request, StatusInfoRequest) super().__init__(client, request) + self._is_fetching = False def _response_send(self) -> None: + # todo: check if response is needed if self._request is not None: self._response = StatusInfoResponse(self._request, self._result) super()._response_send() diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py index 29cd97edd..4166cb70e 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py @@ -1,24 +1,22 @@ -from pydantic import BaseModel from frontends.gamespy.library.exceptions.general import UniSpyException -from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import PublicMasks from typing import Optional, final from frontends.gamespy.library.extentions.gamespy_utils import convert_to_key_value -from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import RequestBase -from frontends.gamespy.protocols.presence_connection_manager.aggregates.user_status import UserStatus, UserStatusInfo -from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import GPStatusCode -from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import GPParseException -from typing import final +from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import ( + RequestBase, +) +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( + GPStatusCode, +) +from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import ( + GPParseException, +) from frontends.gamespy.library.extentions.gamespy_utils import is_email_format_correct from frontends.gamespy.library.extentions.password_encoder import process_password -from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import RequestBase from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( LoginType, QuietModeType, SdkRevisionType, ) -from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import ( - GPParseException, -) # region General EXTRA_INFO_DICT: dict[str, type] = { @@ -58,7 +56,7 @@ "videocard2string": str, "videocard2ram": int, "subscription": int, - "adminrights": int + "adminrights": int, } @@ -91,6 +89,7 @@ class KeepAliveRequest(RequestBase): @final class LoginRequest(RequestBase): + sdk_mapping: list[SdkRevisionType] user_challenge: str response: str unique_nick: str @@ -101,7 +100,7 @@ class LoginRequest(RequestBase): email: str product_id: int type: LoginType - sdk_revision_type: SdkRevisionType + sdk_revision_type: list[SdkRevisionType] game_port: int user_id: int profile_id: int @@ -113,6 +112,18 @@ class LoginRequest(RequestBase): quiet_mode_flags: int firewall: bool + def __init__(self, raw_request: str) -> None: + super().__init__(raw_request) + self.sdk_revision_type = [] + self.sdk_mapping = [ + SdkRevisionType.GPINEW_AUTH_NOTIFICATION, + SdkRevisionType.GPINEW_REVOKE_NOTIFICATION, + SdkRevisionType.GPINEW_STATUS_NOTIFICATION, + SdkRevisionType.GPINEW_LIST_RETRIEVAL_ON_LOGIN, + SdkRevisionType.GPIREMOTE_AUTH_IDS_NOTIFICATION, + SdkRevisionType.GPINEW_CD_KEY_REGISTRATION, + ] + def parse(self): super().parse() @@ -125,10 +136,7 @@ def parse(self): self.user_challenge = self.request_dict["challenge"] self.response = self.request_dict["response"] - if ( - "uniquenick" in self.request_dict - and "namespaceid" in self.request_dict - ): + if "uniquenick" in self.request_dict and "namespaceid" in self.request_dict: namespace_id = int(self.request_dict["namespaceid"]) self.type = LoginType.UNIQUENICK_NAMESPACE_ID self.unique_nick = self.request_dict["uniquenick"] @@ -145,7 +153,7 @@ def parse(self): if pos == -1 or pos < 1 or (pos + 1) >= len(self.user_data): raise GPParseException("user format is incorrect") self.nick = self.user_data[:pos] - self.email = self.user_data[pos + 1:] + self.email = self.user_data[pos + 1 :] if "namespaceid" in self.request_dict: namespace_id = int(self.request_dict["namespaceid"]) self.namespace_id = namespace_id @@ -171,7 +179,9 @@ def parse_other_data(self): if "sdkrevision" in self.request_dict: sdk_revision_type = int(self.request_dict["sdkrevision"]) - self.sdk_revision_type = SdkRevisionType(sdk_revision_type) + for item in self.sdk_mapping: + if item & sdk_revision_type: + self.sdk_revision_type.append(item) if "gamename" in self.request_dict: self.game_name = self.request_dict["gamename"] @@ -269,6 +279,7 @@ def parse_other_info(self): self.has_cd_key_enc_flag = True self.cd_key = self.request_dict["cdkey"] + # region Buddy @@ -276,9 +287,7 @@ class BuddyListRequest(RequestBase): profile_id: int namespace_id: int - def __init__(self, - profile_id: int, - namespace_id: int) -> None: + def __init__(self, profile_id: int, namespace_id: int) -> None: assert isinstance(profile_id, int) assert isinstance(namespace_id, int) self.profile_id = profile_id @@ -292,9 +301,7 @@ class BlockListRequest(RequestBase): profile_id: int namespace_id: int - def __init__(self, - profile_id: int, - namespace_id: int) -> None: + def __init__(self, profile_id: int, namespace_id: int) -> None: assert isinstance(profile_id, int) assert isinstance(namespace_id, int) self.profile_id = profile_id @@ -319,7 +326,7 @@ def parse(self): raise GPParseException("addbuddy request is invalid.") try: self.friend_profile_id = int(self.request_dict["newprofileid"]) - except: + except Exception: raise GPParseException("newprofileid format is incorrect.") self.reason = self.request_dict["reason"] @@ -336,7 +343,7 @@ def parse(self): try: self.friend_profile_id = int(self.request_dict["delprofileid"]) - except: + except Exception: raise GPParseException("delprofileid format is incorrect.") @@ -371,8 +378,19 @@ def parse(self): @final class StatusInfoRequest(RequestBase): namespace_id: int - status_info: UserStatusInfo profile_id: int + status_state: str + buddy_ip: str + host_ip: str + host_private_ip: str + query_report_port: int + host_port: int + session_flags: str + rich_status: str + game_type: str + game_variant: str + game_map_name: str + quiet_mode_flags: str def __init__(self, raw_request: Optional[str] = None) -> None: if raw_request is not None: @@ -395,34 +413,34 @@ def parse(self): ): raise GPParseException("StatusInfo request is invalid.") - self.status_info.status_state = self.request_dict["state"] - self.status_info.host_ip = self.request_dict["hostip"] - self.status_info.host_private_ip = self.request_dict["hprivip"] + self.status_state = self.request_dict["state"] + self.host_ip = self.request_dict["hostip"] + self.host_private_ip = self.request_dict["hprivip"] try: - self.status_info.query_report_port = int( - self.request_dict["qport"]) - self.status_info.host_port = int(self.request_dict["hport"]) - self.status_info.session_flags = self.request_dict["sessflags"] + self.query_report_port = int(self.request_dict["qport"]) + self.host_port = int(self.request_dict["hport"]) + self.session_flags = self.request_dict["sessflags"] except ValueError: - raise GPParseException( - "qport, hport, or sessflags format is incorrect.") + raise GPParseException("qport, hport, or sessflags format is incorrect.") if "namespace_id" in self.request_dict: self.namespace_id = int(self.request_dict["namespaceid"]) else: self.namespace_id = 0 - self.status_info.rich_status = self.request_dict["rechstatus"] - self.status_info.game_type = self.request_dict["gametype"] - self.status_info.game_variant = self.request_dict["gamevariant"] - self.status_info.game_map_name = self.request_dict["gamemapname"] + self.rich_status = self.request_dict["rechstatus"] + self.game_type = self.request_dict["gametype"] + self.game_variant = self.request_dict["gamevariant"] + self.game_map_name = self.request_dict["gamemapname"] @final class StatusRequest(RequestBase): - status: UserStatus - is_get: bool + current_status: GPStatusCode + location_string: str + status_string: str + session_key: str def parse(self): self.request_dict = convert_to_key_value(self.raw_request) @@ -436,17 +454,21 @@ def parse(self): if "locstring" not in self.request_dict: raise GPParseException("locstring is missing.") - + if "sesskey" not in self.request_dict: + raise GPParseException("session key is missing.") + self.session_key = self.request_dict["sesskey"] + self.location_string = self.request_dict["locstring"] + self.status_string = self.request_dict["statstring"] try: status_code = int(self.request_dict["status"]) - self.status = UserStatus( - location_string=self.request_dict["locstring"], status_string=self.request_dict['statstring'], current_status=GPStatusCode(status_code)) + self.current_status = GPStatusCode(status_code) except ValueError: raise GPParseException("status format is incorrect.") # region Profile + @final class AddBlockRequest(RequestBase): taget_id: int @@ -501,10 +523,7 @@ def parse(self): self.session_key = self.request_dict["sesskey"] if "replace" in self.request_dict: - if ( - "oldnick" not in self.request_dict - and "nick" not in self.request_dict - ): + if "oldnick" not in self.request_dict and "nick" not in self.request_dict: raise GPParseException("oldnick or nick is missing.") if "oldnick" in self.request_dict: diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/responses.py b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/responses.py index d113f0d46..05e0875da 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/responses.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/responses.py @@ -1,5 +1,11 @@ -from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import ResponseBase -from frontends.gamespy.protocols.presence_connection_manager.aggregates.login_challenge import SERVER_CHALLENGE, LoginChallengeProof +from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import ( + RequestBase, + ResponseBase, +) +from frontends.gamespy.protocols.presence_connection_manager.aggregates.login_challenge import ( + SERVER_CHALLENGE, + LoginChallengeProof, +) from frontends.gamespy.protocols.presence_connection_manager.applications.client import ( LOGIN_TICKET, SESSION_KEY, @@ -8,12 +14,30 @@ KeepAliveRequest, LoginRequest, NewUserRequest, + AddBuddyRequest, + StatusInfoRequest, + GetProfileRequest, + NewProfileRequest, + RegisterNickRequest, +) + +from frontends.gamespy.library.extentions.gamespy_ramdoms import ( + StringType, + generate_random_string, ) + from frontends.gamespy.protocols.presence_connection_manager.contracts.results import ( + GetProfileResult, + NewProfileResult, + AddBuddyResult, + BlockListResult, + BuddyListResult, + StatusInfoResult, LoginResult, NewUserResult, ) + # region General @@ -35,15 +59,19 @@ def __init__(self, request: LoginRequest, result: LoginResult): assert isinstance(result, LoginResult) def build(self): - - response_proof = LoginChallengeProof(self._request.user_data, - self._request.type, - self._request.partner_id, - self._request.user_challenge, - SERVER_CHALLENGE, - self._result.data.password_hash).generate_proof() - self.sending_buffer = f"\\lc\\2\\sesskey\\{SESSION_KEY}\\proof\\{response_proof}\\userid\\{ - self._result.data.user_id}\\profileid\\{self._result.data.profile_id}" + response_proof = LoginChallengeProof( + self._request.user_data, + self._request.type, + self._request.partner_id, + self._request.user_challenge, + SERVER_CHALLENGE, + self._result.data.password_hash, + ).generate_proof() + self.sending_buffer = f"\\lc\\2\\sesskey\\{SESSION_KEY}\\proof\\{ + response_proof + }\\userid\\{self._result.data.user_id}\\profileid\\{ + self._result.data.profile_id + }" if self._result.data.unique_nick is not None: self.sending_buffer += "\\uniquenick\\" + self._result.data.unique_nick @@ -63,22 +91,11 @@ def __init__(self, request: NewUserRequest, result: NewUserResult) -> None: def build(self): # fmt: on - self.sending_buffer = f"\\nur\\userid\\{self._result.user_id}\\profileid\\{self._result.profile_id}\\id\\{self._request.operation_id}\\final\\" # fmt: off + self.sending_buffer = f"\\nur\\userid\\{self._result.user_id}\\profileid\\{self._result.profile_id}\\id\\{self._request.operation_id}\\final\\" # fmt: off # region Buddy -from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import ( - RequestBase, - ResponseBase, -) -from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import AddBuddyRequest, StatusInfoRequest -from frontends.gamespy.protocols.presence_connection_manager.contracts.results import ( - AddBuddyResult, - BlockListResult, - BuddyListResult, - StatusInfoResult, -) class AddBuddyResponse(ResponseBase): def __init__(self, request: AddBuddyRequest, result: AddBuddyResult) -> None: @@ -138,28 +155,19 @@ def __init__(self, request: StatusInfoRequest, result: StatusInfoResult): def build(self): # \bsi\\state\\profile\\bip\\bport\\hostip\\hprivip\\qport\\hport\\sessflags\\rstatus\\gameType\\gameVnt\\gameMn\\product\\qmodeflags\ self.sending_buffer = ( - f"\\bsi\\state\\{self._result.status_info.status_state}\\" - f"profile\\{self._result.profile_id}\\bip\\{self._result.status_info.buddy_ip}\\" - f"hostIp\\{self._result.status_info.host_ip}\\hprivIp\\{self._result.status_info.host_private_ip}\\" - f"qport\\{self._result.status_info.query_report_port}\\hport\\{self._result.status_info.host_port}\\" - f"sessflags\\{self._result.status_info.session_flags}\\rstatus\\{self._result.status_info.rich_status}\\" - f"gameType\\{self._result.status_info.game_type}\\gameVnt\\{self._result.status_info.game_variant}\\" - f"gameMn\\{self._result.status_info.game_map_name}\\product\\{self._result.product_id}\\" - f"qmodeflags\\{self._result.status_info.quiet_mode_flags}\\final\\" + f"\\bsi\\state\\{self._result.status_state}\\" + f"profile\\{self._result.profile_id}\\bip\\{self._result.buddy_ip}\\" + f"hostIp\\{self._result.host_ip}\\hprivIp\\{self._result.host_private_ip}\\" + f"qport\\{self._result.query_report_port}\\hport\\{self._result.host_port}\\" + f"sessflags\\{self._result.session_flags}\\rstatus\\{self._result.rich_status}\\" + f"gameType\\{self._result.game_type}\\gameVnt\\{self._result.game_variant}\\" + f"gameMn\\{self._result.game_map_name}\\product\\{self._result.product_id}\\" + f"qmodeflags\\{self._result.quiet_mode_flags}\\final\\" ) + # region Profile -from frontends.gamespy.library.extentions.gamespy_ramdoms import StringType, generate_random_string -from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import ResponseBase -from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import ( - GetProfileRequest, - NewProfileRequest, - RegisterNickRequest, -) -from frontends.gamespy.protocols.presence_connection_manager.contracts.results import ( - GetProfileResult, - NewProfileResult, -) + class GetProfileResponse(ResponseBase): _result: GetProfileResult @@ -177,7 +185,7 @@ def build(self): + f"\\uniquenick\\{self._result.user_profile.unique_nick}" + f"\\email\\{self._result.user_profile.email}" ) - for key,value in self._result.user_profile.extra_infos.items(): + for key, value in self._result.user_profile.extra_infos.items(): self.sending_buffer += f"\\{key}\\{value}" self.sending_buffer += ( diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/results.py b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/results.py index 3ba40de53..f5ca919a5 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/results.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/results.py @@ -1,9 +1,6 @@ -from frontends.gamespy.protocols.presence_connection_manager.aggregates.user_status import ( - UserStatusInfo, -) -from frontends.gamespy.protocols.presence_connection_manager.aggregates.user_status import UserStatus from pydantic import BaseModel from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import ResultBase +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import GPStatusCode # region General @@ -48,12 +45,25 @@ class BuddyListResult(ResultBase): class StatusInfoResult(ResultBase): profile_id: int product_id: int - status_info: UserStatusInfo + status_state: str + buddy_ip: str + host_ip: str + host_private_ip: str + query_report_port: int + host_port: int + session_flags: str + rich_status: str + game_type: str + game_variant: str + game_map_name: str + quiet_mode_flags: str -class StatusResult(ResultBase): - status: UserStatus +class StatusResult(ResultBase): + status_string: str + location_string: str + current_status: GPStatusCode # class NewUserResult() diff --git a/src/frontends/tests/gamespy/presence_connection_manager/buddy_request_tests.py b/src/frontends/tests/gamespy/presence_connection_manager/buddy_request_tests.py deleted file mode 100644 index c327e580c..000000000 --- a/src/frontends/tests/gamespy/presence_connection_manager/buddy_request_tests.py +++ /dev/null @@ -1,42 +0,0 @@ -import unittest - -from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import ( - AddBuddyRequest, - DelBuddyRequest, - InviteToRequest, - StatusRequest, -) -ADD_BUDDY = "\\addbuddy\\\\sesskey\\0\\newprofileid\\0\\reason\\test\\final\\" -DEL_BUDDY = "\\delbuddy\\\\sesskey\\0\\delprofileid\\0\\final\\" -INVITE_TO = "\\inviteto\\\\sesskey\\0\\productid\\0\\profileid\\0\\final\\" -STATUS = "\\status\\0\\statstring\\test\\locstring\\test\\final\\" - -class BuddyRequestTest(unittest.TestCase): - - - def test_add_buddy(self) -> None: - request = AddBuddyRequest(ADD_BUDDY) - request.parse() - self.assertEqual(0, request.friend_profile_id) - self.assertEqual("test", request.reason) - - def test_del_buddy(self) -> None: - request = DelBuddyRequest(DEL_BUDDY) - request.parse() - self.assertEqual(0, request.friend_profile_id) - - def test_invite_to(self) -> None: - request = InviteToRequest(INVITE_TO) - request.parse() - self.assertEqual(0, request.product_id) - self.assertEqual(0, request.profile_id) - - def test_status_test(self) -> None: - request = StatusRequest(STATUS) - request.parse() - self.assertEqual("test", request.status.status_string) - self.assertEqual("test", request.status.location_string) - - -if __name__ == "__main__": - unittest.main() diff --git a/src/frontends/tests/gamespy/presence_connection_manager/game_tests.py b/src/frontends/tests/gamespy/presence_connection_manager/game_tests.py index 82ab361f8..269003fba 100644 --- a/src/frontends/tests/gamespy/presence_connection_manager/game_tests.py +++ b/src/frontends/tests/gamespy/presence_connection_manager/game_tests.py @@ -36,8 +36,8 @@ def test_sbwfrontps2(self) -> None: raw = "\\status\\1\\sesskey\\1111\\statstring\\EN LIGNE\\locstring\\\\final\\" request = StatusRequest(raw) request.parse() - self.assertTrue(request.status.location_string == "") - self.assertTrue(request.status.status_string == "EN LIGNE") + self.assertTrue(request.location_string == "") + self.assertTrue(request.status_string == "EN LIGNE") if __name__ == "__main__": diff --git a/src/frontends/tests/gamespy/presence_connection_manager/general_request_tests.py b/src/frontends/tests/gamespy/presence_connection_manager/general_request_tests.py deleted file mode 100644 index 6ff6aa8e7..000000000 --- a/src/frontends/tests/gamespy/presence_connection_manager/general_request_tests.py +++ /dev/null @@ -1,74 +0,0 @@ -import unittest -from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import LoginRequest -from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( - LoginType, - QuietModeType, - SdkRevisionType, -) -LOGIN_AUTH_TOKEN = "\\login\\\\challenge\\xxxx\\authtoken\\xxxx\\userid\\0\\profileid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" -LOGIN_UNIQUE_NICK = "\\login\\\\challenge\\xxxx\\uniquenick\\spyguy\\userid\\0\\profileid\\0\\namespaceid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" -LOGIN_USER = "\\login\\\\challenge\\xxxx\\user\\spyguy@spyguy@gamespy.com\\userid\\0\\profileid\\0\\partnerid\\0\\namespaceid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" - - -class GeneralRequestTest(unittest.TestCase): - - def test_login_auth_token(self) -> None: - request = LoginRequest(LOGIN_AUTH_TOKEN) - request.parse() - self.assertEqual(LoginType.AUTH_TOKEN, request.type) - self.assertEqual("xxxx", request.user_challenge) - self.assertEqual("xxxx", request.auth_token) - self.assertEqual(0, request.user_id) - self.assertEqual(0, request.profile_id) - self.assertEqual(0, request.partner_id) - self.assertEqual("xxxxx", request.response) - self.assertEqual(True, request.firewall) - self.assertEqual(request.game_port, 0) - self.assertEqual(request.product_id, 0) - self.assertEqual("gmtest", request.game_name) - self.assertEqual( - SdkRevisionType.GPINEW_REVOKE_NOTIFICATION, request.sdk_revision_type) - self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) - - def test_login_unique_nick(self) -> None: - request = LoginRequest(LOGIN_UNIQUE_NICK) - request.parse() - self.assertEqual(LoginType.UNIQUENICK_NAMESPACE_ID, request.type) - self.assertEqual("xxxx", request.user_challenge) - self.assertEqual("spyguy", request.unique_nick) - self.assertEqual(0, request.namespace_id) - self.assertEqual(0, request.user_id) - self.assertEqual(0, request.profile_id) - self.assertEqual(0, request.partner_id) - self.assertEqual("xxxxx", request.response) - self.assertEqual(True, request.firewall) - self.assertEqual(0, request.game_port) - self.assertEqual(0, request.product_id) - self.assertEqual("gmtest", request.game_name) - self.assertEqual( - SdkRevisionType.GPINEW_REVOKE_NOTIFICATION, request.sdk_revision_type) - self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) - - def test_login_user(self) -> None: - request = LoginRequest(LOGIN_USER) - request.parse() - self.assertEqual(LoginType.NICK_EMAIL, request.type) - self.assertEqual("xxxx", request.user_challenge) - self.assertEqual("spyguy", request.nick) - self.assertEqual("spyguy@gamespy.com", request.email) - self.assertEqual(0, request.namespace_id) - self.assertEqual(0, request.user_id) - self.assertEqual(0, request.profile_id) - self.assertEqual(0, request.partner_id) - self.assertEqual("xxxxx", request.response) - self.assertEqual(True, request.firewall) - self.assertEqual(0, request.game_port) - self.assertEqual(0, request.product_id) - self.assertEqual("gmtest", request.game_name) - self.assertEqual( - SdkRevisionType.GPINEW_REVOKE_NOTIFICATION, request.sdk_revision_type) - self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) - - -if __name__ == "__main__": - unittest.main() diff --git a/src/frontends/tests/gamespy/presence_connection_manager/handler_tests.py b/src/frontends/tests/gamespy/presence_connection_manager/handler_tests.py new file mode 100644 index 000000000..f778efe0e --- /dev/null +++ b/src/frontends/tests/gamespy/presence_connection_manager/handler_tests.py @@ -0,0 +1,5 @@ +import unittest + +class HandlerTests(unittest.TestCase): + def test_status(self): + pass \ No newline at end of file diff --git a/src/frontends/tests/gamespy/presence_connection_manager/profile_request_tests.py b/src/frontends/tests/gamespy/presence_connection_manager/profile_request_tests.py deleted file mode 100644 index 14e22f2ef..000000000 --- a/src/frontends/tests/gamespy/presence_connection_manager/profile_request_tests.py +++ /dev/null @@ -1,65 +0,0 @@ -import unittest - -from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import ( - AddBlockRequest, - GetProfileRequest, - NewProfileRequest, - RegisterCDKeyRequest, - RegisterNickRequest, - UpdateProfileRequest, -) - - -class ProfileRequestTest(unittest.TestCase): - ADD_BLOCK = "\\addblock\\\\profileid\\0\\final\\" - GET_PROFILE = "\\getprofile\\\\sesskey\\xxxx\\profileid\\0\\final\\" - NEW_PROFILE = "\\newprofile\\\\sesskey\\xxxx\\nick\\spyguy\\id\\1\\final\\" - NEW_PROFILE_REPLACE = "\\newprofile\\\\sesskey\\xxxx\\nick\\spyguy2\\replace\\1\\oldnick\\spyguy\\id\\1\\final\\" - REGISTER_CD_KEY = "\\registercdkey\\\\sesskey\\xxxx\\cdkeyenc\\xxxx\\id\\1\\final\\" - REGISTER_NICK = "\\registernick\\\\sesskey\\xxxx\\uniquenick\\spyguy\\partnerid\\0\\id\\1\\final\\" - - def test_add_block(self) -> None: - request = AddBlockRequest(ProfileRequestTest.ADD_BLOCK) - request.parse() - self.assertEqual(0, request.taget_id) - - def test_get_profile(self) -> None: - request = GetProfileRequest(ProfileRequestTest.GET_PROFILE) - request.parse() - self.assertEqual("xxxx", request.session_key) - self.assertEqual(0, request.profile_id) - - def test_new_profile(self) -> None: - request = NewProfileRequest(ProfileRequestTest.NEW_PROFILE) - request.parse() - self.assertEqual("xxxx", request.session_key) - self.assertEqual("spyguy", request.new_nick) - - def test_new_profile_replace(self) -> None: - request = NewProfileRequest(ProfileRequestTest.NEW_PROFILE_REPLACE) - request.parse() - self.assertEqual("xxxx", request.session_key) - self.assertEqual("spyguy2", request.new_nick) - self.assertEqual("spyguy", request.old_nick) - - def test_register_cd_key(self) -> None: - request = RegisterCDKeyRequest(ProfileRequestTest.REGISTER_CD_KEY) - request.parse() - self.assertEqual("xxxx", request.session_key) - self.assertEqual("xxxx", request.cdkey_enc) - - def test_register_nick(self) -> None: - request = RegisterNickRequest(ProfileRequestTest.REGISTER_NICK) - request.parse() - self.assertEqual("xxxx", request.session_key) - self.assertEqual("spyguy", request.unique_nick) - self.assertEqual(0, request.partner_id) - - def test_update_profile(self) -> None: - crysisWarsRaw = "\\updatepro\\\\sesskey\\1111\\countrycode\\DE\\birthday\\168232912\\partnerid\\0\\final\\" - request = UpdateProfileRequest(crysisWarsRaw) - request.parse() - - -if __name__ == "__main__": - unittest.main() diff --git a/src/frontends/tests/gamespy/presence_connection_manager/request_tests.py b/src/frontends/tests/gamespy/presence_connection_manager/request_tests.py new file mode 100644 index 000000000..d84e80eb8 --- /dev/null +++ b/src/frontends/tests/gamespy/presence_connection_manager/request_tests.py @@ -0,0 +1,174 @@ +import unittest +from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import ( + AddBuddyRequest, + DelBuddyRequest, + InviteToRequest, + StatusRequest, + LoginRequest, + AddBlockRequest, + GetProfileRequest, + NewProfileRequest, + RegisterCDKeyRequest, + RegisterNickRequest, + UpdateProfileRequest, +) + +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( + LoginType, + QuietModeType, + SdkRevisionType, +) + +ADD_BLOCK = "\\addblock\\\\profileid\\0\\final\\" +GET_PROFILE = "\\getprofile\\\\sesskey\\xxxx\\profileid\\0\\final\\" +NEW_PROFILE = "\\newprofile\\\\sesskey\\xxxx\\nick\\spyguy\\id\\1\\final\\" +NEW_PROFILE_REPLACE = "\\newprofile\\\\sesskey\\xxxx\\nick\\spyguy2\\replace\\1\\oldnick\\spyguy\\id\\1\\final\\" +REGISTER_CD_KEY = "\\registercdkey\\\\sesskey\\xxxx\\cdkeyenc\\xxxx\\id\\1\\final\\" +REGISTER_NICK = ( + "\\registernick\\\\sesskey\\xxxx\\uniquenick\\spyguy\\partnerid\\0\\id\\1\\final\\" +) + + +LOGIN_AUTH_TOKEN = "\\login\\\\challenge\\xxxx\\authtoken\\xxxx\\userid\\0\\profileid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" +LOGIN_UNIQUE_NICK = "\\login\\\\challenge\\xxxx\\uniquenick\\spyguy\\userid\\0\\profileid\\0\\namespaceid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" +LOGIN_USER = "\\login\\\\challenge\\xxxx\\user\\spyguy@spyguy@gamespy.com\\userid\\0\\profileid\\0\\partnerid\\0\\namespaceid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" + +ADD_BUDDY = "\\addbuddy\\\\sesskey\\0\\newprofileid\\0\\reason\\test\\final\\" +DEL_BUDDY = "\\delbuddy\\\\sesskey\\0\\delprofileid\\0\\final\\" +INVITE_TO = "\\inviteto\\\\sesskey\\0\\productid\\0\\profileid\\0\\final\\" +STATUS = [ + "\\status\\0\\statstring\\test\\locstring\\test\\final\\", + "\\status\\1\\sesskey\\1111\\statstring\\Not Ready\\locstring\\gptestc\\final\\", +] + + +class RequestTests(unittest.TestCase): + # region General + def test_login_auth_token(self) -> None: + request = LoginRequest(LOGIN_AUTH_TOKEN) + request.parse() + self.assertEqual(LoginType.AUTH_TOKEN, request.type) + self.assertEqual("xxxx", request.user_challenge) + self.assertEqual("xxxx", request.auth_token) + self.assertEqual(0, request.user_id) + self.assertEqual(0, request.profile_id) + self.assertEqual(0, request.partner_id) + self.assertEqual("xxxxx", request.response) + self.assertEqual(True, request.firewall) + self.assertEqual(request.game_port, 0) + self.assertEqual(request.product_id, 0) + self.assertEqual("gmtest", request.game_name) + self.assertEqual( + SdkRevisionType.GPINEW_REVOKE_NOTIFICATION, request.sdk_revision_type[0] + ) + self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) + + def test_login_unique_nick(self) -> None: + request = LoginRequest(LOGIN_UNIQUE_NICK) + request.parse() + self.assertEqual(LoginType.UNIQUENICK_NAMESPACE_ID, request.type) + self.assertEqual("xxxx", request.user_challenge) + self.assertEqual("spyguy", request.unique_nick) + self.assertEqual(0, request.namespace_id) + self.assertEqual(0, request.user_id) + self.assertEqual(0, request.profile_id) + self.assertEqual(0, request.partner_id) + self.assertEqual("xxxxx", request.response) + self.assertEqual(True, request.firewall) + self.assertEqual(0, request.game_port) + self.assertEqual(0, request.product_id) + self.assertEqual("gmtest", request.game_name) + self.assertEqual( + SdkRevisionType.GPINEW_REVOKE_NOTIFICATION, request.sdk_revision_type[0] + ) + self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) + + def test_login_user(self) -> None: + request = LoginRequest(LOGIN_USER) + request.parse() + self.assertEqual(LoginType.NICK_EMAIL, request.type) + self.assertEqual("xxxx", request.user_challenge) + self.assertEqual("spyguy", request.nick) + self.assertEqual("spyguy@gamespy.com", request.email) + self.assertEqual(0, request.namespace_id) + self.assertEqual(0, request.user_id) + self.assertEqual(0, request.profile_id) + self.assertEqual(0, request.partner_id) + self.assertEqual("xxxxx", request.response) + self.assertEqual(True, request.firewall) + self.assertEqual(0, request.game_port) + self.assertEqual(0, request.product_id) + self.assertEqual("gmtest", request.game_name) + self.assertEqual( + SdkRevisionType.GPINEW_REVOKE_NOTIFICATION, request.sdk_revision_type[0] + ) + self.assertEqual(QuietModeType.SILENCE_NONE, request.quiet_mode_flags) + # region Profile + + def test_add_block(self) -> None: + request = AddBlockRequest(ADD_BLOCK) + request.parse() + self.assertEqual(0, request.taget_id) + + def test_get_profile(self) -> None: + request = GetProfileRequest(GET_PROFILE) + request.parse() + self.assertEqual("xxxx", request.session_key) + self.assertEqual(0, request.profile_id) + + def test_new_profile(self) -> None: + request = NewProfileRequest(NEW_PROFILE) + request.parse() + self.assertEqual("xxxx", request.session_key) + self.assertEqual("spyguy", request.new_nick) + + def test_new_profile_replace(self) -> None: + request = NewProfileRequest(NEW_PROFILE_REPLACE) + request.parse() + self.assertEqual("xxxx", request.session_key) + self.assertEqual("spyguy2", request.new_nick) + self.assertEqual("spyguy", request.old_nick) + + def test_register_cd_key(self) -> None: + request = RegisterCDKeyRequest(REGISTER_CD_KEY) + request.parse() + self.assertEqual("xxxx", request.session_key) + self.assertEqual("xxxx", request.cdkey_enc) + + def test_register_nick(self) -> None: + request = RegisterNickRequest(REGISTER_NICK) + request.parse() + self.assertEqual("xxxx", request.session_key) + self.assertEqual("spyguy", request.unique_nick) + self.assertEqual(0, request.partner_id) + + def test_update_profile(self) -> None: + crysisWarsRaw = "\\updatepro\\\\sesskey\\1111\\countrycode\\DE\\birthday\\168232912\\partnerid\\0\\final\\" + request = UpdateProfileRequest(crysisWarsRaw) + request.parse() + + # region Buddy + def test_add_buddy(self) -> None: + request = AddBuddyRequest(ADD_BUDDY) + request.parse() + self.assertEqual(0, request.friend_profile_id) + self.assertEqual("test", request.reason) + + def test_del_buddy(self) -> None: + request = DelBuddyRequest(DEL_BUDDY) + request.parse() + self.assertEqual(0, request.friend_profile_id) + + def test_invite_to(self) -> None: + request = InviteToRequest(INVITE_TO) + request.parse() + self.assertEqual(0, request.product_id) + self.assertEqual(0, request.profile_id) + + def test_status_test(self) -> None: + request = StatusRequest(STATUS[0]) + request.parse() + self.assertEqual("test", request.status_string) + self.assertEqual("test", request.location_string) + request = StatusRequest(STATUS[1]) + request.parse() From d2ae6526c634017afa1f02947d67b9a06d82cab1 Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Tue, 27 May 2025 09:45:27 +0000 Subject: [PATCH 186/231] Added unittests --- src/backends/library/database/pg_orm.py | 2 +- .../protocols/gamespy/game_status/requests.py | 7 +- src/backends/protocols/gamespy/natneg/data.py | 1 - .../protocols/gamespy/natneg/handlers.py | 20 +++- .../protocols/gamespy/natneg/requests.py | 7 +- .../presence_connection_manager/data.py | 41 +++---- .../presence_connection_manager/handlers.py | 7 +- .../presence_connection_manager/requests.py | 2 +- .../gamespy/presence_search_player/data.py | 6 +- .../presence_search_player/handlers.py | 19 +++- .../protocols/gamespy/query_report/data.py | 82 +++++++++----- .../gamespy/query_report/requests.py | 2 +- .../gamespy/server_browser/handlers.py | 101 +++++++++++------- .../gamespy/server_browser/requests.py | 16 +-- .../protocols/gamespy/web_services/data.py | 3 +- .../routers/gamespy/server_browser.py | 2 +- src/backends/routers/home.py | 1 - src/backends/tests/gamespy/chat/room_tests.py | 1 - src/backends/tests/gamespy/natneg/__init__.py | 0 .../tests/gamespy/natneg/handler_tests.py | 23 ++++ .../precence_search_player/handler_tests.py | 1 + .../gamespy/query_report/data_fetch_tests.py | 7 +- .../tests/gamespy/server_browser/__init__.py | 0 .../server_browser/data_fetch_tests.py | 13 +++ .../gamespy/server_browser/handler_tests.py | 34 ++++++ .../gamespy/library/abstractions/handler.py | 23 ++-- .../gamespy/library/encryption/encoding.py | 1 - .../library/encryption/gs_encryption.py | 1 - .../library/encryption/xor_encryption.py | 1 - .../library/extentions/gamespy_utils.py | 2 +- .../gamespy/library/extentions/redis_orm.py | 1 - .../library/extentions/string_extentions.py | 2 - .../protocols/chat/applications/client.py | 1 - .../chat/applications/server_launcher.py | 2 +- .../game_traffic_relay/applications/router.py | 2 +- .../natneg/abstractions/contracts.py | 2 - .../protocols/natneg/applications/client.py | 4 +- .../protocols/natneg/applications/switcher.py | 2 +- .../protocols/natneg/contracts/requests.py | 14 ++- .../protocols/natneg/contracts/results.py | 1 - .../abstractions/contracts.py | 1 - .../applications/client.py | 4 +- .../applications/switcher.py | 2 +- .../contracts/requests.py | 15 ++- .../query_report/applications/client.py | 2 +- .../query_report/v1/abstractions/contracts.py | 1 - .../query_report/v2/applications/switcher.py | 3 +- .../v2/abstractions/handlers.py | 1 - .../server_browser/v2/applications/client.py | 4 +- .../v2/applications/handlers.py | 78 +++++++++----- .../v2/applications/switcher.py | 2 +- .../server_browser/v2/contracts/responses.py | 2 +- .../server_browser/v2/contracts/results.py | 3 - .../modules/auth/handlers/general.py | 1 - .../modules/direct2game/handlers/general.py | 1 - .../modules/sake/contracts/requests.py | 1 - .../tests/gamespy/library/encrypt_tests.py | 1 - .../tests/gamespy/natneg/handler_tests.py | 3 +- .../tests/gamespy/natneg/mock_objects.py | 1 - .../mock_objects.py | 1 - .../request_tests.py | 2 +- .../presence_search_player/game_tests.py | 5 +- .../presence_search_player/mock_objects.py | 1 - .../gamespy/query_report/mock_objects.py | 1 - .../gamespy/server_browser/game_tests.py | 31 +++--- .../gamespy/server_browser/mock_objects.py | 86 ++++++++++++--- 66 files changed, 477 insertions(+), 233 deletions(-) create mode 100644 src/backends/tests/gamespy/natneg/__init__.py create mode 100644 src/backends/tests/gamespy/natneg/handler_tests.py create mode 100644 src/backends/tests/gamespy/server_browser/__init__.py create mode 100644 src/backends/tests/gamespy/server_browser/data_fetch_tests.py create mode 100644 src/backends/tests/gamespy/server_browser/handler_tests.py diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 82062ced4..d0f936e30 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -197,7 +197,7 @@ class InitPacketCaches(Base): version = Column(Integer, nullable=False) port_type = Column(IntEnum(NatPortType), nullable=False) client_index = Column(IntEnum(NatClientIndex), nullable=False) - game_name = Column(String, nullable=False) + game_name = Column(String, nullable=True) use_game_port = Column(Boolean, nullable=False) public_ip = Column(String, nullable=False) public_port = Column(Integer, nullable=False) diff --git a/src/backends/protocols/gamespy/game_status/requests.py b/src/backends/protocols/gamespy/game_status/requests.py index 0c0dff8da..5511d816b 100644 --- a/src/backends/protocols/gamespy/game_status/requests.py +++ b/src/backends/protocols/gamespy/game_status/requests.py @@ -1,12 +1,15 @@ from typing import Optional import backends.library.abstractions.contracts as lib -from frontends.gamespy.protocols.game_status.aggregations.enums import AuthMethod, PersistStorageType +from frontends.gamespy.protocols.game_status.aggregations.enums import ( + AuthMethod, + PersistStorageType, +) class RequestBase(lib.RequestBase): command_name: str raw_request: str - local_id: Optional[int] + local_id: Optional[int] = None request_dict: dict[str, str] diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py index 9f9bbfcb7..e78f372bd 100644 --- a/src/backends/protocols/gamespy/natneg/data.py +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -32,7 +32,6 @@ def get_init_infos(server_id: UUID, cookie: int, client_index: NatClientIndex) - def update_init_info(info: InitPacketCaches) -> None: assert isinstance(info, InitPacketCaches) - remove_init_info(info) store_init_packet(info) diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py index 7903fab50..f2bf570f8 100644 --- a/src/backends/protocols/gamespy/natneg/handlers.py +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -1,4 +1,5 @@ -from backends.library.abstractions.contracts import OKResponse, RequestBase +from datetime import datetime, timezone +from backends.library.abstractions.contracts import RequestBase from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import InitPacketCaches from backends.protocols.gamespy.natneg.data import update_init_info @@ -6,12 +7,27 @@ class InitHandler(HandlerBase): + _request: InitRequest + def __init__(self, request: InitRequest) -> None: assert isinstance(request, InitRequest) super().__init__(request) def _data_operate(self) -> None: - info = InitPacketCaches(**self._request.model_dump(mode="json")) + info = InitPacketCaches( + cookie=self._request.cookie, + server_id=self._request.server_id, + version=self._request.version, + port_type=self._request.port_type, + client_index=self._request.client_index, + game_name=self._request.game_name, + use_game_port=self._request.use_game_port, + public_ip=self._request.client_ip, + public_port=self._request.client_port, + private_ip=self._request.private_ip, + private_port=self._request.private_port, + update_time=datetime.now(timezone.utc), + ) update_init_info(info) diff --git a/src/backends/protocols/gamespy/natneg/requests.py b/src/backends/protocols/gamespy/natneg/requests.py index 500b6b7b6..3f9c63eda 100644 --- a/src/backends/protocols/gamespy/natneg/requests.py +++ b/src/backends/protocols/gamespy/natneg/requests.py @@ -12,7 +12,7 @@ class RequestBase(lib.RequestBase): - raw_request: list + raw_request: str version: int cookie: int port_type: NatPortType @@ -45,8 +45,9 @@ class ErtAckRequest(CommonRequestBase): class InitRequest(CommonRequestBase): - game_name: Optional[str] - private_ip_address: Optional[str] + game_name: Optional[str] = None + private_ip: str + private_port: int class NatifyRequest(CommonRequestBase): diff --git a/src/backends/protocols/gamespy/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py index e7356c506..2aded2b3f 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/data.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/data.py @@ -175,14 +175,14 @@ def get_user_info_list(email: str, nick_name: str) -> list[tuple[int, int, int]] def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: - if TYPE_CHECKING: - assert isinstance(Profiles.profileid, Column) - assert isinstance(Profiles.userid, Column) - assert isinstance(Users.userid, Column) - assert isinstance(Users.email, Column) - assert isinstance(Profiles.nick, Column) - assert isinstance(SubProfiles.uniquenick, Column) - assert isinstance(SubProfiles.namespaceid, Column) + # if TYPE_CHECKING: + # assert isinstance(Profiles.profileid, Column) + # assert isinstance(Profiles.userid, Column) + # assert isinstance(Users.userid, Column) + # assert isinstance(Users.email, Column) + # assert isinstance(Profiles.nick, Column) + # assert isinstance(SubProfiles.uniquenick, Column) + # assert isinstance(SubProfiles.namespaceid, Column) result = ( PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) @@ -327,19 +327,20 @@ def get_user_infos_by_authtoken(auth_token: str) -> LoginData | None: .where(SubProfiles.authtoken == auth_token) .first() ) - data = { - "user_id": result[0], - "profile_id": result[1], - "sub_profile_id": result[2], - "nick": result[3], - "email": result[4], - "unique_nick": result[5], - "password_hash": result[6], - "email_verified_flag": result[7], - "banned_flag": result[8], - "namespace_id": result[9], - } + if result is not None: + data = { + "user_id": result[0], + "profile_id": result[1], + "sub_profile_id": result[2], + "nick": result[3], + "email": result[4], + "unique_nick": result[5], + "password_hash": result[6], + "email_verified_flag": result[7], + "banned_flag": result[8], + "namespace_id": result[9], + } return LoginData(**data) # type: ignore else: return None diff --git a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py index 2e2513400..9cb79cf34 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py @@ -32,6 +32,7 @@ ) from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import ( GPLoginBadEmailException, + GPLoginException, ) # region General @@ -69,7 +70,7 @@ def _nick_email_login(self) -> None: def _unique_nick_login(self) -> None: assert self._request.unique_nick is not None assert self._request.namespace_id is not None - self.data = data.get_user_infos_by_uniquenick_namespace_id( + self._data = data.get_user_infos_by_uniquenick_namespace_id( self._request.unique_nick, self._request.namespace_id ) @@ -78,7 +79,8 @@ def _auth_token_login(self) -> None: self._data = data.get_user_infos_by_authtoken(self._request.auth_token) def _result_construct(self) -> None: - assert self._data + if self._data is None: + raise GPLoginException("User is not exist.") self._result = LoginResult(data=self._data) @@ -195,7 +197,6 @@ def _data_operate(self) -> None: ) - class StatusInfoHandler(HandlerBase): _request: StatusInfoRequest diff --git a/src/backends/protocols/gamespy/presence_connection_manager/requests.py b/src/backends/protocols/gamespy/presence_connection_manager/requests.py index 26b41fadb..fc6691c32 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/requests.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/requests.py @@ -120,7 +120,7 @@ class LoginRequest(RequestBase): sdk_revision_type: list[SdkRevisionType] game_port: int partner_id: int - game_name: Optional[str] + game_name: Optional[str] = None quiet_mode_flags: int firewall: bool diff --git a/src/backends/protocols/gamespy/presence_search_player/data.py b/src/backends/protocols/gamespy/presence_search_player/data.py index 2608e7d70..bbd372ed9 100644 --- a/src/backends/protocols/gamespy/presence_search_player/data.py +++ b/src/backends/protocols/gamespy/presence_search_player/data.py @@ -106,9 +106,9 @@ def get_profile(user_id: int, nick_name: str) -> Profiles | None: def get_sub_profile( profile_id: int, namespace_id: int, product_id: int ) -> SubProfiles | None: - assert isinstance(SubProfiles.profileid, Column) - assert isinstance(SubProfiles.namespaceid, Column) - assert isinstance(SubProfiles.namespaceid, Column) + assert isinstance(profile_id, int) + assert isinstance(namespace_id, int) + assert isinstance(product_id, int) result = ( PG_SESSION.query(SubProfiles) .where( diff --git a/src/backends/protocols/gamespy/presence_search_player/handlers.py b/src/backends/protocols/gamespy/presence_search_player/handlers.py index d5c8bd07d..8ecc649e8 100644 --- a/src/backends/protocols/gamespy/presence_search_player/handlers.py +++ b/src/backends/protocols/gamespy/presence_search_player/handlers.py @@ -13,7 +13,9 @@ ValidRequest, ) from frontends.gamespy.library.exceptions.general import UniSpyException -from frontends.gamespy.protocols.presence_search_player.aggregates.enums import SearchType +from frontends.gamespy.protocols.presence_search_player.aggregates.enums import ( + SearchType, +) from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import ( CheckException, ) @@ -43,9 +45,11 @@ class CheckHandler(HandlerBase): _result: CheckResult def _data_operate(self) -> None: - if data.verify_email(self._request.email): + if not data.verify_email(self._request.email): raise CheckException("The email is not existed") - if data.verify_email_and_password(self._request.email, self._request.password): + if not data.verify_email_and_password( + self._request.email, self._request.password + ): raise CheckException("The password is incorrect") self._profile_id = data.get_profile_id( self._request.email, @@ -102,6 +106,7 @@ def _create_user(self) -> None: if key in Users.__dict__: user_dict[key] = value self.user = Users(**user_dict) + PG_SESSION.add(self.user) PG_SESSION.commit() def _create_profile(self) -> None: @@ -109,7 +114,12 @@ def _create_profile(self) -> None: for key, value in self._request.__dict__.items(): if key in Profiles.__dict__: profile_dict[key] = value + + assert self.user is not None + assert isinstance(self.user.userid, int) + profile_dict["userid"] = self.user.userid self.profile = Profiles(**profile_dict) + PG_SESSION.add(self.profile) PG_SESSION.commit() def _create_subprofile(self) -> None: @@ -117,7 +127,10 @@ def _create_subprofile(self) -> None: for key, value in self._request.__dict__.items(): if key in SubProfiles.__dict__: subprofile_dict[key] = value + assert self.profile is not None + subprofile_dict["profileid"] = self.profile.profileid self.subprofile = SubProfiles(**subprofile_dict) + PG_SESSION.add(self.subprofile) PG_SESSION.commit() diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index b294e9a85..9f480afff 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -1,9 +1,21 @@ from typing import TYPE_CHECKING, Optional, cast -from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches, GroupList, Games, GameServerCaches +from backends.library.database.pg_orm import ( + PG_SESSION, + ChatChannelCaches, + GroupList, + Games, + GameServerCaches, +) from frontends.gamespy.protocols.chat.aggregates.peer_room import PeerRoom -from frontends.gamespy.protocols.query_report.aggregates.game_server_info import GameServerInfo -from frontends.gamespy.protocols.query_report.aggregates.peer_room_info import PeerRoomInfo -from frontends.gamespy.protocols.server_browser.aggregates.exceptions import ServerBrowserException +from frontends.gamespy.protocols.query_report.aggregates.game_server_info import ( + GameServerInfo, +) +from frontends.gamespy.protocols.query_report.aggregates.peer_room_info import ( + PeerRoomInfo, +) +from frontends.gamespy.protocols.server_browser.aggregates.exceptions import ( + ServerBrowserException, +) def get_all_groups() -> dict: @@ -45,11 +57,14 @@ def get_peer_staging_channels(game_name: str, group_id: int) -> list[GameServerI assert isinstance(game_name, str) assert isinstance(group_id, int) staging_name = f"{PeerRoom.StagingRoomPrefix}!{game_name}!*" - result = PG_SESSION.query(ChatChannelCaches).where( - ChatChannelCaches.channel_name == staging_name).all() + result = ( + PG_SESSION.query(ChatChannelCaches) + .where(ChatChannelCaches.channel_name == staging_name) + .all() + ) data = [] for s in result: - t = {k: v for k, v in s.__dict__.items() if k != '_sa_instance_state'} + t = {k: v for k, v in s.__dict__.items() if k != "_sa_instance_state"} data.append(GameServerInfo(**t)) return data @@ -66,15 +81,19 @@ def get_group_data_list_by_gamename(game_name: str) -> list[dict]: def get_peer_group_channel(group_data: list[dict]) -> list[PeerRoomInfo]: assert isinstance(group_data, list) and all( - isinstance(id, dict) for id in group_data) + isinstance(id, dict) for id in group_data + ) # Construct the group names based on the provided group_ids - group_names = [f"{PeerRoom.GroupRoomPrefix}!{ - item['group_id']}" for item in group_data] + group_names = [ + f"{PeerRoom.GroupRoomPrefix}!{item['group_id']}" for item in group_data + ] # Query the database for channels matching the constructed group names - result = PG_SESSION.query(ChatChannelCaches).filter( - ChatChannelCaches.channel_name.in_(group_names) - ).all() + result = ( + PG_SESSION.query(ChatChannelCaches) + .filter(ChatChannelCaches.channel_name.in_(group_names)) + .all() + ) # Convert the result to a list of PeerRoomInfo objects data = [PeerRoomInfo(**s.__dict__) for s in result] @@ -84,34 +103,49 @@ def get_peer_group_channel(group_data: list[dict]) -> list[PeerRoomInfo]: def get_server_info_with_instant_key(instant_key: str) -> Optional[GameServerCaches]: assert isinstance(instant_key, str) - result = PG_SESSION.query(GameServerCaches).where( - GameServerCaches.instant_key == instant_key).first() + result = ( + PG_SESSION.query(GameServerCaches) + .where(GameServerCaches.instant_key == instant_key) + .first() + ) return result def get_server_info_with_game_name(game_name: str) -> Optional[GameServerCaches]: assert isinstance(game_name, str) - result = PG_SESSION.query(GameServerCaches).where( - GameServerCaches.game_name == game_name).first() + result = ( + PG_SESSION.query(GameServerCaches) + .where(GameServerCaches.game_name == game_name) + .first() + ) return result def get_server_info_list_with_game_name(game_name: str) -> list[GameServerInfo]: - result = PG_SESSION.query(GameServerCaches).where( - GameServerCaches.game_name == game_name).all() + result = ( + PG_SESSION.query(GameServerCaches) + .where(GameServerCaches.game_name == game_name) + .all() + ) data = [] for s in result: data.append(GameServerInfo(**s.__dict__)) return data -def get_server_info_with_ip_and_port(ip: str, port: int) -> GameServerInfo: +def get_server_info_with_ip_and_port(ip: str, port: int) -> GameServerInfo | None: assert isinstance(ip, str) assert isinstance(port, int) - result = PG_SESSION.query(GameServerCaches).where( - GameServerCaches.host_ip_address == ip, GameServerCaches.query_report_port == port).first() + result = ( + PG_SESSION.query(GameServerCaches) + .where( + GameServerCaches.host_ip_address == ip, + GameServerCaches.query_report_port == port, + ) + .first() + ) if result is None: - raise ServerBrowserException("game server not found") + return None data = GameServerInfo(**result.__dict__) return data @@ -120,6 +154,7 @@ def remove_server_info(info: GameServerCaches) -> None: PG_SESSION.delete(info) PG_SESSION.commit() + # todo finish the GameServerCaches creation @@ -129,7 +164,6 @@ def create_game_server(info: GameServerCaches) -> None: def update_game_server() -> None: - from datetime import datetime # info.update_time = datetime.now() # type:ignore PG_SESSION.commit() diff --git a/src/backends/protocols/gamespy/query_report/requests.py b/src/backends/protocols/gamespy/query_report/requests.py index 7c4e57e1d..e9a9e8ffd 100644 --- a/src/backends/protocols/gamespy/query_report/requests.py +++ b/src/backends/protocols/gamespy/query_report/requests.py @@ -1,5 +1,5 @@ -from pydantic import UUID4, Field, constr +from pydantic import UUID4 import backends.library.abstractions.contracts as lib from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus, RequestType diff --git a/src/backends/protocols/gamespy/server_browser/handlers.py b/src/backends/protocols/gamespy/server_browser/handlers.py index eea61552e..863a2fa05 100644 --- a/src/backends/protocols/gamespy/server_browser/handlers.py +++ b/src/backends/protocols/gamespy/server_browser/handlers.py @@ -1,12 +1,31 @@ from typing import TYPE_CHECKING, cast from backends.library.abstractions.handler_base import HandlerBase -from backends.protocols.gamespy.server_browser.requests import * import backends.protocols.gamespy.query_report.data as data -from frontends.gamespy.protocols.query_report.aggregates.game_server_info import GameServerInfo -from frontends.gamespy.protocols.query_report.aggregates.peer_room_info import PeerRoomInfo -from frontends.gamespy.protocols.server_browser.aggregates.exceptions import ServerBrowserException -from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import GameServerFlags, ServerListUpdateOption -from frontends.gamespy.protocols.server_browser.v2.contracts.results import P2PGroupRoomListResult, SendMessageResult, ServerInfoResult, ServerMainListResult +from backends.protocols.gamespy.server_browser.requests import ( + AdHocRequestBase, + SendMessageRequest, + ServerInfoRequest, + ServerListRequest, +) +from frontends.gamespy.protocols.query_report.aggregates.game_server_info import ( + GameServerInfo, +) +from frontends.gamespy.protocols.query_report.aggregates.peer_room_info import ( + PeerRoomInfo, +) +from frontends.gamespy.protocols.server_browser.aggregates.exceptions import ( + ServerBrowserException, +) +from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import ( + GameServerFlags, + ServerListUpdateOption, +) +from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( + P2PGroupRoomListResult, + SendMessageResult, + ServerInfoResult, + ServerMainListResult, +) # region Server list @@ -16,45 +35,45 @@ class ServerListHandler(HandlerBase): _caches: list[PeerRoomInfo] | list[GameServerInfo] def _data_operate(self): - if self._request.update_option in\ - [ServerListUpdateOption.SERVER_MAIN_LIST, - ServerListUpdateOption.P2P_SERVER_MAIN_LIST, - ServerListUpdateOption.LIMIT_RESULT_COUNT, - ServerListUpdateOption.SERVER_FULL_INFO_LIST,]: - + if self._request.update_option in ServerListUpdateOption: self._caches = data.get_server_info_list_with_game_name( - self._request.game_name) + self._request.game_name + ) elif self._request.update_option == ServerListUpdateOption.P2P_GROUP_ROOM_LIST: - group_data = data.get_group_data_list_by_gamename( - self._request.game_name) + group_data = data.get_group_data_list_by_gamename(self._request.game_name) self._caches = data.get_peer_group_channel(group_data) else: - raise ServerBrowserException( - "invalid server browser update option") + raise ServerBrowserException("invalid server browser update option") def _result_construct(self): - if self._request.update_option in\ - [ServerListUpdateOption.SERVER_MAIN_LIST, - ServerListUpdateOption.P2P_SERVER_MAIN_LIST, - ServerListUpdateOption.LIMIT_RESULT_COUNT, - ServerListUpdateOption.SERVER_FULL_INFO_LIST,]: + if self._request.update_option in ServerListUpdateOption: assert isinstance(self._caches, list) and all( - isinstance(item, GameServerInfo) for item in self._caches) + isinstance(item, GameServerInfo) for item in self._caches + ) if TYPE_CHECKING: self._caches = cast(list[GameServerInfo], self._caches) - self._result = ServerMainListResult(client_remote_ip=self._request.client_ip, - flag=GameServerFlags.HAS_KEYS_FLAG, game_secret_key="", servers_info=self._caches) + self._result = ServerMainListResult( + client_remote_ip=self._request.client_ip, + flag=GameServerFlags.HAS_KEYS_FLAG, + game_secret_key="", + servers_info=self._caches, + ) elif self._request.update_option == ServerListUpdateOption.P2P_GROUP_ROOM_LIST: assert isinstance(self._caches, list) and all( - isinstance(item, PeerRoomInfo) for item in self._caches) + isinstance(item, PeerRoomInfo) for item in self._caches + ) if TYPE_CHECKING: self._caches = cast(list[PeerRoomInfo], self._caches) self._result = P2PGroupRoomListResult( - client_remote_ip=self._request.client_ip, flag=GameServerFlags.HAS_KEYS_FLAG, game_secret_key="", peer_room_info=self._caches) + client_remote_ip=self._request.client_ip, + flag=GameServerFlags.HAS_KEYS_FLAG, + game_secret_key="", + peer_room_info=self._caches, + ) else: - raise ServerBrowserException( - "invalid server browser update option") + raise ServerBrowserException("invalid server browser update option") + # region Adhoc @@ -70,20 +89,30 @@ class SendMessageHandler(HandlerBase): _request: SendMessageRequest def _data_operate(self): - self.data = data.get_server_info_with_ip_and_port( - self._request.game_server_public_ip, self._request.game_server_public_port) + self._data = data.get_server_info_with_ip_and_port( + self._request.game_server_public_ip, self._request.game_server_public_port + ) def _result_construct(self): - self._result = SendMessageResult(sb_sender_id=self._request.server_id, - natneg_message=self._request.client_message, server_info=self.data) + if self._data is None: + return + self._result = SendMessageResult( + sb_sender_id=self._request.server_id, + natneg_message=self._request.client_message, + server_info=self._data, + ) class ServerInfoHandler(HandlerBase): _request: ServerInfoRequest def _data_operate(self) -> None: - self.data = data.get_server_info_with_ip_and_port( - self._request.game_server_public_ip, self._request.game_server_public_port) + self._data = data.get_server_info_with_ip_and_port( + self._request.game_server_public_ip, self._request.game_server_public_port + ) def _result_construct(self) -> None: - self._result = ServerInfoResult(game_server_info=self.data) + # TODO: check whether we need respond when gameserver not exist + if self._data is None: + return + self._result = ServerInfoResult(game_server_info=self._data) diff --git a/src/backends/protocols/gamespy/server_browser/requests.py b/src/backends/protocols/gamespy/server_browser/requests.py index 3a03ff2fc..7b7dab736 100644 --- a/src/backends/protocols/gamespy/server_browser/requests.py +++ b/src/backends/protocols/gamespy/server_browser/requests.py @@ -1,28 +1,28 @@ -from typing import List, Optional +from typing import Optional import backends.library.abstractions.contracts as lib -from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import RequestType, ServerListUpdateOption +from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import ( + ServerListUpdateOption, +) class RequestBase(lib.RequestBase): - request_length: int raw_request: bytes - command_name: RequestType class ServerListUpdateOptionRequestBase(RequestBase): - source_ip: str request_version: int protocol_version: int encoding_version: int game_version: int - query_options: int dev_game_name: str game_name: str client_challenge: str update_option: ServerListUpdateOption keys: list[str] - filter: list[str] - max_servers: int + filter: str + max_servers: Optional[int] = None + source_ip: Optional[str] = None + query_options: Optional[int] = None class ServerListRequest(ServerListUpdateOptionRequestBase): diff --git a/src/backends/protocols/gamespy/web_services/data.py b/src/backends/protocols/gamespy/web_services/data.py index 8fd64dcf3..d431192fb 100644 --- a/src/backends/protocols/gamespy/web_services/data.py +++ b/src/backends/protocols/gamespy/web_services/data.py @@ -2,9 +2,8 @@ # region auth -from typing import TYPE_CHECKING, cast, overload +from typing import TYPE_CHECKING, cast from backends.library.database.pg_orm import PG_SESSION, Profiles, SubProfiles, Users, SakeStorage -from frontends.gamespy.protocols.web_services.aggregations.exceptions import WebException from frontends.gamespy.protocols.web_services.modules.auth.exceptions.general import AuthException from frontends.gamespy.protocols.web_services.modules.sake.exceptions.general import SakeException diff --git a/src/backends/routers/gamespy/server_browser.py b/src/backends/routers/gamespy/server_browser.py index 731dbca76..e70f904f6 100644 --- a/src/backends/routers/gamespy/server_browser.py +++ b/src/backends/routers/gamespy/server_browser.py @@ -1,7 +1,7 @@ from fastapi import APIRouter, WebSocket, WebSocketDisconnect from backends.protocols.gamespy.server_browser.handlers import ServerInfoHandler, ServerListHandler from backends.protocols.gamespy.server_browser.requests import SendMessageRequest, ServerInfoRequest, ServerListRequest -from backends.urls import SERVER_BROWSER_V1, SERVER_BROWSER_V2 +from backends.urls import SERVER_BROWSER_V2 router = APIRouter() # todo maybe implement this in websocket way diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index 61878610f..c063ef26a 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -10,7 +10,6 @@ from frontends.gamespy.library.log.log_manager import LogManager from frontends.gamespy.library.configs import ServerConfig from backends.routers.gamespy import ( - chat, gstats, natneg, presence_connection_manager, diff --git a/src/backends/tests/gamespy/chat/room_tests.py b/src/backends/tests/gamespy/chat/room_tests.py index 9b6061122..1e2dea08c 100644 --- a/src/backends/tests/gamespy/chat/room_tests.py +++ b/src/backends/tests/gamespy/chat/room_tests.py @@ -22,6 +22,5 @@ # pass -from fastapi import testclient # testclient.TestClient.get() \ No newline at end of file diff --git a/src/backends/tests/gamespy/natneg/__init__.py b/src/backends/tests/gamespy/natneg/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/gamespy/natneg/handler_tests.py b/src/backends/tests/gamespy/natneg/handler_tests.py new file mode 100644 index 000000000..0f466db5d --- /dev/null +++ b/src/backends/tests/gamespy/natneg/handler_tests.py @@ -0,0 +1,23 @@ +import unittest +from backends.protocols.gamespy.natneg.handlers import InitHandler +from backends.protocols.gamespy.natneg.requests import InitRequest +from backends.tests.utils import add_headers +import frontends.gamespy.protocols.natneg.contracts.requests as fnt + + +class HandlerTests(unittest.TestCase): + def test_init(self): + raw = bytes( + [ + 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, + 0x00, + 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + ) # fmt:skip + r = fnt.InitRequest(raw) + data = add_headers(r) + data["raw_request"] = data["raw_request"].decode("ascii", "backslashreplace") + request = InitRequest(**data) + handler = InitHandler(request) + handler.handle() + pass diff --git a/src/backends/tests/gamespy/precence_search_player/handler_tests.py b/src/backends/tests/gamespy/precence_search_player/handler_tests.py index 3db990632..c1f274179 100644 --- a/src/backends/tests/gamespy/precence_search_player/handler_tests.py +++ b/src/backends/tests/gamespy/precence_search_player/handler_tests.py @@ -1,6 +1,7 @@ # the total requests tests import unittest +from backends.library.database.pg_orm import PG_SESSION from backends.tests.utils import add_headers import frontends.gamespy.protocols.presence_search_player.contracts.requests as psp from frontends.tests.gamespy.presence_search_player.handler_tests import ( diff --git a/src/backends/tests/gamespy/query_report/data_fetch_tests.py b/src/backends/tests/gamespy/query_report/data_fetch_tests.py index 8fdb2460a..7a203110d 100644 --- a/src/backends/tests/gamespy/query_report/data_fetch_tests.py +++ b/src/backends/tests/gamespy/query_report/data_fetch_tests.py @@ -4,7 +4,6 @@ from pydantic import ValidationError from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches import backends.protocols.gamespy.query_report.data as data -from frontends.gamespy.protocols.query_report.aggregates.game_server_info import GameServerInfo class DataFetchTests(unittest.TestCase): @@ -20,10 +19,12 @@ def test_get_peer_staging_channels(self): room_name="unispy_test_room_name", group_id=0, max_num_user=100, - update_time=datetime.now(timezone.utc)) + update_time=datetime.now(timezone.utc), + ) PG_SESSION.add(cache) PG_SESSION.commit() self.assertRaises( - ValidationError, data.get_peer_staging_channels, "unispy_test_game_name", 0) + ValidationError, data.get_peer_staging_channels, "unispy_test_game_name", 0 + ) PG_SESSION.delete(cache) PG_SESSION.commit() diff --git a/src/backends/tests/gamespy/server_browser/__init__.py b/src/backends/tests/gamespy/server_browser/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/gamespy/server_browser/data_fetch_tests.py b/src/backends/tests/gamespy/server_browser/data_fetch_tests.py new file mode 100644 index 000000000..8460236a0 --- /dev/null +++ b/src/backends/tests/gamespy/server_browser/data_fetch_tests.py @@ -0,0 +1,13 @@ +import unittest + + + +class DataFetchTests(unittest.TestCase): + def test_server_main_list(self): + pass + + def test_p2p_group_room_list(self): + pass + + def test_server_info(self): + pass diff --git a/src/backends/tests/gamespy/server_browser/handler_tests.py b/src/backends/tests/gamespy/server_browser/handler_tests.py new file mode 100644 index 000000000..554293f9b --- /dev/null +++ b/src/backends/tests/gamespy/server_browser/handler_tests.py @@ -0,0 +1,34 @@ +import unittest +from backends.protocols.gamespy.server_browser.handlers import ( + ServerInfoHandler, + ServerListHandler, +) +from backends.protocols.gamespy.server_browser.requests import ( + ServerInfoRequest, + ServerListRequest, +) +from backends.tests.utils import add_headers +import frontends.gamespy.protocols.server_browser.v2.contracts.requests as fnt + + +class HandlerTests(unittest.TestCase): + def test_server_main_list(self): + raw = b"\x00\x9a\x00\x01\x03\x8fU\x00\x00anno1701\x00anno1701\x00D:@o)Okhgroupid is null\x00\\hostname\\gamemode\\gamever\\gametype\\password\\mapname\\numplayers\\numaiplayers\\openslots\\gamevariant\x00\x00\x00\x00\x04" + r = fnt.ServerListRequest(raw) + data = add_headers(r) + request = ServerListRequest(**data) + handler = ServerListHandler(request) + handler.handle() + pass + + def test_p2p_group_room_list(self): + raise NotImplementedError() + + def test_server_info(self): + raw = b"\x00\t\x01\xc0\xa8z\xe2+g" + r = fnt.ServerInfoRequest(raw) + data = add_headers(r) + request = ServerInfoRequest(**data) + handler = ServerInfoHandler(request) + handler.handle() + pass diff --git a/src/frontends/gamespy/library/abstractions/handler.py b/src/frontends/gamespy/library/abstractions/handler.py index 2223964eb..0c95a07d1 100644 --- a/src/frontends/gamespy/library/abstractions/handler.py +++ b/src/frontends/gamespy/library/abstractions/handler.py @@ -94,7 +94,7 @@ def _prepare_data(self): self._temp_data["server_id"] = self._client.server_config.server_id self._temp_data["client_port"] = self._client.connection.remote_port - def __get_url(self) -> str: + def _get_url(self) -> str: url = f"{CONFIG.backend.url}/GameSpy/{self._client.server_config.server_name}/{ self.__class__.__name__ }" @@ -105,14 +105,14 @@ def _upload_data(self): whether need send data to backend if child class do not require feach, overide this function to do nothing """ - self.__url = self.__get_url() + self._url = self._get_url() json_str = json.dumps( self._temp_data, cls=UniSpyJsonEncoder, ensure_ascii=False ) - self._client.log_network_upload(f"[{self.__url}] {json_str}") + self._client.log_network_upload(f"[{self._url}] {json_str}") try: response = requests.post( - self.__url, data=json_str, headers={"Content-Type": "application/json"} + self._url, data=json_str, headers={"Content-Type": "application/json"} ) except requests.exceptions.ConnectionError: if CONFIG.unittest.is_raise_except: @@ -124,8 +124,19 @@ def _upload_data(self): if response.status_code != 200: raise UniSpyException("failed to upload data to backends.") + self._http_result = response.json() + if "error" in self._http_result: + self._handle_upload_error() + + def _handle_upload_error(self): + """ + handle the error message response from backend + """ + # we raise the error as UniSpyException + raise UniSpyException(self._http_result["error"]) + def _feach_data(self): """ whether need get data from backend. @@ -134,9 +145,9 @@ def _feach_data(self): if self._result_cls is None: raise UniSpyException("_result_cls can not be null when feach data.") assert issubclass(self._result_cls, ResultBase) - self._client.log_network_fetch(f"[{self.__url}] {self._http_result}") + self._client.log_network_fetch(f"[{self._url}] {self._http_result}") - self._result = self._result_cls(**self._http_result["result"]) + self._result = self._result_cls(**self._http_result) def _response_construct(self) -> None: """construct response here in specific child class""" diff --git a/src/frontends/gamespy/library/encryption/encoding.py b/src/frontends/gamespy/library/encryption/encoding.py index 33739938a..bdc42a1e1 100644 --- a/src/frontends/gamespy/library/encryption/encoding.py +++ b/src/frontends/gamespy/library/encryption/encoding.py @@ -1,4 +1,3 @@ -from typing import List class Encoding: diff --git a/src/frontends/gamespy/library/encryption/gs_encryption.py b/src/frontends/gamespy/library/encryption/gs_encryption.py index 7177543a5..fb6feca1f 100644 --- a/src/frontends/gamespy/library/encryption/gs_encryption.py +++ b/src/frontends/gamespy/library/encryption/gs_encryption.py @@ -1,5 +1,4 @@ from frontends.gamespy.library.abstractions.enctypt_base import EncryptBase -import hashlib DIGITS_HEX = "0123456789abcdef" DIGITS_CRYPT = "aFl4uOD9sfWq1vGp" diff --git a/src/frontends/gamespy/library/encryption/xor_encryption.py b/src/frontends/gamespy/library/encryption/xor_encryption.py index f536eb815..bd4e00cc9 100644 --- a/src/frontends/gamespy/library/encryption/xor_encryption.py +++ b/src/frontends/gamespy/library/encryption/xor_encryption.py @@ -1,5 +1,4 @@ from enum import IntEnum -import base64 from frontends.gamespy.library.abstractions.enctypt_base import EncryptBase diff --git a/src/frontends/gamespy/library/extentions/gamespy_utils.py b/src/frontends/gamespy/library/extentions/gamespy_utils.py index 63f1b7e2d..bf15b21ba 100644 --- a/src/frontends/gamespy/library/extentions/gamespy_utils.py +++ b/src/frontends/gamespy/library/extentions/gamespy_utils.py @@ -6,7 +6,7 @@ def is_email_format_correct(email: str) -> bool: try: validate_email(email, check_deliverability=False) - except EmailNotValidError as e: + except EmailNotValidError: return False return True diff --git a/src/frontends/gamespy/library/extentions/redis_orm.py b/src/frontends/gamespy/library/extentions/redis_orm.py index ab469f50e..5c2117118 100644 --- a/src/frontends/gamespy/library/extentions/redis_orm.py +++ b/src/frontends/gamespy/library/extentions/redis_orm.py @@ -34,5 +34,4 @@ # query = QueryBuilder(None, None) # result = query.filter_by(url="example.com", name="hello").first() # print(result) -import ast diff --git a/src/frontends/gamespy/library/extentions/string_extentions.py b/src/frontends/gamespy/library/extentions/string_extentions.py index 6d84bf2b5..1cbc9317b 100644 --- a/src/frontends/gamespy/library/extentions/string_extentions.py +++ b/src/frontends/gamespy/library/extentions/string_extentions.py @@ -1,5 +1,3 @@ -import socket -import struct from typing import Literal diff --git a/src/frontends/gamespy/protocols/chat/applications/client.py b/src/frontends/gamespy/protocols/chat/applications/client.py index a550f40aa..dcb2e43cb 100644 --- a/src/frontends/gamespy/protocols/chat/applications/client.py +++ b/src/frontends/gamespy/protocols/chat/applications/client.py @@ -8,7 +8,6 @@ from typing import Optional -from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage class ClientInfo: diff --git a/src/frontends/gamespy/protocols/chat/applications/server_launcher.py b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py index dbfc8959f..c9b524a04 100644 --- a/src/frontends/gamespy/protocols/chat/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py @@ -1,6 +1,6 @@ from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase from frontends.gamespy.library.network.tcp_handler import TcpServer -from frontends.gamespy.library.configs import CONFIG, ServerConfig +from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.protocols.chat.applications.client import Client diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/router.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/router.py index a2062c0ba..39ef06038 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/router.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/router.py @@ -5,6 +5,6 @@ app = FastAPI() -@app.post(f"/GetNatNegotiationInfo") +@app.post("/GetNatNegotiationInfo") def get_natneg_info(request: InitPacketInfo): data = request.json diff --git a/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py b/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py index f3f5987c7..c777f6e5c 100644 --- a/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py @@ -1,5 +1,3 @@ -import abc -import socket from typing import Optional import frontends.gamespy.library.abstractions.contracts as lib from frontends.gamespy.protocols.natneg.aggregations.enums import ( diff --git a/src/frontends/gamespy/protocols/natneg/applications/client.py b/src/frontends/gamespy/protocols/natneg/applications/client.py index 27b364d0e..9bcee0430 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/client.py +++ b/src/frontends/gamespy/protocols/natneg/applications/client.py @@ -13,6 +13,6 @@ def __init__(self, connection: UdpConnection, server_config: ServerConfig, logge def _create_switcher(self, buffer: bytes): assert isinstance(buffer, bytes) - from frontends.gamespy.protocols.natneg.applications.switcher import CmdSwitcher + from frontends.gamespy.protocols.natneg.applications.switcher import Switcher - return CmdSwitcher(self, buffer) + return Switcher(self, buffer) diff --git a/src/frontends/gamespy/protocols/natneg/applications/switcher.py b/src/frontends/gamespy/protocols/natneg/applications/switcher.py index c04fc1d60..612f79142 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/switcher.py +++ b/src/frontends/gamespy/protocols/natneg/applications/switcher.py @@ -21,7 +21,7 @@ ) -class CmdSwitcher(SwitcherBase): +class Switcher(SwitcherBase): _raw_request: bytes _client: Client diff --git a/src/frontends/gamespy/protocols/natneg/contracts/requests.py b/src/frontends/gamespy/protocols/natneg/contracts/requests.py index 662fc79d3..0b3d10928 100644 --- a/src/frontends/gamespy/protocols/natneg/contracts/requests.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/requests.py @@ -1,8 +1,12 @@ from socket import inet_ntoa import struct +from typing import Optional # from frontends.gamespy.library.extentions.string_extentions import IPEndPoint -from frontends.gamespy.protocols.natneg.abstractions.contracts import CommonRequestBase, RequestBase +from frontends.gamespy.protocols.natneg.abstractions.contracts import ( + CommonRequestBase, + RequestBase, +) from frontends.gamespy.protocols.natneg.aggregations.enums import ( NatClientIndex, NatPortMappingScheme, @@ -42,10 +46,14 @@ class ErtAckRequest(CommonRequestBase): class InitRequest(CommonRequestBase): - game_name: str + game_name: Optional[str] private_ip: str private_port: int + def __init__(self, raw_request: bytes | None = None): + super().__init__(raw_request) + self.game_name = "unknown" + def parse(self) -> None: super().parse() ip_bytes = self.raw_request[15:19] @@ -96,4 +104,4 @@ def parse(self): self.mapping_scheme = NatPortMappingScheme(self.raw_request[17]) end_index = self.raw_request[23:].index(0) - self.game_name = self.raw_request[23: 23 + end_index].decode("ascii") + self.game_name = self.raw_request[23 : 23 + end_index].decode("ascii") diff --git a/src/frontends/gamespy/protocols/natneg/contracts/results.py b/src/frontends/gamespy/protocols/natneg/contracts/results.py index 5c608091a..80bac7726 100644 --- a/src/frontends/gamespy/protocols/natneg/contracts/results.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/results.py @@ -1,4 +1,3 @@ -from typing import Any from frontends.gamespy.protocols.natneg.abstractions.contracts import CommonResultBase, ResultBase from frontends.gamespy.protocols.natneg.aggregations.enums import ( ConnectPacketStatus, diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py b/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py index fd2c7752d..eac5d3c21 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py @@ -1,4 +1,3 @@ -import abc from frontends.gamespy.library.extentions.gamespy_utils import convert_to_key_value from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import ( diff --git a/src/frontends/gamespy/protocols/presence_search_player/applications/client.py b/src/frontends/gamespy/protocols/presence_search_player/applications/client.py index a37234fae..1e48f0830 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/applications/client.py +++ b/src/frontends/gamespy/protocols/presence_search_player/applications/client.py @@ -13,6 +13,6 @@ def __init__(self, connection: TcpConnection, server_config: ServerConfig, logge super().__init__(connection, server_config, logger) def _create_switcher(self, buffer: bytes) -> SwitcherBase: - from frontends.gamespy.protocols.presence_search_player.applications.switcher import CmdSwitcher + from frontends.gamespy.protocols.presence_search_player.applications.switcher import Switcher temp_buffer = buffer.decode() - return CmdSwitcher(self, temp_buffer) + return Switcher(self, temp_buffer) diff --git a/src/frontends/gamespy/protocols/presence_search_player/applications/switcher.py b/src/frontends/gamespy/protocols/presence_search_player/applications/switcher.py index 4829f927c..9b95dffc6 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/applications/switcher.py +++ b/src/frontends/gamespy/protocols/presence_search_player/applications/switcher.py @@ -10,7 +10,7 @@ from frontends.gamespy.protocols.presence_search_player.applications.client import Client -class CmdSwitcher(SwitcherBase): +class Switcher(SwitcherBase): _raw_request: str def __init__(self, client: Client, raw_request: str): diff --git a/src/frontends/gamespy/protocols/presence_search_player/contracts/requests.py b/src/frontends/gamespy/protocols/presence_search_player/contracts/requests.py index b75baaaee..5d7a54917 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/contracts/requests.py +++ b/src/frontends/gamespy/protocols/presence_search_player/contracts/requests.py @@ -1,7 +1,11 @@ from frontends.gamespy.library.extentions.gamespy_utils import is_email_format_correct from frontends.gamespy.library.extentions.password_encoder import process_password -from frontends.gamespy.protocols.presence_search_player.abstractions.contracts import RequestBase -from frontends.gamespy.protocols.presence_search_player.aggregates.enums import SearchType +from frontends.gamespy.protocols.presence_search_player.abstractions.contracts import ( + RequestBase, +) +from frontends.gamespy.protocols.presence_search_player.aggregates.enums import ( + SearchType, +) from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import ( GPParseException, ) @@ -29,11 +33,12 @@ def parse(self): if "partner_id" in self.request_dict.keys(): try: self.partner_id = int(self.request_dict["partner_id"]) - except: + except Exception: raise GPParseException( - "no partner id found, check whether need implement the default partnerid") + "no partner id found, check whether need implement the default partnerid" + ) else: - self.partner_id = 0 + self.partner_id = 1 class NewUserRequest(RequestBase): diff --git a/src/frontends/gamespy/protocols/query_report/applications/client.py b/src/frontends/gamespy/protocols/query_report/applications/client.py index 198650f4f..e07bad46e 100644 --- a/src/frontends/gamespy/protocols/query_report/applications/client.py +++ b/src/frontends/gamespy/protocols/query_report/applications/client.py @@ -8,7 +8,7 @@ class Client(ClientBase): is_log_raw: bool = True def _create_switcher(self, buffer: bytes): - from frontends.gamespy.protocols.query_report.v2.applications.switcher import CmdSwitcher as V2CmdSwitcher + from frontends.gamespy.protocols.query_report.v2.applications.switcher import Switcher as V2CmdSwitcher assert isinstance(buffer, bytes) if buffer[0] == ord("\\"): raise NotImplementedError("v1 protocol not implemented") diff --git a/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py b/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py index 3b7757248..8df9f2cb8 100644 --- a/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py @@ -1,5 +1,4 @@ import frontends.gamespy.library.abstractions.contracts as lib -import abc from frontends.gamespy.library.extentions.gamespy_utils import convert_to_key_value diff --git a/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py b/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py index fccf367f9..fba830d15 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py +++ b/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py @@ -8,7 +8,6 @@ AvaliableRequest, ChallengeRequest, ClientMessageAckRequest, - ClientMessageRequest, EchoRequest, HeartBeatRequest, KeepAliveRequest, @@ -24,7 +23,7 @@ ) -class CmdSwitcher(SwitcherBase): +class Switcher(SwitcherBase): _raw_request: bytes def _process_raw_request(self) -> None: diff --git a/src/frontends/gamespy/protocols/server_browser/v2/abstractions/handlers.py b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/handlers.py index e55db0f53..79480b7e2 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/abstractions/handlers.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/handlers.py @@ -1,5 +1,4 @@ from frontends.gamespy.library.abstractions.handler import CmdHandlerBase as CMB -import abc from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import ( ServerListUpdateOptionRequestBase, diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/client.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/client.py index 881da507f..ff8b37f21 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/client.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/client.py @@ -19,5 +19,5 @@ class Client(ClientBase): crypto: EncryptBase def _create_switcher(self, buffer: bytes) -> "SwitcherBase": - from frontends.gamespy.protocols.server_browser.v2.applications.switcher import CmdSwitcher - return CmdSwitcher(self, buffer) + from frontends.gamespy.protocols.server_browser.v2.applications.switcher import Switcher + return Switcher(self, buffer) diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py index 5bf3301aa..ae2e264cd 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py @@ -1,10 +1,21 @@ from concurrent.futures import ProcessPoolExecutor from typing import TYPE_CHECKING, cast -from frontends.gamespy.protocols.query_report.aggregates.game_server_info import GameServerInfo -from frontends.gamespy.protocols.query_report.v2.contracts.requests import ClientMessageRequest -from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus, RequestType -from frontends.gamespy.protocols.server_browser.aggregates.exceptions import ServerBrowserException -from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.query_report.aggregates.game_server_info import ( + GameServerInfo, +) +from frontends.gamespy.protocols.query_report.v2.contracts.requests import ( + ClientMessageRequest, +) +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import ( + GameServerStatus, + RequestType, +) +from frontends.gamespy.protocols.server_browser.aggregates.exceptions import ( + ServerBrowserException, +) +from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import ( + RequestBase, +) from frontends.gamespy.protocols.server_browser.v2.contracts.requests import ( SendMessageRequest, ServerInfoRequest, @@ -18,6 +29,7 @@ UpdateServerInfoResponse, ) from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( + P2PGroupRoomListResult, ServerInfoResult, ServerMainListResult, ) @@ -25,7 +37,9 @@ # RequestType, ServerListUpdateOption, ) -from frontends.gamespy.protocols.server_browser.v2.abstractions.handlers import CmdHandlerBase +from frontends.gamespy.protocols.server_browser.v2.abstractions.handlers import ( + CmdHandlerBase, +) from frontends.gamespy.protocols.server_browser.v2.applications.client import Client @@ -53,12 +67,12 @@ def __init__(self, message: GameServerInfo) -> None: def handle(self) -> None: result = ServerInfoResult(game_server_info=self._message) - match (self._message.status): - case ( - status - ) if status == GameServerStatus.NORMAL \ - or status == GameServerStatus.UPDATE \ - or status == GameServerStatus.PLAYING: + match self._message.status: + case status if ( + status == GameServerStatus.NORMAL + or status == GameServerStatus.UPDATE + or status == GameServerStatus.PLAYING + ): self.response = UpdateServerInfoResponse(result) case GameServerStatus.SHUTDOWN: self.response = DeleteServerInfoResponse(result) @@ -76,12 +90,11 @@ def send_message(self, client: Client): and client.crypto is not None and ( client.info.search_type == ServerListUpdateOption.SERVER_MAIN_LIST - or client.info.search_type == ServerListUpdateOption.P2P_SERVER_MAIN_LIST + or client.info.search_type + == ServerListUpdateOption.P2P_SERVER_MAIN_LIST ) ): - client.log_info( - f"Sending AdHoc message {self._message.status} to client" - ) + client.log_info(f"Sending AdHoc message {self._message.status} to client") client.send(self.response) @@ -127,11 +140,25 @@ def _response_construct(self) -> None: class ServerListHandler(CmdHandlerBase): _request: ServerListRequest _result: ServerMainListResult - _result_cls: type[ServerMainListResult] - - def __init__(self, client: Client, request: RequestBase) -> None: - super().__init__(client, request) - self._result_cls = ServerMainListResult + _result_cls: ( + type[ServerMainListResult] + | type[P2PGroupRoomListResult] + | type[ServerInfoResult] + ) + + def _request_check(self) -> None: + super()._request_check() + match self._request.update_option: + case option if option in [ + ServerListUpdateOption.SERVER_MAIN_LIST, + ServerListUpdateOption.P2P_SERVER_MAIN_LIST, + ServerListUpdateOption.LIMIT_RESULT_COUNT, + ]: + self._result_cls = ServerMainListResult + case ServerListUpdateOption.P2P_GROUP_ROOM_LIST: + self._result_cls = P2PGroupRoomListResult + case _: + raise ServerBrowserException("unknown serverlist update option type") def _response_construct(self) -> None: match self._request.update_option: @@ -141,15 +168,12 @@ def _response_construct(self) -> None: ServerListUpdateOption.LIMIT_RESULT_COUNT, ServerListUpdateOption.SERVER_FULL_INFO_LIST, ]: - self._response = ServerMainListResponse( - self._request, self._result) + self._response = ServerMainListResponse(self._request, self._result) case ServerListUpdateOption.P2P_GROUP_ROOM_LIST: - self._response = P2PGroupRoomListResponse( - self._request, self._result) + self._response = P2PGroupRoomListResponse(self._request, self._result) case ServerListUpdateOption.SERVER_FULL_INFO_LIST: self._response = ServerNetworkInfoListResponse( self._request, self._result ) case _: - raise ServerBrowserException( - "unknown serverlist update option type") + raise ServerBrowserException("unknown serverlist update option type") diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py index 6366ca197..94ce9ef55 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py @@ -8,7 +8,7 @@ from frontends.gamespy.protocols.server_browser.v2.contracts.requests import SendMessageRequest, ServerInfoRequest, ServerListRequest -class CmdSwitcher(SwitcherBase): +class Switcher(SwitcherBase): _raw_request: bytes def _process_raw_request(self) -> None: diff --git a/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py index c2ab8d473..14a6e1a31 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py @@ -6,7 +6,7 @@ from frontends.gamespy.protocols.server_browser.v2.aggregations.server_info_builder import ( build_server_info_header, ) -from frontends.gamespy.protocols.server_browser.v2.aggregations.string_flags import * +from frontends.gamespy.protocols.server_browser.v2.aggregations.string_flags import ALL_SERVER_END_FLAG, NTS_STRING_FLAG, STRING_SPLITER from frontends.gamespy.protocols.server_browser.v2.contracts.requests import ServerListRequest from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( P2PGroupRoomListResult, diff --git a/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py index 6383d49c4..d02c9b719 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py @@ -1,10 +1,7 @@ -from datetime import datetime from uuid import UUID -from pydantic import BaseModel from frontends.gamespy.protocols.query_report.aggregates.game_server_info import GameServerInfo from frontends.gamespy.protocols.query_report.aggregates.peer_room_info import PeerRoomInfo -from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import ( AdHocResultBase, ResultBase, diff --git a/src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py b/src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py index 87252a6d1..ba77d982e 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py @@ -1,5 +1,4 @@ from frontends.gamespy.protocols.web_services.abstractions.handler import CmdHandlerBase -from frontends.gamespy.protocols.web_services.modules.auth.abstractions.general import LoginResultBase from frontends.gamespy.protocols.web_services.modules.auth.contracts.requests import ( LoginProfileRequest, LoginProfileWithGameIdRequest, diff --git a/src/frontends/gamespy/protocols/web_services/modules/direct2game/handlers/general.py b/src/frontends/gamespy/protocols/web_services/modules/direct2game/handlers/general.py index 15057f678..990a7139a 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/direct2game/handlers/general.py +++ b/src/frontends/gamespy/protocols/web_services/modules/direct2game/handlers/general.py @@ -1,4 +1,3 @@ -from frontends.gamespy.protocols.web_services.abstractions.contracts import RequestBase from frontends.gamespy.protocols.web_services.abstractions.handler import CmdHandlerBase from frontends.gamespy.protocols.web_services.applications.client import Client from frontends.gamespy.protocols.web_services.modules.direct2game.contracts.requests import ( diff --git a/src/frontends/gamespy/protocols/web_services/modules/sake/contracts/requests.py b/src/frontends/gamespy/protocols/web_services/modules/sake/contracts/requests.py index 92352935a..4fc16904b 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/sake/contracts/requests.py +++ b/src/frontends/gamespy/protocols/web_services/modules/sake/contracts/requests.py @@ -1,4 +1,3 @@ -from copy import copy, deepcopy from typing import Optional import xml.etree.ElementTree as ET diff --git a/src/frontends/tests/gamespy/library/encrypt_tests.py b/src/frontends/tests/gamespy/library/encrypt_tests.py index c9d75f37b..a3b743eb3 100644 --- a/src/frontends/tests/gamespy/library/encrypt_tests.py +++ b/src/frontends/tests/gamespy/library/encrypt_tests.py @@ -1,7 +1,6 @@ import unittest from frontends.gamespy.library.encryption.gs_encryption import ChatCrypt from frontends.gamespy.library.encryption.xor_encryption import XorEncoding, XorType -from frontends.gamespy.protocols.game_status.aggregations.gscrypt import GSCrypt class EncryptionTest(unittest.TestCase): diff --git a/src/frontends/tests/gamespy/natneg/handler_tests.py b/src/frontends/tests/gamespy/natneg/handler_tests.py index dd4c578ee..4ab8520c9 100644 --- a/src/frontends/tests/gamespy/natneg/handler_tests.py +++ b/src/frontends/tests/gamespy/natneg/handler_tests.py @@ -37,8 +37,7 @@ def test_init(self): # test request parsing request = InitRequest(raw) request.parse() - cookie = 151191552 - self.assertEqual(cookie, request.cookie) + self.assertEqual(151191552, request.cookie) self.assertEqual(RequestType.INIT, request.command_name) self.assertEqual(NatClientIndex.GAME_CLIENT, request.client_index) self.assertEqual(False, request.use_game_port) diff --git a/src/frontends/tests/gamespy/natneg/mock_objects.py b/src/frontends/tests/gamespy/natneg/mock_objects.py index 1135d3e70..c90697013 100644 --- a/src/frontends/tests/gamespy/natneg/mock_objects.py +++ b/src/frontends/tests/gamespy/natneg/mock_objects.py @@ -14,7 +14,6 @@ InitHandler, NatifyHandler, ) -from frontends.gamespy.library.exceptions.general import UniSpyException class ClientMock(Client): diff --git a/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py b/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py index b7deb8990..af4f15c60 100644 --- a/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py +++ b/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py @@ -1,6 +1,5 @@ from typing import cast from frontends.gamespy.library.configs import CONFIG -from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.tests.gamespy.library.mock_objects import ( ConnectionMock, LogMock, diff --git a/src/frontends/tests/gamespy/presence_connection_manager/request_tests.py b/src/frontends/tests/gamespy/presence_connection_manager/request_tests.py index d84e80eb8..6ae8e52bf 100644 --- a/src/frontends/tests/gamespy/presence_connection_manager/request_tests.py +++ b/src/frontends/tests/gamespy/presence_connection_manager/request_tests.py @@ -37,7 +37,7 @@ DEL_BUDDY = "\\delbuddy\\\\sesskey\\0\\delprofileid\\0\\final\\" INVITE_TO = "\\inviteto\\\\sesskey\\0\\productid\\0\\profileid\\0\\final\\" STATUS = [ - "\\status\\0\\statstring\\test\\locstring\\test\\final\\", + "\\status\\0\\sesskey\\1111\\statstring\\test\\locstring\\test\\final\\", "\\status\\1\\sesskey\\1111\\statstring\\Not Ready\\locstring\\gptestc\\final\\", ] diff --git a/src/frontends/tests/gamespy/presence_search_player/game_tests.py b/src/frontends/tests/gamespy/presence_search_player/game_tests.py index 1920bc444..fad562804 100644 --- a/src/frontends/tests/gamespy/presence_search_player/game_tests.py +++ b/src/frontends/tests/gamespy/presence_search_player/game_tests.py @@ -2,8 +2,7 @@ import unittest from frontends.gamespy.protocols.presence_search_player.contracts.requests import CheckRequest -from frontends.gamespy.protocols.presence_search_player.applications.handlers import CheckHandler -from frontends.gamespy.protocols.presence_search_player.applications.switcher import CmdSwitcher +from frontends.gamespy.protocols.presence_search_player.applications.switcher import Switcher import responses from frontends.gamespy.protocols.presence_search_player.contracts.responses import CheckResponse @@ -16,7 +15,7 @@ def test_check(self): raw = "\\check\\\\nick\\spyguy\\email\\spyguy@gamespy.com\\pass\\0000\\final\\" client = create_client() - switcher = CmdSwitcher(client, raw) + switcher = Switcher(client, raw) switcher.handle() request = switcher._handlers[0]._request if TYPE_CHECKING: diff --git a/src/frontends/tests/gamespy/presence_search_player/mock_objects.py b/src/frontends/tests/gamespy/presence_search_player/mock_objects.py index 46c8962fc..83dfbeec0 100644 --- a/src/frontends/tests/gamespy/presence_search_player/mock_objects.py +++ b/src/frontends/tests/gamespy/presence_search_player/mock_objects.py @@ -5,7 +5,6 @@ from frontends.gamespy.protocols.presence_search_player.applications.handlers import CheckHandler, SearchHandler from frontends.gamespy.protocols.presence_search_player.contracts.results import CheckResult, SearchResult -from frontends.gamespy.library.exceptions.general import UniSpyException class ClientMock(Client): pass diff --git a/src/frontends/tests/gamespy/query_report/mock_objects.py b/src/frontends/tests/gamespy/query_report/mock_objects.py index 037335d52..ed8a62d02 100644 --- a/src/frontends/tests/gamespy/query_report/mock_objects.py +++ b/src/frontends/tests/gamespy/query_report/mock_objects.py @@ -4,7 +4,6 @@ from frontends.gamespy.protocols.query_report.v2.applications.handlers import AvailableHandler, HeartBeatHandler, KeepAliveHandler from frontends.gamespy.protocols.query_report.v2.contracts.results import HeartBeatResult from frontends.tests.gamespy.library.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url -from frontends.gamespy.library.exceptions.general import UniSpyException class ClientMock(Client): diff --git a/src/frontends/tests/gamespy/server_browser/game_tests.py b/src/frontends/tests/gamespy/server_browser/game_tests.py index 8201dac66..fdfb33060 100644 --- a/src/frontends/tests/gamespy/server_browser/game_tests.py +++ b/src/frontends/tests/gamespy/server_browser/game_tests.py @@ -8,10 +8,10 @@ class GameTest(unittest.TestCase): @responses.activate def test_gmtest_20200309(self): - qr_raw = b'\x03\xea+\xafPlocalip0\x00192.168.122.226\x00localport\x0011111\x00natneg\x001\x00statechanged\x003\x00gamename\x00gmtest\x00hostname\x00GameSpy QR2 Sample\x00gamever\x002.00\x00hostport\x0025000\x00mapname\x00gmtmap1\x00gametype\x00arena\x00numplayers\x0010\x00numteams\x002\x00maxplayers\x0032\x00gamemode\x00openplaying\x00teamplay\x001\x00fraglimit\x000\x00timelimit\x0040\x00gravity\x00800\x00rankingon\x001\x00\x00\x00\nplayer_\x00score_\x00deaths_\x00ping_\x00team_\x00time_\x00\x00Joe Player\x004\x002\x0077\x000\x00185\x00L33t 0n3\x006\x0024\x0068\x001\x00820\x00Raptor\x0010\x0029\x00216\x001\x00664\x00Gr81\x008\x006\x00327\x001\x00697\x00Flubber\x0015\x002\x00179\x000\x0048\x00Sarge\x009\x0012\x00337\x000\x00296\x00Void\x0027\x0029\x0045\x000\x00355\x00runaway\x0024\x004\x00197\x001\x00428\x00Ph3ar\x0030\x0030\x00339\x001\x00525\x00wh00t\x0031\x0028\x00269\x001\x0077\x00\x00\x02team_t\x00score_t\x00avgping_t\x00\x00Red\x00487\x00336\x00Blue\x0082\x00458\x00' + qr_raw = b"\x03\xea+\xafPlocalip0\x00192.168.122.226\x00localport\x0011111\x00natneg\x001\x00statechanged\x003\x00gamename\x00gmtest\x00hostname\x00GameSpy QR2 Sample\x00gamever\x002.00\x00hostport\x0025000\x00mapname\x00gmtmap1\x00gametype\x00arena\x00numplayers\x0010\x00numteams\x002\x00maxplayers\x0032\x00gamemode\x00openplaying\x00teamplay\x001\x00fraglimit\x000\x00timelimit\x0040\x00gravity\x00800\x00rankingon\x001\x00\x00\x00\nplayer_\x00score_\x00deaths_\x00ping_\x00team_\x00time_\x00\x00Joe Player\x004\x002\x0077\x000\x00185\x00L33t 0n3\x006\x0024\x0068\x001\x00820\x00Raptor\x0010\x0029\x00216\x001\x00664\x00Gr81\x008\x006\x00327\x001\x00697\x00Flubber\x0015\x002\x00179\x000\x0048\x00Sarge\x009\x0012\x00337\x000\x00296\x00Void\x0027\x0029\x0045\x000\x00355\x00runaway\x0024\x004\x00197\x001\x00428\x00Ph3ar\x0030\x0030\x00339\x001\x00525\x00wh00t\x0031\x0028\x00269\x001\x0077\x00\x00\x02team_t\x00score_t\x00avgping_t\x00\x00Red\x00487\x00336\x00Blue\x0082\x00458\x00" qr_client = create_client() qr_client.on_received(qr_raw) - sb_raw = b'\x00\t\x01\xc0\xa8z\xe2+g' + sb_raw = b"\x00\t\x01\xc0\xa8z\xe2+g" sb_client = create_v2_client() sb_client.on_received(sb_raw) @@ -19,25 +19,25 @@ def test_gmtest_20200309(self): def test_anno1701_20220620(self): qr_raws = [ # available request - b'\t\x00\x00\x00\x00anno1701\x00', + b"\t\x00\x00\x00\x00anno1701\x00", b"\x03\x1dU\xcc\xcalocalip0\x00192.168.0.80\x00localport\x0021701\x00natneg\x001\x00statechanged\x003\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00377563076\x00numaiplayers\x00\x00openslots\x00\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00anno1701_220\x000\x000\x00\x00\x01\x00", b"\x03\x1dU\xcc\xcalocalip0\x00192.168.0.80\x00localport\x0021701\x00natneg\x001\x00statechanged\x003\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00377563076\x00numaiplayers\x00\x00openslots\x00\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00anno1701_220\x000\x000\x00\x00\x01\x00", - b'\x03\x1dU\xcc\xcalocalip0\x00192.168.0.80\x00localport\x0021701\x00natneg\x001\x00statechanged\x001\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00109127620\x00numaiplayers\x000\x00openslots\x004\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00anno1701_220\x000\x000\x00\x00\x01\x00', b"\x03\x1dU\xcc\xcalocalip0\x00192.168.0.80\x00localport\x0021701\x00natneg\x001\x00statechanged\x001\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00109127620\x00numaiplayers\x000\x00openslots\x004\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00anno1701_220\x000\x000\x00\x00\x01\x00", + b"\x03\x1dU\xcc\xcalocalip0\x00192.168.0.80\x00localport\x0021701\x00natneg\x001\x00statechanged\x001\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00109127620\x00numaiplayers\x000\x00openslots\x004\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00anno1701_220\x000\x000\x00\x00\x01\x00", + b"\x03\x1dU\xcc\xcalocalip0\x00192.168.0.80\x00localport\x0021701\x00natneg\x001\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00109127620\x00numaiplayers\x000\x00openslots\x004\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00anno1701_220\x000\x000\x00\x00\x01\x00", b"\x03\x1dU\xcc\xcalocalip0\x00192.168.0.80\x00localport\x0021701\x00natneg\x001\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00109127620\x00numaiplayers\x000\x00openslots\x004\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00anno1701_220\x000\x000\x00\x00\x01\x00", - b'\x03\x1dU\xcc\xcalocalip0\x00192.168.0.80\x00localport\x0021701\x00natneg\x001\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00109127620\x00numaiplayers\x000\x00openslots\x004\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00anno1701_220\x000\x000\x00\x00\x01\x00', b"\x03\x1dU\xcc\xcalocalip0\x00192.168.0.80\x00localport\x0021701\x00natneg\x001\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00109127620\x00numaiplayers\x000\x00openslots\x004\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00anno1701_220\x000\x000\x00\x00\x01\x00", # client message b"\xfe\xfd\x03\x1dU\xcc\xcaTTT\x00\x00[+2\xba\x00\x00\x00\x00\xc5T\x00\x00", # keep alive - b"\x08\x1dU\xcc\xca" + b"\x08\x1dU\xcc\xca", ] sb_raws = [ b"\x00\x9a\x00\x01\x03\x8fU\x00\x00anno1701\x00anno1701\x00D:@o)Okhgroupid is null\x00\\hostname\\gamemode\\gamever\\gametype\\password\\mapname\\numplayers\\numaiplayers\\openslots\\gamevariant\x00\x00\x00\x00\x04", b"\x00\x9a\x00\x01\x03\x8fU\x00\x00anno1701\x00anno1701\x00AHl='lhIgroupid is null\x00\\hostname\\gamemode\\gamever\\gametype\\password\\mapname\\numplayers\\numaiplayers\\openslots\\gamevariant\x00\x00\x00\x00\x04", b"\x00\x9a\x00\x01\x03\x8fU\x00\x00anno1701\x00anno1701\x00TsFhHjvQgroupid is null\x00\\hostname\\gamemode\\gamever\\gametype\\password\\mapname\\numplayers\\numaiplayers\\openslots\\gamevariant\x00\x00\x00\x00\x04", b"\x00\t\x01[+2\xbaT\xc5", - b"\xfd\xfc\x1efj\xb2\x00\x00\x171" + b"\xfd\xfc\x1efj\xb2\x00\x00\x171", ] qr_client = create_client() @@ -48,16 +48,17 @@ def test_anno1701_20220620(self): for raw in sb_raws: sb_client.on_received(raw) + @responses.activate def test_anno1701_20221104(self): - qr_raw = {"qr1": - b"\t\x00\x00\x00\x00anno1701\x00", - "qr2": b"\x03\x98\x92%\xa0localip0\x00192.168.0.50\x00localip1\x00192.168.122.1\x00localport\x0021701\x00natneg\x001\x00statechanged\x003\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00369174468\x00numaiplayers\x00\x00openslots\x00\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00sporesirius\x000\x000\x00\x00\x01\x00", - "sb1": b"\x00\x9a\x00\x01\x03\x8fU\x00\x00anno1701\x00anno1701\x00RcX;M({Ggroupid is null\x00\\hostname\\gamemode\\gamever\\gametype\\password\\mapname\\numplayers\\numaiplayers\\openslots\\gamevariant\x00\x00\x00\x00\x04", - "qr3": b"\x07\x98\x92%\xa0\x00\x00\x00\x00" - } + qr_raw = { + "qr1": b"\t\x00\x00\x00\x00anno1701\x00", + "qr2": b"\x03\x98\x92%\xa0localip0\x00192.168.0.50\x00localip1\x00192.168.122.1\x00localport\x0021701\x00natneg\x001\x00statechanged\x003\x00gamename\x00anno1701\x00publicip\x000\x00publicport\x000\x00hostname\x00(unknown game)\x00gamemode\x00openstaging\x00numplayers\x001\x00maxplayers\x004\x00gamever\x0021903\x00mapname\x00Random map\x00gametype\x00Easy\x00password\x000\x00settings_options\x00369174468\x00numaiplayers\x00\x00openslots\x00\x00gamevariant\x00PvP\x00settings_winconditions\x000\x00settings_usercontent_mapname\x00\x00\x00\x00\x01player_\x00ping_\x00ping_\x00\x00sporesirius\x000\x000\x00\x00\x01\x00", + "sb1": b"\x00\x9a\x00\x01\x03\x8fU\x00\x00anno1701\x00anno1701\x00RcX;M({Ggroupid is null\x00\\hostname\\gamemode\\gamever\\gametype\\password\\mapname\\numplayers\\numaiplayers\\openslots\\gamevariant\x00\x00\x00\x00\x04", + "qr3": b"\x07\x98\x92%\xa0\x00\x00\x00\x00", + } @responses.activate def test_aarts_20230618(self): - raw = b"\x00\xb8\x00\x01\x03\x00\x00\x00\x00aarts\x00aarts\x00F|Cy9!&w\x00\\hostname\\gamemode\\hostport\\hostname\\gamename\\gametype\\gamever\\mapname\\numplayers\\maxplayers\\gamemode\\password\\groupid\\mapsessiontype\\mapids\\internet\x00\x00\x00\x00\x04" + sb_raw = b"\x00\xb8\x00\x01\x03\x00\x00\x00\x00aarts\x00aarts\x00F|Cy9!&w\x00\\hostname\\gamemode\\hostport\\hostname\\gamename\\gametype\\gamever\\mapname\\numplayers\\maxplayers\\gamemode\\password\\groupid\\mapsessiontype\\mapids\\internet\x00\x00\x00\x00\x04" client = create_v2_client() - client.on_received(raw) + client.on_received(sb_raw) diff --git a/src/frontends/tests/gamespy/server_browser/mock_objects.py b/src/frontends/tests/gamespy/server_browser/mock_objects.py index a2207e652..5611f7b05 100644 --- a/src/frontends/tests/gamespy/server_browser/mock_objects.py +++ b/src/frontends/tests/gamespy/server_browser/mock_objects.py @@ -1,10 +1,20 @@ from typing import cast from frontends.gamespy.library.configs import CONFIG -from frontends.tests.gamespy.library.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url +from frontends.tests.gamespy.library.mock_objects import ( + ConnectionMock, + LogMock, + RequestHandlerMock, + create_mock_url, +) from frontends.gamespy.protocols.server_browser.v2.applications.client import Client -from frontends.gamespy.protocols.server_browser.v2.applications.handlers import ServerInfoHandler, ServerListHandler -from frontends.gamespy.protocols.server_browser.v2.contracts.results import ServerInfoResult, ServerMainListResult -import json +from frontends.gamespy.protocols.server_browser.v2.applications.handlers import ( + ServerInfoHandler, + ServerListHandler, +) +from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( + ServerInfoResult, + ServerMainListResult, +) class ClientMock(Client): @@ -16,15 +26,54 @@ def create_v2_client() -> Client: logger = LogMock() conn = ConnectionMock( handler=handler, - config=CONFIG.servers["ServerBrowserV2"], t_client=ClientMock, - logger=logger) + config=CONFIG.servers["ServerBrowserV2"], + t_client=ClientMock, + logger=logger, + ) config = CONFIG.servers["ServerBrowserV2"] - create_mock_url(config, ServerListHandler, - ServerMainListResult.model_validate({"client_remote_ip": "127.0.0.1", "flag": 64, - "game_secret_key": "123567", "servers_info": []}).model_dump(mode="json")) - create_mock_url(config, ServerInfoHandler, ServerInfoResult.model_validate({"game_server_info": {"server_id": "550e8400-e29b-41d4-a716-446655440000", "host_ip_address": "192.168.1.1", "instant_key": "123456", "game_name": "Example Game", "query_report_port": 8080, "last_heart_beat_received_time": "2023-10-01T12:00:00Z", "status": 3, "server_data": { - "max_players": "100", "current_players": "50", "region": "US-East"}, "player_data": [{"player_id": "player1", "player_name": "Player One"}, {"player_id": "player2", "player_name": "Player Two"}], "team_data": [{"team_id": "team1", "team_name": "Team Alpha"}, {"team_id": "team2", "team_name": "Team Beta"}]}}).model_dump(mode="json")) + create_mock_url( + config, + ServerListHandler, + ServerMainListResult.model_validate( + { + "client_remote_ip": "127.0.0.1", + "flag": 64, + "game_secret_key": "123567", + "servers_info": [], + } + ).model_dump(mode="json"), + ) + create_mock_url( + config, + ServerInfoHandler, + ServerInfoResult.model_validate( + { + "game_server_info": { + "server_id": "550e8400-e29b-41d4-a716-446655440000", + "host_ip_address": "192.168.1.1", + "instant_key": "123456", + "game_name": "Example Game", + "query_report_port": 8080, + "last_heart_beat_received_time": "2023-10-01T12:00:00Z", + "status": 3, + "server_data": { + "max_players": "100", + "current_players": "50", + "region": "US-East", + }, + "player_data": [ + {"player_id": "player1", "player_name": "Player One"}, + {"player_id": "player2", "player_name": "Player Two"}, + ], + "team_data": [ + {"team_id": "team1", "team_name": "Team Alpha"}, + {"team_id": "team2", "team_name": "Team Beta"}, + ], + } + } + ).model_dump(mode="json"), + ) return cast(Client, conn._client) @@ -34,10 +83,17 @@ def create_v1_client() -> Client: logger = LogMock() conn = ConnectionMock( handler=handler, - config=CONFIG.servers["ServerBrowserV1"], t_client=ClientMock, - logger=logger) + config=CONFIG.servers["ServerBrowserV1"], + t_client=ClientMock, + logger=logger, + ) config = CONFIG.servers["ServerBrowserV1"] - create_mock_url(config, ServerListHandler, ServerMainListResult.model_validate( - {"remote_ip_address": conn.remote_ip, "remote_port": conn.remote_port}).model_dump()) + create_mock_url( + config, + ServerListHandler, + ServerMainListResult.model_validate( + {"remote_ip_address": conn.remote_ip, "remote_port": conn.remote_port} + ).model_dump(), + ) return cast(Client, conn._client) From 27d18135d9d97bf93430ce318e1ee239a22831b9 Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Thu, 29 May 2025 08:04:29 +0000 Subject: [PATCH 187/231] Added NatNeg tests. --- .../protocols/gamespy/natneg/handlers.py | 3 + .../protocols/gamespy/natneg/requests.py | 2 - .../gamespy/server_browser/handlers.py | 97 ++++++++------- .../routers/gamespy/server_browser.py | 4 +- .../tests/gamespy/natneg/handler_tests.py | 23 +++- .../precence_search_player/handler_tests.py | 2 - .../gamespy/server_browser/handler_tests.py | 7 +- .../protocols/chat/applications/handlers.py | 4 + .../natneg/abstractions/contracts.py | 8 +- .../protocols/natneg/applications/handlers.py | 8 +- .../protocols/natneg/contracts/requests.py | 2 +- .../protocols/natneg/contracts/responses.py | 44 +++++-- .../v2/abstractions/contracts.py | 21 ++-- .../server_browser/v2/aggregations/enums.py | 2 +- .../v2/applications/handlers.py | 116 ++++++++++++------ .../v2/applications/switcher.py | 57 +++++++-- .../server_browser/v2/contracts/requests.py | 24 ++-- .../server_browser/v2/contracts/responses.py | 1 + .../server_browser/v2/contracts/results.py | 12 +- .../gamespy/server_browser/mock_objects.py | 6 +- 20 files changed, 305 insertions(+), 138 deletions(-) diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py index f2bf570f8..650718600 100644 --- a/src/backends/protocols/gamespy/natneg/handlers.py +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -35,3 +35,6 @@ class ConnectHandler(HandlerBase): def __init__(self, request: RequestBase) -> None: super().__init__(request) assert isinstance(request, ConnectRequest) + + def _data_operate(self) -> None: + raise NotImplementedError() diff --git a/src/backends/protocols/gamespy/natneg/requests.py b/src/backends/protocols/gamespy/natneg/requests.py index 3f9c63eda..9d85bcaec 100644 --- a/src/backends/protocols/gamespy/natneg/requests.py +++ b/src/backends/protocols/gamespy/natneg/requests.py @@ -37,8 +37,6 @@ class ConnectRequest(RequestBase): Server will send this request to client to let them connect to each other """ - client_index: NatClientIndex - class ErtAckRequest(CommonRequestBase): pass diff --git a/src/backends/protocols/gamespy/server_browser/handlers.py b/src/backends/protocols/gamespy/server_browser/handlers.py index 863a2fa05..6a82902a4 100644 --- a/src/backends/protocols/gamespy/server_browser/handlers.py +++ b/src/backends/protocols/gamespy/server_browser/handlers.py @@ -13,66 +13,81 @@ from frontends.gamespy.protocols.query_report.aggregates.peer_room_info import ( PeerRoomInfo, ) -from frontends.gamespy.protocols.server_browser.aggregates.exceptions import ( - ServerBrowserException, -) from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import ( GameServerFlags, - ServerListUpdateOption, ) from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( P2PGroupRoomListResult, SendMessageResult, ServerInfoResult, ServerMainListResult, + ServerNetworkInfoListResult, ) + # region Server list +class ServerNetworkInfoListHandler(HandlerBase): + _request: ServerListRequest + _caches: list[GameServerInfo] + + def _data_operate(self): + self._caches = data.get_server_info_list_with_game_name(self._request.game_name) + def _result_construct(self): + assert isinstance(self._caches, list) and all( + isinstance(item, GameServerInfo) for item in self._caches + ) + if TYPE_CHECKING: + self._caches = cast(list[GameServerInfo], self._caches) + self._result = ServerNetworkInfoListResult( + client_remote_ip=self._request.client_ip, + flag=GameServerFlags.HAS_KEYS_FLAG, + game_secret_key="", + servers_info=self._caches, + ) -class ServerListHandler(HandlerBase): + +class P2PGroupRoomListHandler(HandlerBase): _request: ServerListRequest - _caches: list[PeerRoomInfo] | list[GameServerInfo] + _caches: list[PeerRoomInfo] def _data_operate(self): - if self._request.update_option in ServerListUpdateOption: - self._caches = data.get_server_info_list_with_game_name( - self._request.game_name - ) + group_data = data.get_group_data_list_by_gamename(self._request.game_name) + self._caches = data.get_peer_group_channel(group_data) - elif self._request.update_option == ServerListUpdateOption.P2P_GROUP_ROOM_LIST: - group_data = data.get_group_data_list_by_gamename(self._request.game_name) - self._caches = data.get_peer_group_channel(group_data) - else: - raise ServerBrowserException("invalid server browser update option") + def _result_construct(self) -> None: + assert isinstance(self._caches, list) and all( + isinstance(item, PeerRoomInfo) for item in self._caches + ) + if TYPE_CHECKING: + self._caches = cast(list[PeerRoomInfo], self._caches) + self._result = P2PGroupRoomListResult( + client_remote_ip=self._request.client_ip, + flag=GameServerFlags.HAS_KEYS_FLAG, + game_secret_key="", + peer_room_info=self._caches, + ) + + +class ServerMainListHandler(HandlerBase): + _request: ServerListRequest + _caches: list[GameServerInfo] + + def _data_operate(self): + self._caches = data.get_server_info_list_with_game_name(self._request.game_name) def _result_construct(self): - if self._request.update_option in ServerListUpdateOption: - assert isinstance(self._caches, list) and all( - isinstance(item, GameServerInfo) for item in self._caches - ) - if TYPE_CHECKING: - self._caches = cast(list[GameServerInfo], self._caches) - self._result = ServerMainListResult( - client_remote_ip=self._request.client_ip, - flag=GameServerFlags.HAS_KEYS_FLAG, - game_secret_key="", - servers_info=self._caches, - ) - elif self._request.update_option == ServerListUpdateOption.P2P_GROUP_ROOM_LIST: - assert isinstance(self._caches, list) and all( - isinstance(item, PeerRoomInfo) for item in self._caches - ) - if TYPE_CHECKING: - self._caches = cast(list[PeerRoomInfo], self._caches) - self._result = P2PGroupRoomListResult( - client_remote_ip=self._request.client_ip, - flag=GameServerFlags.HAS_KEYS_FLAG, - game_secret_key="", - peer_room_info=self._caches, - ) - else: - raise ServerBrowserException("invalid server browser update option") + assert isinstance(self._caches, list) and all( + isinstance(item, GameServerInfo) for item in self._caches + ) + if TYPE_CHECKING: + self._caches = cast(list[GameServerInfo], self._caches) + self._result = ServerMainListResult( + client_remote_ip=self._request.client_ip, + flag=GameServerFlags.HAS_KEYS_FLAG, + game_secret_key="", + servers_info=self._caches, + ) # region Adhoc diff --git a/src/backends/routers/gamespy/server_browser.py b/src/backends/routers/gamespy/server_browser.py index e70f904f6..e31164e1f 100644 --- a/src/backends/routers/gamespy/server_browser.py +++ b/src/backends/routers/gamespy/server_browser.py @@ -1,5 +1,5 @@ from fastapi import APIRouter, WebSocket, WebSocketDisconnect -from backends.protocols.gamespy.server_browser.handlers import ServerInfoHandler, ServerListHandler +from backends.protocols.gamespy.server_browser.handlers import ServerInfoHandler, ServerMainListHandler from backends.protocols.gamespy.server_browser.requests import SendMessageRequest, ServerInfoRequest, ServerListRequest from backends.urls import SERVER_BROWSER_V2 @@ -39,7 +39,7 @@ def server_info(request: ServerInfoRequest): @router.post(f"{SERVER_BROWSER_V2}/ServerListHandler") def server_list(request: ServerListRequest): - handler = ServerListHandler(request) + handler = ServerMainListHandler(request) handler.handle() return handler.response diff --git a/src/backends/tests/gamespy/natneg/handler_tests.py b/src/backends/tests/gamespy/natneg/handler_tests.py index 0f466db5d..13ffadf63 100644 --- a/src/backends/tests/gamespy/natneg/handler_tests.py +++ b/src/backends/tests/gamespy/natneg/handler_tests.py @@ -1,6 +1,6 @@ import unittest -from backends.protocols.gamespy.natneg.handlers import InitHandler -from backends.protocols.gamespy.natneg.requests import InitRequest +from backends.protocols.gamespy.natneg.handlers import ConnectHandler, InitHandler +from backends.protocols.gamespy.natneg.requests import InitRequest, ConnectRequest from backends.tests.utils import add_headers import frontends.gamespy.protocols.natneg.contracts.requests as fnt @@ -21,3 +21,22 @@ def test_init(self): handler = InitHandler(request) handler.handle() pass + + def test_report(self): + raise NotImplementedError() + + def test_connect(self): + raw = bytes( + [ + 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, + 0x00, + 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + ] + ) # fmt:skip + r = fnt.ConnectRequest(raw) + data = add_headers(r) + data["raw_request"] = data["raw_request"].decode("ascii", "backslashreplace") + request = ConnectRequest(**data) + handler = ConnectHandler(request) + handler.handle() + pass diff --git a/src/backends/tests/gamespy/precence_search_player/handler_tests.py b/src/backends/tests/gamespy/precence_search_player/handler_tests.py index c1f274179..eddeee3f4 100644 --- a/src/backends/tests/gamespy/precence_search_player/handler_tests.py +++ b/src/backends/tests/gamespy/precence_search_player/handler_tests.py @@ -1,7 +1,5 @@ # the total requests tests import unittest - -from backends.library.database.pg_orm import PG_SESSION from backends.tests.utils import add_headers import frontends.gamespy.protocols.presence_search_player.contracts.requests as psp from frontends.tests.gamespy.presence_search_player.handler_tests import ( diff --git a/src/backends/tests/gamespy/server_browser/handler_tests.py b/src/backends/tests/gamespy/server_browser/handler_tests.py index 554293f9b..47aeddaba 100644 --- a/src/backends/tests/gamespy/server_browser/handler_tests.py +++ b/src/backends/tests/gamespy/server_browser/handler_tests.py @@ -1,7 +1,7 @@ import unittest from backends.protocols.gamespy.server_browser.handlers import ( ServerInfoHandler, - ServerListHandler, + ServerMainListHandler, ) from backends.protocols.gamespy.server_browser.requests import ( ServerInfoRequest, @@ -17,13 +17,16 @@ def test_server_main_list(self): r = fnt.ServerListRequest(raw) data = add_headers(r) request = ServerListRequest(**data) - handler = ServerListHandler(request) + handler = ServerMainListHandler(request) handler.handle() pass def test_p2p_group_room_list(self): raise NotImplementedError() + def test_server_network_info_list(self): + raise NotImplementedError() + def test_server_info(self): raw = b"\x00\t\x01\xc0\xa8z\xe2+g" r = fnt.ServerInfoRequest(raw) diff --git a/src/frontends/gamespy/protocols/chat/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py index fda7dd8cd..73d13f253 100644 --- a/src/frontends/gamespy/protocols/chat/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -150,10 +150,12 @@ def __init__(self, client: ClientBase, request: InviteRequest): class ListHandler(PostLoginHandlerBase): _request: ListRequest _result: ListResult + _result_cls: type[ListResult] def __init__(self, client: ClientBase, request: ListRequest): assert isinstance(request, ListRequest) super().__init__(client, request) + self._result_cls = ListResult def _response_construct(self) -> None: self._response = ListResponse(self._result) @@ -175,6 +177,7 @@ def _response_construct(self) -> None: class NickHandler(CmdHandlerBase): _request: NickRequest _result: NickResult + _result_cls: type[NickResult] def __init__(self, client: ClientBase, request: NickRequest): assert isinstance(request, NickRequest) @@ -192,6 +195,7 @@ class PingHandler(CmdHandlerBase): def __init__(self, client: ClientBase, request: PingRequest): assert isinstance(request, PingRequest) super().__init__(client, request) + raise NotImplementedError() def _response_construct(self) -> None: self._response = PingResponse(self._result) diff --git a/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py b/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py index c777f6e5c..565f47da9 100644 --- a/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py @@ -56,8 +56,8 @@ def __init__(self, request: RequestBase, result: ResultBase) -> None: def build(self) -> None: data = bytes() data += MAGIC_DATA - data += self._request.version.to_bytes() - data += self._result.packet_type.value.to_bytes() + data += self._request.version.to_bytes(1) + data += self._result.packet_type.value.to_bytes(1) data += self._request.cookie.to_bytes(4) self.sending_buffer = data @@ -85,8 +85,8 @@ def build(self) -> None: super().build() data = bytes() data += self.sending_buffer - data += self._request.port_type.value.to_bytes() - data += self._request.client_index.value.to_bytes() + data += self._request.port_type.value.to_bytes(1) + data += self._request.client_index.value.to_bytes(1) data += bytes(self._request.use_game_port) data += ip_to_4_bytes(self._result.public_ip_addr) data += self._result.public_port.to_bytes(2) diff --git a/src/frontends/gamespy/protocols/natneg/applications/handlers.py b/src/frontends/gamespy/protocols/natneg/applications/handlers.py index da045028a..a74a333cc 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/handlers.py +++ b/src/frontends/gamespy/protocols/natneg/applications/handlers.py @@ -12,6 +12,7 @@ ) from frontends.gamespy.protocols.natneg.contracts.responses import ( AddressCheckResponse, + ConnectResponse, ErcAckResponse, InitResponse, NatifyResponse, @@ -61,12 +62,17 @@ def __init__(self, client: Client, request: ConnectAckRequest) -> None: class ConnectHandler(CmdHandlerBase): + _request: ConnectRequest + _result: ConnectResult _result_cls: type[ConnectResult] def __init__(self, client: Client, request: ConnectRequest) -> None: assert isinstance(request, ConnectRequest) super().__init__(client, request) - self._is_fetching = False + self._result_cls = ConnectResult + + def _response_construct(self) -> None: + self._response = ConnectResponse(self._request, self._result) class ErtAckHandler(CmdHandlerBase): diff --git a/src/frontends/gamespy/protocols/natneg/contracts/requests.py b/src/frontends/gamespy/protocols/natneg/contracts/requests.py index 0b3d10928..4a19049bf 100644 --- a/src/frontends/gamespy/protocols/natneg/contracts/requests.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/requests.py @@ -33,7 +33,7 @@ def parse(self) -> None: self.client_index = NatClientIndex(self.raw_request[13]) -class ConnectRequest(RequestBase): +class ConnectRequest(CommonRequestBase): """ Server will send this request to client to let them connect to each other """ diff --git a/src/frontends/gamespy/protocols/natneg/contracts/responses.py b/src/frontends/gamespy/protocols/natneg/contracts/responses.py index 5271dff18..2270ed6e5 100644 --- a/src/frontends/gamespy/protocols/natneg/contracts/responses.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/responses.py @@ -1,11 +1,22 @@ -from frontends.gamespy.protocols.natneg.abstractions.contracts import CommonResponseBase +import socket +from frontends.gamespy.protocols.natneg.abstractions.contracts import ( + CommonResponseBase, + ResponseBase, +) from frontends.gamespy.protocols.natneg.contracts.requests import ( AddressCheckRequest, + ConnectRequest, ErtAckRequest, InitRequest, NatifyRequest, ) -from frontends.gamespy.protocols.natneg.contracts.results import AddressCheckResult, ErtAckResult, InitResult, NatifyResult +from frontends.gamespy.protocols.natneg.contracts.results import ( + AddressCheckResult, + ConnectResult, + ErtAckResult, + InitResult, + NatifyResult, +) class InitResponse(CommonResponseBase): @@ -29,23 +40,38 @@ def __init__(self, request: ErtAckRequest, result: ErtAckResult) -> None: self._result = result -class NatifyResponse(InitResponse): +class NatifyResponse(CommonResponseBase): _request: NatifyRequest _result: NatifyResult def __init__(self, request: NatifyRequest, result: NatifyResult) -> None: assert isinstance(request, NatifyRequest) assert isinstance(result, NatifyResult) - self._request = request - self._result = result + super().__init__(request, result) -class AddressCheckResponse(InitResponse): +class AddressCheckResponse(CommonResponseBase): _request: AddressCheckRequest _result: AddressCheckResult - def __init__(self, request: AddressCheckRequest, result: AddressCheckResult) -> None: + def __init__( + self, request: AddressCheckRequest, result: AddressCheckResult + ) -> None: assert isinstance(request, AddressCheckRequest) assert isinstance(result, AddressCheckResult) - self._request = request - self._result = result + super().__init__(request, result) + + +class ConnectResponse(ResponseBase): + _result: ConnectResult + _request: ConnectRequest + + def build(self) -> None: + super().build() + data = bytes() + data += self.sending_buffer + data += socket.inet_aton(self._result.ip) + data += self._result.port.to_bytes(2)[::-1] + data += self._result.got_your_data + data += self._result.finished.value.to_bytes(1) + self.sending_buffer = data diff --git a/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py index d01c2e7be..2b0cb168b 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py @@ -3,9 +3,15 @@ from frontends.gamespy.library.extentions.bytes_extentions import ip_to_4_bytes from frontends.gamespy.library.extentions.encoding import get_bytes -from frontends.gamespy.protocols.query_report.aggregates.game_server_info import GameServerInfo -from frontends.gamespy.protocols.server_browser.v2.aggregations.encryption import SERVER_CHALLENGE -from frontends.gamespy.protocols.server_browser.v2.aggregations.string_flags import STRING_SPLITER +from frontends.gamespy.protocols.query_report.aggregates.game_server_info import ( + GameServerInfo, +) +from frontends.gamespy.protocols.server_browser.v2.aggregations.encryption import ( + SERVER_CHALLENGE, +) +from frontends.gamespy.protocols.server_browser.v2.aggregations.string_flags import ( + STRING_SPLITER, +) from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import ( DataKeyType, GameServerFlags, @@ -26,8 +32,7 @@ def __init__(self, raw_request: bytes) -> None: super().__init__(raw_request) def parse(self) -> None: - self.request_length = int.from_bytes( - self.raw_request[:2], byteorder="little") + self.request_length = int.from_bytes(self.raw_request[:2], byteorder="little") self.command_name = RequestType(self.raw_request[2]) @@ -90,10 +95,8 @@ def __init__( def build(self) -> None: crypt_header = self.build_crypt_header() self._servers_info_buffers.extend(crypt_header) - self._servers_info_buffers.extend( - ip_to_4_bytes(self._result.client_remote_ip)) - self._servers_info_buffers.extend( - QUERY_REPORT_DEFAULT_PORT.to_bytes(4)) + self._servers_info_buffers.extend(ip_to_4_bytes(self._result.client_remote_ip)) + self._servers_info_buffers.extend(QUERY_REPORT_DEFAULT_PORT.to_bytes(4)) def build_crypt_header(self) -> list: # cryptHeader have 14 bytes, when we encrypt data we need skip the first 14 bytes diff --git a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/enums.py b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/enums.py index 5a517798d..7d5141b43 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/enums.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/enums.py @@ -45,7 +45,7 @@ class ProtocolVersion(IntEnum): class ServerListUpdateOption(IntEnum): SERVER_MAIN_LIST = 0 SEND_FIELD_FOR_ALL = 1 - SERVER_FULL_INFO_LIST = 2 + SERVER_FULL_MAIN_LIST = 2 P2P_SERVER_MAIN_LIST = 4 ALTERNATE_SOURCE_IP = 8 P2P_GROUP_ROOM_LIST = 32 diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py index ae2e264cd..c05cea8f7 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py @@ -10,9 +10,6 @@ GameServerStatus, RequestType, ) -from frontends.gamespy.protocols.server_browser.aggregates.exceptions import ( - ServerBrowserException, -) from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import ( RequestBase, ) @@ -30,6 +27,7 @@ ) from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( P2PGroupRoomListResult, + ServerNetworkInfoListResult, ServerInfoResult, ServerMainListResult, ) @@ -137,43 +135,83 @@ def _response_construct(self) -> None: self._response = UpdateServerInfoResponse(self._result) -class ServerListHandler(CmdHandlerBase): +class ServerMainListHandler(CmdHandlerBase): _request: ServerListRequest _result: ServerMainListResult - _result_cls: ( - type[ServerMainListResult] - | type[P2PGroupRoomListResult] - | type[ServerInfoResult] - ) - - def _request_check(self) -> None: - super()._request_check() - match self._request.update_option: - case option if option in [ - ServerListUpdateOption.SERVER_MAIN_LIST, - ServerListUpdateOption.P2P_SERVER_MAIN_LIST, - ServerListUpdateOption.LIMIT_RESULT_COUNT, - ]: - self._result_cls = ServerMainListResult - case ServerListUpdateOption.P2P_GROUP_ROOM_LIST: - self._result_cls = P2PGroupRoomListResult - case _: - raise ServerBrowserException("unknown serverlist update option type") + _result_cls: type[ServerMainListResult] + + def __init__(self, client: Client, request: RequestBase) -> None: + super().__init__(client, request) + self._result_cls = ServerMainListResult def _response_construct(self) -> None: - match self._request.update_option: - case option if option in [ - ServerListUpdateOption.SERVER_MAIN_LIST, - ServerListUpdateOption.P2P_SERVER_MAIN_LIST, - ServerListUpdateOption.LIMIT_RESULT_COUNT, - ServerListUpdateOption.SERVER_FULL_INFO_LIST, - ]: - self._response = ServerMainListResponse(self._request, self._result) - case ServerListUpdateOption.P2P_GROUP_ROOM_LIST: - self._response = P2PGroupRoomListResponse(self._request, self._result) - case ServerListUpdateOption.SERVER_FULL_INFO_LIST: - self._response = ServerNetworkInfoListResponse( - self._request, self._result - ) - case _: - raise ServerBrowserException("unknown serverlist update option type") + self._response = ServerMainListResponse(self._request, self._result) + + +class P2PGroupRoomListHandler(CmdHandlerBase): + _request: ServerListRequest + _result: P2PGroupRoomListResult + _result_cls: type[P2PGroupRoomListResult] + + def __init__(self, client: Client, request: RequestBase) -> None: + super().__init__(client, request) + self._result_cls = P2PGroupRoomListResult + + def _response_construct(self) -> None: + self._response = P2PGroupRoomListResponse(self._request, self._result) + + +class ServerNetworkInfoListHandler(CmdHandlerBase): + _request: ServerListRequest + _result: ServerNetworkInfoListResult + _result_cls: type[ServerNetworkInfoListResult] + + def __init__(self, client: Client, request: RequestBase) -> None: + super().__init__(client, request) + self._result_cls = ServerNetworkInfoListResult + + def _response_construct(self) -> None: + self._response = ServerNetworkInfoListResponse(self._request, self._result) + + +# class ServerListHandler(CmdHandlerBase): +# _request: ServerListRequest +# _result: ServerMainListResult +# _result_cls: ( +# type[ServerMainListResult] +# | type[P2PGroupRoomListResult] +# | type[ServerInfoResult] +# ) + +# def _request_check(self) -> None: +# super()._request_check() +# match self._request.update_option: +# case option if option in [ +# ServerListUpdateOption.SERVER_MAIN_LIST, +# ServerListUpdateOption.P2P_SERVER_MAIN_LIST, +# ServerListUpdateOption.LIMIT_RESULT_COUNT, +# ServerListUpdateOption.SERVER_FULL_MAIN_LIST, +# ]: +# self._result_cls = ServerMainListResult +# case ServerListUpdateOption.P2P_GROUP_ROOM_LIST: +# self._result_cls = P2PGroupRoomListResult +# case _: +# raise ServerBrowserException("unknown serverlist update option type") + +# def _response_construct(self) -> None: +# match self._request.update_option: +# case option if option in [ +# ServerListUpdateOption.SERVER_MAIN_LIST, +# ServerListUpdateOption.P2P_SERVER_MAIN_LIST, +# ServerListUpdateOption.LIMIT_RESULT_COUNT, +# ServerListUpdateOption.SERVER_FULL_MAIN_LIST, +# ]: +# self._response = ServerMainListResponse(self._request, self._result) +# case ServerListUpdateOption.P2P_GROUP_ROOM_LIST: +# self._response = P2PGroupRoomListResponse(self._request, self._result) +# case ServerListUpdateOption.SERVER_FULL_MAIN_LIST: +# self._response = ServerNetworkInfoListResponse( +# self._request, self._result +# ) +# case _: +# raise ServerBrowserException("unknown serverlist update option type") diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py index 94ce9ef55..fa7a47565 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py @@ -1,11 +1,29 @@ from typing import TYPE_CHECKING, Optional, cast from frontends.gamespy.library.abstractions.switcher import SwitcherBase from frontends.gamespy.library.exceptions.general import UniSpyException -from frontends.gamespy.protocols.server_browser.v2.abstractions.handlers import CmdHandlerBase -from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import RequestType +from frontends.gamespy.protocols.server_browser.aggregates.exceptions import ( + ServerBrowserException, +) +from frontends.gamespy.protocols.server_browser.v2.abstractions.handlers import ( + CmdHandlerBase, +) +from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import ( + RequestType, + ServerListUpdateOption, +) from frontends.gamespy.protocols.server_browser.v2.applications.client import Client -from frontends.gamespy.protocols.server_browser.v2.applications.handlers import SendMessageHandler, ServerInfoHandler, ServerListHandler -from frontends.gamespy.protocols.server_browser.v2.contracts.requests import SendMessageRequest, ServerInfoRequest, ServerListRequest +from frontends.gamespy.protocols.server_browser.v2.applications.handlers import ( + P2PGroupRoomListHandler, + SendMessageHandler, + ServerNetworkInfoListHandler, + ServerInfoHandler, + ServerMainListHandler, +) +from frontends.gamespy.protocols.server_browser.v2.contracts.requests import ( + SendMessageRequest, + ServerInfoRequest, + ServerListRequest, +) class Switcher(SwitcherBase): @@ -16,19 +34,42 @@ def _process_raw_request(self) -> None: raise UniSpyException("Invalid request") name = self._raw_request[2] if name not in RequestType: - self._client.log_debug( - f"Request: {name} is not a valid request.") + self._client.log_debug(f"Request: {name} is not a valid request.") return self._requests.append((RequestType(name), self._raw_request)) - def _create_cmd_handlers(self, name: int, raw_request: bytes) -> Optional[CmdHandlerBase]: + def _create_cmd_handlers( + self, name: int, raw_request: bytes + ) -> Optional[CmdHandlerBase]: req = raw_request if TYPE_CHECKING: self._client = cast(Client, self._client) match name: case RequestType.SERVER_LIST_REQUEST: - return ServerListHandler(self._client, ServerListRequest(req)) + update_option_index = raw_request.find(b"\x00\x00\x00\x00") + 1 + update_option_bytes = raw_request[ + update_option_index : update_option_index + 4 + ] + update_option = ServerListUpdateOption( + int.from_bytes(update_option_bytes) + ) + if update_option in [ + ServerListUpdateOption.SERVER_MAIN_LIST, + ServerListUpdateOption.P2P_SERVER_MAIN_LIST, + ServerListUpdateOption.LIMIT_RESULT_COUNT, + ]: + return ServerMainListHandler(self._client, ServerListRequest(req)) + elif update_option == ServerListUpdateOption.P2P_GROUP_ROOM_LIST: + return P2PGroupRoomListHandler(self._client, ServerListRequest(req)) + elif update_option == ServerListUpdateOption.SERVER_FULL_MAIN_LIST: + return ServerNetworkInfoListHandler( + self._client, ServerListRequest(req) + ) + else: + raise ServerBrowserException( + "unknown serverlist update option type" + ) case RequestType.SERVER_INFO_REQUEST: return ServerInfoHandler(self._client, ServerInfoRequest(req)) case RequestType.SEND_MESSAGE_REQUEST: diff --git a/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py index 201fccbd5..6e0c88d0b 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py @@ -1,16 +1,21 @@ from socket import inet_ntoa from typing import Optional +from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import ( AdHocRequestBase, ServerListUpdateOptionRequestBase, ) -from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import RequestType, ServerListUpdateOption +from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import ( + RequestType, + ServerListUpdateOption, +) class ServerListRequest(ServerListUpdateOptionRequestBase): - command_name: RequestType = RequestType.SERVER_LIST_REQUEST - def parse(self) -> None: + self.command_name = RequestType(self.raw_request[0]) + if self.command_name != RequestType.SERVER_LIST_REQUEST: + raise UniSpyException("raw request is not valid server list request") self.request_version = int(self.raw_request[2]) self.protocol_version = int(self.raw_request[3]) self.encoding_version = int(self.raw_request[4]) @@ -19,25 +24,24 @@ def parse(self) -> None: remain_data = self.raw_request[9:] dev_game_name_index = remain_data.index(0) self.dev_game_name = remain_data[:dev_game_name_index].decode() - remain_data = remain_data[dev_game_name_index + 1:] + remain_data = remain_data[dev_game_name_index + 1 :] game_name_index = remain_data.index(0) self.game_name = remain_data[:game_name_index].decode() - remain_data = remain_data[game_name_index + 1:] + remain_data = remain_data[game_name_index + 1 :] self.client_challenge = remain_data[:8].decode() remain_data = remain_data[8:] filter_index = remain_data.index(0) if filter_index > 0: self.filter = remain_data[:filter_index].decode() - remain_data = remain_data[filter_index + 1:] + remain_data = remain_data[filter_index + 1 :] keys_index = remain_data.index(0) self.keys = remain_data[:keys_index].decode().split("\\") - remain_data = remain_data[keys_index + 1:] + remain_data = remain_data[keys_index + 1 :] byte_update_options = remain_data[:4] - self.update_option = ServerListUpdateOption( - int.from_bytes(byte_update_options)) + self.update_option = ServerListUpdateOption(int.from_bytes(byte_update_options)) remain_data = remain_data[4:] if self.update_option & ServerListUpdateOption.ALTERNATE_SOURCE_IP: @@ -46,7 +50,7 @@ def parse(self) -> None: if self.update_option & ServerListUpdateOption.LIMIT_RESULT_COUNT: if len(remain_data) != 4: - raise Exception("The max number of server is incorrect.") + raise UniSpyException("The max number of server is incorrect.") self.max_servers = int(remain_data[:4][::-1]) diff --git a/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py index 14a6e1a31..64360a9c2 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py @@ -135,3 +135,4 @@ class ServerNetworkInfoListResponse(ServerListUpdateOptionResponseBase): def build(self) -> None: super().build() self.sending_buffer = bytes(self._servers_info_buffers) + raise NotImplementedError() diff --git a/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py index d02c9b719..41b6ee82e 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py @@ -1,7 +1,11 @@ from uuid import UUID -from frontends.gamespy.protocols.query_report.aggregates.game_server_info import GameServerInfo -from frontends.gamespy.protocols.query_report.aggregates.peer_room_info import PeerRoomInfo +from frontends.gamespy.protocols.query_report.aggregates.game_server_info import ( + GameServerInfo, +) +from frontends.gamespy.protocols.query_report.aggregates.peer_room_info import ( + PeerRoomInfo, +) from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import ( AdHocResultBase, ResultBase, @@ -21,6 +25,10 @@ class ServerMainListResult(ServerListUpdateOptionResultBase): servers_info: list[GameServerInfo] +class ServerNetworkInfoListResult(ServerListUpdateOptionResultBase): + servers_info: list[GameServerInfo] + + class SendMessageResult(ResultBase): sb_sender_id: UUID natneg_message: str diff --git a/src/frontends/tests/gamespy/server_browser/mock_objects.py b/src/frontends/tests/gamespy/server_browser/mock_objects.py index 5611f7b05..d83a45039 100644 --- a/src/frontends/tests/gamespy/server_browser/mock_objects.py +++ b/src/frontends/tests/gamespy/server_browser/mock_objects.py @@ -9,7 +9,7 @@ from frontends.gamespy.protocols.server_browser.v2.applications.client import Client from frontends.gamespy.protocols.server_browser.v2.applications.handlers import ( ServerInfoHandler, - ServerListHandler, + ServerMainListHandler, ) from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( ServerInfoResult, @@ -34,7 +34,7 @@ def create_v2_client() -> Client: create_mock_url( config, - ServerListHandler, + ServerMainListHandler, ServerMainListResult.model_validate( { "client_remote_ip": "127.0.0.1", @@ -90,7 +90,7 @@ def create_v1_client() -> Client: config = CONFIG.servers["ServerBrowserV1"] create_mock_url( config, - ServerListHandler, + ServerMainListHandler, ServerMainListResult.model_validate( {"remote_ip_address": conn.remote_ip, "remote_port": conn.remote_port} ).model_dump(), From 043ef420d6844ed3f7b7d3db6665a7d9929cfa76 Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Mon, 9 Jun 2025 06:15:04 +0000 Subject: [PATCH 188/231] Update[natneg] nat traversal protocols and tests --- src/backends/protocols/gamespy/natneg/data.py | 48 ++- .../protocols/gamespy/natneg/handlers.py | 69 ++++- .../protocols/gamespy/natneg/helpers.py | 273 ++++++++++++++++++ .../protocols/gamespy/natneg/requests.py | 2 +- .../gamespy/natneg/nat_detection_tests.py | 242 ++++++++++++++++ src/frontends/gamespy/__init__.py | 2 + .../gamespy/protocols/natneg/__init__.py | 4 + .../protocols/natneg/aggregations/enums.py | 21 +- .../protocols/natneg/contracts/results.py | 4 +- 9 files changed, 639 insertions(+), 26 deletions(-) create mode 100644 src/backends/protocols/gamespy/natneg/helpers.py create mode 100644 src/backends/tests/gamespy/natneg/nat_detection_tests.py create mode 100644 src/frontends/gamespy/__init__.py diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py index e78f372bd..a0826963b 100644 --- a/src/backends/protocols/gamespy/natneg/data.py +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -1,5 +1,4 @@ from datetime import datetime, timedelta -from uuid import UUID from backends.library.database.pg_orm import PG_SESSION, InitPacketCaches, NatFailCaches from frontends.gamespy.protocols.natneg.aggregations.enums import NatClientIndex @@ -11,22 +10,31 @@ def store_init_packet(info: InitPacketCaches) -> None: def count_init_info(cookie: int, version: int) -> int: - time = datetime.now()-timedelta(seconds=30) - result = PG_SESSION.query(InitPacketCaches).where( - InitPacketCaches.cookie == cookie, - InitPacketCaches.version == version, - InitPacketCaches.update_time >= time).count() + time = datetime.now() - timedelta(seconds=30) + result = ( + PG_SESSION.query(InitPacketCaches) + .where( + InitPacketCaches.cookie == cookie, + InitPacketCaches.version == version, + InitPacketCaches.update_time >= time, + ) + .count() + ) return result -def get_init_infos(server_id: UUID, cookie: int, client_index: NatClientIndex) -> list[InitPacketCaches]: +def get_init_infos(cookie: int, client_index: NatClientIndex) -> list[InitPacketCaches]: # query the latest init info with in 30 seconds - time = datetime.now()-timedelta(seconds=30) - result = PG_SESSION.query(InitPacketCaches).where( - InitPacketCaches.server_id == server_id, - InitPacketCaches.cookie == cookie, - InitPacketCaches.client_index == client_index, - InitPacketCaches.update_time >= time).all() + time = datetime.now() - timedelta(seconds=30) + result = ( + PG_SESSION.query(InitPacketCaches) + .where( + InitPacketCaches.cookie == cookie, + InitPacketCaches.client_index == client_index, + InitPacketCaches.update_time >= time, + ) + .all() + ) return result @@ -63,11 +71,19 @@ def remove_nat_fail_info(info: NatFailCaches) -> None: def get_nat_fail_info(info: NatFailCaches): result = get_nat_fail_info_by_ip( - str(info.public_ip_address1), str(info.public_ip_address2)) + str(info.public_ip_address1), str(info.public_ip_address2) + ) return result + def get_nat_fail_info_by_ip(public_ip1: str, public_ip2: str) -> list[NatFailCaches]: - result = PG_SESSION.query(NatFailCaches).where(NatFailCaches.public_ip_address1 == public_ip1, - NatFailCaches.public_ip_address2 == public_ip2).all() + result = ( + PG_SESSION.query(NatFailCaches) + .where( + NatFailCaches.public_ip_address1 == public_ip1, + NatFailCaches.public_ip_address2 == public_ip2, + ) + .all() + ) return result diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py index 650718600..2d3871457 100644 --- a/src/backends/protocols/gamespy/natneg/handlers.py +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -1,9 +1,14 @@ from datetime import datetime, timezone -from backends.library.abstractions.contracts import RequestBase from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import InitPacketCaches -from backends.protocols.gamespy.natneg.data import update_init_info +import backends.protocols.gamespy.natneg.data as data +from backends.protocols.gamespy.natneg.helpers import NatProtocolHelper, NatStrategy from backends.protocols.gamespy.natneg.requests import ConnectRequest, InitRequest +from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.protocols.natneg.aggregations.enums import ( + NatClientIndex, +) +from frontends.gamespy.protocols.natneg.contracts.results import ConnectResult class InitHandler(HandlerBase): @@ -28,13 +33,69 @@ def _data_operate(self) -> None: private_port=self._request.private_port, update_time=datetime.now(timezone.utc), ) - update_init_info(info) + data.update_init_info(info) class ConnectHandler(HandlerBase): - def __init__(self, request: RequestBase) -> None: + _request: ConnectRequest + _strategy: NatStrategy + + def __init__(self, request: ConnectRequest) -> None: super().__init__(request) assert isinstance(request, ConnectRequest) def _data_operate(self) -> None: + # analysis NAT of both parties and find the proper ips + init_infos_1 = data.get_init_infos( + self._request.cookie, self._request.client_index + ) + # choose the other index of the currect client + if self._request.client_index == NatClientIndex.GAME_CLIENT: + client_index_2 = NatClientIndex.GAME_SERVER + else: + client_index_2 = NatClientIndex.GAME_CLIENT + init_infos_2 = data.get_init_infos(self._request.cookie, client_index_2) + if len(init_infos_1) == 0 or len(init_infos_2) == 0: + raise UniSpyException( + f"no init info found for cookie {self._request.cookie}" + ) + assert isinstance(init_infos_1[0].public_ip, str) + assert isinstance(init_infos_2[0].public_ip, str) + nat_fail_infos = data.get_nat_fail_info_by_ip( + init_infos_1[0].public_ip, init_infos_2[0].public_ip + ) + self._strategy = NatStrategy.USE_GAME_TRAFFIC_RALEY + if len(nat_fail_infos) != 0: + self._strategy = NatStrategy.USE_GAME_TRAFFIC_RALEY + else: + self._helper1 = NatProtocolHelper(init_infos_1) + self._helper2 = NatProtocolHelper(init_infos_2) + self._strategy = NatProtocolHelper.choose_nat_strategy(self._helper1, self._helper2) + + def _result_construct(self) -> None: + if self._strategy == NatStrategy.USE_PRIVATE_IP: + self._result = ConnectResult( + ip=self._helper2.private_ip, + port=self._helper2.private_port, + version=self._helper2.version, + cookie=self._helper2.cookie, + ) + elif self._strategy == NatStrategy.USE_PUBLIC_IP: + self._result = ConnectResult( + ip=self._helper2.public_ip, + port=self._helper2.public_port, + version=self._helper2.version, + cookie=self._helper2.cookie, + ) + + elif self._strategy == NatStrategy.USE_GAME_TRAFFIC_RALEY: + self._use_game_traffic_relay() + + def _use_game_traffic_relay(self): + raise NotImplementedError() + + def _use_public_address(self): + raise NotImplementedError() + + def _use_private_address(self): raise NotImplementedError() diff --git a/src/backends/protocols/gamespy/natneg/helpers.py b/src/backends/protocols/gamespy/natneg/helpers.py new file mode 100644 index 000000000..049b766d2 --- /dev/null +++ b/src/backends/protocols/gamespy/natneg/helpers.py @@ -0,0 +1,273 @@ +import enum +from backends.library.database.pg_orm import InitPacketCaches +from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.protocols.natneg.aggregations.enums import ( + NatClientIndex, + NatPortMappingScheme, + NatPortType, + NatType, +) + +class NatStrategy(enum.IntEnum): + USE_PUBLIC_IP = 0 + USE_PRIVATE_IP = 1 + USE_GAME_TRAFFIC_RALEY = 2 + + +class NatNegVersion(enum.IntEnum): + VERSION1 = 1 + VERSION2 = 2 + VERSION3 = 3 + + +class NatProtocolHelper: + address_infos: dict[NatPortType, InitPacketCaches] + nat_type: NatType + port_mapping: NatPortMappingScheme + guessed_next_port: int + cookie: int + version: int + client_index: NatClientIndex + public_ip: str + public_port: int + private_ip: str + private_port: int + + def __init__(self, init_caches: list[InitPacketCaches]) -> None: + if len(init_caches) < 3: + raise UniSpyException("init cache length not enough for NAT determination") + self.nat_type = NatType.NO_NAT + self.port_mapping = NatPortMappingScheme.CONSISTENT_PORT + self.guessed_next_port = 0 + self.address_infos = {} + for cache in init_caches: + assert isinstance(cache.port_type, NatPortType) + self.address_infos[cache.port_type] = cache + + + + last_address_info = list(self.address_infos.values())[-1] + assert isinstance(last_address_info.cookie, int) + assert isinstance(last_address_info.version, int) + assert isinstance(last_address_info.client_index, NatClientIndex) + assert isinstance(last_address_info.public_ip, str) + assert isinstance(last_address_info.public_port, int) + assert isinstance(last_address_info.private_ip, str) + assert isinstance(last_address_info.private_port, int) + + self.cookie = last_address_info.cookie + self.version = last_address_info.version + self.client_index = last_address_info.client_index + self.public_ip = last_address_info.public_ip + self.public_port = last_address_info.public_port + self.private_ip = last_address_info.private_ip + self.private_port = last_address_info.private_port + + if self.version not in NatNegVersion: + raise UniSpyException("Unknown natneg version") + version = NatNegVersion(self.version) + if version == NatNegVersion.VERSION1: + raise UniSpyException("Version 1 not implemented") + elif version == NatNegVersion.VERSION2: + self._validate_version2() + NatProtocolHelper._determine_nat_type_version2(self) + elif version == NatNegVersion.VERSION3: + self._validate_version3() + NatProtocolHelper._determine_nat_type_version3(self) + def _validate_version2(self): + if not ( + NatPortType.NN1 in self.address_infos + and NatPortType.NN2 in self.address_infos + ): + raise UniSpyException("Incomplete init packets") + assert isinstance(self.address_infos[NatPortType.NN1].cookie, int) + assert isinstance(self.address_infos[NatPortType.NN2].cookie, int) + + if ( + self.address_infos[NatPortType.NN1].cookie + != self.address_infos[NatPortType.NN2].cookie + ): # type: ignore + raise UniSpyException("Broken cookie") + if ( + self.address_infos[NatPortType.NN1].version + != self.address_infos[NatPortType.NN2].version + ): # type: ignore + raise UniSpyException("Broken version") + if ( + self.address_infos[NatPortType.NN1].client_index + != self.address_infos[NatPortType.NN2].client_index + ): # type: ignore + raise UniSpyException("Broken client index") + + if NatPortType.GP in self.address_infos: + if ( + self.address_infos[NatPortType.GP].cookie + != self.address_infos[NatPortType.NN1].cookie + or self.address_infos[NatPortType.GP].version + != self.address_infos[NatPortType.NN1].version + or self.address_infos[NatPortType.GP].client_index + != self.address_infos[NatPortType.NN1].client_index + or self.address_infos[NatPortType.GP].use_game_port + != self.address_infos[NatPortType.NN1].use_game_port + ): # type: ignore + raise UniSpyException("GP packet info is not correct") + + def _validate_version3(self): + # TODO: some games will not send GP packet to NAT negotiation server; currently, the reason is unknown and requires more games for analysis. + # This will happen in GameClient + + if not ( + NatPortType.NN1 in self.address_infos + and NatPortType.NN2 in self.address_infos + and NatPortType.NN3 in self.address_infos + ): + raise UniSpyException("Incomplete init packets") + + if ( + self.address_infos[NatPortType.NN1].cookie + != self.address_infos[NatPortType.NN2].cookie + or self.address_infos[NatPortType.NN1].cookie + != self.address_infos[NatPortType.NN3].cookie + ): # type: ignore + raise UniSpyException("Broken cookie") + + if ( + self.address_infos[NatPortType.NN1].version + != self.address_infos[NatPortType.NN2].version + or self.address_infos[NatPortType.NN1].version + != self.address_infos[NatPortType.NN3].version + ): # type: ignore + raise UniSpyException("Broken version") + + if ( + self.address_infos[NatPortType.NN1].client_index + != self.address_infos[NatPortType.NN2].client_index + or self.address_infos[NatPortType.NN1].client_index + != self.address_infos[NatPortType.NN3].client_index + ): # type: ignore + raise UniSpyException("Broken client index") + + if ( + self.address_infos[NatPortType.NN1].use_game_port + != self.address_infos[NatPortType.NN2].use_game_port + or self.address_infos[NatPortType.NN1].use_game_port + != self.address_infos[NatPortType.NN3].use_game_port + ): # type: ignore + raise UniSpyException("Broken use game port") + + if ( + self.address_infos[NatPortType.NN2].private_ip + != self.address_infos[NatPortType.NN3].private_ip + ): # type: ignore + raise UniSpyException("Client is sending wrong init packet.") + + if NatPortType.GP in self.address_infos: + if ( + self.address_infos[NatPortType.GP].cookie + != self.address_infos[NatPortType.NN1].cookie + or self.address_infos[NatPortType.GP].version + != self.address_infos[NatPortType.NN1].version + or self.address_infos[NatPortType.GP].client_index + != self.address_infos[NatPortType.NN1].client_index + or self.address_infos[NatPortType.GP].use_game_port + != self.address_infos[NatPortType.NN1].use_game_port + ): # type: ignore + raise UniSpyException("GP packet info is not correct") + + @staticmethod + def _determine_nat_type_version2(info: "NatProtocolHelper"): + if len(info.address_infos) < 3: + raise UniSpyException("We need 3 init records to determine the nat type.") + + nn1 = info.address_infos[NatPortType.NN1] + nn2 = info.address_infos[NatPortType.NN2] + # no nat + if nn1.public_ip == nn1.private_ip and ( + nn2.public_ip == nn2.private_ip and nn2.public_port == nn1.public_port + ): # type: ignore + info.nat_type = NatType.NO_NAT + # detect cone + elif nn1.public_ip == nn2.public_ip and nn1.public_port == nn2.public_port: # type: ignore + info.nat_type = NatType.FULL_CONE + elif nn1.public_ip == nn2.public_ip and nn1.public_port != nn2.public_port: # type: ignore + info.nat_type = NatType.SYMMETRIC + info.port_mapping = NatPortMappingScheme.INCREMENTAL + # todo: get all interval of the port increment value + port_interval = nn2.public_port - nn1.public_port + info.guessed_next_port = nn2.public_port + port_interval # type: ignore + else: + info.nat_type = NatType.UNKNOWN + + @staticmethod + def _determine_nat_type_version3(info: "NatProtocolHelper"): + if len(info.address_infos) < 3: + raise UniSpyException("We need 3 init records to determine the nat type.") + + nn1 = info.address_infos[NatPortType.NN1] + nn2 = info.address_infos[NatPortType.NN2] + nn3 = info.address_infos[NatPortType.NN3] + + # no nat + if ( + nn1.public_ip == nn1.private_ip + and ( + nn2.public_ip == nn2.private_ip and nn2.public_port == nn2.private_port + ) + and ( + nn3.public_ip == nn3.private_ip and nn3.public_port == nn3.private_port + ) + ): # type: ignore + info.nat_type = NatType.NO_NAT + # detect cone + elif ( + nn1.public_ip == nn2.public_ip and nn1.public_port == nn2.public_port + ) and (nn2.public_ip == nn3.public_ip and nn2.public_port == nn3.public_port): # type: ignore + info.nat_type = NatType.FULL_CONE + elif ( + nn1.public_ip == nn2.public_ip and nn1.public_port != nn2.public_port + ) or (nn2.public_ip == nn3.public_ip and nn2.public_port != nn3.public_port): # type: ignore + info.nat_type = NatType.SYMMETRIC + info.port_mapping = NatPortMappingScheme.INCREMENTAL + + # Calculate port increments + port_increment_1: int = nn2.public_port - nn1.public_port # type: ignore + port_increment_2: int = nn3.public_port - nn2.public_port # type: ignore + assert isinstance(port_increment_1, int) + assert isinstance(port_increment_2, int) + if port_increment_1 == port_increment_2: + info.guessed_next_port = port_increment_1 + else: + increase_interval = port_increment_2 - port_increment_1 + info.guessed_next_port = port_increment_2 + increase_interval + else: + info.nat_type = NatType.UNKNOWN + + @staticmethod + def choose_nat_strategy( + helper1: "NatProtocolHelper", helper2: "NatProtocolHelper" + ) -> NatStrategy: + is_both_have_same_public_ip = helper1.public_ip == helper2.public_ip + # Check if the first 3 bytes of the private IP addresses are the same + is_both_in_same_private_ip_range = ( + helper1.private_ip.split(".")[:3] == helper2.private_ip.split(".")[:3] + ) + + # we use p2p strategy when nat punch is available + p2p_nat_types = [ + NatType.NO_NAT, + NatType.FIRE_WALL_ONLY, + NatType.FULL_CONE, + NatType.ADDRESS_RESTRICTED_CONE, + NatType.PORT_RESTRICTED_CONE, + ] + + if helper1.nat_type in p2p_nat_types and helper2.nat_type in p2p_nat_types: + if is_both_have_same_public_ip: + if is_both_in_same_private_ip_range: + return NatStrategy.USE_PRIVATE_IP + else: + return NatStrategy.USE_GAME_TRAFFIC_RALEY + else: + return NatStrategy.USE_PUBLIC_IP + else: + return NatStrategy.USE_GAME_TRAFFIC_RALEY diff --git a/src/backends/protocols/gamespy/natneg/requests.py b/src/backends/protocols/gamespy/natneg/requests.py index 9d85bcaec..f05886bff 100644 --- a/src/backends/protocols/gamespy/natneg/requests.py +++ b/src/backends/protocols/gamespy/natneg/requests.py @@ -32,7 +32,7 @@ class ConnectAckRequest(RequestBase): client_index: NatClientIndex -class ConnectRequest(RequestBase): +class ConnectRequest(CommonRequestBase): """ Server will send this request to client to let them connect to each other """ diff --git a/src/backends/tests/gamespy/natneg/nat_detection_tests.py b/src/backends/tests/gamespy/natneg/nat_detection_tests.py new file mode 100644 index 000000000..deaecbfbd --- /dev/null +++ b/src/backends/tests/gamespy/natneg/nat_detection_tests.py @@ -0,0 +1,242 @@ +import unittest + +from backends.library.database.pg_orm import InitPacketCaches +from backends.protocols.gamespy.natneg.helpers import NatProtocolHelper +from frontends.gamespy.protocols.natneg.aggregations.enums import ( + NatClientIndex, + NatPortType, + NatType, +) + + +class NatDetectionTests(unittest.TestCase): + def test_public_ip(self): + # Create a list of InitPacketCache instances + packet_caches: list[InitPacketCaches] = [ + InitPacketCaches( + port_type=NatPortType.GP, + client_index=NatClientIndex.GAME_CLIENT, + cookie=123, + version=3, + game_name="gmtest", + public_ip="10.0.0.1", + public_port=1, + private_ip="10.0.0.1", + private_port=0, + ), + InitPacketCaches( + port_type=NatPortType.NN1, + client_index=NatClientIndex.GAME_CLIENT, + cookie=123, + version=3, + game_name="gmtest", + public_ip="10.0.0.1", + public_port=2, + private_ip="10.0.0.1", + private_port=0, + ), + InitPacketCaches( + port_type=NatPortType.NN2, + client_index=NatClientIndex.GAME_CLIENT, + cookie=123, + version=3, + game_name="gmtest", + public_ip="10.0.0.1", + public_port=2, + private_ip="10.0.0.1", + private_port=2, + ), + InitPacketCaches( + port_type=NatPortType.NN3, + client_index=NatClientIndex.GAME_CLIENT, + cookie=123, + version=3, + game_name="gmtest", + public_ip="10.0.0.1", + public_port=2, + private_ip="10.0.0.1", + private_port=2, + ), + ] + + # Create an instance of NatInitInfo with the list of packet caches + init_info = NatProtocolHelper(packet_caches) + + # Determine NAT type + NatProtocolHelper._determine_nat_type_version3(init_info) + + # Assert that the NAT type is NoNat + self.assertEqual(init_info.nat_type, NatType.NO_NAT) + + def test_full_cone(self): + # Create a list of InitPacketCache instances + packet_caches: list[InitPacketCaches] = [ + InitPacketCaches( + port_type=NatPortType.GP, + client_index=NatClientIndex.GAME_CLIENT, + cookie=123, + version=3, + game_name="gmtest", + public_ip="10.0.0.1", + public_port=1, + private_ip="192.168.1.1", + private_port=0, + ), + InitPacketCaches( + port_type=NatPortType.NN1, + client_index=NatClientIndex.GAME_CLIENT, + cookie=123, + version=3, + game_name="gmtest", + public_ip="10.0.0.1", + public_port=2, + private_ip="192.168.1.1", + private_port=0, + ), + InitPacketCaches( + port_type=NatPortType.NN2, + client_index=NatClientIndex.GAME_CLIENT, + cookie=123, + version=3, + game_name="gmtest", + public_ip="10.0.0.1", + public_port=2, + private_ip="192.168.1.1", + private_port=2, + ), + InitPacketCaches( + port_type=NatPortType.NN3, + client_index=NatClientIndex.GAME_CLIENT, + cookie=123, + version=3, + game_name="gmtest", + public_ip="10.0.0.1", + public_port=2, + private_ip="192.168.1.1", + private_port=2, + ), + ] + + # Create an instance of NatInitInfo with the list of packet caches + init_info = NatProtocolHelper(packet_caches) + + # Determine NAT type + NatProtocolHelper._determine_nat_type_version3(init_info) + self.assertEqual(init_info.nat_type, NatType.FULL_CONE) + + # Test method + def test_symmetric(self): + # Create a list of InitPacketCaches instances + packet_caches = [ + InitPacketCaches( + port_type=NatPortType.GP, + client_index=NatClientIndex.GAME_CLIENT, + cookie=123, + version=3, + game_name="gmtest", + public_ip="10.0.0.1", + public_port=1, + private_ip="192.168.1.1", + private_port=1, + ), + InitPacketCaches( + port_type=NatPortType.NN1, + client_index=NatClientIndex.GAME_CLIENT, + cookie=123, + version=3, + game_name="gmtest", + public_ip="10.0.0.1", + public_port=2, + private_ip="192.168.1.1", + private_port=2, + ), + InitPacketCaches( + port_type=NatPortType.NN2, + client_index=NatClientIndex.GAME_CLIENT, + cookie=123, + version=3, + game_name="gmtest", + public_ip="10.0.0.1", + public_port=3, + private_ip="192.168.1.1", + private_port=2, + ), + InitPacketCaches( + port_type=NatPortType.NN3, + client_index=NatClientIndex.GAME_CLIENT, + cookie=123, + version=3, + game_name="gmtest", + public_ip="10.0.0.1", + public_port=4, + private_ip="192.168.1.1", + private_port=2, + ), + ] + + # Create an instance of NatInitInfo with the list of packet caches + init_info = NatProtocolHelper(packet_caches) + + # Determine NAT type + NatProtocolHelper._determine_nat_type_version3(init_info) + + # Assert the NAT type is symmetric + self.assertEqual(init_info.nat_type, NatType.SYMMETRIC) + + def test_sposirius_network(self): + # Create a list of InitPacketCaches instances + packet_caches = [ + InitPacketCaches( + port_type=NatPortType.GP, + client_index=NatClientIndex.GAME_CLIENT, + cookie=123, + version=3, + game_name="gmtest", + public_ip="91.52.105.210", + public_port=51520, + private_ip="192.168.0.60", + private_port=0, + ), + InitPacketCaches( + port_type=NatPortType.NN1, + client_index=NatClientIndex.GAME_CLIENT, + cookie=123, + version=3, + game_name="gmtest", + public_ip="91.52.105.210", + public_port=51521, + private_ip="192.168.0.60", + private_port=0, + ), + InitPacketCaches( + port_type=NatPortType.NN2, + client_index=NatClientIndex.GAME_CLIENT, + cookie=123, + version=3, + game_name="gmtest", + public_ip="91.52.105.210", + public_port=49832, + private_ip="192.168.0.60", + private_port=49832, + ), + InitPacketCaches( + port_type=NatPortType.NN3, + client_index=NatClientIndex.GAME_CLIENT, + cookie=123, + version=3, + game_name="gmtest", + public_ip="91.52.105.210", + public_port=49832, + private_ip="192.168.0.60", + private_port=49832, + ), + ] + + # Create an instance of NatInitInfo with the list of packet caches + init_info = NatProtocolHelper(packet_caches) + + # Determine NAT type + NatProtocolHelper._determine_nat_type_version3(init_info) + + # Assert the NAT type is symmetric + self.assertEqual(init_info.nat_type, NatType.SYMMETRIC) diff --git a/src/frontends/gamespy/__init__.py b/src/frontends/gamespy/__init__.py new file mode 100644 index 000000000..48dc09b0d --- /dev/null +++ b/src/frontends/gamespy/__init__.py @@ -0,0 +1,2 @@ +# from .library.exceptions.general import UniSpyException +# from .protocols.natneg import NatClientIndex diff --git a/src/frontends/gamespy/protocols/natneg/__init__.py b/src/frontends/gamespy/protocols/natneg/__init__.py index e69de29bb..a752c796b 100644 --- a/src/frontends/gamespy/protocols/natneg/__init__.py +++ b/src/frontends/gamespy/protocols/natneg/__init__.py @@ -0,0 +1,4 @@ +# from .aggregations.enums import ( +# NatClientIndex, + +# ) \ No newline at end of file diff --git a/src/frontends/gamespy/protocols/natneg/aggregations/enums.py b/src/frontends/gamespy/protocols/natneg/aggregations/enums.py index 86788f465..eae9c7376 100644 --- a/src/frontends/gamespy/protocols/natneg/aggregations/enums.py +++ b/src/frontends/gamespy/protocols/natneg/aggregations/enums.py @@ -1,4 +1,4 @@ -from enum import Enum +from enum import Enum, IntEnum class RequestType(Enum): @@ -53,13 +53,28 @@ class PreInitState(Enum): READY = 2 -class NatType(Enum): +class NatType(IntEnum): NO_NAT = 0 - FIREWALL_ONLY = 1 + FIRE_WALL_ONLY = 1 + """ + C发数据到210.21.12.140:8000,NAT会将数据包送到A(192.168.0.4:5000).因为NAT上已经有了192.168.0.4:5000到210.21.12.140:8000的映射 + """ FULL_CONE = 2 + """ + C无法和A通信,因为A从来没有和C通信过,NAT将拒绝C试图与A连接的动作.但B可以通过210.21.12.140:8000与A的 192.168.0.4:5000通信,且这里B可以使用任何端口与A通信.如:210.15.27.166:2001 -> 210.21.12.140:8000,NAT会送到A的5000端口上 + """ ADDRESS_RESTRICTED_CONE = 3 + """ + C无法与A通信,因为A从来没有和C通信过.而B也只能用它的210.15.27.166:2000与A的192.168.0.4:5000通信,因为A也从来没有和B的其他端口通信过.该类型NAT是端口受限的. + """ PORT_RESTRICTED_CONE = 4 + """ + 上面3种类型,统称为Cone NAT,有一个共同点:只要是从同一个内部地址和端口出来的包,NAT都将它转换成同一个外部地址和端口.但是Symmetric有点不同,具体表现在: 只要是从同一个内部地址和端口出来,且到同一个外部目标地址和端口,则NAT也都将它转换成同一个外部地址和端口.但如果从同一个内部地址和端口出来,是 到另一个外部目标地址和端口,则NAT将使用不同的映射,转换成不同的端口(外部地址只有一个,故不变).而且和Port Restricted Cone一样,只有曾经收到过内部地址发来包的外部地址,才能通过NAT映射后的地址向该内部地址发包. + """ SYMMETRIC = 5 + """ + 端口分配是随机的,无法确定下一次NAT映射端口. + """ UNKNOWN = 6 diff --git a/src/frontends/gamespy/protocols/natneg/contracts/results.py b/src/frontends/gamespy/protocols/natneg/contracts/results.py index 80bac7726..94f14626f 100644 --- a/src/frontends/gamespy/protocols/natneg/contracts/results.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/results.py @@ -15,8 +15,8 @@ class ConnectResult(ResultBase): finished: ConnectPacketStatus = ConnectPacketStatus.NO_ERROR ip: str port: int - version: bytes - cookie: bytes + version: int + cookie: int packet_type: ResponseType = ResponseType.CONNECT From 79759e9155ade70a8086333453ac86702c8b3f1b Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Fri, 13 Jun 2025 08:03:42 +0000 Subject: [PATCH 189/231] Update[gtr]: added nat connection logic --- .../library/abstractions/handler_base.py | 9 ++- src/backends/library/database/pg_orm.py | 1 + .../gamespy/game_traffic_relay/data.py | 31 +++++++- .../gamespy/game_traffic_relay/handlers.py | 38 ++++++++++ .../gamespy/game_traffic_relay/requests.py | 12 +++ src/backends/protocols/gamespy/natneg/data.py | 29 ++++++- .../protocols/gamespy/natneg/handlers.py | 26 ++++--- .../routers/gamespy/game_traffic_relay.py | 27 +++++++ src/backends/routers/gamespy/gstats.py | 18 ++++- .../gamespy/natneg/nat_detection_tests.py | 9 +++ src/backends/urls.py | 1 + .../library/abstractions/server_launcher.py | 17 +++++ .../gamespy/library/extentions/schedular.py | 33 ++++++++ .../protocols/chat/applications/client.py | 6 +- .../game_traffic_relay/applications/broker.py | 5 ++ .../game_traffic_relay/applications/client.py | 72 ++++++++++++++++++ .../applications/connection_listener.py | 21 ----- .../applications/handlers.py | 22 ++++++ .../game_traffic_relay/applications/router.py | 10 --- .../applications/server_launcher.py | 76 +++++++++++++++++++ .../applications/switcher.py | 35 +++++++++ .../game_traffic_relay/contracts/general.py | 7 ++ .../protocols/natneg/applications/handlers.py | 6 +- .../protocols/natneg/applications/switcher.py | 4 - 24 files changed, 451 insertions(+), 64 deletions(-) create mode 100644 src/backends/protocols/gamespy/game_traffic_relay/requests.py create mode 100644 src/backends/routers/gamespy/game_traffic_relay.py create mode 100644 src/frontends/gamespy/library/extentions/schedular.py create mode 100644 src/frontends/gamespy/protocols/game_traffic_relay/applications/broker.py create mode 100644 src/frontends/gamespy/protocols/game_traffic_relay/applications/client.py delete mode 100644 src/frontends/gamespy/protocols/game_traffic_relay/applications/connection_listener.py create mode 100644 src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py delete mode 100644 src/frontends/gamespy/protocols/game_traffic_relay/applications/router.py create mode 100644 src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py create mode 100644 src/frontends/gamespy/protocols/game_traffic_relay/applications/switcher.py diff --git a/src/backends/library/abstractions/handler_base.py b/src/backends/library/abstractions/handler_base.py index 5c58c71e8..63fe81160 100644 --- a/src/backends/library/abstractions/handler_base.py +++ b/src/backends/library/abstractions/handler_base.py @@ -10,7 +10,6 @@ import logging - class HandlerBase: """ The ultimate handler base of backend service @@ -18,7 +17,7 @@ class HandlerBase: _request: RequestBase _result: Optional[ResultBase] - _response: Response + _response: Optional[Response] """ the response using to wrap data """ @@ -28,10 +27,12 @@ class HandlerBase: """ @property - def response(self) -> dict: + def response(self) -> Optional[dict]: """ the dict response which send to client """ + if self._response is None: + return None return self._response.to_json_dict() def __init__(self, request: RequestBase) -> None: @@ -40,7 +41,7 @@ def __init__(self, request: RequestBase) -> None: # decoupling the logging in home.py self.logger = logging.getLogger("backend") self._result = None - self._response = OKResponse() + self._response = None def handle(self) -> None: self._request_check() diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index d0f936e30..5e5b100a4 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -227,6 +227,7 @@ class RelayServerCaches(Base): public_ip_address = Column(String, nullable=False) public_port = Column(Integer, nullable=False) client_count = Column(Integer, nullable=False) + update_time = Column(DateTime, nullable=False) class ChatChannelCaches(Base): diff --git a/src/backends/protocols/gamespy/game_traffic_relay/data.py b/src/backends/protocols/gamespy/game_traffic_relay/data.py index 8750794d6..9ea99f04c 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/data.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/data.py @@ -1,8 +1,21 @@ - +from datetime import datetime +from typing import Optional from uuid import UUID from backends.library.database.pg_orm import PG_SESSION, RelayServerCaches +def search_relay_server(server_id: UUID, server_ip: str) -> Optional[RelayServerCaches]: + result = ( + PG_SESSION.query(RelayServerCaches) + .where( + RelayServerCaches.server_id == server_id, + RelayServerCaches.public_ip_address == server_ip, + ) + .first() + ) + return result + + def get_available_relay_serves() -> list[RelayServerCaches]: """ Return @@ -14,6 +27,11 @@ def get_available_relay_serves() -> list[RelayServerCaches]: def update_relay_server(info: RelayServerCaches): + info.update_time = datetime.now() # type: ignore + PG_SESSION.commit() + + +def add_relay_server(info: RelayServerCaches): PG_SESSION.add(info) PG_SESSION.commit() @@ -22,7 +40,14 @@ def delete_relay_server(server_id: UUID, ip_address: str, port: int): assert isinstance(server_id, UUID) assert isinstance(ip_address, str) assert isinstance(port, int) - info = PG_SESSION.query(RelayServerCaches).where( - RelayServerCaches.server_id == server_id, RelayServerCaches.public_ip_address == ip_address, RelayServerCaches.public_port == port).first() + info = ( + PG_SESSION.query(RelayServerCaches) + .where( + RelayServerCaches.server_id == server_id, + RelayServerCaches.public_ip_address == ip_address, + RelayServerCaches.public_port == port, + ) + .first() + ) PG_SESSION.delete(info) PG_SESSION.commit() diff --git a/src/backends/protocols/gamespy/game_traffic_relay/handlers.py b/src/backends/protocols/gamespy/game_traffic_relay/handlers.py index e69de29bb..cbf20d69d 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/handlers.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/handlers.py @@ -0,0 +1,38 @@ +from datetime import datetime +import logging +from backends.library.abstractions.contracts import OKResponse +from backends.library.abstractions.handler_base import HandlerBase +from backends.library.database.pg_orm import RelayServerCaches +from backends.protocols.gamespy.game_traffic_relay.requests import ( + UpdateGTRServiceRequest, +) + +import backends.protocols.gamespy.game_traffic_relay.data as data + + +class UpdateGTRServiceHandler(HandlerBase): + _request: UpdateGTRServiceRequest + + def __init__(self, request: UpdateGTRServiceRequest) -> None: + assert isinstance(request, UpdateGTRServiceRequest) + self._request = request + self.logger = logging.getLogger("backend") + self._result = None + self._response = OKResponse() + + def _data_operate(self) -> None: + info = data.search_relay_server( + self._request.server_id, self._request.public_ip_address + ) + if info is None: + info = RelayServerCaches( + server_id=self._request.server_id, + public_ip_address=self._request.public_ip_address, + public_port=self._request.public_port, + client_count=self._request.client_count, + update_time=datetime.now(), + ) + data.add_relay_server(info) + else: + # refresh update time + data.update_relay_server(info) diff --git a/src/backends/protocols/gamespy/game_traffic_relay/requests.py b/src/backends/protocols/gamespy/game_traffic_relay/requests.py new file mode 100644 index 000000000..bae52e113 --- /dev/null +++ b/src/backends/protocols/gamespy/game_traffic_relay/requests.py @@ -0,0 +1,12 @@ +from uuid import UUID +from pydantic import BaseModel + +""" +There are 2 UpdateGTRServiceRequest class +The other one is in frontends/gamespy/protocols/game_traffic_relay/contracts/general.py +""" +class UpdateGTRServiceRequest(BaseModel): + server_id: UUID + public_ip_address: str + public_port: int + client_count: int diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py index a0826963b..05638fdab 100644 --- a/src/backends/protocols/gamespy/natneg/data.py +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -1,5 +1,11 @@ from datetime import datetime, timedelta -from backends.library.database.pg_orm import PG_SESSION, InitPacketCaches, NatFailCaches +from typing import Optional +from backends.library.database.pg_orm import ( + PG_SESSION, + InitPacketCaches, + NatFailCaches, + RelayServerCaches, +) from frontends.gamespy.protocols.natneg.aggregations.enums import NatClientIndex @@ -76,7 +82,6 @@ def get_nat_fail_info(info: NatFailCaches): return result - def get_nat_fail_info_by_ip(public_ip1: str, public_ip2: str) -> list[NatFailCaches]: result = ( PG_SESSION.query(NatFailCaches) @@ -87,3 +92,23 @@ def get_nat_fail_info_by_ip(public_ip1: str, public_ip2: str) -> list[NatFailCac .all() ) return result + + +def get_game_traffic_relay_servers( + number: Optional[int] = None, +) -> list[RelayServerCaches]: + if number is None: + result = ( + PG_SESSION.query(RelayServerCaches) + .order_by(RelayServerCaches.client_count.desc()) + .all() + ) + else: + assert isinstance(number, int) + result = ( + PG_SESSION.query(RelayServerCaches) + .order_by(RelayServerCaches.client_count.desc()) + .limit(number) + .all() + ) + return result diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py index 2d3871457..a707a4648 100644 --- a/src/backends/protocols/gamespy/natneg/handlers.py +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -70,7 +70,9 @@ def _data_operate(self) -> None: else: self._helper1 = NatProtocolHelper(init_infos_1) self._helper2 = NatProtocolHelper(init_infos_2) - self._strategy = NatProtocolHelper.choose_nat_strategy(self._helper1, self._helper2) + self._strategy = NatProtocolHelper.choose_nat_strategy( + self._helper1, self._helper2 + ) def _result_construct(self) -> None: if self._strategy == NatStrategy.USE_PRIVATE_IP: @@ -89,13 +91,15 @@ def _result_construct(self) -> None: ) elif self._strategy == NatStrategy.USE_GAME_TRAFFIC_RALEY: - self._use_game_traffic_relay() - - def _use_game_traffic_relay(self): - raise NotImplementedError() - - def _use_public_address(self): - raise NotImplementedError() - - def _use_private_address(self): - raise NotImplementedError() + # get a small number of players server from database + relay_servers = data.get_game_traffic_relay_servers(5) + # select strategy to choose one gtr server + rs = relay_servers[0] + assert isinstance(rs.public_ip_address, str) + assert isinstance(rs.public_port, int) + self._result = ConnectResult( + ip=rs.public_ip_address, + port=rs.public_port, + version=self._helper2.version, + cookie=self._helper2.cookie, + ) diff --git a/src/backends/routers/gamespy/game_traffic_relay.py b/src/backends/routers/gamespy/game_traffic_relay.py new file mode 100644 index 000000000..e879a24a1 --- /dev/null +++ b/src/backends/routers/gamespy/game_traffic_relay.py @@ -0,0 +1,27 @@ +from fastapi import APIRouter + +from backends.protocols.gamespy.game_traffic_relay.handlers import ( + UpdateGTRServiceHandler, +) +from backends.protocols.gamespy.game_traffic_relay.requests import ( + UpdateGTRServiceRequest, +) +from backends.urls import GAME_TRAFFIC_RELAY + +router = APIRouter() + + +@router.post(f"{GAME_TRAFFIC_RELAY}/heartbeat") +def heartbeat(request: UpdateGTRServiceRequest): + handler = UpdateGTRServiceHandler(request) + handler.handle() + return + + +if __name__ == "__main__": + import uvicorn + from fastapi import FastAPI + + app = FastAPI() + app.include_router(router) + uvicorn.run(app, host="0.0.0.0", port=8080) diff --git a/src/backends/routers/gamespy/gstats.py b/src/backends/routers/gamespy/gstats.py index 647314ce5..959689247 100644 --- a/src/backends/routers/gamespy/gstats.py +++ b/src/backends/routers/gamespy/gstats.py @@ -1,5 +1,20 @@ from fastapi import APIRouter -from backends.protocols.gamespy.game_status.handlers import * +from backends.protocols.gamespy.game_status.handlers import ( + AuthGameHandler, + AuthPlayerHandler, + GetPlayerDataHandler, + NewGameHandler, + SetPlayerDataHandler, + UpdateGameHandler, +) +from backends.protocols.gamespy.game_status.requests import ( + AuthGameRequest, + AuthPlayerRequest, + GetPlayerDataRequest, + NewGameRequest, + SetPlayerDataRequest, + UpdateGameRequest, +) from backends.urls import GAMESTATUS router = APIRouter() @@ -50,6 +65,7 @@ def updaet_game(request: UpdateGameRequest): if __name__ == "__main__": import uvicorn from fastapi import FastAPI + app = FastAPI() app.include_router(router) uvicorn.run(app, host="0.0.0.0", port=8080) diff --git a/src/backends/tests/gamespy/natneg/nat_detection_tests.py b/src/backends/tests/gamespy/natneg/nat_detection_tests.py index deaecbfbd..3ad615cb7 100644 --- a/src/backends/tests/gamespy/natneg/nat_detection_tests.py +++ b/src/backends/tests/gamespy/natneg/nat_detection_tests.py @@ -1,3 +1,4 @@ +from datetime import datetime import unittest from backends.library.database.pg_orm import InitPacketCaches @@ -23,6 +24,7 @@ def test_public_ip(self): public_port=1, private_ip="10.0.0.1", private_port=0, + update_time=datetime.now(), ), InitPacketCaches( port_type=NatPortType.NN1, @@ -34,6 +36,7 @@ def test_public_ip(self): public_port=2, private_ip="10.0.0.1", private_port=0, + update_time=datetime.now(), ), InitPacketCaches( port_type=NatPortType.NN2, @@ -45,6 +48,7 @@ def test_public_ip(self): public_port=2, private_ip="10.0.0.1", private_port=2, + update_time=datetime.now(), ), InitPacketCaches( port_type=NatPortType.NN3, @@ -56,6 +60,7 @@ def test_public_ip(self): public_port=2, private_ip="10.0.0.1", private_port=2, + update_time=datetime.now(), ), ] @@ -81,6 +86,7 @@ def test_full_cone(self): public_port=1, private_ip="192.168.1.1", private_port=0, + update_time=datetime.now(), ), InitPacketCaches( port_type=NatPortType.NN1, @@ -92,6 +98,7 @@ def test_full_cone(self): public_port=2, private_ip="192.168.1.1", private_port=0, + update_time=datetime.now(), ), InitPacketCaches( port_type=NatPortType.NN2, @@ -103,6 +110,7 @@ def test_full_cone(self): public_port=2, private_ip="192.168.1.1", private_port=2, + update_time=datetime.now(), ), InitPacketCaches( port_type=NatPortType.NN3, @@ -114,6 +122,7 @@ def test_full_cone(self): public_port=2, private_ip="192.168.1.1", private_port=2, + update_time=datetime.now(), ), ] diff --git a/src/backends/urls.py b/src/backends/urls.py index bd9e1c242..1a1de63c7 100644 --- a/src/backends/urls.py +++ b/src/backends/urls.py @@ -7,3 +7,4 @@ GAMESTATUS = "/GameSpy/GameStatus" CHAT = "/GameSpy/Chat" WEB_SERVICES = "/GameSpy/WebServices" +GAME_TRAFFIC_RELAY = "/GameSpy/GameTrafficRelay" diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index 4619147b6..e2e08ca1f 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -2,6 +2,7 @@ from typing import Optional from frontends.gamespy.library.abstractions.connections import NetworkServerBase from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.library.extentions.schedular import Schedular from frontends.gamespy.library.log.log_manager import LogManager, LogWriter from frontends.gamespy.library.configs import CONFIG, ServerConfig import pyfiglet @@ -30,6 +31,7 @@ class ServerLauncherBase: config: Optional[ServerConfig] logger: Optional[LogWriter] server: Optional[NetworkServerBase] + schedular: Schedular def __init__(self): self.server = None @@ -65,9 +67,13 @@ def _launch_server(self) -> None: if self.server is None: raise UniSpyException("Create network server in child class") print("Press Ctrl+C to Quit") + self._heartbeat_to_backend() self.server.start() def _connect_to_backend(self): + """ + check backend availability + """ if CONFIG.unittest.is_collect_request: return try: @@ -87,6 +93,17 @@ def _connect_to_backend(self): f"backend server: {CONFIG.backend.url} not available." ) + def _heartbeat_to_backend(self): + """ + send heartbeat info to backend to keep the infomation update + """ + #! temperarily use connect to backend function + self.schedular = Schedular(self._connect_to_backend, 30) + self.schedular.start() + + def _test(self): + print("test") + def _create_logger(self): assert self.config is not None short_name = _SERVER_FULL_SHORT_NAME_MAPPING[self.config.server_name] diff --git a/src/frontends/gamespy/library/extentions/schedular.py b/src/frontends/gamespy/library/extentions/schedular.py new file mode 100644 index 000000000..894c2c9fd --- /dev/null +++ b/src/frontends/gamespy/library/extentions/schedular.py @@ -0,0 +1,33 @@ +import threading +import time +from typing import Callable + +import schedule + + +class Schedular: + _job_func: Callable + _is_started: bool + _interval: int + + def __init__(self, job_func: Callable, interval: int) -> None: + self._job_func = job_func + self._interval = interval + schedule.every(interval).seconds.do(job_func) + self._is_started = False + + def start(self): + scheduler_thread = threading.Thread(target=self._run_schedule) + scheduler_thread.daemon = True + scheduler_thread.start() + + def _run_schedule(self): + self._is_started = True + while True: + if not self._is_started: + break + schedule.run_pending() + time.sleep(self._interval) + + def stop(self): + self._is_started = False diff --git a/src/frontends/gamespy/protocols/chat/applications/client.py b/src/frontends/gamespy/protocols/chat/applications/client.py index dcb2e43cb..83c49c16f 100644 --- a/src/frontends/gamespy/protocols/chat/applications/client.py +++ b/src/frontends/gamespy/protocols/chat/applications/client.py @@ -9,7 +9,6 @@ from typing import Optional - class ClientInfo: previously_joined_channel: Optional[str] joined_channels: list[str] @@ -39,7 +38,8 @@ def __init__( def _create_switcher(self, buffer: bytes) -> SwitcherBase: from frontends.gamespy.protocols.chat.applications.switcher import Switcher - return Switcher(self, buffer.decode()) + switcher = Switcher(self, buffer.decode()) + return switcher def on_connected(self) -> None: self.start_brocker() @@ -63,5 +63,5 @@ def stop_brocker(self): def _process_brocker_message(self, message: str): # responsible for receive message and send out - assert message + assert isinstance(message, str) self.connection.send(message.encode()) diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/broker.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/broker.py new file mode 100644 index 000000000..578eaffcf --- /dev/null +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/broker.py @@ -0,0 +1,5 @@ +from frontends.gamespy.library.network.brockers import WebSocketBrocker + + +class Broker(WebSocketBrocker): + pass diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/client.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/client.py new file mode 100644 index 000000000..0684f9bfe --- /dev/null +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/client.py @@ -0,0 +1,72 @@ +import frontends.gamespy.protocols.natneg.applications.client as natneg + + +class ClientInfo: + cookie: int + pass + + +class ConnectionListener: + pool: dict[int, list["Client"]] = {} + + def is_client_exist(self, cookie: int, client: "Client"): + if cookie not in self.pool: + return False + else: + clients = self.pool[cookie] + # if current client is not in the pair + if client not in clients: + return False + + def add_client(self, cookie: int, client: "Client"): + if cookie not in self.pool: + self.pool[cookie] = [client] + else: + clients = self.pool[cookie] + # if current clients is in the pair + if len(clients) == 2 and client in clients: + return + # if current client is not in the pair + if len(clients) == 1 and client not in clients: + clients = (clients[0], client) + + def get_another_client(self, cookie: int, client: "Client"): + if cookie not in self.pool: + raise ValueError("cookie not add to pool") + else: + clients = self.pool[cookie] + # if current client is not in the pair + if client not in clients: + raise ValueError("client not in cookie") + if len(clients) != 2: + raise ValueError("2 clients are not ready") + return clients[0] if clients[1] == client else clients[1] + + def is_both_client_ready(self, cookie: int, client: "Client") -> bool: + clients = self.pool[cookie] + if len(clients) != 2: + return False + elif self.is_client_exist(cookie, client): + return True + else: + return False + + +class Client(natneg.Client): + info: ClientInfo + client_pool: dict[str, "Client"] = {} + listener: ConnectionListener = ConnectionListener() + + def on_received(self, buffer: bytes) -> None: + """ + when we receive udp message, we check whether the client pair is ready. + if client is ready we send the following data to the another client + """ + if self.listener.is_both_client_ready(self.info.cookie, self): + another_client = self.listener.get_another_client(self.info.cookie, self) + another_client.connection.send(buffer) + self.log_info( + f"[{self.connection.ip_endpoint}] => [{another_client.connection.ip_endpoint}] {buffer}" + ) + else: + super().on_received(buffer) diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/connection_listener.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/connection_listener.py deleted file mode 100644 index f09b94f8f..000000000 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/connection_listener.py +++ /dev/null @@ -1,21 +0,0 @@ -import socketserver - -from frontends.gamespy.library.network.udp_handler import UdpHandler - - -class ConnectionListener: - cookie: bytes - ip_addr: str - port: int - - def __init__(self, ip_addr: str, port: int) -> None: - assert isinstance(ip_addr, str) - assert isinstance(port, int) - self.ip_addr = ip_addr - self.port = port - - def start(self): - with socketserver.ThreadingUDPServer( - (self.ip_addr, self.port), UdpHandler - ) as s: - s.serve_forever() diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py new file mode 100644 index 000000000..8e42ff3db --- /dev/null +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py @@ -0,0 +1,22 @@ +from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client +from frontends.gamespy.protocols.natneg.abstractions.handlers import CmdHandlerBase +from frontends.gamespy.protocols.natneg.contracts.requests import PingRequest + + +class PingHandler(CmdHandlerBase): + _request: PingRequest + _client: Client + + def __init__(self, client: Client, request: PingRequest) -> None: + assert isinstance(request, PingRequest) + super().__init__(client, request) + + def _data_operate(self) -> None: + if not self._client.listener.is_client_exist( + self._request.cookie, self._client + ): + self._client.listener.add_client(self._request.cookie, self._client) + self._client.info.cookie = self._request.cookie + self._client.log_info( + f"Add client to listener cookie:{self._request.cookie}" + ) diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/router.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/router.py deleted file mode 100644 index 39ef06038..000000000 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/router.py +++ /dev/null @@ -1,10 +0,0 @@ -from fastapi import FastAPI -from backends.urls import * -from frontends.gamespy.protocols.game_traffic_relay.contracts.general import InitPacketInfo - -app = FastAPI() - - -@app.post("/GetNatNegotiationInfo") -def get_natneg_info(request: InitPacketInfo): - data = request.json diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py new file mode 100644 index 000000000..fe558fb4e --- /dev/null +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py @@ -0,0 +1,76 @@ +import requests +from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.library.network.udp_handler import UdpServer +from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client +from frontends.gamespy.protocols.game_traffic_relay.contracts.general import ( + UpdateGTRServiceRequest, +) + + +class ServerLauncher(ServerLauncherBase): + # todo: implement the websocket brocker to receive the info from backends + def __init__(self) -> None: + super().__init__() + self.config = CONFIG.servers["GameTrafficRelay"] + + def _launch_server(self): + assert self.config is not None + assert self.logger is not None + self.server = UdpServer(self.config, Client, self.logger) + super()._launch_server() + + def _GTR_heartbeat(self): + assert self.config + req = UpdateGTRServiceRequest( + server_id=self.config.server_id, + public_ip_address=self.config.public_address, + public_port=self.config.listening_port, + client_count=len(Client.client_pool), + ) + try: + resp = requests.post( + url=CONFIG.backend.url + "/heartbeat", data=req.model_dump_json() + ) + if resp.status_code == 200: + data = resp.json() + if data["status"] != "online": + raise UniSpyException( + f"backend server: {CONFIG.backend.url} not available." + ) + except requests.ConnectionError: + raise UniSpyException( + f"backend server: {CONFIG.backend.url} not available." + ) + + def _heartbeat_to_backend(self): + self._GTR_heartbeat() + super()._heartbeat_to_backend() + + def _connect_to_backend(self): + """ + check backend availability + """ + super()._connect_to_backend() + if CONFIG.unittest.is_collect_request: + return + try: + # post our server config to backends to register + assert self.config is not None + data = self.config.model_dump_json() + import json + + data = json.loads(data) + data["clients"] = len(Client.client_pool) + resp = requests.post(url=CONFIG.backend.url + "/heartbeat", json=data) + if resp.status_code == 200: + data = resp.json() + if data["status"] != "online": + raise UniSpyException( + f"backend server: {CONFIG.backend.url} not available." + ) + except requests.ConnectionError: + raise UniSpyException( + f"backend server: {CONFIG.backend.url} not available." + ) diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/switcher.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/switcher.py new file mode 100644 index 000000000..5adf68cac --- /dev/null +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/switcher.py @@ -0,0 +1,35 @@ +from typing import Optional +from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client +from frontends.gamespy.protocols.game_traffic_relay.applications.handlers import ( + PingHandler) +from frontends.gamespy.protocols.natneg.aggregations.enums import RequestType +from frontends.gamespy.protocols.natneg.contracts.requests import PingRequest + + +class Switcher(SwitcherBase): + _raw_request: bytes + _client: Client + + def __init__(self, client: Client, raw_request: bytes) -> None: + super().__init__(client, raw_request) + assert issubclass(type(client), Client) + assert isinstance(raw_request, bytes) + + def _process_raw_request(self) -> None: + name = self._raw_request[7] + if name not in RequestType: + self._client.log_debug(f"Request: {name} is not a valid request.") + return + self._requests.append((RequestType(name), self._raw_request)) + + def _create_cmd_handlers( + self, name: RequestType, raw_request: bytes + ) -> Optional[PingHandler | None]: + assert isinstance(name, RequestType) + assert isinstance(raw_request, bytes) + match name: + case RequestType.PING: + return PingHandler(self._client, PingRequest(raw_request)) + case _: + return None diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/contracts/general.py b/src/frontends/gamespy/protocols/game_traffic_relay/contracts/general.py index fe09da232..0cecb0969 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/contracts/general.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/contracts/general.py @@ -15,3 +15,10 @@ class InitPacketInfo(BaseModel): public_port: int private_ip: str private_port: int + + +class UpdateGTRServiceRequest(BaseModel): + server_id: UUID4 + public_ip_address: str + public_port: int + client_count: int diff --git a/src/frontends/gamespy/protocols/natneg/applications/handlers.py b/src/frontends/gamespy/protocols/natneg/applications/handlers.py index a74a333cc..6ab2c134b 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/handlers.py +++ b/src/frontends/gamespy/protocols/natneg/applications/handlers.py @@ -7,7 +7,6 @@ ErtAckRequest, InitRequest, NatifyRequest, - PingRequest, ReportRequest, ) from frontends.gamespy.protocols.natneg.contracts.responses import ( @@ -143,10 +142,7 @@ def _response_construct(self): self._response = NatifyResponse(self._request, self._result) -class PingHandler(CmdHandlerBase): - def __init__(self, client: Client, request: PingRequest) -> None: - super().__init__(client, request) - raise NotImplementedError() + class ReportHandler(CmdHandlerBase): diff --git a/src/frontends/gamespy/protocols/natneg/applications/switcher.py b/src/frontends/gamespy/protocols/natneg/applications/switcher.py index 612f79142..5012e23b1 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/switcher.py +++ b/src/frontends/gamespy/protocols/natneg/applications/switcher.py @@ -7,7 +7,6 @@ ConnectRequest, ErtAckRequest, InitRequest, - PingRequest, ReportRequest, ) from frontends.gamespy.protocols.natneg.aggregations.enums import RequestType @@ -16,7 +15,6 @@ ConnectHandler, ErtAckHandler, InitHandler, - PingHandler, ReportHandler, ) @@ -52,8 +50,6 @@ def _create_cmd_handlers( return ErtAckHandler(self._client, ErtAckRequest(raw_request)) case RequestType.CONNECT: return ConnectHandler(self._client, ConnectRequest(raw_request)) - case RequestType.PING: - return PingHandler(self._client, PingRequest(raw_request)) case RequestType.ADDRESS_CHECK: return AddressCheckHandler( self._client, AddressCheckRequest(raw_request) From 2c4108dc41d08cff3a58022086eaef09b827715a Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Wed, 18 Jun 2025 07:37:01 +0000 Subject: [PATCH 190/231] Fix: websocket connect issue --- .../gamespy/chat/brocker_manager.py} | 10 ++++- src/backends/routers/gamespy/chat.py | 7 ++- src/backends/routers/home.py | 3 +- src/backends/tests/gamespy/chat/lib_tests.py | 5 ++- .../gamespy/library/abstractions/brocker.py | 3 +- .../gamespy/library/network/brockers.py | 45 +++++++------------ .../protocols/chat/applications/broker.py | 7 +++ .../protocols/chat/applications/client.py | 8 +++- .../game_traffic_relay/applications/client.py | 22 ++++++--- .../applications/handlers.py | 6 ++- .../applications/server_launcher.py | 17 ++++++- .../protocols/natneg/contracts/requests.py | 11 ++++- .../gamespy/game_traffic_relay/__init__.py | 0 .../game_traffic_relay/handler_tests.py | 35 +++++++++++++++ .../game_traffic_relay/mock_objects.py | 32 +++++++++++++ .../tests/gamespy/library/mock_objects.py | 7 +-- src/requirements.txt | 2 +- 17 files changed, 167 insertions(+), 53 deletions(-) rename src/backends/{library/brockers/chat.py => protocols/gamespy/chat/brocker_manager.py} (87%) create mode 100644 src/frontends/gamespy/protocols/chat/applications/broker.py create mode 100644 src/frontends/tests/gamespy/game_traffic_relay/__init__.py create mode 100644 src/frontends/tests/gamespy/game_traffic_relay/handler_tests.py create mode 100644 src/frontends/tests/gamespy/game_traffic_relay/mock_objects.py diff --git a/src/backends/library/brockers/chat.py b/src/backends/protocols/gamespy/chat/brocker_manager.py similarity index 87% rename from src/backends/library/brockers/chat.py rename to src/backends/protocols/gamespy/chat/brocker_manager.py index bb1d098e4..fe8550c15 100644 --- a/src/backends/library/brockers/chat.py +++ b/src/backends/protocols/gamespy/chat/brocker_manager.py @@ -1,3 +1,4 @@ +import asyncio from typing import cast from backends.library.abstractions.websocket_manager import ( WebSocketClient, @@ -7,6 +8,8 @@ # from library.network.brockers import RedisBrocker from fastapi import WebSocket +from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage + class ChatWebSocketClient(WebSocketClient): channels: list[str] @@ -74,8 +77,11 @@ def disconnect(self, ws: WebSocket): del channel_dict[client.ip_port] super().disconnect(ws) - def channel_broad_cast(self): - pass + def channel_broad_cast(self, name: str, message: BrockerMessage): + if name in self.channel_info: + channel = self.channel_info[name] + for nick, ws in channel.items(): + asyncio.run(ws.ws.send_json(message.model_dump())) MANAGER = ChatWebSocketManager() diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 5ca5fac6d..ea22d4823 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -1,4 +1,4 @@ -from backends.library.brockers.chat import MANAGER +from backends.protocols.gamespy.chat.brocker_manager import MANAGER from backends.protocols.gamespy.chat.handlers import ( CdKeyHandler, GetKeyHandler, @@ -36,7 +36,6 @@ ) from backends.urls import CHAT from fastapi import APIRouter, FastAPI, WebSocket, WebSocketDisconnect - from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage router = APIRouter() @@ -47,7 +46,7 @@ def check_request(request: dict) -> BrockerMessage: return msg -@router.websocket(f"{CHAT}/Broker") +@router.websocket(f"{CHAT}/ws") async def websocket_endpoint(ws: WebSocket): await ws.accept() if isinstance(ws, WebSocket) and ws.client is not None: @@ -59,7 +58,7 @@ async def websocket_endpoint(ws: WebSocket): if r.message is None: return # currently we broadcast all message - MANAGER.broadcast(r.model_dump()) + MANAGER.channel_broad_cast(r.channel_name, r) except WebSocketDisconnect: if ws.client is not None: diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index c063ef26a..0989a4afb 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -10,6 +10,7 @@ from frontends.gamespy.library.log.log_manager import LogManager from frontends.gamespy.library.configs import ServerConfig from backends.routers.gamespy import ( + chat, gstats, natneg, presence_connection_manager, @@ -21,7 +22,7 @@ app = FastAPI() -# app.include_router(chat.router) +app.include_router(chat.router) app.include_router(gstats.router) app.include_router(natneg.router) app.include_router(presence_connection_manager.router) diff --git a/src/backends/tests/gamespy/chat/lib_tests.py b/src/backends/tests/gamespy/chat/lib_tests.py index 322ea9c39..96b60c182 100644 --- a/src/backends/tests/gamespy/chat/lib_tests.py +++ b/src/backends/tests/gamespy/chat/lib_tests.py @@ -4,7 +4,10 @@ from fastapi import WebSocket from fastapi.datastructures import Address -from backends.library.brockers.chat import ChatWebSocketClient, ChatWebSocketManager +from backends.protocols.gamespy.chat.brocker_manager import ( + ChatWebSocketClient, + ChatWebSocketManager, +) class WebSocketMock: diff --git a/src/frontends/gamespy/library/abstractions/brocker.py b/src/frontends/gamespy/library/abstractions/brocker.py index 53b2f852b..46468c071 100644 --- a/src/frontends/gamespy/library/abstractions/brocker.py +++ b/src/frontends/gamespy/library/abstractions/brocker.py @@ -27,7 +27,8 @@ def subscribe(self): pass @final - def receive_message(self, message): + def receive_message(self, message: dict): + assert isinstance(message, dict) self._call_back_func(message) pass diff --git a/src/frontends/gamespy/library/network/brockers.py b/src/frontends/gamespy/library/network/brockers.py index b4c63f2e5..8b82592eb 100644 --- a/src/frontends/gamespy/library/network/brockers.py +++ b/src/frontends/gamespy/library/network/brockers.py @@ -1,15 +1,14 @@ import threading -from typing import Optional, Callable +from typing import Callable from uuid import UUID from redis import Redis -import websocket +from websockets import ConnectionClosed from frontends.gamespy.library.abstractions.brocker import BrockerBase from redis.client import PubSub from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage - -websocket.enableTrace(True) +from websockets.sync.client import connect, ClientConnection class RedisBrocker(BrockerBase): @@ -45,30 +44,21 @@ def publish_message(self, message): class WebSocketBrocker(BrockerBase): - _subscriber: websocket.WebSocketApp - _publisher: Optional[websocket.WebSocket] = None - - def __init__(self, name: str, url: str, call_back_func: Callable) -> None: - super().__init__(name, url, call_back_func) - self._subscriber = websocket.WebSocketApp( - url, - on_message=lambda _, m: self.receive_message(m), - on_error=print, - on_close=print, - ) - self._subscriber.on_open = self._on_open - - def _on_open(self, ws): - self._publisher = ws - - def _on_message(self, _, message): - threading.Thread(target=self.receive_message, args=message).start() + _subscriber: ClientConnection + _publisher: ClientConnection def subscribe(self): - threading.Thread(target=self._subscriber.run_forever).start() - # # wait for connection establish - if self._publisher is None: - raise UniSpyException("brocker backend is not available") + self._publisher = self._subscriber = connect(self.url) + th = threading.Thread(target=self._listen) + th.start() + + def _listen(self): + try: + while True: + message = self._subscriber.recv() + self._call_back_func(message) + except ConnectionClosed: + raise UniSpyException("websocket connection is not established") def unsubscribe(self): self._subscriber.close() @@ -82,7 +72,7 @@ def publish_message(self, message: BrockerMessage): if __name__ == "__main__": ws = WebSocketBrocker( name="test_channel", - url="ws://127.0.0.1:8080/GameSpy/Chat/Channel", + url="ws://127.0.0.1:8080/GameSpy/Chat/ws", call_back_func=print, ) ws.subscribe() @@ -94,4 +84,3 @@ def publish_message(self, message: BrockerMessage): message="hello", ) ws.publish_message(msg) - diff --git a/src/frontends/gamespy/protocols/chat/applications/broker.py b/src/frontends/gamespy/protocols/chat/applications/broker.py new file mode 100644 index 000000000..4e94dc890 --- /dev/null +++ b/src/frontends/gamespy/protocols/chat/applications/broker.py @@ -0,0 +1,7 @@ + + +from frontends.gamespy.library.network.brockers import WebSocketBrocker + + +# class Brocker(WebSocketBrocker): + diff --git a/src/frontends/gamespy/protocols/chat/applications/client.py b/src/frontends/gamespy/protocols/chat/applications/client.py index 83c49c16f..2aef8142d 100644 --- a/src/frontends/gamespy/protocols/chat/applications/client.py +++ b/src/frontends/gamespy/protocols/chat/applications/client.py @@ -1,3 +1,4 @@ +import json from frontends.gamespy.library.abstractions.client import ClientBase from frontends.gamespy.library.abstractions.switcher import SwitcherBase @@ -8,6 +9,8 @@ from typing import Optional +from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage + class ClientInfo: previously_joined_channel: Optional[str] @@ -63,5 +66,8 @@ def stop_brocker(self): def _process_brocker_message(self, message: str): # responsible for receive message and send out + # TODO: check whether exception here will cause brocker stop assert isinstance(message, str) - self.connection.send(message.encode()) + j = json.loads(message) + r = BrockerMessage(**j) + self.connection.send(r.message.encode()) diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/client.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/client.py index 0684f9bfe..adcd4e466 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/client.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/client.py @@ -1,8 +1,10 @@ +from datetime import datetime import frontends.gamespy.protocols.natneg.applications.client as natneg class ClientInfo: cookie: int + last_receive_time: datetime pass @@ -28,7 +30,7 @@ def add_client(self, cookie: int, client: "Client"): return # if current client is not in the pair if len(clients) == 1 and client not in clients: - clients = (clients[0], client) + clients.append(client) def get_another_client(self, cookie: int, client: "Client"): if cookie not in self.pool: @@ -46,10 +48,8 @@ def is_both_client_ready(self, cookie: int, client: "Client") -> bool: clients = self.pool[cookie] if len(clients) != 2: return False - elif self.is_client_exist(cookie, client): - return True else: - return False + return True class Client(natneg.Client): @@ -57,16 +57,26 @@ class Client(natneg.Client): client_pool: dict[str, "Client"] = {} listener: ConnectionListener = ConnectionListener() + def __init__( + self, + connection: natneg.UdpConnection, + server_config: natneg.ServerConfig, + logger: natneg.LogWriter, + ): + super().__init__(connection, server_config, logger) + self.info = ClientInfo() + def on_received(self, buffer: bytes) -> None: """ when we receive udp message, we check whether the client pair is ready. if client is ready we send the following data to the another client """ + self.info.last_receive_time = datetime.now() if self.listener.is_both_client_ready(self.info.cookie, self): another_client = self.listener.get_another_client(self.info.cookie, self) another_client.connection.send(buffer) - self.log_info( - f"[{self.connection.ip_endpoint}] => [{another_client.connection.ip_endpoint}] {buffer}" + self.log_network_sending( + f"=> [{another_client.connection.ip_endpoint}] {buffer}" ) else: super().on_received(buffer) diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py index 8e42ff3db..60d65a16a 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py @@ -1,5 +1,5 @@ +from frontends.gamespy.library.abstractions.handler import CmdHandlerBase from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client -from frontends.gamespy.protocols.natneg.abstractions.handlers import CmdHandlerBase from frontends.gamespy.protocols.natneg.contracts.requests import PingRequest @@ -10,12 +10,14 @@ class PingHandler(CmdHandlerBase): def __init__(self, client: Client, request: PingRequest) -> None: assert isinstance(request, PingRequest) super().__init__(client, request) + self._is_fetching = False + self._is_uploading = False def _data_operate(self) -> None: if not self._client.listener.is_client_exist( self._request.cookie, self._client ): - self._client.listener.add_client(self._request.cookie, self._client) + Client.listener.add_client(self._request.cookie, self._client) self._client.info.cookie = self._request.cookie self._client.log_info( f"Add client to listener cookie:{self._request.cookie}" diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py index fe558fb4e..c5e8c1e42 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py @@ -1,7 +1,9 @@ import requests +from frontends.gamespy.library.abstractions.brocker import BrockerBase from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.library.network.brockers import WebSocketBrocker from frontends.gamespy.library.network.udp_handler import UdpServer from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client from frontends.gamespy.protocols.game_traffic_relay.contracts.general import ( @@ -10,10 +12,21 @@ class ServerLauncher(ServerLauncherBase): + _broker: BrockerBase + # todo: implement the websocket brocker to receive the info from backends def __init__(self) -> None: super().__init__() self.config = CONFIG.servers["GameTrafficRelay"] + self._broker = WebSocketBrocker( + name=self.config.server_name, + url=f"{CONFIG.backend.url}/GameTrafficRelay/ws", + call_back_func=self._process_brocker_message, + ) + + def _process_brocker_message(self, message): + # todo handle message here + pass def _launch_server(self): assert self.config is not None @@ -21,7 +34,7 @@ def _launch_server(self): self.server = UdpServer(self.config, Client, self.logger) super()._launch_server() - def _GTR_heartbeat(self): + def _gtr_heartbeat(self): assert self.config req = UpdateGTRServiceRequest( server_id=self.config.server_id, @@ -45,7 +58,7 @@ def _GTR_heartbeat(self): ) def _heartbeat_to_backend(self): - self._GTR_heartbeat() + self._gtr_heartbeat() super()._heartbeat_to_backend() def _connect_to_backend(self): diff --git a/src/frontends/gamespy/protocols/natneg/contracts/requests.py b/src/frontends/gamespy/protocols/natneg/contracts/requests.py index 4a19049bf..741ae179f 100644 --- a/src/frontends/gamespy/protocols/natneg/contracts/requests.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/requests.py @@ -1,4 +1,5 @@ from socket import inet_ntoa +import socket import struct from typing import Optional @@ -22,7 +23,15 @@ class AddressCheckRequest(CommonRequestBase): class PingRequest(RequestBase): - pass + def parse(self) -> None: + self.version = int(self.raw_request[6]) + self.command_name = RequestType(self.raw_request[7]) + self.cookie = int.from_bytes(self.raw_request[8:12], byteorder="little") + self.ip = socket.inet_ntoa(self.raw_request[12:16]) + self.port = int.from_bytes(self.raw_request[16:18]) + # port here is not in little endian + self.got_your_data = bool(self.raw_request[18]) + self.is_finished = bool(self.raw_request[19]) class ConnectAckRequest(RequestBase): diff --git a/src/frontends/tests/gamespy/game_traffic_relay/__init__.py b/src/frontends/tests/gamespy/game_traffic_relay/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/frontends/tests/gamespy/game_traffic_relay/handler_tests.py b/src/frontends/tests/gamespy/game_traffic_relay/handler_tests.py new file mode 100644 index 000000000..181fba1a6 --- /dev/null +++ b/src/frontends/tests/gamespy/game_traffic_relay/handler_tests.py @@ -0,0 +1,35 @@ +import unittest + +from frontends.gamespy.protocols.game_traffic_relay.applications.handlers import ( + PingHandler, +) +from frontends.gamespy.protocols.natneg.contracts.requests import PingRequest +from frontends.tests.gamespy.game_traffic_relay.mock_objects import create_client + + +class HandlerTests(unittest.TestCase): + def test_ping(self): + """ + test whether 2 clients can be binding togather with ping command + """ + ping_raw = ( + b"\xfd\xfc\x1efj\xb2\x03\x07\x00\x00\x02\x9a\xc0\xa8\x01gl\xfd\x00\x00" + ) + client1 = create_client(("127.0.0.1", 1234)) + client1.connection.remote_ip = "127.0.0.1" + client1.connection.remote_port = 1234 + client1._log_prefix = "[127.0.0.1:1234]" + client2 = create_client(("127.0.0.1", 1235)) + client2.connection.remote_ip = "127.0.0.1" + client2.connection.remote_port = 1235 + client2._log_prefix = "[127.0.0.1:1235]" + handler = PingHandler(client1, PingRequest(ping_raw)) + handler.handle() + handler = PingHandler(client2, PingRequest(ping_raw)) + handler.handle() + # cookie length check + self.assertEqual(len(client1.listener.pool), 1) + list(client1.listener.pool.values())[0] + self.assertEqual(len(list(client1.listener.pool.values())[0]), 2) + client1.on_received(ping_raw) + pass diff --git a/src/frontends/tests/gamespy/game_traffic_relay/mock_objects.py b/src/frontends/tests/gamespy/game_traffic_relay/mock_objects.py new file mode 100644 index 000000000..4bfd376dd --- /dev/null +++ b/src/frontends/tests/gamespy/game_traffic_relay/mock_objects.py @@ -0,0 +1,32 @@ +from typing import cast +from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client +from frontends.gamespy.protocols.game_traffic_relay.applications.handlers import ( + PingHandler, +) +from frontends.tests.gamespy.library.mock_objects import ( + ConnectionMock, + LogMock, + RequestHandlerMock, + create_mock_url, +) + + +class ClientMock(Client): + pass + + +def create_client(client_address: tuple = ("192.168.0.1", 0)) -> Client: + CONFIG.unittest.is_raise_except = True + handler = RequestHandlerMock(client_address) + logger = LogMock() + conn = ConnectionMock( + handler=handler, + config=CONFIG.servers["NatNegotiation"], + t_client=ClientMock, + logger=logger, + ) + + config = CONFIG.servers["NatNegotiation"] + create_mock_url(config, PingHandler, {"message": "ok"}) + return cast(Client, conn._client) diff --git a/src/frontends/tests/gamespy/library/mock_objects.py b/src/frontends/tests/gamespy/library/mock_objects.py index 15f84cde7..b35b4cd5d 100644 --- a/src/frontends/tests/gamespy/library/mock_objects.py +++ b/src/frontends/tests/gamespy/library/mock_objects.py @@ -14,10 +14,11 @@ def send(self, data: bytes) -> None: class RequestHandlerMock(socketserver.BaseRequestHandler): - def __init__(self) -> None: - pass + client_address: tuple + + def __init__(self, client_address: tuple = ("192.168.0.1", 0)) -> None: + self.client_address = client_address - client_address: tuple = ("192.168.0.1", 0) pass diff --git a/src/requirements.txt b/src/requirements.txt index beba5ee19..b5b1e8fb2 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -8,4 +8,4 @@ fastapi[standard] == 0.115.4 xmltodict == 0.14.2 responses == 0.25.3 redis == 5.2.0 -websocket-client ==1.8.0 +websockets == 15.0.1 From 9f52247ceb503d28ca676bf490b06c4e7ff60d07 Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Fri, 20 Jun 2025 02:53:15 +0000 Subject: [PATCH 191/231] Fix: database query issue --- common/UniSpy_pg.sql | 2 +- src/backends/library/database/pg_orm.py | 20 +- src/backends/protocols/gamespy/chat/data.py | 381 +++++++------ .../protocols/gamespy/chat/handlers.py | 20 +- src/backends/protocols/gamespy/chat/helper.py | 160 +++--- .../protocols/gamespy/game_status/data.py | 49 +- .../gamespy/game_traffic_relay/data.py | 51 +- src/backends/protocols/gamespy/natneg/data.py | 144 +++-- .../protocols/gamespy/natneg/handlers.py | 35 +- .../presence_connection_manager/data.py | 530 ++++++++++-------- .../gamespy/presence_search_player/data.py | 348 ++++++------ .../presence_search_player/handlers.py | 18 +- .../protocols/gamespy/query_report/data.py | 97 ++-- .../gamespy/query_report/handlers.py | 69 ++- .../protocols/gamespy/web_services/data.py | 200 +++++-- src/backends/routers/home.py | 2 + .../gamespy/query_report/data_fetch_tests.py | 21 +- .../gamespy/library/abstractions/handler.py | 4 +- .../natneg/abstractions/contracts.py | 2 +- .../protocols/natneg/applications/handlers.py | 5 +- .../protocols/natneg/applications/switcher.py | 8 +- 21 files changed, 1212 insertions(+), 954 deletions(-) diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index d6373e7e6..a04d4bc43 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -299,7 +299,7 @@ COMMENT ON TABLE unispy.grouplist IS 'Old games use grouplist to create their ga -- CREATE TABLE unispy.init_packet_caches ( - cookie integer NOT NULL, + cookie bigint NOT NULL, server_id uuid NOT NULL, version integer NOT NULL, port_type smallint NOT NULL, diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 5e5b100a4..4f16bcd8c 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -1,6 +1,7 @@ from frontends.gamespy.library.configs import CONFIG from datetime import datetime from sqlalchemy import ( + BigInteger, Boolean, SmallInteger, Text, @@ -15,7 +16,6 @@ ) from sqlalchemy.orm.session import Session from sqlalchemy.dialects.postgresql import JSONB, INET -from sqlalchemy.orm import sessionmaker from sqlalchemy.types import TypeDecorator from frontends.gamespy.protocols.natneg.aggregations.enums import ( NatClientIndex, @@ -192,7 +192,7 @@ class SakeStorage(Base): class InitPacketCaches(Base): __tablename__ = "init_packet_caches" - cookie = Column(Integer, primary_key=True, nullable=False) + cookie = Column(BigInteger, primary_key=True, nullable=False) server_id = Column(UUID, nullable=False) version = Column(Integer, nullable=False) port_type = Column(IntEnum(NatPortType), nullable=False) @@ -305,19 +305,13 @@ class GameServerCaches(Base): avaliable = Column(Boolean, nullable=True) -def connect_to_db() -> Session: - ENGINE = create_engine(CONFIG.postgresql.url) - session = sessionmaker(bind=ENGINE)() - # Base.metadata.create_all(ENGINE) - with ENGINE.connect() as conn: - conn.execute(text("SELECT 1")).first() - return session - - -PG_SESSION = connect_to_db() +ENGINE = create_engine(CONFIG.postgresql.url) +with ENGINE.connect() as conn: + conn.execute(text("SELECT 1")).first() if __name__ == "__main__": - PG_SESSION.query(Users.userid == 0).all() # type:ignore + with Session(ENGINE) as session: + session.query(Users.userid == 0).all() # type:ignore # profile = Profiles(userid=1, nick="spyguy", # extra_info={}, status=GPStatusCode.OFFLINE) # PG_SESSION.add(profile) diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index dfa894e39..1223d323a 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -3,7 +3,7 @@ from sqlalchemy import Column, func from backends.library.database.pg_orm import ( - PG_SESSION, + ENGINE, ChatChannelCaches, ChatUserCaches, ChatChannelUserCaches, @@ -15,19 +15,23 @@ ChatException, NoSuchNickException, ) +from sqlalchemy.orm import Session def is_nick_exist(nick_name: str) -> bool: - c = PG_SESSION.query(ChatUserCaches.nick_name).count() - if c == 1: - return True - else: - return False + with Session(ENGINE) as session: + + c = session.query(ChatUserCaches.nick_name).count() + if c == 1: + return True + else: + return False def add_nick_cache(cache: ChatUserCaches): - PG_SESSION.add(cache) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.add(cache) + session.commit() def nick_and_email_login( @@ -40,19 +44,20 @@ def nick_and_email_login( assert isinstance(nick_name, str) assert isinstance(email, str) assert isinstance(password_hash, str) - - result = ( - PG_SESSION.query( - Users.userid, Profiles.profileid, Users.emailverified, Users.banned - ) - .join(Profiles, (Users.userid == Profiles.userid)) - .where( - Users.email == email, - Profiles.nick == nick_name, - Users.password == password_hash, + with Session(ENGINE) as session: + + result = ( + session.query( + Users.userid, Profiles.profileid, Users.emailverified, Users.banned + ) + .join(Profiles, (Users.userid == Profiles.userid)) + .where( + Users.email == email, + Profiles.nick == nick_name, + Users.password == password_hash, + ) + .first() ) - .first() - ) if TYPE_CHECKING: result = cast(tuple[int, int, bool, bool], result) if result is None: @@ -70,18 +75,19 @@ def uniquenick_login(uniquenick: str, namespace_id: int) -> tuple[int, int, bool """ assert isinstance(uniquenick, str) assert isinstance(namespace_id, int) - result = ( - PG_SESSION.query( - Users.userid, Profiles.profileid, Users.emailverified, Users.banned - ) - .join(Profiles, (Users.userid == Profiles.userid)) - .join(Profiles, (Profiles.profileid == SubProfiles.profileid)) - .where( - SubProfiles.namespaceid == namespace_id, - SubProfiles.uniquenick == uniquenick, + with Session(ENGINE) as session: + result = ( + session.query( + Users.userid, Profiles.profileid, Users.emailverified, Users.banned + ) + .join(Profiles, (Users.userid == Profiles.userid)) + .join(Profiles, (Profiles.profileid == SubProfiles.profileid)) + .where( + SubProfiles.namespaceid == namespace_id, + SubProfiles.uniquenick == uniquenick, + ) + .first() ) - .first() - ) if result is None: # fmt: off raise ChatException(f"Can not find user with uniquenick:{uniquenick} in database.") @@ -97,7 +103,8 @@ def uniquenick_login(uniquenick: str, namespace_id: int) -> tuple[int, int, bool def is_cdkey_valid(cdkey: str) -> bool: if TYPE_CHECKING: assert isinstance(SubProfiles.cdkeyenc, Column) - result = PG_SESSION.query(SubProfiles).where(SubProfiles.cdkeyenc == cdkey).count() + with Session(ENGINE) as session: + result = session.query(SubProfiles).where(SubProfiles.cdkeyenc == cdkey).count() if result == 0: return False @@ -109,15 +116,16 @@ def is_cdkey_valid(cdkey: str) -> bool: def is_channel_exist(channel_name: str, game_name: str) -> bool: - channel_count = ( - PG_SESSION.query(ChatChannelCaches) - .where( - ChatChannelCaches.channel_name == channel_name, - ChatChannelCaches.game_name == game_name, - ChatChannelCaches.update_time >= datetime.now() - timedelta(minutes=10), + with Session(ENGINE) as session: + channel_count = ( + session.query(ChatChannelCaches) + .where( + ChatChannelCaches.channel_name == channel_name, + ChatChannelCaches.game_name == game_name, + ChatChannelCaches.update_time >= datetime.now() - timedelta(minutes=10), + ) + .count() ) - .count() - ) if channel_count == 1: return True else: @@ -125,30 +133,33 @@ def is_channel_exist(channel_name: str, game_name: str) -> bool: def add_channel(channel: ChatChannelCaches): - PG_SESSION.add(channel) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.add(channel) + session.commit() def get_channel_by_name_and_game( channel_name: str, game_name: str ) -> ChatChannelCaches | None: - channel = ( - PG_SESSION.query(ChatChannelCaches) - .where( - ChatChannelCaches.channel_name == channel_name, - ChatChannelCaches.game_name == game_name, + with Session(ENGINE) as session: + channel = ( + session.query(ChatChannelCaches) + .where( + ChatChannelCaches.channel_name == channel_name, + ChatChannelCaches.game_name == game_name, + ) + .first() ) - .first() - ) return channel def get_channel_by_name(channel_name: str) -> ChatChannelCaches | None: - channel = ( - PG_SESSION.query(ChatChannelCaches) - .where(ChatChannelCaches.channel_name == channel_name) - .first() - ) + with Session(ENGINE) as session: + channel = ( + session.query(ChatChannelCaches) + .where(ChatChannelCaches.channel_name == channel_name) + .first() + ) return channel @@ -158,16 +169,17 @@ def get_channel_by_name_and_ip_port( assert isinstance(channel_name, str) assert isinstance(ip, str) assert isinstance(port, int) - result = ( - PG_SESSION.query(ChatChannelCaches) - .join(ChatChannelUserCaches) - .where( - ChatChannelUserCaches.channel_name == channel_name, - ChatChannelUserCaches.remote_ip_address == ip, - ChatChannelUserCaches.remote_port == port, + with Session(ENGINE) as session: + result = ( + session.query(ChatChannelCaches) + .join(ChatChannelUserCaches) + .where( + ChatChannelUserCaches.channel_name == channel_name, + ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_port == port, + ) + .first() ) - .first() - ) return result @@ -176,69 +188,77 @@ def get_channel_user_cache_by_name( ) -> ChatChannelUserCaches | None: assert isinstance(channel_name, str) assert isinstance(nick_name, str) - result = ( - PG_SESSION.query(ChatChannelUserCaches) - .where( - ChatChannelUserCaches.channel_name == channel_name, - ChatChannelUserCaches.nick_name == nick_name, + with Session(ENGINE) as session: + result = ( + session.query(ChatChannelUserCaches) + .where( + ChatChannelUserCaches.channel_name == channel_name, + ChatChannelUserCaches.nick_name == nick_name, + ) + .first() ) - .first() - ) return result def get_channel_user_cache_by_name_and_ip_port( channel_name: str, ip: str, port: int ) -> ChatChannelUserCaches | None: - result = ( - PG_SESSION.query(ChatChannelUserCaches) - .where( - ChatChannelUserCaches.channel_name == channel_name, - ChatChannelUserCaches.remote_ip_address == ip, - ChatChannelUserCaches.remote_port == port, + with Session(ENGINE) as session: + result = ( + session.query(ChatChannelUserCaches) + .where( + ChatChannelUserCaches.channel_name == channel_name, + ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_port == port, + ) + .first() ) - .first() - ) return result def get_channel_user_caches_by_name(channel_name: str) -> list[ChatChannelUserCaches]: assert isinstance(channel_name, str) - result: list[ChatChannelUserCaches] = ( - PG_SESSION.query(ChatChannelUserCaches.key_values) - .where(ChatChannelUserCaches.channel_name == channel_name) - .all() - ) # type:ignore + with Session(ENGINE) as session: + result: list[ChatChannelUserCaches] = ( + session.query(ChatChannelUserCaches.key_values) + .where(ChatChannelUserCaches.channel_name == channel_name) + .all() + ) # type:ignore return result def update_channel_time(channel: ChatChannelCaches): channel.update_time = datetime.now() # type: ignore - PG_SESSION.commit() + with Session(ENGINE) as session: + session.commit() def db_commit(): - PG_SESSION.commit() + with Session(ENGINE) as session: + + session.commit() def get_user_cache_by_nick_name(nick_name: str) -> ChatUserCaches | None: - result = ( - PG_SESSION.query(ChatUserCaches) - .where(ChatUserCaches.nick_name == nick_name) - .first() - ) + with Session(ENGINE) as session: + result = ( + session.query(ChatUserCaches) + .where(ChatUserCaches.nick_name == nick_name) + .first() + ) return result def get_user_cache_by_ip_port(ip: str, port: int) -> ChatUserCaches: - result = ( - PG_SESSION.query(ChatUserCaches) - .where( - ChatUserCaches.remote_ip_address == ip, ChatUserCaches.remote_port == port + with Session(ENGINE) as session: + result = ( + session.query(ChatUserCaches) + .where( + ChatUserCaches.remote_ip_address == ip, ChatUserCaches.remote_port == port + ) + .first() ) - .first() - ) - assert isinstance(result, ChatUserCaches) + assert isinstance(result, ChatUserCaches) return result @@ -246,18 +266,19 @@ def get_whois_result(nick: str) -> tuple: """ nick is unique in chat """ - info = PG_SESSION.query(ChatUserCaches).first() - - if info is None: - raise NoSuchNickException(f"User not find by nick name:{nick}.") - channels = ( - PG_SESSION.query(ChatChannelUserCaches.channel_name) - .join( - ChatUserCaches, ChatChannelUserCaches.nick_name == ChatUserCaches.nick_name + with Session(ENGINE) as session: + info = session.query(ChatUserCaches).first() + + if info is None: + raise NoSuchNickException(f"User not find by nick name:{nick}.") + channels = ( + session.query(ChatChannelUserCaches.channel_name) + .join( + ChatUserCaches, ChatChannelUserCaches.nick_name == ChatUserCaches.nick_name + ) + .where(ChatChannelUserCaches.nick_name == info.nick_name) + .all() ) - .where(ChatChannelUserCaches.nick_name == info.nick_name) - .all() - ) return ( info.nick_name, info.user_name, @@ -270,33 +291,37 @@ def get_whois_result(nick: str) -> tuple: def remove_user_caches_by_ip_port(ip: str, port: int): assert isinstance(ip, str) assert isinstance(port, int) - PG_SESSION.query(ChatChannelUserCaches).where( - ChatChannelUserCaches.remote_ip_address == ip, - ChatChannelUserCaches.remote_port == port, - ).delete() + with Session(ENGINE) as session: + session.query(ChatChannelUserCaches).where( + ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_port == port, + ).delete() def remove_channel(cache: ChatChannelCaches) -> None: assert isinstance(cache, ChatChannelCaches) - PG_SESSION.delete(cache) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.delete(cache) + session.commit() def remove_user(cache: ChatChannelUserCaches): assert isinstance(cache, ChatChannelUserCaches) - PG_SESSION.delete(cache) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.delete(cache) + session.commit() def is_user_exist(ip: str, port: int) -> bool: - user_count = ( - PG_SESSION.query(ChatChannelUserCaches) - .where( - ChatChannelUserCaches.remote_ip_address == ip, - ChatChannelUserCaches.remote_port == port, + with Session(ENGINE) as session: + user_count = ( + session.query(ChatChannelUserCaches) + .where( + ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_port == port, + ) + .count() ) - .count() - ) if user_count == 1: return True else: @@ -305,7 +330,8 @@ def is_user_exist(ip: str, port: int) -> bool: def update_client(cache: ChatChannelUserCaches): assert isinstance(cache, ChatChannelUserCaches) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.commit() def add_invited(channel_name: str, client_ip: str, client_port: int): @@ -315,16 +341,17 @@ def add_invited(channel_name: str, client_ip: str, client_port: int): def find_channel_by_substring(channel_name: str) -> list[dict]: assert isinstance(channel_name, str) - names, topics = ( - PG_SESSION.query(ChatChannelCaches.channel_name, ChatChannelCaches.topic) - .where(ChatChannelCaches.channel_name.like(f"%{channel_name}%")) - .all() - ) - users = ( - PG_SESSION.query(ChatChannelUserCaches) - .where(ChatChannelUserCaches.channel_name.like(f"%{channel_name}%")) - .all() - ) + with Session(ENGINE) as session: + names, topics = ( + session.query(ChatChannelCaches.channel_name, ChatChannelCaches.topic) + .where(ChatChannelCaches.channel_name.like(f"%{channel_name}%")) + .all() + ) + users = ( + session.query(ChatChannelUserCaches) + .where(ChatChannelUserCaches.channel_name.like(f"%{channel_name}%")) + .all() + ) data: list[dict] = [] assert isinstance(names, list) assert isinstance(topics, list) @@ -337,22 +364,23 @@ def find_channel_by_substring(channel_name: str) -> list[dict]: def find_user_by_substring(user_name: str) -> list[dict]: assert isinstance(user_name, str) - names, topics, users = ( - PG_SESSION.query( - ChatChannelCaches.channel_name, - ChatChannelCaches.topic, - func.count(ChatChannelUserCaches.channel_name), - ) - .join( - ChatUserCaches, ChatUserCaches.nick_name == ChatChannelUserCaches.nick_name - ) - .join( - ChatChannelCaches, - ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name, + with Session(ENGINE) as session: + names, topics, users = ( + session.query( + ChatChannelCaches.channel_name, + ChatChannelCaches.topic, + func.count(ChatChannelUserCaches.channel_name), + ) + .join( + ChatUserCaches, ChatUserCaches.nick_name == ChatChannelUserCaches.nick_name + ) + .join( + ChatChannelCaches, + ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name, + ) + .where(ChatUserCaches.user_name.like(f"%{user_name}%")) + .all() ) - .where(ChatUserCaches.user_name.like(f"%{user_name}%")) - .all() - ) data: list[dict] = [] for name, topic, count in zip(names, topics, users): @@ -362,23 +390,25 @@ def find_user_by_substring(user_name: str) -> list[dict]: def create_channel_user_caches(chan_user: ChatChannelUserCaches): - PG_SESSION.add(chan_user) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.add(chan_user) + session.commit() def get_channel_user_caches(channel_name: str) -> list[dict]: - result: list[ChatChannelUserCaches] = ( - PG_SESSION.query(ChatChannelUserCaches) - .join( - ChatChannelCaches, - ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name, + with Session(ENGINE) as session: + result: list[ChatChannelUserCaches] = ( + session.query(ChatChannelUserCaches) + .join( + ChatChannelCaches, + ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name, + ) + .join( + ChatUserCaches, ChatUserCaches.user_name == ChatChannelUserCaches.user_name + ) + .where(ChatChannelUserCaches.channel_name == channel_name) + .all() ) - .join( - ChatUserCaches, ChatUserCaches.user_name == ChatChannelUserCaches.user_name - ) - .where(ChatChannelUserCaches.channel_name == channel_name) - .all() - ) data = [] for r in result: temp = {} @@ -391,20 +421,21 @@ def get_channel_user_caches(channel_name: str) -> list[dict]: def get_channel_user_cache_by_ip(ip: str, port: int) -> list[dict]: - result: list[ChatChannelUserCaches] = ( - PG_SESSION.query(ChatChannelUserCaches) - .join( - ChatChannelCaches, - ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name, - ) - .join( - ChatUserCaches, ChatUserCaches.user_name == ChatChannelUserCaches.user_name - ) - .where( - ChatUserCaches.remote_ip_address == ip, ChatUserCaches.remote_port == port + with Session(ENGINE) as session: + result: list[ChatChannelUserCaches] = ( + session.query(ChatChannelUserCaches) + .join( + ChatChannelCaches, + ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name, + ) + .join( + ChatUserCaches, ChatUserCaches.user_name == ChatChannelUserCaches.user_name + ) + .where( + ChatUserCaches.remote_ip_address == ip, ChatUserCaches.remote_port == port + ) + .all() ) - .all() - ) data = [] for r in result: temp = {} diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index 3f893a369..a920ace6e 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -3,7 +3,7 @@ from backends.library.abstractions.contracts import RequestBase from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import ( - PG_SESSION, + ENGINE, ChatChannelCaches, ChatUserCaches, ChatChannelUserCaches, @@ -69,7 +69,7 @@ WhoIsResult, WhoResult, ) - +from sqlalchemy.orm import Session # region General @@ -86,13 +86,15 @@ class CryptHandler(HandlerBase): _request: CryptRequest def _data_operate(self) -> None: - result = data.get_user_cache_by_ip_port( - self._request.client_ip, self._request.client_port - ) - if result is None: - raise NoSuchNickException(f"No nick found for {self._request.client_ip}") - result.game_name = self._request.gamename # type: ignore - PG_SESSION.commit() + with Session(ENGINE) as session: + + result = data.get_user_cache_by_ip_port( + self._request.client_ip, self._request.client_port + ) + if result is None: + raise NoSuchNickException(f"No nick found for {self._request.client_ip}") + result.game_name = self._request.gamename # type: ignore + session.commit() def _result_construct(self) -> None: self._result = CryptResult() diff --git a/src/backends/protocols/gamespy/chat/helper.py b/src/backends/protocols/gamespy/chat/helper.py index 29990c3d3..8be258322 100644 --- a/src/backends/protocols/gamespy/chat/helper.py +++ b/src/backends/protocols/gamespy/chat/helper.py @@ -3,7 +3,7 @@ from uuid import UUID from backends.library.database.pg_orm import ( - PG_SESSION, + ENGINE, ChatChannelCaches, ChatChannelUserCaches, ChatUserCaches, @@ -18,6 +18,7 @@ InviteOnlyChanException, NoSuchChannelException, ) +from sqlalchemy.orm import Session class ChannelUserProperty(Enum): @@ -79,8 +80,9 @@ def join(channel: ChatChannelCaches, user: ChatUserCaches) -> None: remote_ip_address=user.remote_ip_address, remote_port=user.remote_port, ) - PG_SESSION.add(chan_user) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.add(chan_user) + session.commit() @staticmethod def quit(channel: ChatChannelCaches, quiter: ChatChannelUserCaches) -> None: @@ -92,8 +94,9 @@ def quit(channel: ChatChannelCaches, quiter: ChatChannelUserCaches) -> None: if quiter.channel_name != channel.channel_name: # type:ignore print("user is not in channel, so can not quit") return - PG_SESSION.delete(quiter) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.delete(quiter) + session.commit() @staticmethod def kick( @@ -117,8 +120,9 @@ def kick( ) if not kicker.is_channel_operator: # type:ignore raise BadChannelKeyException("kick failed, kicker is not channel operator") - PG_SESSION.delete(kickee) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.delete(kickee) + session.commit() @staticmethod def invite( @@ -133,7 +137,8 @@ def invite( assert isinstance(channel.invited_nicks, list) channel.invited_nicks.append(invitee.nick_name) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.commit() @staticmethod def create( @@ -170,8 +175,9 @@ def create( creator=creator, modes=modes, ) - PG_SESSION.add(cache) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.add(cache) + session.commit() return cache @@ -185,79 +191,79 @@ def change_modes( assert isinstance(changer, ChatChannelUserCaches) assert isinstance(channel.modes, list) assert all(isinstance(m, ModeOperationType) for m in request.mode_operations) - - for flag in request.mode_operations: - if flag not in channel.modes: - channel.modes.append(flag) - match flag: - case ModeOperationType.ENABLE_USER_QUIET_FLAG: - if changer.is_channel_operator: # type:ignore - if ( - ModeOperationType.ENABLE_USER_QUIET_FLAG - not in channel.modes - ): - channel.modes.append( + with Session(ENGINE) as session: + for flag in request.mode_operations: + if flag not in channel.modes: + channel.modes.append(flag) + match flag: + case ModeOperationType.ENABLE_USER_QUIET_FLAG: + if changer.is_channel_operator: # type:ignore + if ( ModeOperationType.ENABLE_USER_QUIET_FLAG - ) - case ModeOperationType.DISABLE_USER_QUIET_FLAG: - if changer.is_channel_operator: # type:ignore - if ( - ModeOperationType.ENABLE_USER_QUIET_FLAG - not in channel.modes - ): - channel.modes.remove( + not in channel.modes + ): + channel.modes.append( + ModeOperationType.ENABLE_USER_QUIET_FLAG + ) + case ModeOperationType.DISABLE_USER_QUIET_FLAG: + if changer.is_channel_operator: # type:ignore + if ( ModeOperationType.ENABLE_USER_QUIET_FLAG - ) - case ModeOperationType.ADD_CHANNEL_PASSWORD: - assert isinstance(request.password, str) - if changer.is_channel_operator: # type:ignore - channel.password = request.password # type:ignore - case ModeOperationType.REMOVE_CHANNEL_PASSWORD: - if changer.is_channel_operator: # type:ignore - channel.password = "" # type:ignore - case ModeOperationType.ADD_CHANNEL_USER_LIMITS: - channel.max_num_user = request.limit_number # type: ignore - case ModeOperationType.REMOVE_CHANNEL_USER_LIMITS: - channel.max_num_user = 200 # type: ignore - case ModeOperationType.ADD_BAN_ON_USER: - assert isinstance(channel.banned_nicks, list) - # type: ignore - if request.nick_name not in list(channel.banned_nicks): - channel.banned_nicks.append(request.nick_name) - case ModeOperationType.REMOVE_BAN_ON_USER: - assert isinstance(channel.banned_nicks, list) - if request.nick_name in list(channel.banned_nicks): - channel.banned_nicks.remove(request.nick_name) - case ModeOperationType.ADD_CHANNEL_OPERATOR: - u = data.get_channel_user_cache_by_name( - request.channel_name, request.nick_name - ) - if u is None: - raise BadChannelKeyException( - f"no user found with nick name:{request.nick_name}" + not in channel.modes + ): + channel.modes.remove( + ModeOperationType.ENABLE_USER_QUIET_FLAG + ) + case ModeOperationType.ADD_CHANNEL_PASSWORD: + assert isinstance(request.password, str) + if changer.is_channel_operator: # type:ignore + channel.password = request.password # type:ignore + case ModeOperationType.REMOVE_CHANNEL_PASSWORD: + if changer.is_channel_operator: # type:ignore + channel.password = "" # type:ignore + case ModeOperationType.ADD_CHANNEL_USER_LIMITS: + channel.max_num_user = request.limit_number # type: ignore + case ModeOperationType.REMOVE_CHANNEL_USER_LIMITS: + channel.max_num_user = 200 # type: ignore + case ModeOperationType.ADD_BAN_ON_USER: + assert isinstance(channel.banned_nicks, list) + # type: ignore + if request.nick_name not in list(channel.banned_nicks): + channel.banned_nicks.append(request.nick_name) + case ModeOperationType.REMOVE_BAN_ON_USER: + assert isinstance(channel.banned_nicks, list) + if request.nick_name in list(channel.banned_nicks): + channel.banned_nicks.remove(request.nick_name) + case ModeOperationType.ADD_CHANNEL_OPERATOR: + u = data.get_channel_user_cache_by_name( + request.channel_name, request.nick_name ) + if u is None: + raise BadChannelKeyException( + f"no user found with nick name:{request.nick_name}" + ) - u.is_channel_operator = True # type: ignore - PG_SESSION.commit() - case ModeOperationType.REMOVE_CHANNEL_OPERATOR: - u = data.get_channel_user_cache_by_name( - request.channel_name, request.nick_name - ) - u.is_channel_operator = False # type: ignore - PG_SESSION.commit() + u.is_channel_operator = True # type: ignore + session.commit() + case ModeOperationType.REMOVE_CHANNEL_OPERATOR: + u = data.get_channel_user_cache_by_name( + request.channel_name, request.nick_name + ) + u.is_channel_operator = False # type: ignore + session.commit() - case ModeOperationType.ENABLE_USER_VOICE_PERMISSION: - u = data.get_channel_user_cache_by_name( - request.channel_name, request.nick_name - ) - u.is_voiceable = True # type: ignore - case ModeOperationType.DISABLE_USER_VOICE_PERMISSION: - u = data.get_channel_user_cache_by_name( - request.channel_name, request.nick_name - ) - u.is_voiceable = False # type: ignore + case ModeOperationType.ENABLE_USER_VOICE_PERMISSION: + u = data.get_channel_user_cache_by_name( + request.channel_name, request.nick_name + ) + u.is_voiceable = True # type: ignore + case ModeOperationType.DISABLE_USER_VOICE_PERMISSION: + u = data.get_channel_user_cache_by_name( + request.channel_name, request.nick_name + ) + u.is_voiceable = False # type: ignore - PG_SESSION.commit() + session.commit() @staticmethod def get_all_user_nick_string(channel: ChatChannelCaches) -> str: diff --git a/src/backends/protocols/gamespy/game_status/data.py b/src/backends/protocols/gamespy/game_status/data.py index cb50bccc3..9caad3bbb 100644 --- a/src/backends/protocols/gamespy/game_status/data.py +++ b/src/backends/protocols/gamespy/game_status/data.py @@ -1,10 +1,10 @@ from typing import TYPE_CHECKING, cast from sqlalchemy import Column -from backends.library.database.pg_orm import PG_SESSION, PStorage, Profiles, SubProfiles, Users +from backends.library.database.pg_orm import ENGINE, PStorage, Profiles, SubProfiles, Users from frontends.gamespy.protocols.game_status.aggregations.enums import PersistStorageType from frontends.gamespy.protocols.game_status.aggregations.exceptions import GSException - +from sqlalchemy.orm import Session def create_new_game_data(): raise NotImplementedError() @@ -20,23 +20,24 @@ def update_player_data(): def get_profile_id_by_token(token: str) -> int: assert isinstance(token, str) - result = PG_SESSION.query(SubProfiles.profileid).where( - SubProfiles.authtoken == token).first() - if result is None: - raise GSException("No records found in database") - assert isinstance(result, int) - return result + with Session(ENGINE) as session: + result = session.query(SubProfiles.profileid).where( + SubProfiles.authtoken == token).first() + if result is None: + raise GSException("No records found in database") + assert isinstance(result, int) + return result def get_profile_id_by_profile_id(profile_id: int) -> int: assert isinstance(profile_id, int) - - result = PG_SESSION.query(SubProfiles.profileid).where( - SubProfiles.profileid == profile_id).count() - if result != 1: - raise GSException(f"There is no profile_id {profile_id} existed") - assert isinstance(result, int) - return result + with Session(ENGINE) as session: + result = session.query(SubProfiles.profileid).where( + SubProfiles.profileid == profile_id).count() + if result != 1: + raise GSException(f"There is no profile_id {profile_id} existed") + assert isinstance(result, int) + return result def get_profile_id_by_cdkey(cdkey: str, nick_name: str) -> int: @@ -48,11 +49,12 @@ def get_profile_id_by_cdkey(cdkey: str, nick_name: str) -> int: assert isinstance(Profiles.nick, Column) assert isinstance(SubProfiles.profileid, Column) assert isinstance(SubProfiles.cdkeyenc, Column) - result = PG_SESSION.query(SubProfiles.profileid).join( - SubProfiles, Profiles.profileid == SubProfiles.profileid)\ - .where(SubProfiles.cdkeyenc == cdkey, - Profiles.nick == nick_name)\ - .first() + with Session(ENGINE) as session: + result = session.query(SubProfiles.profileid).join( + SubProfiles, Profiles.profileid == SubProfiles.profileid)\ + .where(SubProfiles.cdkeyenc == cdkey, + Profiles.nick == nick_name)\ + .first() if result is None: raise GSException("No record found in database") if TYPE_CHECKING: @@ -61,9 +63,10 @@ def get_profile_id_by_cdkey(cdkey: str, nick_name: str) -> int: def get_player_data(profile_id: int, storage_type: PersistStorageType, data_index: int) -> dict: - result = PG_SESSION.query(PStorage.data).where(PStorage.ptype == storage_type.value, - PStorage.dindex == data_index, - PStorage.profileid == profile_id).first() + with Session(ENGINE) as session: + result = session.query(PStorage.data).where(PStorage.ptype == storage_type.value, + PStorage.dindex == data_index, + PStorage.profileid == profile_id).first() if result is None: raise GSException("No records found in database") if TYPE_CHECKING: diff --git a/src/backends/protocols/gamespy/game_traffic_relay/data.py b/src/backends/protocols/gamespy/game_traffic_relay/data.py index 9ea99f04c..6b08c9db5 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/data.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/data.py @@ -1,18 +1,19 @@ from datetime import datetime from typing import Optional from uuid import UUID -from backends.library.database.pg_orm import PG_SESSION, RelayServerCaches - +from backends.library.database.pg_orm import ENGINE, RelayServerCaches +from sqlalchemy.orm import Session def search_relay_server(server_id: UUID, server_ip: str) -> Optional[RelayServerCaches]: - result = ( - PG_SESSION.query(RelayServerCaches) - .where( - RelayServerCaches.server_id == server_id, - RelayServerCaches.public_ip_address == server_ip, + with Session(ENGINE) as session: + result = ( + session.query(RelayServerCaches) + .where( + RelayServerCaches.server_id == server_id, + RelayServerCaches.public_ip_address == server_ip, + ) + .first() ) - .first() - ) return result @@ -22,32 +23,36 @@ def get_available_relay_serves() -> list[RelayServerCaches]: ------ list of ip:port """ - result: list[RelayServerCaches] = PG_SESSION.query(RelayServerCaches).all() + with Session(ENGINE) as session: + result: list[RelayServerCaches] = session.query(RelayServerCaches).all() return result def update_relay_server(info: RelayServerCaches): info.update_time = datetime.now() # type: ignore - PG_SESSION.commit() + with Session(ENGINE) as session: + session.commit() def add_relay_server(info: RelayServerCaches): - PG_SESSION.add(info) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.add(info) + session.commit() def delete_relay_server(server_id: UUID, ip_address: str, port: int): assert isinstance(server_id, UUID) assert isinstance(ip_address, str) assert isinstance(port, int) - info = ( - PG_SESSION.query(RelayServerCaches) - .where( - RelayServerCaches.server_id == server_id, - RelayServerCaches.public_ip_address == ip_address, - RelayServerCaches.public_port == port, + with Session(ENGINE) as session: + info = ( + session.query(RelayServerCaches) + .where( + RelayServerCaches.server_id == server_id, + RelayServerCaches.public_ip_address == ip_address, + RelayServerCaches.public_port == port, + ) + .first() ) - .first() - ) - PG_SESSION.delete(info) - PG_SESSION.commit() + session.delete(info) + session.commit() diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py index 05638fdab..2d93e4f65 100644 --- a/src/backends/protocols/gamespy/natneg/data.py +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -1,78 +1,106 @@ from datetime import datetime, timedelta from typing import Optional from backends.library.database.pg_orm import ( - PG_SESSION, + ENGINE, InitPacketCaches, NatFailCaches, RelayServerCaches, ) -from frontends.gamespy.protocols.natneg.aggregations.enums import NatClientIndex +from frontends.gamespy.protocols.natneg.aggregations.enums import ( + NatClientIndex, + NatPortType, +) +from sqlalchemy.orm import Session -def store_init_packet(info: InitPacketCaches) -> None: +def add_init_packet(info: InitPacketCaches) -> None: assert isinstance(info, InitPacketCaches) - PG_SESSION.add(info) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.add(info) + session.commit() def count_init_info(cookie: int, version: int) -> int: time = datetime.now() - timedelta(seconds=30) - result = ( - PG_SESSION.query(InitPacketCaches) - .where( - InitPacketCaches.cookie == cookie, - InitPacketCaches.version == version, - InitPacketCaches.update_time >= time, + with Session(ENGINE) as session: + result = ( + session.query(InitPacketCaches) + .where( + InitPacketCaches.cookie == cookie, + InitPacketCaches.version == version, + InitPacketCaches.update_time >= time, + ) + .count() ) - .count() - ) - return result + return result + + +def get_init_info( + cookie: int, client_index: NatClientIndex, port_type: NatPortType +) -> InitPacketCaches | None: + with Session(ENGINE) as session: + result = ( + session.query(InitPacketCaches) + .where( + InitPacketCaches.cookie == cookie, + InitPacketCaches.client_index == client_index, + InitPacketCaches.port_type == port_type, + ) + .first() + ) + return result def get_init_infos(cookie: int, client_index: NatClientIndex) -> list[InitPacketCaches]: # query the latest init info with in 30 seconds time = datetime.now() - timedelta(seconds=30) - result = ( - PG_SESSION.query(InitPacketCaches) - .where( - InitPacketCaches.cookie == cookie, - InitPacketCaches.client_index == client_index, - InitPacketCaches.update_time >= time, + with Session(ENGINE) as session: + result = ( + session.query(InitPacketCaches) + .where( + InitPacketCaches.cookie == cookie, + InitPacketCaches.client_index == client_index, + InitPacketCaches.update_time >= time, + ) + .all() ) - .all() - ) - return result + return result def update_init_info(info: InitPacketCaches) -> None: assert isinstance(info, InitPacketCaches) - store_init_packet(info) + with Session(ENGINE) as session: + session.commit() def remove_init_info(info: InitPacketCaches) -> None: assert isinstance(info, InitPacketCaches) - PG_SESSION.delete(info) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.delete(info) + session.commit() def store_nat_fail_info(info: NatFailCaches) -> None: assert isinstance(info, NatFailCaches) - PG_SESSION.add(info) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.add(info) + session.commit() def update_nat_fail_info(info: NatFailCaches) -> None: assert isinstance(info, NatFailCaches) - result = get_nat_fail_info(info) - if result is not None: - PG_SESSION.delete(result) - store_nat_fail_info(info) + with Session(ENGINE) as session: + result = get_nat_fail_info(info) + if result is not None: + session.delete(result) + store_nat_fail_info(info) def remove_nat_fail_info(info: NatFailCaches) -> None: assert isinstance(info, NatFailCaches) - PG_SESSION.delete(info) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.delete(info) + session.commit() def get_nat_fail_info(info: NatFailCaches): @@ -83,32 +111,34 @@ def get_nat_fail_info(info: NatFailCaches): def get_nat_fail_info_by_ip(public_ip1: str, public_ip2: str) -> list[NatFailCaches]: - result = ( - PG_SESSION.query(NatFailCaches) - .where( - NatFailCaches.public_ip_address1 == public_ip1, - NatFailCaches.public_ip_address2 == public_ip2, + with Session(ENGINE) as session: + result = ( + session.query(NatFailCaches) + .where( + NatFailCaches.public_ip_address1 == public_ip1, + NatFailCaches.public_ip_address2 == public_ip2, + ) + .all() ) - .all() - ) - return result + return result def get_game_traffic_relay_servers( number: Optional[int] = None, ) -> list[RelayServerCaches]: - if number is None: - result = ( - PG_SESSION.query(RelayServerCaches) - .order_by(RelayServerCaches.client_count.desc()) - .all() - ) - else: - assert isinstance(number, int) - result = ( - PG_SESSION.query(RelayServerCaches) - .order_by(RelayServerCaches.client_count.desc()) - .limit(number) - .all() - ) - return result + with Session(ENGINE) as session: + if number is None: + result = ( + session.query(RelayServerCaches) + .order_by(RelayServerCaches.client_count.desc()) + .all() + ) + else: + assert isinstance(number, int) + result = ( + session.query(RelayServerCaches) + .order_by(RelayServerCaches.client_count.desc()) + .limit(number) + .all() + ) + return result diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py index a707a4648..89f2b4be1 100644 --- a/src/backends/protocols/gamespy/natneg/handlers.py +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -19,21 +19,28 @@ def __init__(self, request: InitRequest) -> None: super().__init__(request) def _data_operate(self) -> None: - info = InitPacketCaches( - cookie=self._request.cookie, - server_id=self._request.server_id, - version=self._request.version, - port_type=self._request.port_type, - client_index=self._request.client_index, - game_name=self._request.game_name, - use_game_port=self._request.use_game_port, - public_ip=self._request.client_ip, - public_port=self._request.client_port, - private_ip=self._request.private_ip, - private_port=self._request.private_port, - update_time=datetime.now(timezone.utc), + info = data.get_init_info( + self._request.cookie, self._request.client_index, self._request.port_type ) - data.update_init_info(info) + if info is None: + info = InitPacketCaches( + cookie=self._request.cookie, + server_id=self._request.server_id, + version=self._request.version, + port_type=self._request.port_type, + client_index=self._request.client_index, + game_name=self._request.game_name, + use_game_port=self._request.use_game_port, + public_ip=self._request.client_ip, + public_port=self._request.client_port, + private_ip=self._request.private_ip, + private_port=self._request.private_port, + update_time=datetime.now(timezone.utc), + ) + data.add_init_packet(info) + else: + info.update_time = datetime.now(timezone.utc) # type: ignore + data.update_init_info(info) class ConnectHandler(HandlerBase): diff --git a/src/backends/protocols/gamespy/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py index 2aded2b3f..59da838d4 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/data.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/data.py @@ -10,7 +10,6 @@ Profiles, SubProfiles, Users, - PG_SESSION, ) from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( GPStatusCode, @@ -28,7 +27,10 @@ GPException, ) import backends.protocols.gamespy.presence_search_player.data as psp + # region General +from backends.library.database.pg_orm import ENGINE +from sqlalchemy.orm import Session def is_email_exist(email: str): @@ -38,42 +40,45 @@ def is_email_exist(email: str): def update_online_time(ip: str, port: int): if TYPE_CHECKING: assert isinstance(Users.lastip, Column) - - result = PG_SESSION.query(Users).where(Users.lastip == ip).first() - if result is None: - return False - result.lastonline = datetime.now() - PG_SESSION.commit() + with Session(ENGINE) as session: + result = session.query(Users).where(Users.lastip == ip).first() + if result is None: + return False + result.lastonline = datetime.now() + session.commit() def delete_friend_by_profile_id(profile_id: int): - friend = PG_SESSION.query(Friends).where(Friends.friendid == profile_id).first() - if friend is None: - raise GPDatabaseException( - f"friend deletion have errors on profile id:{profile_id}" - ) - else: - PG_SESSION.delete(friend) - PG_SESSION.commit() + with Session(ENGINE) as session: + friend = session.query(Friends).where(Friends.friendid == profile_id).first() + if friend is None: + raise GPDatabaseException( + f"friend deletion have errors on profile id:{profile_id}" + ) + else: + session.delete(friend) + session.commit() def get_blocked_profile_id_list(profile_id: int, namespace_id: int) -> list[int]: - result = ( - PG_SESSION.query(Blocked.targetid) - .where(Blocked.profileid == profile_id, Blocked.namespaceid == namespace_id) - .all() - ) + with Session(ENGINE) as session: + result = ( + session.query(Blocked.targetid) + .where(Blocked.profileid == profile_id, Blocked.namespaceid == namespace_id) + .all() + ) if TYPE_CHECKING: result = cast(list[int], result) return result def get_friend_profile_id_list(profile_id: int, namespace_id: int) -> list[int]: - result = ( - PG_SESSION.query(Friends.targetid) - .where(Friends.profileid == profile_id, Friends.namespaceid == namespace_id) - .all() - ) + with Session(ENGINE) as session: + result = ( + session.query(Friends.targetid) + .where(Friends.profileid == profile_id, Friends.namespaceid == namespace_id) + .all() + ) if TYPE_CHECKING: result = cast(list[int], result) return result @@ -99,23 +104,26 @@ def get_profile_infos(profile_id: int, session_key: str) -> GetProfileData: assert isinstance(SubProfiles.namespaceid, Column) assert isinstance(SubProfiles.session_key, Column) - namespace_id = ( - PG_SESSION.query(SubProfiles.namespaceid) - .where(SubProfiles.session_key == session_key) - .first() - ) + with Session(ENGINE) as session: + namespace_id = ( + session.query(SubProfiles.namespaceid) + .where(SubProfiles.session_key == session_key) + .first() + ) if namespace_id is None: raise GPException("namespace not found") - result = ( - PG_SESSION.query(Users, Profiles, SubProfiles) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .join(Users, Profiles.userid == Users.userid) - .where( - Profiles.profileid == profile_id, SubProfiles.namespaceid == namespace_id + with Session(ENGINE) as session: + result = ( + session.query(Users, Profiles, SubProfiles) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .join(Users, Profiles.userid == Users.userid) + .where( + Profiles.profileid == profile_id, + SubProfiles.namespaceid == namespace_id, + ) + .first() ) - .first() - ) if result is None: raise GPException("no profile found") @@ -162,13 +170,14 @@ def get_user_info_list(email: str, nick_name: str) -> list[tuple[int, int, int]] assert isinstance(Users.email, Column) assert isinstance(Profiles.nick, Column) - result = ( - PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where(Users.email == email, Profiles.nick == nick_name) - .all() - ) + with Session(ENGINE) as session: + result = ( + session.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where(Users.email == email, Profiles.nick == nick_name) + .all() + ) if TYPE_CHECKING: result = cast(list[tuple[int, int, int]], result) return result @@ -184,16 +193,17 @@ def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: # assert isinstance(SubProfiles.uniquenick, Column) # assert isinstance(SubProfiles.namespaceid, Column) - result = ( - PG_SESSION.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where( - SubProfiles.uniquenick == unique_nick, - SubProfiles.namespaceid == namespace_id, + with Session(ENGINE) as session: + result = ( + session.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where( + SubProfiles.uniquenick == unique_nick, + SubProfiles.namespaceid == namespace_id, + ) + .first() ) - .first() - ) if TYPE_CHECKING: result = cast(tuple[int, int, int], result) return result @@ -214,27 +224,28 @@ def get_user_infos_by_uniquenick_namespace_id( assert isinstance(Users.emailverified, Column) assert isinstance(Users.banned, Column) - result = ( - PG_SESSION.query( - Users.userid, - Profiles.profileid, - SubProfiles.subprofileid, - Profiles.nick, - Users.email, - SubProfiles.uniquenick, - Users.password, - Users.emailverified, - Users.banned, - SubProfiles.namespaceid, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where( - SubProfiles.uniquenick == unique_nick, - SubProfiles.namespaceid == namespace_id, + with Session(ENGINE) as session: + result = ( + session.query( + Users.userid, + Profiles.profileid, + SubProfiles.subprofileid, + Profiles.nick, + Users.email, + SubProfiles.uniquenick, + Users.password, + Users.emailverified, + Users.banned, + SubProfiles.namespaceid, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where( + SubProfiles.uniquenick == unique_nick, + SubProfiles.namespaceid == namespace_id, + ) + .first() ) - .first() - ) return result @@ -252,24 +263,25 @@ def get_user_infos_by_nick_email(nick: str, email: str) -> LoginData | None: assert isinstance(SubProfiles.uniquenick, Column) assert isinstance(SubProfiles.namespaceid, Column) - result = ( - PG_SESSION.query( - Users.userid, - Profiles.profileid, - SubProfiles.subprofileid, - Profiles.nick, - Users.email, - SubProfiles.uniquenick, - Users.password, - Users.emailverified, - Users.banned, - SubProfiles.namespaceid, + with Session(ENGINE) as session: + result = ( + session.query( + Users.userid, + Profiles.profileid, + SubProfiles.subprofileid, + Profiles.nick, + Users.email, + SubProfiles.uniquenick, + Users.password, + Users.emailverified, + Users.banned, + SubProfiles.namespaceid, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where(Users.email == email, Profiles.nick == nick) + .first() ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where(Users.email == email, Profiles.nick == nick) - .first() - ) data = { "user_id": result[0], "profile_id": result[1], @@ -291,8 +303,9 @@ def get_user_infos_by_nick_email(nick: str, email: str) -> LoginData | None: def update_online_status(user_id: int, status: LoginStatus): if TYPE_CHECKING: assert isinstance(Users.userid, Column) - result = PG_SESSION.query(Users).where(Users.userid == user_id).first() raise NotImplementedError("implement sesskey") + with Session(ENGINE) as session: + result = session.query(Users).where(Users.userid == user_id).first() def get_user_infos_by_authtoken(auth_token: str) -> LoginData | None: @@ -309,24 +322,25 @@ def get_user_infos_by_authtoken(auth_token: str) -> LoginData | None: assert isinstance(SubProfiles.namespaceid, Column) assert isinstance(SubProfiles.authtoken, Column) - result = ( - PG_SESSION.query( - Users.userid, - Profiles.profileid, - SubProfiles.subprofileid, - Profiles.nick, - Users.email, - SubProfiles.uniquenick, - Users.password, - Users.emailverified, - Users.banned, - SubProfiles.namespaceid, + with Session(ENGINE) as session: + result = ( + session.query( + Users.userid, + Profiles.profileid, + SubProfiles.subprofileid, + Profiles.nick, + Users.email, + SubProfiles.uniquenick, + Users.password, + Users.emailverified, + Users.banned, + SubProfiles.namespaceid, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where(SubProfiles.authtoken == auth_token) + .first() ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where(SubProfiles.authtoken == auth_token) - .first() - ) if result is not None: data = { @@ -347,14 +361,15 @@ def get_user_infos_by_authtoken(auth_token: str) -> LoginData | None: def get_block_list(profile_id: int, namespace_id: int) -> list[int]: - result = ( - PG_SESSION.query(Blocked.targetid) - .where( - Blocked.namespaceid == namespace_id, - Blocked.profileid == profile_id, + with Session(ENGINE) as session: + result = ( + session.query(Blocked.targetid) + .where( + Blocked.namespaceid == namespace_id, + Blocked.profileid == profile_id, + ) + .all() ) - .all() - ) if TYPE_CHECKING: result = cast(list[int], result) return result @@ -364,14 +379,15 @@ def get_block_list(profile_id: int, namespace_id: int) -> list[int]: def get_buddy_list(profile_id: int, namespace_id: int) -> list[int]: - result = ( - PG_SESSION.query(Friends.targetid) - .where( - Blocked.namespaceid == namespace_id, - Blocked.profileid == profile_id, + with Session(ENGINE) as session: + result = ( + session.query(Friends.targetid) + .where( + Blocked.namespaceid == namespace_id, + Blocked.profileid == profile_id, + ) + .all() ) - .all() - ) # assert isinstance(result, list) if TYPE_CHECKING: result = cast(list[int], result) @@ -381,41 +397,45 @@ def get_buddy_list(profile_id: int, namespace_id: int) -> list[int]: def update_block(profile_id: int, target_id: int, session_key: str) -> None: if TYPE_CHECKING: assert isinstance(SubProfiles.session_key, Column) - namespace_id = ( - PG_SESSION.query(SubProfiles) - .where(SubProfiles.session_key == session_key) - .first() - ) - result = ( - PG_SESSION.query(Blocked) - .where( - Blocked.targetid == target_id, - Blocked.namespaceid == namespace_id, - Blocked.profileid == profile_id, + with Session(ENGINE) as session: + namespace_id = ( + session.query(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() ) - .count() - ) - if result == 0: - b = Blocked(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) - PG_SESSION.add(b) - PG_SESSION.commit() + result = ( + session.query(Blocked) + .where( + Blocked.targetid == target_id, + Blocked.namespaceid == namespace_id, + Blocked.profileid == profile_id, + ) + .count() + ) + if result == 0: + b = Blocked( + targetid=target_id, namespaceid=namespace_id, profileid=profile_id + ) + session.add(b) + session.commit() def update_friend_info(target_id: int, profile_id: int, namespace_id: int): - result = ( - PG_SESSION.query(Friends) - .where( - Friends.targetid == target_id, - Friends.namespaceid == namespace_id, - Friends.profileid == profile_id, + with Session(ENGINE) as session: + result = ( + session.query(Friends) + .where( + Friends.targetid == target_id, + Friends.namespaceid == namespace_id, + Friends.profileid == profile_id, + ) + .count() ) - .count() - ) - f = Friends(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) + f = Friends(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) - if result == 0: - PG_SESSION.add(f) - PG_SESSION.commit() + if result == 0: + session.add(f) + session.commit() def add_nick_name(profile_id: int, old_nick: str, new_nick: str): @@ -428,70 +448,77 @@ def add_nick_name(profile_id: int, old_nick: str, new_nick: str): assert isinstance(Users.userid, Column) assert isinstance(Users.email, Column) assert isinstance(Profiles.nick, Column) - result = ( - PG_SESSION.query(Profiles) - .where(Profiles.profileid == profile_id, Profiles.nick == old_nick) - .first() - ) + with Session(ENGINE) as session: + result = ( + session.query(Profiles) + .where(Profiles.profileid == profile_id, Profiles.nick == old_nick) + .first() + ) - if result is None: - raise GPDatabaseException("No user infomation found in database.") + if result is None: + raise GPDatabaseException("No user infomation found in database.") - result.nick = new_nick # type:ignore - PG_SESSION.commit() + result.nick = new_nick # type:ignore + session.commit() # def update_profile_info(profile: Profiles): -# PG_SESSION.add(profile) -# PG_SESSION.commit() +# session.add(profile) +# session.commit() def update_unique_nick(subprofile_id: int, unique_nick: str): - result = ( - PG_SESSION.query(SubProfiles) - .where(SubProfiles.subprofileid == subprofile_id) - .first() - ) - result.uniquenick = unique_nick # type:ignore - PG_SESSION.commit() + with Session(ENGINE) as session: + result = ( + session.query(SubProfiles) + .where(SubProfiles.subprofileid == subprofile_id) + .first() + ) + result.uniquenick = unique_nick # type:ignore + session.commit() def update_subprofile_info(subprofile: SubProfiles): - PG_SESSION.add(subprofile) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.add(subprofile) + session.commit() def add_friend_request( profileid: int, targetid: int, namespace_id: int, reason: str ) -> None: - data = ( - PG_SESSION.query(FriendAddRequest) - .where( - FriendAddRequest.profileid == profileid, - FriendAddRequest.targetid == targetid, - FriendAddRequest.namespaceid == namespace_id, + with Session(ENGINE) as session: + data = ( + session.query(FriendAddRequest) + .where( + FriendAddRequest.profileid == profileid, + FriendAddRequest.targetid == targetid, + FriendAddRequest.namespaceid == namespace_id, + ) + .first() ) - .first() - ) - if data is not None: - raise GPAddBuddyException("Request is existed, add friend ignored") - request = FriendAddRequest( - profileid=profileid, targetid=targetid, namespaceid=namespace_id, reason=reason - ) - PG_SESSION.add(request) - PG_SESSION.commit() + if data is not None: + raise GPAddBuddyException("Request is existed, add friend ignored") + request = FriendAddRequest( + profileid=profileid, + targetid=targetid, + namespaceid=namespace_id, + reason=reason, + ) + session.add(request) + session.commit() def get_status(session_key: str) -> dict: if TYPE_CHECKING: assert isinstance(SubProfiles.session_key, Column) - - result = ( - PG_SESSION.query(Profiles) - .join(SubProfiles) - .where(SubProfiles.session_key == session_key) - .first() - ) + with Session(ENGINE) as session: + result = ( + session.query(Profiles) + .join(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) if result is None: raise GPStatusException("No profile found with the provided session key") @@ -516,75 +543,86 @@ def update_status( ): if TYPE_CHECKING: assert isinstance(SubProfiles.session_key, Column) - result = ( - PG_SESSION.query(Profiles) - .join(SubProfiles) - .where(SubProfiles.session_key == session_key) - .first() - ) - if result is None: - raise GPStatusException("No profile found with the provided session key") + with Session(ENGINE) as session: + result = ( + session.query(Profiles) + .join(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) + if result is None: + raise GPStatusException("No profile found with the provided session key") - result.statstring = status_string - result.status = current_status - assert isinstance(result.extra_info, list) - result.extra_info.append(location_string) + result.statstring = status_string + result.status = current_status + assert isinstance(result.extra_info, list) + result.extra_info.append(location_string) - PG_SESSION.commit() + session.commit() def update_new_nick(session_key: str, old_nick: str, new_nick: str): - result = ( - PG_SESSION.query(Profiles) - .join(SubProfiles) - .where(SubProfiles.session_key == session_key) - .first() - ) - if result.nick == old_nick and result.nick != new_nick: - result.nick = new_nick - PG_SESSION.commit() + with Session(ENGINE) as session: + result = ( + session.query(Profiles) + .join(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) + if result.nick == old_nick and result.nick != new_nick: + result.nick = new_nick + session.commit() def update_cdkey(session_key: str, cdkey: str): - subprofile = ( - PG_SESSION.query(SubProfiles) - .where(SubProfiles.session_key == session_key) - .first() - ) - if subprofile is None: - raise GPDatabaseException(f"no subprofile found with session key:{session_key}") + with Session(ENGINE) as session: + subprofile = ( + session.query(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) + if subprofile is None: + raise GPDatabaseException( + f"no subprofile found with session key:{session_key}" + ) - subprofile.cdkeyenc = cdkey + subprofile.cdkeyenc = cdkey - PG_SESSION.commit() + session.commit() def update_uniquenick(session_key: str, uniquenick: str): - subprofile = ( - PG_SESSION.query(SubProfiles) - .where(SubProfiles.session_key == session_key) - .first() - ) - if subprofile is None: - raise GPDatabaseException(f"no subprofile found with session key:{session_key}") + with Session(ENGINE) as session: + subprofile = ( + session.query(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) + if subprofile is None: + raise GPDatabaseException( + f"no subprofile found with session key:{session_key}" + ) - subprofile.uniquenick = uniquenick - PG_SESSION.commit() + subprofile.uniquenick = uniquenick + session.commit() def update_profiles(session_key: str, extra_info: dict): - profile = ( - PG_SESSION.query(Profiles) - .join(SubProfiles) - .where(SubProfiles.session_key == session_key) - .first() - ) - if profile is None: - raise GPDatabaseException(f"no profile found with session key:{session_key}") - for key, value in extra_info.items(): - profile.extra_info[key] = value - - PG_SESSION.commit() + with Session(ENGINE) as session: + profile = ( + session.query(Profiles) + .join(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) + if profile is None: + raise GPDatabaseException( + f"no profile found with session key:{session_key}" + ) + for key, value in extra_info.items(): + profile.extra_info[key] = value + + session.commit() def update_user(session_key): diff --git a/src/backends/protocols/gamespy/presence_search_player/data.py b/src/backends/protocols/gamespy/presence_search_player/data.py index bbd372ed9..64b487fbb 100644 --- a/src/backends/protocols/gamespy/presence_search_player/data.py +++ b/src/backends/protocols/gamespy/presence_search_player/data.py @@ -5,31 +5,33 @@ Profiles, SubProfiles, Users, - PG_SESSION, + ENGINE, ) - +from sqlalchemy.orm import Session def db_commit() -> None: - PG_SESSION.commit() + with Session(ENGINE) as session: + session.commit() def verify_email(email: str): assert isinstance(email, str) - if PG_SESSION.query(Users).where(Users.email == email).count() == 1: - return True - else: - return False + with Session(ENGINE) as session: + if session.query(Users).where(Users.email == email).count() == 1: + return True + else: + return False def verify_email_and_password(email: str, password: str): assert isinstance(email, str) assert isinstance(password, str) - - result = ( - PG_SESSION.query(Users) - .where(Users.email == email, Users.password == password) - .count() - ) + with Session(ENGINE) as session: + result = ( + session.query(Users) + .where(Users.email == email, Users.password == password) + .count() + ) if result == 1: return True return False @@ -38,18 +40,19 @@ def verify_email_and_password(email: str, password: str): def get_profile_id( email: str, password: str, nick_name: str, partner_id: int ) -> int | None: - result = ( - PG_SESSION.query(Profiles.profileid) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where( - Users.email == email, - Users.password == password, - Profiles.nick == nick_name, - SubProfiles.partnerid == partner_id, + with Session(ENGINE) as session: + result = ( + session.query(Profiles.profileid) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where( + Users.email == email, + Users.password == password, + Profiles.nick == nick_name, + SubProfiles.partnerid == partner_id, + ) + .first() ) - .first() - ) if result is not None: result = result[0] assert isinstance(result, int) @@ -57,49 +60,57 @@ def get_profile_id( def add_user(user: Users): - PG_SESSION.add(user) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.add(user) + session.commit() def add_profile(profile: Profiles): - PG_SESSION.add(profile) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.add(profile) + session.commit() def add_sub_profile(subprofile: SubProfiles): - PG_SESSION.add(subprofile) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.add(subprofile) + session.commit() def update_user(user: Users): - PG_SESSION.merge(user) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.merge(user) + session.commit() def update_profile(profile: Profiles): - PG_SESSION.merge(profile) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.merge(profile) + session.commit() def update_subprofile(subprofile: SubProfiles): - PG_SESSION.merge(subprofile) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.merge(subprofile) + session.commit() def get_user(email: str) -> Users | None: assert isinstance(email, str) - result = PG_SESSION.query(Users).where(Users.email == email).first() + with Session(ENGINE) as session: + result = session.query(Users).where(Users.email == email).first() return result def get_profile(user_id: int, nick_name: str) -> Profiles | None: assert isinstance(user_id, int) assert isinstance(nick_name, str) - result = ( - PG_SESSION.query(Profiles) - .where(Profiles.userid == user_id, Profiles.nick == nick_name) - .first() - ) + with Session(ENGINE) as session: + result = ( + session.query(Profiles) + .where(Profiles.userid == user_id, Profiles.nick == nick_name) + .first() + ) return result @@ -109,15 +120,16 @@ def get_sub_profile( assert isinstance(profile_id, int) assert isinstance(namespace_id, int) assert isinstance(product_id, int) - result = ( - PG_SESSION.query(SubProfiles) - .where( - SubProfiles.profileid == profile_id, - SubProfiles.namespaceid == namespace_id, - SubProfiles.namespaceid == product_id, + with Session(ENGINE) as session: + result = ( + session.query(SubProfiles) + .where( + SubProfiles.profileid == profile_id, + SubProfiles.namespaceid == namespace_id, + SubProfiles.namespaceid == product_id, + ) + .first() ) - .first() - ) return result @@ -127,17 +139,18 @@ def get_nick_and_unique_nick_list( """ return [(nick, uniquenick)] """ - result = ( - PG_SESSION.query(Profiles.nick, SubProfiles.uniquenick) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where( - Users.email == email, - Users.password == password, - SubProfiles.namespaceid == namespace_id, + with Session(ENGINE) as session: + result = ( + session.query(Profiles.nick, SubProfiles.uniquenick) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where( + Users.email == email, + Users.password == password, + SubProfiles.namespaceid == namespace_id, + ) + .all() ) - .all() - ) assert isinstance(result, list) data = [] for r in result: @@ -150,25 +163,26 @@ def get_friend_info_list(profile_id: int, namespace_id: int, game_name: str) -> """ return [(profileid, nick, uniquenick, lastname, firstname, userid, email)] """ - result = ( - PG_SESSION.query( - Profiles.profileid, - Profiles.nick, - SubProfiles.uniquenick, - Users.userid, - Users.email, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .join(Friends, Profiles.profileid == Friends.friendid) - # todo check whether friends table join is correct - .where( - Friends.profileid == profile_id, - SubProfiles.namespaceid == namespace_id, - SubProfiles.gamename == game_name, + with Session(ENGINE) as session: + result = ( + session.query( + Profiles.profileid, + Profiles.nick, + SubProfiles.uniquenick, + Users.userid, + Users.email, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .join(Friends, Profiles.profileid == Friends.friendid) + # todo check whether friends table join is correct + .where( + Friends.profileid == profile_id, + SubProfiles.namespaceid == namespace_id, + SubProfiles.gamename == game_name, + ) + .all() ) - .all() - ) return result @@ -183,14 +197,15 @@ def get_matched_profile_info_list( assert isinstance(SubProfiles.profileid, Column) assert isinstance(SubProfiles.uniquenick, Column) assert isinstance(SubProfiles.namespaceid, Column) - result = ( - PG_SESSION.query(SubProfiles.profileid, SubProfiles.uniquenick) - .where( - SubProfiles.profileid.in_(profile_ids), - SubProfiles.namespaceid == namespace_id, + with Session(ENGINE) as session: + result = ( + session.query(SubProfiles.profileid, SubProfiles.uniquenick) + .where( + SubProfiles.profileid.in_(profile_ids), + SubProfiles.namespaceid == namespace_id, + ) + .all() ) - .all() - ) data = [] for r in result: data.append(tuple(r)) @@ -200,20 +215,21 @@ def get_matched_profile_info_list( def get_matched_info_by_nick( nick_name: str, ) -> list[dict]: - result = ( - PG_SESSION.query( - Users.email, - Profiles.profileid, - Profiles.nick, - SubProfiles.uniquenick, - SubProfiles.namespaceid, - Profiles.extra_info, + with Session(ENGINE) as session: + result = ( + session.query( + Users.email, + Profiles.profileid, + Profiles.nick, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + Profiles.extra_info, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where(Profiles.nick == nick_name) + .all() ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where(Profiles.nick == nick_name) - .all() - ) temp: list[dict] = [] for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: if TYPE_CHECKING: @@ -237,20 +253,21 @@ def get_matched_info_by_nick( def get_matched_info_by_email( email: str, ) -> list[dict]: - result = ( - PG_SESSION.query( - Users.email, - Profiles.profileid, - Profiles.nick, - SubProfiles.uniquenick, - SubProfiles.namespaceid, - Profiles.extra_info, + with Session(ENGINE) as session: + result = ( + session.query( + Users.email, + Profiles.profileid, + Profiles.nick, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + Profiles.extra_info, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where(Users.email == email) + .all() ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where(Users.email == email) - .all() - ) temp: list[dict] = [] for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: if TYPE_CHECKING: @@ -271,20 +288,21 @@ def get_matched_info_by_email( def get_matched_info_by_nick_and_email(nick_name: str, email: str) -> list[dict]: - result = ( - PG_SESSION.query( - Users.email, - Profiles.profileid, - Profiles.nick, - SubProfiles.uniquenick, - SubProfiles.namespaceid, - Profiles.extra_info, + with Session(ENGINE) as session: + result = ( + session.query( + Users.email, + Profiles.profileid, + Profiles.nick, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + Profiles.extra_info, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where(Users.email == email, Profiles.nick == nick_name) + .all() ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where(Users.email == email, Profiles.nick == nick_name) - .all() - ) data: list[dict] = [] for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: if TYPE_CHECKING: @@ -307,22 +325,23 @@ def get_matched_info_by_nick_and_email(nick_name: str, email: str) -> list[dict] def get_matched_info_by_uniquenick_and_namespaceid( unique_nick: str, namespace_id: int ) -> list[dict]: - result = ( - PG_SESSION.query( - Profiles.profileid, - Profiles.nick, - SubProfiles.uniquenick, - SubProfiles.namespaceid, - Profiles.extra_info, + with Session(ENGINE) as session: + result = ( + session.query( + Profiles.profileid, + Profiles.nick, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + Profiles.extra_info, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where( + SubProfiles.uniquenick == unique_nick, + SubProfiles.namespaceid == namespace_id, + ) + .all() ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where( - SubProfiles.uniquenick == unique_nick, - SubProfiles.namespaceid == namespace_id, - ) - .all() - ) data: list[dict] = [] for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: if TYPE_CHECKING: @@ -346,21 +365,22 @@ def get_matched_info_by_uniquenick_and_namespaceid( def get_matched_info_by_uniquenick_and_namespaceids( unique_nick: str, namespace_ids: list[int] ) -> list[dict]: - result = ( - PG_SESSION.query( - Profiles.profileid, - Profiles.nick, - SubProfiles.uniquenick, - SubProfiles.namespaceid, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where( - SubProfiles.uniquenick == unique_nick, - SubProfiles.namespaceid.in_(namespace_ids), + with Session(ENGINE) as session: + result = ( + session.query( + Profiles.profileid, + Profiles.nick, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where( + SubProfiles.uniquenick == unique_nick, + SubProfiles.namespaceid.in_(namespace_ids), + ) + .all() ) - .all() - ) data: list[dict] = [] for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: if TYPE_CHECKING: @@ -382,16 +402,17 @@ def get_matched_info_by_uniquenick_and_namespaceids( def is_uniquenick_exist(unique_nick: str, namespace_id: int, game_name: str) -> bool: - result = ( - PG_SESSION.query(Profiles) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where( - SubProfiles.uniquenick == unique_nick, - SubProfiles.gamename == game_name, - SubProfiles.namespaceid == namespace_id, + with Session(ENGINE) as session: + result = ( + session.query(Profiles) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where( + SubProfiles.uniquenick == unique_nick, + SubProfiles.gamename == game_name, + SubProfiles.namespaceid == namespace_id, + ) + .count() ) - .count() - ) if result == 0: return False @@ -403,7 +424,8 @@ def is_email_exist(email: str) -> bool: if TYPE_CHECKING: Users.userid = cast(Column, Users.userid) Users.email = cast(Column, Users.email) - result = PG_SESSION.query(Users.userid).where(Users.email == email).count() + with Session(ENGINE) as session: + result = session.query(Users.userid).where(Users.email == email).count() # According to game partnerid is not nessesary if result == 0: return False diff --git a/src/backends/protocols/gamespy/presence_search_player/handlers.py b/src/backends/protocols/gamespy/presence_search_player/handlers.py index 8ecc649e8..b840a41a6 100644 --- a/src/backends/protocols/gamespy/presence_search_player/handlers.py +++ b/src/backends/protocols/gamespy/presence_search_player/handlers.py @@ -1,5 +1,5 @@ from backends.library.abstractions.handler_base import HandlerBase -from backends.library.database.pg_orm import PG_SESSION, Users, Profiles, SubProfiles +from backends.library.database.pg_orm import ENGINE, Users, Profiles, SubProfiles import backends.protocols.gamespy.presence_search_player.data as data from backends.protocols.gamespy.presence_search_player.requests import ( CheckRequest, @@ -34,6 +34,7 @@ UniqueSearchResult, ValidResult, ) +from sqlalchemy.orm import Session class CheckHandler(HandlerBase): @@ -106,8 +107,9 @@ def _create_user(self) -> None: if key in Users.__dict__: user_dict[key] = value self.user = Users(**user_dict) - PG_SESSION.add(self.user) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.add(self.user) + session.commit() def _create_profile(self) -> None: profile_dict = {} @@ -119,8 +121,9 @@ def _create_profile(self) -> None: assert isinstance(self.user.userid, int) profile_dict["userid"] = self.user.userid self.profile = Profiles(**profile_dict) - PG_SESSION.add(self.profile) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.add(self.profile) + session.commit() def _create_subprofile(self) -> None: subprofile_dict = {} @@ -130,8 +133,9 @@ def _create_subprofile(self) -> None: assert self.profile is not None subprofile_dict["profileid"] = self.profile.profileid self.subprofile = SubProfiles(**subprofile_dict) - PG_SESSION.add(self.subprofile) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.add(self.subprofile) + session.commit() class NicksHandler(HandlerBase): diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index 9f480afff..839d9d42c 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING, Optional, cast from backends.library.database.pg_orm import ( - PG_SESSION, + ENGINE, ChatChannelCaches, GroupList, Games, @@ -16,14 +16,16 @@ from frontends.gamespy.protocols.server_browser.aggregates.exceptions import ( ServerBrowserException, ) +from sqlalchemy.orm import Session def get_all_groups() -> dict: - result = ( - PG_SESSION.query(Games, GroupList) - .join(GroupList, (Games.gameid == GroupList.gameid)) - .all() - ) + with Session(ENGINE) as session: + result = ( + session.query(Games, GroupList) + .join(GroupList, (Games.gameid == GroupList.gameid)) + .all() + ) if TYPE_CHECKING: result = cast(list[tuple[Games, GroupList]], result) @@ -57,11 +59,12 @@ def get_peer_staging_channels(game_name: str, group_id: int) -> list[GameServerI assert isinstance(game_name, str) assert isinstance(group_id, int) staging_name = f"{PeerRoom.StagingRoomPrefix}!{game_name}!*" - result = ( - PG_SESSION.query(ChatChannelCaches) - .where(ChatChannelCaches.channel_name == staging_name) - .all() - ) + with Session(ENGINE) as session: + result = ( + session.query(ChatChannelCaches) + .where(ChatChannelCaches.channel_name == staging_name) + .all() + ) data = [] for s in result: t = {k: v for k, v in s.__dict__.items() if k != "_sa_instance_state"} @@ -89,11 +92,12 @@ def get_peer_group_channel(group_data: list[dict]) -> list[PeerRoomInfo]: ] # Query the database for channels matching the constructed group names - result = ( - PG_SESSION.query(ChatChannelCaches) - .filter(ChatChannelCaches.channel_name.in_(group_names)) - .all() - ) + with Session(ENGINE) as session: + result = ( + session.query(ChatChannelCaches) + .filter(ChatChannelCaches.channel_name.in_(group_names)) + .all() + ) # Convert the result to a list of PeerRoomInfo objects data = [PeerRoomInfo(**s.__dict__) for s in result] @@ -103,30 +107,33 @@ def get_peer_group_channel(group_data: list[dict]) -> list[PeerRoomInfo]: def get_server_info_with_instant_key(instant_key: str) -> Optional[GameServerCaches]: assert isinstance(instant_key, str) - result = ( - PG_SESSION.query(GameServerCaches) - .where(GameServerCaches.instant_key == instant_key) - .first() - ) + with Session(ENGINE) as session: + result = ( + session.query(GameServerCaches) + .where(GameServerCaches.instant_key == instant_key) + .first() + ) return result def get_server_info_with_game_name(game_name: str) -> Optional[GameServerCaches]: assert isinstance(game_name, str) - result = ( - PG_SESSION.query(GameServerCaches) - .where(GameServerCaches.game_name == game_name) - .first() - ) + with Session(ENGINE) as session: + result = ( + session.query(GameServerCaches) + .where(GameServerCaches.game_name == game_name) + .first() + ) return result def get_server_info_list_with_game_name(game_name: str) -> list[GameServerInfo]: - result = ( - PG_SESSION.query(GameServerCaches) - .where(GameServerCaches.game_name == game_name) - .all() - ) + with Session(ENGINE) as session: + result = ( + session.query(GameServerCaches) + .where(GameServerCaches.game_name == game_name) + .all() + ) data = [] for s in result: data.append(GameServerInfo(**s.__dict__)) @@ -136,14 +143,15 @@ def get_server_info_list_with_game_name(game_name: str) -> list[GameServerInfo]: def get_server_info_with_ip_and_port(ip: str, port: int) -> GameServerInfo | None: assert isinstance(ip, str) assert isinstance(port, int) - result = ( - PG_SESSION.query(GameServerCaches) - .where( - GameServerCaches.host_ip_address == ip, - GameServerCaches.query_report_port == port, + with Session(ENGINE) as session: + result = ( + session.query(GameServerCaches) + .where( + GameServerCaches.host_ip_address == ip, + GameServerCaches.query_report_port == port, + ) + .first() ) - .first() - ) if result is None: return None data = GameServerInfo(**result.__dict__) @@ -151,21 +159,24 @@ def get_server_info_with_ip_and_port(ip: str, port: int) -> GameServerInfo | Non def remove_server_info(info: GameServerCaches) -> None: - PG_SESSION.delete(info) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.delete(info) + session.commit() # todo finish the GameServerCaches creation def create_game_server(info: GameServerCaches) -> None: - PG_SESSION.add(info) - update_game_server() + with Session(ENGINE) as session: + session.add(info) + update_game_server() def update_game_server() -> None: # info.update_time = datetime.now() # type:ignore - PG_SESSION.commit() + with Session(ENGINE) as session: + session.commit() if __name__ == "__main__": diff --git a/src/backends/protocols/gamespy/query_report/handlers.py b/src/backends/protocols/gamespy/query_report/handlers.py index 2450e35b3..50fc5d3e7 100644 --- a/src/backends/protocols/gamespy/query_report/handlers.py +++ b/src/backends/protocols/gamespy/query_report/handlers.py @@ -1,9 +1,14 @@ from datetime import datetime from backends.library.abstractions.handler_base import HandlerBase -from backends.library.database.pg_orm import PG_SESSION, GameServerCaches -from backends.protocols.gamespy.query_report.requests import * +from backends.library.database.pg_orm import ENGINE, GameServerCaches +from backends.protocols.gamespy.query_report.requests import ( + AvaliableRequest, + HeartBeatRequest, + KeepAliveRequest, +) from frontends.gamespy.protocols.query_report.aggregates.exceptions import QRException import backends.protocols.gamespy.query_report.data as data +from sqlalchemy.orm import Session class AvaliableHandler(HandlerBase): @@ -14,33 +19,39 @@ class ChallengeHandler(HandlerBase): _request: HeartBeatRequest def _data_operate(self) -> None: - cache = PG_SESSION.query(GameServerCaches).where( - GameServerCaches.instant_key == self._request.instant_key).first() - if cache is None: - raise QRException( - "No server found, please make sure there is a server.") - cache.avaliable = True # type: ignore - PG_SESSION.commit() + with Session(ENGINE) as session: + cache = ( + session.query(GameServerCaches) + .where(GameServerCaches.instant_key == self._request.instant_key) + .first() + ) + if cache is None: + raise QRException( + "No server found, please make sure there is a server." + ) + cache.avaliable = True # type: ignore + session.commit() class Heartbeathandler(HandlerBase): _request: HeartBeatRequest def _data_operate(self) -> None: - cache = data.get_server_info_with_instant_key( - str(self._request.instant_key)) + cache = data.get_server_info_with_instant_key(str(self._request.instant_key)) if cache is None: - cache = GameServerCaches(instant_key=self._request.instant_key, - server_id=self._request.server_id, - host_ip_address=self._request.client_ip, - game_name=self._request.game_name, - query_report_port=self._request.client_port, - update_time=datetime.now(), - status=self._request.server_status, - player_data=self._request.player_data, - server_data=self._request.server_data, - team_data=self._request.team_data, - avaliable=True) + cache = GameServerCaches( + instant_key=self._request.instant_key, + server_id=self._request.server_id, + host_ip_address=self._request.client_ip, + game_name=self._request.game_name, + query_report_port=self._request.client_port, + update_time=datetime.now(), + status=self._request.server_status, + player_data=self._request.player_data, + server_data=self._request.server_data, + team_data=self._request.team_data, + avaliable=True, + ) data.create_game_server(cache) else: cache.instant_key = self._request.instant_key # type: ignore @@ -61,9 +72,13 @@ class KeepAliveHandler(HandlerBase): _request: KeepAliveRequest def _data_operate(self) -> None: - cache = PG_SESSION.query(GameServerCaches).where( - GameServerCaches.instant_key == self._request.instant_key).first() - # update heartbeat time - cache.update_time = datetime.now() # type: ignore + with Session(ENGINE) as session: + cache = ( + session.query(GameServerCaches) + .where(GameServerCaches.instant_key == self._request.instant_key) + .first() + ) + # update heartbeat time + cache.update_time = datetime.now() # type: ignore - PG_SESSION.commit() + session.commit() diff --git a/src/backends/protocols/gamespy/web_services/data.py b/src/backends/protocols/gamespy/web_services/data.py index d431192fb..8ee997b12 100644 --- a/src/backends/protocols/gamespy/web_services/data.py +++ b/src/backends/protocols/gamespy/web_services/data.py @@ -3,27 +3,52 @@ # region auth from typing import TYPE_CHECKING, cast -from backends.library.database.pg_orm import PG_SESSION, Profiles, SubProfiles, Users, SakeStorage -from frontends.gamespy.protocols.web_services.modules.auth.exceptions.general import AuthException -from frontends.gamespy.protocols.web_services.modules.sake.exceptions.general import SakeException - - -def is_user_exist(uniquenick: str, cdkey: str, partner_id: int, namespace_id: int, email: str, password: str) -> None: - result = PG_SESSION.query(Profiles)\ - .join(Users)\ - .join(SubProfiles)\ - .where(SubProfiles.uniquenick == uniquenick, - SubProfiles.cdkeyenc == cdkey, - SubProfiles.partnerid == partner_id, - SubProfiles.namespaceid == namespace_id, - Users.email == email, - Users.password == password).first() +from backends.library.database.pg_orm import ( + ENGINE, + Profiles, + SubProfiles, + Users, + SakeStorage, +) +from frontends.gamespy.protocols.web_services.modules.auth.exceptions.general import ( + AuthException, +) +from frontends.gamespy.protocols.web_services.modules.sake.exceptions.general import ( + SakeException, +) +from sqlalchemy.orm import Session + + +def is_user_exist( + uniquenick: str, + cdkey: str, + partner_id: int, + namespace_id: int, + email: str, + password: str, +) -> None: + with Session(ENGINE) as session: + result = ( + session.query(Profiles) + .join(Users) + .join(SubProfiles) + .where( + SubProfiles.uniquenick == uniquenick, + SubProfiles.cdkeyenc == cdkey, + SubProfiles.partnerid == partner_id, + SubProfiles.namespaceid == namespace_id, + Users.email == email, + Users.password == password, + ) + .first() + ) if result is None: - raise AuthException( - "No account exists with the provided email address.") + raise AuthException("No account exists with the provided email address.") -def get_info_by_cdkey_email(uniquenick: str, namespace_id: int, cdkey: str, email: str) -> tuple[int, int, str, str, str]: +def get_info_by_cdkey_email( + uniquenick: str, namespace_id: int, cdkey: str, email: str +) -> tuple[int, int, str, str, str]: """ return [user_id,profile_id,profile_nick,unique_nick,cdkey_hash] """ @@ -32,18 +57,27 @@ def get_info_by_cdkey_email(uniquenick: str, namespace_id: int, cdkey: str, emai assert isinstance(cdkey, str) assert isinstance(email, str) - result = PG_SESSION.query(Users, Profiles, SubProfiles)\ - .join(Users)\ - .join(Profiles,)\ - .join(SubProfiles)\ - .where(SubProfiles.uniquenick == uniquenick, - SubProfiles.namespaceid == namespace_id, - SubProfiles.cdkeyenc == cdkey, - Users.email == email).first() + with Session(ENGINE) as session: + result = ( + session.query(Users, Profiles, SubProfiles) + .join(Users) + .join( + Profiles, + ) + .join(SubProfiles) + .where( + SubProfiles.uniquenick == uniquenick, + SubProfiles.namespaceid == namespace_id, + SubProfiles.cdkeyenc == cdkey, + Users.email == email, + ) + .first() + ) if result is None: raise AuthException( - "No account exists with the provided uniquenick and namespace id.") + "No account exists with the provided uniquenick and namespace id." + ) user: Users = result[0] profile: Profiles = result[1] subprofile: SubProfiles = result[2] @@ -53,20 +87,29 @@ def get_info_by_cdkey_email(uniquenick: str, namespace_id: int, cdkey: str, emai assert isinstance(subprofile.uniquenick, str) assert isinstance(subprofile.cdkeyenc, str) - return user.userid, profile.profileid, profile.nick, subprofile.uniquenick, subprofile.cdkeyenc + return ( + user.userid, + profile.profileid, + profile.nick, + subprofile.uniquenick, + subprofile.cdkeyenc, + ) def get_info_by_authtoken(auth_token: str) -> tuple[int, int, str, str, str]: """ return [user_id,profile_id,profile_nick,unique_nick,cdkey_hash] """ - result = PG_SESSION.query(Users, Profiles, SubProfiles)\ - .join(Users, Users.userid == Profiles.userid).join( - Profiles, Profiles.profileid == SubProfiles.profileid)\ - .where(SubProfiles.authtoken == auth_token).first() + with Session(ENGINE) as session: + result = ( + session.query(Users, Profiles, SubProfiles) + .join(Users, Users.userid == Profiles.userid) + .join(Profiles, Profiles.profileid == SubProfiles.profileid) + .where(SubProfiles.authtoken == auth_token) + .first() + ) if result is None: - raise AuthException( - "No account exists with the provided authtoken.") + raise AuthException("No account exists with the provided authtoken.") user: Users = result[0] profile: Profiles = result[1] subprofile: SubProfiles = result[2] @@ -75,21 +118,37 @@ def get_info_by_authtoken(auth_token: str) -> tuple[int, int, str, str, str]: assert isinstance(profile.nick, str) assert isinstance(subprofile.uniquenick, str) assert isinstance(subprofile.cdkeyenc, str) - return user.userid, profile.profileid, profile.nick, subprofile.uniquenick, subprofile.cdkeyenc - - -def get_info_by_uniquenick(uniquenick: str, namespace_id: int) -> tuple[int, int, str, str, str]: + return ( + user.userid, + profile.profileid, + profile.nick, + subprofile.uniquenick, + subprofile.cdkeyenc, + ) + + +def get_info_by_uniquenick( + uniquenick: str, namespace_id: int +) -> tuple[int, int, str, str, str]: """ return [user_id,profile_id,profile_nick,unique_nick,cdkey_hash] """ - result = PG_SESSION.query(Users, Profiles, SubProfiles)\ - .join(Users, Users.userid == Profiles.userid)\ - .join(Profiles, Profiles.profileid == SubProfiles.profileid)\ - .where(SubProfiles.uniquenick == uniquenick, SubProfiles.namespaceid == namespace_id).first() + with Session(ENGINE) as session: + result = ( + session.query(Users, Profiles, SubProfiles) + .join(Users, Users.userid == Profiles.userid) + .join(Profiles, Profiles.profileid == SubProfiles.profileid) + .where( + SubProfiles.uniquenick == uniquenick, + SubProfiles.namespaceid == namespace_id, + ) + .first() + ) if result is None: raise AuthException( - "No account exists with the provided uniquenick and namespace id.") + "No account exists with the provided uniquenick and namespace id." + ) user: Users = result[0] profile: Profiles = result[1] subprofile: SubProfiles = result[2] @@ -99,7 +158,14 @@ def get_info_by_uniquenick(uniquenick: str, namespace_id: int) -> tuple[int, int assert isinstance(subprofile.uniquenick, str) assert isinstance(subprofile.cdkeyenc, str) - return user.userid, profile.profileid, profile.nick, subprofile.uniquenick, subprofile.cdkeyenc + return ( + user.userid, + profile.profileid, + profile.nick, + subprofile.uniquenick, + subprofile.cdkeyenc, + ) + # region d2g @@ -112,9 +178,15 @@ def get_info_by_uniquenick(uniquenick: str, namespace_id: int) -> tuple[int, int # region sake -def get_user_data(table_id: int,) -> dict: - result = PG_SESSION.query(SakeStorage.data).where( - SakeStorage.tableid == table_id).first() +def get_user_data( + table_id: int, +) -> dict: + with Session(ENGINE) as session: + result = ( + session.query(SakeStorage.data) + .where(SakeStorage.tableid == table_id) + .first() + ) if TYPE_CHECKING: result = cast(dict, result) @@ -122,8 +194,10 @@ def get_user_data(table_id: int,) -> dict: def update_user_data(table_id: int, data: dict) -> None: - result = PG_SESSION.query(SakeStorage).where( - SakeStorage.tableid == table_id).first() + with Session(ENGINE) as session: + result = ( + session.query(SakeStorage).where(SakeStorage.tableid == table_id).first() + ) if result is None: raise SakeException("user data not found") assert isinstance(result.data, dict) @@ -139,20 +213,26 @@ def update_user_data(table_id: int, data: dict) -> None: def create_records(table_id: int, data: dict) -> None: assert isinstance(table_id, int) assert isinstance(data, dict) - result = PG_SESSION.query(SakeStorage).where( - SakeStorage.tableid == table_id).count() + with Session(ENGINE) as session: + result = ( + session.query(SakeStorage).where(SakeStorage.tableid == table_id).count() + ) - if result != 0: - raise SakeException("Records already existed") + if result != 0: + raise SakeException("Records already existed") - sake = SakeStorage(table_id=table_id, data=data) + sake = SakeStorage(table_id=table_id, data=data) - PG_SESSION.add(sake) - PG_SESSION.commit() + session.add(sake) + session.commit() if __name__ == "__main__": - result = PG_SESSION.query(Users, Profiles, SubProfiles)\ - .join(Profiles, Users.userid == Profiles.userid)\ - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid)\ - .where(Users.userid == 1).first() + with Session(ENGINE) as session: + result = ( + session.query(Users, Profiles, SubProfiles) + .join(Profiles, Users.userid == Profiles.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where(Users.userid == 1) + .first() + ) diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index 0989a4afb..8770120a5 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -50,6 +50,8 @@ def validation_exception_handler(_, exc: RequestValidationError): @app.exception_handler(Exception) def handle_unispy_exception(_, exc: Exception): str_error = str(exc) + if len(str_error) == 0: + str_error = exc.__class__.__name__ logger.error(str_error) return JSONResponse({"error": str_error}) diff --git a/src/backends/tests/gamespy/query_report/data_fetch_tests.py b/src/backends/tests/gamespy/query_report/data_fetch_tests.py index 7a203110d..2cf159ec6 100644 --- a/src/backends/tests/gamespy/query_report/data_fetch_tests.py +++ b/src/backends/tests/gamespy/query_report/data_fetch_tests.py @@ -2,8 +2,9 @@ import unittest from pydantic import ValidationError -from backends.library.database.pg_orm import PG_SESSION, ChatChannelCaches +from backends.library.database.pg_orm import ENGINE, ChatChannelCaches import backends.protocols.gamespy.query_report.data as data +from sqlalchemy.orm import Session class DataFetchTests(unittest.TestCase): @@ -21,10 +22,14 @@ def test_get_peer_staging_channels(self): max_num_user=100, update_time=datetime.now(timezone.utc), ) - PG_SESSION.add(cache) - PG_SESSION.commit() - self.assertRaises( - ValidationError, data.get_peer_staging_channels, "unispy_test_game_name", 0 - ) - PG_SESSION.delete(cache) - PG_SESSION.commit() + with Session(ENGINE) as session: + session.add(cache) + session.commit() + self.assertRaises( + ValidationError, + data.get_peer_staging_channels, + "unispy_test_game_name", + 0, + ) + session.delete(cache) + session.commit() diff --git a/src/frontends/gamespy/library/abstractions/handler.py b/src/frontends/gamespy/library/abstractions/handler.py index 0c95a07d1..c747b7195 100644 --- a/src/frontends/gamespy/library/abstractions/handler.py +++ b/src/frontends/gamespy/library/abstractions/handler.py @@ -123,7 +123,9 @@ def _upload_data(self): raise UniSpyException("backends is not avaliable") if response.status_code != 200: - raise UniSpyException("failed to upload data to backends.") + raise UniSpyException( + f"failed to upload data to backends. reason: {response.text}" + ) self._http_result = response.json() diff --git a/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py b/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py index 565f47da9..eaebf2425 100644 --- a/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py @@ -34,7 +34,7 @@ def parse(self) -> None: self.version = int(self.raw_request[6]) self.command_name = RequestType(self.raw_request[7]) self.cookie = int.from_bytes( - self.raw_request[8:12], byteorder="little") + self.raw_request[8:12]) self.port_type = NatPortType(self.raw_request[12]) diff --git a/src/frontends/gamespy/protocols/natneg/applications/handlers.py b/src/frontends/gamespy/protocols/natneg/applications/handlers.py index 6ab2c134b..47b09ab80 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/handlers.py +++ b/src/frontends/gamespy/protocols/natneg/applications/handlers.py @@ -36,6 +36,7 @@ def __init__(self, client: Client, request: AddressCheckRequest) -> None: assert isinstance(request, AddressCheckRequest) super().__init__(client, request) self._is_fetching = False + self._is_uploading = False def _response_construct(self) -> None: """ @@ -130,6 +131,7 @@ def __init__(self, client: Client, request: NatifyRequest) -> None: assert isinstance(request, NatifyRequest) super().__init__(client, request) self._is_fetching = False + self._is_uploading = False def _response_construct(self): """ @@ -142,9 +144,6 @@ def _response_construct(self): self._response = NatifyResponse(self._request, self._result) - - - class ReportHandler(CmdHandlerBase): _request: ReportRequest _result: ReportResult diff --git a/src/frontends/gamespy/protocols/natneg/applications/switcher.py b/src/frontends/gamespy/protocols/natneg/applications/switcher.py index 5012e23b1..5fe01791f 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/switcher.py +++ b/src/frontends/gamespy/protocols/natneg/applications/switcher.py @@ -7,6 +7,7 @@ ConnectRequest, ErtAckRequest, InitRequest, + NatifyRequest, ReportRequest, ) from frontends.gamespy.protocols.natneg.aggregations.enums import RequestType @@ -15,6 +16,7 @@ ConnectHandler, ErtAckHandler, InitHandler, + NatifyHandler, ReportHandler, ) @@ -29,11 +31,9 @@ def __init__(self, client: Client, raw_request: bytes) -> None: assert isinstance(raw_request, bytes) def _process_raw_request(self) -> None: - name = self._raw_request[7] if name not in RequestType: - self._client.log_debug( - f"Request: {name} is not a valid request.") + self._client.log_debug(f"Request: {name} is not a valid request.") return self._requests.append((RequestType(name), self._raw_request)) @@ -43,6 +43,8 @@ def _create_cmd_handlers( assert isinstance(name, RequestType) assert isinstance(raw_request, bytes) match name: + case RequestType.NATIFY_REQUEST: + return NatifyHandler(self._client, NatifyRequest(raw_request)) case RequestType.INIT: return InitHandler(self._client, InitRequest(raw_request)) # case RequestType. From ca7b8104242ed75a929e780d93274c75cef1aeec Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sun, 22 Jun 2025 02:13:42 +0000 Subject: [PATCH 192/231] Simplify network service function call --- .../library/abstractions/connections.py | 5 ++- .../library/abstractions/server_launcher.py | 12 +++--- .../gamespy/library/extentions/schedular.py | 2 +- .../gamespy/library/network/http_handler.py | 7 +--- .../gamespy/library/network/tcp_handler.py | 15 ++++--- .../gamespy/library/network/udp_handler.py | 3 -- .../tests/gamespy/natneg/handler_tests.py | 42 ++++++++++++------- 7 files changed, 47 insertions(+), 39 deletions(-) diff --git a/src/frontends/gamespy/library/abstractions/connections.py b/src/frontends/gamespy/library/abstractions/connections.py index 763d55604..756bf4eba 100644 --- a/src/frontends/gamespy/library/abstractions/connections.py +++ b/src/frontends/gamespy/library/abstractions/connections.py @@ -1,5 +1,6 @@ import abc import socketserver +import threading from frontends.gamespy.library.abstractions.client import ClientBase from frontends.gamespy.library.log.log_manager import LogWriter @@ -83,9 +84,9 @@ def __init__( self._client_cls = t_client self._logger = logger - @abc.abstractmethod def start(self): - pass + thread = threading.Thread(target=self._server.serve_forever) + thread.start() def __del__(self): self._server.shutdown() diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index e2e08ca1f..cd825f0b0 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -44,7 +44,7 @@ def start(self): self.__show_unispy_logo() self._launch_server() print("Server successfully launched.") - self.__keep_running() + self._keep_running() def __show_unispy_logo(self): # display logo @@ -66,9 +66,10 @@ def __show_unispy_logo(self): def _launch_server(self) -> None: if self.server is None: raise UniSpyException("Create network server in child class") - print("Press Ctrl+C to Quit") self._heartbeat_to_backend() self.server.start() + self._keep_running() + pass def _connect_to_backend(self): """ @@ -101,13 +102,10 @@ def _heartbeat_to_backend(self): self.schedular = Schedular(self._connect_to_backend, 30) self.schedular.start() - def _test(self): - print("test") - def _create_logger(self): assert self.config is not None short_name = _SERVER_FULL_SHORT_NAME_MAPPING[self.config.server_name] self.logger = LogManager.create(short_name) - def __keep_running(self): - _ = input("Press Q to Quit") + def _keep_running(self): + _ = input("Press any key to Quit\n") diff --git a/src/frontends/gamespy/library/extentions/schedular.py b/src/frontends/gamespy/library/extentions/schedular.py index 894c2c9fd..d5bb9d8a1 100644 --- a/src/frontends/gamespy/library/extentions/schedular.py +++ b/src/frontends/gamespy/library/extentions/schedular.py @@ -27,7 +27,7 @@ def _run_schedule(self): if not self._is_started: break schedule.run_pending() - time.sleep(self._interval) + time.sleep(self._interval/2) def stop(self): self._is_started = False diff --git a/src/frontends/gamespy/library/network/http_handler.py b/src/frontends/gamespy/library/network/http_handler.py index d36495e29..cbfc2d59e 100644 --- a/src/frontends/gamespy/library/network/http_handler.py +++ b/src/frontends/gamespy/library/network/http_handler.py @@ -64,12 +64,7 @@ def __init__( self._server = ThreadingHTTPServer( (self._config.public_address, self._config.listening_port), HttpHandler ) - # fmt: off - self._server.unispy_params = (self._config, self._client_cls, self._logger)# type: ignore - # fmt: on - - def start(self) -> None: - self._server.serve_forever() + self._server.unispy_params = (self._config, self._client_cls, self._logger) # type: ignore class TestClient(ClientBase): diff --git a/src/frontends/gamespy/library/network/tcp_handler.py b/src/frontends/gamespy/library/network/tcp_handler.py index 9ecfd8b99..928cf204d 100644 --- a/src/frontends/gamespy/library/network/tcp_handler.py +++ b/src/frontends/gamespy/library/network/tcp_handler.py @@ -2,7 +2,10 @@ import socketserver from typing import Optional from frontends.gamespy.library.abstractions.client import ClientBase -from frontends.gamespy.library.abstractions.connections import ConnectionBase, NetworkServerBase +from frontends.gamespy.library.abstractions.connections import ( + ConnectionBase, + NetworkServerBase, +) from frontends.gamespy.library.log.log_manager import LogWriter from frontends.gamespy.library.network import DATA_SIZE @@ -48,17 +51,17 @@ def handle(self) -> None: class TcpServer(NetworkServerBase): - def __init__(self, config: ServerConfig, t_client: type[ClientBase], logger: LogWriter) -> None: + def __init__( + self, config: ServerConfig, t_client: type[ClientBase], logger: LogWriter + ) -> None: super().__init__(config, t_client, logger) self._server = socketserver.ThreadingTCPServer( (self._config.public_address, self._config.listening_port), TcpHandler, ) - def start(self) -> None: - - self._server.allow_reuse_address = True self._server.unispy_params = (self._config, self._client_cls, self._logger) # type: ignore - self._server.serve_forever() + self._server.allow_reuse_address = True # type:ignore + class TestClient(ClientBase): diff --git a/src/frontends/gamespy/library/network/udp_handler.py b/src/frontends/gamespy/library/network/udp_handler.py index 23fa9098e..7483986aa 100644 --- a/src/frontends/gamespy/library/network/udp_handler.py +++ b/src/frontends/gamespy/library/network/udp_handler.py @@ -42,9 +42,6 @@ def __init__( # inject the handler params to ThreadingUDPServer self._server.unispy_params = (self._config, self._client_cls, self._logger) # type: ignore - def start(self) -> None: - self._server.serve_forever() - def __exit__(self, *args): self._server.__exit__(*args) diff --git a/src/frontends/tests/gamespy/natneg/handler_tests.py b/src/frontends/tests/gamespy/natneg/handler_tests.py index 4ab8520c9..b517579d9 100644 --- a/src/frontends/tests/gamespy/natneg/handler_tests.py +++ b/src/frontends/tests/gamespy/natneg/handler_tests.py @@ -1,7 +1,12 @@ import unittest from frontends.gamespy.library.configs import CONFIG -from frontends.gamespy.protocols.natneg.applications.handlers import AddressCheckHandler, ErtAckHandler, InitHandler, NatifyHandler +from frontends.gamespy.protocols.natneg.applications.handlers import ( + AddressCheckHandler, + ErtAckHandler, + InitHandler, + NatifyHandler, +) import responses from frontends.gamespy.protocols.natneg.contracts.requests import ( AddressCheckRequest, @@ -18,6 +23,7 @@ ) from frontends.tests.gamespy.natneg.mock_objects import create_client + CONFIG.unittest.is_raise_except = True @@ -37,7 +43,7 @@ def test_init(self): # test request parsing request = InitRequest(raw) request.parse() - self.assertEqual(151191552, request.cookie) + self.assertEqual(777, request.cookie) self.assertEqual(RequestType.INIT, request.command_name) self.assertEqual(NatClientIndex.GAME_CLIENT, request.client_index) self.assertEqual(False, request.use_game_port) @@ -47,8 +53,10 @@ def test_init(self): handler.handle() # test response constructing - self.assertTrue(handler._response.sending_buffer == - b'\xfd\xfc\x1efj\xb2\x03\x01\t\x03\x00\x00\x01\x00\xc0\xa8\x00\x01\x00\x00') + self.assertTrue( + handler._response.sending_buffer + == b"\xfd\xfc\x1efj\xb2\x03\x01\x00\x00\x03\t\x01\x00\xc0\xa8\x00\x01\x00\x00" + ) @responses.activate def test_address_check(self): @@ -60,7 +68,7 @@ def test_address_check(self): request = AddressCheckRequest(raw) request.parse() - self.assertEqual(151191552, request.cookie) + self.assertEqual(777, request.cookie) self.assertEqual(RequestType.ADDRESS_CHECK, request.command_name) self.assertEqual(NatClientIndex.GAME_CLIENT, request.client_index) self.assertEqual(False, request.use_game_port) @@ -71,8 +79,10 @@ def test_address_check(self): handler = AddressCheckHandler(client, request) handler.handle() - self.assertTrue(handler._response.sending_buffer == - b'\xfd\xfc\x1efj\xb2\x03\x0b\t\x03\x00\x00\x01\x00\xc0\xa8\x00\x01\x00\x00') + self.assertTrue( + handler._response.sending_buffer + == b"\xfd\xfc\x1efj\xb2\x03\x0b\x00\x00\x03\t\x01\x00\xc0\xa8\x00\x01\x00\x00" + ) @responses.activate def test_ert_ack(self): @@ -86,7 +96,7 @@ def test_ert_ack(self): ) # fmt: skip request = ErtAckRequest(raw) request.parse() - self.assertEqual(151191552, request.cookie) + self.assertEqual(777, request.cookie) self.assertEqual(RequestType.ERT_ACK, request.command_name) self.assertEqual(NatClientIndex.GAME_CLIENT, request.client_index) self.assertEqual(3, request.version) @@ -96,8 +106,10 @@ def test_ert_ack(self): handler = ErtAckHandler(client, request) handler.handle() - self.assertTrue(handler._response.sending_buffer == - b'\xfd\xfc\x1efj\xb2\x03\x03\t\x03\x00\x00\x01\x00\xc0\xa8\x00\x01\x00\x00') + self.assertTrue( + handler._response.sending_buffer + == b'\xfd\xfc\x1efj\xb2\x03\x03\x00\x00\x03\t\x01\x00\xc0\xa8\x00\x01\x00\x00' + ) @responses.activate def test_natify(self): @@ -111,7 +123,7 @@ def test_natify(self): ) # fmt: skip request = NatifyRequest(raw) request.parse() - self.assertEqual(151191552, request.cookie) + self.assertEqual(777, request.cookie) self.assertEqual(RequestType.NATIFY_REQUEST, request.command_name) self.assertEqual(NatClientIndex.GAME_CLIENT, request.client_index) self.assertEqual(3, request.version) @@ -121,8 +133,10 @@ def test_natify(self): handler = NatifyHandler(client, request) handler.handle() - self.assertTrue(handler._response.sending_buffer == - b'\xfd\xfc\x1efj\xb2\x03\x02\t\x03\x00\x00\x01\x00\xc0\xa8\x00\x01\x00\x00') + self.assertTrue( + handler._response.sending_buffer + == b'\xfd\xfc\x1efj\xb2\x03\x02\x00\x00\x03\t\x01\x00\xc0\xa8\x00\x01\x00\x00' + ) @responses.activate def test_preinit(self): @@ -134,7 +148,7 @@ def test_preinit(self): req = PreInitRequest(raw) req.parse() - self.assertEqual(714465461, req.cookie) + self.assertEqual(3051394346, req.cookie) self.assertEqual(RequestType.PRE_INIT, req.command_name) self.assertEqual(4, req.version) self.assertEqual(NatPortType.GP, req.port_type) From f512e1ab0bda9855830cf40fb9e7636ed8f68417 Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Tue, 24 Jun 2025 03:12:13 +0000 Subject: [PATCH 193/231] Fix[gstats]: auth game related functions --- .../library/abstractions/contracts.py | 2 +- .../protocols/gamespy/game_status/handlers.py | 39 ++++++++++++++---- .../protocols/gamespy/game_status/requests.py | 9 ++-- .../presence_connection_manager/data.py | 25 +++++------ .../tests/gamespy/game_status/__init__.py | 0 .../gamespy/game_status/handler_tests.py | 29 +++++++++++++ .../gamespy/library/abstractions/handler.py | 9 ++-- .../library/encryption/xor_encryption.py | 1 - .../game_status/applications/client.py | 14 +++++-- .../game_status/contracts/requests.py | 41 +++++++++++-------- .../tests/gamespy/library/mock_objects.py | 3 +- 11 files changed, 121 insertions(+), 51 deletions(-) create mode 100644 src/backends/tests/gamespy/game_status/__init__.py create mode 100644 src/backends/tests/gamespy/game_status/handler_tests.py diff --git a/src/backends/library/abstractions/contracts.py b/src/backends/library/abstractions/contracts.py index 16c8d441f..b16e3e2e0 100644 --- a/src/backends/library/abstractions/contracts.py +++ b/src/backends/library/abstractions/contracts.py @@ -27,7 +27,7 @@ class OKResponse(Response): class DataResponse(OKResponse): - result: object + result: dict pass diff --git a/src/backends/protocols/gamespy/game_status/handlers.py b/src/backends/protocols/gamespy/game_status/handlers.py index 32a14dffd..ec18b40fb 100644 --- a/src/backends/protocols/gamespy/game_status/handlers.py +++ b/src/backends/protocols/gamespy/game_status/handlers.py @@ -1,15 +1,34 @@ - - from backends.library.abstractions.handler_base import HandlerBase -from backends.protocols.gamespy.game_status.requests import * import backends.protocols.gamespy.game_status.data as data +from backends.protocols.gamespy.game_status.requests import ( + AuthGameRequest, + AuthPlayerRequest, + GetPlayerDataRequest, + GetProfileIdRequest, + NewGameRequest, + SetPlayerDataRequest, +) +from frontends.gamespy.protocols.game_status.aggregations.enums import AuthMethod from frontends.gamespy.protocols.game_status.aggregations.exceptions import GSException -from frontends.gamespy.protocols.game_status.contracts.results import AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult +from frontends.gamespy.protocols.game_status.contracts.results import ( + AuthGameResult, + AuthPlayerResult, + GetPlayerDataResult, + GetProfileIdResult, +) class AuthGameHandler(HandlerBase): _request: AuthGameRequest + def _data_operate(self) -> None: + # generate session key + # todo: check whether need to store on database + self.session_key = "11111" + + def _result_construct(self) -> None: + self._result = AuthGameResult(session_key=self.session_key) + class AuthPlayerHandler(HandlerBase): _request: AuthPlayerRequest @@ -20,10 +39,12 @@ def _data_operate(self): self.data = data.get_profile_id_by_token(token=self._request.auth_token) case AuthMethod.PROFILE_ID_AUTH: self.data = data.get_profile_id_by_profile_id( - profile_id=self._request.profile_id) + profile_id=self._request.profile_id + ) case AuthMethod.CDKEY_AUTH: self.data = data.get_profile_id_by_cdkey( - cdkey=self._request.cdkey_hash, nick_name=self._request.nick) + cdkey=self._request.cdkey_hash, nick_name=self._request.nick + ) case _: raise GSException("Invalid auth type") @@ -38,7 +59,8 @@ def _data_operate(self): self.data = data.get_player_data( self._request.profile_id, self._request.storage_type, - self._request.data_index) + self._request.data_index, + ) def _result_construct(self): self._result = GetPlayerDataResult(keyvalues=self.data) @@ -49,7 +71,8 @@ class GetProfileIdHandler(HandlerBase): def _data_operate(self): self.data = data.get_profile_id_by_cdkey( - cdkey=self._request.cdkey, nick_name=self._request.nick) + cdkey=self._request.cdkey, nick_name=self._request.nick + ) def _result_construct(self): self._result = GetProfileIdResult(profile_id=self.data) diff --git a/src/backends/protocols/gamespy/game_status/requests.py b/src/backends/protocols/gamespy/game_status/requests.py index 5511d816b..56167c828 100644 --- a/src/backends/protocols/gamespy/game_status/requests.py +++ b/src/backends/protocols/gamespy/game_status/requests.py @@ -1,4 +1,6 @@ from typing import Optional + +from pydantic import Field import backends.library.abstractions.contracts as lib from frontends.gamespy.protocols.game_status.aggregations.enums import ( AuthMethod, @@ -7,7 +9,6 @@ class RequestBase(lib.RequestBase): - command_name: str raw_request: str local_id: Optional[int] = None request_dict: dict[str, str] @@ -42,8 +43,10 @@ class GetProfileIdRequest(RequestBase): class NewGameRequest(RequestBase): is_client_local_storage_available: bool challenge: str - connection_id: int - session_key: str + connection_id: int = Field( + description="The session key that backend send to client." + ) + session_key: str = Field(description="The game session key") class SetPlayerDataRequest(RequestBase): diff --git a/src/backends/protocols/gamespy/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py index 59da838d4..fe5b85ee4 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/data.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/data.py @@ -343,18 +343,19 @@ def get_user_infos_by_authtoken(auth_token: str) -> LoginData | None: ) if result is not None: - data = { - "user_id": result[0], - "profile_id": result[1], - "sub_profile_id": result[2], - "nick": result[3], - "email": result[4], - "unique_nick": result[5], - "password_hash": result[6], - "email_verified_flag": result[7], - "banned_flag": result[8], - "namespace_id": result[9], - } + keys = [ + "user_id", + "profile_id", + "sub_profile_id", + "nick", + "email", + "unique_nick", + "password_hash", + "email_verified_flag", + "banned_flag", + "namespace_id", + ] + data = {key: result[i] for i, key in enumerate(keys)} return LoginData(**data) # type: ignore else: return None diff --git a/src/backends/tests/gamespy/game_status/__init__.py b/src/backends/tests/gamespy/game_status/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/gamespy/game_status/handler_tests.py b/src/backends/tests/gamespy/game_status/handler_tests.py new file mode 100644 index 000000000..2434b6ae1 --- /dev/null +++ b/src/backends/tests/gamespy/game_status/handler_tests.py @@ -0,0 +1,29 @@ +import unittest + +from backends.protocols.gamespy.game_status.handlers import AuthGameHandler +from backends.protocols.gamespy.game_status.requests import AuthGameRequest + + +class HandlerTests(unittest.TestCase): + def test_auth_game(self): + raw = { + "raw_request": "\\auth\\\\gamename\\gmtest\\response\\b7f8b7f83dcc4427c35864c5d53c5fe5\\port\\2667\\id\\1\\final\\", + "request_dict": { + "auth": "", + "gamename": "gmtest", + "response": "b7f8b7f83dcc4427c35864c5d53c5fe5", + "port": "2667", + "id": "1", + }, + "local_id": 1, + "game_name": "gmtest", + "response": "b7f8b7f83dcc4427c35864c5d53c5fe5", + "port": 2667, + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 38996, + } + req = AuthGameRequest(**raw) + h = AuthGameHandler(req) + h.handle() + pass diff --git a/src/frontends/gamespy/library/abstractions/handler.py b/src/frontends/gamespy/library/abstractions/handler.py index c747b7195..350dba00b 100644 --- a/src/frontends/gamespy/library/abstractions/handler.py +++ b/src/frontends/gamespy/library/abstractions/handler.py @@ -79,7 +79,7 @@ def _data_operate(self) -> None: if self._is_uploading: self._upload_data() if self._is_fetching: - self._feach_data() + self._fetch_data() def _prepare_data(self): self._temp_data = self._request.to_dict() @@ -139,7 +139,7 @@ def _handle_upload_error(self): # we raise the error as UniSpyException raise UniSpyException(self._http_result["error"]) - def _feach_data(self): + def _fetch_data(self): """ whether need get data from backend. if child class do not require feach, overide this function to do nothing @@ -148,8 +148,9 @@ def _feach_data(self): raise UniSpyException("_result_cls can not be null when feach data.") assert issubclass(self._result_cls, ResultBase) self._client.log_network_fetch(f"[{self._url}] {self._http_result}") - - self._result = self._result_cls(**self._http_result) + if "result" not in self._http_result: + raise UniSpyException("result can not be empty when feach data") + self._result = self._result_cls(**self._http_result["result"]) def _response_construct(self) -> None: """construct response here in specific child class""" diff --git a/src/frontends/gamespy/library/encryption/xor_encryption.py b/src/frontends/gamespy/library/encryption/xor_encryption.py index bd4e00cc9..7f705a95f 100644 --- a/src/frontends/gamespy/library/encryption/xor_encryption.py +++ b/src/frontends/gamespy/library/encryption/xor_encryption.py @@ -36,7 +36,6 @@ def encode(plaintext: bytes, enc_type: XorType): key_index = 0 for index in range(len(plaintext)): key_index = index % len(key) - print(key_index) enc_byte = (plaintext[index] ^ key[key_index]) % 255 result.append(enc_byte) diff --git a/src/frontends/gamespy/protocols/game_status/applications/client.py b/src/frontends/gamespy/protocols/game_status/applications/client.py index f31450343..f24d01a0b 100644 --- a/src/frontends/gamespy/protocols/game_status/applications/client.py +++ b/src/frontends/gamespy/protocols/game_status/applications/client.py @@ -34,14 +34,17 @@ class Client(ClientBase): info: ClientInfo client_pool: dict[str, "Client"] = {} - def __init__(self, connection: TcpConnection, server_config: ServerConfig, logger: LogWriter): + def __init__( + self, connection: TcpConnection, server_config: ServerConfig, logger: LogWriter + ): super().__init__(connection, server_config, logger) self.info = ClientInfo() def on_connected(self) -> None: self.crypto = GSCrypt() self.log_network_sending(CHALLENGE_RESPONSE) - self.connection.send(CHALLENGE_RESPONSE.encode("ascii")) + enc_buffer = self.crypto.encrypt(CHALLENGE_RESPONSE.encode("ascii")) + self.connection.send(enc_buffer) def decrypt_message(self, buffer: bytes) -> bytes: if self.crypto is None: @@ -52,12 +55,15 @@ def decrypt_message(self, buffer: bytes) -> bytes: if len(temp) > 1: message = "" for t in temp: - complete_buffer = (t+"\\final\\").encode() + complete_buffer = (t + "\\final\\").encode() message += self.crypto.decrypt(complete_buffer).decode() return message.encode() return self.crypto.decrypt(buffer) def _create_switcher(self, buffer: bytes) -> SwitcherBase: - from frontends.gamespy.protocols.game_status.applications.switcher import Switcher + from frontends.gamespy.protocols.game_status.applications.switcher import ( + Switcher, + ) + return Switcher(self, buffer.decode()) diff --git a/src/frontends/gamespy/protocols/game_status/contracts/requests.py b/src/frontends/gamespy/protocols/game_status/contracts/requests.py index 06c65ca77..c38c28c50 100644 --- a/src/frontends/gamespy/protocols/game_status/contracts/requests.py +++ b/src/frontends/gamespy/protocols/game_status/contracts/requests.py @@ -1,7 +1,10 @@ from typing import Optional, final from frontends.gamespy.library.extentions.gamespy_utils import convert_to_key_value from frontends.gamespy.protocols.game_status.abstractions.contracts import RequestBase -from frontends.gamespy.protocols.game_status.aggregations.enums import AuthMethod, PersistStorageType +from frontends.gamespy.protocols.game_status.aggregations.enums import ( + AuthMethod, + PersistStorageType, +) from frontends.gamespy.protocols.game_status.aggregations.exceptions import GSException @@ -25,7 +28,7 @@ def parse(self) -> None: if "port" in self.request_dict: try: self.port = int(self.request_dict["port"]) - except: + except ValueError: raise GSException("port format is incorrect") @@ -46,7 +49,7 @@ def parse(self) -> None: if "pid" in self.request_dict and "resp" in self.request_dict: try: self.profile_id = int(self.request_dict["pid"]) - except: + except ValueError: raise GSException("profile id format is incorrect") self.auth_type = AuthMethod.PROFILE_ID_AUTH elif "authtoken" in self.request_dict and "response" in self.request_dict: @@ -82,20 +85,19 @@ def parse(self) -> None: if "pid" in self.request_dict: try: self.profile_id = int(self.request_dict["pid"]) - except: + except ValueError: raise GSException("pid format is incorrect") if "ptype" in self.request_dict: try: - self.storage_type = PersistStorageType( - int(self.request_dict["ptype"])) - except: + self.storage_type = PersistStorageType(int(self.request_dict["ptype"])) + except ValueError: raise GSException("ptype format is incorrect") if "dindex" in self.request_dict: try: self.data_index = int(self.request_dict["dindex"]) - except: + except ValueError: raise GSException("dindex format is incorrect") if "keys" not in self.request_dict: @@ -135,7 +137,13 @@ class NewGameRequest(RequestBase): is_client_local_storage_available: bool challenge: str connection_id: int + """ + client session key + """ session_key: str + """ + game session key + """ def parse(self) -> None: super().parse() @@ -148,7 +156,7 @@ def parse(self) -> None: raise GSException("connid is missing") try: self.connection_id = int(self.request_dict["connid"]) - except: + except ValueError: raise GSException("connid format is incorrect") if "challenge" in self.request_dict: @@ -180,23 +188,22 @@ def parse(self) -> None: try: self.profile_id = int(self.request_dict["pid"]) - except: + except ValueError: raise GSException("pid format is incorrect") try: - self.storage_type = PersistStorageType( - int(self.request_dict["ptype"])) - except: + self.storage_type = PersistStorageType(int(self.request_dict["ptype"])) + except ValueError: raise GSException("ptype format is incorrect") try: self.data_index = int(self.request_dict["dindex"]) - except: + except ValueError: raise GSException("dindex format is incorrect") try: self.length = int(self.request_dict["length"]) - except: + except ValueError: raise GSException("length format is incorrect") if "report" in self.request_dict: @@ -230,7 +237,7 @@ def parse(self) -> None: if "dl" in self.request_dict: self.is_client_local_storage_available = True - if 'done' not in self.request_dict: + if "done" not in self.request_dict: raise GSException("done is missing") done = self.request_dict["done"] @@ -250,5 +257,5 @@ def parse(self) -> None: if "connid" in self.request_dict: try: self.connection_id = int(self.request_dict["connid"]) - except: + except ValueError: raise GSException("connid format is incorrect") diff --git a/src/frontends/tests/gamespy/library/mock_objects.py b/src/frontends/tests/gamespy/library/mock_objects.py index b35b4cd5d..6057a03f5 100644 --- a/src/frontends/tests/gamespy/library/mock_objects.py +++ b/src/frontends/tests/gamespy/library/mock_objects.py @@ -61,4 +61,5 @@ def create_mock_url( config: ServerConfig, handler: type[CmdHandlerBase], data: dict ) -> None: url = f"{CONFIG.backend.url}/GameSpy/{config.server_name}/{handler.__name__}" - responses.add(responses.POST, url, json=data, status=200) + resp = {"result": data} + responses.add(responses.POST, url, json=resp, status=200) From ce610e1f6308b6c88a10f585cd9308fc1ae4bcd7 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 26 Jul 2025 00:08:28 +0000 Subject: [PATCH 194/231] Fix[chat]: server port binded twice --- .../gamespy/protocols/chat/applications/server_launcher.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/frontends/gamespy/protocols/chat/applications/server_launcher.py b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py index c9b524a04..57e6e081b 100644 --- a/src/frontends/gamespy/protocols/chat/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py @@ -15,7 +15,6 @@ def _launch_server(self): assert self.config is not None assert self.logger is not None self.server = TcpServer(self.config, Client, self.logger) - self._launch_server() if __name__ == "__main__": From 290cc9afbdf1f9fe1b010dd16c0798551d68a27a Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sun, 27 Jul 2025 00:33:58 +0000 Subject: [PATCH 195/231] Fix: code style of PEP 604 --- src/backends/library/abstractions/handler_base.py | 8 ++++---- .../protocols/gamespy/game_status/requests.py | 4 ++-- .../protocols/gamespy/game_traffic_relay/data.py | 4 ++-- src/backends/protocols/gamespy/natneg/data.py | 4 ++-- src/backends/protocols/gamespy/natneg/requests.py | 4 ++-- .../presence_connection_manager/requests.py | 14 +++++++------- .../gamespy/presence_search_player/requests.py | 10 +++++----- .../protocols/gamespy/query_report/data.py | 4 ++-- .../protocols/gamespy/server_browser/requests.py | 8 ++++---- .../protocols/gamespy/web_services/requests.py | 4 ++-- src/backends/tests/gamespy/chat/room_tests.py | 2 +- .../gamespy/library/abstractions/contracts.py | 4 ++-- .../gamespy/library/abstractions/handler.py | 10 +++++----- .../gamespy/library/abstractions/redis_objects.py | 2 +- .../library/abstractions/server_launcher.py | 8 ++++---- .../gamespy/library/abstractions/switcher.py | 4 ++-- src/frontends/gamespy/library/configs.py | 8 ++++---- .../gamespy/library/network/tcp_handler.py | 4 ++-- .../protocols/chat/abstractions/contract.py | 8 ++++---- .../gamespy/protocols/chat/applications/client.py | 12 ++++++------ .../protocols/chat/applications/switcher.py | 4 ++-- .../gamespy/protocols/chat/contracts/requests.py | 10 +++++----- .../game_status/abstractions/contracts.py | 4 ++-- .../protocols/game_status/abstractions/handlers.py | 4 ++-- .../protocols/game_status/applications/client.py | 10 +++++----- .../protocols/game_status/applications/switcher.py | 4 ++-- .../protocols/game_status/contracts/requests.py | 4 ++-- .../game_traffic_relay/applications/switcher.py | 4 ++-- .../protocols/natneg/abstractions/contracts.py | 4 ++-- .../protocols/natneg/applications/switcher.py | 4 ++-- .../gamespy/protocols/natneg/contracts/requests.py | 4 ++-- .../abstractions/contracts.py | 2 +- .../applications/switcher.py | 2 +- .../contracts/requests.py | 4 ++-- .../applications/switcher.py | 2 +- .../query_report/v2/applications/switcher.py | 2 +- .../query_report/v2/contracts/requests.py | 4 ++-- .../server_browser/v2/applications/switcher.py | 2 +- .../server_browser/v2/contracts/requests.py | 4 ++-- .../web_services/applications/switcher.py | 2 +- .../modules/sake/contracts/requests.py | 14 +++++++------- src/frontends/tests/gamespy/natneg/redis.py | 4 ++-- 42 files changed, 112 insertions(+), 112 deletions(-) diff --git a/src/backends/library/abstractions/handler_base.py b/src/backends/library/abstractions/handler_base.py index 63fe81160..5f1e6d8d2 100644 --- a/src/backends/library/abstractions/handler_base.py +++ b/src/backends/library/abstractions/handler_base.py @@ -1,4 +1,4 @@ -from typing import Optional, final +from typing import final from backends.library.abstractions.contracts import ( DataResponse, OKResponse, @@ -16,8 +16,8 @@ class HandlerBase: """ _request: RequestBase - _result: Optional[ResultBase] - _response: Optional[Response] + _result: ResultBase | None + _response: Response | None """ the response using to wrap data """ @@ -27,7 +27,7 @@ class HandlerBase: """ @property - def response(self) -> Optional[dict]: + def response(self) -> dict | None: """ the dict response which send to client """ diff --git a/src/backends/protocols/gamespy/game_status/requests.py b/src/backends/protocols/gamespy/game_status/requests.py index 56167c828..1f51aee4d 100644 --- a/src/backends/protocols/gamespy/game_status/requests.py +++ b/src/backends/protocols/gamespy/game_status/requests.py @@ -1,4 +1,4 @@ -from typing import Optional + from pydantic import Field import backends.library.abstractions.contracts as lib @@ -10,7 +10,7 @@ class RequestBase(lib.RequestBase): raw_request: str - local_id: Optional[int] = None + local_id: int | None = None request_dict: dict[str, str] diff --git a/src/backends/protocols/gamespy/game_traffic_relay/data.py b/src/backends/protocols/gamespy/game_traffic_relay/data.py index 6b08c9db5..2c8763b86 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/data.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/data.py @@ -1,10 +1,10 @@ from datetime import datetime -from typing import Optional + from uuid import UUID from backends.library.database.pg_orm import ENGINE, RelayServerCaches from sqlalchemy.orm import Session -def search_relay_server(server_id: UUID, server_ip: str) -> Optional[RelayServerCaches]: +def search_relay_server(server_id: UUID, server_ip: str) -> RelayServerCaches | None: with Session(ENGINE) as session: result = ( session.query(RelayServerCaches) diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py index 2d93e4f65..07660c171 100644 --- a/src/backends/protocols/gamespy/natneg/data.py +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -1,5 +1,5 @@ from datetime import datetime, timedelta -from typing import Optional + from backends.library.database.pg_orm import ( ENGINE, InitPacketCaches, @@ -124,7 +124,7 @@ def get_nat_fail_info_by_ip(public_ip1: str, public_ip2: str) -> list[NatFailCac def get_game_traffic_relay_servers( - number: Optional[int] = None, + number: int | None = None, ) -> list[RelayServerCaches]: with Session(ENGINE) as session: if number is None: diff --git a/src/backends/protocols/gamespy/natneg/requests.py b/src/backends/protocols/gamespy/natneg/requests.py index f05886bff..9fae44fdf 100644 --- a/src/backends/protocols/gamespy/natneg/requests.py +++ b/src/backends/protocols/gamespy/natneg/requests.py @@ -6,7 +6,7 @@ PreInitState, RequestType, ) -from typing import Optional, Union +from typing import Union import backends.library.abstractions.contracts as lib @@ -43,7 +43,7 @@ class ErtAckRequest(CommonRequestBase): class InitRequest(CommonRequestBase): - game_name: Optional[str] = None + game_name: str | None = None private_ip: str private_port: int diff --git a/src/backends/protocols/gamespy/presence_connection_manager/requests.py b/src/backends/protocols/gamespy/presence_connection_manager/requests.py index fc6691c32..216aed8d8 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/requests.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/requests.py @@ -27,7 +27,7 @@ class AddBlockRequest(RequestBase): class BuddyListRequest(RequestBase): profile_id: int namespace_id: int - raw_request: Optional[str] = None + raw_request: str | None = None class BlockListRequest(RequestBase): @@ -110,17 +110,17 @@ class LoginRequest(RequestBase): user_challenge: str response: str user_data: str - unique_nick: Optional[str] = None - namespace_id: Optional[int] = None - auth_token: Optional[str] = None - nick: Optional[str] = None - email: Optional[str] = None + unique_nick: str | None = None + namespace_id: int | None = None + auth_token: str | None = None + nick: str | None = None + email: str | None = None product_id: int type: Union[LoginType, int] sdk_revision_type: list[SdkRevisionType] game_port: int partner_id: int - game_name: Optional[str] = None + game_name: str | None = None quiet_mode_flags: int firewall: bool diff --git a/src/backends/protocols/gamespy/presence_search_player/requests.py b/src/backends/protocols/gamespy/presence_search_player/requests.py index a8c8b0faa..dc9c3bf92 100644 --- a/src/backends/protocols/gamespy/presence_search_player/requests.py +++ b/src/backends/protocols/gamespy/presence_search_player/requests.py @@ -1,4 +1,4 @@ -from typing import Optional + from backends.library.abstractions.contracts import RequestBase as RB from frontends.gamespy.protocols.presence_search_player.aggregates.enums import SearchType @@ -25,10 +25,10 @@ class NewUserRequest(RequestBase): uniquenick: str namespace_id: int product_id: int - game_port: Optional[int] = None - cd_key: Optional[str] = None - partner_id: Optional[int] = None - game_name: Optional[str] = None + game_port: int | None = None + cd_key: str | None = None + partner_id: int | None = None + game_name: str | None = None class NicksRequest(RequestBase): diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index 839d9d42c..75ad7c150 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -105,7 +105,7 @@ def get_peer_group_channel(group_data: list[dict]) -> list[PeerRoomInfo]: return data -def get_server_info_with_instant_key(instant_key: str) -> Optional[GameServerCaches]: +def get_server_info_with_instant_key(instant_key: str) -> GameServerCaches | None: assert isinstance(instant_key, str) with Session(ENGINE) as session: result = ( @@ -116,7 +116,7 @@ def get_server_info_with_instant_key(instant_key: str) -> Optional[GameServerCac return result -def get_server_info_with_game_name(game_name: str) -> Optional[GameServerCaches]: +def get_server_info_with_game_name(game_name: str) -> GameServerCaches | None: assert isinstance(game_name, str) with Session(ENGINE) as session: result = ( diff --git a/src/backends/protocols/gamespy/server_browser/requests.py b/src/backends/protocols/gamespy/server_browser/requests.py index 7b7dab736..b2ba1ad0d 100644 --- a/src/backends/protocols/gamespy/server_browser/requests.py +++ b/src/backends/protocols/gamespy/server_browser/requests.py @@ -1,4 +1,4 @@ -from typing import Optional + import backends.library.abstractions.contracts as lib from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import ( ServerListUpdateOption, @@ -20,9 +20,9 @@ class ServerListUpdateOptionRequestBase(RequestBase): update_option: ServerListUpdateOption keys: list[str] filter: str - max_servers: Optional[int] = None - source_ip: Optional[str] = None - query_options: Optional[int] = None + max_servers: int | None = None + source_ip: str | None = None + query_options: int | None = None class ServerListRequest(ServerListUpdateOptionRequestBase): diff --git a/src/backends/protocols/gamespy/web_services/requests.py b/src/backends/protocols/gamespy/web_services/requests.py index aeac55158..edbbcf038 100644 --- a/src/backends/protocols/gamespy/web_services/requests.py +++ b/src/backends/protocols/gamespy/web_services/requests.py @@ -1,4 +1,4 @@ -from typing import Optional + from pydantic import BaseModel import backends.library.abstractions.contracts as lib @@ -96,7 +96,7 @@ class AuthRequestBase(lib.RequestBase): version: int partner_code: int namespace_id: int - game_id: Optional[int] = None + game_id: int | None = None class LoginProfileRequest(AuthRequestBase): diff --git a/src/backends/tests/gamespy/chat/room_tests.py b/src/backends/tests/gamespy/chat/room_tests.py index 1e2dea08c..dd5756427 100644 --- a/src/backends/tests/gamespy/chat/room_tests.py +++ b/src/backends/tests/gamespy/chat/room_tests.py @@ -1,4 +1,4 @@ -# from typing import Optional +# # import unittest # from frontends.gamespy.library.tests.mock_objects import BrokerMock diff --git a/src/frontends/gamespy/library/abstractions/contracts.py b/src/frontends/gamespy/library/abstractions/contracts.py index fddcabe5b..064a0e2cf 100644 --- a/src/frontends/gamespy/library/abstractions/contracts.py +++ b/src/frontends/gamespy/library/abstractions/contracts.py @@ -1,5 +1,5 @@ import abc -from typing import Optional + from pydantic import BaseModel @@ -49,7 +49,7 @@ class ResponseBase: _result: ResultBase _request: RequestBase - def __init__(self, request: RequestBase, result: Optional[ResultBase]) -> None: + def __init__(self, request: RequestBase, result: ResultBase | None) -> None: super().__init__() if request is not None: assert issubclass(type(request), RequestBase) diff --git a/src/frontends/gamespy/library/abstractions/handler.py b/src/frontends/gamespy/library/abstractions/handler.py index 350dba00b..a1db501ce 100644 --- a/src/frontends/gamespy/library/abstractions/handler.py +++ b/src/frontends/gamespy/library/abstractions/handler.py @@ -1,7 +1,7 @@ import json from frontends.gamespy.library.abstractions.client import ClientBase from frontends.gamespy.library.exceptions.general import UniSpyException -from typing import Optional, Type +from typing import Type import requests from frontends.gamespy.library.configs import CONFIG @@ -19,12 +19,12 @@ class CmdHandlerBase: _client: "ClientBase" _request: "RequestBase" - _result: Optional["ResultBase"] - _response: Optional["ResponseBase"] + _result: "ResultBase | None" + _response: "ResponseBase | None" """ the response instance, initialize as None in __init__ """ - _result_cls: Optional["Type[ResultBase]"] + _result_cls: "Type[ResultBase] | None" """ the result type class, use to deserialize json data from backend\n the initialization of _result_cls must after call super().__init__() @@ -41,9 +41,9 @@ def __init__(self, client: "ClientBase", request: "RequestBase") -> None: assert issubclass(type(request), RequestBase) self._client = client self._request = request - self._response = None self._result_cls = None self._result = None + self._response = None self._is_uploading = True self._is_fetching = True diff --git a/src/frontends/gamespy/library/abstractions/redis_objects.py b/src/frontends/gamespy/library/abstractions/redis_objects.py index a6d26ba84..93481107a 100644 --- a/src/frontends/gamespy/library/abstractions/redis_objects.py +++ b/src/frontends/gamespy/library/abstractions/redis_objects.py @@ -2,7 +2,7 @@ # from datetime import datetime # # import threading # import asyncio -# from typing import Optional +# # from redis import Redis diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index cd825f0b0..730991069 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -1,5 +1,5 @@ from types import MappingProxyType -from typing import Optional + from frontends.gamespy.library.abstractions.connections import NetworkServerBase from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.library.extentions.schedular import Schedular @@ -28,9 +28,9 @@ class ServerLauncherBase: - config: Optional[ServerConfig] - logger: Optional[LogWriter] - server: Optional[NetworkServerBase] + config: ServerConfig | None + logger: LogWriter | None + server: NetworkServerBase | None schedular: Schedular def __init__(self): diff --git a/src/frontends/gamespy/library/abstractions/switcher.py b/src/frontends/gamespy/library/abstractions/switcher.py index 9b4e8526d..e7bb38cb3 100644 --- a/src/frontends/gamespy/library/abstractions/switcher.py +++ b/src/frontends/gamespy/library/abstractions/switcher.py @@ -1,7 +1,7 @@ from abc import abstractmethod from frontends.gamespy.library.abstractions.client import ClientBase from frontends.gamespy.library.abstractions.handler import CmdHandlerBase -from typing import Optional + class SwitcherBase: @@ -56,5 +56,5 @@ def _process_raw_request(self) -> None: pass @abstractmethod - def _create_cmd_handlers(self, name: object, raw_request: object) -> Optional[CmdHandlerBase]: + def _create_cmd_handlers(self, name: object, raw_request: object) -> CmdHandlerBase | None: pass diff --git a/src/frontends/gamespy/library/configs.py b/src/frontends/gamespy/library/configs.py index c79ba0539..a5f4106a1 100644 --- a/src/frontends/gamespy/library/configs.py +++ b/src/frontends/gamespy/library/configs.py @@ -13,9 +13,9 @@ class PostgreSql(BaseModel): password: str ssl_mode: str # You might want to restrict this to specific values trust_server_cert: bool - ssl_key: Optional[str] = None # Optional field for SSL key - ssl_password: Optional[str] = None # Optional field for SSL password - root_cert: Optional[str] = None # Optional field for root certificate + ssl_key: str | None = None # Optional field for SSL key + ssl_password: str | None = None # Optional field for SSL password + root_cert: str | None = None # Optional field for root certificate @property def url(self) -> str: @@ -28,7 +28,7 @@ class RedisConfig(BaseModel): user: str password: str ssl: bool # Use bool for SSL flag - ssl_host: Optional[str] = None # Optional field for SSL host + ssl_host: str | None = None # Optional field for SSL host @property def url(self) -> str: diff --git a/src/frontends/gamespy/library/network/tcp_handler.py b/src/frontends/gamespy/library/network/tcp_handler.py index 928cf204d..a61fdc063 100644 --- a/src/frontends/gamespy/library/network/tcp_handler.py +++ b/src/frontends/gamespy/library/network/tcp_handler.py @@ -1,6 +1,6 @@ import socket import socketserver -from typing import Optional + from frontends.gamespy.library.abstractions.client import ClientBase from frontends.gamespy.library.abstractions.connections import ( ConnectionBase, @@ -30,7 +30,7 @@ def disconnect(self) -> None: class TcpHandler(socketserver.BaseRequestHandler): request: socket.socket - conn: Optional[TcpConnection] = None + conn: TcpConnection | None = None def handle(self) -> None: if self.conn is None: diff --git a/src/frontends/gamespy/protocols/chat/abstractions/contract.py b/src/frontends/gamespy/protocols/chat/abstractions/contract.py index 0d34c8414..bfd1d49f3 100644 --- a/src/frontends/gamespy/protocols/chat/abstractions/contract.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/contract.py @@ -1,4 +1,4 @@ -from typing import Optional + from uuid import UUID from pydantic import BaseModel @@ -8,9 +8,9 @@ class RequestBase(lib.RequestBase): raw_request: str command_name: str - _prefix: Optional[str] + _prefix: str | None _cmd_params: list[str] - _long_param: Optional[str] + _long_param: str | None def __init__(self, raw_request: str) -> None: assert isinstance(raw_request, str) @@ -71,7 +71,7 @@ class ResponseBase(lib.ResponseBase): _result: ResultBase _request: RequestBase - def __init__(self, request: RequestBase, result: Optional[ResultBase]) -> None: + def __init__(self, request: RequestBase, result: ResultBase | None) -> None: super().__init__(request, result) if result is not None: assert issubclass(type(result), ResultBase) diff --git a/src/frontends/gamespy/protocols/chat/applications/client.py b/src/frontends/gamespy/protocols/chat/applications/client.py index 2aef8142d..39037ecbb 100644 --- a/src/frontends/gamespy/protocols/chat/applications/client.py +++ b/src/frontends/gamespy/protocols/chat/applications/client.py @@ -7,17 +7,17 @@ from frontends.gamespy.library.network.tcp_handler import TcpConnection from frontends.gamespy.library.configs import CONFIG, ServerConfig -from typing import Optional + from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage class ClientInfo: - previously_joined_channel: Optional[str] + previously_joined_channel: str | None joined_channels: list[str] - nick_name: Optional[str] - gamename: Optional[str] - user_name: Optional[str] + nick_name: str | None + gamename: str | None + user_name: str | None def __init__(self) -> None: self.joined_channels = [] @@ -29,7 +29,7 @@ def __init__(self) -> None: class Client(ClientBase): info: ClientInfo - brocker: Optional[WebSocketBrocker] + brocker: WebSocketBrocker | None def __init__( self, connection: TcpConnection, server_config: ServerConfig, logger: LogWriter diff --git a/src/frontends/gamespy/protocols/chat/applications/switcher.py b/src/frontends/gamespy/protocols/chat/applications/switcher.py index 743c98b60..2aa676a3d 100644 --- a/src/frontends/gamespy/protocols/chat/applications/switcher.py +++ b/src/frontends/gamespy/protocols/chat/applications/switcher.py @@ -1,4 +1,4 @@ -from typing import Optional + from frontends.gamespy.library.abstractions.client import ClientBase from frontends.gamespy.library.abstractions.handler import CmdHandlerBase from frontends.gamespy.library.abstractions.switcher import SwitcherBase @@ -82,7 +82,7 @@ def _process_raw_request(self) -> None: continue self._requests.append((RequestType(name), raw_request)) - def _create_cmd_handlers(self, name: RequestType, raw_request: str) -> Optional[CmdHandlerBase]: + def _create_cmd_handlers(self, name: RequestType, raw_request: str) -> CmdHandlerBase | None: assert isinstance(name, RequestType) r_type = RequestType(name) match r_type: diff --git a/src/frontends/gamespy/protocols/chat/contracts/requests.py b/src/frontends/gamespy/protocols/chat/contracts/requests.py index 9892e7087..180180023 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/requests.py +++ b/src/frontends/gamespy/protocols/chat/contracts/requests.py @@ -8,7 +8,7 @@ ModeRequestType, TopicRequestType, ) -from typing import Optional + import re from frontends.gamespy.library.extentions.string_extentions import ( convert_keystr_to_list, @@ -321,7 +321,7 @@ def parse(self): class GetCKeyRequest(ChannelRequestBase): - nick_name: Optional[str] + nick_name: str | None cookie: str keys: list request_type: GetKeyRequestType @@ -352,7 +352,7 @@ class GetUdpRelayRequest(ChannelRequestBase): class JoinRequest(ChannelRequestBase): - password: Optional[str] + password: str | None def __init__(self, raw_request): super().__init__(raw_request) @@ -556,7 +556,7 @@ def parse(self): class NamesRequest(ChannelRequestBase): - def __init__(self, raw_request: Optional[str] = None) -> None: + def __init__(self, raw_request: str | None = None) -> None: if raw_request is not None: super().__init__(raw_request) @@ -564,7 +564,7 @@ def __init__(self, raw_request: Optional[str] = None) -> None: class PartRequest(ChannelRequestBase): reason: str = "Unknown reason" - def __init__(self, raw_request: Optional[str] = None) -> None: + def __init__(self, raw_request: str | None = None) -> None: if raw_request is not None: super().__init__(raw_request) diff --git a/src/frontends/gamespy/protocols/game_status/abstractions/contracts.py b/src/frontends/gamespy/protocols/game_status/abstractions/contracts.py index ffbb1903b..d13dfa1e0 100644 --- a/src/frontends/gamespy/protocols/game_status/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/game_status/abstractions/contracts.py @@ -1,4 +1,4 @@ -from typing import Optional + import frontends.gamespy.library.abstractions.contracts as lib from frontends.gamespy.library.extentions.gamespy_utils import convert_to_key_value from frontends.gamespy.protocols.game_status.aggregations.exceptions import GSException @@ -7,7 +7,7 @@ class RequestBase(lib.RequestBase): command_name: str raw_request: str - local_id: Optional[int] + local_id: int | None request_dict: dict[str, str] @staticmethod diff --git a/src/frontends/gamespy/protocols/game_status/abstractions/handlers.py b/src/frontends/gamespy/protocols/game_status/abstractions/handlers.py index 938f0b3ea..5edbf9e80 100644 --- a/src/frontends/gamespy/protocols/game_status/abstractions/handlers.py +++ b/src/frontends/gamespy/protocols/game_status/abstractions/handlers.py @@ -1,4 +1,4 @@ -from typing import Optional + from frontends.gamespy.library.abstractions.contracts import ResponseBase import frontends.gamespy.library.abstractions.handler as lib from frontends.gamespy.protocols.game_status.abstractions.contracts import RequestBase, ResultBase @@ -9,7 +9,7 @@ class CmdHandlerBase(lib.CmdHandlerBase): _client: Client _request: RequestBase _result: ResultBase - _response: Optional[ResponseBase] + _response: ResponseBase | None def __init__(self, client: Client, request: RequestBase) -> None: super().__init__(client, request) diff --git a/src/frontends/gamespy/protocols/game_status/applications/client.py b/src/frontends/gamespy/protocols/game_status/applications/client.py index f24d01a0b..fa61299ce 100644 --- a/src/frontends/gamespy/protocols/game_status/applications/client.py +++ b/src/frontends/gamespy/protocols/game_status/applications/client.py @@ -1,4 +1,4 @@ -from typing import Optional + from frontends.gamespy.library.abstractions.client import ClientBase, ClientInfoBase from frontends.gamespy.library.abstractions.switcher import SwitcherBase from frontends.gamespy.library.log.log_manager import LogWriter @@ -11,13 +11,13 @@ class ClientInfo(ClientInfoBase): - session_key: Optional[str] - game_name: Optional[str] + session_key: str | None + game_name: str | None is_user_authenticated: bool is_player_authenticated: bool is_game_authenticated: bool - profile_id: Optional[int] - game_session_key: Optional[str] + profile_id: int | None + game_session_key: str | None def __init__(self) -> None: super().__init__() diff --git a/src/frontends/gamespy/protocols/game_status/applications/switcher.py b/src/frontends/gamespy/protocols/game_status/applications/switcher.py index 0aa7090f4..ed1606eb1 100644 --- a/src/frontends/gamespy/protocols/game_status/applications/switcher.py +++ b/src/frontends/gamespy/protocols/game_status/applications/switcher.py @@ -1,4 +1,4 @@ -from typing import Optional, cast +from typing import cast from frontends.gamespy.library.abstractions.switcher import SwitcherBase from frontends.gamespy.protocols.game_status.abstractions.handlers import CmdHandlerBase from frontends.gamespy.protocols.game_status.aggregations.enums import RequestType @@ -29,7 +29,7 @@ def _process_raw_request(self) -> None: return self._requests.append((RequestType(name), raw_request)) - def _create_cmd_handlers(self, name: RequestType, raw_request: str) -> Optional[CmdHandlerBase]: + def _create_cmd_handlers(self, name: RequestType, raw_request: str) -> CmdHandlerBase | None: assert isinstance(name, RequestType) self._client = cast(Client, self._client) match name: diff --git a/src/frontends/gamespy/protocols/game_status/contracts/requests.py b/src/frontends/gamespy/protocols/game_status/contracts/requests.py index c38c28c50..e4c3737a0 100644 --- a/src/frontends/gamespy/protocols/game_status/contracts/requests.py +++ b/src/frontends/gamespy/protocols/game_status/contracts/requests.py @@ -1,4 +1,4 @@ -from typing import Optional, final +from typing import final from frontends.gamespy.library.extentions.gamespy_utils import convert_to_key_value from frontends.gamespy.protocols.game_status.abstractions.contracts import RequestBase from frontends.gamespy.protocols.game_status.aggregations.enums import ( @@ -215,7 +215,7 @@ def parse(self) -> None: @final class UpdateGameRequest(RequestBase): - connection_id: Optional[int] + connection_id: int | None is_done: bool is_client_local_storage_available: bool game_data: str diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/switcher.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/switcher.py index 5adf68cac..bf0201c80 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/switcher.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/switcher.py @@ -1,4 +1,4 @@ -from typing import Optional + from frontends.gamespy.library.abstractions.switcher import SwitcherBase from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client from frontends.gamespy.protocols.game_traffic_relay.applications.handlers import ( @@ -25,7 +25,7 @@ def _process_raw_request(self) -> None: def _create_cmd_handlers( self, name: RequestType, raw_request: bytes - ) -> Optional[PingHandler | None]: + ) -> PingHandler | None: assert isinstance(name, RequestType) assert isinstance(raw_request, bytes) match name: diff --git a/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py b/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py index eaebf2425..7d3dc3a4f 100644 --- a/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py @@ -1,4 +1,4 @@ -from typing import Optional + import frontends.gamespy.library.abstractions.contracts as lib from frontends.gamespy.protocols.natneg.aggregations.enums import ( NatClientIndex, @@ -23,7 +23,7 @@ class RequestBase(lib.RequestBase): command_name: RequestType raw_request: bytes - def __init__(self, raw_request: Optional[bytes] = None): + def __init__(self, raw_request: bytes | None = None): assert isinstance(raw_request, bytes) self.raw_request = raw_request diff --git a/src/frontends/gamespy/protocols/natneg/applications/switcher.py b/src/frontends/gamespy/protocols/natneg/applications/switcher.py index 5fe01791f..191f6dc65 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/switcher.py +++ b/src/frontends/gamespy/protocols/natneg/applications/switcher.py @@ -1,4 +1,4 @@ -from typing import Optional + from frontends.gamespy.library.abstractions.switcher import SwitcherBase from frontends.gamespy.protocols.natneg.abstractions.handlers import CmdHandlerBase from frontends.gamespy.protocols.natneg.applications.client import Client @@ -39,7 +39,7 @@ def _process_raw_request(self) -> None: def _create_cmd_handlers( self, name: RequestType, raw_request: bytes - ) -> Optional[CmdHandlerBase | None]: + ) -> CmdHandlerBase | None: assert isinstance(name, RequestType) assert isinstance(raw_request, bytes) match name: diff --git a/src/frontends/gamespy/protocols/natneg/contracts/requests.py b/src/frontends/gamespy/protocols/natneg/contracts/requests.py index 741ae179f..3afc2898d 100644 --- a/src/frontends/gamespy/protocols/natneg/contracts/requests.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/requests.py @@ -1,7 +1,7 @@ from socket import inet_ntoa import socket import struct -from typing import Optional + # from frontends.gamespy.library.extentions.string_extentions import IPEndPoint from frontends.gamespy.protocols.natneg.abstractions.contracts import ( @@ -55,7 +55,7 @@ class ErtAckRequest(CommonRequestBase): class InitRequest(CommonRequestBase): - game_name: Optional[str] + game_name: str | None private_ip: str private_port: int diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py b/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py index eac5d3c21..4e76ca6a2 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py @@ -47,7 +47,7 @@ class ResponseBase(lib.ResponseBase): _result: ResultBase sending_buffer: str - def __init__(self, request: RequestBase, result: Optional[ResultBase]) -> None: + def __init__(self, request: RequestBase, result: ResultBase | None) -> None: assert issubclass(type(request), RequestBase) assert issubclass(type(result), ResultBase) super().__init__(request, result) diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/applications/switcher.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/switcher.py index 47db40a37..447e4cb6e 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/applications/switcher.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/switcher.py @@ -31,7 +31,7 @@ def _process_raw_request(self) -> None: continue self._requests.append((RequestType(name), raw_request)) - def _create_cmd_handlers(self, name: RequestType, raw_request: str) -> Optional[CmdHandlerBase]: + def _create_cmd_handlers(self, name: RequestType, raw_request: str) -> CmdHandlerBase | None: assert isinstance(name, RequestType) assert isinstance(raw_request, str) if TYPE_CHECKING: diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py index 4166cb70e..a3fd6d61d 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py @@ -1,5 +1,5 @@ from frontends.gamespy.library.exceptions.general import UniSpyException -from typing import Optional, final +from typing import final from frontends.gamespy.library.extentions.gamespy_utils import convert_to_key_value from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import ( RequestBase, @@ -392,7 +392,7 @@ class StatusInfoRequest(RequestBase): game_map_name: str quiet_mode_flags: str - def __init__(self, raw_request: Optional[str] = None) -> None: + def __init__(self, raw_request: str | None = None) -> None: if raw_request is not None: self.raw_request = raw_request diff --git a/src/frontends/gamespy/protocols/presence_search_player/applications/switcher.py b/src/frontends/gamespy/protocols/presence_search_player/applications/switcher.py index 9b95dffc6..9ea3cab57 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/applications/switcher.py +++ b/src/frontends/gamespy/protocols/presence_search_player/applications/switcher.py @@ -32,7 +32,7 @@ def _process_raw_request(self): continue self._requests.append((RequestType(name), raw_request)) - def _create_cmd_handlers(self, name: RequestType, raw_request: str) -> Optional[CmdHandlerBase]: + def _create_cmd_handlers(self, name: RequestType, raw_request: str) -> CmdHandlerBase | None: assert isinstance(name, RequestType) if TYPE_CHECKING: self._client = cast(Client, self._client) diff --git a/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py b/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py index fba830d15..6e743fa75 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py +++ b/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py @@ -37,7 +37,7 @@ def _process_raw_request(self) -> None: raw_request = self._raw_request self._requests.append((RequestType(name), raw_request)) - def _create_cmd_handlers(self, name: RequestType, raw_request: bytes) -> Optional[CmdHandlerBase]: + def _create_cmd_handlers(self, name: RequestType, raw_request: bytes) -> CmdHandlerBase | None: assert isinstance(name, RequestType) if TYPE_CHECKING: self._client = cast(Client, self._client) diff --git a/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py b/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py index 87c349ef1..d1d6f3e0b 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py +++ b/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py @@ -1,4 +1,4 @@ -from typing import Optional + from uuid import UUID from frontends.gamespy.library.extentions.encoding import get_string from frontends.gamespy.protocols.query_report.aggregates.exceptions import QRException @@ -45,7 +45,7 @@ def cookie(self) -> int: def target_ip_endpoint(self) -> str: return f"{self.target_ip_address}:{self.target_port}" - def __init__(self, raw_request: Optional[bytes] = None) -> None: + def __init__(self, raw_request: bytes | None = None) -> None: if raw_request is not None: super().__init__(raw_request) diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py index fa7a47565..d0f5546ff 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py @@ -41,7 +41,7 @@ def _process_raw_request(self) -> None: def _create_cmd_handlers( self, name: int, raw_request: bytes - ) -> Optional[CmdHandlerBase]: + ) -> CmdHandlerBase | None: req = raw_request if TYPE_CHECKING: self._client = cast(Client, self._client) diff --git a/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py index 6e0c88d0b..a0cac91e1 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py @@ -1,5 +1,5 @@ from socket import inet_ntoa -from typing import Optional + from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import ( AdHocRequestBase, @@ -58,7 +58,7 @@ class SendMessageRequest(AdHocRequestBase): prefix_message: bytes client_message: bytes - def __init__(self, raw_request: Optional[bytes] = None) -> None: + def __init__(self, raw_request: bytes | None = None) -> None: if raw_request is not None: super().__init__(raw_request) diff --git a/src/frontends/gamespy/protocols/web_services/applications/switcher.py b/src/frontends/gamespy/protocols/web_services/applications/switcher.py index acc1fce11..b86250cd0 100644 --- a/src/frontends/gamespy/protocols/web_services/applications/switcher.py +++ b/src/frontends/gamespy/protocols/web_services/applications/switcher.py @@ -35,7 +35,7 @@ def _process_raw_request(self) -> None: raise WebException("request name invalid") self._requests.append((name, self._raw_request)) - def _create_cmd_handlers(self, name: str, raw_request: str) -> Optional[CmdHandlerBase]: + def _create_cmd_handlers(self, name: str, raw_request: str) -> CmdHandlerBase | None: if TYPE_CHECKING: self._client = cast(Client, self._client) diff --git a/src/frontends/gamespy/protocols/web_services/modules/sake/contracts/requests.py b/src/frontends/gamespy/protocols/web_services/modules/sake/contracts/requests.py index 4fc16904b..b5dfe0962 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/sake/contracts/requests.py +++ b/src/frontends/gamespy/protocols/web_services/modules/sake/contracts/requests.py @@ -1,4 +1,4 @@ -from typing import Optional + import xml.etree.ElementTree as ET from frontends.gamespy.protocols.web_services.aggregations.exceptions import WebException @@ -54,7 +54,7 @@ def parse(self) -> None: class GetMyRecordsRequest(RequestBase): - fields: list[tuple[Optional[str], str]] + fields: list[tuple[str | None, str]] def __init__(self, raw_request: str) -> None: super().__init__(raw_request) @@ -74,7 +74,7 @@ def parse(self) -> None: class GetRandomRecordsRequest(RequestBase): max: int - fields: list[tuple[Optional[str], str]] = [] + fields: list[tuple[str | None, str]] = [] def parse(self) -> None: super().parse() @@ -159,14 +159,14 @@ def parse(self) -> None: class SearchForRecordsRequest(RequestBase): - filter: Optional[str] - sort: Optional[str] + filter: str | None + sort: str | None offset: str max: str surrounding: str - owner_ids: Optional[str] + owner_ids: str | None cache_flag: str - fields: list[tuple[Optional[str], str]] + fields: list[tuple[str | None, str]] """ [ (field_name,field_type), diff --git a/src/frontends/tests/gamespy/natneg/redis.py b/src/frontends/tests/gamespy/natneg/redis.py index a2f002b61..667890cf9 100644 --- a/src/frontends/tests/gamespy/natneg/redis.py +++ b/src/frontends/tests/gamespy/natneg/redis.py @@ -1,5 +1,5 @@ # import datetime -# from typing import Optional +# # from pydantic.v1 import EmailStr @@ -12,7 +12,7 @@ # email: EmailStr # join_date: datetime.date # age: int = Field(index=True) -# bio: Optional[str] +# bio: str | None # # Now, if we use this model with a Redis deployment that has the From 582e2670357c1a174f0c6a5d55e8e84cfb1d4eb2 Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Tue, 29 Jul 2025 10:15:08 +0000 Subject: [PATCH 196/231] feat: fix database session life time --- common/UniSpy_pg.sql | 1 + src/.vscode/launch.json | 4 +- .../library/abstractions/handler_base.py | 13 +- src/backends/library/database/pg_orm.py | 19 +- .../protocols/gamespy/chat/abstractions.py | 7 +- src/backends/protocols/gamespy/chat/data.py | 498 ++++++++------- .../protocols/gamespy/chat/handlers.py | 154 +++-- src/backends/protocols/gamespy/chat/helper.py | 22 +- .../protocols/gamespy/chat/requests.py | 16 +- .../protocols/gamespy/game_status/requests.py | 4 +- .../gamespy/game_traffic_relay/data.py | 65 +- .../gamespy/game_traffic_relay/handlers.py | 6 +- src/backends/protocols/gamespy/natneg/data.py | 177 +++--- .../protocols/gamespy/natneg/handlers.py | 19 +- .../presence_connection_manager/data.py | 583 +++++++++--------- .../presence_connection_manager/handlers.py | 52 +- .../gamespy/presence_search_player/data.py | 410 ++++++------ .../presence_search_player/handlers.py | 56 +- .../protocols/gamespy/query_report/data.py | 101 ++- .../gamespy/query_report/handlers.py | 8 +- .../gamespy/server_browser/handlers.py | 18 +- .../protocols/gamespy/web_services/data.py | 149 ++--- .../gamespy/web_services/handlers.py | 11 +- src/backends/routers/gamespy/chat.py | 25 +- .../data_fetch_tests.py | 130 ++-- .../precence_search_player/handler_tests.py | 14 +- src/frontends/gamespy/library/configs.py | 5 +- .../gamespy/library/network/tcp_handler.py | 7 +- .../protocols/chat/aggregates/enums.py | 39 ++ .../protocols/chat/aggregates/peer_room.py | 2 +- .../chat/aggregates/response_name.py | 108 ++-- .../protocols/chat/applications/client.py | 3 +- .../protocols/chat/applications/handlers.py | 23 +- .../chat/applications/server_launcher.py | 2 +- .../protocols/chat/contracts/requests.py | 10 +- .../protocols/chat/contracts/responses.py | 111 ++-- .../protocols/chat/contracts/results.py | 1 + .../game_status/contracts/requests.py | 1 + 38 files changed, 1544 insertions(+), 1330 deletions(-) diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index a04d4bc43..95e0dc835 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -132,6 +132,7 @@ CREATE TABLE unispy.chat_user_caches ( CREATE TABLE unispy.chat_channel_caches ( channel_name character varying NOT NULL PRIMARY KEY, server_id uuid NOT NULL, + creator character varying, game_name character varying NOT NULL, room_name character varying NOT NULL, topic character varying, diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json index ed6303f5c..874ca4fce 100644 --- a/src/.vscode/launch.json +++ b/src/.vscode/launch.json @@ -26,7 +26,9 @@ "127.0.0.1", "--port", "8080", - "--reload" // Optional: enables auto-reload on code changes + "--reload", // Optional: enables auto-reload on code changes + "--reload-dir", + "${workspaceFolder}/backends" ], "console": "integratedTerminal", "env": { diff --git a/src/backends/library/abstractions/handler_base.py b/src/backends/library/abstractions/handler_base.py index 5f1e6d8d2..c468c3162 100644 --- a/src/backends/library/abstractions/handler_base.py +++ b/src/backends/library/abstractions/handler_base.py @@ -5,9 +5,11 @@ RequestBase, Response, ) +from backends.library.database.pg_orm import ENGINE from frontends.gamespy.library.abstractions.contracts import ResultBase import logging +from sqlalchemy.orm import Session class HandlerBase: @@ -44,10 +46,13 @@ def __init__(self, request: RequestBase) -> None: self._response = None def handle(self) -> None: - self._request_check() - self._data_operate() - self._result_construct() - self._response_construct() + with Session(ENGINE) as session: + self._session = session + self._request_check() + self._data_operate() + self._session.commit() + self._result_construct() + self._response_construct() def _request_check(self) -> None: """virtual method""" diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 4f16bcd8c..741e6812f 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -139,7 +139,7 @@ class Games(Base): gameid = Column(Integer, primary_key=True) gamename = Column(String, nullable=False) - secretkey = Column(String) + secretkey = Column(String, nullable=False) description = Column(String(4095), nullable=False) disabled = Column(Boolean, nullable=False) @@ -181,6 +181,16 @@ class PStorage(Base): data = Column(JSONB) +class GameStatusSnapShot(Base): + __tablename__ = "game_status_snapshot" + id = Column(Integer, primary_key=True, autoincrement=True) + connection_id = Column(Integer) + session_key = Column(String) + game_name = Column(String) + game_data = Column(JSONB) + update_time = Column(DateTime, nullable=False) + + class SakeStorage(Base): __tablename__ = "sakestorage" @@ -234,7 +244,9 @@ class ChatChannelCaches(Base): __tablename__ = "chat_channel_caches" server_id = Column(UUID, nullable=False) channel_name = Column(String, primary_key=True, nullable=False) + creator = Column(String, nullable=False) game_name = Column(String, nullable=False) + creator = Column(String, nullable=True) room_name = Column(String, nullable=False) topic = Column(String, nullable=True) password = Column(String, nullable=True) @@ -245,6 +257,7 @@ class ChatChannelCaches(Base): update_time = Column(DateTime, nullable=False) modes = Column(JSONB, default=[]) banned_nicks = Column(JSONB, default=[]) + class ChatUserCaches(Base): @@ -254,12 +267,12 @@ class ChatUserCaches(Base): __tablename__ = "chat_user_caches" server_id = Column(UUID, nullable=False) - nick_name = Column(String, primary_key=True, nullable=False) + nick_name = Column(String, primary_key=True, nullable=True) user_name = Column(String, nullable=True) game_name = Column(String, nullable=True) remote_ip_address = Column(INET, nullable=False) remote_port = Column(Integer, nullable=False) - key_value = Column(JSONB) + key_value = Column(JSONB, default={}) update_time = Column(DateTime, nullable=False) diff --git a/src/backends/protocols/gamespy/chat/abstractions.py b/src/backends/protocols/gamespy/chat/abstractions.py index 659839462..b7848d3af 100644 --- a/src/backends/protocols/gamespy/chat/abstractions.py +++ b/src/backends/protocols/gamespy/chat/abstractions.py @@ -30,17 +30,20 @@ def __init__(self, request: RequestBase) -> None: def _get_user(self): self._user = data.get_user_cache_by_ip_port( - self._request.client_ip, self._request.client_port + self._request.client_ip, self._request.client_port, self._session ) def _get_channel(self): - self._channel = data.get_channel_by_name(self._request.channel_name) + self._channel = data.get_channel_by_name( + self._request.channel_name, self._session + ) def _get_channel_user(self): self._channel_user = data.get_channel_user_cache_by_name_and_ip_port( self._request.channel_name, self._request.client_ip, self._request.client_port, + self._session, ) def _check_user(self): diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index 1223d323a..32feb17d7 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -7,6 +7,7 @@ ChatChannelCaches, ChatUserCaches, ChatChannelUserCaches, + Games, Users, Profiles, SubProfiles, @@ -18,24 +19,35 @@ from sqlalchemy.orm import Session -def is_nick_exist(nick_name: str) -> bool: - with Session(ENGINE) as session: +def is_nick_exist(nick_name: str, session: Session) -> bool: + c = session.query(ChatUserCaches.nick_name).count() + if c == 1: + return True + else: + return False + + +def get_secret_key_by_game_name(game_name: str, session: Session) -> str | None: + result = session.query(Games).where(Games.gamename == game_name).first() + if result is None: + return None + else: + assert isinstance(result.secretkey, str) + return result.secretkey - c = session.query(ChatUserCaches.nick_name).count() - if c == 1: - return True - else: - return False +def add_user_cache(cache: ChatUserCaches, session: Session) -> Session: + session.add(cache) + session.commit() + return session -def add_nick_cache(cache: ChatUserCaches): - with Session(ENGINE) as session: - session.add(cache) - session.commit() + +def update_user_cache(cache: ChatUserCaches, session: Session): + session.commit() def nick_and_email_login( - nick_name: str, email: str, password_hash: str + nick_name: str, email: str, password_hash: str, session: Session ) -> tuple[int, int, bool, bool]: """ return @@ -44,20 +56,19 @@ def nick_and_email_login( assert isinstance(nick_name, str) assert isinstance(email, str) assert isinstance(password_hash, str) - with Session(ENGINE) as session: - - result = ( - session.query( - Users.userid, Profiles.profileid, Users.emailverified, Users.banned - ) - .join(Profiles, (Users.userid == Profiles.userid)) - .where( - Users.email == email, - Profiles.nick == nick_name, - Users.password == password_hash, - ) - .first() + + result = ( + session.query( + Users.userid, Profiles.profileid, Users.emailverified, Users.banned + ) + .join(Profiles, (Users.userid == Profiles.userid)) + .where( + Users.email == email, + Profiles.nick == nick_name, + Users.password == password_hash, ) + .first() + ) if TYPE_CHECKING: result = cast(tuple[int, int, bool, bool], result) if result is None: @@ -68,26 +79,28 @@ def nick_and_email_login( return result -def uniquenick_login(uniquenick: str, namespace_id: int) -> tuple[int, int, bool, bool]: +def uniquenick_login( + uniquenick: str, namespace_id: int, session: Session +) -> tuple[int, int, bool, bool]: """ return userid, profileid, emailverified, banned """ assert isinstance(uniquenick, str) assert isinstance(namespace_id, int) - with Session(ENGINE) as session: - result = ( - session.query( - Users.userid, Profiles.profileid, Users.emailverified, Users.banned - ) - .join(Profiles, (Users.userid == Profiles.userid)) - .join(Profiles, (Profiles.profileid == SubProfiles.profileid)) - .where( - SubProfiles.namespaceid == namespace_id, - SubProfiles.uniquenick == uniquenick, - ) - .first() + + result = ( + session.query( + Users.userid, Profiles.profileid, Users.emailverified, Users.banned + ) + .join(Profiles, (Users.userid == Profiles.userid)) + .join(Profiles, (Profiles.profileid == SubProfiles.profileid)) + .where( + SubProfiles.namespaceid == namespace_id, + SubProfiles.uniquenick == uniquenick, ) + .first() + ) if result is None: # fmt: off raise ChatException(f"Can not find user with uniquenick:{uniquenick} in database.") @@ -100,10 +113,10 @@ def uniquenick_login(uniquenick: str, namespace_id: int) -> tuple[int, int, bool # region User -def is_cdkey_valid(cdkey: str) -> bool: +def is_cdkey_valid(cdkey: str, session: Session) -> bool: if TYPE_CHECKING: assert isinstance(SubProfiles.cdkeyenc, Column) - with Session(ENGINE) as session: + result = session.query(SubProfiles).where(SubProfiles.cdkeyenc == cdkey).count() if result == 0: return False @@ -115,170 +128,171 @@ def is_cdkey_valid(cdkey: str) -> bool: # region Channel -def is_channel_exist(channel_name: str, game_name: str) -> bool: - with Session(ENGINE) as session: - channel_count = ( - session.query(ChatChannelCaches) - .where( - ChatChannelCaches.channel_name == channel_name, - ChatChannelCaches.game_name == game_name, - ChatChannelCaches.update_time >= datetime.now() - timedelta(minutes=10), - ) - .count() +def is_channel_exist(channel_name: str, game_name: str, session: Session) -> bool: + channel_count = ( + session.query(ChatChannelCaches) + .where( + ChatChannelCaches.channel_name == channel_name, + ChatChannelCaches.game_name == game_name, + ChatChannelCaches.update_time >= datetime.now() - timedelta(minutes=10), ) + .count() + ) if channel_count == 1: return True else: return False -def add_channel(channel: ChatChannelCaches): - with Session(ENGINE) as session: - session.add(channel) - session.commit() +def add_channel(channel: ChatChannelCaches, session: Session): + session.add(channel) + session.commit() def get_channel_by_name_and_game( - channel_name: str, game_name: str + channel_name: str, game_name: str, session: Session ) -> ChatChannelCaches | None: - with Session(ENGINE) as session: - channel = ( - session.query(ChatChannelCaches) - .where( - ChatChannelCaches.channel_name == channel_name, - ChatChannelCaches.game_name == game_name, - ) - .first() + channel = ( + session.query(ChatChannelCaches) + .where( + ChatChannelCaches.channel_name == channel_name, + ChatChannelCaches.game_name == game_name, ) + .first() + ) return channel -def get_channel_by_name(channel_name: str) -> ChatChannelCaches | None: - with Session(ENGINE) as session: - channel = ( - session.query(ChatChannelCaches) - .where(ChatChannelCaches.channel_name == channel_name) - .first() - ) +def get_channel_by_name( + channel_name: str, session: Session +) -> ChatChannelCaches | None: + channel = ( + session.query(ChatChannelCaches) + .where(ChatChannelCaches.channel_name == channel_name) + .first() + ) return channel def get_channel_by_name_and_ip_port( - channel_name: str, ip: str, port: int + channel_name: str, ip: str, port: int, session: Session ) -> ChatChannelCaches | None: assert isinstance(channel_name, str) assert isinstance(ip, str) assert isinstance(port, int) - with Session(ENGINE) as session: - result = ( - session.query(ChatChannelCaches) - .join(ChatChannelUserCaches) - .where( - ChatChannelUserCaches.channel_name == channel_name, - ChatChannelUserCaches.remote_ip_address == ip, - ChatChannelUserCaches.remote_port == port, - ) - .first() + + result = ( + session.query(ChatChannelCaches) + .join(ChatChannelUserCaches) + .where( + ChatChannelUserCaches.channel_name == channel_name, + ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_port == port, ) + .first() + ) return result def get_channel_user_cache_by_name( - channel_name: str, nick_name: str + channel_name: str, nick_name: str, session: Session ) -> ChatChannelUserCaches | None: assert isinstance(channel_name, str) assert isinstance(nick_name, str) - with Session(ENGINE) as session: - result = ( - session.query(ChatChannelUserCaches) - .where( - ChatChannelUserCaches.channel_name == channel_name, - ChatChannelUserCaches.nick_name == nick_name, - ) - .first() + + result = ( + session.query(ChatChannelUserCaches) + .where( + ChatChannelUserCaches.channel_name == channel_name, + ChatChannelUserCaches.nick_name == nick_name, ) + .first() + ) return result def get_channel_user_cache_by_name_and_ip_port( - channel_name: str, ip: str, port: int + channel_name: str, ip: str, port: int, session: Session ) -> ChatChannelUserCaches | None: - with Session(ENGINE) as session: - result = ( - session.query(ChatChannelUserCaches) - .where( - ChatChannelUserCaches.channel_name == channel_name, - ChatChannelUserCaches.remote_ip_address == ip, - ChatChannelUserCaches.remote_port == port, - ) - .first() + result = ( + session.query(ChatChannelUserCaches) + .where( + ChatChannelUserCaches.channel_name == channel_name, + ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_port == port, ) + .first() + ) return result -def get_channel_user_caches_by_name(channel_name: str) -> list[ChatChannelUserCaches]: +def get_channel_user_caches_by_name( + channel_name: str, session: Session +) -> list[ChatChannelUserCaches]: assert isinstance(channel_name, str) - with Session(ENGINE) as session: - result: list[ChatChannelUserCaches] = ( - session.query(ChatChannelUserCaches.key_values) - .where(ChatChannelUserCaches.channel_name == channel_name) - .all() - ) # type:ignore + + result: list[ChatChannelUserCaches] = ( + session.query(ChatChannelUserCaches.key_values) + .where(ChatChannelUserCaches.channel_name == channel_name) + .all() + ) # type:ignore return result -def update_channel_time(channel: ChatChannelCaches): +def update_channel_time(channel: ChatChannelCaches, session: Session): channel.update_time = datetime.now() # type: ignore - with Session(ENGINE) as session: - session.commit() + session.commit() -def db_commit(): - with Session(ENGINE) as session: - session.commit() +def db_commit(session: Session): + session.commit() -def get_user_cache_by_nick_name(nick_name: str) -> ChatUserCaches | None: - with Session(ENGINE) as session: - result = ( - session.query(ChatUserCaches) - .where(ChatUserCaches.nick_name == nick_name) - .first() - ) +def get_user_cache_by_nick_name( + nick_name: str, session: Session +) -> ChatUserCaches | None: + result = ( + session.query(ChatUserCaches) + .where(ChatUserCaches.nick_name == nick_name) + .first() + ) return result -def get_user_cache_by_ip_port(ip: str, port: int) -> ChatUserCaches: - with Session(ENGINE) as session: - result = ( - session.query(ChatUserCaches) - .where( - ChatUserCaches.remote_ip_address == ip, ChatUserCaches.remote_port == port - ) - .first() +def get_user_cache_by_ip_port( + ip: str, port: int, session: Session +) -> ChatUserCaches | None: + result = ( + session.query(ChatUserCaches) + .where( + ChatUserCaches.remote_ip_address == ip, + ChatUserCaches.remote_port == port, ) - assert isinstance(result, ChatUserCaches) + .first() + ) + assert isinstance(result, ChatUserCaches | None) return result -def get_whois_result(nick: str) -> tuple: +def get_whois_result(nick: str, session: Session) -> tuple: """ nick is unique in chat """ - with Session(ENGINE) as session: - info = session.query(ChatUserCaches).first() - - if info is None: - raise NoSuchNickException(f"User not find by nick name:{nick}.") - channels = ( - session.query(ChatChannelUserCaches.channel_name) - .join( - ChatUserCaches, ChatChannelUserCaches.nick_name == ChatUserCaches.nick_name - ) - .where(ChatChannelUserCaches.nick_name == info.nick_name) - .all() + + info = session.query(ChatUserCaches).first() + + if info is None: + raise NoSuchNickException(f"User not find by nick name:{nick}.") + channels = ( + session.query(ChatChannelUserCaches.channel_name) + .join( + ChatUserCaches, + ChatChannelUserCaches.nick_name == ChatUserCaches.nick_name, ) + .where(ChatChannelUserCaches.nick_name == info.nick_name) + .all() + ) return ( info.nick_name, info.user_name, @@ -288,70 +302,73 @@ def get_whois_result(nick: str) -> tuple: ) # type:ignore -def remove_user_caches_by_ip_port(ip: str, port: int): +def remove_user_caches_by_ip_port(ip: str, port: int, session: Session): assert isinstance(ip, str) assert isinstance(port, int) - with Session(ENGINE) as session: - session.query(ChatChannelUserCaches).where( - ChatChannelUserCaches.remote_ip_address == ip, - ChatChannelUserCaches.remote_port == port, - ).delete() + + session.query(ChatChannelUserCaches).where( + ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_port == port, + ).delete() -def remove_channel(cache: ChatChannelCaches) -> None: +def remove_user_cache(cache: ChatUserCaches, session: Session): + session.delete(cache) + session.commit() + + +def remove_channel(cache: ChatChannelCaches, session: Session) -> None: assert isinstance(cache, ChatChannelCaches) - with Session(ENGINE) as session: - session.delete(cache) - session.commit() + + session.delete(cache) + session.commit() -def remove_user(cache: ChatChannelUserCaches): +def remove_user(cache: ChatChannelUserCaches, session: Session): assert isinstance(cache, ChatChannelUserCaches) - with Session(ENGINE) as session: - session.delete(cache) - session.commit() - - -def is_user_exist(ip: str, port: int) -> bool: - with Session(ENGINE) as session: - user_count = ( - session.query(ChatChannelUserCaches) - .where( - ChatChannelUserCaches.remote_ip_address == ip, - ChatChannelUserCaches.remote_port == port, - ) - .count() + + session.delete(cache) + session.commit() + + +def is_user_exist(ip: str, port: int, session: Session) -> bool: + user_count = ( + session.query(ChatChannelUserCaches) + .where( + ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_port == port, ) + .count() + ) if user_count == 1: return True else: return False -def update_client(cache: ChatChannelUserCaches): +def update_client(cache: ChatChannelUserCaches, session: Session): assert isinstance(cache, ChatChannelUserCaches) - with Session(ENGINE) as session: - session.commit() + + session.commit() -def add_invited(channel_name: str, client_ip: str, client_port: int): +def add_invited(channel_name: str, client_ip: str, client_port: int, session: Session): pass -def find_channel_by_substring(channel_name: str) -> list[dict]: +def find_channel_by_substring(channel_name: str, session: Session) -> list[dict]: assert isinstance(channel_name, str) - with Session(ENGINE) as session: - names, topics = ( - session.query(ChatChannelCaches.channel_name, ChatChannelCaches.topic) - .where(ChatChannelCaches.channel_name.like(f"%{channel_name}%")) - .all() - ) - users = ( - session.query(ChatChannelUserCaches) - .where(ChatChannelUserCaches.channel_name.like(f"%{channel_name}%")) - .all() - ) + names, topics = ( + session.query(ChatChannelCaches.channel_name, ChatChannelCaches.topic) + .where(ChatChannelCaches.channel_name.like(f"%{channel_name}%")) + .all() + ) + users = ( + session.query(ChatChannelUserCaches) + .where(ChatChannelUserCaches.channel_name.like(f"%{channel_name}%")) + .all() + ) data: list[dict] = [] assert isinstance(names, list) assert isinstance(topics, list) @@ -362,25 +379,26 @@ def find_channel_by_substring(channel_name: str) -> list[dict]: return data -def find_user_by_substring(user_name: str) -> list[dict]: +def find_user_by_substring(user_name: str, session: Session) -> list[dict]: assert isinstance(user_name, str) - with Session(ENGINE) as session: - names, topics, users = ( - session.query( - ChatChannelCaches.channel_name, - ChatChannelCaches.topic, - func.count(ChatChannelUserCaches.channel_name), - ) - .join( - ChatUserCaches, ChatUserCaches.nick_name == ChatChannelUserCaches.nick_name - ) - .join( - ChatChannelCaches, - ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name, - ) - .where(ChatUserCaches.user_name.like(f"%{user_name}%")) - .all() + + names, topics, users = ( + session.query( + ChatChannelCaches.channel_name, + ChatChannelCaches.topic, + func.count(ChatChannelUserCaches.channel_name), + ) + .join( + ChatUserCaches, + ChatUserCaches.nick_name == ChatChannelUserCaches.nick_name, ) + .join( + ChatChannelCaches, + ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name, + ) + .where(ChatUserCaches.user_name.like(f"%{user_name}%")) + .all() + ) data: list[dict] = [] for name, topic, count in zip(names, topics, users): @@ -389,26 +407,25 @@ def find_user_by_substring(user_name: str) -> list[dict]: return data -def create_channel_user_caches(chan_user: ChatChannelUserCaches): - with Session(ENGINE) as session: - session.add(chan_user) - session.commit() - - -def get_channel_user_caches(channel_name: str) -> list[dict]: - with Session(ENGINE) as session: - result: list[ChatChannelUserCaches] = ( - session.query(ChatChannelUserCaches) - .join( - ChatChannelCaches, - ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name, - ) - .join( - ChatUserCaches, ChatUserCaches.user_name == ChatChannelUserCaches.user_name - ) - .where(ChatChannelUserCaches.channel_name == channel_name) - .all() +def create_channel_user_caches(chan_user: ChatChannelUserCaches, session: Session): + session.add(chan_user) + session.commit() + + +def get_channel_user_caches(channel_name: str, session: Session) -> list[dict]: + result: list[ChatChannelUserCaches] = ( + session.query(ChatChannelUserCaches) + .join( + ChatChannelCaches, + ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name, ) + .join( + ChatUserCaches, + ChatUserCaches.user_name == ChatChannelUserCaches.user_name, + ) + .where(ChatChannelUserCaches.channel_name == channel_name) + .all() + ) data = [] for r in result: temp = {} @@ -420,22 +437,23 @@ def get_channel_user_caches(channel_name: str) -> list[dict]: return data -def get_channel_user_cache_by_ip(ip: str, port: int) -> list[dict]: - with Session(ENGINE) as session: - result: list[ChatChannelUserCaches] = ( - session.query(ChatChannelUserCaches) - .join( - ChatChannelCaches, - ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name, - ) - .join( - ChatUserCaches, ChatUserCaches.user_name == ChatChannelUserCaches.user_name - ) - .where( - ChatUserCaches.remote_ip_address == ip, ChatUserCaches.remote_port == port - ) - .all() +def get_channel_user_cache_by_ip(ip: str, port: int, session: Session) -> list[dict]: + result: list[ChatChannelUserCaches] = ( + session.query(ChatChannelUserCaches) + .join( + ChatChannelCaches, + ChatChannelCaches.channel_name == ChatChannelUserCaches.channel_name, + ) + .join( + ChatUserCaches, + ChatUserCaches.user_name == ChatChannelUserCaches.user_name, + ) + .where( + ChatUserCaches.remote_ip_address == ip, + ChatUserCaches.remote_port == port, ) + .all() + ) data = [] for r in result: temp = {} @@ -445,3 +463,15 @@ def get_channel_user_cache_by_ip(ip: str, port: int) -> list[dict]: temp["nick_name"] = r.nick_name data.append(temp) return data + + +if __name__ == "__main__": + pass + # + # result = ( + # session.query(ChatUserCaches) + # .where(ChatUserCaches.nick_name == "172.19.0.5:52986") + # .first() + # ) + # result.nick_name = "changed" + # session.commit() diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index a920ace6e..bf1fc7e16 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -1,9 +1,10 @@ from datetime import datetime from typing import TYPE_CHECKING, cast from backends.library.abstractions.contracts import RequestBase -from backends.library.abstractions.handler_base import HandlerBase +import backends.library.abstractions.handler_base as hb + + from backends.library.database.pg_orm import ( - ENGINE, ChatChannelCaches, ChatUserCaches, ChatChannelUserCaches, @@ -42,6 +43,7 @@ WhoIsRequest, WhoRequest, ) +from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.protocols.chat.aggregates.enums import ( GetKeyRequestType, TopicRequestType, @@ -70,6 +72,29 @@ WhoResult, ) from sqlalchemy.orm import Session + + +class HandlerBase(hb.HandlerBase): + _cache: ChatUserCaches | None + _session: Session + + def _request_check(self) -> None: + self._cache = data.get_user_cache_by_ip_port( + self._request.client_ip, self._request.client_port, self._session + ) + if self._cache is None: + self._cache = ChatUserCaches( + server_id=self._request.server_id, + remote_ip_address=self._request.client_ip, + remote_port=self._request.client_port, + update_time=datetime.now(), + nick_name=f"{self._request.client_ip}:{self._request.client_port}", + ) + self._session.add(self._cache) + else: + self._cache.update_time = datetime.now() # type: ignore + + # region General @@ -77,7 +102,7 @@ class CdKeyHandler(HandlerBase): _request: CdkeyRequest def _data_operate(self) -> None: - is_valid = data.is_cdkey_valid(self._request.cdkey) + is_valid = data.is_cdkey_valid(self._request.cdkey, self._session) if not is_valid: raise LoginFailedException("cdkey not matched") @@ -86,34 +111,36 @@ class CryptHandler(HandlerBase): _request: CryptRequest def _data_operate(self) -> None: - with Session(ENGINE) as session: - - result = data.get_user_cache_by_ip_port( - self._request.client_ip, self._request.client_port - ) - if result is None: - raise NoSuchNickException(f"No nick found for {self._request.client_ip}") - result.game_name = self._request.gamename # type: ignore - session.commit() + assert self._cache is not None + self._cache.game_name = self._request.gamename # type: ignore + self._secret_key = data.get_secret_key_by_game_name( + self._request.gamename, self._session + ) + if self._secret_key is None: + raise UniSpyException("game secret key not found in database.") def _result_construct(self) -> None: - self._result = CryptResult() + assert isinstance(self._secret_key, str) + self._result = CryptResult(secret_key=self._secret_key) class GetKeyHandler(HandlerBase): _request: GetKeyRequest def _data_operate(self) -> None: - caches = data.get_user_cache_by_nick_name(self._request.nick_name) + caches = data.get_user_cache_by_nick_name( + self._request.nick_name, self._session + ) if caches is None: raise NoSuchNickException("nick not found") if TYPE_CHECKING: - self.caches = cast(list, caches) + kv = cast(dict, caches.key_value) + self._values = cast(list, kv.keys()) def _result_construct(self) -> None: self._result = GetKeyResult( - nick_name=self._request.nick_name, values=self.caches + nick_name=self._request.nick_name, values=self._values ) @@ -133,6 +160,7 @@ def _data_operate(self) -> None: self._request.channel_name, self._request.client_ip, self._request.client_port, + self._session, ) if chann is None: raise NoSuchChannelException( @@ -141,7 +169,7 @@ def _data_operate(self) -> None: assert isinstance(chann.invited_nicks, list) chann.invited_nicks.append(self._request.nick_name) - data.db_commit() + data.db_commit(self._session) class ListHandler(HandlerBase): @@ -158,11 +186,15 @@ def _request_check(self) -> None: def _data_operate(self) -> None: if self._request.is_searching_channel: # get the channel names with the substring - self._data = data.find_channel_by_substring(self._request.filter) + self._data = data.find_channel_by_substring( + self._request.filter, self._session + ) return if self._request.is_searching_user: # get the user names with the substring - self._data = data.find_user_by_substring(self._request.filter) + self._data = data.find_user_by_substring( + self._request.filter, self._session + ) def _result_construct(self) -> None: data = [] @@ -188,21 +220,37 @@ class NickHandler(HandlerBase): _request: NickRequest def _data_operate(self) -> None: - is_nick = data.is_nick_exist(self._request.nick_name) - if is_nick: - raise NickNameInUseException( - old_nick=self._request.nick_name, - new_nick="", - message="nick name in use", - ) + # cache = data.get_user_cache_by_nick_name("172.19.0.5:52986") + self._cache.nick_name = self._request.nick_name # type: ignore + self._session.commit() + cache = None + # some game do not use CRYPT + # todo check game with no encryption send nick or user request first + if cache is None: + # assign nick_name to current user + self._cache.nick_name = self._request.nick_name # type: ignore else: - cache = ChatUserCaches( - nick_name=self._request.nick_name, - server_id=self._request.server_id, - update_time=datetime.now(), - ) - ChatUserCaches() - data.add_nick_cache(cache) + assert isinstance(cache.update_time, datetime) + if (datetime.now() - cache.update_time).seconds > 120: + # old profile delete it + self._session.delete(cache) + # data.remove_user_cache(cache) + self._cache.nick_name = self._request.nick_name # type: ignore + return + + if ( + cache.remote_ip_address != self._request.client_ip + and cache.remote_port != self._request.client_port + ): # type: ignore + raise NickNameInUseException( + old_nick=self._request.nick_name, + new_nick="", + message="nick name in use", + ) + else: + # update user cache + self._cache.nick_name = self._request.nick_name # type: ignore + self._session.commit() def _result_construct(self) -> None: self._result = NickResult(nick_name=self._request.nick_name) @@ -213,7 +261,7 @@ class QuitHandler(HandlerBase): def _data_operate(self) -> None: data.remove_user_caches_by_ip_port( - self._request.client_ip, self._request.client_port + self._request.client_ip, self._request.client_port, self._session ) raise NotImplementedError() @@ -228,20 +276,21 @@ class SetKeyHandler(HandlerBase): def _data_operate(self) -> None: user = data.get_user_cache_by_ip_port( - self._request.client_ip, self._request.client_port + self._request.client_ip, self._request.client_port, self._session ) if user is None: raise NoSuchNickException("The ip and port is not find in database") user.key_value = self._request.key_values # type:ignore - data.db_commit() + super()._data_operate() class UserHandler(HandlerBase): _request: UserRequest def _data_operate(self) -> None: - raise NotImplementedError("maybe update the user caches") + self._cache.user_name = self._request.user_name # type: ignore + self._session.commit() class WhoHandler(HandlerBase): @@ -254,11 +303,13 @@ def _data_operate(self) -> None: self._get_user_info() def _get_channel_user_info(self) -> None: - self._data = data.get_channel_user_caches(self._request.channel_name) + self._data = data.get_channel_user_caches( + self._request.channel_name, self._session + ) def _get_user_info(self) -> None: self._data = data.get_channel_user_cache_by_ip( - self._request.client_ip, self._request.client_port + self._request.client_ip, self._request.client_port, self._session ) def _result_construct(self) -> None: @@ -273,7 +324,9 @@ class WhoIsHandler(HandlerBase): _request: WhoIsRequest def _data_operate(self) -> None: - self._data: tuple = data.get_whois_result(self._request.nick_name) + self._data: tuple = data.get_whois_result( + self._request.nick_name, self._session + ) def _result_construct(self) -> None: self._result = WhoIsResult( @@ -307,11 +360,13 @@ def _data_operate(self) -> None: self.get_channel_specific_user_key_value() def get_channel_all_user_key_value(self): - self._data = data.get_channel_user_caches_by_name(self._request.channel_name) + self._data = data.get_channel_user_caches_by_name( + self._request.channel_name, self._session + ) def get_channel_specific_user_key_value(self): d = data.get_channel_user_cache_by_name( - self._request.channel_name, self._request.nick_name + self._request.channel_name, self._request.nick_name, self._session ) if d is not None: self._data = [d] @@ -362,9 +417,10 @@ def _data_operate(self) -> None: creator=self._user.nick_name, group_id=0, max_num_user=100, + session=self._session, ) ChannelHelper.join(self._channel, self._user) - self._nicks = ChannelHelper.get_channel_all_nicks(self._channel) + self._nicks = ChannelHelper.get_channel_all_nicks(self._channel, self._session) def _result_construct(self) -> None: assert self._user is not None @@ -393,7 +449,7 @@ def _request_check(self) -> None: assert isinstance(self._channel, ChatChannelCaches) assert isinstance(self._channel.channel_name, str) self._kickee = data.get_channel_user_cache_by_name( - self._channel.channel_name, self._request.kickee_nick_name + self._channel.channel_name, self._request.kickee_nick_name, self._session ) if self._kickee is None: raise BadChannelKeyException( @@ -413,7 +469,9 @@ class ModeHandler(ChannelHandlerBase): def _data_operate(self) -> None: assert self._channel assert self._channel_user - ChannelHelper.change_modes(self._channel, self._channel_user, self._request) + ChannelHelper.change_modes( + self._channel, self._channel_user, self._request, self._session + ) def _result_construct(self) -> None: raise NotImplementedError() @@ -432,7 +490,7 @@ def _request_check(self) -> None: def _data_operate(self) -> None: assert self._channel - self._nicks = ChannelHelper.get_channel_all_nicks(self._channel) + self._nicks = ChannelHelper.get_channel_all_nicks(self._channel, self._session) def _result_construct(self) -> None: assert self._user @@ -476,7 +534,7 @@ def _request_check(self) -> None: assert isinstance(self._channel_user.is_channel_operator, bool) if self._channel_user.is_channel_operator: self._channel.key_values = self._request.key_values # type:ignore - data.db_commit() + data.db_commit(self._session) def _result_construct(self) -> None: assert self._channel_user @@ -495,7 +553,7 @@ class SetCkeyHandler(ChannelHandlerBase): def _data_operate(self) -> None: self._channel_user.key_values = self._request.key_values # type:ignore - data.db_commit() + data.db_commit(self._session) self._is_broadcast = True def _result_construct(self) -> None: diff --git a/src/backends/protocols/gamespy/chat/helper.py b/src/backends/protocols/gamespy/chat/helper.py index 8be258322..2b4050456 100644 --- a/src/backends/protocols/gamespy/chat/helper.py +++ b/src/backends/protocols/gamespy/chat/helper.py @@ -153,10 +153,11 @@ def create( key_values: dict, update_time: datetime, modes: list, + session: Session, creator: str | None = None, ) -> ChatChannelCaches: # check whether if channel exist, if not user is creator - is_exist = data.is_channel_exist(channel_name, game_name) + is_exist = data.is_channel_exist(channel_name, game_name, session) if is_exist: raise NoSuchChannelException( f"Channel: {channel_name} is already exist, can not create a new one" @@ -186,6 +187,7 @@ def change_modes( channel: ChatChannelCaches, changer: ChatChannelUserCaches, request: ModeRequest, + session: Session, ): assert isinstance(channel, ChatChannelCaches) assert isinstance(changer, ChatChannelUserCaches) @@ -236,7 +238,7 @@ def change_modes( channel.banned_nicks.remove(request.nick_name) case ModeOperationType.ADD_CHANNEL_OPERATOR: u = data.get_channel_user_cache_by_name( - request.channel_name, request.nick_name + request.channel_name, request.nick_name, session ) if u is None: raise BadChannelKeyException( @@ -247,29 +249,29 @@ def change_modes( session.commit() case ModeOperationType.REMOVE_CHANNEL_OPERATOR: u = data.get_channel_user_cache_by_name( - request.channel_name, request.nick_name + request.channel_name, request.nick_name, session ) u.is_channel_operator = False # type: ignore session.commit() case ModeOperationType.ENABLE_USER_VOICE_PERMISSION: u = data.get_channel_user_cache_by_name( - request.channel_name, request.nick_name + request.channel_name, request.nick_name, session ) u.is_voiceable = True # type: ignore case ModeOperationType.DISABLE_USER_VOICE_PERMISSION: u = data.get_channel_user_cache_by_name( - request.channel_name, request.nick_name + request.channel_name, request.nick_name, session ) u.is_voiceable = False # type: ignore session.commit() @staticmethod - def get_all_user_nick_string(channel: ChatChannelCaches) -> str: + def get_all_user_nick_string(channel: ChatChannelCaches, session: Session) -> str: assert isinstance(channel, ChatChannelCaches) assert isinstance(channel.channel_name, str) - users = data.get_channel_user_caches_by_name(channel.channel_name) + users = data.get_channel_user_caches_by_name(channel.channel_name, session) nicks = "" for user in users: assert isinstance(user.is_channel_creator, bool) @@ -284,10 +286,12 @@ def get_all_user_nick_string(channel: ChatChannelCaches) -> str: return nicks @staticmethod - def get_channel_all_nicks(channel: ChatChannelCaches) -> list[str]: + def get_channel_all_nicks( + channel: ChatChannelCaches, session: Session + ) -> list[str]: assert channel is not None assert isinstance(channel.channel_name, str) - users = data.get_channel_user_caches_by_name(channel.channel_name) + users = data.get_channel_user_caches_by_name(channel.channel_name, session) nicks = [] for user in users: nicks.append(user.nick_name) diff --git a/src/backends/protocols/gamespy/chat/requests.py b/src/backends/protocols/gamespy/chat/requests.py index fd51ca2c0..2b21b9007 100644 --- a/src/backends/protocols/gamespy/chat/requests.py +++ b/src/backends/protocols/gamespy/chat/requests.py @@ -1,11 +1,20 @@ import backends.library.abstractions.contracts as lib -from frontends.gamespy.protocols.chat.aggregates.enums import GetKeyRequestType, LoginRequestType, MessageType, ModeOperationType, ModeRequestType, TopicRequestType, WhoRequestType +from frontends.gamespy.protocols.chat.aggregates.enums import ( + GetKeyRequestType, + LoginRequestType, + MessageType, + ModeOperationType, + ModeRequestType, + TopicRequestType, + WhoRequestType, +) class RequestBase(lib.RequestBase): raw_request: str command_name: str + # region General @@ -84,9 +93,8 @@ class UserIPRequest(RequestBase): class UserRequest(RequestBase): user_name: str - host_name: str + local_ip_address: str server_name: str - nick_name: str name: str @@ -107,6 +115,7 @@ class GetKeyRequest(RequestBase): unknown_cmd_param: str keys: list[str] + # region Channel @@ -181,6 +190,7 @@ class MessageRequestBase(ChannelRequestBase): nick_name: str message: str + # region Message diff --git a/src/backends/protocols/gamespy/game_status/requests.py b/src/backends/protocols/gamespy/game_status/requests.py index 1f51aee4d..09bc722f3 100644 --- a/src/backends/protocols/gamespy/game_status/requests.py +++ b/src/backends/protocols/gamespy/game_status/requests.py @@ -1,5 +1,3 @@ - - from pydantic import Field import backends.library.abstractions.contracts as lib from frontends.gamespy.protocols.game_status.aggregations.enums import ( @@ -42,7 +40,7 @@ class GetProfileIdRequest(RequestBase): class NewGameRequest(RequestBase): is_client_local_storage_available: bool - challenge: str + challenge: str | None = None connection_id: int = Field( description="The session key that backend send to client." ) diff --git a/src/backends/protocols/gamespy/game_traffic_relay/data.py b/src/backends/protocols/gamespy/game_traffic_relay/data.py index 2c8763b86..c40fc0c6a 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/data.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/data.py @@ -1,58 +1,59 @@ from datetime import datetime from uuid import UUID -from backends.library.database.pg_orm import ENGINE, RelayServerCaches +from backends.library.database.pg_orm import RelayServerCaches from sqlalchemy.orm import Session -def search_relay_server(server_id: UUID, server_ip: str) -> RelayServerCaches | None: - with Session(ENGINE) as session: - result = ( - session.query(RelayServerCaches) - .where( - RelayServerCaches.server_id == server_id, - RelayServerCaches.public_ip_address == server_ip, - ) - .first() + +def search_relay_server( + server_id: UUID, server_ip: str, session: Session +) -> RelayServerCaches | None: + result = ( + session.query(RelayServerCaches) + .where( + RelayServerCaches.server_id == server_id, + RelayServerCaches.public_ip_address == server_ip, ) + .first() + ) return result -def get_available_relay_serves() -> list[RelayServerCaches]: +def get_available_relay_serves(session: Session) -> list[RelayServerCaches]: """ Return ------ list of ip:port """ - with Session(ENGINE) as session: - result: list[RelayServerCaches] = session.query(RelayServerCaches).all() + + result: list[RelayServerCaches] = session.query(RelayServerCaches).all() return result -def update_relay_server(info: RelayServerCaches): +def update_relay_server(info: RelayServerCaches, session: Session): info.update_time = datetime.now() # type: ignore - with Session(ENGINE) as session: - session.commit() + session.commit() -def add_relay_server(info: RelayServerCaches): - with Session(ENGINE) as session: - session.add(info) - session.commit() +def add_relay_server(info: RelayServerCaches, session: Session): + session.add(info) + session.commit() -def delete_relay_server(server_id: UUID, ip_address: str, port: int): + +def delete_relay_server(server_id: UUID, ip_address: str, port: int, session: Session): assert isinstance(server_id, UUID) assert isinstance(ip_address, str) assert isinstance(port, int) - with Session(ENGINE) as session: - info = ( - session.query(RelayServerCaches) - .where( - RelayServerCaches.server_id == server_id, - RelayServerCaches.public_ip_address == ip_address, - RelayServerCaches.public_port == port, - ) - .first() + + info = ( + session.query(RelayServerCaches) + .where( + RelayServerCaches.server_id == server_id, + RelayServerCaches.public_ip_address == ip_address, + RelayServerCaches.public_port == port, ) - session.delete(info) - session.commit() + .first() + ) + session.delete(info) + session.commit() diff --git a/src/backends/protocols/gamespy/game_traffic_relay/handlers.py b/src/backends/protocols/gamespy/game_traffic_relay/handlers.py index cbf20d69d..38d46367b 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/handlers.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/handlers.py @@ -22,7 +22,7 @@ def __init__(self, request: UpdateGTRServiceRequest) -> None: def _data_operate(self) -> None: info = data.search_relay_server( - self._request.server_id, self._request.public_ip_address + self._request.server_id, self._request.public_ip_address, self._session ) if info is None: info = RelayServerCaches( @@ -32,7 +32,7 @@ def _data_operate(self) -> None: client_count=self._request.client_count, update_time=datetime.now(), ) - data.add_relay_server(info) + data.add_relay_server(info, self._session) else: # refresh update time - data.update_relay_server(info) + data.update_relay_server(info, self._session) diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py index 07660c171..771e0f2b0 100644 --- a/src/backends/protocols/gamespy/natneg/data.py +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -1,7 +1,6 @@ from datetime import datetime, timedelta from backends.library.database.pg_orm import ( - ENGINE, InitPacketCaches, NatFailCaches, RelayServerCaches, @@ -13,132 +12,134 @@ from sqlalchemy.orm import Session -def add_init_packet(info: InitPacketCaches) -> None: + +def add_init_packet(info: InitPacketCaches, session: Session) -> None: assert isinstance(info, InitPacketCaches) - with Session(ENGINE) as session: - session.add(info) - session.commit() + + session.add(info) + session.commit() -def count_init_info(cookie: int, version: int) -> int: +def count_init_info(cookie: int, version: int, session: Session) -> int: time = datetime.now() - timedelta(seconds=30) - with Session(ENGINE) as session: - result = ( - session.query(InitPacketCaches) - .where( - InitPacketCaches.cookie == cookie, - InitPacketCaches.version == version, - InitPacketCaches.update_time >= time, - ) - .count() + + result = ( + session.query(InitPacketCaches) + .where( + InitPacketCaches.cookie == cookie, + InitPacketCaches.version == version, + InitPacketCaches.update_time >= time, ) - return result + .count() + ) + return result def get_init_info( - cookie: int, client_index: NatClientIndex, port_type: NatPortType + cookie: int, client_index: NatClientIndex, port_type: NatPortType, session: Session ) -> InitPacketCaches | None: - with Session(ENGINE) as session: - result = ( - session.query(InitPacketCaches) - .where( - InitPacketCaches.cookie == cookie, - InitPacketCaches.client_index == client_index, - InitPacketCaches.port_type == port_type, - ) - .first() + result = ( + session.query(InitPacketCaches) + .where( + InitPacketCaches.cookie == cookie, + InitPacketCaches.client_index == client_index, + InitPacketCaches.port_type == port_type, ) - return result + .first() + ) + return result -def get_init_infos(cookie: int, client_index: NatClientIndex) -> list[InitPacketCaches]: +def get_init_infos( + cookie: int, client_index: NatClientIndex, session: Session +) -> list[InitPacketCaches]: # query the latest init info with in 30 seconds time = datetime.now() - timedelta(seconds=30) - with Session(ENGINE) as session: - result = ( - session.query(InitPacketCaches) - .where( - InitPacketCaches.cookie == cookie, - InitPacketCaches.client_index == client_index, - InitPacketCaches.update_time >= time, - ) - .all() + + result = ( + session.query(InitPacketCaches) + .where( + InitPacketCaches.cookie == cookie, + InitPacketCaches.client_index == client_index, + InitPacketCaches.update_time >= time, ) - return result + .all() + ) + return result -def update_init_info(info: InitPacketCaches) -> None: +def update_init_info(info: InitPacketCaches, session: Session) -> None: assert isinstance(info, InitPacketCaches) - with Session(ENGINE) as session: - session.commit() + + session.commit() -def remove_init_info(info: InitPacketCaches) -> None: +def remove_init_info(info: InitPacketCaches, session: Session) -> None: assert isinstance(info, InitPacketCaches) - with Session(ENGINE) as session: - session.delete(info) - session.commit() + session.delete(info) + session.commit() -def store_nat_fail_info(info: NatFailCaches) -> None: + +def store_nat_fail_info(info: NatFailCaches, session: Session) -> None: assert isinstance(info, NatFailCaches) - with Session(ENGINE) as session: - session.add(info) - session.commit() + + session.add(info) + session.commit() -def update_nat_fail_info(info: NatFailCaches) -> None: +def update_nat_fail_info(info: NatFailCaches, session: Session) -> None: assert isinstance(info, NatFailCaches) - with Session(ENGINE) as session: - result = get_nat_fail_info(info) - if result is not None: - session.delete(result) - store_nat_fail_info(info) + result = get_nat_fail_info(info, session) + if result is not None: + session.delete(result) + store_nat_fail_info(info, session) -def remove_nat_fail_info(info: NatFailCaches) -> None: + +def remove_nat_fail_info(info: NatFailCaches, session: Session) -> None: assert isinstance(info, NatFailCaches) - with Session(ENGINE) as session: - session.delete(info) - session.commit() + + session.delete(info) + session.commit() -def get_nat_fail_info(info: NatFailCaches): +def get_nat_fail_info(info: NatFailCaches, session: Session): result = get_nat_fail_info_by_ip( - str(info.public_ip_address1), str(info.public_ip_address2) + str(info.public_ip_address1), str(info.public_ip_address2), session ) return result -def get_nat_fail_info_by_ip(public_ip1: str, public_ip2: str) -> list[NatFailCaches]: - with Session(ENGINE) as session: - result = ( - session.query(NatFailCaches) - .where( - NatFailCaches.public_ip_address1 == public_ip1, - NatFailCaches.public_ip_address2 == public_ip2, - ) - .all() +def get_nat_fail_info_by_ip( + public_ip1: str, public_ip2: str, session: Session +) -> list[NatFailCaches]: + result = ( + session.query(NatFailCaches) + .where( + NatFailCaches.public_ip_address1 == public_ip1, + NatFailCaches.public_ip_address2 == public_ip2, ) - return result + .all() + ) + return result def get_game_traffic_relay_servers( - number: int | None = None, + session: Session, number: int | None = None ) -> list[RelayServerCaches]: - with Session(ENGINE) as session: - if number is None: - result = ( - session.query(RelayServerCaches) - .order_by(RelayServerCaches.client_count.desc()) - .all() - ) - else: - assert isinstance(number, int) - result = ( - session.query(RelayServerCaches) - .order_by(RelayServerCaches.client_count.desc()) - .limit(number) - .all() - ) - return result + if number is None: + result = ( + session.query(RelayServerCaches) + .order_by(RelayServerCaches.client_count.desc()) + .all() + ) + else: + assert isinstance(number, int) + result = ( + session.query(RelayServerCaches) + .order_by(RelayServerCaches.client_count.desc()) + .limit(number) + .all() + ) + return result diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py index 89f2b4be1..251290e71 100644 --- a/src/backends/protocols/gamespy/natneg/handlers.py +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -20,7 +20,10 @@ def __init__(self, request: InitRequest) -> None: def _data_operate(self) -> None: info = data.get_init_info( - self._request.cookie, self._request.client_index, self._request.port_type + self._request.cookie, + self._request.client_index, + self._request.port_type, + self._session, ) if info is None: info = InitPacketCaches( @@ -37,10 +40,10 @@ def _data_operate(self) -> None: private_port=self._request.private_port, update_time=datetime.now(timezone.utc), ) - data.add_init_packet(info) + data.add_init_packet(info, self._session) else: info.update_time = datetime.now(timezone.utc) # type: ignore - data.update_init_info(info) + data.update_init_info(info, self._session) class ConnectHandler(HandlerBase): @@ -54,14 +57,16 @@ def __init__(self, request: ConnectRequest) -> None: def _data_operate(self) -> None: # analysis NAT of both parties and find the proper ips init_infos_1 = data.get_init_infos( - self._request.cookie, self._request.client_index + self._request.cookie, self._request.client_index, self._session ) # choose the other index of the currect client if self._request.client_index == NatClientIndex.GAME_CLIENT: client_index_2 = NatClientIndex.GAME_SERVER else: client_index_2 = NatClientIndex.GAME_CLIENT - init_infos_2 = data.get_init_infos(self._request.cookie, client_index_2) + init_infos_2 = data.get_init_infos( + self._request.cookie, client_index_2, self._session + ) if len(init_infos_1) == 0 or len(init_infos_2) == 0: raise UniSpyException( f"no init info found for cookie {self._request.cookie}" @@ -69,7 +74,7 @@ def _data_operate(self) -> None: assert isinstance(init_infos_1[0].public_ip, str) assert isinstance(init_infos_2[0].public_ip, str) nat_fail_infos = data.get_nat_fail_info_by_ip( - init_infos_1[0].public_ip, init_infos_2[0].public_ip + init_infos_1[0].public_ip, init_infos_2[0].public_ip, self._session ) self._strategy = NatStrategy.USE_GAME_TRAFFIC_RALEY if len(nat_fail_infos) != 0: @@ -99,7 +104,7 @@ def _result_construct(self) -> None: elif self._strategy == NatStrategy.USE_GAME_TRAFFIC_RALEY: # get a small number of players server from database - relay_servers = data.get_game_traffic_relay_servers(5) + relay_servers = data.get_game_traffic_relay_servers(self._session, 5) # select strategy to choose one gtr server rs = relay_servers[0] assert isinstance(rs.public_ip_address, str) diff --git a/src/backends/protocols/gamespy/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py index fe5b85ee4..416355b70 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/data.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/data.py @@ -29,7 +29,6 @@ import backends.protocols.gamespy.presence_search_player.data as psp # region General -from backends.library.database.pg_orm import ENGINE from sqlalchemy.orm import Session @@ -37,48 +36,49 @@ def is_email_exist(email: str): return psp.is_email_exist(email) -def update_online_time(ip: str, port: int): +def update_online_time(ip: str, port: int,session:Session): if TYPE_CHECKING: assert isinstance(Users.lastip, Column) - with Session(ENGINE) as session: - result = session.query(Users).where(Users.lastip == ip).first() - if result is None: - return False - result.lastonline = datetime.now() - session.commit() + + result = session.query(Users).where(Users.lastip == ip).first() + if result is None: + return False + result.lastonline = datetime.now() + session.commit() -def delete_friend_by_profile_id(profile_id: int): - with Session(ENGINE) as session: - friend = session.query(Friends).where(Friends.friendid == profile_id).first() - if friend is None: - raise GPDatabaseException( - f"friend deletion have errors on profile id:{profile_id}" - ) - else: - session.delete(friend) - session.commit() - - -def get_blocked_profile_id_list(profile_id: int, namespace_id: int) -> list[int]: - with Session(ENGINE) as session: - result = ( - session.query(Blocked.targetid) - .where(Blocked.profileid == profile_id, Blocked.namespaceid == namespace_id) - .all() +def delete_friend_by_profile_id(profile_id: int,session:Session): + friend = session.query(Friends).where(Friends.friendid == profile_id).first() + if friend is None: + raise GPDatabaseException( + f"friend deletion have errors on profile id:{profile_id}" ) + else: + session.delete(friend) + session.commit() + + +def get_blocked_profile_id_list( + profile_id: int, namespace_id: int, session: Session +) -> list[int]: + result = ( + session.query(Blocked.targetid) + .where(Blocked.profileid == profile_id, Blocked.namespaceid == namespace_id) + .all() + ) if TYPE_CHECKING: result = cast(list[int], result) return result -def get_friend_profile_id_list(profile_id: int, namespace_id: int) -> list[int]: - with Session(ENGINE) as session: - result = ( - session.query(Friends.targetid) - .where(Friends.profileid == profile_id, Friends.namespaceid == namespace_id) - .all() - ) +def get_friend_profile_id_list( + profile_id: int, namespace_id: int, session: Session +) -> list[int]: + result = ( + session.query(Friends.targetid) + .where(Friends.profileid == profile_id, Friends.namespaceid == namespace_id) + .all() + ) if TYPE_CHECKING: result = cast(list[int], result) return result @@ -87,7 +87,9 @@ def get_friend_profile_id_list(profile_id: int, namespace_id: int) -> list[int]: # region Profile -def get_profile_infos(profile_id: int, session_key: str) -> GetProfileData: +def get_profile_infos( + profile_id: int, session_key: str, session: Session +) -> GetProfileData: """ Retrieve profile information based on profile_id and namespace_id. @@ -104,7 +106,6 @@ def get_profile_infos(profile_id: int, session_key: str) -> GetProfileData: assert isinstance(SubProfiles.namespaceid, Column) assert isinstance(SubProfiles.session_key, Column) - with Session(ENGINE) as session: namespace_id = ( session.query(SubProfiles.namespaceid) .where(SubProfiles.session_key == session_key) @@ -113,17 +114,16 @@ def get_profile_infos(profile_id: int, session_key: str) -> GetProfileData: if namespace_id is None: raise GPException("namespace not found") - with Session(ENGINE) as session: - result = ( - session.query(Users, Profiles, SubProfiles) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .join(Users, Profiles.userid == Users.userid) - .where( - Profiles.profileid == profile_id, - SubProfiles.namespaceid == namespace_id, - ) - .first() + result = ( + session.query(Users, Profiles, SubProfiles) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .join(Users, Profiles.userid == Users.userid) + .where( + Profiles.profileid == profile_id, + SubProfiles.namespaceid == namespace_id, ) + .first() + ) if result is None: raise GPException("no profile found") @@ -151,7 +151,9 @@ def get_profile_infos(profile_id: int, session_key: str) -> GetProfileData: return data -def get_user_info_list(email: str, nick_name: str) -> list[tuple[int, int, int]]: +def get_user_info_list( + email: str, nick_name: str, session: Session +) -> list[tuple[int, int, int]]: """ Retrieve the user information list based on the provided email and nickname. @@ -170,20 +172,21 @@ def get_user_info_list(email: str, nick_name: str) -> list[tuple[int, int, int]] assert isinstance(Users.email, Column) assert isinstance(Profiles.nick, Column) - with Session(ENGINE) as session: - result = ( - session.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where(Users.email == email, Profiles.nick == nick_name) - .all() - ) + result = ( + session.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where(Users.email == email, Profiles.nick == nick_name) + .all() + ) if TYPE_CHECKING: result = cast(list[tuple[int, int, int]], result) return result -def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: +def get_user_info( + unique_nick: str, namespace_id: int, session: Session +) -> tuple[int, int, int]: # if TYPE_CHECKING: # assert isinstance(Profiles.profileid, Column) # assert isinstance(Profiles.userid, Column) @@ -193,24 +196,23 @@ def get_user_info(unique_nick: str, namespace_id: int) -> tuple[int, int, int]: # assert isinstance(SubProfiles.uniquenick, Column) # assert isinstance(SubProfiles.namespaceid, Column) - with Session(ENGINE) as session: - result = ( - session.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where( - SubProfiles.uniquenick == unique_nick, - SubProfiles.namespaceid == namespace_id, - ) - .first() + result = ( + session.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where( + SubProfiles.uniquenick == unique_nick, + SubProfiles.namespaceid == namespace_id, ) + .first() + ) if TYPE_CHECKING: result = cast(tuple[int, int, int], result) return result def get_user_infos_by_uniquenick_namespace_id( - unique_nick: str, namespace_id: int + unique_nick: str, namespace_id: int, session: Session ) -> LoginData | None: if TYPE_CHECKING: assert isinstance(Profiles.profileid, Column) @@ -224,33 +226,34 @@ def get_user_infos_by_uniquenick_namespace_id( assert isinstance(Users.emailverified, Column) assert isinstance(Users.banned, Column) - with Session(ENGINE) as session: - result = ( - session.query( - Users.userid, - Profiles.profileid, - SubProfiles.subprofileid, - Profiles.nick, - Users.email, - SubProfiles.uniquenick, - Users.password, - Users.emailverified, - Users.banned, - SubProfiles.namespaceid, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where( - SubProfiles.uniquenick == unique_nick, - SubProfiles.namespaceid == namespace_id, - ) - .first() + result = ( + session.query( + Users.userid, + Profiles.profileid, + SubProfiles.subprofileid, + Profiles.nick, + Users.email, + SubProfiles.uniquenick, + Users.password, + Users.emailverified, + Users.banned, + SubProfiles.namespaceid, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where( + SubProfiles.uniquenick == unique_nick, + SubProfiles.namespaceid == namespace_id, ) + .first() + ) return result -def get_user_infos_by_nick_email(nick: str, email: str) -> LoginData | None: +def get_user_infos_by_nick_email( + nick: str, email: str, session: Session +) -> LoginData | None: if TYPE_CHECKING: assert isinstance(Profiles.profileid, Column) assert isinstance(Profiles.userid, Column) @@ -263,25 +266,24 @@ def get_user_infos_by_nick_email(nick: str, email: str) -> LoginData | None: assert isinstance(SubProfiles.uniquenick, Column) assert isinstance(SubProfiles.namespaceid, Column) - with Session(ENGINE) as session: - result = ( - session.query( - Users.userid, - Profiles.profileid, - SubProfiles.subprofileid, - Profiles.nick, - Users.email, - SubProfiles.uniquenick, - Users.password, - Users.emailverified, - Users.banned, - SubProfiles.namespaceid, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where(Users.email == email, Profiles.nick == nick) - .first() + result = ( + session.query( + Users.userid, + Profiles.profileid, + SubProfiles.subprofileid, + Profiles.nick, + Users.email, + SubProfiles.uniquenick, + Users.password, + Users.emailverified, + Users.banned, + SubProfiles.namespaceid, ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where(Users.email == email, Profiles.nick == nick) + .first() + ) data = { "user_id": result[0], "profile_id": result[1], @@ -300,15 +302,14 @@ def get_user_infos_by_nick_email(nick: str, email: str) -> LoginData | None: return None -def update_online_status(user_id: int, status: LoginStatus): +def update_online_status(user_id: int, status: LoginStatus, session: Session): if TYPE_CHECKING: assert isinstance(Users.userid, Column) raise NotImplementedError("implement sesskey") - with Session(ENGINE) as session: - result = session.query(Users).where(Users.userid == user_id).first() + result = session.query(Users).where(Users.userid == user_id).first() -def get_user_infos_by_authtoken(auth_token: str) -> LoginData | None: +def get_user_infos_by_authtoken(auth_token: str, session: Session) -> LoginData | None: if TYPE_CHECKING: assert isinstance(Profiles.profileid, Column) assert isinstance(Profiles.userid, Column) @@ -322,25 +323,24 @@ def get_user_infos_by_authtoken(auth_token: str) -> LoginData | None: assert isinstance(SubProfiles.namespaceid, Column) assert isinstance(SubProfiles.authtoken, Column) - with Session(ENGINE) as session: - result = ( - session.query( - Users.userid, - Profiles.profileid, - SubProfiles.subprofileid, - Profiles.nick, - Users.email, - SubProfiles.uniquenick, - Users.password, - Users.emailverified, - Users.banned, - SubProfiles.namespaceid, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where(SubProfiles.authtoken == auth_token) - .first() + result = ( + session.query( + Users.userid, + Profiles.profileid, + SubProfiles.subprofileid, + Profiles.nick, + Users.email, + SubProfiles.uniquenick, + Users.password, + Users.emailverified, + Users.banned, + SubProfiles.namespaceid, ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where(SubProfiles.authtoken == auth_token) + .first() + ) if result is not None: keys = [ @@ -361,16 +361,15 @@ def get_user_infos_by_authtoken(auth_token: str) -> LoginData | None: return None -def get_block_list(profile_id: int, namespace_id: int) -> list[int]: - with Session(ENGINE) as session: - result = ( - session.query(Blocked.targetid) - .where( - Blocked.namespaceid == namespace_id, - Blocked.profileid == profile_id, - ) - .all() +def get_block_list(profile_id: int, namespace_id: int, session: Session) -> list[int]: + result = ( + session.query(Blocked.targetid) + .where( + Blocked.namespaceid == namespace_id, + Blocked.profileid == profile_id, ) + .all() + ) if TYPE_CHECKING: result = cast(list[int], result) return result @@ -379,67 +378,65 @@ def get_block_list(profile_id: int, namespace_id: int) -> list[int]: # region Buddy -def get_buddy_list(profile_id: int, namespace_id: int) -> list[int]: - with Session(ENGINE) as session: - result = ( - session.query(Friends.targetid) - .where( - Blocked.namespaceid == namespace_id, - Blocked.profileid == profile_id, - ) - .all() +def get_buddy_list(profile_id: int, namespace_id: int, session: Session) -> list[int]: + result = ( + session.query(Friends.targetid) + .where( + Blocked.namespaceid == namespace_id, + Blocked.profileid == profile_id, ) + .all() + ) # assert isinstance(result, list) if TYPE_CHECKING: result = cast(list[int], result) return result -def update_block(profile_id: int, target_id: int, session_key: str) -> None: +def update_block( + profile_id: int, target_id: int, session_key: str, session: Session +) -> None: if TYPE_CHECKING: assert isinstance(SubProfiles.session_key, Column) - with Session(ENGINE) as session: - namespace_id = ( - session.query(SubProfiles) - .where(SubProfiles.session_key == session_key) - .first() - ) - result = ( - session.query(Blocked) - .where( - Blocked.targetid == target_id, - Blocked.namespaceid == namespace_id, - Blocked.profileid == profile_id, - ) - .count() + + namespace_id = ( + session.query(SubProfiles).where(SubProfiles.session_key == session_key).first() + ) + result = ( + session.query(Blocked) + .where( + Blocked.targetid == target_id, + Blocked.namespaceid == namespace_id, + Blocked.profileid == profile_id, ) - if result == 0: - b = Blocked( - targetid=target_id, namespaceid=namespace_id, profileid=profile_id - ) - session.add(b) - session.commit() - - -def update_friend_info(target_id: int, profile_id: int, namespace_id: int): - with Session(ENGINE) as session: - result = ( - session.query(Friends) - .where( - Friends.targetid == target_id, - Friends.namespaceid == namespace_id, - Friends.profileid == profile_id, - ) - .count() + .count() + ) + if result == 0: + b = Blocked(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) + session.add(b) + session.commit() + + +def update_friend_info( + target_id: int, profile_id: int, namespace_id: int, session: Session +): + result = ( + session.query(Friends) + .where( + Friends.targetid == target_id, + Friends.namespaceid == namespace_id, + Friends.profileid == profile_id, ) - f = Friends(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) + .count() + ) + f = Friends(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) - if result == 0: - session.add(f) - session.commit() + if result == 0: + session.add(f) + session.commit() -def add_nick_name(profile_id: int, old_nick: str, new_nick: str): +def add_nick_name(profile_id: int, old_nick: str, new_nick: str, session: Session): assert isinstance(profile_id, int) assert isinstance(old_nick, str) assert isinstance(new_nick, str) @@ -449,18 +446,18 @@ def add_nick_name(profile_id: int, old_nick: str, new_nick: str): assert isinstance(Users.userid, Column) assert isinstance(Users.email, Column) assert isinstance(Profiles.nick, Column) - with Session(ENGINE) as session: - result = ( - session.query(Profiles) - .where(Profiles.profileid == profile_id, Profiles.nick == old_nick) - .first() - ) - if result is None: - raise GPDatabaseException("No user infomation found in database.") + result = ( + session.query(Profiles) + .where(Profiles.profileid == profile_id, Profiles.nick == old_nick) + .first() + ) - result.nick = new_nick # type:ignore - session.commit() + if result is None: + raise GPDatabaseException("No user infomation found in database.") + + result.nick = new_nick # type:ignore + session.commit() # def update_profile_info(profile: Profiles): @@ -468,58 +465,55 @@ def add_nick_name(profile_id: int, old_nick: str, new_nick: str): # session.commit() -def update_unique_nick(subprofile_id: int, unique_nick: str): - with Session(ENGINE) as session: - result = ( - session.query(SubProfiles) - .where(SubProfiles.subprofileid == subprofile_id) - .first() - ) - result.uniquenick = unique_nick # type:ignore - session.commit() +def update_unique_nick(subprofile_id: int, unique_nick: str, session: Session): + result = ( + session.query(SubProfiles) + .where(SubProfiles.subprofileid == subprofile_id) + .first() + ) + result.uniquenick = unique_nick # type:ignore + session.commit() -def update_subprofile_info(subprofile: SubProfiles): - with Session(ENGINE) as session: - session.add(subprofile) - session.commit() +def update_subprofile_info(subprofile: SubProfiles,session:Session): + session.add(subprofile) + session.commit() def add_friend_request( - profileid: int, targetid: int, namespace_id: int, reason: str + profileid: int, targetid: int, namespace_id: int, reason: str,session:Session ) -> None: - with Session(ENGINE) as session: - data = ( - session.query(FriendAddRequest) - .where( - FriendAddRequest.profileid == profileid, - FriendAddRequest.targetid == targetid, - FriendAddRequest.namespaceid == namespace_id, - ) - .first() - ) - if data is not None: - raise GPAddBuddyException("Request is existed, add friend ignored") - request = FriendAddRequest( - profileid=profileid, - targetid=targetid, - namespaceid=namespace_id, - reason=reason, + data = ( + session.query(FriendAddRequest) + .where( + FriendAddRequest.profileid == profileid, + FriendAddRequest.targetid == targetid, + FriendAddRequest.namespaceid == namespace_id, ) - session.add(request) - session.commit() + .first() + ) + if data is not None: + raise GPAddBuddyException("Request is existed, add friend ignored") + request = FriendAddRequest( + profileid=profileid, + targetid=targetid, + namespaceid=namespace_id, + reason=reason, + ) + session.add(request) + session.commit() -def get_status(session_key: str) -> dict: +def get_status(session_key: str,session:Session) -> dict: if TYPE_CHECKING: assert isinstance(SubProfiles.session_key, Column) - with Session(ENGINE) as session: - result = ( - session.query(Profiles) - .join(SubProfiles) - .where(SubProfiles.session_key == session_key) - .first() - ) + + result = ( + session.query(Profiles) + .join(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) if result is None: raise GPStatusException("No profile found with the provided session key") @@ -541,89 +535,76 @@ def update_status( current_status: GPStatusCode, location_string: str, status_string: str, + session:Session ): if TYPE_CHECKING: assert isinstance(SubProfiles.session_key, Column) - with Session(ENGINE) as session: - result = ( - session.query(Profiles) - .join(SubProfiles) - .where(SubProfiles.session_key == session_key) - .first() - ) - if result is None: - raise GPStatusException("No profile found with the provided session key") - result.statstring = status_string - result.status = current_status - assert isinstance(result.extra_info, list) - result.extra_info.append(location_string) + result = ( + session.query(Profiles) + .join(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) + if result is None: + raise GPStatusException("No profile found with the provided session key") + + result.statstring = status_string + result.status = current_status + assert isinstance(result.extra_info, list) + result.extra_info.append(location_string) - session.commit() + session.commit() -def update_new_nick(session_key: str, old_nick: str, new_nick: str): - with Session(ENGINE) as session: - result = ( - session.query(Profiles) - .join(SubProfiles) - .where(SubProfiles.session_key == session_key) - .first() - ) - if result.nick == old_nick and result.nick != new_nick: - result.nick = new_nick - session.commit() +def update_new_nick(session_key: str, old_nick: str, new_nick: str,session:Session): + result = ( + session.query(Profiles) + .join(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) + if result.nick == old_nick and result.nick != new_nick: + result.nick = new_nick + session.commit() -def update_cdkey(session_key: str, cdkey: str): - with Session(ENGINE) as session: - subprofile = ( - session.query(SubProfiles) - .where(SubProfiles.session_key == session_key) - .first() - ) - if subprofile is None: - raise GPDatabaseException( - f"no subprofile found with session key:{session_key}" - ) +def update_cdkey(session_key: str, cdkey: str,session:Session): + subprofile = ( + session.query(SubProfiles).where(SubProfiles.session_key == session_key).first() + ) + if subprofile is None: + raise GPDatabaseException(f"no subprofile found with session key:{session_key}") - subprofile.cdkeyenc = cdkey + subprofile.cdkeyenc = cdkey - session.commit() + session.commit() -def update_uniquenick(session_key: str, uniquenick: str): - with Session(ENGINE) as session: - subprofile = ( - session.query(SubProfiles) - .where(SubProfiles.session_key == session_key) - .first() - ) - if subprofile is None: - raise GPDatabaseException( - f"no subprofile found with session key:{session_key}" - ) +def update_uniquenick(session_key: str, uniquenick: str,session:Session): + subprofile = ( + session.query(SubProfiles).where(SubProfiles.session_key == session_key).first() + ) + if subprofile is None: + raise GPDatabaseException(f"no subprofile found with session key:{session_key}") - subprofile.uniquenick = uniquenick - session.commit() + subprofile.uniquenick = uniquenick + session.commit() -def update_profiles(session_key: str, extra_info: dict): - with Session(ENGINE) as session: - profile = ( - session.query(Profiles) - .join(SubProfiles) - .where(SubProfiles.session_key == session_key) - .first() - ) - if profile is None: - raise GPDatabaseException( - f"no profile found with session key:{session_key}" - ) - for key, value in extra_info.items(): - profile.extra_info[key] = value +def update_profiles(session_key: str, extra_info: dict,session:Session): + profile = ( + session.query(Profiles) + .join(SubProfiles) + .where(SubProfiles.session_key == session_key) + .first() + ) + if profile is None: + raise GPDatabaseException(f"no profile found with session key:{session_key}") + for key, value in extra_info.items(): + profile.extra_info[key] = value - session.commit() + session.commit() def update_user(session_key): diff --git a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py index 9cb79cf34..42a3ea6c4 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py @@ -41,7 +41,9 @@ class KeepAliveHandler(HandlerBase): _request: KeepAliveRequest def _data_operate(self) -> None: - data.update_online_time(self._request.client_ip, self._request.client_port) + data.update_online_time( + self._request.client_ip, self._request.client_port, self._session + ) class LoginHandler(HandlerBase): @@ -64,19 +66,21 @@ def _nick_email_login(self) -> None: if not is_exsit: raise GPLoginBadEmailException(f"email: {self._request.email} is invalid.") self._data = data.get_user_infos_by_nick_email( - self._request.nick, self._request.email + self._request.nick, self._request.email, self._session ) def _unique_nick_login(self) -> None: assert self._request.unique_nick is not None assert self._request.namespace_id is not None self._data = data.get_user_infos_by_uniquenick_namespace_id( - self._request.unique_nick, self._request.namespace_id + self._request.unique_nick, self._request.namespace_id, self._session ) def _auth_token_login(self) -> None: assert self._request.auth_token is not None - self._data = data.get_user_infos_by_authtoken(self._request.auth_token) + self._data = data.get_user_infos_by_authtoken( + self._request.auth_token, self._session + ) def _result_construct(self) -> None: if self._data is None: @@ -106,7 +110,7 @@ class BuddyListHandler(HandlerBase): def _data_operate(self) -> None: self.data = data.get_buddy_list( - self._request.profile_id, self._request.namespace_id + self._request.profile_id, self._request.namespace_id, self._session ) def _result_construct(self) -> None: @@ -118,7 +122,7 @@ class BlockListHandler(HandlerBase): def _data_operate(self) -> None: self.data = data.get_block_list( - self._request.profile_id, self._request.namespace_id + self._request.profile_id, self._request.namespace_id, self._session ) def _result_construct(self) -> None: @@ -143,7 +147,9 @@ class DelBuddyHandler(HandlerBase): _request: DelBuddyRequest def _data_operate(self) -> None: - self.data = data.delete_friend_by_profile_id(self._request.target_id) + self.data = data.delete_friend_by_profile_id( + self._request.target_id, self._session + ) class AddBuddyHandler(HandlerBase): @@ -155,6 +161,7 @@ def _data_operate(self) -> None: self._request.target_id, self._request.namespace_id, self._request.reason, + self._session, ) @@ -163,7 +170,10 @@ class AddBlockHandler(HandlerBase): def _data_operate(self) -> None: data.update_block( - self._request.profile_id, self._request.taget_id, self._request.session_key + self._request.profile_id, + self._request.taget_id, + self._request.session_key, + self._session, ) @@ -194,6 +204,7 @@ def _data_operate(self) -> None: self._request.current_status, self._request.location_string, self._request.status_string, + self._session, ) @@ -212,7 +223,9 @@ class GetProfileHandler(HandlerBase): def _data_operate(self) -> None: self.data = data.get_profile_infos( - profile_id=self._request.profile_id, session_key=self._request.session_key + profile_id=self._request.profile_id, + session_key=self._request.session_key, + session=self._session, ) def _result_construct(self) -> None: @@ -228,7 +241,10 @@ class NewProfileHandler(HandlerBase): def _data_operate(self) -> None: data.update_new_nick( - self._request.session_key, self._request.old_nick, self._request.new_nick + self._request.session_key, + self._request.old_nick, + self._request.new_nick, + self._session, ) @@ -236,7 +252,9 @@ class RegisterCDKeyHandler(HandlerBase): _request: RegisterCDKeyRequest def _data_operate(self): - data.update_cdkey(self._request.session_key, self._request.cdkey_enc) + data.update_cdkey( + self._request.session_key, self._request.cdkey_enc, self._session + ) class RegisterNickHandler(HandlerBase): @@ -247,7 +265,9 @@ class RegisterNickHandler(HandlerBase): _request: RegisterNickRequest def _data_operate(self): - data.update_uniquenick(self._request.session_key, self._request.unique_nick) + data.update_uniquenick( + self._request.session_key, self._request.unique_nick, self._session + ) class RemoveBlockHandler(HandlerBase): @@ -259,11 +279,15 @@ class UpdateProfileHandler(HandlerBase): _request: UpdateProfileRequest def _data_operate(self): - data.update_profiles(self._request.session_key, self._request.extra_infos) + data.update_profiles( + self._request.session_key, self._request.extra_infos, self._session + ) class UpdateUserInfoHandler(HandlerBase): _request: UpdateUserInfoRequest def _data_operate(self): - data.update_profiles(self._request.session_key, self._request.extra_infos) + data.update_profiles( + self._request.session_key, self._request.extra_infos, self._session + ) diff --git a/src/backends/protocols/gamespy/presence_search_player/data.py b/src/backends/protocols/gamespy/presence_search_player/data.py index 64b487fbb..065ab7ad0 100644 --- a/src/backends/protocols/gamespy/presence_search_player/data.py +++ b/src/backends/protocols/gamespy/presence_search_player/data.py @@ -5,152 +5,144 @@ Profiles, SubProfiles, Users, - ENGINE, ) from sqlalchemy.orm import Session -def db_commit() -> None: - with Session(ENGINE) as session: - session.commit() +def db_commit(session: Session) -> None: + session.commit() -def verify_email(email: str): + +def verify_email(email: str, session: Session): assert isinstance(email, str) - with Session(ENGINE) as session: - if session.query(Users).where(Users.email == email).count() == 1: - return True - else: - return False + + if session.query(Users).where(Users.email == email).count() == 1: + return True + else: + return False -def verify_email_and_password(email: str, password: str): +def verify_email_and_password(email: str, password: str, session: Session): assert isinstance(email, str) assert isinstance(password, str) - with Session(ENGINE) as session: - result = ( - session.query(Users) - .where(Users.email == email, Users.password == password) - .count() - ) + + result = ( + session.query(Users) + .where(Users.email == email, Users.password == password) + .count() + ) if result == 1: return True return False def get_profile_id( - email: str, password: str, nick_name: str, partner_id: int + email: str, password: str, nick_name: str, partner_id: int, session: Session ) -> int | None: - with Session(ENGINE) as session: - result = ( - session.query(Profiles.profileid) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where( - Users.email == email, - Users.password == password, - Profiles.nick == nick_name, - SubProfiles.partnerid == partner_id, - ) - .first() + result = ( + session.query(Profiles.profileid) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where( + Users.email == email, + Users.password == password, + Profiles.nick == nick_name, + SubProfiles.partnerid == partner_id, ) + .first() + ) if result is not None: result = result[0] assert isinstance(result, int) return result -def add_user(user: Users): - with Session(ENGINE) as session: - session.add(user) - session.commit() +def add_user(user: Users, session: Session): + session.add(user) + session.commit() -def add_profile(profile: Profiles): - with Session(ENGINE) as session: - session.add(profile) - session.commit() +def add_profile(profile: Profiles, session: Session): + session.add(profile) + session.commit() -def add_sub_profile(subprofile: SubProfiles): - with Session(ENGINE) as session: - session.add(subprofile) - session.commit() +def add_sub_profile(subprofile: SubProfiles, session: Session): + session.add(subprofile) + session.commit() -def update_user(user: Users): - with Session(ENGINE) as session: - session.merge(user) - session.commit() +def update_user(user: Users, session: Session): + session.merge(user) + session.commit() -def update_profile(profile: Profiles): - with Session(ENGINE) as session: - session.merge(profile) - session.commit() +def update_profile(profile: Profiles, session: Session): + session.merge(profile) + session.commit() -def update_subprofile(subprofile: SubProfiles): - with Session(ENGINE) as session: - session.merge(subprofile) - session.commit() +def update_subprofile(subprofile: SubProfiles, session: Session): + session.merge(subprofile) + session.commit() -def get_user(email: str) -> Users | None: +def get_user(email: str, session: Session) -> Users | None: assert isinstance(email, str) - with Session(ENGINE) as session: - result = session.query(Users).where(Users.email == email).first() + + result = session.query(Users).where(Users.email == email).first() return result -def get_profile(user_id: int, nick_name: str) -> Profiles | None: +def get_profile(user_id: int, nick_name: str, session: Session) -> Profiles | None: assert isinstance(user_id, int) assert isinstance(nick_name, str) - with Session(ENGINE) as session: - result = ( - session.query(Profiles) - .where(Profiles.userid == user_id, Profiles.nick == nick_name) - .first() - ) + + result = ( + session.query(Profiles) + .where(Profiles.userid == user_id, Profiles.nick == nick_name) + .first() + ) return result def get_sub_profile( - profile_id: int, namespace_id: int, product_id: int + profile_id: int, namespace_id: int, product_id: int, session: Session ) -> SubProfiles | None: assert isinstance(profile_id, int) assert isinstance(namespace_id, int) assert isinstance(product_id, int) - with Session(ENGINE) as session: - result = ( - session.query(SubProfiles) - .where( - SubProfiles.profileid == profile_id, - SubProfiles.namespaceid == namespace_id, - SubProfiles.namespaceid == product_id, - ) - .first() + + result = ( + session.query(SubProfiles) + .where( + SubProfiles.profileid == profile_id, + SubProfiles.namespaceid == namespace_id, + SubProfiles.namespaceid == product_id, ) + .first() + ) return result def get_nick_and_unique_nick_list( - email: str, password: str, namespace_id: int + email: str, password: str, namespace_id: int, session: Session ) -> list[tuple[str, str]]: """ return [(nick, uniquenick)] """ - with Session(ENGINE) as session: - result = ( - session.query(Profiles.nick, SubProfiles.uniquenick) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where( - Users.email == email, - Users.password == password, - SubProfiles.namespaceid == namespace_id, - ) - .all() + + result = ( + session.query(Profiles.nick, SubProfiles.uniquenick) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where( + Users.email == email, + Users.password == password, + SubProfiles.namespaceid == namespace_id, ) + .all() + ) assert isinstance(result, list) data = [] for r in result: @@ -159,35 +151,37 @@ def get_nick_and_unique_nick_list( return data -def get_friend_info_list(profile_id: int, namespace_id: int, game_name: str) -> list: +def get_friend_info_list( + profile_id: int, namespace_id: int, game_name: str, session: Session +) -> list: """ return [(profileid, nick, uniquenick, lastname, firstname, userid, email)] """ - with Session(ENGINE) as session: - result = ( - session.query( - Profiles.profileid, - Profiles.nick, - SubProfiles.uniquenick, - Users.userid, - Users.email, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .join(Friends, Profiles.profileid == Friends.friendid) - # todo check whether friends table join is correct - .where( - Friends.profileid == profile_id, - SubProfiles.namespaceid == namespace_id, - SubProfiles.gamename == game_name, - ) - .all() + + result = ( + session.query( + Profiles.profileid, + Profiles.nick, + SubProfiles.uniquenick, + Users.userid, + Users.email, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .join(Friends, Profiles.profileid == Friends.friendid) + # todo check whether friends table join is correct + .where( + Friends.profileid == profile_id, + SubProfiles.namespaceid == namespace_id, + SubProfiles.gamename == game_name, ) + .all() + ) return result def get_matched_profile_info_list( - profile_ids: list[int], namespace_id: int + profile_ids: list[int], namespace_id: int, session: Session ) -> list[tuple[int, str]]: """ return [(profileid,uniquenick)] @@ -197,39 +191,36 @@ def get_matched_profile_info_list( assert isinstance(SubProfiles.profileid, Column) assert isinstance(SubProfiles.uniquenick, Column) assert isinstance(SubProfiles.namespaceid, Column) - with Session(ENGINE) as session: - result = ( - session.query(SubProfiles.profileid, SubProfiles.uniquenick) - .where( - SubProfiles.profileid.in_(profile_ids), - SubProfiles.namespaceid == namespace_id, - ) - .all() + + result = ( + session.query(SubProfiles.profileid, SubProfiles.uniquenick) + .where( + SubProfiles.profileid.in_(profile_ids), + SubProfiles.namespaceid == namespace_id, ) + .all() + ) data = [] for r in result: data.append(tuple(r)) return data -def get_matched_info_by_nick( - nick_name: str, -) -> list[dict]: - with Session(ENGINE) as session: - result = ( - session.query( - Users.email, - Profiles.profileid, - Profiles.nick, - SubProfiles.uniquenick, - SubProfiles.namespaceid, - Profiles.extra_info, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where(Profiles.nick == nick_name) - .all() +def get_matched_info_by_nick(nick_name: str, session: Session) -> list[dict]: + result = ( + session.query( + Users.email, + Profiles.profileid, + Profiles.nick, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + Profiles.extra_info, ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where(Profiles.nick == nick_name) + .all() + ) temp: list[dict] = [] for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: if TYPE_CHECKING: @@ -250,24 +241,21 @@ def get_matched_info_by_nick( return temp -def get_matched_info_by_email( - email: str, -) -> list[dict]: - with Session(ENGINE) as session: - result = ( - session.query( - Users.email, - Profiles.profileid, - Profiles.nick, - SubProfiles.uniquenick, - SubProfiles.namespaceid, - Profiles.extra_info, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where(Users.email == email) - .all() +def get_matched_info_by_email(email: str, session: Session) -> list[dict]: + result = ( + session.query( + Users.email, + Profiles.profileid, + Profiles.nick, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + Profiles.extra_info, ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where(Users.email == email) + .all() + ) temp: list[dict] = [] for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: if TYPE_CHECKING: @@ -287,22 +275,23 @@ def get_matched_info_by_email( return temp -def get_matched_info_by_nick_and_email(nick_name: str, email: str) -> list[dict]: - with Session(ENGINE) as session: - result = ( - session.query( - Users.email, - Profiles.profileid, - Profiles.nick, - SubProfiles.uniquenick, - SubProfiles.namespaceid, - Profiles.extra_info, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where(Users.email == email, Profiles.nick == nick_name) - .all() +def get_matched_info_by_nick_and_email( + nick_name: str, email: str, session: Session +) -> list[dict]: + result = ( + session.query( + Users.email, + Profiles.profileid, + Profiles.nick, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + Profiles.extra_info, ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where(Users.email == email, Profiles.nick == nick_name) + .all() + ) data: list[dict] = [] for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: if TYPE_CHECKING: @@ -323,25 +312,24 @@ def get_matched_info_by_nick_and_email(nick_name: str, email: str) -> list[dict] def get_matched_info_by_uniquenick_and_namespaceid( - unique_nick: str, namespace_id: int + unique_nick: str, namespace_id: int, session: Session ) -> list[dict]: - with Session(ENGINE) as session: - result = ( - session.query( - Profiles.profileid, - Profiles.nick, - SubProfiles.uniquenick, - SubProfiles.namespaceid, - Profiles.extra_info, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where( - SubProfiles.uniquenick == unique_nick, - SubProfiles.namespaceid == namespace_id, - ) - .all() + result = ( + session.query( + Profiles.profileid, + Profiles.nick, + SubProfiles.uniquenick, + SubProfiles.namespaceid, + Profiles.extra_info, + ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where( + SubProfiles.uniquenick == unique_nick, + SubProfiles.namespaceid == namespace_id, ) + .all() + ) data: list[dict] = [] for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: if TYPE_CHECKING: @@ -363,24 +351,23 @@ def get_matched_info_by_uniquenick_and_namespaceid( def get_matched_info_by_uniquenick_and_namespaceids( - unique_nick: str, namespace_ids: list[int] + unique_nick: str, namespace_ids: list[int], session: Session ) -> list[dict]: - with Session(ENGINE) as session: - result = ( - session.query( - Profiles.profileid, - Profiles.nick, - SubProfiles.uniquenick, - SubProfiles.namespaceid, - ) - .join(Users, Profiles.userid == Users.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where( - SubProfiles.uniquenick == unique_nick, - SubProfiles.namespaceid.in_(namespace_ids), - ) - .all() + result = ( + session.query( + Profiles.profileid, + Profiles.nick, + SubProfiles.uniquenick, + SubProfiles.namespaceid, ) + .join(Users, Profiles.userid == Users.userid) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where( + SubProfiles.uniquenick == unique_nick, + SubProfiles.namespaceid.in_(namespace_ids), + ) + .all() + ) data: list[dict] = [] for email, profile_id, nick, uniquenick, namespace_id, extra_info in result: if TYPE_CHECKING: @@ -401,18 +388,19 @@ def get_matched_info_by_uniquenick_and_namespaceids( return data -def is_uniquenick_exist(unique_nick: str, namespace_id: int, game_name: str) -> bool: - with Session(ENGINE) as session: - result = ( - session.query(Profiles) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where( - SubProfiles.uniquenick == unique_nick, - SubProfiles.gamename == game_name, - SubProfiles.namespaceid == namespace_id, - ) - .count() +def is_uniquenick_exist( + unique_nick: str, namespace_id: int, game_name: str, session: Session +) -> bool: + result = ( + session.query(Profiles) + .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) + .where( + SubProfiles.uniquenick == unique_nick, + SubProfiles.gamename == game_name, + SubProfiles.namespaceid == namespace_id, ) + .count() + ) if result == 0: return False @@ -420,12 +408,12 @@ def is_uniquenick_exist(unique_nick: str, namespace_id: int, game_name: str) -> return True -def is_email_exist(email: str) -> bool: +def is_email_exist(email: str, session: Session) -> bool: if TYPE_CHECKING: Users.userid = cast(Column, Users.userid) Users.email = cast(Column, Users.email) - with Session(ENGINE) as session: - result = session.query(Users.userid).where(Users.email == email).count() + + result = session.query(Users.userid).where(Users.email == email).count() # According to game partnerid is not nessesary if result == 0: return False diff --git a/src/backends/protocols/gamespy/presence_search_player/handlers.py b/src/backends/protocols/gamespy/presence_search_player/handlers.py index b840a41a6..c10d1c0a5 100644 --- a/src/backends/protocols/gamespy/presence_search_player/handlers.py +++ b/src/backends/protocols/gamespy/presence_search_player/handlers.py @@ -1,5 +1,5 @@ from backends.library.abstractions.handler_base import HandlerBase -from backends.library.database.pg_orm import ENGINE, Users, Profiles, SubProfiles +from backends.library.database.pg_orm import Users, Profiles, SubProfiles import backends.protocols.gamespy.presence_search_player.data as data from backends.protocols.gamespy.presence_search_player.requests import ( CheckRequest, @@ -34,8 +34,6 @@ UniqueSearchResult, ValidResult, ) -from sqlalchemy.orm import Session - class CheckHandler(HandlerBase): """ @@ -46,10 +44,10 @@ class CheckHandler(HandlerBase): _result: CheckResult def _data_operate(self) -> None: - if not data.verify_email(self._request.email): + if not data.verify_email(self._request.email, self._session): raise CheckException("The email is not existed") if not data.verify_email_and_password( - self._request.email, self._request.password + self._request.email, self._request.password, self._session ): raise CheckException("The password is incorrect") self._profile_id = data.get_profile_id( @@ -57,6 +55,7 @@ def _data_operate(self) -> None: self._request.password, self._request.nick, self._request.partner_id, + self._session, ) if self._profile_id is None: raise CheckException(f"No pid found with email{self._request.email}") @@ -72,14 +71,16 @@ class NewUserHandler(HandlerBase): def _data_operate(self) -> None: # check if user exist - self.user = data.get_user(self._request.email) + self.user = data.get_user(self._request.email, self._session) if self.user is None: self._create_user() assert self.user assert isinstance(self.user.userid, int) - self.profile = data.get_profile(self.user.userid, self._request.nick) + self.profile = data.get_profile( + self.user.userid, self._request.nick, self._session + ) if self.profile is None: self._create_profile() assert self.profile is not None @@ -88,6 +89,7 @@ def _data_operate(self) -> None: profile_id=self.profile.profileid, namespace_id=self._request.namespace_id, product_id=self._request.product_id, + session=self._session, ) if self.subprofile is None: self._create_subprofile() @@ -107,9 +109,8 @@ def _create_user(self) -> None: if key in Users.__dict__: user_dict[key] = value self.user = Users(**user_dict) - with Session(ENGINE) as session: - session.add(self.user) - session.commit() + self._session.add(self.user) + self._session.commit() def _create_profile(self) -> None: profile_dict = {} @@ -121,9 +122,8 @@ def _create_profile(self) -> None: assert isinstance(self.user.userid, int) profile_dict["userid"] = self.user.userid self.profile = Profiles(**profile_dict) - with Session(ENGINE) as session: - session.add(self.profile) - session.commit() + self._session.add(self.profile) + self._session.commit() def _create_subprofile(self) -> None: subprofile_dict = {} @@ -133,9 +133,8 @@ def _create_subprofile(self) -> None: assert self.profile is not None subprofile_dict["profileid"] = self.profile.profileid self.subprofile = SubProfiles(**subprofile_dict) - with Session(ENGINE) as session: - session.add(self.subprofile) - session.commit() + self._session.add(self.subprofile) + self._session.commit() class NicksHandler(HandlerBase): @@ -144,7 +143,10 @@ class NicksHandler(HandlerBase): def _data_operate(self) -> None: self.temp_list = data.get_nick_and_unique_nick_list( - self._request.email, self._request.password, self._request.namespace_id + self._request.email, + self._request.password, + self._request.namespace_id, + self._session, ) self.result_data = [] for nick, unique in self.temp_list: @@ -163,6 +165,7 @@ def _data_operate(self) -> None: self._request.profile_id, self._request.namespace_id, self._request.game_name, + self._session, ) def _result_construct(self) -> None: @@ -188,7 +191,7 @@ class OthersListHandler(HandlerBase): def _data_operate(self) -> None: self._data: list = data.get_matched_profile_info_list( - self._request.profile_ids, self._request.namespace_id + self._request.profile_ids, self._request.namespace_id, self._session ) def _result_construct(self) -> None: @@ -209,21 +212,25 @@ class SearchHandler(HandlerBase): def _data_operate(self) -> None: if self._request.request_type == SearchType.NICK_SEARCH: assert self._request.nick - self._data = data.get_matched_info_by_nick(self._request.nick) + self._data = data.get_matched_info_by_nick( + self._request.nick, self._session + ) elif self._request.request_type == SearchType.NICK_EMAIL_SEARCH: assert self._request.email assert self._request.nick self._data = data.get_matched_info_by_nick_and_email( - self._request.nick, self._request.email + self._request.nick, self._request.email, self._session ) elif self._request.request_type == SearchType.UNIQUENICK_NAMESPACEID_SEARCH: assert self._request.uniquenick self._data = data.get_matched_info_by_uniquenick_and_namespaceid( - self._request.uniquenick, self._request.namespace_id + self._request.uniquenick, self._request.namespace_id, self._session ) elif self._request.request_type == SearchType.EMAIL_SEARCH: assert self._request.email - self._data = data.get_matched_info_by_email(self._request.email) + self._data = data.get_matched_info_by_email( + self._request.email, self._session + ) else: raise UniSpyException("search type invalid") @@ -241,7 +248,7 @@ class SearchUniqueHandler(HandlerBase): def _data_operate(self) -> None: self._data = data.get_matched_info_by_uniquenick_and_namespaceids( - self._request.uniquenick, self._request.namespace_ids + self._request.uniquenick, self._request.namespace_ids, self._session ) def _result_construct(self) -> None: @@ -261,6 +268,7 @@ def _data_operate(self) -> None: self._request.preferred_nick, self._request.namespace_id, self._request.game_name, + self._session, ) def _result_construct(self) -> None: @@ -272,7 +280,7 @@ class ValidHandler(HandlerBase): _result: ValidResult def _data_operate(self) -> None: - self._is_exist = data.is_email_exist(self._request.email) + self._is_exist = data.is_email_exist(self._request.email, self._session) def _result_construct(self) -> None: self._result = ValidResult(is_account_valid=self._is_exist) diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index 75ad7c150..4cee0c36d 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -1,4 +1,4 @@ -from typing import TYPE_CHECKING, Optional, cast +from typing import TYPE_CHECKING, cast from backends.library.database.pg_orm import ( ENGINE, ChatChannelCaches, @@ -13,9 +13,6 @@ from frontends.gamespy.protocols.query_report.aggregates.peer_room_info import ( PeerRoomInfo, ) -from frontends.gamespy.protocols.server_browser.aggregates.exceptions import ( - ServerBrowserException, -) from sqlalchemy.orm import Session @@ -52,19 +49,20 @@ def get_all_groups() -> dict: PEER_GROUP_LIST = get_all_groups() -def get_peer_staging_channels(game_name: str, group_id: int) -> list[GameServerInfo]: +def get_peer_staging_channels( + game_name: str, group_id: int, session: Session +) -> list[GameServerInfo]: """ todo check where use this function """ assert isinstance(game_name, str) assert isinstance(group_id, int) staging_name = f"{PeerRoom.StagingRoomPrefix}!{game_name}!*" - with Session(ENGINE) as session: - result = ( - session.query(ChatChannelCaches) - .where(ChatChannelCaches.channel_name == staging_name) - .all() - ) + result = ( + session.query(ChatChannelCaches) + .where(ChatChannelCaches.channel_name == staging_name) + .all() + ) data = [] for s in result: t = {k: v for k, v in s.__dict__.items() if k != "_sa_instance_state"} @@ -82,7 +80,9 @@ def get_group_data_list_by_gamename(game_name: str) -> list[dict]: return result -def get_peer_group_channel(group_data: list[dict]) -> list[PeerRoomInfo]: +def get_peer_group_channel( + group_data: list[dict], session: Session +) -> list[PeerRoomInfo]: assert isinstance(group_data, list) and all( isinstance(id, dict) for id in group_data ) @@ -92,12 +92,11 @@ def get_peer_group_channel(group_data: list[dict]) -> list[PeerRoomInfo]: ] # Query the database for channels matching the constructed group names - with Session(ENGINE) as session: - result = ( - session.query(ChatChannelCaches) - .filter(ChatChannelCaches.channel_name.in_(group_names)) - .all() - ) + result = ( + session.query(ChatChannelCaches) + .filter(ChatChannelCaches.channel_name.in_(group_names)) + .all() + ) # Convert the result to a list of PeerRoomInfo objects data = [PeerRoomInfo(**s.__dict__) for s in result] @@ -105,46 +104,48 @@ def get_peer_group_channel(group_data: list[dict]) -> list[PeerRoomInfo]: return data -def get_server_info_with_instant_key(instant_key: str) -> GameServerCaches | None: +def get_server_info_with_instant_key( + instant_key: str, session: Session +) -> GameServerCaches | None: assert isinstance(instant_key, str) - with Session(ENGINE) as session: - result = ( - session.query(GameServerCaches) - .where(GameServerCaches.instant_key == instant_key) - .first() - ) + result = ( + session.query(GameServerCaches) + .where(GameServerCaches.instant_key == instant_key) + .first() + ) return result -def get_server_info_with_game_name(game_name: str) -> GameServerCaches | None: +def get_server_info_with_game_name( + game_name: str, session: Session +) -> GameServerCaches | None: assert isinstance(game_name, str) - with Session(ENGINE) as session: - result = ( - session.query(GameServerCaches) - .where(GameServerCaches.game_name == game_name) - .first() - ) + result = ( + session.query(GameServerCaches) + .where(GameServerCaches.game_name == game_name) + .first() + ) return result -def get_server_info_list_with_game_name(game_name: str) -> list[GameServerInfo]: - with Session(ENGINE) as session: - result = ( - session.query(GameServerCaches) - .where(GameServerCaches.game_name == game_name) - .all() - ) +def get_server_info_list_with_game_name( + game_name: str, session: Session +) -> list[GameServerInfo]: + result = ( + session.query(GameServerCaches) + .where(GameServerCaches.game_name == game_name) + .all() + ) data = [] for s in result: data.append(GameServerInfo(**s.__dict__)) return data -def get_server_info_with_ip_and_port(ip: str, port: int) -> GameServerInfo | None: +def get_server_info_with_ip_and_port(ip: str, port: int,session:Session) -> GameServerInfo | None: assert isinstance(ip, str) assert isinstance(port, int) - with Session(ENGINE) as session: - result = ( + result = ( session.query(GameServerCaches) .where( GameServerCaches.host_ip_address == ip, @@ -158,22 +159,20 @@ def get_server_info_with_ip_and_port(ip: str, port: int) -> GameServerInfo | Non return data -def remove_server_info(info: GameServerCaches) -> None: - with Session(ENGINE) as session: - session.delete(info) - session.commit() +def remove_server_info(info: GameServerCaches,session:Session) -> None: + session.delete(info) + session.commit() # todo finish the GameServerCaches creation -def create_game_server(info: GameServerCaches) -> None: - with Session(ENGINE) as session: - session.add(info) - update_game_server() +def create_game_server(info: GameServerCaches,session:Session) -> None: + session.add(info) + update_game_server(session) -def update_game_server() -> None: +def update_game_server(session:Session) -> None: # info.update_time = datetime.now() # type:ignore with Session(ENGINE) as session: session.commit() diff --git a/src/backends/protocols/gamespy/query_report/handlers.py b/src/backends/protocols/gamespy/query_report/handlers.py index 50fc5d3e7..6c93ea77d 100644 --- a/src/backends/protocols/gamespy/query_report/handlers.py +++ b/src/backends/protocols/gamespy/query_report/handlers.py @@ -37,7 +37,9 @@ class Heartbeathandler(HandlerBase): _request: HeartBeatRequest def _data_operate(self) -> None: - cache = data.get_server_info_with_instant_key(str(self._request.instant_key)) + cache = data.get_server_info_with_instant_key( + str(self._request.instant_key), self._session + ) if cache is None: cache = GameServerCaches( instant_key=self._request.instant_key, @@ -52,7 +54,7 @@ def _data_operate(self) -> None: team_data=self._request.team_data, avaliable=True, ) - data.create_game_server(cache) + data.create_game_server(cache, self._session) else: cache.instant_key = self._request.instant_key # type: ignore cache.server_id = self._request.server_id # type: ignore @@ -65,7 +67,7 @@ def _data_operate(self) -> None: cache.server_data = self._request.server_data # type: ignore cache.team_data = self._request.team_data # type: ignore cache.avaliable = True # type: ignore - data.update_game_server() + data.update_game_server(self._session) class KeepAliveHandler(HandlerBase): diff --git a/src/backends/protocols/gamespy/server_browser/handlers.py b/src/backends/protocols/gamespy/server_browser/handlers.py index 6a82902a4..24a06aac7 100644 --- a/src/backends/protocols/gamespy/server_browser/handlers.py +++ b/src/backends/protocols/gamespy/server_browser/handlers.py @@ -31,7 +31,9 @@ class ServerNetworkInfoListHandler(HandlerBase): _caches: list[GameServerInfo] def _data_operate(self): - self._caches = data.get_server_info_list_with_game_name(self._request.game_name) + self._caches = data.get_server_info_list_with_game_name( + self._request.game_name, self._session + ) def _result_construct(self): assert isinstance(self._caches, list) and all( @@ -53,7 +55,7 @@ class P2PGroupRoomListHandler(HandlerBase): def _data_operate(self): group_data = data.get_group_data_list_by_gamename(self._request.game_name) - self._caches = data.get_peer_group_channel(group_data) + self._caches = data.get_peer_group_channel(group_data, self._session) def _result_construct(self) -> None: assert isinstance(self._caches, list) and all( @@ -74,7 +76,9 @@ class ServerMainListHandler(HandlerBase): _caches: list[GameServerInfo] def _data_operate(self): - self._caches = data.get_server_info_list_with_game_name(self._request.game_name) + self._caches = data.get_server_info_list_with_game_name( + self._request.game_name, self._session + ) def _result_construct(self): assert isinstance(self._caches, list) and all( @@ -105,7 +109,9 @@ class SendMessageHandler(HandlerBase): def _data_operate(self): self._data = data.get_server_info_with_ip_and_port( - self._request.game_server_public_ip, self._request.game_server_public_port + self._request.game_server_public_ip, + self._request.game_server_public_port, + self._session, ) def _result_construct(self): @@ -123,7 +129,9 @@ class ServerInfoHandler(HandlerBase): def _data_operate(self) -> None: self._data = data.get_server_info_with_ip_and_port( - self._request.game_server_public_ip, self._request.game_server_public_port + self._request.game_server_public_ip, + self._request.game_server_public_port, + self._session, ) def _result_construct(self) -> None: diff --git a/src/backends/protocols/gamespy/web_services/data.py b/src/backends/protocols/gamespy/web_services/data.py index 8ee997b12..48867e41f 100644 --- a/src/backends/protocols/gamespy/web_services/data.py +++ b/src/backends/protocols/gamespy/web_services/data.py @@ -4,7 +4,6 @@ from typing import TYPE_CHECKING, cast from backends.library.database.pg_orm import ( - ENGINE, Profiles, SubProfiles, Users, @@ -26,28 +25,28 @@ def is_user_exist( namespace_id: int, email: str, password: str, + session: Session, ) -> None: - with Session(ENGINE) as session: - result = ( - session.query(Profiles) - .join(Users) - .join(SubProfiles) - .where( - SubProfiles.uniquenick == uniquenick, - SubProfiles.cdkeyenc == cdkey, - SubProfiles.partnerid == partner_id, - SubProfiles.namespaceid == namespace_id, - Users.email == email, - Users.password == password, - ) - .first() + result = ( + session.query(Profiles) + .join(Users) + .join(SubProfiles) + .where( + SubProfiles.uniquenick == uniquenick, + SubProfiles.cdkeyenc == cdkey, + SubProfiles.partnerid == partner_id, + SubProfiles.namespaceid == namespace_id, + Users.email == email, + Users.password == password, ) + .first() + ) if result is None: raise AuthException("No account exists with the provided email address.") def get_info_by_cdkey_email( - uniquenick: str, namespace_id: int, cdkey: str, email: str + uniquenick: str, namespace_id: int, cdkey: str, email: str, session: Session ) -> tuple[int, int, str, str, str]: """ return [user_id,profile_id,profile_nick,unique_nick,cdkey_hash] @@ -57,22 +56,21 @@ def get_info_by_cdkey_email( assert isinstance(cdkey, str) assert isinstance(email, str) - with Session(ENGINE) as session: - result = ( - session.query(Users, Profiles, SubProfiles) - .join(Users) - .join( - Profiles, - ) - .join(SubProfiles) - .where( - SubProfiles.uniquenick == uniquenick, - SubProfiles.namespaceid == namespace_id, - SubProfiles.cdkeyenc == cdkey, - Users.email == email, - ) - .first() + result = ( + session.query(Users, Profiles, SubProfiles) + .join(Users) + .join( + Profiles, + ) + .join(SubProfiles) + .where( + SubProfiles.uniquenick == uniquenick, + SubProfiles.namespaceid == namespace_id, + SubProfiles.cdkeyenc == cdkey, + Users.email == email, ) + .first() + ) if result is None: raise AuthException( @@ -96,18 +94,20 @@ def get_info_by_cdkey_email( ) -def get_info_by_authtoken(auth_token: str) -> tuple[int, int, str, str, str]: +def get_info_by_authtoken( + auth_token: str, session: Session +) -> tuple[int, int, str, str, str]: """ return [user_id,profile_id,profile_nick,unique_nick,cdkey_hash] """ - with Session(ENGINE) as session: - result = ( - session.query(Users, Profiles, SubProfiles) - .join(Users, Users.userid == Profiles.userid) - .join(Profiles, Profiles.profileid == SubProfiles.profileid) - .where(SubProfiles.authtoken == auth_token) - .first() - ) + + result = ( + session.query(Users, Profiles, SubProfiles) + .join(Users, Users.userid == Profiles.userid) + .join(Profiles, Profiles.profileid == SubProfiles.profileid) + .where(SubProfiles.authtoken == auth_token) + .first() + ) if result is None: raise AuthException("No account exists with the provided authtoken.") user: Users = result[0] @@ -128,22 +128,22 @@ def get_info_by_authtoken(auth_token: str) -> tuple[int, int, str, str, str]: def get_info_by_uniquenick( - uniquenick: str, namespace_id: int + uniquenick: str, namespace_id: int, session: Session ) -> tuple[int, int, str, str, str]: """ return [user_id,profile_id,profile_nick,unique_nick,cdkey_hash] """ - with Session(ENGINE) as session: - result = ( - session.query(Users, Profiles, SubProfiles) - .join(Users, Users.userid == Profiles.userid) - .join(Profiles, Profiles.profileid == SubProfiles.profileid) - .where( - SubProfiles.uniquenick == uniquenick, - SubProfiles.namespaceid == namespace_id, - ) - .first() + + result = ( + session.query(Users, Profiles, SubProfiles) + .join(Users, Users.userid == Profiles.userid) + .join(Profiles, Profiles.profileid == SubProfiles.profileid) + .where( + SubProfiles.uniquenick == uniquenick, + SubProfiles.namespaceid == namespace_id, ) + .first() + ) if result is None: raise AuthException( @@ -178,26 +178,18 @@ def get_info_by_uniquenick( # region sake -def get_user_data( - table_id: int, -) -> dict: - with Session(ENGINE) as session: - result = ( - session.query(SakeStorage.data) - .where(SakeStorage.tableid == table_id) - .first() - ) +def get_user_data(table_id: int, session: Session) -> dict: + result = ( + session.query(SakeStorage.data).where(SakeStorage.tableid == table_id).first() + ) if TYPE_CHECKING: result = cast(dict, result) return result -def update_user_data(table_id: int, data: dict) -> None: - with Session(ENGINE) as session: - result = ( - session.query(SakeStorage).where(SakeStorage.tableid == table_id).first() - ) +def update_user_data(table_id: int, data: dict, session: Session) -> None: + result = session.query(SakeStorage).where(SakeStorage.tableid == table_id).first() if result is None: raise SakeException("user data not found") assert isinstance(result.data, dict) @@ -210,29 +202,16 @@ def update_user_data(table_id: int, data: dict) -> None: result.data[key] = data[key] -def create_records(table_id: int, data: dict) -> None: +def create_records(table_id: int, data: dict, session: Session) -> None: assert isinstance(table_id, int) assert isinstance(data, dict) - with Session(ENGINE) as session: - result = ( - session.query(SakeStorage).where(SakeStorage.tableid == table_id).count() - ) - - if result != 0: - raise SakeException("Records already existed") - sake = SakeStorage(table_id=table_id, data=data) + result = session.query(SakeStorage).where(SakeStorage.tableid == table_id).count() - session.add(sake) - session.commit() + if result != 0: + raise SakeException("Records already existed") + sake = SakeStorage(table_id=table_id, data=data) -if __name__ == "__main__": - with Session(ENGINE) as session: - result = ( - session.query(Users, Profiles, SubProfiles) - .join(Profiles, Users.userid == Profiles.userid) - .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) - .where(Users.userid == 1) - .first() - ) + session.add(sake) + session.commit() diff --git a/src/backends/protocols/gamespy/web_services/handlers.py b/src/backends/protocols/gamespy/web_services/handlers.py index 854a73353..a930433a4 100644 --- a/src/backends/protocols/gamespy/web_services/handlers.py +++ b/src/backends/protocols/gamespy/web_services/handlers.py @@ -33,6 +33,7 @@ def _data_operate(self) -> None: namespace_id=self._request.namespace_id, cdkey=self._request.cdkey, email=self._request.email, + session=self._session, ) def _result_construct(self) -> None: @@ -56,7 +57,9 @@ class LoginRemoteAuthHandler(HandlerBase): _request: LoginRemoteAuthRequest def _data_operate(self) -> None: - self.data = data.get_info_by_authtoken(auth_token=self._request.auth_token) + self.data = data.get_info_by_authtoken( + auth_token=self._request.auth_token, session=self._session + ) def _result_construct(self) -> None: self._result = LoginProfileResult( @@ -73,7 +76,9 @@ class LoginUniqueNickHandler(HandlerBase): def _data_operate(self) -> None: self.data = data.get_info_by_uniquenick( - uniquenick=self._request.uniquenick, namespace_id=self._request.namespace_id + uniquenick=self._request.uniquenick, + namespace_id=self._request.namespace_id, + session=self._session, ) def _result_construct(self) -> None: @@ -136,7 +141,7 @@ class GetMyRecordsHandler(HandlerBase): _request: GetMyRecordsRequest def _data_operate(self): - self.data = data.get_user_data(self._request.table_id) + self.data = data.get_user_data(self._request.table_id, self._session) raise NotImplementedError() diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index ea22d4823..137bdd45e 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -1,13 +1,18 @@ +from backends.library.abstractions.contracts import OKResponse from backends.protocols.gamespy.chat.brocker_manager import MANAGER from backends.protocols.gamespy.chat.handlers import ( CdKeyHandler, + CryptHandler, GetKeyHandler, GetUdpRelayHandler, InviteHandler, + NickHandler, + UserHandler, ) from backends.protocols.gamespy.chat.requests import ( AtmRequest, CdkeyRequest, + CryptRequest, GetCKeyRequest, GetChannelKeyRequest, GetKeyRequest, @@ -66,6 +71,8 @@ async def websocket_endpoint(ws: WebSocket): print("Client disconnected") + + # region General @@ -110,7 +117,9 @@ def login(request: LoginRequest): @router.post(f"{CHAT}/NickHandler") def nick(request: NickRequest): - pass + handler = NickHandler(request) + handler.handle() + return handler.response @router.post(f"{CHAT}/QuitHandler") @@ -125,12 +134,15 @@ def set_key(request: SetKeyRequest): @router.post(f"{CHAT}/UserHandler") def user(request: UserRequest): - pass + handler = UserHandler(request) + handler.handle() + return handler.response @router.post(f"{CHAT}/UserIPHandler") def user_ip(request: UserIPRequest): - pass + print(request) + return OKResponse() @router.post(f"{CHAT}/WhoHandler") @@ -194,6 +206,13 @@ def topic(request: TopicRequest): pass +@router.post(f"{CHAT}/CryptHandler") +def crypt(request: CryptRequest): + handler = CryptHandler(request) + handler.handle() + return handler.response + + # region Message diff --git a/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py b/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py index a3c7f3afb..91d5e7ebd 100644 --- a/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py +++ b/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py @@ -1,86 +1,108 @@ # the tests related to database operations from unittest import TestCase -from backends.library.database.pg_orm import Profiles, Users +from backends.library.database.pg_orm import ENGINE, Profiles, Users import backends.protocols.gamespy.presence_search_player.data as data +from sqlalchemy.orm import Session class DataFetchTests(TestCase): def test_verify_email(self) -> None: - result1 = data.verify_email("spyguy@unispy.net") - self.assertFalse(result1) - result2 = data.verify_email("spyguy@gamespy.com") - self.assertTrue(result2) + with Session(ENGINE) as session: + result1 = data.verify_email("spyguy@unispy.net", session) + self.assertFalse(result1) + result2 = data.verify_email("spyguy@gamespy.com", session) + self.assertTrue(result2) def test_verify_email_and_password(self): - result1 = data.verify_email_and_password( - "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d91" - ) - self.assertFalse(result1) - result2 = data.verify_email_and_password( - "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b" - ) - self.assertTrue(result2) + with Session(ENGINE) as session: + result1 = data.verify_email_and_password( + "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d91", session + ) + self.assertFalse(result1) + result2 = data.verify_email_and_password( + "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b", session + ) + self.assertTrue(result2) def test_get_profile_id(self): - result1 = data.get_profile_id( - "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b", "spyguy1", 1 - ) - self.assertIsNone(result1) - result2 = data.get_profile_id( - "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b", "spyguy", 1 - ) - self.assertIsNotNone(result2) - self.assertEqual(result2, 1) + with Session(ENGINE) as session: + result1 = data.get_profile_id( + "spyguy@gamespy.com", + "4a7d1ed414474e4033ac29ccb8653d9b", + "spyguy1", + 1, + session, + ) + self.assertIsNone(result1) + result2 = data.get_profile_id( + "spyguy@gamespy.com", + "4a7d1ed414474e4033ac29ccb8653d9b", + "spyguy", + 1, + session, + ) + self.assertIsNotNone(result2) + self.assertEqual(result2, 1) def test_get_users(self): - result1 = data.get_user("spyguy@gamespy.com") - self.assertIsNotNone(result1) - self.assertEqual(type(result1), Users) - result2 = data.get_user("spyguy_not_user@gamespy.com") - self.assertIsNone(result2) + with Session(ENGINE) as session: + result1 = data.get_user("spyguy@gamespy.com", session) + self.assertIsNotNone(result1) + self.assertEqual(type(result1), Users) + result2 = data.get_user("spyguy_not_user@gamespy.com", session) + self.assertIsNone(result2) def test_get_profile(self): - result1 = data.get_profile(1, "spyguy") - self.assertEqual(type(result1), Profiles) + with Session(ENGINE) as session: + result1 = data.get_profile(1, "spyguy", session) + self.assertEqual(type(result1), Profiles) - result2 = data.get_profile(1, "spyguy_not_profile") - self.assertIsNone(result2) + result2 = data.get_profile(1, "spyguy_not_profile", session) + self.assertIsNone(result2) def test_get_sub_profile(self): pass def test_get_nick_and_unique_nick_list(self): - result = data.get_nick_and_unique_nick_list( - "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b", 0 - ) - self.assertIsInstance(result, list) - self.assertEqual(len(result), 1) - self.assertIsInstance(result[0], tuple) + with Session(ENGINE) as session: + result = data.get_nick_and_unique_nick_list( + "spyguy@gamespy.com", "4a7d1ed414474e4033ac29ccb8653d9b", 0, session + ) + self.assertIsInstance(result, list) + self.assertEqual(len(result), 1) + self.assertIsInstance(result[0], tuple) def test_get_matched_profile_info_list(self): - result = data.get_matched_profile_info_list([1], 0) - self.assertIsInstance(result, list) - self.assertNotEqual(len(result), 0) + with Session(ENGINE) as session: + result = data.get_matched_profile_info_list([1], 0, session) + self.assertIsInstance(result, list) + self.assertNotEqual(len(result), 0) def test_get_matched_info_by_nick(self): - result = data.get_matched_info_by_nick("spyguy") - self.assertIsNotNone(result) - self.assertNotEqual(len(result), 0) + with Session(ENGINE) as session: + result = data.get_matched_info_by_nick("spyguy", session) + self.assertIsNotNone(result) + self.assertNotEqual(len(result), 0) def test_get_matched_info_by_email(self): - result = data.get_matched_info_by_email("spyguy@gamespy.com") - self.assertEqual(result[0]["nick"], "spyguy") - self.assertEqual(result[0]["profile_id"], 1) + with Session(ENGINE) as session: + result = data.get_matched_info_by_email("spyguy@gamespy.com", session) + self.assertEqual(result[0]["nick"], "spyguy") + self.assertEqual(result[0]["profile_id"], 1) def test_is_uniquenick_exist(self): - result1 = data.is_uniquenick_exist("spyguy_test", 0, "gmtests") - self.assertTrue(result1) + with Session(ENGINE) as session: + result1 = data.is_uniquenick_exist("spyguy_test", 0, "gmtests", session) + self.assertTrue(result1) - result2 = data.is_uniquenick_exist("spyguy_not_uniquenick", 0, "gmtests") - self.assertFalse(result2) + result2 = data.is_uniquenick_exist( + "spyguy_not_uniquenick", 0, "gmtests", session + ) + self.assertFalse(result2) def test_is_email_exist(self): - result1 = data.is_email_exist("spyguy@gamespy.com") - self.assertTrue(result1) - result2 = data.is_email_exist("spyguy@gamespy.net") - self.assertFalse(result2) + with Session(ENGINE) as session: + result1 = data.is_email_exist("spyguy@gamespy.com", session) + self.assertTrue(result1) + result2 = data.is_email_exist("spyguy@gamespy.net", session) + self.assertFalse(result2) diff --git a/src/backends/tests/gamespy/precence_search_player/handler_tests.py b/src/backends/tests/gamespy/precence_search_player/handler_tests.py index eddeee3f4..1306f0456 100644 --- a/src/backends/tests/gamespy/precence_search_player/handler_tests.py +++ b/src/backends/tests/gamespy/precence_search_player/handler_tests.py @@ -3,7 +3,16 @@ from backends.tests.utils import add_headers import frontends.gamespy.protocols.presence_search_player.contracts.requests as psp from frontends.tests.gamespy.presence_search_player.handler_tests import ( - NICKS, SEARCH_1, SEARCH_2, SEARCH_3, SEARCH_4, CHECK1, NEWUSER, SEARCH_UNIQUENICK, VALID) + NICKS, + SEARCH_1, + SEARCH_2, + SEARCH_3, + SEARCH_4, + CHECK1, + NEWUSER, + SEARCH_UNIQUENICK, + VALID, +) import backends.protocols.gamespy.presence_search_player.requests as bkr import backends.protocols.gamespy.presence_search_player.handlers as bkh @@ -12,6 +21,7 @@ class HandlerTest(unittest.TestCase): """ test backend and server request compability """ + # region PCM # region PSP @@ -35,6 +45,8 @@ def test_chenck(self): def test_new_user(self): r = psp.NewUserRequest(NEWUSER) data = add_headers(r) + data["email"] = "xiaojiuwo1@gamespy.com" + data["nick"] = "xiaojiuwo1" request = bkr.NewUserRequest(**data) handler = bkh.NewUserHandler(request) handler.handle() diff --git a/src/frontends/gamespy/library/configs.py b/src/frontends/gamespy/library/configs.py index a5f4106a1..577aeab02 100644 --- a/src/frontends/gamespy/library/configs.py +++ b/src/frontends/gamespy/library/configs.py @@ -2,7 +2,7 @@ from typing import Literal, Optional from uuid import UUID -from pydantic import BaseModel, field_validator +from pydantic import BaseModel, HttpUrl, field_validator class PostgreSql(BaseModel): @@ -63,6 +63,9 @@ class BackendConfig(BaseModel): token_expire_time: int = 30 + + + class UnittestConfig(BaseModel): """ unittest related config diff --git a/src/frontends/gamespy/library/network/tcp_handler.py b/src/frontends/gamespy/library/network/tcp_handler.py index a61fdc063..076621a03 100644 --- a/src/frontends/gamespy/library/network/tcp_handler.py +++ b/src/frontends/gamespy/library/network/tcp_handler.py @@ -51,6 +51,7 @@ def handle(self) -> None: class TcpServer(NetworkServerBase): + def __init__( self, config: ServerConfig, t_client: type[ClientBase], logger: LogWriter ) -> None: @@ -58,10 +59,12 @@ def __init__( self._server = socketserver.ThreadingTCPServer( (self._config.public_address, self._config.listening_port), TcpHandler, + bind_and_activate=False ) - self._server.unispy_params = (self._config, self._client_cls, self._logger) # type: ignore self._server.allow_reuse_address = True # type:ignore - + self._server.unispy_params = (self._config, self._client_cls, self._logger) # type: ignore + self._server.server_bind() + self._server.server_activate() class TestClient(ClientBase): diff --git a/src/frontends/gamespy/protocols/chat/aggregates/enums.py b/src/frontends/gamespy/protocols/chat/aggregates/enums.py index 1f0646fd6..75d981566 100644 --- a/src/frontends/gamespy/protocols/chat/aggregates/enums.py +++ b/src/frontends/gamespy/protocols/chat/aggregates/enums.py @@ -62,6 +62,44 @@ class TopicRequestType(IntEnum): SET_CHANNEL_TOPIC = 1 +class ResponseCode(Enum): + WELCOME = "001" + USRIP = "302" + WHOISUSER = "311" + ENDOFWHO = "315" + ENDOFWHOIS = "318" + WHOISCHANNELS = "319" + LISTSTART = "321" + LIST = "322" + LISTEND = "323" + CHANNELMODEIS = "324" + NOTOPIC = "331" + TOPIC = "332" + WHOREPLY = "352" + NAMEREPLY = "353" + ENDOFNAMES = "366" + BANLIST = "367" + ENDOFBANLIST = "368" + GETKEY = "700" + ENDGETKEY = "701" + GETCKEY = "702" + ENDGETCKEY = "703" + GETCHANKEY = "704" + SECUREKEY = "705" + CDKEY = "706" + LOGIN = "707" + GETUDPRELAY = "712" + PONG = "PONG" + JOIN = "JOIN" + KICK = "KICK" + QUIT = "QUIT" + PART = "PART" + ATM = "ATM" + UTM = "UTM" + PRIVMSG = "PRIVMSG" + NOTICE = "NOTICE" + + # region IRC error code class IRCErrorCode(Enum): NO_SUCH_NICK = "401" @@ -80,6 +118,7 @@ class IRCErrorCode(Enum): UNIQUE_NICK_EXPIRED = "710" REGISTER_NICK_FAILED = "711" + # region Peer room diff --git a/src/frontends/gamespy/protocols/chat/aggregates/peer_room.py b/src/frontends/gamespy/protocols/chat/aggregates/peer_room.py index 782442ac7..b4af8e63b 100644 --- a/src/frontends/gamespy/protocols/chat/aggregates/peer_room.py +++ b/src/frontends/gamespy/protocols/chat/aggregates/peer_room.py @@ -2,7 +2,6 @@ class PeerRoom: - TitleRoomPrefix = "#GSP" """ When game connects to the server, the player will enter the default channel for communicating with other players.""" StagingRoomPrefix = "#GSP" @@ -61,4 +60,5 @@ def is_group_room(channel_name: str) -> bool: if __name__ == "__main__": result = PeerRoom.get_room_type("#GSP!worms3!Ml4lz344lM") + result = PeerRoom.get_room_type("#GPG!700") pass diff --git a/src/frontends/gamespy/protocols/chat/aggregates/response_name.py b/src/frontends/gamespy/protocols/chat/aggregates/response_name.py index 6ec27ef66..5cd42b1a3 100644 --- a/src/frontends/gamespy/protocols/chat/aggregates/response_name.py +++ b/src/frontends/gamespy/protocols/chat/aggregates/response_name.py @@ -1,57 +1,57 @@ -WELCOME = "001" -USER_IP = "302" -WHO_IS_USER = "311" -END_OF_WHO = "315" -END_OF_WHO_IS = "318" -WHO_IS_CHANNELS = "319" -LIST_START = "321" -LIST = "322" -LIST_END = "323" -CHANNEL_MODELS = "324" -NO_TOPIC = "331" -TOPIC = "332" -WHO_REPLY = "352" -NAME_REPLY = "353" -END_OF_NAMES = "366" -BAN_LIST = "367" -END_OF_BAN_LIST = "368" -GET_KEY = "700" -END_GET_KEY = "701" -GET_CKEY = "702" -END_GET_CKEY = "703" -GET_CHAN_KEY = "704" -SECURE_KEY = "705" -CD_KEY = "706" -LOGIN = "707" -GET_UDP_RELAY = "712" +# WELCOME = "001" +# USER_IP = "302" +# WHO_IS_USER = "311" +# END_OF_WHO = "315" +# END_OF_WHO_IS = "318" +# WHO_IS_CHANNELS = "319" +# LIST_START = "321" +# LIST = "322" +# LIST_END = "323" +# CHANNEL_MODELS = "324" +# NO_TOPIC = "331" +# TOPIC = "332" +# WHO_REPLY = "352" +# NAME_REPLY = "353" +# END_OF_NAMES = "366" +# BAN_LIST = "367" +# END_OF_BAN_LIST = "368" +# GET_KEY = "700" +# END_GET_KEY = "701" +# GET_CKEY = "702" +# END_GET_CKEY = "703" +# GET_CHAN_KEY = "704" +# SECURE_KEY = "705" +# CD_KEY = "706" +# LOGIN = "707" +# GET_UDP_RELAY = "712" -# Send a private message -PRIVATE_MSG = "PRIVMSG" -# Send a notice message -NOTICE = "NOTICE" -# Send an under the table message -UNDER_THE_TABLE_MSG = "UTM" -# Send an above the table message -ABOVE_THE_TABLE_MSG = "ATM" -PING = "PING" -PONG = "PONG" -# Search with nickname -NICK = "NICK" -# Join a channel -JOIN = "JOIN" -# Leave a channel -PART = "PART" -# Kick a user from a channel -KICK = "KICK" -# Quit irc chat server -QUIT = "QUIT" +# # Send a private message +# PRIVATE_MSG = "PRIVMSG" +# # Send a notice message +# NOTICE = "NOTICE" +# # Send an under the table message +# UNDER_THE_TABLE_MSG = "UTM" +# # Send an above the table message +# ABOVE_THE_TABLE_MSG = "ATM" +# PING = "PING" +# PONG = "PONG" +# # Search with nickname +# NICK = "NICK" +# # Join a channel +# JOIN = "JOIN" +# # Leave a channel +# PART = "PART" +# # Kick a user from a channel +# KICK = "KICK" +# # Quit irc chat server +# QUIT = "QUIT" -KILL = "KILL" -# Change channel topic -CHANNEL_TOPIC = "TOPIC" -# Change channel mode -MODE = "MODE" +# KILL = "KILL" +# # Change channel topic +# CHANNEL_TOPIC = "TOPIC" +# # Change channel mode +# MODE = "MODE" -ERROR = "ERROR" -# Invite a user to a channel -INVITE = "INVITE" +# ERROR = "ERROR" +# # Invite a user to a channel +# INVITE = "INVITE" diff --git a/src/frontends/gamespy/protocols/chat/applications/client.py b/src/frontends/gamespy/protocols/chat/applications/client.py index 39037ecbb..339527fd5 100644 --- a/src/frontends/gamespy/protocols/chat/applications/client.py +++ b/src/frontends/gamespy/protocols/chat/applications/client.py @@ -8,7 +8,6 @@ from frontends.gamespy.library.configs import CONFIG, ServerConfig - from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage @@ -55,7 +54,7 @@ def on_disconnected(self) -> None: def start_brocker(self): self.brocker = WebSocketBrocker( name=self.server_config.server_name, - url=f"{CONFIG.backend.url}/Chat/ws", + url=f"{CONFIG.backend.url.replace('http', 'ws')}/GameSpy/Chat/ws", call_back_func=self._process_brocker_message, ) self.brocker.subscribe() diff --git a/src/frontends/gamespy/protocols/chat/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py index 73d13f253..e5264c4e1 100644 --- a/src/frontends/gamespy/protocols/chat/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -1,3 +1,4 @@ +from frontends.gamespy.library.encryption.gs_encryption import ChatCrypt from frontends.gamespy.protocols.chat.contracts.results import ( ATMResult, NoticeResult, @@ -110,13 +111,19 @@ class CryptHandler(CmdHandlerBase): def __init__(self, client: ClientBase, request: RequestBase): assert isinstance(request, CryptRequest) super().__init__(client, request) + self._result_cls = CryptResult - def _data_operate(self): - pass + def _data_operate(self) -> None: + super()._data_operate() + self._client.info.gamename = self._request.gamename def _response_construct(self) -> None: self._response = CryptResponse() + def _response_send(self) -> None: + super()._response_send() + self._client.crypto = ChatCrypt(self._result.secret_key) + class GetKeyHandler(CmdHandlerBase): _request: GetKeyRequest @@ -226,6 +233,9 @@ def __init__(self, client: ClientBase, request: UserRequest): super().__init__(client, request) self._is_fetching = False + def _request_check(self) -> None: + super()._request_check() + class UserIPHandler(CmdHandlerBase): _request: UserIPRequest @@ -236,8 +246,15 @@ def __init__(self, client: ClientBase, request: UserIPRequest): super().__init__(client, request) self._is_fetching = False - def _response_construct(self) -> None: + def _request_check(self) -> None: + super()._request_check() + self._request.remote_ip_address = self._client.connection.remote_ip + + def _data_operate(self) -> None: + super()._data_operate() self._result = UserIPResult(remote_ip_address=self._client.connection.remote_ip) + + def _response_construct(self) -> None: self._response = UserIPResponse(self._result) diff --git a/src/frontends/gamespy/protocols/chat/applications/server_launcher.py b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py index 57e6e081b..f84a507fc 100644 --- a/src/frontends/gamespy/protocols/chat/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py @@ -15,7 +15,7 @@ def _launch_server(self): assert self.config is not None assert self.logger is not None self.server = TcpServer(self.config, Client, self.logger) - + super()._launch_server() if __name__ == "__main__": s = ServerLauncher() diff --git a/src/frontends/gamespy/protocols/chat/contracts/requests.py b/src/frontends/gamespy/protocols/chat/contracts/requests.py index 180180023..2e0cb0819 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/requests.py +++ b/src/frontends/gamespy/protocols/chat/contracts/requests.py @@ -218,19 +218,21 @@ class UserIPRequest(RequestBase): class UserRequest(RequestBase): user_name: str - host_name: str + local_ip_address: str server_name: str - nick_name: str name: str + def __init__(self, raw_request: str) -> None: + super().__init__(raw_request) + def parse(self): super().parse() if len(self._cmd_params) == 3: self.user_name = self._cmd_params[0] - self.host_name = self._cmd_params[1] + self.local_ip_address = self._cmd_params[1] self.server_name = self._cmd_params[2] else: - self.host_name = self._cmd_params[0] + self.local_ip_address = self._cmd_params[0] self.server_name = self._cmd_params[1] assert isinstance(self._long_param, str) diff --git a/src/frontends/gamespy/protocols/chat/contracts/responses.py b/src/frontends/gamespy/protocols/chat/contracts/responses.py index 74b2c8cb1..481b6dc73 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/responses.py +++ b/src/frontends/gamespy/protocols/chat/contracts/responses.py @@ -2,37 +2,7 @@ from frontends.gamespy.protocols.chat.aggregates.enums import ( ModeRequestType, WhoRequestType, -) -from frontends.gamespy.protocols.chat.aggregates.response_name import ( - ABOVE_THE_TABLE_MSG, - CD_KEY, - CHANNEL_MODELS, - END_GET_CKEY, - END_GET_KEY, - END_OF_NAMES, - END_OF_WHO, - END_OF_WHO_IS, - GET_CHAN_KEY, - GET_CKEY, - GET_KEY, - JOIN, - KICK, - LIST_END, - LIST_START, - LOGIN, - NAME_REPLY, - NO_TOPIC, - NOTICE, - PART, - PONG, - PRIVATE_MSG, - SECURE_KEY, - TOPIC, - UNDER_THE_TABLE_MSG, - WELCOME, - WHO_IS_CHANNELS, - WHO_IS_USER, - WHO_REPLY, + ResponseCode, ) from frontends.gamespy.protocols.chat.contracts.results import ( GetCKeyResult, @@ -80,7 +50,6 @@ ResponseBase, ) from frontends.gamespy.library.encryption.gs_encryption import CLIENT_KEY, SERVER_KEY -from frontends.tests.gamespy.chat.request_tests import USER_IP # region General @@ -90,7 +59,9 @@ def __init__(self) -> None: pass def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {CD_KEY} * 1 :Authenticated.\r\n" # noqa + self.sending_buffer = ( + f":{SERVER_DOMAIN} {ResponseCode.CDKEY.value} * 1 :Authenticated.\r\n" # noqa + ) class CryptResponse(ResponseBase): @@ -99,7 +70,7 @@ def __init__(self) -> None: def build(self) -> None: self.sending_buffer = ( - f":{SERVER_DOMAIN} {SECURE_KEY} * {CLIENT_KEY} {SERVER_KEY}\r\n" # noqa + f":{SERVER_DOMAIN} {ResponseCode.SECUREKEY.value} * {CLIENT_KEY} {SERVER_KEY}\r\n" # noqa ) @@ -116,9 +87,9 @@ def build(self) -> None: self.sending_buffer = "" for value in self._result.values: - self.sending_buffer += f":{SERVER_DOMAIN} {GET_KEY} * {self._result.nick_name} {self._request.cookie} {value}\r\n" # noqa + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.GETKEY.value} * {self._result.nick_name} {self._request.cookie} {value}\r\n" # noqa - self.sending_buffer += f":{SERVER_DOMAIN} {END_GET_KEY} * {self._request.cookie} * :End Of GETKEY.\r\n" # noqa + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDGETKEY.value} * {self._request.cookie} * :End Of GETKEY.\r\n" # noqa class ListResponse(ResponseBase): @@ -135,8 +106,10 @@ def build(self) -> None: total_channel_user, channel_topic, ) in self._result.channel_info_list: - self.sending_buffer += f":{self._result.user_irc_prefix} {LIST_START} * {channel_name} {total_channel_user} {channel_topic}\r\n" # noqa - self.sending_buffer += f":{self._result.user_irc_prefix} {LIST_END}\r\n" # noqa + self.sending_buffer += f":{self._result.user_irc_prefix} {ResponseCode.LISTSTART.value} * {channel_name} {total_channel_user} {channel_topic}\r\n" # noqa + self.sending_buffer += ( + f":{self._result.user_irc_prefix} {ResponseCode.LISTEND.value}\r\n" # noqa + ) class LoginResponse(ResponseBase): @@ -147,7 +120,7 @@ def __init__(self, result: LoginResult) -> None: self._result = result def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {LOGIN} * {self._result.user_id} {self._result.profile_id}\r\n" # noqa + self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.LOGIN.value} * {self._result.user_id} {self._result.profile_id}\r\n" # noqa class NickResponse(ResponseBase): @@ -158,7 +131,7 @@ def __init__(self, result: NickResult) -> None: self._result = result def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {WELCOME} {self._result.nick_name} :Welcome to UniSpy!\r\n" # noqa + self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.WELCOME.value} {self._result.nick_name} :Welcome to UniSpy!\r\n" # noqa class PingResponse(ResponseBase): @@ -169,7 +142,9 @@ def __init__(self, result: PingResult) -> None: self._result = result def build(self) -> None: - self.sending_buffer = f":{self._result.requester_irc_prefix} {PONG}\r\n" + self.sending_buffer = ( + f":{self._result.requester_irc_prefix} {ResponseCode.PONG.value}\r\n" + ) class UserIPResponse(ResponseBase): @@ -180,9 +155,7 @@ def __init__(self, result: UserIPResult) -> None: self._result = result def build(self) -> None: - self.sending_buffer = ( - f":{SERVER_DOMAIN} {USER_IP} :@{self._result.remote_ip_address}\r\n" - ) + self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.USRIP.value} :@{self._result.remote_ip_address}\r\n" class WhoIsResponse(ResponseBase): @@ -193,16 +166,16 @@ def __init__(self, result: WhoIsResult) -> None: self._result = result def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {WHO_IS_USER} {self._result.nick_name} {self._result.name} {self._result.user_name} {self._result.public_ip_address} * :{self._result.user_name}\r\n" # noqa + self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.WHOISUSER.value} {self._result.nick_name} {self._result.name} {self._result.user_name} {self._result.public_ip_address} * :{self._result.user_name}\r\n" # noqa if len(self._result.joined_channel_name) != 0: channel_name = "" for name in self._result.joined_channel_name: channel_name += f"@{name} " # noqa - self.sending_buffer += f":{SERVER_DOMAIN} {WHO_IS_CHANNELS} {self._result.nick_name} {self._result.name} :{channel_name}\r\n" # noqa + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.WHOISCHANNELS.value} {self._result.nick_name} {self._result.name} :{channel_name}\r\n" # noqa - self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO_IS} {self._result.nick_name} {self._result.name} :End of WHOIS list. \r\n" # noqa + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDOFWHOIS.value} {self._result.nick_name} {self._result.name} :End of WHOIS list. \r\n" # noqa class WhoResponse(ResponseBase): @@ -223,14 +196,14 @@ def build(self): nick_name, modes, ) in self._result.infos: - self.sending_buffer += f":{SERVER_DOMAIN} {WHO_REPLY} * {channel_name} {user_name} {public_ip_address} * {nick_name} {modes} *\r\n" # noqa + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.WHOREPLY.value} * {channel_name} {user_name} {public_ip_address} * {nick_name} {modes} *\r\n" # noqa if self._request.request_type == WhoRequestType.GET_CHANNEL_USER_INFO: # if len(self._result.infos) > 0: - self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.channel_name} * :End of WHO.\r\n" # noqa + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDOFWHO.value} * {self._request.channel_name} * :End of WHO.\r\n" # noqa elif self._request.request_type == WhoRequestType.GET_USER_INFO: # if len(self._result.infos) > 0: - self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_WHO} * {self._request.nick_name} * :End of WHO.\r\n" # noqa + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDOFWHO.value} * {self._request.nick_name} * :End of WHO.\r\n" # noqa # region Channel @@ -241,7 +214,7 @@ class GetChannelKeyResponse(ChannelResponseBase): _result: GetChannelKeyResult def build(self) -> None: - self.sending_buffer = f":{self._result.channel_user_irc_prefix} {GET_CHAN_KEY} * {self._result.channel_name} {self._request.cookie} {self._result.values}\r\n" + self.sending_buffer = f":{self._result.channel_user_irc_prefix} {ResponseCode.GETCHANKEY.value} * {self._result.channel_name} {self._request.cookie} {self._result.values}\r\n" class GetCKeyResponse(ResponseBase): @@ -256,9 +229,9 @@ def __init__(self, request: GetCKeyRequest, result: GetCKeyResult) -> None: def build(self) -> None: self.sending_buffer = "" for nick_name, user_values in self._result.infos: - self.sending_buffer += f":{SERVER_DOMAIN} {GET_CKEY} * {self._request.channel_name} {nick_name} {self._request.cookie} {user_values}\r\n" # noqa + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.GETCKEY.value} * {self._request.channel_name} {nick_name} {self._request.cookie} {user_values}\r\n" # noqa - self.sending_buffer += f"{SERVER_DOMAIN} {END_GET_CKEY} * {self._request.channel_name} {self._request.cookie} :End Of GETCKEY.\r\n" # noqa + self.sending_buffer += f"{SERVER_DOMAIN} {ResponseCode.ENDGETCKEY.value} * {self._request.channel_name} {self._request.cookie} :End Of GETCKEY.\r\n" # noqa class JoinResponse(ResponseBase): @@ -273,7 +246,7 @@ def __init__(self, request: JoinRequest, result: JoinResult) -> None: def build(self) -> None: joiner_irc_prefix = f"{self._result.joiner_nick_name}!{self._result.joiner_user_name}@{SERVER_DOMAIN}" self.sending_buffer = ( - f"{joiner_irc_prefix} {JOIN} {self._request.channel_name}\r\n" + f"{joiner_irc_prefix} {ResponseCode.JOIN.value} {self._request.channel_name}\r\n" ) @@ -288,7 +261,7 @@ def __init__(self, request: KickRequest, result: KickResult) -> None: def build(self) -> None: kicker_irc_prefix = f"{self._result.kicker_nick_name}!{self._result.kicker_user_name}@{SERVER_DOMAIN}" - self.sending_buffer = f"{kicker_irc_prefix} {KICK} {self._result.channel_name} {self._result.kickee_nick_name} :{self._request.reason}\r\n" + self.sending_buffer = f"{kicker_irc_prefix} {ResponseCode.KICK.value} {self._result.channel_name} {self._result.kickee_nick_name} :{self._request.reason}\r\n" class ModeResponse(ResponseBase): @@ -302,7 +275,7 @@ def __init__(self, request: ModeRequest, result: ModeResult) -> None: def build(self) -> None: if self._request.type == ModeRequestType.GET_CHANNEL_MODES: - self.sending_buffer = f":{SERVER_DOMAIN} {CHANNEL_MODELS} * {self._result.channel_modes} {self._result.channel_modes}\r\n" + self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.CHANNELMODEIS.value} * {self._result.channel_modes} {self._result.channel_modes}\r\n" class NamesResponse(ChannelResponseBase): @@ -315,8 +288,8 @@ def __init__(self, request: NamesRequest, result: NamesResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {NAME_REPLY} {self._result.requester_nick_name} = {self._result.channel_name} :{self._result.all_channel_nicks}\r\n" - self.sending_buffer += f":{SERVER_DOMAIN} {END_OF_NAMES} {self._result.requester_nick_name} {self._result.channel_name} :End of NAMES list. \r\n" + self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.NAMEREPLY.value} {self._result.requester_nick_name} = {self._result.channel_name} :{self._result.all_channel_nicks}\r\n" + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDOFNAMES.value} {self._result.requester_nick_name} {self._result.channel_name} :End of NAMES list. \r\n" class PartResponse(ResponseBase): @@ -329,7 +302,7 @@ def __init__(self, request: PartRequest, result: PartResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.leaver_irc_prefix} {PART} {self._result.channel_name} :{self._request.reason}\r\n" + self.sending_buffer = f":{self._result.leaver_irc_prefix} {ResponseCode.PART.value} {self._result.channel_name} :{self._request.reason}\r\n" class SetChannelKeyResponse(ChannelResponseBase): @@ -344,7 +317,7 @@ def __init__( super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.channel_user_irc_prefix} {GET_CHAN_KEY} * {self._request.channel_name} BCAST {self._request.key_value_string}\r\n" + self.sending_buffer = f":{self._result.channel_user_irc_prefix} {ResponseCode.GETCHANKEY.value} * {self._request.channel_name} BCAST {self._request.key_value_string}\r\n" class SetCKeyResponse(ResponseBase): @@ -355,9 +328,9 @@ def __init__(self, request: SetCKeyRequest) -> None: super().__init__(request, None) def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {GET_CKEY} * {self._request.channel_name} {self._request.nick_name} {self._request.cookie} {self._request.key_value_string}\r\n" + self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.GETCKEY.value} * {self._request.channel_name} {self._request.nick_name} {self._request.cookie} {self._request.key_value_string}\r\n" - self.sending_buffer += f":{SERVER_DOMAIN} {END_GET_CKEY} * {self._request.channel_name} {self._request.nick_name} {self._request.cookie} :End Of SETCKEY.\r\n" + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDGETCKEY.value} * {self._request.channel_name} {self._request.nick_name} {self._request.cookie} :End Of SETCKEY.\r\n" class TopicResponse(ResponseBase): @@ -371,11 +344,9 @@ def __init__(self, request: TopicRequest, result: TopicResult) -> None: def build(self) -> None: if self._result.channel_topic == "" or self._result.channel_topic is None: - self.sending_buffer = ( - f":{SERVER_DOMAIN} {NO_TOPIC} * {self._result.channel_name}\r\n" - ) + self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.NOTOPIC.value} * {self._result.channel_name}\r\n" else: - self.sending_buffer = f":{SERVER_DOMAIN} {TOPIC} * {self._result.channel_name} :{self._result.channel_topic}\r\n" + self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.TOPIC.value} * {self._result.channel_name} :{self._result.channel_topic}\r\n" # region Message @@ -391,7 +362,7 @@ def __init__(self, request: ATMRequest, result: ATMResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} {ABOVE_THE_TABLE_MSG} {self._result.target_name} :{self._request.message}\r\n" + self.sending_buffer = f":{self._result.user_irc_prefix} {ResponseCode.ATM.value} {self._result.target_name} :{self._request.message}\r\n" class NoticeResponse(ResponseBase): @@ -405,7 +376,7 @@ def __init__(self, request: NoticeRequest, result: NoticeResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} {NOTICE} {self._result.target_name} {self._request.message}\r\n" + self.sending_buffer = f":{self._result.user_irc_prefix} {ResponseCode.NOTICE.value} {self._result.target_name} {self._request.message}\r\n" class PrivateResponse(ResponseBase): @@ -418,7 +389,7 @@ def __init__(self, request: PrivateRequest, result: PrivateResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} {PRIVATE_MSG} {self._result.target_name} :{self._request.message}\r\n" + self.sending_buffer = f":{self._result.user_irc_prefix} {ResponseCode.PRIVMSG.value} {self._result.target_name} :{self._request.message}\r\n" class UTMResponse(ResponseBase): @@ -431,6 +402,6 @@ def __init__(self, request: UTMRequest, result: UTMResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} {UNDER_THE_TABLE_MSG} { + self.sending_buffer = f":{self._result.user_irc_prefix} {ResponseCode.UTM.value} { self._result.target_name } :{self._request.message}" diff --git a/src/frontends/gamespy/protocols/chat/contracts/results.py b/src/frontends/gamespy/protocols/chat/contracts/results.py index cea9e6782..2043f754b 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/results.py +++ b/src/frontends/gamespy/protocols/chat/contracts/results.py @@ -6,6 +6,7 @@ class CryptResult(ResultBase): + secret_key: str pass diff --git a/src/frontends/gamespy/protocols/game_status/contracts/requests.py b/src/frontends/gamespy/protocols/game_status/contracts/requests.py index e4c3737a0..a24cec9d1 100644 --- a/src/frontends/gamespy/protocols/game_status/contracts/requests.py +++ b/src/frontends/gamespy/protocols/game_status/contracts/requests.py @@ -147,6 +147,7 @@ class NewGameRequest(RequestBase): def parse(self) -> None: super().parse() + self.is_client_local_storage_available = True if "sesskey" not in self.request_dict: raise GSException("sesskey is missing") From ccde780355303b60d09839c6dc7c88bb8caa3ad6 Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Wed, 30 Jul 2025 08:54:56 +0000 Subject: [PATCH 197/231] feat: add log color output --- .../gamespy/library/log/log_manager.py | 20 ++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/src/frontends/gamespy/library/log/log_manager.py b/src/frontends/gamespy/library/log/log_manager.py index 23f607a0a..d69e473fe 100644 --- a/src/frontends/gamespy/library/log/log_manager.py +++ b/src/frontends/gamespy/library/log/log_manager.py @@ -33,6 +33,24 @@ def create_dir(path): os.makedirs(log_path) +class ColoredFormatter(logging.Formatter): + COLORS = { + "DEBUG": "\033[94m", # Blue + "INFO": "\033[92m", # Green + "WARNING": "\033[93m", # Yellow + "ERROR": "\033[91m", # Red + "CRITICAL": "\033[41m", # White text on Red background + } + RESET = "\033[0m" # Reset to default color + + def format(self, record): + # Get the color for the levelname + color = self.COLORS.get(record.levelname, self.RESET) + # Format the levelname with color + record.levelname = f"{color}{record.levelname}{self.RESET}" + return super().format(record) + + class LogManager: @staticmethod def create(logger_name: str) -> "LogWriter": @@ -53,7 +71,7 @@ def create(logger_name: str) -> "LogWriter": backupCount=7, encoding="utf-8", ) - formatter = logging.Formatter( + formatter = ColoredFormatter( f"%(asctime)s [{logger_name}] [%(levelname)s]: %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) From 2dde814da2856543d1239dbf51f1ce1cbbd3e445 Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Wed, 30 Jul 2025 08:55:30 +0000 Subject: [PATCH 198/231] fix(chat): join logic --- common/UniSpy_pg.sql | 2 +- src/backends/library/database/pg_orm.py | 1 + .../protocols/gamespy/chat/abstractions.py | 98 -------- src/backends/protocols/gamespy/chat/data.py | 2 +- .../protocols/gamespy/chat/handlers.py | 216 ++++++++++++------ src/backends/protocols/gamespy/chat/helper.py | 44 ++-- .../protocols/gamespy/chat/requests.py | 16 +- src/backends/routers/gamespy/chat.py | 49 ++-- .../gamespy/library/abstractions/handler.py | 9 +- .../gamespy/library/network/brockers.py | 5 +- .../protocols/chat/applications/handlers.py | 25 +- .../protocols/chat/contracts/requests.py | 198 ++++++---------- .../protocols/chat/contracts/responses.py | 36 ++- .../protocols/chat/contracts/results.py | 34 +-- .../tests/gamespy/chat/game_tests.py | 56 +++-- .../tests/gamespy/chat/mock_objects.py | 17 +- 16 files changed, 403 insertions(+), 405 deletions(-) delete mode 100644 src/backends/protocols/gamespy/chat/abstractions.py diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 95e0dc835..7e1ec72db 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -142,7 +142,7 @@ CREATE TABLE unispy.chat_channel_caches ( key_values jsonb, invited_nicks jsonb, update_time timestamp without time zone NOT NULL, - modes character varying NOT NULL, + modes jsonb NOT NULL, banned_nicks jsonb ); diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 741e6812f..8b1b60627 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -293,6 +293,7 @@ class ChatChannelUserCaches(Base): channel_name = Column( String, ForeignKey("chat_channel_caches.channel_name"), nullable=False ) + server_id = Column(UUID, nullable=False) update_time = Column(DateTime, nullable=False) # can we directly store the flags? is_voiceable = Column(Boolean, nullable=False) diff --git a/src/backends/protocols/gamespy/chat/abstractions.py b/src/backends/protocols/gamespy/chat/abstractions.py deleted file mode 100644 index b7848d3af..000000000 --- a/src/backends/protocols/gamespy/chat/abstractions.py +++ /dev/null @@ -1,98 +0,0 @@ -from backends.library.abstractions.contracts import ErrorResponse, RequestBase -from backends.library.abstractions.handler_base import HandlerBase -from backends.library.database.pg_orm import ( - ChatChannelCaches, - ChatChannelUserCaches, - ChatUserCaches, -) -from backends.protocols.gamespy.chat.requests import ChannelRequestBase -from frontends.gamespy.library.exceptions.general import UniSpyException -from frontends.gamespy.protocols.chat.aggregates.exceptions import ( - NoSuchChannelException, - NoSuchNickException, -) -import backends.protocols.gamespy.chat.data as data - - -class ChannelHandlerBase(HandlerBase): - _request: ChannelRequestBase - _user: ChatUserCaches | None - _channel: ChatChannelCaches | None - _channel_user: ChatChannelUserCaches | None - _is_broadcast: bool - - def __init__(self, request: RequestBase) -> None: - super().__init__(request) - self._user = None - self._channel = None - self._channel_user = None - self._is_broadcast = False - - def _get_user(self): - self._user = data.get_user_cache_by_ip_port( - self._request.client_ip, self._request.client_port, self._session - ) - - def _get_channel(self): - self._channel = data.get_channel_by_name( - self._request.channel_name, self._session - ) - - def _get_channel_user(self): - self._channel_user = data.get_channel_user_cache_by_name_and_ip_port( - self._request.channel_name, - self._request.client_ip, - self._request.client_port, - self._session, - ) - - def _check_user(self): - if self._user is None: - raise NoSuchNickException( - f"Can not find user with ip address: {self._request.client_ip}:{self._request.client_port}" - ) - - def _check_channel(self): - if self._channel is None: - raise NoSuchChannelException( - f"Can not find channel with name: {self._request.channel_name}" - ) - - def _check_channel_user(self): - if self._channel_user is None: - raise NoSuchNickException( - f"Can not find channel user with channel name: {self._request.channel_name}, ip address: {self._request.client_ip}:{self._request.client_port}" - ) - - def _request_check(self) -> None: - self._get_user() - self._check_user() - - self._get_channel() - self._check_channel() - - self._get_channel_user() - self._check_channel_user() - - def _boradcast(self) -> None: - # todo boradcast message here - raise NotImplementedError() - - def handle(self) -> None: - try: - self._request_check() - self._data_operate() - self._result_construct() - self._response_construct() - - self._boradcast() - except UniSpyException as ex: - self.logger.error(ex.message) - self._response = ErrorResponse(message=ex.message) - except Exception as ex: - self.logger.error(ex) - self._response = ErrorResponse(message=str(ex)) - - -class MessageHandlerBase(ChannelHandlerBase): - pass diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index 32feb17d7..4916332e5 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -232,7 +232,7 @@ def get_channel_user_caches_by_name( assert isinstance(channel_name, str) result: list[ChatChannelUserCaches] = ( - session.query(ChatChannelUserCaches.key_values) + session.query(ChatChannelUserCaches) .where(ChatChannelUserCaches.channel_name == channel_name) .all() ) # type:ignore diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index bf1fc7e16..4b4bd5e6f 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -9,15 +9,12 @@ ChatUserCaches, ChatChannelUserCaches, ) -from backends.protocols.gamespy.chat.abstractions import ( - ChannelHandlerBase, - MessageHandlerBase, -) from backends.protocols.gamespy.chat.helper import ChannelHelper, ChannelUserHelper import backends.protocols.gamespy.chat.data as data from backends.protocols.gamespy.chat.requests import ( AtmRequest, CdkeyRequest, + ChannelRequestBase, CryptRequest, GetCKeyRequest, GetKeyRequest, @@ -64,6 +61,7 @@ JoinResult, ListResult, NamesResult, + NamesResultData, NickResult, PartResult, SetChannelKeyResult, @@ -73,26 +71,104 @@ ) from sqlalchemy.orm import Session +# abstraction + class HandlerBase(hb.HandlerBase): - _cache: ChatUserCaches | None + _user: ChatUserCaches | None _session: Session + def __init__(self, request: RequestBase) -> None: + super().__init__(request) + self._user = None + def _request_check(self) -> None: - self._cache = data.get_user_cache_by_ip_port( + self._get_user() + self._check_user() + + def _get_user(self): + self._user = data.get_user_cache_by_ip_port( self._request.client_ip, self._request.client_port, self._session ) - if self._cache is None: - self._cache = ChatUserCaches( + if self._user is None: + self._user = ChatUserCaches( server_id=self._request.server_id, remote_ip_address=self._request.client_ip, remote_port=self._request.client_port, update_time=datetime.now(), nick_name=f"{self._request.client_ip}:{self._request.client_port}", ) - self._session.add(self._cache) + self._session.add(self._user) else: - self._cache.update_time = datetime.now() # type: ignore + self._user.update_time = datetime.now() # type: ignore + + def _check_user(self): + if self._user is None: + raise NoSuchNickException( + f"Can not find user with ip address: {self._request.client_ip}:{self._request.client_port}" + ) + + +class ChannelHandlerBase(HandlerBase): + _request: ChannelRequestBase + _channel: ChatChannelCaches | None + _channel_user: ChatChannelUserCaches | None + _is_broadcast: bool + + def __init__(self, request: RequestBase) -> None: + super().__init__(request) + self._channel = None + self._channel_user = None + self._is_broadcast = False + + def _request_check(self) -> None: + super()._request_check() + + self._get_channel() + self._check_channel() + + self._get_channel_user() + self._check_channel_user() + + def _get_channel(self): + self._channel = data.get_channel_by_name( + self._request.channel_name, self._session + ) + + def _get_channel_user(self): + self._channel_user = data.get_channel_user_cache_by_name_and_ip_port( + self._request.channel_name, + self._request.client_ip, + self._request.client_port, + self._session, + ) + + def _check_channel(self): + if self._channel is None: + raise NoSuchChannelException( + f"Can not find channel with name: {self._request.channel_name}" + ) + self._channel.update_time = datetime.now() # type: ignore + + def _check_channel_user(self): + if self._channel_user is None: + raise NoSuchNickException( + f"Can not find channel user with channel name: {self._request.channel_name}, ip address: {self._request.client_ip}:{self._request.client_port}" + ) + self._channel_user.update_time = datetime.now() # type: ignore + + def _boradcast(self) -> None: + # todo boradcast message here + raise NotImplementedError() + + def handle(self) -> None: + super().handle() + if self._is_broadcast: + self._boradcast() + + +class MessageHandlerBase(ChannelHandlerBase): + pass # region General @@ -111,8 +187,8 @@ class CryptHandler(HandlerBase): _request: CryptRequest def _data_operate(self) -> None: - assert self._cache is not None - self._cache.game_name = self._request.gamename # type: ignore + assert self._user is not None + self._user.game_name = self._request.gamename # type: ignore self._secret_key = data.get_secret_key_by_game_name( self._request.gamename, self._session ) @@ -221,21 +297,21 @@ class NickHandler(HandlerBase): def _data_operate(self) -> None: # cache = data.get_user_cache_by_nick_name("172.19.0.5:52986") - self._cache.nick_name = self._request.nick_name # type: ignore + self._user.nick_name = self._request.nick_name # type: ignore self._session.commit() cache = None # some game do not use CRYPT # todo check game with no encryption send nick or user request first if cache is None: # assign nick_name to current user - self._cache.nick_name = self._request.nick_name # type: ignore + self._user.nick_name = self._request.nick_name # type: ignore else: assert isinstance(cache.update_time, datetime) if (datetime.now() - cache.update_time).seconds > 120: # old profile delete it self._session.delete(cache) # data.remove_user_cache(cache) - self._cache.nick_name = self._request.nick_name # type: ignore + self._user.nick_name = self._request.nick_name # type: ignore return if ( @@ -249,7 +325,7 @@ def _data_operate(self) -> None: ) else: # update user cache - self._cache.nick_name = self._request.nick_name # type: ignore + self._user.nick_name = self._request.nick_name # type: ignore self._session.commit() def _result_construct(self) -> None: @@ -289,7 +365,7 @@ class UserHandler(HandlerBase): _request: UserRequest def _data_operate(self) -> None: - self._cache.user_name = self._request.user_name # type: ignore + self._user.user_name = self._request.user_name # type: ignore self._session.commit() @@ -339,6 +415,53 @@ def _result_construct(self) -> None: # region Channel + + +class JoinHandler(ChannelHandlerBase): + _request: JoinRequest + + def _request_check(self) -> None: + self._get_user() + self._check_user() + + self._get_channel() + + def _data_operate(self) -> None: + assert self._user is not None + + if self._channel is None: + self._channel = ChannelHelper.create( + server_id=self._request.server_id, + channel_name=self._request.channel_name, + password=self._request.password, + game_name=self._user.game_name, # type: ignore + room_name="", + topic="", + key_values={}, + update_time=datetime.now(), + creator=self._user.nick_name, # type: ignore + group_id=0, + max_num_user=100, + session=self._session, + ) + ChannelHelper.join(self._channel, self._user, self._session) + self._channel_users_info = ChannelHelper.get_channel_all_nicks( + self._channel, self._session + ) + + def _result_construct(self) -> None: + assert self._user is not None + assert isinstance(self._user.user_name, str) + assert isinstance(self._user.nick_name, str) + assert self._channel is not None + assert isinstance(self._channel.modes, list) + + self._result = JoinResult( + joiner_user_name=self._user.user_name, + joiner_nick_name=self._user.nick_name, + ) + + class GetChannelKeyHandler(ChannelHandlerBase): def _get_key_values(self): assert isinstance(self._channel, ChatChannelCaches) @@ -389,53 +512,6 @@ def _result_construct(self) -> None: ) -class JoinHandler(ChannelHandlerBase): - _request: JoinRequest - - def _request_check(self) -> None: - self._get_user() - self._check_user() - - self._get_channel() - - def _data_operate(self) -> None: - assert self._user is not None - assert isinstance(self._user.nick_name, str) - assert self._channel is not None - assert isinstance(self._channel.channel_name, str) - if self._channel is None: - self._channel = ChannelHelper.create( - server_id=self._request.server_id, - channel_name=self._request.channel_name, - password=self._request.password, - game_name=self._request.game_name, - room_name="", - topic="", - key_values={}, - update_time=datetime.now(), - modes=[], - creator=self._user.nick_name, - group_id=0, - max_num_user=100, - session=self._session, - ) - ChannelHelper.join(self._channel, self._user) - self._nicks = ChannelHelper.get_channel_all_nicks(self._channel, self._session) - - def _result_construct(self) -> None: - assert self._user is not None - assert isinstance(self._user.user_name, str) - assert isinstance(self._user.nick_name, str) - assert self._channel is not None - assert isinstance(self._channel.modes, list) - self._result = JoinResult( - joiner_user_name=self._user.user_name, - joiner_nick_name=self._user.nick_name, - all_channel_user_nicks=self._nicks, - channel_modes=self._channel.modes, - ) - - class KickHandler(ChannelHandlerBase): _kickee: ChatChannelUserCaches | None _request: KickRequest @@ -490,13 +566,21 @@ def _request_check(self) -> None: def _data_operate(self) -> None: assert self._channel - self._nicks = ChannelHelper.get_channel_all_nicks(self._channel, self._session) + self._channel_users_info = ChannelHelper.get_channel_all_nicks( + self._channel, self._session + ) def _result_construct(self) -> None: assert self._user assert isinstance(self._user.nick_name, str) + nicks = [] + for nick in self._channel_users_info: + nick.channel_name # yield nick + data = NamesResultData(**nick.__dict__) + nicks.append(data) + self._result = NamesResult( - all_channel_nicks=self._nicks, + channel_nicks=nicks, channel_name=self._request.channel_name, requester_nick_name=self._user.nick_name, ) diff --git a/src/backends/protocols/gamespy/chat/helper.py b/src/backends/protocols/gamespy/chat/helper.py index 2b4050456..fc5072381 100644 --- a/src/backends/protocols/gamespy/chat/helper.py +++ b/src/backends/protocols/gamespy/chat/helper.py @@ -46,7 +46,9 @@ def get_user_irc_prefix(user: ChatChannelUserCaches): class ChannelHelper: @staticmethod - def join(channel: ChatChannelCaches, user: ChatUserCaches) -> None: + def join( + channel: ChatChannelCaches, user: ChatUserCaches, session: Session + ) -> None: assert isinstance(channel, ChatChannelCaches) assert isinstance(channel.modes, list) assert isinstance(user.nick_name, str) @@ -70,6 +72,7 @@ def join(channel: ChatChannelCaches, user: ChatUserCaches) -> None: else: is_creator = False chan_user = ChatChannelUserCaches( + server_id=user.server_id, nick_name=user.nick_name, user_name=user.user_name, channel_name=channel.channel_name, @@ -80,9 +83,8 @@ def join(channel: ChatChannelCaches, user: ChatUserCaches) -> None: remote_ip_address=user.remote_ip_address, remote_port=user.remote_port, ) - with Session(ENGINE) as session: - session.add(chan_user) - session.commit() + session.add(chan_user) + session.commit() @staticmethod def quit(channel: ChatChannelCaches, quiter: ChatChannelUserCaches) -> None: @@ -144,7 +146,7 @@ def invite( def create( server_id: UUID, channel_name: str, - password: str, + password: str | None, game_name: str, room_name: str, topic: str, @@ -152,8 +154,8 @@ def create( max_num_user: int, key_values: dict, update_time: datetime, - modes: list, session: Session, + modes: list = [], creator: str | None = None, ) -> ChatChannelCaches: # check whether if channel exist, if not user is creator @@ -176,9 +178,8 @@ def create( creator=creator, modes=modes, ) - with Session(ENGINE) as session: - session.add(cache) - session.commit() + session.add(cache) + session.commit() return cache @@ -222,7 +223,7 @@ def change_modes( channel.password = request.password # type:ignore case ModeOperationType.REMOVE_CHANNEL_PASSWORD: if changer.is_channel_operator: # type:ignore - channel.password = "" # type:ignore + channel.password = None # type:ignore case ModeOperationType.ADD_CHANNEL_USER_LIMITS: channel.max_num_user = request.limit_number # type: ignore case ModeOperationType.REMOVE_CHANNEL_USER_LIMITS: @@ -237,6 +238,10 @@ def change_modes( if request.nick_name in list(channel.banned_nicks): channel.banned_nicks.remove(request.nick_name) case ModeOperationType.ADD_CHANNEL_OPERATOR: + if request.nick_name is None: + raise BadChannelKeyException( + "ADD_CHANNEL_OPERATOR require nick name" + ) u = data.get_channel_user_cache_by_name( request.channel_name, request.nick_name, session ) @@ -248,6 +253,10 @@ def change_modes( u.is_channel_operator = True # type: ignore session.commit() case ModeOperationType.REMOVE_CHANNEL_OPERATOR: + if request.nick_name is None: + raise BadChannelKeyException( + "REMOVE_CHANNEL_OPERATOR require nick name" + ) u = data.get_channel_user_cache_by_name( request.channel_name, request.nick_name, session ) @@ -255,11 +264,19 @@ def change_modes( session.commit() case ModeOperationType.ENABLE_USER_VOICE_PERMISSION: + if request.nick_name is None: + raise BadChannelKeyException( + "ENABLE_USER_VOICE_PERMISSION require nick name" + ) u = data.get_channel_user_cache_by_name( request.channel_name, request.nick_name, session ) u.is_voiceable = True # type: ignore case ModeOperationType.DISABLE_USER_VOICE_PERMISSION: + if request.nick_name is None: + raise BadChannelKeyException( + "DISABLE_USER_VOICE_PERMISSION require nick name" + ) u = data.get_channel_user_cache_by_name( request.channel_name, request.nick_name, session ) @@ -288,11 +305,8 @@ def get_all_user_nick_string(channel: ChatChannelCaches, session: Session) -> st @staticmethod def get_channel_all_nicks( channel: ChatChannelCaches, session: Session - ) -> list[str]: + ) ->list[ChatChannelUserCaches]: assert channel is not None assert isinstance(channel.channel_name, str) users = data.get_channel_user_caches_by_name(channel.channel_name, session) - nicks = [] - for user in users: - nicks.append(user.nick_name) - return nicks + return users diff --git a/src/backends/protocols/gamespy/chat/requests.py b/src/backends/protocols/gamespy/chat/requests.py index 2b21b9007..9b89ca916 100644 --- a/src/backends/protocols/gamespy/chat/requests.py +++ b/src/backends/protocols/gamespy/chat/requests.py @@ -136,8 +136,7 @@ class GetCKeyRequest(ChannelRequestBase): class JoinRequest(ChannelRequestBase): - password: str - game_name: str + password: str | None = None class KickRequest(ChannelRequestBase): @@ -147,12 +146,12 @@ class KickRequest(ChannelRequestBase): class ModeRequest(ChannelRequestBase): request_type: ModeRequestType - mode_operations: list[ModeOperationType] - nick_name: str - user_name: str - limit_number: int - mode_flag: str - password: str + mode_operations: list[ModeOperationType] = [] + limit_number: int | None = None + mode_flag: str | None = None + password: str | None = None + nick_name: str | None = None + user_name: str | None = None class NamesRequest(ChannelRequestBase): @@ -187,7 +186,6 @@ class TopicRequest(ChannelRequestBase): class MessageRequestBase(ChannelRequestBase): type: MessageType - nick_name: str message: str diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 137bdd45e..33dfd5355 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -6,6 +6,9 @@ GetKeyHandler, GetUdpRelayHandler, InviteHandler, + JoinHandler, + ModeHandler, + NamesHandler, NickHandler, UserHandler, ) @@ -71,8 +74,6 @@ async def websocket_endpoint(ws: WebSocket): print("Client disconnected") - - # region General @@ -112,7 +113,7 @@ def list_data(request: ListRequest): @router.post(f"{CHAT}/LoginHandler") def login(request: LoginRequest): - pass + raise NotImplementedError() @router.post(f"{CHAT}/NickHandler") @@ -124,12 +125,12 @@ def nick(request: NickRequest): @router.post(f"{CHAT}/QuitHandler") def quit(request: QuitRequest): - pass + raise NotImplementedError() @router.post(f"{CHAT}/SetKeyHandler") def set_key(request: SetKeyRequest): - pass + raise NotImplementedError() @router.post(f"{CHAT}/UserHandler") @@ -147,63 +148,69 @@ def user_ip(request: UserIPRequest): @router.post(f"{CHAT}/WhoHandler") def who(request: WhoRequest): - pass + raise NotImplementedError() @router.post(f"{CHAT}/WhoIsHandler") def whois(request: WhoIsRequest): - pass + raise NotImplementedError() # region channel @router.post(f"{CHAT}/GetChannelKeyHandler") def get_channel_key(request: GetChannelKeyRequest): - pass + raise NotImplementedError() @router.post(f"{CHAT}/GetCKeyHandler") def get_ckey(request: GetCKeyRequest): - pass + raise NotImplementedError() @router.post(f"{CHAT}/JoinHandler") def join(request: JoinRequest): - pass + handler = JoinHandler(request) + handler.handle() + return handler.response @router.post(f"{CHAT}/KickHandler") def kick(request: KickRequest): - pass + raise NotImplementedError() @router.post(f"{CHAT}/ModeHandler") def mode(request: ModeRequest): - pass + handler = ModeHandler(request) + handler.handle() + return handler.response @router.post(f"{CHAT}/NamesHandler") def names(request: NamesRequest): - pass + handler = NamesHandler(request) + handler.handle() + return handler.response @router.post(f"{CHAT}/PartHandler") def part(request: PartRequest): - pass + raise NotImplementedError() @router.post(f"{CHAT}/SetChannelKeyHandler") def set_channel_key(request: SetChannelKeyRequest): - pass + raise NotImplementedError() @router.post(f"{CHAT}/SetGroupHandler") def set_group(request: SetGroupRequest): - pass + raise NotImplementedError() @router.post(f"{CHAT}/TopicHandler") def topic(request: TopicRequest): - pass + raise NotImplementedError() @router.post(f"{CHAT}/CryptHandler") @@ -218,22 +225,22 @@ def crypt(request: CryptRequest): @router.post(f"{CHAT}/ATMHandler") def atm(request: AtmRequest): - pass + raise NotImplementedError() @router.post(f"{CHAT}/NoticeHandler") def notice(request: NoticeRequest): - pass + raise NotImplementedError() @router.post(f"{CHAT}/PrivateHandler") def private(request: PrivateRequest): - pass + raise NotImplementedError() @router.post(f"{CHAT}/UTMHandler") def utm(request: UtmRequest): - pass + raise NotImplementedError() if __name__ == "__main__": diff --git a/src/frontends/gamespy/library/abstractions/handler.py b/src/frontends/gamespy/library/abstractions/handler.py index a1db501ce..5fa093401 100644 --- a/src/frontends/gamespy/library/abstractions/handler.py +++ b/src/frontends/gamespy/library/abstractions/handler.py @@ -1,7 +1,7 @@ import json from frontends.gamespy.library.abstractions.client import ClientBase from frontends.gamespy.library.exceptions.general import UniSpyException -from typing import Type +from typing import Type, final import requests from frontends.gamespy.library.configs import CONFIG @@ -62,14 +62,13 @@ def handle(self) -> None: self._response_send() except Exception as ex: self._handle_exception(ex) - + def _request_check(self) -> None: """ - virtual function, can be override + raw request is nessesary param """ # if there is gamespy raw request we convert it to unispy request - if self._request.raw_request is not None: - self._request.parse() + self._request.parse() def _data_operate(self) -> None: """ diff --git a/src/frontends/gamespy/library/network/brockers.py b/src/frontends/gamespy/library/network/brockers.py index 8b82592eb..1bd9097ec 100644 --- a/src/frontends/gamespy/library/network/brockers.py +++ b/src/frontends/gamespy/library/network/brockers.py @@ -6,7 +6,7 @@ from frontends.gamespy.library.abstractions.brocker import BrockerBase from redis.client import PubSub -from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage from websockets.sync.client import connect, ClientConnection @@ -58,7 +58,8 @@ def _listen(self): message = self._subscriber.recv() self._call_back_func(message) except ConnectionClosed: - raise UniSpyException("websocket connection is not established") + GLOBAL_LOGGER.warn("backend websocket server is not avaliable") + # raise UniSpyException("websocket connection is not established") def unsubscribe(self): self._subscriber.close() diff --git a/src/frontends/gamespy/protocols/chat/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py index e5264c4e1..2c542d467 100644 --- a/src/frontends/gamespy/protocols/chat/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -191,6 +191,10 @@ def __init__(self, client: ClientBase, request: NickRequest): super().__init__(client, request) self._result_cls = NickResult + def _data_operate(self) -> None: + super()._data_operate() + self._client.info.nick_name = self._request.nick_name + def _response_construct(self) -> None: self._response = NickResponse(self._result) @@ -233,6 +237,10 @@ def __init__(self, client: ClientBase, request: UserRequest): super().__init__(client, request) self._is_fetching = False + def _data_operate(self) -> None: + super()._data_operate() + self._client.info.user_name = self._request.user_name + def _request_check(self) -> None: super()._request_check() @@ -327,6 +335,18 @@ def __init__(self, client: ClientBase, request: JoinRequest): def _response_construct(self): self._response = JoinResponse(self._request, self._result) + def _response_send(self) -> None: + super()._response_send() + names_raw = NamesRequest.build(self._request.channel_name) + names_req = NamesRequest(names_raw) + names_handler = NamesHandler(self._client, names_req) + names_handler.handle() + + mode_raw = ModeRequest.build(self._request.channel_name) + mode_req = ModeRequest(mode_raw) + mode_handler = ModeHandler(self._client, mode_req) + mode_handler.handle() + class KickHandler(ChannelHandlerBase): _request: KickRequest @@ -352,11 +372,11 @@ def __init__(self, client: ClientBase, request: ModeRequest): def _request_check(self) -> None: super()._request_check() - if self._request.type == ModeRequestType.SET_CHANNEL_MODES: + if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: self._is_fetching = False def _response_construct(self): - if self._request.type in [ + if self._request.request_type in [ ModeRequestType.GET_CHANNEL_AND_USER_MODES, ModeRequestType.GET_CHANNEL_MODES, ]: @@ -370,6 +390,7 @@ class NamesHandler(ChannelHandlerBase): def __init__(self, client: ClientBase, request: NamesRequest): assert isinstance(request, NamesRequest) super().__init__(client, request) + self._result_cls = NamesResult def _response_construct(self): self._response = NamesResponse(self._request, self._result) diff --git a/src/frontends/gamespy/protocols/chat/contracts/requests.py b/src/frontends/gamespy/protocols/chat/contracts/requests.py index 2e0cb0819..5a139a73d 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/requests.py +++ b/src/frontends/gamespy/protocols/chat/contracts/requests.py @@ -394,7 +394,7 @@ class ModeRequest(ChannelRequestBase): # "MODE +l " # "MODE -l" - # "MODE +b" actually we do not care about this request + # "MODE +b *!*@" actually we do not care about this request # "MODE +/-b " # "MODE +/-co " @@ -402,165 +402,97 @@ class ModeRequest(ChannelRequestBase): # "MODE " # "MODE " - type: ModeRequestType + request_type: ModeRequestType mode_operations: list[ModeOperationType] nick_name: str user_name: str limit_number: int mode_flag: str - password: str - - def __init__(self, raw_request: str) -> None: - super().__init__(raw_request) - self.mode_operations = [] + password: str | None def parse(self): - if self.raw_request is None: - return super().parse() + self.mode_operations = [] if len(self._cmd_params) == 1: - self.type = ModeRequestType.GET_CHANNEL_MODES + self.request_type = ModeRequestType.GET_CHANNEL_MODES elif len(self._cmd_params) == 2 or len(self._cmd_params) == 3: - self.type = ModeRequestType.SET_CHANNEL_MODES + self.request_type = ModeRequestType.SET_CHANNEL_MODES self.mode_flag = self._cmd_params[1] modeFlags = [s for s in re.split(r"(?=\+|\-)", self.mode_flag) if s.strip()] modeFlags = list(filter(None, modeFlags)) for flag in modeFlags: - match flag: - case "+e": - self.mode_operations.append( - ModeOperationType.SET_OPERATOR_ABEY_CHANNEL_LIMITS - ) - case "-e": - self.mode_operations.append( - ModeOperationType.REMOVE_OPERATOR_ABEY_CHANNEL_LIMITS - ) - case "+t": - self.mode_operations.append( - ModeOperationType.SET_TOPIC_CHANGE_BY_OPERATOR_FLAG - ) - case "-t": - self.mode_operations.append( - ModeOperationType.REMOVE_TOPIC_CHANGE_BY_OPERATOR_FLAG - ) - case "+n": - self.mode_operations.append( - ModeOperationType.ENABLE_EXTERNAL_MESSAGES_FLAG - ) - case "-n": - self.mode_operations.append( - ModeOperationType.DISABLE_EXTERNAL_MESSAGES_FLAG - ) - case "+m": - self.mode_operations.append( - ModeOperationType.SET_MODERATED_CHANNEL_FLAG - ) - case "-m": - self.mode_operations.append( - ModeOperationType.REMOVE_MODERATED_CHANNEL_FLAG - ) - case "+s": - self.mode_operations.append( - ModeOperationType.SET_SECRET_CHANNEL_FLAG - ) - - case "-s": - self.mode_operations.append( - ModeOperationType.REMOVE_SECRET_CHANNEL_FLAG - ) - case "+i": - self.mode_operations.append(ModeOperationType.SET_INVITED_ONLY) - case "-i": - self.mode_operations.append( - ModeOperationType.REMOVE_INVITED_ONLY - ) - case "-p": - self.mode_operations.append( - ModeOperationType.REMOVE_PRIVATE_CHANNEL_FLAG - ) - case "+p": - self.mode_operations.append( - ModeOperationType.SET_PRIVATE_CHANNEL_FLAG - ) - case "+q": - self.mode_operations.append( - ModeOperationType.ENABLE_USER_QUIET_FLAG - ) - case "-q": - self.mode_operations.append( - ModeOperationType.DISABLE_USER_QUIET_FLAG - ) - case "+k": - self.mode_operations.append( - ModeOperationType.ADD_CHANNEL_PASSWORD - ) - case "-k": - self.mode_operations.append( - ModeOperationType.REMOVE_CHANNEL_PASSWORD - ) - - case "+l": + conv_flag = ModeOperationType(flag) + match conv_flag: + case ( + ModeOperationType.SET_OPERATOR_ABEY_CHANNEL_LIMITS + | ModeOperationType.REMOVE_OPERATOR_ABEY_CHANNEL_LIMITS + | ModeOperationType.SET_TOPIC_CHANGE_BY_OPERATOR_FLAG + | ModeOperationType.REMOVE_TOPIC_CHANGE_BY_OPERATOR_FLAG + | ModeOperationType.ENABLE_EXTERNAL_MESSAGES_FLAG + | ModeOperationType.DISABLE_EXTERNAL_MESSAGES_FLAG + | ModeOperationType.SET_MODERATED_CHANNEL_FLAG + | ModeOperationType.REMOVE_MODERATED_CHANNEL_FLAG + | ModeOperationType.SET_SECRET_CHANNEL_FLAG + | ModeOperationType.REMOVE_SECRET_CHANNEL_FLAG + | ModeOperationType.SET_INVITED_ONLY + | ModeOperationType.REMOVE_INVITED_ONLY + | ModeOperationType.SET_PRIVATE_CHANNEL_FLAG + | ModeOperationType.REMOVE_PRIVATE_CHANNEL_FLAG + | ModeOperationType.ENABLE_USER_QUIET_FLAG + | ModeOperationType.DISABLE_USER_QUIET_FLAG + | ModeOperationType.ADD_CHANNEL_PASSWORD + | ModeOperationType.REMOVE_CHANNEL_PASSWORD + ): + self.mode_operations.append(conv_flag) + + case ModeOperationType.ADD_CHANNEL_USER_LIMITS: self.channel_name = self._cmd_params[0] self.limit_number = int(self._cmd_params[2]) - self.mode_operations.append( - ModeOperationType.ADD_CHANNEL_USER_LIMITS - ) - case "-l": + self.mode_operations.append(conv_flag) + case ( + ModeOperationType.REMOVE_CHANNEL_USER_LIMITS + | ModeOperationType.REMOVE_BAN_ON_USER + ): self.channel_name = self._cmd_params[0] - self.mode_operations.append( - ModeOperationType.REMOVE_CHANNEL_USER_LIMITS - ) - case "+b": + self.mode_operations.append(conv_flag) + case ( + ModeOperationType.ADD_BAN_ON_USER + | ModeOperationType.GET_BANNED_USERS + ): self.channel_name = self._cmd_params[0] if len(self._cmd_params) == 3: self.nick_name = self._cmd_params[2] - self.mode_operations.append( - ModeOperationType.ADD_BAN_ON_USER - ) - else: - self.mode_operations.append( - ModeOperationType.GET_BANNED_USERS - ) - case "-b": - self.channel_name = self._cmd_params[0] - self.mode_operations.append( - ModeOperationType.REMOVE_BAN_ON_USER - ) - case "+co": - self.channel_name = self._cmd_params[0] - self.user_name = self._cmd_params[2] - self.mode_operations.append( - ModeOperationType.ADD_CHANNEL_OPERATOR - ) - case "-co": + self.mode_operations.append(conv_flag) + case ( + ModeOperationType.ADD_CHANNEL_OPERATOR + | ModeOperationType.REMOVE_CHANNEL_OPERATOR + | ModeOperationType.ENABLE_USER_VOICE_PERMISSION + | ModeOperationType.DISABLE_USER_VOICE_PERMISSION + ): self.channel_name = self._cmd_params[0] self.user_name = self._cmd_params[2] - self.mode_operations.append( - ModeOperationType.REMOVE_CHANNEL_OPERATOR - ) - case "+cv": - self.channel_name = self._cmd_params[0] - self.user_name = self._cmd_params[2] - self.mode_operations.append( - ModeOperationType.ENABLE_USER_VOICE_PERMISSION - ) - case "-cv": - self.channel_name = self._cmd_params[0] - self.user_name = self._cmd_params[2] - self.mode_operations.append( - ModeOperationType.DISABLE_USER_VOICE_PERMISSION - ) - # Add more cases for other flags following the same pattern - case _: - raise ChatException("Unknown mode request type.") + self.mode_operations.append(conv_flag) + else: raise ChatException("The number of IRC parameters are incorrect.") + @staticmethod + def build(channel_name: str): + """ + build the irc request for get the channel modes + """ + raw = f"MODE {channel_name}" + return raw + class NamesRequest(ChannelRequestBase): - def __init__(self, raw_request: str | None = None) -> None: - if raw_request is not None: - super().__init__(raw_request) + @staticmethod + def build(channel_name: str): + """ + build raw request to get channel mode + """ + raw = f"NAMES {channel_name}" + return raw class PartRequest(ChannelRequestBase): diff --git a/src/frontends/gamespy/protocols/chat/contracts/responses.py b/src/frontends/gamespy/protocols/chat/contracts/responses.py index 481b6dc73..272ffd6a7 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/responses.py +++ b/src/frontends/gamespy/protocols/chat/contracts/responses.py @@ -11,6 +11,7 @@ KickResult, ModeResult, NamesResult, + NamesResultData, PartResult, SetChannelKeyResult, TopicResult, @@ -69,9 +70,7 @@ def __init__(self) -> None: pass def build(self) -> None: - self.sending_buffer = ( - f":{SERVER_DOMAIN} {ResponseCode.SECUREKEY.value} * {CLIENT_KEY} {SERVER_KEY}\r\n" # noqa - ) + self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.SECUREKEY.value} * {CLIENT_KEY} {SERVER_KEY}\r\n" # noqa class GetKeyResponse(ResponseBase): @@ -245,9 +244,7 @@ def __init__(self, request: JoinRequest, result: JoinResult) -> None: def build(self) -> None: joiner_irc_prefix = f"{self._result.joiner_nick_name}!{self._result.joiner_user_name}@{SERVER_DOMAIN}" - self.sending_buffer = ( - f"{joiner_irc_prefix} {ResponseCode.JOIN.value} {self._request.channel_name}\r\n" - ) + self.sending_buffer = f"{joiner_irc_prefix} {ResponseCode.JOIN.value} {self._request.channel_name}\r\n" class KickResponse(ResponseBase): @@ -274,7 +271,7 @@ def __init__(self, request: ModeRequest, result: ModeResult) -> None: super().__init__(request, result) def build(self) -> None: - if self._request.type == ModeRequestType.GET_CHANNEL_MODES: + if self._request.request_type == ModeRequestType.GET_CHANNEL_MODES: self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.CHANNELMODEIS.value} * {self._result.channel_modes} {self._result.channel_modes}\r\n" @@ -287,8 +284,25 @@ def __init__(self, request: NamesRequest, result: NamesResult) -> None: assert isinstance(result, NamesResult) super().__init__(request, result) + @staticmethod + def get_nicks_list(data: list[NamesResultData]): + nicks_str = "" + for i in range(len(data)): + user = data[i] + assert isinstance(user.is_channel_creator, bool) + assert isinstance(user.nick_name, str) + if user.is_channel_creator: + nicks_str += f"@{user.nick_name}" + else: + nicks_str += user.nick_name + # use space as seperator + if i != (len(data) - 1): + nicks_str += " " + return nicks_str + def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.NAMEREPLY.value} {self._result.requester_nick_name} = {self._result.channel_name} :{self._result.all_channel_nicks}\r\n" + nicks_str = NamesResponse.get_nicks_list(self._result.channel_nicks) + self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.NAMEREPLY.value} {self._result.requester_nick_name} = {self._result.channel_name} :{nicks_str}\r\n" self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDOFNAMES.value} {self._result.requester_nick_name} {self._result.channel_name} :End of NAMES list. \r\n" @@ -402,6 +416,6 @@ def __init__(self, request: UTMRequest, result: UTMResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} {ResponseCode.UTM.value} { - self._result.target_name - } :{self._request.message}" + self.sending_buffer = f":{self._result.user_irc_prefix} { + ResponseCode.UTM.value + } {self._result.target_name} :{self._request.message}" diff --git a/src/frontends/gamespy/protocols/chat/contracts/results.py b/src/frontends/gamespy/protocols/chat/contracts/results.py index 2043f754b..28729cf03 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/results.py +++ b/src/frontends/gamespy/protocols/chat/contracts/results.py @@ -95,30 +95,36 @@ class GetCKeyInfos(BaseModel): channel_name: str -class JoinResult(ResultBase): - joiner_user_name: str +class ModeResult(ResultBase): + channel_name: str + channel_modes: str joiner_nick_name: str - all_channel_user_nicks: list[str] - channel_modes: list[str] -class KickResult(ResultBase): - channel_name: str - kicker_nick_name: str - kicker_user_name: str - kickee_nick_name: str +class NamesResultData(BaseModel): + nick_name: str + is_channel_creator: bool = False + is_channel_operator: bool = False -class ModeResult(ResultBase): +class NamesResult(ResultBase): + channel_nicks: list[NamesResultData] channel_name: str - channel_modes: str + requester_nick_name: str + + +class JoinResult(ResultBase): + joiner_user_name: str joiner_nick_name: str + # channel_nicks_data: list[NamesResultData] + # channel_modes: list[str] -class NamesResult(ResultBase): - all_channel_nicks: list[str] +class KickResult(ResultBase): channel_name: str - requester_nick_name: str + kicker_nick_name: str + kicker_user_name: str + kickee_nick_name: str class PartResult(ResultBase): diff --git a/src/frontends/tests/gamespy/chat/game_tests.py b/src/frontends/tests/gamespy/chat/game_tests.py index ac63a924d..dce6e768e 100644 --- a/src/frontends/tests/gamespy/chat/game_tests.py +++ b/src/frontends/tests/gamespy/chat/game_tests.py @@ -6,25 +6,27 @@ class GameTests(unittest.TestCase): - @responses.activate def test_civilization4(self): - raws = ["USRIP", - "USER X419pGl4sX|18 127.0.0.1 peerchat.gamespy.com :aa3041ada9385b28fc4d4e47db288769", - "NICK a1701-5", - "CDKEY 81123-67814-77652-27631-11723-47707-22638-10701", - "JOIN #GSP!anno1701 ", - "MODE #GSP!anno1701", - "GETCKEY #GSP!anno1701 * 008 0 :\\b_flags", "WHO a1701-5", - "JOIN #GSP!anno1701!M9zK0KJaKM ", - "MODE #GSP!anno1701!M9zK0KJaKM", - "SETCKEY #GSP!anno1701 a1701-5 :\\b_flags\\s", - "SETCKEY #GSP!anno1701!M9zK0KJaKM a1701-5 :\\b_flags\\sh", - "GETCKEY #GSP!anno1701!M9zK0KJaKM * 009 0 :\\b_flags", - "TOPIC #GSP!anno1701!M9zK0KJaKM :test", - "MODE #GSP!anno1701!M9zK0KJaKM +l 4", - "MODE #GSP!anno1701!M9zK0KJaKM -i-p-s+m+n+t+l+e 4", - "PART #GSP!anno1701 :"] + raws = [ + "USRIP", + "USER X419pGl4sX|18 127.0.0.1 peerchat.gamespy.com :aa3041ada9385b28fc4d4e47db288769", + "NICK a1701-5", + "CDKEY 81123-67814-77652-27631-11723-47707-22638-10701", + "JOIN #GSP!anno1701 ", + "MODE #GSP!anno1701", + "GETCKEY #GSP!anno1701 * 008 0 :\\b_flags", + "WHO a1701-5", + "JOIN #GSP!anno1701!M9zK0KJaKM ", + "MODE #GSP!anno1701!M9zK0KJaKM", + "SETCKEY #GSP!anno1701 a1701-5 :\\b_flags\\s", + "SETCKEY #GSP!anno1701!M9zK0KJaKM a1701-5 :\\b_flags\\sh", + "GETCKEY #GSP!anno1701!M9zK0KJaKM * 009 0 :\\b_flags", + "TOPIC #GSP!anno1701!M9zK0KJaKM :test", + "MODE #GSP!anno1701!M9zK0KJaKM +l 4", + "MODE #GSP!anno1701!M9zK0KJaKM -i-p-s+m+n+t+l+e 4", + "PART #GSP!anno1701 :", + ] client = create_client() for raw in raws: client.on_received(raw.encode()) @@ -41,8 +43,8 @@ def test_worm3d(self): "GETCKEY #GPG!622 * 024 0 :\\username\\b_flags\r\n", "JOIN #GSP!worms3!Ml4lz344lM\r\n", "MODE #GSP!worms3!Ml4lz344lM\r\n", - "SETCKEY #GPG!622 worms10 :\\b_flags\\s"+"\r\n", - "SETCKEY #GSP!worms3!Ml4lz344lM worms10 :\\b_flags\\sh"+"\r\n", + "SETCKEY #GPG!622 worms10 :\\b_flags\\s" + "\r\n", + "SETCKEY #GSP!worms3!Ml4lz344lM worms10 :\\b_flags\\sh" + "\r\n", "GETCKEY #GSP!worms3!Ml4lz344lM * 025 0 :\\username\\b_flags\r\n", "TOPIC #GSP!worms3!Ml4lz344lM :tesr\r\n", "MODE #GSP!worms3!Ml4lz344lM +l 2\r\n", @@ -57,19 +59,23 @@ def test_worm3d(self): "UTM #GSP!worms3!Ml4lz344lM :MDM |Obj|3|Land.Time|0|LogicalSeed|3891226431|GraphicalSeed|3269271590|Land.RealSeed|3281489942|Land.Theme|Pirate.Lumps|LevelToUse|FE.Level.RandomLand|Land.Ind|0|Wormpot.Reel1|17|Wormpot.Reel2|17|Wormpot.Reel3|17|TimeStamp|6206364\r\n", "UTM #GSP!worms3!Ml4lz344lM :TDM aA\r\n", "UTM #GSP!worms3!Ml4lz344lM :SDM ASFE.Scheme.StandardCUnAACADCBBCACBBFFBKBB8C/C3C!A!A*C*C Client: conn = ConnectionMock( handler=handler, config=config, t_client=ClientMock, logger=logger ) + create_mock_url(config, CryptHandler, CryptResult(secret_key="test").model_dump()) create_mock_url( config, LoginHandler, LoginResult(profile_id=1, user_id=1).model_dump() ) + create_mock_url( + config, + NamesHandler, + NamesResult( + channel_nicks=[NamesResultData(nick_name="test")], + channel_name="test", + requester_nick_name="test1", + ).model_dump(), + ) create_mock_url(config, QuitHandler, {"message": "ok"}) create_mock_url(config, UserIPHandler, {"message": "ok"}) create_mock_url( @@ -80,8 +95,6 @@ def create_client() -> Client: JoinResult( joiner_nick_name="xiaojiuwo", joiner_user_name="unispy", - all_channel_user_nicks=["unispy1", "unispy2"], - channel_modes=["+q"], ).model_dump(), ) create_mock_url(config, UserHandler, {"message": "ok"}) From 6983886129b030267566368072be1f38456d3489 Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Thu, 31 Jul 2025 07:17:01 +0000 Subject: [PATCH 199/231] fix(chat): mode, privmsg functionalities --- common/UniSpy_pg.sql | 9 +- .../library/abstractions/websocket_manager.py | 20 +++- src/backends/library/database/pg_orm.py | 6 +- .../protocols/gamespy/chat/brocker_manager.py | 87 +------------- src/backends/protocols/gamespy/chat/data.py | 19 ++- .../protocols/gamespy/chat/handlers.py | 61 +++++++--- src/backends/protocols/gamespy/chat/helper.py | 80 ++++++------- .../protocols/gamespy/chat/requests.py | 9 +- src/backends/routers/gamespy/chat.py | 13 +-- src/backends/tests/gamespy/chat/lib_tests.py | 96 +++++++-------- .../gamespy/library/abstractions/handler.py | 2 +- .../library/abstractions/server_launcher.py | 2 - .../gamespy/library/network/brockers.py | 5 +- .../protocols/chat/abstractions/contract.py | 2 +- .../protocols/chat/abstractions/handler.py | 13 ++- .../protocols/chat/aggregates/enums.py | 83 ++++++++----- .../protocols/chat/applications/client.py | 11 +- .../protocols/chat/applications/handlers.py | 37 ++---- .../protocols/chat/contracts/requests.py | 109 ++++++++---------- .../protocols/chat/contracts/responses.py | 17 ++- .../protocols/chat/contracts/results.py | 4 +- .../query_report/v2/applications/handlers.py | 2 +- .../query_report/v2/contracts/requests.py | 2 +- .../query_report/v2/contracts/responses.py | 2 +- .../query_report/v2/contracts/results.py | 2 +- .../tests/gamespy/chat/mock_objects.py | 2 +- .../tests/gamespy/chat/request_tests.py | 30 +++-- .../gamespy/query_report/mock_objects.py | 2 +- .../gamespy/server_browser/mock_objects.py | 2 +- 29 files changed, 357 insertions(+), 372 deletions(-) diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 7e1ec72db..774649942 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -119,8 +119,9 @@ CREATE TABLE unispy.chat_user_caches ( nick_name character varying PRIMARY KEY NOT NULL, game_name character varying, user_name character varying, - remote_ip_address inet NOT NULL, + remote_ip inet NOT NULL, remote_port INTEGER NOT NULL, + websocket_address character varying NOT NULL, key_value jsonb, update_time timestamp without time zone NOT NULL ); @@ -166,7 +167,7 @@ CREATE TABLE unispy.chat_channel_user_caches ( is_voiceable boolean NOT NULL, is_channel_operator boolean NOT NULL, is_channel_creator boolean NOT NULL, - remote_ip_address inet NOT NULL, + remote_ip inet NOT NULL, remote_port integer NOT NULL, key_values jsonb ); @@ -770,7 +771,7 @@ COPY unispy.chat_channel_caches (channel_name, server_id, game_name, room_name, -- Data for Name: chat_nick_caches; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.chat_user_caches (server_id, nick_name, game_name, user_name, remote_ip_address, remote_port, key_value, update_time) FROM stdin; +COPY unispy.chat_user_caches (server_id, nick_name, game_name, user_name, remote_ip, remote_port,websocket_address, key_value, update_time) FROM stdin; \. @@ -778,7 +779,7 @@ COPY unispy.chat_user_caches (server_id, nick_name, game_name, user_name, remote -- Data for Name: chat_user_caches; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.chat_channel_user_caches (nick_name, channel_name, server_id, user_name, update_time, is_voiceable, is_channel_operator, is_channel_creator, remote_ip_address, remote_port, key_values) FROM stdin; +COPY unispy.chat_channel_user_caches (nick_name, channel_name, server_id, user_name, update_time, is_voiceable, is_channel_operator, is_channel_creator, remote_ip, remote_port, key_values) FROM stdin; \. diff --git a/src/backends/library/abstractions/websocket_manager.py b/src/backends/library/abstractions/websocket_manager.py index 9187024ed..98b3e1f8e 100644 --- a/src/backends/library/abstractions/websocket_manager.py +++ b/src/backends/library/abstractions/websocket_manager.py @@ -2,6 +2,7 @@ from uuid import UUID from fastapi import WebSocket +from backends.protocols.gamespy.chat.requests import RequestBase from frontends.gamespy.library.exceptions.general import UniSpyException @@ -63,7 +64,18 @@ def disconnect(self, ws: WebSocket): if temp.ip_port in self.client_pool: del self.client_pool[temp.ip_port] - def broadcast(self, message: dict): - loop = asyncio.get_event_loop() - for client in self.client_pool.values(): - loop.create_task(client.ws.send_json(message)) + def broadcast(self, message: RequestBase, ws: list[str] | None = None): + try: + loop = asyncio.get_running_loop() + except RuntimeError: # No event loop is running + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + if ws is None: + clients = self.client_pool.values() + else: + clients = [] + for w in ws: + clients.append(self.client_pool[w]) + + for client in clients: + loop.create_task(client.ws.send_json(message.model_dump())) diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 8b1b60627..6f15f4c7c 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -257,7 +257,6 @@ class ChatChannelCaches(Base): update_time = Column(DateTime, nullable=False) modes = Column(JSONB, default=[]) banned_nicks = Column(JSONB, default=[]) - class ChatUserCaches(Base): @@ -270,8 +269,9 @@ class ChatUserCaches(Base): nick_name = Column(String, primary_key=True, nullable=True) user_name = Column(String, nullable=True) game_name = Column(String, nullable=True) - remote_ip_address = Column(INET, nullable=False) + remote_ip = Column(INET, nullable=False) remote_port = Column(Integer, nullable=False) + websocket_address = Column(String, nullable=False) key_value = Column(JSONB, default={}) update_time = Column(DateTime, nullable=False) @@ -299,7 +299,7 @@ class ChatChannelUserCaches(Base): is_voiceable = Column(Boolean, nullable=False) is_channel_operator = Column(Boolean, nullable=False) is_channel_creator = Column(Boolean, nullable=False) - remote_ip_address = Column(INET, nullable=False) + remote_ip = Column(INET, nullable=False) remote_port = Column(Integer, nullable=False) key_values = Column(JSONB, default={}) diff --git a/src/backends/protocols/gamespy/chat/brocker_manager.py b/src/backends/protocols/gamespy/chat/brocker_manager.py index fe8550c15..d925f3dad 100644 --- a/src/backends/protocols/gamespy/chat/brocker_manager.py +++ b/src/backends/protocols/gamespy/chat/brocker_manager.py @@ -1,90 +1,7 @@ -import asyncio -from typing import cast -from backends.library.abstractions.websocket_manager import ( - WebSocketClient, - WebSocketManager, -) +from backends.library.abstractions.websocket_manager import WebSocketManager -# from library.network.brockers import RedisBrocker -from fastapi import WebSocket -from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage - - -class ChatWebSocketClient(WebSocketClient): - channels: list[str] - - def __init__(self, ws: WebSocket) -> None: - super().__init__(ws) - self.channels = [] - - -class ChatWebSocketManager(WebSocketManager): - channel_info: dict[str, dict[str, ChatWebSocketClient]] - """ - [channel_name,[ip_port,WebSocket]] - """ - client_pool: dict[str, ChatWebSocketClient] - """ - [ip_port,ChatWebSocketClient] - """ - - def __init__(self) -> None: - super().__init__() - self.channel_info = {} - - def create_client(self, ws: WebSocket) -> WebSocketClient: - client = ChatWebSocketClient(ws) - return client - - def add_to_channel(self, channel_name: str, ws: WebSocket): - client = self.get_client(ws) - client = cast(ChatWebSocketClient, client) - # add channel to client.channels - if channel_name not in client.channels: - client.channels.append(channel_name) - # add client to channel_info - if channel_name not in self.channel_info: - self.channel_info[channel_name] = {} - channel = self.channel_info[channel_name] - channel[client.ip_port] = client - - def remove_from_channel(self, channel_name, ws: WebSocket): - # remove from channel_info - if channel_name not in self.channel_info: - return - - client = self.get_client(ws) - client = cast(ChatWebSocketClient, client) - - if client.ip_port in self.channel_info[channel_name]: - del self.channel_info[channel_name][client.ip_port] - # when channel do not have player we remove it - if len(self.channel_info[channel_name]) == 0: - del self.channel_info[channel_name] - # remove from client.channels - if channel_name not in client.channels: - return - client.channels.remove(channel_name) - - def disconnect(self, ws: WebSocket): - client = self.get_client(ws) - client = cast(ChatWebSocketClient, client) - for channel_name in client.channels: - if channel_name in self.channel_info: - channel_dict = self.channel_info[channel_name] - if client.ip_port in channel_dict: - del channel_dict[client.ip_port] - super().disconnect(ws) - - def channel_broad_cast(self, name: str, message: BrockerMessage): - if name in self.channel_info: - channel = self.channel_info[name] - for nick, ws in channel.items(): - asyncio.run(ws.ws.send_json(message.model_dump())) - - -MANAGER = ChatWebSocketManager() +MANAGER = WebSocketManager() # create redis pubsub to share message cross all backends # currently we simply implement without redis pubsub diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index 4916332e5..338d8d9d8 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -3,7 +3,6 @@ from sqlalchemy import Column, func from backends.library.database.pg_orm import ( - ENGINE, ChatChannelCaches, ChatUserCaches, ChatChannelUserCaches, @@ -186,7 +185,7 @@ def get_channel_by_name_and_ip_port( .join(ChatChannelUserCaches) .where( ChatChannelUserCaches.channel_name == channel_name, - ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_ip == ip, ChatChannelUserCaches.remote_port == port, ) .first() @@ -218,7 +217,7 @@ def get_channel_user_cache_by_name_and_ip_port( session.query(ChatChannelUserCaches) .where( ChatChannelUserCaches.channel_name == channel_name, - ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_ip == ip, ChatChannelUserCaches.remote_port == port, ) .first() @@ -266,7 +265,7 @@ def get_user_cache_by_ip_port( result = ( session.query(ChatUserCaches) .where( - ChatUserCaches.remote_ip_address == ip, + ChatUserCaches.remote_ip == ip, ChatUserCaches.remote_port == port, ) .first() @@ -297,7 +296,7 @@ def get_whois_result(nick: str, session: Session) -> tuple: info.nick_name, info.user_name, info.nick_name, - info.remote_ip_address, + info.remote_ip, channels, ) # type:ignore @@ -307,7 +306,7 @@ def remove_user_caches_by_ip_port(ip: str, port: int, session: Session): assert isinstance(port, int) session.query(ChatChannelUserCaches).where( - ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_ip == ip, ChatChannelUserCaches.remote_port == port, ).delete() @@ -335,7 +334,7 @@ def is_user_exist(ip: str, port: int, session: Session) -> bool: user_count = ( session.query(ChatChannelUserCaches) .where( - ChatChannelUserCaches.remote_ip_address == ip, + ChatChannelUserCaches.remote_ip == ip, ChatChannelUserCaches.remote_port == port, ) .count() @@ -431,7 +430,7 @@ def get_channel_user_caches(channel_name: str, session: Session) -> list[dict]: temp = {} temp["channel_name"] = r.channel_name temp["user_name"] = r.user_name - temp["public_ip_addr"] = r.remote_ip_address + temp["public_ip_addr"] = r.remote_ip temp["nick_name"] = r.nick_name data.append(temp) return data @@ -449,7 +448,7 @@ def get_channel_user_cache_by_ip(ip: str, port: int, session: Session) -> list[d ChatUserCaches.user_name == ChatChannelUserCaches.user_name, ) .where( - ChatUserCaches.remote_ip_address == ip, + ChatUserCaches.remote_ip == ip, ChatUserCaches.remote_port == port, ) .all() @@ -459,7 +458,7 @@ def get_channel_user_cache_by_ip(ip: str, port: int, session: Session) -> list[d temp = {} temp["channel_name"] = r.channel_name temp["user_name"] = r.user_name - temp["public_ip_addr"] = r.remote_ip_address + temp["public_ip_addr"] = r.remote_ip temp["nick_name"] = r.nick_name data.append(temp) return data diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index 4b4bd5e6f..c7ba01149 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -1,14 +1,15 @@ from datetime import datetime from typing import TYPE_CHECKING, cast -from backends.library.abstractions.contracts import RequestBase import backends.library.abstractions.handler_base as hb +from backends.library.abstractions.websocket_manager import WebSocketManager from backends.library.database.pg_orm import ( ChatChannelCaches, ChatUserCaches, ChatChannelUserCaches, ) +from backends.protocols.gamespy.chat.brocker_manager import MANAGER from backends.protocols.gamespy.chat.helper import ChannelHelper, ChannelUserHelper import backends.protocols.gamespy.chat.data as data from backends.protocols.gamespy.chat.requests import ( @@ -31,6 +32,7 @@ PartRequest, PrivateRequest, QuitRequest, + RequestBase, SetCKeyRequest, SetChannelKeyRequest, SetKeyRequest, @@ -43,6 +45,7 @@ from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.protocols.chat.aggregates.enums import ( GetKeyRequestType, + ModeRequestType, TopicRequestType, WhoRequestType, ) @@ -60,6 +63,7 @@ GetKeyResult, JoinResult, ListResult, + ModeResult, NamesResult, NamesResultData, NickResult, @@ -75,6 +79,7 @@ class HandlerBase(hb.HandlerBase): + _request: RequestBase _user: ChatUserCaches | None _session: Session @@ -93,10 +98,11 @@ def _get_user(self): if self._user is None: self._user = ChatUserCaches( server_id=self._request.server_id, - remote_ip_address=self._request.client_ip, + remote_ip=self._request.client_ip, remote_port=self._request.client_port, update_time=datetime.now(), nick_name=f"{self._request.client_ip}:{self._request.client_port}", + websocket_address=self._request.websocket_address, ) self._session.add(self._user) else: @@ -159,16 +165,36 @@ def _check_channel_user(self): def _boradcast(self) -> None: # todo boradcast message here - raise NotImplementedError() + # find channel user websockets + assert self._channel + users = ( + self._session.query(ChatUserCaches) + .join( + ChatChannelUserCaches, + ChatUserCaches.nick_name == ChatChannelUserCaches.nick_name, + ) + .where(ChatChannelUserCaches.channel_name == self._channel.channel_name) + .all() + ) # type: ignore + ws_list = [] + + for user in users: + assert self._user + if user.websocket_address != self._user.websocket_address: # type: ignore + ws_list.append(user.websocket_address) - def handle(self) -> None: - super().handle() + MANAGER.broadcast(self._request, ws_list) + + def _response_construct(self) -> None: + super()._response_construct() if self._is_broadcast: self._boradcast() class MessageHandlerBase(ChannelHandlerBase): - pass + def __init__(self, request: RequestBase) -> None: + super().__init__(request) + self._is_broadcast = True # region General @@ -315,7 +341,7 @@ def _data_operate(self) -> None: return if ( - cache.remote_ip_address != self._request.client_ip + cache.remote_ip != self._request.client_ip and cache.remote_port != self._request.client_port ): # type: ignore raise NickNameInUseException( @@ -455,7 +481,7 @@ def _result_construct(self) -> None: assert isinstance(self._user.nick_name, str) assert self._channel is not None assert isinstance(self._channel.modes, list) - + self._result = JoinResult( joiner_user_name=self._user.user_name, joiner_nick_name=self._user.nick_name, @@ -545,13 +571,20 @@ class ModeHandler(ChannelHandlerBase): def _data_operate(self) -> None: assert self._channel assert self._channel_user - ChannelHelper.change_modes( - self._channel, self._channel_user, self._request, self._session - ) + if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: + # set modes of channel + ChannelHelper.change_modes( + self._channel, self._channel_user, self._request, self._session + ) def _result_construct(self) -> None: - raise NotImplementedError() - return super()._result_construct() + # we send the response when type is GET_CHANNEL_MODES + if self._request.request_type == ModeRequestType.GET_CHANNEL_MODES: + self._result = ModeResult( + channel_name=self._channel.channel_name, # type: ignore + channel_modes=self._channel.modes, # type: ignore + joiner_nick_name=self._user.nick_name, # type: ignore + ) class NamesHandler(ChannelHandlerBase): @@ -575,7 +608,7 @@ def _result_construct(self) -> None: assert isinstance(self._user.nick_name, str) nicks = [] for nick in self._channel_users_info: - nick.channel_name # yield nick + nick.channel_name # yield nick data = NamesResultData(**nick.__dict__) nicks.append(data) diff --git a/src/backends/protocols/gamespy/chat/helper.py b/src/backends/protocols/gamespy/chat/helper.py index fc5072381..7ca84286c 100644 --- a/src/backends/protocols/gamespy/chat/helper.py +++ b/src/backends/protocols/gamespy/chat/helper.py @@ -1,5 +1,6 @@ from datetime import datetime from enum import Enum +from typing import cast from uuid import UUID from backends.library.database.pg_orm import ( @@ -11,13 +12,14 @@ import backends.protocols.gamespy.chat.data as data from backends.protocols.gamespy.chat.requests import ModeRequest from frontends.gamespy.protocols.chat.abstractions.contract import SERVER_DOMAIN -from frontends.gamespy.protocols.chat.aggregates.enums import ModeOperationType +from frontends.gamespy.protocols.chat.aggregates.enums import ModeName, ModeOperation from frontends.gamespy.protocols.chat.aggregates.exceptions import ( BadChannelKeyException, BannedFromChanException, InviteOnlyChanException, NoSuchChannelException, ) + from sqlalchemy.orm import Session @@ -55,8 +57,8 @@ def join( assert isinstance(channel.banned_nicks, list) # 1 check if is a invited channel # 1.1 check if user is in a invited list - - if ModeOperationType.SET_INVITED_ONLY in channel.modes: + channel_modes = [ModeName(m) for m in channel.modes] + if ModeName.INVITED_ONLY in channel_modes: if user.nick_name not in channel.invited_nicks: raise InviteOnlyChanException( f"You can only join channel: {channel.channel_name} when you are in invite list" @@ -80,7 +82,7 @@ def join( is_voiceable=True, is_channel_operator=False, is_channel_creator=is_creator, - remote_ip_address=user.remote_ip_address, + remote_ip=user.remote_ip, remote_port=user.remote_port, ) session.add(chan_user) @@ -192,52 +194,42 @@ def change_modes( ): assert isinstance(channel, ChatChannelCaches) assert isinstance(changer, ChatChannelUserCaches) - assert isinstance(channel.modes, list) - assert all(isinstance(m, ModeOperationType) for m in request.mode_operations) - with Session(ENGINE) as session: - for flag in request.mode_operations: - if flag not in channel.modes: - channel.modes.append(flag) - match flag: - case ModeOperationType.ENABLE_USER_QUIET_FLAG: + channel_modes = cast(list, channel.modes) + for flag, operation in request.mode_operations.items(): + match flag: + case ModeName.USER_QUIET_FLAG: + if operation == ModeOperation.SET: if changer.is_channel_operator: # type:ignore - if ( - ModeOperationType.ENABLE_USER_QUIET_FLAG - not in channel.modes - ): - channel.modes.append( - ModeOperationType.ENABLE_USER_QUIET_FLAG - ) - case ModeOperationType.DISABLE_USER_QUIET_FLAG: + if flag.value not in channel_modes: + channel_modes.append(flag.value) + else: if changer.is_channel_operator: # type:ignore - if ( - ModeOperationType.ENABLE_USER_QUIET_FLAG - not in channel.modes - ): - channel.modes.remove( - ModeOperationType.ENABLE_USER_QUIET_FLAG - ) - case ModeOperationType.ADD_CHANNEL_PASSWORD: + if flag.value in channel_modes: + channel_modes.remove(flag.value) + case ModeName.CHANNEL_PASSWORD: + if operation == ModeOperation.SET: assert isinstance(request.password, str) if changer.is_channel_operator: # type:ignore channel.password = request.password # type:ignore - case ModeOperationType.REMOVE_CHANNEL_PASSWORD: + else: if changer.is_channel_operator: # type:ignore channel.password = None # type:ignore - case ModeOperationType.ADD_CHANNEL_USER_LIMITS: + case ModeName.CHANNEL_USER_LIMITS: + if operation == ModeOperation.SET: channel.max_num_user = request.limit_number # type: ignore - case ModeOperationType.REMOVE_CHANNEL_USER_LIMITS: + else: channel.max_num_user = 200 # type: ignore - case ModeOperationType.ADD_BAN_ON_USER: - assert isinstance(channel.banned_nicks, list) + case ModeName.BAN_ON_USER: + assert isinstance(channel.banned_nicks, list) + if operation == ModeOperation.SET: # type: ignore if request.nick_name not in list(channel.banned_nicks): channel.banned_nicks.append(request.nick_name) - case ModeOperationType.REMOVE_BAN_ON_USER: - assert isinstance(channel.banned_nicks, list) + else: if request.nick_name in list(channel.banned_nicks): channel.banned_nicks.remove(request.nick_name) - case ModeOperationType.ADD_CHANNEL_OPERATOR: + case ModeName.CHANNEL_OPERATOR: + if operation == ModeOperation.SET: if request.nick_name is None: raise BadChannelKeyException( "ADD_CHANNEL_OPERATOR require nick name" @@ -249,10 +241,8 @@ def change_modes( raise BadChannelKeyException( f"no user found with nick name:{request.nick_name}" ) - u.is_channel_operator = True # type: ignore - session.commit() - case ModeOperationType.REMOVE_CHANNEL_OPERATOR: + else: if request.nick_name is None: raise BadChannelKeyException( "REMOVE_CHANNEL_OPERATOR require nick name" @@ -261,9 +251,8 @@ def change_modes( request.channel_name, request.nick_name, session ) u.is_channel_operator = False # type: ignore - session.commit() - - case ModeOperationType.ENABLE_USER_VOICE_PERMISSION: + case ModeName.USER_VOICE_PERMISSION: + if operation == ModeOperation.SET: if request.nick_name is None: raise BadChannelKeyException( "ENABLE_USER_VOICE_PERMISSION require nick name" @@ -272,7 +261,7 @@ def change_modes( request.channel_name, request.nick_name, session ) u.is_voiceable = True # type: ignore - case ModeOperationType.DISABLE_USER_VOICE_PERMISSION: + else: if request.nick_name is None: raise BadChannelKeyException( "DISABLE_USER_VOICE_PERMISSION require nick name" @@ -281,8 +270,7 @@ def change_modes( request.channel_name, request.nick_name, session ) u.is_voiceable = False # type: ignore - - session.commit() + session.commit() @staticmethod def get_all_user_nick_string(channel: ChatChannelCaches, session: Session) -> str: @@ -305,7 +293,7 @@ def get_all_user_nick_string(channel: ChatChannelCaches, session: Session) -> st @staticmethod def get_channel_all_nicks( channel: ChatChannelCaches, session: Session - ) ->list[ChatChannelUserCaches]: + ) -> list[ChatChannelUserCaches]: assert channel is not None assert isinstance(channel.channel_name, str) users = data.get_channel_user_caches_by_name(channel.channel_name, session) diff --git a/src/backends/protocols/gamespy/chat/requests.py b/src/backends/protocols/gamespy/chat/requests.py index 9b89ca916..9744e3278 100644 --- a/src/backends/protocols/gamespy/chat/requests.py +++ b/src/backends/protocols/gamespy/chat/requests.py @@ -3,7 +3,8 @@ GetKeyRequestType, LoginRequestType, MessageType, - ModeOperationType, + ModeName, + ModeOperation, ModeRequestType, TopicRequestType, WhoRequestType, @@ -13,6 +14,7 @@ class RequestBase(lib.RequestBase): raw_request: str command_name: str + websocket_address: str # region General @@ -88,7 +90,7 @@ class SetKeyRequest(RequestBase): class UserIPRequest(RequestBase): - remote_ip_address: str + remote_ip: str class UserRequest(RequestBase): @@ -121,6 +123,7 @@ class GetKeyRequest(RequestBase): class ChannelRequestBase(RequestBase): channel_name: str + broad_cast_raw: str | None = None class GetChannelKeyRequest(ChannelRequestBase): @@ -146,7 +149,7 @@ class KickRequest(ChannelRequestBase): class ModeRequest(ChannelRequestBase): request_type: ModeRequestType - mode_operations: list[ModeOperationType] = [] + mode_operations: dict[ModeName, ModeOperation] limit_number: int | None = None mode_flag: str | None = None password: str | None = None diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 33dfd5355..d23ed6489 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -10,6 +10,7 @@ ModeHandler, NamesHandler, NickHandler, + PrivateHandler, UserHandler, ) from backends.protocols.gamespy.chat.requests import ( @@ -61,13 +62,7 @@ async def websocket_endpoint(ws: WebSocket): MANAGER.connect(ws) try: while True: - request = await ws.receive_json() - r = check_request(request) - if r.message is None: - return - # currently we broadcast all message - MANAGER.channel_broad_cast(r.channel_name, r) - + _ = await ws.receive_json() except WebSocketDisconnect: if ws.client is not None: MANAGER.disconnect(ws) @@ -235,7 +230,9 @@ def notice(request: NoticeRequest): @router.post(f"{CHAT}/PrivateHandler") def private(request: PrivateRequest): - raise NotImplementedError() + handler = PrivateHandler(request) + handler.handle() + return handler.response @router.post(f"{CHAT}/UTMHandler") diff --git a/src/backends/tests/gamespy/chat/lib_tests.py b/src/backends/tests/gamespy/chat/lib_tests.py index 96b60c182..d2a356919 100644 --- a/src/backends/tests/gamespy/chat/lib_tests.py +++ b/src/backends/tests/gamespy/chat/lib_tests.py @@ -1,48 +1,48 @@ -from typing import cast -import unittest - -from fastapi import WebSocket -from fastapi.datastructures import Address - -from backends.protocols.gamespy.chat.brocker_manager import ( - ChatWebSocketClient, - ChatWebSocketManager, -) - - -class WebSocketMock: - client: Address = Address("127.0.0.1", 123) - - -class LibTests(unittest.TestCase): - def test_ws_manager(self): - ws = WebSocketMock() - manager = ChatWebSocketManager() - ws = cast(WebSocket, ws) - manager.connect(ws) - self.assertEqual(list(manager.client_pool.values())[0].ws, ws) - manager.disconnect(ws) - self.assertEqual(len(manager.client_pool.values()), 0) - - def test_chat_ws_manager(self): - ws = WebSocketMock() - manager = ChatWebSocketManager() - ws = cast(WebSocket, ws) - manager.connect(ws) - self.assertEqual(list(manager.client_pool.values())[0].ws, ws) - - channel_name = "gmtest" - manager.add_to_channel(channel_name, ws) - client = manager.get_client(ws) - client = cast(ChatWebSocketClient, client) - manager.channel_info - self.assertTrue(channel_name in manager.channel_info) - self.assertTrue(len(manager.channel_info.values()) != 0) - self.assertTrue(channel_name in client.channels) - manager.remove_from_channel(channel_name, ws) - self.assertTrue(channel_name not in manager.channel_info) - self.assertTrue(len(manager.channel_info.values()) == 0) - self.assertTrue(channel_name not in client.channels) - - manager.disconnect(ws) - self.assertEqual(len(manager.client_pool.values()), 0) +# from typing import cast +# import unittest + +# from fastapi import WebSocket +# from fastapi.datastructures import Address + +# from backends.protocols.gamespy.chat.brocker_manager import ( +# ChatWebSocketClient, +# ChatWebSocketManager, +# ) + + +# class WebSocketMock: +# client: Address = Address("127.0.0.1", 123) + + +# class LibTests(unittest.TestCase): +# def test_ws_manager(self): +# ws = WebSocketMock() +# manager = ChatWebSocketManager() +# ws = cast(WebSocket, ws) +# manager.connect(ws) +# self.assertEqual(list(manager.client_pool.values())[0].ws, ws) +# manager.disconnect(ws) +# self.assertEqual(len(manager.client_pool.values()), 0) + +# def test_chat_ws_manager(self): +# ws = WebSocketMock() +# manager = ChatWebSocketManager() +# ws = cast(WebSocket, ws) +# manager.connect(ws) +# self.assertEqual(list(manager.client_pool.values())[0].ws, ws) + +# channel_name = "gmtest" +# manager.add_to_channel(channel_name, ws) +# client = manager.get_client(ws) +# client = cast(ChatWebSocketClient, client) +# manager.channel_info +# self.assertTrue(channel_name in manager.channel_info) +# self.assertTrue(len(manager.channel_info.values()) != 0) +# self.assertTrue(channel_name in client.channels) +# manager.remove_from_channel(channel_name, ws) +# self.assertTrue(channel_name not in manager.channel_info) +# self.assertTrue(len(manager.channel_info.values()) == 0) +# self.assertTrue(channel_name not in client.channels) + +# manager.disconnect(ws) +# self.assertEqual(len(manager.client_pool.values()), 0) diff --git a/src/frontends/gamespy/library/abstractions/handler.py b/src/frontends/gamespy/library/abstractions/handler.py index 5fa093401..8ff1429f2 100644 --- a/src/frontends/gamespy/library/abstractions/handler.py +++ b/src/frontends/gamespy/library/abstractions/handler.py @@ -120,7 +120,7 @@ def _upload_data(self): ) else: raise UniSpyException("backends is not avaliable") - + # todo http code to determine object type if response.status_code != 200: raise UniSpyException( f"failed to upload data to backends. reason: {response.text}" diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index 730991069..1cdf15d58 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -68,8 +68,6 @@ def _launch_server(self) -> None: raise UniSpyException("Create network server in child class") self._heartbeat_to_backend() self.server.start() - self._keep_running() - pass def _connect_to_backend(self): """ diff --git a/src/frontends/gamespy/library/network/brockers.py b/src/frontends/gamespy/library/network/brockers.py index 1bd9097ec..549b8ff6f 100644 --- a/src/frontends/gamespy/library/network/brockers.py +++ b/src/frontends/gamespy/library/network/brockers.py @@ -51,7 +51,10 @@ def subscribe(self): self._publisher = self._subscriber = connect(self.url) th = threading.Thread(target=self._listen) th.start() - + @property + def ip_port(self)->str: + name = self._subscriber.socket.getsockname() + return f"{name[0]}:{name[1]}" def _listen(self): try: while True: diff --git a/src/frontends/gamespy/protocols/chat/abstractions/contract.py b/src/frontends/gamespy/protocols/chat/abstractions/contract.py index bfd1d49f3..aac0361e0 100644 --- a/src/frontends/gamespy/protocols/chat/abstractions/contract.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/contract.py @@ -1,4 +1,3 @@ - from uuid import UUID from pydantic import BaseModel @@ -11,6 +10,7 @@ class RequestBase(lib.RequestBase): _prefix: str | None _cmd_params: list[str] _long_param: str | None + websocket_address: str def __init__(self, raw_request: str) -> None: assert isinstance(raw_request, str) diff --git a/src/frontends/gamespy/protocols/chat/abstractions/handler.py b/src/frontends/gamespy/protocols/chat/abstractions/handler.py index 6483965bf..05bf8759e 100644 --- a/src/frontends/gamespy/protocols/chat/abstractions/handler.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/handler.py @@ -25,7 +25,10 @@ class CmdHandlerBase(lib.CmdHandlerBase): def __init__(self, client: ClientBase, request: RequestBase): super().__init__(client, request) assert issubclass(type(request), RequestBase) - + def _request_check(self) -> None: + super()._request_check() + assert self._client.brocker + self._request.websocket_address = self._client.brocker.ip_port def _handle_exception(self, ex: Exception) -> None: t_ex = type(ex) if t_ex is IRCException: @@ -99,6 +102,7 @@ class MessageRequestBase(ChannelRequestBase): type: MessageType nick_name: str message: str + broad_cast_raw: str def parse(self): super().parse() @@ -129,6 +133,13 @@ def __init__(self, client: ClientBase, request: MessageRequestBase): assert isinstance(request, MessageRequestBase) super().__init__(client, request) + def _request_check(self) -> None: + super()._request_check() + self._request.broad_cast_raw = ( + f"{self._client.info.irc_prefix} {self._request.raw_request}" + ) + + def _update_channel_cache(self): """we do nothing here, channel message do not need to update channel cache""" pass diff --git a/src/frontends/gamespy/protocols/chat/aggregates/enums.py b/src/frontends/gamespy/protocols/chat/aggregates/enums.py index 75d981566..2a0fdb0b1 100644 --- a/src/frontends/gamespy/protocols/chat/aggregates/enums.py +++ b/src/frontends/gamespy/protocols/chat/aggregates/enums.py @@ -21,40 +21,61 @@ class GetKeyRequestType(IntEnum): GET_CHANNEL_SPECIFIC_USER_KEY_VALUE = 1 -class ModeOperationType(Enum): - ENABLE_USER_QUIET_FLAG = "+q" - DISABLE_USER_QUIET_FLAG = "-q" - ADD_CHANNEL_PASSWORD = "+k" - REMOVE_CHANNEL_PASSWORD = "-k" - ADD_CHANNEL_USER_LIMITS = "+l" - REMOVE_CHANNEL_USER_LIMITS = "-l" - ADD_BAN_ON_USER = "+b" - GET_BANNED_USERS = "+b" - REMOVE_BAN_ON_USER = "-b" - ADD_CHANNEL_OPERATOR = "+co" - REMOVE_CHANNEL_OPERATOR = "-co" - ENABLE_USER_VOICE_PERMISSION = "+cv" - DISABLE_USER_VOICE_PERMISSION = "-cv" - SET_INVITED_ONLY = "+i" - REMOVE_INVITED_ONLY = "-i" - SET_PRIVATE_CHANNEL_FLAG = "+p" - REMOVE_PRIVATE_CHANNEL_FLAG = "-p" - SET_SECRET_CHANNEL_FLAG = "+s" - REMOVE_SECRET_CHANNEL_FLAG = "-s" - SET_MODERATED_CHANNEL_FLAG = "+m" - REMOVE_MODERATED_CHANNEL_FLAG = "-m" - ENABLE_EXTERNAL_MESSAGES_FLAG = "+n" - DISABLE_EXTERNAL_MESSAGES_FLAG = "-n" - SET_TOPIC_CHANGE_BY_OPERATOR_FLAG = "+t" - REMOVE_TOPIC_CHANGE_BY_OPERATOR_FLAG = "-t" - SET_OPERATOR_ABEY_CHANNEL_LIMITS = "+e" - REMOVE_OPERATOR_ABEY_CHANNEL_LIMITS = "-e" +class ModeOperation(Enum): + SET = "+" + UNSET = "-" + + +class ModeName(Enum): + USER_QUIET_FLAG = "q" + CHANNEL_PASSWORD = "k" + CHANNEL_USER_LIMITS = "l" + BAN_ON_USER = "b" + CHANNEL_OPERATOR = "co" + USER_VOICE_PERMISSION = "cv" + INVITED_ONLY = "i" + PRIVATE_CHANNEL_FLAG = "p" + SECRET_CHANNEL_FLAG = "s" + MODERATED_CHANNEL_FLAG = "m" + EXTERNAL_MESSAGES_FLAG = "n" + TOPIC_CHANGE_BY_OPERATOR_FLAG = "t" + OPERATOR_ABEY_CHANNEL_LIMITS = "e" + + +# class ModeOperationType(Enum): +# ENABLE_USER_QUIET_FLAG = "+q" +# DISABLE_USER_QUIET_FLAG = "-q" +# ADD_CHANNEL_PASSWORD = "+k" +# REMOVE_CHANNEL_PASSWORD = "-k" +# ADD_CHANNEL_USER_LIMITS = "+l" +# REMOVE_CHANNEL_USER_LIMITS = "-l" +# ADD_BAN_ON_USER = "+b" +# GET_BANNED_USERS = "+b" +# REMOVE_BAN_ON_USER = "-b" +# ADD_CHANNEL_OPERATOR = "+co" +# REMOVE_CHANNEL_OPERATOR = "-co" +# ENABLE_USER_VOICE_PERMISSION = "+cv" +# DISABLE_USER_VOICE_PERMISSION = "-cv" +# SET_INVITED_ONLY = "+i" +# REMOVE_INVITED_ONLY = "-i" +# SET_PRIVATE_CHANNEL_FLAG = "+p" +# REMOVE_PRIVATE_CHANNEL_FLAG = "-p" +# SET_SECRET_CHANNEL_FLAG = "+s" +# REMOVE_SECRET_CHANNEL_FLAG = "-s" +# SET_MODERATED_CHANNEL_FLAG = "+m" +# REMOVE_MODERATED_CHANNEL_FLAG = "-m" +# ENABLE_EXTERNAL_MESSAGES_FLAG = "+n" +# DISABLE_EXTERNAL_MESSAGES_FLAG = "-n" +# SET_TOPIC_CHANGE_BY_OPERATOR_FLAG = "+t" +# REMOVE_TOPIC_CHANGE_BY_OPERATOR_FLAG = "-t" +# SET_OPERATOR_ABEY_CHANNEL_LIMITS = "+e" +# REMOVE_OPERATOR_ABEY_CHANNEL_LIMITS = "-e" class ModeRequestType(IntEnum): GET_CHANNEL_MODES = 0 - GET_CHANNEL_AND_USER_MODES = 1 - SET_CHANNEL_MODES = 2 + SET_CHANNEL_MODES = 1 + SET_CHANNEL_USER_MODES = 2 class TopicRequestType(IntEnum): @@ -98,7 +119,7 @@ class ResponseCode(Enum): UTM = "UTM" PRIVMSG = "PRIVMSG" NOTICE = "NOTICE" - + # region IRC error code class IRCErrorCode(Enum): diff --git a/src/frontends/gamespy/protocols/chat/applications/client.py b/src/frontends/gamespy/protocols/chat/applications/client.py index 339527fd5..76adfc612 100644 --- a/src/frontends/gamespy/protocols/chat/applications/client.py +++ b/src/frontends/gamespy/protocols/chat/applications/client.py @@ -8,7 +8,10 @@ from frontends.gamespy.library.configs import CONFIG, ServerConfig -from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage +from frontends.gamespy.protocols.chat.abstractions.contract import ( + SERVER_DOMAIN, + BrockerMessage, +) class ClientInfo: @@ -25,6 +28,12 @@ def __init__(self) -> None: self.user_name = None self.previously_joined_channel = None + @property + def irc_prefix(self) -> str: + assert self.nick_name + assert self.user_name + return f"{self.nick_name}!{self.user_name}@{SERVER_DOMAIN}" + class Client(ClientBase): info: ClientInfo diff --git a/src/frontends/gamespy/protocols/chat/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py index 2c542d467..0ff1c28df 100644 --- a/src/frontends/gamespy/protocols/chat/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -2,7 +2,6 @@ from frontends.gamespy.protocols.chat.contracts.results import ( ATMResult, NoticeResult, - PrivateResult, UTMResult, GetCKeyResult, GetChannelKeyResult, @@ -24,10 +23,6 @@ WhoResult, ) from frontends.gamespy.protocols.chat.contracts.responses import ( - ATMResponse, - NoticeResponse, - PrivateResponse, - UTMResponse, GetCKeyResponse, JoinResponse, KickResponse, @@ -116,7 +111,7 @@ def __init__(self, client: ClientBase, request: RequestBase): def _data_operate(self) -> None: super()._data_operate() self._client.info.gamename = self._request.gamename - + def _response_construct(self) -> None: self._response = CryptResponse() @@ -256,11 +251,11 @@ def __init__(self, client: ClientBase, request: UserIPRequest): def _request_check(self) -> None: super()._request_check() - self._request.remote_ip_address = self._client.connection.remote_ip + self._request.remote_ip = self._client.connection.remote_ip def _data_operate(self) -> None: super()._data_operate() - self._result = UserIPResult(remote_ip_address=self._client.connection.remote_ip) + self._result = UserIPResult(remote_ip=self._client.connection.remote_ip) def _response_construct(self) -> None: self._response = UserIPResponse(self._result) @@ -376,10 +371,7 @@ def _request_check(self) -> None: self._is_fetching = False def _response_construct(self): - if self._request.request_type in [ - ModeRequestType.GET_CHANNEL_AND_USER_MODES, - ModeRequestType.GET_CHANNEL_MODES, - ]: + if self._request.request_type == ModeRequestType.GET_CHANNEL_MODES: self._response = ModeResponse(self._request, self._result) @@ -456,10 +448,7 @@ class ATMHandler(MessageHandlerBase): def __init__(self, client: ClientBase, request: ATMRequest): assert isinstance(request, ATMRequest) - super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = ATMResponse(self._request, self._result) + self._is_fetching = False class UTMHandler(MessageHandlerBase): @@ -469,10 +458,7 @@ class UTMHandler(MessageHandlerBase): def __init__(self, client: ClientBase, request: UTMRequest): assert isinstance(request, UTMRequest) super().__init__(client, request) - self._result_cls = UTMResult - - def _response_construct(self) -> None: - self._response = UTMResponse(self._request, self._result) + self._is_fetching = False class NoticeHandler(MessageHandlerBase): @@ -482,20 +468,13 @@ class NoticeHandler(MessageHandlerBase): def __init__(self, client: ClientBase, request: NoticeRequest): assert isinstance(request, NoticeRequest) super().__init__(client, request) - self._result_cls = NoticeResult - - def _response_construct(self) -> None: - self._response = NoticeResponse(self._request, self._result) + self._is_fetching = False class PrivateHandler(MessageHandlerBase): _request: PrivateRequest - _result: PrivateResult def __init__(self, client: ClientBase, request: PrivateRequest): assert isinstance(request, PrivateRequest) super().__init__(client, request) - self._result_cls = PrivateResult - - def _response_construct(self) -> None: - self._response = PrivateResponse(self._request, self._result) + self._is_fetching = False diff --git a/src/frontends/gamespy/protocols/chat/contracts/requests.py b/src/frontends/gamespy/protocols/chat/contracts/requests.py index 5a139a73d..f3eb81d97 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/requests.py +++ b/src/frontends/gamespy/protocols/chat/contracts/requests.py @@ -4,7 +4,8 @@ ) from frontends.gamespy.protocols.chat.aggregates.enums import ( GetKeyRequestType, - ModeOperationType, + ModeName, + ModeOperation, ModeRequestType, TopicRequestType, ) @@ -213,7 +214,7 @@ def parse(self): class UserIPRequest(RequestBase): - remote_ip_address: str + remote_ip: str class UserRequest(RequestBase): @@ -403,7 +404,12 @@ class ModeRequest(ChannelRequestBase): # "MODE " # "MODE " request_type: ModeRequestType - mode_operations: list[ModeOperationType] + mode_operations: dict[str, str] + """ + , + o,+ + i,+ + """ nick_name: str user_name: str limit_number: int @@ -412,69 +418,56 @@ class ModeRequest(ChannelRequestBase): def parse(self): super().parse() - self.mode_operations = [] + self.mode_operations = {} if len(self._cmd_params) == 1: self.request_type = ModeRequestType.GET_CHANNEL_MODES elif len(self._cmd_params) == 2 or len(self._cmd_params) == 3: self.request_type = ModeRequestType.SET_CHANNEL_MODES self.mode_flag = self._cmd_params[1] - modeFlags = [s for s in re.split(r"(?=\+|\-)", self.mode_flag) if s.strip()] - modeFlags = list(filter(None, modeFlags)) - for flag in modeFlags: - conv_flag = ModeOperationType(flag) - match conv_flag: - case ( - ModeOperationType.SET_OPERATOR_ABEY_CHANNEL_LIMITS - | ModeOperationType.REMOVE_OPERATOR_ABEY_CHANNEL_LIMITS - | ModeOperationType.SET_TOPIC_CHANGE_BY_OPERATOR_FLAG - | ModeOperationType.REMOVE_TOPIC_CHANGE_BY_OPERATOR_FLAG - | ModeOperationType.ENABLE_EXTERNAL_MESSAGES_FLAG - | ModeOperationType.DISABLE_EXTERNAL_MESSAGES_FLAG - | ModeOperationType.SET_MODERATED_CHANNEL_FLAG - | ModeOperationType.REMOVE_MODERATED_CHANNEL_FLAG - | ModeOperationType.SET_SECRET_CHANNEL_FLAG - | ModeOperationType.REMOVE_SECRET_CHANNEL_FLAG - | ModeOperationType.SET_INVITED_ONLY - | ModeOperationType.REMOVE_INVITED_ONLY - | ModeOperationType.SET_PRIVATE_CHANNEL_FLAG - | ModeOperationType.REMOVE_PRIVATE_CHANNEL_FLAG - | ModeOperationType.ENABLE_USER_QUIET_FLAG - | ModeOperationType.DISABLE_USER_QUIET_FLAG - | ModeOperationType.ADD_CHANNEL_PASSWORD - | ModeOperationType.REMOVE_CHANNEL_PASSWORD - ): - self.mode_operations.append(conv_flag) - - case ModeOperationType.ADD_CHANNEL_USER_LIMITS: + mode_flags = [ + s for s in re.split(r"(?=\+|\-)", self.mode_flag) if s.strip() + ] + mode_flags = list(filter(None, mode_flags)) + self.process_mode_flags(mode_flags) + else: + raise ChatException("The number of IRC parameters are incorrect.") + + def process_mode_flags(self, mode_flags: list[str]): + for flag in mode_flags: + try: + operation = ModeOperation(flag[0]) + flag_name = ModeName(flag[1:]) + except Exception: + continue + match flag_name: + case ( + ModeName.OPERATOR_ABEY_CHANNEL_LIMITS + | ModeName.TOPIC_CHANGE_BY_OPERATOR_FLAG + | ModeName.EXTERNAL_MESSAGES_FLAG + | ModeName.MODERATED_CHANNEL_FLAG + | ModeName.SECRET_CHANNEL_FLAG + | ModeName.INVITED_ONLY + | ModeName.PRIVATE_CHANNEL_FLAG + | ModeName.USER_QUIET_FLAG + | ModeName.CHANNEL_PASSWORD + ): + self.mode_operations[flag_name.value] = operation.value + case ModeName.CHANNEL_USER_LIMITS: + if operation == ModeOperation.SET: self.channel_name = self._cmd_params[0] self.limit_number = int(self._cmd_params[2]) - self.mode_operations.append(conv_flag) - case ( - ModeOperationType.REMOVE_CHANNEL_USER_LIMITS - | ModeOperationType.REMOVE_BAN_ON_USER - ): - self.channel_name = self._cmd_params[0] - self.mode_operations.append(conv_flag) - case ( - ModeOperationType.ADD_BAN_ON_USER - | ModeOperationType.GET_BANNED_USERS - ): + else: self.channel_name = self._cmd_params[0] - if len(self._cmd_params) == 3: - self.nick_name = self._cmd_params[2] - self.mode_operations.append(conv_flag) - case ( - ModeOperationType.ADD_CHANNEL_OPERATOR - | ModeOperationType.REMOVE_CHANNEL_OPERATOR - | ModeOperationType.ENABLE_USER_VOICE_PERMISSION - | ModeOperationType.DISABLE_USER_VOICE_PERMISSION - ): - self.channel_name = self._cmd_params[0] - self.user_name = self._cmd_params[2] - self.mode_operations.append(conv_flag) - - else: - raise ChatException("The number of IRC parameters are incorrect.") + self.mode_operations[flag_name.value] = operation.value + case ModeName.BAN_ON_USER: + self.channel_name = self._cmd_params[0] + if len(self._cmd_params) == 3: + self.nick_name = self._cmd_params[2] + self.mode_operations[flag_name.value] = operation.value + case ModeName.CHANNEL_OPERATOR | ModeName.USER_VOICE_PERMISSION: + self.channel_name = self._cmd_params[0] + self.user_name = self._cmd_params[2] + self.mode_operations[flag_name.value] = operation.value @staticmethod def build(channel_name: str): diff --git a/src/frontends/gamespy/protocols/chat/contracts/responses.py b/src/frontends/gamespy/protocols/chat/contracts/responses.py index 272ffd6a7..578a30e8e 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/responses.py +++ b/src/frontends/gamespy/protocols/chat/contracts/responses.py @@ -1,6 +1,5 @@ from frontends.gamespy.protocols.chat.abstractions.handler import ChannelResponseBase from frontends.gamespy.protocols.chat.aggregates.enums import ( - ModeRequestType, WhoRequestType, ResponseCode, ) @@ -154,7 +153,7 @@ def __init__(self, result: UserIPResult) -> None: self._result = result def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.USRIP.value} :@{self._result.remote_ip_address}\r\n" + self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.USRIP.value} :@{self._result.remote_ip}\r\n" class WhoIsResponse(ResponseBase): @@ -270,9 +269,19 @@ def __init__(self, request: ModeRequest, result: ModeResult) -> None: assert isinstance(result, ModeResult) super().__init__(request, result) + @staticmethod + def get_mode_str(modes: list[str]): + if len(modes) == 0: + return "" + + modes_str = "+" + for m in modes: + modes_str += m + return modes_str + def build(self) -> None: - if self._request.request_type == ModeRequestType.GET_CHANNEL_MODES: - self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.CHANNELMODEIS.value} * {self._result.channel_modes} {self._result.channel_modes}\r\n" + chann_modes_str = ModeResponse.get_mode_str(self._result.channel_modes) + self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.CHANNELMODEIS.value} * {self._request.channel_name} {chann_modes_str}\r\n" class NamesResponse(ChannelResponseBase): diff --git a/src/frontends/gamespy/protocols/chat/contracts/results.py b/src/frontends/gamespy/protocols/chat/contracts/results.py index 28729cf03..4898ab8bb 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/results.py +++ b/src/frontends/gamespy/protocols/chat/contracts/results.py @@ -54,7 +54,7 @@ class QuitInfo(BaseModel): class UserIPResult(ResultBase): - remote_ip_address: str + remote_ip: str class WhoIsResult(ResultBase): @@ -97,7 +97,7 @@ class GetCKeyInfos(BaseModel): class ModeResult(ResultBase): channel_name: str - channel_modes: str + channel_modes: list[str] joiner_nick_name: str diff --git a/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py b/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py index ef99189d3..1fa1e9d76 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py +++ b/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py @@ -90,7 +90,7 @@ def __init__(self, client: Client, request: HeartBeatRequest) -> None: def _response_construct(self) -> None: self._result = HeartBeatResult( - remote_ip_address=self._client.connection.remote_ip, + remote_ip=self._client.connection.remote_ip, remote_port=self._client.connection.remote_port, ) self._response = HeartBeatResponse(self._request, self._result) diff --git a/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py b/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py index d1d6f3e0b..b34682504 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py +++ b/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py @@ -56,7 +56,7 @@ class HeartBeatRequest(RequestBase): team_data: list[dict[str, str]] server_status: GameServerStatus group_id: int | None - remote_ip_address: str + remote_ip: str remote_port: int game_name: str diff --git a/src/frontends/gamespy/protocols/query_report/v2/contracts/responses.py b/src/frontends/gamespy/protocols/query_report/v2/contracts/responses.py index 19bed6668..715e34f30 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/contracts/responses.py +++ b/src/frontends/gamespy/protocols/query_report/v2/contracts/responses.py @@ -80,7 +80,7 @@ def build(self) -> None: data = bytearray() data.extend(self.sending_buffer) data.extend(CHALLENGE) - data.extend(ip_to_4_bytes(self._result.remote_ip_address)) + data.extend(ip_to_4_bytes(self._result.remote_ip)) data.extend(SPLITER) data.extend(port_to_2_bytes(self._result.remote_port)) self.sending_buffer = bytes(data) diff --git a/src/frontends/gamespy/protocols/query_report/v2/contracts/results.py b/src/frontends/gamespy/protocols/query_report/v2/contracts/results.py index e4b723f3f..1fc2040de 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/contracts/results.py +++ b/src/frontends/gamespy/protocols/query_report/v2/contracts/results.py @@ -27,5 +27,5 @@ class HeartBeatResult(ResultBase): this result is replied in unispy server """ packet_type: PacketType = PacketType.HEARTBEAT - remote_ip_address: str + remote_ip: str remote_port: int diff --git a/src/frontends/tests/gamespy/chat/mock_objects.py b/src/frontends/tests/gamespy/chat/mock_objects.py index 61e4bc67e..f7a52671f 100644 --- a/src/frontends/tests/gamespy/chat/mock_objects.py +++ b/src/frontends/tests/gamespy/chat/mock_objects.py @@ -120,7 +120,7 @@ def create_client() -> Client: ModeResult.model_validate( { "channel_name": "test", - "channel_modes": "+n", + "channel_modes": ["n", "m"], "joiner_nick_name": "test_nick", } ).model_dump(), diff --git a/src/frontends/tests/gamespy/chat/request_tests.py b/src/frontends/tests/gamespy/chat/request_tests.py index fcfa09f83..4d0c94b67 100644 --- a/src/frontends/tests/gamespy/chat/request_tests.py +++ b/src/frontends/tests/gamespy/chat/request_tests.py @@ -1,7 +1,8 @@ import unittest -from frontends.gamespy.protocols.chat.aggregates.enums import MessageType -from frontends.gamespy.protocols.chat.contracts.requests import * +from frontends.gamespy.protocols.chat.aggregates.enums import GetKeyRequestType, MessageType, ModeName, ModeOperation +from frontends.gamespy.protocols.chat.contracts.requests import ATMRequest, GetCKeyRequest, GetChannelKeyRequest, JoinRequest, KickRequest, ModeRequest, NoticeRequest, PartRequest, PrivateRequest, SetCKeyRequest, SetChannelKeyRequest, TopicRequest, UTMRequest + # region General CD_KEY = "CDKEY XXXX-XXXX-XXXX-XXXX\r\n" CRYPT = "CRYPT des 1 gmtest\r\n" @@ -15,6 +16,8 @@ LOGIN_NICK_AND_EMAIL = "LOGIN 0 * xxxxx :spyguy@spyguy@gamespy.com\r\n" LOGIN_UNIQUE_NICK = "LOGIN 0 spyguy xxxxx\r\n" NAMES = "NAMES\r\n" +MODES_GET = "MODES #gmtest" +MODES_SET = "MODES #gmtest +s" NICK = "NICK :spyguy\r\n" PING = "PING\r\n" # TODO: add binary data test [0D][0A] PONG = "PONG :Pong!\r\n" @@ -36,8 +39,12 @@ def test_get_chann_key(self): # region Channel GET_CHANNEL_KEY = "GETCHANKEY #GSP!room!test 0000 0 :\\username\\nickname\0\r\n" -GET_CKEY_CHANNEL_SPECIFIC_USER = "GETCKEY #GSP!room!test spyguy 0000 0 :\\username\\nickname\0\r\n" -GET_CKEY_CHANNEL_ALL_USER = "GETCKEY #GSP!room!test * 0000 0 :\\username\\nickname\0\r\n" +GET_CKEY_CHANNEL_SPECIFIC_USER = ( + "GETCKEY #GSP!room!test spyguy 0000 0 :\\username\\nickname\0\r\n" +) +GET_CKEY_CHANNEL_ALL_USER = ( + "GETCKEY #GSP!room!test * 0000 0 :\\username\\nickname\0\r\n" +) JOIN = "JOIN #GSP!room!test\r\n" JOIN_WITH_PASS = "JOIN #GSP!room!test pass123\r\n" KICK = "KICK #islabul spyguy :Spam\r\n" @@ -62,8 +69,9 @@ def test_get_channel_key(self): def test_get_ckey_channel_specific_user(self): request = GetCKeyRequest(GET_CKEY_CHANNEL_SPECIFIC_USER) request.parse() - self.assertEqual(request.request_type, - GetKeyRequestType.GET_CHANNEL_SPECIFIC_USER_KEY_VALUE) + self.assertEqual( + request.request_type, GetKeyRequestType.GET_CHANNEL_SPECIFIC_USER_KEY_VALUE + ) self.assertEqual(request.channel_name, "#GSP!room!test") self.assertEqual(request.nick_name, "spyguy") self.assertEqual(request.cookie, "0000") @@ -73,8 +81,9 @@ def test_get_ckey_channel_specific_user(self): def test_get_ckey_channel_all_user(self): request = GetCKeyRequest(GET_CKEY_CHANNEL_ALL_USER) request.parse() - self.assertEqual(request.request_type, - GetKeyRequestType.GET_CHANNEL_ALL_USER_KEY_VALUE) + self.assertEqual( + request.request_type, GetKeyRequestType.GET_CHANNEL_ALL_USER_KEY_VALUE + ) self.assertEqual(request.channel_name, "#GSP!room!test") self.assertEqual(request.cookie, "0000") self.assertEqual(request.keys[0], "username") @@ -100,7 +109,9 @@ def test_mode(self): request = ModeRequest(MODE_CHANNEL) request.parse() self.assertEqual( - request.mode_operations[0], ModeOperationType.ADD_CHANNEL_USER_LIMITS) + request.mode_operations, + {ModeName.CHANNEL_USER_LIMITS.value: ModeOperation.SET.value}, + ) self.assertEqual(request.channel_name, "#GSP!room!test") self.assertEqual(request.mode_flag, "+l") self.assertEqual(request.limit_number, 2) @@ -136,6 +147,7 @@ def test_topic_set_channel_topic(self): self.assertEqual(request.channel_name, "#GSP!room!test") self.assertEqual(request.channel_topic, "This is a topic message.") + # region Message diff --git a/src/frontends/tests/gamespy/query_report/mock_objects.py b/src/frontends/tests/gamespy/query_report/mock_objects.py index ed8a62d02..9edc1691d 100644 --- a/src/frontends/tests/gamespy/query_report/mock_objects.py +++ b/src/frontends/tests/gamespy/query_report/mock_objects.py @@ -20,7 +20,7 @@ def create_client() -> Client: logger=logger) config = CONFIG.servers["QueryReport"] create_mock_url(config, HeartBeatHandler, HeartBeatResult.model_validate( - {"remote_ip_address": conn.remote_ip, "remote_port": conn.remote_port}).model_dump(mode='json')) + {"remote_ip": conn.remote_ip, "remote_port": conn.remote_port}).model_dump(mode='json')) create_mock_url(config, AvailableHandler, {"message": "ok"}) create_mock_url(config, KeepAliveHandler, {"message": "ok"}) return cast(Client, conn._client) diff --git a/src/frontends/tests/gamespy/server_browser/mock_objects.py b/src/frontends/tests/gamespy/server_browser/mock_objects.py index d83a45039..56ee022f5 100644 --- a/src/frontends/tests/gamespy/server_browser/mock_objects.py +++ b/src/frontends/tests/gamespy/server_browser/mock_objects.py @@ -92,7 +92,7 @@ def create_v1_client() -> Client: config, ServerMainListHandler, ServerMainListResult.model_validate( - {"remote_ip_address": conn.remote_ip, "remote_port": conn.remote_port} + {"remote_ip": conn.remote_ip, "remote_port": conn.remote_port} ).model_dump(), ) From bc815f114369d760b9c0181bf0fa34c0e43455d0 Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Wed, 17 Sep 2025 14:27:39 +0800 Subject: [PATCH 200/231] fix(chat): channel join --- .../library/abstractions/handler_base.py | 2 - .../library/abstractions/websocket_manager.py | 10 +- src/backends/protocols/gamespy/chat/data.py | 151 +++++++-- .../protocols/gamespy/chat/handlers.py | 133 +++++--- src/backends/protocols/gamespy/chat/helper.py | 53 +-- src/backends/routers/gamespy/chat.py | 16 +- src/backends/routers/home.py | 10 +- src/backends/tests/gamespy/chat/room_tests.py | 316 +++++++++++++++++- .../gamespy/query_report/data_fetch_tests.py | 1 + .../gamespy/library/abstractions/handler.py | 23 +- .../gamespy/library/exceptions/general.py | 3 +- .../protocols/chat/abstractions/contract.py | 4 + .../protocols/chat/abstractions/handler.py | 22 +- .../protocols/chat/applications/handlers.py | 4 +- .../protocols/chat/contracts/requests.py | 18 +- .../protocols/chat/contracts/responses.py | 53 +-- .../protocols/chat/contracts/results.py | 22 +- .../tests/gamespy/chat/mock_objects.py | 18 +- 18 files changed, 709 insertions(+), 150 deletions(-) diff --git a/src/backends/library/abstractions/handler_base.py b/src/backends/library/abstractions/handler_base.py index c468c3162..59c8cb1b5 100644 --- a/src/backends/library/abstractions/handler_base.py +++ b/src/backends/library/abstractions/handler_base.py @@ -75,7 +75,5 @@ def _response_construct(self) -> None: # if there are no result, we send ok response if self._result is None: self._response = OKResponse() - self.logger.info(f"[{self.__class__.__name__}] use default OKResponse") else: self._response = DataResponse(result=self._result.model_dump(mode="json")) - self.logger.info(f"[{self.__class__.__name__}] use default DataResponse") diff --git a/src/backends/library/abstractions/websocket_manager.py b/src/backends/library/abstractions/websocket_manager.py index 98b3e1f8e..713055fd6 100644 --- a/src/backends/library/abstractions/websocket_manager.py +++ b/src/backends/library/abstractions/websocket_manager.py @@ -1,4 +1,5 @@ import asyncio +from logging import getLogger from uuid import UUID from fastapi import WebSocket @@ -63,6 +64,9 @@ def disconnect(self, ws: WebSocket): temp = self.create_client(ws) if temp.ip_port in self.client_pool: del self.client_pool[temp.ip_port] + # todo remove record in database + + # todo check channelcache,usercache,channelusercache def broadcast(self, message: RequestBase, ws: list[str] | None = None): try: @@ -75,7 +79,11 @@ def broadcast(self, message: RequestBase, ws: list[str] | None = None): else: clients = [] for w in ws: - clients.append(self.client_pool[w]) + if w in self.client_pool: + clients.append(self.client_pool[w]) + else: + logger = getLogger("backend") + logger.info(f"{ws} not in websocket client list") for client in clients: loop.create_task(client.ws.send_json(message.model_dump())) diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index 338d8d9d8..8ea851bd4 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -3,6 +3,7 @@ from sqlalchemy import Column, func from backends.library.database.pg_orm import ( + ENGINE, ChatChannelCaches, ChatUserCaches, ChatChannelUserCaches, @@ -41,10 +42,6 @@ def add_user_cache(cache: ChatUserCaches, session: Session) -> Session: return session -def update_user_cache(cache: ChatUserCaches, session: Session): - session.commit() - - def nick_and_email_login( nick_name: str, email: str, password_hash: str, session: Session ) -> tuple[int, int, bool, bool]: @@ -133,7 +130,6 @@ def is_channel_exist(channel_name: str, game_name: str, session: Session) -> boo .where( ChatChannelCaches.channel_name == channel_name, ChatChannelCaches.game_name == game_name, - ChatChannelCaches.update_time >= datetime.now() - timedelta(minutes=10), ) .count() ) @@ -173,6 +169,39 @@ def get_channel_by_name( return channel +def get_channel_user_list_by_ip_port( + ip: str, port: int, session: Session +) -> list[ChatChannelUserCaches]: + assert isinstance(ip, str) + assert isinstance(port, int) + result = ( + session.query(ChatChannelUserCaches) + .where( + ChatChannelUserCaches.remote_ip == ip, + ChatChannelUserCaches.remote_port == port, + ) + .all() + ) + return result + + +# def get_channel_cache_list_by_ip_port( +# ip: str, port: int, session: Session +# ) -> list[ChatChannelCaches]: +# assert isinstance(ip, str) +# assert isinstance(port, int) +# result = ( +# session.query(ChatChannelCaches) +# .join(ChatChannelUserCaches) +# .where( +# ChatChannelUserCaches.remote_ip == ip, +# ChatChannelUserCaches.remote_port == port, +# ) +# .all() +# ) +# return result + + def get_channel_by_name_and_ip_port( channel_name: str, ip: str, port: int, session: Session ) -> ChatChannelCaches | None: @@ -193,6 +222,26 @@ def get_channel_by_name_and_ip_port( return result +def check_channel_user_trash_data( + channel: ChatChannelCaches, user: ChatUserCaches, session: Session +): + assert isinstance(channel.channel_name, str) + assert isinstance(user.nick_name, str) + exist_user = get_channel_user_cache_by_name( + channel.channel_name, user.nick_name, session + ) + if exist_user is not None: + if exist_user.update_time <= datetime.now() - timedelta(minutes=2): # type: ignore + session.delete(exist_user) + session.commit() + else: + raise NoSuchNickException( + "There are two same channel user cache in database" + ) + + # session.commit() + + def get_channel_user_cache_by_name( channel_name: str, nick_name: str, session: Session ) -> ChatChannelUserCaches | None: @@ -238,12 +287,6 @@ def get_channel_user_caches_by_name( return result -def update_channel_time(channel: ChatChannelCaches, session: Session): - channel.update_time = datetime.now() # type: ignore - - session.commit() - - def db_commit(session: Session): session.commit() @@ -259,6 +302,17 @@ def get_user_cache_by_nick_name( return result +def get_user_cache_by_ws( + websocket_address: str, session: Session +) -> ChatUserCaches | None: + user = ( + session.query(ChatUserCaches) + .where(ChatUserCaches.websocket_address == websocket_address) + .first() + ) + return user + + def get_user_cache_by_ip_port( ip: str, port: int, session: Session ) -> ChatUserCaches | None: @@ -274,7 +328,7 @@ def get_user_cache_by_ip_port( return result -def get_whois_result(nick: str, session: Session) -> tuple: +def get_whois_result(nick: str, session: Session) -> dict: """ nick is unique in chat """ @@ -292,16 +346,39 @@ def get_whois_result(nick: str, session: Session) -> tuple: .where(ChatChannelUserCaches.nick_name == info.nick_name) .all() ) - return ( - info.nick_name, - info.user_name, - info.nick_name, - info.remote_ip, - channels, - ) # type:ignore + ret_dict = { + "nick_name": info.nick_name, + "user_name": info.user_name, + "remote_ip": info.remote_ip, + "channels": channels[0], + } + return ret_dict + + +def get_websocket_addr_by_channel_name( + channel_name: str, session: Session +) -> list[str]: + """ + find the client websocket address in database which updated within 1 min + """ + users = ( + session.query(ChatUserCaches.websocket_address) + .join( + ChatChannelUserCaches, + ChatUserCaches.nick_name == ChatChannelUserCaches.nick_name, + ) + .where( + ChatChannelUserCaches.channel_name == channel_name, + ChatChannelUserCaches.update_time >= datetime.now() - timedelta(minutes=1), + ) + .all() + ) # type: ignore + users = [user[0] for user in users] + assert isinstance(users, list) + return users -def remove_user_caches_by_ip_port(ip: str, port: int, session: Session): +def remove_channel_user_caches_by_ip_port(ip: str, port: int, session: Session): assert isinstance(ip, str) assert isinstance(port, int) @@ -311,6 +388,15 @@ def remove_user_caches_by_ip_port(ip: str, port: int, session: Session): ).delete() +def remove_user_cache_by_ip_port(ip: str, port: int, session: Session): + assert isinstance(ip, str) + assert isinstance(port, int) + session.query(ChatUserCaches).where( + ChatUserCaches.remote_ip == ip, + ChatUserCaches.remote_port == port, + ).delete() + + def remove_user_cache(cache: ChatUserCaches, session: Session): session.delete(cache) session.commit() @@ -464,8 +550,33 @@ def get_channel_user_cache_by_ip(ip: str, port: int, session: Session) -> list[d return data +def remove_expired_user_cache(session: Session): + session.query(ChatUserCaches).where( + ChatUserCaches.update_time > (datetime.now() - timedelta(minutes=5)) + ).delete() + + +def remove_expired_channel_user_cache(session: Session): + session.query(ChatChannelUserCaches).where( + ChatUserCaches.update_time > (datetime.now() - timedelta(minutes=5)) + ).delete() + + +def _flush_chat_database(): + """ + be caution with this function + this function will remove all datatable for chat + """ + with Session(ENGINE) as session: + session.query(ChatChannelUserCaches).delete() + session.query(ChatUserCaches).delete() + session.query(ChatChannelCaches).delete() + session.commit() + + if __name__ == "__main__": pass + _flush_chat_database() # # result = ( # session.query(ChatUserCaches) diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index c7ba01149..f1c795ebd 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -3,14 +3,13 @@ import backends.library.abstractions.handler_base as hb -from backends.library.abstractions.websocket_manager import WebSocketManager from backends.library.database.pg_orm import ( ChatChannelCaches, ChatUserCaches, ChatChannelUserCaches, ) from backends.protocols.gamespy.chat.brocker_manager import MANAGER -from backends.protocols.gamespy.chat.helper import ChannelHelper, ChannelUserHelper +from backends.protocols.gamespy.chat.helper import ChannelHelper import backends.protocols.gamespy.chat.data as data from backends.protocols.gamespy.chat.requests import ( AtmRequest, @@ -60,8 +59,10 @@ from frontends.gamespy.protocols.chat.contracts.results import ( CryptResult, GetCKeyResult, + GetChannelKeyResult, GetKeyResult, JoinResult, + KickResult, ListResult, ModeResult, NamesResult, @@ -167,21 +168,16 @@ def _boradcast(self) -> None: # todo boradcast message here # find channel user websockets assert self._channel - users = ( - self._session.query(ChatUserCaches) - .join( - ChatChannelUserCaches, - ChatUserCaches.nick_name == ChatChannelUserCaches.nick_name, - ) - .where(ChatChannelUserCaches.channel_name == self._channel.channel_name) - .all() - ) # type: ignore + assert isinstance(self._channel.channel_name, str) + result = data.get_websocket_addr_by_channel_name( + self._channel.channel_name, self._session + ) ws_list = [] - - for user in users: + + for ws in result: assert self._user - if user.websocket_address != self._user.websocket_address: # type: ignore - ws_list.append(user.websocket_address) + if ws != self._user.websocket_address: # type: ignore + ws_list.append(ws) MANAGER.broadcast(self._request, ws_list) @@ -271,7 +267,6 @@ def _data_operate(self) -> None: assert isinstance(chann.invited_nicks, list) chann.invited_nicks.append(self._request.nick_name) - data.db_commit(self._session) class ListHandler(HandlerBase): @@ -299,11 +294,19 @@ def _data_operate(self) -> None: ) def _result_construct(self) -> None: + assert self._user + assert isinstance(self._user.nick_name, str) + assert isinstance(self._user.user_name, str) + data = [] for d in self._data: dd = ListResult.ListInfo(**d) data.append(dd) - self._result = ListResult(user_irc_prefix="", channel_info_list=data) + self._result = ListResult( + invoker_nick_name=self._user.nick_name, + invoker_user_name=self._user.user_name, + channel_info_list=data, + ) class LoginPreAuthHandler(HandlerBase): @@ -322,6 +325,9 @@ class NickHandler(HandlerBase): _request: NickRequest def _data_operate(self) -> None: + # todo we remove expired data + # data.remove_expired_user_cache(self._session) + # data.remove_expired_channel_user_cache(self._session) # cache = data.get_user_cache_by_nick_name("172.19.0.5:52986") self._user.nick_name = self._request.nick_name # type: ignore self._session.commit() @@ -352,7 +358,6 @@ def _data_operate(self) -> None: else: # update user cache self._user.nick_name = self._request.nick_name # type: ignore - self._session.commit() def _result_construct(self) -> None: self._result = NickResult(nick_name=self._request.nick_name) @@ -362,10 +367,12 @@ class QuitHandler(HandlerBase): _request: QuitRequest def _data_operate(self) -> None: - data.remove_user_caches_by_ip_port( + data.remove_user_cache_by_ip_port( + self._request.client_ip, self._request.client_port, self._session + ) + data.remove_channel_user_caches_by_ip_port( self._request.client_ip, self._request.client_port, self._session ) - raise NotImplementedError() class RegisterNickHandler(HandlerBase): @@ -392,7 +399,6 @@ class UserHandler(HandlerBase): def _data_operate(self) -> None: self._user.user_name = self._request.user_name # type: ignore - self._session.commit() class WhoHandler(HandlerBase): @@ -426,17 +432,14 @@ class WhoIsHandler(HandlerBase): _request: WhoIsRequest def _data_operate(self) -> None: - self._data: tuple = data.get_whois_result( - self._request.nick_name, self._session - ) + self._data: dict = data.get_whois_result(self._request.nick_name, self._session) def _result_construct(self) -> None: self._result = WhoIsResult( - nick_name=self._data[0], - user_name=self._data[1], - name=self._data[2], - public_ip_address=self._data[3], - joined_channel_name=self._data[4], + nick_name=self._data["nick_name"], + user_name=self._data["user_name"], + public_ip_address=self._data["remote_ip"], + joined_channel_name=list(self._data["channels"]), ) @@ -470,32 +473,40 @@ def _data_operate(self) -> None: max_num_user=100, session=self._session, ) - ChannelHelper.join(self._channel, self._user, self._session) + self._channel_user = ChannelHelper.join( + self._channel, self._user, self._session + ) self._channel_users_info = ChannelHelper.get_channel_all_nicks( self._channel, self._session ) def _result_construct(self) -> None: - assert self._user is not None - assert isinstance(self._user.user_name, str) - assert isinstance(self._user.nick_name, str) - assert self._channel is not None - assert isinstance(self._channel.modes, list) + assert self._channel_user + assert isinstance(self._channel_user.nick_name, str) + assert isinstance(self._channel_user.user_name, str) self._result = JoinResult( - joiner_user_name=self._user.user_name, - joiner_nick_name=self._user.nick_name, + joiner_nick_name=self._channel_user.nick_name, + joiner_user_name=self._channel_user.user_name, ) class GetChannelKeyHandler(ChannelHandlerBase): - def _get_key_values(self): - assert isinstance(self._channel, ChatChannelCaches) - self._key_values = self._channel.key_values + _values: list - def _request_check(self) -> None: - super()._request_check() - self._get_key_values() + def _result_construct(self) -> None: + assert self._channel + assert isinstance(self._channel.key_values, dict) + assert self._user + assert isinstance(self._user.nick_name, str) + assert isinstance(self._user.user_name, str) + + self._result = GetChannelKeyResult( + channel_name=self._request.channel_name, + key_values=dict(self._channel.key_values), + nick_name=self._user.nick_name, + user_name=self._user.user_name, + ) class GetCKeyHandler(ChannelHandlerBase): @@ -562,7 +573,23 @@ def _data_operate(self) -> None: assert self._channel assert self._channel_user assert self._kickee - ChannelHelper.kick(self._channel, self._channel_user, self._kickee) + ChannelHelper.kick( + self._session, + self._channel, + self._channel_user, + self._kickee, + ) + + def _result_construct(self) -> None: + assert self._channel_user + assert isinstance(self._channel_user.nick_name, str) + assert isinstance(self._channel_user.user_name, str) + self._result = KickResult( + channel_name=self._request.channel_name, + kicker_user_name=self._channel_user.user_name, + kicker_nick_name=self._channel_user.nick_name, + kickee_nick_name=self._request.kickee_nick_name, + ) class ModeHandler(ChannelHandlerBase): @@ -625,7 +652,7 @@ class PartHandler(ChannelHandlerBase): def _data_operate(self) -> None: assert self._channel assert self._channel_user - ChannelHelper.quit(self._channel, self._channel_user) + ChannelHelper.quit(self._channel, self._channel_user, self._session) def _result_construct(self) -> None: assert self._channel_user @@ -633,10 +660,12 @@ def _result_construct(self) -> None: assert isinstance(self._channel_user.is_channel_creator, bool) assert isinstance(self._channel_user.is_channel_operator, bool) assert isinstance(self._channel.channel_name, str) + assert isinstance(self._channel_user.nick_name, str) + assert isinstance(self._channel_user.user_name, str) - irc = ChannelUserHelper.get_user_irc_prefix(self._channel_user) self._result = PartResult( - leaver_irc_prefix=irc, + leaver_nick_name=self._channel_user.nick_name, + leaver_user_name=self._channel_user.user_name, is_channel_creator=self._channel_user.is_channel_creator, channel_name=self._channel.channel_name, ) @@ -651,13 +680,16 @@ def _request_check(self) -> None: assert isinstance(self._channel_user.is_channel_operator, bool) if self._channel_user.is_channel_operator: self._channel.key_values = self._request.key_values # type:ignore - data.db_commit(self._session) def _result_construct(self) -> None: assert self._channel_user - irc = ChannelUserHelper.get_user_irc_prefix(self._channel_user) + assert isinstance(self._channel_user.nick_name, str) + assert isinstance(self._channel_user.user_name, str) + self._result = SetChannelKeyResult( - channel_user_irc_prefix=irc, channel_name=self._request.channel_name + setter_nick_name=self._channel_user.nick_name, + setter_user_name=self._channel_user.user_name, + channel_name=self._request.channel_name, ) @@ -670,7 +702,6 @@ class SetCkeyHandler(ChannelHandlerBase): def _data_operate(self) -> None: self._channel_user.key_values = self._request.key_values # type:ignore - data.db_commit(self._session) self._is_broadcast = True def _result_construct(self) -> None: diff --git a/src/backends/protocols/gamespy/chat/helper.py b/src/backends/protocols/gamespy/chat/helper.py index 7ca84286c..796c692c2 100644 --- a/src/backends/protocols/gamespy/chat/helper.py +++ b/src/backends/protocols/gamespy/chat/helper.py @@ -1,5 +1,4 @@ from datetime import datetime -from enum import Enum from typing import cast from uuid import UUID @@ -11,7 +10,6 @@ ) import backends.protocols.gamespy.chat.data as data from backends.protocols.gamespy.chat.requests import ModeRequest -from frontends.gamespy.protocols.chat.abstractions.contract import SERVER_DOMAIN from frontends.gamespy.protocols.chat.aggregates.enums import ModeName, ModeOperation from frontends.gamespy.protocols.chat.aggregates.exceptions import ( BadChannelKeyException, @@ -23,11 +21,6 @@ from sqlalchemy.orm import Session -class ChannelUserProperty(Enum): - CHANNEL_CREATOR = "channel_creator" - CHANNEL_OPERATOR = "channel_operator" - - class ChannelUserHelper: @staticmethod def get_mode_string(user: ChatChannelUserCaches): @@ -41,23 +34,39 @@ def get_mode_string(user: ChatChannelUserCaches): return buffer @staticmethod - def get_user_irc_prefix(user: ChatChannelUserCaches): - irc = f"{user.nick_name}!{user.user_name}@{SERVER_DOMAIN}" - return irc + def connect(ip: str, port: str): + pass + + @staticmethod + def disconnect(websocket_address: str): + with Session(ENGINE) as session: + user = data.get_user_cache_by_ws(websocket_address, session) + if user is None: + return + session.delete(user) + channel_users = data.get_channel_user_list_by_ip_port( + user.remote_ip, # type: ignore + user.remote_port, # type: ignore + session, + ) + for user in channel_users: + session.delete(user) + session.commit() class ChannelHelper: @staticmethod def join( channel: ChatChannelCaches, user: ChatUserCaches, session: Session - ) -> None: + ) -> ChatChannelUserCaches: assert isinstance(channel, ChatChannelCaches) + assert isinstance(channel.channel_name, str) assert isinstance(channel.modes, list) assert isinstance(user.nick_name, str) assert isinstance(channel.banned_nicks, list) # 1 check if is a invited channel # 1.1 check if user is in a invited list - channel_modes = [ModeName(m) for m in channel.modes] + channel_modes = [ModeName(m) for m in channel.modes] if ModeName.INVITED_ONLY in channel_modes: if user.nick_name not in channel.invited_nicks: raise InviteOnlyChanException( @@ -69,6 +78,9 @@ def join( raise BannedFromChanException( "can not join channel, because you are in ban list" ) + + data.check_channel_user_trash_data(channel, user, session) + if channel.creator == user.nick_name: # type:ignore is_creator = True else: @@ -86,10 +98,14 @@ def join( remote_port=user.remote_port, ) session.add(chan_user) + session.commit() + return chan_user @staticmethod - def quit(channel: ChatChannelCaches, quiter: ChatChannelUserCaches) -> None: + def quit( + channel: ChatChannelCaches, quiter: ChatChannelUserCaches, session: Session + ) -> None: assert isinstance(quiter, ChatChannelUserCaches) assert isinstance(channel, ChatChannelCaches) assert isinstance(quiter.channel_name, str) @@ -98,12 +114,12 @@ def quit(channel: ChatChannelCaches, quiter: ChatChannelUserCaches) -> None: if quiter.channel_name != channel.channel_name: # type:ignore print("user is not in channel, so can not quit") return - with Session(ENGINE) as session: - session.delete(quiter) - session.commit() + session.delete(quiter) + session.commit() @staticmethod def kick( + session: Session, channel: ChatChannelCaches, kicker: ChatChannelUserCaches, kickee: ChatChannelUserCaches, @@ -124,9 +140,8 @@ def kick( ) if not kicker.is_channel_operator: # type:ignore raise BadChannelKeyException("kick failed, kicker is not channel operator") - with Session(ENGINE) as session: - session.delete(kickee) - session.commit() + session.delete(kickee) + session.commit() @staticmethod def invite( diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index d23ed6489..0e32550dc 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -10,8 +10,11 @@ ModeHandler, NamesHandler, NickHandler, + PartHandler, PrivateHandler, + QuitHandler, UserHandler, + WhoIsHandler, ) from backends.protocols.gamespy.chat.requests import ( AtmRequest, @@ -66,6 +69,7 @@ async def websocket_endpoint(ws: WebSocket): except WebSocketDisconnect: if ws.client is not None: MANAGER.disconnect(ws) + # todo remove chat info by websocket print("Client disconnected") @@ -120,7 +124,9 @@ def nick(request: NickRequest): @router.post(f"{CHAT}/QuitHandler") def quit(request: QuitRequest): - raise NotImplementedError() + handler = QuitHandler(request) + handler.handle() + return handler.response @router.post(f"{CHAT}/SetKeyHandler") @@ -148,7 +154,9 @@ def who(request: WhoRequest): @router.post(f"{CHAT}/WhoIsHandler") def whois(request: WhoIsRequest): - raise NotImplementedError() + handler = WhoIsHandler(request) + handler.handle() + return handler.response # region channel @@ -190,7 +198,9 @@ def names(request: NamesRequest): @router.post(f"{CHAT}/PartHandler") def part(request: PartRequest): - raise NotImplementedError() + handler = PartHandler(request) + handler.handle() + return handler.response @router.post(f"{CHAT}/SetChannelKeyHandler") diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index 8770120a5..d190bfce4 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -1,6 +1,6 @@ from ipaddress import IPv4Address from uuid import UUID -from fastapi import FastAPI +from fastapi import FastAPI, status from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse from pydantic import BaseModel @@ -37,14 +37,14 @@ @app.exception_handler(UniSpyException) def unispy_exception_handler(_, exc: UniSpyException): logger.error(exc.message) - return JSONResponse({"error": exc.message}) + return JSONResponse({"error": exc.message}, status_code=450) @app.exception_handler(RequestValidationError) def validation_exception_handler(_, exc: RequestValidationError): str_error = str(exc.args) logger.error(str_error) - return JSONResponse({"error": str_error}) + return JSONResponse({"error": str_error}, status_code=status.HTTP_400_BAD_REQUEST) @app.exception_handler(Exception) @@ -53,7 +53,9 @@ def handle_unispy_exception(_, exc: Exception): if len(str_error) == 0: str_error = exc.__class__.__name__ logger.error(str_error) - return JSONResponse({"error": str_error}) + return JSONResponse( + {"error": str_error}, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR + ) @app.post("/") diff --git a/src/backends/tests/gamespy/chat/room_tests.py b/src/backends/tests/gamespy/chat/room_tests.py index dd5756427..078f5d961 100644 --- a/src/backends/tests/gamespy/chat/room_tests.py +++ b/src/backends/tests/gamespy/chat/room_tests.py @@ -1,4 +1,3 @@ -# # import unittest # from frontends.gamespy.library.tests.mock_objects import BrokerMock @@ -22,5 +21,318 @@ # pass +# testclient.TestClient.get() +from fastapi.testclient import TestClient +from backends.routers.home import app +import unittest -# testclient.TestClient.get() \ No newline at end of file +client = TestClient(app) + + +class RoomTest(unittest.TestCase): + def test_sdk(self): + client1_msg = [ + { + "raw_request": "CRYPT des 1 gmtest", + "command_name": "CRYPT", + "version_id": "1", + "gamename": "gmtest", + "websocket_address": "127.0.0.1:60720", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40394, + }, + { + "raw_request": "USRIP", + "command_name": "USRIP", + "websocket_address": "127.0.0.1:60720", + "remote_ip": "172.19.0.5", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40394, + }, + { + "raw_request": "USER ChatCUser 127.0.0.1 unispy_server_dev :ChatCName", + "command_name": "USER", + "user_name": "ChatCUser", + "local_ip_address": "127.0.0.1", + "server_name": "unispy_server_dev", + "name": "ChatCName", + "websocket_address": "127.0.0.1:60720", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40394, + }, + { + "raw_request": "NICK ChatC660", + "command_name": "NICK", + "nick_name": "ChatC660", + "websocket_address": "127.0.0.1:60720", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40394, + }, + { + "raw_request": "JOIN #GSP!gmtest ", + "broad_cast_raw": None, + "password": None, + "command_name": "JOIN", + "channel_name": "#GSP!gmtest", + "websocket_address": "127.0.0.1:60720", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40394, + }, + { + "raw_request": "NAMES #GSP!gmtest", + "broad_cast_raw": None, + "command_name": "NAMES", + "channel_name": "#GSP!gmtest", + "websocket_address": "127.0.0.1:60720", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40394, + }, + { + "raw_request": "MODE #GSP!gmtest", + "broad_cast_raw": None, + "command_name": "MODE", + "channel_name": "#GSP!gmtest", + "mode_operations": {}, + "request_type": 0, + "websocket_address": "127.0.0.1:60720", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40394, + }, + { + "raw_request": "PRIVMSG #GSP!gmtest :Hi", + "broad_cast_raw": "ChatC660!ChatCUser@unispy.net PRIVMSG #GSP!gmtest :Hi", + "command_name": "PRIVMSG", + "channel_name": "#GSP!gmtest", + "type": 0, + "message": "Hi", + "websocket_address": "127.0.0.1:60720", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40394, + }, + { + "raw_request": "NAMES #GSP!gmtest", + "broad_cast_raw": None, + "command_name": "NAMES", + "channel_name": "#GSP!gmtest", + "websocket_address": "127.0.0.1:60720", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40394, + }, + { + "raw_request": "WHOIS ChatC660", + "command_name": "WHOIS", + "nick_name": "ChatC660", + "websocket_address": "127.0.0.1:60720", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40394, + }, + { + "raw_request": "PRIVMSG #GSP!gmtest :Bye", + "broad_cast_raw": "ChatC660!ChatCUser@unispy.net PRIVMSG #GSP!gmtest :Bye", + "command_name": "PRIVMSG", + "channel_name": "#GSP!gmtest", + "type": 0, + "message": "Bye", + "websocket_address": "127.0.0.1:60720", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40394, + }, + { + "raw_request": "PART #GSP!gmtest :", + "broad_cast_raw": None, + "command_name": "PART", + "channel_name": "#GSP!gmtest", + "reason": "", + "websocket_address": "127.0.0.1:60720", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40394, + }, + { + "raw_request": "QUIT :Later!", + "command_name": "QUIT", + "reason": "Later!", + "websocket_address": "127.0.0.1:60720", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40394, + }, + ] + + client2_msg = [ + { + "raw_request": "CRYPT des 1 gmtest", + "command_name": "CRYPT", + "version_id": "1", + "gamename": "gmtest", + "websocket_address": "127.0.0.1:60721", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40395, + }, + { + "raw_request": "USRIP", + "command_name": "USRIP", + "websocket_address": "127.0.0.1:60721", + "remote_ip": "172.19.0.5", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40395, + }, + { + "raw_request": "USER ChatCUser1 127.0.0.1 unispy_server_dev :ChatCName", + "command_name": "USER", + "user_name": "ChatCUser1", + "local_ip_address": "127.0.0.1", + "server_name": "unispy_server_dev", + "name": "ChatCName", + "websocket_address": "127.0.0.1:60721", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40395, + }, + { + "raw_request": "NICK ChatC661", + "command_name": "NICK", + "nick_name": "ChatC661", + "websocket_address": "127.0.0.1:60721", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40395, + }, + { + "raw_request": "JOIN #GSP!gmtest ", + "broad_cast_raw": None, + "password": None, + "command_name": "JOIN", + "channel_name": "#GSP!gmtest", + "websocket_address": "127.0.0.1:60721", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40395, + }, + { + "raw_request": "NAMES #GSP!gmtest", + "broad_cast_raw": None, + "command_name": "NAMES", + "channel_name": "#GSP!gmtest", + "websocket_address": "127.0.0.1:60721", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40395, + }, + { + "raw_request": "MODE #GSP!gmtest", + "broad_cast_raw": None, + "command_name": "MODE", + "channel_name": "#GSP!gmtest", + "mode_operations": {}, + "request_type": 0, + "websocket_address": "127.0.0.1:60721", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40395, + }, + { + "raw_request": "PRIVMSG #GSP!gmtest :Hi", + "broad_cast_raw": "ChatC661!ChatCUser1@unispy.net PRIVMSG #GSP!gmtest :Hi", + "command_name": "PRIVMSG", + "channel_name": "#GSP!gmtest", + "type": 0, + "message": "Hi", + "websocket_address": "127.0.0.1:60721", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40395, + }, + { + "raw_request": "NAMES #GSP!gmtest", + "broad_cast_raw": None, + "command_name": "NAMES", + "channel_name": "#GSP!gmtest", + "websocket_address": "127.0.0.1:60721", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40395, + }, + { + "raw_request": "WHOIS ChatC661", + "command_name": "WHOIS", + "nick_name": "ChatC661", + "websocket_address": "127.0.0.1:60721", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40395, + }, + { + "raw_request": "PRIVMSG #GSP!gmtest :Bye", + "broad_cast_raw": "ChatC661!ChatCUser1@unispy.net PRIVMSG #GSP!gmtest :Bye", + "command_name": "PRIVMSG", + "channel_name": "#GSP!gmtest", + "type": 0, + "message": "Bye", + "websocket_address": "127.0.0.1:60721", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40395, + }, + { + "raw_request": "PART #GSP!gmtest :", + "broad_cast_raw": None, + "command_name": "PART", + "channel_name": "#GSP!gmtest", + "reason": "", + "websocket_address": "127.0.0.1:60721", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40395, + }, + { + "raw_request": "QUIT :Later!", + "command_name": "QUIT", + "reason": "Later!", + "websocket_address": "127.0.0.1:60721", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 40395, + }, + ] + + api = [ + "GameSpy/Chat/CryptHandler", + "GameSpy/Chat/UserIPHandler", + "GameSpy/Chat/UserHandler", + "GameSpy/Chat/NickHandler", + "GameSpy/Chat/JoinHandler", + "GameSpy/Chat/NamesHandler", + "GameSpy/Chat/ModeHandler", + "GameSpy/Chat/PrivateHandler", + "GameSpy/Chat/NamesHandler", + "GameSpy/Chat/WhoIsHandler", + "GameSpy/Chat/PrivateHandler", + "GameSpy/Chat/PartHandler", + "GameSpy/Chat/QuitHandler", + ] + for c1, c2, route in zip(client1_msg, client2_msg, api): + try: + client.post(url=route, json=c1) + client.post(url=route, json=c2) + except Exception as e: + print(e) + pass + + +if __name__ == "__main__": + test = RoomTest() + test.test_sdk() diff --git a/src/backends/tests/gamespy/query_report/data_fetch_tests.py b/src/backends/tests/gamespy/query_report/data_fetch_tests.py index 2cf159ec6..3dc64c0c4 100644 --- a/src/backends/tests/gamespy/query_report/data_fetch_tests.py +++ b/src/backends/tests/gamespy/query_report/data_fetch_tests.py @@ -30,6 +30,7 @@ def test_get_peer_staging_channels(self): data.get_peer_staging_channels, "unispy_test_game_name", 0, + session, ) session.delete(cache) session.commit() diff --git a/src/frontends/gamespy/library/abstractions/handler.py b/src/frontends/gamespy/library/abstractions/handler.py index 8ff1429f2..3d9cf605e 100644 --- a/src/frontends/gamespy/library/abstractions/handler.py +++ b/src/frontends/gamespy/library/abstractions/handler.py @@ -62,7 +62,7 @@ def handle(self) -> None: self._response_send() except Exception as ex: self._handle_exception(ex) - + def _request_check(self) -> None: """ raw request is nessesary param @@ -99,6 +99,7 @@ def _get_url(self) -> str: }" return url + @final def _upload_data(self): """ whether need send data to backend @@ -120,13 +121,25 @@ def _upload_data(self): ) else: raise UniSpyException("backends is not avaliable") + # todo http code to determine object type - if response.status_code != 200: + if response.status_code in [200, 450, 500]: + self._http_result = response.json() + else: raise UniSpyException( f"failed to upload data to backends. reason: {response.text}" ) - - self._http_result = response.json() + # match response.status_code: + # case 200: + # pass + # case 450: + # self._handle_upload_error() + # case 500: + # self._handle_upload_error() + # case _: + # raise UniSpyException( + # f"failed to upload data to backends. reason: {response.text}" + # ) if "error" in self._http_result: self._handle_upload_error() @@ -138,6 +151,7 @@ def _handle_upload_error(self): # we raise the error as UniSpyException raise UniSpyException(self._http_result["error"]) + @final def _fetch_data(self): """ whether need get data from backend. @@ -169,6 +183,7 @@ def _handle_exception(self, ex: Exception) -> None: """ UniSpyException.handle_exception(ex, self._client) + @final def _log_current_class(self) -> None: if self._client is None: # todo diff --git a/src/frontends/gamespy/library/exceptions/general.py b/src/frontends/gamespy/library/exceptions/general.py index c7b545819..43ee310f3 100644 --- a/src/frontends/gamespy/library/exceptions/general.py +++ b/src/frontends/gamespy/library/exceptions/general.py @@ -1,3 +1,4 @@ +import traceback from typing import TYPE_CHECKING, Optional from frontends.gamespy.library.configs import CONFIG @@ -26,7 +27,7 @@ def handle_exception(e: Exception, client: Optional["ClientBase"] = None): ex: UniSpyException = e # type:ignore client.log_error(ex.message) else: - client.log_error(str(e)) + client.log_error(traceback.format_exc()) # if we are unittesting we raise the exception out if CONFIG.unittest.is_raise_except: raise e diff --git a/src/frontends/gamespy/protocols/chat/abstractions/contract.py b/src/frontends/gamespy/protocols/chat/abstractions/contract.py index aac0361e0..9e70491fb 100644 --- a/src/frontends/gamespy/protocols/chat/abstractions/contract.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/contract.py @@ -77,6 +77,10 @@ def __init__(self, request: RequestBase, result: ResultBase | None) -> None: assert issubclass(type(result), ResultBase) assert issubclass(type(request), RequestBase) + @staticmethod + def build_irc_user_prefix(nick_name: str, user_name: str): + return f"{nick_name}!{user_name}@{SERVER_DOMAIN}" + # region Brocker diff --git a/src/frontends/gamespy/protocols/chat/abstractions/handler.py b/src/frontends/gamespy/protocols/chat/abstractions/handler.py index 05bf8759e..7860042ed 100644 --- a/src/frontends/gamespy/protocols/chat/abstractions/handler.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/handler.py @@ -25,6 +25,7 @@ class CmdHandlerBase(lib.CmdHandlerBase): def __init__(self, client: ClientBase, request: RequestBase): super().__init__(client, request) assert issubclass(type(request), RequestBase) + def _request_check(self) -> None: super()._request_check() assert self._client.brocker @@ -46,9 +47,11 @@ class PostLoginHandlerBase(CmdHandlerBase): class ChannelRequestBase(RequestBase): channel_name: str + broad_cast_raw: str | None def __init__(self, raw_request: str) -> None: super().__init__(raw_request) + self.broad_cast_raw = None def parse(self) -> None: super().parse() @@ -65,6 +68,22 @@ def __init__(self, request: RequestBase, result: ResultBase) -> None: assert isinstance(request, RequestBase) assert isinstance(result, ResultBase) + @staticmethod + def build_value_str(keys: list, kv: dict) -> str: + v_str = "" + for k in keys: + v_str += "\\" + if k in kv: + v_str += kv[k] + return v_str + + @staticmethod + def build_key_value_str(kv: dict) -> str: + kv_str = "" + for k, v in kv.items(): + kv_str += f"\\{k}\\{v}" + return kv_str + class ChannelHandlerBase(PostLoginHandlerBase): _request: ChannelRequestBase @@ -121,7 +140,7 @@ def parse(self): class MessageResultBase(ResultBase): - user_irc_prefix: str + irc_prefix: str target_name: str @@ -138,7 +157,6 @@ def _request_check(self) -> None: self._request.broad_cast_raw = ( f"{self._client.info.irc_prefix} {self._request.raw_request}" ) - def _update_channel_cache(self): """we do nothing here, channel message do not need to update channel cache""" diff --git a/src/frontends/gamespy/protocols/chat/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py index 0ff1c28df..3c6bd4d70 100644 --- a/src/frontends/gamespy/protocols/chat/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -75,7 +75,6 @@ GetUdpRelayRequest, ) from frontends.gamespy.protocols.chat.aggregates.enums import ModeRequestType -from typing import Type from frontends.gamespy.library.abstractions.client import ClientBase from frontends.gamespy.protocols.chat.abstractions.contract import RequestBase from frontends.gamespy.protocols.chat.abstractions.handler import ( @@ -277,11 +276,11 @@ def _response_construct(self) -> None: class WhoIsHandler(CmdHandlerBase): _request: WhoIsRequest _result: WhoIsResult - _result_type: Type = WhoIsResult def __init__(self, client: ClientBase, request: WhoIsRequest): assert isinstance(request, WhoIsRequest) super().__init__(client, request) + self._result_cls = WhoIsResult def _response_construct(self) -> None: self._response = WhoIsResponse(self._result) @@ -447,6 +446,7 @@ class ATMHandler(MessageHandlerBase): _result: ATMResult def __init__(self, client: ClientBase, request: ATMRequest): + super().__init__(client, request) assert isinstance(request, ATMRequest) self._is_fetching = False diff --git a/src/frontends/gamespy/protocols/chat/contracts/requests.py b/src/frontends/gamespy/protocols/chat/contracts/requests.py index f3eb81d97..2154f8e65 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/requests.py +++ b/src/frontends/gamespy/protocols/chat/contracts/requests.py @@ -308,6 +308,10 @@ def parse(self) -> None: class GetChannelKeyRequest(ChannelRequestBase): + """ + sprintf(buffer, "GETCHANKEY %s %s 0 :", channel, cookie); + """ + cookie: str keys: list @@ -324,6 +328,10 @@ def parse(self): class GetCKeyRequest(ChannelRequestBase): + """ + sprintf(buffer, "GETCKEY %s %s %s 0 :", channel, nick, cookie); + """ + nick_name: str | None cookie: str keys: list @@ -504,6 +512,10 @@ def parse(self): class SetChannelKeyRequest(ChannelRequestBase): + """ + sprintf(buffer, "SETCHANKEY %s :", channel); + """ + key_value_string: str key_values: dict[str, str] @@ -517,12 +529,16 @@ def parse(self): class SetCKeyRequest(ChannelRequestBase): + """ + sprintf(buffer, "SETCKEY %s %s :", channel, user); + """ + nick_name: str cookie: str is_broadcast: bool key_value_string: str key_values: dict[str, str] - + def parse(self) -> None: super().parse() if self._cmd_params is None: diff --git a/src/frontends/gamespy/protocols/chat/contracts/responses.py b/src/frontends/gamespy/protocols/chat/contracts/responses.py index 578a30e8e..b9ac7f9b8 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/responses.py +++ b/src/frontends/gamespy/protocols/chat/contracts/responses.py @@ -98,16 +98,17 @@ def __init__(self, result: ListResult) -> None: self._result = result def build(self) -> None: + involer_irc_prefix = ResponseBase.build_irc_user_prefix( + self._result.invoker_nick_name, self._result.invoker_user_name + ) self.sending_buffer = "" for ( channel_name, total_channel_user, channel_topic, ) in self._result.channel_info_list: - self.sending_buffer += f":{self._result.user_irc_prefix} {ResponseCode.LISTSTART.value} * {channel_name} {total_channel_user} {channel_topic}\r\n" # noqa - self.sending_buffer += ( - f":{self._result.user_irc_prefix} {ResponseCode.LISTEND.value}\r\n" # noqa - ) + self.sending_buffer += f":{involer_irc_prefix} {ResponseCode.LISTSTART.value} * {channel_name} {total_channel_user} {channel_topic}\r\n" # noqa + self.sending_buffer += f":{involer_irc_prefix} {ResponseCode.LISTEND.value}\r\n" # noqa class LoginResponse(ResponseBase): @@ -160,20 +161,20 @@ class WhoIsResponse(ResponseBase): _result: WhoIsResult def __init__(self, result: WhoIsResult) -> None: - assert isinstance(result, UserIPResult) + assert isinstance(result, WhoIsResult) self._result = result def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.WHOISUSER.value} {self._result.nick_name} {self._result.name} {self._result.user_name} {self._result.public_ip_address} * :{self._result.user_name}\r\n" # noqa + self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.WHOISUSER.value} {self._result.nick_name} {self._result.user_name} {self._result.user_name} {self._result.public_ip_address} * :{self._result.user_name}\r\n" # noqa if len(self._result.joined_channel_name) != 0: channel_name = "" for name in self._result.joined_channel_name: channel_name += f"@{name} " # noqa - self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.WHOISCHANNELS.value} {self._result.nick_name} {self._result.name} :{channel_name}\r\n" # noqa + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.WHOISCHANNELS.value} {self._result.nick_name} {self._result.user_name} :{channel_name}\r\n" # noqa - self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDOFWHOIS.value} {self._result.nick_name} {self._result.name} :End of WHOIS list. \r\n" # noqa + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDOFWHOIS.value} {self._result.nick_name} {self._result.user_name} :End of WHOIS list. \r\n" # noqa class WhoResponse(ResponseBase): @@ -212,7 +213,10 @@ class GetChannelKeyResponse(ChannelResponseBase): _result: GetChannelKeyResult def build(self) -> None: - self.sending_buffer = f":{self._result.channel_user_irc_prefix} {ResponseCode.GETCHANKEY.value} * {self._result.channel_name} {self._request.cookie} {self._result.values}\r\n" + involker_irc_prefix = ResponseBase.build_irc_user_prefix( + self._result.nick_name, self._result.user_name + ) + self.sending_buffer = f":{involker_irc_prefix} {ResponseCode.GETCHANKEY.value} * {self._result.channel_name} {self._request.cookie} {self._result.key_values}\r\n" class GetCKeyResponse(ResponseBase): @@ -242,7 +246,9 @@ def __init__(self, request: JoinRequest, result: JoinResult) -> None: super().__init__(request, result) def build(self) -> None: - joiner_irc_prefix = f"{self._result.joiner_nick_name}!{self._result.joiner_user_name}@{SERVER_DOMAIN}" + joiner_irc_prefix = ResponseBase.build_irc_user_prefix( + self._result.joiner_nick_name, self._result.joiner_user_name + ) self.sending_buffer = f"{joiner_irc_prefix} {ResponseCode.JOIN.value} {self._request.channel_name}\r\n" @@ -256,7 +262,9 @@ def __init__(self, request: KickRequest, result: KickResult) -> None: super().__init__(request, result) def build(self) -> None: - kicker_irc_prefix = f"{self._result.kicker_nick_name}!{self._result.kicker_user_name}@{SERVER_DOMAIN}" + kicker_irc_prefix = ResponseBase.build_irc_user_prefix( + self._result.kicker_nick_name, self._result.kicker_user_name + ) self.sending_buffer = f"{kicker_irc_prefix} {ResponseCode.KICK.value} {self._result.channel_name} {self._result.kickee_nick_name} :{self._request.reason}\r\n" @@ -273,7 +281,6 @@ def __init__(self, request: ModeRequest, result: ModeResult) -> None: def get_mode_str(modes: list[str]): if len(modes) == 0: return "" - modes_str = "+" for m in modes: modes_str += m @@ -325,7 +332,10 @@ def __init__(self, request: PartRequest, result: PartResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.leaver_irc_prefix} {ResponseCode.PART.value} {self._result.channel_name} :{self._request.reason}\r\n" + leaver_irc_prefix = ResponseBase.build_irc_user_prefix( + self._result.leaver_nick_name, self._result.leaver_user_name + ) + self.sending_buffer = f":{leaver_irc_prefix} {ResponseCode.PART.value} {self._result.channel_name} :{self._request.reason}\r\n" class SetChannelKeyResponse(ChannelResponseBase): @@ -340,7 +350,10 @@ def __init__( super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.channel_user_irc_prefix} {ResponseCode.GETCHANKEY.value} * {self._request.channel_name} BCAST {self._request.key_value_string}\r\n" + setter_irc_prefix = ResponseBase.build_irc_user_prefix( + self._result.setter_nick_name, self._result.setter_user_name + ) + self.sending_buffer = f":{setter_irc_prefix} {ResponseCode.GETCHANKEY.value} * {self._request.channel_name} BCAST {self._request.key_value_string}\r\n" class SetCKeyResponse(ResponseBase): @@ -385,7 +398,7 @@ def __init__(self, request: ATMRequest, result: ATMResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} {ResponseCode.ATM.value} {self._result.target_name} :{self._request.message}\r\n" + self.sending_buffer = f":{self._result.irc_prefix} {ResponseCode.ATM.value} {self._result.target_name} :{self._request.message}\r\n" class NoticeResponse(ResponseBase): @@ -399,7 +412,7 @@ def __init__(self, request: NoticeRequest, result: NoticeResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} {ResponseCode.NOTICE.value} {self._result.target_name} {self._request.message}\r\n" + self.sending_buffer = f":{self._result.irc_prefix} {ResponseCode.NOTICE.value} {self._result.target_name} {self._request.message}\r\n" class PrivateResponse(ResponseBase): @@ -412,7 +425,7 @@ def __init__(self, request: PrivateRequest, result: PrivateResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} {ResponseCode.PRIVMSG.value} {self._result.target_name} :{self._request.message}\r\n" + self.sending_buffer = f":{self._result.irc_prefix} {ResponseCode.PRIVMSG.value} {self._result.target_name} :{self._request.message}\r\n" class UTMResponse(ResponseBase): @@ -425,6 +438,6 @@ def __init__(self, request: UTMRequest, result: UTMResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.user_irc_prefix} { - ResponseCode.UTM.value - } {self._result.target_name} :{self._request.message}" + self.sending_buffer = f":{self._result.irc_prefix} {ResponseCode.UTM.value} { + self._result.target_name + } :{self._request.message}" diff --git a/src/frontends/gamespy/protocols/chat/contracts/results.py b/src/frontends/gamespy/protocols/chat/contracts/results.py index 4898ab8bb..91991d7ef 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/results.py +++ b/src/frontends/gamespy/protocols/chat/contracts/results.py @@ -20,8 +20,8 @@ class ListInfo(BaseModel): channel_name: str total_channel_user: int channel_topic: str - - user_irc_prefix: str + invoker_nick_name:str + invoker_user_name:str channel_info_list: list[ListInfo] = [] """(channel_name:str,total_channel_user:int,channel_topic:str)""" @@ -60,9 +60,8 @@ class UserIPResult(ResultBase): class WhoIsResult(ResultBase): nick_name: str user_name: str - name: str public_ip_address: str - joined_channel_name: list[str] = [] + joined_channel_name: list[str] class WhoResult(ResultBase): @@ -80,9 +79,10 @@ class WhoInfo(BaseModel): class GetChannelKeyResult(ResultBase): - channel_user_irc_prefix: str + nick_name: str + user_name: str channel_name: str - values: str + key_values: dict class GetCKeyResult(ResultBase): @@ -114,10 +114,8 @@ class NamesResult(ResultBase): class JoinResult(ResultBase): - joiner_user_name: str joiner_nick_name: str - # channel_nicks_data: list[NamesResultData] - # channel_modes: list[str] + joiner_user_name: str class KickResult(ResultBase): @@ -128,7 +126,8 @@ class KickResult(ResultBase): class PartResult(ResultBase): - leaver_irc_prefix: str + leaver_nick_name: str + leaver_user_name: str is_channel_creator: bool channel_name: str @@ -139,7 +138,8 @@ class TopicResult(ResultBase): class SetChannelKeyResult(ResultBase): - channel_user_irc_prefix: str + setter_nick_name: str + setter_user_name: str channel_name: str diff --git a/src/frontends/tests/gamespy/chat/mock_objects.py b/src/frontends/tests/gamespy/chat/mock_objects.py index f7a52671f..1bda428f8 100644 --- a/src/frontends/tests/gamespy/chat/mock_objects.py +++ b/src/frontends/tests/gamespy/chat/mock_objects.py @@ -59,6 +59,10 @@ def publish_message(self, message): def unsubscribe(self): pass + @property + def ip_port(self): + return "127.0.0.1:10086" + class ClientMock(Client): def start_brocker(self): @@ -93,8 +97,7 @@ def create_client() -> Client: config, JoinHandler, JoinResult( - joiner_nick_name="xiaojiuwo", - joiner_user_name="unispy", + joiner_nick_name="nickname", joiner_user_name="username" ).model_dump(), ) create_mock_url(config, UserHandler, {"message": "ok"}) @@ -135,7 +138,8 @@ def create_client() -> Client: config, PartHandler, PartResult( - leaver_irc_prefix="test_prefix", + leaver_nick_name="nickname", + leaver_user_name="username", is_channel_creator=False, channel_name="test_chan", ).model_dump(), @@ -146,7 +150,9 @@ def create_client() -> Client: config, SetChannelKeyHandler, SetChannelKeyResult( - channel_user_irc_prefix="unispy!unispy@unispyserver", channel_name="test" + setter_nick_name="nickname", + setter_user_name="username", + channel_name="test", ).model_dump(), ) create_mock_url( @@ -155,9 +161,7 @@ def create_client() -> Client: create_mock_url( config, UTMHandler, - UTMResult( - user_irc_prefix="unispy!unispy@unispy", target_name="spyguy" - ).model_dump(), + UTMResult(irc_prefix="unispy!unispy@unispy", target_name="spyguy").model_dump(), ) if TYPE_CHECKING: From 421c312f770ac8b51c5ae9046381d8125ffd5284 Mon Sep 17 00:00:00 2001 From: koujiangheng Date: Fri, 19 Sep 2025 08:49:51 +0000 Subject: [PATCH 201/231] Fix:(chat) channel message broadcast issue --- .../library/abstractions/websocket_manager.py | 173 +++++++++--------- src/backends/library/database/pg_orm.py | 1 - .../protocols/gamespy/chat/brocker_manager.py | 77 +++++++- src/backends/protocols/gamespy/chat/data.py | 14 +- .../protocols/gamespy/chat/handlers.py | 107 +++++++---- src/backends/routers/gamespy/chat.py | 11 +- .../gamespy/library/abstractions/client.py | 5 +- .../library/abstractions/connections.py | 1 + .../gamespy/library/network/brockers.py | 19 +- .../protocols/chat/abstractions/handler.py | 39 ++-- .../protocols/chat/applications/client.py | 13 +- .../protocols/chat/applications/handlers.py | 63 +++++-- .../protocols/chat/applications/switcher.py | 8 +- .../protocols/chat/contracts/requests.py | 4 +- .../protocols/chat/contracts/responses.py | 62 ++++--- .../protocols/chat/contracts/results.py | 7 +- .../tests/gamespy/chat/mock_objects.py | 4 +- .../tests/gamespy/chat/request_tests.py | 6 +- 18 files changed, 380 insertions(+), 234 deletions(-) diff --git a/src/backends/library/abstractions/websocket_manager.py b/src/backends/library/abstractions/websocket_manager.py index 713055fd6..1a76e8e5c 100644 --- a/src/backends/library/abstractions/websocket_manager.py +++ b/src/backends/library/abstractions/websocket_manager.py @@ -1,89 +1,84 @@ -import asyncio -from logging import getLogger -from uuid import UUID -from fastapi import WebSocket - -from backends.protocols.gamespy.chat.requests import RequestBase -from frontends.gamespy.library.exceptions.general import UniSpyException - - -class WebSocketClient: - server_id: UUID - ip: str - port: int - ws: WebSocket - - def __init__(self, ws: WebSocket) -> None: - assert ws.client - ip, port = ws.client - self.ip = ip - self.port = port - self.ws = ws - - @property - def ip_port(self) -> str: - return f"{self.ip}:{self.port}" - - @staticmethod - def get_ip_port_str(ws: WebSocket): - assert ws.client - ip, port = ws.client - return f"{ip}:{port}" - - -class WebSocketManager: - client_pool: dict[str, WebSocketClient] - - def __init__(self) -> None: - self.client_pool = {} - - def create_client(self, ws: WebSocket) -> WebSocketClient: - client = WebSocketClient(ws) - return client - - def get_client(self, ws: WebSocket) -> WebSocketClient: - ip_port = WebSocketClient.get_ip_port_str(ws) - client = None - if ip_port in self.client_pool: - client = self.client_pool[ip_port] - if client is None: - raise UniSpyException( - "client is not existed in client pool. skip deleting." - ) - return client - - def connect(self, ws: WebSocket): - client = self.create_client(ws) - if client.ip_port not in self.client_pool: - self.client_pool[client.ip_port] = client - - def disconnect(self, ws: WebSocket): - """ - call at last in inherited classs - """ - temp = self.create_client(ws) - if temp.ip_port in self.client_pool: - del self.client_pool[temp.ip_port] - # todo remove record in database - - # todo check channelcache,usercache,channelusercache - - def broadcast(self, message: RequestBase, ws: list[str] | None = None): - try: - loop = asyncio.get_running_loop() - except RuntimeError: # No event loop is running - loop = asyncio.new_event_loop() - asyncio.set_event_loop(loop) - if ws is None: - clients = self.client_pool.values() - else: - clients = [] - for w in ws: - if w in self.client_pool: - clients.append(self.client_pool[w]) - else: - logger = getLogger("backend") - logger.info(f"{ws} not in websocket client list") - - for client in clients: - loop.create_task(client.ws.send_json(message.model_dump())) +# from logging import getLogger +# from uuid import UUID +# from fastapi import WebSocket + +# from frontends.gamespy.library.exceptions.general import UniSpyException +# from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage + + +# class WebSocketClient: +# server_id: UUID +# ip: str +# port: int +# ws: WebSocket + +# def __init__(self, ws: WebSocket) -> None: +# assert ws.client +# ip, port = ws.client +# self.ip = ip +# self.port = port +# self.ws = ws + +# @property +# def ip_port(self) -> str: +# return f"{self.ip}:{self.port}" + +# @staticmethod +# def get_ip_port_str(ws: WebSocket): +# assert ws.client +# ip, port = ws.client +# return f"{ip}:{port}" + + +# class WebSocketManager: +# client_pool: dict[str, WebSocketClient] + +# def __init__(self) -> None: +# self.client_pool = {} + +# def create_client(self, ws: WebSocket) -> WebSocketClient: +# client = WebSocketClient(ws) +# return client + +# def get_client(self, ws: WebSocket) -> WebSocketClient: +# ip_port = WebSocketClient.get_ip_port_str(ws) +# client = None +# if ip_port in self.client_pool: +# client = self.client_pool[ip_port] +# if client is None: +# raise UniSpyException( +# "client is not existed in client pool. skip deleting." +# ) +# return client + +# def connect(self, ws: WebSocket): +# client = self.create_client(ws) +# if client.ip_port not in self.client_pool: +# self.client_pool[client.ip_port] = client + +# def disconnect(self, ws: WebSocket): +# """ +# call at last in inherited classs +# """ +# temp = self.create_client(ws) +# if temp.ip_port in self.client_pool: +# del self.client_pool[temp.ip_port] +# # todo remove record in database + +# # todo check channelcache,usercache,channelusercache + +# async def broadcast(self, message: BrockerMessage, ws: list[str] | None = None): +# if ws is None: +# clients = self.client_pool.values() +# else: +# clients = [] +# for w in ws: +# if w in self.client_pool: +# clients.append(self.client_pool[w]) +# else: +# logger = getLogger("backend") +# logger.info(f"{ws} not in websocket client list") + +# for client in clients: +# await client.ws.send_json(message.model_dump_json()) + diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 6f15f4c7c..8eebcb2b7 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -32,7 +32,6 @@ import enum from sqlalchemy.orm.decl_api import DeclarativeBase - class IntEnum(TypeDecorator): impl = sa.Integer diff --git a/src/backends/protocols/gamespy/chat/brocker_manager.py b/src/backends/protocols/gamespy/chat/brocker_manager.py index d925f3dad..4b38ce28e 100644 --- a/src/backends/protocols/gamespy/chat/brocker_manager.py +++ b/src/backends/protocols/gamespy/chat/brocker_manager.py @@ -1,7 +1,76 @@ -from backends.library.abstractions.websocket_manager import WebSocketManager +from logging import Logger +import logging +from fastapi import WebSocket +from backends.library.database.pg_orm import ENGINE +from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage +import backends.protocols.gamespy.chat.data as data +from sqlalchemy.orm import Session -MANAGER = WebSocketManager() -# create redis pubsub to share message cross all backends -# currently we simply implement without redis pubsub +class ClientManager: + """ + current: single server mode + client1 -> frontend1 -> backend1 (rest api) + client2 <- | <- + client3 <- | + """ + + """ + future: distributed mode + client1 -> frontend1 -> backend1 (rest api) + client2 <- | <- + client3 <- | + -> (websocket) -> redis + client2 -> frontend2 <- backend2 <- | + client3 -> frontend3 <- backend3 <- | + client4 -> frontend4 <- backend4 <- | + """ + client_pool: dict[str, WebSocket] + logger: Logger + + def __init__(self) -> None: + self.client_pool = {} + self.logger = logging.getLogger("backend") + + def get_address_str(self, ws: WebSocket) -> str: + assert ws.client is not None + ws_address = f"{ws.client.host}:{ws.client.port}" + return ws_address + + def connect(self, ws: WebSocket): + assert ws.client is not None + ws_address = self.get_address_str(ws) + self.client_pool[ws_address] = ws + + def disconnect(self, ws: WebSocket): + assert ws.client is not None + ws_address = self.get_address_str(ws) + if ws_address in self.client_pool: + del self.client_pool[ws_address] + + def get_websocket(self, ws_address: str) -> WebSocket | None: + if ws_address in self.client_pool: + return self.client_pool[ws_address] + + def process_message(self, message: dict) -> BrockerMessage: + self.logger.info(f"[cast] [recv] {message}") + msg = BrockerMessage.model_validate(message) + return msg + + # create redis pubsub to share message cross all backends + # currently we simply implement without redis pubsub + async def broadcast(self, message: BrockerMessage, ws_client: WebSocket): + exclude_addr = self.get_address_str(ws_client) + with Session(ENGINE) as session: + wss = data.get_websocket_addr_by_channel_name(message.channel_name, session) + if exclude_addr in wss: + wss.remove(exclude_addr) + for ws_addr in wss: + if ws_addr in self.client_pool: + ws = self.client_pool[ws_addr] + await ws.send_json(message.model_dump_json()) + self.logger.info(f"[cast] [send] {message.model_dump_json()}") + + +MANAGER = ClientManager() diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index 8ea851bd4..e8b84ba08 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -550,15 +550,21 @@ def get_channel_user_cache_by_ip(ip: str, port: int, session: Session) -> list[d return data -def remove_expired_user_cache(session: Session): +def clean_expired_user_cache(session: Session): session.query(ChatUserCaches).where( - ChatUserCaches.update_time > (datetime.now() - timedelta(minutes=5)) + ChatUserCaches.update_time < (datetime.now() - timedelta(minutes=5)) ).delete() -def remove_expired_channel_user_cache(session: Session): +def clean_expired_channel_cache(session: Session): + session.query(ChatChannelCaches).where( + ChatChannelCaches.update_time < (datetime.now() - timedelta(minutes=5)) + ).delete() + + +def clean_expired_channel_user_cache(session: Session): session.query(ChatChannelUserCaches).where( - ChatUserCaches.update_time > (datetime.now() - timedelta(minutes=5)) + ChatUserCaches.update_time < (datetime.now() - timedelta(minutes=5)) ).delete() diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index f1c795ebd..7500e9420 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -8,7 +8,6 @@ ChatUserCaches, ChatChannelUserCaches, ) -from backends.protocols.gamespy.chat.brocker_manager import MANAGER from backends.protocols.gamespy.chat.helper import ChannelHelper import backends.protocols.gamespy.chat.data as data from backends.protocols.gamespy.chat.requests import ( @@ -57,6 +56,7 @@ NoSuchNickException, ) from frontends.gamespy.protocols.chat.contracts.results import ( + AtmResult, CryptResult, GetCKeyResult, GetChannelKeyResult, @@ -68,9 +68,12 @@ NamesResult, NamesResultData, NickResult, + NoticeResult, PartResult, + PrivateResult, SetChannelKeyResult, TopicResult, + UtmResult, WhoIsResult, WhoResult, ) @@ -96,17 +99,7 @@ def _get_user(self): self._user = data.get_user_cache_by_ip_port( self._request.client_ip, self._request.client_port, self._session ) - if self._user is None: - self._user = ChatUserCaches( - server_id=self._request.server_id, - remote_ip=self._request.client_ip, - remote_port=self._request.client_port, - update_time=datetime.now(), - nick_name=f"{self._request.client_ip}:{self._request.client_port}", - websocket_address=self._request.websocket_address, - ) - self._session.add(self._user) - else: + if self._user is not None: self._user.update_time = datetime.now() # type: ignore def _check_user(self): @@ -120,13 +113,11 @@ class ChannelHandlerBase(HandlerBase): _request: ChannelRequestBase _channel: ChatChannelCaches | None _channel_user: ChatChannelUserCaches | None - _is_broadcast: bool def __init__(self, request: RequestBase) -> None: super().__init__(request) self._channel = None self._channel_user = None - self._is_broadcast = False def _request_check(self) -> None: super()._request_check() @@ -164,33 +155,10 @@ def _check_channel_user(self): ) self._channel_user.update_time = datetime.now() # type: ignore - def _boradcast(self) -> None: - # todo boradcast message here - # find channel user websockets - assert self._channel - assert isinstance(self._channel.channel_name, str) - result = data.get_websocket_addr_by_channel_name( - self._channel.channel_name, self._session - ) - ws_list = [] - - for ws in result: - assert self._user - if ws != self._user.websocket_address: # type: ignore - ws_list.append(ws) - - MANAGER.broadcast(self._request, ws_list) - - def _response_construct(self) -> None: - super()._response_construct() - if self._is_broadcast: - self._boradcast() - class MessageHandlerBase(ChannelHandlerBase): def __init__(self, request: RequestBase) -> None: super().__init__(request) - self._is_broadcast = True # region General @@ -208,9 +176,28 @@ def _data_operate(self) -> None: class CryptHandler(HandlerBase): _request: CryptRequest + def _request_check(self) -> None: + # we just clean the garbage data + data.clean_expired_channel_user_cache(self._session) + data.clean_expired_channel_cache(self._session) + data.clean_expired_user_cache(self._session) + self._get_user() + if self._user is not None: + raise UniSpyException("user cache is trash in database") + def _data_operate(self) -> None: - assert self._user is not None - self._user.game_name = self._request.gamename # type: ignore + assert self._user is None + if self._user is None: + self._user = ChatUserCaches( + server_id=self._request.server_id, + remote_ip=self._request.client_ip, + remote_port=self._request.client_port, + update_time=datetime.now(), + nick_name=f"{self._request.client_ip}:{self._request.client_port}", + websocket_address=self._request.websocket_address, + game_name=self._request.gamename, + ) + self._session.add(self._user) self._secret_key = data.get_secret_key_by_game_name( self._request.gamename, self._session ) @@ -325,7 +312,7 @@ class NickHandler(HandlerBase): _request: NickRequest def _data_operate(self) -> None: - # todo we remove expired data + # todo we remove expired data # data.remove_expired_user_cache(self._session) # data.remove_expired_channel_user_cache(self._session) # cache = data.get_user_cache_by_nick_name("172.19.0.5:52986") @@ -397,6 +384,10 @@ def _data_operate(self) -> None: class UserHandler(HandlerBase): _request: UserRequest + def _request_check(self) -> None: + data.clean_expired_user_cache(self._session) + super()._request_check() + def _data_operate(self) -> None: self._user.user_name = self._request.user_name # type: ignore @@ -737,14 +728,50 @@ def _result_construct(self) -> None: class AtmHandler(MessageHandlerBase): _request: AtmRequest + def _result_construct(self) -> None: + assert self._user is not None + assert isinstance(self._user.nick_name, str) + assert isinstance(self._user.user_name, str) + + self._result = AtmResult( + nick_name=self._user.nick_name, user_name=self._user.user_name + ) + class UtmHandler(MessageHandlerBase): _request: UtmRequest + def _result_construct(self) -> None: + assert self._user is not None + assert isinstance(self._user.nick_name, str) + assert isinstance(self._user.user_name, str) + + self._result = UtmResult( + nick_name=self._user.nick_name, user_name=self._user.user_name + ) + class NoticeHandler(MessageHandlerBase): _request: NoticeRequest + def _result_construct(self) -> None: + assert self._user is not None + assert isinstance(self._user.nick_name, str) + assert isinstance(self._user.user_name, str) + + self._result = NoticeResult( + nick_name=self._user.nick_name, user_name=self._user.user_name + ) + class PrivateHandler(MessageHandlerBase): _request: PrivateRequest + + def _result_construct(self) -> None: + assert self._user is not None + assert isinstance(self._user.nick_name, str) + assert isinstance(self._user.user_name, str) + + self._result = PrivateResult( + nick_name=self._user.nick_name, user_name=self._user.user_name + ) diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 0e32550dc..1496da62f 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -48,14 +48,11 @@ ) from backends.urls import CHAT from fastapi import APIRouter, FastAPI, WebSocket, WebSocketDisconnect -from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage -router = APIRouter() -def check_request(request: dict) -> BrockerMessage: - msg = BrockerMessage(**request) - return msg +router = APIRouter() +client_pool = {} @router.websocket(f"{CHAT}/ws") @@ -65,7 +62,9 @@ async def websocket_endpoint(ws: WebSocket): MANAGER.connect(ws) try: while True: - _ = await ws.receive_json() + data = await ws.receive_json() + msg = MANAGER.process_message(data) + await MANAGER.broadcast(msg, ws) except WebSocketDisconnect: if ws.client is not None: MANAGER.disconnect(ws) diff --git a/src/frontends/gamespy/library/abstractions/client.py b/src/frontends/gamespy/library/abstractions/client.py index c29eab14f..76f9668bc 100644 --- a/src/frontends/gamespy/library/abstractions/client.py +++ b/src/frontends/gamespy/library/abstractions/client.py @@ -82,9 +82,10 @@ def decrypt_message(self, buffer: bytes) -> bytes: else: return buffer - def send(self, response: "ResponseBase") -> None: + def send(self, response: "ResponseBase|None") -> None: from frontends.gamespy.library.abstractions.contracts import ResponseBase - + if response is None: + return assert issubclass(type(response), ResponseBase) response.build() sending_buffer = response.sending_buffer diff --git a/src/frontends/gamespy/library/abstractions/connections.py b/src/frontends/gamespy/library/abstractions/connections.py index 756bf4eba..56bee8e1d 100644 --- a/src/frontends/gamespy/library/abstractions/connections.py +++ b/src/frontends/gamespy/library/abstractions/connections.py @@ -41,6 +41,7 @@ def on_received(self, data: bytes) -> None: @abc.abstractmethod def send(self, data: bytes) -> None: + assert isinstance(data, bytes) if not self._is_started: raise Exception("Server is not running.") assert isinstance(data, bytes) diff --git a/src/frontends/gamespy/library/network/brockers.py b/src/frontends/gamespy/library/network/brockers.py index 549b8ff6f..469a8ee9c 100644 --- a/src/frontends/gamespy/library/network/brockers.py +++ b/src/frontends/gamespy/library/network/brockers.py @@ -51,10 +51,12 @@ def subscribe(self): self._publisher = self._subscriber = connect(self.url) th = threading.Thread(target=self._listen) th.start() + @property - def ip_port(self)->str: + def ip_port(self) -> str: name = self._subscriber.socket.getsockname() return f"{name[0]}:{name[1]}" + def _listen(self): try: while True: @@ -73,11 +75,21 @@ def publish_message(self, message: BrockerMessage): self._publisher.send(message.model_dump_json()) +COUNT = 0 + + +def call_back(str): + global COUNT + COUNT +=1 + print(COUNT) + # print(f"{datetime.now()}:{str}") + + if __name__ == "__main__": ws = WebSocketBrocker( name="test_channel", url="ws://127.0.0.1:8080/GameSpy/Chat/ws", - call_back_func=print, + call_back_func=call_back, ) ws.subscribe() msg = BrockerMessage( @@ -87,4 +99,5 @@ def publish_message(self, message: BrockerMessage): sender_port=80, message="hello", ) - ws.publish_message(msg) + while True: + ws.publish_message(msg) diff --git a/src/frontends/gamespy/protocols/chat/abstractions/handler.py b/src/frontends/gamespy/protocols/chat/abstractions/handler.py index 7860042ed..099b08563 100644 --- a/src/frontends/gamespy/protocols/chat/abstractions/handler.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/handler.py @@ -25,11 +25,12 @@ class CmdHandlerBase(lib.CmdHandlerBase): def __init__(self, client: ClientBase, request: RequestBase): super().__init__(client, request) assert issubclass(type(request), RequestBase) - + def _request_check(self) -> None: super()._request_check() assert self._client.brocker self._request.websocket_address = self._client.brocker.ip_port + def _handle_exception(self, ex: Exception) -> None: t_ex = type(ex) if t_ex is IRCException: @@ -47,11 +48,9 @@ class PostLoginHandlerBase(CmdHandlerBase): class ChannelRequestBase(RequestBase): channel_name: str - broad_cast_raw: str | None def __init__(self, raw_request: str) -> None: super().__init__(raw_request) - self.broad_cast_raw = None def parse(self) -> None: super().parse() @@ -90,6 +89,7 @@ class ChannelHandlerBase(PostLoginHandlerBase): _response: ResponseBase _result: ResultBase _channel: ChatChannelCaches + _is_broadcast: bool def __init__(self, client: ClientBase, request: RequestBase): super().__init__(client, request) @@ -97,13 +97,17 @@ def __init__(self, client: ClientBase, request: RequestBase): we handle message broadcasting in backend api frontends -> backends -> backends_api -> websocket broadcast. -> frontends.client.brocker.receive """ + self._is_broadcast = False def _response_send(self) -> None: - super()._response_send() - # send message to backend websocket - self.broadcast_message() + if self._is_broadcast: + # send message to backend websocket + self.broadcast() + else: + super()._response_send() - def broadcast_message(self): + def broadcast(self): + self._response.build() assert self._request.channel_name msg = BrockerMessage( server_id=self._client.server_config.server_id, @@ -114,6 +118,7 @@ def broadcast_message(self): ) assert self._client.brocker self._client.brocker.publish_message(msg) + self._client.log_network_broadcast(msg) # region Message @@ -121,27 +126,28 @@ class MessageRequestBase(ChannelRequestBase): type: MessageType nick_name: str message: str - broad_cast_raw: str - + target_name:str def parse(self): super().parse() if self.channel_name is None: raise NoSuchNickException("the channel name is missing from the request") if "#" in self.channel_name: self.type = MessageType.CHANNEL_MESSAGE + self.target_name = self.channel_name else: if self._cmd_params is None or len(self._cmd_params) < 1: raise ChatException("cmd params is invalid") self.type = MessageType.USER_MESSAGE self.nick_name = self._cmd_params[0] + self.target_name = self.nick_name if self._long_param is None: raise ChatException("long param is invalid") self.message = self._long_param class MessageResultBase(ResultBase): - irc_prefix: str - target_name: str + nick_name: str + user_name: str class MessageHandlerBase(ChannelHandlerBase): @@ -151,13 +157,4 @@ class MessageHandlerBase(ChannelHandlerBase): def __init__(self, client: ClientBase, request: MessageRequestBase): assert isinstance(request, MessageRequestBase) super().__init__(client, request) - - def _request_check(self) -> None: - super()._request_check() - self._request.broad_cast_raw = ( - f"{self._client.info.irc_prefix} {self._request.raw_request}" - ) - - def _update_channel_cache(self): - """we do nothing here, channel message do not need to update channel cache""" - pass + self._is_broadcast = True diff --git a/src/frontends/gamespy/protocols/chat/applications/client.py b/src/frontends/gamespy/protocols/chat/applications/client.py index 76adfc612..7163c99a6 100644 --- a/src/frontends/gamespy/protocols/chat/applications/client.py +++ b/src/frontends/gamespy/protocols/chat/applications/client.py @@ -1,4 +1,3 @@ -import json from frontends.gamespy.library.abstractions.client import ClientBase from frontends.gamespy.library.abstractions.switcher import SwitcherBase @@ -75,7 +74,11 @@ def stop_brocker(self): def _process_brocker_message(self, message: str): # responsible for receive message and send out # TODO: check whether exception here will cause brocker stop - assert isinstance(message, str) - j = json.loads(message) - r = BrockerMessage(**j) - self.connection.send(r.message.encode()) + try: + assert isinstance(message, str) + r = BrockerMessage.model_validate_strings(message) + self.log_network_broadcast(r.model_dump_json()) + self.connection.send(r.message.encode()) + except Exception as e: + # todo change to log and handle exception here + print(e) diff --git a/src/frontends/gamespy/protocols/chat/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py index 3c6bd4d70..24198ef45 100644 --- a/src/frontends/gamespy/protocols/chat/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -1,8 +1,9 @@ from frontends.gamespy.library.encryption.gs_encryption import ChatCrypt from frontends.gamespy.protocols.chat.contracts.results import ( - ATMResult, + AtmResult, NoticeResult, - UTMResult, + PrivateResult, + UtmResult, GetCKeyResult, GetChannelKeyResult, JoinResult, @@ -23,12 +24,15 @@ WhoResult, ) from frontends.gamespy.protocols.chat.contracts.responses import ( + AtmResponse, GetCKeyResponse, JoinResponse, KickResponse, ModeResponse, NamesResponse, + NoticeResponse, PartResponse, + PrivateResponse, SetCKeyResponse, SetChannelKeyResponse, TopicResponse, @@ -39,15 +43,16 @@ LoginResponse, NickResponse, PingResponse, + UtmResponse, UserIPResponse, WhoIsResponse, WhoResponse, ) from frontends.gamespy.protocols.chat.contracts.requests import ( - ATMRequest, + AtmRequest, NoticeRequest, PrivateRequest, - UTMRequest, + UtmRequest, GetCKeyRequest, GetChannelKeyRequest, JoinRequest, @@ -110,7 +115,7 @@ def __init__(self, client: ClientBase, request: RequestBase): def _data_operate(self) -> None: super()._data_operate() self._client.info.gamename = self._request.gamename - + def _response_construct(self) -> None: self._response = CryptResponse() @@ -325,12 +330,14 @@ def __init__(self, client: ClientBase, request: JoinRequest): assert isinstance(request, JoinRequest) super().__init__(client, request) self._result_cls = JoinResult + self._is_broadcast = True def _response_construct(self): self._response = JoinResponse(self._request, self._result) def _response_send(self) -> None: super()._response_send() + # gamespy sdk join require ciNameReplyHandler names_raw = NamesRequest.build(self._request.channel_name) names_req = NamesRequest(names_raw) names_handler = NamesHandler(self._client, names_req) @@ -366,8 +373,13 @@ def __init__(self, client: ClientBase, request: ModeRequest): def _request_check(self) -> None: super()._request_check() - if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: - self._is_fetching = False + # if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: + # self._is_fetching = False + if self._request.request_type == [ + ModeRequestType.SET_CHANNEL_MODES, + ModeRequestType.SET_CHANNEL_USER_MODES, + ]: + self._is_broadcast = True def _response_construct(self): if self._request.request_type == ModeRequestType.GET_CHANNEL_MODES: @@ -442,23 +454,29 @@ def _response_construct(self) -> None: class ATMHandler(MessageHandlerBase): - _request: ATMRequest - _result: ATMResult + _request: AtmRequest + _result: AtmResult - def __init__(self, client: ClientBase, request: ATMRequest): + def __init__(self, client: ClientBase, request: AtmRequest): super().__init__(client, request) - assert isinstance(request, ATMRequest) - self._is_fetching = False + assert isinstance(request, AtmRequest) + self._result_cls = AtmResult + + def _response_construct(self) -> None: + self._response = AtmResponse(self._request, self._result) class UTMHandler(MessageHandlerBase): - _request: UTMRequest - _result: UTMResult + _request: UtmRequest + _result: UtmResult - def __init__(self, client: ClientBase, request: UTMRequest): - assert isinstance(request, UTMRequest) + def __init__(self, client: ClientBase, request: UtmRequest): + assert isinstance(request, UtmRequest) super().__init__(client, request) - self._is_fetching = False + self._result_cls = UtmResult + + def _response_construct(self) -> None: + self._response = UtmResponse(self._request, self._result) class NoticeHandler(MessageHandlerBase): @@ -468,13 +486,20 @@ class NoticeHandler(MessageHandlerBase): def __init__(self, client: ClientBase, request: NoticeRequest): assert isinstance(request, NoticeRequest) super().__init__(client, request) - self._is_fetching = False + self._result_cls = NoticeResult + + def _response_construct(self) -> None: + self._response = NoticeResponse(self._request, self._result) class PrivateHandler(MessageHandlerBase): _request: PrivateRequest + _result: PrivateResult def __init__(self, client: ClientBase, request: PrivateRequest): assert isinstance(request, PrivateRequest) super().__init__(client, request) - self._is_fetching = False + self._result_cls = PrivateResult + + def _response_construct(self) -> None: + self._response = PrivateResponse(self._request, self._result) diff --git a/src/frontends/gamespy/protocols/chat/applications/switcher.py b/src/frontends/gamespy/protocols/chat/applications/switcher.py index 2aa676a3d..6749418ef 100644 --- a/src/frontends/gamespy/protocols/chat/applications/switcher.py +++ b/src/frontends/gamespy/protocols/chat/applications/switcher.py @@ -27,10 +27,10 @@ UserRequest, WhoIsRequest, WhoRequest, - ATMRequest, + AtmRequest, NoticeRequest, PrivateRequest, - UTMRequest, + UtmRequest, ) from frontends.gamespy.protocols.chat.applications.handlers import ( GetCKeyHandler, @@ -138,13 +138,13 @@ def _create_cmd_handlers(self, name: RequestType, raw_request: str) -> CmdHandle # Message commands case RequestType.ATM: - return ATMHandler(self._client, ATMRequest(raw_request)) + return ATMHandler(self._client, AtmRequest(raw_request)) case RequestType.NOTICE: return NoticeHandler(self._client, NoticeRequest(raw_request)) case RequestType.PRIVMSG: return PrivateHandler(self._client, PrivateRequest(raw_request)) case RequestType.UTM: - return UTMHandler(self._client, UTMRequest(raw_request)) + return UTMHandler(self._client, UtmRequest(raw_request)) case _: return None # endregion diff --git a/src/frontends/gamespy/protocols/chat/contracts/requests.py b/src/frontends/gamespy/protocols/chat/contracts/requests.py index 2154f8e65..ef60453cc 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/requests.py +++ b/src/frontends/gamespy/protocols/chat/contracts/requests.py @@ -589,7 +589,7 @@ def parse(self) -> None: # region Message -class ATMRequest(MessageRequestBase): +class AtmRequest(MessageRequestBase): pass @@ -601,5 +601,5 @@ class PrivateRequest(MessageRequestBase): pass -class UTMRequest(MessageRequestBase): +class UtmRequest(MessageRequestBase): pass diff --git a/src/frontends/gamespy/protocols/chat/contracts/responses.py b/src/frontends/gamespy/protocols/chat/contracts/responses.py index b9ac7f9b8..05da3a2d5 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/responses.py +++ b/src/frontends/gamespy/protocols/chat/contracts/responses.py @@ -14,10 +14,10 @@ PartResult, SetChannelKeyResult, TopicResult, - ATMResult, + AtmResult, NoticeResult, PrivateResult, - UTMResult, + UtmResult, GetKeyResult, ListResult, LoginResult, @@ -38,10 +38,10 @@ SetCKeyRequest, SetChannelKeyRequest, TopicRequest, - ATMRequest, + AtmRequest, NoticeRequest, PrivateRequest, - UTMRequest, + UtmRequest, GetKeyRequest, WhoRequest, ) @@ -249,7 +249,7 @@ def build(self) -> None: joiner_irc_prefix = ResponseBase.build_irc_user_prefix( self._result.joiner_nick_name, self._result.joiner_user_name ) - self.sending_buffer = f"{joiner_irc_prefix} {ResponseCode.JOIN.value} {self._request.channel_name}\r\n" + self.sending_buffer = f":{joiner_irc_prefix} {ResponseCode.JOIN.value} {self._request.channel_name}\r\n" class KickResponse(ResponseBase): @@ -388,17 +388,20 @@ def build(self) -> None: # region Message -class ATMResponse(ResponseBase): - _request: ATMRequest - _result: ATMResult +class AtmResponse(ResponseBase): + _request: AtmRequest + _result: AtmResult - def __init__(self, request: ATMRequest, result: ATMResult) -> None: - assert isinstance(request, ATMRequest) - assert isinstance(result, ATMResult) + def __init__(self, request: AtmRequest, result: AtmResult) -> None: + assert isinstance(request, AtmRequest) + assert isinstance(result, AtmResult) super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.irc_prefix} {ResponseCode.ATM.value} {self._result.target_name} :{self._request.message}\r\n" + irc_prefix = ResponseBase.build_irc_user_prefix( + self._result.nick_name, self._result.user_name + ) + self.sending_buffer = f":{irc_prefix} {ResponseCode.ATM.value} {self._request.target_name} :{self._request.message}\r\n" class NoticeResponse(ResponseBase): @@ -412,7 +415,11 @@ def __init__(self, request: NoticeRequest, result: NoticeResult) -> None: super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.irc_prefix} {ResponseCode.NOTICE.value} {self._result.target_name} {self._request.message}\r\n" + irc_prefix = ResponseBase.build_irc_user_prefix( + self._result.nick_name, self._result.user_name + ) + + self.sending_buffer = f":{irc_prefix} {ResponseCode.NOTICE.value} {self._request.target_name} {self._request.message}\r\n" class PrivateResponse(ResponseBase): @@ -420,24 +427,29 @@ class PrivateResponse(ResponseBase): _result: PrivateResult def __init__(self, request: PrivateRequest, result: PrivateResult) -> None: - assert isinstance(result, PrivateRequest) - assert isinstance(request, PrivateResult) + assert isinstance(request, PrivateRequest) + assert isinstance(result, PrivateResult) super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.irc_prefix} {ResponseCode.PRIVMSG.value} {self._result.target_name} :{self._request.message}\r\n" + irc_prefix = ResponseBase.build_irc_user_prefix( + self._result.nick_name, self._result.user_name + ) + self.sending_buffer = f":{irc_prefix} {ResponseCode.PRIVMSG.value} {self._request.target_name} :{self._request.message}\r\n" -class UTMResponse(ResponseBase): - _request: UTMRequest - _result: UTMResult +class UtmResponse(ResponseBase): + _request: UtmRequest + _result: UtmResult - def __init__(self, request: UTMRequest, result: UTMResult) -> None: - assert isinstance(request, UTMRequest) - assert isinstance(result, UTMResult) + def __init__(self, request: UtmRequest, result: UtmResult) -> None: + assert isinstance(request, UtmRequest) + assert isinstance(result, UtmResult) super().__init__(request, result) def build(self) -> None: - self.sending_buffer = f":{self._result.irc_prefix} {ResponseCode.UTM.value} { - self._result.target_name - } :{self._request.message}" + irc_prefix = ResponseBase.build_irc_user_prefix( + self._result.nick_name, self._result.user_name + ) + + self.sending_buffer = f":{irc_prefix} {ResponseCode.UTM.value} {self._request.target_name} :{self._request.message}\r\n" diff --git a/src/frontends/gamespy/protocols/chat/contracts/results.py b/src/frontends/gamespy/protocols/chat/contracts/results.py index 91991d7ef..0fa7ff053 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/results.py +++ b/src/frontends/gamespy/protocols/chat/contracts/results.py @@ -157,7 +157,7 @@ class SetChannelKeyResult(ResultBase): # region Message -class ATMResult(MessageResultBase): +class AtmResult(MessageResultBase): pass @@ -166,8 +166,7 @@ class NoticeResult(MessageResultBase): class PrivateResult(MessageResultBase): - is_broadcast_message: bool = False - + pass -class UTMResult(MessageResultBase): +class UtmResult(MessageResultBase): pass diff --git a/src/frontends/tests/gamespy/chat/mock_objects.py b/src/frontends/tests/gamespy/chat/mock_objects.py index 1bda428f8..37b465fa4 100644 --- a/src/frontends/tests/gamespy/chat/mock_objects.py +++ b/src/frontends/tests/gamespy/chat/mock_objects.py @@ -34,7 +34,7 @@ PartResult, SetChannelKeyResult, TopicResult, - UTMResult, + UtmResult, WhoResult, ) from frontends.tests.gamespy.library.mock_objects import ( @@ -161,7 +161,7 @@ def create_client() -> Client: create_mock_url( config, UTMHandler, - UTMResult(irc_prefix="unispy!unispy@unispy", target_name="spyguy").model_dump(), + UtmResult(nick_name="unispy", user_name="unispy").model_dump(), ) if TYPE_CHECKING: diff --git a/src/frontends/tests/gamespy/chat/request_tests.py b/src/frontends/tests/gamespy/chat/request_tests.py index 4d0c94b67..4f1351228 100644 --- a/src/frontends/tests/gamespy/chat/request_tests.py +++ b/src/frontends/tests/gamespy/chat/request_tests.py @@ -1,7 +1,7 @@ import unittest from frontends.gamespy.protocols.chat.aggregates.enums import GetKeyRequestType, MessageType, ModeName, ModeOperation -from frontends.gamespy.protocols.chat.contracts.requests import ATMRequest, GetCKeyRequest, GetChannelKeyRequest, JoinRequest, KickRequest, ModeRequest, NoticeRequest, PartRequest, PrivateRequest, SetCKeyRequest, SetChannelKeyRequest, TopicRequest, UTMRequest +from frontends.gamespy.protocols.chat.contracts.requests import AtmRequest, GetCKeyRequest, GetChannelKeyRequest, JoinRequest, KickRequest, ModeRequest, NoticeRequest, PartRequest, PrivateRequest, SetCKeyRequest, SetChannelKeyRequest, TopicRequest, UtmRequest # region General CD_KEY = "CDKEY XXXX-XXXX-XXXX-XXXX\r\n" @@ -160,7 +160,7 @@ def test_topic_set_channel_topic(self): class MessageRequestTests(unittest.TestCase): def test_atm(self): - request = ATMRequest(ABOVE_THE_TABLE_MSG) + request = AtmRequest(ABOVE_THE_TABLE_MSG) request.parse() self.assertEqual(MessageType.CHANNEL_MESSAGE, request.type) self.assertEqual(False, hasattr(request, "nick_name")) @@ -181,7 +181,7 @@ def test_private(self): self.assertEqual("#GSP!room!test", request.channel_name) def test_utm(self): - request = UTMRequest(UNDER_THE_TABLE_MSG) + request = UtmRequest(UNDER_THE_TABLE_MSG) request.parse() self.assertEqual(MessageType.CHANNEL_MESSAGE, request.type) self.assertEqual(False, hasattr(request, "nick_name")) From f762a14b9b18a7ab4360ace6123dec482a334dac Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 20 Sep 2025 00:59:37 +0000 Subject: [PATCH 202/231] Fix: docker related configs --- Dockerfile | 9 +- common/config.json | 2 +- docker-compose-unispy-server.yml | 201 +++++++++++++++--- .../library/abstractions/server_launcher.py | 11 +- src/requirements.txt | 1 + 5 files changed, 188 insertions(+), 36 deletions(-) diff --git a/Dockerfile b/Dockerfile index 56d45abaa..2e6031987 100644 --- a/Dockerfile +++ b/Dockerfile @@ -5,13 +5,10 @@ FROM python:3.12-slim WORKDIR /unispy-server # Copy the requirements file into the container -COPY requirements.txt . +COPY src/requirements.txt . # Install the dependencies RUN pip install --no-cache-dir -r requirements.txt -# Copy the rest of the application code into the container -COPY . . - -# Specify the command to run the application -CMD ["python", "app.py"] \ No newline at end of file +RUN apt update +RUN apt install -y curl \ No newline at end of file diff --git a/common/config.json b/common/config.json index 28006feff..02bb5da2d 100644 --- a/common/config.json +++ b/common/config.json @@ -1,6 +1,6 @@ { "backend": { - "url": "http://localhost:8080", + "url": "http://unispy_backends:8080", "token_secret_key": "I love UniSpy", "token_algorithm": "HS256", "token_expire_time": 30 diff --git a/docker-compose-unispy-server.yml b/docker-compose-unispy-server.yml index fff680e2a..e6dc686ef 100644 --- a/docker-compose-unispy-server.yml +++ b/docker-compose-unispy-server.yml @@ -1,58 +1,205 @@ -version: '3.8' - - services: backends: - image: unispy-python - ports: - - "8000:8000" - command: python backends/home.py - restart: always - pcm: + container_name: unispy_backends image: unispy-python restart: always + working_dir: /unispy-server/src + environment: + - PYTHONPATH=/unispy-server/src:${PYTHONPATH:-} + - UNISPY_CONFIG=/unispy-server/common/config.json + volumes: + - ./:/unispy-server/ + - ./log:/root/Downloads/unispy-server/log/ ports: - - "29900:29900" - command: python servers/presence_connection_manager/application/server_launcher.py - psp: + - "8080:8080" + command: uvicorn --host 0.0.0.0 --port 8080 backends.routers.home:app + healthcheck: + test: curl --silent --show-error http://localhost:8080 >/dev/null 2>&1 || exit 1 + interval: 30s + timeout: 3s + retries: 3 + start_period: 2s + networks: + - unispy + chat: image: unispy-python + container_name: unispy-chat restart: always + environment: + - PYTHONPATH=/unispy-server/src:${PYTHONPATH:-} + - UNISPY_CONFIG=/unispy-server/common/config.json + - PYTHONUNBUFFERED=1 + volumes: + - ./:/unispy-server/ ports: - - "29901:29901" - command: python servers/presence_search_player/application/server_launcher.py - natneg: + - "6667:6667" + command: python -u src/frontends/gamespy/protocols/chat/applications/server_launcher.py + depends_on: + backends: + condition: service_healthy + networks: + - unispy + + gs: image: unispy-python + container_name: unispy-gs restart: always + environment: + - PYTHONPATH=/unispy-server/src:${PYTHONPATH:-} + - UNISPY_CONFIG=/unispy-server/common/config.json + - PYTHONUNBUFFERED=1 + volumes: + - ./:/unispy-server/ ports: - - "27901:27901" - command: python servers/natneg/application/server_launcher.py - chat: + - "29920:29920" + command: python -u src/frontends/gamespy/protocols/game_status/applications/server_launcher.py + depends_on: + backends: + condition: service_healthy + networks: + - unispy + + + + pcm: image: unispy-python + container_name: unispy-pcm restart: always + environment: + - PYTHONPATH=/unispy-server/src:${PYTHONPATH:-} + - UNISPY_CONFIG=/unispy-server/common/config.json + - PYTHONUNBUFFERED=1 + volumes: + - ./:/unispy-server/ ports: - - "6667:6667" - command: python servers/chat/application/server_launcher.py - gstats: + - "29900:29900" + command: python -u src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py + depends_on: + backends: + condition: service_healthy + networks: + - unispy + psp: image: unispy-python + container_name: unispy-psp restart: always + environment: + - PYTHONPATH=/unispy-server/src:${PYTHONPATH:-} + - UNISPY_CONFIG=/unispy-server/common/config.json + - PYTHONUNBUFFERED=1 + volumes: + - ./:/unispy-server/ ports: - - "29920:29920" - command: python servers/game_status/application/server_launcher.py + - "29901:29901" + command: python -u src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py + depends_on: + backends: + condition: service_healthy + networks: + - unispy + qr: image: unispy-python + container_name: unispy-qr restart: always + environment: + - PYTHONPATH=/unispy-server/src:${PYTHONPATH:-} + - UNISPY_CONFIG=/unispy-server/common/config.json + - PYTHONUNBUFFERED=1 + volumes: + - ./:/unispy-server/ ports: - "27900:27900" - command: python servers/query_report/application/server_launcher.py - sbv2: + command: python -u src/frontends/gamespy/protocols/query_report/applications/server_launcher.py + depends_on: + backends: + condition: service_healthy + networks: + - unispy + + sb: image: unispy-python + container_name: unispy-sb restart: always + environment: + - PYTHONPATH=/unispy-server/src:${PYTHONPATH:-} + - UNISPY_CONFIG=/unispy-server/common/config.json + - PYTHONUNBUFFERED=1 + volumes: + - ./:/unispy-server/ ports: + - "28910:28910" - "28900:28900" - command: python servers/server_browser/application/server_launcher.py + command: python -u src/frontends/gamespy/protocols/query_report/applications/server_launcher.py + depends_on: + backends: + condition: service_healthy + networks: + - unispy + + + natneg: + image: unispy-python + container_name: unispy-natneg + restart: always + environment: + - PYTHONPATH=/unispy-server/src:${PYTHONPATH:-} + - UNISPY_CONFIG=/unispy-server/common/config.json + - PYTHONUNBUFFERED=1 + volumes: + - ./:/unispy-server/ + ports: + - "27901:27901" + command: python -u src/frontends/gamespy/protocols/natneg/applications/server_launcher.py + depends_on: + backends: + condition: service_healthy + networks: + - unispy + web: image: unispy-python + container_name: unispy-web restart: always + environment: + - PYTHONPATH=/unispy-server/src:${PYTHONPATH:-} + - UNISPY_CONFIG=/unispy-server/common/config.json + - PYTHONUNBUFFERED=1 + volumes: + - ./:/unispy-server/ ports: - "80:80" - command: python servers/web_services/application/server_launcher.py \ No newline at end of file + command: python -u src/frontends/gamespy/protocols/web_services/applications/server_launcher.py + depends_on: + backends: + condition: service_healthy + networks: + - unispy + + # gtr: + # image: unispy-python + # container_name: unispy-gtr + # restart: always + # environment: + # - PYTHONPATH=/unispy-server/src:${PYTHONPATH:-} + # - UNISPY_CONFIG=/unispy-server/common/config.json + # - PYTHONUNBUFFERED=1 + # volumes: + # - ./:/unispy-server/ + # ports: + # - "10086:10086" + # command: python -u src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py + # depends_on: + # backends: + # condition: service_healthy + # networks: + # - unispy + + + + +networks: + unispy: + external: true + name: unispy + driver: bridge \ No newline at end of file diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index 1cdf15d58..382b119e0 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -52,7 +52,11 @@ def __show_unispy_logo(self): # display version info print(f"version {VERSION}") table = PrettyTable() - table.field_names = ["Server Name", "Listening Address", "Listening Port"] + table.field_names = [ + "Server Name", + "Listening Address", + "Listening Port", + ] assert self.config is not None table.add_row( [ @@ -106,4 +110,7 @@ def _create_logger(self): self.logger = LogManager.create(short_name) def _keep_running(self): - _ = input("Press any key to Quit\n") + # _ = input("Press any key to Quit\n") + print("Press ctr+c to Quit\n") + while True: + pass diff --git a/src/requirements.txt b/src/requirements.txt index b5b1e8fb2..28d2ef112 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -9,3 +9,4 @@ xmltodict == 0.14.2 responses == 0.25.3 redis == 5.2.0 websockets == 15.0.1 +schedule \ No newline at end of file From df23272fbf927e2a61f66d15629d46ff665974f5 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sun, 21 Sep 2025 01:48:43 +0000 Subject: [PATCH 203/231] Update: simplify server creation --- .../gamespy/game_traffic_relay/handlers.py | 10 +-- .../gamespy/game_traffic_relay/requests.py | 2 +- .../routers/gamespy/game_traffic_relay.py | 10 +-- .../library/abstractions/server_launcher.py | 74 +++++++++++----- .../protocols/chat/applications/handlers.py | 1 - .../chat/applications/server_launcher.py | 14 ++-- .../applications/server_launcher.py | 14 ++-- .../applications/server_launcher.py | 84 +++++-------------- .../game_traffic_relay/contracts/general.py | 2 +- .../natneg/applications/server_launcher.py | 14 ++-- .../applications/server_launcher.py | 19 ++--- .../applications/server_launcher.py | 19 ++--- .../applications/server_launcher.py | 14 ++-- .../applications/server_launcher.py | 20 ++--- .../applications/server_launcher.py | 13 +-- src/redis_test.py | 14 ++++ 16 files changed, 145 insertions(+), 179 deletions(-) diff --git a/src/backends/protocols/gamespy/game_traffic_relay/handlers.py b/src/backends/protocols/gamespy/game_traffic_relay/handlers.py index 38d46367b..a39ae0ea7 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/handlers.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/handlers.py @@ -4,17 +4,17 @@ from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import RelayServerCaches from backends.protocols.gamespy.game_traffic_relay.requests import ( - UpdateGTRServiceRequest, + GTRHeartBeat, ) import backends.protocols.gamespy.game_traffic_relay.data as data -class UpdateGTRServiceHandler(HandlerBase): - _request: UpdateGTRServiceRequest +class GTRHeartBeatHandler(HandlerBase): + _request: GTRHeartBeat - def __init__(self, request: UpdateGTRServiceRequest) -> None: - assert isinstance(request, UpdateGTRServiceRequest) + def __init__(self, request: GTRHeartBeat) -> None: + assert isinstance(request, GTRHeartBeat) self._request = request self.logger = logging.getLogger("backend") self._result = None diff --git a/src/backends/protocols/gamespy/game_traffic_relay/requests.py b/src/backends/protocols/gamespy/game_traffic_relay/requests.py index bae52e113..473015492 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/requests.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/requests.py @@ -5,7 +5,7 @@ There are 2 UpdateGTRServiceRequest class The other one is in frontends/gamespy/protocols/game_traffic_relay/contracts/general.py """ -class UpdateGTRServiceRequest(BaseModel): +class GTRHeartBeat(BaseModel): server_id: UUID public_ip_address: str public_port: int diff --git a/src/backends/routers/gamespy/game_traffic_relay.py b/src/backends/routers/gamespy/game_traffic_relay.py index e879a24a1..f1203db80 100644 --- a/src/backends/routers/gamespy/game_traffic_relay.py +++ b/src/backends/routers/gamespy/game_traffic_relay.py @@ -1,10 +1,10 @@ from fastapi import APIRouter from backends.protocols.gamespy.game_traffic_relay.handlers import ( - UpdateGTRServiceHandler, + GTRHeartBeatHandler, ) from backends.protocols.gamespy.game_traffic_relay.requests import ( - UpdateGTRServiceRequest, + GTRHeartBeat, ) from backends.urls import GAME_TRAFFIC_RELAY @@ -12,10 +12,10 @@ @router.post(f"{GAME_TRAFFIC_RELAY}/heartbeat") -def heartbeat(request: UpdateGTRServiceRequest): - handler = UpdateGTRServiceHandler(request) +def heartbeat(request: GTRHeartBeat): + handler = GTRHeartBeatHandler(request) handler.handle() - return + return None if __name__ == "__main__": diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index 382b119e0..d017a164f 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -8,6 +8,8 @@ import pyfiglet import requests from prettytable import PrettyTable +from frontends.gamespy.library.abstractions.client import ClientBase +from typing import final VERSION = 0.45 _SERVER_FULL_SHORT_NAME_MAPPING = MappingProxyType( @@ -22,27 +24,40 @@ "GameStatus": "GS", "Chat": "Chat", "WebServices": "Web", - "GameTrafficReplay": "GTR", + "GameTrafficRelay": "GTR", } ) class ServerLauncherBase: - config: ServerConfig | None + config: ServerConfig logger: LogWriter | None server: NetworkServerBase | None - schedular: Schedular + _schedular: Schedular + _server_cls: type[NetworkServerBase] + _client_cls: type[ClientBase] - def __init__(self): + def __init__( + self, + config_name: str, + client_cls: type[ClientBase], + server_cls: type[NetworkServerBase], + ): + assert issubclass(client_cls, ClientBase) + assert issubclass(server_cls, NetworkServerBase) + assert config_name in CONFIG.servers + self.config = CONFIG.servers[config_name] self.server = None self.logger = None - self.config = None + self._server_cls = server_cls + self._client_cls = client_cls + self._create_logger() def start(self): self._connect_to_backend() - self._create_logger() self.__show_unispy_logo() self._launch_server() + self._launch_heartbeat_schedular() print("Server successfully launched.") self._keep_running() @@ -67,24 +82,26 @@ def __show_unispy_logo(self): ) print(table) + @final def _launch_server(self) -> None: - if self.server is None: - raise UniSpyException("Create network server in child class") - self._heartbeat_to_backend() + """ + assign data in child class so the related instance can be created here + """ + assert self.logger is not None + assert self._server_cls is not None + assert self._client_cls is not None + self.server = self._server_cls(self.config, self._client_cls, self.logger) self.server.start() - def _connect_to_backend(self): + @final + def _heartbeat_to_backend(self, url: str, json_str: str): """ - check backend availability + send heartbeat to backends """ - if CONFIG.unittest.is_collect_request: - return + assert isinstance(json_str, str) try: # post our server config to backends to register - assert self.config is not None - resp = requests.post( - url=CONFIG.backend.url + "/", data=self.config.model_dump_json() - ) + resp = requests.post(url=url, data=json_str) if resp.status_code == 200: data = resp.json() if data["status"] != "online": @@ -96,21 +113,34 @@ def _connect_to_backend(self): f"backend server: {CONFIG.backend.url} not available." ) - def _heartbeat_to_backend(self): + def _connect_to_backend(self): + """ + check backend availability + """ + assert self.config is not None + if CONFIG.unittest.is_collect_request: + return + self._heartbeat_to_backend(CONFIG.backend.url, self.config.model_dump_json()) + + @final + def _launch_heartbeat_schedular(self): """ - send heartbeat info to backend to keep the infomation update + set the schedular to send heartbeat info to backend to keep the infomation update """ #! temperarily use connect to backend function - self.schedular = Schedular(self._connect_to_backend, 30) - self.schedular.start() + self._schedular = Schedular(self._connect_to_backend, 30) + self._schedular.start() + @final def _create_logger(self): assert self.config is not None short_name = _SERVER_FULL_SHORT_NAME_MAPPING[self.config.server_name] self.logger = LogManager.create(short_name) + @final def _keep_running(self): - # _ = input("Press any key to Quit\n") print("Press ctr+c to Quit\n") + from time import sleep while True: + sleep(1) pass diff --git a/src/frontends/gamespy/protocols/chat/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py index 24198ef45..20fc26ac6 100644 --- a/src/frontends/gamespy/protocols/chat/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -102,7 +102,6 @@ def __init__(self, client: ClientBase, request: CdkeyRequest): def _response_construct(self) -> None: self._response = CdKeyResponse() - class CryptHandler(CmdHandlerBase): _request: CryptRequest _result: CryptResult diff --git a/src/frontends/gamespy/protocols/chat/applications/server_launcher.py b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py index f84a507fc..3d373dba0 100644 --- a/src/frontends/gamespy/protocols/chat/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py @@ -1,21 +1,17 @@ from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase from frontends.gamespy.library.network.tcp_handler import TcpServer -from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.protocols.chat.applications.client import Client class ServerLauncher(ServerLauncherBase): - server: "TcpServer" def __init__(self) -> None: - super().__init__() - self.config = CONFIG.servers["Chat"] + super().__init__( + config_name="Chat", + client_cls=Client, + server_cls=TcpServer, + ) - def _launch_server(self): - assert self.config is not None - assert self.logger is not None - self.server = TcpServer(self.config, Client, self.logger) - super()._launch_server() if __name__ == "__main__": s = ServerLauncher() diff --git a/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py index 94543332b..36da1e0a0 100644 --- a/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py @@ -1,21 +1,17 @@ from frontends.gamespy.protocols.game_status.applications.client import Client from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase from frontends.gamespy.library.network.tcp_handler import TcpServer -from frontends.gamespy.library.configs import CONFIG class ServerLauncher(ServerLauncherBase): server: "TcpServer" def __init__(self) -> None: - super().__init__() - self.config = CONFIG.servers["GameStatus"] - - def _launch_server(self): - assert self.config is not None - assert self.logger is not None - self.server = TcpServer(self.config, Client, self.logger) - super()._launch_server() + super().__init__( + config_name="GameStatus", + client_cls=Client, + server_cls=TcpServer, + ) diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py index c5e8c1e42..a38eea152 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py @@ -1,89 +1,47 @@ -import requests -from frontends.gamespy.library.abstractions.brocker import BrockerBase from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase from frontends.gamespy.library.configs import CONFIG -from frontends.gamespy.library.exceptions.general import UniSpyException -from frontends.gamespy.library.network.brockers import WebSocketBrocker from frontends.gamespy.library.network.udp_handler import UdpServer from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client from frontends.gamespy.protocols.game_traffic_relay.contracts.general import ( - UpdateGTRServiceRequest, + GtrHeartbeat, ) class ServerLauncher(ServerLauncherBase): - _broker: BrockerBase - - # todo: implement the websocket brocker to receive the info from backends def __init__(self) -> None: - super().__init__() - self.config = CONFIG.servers["GameTrafficRelay"] - self._broker = WebSocketBrocker( - name=self.config.server_name, - url=f"{CONFIG.backend.url}/GameTrafficRelay/ws", - call_back_func=self._process_brocker_message, + super().__init__( + config_name="GameTrafficRelay", + client_cls=Client, + server_cls=UdpServer, ) - - def _process_brocker_message(self, message): - # todo handle message here - pass - - def _launch_server(self): - assert self.config is not None - assert self.logger is not None - self.server = UdpServer(self.config, Client, self.logger) - super()._launch_server() - def _gtr_heartbeat(self): assert self.config - req = UpdateGTRServiceRequest( + req = GtrHeartbeat( server_id=self.config.server_id, public_ip_address=self.config.public_address, public_port=self.config.listening_port, client_count=len(Client.client_pool), ) - try: - resp = requests.post( - url=CONFIG.backend.url + "/heartbeat", data=req.model_dump_json() - ) - if resp.status_code == 200: - data = resp.json() - if data["status"] != "online": - raise UniSpyException( - f"backend server: {CONFIG.backend.url} not available." - ) - except requests.ConnectionError: - raise UniSpyException( - f"backend server: {CONFIG.backend.url} not available." - ) - - def _heartbeat_to_backend(self): - self._gtr_heartbeat() - super()._heartbeat_to_backend() + req_str = req.model_dump_json() + self._heartbeat_to_backend( + f"{CONFIG.backend.url}/GameSpy/GameTrafficRelay/heartbeat", req_str + ) def _connect_to_backend(self): """ check backend availability """ - super()._connect_to_backend() + assert self.logger is not None if CONFIG.unittest.is_collect_request: + self.logger.debug( + "CONFIG.unittest.is_collect_request is enabled ignore send heartbeat to backend" + ) return - try: - # post our server config to backends to register - assert self.config is not None - data = self.config.model_dump_json() - import json + super()._connect_to_backend() + self._gtr_heartbeat() - data = json.loads(data) - data["clients"] = len(Client.client_pool) - resp = requests.post(url=CONFIG.backend.url + "/heartbeat", json=data) - if resp.status_code == 200: - data = resp.json() - if data["status"] != "online": - raise UniSpyException( - f"backend server: {CONFIG.backend.url} not available." - ) - except requests.ConnectionError: - raise UniSpyException( - f"backend server: {CONFIG.backend.url} not available." - ) + + +if __name__ == "__main__": + s = ServerLauncher() + s.start() \ No newline at end of file diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/contracts/general.py b/src/frontends/gamespy/protocols/game_traffic_relay/contracts/general.py index 0cecb0969..4cb6ef5b3 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/contracts/general.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/contracts/general.py @@ -17,7 +17,7 @@ class InitPacketInfo(BaseModel): private_port: int -class UpdateGTRServiceRequest(BaseModel): +class GtrHeartbeat(BaseModel): server_id: UUID4 public_ip_address: str public_port: int diff --git a/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py b/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py index 3d29676ea..f82ea8469 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py @@ -1,6 +1,5 @@ from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase from frontends.gamespy.library.network.udp_handler import UdpServer -from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.protocols.natneg.applications.client import Client @@ -8,14 +7,11 @@ class ServerLauncher(ServerLauncherBase): server: UdpServer def __init__(self) -> None: - super().__init__() - self.config = CONFIG.servers["NatNegotiation"] - - def _launch_server(self): - assert self.config is not None - assert self.logger is not None - self.server = UdpServer(self.config, Client, self.logger) - super()._launch_server() + super().__init__( + config_name="NatNegotiation", + client_cls=Client, + server_cls=UdpServer, + ) if __name__ == "__main__": diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py index a6b894262..4a71c2fc1 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py @@ -1,20 +1,17 @@ from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase from frontends.gamespy.library.network.tcp_handler import TcpServer -from frontends.gamespy.library.configs import CONFIG -from frontends.gamespy.protocols.presence_connection_manager.applications.client import Client +from frontends.gamespy.protocols.presence_connection_manager.applications.client import ( + Client, +) class ServerLauncher(ServerLauncherBase): - def __init__(self) -> None: - super().__init__() - self.config = CONFIG.servers["PresenceConnectionManager"] - - def _launch_server(self): - assert self.config is not None - assert self.logger is not None - self.server = TcpServer(self.config, Client, self.logger) - super()._launch_server() + super().__init__( + config_name="PresenceConnectionManager", + client_cls=Client, + server_cls=TcpServer, + ) if __name__ == "__main__": diff --git a/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py b/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py index ff345544c..749e6aaa5 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py @@ -1,20 +1,17 @@ from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase from frontends.gamespy.library.network.tcp_handler import TcpServer -from frontends.gamespy.library.configs import CONFIG -from frontends.gamespy.protocols.presence_search_player.applications.client import Client +from frontends.gamespy.protocols.presence_search_player.applications.client import ( + Client, +) class ServerLauncher(ServerLauncherBase): - def __init__(self) -> None: - super().__init__() - self.config = CONFIG.servers["PresenceSearchPlayer"] - - def _launch_server(self): - assert self.config is not None - assert self.logger is not None - self.server = TcpServer(self.config, Client, self.logger) - super()._launch_server() + super().__init__( + config_name="PresenceSearchPlayer", + client_cls=Client, + server_cls=TcpServer + ) if __name__ == "__main__": diff --git a/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py b/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py index 26d47c8fd..4410e59aa 100644 --- a/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py @@ -1,6 +1,5 @@ from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase from frontends.gamespy.library.network.udp_handler import UdpServer -from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.protocols.query_report.applications.client import Client @@ -8,14 +7,11 @@ class ServerLauncher(ServerLauncherBase): natneg_channel: object def __init__(self) -> None: - super().__init__() - self.config = CONFIG.servers["QueryReport"] - - def _launch_server(self): - assert self.config is not None - assert self.logger is not None - self.server = UdpServer(self.config, Client, self.logger) - super()._launch_server() + super().__init__( + config_name="QueryReport", + client_cls=Client, + server_cls=UdpServer + ) if __name__ == "__main__": diff --git a/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py b/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py index ebd02d5d4..6f94c6513 100644 --- a/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py @@ -1,22 +1,16 @@ from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase from frontends.gamespy.library.network.tcp_handler import TcpServer -from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.protocols.server_browser.v2.applications.client import Client class ServerLauncher(ServerLauncherBase): def __init__(self) -> None: - super().__init__() - self.config = CONFIG.servers["ServerBrowserV2"] - - def _launch_server(self): - assert self.config is not None - assert self.logger is not None - # todo: add v1 server here - self.server = TcpServer(self.config, Client, self.logger) - super()._launch_server() - + super().__init__( + config_name="ServerBrowserV2", client_cls=Client, server_cls=TcpServer + ) if __name__ == "__main__": - s = ServerLauncher() - s.start() + v2 = ServerLauncher() + v2.start() + # todo: add v1 server here + diff --git a/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py b/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py index 89fdd6f88..5cd286a4f 100644 --- a/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py @@ -1,6 +1,5 @@ from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase from frontends.gamespy.library.network.http_handler import HttpServer -from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.protocols.web_services.applications.client import Client @@ -8,15 +7,9 @@ class ServerLauncher(ServerLauncherBase): server: "HttpServer" def __init__(self) -> None: - super().__init__() - self.config = CONFIG.servers["WebServices"] - - def _launch_server(self): - assert self.config is not None - assert self.logger is not None - self.server = HttpServer(self.config, Client, self.logger) - super()._launch_server() - + super().__init__( + config_name="WebServices", client_cls=Client, server_cls=HttpServer + ) if __name__ == "__main__": diff --git a/src/redis_test.py b/src/redis_test.py index f3999c142..5f784cca6 100644 --- a/src/redis_test.py +++ b/src/redis_test.py @@ -16,3 +16,17 @@ # client.set("hello", "hi") # data = client.get("hello") # pass +import socket + +SERVER_IP = "127.0.0.1" # change to target IP if needed +SERVER_PORT = 27901 +MESSAGE = b"hello" + +sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + +try: + for _ in range(10): + sock.sendto(MESSAGE, (SERVER_IP, SERVER_PORT)) + print(f"Sent {MESSAGE!r} to {SERVER_IP}:{SERVER_PORT}") +finally: + sock.close() \ No newline at end of file From fb0547e77bd39aca899bd0bcdb7756fb897c56ca Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Tue, 23 Sep 2025 08:44:56 +0000 Subject: [PATCH 204/231] Update: natneg and gtr --- common/UniSpy_pg.sql | 113 ++++++++++-------- src/.devcontainer/devcontainer.json | 2 - src/.vscode/launch.json | 11 ++ src/Contribute.md | 2 +- src/backends/library/database/pg_orm.py | 85 ++++++------- .../protocols/gamespy/chat/handlers.py | 23 ++-- src/backends/protocols/gamespy/chat/helper.py | 3 - .../gamespy/game_traffic_relay/data.py | 6 +- .../gamespy/game_traffic_relay/handlers.py | 18 ++- .../gamespy/game_traffic_relay/requests.py | 2 +- src/backends/protocols/gamespy/natneg/data.py | 63 ++++++---- .../protocols/gamespy/natneg/handlers.py | 88 ++++++++++---- .../protocols/gamespy/natneg/helpers.py | 34 +++--- .../protocols/gamespy/query_report/data.py | 63 +++++++--- .../gamespy/query_report/handlers.py | 39 +++--- .../protocols/gamespy/web_services/data.py | 43 +++---- .../routers/gamespy/game_traffic_relay.py | 20 +++- src/backends/routers/gamespy/natneg.py | 11 +- src/backends/routers/gamespy/webservices.py | 46 ++++--- src/backends/routers/home.py | 5 +- .../gamespy/natneg/nat_detection_tests.py | 20 ++-- src/backends/tests/gamespy/web/__init__.py | 0 .../tests/gamespy/web/handler_tests.py | 17 +++ .../library/abstractions/server_launcher.py | 15 ++- .../gamespy/library/network/http_handler.py | 6 +- .../gamespy/library/network/tcp_handler.py | 11 +- .../protocols/chat/contracts/results.py | 11 +- .../applications/server_launcher.py | 27 ++++- .../protocols/natneg/abstractions/handlers.py | 14 --- .../protocols/natneg/aggregations/enums.py | 2 +- .../protocols/natneg/applications/handlers.py | 41 +++++++ .../protocols/natneg/applications/switcher.py | 4 + .../protocols/natneg/contracts/requests.py | 20 +++- .../protocols/natneg/contracts/responses.py | 5 +- .../protocols/natneg/contracts/results.py | 9 +- .../web_services/applications/switcher.py | 4 +- .../game_traffic_relay/handler_tests.py | 4 +- src/{ => tests}/redis_test.py | 22 ++-- 38 files changed, 574 insertions(+), 335 deletions(-) create mode 100644 src/backends/tests/gamespy/web/__init__.py create mode 100644 src/backends/tests/gamespy/web/handler_tests.py rename src/{ => tests}/redis_test.py (54%) diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 774649942..454e0f583 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -301,6 +301,7 @@ COMMENT ON TABLE unispy.grouplist IS 'Old games use grouplist to create their ga -- CREATE TABLE unispy.init_packet_caches ( + id SERIAL PRIMARY KEY NOT NULL, cookie bigint NOT NULL, server_id uuid NOT NULL, version integer NOT NULL, @@ -345,7 +346,7 @@ ALTER TABLE unispy.init_packet_caches OWNER TO unispy; -- CREATE TABLE unispy.messages ( - messageid SERIAL PRIMARY KEY NOT NULL, + id SERIAL PRIMARY KEY NOT NULL, namespaceid integer NOT NULL, type integer, from_user integer NOT NULL, @@ -358,10 +359,10 @@ CREATE TABLE unispy.messages ( ALTER TABLE unispy.messages OWNER TO unispy; -- --- Name: messages_messageid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy +-- Name: messages_id_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- --- CREATE SEQUENCE unispy.messages_messageid_seq +-- CREATE SEQUENCE unispy.messages_id_seq -- AS integer -- START WITH 1 -- INCREMENT BY 1 @@ -370,34 +371,40 @@ ALTER TABLE unispy.messages OWNER TO unispy; -- CACHE 1; -ALTER TABLE unispy.messages_messageid_seq OWNER TO unispy; +ALTER TABLE unispy.messages_id_seq OWNER TO unispy; -- --- Name: messages_messageid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy +-- Name: messages_id_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy -- -ALTER SEQUENCE unispy.messages_messageid_seq OWNED BY unispy.messages.messageid; +ALTER SEQUENCE unispy.messages_id_seq OWNED BY unispy.messages.id; -- --- Name: nat_fail_cachess; Type: TABLE; Schema: unispy; Owner: unispy +-- Name: nat_result_caches; Type: TABLE; Schema: unispy; Owner: unispy -- -CREATE TABLE unispy.nat_fail_cachess ( - record_id SERIAL NOT NULL, - public_ip_address1 inet NOT NULL, - public_ip_address2 inet NOT NULL, +CREATE TABLE unispy.nat_result_caches ( + id SERIAL PRIMARY KEY NOT NULL, + cookie smallint NOT NULL, + public_ip inet NOT NULL, + private_ip inet NOT NULL, + is_success boolean NOT NULL, + port_mapping_type smallint NOT NULL, + port_type smallint NOT NULL, + client_index smallint NOT NULL, + game_name character varying[20] NOT NULL, update_time timestamp without time zone NOT NULL ); -ALTER TABLE unispy.nat_fail_cachess OWNER TO unispy; +ALTER TABLE unispy.nat_result_caches OWNER TO unispy; -- --- Name: nat_fail_cachess_record_id_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy +-- Name: nat_result_caches_id_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- --- CREATE SEQUENCE unispy.nat_fail_cachess_record_id_seq +-- CREATE SEQUENCE unispy.nat_result_caches_id_seq -- AS integer -- START WITH 1 -- INCREMENT BY 1 @@ -406,13 +413,13 @@ ALTER TABLE unispy.nat_fail_cachess OWNER TO unispy; -- CACHE 1; -ALTER TABLE unispy.nat_fail_cachess_record_id_seq OWNER TO unispy; +ALTER TABLE unispy.nat_result_caches_id_seq OWNER TO unispy; -- --- Name: nat_fail_cachess_record_id_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy +-- Name: nat_result_caches_id_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy -- -ALTER SEQUENCE unispy.nat_fail_cachess_record_id_seq OWNED BY unispy.nat_fail_cachess.record_id; +ALTER SEQUENCE unispy.nat_result_caches_id_seq OWNED BY unispy.nat_result_caches.id; -- @@ -479,7 +486,7 @@ ALTER SEQUENCE unispy.profiles_profileid_seq OWNED BY unispy.profiles.profileid; -- CREATE TABLE unispy.pstorage ( - pstorageid SERIAL PRIMARY KEY NOT NULL, + id SERIAL PRIMARY KEY NOT NULL, profileid integer NOT NULL, ptype integer NOT NULL, dindex integer NOT NULL, @@ -490,10 +497,10 @@ CREATE TABLE unispy.pstorage ( ALTER TABLE unispy.pstorage OWNER TO unispy; -- --- Name: pstorage_pstorageid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy +-- Name: pstorage_id_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- --- CREATE SEQUENCE unispy.pstorage_pstorageid_seq +-- CREATE SEQUENCE unispy.pstorage_id_seq -- AS integer -- START WITH 1 -- INCREMENT BY 1 @@ -502,13 +509,13 @@ ALTER TABLE unispy.pstorage OWNER TO unispy; -- CACHE 1; -ALTER TABLE unispy.pstorage_pstorageid_seq OWNER TO unispy; +ALTER TABLE unispy.pstorage_id_seq OWNER TO unispy; -- --- Name: pstorage_pstorageid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy +-- Name: pstorage_id_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy -- -ALTER SEQUENCE unispy.pstorage_pstorageid_seq OWNED BY unispy.pstorage.pstorageid; +ALTER SEQUENCE unispy.pstorage_id_seq OWNED BY unispy.pstorage.id; -- @@ -516,10 +523,12 @@ ALTER SEQUENCE unispy.pstorage_pstorageid_seq OWNED BY unispy.pstorage.pstoragei -- CREATE TABLE unispy.relay_server_caches ( + id SERIAL PRIMARY KEY NOT NULL, server_id uuid NOT NULL, - public_ip_address character varying NOT NULL, + public_ip character varying NOT NULL, public_port integer NOT NULL, - client_count integer NOT NULL + client_count integer NOT NULL, + update_time timestamp without time zone NOT NULL ); @@ -530,7 +539,7 @@ ALTER TABLE unispy.relay_server_caches OWNER TO unispy; -- CREATE TABLE unispy.sakestorage ( - sakestorageid SERIAL PRIMARY KEY NOT NULL, + id SERIAL PRIMARY KEY NOT NULL, tableid integer NOT NULL, data jsonb ); @@ -546,10 +555,10 @@ COMMENT ON TABLE unispy.sakestorage IS 'Sake storage system.'; -- --- Name: sakestorage_sakestorageid_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy +-- Name: sakestorage_id_seq; Type: SEQUENCE; Schema: unispy; Owner: unispy -- --- CREATE SEQUENCE unispy.sakestorage_sakestorageid_seq +-- CREATE SEQUENCE unispy.sakestorage_id_seq -- AS integer -- START WITH 1 -- INCREMENT BY 1 @@ -558,13 +567,13 @@ COMMENT ON TABLE unispy.sakestorage IS 'Sake storage system.'; -- CACHE 1; -ALTER TABLE unispy.sakestorage_sakestorageid_seq OWNER TO unispy; +ALTER TABLE unispy.sakestorage_id_seq OWNER TO unispy; -- --- Name: sakestorage_sakestorageid_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy +-- Name: sakestorage_id_seq; Type: SEQUENCE OWNED BY; Schema: unispy; Owner: unispy -- -ALTER SEQUENCE unispy.sakestorage_sakestorageid_seq OWNED BY unispy.sakestorage.sakestorageid; +ALTER SEQUENCE unispy.sakestorage_id_seq OWNED BY unispy.sakestorage.id; -- @@ -695,17 +704,17 @@ ALTER TABLE ONLY unispy.blocked ALTER COLUMN blockid SET DEFAULT nextval('unispy -- --- Name: messages messageid; Type: DEFAULT; Schema: unispy; Owner: unispy +-- Name: messages id; Type: DEFAULT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.messages ALTER COLUMN messageid SET DEFAULT nextval('unispy.messages_messageid_seq'::regclass); +ALTER TABLE ONLY unispy.messages ALTER COLUMN id SET DEFAULT nextval('unispy.messages_id_seq'::regclass); -- --- Name: nat_fail_cachess record_id; Type: DEFAULT; Schema: unispy; Owner: unispy +-- Name: nat_result_caches id; Type: DEFAULT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.nat_fail_cachess ALTER COLUMN record_id SET DEFAULT nextval('unispy.nat_fail_cachess_record_id_seq'::regclass); +ALTER TABLE ONLY unispy.nat_result_caches ALTER COLUMN id SET DEFAULT nextval('unispy.nat_result_caches_id_seq'::regclass); -- @@ -716,17 +725,17 @@ ALTER TABLE ONLY unispy.profiles ALTER COLUMN profileid SET DEFAULT nextval('uni -- --- Name: pstorage pstorageid; Type: DEFAULT; Schema: unispy; Owner: unispy +-- Name: pstorage id; Type: DEFAULT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.pstorage ALTER COLUMN pstorageid SET DEFAULT nextval('unispy.pstorage_pstorageid_seq'::regclass); +ALTER TABLE ONLY unispy.pstorage ALTER COLUMN id SET DEFAULT nextval('unispy.pstorage_id_seq'::regclass); -- --- Name: sakestorage sakestorageid; Type: DEFAULT; Schema: unispy; Owner: unispy +-- Name: sakestorage id; Type: DEFAULT; Schema: unispy; Owner: unispy -- -ALTER TABLE ONLY unispy.sakestorage ALTER COLUMN sakestorageid SET DEFAULT nextval('unispy.sakestorage_sakestorageid_seq'::regclass); +ALTER TABLE ONLY unispy.sakestorage ALTER COLUMN id SET DEFAULT nextval('unispy.sakestorage_id_seq'::regclass); -- @@ -5317,15 +5326,15 @@ COPY unispy.init_packet_caches (cookie, server_id, version, port_type, client_in -- Data for Name: messages; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.messages (messageid, namespaceid, type, from_user, to_user, date, message) FROM stdin; +COPY unispy.messages (id, namespaceid, type, from_user, to_user, date, message) FROM stdin; \. -- --- Data for Name: nat_fail_cachess; Type: TABLE DATA; Schema: unispy; Owner: unispy +-- Data for Name: nat_result_caches; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.nat_fail_cachess (record_id, public_ip_address1, public_ip_address2, update_time) FROM stdin; +COPY unispy.nat_result_caches (id, public_ip_address1, public_ip_address2, update_time) FROM stdin; \. @@ -5352,7 +5361,7 @@ COPY unispy.profiles (profileid, userid, nick, serverflag, status, statstring, e -- Data for Name: pstorage; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.pstorage (pstorageid, profileid, ptype, dindex, data) FROM stdin; +COPY unispy.pstorage (id, profileid, ptype, dindex, data) FROM stdin; \. @@ -5368,7 +5377,7 @@ COPY unispy.relay_server_caches (server_id, public_ip_address, public_port, clie -- Data for Name: sakestorage; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.sakestorage (sakestorageid, tableid, data) FROM stdin; +COPY unispy.sakestorage (id, tableid, data) FROM stdin; \. @@ -5430,17 +5439,17 @@ SELECT pg_catalog.setval('unispy.friends_friendid_seq', 1, false); -- --- Name: messages_messageid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy +-- Name: messages_id_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy -- -SELECT pg_catalog.setval('unispy.messages_messageid_seq', 1, false); +SELECT pg_catalog.setval('unispy.messages_id_seq', 1, false); -- --- Name: nat_fail_cachess_record_id_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy +-- Name: nat_result_caches_id_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy -- -SELECT pg_catalog.setval('unispy.nat_fail_cachess_record_id_seq', 1, false); +SELECT pg_catalog.setval('unispy.nat_result_caches_id_seq', 1, false); -- @@ -5451,17 +5460,17 @@ SELECT pg_catalog.setval('unispy.profiles_profileid_seq', 2, true); -- --- Name: pstorage_pstorageid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy +-- Name: pstorage_id_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy -- -SELECT pg_catalog.setval('unispy.pstorage_pstorageid_seq', 1, false); +SELECT pg_catalog.setval('unispy.pstorage_id_seq', 1, false); -- --- Name: sakestorage_sakestorageid_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy +-- Name: sakestorage_id_seq; Type: SEQUENCE SET; Schema: unispy; Owner: unispy -- -SELECT pg_catalog.setval('unispy.sakestorage_sakestorageid_seq', 1, false); +SELECT pg_catalog.setval('unispy.sakestorage_id_seq', 1, false); -- diff --git a/src/.devcontainer/devcontainer.json b/src/.devcontainer/devcontainer.json index b8c8c34a8..e18f6d8b7 100644 --- a/src/.devcontainer/devcontainer.json +++ b/src/.devcontainer/devcontainer.json @@ -3,8 +3,6 @@ { "name": "Python 3", // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile - // "initializeCommand":"docker build --network unispy --tag mcr.microsoft.com/devcontainers/python:1-3.12-bullseye .", - // "containerName": "unispy_server_dev", "runArgs": [ "--network=unispy", "--name=unispy_server_dev" diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json index 874ca4fce..12653350d 100644 --- a/src/.vscode/launch.json +++ b/src/.vscode/launch.json @@ -80,6 +80,17 @@ }, "justMyCode": true }, + { + "name": "gtr", + "type": "debugpy", + "request": "launch", + "program": "frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py", + "console": "integratedTerminal", + "env": { + "PYTHONPATH": "${workspaceFolder}", + }, + "justMyCode": true + }, { "name": "natneg", "type": "debugpy", diff --git a/src/Contribute.md b/src/Contribute.md index 002848b89..fff0a246d 100644 --- a/src/Contribute.md +++ b/src/Contribute.md @@ -13,6 +13,6 @@ class BaseClass: # if the property do not have default value it must be initialized as None self._property1 = None # In the base class we have to check whether the _property has been initialized, if not we init it - if not hasattr(self,"_property1"): + if self._property1 is not None: self._property2 = value2 ``` \ No newline at end of file diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 8eebcb2b7..41d31f40d 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -19,6 +19,7 @@ from sqlalchemy.types import TypeDecorator from frontends.gamespy.protocols.natneg.aggregations.enums import ( NatClientIndex, + NatPortMappingScheme, NatPortType, ) from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( @@ -32,6 +33,7 @@ import enum from sqlalchemy.orm.decl_api import DeclarativeBase + class IntEnum(TypeDecorator): impl = sa.Integer @@ -72,8 +74,10 @@ class Users(Base): class Profiles(Base): __tablename__ = "profiles" - profileid: Column[int] = Column(Integer, primary_key=True, autoincrement=True) - userid: Column[int] = Column(Integer, ForeignKey("users.userid"), nullable=False) + profileid: Column[int] = Column( + Integer, primary_key=True, autoincrement=True) + userid: Column[int] = Column( + Integer, ForeignKey("users.userid"), nullable=False) nick: Column[str] = Column(String, nullable=False) serverflag: Column[int] = Column(Integer, nullable=False, default=0) status = Column(IntEnum(GPStatusCode), default=GPStatusCode.OFFLINE) @@ -104,7 +108,8 @@ class Blocked(Base): __tablename__ = "blocked" blockid = Column(Integer, primary_key=True, autoincrement=True) - profileid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) + profileid = Column(Integer, ForeignKey( + "profiles.profileid"), nullable=False) namespaceid = Column(Integer, nullable=False) targetid = Column(Integer, nullable=False) @@ -113,7 +118,8 @@ class Friends(Base): __tablename__ = "friends" friendid = Column(Integer, primary_key=True, autoincrement=True) - profileid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) + profileid = Column(Integer, ForeignKey( + "profiles.profileid"), nullable=False) targetid = Column(Integer, nullable=False) namespaceid = Column(Integer, nullable=False) @@ -122,8 +128,10 @@ class FriendAddRequest(Base): __tablename__ = "addrequests" addrequestid = Column(Integer, primary_key=True, autoincrement=True) - profileid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) - targetid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) + profileid = Column(Integer, ForeignKey( + "profiles.profileid"), nullable=False) + targetid = Column(Integer, ForeignKey( + "profiles.profileid"), nullable=False) namespaceid = Column(Integer, nullable=False) reason = Column(String, nullable=False) status = Column( @@ -135,7 +143,6 @@ class FriendAddRequest(Base): class Games(Base): __tablename__ = "games" - gameid = Column(Integer, primary_key=True) gamename = Column(String, nullable=False) secretkey = Column(String, nullable=False) @@ -145,7 +152,6 @@ class Games(Base): class GroupList(Base): __tablename__ = "grouplist" - groupid = Column(Integer, primary_key=True) gameid = Column(Integer, ForeignKey("games.gameid"), nullable=False) roomname = Column(Text, nullable=False) @@ -153,8 +159,7 @@ class GroupList(Base): class Messages(Base): __tablename__ = "messages" - - messageid = Column(Integer, primary_key=True, autoincrement=True) + id = Column(Integer, primary_key=True, autoincrement=True) namespaceid = Column(Integer, nullable=False) type = Column(Integer) from_user = Column(Integer, nullable=False) @@ -165,16 +170,15 @@ class Messages(Base): class Partner(Base): __tablename__ = "partner" - partnerid = Column(Integer, primary_key=True) partnername = Column(String, nullable=False) class PStorage(Base): __tablename__ = "pstorage" - - pstorageid = Column(Integer, primary_key=True, autoincrement=True) - profileid = Column(Integer, ForeignKey("profiles.profileid"), nullable=False) + id = Column(Integer, primary_key=True, autoincrement=True) + profileid = Column(Integer, ForeignKey( + "profiles.profileid"), nullable=False) ptype = Column(Integer, nullable=False) dindex = Column(Integer, nullable=False) data = Column(JSONB) @@ -187,21 +191,20 @@ class GameStatusSnapShot(Base): session_key = Column(String) game_name = Column(String) game_data = Column(JSONB) - update_time = Column(DateTime, nullable=False) + update_time = Column(DateTime, nullable=False, default=datetime.now()) class SakeStorage(Base): __tablename__ = "sakestorage" - - sakestorageid = Column(Integer, primary_key=True, autoincrement=True) + id = Column(Integer, primary_key=True, autoincrement=True) tableid = Column(Integer, nullable=False) data: Column[JSONB] = Column(JSONB, nullable=False) class InitPacketCaches(Base): __tablename__ = "init_packet_caches" - - cookie = Column(BigInteger, primary_key=True, nullable=False) + id = Column(Integer, primary_key=True, autoincrement=True) + cookie = Column(SmallInteger, nullable=False) server_id = Column(UUID, nullable=False) version = Column(Integer, nullable=False) port_type = Column(IntEnum(NatPortType), nullable=False) @@ -212,37 +215,37 @@ class InitPacketCaches(Base): public_port = Column(Integer, nullable=False) private_ip = Column(String, nullable=False) private_port = Column(Integer, nullable=False) - update_time = Column(DateTime, nullable=False) + update_time = Column(DateTime, nullable=False, default=datetime.now()) -class NatFailCaches(Base): - __tablename__ = "nat_fail_cachess" - record_id = Column(Integer, primary_key=True, autoincrement=True) - public_ip_address1 = Column(INET, nullable=False) - public_ip_address2 = Column(INET, nullable=False) - update_time = Column(DateTime, nullable=False) - - -# class ReportPackets(Base): -# __tablename__ = "report_packets" -# public_ip_address1 = Column(String, nullable=False) -# public_ip_address2 = Column(String, nullable=False) -# update_time = Column(DateTime, nullable=False) +class NatResultCaches(Base): + __tablename__ = "nat_result_caches" + id = Column(Integer, primary_key=True, autoincrement=True) + cookie = Column(SmallInteger, nullable=False) + public_ip = Column(SmallInteger, nullable=False) + private_ip = Column(SmallInteger, nullable=False) + is_success = Column(Boolean, nullable=False) + port_mapping_type = Column(IntEnum(NatPortMappingScheme), nullable=False) + port_type = Column(IntEnum(NatPortType), nullable=False) + client_index = Column(IntEnum(NatClientIndex), nullable=False) + game_name = Column(String, nullable=True) + update_time = Column(DateTime, nullable=False, default=datetime.now()) class RelayServerCaches(Base): __tablename__ = "relay_server_caches" + id = Column(Integer, primary_key=True, autoincrement=True) server_id = Column(UUID, primary_key=True, nullable=False) - public_ip_address = Column(String, nullable=False) + public_ip = Column(String, nullable=False) public_port = Column(Integer, nullable=False) client_count = Column(Integer, nullable=False) - update_time = Column(DateTime, nullable=False) + update_time = Column(DateTime, nullable=False, default=datetime.now()) class ChatChannelCaches(Base): __tablename__ = "chat_channel_caches" - server_id = Column(UUID, nullable=False) channel_name = Column(String, primary_key=True, nullable=False) + server_id = Column(UUID, nullable=False) creator = Column(String, nullable=False) game_name = Column(String, nullable=False) creator = Column(String, nullable=True) @@ -253,9 +256,9 @@ class ChatChannelCaches(Base): max_num_user = Column(Integer, nullable=False) key_values = Column(JSONB, default={}) invited_nicks = Column(JSONB, default=[]) - update_time = Column(DateTime, nullable=False) modes = Column(JSONB, default=[]) banned_nicks = Column(JSONB, default=[]) + update_time = Column(DateTime, nullable=False, default=datetime.now()) class ChatUserCaches(Base): @@ -264,15 +267,15 @@ class ChatUserCaches(Base): """ __tablename__ = "chat_user_caches" - server_id = Column(UUID, nullable=False) nick_name = Column(String, primary_key=True, nullable=True) + server_id = Column(UUID, nullable=False) user_name = Column(String, nullable=True) game_name = Column(String, nullable=True) remote_ip = Column(INET, nullable=False) remote_port = Column(Integer, nullable=False) websocket_address = Column(String, nullable=False) key_value = Column(JSONB, default={}) - update_time = Column(DateTime, nullable=False) + update_time = Column(DateTime, nullable=False, default=datetime.now()) class ChatChannelUserCaches(Base): @@ -293,7 +296,7 @@ class ChatChannelUserCaches(Base): String, ForeignKey("chat_channel_caches.channel_name"), nullable=False ) server_id = Column(UUID, nullable=False) - update_time = Column(DateTime, nullable=False) + update_time = Column(DateTime, nullable=False, default=datetime.now()) # can we directly store the flags? is_voiceable = Column(Boolean, nullable=False) is_channel_operator = Column(Boolean, nullable=False) @@ -310,12 +313,12 @@ class GameServerCaches(Base): host_ip_address = Column(INET, nullable=False) game_name = Column(String, nullable=False) query_report_port = Column(Integer, nullable=False) - update_time = Column(DateTime, nullable=False) status = Column(IntEnum(GameServerStatus)) player_data = Column(JSONB, nullable=False) server_data = Column(JSONB, nullable=False) team_data = Column(JSONB, nullable=False) avaliable = Column(Boolean, nullable=True) + update_time = Column(DateTime, nullable=False, default=datetime.now()) ENGINE = create_engine(CONFIG.postgresql.url) diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index 7500e9420..d2eea7c2c 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -71,6 +71,7 @@ NoticeResult, PartResult, PrivateResult, + SetCKeyResult, SetChannelKeyResult, TopicResult, UtmResult, @@ -192,7 +193,6 @@ def _data_operate(self) -> None: server_id=self._request.server_id, remote_ip=self._request.client_ip, remote_port=self._request.client_port, - update_time=datetime.now(), nick_name=f"{self._request.client_ip}:{self._request.client_port}", websocket_address=self._request.websocket_address, game_name=self._request.gamename, @@ -364,7 +364,8 @@ def _data_operate(self) -> None: class RegisterNickHandler(HandlerBase): def _data_operate(self) -> None: - raise NotImplementedError("we do not know which unique nick should be updated") + raise NotImplementedError( + "we do not know which unique nick should be updated") class SetKeyHandler(HandlerBase): @@ -375,7 +376,8 @@ def _data_operate(self) -> None: self._request.client_ip, self._request.client_port, self._session ) if user is None: - raise NoSuchNickException("The ip and port is not find in database") + raise NoSuchNickException( + "The ip and port is not find in database") user.key_value = self._request.key_values # type:ignore super()._data_operate() @@ -423,7 +425,8 @@ class WhoIsHandler(HandlerBase): _request: WhoIsRequest def _data_operate(self) -> None: - self._data: dict = data.get_whois_result(self._request.nick_name, self._session) + self._data: dict = data.get_whois_result( + self._request.nick_name, self._session) def _result_construct(self) -> None: self._result = WhoIsResult( @@ -458,7 +461,6 @@ def _data_operate(self) -> None: room_name="", topic="", key_values={}, - update_time=datetime.now(), creator=self._user.nick_name, # type: ignore group_id=0, max_num_user=100, @@ -671,6 +673,7 @@ def _request_check(self) -> None: assert isinstance(self._channel_user.is_channel_operator, bool) if self._channel_user.is_channel_operator: self._channel.key_values = self._request.key_values # type:ignore + self._session.commit() def _result_construct(self) -> None: assert self._channel_user @@ -696,8 +699,14 @@ def _data_operate(self) -> None: self._is_broadcast = True def _result_construct(self) -> None: - # todo think how to broadcast message - raise NotImplementedError() + assert self._channel_user + assert isinstance(self._channel_user.nick_name, str) + assert isinstance(self._channel_user.user_name, str) + self._result = SetCKeyResult( + setter_nick_name=self._channel_user.nick_name, + setter_user_name=self._channel_user.user_name, + channel_name=self._request.channel_name, + ) class TopicHandler(ChannelHandlerBase): diff --git a/src/backends/protocols/gamespy/chat/helper.py b/src/backends/protocols/gamespy/chat/helper.py index 796c692c2..14e8dcc4f 100644 --- a/src/backends/protocols/gamespy/chat/helper.py +++ b/src/backends/protocols/gamespy/chat/helper.py @@ -90,7 +90,6 @@ def join( nick_name=user.nick_name, user_name=user.user_name, channel_name=channel.channel_name, - update_time=datetime.now(), is_voiceable=True, is_channel_operator=False, is_channel_creator=is_creator, @@ -170,7 +169,6 @@ def create( group_id: int, max_num_user: int, key_values: dict, - update_time: datetime, session: Session, modes: list = [], creator: str | None = None, @@ -191,7 +189,6 @@ def create( group_id=group_id, max_num_user=max_num_user, key_values=key_values, - update_time=update_time, creator=creator, modes=modes, ) diff --git a/src/backends/protocols/gamespy/game_traffic_relay/data.py b/src/backends/protocols/gamespy/game_traffic_relay/data.py index c40fc0c6a..8e9b27296 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/data.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/data.py @@ -12,7 +12,7 @@ def search_relay_server( session.query(RelayServerCaches) .where( RelayServerCaches.server_id == server_id, - RelayServerCaches.public_ip_address == server_ip, + RelayServerCaches.public_ip == server_ip, ) .first() ) @@ -36,7 +36,7 @@ def update_relay_server(info: RelayServerCaches, session: Session): session.commit() -def add_relay_server(info: RelayServerCaches, session: Session): +def create_relay_server(info: RelayServerCaches, session: Session): session.add(info) session.commit() @@ -50,7 +50,7 @@ def delete_relay_server(server_id: UUID, ip_address: str, port: int, session: Se session.query(RelayServerCaches) .where( RelayServerCaches.server_id == server_id, - RelayServerCaches.public_ip_address == ip_address, + RelayServerCaches.public_ip == ip_address, RelayServerCaches.public_port == port, ) .first() diff --git a/src/backends/protocols/gamespy/game_traffic_relay/handlers.py b/src/backends/protocols/gamespy/game_traffic_relay/handlers.py index a39ae0ea7..b90f38aae 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/handlers.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/handlers.py @@ -4,21 +4,20 @@ from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import RelayServerCaches from backends.protocols.gamespy.game_traffic_relay.requests import ( - GTRHeartBeat, + GtrHeartBeatRequest, ) import backends.protocols.gamespy.game_traffic_relay.data as data -class GTRHeartBeatHandler(HandlerBase): - _request: GTRHeartBeat +class GtrHeartBeatHandler(HandlerBase): + _request: GtrHeartBeatRequest - def __init__(self, request: GTRHeartBeat) -> None: - assert isinstance(request, GTRHeartBeat) + def __init__(self, request: GtrHeartBeatRequest) -> None: + assert isinstance(request, GtrHeartBeatRequest) self._request = request - self.logger = logging.getLogger("backend") self._result = None - self._response = OKResponse() + self.logger = logging.getLogger("backend") def _data_operate(self) -> None: info = data.search_relay_server( @@ -27,12 +26,11 @@ def _data_operate(self) -> None: if info is None: info = RelayServerCaches( server_id=self._request.server_id, - public_ip_address=self._request.public_ip_address, + public_ip=self._request.public_ip_address, public_port=self._request.public_port, client_count=self._request.client_count, - update_time=datetime.now(), ) - data.add_relay_server(info, self._session) + data.create_relay_server(info, self._session) else: # refresh update time data.update_relay_server(info, self._session) diff --git a/src/backends/protocols/gamespy/game_traffic_relay/requests.py b/src/backends/protocols/gamespy/game_traffic_relay/requests.py index 473015492..69e8e43a8 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/requests.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/requests.py @@ -5,7 +5,7 @@ There are 2 UpdateGTRServiceRequest class The other one is in frontends/gamespy/protocols/game_traffic_relay/contracts/general.py """ -class GTRHeartBeat(BaseModel): +class GtrHeartBeatRequest(BaseModel): server_id: UUID public_ip_address: str public_port: int diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py index 771e0f2b0..b7e52d30e 100644 --- a/src/backends/protocols/gamespy/natneg/data.py +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -2,7 +2,7 @@ from backends.library.database.pg_orm import ( InitPacketCaches, - NatFailCaches, + NatResultCaches, RelayServerCaches, ) from frontends.gamespy.protocols.natneg.aggregations.enums import ( @@ -20,6 +20,12 @@ def add_init_packet(info: InitPacketCaches, session: Session) -> None: session.commit() +def clean_expired_init_cache(session: Session) -> None: + session.query(InitPacketCaches).where( + InitPacketCaches.update_time < datetime.now() - timedelta(seconds=30) + ) + + def count_init_info(cookie: int, version: int, session: Session) -> int: time = datetime.now() - timedelta(seconds=30) @@ -28,14 +34,14 @@ def count_init_info(cookie: int, version: int, session: Session) -> int: .where( InitPacketCaches.cookie == cookie, InitPacketCaches.version == version, - InitPacketCaches.update_time >= time, + InitPacketCaches.update_time <= time, ) .count() ) return result -def get_init_info( +def get_init_cache( cookie: int, client_index: NatClientIndex, port_type: NatPortType, session: Session ) -> InitPacketCaches | None: result = ( @@ -50,7 +56,7 @@ def get_init_info( return result -def get_init_infos( +def get_init_caches( cookie: int, client_index: NatClientIndex, session: Session ) -> list[InitPacketCaches]: # query the latest init info with in 30 seconds @@ -81,44 +87,59 @@ def remove_init_info(info: InitPacketCaches, session: Session) -> None: session.commit() -def store_nat_fail_info(info: NatFailCaches, session: Session) -> None: - assert isinstance(info, NatFailCaches) +def store_nat_result_info(info: NatResultCaches, session: Session) -> None: + assert isinstance(info, NatResultCaches) session.add(info) session.commit() -def update_nat_fail_info(info: NatFailCaches, session: Session) -> None: - assert isinstance(info, NatFailCaches) +def update_nat_result_info(info: NatResultCaches, session: Session) -> None: + assert isinstance(info, NatResultCaches) - result = get_nat_fail_info(info, session) + result = get_nat_result_info(info, session) if result is not None: session.delete(result) - store_nat_fail_info(info, session) + store_nat_result_info(info, session) -def remove_nat_fail_info(info: NatFailCaches, session: Session) -> None: - assert isinstance(info, NatFailCaches) +def remove_nat_result_info(info: NatResultCaches, session: Session) -> None: + assert isinstance(info, NatResultCaches) session.delete(info) session.commit() -def get_nat_fail_info(info: NatFailCaches, session: Session): - result = get_nat_fail_info_by_ip( - str(info.public_ip_address1), str(info.public_ip_address2), session +def get_nat_result_info(info: NatResultCaches, session: Session): + assert isinstance(info.cookie, int) + assert isinstance(info.public_ip, str) + result = get_nat_result_info_by_cookie_ip( + info.cookie, info.public_ip, session + ) + return result + + +def get_nat_result_info_by_ip( + public_ip: str, session: Session +) -> list[NatResultCaches]: + result = ( + session.query(NatResultCaches) + .where( + NatResultCaches.public_ip == public_ip, + ) + .all() ) return result -def get_nat_fail_info_by_ip( - public_ip1: str, public_ip2: str, session: Session -) -> list[NatFailCaches]: +def get_nat_result_info_by_cookie_ip( + cookie: int, public_ip: str, session: Session +) -> list[NatResultCaches]: result = ( - session.query(NatFailCaches) + session.query(NatResultCaches) .where( - NatFailCaches.public_ip_address1 == public_ip1, - NatFailCaches.public_ip_address2 == public_ip2, + NatResultCaches.cookie == cookie, + NatResultCaches.public_ip == public_ip, ) .all() ) diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py index 251290e71..3657d392a 100644 --- a/src/backends/protocols/gamespy/natneg/handlers.py +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -1,11 +1,13 @@ from datetime import datetime, timezone +from time import sleep from backends.library.abstractions.handler_base import HandlerBase -from backends.library.database.pg_orm import InitPacketCaches +from backends.library.database.pg_orm import InitPacketCaches, NatResultCaches import backends.protocols.gamespy.natneg.data as data from backends.protocols.gamespy.natneg.helpers import NatProtocolHelper, NatStrategy -from backends.protocols.gamespy.natneg.requests import ConnectRequest, InitRequest +from backends.protocols.gamespy.natneg.requests import ConnectRequest, InitRequest, ReportRequest from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.protocols.natneg.aggregations.enums import ( + ConnectPacketStatus, NatClientIndex, ) from frontends.gamespy.protocols.natneg.contracts.results import ConnectResult @@ -19,7 +21,7 @@ def __init__(self, request: InitRequest) -> None: super().__init__(request) def _data_operate(self) -> None: - info = data.get_init_info( + info = data.get_init_cache( self._request.cookie, self._request.client_index, self._request.port_type, @@ -48,15 +50,22 @@ def _data_operate(self) -> None: class ConnectHandler(HandlerBase): _request: ConnectRequest - _strategy: NatStrategy + _strategy: NatStrategy | None + _is_valid: bool def __init__(self, request: ConnectRequest) -> None: super().__init__(request) assert isinstance(request, ConnectRequest) + self._strategy = None def _data_operate(self) -> None: + # first sleep 5 time to + sleep(2) + self._get_client_pair() + + def _get_client_pair(self) -> None: # analysis NAT of both parties and find the proper ips - init_infos_1 = data.get_init_infos( + init_infos_1 = data.get_init_caches( self._request.cookie, self._request.client_index, self._session ) # choose the other index of the currect client @@ -64,17 +73,21 @@ def _data_operate(self) -> None: client_index_2 = NatClientIndex.GAME_SERVER else: client_index_2 = NatClientIndex.GAME_CLIENT - init_infos_2 = data.get_init_infos( + init_infos_2 = data.get_init_caches( self._request.cookie, client_index_2, self._session ) if len(init_infos_1) == 0 or len(init_infos_2) == 0: - raise UniSpyException( - f"no init info found for cookie {self._request.cookie}" - ) + self._is_valid = False + self.logger.info( + f"client1 init count:{len(init_infos_1)}, client2 init count:{len(init_infos_2)}") + return + else: + self._is_valid = True + assert isinstance(init_infos_1[0].public_ip, str) assert isinstance(init_infos_2[0].public_ip, str) - nat_fail_infos = data.get_nat_fail_info_by_ip( - init_infos_1[0].public_ip, init_infos_2[0].public_ip, self._session + nat_fail_infos = data.get_nat_result_info_by_ip( + init_infos_1[0].public_ip, self._session ) self._strategy = NatStrategy.USE_GAME_TRAFFIC_RALEY if len(nat_fail_infos) != 0: @@ -87,31 +100,66 @@ def _data_operate(self) -> None: ) def _result_construct(self) -> None: + if not self._is_valid: + self._result = ConnectResult( + is_both_client_ready=False, + ip=None, + port=None, + status=None + ) + return + if self._strategy == NatStrategy.USE_PRIVATE_IP: self._result = ConnectResult( + is_both_client_ready=True, ip=self._helper2.private_ip, port=self._helper2.private_port, - version=self._helper2.version, - cookie=self._helper2.cookie, + status=ConnectPacketStatus.NO_ERROR ) elif self._strategy == NatStrategy.USE_PUBLIC_IP: self._result = ConnectResult( + is_both_client_ready=True, ip=self._helper2.public_ip, port=self._helper2.public_port, - version=self._helper2.version, - cookie=self._helper2.cookie, + status=ConnectPacketStatus.NO_ERROR ) elif self._strategy == NatStrategy.USE_GAME_TRAFFIC_RALEY: # get a small number of players server from database - relay_servers = data.get_game_traffic_relay_servers(self._session, 5) + relay_servers = data.get_game_traffic_relay_servers( + self._session, 5) # select strategy to choose one gtr server rs = relay_servers[0] - assert isinstance(rs.public_ip_address, str) + assert isinstance(rs.public_ip, str) assert isinstance(rs.public_port, int) self._result = ConnectResult( - ip=rs.public_ip_address, + is_both_client_ready=True, + ip=rs.public_ip, port=rs.public_port, - version=self._helper2.version, - cookie=self._helper2.cookie, + status=ConnectPacketStatus.NO_ERROR ) + + +class ReportHandler(HandlerBase): + _request: ReportRequest + + def _data_operate(self) -> None: + init_cache = data.get_init_cache( + self._request.cookie, + self._request.client_index, + self._request.port_type, + self._session + ) + if init_cache is None: + raise UniSpyException( + f"No init package found for report pacakge cookie: {self._request.cookie} client_index: {self._request.client_index}") + report_cache = NatResultCaches( + cookie=self._request.cookie, + public_ip=init_cache.public_ip, + private_ip=init_cache.private_ip, + is_success=self._request.is_nat_success, + nat_type=self._request.nat_type, + client_index=self._request.client_index, + game_name=self._request.game_name, + ) + data.update_nat_result_info(report_cache, self._session) diff --git a/src/backends/protocols/gamespy/natneg/helpers.py b/src/backends/protocols/gamespy/natneg/helpers.py index 049b766d2..66b2f9926 100644 --- a/src/backends/protocols/gamespy/natneg/helpers.py +++ b/src/backends/protocols/gamespy/natneg/helpers.py @@ -8,6 +8,7 @@ NatType, ) + class NatStrategy(enum.IntEnum): USE_PUBLIC_IP = 0 USE_PRIVATE_IP = 1 @@ -15,9 +16,9 @@ class NatStrategy(enum.IntEnum): class NatNegVersion(enum.IntEnum): - VERSION1 = 1 VERSION2 = 2 VERSION3 = 3 + VERSION4 = 4 class NatProtocolHelper: @@ -35,7 +36,8 @@ class NatProtocolHelper: def __init__(self, init_caches: list[InitPacketCaches]) -> None: if len(init_caches) < 3: - raise UniSpyException("init cache length not enough for NAT determination") + raise UniSpyException( + "init cache length not enough for NAT determination") self.nat_type = NatType.NO_NAT self.port_mapping = NatPortMappingScheme.CONSISTENT_PORT self.guessed_next_port = 0 @@ -44,8 +46,6 @@ def __init__(self, init_caches: list[InitPacketCaches]) -> None: assert isinstance(cache.port_type, NatPortType) self.address_infos[cache.port_type] = cache - - last_address_info = list(self.address_infos.values())[-1] assert isinstance(last_address_info.cookie, int) assert isinstance(last_address_info.version, int) @@ -66,15 +66,16 @@ def __init__(self, init_caches: list[InitPacketCaches]) -> None: if self.version not in NatNegVersion: raise UniSpyException("Unknown natneg version") version = NatNegVersion(self.version) - if version == NatNegVersion.VERSION1: + if version == NatNegVersion.VERSION2: raise UniSpyException("Version 1 not implemented") - elif version == NatNegVersion.VERSION2: - self._validate_version2() - NatProtocolHelper._determine_nat_type_version2(self) elif version == NatNegVersion.VERSION3: self._validate_version3() NatProtocolHelper._determine_nat_type_version3(self) - def _validate_version2(self): + elif version == NatNegVersion.VERSION4: + self._validate_version4() + NatProtocolHelper._determine_nat_type_version4(self) + + def _validate_version3(self): if not ( NatPortType.NN1 in self.address_infos and NatPortType.NN2 in self.address_infos @@ -112,7 +113,7 @@ def _validate_version2(self): ): # type: ignore raise UniSpyException("GP packet info is not correct") - def _validate_version3(self): + def _validate_version4(self): # TODO: some games will not send GP packet to NAT negotiation server; currently, the reason is unknown and requires more games for analysis. # This will happen in GameClient @@ -175,9 +176,10 @@ def _validate_version3(self): raise UniSpyException("GP packet info is not correct") @staticmethod - def _determine_nat_type_version2(info: "NatProtocolHelper"): + def _determine_nat_type_version3(info: "NatProtocolHelper"): if len(info.address_infos) < 3: - raise UniSpyException("We need 3 init records to determine the nat type.") + raise UniSpyException( + "We need 3 init records to determine the nat type.") nn1 = info.address_infos[NatPortType.NN1] nn2 = info.address_infos[NatPortType.NN2] @@ -199,9 +201,10 @@ def _determine_nat_type_version2(info: "NatProtocolHelper"): info.nat_type = NatType.UNKNOWN @staticmethod - def _determine_nat_type_version3(info: "NatProtocolHelper"): + def _determine_nat_type_version4(info: "NatProtocolHelper"): if len(info.address_infos) < 3: - raise UniSpyException("We need 3 init records to determine the nat type.") + raise UniSpyException( + "We need 3 init records to determine the nat type.") nn1 = info.address_infos[NatPortType.NN1] nn2 = info.address_infos[NatPortType.NN2] @@ -249,7 +252,8 @@ def choose_nat_strategy( is_both_have_same_public_ip = helper1.public_ip == helper2.public_ip # Check if the first 3 bytes of the private IP addresses are the same is_both_in_same_private_ip_range = ( - helper1.private_ip.split(".")[:3] == helper2.private_ip.split(".")[:3] + helper1.private_ip.split( + ".")[:3] == helper2.private_ip.split(".")[:3] ) # we use p2p strategy when nat punch is available diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index 4cee0c36d..9c2242ec8 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -1,4 +1,5 @@ from typing import TYPE_CHECKING, cast +from uuid import UUID from backends.library.database.pg_orm import ( ENGINE, ChatChannelCaches, @@ -6,6 +7,7 @@ Games, GameServerCaches, ) +from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.protocols.chat.aggregates.peer_room import PeerRoom from frontends.gamespy.protocols.query_report.aggregates.game_server_info import ( GameServerInfo, @@ -15,6 +17,10 @@ ) from sqlalchemy.orm import Session +from datetime import datetime + +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus + def get_all_groups() -> dict: with Session(ENGINE) as session: @@ -142,24 +148,24 @@ def get_server_info_list_with_game_name( return data -def get_server_info_with_ip_and_port(ip: str, port: int,session:Session) -> GameServerInfo | None: +def get_server_info_with_ip_and_port(ip: str, port: int, session: Session) -> GameServerInfo | None: assert isinstance(ip, str) assert isinstance(port, int) result = ( - session.query(GameServerCaches) - .where( - GameServerCaches.host_ip_address == ip, - GameServerCaches.query_report_port == port, - ) - .first() + session.query(GameServerCaches) + .where( + GameServerCaches.host_ip_address == ip, + GameServerCaches.query_report_port == port, ) + .first() + ) if result is None: return None data = GameServerInfo(**result.__dict__) return data -def remove_server_info(info: GameServerCaches,session:Session) -> None: +def remove_server_info(info: GameServerCaches, session: Session) -> None: session.delete(info) session.commit() @@ -167,15 +173,44 @@ def remove_server_info(info: GameServerCaches,session:Session) -> None: # todo finish the GameServerCaches creation -def create_game_server(info: GameServerCaches,session:Session) -> None: +def create_game_server(info: GameServerCaches, session: Session) -> None: session.add(info) - update_game_server(session) + session.commit() -def update_game_server(session:Session) -> None: - # info.update_time = datetime.now() # type:ignore - with Session(ENGINE) as session: - session.commit() +def update_game_server( + cache: GameServerCaches, + instant_key: str, + server_id: UUID, + host_ip_address: str, + game_name: str, + query_report_port: int, + server_status: GameServerStatus, + player_data: list[dict[str, object]], + server_data: dict[str, object], + team_data: list[dict[str, object]], + session: Session, +) -> None: + cache.instant_key = instant_key # type: ignore + cache.server_id = server_id # type: ignore + cache.host_ip_address = host_ip_address # type: ignore + cache.game_name = game_name # type: ignore + cache.query_report_port = query_report_port # type: ignore + cache.update_time = datetime.now() # type: ignore + cache.status = server_status # type: ignore + cache.player_data = player_data # type: ignore + cache.server_data = server_data # type: ignore + cache.team_data = team_data # type: ignore + session.commit() + + +def refresh_game_server_cache(instant_key: str, session: Session): + cache = get_server_info_with_instant_key(instant_key, session) + if cache is None: + raise UniSpyException( + "no game server cache found, please check the database") + cache.update_time = datetime.now() # type: ignore + session.commit() if __name__ == "__main__": diff --git a/src/backends/protocols/gamespy/query_report/handlers.py b/src/backends/protocols/gamespy/query_report/handlers.py index 6c93ea77d..6c5c04cdb 100644 --- a/src/backends/protocols/gamespy/query_report/handlers.py +++ b/src/backends/protocols/gamespy/query_report/handlers.py @@ -47,7 +47,6 @@ def _data_operate(self) -> None: host_ip_address=self._request.client_ip, game_name=self._request.game_name, query_report_port=self._request.client_port, - update_time=datetime.now(), status=self._request.server_status, player_data=self._request.player_data, server_data=self._request.server_data, @@ -56,31 +55,25 @@ def _data_operate(self) -> None: ) data.create_game_server(cache, self._session) else: - cache.instant_key = self._request.instant_key # type: ignore - cache.server_id = self._request.server_id # type: ignore - cache.host_ip_address = self._request.client_ip # type: ignore - cache.game_name = self._request.game_name # type: ignore - cache.query_report_port = self._request.client_port # type: ignore - cache.update_time = datetime.now() # type: ignore - cache.status = self._request.server_status # type: ignore - cache.player_data = self._request.player_data # type: ignore - cache.server_data = self._request.server_data # type: ignore - cache.team_data = self._request.team_data # type: ignore - cache.avaliable = True # type: ignore - data.update_game_server(self._session) + data.update_game_server( + cache=cache, + instant_key=self._request.instant_key, + server_id=self._request.server_id, + host_ip_address=self._request.client_ip, + game_name=self._request.game_name, + query_report_port=self._request.client_port, + server_status=self._request.server_status, + player_data=self._request.player_data, + server_data=self._request.server_data, + team_data=self._request.team_data, + session=self._session, + ) class KeepAliveHandler(HandlerBase): _request: KeepAliveRequest def _data_operate(self) -> None: - with Session(ENGINE) as session: - cache = ( - session.query(GameServerCaches) - .where(GameServerCaches.instant_key == self._request.instant_key) - .first() - ) - # update heartbeat time - cache.update_time = datetime.now() # type: ignore - - session.commit() + assert isinstance(self._request.instant_key, str) + data.refresh_game_server_cache( + self._request.instant_key, self._session) diff --git a/src/backends/protocols/gamespy/web_services/data.py b/src/backends/protocols/gamespy/web_services/data.py index 48867e41f..53f539183 100644 --- a/src/backends/protocols/gamespy/web_services/data.py +++ b/src/backends/protocols/gamespy/web_services/data.py @@ -42,7 +42,8 @@ def is_user_exist( .first() ) if result is None: - raise AuthException("No account exists with the provided email address.") + raise AuthException( + "No account exists with the provided email address.") def get_info_by_cdkey_email( @@ -102,28 +103,27 @@ def get_info_by_authtoken( """ result = ( - session.query(Users, Profiles, SubProfiles) - .join(Users, Users.userid == Profiles.userid) - .join(Profiles, Profiles.profileid == SubProfiles.profileid) + session.query(Users.userid, + Profiles.profileid, + Profiles.nick, + SubProfiles.uniquenick, + SubProfiles.cdkeyenc) + .join(Profiles, Profiles.userid == Users.userid) + .join(SubProfiles, SubProfiles.profileid == Profiles.profileid) .where(SubProfiles.authtoken == auth_token) .first() ) if result is None: raise AuthException("No account exists with the provided authtoken.") - user: Users = result[0] - profile: Profiles = result[1] - subprofile: SubProfiles = result[2] - assert isinstance(user.userid, int) - assert isinstance(profile.profileid, int) - assert isinstance(profile.nick, str) - assert isinstance(subprofile.uniquenick, str) - assert isinstance(subprofile.cdkeyenc, str) + + userid, profileid, nick, uniquenick, cdkeyenc = result + assert isinstance(userid, int) + assert isinstance(profileid, int) + assert isinstance(nick, str) + assert isinstance(uniquenick, str) + assert isinstance(cdkeyenc, str) return ( - user.userid, - profile.profileid, - profile.nick, - subprofile.uniquenick, - subprofile.cdkeyenc, + userid, profileid, nick, uniquenick, cdkeyenc ) @@ -180,7 +180,8 @@ def get_info_by_uniquenick( def get_user_data(table_id: int, session: Session) -> dict: result = ( - session.query(SakeStorage.data).where(SakeStorage.tableid == table_id).first() + session.query(SakeStorage.data).where( + SakeStorage.tableid == table_id).first() ) if TYPE_CHECKING: @@ -189,7 +190,8 @@ def get_user_data(table_id: int, session: Session) -> dict: def update_user_data(table_id: int, data: dict, session: Session) -> None: - result = session.query(SakeStorage).where(SakeStorage.tableid == table_id).first() + result = session.query(SakeStorage).where( + SakeStorage.tableid == table_id).first() if result is None: raise SakeException("user data not found") assert isinstance(result.data, dict) @@ -206,7 +208,8 @@ def create_records(table_id: int, data: dict, session: Session) -> None: assert isinstance(table_id, int) assert isinstance(data, dict) - result = session.query(SakeStorage).where(SakeStorage.tableid == table_id).count() + result = session.query(SakeStorage).where( + SakeStorage.tableid == table_id).count() if result != 0: raise SakeException("Records already existed") diff --git a/src/backends/routers/gamespy/game_traffic_relay.py b/src/backends/routers/gamespy/game_traffic_relay.py index f1203db80..c57618187 100644 --- a/src/backends/routers/gamespy/game_traffic_relay.py +++ b/src/backends/routers/gamespy/game_traffic_relay.py @@ -1,21 +1,29 @@ -from fastapi import APIRouter +from fastapi import APIRouter, Request from backends.protocols.gamespy.game_traffic_relay.handlers import ( - GTRHeartBeatHandler, + GtrHeartBeatHandler, ) from backends.protocols.gamespy.game_traffic_relay.requests import ( - GTRHeartBeat, + GtrHeartBeatRequest, ) from backends.urls import GAME_TRAFFIC_RELAY +from frontends.gamespy.library.exceptions.general import UniSpyException router = APIRouter() +@router.get(f"{GAME_TRAFFIC_RELAY}/get_my_ip") +def get_my_ip(request: Request): + assert request.client + return {"ip": request.client.host} + + @router.post(f"{GAME_TRAFFIC_RELAY}/heartbeat") -def heartbeat(request: GTRHeartBeat): - handler = GTRHeartBeatHandler(request) +def heartbeat(heartbeat: GtrHeartBeatRequest): + + handler = GtrHeartBeatHandler(heartbeat) handler.handle() - return None + return handler.response if __name__ == "__main__": diff --git a/src/backends/routers/gamespy/natneg.py b/src/backends/routers/gamespy/natneg.py index 0622fe241..e7f1238c0 100644 --- a/src/backends/routers/gamespy/natneg.py +++ b/src/backends/routers/gamespy/natneg.py @@ -1,7 +1,7 @@ from fastapi import APIRouter from backends.protocols.gamespy.chat.requests import PingRequest -from backends.protocols.gamespy.natneg.handlers import ConnectHandler, InitHandler +from backends.protocols.gamespy.natneg.handlers import ConnectHandler, InitHandler, ReportHandler from backends.protocols.gamespy.natneg.requests import AddressCheckRequest, ConnectRequest, ErtAckRequest, InitRequest, ReportRequest from backends.urls import NATNEG @@ -33,14 +33,11 @@ def init(request: InitRequest): return handler.response -@router.post(f"{NATNEG}/PingHandler") -def ping(request: PingRequest): - raise NotImplementedError() - - @router.post(f"{NATNEG}/ReportHandler") def report(request: ReportRequest): - raise NotImplementedError() + handler = ReportHandler(request) + handler.handle() + return handler.response if __name__ == "__main__": diff --git a/src/backends/routers/gamespy/webservices.py b/src/backends/routers/gamespy/webservices.py index 05192a1c9..9da42826c 100644 --- a/src/backends/routers/gamespy/webservices.py +++ b/src/backends/routers/gamespy/webservices.py @@ -9,94 +9,110 @@ # Altas services -@router.post(f"{WEB_SERVICES}/Altas/CreateRecordHandler") +@router.post(f"{WEB_SERVICES}/CreateRecordHandler") def create_matchless_session(request): raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/Altas/CreateSessionHandler") +@router.post(f"{WEB_SERVICES}/CreateSessionHandler") def create_session(request): raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/Altas/SetReportIntentionHandler") +@router.post(f"{WEB_SERVICES}/SetReportIntentionHandler") def set_report_intention(request): raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/Altas/SubmitReportHandler") +@router.post(f"{WEB_SERVICES}/SubmitReportHandler") def submit_report(request): raise NotImplementedError() # Auth services -@router.post(f"{WEB_SERVICES}/Auth/LoginProfileHandler") +@router.post(f"{WEB_SERVICES}/LoginProfileHandler") def login_profile(request: LoginProfileRequest): handler = LoginProfileHandler(request) handler.handle() return handler.response -@router.post(f"{WEB_SERVICES}/Auth/LoginRemoteAuthHandler") +@router.post(f"{WEB_SERVICES}/LoginProfileWithGameIdHandler") +def login_profile_with_game_id(request: LoginProfileRequest): + return login_profile(request) + + +@router.post(f"{WEB_SERVICES}/LoginRemoteAuthHandler") def login_remote_auth(request: LoginRemoteAuthRequest): handler = LoginRemoteAuthHandler(request) handler.handle() return handler.response -@router.post(f"{WEB_SERVICES}/Auth/LoginUniqueNickHandler") +@router.post(f"{WEB_SERVICES}/LoginRemoteAuthWithGameIdHandler") +def login_remote_auth_with_game_id(request: LoginRemoteAuthRequest): + return login_remote_auth(request) + + +@router.post(f"{WEB_SERVICES}/LoginUniqueNickHandler") def login_uniquenick(request: LoginUniqueNickRequest): handler = LoginUniqueNickHandler(request) handler.handle() return handler.response +@router.post(f"{WEB_SERVICES}/LoginUniqueNickWithGameIdHandler") +def login_uniquenick_with_game_id(request: LoginUniqueNickRequest): + return login_uniquenick(request) + + # SAKE services -@router.post(f"{WEB_SERVICES}/Sake/CreateRecordHandler") +@router.post(f"{WEB_SERVICES}/CreateRecordHandler") def create_record(request: CreateRecordRequest): handler = CreateRecordHandler(request) handler.handle() return handler.response -@router.post(f"{WEB_SERVICES}/Sake/DeleteRecordHandler") +@router.post(f"{WEB_SERVICES}/DeleteRecordHandler") def delete_record(request): raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/Sake/GetMyRecordsHandler") +@router.post(f"{WEB_SERVICES}/GetMyRecordsHandler") def get_my_records(request: GetMyRecordsRequest): handler = GetMyRecordsHandler(request) handler.handle() return handler.response -@router.post(f"{WEB_SERVICES}/Sake/GetRandomRecordsHandler") +@router.post(f"{WEB_SERVICES}/GetRandomRecordsHandler") def get_random_records(request): raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/Sake/GetRecordLimitHandler") +@router.post(f"{WEB_SERVICES}/GetRecordLimitHandler") def get_record_limit(request): raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/Sake/RateRecordHandler") +@router.post(f"{WEB_SERVICES}/RateRecordHandler") def rate_record(request): raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/Sake/SearchForRecordsHandler") +@router.post(f"{WEB_SERVICES}/SearchForRecordsHandler") def search_for_records(request: SearchForRecordsRequest): handler = SearchForRecordsHandler(request) handler.handle() return handler.response -@router.post(f"{WEB_SERVICES}/Sake/UpdateRecordHandler") +@router.post(f"{WEB_SERVICES}/UpdateRecordHandler") def update_record(request): raise NotImplementedError() + if __name__ == "__main__": import uvicorn from fastapi import FastAPI diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index d190bfce4..3967a6151 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -11,6 +11,7 @@ from frontends.gamespy.library.configs import ServerConfig from backends.routers.gamespy import ( chat, + game_traffic_relay, gstats, natneg, presence_connection_manager, @@ -24,6 +25,7 @@ app.include_router(chat.router) app.include_router(gstats.router) +app.include_router(game_traffic_relay.router) app.include_router(natneg.router) app.include_router(presence_connection_manager.router) app.include_router(presence_search_player.router) @@ -75,4 +77,5 @@ def get_auth_token(request: RegisterRequest): if __name__ == "__main__": - uvicorn.run("backends.routers.home:app", host="127.0.0.1", port=8080, reload=True) + uvicorn.run("backends.routers.home:app", + host="127.0.0.1", port=8080, reload=True) diff --git a/src/backends/tests/gamespy/natneg/nat_detection_tests.py b/src/backends/tests/gamespy/natneg/nat_detection_tests.py index 3ad615cb7..c92d208d9 100644 --- a/src/backends/tests/gamespy/natneg/nat_detection_tests.py +++ b/src/backends/tests/gamespy/natneg/nat_detection_tests.py @@ -24,7 +24,6 @@ def test_public_ip(self): public_port=1, private_ip="10.0.0.1", private_port=0, - update_time=datetime.now(), ), InitPacketCaches( port_type=NatPortType.NN1, @@ -36,7 +35,6 @@ def test_public_ip(self): public_port=2, private_ip="10.0.0.1", private_port=0, - update_time=datetime.now(), ), InitPacketCaches( port_type=NatPortType.NN2, @@ -48,7 +46,6 @@ def test_public_ip(self): public_port=2, private_ip="10.0.0.1", private_port=2, - update_time=datetime.now(), ), InitPacketCaches( port_type=NatPortType.NN3, @@ -60,7 +57,6 @@ def test_public_ip(self): public_port=2, private_ip="10.0.0.1", private_port=2, - update_time=datetime.now(), ), ] @@ -68,7 +64,7 @@ def test_public_ip(self): init_info = NatProtocolHelper(packet_caches) # Determine NAT type - NatProtocolHelper._determine_nat_type_version3(init_info) + NatProtocolHelper._determine_nat_type_version4(init_info) # Assert that the NAT type is NoNat self.assertEqual(init_info.nat_type, NatType.NO_NAT) @@ -86,7 +82,7 @@ def test_full_cone(self): public_port=1, private_ip="192.168.1.1", private_port=0, - update_time=datetime.now(), + ), InitPacketCaches( port_type=NatPortType.NN1, @@ -98,7 +94,7 @@ def test_full_cone(self): public_port=2, private_ip="192.168.1.1", private_port=0, - update_time=datetime.now(), + ), InitPacketCaches( port_type=NatPortType.NN2, @@ -110,7 +106,7 @@ def test_full_cone(self): public_port=2, private_ip="192.168.1.1", private_port=2, - update_time=datetime.now(), + ), InitPacketCaches( port_type=NatPortType.NN3, @@ -122,7 +118,7 @@ def test_full_cone(self): public_port=2, private_ip="192.168.1.1", private_port=2, - update_time=datetime.now(), + ), ] @@ -130,7 +126,7 @@ def test_full_cone(self): init_info = NatProtocolHelper(packet_caches) # Determine NAT type - NatProtocolHelper._determine_nat_type_version3(init_info) + NatProtocolHelper._determine_nat_type_version4(init_info) self.assertEqual(init_info.nat_type, NatType.FULL_CONE) # Test method @@ -187,7 +183,7 @@ def test_symmetric(self): init_info = NatProtocolHelper(packet_caches) # Determine NAT type - NatProtocolHelper._determine_nat_type_version3(init_info) + NatProtocolHelper._determine_nat_type_version4(init_info) # Assert the NAT type is symmetric self.assertEqual(init_info.nat_type, NatType.SYMMETRIC) @@ -245,7 +241,7 @@ def test_sposirius_network(self): init_info = NatProtocolHelper(packet_caches) # Determine NAT type - NatProtocolHelper._determine_nat_type_version3(init_info) + NatProtocolHelper._determine_nat_type_version4(init_info) # Assert the NAT type is symmetric self.assertEqual(init_info.nat_type, NatType.SYMMETRIC) diff --git a/src/backends/tests/gamespy/web/__init__.py b/src/backends/tests/gamespy/web/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/tests/gamespy/web/handler_tests.py b/src/backends/tests/gamespy/web/handler_tests.py new file mode 100644 index 000000000..deed3ed58 --- /dev/null +++ b/src/backends/tests/gamespy/web/handler_tests.py @@ -0,0 +1,17 @@ +import unittest + +from backends.protocols.gamespy.web_services.handlers import LoginRemoteAuthHandler +from backends.protocols.gamespy.web_services.requests import LoginRemoteAuthRequest + + +class HandlerTests(unittest.TestCase): + def test_sdk_login_remote_auth(self): + raw = {"raw_request": "1000GMTy13lsJmiY7L19ojyN3XTM08ll0C4EWWijwmJyq3ttiZmoDUQJ0OSnar9nQCu5MpOGvi4Z0EcC2uNaS4yKrUA+h+tTDDoJHF7ZjoWKOTj00yNOEdzWyG08cKdVQwFRkF+h8oG/Jd+Ik3sWviXq/+5bhZQ7iXxTbbDwNL6Lagp/pLZ9czLnYPhY7VEcoQlx9oOLH8c.DLe", + "version": 1, "partner_code": 0, "namespace_id": 0, "auth_token": "GMTy13lsJmiY7L19ojyN3XTM08ll0C4EWWijwmJyq3ttiZmoDUQJ0OSnar9nQCu5MpOGvi4Z0EcC2uNaS4yKrUA+h+tTDDoJHF7ZjoWKOTj00yNOEdzWyG08cKdVQwFRkF+h8oG/Jd+Ik3sWviXq/+5bhZQ7iXxTbbDwNL6Lagp/pLZ9czLnYPhY7VEcoQlx9oO", "challenge": "LH8c.DLe", "game_id": 0, "client_ip": "172.19.0.4", "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "client_port": 57502} + request = LoginRemoteAuthRequest.model_validate(raw) + handler = LoginRemoteAuthHandler(request) + handler.handle() + handler.response + + def test_sdk_login_uniquenick(self): + raw = {} diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index d017a164f..ef0b710fd 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -90,7 +90,8 @@ def _launch_server(self) -> None: assert self.logger is not None assert self._server_cls is not None assert self._client_cls is not None - self.server = self._server_cls(self.config, self._client_cls, self.logger) + self.server = self._server_cls( + self.config, self._client_cls, self.logger) self.server.start() @final @@ -102,12 +103,9 @@ def _heartbeat_to_backend(self, url: str, json_str: str): try: # post our server config to backends to register resp = requests.post(url=url, data=json_str) - if resp.status_code == 200: - data = resp.json() - if data["status"] != "online": - raise UniSpyException( - f"backend server: {CONFIG.backend.url} not available." - ) + if resp.status_code != 200: + message = resp.json() + raise UniSpyException(message["error"]) except requests.ConnectionError: raise UniSpyException( f"backend server: {CONFIG.backend.url} not available." @@ -120,7 +118,8 @@ def _connect_to_backend(self): assert self.config is not None if CONFIG.unittest.is_collect_request: return - self._heartbeat_to_backend(CONFIG.backend.url, self.config.model_dump_json()) + self._heartbeat_to_backend( + CONFIG.backend.url, self.config.model_dump_json()) @final def _launch_heartbeat_schedular(self): diff --git a/src/frontends/gamespy/library/network/http_handler.py b/src/frontends/gamespy/library/network/http_handler.py index cbfc2d59e..3c633eb22 100644 --- a/src/frontends/gamespy/library/network/http_handler.py +++ b/src/frontends/gamespy/library/network/http_handler.py @@ -45,14 +45,12 @@ def send(self, data: HttpResponse) -> None: class HttpHandler(BaseHTTPRequestHandler): conn: HttpConnection - + def do_POST(self) -> None: # parsed_url = urlparse(self.path).geturl() content_length = int(self.headers["Content-Length"]) data = self.rfile.read(content_length).decode() - # request = HttpRequest(parsed_url, dict(self.headers), data) - if self.conn is None: - self.conn = HttpConnection(self, *self.server.unispy_params) # type: ignore + self.conn = HttpConnection(self, *self.server.unispy_params) # type: ignore self.conn.on_received(data.encode()) diff --git a/src/frontends/gamespy/library/network/tcp_handler.py b/src/frontends/gamespy/library/network/tcp_handler.py index 076621a03..69dd76c0d 100644 --- a/src/frontends/gamespy/library/network/tcp_handler.py +++ b/src/frontends/gamespy/library/network/tcp_handler.py @@ -30,11 +30,11 @@ def disconnect(self) -> None: class TcpHandler(socketserver.BaseRequestHandler): request: socket.socket - conn: TcpConnection | None = None + conn: TcpConnection def handle(self) -> None: - if self.conn is None: - self.conn = TcpConnection(self, *self.server.unispy_params) # type: ignore + self.conn = TcpConnection( + self, *self.server.unispy_params) # type: ignore self.conn.on_connected() while True: try: @@ -51,7 +51,7 @@ def handle(self) -> None: class TcpServer(NetworkServerBase): - + def __init__( self, config: ServerConfig, t_client: type[ClientBase], logger: LogWriter ) -> None: @@ -62,7 +62,8 @@ def __init__( bind_and_activate=False ) self._server.allow_reuse_address = True # type:ignore - self._server.unispy_params = (self._config, self._client_cls, self._logger) # type: ignore + self._server.unispy_params = ( # type: ignore + self._config, self._client_cls, self._logger) self._server.server_bind() self._server.server_activate() diff --git a/src/frontends/gamespy/protocols/chat/contracts/results.py b/src/frontends/gamespy/protocols/chat/contracts/results.py index 0fa7ff053..16d3570e4 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/results.py +++ b/src/frontends/gamespy/protocols/chat/contracts/results.py @@ -20,8 +20,8 @@ class ListInfo(BaseModel): channel_name: str total_channel_user: int channel_topic: str - invoker_nick_name:str - invoker_user_name:str + invoker_nick_name: str + invoker_user_name: str channel_info_list: list[ListInfo] = [] """(channel_name:str,total_channel_user:int,channel_topic:str)""" @@ -143,6 +143,12 @@ class SetChannelKeyResult(ResultBase): channel_name: str +class SetCKeyResult(ResultBase): + setter_nick_name: str + setter_user_name: str + channel_name: str + + if __name__ == "__main__": dd = { "infos": [ @@ -168,5 +174,6 @@ class NoticeResult(MessageResultBase): class PrivateResult(MessageResultBase): pass + class UtmResult(MessageResultBase): pass diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py index a38eea152..c9dad5ded 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py @@ -1,24 +1,46 @@ from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.library.network.udp_handler import UdpServer from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client from frontends.gamespy.protocols.game_traffic_relay.contracts.general import ( GtrHeartbeat, ) +import requests class ServerLauncher(ServerLauncherBase): + _public_ip: str + def __init__(self) -> None: super().__init__( config_name="GameTrafficRelay", client_cls=Client, server_cls=UdpServer, ) + self._get_public_ip() + + def _get_public_ip(self): + try: + # post our server config to backends to register + resp = requests.get( + url=f"{CONFIG.backend.url}/GameSpy/GameTrafficRelay/get_my_ip") + data = resp.json() + if resp.status_code == 200: + self._public_ip = data['ip'] + print(f"GameTrafficRelay public ip: {self._public_ip}") + else: + raise UniSpyException(data["error"]) + except requests.ConnectionError: + raise UniSpyException( + f"backend server: {CONFIG.backend.url} not available." + ) + def _gtr_heartbeat(self): assert self.config req = GtrHeartbeat( server_id=self.config.server_id, - public_ip_address=self.config.public_address, + public_ip_address=self._public_ip, public_port=self.config.listening_port, client_count=len(Client.client_pool), ) @@ -41,7 +63,6 @@ def _connect_to_backend(self): self._gtr_heartbeat() - if __name__ == "__main__": s = ServerLauncher() - s.start() \ No newline at end of file + s.start() diff --git a/src/frontends/gamespy/protocols/natneg/abstractions/handlers.py b/src/frontends/gamespy/protocols/natneg/abstractions/handlers.py index 0f42c43fe..151636a69 100644 --- a/src/frontends/gamespy/protocols/natneg/abstractions/handlers.py +++ b/src/frontends/gamespy/protocols/natneg/abstractions/handlers.py @@ -9,20 +9,6 @@ def __init__(self, client: Client, request: RequestBase) -> None: assert isinstance(client, Client) assert issubclass(type(request), RequestBase) - def handle(self) -> None: - try: - # we first log this class - self._log_current_class() - # then we handle it - self._request_check() - self._response_construct() - # first send the response - if self._response is not None: - self._response_send() - # then send to backends - self._data_operate() - except Exception as ex: - self._handle_exception(ex) if __name__ == "__main__": diff --git a/src/frontends/gamespy/protocols/natneg/aggregations/enums.py b/src/frontends/gamespy/protocols/natneg/aggregations/enums.py index eae9c7376..b88e8ef72 100644 --- a/src/frontends/gamespy/protocols/natneg/aggregations/enums.py +++ b/src/frontends/gamespy/protocols/natneg/aggregations/enums.py @@ -35,7 +35,7 @@ class ResponseType(Enum): class ConnectPacketStatus(Enum): NO_ERROR = 0 - BEAD_HEART_BEAT = 1 + BAD_HEART_BEAT = 1 INIT_PACKET_TIMEOUT = 2 diff --git a/src/frontends/gamespy/protocols/natneg/applications/handlers.py b/src/frontends/gamespy/protocols/natneg/applications/handlers.py index 47b09ab80..637a50762 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/handlers.py +++ b/src/frontends/gamespy/protocols/natneg/applications/handlers.py @@ -1,4 +1,5 @@ from frontends.gamespy.protocols.natneg.abstractions.handlers import CmdHandlerBase +from frontends.gamespy.protocols.natneg.aggregations.enums import RequestType from frontends.gamespy.protocols.natneg.applications.client import Client from frontends.gamespy.protocols.natneg.contracts.requests import ( AddressCheckRequest, @@ -59,6 +60,11 @@ def __init__(self, client: Client, request: ConnectAckRequest) -> None: assert isinstance(request, ConnectAckRequest) super().__init__(client, request) self._is_fetching = False + self._is_uploading = False + + def _response_construct(self) -> None: + self._client.log_info( + f"client: {self._request.client_index} is received the connect packet.") class ConnectHandler(CmdHandlerBase): @@ -72,6 +78,10 @@ def __init__(self, client: Client, request: ConnectRequest) -> None: self._result_cls = ConnectResult def _response_construct(self) -> None: + if not self._result.is_both_client_ready: + self._client.log_warn( + f"init cache is not enough for cookie: {self._request.cookie}") + return self._response = ConnectResponse(self._request, self._result) @@ -104,6 +114,7 @@ class InitHandler(CmdHandlerBase): _request: InitRequest _result: InitResult _response: InitResponse + _client: Client def __init__(self, client: Client, request: InitRequest) -> None: assert isinstance(request, InitRequest) @@ -121,6 +132,36 @@ def _response_construct(self): self._response = InitResponse(self._request, self._result) + def handle(self) -> None: + try: + # we first log this class + self._log_current_class() + # then we handle it + self._request_check() + self._response_construct() + # first send the response + if self._response is None: + return + self._response_send() + # then send to backends + self._data_operate() + self._invoke_connect() + except Exception as ex: + self._handle_exception(ex) + + def _invoke_connect(self) -> None: + connect_raw = ConnectRequest.build( + version=self._request.version, + command_name=RequestType.CONNECT, + cookie=self._request.cookie, + port_type=self._request.port_type, + client_index=self._request.client_index, + use_game_port=self._request.use_game_port + ) + request = ConnectRequest(connect_raw) + handler = ConnectHandler(self._client, request) + handler.handle() + class NatifyHandler(CmdHandlerBase): _request: NatifyRequest diff --git a/src/frontends/gamespy/protocols/natneg/applications/switcher.py b/src/frontends/gamespy/protocols/natneg/applications/switcher.py index 191f6dc65..b41d51a58 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/switcher.py +++ b/src/frontends/gamespy/protocols/natneg/applications/switcher.py @@ -4,6 +4,7 @@ from frontends.gamespy.protocols.natneg.applications.client import Client from frontends.gamespy.protocols.natneg.contracts.requests import ( AddressCheckRequest, + ConnectAckRequest, ConnectRequest, ErtAckRequest, InitRequest, @@ -13,6 +14,7 @@ from frontends.gamespy.protocols.natneg.aggregations.enums import RequestType from frontends.gamespy.protocols.natneg.applications.handlers import ( AddressCheckHandler, + ConnectAckHandler, ConnectHandler, ErtAckHandler, InitHandler, @@ -52,6 +54,8 @@ def _create_cmd_handlers( return ErtAckHandler(self._client, ErtAckRequest(raw_request)) case RequestType.CONNECT: return ConnectHandler(self._client, ConnectRequest(raw_request)) + case RequestType.CONNECT_ACK: + return ConnectAckHandler(self._client, ConnectAckRequest(raw_request)) case RequestType.ADDRESS_CHECK: return AddressCheckHandler( self._client, AddressCheckRequest(raw_request) diff --git a/src/frontends/gamespy/protocols/natneg/contracts/requests.py b/src/frontends/gamespy/protocols/natneg/contracts/requests.py index 3afc2898d..7dffd556b 100644 --- a/src/frontends/gamespy/protocols/natneg/contracts/requests.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/requests.py @@ -5,6 +5,7 @@ # from frontends.gamespy.library.extentions.string_extentions import IPEndPoint from frontends.gamespy.protocols.natneg.abstractions.contracts import ( + MAGIC_DATA, CommonRequestBase, RequestBase, ) @@ -26,7 +27,8 @@ class PingRequest(RequestBase): def parse(self) -> None: self.version = int(self.raw_request[6]) self.command_name = RequestType(self.raw_request[7]) - self.cookie = int.from_bytes(self.raw_request[8:12], byteorder="little") + self.cookie = int.from_bytes( + self.raw_request[8:12], byteorder="little") self.ip = socket.inet_ntoa(self.raw_request[12:16]) self.port = int.from_bytes(self.raw_request[16:18]) # port here is not in little endian @@ -49,6 +51,18 @@ class ConnectRequest(CommonRequestBase): client_index: NatClientIndex + @staticmethod + def build(version: int, command_name: RequestType, cookie: int, port_type: NatPortType, client_index: NatClientIndex, use_game_port: bool) -> bytes: + data = bytes() + data += MAGIC_DATA + data += version.to_bytes(1) + data += command_name.value.to_bytes(1) + data += cookie.to_bytes(4) + data += port_type.value.to_bytes(1) + data += client_index.value.to_bytes(1) + data += use_game_port.to_bytes(1) + return data + class ErtAckRequest(CommonRequestBase): pass @@ -94,7 +108,7 @@ def parse(self) -> None: class ReportRequest(CommonRequestBase): - is_nat_success: bool = False + is_nat_success: bool game_name: str nat_type: NatType mapping_scheme: NatPortMappingScheme @@ -113,4 +127,4 @@ def parse(self): self.mapping_scheme = NatPortMappingScheme(self.raw_request[17]) end_index = self.raw_request[23:].index(0) - self.game_name = self.raw_request[23 : 23 + end_index].decode("ascii") + self.game_name = self.raw_request[23: 23 + end_index].decode("ascii") diff --git a/src/frontends/gamespy/protocols/natneg/contracts/responses.py b/src/frontends/gamespy/protocols/natneg/contracts/responses.py index 2270ed6e5..0334f04e1 100644 --- a/src/frontends/gamespy/protocols/natneg/contracts/responses.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/responses.py @@ -67,11 +67,14 @@ class ConnectResponse(ResponseBase): _request: ConnectRequest def build(self) -> None: + assert self._result.ip is not None + assert self._result.port is not None + assert self._result.status is not None super().build() data = bytes() data += self.sending_buffer data += socket.inet_aton(self._result.ip) data += self._result.port.to_bytes(2)[::-1] data += self._result.got_your_data - data += self._result.finished.value.to_bytes(1) + data += self._result.status.value.to_bytes(1) self.sending_buffer = data diff --git a/src/frontends/gamespy/protocols/natneg/contracts/results.py b/src/frontends/gamespy/protocols/natneg/contracts/results.py index 94f14626f..6a16db179 100644 --- a/src/frontends/gamespy/protocols/natneg/contracts/results.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/results.py @@ -11,12 +11,11 @@ class AddressCheckResult(CommonResultBase): class ConnectResult(ResultBase): + is_both_client_ready: bool got_your_data: bytes = bytes([1]) - finished: ConnectPacketStatus = ConnectPacketStatus.NO_ERROR - ip: str - port: int - version: int - cookie: int + status: ConnectPacketStatus | None + ip: str | None + port: int | None packet_type: ResponseType = ResponseType.CONNECT diff --git a/src/frontends/gamespy/protocols/web_services/applications/switcher.py b/src/frontends/gamespy/protocols/web_services/applications/switcher.py index b86250cd0..e3b05176a 100644 --- a/src/frontends/gamespy/protocols/web_services/applications/switcher.py +++ b/src/frontends/gamespy/protocols/web_services/applications/switcher.py @@ -26,10 +26,10 @@ def _process_raw_request(self) -> None: name_node = ET.fromstring(self._raw_request)[0][0] if name_node is None: raise WebException("name node is missing from soap request") - if name_node.text is None: + if name_node.tag is None: raise WebException( "name node text field is missing from soap request") - name = name_node.text.split("}")[1] + name = name_node.tag.split("}")[1] if len(name) < 4: raise WebException("request name invalid") diff --git a/src/frontends/tests/gamespy/game_traffic_relay/handler_tests.py b/src/frontends/tests/gamespy/game_traffic_relay/handler_tests.py index 181fba1a6..b129907a5 100644 --- a/src/frontends/tests/gamespy/game_traffic_relay/handler_tests.py +++ b/src/frontends/tests/gamespy/game_traffic_relay/handler_tests.py @@ -29,7 +29,7 @@ def test_ping(self): handler.handle() # cookie length check self.assertEqual(len(client1.listener.pool), 1) - list(client1.listener.pool.values())[0] - self.assertEqual(len(list(client1.listener.pool.values())[0]), 2) + clients = list(client1.listener.pool.values())[0] + self.assertEqual(clients, 2) client1.on_received(ping_raw) pass diff --git a/src/redis_test.py b/src/tests/redis_test.py similarity index 54% rename from src/redis_test.py rename to src/tests/redis_test.py index 5f784cca6..4fb912d7a 100644 --- a/src/redis_test.py +++ b/src/tests/redis_test.py @@ -16,17 +16,17 @@ # client.set("hello", "hi") # data = client.get("hello") # pass -import socket +# import socket -SERVER_IP = "127.0.0.1" # change to target IP if needed -SERVER_PORT = 27901 -MESSAGE = b"hello" +# SERVER_IP = "127.0.0.1" # change to target IP if needed +# SERVER_PORT = 27901 +# MESSAGE = b"hello" -sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) +# sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) -try: - for _ in range(10): - sock.sendto(MESSAGE, (SERVER_IP, SERVER_PORT)) - print(f"Sent {MESSAGE!r} to {SERVER_IP}:{SERVER_PORT}") -finally: - sock.close() \ No newline at end of file +# try: +# for _ in range(10): +# sock.sendto(MESSAGE, (SERVER_IP, SERVER_PORT)) +# print(f"Sent {MESSAGE!r} to {SERVER_IP}:{SERVER_PORT}") +# finally: +# sock.close() \ No newline at end of file From 461d76170cc0fc33a80975d274c1b0296b4d4702 Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Wed, 24 Sep 2025 07:15:02 +0000 Subject: [PATCH 205/231] Update: simplify object creation --- common/UniSpy_pg.sql | 4 +- src/.devcontainer/devcontainer.json | 1 - .../protocols/gamespy/chat/handlers.py | 52 +++-- src/backends/protocols/gamespy/chat/helper.py | 14 +- .../protocols/gamespy/chat/requests.py | 1 + .../protocols/gamespy/game_status/handlers.py | 18 +- .../protocols/gamespy/game_status/requests.py | 2 +- .../protocols/gamespy/natneg/handlers.py | 23 ++- .../protocols/gamespy/natneg/helpers.py | 36 ++-- .../presence_connection_manager/handlers.py | 17 +- .../presence_connection_manager/requests.py | 6 +- .../presence_search_player/handlers.py | 21 +- .../protocols/gamespy/query_report/data.py | 6 +- .../gamespy/server_browser/handlers.py | 6 +- .../gamespy/web_services/handlers.py | 9 + src/backends/routers/gamespy/chat.py | 11 +- .../routers/gamespy/game_traffic_relay.py | 1 - src/backends/routers/home.py | 2 +- .../gamespy/library/abstractions/contracts.py | 14 +- .../gamespy/library/abstractions/handler.py | 12 +- .../library/abstractions/server_launcher.py | 18 +- .../protocols/chat/abstractions/contract.py | 6 +- .../protocols/chat/abstractions/handler.py | 21 +- .../chat/aggregates/response_name.py | 57 ------ .../protocols/chat/applications/handlers.py | 71 +++---- .../protocols/chat/contracts/responses.py | 183 +++++++++--------- .../protocols/chat/contracts/results.py | 22 ++- .../game_status/abstractions/contracts.py | 1 + .../game_status/applications/handlers.py | 10 +- .../game_status/contracts/responses.py | 14 +- .../game_status/contracts/results.py | 2 + .../aggregates/exceptions.py | 5 + .../applications/server_launcher.py | 19 +- .../natneg/abstractions/contracts.py | 22 ++- .../natneg/aggregations/exceptions.py | 5 + .../protocols/natneg/applications/handlers.py | 31 ++- .../protocols/natneg/contracts/responses.py | 31 +-- .../abstractions/contracts.py | 8 +- .../applications/handlers.py | 37 ++-- .../contracts/requests.py | 9 +- .../contracts/responses.py | 70 ++++--- .../contracts/results.py | 12 +- .../applications/handlers.py | 24 +-- .../contracts/responses.py | 37 ++-- .../contracts/results.py | 1 + .../query_report/v1/abstractions/contracts.py | 5 +- .../query_report/v2/abstractions/contracts.py | 8 +- .../query_report/v2/applications/handlers.py | 29 ++- .../query_report/v2/applications/switcher.py | 6 +- .../query_report/v2/contracts/responses.py | 41 ++-- .../query_report/v2/contracts/results.py | 5 + .../v2/abstractions/contracts.py | 29 ++- .../v2/aggregations/exceptions.py | 5 + .../v2/applications/handlers.py | 6 +- .../v2/applications/switcher.py | 4 +- .../server_browser/v2/contracts/requests.py | 6 +- .../server_browser/v2/contracts/responses.py | 4 +- .../web_services/abstractions/contracts.py | 9 +- .../modules/auth/abstractions/general.py | 21 +- .../modules/auth/handlers/general.py | 16 +- .../modules/direct2game/handlers/general.py | 4 +- .../modules/sake/abstractions/generals.py | 4 +- .../tests/gamespy/chat/mock_objects.py | 42 +++- .../tests/gamespy/game_status/mock_objects.py | 14 +- .../game_traffic_relay/handler_tests.py | 2 +- .../tests/gamespy/natneg/handler_tests.py | 8 +- .../tests/gamespy/natneg/mock_objects.py | 11 +- .../mock_objects.py | 8 +- .../gamespy/query_report/mock_objects.py | 2 +- .../gamespy/server_browser/mock_objects.py | 1 + 70 files changed, 651 insertions(+), 611 deletions(-) delete mode 100644 src/frontends/gamespy/protocols/chat/aggregates/response_name.py create mode 100644 src/frontends/gamespy/protocols/game_traffic_relay/aggregates/exceptions.py create mode 100644 src/frontends/gamespy/protocols/natneg/aggregations/exceptions.py create mode 100644 src/frontends/gamespy/protocols/server_browser/v2/aggregations/exceptions.py diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 454e0f583..d8144d945 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -5334,7 +5334,7 @@ COPY unispy.messages (id, namespaceid, type, from_user, to_user, date, message) -- Data for Name: nat_result_caches; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.nat_result_caches (id, public_ip_address1, public_ip_address2, update_time) FROM stdin; +COPY unispy.nat_result_caches (id, public_ip, private_ip, is_success, port_mapping_type, port_type, client_index, game_name, update_time) FROM stdin; \. @@ -5369,7 +5369,7 @@ COPY unispy.pstorage (id, profileid, ptype, dindex, data) FROM stdin; -- Data for Name: relay_server_caches; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.relay_server_caches (server_id, public_ip_address, public_port, client_count) FROM stdin; +COPY unispy.relay_server_caches (id, public_ip, public_port, client_count, update_time) FROM stdin; \. diff --git a/src/.devcontainer/devcontainer.json b/src/.devcontainer/devcontainer.json index e18f6d8b7..5ce0d2e51 100644 --- a/src/.devcontainer/devcontainer.json +++ b/src/.devcontainer/devcontainer.json @@ -26,7 +26,6 @@ ], // Use 'postCreateCommand' to run commands after the container is created. "postCreateCommand": "pip3 install --user -r requirements.txt" - // "postCreateCommand": "pip3 install --user -r requirements.txt" // Configure tool-specific properties. // "customizations": {}, // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index d2eea7c2c..5a0f0ec79 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -16,6 +16,7 @@ ChannelRequestBase, CryptRequest, GetCKeyRequest, + GetChannelKeyRequest, GetKeyRequest, GetUdpRelayRequest, InviteRequest, @@ -40,7 +41,6 @@ WhoIsRequest, WhoRequest, ) -from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.protocols.chat.aggregates.enums import ( GetKeyRequestType, ModeRequestType, @@ -184,7 +184,7 @@ def _request_check(self) -> None: data.clean_expired_user_cache(self._session) self._get_user() if self._user is not None: - raise UniSpyException("user cache is trash in database") + raise ChatException("user cache is trash in database") def _data_operate(self) -> None: assert self._user is None @@ -202,7 +202,7 @@ def _data_operate(self) -> None: self._request.gamename, self._session ) if self._secret_key is None: - raise UniSpyException("game secret key not found in database.") + raise ChatException("game secret key not found in database.") def _result_construct(self) -> None: assert isinstance(self._secret_key, str) @@ -225,7 +225,9 @@ def _data_operate(self) -> None: def _result_construct(self) -> None: self._result = GetKeyResult( - nick_name=self._request.nick_name, values=self._values + nick_name=self._request.nick_name, + values=self._values, + cookie=self._request.cookie ) @@ -418,7 +420,9 @@ def _result_construct(self) -> None: for d in self._data: info = WhoResult.WhoInfo(**d) infos.append(info) - self._result = WhoResult(infos=infos) + self._result = WhoResult(infos=infos, + request_type=self._request.request_type, + channel_name=self._request.channel_name, nick_name=self._request.nick_name) class WhoIsHandler(HandlerBase): @@ -481,10 +485,12 @@ def _result_construct(self) -> None: self._result = JoinResult( joiner_nick_name=self._channel_user.nick_name, joiner_user_name=self._channel_user.user_name, + channel_name=self._request.channel_name ) class GetChannelKeyHandler(ChannelHandlerBase): + _request: GetChannelKeyRequest _values: list def _result_construct(self) -> None: @@ -499,6 +505,7 @@ def _result_construct(self) -> None: key_values=dict(self._channel.key_values), nick_name=self._user.nick_name, user_name=self._user.user_name, + cookie=self._request.cookie ) @@ -538,7 +545,9 @@ def _result_construct(self) -> None: infos.append(info) self._result = GetCKeyResult( - infos=infos, channel_name=self._request.channel_name + infos=infos, + channel_name=self._request.channel_name, + cookie=self._request.cookie ) @@ -582,6 +591,7 @@ def _result_construct(self) -> None: kicker_user_name=self._channel_user.user_name, kicker_nick_name=self._channel_user.nick_name, kickee_nick_name=self._request.kickee_nick_name, + reason=self._request.reason ) @@ -661,6 +671,7 @@ def _result_construct(self) -> None: leaver_user_name=self._channel_user.user_name, is_channel_creator=self._channel_user.is_channel_creator, channel_name=self._channel.channel_name, + reason=self._request.reason ) @@ -669,11 +680,12 @@ class SetChannelKeyHandler(ChannelHandlerBase): def _request_check(self) -> None: super()._request_check() - assert self._channel_user + assert self._channel is not None + assert self._channel_user is not None assert isinstance(self._channel_user.is_channel_operator, bool) if self._channel_user.is_channel_operator: - self._channel.key_values = self._request.key_values # type:ignore - self._session.commit() + ChannelHelper.update_channel_key_values( + self._request.key_values, self._channel, self._session) def _result_construct(self) -> None: assert self._channel_user @@ -684,6 +696,7 @@ def _result_construct(self) -> None: setter_nick_name=self._channel_user.nick_name, setter_user_name=self._channel_user.user_name, channel_name=self._request.channel_name, + key_value=self._request.key_values ) @@ -695,8 +708,8 @@ class SetCkeyHandler(ChannelHandlerBase): _request: SetCKeyRequest def _data_operate(self) -> None: + assert self._channel_user is not None self._channel_user.key_values = self._request.key_values # type:ignore - self._is_broadcast = True def _result_construct(self) -> None: assert self._channel_user @@ -706,6 +719,8 @@ def _result_construct(self) -> None: setter_nick_name=self._channel_user.nick_name, setter_user_name=self._channel_user.user_name, channel_name=self._request.channel_name, + key_value=self._request.key_values, + cookie=self._request.cookie ) @@ -743,7 +758,10 @@ def _result_construct(self) -> None: assert isinstance(self._user.user_name, str) self._result = AtmResult( - nick_name=self._user.nick_name, user_name=self._user.user_name + sender_nick_name=self._user.nick_name, + sender_user_name=self._user.user_name, + target_name=self._request.target_name, + message=self._request.message ) @@ -756,7 +774,9 @@ def _result_construct(self) -> None: assert isinstance(self._user.user_name, str) self._result = UtmResult( - nick_name=self._user.nick_name, user_name=self._user.user_name + sender_nick_name=self._user.nick_name, sender_user_name=self._user.user_name, + target_name=self._request.target_name, + message=self._request.message ) @@ -769,7 +789,9 @@ def _result_construct(self) -> None: assert isinstance(self._user.user_name, str) self._result = NoticeResult( - nick_name=self._user.nick_name, user_name=self._user.user_name + sender_nick_name=self._user.nick_name, sender_user_name=self._user.user_name, + target_name=self._request.target_name, + message=self._request.message ) @@ -782,5 +804,7 @@ def _result_construct(self) -> None: assert isinstance(self._user.user_name, str) self._result = PrivateResult( - nick_name=self._user.nick_name, user_name=self._user.user_name + sender_nick_name=self._user.nick_name, sender_user_name=self._user.user_name, + target_name=self._request.target_name, + message=self._request.message ) diff --git a/src/backends/protocols/gamespy/chat/helper.py b/src/backends/protocols/gamespy/chat/helper.py index 14e8dcc4f..aa7ed3976 100644 --- a/src/backends/protocols/gamespy/chat/helper.py +++ b/src/backends/protocols/gamespy/chat/helper.py @@ -138,7 +138,8 @@ def kick( f"kickee is not in channel: {channel.channel_name}" ) if not kicker.is_channel_operator: # type:ignore - raise BadChannelKeyException("kick failed, kicker is not channel operator") + raise BadChannelKeyException( + "kick failed, kicker is not channel operator") session.delete(kickee) session.commit() @@ -288,7 +289,8 @@ def change_modes( def get_all_user_nick_string(channel: ChatChannelCaches, session: Session) -> str: assert isinstance(channel, ChatChannelCaches) assert isinstance(channel.channel_name, str) - users = data.get_channel_user_caches_by_name(channel.channel_name, session) + users = data.get_channel_user_caches_by_name( + channel.channel_name, session) nicks = "" for user in users: assert isinstance(user.is_channel_creator, bool) @@ -308,5 +310,11 @@ def get_channel_all_nicks( ) -> list[ChatChannelUserCaches]: assert channel is not None assert isinstance(channel.channel_name, str) - users = data.get_channel_user_caches_by_name(channel.channel_name, session) + users = data.get_channel_user_caches_by_name( + channel.channel_name, session) return users + + @staticmethod + def update_channel_key_values(kv: dict, channel: ChatChannelCaches, session: Session): + channel.key_values = kv # type:ignore + session.commit() diff --git a/src/backends/protocols/gamespy/chat/requests.py b/src/backends/protocols/gamespy/chat/requests.py index 9744e3278..ff81bbd92 100644 --- a/src/backends/protocols/gamespy/chat/requests.py +++ b/src/backends/protocols/gamespy/chat/requests.py @@ -189,6 +189,7 @@ class TopicRequest(ChannelRequestBase): class MessageRequestBase(ChannelRequestBase): type: MessageType + target_name: str message: str diff --git a/src/backends/protocols/gamespy/game_status/handlers.py b/src/backends/protocols/gamespy/game_status/handlers.py index ec18b40fb..21bc21ae4 100644 --- a/src/backends/protocols/gamespy/game_status/handlers.py +++ b/src/backends/protocols/gamespy/game_status/handlers.py @@ -27,7 +27,8 @@ def _data_operate(self) -> None: self.session_key = "11111" def _result_construct(self) -> None: - self._result = AuthGameResult(session_key=self.session_key) + self._result = AuthGameResult( + session_key=self.session_key, local_id=self._request.local_id, game_name=self._request.game_name) class AuthPlayerHandler(HandlerBase): @@ -36,7 +37,8 @@ class AuthPlayerHandler(HandlerBase): def _data_operate(self): match self._request.auth_type: case AuthMethod.PARTNER_ID_AUTH: - self.data = data.get_profile_id_by_token(token=self._request.auth_token) + self.data = data.get_profile_id_by_token( + token=self._request.auth_token) case AuthMethod.PROFILE_ID_AUTH: self.data = data.get_profile_id_by_profile_id( profile_id=self._request.profile_id @@ -49,7 +51,8 @@ def _data_operate(self): raise GSException("Invalid auth type") def _result_construct(self): - self._result = AuthPlayerResult(profile_id=self.data) + self._result = AuthPlayerResult( + profile_id=self.data, local_id=self._request.local_id) class GetPlayerDataHandler(HandlerBase): @@ -63,7 +66,10 @@ def _data_operate(self): ) def _result_construct(self): - self._result = GetPlayerDataResult(keyvalues=self.data) + self._result = GetPlayerDataResult( + keyvalues=self.data, + local_id=self._request.local_id, + profile_id=self._request.profile_id) class GetProfileIdHandler(HandlerBase): @@ -75,7 +81,9 @@ def _data_operate(self): ) def _result_construct(self): - self._result = GetProfileIdResult(profile_id=self.data) + self._result = GetProfileIdResult( + profile_id=self.data, + local_id=self._request.local_id) class NewGameHandler(HandlerBase): diff --git a/src/backends/protocols/gamespy/game_status/requests.py b/src/backends/protocols/gamespy/game_status/requests.py index 09bc722f3..b21ed2242 100644 --- a/src/backends/protocols/gamespy/game_status/requests.py +++ b/src/backends/protocols/gamespy/game_status/requests.py @@ -8,7 +8,7 @@ class RequestBase(lib.RequestBase): raw_request: str - local_id: int | None = None + local_id: int request_dict: dict[str, str] diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py index 3657d392a..fd0b78a18 100644 --- a/src/backends/protocols/gamespy/natneg/handlers.py +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -5,11 +5,11 @@ import backends.protocols.gamespy.natneg.data as data from backends.protocols.gamespy.natneg.helpers import NatProtocolHelper, NatStrategy from backends.protocols.gamespy.natneg.requests import ConnectRequest, InitRequest, ReportRequest -from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.protocols.natneg.aggregations.enums import ( ConnectPacketStatus, NatClientIndex, ) +from frontends.gamespy.protocols.natneg.aggregations.exceptions import NatNegException from frontends.gamespy.protocols.natneg.contracts.results import ConnectResult @@ -105,7 +105,9 @@ def _result_construct(self) -> None: is_both_client_ready=False, ip=None, port=None, - status=None + status=None, + version=self._request.version, + cookie=self._request.cookie, ) return @@ -114,14 +116,18 @@ def _result_construct(self) -> None: is_both_client_ready=True, ip=self._helper2.private_ip, port=self._helper2.private_port, - status=ConnectPacketStatus.NO_ERROR + status=ConnectPacketStatus.NO_ERROR, + version=self._request.version, + cookie=self._request.cookie, ) elif self._strategy == NatStrategy.USE_PUBLIC_IP: self._result = ConnectResult( is_both_client_ready=True, ip=self._helper2.public_ip, port=self._helper2.public_port, - status=ConnectPacketStatus.NO_ERROR + status=ConnectPacketStatus.NO_ERROR, + version=self._request.version, + cookie=self._request.cookie, ) elif self._strategy == NatStrategy.USE_GAME_TRAFFIC_RALEY: @@ -129,6 +135,9 @@ def _result_construct(self) -> None: relay_servers = data.get_game_traffic_relay_servers( self._session, 5) # select strategy to choose one gtr server + if len(relay_servers) == 0: + raise NatNegException( + "No relay server found, please check the database") rs = relay_servers[0] assert isinstance(rs.public_ip, str) assert isinstance(rs.public_port, int) @@ -136,7 +145,9 @@ def _result_construct(self) -> None: is_both_client_ready=True, ip=rs.public_ip, port=rs.public_port, - status=ConnectPacketStatus.NO_ERROR + status=ConnectPacketStatus.NO_ERROR, + version=self._request.version, + cookie=self._request.cookie, ) @@ -151,7 +162,7 @@ def _data_operate(self) -> None: self._session ) if init_cache is None: - raise UniSpyException( + raise NatNegException( f"No init package found for report pacakge cookie: {self._request.cookie} client_index: {self._request.client_index}") report_cache = NatResultCaches( cookie=self._request.cookie, diff --git a/src/backends/protocols/gamespy/natneg/helpers.py b/src/backends/protocols/gamespy/natneg/helpers.py index 66b2f9926..9affa94bc 100644 --- a/src/backends/protocols/gamespy/natneg/helpers.py +++ b/src/backends/protocols/gamespy/natneg/helpers.py @@ -1,12 +1,12 @@ import enum from backends.library.database.pg_orm import InitPacketCaches -from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.protocols.natneg.aggregations.enums import ( NatClientIndex, NatPortMappingScheme, NatPortType, NatType, ) +from frontends.gamespy.protocols.natneg.aggregations.exceptions import NatNegException class NatStrategy(enum.IntEnum): @@ -36,7 +36,7 @@ class NatProtocolHelper: def __init__(self, init_caches: list[InitPacketCaches]) -> None: if len(init_caches) < 3: - raise UniSpyException( + raise NatNegException( "init cache length not enough for NAT determination") self.nat_type = NatType.NO_NAT self.port_mapping = NatPortMappingScheme.CONSISTENT_PORT @@ -64,10 +64,10 @@ def __init__(self, init_caches: list[InitPacketCaches]) -> None: self.private_port = last_address_info.private_port if self.version not in NatNegVersion: - raise UniSpyException("Unknown natneg version") + raise NatNegException("Unknown natneg version") version = NatNegVersion(self.version) if version == NatNegVersion.VERSION2: - raise UniSpyException("Version 1 not implemented") + raise NatNegException("Version 1 not implemented") elif version == NatNegVersion.VERSION3: self._validate_version3() NatProtocolHelper._determine_nat_type_version3(self) @@ -80,7 +80,7 @@ def _validate_version3(self): NatPortType.NN1 in self.address_infos and NatPortType.NN2 in self.address_infos ): - raise UniSpyException("Incomplete init packets") + raise NatNegException("Incomplete init packets") assert isinstance(self.address_infos[NatPortType.NN1].cookie, int) assert isinstance(self.address_infos[NatPortType.NN2].cookie, int) @@ -88,17 +88,17 @@ def _validate_version3(self): self.address_infos[NatPortType.NN1].cookie != self.address_infos[NatPortType.NN2].cookie ): # type: ignore - raise UniSpyException("Broken cookie") + raise NatNegException("Broken cookie") if ( self.address_infos[NatPortType.NN1].version != self.address_infos[NatPortType.NN2].version ): # type: ignore - raise UniSpyException("Broken version") + raise NatNegException("Broken version") if ( self.address_infos[NatPortType.NN1].client_index != self.address_infos[NatPortType.NN2].client_index ): # type: ignore - raise UniSpyException("Broken client index") + raise NatNegException("Broken client index") if NatPortType.GP in self.address_infos: if ( @@ -111,7 +111,7 @@ def _validate_version3(self): or self.address_infos[NatPortType.GP].use_game_port != self.address_infos[NatPortType.NN1].use_game_port ): # type: ignore - raise UniSpyException("GP packet info is not correct") + raise NatNegException("GP packet info is not correct") def _validate_version4(self): # TODO: some games will not send GP packet to NAT negotiation server; currently, the reason is unknown and requires more games for analysis. @@ -122,7 +122,7 @@ def _validate_version4(self): and NatPortType.NN2 in self.address_infos and NatPortType.NN3 in self.address_infos ): - raise UniSpyException("Incomplete init packets") + raise NatNegException("Incomplete init packets") if ( self.address_infos[NatPortType.NN1].cookie @@ -130,7 +130,7 @@ def _validate_version4(self): or self.address_infos[NatPortType.NN1].cookie != self.address_infos[NatPortType.NN3].cookie ): # type: ignore - raise UniSpyException("Broken cookie") + raise NatNegException("Broken cookie") if ( self.address_infos[NatPortType.NN1].version @@ -138,7 +138,7 @@ def _validate_version4(self): or self.address_infos[NatPortType.NN1].version != self.address_infos[NatPortType.NN3].version ): # type: ignore - raise UniSpyException("Broken version") + raise NatNegException("Broken version") if ( self.address_infos[NatPortType.NN1].client_index @@ -146,7 +146,7 @@ def _validate_version4(self): or self.address_infos[NatPortType.NN1].client_index != self.address_infos[NatPortType.NN3].client_index ): # type: ignore - raise UniSpyException("Broken client index") + raise NatNegException("Broken client index") if ( self.address_infos[NatPortType.NN1].use_game_port @@ -154,13 +154,13 @@ def _validate_version4(self): or self.address_infos[NatPortType.NN1].use_game_port != self.address_infos[NatPortType.NN3].use_game_port ): # type: ignore - raise UniSpyException("Broken use game port") + raise NatNegException("Broken use game port") if ( self.address_infos[NatPortType.NN2].private_ip != self.address_infos[NatPortType.NN3].private_ip ): # type: ignore - raise UniSpyException("Client is sending wrong init packet.") + raise NatNegException("Client is sending wrong init packet.") if NatPortType.GP in self.address_infos: if ( @@ -173,12 +173,12 @@ def _validate_version4(self): or self.address_infos[NatPortType.GP].use_game_port != self.address_infos[NatPortType.NN1].use_game_port ): # type: ignore - raise UniSpyException("GP packet info is not correct") + raise NatNegException("GP packet info is not correct") @staticmethod def _determine_nat_type_version3(info: "NatProtocolHelper"): if len(info.address_infos) < 3: - raise UniSpyException( + raise NatNegException( "We need 3 init records to determine the nat type.") nn1 = info.address_infos[NatPortType.NN1] @@ -203,7 +203,7 @@ def _determine_nat_type_version3(info: "NatProtocolHelper"): @staticmethod def _determine_nat_type_version4(info: "NatProtocolHelper"): if len(info.address_infos) < 3: - raise UniSpyException( + raise NatNegException( "We need 3 init records to determine the nat type.") nn1 = info.address_infos[NatPortType.NN1] diff --git a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py index 42a3ea6c4..477415c48 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py @@ -64,7 +64,8 @@ def _nick_email_login(self) -> None: assert self._request.nick is not None is_exsit = data.is_email_exist(self._request.email) if not is_exsit: - raise GPLoginBadEmailException(f"email: {self._request.email} is invalid.") + raise GPLoginBadEmailException( + f"email: {self._request.email} is invalid.") self._data = data.get_user_infos_by_nick_email( self._request.nick, self._request.email, self._session ) @@ -85,7 +86,12 @@ def _auth_token_login(self) -> None: def _result_construct(self) -> None: if self._data is None: raise GPLoginException("User is not exist.") - self._result = LoginResult(data=self._data) + self._result = LoginResult(data=self._data, + operation_id=self._request.operation_id, + type=self._request.type, + partner_id=self._request.partner_id, + user_challenge=self._request.user_challenge, + user_data=self._request.user_data) class LogoutHandler(HandlerBase): @@ -114,7 +120,8 @@ def _data_operate(self) -> None: ) def _result_construct(self) -> None: - self._result = BuddyListResult(profile_ids=self.data) + self._result = BuddyListResult( + profile_ids=self.data, operation_id=self._request.operation_id) class BlockListHandler(HandlerBase): @@ -126,7 +133,7 @@ def _data_operate(self) -> None: ) def _result_construct(self) -> None: - self._result = BlockListResult(profile_ids=self.data) + self._result = BlockListResult(profile_ids=self.data,operation_id=self._request.operation_id) class BuddyStatusInfoHandler(HandlerBase): @@ -229,7 +236,7 @@ def _data_operate(self) -> None: ) def _result_construct(self) -> None: - self._result = GetProfileResult(user_profile=self.data) + self._result = GetProfileResult(user_profile=self.data,operation_id=self._request.operation_id) class NewProfileHandler(HandlerBase): diff --git a/src/backends/protocols/gamespy/presence_connection_manager/requests.py b/src/backends/protocols/gamespy/presence_connection_manager/requests.py index 216aed8d8..1294802ff 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/requests.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/requests.py @@ -28,11 +28,13 @@ class BuddyListRequest(RequestBase): profile_id: int namespace_id: int raw_request: str | None = None + operation_id: int class BlockListRequest(RequestBase): profile_id: int namespace_id: int + operation_id: int class AddBuddyRequest(RequestBase): @@ -116,13 +118,14 @@ class LoginRequest(RequestBase): nick: str | None = None email: str | None = None product_id: int - type: Union[LoginType, int] + type: LoginType sdk_revision_type: list[SdkRevisionType] game_port: int partner_id: int game_name: str | None = None quiet_mode_flags: int firewall: bool + operation_id: int def model_post_init(self, __context: Any) -> None: super().model_post_init(__context) @@ -153,6 +156,7 @@ class LogoutRequest(RequestBase): class GetProfileRequest(RequestBase): profile_id: int session_key: str + operation_id: int class NewProfileRequest(RequestBase): diff --git a/src/backends/protocols/gamespy/presence_search_player/handlers.py b/src/backends/protocols/gamespy/presence_search_player/handlers.py index c10d1c0a5..6f19f4406 100644 --- a/src/backends/protocols/gamespy/presence_search_player/handlers.py +++ b/src/backends/protocols/gamespy/presence_search_player/handlers.py @@ -12,12 +12,12 @@ UniqueSearchRequest, ValidRequest, ) -from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.protocols.presence_search_player.aggregates.enums import ( SearchType, ) from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import ( CheckException, + GPException, ) from frontends.gamespy.protocols.presence_search_player.contracts.results import ( CheckResult, @@ -35,6 +35,7 @@ ValidResult, ) + class CheckHandler(HandlerBase): """ todo: whether need check the partner id, which means whether we need to check subprofiles @@ -58,7 +59,8 @@ def _data_operate(self) -> None: self._session, ) if self._profile_id is None: - raise CheckException(f"No pid found with email{self._request.email}") + raise CheckException( + f"No pid found with email{self._request.email}") def _result_construct(self) -> None: assert self._profile_id is not None @@ -150,7 +152,8 @@ def _data_operate(self) -> None: ) self.result_data = [] for nick, unique in self.temp_list: - self.result_data.append(NickResultData(nick=nick, uniquenick=unique)) + self.result_data.append( + NickResultData(nick=nick, uniquenick=unique)) def _result_construct(self) -> None: self._result = NicksResult(data=self.result_data) @@ -197,7 +200,8 @@ def _data_operate(self) -> None: def _result_construct(self) -> None: temp = [] for profile_id, uniquenick in self._data: - temp.append(OthersListData(profile_id=profile_id, unique_nick=uniquenick)) + temp.append(OthersListData( + profile_id=profile_id, unique_nick=uniquenick)) self._result = OthersListResult(data=temp) @@ -232,7 +236,7 @@ def _data_operate(self) -> None: self._request.email, self._session ) else: - raise UniSpyException("search type invalid") + raise GPException("search type invalid") def _result_construct(self) -> None: data = [] @@ -272,7 +276,9 @@ def _data_operate(self) -> None: ) def _result_construct(self) -> None: - self._result = UniqueSearchResult(is_uniquenick_exist=self._is_exist) + self._result = UniqueSearchResult( + is_uniquenick_exist=self._is_exist, + preferred_nick=self._request.preferred_nick) class ValidHandler(HandlerBase): @@ -280,7 +286,8 @@ class ValidHandler(HandlerBase): _result: ValidResult def _data_operate(self) -> None: - self._is_exist = data.is_email_exist(self._request.email, self._session) + self._is_exist = data.is_email_exist( + self._request.email, self._session) def _result_construct(self) -> None: self._result = ValidResult(is_account_valid=self._is_exist) diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index 9c2242ec8..d1addf7d0 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -7,8 +7,8 @@ Games, GameServerCaches, ) -from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.protocols.chat.aggregates.peer_room import PeerRoom +from frontends.gamespy.protocols.query_report.aggregates.exceptions import QRException from frontends.gamespy.protocols.query_report.aggregates.game_server_info import ( GameServerInfo, ) @@ -207,9 +207,9 @@ def update_game_server( def refresh_game_server_cache(instant_key: str, session: Session): cache = get_server_info_with_instant_key(instant_key, session) if cache is None: - raise UniSpyException( + raise QRException( "no game server cache found, please check the database") - cache.update_time = datetime.now() # type: ignore + cache.update_time = datetime.now() # type: ignore session.commit() diff --git a/src/backends/protocols/gamespy/server_browser/handlers.py b/src/backends/protocols/gamespy/server_browser/handlers.py index 24a06aac7..80b6a00ef 100644 --- a/src/backends/protocols/gamespy/server_browser/handlers.py +++ b/src/backends/protocols/gamespy/server_browser/handlers.py @@ -46,6 +46,7 @@ def _result_construct(self): flag=GameServerFlags.HAS_KEYS_FLAG, game_secret_key="", servers_info=self._caches, + keys=self._request.keys ) @@ -54,7 +55,8 @@ class P2PGroupRoomListHandler(HandlerBase): _caches: list[PeerRoomInfo] def _data_operate(self): - group_data = data.get_group_data_list_by_gamename(self._request.game_name) + group_data = data.get_group_data_list_by_gamename( + self._request.game_name) self._caches = data.get_peer_group_channel(group_data, self._session) def _result_construct(self) -> None: @@ -68,6 +70,7 @@ def _result_construct(self) -> None: flag=GameServerFlags.HAS_KEYS_FLAG, game_secret_key="", peer_room_info=self._caches, + keys=self._request.keys ) @@ -91,6 +94,7 @@ def _result_construct(self): flag=GameServerFlags.HAS_KEYS_FLAG, game_secret_key="", servers_info=self._caches, + keys=self._request.keys ) diff --git a/src/backends/protocols/gamespy/web_services/handlers.py b/src/backends/protocols/gamespy/web_services/handlers.py index a930433a4..bcf6ca895 100644 --- a/src/backends/protocols/gamespy/web_services/handlers.py +++ b/src/backends/protocols/gamespy/web_services/handlers.py @@ -43,6 +43,9 @@ def _result_construct(self) -> None: profile_nick=self.data[2], unique_nick=self.data[3], cdkey_hash=self.data[4], + version=self._request.version, + namespace_id=self._request.namespace_id, + partner_code=self._request.partner_code ) @@ -68,6 +71,9 @@ def _result_construct(self) -> None: profile_nick=self.data[2], unique_nick=self.data[3], cdkey_hash=self.data[4], + version=self._request.version, + namespace_id=self._request.namespace_id, + partner_code=self._request.partner_code ) @@ -88,6 +94,9 @@ def _result_construct(self) -> None: profile_nick=self.data[2], unique_nick=self.data[3], cdkey_hash=self.data[4], + version=self._request.version, + namespace_id=self._request.namespace_id, + partner_code=self._request.partner_code ) diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 1496da62f..234e7cfc5 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -3,6 +3,8 @@ from backends.protocols.gamespy.chat.handlers import ( CdKeyHandler, CryptHandler, + GetCKeyHandler, + GetChannelKeyHandler, GetKeyHandler, GetUdpRelayHandler, InviteHandler, @@ -50,7 +52,6 @@ from fastapi import APIRouter, FastAPI, WebSocket, WebSocketDisconnect - router = APIRouter() client_pool = {} @@ -161,12 +162,16 @@ def whois(request: WhoIsRequest): # region channel @router.post(f"{CHAT}/GetChannelKeyHandler") def get_channel_key(request: GetChannelKeyRequest): - raise NotImplementedError() + handler = GetChannelKeyHandler(request) + handler.handle() + return handler.response @router.post(f"{CHAT}/GetCKeyHandler") def get_ckey(request: GetCKeyRequest): - raise NotImplementedError() + handler = GetCKeyHandler(request) + handler.handle() + return handler.response @router.post(f"{CHAT}/JoinHandler") diff --git a/src/backends/routers/gamespy/game_traffic_relay.py b/src/backends/routers/gamespy/game_traffic_relay.py index c57618187..8b4d40d53 100644 --- a/src/backends/routers/gamespy/game_traffic_relay.py +++ b/src/backends/routers/gamespy/game_traffic_relay.py @@ -7,7 +7,6 @@ GtrHeartBeatRequest, ) from backends.urls import GAME_TRAFFIC_RELAY -from frontends.gamespy.library.exceptions.general import UniSpyException router = APIRouter() diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index 3967a6151..efcb2eb90 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -39,7 +39,7 @@ @app.exception_handler(UniSpyException) def unispy_exception_handler(_, exc: UniSpyException): logger.error(exc.message) - return JSONResponse({"error": exc.message}, status_code=450) + return JSONResponse({"error": exc.message, "exception": type(exc).__name__}, status_code=450) @app.exception_handler(RequestValidationError) diff --git a/src/frontends/gamespy/library/abstractions/contracts.py b/src/frontends/gamespy/library/abstractions/contracts.py index 064a0e2cf..717e1372e 100644 --- a/src/frontends/gamespy/library/abstractions/contracts.py +++ b/src/frontends/gamespy/library/abstractions/contracts.py @@ -47,17 +47,9 @@ class ResultBase(BaseModel): class ResponseBase: sending_buffer: object _result: ResultBase - _request: RequestBase - - def __init__(self, request: RequestBase, result: ResultBase | None) -> None: - super().__init__() - if request is not None: - assert issubclass(type(request), RequestBase) - self._request = request - - if result is not None: - assert issubclass(type(result), ResultBase) - self._result = result + def __init__(self, result: ResultBase) -> None: + assert issubclass(type(result), ResultBase) + self._result = result @abc.abstractmethod def build(self) -> None: diff --git a/src/frontends/gamespy/library/abstractions/handler.py b/src/frontends/gamespy/library/abstractions/handler.py index 3d9cf605e..9b1eddd8a 100644 --- a/src/frontends/gamespy/library/abstractions/handler.py +++ b/src/frontends/gamespy/library/abstractions/handler.py @@ -29,6 +29,7 @@ class CmdHandlerBase: the result type class, use to deserialize json data from backend\n the initialization of _result_cls must after call super().__init__() """ + _response_cls: "Type[ResponseBase]|None" _debug: bool = False """ whether is in debug mode, if in debug mode exception will raise from handler @@ -42,6 +43,7 @@ def __init__(self, client: "ClientBase", request: "RequestBase") -> None: self._client = client self._request = request self._result_cls = None + self._response_cls = None self._result = None self._response = None self._is_uploading = True @@ -112,7 +114,8 @@ def _upload_data(self): self._client.log_network_upload(f"[{self._url}] {json_str}") try: response = requests.post( - self._url, data=json_str, headers={"Content-Type": "application/json"} + self._url, data=json_str, headers={ + "Content-Type": "application/json"} ) except requests.exceptions.ConnectionError: if CONFIG.unittest.is_raise_except: @@ -158,7 +161,8 @@ def _fetch_data(self): if child class do not require feach, overide this function to do nothing """ if self._result_cls is None: - raise UniSpyException("_result_cls can not be null when feach data.") + raise UniSpyException( + "_result_cls can not be null when feach data.") assert issubclass(self._result_cls, ResultBase) self._client.log_network_fetch(f"[{self._url}] {self._http_result}") if "result" not in self._http_result: @@ -167,6 +171,10 @@ def _fetch_data(self): def _response_construct(self) -> None: """construct response here in specific child class""" + if self._response_cls is not None: + assert self._result is not None + self._response = self._response_cls( + result=self._result) pass def _response_send(self) -> None: diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index ef0b710fd..c03369d0e 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -100,12 +100,22 @@ def _heartbeat_to_backend(self, url: str, json_str: str): send heartbeat to backends """ assert isinstance(json_str, str) + self._get_data_from_backends(url, json_str=json_str) + + @final + def _get_data_from_backends(self, url: str, is_post: bool = True, json_str: str | None = None): try: - # post our server config to backends to register - resp = requests.post(url=url, data=json_str) + if is_post: + # post our server config to backends to register + resp = requests.post(url=url, data=json_str) + else: + resp = requests.get(url=url) + + data = resp.json() if resp.status_code != 200: - message = resp.json() - raise UniSpyException(message["error"]) + raise UniSpyException(data["error"]) + else: + return data except requests.ConnectionError: raise UniSpyException( f"backend server: {CONFIG.backend.url} not available." diff --git a/src/frontends/gamespy/protocols/chat/abstractions/contract.py b/src/frontends/gamespy/protocols/chat/abstractions/contract.py index 9e70491fb..31b3de87a 100644 --- a/src/frontends/gamespy/protocols/chat/abstractions/contract.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/contract.py @@ -69,13 +69,11 @@ class ResultBase(lib.ResultBase): class ResponseBase(lib.ResponseBase): sending_buffer: str _result: ResultBase - _request: RequestBase - def __init__(self, request: RequestBase, result: ResultBase | None) -> None: - super().__init__(request, result) + def __init__(self, result: ResultBase) -> None: + super().__init__(result) if result is not None: assert issubclass(type(result), ResultBase) - assert issubclass(type(request), RequestBase) @staticmethod def build_irc_user_prefix(nick_name: str, user_name: str): diff --git a/src/frontends/gamespy/protocols/chat/abstractions/handler.py b/src/frontends/gamespy/protocols/chat/abstractions/handler.py index 099b08563..62c29bd70 100644 --- a/src/frontends/gamespy/protocols/chat/abstractions/handler.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/handler.py @@ -60,12 +60,9 @@ def parse(self) -> None: class ChannelResponseBase(ResponseBase): - _request: ChannelRequestBase - - def __init__(self, request: RequestBase, result: ResultBase) -> None: - super().__init__(request, result) - assert isinstance(request, RequestBase) + def __init__(self, result: ResultBase) -> None: assert isinstance(result, ResultBase) + super().__init__(result) @staticmethod def build_value_str(keys: list, kv: dict) -> str: @@ -126,11 +123,13 @@ class MessageRequestBase(ChannelRequestBase): type: MessageType nick_name: str message: str - target_name:str + target_name: str + def parse(self): super().parse() if self.channel_name is None: - raise NoSuchNickException("the channel name is missing from the request") + raise NoSuchNickException( + "the channel name is missing from the request") if "#" in self.channel_name: self.type = MessageType.CHANNEL_MESSAGE self.target_name = self.channel_name @@ -146,8 +145,10 @@ def parse(self): class MessageResultBase(ResultBase): - nick_name: str - user_name: str + sender_nick_name: str + sender_user_name: str + target_name: str + message: str class MessageHandlerBase(ChannelHandlerBase): @@ -157,4 +158,4 @@ class MessageHandlerBase(ChannelHandlerBase): def __init__(self, client: ClientBase, request: MessageRequestBase): assert isinstance(request, MessageRequestBase) super().__init__(client, request) - self._is_broadcast = True + self._is_broadcast = True \ No newline at end of file diff --git a/src/frontends/gamespy/protocols/chat/aggregates/response_name.py b/src/frontends/gamespy/protocols/chat/aggregates/response_name.py deleted file mode 100644 index 5cd42b1a3..000000000 --- a/src/frontends/gamespy/protocols/chat/aggregates/response_name.py +++ /dev/null @@ -1,57 +0,0 @@ -# WELCOME = "001" -# USER_IP = "302" -# WHO_IS_USER = "311" -# END_OF_WHO = "315" -# END_OF_WHO_IS = "318" -# WHO_IS_CHANNELS = "319" -# LIST_START = "321" -# LIST = "322" -# LIST_END = "323" -# CHANNEL_MODELS = "324" -# NO_TOPIC = "331" -# TOPIC = "332" -# WHO_REPLY = "352" -# NAME_REPLY = "353" -# END_OF_NAMES = "366" -# BAN_LIST = "367" -# END_OF_BAN_LIST = "368" -# GET_KEY = "700" -# END_GET_KEY = "701" -# GET_CKEY = "702" -# END_GET_CKEY = "703" -# GET_CHAN_KEY = "704" -# SECURE_KEY = "705" -# CD_KEY = "706" -# LOGIN = "707" -# GET_UDP_RELAY = "712" - -# # Send a private message -# PRIVATE_MSG = "PRIVMSG" -# # Send a notice message -# NOTICE = "NOTICE" -# # Send an under the table message -# UNDER_THE_TABLE_MSG = "UTM" -# # Send an above the table message -# ABOVE_THE_TABLE_MSG = "ATM" -# PING = "PING" -# PONG = "PONG" -# # Search with nickname -# NICK = "NICK" -# # Join a channel -# JOIN = "JOIN" -# # Leave a channel -# PART = "PART" -# # Kick a user from a channel -# KICK = "KICK" -# # Quit irc chat server -# QUIT = "QUIT" - -# KILL = "KILL" -# # Change channel topic -# CHANNEL_TOPIC = "TOPIC" -# # Change channel mode -# MODE = "MODE" - -# ERROR = "ERROR" -# # Invite a user to a channel -# INVITE = "INVITE" diff --git a/src/frontends/gamespy/protocols/chat/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py index 20fc26ac6..479efaf14 100644 --- a/src/frontends/gamespy/protocols/chat/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -3,6 +3,7 @@ AtmResult, NoticeResult, PrivateResult, + SetCKeyResult, UtmResult, GetCKeyResult, GetChannelKeyResult, @@ -102,6 +103,7 @@ def __init__(self, client: ClientBase, request: CdkeyRequest): def _response_construct(self) -> None: self._response = CdKeyResponse() + class CryptHandler(CmdHandlerBase): _request: CryptRequest _result: CryptResult @@ -131,9 +133,7 @@ def __init__(self, client: ClientBase, request: GetKeyRequest): assert isinstance(request, GetKeyRequest) super().__init__(client, request) self._result_cls = GetKeyResult - - def _response_construct(self) -> None: - self._response = GetKeyResponse(self._request, self._result) + self._response_cls = GetKeyResponse class GetUdpRelayHandler(CmdHandlerBase): @@ -258,7 +258,8 @@ def _request_check(self) -> None: def _data_operate(self) -> None: super()._data_operate() - self._result = UserIPResult(remote_ip=self._client.connection.remote_ip) + self._result = UserIPResult( + remote_ip=self._client.connection.remote_ip) def _response_construct(self) -> None: self._response = UserIPResponse(self._result) @@ -272,9 +273,7 @@ def __init__(self, client: ClientBase, request: WhoRequest): assert isinstance(request, WhoRequest) super().__init__(client, request) self._result_cls = WhoResult - - def _response_construct(self) -> None: - self._response = WhoResponse(self._request, self._result) + self._response_cls = WhoResponse class WhoIsHandler(CmdHandlerBase): @@ -285,9 +284,7 @@ def __init__(self, client: ClientBase, request: WhoIsRequest): assert isinstance(request, WhoIsRequest) super().__init__(client, request) self._result_cls = WhoIsResult - - def _response_construct(self) -> None: - self._response = WhoIsResponse(self._result) + self._response_cls = WhoIsResponse # region channel @@ -316,9 +313,7 @@ def __init__(self, client: ClientBase, request: GetCKeyRequest): assert isinstance(request, GetCKeyRequest) super().__init__(client, request) self._result_cls = GetCKeyResult - - def _response_construct(self): - self._response = GetCKeyResponse(self._request, self._result) + self._response_cls = GetCKeyResponse class JoinHandler(ChannelHandlerBase): @@ -328,11 +323,9 @@ class JoinHandler(ChannelHandlerBase): def __init__(self, client: ClientBase, request: JoinRequest): assert isinstance(request, JoinRequest) super().__init__(client, request) - self._result_cls = JoinResult self._is_broadcast = True - - def _response_construct(self): - self._response = JoinResponse(self._request, self._result) + self._result_cls = JoinResult + self._response_cls = JoinResponse def _response_send(self) -> None: super()._response_send() @@ -356,9 +349,7 @@ def __init__(self, client: ClientBase, request: KickRequest): assert isinstance(request, KickRequest) super().__init__(client, request) self._result_cls = KickResult - - def _response_construct(self): - self._response = KickResponse(self._request, self._result) + self._response_cls = KickResponse class ModeHandler(ChannelHandlerBase): @@ -369,6 +360,7 @@ def __init__(self, client: ClientBase, request: ModeRequest): assert isinstance(request, ModeRequest) super().__init__(client, request) self._result_cls = ModeResult + self._response_cls = ModeResponse def _request_check(self) -> None: super()._request_check() @@ -382,7 +374,7 @@ def _request_check(self) -> None: def _response_construct(self): if self._request.request_type == ModeRequestType.GET_CHANNEL_MODES: - self._response = ModeResponse(self._request, self._result) + super()._response_construct() class NamesHandler(ChannelHandlerBase): @@ -393,9 +385,7 @@ def __init__(self, client: ClientBase, request: NamesRequest): assert isinstance(request, NamesRequest) super().__init__(client, request) self._result_cls = NamesResult - - def _response_construct(self): - self._response = NamesResponse(self._request, self._result) + self._response_cls = NamesResponse class PartHandler(ChannelHandlerBase): @@ -406,9 +396,7 @@ def __init__(self, client: ClientBase, request: PartRequest): assert isinstance(request, PartRequest) super().__init__(client, request) self._result_cls = PartResult - - def _response_construct(self): - self._response = PartResponse(self._request, self._result) + self._response_cls = PartResponse class SetChannelKeyHandler(ChannelHandlerBase): @@ -419,9 +407,10 @@ def __init__(self, client: ClientBase, request: SetChannelKeyRequest): assert isinstance(request, SetChannelKeyRequest) super().__init__(client, request) self._result_cls = SetChannelKeyResult + self._is_broadcast = True def _response_construct(self): - self._response = SetChannelKeyResponse(self._request, self._result) + self._response = SetChannelKeyResponse(self._result) class SetCKeyHandler(ChannelHandlerBase): @@ -430,10 +419,8 @@ class SetCKeyHandler(ChannelHandlerBase): def __init__(self, client: ClientBase, request: SetCKeyRequest): assert isinstance(request, SetCKeyRequest) super().__init__(client, request) - self._is_fetching = False - - def _response_construct(self) -> None: - self._response = SetCKeyResponse(self._request) + self._result_cls = SetCKeyResult + self._response_cls = SetCKeyResponse class TopicHandler(ChannelHandlerBase): @@ -444,9 +431,7 @@ def __init__(self, client: ClientBase, request: TopicRequest): assert isinstance(request, TopicRequest) super().__init__(client, request) self._result_cls = TopicResult - - def _response_construct(self) -> None: - self._response = TopicResponse(self._request, self._result) + self._response_cls = TopicResponse # region Message @@ -460,9 +445,7 @@ def __init__(self, client: ClientBase, request: AtmRequest): super().__init__(client, request) assert isinstance(request, AtmRequest) self._result_cls = AtmResult - - def _response_construct(self) -> None: - self._response = AtmResponse(self._request, self._result) + self._response_cls = AtmResponse class UTMHandler(MessageHandlerBase): @@ -473,9 +456,7 @@ def __init__(self, client: ClientBase, request: UtmRequest): assert isinstance(request, UtmRequest) super().__init__(client, request) self._result_cls = UtmResult - - def _response_construct(self) -> None: - self._response = UtmResponse(self._request, self._result) + self._response_cls = UtmResponse class NoticeHandler(MessageHandlerBase): @@ -486,9 +467,7 @@ def __init__(self, client: ClientBase, request: NoticeRequest): assert isinstance(request, NoticeRequest) super().__init__(client, request) self._result_cls = NoticeResult - - def _response_construct(self) -> None: - self._response = NoticeResponse(self._request, self._result) + self._response_cls = NoticeResponse class PrivateHandler(MessageHandlerBase): @@ -499,6 +478,4 @@ def __init__(self, client: ClientBase, request: PrivateRequest): assert isinstance(request, PrivateRequest) super().__init__(client, request) self._result_cls = PrivateResult - - def _response_construct(self) -> None: - self._response = PrivateResponse(self._request, self._result) + self._response_cls = PrivateResponse diff --git a/src/frontends/gamespy/protocols/chat/contracts/responses.py b/src/frontends/gamespy/protocols/chat/contracts/responses.py index 05da3a2d5..fdcc3b0b8 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/responses.py +++ b/src/frontends/gamespy/protocols/chat/contracts/responses.py @@ -12,6 +12,7 @@ NamesResult, NamesResultData, PartResult, + SetCKeyResult, SetChannelKeyResult, TopicResult, AtmResult, @@ -48,6 +49,7 @@ from frontends.gamespy.protocols.chat.abstractions.contract import ( SERVER_DOMAIN, ResponseBase, + ResultBase, ) from frontends.gamespy.library.encryption.gs_encryption import CLIENT_KEY, SERVER_KEY @@ -73,27 +75,27 @@ def build(self) -> None: class GetKeyResponse(ResponseBase): - _request: GetKeyRequest + _result: GetKeyResult - def __init__(self, request: GetKeyRequest, result: GetKeyResult) -> None: - assert isinstance(request, GetKeyRequest) + def __init__(self, result: GetKeyResult) -> None: assert isinstance(result, GetKeyResult) - super().__init__(request, result) + super().__init__(result) def build(self) -> None: self.sending_buffer = "" for value in self._result.values: - self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.GETKEY.value} * {self._result.nick_name} {self._request.cookie} {value}\r\n" # noqa + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.GETKEY.value} * {self._result.nick_name} {self._result.cookie} {value}\r\n" # noqa - self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDGETKEY.value} * {self._request.cookie} * :End Of GETKEY.\r\n" # noqa + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDGETKEY.value} * {self._result.cookie} * :End Of GETKEY.\r\n" # noqa class ListResponse(ResponseBase): _result: ListResult - def __init__(self, result: ListResult) -> None: + def __init__( + self, result: ListResult) -> None: assert isinstance(result, ListResult) self._result = result @@ -114,7 +116,8 @@ def build(self) -> None: class LoginResponse(ResponseBase): _result: LoginResult - def __init__(self, result: LoginResult) -> None: + def __init__( + self, result: LoginResult) -> None: assert isinstance(result, LoginResult) self._result = result @@ -125,7 +128,8 @@ def build(self) -> None: class NickResponse(ResponseBase): _result: NickResult - def __init__(self, result: NickResult) -> None: + def __init__( + self, result: NickResult) -> None: assert isinstance(result, NickResult) self._result = result @@ -136,7 +140,8 @@ def build(self) -> None: class PingResponse(ResponseBase): _result: PingResult - def __init__(self, result: PingResult) -> None: + def __init__( + self, result: PingResult) -> None: assert isinstance(result, PingResult) self._result = result @@ -149,7 +154,8 @@ def build(self) -> None: class UserIPResponse(ResponseBase): _result: UserIPResult - def __init__(self, result: UserIPResult) -> None: + def __init__( + self, result: UserIPResult) -> None: assert isinstance(result, UserIPResult) self._result = result @@ -160,7 +166,8 @@ def build(self) -> None: class WhoIsResponse(ResponseBase): _result: WhoIsResult - def __init__(self, result: WhoIsResult) -> None: + def __init__( + self, result: WhoIsResult) -> None: assert isinstance(result, WhoIsResult) self._result = result @@ -178,13 +185,11 @@ def build(self) -> None: class WhoResponse(ResponseBase): - _request: WhoRequest _result: WhoResult - def __init__(self, request: WhoRequest, result: WhoResult) -> None: - assert isinstance(request, WhoRequest) + def __init__(self, result: WhoResult) -> None: assert isinstance(result, WhoResult) - super().__init__(request, result) + super().__init__(result) def build(self): self.sending_buffer = "" @@ -197,85 +202,80 @@ def build(self): ) in self._result.infos: self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.WHOREPLY.value} * {channel_name} {user_name} {public_ip_address} * {nick_name} {modes} *\r\n" # noqa - if self._request.request_type == WhoRequestType.GET_CHANNEL_USER_INFO: + if self._result.request_type == WhoRequestType.GET_CHANNEL_USER_INFO: # if len(self._result.infos) > 0: - self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDOFWHO.value} * {self._request.channel_name} * :End of WHO.\r\n" # noqa - elif self._request.request_type == WhoRequestType.GET_USER_INFO: + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDOFWHO.value} * {self._result.channel_name} * :End of WHO.\r\n" # noqa + elif self._result.request_type == WhoRequestType.GET_USER_INFO: # if len(self._result.infos) > 0: - self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDOFWHO.value} * {self._request.nick_name} * :End of WHO.\r\n" # noqa + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDOFWHO.value} * {self._result.nick_name} * :End of WHO.\r\n" # noqa # region Channel class GetChannelKeyResponse(ChannelResponseBase): - _request: GetChannelKeyRequest _result: GetChannelKeyResult def build(self) -> None: involker_irc_prefix = ResponseBase.build_irc_user_prefix( self._result.nick_name, self._result.user_name ) - self.sending_buffer = f":{involker_irc_prefix} {ResponseCode.GETCHANKEY.value} * {self._result.channel_name} {self._request.cookie} {self._result.key_values}\r\n" + self.sending_buffer = f":{involker_irc_prefix} {ResponseCode.GETCHANKEY.value} * {self._result.channel_name} {self._result.cookie} {self._result.key_values}\r\n" class GetCKeyResponse(ResponseBase): - _request: GetCKeyRequest _result: GetCKeyResult - def __init__(self, request: GetCKeyRequest, result: GetCKeyResult) -> None: - assert isinstance(request, GetCKeyRequest) + def __init__( + self, result: GetCKeyResult) -> None: assert isinstance(result, GetCKeyResult) - super().__init__(request, result) + super().__init__(result) def build(self) -> None: self.sending_buffer = "" - for nick_name, user_values in self._result.infos: - self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.GETCKEY.value} * {self._request.channel_name} {nick_name} {self._request.cookie} {user_values}\r\n" # noqa + for info in self._result.infos: + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.GETCKEY.value} * {self._result.channel_name} {info.nick_name} {self._result.cookie} {info.user_values}\r\n" # noqa - self.sending_buffer += f"{SERVER_DOMAIN} {ResponseCode.ENDGETCKEY.value} * {self._request.channel_name} {self._request.cookie} :End Of GETCKEY.\r\n" # noqa + self.sending_buffer += f"{SERVER_DOMAIN} {ResponseCode.ENDGETCKEY.value} * {self._result.channel_name} {self._result.cookie} :End Of GETCKEY.\r\n" # noqa class JoinResponse(ResponseBase): - _request: JoinRequest + _result: JoinResult - def __init__(self, request: JoinRequest, result: JoinResult) -> None: - assert isinstance(request, JoinRequest) + def __init__(self, result: JoinResult) -> None: assert isinstance(result, JoinResult) - super().__init__(request, result) + super().__init__(result) def build(self) -> None: joiner_irc_prefix = ResponseBase.build_irc_user_prefix( self._result.joiner_nick_name, self._result.joiner_user_name ) - self.sending_buffer = f":{joiner_irc_prefix} {ResponseCode.JOIN.value} {self._request.channel_name}\r\n" + self.sending_buffer = f":{joiner_irc_prefix} {ResponseCode.JOIN.value} {self._result.channel_name}\r\n" class KickResponse(ResponseBase): - _request: KickRequest + _result: KickResult - def __init__(self, request: KickRequest, result: KickResult) -> None: - assert isinstance(request, KickRequest) + def __init__(self, result: KickResult) -> None: assert isinstance(result, KickResult) - super().__init__(request, result) + super().__init__(result) def build(self) -> None: kicker_irc_prefix = ResponseBase.build_irc_user_prefix( self._result.kicker_nick_name, self._result.kicker_user_name ) - self.sending_buffer = f"{kicker_irc_prefix} {ResponseCode.KICK.value} {self._result.channel_name} {self._result.kickee_nick_name} :{self._request.reason}\r\n" + self.sending_buffer = f"{kicker_irc_prefix} {ResponseCode.KICK.value} {self._result.channel_name} {self._result.kickee_nick_name} :{self._result.reason}\r\n" class ModeResponse(ResponseBase): - _request: ModeRequest + _result: ModeResult - def __init__(self, request: ModeRequest, result: ModeResult) -> None: - assert isinstance(request, ModeRequest) + def __init__(self, result: ModeResult) -> None: assert isinstance(result, ModeResult) - super().__init__(request, result) + super().__init__(result) @staticmethod def get_mode_str(modes: list[str]): @@ -288,17 +288,16 @@ def get_mode_str(modes: list[str]): def build(self) -> None: chann_modes_str = ModeResponse.get_mode_str(self._result.channel_modes) - self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.CHANNELMODEIS.value} * {self._request.channel_name} {chann_modes_str}\r\n" + self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.CHANNELMODEIS.value} * {self._result.channel_name} {chann_modes_str}\r\n" class NamesResponse(ChannelResponseBase): - _request: NamesRequest + _result: NamesResult - def __init__(self, request: NamesRequest, result: NamesResult) -> None: - assert isinstance(request, NamesRequest) + def __init__(self, result: NamesResult) -> None: assert isinstance(result, NamesResult) - super().__init__(request, result) + super().__init__(result) @staticmethod def get_nicks_list(data: list[NamesResultData]): @@ -323,60 +322,58 @@ def build(self) -> None: class PartResponse(ResponseBase): - _request: PartRequest + _result: PartResult - def __init__(self, request: PartRequest, result: PartResult) -> None: - assert isinstance(request, PartRequest) + def __init__(self, result: PartResult) -> None: assert isinstance(result, PartResult) - super().__init__(request, result) + super().__init__(result) def build(self) -> None: leaver_irc_prefix = ResponseBase.build_irc_user_prefix( self._result.leaver_nick_name, self._result.leaver_user_name ) - self.sending_buffer = f":{leaver_irc_prefix} {ResponseCode.PART.value} {self._result.channel_name} :{self._request.reason}\r\n" + self.sending_buffer = f":{leaver_irc_prefix} {ResponseCode.PART.value} {self._result.channel_name} :{self._result.reason}\r\n" class SetChannelKeyResponse(ChannelResponseBase): - _request: SetChannelKeyRequest _result: SetChannelKeyResult - def __init__( - self, request: SetChannelKeyRequest, result: SetChannelKeyResult - ) -> None: - assert isinstance(request, SetChannelKeyRequest) + def __init__(self, result: SetChannelKeyResult) -> None: + super().__init__(result) assert isinstance(result, SetChannelKeyResult) - super().__init__(request, result) + def build(self) -> None: setter_irc_prefix = ResponseBase.build_irc_user_prefix( self._result.setter_nick_name, self._result.setter_user_name ) - self.sending_buffer = f":{setter_irc_prefix} {ResponseCode.GETCHANKEY.value} * {self._request.channel_name} BCAST {self._request.key_value_string}\r\n" + key_value_str = ChannelResponseBase.build_key_value_str( + self._result.key_value) + self.sending_buffer = f":{setter_irc_prefix} {ResponseCode.GETCHANKEY.value} * {self._result.channel_name} BCAST {key_value_str}\r\n" class SetCKeyResponse(ResponseBase): - _request: SetCKeyRequest + _result: SetCKeyResult - def __init__(self, request: SetCKeyRequest) -> None: - assert isinstance(request, SetCKeyRequest) - super().__init__(request, None) + def __init__(self, result: SetCKeyResult) -> None: + super().__init__(result) def build(self) -> None: - self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.GETCKEY.value} * {self._request.channel_name} {self._request.nick_name} {self._request.cookie} {self._request.key_value_string}\r\n" + kv_str = ChannelResponseBase.build_key_value_str( + self._result.key_value) + self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.GETCKEY.value} * {self._result.channel_name} {self._result.setter_nick_name} {self._result.cookie} {kv_str}\r\n" - self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDGETCKEY.value} * {self._request.channel_name} {self._request.nick_name} {self._request.cookie} :End Of SETCKEY.\r\n" + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.ENDGETCKEY.value} * {self._result.channel_name} {self._result.setter_nick_name} {self._result.cookie} :End Of SETCKEY.\r\n" class TopicResponse(ResponseBase): - _request: TopicRequest + _result: TopicResult - def __init__(self, request: TopicRequest, result: TopicResult) -> None: - assert isinstance(request, TopicRequest) + def __init__(self, result: TopicResult) -> None: assert isinstance(result, TopicResult) - super().__init__(request, result) + super().__init__(result) def build(self) -> None: if self._result.channel_topic == "" or self._result.channel_topic is None: @@ -389,67 +386,63 @@ def build(self) -> None: class AtmResponse(ResponseBase): - _request: AtmRequest + _result: AtmResult - def __init__(self, request: AtmRequest, result: AtmResult) -> None: - assert isinstance(request, AtmRequest) + def __init__(self, result: AtmResult) -> None: assert isinstance(result, AtmResult) - super().__init__(request, result) + super().__init__(result) def build(self) -> None: irc_prefix = ResponseBase.build_irc_user_prefix( - self._result.nick_name, self._result.user_name + self._result.sender_nick_name, self._result.sender_user_name ) - self.sending_buffer = f":{irc_prefix} {ResponseCode.ATM.value} {self._request.target_name} :{self._request.message}\r\n" + self.sending_buffer = f":{irc_prefix} {ResponseCode.ATM.value} {self._result.target_name} :{self._result.message}\r\n" class NoticeResponse(ResponseBase): - _request: NoticeRequest + _result: NoticeResult - def __init__(self, request: NoticeRequest, result: NoticeResult) -> None: + def __init__( + self, result: NoticeResult) -> None: assert isinstance(result, NoticeResult) - assert isinstance(request, NoticeRequest) - - super().__init__(request, result) + super().__init__(result) def build(self) -> None: irc_prefix = ResponseBase.build_irc_user_prefix( - self._result.nick_name, self._result.user_name + self._result.sender_nick_name, self._result.sender_user_name ) - self.sending_buffer = f":{irc_prefix} {ResponseCode.NOTICE.value} {self._request.target_name} {self._request.message}\r\n" + self.sending_buffer = f":{irc_prefix} {ResponseCode.NOTICE.value} {self._result.target_name} {self._result.message}\r\n" class PrivateResponse(ResponseBase): - _request: PrivateRequest + _result: PrivateResult - def __init__(self, request: PrivateRequest, result: PrivateResult) -> None: - assert isinstance(request, PrivateRequest) + def __init__(self, result: PrivateResult) -> None: assert isinstance(result, PrivateResult) - super().__init__(request, result) + super().__init__(result) def build(self) -> None: irc_prefix = ResponseBase.build_irc_user_prefix( - self._result.nick_name, self._result.user_name + self._result.sender_nick_name, self._result.sender_user_name ) - self.sending_buffer = f":{irc_prefix} {ResponseCode.PRIVMSG.value} {self._request.target_name} :{self._request.message}\r\n" + self.sending_buffer = f":{irc_prefix} {ResponseCode.PRIVMSG.value} {self._result.target_name} :{self._result.message}\r\n" class UtmResponse(ResponseBase): - _request: UtmRequest + _result: UtmResult - def __init__(self, request: UtmRequest, result: UtmResult) -> None: - assert isinstance(request, UtmRequest) + def __init__(self, result: UtmResult) -> None: assert isinstance(result, UtmResult) - super().__init__(request, result) + super().__init__(result) def build(self) -> None: irc_prefix = ResponseBase.build_irc_user_prefix( - self._result.nick_name, self._result.user_name + self._result.sender_nick_name, self._result.sender_user_name ) - self.sending_buffer = f":{irc_prefix} {ResponseCode.UTM.value} {self._request.target_name} :{self._request.message}\r\n" + self.sending_buffer = f":{irc_prefix} {ResponseCode.UTM.value} {self._result.target_name} :{self._result.message}\r\n" diff --git a/src/frontends/gamespy/protocols/chat/contracts/results.py b/src/frontends/gamespy/protocols/chat/contracts/results.py index 16d3570e4..004e4d5b0 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/results.py +++ b/src/frontends/gamespy/protocols/chat/contracts/results.py @@ -1,6 +1,7 @@ from pydantic import BaseModel from frontends.gamespy.protocols.chat.abstractions.contract import ResultBase from frontends.gamespy.protocols.chat.abstractions.handler import MessageResultBase +from frontends.gamespy.protocols.chat.aggregates.enums import WhoRequestType # region General @@ -13,6 +14,7 @@ class CryptResult(ResultBase): class GetKeyResult(ResultBase): nick_name: str values: list + cookie: str class ListResult(ResultBase): @@ -66,15 +68,16 @@ class WhoIsResult(ResultBase): class WhoResult(ResultBase): class WhoInfo(BaseModel): - channel_name: str user_name: str - public_ip_addr: str nick_name: str + channel_name: str + public_ip_addr: str modes: str infos: list[WhoInfo] - - + request_type: WhoRequestType + channel_name: str + nick_name: str # region Channel @@ -83,16 +86,17 @@ class GetChannelKeyResult(ResultBase): user_name: str channel_name: str key_values: dict + cookie: str class GetCKeyResult(ResultBase): class GetCKeyInfos(BaseModel): nick_name: str - user_values: list - + user_values: list[str] infos: list[GetCKeyInfos] """ nick_name:str, user_values:str""" channel_name: str + cookie: str class ModeResult(ResultBase): @@ -116,6 +120,7 @@ class NamesResult(ResultBase): class JoinResult(ResultBase): joiner_nick_name: str joiner_user_name: str + channel_name: str class KickResult(ResultBase): @@ -123,6 +128,7 @@ class KickResult(ResultBase): kicker_nick_name: str kicker_user_name: str kickee_nick_name: str + reason: str class PartResult(ResultBase): @@ -130,6 +136,7 @@ class PartResult(ResultBase): leaver_user_name: str is_channel_creator: bool channel_name: str + reason: str class TopicResult(ResultBase): @@ -141,12 +148,15 @@ class SetChannelKeyResult(ResultBase): setter_nick_name: str setter_user_name: str channel_name: str + key_value: dict[str, str] class SetCKeyResult(ResultBase): setter_nick_name: str setter_user_name: str channel_name: str + cookie: str + key_value: dict if __name__ == "__main__": diff --git a/src/frontends/gamespy/protocols/game_status/abstractions/contracts.py b/src/frontends/gamespy/protocols/game_status/abstractions/contracts.py index d13dfa1e0..512bc6b66 100644 --- a/src/frontends/gamespy/protocols/game_status/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/game_status/abstractions/contracts.py @@ -33,6 +33,7 @@ def parse(self) -> None: class ResultBase(lib.ResultBase): + local_id: int pass diff --git a/src/frontends/gamespy/protocols/game_status/applications/handlers.py b/src/frontends/gamespy/protocols/game_status/applications/handlers.py index ef91e9de3..36c4e7b1b 100644 --- a/src/frontends/gamespy/protocols/game_status/applications/handlers.py +++ b/src/frontends/gamespy/protocols/game_status/applications/handlers.py @@ -13,12 +13,12 @@ def __init__(self, client: Client, request: AuthGameRequest) -> None: assert isinstance(request, AuthGameRequest) super().__init__(client, request) self._result_cls = AuthGameResult + self._response_cls = AuthGameResponse def _response_construct(self) -> None: self._client.info.session_key = self._result.session_key - self._client.info.game_name = self._request.game_name + self._client.info.game_name = self._result.game_name self._client.info.is_game_authenticated = True - self._response = AuthGameResponse(self._request, self._result) class AuthPlayerHandler(CmdHandlerBase): @@ -30,7 +30,7 @@ def __init__(self, client: Client, request: AuthPlayerRequest) -> None: self._result_cls = AuthPlayerResult def _response_construct(self) -> None: - self._response = AuthPlayerResponse(self._request, self._result) + self._response = AuthPlayerResponse(self._result) class GetPlayerDataHandler(CmdHandlerBase): @@ -42,7 +42,7 @@ def __init__(self, client: Client, request: GetPlayerDataRequest) -> None: self._result_cls = GetPlayerDataResult def _response_construct(self) -> None: - self._response = GetPlayerDataResponse(self._request, self._result) + self._response = GetPlayerDataResponse(self._result) class GetProfileIdHandler(CmdHandlerBase): @@ -54,7 +54,7 @@ def __init__(self, client: Client, request: GetProfileIdRequest) -> None: self._result_cls = GetProfileIdResult def _response_construct(self) -> None: - self._response = GetProfileIdResponse(self._request, self._result) + self._response = GetProfileIdResponse(self._result) class NewGameHandler(CmdHandlerBase): diff --git a/src/frontends/gamespy/protocols/game_status/contracts/responses.py b/src/frontends/gamespy/protocols/game_status/contracts/responses.py index d16e0b860..8d81c68bc 100644 --- a/src/frontends/gamespy/protocols/game_status/contracts/responses.py +++ b/src/frontends/gamespy/protocols/game_status/contracts/responses.py @@ -1,57 +1,51 @@ from typing import final from frontends.gamespy.library.abstractions.contracts import ResponseBase -from frontends.gamespy.protocols.game_status.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, SetPlayerDataRequest from frontends.gamespy.protocols.game_status.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult @final class AuthGameResponse(ResponseBase): - _request: AuthGameRequest _result: AuthGameResult def build(self) -> None: # fmt: off - self.sending_buffer = f"\\sesskey\\{self._result.session_key}\\lid\\{self._request.local_id}\\final\\" + self.sending_buffer = f"\\sesskey\\{self._result.session_key}\\lid\\{self._result.local_id}\\final\\" # fmt: on @final class AuthPlayerResponse(ResponseBase): - _request: AuthPlayerRequest _result: AuthPlayerResult def build(self) -> None: # fmt: off - self.sending_buffer = f"\\pauthr\\{self._result.profile_id}\\lid\\{self._request.local_id}\\final\\" + self.sending_buffer = f"\\pauthr\\{self._result.profile_id}\\lid\\{self._result.local_id}\\final\\" # fmt: on @final class GetPlayerDataResponse(ResponseBase): - _request: GetPlayerDataRequest _result: GetPlayerDataResult def build(self) -> None: # fmt: off - self.sending_buffer = f"\\getpdr\\1\\pid\\{self._request.profile_id}\\lid\\{self._request.local_id}\\mod\\1234\\length\\5\\data\\mydata\\final\\" + self.sending_buffer = f"\\getpdr\\1\\pid\\{self._result.profile_id}\\lid\\{self._result.local_id}\\mod\\1234\\length\\5\\data\\mydata\\final\\" # fmt: on @final class GetProfileIdResponse(ResponseBase): - _request: GetProfileIdRequest _result: GetProfileIdResult def build(self) -> None: # fmt: off - self.sending_buffer = f"\\getpidr\\{self._result.profile_id}\\lid\\{self._request.local_id}\\final\\" + self.sending_buffer = f"\\getpidr\\{self._result.profile_id}\\lid\\{self._result.local_id}\\final\\" # fmt: on @final class SetPlayerDataResponse(ResponseBase): - _request: SetPlayerDataRequest _result: GetPlayerDataResult def build(self) -> None: diff --git a/src/frontends/gamespy/protocols/game_status/contracts/results.py b/src/frontends/gamespy/protocols/game_status/contracts/results.py index 863aed137..a72ba389b 100644 --- a/src/frontends/gamespy/protocols/game_status/contracts/results.py +++ b/src/frontends/gamespy/protocols/game_status/contracts/results.py @@ -5,6 +5,7 @@ @final class AuthGameResult(ResultBase): session_key: str + game_name: str @final @@ -15,6 +16,7 @@ class AuthPlayerResult(ResultBase): @final class GetPlayerDataResult(ResultBase): keyvalues: dict[str, str] + profile_id: int @final diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/aggregates/exceptions.py b/src/frontends/gamespy/protocols/game_traffic_relay/aggregates/exceptions.py new file mode 100644 index 000000000..47912d4a6 --- /dev/null +++ b/src/frontends/gamespy/protocols/game_traffic_relay/aggregates/exceptions.py @@ -0,0 +1,5 @@ +from frontends.gamespy.library.exceptions.general import UniSpyException + + +class GameTrafficException(UniSpyException): + pass diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py index c9dad5ded..c21202384 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py @@ -1,12 +1,10 @@ from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase from frontends.gamespy.library.configs import CONFIG -from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.library.network.udp_handler import UdpServer from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client from frontends.gamespy.protocols.game_traffic_relay.contracts.general import ( GtrHeartbeat, ) -import requests class ServerLauncher(ServerLauncherBase): @@ -21,20 +19,9 @@ def __init__(self) -> None: self._get_public_ip() def _get_public_ip(self): - try: - # post our server config to backends to register - resp = requests.get( - url=f"{CONFIG.backend.url}/GameSpy/GameTrafficRelay/get_my_ip") - data = resp.json() - if resp.status_code == 200: - self._public_ip = data['ip'] - print(f"GameTrafficRelay public ip: {self._public_ip}") - else: - raise UniSpyException(data["error"]) - except requests.ConnectionError: - raise UniSpyException( - f"backend server: {CONFIG.backend.url} not available." - ) + url = f"{CONFIG.backend.url}/GameSpy/GameTrafficRelay/get_my_ip" + data = self._get_data_from_backends(url=url, is_post=False) + self._public_ip = data['ip'] def _gtr_heartbeat(self): assert self.config diff --git a/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py b/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py index 7d3dc3a4f..52f1d30ad 100644 --- a/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py @@ -40,25 +40,25 @@ def parse(self) -> None: class ResultBase(lib.ResultBase): packet_type: ResponseType + version: int + cookie: int pass class ResponseBase(lib.ResponseBase): - _request: RequestBase _result: ResultBase sending_buffer: bytes - def __init__(self, request: RequestBase, result: ResultBase) -> None: - super().__init__(request, result) - assert issubclass(type(request), RequestBase) + def __init__(self, result: ResultBase) -> None: assert issubclass(type(result), ResultBase) + super().__init__(result) def build(self) -> None: data = bytes() data += MAGIC_DATA - data += self._request.version.to_bytes(1) + data += self._result.version.to_bytes(1) data += self._result.packet_type.value.to_bytes(1) - data += self._request.cookie.to_bytes(4) + data += self._result.cookie.to_bytes(4) self.sending_buffer = data @@ -75,19 +75,21 @@ def parse(self): class CommonResultBase(ResultBase): public_ip_addr: str public_port: int + port_type: NatPortType + client_index: NatClientIndex + use_game_port: bool class CommonResponseBase(ResponseBase): _result: CommonResultBase - _request: CommonRequestBase def build(self) -> None: super().build() data = bytes() data += self.sending_buffer - data += self._request.port_type.value.to_bytes(1) - data += self._request.client_index.value.to_bytes(1) - data += bytes(self._request.use_game_port) + data += self._result.port_type.value.to_bytes(1) + data += self._result.client_index.value.to_bytes(1) + data += int(self._result.use_game_port).to_bytes(1) data += ip_to_4_bytes(self._result.public_ip_addr) data += self._result.public_port.to_bytes(2) self.sending_buffer = data diff --git a/src/frontends/gamespy/protocols/natneg/aggregations/exceptions.py b/src/frontends/gamespy/protocols/natneg/aggregations/exceptions.py new file mode 100644 index 000000000..7ca37e69e --- /dev/null +++ b/src/frontends/gamespy/protocols/natneg/aggregations/exceptions.py @@ -0,0 +1,5 @@ +from frontends.gamespy.library.exceptions.general import UniSpyException + + +class NatNegException(UniSpyException): + pass diff --git a/src/frontends/gamespy/protocols/natneg/applications/handlers.py b/src/frontends/gamespy/protocols/natneg/applications/handlers.py index 637a50762..372a22985 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/handlers.py +++ b/src/frontends/gamespy/protocols/natneg/applications/handlers.py @@ -47,10 +47,15 @@ def _response_construct(self) -> None: self._result = AddressCheckResult( public_ip_addr=self._client.connection.remote_ip, public_port=self._client.connection.remote_port, + version=self._request.version, + cookie=self._request.cookie, + client_index=self._request.client_index, + use_game_port=self._request.use_game_port, + port_type=self._request.port_type ) self._result.public_ip_addr = self._client.connection.remote_ip self._result.public_port = self._client.connection.remote_port - self._response = AddressCheckResponse(self._request, self._result) + self._response = AddressCheckResponse(self._result) class ConnectAckHandler(CmdHandlerBase): @@ -76,13 +81,14 @@ def __init__(self, client: Client, request: ConnectRequest) -> None: assert isinstance(request, ConnectRequest) super().__init__(client, request) self._result_cls = ConnectResult + self._response_cls = ConnectResponse def _response_construct(self) -> None: if not self._result.is_both_client_ready: self._client.log_warn( f"init cache is not enough for cookie: {self._request.cookie}") return - self._response = ConnectResponse(self._request, self._result) + super()._response_construct() class ErtAckHandler(CmdHandlerBase): @@ -102,8 +108,13 @@ def _response_construct(self) -> None: self._result = ErtAckResult( public_ip_addr=self._client.connection.remote_ip, public_port=self._client.connection.remote_port, + version=self._request.version, + cookie=self._request.cookie, + client_index=self._request.client_index, + use_game_port=self._request.use_game_port, + port_type=self._request.port_type ) - self._response = ErcAckResponse(self._request, self._result) + self._response = ErcAckResponse(self._result) class InitHandler(CmdHandlerBase): @@ -128,9 +139,14 @@ def _response_construct(self): self._result = InitResult( public_ip_addr=self._client.connection.remote_ip, public_port=self._client.connection.remote_port, + version=self._request.version, + cookie=self._request.cookie, + client_index=self._request.client_index, + use_game_port=self._request.use_game_port, + port_type=self._request.port_type ) - self._response = InitResponse(self._request, self._result) + self._response = InitResponse(self._result) def handle(self) -> None: try: @@ -181,8 +197,13 @@ def _response_construct(self): self._result = NatifyResult( public_ip_addr=self._client.connection.remote_ip, public_port=self._client.connection.remote_port, + version=self._request.version, + cookie=self._request.cookie, + client_index=self._request.client_index, + use_game_port=self._request.use_game_port, + port_type=self._request.port_type ) - self._response = NatifyResponse(self._request, self._result) + self._response = NatifyResponse(self._result) class ReportHandler(CmdHandlerBase): diff --git a/src/frontends/gamespy/protocols/natneg/contracts/responses.py b/src/frontends/gamespy/protocols/natneg/contracts/responses.py index 0334f04e1..dbb996133 100644 --- a/src/frontends/gamespy/protocols/natneg/contracts/responses.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/responses.py @@ -3,13 +3,6 @@ CommonResponseBase, ResponseBase, ) -from frontends.gamespy.protocols.natneg.contracts.requests import ( - AddressCheckRequest, - ConnectRequest, - ErtAckRequest, - InitRequest, - NatifyRequest, -) from frontends.gamespy.protocols.natneg.contracts.results import ( AddressCheckResult, ConnectResult, @@ -20,51 +13,41 @@ class InitResponse(CommonResponseBase): - _request: InitRequest _result: InitResult - def __init__(self, request: InitRequest, result: InitResult) -> None: - super().__init__(request, result) - assert isinstance(request, InitRequest) + def __init__(self, result: InitResult) -> None: assert isinstance(result, InitResult) + super().__init__(result) class ErcAckResponse(InitResponse): - _request: ErtAckRequest _result: ErtAckResult - def __init__(self, request: ErtAckRequest, result: ErtAckResult) -> None: - assert isinstance(request, ErtAckRequest) + def __init__(self, result: ErtAckResult) -> None: assert isinstance(result, ErtAckResult) - self._request = request self._result = result class NatifyResponse(CommonResponseBase): - _request: NatifyRequest _result: NatifyResult - def __init__(self, request: NatifyRequest, result: NatifyResult) -> None: - assert isinstance(request, NatifyRequest) + def __init__(self, result: NatifyResult) -> None: assert isinstance(result, NatifyResult) - super().__init__(request, result) + super().__init__(result) class AddressCheckResponse(CommonResponseBase): - _request: AddressCheckRequest _result: AddressCheckResult def __init__( - self, request: AddressCheckRequest, result: AddressCheckResult + self, result: AddressCheckResult ) -> None: - assert isinstance(request, AddressCheckRequest) assert isinstance(result, AddressCheckResult) - super().__init__(request, result) + super().__init__(result) class ConnectResponse(ResponseBase): _result: ConnectResult - _request: ConnectRequest def build(self) -> None: assert self._result.ip is not None diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py b/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py index 4e76ca6a2..1d56da16c 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py @@ -11,7 +11,7 @@ def normalize_request(message: str): if "login" in message: message = message.replace("\\-", "\\") pos = message.index("\\", message.index("\\") + 1) - if message[pos : pos + 2] != "\\\\": + if message[pos: pos + 2] != "\\\\": message = message[:pos] + "\\" + message[pos:] return message @@ -39,6 +39,7 @@ def parse(self): class ResultBase(lib.ResultBase): + operation_id: int pass @@ -47,7 +48,6 @@ class ResponseBase(lib.ResponseBase): _result: ResultBase sending_buffer: str - def __init__(self, request: RequestBase, result: ResultBase | None) -> None: - assert issubclass(type(request), RequestBase) + def __init__(self, result: ResultBase) -> None: assert issubclass(type(result), ResultBase) - super().__init__(request, result) + super().__init__(result) diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py index fc52649af..31c6b7334 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py @@ -22,6 +22,7 @@ BlockListResult, BuddyListResult, NewUserResult, + RegisterNickResult, StatusInfoResult, StatusResult, GetProfileResult, @@ -76,7 +77,7 @@ def _data_operate(self) -> None: super()._data_operate() def _response_construct(self) -> None: - self._response = KeepAliveResponse(self._request) + self._response = KeepAliveResponse() class LoginHandler(CmdHandlerBase): @@ -88,9 +89,7 @@ def __init__(self, client: "Client", request: LoginRequest) -> None: assert isinstance(request, LoginRequest) super().__init__(client, request) self._result_cls = LoginResult - - def _response_construct(self) -> None: - self._response = LoginResponse(self._request, self._result) + self._response_cls = LoginResponse class LogoutHandler(LoginedHandlerBase): @@ -112,9 +111,7 @@ def __init__(self, client: Client, request: NewUserRequest) -> None: assert isinstance(request, NewUserRequest) super().__init__(client, request) self._result_cls = NewUserResult - - def _response_construct(self): - self._response = NewUserResponse(self._request, self._result) + self._response_cls = NewUserResponse class SdkRevisionHandler(CmdHandlerBase): @@ -167,9 +164,8 @@ class BuddyListHandler(LoginedHandlerBase): def __init__(self, client): self._client = client assert isinstance(client, Client) - - def response_construct(self): - self._response = BuddyListResponse(self._request, self._result) + self._result_cls = BuddyListResult + self._response_cls = BuddyListResponse class BuddyStatusInfoHandler(CmdHandlerBase): @@ -219,13 +215,12 @@ class StatusInfoHandler(LoginedHandlerBase): def __init__(self, client: Client, request: StatusInfoRequest) -> None: assert isinstance(request, StatusInfoRequest) super().__init__(client, request) - self._is_fetching = False + self._result_cls = StatusInfoResult + self._response_cls = StatusInfoResponse def _response_send(self) -> None: # todo: check if response is needed - if self._request is not None: - self._response = StatusInfoResponse(self._request, self._result) - super()._response_send() + super()._response_send() # region Profile @@ -250,9 +245,7 @@ def __init__(self, client: Client, request: GetProfileRequest) -> None: assert isinstance(request, GetProfileRequest) super().__init__(client, request) self._result_cls = GetProfileResult - - def _response_construct(self) -> None: - self._response = GetProfileResponse(self._request, self._result) + self._response_cls = GetProfileResponse @final @@ -264,9 +257,7 @@ def __init__(self, client: Client, request: NewProfileRequest) -> None: assert isinstance(request, NewProfileRequest) super().__init__(client, request) self._result_cls = NewProfileResult - - def _response_construct(self) -> None: - self._response = NewProfileResponse(self._request, self._result) + self._response_cls = NewProfileResponse @final @@ -286,10 +277,8 @@ class RegisterNickHandler(CmdHandlerBase): def __init__(self, client: Client, request: RegisterNickRequest) -> None: assert isinstance(request, RegisterNickRequest) super().__init__(client, request) - self._is_fetching = False - - def _response_construct(self) -> None: - self._response = RegisterNickResponse(self._request) + self._result_cls = RegisterNickResult + self._response_cls = RegisterNickResponse @final diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py index a3fd6d61d..944aa94f5 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py @@ -1,4 +1,3 @@ -from frontends.gamespy.library.exceptions.general import UniSpyException from typing import final from frontends.gamespy.library.extentions.gamespy_utils import convert_to_key_value from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import ( @@ -8,6 +7,7 @@ GPStatusCode, ) from frontends.gamespy.protocols.presence_search_player.aggregates.exceptions import ( + GPException, GPParseException, ) from frontends.gamespy.library.extentions.gamespy_utils import is_email_format_correct @@ -75,7 +75,7 @@ def validate_extra_infos(info_dict: dict) -> dict: value_type = EXTRA_INFO_DICT[key] converted_value = value_type(value) except Exception as e: - raise UniSpyException(f"value conversion failed: {e}") + raise GPException(f"value conversion failed: {e}") extra_infos[key] = converted_value return extra_infos @@ -153,7 +153,7 @@ def parse(self): if pos == -1 or pos < 1 or (pos + 1) >= len(self.user_data): raise GPParseException("user format is incorrect") self.nick = self.user_data[:pos] - self.email = self.user_data[pos + 1 :] + self.email = self.user_data[pos + 1:] if "namespaceid" in self.request_dict: namespace_id = int(self.request_dict["namespaceid"]) self.namespace_id = namespace_id @@ -422,7 +422,8 @@ def parse(self): self.host_port = int(self.request_dict["hport"]) self.session_flags = self.request_dict["sessflags"] except ValueError: - raise GPParseException("qport, hport, or sessflags format is incorrect.") + raise GPParseException( + "qport, hport, or sessflags format is incorrect.") if "namespace_id" in self.request_dict: self.namespace_id = int(self.request_dict["namespaceid"]) diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/responses.py b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/responses.py index 05e0875da..ad8530b6c 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/responses.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/responses.py @@ -32,6 +32,7 @@ AddBuddyResult, BlockListResult, BuddyListResult, + RegisterNickResult, StatusInfoResult, LoginResult, NewUserResult, @@ -42,8 +43,8 @@ class KeepAliveResponse(ResponseBase): - def __init__(self, request: KeepAliveRequest) -> None: - super().__init__(request, None) + def __init__(self) -> None: + pass def build(self) -> None: self.sending_buffer = "\\ka\\final\\" @@ -53,17 +54,17 @@ class LoginResponse(ResponseBase): _result: LoginResult _request: LoginRequest - def __init__(self, request: LoginRequest, result: LoginResult): - super().__init__(request, result) - assert isinstance(request, LoginRequest) + def __init__(self, result: LoginResult): + super().__init__(result) + assert isinstance(result, LoginResult) def build(self): response_proof = LoginChallengeProof( - self._request.user_data, - self._request.type, - self._request.partner_id, - self._request.user_challenge, + self._result.user_data, + self._result.type, + self._result.partner_id, + self._result.user_challenge, SERVER_CHALLENGE, self._result.data.password_hash, ).generate_proof() @@ -77,31 +78,30 @@ def build(self): self.sending_buffer += "\\uniquenick\\" + self._result.data.unique_nick self.sending_buffer += f"\\lt\\{LOGIN_TICKET}" - self.sending_buffer += f"\\id\\{self._request.operation_id}\\final\\" + self.sending_buffer += f"\\id\\{self._result.operation_id}\\final\\" class NewUserResponse(ResponseBase): _request: NewUserRequest _result: NewUserResult - def __init__(self, request: NewUserRequest, result: NewUserResult) -> None: - super().__init__(request, result) - assert isinstance(request, NewUserRequest) + def __init__(self, result: NewUserResult) -> None: + super().__init__(result) + assert isinstance(result, NewUserResult) def build(self): # fmt: on - self.sending_buffer = f"\\nur\\userid\\{self._result.user_id}\\profileid\\{self._result.profile_id}\\id\\{self._request.operation_id}\\final\\" # fmt: off + self.sending_buffer = f"\\nur\\userid\\{self._result.user_id}\\profileid\\{self._result.profile_id}\\id\\{self._result.operation_id}\\final\\" # fmt: off # region Buddy class AddBuddyResponse(ResponseBase): - def __init__(self, request: AddBuddyRequest, result: AddBuddyResult) -> None: - assert issubclass(type(request), AddBuddyRequest) + def __init__(self, result: AddBuddyResult) -> None: assert issubclass(type(result), AddBuddyResult) - super().__init__(request, result) + super().__init__( result) def build(self) -> None: # return super().build() @@ -134,8 +134,8 @@ def build(self): class BuddyListResponse(ResponseBase): _result: BuddyListResult - def __init__(self, request: RequestBase, result: BuddyListResult): - super().__init__(request, result) + def __init__(self, result: BuddyListResult): + super().__init__(result) def build(self): # \bdy\< num in list >\list\< profileid list - comma delimited >\final\ @@ -147,10 +147,10 @@ def build(self): class StatusInfoResponse(ResponseBase): _result: StatusInfoResult - def __init__(self, request: StatusInfoRequest, result: StatusInfoResult): - assert isinstance(request, StatusInfoRequest) + def __init__(self, result: StatusInfoResult): + assert isinstance(result, StatusInfoResult) - super().__init__(request, result) + super().__init__(result) def build(self): # \bsi\\state\\profile\\bip\\bport\\hostip\\hprivip\\qport\\hport\\sessflags\\rstatus\\gameType\\gameVnt\\gameMn\\product\\qmodeflags\ @@ -173,10 +173,10 @@ class GetProfileResponse(ResponseBase): _result: GetProfileResult _request: GetProfileRequest - def __init__(self, request: GetProfileRequest, result: GetProfileResult): - assert isinstance(request, GetProfileRequest) + def __init__(self, result: GetProfileResult): + assert isinstance(result, GetProfileResult) - super().__init__(request, result) + super().__init__(result) def build(self): self.sending_buffer = ( @@ -190,7 +190,7 @@ def build(self): self.sending_buffer += ( f"\\sig\\+{generate_random_string(10, StringType.HEX)}" - + f"\\id\\{self._request.operation_id}\\final\\" + + f"\\id\\{self._result.operation_id}\\final\\" ) @@ -198,14 +198,14 @@ class NewProfileResponse(ResponseBase): _request: NewProfileRequest _result: NewProfileResult - def __init__(self, request: NewProfileRequest, result: NewProfileResult): - assert isinstance(request, NewProfileRequest) + def __init__(self, result: NewProfileResult): + assert isinstance(result, NewProfileResult) - super().__init__(request, result) + super().__init__(result) def build(self): # fmt: off - self.sending_buffer = f"\\npr\\\\profileid\\{self.sending_buffer}\\id\\{self._request.operation_id}\\final\\" + self.sending_buffer = f"\\npr\\\\profileid\\{self.sending_buffer}\\id\\{self._result.operation_id}\\final\\" # fmt: on @@ -218,13 +218,9 @@ def build(self): class RegisterNickResponse(ResponseBase): - _request: RegisterNickRequest - - def __init__(self, request: RegisterNickRequest) -> None: - assert isinstance(request, RegisterNickRequest) - self._request = request - + _result: RegisterNickResult + def build(self) -> None: # fmt: off - self.sending_buffer = f"\\rn\\\\id\\{self._request.operation_id}\\final\\" + self.sending_buffer = f"\\rn\\\\id\\{self._result.operation_id}\\final\\" # fmt: on diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/results.py b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/results.py index f5ca919a5..623340731 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/results.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/results.py @@ -1,6 +1,6 @@ from pydantic import BaseModel from frontends.gamespy.protocols.presence_connection_manager.abstractions.contracts import ResultBase -from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import GPStatusCode +from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import GPStatusCode, LoginType # region General @@ -20,6 +20,10 @@ class LoginData(BaseModel): class LoginResult(ResultBase): data: LoginData + user_data: str + type: LoginType + partner_id: int + user_challenge: str class NewUserResult(ResultBase): @@ -36,6 +40,7 @@ class AddBuddyResult(ResultBase): class BlockListResult(ResultBase): profile_ids: list[int] + operation_id: int class BuddyListResult(ResultBase): @@ -59,7 +64,6 @@ class StatusInfoResult(ResultBase): quiet_mode_flags: str - class StatusResult(ResultBase): status_string: str location_string: str @@ -84,3 +88,7 @@ class GetProfileResult(ResultBase): class NewProfileResult(ResultBase): profile_id: int + + +class RegisterNickResult(ResultBase): + pass diff --git a/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py b/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py index 279b2ef6e..87ed9a536 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py +++ b/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py @@ -17,9 +17,7 @@ def __init__(self, client: Client, request: CheckRequest) -> None: assert isinstance(request, CheckRequest) super().__init__(client, request) self._result_cls = CheckResult - - def _response_construct(self): - self._response = CheckResponse(self._request, self._result) + self._response_cls = CheckResponse class NewUserHandler(CmdHandlerBase): @@ -31,9 +29,7 @@ def __init__(self, client: Client, request: NewUserRequest) -> None: assert isinstance(request, NewUserRequest) super().__init__(client, request) self._result_cls = NewUserResult - - def _response_construct(self): - self._response = NewUserResponse(self._request, self._result) + self._response_cls = NewUserResponse class NicksHandler(CmdHandlerBase): @@ -45,9 +41,7 @@ def __init__(self, client: Client, request: NicksRequest) -> None: assert isinstance(request, NicksRequest) super().__init__(client, request) self._result_cls = NicksResult - - def _response_construct(self): - self._response = NicksResponse(self._request, self._result) + self._response_cls = NicksResponse class OthersHandler(CmdHandlerBase): @@ -110,9 +104,7 @@ def __init__(self, client: Client, request: SearchUniqueRequest) -> None: assert isinstance(request, SearchUniqueRequest) super().__init__(client, request) self._result_cls = SearchUniqueResult - - def _response_construct(self): - self._response = SearchUniqueResponse(self._result) + self._response_cls = SearchUniqueResponse class UniqueSearchHandler(CmdHandlerBase): @@ -124,9 +116,7 @@ def __init__(self, client: Client, request: UniqueSearchRequest) -> None: assert isinstance(request, UniqueSearchRequest) super().__init__(client, request) self._result_cls = UniqueSearchResult - - def _response_construct(self): - self._response = UniqueSearchResponse(self._request, self._result) + self._response_cls = UniqueSearchResponse class ValidHandler(CmdHandlerBase): @@ -138,6 +128,4 @@ def __init__(self, client: Client, request: ValidRequest) -> None: assert isinstance(request, ValidRequest) super().__init__(client, request) self._result_cls = ValidResult - - def _response_construct(self): - self._response = ValidResponse(self._request, self._result) + self._response_cls = ValidResponse diff --git a/src/frontends/gamespy/protocols/presence_search_player/contracts/responses.py b/src/frontends/gamespy/protocols/presence_search_player/contracts/responses.py index 74cd4b5c8..32a5ab57f 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/contracts/responses.py +++ b/src/frontends/gamespy/protocols/presence_search_player/contracts/responses.py @@ -24,10 +24,9 @@ class CheckResponse(ResponseBase): _result: CheckResult - def __init__(self, request: CheckRequest, result: CheckResult) -> None: - assert isinstance(request, CheckRequest) + def __init__(self, result: CheckResult) -> None: assert isinstance(result, CheckResult) - super().__init__(request, result) + super().__init__(result) def build(self): if self._result.profile_id is None: @@ -38,32 +37,28 @@ def build(self): class NewUserResponse(ResponseBase): _result: NewUserResult - _request: NewUserRequest - def __init__(self, request: NewUserRequest, result: NewUserResult) -> None: - assert isinstance(request, NewUserRequest) + def __init__(self, result: NewUserResult) -> None: assert isinstance(result, NewUserResult) - super().__init__(request, result) + super().__init__(result) def build(self): self.sending_buffer = f"\\nur\\\\pid\\{self._result.profile_id}\\final\\" class NicksResponse(ResponseBase): - _request: NicksRequest + _result: NicksResult - def __init__(self, request: NicksRequest, result: NicksResult) -> None: - assert isinstance(request, NicksRequest) + def __init__(self, result: NicksResult) -> None: assert isinstance(result, NicksResult) - - super().__init__(request, result) + super().__init__(result) def build(self): self.sending_buffer = "\\nr\\" for info in self._result.data: self.sending_buffer += f"\\nick\\{info.nick}" - if self._request.is_require_uniquenicks: + if self._result.is_require_uniquenicks: self.sending_buffer += f"\\uniquenick\\{info.uniquenick}" self.sending_buffer += "\\ndone\\final\\" @@ -143,13 +138,12 @@ def build(self): class ValidResponse(ResponseBase): - _request: ValidRequest + _result: ValidResult - def __init__(self, request: ValidRequest, result: ValidResult) -> None: - assert isinstance(request, ValidRequest) + def __init__(self, result: ValidResult) -> None: assert isinstance(result, ValidResult) - super().__init__(request, result) + super().__init__(result) def build(self): if self._result.is_account_valid: @@ -159,20 +153,19 @@ def build(self): class UniqueSearchResponse(ResponseBase): - _request: UniqueSearchRequest + _result: UniqueSearchResult def __init__( - self, request: UniqueSearchRequest, result: UniqueSearchResult + self, result: UniqueSearchResult ) -> None: - assert isinstance(request, UniqueSearchRequest) assert isinstance(result, UniqueSearchResult) - super().__init__(request, result) + super().__init__(result) def build(self): if self._result.is_uniquenick_exist: self.sending_buffer = "\\us\\1\\nick\\Choose another name\\usdone\\final\\" else: self.sending_buffer = ( - f"\\us\\1\\nick\\{self._request.preferred_nick}\\usdone\\final\\" + f"\\us\\1\\nick\\{self._result.preferred_nick}\\usdone\\final\\" ) diff --git a/src/frontends/gamespy/protocols/presence_search_player/contracts/results.py b/src/frontends/gamespy/protocols/presence_search_player/contracts/results.py index 2ec0cb20a..8295db1fb 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/contracts/results.py +++ b/src/frontends/gamespy/protocols/presence_search_player/contracts/results.py @@ -80,6 +80,7 @@ class SearchUniqueResult(ResultBase): class UniqueSearchResult(ResultBase): is_uniquenick_exist: bool + preferred_nick: str class ValidResult(ResultBase): diff --git a/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py b/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py index 8df9f2cb8..e1c38947a 100644 --- a/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py @@ -24,7 +24,6 @@ class ResultBase(lib.ResultBase): class ResponseBase(lib.ResponseBase): sending_buffer: str - def __init__(self, request: RequestBase, result: ResultBase) -> None: - assert issubclass(type(request), RequestBase) + def __init__(self, result: ResultBase) -> None: assert issubclass(type(result), ResultBase) - super().__init__(request, result) + super().__init__(result) diff --git a/src/frontends/gamespy/protocols/query_report/v2/abstractions/contracts.py b/src/frontends/gamespy/protocols/query_report/v2/abstractions/contracts.py index 7bd9d310d..dbc032c04 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/query_report/v2/abstractions/contracts.py @@ -24,16 +24,16 @@ def parse(self): class ResultBase(lib.ResultBase): packet_type: PacketType - + command_name: RequestType + instant_key: str class ResponseBase(lib.ResponseBase): _result: ResultBase - _request: RequestBase sending_buffer: bytes def build(self) -> None: data = bytearray() data.extend(MAGIC_DATA) - data.append(self._request.command_name.value) - data.extend(int(self._request.instant_key).to_bytes(4)) + data.append(self._result.command_name.value) + data.extend(int(self._result.instant_key).to_bytes(4)) self.sending_buffer = bytes(data) diff --git a/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py b/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py index 1fa1e9d76..75f317f8a 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py +++ b/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py @@ -12,13 +12,15 @@ KeepAliveRequest, ) from frontends.gamespy.protocols.query_report.v2.contracts.responses import ( - AvaliableResponse, + AvailableResponse, ChallengeResponse, ClientMessageResponse, HeartBeatResponse, ) from frontends.gamespy.protocols.query_report.v2.contracts.results import ( + AvailableResult, ChallengeResult, + ClientMessageResult, HeartBeatResult, ) @@ -32,7 +34,10 @@ def __init__(self, client: Client, request: AvaliableRequest) -> None: self._is_fetching = False def _response_construct(self): - self._response = AvaliableResponse(self._request) + self._result = AvailableResult( + command_name=self._request.command_name, + instant_key=self._request.instant_key) + self._response = AvailableResponse(self._result) class ChallengeHanler(CmdHandlerBase): @@ -42,10 +47,8 @@ class ChallengeHanler(CmdHandlerBase): def __init__(self, client: Client, request: ChallengeRequest) -> None: assert isinstance(request, ChallengeRequest) super().__init__(client, request) - self._is_fetching = False - - def _response_construct(self): - self._response = ChallengeResponse(self._request, self._result) + self._result_cls = ChallengeResult + self._response_cls = ChallengeResponse class ClientMessageAckHandler(CmdHandlerBase): @@ -59,17 +62,12 @@ def _response_construct(self): class ClientMessageHandler(CmdHandlerBase): - _request: ClientMessageRequest def __init__(self, client: Client, request: ClientMessageRequest) -> None: assert isinstance(request, ClientMessageRequest) super().__init__(client, request) - - def _request_check(self) -> None: - pass - - def _response_construct(self) -> None: - self._response = ClientMessageResponse(self._request) + self._result_cls = ClientMessageResult + self._response_cls = ClientMessageResponse class EchoHandler(CmdHandlerBase): @@ -92,12 +90,13 @@ def _response_construct(self) -> None: self._result = HeartBeatResult( remote_ip=self._client.connection.remote_ip, remote_port=self._client.connection.remote_port, + instant_key=self._request.instant_key, + command_name=self._request.command_name ) - self._response = HeartBeatResponse(self._request, self._result) + self._response = HeartBeatResponse(self._result) class KeepAliveHandler(CmdHandlerBase): - _request: KeepAliveRequest def __init__(self, client: Client, request: KeepAliveRequest) -> None: assert isinstance(request, KeepAliveRequest) diff --git a/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py b/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py index 6e743fa75..67d81a5e3 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py +++ b/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py @@ -1,6 +1,6 @@ -from typing import TYPE_CHECKING, Optional, cast +from typing import TYPE_CHECKING, cast from frontends.gamespy.library.abstractions.switcher import SwitcherBase -from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.protocols.query_report.aggregates.exceptions import QRException from frontends.gamespy.protocols.query_report.applications.client import Client from frontends.gamespy.protocols.query_report.v2.abstractions.cmd_handler_base import CmdHandlerBase @@ -28,7 +28,7 @@ class Switcher(SwitcherBase): def _process_raw_request(self) -> None: if len(self._raw_request) < 4: - raise UniSpyException("Invalid request") + raise QRException("Invalid request") name = self._raw_request[0] if name not in RequestType: self._client.log_debug( diff --git a/src/frontends/gamespy/protocols/query_report/v2/contracts/responses.py b/src/frontends/gamespy/protocols/query_report/v2/contracts/responses.py index 715e34f30..7be912de3 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/contracts/responses.py +++ b/src/frontends/gamespy/protocols/query_report/v2/contracts/responses.py @@ -8,7 +8,9 @@ KeepAliveRequest ) from frontends.gamespy.protocols.query_report.v2.contracts.results import ( + AvailableResult, ChallengeResult, + ClientMessageResult, HeartBeatResult, ) from frontends.gamespy.protocols.query_report.v2.aggregates.enums import ServerAvailability @@ -17,10 +19,10 @@ RESPONSE_PREFIX = bytes([0xFE, 0xFD, 0x09, 0x00, 0x00, 0x00]) -class AvaliableResponse(ResponseBase): - def __init__(self, request: AvaliableRequest) -> None: - assert isinstance(request, AvaliableRequest) - super().__init__(request, None) +class AvailableResponse(ResponseBase): + def __init__(self, result: AvailableResult) -> None: + assert isinstance(result, AvailableResult) + super().__init__(result) def build(self): data = bytearray() @@ -33,10 +35,9 @@ def build(self): class ChallengeResponse(ResponseBase): - def __init__(self, request: ChallengeRequest, result: ChallengeResult) -> None: - assert isinstance(request, ChallengeRequest) + def __init__(self, result: ChallengeResult) -> None: assert isinstance(result, ChallengeResult) - super().__init__(request, result) + super().__init__( result) def build(self) -> None: super().build() @@ -47,18 +48,18 @@ def build(self) -> None: class ClientMessageResponse(ResponseBase): - _request: ClientMessageRequest + _result: ClientMessageResult - def __init__(self, request: ClientMessageRequest) -> None: - assert isinstance(request, ClientMessageRequest) - super().__init__(request, None) + def __init__(self, result: ClientMessageResult) -> None: + assert isinstance(result, ClientMessageResult) + super().__init__(result) def build(self): super().build() data = bytearray() data.extend(self.sending_buffer) - data.extend(self._request.message_key.to_bytes(4, byteorder="little")) - data.extend(self._request.natneg_message) + data.extend(self._result.message_key.to_bytes(4, byteorder="little")) + data.extend(self._result.natneg_message) self.sending_buffer = bytes(data) @@ -67,13 +68,11 @@ def build(self): class HeartBeatResponse(ResponseBase): - _request: HeartBeatRequest _result: HeartBeatResult - def __init__(self, request: HeartBeatRequest, result: HeartBeatResult) -> None: - assert isinstance(request, HeartBeatRequest) + def __init__(self, result: HeartBeatResult) -> None: assert isinstance(result, HeartBeatResult) - super().__init__(request, result) + super().__init__(result) def build(self) -> None: super().build() @@ -86,7 +85,7 @@ def build(self) -> None: self.sending_buffer = bytes(data) -class KeepAliveResponse(ResponseBase): - def __init__(self, request: KeepAliveRequest) -> None: - assert isinstance(request, KeepAliveRequest) - super().__init__(request, None) +# class KeepAliveResponse(ResponseBase): +# def __init__(self, result:KeepAliveResponse) -> None: +# assert isinstance(request, KeepAliveRequest) +# super().__init__(request, None) diff --git a/src/frontends/gamespy/protocols/query_report/v2/contracts/results.py b/src/frontends/gamespy/protocols/query_report/v2/contracts/results.py index 1fc2040de..50a68d859 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/contracts/results.py +++ b/src/frontends/gamespy/protocols/query_report/v2/contracts/results.py @@ -3,6 +3,11 @@ from frontends.gamespy.protocols.query_report.v2.aggregates.enums import PacketType +@final +class AvailableResult(ResultBase): + packet_type: PacketType = PacketType.AVALIABLE_CHECK + + @final class ChallengeResult(ResultBase): packet_type: PacketType = PacketType.CHALLENGE diff --git a/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py index 2b0cb168b..2b46f62c5 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py @@ -32,7 +32,8 @@ def __init__(self, raw_request: bytes) -> None: super().__init__(raw_request) def parse(self) -> None: - self.request_length = int.from_bytes(self.raw_request[:2], byteorder="little") + self.request_length = int.from_bytes( + self.raw_request[:2], byteorder="little") self.command_name = RequestType(self.raw_request[2]) @@ -41,14 +42,12 @@ class ResultBase(lib.ResultBase): class ResponseBase(lib.ResponseBase): - _request: RequestBase _result: ResultBase sending_buffer: bytes - def __init__(self, request: RequestBase, result: ResultBase) -> None: - assert issubclass(type(request), RequestBase) + def __init__(self, result: ResultBase) -> None: assert issubclass(type(result), ResultBase) - super().__init__(request, result) + super().__init__(result) class ServerListUpdateOptionRequestBase(RequestBase): @@ -75,28 +74,28 @@ class ServerListUpdateOptionResultBase(ResultBase): client_remote_ip: str flag: GameServerFlags game_secret_key: str + keys: list[str] class ServerListUpdateOptionResponseBase(ResponseBase): - _request: ServerListUpdateOptionRequestBase _result: ServerListUpdateOptionResultBase _servers_info_buffers: bytearray def __init__( self, - request: ServerListUpdateOptionRequestBase, result: ServerListUpdateOptionResultBase, ) -> None: - assert issubclass(type(request), ServerListUpdateOptionRequestBase) assert issubclass(type(result), ServerListUpdateOptionResultBase) - super().__init__(request, result) + super().__init__(result) self._servers_info_buffers = bytearray() def build(self) -> None: crypt_header = self.build_crypt_header() self._servers_info_buffers.extend(crypt_header) - self._servers_info_buffers.extend(ip_to_4_bytes(self._result.client_remote_ip)) - self._servers_info_buffers.extend(QUERY_REPORT_DEFAULT_PORT.to_bytes(4)) + self._servers_info_buffers.extend( + ip_to_4_bytes(self._result.client_remote_ip)) + self._servers_info_buffers.extend( + QUERY_REPORT_DEFAULT_PORT.to_bytes(4)) def build_crypt_header(self) -> list: # cryptHeader have 14 bytes, when we encrypt data we need skip the first 14 bytes @@ -109,9 +108,9 @@ def build_crypt_header(self) -> list: def build_server_keys(self) -> None: # we add the total number of the requested keys - self._servers_info_buffers.append(len(self._request.keys)) + self._servers_info_buffers.append(len(self._result.keys)) # then we add the keys - for key in self._request.keys: + for key in self._result.keys: self._servers_info_buffers.append(DataKeyType.STRING) self._servers_info_buffers.extend(get_bytes(key)) self._servers_info_buffers.extend(STRING_SPLITER) @@ -140,6 +139,6 @@ class AdHocResponseBase(ResponseBase): _result: AdHocResultBase _buffer: bytearray - def __init__(self, request: RequestBase, result: ResultBase) -> None: - super().__init__(request, result) + def __init__(self, result: ResultBase) -> None: + super().__init__(result) self._buffer = bytearray() diff --git a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/exceptions.py b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/exceptions.py new file mode 100644 index 000000000..1ce346c8f --- /dev/null +++ b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/exceptions.py @@ -0,0 +1,5 @@ +from frontends.gamespy.library.exceptions.general import UniSpyException + + +class SBException(UniSpyException): + pass diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py index c05cea8f7..44808bfa4 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py @@ -145,7 +145,7 @@ def __init__(self, client: Client, request: RequestBase) -> None: self._result_cls = ServerMainListResult def _response_construct(self) -> None: - self._response = ServerMainListResponse(self._request, self._result) + self._response = ServerMainListResponse(self._result) class P2PGroupRoomListHandler(CmdHandlerBase): @@ -158,7 +158,7 @@ def __init__(self, client: Client, request: RequestBase) -> None: self._result_cls = P2PGroupRoomListResult def _response_construct(self) -> None: - self._response = P2PGroupRoomListResponse(self._request, self._result) + self._response = P2PGroupRoomListResponse(self._result) class ServerNetworkInfoListHandler(CmdHandlerBase): @@ -171,7 +171,7 @@ def __init__(self, client: Client, request: RequestBase) -> None: self._result_cls = ServerNetworkInfoListResult def _response_construct(self) -> None: - self._response = ServerNetworkInfoListResponse(self._request, self._result) + self._response = ServerNetworkInfoListResponse(self._result) # class ServerListHandler(CmdHandlerBase): diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py index d0f5546ff..f7dda59df 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py @@ -1,6 +1,5 @@ from typing import TYPE_CHECKING, Optional, cast from frontends.gamespy.library.abstractions.switcher import SwitcherBase -from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.protocols.server_browser.aggregates.exceptions import ( ServerBrowserException, ) @@ -11,6 +10,7 @@ RequestType, ServerListUpdateOption, ) +from frontends.gamespy.protocols.server_browser.v2.aggregations.exceptions import SBException from frontends.gamespy.protocols.server_browser.v2.applications.client import Client from frontends.gamespy.protocols.server_browser.v2.applications.handlers import ( P2PGroupRoomListHandler, @@ -31,7 +31,7 @@ class Switcher(SwitcherBase): def _process_raw_request(self) -> None: if len(self._raw_request) < 4: - raise UniSpyException("Invalid request") + raise SBException("Invalid request") name = self._raw_request[2] if name not in RequestType: self._client.log_debug(f"Request: {name} is not a valid request.") diff --git a/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py index a0cac91e1..f0a4e683a 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py @@ -1,6 +1,5 @@ from socket import inet_ntoa -from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import ( AdHocRequestBase, ServerListUpdateOptionRequestBase, @@ -9,13 +8,14 @@ RequestType, ServerListUpdateOption, ) +from frontends.gamespy.protocols.server_browser.v2.aggregations.exceptions import SBException class ServerListRequest(ServerListUpdateOptionRequestBase): def parse(self) -> None: self.command_name = RequestType(self.raw_request[0]) if self.command_name != RequestType.SERVER_LIST_REQUEST: - raise UniSpyException("raw request is not valid server list request") + raise SBException("raw request is not valid server list request") self.request_version = int(self.raw_request[2]) self.protocol_version = int(self.raw_request[3]) self.encoding_version = int(self.raw_request[4]) @@ -50,7 +50,7 @@ def parse(self) -> None: if self.update_option & ServerListUpdateOption.LIMIT_RESULT_COUNT: if len(remain_data) != 4: - raise UniSpyException("The max number of server is incorrect.") + raise SBException("The max number of server is incorrect.") self.max_servers = int(remain_data[:4][::-1]) diff --git a/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py index 64360a9c2..44cfa0403 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py @@ -92,7 +92,7 @@ def _build_servers_full_info(self): self._servers_info_buffers.extend(group_id_bytes) # get gamespy format dict gamespy_dict = room.get_gamespy_dict() - for key in self._request.keys: + for key in self._result.keys: self._servers_info_buffers.extend(NTS_STRING_FLAG) value = ( gamespy_dict[key] @@ -113,7 +113,7 @@ def __build_servers_full_info(self): for info in self._result.servers_info: header = build_server_info_header(self._result.flag, info) self._servers_info_buffers.extend(header) - for key in self._request.keys: + for key in self._result.keys: self._servers_info_buffers.extend(NTS_STRING_FLAG) if key in info.server_data.keys(): self._servers_info_buffers.extend( diff --git a/src/frontends/gamespy/protocols/web_services/abstractions/contracts.py b/src/frontends/gamespy/protocols/web_services/abstractions/contracts.py index aec2c8fd6..bbd89608f 100644 --- a/src/frontends/gamespy/protocols/web_services/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/web_services/abstractions/contracts.py @@ -1,7 +1,7 @@ import frontends.gamespy.library.abstractions.contracts as lib import xml.etree.ElementTree as ET -from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.protocols.web_services.aggregations.exceptions import WebException from frontends.gamespy.protocols.web_services.aggregations.soap_envelop import SoapEnvelop @@ -29,15 +29,14 @@ class ResponseBase(lib.ResponseBase): """ sending_buffer: str - def __init__(self, request: RequestBase, result: ResultBase) -> None: - assert issubclass(type(request), RequestBase) + def __init__(self, result: ResultBase) -> None: assert issubclass(type(result), ResultBase) if not hasattr(self, "_content"): - raise UniSpyException( + raise WebException( "Soap envelope content must be initialized in response sub class" ) assert isinstance(self._content, SoapEnvelop) - super().__init__(request, result) + super().__init__(result) def build(self) -> None: self.sending_buffer = str(self._content) diff --git a/src/frontends/gamespy/protocols/web_services/modules/auth/abstractions/general.py b/src/frontends/gamespy/protocols/web_services/modules/auth/abstractions/general.py index 214f8ccd4..a9afb80ec 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/auth/abstractions/general.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/abstractions/general.py @@ -40,20 +40,21 @@ class LoginResultBase(lib.ResultBase): profile_nick: str unique_nick: str cdkey_hash: str + version: int + namespace_id: int + partner_code: int class LoginResponseBase(lib.ResponseBase): - _request: LoginRequestBase _result: LoginResultBase _content: SoapEnvelop = SoapEnvelop("http://gamespy.net/AuthService/") _expiretime: int = int( (datetime.datetime.now() + datetime.timedelta(days=1)).timestamp() ) - def __init__(self, request: LoginRequestBase, result: LoginResultBase) -> None: - assert isinstance(request, LoginRequestBase) + def __init__(self, result: LoginResultBase) -> None: assert isinstance(result, LoginResultBase) - super().__init__(request, result) + super().__init__(result) def build(self) -> None: self._build_context() @@ -63,9 +64,9 @@ def _build_context(self): self._content.add("responseCode", "h") self._content.add("certificate") self._content.add("length", self._result.length) - self._content.add("version", self._request.version) - self._content.add("partnercode", self._request.partner_code) - self._content.add("namespaceid", self._request.namespace_id) + self._content.add("version", self._result.version) + self._content.add("partnercode", self._result.partner_code) + self._content.add("namespaceid", self._result.namespace_id) self._content.add("userid", self._result.user_id) self._content.add("profileid", self._result.profile_id) self._content.add("expiretime", self._expiretime) @@ -86,11 +87,11 @@ def __compute_hash(self) -> str: data_to_hash.extend( self._result.length.to_bytes(4, byteorder="little")) data_to_hash.extend( - self._request.version.to_bytes(4, byteorder="little")) + self._result.version.to_bytes(4, byteorder="little")) data_to_hash.extend( - self._request.partner_code.to_bytes(4, byteorder="little")) + self._result.partner_code.to_bytes(4, byteorder="little")) data_to_hash.extend( - self._request.namespace_id.to_bytes(4, byteorder="little")) + self._result.namespace_id.to_bytes(4, byteorder="little")) data_to_hash.extend( self._result.user_id.to_bytes(4, byteorder="little")) data_to_hash.extend( diff --git a/src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py b/src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py index ba77d982e..049054ea4 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py @@ -30,7 +30,7 @@ class LoginProfileHandler(CmdHandlerBase): _result: LoginProfileResult def _response_construct(self) -> None: - self._response = LoginProfileResponse(self._request, self._result) + self._response = LoginProfileResponse(self._result) class LoginProfileWithGameIdHandler(CmdHandlerBase): @@ -38,8 +38,7 @@ class LoginProfileWithGameIdHandler(CmdHandlerBase): _result: LoginProfileResult def _response_construct(self) -> None: - self._response = LoginProfileWithGameIdResponse( - self._request, self._result) + self._response = LoginProfileWithGameIdResponse(self._result) class LoginPs3CertHandler(CmdHandlerBase): @@ -57,7 +56,7 @@ class LoginRemoteAuthHandler(CmdHandlerBase): _result: LoginRemoteAuthResult def _response_construct(self) -> None: - self._response = LoginRemoteAuthResponse(self._request, self._result) + self._response = LoginRemoteAuthResponse(self._result) class LoginRemoteAuthWithGameIdHandler(CmdHandlerBase): @@ -65,8 +64,7 @@ class LoginRemoteAuthWithGameIdHandler(CmdHandlerBase): _result: LoginRemoteAuthResult def _response_construct(self) -> None: - self._response = LoginRemoteAuthWithGameIdResponse( - self._request, self._result) + self._response = LoginRemoteAuthWithGameIdResponse(self._result) class LoginUniqueNickHandler(CmdHandlerBase): @@ -74,12 +72,12 @@ class LoginUniqueNickHandler(CmdHandlerBase): _result: LoginUniqueNickResult def _response_construct(self) -> None: - self._response = LoginUniqueNickResponse(self._request, self._result) + self._response = LoginUniqueNickResponse(self._result) class LoginUniqueNickWithGameIdHandler(CmdHandlerBase): _request: LoginUniqueNickWithGameIdRequest _result: LoginUniqueNickResult + def _response_construct(self) -> None: - self._response = LoginUniqueNickWithGameIdResponse( - self._request, self._result) + self._response = LoginUniqueNickWithGameIdResponse(self._result) diff --git a/src/frontends/gamespy/protocols/web_services/modules/direct2game/handlers/general.py b/src/frontends/gamespy/protocols/web_services/modules/direct2game/handlers/general.py index 990a7139a..f46bc1e1e 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/direct2game/handlers/general.py +++ b/src/frontends/gamespy/protocols/web_services/modules/direct2game/handlers/general.py @@ -23,7 +23,7 @@ def __init__(self, client: Client, request: GetPurchaseHistoryRequest) -> None: super().__init__(client, request) def _response_construct(self) -> None: - self._response = GetPurchaseHistoryResponse(self._request, self._result) + self._response = GetPurchaseHistoryResponse(self._result) class GetStoreAvailabilityHandler(CmdHandlerBase): @@ -35,4 +35,4 @@ def __init__(self, client: Client, request: GetStoreAvailabilityRequest) -> None super().__init__(client, request) def _response_construct(self) -> None: - self._response = GetStoreAvailabilityResponse(self._request, self._result) + self._response = GetStoreAvailabilityResponse(self._result) diff --git a/src/frontends/gamespy/protocols/web_services/modules/sake/abstractions/generals.py b/src/frontends/gamespy/protocols/web_services/modules/sake/abstractions/generals.py index b3ed391d6..7e4c4b87d 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/sake/abstractions/generals.py +++ b/src/frontends/gamespy/protocols/web_services/modules/sake/abstractions/generals.py @@ -53,9 +53,9 @@ class ResultBase(lib.ResultBase): class ResponseBase(lib.ResponseBase): - def __init__(self, request: RequestBase, result: ResultBase) -> None: + def __init__(self, result: ResultBase) -> None: self._content = SoapEnvelop(NAMESPACE) - super().__init__(request, result) + super().__init__(result) class CmdHandlerBase(h.CmdHandlerBase): diff --git a/src/frontends/tests/gamespy/chat/mock_objects.py b/src/frontends/tests/gamespy/chat/mock_objects.py index 37b465fa4..b7791e6d7 100644 --- a/src/frontends/tests/gamespy/chat/mock_objects.py +++ b/src/frontends/tests/gamespy/chat/mock_objects.py @@ -1,6 +1,7 @@ from typing import TYPE_CHECKING, cast from frontends.gamespy.library.abstractions.brocker import BrockerBase from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.protocols.chat.aggregates.enums import WhoRequestType from frontends.gamespy.protocols.chat.applications.handlers import ( CdKeyHandler, GetCKeyHandler, @@ -32,6 +33,7 @@ NamesResultData, NickResult, PartResult, + SetCKeyResult, SetChannelKeyResult, TopicResult, UtmResult, @@ -78,7 +80,8 @@ def create_client() -> Client: conn = ConnectionMock( handler=handler, config=config, t_client=ClientMock, logger=logger ) - create_mock_url(config, CryptHandler, CryptResult(secret_key="test").model_dump()) + create_mock_url(config, CryptHandler, CryptResult( + secret_key="test").model_dump()) create_mock_url( config, LoginHandler, LoginResult(profile_id=1, user_id=1).model_dump() ) @@ -97,7 +100,9 @@ def create_client() -> Client: config, JoinHandler, JoinResult( - joiner_nick_name="nickname", joiner_user_name="username" + joiner_nick_name="nickname", + joiner_user_name="username", + channel_name="#GP!test" ).model_dump(), ) create_mock_url(config, UserHandler, {"message": "ok"}) @@ -112,8 +117,9 @@ def create_client() -> Client: { "nick_name": "test_nick", "user_values": ["data", "key", "value", "data"], - } + }, ], + "cookie": "000", } ).model_dump(), ) @@ -128,11 +134,20 @@ def create_client() -> Client: } ).model_dump(), ) - create_mock_url(config, SetCKeyHandler, {"message": "ok"}) + create_mock_url(config, + SetCKeyHandler, + SetCKeyResult.model_validate( + {"setter_nick_name": "unispy", + "setter_user_name": "unispy", + "channel_name": "#GP!test", + "cookie": "000", + "key_value": {}} + ).model_dump(mode="json")) create_mock_url( config, TopicHandler, - TopicResult(channel_name="test_chan", channel_topic="test").model_dump(), + TopicResult(channel_name="test_chan", + channel_topic="test").model_dump(), ) create_mock_url( config, @@ -142,10 +157,16 @@ def create_client() -> Client: leaver_user_name="username", is_channel_creator=False, channel_name="test_chan", + reason="part" ).model_dump(), ) - create_mock_url(config, NickHandler, NickResult(nick_name="test").model_dump()) - create_mock_url(config, WhoHandler, WhoResult(infos=[]).model_dump()) + create_mock_url(config, NickHandler, NickResult( + nick_name="test").model_dump()) + create_mock_url(config, WhoHandler, WhoResult( + infos=[], + request_type=WhoRequestType.GET_CHANNEL_USER_INFO, + channel_name="#GP!test", + nick_name="unispy").model_dump()) create_mock_url( config, SetChannelKeyHandler, @@ -153,15 +174,18 @@ def create_client() -> Client: setter_nick_name="nickname", setter_user_name="username", channel_name="test", + key_value={} ).model_dump(), ) create_mock_url( - config, GetKeyHandler, GetKeyResult(nick_name="unispy", values=[]).model_dump() + config, GetKeyHandler, GetKeyResult( + nick_name="unispy", values=[], cookie="000").model_dump() ) create_mock_url( config, UTMHandler, - UtmResult(nick_name="unispy", user_name="unispy").model_dump(), + UtmResult(sender_nick_name="unispy", sender_user_name="unispy", + target_name="unispy", message="hello").model_dump(), ) if TYPE_CHECKING: diff --git a/src/frontends/tests/gamespy/game_status/mock_objects.py b/src/frontends/tests/gamespy/game_status/mock_objects.py index bdd0e135f..59d634f54 100644 --- a/src/frontends/tests/gamespy/game_status/mock_objects.py +++ b/src/frontends/tests/gamespy/game_status/mock_objects.py @@ -44,23 +44,29 @@ def create_client() -> Client: config, GetPlayerDataHandler, GetPlayerDataResult( - **{"keyvalues": {"hello": "hello_value", "hi": "hi_value"}} + keyvalues={"hello": "hello_value", "hi": "hi_value"}, + local_id=0, + profile_id=0 ).model_dump(), ) create_mock_url( config, GetProfileIdHandler, - GetProfileIdResult(**{"profile_id": 1}).model_dump(), + GetProfileIdResult.model_validate( + {"profile_id": 1, "local_id": 0}).model_dump(), ) create_mock_url(config, UpdateGameHandler, {"message": "ok"}) create_mock_url( - config, AuthPlayerHandler, AuthPlayerResult(**{"profile_id": 1}).model_dump() + config, AuthPlayerHandler, AuthPlayerResult.model_validate( + {"profile_id": 1, "local_id": 0}).model_dump(mode="json") ) create_mock_url(config, NewGameHandler, {"message": "ok"}) create_mock_url( config, AuthGameHandler, - AuthGameResult(**{"session_key": "123456"}).model_dump(), + AuthGameResult(session_key="123456", + local_id=0, + game_name="gmtest").model_dump(), ) return cast(Client, conn._client) diff --git a/src/frontends/tests/gamespy/game_traffic_relay/handler_tests.py b/src/frontends/tests/gamespy/game_traffic_relay/handler_tests.py index b129907a5..ec03da967 100644 --- a/src/frontends/tests/gamespy/game_traffic_relay/handler_tests.py +++ b/src/frontends/tests/gamespy/game_traffic_relay/handler_tests.py @@ -30,6 +30,6 @@ def test_ping(self): # cookie length check self.assertEqual(len(client1.listener.pool), 1) clients = list(client1.listener.pool.values())[0] - self.assertEqual(clients, 2) + self.assertEqual(len(clients), 2) client1.on_received(ping_raw) pass diff --git a/src/frontends/tests/gamespy/natneg/handler_tests.py b/src/frontends/tests/gamespy/natneg/handler_tests.py index b517579d9..1b8cc7395 100644 --- a/src/frontends/tests/gamespy/natneg/handler_tests.py +++ b/src/frontends/tests/gamespy/natneg/handler_tests.py @@ -55,7 +55,7 @@ def test_init(self): # test response constructing self.assertTrue( handler._response.sending_buffer - == b"\xfd\xfc\x1efj\xb2\x03\x01\x00\x00\x03\t\x01\x00\xc0\xa8\x00\x01\x00\x00" + == b"\xfd\xfc\x1efj\xb2\x03\x01\x00\x00\x03\t\x01\x00\x00\xc0\xa8\x00\x01\x00\x00" ) @responses.activate @@ -81,7 +81,7 @@ def test_address_check(self): self.assertTrue( handler._response.sending_buffer - == b"\xfd\xfc\x1efj\xb2\x03\x0b\x00\x00\x03\t\x01\x00\xc0\xa8\x00\x01\x00\x00" + == b"\xfd\xfc\x1efj\xb2\x03\x0b\x00\x00\x03\t\x01\x00\x00\xc0\xa8\x00\x01\x00\x00" ) @responses.activate @@ -108,7 +108,7 @@ def test_ert_ack(self): handler.handle() self.assertTrue( handler._response.sending_buffer - == b'\xfd\xfc\x1efj\xb2\x03\x03\x00\x00\x03\t\x01\x00\xc0\xa8\x00\x01\x00\x00' + == b'\xfd\xfc\x1efj\xb2\x03\x03\x00\x00\x03\t\x01\x00\x00\xc0\xa8\x00\x01\x00\x00' ) @responses.activate @@ -135,7 +135,7 @@ def test_natify(self): handler.handle() self.assertTrue( handler._response.sending_buffer - == b'\xfd\xfc\x1efj\xb2\x03\x02\x00\x00\x03\t\x01\x00\xc0\xa8\x00\x01\x00\x00' + == b'\xfd\xfc\x1efj\xb2\x03\x02\x00\x00\x03\t\x01\x00\x00\xc0\xa8\x00\x01\x00\x00' ) @responses.activate diff --git a/src/frontends/tests/gamespy/natneg/mock_objects.py b/src/frontends/tests/gamespy/natneg/mock_objects.py index c90697013..0f4d91bde 100644 --- a/src/frontends/tests/gamespy/natneg/mock_objects.py +++ b/src/frontends/tests/gamespy/natneg/mock_objects.py @@ -1,3 +1,4 @@ +from frontends.gamespy.protocols.natneg.contracts.results import AddressCheckResult, ConnectResult, InitResult from frontends.tests.gamespy.library.mock_objects import ( ConnectionMock, LogMock, @@ -10,6 +11,7 @@ from frontends.gamespy.protocols.natneg.applications.handlers import ( AddressCheckHandler, + ConnectHandler, ErtAckHandler, InitHandler, NatifyHandler, @@ -32,9 +34,12 @@ def create_client() -> Client: ) config = CONFIG.servers["NatNegotiation"] - create_mock_url(config, InitHandler, {"message": "ok"}) - create_mock_url(config, AddressCheckHandler, {"message": "ok"}) - create_mock_url(config, AddressCheckHandler, {"message": "ok"}) + create_mock_url(config, InitHandler, InitResult.model_validate( + {"version": 3, "cookie": 777, "public_ip_addr": "127.0.0.1", "public_port": 1234, "use_game_port": False, "port_type": 1, "client_index": 0, "use_game_port": 0}).model_dump(mode="json")) + create_mock_url(config, AddressCheckHandler, AddressCheckResult.model_validate( + {"version": 3, "cookie": 0, "public_ip_addr": "127.0.0.1", "public_port": 1234, "use_game_port": False, "port_type": 0, "client_index": 0, "use_game_port": 0}).model_dump(mode="json")) create_mock_url(config, NatifyHandler, {"message": "ok"}) create_mock_url(config, ErtAckHandler, {"message": "ok"}) + create_mock_url(config, ConnectHandler, ConnectResult.model_validate( + {"version": 3, "cookie": 0, "is_both_client_ready": True, "ip": "192.168.0.1", "port": 7890, "status": 0}).model_dump(mode="json")) return cast(Client, conn._client) diff --git a/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py b/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py index af4f15c60..5451fc56f 100644 --- a/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py +++ b/src/frontends/tests/gamespy/presence_connection_manager/mock_objects.py @@ -30,7 +30,8 @@ def create_client() -> Client: logger=logger, ) config = CONFIG.servers["PresenceConnectionManager"] - create_mock_url(config, NewUserHandler, {"user_id": 0, "profile_id": 0}) + create_mock_url(config, NewUserHandler, { + "user_id": 0, "profile_id": 0, "operation_id": 0}) create_mock_url( config, LoginHandler, @@ -48,6 +49,11 @@ def create_client() -> Client: "sub_profile_id": 0, "banned_flag": False, }, + "operation_id": 0, + "user_data": "", + "type": 0, + "partner_id": 0, + "user_challenge": "xMsHUXuWNXL3KMwmhoQZJrP0RVsArCYT" }, ) diff --git a/src/frontends/tests/gamespy/query_report/mock_objects.py b/src/frontends/tests/gamespy/query_report/mock_objects.py index 9edc1691d..252634db4 100644 --- a/src/frontends/tests/gamespy/query_report/mock_objects.py +++ b/src/frontends/tests/gamespy/query_report/mock_objects.py @@ -20,7 +20,7 @@ def create_client() -> Client: logger=logger) config = CONFIG.servers["QueryReport"] create_mock_url(config, HeartBeatHandler, HeartBeatResult.model_validate( - {"remote_ip": conn.remote_ip, "remote_port": conn.remote_port}).model_dump(mode='json')) + {"remote_ip": conn.remote_ip, "remote_port": conn.remote_port, "instant_key": "123", "command_name": 3}).model_dump(mode='json')) create_mock_url(config, AvailableHandler, {"message": "ok"}) create_mock_url(config, KeepAliveHandler, {"message": "ok"}) return cast(Client, conn._client) diff --git a/src/frontends/tests/gamespy/server_browser/mock_objects.py b/src/frontends/tests/gamespy/server_browser/mock_objects.py index 56ee022f5..13a8845af 100644 --- a/src/frontends/tests/gamespy/server_browser/mock_objects.py +++ b/src/frontends/tests/gamespy/server_browser/mock_objects.py @@ -41,6 +41,7 @@ def create_v2_client() -> Client: "flag": 64, "game_secret_key": "123567", "servers_info": [], + "keys": [] } ).model_dump(mode="json"), ) From 37a617d70aa14e395db58aca36d58775c8d2ee3b Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Wed, 24 Sep 2025 08:28:22 +0000 Subject: [PATCH 206/231] Update: add unispy_backends to devcontainer hosts file --- src/.devcontainer/devcontainer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/.devcontainer/devcontainer.json b/src/.devcontainer/devcontainer.json index 5ce0d2e51..a14078b1f 100644 --- a/src/.devcontainer/devcontainer.json +++ b/src/.devcontainer/devcontainer.json @@ -25,7 +25,7 @@ 80 ], // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "pip3 install --user -r requirements.txt" + "postCreateCommand": "echo '127.0.0.1 unispy_backends' | sudo tee -a /etc/hosts && pip3 install --user -r requirements.txt" // Configure tool-specific properties. // "customizations": {}, // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. From c52251e8c09831cb52eb706325330c81e3e8c1ce Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Thu, 25 Sep 2025 09:29:40 +0000 Subject: [PATCH 207/231] Fix: enctypeX initialization byte operation issue --- .../chat/{brocker_manager.py => brocker.py} | 7 +- .../protocols/gamespy/chat/handlers.py | 18 +- .../protocols/gamespy/query_report/broker.py | 77 +++++ .../protocols/gamespy/query_report/data.py | 48 ++- .../gamespy/query_report/handlers.py | 10 + .../gamespy/query_report/requests.py | 6 +- .../gamespy/server_browser/handlers.py | 5 +- .../gamespy/server_browser/requests.py | 2 +- src/backends/routers/gamespy/chat.py | 2 +- src/backends/routers/gamespy/query_report.py | 28 +- .../routers/gamespy/server_browser.py | 2 +- .../gamespy/query_report/handler_tests.py | 12 +- .../gamespy/library/abstractions/client.py | 5 +- .../protocols/chat/applications/handlers.py | 9 +- .../protocols/chat/contracts/responses.py | 10 +- .../protocols/chat/contracts/results.py | 6 +- .../query_report/applications/client.py | 9 +- .../query_report/v2/contracts/requests.py | 27 +- .../v2/abstractions/contracts.py | 9 +- .../v2/abstractions/handlers.py | 10 +- .../v2/aggregations/encryption.py | 285 +++++++++++++----- .../v2/aggregations/server_info_builder.py | 16 +- .../server_browser/v2/applications/client.py | 10 +- .../v2/applications/handlers.py | 70 +---- .../v2/applications/switcher.py | 2 +- .../server_browser/v2/contracts/requests.py | 2 +- .../gamespy/query_report/request_tests.py | 3 + .../gamespy/server_browser/encrypt_test.py | 20 ++ 28 files changed, 497 insertions(+), 213 deletions(-) rename src/backends/protocols/gamespy/chat/{brocker_manager.py => brocker.py} (93%) create mode 100644 src/backends/protocols/gamespy/query_report/broker.py create mode 100644 src/frontends/tests/gamespy/server_browser/encrypt_test.py diff --git a/src/backends/protocols/gamespy/chat/brocker_manager.py b/src/backends/protocols/gamespy/chat/brocker.py similarity index 93% rename from src/backends/protocols/gamespy/chat/brocker_manager.py rename to src/backends/protocols/gamespy/chat/brocker.py index 4b38ce28e..4583175c2 100644 --- a/src/backends/protocols/gamespy/chat/brocker_manager.py +++ b/src/backends/protocols/gamespy/chat/brocker.py @@ -8,7 +8,7 @@ from sqlalchemy.orm import Session -class ClientManager: +class WebsocketManager: """ current: single server mode client1 -> frontend1 -> backend1 (rest api) @@ -63,7 +63,8 @@ def process_message(self, message: dict) -> BrockerMessage: async def broadcast(self, message: BrockerMessage, ws_client: WebSocket): exclude_addr = self.get_address_str(ws_client) with Session(ENGINE) as session: - wss = data.get_websocket_addr_by_channel_name(message.channel_name, session) + wss = data.get_websocket_addr_by_channel_name( + message.channel_name, session) if exclude_addr in wss: wss.remove(exclude_addr) for ws_addr in wss: @@ -73,4 +74,4 @@ async def broadcast(self, message: BrockerMessage, ws_client: WebSocket): self.logger.info(f"[cast] [send] {message.model_dump_json()}") -MANAGER = ClientManager() +MANAGER = WebsocketManager() diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index 5a0f0ec79..3e165c9cb 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -70,6 +70,7 @@ NickResult, NoticeResult, PartResult, + PingResult, PrivateResult, SetCKeyResult, SetChannelKeyResult, @@ -164,6 +165,17 @@ def __init__(self, request: RequestBase) -> None: # region General +class PingHandler(HandlerBase): + + def _result_construct(self) -> None: + assert self._user is not None + assert isinstance(self._user.nick_name, str) + assert isinstance(self._user.user_name, str) + self._result = PingResult( + nick_name=self._user.nick_name, + user_name=self._user.user_name + ) + class CdKeyHandler(HandlerBase): _request: CdkeyRequest @@ -540,14 +552,16 @@ def _result_construct(self) -> None: assert isinstance(d.nick_name, str) assert isinstance(d.key_values, dict) info = GetCKeyResult.GetCKeyInfos( - nick_name=d.nick_name, user_values=list(d.key_values.values()) + nick_name=d.nick_name, + key_values=d.key_values ) infos.append(info) self._result = GetCKeyResult( infos=infos, channel_name=self._request.channel_name, - cookie=self._request.cookie + cookie=self._request.cookie, + keys=self._request.keys ) diff --git a/src/backends/protocols/gamespy/query_report/broker.py b/src/backends/protocols/gamespy/query_report/broker.py new file mode 100644 index 000000000..4583175c2 --- /dev/null +++ b/src/backends/protocols/gamespy/query_report/broker.py @@ -0,0 +1,77 @@ +from logging import Logger +import logging +from fastapi import WebSocket +from backends.library.database.pg_orm import ENGINE +from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage + +import backends.protocols.gamespy.chat.data as data +from sqlalchemy.orm import Session + + +class WebsocketManager: + """ + current: single server mode + client1 -> frontend1 -> backend1 (rest api) + client2 <- | <- + client3 <- | + """ + + """ + future: distributed mode + client1 -> frontend1 -> backend1 (rest api) + client2 <- | <- + client3 <- | + -> (websocket) -> redis + client2 -> frontend2 <- backend2 <- | + client3 -> frontend3 <- backend3 <- | + client4 -> frontend4 <- backend4 <- | + """ + client_pool: dict[str, WebSocket] + logger: Logger + + def __init__(self) -> None: + self.client_pool = {} + self.logger = logging.getLogger("backend") + + def get_address_str(self, ws: WebSocket) -> str: + assert ws.client is not None + ws_address = f"{ws.client.host}:{ws.client.port}" + return ws_address + + def connect(self, ws: WebSocket): + assert ws.client is not None + ws_address = self.get_address_str(ws) + self.client_pool[ws_address] = ws + + def disconnect(self, ws: WebSocket): + assert ws.client is not None + ws_address = self.get_address_str(ws) + if ws_address in self.client_pool: + del self.client_pool[ws_address] + + def get_websocket(self, ws_address: str) -> WebSocket | None: + if ws_address in self.client_pool: + return self.client_pool[ws_address] + + def process_message(self, message: dict) -> BrockerMessage: + self.logger.info(f"[cast] [recv] {message}") + msg = BrockerMessage.model_validate(message) + return msg + + # create redis pubsub to share message cross all backends + # currently we simply implement without redis pubsub + async def broadcast(self, message: BrockerMessage, ws_client: WebSocket): + exclude_addr = self.get_address_str(ws_client) + with Session(ENGINE) as session: + wss = data.get_websocket_addr_by_channel_name( + message.channel_name, session) + if exclude_addr in wss: + wss.remove(exclude_addr) + for ws_addr in wss: + if ws_addr in self.client_pool: + ws = self.client_pool[ws_addr] + await ws.send_json(message.model_dump_json()) + self.logger.info(f"[cast] [send] {message.model_dump_json()}") + + +MANAGER = WebsocketManager() diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index d1addf7d0..d4dc1127d 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -20,6 +20,7 @@ from datetime import datetime from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus +from frontends.gamespy.protocols.server_browser.v2.aggregations.exceptions import SBException def get_all_groups() -> dict: @@ -134,6 +135,14 @@ def get_server_info_with_game_name( return result +def get_secret_key(game_name: str, session: Session) -> str: + result = session.query(Games.secretkey).where( + Games.gamename == game_name).first() + if result is None: + raise SBException(f"{game_name} secret key not found in database") + return result[0] + + def get_server_info_list_with_game_name( game_name: str, session: Session ) -> list[GameServerInfo]: @@ -144,7 +153,29 @@ def get_server_info_list_with_game_name( ) data = [] for s in result: - data.append(GameServerInfo(**s.__dict__)) + assert isinstance(s.server_id, UUID) + assert isinstance(s.host_ip_address, str) + assert isinstance(s.instant_key, str) + assert isinstance(s.game_name, str) + assert isinstance(s.query_report_port, int) + assert isinstance(s.update_time, datetime) + assert isinstance(s.status, GameServerStatus) + assert isinstance(s.server_data, dict) + assert isinstance(s.player_data, list) + assert isinstance(s.team_data, list) + + data.append(GameServerInfo( + server_id=s.server_id, + host_ip_address=s.host_ip_address, + instant_key=s.instant_key, + game_name=s.game_name, + query_report_port=s.query_report_port, + last_heart_beat_received_time=s.update_time, + status=s.status, + server_data=s.server_data, + player_data=s.player_data, + team_data=s.team_data + )) return data @@ -186,9 +217,9 @@ def update_game_server( game_name: str, query_report_port: int, server_status: GameServerStatus, - player_data: list[dict[str, object]], - server_data: dict[str, object], - team_data: list[dict[str, object]], + player_data: list[dict[str, object]] | None, + server_data: dict[str, object] | None, + team_data: list[dict[str, object]] | None, session: Session, ) -> None: cache.instant_key = instant_key # type: ignore @@ -198,9 +229,12 @@ def update_game_server( cache.query_report_port = query_report_port # type: ignore cache.update_time = datetime.now() # type: ignore cache.status = server_status # type: ignore - cache.player_data = player_data # type: ignore - cache.server_data = server_data # type: ignore - cache.team_data = team_data # type: ignore + if player_data is not None: + cache.player_data = player_data # type: ignore + if server_data is not None: + cache.server_data = server_data # type: ignore + if team_data is not None: + cache.team_data = team_data # type: ignore session.commit() diff --git a/src/backends/protocols/gamespy/query_report/handlers.py b/src/backends/protocols/gamespy/query_report/handlers.py index 6c5c04cdb..344776232 100644 --- a/src/backends/protocols/gamespy/query_report/handlers.py +++ b/src/backends/protocols/gamespy/query_report/handlers.py @@ -41,6 +41,16 @@ def _data_operate(self) -> None: str(self._request.instant_key), self._session ) if cache is None: + # todo check whether these data can be null at first heartbeat + if self._request.player_data is None: + raise QRException( + "player data in first heartbeat can not be null") + if self._request.server_data is None: + raise QRException( + "server data in first heartbeat can not be null") + if self._request.team_data is None: + raise QRException( + "team data in first heartbeat can not be null") cache = GameServerCaches( instant_key=self._request.instant_key, server_id=self._request.server_id, diff --git a/src/backends/protocols/gamespy/query_report/requests.py b/src/backends/protocols/gamespy/query_report/requests.py index e9a9e8ffd..a9e3d697c 100644 --- a/src/backends/protocols/gamespy/query_report/requests.py +++ b/src/backends/protocols/gamespy/query_report/requests.py @@ -33,9 +33,9 @@ class ClientMessageRequest(RequestBase): class HeartBeatRequest(RequestBase): - server_data: dict[str, object] - player_data: list[dict[str, object]] - team_data: list[dict[str, object]] + server_data: dict[str, object] | None + player_data: list[dict[str, object]] | None + team_data: list[dict[str, object]] | None server_status: GameServerStatus group_id: int | None game_name: str diff --git a/src/backends/protocols/gamespy/server_browser/handlers.py b/src/backends/protocols/gamespy/server_browser/handlers.py index 80b6a00ef..c24be5b12 100644 --- a/src/backends/protocols/gamespy/server_browser/handlers.py +++ b/src/backends/protocols/gamespy/server_browser/handlers.py @@ -77,8 +77,11 @@ def _result_construct(self) -> None: class ServerMainListHandler(HandlerBase): _request: ServerListRequest _caches: list[GameServerInfo] + _secret_key: str def _data_operate(self): + self._secret_key = data.get_secret_key( + self._request.game_name, self._session) self._caches = data.get_server_info_list_with_game_name( self._request.game_name, self._session ) @@ -92,7 +95,7 @@ def _result_construct(self): self._result = ServerMainListResult( client_remote_ip=self._request.client_ip, flag=GameServerFlags.HAS_KEYS_FLAG, - game_secret_key="", + game_secret_key=self._secret_key, servers_info=self._caches, keys=self._request.keys ) diff --git a/src/backends/protocols/gamespy/server_browser/requests.py b/src/backends/protocols/gamespy/server_browser/requests.py index b2ba1ad0d..5d91e99e9 100644 --- a/src/backends/protocols/gamespy/server_browser/requests.py +++ b/src/backends/protocols/gamespy/server_browser/requests.py @@ -19,7 +19,7 @@ class ServerListUpdateOptionRequestBase(RequestBase): client_challenge: str update_option: ServerListUpdateOption keys: list[str] - filter: str + filter: str | None = None max_servers: int | None = None source_ip: str | None = None query_options: int | None = None diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 234e7cfc5..aa79e7a5d 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -1,5 +1,5 @@ from backends.library.abstractions.contracts import OKResponse -from backends.protocols.gamespy.chat.brocker_manager import MANAGER +from backends.protocols.gamespy.chat.brocker import MANAGER from backends.protocols.gamespy.chat.handlers import ( CdKeyHandler, CryptHandler, diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index e62742393..532d32153 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -1,13 +1,27 @@ -from fastapi import APIRouter +from fastapi import APIRouter, WebSocket, WebSocketDisconnect -from backends.protocols.gamespy.presence_connection_manager.requests import KeepAliveRequest -from backends.protocols.gamespy.query_report.handlers import AvaliableHandler, Heartbeathandler -from backends.protocols.gamespy.query_report.requests import AvaliableRequest, ChallengeRequest, ClientMessageRequest, EchoRequest, HeartBeatRequest +from backends.protocols.gamespy.query_report.broker import MANAGER +from backends.protocols.gamespy.query_report.handlers import AvaliableHandler, Heartbeathandler, KeepAliveHandler +from backends.protocols.gamespy.query_report.requests import AvaliableRequest, ChallengeRequest, ClientMessageRequest, EchoRequest, HeartBeatRequest, KeepAliveRequest from backends.urls import QUERY_REPORT router = APIRouter() - +@router.websocket(f"{QUERY_REPORT}/ws") +async def websocket_endpoint(ws: WebSocket): + await ws.accept() + if isinstance(ws, WebSocket) and ws.client is not None: + MANAGER.connect(ws) + try: + while True: + data = await ws.receive_json() + msg = MANAGER.process_message(data) + await MANAGER.broadcast(msg, ws) + except WebSocketDisconnect: + if ws.client is not None: + MANAGER.disconnect(ws) + # todo remove chat info by websocket + print("Client disconnected") @router.post(f"{QUERY_REPORT}/HeartBeatHandler") def heartbeat(request: HeartBeatRequest): handler = Heartbeathandler(request) @@ -39,7 +53,9 @@ def echo(request: EchoRequest): @router.post(f"{QUERY_REPORT}/KeepAliveHandler") def keep_alive(request: KeepAliveRequest): - raise NotImplementedError() + handler = KeepAliveHandler(request) + handler.handle() + return handler.response if __name__ == "__main__": diff --git a/src/backends/routers/gamespy/server_browser.py b/src/backends/routers/gamespy/server_browser.py index e31164e1f..d553c89dd 100644 --- a/src/backends/routers/gamespy/server_browser.py +++ b/src/backends/routers/gamespy/server_browser.py @@ -37,7 +37,7 @@ def server_info(request: ServerInfoRequest): return handler.response -@router.post(f"{SERVER_BROWSER_V2}/ServerListHandler") +@router.post(f"{SERVER_BROWSER_V2}/ServerMainListHandler") def server_list(request: ServerListRequest): handler = ServerMainListHandler(request) handler.handle() diff --git a/src/backends/tests/gamespy/query_report/handler_tests.py b/src/backends/tests/gamespy/query_report/handler_tests.py index eeec4fc41..4d57576ec 100644 --- a/src/backends/tests/gamespy/query_report/handler_tests.py +++ b/src/backends/tests/gamespy/query_report/handler_tests.py @@ -1,7 +1,7 @@ import unittest -from backends.protocols.gamespy.query_report.handlers import Heartbeathandler, AvaliableHandler -from backends.protocols.gamespy.query_report.requests import HeartBeatRequest, AvaliableRequest +from backends.protocols.gamespy.query_report.handlers import Heartbeathandler, AvaliableHandler, KeepAliveHandler +from backends.protocols.gamespy.query_report.requests import HeartBeatRequest, AvaliableRequest, KeepAliveRequest class HandlerTests(unittest.IsolatedAsyncioTestCase): @@ -20,3 +20,11 @@ def test_available(self): handler = AvaliableHandler(new_req) handler.handle() pass + + def test_keep_alive(self): + request = {"raw_request": "\bg\\xd4\\xcbl", "command_name": 8, "instant_key": "1741998956", + "client_ip": "172.19.0.4", "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "client_port": 11111} + new_req = KeepAliveRequest(**request) + handler = KeepAliveHandler(new_req) + handler.handle() + self.assertIsNotNone(new_req.instant_key) diff --git a/src/frontends/gamespy/library/abstractions/client.py b/src/frontends/gamespy/library/abstractions/client.py index 76f9668bc..bb5c4df5a 100644 --- a/src/frontends/gamespy/library/abstractions/client.py +++ b/src/frontends/gamespy/library/abstractions/client.py @@ -82,10 +82,9 @@ def decrypt_message(self, buffer: bytes) -> bytes: else: return buffer - def send(self, response: "ResponseBase|None") -> None: + def send(self, response: "ResponseBase") -> None: from frontends.gamespy.library.abstractions.contracts import ResponseBase - if response is None: - return + assert response is not None assert issubclass(type(response), ResponseBase) response.build() sending_buffer = response.sending_buffer diff --git a/src/frontends/gamespy/protocols/chat/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py index 479efaf14..2fefe01e6 100644 --- a/src/frontends/gamespy/protocols/chat/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -188,14 +188,12 @@ def __init__(self, client: ClientBase, request: NickRequest): assert isinstance(request, NickRequest) super().__init__(client, request) self._result_cls = NickResult + self._response_cls = NickResponse def _data_operate(self) -> None: super()._data_operate() self._client.info.nick_name = self._request.nick_name - def _response_construct(self) -> None: - self._response = NickResponse(self._result) - class PingHandler(CmdHandlerBase): _request: PingRequest @@ -204,10 +202,9 @@ class PingHandler(CmdHandlerBase): def __init__(self, client: ClientBase, request: PingRequest): assert isinstance(request, PingRequest) super().__init__(client, request) - raise NotImplementedError() + self._result_cls = PingResult + self._response_cls = PingResponse - def _response_construct(self) -> None: - self._response = PingResponse(self._result) class QuitHandler(CmdHandlerBase): diff --git a/src/frontends/gamespy/protocols/chat/contracts/responses.py b/src/frontends/gamespy/protocols/chat/contracts/responses.py index fdcc3b0b8..ed6b45b8c 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/responses.py +++ b/src/frontends/gamespy/protocols/chat/contracts/responses.py @@ -146,9 +146,10 @@ def __init__( self._result = result def build(self) -> None: - self.sending_buffer = ( - f":{self._result.requester_irc_prefix} {ResponseCode.PONG.value}\r\n" + requester_irc_prefix = ResponseBase.build_irc_user_prefix( + self._result.nick_name, self._result.user_name ) + self.sending_buffer = f":{requester_irc_prefix} {ResponseCode.PONG.value}\r\n" class UserIPResponse(ResponseBase): @@ -234,7 +235,9 @@ def __init__( def build(self) -> None: self.sending_buffer = "" for info in self._result.infos: - self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.GETCKEY.value} * {self._result.channel_name} {info.nick_name} {self._result.cookie} {info.user_values}\r\n" # noqa + value_str = ChannelResponseBase.build_value_str( + self._result.keys, info.key_values) + self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.GETCKEY.value} * {self._result.channel_name} {info.nick_name} {self._result.cookie} {value_str}\r\n" # noqa self.sending_buffer += f"{SERVER_DOMAIN} {ResponseCode.ENDGETCKEY.value} * {self._result.channel_name} {self._result.cookie} :End Of GETCKEY.\r\n" # noqa @@ -343,7 +346,6 @@ def __init__(self, result: SetChannelKeyResult) -> None: super().__init__(result) assert isinstance(result, SetChannelKeyResult) - def build(self) -> None: setter_irc_prefix = ResponseBase.build_irc_user_prefix( self._result.setter_nick_name, self._result.setter_user_name diff --git a/src/frontends/gamespy/protocols/chat/contracts/results.py b/src/frontends/gamespy/protocols/chat/contracts/results.py index 004e4d5b0..09b3b5c69 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/results.py +++ b/src/frontends/gamespy/protocols/chat/contracts/results.py @@ -38,7 +38,8 @@ class NickResult(ResultBase): class PingResult(ResultBase): - requester_irc_prefix: str + nick_name:str + user_name:str class QuitResult(ResultBase): @@ -92,11 +93,12 @@ class GetChannelKeyResult(ResultBase): class GetCKeyResult(ResultBase): class GetCKeyInfos(BaseModel): nick_name: str - user_values: list[str] + key_values: dict infos: list[GetCKeyInfos] """ nick_name:str, user_values:str""" channel_name: str cookie: str + keys: list[str] class ModeResult(ResultBase): diff --git a/src/frontends/gamespy/protocols/query_report/applications/client.py b/src/frontends/gamespy/protocols/query_report/applications/client.py index e07bad46e..fba3c4b31 100644 --- a/src/frontends/gamespy/protocols/query_report/applications/client.py +++ b/src/frontends/gamespy/protocols/query_report/applications/client.py @@ -1,11 +1,18 @@ from frontends.gamespy.library.abstractions.client import ClientBase +from frontends.gamespy.library.abstractions.connections import ConnectionBase +from frontends.gamespy.library.configs import ServerConfig +from frontends.gamespy.library.log.log_manager import LogWriter # import servers.query_report.v1 class Client(ClientBase): pool: dict[str, "Client"] - is_log_raw: bool = True + is_log_raw: bool + + def __init__(self, connection: ConnectionBase, server_config: ServerConfig, logger: LogWriter): + super().__init__(connection, server_config, logger) + self.is_log_raw = True def _create_switcher(self, buffer: bytes): from frontends.gamespy.protocols.query_report.v2.applications.switcher import Switcher as V2CmdSwitcher diff --git a/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py b/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py index b34682504..4bd79acdc 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py +++ b/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py @@ -51,15 +51,21 @@ def __init__(self, raw_request: bytes | None = None) -> None: class HeartBeatRequest(RequestBase): - server_data: dict[str, str] - player_data: list[dict[str, str]] - team_data: list[dict[str, str]] + server_data: dict[str, str] | None + player_data: list[dict[str, str]] | None + team_data: list[dict[str, str]] | None server_status: GameServerStatus group_id: int | None remote_ip: str remote_port: int game_name: str + def __init__(self, raw_request: bytes) -> None: + super().__init__(raw_request) + self.server_data = None + self.player_data = None + self.team_data = None + def parse(self): super().parse() player_pos, team_pos = 0, 0 @@ -113,13 +119,6 @@ def parse(self): else: raise QRException("HeartBeat request is invalid.") - if "groupid" in self.server_data: - group_id = 0 - if not int(self.server_data["groupid"], group_id): - raise QRException("GroupId is invalid.") - self.group_id = group_id - else: - self.group_id = None def parse_server_data(self, server_data_str: str): self.server_data = {} @@ -142,6 +141,14 @@ def parse_server_data(self, server_data_str: str): else: self.server_status = GameServerStatus( int(self.server_data["statechanged"])) + + if "groupid" in self.server_data: + group_id = 0 + if not int(self.server_data["groupid"], group_id): + raise QRException("GroupId is invalid.") + self.group_id = group_id + else: + self.group_id = None def parse_player_data(self, player_data_str: str): self.player_data = [] diff --git a/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py index 2b46f62c5..8f34dbfe9 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py @@ -61,13 +61,14 @@ class ServerListUpdateOptionRequestBase(RequestBase): client_challenge: str update_option: ServerListUpdateOption keys: list[str] - filter: str + filter: str | None source_ip: str max_servers: int def __init__(self, raw_request: bytes): assert isinstance(raw_request, bytes) super().__init__(raw_request) + self.filter = None class ServerListUpdateOptionResultBase(ResultBase): @@ -96,14 +97,16 @@ def build(self) -> None: ip_to_4_bytes(self._result.client_remote_ip)) self._servers_info_buffers.extend( QUERY_REPORT_DEFAULT_PORT.to_bytes(4)) + assert len(self._servers_info_buffers) == 22 - def build_crypt_header(self) -> list: + def build_crypt_header(self) -> bytearray: # cryptHeader have 14 bytes, when we encrypt data we need skip the first 14 bytes - crypt_header = [] + crypt_header = bytearray() crypt_header.append(2 ^ 0xEC) crypt_header.extend([0, 0]) # message length? crypt_header.append(len(SERVER_CHALLENGE) ^ 0xEA) crypt_header.extend(get_bytes(SERVER_CHALLENGE)) + assert len(crypt_header) == 14 return crypt_header def build_server_keys(self) -> None: diff --git a/src/frontends/gamespy/protocols/server_browser/v2/abstractions/handlers.py b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/handlers.py index 79480b7e2..465ce27bf 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/abstractions/handlers.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/handlers.py @@ -32,16 +32,14 @@ class ServerListUpdateOptionHandlerBase(CmdHandlerBase): _response: ServerListUpdateOptionResponseBase def _data_operate(self) -> None: - # send to backend to query the game secret key - secretkey = None - if secretkey is None: - raise NotImplementedError("not implemented") - self._client.info.game_secret_key = secretkey + # query game secret key + super()._data_operate() + self._client.info.client_challenge = self._request.client_challenge + self._client.info.game_secret_key = self._result.game_secret_key # use secret key to construct _client.crypto self._client.crypto = EnctypeX( self._client.info.game_secret_key, self._client.info.client_challenge ) - pass def _response_send(self) -> None: self._response.build() diff --git a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/encryption.py b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/encryption.py index b2b03640f..069cd0abc 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/encryption.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/encryption.py @@ -3,24 +3,140 @@ SERVER_CHALLENGE = "0000000000" +class Byte: + value: int + + def __init__(self, value): + if value > 255: + raise ValueError("byte should be in 0 to 256") + self.value = value + + def _clamp(self, value): + """clamp value in 0 to 255""" + return value % 256 + + def __add__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must on Byte object") + new = self._clamp(self.value + other.value) + return Byte(new) + + def __sub__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must on Byte object") + new = self._clamp(self.value - other.value) + return Byte(new) + + def __mul__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must on Byte object") + new = self._clamp(self.value * other.value) + return Byte(new) + + def __truediv__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must on Byte object") + if other.value == 0: + raise ValueError("Cannot divide by zero") + new = self._clamp(self.value // other.value) + return Byte(new) + + def __xor__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must on Byte object") + new = self._clamp(self.value ^ other.value) + return Byte(new) + + def __lt__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must be on Byte object") + return self.value < other.value + + def __le__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must be on Byte object") + return self.value <= other.value + + def __gt__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must be on Byte object") + return self.value > other.value + + def __ge__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must be on Byte object") + return self.value >= other.value + + def __eq__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must be on Byte object") + return self.value == other.value + + def __lshift__(self, other): + """Perform in-place left shift operation.""" + if not isinstance(other, Byte): + raise TypeError("operation must be on Byte object") + if other < Byte(0): + raise ValueError("Shift amount must be non-negative") + return Byte(self.value << other.value) + + def __and__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must be on Byte object") + return Byte(self.value & other.value) + + def __mod__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must be on Byte object") + return Byte(self.value % other.value) + + def __repr__(self): + return f"Byte({self.value})" + + @staticmethod + def from_bytes(data: bytes | bytearray): + temp = [] + for d in data: + temp.append(Byte(d)) + return temp + + class EncryptionParameters: + register: list[Byte] + index_0: Byte + index_1: Byte + index_2: Byte + index_3: Byte + index_4: Byte + def __init__(self): - self.Register = bytearray(256) - self.Index0 = 0 - self.Index1 = 0 - self.Index2 = 0 - self.Index3 = 0 - self.Index4 = 0 + self.register = [Byte(i) for i in range(256)] + self.index_0 = Byte(0) + self.index_1 = Byte(0) + self.index_2 = Byte(0) + self.index_3 = Byte(0) + self.index_4 = Byte(0) + + +def clamp(value: int) -> int: + return value % 256 class EnctypeX(EncryptBase): + _enc_params: EncryptionParameters + _clientChallenge: list[Byte] + _serverChallenge: list[Byte] + _secretKey: list[Byte] + def __init__(self, secretKey: str, clientChallenge: str): assert isinstance(secretKey, str) assert isinstance(clientChallenge, str) - self._encParams = EncryptionParameters() - self._clientChallenge = bytearray(clientChallenge.encode("ascii")) - self._serverChallenge = bytearray(SERVER_CHALLENGE.encode("ascii")) - self._secretKey = bytearray(secretKey.encode("ascii")) + self._enc_params = EncryptionParameters() + self._clientChallenge = Byte.from_bytes( + clientChallenge.encode("ascii")) + self._serverChallenge = Byte.from_bytes( + SERVER_CHALLENGE.encode("ascii")) + self._secretKey = Byte.from_bytes(secretKey.encode("ascii")) self.init_encryption_algorithm() def init_encryption_algorithm(self): @@ -28,10 +144,11 @@ def init_encryption_algorithm(self): raise ValueError("Client challenge length not valid!") for i in range(len(self._serverChallenge)): - tempIndex0 = i * self._secretKey[i % len(self._secretKey)] % 8 - tempIndex1 = i % 8 - bitwiseResult = self._clientChallenge[tempIndex1] ^ self._serverChallenge[i] - self._clientChallenge[tempIndex0] ^= bitwiseResult & 0xFF + temp_index_0 = Byte( + i) * self._secretKey[i % len(self._secretKey)] % Byte(8) + temp_index1 = Byte(i % 8) + bitwise_result = self._clientChallenge[temp_index1.value] ^ self._serverChallenge[i] + self._clientChallenge[temp_index_0.value] ^= bitwise_result self.init_encryption_parameters() @@ -40,95 +157,103 @@ def init_encryption_parameters(self): self.non_challenge_mapping_init() return - self._encParams.Register = bytearray(range(256)) - toSwap = 0 - keyPosition = 0 - randomSum = 0 + to_swap = Byte(0) + key_position = Byte(0) + random_sum = Byte(0) for i in range(255, 0, -1): - toSwap = self.index_position_generation(i, randomSum, keyPosition) - swapTemp = self._encParams.Register[i] - self._encParams.Register[i] = self._encParams.Register[toSwap] - self._encParams.Register[toSwap] = swapTemp + random_sum, key_position, to_swap = self.index_position_generation( + Byte(i), random_sum, key_position) + swap_temp = self._enc_params.register[i] + self._enc_params.register[i] = self._enc_params.register[to_swap.value] + self._enc_params.register[to_swap.value] = swap_temp - self._encParams.Index0 = self._encParams.Register[1] - self._encParams.Index1 = self._encParams.Register[3] - self._encParams.Index2 = self._encParams.Register[5] - self._encParams.Index3 = self._encParams.Register[7] - self._encParams.Index4 = self._encParams.Register[randomSum] + self._enc_params.index_0 = self._enc_params.register[1] + self._enc_params.index_1 = self._enc_params.register[3] + self._enc_params.index_2 = self._enc_params.register[5] + self._enc_params.index_3 = self._enc_params.register[7] + self._enc_params.index_4 = self._enc_params.register[random_sum.value] def non_challenge_mapping_init(self): - self._encParams.Index0 = 1 - self._encParams.Index1 = 3 - self._encParams.Index2 = 5 - self._encParams.Index3 = 7 - self._encParams.Index4 = 11 - self._encParams.Register = bytearray(range(255, -1, -1)) + self._enc_params.index_0 = Byte(1) + self._enc_params.index_1 = Byte(3) + self._enc_params.index_2 = Byte(5) + self._enc_params.index_3 = Byte(7) + self._enc_params.index_4 = Byte(11) + self._enc_params.register = [Byte(i) for i in range(256, 0, -1)] def byte_shift(self, b): - self._encParams.Index1 += self._encParams.Register[self._encParams.Index0] - swapTempStorage = self._encParams.Register[self._encParams.Index4] - self._encParams.Register[self._encParams.Index4] = self._encParams.Register[ - self._encParams.Index1 + self._enc_params.index_1 += self._enc_params.register[self._enc_params.index_0.value] + swap_temp_storage = self._enc_params.register[self._enc_params.index_4.value] + self._enc_params.register[self._enc_params.index_4.value] = self._enc_params.register[ + self._enc_params.index_1.value ] - self._encParams.Register[self._encParams.Index1] = self._encParams.Register[ - self._encParams.Index3 + self._enc_params.register[self._enc_params.index_1.value] = self._enc_params.register[ + self._enc_params.index_3.value ] - self._encParams.Register[self._encParams.Index3] = self._encParams.Register[ - self._encParams.Index0 + self._enc_params.register[self._enc_params.index_3.value] = self._enc_params.register[ + self._enc_params.index_0.value ] - self._encParams.Register[self._encParams.Index0] = swapTempStorage - self._encParams.Index2 += self._encParams.Register[swapTempStorage] + self._enc_params.register[self._enc_params.index_0.value] = swap_temp_storage + self._enc_params.index_2 += self._enc_params.register[swap_temp_storage.value] - self._encParams.Index4 = ( - b - ^ self._encParams.Register[ + self._enc_params.index_4 = ( + b ^ self._enc_params.register[ ( - self._encParams.Register[self._encParams.Index2] - + self._encParams.Register[self._encParams.Index0] - ) - & 0xFF - ] - ^ self._encParams.Register[ - self._encParams.Register[ + self._enc_params.register[self._enc_params.index_2.value] + + self._enc_params.register[self._enc_params.index_0.value] + ).value + ] ^ self._enc_params.register[ + self._enc_params.register[ ( - self._encParams.Register[self._encParams.Index3] - + self._encParams.Register[self._encParams.Index4] - + self._encParams.Register[self._encParams.Index1] - ) - & 0xFF - ] + self._enc_params.register[self._enc_params.index_3.value] + + self._enc_params.register[self._enc_params.index_4.value] + + self._enc_params.register[self._enc_params.index_1.value] + ).value + ].value ] ) - self._encParams.Index3 = b + self._enc_params.index_3 = b - return self._encParams.Index4 + return self._enc_params.index_4 - def index_position_generation(self, limit, randomSum, keyPosition): - swapIndex, retryLimiter, bitMask = 0, 0, 1 - if limit == 0: - return 0 + def index_position_generation(self, limit: Byte, random_sum: Byte, key_position: Byte) -> tuple[Byte, Byte, Byte]: + swap_index, retry_limiter, bit_mask = Byte(0), Byte(0), Byte(1) + if limit == Byte(0): + return random_sum, key_position, Byte(0) - while bitMask < limit: - bitMask = (bitMask << 1) + 1 + while bit_mask < limit: + bit_mask = (bit_mask << Byte(1)) + Byte(1) while True: - randomSum = ( - self._encParams.Register[randomSum] + self._clientChallenge[keyPosition] + random_sum = ( + self._enc_params.register[random_sum.value] + + self._clientChallenge[key_position.value] ) + key_position += Byte(1) + + if key_position >= Byte(len(self._clientChallenge)): + key_position = Byte(0) + random_sum += Byte(len(self._clientChallenge)) - if keyPosition >= len(self._clientChallenge): - keyPosition = 0 - randomSum += len(self._clientChallenge) + swap_index = bit_mask & random_sum - swapIndex = bitMask & randomSum - retryLimiter += 1 - if retryLimiter > 11: - swapIndex %= limit + if retry_limiter > Byte(11): + swap_index %= limit + retry_limiter += Byte(1) - if swapIndex <= limit: + if swap_index <= limit: break - return swapIndex + return random_sum, key_position, swap_index - def encrypt(self, plainText): - return plainText + def encrypt(self, plain_text: bytes): + # skip first 14 bytes + head_buffer = plain_text[:14] + body_buffer = plain_text[14:] + cipher_body = bytearray(body_buffer) + for i in range(len(body_buffer)): + c = self.byte_shift(body_buffer[i]) + cipher_body[i] = c.value + cipher_body = bytes(cipher_body) + cipher = head_buffer+cipher_body + return cipher diff --git a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/server_info_builder.py b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/server_info_builder.py index 9af3ef6b3..b64f79be7 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/server_info_builder.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/server_info_builder.py @@ -11,8 +11,8 @@ def build_server_info_header( flag: GameServerFlags, server_info: GameServerInfo -) -> list[int]: - header = [] +) -> bytearray: + header = bytearray() # add key flag header.append(flag.value) # we add server public ip here @@ -33,7 +33,7 @@ def build_server_info_header( return header -def check_nat_neg_flag(header: list[int], server_info: GameServerInfo): +def check_nat_neg_flag(header: bytearray, server_info: GameServerInfo): if "natneg" in server_info.server_data: nat_neg_flag = int(server_info.server_data["natneg"]) unsolicited_udp = header[0] & GameServerFlags.UNSOLICITED_UDP_FLAG.value @@ -41,14 +41,14 @@ def check_nat_neg_flag(header: list[int], server_info: GameServerInfo): header[0] ^= GameServerFlags.CONNECT_NEGOTIATE_FLAG.value -def check_unsolicited_udp(header: list[int], server_info: GameServerInfo): +def check_unsolicited_udp(header: bytearray, server_info: GameServerInfo): if "allow_unsolicited_udp" in server_info.server_data: unsolicited_udp = int(server_info.server_data["unsolicitedudp"]) if unsolicited_udp == 1: header[0] ^= GameServerFlags.UNSOLICITED_UDP_FLAG.value -def check_private_ip(header: list[int], server_info: GameServerInfo): +def check_private_ip(header: bytearray, server_info: GameServerInfo): #!when game create a channel chat, it will use both the public ip and private ip to build the name. #!known game: Worm3d # todo @@ -59,7 +59,7 @@ def check_private_ip(header: list[int], server_info: GameServerInfo): header.extend(bytes_address) -def check_non_standard_port(header: list[int], server_info: GameServerInfo): +def check_non_standard_port(header: bytearray, server_info: GameServerInfo): # !! only dedicated server have different query report port and host port # !! but peer server have same query report port and host port # todo we have to check when we need send host port or query report port @@ -69,7 +69,7 @@ def check_non_standard_port(header: list[int], server_info: GameServerInfo): header.extend(hton_port) -def check_non_standard_private_port(header: list[int], server_info: GameServerInfo): +def check_non_standard_private_port(header: bytearray, server_info: GameServerInfo): if "localport" in server_info.server_data: local_port = server_info.server_data["localport"] if local_port != "" and local_port != QUERY_REPORT_DEFAULT_PORT: @@ -78,7 +78,7 @@ def check_non_standard_private_port(header: list[int], server_info: GameServerIn header.extend(port) -def check_icmp_support(header: list[int], server_info: GameServerInfo): +def check_icmp_support(header: bytearray, server_info: GameServerInfo): if "icmp_address" in server_info.server_data: header[0] ^= GameServerFlags.ICMP_IP_FLAG.value bytes_address = inet_aton(server_info.server_data["icmp_address"]) diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/client.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/client.py index ff8b37f21..dda282710 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/client.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/client.py @@ -1,6 +1,9 @@ from typing import TYPE_CHECKING from frontends.gamespy.library.abstractions.client import ClientBase, ClientInfoBase +from frontends.gamespy.library.abstractions.connections import ConnectionBase from frontends.gamespy.library.abstractions.enctypt_base import EncryptBase +from frontends.gamespy.library.configs import ServerConfig +from frontends.gamespy.library.log.log_manager import LogWriter from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import ServerListUpdateOption if TYPE_CHECKING: from frontends.gamespy.library.abstractions.switcher import SwitcherBase @@ -14,10 +17,15 @@ class ClientInfo(ClientInfoBase): class Client(ClientBase): - is_log_raw: bool = True + is_log_raw: bool info: ClientInfo crypto: EncryptBase + def __init__(self, connection: ConnectionBase, server_config: ServerConfig, logger: LogWriter): + super().__init__(connection, server_config, logger) + self.is_log_raw = True + self.info = ClientInfo() + def _create_switcher(self, buffer: bytes) -> "SwitcherBase": from frontends.gamespy.protocols.server_browser.v2.applications.switcher import Switcher return Switcher(self, buffer) diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py index 44808bfa4..8b1f192e2 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py @@ -37,6 +37,7 @@ ) from frontends.gamespy.protocols.server_browser.v2.abstractions.handlers import ( CmdHandlerBase, + ServerListUpdateOptionHandlerBase, ) from frontends.gamespy.protocols.server_browser.v2.applications.client import Client @@ -92,7 +93,8 @@ def send_message(self, client: Client): == ServerListUpdateOption.P2P_SERVER_MAIN_LIST ) ): - client.log_info(f"Sending AdHoc message {self._message.status} to client") + client.log_info( + f"Sending AdHoc message {self._message.status} to client") client.send(self.response) @@ -129,13 +131,10 @@ class ServerInfoHandler(CmdHandlerBase): def __init__(self, client: Client, request: RequestBase) -> None: super().__init__(client, request) self._result_cls = ServerInfoResult - - def _response_construct(self) -> None: - if self._result.game_server_info is not None: - self._response = UpdateServerInfoResponse(self._result) + self._response_cls = UpdateServerInfoResponse -class ServerMainListHandler(CmdHandlerBase): +class ServerMainListHandler(ServerListUpdateOptionHandlerBase): _request: ServerListRequest _result: ServerMainListResult _result_cls: type[ServerMainListResult] @@ -143,12 +142,10 @@ class ServerMainListHandler(CmdHandlerBase): def __init__(self, client: Client, request: RequestBase) -> None: super().__init__(client, request) self._result_cls = ServerMainListResult - - def _response_construct(self) -> None: - self._response = ServerMainListResponse(self._result) + self._response_cls = ServerMainListResponse -class P2PGroupRoomListHandler(CmdHandlerBase): +class P2PGroupRoomListHandler(ServerListUpdateOptionHandlerBase): _request: ServerListRequest _result: P2PGroupRoomListResult _result_cls: type[P2PGroupRoomListResult] @@ -156,12 +153,10 @@ class P2PGroupRoomListHandler(CmdHandlerBase): def __init__(self, client: Client, request: RequestBase) -> None: super().__init__(client, request) self._result_cls = P2PGroupRoomListResult - - def _response_construct(self) -> None: - self._response = P2PGroupRoomListResponse(self._result) + self._response_cls = P2PGroupRoomListResponse -class ServerNetworkInfoListHandler(CmdHandlerBase): +class ServerNetworkInfoListHandler(ServerListUpdateOptionHandlerBase): _request: ServerListRequest _result: ServerNetworkInfoListResult _result_cls: type[ServerNetworkInfoListResult] @@ -169,49 +164,4 @@ class ServerNetworkInfoListHandler(CmdHandlerBase): def __init__(self, client: Client, request: RequestBase) -> None: super().__init__(client, request) self._result_cls = ServerNetworkInfoListResult - - def _response_construct(self) -> None: - self._response = ServerNetworkInfoListResponse(self._result) - - -# class ServerListHandler(CmdHandlerBase): -# _request: ServerListRequest -# _result: ServerMainListResult -# _result_cls: ( -# type[ServerMainListResult] -# | type[P2PGroupRoomListResult] -# | type[ServerInfoResult] -# ) - -# def _request_check(self) -> None: -# super()._request_check() -# match self._request.update_option: -# case option if option in [ -# ServerListUpdateOption.SERVER_MAIN_LIST, -# ServerListUpdateOption.P2P_SERVER_MAIN_LIST, -# ServerListUpdateOption.LIMIT_RESULT_COUNT, -# ServerListUpdateOption.SERVER_FULL_MAIN_LIST, -# ]: -# self._result_cls = ServerMainListResult -# case ServerListUpdateOption.P2P_GROUP_ROOM_LIST: -# self._result_cls = P2PGroupRoomListResult -# case _: -# raise ServerBrowserException("unknown serverlist update option type") - -# def _response_construct(self) -> None: -# match self._request.update_option: -# case option if option in [ -# ServerListUpdateOption.SERVER_MAIN_LIST, -# ServerListUpdateOption.P2P_SERVER_MAIN_LIST, -# ServerListUpdateOption.LIMIT_RESULT_COUNT, -# ServerListUpdateOption.SERVER_FULL_MAIN_LIST, -# ]: -# self._response = ServerMainListResponse(self._request, self._result) -# case ServerListUpdateOption.P2P_GROUP_ROOM_LIST: -# self._response = P2PGroupRoomListResponse(self._request, self._result) -# case ServerListUpdateOption.SERVER_FULL_MAIN_LIST: -# self._response = ServerNetworkInfoListResponse( -# self._request, self._result -# ) -# case _: -# raise ServerBrowserException("unknown serverlist update option type") + self._response_cls = ServerNetworkInfoListResponse diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py index f7dda59df..93d629a9e 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py @@ -47,7 +47,7 @@ def _create_cmd_handlers( self._client = cast(Client, self._client) match name: case RequestType.SERVER_LIST_REQUEST: - update_option_index = raw_request.find(b"\x00\x00\x00\x00") + 1 + update_option_index = raw_request.find(b"\x00\x00\x00\x00",6) update_option_bytes = raw_request[ update_option_index : update_option_index + 4 ] diff --git a/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py index f0a4e683a..72e0607c2 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py @@ -37,7 +37,7 @@ def parse(self) -> None: remain_data = remain_data[filter_index + 1 :] keys_index = remain_data.index(0) - self.keys = remain_data[:keys_index].decode().split("\\") + self.keys = remain_data[1:keys_index].decode().split("\\") remain_data = remain_data[keys_index + 1 :] byte_update_options = remain_data[:4] diff --git a/src/frontends/tests/gamespy/query_report/request_tests.py b/src/frontends/tests/gamespy/query_report/request_tests.py index e0e4da7fa..56912c072 100644 --- a/src/frontends/tests/gamespy/query_report/request_tests.py +++ b/src/frontends/tests/gamespy/query_report/request_tests.py @@ -54,6 +54,9 @@ def test_heartbeat(self): request.parse() self.assertEqual("gmtest", request.game_name) self.assertEqual("2921297764", request.instant_key) + assert request.player_data is not None + assert request.team_data is not None + assert request.server_data is not None self.assertEqual(6, len(request.player_data)) self.assertEqual(19, len(request.server_data)) self.assertEqual(2, len(request.team_data)) diff --git a/src/frontends/tests/gamespy/server_browser/encrypt_test.py b/src/frontends/tests/gamespy/server_browser/encrypt_test.py new file mode 100644 index 000000000..16d437914 --- /dev/null +++ b/src/frontends/tests/gamespy/server_browser/encrypt_test.py @@ -0,0 +1,20 @@ +import unittest + +from frontends.gamespy.protocols.server_browser.v2.aggregations.encryption import EnctypeX + + +class EncryptionTest(unittest.TestCase): + def test_enctypex(self): + """ + test if enctypex param init correctness + """ + enc = EnctypeX("000000", "00000000") + self.assertTrue(enc._enc_params.index_0.value == 250) + self.assertTrue(enc._enc_params.index_1.value == 220) + self.assertTrue(enc._enc_params.index_2.value == 245) + self.assertTrue(enc._enc_params.index_3.value == 229) + self.assertTrue(enc._enc_params.index_4.value == 49) + register = b"\x7A\xFA\x64\xDC\xB9\xF5\xF6\xE5\x89\x84\x9D\x66\xD7\xEA\x8E\xD8\xD4\xC0\xA1\xA4\x67\xA9\xAA\xDF\xF0\x71\x99\xEC\x87\xFD\xD0\xA2\xF3\xB5\xE3\x01\xE7\x7D\xE9\xE2\xEB\x97\xF1\x6F\x70\xFC\xD6\xFB\x82\x95\xC1\xB1\xD9\xBF\xD1\xE0\x9E\x81\xE8\xBB\xE1\xF8\xAD\xDA\xDB\xE4\x65\xAE\xCE\xAB\xB4\xBD\xA5\xB6\xB8\xEE\xF4\x75\xBC\xC3\xC6\xB7\xC8\xC9\xBA\xA6\xC4\xC5\x85\x6B\x78\x6D\xC2\xA7\xCC\xCD\x62\x9F\xBE\xA8\xCB\xB0\xE6\xDD\x79\xA3\xCA\xF2\xCF\xAC\x6A\xEF\xFE\xED\xD5\xB2\xDE\xB3\x72\xF7\x6C\xF9\xC7\x39\xA0\xAF\x77\x35\x73\x74\x68\x76\x8A\x80\x6E\x63\x7B\x7C\x7F\x98\x9B\x88\x91\x94\x83\x93\x8D\x86\x9C\x9A\x7E\x92\x8B\x8C\xD2\x96\x8F\x90\xFF\x41\x32\x33\x34\x3D\x36\x37\x38\x49\x3A\x3B\x3C\x45\x3E\x3F\x40\x51\x42\x43\x44\x4D\x46\x47\x48\x59\x4A\x4B\x4C\x55\x4E\x4F\x50\x61\x52\x53\x54\x5D\x56\x57\x58\x69\x5A\x5B\x5C\xD3\x5E\x5F\x60\x00\x09\x02\x03\x04\x05\x06\x07\x08\x11\x0A\x0B\x0C\x0D\x0E\x0F\x10\x19\x12\x13\x14\x15\x16\x17\x18\x21\x1A\x1B\x1C\x1D\x1E\x1F\x20\x29\x22\x23\x24\x25\x26\x27\x28\x31\x2A\x2B\x2C\x2D\x2E\x2F\x30" + temp_register = bytes([v.value for v in enc._enc_params.register]) + self.assertTrue(temp_register == register) + pass From 529f4104c01b67f86dec8a6b57ab6d31fce8d7ab Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Fri, 26 Sep 2025 08:55:36 +0000 Subject: [PATCH 208/231] Fix(sb): server main list response bytes build issue --- .../v2/abstractions/contracts.py | 4 +-- .../v2/abstractions/handlers.py | 8 ----- .../v2/aggregations/encryption.py | 33 +++++++++---------- .../v2/aggregations/string_flags.py | 4 +-- .../gamespy/server_browser/encrypt_test.py | 11 ++++++- 5 files changed, 29 insertions(+), 31 deletions(-) diff --git a/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py index 8f34dbfe9..c45ce3453 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py @@ -96,8 +96,8 @@ def build(self) -> None: self._servers_info_buffers.extend( ip_to_4_bytes(self._result.client_remote_ip)) self._servers_info_buffers.extend( - QUERY_REPORT_DEFAULT_PORT.to_bytes(4)) - assert len(self._servers_info_buffers) == 22 + QUERY_REPORT_DEFAULT_PORT.to_bytes(2)) + assert len(self._servers_info_buffers) == 20 def build_crypt_header(self) -> bytearray: # cryptHeader have 14 bytes, when we encrypt data we need skip the first 14 bytes diff --git a/src/frontends/gamespy/protocols/server_browser/v2/abstractions/handlers.py b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/handlers.py index 465ce27bf..cef5a9f4f 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/abstractions/handlers.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/handlers.py @@ -41,11 +41,3 @@ def _data_operate(self) -> None: self._client.info.game_secret_key, self._client.info.client_challenge ) - def _response_send(self) -> None: - self._response.build() - head_buffer = self._response.sending_buffer[:14] - body_buffer = self._response.sending_buffer[14:] - encrypted_body_buffer = self._client.crypto.encrypt(body_buffer) - buffer = head_buffer + encrypted_body_buffer - self._client.log_network_sending(buffer) - self._client.connection.send(buffer) diff --git a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/encryption.py b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/encryption.py index 069cd0abc..6e3f12977 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/encryption.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/encryption.py @@ -11,7 +11,7 @@ def __init__(self, value): raise ValueError("byte should be in 0 to 256") self.value = value - def _clamp(self, value): + def _clamp(self, value) -> int: """clamp value in 0 to 255""" return value % 256 @@ -181,8 +181,9 @@ def non_challenge_mapping_init(self): self._enc_params.index_4 = Byte(11) self._enc_params.register = [Byte(i) for i in range(256, 0, -1)] - def byte_shift(self, b): + def byte_shift(self, b: Byte) -> Byte: self._enc_params.index_1 += self._enc_params.register[self._enc_params.index_0.value] + self._enc_params.index_0 += Byte(1) swap_temp_storage = self._enc_params.register[self._enc_params.index_4.value] self._enc_params.register[self._enc_params.index_4.value] = self._enc_params.register[ self._enc_params.index_1.value @@ -196,21 +197,17 @@ def byte_shift(self, b): self._enc_params.register[self._enc_params.index_0.value] = swap_temp_storage self._enc_params.index_2 += self._enc_params.register[swap_temp_storage.value] - self._enc_params.index_4 = ( - b ^ self._enc_params.register[ - ( - self._enc_params.register[self._enc_params.index_2.value] - + self._enc_params.register[self._enc_params.index_0.value] - ).value - ] ^ self._enc_params.register[ - self._enc_params.register[ - ( - self._enc_params.register[self._enc_params.index_3.value] - + self._enc_params.register[self._enc_params.index_4.value] - + self._enc_params.register[self._enc_params.index_1.value] - ).value - ].value - ] + part1 = ( + self._enc_params.register[self._enc_params.index_2.value] + + self._enc_params.register[self._enc_params.index_0.value] + ).value % 256 + part2 = (self._enc_params.register[self._enc_params.index_3.value].value + + self._enc_params.register[self._enc_params.index_4.value].value + + self._enc_params.register[self._enc_params.index_1.value].value) % 256 + part3 = self._enc_params.register[part2].value + self._enc_params.index_4 = Byte( + b.value ^ self._enc_params.register[part1].value + ^ self._enc_params.register[part3].value ) self._enc_params.index_3 = b @@ -252,7 +249,7 @@ def encrypt(self, plain_text: bytes): body_buffer = plain_text[14:] cipher_body = bytearray(body_buffer) for i in range(len(body_buffer)): - c = self.byte_shift(body_buffer[i]) + c = self.byte_shift(Byte(body_buffer[i])) cipher_body[i] = c.value cipher_body = bytes(cipher_body) cipher = head_buffer+cipher_body diff --git a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/string_flags.py b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/string_flags.py index a1b38a6ff..3b5b0f114 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/string_flags.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/string_flags.py @@ -1,3 +1,3 @@ -ALL_SERVER_END_FLAG = bytearray([0, 255, 255, 255, 255]) +ALL_SERVER_END_FLAG = b"\x00\xFF\xFF\xFF\xFF" STRING_SPLITER = b"\x00" -NTS_STRING_FLAG = b"0xFF" +NTS_STRING_FLAG = b"\xFF" diff --git a/src/frontends/tests/gamespy/server_browser/encrypt_test.py b/src/frontends/tests/gamespy/server_browser/encrypt_test.py index 16d437914..8048c6c60 100644 --- a/src/frontends/tests/gamespy/server_browser/encrypt_test.py +++ b/src/frontends/tests/gamespy/server_browser/encrypt_test.py @@ -1,6 +1,6 @@ import unittest -from frontends.gamespy.protocols.server_browser.v2.aggregations.encryption import EnctypeX +from frontends.gamespy.protocols.server_browser.v2.aggregations.encryption import Byte, EnctypeX class EncryptionTest(unittest.TestCase): @@ -17,4 +17,13 @@ def test_enctypex(self): register = b"\x7A\xFA\x64\xDC\xB9\xF5\xF6\xE5\x89\x84\x9D\x66\xD7\xEA\x8E\xD8\xD4\xC0\xA1\xA4\x67\xA9\xAA\xDF\xF0\x71\x99\xEC\x87\xFD\xD0\xA2\xF3\xB5\xE3\x01\xE7\x7D\xE9\xE2\xEB\x97\xF1\x6F\x70\xFC\xD6\xFB\x82\x95\xC1\xB1\xD9\xBF\xD1\xE0\x9E\x81\xE8\xBB\xE1\xF8\xAD\xDA\xDB\xE4\x65\xAE\xCE\xAB\xB4\xBD\xA5\xB6\xB8\xEE\xF4\x75\xBC\xC3\xC6\xB7\xC8\xC9\xBA\xA6\xC4\xC5\x85\x6B\x78\x6D\xC2\xA7\xCC\xCD\x62\x9F\xBE\xA8\xCB\xB0\xE6\xDD\x79\xA3\xCA\xF2\xCF\xAC\x6A\xEF\xFE\xED\xD5\xB2\xDE\xB3\x72\xF7\x6C\xF9\xC7\x39\xA0\xAF\x77\x35\x73\x74\x68\x76\x8A\x80\x6E\x63\x7B\x7C\x7F\x98\x9B\x88\x91\x94\x83\x93\x8D\x86\x9C\x9A\x7E\x92\x8B\x8C\xD2\x96\x8F\x90\xFF\x41\x32\x33\x34\x3D\x36\x37\x38\x49\x3A\x3B\x3C\x45\x3E\x3F\x40\x51\x42\x43\x44\x4D\x46\x47\x48\x59\x4A\x4B\x4C\x55\x4E\x4F\x50\x61\x52\x53\x54\x5D\x56\x57\x58\x69\x5A\x5B\x5C\xD3\x5E\x5F\x60\x00\x09\x02\x03\x04\x05\x06\x07\x08\x11\x0A\x0B\x0C\x0D\x0E\x0F\x10\x19\x12\x13\x14\x15\x16\x17\x18\x21\x1A\x1B\x1C\x1D\x1E\x1F\x20\x29\x22\x23\x24\x25\x26\x27\x28\x31\x2A\x2B\x2C\x2D\x2E\x2F\x30" temp_register = bytes([v.value for v in enc._enc_params.register]) self.assertTrue(temp_register == register) + plain_text = bytes([0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]) + cipher = [] + for i in range(len(plain_text)): + c = enc.byte_shift(Byte(plain_text[i])) + cipher.append(c.value) + b_cipher = bytes(cipher) + correct_cipher = b"\x84\xF2\xF5\x20\xE8\x4D\x86\x67\xB6\xD3\xB3\xC0\x23\x0D\x40\x74\x67\xFE\x9C\x30" + self.assertTrue(b_cipher == correct_cipher) pass From 855f107f0329861c2a7069e6129e3a0e496109fb Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Fri, 10 Oct 2025 08:06:12 +0000 Subject: [PATCH 209/231] Fix(sb): wrong string split flag --- src/.devcontainer/devcontainer.json | 12 ++- src/.vscode/launch.json | 74 ++++++------- .../library/abstractions/handler_base.py | 1 - src/backends/library/database/pg_orm.py | 11 ++ .../library/networks/redis_brocker.py | 52 +++++++++ src/backends/library/networks/ws_manager.py | 66 ++++++++++++ .../protocols/gamespy/chat/brocker.py | 61 ++++------- .../protocols/gamespy/natneg/helpers.py | 44 ++++---- .../protocols/gamespy/query_report/broker.py | 81 ++++---------- .../protocols/gamespy/query_report/data.py | 53 ++++++++-- .../gamespy/query_report/handlers.py | 26 ++++- .../gamespy/query_report/requests.py | 8 +- .../gamespy/server_browser/brocker.py | 7 ++ .../gamespy/server_browser/handlers.py | 74 ++++++++++--- .../gamespy/server_browser/requests.py | 2 +- src/backends/routers/gamespy/chat.py | 8 +- src/backends/routers/gamespy/query_report.py | 27 +++-- .../routers/gamespy/server_browser.py | 7 ++ src/backends/routers/home.py | 8 +- src/backends/services/register.py | 31 ++++++ .../gamespy/query_report/handler_tests.py | 4 +- .../gamespy/library/abstractions/brocker.py | 20 ++-- .../library/abstractions/enctypt_base.py | 96 +++++++++++++++++ .../{brockers.py => websocket_brocker.py} | 53 +++------- .../protocols/chat/abstractions/handler.py | 4 +- .../protocols/chat/applications/broker.py | 2 +- .../protocols/chat/applications/client.py | 2 +- .../protocols/chat/applications/handlers.py | 16 +-- .../game_status/applications/handlers.py | 10 +- .../game_traffic_relay/applications/broker.py | 2 +- .../applications/handlers.py | 1 + .../applications/handlers.py | 12 +-- .../query_report/aggregates/natneg_channel.py | 2 +- .../query_report/applications/client.py | 31 +++++- .../query_report/v2/applications/handlers.py | 22 ++-- .../query_report/v2/applications/switcher.py | 6 +- .../query_report/v2/contracts/requests.py | 8 +- .../query_report/v2/contracts/responses.py | 12 +-- .../query_report/v2/contracts/results.py | 2 +- .../v2/aggregations/encryption.py | 100 +----------------- .../server_browser/v2/aggregations/enums.py | 18 ++-- .../v2/applications/handlers.py | 41 +++---- .../v2/applications/switcher.py | 54 ++++++---- .../server_browser/v2/contracts/requests.py | 13 ++- .../server_browser/v2/contracts/responses.py | 29 ++++- .../server_browser/v2/contracts/results.py | 5 +- .../modules/auth/handlers/general.py | 50 ++++++--- .../gamespy/query_report/mock_objects.py | 6 +- .../gamespy/query_report/request_tests.py | 4 +- .../gamespy/server_browser/contract_tests.py | 60 +++++++++++ .../{encrypt_test.py => encrypt_tests.py} | 0 51 files changed, 835 insertions(+), 503 deletions(-) create mode 100644 src/backends/library/networks/redis_brocker.py create mode 100644 src/backends/library/networks/ws_manager.py create mode 100644 src/backends/protocols/gamespy/server_browser/brocker.py create mode 100644 src/backends/services/register.py rename src/frontends/gamespy/library/network/{brockers.py => websocket_brocker.py} (55%) create mode 100644 src/frontends/tests/gamespy/server_browser/contract_tests.py rename src/frontends/tests/gamespy/server_browser/{encrypt_test.py => encrypt_tests.py} (100%) diff --git a/src/.devcontainer/devcontainer.json b/src/.devcontainer/devcontainer.json index a14078b1f..464c0b8a4 100644 --- a/src/.devcontainer/devcontainer.json +++ b/src/.devcontainer/devcontainer.json @@ -25,7 +25,17 @@ 80 ], // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "echo '127.0.0.1 unispy_backends' | sudo tee -a /etc/hosts && pip3 install --user -r requirements.txt" + "postCreateCommand": "echo '127.0.0.1 unispy_backends' | sudo tee -a /etc/hosts && pip3 install --user -r requirements.txt", + "customizations": { + "vscode": { + "extensions": [ + "ms-python.vscode-pylance@2024.8.2", + "ms-python.python@2024.14.1", + "ms-python.debugpy@2024.8.0", + "ms-python.autopep8@2025.2.0" + ] + } + } // Configure tool-specific properties. // "customizations": {}, // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json index 12653350d..fd4465e67 100644 --- a/src/.vscode/launch.json +++ b/src/.vscode/launch.json @@ -136,47 +136,35 @@ "justMyCode": true }, ], - // "compounds": [ - // { - // "name": "pcm", - // "configurations": [ - // "backend", - // "_pcm" - // ] - // }, - // { - // "name": "psp", - // "configurations": [ - // "backend", - // "_psp" - // ] - // }, - // { - // "name": "qr", - // "configurations": [ - // "backend", - // "_qr" - // ] - // }, - // { - // "name": "sb", - // "configurations": [ - // "backend", - // "_sb" - // ] - // }, - // { - // "name": "all", - // "configurations": [ - // "_psp", - // "_pcm", - // "backend", - // "_natneg", - // "_gs", - // "_sb", - // "_qr", - // "_web" - // ] - // }, - // ] + "compounds": [ + { + "name": "pcm_psp", + "configurations": [ + "backend", + "pcm", + "psp" + ] + }, + { + "name": "qr_sb", + "configurations": [ + "backend", + "qr", + "sb" + ] + }, + { + "name": "all_services", + "configurations": [ + "backend", + "psp", + "pcm", + "natneg", + "gs", + "sb", + "qr", + "web" + ] + }, + ] } \ No newline at end of file diff --git a/src/backends/library/abstractions/handler_base.py b/src/backends/library/abstractions/handler_base.py index 59c8cb1b5..59e7b762f 100644 --- a/src/backends/library/abstractions/handler_base.py +++ b/src/backends/library/abstractions/handler_base.py @@ -50,7 +50,6 @@ def handle(self) -> None: self._session = session self._request_check() self._data_operate() - self._session.commit() self._result_construct() self._response_construct() diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 41d31f40d..4cbd22864 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -321,6 +321,17 @@ class GameServerCaches(Base): update_time = Column(DateTime, nullable=False, default=datetime.now()) +class FrontendInfo(Base): + __tablename__ = "frontend_info" + id = Column(Integer, primary_key=True, autoincrement=True) + server_id = Column(UUID, nullable=False) + server_name = Column(String, nullable=False) + external_ip = Column(String, nullable=False) + listening_ip = Column(INET, nullable=False) + listening_port = Column(Integer, nullable=False) + update_time = Column(DateTime, nullable=False, default=datetime.now()) + + ENGINE = create_engine(CONFIG.postgresql.url) with ENGINE.connect() as conn: conn.execute(text("SELECT 1")).first() diff --git a/src/backends/library/networks/redis_brocker.py b/src/backends/library/networks/redis_brocker.py new file mode 100644 index 000000000..7906eb2a8 --- /dev/null +++ b/src/backends/library/networks/redis_brocker.py @@ -0,0 +1,52 @@ + +import threading +from redis.client import PubSub +from typing import Callable +from redis import Redis + +from frontends.gamespy.library.abstractions.brocker import BrockerBase +from frontends.gamespy.library.configs import CONFIG + + +class RedisBrocker(BrockerBase): + _client: Redis + _subscriber: PubSub + + def __init__(self, name: str, url: str, call_back_func: Callable | None = None) -> None: + super().__init__(name, url, call_back_func) + self._client = Redis.from_url(url, socket_timeout=5) + self._client.ping() + self._subscriber = self._client.pubsub() + + def subscribe(self): + self.is_started = True + threading.Thread(target=self.get_message).start() + + def get_message(self): + self._subscriber.subscribe(self._name) + while True: + m = self._subscriber.get_message(timeout=60) + if m is not None: + if "data" not in m: + continue + if not isinstance(m['data'], bytes): + continue + msg = m['data'].decode("utf-8") + threading.Thread(target=self.receive_message, args=[msg]).start() + + def unsubscribe(self): + self.is_started = False + self._subscriber.unsubscribe(self._name) + self._subscriber.close() + + def publish_message(self, message: str): + assert isinstance(message, str) + self._client.publish(self._name, message) + + +if __name__ == "__main__": + pass + + brocker = RedisBrocker("master", CONFIG.redis.url) + brocker.subscribe() + pass diff --git a/src/backends/library/networks/ws_manager.py b/src/backends/library/networks/ws_manager.py new file mode 100644 index 000000000..2f69c45ed --- /dev/null +++ b/src/backends/library/networks/ws_manager.py @@ -0,0 +1,66 @@ +import asyncio +import logging +from fastapi import WebSocket + + +class WebsocketManager: + """ + current: single server mode + client1 -> frontend1 -> backend1 (rest api) + client2 <- | <- + client3 <- | + """ + + """ + future: distributed mode + client1 -> frontend1 -> backend1 (rest api) + client2 <- | <- + client3 <- | + -> (websocket) -> redis + client2 -> frontend2 <- backend2 <- | + client3 -> frontend3 <- backend3 <- | + client4 -> frontend4 <- backend4 <- | + """ + client_pool: dict[str, WebSocket] + logger: logging.Logger + + def __init__(self) -> None: + self.client_pool = {} + self.logger = logging.getLogger("backend") + + def get_address_str(self, ws: WebSocket) -> str: + assert ws.client is not None + ws_address = f"{ws.client.host}:{ws.client.port}" + return ws_address + + def connect(self, ws: WebSocket): + assert ws.client is not None + ws_address = self.get_address_str(ws) + self.client_pool[ws_address] = ws + + def disconnect(self, ws: WebSocket): + assert ws.client is not None + ws_address = self.get_address_str(ws) + if ws_address in self.client_pool: + del self.client_pool[ws_address] + + def get_websocket(self, ws_address: str) -> WebSocket | None: + if ws_address in self.client_pool: + return self.client_pool[ws_address] + + def broadcast(self, message: str): + self._broadcast(message, list(self.client_pool.values())) + + def _broadcast(self, message: str, wss: list[WebSocket]): + loop = asyncio.get_event_loop() + self.logger.info(f"[cast] [send] {message}") + for ws in wss: + loop.create_task(ws.send_json(message)) + + def broadcast_except(self, message: str, wss: list[WebSocket], except_ip: list[str]): + filtered_wss = [] + for ws in wss: + assert ws.client is not None + if ws.client.host not in except_ip: + filtered_wss.append(ws) + self._broadcast(message, filtered_wss) diff --git a/src/backends/protocols/gamespy/chat/brocker.py b/src/backends/protocols/gamespy/chat/brocker.py index 4583175c2..906455ac4 100644 --- a/src/backends/protocols/gamespy/chat/brocker.py +++ b/src/backends/protocols/gamespy/chat/brocker.py @@ -1,3 +1,4 @@ +import asyncio from logging import Logger import logging from fastapi import WebSocket @@ -6,9 +7,10 @@ import backends.protocols.gamespy.chat.data as data from sqlalchemy.orm import Session +from backends.library.networks.ws_manager import WebsocketManager as WsManager -class WebsocketManager: +class WebsocketManager(WsManager): """ current: single server mode client1 -> frontend1 -> backend1 (rest api) @@ -26,52 +28,31 @@ class WebsocketManager: client3 -> frontend3 <- backend3 <- | client4 -> frontend4 <- backend4 <- | """ - client_pool: dict[str, WebSocket] - logger: Logger - - def __init__(self) -> None: - self.client_pool = {} - self.logger = logging.getLogger("backend") - - def get_address_str(self, ws: WebSocket) -> str: - assert ws.client is not None - ws_address = f"{ws.client.host}:{ws.client.port}" - return ws_address - - def connect(self, ws: WebSocket): - assert ws.client is not None - ws_address = self.get_address_str(ws) - self.client_pool[ws_address] = ws - - def disconnect(self, ws: WebSocket): - assert ws.client is not None - ws_address = self.get_address_str(ws) - if ws_address in self.client_pool: - del self.client_pool[ws_address] - - def get_websocket(self, ws_address: str) -> WebSocket | None: - if ws_address in self.client_pool: - return self.client_pool[ws_address] def process_message(self, message: dict) -> BrockerMessage: self.logger.info(f"[cast] [recv] {message}") msg = BrockerMessage.model_validate(message) return msg - # create redis pubsub to share message cross all backends - # currently we simply implement without redis pubsub - async def broadcast(self, message: BrockerMessage, ws_client: WebSocket): - exclude_addr = self.get_address_str(ws_client) + def _get_wss_in_channel(self, channel_name: str) -> list[WebSocket]: with Session(ENGINE) as session: - wss = data.get_websocket_addr_by_channel_name( - message.channel_name, session) - if exclude_addr in wss: - wss.remove(exclude_addr) - for ws_addr in wss: - if ws_addr in self.client_pool: - ws = self.client_pool[ws_addr] - await ws.send_json(message.model_dump_json()) - self.logger.info(f"[cast] [send] {message.model_dump_json()}") + ws_addrss = data.get_websocket_addr_by_channel_name( + channel_name, session) + wss = [] + for addr in ws_addrss: + if addr in self.client_pool: + wss.append(self.client_pool[addr]) + return wss + + def broadcast_channel_message(self, message: BrockerMessage, ws_client: WebSocket): + """ + create redis pubsub to share message cross all backends + currently we simply implement without redis pubsub + """ + exclude_addr = self.get_address_str(ws_client) + wss = self._get_wss_in_channel(message.channel_name) + message_str = message.model_dump_json() + self.broadcast_except(message_str, wss, [exclude_addr]) MANAGER = WebsocketManager() diff --git a/src/backends/protocols/gamespy/natneg/helpers.py b/src/backends/protocols/gamespy/natneg/helpers.py index 9affa94bc..37cd9e391 100644 --- a/src/backends/protocols/gamespy/natneg/helpers.py +++ b/src/backends/protocols/gamespy/natneg/helpers.py @@ -103,14 +103,16 @@ def _validate_version3(self): if NatPortType.GP in self.address_infos: if ( self.address_infos[NatPortType.GP].cookie - != self.address_infos[NatPortType.NN1].cookie + != self.address_infos[NatPortType.NN1].cookie # type:ignore or self.address_infos[NatPortType.GP].version - != self.address_infos[NatPortType.NN1].version + != self.address_infos[NatPortType.NN1].version # type:ignore or self.address_infos[NatPortType.GP].client_index + # type:ignore != self.address_infos[NatPortType.NN1].client_index or self.address_infos[NatPortType.GP].use_game_port + # type:ignore != self.address_infos[NatPortType.NN1].use_game_port - ): # type: ignore + ): raise NatNegException("GP packet info is not correct") def _validate_version4(self): @@ -126,33 +128,33 @@ def _validate_version4(self): if ( self.address_infos[NatPortType.NN1].cookie - != self.address_infos[NatPortType.NN2].cookie + != self.address_infos[NatPortType.NN2].cookie # type:ignore or self.address_infos[NatPortType.NN1].cookie - != self.address_infos[NatPortType.NN3].cookie + != self.address_infos[NatPortType.NN3].cookie # type:ignore ): # type: ignore raise NatNegException("Broken cookie") if ( self.address_infos[NatPortType.NN1].version - != self.address_infos[NatPortType.NN2].version + != self.address_infos[NatPortType.NN2].version # type:ignore or self.address_infos[NatPortType.NN1].version - != self.address_infos[NatPortType.NN3].version + != self.address_infos[NatPortType.NN3].version # type:ignore ): # type: ignore raise NatNegException("Broken version") if ( self.address_infos[NatPortType.NN1].client_index - != self.address_infos[NatPortType.NN2].client_index + != self.address_infos[NatPortType.NN2].client_index # type:ignore or self.address_infos[NatPortType.NN1].client_index - != self.address_infos[NatPortType.NN3].client_index + != self.address_infos[NatPortType.NN3].client_index # type:ignore ): # type: ignore raise NatNegException("Broken client index") if ( self.address_infos[NatPortType.NN1].use_game_port - != self.address_infos[NatPortType.NN2].use_game_port + != self.address_infos[NatPortType.NN2].use_game_port # type:ignore or self.address_infos[NatPortType.NN1].use_game_port - != self.address_infos[NatPortType.NN3].use_game_port + != self.address_infos[NatPortType.NN3].use_game_port # type:ignore ): # type: ignore raise NatNegException("Broken use game port") @@ -165,12 +167,14 @@ def _validate_version4(self): if NatPortType.GP in self.address_infos: if ( self.address_infos[NatPortType.GP].cookie - != self.address_infos[NatPortType.NN1].cookie + != self.address_infos[NatPortType.NN1].cookie # type:ignore or self.address_infos[NatPortType.GP].version - != self.address_infos[NatPortType.NN1].version + != self.address_infos[NatPortType.NN1].version # type:ignore or self.address_infos[NatPortType.GP].client_index + # type:ignore != self.address_infos[NatPortType.NN1].client_index or self.address_infos[NatPortType.GP].use_game_port + # type:ignore != self.address_infos[NatPortType.NN1].use_game_port ): # type: ignore raise NatNegException("GP packet info is not correct") @@ -184,8 +188,8 @@ def _determine_nat_type_version3(info: "NatProtocolHelper"): nn1 = info.address_infos[NatPortType.NN1] nn2 = info.address_infos[NatPortType.NN2] # no nat - if nn1.public_ip == nn1.private_ip and ( - nn2.public_ip == nn2.private_ip and nn2.public_port == nn1.public_port + if nn1.public_ip == nn1.private_ip and ( # type:ignore + nn2.public_ip == nn2.private_ip and nn2.public_port == nn1.public_port # type:ignore ): # type: ignore info.nat_type = NatType.NO_NAT # detect cone @@ -212,22 +216,22 @@ def _determine_nat_type_version4(info: "NatProtocolHelper"): # no nat if ( - nn1.public_ip == nn1.private_ip + nn1.public_ip == nn1.private_ip # type:ignore and ( - nn2.public_ip == nn2.private_ip and nn2.public_port == nn2.private_port + nn2.public_ip == nn2.private_ip and nn2.public_port == nn2.private_port # type:ignore ) and ( - nn3.public_ip == nn3.private_ip and nn3.public_port == nn3.private_port + nn3.public_ip == nn3.private_ip and nn3.public_port == nn3.private_port # type:ignore ) ): # type: ignore info.nat_type = NatType.NO_NAT # detect cone elif ( - nn1.public_ip == nn2.public_ip and nn1.public_port == nn2.public_port + nn1.public_ip == nn2.public_ip and nn1.public_port == nn2.public_port # type:ignore ) and (nn2.public_ip == nn3.public_ip and nn2.public_port == nn3.public_port): # type: ignore info.nat_type = NatType.FULL_CONE elif ( - nn1.public_ip == nn2.public_ip and nn1.public_port != nn2.public_port + nn1.public_ip == nn2.public_ip and nn1.public_port != nn2.public_port # type:ignore ) or (nn2.public_ip == nn3.public_ip and nn2.public_port != nn3.public_port): # type: ignore info.nat_type = NatType.SYMMETRIC info.port_mapping = NatPortMappingScheme.INCREMENTAL diff --git a/src/backends/protocols/gamespy/query_report/broker.py b/src/backends/protocols/gamespy/query_report/broker.py index 4583175c2..188722e30 100644 --- a/src/backends/protocols/gamespy/query_report/broker.py +++ b/src/backends/protocols/gamespy/query_report/broker.py @@ -1,77 +1,34 @@ from logging import Logger import logging from fastapi import WebSocket +from pydantic import BaseModel from backends.library.database.pg_orm import ENGINE +from backends.protocols.gamespy.query_report.handlers import ClientMessageHandler +from backends.protocols.gamespy.query_report.requests import ClientMessageRequest +from backends.library.networks.redis_brocker import RedisBrocker +from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage import backends.protocols.gamespy.chat.data as data from sqlalchemy.orm import Session +from backends.library.networks.ws_manager import WebsocketManager as WsManager -class WebsocketManager: - """ - current: single server mode - client1 -> frontend1 -> backend1 (rest api) - client2 <- | <- - client3 <- | - """ +class WebsocketManager(WsManager): + pass - """ - future: distributed mode - client1 -> frontend1 -> backend1 (rest api) - client2 <- | <- - client3 <- | - -> (websocket) -> redis - client2 -> frontend2 <- backend2 <- | - client3 -> frontend3 <- backend3 <- | - client4 -> frontend4 <- backend4 <- | - """ - client_pool: dict[str, WebSocket] - logger: Logger - def __init__(self) -> None: - self.client_pool = {} - self.logger = logging.getLogger("backend") - - def get_address_str(self, ws: WebSocket) -> str: - assert ws.client is not None - ws_address = f"{ws.client.host}:{ws.client.port}" - return ws_address - - def connect(self, ws: WebSocket): - assert ws.client is not None - ws_address = self.get_address_str(ws) - self.client_pool[ws_address] = ws - - def disconnect(self, ws: WebSocket): - assert ws.client is not None - ws_address = self.get_address_str(ws) - if ws_address in self.client_pool: - del self.client_pool[ws_address] - - def get_websocket(self, ws_address: str) -> WebSocket | None: - if ws_address in self.client_pool: - return self.client_pool[ws_address] - - def process_message(self, message: dict) -> BrockerMessage: - self.logger.info(f"[cast] [recv] {message}") - msg = BrockerMessage.model_validate(message) - return msg - - # create redis pubsub to share message cross all backends - # currently we simply implement without redis pubsub - async def broadcast(self, message: BrockerMessage, ws_client: WebSocket): - exclude_addr = self.get_address_str(ws_client) - with Session(ENGINE) as session: - wss = data.get_websocket_addr_by_channel_name( - message.channel_name, session) - if exclude_addr in wss: - wss.remove(exclude_addr) - for ws_addr in wss: - if ws_addr in self.client_pool: - ws = self.client_pool[ws_addr] - await ws.send_json(message.model_dump_json()) - self.logger.info(f"[cast] [send] {message.model_dump_json()}") +def handle_client_message(message: str): + try: + request = ClientMessageRequest.model_validate(message) + handler = ClientMessageHandler(request) + handler.handle() + except Exception as e: + GLOBAL_LOGGER.error(str(e)) MANAGER = WebsocketManager() + +BROCKER = RedisBrocker("master", CONFIG.redis.url, handle_client_message) +# BROCKER.subscribe() diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index d4dc1127d..cac25c6e2 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -17,12 +17,16 @@ ) from sqlalchemy.orm import Session -from datetime import datetime +from datetime import datetime, timedelta from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus from frontends.gamespy.protocols.server_browser.v2.aggregations.exceptions import SBException +def __expire_time(): + return datetime.now() - timedelta(5) + + def get_all_groups() -> dict: with Session(ENGINE) as session: result = ( @@ -111,13 +115,25 @@ def get_peer_group_channel( return data -def get_server_info_with_instant_key( +def get_game_server_cache_by_ip_port(ip: str, port: int, session: Session) -> GameServerCaches | None: + result = ( + session.query(GameServerCaches) + .where(GameServerCaches.host_ip_address == ip, + GameServerCaches.query_report_port == port, + GameServerCaches.update_time >= __expire_time()) + .first() + ) + return result + + +def get_game_server_cache_with_instant_key( instant_key: str, session: Session ) -> GameServerCaches | None: assert isinstance(instant_key, str) result = ( session.query(GameServerCaches) - .where(GameServerCaches.instant_key == instant_key) + .where(GameServerCaches.instant_key == instant_key, + GameServerCaches.update_time >= __expire_time()) .first() ) return result @@ -148,7 +164,9 @@ def get_server_info_list_with_game_name( ) -> list[GameServerInfo]: result = ( session.query(GameServerCaches) - .where(GameServerCaches.game_name == game_name) + .where(GameServerCaches.game_name == game_name, + GameServerCaches.update_time >= __expire_time(), + GameServerCaches.avaliable == True) .all() ) data = [] @@ -179,6 +197,15 @@ def get_server_info_list_with_game_name( return data +def check_game_server_cache_conflict(ip: str, port: int, instant_key: str, session: Session): + cache = get_server_info_with_ip_and_port( + ip, port, session) + if cache is not None: + if cache.instant_key != instant_key: + session.delete(cache) + session.commit() + + def get_server_info_with_ip_and_port(ip: str, port: int, session: Session) -> GameServerInfo | None: assert isinstance(ip, str) assert isinstance(port, int) @@ -187,7 +214,7 @@ def get_server_info_with_ip_and_port(ip: str, port: int, session: Session) -> Ga .where( GameServerCaches.host_ip_address == ip, GameServerCaches.query_report_port == port, - ) + GameServerCaches.update_time >= __expire_time()) .first() ) if result is None: @@ -204,12 +231,12 @@ def remove_server_info(info: GameServerCaches, session: Session) -> None: # todo finish the GameServerCaches creation -def create_game_server(info: GameServerCaches, session: Session) -> None: +def create_game_server_cache(info: GameServerCaches, session: Session) -> None: session.add(info) session.commit() -def update_game_server( +def update_game_server_cache( cache: GameServerCaches, instant_key: str, server_id: UUID, @@ -239,7 +266,7 @@ def update_game_server( def refresh_game_server_cache(instant_key: str, session: Session): - cache = get_server_info_with_instant_key(instant_key, session) + cache = get_game_server_cache_with_instant_key(instant_key, session) if cache is None: raise QRException( "no game server cache found, please check the database") @@ -247,5 +274,11 @@ def refresh_game_server_cache(instant_key: str, session: Session): session.commit() -if __name__ == "__main__": - get_all_groups() +def clean_expired_game_server_cache(session: Session): + session.query(GameServerCaches).where( + GameServerCaches.update_time < __expire_time() + ).delete() + session.commit() + + if __name__ == "__main__": + get_all_groups() diff --git a/src/backends/protocols/gamespy/query_report/handlers.py b/src/backends/protocols/gamespy/query_report/handlers.py index 344776232..0de2200db 100644 --- a/src/backends/protocols/gamespy/query_report/handlers.py +++ b/src/backends/protocols/gamespy/query_report/handlers.py @@ -1,8 +1,10 @@ +import asyncio from datetime import datetime from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import ENGINE, GameServerCaches from backends.protocols.gamespy.query_report.requests import ( AvaliableRequest, + ClientMessageRequest, HeartBeatRequest, KeepAliveRequest, ) @@ -33,13 +35,17 @@ def _data_operate(self) -> None: session.commit() -class Heartbeathandler(HandlerBase): +class HeartbeatHandler(HandlerBase): _request: HeartBeatRequest def _data_operate(self) -> None: - cache = data.get_server_info_with_instant_key( - str(self._request.instant_key), self._session + # clean the expired server cache + data.clean_expired_game_server_cache(self._session) + + cache = data.get_game_server_cache_by_ip_port( + self._request.client_ip, self._request.client_port, self._session ) + if cache is None: # todo check whether these data can be null at first heartbeat if self._request.player_data is None: @@ -63,9 +69,9 @@ def _data_operate(self) -> None: team_data=self._request.team_data, avaliable=True, ) - data.create_game_server(cache, self._session) + data.create_game_server_cache(cache, self._session) else: - data.update_game_server( + data.update_game_server_cache( cache=cache, instant_key=self._request.instant_key, server_id=self._request.server_id, @@ -87,3 +93,13 @@ def _data_operate(self) -> None: assert isinstance(self._request.instant_key, str) data.refresh_game_server_cache( self._request.instant_key, self._session) + + +class ClientMessageHandler(HandlerBase): + _request: ClientMessageRequest + + def _response_construct(self) -> None: + # todo use websocket to send the message to qr client, but how to determine which qr router should be received + # currently we just use broadcast message to all qr frontends + from backends.protocols.gamespy.query_report.broker import MANAGER + MANAGER.broadcast(self._request.model_dump_json()) diff --git a/src/backends/protocols/gamespy/query_report/requests.py b/src/backends/protocols/gamespy/query_report/requests.py index a9e3d697c..51e045316 100644 --- a/src/backends/protocols/gamespy/query_report/requests.py +++ b/src/backends/protocols/gamespy/query_report/requests.py @@ -25,11 +25,11 @@ class ClientMessageAckRequest(RequestBase): class ClientMessageRequest(RequestBase): server_browser_sender_id: UUID4 - natneg_message: bytes + target_query_report_id: UUID4 + natneg_message: str target_ip_address: str - target_port: str - message_key: int - cookie: int + target_port: int + command_name: None = None class HeartBeatRequest(RequestBase): diff --git a/src/backends/protocols/gamespy/server_browser/brocker.py b/src/backends/protocols/gamespy/server_browser/brocker.py new file mode 100644 index 000000000..6da6f3961 --- /dev/null +++ b/src/backends/protocols/gamespy/server_browser/brocker.py @@ -0,0 +1,7 @@ +from backends.library.networks.redis_brocker import RedisBrocker +from frontends.gamespy.library.configs import CONFIG + + +# we only need publish so du not need subscribe +BROCKER = RedisBrocker("master", CONFIG.redis.url) +pass diff --git a/src/backends/protocols/gamespy/server_browser/handlers.py b/src/backends/protocols/gamespy/server_browser/handlers.py index c24be5b12..86a013a64 100644 --- a/src/backends/protocols/gamespy/server_browser/handlers.py +++ b/src/backends/protocols/gamespy/server_browser/handlers.py @@ -1,6 +1,10 @@ from typing import TYPE_CHECKING, cast +from uuid import UUID from backends.library.abstractions.handler_base import HandlerBase +from backends.protocols.gamespy.query_report.broker import BROCKER, MANAGER import backends.protocols.gamespy.query_report.data as data +from backends.protocols.gamespy.query_report.handlers import ClientMessageHandler +from backends.protocols.gamespy.query_report.requests import ClientMessageRequest from backends.protocols.gamespy.server_browser.requests import ( AdHocRequestBase, SendMessageRequest, @@ -13,15 +17,17 @@ from frontends.gamespy.protocols.query_report.aggregates.peer_room_info import ( PeerRoomInfo, ) +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import RequestType from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import ( GameServerFlags, ) +from frontends.gamespy.protocols.server_browser.v2.aggregations.exceptions import SBException from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( P2PGroupRoomListResult, SendMessageResult, ServerInfoResult, ServerMainListResult, - ServerNetworkInfoListResult, + ServerFullInfoListResult, ) @@ -41,7 +47,7 @@ def _result_construct(self): ) if TYPE_CHECKING: self._caches = cast(list[GameServerInfo], self._caches) - self._result = ServerNetworkInfoListResult( + self._result = ServerFullInfoListResult( client_remote_ip=self._request.client_ip, flag=GameServerFlags.HAS_KEYS_FLAG, game_secret_key="", @@ -85,22 +91,50 @@ def _data_operate(self): self._caches = data.get_server_info_list_with_game_name( self._request.game_name, self._session ) + # we just need server data + for cache in self._caches: + cache.player_data = [] + cache.team_data = [] def _result_construct(self): assert isinstance(self._caches, list) and all( isinstance(item, GameServerInfo) for item in self._caches ) + if TYPE_CHECKING: self._caches = cast(list[GameServerInfo], self._caches) self._result = ServerMainListResult( client_remote_ip=self._request.client_ip, - flag=GameServerFlags.HAS_KEYS_FLAG, game_secret_key=self._secret_key, servers_info=self._caches, keys=self._request.keys ) +class ServerFullInfoListHandler(HandlerBase): + _request: ServerListRequest + _caches: list[GameServerInfo] + + def _data_operate(self): + self._secret_key = data.get_secret_key( + self._request.game_name, self._session) + self._caches = data.get_server_info_list_with_game_name( + self._request.game_name, self._session + ) + + def _result_construct(self): + assert isinstance(self._caches, list) and all( + isinstance(item, GameServerInfo) for item in self._caches + ) + if TYPE_CHECKING: + self._caches = cast(list[GameServerInfo], self._caches) + self._result = ServerFullInfoListResult( + client_remote_ip=self._request.client_ip, + game_secret_key=self._secret_key, + servers_info=self._caches, + keys=self._request.keys + ) + # region Adhoc @@ -112,23 +146,33 @@ def _data_operate(self): class SendMessageHandler(HandlerBase): + """ + client -> server browser -> backend -> sb-SendMessageHandler -> qr-ClientMessageHandler -> websocket -> query report -> client + """ _request: SendMessageRequest def _data_operate(self): - self._data = data.get_server_info_with_ip_and_port( - self._request.game_server_public_ip, - self._request.game_server_public_port, - self._session, - ) - - def _result_construct(self): - if self._data is None: - return - self._result = SendMessageResult( - sb_sender_id=self._request.server_id, + # construct client message and invoke frontends qr server client message + cache = data.get_game_server_cache_by_ip_port( + self._request.game_server_public_ip, self._request.game_server_public_port, self._session) + if cache is None: + raise SBException( + f"could not find game server: {self._request.game_server_public_ip}:{self._request.game_server_public_port}") + assert isinstance(cache.instant_key, str) + assert isinstance(cache.server_id, UUID) + request = ClientMessageRequest( + server_id=self._request.server_id, + server_browser_sender_id=self._request.server_id, + target_query_report_id=cache.server_id, + raw_request=self._request.raw_request, + client_ip=self._request.client_ip, + client_port=self._request.client_port, + target_ip_address=self._request.game_server_public_ip, + target_port=self._request.game_server_public_port, natneg_message=self._request.client_message, - server_info=self._data, + instant_key=cache.instant_key, ) + BROCKER.publish_message(request.model_dump_json()) class ServerInfoHandler(HandlerBase): diff --git a/src/backends/protocols/gamespy/server_browser/requests.py b/src/backends/protocols/gamespy/server_browser/requests.py index 5d91e99e9..f2e7489cd 100644 --- a/src/backends/protocols/gamespy/server_browser/requests.py +++ b/src/backends/protocols/gamespy/server_browser/requests.py @@ -6,7 +6,7 @@ class RequestBase(lib.RequestBase): - raw_request: bytes + raw_request: str class ServerListUpdateOptionRequestBase(RequestBase): diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index aa79e7a5d..a303a020d 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -1,3 +1,4 @@ +import asyncio from backends.library.abstractions.contracts import OKResponse from backends.protocols.gamespy.chat.brocker import MANAGER from backends.protocols.gamespy.chat.handlers import ( @@ -56,6 +57,11 @@ client_pool = {} +# @asynccontextmanager +# async def launch_brocker(router: APIRouter): + + + @router.websocket(f"{CHAT}/ws") async def websocket_endpoint(ws: WebSocket): await ws.accept() @@ -65,7 +71,7 @@ async def websocket_endpoint(ws: WebSocket): while True: data = await ws.receive_json() msg = MANAGER.process_message(data) - await MANAGER.broadcast(msg, ws) + MANAGER.broadcast_channel_message(msg, ws) except WebSocketDisconnect: if ws.client is not None: MANAGER.disconnect(ws) diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index 532d32153..8b3f720b9 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -1,11 +1,18 @@ from fastapi import APIRouter, WebSocket, WebSocketDisconnect - -from backends.protocols.gamespy.query_report.broker import MANAGER -from backends.protocols.gamespy.query_report.handlers import AvaliableHandler, Heartbeathandler, KeepAliveHandler +from contextlib import asynccontextmanager +from backends.protocols.gamespy.query_report.broker import MANAGER, BROCKER +from backends.protocols.gamespy.query_report.handlers import AvaliableHandler, HeartbeatHandler, KeepAliveHandler from backends.protocols.gamespy.query_report.requests import AvaliableRequest, ChallengeRequest, ClientMessageRequest, EchoRequest, HeartBeatRequest, KeepAliveRequest from backends.urls import QUERY_REPORT -router = APIRouter() + +@asynccontextmanager +async def launch_brocker(router: APIRouter): + BROCKER.subscribe() + yield + +router = APIRouter(lifespan=launch_brocker) + @router.websocket(f"{QUERY_REPORT}/ws") async def websocket_endpoint(ws: WebSocket): @@ -14,17 +21,17 @@ async def websocket_endpoint(ws: WebSocket): MANAGER.connect(ws) try: while True: - data = await ws.receive_json() - msg = MANAGER.process_message(data) - await MANAGER.broadcast(msg, ws) + _ = await ws.receive_json() + # query report do not process ws client message except WebSocketDisconnect: if ws.client is not None: MANAGER.disconnect(ws) - # todo remove chat info by websocket print("Client disconnected") -@router.post(f"{QUERY_REPORT}/HeartBeatHandler") + + +@router.post(f"{QUERY_REPORT}/HeartbeatHandler") def heartbeat(request: HeartBeatRequest): - handler = Heartbeathandler(request) + handler = HeartbeatHandler(request) handler.handle() return handler._response diff --git a/src/backends/routers/gamespy/server_browser.py b/src/backends/routers/gamespy/server_browser.py index d553c89dd..458ab4905 100644 --- a/src/backends/routers/gamespy/server_browser.py +++ b/src/backends/routers/gamespy/server_browser.py @@ -44,6 +44,13 @@ def server_list(request: ServerListRequest): return handler.response +@router.post(f"{SERVER_BROWSER_V2}/ServerFullInfoListHandler") +def full_info_list(request: ServerListRequest): + handler = ServerMainListHandler(request) + handler.handle() + return handler.response + + if __name__ == "__main__": import uvicorn from fastapi import FastAPI diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index efcb2eb90..083cadcce 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -1,11 +1,13 @@ from ipaddress import IPv4Address from uuid import UUID -from fastapi import FastAPI, status +from fastapi import FastAPI, Request, status from fastapi.exceptions import RequestValidationError from fastapi.responses import JSONResponse from pydantic import BaseModel import uvicorn +from backends.library.database.pg_orm import ENGINE +from backends.services.register import register_services from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.library.log.log_manager import LogManager from frontends.gamespy.library.configs import ServerConfig @@ -61,8 +63,10 @@ def handle_unispy_exception(_, exc: Exception): @app.post("/") -def home(request: ServerConfig) -> dict: +def home(request: Request, config: ServerConfig,) -> dict: # todo add the server config to our database + assert request.client is not None + # response = register_services(config, request.client.host) return {"status": "online"} diff --git a/src/backends/services/register.py b/src/backends/services/register.py new file mode 100644 index 000000000..23d2f1ce5 --- /dev/null +++ b/src/backends/services/register.py @@ -0,0 +1,31 @@ + + +from datetime import datetime, timedelta +from backends.library.database.pg_orm import ENGINE, FrontendInfo +from frontends.gamespy.library.configs import ServerConfig +from sqlalchemy.orm import Session + +from frontends.gamespy.library.exceptions.general import UniSpyException + + +def register_services(config: ServerConfig, external_ip: str) -> dict: + expire_time = datetime.now() - timedelta(minutes=5) + with Session(ENGINE) as session: + result = session.query( + FrontendInfo.server_id == config.server_id, + FrontendInfo.update_time >= expire_time).first() + if result is not None: + info = FrontendInfo( + server_id=config.server_id, + server_name=config.server_name, + external_ip="", + listening_ip=config.public_address, + listening_port=config.listening_port, + update_time=datetime.now() + ) + + session.add(info) + session.commit() + return {"status": "online"} + else: + raise UniSpyException("server is already registered") diff --git a/src/backends/tests/gamespy/query_report/handler_tests.py b/src/backends/tests/gamespy/query_report/handler_tests.py index 4d57576ec..fa9f495f1 100644 --- a/src/backends/tests/gamespy/query_report/handler_tests.py +++ b/src/backends/tests/gamespy/query_report/handler_tests.py @@ -1,6 +1,6 @@ import unittest -from backends.protocols.gamespy.query_report.handlers import Heartbeathandler, AvaliableHandler, KeepAliveHandler +from backends.protocols.gamespy.query_report.handlers import HeartbeatHandler, AvaliableHandler, KeepAliveHandler from backends.protocols.gamespy.query_report.requests import HeartBeatRequest, AvaliableRequest, KeepAliveRequest @@ -9,7 +9,7 @@ def test_heartbeat(self): request = {"server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "raw_request": "\\u0003\\\\xe5\\\\xcfaZlocalip0\\u0000172.19.0.5\\u0000localport\\u000011111\\u0000natneg\\u00001\\u0000statechanged\\u00003\\u0000gamename\\u0000gmtest\\u0000hostname\\u0000GameSpy QR2 Sample\\u0000gamever\\u00002.00\\u0000hostport\\u000025000\\u0000mapname\\u0000gmtmap1\\u0000gametype\\u0000arena\\u0000numplayers\\u00005\\u0000numteams\\u00002\\u0000maxplayers\\u000032\\u0000gamemode\\u0000openplaying\\u0000teamplay\\u00001\\u0000fraglimit\\u00000\\u0000timelimit\\u000040\\u0000gravity\\u0000800\\u0000rankingon\\u00001\\u0000\\u0000\\u0000\\u0005player_\\u0000score_\\u0000deaths_\\u0000ping_\\u0000team_\\u0000time_\\u0000\\u0000Joe Player\\u000030\\u000012\\u0000411\\u00000\\u000010\\u0000L33t 0n3\\u00000\\u00006\\u0000233\\u00001\\u0000325\\u0000Raptor\\u000015\\u000025\\u000063\\u00001\\u0000462\\u0000Gr81\\u00000\\u000016\\u0000294\\u00000\\u0000870\\u0000Flubber\\u000017\\u000012\\u0000232\\u00001\\u0000384\\u0000\\u0000\\u0002team_t\\u0000score_t\\u0000avgping_t\\u0000\\u0000Red\\u0000294\\u0000357\\u0000Blue\\u0000498\\u0000454\\u0000", "client_ip": "172.19.0.5", "client_port": 11111, "instant_key": "3855573338", "command_name": 3, "server_data": {"localip0": "172.19.0.5", "localport": "11111", "natneg": "1", "statechanged": "3", "gamename": "gmtest", "hostname": "GameSpy QR2 Sample", "gamever": "2.00", "hostport": "25000", "mapname": "gmtmap1", "gametype": "arena", "numplayers": "5", "numteams": "2", "maxplayers": "32", "gamemode": "openplaying", "teamplay": "1", "fraglimit": "0", "timelimit": "40", "gravity": "800", "rankingon": "1"}, "player_data": [{"player_0": "Joe Player", "score_0": "30", "deaths_0": "12", "ping_0": "411", "team_0": "0", "time_0": "10"}, {"player_1": "L33t 0n3", "score_1": "0", "deaths_1": "6", "ping_1": "233", "team_1": "1", "time_1": "325"}, {"player_2": "Raptor", "score_2": "15", "deaths_2": "25", "ping_2": "63", "team_2": "1", "time_2": "462"}, {"player_3": "Gr81", "score_3": "0", "deaths_3": "16", "ping_3": "294", "team_3": "0", "time_3": "870"}, {"player_4": "Flubber", "score_4": "17", "deaths_4": "12", "ping_4": "232", "team_4": "1", "time_4": "384"}], "team_data": [{"team_t0": "Red", "score_t0": "294", "avgping_t0": "357"}, {"team_t1": "Blue", "score_t1": "498", "avgping_t1": "454"}], "server_status": 3, "group_id": None, "game_name": "gmtest"} req = HeartBeatRequest(**request) - handler = Heartbeathandler(req) + handler = HeartbeatHandler(req) handler.handle() handler._response diff --git a/src/frontends/gamespy/library/abstractions/brocker.py b/src/frontends/gamespy/library/abstractions/brocker.py index 46468c071..8e6a3541d 100644 --- a/src/frontends/gamespy/library/abstractions/brocker.py +++ b/src/frontends/gamespy/library/abstractions/brocker.py @@ -6,18 +6,19 @@ class BrockerBase: _subscriber: object is_started: bool = False _name: str - _call_back_func: Callable + _call_back_func: Callable | None """ brocker subscribe name """ - def __init__(self, name: str, url: str, call_back_func: Callable) -> None: + def __init__(self, name: str, url: str, call_back_func: Callable | None) -> None: assert isinstance(name, str) - assert callable(call_back_func) self._name = name - self._call_back_func = call_back_func self.url = url + if call_back_func is not None: + assert callable(call_back_func) + self._call_back_func = call_back_func @abc.abstractmethod def subscribe(self): @@ -27,14 +28,15 @@ def subscribe(self): pass @final - def receive_message(self, message: dict): - assert isinstance(message, dict) + def receive_message(self, message: str): + assert isinstance(message, str) + if self._call_back_func is None: + return self._call_back_func(message) - pass @abc.abstractmethod - def publish_message(self, message): - pass + def publish_message(self, message: str): + assert isinstance(message, str) @abc.abstractmethod def unsubscribe(self): diff --git a/src/frontends/gamespy/library/abstractions/enctypt_base.py b/src/frontends/gamespy/library/abstractions/enctypt_base.py index efc1d6d7d..267234a20 100644 --- a/src/frontends/gamespy/library/abstractions/enctypt_base.py +++ b/src/frontends/gamespy/library/abstractions/enctypt_base.py @@ -1,5 +1,101 @@ from abc import abstractmethod +class Byte: + value: int + + def __init__(self, value): + if value > 255: + raise ValueError("byte should be in 0 to 256") + self.value = value + + def _clamp(self, value) -> int: + """clamp value in 0 to 255""" + return value % 256 + + def __add__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must on Byte object") + new = self._clamp(self.value + other.value) + return Byte(new) + + def __sub__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must on Byte object") + new = self._clamp(self.value - other.value) + return Byte(new) + + def __mul__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must on Byte object") + new = self._clamp(self.value * other.value) + return Byte(new) + + def __truediv__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must on Byte object") + if other.value == 0: + raise ValueError("Cannot divide by zero") + new = self._clamp(self.value // other.value) + return Byte(new) + + def __xor__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must on Byte object") + new = self._clamp(self.value ^ other.value) + return Byte(new) + + def __lt__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must be on Byte object") + return self.value < other.value + + def __le__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must be on Byte object") + return self.value <= other.value + + def __gt__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must be on Byte object") + return self.value > other.value + + def __ge__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must be on Byte object") + return self.value >= other.value + + def __eq__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must be on Byte object") + return self.value == other.value + + def __lshift__(self, other): + """Perform in-place left shift operation.""" + if not isinstance(other, Byte): + raise TypeError("operation must be on Byte object") + if other < Byte(0): + raise ValueError("Shift amount must be non-negative") + return Byte(self.value << other.value) + + def __and__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must be on Byte object") + return Byte(self.value & other.value) + + def __mod__(self, other): + if not isinstance(other, Byte): + raise TypeError("operation must be on Byte object") + return Byte(self.value % other.value) + + def __repr__(self): + return f"Byte({self.value})" + + @staticmethod + def from_bytes(data: bytes | bytearray): + temp = [] + for d in data: + temp.append(Byte(d)) + return temp class EncryptBase: @abstractmethod diff --git a/src/frontends/gamespy/library/network/brockers.py b/src/frontends/gamespy/library/network/websocket_brocker.py similarity index 55% rename from src/frontends/gamespy/library/network/brockers.py rename to src/frontends/gamespy/library/network/websocket_brocker.py index 469a8ee9c..7beaad61a 100644 --- a/src/frontends/gamespy/library/network/brockers.py +++ b/src/frontends/gamespy/library/network/websocket_brocker.py @@ -1,48 +1,12 @@ import threading -from typing import Callable from uuid import UUID -from redis import Redis from websockets import ConnectionClosed from frontends.gamespy.library.abstractions.brocker import BrockerBase -from redis.client import PubSub from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER -from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage from websockets.sync.client import connect, ClientConnection -class RedisBrocker(BrockerBase): - _client: Redis - _subscriber: PubSub - - def __init__(self, name: str, url: str, call_back_func: Callable) -> None: - super().__init__(name, url, call_back_func) - self._client = Redis.from_url(url) - self._subscriber = self._client.pubsub() - - def subscribe(self): - self.is_started = True - self._subscriber.subscribe(self._name) - threading.Thread(target=self.get_message).start() - - def get_message(self): - for message in self._subscriber.listen(): - if not self.is_started: - break - if message["type"] == "message": - msg = message["data"].decode("utf-8") - # run receive message in background do not block receiving - threading.Thread(target=self.receive_message, args=msg) - - def unsubscribe(self): - self.is_started = False - self._subscriber.unsubscribe(self._name) - self._subscriber.close() - - def publish_message(self, message): - self._client.publish(self._name, message) - - class WebSocketBrocker(BrockerBase): _subscriber: ClientConnection _publisher: ClientConnection @@ -58,6 +22,10 @@ def ip_port(self) -> str: return f"{name[0]}:{name[1]}" def _listen(self): + # we do not listen to channel, if the call back is none + if self._call_back_func is None: + return + try: while True: message = self._subscriber.recv() @@ -69,10 +37,11 @@ def _listen(self): def unsubscribe(self): self._subscriber.close() - def publish_message(self, message: BrockerMessage): + def publish_message(self, message: str): + super().publish_message(message) if self._publisher is None: raise ValueError("websocket connection is not established") - self._publisher.send(message.model_dump_json()) + self._publisher.send(message) COUNT = 0 @@ -80,7 +49,7 @@ def publish_message(self, message: BrockerMessage): def call_back(str): global COUNT - COUNT +=1 + COUNT += 1 print(COUNT) # print(f"{datetime.now()}:{str}") @@ -92,6 +61,8 @@ def call_back(str): call_back_func=call_back, ) ws.subscribe() + from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage + msg = BrockerMessage( server_id=UUID("08ed7859-1d9e-448b-8fda-dabb845d85f9"), channel_name="gmtest", @@ -99,5 +70,7 @@ def call_back(str): sender_port=80, message="hello", ) + # while True: while True: - ws.publish_message(msg) + ws.publish_message(msg.model_dump_json()) + pass diff --git a/src/frontends/gamespy/protocols/chat/abstractions/handler.py b/src/frontends/gamespy/protocols/chat/abstractions/handler.py index 62c29bd70..e4a631474 100644 --- a/src/frontends/gamespy/protocols/chat/abstractions/handler.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/handler.py @@ -114,7 +114,7 @@ def broadcast(self): message=self._response.sending_buffer, ) assert self._client.brocker - self._client.brocker.publish_message(msg) + self._client.brocker.publish_message(msg.model_dump_json()) self._client.log_network_broadcast(msg) @@ -158,4 +158,4 @@ class MessageHandlerBase(ChannelHandlerBase): def __init__(self, client: ClientBase, request: MessageRequestBase): assert isinstance(request, MessageRequestBase) super().__init__(client, request) - self._is_broadcast = True \ No newline at end of file + self._is_broadcast = True diff --git a/src/frontends/gamespy/protocols/chat/applications/broker.py b/src/frontends/gamespy/protocols/chat/applications/broker.py index 4e94dc890..e99634ed2 100644 --- a/src/frontends/gamespy/protocols/chat/applications/broker.py +++ b/src/frontends/gamespy/protocols/chat/applications/broker.py @@ -1,6 +1,6 @@ -from frontends.gamespy.library.network.brockers import WebSocketBrocker +from frontends.gamespy.library.network.websocket_brocker import WebSocketBrocker # class Brocker(WebSocketBrocker): diff --git a/src/frontends/gamespy/protocols/chat/applications/client.py b/src/frontends/gamespy/protocols/chat/applications/client.py index 7163c99a6..1b0c2a597 100644 --- a/src/frontends/gamespy/protocols/chat/applications/client.py +++ b/src/frontends/gamespy/protocols/chat/applications/client.py @@ -2,7 +2,7 @@ from frontends.gamespy.library.abstractions.switcher import SwitcherBase from frontends.gamespy.library.log.log_manager import LogWriter -from frontends.gamespy.library.network.brockers import WebSocketBrocker +from frontends.gamespy.library.network.websocket_brocker import WebSocketBrocker from frontends.gamespy.library.network.tcp_handler import TcpConnection from frontends.gamespy.library.configs import CONFIG, ServerConfig diff --git a/src/frontends/gamespy/protocols/chat/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py index 2fefe01e6..ac210f882 100644 --- a/src/frontends/gamespy/protocols/chat/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -161,9 +161,7 @@ def __init__(self, client: ClientBase, request: ListRequest): assert isinstance(request, ListRequest) super().__init__(client, request) self._result_cls = ListResult - - def _response_construct(self) -> None: - self._response = ListResponse(self._result) + self._response_cls = ListResponse class LoginHandler(CmdHandlerBase): @@ -174,9 +172,7 @@ def __init__(self, client: ClientBase, request: LoginRequest): assert isinstance(request, LoginRequest) super().__init__(client, request) self._result_cls = LoginResult - - def _response_construct(self) -> None: - self._response = LoginResponse(self._result) + self._response_cls = LoginResponse class NickHandler(CmdHandlerBase): @@ -206,7 +202,6 @@ def __init__(self, client: ClientBase, request: PingRequest): self._response_cls = PingResponse - class QuitHandler(CmdHandlerBase): _request: QuitRequest @@ -236,9 +231,6 @@ def _data_operate(self) -> None: super()._data_operate() self._client.info.user_name = self._request.user_name - def _request_check(self) -> None: - super()._request_check() - class UserIPHandler(CmdHandlerBase): _request: UserIPRequest @@ -404,11 +396,9 @@ def __init__(self, client: ClientBase, request: SetChannelKeyRequest): assert isinstance(request, SetChannelKeyRequest) super().__init__(client, request) self._result_cls = SetChannelKeyResult + self._response_cls = SetChannelKeyResponse self._is_broadcast = True - def _response_construct(self): - self._response = SetChannelKeyResponse(self._result) - class SetCKeyHandler(ChannelHandlerBase): _request: SetCKeyRequest diff --git a/src/frontends/gamespy/protocols/game_status/applications/handlers.py b/src/frontends/gamespy/protocols/game_status/applications/handlers.py index 36c4e7b1b..3131e38d8 100644 --- a/src/frontends/gamespy/protocols/game_status/applications/handlers.py +++ b/src/frontends/gamespy/protocols/game_status/applications/handlers.py @@ -28,9 +28,8 @@ def __init__(self, client: Client, request: AuthPlayerRequest) -> None: assert isinstance(request, AuthPlayerRequest) super().__init__(client, request) self._result_cls = AuthPlayerResult + self._response_cls = AuthPlayerResponse - def _response_construct(self) -> None: - self._response = AuthPlayerResponse(self._result) class GetPlayerDataHandler(CmdHandlerBase): @@ -40,9 +39,9 @@ def __init__(self, client: Client, request: GetPlayerDataRequest) -> None: assert isinstance(request, GetPlayerDataRequest) super().__init__(client, request) self._result_cls = GetPlayerDataResult + self._response_cls = GetPlayerDataResponse + - def _response_construct(self) -> None: - self._response = GetPlayerDataResponse(self._result) class GetProfileIdHandler(CmdHandlerBase): @@ -52,9 +51,8 @@ def __init__(self, client: Client, request: GetProfileIdRequest) -> None: assert isinstance(request, GetProfileIdRequest) super().__init__(client, request) self._result_cls = GetProfileIdResult + self._response_cls = GetProfileIdResponse - def _response_construct(self) -> None: - self._response = GetProfileIdResponse(self._result) class NewGameHandler(CmdHandlerBase): diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/broker.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/broker.py index 578eaffcf..75dcc740c 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/broker.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/broker.py @@ -1,4 +1,4 @@ -from frontends.gamespy.library.network.brockers import WebSocketBrocker +from frontends.gamespy.library.network.websocket_brocker import WebSocketBrocker class Broker(WebSocketBrocker): diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py index 31c6b7334..0396cb434 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py @@ -152,6 +152,7 @@ class BlockListHandler(CmdHandlerBase): def __init__(self, client: Client) -> None: assert isinstance(client, Client) self._is_fetching = False + raise NotImplementedError() def _response_construct(self) -> None: self._response = BlockListResponse(self._result) diff --git a/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py b/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py index 87ed9a536..0d69c3631 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py +++ b/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py @@ -53,9 +53,7 @@ def __init__(self, client: Client, request: OthersRequest) -> None: assert isinstance(request, OthersRequest) super().__init__(client, request) self._result_cls = OthersResult - - def _response_construct(self): - self._response = OthersResponse(self._result) + self._response_cls = OthersResponse class OthersListHandler(CmdHandlerBase): @@ -66,9 +64,7 @@ def __init__(self, client: Client, request: OthersListRequest) -> None: assert isinstance(request, OthersListRequest) super().__init__(client, request) self._result_cls = OthersListResult - - def _response_construct(self): - self._response = OthersListResponse(self._result) + self._response_cls = OthersListResponse class SearchHandler(CmdHandlerBase): @@ -91,9 +87,7 @@ def __init__(self, client: Client, request: SearchRequest) -> None: assert isinstance(request, SearchRequest) super().__init__(client, request) self._result_cls = SearchResult - - def _response_construct(self): - self._response = SearchResponse(self._result) + self._response_cls = SearchResponse class SearchUniqueHandler(CmdHandlerBase): diff --git a/src/frontends/gamespy/protocols/query_report/aggregates/natneg_channel.py b/src/frontends/gamespy/protocols/query_report/aggregates/natneg_channel.py index 084e77386..6c4351f6f 100644 --- a/src/frontends/gamespy/protocols/query_report/aggregates/natneg_channel.py +++ b/src/frontends/gamespy/protocols/query_report/aggregates/natneg_channel.py @@ -2,7 +2,7 @@ from frontends.gamespy.library.abstractions.brocker import BrockerBase from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER -from frontends.gamespy.library.network.brockers import WebSocketBrocker +from frontends.gamespy.library.network.websocket_brocker import WebSocketBrocker from frontends.gamespy.protocols.query_report.v2.applications.handlers import ClientMessageHandler from frontends.gamespy.protocols.query_report.v2.contracts.requests import ClientMessageRequest from types import MappingProxyType diff --git a/src/frontends/gamespy/protocols/query_report/applications/client.py b/src/frontends/gamespy/protocols/query_report/applications/client.py index fba3c4b31..d83fbf0ec 100644 --- a/src/frontends/gamespy/protocols/query_report/applications/client.py +++ b/src/frontends/gamespy/protocols/query_report/applications/client.py @@ -1,7 +1,8 @@ from frontends.gamespy.library.abstractions.client import ClientBase from frontends.gamespy.library.abstractions.connections import ConnectionBase -from frontends.gamespy.library.configs import ServerConfig +from frontends.gamespy.library.configs import CONFIG, ServerConfig from frontends.gamespy.library.log.log_manager import LogWriter +from frontends.gamespy.library.network.websocket_brocker import WebSocketBrocker # import servers.query_report.v1 @@ -22,3 +23,31 @@ def _create_switcher(self, buffer: bytes): return V1CmdSwitcher(self, (buffer)) else: return V2CmdSwitcher(self, buffer) + + def start_brocker(self): + self.brocker = WebSocketBrocker( + name=self.server_config.server_name, + url=f"{CONFIG.backend.url.replace('http', 'ws')}/GameSpy/Chat/ws", + call_back_func=self._process_brocker_message, + ) + self.brocker.subscribe() + + def stop_brocker(self): + assert self.brocker is not None + self.brocker.unsubscribe() + + def _process_brocker_message(self, message: str): + from frontends.gamespy.protocols.query_report.v2.contracts.requests import ClientMessageRequest + from frontends.gamespy.protocols.query_report.v2.applications.handlers import ClientMessageHandler + + # responsible for receive message and send out + # TODO: check whether exception here will cause brocker stop + try: + assert isinstance(message, str) + # deserialize str to object + request = ClientMessageRequest() + handler = ClientMessageHandler(self, request) + handler.handle() + except Exception as e: + # todo change to log and handle exception here + print(e) diff --git a/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py b/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py index 75f317f8a..7c8d56632 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py +++ b/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py @@ -8,20 +8,20 @@ ClientMessageAckRequest, ClientMessageRequest, EchoRequest, - HeartBeatRequest, + HeartbeatRequest, KeepAliveRequest, ) from frontends.gamespy.protocols.query_report.v2.contracts.responses import ( AvailableResponse, ChallengeResponse, ClientMessageResponse, - HeartBeatResponse, + HeartbeatResponse, ) from frontends.gamespy.protocols.query_report.v2.contracts.results import ( AvailableResult, ChallengeResult, ClientMessageResult, - HeartBeatResult, + HeartbeatResult, ) @@ -76,24 +76,24 @@ def __init__(self, client: Client, request: EchoRequest) -> None: super().__init__(client, request) -class HeartBeatHandler(CmdHandlerBase): - _request: HeartBeatRequest - _result: HeartBeatResult - _result_cls: type[HeartBeatResult] +class HeartbeatHandler(CmdHandlerBase): + _request: HeartbeatRequest + _result: HeartbeatResult + _result_cls: type[HeartbeatResult] - def __init__(self, client: Client, request: HeartBeatRequest) -> None: - assert isinstance(request, HeartBeatRequest) + def __init__(self, client: Client, request: HeartbeatRequest) -> None: + assert isinstance(request, HeartbeatRequest) super().__init__(client, request) self._is_fetching = False def _response_construct(self) -> None: - self._result = HeartBeatResult( + self._result = HeartbeatResult( remote_ip=self._client.connection.remote_ip, remote_port=self._client.connection.remote_port, instant_key=self._request.instant_key, command_name=self._request.command_name ) - self._response = HeartBeatResponse(self._result) + self._response = HeartbeatResponse(self._result) class KeepAliveHandler(CmdHandlerBase): diff --git a/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py b/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py index 67d81a5e3..2dba3d64e 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py +++ b/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py @@ -9,7 +9,7 @@ ChallengeRequest, ClientMessageAckRequest, EchoRequest, - HeartBeatRequest, + HeartbeatRequest, KeepAliveRequest, ) from frontends.gamespy.protocols.query_report.v2.aggregates.enums import RequestType @@ -18,7 +18,7 @@ ChallengeHanler, ClientMessageAckHandler, EchoHandler, - HeartBeatHandler, + HeartbeatHandler, KeepAliveHandler, ) @@ -43,7 +43,7 @@ def _create_cmd_handlers(self, name: RequestType, raw_request: bytes) -> CmdHand self._client = cast(Client, self._client) match name: case RequestType.HEARTBEAT: - return HeartBeatHandler(self._client, HeartBeatRequest(raw_request)) + return HeartbeatHandler(self._client, HeartbeatRequest(raw_request)) case RequestType.CHALLENGE: return ChallengeHanler(self._client, ChallengeRequest(raw_request)) case RequestType.AVALIABLE_CHECK: diff --git a/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py b/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py index 4bd79acdc..0fc5f09a3 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py +++ b/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py @@ -48,16 +48,15 @@ def target_ip_endpoint(self) -> str: def __init__(self, raw_request: bytes | None = None) -> None: if raw_request is not None: super().__init__(raw_request) + -class HeartBeatRequest(RequestBase): +class HeartbeatRequest(RequestBase): server_data: dict[str, str] | None player_data: list[dict[str, str]] | None team_data: list[dict[str, str]] | None server_status: GameServerStatus group_id: int | None - remote_ip: str - remote_port: int game_name: str def __init__(self, raw_request: bytes) -> None: @@ -119,7 +118,6 @@ def parse(self): else: raise QRException("HeartBeat request is invalid.") - def parse_server_data(self, server_data_str: str): self.server_data = {} key_value_array = server_data_str.split("\0") @@ -141,7 +139,7 @@ def parse_server_data(self, server_data_str: str): else: self.server_status = GameServerStatus( int(self.server_data["statechanged"])) - + if "groupid" in self.server_data: group_id = 0 if not int(self.server_data["groupid"], group_id): diff --git a/src/frontends/gamespy/protocols/query_report/v2/contracts/responses.py b/src/frontends/gamespy/protocols/query_report/v2/contracts/responses.py index 7be912de3..f79b06799 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/contracts/responses.py +++ b/src/frontends/gamespy/protocols/query_report/v2/contracts/responses.py @@ -4,14 +4,14 @@ AvaliableRequest, ChallengeRequest, ClientMessageRequest, - HeartBeatRequest, + HeartbeatRequest, KeepAliveRequest ) from frontends.gamespy.protocols.query_report.v2.contracts.results import ( AvailableResult, ChallengeResult, ClientMessageResult, - HeartBeatResult, + HeartbeatResult, ) from frontends.gamespy.protocols.query_report.v2.aggregates.enums import ServerAvailability from frontends.gamespy.library.extentions.bytes_extentions import ip_to_4_bytes, port_to_2_bytes @@ -67,11 +67,11 @@ def build(self): SPLITER = bytes([0x00, 0x00, 0x00, 0x00]) -class HeartBeatResponse(ResponseBase): - _result: HeartBeatResult +class HeartbeatResponse(ResponseBase): + _result: HeartbeatResult - def __init__(self, result: HeartBeatResult) -> None: - assert isinstance(result, HeartBeatResult) + def __init__(self, result: HeartbeatResult) -> None: + assert isinstance(result, HeartbeatResult) super().__init__(result) def build(self) -> None: diff --git a/src/frontends/gamespy/protocols/query_report/v2/contracts/results.py b/src/frontends/gamespy/protocols/query_report/v2/contracts/results.py index 50a68d859..229a4d91e 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/contracts/results.py +++ b/src/frontends/gamespy/protocols/query_report/v2/contracts/results.py @@ -27,7 +27,7 @@ class EchoResult(ResultBase): @final -class HeartBeatResult(ResultBase): +class HeartbeatResult(ResultBase): """ this result is replied in unispy server """ diff --git a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/encryption.py b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/encryption.py index 6e3f12977..e71bbc865 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/encryption.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/encryption.py @@ -1,106 +1,8 @@ -from frontends.gamespy.library.abstractions.enctypt_base import EncryptBase +from frontends.gamespy.library.abstractions.enctypt_base import Byte, EncryptBase SERVER_CHALLENGE = "0000000000" -class Byte: - value: int - - def __init__(self, value): - if value > 255: - raise ValueError("byte should be in 0 to 256") - self.value = value - - def _clamp(self, value) -> int: - """clamp value in 0 to 255""" - return value % 256 - - def __add__(self, other): - if not isinstance(other, Byte): - raise TypeError("operation must on Byte object") - new = self._clamp(self.value + other.value) - return Byte(new) - - def __sub__(self, other): - if not isinstance(other, Byte): - raise TypeError("operation must on Byte object") - new = self._clamp(self.value - other.value) - return Byte(new) - - def __mul__(self, other): - if not isinstance(other, Byte): - raise TypeError("operation must on Byte object") - new = self._clamp(self.value * other.value) - return Byte(new) - - def __truediv__(self, other): - if not isinstance(other, Byte): - raise TypeError("operation must on Byte object") - if other.value == 0: - raise ValueError("Cannot divide by zero") - new = self._clamp(self.value // other.value) - return Byte(new) - - def __xor__(self, other): - if not isinstance(other, Byte): - raise TypeError("operation must on Byte object") - new = self._clamp(self.value ^ other.value) - return Byte(new) - - def __lt__(self, other): - if not isinstance(other, Byte): - raise TypeError("operation must be on Byte object") - return self.value < other.value - - def __le__(self, other): - if not isinstance(other, Byte): - raise TypeError("operation must be on Byte object") - return self.value <= other.value - - def __gt__(self, other): - if not isinstance(other, Byte): - raise TypeError("operation must be on Byte object") - return self.value > other.value - - def __ge__(self, other): - if not isinstance(other, Byte): - raise TypeError("operation must be on Byte object") - return self.value >= other.value - - def __eq__(self, other): - if not isinstance(other, Byte): - raise TypeError("operation must be on Byte object") - return self.value == other.value - - def __lshift__(self, other): - """Perform in-place left shift operation.""" - if not isinstance(other, Byte): - raise TypeError("operation must be on Byte object") - if other < Byte(0): - raise ValueError("Shift amount must be non-negative") - return Byte(self.value << other.value) - - def __and__(self, other): - if not isinstance(other, Byte): - raise TypeError("operation must be on Byte object") - return Byte(self.value & other.value) - - def __mod__(self, other): - if not isinstance(other, Byte): - raise TypeError("operation must be on Byte object") - return Byte(self.value % other.value) - - def __repr__(self): - return f"Byte({self.value})" - - @staticmethod - def from_bytes(data: bytes | bytearray): - temp = [] - for d in data: - temp.append(Byte(d)) - return temp - - class EncryptionParameters: register: list[Byte] index_0: Byte diff --git a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/enums.py b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/enums.py index 7d5141b43..013704d85 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/enums.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/enums.py @@ -9,15 +9,15 @@ class PlayerSearchOptions(IntEnum): class QueryType(IntEnum): - BASIC = 1 - FULL = 2 - ICMP = 3 + BASIC = 0 + FULL = 1 + ICMP = 2 class DataKeyType(IntEnum): - STRING = 1 - BYTE = 2 - SHORT = 3 + STRING = 0 + BYTE = 1 + SHORT = 2 class RequestType(IntEnum): @@ -28,6 +28,7 @@ class RequestType(IntEnum): MAP_LOOP_REQUEST = 0x04 PLAYER_SEARCH_REQUEST = 0x05 + class ResponseType(IntEnum): PUSH_KEYS_MESSAGE = 1 PUSH_SERVER_MESSAGE = 2 @@ -45,7 +46,10 @@ class ProtocolVersion(IntEnum): class ServerListUpdateOption(IntEnum): SERVER_MAIN_LIST = 0 SEND_FIELD_FOR_ALL = 1 - SERVER_FULL_MAIN_LIST = 2 + SERVER_FULL_INFO_LIST = 2 + """ + get the full info of a server + """ P2P_SERVER_MAIN_LIST = 4 ALTERNATE_SOURCE_IP = 8 P2P_GROUP_ROOM_LIST = 32 diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py index 8b1f192e2..2cdff6e06 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py @@ -22,12 +22,12 @@ DeleteServerInfoResponse, P2PGroupRoomListResponse, ServerMainListResponse, - ServerNetworkInfoListResponse, + ServerFullInfoListResponse, UpdateServerInfoResponse, ) from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( P2PGroupRoomListResult, - ServerNetworkInfoListResult, + ServerFullInfoListResult, ServerInfoResult, ServerMainListResult, ) @@ -100,28 +100,12 @@ def send_message(self, client: Client): class SendMessageHandler(CmdHandlerBase): _request: SendMessageRequest - _result: GameServerInfo def __init__(self, client: Client, request: SendMessageRequest) -> None: assert isinstance(request, SendMessageRequest) - # super().__init__(client, request) - assert isinstance(client, Client) - - def _response_construct(self) -> None: - message = ClientMessageRequest() - message.server_browser_sender_id = self._client.server_config.server_id - message.natneg_message = self._request.client_message - message.instant_key = self._result.instant_key - message.target_ip_address = self._result.host_ip_address - message.target_port = self._result.query_report_port - message.command_name = RequestType.CLIENT_MESSAGE - - def _response_send(self) -> None: - """ - QueryReport.V2.Application.StorageOperation.Persistance.PublishClientMessage(message); - _client.LogInfo($"Send client message to QueryReport Server: {gameServer.ServerID} [{StringExtensions.ConvertByteToHexString(message.NatNegMessage)}]"); - """ - raise NotImplementedError() + super().__init__(client, request) + self._is_fetching = False + # we just need send the message to backend, then backend will send to queryreport frontend, query report frontend will handle for us class ServerInfoHandler(CmdHandlerBase): @@ -156,12 +140,17 @@ def __init__(self, client: Client, request: RequestBase) -> None: self._response_cls = P2PGroupRoomListResponse -class ServerNetworkInfoListHandler(ServerListUpdateOptionHandlerBase): +class ServerFullInfoListHandler(ServerListUpdateOptionHandlerBase): + """ + In sbctest.c + line 392 ServerBrowserAuxUpdateServer(sb, server, async, fullUpdate); + will get the full info of a server such as: player data, server data, team data + """ _request: ServerListRequest - _result: ServerNetworkInfoListResult - _result_cls: type[ServerNetworkInfoListResult] + _result: ServerFullInfoListResult + _result_cls: type[ServerFullInfoListResult] def __init__(self, client: Client, request: RequestBase) -> None: super().__init__(client, request) - self._result_cls = ServerNetworkInfoListResult - self._response_cls = ServerNetworkInfoListResponse + self._result_cls = ServerFullInfoListResult + self._response_cls = ServerFullInfoListResponse diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py index 93d629a9e..d90cdae10 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py @@ -15,7 +15,7 @@ from frontends.gamespy.protocols.server_browser.v2.applications.handlers import ( P2PGroupRoomListHandler, SendMessageHandler, - ServerNetworkInfoListHandler, + ServerFullInfoListHandler, ServerInfoHandler, ServerMainListHandler, ) @@ -28,6 +28,7 @@ class Switcher(SwitcherBase): _raw_request: bytes + _client: Client def _process_raw_request(self) -> None: if len(self._raw_request) < 4: @@ -47,32 +48,47 @@ def _create_cmd_handlers( self._client = cast(Client, self._client) match name: case RequestType.SERVER_LIST_REQUEST: - update_option_index = raw_request.find(b"\x00\x00\x00\x00",6) + # todo check if all game follow this pattern, +2 is calc by sdk server info request + update_option_index = raw_request.find( + b"\x00\x00\x00\x00", 6)+2 update_option_bytes = raw_request[ - update_option_index : update_option_index + 4 + update_option_index: update_option_index + 4 ] update_option = ServerListUpdateOption( int.from_bytes(update_option_bytes) ) - if update_option in [ - ServerListUpdateOption.SERVER_MAIN_LIST, - ServerListUpdateOption.P2P_SERVER_MAIN_LIST, - ServerListUpdateOption.LIMIT_RESULT_COUNT, - ]: - return ServerMainListHandler(self._client, ServerListRequest(req)) - elif update_option == ServerListUpdateOption.P2P_GROUP_ROOM_LIST: - return P2PGroupRoomListHandler(self._client, ServerListRequest(req)) - elif update_option == ServerListUpdateOption.SERVER_FULL_MAIN_LIST: - return ServerNetworkInfoListHandler( - self._client, ServerListRequest(req) - ) - else: - raise ServerBrowserException( - "unknown serverlist update option type" - ) + handler = self.__create_cmd_by_update_option( + update_option, req) + return handler case RequestType.SERVER_INFO_REQUEST: return ServerInfoHandler(self._client, ServerInfoRequest(req)) case RequestType.SEND_MESSAGE_REQUEST: return SendMessageHandler(self._client, SendMessageRequest(req)) case _: return None + + def __create_cmd_by_update_option(self, update_option: ServerListUpdateOption, request: bytes) -> CmdHandlerBase: + match update_option: + case (ServerListUpdateOption.SERVER_MAIN_LIST + | ServerListUpdateOption.P2P_SERVER_MAIN_LIST + | ServerListUpdateOption.LIMIT_RESULT_COUNT): + return ServerMainListHandler(self._client, ServerListRequest(request)) + case ServerListUpdateOption.P2P_GROUP_ROOM_LIST: + return P2PGroupRoomListHandler(self._client, ServerListRequest(request)) + case ServerListUpdateOption.SERVER_FULL_INFO_LIST: + return ServerFullInfoListHandler(self._client, ServerListRequest(request)) + case _: + raise ServerBrowserException( + "unknown serverlist update option type") + + @staticmethod + def get_update_option(raw_request: bytes) -> ServerListUpdateOption: + update_option_index = raw_request.find( + b"\x00\x00\x00\x00", 6)+2 + update_option_bytes = raw_request[ + update_option_index: update_option_index + 4 + ] + update_option = ServerListUpdateOption( + int.from_bytes(update_option_bytes, byteorder='big') + ) + return update_option diff --git a/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py index 72e0607c2..a64648dbc 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/requests.py @@ -24,24 +24,25 @@ def parse(self) -> None: remain_data = self.raw_request[9:] dev_game_name_index = remain_data.index(0) self.dev_game_name = remain_data[:dev_game_name_index].decode() - remain_data = remain_data[dev_game_name_index + 1 :] + remain_data = remain_data[dev_game_name_index + 1:] game_name_index = remain_data.index(0) self.game_name = remain_data[:game_name_index].decode() - remain_data = remain_data[game_name_index + 1 :] + remain_data = remain_data[game_name_index + 1:] self.client_challenge = remain_data[:8].decode() remain_data = remain_data[8:] filter_index = remain_data.index(0) if filter_index > 0: self.filter = remain_data[:filter_index].decode() - remain_data = remain_data[filter_index + 1 :] + remain_data = remain_data[filter_index + 1:] keys_index = remain_data.index(0) self.keys = remain_data[1:keys_index].decode().split("\\") - remain_data = remain_data[keys_index + 1 :] + remain_data = remain_data[keys_index + 1:] byte_update_options = remain_data[:4] - self.update_option = ServerListUpdateOption(int.from_bytes(byte_update_options)) + self.update_option = ServerListUpdateOption( + int.from_bytes(byte_update_options)) remain_data = remain_data[4:] if self.update_option & ServerListUpdateOption.ALTERNATE_SOURCE_IP: @@ -57,6 +58,7 @@ def parse(self) -> None: class SendMessageRequest(AdHocRequestBase): prefix_message: bytes client_message: bytes + natneg_cookie: int def __init__(self, raw_request: bytes | None = None) -> None: if raw_request is not None: @@ -65,6 +67,7 @@ def __init__(self, raw_request: bytes | None = None) -> None: def parse(self) -> None: super().parse() self.client_message = self.raw_request[9:] + self.natneg_cookie = int.from_bytes(self.client_message[6:10]) class ServerInfoRequest(AdHocRequestBase): diff --git a/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py index 44cfa0403..2b7c373f8 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py @@ -10,6 +10,7 @@ from frontends.gamespy.protocols.server_browser.v2.contracts.requests import ServerListRequest from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( P2PGroupRoomListResult, + ServerFullInfoListResult, ServerInfoResult, ServerMainListResult, ) @@ -130,9 +131,33 @@ def build(self) -> None: self.sending_buffer = bytes(self._servers_info_buffers) -class ServerNetworkInfoListResponse(ServerListUpdateOptionResponseBase): +class ServerFullInfoListResponse(ServerMainListResponse): + _result: ServerFullInfoListResult def build(self) -> None: super().build() + self.__build_servers_full_info() self.sending_buffer = bytes(self._servers_info_buffers) - raise NotImplementedError() + + def __build_servers_full_info(self): + for info in self._result.servers_info: + header = build_server_info_header(self._result.flag, info) + self._servers_info_buffers.extend(header) + + for key, value in info.server_data.items(): + self.__add_key_value_to_buffer(key, value) + + for item in info.player_data: + for key, value in item.items(): + self.__add_key_value_to_buffer(key, value) + + for item in info.team_data: + for key, value in item.items(): + self.__add_key_value_to_buffer(key, value) + + def __add_key_value_to_buffer(self, key, value): + self._servers_info_buffers.extend( + get_bytes(key)) + self._servers_info_buffers.extend(STRING_SPLITER) + self._servers_info_buffers.extend(get_bytes(value)) + self._servers_info_buffers.extend(STRING_SPLITER) diff --git a/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py index 41b6ee82e..5327aceaf 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py @@ -11,6 +11,7 @@ ResultBase, ServerListUpdateOptionResultBase, ) +from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import GameServerFlags class ServerInfoResult(AdHocResultBase): @@ -23,10 +24,12 @@ class P2PGroupRoomListResult(ServerListUpdateOptionResultBase): class ServerMainListResult(ServerListUpdateOptionResultBase): servers_info: list[GameServerInfo] + flag: GameServerFlags = GameServerFlags.HAS_KEYS_FLAG -class ServerNetworkInfoListResult(ServerListUpdateOptionResultBase): +class ServerFullInfoListResult(ServerListUpdateOptionResultBase): servers_info: list[GameServerInfo] + flag: GameServerFlags = GameServerFlags.HAS_FULL_RULES_FLAG class SendMessageResult(ResultBase): diff --git a/src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py b/src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py index 049054ea4..86652a582 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py @@ -1,4 +1,6 @@ +from frontends.gamespy.protocols.web_services.abstractions.contracts import RequestBase from frontends.gamespy.protocols.web_services.abstractions.handler import CmdHandlerBase +from frontends.gamespy.protocols.web_services.applications.client import Client from frontends.gamespy.protocols.web_services.modules.auth.contracts.requests import ( LoginProfileRequest, LoginProfileWithGameIdRequest, @@ -12,6 +14,8 @@ from frontends.gamespy.protocols.web_services.modules.auth.contracts.responses import ( LoginProfileResponse, LoginProfileWithGameIdResponse, + LoginPs3CertResponse, + LoginPs3CertWithGameIdResponse, LoginRemoteAuthResponse, LoginRemoteAuthWithGameIdResponse, LoginUniqueNickResponse, @@ -29,55 +33,77 @@ class LoginProfileHandler(CmdHandlerBase): _request: LoginProfileRequest _result: LoginProfileResult - def _response_construct(self) -> None: - self._response = LoginProfileResponse(self._result) + def __init__(self, client: Client, request: RequestBase) -> None: + super().__init__(client, request) + self._result_cls = LoginProfileResult + self._response_cls = LoginProfileResponse class LoginProfileWithGameIdHandler(CmdHandlerBase): _request: LoginProfileWithGameIdRequest _result: LoginProfileResult - def _response_construct(self) -> None: - self._response = LoginProfileWithGameIdResponse(self._result) + def __init__(self, client: Client, request: RequestBase) -> None: + super().__init__(client, request) + self._result_cls = LoginProfileResult + self._response_cls = LoginProfileWithGameIdResponse class LoginPs3CertHandler(CmdHandlerBase): _request: LoginPs3CertRequest _result: LoginPs3CertResult + def __init__(self, client: Client, request: LoginPs3CertRequest) -> None: + super().__init__(client, request) + self._result_cls = LoginPs3CertResult + self._response_cls = LoginPs3CertResponse + class LoginPs3CertWithGameIdHandler(CmdHandlerBase): _request: LoginPs3CertWithGameIdRequest _result: LoginPs3CertResult + def __init__(self, client: Client, request: LoginPs3CertRequest) -> None: + super().__init__(client, request) + self._result_cls = LoginPs3CertResult + self._response_cls = LoginPs3CertWithGameIdResponse + class LoginRemoteAuthHandler(CmdHandlerBase): _request: LoginRemoteAuthRequest _result: LoginRemoteAuthResult - def _response_construct(self) -> None: - self._response = LoginRemoteAuthResponse(self._result) + def __init__(self, client: Client, request: LoginRemoteAuthRequest) -> None: + super().__init__(client, request) + self._result_cls = LoginRemoteAuthResult + self._response_cls = LoginRemoteAuthResponse class LoginRemoteAuthWithGameIdHandler(CmdHandlerBase): _request: LoginRemoteAuthWithGameIdRequest _result: LoginRemoteAuthResult - def _response_construct(self) -> None: - self._response = LoginRemoteAuthWithGameIdResponse(self._result) + def __init__(self, client: Client, request: LoginRemoteAuthWithGameIdRequest) -> None: + super().__init__(client, request) + self._result_cls = LoginRemoteAuthResult + self._response_cls = LoginRemoteAuthWithGameIdResponse class LoginUniqueNickHandler(CmdHandlerBase): _request: LoginUniqueNickRequest _result: LoginUniqueNickResult - def _response_construct(self) -> None: - self._response = LoginUniqueNickResponse(self._result) + def __init__(self, client: Client, request: LoginUniqueNickRequest) -> None: + super().__init__(client, request) + self._result_cls = LoginUniqueNickResult + self._response_cls = LoginUniqueNickResponse class LoginUniqueNickWithGameIdHandler(CmdHandlerBase): _request: LoginUniqueNickWithGameIdRequest _result: LoginUniqueNickResult - def _response_construct(self) -> None: - self._response = LoginUniqueNickWithGameIdResponse(self._result) + def __init__(self, client: Client, request: LoginUniqueNickWithGameIdRequest) -> None: + super().__init__(client, request) + self._result_cls = LoginUniqueNickResult + self._response_cls = LoginUniqueNickWithGameIdResponse diff --git a/src/frontends/tests/gamespy/query_report/mock_objects.py b/src/frontends/tests/gamespy/query_report/mock_objects.py index 252634db4..4847c6793 100644 --- a/src/frontends/tests/gamespy/query_report/mock_objects.py +++ b/src/frontends/tests/gamespy/query_report/mock_objects.py @@ -1,8 +1,8 @@ from typing import cast from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.protocols.query_report.applications.client import Client -from frontends.gamespy.protocols.query_report.v2.applications.handlers import AvailableHandler, HeartBeatHandler, KeepAliveHandler -from frontends.gamespy.protocols.query_report.v2.contracts.results import HeartBeatResult +from frontends.gamespy.protocols.query_report.v2.applications.handlers import AvailableHandler, HeartbeatHandler, KeepAliveHandler +from frontends.gamespy.protocols.query_report.v2.contracts.results import HeartbeatResult from frontends.tests.gamespy.library.mock_objects import ConnectionMock, LogMock, RequestHandlerMock, create_mock_url @@ -19,7 +19,7 @@ def create_client() -> Client: config=CONFIG.servers["QueryReport"], t_client=ClientMock, logger=logger) config = CONFIG.servers["QueryReport"] - create_mock_url(config, HeartBeatHandler, HeartBeatResult.model_validate( + create_mock_url(config, HeartbeatHandler, HeartbeatResult.model_validate( {"remote_ip": conn.remote_ip, "remote_port": conn.remote_port, "instant_key": "123", "command_name": 3}).model_dump(mode='json')) create_mock_url(config, AvailableHandler, {"message": "ok"}) create_mock_url(config, KeepAliveHandler, {"message": "ok"}) diff --git a/src/frontends/tests/gamespy/query_report/request_tests.py b/src/frontends/tests/gamespy/query_report/request_tests.py index 56912c072..e6e519a48 100644 --- a/src/frontends/tests/gamespy/query_report/request_tests.py +++ b/src/frontends/tests/gamespy/query_report/request_tests.py @@ -1,7 +1,7 @@ import unittest from frontends.gamespy.protocols.query_report.v2.aggregates.enums import RequestType -from frontends.gamespy.protocols.query_report.v2.contracts.requests import AvaliableRequest, ChallengeRequest, EchoRequest, HeartBeatRequest +from frontends.gamespy.protocols.query_report.v2.contracts.requests import AvaliableRequest, ChallengeRequest, EchoRequest, HeartbeatRequest AVALIABLE_REQUEST = bytes([0x09, # packet type 0x00, 0x00, 0x00, 0x00, # instant key @@ -50,7 +50,7 @@ def test_echo_request(self): def test_heartbeat(self): - request = HeartBeatRequest(HEARTBEAT_REQUEST) + request = HeartbeatRequest(HEARTBEAT_REQUEST) request.parse() self.assertEqual("gmtest", request.game_name) self.assertEqual("2921297764", request.instant_key) diff --git a/src/frontends/tests/gamespy/server_browser/contract_tests.py b/src/frontends/tests/gamespy/server_browser/contract_tests.py new file mode 100644 index 000000000..d666fcf25 --- /dev/null +++ b/src/frontends/tests/gamespy/server_browser/contract_tests.py @@ -0,0 +1,60 @@ +from datetime import datetime +import unittest +from uuid import UUID + +from frontends.gamespy.protocols.query_report.aggregates.game_server_info import ( + GameServerInfo, +) +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import ( + GameServerStatus, +) +from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import ( + GameServerFlags, + ServerListUpdateOption, +) +from frontends.gamespy.protocols.server_browser.v2.applications.switcher import Switcher +from frontends.gamespy.protocols.server_browser.v2.contracts.responses import ( + ServerMainListResponse, +) +from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( + ServerMainListResult, +) + + +class ResponseTests(unittest.TestCase): + def test_main_list(self): + result = ServerMainListResult( + client_remote_ip="127.0.0.1", + flag=GameServerFlags.HAS_KEYS_FLAG, + game_secret_key="xxxx", + keys=["hostname", "gametype", "mapname", + "numplayers", "maxplayers"], + servers_info=[ + GameServerInfo( + server_id=UUID("950b7638-a90d-469b-ac1f-861e63c8c613"), + instant_key="12356", + game_name="gmtest", + query_report_port=6900, + last_heart_beat_received_time=datetime.now(), + status=GameServerStatus.NORMAL, + server_data={"hostname": "GameSpy QR2 Sample"}, + player_data=[{}], + team_data=[{}], + host_ip_address="127.0.0.1", + ) + ], + ) + response = ServerMainListResponse(result) + response.build() + correct = b'\xee\x00\x00\xe00000000000\x7f\x00\x00\x01\x19d\x05\x00hostname\x00\x00gametype\x00\x00mapname\x00\x00numplayers\x00\x00maxplayers\x00\x00P\x7f\x00\x00\x01\x1a\xf4\xffGameSpy QR2 Sample\x00\xff\x00\xff\x00\xff\x00\xff\x00\x00\xff\xff\xff\xff' + self.assertTrue(response.sending_buffer == correct) + + def test_server_info_request(self): + raw = b'\x00%\x00\x01\x03\x00\x00\x00\x00gmtest\x00gmtest\x00$(A:{<]p\x00\x00\x00\x00\x00\x02\x00\t\x01\xac\x13\x00\x05+g' + option = Switcher.get_update_option(raw) + self.assertEqual(option, ServerListUpdateOption.SERVER_FULL_INFO_LIST) + pass + + +if __name__ == "__main__": + ResponseTests().test_server_info_request() diff --git a/src/frontends/tests/gamespy/server_browser/encrypt_test.py b/src/frontends/tests/gamespy/server_browser/encrypt_tests.py similarity index 100% rename from src/frontends/tests/gamespy/server_browser/encrypt_test.py rename to src/frontends/tests/gamespy/server_browser/encrypt_tests.py From 688d23d6051f4db8c7a73b9e2278cd46e5e2675e Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Mon, 13 Oct 2025 08:47:09 +0000 Subject: [PATCH 210/231] Update(sb): server aux-update functions --- common/config.json | 6 +- .../library/networks/redis_brocker.py | 17 ++- .../protocols/gamespy/chat/brocker.py | 39 ++++--- src/backends/protocols/gamespy/chat/data.py | 29 +++-- .../protocols/gamespy/chat/handlers.py | 96 +++++++++------ .../protocols/gamespy/chat/requests.py | 7 ++ .../protocols/gamespy/query_report/broker.py | 19 ++- .../gamespy/server_browser/brocker.py | 7 -- .../gamespy/server_browser/handlers.py | 7 +- src/backends/routers/gamespy/chat.py | 18 +-- src/backends/routers/gamespy/query_report.py | 14 +-- .../routers/gamespy/server_browser.py | 7 +- .../tests/gamespy/natneg/handler_tests.py | 7 +- .../gamespy/library/abstractions/brocker.py | 1 + src/frontends/gamespy/library/configs.py | 11 +- .../protocols/chat/abstractions/handler.py | 5 +- .../protocols/chat/contracts/responses.py | 4 +- .../protocols/chat/contracts/results.py | 2 +- .../v2/abstractions/contracts.py | 22 ++-- .../v2/aggregations/string_flags.py | 5 +- .../server_browser/v2/applications/client.py | 3 +- .../v2/applications/handlers.py | 23 +++- .../v2/applications/switcher.py | 23 ++-- .../server_browser/v2/contracts/responses.py | 109 ++++++++++-------- .../server_browser/v2/contracts/results.py | 2 +- .../tests/gamespy/chat/mock_objects.py | 21 ++-- .../gamespy/server_browser/contract_tests.py | 6 + .../gamespy/server_browser/game_tests.py | 1 + .../gamespy/server_browser/mock_objects.py | 8 +- 29 files changed, 299 insertions(+), 220 deletions(-) delete mode 100644 src/backends/protocols/gamespy/server_browser/brocker.py diff --git a/common/config.json b/common/config.json index 02bb5da2d..140b0bc71 100644 --- a/common/config.json +++ b/common/config.json @@ -30,10 +30,10 @@ }, "redis": { "server": "unispy_redis", - "port": 5678, + "port": 6379, "user": "", - "password": "password", - "ssl": "true", + "password": "123456", + "ssl": "false", "ssl_host": "" }, "servers": { diff --git a/src/backends/library/networks/redis_brocker.py b/src/backends/library/networks/redis_brocker.py index 7906eb2a8..2a2433d0f 100644 --- a/src/backends/library/networks/redis_brocker.py +++ b/src/backends/library/networks/redis_brocker.py @@ -12,15 +12,13 @@ class RedisBrocker(BrockerBase): _client: Redis _subscriber: PubSub - def __init__(self, name: str, url: str, call_back_func: Callable | None = None) -> None: - super().__init__(name, url, call_back_func) - self._client = Redis.from_url(url, socket_timeout=5) - self._client.ping() - self._subscriber = self._client.pubsub() - def subscribe(self): self.is_started = True - threading.Thread(target=self.get_message).start() + self._client = Redis.from_url(self.url, socket_timeout=5) + self._client.ping() + self._subscriber = self._client.pubsub() + th = threading.Thread(target=self.get_message) + th.start() def get_message(self): self._subscriber.subscribe(self._name) @@ -32,7 +30,8 @@ def get_message(self): if not isinstance(m['data'], bytes): continue msg = m['data'].decode("utf-8") - threading.Thread(target=self.receive_message, args=[msg]).start() + threading.Thread(target=self.receive_message, + args=[msg]).start() def unsubscribe(self): self.is_started = False @@ -47,6 +46,6 @@ def publish_message(self, message: str): if __name__ == "__main__": pass - brocker = RedisBrocker("master", CONFIG.redis.url) + brocker = RedisBrocker("master", CONFIG.redis.url, print) brocker.subscribe() pass diff --git a/src/backends/protocols/gamespy/chat/brocker.py b/src/backends/protocols/gamespy/chat/brocker.py index 906455ac4..92a0de020 100644 --- a/src/backends/protocols/gamespy/chat/brocker.py +++ b/src/backends/protocols/gamespy/chat/brocker.py @@ -1,13 +1,13 @@ -import asyncio -from logging import Logger -import logging -from fastapi import WebSocket +from contextlib import asynccontextmanager +from fastapi import APIRouter, WebSocket from backends.library.database.pg_orm import ENGINE -from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage +from backends.library.networks.redis_brocker import RedisBrocker +from frontends.gamespy.library.configs import CONFIG import backends.protocols.gamespy.chat.data as data from sqlalchemy.orm import Session from backends.library.networks.ws_manager import WebsocketManager as WsManager +from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER class WebsocketManager(WsManager): @@ -29,11 +29,6 @@ class WebsocketManager(WsManager): client4 -> frontend4 <- backend4 <- | """ - def process_message(self, message: dict) -> BrockerMessage: - self.logger.info(f"[cast] [recv] {message}") - msg = BrockerMessage.model_validate(message) - return msg - def _get_wss_in_channel(self, channel_name: str) -> list[WebSocket]: with Session(ENGINE) as session: ws_addrss = data.get_websocket_addr_by_channel_name( @@ -44,15 +39,31 @@ def _get_wss_in_channel(self, channel_name: str) -> list[WebSocket]: wss.append(self.client_pool[addr]) return wss - def broadcast_channel_message(self, message: BrockerMessage, ws_client: WebSocket): + def broadcast_channel_message(self, channel_name: str, message: str, ws_client: WebSocket): """ create redis pubsub to share message cross all backends currently we simply implement without redis pubsub """ exclude_addr = self.get_address_str(ws_client) - wss = self._get_wss_in_channel(message.channel_name) - message_str = message.model_dump_json() - self.broadcast_except(message_str, wss, [exclude_addr]) + wss = self._get_wss_in_channel(channel_name) + self.broadcast_except(message, wss, [exclude_addr]) + + +def handle_client_message(message: str): + from backends.protocols.gamespy.chat.requests import PublishMessageRequest + from backends.protocols.gamespy.chat.handlers import PublishMessageHandler + try: + request = PublishMessageRequest.model_validate(message) + PublishMessageHandler.broad_cast_loacl(request) + except Exception as e: + GLOBAL_LOGGER.error(str(e)) MANAGER = WebsocketManager() +BROCKER = RedisBrocker("chat", CONFIG.redis.url, handle_client_message) + + +@asynccontextmanager +async def launch_brocker(_: APIRouter): + BROCKER.subscribe() + yield diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index e8b84ba08..a1c1759c7 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -18,6 +18,8 @@ ) from sqlalchemy.orm import Session +from frontends.gamespy.protocols.chat.contracts.results import WhoIsResult + def is_nick_exist(nick_name: str, session: Session) -> bool: c = session.query(ChatUserCaches.nick_name).count() @@ -328,7 +330,7 @@ def get_user_cache_by_ip_port( return result -def get_whois_result(nick: str, session: Session) -> dict: +def get_whois_result(nick: str, session: Session) -> WhoIsResult: """ nick is unique in chat """ @@ -346,13 +348,24 @@ def get_whois_result(nick: str, session: Session) -> dict: .where(ChatChannelUserCaches.nick_name == info.nick_name) .all() ) - ret_dict = { - "nick_name": info.nick_name, - "user_name": info.user_name, - "remote_ip": info.remote_ip, - "channels": channels[0], - } - return ret_dict + + if info.nick_name is None: + raise ChatException("nick name is missing") + if info.user_name is None: + raise ChatException("user name is missing") + + assert isinstance(info.nick_name,str) + assert isinstance(info.user_name,str) + assert isinstance(info.remote_ip,str) + assert isinstance(channels,list) + re = WhoIsResult( + nick_name=info.nick_name, + user_name=info.user_name, + public_ip_address=info.remote_ip, + joined_channels=list(channels[0]) + ) + + return re def get_websocket_addr_by_channel_name( diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index 3e165c9cb..edc3b5655 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -8,6 +8,7 @@ ChatUserCaches, ChatChannelUserCaches, ) +from backends.protocols.gamespy.chat.brocker import BROCKER, MANAGER from backends.protocols.gamespy.chat.helper import ChannelHelper import backends.protocols.gamespy.chat.data as data from backends.protocols.gamespy.chat.requests import ( @@ -30,6 +31,7 @@ NoticeRequest, PartRequest, PrivateRequest, + PublishMessageRequest, QuitRequest, RequestBase, SetCKeyRequest, @@ -103,6 +105,7 @@ def _get_user(self): ) if self._user is not None: self._user.update_time = datetime.now() # type: ignore + self._session.commit() def _check_user(self): if self._user is None: @@ -129,6 +132,7 @@ def _request_check(self) -> None: self._get_channel_user() self._check_channel_user() + self._session.commit() def _get_channel(self): self._channel = data.get_channel_by_name( @@ -209,12 +213,13 @@ def _data_operate(self) -> None: websocket_address=self._request.websocket_address, game_name=self._request.gamename, ) - self._session.add(self._user) self._secret_key = data.get_secret_key_by_game_name( self._request.gamename, self._session ) if self._secret_key is None: raise ChatException("game secret key not found in database.") + self._session.add(self._user) + self._session.commit() def _result_construct(self) -> None: assert isinstance(self._secret_key, str) @@ -325,31 +330,33 @@ def _data_operate(self) -> None: class NickHandler(HandlerBase): _request: NickRequest + def _request_check(self) -> None: + self._get_user() + def _data_operate(self) -> None: - # todo we remove expired data - # data.remove_expired_user_cache(self._session) - # data.remove_expired_channel_user_cache(self._session) - # cache = data.get_user_cache_by_nick_name("172.19.0.5:52986") - self._user.nick_name = self._request.nick_name # type: ignore - self._session.commit() - cache = None # some game do not use CRYPT # todo check game with no encryption send nick or user request first - if cache is None: + if self._user is None: # assign nick_name to current user - self._user.nick_name = self._request.nick_name # type: ignore + self._user = ChatUserCaches( + server_id=self._request.server_id, + remote_ip=self._request.client_ip, + remote_port=self._request.client_port, + nick_name=f"{self._request.client_ip}:{self._request.client_port}", + websocket_address=self._request.websocket_address, + game_name="", + ) else: - assert isinstance(cache.update_time, datetime) - if (datetime.now() - cache.update_time).seconds > 120: + assert isinstance(self._user.update_time, datetime) + if (datetime.now() - self._user.update_time).seconds > 120: # old profile delete it - self._session.delete(cache) + self._session.delete(self._user) # data.remove_user_cache(cache) self._user.nick_name = self._request.nick_name # type: ignore return - if ( - cache.remote_ip != self._request.client_ip - and cache.remote_port != self._request.client_port + self._user.remote_ip != self._request.client_ip # type: ignore + and self._user.remote_port != self._request.client_port # type: ignore ): # type: ignore raise NickNameInUseException( old_nick=self._request.nick_name, @@ -359,6 +366,7 @@ def _data_operate(self) -> None: else: # update user cache self._user.nick_name = self._request.nick_name # type: ignore + self._session.commit() def _result_construct(self) -> None: self._result = NickResult(nick_name=self._request.nick_name) @@ -374,6 +382,7 @@ def _data_operate(self) -> None: data.remove_channel_user_caches_by_ip_port( self._request.client_ip, self._request.client_port, self._session ) + self._session.commit() class RegisterNickHandler(HandlerBase): @@ -394,18 +403,19 @@ def _data_operate(self) -> None: "The ip and port is not find in database") user.key_value = self._request.key_values # type:ignore - super()._data_operate() + self._session.commit() class UserHandler(HandlerBase): _request: UserRequest - def _request_check(self) -> None: - data.clean_expired_user_cache(self._session) - super()._request_check() + # def _request_check(self) -> None: + # data.clean_expired_user_cache(self._session) + # super()._request_check() def _data_operate(self) -> None: self._user.user_name = self._request.user_name # type: ignore + self._session.commit() class WhoHandler(HandlerBase): @@ -441,16 +451,11 @@ class WhoIsHandler(HandlerBase): _request: WhoIsRequest def _data_operate(self) -> None: - self._data: dict = data.get_whois_result( + self._data: WhoIsResult = data.get_whois_result( self._request.nick_name, self._session) def _result_construct(self) -> None: - self._result = WhoIsResult( - nick_name=self._data["nick_name"], - user_name=self._data["user_name"], - public_ip_address=self._data["remote_ip"], - joined_channel_name=list(self._data["channels"]), - ) + self._result = self._data # region Channel @@ -674,11 +679,12 @@ def _data_operate(self) -> None: def _result_construct(self) -> None: assert self._channel_user assert self._channel - assert isinstance(self._channel_user.is_channel_creator, bool) - assert isinstance(self._channel_user.is_channel_operator, bool) - assert isinstance(self._channel.channel_name, str) - assert isinstance(self._channel_user.nick_name, str) - assert isinstance(self._channel_user.user_name, str) + if TYPE_CHECKING: + assert isinstance(self._channel_user.is_channel_creator, bool) + assert isinstance(self._channel_user.is_channel_operator, bool) + assert isinstance(self._channel.channel_name, str) + assert isinstance(self._channel_user.nick_name, str) + assert isinstance(self._channel_user.user_name, str) self._result = PartResult( leaver_nick_name=self._channel_user.nick_name, @@ -724,6 +730,7 @@ class SetCkeyHandler(ChannelHandlerBase): def _data_operate(self) -> None: assert self._channel_user is not None self._channel_user.key_values = self._request.key_values # type:ignore + self._session.commit() def _result_construct(self) -> None: assert self._channel_user @@ -751,8 +758,9 @@ def _data_operate(self) -> None: raise NoSuchChannelException( "inorder to set channel topic, you have to be channel operator" ) - self._channel.topic = self._request.channel_topic # type:ignore self._data: str = self._request.channel_topic + self._channel.topic = self._request.channel_topic # type:ignore + self._session.commit() def _result_construct(self) -> None: self._result = TopicResult( @@ -822,3 +830,25 @@ def _result_construct(self) -> None: target_name=self._request.target_name, message=self._request.message ) + + +class PublishMessageHandler(hb.HandlerBase): + _request: PublishMessageRequest + + def _data_operate(self) -> None: + # todo add checking on request validation, like broadcast key + PublishMessageHandler.broad_cast_loacl(self._request) + PublishMessageHandler.broadcast_global(self._request) + + @staticmethod + def broad_cast_loacl(request: PublishMessageRequest): + ws = MANAGER.get_websocket(request.client_ip) + if ws is not None: + # if websocket is not none means the frontend that gamespy client connect to is connecting to this backend + # then we just broad cast on this channel + MANAGER.broadcast_channel_message( + request.channel_name, request.model_dump_json(), ws) + + @staticmethod + def broadcast_global(request: PublishMessageRequest): + BROCKER.publish_message(request.model_dump_json()) diff --git a/src/backends/protocols/gamespy/chat/requests.py b/src/backends/protocols/gamespy/chat/requests.py index ff81bbd92..83f04f9e0 100644 --- a/src/backends/protocols/gamespy/chat/requests.py +++ b/src/backends/protocols/gamespy/chat/requests.py @@ -210,3 +210,10 @@ class PrivateRequest(MessageRequestBase): class UtmRequest(MessageRequestBase): pass + + +class PublishMessageRequest(lib.RequestBase): + channel_name: str + sender_ip_address: str + sender_port: int + message: str diff --git a/src/backends/protocols/gamespy/query_report/broker.py b/src/backends/protocols/gamespy/query_report/broker.py index 188722e30..997e5d715 100644 --- a/src/backends/protocols/gamespy/query_report/broker.py +++ b/src/backends/protocols/gamespy/query_report/broker.py @@ -1,17 +1,12 @@ -from logging import Logger -import logging -from fastapi import WebSocket -from pydantic import BaseModel -from backends.library.database.pg_orm import ENGINE +from contextlib import asynccontextmanager + +from fastapi import APIRouter from backends.protocols.gamespy.query_report.handlers import ClientMessageHandler from backends.protocols.gamespy.query_report.requests import ClientMessageRequest from backends.library.networks.redis_brocker import RedisBrocker from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER -from frontends.gamespy.protocols.chat.abstractions.contract import BrockerMessage -import backends.protocols.gamespy.chat.data as data -from sqlalchemy.orm import Session from backends.library.networks.ws_manager import WebsocketManager as WsManager @@ -29,6 +24,10 @@ def handle_client_message(message: str): MANAGER = WebsocketManager() - BROCKER = RedisBrocker("master", CONFIG.redis.url, handle_client_message) -# BROCKER.subscribe() + + +@asynccontextmanager +async def launch_brocker(_: APIRouter): + BROCKER.subscribe() + yield diff --git a/src/backends/protocols/gamespy/server_browser/brocker.py b/src/backends/protocols/gamespy/server_browser/brocker.py deleted file mode 100644 index 6da6f3961..000000000 --- a/src/backends/protocols/gamespy/server_browser/brocker.py +++ /dev/null @@ -1,7 +0,0 @@ -from backends.library.networks.redis_brocker import RedisBrocker -from frontends.gamespy.library.configs import CONFIG - - -# we only need publish so du not need subscribe -BROCKER = RedisBrocker("master", CONFIG.redis.url) -pass diff --git a/src/backends/protocols/gamespy/server_browser/handlers.py b/src/backends/protocols/gamespy/server_browser/handlers.py index 86a013a64..4855a25c3 100644 --- a/src/backends/protocols/gamespy/server_browser/handlers.py +++ b/src/backends/protocols/gamespy/server_browser/handlers.py @@ -25,7 +25,7 @@ from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( P2PGroupRoomListResult, SendMessageResult, - ServerInfoResult, + UpdateServerInfoResult, ServerMainListResult, ServerFullInfoListResult, ) @@ -128,11 +128,12 @@ def _result_construct(self): ) if TYPE_CHECKING: self._caches = cast(list[GameServerInfo], self._caches) + all_keys = list(self._caches[0].server_data.keys()) self._result = ServerFullInfoListResult( client_remote_ip=self._request.client_ip, game_secret_key=self._secret_key, servers_info=self._caches, - keys=self._request.keys + keys=all_keys ) # region Adhoc @@ -189,4 +190,4 @@ def _result_construct(self) -> None: # TODO: check whether we need respond when gameserver not exist if self._data is None: return - self._result = ServerInfoResult(game_server_info=self._data) + self._result = UpdateServerInfoResult(game_server_info=self._data) diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index a303a020d..d2434980b 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -1,6 +1,6 @@ import asyncio from backends.library.abstractions.contracts import OKResponse -from backends.protocols.gamespy.chat.brocker import MANAGER +from backends.protocols.gamespy.chat.brocker import MANAGER, launch_brocker from backends.protocols.gamespy.chat.handlers import ( CdKeyHandler, CryptHandler, @@ -15,6 +15,7 @@ NickHandler, PartHandler, PrivateHandler, + PublishMessageHandler, QuitHandler, UserHandler, WhoIsHandler, @@ -38,6 +39,7 @@ NoticeRequest, PartRequest, PrivateRequest, + PublishMessageRequest, QuitRequest, SetChannelKeyRequest, SetGroupRequest, @@ -53,13 +55,15 @@ from fastapi import APIRouter, FastAPI, WebSocket, WebSocketDisconnect -router = APIRouter() +router = APIRouter(lifespan=launch_brocker) client_pool = {} -# @asynccontextmanager -# async def launch_brocker(router: APIRouter): - +@router.post(f"{CHAT}/publish") +def publish_message(request: PublishMessageRequest): + handler = PublishMessageHandler(request) + handler.handle() + return handler.response @router.websocket(f"{CHAT}/ws") @@ -69,9 +73,7 @@ async def websocket_endpoint(ws: WebSocket): MANAGER.connect(ws) try: while True: - data = await ws.receive_json() - msg = MANAGER.process_message(data) - MANAGER.broadcast_channel_message(msg, ws) + _ = await ws.receive_json() except WebSocketDisconnect: if ws.client is not None: MANAGER.disconnect(ws) diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index 8b3f720b9..991d7855f 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -1,16 +1,12 @@ from fastapi import APIRouter, WebSocket, WebSocketDisconnect -from contextlib import asynccontextmanager -from backends.protocols.gamespy.query_report.broker import MANAGER, BROCKER -from backends.protocols.gamespy.query_report.handlers import AvaliableHandler, HeartbeatHandler, KeepAliveHandler -from backends.protocols.gamespy.query_report.requests import AvaliableRequest, ChallengeRequest, ClientMessageRequest, EchoRequest, HeartBeatRequest, KeepAliveRequest +from backends.protocols.gamespy.query_report.broker import MANAGER, launch_brocker +from backends.protocols.gamespy.query_report.handlers import ( + AvaliableHandler, HeartbeatHandler, KeepAliveHandler) +from backends.protocols.gamespy.query_report.requests import ( + AvaliableRequest, ChallengeRequest, ClientMessageRequest, EchoRequest, HeartBeatRequest, KeepAliveRequest) from backends.urls import QUERY_REPORT -@asynccontextmanager -async def launch_brocker(router: APIRouter): - BROCKER.subscribe() - yield - router = APIRouter(lifespan=launch_brocker) diff --git a/src/backends/routers/gamespy/server_browser.py b/src/backends/routers/gamespy/server_browser.py index 458ab4905..45f13b192 100644 --- a/src/backends/routers/gamespy/server_browser.py +++ b/src/backends/routers/gamespy/server_browser.py @@ -1,5 +1,5 @@ from fastapi import APIRouter, WebSocket, WebSocketDisconnect -from backends.protocols.gamespy.server_browser.handlers import ServerInfoHandler, ServerMainListHandler +from backends.protocols.gamespy.server_browser.handlers import ServerFullInfoListHandler, ServerInfoHandler, ServerMainListHandler from backends.protocols.gamespy.server_browser.requests import SendMessageRequest, ServerInfoRequest, ServerListRequest from backends.urls import SERVER_BROWSER_V2 @@ -39,6 +39,9 @@ def server_info(request: ServerInfoRequest): @router.post(f"{SERVER_BROWSER_V2}/ServerMainListHandler") def server_list(request: ServerListRequest): + """ we send all server data to client to make it have HAS_FULL_RULES_FLAG + and will not send ServerBrowserAuxUpdateServer(sb, server, async, fullUpdate) to + """ handler = ServerMainListHandler(request) handler.handle() return handler.response @@ -46,7 +49,7 @@ def server_list(request: ServerListRequest): @router.post(f"{SERVER_BROWSER_V2}/ServerFullInfoListHandler") def full_info_list(request: ServerListRequest): - handler = ServerMainListHandler(request) + handler = ServerFullInfoListHandler(request) handler.handle() return handler.response diff --git a/src/backends/tests/gamespy/natneg/handler_tests.py b/src/backends/tests/gamespy/natneg/handler_tests.py index 13ffadf63..3c924aae7 100644 --- a/src/backends/tests/gamespy/natneg/handler_tests.py +++ b/src/backends/tests/gamespy/natneg/handler_tests.py @@ -16,12 +16,14 @@ def test_init(self): ) # fmt:skip r = fnt.InitRequest(raw) data = add_headers(r) - data["raw_request"] = data["raw_request"].decode("ascii", "backslashreplace") + data["raw_request"] = data["raw_request"].decode( + "ascii", "backslashreplace") request = InitRequest(**data) handler = InitHandler(request) handler.handle() pass + @unittest.skip("not implemented") def test_report(self): raise NotImplementedError() @@ -35,7 +37,8 @@ def test_connect(self): ) # fmt:skip r = fnt.ConnectRequest(raw) data = add_headers(r) - data["raw_request"] = data["raw_request"].decode("ascii", "backslashreplace") + data["raw_request"] = data["raw_request"].decode( + "ascii", "backslashreplace") request = ConnectRequest(**data) handler = ConnectHandler(request) handler.handle() diff --git a/src/frontends/gamespy/library/abstractions/brocker.py b/src/frontends/gamespy/library/abstractions/brocker.py index 8e6a3541d..cecac8ec3 100644 --- a/src/frontends/gamespy/library/abstractions/brocker.py +++ b/src/frontends/gamespy/library/abstractions/brocker.py @@ -1,4 +1,5 @@ import abc +from threading import Thread from typing import final, Callable diff --git a/src/frontends/gamespy/library/configs.py b/src/frontends/gamespy/library/configs.py index 577aeab02..4e2277733 100644 --- a/src/frontends/gamespy/library/configs.py +++ b/src/frontends/gamespy/library/configs.py @@ -63,9 +63,6 @@ class BackendConfig(BaseModel): token_expire_time: int = 30 - - - class UnittestConfig(BaseModel): """ unittest related config @@ -98,13 +95,13 @@ class UniSpyServerConfig(BaseModel): # "Unispy server config not found, you should set the UNISPY_CONFIG in the system enviroment." # ) if not os.path.exists(unispy_config): - raise Exception("Unispy server config file not exist, check UNISPY_CONFIG path.") + raise Exception( + "Unispy server config file not exist, check UNISPY_CONFIG path.") with open(unispy_config, "r") as f: import json - config = json.load(f) - CONFIG = UniSpyServerConfig(**config) - pass +CONFIG = UniSpyServerConfig.model_validate(config) +pass if __name__ == "__main__": pass diff --git a/src/frontends/gamespy/protocols/chat/abstractions/handler.py b/src/frontends/gamespy/protocols/chat/abstractions/handler.py index e4a631474..accfa3091 100644 --- a/src/frontends/gamespy/protocols/chat/abstractions/handler.py +++ b/src/frontends/gamespy/protocols/chat/abstractions/handler.py @@ -29,7 +29,10 @@ def __init__(self, client: ClientBase, request: RequestBase): def _request_check(self) -> None: super()._request_check() assert self._client.brocker - self._request.websocket_address = self._client.brocker.ip_port + try: + self._request.websocket_address = self._client.brocker.ip_port + except: + raise ChatException("websocket is disconnected") def _handle_exception(self, ex: Exception) -> None: t_ex = type(ex) diff --git a/src/frontends/gamespy/protocols/chat/contracts/responses.py b/src/frontends/gamespy/protocols/chat/contracts/responses.py index ed6b45b8c..ceb35429b 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/responses.py +++ b/src/frontends/gamespy/protocols/chat/contracts/responses.py @@ -175,9 +175,9 @@ def __init__( def build(self) -> None: self.sending_buffer = f":{SERVER_DOMAIN} {ResponseCode.WHOISUSER.value} {self._result.nick_name} {self._result.user_name} {self._result.user_name} {self._result.public_ip_address} * :{self._result.user_name}\r\n" # noqa - if len(self._result.joined_channel_name) != 0: + if len(self._result.joined_channels) != 0: channel_name = "" - for name in self._result.joined_channel_name: + for name in self._result.joined_channels: channel_name += f"@{name} " # noqa self.sending_buffer += f":{SERVER_DOMAIN} {ResponseCode.WHOISCHANNELS.value} {self._result.nick_name} {self._result.user_name} :{channel_name}\r\n" # noqa diff --git a/src/frontends/gamespy/protocols/chat/contracts/results.py b/src/frontends/gamespy/protocols/chat/contracts/results.py index 09b3b5c69..b184be4c5 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/results.py +++ b/src/frontends/gamespy/protocols/chat/contracts/results.py @@ -64,7 +64,7 @@ class WhoIsResult(ResultBase): nick_name: str user_name: str public_ip_address: str - joined_channel_name: list[str] + joined_channels: list[str] class WhoResult(ResultBase): diff --git a/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py index c45ce3453..4150106fa 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/abstractions/contracts.py @@ -80,7 +80,7 @@ class ServerListUpdateOptionResultBase(ResultBase): class ServerListUpdateOptionResponseBase(ResponseBase): _result: ServerListUpdateOptionResultBase - _servers_info_buffers: bytearray + _buffer: bytearray def __init__( self, @@ -88,16 +88,16 @@ def __init__( ) -> None: assert issubclass(type(result), ServerListUpdateOptionResultBase) super().__init__(result) - self._servers_info_buffers = bytearray() + self._buffer = bytearray() def build(self) -> None: crypt_header = self.build_crypt_header() - self._servers_info_buffers.extend(crypt_header) - self._servers_info_buffers.extend( + self._buffer.extend(crypt_header) + self._buffer.extend( ip_to_4_bytes(self._result.client_remote_ip)) - self._servers_info_buffers.extend( + self._buffer.extend( QUERY_REPORT_DEFAULT_PORT.to_bytes(2)) - assert len(self._servers_info_buffers) == 20 + assert len(self._buffer) == 20 def build_crypt_header(self) -> bytearray: # cryptHeader have 14 bytes, when we encrypt data we need skip the first 14 bytes @@ -111,15 +111,15 @@ def build_crypt_header(self) -> bytearray: def build_server_keys(self) -> None: # we add the total number of the requested keys - self._servers_info_buffers.append(len(self._result.keys)) + self._buffer.append(len(self._result.keys)) # then we add the keys for key in self._result.keys: - self._servers_info_buffers.append(DataKeyType.STRING) - self._servers_info_buffers.extend(get_bytes(key)) - self._servers_info_buffers.extend(STRING_SPLITER) + self._buffer.append(DataKeyType.STRING) + self._buffer.extend(get_bytes(key)) + self._buffer.append(STRING_SPLITER) def build_unique_value(self): - self._servers_info_buffers.append(0) + self._buffer.append(0) class AdHocResultBase(ResultBase): diff --git a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/string_flags.py b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/string_flags.py index 3b5b0f114..ce6769f3f 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/string_flags.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/string_flags.py @@ -1,3 +1,4 @@ +SINGLE_SERVER_END_FLAG = 0 ALL_SERVER_END_FLAG = b"\x00\xFF\xFF\xFF\xFF" -STRING_SPLITER = b"\x00" -NTS_STRING_FLAG = b"\xFF" +STRING_SPLITER = 0 +NTS_STRING_FLAG = 255 diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/client.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/client.py index dda282710..08381cc90 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/client.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/client.py @@ -4,6 +4,7 @@ from frontends.gamespy.library.abstractions.enctypt_base import EncryptBase from frontends.gamespy.library.configs import ServerConfig from frontends.gamespy.library.log.log_manager import LogWriter +from frontends.gamespy.protocols.server_browser.v2.aggregations.encryption import EnctypeX from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import ServerListUpdateOption if TYPE_CHECKING: from frontends.gamespy.library.abstractions.switcher import SwitcherBase @@ -19,7 +20,7 @@ class ClientInfo(ClientInfoBase): class Client(ClientBase): is_log_raw: bool info: ClientInfo - crypto: EncryptBase + crypto: EnctypeX | None def __init__(self, connection: ConnectionBase, server_config: ServerConfig, logger: LogWriter): super().__init__(connection, server_config, logger) diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py index 2cdff6e06..baa9e0c12 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py @@ -28,7 +28,7 @@ from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( P2PGroupRoomListResult, ServerFullInfoListResult, - ServerInfoResult, + UpdateServerInfoResult, ServerMainListResult, ) from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import ( @@ -58,14 +58,14 @@ def get_clients(game_name: str): class AdHocHandler(CmdHandlerBase): _message: GameServerInfo # !fix this - _result: ServerInfoResult + _result: UpdateServerInfoResult def __init__(self, message: GameServerInfo) -> None: self._log_current_class() self._message = message def handle(self) -> None: - result = ServerInfoResult(game_server_info=self._message) + result = UpdateServerInfoResult(game_server_info=self._message) match self._message.status: case status if ( status == GameServerStatus.NORMAL @@ -108,13 +108,13 @@ def __init__(self, client: Client, request: SendMessageRequest) -> None: # we just need send the message to backend, then backend will send to queryreport frontend, query report frontend will handle for us -class ServerInfoHandler(CmdHandlerBase): +class UpdateServerInfoHandler(CmdHandlerBase): _request: ServerInfoRequest - _result: ServerInfoResult + _result: UpdateServerInfoResult def __init__(self, client: Client, request: RequestBase) -> None: super().__init__(client, request) - self._result_cls = ServerInfoResult + self._result_cls = UpdateServerInfoResult self._response_cls = UpdateServerInfoResponse @@ -146,6 +146,17 @@ class ServerFullInfoListHandler(ServerListUpdateOptionHandlerBase): line 392 ServerBrowserAuxUpdateServer(sb, server, async, fullUpdate); will get the full info of a server such as: player data, server data, team data """ + """ + !! below is the source code of sb v2, adhocdata is directly follow the mainlist response + todo check if adhoc data is append after mainlist response + if (slist->state == sl_mainlist) + err = ProcessMainListData(slist); + if (err != sbe_noerror) + return err; + //always need to check this after mainlistdata, in case some extra data has some in (e.g. key list for push) + if (slist->state == sl_connected && slist->inbufferlen > 0) + return ProcessAdHocData(slist); + """ _request: ServerListRequest _result: ServerFullInfoListResult _result_cls: type[ServerFullInfoListResult] diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py index d90cdae10..bc9235c3f 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/switcher.py @@ -16,7 +16,7 @@ P2PGroupRoomListHandler, SendMessageHandler, ServerFullInfoListHandler, - ServerInfoHandler, + UpdateServerInfoHandler, ServerMainListHandler, ) from frontends.gamespy.protocols.server_browser.v2.contracts.requests import ( @@ -48,26 +48,20 @@ def _create_cmd_handlers( self._client = cast(Client, self._client) match name: case RequestType.SERVER_LIST_REQUEST: - # todo check if all game follow this pattern, +2 is calc by sdk server info request - update_option_index = raw_request.find( - b"\x00\x00\x00\x00", 6)+2 - update_option_bytes = raw_request[ - update_option_index: update_option_index + 4 - ] - update_option = ServerListUpdateOption( - int.from_bytes(update_option_bytes) - ) - handler = self.__create_cmd_by_update_option( - update_option, req) + handler = self.__check_update_option(req) return handler case RequestType.SERVER_INFO_REQUEST: - return ServerInfoHandler(self._client, ServerInfoRequest(req)) + return UpdateServerInfoHandler(self._client, ServerInfoRequest(req)) case RequestType.SEND_MESSAGE_REQUEST: return SendMessageHandler(self._client, SendMessageRequest(req)) case _: return None - def __create_cmd_by_update_option(self, update_option: ServerListUpdateOption, request: bytes) -> CmdHandlerBase: + def __check_update_option(self, request: bytes) -> CmdHandlerBase: + """ + check update option and create handler + """ + update_option = self.get_update_option(request) match update_option: case (ServerListUpdateOption.SERVER_MAIN_LIST | ServerListUpdateOption.P2P_SERVER_MAIN_LIST @@ -83,6 +77,7 @@ def __create_cmd_by_update_option(self, update_option: ServerListUpdateOption, r @staticmethod def get_update_option(raw_request: bytes) -> ServerListUpdateOption: + # todo check if all game follow this pattern, +2 is calc by sdk server info request update_option_index = raw_request.find( b"\x00\x00\x00\x00", 6)+2 update_option_bytes = raw_request[ diff --git a/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py index 2b7c373f8..fc36ae0a7 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py @@ -1,17 +1,19 @@ from frontends.gamespy.library.extentions.encoding import get_bytes +from frontends.gamespy.protocols.query_report.aggregates.game_server_info import GameServerInfo from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import ( AdHocResponseBase, ServerListUpdateOptionResponseBase, ) +from frontends.gamespy.protocols.server_browser.v2.aggregations.exceptions import SBException from frontends.gamespy.protocols.server_browser.v2.aggregations.server_info_builder import ( build_server_info_header, ) -from frontends.gamespy.protocols.server_browser.v2.aggregations.string_flags import ALL_SERVER_END_FLAG, NTS_STRING_FLAG, STRING_SPLITER +from frontends.gamespy.protocols.server_browser.v2.aggregations.string_flags import ALL_SERVER_END_FLAG, NTS_STRING_FLAG, STRING_SPLITER, SINGLE_SERVER_END_FLAG from frontends.gamespy.protocols.server_browser.v2.contracts.requests import ServerListRequest from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( P2PGroupRoomListResult, ServerFullInfoListResult, - ServerInfoResult, + UpdateServerInfoResult, ServerMainListResult, ) @@ -19,10 +21,10 @@ class DeleteServerInfoResponse(AdHocResponseBase): - _result: ServerInfoResult + _result: UpdateServerInfoResult - def __init__(self, result: ServerInfoResult) -> None: - assert isinstance(result, ServerInfoResult) + def __init__(self, result: UpdateServerInfoResult) -> None: + assert isinstance(result, UpdateServerInfoResult) self._result = result def build(self): @@ -33,8 +35,8 @@ def build(self): class UpdateServerInfoResponse(AdHocResponseBase): - def __init__(self, result: ServerInfoResult) -> None: - assert isinstance(result, ServerInfoResult) + def __init__(self, result: UpdateServerInfoResult) -> None: + assert isinstance(result, UpdateServerInfoResult) self._result = result self._buffer = bytearray() @@ -69,9 +71,9 @@ def _build_kv(data: dict) -> bytearray: buffer = bytearray() for k, v in data.items(): buffer.extend(get_bytes(k)) - buffer.extend(STRING_SPLITER) + buffer.append(STRING_SPLITER) buffer.extend(get_bytes(v)) - buffer.extend(STRING_SPLITER) + buffer.append(STRING_SPLITER) return buffer @@ -84,80 +86,85 @@ def build(self) -> None: self.build_server_keys() self.build_unique_value() self._build_servers_full_info() - self.sending_buffer = bytes(self._servers_info_buffers) + self.sending_buffer = bytes(self._buffer) def _build_servers_full_info(self): for room in self._result.peer_room_info: - self._servers_info_buffers.append(GameServerFlags.HAS_KEYS_FLAG) + self._buffer.append(GameServerFlags.HAS_KEYS_FLAG) group_id_bytes = room.group_id.to_bytes() - self._servers_info_buffers.extend(group_id_bytes) + self._buffer.extend(group_id_bytes) # get gamespy format dict gamespy_dict = room.get_gamespy_dict() for key in self._result.keys: - self._servers_info_buffers.extend(NTS_STRING_FLAG) + self._buffer.append(NTS_STRING_FLAG) value = ( gamespy_dict[key] if key in gamespy_dict.keys() else "" ) - self._servers_info_buffers.extend(get_bytes(value)) - self._servers_info_buffers.extend(STRING_SPLITER) + self._buffer.extend(get_bytes(value)) + self._buffer.append(STRING_SPLITER) end_flag = b"\x00" - self._servers_info_buffers.extend(end_flag) + self._buffer.extend(end_flag) class ServerMainListResponse(ServerListUpdateOptionResponseBase): - _request: ServerListRequest _result: ServerMainListResult + def __add_key_value_to_buffer(self, value): + self._buffer.append(NTS_STRING_FLAG) + self._buffer.extend(get_bytes(value)) + self._buffer.append(STRING_SPLITER) + + def __check_key_existance(self): + for info in self._result.servers_info: + for key in self._result.keys: + if key not in info.server_data: + raise SBException( + f"key:{key} is not in server info, please check database") + def __build_servers_full_info(self): for info in self._result.servers_info: - header = build_server_info_header(self._result.flag, info) - self._servers_info_buffers.extend(header) + last_header = build_server_info_header(self._result.flag, info) + self._buffer.extend(last_header) for key in self._result.keys: - self._servers_info_buffers.extend(NTS_STRING_FLAG) - if key in info.server_data.keys(): - self._servers_info_buffers.extend( - get_bytes(info.server_data[key])) - self._servers_info_buffers.extend(STRING_SPLITER) + value = info.server_data[key] + self.__add_key_value_to_buffer(value) - self._servers_info_buffers.extend(ALL_SERVER_END_FLAG) + def __build_tail(self): + # after all server is added we add the tail + self._buffer.extend(ALL_SERVER_END_FLAG) def build(self) -> None: super().build() self.build_server_keys() self.build_unique_value() + self.__check_key_existance() self.__build_servers_full_info() - self.sending_buffer = bytes(self._servers_info_buffers) + self.__build_tail() + self.sending_buffer = bytes(self._buffer) -class ServerFullInfoListResponse(ServerMainListResponse): +class ServerFullInfoListResponse(ServerListUpdateOptionResponseBase): + """ + currently we send all server info to game to make it pass HAS_FULL_RULES_FLAG + todo check how this cmd is handled + """ _result: ServerFullInfoListResult def build(self) -> None: + """ + adhoc data can contains multiple message, + gamespy will read the response to the end + """ + # cryptheader is nessesary for make the connection with server browser super().build() - self.__build_servers_full_info() - self.sending_buffer = bytes(self._servers_info_buffers) - - def __build_servers_full_info(self): + push_buffer = bytearray() for info in self._result.servers_info: - header = build_server_info_header(self._result.flag, info) - self._servers_info_buffers.extend(header) - - for key, value in info.server_data.items(): - self.__add_key_value_to_buffer(key, value) - - for item in info.player_data: - for key, value in item.items(): - self.__add_key_value_to_buffer(key, value) - - for item in info.team_data: - for key, value in item.items(): - self.__add_key_value_to_buffer(key, value) - - def __add_key_value_to_buffer(self, key, value): - self._servers_info_buffers.extend( - get_bytes(key)) - self._servers_info_buffers.extend(STRING_SPLITER) - self._servers_info_buffers.extend(get_bytes(value)) - self._servers_info_buffers.extend(STRING_SPLITER) + result = UpdateServerInfoResult(game_server_info=info) + response = UpdateServerInfoResponse(result) + response.build() + push_buffer.extend(response.sending_buffer) + # we append the push server full info response (Adhoc response) to the crypt header + self._buffer = self._buffer+push_buffer + self.sending_buffer = bytes(self._buffer) diff --git a/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py index 5327aceaf..09d306d93 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/results.py @@ -14,7 +14,7 @@ from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import GameServerFlags -class ServerInfoResult(AdHocResultBase): +class UpdateServerInfoResult(AdHocResultBase): pass diff --git a/src/frontends/tests/gamespy/chat/mock_objects.py b/src/frontends/tests/gamespy/chat/mock_objects.py index b7791e6d7..926b358df 100644 --- a/src/frontends/tests/gamespy/chat/mock_objects.py +++ b/src/frontends/tests/gamespy/chat/mock_objects.py @@ -110,17 +110,16 @@ def create_client() -> Client: create_mock_url( config, GetCKeyHandler, - GetCKeyResult.model_validate( - { - "channel_name": "test", - "infos": [ - { - "nick_name": "test_nick", - "user_values": ["data", "key", "value", "data"], - }, - ], - "cookie": "000", - } + + GetCKeyResult + ( + channel_name="test", + infos=[GetCKeyResult.GetCKeyInfos( + nick_name="test_nick", + key_values={"hello": "hi"} + )], + cookie="000", + keys=["hello"] ).model_dump(), ) create_mock_url( diff --git a/src/frontends/tests/gamespy/server_browser/contract_tests.py b/src/frontends/tests/gamespy/server_browser/contract_tests.py index d666fcf25..8374c5aa7 100644 --- a/src/frontends/tests/gamespy/server_browser/contract_tests.py +++ b/src/frontends/tests/gamespy/server_browser/contract_tests.py @@ -49,6 +49,12 @@ def test_main_list(self): correct = b'\xee\x00\x00\xe00000000000\x7f\x00\x00\x01\x19d\x05\x00hostname\x00\x00gametype\x00\x00mapname\x00\x00numplayers\x00\x00maxplayers\x00\x00P\x7f\x00\x00\x01\x1a\xf4\xffGameSpy QR2 Sample\x00\xff\x00\xff\x00\xff\x00\xff\x00\x00\xff\xff\xff\xff' self.assertTrue(response.sending_buffer == correct) + def test_server_list_with_filter(self): + """ + filter: US servers with more than 5 players, or servers with a hostname containing 'GameSpy' + """ + raw = b"\x00\x95\x00\x01\x03\x00\x00\x00\x00gmtest\x00gmtest\x000`mpW:[>(country = 'US' and numplayers > 5) or hostname like ' % GameSpy % '\x00\\hostname\\gametype\\mapname\\numplayers\\maxplayers\x00\x00\x00\x00\x00" + def test_server_info_request(self): raw = b'\x00%\x00\x01\x03\x00\x00\x00\x00gmtest\x00gmtest\x00$(A:{<]p\x00\x00\x00\x00\x00\x02\x00\t\x01\xac\x13\x00\x05+g' option = Switcher.get_update_option(raw) diff --git a/src/frontends/tests/gamespy/server_browser/game_tests.py b/src/frontends/tests/gamespy/server_browser/game_tests.py index fdfb33060..0940a6448 100644 --- a/src/frontends/tests/gamespy/server_browser/game_tests.py +++ b/src/frontends/tests/gamespy/server_browser/game_tests.py @@ -46,6 +46,7 @@ def test_anno1701_20220620(self): sb_client = create_v2_client() for raw in sb_raws: + sb_client.crypto = None sb_client.on_received(raw) @responses.activate diff --git a/src/frontends/tests/gamespy/server_browser/mock_objects.py b/src/frontends/tests/gamespy/server_browser/mock_objects.py index 13a8845af..ca300d416 100644 --- a/src/frontends/tests/gamespy/server_browser/mock_objects.py +++ b/src/frontends/tests/gamespy/server_browser/mock_objects.py @@ -8,11 +8,11 @@ ) from frontends.gamespy.protocols.server_browser.v2.applications.client import Client from frontends.gamespy.protocols.server_browser.v2.applications.handlers import ( - ServerInfoHandler, + UpdateServerInfoHandler, ServerMainListHandler, ) from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( - ServerInfoResult, + UpdateServerInfoResult, ServerMainListResult, ) @@ -47,8 +47,8 @@ def create_v2_client() -> Client: ) create_mock_url( config, - ServerInfoHandler, - ServerInfoResult.model_validate( + UpdateServerInfoHandler, + UpdateServerInfoResult.model_validate( { "game_server_info": { "server_id": "550e8400-e29b-41d4-a716-446655440000", From 1d07a9b083ae04d539c3b3b9f4868c1376a71b19 Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Wed, 15 Oct 2025 07:44:59 +0000 Subject: [PATCH 211/231] Fix(nn): report cache storage --- common/UniSpy_pg.sql | 5 +- src/backends/library/database/pg_orm.py | 9 +-- .../library/networks/redis_brocker.py | 2 +- src/backends/protocols/gamespy/chat/data.py | 3 + src/backends/protocols/gamespy/natneg/data.py | 9 +-- .../protocols/gamespy/natneg/handlers.py | 3 + .../protocols/gamespy/query_report/broker.py | 6 +- .../tests/gamespy/natneg/handler_tests.py | 34 +++++---- .../gamespy/server_browser/filter_tests.py | 70 +++++++++++++++++++ .../protocols/natneg/contracts/requests.py | 8 ++- .../tests/gamespy/natneg/contract_tests.py | 20 ++++++ 11 files changed, 141 insertions(+), 28 deletions(-) create mode 100644 src/backends/tests/gamespy/server_browser/filter_tests.py create mode 100644 src/frontends/tests/gamespy/natneg/contract_tests.py diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index d8144d945..8672d43c4 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -390,10 +390,11 @@ CREATE TABLE unispy.nat_result_caches ( public_ip inet NOT NULL, private_ip inet NOT NULL, is_success boolean NOT NULL, - port_mapping_type smallint NOT NULL, + port_mapping_scheme smallint NOT NULL, port_type smallint NOT NULL, + nat_type smallint NOT NULL, client_index smallint NOT NULL, - game_name character varying[20] NOT NULL, + game_name character varying NOT NULL, update_time timestamp without time zone NOT NULL ); diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 4cbd22864..5042c3253 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -1,7 +1,6 @@ from frontends.gamespy.library.configs import CONFIG from datetime import datetime from sqlalchemy import ( - BigInteger, Boolean, SmallInteger, Text, @@ -21,6 +20,7 @@ NatClientIndex, NatPortMappingScheme, NatPortType, + NatType, ) from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( FriendRequestStatus, @@ -222,10 +222,11 @@ class NatResultCaches(Base): __tablename__ = "nat_result_caches" id = Column(Integer, primary_key=True, autoincrement=True) cookie = Column(SmallInteger, nullable=False) - public_ip = Column(SmallInteger, nullable=False) - private_ip = Column(SmallInteger, nullable=False) + public_ip = Column(INET, nullable=False) + private_ip = Column(INET, nullable=False) is_success = Column(Boolean, nullable=False) - port_mapping_type = Column(IntEnum(NatPortMappingScheme), nullable=False) + port_mapping_scheme = Column(IntEnum(NatPortMappingScheme), nullable=False) + nat_type = Column(IntEnum(NatType), nullable=False) port_type = Column(IntEnum(NatPortType), nullable=False) client_index = Column(IntEnum(NatClientIndex), nullable=False) game_name = Column(String, nullable=True) diff --git a/src/backends/library/networks/redis_brocker.py b/src/backends/library/networks/redis_brocker.py index 2a2433d0f..66d253f51 100644 --- a/src/backends/library/networks/redis_brocker.py +++ b/src/backends/library/networks/redis_brocker.py @@ -23,7 +23,7 @@ def subscribe(self): def get_message(self): self._subscriber.subscribe(self._name) while True: - m = self._subscriber.get_message(timeout=60) + m = self._subscriber.get_message(timeout=10) if m is not None: if "data" not in m: continue diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index a1c1759c7..61cb873ae 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -567,18 +567,21 @@ def clean_expired_user_cache(session: Session): session.query(ChatUserCaches).where( ChatUserCaches.update_time < (datetime.now() - timedelta(minutes=5)) ).delete() + session.commit() def clean_expired_channel_cache(session: Session): session.query(ChatChannelCaches).where( ChatChannelCaches.update_time < (datetime.now() - timedelta(minutes=5)) ).delete() + session.commit() def clean_expired_channel_user_cache(session: Session): session.query(ChatChannelUserCaches).where( ChatUserCaches.update_time < (datetime.now() - timedelta(minutes=5)) ).delete() + session.commit() def _flush_chat_database(): diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py index b7e52d30e..cdc376b81 100644 --- a/src/backends/protocols/gamespy/natneg/data.py +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -22,8 +22,9 @@ def add_init_packet(info: InitPacketCaches, session: Session) -> None: def clean_expired_init_cache(session: Session) -> None: session.query(InitPacketCaches).where( - InitPacketCaches.update_time < datetime.now() - timedelta(seconds=30) - ) + InitPacketCaches.update_time < datetime.now() - timedelta(minutes=5) + ).delete() + session.commit() def count_init_info(cookie: int, version: int, session: Session) -> int: @@ -98,7 +99,7 @@ def update_nat_result_info(info: NatResultCaches, session: Session) -> None: assert isinstance(info, NatResultCaches) result = get_nat_result_info(info, session) - if result is not None: + if len(result) != 0: session.delete(result) store_nat_result_info(info, session) @@ -110,7 +111,7 @@ def remove_nat_result_info(info: NatResultCaches, session: Session) -> None: session.commit() -def get_nat_result_info(info: NatResultCaches, session: Session): +def get_nat_result_info(info: NatResultCaches, session: Session) -> list: assert isinstance(info.cookie, int) assert isinstance(info.public_ip, str) result = get_nat_result_info_by_cookie_ip( diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py index fd0b78a18..54a6b3d86 100644 --- a/src/backends/protocols/gamespy/natneg/handlers.py +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -21,6 +21,7 @@ def __init__(self, request: InitRequest) -> None: super().__init__(request) def _data_operate(self) -> None: + data.clean_expired_init_cache(self._session) info = data.get_init_cache( self._request.cookie, self._request.client_index, @@ -169,7 +170,9 @@ def _data_operate(self) -> None: public_ip=init_cache.public_ip, private_ip=init_cache.private_ip, is_success=self._request.is_nat_success, + port_mapping_scheme=self._request.mapping_scheme, nat_type=self._request.nat_type, + port_type=self._request.port_type, client_index=self._request.client_index, game_name=self._request.game_name, ) diff --git a/src/backends/protocols/gamespy/query_report/broker.py b/src/backends/protocols/gamespy/query_report/broker.py index 997e5d715..49dfd60ee 100644 --- a/src/backends/protocols/gamespy/query_report/broker.py +++ b/src/backends/protocols/gamespy/query_report/broker.py @@ -1,14 +1,16 @@ from contextlib import asynccontextmanager +import logging from fastapi import APIRouter from backends.protocols.gamespy.query_report.handlers import ClientMessageHandler from backends.protocols.gamespy.query_report.requests import ClientMessageRequest from backends.library.networks.redis_brocker import RedisBrocker from frontends.gamespy.library.configs import CONFIG -from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER from backends.library.networks.ws_manager import WebsocketManager as WsManager +logger = logging.getLogger("backend") + class WebsocketManager(WsManager): pass @@ -20,7 +22,7 @@ def handle_client_message(message: str): handler = ClientMessageHandler(request) handler.handle() except Exception as e: - GLOBAL_LOGGER.error(str(e)) + logger.error(str(e)) MANAGER = WebsocketManager() diff --git a/src/backends/tests/gamespy/natneg/handler_tests.py b/src/backends/tests/gamespy/natneg/handler_tests.py index 3c924aae7..bf7f3bb1d 100644 --- a/src/backends/tests/gamespy/natneg/handler_tests.py +++ b/src/backends/tests/gamespy/natneg/handler_tests.py @@ -1,12 +1,22 @@ import unittest -from backends.protocols.gamespy.natneg.handlers import ConnectHandler, InitHandler -from backends.protocols.gamespy.natneg.requests import InitRequest, ConnectRequest +from backends.protocols.gamespy.natneg.handlers import ConnectHandler, InitHandler, ReportHandler +from backends.protocols.gamespy.natneg.requests import InitRequest, ConnectRequest, ReportRequest from backends.tests.utils import add_headers import frontends.gamespy.protocols.natneg.contracts.requests as fnt class HandlerTests(unittest.TestCase): - def test_init(self): + + @unittest.skip("") + def test_report(self): + req_dict = {"raw_request": "\\xfd\\xfc\u001efj\\xb2\u0004\r\u0000\u0000\u0002\\x9a\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0001\u0000\u0000\u0000gmtest\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000", + "version": 4, "command_name": 13, "cookie": 666, "port_type": 0, "client_index": 0, "use_game_port": False, "is_nat_success": False, "nat_type": 0, "mapping_scheme": 0, "game_name": "gmtest", "client_ip": "172.19.0.5", "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "client_port": 36229} + req = ReportRequest.model_validate(req_dict) + handler = ReportHandler(req) + handler.handle() + pass + + def test_connect(self): raw = bytes( [ 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, @@ -14,20 +24,16 @@ def test_init(self): 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] ) # fmt:skip - r = fnt.InitRequest(raw) + r = fnt.ConnectRequest(raw) data = add_headers(r) data["raw_request"] = data["raw_request"].decode( "ascii", "backslashreplace") - request = InitRequest(**data) - handler = InitHandler(request) + request = ConnectRequest(**data) + handler = ConnectHandler(request) handler.handle() pass - @unittest.skip("not implemented") - def test_report(self): - raise NotImplementedError() - - def test_connect(self): + def test_init(self): raw = bytes( [ 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, @@ -35,11 +41,11 @@ def test_connect(self): 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ] ) # fmt:skip - r = fnt.ConnectRequest(raw) + r = fnt.InitRequest(raw) data = add_headers(r) data["raw_request"] = data["raw_request"].decode( "ascii", "backslashreplace") - request = ConnectRequest(**data) - handler = ConnectHandler(request) + request = InitRequest(**data) + handler = InitHandler(request) handler.handle() pass diff --git a/src/backends/tests/gamespy/server_browser/filter_tests.py b/src/backends/tests/gamespy/server_browser/filter_tests.py new file mode 100644 index 000000000..937b37de5 --- /dev/null +++ b/src/backends/tests/gamespy/server_browser/filter_tests.py @@ -0,0 +1,70 @@ +# 导入 Interpreter +from asteval import Interpreter +import re + +# 步骤1:创建 Interpreter 实例 +aeval = Interpreter() + +# 步骤2:定义服务器数据字典列表 +server_data_list = [ + { + "natneg": "1", + "gamever": "2.00", + "gravity": 800, + "mapname": "gmtmap2", + "gamemode": "openplaying", + "gamename": "gmtest2", + "gametype": "arena", + "hostname": "Server 1", + "hostport": "25000", + "localip0": "172.19.0.4", + "numteams": 2, + "teamplay": 1, + "fraglimit": 0, + "localport": 11111, + "rankingon": 1, + "timelimit": 40, + "maxplayers": 32, + "numplayers": 8, + "statechanged": 1, + }, + { + "natneg": "1", + "gamever": "2.00", + "gravity": 600, + "mapname": "gmtmap2", + "gamemode": "openplaying", + "gamename": "gmtest2", + "gametype": "arena", + "hostname": "Server 2", + "hostport": "25000", + "localip0": "172.19.0.4", + "numteams": 2, + "teamplay": 1, + "fraglimit": 0, + "localport": 11111, + "rankingon": 1, + "timelimit": 40, + "maxplayers": 32, + "numplayers": 4, + "statechanged": 1, + } +] + +# 步骤3:定义条件 +condition = "gravity > 700 and numplayers > 5 and gamemode == 'openplaying'" + +# 步骤4:提取条件中的键 +pattern = r'(\w+)\s*([<>!=]+)\s*([\'\"]?)(.*?)\3' +matches = re.findall(pattern, condition) +keys = [match[0] for match in matches] +print(f"提取的键: {keys}") + +# 步骤5:评估每个服务器数据字典的条件 +for server in server_data_list: + # 更新上下文:动态加载提取的键 + for key in keys: + if key in server: + aeval.symtable[key] = server[key] + result = aeval(condition) + print(f"服务器 {server['hostname']} (Gravity: {server['gravity']}, Players: {server['numplayers']}): {result}") diff --git a/src/frontends/gamespy/protocols/natneg/contracts/requests.py b/src/frontends/gamespy/protocols/natneg/contracts/requests.py index 7dffd556b..b78293bb6 100644 --- a/src/frontends/gamespy/protocols/natneg/contracts/requests.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/requests.py @@ -40,7 +40,13 @@ class ConnectAckRequest(RequestBase): client_index: NatClientIndex def parse(self) -> None: - super().parse() + if len(self.raw_request) < 12: + return + + self.version = int(self.raw_request[6]) + self.command_name = RequestType(self.raw_request[7]) + self.cookie = int.from_bytes( + self.raw_request[8:12]) self.client_index = NatClientIndex(self.raw_request[13]) diff --git a/src/frontends/tests/gamespy/natneg/contract_tests.py b/src/frontends/tests/gamespy/natneg/contract_tests.py new file mode 100644 index 000000000..0350e6d06 --- /dev/null +++ b/src/frontends/tests/gamespy/natneg/contract_tests.py @@ -0,0 +1,20 @@ +from unittest import TestCase + +from frontends.gamespy.protocols.natneg.aggregations.enums import NatClientIndex +from frontends.gamespy.protocols.natneg.applications.switcher import Switcher +from frontends.gamespy.protocols.natneg.contracts.requests import ConnectAckRequest +from frontends.tests.gamespy.natneg.mock_objects import create_client + + +class ContractTests(TestCase): + def test_connect_ack(self): + raws = [ + b'\xfd\xfc\x1efj\xb2\x04\x06\x00\x00\x02\x9a\x00\x01\x00\x00\xd8\xf2@\x00\x00', + b'\xfd\xfc\x1efj\xb2\x04\x06\x00\x00\x02\x9aj\x01\x04\x07\x00\x00\x02\x9a\xac', + b'\xfd\xfc\x1efj\xb2\x04\x06\x00\x00\x02\x9aj\x01\x04\x07\x00\x00\x02\x9a\xac', + b'\xfd\xfc\x1efj\xb2\x04\x06\x00\x00\x02\x9a@\x01\x00\x00\xc0\xf28o\xfd', + ] + for raw in raws: + r = ConnectAckRequest(raw) + r.parse() + self.assertEqual(r.client_index, NatClientIndex.GAME_SERVER) From d5c2004eb9b45bc8bda8765d2527caa3c2d24811 Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Fri, 17 Oct 2025 17:45:08 +0800 Subject: [PATCH 212/231] fix(gtr): negotiating logic --- src/backends/protocols/gamespy/natneg/data.py | 14 ++- .../library/abstractions/connections.py | 4 + .../game_traffic_relay/applications/client.py | 103 ++++++++---------- .../applications/connection.py | 96 ++++++++++++++++ .../applications/handlers.py | 35 +++++- .../applications/server_launcher.py | 5 +- .../natneg/abstractions/contracts.py | 2 +- .../protocols/natneg/applications/handlers.py | 4 +- .../protocols/natneg/applications/switcher.py | 4 + .../protocols/natneg/contracts/requests.py | 2 +- .../protocols/natneg/contracts/responses.py | 2 +- .../game_traffic_relay/handler_tests.py | 16 +-- .../game_traffic_relay/mock_objects.py | 4 +- 13 files changed, 208 insertions(+), 83 deletions(-) create mode 100644 src/frontends/gamespy/protocols/game_traffic_relay/applications/connection.py diff --git a/src/backends/protocols/gamespy/natneg/data.py b/src/backends/protocols/gamespy/natneg/data.py index cdc376b81..7b7f5b5cc 100644 --- a/src/backends/protocols/gamespy/natneg/data.py +++ b/src/backends/protocols/gamespy/natneg/data.py @@ -90,17 +90,21 @@ def remove_init_info(info: InitPacketCaches, session: Session) -> None: def store_nat_result_info(info: NatResultCaches, session: Session) -> None: assert isinstance(info, NatResultCaches) - session.add(info) session.commit() def update_nat_result_info(info: NatResultCaches, session: Session) -> None: + """ + remove the exist record and store the newest record + """ assert isinstance(info, NatResultCaches) - result = get_nat_result_info(info, session) - if len(result) != 0: - session.delete(result) + session.query(NatResultCaches).where( + NatResultCaches.cookie == info.cookie, + NatResultCaches.public_ip == info.public_ip, + NatResultCaches.private_ip == info.private_ip + ).delete() store_nat_result_info(info, session) @@ -111,7 +115,7 @@ def remove_nat_result_info(info: NatResultCaches, session: Session) -> None: session.commit() -def get_nat_result_info(info: NatResultCaches, session: Session) -> list: +def get_nat_result_info(info: NatResultCaches, session: Session) -> list[NatResultCaches]: assert isinstance(info.cookie, int) assert isinstance(info.public_ip, str) result = get_nat_result_info_by_cookie_ip( diff --git a/src/frontends/gamespy/library/abstractions/connections.py b/src/frontends/gamespy/library/abstractions/connections.py index 56bee8e1d..70c14ac99 100644 --- a/src/frontends/gamespy/library/abstractions/connections.py +++ b/src/frontends/gamespy/library/abstractions/connections.py @@ -16,6 +16,10 @@ class ConnectionBase: logger: LogWriter handler: socketserver.BaseRequestHandler _client: ClientBase + ip_endpoint: str + """ + : string + """ def __init__( self, diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/client.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/client.py index adcd4e466..b9de87adf 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/client.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/client.py @@ -1,61 +1,26 @@ from datetime import datetime +from frontends.gamespy.library.abstractions.client import ClientBase +from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.protocols.game_traffic_relay.applications.connection import ConnectStatus, ConnectionListener +from frontends.gamespy.protocols.natneg.abstractions.contracts import MAGIC_DATA import frontends.gamespy.protocols.natneg.applications.client as natneg class ClientInfo: - cookie: int + cookie: int | None last_receive_time: datetime - pass + status: ConnectStatus + ping_recv_times: int + def __init__(self) -> None: + self.cookie = None + self.last_receive_time = datetime.now() + self.status = ConnectStatus.WAITING_FOR_ANOTHER + self.ping_recv_times = 0 -class ConnectionListener: - pool: dict[int, list["Client"]] = {} - def is_client_exist(self, cookie: int, client: "Client"): - if cookie not in self.pool: - return False - else: - clients = self.pool[cookie] - # if current client is not in the pair - if client not in clients: - return False - - def add_client(self, cookie: int, client: "Client"): - if cookie not in self.pool: - self.pool[cookie] = [client] - else: - clients = self.pool[cookie] - # if current clients is in the pair - if len(clients) == 2 and client in clients: - return - # if current client is not in the pair - if len(clients) == 1 and client not in clients: - clients.append(client) - - def get_another_client(self, cookie: int, client: "Client"): - if cookie not in self.pool: - raise ValueError("cookie not add to pool") - else: - clients = self.pool[cookie] - # if current client is not in the pair - if client not in clients: - raise ValueError("client not in cookie") - if len(clients) != 2: - raise ValueError("2 clients are not ready") - return clients[0] if clients[1] == client else clients[1] - - def is_both_client_ready(self, cookie: int, client: "Client") -> bool: - clients = self.pool[cookie] - if len(clients) != 2: - return False - else: - return True - - -class Client(natneg.Client): +class Client(ClientBase): info: ClientInfo - client_pool: dict[str, "Client"] = {} - listener: ConnectionListener = ConnectionListener() def __init__( self, @@ -66,17 +31,43 @@ def __init__( super().__init__(connection, server_config, logger) self.info = ClientInfo() + def _create_switcher(self, buffer: bytes) -> SwitcherBase: + from frontends.gamespy.protocols.game_traffic_relay.applications.switcher import Switcher + return Switcher(self, buffer) + def on_received(self, buffer: bytes) -> None: """ when we receive udp message, we check whether the client pair is ready. if client is ready we send the following data to the another client """ - self.info.last_receive_time = datetime.now() - if self.listener.is_both_client_ready(self.info.cookie, self): - another_client = self.listener.get_another_client(self.info.cookie, self) - another_client.connection.send(buffer) - self.log_network_sending( - f"=> [{another_client.connection.ip_endpoint}] {buffer}" - ) + # get client from client pool + saved_client = ConnectionListener.get_client_by_ip( + self.connection.ip_endpoint) + if saved_client is not None: + Client.process_message(saved_client, buffer) else: - super().on_received(buffer) + Client.process_message(self, buffer) + + @staticmethod + def process_message(client: "Client", buffer: bytes): + client.info.last_receive_time = datetime.now() + match client.info.status: + case ConnectStatus.WAITING_FOR_ANOTHER | ConnectStatus.CONNECTING: + client.log_network_receving(buffer) + switcher = client._create_switcher(buffer) + switcher.handle() + case ConnectStatus.FINISHED: + # !! manually stop ping message here, otherwise the ping message will continuely sending + if client.info.ping_recv_times >= 7: + if len(buffer) >= 6 and buffer[:6] == MAGIC_DATA: + client.log_info( + "Negotiation is finished, ignore ping packet") + return + + assert client.info.cookie is not None + another_client = ConnectionListener.get_another_client( + client.info.cookie, client) + another_client.connection.send(buffer) + client.log_network_sending( + f"=> [{another_client.connection.ip_endpoint}] {buffer}" + ) diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/connection.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/connection.py new file mode 100644 index 000000000..e918d6d84 --- /dev/null +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/connection.py @@ -0,0 +1,96 @@ +from datetime import datetime, timedelta +from enum import Enum +from typing import TYPE_CHECKING + +from frontends.gamespy.library.exceptions.general import UniSpyException + +if TYPE_CHECKING: + from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client + + +class ConnectStatus(Enum): + WAITING_FOR_ANOTHER = 0 + CONNECTING = 1 + FINISHED = 2 + + +class ConnectionListener: + """ + manage and monitor the connections + """ + cookie_pool: dict[int, list["Client"]] = {} + client_pool: dict[str, "Client"] = {} + + @staticmethod + def get_client_by_ip(ip_end: str) -> "Client|None": + if ip_end in ConnectionListener.client_pool: + return ConnectionListener.client_pool[ip_end] + else: + return None + + @staticmethod + def is_client_exist(cookie: int, client: "Client"): + if cookie not in ConnectionListener.cookie_pool: + return False + else: + clients = ConnectionListener.cookie_pool[cookie] + # if current client is not in the pair + if client not in clients: + return False + else: + return True + + @staticmethod + def add_client(cookie: int, client: "Client"): + # add client to client pool + if client.connection.ip_endpoint not in ConnectionListener.client_pool: + ConnectionListener.client_pool[client.connection.ip_endpoint] = client + # then add to cookie pool + if cookie not in ConnectionListener.cookie_pool: + ConnectionListener.cookie_pool[cookie] = [client] + else: + clients = ConnectionListener.cookie_pool[cookie] + ConnectionListener.check_whether_accept_new_connection( + cookie, client, clients) + + @staticmethod + def get_another_client(cookie: int, client: "Client"): + if cookie not in ConnectionListener.cookie_pool: + raise ValueError("cookie not add to pool") + else: + clients = ConnectionListener.cookie_pool[cookie] + # if current client is not in the pair + if client not in clients: + raise ValueError("client not in cookie") + if len(clients) != 2: + raise ValueError("2 clients are not ready") + return clients[0] if clients[1] == client else clients[1] + + @staticmethod + def is_both_client_ready(cookie: int) -> bool: + clients = ConnectionListener.cookie_pool[cookie] + if len(clients) != 2: + return False + else: + return True + + @staticmethod + def check_whether_accept_new_connection(cookie: int, client: "Client", clients: list["Client"]): + # if current clients is in the pair + if len(clients) == 2: + if client in clients: + return + else: + raise UniSpyException( + f"cookie: {cookie} is alive, you can not neogotiate with exist connections") + # expire_time = datetime.now() - timedelta(minutes=5) + # if 2 clients are expired + # if all(client.info.last_receive_time < expire_time for client in clients): + # client.log_info( + # f"cookie:{cookie} is expired, replace with new clients") + # ConnectionListener.cookie_pool[cookie] = [client] + # return + + # if current client is not in the pair + if len(clients) == 1 and client not in clients: + clients.append(client) diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py index 60d65a16a..f6699c712 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py @@ -1,5 +1,6 @@ from frontends.gamespy.library.abstractions.handler import CmdHandlerBase from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client +from frontends.gamespy.protocols.game_traffic_relay.applications.connection import ConnectStatus, ConnectionListener from frontends.gamespy.protocols.natneg.contracts.requests import PingRequest @@ -14,11 +15,39 @@ def __init__(self, client: Client, request: PingRequest) -> None: self._is_uploading = False def _data_operate(self) -> None: - if not self._client.listener.is_client_exist( + match self._client.info.status: + case ConnectStatus.WAITING_FOR_ANOTHER: + self.__waiting_for_another() + case ConnectStatus.CONNECTING: + self.__connecting() + + def __waiting_for_another(self): + is_exist = ConnectionListener.is_client_exist( self._request.cookie, self._client - ): - Client.listener.add_client(self._request.cookie, self._client) + ) + if not is_exist: + ConnectionListener.add_client( + self._request.cookie, self._client) self._client.info.cookie = self._request.cookie self._client.log_info( f"Add client to listener cookie:{self._request.cookie}" ) + self._client.info.status = ConnectStatus.WAITING_FOR_ANOTHER + else: + assert self._client.info.cookie is not None + is_both_client_ready = ConnectionListener.is_both_client_ready( + self._client.info.cookie) + if is_both_client_ready: + self._client.info.status = ConnectStatus.CONNECTING + + def __connecting(self): + assert self._client.info.cookie is not None + another_client = ConnectionListener.get_another_client( + self._client.info.cookie, self._client) + another_client.connection.send(self._request.raw_request) + self._client.log_info( + f"=> [{another_client.connection.ip_endpoint}] {self._request.raw_request}" + ) + self._client.info.ping_recv_times += 1 + if self._client.info.ping_recv_times >= 7: + self._client.info.status = ConnectStatus.FINISHED diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py index c21202384..f4a3c722a 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py @@ -2,6 +2,7 @@ from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.library.network.udp_handler import UdpServer from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client +from frontends.gamespy.protocols.game_traffic_relay.applications.connection import ConnectionListener from frontends.gamespy.protocols.game_traffic_relay.contracts.general import ( GtrHeartbeat, ) @@ -27,9 +28,9 @@ def _gtr_heartbeat(self): assert self.config req = GtrHeartbeat( server_id=self.config.server_id, - public_ip_address=self._public_ip, + public_ip_address=self.config.public_address, public_port=self.config.listening_port, - client_count=len(Client.client_pool), + client_count=len(ConnectionListener.client_pool), ) req_str = req.model_dump_json() self._heartbeat_to_backend( diff --git a/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py b/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py index 52f1d30ad..89e46b7f3 100644 --- a/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py @@ -8,7 +8,7 @@ ) from frontends.gamespy.library.extentions.bytes_extentions import ip_to_4_bytes -MAGIC_DATA = bytes([0xFD, 0xFC, 0x1E, 0x66, 0x6A, 0xB2]) +MAGIC_DATA = b"\xfd\xfc\x1e\x66\x6a\xb2" class RequestBase(lib.RequestBase): diff --git a/src/frontends/gamespy/protocols/natneg/applications/handlers.py b/src/frontends/gamespy/protocols/natneg/applications/handlers.py index 372a22985..16b3d6f98 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/handlers.py +++ b/src/frontends/gamespy/protocols/natneg/applications/handlers.py @@ -1,5 +1,5 @@ from frontends.gamespy.protocols.natneg.abstractions.handlers import CmdHandlerBase -from frontends.gamespy.protocols.natneg.aggregations.enums import RequestType +from frontends.gamespy.protocols.natneg.aggregations.enums import ConnectPacketStatus, RequestType from frontends.gamespy.protocols.natneg.applications.client import Client from frontends.gamespy.protocols.natneg.contracts.requests import ( AddressCheckRequest, @@ -69,7 +69,7 @@ def __init__(self, client: Client, request: ConnectAckRequest) -> None: def _response_construct(self) -> None: self._client.log_info( - f"client: {self._request.client_index} is received the connect packet.") + f"client: {self._client.connection.remote_ip} index:{self._request.client_index} is received the connect packet.") class ConnectHandler(CmdHandlerBase): diff --git a/src/frontends/gamespy/protocols/natneg/applications/switcher.py b/src/frontends/gamespy/protocols/natneg/applications/switcher.py index b41d51a58..ff463a65f 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/switcher.py +++ b/src/frontends/gamespy/protocols/natneg/applications/switcher.py @@ -1,5 +1,6 @@ from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.protocols.natneg.abstractions.handlers import CmdHandlerBase from frontends.gamespy.protocols.natneg.applications.client import Client from frontends.gamespy.protocols.natneg.contracts.requests import ( @@ -33,6 +34,9 @@ def __init__(self, client: Client, raw_request: bytes) -> None: assert isinstance(raw_request, bytes) def _process_raw_request(self) -> None: + if len(self._raw_request) < 8: + self._client.log_debug("incoming buffer length is not valid") + return name = self._raw_request[7] if name not in RequestType: self._client.log_debug(f"Request: {name} is not a valid request.") diff --git a/src/frontends/gamespy/protocols/natneg/contracts/requests.py b/src/frontends/gamespy/protocols/natneg/contracts/requests.py index b78293bb6..f923c3018 100644 --- a/src/frontends/gamespy/protocols/natneg/contracts/requests.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/requests.py @@ -28,7 +28,7 @@ def parse(self) -> None: self.version = int(self.raw_request[6]) self.command_name = RequestType(self.raw_request[7]) self.cookie = int.from_bytes( - self.raw_request[8:12], byteorder="little") + self.raw_request[8:12]) self.ip = socket.inet_ntoa(self.raw_request[12:16]) self.port = int.from_bytes(self.raw_request[16:18]) # port here is not in little endian diff --git a/src/frontends/gamespy/protocols/natneg/contracts/responses.py b/src/frontends/gamespy/protocols/natneg/contracts/responses.py index dbb996133..10806166a 100644 --- a/src/frontends/gamespy/protocols/natneg/contracts/responses.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/responses.py @@ -57,7 +57,7 @@ def build(self) -> None: data = bytes() data += self.sending_buffer data += socket.inet_aton(self._result.ip) - data += self._result.port.to_bytes(2)[::-1] + data += self._result.port.to_bytes(2) data += self._result.got_your_data data += self._result.status.value.to_bytes(1) self.sending_buffer = data diff --git a/src/frontends/tests/gamespy/game_traffic_relay/handler_tests.py b/src/frontends/tests/gamespy/game_traffic_relay/handler_tests.py index ec03da967..7dcde2bc4 100644 --- a/src/frontends/tests/gamespy/game_traffic_relay/handler_tests.py +++ b/src/frontends/tests/gamespy/game_traffic_relay/handler_tests.py @@ -1,5 +1,6 @@ import unittest +from frontends.gamespy.protocols.game_traffic_relay.applications.client import ConnectionListener from frontends.gamespy.protocols.game_traffic_relay.applications.handlers import ( PingHandler, ) @@ -16,20 +17,15 @@ def test_ping(self): b"\xfd\xfc\x1efj\xb2\x03\x07\x00\x00\x02\x9a\xc0\xa8\x01gl\xfd\x00\x00" ) client1 = create_client(("127.0.0.1", 1234)) - client1.connection.remote_ip = "127.0.0.1" - client1.connection.remote_port = 1234 client1._log_prefix = "[127.0.0.1:1234]" client2 = create_client(("127.0.0.1", 1235)) - client2.connection.remote_ip = "127.0.0.1" - client2.connection.remote_port = 1235 client2._log_prefix = "[127.0.0.1:1235]" - handler = PingHandler(client1, PingRequest(ping_raw)) - handler.handle() - handler = PingHandler(client2, PingRequest(ping_raw)) - handler.handle() + client1.on_received(ping_raw) + client2.on_received(ping_raw) # cookie length check - self.assertEqual(len(client1.listener.pool), 1) - clients = list(client1.listener.pool.values())[0] + self.assertEqual(len(ConnectionListener.cookie_pool), 1) + clients = list(ConnectionListener.cookie_pool.values())[0] self.assertEqual(len(clients), 2) + client1 = create_client(("127.0.0.1", 1234)) client1.on_received(ping_raw) pass diff --git a/src/frontends/tests/gamespy/game_traffic_relay/mock_objects.py b/src/frontends/tests/gamespy/game_traffic_relay/mock_objects.py index 4bfd376dd..a8d761954 100644 --- a/src/frontends/tests/gamespy/game_traffic_relay/mock_objects.py +++ b/src/frontends/tests/gamespy/game_traffic_relay/mock_objects.py @@ -22,11 +22,11 @@ def create_client(client_address: tuple = ("192.168.0.1", 0)) -> Client: logger = LogMock() conn = ConnectionMock( handler=handler, - config=CONFIG.servers["NatNegotiation"], + config=CONFIG.servers["GameTrafficRelay"], t_client=ClientMock, logger=logger, ) - config = CONFIG.servers["NatNegotiation"] + config = CONFIG.servers["GameTrafficRelay"] create_mock_url(config, PingHandler, {"message": "ok"}) return cast(Client, conn._client) From 14a054596d2f02bd02cc0c6c223f5e32f285f173 Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Mon, 20 Oct 2025 02:44:09 +0000 Subject: [PATCH 213/231] Update: optimize websocket process logic --- src/backends/library/networks/ws_manager.py | 21 +++++++++++++++++++- src/backends/routers/gamespy/chat.py | 12 +---------- src/backends/routers/gamespy/query_report.py | 14 ++----------- 3 files changed, 23 insertions(+), 24 deletions(-) diff --git a/src/backends/library/networks/ws_manager.py b/src/backends/library/networks/ws_manager.py index 2f69c45ed..949b68ce2 100644 --- a/src/backends/library/networks/ws_manager.py +++ b/src/backends/library/networks/ws_manager.py @@ -1,6 +1,8 @@ import asyncio import logging -from fastapi import WebSocket +from fastapi import WebSocket, WebSocketDisconnect + +from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER class WebsocketManager: @@ -64,3 +66,20 @@ def broadcast_except(self, message: str, wss: list[WebSocket], except_ip: list[s if ws.client.host not in except_ip: filtered_wss.append(ws) self._broadcast(message, filtered_wss) + + async def process_websocket(self, ws: WebSocket): + """ + process websocket connection here + """ + await ws.accept() + if isinstance(ws, WebSocket) and ws.client is not None: + self.connect(ws) + try: + while True: + _ = await ws.receive_json() + except WebSocketDisconnect: + if ws.client is not None: + # remove chat info by websocket + self.disconnect(ws) + GLOBAL_LOGGER.info( + f"websocket client: [{ws.client.host}:{ws.client.port} is disconnected") diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index d2434980b..8b30faaaa 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -68,17 +68,7 @@ def publish_message(request: PublishMessageRequest): @router.websocket(f"{CHAT}/ws") async def websocket_endpoint(ws: WebSocket): - await ws.accept() - if isinstance(ws, WebSocket) and ws.client is not None: - MANAGER.connect(ws) - try: - while True: - _ = await ws.receive_json() - except WebSocketDisconnect: - if ws.client is not None: - MANAGER.disconnect(ws) - # todo remove chat info by websocket - print("Client disconnected") + await MANAGER.process_websocket(ws) # region General diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index 991d7855f..6c0031216 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -1,4 +1,4 @@ -from fastapi import APIRouter, WebSocket, WebSocketDisconnect +from fastapi import APIRouter, WebSocket from backends.protocols.gamespy.query_report.broker import MANAGER, launch_brocker from backends.protocols.gamespy.query_report.handlers import ( AvaliableHandler, HeartbeatHandler, KeepAliveHandler) @@ -12,17 +12,7 @@ @router.websocket(f"{QUERY_REPORT}/ws") async def websocket_endpoint(ws: WebSocket): - await ws.accept() - if isinstance(ws, WebSocket) and ws.client is not None: - MANAGER.connect(ws) - try: - while True: - _ = await ws.receive_json() - # query report do not process ws client message - except WebSocketDisconnect: - if ws.client is not None: - MANAGER.disconnect(ws) - print("Client disconnected") + await MANAGER.process_websocket(ws) @router.post(f"{QUERY_REPORT}/HeartbeatHandler") From 5da3e05f9a60a4f5a692d78d335616295148fed3 Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Mon, 20 Oct 2025 02:45:08 +0000 Subject: [PATCH 214/231] Update(gtr): optimize message handle logic --- .../library/abstractions/server_launcher.py | 6 +-- .../gamespy/library/log/log_manager.py | 11 ++--- .../game_traffic_relay/applications/client.py | 39 +----------------- .../applications/connection.py | 3 ++ .../applications/handlers.py | 40 ++++++++++++++++--- .../applications/server_launcher.py | 21 ++++++++++ .../applications/switcher.py | 27 +++++++++---- .../game_traffic_relay/contracts/general.py | 6 +++ .../protocols/natneg/aggregations/enums.py | 5 ++- 9 files changed, 99 insertions(+), 59 deletions(-) diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index c03369d0e..ed1869a4f 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -33,7 +33,7 @@ class ServerLauncherBase: config: ServerConfig logger: LogWriter | None server: NetworkServerBase | None - _schedular: Schedular + _conn_checker: Schedular _server_cls: type[NetworkServerBase] _client_cls: type[ClientBase] @@ -137,8 +137,8 @@ def _launch_heartbeat_schedular(self): set the schedular to send heartbeat info to backend to keep the infomation update """ #! temperarily use connect to backend function - self._schedular = Schedular(self._connect_to_backend, 30) - self._schedular.start() + self._conn_checker = Schedular(self._connect_to_backend, 30) + self._conn_checker.start() @final def _create_logger(self): diff --git a/src/frontends/gamespy/library/log/log_manager.py b/src/frontends/gamespy/library/log/log_manager.py index d69e473fe..586575d56 100644 --- a/src/frontends/gamespy/library/log/log_manager.py +++ b/src/frontends/gamespy/library/log/log_manager.py @@ -57,9 +57,14 @@ def create(logger_name: str) -> "LogWriter": log_file_path = CONFIG.logging.path create_dir(log_file_path) file_name = f"{log_file_path}/{logger_name}.log" + if CONFIG.logging.min_log_level == "debug": + log_level = logging.DEBUG + else: + log_level = logging.INFO + logging.basicConfig( filename=file_name, - level=logging.INFO, + level=log_level, format=f"%(asctime)s [{logger_name}] [%(levelname)s]: %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) @@ -75,14 +80,10 @@ def create(logger_name: str) -> "LogWriter": f"%(asctime)s [{logger_name}] [%(levelname)s]: %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) - file_handler.setLevel( - logging.DEBUG - ) # Set the desired log level for the console file_handler.setFormatter(formatter) # create console log handler console_handler = logging.StreamHandler() - console_handler.setLevel(logging.DEBUG) console_handler.setFormatter(formatter) # create logger logger = logging.getLogger(logger_name) diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/client.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/client.py index b9de87adf..585d8b188 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/client.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/client.py @@ -33,41 +33,4 @@ def __init__( def _create_switcher(self, buffer: bytes) -> SwitcherBase: from frontends.gamespy.protocols.game_traffic_relay.applications.switcher import Switcher - return Switcher(self, buffer) - - def on_received(self, buffer: bytes) -> None: - """ - when we receive udp message, we check whether the client pair is ready. - if client is ready we send the following data to the another client - """ - # get client from client pool - saved_client = ConnectionListener.get_client_by_ip( - self.connection.ip_endpoint) - if saved_client is not None: - Client.process_message(saved_client, buffer) - else: - Client.process_message(self, buffer) - - @staticmethod - def process_message(client: "Client", buffer: bytes): - client.info.last_receive_time = datetime.now() - match client.info.status: - case ConnectStatus.WAITING_FOR_ANOTHER | ConnectStatus.CONNECTING: - client.log_network_receving(buffer) - switcher = client._create_switcher(buffer) - switcher.handle() - case ConnectStatus.FINISHED: - # !! manually stop ping message here, otherwise the ping message will continuely sending - if client.info.ping_recv_times >= 7: - if len(buffer) >= 6 and buffer[:6] == MAGIC_DATA: - client.log_info( - "Negotiation is finished, ignore ping packet") - return - - assert client.info.cookie is not None - another_client = ConnectionListener.get_another_client( - client.info.cookie, client) - another_client.connection.send(buffer) - client.log_network_sending( - f"=> [{another_client.connection.ip_endpoint}] {buffer}" - ) + return Switcher(self, buffer) \ No newline at end of file diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/connection.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/connection.py index e918d6d84..d6f9d5f5c 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/connection.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/connection.py @@ -3,6 +3,7 @@ from typing import TYPE_CHECKING from frontends.gamespy.library.exceptions.general import UniSpyException +from frontends.gamespy.library.extentions.schedular import Schedular if TYPE_CHECKING: from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client @@ -94,3 +95,5 @@ def check_whether_accept_new_connection(cookie: int, client: "Client", clients: # if current client is not in the pair if len(clients) == 1 and client not in clients: clients.append(client) + + diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py index f6699c712..891cf4476 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py @@ -1,6 +1,10 @@ +from datetime import datetime +from frontends.gamespy.library.abstractions.client import ClientBase +from frontends.gamespy.library.abstractions.contracts import RequestBase from frontends.gamespy.library.abstractions.handler import CmdHandlerBase from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client from frontends.gamespy.protocols.game_traffic_relay.applications.connection import ConnectStatus, ConnectionListener +from frontends.gamespy.protocols.game_traffic_relay.contracts.general import MessageRelayRequest from frontends.gamespy.protocols.natneg.contracts.requests import PingRequest @@ -19,6 +23,10 @@ def _data_operate(self) -> None: case ConnectStatus.WAITING_FOR_ANOTHER: self.__waiting_for_another() case ConnectStatus.CONNECTING: + if self._client.info.ping_recv_times >= 7: + self._client.log_info( + "Negotiation is finished, ignore ping packet") + return self.__connecting() def __waiting_for_another(self): @@ -32,7 +40,6 @@ def __waiting_for_another(self): self._client.log_info( f"Add client to listener cookie:{self._request.cookie}" ) - self._client.info.status = ConnectStatus.WAITING_FOR_ANOTHER else: assert self._client.info.cookie is not None is_both_client_ready = ConnectionListener.is_both_client_ready( @@ -41,13 +48,36 @@ def __waiting_for_another(self): self._client.info.status = ConnectStatus.CONNECTING def __connecting(self): + assert self._client.info.cookie is not None + handler = MessageRelayHandler( + self._client, MessageRelayRequest(self._request.raw_request)) + handler.handle() + self._client.info.ping_recv_times += 1 + + if ConnectionListener.is_both_client_ready(self._client.info.cookie): + if self._client.info.ping_recv_times >= 7: + self._client.info.status = ConnectStatus.FINISHED + + +class MessageRelayHandler(CmdHandlerBase): + _request: MessageRelayRequest + _client: Client + + def __init__(self, client: ClientBase, request: RequestBase) -> None: + super().__init__(client, request) + self._is_fetching = False + self._is_uploading = False + + def _data_operate(self) -> None: + """ + when we receive udp message, we check whether the client pair is ready. + if client is ready we send the following data to the another client + """ + self._client.info.last_receive_time = datetime.now() assert self._client.info.cookie is not None another_client = ConnectionListener.get_another_client( self._client.info.cookie, self._client) another_client.connection.send(self._request.raw_request) - self._client.log_info( + self._client.log_network_sending( f"=> [{another_client.connection.ip_endpoint}] {self._request.raw_request}" ) - self._client.info.ping_recv_times += 1 - if self._client.info.ping_recv_times >= 7: - self._client.info.status = ConnectStatus.FINISHED diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py index f4a3c722a..87c1dc243 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py @@ -1,5 +1,8 @@ +from datetime import datetime, timedelta from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.library.extentions.schedular import Schedular +from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER from frontends.gamespy.library.network.udp_handler import UdpServer from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client from frontends.gamespy.protocols.game_traffic_relay.applications.connection import ConnectionListener @@ -18,6 +21,7 @@ def __init__(self) -> None: server_cls=UdpServer, ) self._get_public_ip() + self._launch_connection_expire_checker() def _get_public_ip(self): url = f"{CONFIG.backend.url}/GameSpy/GameTrafficRelay/get_my_ip" @@ -50,6 +54,23 @@ def _connect_to_backend(self): super()._connect_to_backend() self._gtr_heartbeat() + # region Expire Checker + def _launch_connection_expire_checker(self): + self._conn_checker = Schedular(self.__check_expired_connection, 30) + self._conn_checker.start() + + def __check_expired_connection(self): + expired_time = datetime.now() - timedelta(seconds=30) + try: + for key in ConnectionListener.cookie_pool.keys(): + pair = ConnectionListener.cookie_pool[key] + if pair[0].info.last_receive_time < expired_time: + del ConnectionListener.cookie_pool[key] + del ConnectionListener.client_pool[pair[0].connection.ip_endpoint] + del ConnectionListener.client_pool[pair[1].connection.ip_endpoint] + except Exception as e: + GLOBAL_LOGGER.warn(f"Errors occured when doing cookie delete: {e}") + if __name__ == "__main__": s = ServerLauncher() diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/switcher.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/switcher.py index bf0201c80..d0f538b7a 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/switcher.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/switcher.py @@ -1,8 +1,13 @@ +from frontends.gamespy.library.abstractions.handler import CmdHandlerBase from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client +from frontends.gamespy.protocols.game_traffic_relay.applications.connection import ConnectStatus, ConnectionListener from frontends.gamespy.protocols.game_traffic_relay.applications.handlers import ( + MessageRelayHandler, PingHandler) +from frontends.gamespy.protocols.game_traffic_relay.contracts.general import MessageRelayRequest from frontends.gamespy.protocols.natneg.aggregations.enums import RequestType from frontends.gamespy.protocols.natneg.contracts.requests import PingRequest @@ -18,18 +23,26 @@ def __init__(self, client: Client, raw_request: bytes) -> None: def _process_raw_request(self) -> None: name = self._raw_request[7] - if name not in RequestType: - self._client.log_debug(f"Request: {name} is not a valid request.") - return - self._requests.append((RequestType(name), self._raw_request)) + if name == RequestType.PING.value: + self._requests.append((RequestType.PING, self._raw_request)) + else: + self._requests.append((RequestType.RELAY_MSG, self._raw_request)) def _create_cmd_handlers( self, name: RequestType, raw_request: bytes - ) -> PingHandler | None: + ) -> CmdHandlerBase: assert isinstance(name, RequestType) assert isinstance(raw_request, bytes) + saved_client = ConnectionListener.get_client_by_ip( + self._client.connection.ip_endpoint) + if saved_client is None: + client = self._client + else: + client = saved_client match name: case RequestType.PING: - return PingHandler(self._client, PingRequest(raw_request)) + return PingHandler(client, PingRequest(raw_request)) + case RequestType.RELAY_MSG: + return MessageRelayHandler(client, MessageRelayRequest(raw_request)) case _: - return None + raise UniSpyException("unable to handle the message") diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/contracts/general.py b/src/frontends/gamespy/protocols/game_traffic_relay/contracts/general.py index 4cb6ef5b3..6d7615f31 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/contracts/general.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/contracts/general.py @@ -1,5 +1,6 @@ from pydantic import BaseModel, UUID4 +from frontends.gamespy.library.abstractions.contracts import RequestBase from frontends.gamespy.protocols.natneg.aggregations.enums import NatClientIndex, NatPortType @@ -22,3 +23,8 @@ class GtrHeartbeat(BaseModel): public_ip_address: str public_port: int client_count: int + + +class MessageRelayRequest(RequestBase): + raw_request: bytes + pass diff --git a/src/frontends/gamespy/protocols/natneg/aggregations/enums.py b/src/frontends/gamespy/protocols/natneg/aggregations/enums.py index b88e8ef72..94c488922 100644 --- a/src/frontends/gamespy/protocols/natneg/aggregations/enums.py +++ b/src/frontends/gamespy/protocols/natneg/aggregations/enums.py @@ -14,7 +14,10 @@ class RequestType(Enum): NATIFY_REQUEST = 12 REPORT = 13 PRE_INIT = 15 - + RELAY_MSG = 100 + """ + unispy custom request type for GTR + """ class NatPortType(Enum): GP = 0 From 14732ab99711cc597788211f64cc1f3d7d02ab42 Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Mon, 20 Oct 2025 06:23:28 +0000 Subject: [PATCH 215/231] Update: add watchdog to restart server when debugging --- src/.vscode/launch.json | 58 ++++++------ .../library/abstractions/server_launcher.py | 2 +- .../library/extentions/debug_helper.py | 90 +++++++++++++++++++ .../chat/applications/server_launcher.py | 6 +- .../applications/server_launcher.py | 6 +- .../applications/server_launcher.py | 6 +- .../natneg/applications/server_launcher.py | 6 +- .../applications/server_launcher.py | 6 +- .../applications/server_launcher.py | 7 +- .../applications/server_launcher.py | 6 +- .../applications/server_launcher.py | 8 +- .../applications/server_launcher.py | 8 +- src/requirements.txt | 3 +- 13 files changed, 159 insertions(+), 53 deletions(-) create mode 100644 src/frontends/gamespy/library/extentions/debug_helper.py diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json index fd4465e67..bf3556f6b 100644 --- a/src/.vscode/launch.json +++ b/src/.vscode/launch.json @@ -42,6 +42,9 @@ "request": "launch", "program": "frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py", "console": "integratedTerminal", + "args": [ + "--debug" + ], "env": { "PYTHONPATH": "${workspaceFolder}", }, @@ -53,6 +56,9 @@ "request": "launch", "program": "frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py", "console": "integratedTerminal", + "args": [ + "--debug" + ], "env": { "PYTHONPATH": "${workspaceFolder}", }, @@ -63,6 +69,9 @@ "type": "debugpy", "request": "launch", "program": "frontends/gamespy/protocols/chat/applications/server_launcher.py", + "args": [ + "--debug" + ], "console": "integratedTerminal", "env": { "PYTHONPATH": "${workspaceFolder}", @@ -75,6 +84,9 @@ "request": "launch", "program": "frontends/gamespy/protocols/game_status/applications/server_launcher.py", "console": "integratedTerminal", + "args": [ + "--debug" + ], "env": { "PYTHONPATH": "${workspaceFolder}", }, @@ -86,6 +98,9 @@ "request": "launch", "program": "frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py", "console": "integratedTerminal", + "args": [ + "--debug" + ], "env": { "PYTHONPATH": "${workspaceFolder}", }, @@ -97,6 +112,9 @@ "request": "launch", "program": "frontends/gamespy/protocols/natneg/applications/server_launcher.py", "console": "integratedTerminal", + "args": [ + "--debug" + ], "env": { "PYTHONPATH": "${workspaceFolder}", }, @@ -108,6 +126,9 @@ "request": "launch", "program": "frontends/gamespy/protocols/query_report/applications/server_launcher.py", "console": "integratedTerminal", + "args": [ + "--debug" + ], "env": { "PYTHONPATH": "${workspaceFolder}", }, @@ -119,6 +140,9 @@ "request": "launch", "program": "frontends/gamespy/protocols/server_browser/applications/server_launcher.py", "console": "integratedTerminal", + "args": [ + "--debug" + ], "env": { "PYTHONPATH": "${workspaceFolder}", }, @@ -130,41 +154,13 @@ "request": "launch", "program": "frontends/gamespy/protocols/web_services/applications/server_launcher.py", "console": "integratedTerminal", + "args": [ + "--debug" + ], "env": { "PYTHONPATH": "${workspaceFolder}", }, "justMyCode": true }, ], - "compounds": [ - { - "name": "pcm_psp", - "configurations": [ - "backend", - "pcm", - "psp" - ] - }, - { - "name": "qr_sb", - "configurations": [ - "backend", - "qr", - "sb" - ] - }, - { - "name": "all_services", - "configurations": [ - "backend", - "psp", - "pcm", - "natneg", - "gs", - "sb", - "qr", - "web" - ] - }, - ] } \ No newline at end of file diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index ed1869a4f..08dd46231 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -11,7 +11,7 @@ from frontends.gamespy.library.abstractions.client import ClientBase from typing import final -VERSION = 0.45 +VERSION = 0.46 _SERVER_FULL_SHORT_NAME_MAPPING = MappingProxyType( { "PresenceConnectionManager": "PCM", diff --git a/src/frontends/gamespy/library/extentions/debug_helper.py b/src/frontends/gamespy/library/extentions/debug_helper.py new file mode 100644 index 000000000..477484e4f --- /dev/null +++ b/src/frontends/gamespy/library/extentions/debug_helper.py @@ -0,0 +1,90 @@ +import argparse +from multiprocessing import Process +import os +import time +from typing import TYPE_CHECKING, Callable +from watchdog.events import FileSystemEventHandler +from watchdog.observers import Observer +if TYPE_CHECKING: + from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase + + +class FileChangeHandler(FileSystemEventHandler): + process: Process | None + monitor_path: str + launch_func: Callable + + def __init__(self, folder_path: str, launch_func: Callable): + assert isinstance(folder_path, str) + super().__init__() + self.process = None + self.monitor_path = os.path.abspath(folder_path) + self.launch_func = launch_func + self.__start_process() + + def __start_process(self): + if self.process is not None: + self.process.terminate() + self.process = Process(target=self.launch_func) + self.process.start() + + def on_modified(self, event): + assert isinstance(event.src_path, str) + abs_event_path = os.path.abspath(event.src_path) + if abs_event_path.endswith('.py'): + common_path = os.path.commonpath( + [abs_event_path, self.monitor_path]) + if common_path == self.monitor_path: + print( + f"File {event.src_path} has been modified. Restarting process...") + self.__start_process() + + +class DebugHelper: + _observer: object + _folder_path: str + _launch_cls: type["ServerLauncherBase"] + + def __init__(self, folder_path: str, launch_cls: type["ServerLauncherBase"]) -> None: + self._folder_path = folder_path + self._launch_cls = launch_cls + + def start(self): + # Initialize the ArgumentParser + parser = argparse.ArgumentParser( + description='Example script to demonstrate argparse with debug mode.') + + # Add a debug argument + parser.add_argument( + '--debug', + action='store_true', + help='Enable debugging mode.' + ) + + # Parse the arguments + args = parser.parse_args() + + # Check if debug mode is enabled + if args.debug: + print( + "\033[93mUniSpy is starting with watchdog, any changes in code will restart the server\033[0m") + self.__start_with_watch() + else: + self.__start_normal() + + def __start_with_watch(self): + event_handler = FileChangeHandler( + self._folder_path, self.__start_normal) + self._observer = Observer() + self._observer.schedule( + event_handler, self._folder_path, recursive=True) + try: + self._observer.start() + while True: + time.sleep(1) + except: + self._observer.stop() + + def __start_normal(self): + target = self._launch_cls() # type: ignore + target.start() # type: ignore diff --git a/src/frontends/gamespy/protocols/chat/applications/server_launcher.py b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py index 3d373dba0..8c82fa2db 100644 --- a/src/frontends/gamespy/protocols/chat/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py @@ -14,5 +14,7 @@ def __init__(self) -> None: if __name__ == "__main__": - s = ServerLauncher() - s.start() + from frontends.gamespy.library.extentions.debug_helper import DebugHelper + helper = DebugHelper( + "./frontends/gamespy/protocols/chat", ServerLauncher) + helper.start() diff --git a/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py index 36da1e0a0..8a455257c 100644 --- a/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py @@ -16,5 +16,7 @@ def __init__(self) -> None: if __name__ == "__main__": - s = ServerLauncher() - s.start() + from frontends.gamespy.library.extentions.debug_helper import DebugHelper + helper = DebugHelper( + "./frontends/gamespy/protocols/game_status", ServerLauncher) + helper.start() \ No newline at end of file diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py index 87c1dc243..f94384d9f 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py @@ -73,5 +73,7 @@ def __check_expired_connection(self): if __name__ == "__main__": - s = ServerLauncher() - s.start() + from frontends.gamespy.library.extentions.debug_helper import DebugHelper + helper = DebugHelper( + "./frontends/gamespy/protocols/game_traffic_relay", ServerLauncher) + helper.start() diff --git a/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py b/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py index f82ea8469..8640ab88a 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py @@ -15,5 +15,7 @@ def __init__(self) -> None: if __name__ == "__main__": - s = ServerLauncher() - s.start() + from frontends.gamespy.library.extentions.debug_helper import DebugHelper + helper = DebugHelper( + "./frontends/gamespy/protocols/natneg", ServerLauncher) + helper.start() diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py index 4a71c2fc1..f309c5e18 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py @@ -15,5 +15,7 @@ def __init__(self) -> None: if __name__ == "__main__": - s = ServerLauncher() - s.start() + from frontends.gamespy.library.extentions.debug_helper import DebugHelper + helper = DebugHelper( + "./frontends/gamespy/protocols/presence_connection_manager", ServerLauncher) + helper.start() diff --git a/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py b/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py index 749e6aaa5..4db76a387 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py @@ -15,5 +15,8 @@ def __init__(self) -> None: if __name__ == "__main__": - s = ServerLauncher() - s.start() + from frontends.gamespy.library.extentions.debug_helper import DebugHelper + helper = DebugHelper( + "./frontends/gamespy/protocols/presence_search_player", ServerLauncher) + helper.start() + diff --git a/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py b/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py index 4410e59aa..03bbb4ecb 100644 --- a/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py @@ -15,5 +15,7 @@ def __init__(self) -> None: if __name__ == "__main__": - s = ServerLauncher() - s.start() + from frontends.gamespy.library.extentions.debug_helper import DebugHelper + helper = DebugHelper( + "./frontends/gamespy/protocols/query_report", ServerLauncher) + helper.start() diff --git a/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py b/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py index 6f94c6513..259fa858a 100644 --- a/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py @@ -9,8 +9,10 @@ def __init__(self) -> None: config_name="ServerBrowserV2", client_cls=Client, server_cls=TcpServer ) + if __name__ == "__main__": - v2 = ServerLauncher() - v2.start() + from frontends.gamespy.library.extentions.debug_helper import DebugHelper + helper = DebugHelper( + "./frontends/gamespy/protocols/server_browser", ServerLauncher) + helper.start() # todo: add v1 server here - diff --git a/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py b/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py index 5cd286a4f..48fe6c6f6 100644 --- a/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py @@ -11,7 +11,9 @@ def __init__(self) -> None: config_name="WebServices", client_cls=Client, server_cls=HttpServer ) -if __name__ == "__main__": - s = ServerLauncher() - s.start() +if __name__ == "__main__": + from frontends.gamespy.library.extentions.debug_helper import DebugHelper + helper = DebugHelper( + "./frontends/gamespy/protocols/web_services", ServerLauncher) + helper.start() diff --git a/src/requirements.txt b/src/requirements.txt index 28d2ef112..5e3e5a2fc 100644 --- a/src/requirements.txt +++ b/src/requirements.txt @@ -9,4 +9,5 @@ xmltodict == 0.14.2 responses == 0.25.3 redis == 5.2.0 websockets == 15.0.1 -schedule \ No newline at end of file +schedule == 1.2.2 +watchdog == 6.0.0 \ No newline at end of file From ca29530f066f987abe83881b672bb87633d5a20b Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Mon, 20 Oct 2025 07:10:49 +0000 Subject: [PATCH 216/231] Fix: log file have unreadable formatter issue --- src/frontends/gamespy/library/log/log_manager.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/frontends/gamespy/library/log/log_manager.py b/src/frontends/gamespy/library/log/log_manager.py index 586575d56..e75592d3a 100644 --- a/src/frontends/gamespy/library/log/log_manager.py +++ b/src/frontends/gamespy/library/log/log_manager.py @@ -76,15 +76,19 @@ def create(logger_name: str) -> "LogWriter": backupCount=7, encoding="utf-8", ) - formatter = ColoredFormatter( + formater = logging.Formatter( f"%(asctime)s [{logger_name}] [%(levelname)s]: %(message)s", datefmt="%Y-%m-%d %H:%M:%S", ) - file_handler.setFormatter(formatter) + color_formatter = ColoredFormatter( + f"%(asctime)s [{logger_name}] [%(levelname)s]: %(message)s", + datefmt="%Y-%m-%d %H:%M:%S", + ) + file_handler.setFormatter(formater) # create console log handler console_handler = logging.StreamHandler() - console_handler.setFormatter(formatter) + console_handler.setFormatter(color_formatter) # create logger logger = logging.getLogger(logger_name) logger.addHandler(file_handler) From ca62ff70bdf601b8d57c9e97aa2f2a35e71fae3a Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Tue, 21 Oct 2025 09:00:35 +0000 Subject: [PATCH 217/231] Refactor: server launcher --- src/frontends/app.py | 32 ++++ .../library/abstractions/connections.py | 3 + .../gamespy/library/abstractions/handler.py | 28 ++-- .../library/abstractions/server_launcher.py | 146 ++++++++++-------- .../library/extentions/debug_helper.py | 11 +- .../protocols/chat/applications/handlers.py | 69 +++++---- .../chat/applications/server_launcher.py | 6 +- .../protocols/chat/applications/switcher.py | 5 +- .../protocols/chat/contracts/requests.py | 25 ++- .../applications/server_launcher.py | 8 +- .../applications/server_launcher.py | 16 +- .../natneg/abstractions/contracts.py | 28 ++-- .../natneg/applications/server_launcher.py | 5 +- .../protocols/natneg/contracts/responses.py | 15 +- .../protocols/natneg/contracts/results.py | 1 - .../applications/server_launcher.py | 5 +- .../applications/server_launcher.py | 8 +- .../applications/server_launcher.py | 9 +- .../applications/server_launcher.py | 5 +- .../applications/server_launcher.py | 11 +- 20 files changed, 264 insertions(+), 172 deletions(-) create mode 100644 src/frontends/app.py diff --git a/src/frontends/app.py b/src/frontends/app.py new file mode 100644 index 000000000..c9870cd2d --- /dev/null +++ b/src/frontends/app.py @@ -0,0 +1,32 @@ + + +from frontends.gamespy.library.extentions.debug_helper import DebugHelper + + +if __name__ == "__main__": + from frontends.gamespy.protocols.chat.applications.server_launcher import ServerLauncher as chat + from frontends.gamespy.protocols.game_status.applications.server_launcher import ServerLauncher as gs + from frontends.gamespy.protocols.game_traffic_relay.applications.server_launcher import ServerLauncher as gtr + from frontends.gamespy.protocols.natneg.applications.server_launcher import ServerLauncher as nn + from frontends.gamespy.protocols.presence_connection_manager.applications.server_launcher import ServerLauncher as pcm + from frontends.gamespy.protocols.presence_search_player.applications.server_launcher import ServerLauncher as psp + from frontends.gamespy.protocols.query_report.applications.server_launcher import ServerLauncher as qr + from frontends.gamespy.protocols.server_browser.applications.server_launcher import ServerLauncher as sb + from frontends.gamespy.protocols.web_services.applications.server_launcher import ServerLauncher as web + + from frontends.gamespy.library.abstractions.server_launcher import ServerFactory + launchers = [ + chat(), + gs(), + gtr(), + nn(), + pcm(), + psp(), + qr(), + sb(), + web() + ] + + factory = ServerFactory(launchers) + helper = DebugHelper("./frontends", factory) + helper.start() diff --git a/src/frontends/gamespy/library/abstractions/connections.py b/src/frontends/gamespy/library/abstractions/connections.py index 70c14ac99..20c5ab125 100644 --- a/src/frontends/gamespy/library/abstractions/connections.py +++ b/src/frontends/gamespy/library/abstractions/connections.py @@ -90,6 +90,9 @@ def __init__( self._logger = logger def start(self): + """ + server non-blocking start + """ thread = threading.Thread(target=self._server.serve_forever) thread.start() diff --git a/src/frontends/gamespy/library/abstractions/handler.py b/src/frontends/gamespy/library/abstractions/handler.py index 9b1eddd8a..5355fa1f9 100644 --- a/src/frontends/gamespy/library/abstractions/handler.py +++ b/src/frontends/gamespy/library/abstractions/handler.py @@ -132,27 +132,25 @@ def _upload_data(self): raise UniSpyException( f"failed to upload data to backends. reason: {response.text}" ) - # match response.status_code: - # case 200: - # pass - # case 450: - # self._handle_upload_error() - # case 500: - # self._handle_upload_error() - # case _: - # raise UniSpyException( - # f"failed to upload data to backends. reason: {response.text}" - # ) - - if "error" in self._http_result: - self._handle_upload_error() + + match response.status_code: + case 200: + pass + case 450: + self._handle_upload_error() + case 500: + self._handle_upload_error() + case _: + raise UniSpyException( + f"failed to upload data to backends. reason: {response.text}" + ) def _handle_upload_error(self): """ handle the error message response from backend """ # we raise the error as UniSpyException - raise UniSpyException(self._http_result["error"]) + raise UniSpyException(self._http_result["message"]) @final def _fetch_data(self): diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index 08dd46231..f8df13603 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -31,11 +31,12 @@ class ServerLauncherBase: config: ServerConfig - logger: LogWriter | None - server: NetworkServerBase | None - _conn_checker: Schedular + _config_name: str _server_cls: type[NetworkServerBase] _client_cls: type[ClientBase] + _logger: LogWriter + _server: NetworkServerBase + _available_checker: Schedular def __init__( self, @@ -45,75 +46,41 @@ def __init__( ): assert issubclass(client_cls, ClientBase) assert issubclass(server_cls, NetworkServerBase) + assert isinstance(config_name, str) assert config_name in CONFIG.servers self.config = CONFIG.servers[config_name] - self.server = None - self.logger = None self._server_cls = server_cls self._client_cls = client_cls self._create_logger() + self._create_server() - def start(self): - self._connect_to_backend() - self.__show_unispy_logo() - self._launch_server() - self._launch_heartbeat_schedular() - print("Server successfully launched.") - self._keep_running() - - def __show_unispy_logo(self): - # display logo - print(pyfiglet.Figlet().renderText("UniSpy.Server")) - # display version info - print(f"version {VERSION}") - table = PrettyTable() - table.field_names = [ - "Server Name", - "Listening Address", - "Listening Port", - ] + @final + def _create_logger(self): assert self.config is not None - table.add_row( - [ - self.config.server_name, - self.config.public_address, - self.config.listening_port, - ] - ) - print(table) + short_name = _SERVER_FULL_SHORT_NAME_MAPPING[self.config.server_name] + self._logger = LogManager.create(short_name) @final - def _launch_server(self) -> None: - """ - assign data in child class so the related instance can be created here - """ - assert self.logger is not None + def _create_server(self): + assert self._logger is not None assert self._server_cls is not None assert self._client_cls is not None - self.server = self._server_cls( - self.config, self._client_cls, self.logger) - self.server.start() + self._server = self._server_cls( + self.config, self._client_cls, self._logger) @final - def _heartbeat_to_backend(self, url: str, json_str: str): - """ - send heartbeat to backends - """ - assert isinstance(json_str, str) - self._get_data_from_backends(url, json_str=json_str) + def start(self): + self._server.start() - @final - def _get_data_from_backends(self, url: str, is_post: bool = True, json_str: str | None = None): + @staticmethod + def get_data_from_backends(url: str, json_str: str): try: - if is_post: - # post our server config to backends to register - resp = requests.post(url=url, data=json_str) - else: - resp = requests.get(url=url) + # post our server config to backends to register + resp = requests.post(url=url, data=json_str) data = resp.json() if resp.status_code != 200: - raise UniSpyException(data["error"]) + raise UniSpyException(data["message"]) else: return data except requests.ConnectionError: @@ -121,7 +88,15 @@ def _get_data_from_backends(self, url: str, is_post: bool = True, json_str: str f"backend server: {CONFIG.backend.url} not available." ) - def _connect_to_backend(self): + @final + def _heartbeat_to_backend(self, url: str, json_str: str): + """ + send heartbeat to backends + """ + assert isinstance(json_str, str) + ServerLauncherBase.get_data_from_backends(url, json_str=json_str) + + def connect_to_backend(self): """ check backend availability """ @@ -132,19 +107,65 @@ def _connect_to_backend(self): CONFIG.backend.url, self.config.model_dump_json()) @final - def _launch_heartbeat_schedular(self): + def launch_heartbeat_schedular(self): """ set the schedular to send heartbeat info to backend to keep the infomation update """ #! temperarily use connect to backend function - self._conn_checker = Schedular(self._connect_to_backend, 30) - self._conn_checker.start() + self._available_checker = Schedular(self.connect_to_backend, 30) + self._available_checker.start() + + +class ServerFactory: + _lauchers: list[ServerLauncherBase] + + def __init__( + self, + launchers: list[ServerLauncherBase] + ): + self._lauchers = launchers + + def start(self): + self._connect_to_backend() + self.__show_unispy_logo() + self._launch_servers() + print("Server successfully launched.") + self._keep_running() + + def _connect_to_backend(self): + for info in self._lauchers: + info.connect_to_backend() + info.launch_heartbeat_schedular() + + def __show_unispy_logo(self): + # display logo + print(pyfiglet.Figlet().renderText("UniSpy.Server")) + # display version info + print(f"version {VERSION}") + table = PrettyTable() + table.field_names = [ + "Server Name", + "Listening Address", + "Listening Port", + ] + for info in self._lauchers: + table.add_row( + [ + info.config.server_name, + info.config.public_address, + info.config.listening_port, + ] + ) + print(table) @final - def _create_logger(self): - assert self.config is not None - short_name = _SERVER_FULL_SHORT_NAME_MAPPING[self.config.server_name] - self.logger = LogManager.create(short_name) + def _launch_servers(self) -> None: + """ + assign data in child class so the related instance can be created here + """ + + for info in self._lauchers: + info.start() @final def _keep_running(self): @@ -153,3 +174,4 @@ def _keep_running(self): while True: sleep(1) pass + diff --git a/src/frontends/gamespy/library/extentions/debug_helper.py b/src/frontends/gamespy/library/extentions/debug_helper.py index 477484e4f..fc658bbe0 100644 --- a/src/frontends/gamespy/library/extentions/debug_helper.py +++ b/src/frontends/gamespy/library/extentions/debug_helper.py @@ -6,7 +6,7 @@ from watchdog.events import FileSystemEventHandler from watchdog.observers import Observer if TYPE_CHECKING: - from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase + from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase, ServerFactory class FileChangeHandler(FileSystemEventHandler): @@ -43,11 +43,11 @@ def on_modified(self, event): class DebugHelper: _observer: object _folder_path: str - _launch_cls: type["ServerLauncherBase"] + _factory: "ServerFactory" - def __init__(self, folder_path: str, launch_cls: type["ServerLauncherBase"]) -> None: + def __init__(self, folder_path: str, factory: "ServerFactory") -> None: self._folder_path = folder_path - self._launch_cls = launch_cls + self._factory = factory def start(self): # Initialize the ArgumentParser @@ -86,5 +86,4 @@ def __start_with_watch(self): self._observer.stop() def __start_normal(self): - target = self._launch_cls() # type: ignore - target.start() # type: ignore + self._factory.start() diff --git a/src/frontends/gamespy/protocols/chat/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py index ac210f882..fdbebec69 100644 --- a/src/frontends/gamespy/protocols/chat/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -1,4 +1,5 @@ from frontends.gamespy.library.encryption.gs_encryption import ChatCrypt +from frontends.gamespy.protocols.chat.applications.client import Client from frontends.gamespy.protocols.chat.contracts.results import ( AtmResult, NoticeResult, @@ -53,6 +54,7 @@ AtmRequest, NoticeRequest, PrivateRequest, + PublishMessageRequest, UtmRequest, GetCKeyRequest, GetChannelKeyRequest, @@ -81,7 +83,6 @@ GetUdpRelayRequest, ) from frontends.gamespy.protocols.chat.aggregates.enums import ModeRequestType -from frontends.gamespy.library.abstractions.client import ClientBase from frontends.gamespy.protocols.chat.abstractions.contract import RequestBase from frontends.gamespy.protocols.chat.abstractions.handler import ( ChannelHandlerBase, @@ -95,7 +96,7 @@ class CdKeyHandler(CmdHandlerBase): _request: CdkeyRequest - def __init__(self, client: ClientBase, request: CdkeyRequest): + def __init__(self, client: Client, request: CdkeyRequest): assert isinstance(request, CdkeyRequest) super().__init__(client, request) self._is_fetching = False @@ -108,7 +109,7 @@ class CryptHandler(CmdHandlerBase): _request: CryptRequest _result: CryptResult - def __init__(self, client: ClientBase, request: RequestBase): + def __init__(self, client: Client, request: RequestBase): assert isinstance(request, CryptRequest) super().__init__(client, request) self._result_cls = CryptResult @@ -129,7 +130,7 @@ class GetKeyHandler(CmdHandlerBase): _request: GetKeyRequest _result: GetKeyResult - def __init__(self, client: ClientBase, request: GetKeyRequest): + def __init__(self, client: Client, request: GetKeyRequest): assert isinstance(request, GetKeyRequest) super().__init__(client, request) self._result_cls = GetKeyResult @@ -139,7 +140,7 @@ def __init__(self, client: ClientBase, request: GetKeyRequest): class GetUdpRelayHandler(CmdHandlerBase): _request: GetUdpRelayRequest - def __init__(self, client: ClientBase, request: GetUdpRelayRequest): + def __init__(self, client: Client, request: GetUdpRelayRequest): assert isinstance(request, GetUdpRelayRequest) super().__init__(client, request) @@ -147,7 +148,7 @@ def __init__(self, client: ClientBase, request: GetUdpRelayRequest): class InviteHandler(CmdHandlerBase): _request: InviteRequest - def __init__(self, client: ClientBase, request: InviteRequest): + def __init__(self, client: Client, request: InviteRequest): assert isinstance(request, InviteRequest) super().__init__(client, request) @@ -157,7 +158,7 @@ class ListHandler(PostLoginHandlerBase): _result: ListResult _result_cls: type[ListResult] - def __init__(self, client: ClientBase, request: ListRequest): + def __init__(self, client: Client, request: ListRequest): assert isinstance(request, ListRequest) super().__init__(client, request) self._result_cls = ListResult @@ -168,7 +169,7 @@ class LoginHandler(CmdHandlerBase): _request: LoginRequest _result: LoginResult - def __init__(self, client: ClientBase, request: LoginRequest): + def __init__(self, client: Client, request: LoginRequest): assert isinstance(request, LoginRequest) super().__init__(client, request) self._result_cls = LoginResult @@ -180,7 +181,7 @@ class NickHandler(CmdHandlerBase): _result: NickResult _result_cls: type[NickResult] - def __init__(self, client: ClientBase, request: NickRequest): + def __init__(self, client: Client, request: NickRequest): assert isinstance(request, NickRequest) super().__init__(client, request) self._result_cls = NickResult @@ -195,7 +196,7 @@ class PingHandler(CmdHandlerBase): _request: PingRequest _result: PingResult - def __init__(self, client: ClientBase, request: PingRequest): + def __init__(self, client: Client, request: PingRequest): assert isinstance(request, PingRequest) super().__init__(client, request) self._result_cls = PingResult @@ -205,7 +206,7 @@ def __init__(self, client: ClientBase, request: PingRequest): class QuitHandler(CmdHandlerBase): _request: QuitRequest - def __init__(self, client: ClientBase, request: QuitRequest): + def __init__(self, client: Client, request: QuitRequest): assert isinstance(request, QuitRequest) super().__init__(client, request) self._is_fetching = False @@ -214,7 +215,7 @@ def __init__(self, client: ClientBase, request: QuitRequest): class SetKeyHandler(PostLoginHandlerBase): _request: SetKeyRequest - def __init__(self, client: ClientBase, request: SetKeyRequest): + def __init__(self, client: Client, request: SetKeyRequest): assert isinstance(request, SetKeyRequest) super().__init__(client, request) @@ -222,7 +223,7 @@ def __init__(self, client: ClientBase, request: SetKeyRequest): class UserHandler(CmdHandlerBase): _request: UserRequest - def __init__(self, client: ClientBase, request: UserRequest): + def __init__(self, client: Client, request: UserRequest): assert isinstance(request, UserRequest) super().__init__(client, request) self._is_fetching = False @@ -236,7 +237,7 @@ class UserIPHandler(CmdHandlerBase): _request: UserIPRequest _result: UserIPResult - def __init__(self, client: ClientBase, request: UserIPRequest): + def __init__(self, client: Client, request: UserIPRequest): assert isinstance(request, UserIPRequest) super().__init__(client, request) self._is_fetching = False @@ -258,7 +259,7 @@ class WhoHandler(CmdHandlerBase): _request: WhoRequest _result: WhoResult - def __init__(self, client: ClientBase, request: WhoRequest): + def __init__(self, client: Client, request: WhoRequest): assert isinstance(request, WhoRequest) super().__init__(client, request) self._result_cls = WhoResult @@ -269,7 +270,7 @@ class WhoIsHandler(CmdHandlerBase): _request: WhoIsRequest _result: WhoIsResult - def __init__(self, client: ClientBase, request: WhoIsRequest): + def __init__(self, client: Client, request: WhoIsRequest): assert isinstance(request, WhoIsRequest) super().__init__(client, request) self._result_cls = WhoIsResult @@ -283,7 +284,7 @@ class GetChannelKeyHandler(ChannelHandlerBase): _request: GetChannelKeyRequest _result: GetChannelKeyResult - def __init__(self, client: ClientBase, request: GetChannelKeyRequest): + def __init__(self, client: Client, request: GetChannelKeyRequest): assert isinstance(request, GetChannelKeyRequest) super().__init__(client, request) @@ -298,7 +299,7 @@ class GetCKeyHandler(ChannelHandlerBase): _request: GetCKeyRequest _result: GetCKeyResult - def __init__(self, client: ClientBase, request: GetCKeyRequest): + def __init__(self, client: Client, request: GetCKeyRequest): assert isinstance(request, GetCKeyRequest) super().__init__(client, request) self._result_cls = GetCKeyResult @@ -309,7 +310,7 @@ class JoinHandler(ChannelHandlerBase): _request: JoinRequest _result: JoinResult - def __init__(self, client: ClientBase, request: JoinRequest): + def __init__(self, client: Client, request: JoinRequest): assert isinstance(request, JoinRequest) super().__init__(client, request) self._is_broadcast = True @@ -334,7 +335,7 @@ class KickHandler(ChannelHandlerBase): _request: KickRequest _result: KickResult - def __init__(self, client: ClientBase, request: KickRequest): + def __init__(self, client: Client, request: KickRequest): assert isinstance(request, KickRequest) super().__init__(client, request) self._result_cls = KickResult @@ -345,7 +346,7 @@ class ModeHandler(ChannelHandlerBase): _request: ModeRequest _result: ModeResult - def __init__(self, client: ClientBase, request: ModeRequest): + def __init__(self, client: Client, request: ModeRequest): assert isinstance(request, ModeRequest) super().__init__(client, request) self._result_cls = ModeResult @@ -370,7 +371,7 @@ class NamesHandler(ChannelHandlerBase): _request: NamesRequest _result: NamesResult - def __init__(self, client: ClientBase, request: NamesRequest): + def __init__(self, client: Client, request: NamesRequest): assert isinstance(request, NamesRequest) super().__init__(client, request) self._result_cls = NamesResult @@ -381,7 +382,7 @@ class PartHandler(ChannelHandlerBase): _request: PartRequest _result: PartResult - def __init__(self, client: ClientBase, request: PartRequest): + def __init__(self, client: Client, request: PartRequest): assert isinstance(request, PartRequest) super().__init__(client, request) self._result_cls = PartResult @@ -392,7 +393,7 @@ class SetChannelKeyHandler(ChannelHandlerBase): _request: SetChannelKeyRequest _result: SetChannelKeyResult - def __init__(self, client: ClientBase, request: SetChannelKeyRequest): + def __init__(self, client: Client, request: SetChannelKeyRequest): assert isinstance(request, SetChannelKeyRequest) super().__init__(client, request) self._result_cls = SetChannelKeyResult @@ -403,7 +404,7 @@ def __init__(self, client: ClientBase, request: SetChannelKeyRequest): class SetCKeyHandler(ChannelHandlerBase): _request: SetCKeyRequest - def __init__(self, client: ClientBase, request: SetCKeyRequest): + def __init__(self, client: Client, request: SetCKeyRequest): assert isinstance(request, SetCKeyRequest) super().__init__(client, request) self._result_cls = SetCKeyResult @@ -414,7 +415,7 @@ class TopicHandler(ChannelHandlerBase): _request: TopicRequest _result: TopicResult - def __init__(self, client: ClientBase, request: TopicRequest): + def __init__(self, client: Client, request: TopicRequest): assert isinstance(request, TopicRequest) super().__init__(client, request) self._result_cls = TopicResult @@ -428,7 +429,7 @@ class ATMHandler(MessageHandlerBase): _request: AtmRequest _result: AtmResult - def __init__(self, client: ClientBase, request: AtmRequest): + def __init__(self, client: Client, request: AtmRequest): super().__init__(client, request) assert isinstance(request, AtmRequest) self._result_cls = AtmResult @@ -439,7 +440,7 @@ class UTMHandler(MessageHandlerBase): _request: UtmRequest _result: UtmResult - def __init__(self, client: ClientBase, request: UtmRequest): + def __init__(self, client: Client, request: UtmRequest): assert isinstance(request, UtmRequest) super().__init__(client, request) self._result_cls = UtmResult @@ -450,7 +451,7 @@ class NoticeHandler(MessageHandlerBase): _request: NoticeRequest _result: NoticeResult - def __init__(self, client: ClientBase, request: NoticeRequest): + def __init__(self, client: Client, request: NoticeRequest): assert isinstance(request, NoticeRequest) super().__init__(client, request) self._result_cls = NoticeResult @@ -461,8 +462,16 @@ class PrivateHandler(MessageHandlerBase): _request: PrivateRequest _result: PrivateResult - def __init__(self, client: ClientBase, request: PrivateRequest): + def __init__(self, client: Client, request: PrivateRequest): assert isinstance(request, PrivateRequest) super().__init__(client, request) self._result_cls = PrivateResult self._response_cls = PrivateResponse + + +# region publish message handler +class PublishMessageHandler(CmdHandlerBase): + _request: PublishMessageRequest + + def __init__(self, client: Client, request: RequestBase): + super().__init__(client, request) \ No newline at end of file diff --git a/src/frontends/gamespy/protocols/chat/applications/server_launcher.py b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py index 8c82fa2db..31fdfd125 100644 --- a/src/frontends/gamespy/protocols/chat/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py @@ -1,4 +1,4 @@ -from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase, ServerFactory from frontends.gamespy.library.network.tcp_handler import TcpServer from frontends.gamespy.protocols.chat.applications.client import Client @@ -15,6 +15,8 @@ def __init__(self) -> None: if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper + + chat = ServerLauncher() helper = DebugHelper( - "./frontends/gamespy/protocols/chat", ServerLauncher) + "./frontends/gamespy/protocols/chat", ServerFactory([chat])) helper.start() diff --git a/src/frontends/gamespy/protocols/chat/applications/switcher.py b/src/frontends/gamespy/protocols/chat/applications/switcher.py index 6749418ef..e55e78294 100644 --- a/src/frontends/gamespy/protocols/chat/applications/switcher.py +++ b/src/frontends/gamespy/protocols/chat/applications/switcher.py @@ -1,4 +1,5 @@ +from typing import TYPE_CHECKING from frontends.gamespy.library.abstractions.client import ClientBase from frontends.gamespy.library.abstractions.handler import CmdHandlerBase from frontends.gamespy.library.abstractions.switcher import SwitcherBase @@ -62,10 +63,12 @@ UTMHandler, ) +from frontends.gamespy.protocols.chat.applications.client import Client + class Switcher(SwitcherBase): _raw_request: str - _client: ClientBase + _client: Client def __init__(self, client: ClientBase, raw_request: str) -> None: assert isinstance(raw_request, str) diff --git a/src/frontends/gamespy/protocols/chat/contracts/requests.py b/src/frontends/gamespy/protocols/chat/contracts/requests.py index ef60453cc..e0601f4cf 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/requests.py +++ b/src/frontends/gamespy/protocols/chat/contracts/requests.py @@ -133,7 +133,7 @@ def parse(self): profile_nick_index = self._long_param.index("@") self.nick_name = self._long_param[0:profile_nick_index] - self.email = self._long_param[profile_nick_index + 1 :] + self.email = self._long_param[profile_nick_index + 1:] return self.request_type = LoginRequestType.UNIQUE_NICK_LOGIN @@ -538,14 +538,16 @@ class SetCKeyRequest(ChannelRequestBase): is_broadcast: bool key_value_string: str key_values: dict[str, str] - + def parse(self) -> None: super().parse() if self._cmd_params is None: - raise ChatException("The cmdParams from SETCKEY request are missing.") + raise ChatException( + "The cmdParams from SETCKEY request are missing.") if self._long_param is None: - raise ChatException("The longParam from SETCKEY request is missing.") + raise ChatException( + "The longParam from SETCKEY request is missing.") self.channel_name = self._cmd_params[0] self.nick_name = self._cmd_params[1] @@ -603,3 +605,18 @@ class PrivateRequest(MessageRequestBase): class UtmRequest(MessageRequestBase): pass + + +# region publish message + +class PublishMessageRequest(RequestBase): + """ + this class is use to send broadcast message to backends + """ + + def parse(self) -> None: + """ + we do not need to parse any irc request, we just send it to backend + """ + pass + # return super().parse() diff --git a/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py index 8a455257c..4be41a5d5 100644 --- a/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py @@ -1,5 +1,5 @@ from frontends.gamespy.protocols.game_status.applications.client import Client -from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServerFactory, ServerLauncherBase from frontends.gamespy.library.network.tcp_handler import TcpServer @@ -14,9 +14,9 @@ def __init__(self) -> None: ) - if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper + gs = ServerLauncher() helper = DebugHelper( - "./frontends/gamespy/protocols/game_status", ServerLauncher) - helper.start() \ No newline at end of file + "./frontends/gamespy/protocols/game_status", ServerFactory([gs])) + helper.start() diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py index f94384d9f..c98f5c1a6 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py @@ -1,5 +1,5 @@ from datetime import datetime, timedelta -from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServerFactory, ServerLauncherBase from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.library.extentions.schedular import Schedular from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER @@ -25,7 +25,8 @@ def __init__(self) -> None: def _get_public_ip(self): url = f"{CONFIG.backend.url}/GameSpy/GameTrafficRelay/get_my_ip" - data = self._get_data_from_backends(url=url, is_post=False) + data = ServerLauncherBase.get_data_from_backends( + url=url, json_str="{}") self._public_ip = data['ip'] def _gtr_heartbeat(self): @@ -41,17 +42,17 @@ def _gtr_heartbeat(self): f"{CONFIG.backend.url}/GameSpy/GameTrafficRelay/heartbeat", req_str ) - def _connect_to_backend(self): + def connect_to_backend(self): """ check backend availability """ - assert self.logger is not None + assert self._logger is not None if CONFIG.unittest.is_collect_request: - self.logger.debug( + self._logger.debug( "CONFIG.unittest.is_collect_request is enabled ignore send heartbeat to backend" ) return - super()._connect_to_backend() + super().connect_to_backend() self._gtr_heartbeat() # region Expire Checker @@ -74,6 +75,7 @@ def __check_expired_connection(self): if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper + gtr = ServerLauncher() helper = DebugHelper( - "./frontends/gamespy/protocols/game_traffic_relay", ServerLauncher) + "./frontends/gamespy/protocols/game_traffic_relay", ServerFactory([gtr])) helper.start() diff --git a/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py b/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py index 89e46b7f3..ab3447117 100644 --- a/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/natneg/abstractions/contracts.py @@ -54,12 +54,12 @@ def __init__(self, result: ResultBase) -> None: super().__init__(result) def build(self) -> None: - data = bytes() - data += MAGIC_DATA - data += self._result.version.to_bytes(1) - data += self._result.packet_type.value.to_bytes(1) - data += self._result.cookie.to_bytes(4) - self.sending_buffer = data + data = bytearray() + data.extend(MAGIC_DATA) + data.append(self._result.version) + data.append(self._result.packet_type.value) + data.extend(self._result.cookie.to_bytes(4)) + self.sending_buffer = bytes(data) class CommonRequestBase(RequestBase): @@ -85,11 +85,11 @@ class CommonResponseBase(ResponseBase): def build(self) -> None: super().build() - data = bytes() - data += self.sending_buffer - data += self._result.port_type.value.to_bytes(1) - data += self._result.client_index.value.to_bytes(1) - data += int(self._result.use_game_port).to_bytes(1) - data += ip_to_4_bytes(self._result.public_ip_addr) - data += self._result.public_port.to_bytes(2) - self.sending_buffer = data + data = bytearray() + data.extend(self.sending_buffer) + data.append(self._result.port_type.value) + data.append(self._result.client_index.value) + data.append(int(self._result.use_game_port)) + data.extend(ip_to_4_bytes(self._result.public_ip_addr)) + data.extend(self._result.public_port.to_bytes(2)) + self.sending_buffer = bytes(data) diff --git a/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py b/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py index 8640ab88a..5b393d109 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py @@ -1,4 +1,4 @@ -from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServerFactory, ServerLauncherBase from frontends.gamespy.library.network.udp_handler import UdpServer from frontends.gamespy.protocols.natneg.applications.client import Client @@ -16,6 +16,7 @@ def __init__(self) -> None: if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper + nn = ServerLauncher() helper = DebugHelper( - "./frontends/gamespy/protocols/natneg", ServerLauncher) + "./frontends/gamespy/protocols/natneg", ServerFactory([nn])) helper.start() diff --git a/src/frontends/gamespy/protocols/natneg/contracts/responses.py b/src/frontends/gamespy/protocols/natneg/contracts/responses.py index 10806166a..49f913fac 100644 --- a/src/frontends/gamespy/protocols/natneg/contracts/responses.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/responses.py @@ -54,10 +54,11 @@ def build(self) -> None: assert self._result.port is not None assert self._result.status is not None super().build() - data = bytes() - data += self.sending_buffer - data += socket.inet_aton(self._result.ip) - data += self._result.port.to_bytes(2) - data += self._result.got_your_data - data += self._result.status.value.to_bytes(1) - self.sending_buffer = data + data = bytearray() + data.extend(self.sending_buffer) + data.extend(socket.inet_aton(self._result.ip)) + data.extend(self._result.port.to_bytes(2)) + # got your data + data.append(1) + data.append(self._result.status.value) + self.sending_buffer = bytes(data) diff --git a/src/frontends/gamespy/protocols/natneg/contracts/results.py b/src/frontends/gamespy/protocols/natneg/contracts/results.py index 6a16db179..f486c20f5 100644 --- a/src/frontends/gamespy/protocols/natneg/contracts/results.py +++ b/src/frontends/gamespy/protocols/natneg/contracts/results.py @@ -12,7 +12,6 @@ class AddressCheckResult(CommonResultBase): class ConnectResult(ResultBase): is_both_client_ready: bool - got_your_data: bytes = bytes([1]) status: ConnectPacketStatus | None ip: str | None port: int | None diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py index f309c5e18..4d78f6691 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py @@ -1,4 +1,4 @@ -from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServerFactory, ServerLauncherBase from frontends.gamespy.library.network.tcp_handler import TcpServer from frontends.gamespy.protocols.presence_connection_manager.applications.client import ( Client, @@ -16,6 +16,7 @@ def __init__(self) -> None: if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper + pcm = ServerLauncher() helper = DebugHelper( - "./frontends/gamespy/protocols/presence_connection_manager", ServerLauncher) + "./frontends/gamespy/protocols/presence_connection_manager", ServerFactory([pcm])) helper.start() diff --git a/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py b/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py index 4db76a387..5d7ddf381 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py @@ -1,4 +1,4 @@ -from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServerFactory, ServerLauncherBase from frontends.gamespy.library.network.tcp_handler import TcpServer from frontends.gamespy.protocols.presence_search_player.applications.client import ( Client, @@ -9,14 +9,14 @@ class ServerLauncher(ServerLauncherBase): def __init__(self) -> None: super().__init__( config_name="PresenceSearchPlayer", - client_cls=Client, + client_cls=Client, server_cls=TcpServer ) if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper + psp = ServerLauncher() helper = DebugHelper( - "./frontends/gamespy/protocols/presence_search_player", ServerLauncher) + "./frontends/gamespy/protocols/presence_search_player", ServerFactory([psp])) helper.start() - diff --git a/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py b/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py index 03bbb4ecb..a56d9f290 100644 --- a/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py @@ -1,4 +1,4 @@ -from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServerFactory, ServerLauncherBase from frontends.gamespy.library.network.udp_handler import UdpServer from frontends.gamespy.protocols.query_report.applications.client import Client @@ -8,14 +8,15 @@ class ServerLauncher(ServerLauncherBase): def __init__(self) -> None: super().__init__( - config_name="QueryReport", - client_cls=Client, + config_name="QueryReport", + client_cls=Client, server_cls=UdpServer ) if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper + qr = ServerLauncher() helper = DebugHelper( - "./frontends/gamespy/protocols/query_report", ServerLauncher) + "./frontends/gamespy/protocols/query_report", ServerFactory([qr])) helper.start() diff --git a/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py b/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py index 259fa858a..7faf83430 100644 --- a/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py @@ -1,4 +1,4 @@ -from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServerFactory, ServerLauncherBase from frontends.gamespy.library.network.tcp_handler import TcpServer from frontends.gamespy.protocols.server_browser.v2.applications.client import Client @@ -12,7 +12,8 @@ def __init__(self) -> None: if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper + sb2 = ServerLauncher() helper = DebugHelper( - "./frontends/gamespy/protocols/server_browser", ServerLauncher) + "./frontends/gamespy/protocols/server_browser", ServerFactory([sb2])) helper.start() # todo: add v1 server here diff --git a/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py b/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py index 48fe6c6f6..758b41971 100644 --- a/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py @@ -1,19 +1,20 @@ -from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServerFactory, ServerLauncherBase from frontends.gamespy.library.network.http_handler import HttpServer from frontends.gamespy.protocols.web_services.applications.client import Client class ServerLauncher(ServerLauncherBase): - server: "HttpServer" - def __init__(self) -> None: super().__init__( - config_name="WebServices", client_cls=Client, server_cls=HttpServer + config_name="WebServices", + client_cls=Client, + server_cls=HttpServer ) if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper + web = ServerLauncher() helper = DebugHelper( - "./frontends/gamespy/protocols/web_services", ServerLauncher) + "./frontends/gamespy/protocols/web_services", ServerFactory([web])) helper.start() From 40ddb9905b250c4d3ff15b9d5a9832abaadd68e4 Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Fri, 31 Oct 2025 01:14:55 +0000 Subject: [PATCH 218/231] Update: add devcontainer host redirection --- src/.devcontainer/devcontainer.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/.devcontainer/devcontainer.json b/src/.devcontainer/devcontainer.json index 464c0b8a4..1278d764e 100644 --- a/src/.devcontainer/devcontainer.json +++ b/src/.devcontainer/devcontainer.json @@ -5,7 +5,8 @@ // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile "runArgs": [ "--network=unispy", - "--name=unispy_server_dev" + "--name=unispy_server_dev", + "--add-host=unispy_backends:127.0.0.1" ], "image": "mcr.microsoft.com/devcontainers/python:1-3.12-bullseye", // Features to add to the dev container. More info: https://containers.dev/features. @@ -25,7 +26,7 @@ 80 ], // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "echo '127.0.0.1 unispy_backends' | sudo tee -a /etc/hosts && pip3 install --user -r requirements.txt", + "postCreateCommand": "sudo tee -a /etc/hosts && pip3 install --user -r requirements.txt", "customizations": { "vscode": { "extensions": [ From 344ed5542f2a1b49699db5238b00755b5189c48f Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Fri, 31 Oct 2025 01:15:50 +0000 Subject: [PATCH 219/231] Update: debug helper to auto restart server when code change --- .../library/abstractions/connections.py | 3 + .../gamespy/library/abstractions/handler.py | 58 +++++++++---- .../library/abstractions/server_launcher.py | 19 ++++- .../protocols/chat/applications/handlers.py | 82 +++++++------------ .../chat/applications/server_launcher.py | 3 +- .../game_status/applications/handlers.py | 30 ++----- .../applications/server_launcher.py | 3 +- .../applications/handlers.py | 3 +- .../applications/server_launcher.py | 3 +- .../protocols/natneg/abstractions/handlers.py | 7 +- .../protocols/natneg/applications/handlers.py | 21 +---- .../natneg/applications/server_launcher.py | 3 +- .../applications/handlers.py | 60 +++++++------- .../applications/server_launcher.py | 3 +- .../applications/handlers.py | 45 ++++------ .../applications/server_launcher.py | 3 +- .../query_report/aggregates/peer_room_info.py | 19 +---- .../applications/server_launcher.py | 3 +- .../query_report/v2/applications/handlers.py | 18 ++-- .../applications/server_launcher.py | 5 +- .../v2/applications/handlers.py | 28 +++---- .../server_browser/v2/contracts/responses.py | 10 ++- .../applications/server_launcher.py | 3 +- .../web_services/applications/switcher.py | 4 +- .../general.py => applications/handlers.py} | 21 ++--- .../general.py => applications/handlers.py} | 8 +- .../modules/sake/applications/handlers.py | 22 +++-- .../gamespy/server_browser/contract_tests.py | 3 +- 28 files changed, 230 insertions(+), 260 deletions(-) rename src/frontends/gamespy/protocols/web_services/modules/auth/{handlers/general.py => applications/handlers.py} (82%) rename src/frontends/gamespy/protocols/web_services/modules/direct2game/{handlers/general.py => applications/handlers.py} (85%) diff --git a/src/frontends/gamespy/library/abstractions/connections.py b/src/frontends/gamespy/library/abstractions/connections.py index 20c5ab125..0b002afb2 100644 --- a/src/frontends/gamespy/library/abstractions/connections.py +++ b/src/frontends/gamespy/library/abstractions/connections.py @@ -98,3 +98,6 @@ def start(self): def __del__(self): self._server.shutdown() + + def stop(self): + self.__del__() diff --git a/src/frontends/gamespy/library/abstractions/handler.py b/src/frontends/gamespy/library/abstractions/handler.py index 5355fa1f9..43ca519c9 100644 --- a/src/frontends/gamespy/library/abstractions/handler.py +++ b/src/frontends/gamespy/library/abstractions/handler.py @@ -1,7 +1,7 @@ import json from frontends.gamespy.library.abstractions.client import ClientBase from frontends.gamespy.library.exceptions.general import UniSpyException -from typing import Type, final +from typing import final import requests from frontends.gamespy.library.configs import CONFIG @@ -20,34 +20,59 @@ class CmdHandlerBase: _client: "ClientBase" _request: "RequestBase" _result: "ResultBase | None" + """ + the result instance, create by annotation in child class + """ _response: "ResponseBase | None" """ - the response instance, initialize as None in __init__ + the response instance, create by annotation in child class + """ + _result_cls: type["ResultBase"] | None + """ + the result class type, create by annotation in child class """ - _result_cls: "Type[ResultBase] | None" + _response_cls: type["ResponseBase"] | None """ - the result type class, use to deserialize json data from backend\n - the initialization of _result_cls must after call super().__init__() + the response class type, create by annotation in child class """ - _response_cls: "Type[ResponseBase]|None" _debug: bool = False """ whether is in debug mode, if in debug mode exception will raise from handler """ _is_uploading: bool _is_fetching: bool + """ + this is auto assigned variable + """ def __init__(self, client: "ClientBase", request: "RequestBase") -> None: assert issubclass(type(client), ClientBase) assert issubclass(type(request), RequestBase) self._client = client self._request = request - self._result_cls = None - self._response_cls = None + # create class type by annotation + self._get_property_types() self._result = None self._response = None self._is_uploading = True - self._is_fetching = True + if self._result_cls is None: + self._is_fetching = False + else: + self._is_fetching = True + + def _get_property_types(self): + if "_result" in self.__class__.__annotations__: + self._result_cls = self.__class__.__annotations__['_result'] + if self._result_cls == ResultBase: + self._result_cls = None + else: + self._result_cls = None + if "_response" in self.__class__.__annotations__: + self._response_cls = self.__class__.__annotations__['_response'] + if self._response_cls == ResponseBase: + self._response_cls = None + else: + self._response_cls = None def handle(self) -> None: try: @@ -76,11 +101,11 @@ def _data_operate(self) -> None: """ virtual function, can be override """ - self._prepare_data() if self._is_uploading: + self._prepare_data() self._upload_data() - if self._is_fetching: - self._fetch_data() + if self._is_fetching: + self._fetch_data() def _prepare_data(self): self._temp_data = self._request.to_dict() @@ -161,6 +186,7 @@ def _fetch_data(self): if self._result_cls is None: raise UniSpyException( "_result_cls can not be null when feach data.") + assert issubclass(self._result_cls, ResultBase) self._client.log_network_fetch(f"[{self._url}] {self._http_result}") if "result" not in self._http_result: @@ -170,10 +196,10 @@ def _fetch_data(self): def _response_construct(self) -> None: """construct response here in specific child class""" if self._response_cls is not None: - assert self._result is not None - self._response = self._response_cls( - result=self._result) - pass + if self._result is None: + raise UniSpyException( + "result instance is required for response construction") + self._response = self._response_cls(result=self._result) def _response_send(self) -> None: """ diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index f8df13603..a8c34075e 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -1,3 +1,4 @@ +from threading import Thread, Event from types import MappingProxyType from frontends.gamespy.library.abstractions.connections import NetworkServerBase @@ -72,6 +73,10 @@ def _create_server(self): def start(self): self._server.start() + @final + def stop(self): + self._server.stop() + @staticmethod def get_data_from_backends(url: str, json_str: str): try: @@ -124,6 +129,7 @@ def __init__( launchers: list[ServerLauncherBase] ): self._lauchers = launchers + self.stop_event = Event() # Event for stopping threads def start(self): self._connect_to_backend() @@ -171,7 +177,12 @@ def _launch_servers(self) -> None: def _keep_running(self): print("Press ctr+c to Quit\n") from time import sleep - while True: - sleep(1) - pass - + try: + while True: + sleep(1) + pass + except KeyboardInterrupt: + self.stop_event.set() + for info in self._lauchers: + info.stop() + print("\nUniSpy shutdown.") diff --git a/src/frontends/gamespy/protocols/chat/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py index fdbebec69..a70177ffa 100644 --- a/src/frontends/gamespy/protocols/chat/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -28,6 +28,7 @@ from frontends.gamespy.protocols.chat.contracts.responses import ( AtmResponse, GetCKeyResponse, + GetChannelKeyResponse, JoinResponse, KickResponse, ModeResponse, @@ -99,7 +100,6 @@ class CdKeyHandler(CmdHandlerBase): def __init__(self, client: Client, request: CdkeyRequest): assert isinstance(request, CdkeyRequest) super().__init__(client, request) - self._is_fetching = False def _response_construct(self) -> None: self._response = CdKeyResponse() @@ -129,12 +129,11 @@ def _response_send(self) -> None: class GetKeyHandler(CmdHandlerBase): _request: GetKeyRequest _result: GetKeyResult + _response: GetKeyResponse def __init__(self, client: Client, request: GetKeyRequest): assert isinstance(request, GetKeyRequest) super().__init__(client, request) - self._result_cls = GetKeyResult - self._response_cls = GetKeyResponse class GetUdpRelayHandler(CmdHandlerBase): @@ -156,36 +155,31 @@ def __init__(self, client: Client, request: InviteRequest): class ListHandler(PostLoginHandlerBase): _request: ListRequest _result: ListResult - _result_cls: type[ListResult] + _response: ListResponse def __init__(self, client: Client, request: ListRequest): assert isinstance(request, ListRequest) super().__init__(client, request) - self._result_cls = ListResult - self._response_cls = ListResponse class LoginHandler(CmdHandlerBase): _request: LoginRequest _result: LoginResult + _response: LoginResponse def __init__(self, client: Client, request: LoginRequest): assert isinstance(request, LoginRequest) super().__init__(client, request) - self._result_cls = LoginResult - self._response_cls = LoginResponse class NickHandler(CmdHandlerBase): _request: NickRequest _result: NickResult - _result_cls: type[NickResult] + _response: NickResponse def __init__(self, client: Client, request: NickRequest): assert isinstance(request, NickRequest) super().__init__(client, request) - self._result_cls = NickResult - self._response_cls = NickResponse def _data_operate(self) -> None: super()._data_operate() @@ -195,12 +189,11 @@ def _data_operate(self) -> None: class PingHandler(CmdHandlerBase): _request: PingRequest _result: PingResult + _response: PingResponse def __init__(self, client: Client, request: PingRequest): assert isinstance(request, PingRequest) super().__init__(client, request) - self._result_cls = PingResult - self._response_cls = PingResponse class QuitHandler(CmdHandlerBase): @@ -209,7 +202,6 @@ class QuitHandler(CmdHandlerBase): def __init__(self, client: Client, request: QuitRequest): assert isinstance(request, QuitRequest) super().__init__(client, request) - self._is_fetching = False class SetKeyHandler(PostLoginHandlerBase): @@ -226,7 +218,6 @@ class UserHandler(CmdHandlerBase): def __init__(self, client: Client, request: UserRequest): assert isinstance(request, UserRequest) super().__init__(client, request) - self._is_fetching = False def _data_operate(self) -> None: super()._data_operate() @@ -235,12 +226,11 @@ def _data_operate(self) -> None: class UserIPHandler(CmdHandlerBase): _request: UserIPRequest - _result: UserIPResult + _response: UserIPResponse def __init__(self, client: Client, request: UserIPRequest): assert isinstance(request, UserIPRequest) super().__init__(client, request) - self._is_fetching = False def _request_check(self) -> None: super()._request_check() @@ -251,30 +241,25 @@ def _data_operate(self) -> None: self._result = UserIPResult( remote_ip=self._client.connection.remote_ip) - def _response_construct(self) -> None: - self._response = UserIPResponse(self._result) - class WhoHandler(CmdHandlerBase): _request: WhoRequest _result: WhoResult + _response: WhoResponse def __init__(self, client: Client, request: WhoRequest): assert isinstance(request, WhoRequest) super().__init__(client, request) - self._result_cls = WhoResult - self._response_cls = WhoResponse class WhoIsHandler(CmdHandlerBase): _request: WhoIsRequest _result: WhoIsResult + _response: WhoIsResponse def __init__(self, client: Client, request: WhoIsRequest): assert isinstance(request, WhoIsRequest) super().__init__(client, request) - self._result_cls = WhoIsResult - self._response_cls = WhoIsResponse # region channel @@ -283,6 +268,7 @@ def __init__(self, client: Client, request: WhoIsRequest): class GetChannelKeyHandler(ChannelHandlerBase): _request: GetChannelKeyRequest _result: GetChannelKeyResult + _response: GetChannelKeyResponse def __init__(self, client: Client, request: GetChannelKeyRequest): assert isinstance(request, GetChannelKeyRequest) @@ -298,24 +284,22 @@ def _update_channel_cache(self): class GetCKeyHandler(ChannelHandlerBase): _request: GetCKeyRequest _result: GetCKeyResult + _response: GetCKeyResponse def __init__(self, client: Client, request: GetCKeyRequest): assert isinstance(request, GetCKeyRequest) super().__init__(client, request) - self._result_cls = GetCKeyResult - self._response_cls = GetCKeyResponse class JoinHandler(ChannelHandlerBase): _request: JoinRequest _result: JoinResult + _response: JoinResponse def __init__(self, client: Client, request: JoinRequest): assert isinstance(request, JoinRequest) super().__init__(client, request) self._is_broadcast = True - self._result_cls = JoinResult - self._response_cls = JoinResponse def _response_send(self) -> None: super()._response_send() @@ -334,23 +318,21 @@ def _response_send(self) -> None: class KickHandler(ChannelHandlerBase): _request: KickRequest _result: KickResult + _response: KickResponse def __init__(self, client: Client, request: KickRequest): assert isinstance(request, KickRequest) super().__init__(client, request) - self._result_cls = KickResult - self._response_cls = KickResponse class ModeHandler(ChannelHandlerBase): _request: ModeRequest _result: ModeResult + _response: ModeResponse def __init__(self, client: Client, request: ModeRequest): assert isinstance(request, ModeRequest) super().__init__(client, request) - self._result_cls = ModeResult - self._response_cls = ModeResponse def _request_check(self) -> None: super()._request_check() @@ -370,56 +352,53 @@ def _response_construct(self): class NamesHandler(ChannelHandlerBase): _request: NamesRequest _result: NamesResult + _response: NamesResponse def __init__(self, client: Client, request: NamesRequest): assert isinstance(request, NamesRequest) super().__init__(client, request) - self._result_cls = NamesResult - self._response_cls = NamesResponse class PartHandler(ChannelHandlerBase): _request: PartRequest _result: PartResult + _response: PartResponse def __init__(self, client: Client, request: PartRequest): assert isinstance(request, PartRequest) super().__init__(client, request) - self._result_cls = PartResult - self._response_cls = PartResponse class SetChannelKeyHandler(ChannelHandlerBase): _request: SetChannelKeyRequest _result: SetChannelKeyResult + _response: SetChannelKeyResponse def __init__(self, client: Client, request: SetChannelKeyRequest): assert isinstance(request, SetChannelKeyRequest) super().__init__(client, request) - self._result_cls = SetChannelKeyResult - self._response_cls = SetChannelKeyResponse self._is_broadcast = True class SetCKeyHandler(ChannelHandlerBase): _request: SetCKeyRequest + _result: SetCKeyResult + _response: SetCKeyResponse def __init__(self, client: Client, request: SetCKeyRequest): assert isinstance(request, SetCKeyRequest) super().__init__(client, request) - self._result_cls = SetCKeyResult - self._response_cls = SetCKeyResponse class TopicHandler(ChannelHandlerBase): _request: TopicRequest _result: TopicResult + _response: TopicResponse def __init__(self, client: Client, request: TopicRequest): assert isinstance(request, TopicRequest) super().__init__(client, request) - self._result_cls = TopicResult - self._response_cls = TopicResponse + self._is_broadcast = True # region Message @@ -428,50 +407,47 @@ def __init__(self, client: Client, request: TopicRequest): class ATMHandler(MessageHandlerBase): _request: AtmRequest _result: AtmResult + _response: AtmResponse def __init__(self, client: Client, request: AtmRequest): - super().__init__(client, request) assert isinstance(request, AtmRequest) - self._result_cls = AtmResult - self._response_cls = AtmResponse + super().__init__(client, request) class UTMHandler(MessageHandlerBase): _request: UtmRequest _result: UtmResult + _response: UtmResponse def __init__(self, client: Client, request: UtmRequest): assert isinstance(request, UtmRequest) super().__init__(client, request) - self._result_cls = UtmResult - self._response_cls = UtmResponse class NoticeHandler(MessageHandlerBase): _request: NoticeRequest _result: NoticeResult + _response: NoticeResponse def __init__(self, client: Client, request: NoticeRequest): assert isinstance(request, NoticeRequest) super().__init__(client, request) - self._result_cls = NoticeResult - self._response_cls = NoticeResponse class PrivateHandler(MessageHandlerBase): _request: PrivateRequest _result: PrivateResult + _response: PrivateResponse def __init__(self, client: Client, request: PrivateRequest): assert isinstance(request, PrivateRequest) super().__init__(client, request) - self._result_cls = PrivateResult - self._response_cls = PrivateResponse # region publish message handler class PublishMessageHandler(CmdHandlerBase): _request: PublishMessageRequest - def __init__(self, client: Client, request: RequestBase): - super().__init__(client, request) \ No newline at end of file + def __init__(self, client: Client, request: PublishMessageRequest): + assert isinstance(request, PublishMessageRequest) + super().__init__(client, request) diff --git a/src/frontends/gamespy/protocols/chat/applications/server_launcher.py b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py index 31fdfd125..47c06d06f 100644 --- a/src/frontends/gamespy/protocols/chat/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py @@ -17,6 +17,5 @@ def __init__(self) -> None: from frontends.gamespy.library.extentions.debug_helper import DebugHelper chat = ServerLauncher() - helper = DebugHelper( - "./frontends/gamespy/protocols/chat", ServerFactory([chat])) + helper = DebugHelper("./frontends/", ServerFactory([chat])) helper.start() diff --git a/src/frontends/gamespy/protocols/game_status/applications/handlers.py b/src/frontends/gamespy/protocols/game_status/applications/handlers.py index 3131e38d8..db9a365ad 100644 --- a/src/frontends/gamespy/protocols/game_status/applications/handlers.py +++ b/src/frontends/gamespy/protocols/game_status/applications/handlers.py @@ -12,8 +12,6 @@ class AuthGameHandler(CmdHandlerBase): def __init__(self, client: Client, request: AuthGameRequest) -> None: assert isinstance(request, AuthGameRequest) super().__init__(client, request) - self._result_cls = AuthGameResult - self._response_cls = AuthGameResponse def _response_construct(self) -> None: self._client.info.session_key = self._result.session_key @@ -22,53 +20,45 @@ def _response_construct(self) -> None: class AuthPlayerHandler(CmdHandlerBase): - _result_cls: type[AuthPlayerResult] + _request: AuthPlayerRequest + _result: AuthPlayerResult + _response: AuthPlayerResponse def __init__(self, client: Client, request: AuthPlayerRequest) -> None: assert isinstance(request, AuthPlayerRequest) super().__init__(client, request) - self._result_cls = AuthPlayerResult - self._response_cls = AuthPlayerResponse - class GetPlayerDataHandler(CmdHandlerBase): - _result_cls: type[GetPlayerDataResult] + _result: GetPlayerDataResult + _response: GetPlayerDataResponse def __init__(self, client: Client, request: GetPlayerDataRequest) -> None: assert isinstance(request, GetPlayerDataRequest) super().__init__(client, request) - self._result_cls = GetPlayerDataResult - self._response_cls = GetPlayerDataResponse - - class GetProfileIdHandler(CmdHandlerBase): - _result_cls: type[GetProfileIdResult] + _result: GetProfileIdResult + _response: GetProfileIdResponse def __init__(self, client: Client, request: GetProfileIdRequest) -> None: assert isinstance(request, GetProfileIdRequest) super().__init__(client, request) - self._result_cls = GetProfileIdResult - self._response_cls = GetProfileIdResponse - class NewGameHandler(CmdHandlerBase): + _request: NewGameRequest + def __init__(self, client: Client, request: NewGameRequest) -> None: assert isinstance(request, NewGameRequest) super().__init__(client, request) - self._is_fetching = False class SetPlayerDataHandler(CmdHandlerBase): def __init__(self, client: Client, request: SetPlayerDataRequest) -> None: assert isinstance(request, SetPlayerDataRequest) super().__init__(client, request) - self._is_fetching = False - - class UpdateGameHandler(CmdHandlerBase): @@ -82,5 +72,3 @@ class UpdateGameHandler(CmdHandlerBase): def __init__(self, client: Client, request: UpdateGameRequest) -> None: assert isinstance(request, UpdateGameRequest) super().__init__(client, request) - self._is_fetching = False - diff --git a/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py index 4be41a5d5..ecd375697 100644 --- a/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py @@ -17,6 +17,5 @@ def __init__(self) -> None: if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper gs = ServerLauncher() - helper = DebugHelper( - "./frontends/gamespy/protocols/game_status", ServerFactory([gs])) + helper = DebugHelper("./frontends/", ServerFactory([gs])) helper.start() diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py index 891cf4476..02057d388 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/handlers.py @@ -15,7 +15,6 @@ class PingHandler(CmdHandlerBase): def __init__(self, client: Client, request: PingRequest) -> None: assert isinstance(request, PingRequest) super().__init__(client, request) - self._is_fetching = False self._is_uploading = False def _data_operate(self) -> None: @@ -65,7 +64,7 @@ class MessageRelayHandler(CmdHandlerBase): def __init__(self, client: ClientBase, request: RequestBase) -> None: super().__init__(client, request) - self._is_fetching = False + self._is_uploading = False def _data_operate(self) -> None: diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py index c98f5c1a6..4cf1f0c92 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py @@ -76,6 +76,5 @@ def __check_expired_connection(self): if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper gtr = ServerLauncher() - helper = DebugHelper( - "./frontends/gamespy/protocols/game_traffic_relay", ServerFactory([gtr])) + helper = DebugHelper("./frontends/", ServerFactory([gtr])) helper.start() diff --git a/src/frontends/gamespy/protocols/natneg/abstractions/handlers.py b/src/frontends/gamespy/protocols/natneg/abstractions/handlers.py index 151636a69..590268c37 100644 --- a/src/frontends/gamespy/protocols/natneg/abstractions/handlers.py +++ b/src/frontends/gamespy/protocols/natneg/abstractions/handlers.py @@ -1,16 +1,19 @@ from frontends.gamespy.protocols.natneg.applications.client import Client -from frontends.gamespy.protocols.natneg.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.natneg.abstractions.contracts import RequestBase, ResponseBase, ResultBase import frontends.gamespy.library.abstractions.handler as lib class CmdHandlerBase(lib.CmdHandlerBase): + _request: RequestBase + _result: ResultBase + _response: ResponseBase + def __init__(self, client: Client, request: RequestBase) -> None: super().__init__(client, request) assert isinstance(client, Client) assert issubclass(type(request), RequestBase) - if __name__ == "__main__": # cmd = CmdHandlerBase(None, None) pass diff --git a/src/frontends/gamespy/protocols/natneg/applications/handlers.py b/src/frontends/gamespy/protocols/natneg/applications/handlers.py index 16b3d6f98..78f1f75b1 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/handlers.py +++ b/src/frontends/gamespy/protocols/natneg/applications/handlers.py @@ -36,7 +36,6 @@ def __init__(self, client: Client, request: AddressCheckRequest) -> None: assert isinstance(client, Client) assert isinstance(request, AddressCheckRequest) super().__init__(client, request) - self._is_fetching = False self._is_uploading = False def _response_construct(self) -> None: @@ -64,7 +63,6 @@ class ConnectAckHandler(CmdHandlerBase): def __init__(self, client: Client, request: ConnectAckRequest) -> None: assert isinstance(request, ConnectAckRequest) super().__init__(client, request) - self._is_fetching = False self._is_uploading = False def _response_construct(self) -> None: @@ -75,13 +73,11 @@ def _response_construct(self) -> None: class ConnectHandler(CmdHandlerBase): _request: ConnectRequest _result: ConnectResult - _result_cls: type[ConnectResult] + _response: ConnectResponse def __init__(self, client: Client, request: ConnectRequest) -> None: assert isinstance(request, ConnectRequest) super().__init__(client, request) - self._result_cls = ConnectResult - self._response_cls = ConnectResponse def _response_construct(self) -> None: if not self._result.is_both_client_ready: @@ -93,15 +89,13 @@ def _response_construct(self) -> None: class ErtAckHandler(CmdHandlerBase): _request: ErtAckRequest - _result: ErtAckResult _response: ErcAckResponse def __init__(self, client: Client, request: ErtAckRequest) -> None: assert isinstance(request, ErtAckRequest) super().__init__(client, request) - self._is_fetching = False - def _response_construct(self) -> None: + def _data_operate(self) -> None: """ Natneg require fast response, so we do not wait for upload data. """ @@ -114,7 +108,6 @@ def _response_construct(self) -> None: use_game_port=self._request.use_game_port, port_type=self._request.port_type ) - self._response = ErcAckResponse(self._result) class InitHandler(CmdHandlerBase): @@ -123,14 +116,13 @@ class InitHandler(CmdHandlerBase): """ _request: InitRequest - _result: InitResult - _response: InitResponse _client: Client + _result: InitResult + # _response: InitResponse def __init__(self, client: Client, request: InitRequest) -> None: assert isinstance(request, InitRequest) super().__init__(client, request) - self._is_fetching = False def _response_construct(self): """ @@ -181,13 +173,10 @@ def _invoke_connect(self) -> None: class NatifyHandler(CmdHandlerBase): _request: NatifyRequest - _result: NatifyResult - _response: NatifyResponse def __init__(self, client: Client, request: NatifyRequest) -> None: assert isinstance(request, NatifyRequest) super().__init__(client, request) - self._is_fetching = False self._is_uploading = False def _response_construct(self): @@ -208,9 +197,7 @@ def _response_construct(self): class ReportHandler(CmdHandlerBase): _request: ReportRequest - _result: ReportResult def __init__(self, client: Client, request: ReportRequest) -> None: assert isinstance(request, ReportRequest) super().__init__(client, request) - self._is_fetching = False diff --git a/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py b/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py index 5b393d109..317fc73e5 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py @@ -17,6 +17,5 @@ def __init__(self) -> None: if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper nn = ServerLauncher() - helper = DebugHelper( - "./frontends/gamespy/protocols/natneg", ServerFactory([nn])) + helper = DebugHelper("./frontends/", ServerFactory([nn])) helper.start() diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py index 0396cb434..1517c3ad5 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py @@ -62,13 +62,14 @@ # region General +@final class KeepAliveHandler(CmdHandlerBase): _request: KeepAliveRequest - def __init__(self, client: "Client", request: KeepAliveRequest) -> None: + def __init__(self, client: Client, request: KeepAliveRequest) -> None: assert isinstance(request, KeepAliveRequest) super().__init__(client, request) - self._is_fetching = False + def _data_operate(self) -> None: # we set ip and data to request @@ -80,44 +81,46 @@ def _response_construct(self) -> None: self._response = KeepAliveResponse() +@final class LoginHandler(CmdHandlerBase): _request: LoginRequest - _result_cls: type[LoginResult] _result: LoginResult + _response: LoginResponse - def __init__(self, client: "Client", request: LoginRequest) -> None: + def __init__(self, client: Client, request: LoginRequest) -> None: assert isinstance(request, LoginRequest) super().__init__(client, request) self._result_cls = LoginResult self._response_cls = LoginResponse +@final class LogoutHandler(LoginedHandlerBase): _request: LogoutRequest - def __init__(self, client: "Client", request: LogoutRequest) -> None: + def __init__(self, client: Client, request: LogoutRequest) -> None: assert isinstance(request, LogoutRequest) super().__init__(client, request) +@final # todo create new handler class NewUserHandler(CmdHandlerBase): _request: NewUserRequest - _result_cls: type[NewUserResult] _result: NewUserResult + _response: NewUserResponse # todo create seperate request and result def __init__(self, client: Client, request: NewUserRequest) -> None: assert isinstance(request, NewUserRequest) super().__init__(client, request) - self._result_cls = NewUserResult - self._response_cls = NewUserResponse +@final class SdkRevisionHandler(CmdHandlerBase): _request: LoginRequest - def __init__(self, client: "Client", request: LoginRequest) -> None: + def __init__(self, client: Client, request: LoginRequest) -> None: assert isinstance(request, LoginRequest) super().__init__(client, request) @@ -140,35 +143,35 @@ def _response_construct(self) -> None: # region Buddy +@final class AddBuddyHandler(CmdHandlerBase): def __init__(self, client: Client, request: RequestBase) -> None: raise NotImplementedError() super().__init__(client, request) +@final class BlockListHandler(CmdHandlerBase): _result: BlockListResult + _response: BlockListResponse def __init__(self, client: Client) -> None: assert isinstance(client, Client) - self._is_fetching = False raise NotImplementedError() - def _response_construct(self) -> None: - self._response = BlockListResponse(self._result) - +@final class BuddyListHandler(LoginedHandlerBase): _result: BuddyListResult - _result_cls: type[BuddyListResult] + _response: BuddyListResponse def __init__(self, client): self._client = client assert isinstance(client, Client) - self._result_cls = BuddyListResult - self._response_cls = BuddyListResponse + raise NotImplementedError() +@final class BuddyStatusInfoHandler(CmdHandlerBase): """ This is what the message should look like. Its broken up for easy viewing. @@ -181,6 +184,7 @@ def __init__(self, client: Client, request: RequestBase) -> None: super().__init__(client, request) +@final class DelBuddyHandler(LoginedHandlerBase): _request: DelBuddyRequest @@ -190,6 +194,7 @@ def __init__(self, client: Client, request: DelBuddyRequest) -> None: self._is_uploading = False +@final class InviteToHandler(LoginedHandlerBase): def __init__(self, client: Client, request: RequestBase) -> None: raise NotImplementedError() @@ -198,6 +203,7 @@ def __init__(self, client: Client, request: RequestBase) -> None: pass +@final class StatusHandler(CmdHandlerBase): _request: StatusRequest _result: StatusResult @@ -205,19 +211,19 @@ class StatusHandler(CmdHandlerBase): def __init__(self, client: Client, request: StatusRequest) -> None: assert isinstance(request, StatusRequest) super().__init__(client, request) - self._is_fetching = False + +@final class StatusInfoHandler(LoginedHandlerBase): _request: StatusInfoRequest _result: StatusInfoResult - + _response: StatusInfoResponse # TODO: check if this implement is correct + def __init__(self, client: Client, request: StatusInfoRequest) -> None: assert isinstance(request, StatusInfoRequest) super().__init__(client, request) - self._result_cls = StatusInfoResult - self._response_cls = StatusInfoResponse def _response_send(self) -> None: # todo: check if response is needed @@ -234,31 +240,29 @@ class AddBlockHandler(CmdHandlerBase): def __init__(self, client: Client, request: AddBlockRequest) -> None: assert isinstance(request, AddBlockRequest) super().__init__(client, request) - self._is_fetching = False + @final class GetProfileHandler(CmdHandlerBase): _request: GetProfileRequest _result: GetProfileResult + _response: GetProfileResponse def __init__(self, client: Client, request: GetProfileRequest) -> None: assert isinstance(request, GetProfileRequest) super().__init__(client, request) - self._result_cls = GetProfileResult - self._response_cls = GetProfileResponse @final class NewProfileHandler(CmdHandlerBase): _request: NewProfileRequest _result: NewProfileResult + _response: NewProfileResponse def __init__(self, client: Client, request: NewProfileRequest) -> None: assert isinstance(request, NewProfileRequest) super().__init__(client, request) - self._result_cls = NewProfileResult - self._response_cls = NewProfileResponse @final @@ -268,18 +272,18 @@ class RegisterCDKeyHandler(CmdHandlerBase): def __init__(self, client: Client, request: RegisterCDKeyRequest) -> None: assert isinstance(request, RegisterCDKeyRequest) super().__init__(client, request) - self._is_fetching = False + @final class RegisterNickHandler(CmdHandlerBase): _request: RegisterNickRequest + _result: RegisterNickResult + _response: RegisterNickResponse def __init__(self, client: Client, request: RegisterNickRequest) -> None: assert isinstance(request, RegisterNickRequest) super().__init__(client, request) - self._result_cls = RegisterNickResult - self._response_cls = RegisterNickResponse @final diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py index 4d78f6691..3334d1313 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py @@ -17,6 +17,5 @@ def __init__(self) -> None: if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper pcm = ServerLauncher() - helper = DebugHelper( - "./frontends/gamespy/protocols/presence_connection_manager", ServerFactory([pcm])) + helper = DebugHelper("./frontends/", ServerFactory([pcm])) helper.start() diff --git a/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py b/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py index 0d69c3631..a5b8c2a13 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py +++ b/src/frontends/gamespy/protocols/presence_search_player/applications/handlers.py @@ -1,4 +1,5 @@ +from typing import final from frontends.gamespy.protocols.presence_search_player.contracts.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest from frontends.gamespy.protocols.presence_search_player.contracts.responses import CheckResponse, NewUserResponse, NicksResponse, OthersListResponse, OthersResponse, SearchResponse, SearchUniqueResponse, UniqueSearchResponse, ValidResponse @@ -8,65 +9,61 @@ from frontends.gamespy.protocols.presence_search_player.applications.client import Client +@final class CheckHandler(CmdHandlerBase): - _result_cls: type[CheckResult] _request: CheckRequest _result: CheckResult + _response: CheckResponse def __init__(self, client: Client, request: CheckRequest) -> None: assert isinstance(request, CheckRequest) super().__init__(client, request) - self._result_cls = CheckResult - self._response_cls = CheckResponse +@final class NewUserHandler(CmdHandlerBase): - _result_cls: type[NewUserResult] _request: NewUserRequest _result: NewUserResult + _response: NewUserResponse def __init__(self, client: Client, request: NewUserRequest) -> None: assert isinstance(request, NewUserRequest) super().__init__(client, request) - self._result_cls = NewUserResult - self._response_cls = NewUserResponse +@final class NicksHandler(CmdHandlerBase): - _result_cls: type[NicksResult] _request: NicksRequest _result: NicksResult + _response: NicksResponse def __init__(self, client: Client, request: NicksRequest) -> None: assert isinstance(request, NicksRequest) super().__init__(client, request) - self._result_cls = NicksResult - self._response_cls = NicksResponse +@final class OthersHandler(CmdHandlerBase): _request: OthersRequest - _result_cls: type[OthersResult] _result: OthersResult + _response: OthersResponse def __init__(self, client: Client, request: OthersRequest) -> None: assert isinstance(request, OthersRequest) super().__init__(client, request) - self._result_cls = OthersResult - self._response_cls = OthersResponse +@final class OthersListHandler(CmdHandlerBase): - _result_cls: type[OthersListResult] _result: OthersListResult + _response: OthersListResponse def __init__(self, client: Client, request: OthersListRequest) -> None: assert isinstance(request, OthersListRequest) super().__init__(client, request) - self._result_cls = OthersListResult - self._response_cls = OthersListResponse +@final class SearchHandler(CmdHandlerBase): """ last one we search with email this may get few profile so we can not return GPErrorCode @@ -79,47 +76,41 @@ class SearchHandler(CmdHandlerBase): \\more\\\\final\\ \\search\\sesskey\\0\\profileid\\0\\namespaceid\\0\\nick\\gbr359_jordips\\gamename\\gbrome\\final\\ """ - _result_cls: type[SearchResult] _result: SearchResult _response: SearchResponse def __init__(self, client: Client, request: SearchRequest) -> None: assert isinstance(request, SearchRequest) super().__init__(client, request) - self._result_cls = SearchResult - self._response_cls = SearchResponse +@final class SearchUniqueHandler(CmdHandlerBase): _result: SearchUniqueResult - _result_cls: type[SearchUniqueResult] + _response: SearchUniqueResponse def __init__(self, client: Client, request: SearchUniqueRequest) -> None: assert isinstance(request, SearchUniqueRequest) super().__init__(client, request) - self._result_cls = SearchUniqueResult - self._response_cls = SearchUniqueResponse +@final class UniqueSearchHandler(CmdHandlerBase): _request: UniqueSearchRequest _result: UniqueSearchResult - _result_cls: type[UniqueSearchResult] + _response: UniqueSearchResponse def __init__(self, client: Client, request: UniqueSearchRequest) -> None: assert isinstance(request, UniqueSearchRequest) super().__init__(client, request) - self._result_cls = UniqueSearchResult - self._response_cls = UniqueSearchResponse +@final class ValidHandler(CmdHandlerBase): _result: ValidResult _request: ValidRequest - _result_cls: type[ValidResult] + _response: ValidResponse def __init__(self, client: Client, request: ValidRequest) -> None: assert isinstance(request, ValidRequest) super().__init__(client, request) - self._result_cls = ValidResult - self._response_cls = ValidResponse diff --git a/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py b/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py index 5d7ddf381..649b9fd84 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py @@ -17,6 +17,5 @@ def __init__(self) -> None: if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper psp = ServerLauncher() - helper = DebugHelper( - "./frontends/gamespy/protocols/presence_search_player", ServerFactory([psp])) + helper = DebugHelper("./frontends/", ServerFactory([psp])) helper.start() diff --git a/src/frontends/gamespy/protocols/query_report/aggregates/peer_room_info.py b/src/frontends/gamespy/protocols/query_report/aggregates/peer_room_info.py index 11fcc127d..6811cf9ba 100644 --- a/src/frontends/gamespy/protocols/query_report/aggregates/peer_room_info.py +++ b/src/frontends/gamespy/protocols/query_report/aggregates/peer_room_info.py @@ -1,12 +1,7 @@ -from uuid import UUID - from pydantic import BaseModel, Field -from types import MappingProxyType - class PeerRoomInfo(BaseModel): - server_id: UUID game_name: str group_id: int = Field(..., alias='groupid') room_name: str = Field(alias="hostname") @@ -14,21 +9,15 @@ class PeerRoomInfo(BaseModel): max_waiting: int = Field(default=200, alias='maxwaiting') number_of_servers: int = Field(default=0, alias="numservers") number_of_players: int = Field(default=0, alias="numplayers") - max_players: int = Field(200, alias="maxplayers") + max_players: int = Field(default=200, alias="maxplayers") password: str = Field(default="", alias="password") number_of_games: int = Field(default=0, alias="numgames") number_of_playing: int = Field(default=0, alias="numplaying") - # def __init__(self, game_name, group_id, room_name) -> None: - # self.game_name = game_name - # self.group_id = group_id - # self.room_name = room_name - - def get_gamespy_dict(self) -> MappingProxyType: + def get_gamespy_dict(self) -> dict: """ return a immutable dict """ - data = self.model_dump() - del data["server_id"] + data = self.model_dump(mode="json") del data["game_name"] - return MappingProxyType(data) + return data diff --git a/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py b/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py index a56d9f290..781c8d925 100644 --- a/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py @@ -17,6 +17,5 @@ def __init__(self) -> None: if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper qr = ServerLauncher() - helper = DebugHelper( - "./frontends/gamespy/protocols/query_report", ServerFactory([qr])) + helper = DebugHelper("./frontends/", ServerFactory([qr])) helper.start() diff --git a/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py b/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py index 7c8d56632..1cf7c9248 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py +++ b/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py @@ -1,3 +1,4 @@ +from typing import final from frontends.gamespy.protocols.query_report.applications.client import Client from frontends.gamespy.protocols.query_report.v2.abstractions.cmd_handler_base import ( CmdHandlerBase, @@ -25,13 +26,14 @@ ) +@final class AvailableHandler(CmdHandlerBase): _request: AvaliableRequest def __init__(self, client: Client, request: AvaliableRequest) -> None: assert isinstance(request, AvaliableRequest) super().__init__(client, request) - self._is_fetching = False + def _response_construct(self): self._result = AvailableResult( @@ -40,6 +42,7 @@ def _response_construct(self): self._response = AvailableResponse(self._result) +@final class ChallengeHanler(CmdHandlerBase): _request: ChallengeRequest _result: ChallengeResult @@ -51,16 +54,18 @@ def __init__(self, client: Client, request: ChallengeRequest) -> None: self._response_cls = ChallengeResponse +@final class ClientMessageAckHandler(CmdHandlerBase): def __init__(self, client: Client, request: ClientMessageAckRequest) -> None: assert isinstance(request, ClientMessageAckRequest) super().__init__(client, request) - self._is_fetching = False + def _response_construct(self): self._client.log_info("Get client message ack") +@final class ClientMessageHandler(CmdHandlerBase): def __init__(self, client: Client, request: ClientMessageRequest) -> None: @@ -70,21 +75,21 @@ def __init__(self, client: Client, request: ClientMessageRequest) -> None: self._response_cls = ClientMessageResponse +@final class EchoHandler(CmdHandlerBase): def __init__(self, client: Client, request: EchoRequest) -> None: assert isinstance(request, EchoRequest) super().__init__(client, request) +@final class HeartbeatHandler(CmdHandlerBase): _request: HeartbeatRequest - _result: HeartbeatResult - _result_cls: type[HeartbeatResult] def __init__(self, client: Client, request: HeartbeatRequest) -> None: assert isinstance(request, HeartbeatRequest) super().__init__(client, request) - self._is_fetching = False + def _response_construct(self) -> None: self._result = HeartbeatResult( @@ -96,9 +101,10 @@ def _response_construct(self) -> None: self._response = HeartbeatResponse(self._result) +@final class KeepAliveHandler(CmdHandlerBase): def __init__(self, client: Client, request: KeepAliveRequest) -> None: assert isinstance(request, KeepAliveRequest) super().__init__(client, request) - self._is_fetching = False + diff --git a/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py b/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py index 7faf83430..56b75ccfe 100644 --- a/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py @@ -13,7 +13,6 @@ def __init__(self) -> None: if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper sb2 = ServerLauncher() - helper = DebugHelper( - "./frontends/gamespy/protocols/server_browser", ServerFactory([sb2])) - helper.start() # todo: add v1 server here + helper = DebugHelper("./frontends/", ServerFactory([sb2])) + helper.start() diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py index baa9e0c12..e9695c839 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py @@ -104,40 +104,37 @@ class SendMessageHandler(CmdHandlerBase): def __init__(self, client: Client, request: SendMessageRequest) -> None: assert isinstance(request, SendMessageRequest) super().__init__(client, request) - self._is_fetching = False # we just need send the message to backend, then backend will send to queryreport frontend, query report frontend will handle for us class UpdateServerInfoHandler(CmdHandlerBase): _request: ServerInfoRequest _result: UpdateServerInfoResult + _response: UpdateServerInfoResponse - def __init__(self, client: Client, request: RequestBase) -> None: + def __init__(self, client: Client, request: ServerInfoRequest) -> None: + assert isinstance(request, ServerInfoRequest) super().__init__(client, request) - self._result_cls = UpdateServerInfoResult - self._response_cls = UpdateServerInfoResponse class ServerMainListHandler(ServerListUpdateOptionHandlerBase): _request: ServerListRequest _result: ServerMainListResult - _result_cls: type[ServerMainListResult] + _response: ServerMainListResponse - def __init__(self, client: Client, request: RequestBase) -> None: + def __init__(self, client: Client, request: ServerListRequest) -> None: + assert isinstance(request, ServerListRequest) super().__init__(client, request) - self._result_cls = ServerMainListResult - self._response_cls = ServerMainListResponse class P2PGroupRoomListHandler(ServerListUpdateOptionHandlerBase): _request: ServerListRequest _result: P2PGroupRoomListResult - _result_cls: type[P2PGroupRoomListResult] + _response: P2PGroupRoomListResponse - def __init__(self, client: Client, request: RequestBase) -> None: + def __init__(self, client: Client, request: ServerListRequest) -> None: + assert isinstance(request, ServerListRequest) super().__init__(client, request) - self._result_cls = P2PGroupRoomListResult - self._response_cls = P2PGroupRoomListResponse class ServerFullInfoListHandler(ServerListUpdateOptionHandlerBase): @@ -159,9 +156,8 @@ class ServerFullInfoListHandler(ServerListUpdateOptionHandlerBase): """ _request: ServerListRequest _result: ServerFullInfoListResult - _result_cls: type[ServerFullInfoListResult] + _response: ServerFullInfoListResponse - def __init__(self, client: Client, request: RequestBase) -> None: + def __init__(self, client: Client, request: ServerListRequest) -> None: + assert isinstance(request, ServerListRequest) super().__init__(client, request) - self._result_cls = ServerFullInfoListResult - self._response_cls = ServerFullInfoListResponse diff --git a/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py index fc36ae0a7..a9109c260 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py @@ -1,5 +1,6 @@ from frontends.gamespy.library.extentions.encoding import get_bytes from frontends.gamespy.protocols.query_report.aggregates.game_server_info import GameServerInfo +from frontends.gamespy.protocols.query_report.aggregates.peer_room_info import PeerRoomInfo from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import ( AdHocResponseBase, ServerListUpdateOptionResponseBase, @@ -78,7 +79,6 @@ def _build_kv(data: dict) -> bytearray: class P2PGroupRoomListResponse(ServerListUpdateOptionResponseBase): - _request: ServerListRequest _result: P2PGroupRoomListResult def build(self) -> None: @@ -89,9 +89,12 @@ def build(self) -> None: self.sending_buffer = bytes(self._buffer) def _build_servers_full_info(self): + # we have to add a server with group ip 0 to make peer sdk finish the sb process + empty_info = PeerRoomInfo(game_name="", groupid=0, hostname="") + self._result.peer_room_info.append(empty_info) for room in self._result.peer_room_info: self._buffer.append(GameServerFlags.HAS_KEYS_FLAG) - group_id_bytes = room.group_id.to_bytes() + group_id_bytes = room.group_id.to_bytes(length=4) self._buffer.extend(group_id_bytes) # get gamespy format dict gamespy_dict = room.get_gamespy_dict() @@ -104,7 +107,8 @@ def _build_servers_full_info(self): ) self._buffer.extend(get_bytes(value)) self._buffer.append(STRING_SPLITER) - end_flag = b"\x00" + # apped end flag + end_flag = b"\x00\x00\x00\x00" self._buffer.extend(end_flag) diff --git a/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py b/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py index 758b41971..d0eea71d0 100644 --- a/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py @@ -15,6 +15,5 @@ def __init__(self) -> None: if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper web = ServerLauncher() - helper = DebugHelper( - "./frontends/gamespy/protocols/web_services", ServerFactory([web])) + helper = DebugHelper("./frontends/", ServerFactory([web])) helper.start() diff --git a/src/frontends/gamespy/protocols/web_services/applications/switcher.py b/src/frontends/gamespy/protocols/web_services/applications/switcher.py index e3b05176a..869852166 100644 --- a/src/frontends/gamespy/protocols/web_services/applications/switcher.py +++ b/src/frontends/gamespy/protocols/web_services/applications/switcher.py @@ -7,9 +7,9 @@ from frontends.gamespy.protocols.web_services.applications.client import Client from frontends.gamespy.protocols.web_services.aggregations.exceptions import WebException from frontends.gamespy.protocols.web_services.modules.auth.contracts.requests import LoginProfileRequest, LoginProfileWithGameIdRequest, LoginRemoteAuthRequest, LoginRemoteAuthWithGameIdRequest, LoginUniqueNickRequest, LoginUniqueNickWithGameIdRequest -from frontends.gamespy.protocols.web_services.modules.auth.handlers.general import LoginProfileHandler, LoginProfileWithGameIdHandler, LoginRemoteAuthHandler, LoginRemoteAuthWithGameIdHandler, LoginUniqueNickHandler, LoginUniqueNickWithGameIdHandler +from frontends.gamespy.protocols.web_services.modules.auth.applications.handlers import LoginProfileHandler, LoginProfileWithGameIdHandler, LoginRemoteAuthHandler, LoginRemoteAuthWithGameIdHandler, LoginUniqueNickHandler, LoginUniqueNickWithGameIdHandler from frontends.gamespy.protocols.web_services.modules.direct2game.contracts.requests import GetPurchaseHistoryRequest, GetStoreAvailabilityRequest -from frontends.gamespy.protocols.web_services.modules.direct2game.handlers.general import GetPurchaseHistoryHandler, GetStoreAvailabilityHandler +from frontends.gamespy.protocols.web_services.modules.direct2game.applications.handlers import GetPurchaseHistoryHandler, GetStoreAvailabilityHandler from frontends.gamespy.protocols.web_services.modules.sake.contracts.requests import CreateRecordRequest, GetMyRecordsRequest, SearchForRecordsRequest from frontends.gamespy.protocols.web_services.modules.sake.applications.handlers import CreateRecordHandler, GetMyRecordsHandler, SearchForRecordsHandler diff --git a/src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py b/src/frontends/gamespy/protocols/web_services/modules/auth/applications/handlers.py similarity index 82% rename from src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py rename to src/frontends/gamespy/protocols/web_services/modules/auth/applications/handlers.py index 86652a582..c770db356 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/auth/handlers/general.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/applications/handlers.py @@ -42,36 +42,34 @@ def __init__(self, client: Client, request: RequestBase) -> None: class LoginProfileWithGameIdHandler(CmdHandlerBase): _request: LoginProfileWithGameIdRequest _result: LoginProfileResult + _response: LoginProfileWithGameIdResponse - def __init__(self, client: Client, request: RequestBase) -> None: + def __init__(self, client: Client, request: LoginProfileWithGameIdRequest) -> None: super().__init__(client, request) - self._result_cls = LoginProfileResult - self._response_cls = LoginProfileWithGameIdResponse class LoginPs3CertHandler(CmdHandlerBase): _request: LoginPs3CertRequest _result: LoginPs3CertResult + _response: LoginPs3CertResponse def __init__(self, client: Client, request: LoginPs3CertRequest) -> None: super().__init__(client, request) - self._result_cls = LoginPs3CertResult - self._response_cls = LoginPs3CertResponse class LoginPs3CertWithGameIdHandler(CmdHandlerBase): _request: LoginPs3CertWithGameIdRequest _result: LoginPs3CertResult + _response: LoginPs3CertWithGameIdResponse def __init__(self, client: Client, request: LoginPs3CertRequest) -> None: super().__init__(client, request) - self._result_cls = LoginPs3CertResult - self._response_cls = LoginPs3CertWithGameIdResponse class LoginRemoteAuthHandler(CmdHandlerBase): _request: LoginRemoteAuthRequest _result: LoginRemoteAuthResult + _response: LoginRemoteAuthResponse def __init__(self, client: Client, request: LoginRemoteAuthRequest) -> None: super().__init__(client, request) @@ -82,28 +80,25 @@ def __init__(self, client: Client, request: LoginRemoteAuthRequest) -> None: class LoginRemoteAuthWithGameIdHandler(CmdHandlerBase): _request: LoginRemoteAuthWithGameIdRequest _result: LoginRemoteAuthResult + _response: LoginRemoteAuthWithGameIdResponse def __init__(self, client: Client, request: LoginRemoteAuthWithGameIdRequest) -> None: super().__init__(client, request) - self._result_cls = LoginRemoteAuthResult - self._response_cls = LoginRemoteAuthWithGameIdResponse class LoginUniqueNickHandler(CmdHandlerBase): _request: LoginUniqueNickRequest _result: LoginUniqueNickResult + _response: LoginUniqueNickResponse def __init__(self, client: Client, request: LoginUniqueNickRequest) -> None: super().__init__(client, request) - self._result_cls = LoginUniqueNickResult - self._response_cls = LoginUniqueNickResponse class LoginUniqueNickWithGameIdHandler(CmdHandlerBase): _request: LoginUniqueNickWithGameIdRequest _result: LoginUniqueNickResult + _response: LoginUniqueNickWithGameIdResponse def __init__(self, client: Client, request: LoginUniqueNickWithGameIdRequest) -> None: super().__init__(client, request) - self._result_cls = LoginUniqueNickResult - self._response_cls = LoginUniqueNickWithGameIdResponse diff --git a/src/frontends/gamespy/protocols/web_services/modules/direct2game/handlers/general.py b/src/frontends/gamespy/protocols/web_services/modules/direct2game/applications/handlers.py similarity index 85% rename from src/frontends/gamespy/protocols/web_services/modules/direct2game/handlers/general.py rename to src/frontends/gamespy/protocols/web_services/modules/direct2game/applications/handlers.py index f46bc1e1e..b3f39d391 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/direct2game/handlers/general.py +++ b/src/frontends/gamespy/protocols/web_services/modules/direct2game/applications/handlers.py @@ -17,22 +17,18 @@ class GetPurchaseHistoryHandler(CmdHandlerBase): _request: GetPurchaseHistoryRequest _result: GetPurchaseHistoryResult + _response: GetPurchaseHistoryResponse def __init__(self, client: Client, request: GetPurchaseHistoryRequest) -> None: assert isinstance(request, GetPurchaseHistoryRequest) super().__init__(client, request) - def _response_construct(self) -> None: - self._response = GetPurchaseHistoryResponse(self._result) - class GetStoreAvailabilityHandler(CmdHandlerBase): _request: GetStoreAvailabilityRequest _result: GetStoreAvailabilityResult + _response: GetStoreAvailabilityResponse def __init__(self, client: Client, request: GetStoreAvailabilityRequest) -> None: assert isinstance(request, GetStoreAvailabilityRequest) super().__init__(client, request) - - def _response_construct(self) -> None: - self._response = GetStoreAvailabilityResponse(self._result) diff --git a/src/frontends/gamespy/protocols/web_services/modules/sake/applications/handlers.py b/src/frontends/gamespy/protocols/web_services/modules/sake/applications/handlers.py index 34abb223b..6588ec29f 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/sake/applications/handlers.py +++ b/src/frontends/gamespy/protocols/web_services/modules/sake/applications/handlers.py @@ -1,31 +1,35 @@ from frontends.gamespy.protocols.web_services.applications.client import Client from frontends.gamespy.protocols.web_services.modules.sake.abstractions.generals import CmdHandlerBase from frontends.gamespy.protocols.web_services.modules.sake.contracts.requests import CreateRecordRequest, GetMyRecordsRequest, SearchForRecordsRequest +from frontends.gamespy.protocols.web_services.modules.sake.contracts.responses import CreateRecordResponse, GetMyRecordResponse, SearchForRecordsResponse from frontends.gamespy.protocols.web_services.modules.sake.contracts.results import CreateRecordResult, GetMyRecordsResult, SearchForRecordsResult class CreateRecordHandler(CmdHandlerBase): - _request: "CreateRecordRequest" - _result: "CreateRecordResult" + _request: CreateRecordRequest + _result: CreateRecordResult + _response: CreateRecordResponse - def __init__(self, client: "Client", request: "CreateRecordRequest") -> None: + def __init__(self, client: Client, request: CreateRecordRequest) -> None: assert isinstance(request, CreateRecordRequest) super().__init__(client, request) class GetMyRecordsHandler(CmdHandlerBase): - _request: "GetMyRecordsRequest" - _result: "GetMyRecordsResult" + _request: GetMyRecordsRequest + _result: GetMyRecordsResult + _response: GetMyRecordResponse - def __init__(self, client: "Client", request: "GetMyRecordsRequest") -> None: + def __init__(self, client: Client, request: GetMyRecordsRequest) -> None: assert isinstance(request, GetMyRecordsRequest) super().__init__(client, request) class SearchForRecordsHandler(CmdHandlerBase): - _request: "SearchForRecordsRequest" - _result: "SearchForRecordsResult" + _request: SearchForRecordsRequest + _result: SearchForRecordsResult + _response: SearchForRecordsResponse - def __init__(self, client: "Client", request: "SearchForRecordsRequest") -> None: + def __init__(self, client: Client, request: SearchForRecordsRequest) -> None: assert isinstance(request, SearchForRecordsRequest) super().__init__(client, request) diff --git a/src/frontends/tests/gamespy/server_browser/contract_tests.py b/src/frontends/tests/gamespy/server_browser/contract_tests.py index 8374c5aa7..005a93b40 100644 --- a/src/frontends/tests/gamespy/server_browser/contract_tests.py +++ b/src/frontends/tests/gamespy/server_browser/contract_tests.py @@ -37,7 +37,8 @@ def test_main_list(self): query_report_port=6900, last_heart_beat_received_time=datetime.now(), status=GameServerStatus.NORMAL, - server_data={"hostname": "GameSpy QR2 Sample"}, + server_data={"hostname": "GameSpy QR2 Sample", "gametype": "", + "mapname": "", "numplayers": "", "maxplayers": ""}, player_data=[{}], team_data=[{}], host_ip_address="127.0.0.1", From f7ea55ca6d2ca3c9b58e5a57fecb9fd073781c66 Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Fri, 31 Oct 2025 01:16:59 +0000 Subject: [PATCH 220/231] Fix: fastapi swagger docs response schemas --- .../library/abstractions/contracts.py | 14 +- .../library/abstractions/handler_base.py | 29 ++-- src/backends/protocols/gamespy/chat/data.py | 7 +- .../protocols/gamespy/chat/handlers.py | 26 ++- src/backends/protocols/gamespy/chat/helper.py | 8 +- .../protocols/gamespy/chat/response.py | 87 ++++++++++ .../protocols/gamespy/game_status/handlers.py | 11 +- .../protocols/gamespy/game_status/response.py | 18 ++ .../gamespy/game_traffic_relay/handlers.py | 11 +- .../gamespy/game_traffic_relay/requests.py | 9 +- .../gamespy/game_traffic_relay/responses.py | 5 + .../protocols/gamespy/natneg/handlers.py | 5 + .../protocols/gamespy/natneg/requests.py | 4 +- .../protocols/gamespy/natneg/responses.py | 7 + .../presence_connection_manager/handlers.py | 27 ++- .../presence_connection_manager/responses.py | 18 ++ .../presence_search_player/handlers.py | 11 ++ .../presence_search_player/responses.py | 38 +++++ .../protocols/gamespy/query_report/data.py | 88 ++++------ .../gamespy/query_report/handlers.py | 8 +- .../gamespy/query_report/responses.py | 0 .../gamespy/server_browser/handlers.py | 52 +++--- .../gamespy/server_browser/responses.py | 29 ++++ .../gamespy/web_services/handlers.py | 9 +- .../gamespy/web_services/responses.py | 40 +++++ src/backends/routers/gamespy/chat.py | 137 ++++++++------- src/backends/routers/gamespy/game_stats.py | 73 ++++++++ .../routers/gamespy/game_traffic_relay.py | 13 +- src/backends/routers/gamespy/gstats.py | 71 -------- src/backends/routers/gamespy/natneg.py | 25 +-- .../gamespy/presence_connection_manager.py | 51 +++--- .../routers/gamespy/presence_search_player.py | 47 +++--- src/backends/routers/gamespy/query_report.py | 29 ++-- .../routers/gamespy/server_browser.py | 30 ++-- src/backends/routers/gamespy/webservices.py | 58 +++---- src/backends/routers/home.py | 22 ++- src/backends/tests/gamespy/chat/room_tests.py | 157 ++++++++++++++++++ .../gamespy/query_report/data_fetch_tests.py | 4 - .../gamespy/query_report/handler_tests.py | 2 +- .../gamespy/server_browser/filter_tests.py | 125 +++++++------- 40 files changed, 953 insertions(+), 452 deletions(-) create mode 100644 src/backends/protocols/gamespy/chat/response.py create mode 100644 src/backends/protocols/gamespy/game_status/response.py create mode 100644 src/backends/protocols/gamespy/game_traffic_relay/responses.py create mode 100644 src/backends/protocols/gamespy/natneg/responses.py create mode 100644 src/backends/protocols/gamespy/presence_connection_manager/responses.py create mode 100644 src/backends/protocols/gamespy/presence_search_player/responses.py create mode 100644 src/backends/protocols/gamespy/query_report/responses.py create mode 100644 src/backends/protocols/gamespy/server_browser/responses.py create mode 100644 src/backends/protocols/gamespy/web_services/responses.py create mode 100644 src/backends/routers/gamespy/game_stats.py delete mode 100644 src/backends/routers/gamespy/gstats.py diff --git a/src/backends/library/abstractions/contracts.py b/src/backends/library/abstractions/contracts.py index b16e3e2e0..aa1437c6f 100644 --- a/src/backends/library/abstractions/contracts.py +++ b/src/backends/library/abstractions/contracts.py @@ -1,3 +1,4 @@ +from typing import Dict from pydantic import BaseModel, UUID4 @@ -27,10 +28,19 @@ class OKResponse(Response): class DataResponse(OKResponse): - result: dict + result: BaseModel pass class ErrorResponse(Response): - # code: int + exception_name: str pass + + +RESPONSES_DEF: Dict = { + 400: {"model": ErrorResponse}, + 422: {"model": ErrorResponse}, + 500: {"model": ErrorResponse}, + 450: {"model": ErrorResponse} +} +"""Dict type is using to compatible with language checking""" diff --git a/src/backends/library/abstractions/handler_base.py b/src/backends/library/abstractions/handler_base.py index 59e7b762f..93dfec2b0 100644 --- a/src/backends/library/abstractions/handler_base.py +++ b/src/backends/library/abstractions/handler_base.py @@ -11,6 +11,8 @@ import logging from sqlalchemy.orm import Session +from frontends.gamespy.library.exceptions.general import UniSpyException + class HandlerBase: """ @@ -19,8 +21,9 @@ class HandlerBase: _request: RequestBase _result: ResultBase | None - _response: Response | None + response: Response """ + response is created by child class annotation the response using to wrap data """ # _data: object @@ -28,22 +31,13 @@ class HandlerBase: the data get from database, can be any type """ - @property - def response(self) -> dict | None: - """ - the dict response which send to client - """ - if self._response is None: - return None - return self._response.to_json_dict() - def __init__(self, request: RequestBase) -> None: assert issubclass(type(request), RequestBase) self._request = request # decoupling the logging in home.py self.logger = logging.getLogger("backend") self._result = None - self._response = None + self.response = OKResponse() def handle(self) -> None: with Session(ENGINE) as session: @@ -73,6 +67,13 @@ def _response_construct(self) -> None: """ # if there are no result, we send ok response if self._result is None: - self._response = OKResponse() - else: - self._response = DataResponse(result=self._result.model_dump(mode="json")) + return + if "response" not in self.__class__.__annotations__: + raise UniSpyException( + "write response type annotation in child class to create response instance.") + response_cls = self.__class__.__annotations__['response'] + if not issubclass(response_cls, DataResponse): + raise UniSpyException( + "response type annotation must be a subclass of DataResponse") + self.response = response_cls(result=self._result) + # self.response = DataResponse(result=self._result) diff --git a/src/backends/protocols/gamespy/chat/data.py b/src/backends/protocols/gamespy/chat/data.py index 61cb873ae..06ee402c5 100644 --- a/src/backends/protocols/gamespy/chat/data.py +++ b/src/backends/protocols/gamespy/chat/data.py @@ -227,13 +227,14 @@ def get_channel_by_name_and_ip_port( def check_channel_user_trash_data( channel: ChatChannelCaches, user: ChatUserCaches, session: Session ): + expire_time = datetime.now() - timedelta(minutes=2) assert isinstance(channel.channel_name, str) assert isinstance(user.nick_name, str) - exist_user = get_channel_user_cache_by_name( + exist_user = get_channel_user_cache_by_nick_name( channel.channel_name, user.nick_name, session ) if exist_user is not None: - if exist_user.update_time <= datetime.now() - timedelta(minutes=2): # type: ignore + if exist_user.update_time <= expire_time: # type: ignore session.delete(exist_user) session.commit() else: @@ -244,7 +245,7 @@ def check_channel_user_trash_data( # session.commit() -def get_channel_user_cache_by_name( +def get_channel_user_cache_by_nick_name( channel_name: str, nick_name: str, session: Session ) -> ChatChannelUserCaches | None: assert isinstance(channel_name, str) diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index edc3b5655..3bb3bb629 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -1,5 +1,6 @@ from datetime import datetime from typing import TYPE_CHECKING, cast +from backends.library.abstractions.contracts import OKResponse import backends.library.abstractions.handler_base as hb @@ -43,6 +44,7 @@ WhoIsRequest, WhoRequest, ) +from backends.protocols.gamespy.chat.response import AtmResponse, CryptResponse, GetCkeyResponse, GetKeyResponse, JoinResponse, KickResponse, ModeResponse, NamesResponse, NicksResponse, PartResponse, PingResponse, PrivateResponse, SetChannelKeyResponse, TopicResponse, UtmResponse, WhoIsResponse from frontends.gamespy.protocols.chat.aggregates.enums import ( GetKeyRequestType, ModeRequestType, @@ -57,6 +59,7 @@ NoSuchChannelException, NoSuchNickException, ) +from frontends.gamespy.protocols.chat.contracts.responses import NoticeResponse from frontends.gamespy.protocols.chat.contracts.results import ( AtmResult, CryptResult, @@ -170,6 +173,7 @@ def __init__(self, request: RequestBase) -> None: # region General class PingHandler(HandlerBase): + response: PingResponse def _result_construct(self) -> None: assert self._user is not None @@ -192,6 +196,7 @@ def _data_operate(self) -> None: class CryptHandler(HandlerBase): _request: CryptRequest + response: CryptResponse def _request_check(self) -> None: # we just clean the garbage data @@ -228,6 +233,7 @@ def _result_construct(self) -> None: class GetKeyHandler(HandlerBase): _request: GetKeyRequest + response: GetKeyResponse def _data_operate(self) -> None: caches = data.get_user_cache_by_nick_name( @@ -329,6 +335,7 @@ def _data_operate(self) -> None: class NickHandler(HandlerBase): _request: NickRequest + response: NicksResponse def _request_check(self) -> None: self._get_user() @@ -393,6 +400,7 @@ def _data_operate(self) -> None: class SetKeyHandler(HandlerBase): _request: SetKeyRequest + response: OKResponse def _data_operate(self) -> None: user = data.get_user_cache_by_ip_port( @@ -449,6 +457,7 @@ def _result_construct(self) -> None: class WhoIsHandler(HandlerBase): _request: WhoIsRequest + response: WhoIsResponse def _data_operate(self) -> None: self._data: WhoIsResult = data.get_whois_result( @@ -463,6 +472,7 @@ def _result_construct(self) -> None: class JoinHandler(ChannelHandlerBase): _request: JoinRequest + response: JoinResponse def _request_check(self) -> None: self._get_user() @@ -528,6 +538,7 @@ def _result_construct(self) -> None: class GetCKeyHandler(ChannelHandlerBase): _request: GetCKeyRequest + response: GetCkeyResponse def _data_operate(self) -> None: match self._request.request_type: @@ -542,7 +553,7 @@ def get_channel_all_user_key_value(self): ) def get_channel_specific_user_key_value(self): - d = data.get_channel_user_cache_by_name( + d = data.get_channel_user_cache_by_nick_name( self._request.channel_name, self._request.nick_name, self._session ) if d is not None: @@ -573,6 +584,7 @@ def _result_construct(self) -> None: class KickHandler(ChannelHandlerBase): _kickee: ChatChannelUserCaches | None _request: KickRequest + response: KickResponse def __init__(self, request: RequestBase) -> None: super().__init__(request) @@ -582,7 +594,7 @@ def _request_check(self) -> None: super()._request_check() assert isinstance(self._channel, ChatChannelCaches) assert isinstance(self._channel.channel_name, str) - self._kickee = data.get_channel_user_cache_by_name( + self._kickee = data.get_channel_user_cache_by_nick_name( self._channel.channel_name, self._request.kickee_nick_name, self._session ) if self._kickee is None: @@ -616,6 +628,7 @@ def _result_construct(self) -> None: class ModeHandler(ChannelHandlerBase): _request: ModeRequest + response: ModeResponse def _data_operate(self) -> None: assert self._channel @@ -638,6 +651,7 @@ def _result_construct(self) -> None: class NamesHandler(ChannelHandlerBase): _request: NamesRequest + response: NamesResponse def _request_check(self) -> None: self._get_user() @@ -670,6 +684,7 @@ def _result_construct(self) -> None: class PartHandler(ChannelHandlerBase): _request: PartRequest + response: PartResponse def _data_operate(self) -> None: assert self._channel @@ -697,6 +712,7 @@ def _result_construct(self) -> None: class SetChannelKeyHandler(ChannelHandlerBase): _request: SetChannelKeyRequest + response: SetChannelKeyResponse def _request_check(self) -> None: super()._request_check() @@ -747,6 +763,7 @@ def _result_construct(self) -> None: class TopicHandler(ChannelHandlerBase): _request: TopicRequest + response: TopicResponse def _data_operate(self) -> None: assert self._channel_user @@ -773,6 +790,7 @@ def _result_construct(self) -> None: class AtmHandler(MessageHandlerBase): _request: AtmRequest + response: AtmResponse def _result_construct(self) -> None: assert self._user is not None @@ -789,6 +807,7 @@ def _result_construct(self) -> None: class UtmHandler(MessageHandlerBase): _request: UtmRequest + response: UtmResponse def _result_construct(self) -> None: assert self._user is not None @@ -804,6 +823,7 @@ def _result_construct(self) -> None: class NoticeHandler(MessageHandlerBase): _request: NoticeRequest + response: NoticeResponse def _result_construct(self) -> None: assert self._user is not None @@ -819,6 +839,7 @@ def _result_construct(self) -> None: class PrivateHandler(MessageHandlerBase): _request: PrivateRequest + response: PrivateResponse def _result_construct(self) -> None: assert self._user is not None @@ -834,6 +855,7 @@ def _result_construct(self) -> None: class PublishMessageHandler(hb.HandlerBase): _request: PublishMessageRequest + response: OKResponse def _data_operate(self) -> None: # todo add checking on request validation, like broadcast key diff --git a/src/backends/protocols/gamespy/chat/helper.py b/src/backends/protocols/gamespy/chat/helper.py index aa7ed3976..c14ec88ff 100644 --- a/src/backends/protocols/gamespy/chat/helper.py +++ b/src/backends/protocols/gamespy/chat/helper.py @@ -247,7 +247,7 @@ def change_modes( raise BadChannelKeyException( "ADD_CHANNEL_OPERATOR require nick name" ) - u = data.get_channel_user_cache_by_name( + u = data.get_channel_user_cache_by_nick_name( request.channel_name, request.nick_name, session ) if u is None: @@ -260,7 +260,7 @@ def change_modes( raise BadChannelKeyException( "REMOVE_CHANNEL_OPERATOR require nick name" ) - u = data.get_channel_user_cache_by_name( + u = data.get_channel_user_cache_by_nick_name( request.channel_name, request.nick_name, session ) u.is_channel_operator = False # type: ignore @@ -270,7 +270,7 @@ def change_modes( raise BadChannelKeyException( "ENABLE_USER_VOICE_PERMISSION require nick name" ) - u = data.get_channel_user_cache_by_name( + u = data.get_channel_user_cache_by_nick_name( request.channel_name, request.nick_name, session ) u.is_voiceable = True # type: ignore @@ -279,7 +279,7 @@ def change_modes( raise BadChannelKeyException( "DISABLE_USER_VOICE_PERMISSION require nick name" ) - u = data.get_channel_user_cache_by_name( + u = data.get_channel_user_cache_by_nick_name( request.channel_name, request.nick_name, session ) u.is_voiceable = False # type: ignore diff --git a/src/backends/protocols/gamespy/chat/response.py b/src/backends/protocols/gamespy/chat/response.py new file mode 100644 index 000000000..4b504a8b0 --- /dev/null +++ b/src/backends/protocols/gamespy/chat/response.py @@ -0,0 +1,87 @@ +from backends.library.abstractions.contracts import DataResponse +from frontends.gamespy.protocols.chat.contracts.results import AtmResult, CryptResult, GetCKeyResult, GetChannelKeyResult, GetKeyResult, JoinResult, KickResult, ListResult, ModeResult, NamesResult, NickResult, NoticeResult, PartResult, PingResult, PrivateResult, SetCKeyResult, SetChannelKeyResult, TopicResult, UtmResult, WhoIsResult, WhoResult + + +class PingResponse(DataResponse): + result: PingResult + + +class CryptResponse(DataResponse): + result: CryptResult + + +class GetKeyResponse(DataResponse): + result: GetKeyResult + + +class ListResponse(DataResponse): + result: ListResult + + +class NicksResponse(DataResponse): + result: NickResult + + +class WhoResponse(DataResponse): + result: WhoResult + + +class WhoIsResponse(DataResponse): + result: WhoIsResult + + +class JoinResponse(DataResponse): + result: JoinResult + + +class GetChannelKeyResponse(DataResponse): + result: GetChannelKeyResult + + +class GetCkeyResponse(DataResponse): + result: GetCKeyResult + + +class KickResponse(DataResponse): + result: KickResult + + +class ModeResponse(DataResponse): + result: ModeResult + + +class NamesResponse(DataResponse): + result: NamesResult + + +class PartResponse(DataResponse): + result: PartResult + + +class SetChannelKeyResponse(DataResponse): + result: SetChannelKeyResult + + +class SetCKeyResponse(DataResponse): + result: SetCKeyResult + + +class TopicResponse(DataResponse): + result: TopicResult + + +class AtmResponse(DataResponse): + result: AtmResult + + +class UtmResponse(DataResponse): + result: UtmResult + + +class NoticeResponse(DataResponse): + result: NoticeResult + + +class PrivateResponse(DataResponse): + result: PrivateResult + diff --git a/src/backends/protocols/gamespy/game_status/handlers.py b/src/backends/protocols/gamespy/game_status/handlers.py index 21bc21ae4..5294439b2 100644 --- a/src/backends/protocols/gamespy/game_status/handlers.py +++ b/src/backends/protocols/gamespy/game_status/handlers.py @@ -1,3 +1,4 @@ +from backends.library.abstractions.contracts import OKResponse from backends.library.abstractions.handler_base import HandlerBase import backends.protocols.gamespy.game_status.data as data from backends.protocols.gamespy.game_status.requests import ( @@ -8,6 +9,7 @@ NewGameRequest, SetPlayerDataRequest, ) +from backends.protocols.gamespy.game_status.response import AuthGameResponse, AuthPlayerResponse, GetPlayerDataResponse, GetProfileIdResponse from frontends.gamespy.protocols.game_status.aggregations.enums import AuthMethod from frontends.gamespy.protocols.game_status.aggregations.exceptions import GSException from frontends.gamespy.protocols.game_status.contracts.results import ( @@ -15,11 +17,13 @@ AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult, + ) class AuthGameHandler(HandlerBase): _request: AuthGameRequest + response: AuthGameResponse def _data_operate(self) -> None: # generate session key @@ -33,6 +37,7 @@ def _result_construct(self) -> None: class AuthPlayerHandler(HandlerBase): _request: AuthPlayerRequest + response: AuthPlayerResponse def _data_operate(self): match self._request.auth_type: @@ -57,6 +62,7 @@ def _result_construct(self): class GetPlayerDataHandler(HandlerBase): _request: GetPlayerDataRequest + response: GetPlayerDataResponse def _data_operate(self): self.data = data.get_player_data( @@ -74,6 +80,7 @@ def _result_construct(self): class GetProfileIdHandler(HandlerBase): _request: GetProfileIdRequest + response: GetProfileIdResponse def _data_operate(self): self.data = data.get_profile_id_by_cdkey( @@ -87,10 +94,11 @@ def _result_construct(self): class NewGameHandler(HandlerBase): - _request: NewGameRequest """ find game based on the session key, and create a space for the game data """ + _request: NewGameRequest + response: OKResponse def _data_operate(self): self.data = data.create_new_game_data() @@ -105,6 +113,7 @@ def _data_operate(self): class UpdateGameHandler(HandlerBase): _request: SetPlayerDataRequest + response: OKResponse def _data_operate(self): raise NotImplementedError() diff --git a/src/backends/protocols/gamespy/game_status/response.py b/src/backends/protocols/gamespy/game_status/response.py new file mode 100644 index 000000000..0ae6f1c9a --- /dev/null +++ b/src/backends/protocols/gamespy/game_status/response.py @@ -0,0 +1,18 @@ +from backends.library.abstractions.contracts import DataResponse +from frontends.gamespy.protocols.game_status.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult + + +class AuthGameResponse(DataResponse): + result: AuthGameResult + + +class AuthPlayerResponse(DataResponse): + result: AuthPlayerResult + + +class GetPlayerDataResponse(DataResponse): + result: GetPlayerDataResult + + +class GetProfileIdResponse(DataResponse): + result: GetProfileIdResult diff --git a/src/backends/protocols/gamespy/game_traffic_relay/handlers.py b/src/backends/protocols/gamespy/game_traffic_relay/handlers.py index b90f38aae..1c539e5f7 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/handlers.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/handlers.py @@ -12,12 +12,13 @@ class GtrHeartBeatHandler(HandlerBase): _request: GtrHeartBeatRequest + response: OKResponse - def __init__(self, request: GtrHeartBeatRequest) -> None: - assert isinstance(request, GtrHeartBeatRequest) - self._request = request - self._result = None - self.logger = logging.getLogger("backend") + # def __init__(self, request: GtrHeartBeatRequest) -> None: + # assert isinstance(request, GtrHeartBeatRequest) + # self._request = request + # self._result = None + # self.logger = logging.getLogger("backend") def _data_operate(self) -> None: info = data.search_relay_server( diff --git a/src/backends/protocols/gamespy/game_traffic_relay/requests.py b/src/backends/protocols/gamespy/game_traffic_relay/requests.py index 69e8e43a8..a0e857760 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/requests.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/requests.py @@ -1,12 +1,19 @@ from uuid import UUID from pydantic import BaseModel +from backends.library.abstractions.contracts import RequestBase + """ There are 2 UpdateGTRServiceRequest class The other one is in frontends/gamespy/protocols/game_traffic_relay/contracts/general.py """ -class GtrHeartBeatRequest(BaseModel): + + +class GtrHeartBeatRequest(RequestBase): server_id: UUID public_ip_address: str public_port: int client_count: int + raw_request: None = None + client_ip: None = None + client_port: None = None diff --git a/src/backends/protocols/gamespy/game_traffic_relay/responses.py b/src/backends/protocols/gamespy/game_traffic_relay/responses.py new file mode 100644 index 000000000..f698368d2 --- /dev/null +++ b/src/backends/protocols/gamespy/game_traffic_relay/responses.py @@ -0,0 +1,5 @@ +from pydantic import BaseModel + + +class GetMyIPResponse(BaseModel): + ip: str diff --git a/src/backends/protocols/gamespy/natneg/handlers.py b/src/backends/protocols/gamespy/natneg/handlers.py index 54a6b3d86..46ef13b2b 100644 --- a/src/backends/protocols/gamespy/natneg/handlers.py +++ b/src/backends/protocols/gamespy/natneg/handlers.py @@ -1,10 +1,12 @@ from datetime import datetime, timezone from time import sleep +from backends.library.abstractions.contracts import OKResponse from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import InitPacketCaches, NatResultCaches import backends.protocols.gamespy.natneg.data as data from backends.protocols.gamespy.natneg.helpers import NatProtocolHelper, NatStrategy from backends.protocols.gamespy.natneg.requests import ConnectRequest, InitRequest, ReportRequest +from backends.protocols.gamespy.natneg.responses import ConnectResponse from frontends.gamespy.protocols.natneg.aggregations.enums import ( ConnectPacketStatus, NatClientIndex, @@ -15,6 +17,7 @@ class InitHandler(HandlerBase): _request: InitRequest + response: OKResponse def __init__(self, request: InitRequest) -> None: assert isinstance(request, InitRequest) @@ -53,6 +56,7 @@ class ConnectHandler(HandlerBase): _request: ConnectRequest _strategy: NatStrategy | None _is_valid: bool + response: ConnectResponse def __init__(self, request: ConnectRequest) -> None: super().__init__(request) @@ -154,6 +158,7 @@ def _result_construct(self) -> None: class ReportHandler(HandlerBase): _request: ReportRequest + response: OKResponse def _data_operate(self) -> None: init_cache = data.get_init_cache( diff --git a/src/backends/protocols/gamespy/natneg/requests.py b/src/backends/protocols/gamespy/natneg/requests.py index 9fae44fdf..9bed1fb24 100644 --- a/src/backends/protocols/gamespy/natneg/requests.py +++ b/src/backends/protocols/gamespy/natneg/requests.py @@ -9,8 +9,9 @@ from typing import Union import backends.library.abstractions.contracts as lib +from frontends.gamespy.protocols.natneg.contracts.results import ConnectResult - +# region Requests class RequestBase(lib.RequestBase): raw_request: str version: int @@ -62,3 +63,4 @@ class ReportRequest(CommonRequestBase): game_name: str nat_type: NatType mapping_scheme: NatPortMappingScheme + diff --git a/src/backends/protocols/gamespy/natneg/responses.py b/src/backends/protocols/gamespy/natneg/responses.py new file mode 100644 index 000000000..df4e215bd --- /dev/null +++ b/src/backends/protocols/gamespy/natneg/responses.py @@ -0,0 +1,7 @@ +# region Response +from backends.library.abstractions.contracts import DataResponse +from frontends.gamespy.protocols.natneg.contracts.results import ConnectResult + + +class ConnectResponse(DataResponse): + result: ConnectResult diff --git a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py index 477415c48..c21d6fa34 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py @@ -1,4 +1,4 @@ -from backends.library.abstractions.contracts import RequestBase +from backends.library.abstractions.contracts import OKResponse, RequestBase from backends.library.abstractions.handler_base import HandlerBase import backends.protocols.gamespy.presence_connection_manager.data as data from backends.protocols.gamespy.presence_connection_manager.requests import ( @@ -21,6 +21,7 @@ UpdateProfileRequest, UpdateUserInfoRequest, ) +from backends.protocols.gamespy.presence_connection_manager.responses import BlockListResponse, BuddyListResponse, GetProfileResponse, LoginResponse from frontends.gamespy.protocols.presence_connection_manager.aggregates.enums import ( LoginType, ) @@ -39,6 +40,7 @@ class KeepAliveHandler(HandlerBase): _request: KeepAliveRequest + response: OKResponse def _data_operate(self) -> None: data.update_online_time( @@ -48,6 +50,7 @@ def _data_operate(self) -> None: class LoginHandler(HandlerBase): _request: LoginRequest + response: LoginResponse def _data_operate(self) -> None: if self._request.type == LoginType.NICK_EMAIL: @@ -96,6 +99,7 @@ def _result_construct(self) -> None: class LogoutHandler(HandlerBase): _request: LogoutRequest + response: OKResponse def _data_operate(self) -> None: # data.update_online_status(user_id=, status=LoginStatus.DISCONNECTED) @@ -103,6 +107,8 @@ def _data_operate(self) -> None: class NewUserHandler(HandlerBase): + response: OKResponse + def __init__(self, request: NewUserRequest) -> None: raise NotImplementedError("Use presence search player newuser router") super().__init__(request) @@ -113,6 +119,7 @@ def __init__(self, request: NewUserRequest) -> None: class BuddyListHandler(HandlerBase): _request: BuddyListRequest + response: BuddyListResponse def _data_operate(self) -> None: self.data = data.get_buddy_list( @@ -126,6 +133,7 @@ def _result_construct(self) -> None: class BlockListHandler(HandlerBase): _request: BlockListRequest + response: BlockListResponse def _data_operate(self) -> None: self.data = data.get_block_list( @@ -133,7 +141,8 @@ def _data_operate(self) -> None: ) def _result_construct(self) -> None: - self._result = BlockListResult(profile_ids=self.data,operation_id=self._request.operation_id) + self._result = BlockListResult( + profile_ids=self.data, operation_id=self._request.operation_id) class BuddyStatusInfoHandler(HandlerBase): @@ -152,6 +161,7 @@ def __init__(self, request: RequestBase) -> None: class DelBuddyHandler(HandlerBase): _request: DelBuddyRequest + response: OKResponse def _data_operate(self) -> None: self.data = data.delete_friend_by_profile_id( @@ -161,6 +171,7 @@ def _data_operate(self) -> None: class AddBuddyHandler(HandlerBase): _request: AddBuddyRequest + response: OKResponse def _data_operate(self) -> None: data.add_friend_request( @@ -174,6 +185,7 @@ def _data_operate(self) -> None: class AddBlockHandler(HandlerBase): _request: AddBlockRequest + response: OKResponse def _data_operate(self) -> None: data.update_block( @@ -204,6 +216,7 @@ def _data_operate(self) -> None: class StatusHandler(HandlerBase): _request: StatusRequest + response: OKResponse def _data_operate(self) -> None: data.update_status( @@ -217,6 +230,7 @@ def _data_operate(self) -> None: class StatusInfoHandler(HandlerBase): _request: StatusInfoRequest + response: OKResponse def _data_operate(self) -> None: raise NotImplementedError() @@ -227,6 +241,7 @@ def _data_operate(self) -> None: class GetProfileHandler(HandlerBase): _request: GetProfileRequest + response: GetProfileResponse def _data_operate(self) -> None: self.data = data.get_profile_infos( @@ -236,7 +251,8 @@ def _data_operate(self) -> None: ) def _result_construct(self) -> None: - self._result = GetProfileResult(user_profile=self.data,operation_id=self._request.operation_id) + self._result = GetProfileResult( + user_profile=self.data, operation_id=self._request.operation_id) class NewProfileHandler(HandlerBase): @@ -245,6 +261,7 @@ class NewProfileHandler(HandlerBase): """ _request: NewProfileRequest + response: OKResponse def _data_operate(self) -> None: data.update_new_nick( @@ -257,6 +274,7 @@ def _data_operate(self) -> None: class RegisterCDKeyHandler(HandlerBase): _request: RegisterCDKeyRequest + response: OKResponse def _data_operate(self): data.update_cdkey( @@ -270,6 +288,7 @@ class RegisterNickHandler(HandlerBase): """ _request: RegisterNickRequest + response: OKResponse def _data_operate(self): data.update_uniquenick( @@ -284,6 +303,7 @@ def _data_operate(self): class UpdateProfileHandler(HandlerBase): _request: UpdateProfileRequest + response: OKResponse def _data_operate(self): data.update_profiles( @@ -293,6 +313,7 @@ def _data_operate(self): class UpdateUserInfoHandler(HandlerBase): _request: UpdateUserInfoRequest + response: OKResponse def _data_operate(self): data.update_profiles( diff --git a/src/backends/protocols/gamespy/presence_connection_manager/responses.py b/src/backends/protocols/gamespy/presence_connection_manager/responses.py new file mode 100644 index 000000000..d47ed6e11 --- /dev/null +++ b/src/backends/protocols/gamespy/presence_connection_manager/responses.py @@ -0,0 +1,18 @@ +from backends.library.abstractions.contracts import DataResponse +from frontends.gamespy.protocols.presence_connection_manager.contracts.results import BlockListResult, BuddyListResult, GetProfileResult, LoginResult + + +class LoginResponse(DataResponse): + result: LoginResult + + +class BuddyListResponse(DataResponse): + result: BuddyListResult + + +class BlockListResponse(DataResponse): + result: BlockListResult + + +class GetProfileResponse(DataResponse): + result: GetProfileResult diff --git a/src/backends/protocols/gamespy/presence_search_player/handlers.py b/src/backends/protocols/gamespy/presence_search_player/handlers.py index 6f19f4406..14482ae52 100644 --- a/src/backends/protocols/gamespy/presence_search_player/handlers.py +++ b/src/backends/protocols/gamespy/presence_search_player/handlers.py @@ -1,5 +1,6 @@ from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import Users, Profiles, SubProfiles +from backends.protocols.gamespy.chat.response import NicksResponse import backends.protocols.gamespy.presence_search_player.data as data from backends.protocols.gamespy.presence_search_player.requests import ( CheckRequest, @@ -12,6 +13,7 @@ UniqueSearchRequest, ValidRequest, ) +from backends.protocols.gamespy.presence_search_player.responses import CheckResponse, NewUserResponse, OthersListResponse, OthersResponse, SearchResponse, SearchUniqueResponse, UniqueSearchResponse, ValidResponse from frontends.gamespy.protocols.presence_search_player.aggregates.enums import ( SearchType, ) @@ -43,6 +45,7 @@ class CheckHandler(HandlerBase): _request: CheckRequest _result: CheckResult + response: CheckResponse def _data_operate(self) -> None: if not data.verify_email(self._request.email, self._session): @@ -70,6 +73,7 @@ def _result_construct(self) -> None: class NewUserHandler(HandlerBase): _request: NewUserRequest _result: NewUserResult + response: NewUserResponse def _data_operate(self) -> None: # check if user exist @@ -142,6 +146,7 @@ def _create_subprofile(self) -> None: class NicksHandler(HandlerBase): _request: NicksRequest _result: NicksResult + response: NicksResponse def _data_operate(self) -> None: self.temp_list = data.get_nick_and_unique_nick_list( @@ -162,6 +167,7 @@ def _result_construct(self) -> None: class OthersHandler(HandlerBase): _request: OthersRequest _result: OthersResult + response: OthersResponse def _data_operate(self) -> None: self._data: list = data.get_friend_info_list( @@ -191,6 +197,7 @@ def _result_construct(self) -> None: class OthersListHandler(HandlerBase): _request: OthersListRequest result: OthersListResult + response: OthersListResponse def _data_operate(self) -> None: self._data: list = data.get_matched_profile_info_list( @@ -212,6 +219,7 @@ def _result_construct(self) -> None: class SearchHandler(HandlerBase): _request: SearchRequest _result: SearchResult + response: SearchResponse def _data_operate(self) -> None: if self._request.request_type == SearchType.NICK_SEARCH: @@ -249,6 +257,7 @@ def _result_construct(self) -> None: class SearchUniqueHandler(HandlerBase): _request: SearchUniqueRequest _result: SearchUniqueResult + response: SearchUniqueResponse def _data_operate(self) -> None: self._data = data.get_matched_info_by_uniquenick_and_namespaceids( @@ -266,6 +275,7 @@ def _result_construct(self) -> None: class UniqueSearchHandler(HandlerBase): _request: UniqueSearchRequest _result: UniqueSearchResult + response: UniqueSearchResponse def _data_operate(self) -> None: self._is_exist = data.is_uniquenick_exist( @@ -284,6 +294,7 @@ def _result_construct(self) -> None: class ValidHandler(HandlerBase): _request: ValidRequest _result: ValidResult + response: ValidResponse def _data_operate(self) -> None: self._is_exist = data.is_email_exist( diff --git a/src/backends/protocols/gamespy/presence_search_player/responses.py b/src/backends/protocols/gamespy/presence_search_player/responses.py new file mode 100644 index 000000000..75c432ca0 --- /dev/null +++ b/src/backends/protocols/gamespy/presence_search_player/responses.py @@ -0,0 +1,38 @@ +from backends.library.abstractions.contracts import DataResponse +from frontends.gamespy.protocols.chat.contracts.results import NickResult +from frontends.gamespy.protocols.presence_search_player.contracts.results import CheckResult, NewUserResult, OthersListResult, OthersResult, SearchResult, SearchUniqueResult, UniqueSearchResult, ValidResult + + +class CheckResponse(DataResponse): + result: CheckResult + + +class NewUserResponse(DataResponse): + result: NewUserResult + + +class NickResponse(DataResponse): + result: NickResult + + +class OthersResponse(DataResponse): + result: OthersResult + + +class OthersListResponse(DataResponse): + result: OthersListResult + + +class SearchResponse(DataResponse): + result: SearchResult + + +class SearchUniqueResponse(DataResponse): + result: SearchUniqueResult + + +class ValidResponse(DataResponse): + result: ValidResult + +class UniqueSearchResponse(DataResponse): + result:UniqueSearchResult \ No newline at end of file diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index cac25c6e2..09e38ac0b 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -26,40 +26,6 @@ def __expire_time(): return datetime.now() - timedelta(5) - -def get_all_groups() -> dict: - with Session(ENGINE) as session: - result = ( - session.query(Games, GroupList) - .join(GroupList, (Games.gameid == GroupList.gameid)) - .all() - ) - if TYPE_CHECKING: - result = cast(list[tuple[Games, GroupList]], result) - - # Group the results by Game name - grouped_result = {} - for game, group in result: - if game.gamename not in grouped_result: - temp_list = [] - grouped_result[game.gamename] = temp_list - temp_list.append( - { - "game_id": group.gameid, - "game_name": game.gamename, - "group_id": group.groupid, - "room_name": group.roomname, - "secret_key": game.secretkey, - } - ) - - # Convert the grouped result to the desired format - return grouped_result - - -PEER_GROUP_LIST = get_all_groups() - - def get_peer_staging_channels( game_name: str, group_id: int, session: Session ) -> list[GameServerInfo]: @@ -81,38 +47,40 @@ def get_peer_staging_channels( return data -def get_group_data_list_by_gamename(game_name: str) -> list[dict]: - assert isinstance(game_name, str) - if game_name not in PEER_GROUP_LIST: - raise ValueError(f"game name: {game_name} not in PEER_GROUP_LIST") - # the group id list length can not be 0 - result = PEER_GROUP_LIST[game_name] - assert len(result) != 0 - return result - def get_peer_group_channel( - group_data: list[dict], session: Session + game_name: str, session: Session ) -> list[PeerRoomInfo]: - assert isinstance(group_data, list) and all( - isinstance(id, dict) for id in group_data - ) - # Construct the group names based on the provided group_ids - group_names = [ - f"{PeerRoom.GroupRoomPrefix}!{item['group_id']}" for item in group_data - ] - # Query the database for channels matching the constructed group names - result = ( - session.query(ChatChannelCaches) - .filter(ChatChannelCaches.channel_name.in_(group_names)) + group_data = ( + session.query(GroupList) + .select_from(Games) + .join(GroupList, (Games.gameid == GroupList.gameid)) + .where(Games.gamename == game_name) .all() ) + group_info: list[PeerRoomInfo] = [] + for gd in group_data: + # get the group room info from GroupList - # Convert the result to a list of PeerRoomInfo objects - data = [PeerRoomInfo(**s.__dict__) for s in result] + # Construct the group names based on the provided group_ids + group_name = f"{PeerRoom.GroupRoomPrefix}!{gd.groupid}" + # Query the database for channels matching the constructed group names + result = ( + session.query(ChatChannelCaches) + .where(ChatChannelCaches.channel_name == group_name + ).first()) + assert isinstance(gd.groupid, int) + assert isinstance(gd.roomname, str) - return data + if result is None: + info = PeerRoomInfo(groupid=gd.groupid, + game_name=game_name, hostname=gd.roomname) + else: + info = PeerRoomInfo(**result.__dict__) + + group_info.append(info) + return group_info def get_game_server_cache_by_ip_port(ip: str, port: int, session: Session) -> GameServerCaches | None: @@ -280,5 +248,5 @@ def clean_expired_game_server_cache(session: Session): ).delete() session.commit() - if __name__ == "__main__": - get_all_groups() +if __name__ == "__main__": + pass diff --git a/src/backends/protocols/gamespy/query_report/handlers.py b/src/backends/protocols/gamespy/query_report/handlers.py index 0de2200db..2cdf9272b 100644 --- a/src/backends/protocols/gamespy/query_report/handlers.py +++ b/src/backends/protocols/gamespy/query_report/handlers.py @@ -1,5 +1,4 @@ -import asyncio -from datetime import datetime +from backends.library.abstractions.contracts import OKResponse from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import ENGINE, GameServerCaches from backends.protocols.gamespy.query_report.requests import ( @@ -15,10 +14,12 @@ class AvaliableHandler(HandlerBase): _request: AvaliableRequest + response: OKResponse class ChallengeHandler(HandlerBase): _request: HeartBeatRequest + response: OKResponse def _data_operate(self) -> None: with Session(ENGINE) as session: @@ -37,6 +38,7 @@ def _data_operate(self) -> None: class HeartbeatHandler(HandlerBase): _request: HeartBeatRequest + response: OKResponse def _data_operate(self) -> None: # clean the expired server cache @@ -88,6 +90,7 @@ def _data_operate(self) -> None: class KeepAliveHandler(HandlerBase): _request: KeepAliveRequest + response: OKResponse def _data_operate(self) -> None: assert isinstance(self._request.instant_key, str) @@ -97,6 +100,7 @@ def _data_operate(self) -> None: class ClientMessageHandler(HandlerBase): _request: ClientMessageRequest + response: OKResponse def _response_construct(self) -> None: # todo use websocket to send the message to qr client, but how to determine which qr router should be received diff --git a/src/backends/protocols/gamespy/query_report/responses.py b/src/backends/protocols/gamespy/query_report/responses.py new file mode 100644 index 000000000..e69de29bb diff --git a/src/backends/protocols/gamespy/server_browser/handlers.py b/src/backends/protocols/gamespy/server_browser/handlers.py index 4855a25c3..94963eb0b 100644 --- a/src/backends/protocols/gamespy/server_browser/handlers.py +++ b/src/backends/protocols/gamespy/server_browser/handlers.py @@ -1,5 +1,6 @@ from typing import TYPE_CHECKING, cast from uuid import UUID +from backends.library.abstractions.contracts import RequestBase from backends.library.abstractions.handler_base import HandlerBase from backends.protocols.gamespy.query_report.broker import BROCKER, MANAGER import backends.protocols.gamespy.query_report.data as data @@ -11,6 +12,7 @@ ServerInfoRequest, ServerListRequest, ) +from backends.protocols.gamespy.server_browser.responses import P2PGroupRoomListResponse, SendMessageResponse, ServerFullInfoListResponse, ServerInfoResponse from frontends.gamespy.protocols.query_report.aggregates.game_server_info import ( GameServerInfo, ) @@ -29,52 +31,34 @@ ServerMainListResult, ServerFullInfoListResult, ) +from backends.protocols.gamespy.server_browser.responses import ServerInfoResponse, ServerMainListResponse # region Server list -class ServerNetworkInfoListHandler(HandlerBase): - _request: ServerListRequest - _caches: list[GameServerInfo] - - def _data_operate(self): - self._caches = data.get_server_info_list_with_game_name( - self._request.game_name, self._session - ) - - def _result_construct(self): - assert isinstance(self._caches, list) and all( - isinstance(item, GameServerInfo) for item in self._caches - ) - if TYPE_CHECKING: - self._caches = cast(list[GameServerInfo], self._caches) - self._result = ServerFullInfoListResult( - client_remote_ip=self._request.client_ip, - flag=GameServerFlags.HAS_KEYS_FLAG, - game_secret_key="", - servers_info=self._caches, - keys=self._request.keys - ) - - class P2PGroupRoomListHandler(HandlerBase): _request: ServerListRequest _caches: list[PeerRoomInfo] + response: P2PGroupRoomListResponse + + def __init__(self, request: ServerListRequest) -> None: + assert isinstance(request, ServerListRequest) + super().__init__(request) def _data_operate(self): - group_data = data.get_group_data_list_by_gamename( - self._request.game_name) - self._caches = data.get_peer_group_channel(group_data, self._session) + self._secret_key = data.get_secret_key( + self._request.game_name, self._session) + self._caches = data.get_peer_group_channel( + self._request.game_name, self._session) def _result_construct(self) -> None: assert isinstance(self._caches, list) and all( isinstance(item, PeerRoomInfo) for item in self._caches ) - if TYPE_CHECKING: - self._caches = cast(list[PeerRoomInfo], self._caches) + self._result = P2PGroupRoomListResult( client_remote_ip=self._request.client_ip, flag=GameServerFlags.HAS_KEYS_FLAG, - game_secret_key="", + game_secret_key=self._secret_key, peer_room_info=self._caches, keys=self._request.keys ) @@ -84,6 +68,7 @@ class ServerMainListHandler(HandlerBase): _request: ServerListRequest _caches: list[GameServerInfo] _secret_key: str + response: ServerMainListResponse def _data_operate(self): self._secret_key = data.get_secret_key( @@ -114,6 +99,7 @@ def _result_construct(self): class ServerFullInfoListHandler(HandlerBase): _request: ServerListRequest _caches: list[GameServerInfo] + response: ServerFullInfoListResponse def _data_operate(self): self._secret_key = data.get_secret_key( @@ -151,6 +137,7 @@ class SendMessageHandler(HandlerBase): client -> server browser -> backend -> sb-SendMessageHandler -> qr-ClientMessageHandler -> websocket -> query report -> client """ _request: SendMessageRequest + response: SendMessageResponse def _data_operate(self): # construct client message and invoke frontends qr server client message @@ -178,6 +165,11 @@ def _data_operate(self): class ServerInfoHandler(HandlerBase): _request: ServerInfoRequest + response: ServerInfoResponse + + def __init__(self, request: ServerInfoRequest) -> None: + assert isinstance(request, ServerInfoRequest) + super().__init__(request) def _data_operate(self) -> None: self._data = data.get_server_info_with_ip_and_port( diff --git a/src/backends/protocols/gamespy/server_browser/responses.py b/src/backends/protocols/gamespy/server_browser/responses.py new file mode 100644 index 000000000..da9e4d58c --- /dev/null +++ b/src/backends/protocols/gamespy/server_browser/responses.py @@ -0,0 +1,29 @@ +from backends.library.abstractions.contracts import DataResponse +from frontends.gamespy.protocols.server_browser.v2.contracts.results import ServerFullInfoListResult +from frontends.gamespy.protocols.server_browser.v2.contracts.results import ( + P2PGroupRoomListResult, + SendMessageResult, + UpdateServerInfoResult, + ServerMainListResult, + ServerFullInfoListResult, +) + + +class ServerFullInfoListResponse(DataResponse): + result: ServerFullInfoListResult + + +class P2PGroupRoomListResponse(DataResponse): + result: P2PGroupRoomListResult + + +class SendMessageResponse(DataResponse): + result: SendMessageResult + + +class ServerInfoResponse(DataResponse): + result: UpdateServerInfoResult + + +class ServerMainListResponse(DataResponse): + result: ServerMainListResult diff --git a/src/backends/protocols/gamespy/web_services/handlers.py b/src/backends/protocols/gamespy/web_services/handlers.py index bcf6ca895..2f4e452dd 100644 --- a/src/backends/protocols/gamespy/web_services/handlers.py +++ b/src/backends/protocols/gamespy/web_services/handlers.py @@ -15,6 +15,7 @@ LoginUniqueNickRequest, SearchForRecordsRequest, ) +from backends.protocols.gamespy.web_services.responses import CreateRecordResponse, GetMyRecordsResponse, LoginProfileResponse, LoginRemoteAuthRepsonse, LoginUniqueNickResponse, SearchForRecordsResponse from frontends.gamespy.protocols.web_services.modules.auth.contracts.results import ( LoginProfileResult, ) @@ -26,6 +27,7 @@ class LoginProfileHandler(HandlerBase): _request: LoginProfileRequest + response: LoginProfileResponse def _data_operate(self) -> None: self.data = data.get_info_by_cdkey_email( @@ -58,6 +60,7 @@ def _data_operate(self) -> None: class LoginRemoteAuthHandler(HandlerBase): _request: LoginRemoteAuthRequest + response: LoginRemoteAuthRepsonse def _data_operate(self) -> None: self.data = data.get_info_by_authtoken( @@ -79,6 +82,7 @@ def _result_construct(self) -> None: class LoginUniqueNickHandler(HandlerBase): _request: LoginUniqueNickRequest + response: LoginUniqueNickResponse def _data_operate(self) -> None: self.data = data.get_info_by_uniquenick( @@ -141,6 +145,7 @@ def __init__(self, request: RequestBase) -> None: class CreateRecordHandler(HandlerBase): _request: CreateRecordRequest + response: CreateRecordResponse def _data_operate(self) -> None: raise NotImplementedError() @@ -148,6 +153,7 @@ def _data_operate(self) -> None: class GetMyRecordsHandler(HandlerBase): _request: GetMyRecordsRequest + response: GetMyRecordsResponse def _data_operate(self): self.data = data.get_user_data(self._request.table_id, self._session) @@ -156,6 +162,7 @@ def _data_operate(self): class SearchForRecordsHandler(HandlerBase): _request: SearchForRecordsRequest + response: SearchForRecordsResponse def _data_operate(self) -> None: - return super()._data_operate() + raise NotImplementedError() diff --git a/src/backends/protocols/gamespy/web_services/responses.py b/src/backends/protocols/gamespy/web_services/responses.py new file mode 100644 index 000000000..f3c11cb4c --- /dev/null +++ b/src/backends/protocols/gamespy/web_services/responses.py @@ -0,0 +1,40 @@ +from backends.library.abstractions.contracts import DataResponse +from frontends.gamespy.protocols.web_services.modules.auth.contracts.results import LoginProfileResult, LoginPs3CertResult, LoginRemoteAuthResult, LoginUniqueNickResult +from frontends.gamespy.protocols.web_services.modules.direct2game.contracts.results import GetPurchaseHistoryResult +from frontends.gamespy.protocols.web_services.modules.sake.contracts.results import CreateRecordResult, GetMyRecordsResult, SearchForRecordsResult + + +class LoginProfileResponse(DataResponse): + result: LoginProfileResult + + +class LoginPS3CertRepsonse(DataResponse): + result: LoginPs3CertResult + + +class LoginRemoteAuthRepsonse(DataResponse): + result: LoginRemoteAuthResult + + +class LoginUniqueNickResponse(DataResponse): + result: LoginUniqueNickResult + + +class GetPurchaceHistoryResponse(DataResponse): + result: GetPurchaseHistoryResult + + +# class GetTargettedAdResponse(DataResponse): +# result: GetTargettedAdResult + + +class CreateRecordResponse(DataResponse): + result: CreateRecordResult + + +class GetMyRecordsResponse(DataResponse): + result: GetMyRecordsResult + + +class SearchForRecordsResponse(DataResponse): + result: SearchForRecordsResult diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 8b30faaaa..535823464 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -1,5 +1,4 @@ -import asyncio -from backends.library.abstractions.contracts import OKResponse +from backends.library.abstractions.contracts import RESPONSES_DEF, OKResponse, Response from backends.protocols.gamespy.chat.brocker import MANAGER, launch_brocker from backends.protocols.gamespy.chat.handlers import ( CdKeyHandler, @@ -17,6 +16,8 @@ PrivateHandler, PublishMessageHandler, QuitHandler, + SetKeyHandler, + TopicHandler, UserHandler, WhoIsHandler, ) @@ -41,6 +42,7 @@ PrivateRequest, PublishMessageRequest, QuitRequest, + SetCKeyRequest, SetChannelKeyRequest, SetGroupRequest, SetKeyRequest, @@ -51,16 +53,17 @@ WhoIsRequest, WhoRequest, ) +from backends.protocols.gamespy.chat.response import AtmResponse, CryptResponse, GetCkeyResponse, GetKeyResponse, JoinResponse, ListResponse, ModeResponse, NamesResponse, NicksResponse, NoticeResponse, PartResponse, PrivateResponse, SetCKeyResponse, SetChannelKeyResponse, TopicResponse, UtmResponse, WhoIsResponse, WhoResponse from backends.urls import CHAT -from fastapi import APIRouter, FastAPI, WebSocket, WebSocketDisconnect +from fastapi import APIRouter, FastAPI, WebSocket router = APIRouter(lifespan=launch_brocker) client_pool = {} -@router.post(f"{CHAT}/publish") -def publish_message(request: PublishMessageRequest): +@router.post(f"{CHAT}/PublishMessageHandler", responses=RESPONSES_DEF) +def publish_message(request: PublishMessageRequest) -> OKResponse: handler = PublishMessageHandler(request) handler.handle() return handler.response @@ -74,154 +77,162 @@ async def websocket_endpoint(ws: WebSocket): # region General -@router.post(f"{CHAT}/CdKeyHandler") -def cdkey(request: CdkeyRequest): +@router.post(f"{CHAT}/CdKeyHandler", responses=RESPONSES_DEF) +def cdkey(request: CdkeyRequest) -> Response: handler = CdKeyHandler(request) handler.handle() return handler.response -@router.post(f"{CHAT}/GetKeyHandler") -def getkey(request: GetKeyRequest): +@router.post(f"{CHAT}/GetKeyHandler", responses=RESPONSES_DEF) +def getkey(request: GetKeyRequest) -> GetKeyResponse: handler = GetKeyHandler(request) handler.handle() return handler.response -@router.post(f"{CHAT}/GetUdpRelayHandler") -def get_udp_relay(request: GetUdpRelayRequest): +@router.post(f"{CHAT}/GetUdpRelayHandler", responses=RESPONSES_DEF) +def get_udp_relay(request: GetUdpRelayRequest) -> Response: handler = GetUdpRelayHandler(request) handler.handle() return handler.response -@router.post(f"{CHAT}/InviteHandler") -def invite(request: InviteRequest): +@router.post(f"{CHAT}/InviteHandler", responses=RESPONSES_DEF) +def invite(request: InviteRequest) -> Response: handler = InviteHandler(request) handler.handle() return handler.response -@router.post(f"{CHAT}/ListHandler") -def list_data(request: ListRequest): +@router.post(f"{CHAT}/ListHandler", responses=RESPONSES_DEF) +def list_data(request: ListRequest) -> ListResponse: # handler = ListHandler raise NotImplementedError() -@router.post(f"{CHAT}/LoginHandler") -def login(request: LoginRequest): +@router.post(f"{CHAT}/LoginHandler", responses=RESPONSES_DEF) +def login(request: LoginRequest) -> Response: raise NotImplementedError() -@router.post(f"{CHAT}/NickHandler") -def nick(request: NickRequest): +@router.post(f"{CHAT}/NickHandler", responses=RESPONSES_DEF) +def nick(request: NickRequest) -> NicksResponse: handler = NickHandler(request) handler.handle() return handler.response -@router.post(f"{CHAT}/QuitHandler") -def quit(request: QuitRequest): +@router.post(f"{CHAT}/QuitHandler", responses=RESPONSES_DEF) +def quit(request: QuitRequest) -> Response: handler = QuitHandler(request) handler.handle() return handler.response -@router.post(f"{CHAT}/SetKeyHandler") -def set_key(request: SetKeyRequest): - raise NotImplementedError() +@router.post(f"{CHAT}/SetKeyHandler", responses=RESPONSES_DEF) +def set_key(request: SetKeyRequest) -> OKResponse: + handler = SetKeyHandler(request) + handler.handle + return handler.response -@router.post(f"{CHAT}/UserHandler") -def user(request: UserRequest): +@router.post(f"{CHAT}/UserHandler", responses=RESPONSES_DEF) +def user(request: UserRequest) -> Response: handler = UserHandler(request) handler.handle() return handler.response -@router.post(f"{CHAT}/UserIPHandler") -def user_ip(request: UserIPRequest): - print(request) +@router.post(f"{CHAT}/UserIPHandler", responses=RESPONSES_DEF) +def user_ip(request: UserIPRequest) -> OKResponse: return OKResponse() -@router.post(f"{CHAT}/WhoHandler") -def who(request: WhoRequest): +@router.post(f"{CHAT}/WhoHandler", responses=RESPONSES_DEF) +def who(request: WhoRequest) -> WhoResponse: raise NotImplementedError() -@router.post(f"{CHAT}/WhoIsHandler") -def whois(request: WhoIsRequest): +@router.post(f"{CHAT}/WhoIsHandler", responses=RESPONSES_DEF) +def whois(request: WhoIsRequest) -> WhoIsResponse: handler = WhoIsHandler(request) handler.handle() return handler.response # region channel -@router.post(f"{CHAT}/GetChannelKeyHandler") -def get_channel_key(request: GetChannelKeyRequest): +@router.post(f"{CHAT}/GetChannelKeyHandler", responses=RESPONSES_DEF) +def get_channel_key(request: GetChannelKeyRequest) -> Response: handler = GetChannelKeyHandler(request) handler.handle() return handler.response -@router.post(f"{CHAT}/GetCKeyHandler") -def get_ckey(request: GetCKeyRequest): +@router.post(f"{CHAT}/GetCKeyHandler", responses=RESPONSES_DEF) +def get_ckey(request: GetCKeyRequest) -> GetCkeyResponse: handler = GetCKeyHandler(request) handler.handle() return handler.response -@router.post(f"{CHAT}/JoinHandler") -def join(request: JoinRequest): +@router.post(f"{CHAT}/JoinHandler", responses=RESPONSES_DEF) +def join(request: JoinRequest) -> JoinResponse: handler = JoinHandler(request) handler.handle() return handler.response -@router.post(f"{CHAT}/KickHandler") -def kick(request: KickRequest): +@router.post(f"{CHAT}/KickHandler", responses=RESPONSES_DEF) +def kick(request: KickRequest) -> Response: raise NotImplementedError() -@router.post(f"{CHAT}/ModeHandler") -def mode(request: ModeRequest): +@router.post(f"{CHAT}/ModeHandler", responses=RESPONSES_DEF) +def mode(request: ModeRequest) -> ModeResponse | OKResponse: handler = ModeHandler(request) handler.handle() return handler.response -@router.post(f"{CHAT}/NamesHandler") -def names(request: NamesRequest): +@router.post(f"{CHAT}/NamesHandler", responses=RESPONSES_DEF) +def names(request: NamesRequest) -> NamesResponse: handler = NamesHandler(request) handler.handle() return handler.response -@router.post(f"{CHAT}/PartHandler") -def part(request: PartRequest): +@router.post(f"{CHAT}/PartHandler", responses=RESPONSES_DEF) +def part(request: PartRequest) -> PartResponse: handler = PartHandler(request) handler.handle() return handler.response -@router.post(f"{CHAT}/SetChannelKeyHandler") -def set_channel_key(request: SetChannelKeyRequest): +@router.post(f"{CHAT}/SetChannelKeyHandler", responses=RESPONSES_DEF) +def set_channel_key(request: SetChannelKeyRequest) -> SetChannelKeyResponse: raise NotImplementedError() -@router.post(f"{CHAT}/SetGroupHandler") -def set_group(request: SetGroupRequest): +@router.post(f"{CHAT}/SetCKeyHandler", responses=RESPONSES_DEF) +def set_c_key(request: SetCKeyRequest) -> SetCKeyResponse: raise NotImplementedError() -@router.post(f"{CHAT}/TopicHandler") -def topic(request: TopicRequest): +@router.post(f"{CHAT}/SetGroupHandler", responses=RESPONSES_DEF) +def set_group(request: SetGroupRequest) -> Response: raise NotImplementedError() -@router.post(f"{CHAT}/CryptHandler") -def crypt(request: CryptRequest): +@router.post(f"{CHAT}/TopicHandler", responses=RESPONSES_DEF) +def topic(request: TopicRequest) -> TopicResponse: + handler = TopicHandler(request) + handler.handle() + return handler.response + + +@router.post(f"{CHAT}/CryptHandler", responses=RESPONSES_DEF) +def crypt(request: CryptRequest) -> CryptResponse: handler = CryptHandler(request) handler.handle() return handler.response @@ -230,25 +241,25 @@ def crypt(request: CryptRequest): # region Message -@router.post(f"{CHAT}/ATMHandler") -def atm(request: AtmRequest): +@router.post(f"{CHAT}/ATMHandler", responses=RESPONSES_DEF) +def atm(request: AtmRequest) -> AtmResponse: raise NotImplementedError() -@router.post(f"{CHAT}/NoticeHandler") -def notice(request: NoticeRequest): +@router.post(f"{CHAT}/NoticeHandler", responses=RESPONSES_DEF) +def notice(request: NoticeRequest) -> NoticeResponse: raise NotImplementedError() -@router.post(f"{CHAT}/PrivateHandler") -def private(request: PrivateRequest): +@router.post(f"{CHAT}/PrivateHandler", responses=RESPONSES_DEF) +def private(request: PrivateRequest) -> PrivateResponse: handler = PrivateHandler(request) handler.handle() return handler.response -@router.post(f"{CHAT}/UTMHandler") -def utm(request: UtmRequest): +@router.post(f"{CHAT}/UTMHandler", responses=RESPONSES_DEF) +def utm(request: UtmRequest) -> UtmResponse: raise NotImplementedError() diff --git a/src/backends/routers/gamespy/game_stats.py b/src/backends/routers/gamespy/game_stats.py new file mode 100644 index 000000000..cc3d35211 --- /dev/null +++ b/src/backends/routers/gamespy/game_stats.py @@ -0,0 +1,73 @@ +from fastapi import APIRouter +from backends.library.abstractions.contracts import RESPONSES_DEF, OKResponse, Response +from backends.protocols.gamespy.game_status.handlers import ( + AuthGameHandler, + AuthPlayerHandler, + GetPlayerDataHandler, + NewGameHandler, + SetPlayerDataHandler, + UpdateGameHandler, +) +from backends.protocols.gamespy.game_status.requests import ( + AuthGameRequest, + AuthPlayerRequest, + GetPlayerDataRequest, + NewGameRequest, + SetPlayerDataRequest, + UpdateGameRequest, +) +from backends.protocols.gamespy.game_status.response import AuthGameResponse, AuthPlayerResponse, GetPlayerDataResponse +from backends.urls import GAMESTATUS + +router = APIRouter() + + +@router.post(f"{GAMESTATUS}/AuthGameHandler", responses=RESPONSES_DEF) +def auth_game(request: AuthGameRequest) -> AuthGameResponse: + handler = AuthGameHandler(request) + handler.handle() + return handler.response + + +@router.post(f"{GAMESTATUS}/AuthPlayerHandler", responses=RESPONSES_DEF) +def auth_player(request: AuthPlayerRequest) -> AuthPlayerResponse: + handler = AuthPlayerHandler(request) + handler.handle() + return handler.response + + +@router.post(f"{GAMESTATUS}/NewGameHandler", responses=RESPONSES_DEF) +def new_game(request: NewGameRequest) -> OKResponse: + handler = NewGameHandler(request) + handler.handle() + return handler.response + + +@router.post(f"{GAMESTATUS}/GetPlayerDataHandler", responses=RESPONSES_DEF) +def get_player_data(request: GetPlayerDataRequest) -> GetPlayerDataResponse: + handler = GetPlayerDataHandler(request) + handler.handle() + return handler.response + + +@router.post(f"{GAMESTATUS}/SetPlayerDataHandler", responses=RESPONSES_DEF) +def set_player_data(request: SetPlayerDataRequest) -> Response: + handler = SetPlayerDataHandler(request) + handler.handle() + return handler.response + + +@router.post(f"{GAMESTATUS}/UpdateGameHandler", responses=RESPONSES_DEF) +def updaet_game(request: UpdateGameRequest) -> OKResponse: + handler = UpdateGameHandler(request) + handler.handle() + return handler.response + + +if __name__ == "__main__": + import uvicorn + from fastapi import FastAPI + + app = FastAPI() + app.include_router(router) + uvicorn.run(app, host="0.0.0.0", port=8080) diff --git a/src/backends/routers/gamespy/game_traffic_relay.py b/src/backends/routers/gamespy/game_traffic_relay.py index 8b4d40d53..6a2340cd7 100644 --- a/src/backends/routers/gamespy/game_traffic_relay.py +++ b/src/backends/routers/gamespy/game_traffic_relay.py @@ -1,25 +1,26 @@ from fastapi import APIRouter, Request +from backends.library.abstractions.contracts import RESPONSES_DEF, Response from backends.protocols.gamespy.game_traffic_relay.handlers import ( GtrHeartBeatHandler, ) from backends.protocols.gamespy.game_traffic_relay.requests import ( GtrHeartBeatRequest, ) +from backends.protocols.gamespy.game_traffic_relay.responses import GetMyIPResponse from backends.urls import GAME_TRAFFIC_RELAY router = APIRouter() -@router.get(f"{GAME_TRAFFIC_RELAY}/get_my_ip") -def get_my_ip(request: Request): +@router.post(f"{GAME_TRAFFIC_RELAY}/get_my_ip", responses=RESPONSES_DEF) +def get_my_ip(request: Request) -> GetMyIPResponse: assert request.client - return {"ip": request.client.host} + return GetMyIPResponse(ip=request.client.host) -@router.post(f"{GAME_TRAFFIC_RELAY}/heartbeat") -def heartbeat(heartbeat: GtrHeartBeatRequest): - +@router.post(f"{GAME_TRAFFIC_RELAY}/heartbeat", responses=RESPONSES_DEF) +def heartbeat(heartbeat: GtrHeartBeatRequest) -> Response: handler = GtrHeartBeatHandler(heartbeat) handler.handle() return handler.response diff --git a/src/backends/routers/gamespy/gstats.py b/src/backends/routers/gamespy/gstats.py deleted file mode 100644 index 959689247..000000000 --- a/src/backends/routers/gamespy/gstats.py +++ /dev/null @@ -1,71 +0,0 @@ -from fastapi import APIRouter -from backends.protocols.gamespy.game_status.handlers import ( - AuthGameHandler, - AuthPlayerHandler, - GetPlayerDataHandler, - NewGameHandler, - SetPlayerDataHandler, - UpdateGameHandler, -) -from backends.protocols.gamespy.game_status.requests import ( - AuthGameRequest, - AuthPlayerRequest, - GetPlayerDataRequest, - NewGameRequest, - SetPlayerDataRequest, - UpdateGameRequest, -) -from backends.urls import GAMESTATUS - -router = APIRouter() - - -@router.post(f"{GAMESTATUS}/AuthGameHandler") -def auth_game(request: AuthGameRequest): - handler = AuthGameHandler(request) - handler.handle() - return handler._response - - -@router.post(f"{GAMESTATUS}/AuthPlayerHandler") -def auth_player(request: AuthPlayerRequest): - handler = AuthPlayerHandler(request) - handler.handle() - return handler._response - - -@router.post(f"{GAMESTATUS}/NewGameHandler") -def new_game(request: NewGameRequest): - handler = NewGameHandler(request) - handler.handle() - return handler._response - - -@router.post(f"{GAMESTATUS}/GetPlayerDataHandler") -def get_player_data(request: GetPlayerDataRequest): - handler = GetPlayerDataHandler(request) - handler.handle() - return handler._response - - -@router.post(f"{GAMESTATUS}/SetPlayerDataHandler") -def set_player_data(request: SetPlayerDataRequest): - handler = SetPlayerDataHandler(request) - handler.handle() - return handler._response - - -@router.post(f"{GAMESTATUS}/UpdateGameHandler") -def updaet_game(request: UpdateGameRequest): - handler = UpdateGameHandler(request) - handler.handle() - return handler._response - - -if __name__ == "__main__": - import uvicorn - from fastapi import FastAPI - - app = FastAPI() - app.include_router(router) - uvicorn.run(app, host="0.0.0.0", port=8080) diff --git a/src/backends/routers/gamespy/natneg.py b/src/backends/routers/gamespy/natneg.py index e7f1238c0..437edbc16 100644 --- a/src/backends/routers/gamespy/natneg.py +++ b/src/backends/routers/gamespy/natneg.py @@ -1,40 +1,41 @@ from fastapi import APIRouter -from backends.protocols.gamespy.chat.requests import PingRequest +from backends.library.abstractions.contracts import RESPONSES_DEF, DataResponse, OKResponse, Response from backends.protocols.gamespy.natneg.handlers import ConnectHandler, InitHandler, ReportHandler -from backends.protocols.gamespy.natneg.requests import AddressCheckRequest, ConnectRequest, ErtAckRequest, InitRequest, ReportRequest +from backends.protocols.gamespy.natneg.requests import AddressCheckRequest, ConnectRequest, ErtAckRequest, InitRequest, ReportRequest +from backends.protocols.gamespy.natneg.responses import ConnectResponse from backends.urls import NATNEG router = APIRouter() -@router.post(f"{NATNEG}/AddressCheckHandler") -def address_check(request: AddressCheckRequest): +@router.post(f"{NATNEG}/AddressCheckHandler", responses=RESPONSES_DEF) +def address_check(request: AddressCheckRequest) -> Response: raise NotImplementedError() -@router.post(f"{NATNEG}/ConnectHandler") -def connect(request: ConnectRequest): +@router.post(f"{NATNEG}/ConnectHandler", responses=RESPONSES_DEF) +def connect(request: ConnectRequest) -> ConnectResponse: handler = ConnectHandler(request) handler.handle() return handler.response -@router.post(f"{NATNEG}/ErtAckHandler") -def ert_ack(request: ErtAckRequest): +@router.post(f"{NATNEG}/ErtAckHandler", responses=RESPONSES_DEF) +def ert_ack(request: ErtAckRequest) -> Response: raise NotImplementedError() -@router.post(f"{NATNEG}/InitHandler") -def init(request: InitRequest): +@router.post(f"{NATNEG}/InitHandler", responses=RESPONSES_DEF) +def init(request: InitRequest) -> Response: handler = InitHandler(request) handler.handle() return handler.response -@router.post(f"{NATNEG}/ReportHandler") -def report(request: ReportRequest): +@router.post(f"{NATNEG}/ReportHandler", responses=RESPONSES_DEF) +def report(request: ReportRequest) -> OKResponse: handler = ReportHandler(request) handler.handle() return handler.response diff --git a/src/backends/routers/gamespy/presence_connection_manager.py b/src/backends/routers/gamespy/presence_connection_manager.py index 2fc13b128..e4fc021bc 100644 --- a/src/backends/routers/gamespy/presence_connection_manager.py +++ b/src/backends/routers/gamespy/presence_connection_manager.py @@ -1,97 +1,100 @@ from fastapi import APIRouter +from backends.library.abstractions.contracts import RESPONSES_DEF, OKResponse from backends.protocols.gamespy.chat.requests import RegisterNickRequest from backends.protocols.gamespy.presence_connection_manager.handlers import AddBlockHandler, GetProfileHandler, KeepAliveHandler, LoginHandler, LogoutHandler, NewProfileHandler, NewUserHandler, RegisterCDKeyHandler, RegisterNickHandler, StatusHandler, StatusInfoHandler, UpdateProfileHandler from backends.protocols.gamespy.presence_connection_manager.requests import GetProfileRequest, LoginRequest, LogoutRequest, NewProfileRequest, RegisterCDKeyRequest, StatusInfoRequest, StatusRequest, UpdateProfileRequest, KeepAliveRequest, NewUserRequest, AddBlockRequest +from backends.protocols.gamespy.presence_connection_manager.responses import GetProfileResponse, LoginResponse from backends.urls import PRESENCE_CONNECTION_MANAGER router = APIRouter() -@router.post(f"{PRESENCE_CONNECTION_MANAGER}/LoginHandler") -def login(request: LoginRequest): +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/LoginHandler", responses=RESPONSES_DEF) +def login(request: LoginRequest) -> LoginResponse: handler = LoginHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_CONNECTION_MANAGER}/LogoutHandler") -def logout(request: LogoutRequest): +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/LogoutHandler", responses=RESPONSES_DEF) +def logout(request: LogoutRequest) -> OKResponse: handler = LogoutHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_CONNECTION_MANAGER}/KeepAliveHandler") -def keep_alive(request: KeepAliveRequest): +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/KeepAliveHandler", responses=RESPONSES_DEF) +def keep_alive(request: KeepAliveRequest) -> OKResponse: handler = KeepAliveHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_CONNECTION_MANAGER}/NewUserHandler") -def new_user(request: NewUserRequest): +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/NewUserHandler", responses=RESPONSES_DEF) +def new_user(request: NewUserRequest) -> OKResponse: handler = NewUserHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_CONNECTION_MANAGER}/AddBlockHandler") -def add_block(request: AddBlockRequest): +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/AddBlockHandler", responses=RESPONSES_DEF) +def add_block(request: AddBlockRequest) -> OKResponse: handler = AddBlockHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_CONNECTION_MANAGER}/GetProfileHandler") -def get_profile(request: GetProfileRequest): +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/GetProfileHandler", responses=RESPONSES_DEF) +def get_profile(request: GetProfileRequest) -> GetProfileResponse: handler = GetProfileHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_CONNECTION_MANAGER}/NewProfileHandler") -def new_proflie(request: NewProfileRequest): +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/NewProfileHandler", responses=RESPONSES_DEF) +def new_proflie(request: NewProfileRequest) -> OKResponse: handler = NewProfileHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_CONNECTION_MANAGER}/RegisterCDKeyHandler") -def register_cdkey(request: RegisterCDKeyRequest): +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/RegisterCDKeyHandler", responses=RESPONSES_DEF) +def register_cdkey(request: RegisterCDKeyRequest) -> OKResponse: handler = RegisterCDKeyHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_CONNECTION_MANAGER}/RegisterNickHandler") -def register_nick(request: RegisterNickRequest): +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/RegisterNickHandler", responses=RESPONSES_DEF) +def register_nick(request: RegisterNickRequest) -> OKResponse: handler = RegisterNickHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_CONNECTION_MANAGER}/UpdateProfileHandler") -def update_profile(request: UpdateProfileRequest): +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/UpdateProfileHandler", responses=RESPONSES_DEF) +def update_profile(request: UpdateProfileRequest) -> OKResponse: handler = UpdateProfileHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_CONNECTION_MANAGER}/StatusHandler") -def status(request: StatusRequest): +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/StatusHandler", responses=RESPONSES_DEF) +def status(request: StatusRequest) -> OKResponse: handler = StatusHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_CONNECTION_MANAGER}/StatusInfoHandler") -def status_info(request: StatusInfoRequest): +@router.post(f"{PRESENCE_CONNECTION_MANAGER}/StatusInfoHandler", responses=RESPONSES_DEF) +def status_info(request: StatusInfoRequest) -> OKResponse: handler = StatusInfoHandler(request) handler.handle() return handler.response + if __name__ == "__main__": import uvicorn from fastapi import FastAPI diff --git a/src/backends/routers/gamespy/presence_search_player.py b/src/backends/routers/gamespy/presence_search_player.py index d44a8b006..896ae45e9 100644 --- a/src/backends/routers/gamespy/presence_search_player.py +++ b/src/backends/routers/gamespy/presence_search_player.py @@ -1,75 +1,82 @@ from fastapi import APIRouter +from backends.library.abstractions.contracts import RESPONSES_DEF + +from backends.protocols.gamespy.chat.response import NicksResponse from backends.protocols.gamespy.presence_search_player.handlers import CheckHandler, NewUserHandler, NicksHandler, OthersHandler, OthersListHandler, SearchHandler, SearchUniqueHandler, UniqueSearchHandler, ValidHandler + from backends.protocols.gamespy.presence_search_player.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest + +from backends.protocols.gamespy.presence_search_player.responses import CheckResponse, NewUserResponse, OthersListResponse, OthersResponse, SearchResponse, SearchUniqueResponse, UniqueSearchResponse, ValidResponse + from backends.urls import PRESENCE_SEARCH_PLAYER router = APIRouter() -@router.post(f"{PRESENCE_SEARCH_PLAYER}/CheckHandler") -def check(request: CheckRequest): +@router.post(f"{PRESENCE_SEARCH_PLAYER}/CheckHandler", responses=RESPONSES_DEF) +def check(request: CheckRequest) -> CheckResponse: handler = CheckHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_SEARCH_PLAYER}/NewUserHandler") -def new_user(request: NewUserRequest): +@router.post(f"{PRESENCE_SEARCH_PLAYER}/NewUserHandler", responses=RESPONSES_DEF) +def new_user(request: NewUserRequest) -> NewUserResponse: handler = NewUserHandler(request) handler.handle() - handler.response + return handler.response -@router.post(f"{PRESENCE_SEARCH_PLAYER}/NicksHandler") -def nicks(request: NicksRequest): +@router.post(f"{PRESENCE_SEARCH_PLAYER}/NicksHandler", responses=RESPONSES_DEF) +def nicks(request: NicksRequest) -> NicksResponse: handler = NicksHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_SEARCH_PLAYER}/OthersHandler") -def others(request: OthersRequest): +@router.post(f"{PRESENCE_SEARCH_PLAYER}/OthersHandler", responses=RESPONSES_DEF) +def others(request: OthersRequest) -> OthersResponse: handler = OthersHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_SEARCH_PLAYER}/OthersListHandler") -def others_list(request: OthersListRequest): +@router.post(f"{PRESENCE_SEARCH_PLAYER}/OthersListHandler", responses=RESPONSES_DEF) +def others_list(request: OthersListRequest) -> OthersListResponse: handler = OthersListHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_SEARCH_PLAYER}/PMatchHandler") +@router.post(f"{PRESENCE_SEARCH_PLAYER}/PMatchHandler", responses=RESPONSES_DEF) def player_match(request: dict): raise NotImplementedError() -@router.post(f"{PRESENCE_SEARCH_PLAYER}/SearchHandler") -def search(request: SearchRequest): +@router.post(f"{PRESENCE_SEARCH_PLAYER}/SearchHandler", responses=RESPONSES_DEF) +def search(request: SearchRequest) -> SearchResponse: handler = SearchHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_SEARCH_PLAYER}/SearchUniqueHandler") -def search_unique(request: SearchUniqueRequest): +@router.post(f"{PRESENCE_SEARCH_PLAYER}/SearchUniqueHandler", responses=RESPONSES_DEF) +def search_unique(request: SearchUniqueRequest) -> SearchUniqueResponse: handler = SearchUniqueHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_SEARCH_PLAYER}/UniqueSearchHandler") -def unique_search(request: UniqueSearchRequest): +@router.post(f"{PRESENCE_SEARCH_PLAYER}/UniqueSearchHandler", responses=RESPONSES_DEF) +def unique_search(request: UniqueSearchRequest) -> UniqueSearchResponse: handler = UniqueSearchHandler(request) handler.handle() return handler.response -@router.post(f"{PRESENCE_SEARCH_PLAYER}/ValidHandler") -def valid(request: ValidRequest): +@router.post(f"{PRESENCE_SEARCH_PLAYER}/ValidHandler", responses=RESPONSES_DEF) +def valid(request: ValidRequest) -> ValidResponse: handler = ValidHandler(request) handler.handle() return handler.response diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index 6c0031216..c5abfec0b 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -1,4 +1,5 @@ from fastapi import APIRouter, WebSocket +from backends.library.abstractions.contracts import RESPONSES_DEF, OKResponse from backends.protocols.gamespy.query_report.broker import MANAGER, launch_brocker from backends.protocols.gamespy.query_report.handlers import ( AvaliableHandler, HeartbeatHandler, KeepAliveHandler) @@ -15,37 +16,37 @@ async def websocket_endpoint(ws: WebSocket): await MANAGER.process_websocket(ws) -@router.post(f"{QUERY_REPORT}/HeartbeatHandler") -def heartbeat(request: HeartBeatRequest): +@router.post(f"{QUERY_REPORT}/HeartbeatHandler", responses=RESPONSES_DEF) +def heartbeat(request: HeartBeatRequest)->OKResponse: handler = HeartbeatHandler(request) handler.handle() - return handler._response + return handler.response -@router.post(f"{QUERY_REPORT}/ChallengeHanler") -def challenge(request: ChallengeRequest): +@router.post(f"{QUERY_REPORT}/ChallengeHanler", responses=RESPONSES_DEF) +def challenge(request: ChallengeRequest)->OKResponse: raise NotImplementedError() -@router.post(f"{QUERY_REPORT}/AvailableHandler") -def available(request: AvaliableRequest): +@router.post(f"{QUERY_REPORT}/AvailableHandler", responses=RESPONSES_DEF) +def available(request: AvaliableRequest)->OKResponse: handler = AvaliableHandler(request) handler.handle() - return handler._response + return handler.response -@router.post(f"{QUERY_REPORT}/ClientMessageAckHandler") -def client_message(request: ClientMessageRequest): +@router.post(f"{QUERY_REPORT}/ClientMessageAckHandler", responses=RESPONSES_DEF) +def client_message(request: ClientMessageRequest)->OKResponse: raise NotImplementedError() -@router.post(f"{QUERY_REPORT}/EchoHandler") -def echo(request: EchoRequest): +@router.post(f"{QUERY_REPORT}/EchoHandler", responses=RESPONSES_DEF) +def echo(request: EchoRequest)->OKResponse: raise NotImplementedError() -@router.post(f"{QUERY_REPORT}/KeepAliveHandler") -def keep_alive(request: KeepAliveRequest): +@router.post(f"{QUERY_REPORT}/KeepAliveHandler", responses=RESPONSES_DEF) +def keep_alive(request: KeepAliveRequest)->OKResponse: handler = KeepAliveHandler(request) handler.handle() return handler.response diff --git a/src/backends/routers/gamespy/server_browser.py b/src/backends/routers/gamespy/server_browser.py index 45f13b192..a38448b77 100644 --- a/src/backends/routers/gamespy/server_browser.py +++ b/src/backends/routers/gamespy/server_browser.py @@ -1,6 +1,8 @@ from fastapi import APIRouter, WebSocket, WebSocketDisconnect -from backends.protocols.gamespy.server_browser.handlers import ServerFullInfoListHandler, ServerInfoHandler, ServerMainListHandler +from backends.library.abstractions.contracts import RESPONSES_DEF, OKResponse +from backends.protocols.gamespy.server_browser.handlers import P2PGroupRoomListHandler, ServerFullInfoListHandler, ServerInfoHandler, ServerMainListHandler from backends.protocols.gamespy.server_browser.requests import SendMessageRequest, ServerInfoRequest, ServerListRequest +from backends.protocols.gamespy.server_browser.responses import P2PGroupRoomListResponse, ServerFullInfoListResponse, ServerInfoResponse, ServerMainListResponse from backends.urls import SERVER_BROWSER_V2 router = APIRouter() @@ -20,25 +22,33 @@ def check(websocket: WebSocket): try: data = websocket.receive_text() client_pool[websocket.client.host] = websocket - websocket.send_text(f"Message text was: {data}") + websocket.send_text( + f"Message text was: {data}", responses=RESPONSES_DEF) except WebSocketDisconnect: del client_pool[websocket.client.host] -@router.post(f"{SERVER_BROWSER_V2}/SendMessageHandler") -def send_message(request: SendMessageRequest): +@router.post(f"{SERVER_BROWSER_V2}/SendMessageHandler", responses=RESPONSES_DEF) +def send_message(request: SendMessageRequest) -> OKResponse: raise NotImplementedError() -@router.post(f"{SERVER_BROWSER_V2}/ServerInfoHandler") -def server_info(request: ServerInfoRequest): +@router.post(f"{SERVER_BROWSER_V2}/P2PGroupRoomListHandler", responses=RESPONSES_DEF) +def p2p_group_room_list(request: ServerListRequest) -> P2PGroupRoomListResponse: + handler = P2PGroupRoomListHandler(request) + handler.handle() + return handler.response + + +@router.post(f"{SERVER_BROWSER_V2}/ServerInfoHandler", responses=RESPONSES_DEF) +def server_info(request: ServerInfoRequest) -> ServerInfoResponse: handler = ServerInfoHandler(request) handler.handle() return handler.response -@router.post(f"{SERVER_BROWSER_V2}/ServerMainListHandler") -def server_list(request: ServerListRequest): +@router.post(f"{SERVER_BROWSER_V2}/ServerMainListHandler", responses=RESPONSES_DEF) +def server_list(request: ServerListRequest) -> ServerMainListResponse: """ we send all server data to client to make it have HAS_FULL_RULES_FLAG and will not send ServerBrowserAuxUpdateServer(sb, server, async, fullUpdate) to """ @@ -47,8 +57,8 @@ def server_list(request: ServerListRequest): return handler.response -@router.post(f"{SERVER_BROWSER_V2}/ServerFullInfoListHandler") -def full_info_list(request: ServerListRequest): +@router.post(f"{SERVER_BROWSER_V2}/ServerFullInfoListHandler", responses=RESPONSES_DEF) +def full_info_list(request: ServerListRequest) -> ServerFullInfoListResponse: handler = ServerFullInfoListHandler(request) handler.handle() return handler.response diff --git a/src/backends/routers/gamespy/webservices.py b/src/backends/routers/gamespy/webservices.py index 9da42826c..5c0034b07 100644 --- a/src/backends/routers/gamespy/webservices.py +++ b/src/backends/routers/gamespy/webservices.py @@ -1,6 +1,8 @@ from fastapi import APIRouter +from backends.library.abstractions.contracts import RESPONSES_DEF, OKResponse from backends.protocols.gamespy.web_services.handlers import CreateRecordHandler, GetMyRecordsHandler, LoginProfileHandler, LoginRemoteAuthHandler, LoginUniqueNickHandler, SearchForRecordsHandler +from backends.protocols.gamespy.web_services.responses import CreateRecordResponse, GetMyRecordsResponse, LoginProfileResponse, LoginRemoteAuthRepsonse, LoginUniqueNickResponse, SearchForRecordsResponse from backends.urls import WEB_SERVICES from backends.protocols.gamespy.web_services.requests import CreateRecordRequest, GetMyRecordsRequest, LoginProfileRequest, LoginRemoteAuthRequest, LoginUniqueNickRequest, SearchForRecordsRequest @@ -9,106 +11,106 @@ # Altas services -@router.post(f"{WEB_SERVICES}/CreateRecordHandler") +@router.post(f"{WEB_SERVICES}/CreateRecordHandler", responses=RESPONSES_DEF) def create_matchless_session(request): raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/CreateSessionHandler") +@router.post(f"{WEB_SERVICES}/CreateSessionHandler", responses=RESPONSES_DEF) def create_session(request): raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/SetReportIntentionHandler") +@router.post(f"{WEB_SERVICES}/SetReportIntentionHandler", responses=RESPONSES_DEF) def set_report_intention(request): raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/SubmitReportHandler") +@router.post(f"{WEB_SERVICES}/SubmitReportHandler", responses=RESPONSES_DEF) def submit_report(request): raise NotImplementedError() # Auth services -@router.post(f"{WEB_SERVICES}/LoginProfileHandler") -def login_profile(request: LoginProfileRequest): +@router.post(f"{WEB_SERVICES}/LoginProfileHandler", responses=RESPONSES_DEF) +def login_profile(request: LoginProfileRequest) -> LoginProfileResponse: handler = LoginProfileHandler(request) handler.handle() return handler.response -@router.post(f"{WEB_SERVICES}/LoginProfileWithGameIdHandler") -def login_profile_with_game_id(request: LoginProfileRequest): +@router.post(f"{WEB_SERVICES}/LoginProfileWithGameIdHandler", responses=RESPONSES_DEF) +def login_profile_with_game_id(request: LoginProfileRequest) -> LoginProfileResponse: return login_profile(request) -@router.post(f"{WEB_SERVICES}/LoginRemoteAuthHandler") -def login_remote_auth(request: LoginRemoteAuthRequest): +@router.post(f"{WEB_SERVICES}/LoginRemoteAuthHandler", responses=RESPONSES_DEF) +def login_remote_auth(request: LoginRemoteAuthRequest) -> LoginRemoteAuthRepsonse: handler = LoginRemoteAuthHandler(request) handler.handle() return handler.response -@router.post(f"{WEB_SERVICES}/LoginRemoteAuthWithGameIdHandler") -def login_remote_auth_with_game_id(request: LoginRemoteAuthRequest): +@router.post(f"{WEB_SERVICES}/LoginRemoteAuthWithGameIdHandler", responses=RESPONSES_DEF) +def login_remote_auth_with_game_id(request: LoginRemoteAuthRequest) -> LoginRemoteAuthRepsonse: return login_remote_auth(request) -@router.post(f"{WEB_SERVICES}/LoginUniqueNickHandler") -def login_uniquenick(request: LoginUniqueNickRequest): +@router.post(f"{WEB_SERVICES}/LoginUniqueNickHandler", responses=RESPONSES_DEF) +def login_uniquenick(request: LoginUniqueNickRequest) -> LoginUniqueNickResponse: handler = LoginUniqueNickHandler(request) handler.handle() return handler.response -@router.post(f"{WEB_SERVICES}/LoginUniqueNickWithGameIdHandler") -def login_uniquenick_with_game_id(request: LoginUniqueNickRequest): +@router.post(f"{WEB_SERVICES}/LoginUniqueNickWithGameIdHandler", responses=RESPONSES_DEF) +def login_uniquenick_with_game_id(request: LoginUniqueNickRequest) -> LoginUniqueNickResponse: return login_uniquenick(request) # SAKE services -@router.post(f"{WEB_SERVICES}/CreateRecordHandler") -def create_record(request: CreateRecordRequest): +@router.post(f"{WEB_SERVICES}/CreateRecordHandler", responses=RESPONSES_DEF) +def create_record(request: CreateRecordRequest) -> CreateRecordResponse: handler = CreateRecordHandler(request) handler.handle() return handler.response -@router.post(f"{WEB_SERVICES}/DeleteRecordHandler") -def delete_record(request): +@router.post(f"{WEB_SERVICES}/DeleteRecordHandler", responses=RESPONSES_DEF) +def delete_record(request) -> OKResponse: raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/GetMyRecordsHandler") -def get_my_records(request: GetMyRecordsRequest): +@router.post(f"{WEB_SERVICES}/GetMyRecordsHandler", responses=RESPONSES_DEF) +def get_my_records(request: GetMyRecordsRequest) -> GetMyRecordsResponse: handler = GetMyRecordsHandler(request) handler.handle() return handler.response -@router.post(f"{WEB_SERVICES}/GetRandomRecordsHandler") +@router.post(f"{WEB_SERVICES}/GetRandomRecordsHandler", responses=RESPONSES_DEF) def get_random_records(request): raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/GetRecordLimitHandler") +@router.post(f"{WEB_SERVICES}/GetRecordLimitHandler", responses=RESPONSES_DEF) def get_record_limit(request): raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/RateRecordHandler") +@router.post(f"{WEB_SERVICES}/RateRecordHandler", responses=RESPONSES_DEF) def rate_record(request): raise NotImplementedError() -@router.post(f"{WEB_SERVICES}/SearchForRecordsHandler") -def search_for_records(request: SearchForRecordsRequest): +@router.post(f"{WEB_SERVICES}/SearchForRecordsHandler", responses=RESPONSES_DEF) +def search_for_records(request: SearchForRecordsRequest) -> SearchForRecordsResponse: handler = SearchForRecordsHandler(request) handler.handle() return handler.response -@router.post(f"{WEB_SERVICES}/UpdateRecordHandler") +@router.post(f"{WEB_SERVICES}/UpdateRecordHandler", responses=RESPONSES_DEF) def update_record(request): raise NotImplementedError() diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index 083cadcce..b18d3c721 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -6,6 +6,7 @@ from pydantic import BaseModel import uvicorn +from backends.library.abstractions.contracts import ErrorResponse from backends.library.database.pg_orm import ENGINE from backends.services.register import register_services from frontends.gamespy.library.exceptions.general import UniSpyException @@ -13,8 +14,8 @@ from frontends.gamespy.library.configs import ServerConfig from backends.routers.gamespy import ( chat, + game_stats, game_traffic_relay, - gstats, natneg, presence_connection_manager, presence_search_player, @@ -26,7 +27,7 @@ app = FastAPI() app.include_router(chat.router) -app.include_router(gstats.router) +app.include_router(game_stats.router) app.include_router(game_traffic_relay.router) app.include_router(natneg.router) app.include_router(presence_connection_manager.router) @@ -40,26 +41,31 @@ @app.exception_handler(UniSpyException) def unispy_exception_handler(_, exc: UniSpyException): + str_error = exc.message logger.error(exc.message) - return JSONResponse({"error": exc.message, "exception": type(exc).__name__}, status_code=450) + err_resp = ErrorResponse( + message=str_error, exception_name=type(exc).__name__) + return JSONResponse(err_resp.model_dump(mode="json"), status_code=450) @app.exception_handler(RequestValidationError) def validation_exception_handler(_, exc: RequestValidationError): str_error = str(exc.args) logger.error(str_error) - return JSONResponse({"error": str_error}, status_code=status.HTTP_400_BAD_REQUEST) + err_resp = ErrorResponse( + message=str_error, exception_name=type(exc).__name__) + return JSONResponse(err_resp.model_dump(mode="json"), status_code=status.HTTP_400_BAD_REQUEST) @app.exception_handler(Exception) -def handle_unispy_exception(_, exc: Exception): +def general_exception_handler(_, exc: Exception): str_error = str(exc) if len(str_error) == 0: str_error = exc.__class__.__name__ logger.error(str_error) - return JSONResponse( - {"error": str_error}, status_code=status.HTTP_500_INTERNAL_SERVER_ERROR - ) + err_resp = ErrorResponse( + message=str_error, exception_name=type(exc).__name__) + return JSONResponse(err_resp.model_dump(mode="json"), status_code=status.HTTP_500_INTERNAL_SERVER_ERROR) @app.post("/") diff --git a/src/backends/tests/gamespy/chat/room_tests.py b/src/backends/tests/gamespy/chat/room_tests.py index 078f5d961..6ab04502f 100644 --- a/src/backends/tests/gamespy/chat/room_tests.py +++ b/src/backends/tests/gamespy/chat/room_tests.py @@ -332,6 +332,163 @@ def test_sdk(self): print(e) pass + def test_peer(self): + """ + peer test in gamespy sdk + """ + test_msg = [ + { + "raw_request": "CRYPT des 1 gmtest", + "command_name": "CRYPT", + "version_id": "1", + "gamename": "gmtest", + "websocket_address": "127.0.0.1:59754", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 59258 + }, + { + "raw_request": "USRIP", + "command_name": "USRIP", + "websocket_address": "127.0.0.1:59754", + "remote_ip": "172.19.0.5", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 59258 + }, + { + "raw_request": "USER ChatCUser 127.0.0.1 unispy_server_dev :ChatCName", + "command_name": "USER", + "user_name": "ChatCUser", + "local_ip_address": "127.0.0.1", + "server_name": "unispy_server_dev", + "name": "ChatCName", + "websocket_address": "127.0.0.1:59754", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 59258 + }, + { + "raw_request": "NICK ChatC238", + "command_name": "NICK", + "nick_name": "ChatC238", + "websocket_address": "127.0.0.1:59754", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 59258 + }, + { + "raw_request": "JOIN #GSP!gmtest ", + "password": None, + "command_name": "JOIN", + "channel_name": "#GSP!gmtest", + "websocket_address": "127.0.0.1:59754", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 59258 + }, + { + "raw_request": "NAMES #GSP!gmtest", + "command_name": "NAMES", + "channel_name": "#GSP!gmtest", + "websocket_address": "127.0.0.1:59754", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 59258 + }, + { + "raw_request": "MODE #GSP!gmtest", + "command_name": "MODE", + "channel_name": "#GSP!gmtest", + "mode_operations": {}, + "request_type": 0, + "websocket_address": "127.0.0.1:59754", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 59258 + }, + { + "raw_request": "PRIVMSG #GSP!gmtest :Hi", + "command_name": "PRIVMSG", + "channel_name": "#GSP!gmtest", + "type": 0, + "target_name": "#GSP!gmtest", + "message": "Hi", + "websocket_address": "127.0.0.1:59754", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 59258 + }, + { + "raw_request": "NAMES #GSP!gmtest", + "command_name": "NAMES", + "channel_name": "#GSP!gmtest", + "websocket_address": "127.0.0.1:59754", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 59258 + }, + { + "raw_request": "WHOIS ChatC238", + "command_name": "WHOIS", + "nick_name": "ChatC238", + "websocket_address": "127.0.0.1:59754", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 59258 + }, + { + "raw_request": "PRIVMSG #GSP!gmtest :Bye", + "command_name": "PRIVMSG", + "channel_name": "#GSP!gmtest", + "type": 0, + "target_name": "#GSP!gmtest", + "message": "Bye", + "websocket_address": "127.0.0.1:59754", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 59258 + }, + { + "raw_request": "PART #GSP!gmtest :", + "command_name": "PART", + "channel_name": "#GSP!gmtest", + "reason": "", + "websocket_address": "127.0.0.1:59754", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 59258 + }, + { + "raw_request": "QUIT :Later!", + "command_name": "QUIT", + "reason": "Later!", + "websocket_address": "127.0.0.1:59754", + "client_ip": "172.19.0.5", + "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", + "client_port": 59258 + } + ] + test_api = ["GameSpy/Chat/CryptHandler", + "GameSpy/Chat/UserIPHandler", + "GameSpy/Chat/UserHandler", + "GameSpy/Chat/NickHandler", + "GameSpy/Chat/JoinHandler", + "GameSpy/Chat/NamesHandler", + "GameSpy/Chat/ModeHandler", + "GameSpy/Chat/PrivateHandler", + "GameSpy/Chat/NamesHandler", + "GameSpy/Chat/WhoIsHandler", + "GameSpy/Chat/PrivateHandler", + "GameSpy/Chat/PartHandler", + "GameSpy/Chat/QuitHandler"] + for m, route in zip(test_msg, test_api): + try: + print(route) + client.post(url=route, json=m) + except: + pass + if __name__ == "__main__": test = RoomTest() diff --git a/src/backends/tests/gamespy/query_report/data_fetch_tests.py b/src/backends/tests/gamespy/query_report/data_fetch_tests.py index 3dc64c0c4..1f572cee9 100644 --- a/src/backends/tests/gamespy/query_report/data_fetch_tests.py +++ b/src/backends/tests/gamespy/query_report/data_fetch_tests.py @@ -8,10 +8,6 @@ class DataFetchTests(unittest.TestCase): - def test_get_all_groups(self): - self.assertIsNotNone(data.PEER_GROUP_LIST) - self.assertIsInstance(data.PEER_GROUP_LIST, dict) - def test_get_peer_staging_channels(self): cache = ChatChannelCaches( channel_name="#GSP!unispy_test_game_name!*", diff --git a/src/backends/tests/gamespy/query_report/handler_tests.py b/src/backends/tests/gamespy/query_report/handler_tests.py index fa9f495f1..c591613ad 100644 --- a/src/backends/tests/gamespy/query_report/handler_tests.py +++ b/src/backends/tests/gamespy/query_report/handler_tests.py @@ -11,7 +11,7 @@ def test_heartbeat(self): req = HeartBeatRequest(**request) handler = HeartbeatHandler(req) handler.handle() - handler._response + handler.response def test_available(self): request = {"raw_request": "\\t\\u0000\\u0000\\u0000\\u0000\\t\\u0000\\u0000\\u0000\\u0000gamespy\\u0000", "command_name": 9, diff --git a/src/backends/tests/gamespy/server_browser/filter_tests.py b/src/backends/tests/gamespy/server_browser/filter_tests.py index 937b37de5..9c1dfe2d6 100644 --- a/src/backends/tests/gamespy/server_browser/filter_tests.py +++ b/src/backends/tests/gamespy/server_browser/filter_tests.py @@ -2,69 +2,70 @@ from asteval import Interpreter import re -# 步骤1:创建 Interpreter 实例 -aeval = Interpreter() +if __name__ == "__main__": + # 步骤1:创建 Interpreter 实例 + aeval = Interpreter() -# 步骤2:定义服务器数据字典列表 -server_data_list = [ - { - "natneg": "1", - "gamever": "2.00", - "gravity": 800, - "mapname": "gmtmap2", - "gamemode": "openplaying", - "gamename": "gmtest2", - "gametype": "arena", - "hostname": "Server 1", - "hostport": "25000", - "localip0": "172.19.0.4", - "numteams": 2, - "teamplay": 1, - "fraglimit": 0, - "localport": 11111, - "rankingon": 1, - "timelimit": 40, - "maxplayers": 32, - "numplayers": 8, - "statechanged": 1, - }, - { - "natneg": "1", - "gamever": "2.00", - "gravity": 600, - "mapname": "gmtmap2", - "gamemode": "openplaying", - "gamename": "gmtest2", - "gametype": "arena", - "hostname": "Server 2", - "hostport": "25000", - "localip0": "172.19.0.4", - "numteams": 2, - "teamplay": 1, - "fraglimit": 0, - "localport": 11111, - "rankingon": 1, - "timelimit": 40, - "maxplayers": 32, - "numplayers": 4, - "statechanged": 1, - } -] + # 步骤2:定义服务器数据字典列表 + server_data_list = [ + { + "natneg": "1", + "gamever": "2.00", + "gravity": 800, + "mapname": "gmtmap2", + "gamemode": "openplaying", + "gamename": "gmtest2", + "gametype": "arena", + "hostname": "Server 1", + "hostport": "25000", + "localip0": "172.19.0.4", + "numteams": 2, + "teamplay": 1, + "fraglimit": 0, + "localport": 11111, + "rankingon": 1, + "timelimit": 40, + "maxplayers": 32, + "numplayers": 8, + "statechanged": 1, + }, + { + "natneg": "1", + "gamever": "2.00", + "gravity": 600, + "mapname": "gmtmap2", + "gamemode": "openplaying", + "gamename": "gmtest2", + "gametype": "arena", + "hostname": "Server 2", + "hostport": "25000", + "localip0": "172.19.0.4", + "numteams": 2, + "teamplay": 1, + "fraglimit": 0, + "localport": 11111, + "rankingon": 1, + "timelimit": 40, + "maxplayers": 32, + "numplayers": 4, + "statechanged": 1, + } + ] -# 步骤3:定义条件 -condition = "gravity > 700 and numplayers > 5 and gamemode == 'openplaying'" + # 步骤3:定义条件 + condition = "gravity > 700 and numplayers > 5 and gamemode == 'openplaying'" -# 步骤4:提取条件中的键 -pattern = r'(\w+)\s*([<>!=]+)\s*([\'\"]?)(.*?)\3' -matches = re.findall(pattern, condition) -keys = [match[0] for match in matches] -print(f"提取的键: {keys}") + # 步骤4:提取条件中的键 + pattern = r'(\w+)\s*([<>!=]+)\s*([\'\"]?)(.*?)\3' + matches = re.findall(pattern, condition) + keys = [match[0] for match in matches] + print(f"提取的键: {keys}") -# 步骤5:评估每个服务器数据字典的条件 -for server in server_data_list: - # 更新上下文:动态加载提取的键 - for key in keys: - if key in server: - aeval.symtable[key] = server[key] - result = aeval(condition) - print(f"服务器 {server['hostname']} (Gravity: {server['gravity']}, Players: {server['numplayers']}): {result}") + # 步骤5:评估每个服务器数据字典的条件 + for server in server_data_list: + # 更新上下文:动态加载提取的键 + for key in keys: + if key in server: + aeval.symtable[key] = server[key] + result = aeval(condition) + print(f"服务器 {server['hostname']} (Gravity: {server['gravity']}, Players: {server['numplayers']}): {result}") From b2f0b495b8f7816d10f72ed92fb72304051d9d1c Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Fri, 31 Oct 2025 01:18:29 +0000 Subject: [PATCH 221/231] Fix: wrong devcontainer config post create cmd --- src/.devcontainer/devcontainer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/.devcontainer/devcontainer.json b/src/.devcontainer/devcontainer.json index 1278d764e..42e5ae6ff 100644 --- a/src/.devcontainer/devcontainer.json +++ b/src/.devcontainer/devcontainer.json @@ -26,7 +26,7 @@ 80 ], // Use 'postCreateCommand' to run commands after the container is created. - "postCreateCommand": "sudo tee -a /etc/hosts && pip3 install --user -r requirements.txt", + "postCreateCommand": "pip3 install --user -r requirements.txt", "customizations": { "vscode": { "extensions": [ From ceafdb3daa2ff29203be1dac95631b5a789e637c Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Fri, 31 Oct 2025 06:45:19 +0000 Subject: [PATCH 222/231] Update: debug helper print error --- .../gamespy/library/abstractions/server_launcher.py | 2 -- src/frontends/gamespy/library/exceptions/general.py | 2 ++ src/frontends/gamespy/library/extentions/debug_helper.py | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index a8c34075e..58e20664d 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -129,7 +129,6 @@ def __init__( launchers: list[ServerLauncherBase] ): self._lauchers = launchers - self.stop_event = Event() # Event for stopping threads def start(self): self._connect_to_backend() @@ -182,7 +181,6 @@ def _keep_running(self): sleep(1) pass except KeyboardInterrupt: - self.stop_event.set() for info in self._lauchers: info.stop() print("\nUniSpy shutdown.") diff --git a/src/frontends/gamespy/library/exceptions/general.py b/src/frontends/gamespy/library/exceptions/general.py index 43ee310f3..fd2a9b0f4 100644 --- a/src/frontends/gamespy/library/exceptions/general.py +++ b/src/frontends/gamespy/library/exceptions/general.py @@ -26,6 +26,8 @@ def handle_exception(e: Exception, client: Optional["ClientBase"] = None): if issubclass(type(e), UniSpyException): ex: UniSpyException = e # type:ignore client.log_error(ex.message) + elif issubclass(type(e), BrokenPipeError): + client.log_warn(f"client disconnect before message send") else: client.log_error(traceback.format_exc()) # if we are unittesting we raise the exception out diff --git a/src/frontends/gamespy/library/extentions/debug_helper.py b/src/frontends/gamespy/library/extentions/debug_helper.py index fc658bbe0..41c2f707c 100644 --- a/src/frontends/gamespy/library/extentions/debug_helper.py +++ b/src/frontends/gamespy/library/extentions/debug_helper.py @@ -6,7 +6,7 @@ from watchdog.events import FileSystemEventHandler from watchdog.observers import Observer if TYPE_CHECKING: - from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase, ServerFactory + from frontends.gamespy.library.abstractions.server_launcher import ServerFactory class FileChangeHandler(FileSystemEventHandler): @@ -82,7 +82,9 @@ def __start_with_watch(self): self._observer.start() while True: time.sleep(1) - except: + except Exception as e: + print(str(e)) + finally: self._observer.stop() def __start_normal(self): From c5be6fe4ae6b611a23fcd86b52ffce909f9f92ce Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Fri, 31 Oct 2025 06:46:13 +0000 Subject: [PATCH 223/231] Fix(chat): database table missing primary key --- common/UniSpy_pg.sql | 3 ++- src/.vscode/launch.json | 16 +++++++++++- src/backends/library/database/pg_orm.py | 9 +++---- .../protocols/gamespy/chat/handlers.py | 25 +++++++++++-------- src/backends/protocols/gamespy/chat/helper.py | 5 +++- .../protocols/gamespy/query_report/data.py | 23 ++++++++++++++--- .../gamespy/query_report/handlers.py | 4 +-- src/backends/routers/gamespy/chat.py | 5 +++- .../protocols/chat/applications/handlers.py | 4 +-- 9 files changed, 67 insertions(+), 27 deletions(-) diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 8672d43c4..9a2f8aef6 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -131,7 +131,7 @@ CREATE TABLE unispy.chat_user_caches ( -- CREATE TABLE unispy.chat_channel_caches ( - channel_name character varying NOT NULL PRIMARY KEY, + channel_name character varying PRIMARY KEY NOT NULL, server_id uuid NOT NULL, creator character varying, game_name character varying NOT NULL, @@ -159,6 +159,7 @@ ALTER TABLE unispy.chat_user_caches OWNER TO unispy; -- CREATE TABLE unispy.chat_channel_user_caches ( + id SERIAL PRIMARY KEY NOT NULL, nick_name character varying NOT NULL, user_name character varying NOT NULL, channel_name character varying NOT NULL, diff --git a/src/.vscode/launch.json b/src/.vscode/launch.json index bf3556f6b..1a94cc25d 100644 --- a/src/.vscode/launch.json +++ b/src/.vscode/launch.json @@ -16,7 +16,7 @@ "justMyCode": true }, { - "name": "backend", + "name": "backends", "type": "debugpy", "request": "launch", "module": "uvicorn", @@ -36,6 +36,20 @@ }, "justMyCode": true }, + { + "name": "frontends", + "type": "debugpy", + "request": "launch", + "program": "frontends/app.py", + "console": "integratedTerminal", + "args": [ + "--debug" + ], + "env": { + "PYTHONPATH": "${workspaceFolder}", + }, + "justMyCode": true + }, { "name": "pcm", "type": "debugpy", diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 5042c3253..960a6e3fc 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -281,16 +281,15 @@ class ChatUserCaches(Base): class ChatChannelUserCaches(Base): __tablename__ = "chat_channel_user_caches" + id = Column(Integer, primary_key=True, autoincrement=True) nick_name = Column( String, ForeignKey("chat_user_caches.nick_name"), - primary_key=True, nullable=False, ) user_name = Column( String, ForeignKey("chat_user_caches.user_name"), - primary_key=True, nullable=False, ) channel_name = Column( @@ -315,9 +314,9 @@ class GameServerCaches(Base): game_name = Column(String, nullable=False) query_report_port = Column(Integer, nullable=False) status = Column(IntEnum(GameServerStatus)) - player_data = Column(JSONB, nullable=False) - server_data = Column(JSONB, nullable=False) - team_data = Column(JSONB, nullable=False) + player_data = Column(JSONB, nullable=False, default={}) + server_data = Column(JSONB, nullable=False, default=[]) + team_data = Column(JSONB, nullable=False, default=[]) avaliable = Column(Boolean, nullable=True) update_time = Column(DateTime, nullable=False, default=datetime.now()) diff --git a/src/backends/protocols/gamespy/chat/handlers.py b/src/backends/protocols/gamespy/chat/handlers.py index 3bb3bb629..26ddb76c2 100644 --- a/src/backends/protocols/gamespy/chat/handlers.py +++ b/src/backends/protocols/gamespy/chat/handlers.py @@ -44,7 +44,7 @@ WhoIsRequest, WhoRequest, ) -from backends.protocols.gamespy.chat.response import AtmResponse, CryptResponse, GetCkeyResponse, GetKeyResponse, JoinResponse, KickResponse, ModeResponse, NamesResponse, NicksResponse, PartResponse, PingResponse, PrivateResponse, SetChannelKeyResponse, TopicResponse, UtmResponse, WhoIsResponse +from backends.protocols.gamespy.chat.response import AtmResponse, CryptResponse, GetCkeyResponse, GetKeyResponse, JoinResponse, KickResponse, ModeResponse, NamesResponse, NicksResponse, PartResponse, PingResponse, PrivateResponse, SetCKeyResponse, SetChannelKeyResponse, TopicResponse, UtmResponse, WhoIsResponse from frontends.gamespy.protocols.chat.aggregates.enums import ( GetKeyRequestType, ModeRequestType, @@ -101,20 +101,19 @@ def __init__(self, request: RequestBase) -> None: def _request_check(self) -> None: self._get_user() self._check_user() + self._session.commit() def _get_user(self): self._user = data.get_user_cache_by_ip_port( self._request.client_ip, self._request.client_port, self._session ) - if self._user is not None: - self._user.update_time = datetime.now() # type: ignore - self._session.commit() def _check_user(self): if self._user is None: raise NoSuchNickException( f"Can not find user with ip address: {self._request.client_ip}:{self._request.client_port}" ) + self._user.update_time = datetime.now() # type: ignore class ChannelHandlerBase(HandlerBase): @@ -128,7 +127,8 @@ def __init__(self, request: RequestBase) -> None: self._channel_user = None def _request_check(self) -> None: - super()._request_check() + self._get_user() + self._check_user() self._get_channel() self._check_channel() @@ -143,10 +143,13 @@ def _get_channel(self): ) def _get_channel_user(self): - self._channel_user = data.get_channel_user_cache_by_name_and_ip_port( + assert self._user is not None + # typechecking require this step + nick = self._user.nick_name + assert isinstance(nick, str) + self._channel_user = data.get_channel_user_cache_by_nick_name( self._request.channel_name, - self._request.client_ip, - self._request.client_port, + nick, self._session, ) @@ -482,7 +485,7 @@ def _request_check(self) -> None: def _data_operate(self) -> None: assert self._user is not None - + if self._channel is None: self._channel = ChannelHelper.create( server_id=self._request.server_id, @@ -497,6 +500,7 @@ def _data_operate(self) -> None: max_num_user=100, session=self._session, ) + self._channel_user = ChannelHelper.join( self._channel, self._user, self._session ) @@ -736,12 +740,13 @@ def _result_construct(self) -> None: ) -class SetCkeyHandler(ChannelHandlerBase): +class SetCKeyHandler(ChannelHandlerBase): """ todo check if set channel_user or user keyvalue or set for other channeluser keyvalue """ _request: SetCKeyRequest + response: SetCKeyResponse def _data_operate(self) -> None: assert self._channel_user is not None diff --git a/src/backends/protocols/gamespy/chat/helper.py b/src/backends/protocols/gamespy/chat/helper.py index c14ec88ff..c3f8e2a2f 100644 --- a/src/backends/protocols/gamespy/chat/helper.py +++ b/src/backends/protocols/gamespy/chat/helper.py @@ -83,15 +83,18 @@ def join( if channel.creator == user.nick_name: # type:ignore is_creator = True + is_operator = True else: is_creator = False + is_operator = False + chan_user = ChatChannelUserCaches( server_id=user.server_id, nick_name=user.nick_name, user_name=user.user_name, channel_name=channel.channel_name, is_voiceable=True, - is_channel_operator=False, + is_channel_operator=is_operator, is_channel_creator=is_creator, remote_ip=user.remote_ip, remote_port=user.remote_port, diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index 09e38ac0b..d0b5a499d 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -3,6 +3,7 @@ from backends.library.database.pg_orm import ( ENGINE, ChatChannelCaches, + ChatChannelUserCaches, GroupList, Games, GameServerCaches, @@ -26,6 +27,7 @@ def __expire_time(): return datetime.now() - timedelta(5) + def get_peer_staging_channels( game_name: str, group_id: int, session: Session ) -> list[GameServerInfo]: @@ -47,7 +49,6 @@ def get_peer_staging_channels( return data - def get_peer_group_channel( game_name: str, session: Session ) -> list[PeerRoomInfo]: @@ -75,9 +76,24 @@ def get_peer_group_channel( if result is None: info = PeerRoomInfo(groupid=gd.groupid, - game_name=game_name, hostname=gd.roomname) + game_name=game_name, + hostname=gd.roomname) else: - info = PeerRoomInfo(**result.__dict__) + assert isinstance(result.channel_name, str) + + assert isinstance(result.max_num_user, int) + # todo get the peer room extra info + player_count = session.query(ChatChannelUserCaches).where( + ChatChannelUserCaches.channel_name == result.channel_name).count() + password = result.password if result.password is not None else "" + assert isinstance(password, str) + info = PeerRoomInfo(groupid=gd.groupid, + game_name=game_name, + hostname=result.channel_name, + password=password, + maxplayers=result.max_num_user, + numplayers=player_count, + ) group_info.append(info) return group_info @@ -248,5 +264,6 @@ def clean_expired_game_server_cache(session: Session): ).delete() session.commit() + if __name__ == "__main__": pass diff --git a/src/backends/protocols/gamespy/query_report/handlers.py b/src/backends/protocols/gamespy/query_report/handlers.py index 2cdf9272b..c56840d7e 100644 --- a/src/backends/protocols/gamespy/query_report/handlers.py +++ b/src/backends/protocols/gamespy/query_report/handlers.py @@ -56,9 +56,7 @@ def _data_operate(self) -> None: if self._request.server_data is None: raise QRException( "server data in first heartbeat can not be null") - if self._request.team_data is None: - raise QRException( - "team data in first heartbeat can not be null") + # team data can be none in peertest sdk cache = GameServerCaches( instant_key=self._request.instant_key, server_id=self._request.server_id, diff --git a/src/backends/routers/gamespy/chat.py b/src/backends/routers/gamespy/chat.py index 535823464..018f3c436 100644 --- a/src/backends/routers/gamespy/chat.py +++ b/src/backends/routers/gamespy/chat.py @@ -16,6 +16,7 @@ PrivateHandler, PublishMessageHandler, QuitHandler, + SetCKeyHandler, SetKeyHandler, TopicHandler, UserHandler, @@ -216,7 +217,9 @@ def set_channel_key(request: SetChannelKeyRequest) -> SetChannelKeyResponse: @router.post(f"{CHAT}/SetCKeyHandler", responses=RESPONSES_DEF) def set_c_key(request: SetCKeyRequest) -> SetCKeyResponse: - raise NotImplementedError() + handler = SetCKeyHandler(request) + handler.handle() + return handler.response @router.post(f"{CHAT}/SetGroupHandler", responses=RESPONSES_DEF) diff --git a/src/frontends/gamespy/protocols/chat/applications/handlers.py b/src/frontends/gamespy/protocols/chat/applications/handlers.py index a70177ffa..bf7697f02 100644 --- a/src/frontends/gamespy/protocols/chat/applications/handlers.py +++ b/src/frontends/gamespy/protocols/chat/applications/handlers.py @@ -336,8 +336,8 @@ def __init__(self, client: Client, request: ModeRequest): def _request_check(self) -> None: super()._request_check() - # if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: - # self._is_fetching = False + if self._request.request_type == ModeRequestType.SET_CHANNEL_MODES: + self._is_fetching = False if self._request.request_type == [ ModeRequestType.SET_CHANNEL_MODES, ModeRequestType.SET_CHANNEL_USER_MODES, From 6f156751f8a0674253e862409ebe8ef6030a0ac5 Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Tue, 4 Nov 2025 03:10:58 +0000 Subject: [PATCH 224/231] Fix(gtr): server launch stuck issue --- .../game_traffic_relay/applications/server_launcher.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py index 4cf1f0c92..1fbbd5e1a 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py @@ -20,8 +20,11 @@ def __init__(self) -> None: client_cls=Client, server_cls=UdpServer, ) + + def start(self): self._get_public_ip() self._launch_connection_expire_checker() + super().start() def _get_public_ip(self): url = f"{CONFIG.backend.url}/GameSpy/GameTrafficRelay/get_my_ip" From c962a286e1771c0f2a6ed7bc9f51d8f1f4f1932e Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Fri, 7 Nov 2025 08:49:05 +0000 Subject: [PATCH 225/231] Fix(gs): issue with persistance sdk tests --- common/UniSpy_pg.sql | 7 +- src/backends/library/database/pg_orm.py | 4 +- .../protocols/gamespy/chat/requests.py | 1 + .../protocols/gamespy/game_status/data.py | 88 +++--- .../protocols/gamespy/game_status/handlers.py | 82 +++++- .../protocols/gamespy/game_status/requests.py | 20 +- .../protocols/gamespy/game_status/response.py | 6 +- .../presence_connection_manager/data.py | 69 +++-- .../presence_connection_manager/handlers.py | 5 +- .../protocols/gamespy/query_report/data.py | 15 +- src/backends/routers/gamespy/game_stats.py | 15 +- .../protocols/chat/contracts/requests.py | 12 +- .../game_status/abstractions/contracts.py | 12 +- .../game_status/applications/client.py | 13 +- .../game_status/applications/handlers.py | 12 +- .../game_status/contracts/requests.py | 145 ++++++---- .../game_status/contracts/responses.py | 15 +- .../game_status/contracts/results.py | 10 +- .../abstractions/contracts.py | 10 +- .../applications/handlers.py | 38 +-- .../contracts/requests.py | 262 +++++++++--------- .../abstractions/contracts.py | 14 +- .../contracts/requests.py | 164 +++++------ .../query_report/v1/abstractions/contracts.py | 8 +- .../gamespy/game_status/handler_tests.py | 15 +- .../tests/gamespy/game_status/mock_objects.py | 6 +- 26 files changed, 609 insertions(+), 439 deletions(-) diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 9a2f8aef6..753409b84 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -492,7 +492,8 @@ CREATE TABLE unispy.pstorage ( profileid integer NOT NULL, ptype integer NOT NULL, dindex integer NOT NULL, - data jsonb + data character varying NOT NULL, + update_time timestamp without time zone NOT NULL ); @@ -5336,7 +5337,7 @@ COPY unispy.messages (id, namespaceid, type, from_user, to_user, date, message) -- Data for Name: nat_result_caches; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.nat_result_caches (id, public_ip, private_ip, is_success, port_mapping_type, port_type, client_index, game_name, update_time) FROM stdin; +COPY unispy.nat_result_caches (id, public_ip, private_ip, is_success, port_mapping_scheme, port_type, client_index, game_name, update_time) FROM stdin; \. @@ -5388,7 +5389,7 @@ COPY unispy.sakestorage (id, tableid, data) FROM stdin; -- COPY unispy.subprofiles (subprofileid, profileid, uniquenick, namespaceid, partnerid, productid, gamename, cdkeyenc, firewall, port, authtoken, session_key) FROM stdin; -1 1 spyguy_test 0 1 1 gmtests encrypted_cdkey 0 8080 auth_token_example session_key_example +1 1 spyguy_test 0 1 1 gmtests 00000a308fd86a7eb92cbc8322b03a36 0 8080 example_auth 1111 \. diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 960a6e3fc..8df36133e 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -181,7 +181,9 @@ class PStorage(Base): "profiles.profileid"), nullable=False) ptype = Column(Integer, nullable=False) dindex = Column(Integer, nullable=False) - data = Column(JSONB) + data = Column(String,nullable=False) + update_time = Column(DateTime, nullable=False, default=datetime.now()) + class GameStatusSnapShot(Base): diff --git a/src/backends/protocols/gamespy/chat/requests.py b/src/backends/protocols/gamespy/chat/requests.py index 83f04f9e0..2e090447e 100644 --- a/src/backends/protocols/gamespy/chat/requests.py +++ b/src/backends/protocols/gamespy/chat/requests.py @@ -168,6 +168,7 @@ class PartRequest(ChannelRequestBase): class SetChannelKeyRequest(ChannelRequestBase): key_value_string: str key_values: dict[str, str] + is_broadcast: bool class SetCKeyRequest(ChannelRequestBase): diff --git a/src/backends/protocols/gamespy/game_status/data.py b/src/backends/protocols/gamespy/game_status/data.py index 9caad3bbb..50e141b83 100644 --- a/src/backends/protocols/gamespy/game_status/data.py +++ b/src/backends/protocols/gamespy/game_status/data.py @@ -1,3 +1,4 @@ +from datetime import datetime from typing import TYPE_CHECKING, cast from sqlalchemy import Column @@ -6,41 +7,57 @@ from frontends.gamespy.protocols.game_status.aggregations.exceptions import GSException from sqlalchemy.orm import Session + +def get_player_data(profile_id: int, storage_type: PersistStorageType, data_index: int, session: Session) -> PStorage | None: + result = (session.query(PStorage) + .where(PStorage.ptype == storage_type.value, + PStorage.dindex == data_index, + PStorage.profileid == profile_id).first()) + return result + + def create_new_game_data(): raise NotImplementedError() -def create_new_player_data(): - raise NotImplementedError() +def create_player_data(profile_id: int, ptype, dindex, data: str, session: Session) -> None: + pd = PStorage( + profileid=profile_id, + ptype=ptype, + dindex=dindex, + data=data + ) + session.add(pd) + session.commit() -def update_player_data(): - raise NotImplementedError() +def update_player_data(pd: PStorage, data: str, session: Session) -> None: + pd.data = data # type: ignore + pd.update_time = datetime.now() # type: ignore + session.commit() -def get_profile_id_by_token(token: str) -> int: +def get_profile_id_by_token(token: str, session: Session) -> int: assert isinstance(token, str) - with Session(ENGINE) as session: - result = session.query(SubProfiles.profileid).where( - SubProfiles.authtoken == token).first() - if result is None: - raise GSException("No records found in database") - assert isinstance(result, int) - return result + result = session.query(SubProfiles.profileid).where( + SubProfiles.authtoken == token).first() + if result is None: + raise GSException("No records found in database") + assert isinstance(result, int) + return result -def get_profile_id_by_profile_id(profile_id: int) -> int: +def get_profile_id_by_profile_id(profile_id: int, session: Session) -> int: assert isinstance(profile_id, int) - with Session(ENGINE) as session: - result = session.query(SubProfiles.profileid).where( - SubProfiles.profileid == profile_id).count() - if result != 1: - raise GSException(f"There is no profile_id {profile_id} existed") - assert isinstance(result, int) - return result + result = session.query(SubProfiles.profileid).where( + SubProfiles.profileid == profile_id).count() + if result != 1: + raise GSException(f"There is no profile_id {profile_id} existed") + assert isinstance(result, int) + return result -def get_profile_id_by_cdkey(cdkey: str, nick_name: str) -> int: +def get_profile_id_by_cdkey(cdkey: str, nick_name: str, session: Session) -> int: if TYPE_CHECKING: assert isinstance(Profiles.profileid, Column) assert isinstance(Profiles.userid, Column) @@ -49,26 +66,13 @@ def get_profile_id_by_cdkey(cdkey: str, nick_name: str) -> int: assert isinstance(Profiles.nick, Column) assert isinstance(SubProfiles.profileid, Column) assert isinstance(SubProfiles.cdkeyenc, Column) - with Session(ENGINE) as session: - result = session.query(SubProfiles.profileid).join( - SubProfiles, Profiles.profileid == SubProfiles.profileid)\ - .where(SubProfiles.cdkeyenc == cdkey, - Profiles.nick == nick_name)\ - .first() - if result is None: - raise GSException("No record found in database") - if TYPE_CHECKING: - result = cast(int, result) - return result - -def get_player_data(profile_id: int, storage_type: PersistStorageType, data_index: int) -> dict: - with Session(ENGINE) as session: - result = session.query(PStorage.data).where(PStorage.ptype == storage_type.value, - PStorage.dindex == data_index, - PStorage.profileid == profile_id).first() + result = (session.query(Profiles.profileid) + .join(SubProfiles) + .where(SubProfiles.cdkeyenc == cdkey, + Profiles.nick == nick_name) + .first()) if result is None: - raise GSException("No records found in database") - if TYPE_CHECKING: - result = cast(dict, result) - return result + raise GSException("No record found in database") + pid: int = result[0] + return pid diff --git a/src/backends/protocols/gamespy/game_status/handlers.py b/src/backends/protocols/gamespy/game_status/handlers.py index 5294439b2..eefff8ec2 100644 --- a/src/backends/protocols/gamespy/game_status/handlers.py +++ b/src/backends/protocols/gamespy/game_status/handlers.py @@ -1,3 +1,4 @@ +from datetime import datetime from backends.library.abstractions.contracts import OKResponse from backends.library.abstractions.handler_base import HandlerBase import backends.protocols.gamespy.game_status.data as data @@ -9,7 +10,7 @@ NewGameRequest, SetPlayerDataRequest, ) -from backends.protocols.gamespy.game_status.response import AuthGameResponse, AuthPlayerResponse, GetPlayerDataResponse, GetProfileIdResponse +from backends.protocols.gamespy.game_status.response import AuthGameResponse, AuthPlayerResponse, GetPlayerDataResponse, GetProfileIdResponse, SetPlayerDataResponse from frontends.gamespy.protocols.game_status.aggregations.enums import AuthMethod from frontends.gamespy.protocols.game_status.aggregations.exceptions import GSException from frontends.gamespy.protocols.game_status.contracts.results import ( @@ -17,8 +18,10 @@ AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult, + SetPlayerDataResult, ) +import base64 class AuthGameHandler(HandlerBase): @@ -32,7 +35,9 @@ def _data_operate(self) -> None: def _result_construct(self) -> None: self._result = AuthGameResult( - session_key=self.session_key, local_id=self._request.local_id, game_name=self._request.game_name) + session_key=self.session_key, + local_id=self._request.local_id, + game_name=self._request.game_name) class AuthPlayerHandler(HandlerBase): @@ -42,15 +47,27 @@ class AuthPlayerHandler(HandlerBase): def _data_operate(self): match self._request.auth_type: case AuthMethod.PARTNER_ID_AUTH: + if self._request.auth_token is None: + raise GSException("auth tocken is missing") self.data = data.get_profile_id_by_token( - token=self._request.auth_token) + token=self._request.auth_token, + session=self._session) case AuthMethod.PROFILE_ID_AUTH: + if self._request.profile_id is None: + raise GSException("profileid is missing") self.data = data.get_profile_id_by_profile_id( - profile_id=self._request.profile_id + profile_id=self._request.profile_id, + session=self._session ) case AuthMethod.CDKEY_AUTH: + if self._request.cdkey_hash is None: + raise GSException("cdkey hash is required") + if self._request.nick is None: + raise GSException("nick is missing") self.data = data.get_profile_id_by_cdkey( - cdkey=self._request.cdkey_hash, nick_name=self._request.nick + cdkey=self._request.cdkey_hash, + nick_name=self._request.nick, + session=self._session ) case _: raise GSException("Invalid auth type") @@ -65,17 +82,30 @@ class GetPlayerDataHandler(HandlerBase): response: GetPlayerDataResponse def _data_operate(self): - self.data = data.get_player_data( + self.pd = data.get_player_data( self._request.profile_id, self._request.storage_type, self._request.data_index, + self._session ) + if self.pd is None: + raise GSException("No records found in database") def _result_construct(self): + assert self.pd is not None + assert isinstance(self.pd.data, str) + assert isinstance(self.pd.update_time, datetime) + + if str(self.pd.data).endswith("=="): + data_formated = base64.b64decode(self.pd.data).decode() + else: + data_formated = self.pd.data + self._result = GetPlayerDataResult( - keyvalues=self.data, local_id=self._request.local_id, - profile_id=self._request.profile_id) + profile_id=self._request.profile_id, + data=data_formated, + modified=self.pd.update_time) class GetProfileIdHandler(HandlerBase): @@ -84,7 +114,9 @@ class GetProfileIdHandler(HandlerBase): def _data_operate(self): self.data = data.get_profile_id_by_cdkey( - cdkey=self._request.cdkey, nick_name=self._request.nick + cdkey=self._request.key_hash, + nick_name=self._request.nick, + session=self._session ) def _result_construct(self): @@ -106,9 +138,39 @@ def _data_operate(self): class SetPlayerDataHandler(HandlerBase): _request: SetPlayerDataRequest + _result: SetPlayerDataResult + response: SetPlayerDataResponse def _data_operate(self): - raise NotImplementedError() + if not self._request.is_key_value: + data_formated = base64.b64encode( + self._request.data.encode()).decode() + else: + data_formated = self._request.data + + pd = data.get_player_data(self._request.profile_id, + self._request.storage_type, + self._request.data_index, + self._session) + if pd is not None: + data.update_player_data(pd, + data_formated, + self._session) + else: + data.create_player_data( + self._request.profile_id, + self._request.storage_type, + self._request.data_index, + self._request.data, + self._session + ) + + def _result_construct(self) -> None: + self._result = SetPlayerDataResult( + local_id=self._request.local_id, + profile_id=self._request.profile_id, + modified=datetime.now() + ) class UpdateGameHandler(HandlerBase): diff --git a/src/backends/protocols/gamespy/game_status/requests.py b/src/backends/protocols/gamespy/game_status/requests.py index b21ed2242..febd40582 100644 --- a/src/backends/protocols/gamespy/game_status/requests.py +++ b/src/backends/protocols/gamespy/game_status/requests.py @@ -1,15 +1,16 @@ -from pydantic import Field +from pydantic import Field, model_validator import backends.library.abstractions.contracts as lib from frontends.gamespy.protocols.game_status.aggregations.enums import ( AuthMethod, PersistStorageType, ) +from frontends.gamespy.protocols.game_status.aggregations.exceptions import GSException class RequestBase(lib.RequestBase): raw_request: str local_id: int - request_dict: dict[str, str] + _request_dict: dict[str, str] class AuthGameRequest(RequestBase): @@ -18,11 +19,11 @@ class AuthGameRequest(RequestBase): class AuthPlayerRequest(RequestBase): auth_type: AuthMethod - profile_id: int - auth_token: str - response: str - cdkey_hash: str - nick: str + profile_id: int | None = None + auth_token: str | None = None + cdkey_hash: str | None = None + response: str | None = None + nick: str | None = None class GetPlayerDataRequest(RequestBase): @@ -35,7 +36,7 @@ class GetPlayerDataRequest(RequestBase): class GetProfileIdRequest(RequestBase): nick: str - cdkey: str + key_hash: str class NewGameRequest(RequestBase): @@ -52,8 +53,9 @@ class SetPlayerDataRequest(RequestBase): storage_type: PersistStorageType data_index: int length: int - report: str + report: str | None = None data: str + is_key_value: bool class UpdateGameRequest(RequestBase): diff --git a/src/backends/protocols/gamespy/game_status/response.py b/src/backends/protocols/gamespy/game_status/response.py index 0ae6f1c9a..459d7749e 100644 --- a/src/backends/protocols/gamespy/game_status/response.py +++ b/src/backends/protocols/gamespy/game_status/response.py @@ -1,5 +1,5 @@ from backends.library.abstractions.contracts import DataResponse -from frontends.gamespy.protocols.game_status.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult +from frontends.gamespy.protocols.game_status.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult, SetPlayerDataResult class AuthGameResponse(DataResponse): @@ -16,3 +16,7 @@ class GetPlayerDataResponse(DataResponse): class GetProfileIdResponse(DataResponse): result: GetProfileIdResult + + +class SetPlayerDataResponse(DataResponse): + result: SetPlayerDataResult diff --git a/src/backends/protocols/gamespy/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py index 416355b70..d9211f8bf 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/data.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/data.py @@ -32,11 +32,11 @@ from sqlalchemy.orm import Session -def is_email_exist(email: str): - return psp.is_email_exist(email) +def is_email_exist(email: str, session: Session): + return psp.is_email_exist(email, session) -def update_online_time(ip: str, port: int,session:Session): +def update_online_time(ip: str, port: int, session: Session): if TYPE_CHECKING: assert isinstance(Users.lastip, Column) @@ -47,8 +47,9 @@ def update_online_time(ip: str, port: int,session:Session): session.commit() -def delete_friend_by_profile_id(profile_id: int,session:Session): - friend = session.query(Friends).where(Friends.friendid == profile_id).first() +def delete_friend_by_profile_id(profile_id: int, session: Session): + friend = session.query(Friends).where( + Friends.friendid == profile_id).first() if friend is None: raise GPDatabaseException( f"friend deletion have errors on profile id:{profile_id}" @@ -173,7 +174,8 @@ def get_user_info_list( assert isinstance(Profiles.nick, Column) result = ( - session.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) + session.query(Users.userid, Profiles.profileid, + SubProfiles.subprofileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .where(Users.email == email, Profiles.nick == nick_name) @@ -197,7 +199,8 @@ def get_user_info( # assert isinstance(SubProfiles.namespaceid, Column) result = ( - session.query(Users.userid, Profiles.profileid, SubProfiles.subprofileid) + session.query(Users.userid, Profiles.profileid, + SubProfiles.subprofileid) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) .where( @@ -400,7 +403,8 @@ def update_block( assert isinstance(SubProfiles.session_key, Column) namespace_id = ( - session.query(SubProfiles).where(SubProfiles.session_key == session_key).first() + session.query(SubProfiles).where( + SubProfiles.session_key == session_key).first() ) result = ( session.query(Blocked) @@ -412,7 +416,8 @@ def update_block( .count() ) if result == 0: - b = Blocked(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) + b = Blocked(targetid=target_id, namespaceid=namespace_id, + profileid=profile_id) session.add(b) session.commit() @@ -429,7 +434,8 @@ def update_friend_info( ) .count() ) - f = Friends(targetid=target_id, namespaceid=namespace_id, profileid=profile_id) + f = Friends(targetid=target_id, namespaceid=namespace_id, + profileid=profile_id) if result == 0: session.add(f) @@ -475,13 +481,13 @@ def update_unique_nick(subprofile_id: int, unique_nick: str, session: Session): session.commit() -def update_subprofile_info(subprofile: SubProfiles,session:Session): +def update_subprofile_info(subprofile: SubProfiles, session: Session): session.add(subprofile) session.commit() def add_friend_request( - profileid: int, targetid: int, namespace_id: int, reason: str,session:Session + profileid: int, targetid: int, namespace_id: int, reason: str, session: Session ) -> None: data = ( session.query(FriendAddRequest) @@ -504,7 +510,7 @@ def add_friend_request( session.commit() -def get_status(session_key: str,session:Session) -> dict: +def get_status(session_key: str, session: Session) -> dict: if TYPE_CHECKING: assert isinstance(SubProfiles.session_key, Column) @@ -515,7 +521,8 @@ def get_status(session_key: str,session:Session) -> dict: .first() ) if result is None: - raise GPStatusException("No profile found with the provided session key") + raise GPStatusException( + "No profile found with the provided session key") if TYPE_CHECKING: assert isinstance(result.statstring, str) @@ -524,7 +531,7 @@ def get_status(session_key: str,session:Session) -> dict: result.extra_info["location"] = "" data = { "status_string": result.statstring, - "location_string": result.extra_info["location"], + "location_string": result.extra_info["locstring"], "current_status": result.status, } return data @@ -535,7 +542,7 @@ def update_status( current_status: GPStatusCode, location_string: str, status_string: str, - session:Session + session: Session ): if TYPE_CHECKING: assert isinstance(SubProfiles.session_key, Column) @@ -547,17 +554,18 @@ def update_status( .first() ) if result is None: - raise GPStatusException("No profile found with the provided session key") + raise GPStatusException( + "No profile found with the provided session key") result.statstring = status_string result.status = current_status - assert isinstance(result.extra_info, list) - result.extra_info.append(location_string) + assert isinstance(result.extra_info, dict) + result.extra_info['locstring'] = location_string session.commit() -def update_new_nick(session_key: str, old_nick: str, new_nick: str,session:Session): +def update_new_nick(session_key: str, old_nick: str, new_nick: str, session: Session): result = ( session.query(Profiles) .join(SubProfiles) @@ -569,30 +577,34 @@ def update_new_nick(session_key: str, old_nick: str, new_nick: str,session:Sessi session.commit() -def update_cdkey(session_key: str, cdkey: str,session:Session): +def update_cdkey(session_key: str, cdkey: str, session: Session): subprofile = ( - session.query(SubProfiles).where(SubProfiles.session_key == session_key).first() + session.query(SubProfiles).where( + SubProfiles.session_key == session_key).first() ) if subprofile is None: - raise GPDatabaseException(f"no subprofile found with session key:{session_key}") + raise GPDatabaseException( + f"no subprofile found with session key:{session_key}") subprofile.cdkeyenc = cdkey session.commit() -def update_uniquenick(session_key: str, uniquenick: str,session:Session): +def update_uniquenick(session_key: str, uniquenick: str, session: Session): subprofile = ( - session.query(SubProfiles).where(SubProfiles.session_key == session_key).first() + session.query(SubProfiles).where( + SubProfiles.session_key == session_key).first() ) if subprofile is None: - raise GPDatabaseException(f"no subprofile found with session key:{session_key}") + raise GPDatabaseException( + f"no subprofile found with session key:{session_key}") subprofile.uniquenick = uniquenick session.commit() -def update_profiles(session_key: str, extra_info: dict,session:Session): +def update_profiles(session_key: str, extra_info: dict, session: Session): profile = ( session.query(Profiles) .join(SubProfiles) @@ -600,7 +612,8 @@ def update_profiles(session_key: str, extra_info: dict,session:Session): .first() ) if profile is None: - raise GPDatabaseException(f"no profile found with session key:{session_key}") + raise GPDatabaseException( + f"no profile found with session key:{session_key}") for key, value in extra_info.items(): profile.extra_info[key] = value diff --git a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py index c21d6fa34..8057fced9 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py @@ -65,7 +65,7 @@ def _data_operate(self) -> None: def _nick_email_login(self) -> None: assert self._request.email is not None assert self._request.nick is not None - is_exsit = data.is_email_exist(self._request.email) + is_exsit = data.is_email_exist(self._request.email, self._session) if not is_exsit: raise GPLoginBadEmailException( f"email: {self._request.email} is invalid.") @@ -95,6 +95,7 @@ def _result_construct(self) -> None: partner_id=self._request.partner_id, user_challenge=self._request.user_challenge, user_data=self._request.user_data) + class LogoutHandler(HandlerBase): @@ -211,7 +212,7 @@ def _data_operate(self) -> None: # } # TODO # parse user to buddy message system - raise NotImplementedError() + raise NotImplementedError("whether need chat server to interact?") class StatusHandler(HandlerBase): diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index d0b5a499d..bf7239812 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -65,11 +65,12 @@ def get_peer_group_channel( # get the group room info from GroupList # Construct the group names based on the provided group_ids - group_name = f"{PeerRoom.GroupRoomPrefix}!{gd.groupid}" + group_room_channel_name = f"{PeerRoom.GroupRoomPrefix}!{gd.groupid}" + channel_name_prefix_regex = f"{group_room_channel_name}!%" # Query the database for channels matching the constructed group names result = ( session.query(ChatChannelCaches) - .where(ChatChannelCaches.channel_name == group_name + .where(ChatChannelCaches.channel_name == group_room_channel_name ).first()) assert isinstance(gd.groupid, int) assert isinstance(gd.roomname, str) @@ -80,11 +81,13 @@ def get_peer_group_channel( hostname=gd.roomname) else: assert isinstance(result.channel_name, str) - + assert isinstance(result.max_num_user, int) # todo get the peer room extra info - player_count = session.query(ChatChannelUserCaches).where( + waiting_player_count = session.query(ChatChannelUserCaches).where( ChatChannelUserCaches.channel_name == result.channel_name).count() + playing_player_count = session.query(ChatChannelUserCaches).where( + ChatChannelUserCaches.channel_name.like(channel_name_prefix_regex)).count() password = result.password if result.password is not None else "" assert isinstance(password, str) info = PeerRoomInfo(groupid=gd.groupid, @@ -92,7 +95,9 @@ def get_peer_group_channel( hostname=result.channel_name, password=password, maxplayers=result.max_num_user, - numplayers=player_count, + numplayers=waiting_player_count+playing_player_count, + numwaiting=waiting_player_count, + numplaying=playing_player_count ) group_info.append(info) diff --git a/src/backends/routers/gamespy/game_stats.py b/src/backends/routers/gamespy/game_stats.py index cc3d35211..31f0b5457 100644 --- a/src/backends/routers/gamespy/game_stats.py +++ b/src/backends/routers/gamespy/game_stats.py @@ -4,6 +4,7 @@ AuthGameHandler, AuthPlayerHandler, GetPlayerDataHandler, + GetProfileIdHandler, NewGameHandler, SetPlayerDataHandler, UpdateGameHandler, @@ -12,11 +13,12 @@ AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, + GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest, ) -from backends.protocols.gamespy.game_status.response import AuthGameResponse, AuthPlayerResponse, GetPlayerDataResponse +from backends.protocols.gamespy.game_status.response import AuthGameResponse, AuthPlayerResponse, GetPlayerDataResponse, GetProfileIdResponse, SetPlayerDataResponse from backends.urls import GAMESTATUS router = APIRouter() @@ -51,19 +53,26 @@ def get_player_data(request: GetPlayerDataRequest) -> GetPlayerDataResponse: @router.post(f"{GAMESTATUS}/SetPlayerDataHandler", responses=RESPONSES_DEF) -def set_player_data(request: SetPlayerDataRequest) -> Response: +def set_player_data(request: SetPlayerDataRequest) -> SetPlayerDataResponse: handler = SetPlayerDataHandler(request) handler.handle() return handler.response @router.post(f"{GAMESTATUS}/UpdateGameHandler", responses=RESPONSES_DEF) -def updaet_game(request: UpdateGameRequest) -> OKResponse: +def update_game(request: UpdateGameRequest) -> OKResponse: handler = UpdateGameHandler(request) handler.handle() return handler.response +@router.post(f"{GAMESTATUS}/GetProfileIdHandler", responses=RESPONSES_DEF) +def get_profileid(request: GetProfileIdRequest)->GetProfileIdResponse: + handler = GetProfileIdHandler(request) + handler.handle() + return handler.response + + if __name__ == "__main__": import uvicorn from fastapi import FastAPI diff --git a/src/frontends/gamespy/protocols/chat/contracts/requests.py b/src/frontends/gamespy/protocols/chat/contracts/requests.py index e0601f4cf..f752fb01f 100644 --- a/src/frontends/gamespy/protocols/chat/contracts/requests.py +++ b/src/frontends/gamespy/protocols/chat/contracts/requests.py @@ -514,10 +514,12 @@ def parse(self): class SetChannelKeyRequest(ChannelRequestBase): """ sprintf(buffer, "SETCHANKEY %s :", channel); + BCAST is broadcast flag """ key_value_string: str key_values: dict[str, str] + is_broadcast: bool def parse(self): super().parse() @@ -526,11 +528,19 @@ def parse(self): self._long_param = self._long_param[1:] self.key_value_string = self._long_param self.key_values = convert_kvstring_to_dictionary(self.key_value_string) - + is_broadcast = False + for key in self.key_values: + if "b_" in key: + is_broadcast = True + break + if is_broadcast: + self.cookie = "BCAST" + self.is_broadcast = True class SetCKeyRequest(ChannelRequestBase): """ sprintf(buffer, "SETCKEY %s %s :", channel, user); + BCAST is broadcast flag """ nick_name: str diff --git a/src/frontends/gamespy/protocols/game_status/abstractions/contracts.py b/src/frontends/gamespy/protocols/game_status/abstractions/contracts.py index 512bc6b66..6d2728cad 100644 --- a/src/frontends/gamespy/protocols/game_status/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/game_status/abstractions/contracts.py @@ -8,7 +8,7 @@ class RequestBase(lib.RequestBase): command_name: str raw_request: str local_id: int | None - request_dict: dict[str, str] + _request_dict: dict[str, str] @staticmethod def convert_game_data_to_key_values(game_data: str): @@ -17,17 +17,17 @@ def convert_game_data_to_key_values(game_data: str): convert_to_key_value(game_data) def parse(self) -> None: - self.request_dict = convert_to_key_value(self.raw_request) + self._request_dict = convert_to_key_value(self.raw_request) - if "lid" in self.request_dict: + if "lid" in self._request_dict: try: - self.local_id = int(self.request_dict["lid"]) + self.local_id = int(self._request_dict["lid"]) except: raise GSException("local id is not valid.") - if "id" in self.request_dict: + if "id" in self._request_dict: try: - self.local_id = int(self.request_dict["id"]) + self.local_id = int(self._request_dict["id"]) except: raise GSException("local id is not valid.") diff --git a/src/frontends/gamespy/protocols/game_status/applications/client.py b/src/frontends/gamespy/protocols/game_status/applications/client.py index fa61299ce..3133298da 100644 --- a/src/frontends/gamespy/protocols/game_status/applications/client.py +++ b/src/frontends/gamespy/protocols/game_status/applications/client.py @@ -56,7 +56,7 @@ def decrypt_message(self, buffer: bytes) -> bytes: message = "" for t in temp: complete_buffer = (t + "\\final\\").encode() - message += self.crypto.decrypt(complete_buffer).decode() + message += self.crypto.decrypt(complete_buffer).decode("ascii") return message.encode() return self.crypto.decrypt(buffer) @@ -65,5 +65,12 @@ def _create_switcher(self, buffer: bytes) -> SwitcherBase: from frontends.gamespy.protocols.game_status.applications.switcher import ( Switcher, ) - - return Switcher(self, buffer.decode()) + # ! sometime the request will decode with exception, something happend in sdk, + # ! we do not process that here, let client login again will resolve the problem + try: + data_str = buffer.decode("ascii") + except UnicodeDecodeError: + self.log_warn( + f"can not decode {buffer} with ascii, change decode method to latin1") + data_str = buffer.decode("latin1") + return Switcher(self, data_str) diff --git a/src/frontends/gamespy/protocols/game_status/applications/handlers.py b/src/frontends/gamespy/protocols/game_status/applications/handlers.py index db9a365ad..e69b35c58 100644 --- a/src/frontends/gamespy/protocols/game_status/applications/handlers.py +++ b/src/frontends/gamespy/protocols/game_status/applications/handlers.py @@ -1,19 +1,21 @@ from frontends.gamespy.protocols.game_status.abstractions.handlers import CmdHandlerBase from frontends.gamespy.protocols.game_status.applications.client import Client from frontends.gamespy.protocols.game_status.contracts.requests import AuthGameRequest, AuthPlayerRequest, GetPlayerDataRequest, GetProfileIdRequest, NewGameRequest, SetPlayerDataRequest, UpdateGameRequest -from frontends.gamespy.protocols.game_status.contracts.responses import AuthGameResponse, AuthPlayerResponse, GetPlayerDataResponse, GetProfileIdResponse -from frontends.gamespy.protocols.game_status.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult +from frontends.gamespy.protocols.game_status.contracts.responses import AuthGameResponse, AuthPlayerResponse, GetPlayerDataResponse, GetProfileIdResponse, SetPlayerDataResponse +from frontends.gamespy.protocols.game_status.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult, SetPlayerDataResult class AuthGameHandler(CmdHandlerBase): _request: AuthGameRequest _result: AuthGameResult + _response: AuthGameResponse def __init__(self, client: Client, request: AuthGameRequest) -> None: assert isinstance(request, AuthGameRequest) super().__init__(client, request) - def _response_construct(self) -> None: + def _data_operate(self) -> None: + super()._data_operate() self._client.info.session_key = self._result.session_key self._client.info.game_name = self._result.game_name self._client.info.is_game_authenticated = True @@ -56,6 +58,10 @@ def __init__(self, client: Client, request: NewGameRequest) -> None: class SetPlayerDataHandler(CmdHandlerBase): + _request: SetPlayerDataRequest + _result: SetPlayerDataResult + _response: SetPlayerDataResponse + def __init__(self, client: Client, request: SetPlayerDataRequest) -> None: assert isinstance(request, SetPlayerDataRequest) super().__init__(client, request) diff --git a/src/frontends/gamespy/protocols/game_status/contracts/requests.py b/src/frontends/gamespy/protocols/game_status/contracts/requests.py index a24cec9d1..3329858ad 100644 --- a/src/frontends/gamespy/protocols/game_status/contracts/requests.py +++ b/src/frontends/gamespy/protocols/game_status/contracts/requests.py @@ -14,20 +14,20 @@ class AuthGameRequest(RequestBase): def parse(self) -> None: super().parse() - if "lid" not in self.request_dict and "id" not in self.request_dict: + if "lid" not in self._request_dict and "id" not in self._request_dict: raise GSException("localid is missing") - if "gamename" not in self.request_dict: + if "gamename" not in self._request_dict: raise GSException("gamename is missing") - self.game_name = self.request_dict["gamename"] + self.game_name = self._request_dict["gamename"] - if "response" not in self.request_dict: + if "response" not in self._request_dict: raise GSException("response is missing") - self.response = self.request_dict["response"] + self.response = self._request_dict["response"] - if "port" in self.request_dict: + if "port" in self._request_dict: try: - self.port = int(self.request_dict["port"]) + self.port = int(self._request_dict["port"]) except ValueError: raise GSException("port format is incorrect") @@ -44,22 +44,22 @@ class AuthPlayerRequest(RequestBase): def parse(self) -> None: super().parse() - if "lid" not in self.request_dict and "id" not in self.request_dict: + if "lid" not in self._request_dict and "id" not in self._request_dict: raise GSException("localid is missing from auth game request") - if "pid" in self.request_dict and "resp" in self.request_dict: + if "pid" in self._request_dict and "resp" in self._request_dict: try: - self.profile_id = int(self.request_dict["pid"]) + self.profile_id = int(self._request_dict["pid"]) except ValueError: raise GSException("profile id format is incorrect") self.auth_type = AuthMethod.PROFILE_ID_AUTH - elif "authtoken" in self.request_dict and "response" in self.request_dict: - self.auth_token = self.request_dict["authtoken"] - self.response = self.request_dict["response"] + elif "authtoken" in self._request_dict and "response" in self._request_dict: + self.auth_token = self._request_dict["authtoken"] + self.response = self._request_dict["response"] self.auth_type = AuthMethod.PARTNER_ID_AUTH - elif "keyhash" in self.request_dict and "nick" in self.request_dict: - self.cdkey_hash = self.request_dict["keyhash"] - self.nick = self.request_dict["nick"] - + elif "keyhash" in self._request_dict and "nick" in self._request_dict: + self.cdkey_hash = self._request_dict["keyhash"] + self.nick = self._request_dict["nick"] + self.auth_type = AuthMethod.CDKEY_AUTH else: raise GSException("unknown authp request type") @@ -79,31 +79,32 @@ def __init__(self, raw_request: object) -> None: def parse(self) -> None: super().parse() - if "lid" not in self.request_dict and "id" not in self.request_dict: + if "lid" not in self._request_dict and "id" not in self._request_dict: raise GSException("localid is missing from auth game request") - if "pid" in self.request_dict: + if "pid" in self._request_dict: try: - self.profile_id = int(self.request_dict["pid"]) + self.profile_id = int(self._request_dict["pid"]) except ValueError: raise GSException("pid format is incorrect") - if "ptype" in self.request_dict: + if "ptype" in self._request_dict: try: - self.storage_type = PersistStorageType(int(self.request_dict["ptype"])) + self.storage_type = PersistStorageType( + int(self._request_dict["ptype"])) except ValueError: raise GSException("ptype format is incorrect") - if "dindex" in self.request_dict: + if "dindex" in self._request_dict: try: - self.data_index = int(self.request_dict["dindex"]) + self.data_index = int(self._request_dict["dindex"]) except ValueError: raise GSException("dindex format is incorrect") - if "keys" not in self.request_dict: + if "keys" not in self._request_dict: raise GSException("keys is missing") - keys = self.request_dict["keys"] + keys = self._request_dict["keys"] if not keys: self.is_get_all_data = True else: @@ -116,20 +117,20 @@ def parse(self) -> None: @final class GetProfileIdRequest(RequestBase): nick: str - keyhash: str + key_hash: str def parse(self) -> None: super().parse() - if "lid" not in self.request_dict and "id" not in self.request_dict: + if "lid" not in self._request_dict and "id" not in self._request_dict: raise GSException("localid is missing from auth game request") - if "nick" not in self.request_dict or "keyhash" not in self.request_dict: + if "nick" not in self._request_dict or "keyhash" not in self._request_dict: raise GSException("nick or keyhash is missing") - if "nick" in self.request_dict: - self.nick = self.request_dict["nick"] - if "keyhash" in self.request_dict: - self.keyhash = self.request_dict["keyhash"] + if "nick" in self._request_dict: + self.nick = self._request_dict["nick"] + if "keyhash" in self._request_dict: + self.key_hash = self._request_dict["keyhash"] @final @@ -148,70 +149,94 @@ class NewGameRequest(RequestBase): def parse(self) -> None: super().parse() self.is_client_local_storage_available = True - if "sesskey" not in self.request_dict: + if "sesskey" not in self._request_dict: raise GSException("sesskey is missing") - self.session_key = self.request_dict["sesskey"] + self.session_key = self._request_dict["sesskey"] - if "connid" not in self.request_dict: + if "connid" not in self._request_dict: raise GSException("connid is missing") try: - self.connection_id = int(self.request_dict["connid"]) + self.connection_id = int(self._request_dict["connid"]) except ValueError: raise GSException("connid format is incorrect") - if "challenge" in self.request_dict: - self.challenge = self.request_dict["challenge"] + if "challenge" in self._request_dict: + self.challenge = self._request_dict["challenge"] @final class SetPlayerDataRequest(RequestBase): + """ + request have 2 different data type + SetPersistData() data type is bytes + SetPersistDataValues() data type is \\key\\value + """ profile_id: int storage_type: PersistStorageType data_index: int length: int report: str data: str + is_key_value: bool def parse(self) -> None: + data_index = self.raw_request.index("data")+4 + final_index = self.raw_request.index("final") + # we temperary save raw request and restore it later, + # for not geting error in super method + temp_raw = self.raw_request + # we get the data part out of the request + self.raw_request = self.raw_request[0:data_index] + \ + self.raw_request[final_index:] super().parse() - if "pid" not in self.request_dict: + self.raw_request = temp_raw + self._request_dict["data"] = temp_raw[data_index:final_index-2] + if "pid" not in self._request_dict: raise GSException("pid is missing") - if "ptype" not in self.request_dict: + if "ptype" not in self._request_dict: raise GSException("ptype is missing") - if "dindex" not in self.request_dict: + if "dindex" not in self._request_dict: raise GSException("dindex is missing") - if "length" not in self.request_dict: + if "length" not in self._request_dict: raise GSException("length is missing") + if "kv" not in self._request_dict: + raise GSException("key value type is missing") + + try: + self.is_key_value = bool(int(self._request_dict['kv'])) + except ValueError: + raise GSException("kv format is incorrect") try: - self.profile_id = int(self.request_dict["pid"]) + self.profile_id = int(self._request_dict["pid"]) except ValueError: raise GSException("pid format is incorrect") try: - self.storage_type = PersistStorageType(int(self.request_dict["ptype"])) + self.storage_type = PersistStorageType( + int(self._request_dict["ptype"])) except ValueError: raise GSException("ptype format is incorrect") try: - self.data_index = int(self.request_dict["dindex"]) + self.data_index = int(self._request_dict["dindex"]) except ValueError: raise GSException("dindex format is incorrect") try: - self.length = int(self.request_dict["length"]) + self.length = int(self._request_dict["length"]) except ValueError: raise GSException("length format is incorrect") - if "report" in self.request_dict: - self.report = self.request_dict["report"] + if "report" in self._request_dict: + self.report = self._request_dict["report"] - if "data" in self.request_dict: - self.data = self.request_dict["data"] + if "data" in self._request_dict: + self.data = self._request_dict["data"] @final @@ -229,19 +254,19 @@ def __init__(self, raw_request: object) -> None: def parse(self) -> None: super().parse() - if "gamedata" not in self.request_dict: + if "gamedata" not in self._request_dict: raise GSException("gamedata is missing") - self.game_data = self.request_dict["gamedata"] + self.game_data = self._request_dict["gamedata"] self.game_data_dict = convert_to_key_value(self.game_data) - if "dl" in self.request_dict: + if "dl" in self._request_dict: self.is_client_local_storage_available = True - if "done" not in self.request_dict: + if "done" not in self._request_dict: raise GSException("done is missing") - done = self.request_dict["done"] + done = self._request_dict["done"] if done == "1": self.is_done = True @@ -250,13 +275,13 @@ def parse(self) -> None: else: raise GSException("done format is incorrect") - if "sesskey" not in self.request_dict: + if "sesskey" not in self._request_dict: raise GSException("sesskey is missing") - self.session_key = self.request_dict["sesskey"] + self.session_key = self._request_dict["sesskey"] - if "connid" in self.request_dict: + if "connid" in self._request_dict: try: - self.connection_id = int(self.request_dict["connid"]) + self.connection_id = int(self._request_dict["connid"]) except ValueError: raise GSException("connid format is incorrect") diff --git a/src/frontends/gamespy/protocols/game_status/contracts/responses.py b/src/frontends/gamespy/protocols/game_status/contracts/responses.py index 8d81c68bc..710882134 100644 --- a/src/frontends/gamespy/protocols/game_status/contracts/responses.py +++ b/src/frontends/gamespy/protocols/game_status/contracts/responses.py @@ -1,7 +1,7 @@ from typing import final from frontends.gamespy.library.abstractions.contracts import ResponseBase -from frontends.gamespy.protocols.game_status.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult +from frontends.gamespy.protocols.game_status.contracts.results import AuthGameResult, AuthPlayerResult, GetPlayerDataResult, GetProfileIdResult, SetPlayerDataResult @final @@ -19,9 +19,7 @@ class AuthPlayerResponse(ResponseBase): _result: AuthPlayerResult def build(self) -> None: - # fmt: off self.sending_buffer = f"\\pauthr\\{self._result.profile_id}\\lid\\{self._result.local_id}\\final\\" - # fmt: on @final @@ -29,9 +27,8 @@ class GetPlayerDataResponse(ResponseBase): _result: GetPlayerDataResult def build(self) -> None: - # fmt: off - self.sending_buffer = f"\\getpdr\\1\\pid\\{self._result.profile_id}\\lid\\{self._result.local_id}\\mod\\1234\\length\\5\\data\\mydata\\final\\" - # fmt: on + mod_time = int(self._result.modified.timestamp()) + self.sending_buffer = f"\\getpdr\\1\\pid\\{self._result.profile_id}\\lid\\{self._result.local_id}\\mod\\{mod_time}\\length\\{len(self._result.data)}\\data\\{self._result.data}\\final\\" @final @@ -46,7 +43,9 @@ def build(self) -> None: @final class SetPlayerDataResponse(ResponseBase): - _result: GetPlayerDataResult + _result: SetPlayerDataResult def build(self) -> None: - raise NotImplementedError() + # \\setpdr\\1\\lid\\2\\pid\\100000\\mod\\12345 + mod_time = int(self._result.modified.timestamp()) + self.sending_buffer = f"\\setpdr\\1\\pid\\{self._result.profile_id}\\lid\\{self._result.local_id}\\mod\\{mod_time}\\final\\" diff --git a/src/frontends/gamespy/protocols/game_status/contracts/results.py b/src/frontends/gamespy/protocols/game_status/contracts/results.py index a72ba389b..49198bda9 100644 --- a/src/frontends/gamespy/protocols/game_status/contracts/results.py +++ b/src/frontends/gamespy/protocols/game_status/contracts/results.py @@ -1,3 +1,4 @@ +from datetime import datetime from typing import final from frontends.gamespy.protocols.game_status.abstractions.contracts import ResultBase @@ -15,10 +16,17 @@ class AuthPlayerResult(ResultBase): @final class GetPlayerDataResult(ResultBase): - keyvalues: dict[str, str] + data: str profile_id: int + modified: datetime @final class GetProfileIdResult(ResultBase): profile_id: int + + +@final +class SetPlayerDataResult(ResultBase): + profile_id: int + modified: datetime diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py b/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py index 1d56da16c..54c000d1d 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/abstractions/contracts.py @@ -20,7 +20,7 @@ class RequestBase(lib.RequestBase): command_name: str operation_id: int raw_request: str - request_dict: Dict[str, str] + _request_dict: Dict[str, str] def __init__(self, raw_request: str) -> None: assert isinstance(raw_request, str) @@ -28,12 +28,12 @@ def __init__(self, raw_request: str) -> None: def parse(self): super().parse() - self.request_dict = convert_to_key_value(self.raw_request) - self.command_name = list(self.request_dict.keys())[0] + self._request_dict = convert_to_key_value(self.raw_request) + self.command_name = list(self._request_dict.keys())[0] - if "id" in self.request_dict: + if "id" in self._request_dict: try: - self.operation_id = int(self.request_dict["id"]) + self.operation_id = int(self._request_dict["id"]) except Exception: raise GPParseException("namespaceid is invalid.") diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py index 1517c3ad5..06689a2cf 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/handlers.py @@ -5,6 +5,7 @@ ) from frontends.gamespy.protocols.presence_connection_manager.contracts.requests import ( AddBlockRequest, + AddBuddyRequest, GetProfileRequest, NewProfileRequest, NewUserRequest, @@ -69,7 +70,6 @@ class KeepAliveHandler(CmdHandlerBase): def __init__(self, client: Client, request: KeepAliveRequest) -> None: assert isinstance(request, KeepAliveRequest) super().__init__(client, request) - def _data_operate(self) -> None: # we set ip and data to request @@ -90,8 +90,11 @@ class LoginHandler(CmdHandlerBase): def __init__(self, client: Client, request: LoginRequest) -> None: assert isinstance(request, LoginRequest) super().__init__(client, request) - self._result_cls = LoginResult - self._response_cls = LoginResponse + + def _response_send(self) -> None: + super()._response_send() + handler = SdkRevisionHandler(self._client, self._request) + handler.handle() @final @@ -123,20 +126,18 @@ class SdkRevisionHandler(CmdHandlerBase): def __init__(self, client: Client, request: LoginRequest) -> None: assert isinstance(request, LoginRequest) super().__init__(client, request) - - def _data_operate(self): - pass + self._is_fetching = False + self._is_uploading = False def _response_construct(self) -> None: self._client.info.sdk_revision = self._request.sdk_revision_type - for operation in self._client.info.sdk_revision: - if operation == SdkRevisionType.GPINEW_LIST_RETRIEVAL_ON_LOGIN: - BuddyListHandler(self._client).handle() - BlockListHandler(self._client).handle() - request = StatusInfoRequest() - request.profile_id = self._client.info.profile_id - request.namespace_id = int(self._client.info.namespace_id) - StatusInfoHandler(self._client, request).handle() + if SdkRevisionType.GPINEW_LIST_RETRIEVAL_ON_LOGIN in self._client.info.sdk_revision: + BuddyListHandler(self._client).handle() + BlockListHandler(self._client).handle() + request = StatusInfoRequest() + request.profile_id = self._client.info.profile_id + request.namespace_id = int(self._client.info.namespace_id) + StatusInfoHandler(self._client, request).handle() # todo: add other revision operations @@ -145,9 +146,10 @@ def _response_construct(self) -> None: @final class AddBuddyHandler(CmdHandlerBase): - def __init__(self, client: Client, request: RequestBase) -> None: - raise NotImplementedError() + def __init__(self, client: Client, request: AddBuddyRequest) -> None: + assert isinstance(request, AddBuddyRequest) super().__init__(client, request) + self._is_fetching = False @final @@ -211,7 +213,7 @@ class StatusHandler(CmdHandlerBase): def __init__(self, client: Client, request: StatusRequest) -> None: assert isinstance(request, StatusRequest) super().__init__(client, request) - + self._is_fetching = False @final @@ -240,7 +242,6 @@ class AddBlockHandler(CmdHandlerBase): def __init__(self, client: Client, request: AddBlockRequest) -> None: assert isinstance(request, AddBlockRequest) super().__init__(client, request) - @final @@ -272,7 +273,6 @@ class RegisterCDKeyHandler(CmdHandlerBase): def __init__(self, client: Client, request: RegisterCDKeyRequest) -> None: assert isinstance(request, RegisterCDKeyRequest) super().__init__(client, request) - @final diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py index 944aa94f5..ccb794e67 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/contracts/requests.py @@ -127,35 +127,35 @@ def __init__(self, raw_request: str) -> None: def parse(self): super().parse() - if "challenge" not in self.request_dict: + if "challenge" not in self._request_dict: raise GPParseException("challenge is missing") - if "response" not in self.request_dict: + if "response" not in self._request_dict: raise GPParseException("response is missing") - self.user_challenge = self.request_dict["challenge"] - self.response = self.request_dict["response"] + self.user_challenge = self._request_dict["challenge"] + self.response = self._request_dict["response"] - if "uniquenick" in self.request_dict and "namespaceid" in self.request_dict: - namespace_id = int(self.request_dict["namespaceid"]) + if "uniquenick" in self._request_dict and "namespaceid" in self._request_dict: + namespace_id = int(self._request_dict["namespaceid"]) self.type = LoginType.UNIQUENICK_NAMESPACE_ID - self.unique_nick = self.request_dict["uniquenick"] + self.unique_nick = self._request_dict["uniquenick"] self.user_data = self.unique_nick self.namespace_id = namespace_id - elif "authtoken" in self.request_dict: + elif "authtoken" in self._request_dict: self.type = LoginType.AUTH_TOKEN - self.auth_token = self.request_dict["authtoken"] + self.auth_token = self._request_dict["authtoken"] self.user_data = self.auth_token - elif "user" in self.request_dict: + elif "user" in self._request_dict: self.type = LoginType.NICK_EMAIL - self.user_data = self.request_dict["user"] + self.user_data = self._request_dict["user"] pos = self.user_data.index("@") if pos == -1 or pos < 1 or (pos + 1) >= len(self.user_data): raise GPParseException("user format is incorrect") self.nick = self.user_data[:pos] self.email = self.user_data[pos + 1:] - if "namespaceid" in self.request_dict: - namespace_id = int(self.request_dict["namespaceid"]) + if "namespaceid" in self._request_dict: + namespace_id = int(self._request_dict["namespaceid"]) self.namespace_id = namespace_id else: raise GPParseException("Unknown login method detected.") @@ -163,42 +163,42 @@ def parse(self): self.parse_other_data() def parse_other_data(self): - if "userid" in self.request_dict: - user_id = int(self.request_dict["userid"]) + if "userid" in self._request_dict: + user_id = int(self._request_dict["userid"]) self.user_id = user_id - if "profileid" in self.request_dict: - profile_id = int(self.request_dict["profileid"]) + if "profileid" in self._request_dict: + profile_id = int(self._request_dict["profileid"]) self.profile_id = profile_id - if "partnerid" in self.request_dict: - partner_id = int(self.request_dict["partnerid"]) + if "partnerid" in self._request_dict: + partner_id = int(self._request_dict["partnerid"]) self.partner_id = partner_id else: self.partner_id = 0 - if "sdkrevision" in self.request_dict: - sdk_revision_type = int(self.request_dict["sdkrevision"]) + if "sdkrevision" in self._request_dict: + sdk_revision_type = int(self._request_dict["sdkrevision"]) for item in self.sdk_mapping: if item & sdk_revision_type: self.sdk_revision_type.append(item) - if "gamename" in self.request_dict: - self.game_name = self.request_dict["gamename"] + if "gamename" in self._request_dict: + self.game_name = self._request_dict["gamename"] - if "port" in self.request_dict: - game_port = int(self.request_dict["port"]) + if "port" in self._request_dict: + game_port = int(self._request_dict["port"]) self.game_port = game_port - if "productid" in self.request_dict: - product_id = int(self.request_dict["productid"]) + if "productid" in self._request_dict: + product_id = int(self._request_dict["productid"]) self.product_id = product_id - if "firewall" in self.request_dict: - self.firewall = bool(self.request_dict["firewall"]) + if "firewall" in self._request_dict: + self.firewall = bool(self._request_dict["firewall"]) - if "quiet" in self.request_dict: - quiet = int(self.request_dict["quiet"]) + if "quiet" in self._request_dict: + quiet = int(self._request_dict["quiet"]) self.quiet_mode_flags = QuietModeType(quiet) @@ -226,58 +226,58 @@ class NewUserRequest(RequestBase): def parse(self): super().parse() - self.password = process_password(self.request_dict) + self.password = process_password(self._request_dict) - if "nick" not in self.request_dict: + if "nick" not in self._request_dict: raise GPParseException("nickname is missing.") - if "email" not in self.request_dict: + if "email" not in self._request_dict: raise GPParseException("email is missing.") - if not is_email_format_correct(self.request_dict["email"]): + if not is_email_format_correct(self._request_dict["email"]): raise GPParseException("email format is incorrect.") - self.nick = self.request_dict["nick"] - self.email = self.request_dict["email"] + self.nick = self._request_dict["nick"] + self.email = self._request_dict["email"] - if "uniquenick" in self.request_dict and "namespaceid" in self.request_dict: - if "namespaceid" in self.request_dict: + if "uniquenick" in self._request_dict and "namespaceid" in self._request_dict: + if "namespaceid" in self._request_dict: try: - self.namespace_id = int(self.request_dict["namespaceid"]) + self.namespace_id = int(self._request_dict["namespaceid"]) except ValueError: raise GPParseException("namespaceid is incorrect.") - self.uniquenick = self.request_dict["uniquenick"] + self.uniquenick = self._request_dict["uniquenick"] self.parse_other_info() def parse_other_info(self): - if "partnerid" in self.request_dict: + if "partnerid" in self._request_dict: try: - self.partner_id = int(self.request_dict["partnerid"]) + self.partner_id = int(self._request_dict["partnerid"]) self.has_partner_id_flag = True except ValueError: raise GPParseException("partnerid is incorrect.") else: self.partner_id = 0 # set default partner id to 0 means gamespy - if "productid" in self.request_dict: + if "productid" in self._request_dict: try: - self.product_id = int(self.request_dict["productid"]) + self.product_id = int(self._request_dict["productid"]) self.has_product_id_flag = True except ValueError: raise GPParseException("productid is incorrect.") - if "gamename" in self.request_dict: + if "gamename" in self._request_dict: self.has_game_name_flag = True - self.game_name = self.request_dict["gamename"] + self.game_name = self._request_dict["gamename"] - if "port" in self.request_dict: + if "port" in self._request_dict: try: - self.game_port = int(self.request_dict["port"]) + self.game_port = int(self._request_dict["port"]) self.has_game_port_flag = True except ValueError: raise GPParseException("port is incorrect.") - if "cdkey" in self.request_dict: + if "cdkey" in self._request_dict: self.has_cd_key_enc_flag = True - self.cd_key = self.request_dict["cdkey"] + self.cd_key = self._request_dict["cdkey"] # region Buddy @@ -319,17 +319,17 @@ class AddBuddyRequest(RequestBase): def parse(self): super().parse() if ( - ("sesskey" not in self.request_dict) - or ("newprofileid" not in self.request_dict) - or ("reason" not in self.request_dict) + ("sesskey" not in self._request_dict) + or ("newprofileid" not in self._request_dict) + or ("reason" not in self._request_dict) ): raise GPParseException("addbuddy request is invalid.") try: - self.friend_profile_id = int(self.request_dict["newprofileid"]) + self.friend_profile_id = int(self._request_dict["newprofileid"]) except Exception: raise GPParseException("newprofileid format is incorrect.") - self.reason = self.request_dict["reason"] + self.reason = self._request_dict["reason"] @final @@ -338,11 +338,11 @@ class DelBuddyRequest(RequestBase): def parse(self): super().parse() - if "delprofileid" not in self.request_dict: + if "delprofileid" not in self._request_dict: raise GPParseException("delprofileid is missing.") try: - self.friend_profile_id = int(self.request_dict["delprofileid"]) + self.friend_profile_id = int(self._request_dict["delprofileid"]) except Exception: raise GPParseException("delprofileid format is incorrect.") @@ -356,23 +356,23 @@ class InviteToRequest(RequestBase): def parse(self): super().parse() - if "productid" not in self.request_dict: + if "productid" not in self._request_dict: raise GPParseException("productid is missing.") - if "sesskey" not in self.request_dict: + if "sesskey" not in self._request_dict: raise GPParseException("sesskey is missing.") try: - self.product_id = int(self.request_dict["productid"]) + self.product_id = int(self._request_dict["productid"]) except ValueError: raise GPParseException("productid format is incorrect.") try: - self.profile_id = int(self.request_dict["profileid"]) + self.profile_id = int(self._request_dict["profileid"]) except ValueError: raise GPParseException("profileid format is incorrect.") - self.session_key = self.request_dict["sesskey"] + self.session_key = self._request_dict["sesskey"] @final @@ -400,40 +400,40 @@ def parse(self): super().parse() if ( - "state" not in self.request_dict - or "hostip" not in self.request_dict - or "hprivip" not in self.request_dict - or "qport" not in self.request_dict - or "hport" not in self.request_dict - or "sessflags" not in self.request_dict - or "rechstatus" not in self.request_dict - or "gametype" not in self.request_dict - or "gamevariant" not in self.request_dict - or "gamemapname" not in self.request_dict + "state" not in self._request_dict + or "hostip" not in self._request_dict + or "hprivip" not in self._request_dict + or "qport" not in self._request_dict + or "hport" not in self._request_dict + or "sessflags" not in self._request_dict + or "rechstatus" not in self._request_dict + or "gametype" not in self._request_dict + or "gamevariant" not in self._request_dict + or "gamemapname" not in self._request_dict ): raise GPParseException("StatusInfo request is invalid.") - self.status_state = self.request_dict["state"] - self.host_ip = self.request_dict["hostip"] - self.host_private_ip = self.request_dict["hprivip"] + self.status_state = self._request_dict["state"] + self.host_ip = self._request_dict["hostip"] + self.host_private_ip = self._request_dict["hprivip"] try: - self.query_report_port = int(self.request_dict["qport"]) - self.host_port = int(self.request_dict["hport"]) - self.session_flags = self.request_dict["sessflags"] + self.query_report_port = int(self._request_dict["qport"]) + self.host_port = int(self._request_dict["hport"]) + self.session_flags = self._request_dict["sessflags"] except ValueError: raise GPParseException( "qport, hport, or sessflags format is incorrect.") - if "namespace_id" in self.request_dict: - self.namespace_id = int(self.request_dict["namespaceid"]) + if "namespace_id" in self._request_dict: + self.namespace_id = int(self._request_dict["namespaceid"]) else: self.namespace_id = 0 - self.rich_status = self.request_dict["rechstatus"] - self.game_type = self.request_dict["gametype"] - self.game_variant = self.request_dict["gamevariant"] - self.game_map_name = self.request_dict["gamemapname"] + self.rich_status = self._request_dict["rechstatus"] + self.game_type = self._request_dict["gametype"] + self.game_variant = self._request_dict["gamevariant"] + self.game_map_name = self._request_dict["gamemapname"] @final @@ -444,24 +444,24 @@ class StatusRequest(RequestBase): session_key: str def parse(self): - self.request_dict = convert_to_key_value(self.raw_request) - self.command_name = list(self.request_dict.keys())[0] + self._request_dict = convert_to_key_value(self.raw_request) + self.command_name = list(self._request_dict.keys())[0] - if "status" not in self.request_dict: + if "status" not in self._request_dict: raise GPParseException("status is missing.") - if "statstring" not in self.request_dict: + if "statstring" not in self._request_dict: raise GPParseException("statstring is missing.") - if "locstring" not in self.request_dict: + if "locstring" not in self._request_dict: raise GPParseException("locstring is missing.") - if "sesskey" not in self.request_dict: + if "sesskey" not in self._request_dict: raise GPParseException("session key is missing.") - self.session_key = self.request_dict["sesskey"] - self.location_string = self.request_dict["locstring"] - self.status_string = self.request_dict["statstring"] + self.session_key = self._request_dict["sesskey"] + self.location_string = self._request_dict["locstring"] + self.status_string = self._request_dict["statstring"] try: - status_code = int(self.request_dict["status"]) + status_code = int(self._request_dict["status"]) self.current_status = GPStatusCode(status_code) except ValueError: raise GPParseException("status format is incorrect.") @@ -477,11 +477,11 @@ class AddBlockRequest(RequestBase): def parse(self): super().parse() - if "profileid" not in self.request_dict: + if "profileid" not in self._request_dict: raise GPParseException("profileid is missing") try: - self.taget_id = int(self.request_dict["profileid"]) + self.taget_id = int(self._request_dict["profileid"]) except ValueError: raise GPParseException("profileid format is incorrect") @@ -494,18 +494,18 @@ class GetProfileRequest(RequestBase): def parse(self): super().parse() - if "profileid" not in self.request_dict: + if "profileid" not in self._request_dict: raise GPParseException("profileid is missing") try: - self.profile_id = int(self.request_dict["profileid"]) + self.profile_id = int(self._request_dict["profileid"]) except ValueError: raise GPParseException("profileid format is incorrect") - if "sesskey" not in self.request_dict: + if "sesskey" not in self._request_dict: raise GPParseException("sesskey is missing") - self.session_key = self.request_dict["sesskey"] + self.session_key = self._request_dict["sesskey"] @final @@ -518,26 +518,26 @@ class NewProfileRequest(RequestBase): def parse(self): super().parse() - if "sesskey" not in self.request_dict: + if "sesskey" not in self._request_dict: raise GPParseException("sesskey is missing") - self.session_key = self.request_dict["sesskey"] + self.session_key = self._request_dict["sesskey"] - if "replace" in self.request_dict: - if "oldnick" not in self.request_dict and "nick" not in self.request_dict: + if "replace" in self._request_dict: + if "oldnick" not in self._request_dict and "nick" not in self._request_dict: raise GPParseException("oldnick or nick is missing.") - if "oldnick" in self.request_dict: - self.old_nick = self.request_dict["oldnick"] - if "nick" in self.request_dict: - self.new_nick = self.request_dict["nick"] + if "oldnick" in self._request_dict: + self.old_nick = self._request_dict["oldnick"] + if "nick" in self._request_dict: + self.new_nick = self._request_dict["nick"] self.is_replace_nick_name = True else: - if "nick" not in self.request_dict: + if "nick" not in self._request_dict: raise GPParseException("nick is missing.") - self.new_nick = self.request_dict["nick"] + self.new_nick = self._request_dict["nick"] self.is_replace_nick_name = False @@ -549,15 +549,15 @@ class RegisterCDKeyRequest(RequestBase): def parse(self): super().parse() - if "sesskey" not in self.request_dict: + if "sesskey" not in self._request_dict: raise GPParseException("sesskey is missing") - self.session_key = self.request_dict["sesskey"] + self.session_key = self._request_dict["sesskey"] - if "cdkeyenc" not in self.request_dict: + if "cdkeyenc" not in self._request_dict: raise GPParseException("cdkeyenc is missing") - self.cdkey_enc = self.request_dict["cdkeyenc"] + self.cdkey_enc = self._request_dict["cdkeyenc"] @final @@ -569,19 +569,19 @@ class RegisterNickRequest(RequestBase): def parse(self): super().parse() - if "sesskey" not in self.request_dict: + if "sesskey" not in self._request_dict: raise GPParseException("sesskey is missing") - self.session_key = self.request_dict["sesskey"] + self.session_key = self._request_dict["sesskey"] - if "uniquenick" not in self.request_dict: + if "uniquenick" not in self._request_dict: raise GPParseException("uniquenick is missing") - self.unique_nick = self.request_dict["uniquenick"] + self.unique_nick = self._request_dict["uniquenick"] - if "partnerid" in self.request_dict: + if "partnerid" in self._request_dict: try: - self.partner_id = int(self.request_dict["partnerid"]) + self.partner_id = int(self._request_dict["partnerid"]) except ValueError: raise GPParseException("partnerid is missing") @@ -597,23 +597,23 @@ class UpdateProfileRequest(RequestBase): def parse(self): super().parse() - if "sesskey" not in self.request_dict: + if "sesskey" not in self._request_dict: raise GPParseException("sesskey is missing") - self.session_key = self.request_dict["sesskey"] + self.session_key = self._request_dict["sesskey"] - if "partnerid" in self.request_dict: + if "partnerid" in self._request_dict: try: - self.partner_id = int(self.request_dict["partnerid"]) + self.partner_id = int(self._request_dict["partnerid"]) except ValueError: raise GPParseException("partnerid is incorrect") - if "nick" in self.request_dict: - self.nick = self.request_dict["nick"] + if "nick" in self._request_dict: + self.nick = self._request_dict["nick"] - if "uniquenick" in self.request_dict: - self.uniquenick = self.request_dict["uniquenick"] + if "uniquenick" in self._request_dict: + self.uniquenick = self._request_dict["uniquenick"] - self.extra_info = validate_extra_infos(self.request_dict) + self.extra_info = validate_extra_infos(self._request_dict) @final @@ -622,4 +622,4 @@ class UpdateUserInfoRequest(RequestBase): def parse(self): super().parse() - self.extra_infos = validate_extra_infos(self.request_dict) + self.extra_infos = validate_extra_infos(self._request_dict) diff --git a/src/frontends/gamespy/protocols/presence_search_player/abstractions/contracts.py b/src/frontends/gamespy/protocols/presence_search_player/abstractions/contracts.py index 652ac58a3..13932b6b9 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/presence_search_player/abstractions/contracts.py @@ -7,7 +7,7 @@ class RequestBase(lib.RequestBase): - request_dict: Dict[str, str] + _request_dict: dict[str, str] raw_request: str command_name: str operation_id: int @@ -20,17 +20,17 @@ def __init__(self, raw_request: str) -> None: self.namespace_id = 0 def parse(self) -> None: - self.request_dict = convert_to_key_value(self.raw_request) - self.command_name = list(self.request_dict.keys())[0] - if "id" in self.request_dict.keys(): + self._request_dict = convert_to_key_value(self.raw_request) + self.command_name = list(self._request_dict.keys())[0] + if "id" in self._request_dict.keys(): try: - self.operation_id = int(self.request_dict["id"]) + self.operation_id = int(self._request_dict["id"]) except ValueError: raise GPParseException("operation id is invalid.") - if "namespaceid" in self.request_dict: + if "namespaceid" in self._request_dict: try: - self.namespace_id = int(self.request_dict["namespaceid"]) + self.namespace_id = int(self._request_dict["namespaceid"]) except ValueError: raise GPParseException("namespaceid is incorrect.") diff --git a/src/frontends/gamespy/protocols/presence_search_player/contracts/requests.py b/src/frontends/gamespy/protocols/presence_search_player/contracts/requests.py index 5d7a54917..4b9ba84f2 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/contracts/requests.py +++ b/src/frontends/gamespy/protocols/presence_search_player/contracts/requests.py @@ -20,19 +20,19 @@ class CheckRequest(RequestBase): def parse(self): super().parse() - self.password = process_password(self.request_dict) - if "nick" not in self.request_dict or "email" not in self.request_dict: + self.password = process_password(self._request_dict) + if "nick" not in self._request_dict or "email" not in self._request_dict: raise GPParseException("check request is incompelete.") - if not is_email_format_correct(self.request_dict["email"]): + if not is_email_format_correct(self._request_dict["email"]): raise GPParseException(" email format is incorrect.") - self.nick = self.request_dict["nick"] - self.email = self.request_dict["email"] + self.nick = self._request_dict["nick"] + self.email = self._request_dict["email"] - if "partner_id" in self.request_dict.keys(): + if "partner_id" in self._request_dict.keys(): try: - self.partner_id = int(self.request_dict["partner_id"]) + self.partner_id = int(self._request_dict["partner_id"]) except Exception: raise GPParseException( "no partner id found, check whether need implement the default partnerid" @@ -54,55 +54,55 @@ class NewUserRequest(RequestBase): def parse(self): super().parse() - self.password = process_password(self.request_dict) + self.password = process_password(self._request_dict) - if "nick" not in self.request_dict: + if "nick" not in self._request_dict: raise GPParseException("nickname is missing.") - if "email" not in self.request_dict: + if "email" not in self._request_dict: raise GPParseException("email is missing.") - if not is_email_format_correct(self.request_dict["email"]): + if not is_email_format_correct(self._request_dict["email"]): raise GPParseException("email format is incorrect.") - self.nick = self.request_dict["nick"] - self.email = self.request_dict["email"] + self.nick = self._request_dict["nick"] + self.email = self._request_dict["email"] - if "uniquenick" in self.request_dict and "namespaceid" in self.request_dict: - if "namespaceid" in self.request_dict: + if "uniquenick" in self._request_dict and "namespaceid" in self._request_dict: + if "namespaceid" in self._request_dict: try: - self.namespace_id = int(self.request_dict["namespaceid"]) + self.namespace_id = int(self._request_dict["namespaceid"]) except ValueError: raise GPParseException("namespaceid is incorrect.") - self.uniquenick = self.request_dict["uniquenick"] + self.uniquenick = self._request_dict["uniquenick"] self.parse_other_info() def parse_other_info(self): - if "partnerid" in self.request_dict: + if "partnerid" in self._request_dict: try: - self.partner_id = int(self.request_dict["partnerid"]) + self.partner_id = int(self._request_dict["partnerid"]) except ValueError: raise GPParseException("partnerid is incorrect.") else: self.partner_id = 0 - if "productid" in self.request_dict: + if "productid" in self._request_dict: try: - self.product_id = int(self.request_dict["productid"]) + self.product_id = int(self._request_dict["productid"]) except ValueError: raise GPParseException("productid is incorrect.") else: # we give default product id here self.product_id = 0 - if "gamename" in self.request_dict: - self.game_name = self.request_dict["gamename"] + if "gamename" in self._request_dict: + self.game_name = self._request_dict["gamename"] - if "port" in self.request_dict: + if "port" in self._request_dict: try: - self.game_port = int(self.request_dict["port"]) + self.game_port = int(self._request_dict["port"]) except ValueError: raise GPParseException("port is incorrect.") - if "cdkey" in self.request_dict: - self.cd_key = self.request_dict["cdkey"] + if "cdkey" in self._request_dict: + self.cd_key = self._request_dict["cdkey"] class NicksRequest(RequestBase): @@ -113,13 +113,13 @@ class NicksRequest(RequestBase): def parse(self): super().parse() self.is_require_uniquenicks = True - self.password = process_password(self.request_dict) - if "email" not in self.request_dict.keys(): + self.password = process_password(self._request_dict) + if "email" not in self._request_dict.keys(): raise GPParseException("email is missing.") - self.email = self.request_dict["email"] + self.email = self._request_dict["email"] - if "pass" in self.request_dict.keys(): + if "pass" in self._request_dict.keys(): self.is_require_uniquenicks = False @@ -129,12 +129,12 @@ class OthersListRequest(RequestBase): def parse(self) -> None: super().parse() - if "opids" not in self.request_dict or "namespaceid" not in self.request_dict: + if "opids" not in self._request_dict or "namespaceid" not in self._request_dict: raise GPParseException("opids or namespaceid is missing.") try: self.profile_ids = [ - int(opid) for opid in self.request_dict["opids"].strip("|").split("|") + int(opid) for opid in self._request_dict["opids"].strip("|").split("|") ] except: raise GPParseException("opids is incorrect") @@ -147,22 +147,22 @@ class OthersRequest(RequestBase): def parse(self): super().parse() - if "gamename" not in self.request_dict: + if "gamename" not in self._request_dict: raise GPParseException("gamename is missing.") - self.game_name = self.request_dict["gamename"] + self.game_name = self._request_dict["gamename"] if ( - "profileid" not in self.request_dict - or "namespaceid" not in self.request_dict + "profileid" not in self._request_dict + or "namespaceid" not in self._request_dict ): raise GPParseException("profileid or namespaceid is missing.") - if "profileid" not in self.request_dict: + if "profileid" not in self._request_dict: raise GPParseException("profileid is incorrect.") try: - self.profile_id = int(self.request_dict["profileid"]) + self.profile_id = int(self._request_dict["profileid"]) except ValueError: raise GPParseException("profileid is incorrect.") @@ -190,60 +190,60 @@ def parse(self) -> None: super().parse() if ( - "profileid" not in self.request_dict - and "nick" not in self.request_dict - and "email" not in self.request_dict - and "namespaceid" not in self.request_dict - and "gamename" not in self.request_dict - and "sesskey" not in self.request_dict + "profileid" not in self._request_dict + and "nick" not in self._request_dict + and "email" not in self._request_dict + and "namespaceid" not in self._request_dict + and "gamename" not in self._request_dict + and "sesskey" not in self._request_dict ): raise GPParseException("Search request is incomplete.") - self.session_key = self.request_dict["sesskey"] + self.session_key = self._request_dict["sesskey"] - if "firstname" in self.request_dict: - self.firstname = self.request_dict["firstname"] + if "firstname" in self._request_dict: + self.firstname = self._request_dict["firstname"] - if "lastname" in self.request_dict: - self.lastname = self.request_dict["lastname"] + if "lastname" in self._request_dict: + self.lastname = self._request_dict["lastname"] - if "icquin" in self.request_dict: - self.icquin = self.request_dict["icquin"] + if "icquin" in self._request_dict: + self.icquin = self._request_dict["icquin"] - if "gamename" in self.request_dict: - self.game_name = self.request_dict["gamename"] + if "gamename" in self._request_dict: + self.game_name = self._request_dict["gamename"] - if "profileid" in self.request_dict: + if "profileid" in self._request_dict: try: - self.profile_id = int(self.request_dict["profileid"]) + self.profile_id = int(self._request_dict["profileid"]) except ValueError: raise GPParseException("profileid is incorrect.") - if "partnerid" in self.request_dict: + if "partnerid" in self._request_dict: try: - self.partner_id = int(self.request_dict["partnerid"]) + self.partner_id = int(self._request_dict["partnerid"]) except ValueError: raise GPParseException("partnerid is incorrect.") - if "skip" in self.request_dict: + if "skip" in self._request_dict: try: - self.skip_num = int(self.request_dict["skip"]) + self.skip_num = int(self._request_dict["skip"]) except ValueError: raise GPParseException("skip number is incorrect.") - if "uniquenick" in self.request_dict and "namespaceid" in self.request_dict: + if "uniquenick" in self._request_dict and "namespaceid" in self._request_dict: self.request_type = SearchType.UNIQUENICK_NAMESPACEID_SEARCH - self.uniquenick = self.request_dict["uniquenick"] - elif "nick" in self.request_dict and "email" in self.request_dict: + self.uniquenick = self._request_dict["uniquenick"] + elif "nick" in self._request_dict and "email" in self._request_dict: self.request_type = SearchType.NICK_EMAIL_SEARCH - self.nick = self.request_dict["nick"] - self.email = self.request_dict["email"] - elif "nick" in self.request_dict: + self.nick = self._request_dict["nick"] + self.email = self._request_dict["email"] + elif "nick" in self._request_dict: self.request_type = SearchType.NICK_SEARCH - self.nick = self.request_dict["nick"] - elif "email" in self.request_dict: + self.nick = self._request_dict["nick"] + elif "email" in self._request_dict: self.request_type = SearchType.EMAIL_SEARCH - self.email = self.request_dict["email"] + self.email = self._request_dict["email"] else: raise GPParseException("unknown search type.") @@ -256,20 +256,20 @@ def parse(self): super().parse() self.namespace_ids = [] if ( - "uniquenick" not in self.request_dict - or "namespaces" not in self.request_dict + "uniquenick" not in self._request_dict + or "namespaces" not in self._request_dict ): raise GPParseException("searchunique request is incomplete.") try: - self.uniquenick = self.request_dict["uniquenick"] + self.uniquenick = self._request_dict["uniquenick"] except KeyError: raise GPParseException("uniquenick is missing.") try: self.namespace_ids = [ int(namespace_id) - for namespace_id in self.request_dict["namespaces"] + for namespace_id in self._request_dict["namespaces"] .lstrip(",") .split(",") ] @@ -284,21 +284,21 @@ class UniqueSearchRequest(RequestBase): def parse(self): super().parse() - if "preferrednick" not in self.request_dict: + if "preferrednick" not in self._request_dict: raise GPParseException("preferrednick is missing.") - self.preferred_nick = self.request_dict["preferrednick"] + self.preferred_nick = self._request_dict["preferrednick"] - if "gamename" not in self.request_dict: + if "gamename" not in self._request_dict: raise GPParseException("gamename is missing.") - self.game_name = self.request_dict["gamename"] + self.game_name = self._request_dict["gamename"] - if "namespaceid" not in self.request_dict: + if "namespaceid" not in self._request_dict: raise GPParseException("namespaceid is missing.") try: - self.namespace_id = int(self.request_dict["namespaceid"]) + self.namespace_id = int(self._request_dict["namespaceid"]) except ValueError: raise GPParseException("namespaceid is incorrect.") @@ -309,9 +309,9 @@ class ValidRequest(RequestBase): def parse(self): super().parse() - if "email" not in self.request_dict or not is_email_format_correct( - self.request_dict["email"] + if "email" not in self._request_dict or not is_email_format_correct( + self._request_dict["email"] ): raise GPParseException("valid request is incomplete.") - self.email = self.request_dict["email"] + self.email = self._request_dict["email"] diff --git a/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py b/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py index e1c38947a..3ef1dfc49 100644 --- a/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py @@ -4,17 +4,17 @@ class RequestBase(lib.RequestBase): - request_dict: dict[str, str] raw_request: str + _request_dict: dict[str, str] def __init__(self, raw_request: str) -> None: assert isinstance(raw_request, str) super().__init__(raw_request) - self.request_dict = {} + self._request_dict = {} def parse(self) -> None: - self.request_dict = convert_to_key_value(self.raw_request) - self.command_name = list(self.request_dict.keys())[0] + self._request_dict = convert_to_key_value(self.raw_request) + self.command_name = list(self._request_dict.keys())[0] class ResultBase(lib.ResultBase): diff --git a/src/frontends/tests/gamespy/game_status/handler_tests.py b/src/frontends/tests/gamespy/game_status/handler_tests.py index b632cf55b..0f4287879 100644 --- a/src/frontends/tests/gamespy/game_status/handler_tests.py +++ b/src/frontends/tests/gamespy/game_status/handler_tests.py @@ -31,7 +31,8 @@ def test_set_player_data_20230329(self): request = SetPlayerDataRequest(raw) request.parse() self.assertEqual(1, request.profile_id) - self.assertEqual(PersistStorageType.PRIVATE_READ_WRITE, request.storage_type) + self.assertEqual(PersistStorageType.PRIVATE_READ_WRITE, + request.storage_type) self.assertEqual(0, request.data_index) self.assertEqual("", request.data) self.assertEqual(111, request.length) @@ -91,7 +92,8 @@ def test_get_player_data(self): request = GetPlayerDataRequest(raw) request.parse() self.assertEqual(0, request.profile_id) - self.assertEqual(PersistStorageType.PRIVATE_READ_ONLY, request.storage_type) + self.assertEqual(PersistStorageType.PRIVATE_READ_ONLY, + request.storage_type) self.assertEqual(1, request.data_index) self.assertEqual(2, len(request.keys)) self.assertEqual("hello", request.keys[0]) @@ -102,7 +104,7 @@ def test_get_profile_id(self): request = GetProfileIdRequest(raw) request.parse() self.assertEqual("xiaojiuwo", request.nick) - self.assertEqual("00000", request.keyhash) + self.assertEqual("00000", request.key_hash) self.assertEqual(1, request.local_id) def test_new_game(self): @@ -138,6 +140,13 @@ def test_update_game(self): self.assertEqual("hello", request2.game_data) self.assertEqual(1, request2.connection_id) + def test_auth_player_2025_11_06(self): + raw = b'\\authp\\\\nick\\spyguy\\keyhash\\00000a308fd86a7eb92cbc8322b03a36\\resp\\a146083990caca4925e3144deb552817\\lid\\1,\x1fZ*&\r1~Shh\x08Db\x0eYKVA-@Fn/\\final\\' + request = AuthPlayerRequest(raw) + client = create_client() + handler = AuthPlayerHandler(client, request) + handler.handle() + if __name__ == "__main__": unittest.main() diff --git a/src/frontends/tests/gamespy/game_status/mock_objects.py b/src/frontends/tests/gamespy/game_status/mock_objects.py index 59d634f54..c7012fe46 100644 --- a/src/frontends/tests/gamespy/game_status/mock_objects.py +++ b/src/frontends/tests/gamespy/game_status/mock_objects.py @@ -1,3 +1,4 @@ +from datetime import datetime from typing import cast from frontends.gamespy.library.configs import CONFIG from frontends.gamespy.protocols.game_status.applications.client import Client @@ -44,9 +45,10 @@ def create_client() -> Client: config, GetPlayerDataHandler, GetPlayerDataResult( - keyvalues={"hello": "hello_value", "hi": "hi_value"}, + data="\\key1\\value1\\key2\\value2\\key3\\value3", local_id=0, - profile_id=0 + profile_id=0, + modified=datetime.now() ).model_dump(), ) create_mock_url( From 931032f96cd9b861afc51efe67f9f268d6f2e4e1 Mon Sep 17 00:00:00 2001 From: xiaojiuwo Date: Sat, 15 Nov 2025 15:34:09 +0000 Subject: [PATCH 226/231] Update: service launch logic --- common/config.json | 25 +++--- src/backends/library/utils/misc.py | 9 +++ .../gamespy/game_traffic_relay/data.py | 12 ++- .../gamespy/game_traffic_relay/handlers.py | 11 +-- .../gamespy/game_traffic_relay/responses.py | 5 -- .../routers/gamespy/game_traffic_relay.py | 14 ++-- src/backends/routers/home.py | 8 +- src/backends/services/register.py | 2 +- src/frontends/app.py | 22 +++--- .../library/abstractions/server_launcher.py | 76 +++++++++++-------- src/frontends/gamespy/library/configs.py | 3 +- .../library/extentions/debug_helper.py | 6 +- .../library/extentions/file_watcher.py | 29 +++++++ .../gamespy/library/extentions/schedular.py | 33 -------- .../gamespy/library/network/http_handler.py | 2 +- .../gamespy/library/network/tcp_handler.py | 2 +- .../gamespy/library/network/udp_handler.py | 2 +- .../chat/applications/server_launcher.py | 10 +-- .../applications/server_launcher.py | 10 +-- .../applications/connection.py | 11 --- .../applications/server_launcher.py | 50 +++--------- .../natneg/applications/server_launcher.py | 10 +-- .../applications/server_launcher.py | 10 +-- .../applications/server_launcher.py | 10 +-- .../applications/server_launcher.py | 10 +-- .../applications/server_launcher.py | 10 +-- .../applications/server_launcher.py | 10 +-- src/frontends/tests/test_runner.py | 9 +++ 28 files changed, 202 insertions(+), 209 deletions(-) create mode 100644 src/backends/library/utils/misc.py delete mode 100644 src/backends/protocols/gamespy/game_traffic_relay/responses.py create mode 100644 src/frontends/gamespy/library/extentions/file_watcher.py delete mode 100644 src/frontends/gamespy/library/extentions/schedular.py create mode 100644 src/frontends/tests/test_runner.py diff --git a/common/config.json b/common/config.json index 140b0bc71..4d11cefdd 100644 --- a/common/config.json +++ b/common/config.json @@ -3,7 +3,8 @@ "url": "http://unispy_backends:8080", "token_secret_key": "I love UniSpy", "token_algorithm": "HS256", - "token_expire_time": 30 + "token_expire_time": 30, + "is_check_public_ip": false }, "logging": { "path": "~/Downloads/unispy_server/log/", @@ -40,67 +41,67 @@ "PresenceConnectionManager": { "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "PresenceConnectionManager", - "public_address": "0.0.0.0", + "listening_address": "0.0.0.0", "listening_port": 29900 }, "PresenceSearchPlayer": { "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "PresenceSearchPlayer", - "public_address": "0.0.0.0", + "listening_address": "0.0.0.0", "listening_port": 29901 }, "CDKey": { "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "CDKey", - "public_address": "0.0.0.0", + "listening_address": "0.0.0.0", "listening_port": 29910 }, "ServerBrowserV1": { "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "ServerBrowserV1", - "public_address": "0.0.0.0", + "listening_address": "0.0.0.0", "listening_port": 28900 }, "ServerBrowserV2": { "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "ServerBrowserV2", - "public_address": "0.0.0.0", + "listening_address": "0.0.0.0", "listening_port": 28910 }, "QueryReport": { "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "QueryReport", - "public_address": "0.0.0.0", + "listening_address": "0.0.0.0", "listening_port": 27900 }, "NatNegotiation": { "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "NatNegotiation", - "public_address": "0.0.0.0", + "listening_address": "0.0.0.0", "listening_port": 27901 }, "GameStatus": { "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "GameStatus", - "public_address": "0.0.0.0", + "listening_address": "0.0.0.0", "listening_port": 29920 }, "Chat": { "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "Chat", - "public_address": "0.0.0.0", + "listening_address": "0.0.0.0", "listening_port": 6667 }, "WebServices": { "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "WebServices", - "public_address": "0.0.0.0", + "listening_address": "0.0.0.0", "listening_port": 80 }, "GameTrafficRelay": { "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "server_name": "GameTrafficRelay", - "public_address": "0.0.0.0", + "listening_address": "0.0.0.0", "listening_port": 10086 } }, diff --git a/src/backends/library/utils/misc.py b/src/backends/library/utils/misc.py new file mode 100644 index 000000000..2f96c2bc7 --- /dev/null +++ b/src/backends/library/utils/misc.py @@ -0,0 +1,9 @@ +from frontends.gamespy.library.configs import CONFIG +from frontends.gamespy.library.exceptions.general import UniSpyException + + +def check_public_ip(real_ip: str, report_ip: str): + if CONFIG.backend.is_check_public_ip: + if real_ip != report_ip: + raise UniSpyException( + "client real ip is not equal to its config ip") diff --git a/src/backends/protocols/gamespy/game_traffic_relay/data.py b/src/backends/protocols/gamespy/game_traffic_relay/data.py index 8e9b27296..76aedad97 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/data.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/data.py @@ -1,4 +1,4 @@ -from datetime import datetime +from datetime import datetime, timedelta from uuid import UUID from backends.library.database.pg_orm import RelayServerCaches @@ -41,6 +41,16 @@ def create_relay_server(info: RelayServerCaches, session: Session): session.commit() +def check_expired_server(session: Session): + expire_time = datetime.now()-timedelta(seconds=30) + session.query( + RelayServerCaches + ).where( + RelayServerCaches.update_time < expire_time + ).delete() + session.commit() + + def delete_relay_server(server_id: UUID, ip_address: str, port: int, session: Session): assert isinstance(server_id, UUID) assert isinstance(ip_address, str) diff --git a/src/backends/protocols/gamespy/game_traffic_relay/handlers.py b/src/backends/protocols/gamespy/game_traffic_relay/handlers.py index 1c539e5f7..4a10890cd 100644 --- a/src/backends/protocols/gamespy/game_traffic_relay/handlers.py +++ b/src/backends/protocols/gamespy/game_traffic_relay/handlers.py @@ -1,5 +1,6 @@ -from datetime import datetime +from datetime import datetime, timedelta import logging +import socket from backends.library.abstractions.contracts import OKResponse from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import RelayServerCaches @@ -8,19 +9,15 @@ ) import backends.protocols.gamespy.game_traffic_relay.data as data +from frontends.gamespy.library.exceptions.general import UniSpyException class GtrHeartBeatHandler(HandlerBase): _request: GtrHeartBeatRequest response: OKResponse - # def __init__(self, request: GtrHeartBeatRequest) -> None: - # assert isinstance(request, GtrHeartBeatRequest) - # self._request = request - # self._result = None - # self.logger = logging.getLogger("backend") - def _data_operate(self) -> None: + data.check_expired_server(self._session) info = data.search_relay_server( self._request.server_id, self._request.public_ip_address, self._session ) diff --git a/src/backends/protocols/gamespy/game_traffic_relay/responses.py b/src/backends/protocols/gamespy/game_traffic_relay/responses.py deleted file mode 100644 index f698368d2..000000000 --- a/src/backends/protocols/gamespy/game_traffic_relay/responses.py +++ /dev/null @@ -1,5 +0,0 @@ -from pydantic import BaseModel - - -class GetMyIPResponse(BaseModel): - ip: str diff --git a/src/backends/routers/gamespy/game_traffic_relay.py b/src/backends/routers/gamespy/game_traffic_relay.py index 6a2340cd7..540cd4326 100644 --- a/src/backends/routers/gamespy/game_traffic_relay.py +++ b/src/backends/routers/gamespy/game_traffic_relay.py @@ -1,26 +1,24 @@ from fastapi import APIRouter, Request from backends.library.abstractions.contracts import RESPONSES_DEF, Response +from backends.library.utils.misc import check_public_ip from backends.protocols.gamespy.game_traffic_relay.handlers import ( GtrHeartBeatHandler, ) from backends.protocols.gamespy.game_traffic_relay.requests import ( GtrHeartBeatRequest, ) -from backends.protocols.gamespy.game_traffic_relay.responses import GetMyIPResponse from backends.urls import GAME_TRAFFIC_RELAY +from frontends.gamespy.library.exceptions.general import UniSpyException router = APIRouter() -@router.post(f"{GAME_TRAFFIC_RELAY}/get_my_ip", responses=RESPONSES_DEF) -def get_my_ip(request: Request) -> GetMyIPResponse: - assert request.client - return GetMyIPResponse(ip=request.client.host) +@router.post(f"{GAME_TRAFFIC_RELAY}/Heartbeat", responses=RESPONSES_DEF) +def heartbeat(request: Request, heartbeat: GtrHeartBeatRequest) -> Response: + assert request.client is not None + check_public_ip(request.client.host, heartbeat.public_ip_address) - -@router.post(f"{GAME_TRAFFIC_RELAY}/heartbeat", responses=RESPONSES_DEF) -def heartbeat(heartbeat: GtrHeartBeatRequest) -> Response: handler = GtrHeartBeatHandler(heartbeat) handler.handle() return handler.response diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index b18d3c721..b90ca2124 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -8,10 +8,11 @@ from backends.library.abstractions.contracts import ErrorResponse from backends.library.database.pg_orm import ENGINE +from backends.library.utils.misc import check_public_ip from backends.services.register import register_services from frontends.gamespy.library.exceptions.general import UniSpyException from frontends.gamespy.library.log.log_manager import LogManager -from frontends.gamespy.library.configs import ServerConfig +from frontends.gamespy.library.configs import CONFIG, ServerConfig from backends.routers.gamespy import ( chat, game_stats, @@ -69,9 +70,10 @@ def general_exception_handler(_, exc: Exception): @app.post("/") -def home(request: Request, config: ServerConfig,) -> dict: +def home(request: Request, config: ServerConfig) -> dict: # todo add the server config to our database assert request.client is not None + check_public_ip(request.client.host, config.listening_address) # response = register_services(config, request.client.host) return {"status": "online"} @@ -88,4 +90,4 @@ def get_auth_token(request: RegisterRequest): if __name__ == "__main__": uvicorn.run("backends.routers.home:app", - host="127.0.0.1", port=8080, reload=True) + host="0.0.0.0", port=8080, reload=True) diff --git a/src/backends/services/register.py b/src/backends/services/register.py index 23d2f1ce5..0ae866ad6 100644 --- a/src/backends/services/register.py +++ b/src/backends/services/register.py @@ -19,7 +19,7 @@ def register_services(config: ServerConfig, external_ip: str) -> dict: server_id=config.server_id, server_name=config.server_name, external_ip="", - listening_ip=config.public_address, + listening_ip=config.listening_address, listening_port=config.listening_port, update_time=datetime.now() ) diff --git a/src/frontends/app.py b/src/frontends/app.py index c9870cd2d..cadc6a811 100644 --- a/src/frontends/app.py +++ b/src/frontends/app.py @@ -4,17 +4,17 @@ if __name__ == "__main__": - from frontends.gamespy.protocols.chat.applications.server_launcher import ServerLauncher as chat - from frontends.gamespy.protocols.game_status.applications.server_launcher import ServerLauncher as gs - from frontends.gamespy.protocols.game_traffic_relay.applications.server_launcher import ServerLauncher as gtr - from frontends.gamespy.protocols.natneg.applications.server_launcher import ServerLauncher as nn - from frontends.gamespy.protocols.presence_connection_manager.applications.server_launcher import ServerLauncher as pcm - from frontends.gamespy.protocols.presence_search_player.applications.server_launcher import ServerLauncher as psp - from frontends.gamespy.protocols.query_report.applications.server_launcher import ServerLauncher as qr - from frontends.gamespy.protocols.server_browser.applications.server_launcher import ServerLauncher as sb - from frontends.gamespy.protocols.web_services.applications.server_launcher import ServerLauncher as web + from frontends.gamespy.protocols.chat.applications.server_launcher import Service as chat + from frontends.gamespy.protocols.game_status.applications.server_launcher import Service as gs + from frontends.gamespy.protocols.game_traffic_relay.applications.server_launcher import Service as gtr + from frontends.gamespy.protocols.natneg.applications.server_launcher import Service as nn + from frontends.gamespy.protocols.presence_connection_manager.applications.server_launcher import Service as pcm + from frontends.gamespy.protocols.presence_search_player.applications.server_launcher import Service as psp + from frontends.gamespy.protocols.query_report.applications.server_launcher import Service as qr + from frontends.gamespy.protocols.server_browser.applications.server_launcher import Service as sb + from frontends.gamespy.protocols.web_services.applications.server_launcher import Service as web - from frontends.gamespy.library.abstractions.server_launcher import ServerFactory + from frontends.gamespy.library.abstractions.server_launcher import ServicesFactory launchers = [ chat(), gs(), @@ -27,6 +27,6 @@ web() ] - factory = ServerFactory(launchers) + factory = ServicesFactory(launchers) helper = DebugHelper("./frontends", factory) helper.start() diff --git a/src/frontends/gamespy/library/abstractions/server_launcher.py b/src/frontends/gamespy/library/abstractions/server_launcher.py index 58e20664d..6b1b5c44d 100644 --- a/src/frontends/gamespy/library/abstractions/server_launcher.py +++ b/src/frontends/gamespy/library/abstractions/server_launcher.py @@ -1,9 +1,8 @@ -from threading import Thread, Event from types import MappingProxyType from frontends.gamespy.library.abstractions.connections import NetworkServerBase from frontends.gamespy.library.exceptions.general import UniSpyException -from frontends.gamespy.library.extentions.schedular import Schedular +import schedule from frontends.gamespy.library.log.log_manager import LogManager, LogWriter from frontends.gamespy.library.configs import CONFIG, ServerConfig import pyfiglet @@ -30,30 +29,30 @@ ) -class ServerLauncherBase: +class ServiceBase: config: ServerConfig _config_name: str - _server_cls: type[NetworkServerBase] + _network_server_cls: type[NetworkServerBase] _client_cls: type[ClientBase] _logger: LogWriter - _server: NetworkServerBase - _available_checker: Schedular + _network_server: NetworkServerBase + _available_checker: object def __init__( self, config_name: str, client_cls: type[ClientBase], - server_cls: type[NetworkServerBase], + network_server_cls: type[NetworkServerBase], ): assert issubclass(client_cls, ClientBase) - assert issubclass(server_cls, NetworkServerBase) + assert issubclass(network_server_cls, NetworkServerBase) assert isinstance(config_name, str) assert config_name in CONFIG.servers self.config = CONFIG.servers[config_name] - self._server_cls = server_cls + self._network_server_cls = network_server_cls self._client_cls = client_cls self._create_logger() - self._create_server() + self._create_network_server() @final def _create_logger(self): @@ -62,20 +61,20 @@ def _create_logger(self): self._logger = LogManager.create(short_name) @final - def _create_server(self): + def _create_network_server(self): assert self._logger is not None - assert self._server_cls is not None + assert self._network_server_cls is not None assert self._client_cls is not None - self._server = self._server_cls( + self._network_server = self._network_server_cls( self.config, self._client_cls, self._logger) @final def start(self): - self._server.start() + self._network_server.start() @final def stop(self): - self._server.stop() + self._network_server.stop() @staticmethod def get_data_from_backends(url: str, json_str: str): @@ -99,9 +98,9 @@ def _heartbeat_to_backend(self, url: str, json_str: str): send heartbeat to backends """ assert isinstance(json_str, str) - ServerLauncherBase.get_data_from_backends(url, json_str=json_str) + ServiceBase.get_data_from_backends(url, json_str=json_str) - def connect_to_backend(self): + def _connect_to_backend(self): """ check backend availability """ @@ -112,35 +111,47 @@ def connect_to_backend(self): CONFIG.backend.url, self.config.model_dump_json()) @final - def launch_heartbeat_schedular(self): + def start_post_tasks(self): + """ + run post tasks + - set the schedular to send heartbeat info to backend to keep the infomation update + """ + # launch schedular + schedule.every(30).seconds.do(self._post_task) + + def _post_task(self): """ - set the schedular to send heartbeat info to backend to keep the infomation update + the post task after network server launched + call this function in server factory """ - #! temperarily use connect to backend function - self._available_checker = Schedular(self.connect_to_backend, 30) - self._available_checker.start() + self._connect_to_backend() -class ServerFactory: - _lauchers: list[ServerLauncherBase] +class ServicesFactory: + _lauchers: list[ServiceBase] def __init__( self, - launchers: list[ServerLauncherBase] + launchers: list[ServiceBase] ): self._lauchers = launchers def start(self): - self._connect_to_backend() self.__show_unispy_logo() self._launch_servers() print("Server successfully launched.") + print("Press ctr+c to Quit\n") + self._run_post_tasks() self._keep_running() - def _connect_to_backend(self): - for info in self._lauchers: - info.connect_to_backend() - info.launch_heartbeat_schedular() + def _run_post_tasks(self): + """ + run the launcher post task + """ + for launcher in self._lauchers: + launcher.start_post_tasks() + # call all post tasks immediately + schedule.run_all() def __show_unispy_logo(self): # display logo @@ -157,7 +168,7 @@ def __show_unispy_logo(self): table.add_row( [ info.config.server_name, - info.config.public_address, + info.config.listening_address, info.config.listening_port, ] ) @@ -174,11 +185,12 @@ def _launch_servers(self) -> None: @final def _keep_running(self): - print("Press ctr+c to Quit\n") from time import sleep try: while True: sleep(1) + # run schedule here + schedule.run_pending() pass except KeyboardInterrupt: for info in self._lauchers: diff --git a/src/frontends/gamespy/library/configs.py b/src/frontends/gamespy/library/configs.py index 4e2277733..7ce7fd779 100644 --- a/src/frontends/gamespy/library/configs.py +++ b/src/frontends/gamespy/library/configs.py @@ -41,7 +41,7 @@ def url(self) -> str: class ServerConfig(BaseModel): server_id: UUID server_name: str - public_address: str + listening_address: str listening_port: int # Ensures listening_port is between 1 and 65535 @@ -61,6 +61,7 @@ class BackendConfig(BaseModel): token_secret_key: str token_algorithm: str token_expire_time: int = 30 + is_check_public_ip: bool class UnittestConfig(BaseModel): diff --git a/src/frontends/gamespy/library/extentions/debug_helper.py b/src/frontends/gamespy/library/extentions/debug_helper.py index 41c2f707c..643fc447e 100644 --- a/src/frontends/gamespy/library/extentions/debug_helper.py +++ b/src/frontends/gamespy/library/extentions/debug_helper.py @@ -6,7 +6,7 @@ from watchdog.events import FileSystemEventHandler from watchdog.observers import Observer if TYPE_CHECKING: - from frontends.gamespy.library.abstractions.server_launcher import ServerFactory + from frontends.gamespy.library.abstractions.server_launcher import ServicesFactory class FileChangeHandler(FileSystemEventHandler): @@ -43,9 +43,9 @@ def on_modified(self, event): class DebugHelper: _observer: object _folder_path: str - _factory: "ServerFactory" + _factory: "ServicesFactory" - def __init__(self, folder_path: str, factory: "ServerFactory") -> None: + def __init__(self, folder_path: str, factory: "ServicesFactory") -> None: self._folder_path = folder_path self._factory = factory diff --git a/src/frontends/gamespy/library/extentions/file_watcher.py b/src/frontends/gamespy/library/extentions/file_watcher.py new file mode 100644 index 000000000..06893cc4e --- /dev/null +++ b/src/frontends/gamespy/library/extentions/file_watcher.py @@ -0,0 +1,29 @@ +import time +from watchdog.observers import Observer +from watchdog.events import FileSystemEventHandler + +class FileWatcher: + pass + +class MyHandler(FileSystemEventHandler): + def on_any_event(self, event): + if event.is_directory: + return None + elif event.event_type == 'created': + print(f"Created: {event.src_path}") + elif event.event_type =='modified': + print(f"Modified: {event.src_path}") + + +if __name__ == "__main__": + event_handler = MyHandler() + observer = Observer() + path = '.' # Monitor the current directory + observer.schedule(event_handler, path, recursive=True) + observer.start() + try: + while True: + time.sleep(1) + except KeyboardInterrupt: + observer.stop() + observer.join() \ No newline at end of file diff --git a/src/frontends/gamespy/library/extentions/schedular.py b/src/frontends/gamespy/library/extentions/schedular.py deleted file mode 100644 index d5bb9d8a1..000000000 --- a/src/frontends/gamespy/library/extentions/schedular.py +++ /dev/null @@ -1,33 +0,0 @@ -import threading -import time -from typing import Callable - -import schedule - - -class Schedular: - _job_func: Callable - _is_started: bool - _interval: int - - def __init__(self, job_func: Callable, interval: int) -> None: - self._job_func = job_func - self._interval = interval - schedule.every(interval).seconds.do(job_func) - self._is_started = False - - def start(self): - scheduler_thread = threading.Thread(target=self._run_schedule) - scheduler_thread.daemon = True - scheduler_thread.start() - - def _run_schedule(self): - self._is_started = True - while True: - if not self._is_started: - break - schedule.run_pending() - time.sleep(self._interval/2) - - def stop(self): - self._is_started = False diff --git a/src/frontends/gamespy/library/network/http_handler.py b/src/frontends/gamespy/library/network/http_handler.py index 3c633eb22..0e0c81734 100644 --- a/src/frontends/gamespy/library/network/http_handler.py +++ b/src/frontends/gamespy/library/network/http_handler.py @@ -60,7 +60,7 @@ def __init__( ) -> None: super().__init__(config, t_client, logger) self._server = ThreadingHTTPServer( - (self._config.public_address, self._config.listening_port), HttpHandler + (self._config.listening_address, self._config.listening_port), HttpHandler ) self._server.unispy_params = (self._config, self._client_cls, self._logger) # type: ignore diff --git a/src/frontends/gamespy/library/network/tcp_handler.py b/src/frontends/gamespy/library/network/tcp_handler.py index 69dd76c0d..f4390c04b 100644 --- a/src/frontends/gamespy/library/network/tcp_handler.py +++ b/src/frontends/gamespy/library/network/tcp_handler.py @@ -57,7 +57,7 @@ def __init__( ) -> None: super().__init__(config, t_client, logger) self._server = socketserver.ThreadingTCPServer( - (self._config.public_address, self._config.listening_port), + (self._config.listening_address, self._config.listening_port), TcpHandler, bind_and_activate=False ) diff --git a/src/frontends/gamespy/library/network/udp_handler.py b/src/frontends/gamespy/library/network/udp_handler.py index 7483986aa..852782962 100644 --- a/src/frontends/gamespy/library/network/udp_handler.py +++ b/src/frontends/gamespy/library/network/udp_handler.py @@ -36,7 +36,7 @@ def __init__( ) -> None: super().__init__(config, t_client, logger) self._server = socketserver.ThreadingUDPServer( - (self._config.public_address, self._config.listening_port), + (self._config.listening_address, self._config.listening_port), UdpHandler, ) # inject the handler params to ThreadingUDPServer diff --git a/src/frontends/gamespy/protocols/chat/applications/server_launcher.py b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py index 47c06d06f..a4e0e855a 100644 --- a/src/frontends/gamespy/protocols/chat/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/chat/applications/server_launcher.py @@ -1,21 +1,21 @@ -from frontends.gamespy.library.abstractions.server_launcher import ServerLauncherBase, ServerFactory +from frontends.gamespy.library.abstractions.server_launcher import ServiceBase, ServicesFactory from frontends.gamespy.library.network.tcp_handler import TcpServer from frontends.gamespy.protocols.chat.applications.client import Client -class ServerLauncher(ServerLauncherBase): +class Service(ServiceBase): def __init__(self) -> None: super().__init__( config_name="Chat", client_cls=Client, - server_cls=TcpServer, + network_server_cls=TcpServer, ) if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper - chat = ServerLauncher() - helper = DebugHelper("./frontends/", ServerFactory([chat])) + chat = Service() + helper = DebugHelper("./frontends/", ServicesFactory([chat])) helper.start() diff --git a/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py index ecd375697..5b9c19806 100644 --- a/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_status/applications/server_launcher.py @@ -1,21 +1,21 @@ from frontends.gamespy.protocols.game_status.applications.client import Client -from frontends.gamespy.library.abstractions.server_launcher import ServerFactory, ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServicesFactory, ServiceBase from frontends.gamespy.library.network.tcp_handler import TcpServer -class ServerLauncher(ServerLauncherBase): +class Service(ServiceBase): server: "TcpServer" def __init__(self) -> None: super().__init__( config_name="GameStatus", client_cls=Client, - server_cls=TcpServer, + network_server_cls=TcpServer, ) if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper - gs = ServerLauncher() - helper = DebugHelper("./frontends/", ServerFactory([gs])) + gs = Service() + helper = DebugHelper("./frontends/", ServicesFactory([gs])) helper.start() diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/connection.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/connection.py index d6f9d5f5c..4a0bba74b 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/connection.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/connection.py @@ -1,9 +1,7 @@ -from datetime import datetime, timedelta from enum import Enum from typing import TYPE_CHECKING from frontends.gamespy.library.exceptions.general import UniSpyException -from frontends.gamespy.library.extentions.schedular import Schedular if TYPE_CHECKING: from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client @@ -84,16 +82,7 @@ def check_whether_accept_new_connection(cookie: int, client: "Client", clients: else: raise UniSpyException( f"cookie: {cookie} is alive, you can not neogotiate with exist connections") - # expire_time = datetime.now() - timedelta(minutes=5) - # if 2 clients are expired - # if all(client.info.last_receive_time < expire_time for client in clients): - # client.log_info( - # f"cookie:{cookie} is expired, replace with new clients") - # ConnectionListener.cookie_pool[cookie] = [client] - # return # if current client is not in the pair if len(clients) == 1 and client not in clients: clients.append(client) - - diff --git a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py index 1fbbd5e1a..229b47843 100644 --- a/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/game_traffic_relay/applications/server_launcher.py @@ -1,7 +1,6 @@ from datetime import datetime, timedelta -from frontends.gamespy.library.abstractions.server_launcher import ServerFactory, ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServicesFactory, ServiceBase from frontends.gamespy.library.configs import CONFIG -from frontends.gamespy.library.extentions.schedular import Schedular from frontends.gamespy.library.log.log_manager import GLOBAL_LOGGER from frontends.gamespy.library.network.udp_handler import UdpServer from frontends.gamespy.protocols.game_traffic_relay.applications.client import Client @@ -11,58 +10,33 @@ ) -class ServerLauncher(ServerLauncherBase): - _public_ip: str +class Service(ServiceBase): def __init__(self) -> None: super().__init__( config_name="GameTrafficRelay", client_cls=Client, - server_cls=UdpServer, + network_server_cls=UdpServer, ) - def start(self): - self._get_public_ip() - self._launch_connection_expire_checker() - super().start() + def _post_task(self): + super()._post_task() + self.__gtr_heartbeat() + self.__check_expired_connection() - def _get_public_ip(self): - url = f"{CONFIG.backend.url}/GameSpy/GameTrafficRelay/get_my_ip" - data = ServerLauncherBase.get_data_from_backends( - url=url, json_str="{}") - self._public_ip = data['ip'] - - def _gtr_heartbeat(self): + def __gtr_heartbeat(self): assert self.config req = GtrHeartbeat( server_id=self.config.server_id, - public_ip_address=self.config.public_address, + public_ip_address=self.config.listening_address, public_port=self.config.listening_port, client_count=len(ConnectionListener.client_pool), ) req_str = req.model_dump_json() self._heartbeat_to_backend( - f"{CONFIG.backend.url}/GameSpy/GameTrafficRelay/heartbeat", req_str + f"{CONFIG.backend.url}/GameSpy/GameTrafficRelay/Heartbeat", req_str ) - def connect_to_backend(self): - """ - check backend availability - """ - assert self._logger is not None - if CONFIG.unittest.is_collect_request: - self._logger.debug( - "CONFIG.unittest.is_collect_request is enabled ignore send heartbeat to backend" - ) - return - super().connect_to_backend() - self._gtr_heartbeat() - - # region Expire Checker - def _launch_connection_expire_checker(self): - self._conn_checker = Schedular(self.__check_expired_connection, 30) - self._conn_checker.start() - def __check_expired_connection(self): expired_time = datetime.now() - timedelta(seconds=30) try: @@ -78,6 +52,6 @@ def __check_expired_connection(self): if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper - gtr = ServerLauncher() - helper = DebugHelper("./frontends/", ServerFactory([gtr])) + gtr = Service() + helper = DebugHelper("./frontends/", ServicesFactory([gtr])) helper.start() diff --git a/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py b/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py index 317fc73e5..a9b2bf440 100644 --- a/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/natneg/applications/server_launcher.py @@ -1,21 +1,21 @@ -from frontends.gamespy.library.abstractions.server_launcher import ServerFactory, ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServicesFactory, ServiceBase from frontends.gamespy.library.network.udp_handler import UdpServer from frontends.gamespy.protocols.natneg.applications.client import Client -class ServerLauncher(ServerLauncherBase): +class Service(ServiceBase): server: UdpServer def __init__(self) -> None: super().__init__( config_name="NatNegotiation", client_cls=Client, - server_cls=UdpServer, + network_server_cls=UdpServer, ) if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper - nn = ServerLauncher() - helper = DebugHelper("./frontends/", ServerFactory([nn])) + nn = Service() + helper = DebugHelper("./frontends/", ServicesFactory([nn])) helper.start() diff --git a/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py b/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py index 3334d1313..4dc4cd3ea 100644 --- a/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/presence_connection_manager/applications/server_launcher.py @@ -1,21 +1,21 @@ -from frontends.gamespy.library.abstractions.server_launcher import ServerFactory, ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServicesFactory, ServiceBase from frontends.gamespy.library.network.tcp_handler import TcpServer from frontends.gamespy.protocols.presence_connection_manager.applications.client import ( Client, ) -class ServerLauncher(ServerLauncherBase): +class Service(ServiceBase): def __init__(self) -> None: super().__init__( config_name="PresenceConnectionManager", client_cls=Client, - server_cls=TcpServer, + network_server_cls=TcpServer, ) if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper - pcm = ServerLauncher() - helper = DebugHelper("./frontends/", ServerFactory([pcm])) + pcm = Service() + helper = DebugHelper("./frontends/", ServicesFactory([pcm])) helper.start() diff --git a/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py b/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py index 649b9fd84..8acdf0155 100644 --- a/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/presence_search_player/applications/server_launcher.py @@ -1,21 +1,21 @@ -from frontends.gamespy.library.abstractions.server_launcher import ServerFactory, ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServicesFactory, ServiceBase from frontends.gamespy.library.network.tcp_handler import TcpServer from frontends.gamespy.protocols.presence_search_player.applications.client import ( Client, ) -class ServerLauncher(ServerLauncherBase): +class Service(ServiceBase): def __init__(self) -> None: super().__init__( config_name="PresenceSearchPlayer", client_cls=Client, - server_cls=TcpServer + network_server_cls=TcpServer ) if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper - psp = ServerLauncher() - helper = DebugHelper("./frontends/", ServerFactory([psp])) + psp = Service() + helper = DebugHelper("./frontends/", ServicesFactory([psp])) helper.start() diff --git a/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py b/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py index 781c8d925..b99f3b9d5 100644 --- a/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/query_report/applications/server_launcher.py @@ -1,21 +1,21 @@ -from frontends.gamespy.library.abstractions.server_launcher import ServerFactory, ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServicesFactory, ServiceBase from frontends.gamespy.library.network.udp_handler import UdpServer from frontends.gamespy.protocols.query_report.applications.client import Client -class ServerLauncher(ServerLauncherBase): +class Service(ServiceBase): natneg_channel: object def __init__(self) -> None: super().__init__( config_name="QueryReport", client_cls=Client, - server_cls=UdpServer + network_server_cls=UdpServer ) if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper - qr = ServerLauncher() - helper = DebugHelper("./frontends/", ServerFactory([qr])) + qr = Service() + helper = DebugHelper("./frontends/", ServicesFactory([qr])) helper.start() diff --git a/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py b/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py index 56b75ccfe..b21f3e846 100644 --- a/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/server_browser/applications/server_launcher.py @@ -1,18 +1,18 @@ -from frontends.gamespy.library.abstractions.server_launcher import ServerFactory, ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServicesFactory, ServiceBase from frontends.gamespy.library.network.tcp_handler import TcpServer from frontends.gamespy.protocols.server_browser.v2.applications.client import Client -class ServerLauncher(ServerLauncherBase): +class Service(ServiceBase): def __init__(self) -> None: super().__init__( - config_name="ServerBrowserV2", client_cls=Client, server_cls=TcpServer + config_name="ServerBrowserV2", client_cls=Client, network_server_cls=TcpServer ) if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper - sb2 = ServerLauncher() + sb2 = Service() # todo: add v1 server here - helper = DebugHelper("./frontends/", ServerFactory([sb2])) + helper = DebugHelper("./frontends/", ServicesFactory([sb2])) helper.start() diff --git a/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py b/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py index d0eea71d0..256a5be71 100644 --- a/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py +++ b/src/frontends/gamespy/protocols/web_services/applications/server_launcher.py @@ -1,19 +1,19 @@ -from frontends.gamespy.library.abstractions.server_launcher import ServerFactory, ServerLauncherBase +from frontends.gamespy.library.abstractions.server_launcher import ServicesFactory, ServiceBase from frontends.gamespy.library.network.http_handler import HttpServer from frontends.gamespy.protocols.web_services.applications.client import Client -class ServerLauncher(ServerLauncherBase): +class Service(ServiceBase): def __init__(self) -> None: super().__init__( config_name="WebServices", client_cls=Client, - server_cls=HttpServer + network_server_cls=HttpServer ) if __name__ == "__main__": from frontends.gamespy.library.extentions.debug_helper import DebugHelper - web = ServerLauncher() - helper = DebugHelper("./frontends/", ServerFactory([web])) + web = Service() + helper = DebugHelper("./frontends/", ServicesFactory([web])) helper.start() diff --git a/src/frontends/tests/test_runner.py b/src/frontends/tests/test_runner.py new file mode 100644 index 000000000..69784e53e --- /dev/null +++ b/src/frontends/tests/test_runner.py @@ -0,0 +1,9 @@ +if __name__ == "__main__": + import unittest + # Create a test suite + loader = unittest.TestLoader() + suite = loader.discover(start_dir='./', pattern='*tests.py') + + # Run the tests + runner = unittest.TextTestRunner() + runner.run(suite) From 54124bf4536069e6d57dd48f3ecb752e7585285f Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Mon, 24 Nov 2025 03:52:11 +0000 Subject: [PATCH 227/231] Update(qr): added v1 heartbeat --- common/UniSpy_pg.sql | 9 ++- src/backends/library/database/pg_orm.py | 16 ++--- .../protocols/gamespy/query_report/data.py | 57 +++++++-------- .../gamespy/query_report/handlers.py | 69 +++++++++++++++---- .../gamespy/query_report/requests.py | 22 ++++-- .../gamespy/server_browser/handlers.py | 10 +-- src/backends/routers/gamespy/query_report.py | 23 ++++--- .../gamespy/library/abstractions/client.py | 6 +- .../gamespy/library/abstractions/switcher.py | 3 +- .../query_report/aggregates/enums.py | 13 ++++ .../aggregates/game_server_info.py | 10 +-- .../query_report/applications/client.py | 12 ++-- .../query_report/v1/abstractions/contracts.py | 3 + .../query_report/v1/aggregates/enums.py | 12 ++++ .../query_report/v1/applications/handlers.py | 31 +++++++++ .../query_report/v1/applications/switcher.py | 31 +++++++++ .../query_report/v1/contracts/requests.py | 66 ++++++++++++++++++ .../query_report/v1/contracts/responses.py | 10 +++ .../query_report/v1/contracts/results.py | 7 ++ .../{cmd_handler_base.py => handlers.py} | 0 .../query_report/v2/aggregates/enums.py | 5 -- .../query_report/v2/applications/handlers.py | 2 +- .../query_report/v2/applications/switcher.py | 2 +- .../query_report/v2/contracts/requests.py | 52 ++++++-------- .../v2/aggregations/server_info_builder.py | 20 +++--- .../v2/applications/handlers.py | 9 +-- .../server_browser/v2/contracts/responses.py | 20 ++---- .../gamespy/query_report/request_tests.py | 8 +-- .../gamespy/server_browser/contract_tests.py | 13 ++-- 29 files changed, 365 insertions(+), 176 deletions(-) create mode 100644 src/frontends/gamespy/protocols/query_report/aggregates/enums.py create mode 100644 src/frontends/gamespy/protocols/query_report/v1/aggregates/enums.py create mode 100644 src/frontends/gamespy/protocols/query_report/v1/applications/handlers.py create mode 100644 src/frontends/gamespy/protocols/query_report/v1/applications/switcher.py create mode 100644 src/frontends/gamespy/protocols/query_report/v1/contracts/requests.py create mode 100644 src/frontends/gamespy/protocols/query_report/v1/contracts/responses.py create mode 100644 src/frontends/gamespy/protocols/query_report/v1/contracts/results.py rename src/frontends/gamespy/protocols/query_report/v2/abstractions/{cmd_handler_base.py => handlers.py} (100%) diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index 753409b84..bd16c6532 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -217,16 +217,15 @@ ALTER SEQUENCE unispy.friends_friendid_seq OWNED BY unispy.friends.friendid; -- CREATE TABLE unispy.game_server_caches ( - instant_key character varying(10) PRIMARY KEY NOT NULL UNIQUE, + id SERIAL PRIMARY KEY NOT NULL, + instant_key character varying(10), server_id uuid NOT NULL, host_ip_address inet NOT NULL, game_name character varying NOT NULL, query_report_port integer NOT NULL, update_time timestamp without time zone NOT NULL, status smallint not NULL, - player_data jsonb NOT NULL, - server_data jsonb NOT NULL, - team_data jsonb NOT NULL, + data jsonb NOT NULL, avaliable boolean ); @@ -807,7 +806,7 @@ COPY unispy.friends (friendid, profileid, targetid, namespaceid) FROM stdin; -- Data for Name: game_server_caches; Type: TABLE DATA; Schema: unispy; Owner: unispy -- -COPY unispy.game_server_caches (instant_key, server_id, host_ip_address, game_name, query_report_port, update_time, status, player_data, server_data, team_data, avaliable) FROM stdin; +COPY unispy.game_server_caches (instant_key, server_id, host_ip_address, game_name, query_report_port, update_time, status, data, avaliable) FROM stdin; \. diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index 8df36133e..efe24702e 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -26,13 +26,13 @@ FriendRequestStatus, GPStatusCode, ) -from frontends.gamespy.protocols.query_report.v2.aggregates.enums import ( - GameServerStatus, -) + import sqlalchemy as sa import enum from sqlalchemy.orm.decl_api import DeclarativeBase +from frontends.gamespy.protocols.query_report.aggregates.enums import GameServerStatus + class IntEnum(TypeDecorator): impl = sa.Integer @@ -181,11 +181,10 @@ class PStorage(Base): "profiles.profileid"), nullable=False) ptype = Column(Integer, nullable=False) dindex = Column(Integer, nullable=False) - data = Column(String,nullable=False) + data = Column(String, nullable=False) update_time = Column(DateTime, nullable=False, default=datetime.now()) - class GameStatusSnapShot(Base): __tablename__ = "game_status_snapshot" id = Column(Integer, primary_key=True, autoincrement=True) @@ -310,15 +309,14 @@ class ChatChannelUserCaches(Base): class GameServerCaches(Base): __tablename__ = "game_server_caches" - instant_key = Column(String, primary_key=True, nullable=False) + id = Column(Integer, primary_key=True) + instant_key = Column(String, nullable=True) server_id = Column(UUID, nullable=False) host_ip_address = Column(INET, nullable=False) game_name = Column(String, nullable=False) query_report_port = Column(Integer, nullable=False) status = Column(IntEnum(GameServerStatus)) - player_data = Column(JSONB, nullable=False, default={}) - server_data = Column(JSONB, nullable=False, default=[]) - team_data = Column(JSONB, nullable=False, default=[]) + data = Column(JSONB, nullable=False, default={}) avaliable = Column(Boolean, nullable=True) update_time = Column(DateTime, nullable=False, default=datetime.now()) diff --git a/src/backends/protocols/gamespy/query_report/data.py b/src/backends/protocols/gamespy/query_report/data.py index bf7239812..969d563a2 100644 --- a/src/backends/protocols/gamespy/query_report/data.py +++ b/src/backends/protocols/gamespy/query_report/data.py @@ -9,6 +9,7 @@ GameServerCaches, ) from frontends.gamespy.protocols.chat.aggregates.peer_room import PeerRoom +from frontends.gamespy.protocols.query_report.aggregates.enums import GameServerStatus from frontends.gamespy.protocols.query_report.aggregates.exceptions import QRException from frontends.gamespy.protocols.query_report.aggregates.game_server_info import ( GameServerInfo, @@ -20,7 +21,6 @@ from datetime import datetime, timedelta -from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus from frontends.gamespy.protocols.server_browser.v2.aggregations.exceptions import SBException @@ -159,29 +159,25 @@ def get_server_info_list_with_game_name( .all() ) data = [] - for s in result: - assert isinstance(s.server_id, UUID) - assert isinstance(s.host_ip_address, str) - assert isinstance(s.instant_key, str) - assert isinstance(s.game_name, str) - assert isinstance(s.query_report_port, int) - assert isinstance(s.update_time, datetime) - assert isinstance(s.status, GameServerStatus) - assert isinstance(s.server_data, dict) - assert isinstance(s.player_data, list) - assert isinstance(s.team_data, list) + for info in result: + assert isinstance(info.server_id, UUID) + assert isinstance(info.host_ip_address, str) + assert isinstance(info.instant_key, str) + assert isinstance(info.game_name, str) + assert isinstance(info.query_report_port, int) + assert isinstance(info.update_time, datetime) + assert isinstance(info.status, GameServerStatus) + assert isinstance(info.data, dict) data.append(GameServerInfo( - server_id=s.server_id, - host_ip_address=s.host_ip_address, - instant_key=s.instant_key, - game_name=s.game_name, - query_report_port=s.query_report_port, - last_heart_beat_received_time=s.update_time, - status=s.status, - server_data=s.server_data, - player_data=s.player_data, - team_data=s.team_data + server_id=info.server_id, + host_ip_address=info.host_ip_address, + instant_key=info.instant_key, + game_name=info.game_name, + query_report_port=info.query_report_port, + update_time=info.update_time, + status=info.status, + data=info.data )) return data @@ -227,15 +223,13 @@ def create_game_server_cache(info: GameServerCaches, session: Session) -> None: def update_game_server_cache( cache: GameServerCaches, - instant_key: str, + instant_key: str | None, server_id: UUID, host_ip_address: str, game_name: str, query_report_port: int, server_status: GameServerStatus, - player_data: list[dict[str, object]] | None, - server_data: dict[str, object] | None, - team_data: list[dict[str, object]] | None, + data: dict[str, str], session: Session, ) -> None: cache.instant_key = instant_key # type: ignore @@ -245,17 +239,12 @@ def update_game_server_cache( cache.query_report_port = query_report_port # type: ignore cache.update_time = datetime.now() # type: ignore cache.status = server_status # type: ignore - if player_data is not None: - cache.player_data = player_data # type: ignore - if server_data is not None: - cache.server_data = server_data # type: ignore - if team_data is not None: - cache.team_data = team_data # type: ignore + cache.data = data # type: ignore session.commit() -def refresh_game_server_cache(instant_key: str, session: Session): - cache = get_game_server_cache_with_instant_key(instant_key, session) +def refresh_game_server_cache(client_ip: str, client_port: int, session: Session): + cache = get_game_server_cache_by_ip_port(client_ip, client_port, session) if cache is None: raise QRException( "no game server cache found, please check the database") diff --git a/src/backends/protocols/gamespy/query_report/handlers.py b/src/backends/protocols/gamespy/query_report/handlers.py index c56840d7e..01ac65cbf 100644 --- a/src/backends/protocols/gamespy/query_report/handlers.py +++ b/src/backends/protocols/gamespy/query_report/handlers.py @@ -6,7 +6,9 @@ ClientMessageRequest, HeartBeatRequest, KeepAliveRequest, + LegacyHeartbeatRequest, ) +from frontends.gamespy.protocols.query_report.aggregates.enums import GameServerStatus from frontends.gamespy.protocols.query_report.aggregates.exceptions import QRException import backends.protocols.gamespy.query_report.data as data from sqlalchemy.orm import Session @@ -37,6 +39,10 @@ def _data_operate(self) -> None: class HeartbeatHandler(HandlerBase): + """ + v2 protocol qr heartbeat + this heartbeat have instantkey which is using for natneg + """ _request: HeartBeatRequest response: OKResponse @@ -50,12 +56,9 @@ def _data_operate(self) -> None: if cache is None: # todo check whether these data can be null at first heartbeat - if self._request.player_data is None: + if len(self._request.data) == 0: raise QRException( - "player data in first heartbeat can not be null") - if self._request.server_data is None: - raise QRException( - "server data in first heartbeat can not be null") + "data in first heartbeat can not be null") # team data can be none in peertest sdk cache = GameServerCaches( instant_key=self._request.instant_key, @@ -63,10 +66,8 @@ def _data_operate(self) -> None: host_ip_address=self._request.client_ip, game_name=self._request.game_name, query_report_port=self._request.client_port, - status=self._request.server_status, - player_data=self._request.player_data, - server_data=self._request.server_data, - team_data=self._request.team_data, + status=self._request.status, + data=self._request.data, avaliable=True, ) data.create_game_server_cache(cache, self._session) @@ -78,10 +79,50 @@ def _data_operate(self) -> None: host_ip_address=self._request.client_ip, game_name=self._request.game_name, query_report_port=self._request.client_port, - server_status=self._request.server_status, - player_data=self._request.player_data, - server_data=self._request.server_data, - team_data=self._request.team_data, + server_status=self._request.status, + data=self._request.data, + session=self._session, + ) + + +class LegacyHeartbeatHandler(HandlerBase): + """ + same as HeartbeatHandler + The v1 protocol heartbeat do not have instantkey + """ + _request: LegacyHeartbeatRequest + response: OKResponse + + def _data_operate(self) -> None: + # clean the expired server cache + data.clean_expired_game_server_cache(self._session) + + cache = data.get_game_server_cache_by_ip_port( + self._request.client_ip, self._request.client_port, self._session + ) + + if cache is None: + cache = GameServerCaches( + instant_key=None, + server_id=self._request.server_id, + host_ip_address=self._request.client_ip, + game_name=self._request.game_name, + query_report_port=self._request.client_port, + status=GameServerStatus.NORMAL, + data=self._request.data, + avaliable=True, + ) + data.create_game_server_cache(cache, self._session) + else: + data.update_game_server_cache( + cache=cache, + instant_key=None, + server_id=self._request.server_id, + host_ip_address=self._request.client_ip, + game_name=self._request.game_name, + query_report_port=self._request.client_port, + server_status=GameServerStatus.NORMAL, + data=self._request.data, session=self._session, ) @@ -93,7 +134,7 @@ class KeepAliveHandler(HandlerBase): def _data_operate(self) -> None: assert isinstance(self._request.instant_key, str) data.refresh_game_server_cache( - self._request.instant_key, self._session) + self._request.client_ip, self._request.client_port, self._session) class ClientMessageHandler(HandlerBase): diff --git a/src/backends/protocols/gamespy/query_report/requests.py b/src/backends/protocols/gamespy/query_report/requests.py index 51e045316..4a60b5791 100644 --- a/src/backends/protocols/gamespy/query_report/requests.py +++ b/src/backends/protocols/gamespy/query_report/requests.py @@ -2,12 +2,15 @@ from pydantic import UUID4 import backends.library.abstractions.contracts as lib -from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus, RequestType +from frontends.gamespy.protocols.query_report.aggregates.enums import GameServerStatus +from frontends.gamespy.protocols.query_report.v2.aggregates.enums import RequestType as V2RequestType + +from frontends.gamespy.protocols.query_report.v1.aggregates.enums import RequestType as V1RequestType class RequestBase(lib.RequestBase): instant_key: str - command_name: RequestType + command_name: V2RequestType raw_request: str @@ -33,14 +36,21 @@ class ClientMessageRequest(RequestBase): class HeartBeatRequest(RequestBase): - server_data: dict[str, object] | None - player_data: list[dict[str, object]] | None - team_data: list[dict[str, object]] | None - server_status: GameServerStatus + data: dict[str, str] + status: GameServerStatus group_id: int | None game_name: str + + +class LegacyHeartbeatRequest(lib.RequestBase): + command_name: V1RequestType + raw_request: str + query_id: str + game_name: str + data: dict[str, str] + class EchoRequest(RequestBase): pass diff --git a/src/backends/protocols/gamespy/server_browser/handlers.py b/src/backends/protocols/gamespy/server_browser/handlers.py index 94963eb0b..e4f3cd72e 100644 --- a/src/backends/protocols/gamespy/server_browser/handlers.py +++ b/src/backends/protocols/gamespy/server_browser/handlers.py @@ -4,7 +4,6 @@ from backends.library.abstractions.handler_base import HandlerBase from backends.protocols.gamespy.query_report.broker import BROCKER, MANAGER import backends.protocols.gamespy.query_report.data as data -from backends.protocols.gamespy.query_report.handlers import ClientMessageHandler from backends.protocols.gamespy.query_report.requests import ClientMessageRequest from backends.protocols.gamespy.server_browser.requests import ( AdHocRequestBase, @@ -78,8 +77,11 @@ def _data_operate(self): ) # we just need server data for cache in self._caches: - cache.player_data = [] - cache.team_data = [] + filtered_data = {} + for key in self._request.keys: + if key in cache.data: + filtered_data[key] = cache.data[key] + cache.data = filtered_data def _result_construct(self): assert isinstance(self._caches, list) and all( @@ -114,7 +116,7 @@ def _result_construct(self): ) if TYPE_CHECKING: self._caches = cast(list[GameServerInfo], self._caches) - all_keys = list(self._caches[0].server_data.keys()) + all_keys = list(self._caches[0].data.keys()) self._result = ServerFullInfoListResult( client_remote_ip=self._request.client_ip, game_secret_key=self._secret_key, diff --git a/src/backends/routers/gamespy/query_report.py b/src/backends/routers/gamespy/query_report.py index c5abfec0b..e864dcd55 100644 --- a/src/backends/routers/gamespy/query_report.py +++ b/src/backends/routers/gamespy/query_report.py @@ -2,9 +2,9 @@ from backends.library.abstractions.contracts import RESPONSES_DEF, OKResponse from backends.protocols.gamespy.query_report.broker import MANAGER, launch_brocker from backends.protocols.gamespy.query_report.handlers import ( - AvaliableHandler, HeartbeatHandler, KeepAliveHandler) + AvaliableHandler, HeartbeatHandler, KeepAliveHandler, LegacyHeartbeatHandler) from backends.protocols.gamespy.query_report.requests import ( - AvaliableRequest, ChallengeRequest, ClientMessageRequest, EchoRequest, HeartBeatRequest, KeepAliveRequest) + AvaliableRequest, ChallengeRequest, ClientMessageRequest, EchoRequest, HeartBeatRequest, KeepAliveRequest, LegacyHeartbeatRequest) from backends.urls import QUERY_REPORT @@ -17,41 +17,48 @@ async def websocket_endpoint(ws: WebSocket): @router.post(f"{QUERY_REPORT}/HeartbeatHandler", responses=RESPONSES_DEF) -def heartbeat(request: HeartBeatRequest)->OKResponse: +def heartbeat(request: HeartBeatRequest) -> OKResponse: handler = HeartbeatHandler(request) handler.handle() return handler.response @router.post(f"{QUERY_REPORT}/ChallengeHanler", responses=RESPONSES_DEF) -def challenge(request: ChallengeRequest)->OKResponse: +def challenge(request: ChallengeRequest) -> OKResponse: raise NotImplementedError() @router.post(f"{QUERY_REPORT}/AvailableHandler", responses=RESPONSES_DEF) -def available(request: AvaliableRequest)->OKResponse: +def available(request: AvaliableRequest) -> OKResponse: handler = AvaliableHandler(request) handler.handle() return handler.response @router.post(f"{QUERY_REPORT}/ClientMessageAckHandler", responses=RESPONSES_DEF) -def client_message(request: ClientMessageRequest)->OKResponse: +def client_message(request: ClientMessageRequest) -> OKResponse: raise NotImplementedError() @router.post(f"{QUERY_REPORT}/EchoHandler", responses=RESPONSES_DEF) -def echo(request: EchoRequest)->OKResponse: +def echo(request: EchoRequest) -> OKResponse: raise NotImplementedError() @router.post(f"{QUERY_REPORT}/KeepAliveHandler", responses=RESPONSES_DEF) -def keep_alive(request: KeepAliveRequest)->OKResponse: +def keep_alive(request: KeepAliveRequest) -> OKResponse: handler = KeepAliveHandler(request) handler.handle() return handler.response +@router.post(f"{QUERY_REPORT}/LegacyHeartbeatHandler", responses=RESPONSES_DEF) +def legacy_heartbeat(request: LegacyHeartbeatRequest) -> OKResponse: + handler = LegacyHeartbeatHandler(request) + handler.handle() + return handler.response + + if __name__ == "__main__": import uvicorn from fastapi import FastAPI diff --git a/src/frontends/gamespy/library/abstractions/client.py b/src/frontends/gamespy/library/abstractions/client.py index bb5c4df5a..581d40834 100644 --- a/src/frontends/gamespy/library/abstractions/client.py +++ b/src/frontends/gamespy/library/abstractions/client.py @@ -54,11 +54,13 @@ def on_connected(self) -> None: lock = threading.Lock() with lock: ClientBase.pool[self.connection.ip_endpoint] = self + self.log_debug("client connected") def on_disconnected(self) -> None: lock = threading.Lock() with lock: del ClientBase.pool[self.connection.ip_endpoint] + self.log_debug("client disconnected") def _create_switcher(self, buffer: bytes) -> "SwitcherBase": # type: ignore """ @@ -124,11 +126,13 @@ def log_network_receving(self, data: object) -> None: def log_network_upload(self, data: object) -> None: self.logger.info(f"{self._log_prefix} [upload]: {data}") + def log_network_fetch(self, data: object) -> None: self.logger.info(f"{self._log_prefix} [fetch]: {data}") def log_current_class(self, object: "CmdHandlerBase") -> None: - self.logger.debug(f"{self._log_prefix} [=>] <{object.__class__.__name__}>") + self.logger.debug( + f"{self._log_prefix} [=>] <{object.__class__.__name__}>") class EasyTimer: diff --git a/src/frontends/gamespy/library/abstractions/switcher.py b/src/frontends/gamespy/library/abstractions/switcher.py index e7bb38cb3..c8c0a52d0 100644 --- a/src/frontends/gamespy/library/abstractions/switcher.py +++ b/src/frontends/gamespy/library/abstractions/switcher.py @@ -3,7 +3,6 @@ from frontends.gamespy.library.abstractions.handler import CmdHandlerBase - class SwitcherBase: """ class member type hint can use class static member, but you can not initialize any class static member here! Init it in the __init__() function @@ -14,7 +13,7 @@ class member type hint can use class static member, but you can not initialize a def __init__(self, client: ClientBase, raw_request: bytes | str) -> None: assert isinstance(client, ClientBase) - assert isinstance(raw_request, bytes) or isinstance(raw_request, str) + assert isinstance(raw_request, str) or isinstance(raw_request, bytes) self._client: ClientBase = client self._raw_request: object = raw_request self._handlers: list[CmdHandlerBase] = [] diff --git a/src/frontends/gamespy/protocols/query_report/aggregates/enums.py b/src/frontends/gamespy/protocols/query_report/aggregates/enums.py new file mode 100644 index 000000000..dc8b14a3f --- /dev/null +++ b/src/frontends/gamespy/protocols/query_report/aggregates/enums.py @@ -0,0 +1,13 @@ +from enum import Enum + + +class GameServerStatus(Enum): + NORMAL = 0 + UPDATE = 1 + SHUTDOWN = 2 + PLAYING = 3 + + +class ProtocolVersion(Enum): + V1 = 1 + V2 = 2 diff --git a/src/frontends/gamespy/protocols/query_report/aggregates/game_server_info.py b/src/frontends/gamespy/protocols/query_report/aggregates/game_server_info.py index 50820c6a9..1a4b5ee7e 100644 --- a/src/frontends/gamespy/protocols/query_report/aggregates/game_server_info.py +++ b/src/frontends/gamespy/protocols/query_report/aggregates/game_server_info.py @@ -4,7 +4,9 @@ from pydantic import BaseModel from frontends.gamespy.library.extentions.bytes_extentions import ip_to_4_bytes -from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus +from frontends.gamespy.protocols.query_report.aggregates.enums import GameServerStatus + +NESSESARY_KEYS: list[str] = ["gamename", "hostname", "hostport"] class GameServerInfo(BaseModel): @@ -14,11 +16,9 @@ class GameServerInfo(BaseModel): game_name: str query_report_port: int - last_heart_beat_received_time: datetime + update_time: datetime status: GameServerStatus - server_data: dict[str, str] - player_data: list[dict[str, str]] - team_data: list[dict[str, str]] + data:dict[str,str] @property def query_report_port_bytes(self) -> bytes: diff --git a/src/frontends/gamespy/protocols/query_report/applications/client.py b/src/frontends/gamespy/protocols/query_report/applications/client.py index d83fbf0ec..3000e26ab 100644 --- a/src/frontends/gamespy/protocols/query_report/applications/client.py +++ b/src/frontends/gamespy/protocols/query_report/applications/client.py @@ -10,19 +10,23 @@ class Client(ClientBase): pool: dict[str, "Client"] is_log_raw: bool + """ + v1 protocol request is send with different packet, the \\final\\ tells whether request is finished + """ def __init__(self, connection: ConnectionBase, server_config: ServerConfig, logger: LogWriter): super().__init__(connection, server_config, logger) self.is_log_raw = True def _create_switcher(self, buffer: bytes): - from frontends.gamespy.protocols.query_report.v2.applications.switcher import Switcher as V2CmdSwitcher + from frontends.gamespy.protocols.query_report.v2.applications.switcher import Switcher as V2Switcher + from frontends.gamespy.protocols.query_report.v1.applications.switcher import Switcher as V1Switcher assert isinstance(buffer, bytes) + # !! qr v1 doesn't actually need encryption because encryption isn't part of the main communication process. if buffer[0] == ord("\\"): - raise NotImplementedError("v1 protocol not implemented") - return V1CmdSwitcher(self, (buffer)) + return V1Switcher(self, buffer.decode()) else: - return V2CmdSwitcher(self, buffer) + return V2Switcher(self, buffer) def start_brocker(self): self.brocker = WebSocketBrocker( diff --git a/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py b/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py index 3ef1dfc49..433d4ab28 100644 --- a/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/query_report/v1/abstractions/contracts.py @@ -1,3 +1,4 @@ +from enum import Enum import frontends.gamespy.library.abstractions.contracts as lib from frontends.gamespy.library.extentions.gamespy_utils import convert_to_key_value @@ -14,6 +15,8 @@ def __init__(self, raw_request: str) -> None: def parse(self) -> None: self._request_dict = convert_to_key_value(self.raw_request) + if 'final' in self._request_dict: + del self._request_dict['final'] self.command_name = list(self._request_dict.keys())[0] diff --git a/src/frontends/gamespy/protocols/query_report/v1/aggregates/enums.py b/src/frontends/gamespy/protocols/query_report/v1/aggregates/enums.py new file mode 100644 index 000000000..550cff4c5 --- /dev/null +++ b/src/frontends/gamespy/protocols/query_report/v1/aggregates/enums.py @@ -0,0 +1,12 @@ +from enum import Enum + + +class RequestType(Enum): + HEARTBEAT = "heartbeat" + HEARTBEAT_ACK = "gamename" + + +class ServerStatus(Enum): + START = 0 + CHANGED = 1 + SHUTDOWN = 2 diff --git a/src/frontends/gamespy/protocols/query_report/v1/applications/handlers.py b/src/frontends/gamespy/protocols/query_report/v1/applications/handlers.py new file mode 100644 index 000000000..a37aeb9ae --- /dev/null +++ b/src/frontends/gamespy/protocols/query_report/v1/applications/handlers.py @@ -0,0 +1,31 @@ +from frontends.gamespy.protocols.query_report.applications.client import Client +from frontends.gamespy.protocols.query_report.v1.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.query_report.v1.abstractions.handlers import CmdHandlerBase +from frontends.gamespy.protocols.query_report.v1.contracts.requests import LegacyHeartbeatRequest, HeartbeatPreRequest +from frontends.gamespy.protocols.query_report.v1.contracts.responses import HeartbeatPreResponse +from frontends.gamespy.protocols.query_report.v1.contracts.results import HeartbeatPreResult + + +class HeartbeatPreHandler(CmdHandlerBase): + _request: HeartbeatPreRequest + _result: HeartbeatPreResult + _response: HeartbeatPreResponse + + def __init__(self, client: Client, request: RequestBase) -> None: + super().__init__(client, request) + self._is_fetching = False + self._is_uploading = False + + def _response_construct(self) -> None: + self._result = HeartbeatPreResult( + status=self._request.status, + game_name=self._request.game_name) + self._response = HeartbeatPreResponse(self._result) + + +class LegacyHeartbeatHandler(CmdHandlerBase): + _request: LegacyHeartbeatRequest + + def __init__(self, client: Client, request: RequestBase) -> None: + super().__init__(client, request) + self._is_fetching = False diff --git a/src/frontends/gamespy/protocols/query_report/v1/applications/switcher.py b/src/frontends/gamespy/protocols/query_report/v1/applications/switcher.py new file mode 100644 index 000000000..29910cfd3 --- /dev/null +++ b/src/frontends/gamespy/protocols/query_report/v1/applications/switcher.py @@ -0,0 +1,31 @@ + +from frontends.gamespy.library.abstractions.handler import CmdHandlerBase +from frontends.gamespy.library.abstractions.switcher import SwitcherBase +from frontends.gamespy.protocols.query_report.aggregates.exceptions import QRException +from frontends.gamespy.protocols.query_report.applications.client import Client +from frontends.gamespy.protocols.query_report.v1.aggregates.enums import RequestType +from frontends.gamespy.protocols.query_report.v1.applications.handlers import HeartbeatPreHandler, LegacyHeartbeatHandler +from frontends.gamespy.protocols.query_report.v1.contracts.requests import HeartbeatPreRequest, LegacyHeartbeatRequest + + +class Switcher(SwitcherBase): + _raw_request: str + _client: Client + + def _process_raw_request(self) -> None: + if len(self._raw_request) < 4: + raise QRException("Invalid request length") + if self._raw_request[0] != "\\": + raise QRException("Invalid queryreport v1 request") + name = self._raw_request.strip("\\").split("\\")[0] + if name not in RequestType: + self._client.log_debug( + f"Request: {name} is not a valid request.") + self._requests.append((RequestType(name), self._raw_request)) + + def _create_cmd_handlers(self, name: RequestType, raw_request: str) -> CmdHandlerBase | None: + match(name): + case RequestType.HEARTBEAT: + return HeartbeatPreHandler(self._client, HeartbeatPreRequest(raw_request)) + case RequestType.HEARTBEAT_ACK: + return LegacyHeartbeatHandler(self._client, LegacyHeartbeatRequest(raw_request)) diff --git a/src/frontends/gamespy/protocols/query_report/v1/contracts/requests.py b/src/frontends/gamespy/protocols/query_report/v1/contracts/requests.py new file mode 100644 index 000000000..b065e2b8b --- /dev/null +++ b/src/frontends/gamespy/protocols/query_report/v1/contracts/requests.py @@ -0,0 +1,66 @@ +from frontends.gamespy.protocols.query_report.aggregates.exceptions import QRException +from frontends.gamespy.protocols.query_report.aggregates.game_server_info import NESSESARY_KEYS +from frontends.gamespy.protocols.query_report.v1.abstractions.contracts import RequestBase +from frontends.gamespy.protocols.query_report.v1.aggregates.enums import ServerStatus + + +class HeartbeatPreRequest(RequestBase): + """ + heartbeat do not have final command + \\heartbeat\\26900\\gamename\\gmtest + """ + game_port: int + game_name: str + status: ServerStatus + + def parse(self) -> None: + super().parse() + if 'gamename' not in self._request_dict: + raise QRException("gamename is missing") + self.game_name = self._request_dict['gamename'] + if 'heartbeat' not in self._request_dict: + raise QRException("game port is missing") + self.game_port = int(self._request_dict['heartbeat']) + + if 'statechanged' in self._request_dict: + self.status = ServerStatus(int(self._request_dict['statechanged'])) + else: + self.status = ServerStatus.START + + +class LegacyHeartbeatRequest(RequestBase): + """ + heartbeat ack request is start with gamename: + \\gamename\\gmtest\\gamever\\2.00\\location\\1\\hostname\\GameMaster Arena Server\\hostport\\25000\\mapname\\gmtmap1\\gametype\\arena\\numplayers\\4\\maxplayers\\32\\gamemode\\openplaying\\timelimit\\40\\fraglimit\\0\\teamplay\\1\\rankedserver\\1\\player_0\\Joe Player\\frags_0\\25\\deaths_0\\6\\skill_0\\272\\ping_0\\119\\team_0\\\\player_1\\L33t 0n3\\frags_1\\12\\deaths_1\\24\\skill_1\\136\\ping_1\\98\\team_1\\\\player_2\\Raptor\\frags_2\\11\\deaths_2\\26\\skill_2\\44\\ping_2\\445\\team_2\\Blue\\player_3\\Gr81\\frags_3\\23\\deaths_3\\18\\skill_3\\173\\ping_3\\129\\team_3\\Red\\final\\\\queryid\\2.1 + """ + query_id: str + data: dict[str, str] + game_name: str + group_id: int | None + + def parse(self) -> None: + super().parse() + if "queryid" not in self._request_dict: + raise QRException("queryid is missing") + self.query_id = self._request_dict['queryid'] + for key in NESSESARY_KEYS: + if key not in self._request_dict: + raise QRException(f"key:<{key}> is missing") + # currently we put basic, info, rules, players, teams into data + self.data = self._request_dict.copy() + self.game_name = self.data['gamename'] + if "groupid" in self.data: + self.group_id = int(self.data["groupid"]) + else: + self.group_id = None + +if __name__ == "__main__": + heartbeat = '\\heartbeat\\26900\\gamename\\gmtest' + basic = '\\gamename\\gmtest\\gamever\\2.00\\location\\1\\queryid\\2.1' + info = '\\hostname\\GameMaster Arena Server\\hostport\\25000\\mapname\\gmtmap1\\gametype\\arena\\numplayers\\5\\maxplayers\\32\\gamemode\\openplaying\\queryid\\2.2' + rules = '\\timelimit\\40\\fraglimit\\0\\teamplay\\1\\rankedserver\\1\\queryid\\2.3' + players = '\\player_0\\Joe Player\\frags_0\\3\\deaths_0\\6\\skill_0\\225\\ping_0\\137\\team_0\\Blue\\player_1\\L33t 0n3\\frags_1\\26\\deaths_1\\18\\skill_1\\829\\ping_1\\247\\team_1\\\\player_2\\Raptor\\frags_2\\30\\deaths_2\\17\\skill_2\\888\\ping_2\\233\\team_2\\Red\\player_3\\Gr81\\frags_3\\23\\deaths_3\\19\\skill_3\\415\\ping_3\\409\\team_3\\\\player_4\\Flubber\\frags_4\\23\\deaths_4\\31\\skill_4\\837\\ping_4\\443\\team_4\\\\final\\\\queryid\\2.4' + + status = "\\gamename\\gmtest\\gamever\\2.00\\location\\1\\hostname\\GameMaster Arena Server\\hostport\\25000\\mapname\\gmtmap1\\gametype\\arena\\numplayers\\4\\maxplayers\\32\\gamemode\\openplaying\\timelimit\\40\\fraglimit\\0\\teamplay\\1\\rankedserver\\1\\player_0\\Joe Player\\frags_0\\25\\deaths_0\\6\\skill_0\\272\\ping_0\\119\\team_0\\\\player_1\\L33t 0n3\\frags_1\\12\\deaths_1\\24\\skill_1\\136\\ping_1\\98\\team_1\\\\player_2\\Raptor\\frags_2\\11\\deaths_2\\26\\skill_2\\44\\ping_2\\445\\team_2\\Blue\\player_3\\Gr81\\frags_3\\23\\deaths_3\\18\\skill_3\\173\\ping_3\\129\\team_3\\Red\\final\\\\queryid\\2.1" + req = LegacyHeartbeatRequest(status) + req.parse() diff --git a/src/frontends/gamespy/protocols/query_report/v1/contracts/responses.py b/src/frontends/gamespy/protocols/query_report/v1/contracts/responses.py new file mode 100644 index 000000000..728160bcb --- /dev/null +++ b/src/frontends/gamespy/protocols/query_report/v1/contracts/responses.py @@ -0,0 +1,10 @@ +from frontends.gamespy.protocols.query_report.v1.abstractions.contracts import ResponseBase +from frontends.gamespy.protocols.query_report.v1.aggregates.enums import ServerStatus +from frontends.gamespy.protocols.query_report.v1.contracts.results import HeartbeatPreResult + + +class HeartbeatPreResponse(ResponseBase): + _result: HeartbeatPreResult + + def build(self) -> None: + self.sending_buffer = "\\status\\" diff --git a/src/frontends/gamespy/protocols/query_report/v1/contracts/results.py b/src/frontends/gamespy/protocols/query_report/v1/contracts/results.py new file mode 100644 index 000000000..28915ed86 --- /dev/null +++ b/src/frontends/gamespy/protocols/query_report/v1/contracts/results.py @@ -0,0 +1,7 @@ +from frontends.gamespy.protocols.query_report.v1.abstractions.contracts import ResultBase +from frontends.gamespy.protocols.query_report.v1.aggregates.enums import ServerStatus + + +class HeartbeatPreResult(ResultBase): + status: ServerStatus + game_name: str diff --git a/src/frontends/gamespy/protocols/query_report/v2/abstractions/cmd_handler_base.py b/src/frontends/gamespy/protocols/query_report/v2/abstractions/handlers.py similarity index 100% rename from src/frontends/gamespy/protocols/query_report/v2/abstractions/cmd_handler_base.py rename to src/frontends/gamespy/protocols/query_report/v2/abstractions/handlers.py diff --git a/src/frontends/gamespy/protocols/query_report/v2/aggregates/enums.py b/src/frontends/gamespy/protocols/query_report/v2/aggregates/enums.py index 6721506d3..6cb023ac5 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/aggregates/enums.py +++ b/src/frontends/gamespy/protocols/query_report/v2/aggregates/enums.py @@ -49,11 +49,6 @@ class HeartBeatReportType(Enum): SERVER_DATA = 3 -class GameServerStatus(Enum): - NORMAL = 0 - UPDATE = 1 - SHUTDOWN = 2 - PLAYING = 3 class ServerAvailability(Enum): diff --git a/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py b/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py index 1cf7c9248..7655f5942 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py +++ b/src/frontends/gamespy/protocols/query_report/v2/applications/handlers.py @@ -1,6 +1,6 @@ from typing import final from frontends.gamespy.protocols.query_report.applications.client import Client -from frontends.gamespy.protocols.query_report.v2.abstractions.cmd_handler_base import ( +from frontends.gamespy.protocols.query_report.v2.abstractions.handlers import ( CmdHandlerBase, ) from frontends.gamespy.protocols.query_report.v2.contracts.requests import ( diff --git a/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py b/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py index 2dba3d64e..9e0bd5235 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py +++ b/src/frontends/gamespy/protocols/query_report/v2/applications/switcher.py @@ -2,7 +2,7 @@ from frontends.gamespy.library.abstractions.switcher import SwitcherBase from frontends.gamespy.protocols.query_report.aggregates.exceptions import QRException from frontends.gamespy.protocols.query_report.applications.client import Client -from frontends.gamespy.protocols.query_report.v2.abstractions.cmd_handler_base import CmdHandlerBase +from frontends.gamespy.protocols.query_report.v2.abstractions.handlers import CmdHandlerBase from frontends.gamespy.protocols.query_report.v2.contracts.requests import ( AvaliableRequest, diff --git a/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py b/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py index 0fc5f09a3..bbda10bb5 100644 --- a/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py +++ b/src/frontends/gamespy/protocols/query_report/v2/contracts/requests.py @@ -1,9 +1,9 @@ from uuid import UUID from frontends.gamespy.library.extentions.encoding import get_string +from frontends.gamespy.protocols.query_report.aggregates.enums import GameServerStatus from frontends.gamespy.protocols.query_report.aggregates.exceptions import QRException from frontends.gamespy.protocols.query_report.v2.abstractions.contracts import RequestBase -from frontends.gamespy.protocols.query_report.v2.aggregates.enums import GameServerStatus PREFIX = bytes([0x09, 0x00, 0x00, 0x00, 0x00]) @@ -48,23 +48,14 @@ def target_ip_endpoint(self) -> str: def __init__(self, raw_request: bytes | None = None) -> None: if raw_request is not None: super().__init__(raw_request) - class HeartbeatRequest(RequestBase): - server_data: dict[str, str] | None - player_data: list[dict[str, str]] | None - team_data: list[dict[str, str]] | None - server_status: GameServerStatus + data: dict[str, str] + status: GameServerStatus group_id: int | None game_name: str - def __init__(self, raw_request: bytes) -> None: - super().__init__(raw_request) - self.server_data = None - self.player_data = None - self.team_data = None - def parse(self): super().parse() player_pos, team_pos = 0, 0 @@ -119,7 +110,7 @@ def parse(self): raise QRException("HeartBeat request is invalid.") def parse_server_data(self, server_data_str: str): - self.server_data = {} + self.data = {} key_value_array = server_data_str.split("\0") for i in range(0, len(key_value_array), 2): @@ -129,27 +120,23 @@ def parse_server_data(self, server_data_str: str): temp_key = key_value_array[i] temp_value = key_value_array[i + 1] - if temp_key in self.server_data: - self.server_data[temp_key] = temp_value + if temp_key in self.data: + self.data[temp_key] = temp_value else: - self.server_data[temp_key] = temp_value + self.data[temp_key] = temp_value - if "statechanged" not in self.server_data: - self.server_status = GameServerStatus.NORMAL + if "statechanged" not in self.data: + self.status = GameServerStatus.NORMAL else: - self.server_status = GameServerStatus( - int(self.server_data["statechanged"])) - - if "groupid" in self.server_data: - group_id = 0 - if not int(self.server_data["groupid"], group_id): - raise QRException("GroupId is invalid.") - self.group_id = group_id + self.status = GameServerStatus( + int(self.data["statechanged"])) + + if "groupid" in self.data: + self.group_id = int(self.data["groupid"]) else: self.group_id = None def parse_player_data(self, player_data_str: str): - self.player_data = [] player_count = int.from_bytes(player_data_str[0].encode()) player_data_str = player_data_str[1:] @@ -160,8 +147,8 @@ def parse_player_data(self, player_data_str: str): values_str = player_data_str[index_of_key + 2:] values = values_str.split("\0") + key_value = {} for player_index in range(player_count): - key_value = {} for key_index in range(len(keys)): temp_key = keys[key_index] + str(player_index) @@ -172,10 +159,10 @@ def parse_player_data(self, player_data_str: str): else: key_value[temp_key] = temp_value - self.player_data.append(key_value) + for key, value in key_value.items(): + self.data[key] = value def parse_team_data(self, team_data_str: str): - self.team_data = [] team_count = int.from_bytes(team_data_str[0].encode()) team_data_str = team_data_str[1:] @@ -186,8 +173,8 @@ def parse_team_data(self, team_data_str: str): value_str = team_data_str[end_key_index + 2:] values = value_str.split("\0") + key_value = {} for team_index in range(team_count): - key_value = {} for key_index in range(len(keys)): temp_key = keys[key_index] + str(team_index) @@ -198,7 +185,8 @@ def parse_team_data(self, team_data_str: str): else: key_value[temp_key] = temp_value - self.team_data.append(key_value) + for key, value in key_value.items(): + self.data[key] = value class EchoRequest(RequestBase): diff --git a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/server_info_builder.py b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/server_info_builder.py index b64f79be7..e02746546 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/aggregations/server_info_builder.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/aggregations/server_info_builder.py @@ -34,16 +34,16 @@ def build_server_info_header( def check_nat_neg_flag(header: bytearray, server_info: GameServerInfo): - if "natneg" in server_info.server_data: - nat_neg_flag = int(server_info.server_data["natneg"]) + if "natneg" in server_info.data: + nat_neg_flag = int(server_info.data["natneg"]) unsolicited_udp = header[0] & GameServerFlags.UNSOLICITED_UDP_FLAG.value if nat_neg_flag == 1 and unsolicited_udp == 0: header[0] ^= GameServerFlags.CONNECT_NEGOTIATE_FLAG.value def check_unsolicited_udp(header: bytearray, server_info: GameServerInfo): - if "allow_unsolicited_udp" in server_info.server_data: - unsolicited_udp = int(server_info.server_data["unsolicitedudp"]) + if "allow_unsolicited_udp" in server_info.data: + unsolicited_udp = int(server_info.data["unsolicitedudp"]) if unsolicited_udp == 1: header[0] ^= GameServerFlags.UNSOLICITED_UDP_FLAG.value @@ -53,9 +53,9 @@ def check_private_ip(header: bytearray, server_info: GameServerInfo): #!known game: Worm3d # todo # if server_info.game_name in PEER_GROUP_LIST: - if "localip0" in server_info.server_data: + if "localip0" in server_info.data: header[0] ^= GameServerFlags.PRIVATE_IP_FLAG.value - bytes_address = inet_aton(server_info.server_data["localip0"]) + bytes_address = inet_aton(server_info.data["localip0"]) header.extend(bytes_address) @@ -70,8 +70,8 @@ def check_non_standard_port(header: bytearray, server_info: GameServerInfo): def check_non_standard_private_port(header: bytearray, server_info: GameServerInfo): - if "localport" in server_info.server_data: - local_port = server_info.server_data["localport"] + if "localport" in server_info.data: + local_port = server_info.data["localport"] if local_port != "" and local_port != QUERY_REPORT_DEFAULT_PORT: header[0] ^= GameServerFlags.NON_STANDARD_PRIVATE_PORT_FLAG.value port = int(local_port).to_bytes(2, byteorder="big") @@ -79,7 +79,7 @@ def check_non_standard_private_port(header: bytearray, server_info: GameServerIn def check_icmp_support(header: bytearray, server_info: GameServerInfo): - if "icmp_address" in server_info.server_data: + if "icmp_address" in server_info.data: header[0] ^= GameServerFlags.ICMP_IP_FLAG.value - bytes_address = inet_aton(server_info.server_data["icmp_address"]) + bytes_address = inet_aton(server_info.data["icmp_address"]) header.extend(bytes_address) diff --git a/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py index e9695c839..a2574f6f3 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/applications/handlers.py @@ -1,15 +1,10 @@ from concurrent.futures import ProcessPoolExecutor from typing import TYPE_CHECKING, cast +from frontends.gamespy.protocols.query_report.aggregates.enums import GameServerStatus from frontends.gamespy.protocols.query_report.aggregates.game_server_info import ( GameServerInfo, ) -from frontends.gamespy.protocols.query_report.v2.contracts.requests import ( - ClientMessageRequest, -) -from frontends.gamespy.protocols.query_report.v2.aggregates.enums import ( - GameServerStatus, - RequestType, -) + from frontends.gamespy.protocols.server_browser.v2.abstractions.contracts import ( RequestBase, ) diff --git a/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py b/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py index a9109c260..8bf144e52 100644 --- a/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py +++ b/src/frontends/gamespy/protocols/server_browser/v2/contracts/responses.py @@ -54,18 +54,10 @@ def __build_single_server_full_info(self): GameServerFlags.HAS_FULL_RULES_FLAG, self._result.game_server_info ) self._buffer.extend(header) - if self._result.game_server_info.server_data is not None: - server_data = UpdateServerInfoResponse._build_kv( - self._result.game_server_info.server_data) - self._buffer.extend(server_data) - if self._result.game_server_info.player_data is not None: - for pd in self._result.game_server_info.player_data: - player_data = UpdateServerInfoResponse._build_kv(pd) - self._buffer.extend(player_data) - if self._result.game_server_info.team_data is not None: - for td in self._result.game_server_info.team_data: - team_data = UpdateServerInfoResponse._build_kv(td) - self._buffer.extend(team_data) + if self._result.game_server_info.data is not None: + data_str = UpdateServerInfoResponse._build_kv( + self._result.game_server_info.data) + self._buffer.extend(data_str) @staticmethod def _build_kv(data: dict) -> bytearray: @@ -123,7 +115,7 @@ def __add_key_value_to_buffer(self, value): def __check_key_existance(self): for info in self._result.servers_info: for key in self._result.keys: - if key not in info.server_data: + if key not in info.data: raise SBException( f"key:{key} is not in server info, please check database") @@ -132,7 +124,7 @@ def __build_servers_full_info(self): last_header = build_server_info_header(self._result.flag, info) self._buffer.extend(last_header) for key in self._result.keys: - value = info.server_data[key] + value = info.data[key] self.__add_key_value_to_buffer(value) def __build_tail(self): diff --git a/src/frontends/tests/gamespy/query_report/request_tests.py b/src/frontends/tests/gamespy/query_report/request_tests.py index e6e519a48..bc64519c0 100644 --- a/src/frontends/tests/gamespy/query_report/request_tests.py +++ b/src/frontends/tests/gamespy/query_report/request_tests.py @@ -54,9 +54,5 @@ def test_heartbeat(self): request.parse() self.assertEqual("gmtest", request.game_name) self.assertEqual("2921297764", request.instant_key) - assert request.player_data is not None - assert request.team_data is not None - assert request.server_data is not None - self.assertEqual(6, len(request.player_data)) - self.assertEqual(19, len(request.server_data)) - self.assertEqual(2, len(request.team_data)) + assert request.data is not None + self.assertEqual(61, len(request.data)) diff --git a/src/frontends/tests/gamespy/server_browser/contract_tests.py b/src/frontends/tests/gamespy/server_browser/contract_tests.py index 005a93b40..0f4de0e92 100644 --- a/src/frontends/tests/gamespy/server_browser/contract_tests.py +++ b/src/frontends/tests/gamespy/server_browser/contract_tests.py @@ -2,12 +2,11 @@ import unittest from uuid import UUID +from frontends.gamespy.protocols.query_report.aggregates.enums import GameServerStatus from frontends.gamespy.protocols.query_report.aggregates.game_server_info import ( GameServerInfo, ) -from frontends.gamespy.protocols.query_report.v2.aggregates.enums import ( - GameServerStatus, -) + from frontends.gamespy.protocols.server_browser.v2.aggregations.enums import ( GameServerFlags, ServerListUpdateOption, @@ -35,12 +34,10 @@ def test_main_list(self): instant_key="12356", game_name="gmtest", query_report_port=6900, - last_heart_beat_received_time=datetime.now(), + update_time=datetime.now(), status=GameServerStatus.NORMAL, - server_data={"hostname": "GameSpy QR2 Sample", "gametype": "", - "mapname": "", "numplayers": "", "maxplayers": ""}, - player_data=[{}], - team_data=[{}], + data={"hostname": "GameSpy QR2 Sample", "gametype": "", + "mapname": "", "numplayers": "", "maxplayers": ""}, host_ip_address="127.0.0.1", ) ], From ff5d836b9d8fd04d0aa929832463bc67a5ef851a Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Tue, 25 Nov 2025 08:12:49 +0000 Subject: [PATCH 228/231] Fix: unittest errors --- common/UniSpy_pg.sql | 2 +- .../presence_connection_manager/data.py | 62 +++++++++---------- .../presence_connection_manager/handlers.py | 11 ++-- .../gamespy/presence_search_player/data.py | 1 + .../presence_search_player/handlers.py | 5 +- .../presence_search_player/responses.py | 7 +-- .../gamespy/web_services/handlers.py | 3 +- .../routers/gamespy/presence_search_player.py | 3 +- .../tests/gamespy/natneg/handler_tests.py | 20 +----- .../data_fetch_tests.py | 2 +- .../handler_tests.py | 25 +++++++- .../gamespy/query_report/handler_tests.py | 3 +- .../gamespy/server_browser/handler_tests.py | 2 + .../tests/gamespy/web/handler_tests.py | 14 ++++- src/backends/tests/utils.py | 3 + .../gamespy/library/abstractions/handler.py | 2 +- .../game_status/applications/handlers.py | 2 - .../game_status/contracts/requests.py | 10 --- .../tests/gamespy/game_status/game_tests.py | 2 +- .../gamespy/game_status/handler_tests.py | 8 ++- .../tests/gamespy/game_status/mock_objects.py | 2 +- .../request_tests.py | 14 ++--- .../presence_search_player/handler_tests.py | 8 +-- .../gamespy/server_browser/mock_objects.py | 12 +--- 24 files changed, 114 insertions(+), 109 deletions(-) diff --git a/common/UniSpy_pg.sql b/common/UniSpy_pg.sql index bd16c6532..a91a1ab86 100644 --- a/common/UniSpy_pg.sql +++ b/common/UniSpy_pg.sql @@ -5388,7 +5388,7 @@ COPY unispy.sakestorage (id, tableid, data) FROM stdin; -- COPY unispy.subprofiles (subprofileid, profileid, uniquenick, namespaceid, partnerid, productid, gamename, cdkeyenc, firewall, port, authtoken, session_key) FROM stdin; -1 1 spyguy_test 0 1 1 gmtests 00000a308fd86a7eb92cbc8322b03a36 0 8080 example_auth 1111 +1 1 spyguy 0 1 1 gmtests 00000a308fd86a7eb92cbc8322b03a36 0 8080 example_auth 1111 \. diff --git a/src/backends/protocols/gamespy/presence_connection_manager/data.py b/src/backends/protocols/gamespy/presence_connection_manager/data.py index d9211f8bf..c649d72f7 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/data.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/data.py @@ -214,6 +214,18 @@ def get_user_info( return result +_login_infos = [Users.userid, + Profiles.profileid, + SubProfiles.subprofileid, + Profiles.nick, + Users.email, + SubProfiles.uniquenick, + Users.password, + Users.emailverified, + Users.banned, + SubProfiles.namespaceid] + + def get_user_infos_by_uniquenick_namespace_id( unique_nick: str, namespace_id: int, session: Session ) -> LoginData | None: @@ -231,16 +243,7 @@ def get_user_infos_by_uniquenick_namespace_id( result = ( session.query( - Users.userid, - Profiles.profileid, - SubProfiles.subprofileid, - Profiles.nick, - Users.email, - SubProfiles.uniquenick, - Users.password, - Users.emailverified, - Users.banned, - SubProfiles.namespaceid, + *_login_infos ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) @@ -250,7 +253,22 @@ def get_user_infos_by_uniquenick_namespace_id( ) .first() ) - + data = { + "user_id": result[0], + "profile_id": result[1], + "sub_profile_id": result[2], + "nick": result[3], + "email": result[4], + "unique_nick": result[5], + "password_hash": result[6], + "email_verified_flag": result[7], + "banned_flag": result[8], + "namespace_id": result[9], + } + if result is not None: + return LoginData(**data) # type: ignore + else: + return None return result @@ -271,16 +289,7 @@ def get_user_infos_by_nick_email( result = ( session.query( - Users.userid, - Profiles.profileid, - SubProfiles.subprofileid, - Profiles.nick, - Users.email, - SubProfiles.uniquenick, - Users.password, - Users.emailverified, - Users.banned, - SubProfiles.namespaceid, + *_login_infos ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) @@ -328,16 +337,7 @@ def get_user_infos_by_authtoken(auth_token: str, session: Session) -> LoginData result = ( session.query( - Users.userid, - Profiles.profileid, - SubProfiles.subprofileid, - Profiles.nick, - Users.email, - SubProfiles.uniquenick, - Users.password, - Users.emailverified, - Users.banned, - SubProfiles.namespaceid, + *_login_infos ) .join(Users, Profiles.userid == Users.userid) .join(SubProfiles, Profiles.profileid == SubProfiles.profileid) diff --git a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py index 8057fced9..d3c28519c 100644 --- a/src/backends/protocols/gamespy/presence_connection_manager/handlers.py +++ b/src/backends/protocols/gamespy/presence_connection_manager/handlers.py @@ -88,14 +88,13 @@ def _auth_token_login(self) -> None: def _result_construct(self) -> None: if self._data is None: - raise GPLoginException("User is not exist.") - self._result = LoginResult(data=self._data, - operation_id=self._request.operation_id, + raise GPLoginException("User do not exist.") + self._result = LoginResult(operation_id=self._request.operation_id, + data=self._data, + user_data=self._request.user_data, type=self._request.type, partner_id=self._request.partner_id, - user_challenge=self._request.user_challenge, - user_data=self._request.user_data) - + user_challenge=self._request.user_challenge) class LogoutHandler(HandlerBase): diff --git a/src/backends/protocols/gamespy/presence_search_player/data.py b/src/backends/protocols/gamespy/presence_search_player/data.py index 065ab7ad0..d47f46c94 100644 --- a/src/backends/protocols/gamespy/presence_search_player/data.py +++ b/src/backends/protocols/gamespy/presence_search_player/data.py @@ -316,6 +316,7 @@ def get_matched_info_by_uniquenick_and_namespaceid( ) -> list[dict]: result = ( session.query( + Users.email, Profiles.profileid, Profiles.nick, SubProfiles.uniquenick, diff --git a/src/backends/protocols/gamespy/presence_search_player/handlers.py b/src/backends/protocols/gamespy/presence_search_player/handlers.py index 14482ae52..ed0a5a3c2 100644 --- a/src/backends/protocols/gamespy/presence_search_player/handlers.py +++ b/src/backends/protocols/gamespy/presence_search_player/handlers.py @@ -1,6 +1,5 @@ from backends.library.abstractions.handler_base import HandlerBase from backends.library.database.pg_orm import Users, Profiles, SubProfiles -from backends.protocols.gamespy.chat.response import NicksResponse import backends.protocols.gamespy.presence_search_player.data as data from backends.protocols.gamespy.presence_search_player.requests import ( CheckRequest, @@ -13,7 +12,7 @@ UniqueSearchRequest, ValidRequest, ) -from backends.protocols.gamespy.presence_search_player.responses import CheckResponse, NewUserResponse, OthersListResponse, OthersResponse, SearchResponse, SearchUniqueResponse, UniqueSearchResponse, ValidResponse +from backends.protocols.gamespy.presence_search_player.responses import CheckResponse, NewUserResponse, NicksResponse, OthersListResponse, OthersResponse, SearchResponse, SearchUniqueResponse, UniqueSearchResponse, ValidResponse from frontends.gamespy.protocols.presence_search_player.aggregates.enums import ( SearchType, ) @@ -156,7 +155,7 @@ def _data_operate(self) -> None: self._session, ) self.result_data = [] - for nick, unique in self.temp_list: + for nick, unique in self.temp_list: self.result_data.append( NickResultData(nick=nick, uniquenick=unique)) diff --git a/src/backends/protocols/gamespy/presence_search_player/responses.py b/src/backends/protocols/gamespy/presence_search_player/responses.py index 75c432ca0..25b58e8f5 100644 --- a/src/backends/protocols/gamespy/presence_search_player/responses.py +++ b/src/backends/protocols/gamespy/presence_search_player/responses.py @@ -1,6 +1,5 @@ from backends.library.abstractions.contracts import DataResponse -from frontends.gamespy.protocols.chat.contracts.results import NickResult -from frontends.gamespy.protocols.presence_search_player.contracts.results import CheckResult, NewUserResult, OthersListResult, OthersResult, SearchResult, SearchUniqueResult, UniqueSearchResult, ValidResult +from frontends.gamespy.protocols.presence_search_player.contracts.results import CheckResult, NewUserResult, NicksResult, OthersListResult, OthersResult, SearchResult, SearchUniqueResult, UniqueSearchResult, ValidResult class CheckResponse(DataResponse): @@ -11,8 +10,8 @@ class NewUserResponse(DataResponse): result: NewUserResult -class NickResponse(DataResponse): - result: NickResult +class NicksResponse(DataResponse): + result: NicksResult class OthersResponse(DataResponse): diff --git a/src/backends/protocols/gamespy/web_services/handlers.py b/src/backends/protocols/gamespy/web_services/handlers.py index 2f4e452dd..7e6125ce4 100644 --- a/src/backends/protocols/gamespy/web_services/handlers.py +++ b/src/backends/protocols/gamespy/web_services/handlers.py @@ -18,6 +18,7 @@ from backends.protocols.gamespy.web_services.responses import CreateRecordResponse, GetMyRecordsResponse, LoginProfileResponse, LoginRemoteAuthRepsonse, LoginUniqueNickResponse, SearchForRecordsResponse from frontends.gamespy.protocols.web_services.modules.auth.contracts.results import ( LoginProfileResult, + LoginRemoteAuthResult, ) from frontends.gamespy.protocols.web_services.modules.direct2game.contracts.results import ( GetPurchaseHistoryResult, @@ -68,7 +69,7 @@ def _data_operate(self) -> None: ) def _result_construct(self) -> None: - self._result = LoginProfileResult( + self._result = LoginRemoteAuthResult( user_id=self.data[0], profile_id=self.data[1], profile_nick=self.data[2], diff --git a/src/backends/routers/gamespy/presence_search_player.py b/src/backends/routers/gamespy/presence_search_player.py index 896ae45e9..8d3ec71cd 100644 --- a/src/backends/routers/gamespy/presence_search_player.py +++ b/src/backends/routers/gamespy/presence_search_player.py @@ -2,12 +2,11 @@ from backends.library.abstractions.contracts import RESPONSES_DEF -from backends.protocols.gamespy.chat.response import NicksResponse from backends.protocols.gamespy.presence_search_player.handlers import CheckHandler, NewUserHandler, NicksHandler, OthersHandler, OthersListHandler, SearchHandler, SearchUniqueHandler, UniqueSearchHandler, ValidHandler from backends.protocols.gamespy.presence_search_player.requests import CheckRequest, NewUserRequest, NicksRequest, OthersListRequest, OthersRequest, SearchRequest, SearchUniqueRequest, UniqueSearchRequest, ValidRequest -from backends.protocols.gamespy.presence_search_player.responses import CheckResponse, NewUserResponse, OthersListResponse, OthersResponse, SearchResponse, SearchUniqueResponse, UniqueSearchResponse, ValidResponse +from backends.protocols.gamespy.presence_search_player.responses import CheckResponse, NewUserResponse, NicksResponse, OthersListResponse, OthersResponse, SearchResponse, SearchUniqueResponse, UniqueSearchResponse, ValidResponse from backends.urls import PRESENCE_SEARCH_PLAYER diff --git a/src/backends/tests/gamespy/natneg/handler_tests.py b/src/backends/tests/gamespy/natneg/handler_tests.py index bf7f3bb1d..f48eccf01 100644 --- a/src/backends/tests/gamespy/natneg/handler_tests.py +++ b/src/backends/tests/gamespy/natneg/handler_tests.py @@ -17,34 +17,18 @@ def test_report(self): pass def test_connect(self): - raw = bytes( - [ - 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, - 0x00, - 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - ] - ) # fmt:skip + raw = b'\xfd\xfc\x1efj\xb2\x03\x00\x00\x00\x03\t\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' r = fnt.ConnectRequest(raw) data = add_headers(r) - data["raw_request"] = data["raw_request"].decode( - "ascii", "backslashreplace") request = ConnectRequest(**data) handler = ConnectHandler(request) handler.handle() pass def test_init(self): - raw = bytes( - [ - 0xfd, 0xfc, 0x1e, 0x66, 0x6a, 0xb2, 0x03, - 0x00, - 0x00, 0x00, 0x03, 0x09, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 - ] - ) # fmt:skip + raw = b'\xfd\xfc\x1efj\xb2\x03\x00\x00\x00\x03\t\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' r = fnt.InitRequest(raw) data = add_headers(r) - data["raw_request"] = data["raw_request"].decode( - "ascii", "backslashreplace") request = InitRequest(**data) handler = InitHandler(request) handler.handle() diff --git a/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py b/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py index 91d5e7ebd..f9da44f32 100644 --- a/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py +++ b/src/backends/tests/gamespy/precence_search_player/data_fetch_tests.py @@ -92,7 +92,7 @@ def test_get_matched_info_by_email(self): def test_is_uniquenick_exist(self): with Session(ENGINE) as session: - result1 = data.is_uniquenick_exist("spyguy_test", 0, "gmtests", session) + result1 = data.is_uniquenick_exist("spyguy", 0, "gmtests", session) self.assertTrue(result1) result2 = data.is_uniquenick_exist( diff --git a/src/backends/tests/gamespy/presence_conection_manager/handler_tests.py b/src/backends/tests/gamespy/presence_conection_manager/handler_tests.py index 71284dfbd..0f13886d3 100644 --- a/src/backends/tests/gamespy/presence_conection_manager/handler_tests.py +++ b/src/backends/tests/gamespy/presence_conection_manager/handler_tests.py @@ -1,17 +1,19 @@ import unittest from backends.tests.utils import add_headers import frontends.gamespy.protocols.presence_connection_manager.contracts.requests as pcm +import backends.protocols.gamespy.presence_connection_manager.requests as bkr +import backends.protocols.gamespy.presence_connection_manager.handlers as bkh +import responses from frontends.tests.gamespy.presence_connection_manager.request_tests import ( LOGIN_AUTH_TOKEN, LOGIN_UNIQUE_NICK, LOGIN_USER, ) -import backends.protocols.gamespy.presence_connection_manager.requests as bkr -import backends.protocols.gamespy.presence_connection_manager.handlers as bkh class HandlerTest(unittest.TestCase): # region General + @responses.activate def test_login_authtoken(self): r = pcm.LoginRequest(LOGIN_AUTH_TOKEN) data = add_headers(r) @@ -20,6 +22,7 @@ def test_login_authtoken(self): handler.handle() pass + @responses.activate def test_login_uniquenick(self): r = pcm.LoginRequest(LOGIN_UNIQUE_NICK) data = add_headers(r) @@ -28,6 +31,7 @@ def test_login_uniquenick(self): handler.handle() pass + @responses.activate def test_login_user(self): r = pcm.LoginRequest(LOGIN_USER) data = add_headers(r) @@ -37,16 +41,19 @@ def test_login_user(self): pass @unittest.skip("not implemented") + @responses.activate def test_new_user(self): raise NotImplementedError() @unittest.skip("not implemented") + @responses.activate def test_logout(self): raise NotImplementedError() # region Buddy @unittest.skip("not implemented") + @responses.activate def test_buddy_list(self): r = pcm.BuddyListRequest(profile_id=1, namespace_id=0) data = add_headers(r) @@ -56,6 +63,7 @@ def test_buddy_list(self): pass @unittest.skip("not implemented") + @responses.activate def test_block_list(self): r = pcm.BlockListRequest(profile_id=1, namespace_id=0) data = add_headers(r) @@ -65,62 +73,75 @@ def test_block_list(self): pass @unittest.skip("not implemented") + @responses.activate def test_add_buddy(self): # r = pcm.AddBuddyRequest() raise NotImplementedError() pass @unittest.skip("not implemented") + @responses.activate def test_del_buddy(self): raise NotImplementedError() pass @unittest.skip("not implemented") + @responses.activate def test_add_block(self): raise NotImplementedError() pass @unittest.skip("not implemented") + @responses.activate def test_del_block(self): raise NotImplementedError() pass @unittest.skip("not implemented") + @responses.activate def test_invite_to(self): raise NotImplementedError() pass @unittest.skip("not implemented") + @responses.activate def test_status_info(self): raise NotImplementedError() pass @unittest.skip("not implemented") + @responses.activate def test_statue(self): raise NotImplementedError() pass # region Profile @unittest.skip("not implemented") + @responses.activate def test_get_profile(self): raise NotImplementedError() @unittest.skip("not implemented") + @responses.activate def test_new_profile(self): raise NotImplementedError() @unittest.skip("not implemented") + @responses.activate def test_register_cdkey(self): raise NotImplementedError() @unittest.skip("not implemented") + @responses.activate def test_register_nick(self): raise NotImplementedError() @unittest.skip("not implemented") + @responses.activate def test_update_profile(self): raise NotImplementedError() @unittest.skip("not implemented") + @responses.activate def test_update_user_info(self): raise NotImplementedError() diff --git a/src/backends/tests/gamespy/query_report/handler_tests.py b/src/backends/tests/gamespy/query_report/handler_tests.py index c591613ad..040db7317 100644 --- a/src/backends/tests/gamespy/query_report/handler_tests.py +++ b/src/backends/tests/gamespy/query_report/handler_tests.py @@ -7,7 +7,7 @@ class HandlerTests(unittest.IsolatedAsyncioTestCase): def test_heartbeat(self): request = {"server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "raw_request": "\\u0003\\\\xe5\\\\xcfaZlocalip0\\u0000172.19.0.5\\u0000localport\\u000011111\\u0000natneg\\u00001\\u0000statechanged\\u00003\\u0000gamename\\u0000gmtest\\u0000hostname\\u0000GameSpy QR2 Sample\\u0000gamever\\u00002.00\\u0000hostport\\u000025000\\u0000mapname\\u0000gmtmap1\\u0000gametype\\u0000arena\\u0000numplayers\\u00005\\u0000numteams\\u00002\\u0000maxplayers\\u000032\\u0000gamemode\\u0000openplaying\\u0000teamplay\\u00001\\u0000fraglimit\\u00000\\u0000timelimit\\u000040\\u0000gravity\\u0000800\\u0000rankingon\\u00001\\u0000\\u0000\\u0000\\u0005player_\\u0000score_\\u0000deaths_\\u0000ping_\\u0000team_\\u0000time_\\u0000\\u0000Joe Player\\u000030\\u000012\\u0000411\\u00000\\u000010\\u0000L33t 0n3\\u00000\\u00006\\u0000233\\u00001\\u0000325\\u0000Raptor\\u000015\\u000025\\u000063\\u00001\\u0000462\\u0000Gr81\\u00000\\u000016\\u0000294\\u00000\\u0000870\\u0000Flubber\\u000017\\u000012\\u0000232\\u00001\\u0000384\\u0000\\u0000\\u0002team_t\\u0000score_t\\u0000avgping_t\\u0000\\u0000Red\\u0000294\\u0000357\\u0000Blue\\u0000498\\u0000454\\u0000", "client_ip": "172.19.0.5", "client_port": 11111, - "instant_key": "3855573338", "command_name": 3, "server_data": {"localip0": "172.19.0.5", "localport": "11111", "natneg": "1", "statechanged": "3", "gamename": "gmtest", "hostname": "GameSpy QR2 Sample", "gamever": "2.00", "hostport": "25000", "mapname": "gmtmap1", "gametype": "arena", "numplayers": "5", "numteams": "2", "maxplayers": "32", "gamemode": "openplaying", "teamplay": "1", "fraglimit": "0", "timelimit": "40", "gravity": "800", "rankingon": "1"}, "player_data": [{"player_0": "Joe Player", "score_0": "30", "deaths_0": "12", "ping_0": "411", "team_0": "0", "time_0": "10"}, {"player_1": "L33t 0n3", "score_1": "0", "deaths_1": "6", "ping_1": "233", "team_1": "1", "time_1": "325"}, {"player_2": "Raptor", "score_2": "15", "deaths_2": "25", "ping_2": "63", "team_2": "1", "time_2": "462"}, {"player_3": "Gr81", "score_3": "0", "deaths_3": "16", "ping_3": "294", "team_3": "0", "time_3": "870"}, {"player_4": "Flubber", "score_4": "17", "deaths_4": "12", "ping_4": "232", "team_4": "1", "time_4": "384"}], "team_data": [{"team_t0": "Red", "score_t0": "294", "avgping_t0": "357"}, {"team_t1": "Blue", "score_t1": "498", "avgping_t1": "454"}], "server_status": 3, "group_id": None, "game_name": "gmtest"} + "instant_key": "3855573338", "command_name": 3, "data": {"localip0": "172.19.0.5", "localport": "11111", "natneg": "1", "statechanged": "3", "gamename": "gmtest", "hostname": "GameSpy QR2 Sample", "gamever": "2.00", "hostport": "25000", "mapname": "gmtmap1", "gametype": "arena", "numplayers": "5", "numteams": "2", "maxplayers": "32", "gamemode": "openplaying", "teamplay": "1", "fraglimit": "0", "timelimit": "40", "gravity": "800", "rankingon": "1", "player_0": "Joe Player", "score_0": "30", "deaths_0": "12", "ping_0": "411", "team_0": "0", "time_0": "10", "player_1": "L33t 0n3", "score_1": "0", "deaths_1": "6", "ping_1": "233", "team_1": "1", "time_1": "325", "player_2": "Raptor", "score_2": "15", "deaths_2": "25", "ping_2": "63", "team_2": "1", "time_2": "462", "player_3": "Gr81", "score_3": "0", "deaths_3": "16", "ping_3": "294", "team_3": "0", "time_3": "870", "player_4": "Flubber", "score_4": "17", "deaths_4": "12", "ping_4": "232", "team_4": "1", "time_4": "384", "team_t0": "Red", "score_t0": "294", "avgping_t0": "357", "team_t1": "Blue", "score_t1": "498", "avgping_t1": "454"}, "status": 3, "group_id": None, "game_name": "gmtest"} req = HeartBeatRequest(**request) handler = HeartbeatHandler(req) handler.handle() @@ -21,6 +21,7 @@ def test_available(self): handler.handle() pass + @unittest.skip("game not found") def test_keep_alive(self): request = {"raw_request": "\bg\\xd4\\xcbl", "command_name": 8, "instant_key": "1741998956", "client_ip": "172.19.0.4", "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "client_port": 11111} diff --git a/src/backends/tests/gamespy/server_browser/handler_tests.py b/src/backends/tests/gamespy/server_browser/handler_tests.py index 47aeddaba..17d0072f2 100644 --- a/src/backends/tests/gamespy/server_browser/handler_tests.py +++ b/src/backends/tests/gamespy/server_browser/handler_tests.py @@ -21,9 +21,11 @@ def test_server_main_list(self): handler.handle() pass + @unittest.skip("not implemented") def test_p2p_group_room_list(self): raise NotImplementedError() + @unittest.skip("not implemented") def test_server_network_info_list(self): raise NotImplementedError() diff --git a/src/backends/tests/gamespy/web/handler_tests.py b/src/backends/tests/gamespy/web/handler_tests.py index deed3ed58..ddefa0ff9 100644 --- a/src/backends/tests/gamespy/web/handler_tests.py +++ b/src/backends/tests/gamespy/web/handler_tests.py @@ -2,12 +2,22 @@ from backends.protocols.gamespy.web_services.handlers import LoginRemoteAuthHandler from backends.protocols.gamespy.web_services.requests import LoginRemoteAuthRequest +from frontends.gamespy.protocols.web_services.modules.auth.exceptions.general import AuthException class HandlerTests(unittest.TestCase): def test_sdk_login_remote_auth(self): - raw = {"raw_request": "1000GMTy13lsJmiY7L19ojyN3XTM08ll0C4EWWijwmJyq3ttiZmoDUQJ0OSnar9nQCu5MpOGvi4Z0EcC2uNaS4yKrUA+h+tTDDoJHF7ZjoWKOTj00yNOEdzWyG08cKdVQwFRkF+h8oG/Jd+Ik3sWviXq/+5bhZQ7iXxTbbDwNL6Lagp/pLZ9czLnYPhY7VEcoQlx9oOLH8c.DLe", - "version": 1, "partner_code": 0, "namespace_id": 0, "auth_token": "GMTy13lsJmiY7L19ojyN3XTM08ll0C4EWWijwmJyq3ttiZmoDUQJ0OSnar9nQCu5MpOGvi4Z0EcC2uNaS4yKrUA+h+tTDDoJHF7ZjoWKOTj00yNOEdzWyG08cKdVQwFRkF+h8oG/Jd+Ik3sWviXq/+5bhZQ7iXxTbbDwNL6Lagp/pLZ9czLnYPhY7VEcoQlx9oO", "challenge": "LH8c.DLe", "game_id": 0, "client_ip": "172.19.0.4", "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "client_port": 57502} + with self.assertRaises(AuthException) as context: + raw = {"raw_request": "1000GMTy13lsJmiY7L19ojyN3XTM08ll0C4EWWijwmJyq3ttiZmoDUQJ0OSnar9nQCu5MpOGvi4Z0EcC2uNaS4yKrUA+h+tTDDoJHF7ZjoWKOTj00yNOEdzWyG08cKdVQwFRkF+h8oG/Jd+Ik3sWviXq/+5bhZQ7iXxTbbDwNL6Lagp/pLZ9czLnYPhY7VEcoQlx9oOLH8c.DLe", + "version": 1, "partner_code": 0, "namespace_id": 0, "auth_token": "GMTy13lsJmiY7L19ojyN3XTM08ll0C4EWWijwmJyq3ttiZmoDUQJ0OSnar9nQCu5MpOGvi4Z0EcC2uNaS4yKrUA+h+tTDDoJHF7ZjoWKOTj00yNOEdzWyG08cKdVQwFRkF+h8oG/Jd+Ik3sWviXq/+5bhZQ7iXxTbbDwNL6Lagp/pLZ9czLnYPhY7VEcoQlx9oO", "challenge": "LH8c.DLe", "game_id": 0, "client_ip": "172.19.0.4", "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "client_port": 57502} + request = LoginRemoteAuthRequest.model_validate(raw) + handler = LoginRemoteAuthHandler(request) + handler.handle() + handler.response + + def test_sdk_login_remote_auth_fake_data(self): + raw = {"raw_request": "", + "version": 1, "partner_code": 0, "namespace_id": 0, "auth_token": "example_auth", "challenge": "LH8c.DLe", "game_id": 0, "client_ip": "172.19.0.4", "server_id": "950b7638-a90d-469b-ac1f-861e63c8c613", "client_port": 57502} request = LoginRemoteAuthRequest.model_validate(raw) handler = LoginRemoteAuthHandler(request) handler.handle() diff --git a/src/backends/tests/utils.py b/src/backends/tests/utils.py index 7ad0831f4..629210740 100644 --- a/src/backends/tests/utils.py +++ b/src/backends/tests/utils.py @@ -3,6 +3,9 @@ def add_headers(request: RequestBase) -> dict: request.parse() + if isinstance(request.raw_request, bytes): + request.raw_request = request.raw_request.decode( + "ascii", "backslashreplace") data = request.to_dict() data["client_ip"] = "192.168.0.1" data["server_id"] = "950b7638-a90d-469b-ac1f-861e63c8c613" diff --git a/src/frontends/gamespy/library/abstractions/handler.py b/src/frontends/gamespy/library/abstractions/handler.py index 43ca519c9..ec479681e 100644 --- a/src/frontends/gamespy/library/abstractions/handler.py +++ b/src/frontends/gamespy/library/abstractions/handler.py @@ -130,7 +130,7 @@ def _get_url(self) -> str: def _upload_data(self): """ whether need send data to backend - if child class do not require feach, overide this function to do nothing + if child class do not require fetch, overide this function to do nothing """ self._url = self._get_url() json_str = json.dumps( diff --git a/src/frontends/gamespy/protocols/game_status/applications/handlers.py b/src/frontends/gamespy/protocols/game_status/applications/handlers.py index e69b35c58..69aa48792 100644 --- a/src/frontends/gamespy/protocols/game_status/applications/handlers.py +++ b/src/frontends/gamespy/protocols/game_status/applications/handlers.py @@ -59,8 +59,6 @@ def __init__(self, client: Client, request: NewGameRequest) -> None: class SetPlayerDataHandler(CmdHandlerBase): _request: SetPlayerDataRequest - _result: SetPlayerDataResult - _response: SetPlayerDataResponse def __init__(self, client: Client, request: SetPlayerDataRequest) -> None: assert isinstance(request, SetPlayerDataRequest) diff --git a/src/frontends/gamespy/protocols/game_status/contracts/requests.py b/src/frontends/gamespy/protocols/game_status/contracts/requests.py index 3329858ad..0bf1d1ff0 100644 --- a/src/frontends/gamespy/protocols/game_status/contracts/requests.py +++ b/src/frontends/gamespy/protocols/game_status/contracts/requests.py @@ -181,17 +181,7 @@ class SetPlayerDataRequest(RequestBase): is_key_value: bool def parse(self) -> None: - data_index = self.raw_request.index("data")+4 - final_index = self.raw_request.index("final") - # we temperary save raw request and restore it later, - # for not geting error in super method - temp_raw = self.raw_request - # we get the data part out of the request - self.raw_request = self.raw_request[0:data_index] + \ - self.raw_request[final_index:] super().parse() - self.raw_request = temp_raw - self._request_dict["data"] = temp_raw[data_index:final_index-2] if "pid" not in self._request_dict: raise GSException("pid is missing") diff --git a/src/frontends/tests/gamespy/game_status/game_tests.py b/src/frontends/tests/gamespy/game_status/game_tests.py index 1ed007786..9e642062e 100644 --- a/src/frontends/tests/gamespy/game_status/game_tests.py +++ b/src/frontends/tests/gamespy/game_status/game_tests.py @@ -30,7 +30,7 @@ def test_gmtest(self): "\\newgame\\\\connid\\123\\sesskey\\2020\\lid\\1\\final\\", "\\newgame\\\\connid\\123\\sesskey\\123456\\challenge\\123456789\\lid\\1\\final\\", "\\newgame\\\\connid\\123\\sesskey\\2020\\challenge\\123456789\\lid\\1\\final\\", - "\\setpd\\\\pid\\123\\ptype\\0\\dindex\\1\\kv\\%d\\lid\\1\\length\\5\\data\\11\\lid\\1\\final\\", + "\\setpd\\\\pid\\123\\ptype\\0\\dindex\\1\\kv\\1\\lid\\1\\length\\5\\data\\11\\lid\\1\\final\\", "\\updgame\\\\sesskey\\0\\done\\1\\gamedata\\hello\\lid\\1\\final\\", "\\updgame\\\\sesskey\\2020\\done\\1\\gamedata\\hello\\lid\\1\\final\\", "\\updgame\\\\sesskey\\2020\\connid\\1\\done\\1\\gamedata\\hello\\lid\\1\\final\\", diff --git a/src/frontends/tests/gamespy/game_status/handler_tests.py b/src/frontends/tests/gamespy/game_status/handler_tests.py index 0f4287879..6f83f8dcf 100644 --- a/src/frontends/tests/gamespy/game_status/handler_tests.py +++ b/src/frontends/tests/gamespy/game_status/handler_tests.py @@ -76,6 +76,7 @@ def test_worm3d_auth_player(self): # self.assertEqual() + @responses.activate def test_auth(self): raw = "\\auth\\\\gamename\\crysis2\\response\\xxxxx\\port\\30\\id\\1\\final\\" request = AuthGameRequest(raw) @@ -84,6 +85,7 @@ def test_auth(self): self.assertEqual(30, request.port) self.assertEqual(1, request.local_id) + @responses.activate def test_get_player_data(self): raw = ( "\\getpd\\\\pid\\0\\ptype\\0\\dindex\\1\\keys\\hello\x01hi\\lid\\1\\final\\" @@ -99,6 +101,7 @@ def test_get_player_data(self): self.assertEqual("hello", request.keys[0]) self.assertEqual("hi", request.keys[1]) + @responses.activate def test_get_profile_id(self): raw = "\\getpid\\\\nick\\xiaojiuwo\\keyhash\\00000\\lid\\1\\final\\" request = GetProfileIdRequest(raw) @@ -107,6 +110,7 @@ def test_get_profile_id(self): self.assertEqual("00000", request.key_hash) self.assertEqual(1, request.local_id) + @responses.activate def test_new_game(self): raw1 = "\\newgame\\\\connid\\123\\sesskey\\123456\\lid\\1\\final\\" request1 = NewGameRequest(raw1) @@ -124,6 +128,7 @@ def test_new_game(self): self.assertEqual("123456789", request2.challenge) self.assertEqual(1, request2.local_id) + @responses.activate def test_update_game(self): raw1 = "\\updgame\\\\sesskey\\0\\done\\1\\gamedata\\hello\\lid\\1\\final\\" request1 = UpdateGameRequest(raw1) @@ -140,8 +145,9 @@ def test_update_game(self): self.assertEqual("hello", request2.game_data) self.assertEqual(1, request2.connection_id) + @responses.activate def test_auth_player_2025_11_06(self): - raw = b'\\authp\\\\nick\\spyguy\\keyhash\\00000a308fd86a7eb92cbc8322b03a36\\resp\\a146083990caca4925e3144deb552817\\lid\\1,\x1fZ*&\r1~Shh\x08Db\x0eYKVA-@Fn/\\final\\' + raw = '\\authp\\\\nick\\spyguy\\keyhash\\00000a308fd86a7eb92cbc8322b03a36\\resp\\a146083990caca4925e3144deb552817\\lid\\1\\final\\' request = AuthPlayerRequest(raw) client = create_client() handler = AuthPlayerHandler(client, request) diff --git a/src/frontends/tests/gamespy/game_status/mock_objects.py b/src/frontends/tests/gamespy/game_status/mock_objects.py index c7012fe46..3d5ba74be 100644 --- a/src/frontends/tests/gamespy/game_status/mock_objects.py +++ b/src/frontends/tests/gamespy/game_status/mock_objects.py @@ -49,7 +49,7 @@ def create_client() -> Client: local_id=0, profile_id=0, modified=datetime.now() - ).model_dump(), + ).model_dump(mode="json"), ) create_mock_url( config, diff --git a/src/frontends/tests/gamespy/presence_connection_manager/request_tests.py b/src/frontends/tests/gamespy/presence_connection_manager/request_tests.py index 6ae8e52bf..01a502cfb 100644 --- a/src/frontends/tests/gamespy/presence_connection_manager/request_tests.py +++ b/src/frontends/tests/gamespy/presence_connection_manager/request_tests.py @@ -29,8 +29,8 @@ ) -LOGIN_AUTH_TOKEN = "\\login\\\\challenge\\xxxx\\authtoken\\xxxx\\userid\\0\\profileid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" -LOGIN_UNIQUE_NICK = "\\login\\\\challenge\\xxxx\\uniquenick\\spyguy\\userid\\0\\profileid\\0\\namespaceid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" +LOGIN_AUTH_TOKEN = "\\login\\\\challenge\\xxxx\\authtoken\\example_auth\\userid\\1\\profileid\\1\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" +LOGIN_UNIQUE_NICK = "\\login\\\\challenge\\xxxx\\uniquenick\\spyguy\\userid\\1\\profileid\\1\\namespaceid\\0\\partnerid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" LOGIN_USER = "\\login\\\\challenge\\xxxx\\user\\spyguy@spyguy@gamespy.com\\userid\\0\\profileid\\0\\partnerid\\0\\namespaceid\\0\\response\\xxxxx\\firewall\\1\\port\\0000\\productid\\0\\gamename\\gmtest\\sdkrevision\\4\\quiet\\0\\id\\1\\final\\" ADD_BUDDY = "\\addbuddy\\\\sesskey\\0\\newprofileid\\0\\reason\\test\\final\\" @@ -49,9 +49,9 @@ def test_login_auth_token(self) -> None: request.parse() self.assertEqual(LoginType.AUTH_TOKEN, request.type) self.assertEqual("xxxx", request.user_challenge) - self.assertEqual("xxxx", request.auth_token) - self.assertEqual(0, request.user_id) - self.assertEqual(0, request.profile_id) + self.assertEqual("example_auth", request.auth_token) + self.assertEqual(1, request.user_id) + self.assertEqual(1, request.profile_id) self.assertEqual(0, request.partner_id) self.assertEqual("xxxxx", request.response) self.assertEqual(True, request.firewall) @@ -70,8 +70,8 @@ def test_login_unique_nick(self) -> None: self.assertEqual("xxxx", request.user_challenge) self.assertEqual("spyguy", request.unique_nick) self.assertEqual(0, request.namespace_id) - self.assertEqual(0, request.user_id) - self.assertEqual(0, request.profile_id) + self.assertEqual(1, request.user_id) + self.assertEqual(1, request.profile_id) self.assertEqual(0, request.partner_id) self.assertEqual("xxxxx", request.response) self.assertEqual(True, request.firewall) diff --git a/src/frontends/tests/gamespy/presence_search_player/handler_tests.py b/src/frontends/tests/gamespy/presence_search_player/handler_tests.py index 11af9560b..0a9b0815a 100644 --- a/src/frontends/tests/gamespy/presence_search_player/handler_tests.py +++ b/src/frontends/tests/gamespy/presence_search_player/handler_tests.py @@ -7,10 +7,10 @@ from frontends.tests.gamespy.presence_search_player.mock_objects import create_client CHECK1 = "\\check\\\\nick\\spyguy\\email\\spyguy@gamespy.com\\pass\\0000\\final\\" -SEARCH_1 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\namespaceid\\0\\uniquenick\\spyguy\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" -SEARCH_2 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\nick\\spyguy\\email\\spyguy@gamespy.com\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" -SEARCH_3 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\nick\\spyguy\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" -SEARCH_4 = "\\search\\\\sesskey\\xxxx\\profileid\\0\\email\\spyguy@gamespy.com\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" +SEARCH_1 = "\\search\\\\sesskey\\xxxx\\profileid\\1\\namespaceid\\0\\uniquenick\\spyguy\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" +SEARCH_2 = "\\search\\\\sesskey\\xxxx\\profileid\\1\\nick\\spyguy\\email\\spyguy@gamespy.com\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" +SEARCH_3 = "\\search\\\\sesskey\\xxxx\\profileid\\1\\nick\\spyguy\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" +SEARCH_4 = "\\search\\\\sesskey\\xxxx\\profileid\\1\\email\\spyguy@gamespy.com\\firstname\\spy\\lastname\\guy\\icquin\\123\\skip\\0\\gamename\\gmtest\\final\\" SEARCH_UNIQUENICK = "\\searchunique\\\\sesskey\\xxxx\\profileid\\0\\uniquenick\\spyguy\\namespaces\\1,2,3,4,5\\gamename\\gmtest\\final\\" diff --git a/src/frontends/tests/gamespy/server_browser/mock_objects.py b/src/frontends/tests/gamespy/server_browser/mock_objects.py index ca300d416..06beef543 100644 --- a/src/frontends/tests/gamespy/server_browser/mock_objects.py +++ b/src/frontends/tests/gamespy/server_browser/mock_objects.py @@ -56,21 +56,13 @@ def create_v2_client() -> Client: "instant_key": "123456", "game_name": "Example Game", "query_report_port": 8080, - "last_heart_beat_received_time": "2023-10-01T12:00:00Z", + "update_time": "2023-10-01T12:00:00Z", "status": 3, - "server_data": { + "data": { "max_players": "100", "current_players": "50", "region": "US-East", }, - "player_data": [ - {"player_id": "player1", "player_name": "Player One"}, - {"player_id": "player2", "player_name": "Player Two"}, - ], - "team_data": [ - {"team_id": "team1", "team_name": "Team Alpha"}, - {"team_id": "team2", "team_name": "Team Beta"}, - ], } } ).model_dump(mode="json"), From b856d816464cdf4b4c6132a6003cc9c240894bb6 Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Fri, 12 Dec 2025 08:25:14 +0000 Subject: [PATCH 229/231] Update(web): create user account functions --- src/backends/library/database/pg_orm.py | 2 +- .../protocols/gamespy/web_services/data.py | 2 +- .../gamespy/web_services/handlers.py | 8 +- .../gamespy/web_services/requests.py | 7 ++ .../gamespy/web_services/responses.py | 11 ++- .../gamespy/library/network/http_handler.py | 46 ++++-------- .../web_services/abstractions/contracts.py | 5 -- .../web_services/aggregations/soap_envelop.py | 19 +++-- .../web_services/applications/client.py | 1 + .../modules/auth/abstractions/general.py | 14 ++-- .../modules/auth/applications/handlers.py | 14 ++++ .../modules/auth/contracts/requests.py | 75 +++++++++++++------ .../modules/auth/contracts/responses.py | 7 ++ .../modules/auth/contracts/results.py | 4 + .../tests/gamespy/web_services/auth_tests.py | 69 ++++++++++------- 15 files changed, 181 insertions(+), 103 deletions(-) diff --git a/src/backends/library/database/pg_orm.py b/src/backends/library/database/pg_orm.py index efe24702e..775014087 100644 --- a/src/backends/library/database/pg_orm.py +++ b/src/backends/library/database/pg_orm.py @@ -69,7 +69,7 @@ class Users(Base): ) banned: Column[bool] = Column(Boolean, default=False, nullable=False) deleted: Column[bool] = Column(Boolean, default=False, nullable=False) - + class Profiles(Base): __tablename__ = "profiles" diff --git a/src/backends/protocols/gamespy/web_services/data.py b/src/backends/protocols/gamespy/web_services/data.py index 53f539183..0bc299089 100644 --- a/src/backends/protocols/gamespy/web_services/data.py +++ b/src/backends/protocols/gamespy/web_services/data.py @@ -137,7 +137,7 @@ def get_info_by_uniquenick( result = ( session.query(Users, Profiles, SubProfiles) .join(Users, Users.userid == Profiles.userid) - .join(Profiles, Profiles.profileid == SubProfiles.profileid) + .join(SubProfiles, SubProfiles.profileid == Profiles.profileid) .where( SubProfiles.uniquenick == uniquenick, SubProfiles.namespaceid == namespace_id, diff --git a/src/backends/protocols/gamespy/web_services/handlers.py b/src/backends/protocols/gamespy/web_services/handlers.py index 7e6125ce4..16933bd0d 100644 --- a/src/backends/protocols/gamespy/web_services/handlers.py +++ b/src/backends/protocols/gamespy/web_services/handlers.py @@ -6,6 +6,7 @@ import backends.protocols.gamespy.web_services.data as data from backends.protocols.gamespy.web_services.requests import ( CreateRecordRequest, + CreateUserAccountRequest, GetMyRecordsRequest, GetPurchaseHistoryRequest, GetStoreAvailabilityRequest, @@ -19,6 +20,7 @@ from frontends.gamespy.protocols.web_services.modules.auth.contracts.results import ( LoginProfileResult, LoginRemoteAuthResult, + LoginUniqueNickResult, ) from frontends.gamespy.protocols.web_services.modules.direct2game.contracts.results import ( GetPurchaseHistoryResult, @@ -93,7 +95,7 @@ def _data_operate(self) -> None: ) def _result_construct(self) -> None: - self._result = LoginProfileResult( + self._result = LoginUniqueNickResult( user_id=self.data[0], profile_id=self.data[1], profile_nick=self.data[2], @@ -105,6 +107,10 @@ def _result_construct(self) -> None: ) +class CreateUserAccountHandler(HandlerBase): + _request: CreateUserAccountRequest + + # region d2g diff --git a/src/backends/protocols/gamespy/web_services/requests.py b/src/backends/protocols/gamespy/web_services/requests.py index edbbcf038..2c92ba29f 100644 --- a/src/backends/protocols/gamespy/web_services/requests.py +++ b/src/backends/protocols/gamespy/web_services/requests.py @@ -121,8 +121,15 @@ class LoginUniqueNickRequest(AuthRequestBase): password: str +class CreateUserAccountRequest(AuthRequestBase): + email: str + profile_nick: str + uniquenick: str + password: str + # D2G + class Direct2GameRequestBase(lib.RequestBase): pass diff --git a/src/backends/protocols/gamespy/web_services/responses.py b/src/backends/protocols/gamespy/web_services/responses.py index f3c11cb4c..2a12dad96 100644 --- a/src/backends/protocols/gamespy/web_services/responses.py +++ b/src/backends/protocols/gamespy/web_services/responses.py @@ -1,8 +1,10 @@ from backends.library.abstractions.contracts import DataResponse -from frontends.gamespy.protocols.web_services.modules.auth.contracts.results import LoginProfileResult, LoginPs3CertResult, LoginRemoteAuthResult, LoginUniqueNickResult +from frontends.gamespy.protocols.web_services.modules.auth.contracts.results import CreateUserAccountResult, LoginProfileResult, LoginPs3CertResult, LoginRemoteAuthResult, LoginUniqueNickResult from frontends.gamespy.protocols.web_services.modules.direct2game.contracts.results import GetPurchaseHistoryResult from frontends.gamespy.protocols.web_services.modules.sake.contracts.results import CreateRecordResult, GetMyRecordsResult, SearchForRecordsResult +# region Auth + class LoginProfileResponse(DataResponse): result: LoginProfileResult @@ -24,9 +26,16 @@ class GetPurchaceHistoryResponse(DataResponse): result: GetPurchaseHistoryResult +class CreateUserAccountResponse(DataResponse): + result: CreateUserAccountResult + + + # class GetTargettedAdResponse(DataResponse): # result: GetTargettedAdResult +# region Sake + class CreateRecordResponse(DataResponse): result: CreateRecordResult diff --git a/src/frontends/gamespy/library/network/http_handler.py b/src/frontends/gamespy/library/network/http_handler.py index 0e0c81734..a88a0af4a 100644 --- a/src/frontends/gamespy/library/network/http_handler.py +++ b/src/frontends/gamespy/library/network/http_handler.py @@ -8,51 +8,31 @@ from frontends.gamespy.library.log.log_manager import LogWriter -class HttpRequest: - url: str - headers: dict[str, str] - content: str - - def __init__(self, url: str, headers: dict, content: str) -> None: - assert isinstance(url, str) - assert isinstance(headers, dict) - assert isinstance(content, str) - self.url = url - self.headers = headers - self.content = content - - -class HttpResponse: - def __init__(self, request: HttpRequest, content: str) -> None: - assert isinstance(request, HttpRequest) - assert isinstance(content, str) - self.request = request - self.content = content - - def get_content_bytes(self) -> bytes: - return self.content.encode("ascii") - - class HttpConnection(ConnectionBase): handler: BaseHTTPRequestHandler - def send(self, data: HttpResponse) -> None: + def send(self, data: bytes) -> None: + assert isinstance(data, bytes) self.handler.send_response(200) self.handler.send_header("Content-type", "text/xml") self.handler.end_headers() - self.handler.wfile.write(data.get_content_bytes()) + self.handler.wfile.write(data) class HttpHandler(BaseHTTPRequestHandler): conn: HttpConnection - + def do_POST(self) -> None: # parsed_url = urlparse(self.path).geturl() content_length = int(self.headers["Content-Length"]) data = self.rfile.read(content_length).decode() - self.conn = HttpConnection(self, *self.server.unispy_params) # type: ignore + self.conn = HttpConnection( + self, *self.server.unispy_params) # type: ignore self.conn.on_received(data.encode()) + def log_message(self, format, *args): + pass + class HttpServer(NetworkServerBase): def __init__( @@ -60,9 +40,13 @@ def __init__( ) -> None: super().__init__(config, t_client, logger) self._server = ThreadingHTTPServer( - (self._config.listening_address, self._config.listening_port), HttpHandler + (self._config.listening_address, + self._config.listening_port), HttpHandler ) - self._server.unispy_params = (self._config, self._client_cls, self._logger) # type: ignore + self._server.unispy_params = ( # type: ignore + self._config, + self._client_cls, + self._logger) class TestClient(ClientBase): diff --git a/src/frontends/gamespy/protocols/web_services/abstractions/contracts.py b/src/frontends/gamespy/protocols/web_services/abstractions/contracts.py index bbd89608f..55d56cedd 100644 --- a/src/frontends/gamespy/protocols/web_services/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/web_services/abstractions/contracts.py @@ -31,11 +31,6 @@ class ResponseBase(lib.ResponseBase): def __init__(self, result: ResultBase) -> None: assert issubclass(type(result), ResultBase) - if not hasattr(self, "_content"): - raise WebException( - "Soap envelope content must be initialized in response sub class" - ) - assert isinstance(self._content, SoapEnvelop) super().__init__(result) def build(self) -> None: diff --git a/src/frontends/gamespy/protocols/web_services/aggregations/soap_envelop.py b/src/frontends/gamespy/protocols/web_services/aggregations/soap_envelop.py index 57949dffe..868fe31db 100644 --- a/src/frontends/gamespy/protocols/web_services/aggregations/soap_envelop.py +++ b/src/frontends/gamespy/protocols/web_services/aggregations/soap_envelop.py @@ -6,6 +6,7 @@ class SoapEnvelop: soap_envelop_namespace = "http://schemas.xmlsoap.org/soap/envelope/" current_element: ET.Element + parent_element: ET.Element content: ET.Element def __init__(self, body_namespace: str): @@ -27,22 +28,26 @@ def change_to_element(self, name: str): raise WebException("can not find the node") self.current_element = current_element - def back_to_parent_element(self): - self.current_element = self.body + def go_to_content_element(self): + content_element = list(self.body.iter())[1] + self.current_element = content_element def add(self, name: str, value: object = None): + tag = f"{{{self._body_namespace}}}{name}" new_element = ET.SubElement( - self.current_element, f"{{{self._body_namespace}}}{name}" + self.current_element, tag ) - if value is not None: - new_element.text = str(value) + if value is None: + self.parent_element = self.current_element self.current_element = new_element + else: + new_element.text = str(value) def __str__(self) -> str: return ET.tostring( self.content, xml_declaration=True, encoding="utf-8", method="xml" - ).decode() + ).decode("utf-8").replace("'", '"') if __name__ == "__main__": @@ -51,6 +56,6 @@ def __str__(self) -> str: s = SoapEnvelop("http://gamespy.net/AuthService/") s.add("level1", "1") s.add("level1.1", "1.1") - s.back_to_parent_element() + s.go_to_content_element() s.add("level2", "2") str(s) diff --git a/src/frontends/gamespy/protocols/web_services/applications/client.py b/src/frontends/gamespy/protocols/web_services/applications/client.py index 7c80e4e58..213edb6c5 100644 --- a/src/frontends/gamespy/protocols/web_services/applications/client.py +++ b/src/frontends/gamespy/protocols/web_services/applications/client.py @@ -36,6 +36,7 @@ class Client(ClientBase): def __init__(self, connection: HttpConnection, server_config: ServerConfig, logger: LogWriter): super().__init__(connection, server_config, logger) self.info = ClientInfo() + self.is_log_raw = False def _create_switcher(self, buffer: bytes) -> SwitcherBase: from frontends.gamespy.protocols.web_services.applications.switcher import Switcher diff --git a/src/frontends/gamespy/protocols/web_services/modules/auth/abstractions/general.py b/src/frontends/gamespy/protocols/web_services/modules/auth/abstractions/general.py index a9afb80ec..5cb4834f1 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/auth/abstractions/general.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/abstractions/general.py @@ -47,21 +47,23 @@ class LoginResultBase(lib.ResultBase): class LoginResponseBase(lib.ResponseBase): _result: LoginResultBase - _content: SoapEnvelop = SoapEnvelop("http://gamespy.net/AuthService/") - _expiretime: int = int( - (datetime.datetime.now() + datetime.timedelta(days=1)).timestamp() - ) + _content: SoapEnvelop + _expiretime: int def __init__(self, result: LoginResultBase) -> None: assert isinstance(result, LoginResultBase) super().__init__(result) + self._content = SoapEnvelop("http://gamespy.net/AuthService/") + self._expiretime = int( + (datetime.datetime.now() + datetime.timedelta(days=1)).timestamp() + ) def build(self) -> None: self._build_context() super().build() def _build_context(self): - self._content.add("responseCode", "h") + self._content.add("responseCode", "0") self._content.add("certificate") self._content.add("length", self._result.length) self._content.add("version", self._result.version) @@ -78,7 +80,7 @@ def _build_context(self): self._content.add("serverdata", ClientInfo.SERVER_DATA) hash_str = self.__compute_hash() self._content.add("signature", ClientInfo.SIGNATURE_PREFIX + hash_str) - self._content.back_to_parent_element() + self._content.go_to_content_element() self._content.add("peerkeyprivate", ClientInfo.PEER_KEY_EXPONENT) def __compute_hash(self) -> str: diff --git a/src/frontends/gamespy/protocols/web_services/modules/auth/applications/handlers.py b/src/frontends/gamespy/protocols/web_services/modules/auth/applications/handlers.py index c770db356..78b6f4a38 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/auth/applications/handlers.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/applications/handlers.py @@ -2,6 +2,7 @@ from frontends.gamespy.protocols.web_services.abstractions.handler import CmdHandlerBase from frontends.gamespy.protocols.web_services.applications.client import Client from frontends.gamespy.protocols.web_services.modules.auth.contracts.requests import ( + CreateUserAccountRequest, LoginProfileRequest, LoginProfileWithGameIdRequest, LoginPs3CertRequest, @@ -12,6 +13,7 @@ LoginUniqueNickWithGameIdRequest, ) from frontends.gamespy.protocols.web_services.modules.auth.contracts.responses import ( + CreateUserAccountResponse, LoginProfileResponse, LoginProfileWithGameIdResponse, LoginPs3CertResponse, @@ -22,6 +24,7 @@ LoginUniqueNickWithGameIdResponse, ) from frontends.gamespy.protocols.web_services.modules.auth.contracts.results import ( + CreateUserAccountResult, LoginProfileResult, LoginPs3CertResult, LoginRemoteAuthResult, @@ -101,4 +104,15 @@ class LoginUniqueNickWithGameIdHandler(CmdHandlerBase): _response: LoginUniqueNickWithGameIdResponse def __init__(self, client: Client, request: LoginUniqueNickWithGameIdRequest) -> None: + assert isinstance(request, LoginUniqueNickWithGameIdRequest) super().__init__(client, request) + + +class CreateUserAccountHandler(CmdHandlerBase): + _request: CreateUserAccountRequest + _result: CreateUserAccountResult + _response: CreateUserAccountResponse + + def __init__(self, client: Client, request: CreateUserAccountRequest) -> None: + assert isinstance(request, CreateUserAccountRequest) + super().__init__(client, request) \ No newline at end of file diff --git a/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/requests.py b/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/requests.py index c4688c5d8..ad3e3aa8a 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/requests.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/requests.py @@ -7,7 +7,7 @@ class LoginProfileRequest(LoginRequestBase): email: str - uniquenick: str + nick: str cdkey: str password: str @@ -15,24 +15,19 @@ def parse(self) -> None: super().parse() email = self._content_element.find(f".//{{{NAMESPACE}}}email") if email is None or email.text is None: - raise AuthException("email is missing from the request.") + raise AuthException("email is missing") self.email = email.text - uniquenick = self._content_element.find( - f".//{{{NAMESPACE}}}uniquenick") - if uniquenick is None or uniquenick.text is None: - raise AuthException("uniquenick is missing from the request.") - self.uniquenick = uniquenick.text - - cdkey = self._content_element.find(f".//{{{NAMESPACE}}}cdkey") - if cdkey is None or cdkey.text is None: - raise AuthException("cdkey is missing from the request.") - self.cdkey = cdkey.text + nick = self._content_element.find( + f".//{{{NAMESPACE}}}profilenick") + if nick is None or nick.text is None: + raise AuthException("uniquenick is missing") + self.nick = nick.text password = self._content_element.find( f".//{{{NAMESPACE}}}password//{{{NAMESPACE}}}Value") if password is None or password.text is None: - raise AuthException("password is missing from the request.") + raise AuthException("password is missing") self.password = password.text @@ -43,7 +38,7 @@ def parse(self) -> None: super().parse() game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") if game_id is None or game_id.text is None: - raise AuthException("game id is missing from the request.") + raise AuthException("game id is missing") self.game_id = int(game_id.text) @@ -63,12 +58,12 @@ def parse(self) -> None: game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") if game_id is None or game_id.text is None: - raise AuthException("game id is missing from the request.") + raise AuthException("game id is missing") self.game_id = int(game_id.text) npticket = self._content_element.find(f".//{{{NAMESPACE}}}npticket") if npticket is None or npticket.text is None: - raise AuthException("npticket is missing from the request.") + raise AuthException("npticket is missing") self.npticket = npticket.text @@ -79,7 +74,7 @@ def parse(self) -> None: super().parse() game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") if game_id is None or game_id.text is None: - raise AuthException("game id is missing from the request.") + raise AuthException("game id is missing") self.game_id = int(game_id.text) @@ -92,18 +87,18 @@ def parse(self) -> None: auth_token = self._content_element.find( f".//{{{NAMESPACE}}}authtoken") if auth_token is None or auth_token.text is None: - raise AuthException("authtoken is missing from the request.") + raise AuthException("authtoken is missing") self.auth_token = auth_token.text challenge = self._content_element.find( f".//{{{NAMESPACE}}}challenge") if challenge is None or challenge.text is None: - raise AuthException("challenge is missing from the request.") + raise AuthException("challenge is missing") self.challenge = challenge.text game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") if game_id is None or game_id.text is None: - raise AuthException("game id is missing from the request.") + raise AuthException("game id is missing") self.game_id = int(game_id.text) @@ -114,7 +109,7 @@ def parse(self) -> None: super().parse() game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") if game_id is None or game_id.text is None: - raise AuthException("game id is missing from the request.") + raise AuthException("game id is missing") self.game_id = int(game_id.text) @@ -128,13 +123,13 @@ def parse(self) -> None: unique_nick_node = self._content_element.find( f".//{{{NAMESPACE}}}uniquenick") if unique_nick_node is None or unique_nick_node.text is None: - raise AuthException("uniquenick is missing from the request.") + raise AuthException("uniquenick is missing") self.uniquenick = unique_nick_node.text password = self._content_element.find( f".//{{{NAMESPACE}}}password//{{{NAMESPACE}}}Value") if password is None or password.text is None: - raise AuthException("password is missing from the request.") + raise AuthException("password is missing") self.password = password.text @@ -145,6 +140,38 @@ def parse(self) -> None: super().parse() game_id = self._content_element.find(f".//{{{NAMESPACE}}}gameid") if game_id is None or game_id.text is None: - raise AuthException("game id is missing from the request.") + raise AuthException("game id is missing") self.game_id = int(game_id.text) + + +class CreateUserAccountRequest(LoginRequestBase): + email: str + profile_nick: str + uniquenick: str + password: str + + def parse(self) -> None: + super().parse() + email = self._content_element.find(f".//{{{NAMESPACE}}}email") + if email is None or email.text is None: + raise AuthException("email is missing") + self.email = email.text + + profile_nick = self._content_element.find( + f".//{{{NAMESPACE}}}profilenick") + if profile_nick is None or profile_nick.text is None: + raise AuthException("password is missing") + self.profile_nick = profile_nick.text + + uniquenick = self._content_element.find( + f".//{{{NAMESPACE}}}uniquenick") + if uniquenick is None or uniquenick.text is None: + raise AuthException("password is missing") + self.pasuniquenicksword = uniquenick.text + + password = self._content_element.find( + f".//{{{NAMESPACE}}}password//{{{NAMESPACE}}}Value") + if password is None or password.text is None: + raise AuthException("password is missing") + self.password = password.text diff --git a/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/responses.py b/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/responses.py index 26f4a62b5..5210a5ee3 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/responses.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/responses.py @@ -1,3 +1,4 @@ +from frontends.gamespy.protocols.web_services.applications.client import ClientInfo from frontends.gamespy.protocols.web_services.modules.auth.abstractions.general import LoginResponseBase from frontends.gamespy.protocols.web_services.modules.auth.contracts.requests import ( LoginProfileRequest, @@ -70,3 +71,9 @@ class LoginUniqueNickWithGameIdResponse(LoginResponseBase): def build(self) -> None: self._content.add("LoginUniqueNickWithGameIdResult") super().build() + + +class CreateUserAccountResponse(LoginResponseBase): + def build(self) -> None: + self._content.add("CreateUserAccountResult") + super().build() diff --git a/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/results.py b/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/results.py index 1e1b985ea..30c8cea3c 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/results.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/results.py @@ -16,3 +16,7 @@ class LoginRemoteAuthResult(LoginResultBase): class LoginUniqueNickResult(LoginResultBase): pass + + +class CreateUserAccountResult(LoginResultBase): + pass diff --git a/src/frontends/tests/gamespy/web_services/auth_tests.py b/src/frontends/tests/gamespy/web_services/auth_tests.py index bac197327..4c02d4450 100644 --- a/src/frontends/tests/gamespy/web_services/auth_tests.py +++ b/src/frontends/tests/gamespy/web_services/auth_tests.py @@ -2,29 +2,25 @@ import responses -from frontends.gamespy.protocols.web_services.modules.auth.contracts.requests import LoginProfileWithGameIdRequest, LoginPs3CertRequest, LoginRemoteAuthRequest, LoginUniqueNickRequest - -LOGIN_PROFILE = """ - - - - 1 - 0 - 0 - 0 - spyguy@gamespy.com - spyguy - XXXXXXXXXXX - - XXXXXXXXXXX - - - - """ +from frontends.gamespy.protocols.web_services.modules.auth.contracts.requests import CreateUserAccountRequest, LoginProfileWithGameIdRequest, LoginPs3CertRequest, LoginRemoteAuthRequest, LoginUniqueNickRequest + +LOGIN_PROFILE = """ + + 1 + 0 + 0 + 0 + spyguy@gamespy.com + spyguy + + + 00026cfb61b75553fb113e8a158a1ce8c88dcb5415a405efeeb4ba605c315bb9204c09a53e23fb63081d208f44f813f5967c4da7d85f55ef66d4d39a4c89a9800771ee4f742bb808f35bced93fe7fa289eb4bdc94a44fa6f9121ef0eba4d3827d0cacaaeafdc85b64e4d9dc34814f6d4178363303f543a2fd8d8af0030303030 + + + + """ LOGIN_PS3_CERT = """ """ +CREATE_USER_ACCOUNT = """ + + 1 + 99 + 102 + 4C84AA7017F21B0A5B83B56D6D06C86D22E93662@example.com + 4C84AA7017F21B0A5B83 + 4C84AA7017F21B0A5B83B56D6D06C86D22E93662 + + 411CDA6489C3223A4FB6CA7BD6521F961679CFA5E04C6C95A988995658975B4B99D2C1E91629354F8422E470BDA5EC06614E42576E2E812A1792E75589EC9BDA0CF3B9D1D60BF1847A424717950AA1C81DD93CEF029F931A33359208D6EC2F0A8AEF3C49F1048E9D585D55AFA0A9E2D545EAF2DB0460B3DE2965EB55E5FF88A2 + + + """ + class AuthTests(unittest.TestCase): + @responses.activate + def test_create_user_account(self): + request = CreateUserAccountRequest(CREATE_USER_ACCOUNT) + request.parse() + pass + @responses.activate def test_crysis_auth(self): @@ -117,9 +135,8 @@ def test_login_profile(self): self.assertEqual(0, request.partner_code) self.assertEqual(0, request.namespace_id) self.assertEqual("spyguy@gamespy.com", request.email) - self.assertEqual("spyguy", request.uniquenick) - self.assertEqual("XXXXXXXXXXX", request.cdkey) - self.assertEqual("XXXXXXXXXXX", request.password) + self.assertEqual("spyguy", request.nick) + self.assertEqual("00026cfb61b75553fb113e8a158a1ce8c88dcb5415a405efeeb4ba605c315bb9204c09a53e23fb63081d208f44f813f5967c4da7d85f55ef66d4d39a4c89a9800771ee4f742bb808f35bced93fe7fa289eb4bdc94a44fa6f9121ef0eba4d3827d0cacaaeafdc85b64e4d9dc34814f6d4178363303f543a2fd8d8af0030303030", request.password) # handler = LoginProfileWithGameIdHandler(request) # handler.handle() From b6c13846a45496f332630d84da1df8795912daf8 Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Sun, 14 Dec 2025 06:13:10 +0000 Subject: [PATCH 230/231] Update(web): remove soap response namespaces --- .../web_services/aggregations/soap_envelop.py | 36 ++++++++----------- .../modules/auth/abstractions/general.py | 1 - .../modules/auth/applications/handlers.py | 2 +- .../modules/auth/contracts/responses.py | 20 +++++------ .../direct2game/abstractions/contracts.py | 3 +- .../direct2game/contracts/responses.py | 8 ++--- .../modules/sake/abstractions/generals.py | 1 - 7 files changed, 30 insertions(+), 41 deletions(-) diff --git a/src/frontends/gamespy/protocols/web_services/aggregations/soap_envelop.py b/src/frontends/gamespy/protocols/web_services/aggregations/soap_envelop.py index 868fe31db..d7ef43ba1 100644 --- a/src/frontends/gamespy/protocols/web_services/aggregations/soap_envelop.py +++ b/src/frontends/gamespy/protocols/web_services/aggregations/soap_envelop.py @@ -4,36 +4,26 @@ class SoapEnvelop: - soap_envelop_namespace = "http://schemas.xmlsoap.org/soap/envelope/" current_element: ET.Element - parent_element: ET.Element content: ET.Element - def __init__(self, body_namespace: str): - self._body_namespace = body_namespace - ET.register_namespace("SOAP-ENV", self.soap_envelop_namespace) - self.content = ET.Element(f"{{{self.soap_envelop_namespace}}}Envelope") - self.body = ET.Element(f"{{{self.soap_envelop_namespace}}}Body") - self.content.append(self.body) - self.current_element = self.body - - def finish_add_sub_element(self): - self.current_element = ET.SubElement( - self.current_element, self._body_namespace) + def __init__(self, root_name: str): + assert isinstance(root_name, str) + self.content = ET.Element(root_name) + self.current_element = self.content def change_to_element(self, name: str): - current_element = self.body.find( - f".//{{{self._body_namespace}}}{name}") + current_element = self.content.find( + f".//{name}") if current_element is None: raise WebException("can not find the node") self.current_element = current_element def go_to_content_element(self): - content_element = list(self.body.iter())[1] - self.current_element = content_element + self.current_element = self.content def add(self, name: str, value: object = None): - tag = f"{{{self._body_namespace}}}{name}" + tag = f"{name}" new_element = ET.SubElement( self.current_element, tag ) @@ -45,15 +35,17 @@ def add(self, name: str, value: object = None): new_element.text = str(value) def __str__(self) -> str: - return ET.tostring( - self.content, xml_declaration=True, encoding="utf-8", method="xml" - ).decode("utf-8").replace("'", '"') + xml_str: str = ET.tostring( + self.content, + encoding="unicode" + ) + return xml_str if __name__ == "__main__": import xml.etree.ElementTree as ET - s = SoapEnvelop("http://gamespy.net/AuthService/") + s = SoapEnvelop("test_name") s.add("level1", "1") s.add("level1.1", "1.1") s.go_to_content_element() diff --git a/src/frontends/gamespy/protocols/web_services/modules/auth/abstractions/general.py b/src/frontends/gamespy/protocols/web_services/modules/auth/abstractions/general.py index 5cb4834f1..ca98e2da5 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/auth/abstractions/general.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/abstractions/general.py @@ -53,7 +53,6 @@ class LoginResponseBase(lib.ResponseBase): def __init__(self, result: LoginResultBase) -> None: assert isinstance(result, LoginResultBase) super().__init__(result) - self._content = SoapEnvelop("http://gamespy.net/AuthService/") self._expiretime = int( (datetime.datetime.now() + datetime.timedelta(days=1)).timestamp() ) diff --git a/src/frontends/gamespy/protocols/web_services/modules/auth/applications/handlers.py b/src/frontends/gamespy/protocols/web_services/modules/auth/applications/handlers.py index 78b6f4a38..d2a1f156d 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/auth/applications/handlers.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/applications/handlers.py @@ -115,4 +115,4 @@ class CreateUserAccountHandler(CmdHandlerBase): def __init__(self, client: Client, request: CreateUserAccountRequest) -> None: assert isinstance(request, CreateUserAccountRequest) - super().__init__(client, request) \ No newline at end of file + super().__init__(client, request) diff --git a/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/responses.py b/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/responses.py index 5210a5ee3..31090a92e 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/responses.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/responses.py @@ -1,4 +1,4 @@ -from frontends.gamespy.protocols.web_services.applications.client import ClientInfo +from frontends.gamespy.protocols.web_services.aggregations.soap_envelop import SoapEnvelop from frontends.gamespy.protocols.web_services.modules.auth.abstractions.general import LoginResponseBase from frontends.gamespy.protocols.web_services.modules.auth.contracts.requests import ( LoginProfileRequest, @@ -15,7 +15,7 @@ class LoginProfileResponse(LoginResponseBase): _result: LoginProfileResult def build(self) -> None: - self._content.add("LoginProfileResult") + self._content = SoapEnvelop("LoginProfileResult") super().build() @@ -23,7 +23,7 @@ class LoginProfileWithGameIdResponse(LoginResponseBase): _request: LoginProfileWithGameIdRequest def build(self) -> None: - self._content.add("LoginProfileWithGameIdResult") + self._content = SoapEnvelop("LoginProfileWithGameIdResult") super().build() @@ -31,7 +31,7 @@ class LoginPs3CertResponse(LoginResponseBase): _result: LoginPs3CertResult def build(self) -> None: - self._content.add("LoginPs3CertResult") + self._content = SoapEnvelop("LoginPs3CertResult") self._content.add("responseCode", self._result.response_code) self._content.add("authToken", self._result.auth_token) self._content.add("partnerChallenge", self._result.partner_challenge) @@ -42,7 +42,7 @@ class LoginPs3CertWithGameIdResponse(LoginResponseBase): _result: LoginPs3CertResult def build(self) -> None: - self._content.add("LoginPs3CertWithGameIdResult") + self._content = SoapEnvelop("LoginPs3CertWithGameIdResult") self._content.add("responseCode", self._result.response_code) self._content.add("authToken", self._result.auth_token) self._content.add("partnerChallenge", self._result.partner_challenge) @@ -51,29 +51,29 @@ def build(self) -> None: class LoginRemoteAuthResponse(LoginResponseBase): def build(self) -> None: - self._content.add("LoginRemoteAuthResult") + self._content = SoapEnvelop("LoginRemoteAuthResult") super().build() class LoginRemoteAuthWithGameIdResponse(LoginResponseBase): def build(self) -> None: - self._content.add("LoginRemoteAuthWithGameIdResult") + self._content = SoapEnvelop("LoginRemoteAuthWithGameIdResult") super().build() class LoginUniqueNickResponse(LoginResponseBase): def build(self) -> None: - self._content.add("LoginUniqueNickResult") + self._content = SoapEnvelop("LoginUniqueNickResult") super().build() class LoginUniqueNickWithGameIdResponse(LoginResponseBase): def build(self) -> None: - self._content.add("LoginUniqueNickWithGameIdResult") + self._content = SoapEnvelop("LoginUniqueNickWithGameIdResult") super().build() class CreateUserAccountResponse(LoginResponseBase): def build(self) -> None: - self._content.add("CreateUserAccountResult") + self._content = SoapEnvelop("CreateUserAccountResult") super().build() diff --git a/src/frontends/gamespy/protocols/web_services/modules/direct2game/abstractions/contracts.py b/src/frontends/gamespy/protocols/web_services/modules/direct2game/abstractions/contracts.py index f07625095..6f4d0cfc8 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/direct2game/abstractions/contracts.py +++ b/src/frontends/gamespy/protocols/web_services/modules/direct2game/abstractions/contracts.py @@ -13,5 +13,4 @@ class ResultBase(lib.ResultBase): class ResponseBase(lib.ResponseBase): - _content: SoapEnvelop = SoapEnvelop(NAMESPACE) - pass + _content: SoapEnvelop \ No newline at end of file diff --git a/src/frontends/gamespy/protocols/web_services/modules/direct2game/contracts/responses.py b/src/frontends/gamespy/protocols/web_services/modules/direct2game/contracts/responses.py index 1d083c07c..cc7e30523 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/direct2game/contracts/responses.py +++ b/src/frontends/gamespy/protocols/web_services/modules/direct2game/contracts/responses.py @@ -1,3 +1,5 @@ +from frontends.gamespy.protocols.web_services.abstractions.contracts import ResultBase +from frontends.gamespy.protocols.web_services.aggregations.soap_envelop import SoapEnvelop from frontends.gamespy.protocols.web_services.modules.direct2game.abstractions.contracts import ResponseBase from frontends.gamespy.protocols.web_services.modules.direct2game.contracts.results import ( GetPurchaseHistoryResult, @@ -9,8 +11,7 @@ class GetPurchaseHistoryResponse(ResponseBase): _result: GetPurchaseHistoryResult def build(self) -> None: - self._content.add("GetPurchaseHistoryResponse") - self._content.add("GetPurchaseHistoryResult") + self._content = SoapEnvelop("GetPurchaseHistoryResult") self._content.add("status") self._content.add("code", self._result.code) self._content.change_to_element("GetPurchaseHistoryResult") @@ -23,8 +24,7 @@ class GetStoreAvailabilityResponse(ResponseBase): _result: GetStoreAvailabilityResult def build(self) -> None: - self._content.add("GetStoreAvailabilityResponse") - self._content.add("GetStoreAvailabilityResult") + self._content = SoapEnvelop("GetStoreAvailabilityResult") self._content.add("status") self._content.add("code", self._result.code) self._content.change_to_element("GetStoreAvailabilityResult") diff --git a/src/frontends/gamespy/protocols/web_services/modules/sake/abstractions/generals.py b/src/frontends/gamespy/protocols/web_services/modules/sake/abstractions/generals.py index 7e4c4b87d..a2887eee7 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/sake/abstractions/generals.py +++ b/src/frontends/gamespy/protocols/web_services/modules/sake/abstractions/generals.py @@ -54,7 +54,6 @@ class ResultBase(lib.ResultBase): class ResponseBase(lib.ResponseBase): def __init__(self, result: ResultBase) -> None: - self._content = SoapEnvelop(NAMESPACE) super().__init__(result) From cc8c7986acf3e86a2d9fee5d85190704422f71e6 Mon Sep 17 00:00:00 2001 From: xiaojiuwo1993 Date: Mon, 15 Dec 2025 08:15:07 +0000 Subject: [PATCH 231/231] Fix(web): create user account errors --- .../gamespy/web_services/handlers.py | 27 ++++++++++++++++++- .../gamespy/web_services/requests.py | 2 +- .../{webservices.py => web_services.py} | 12 +++++++-- src/backends/routers/home.py | 4 +-- .../gamespy/library/abstractions/client.py | 3 ++- .../gamespy/library/network/http_handler.py | 2 +- .../web_services/applications/switcher.py | 12 ++++++--- .../modules/auth/contracts/requests.py | 10 +++---- 8 files changed, 55 insertions(+), 17 deletions(-) rename src/backends/routers/gamespy/{webservices.py => web_services.py} (88%) diff --git a/src/backends/protocols/gamespy/web_services/handlers.py b/src/backends/protocols/gamespy/web_services/handlers.py index 16933bd0d..b0980c1a6 100644 --- a/src/backends/protocols/gamespy/web_services/handlers.py +++ b/src/backends/protocols/gamespy/web_services/handlers.py @@ -3,6 +3,8 @@ # region auth from backends.library.abstractions.contracts import RequestBase from backends.library.abstractions.handler_base import HandlerBase +from backends.protocols.gamespy.presence_search_player.handlers import NewUserHandler +from backends.protocols.gamespy.presence_search_player.requests import NewUserRequest import backends.protocols.gamespy.web_services.data as data from backends.protocols.gamespy.web_services.requests import ( CreateRecordRequest, @@ -16,8 +18,9 @@ LoginUniqueNickRequest, SearchForRecordsRequest, ) -from backends.protocols.gamespy.web_services.responses import CreateRecordResponse, GetMyRecordsResponse, LoginProfileResponse, LoginRemoteAuthRepsonse, LoginUniqueNickResponse, SearchForRecordsResponse +from backends.protocols.gamespy.web_services.responses import CreateRecordResponse, CreateUserAccountResponse, GetMyRecordsResponse, LoginProfileResponse, LoginRemoteAuthRepsonse, LoginUniqueNickResponse, SearchForRecordsResponse from frontends.gamespy.protocols.web_services.modules.auth.contracts.results import ( + CreateUserAccountResult, LoginProfileResult, LoginRemoteAuthResult, LoginUniqueNickResult, @@ -109,6 +112,28 @@ def _result_construct(self) -> None: class CreateUserAccountHandler(HandlerBase): _request: CreateUserAccountRequest + response: CreateUserAccountResponse + + def _data_operate(self) -> None: + dump = self._request.model_dump() + dump["operation_id"] = 0 + dump["product_id"] = 0 + req = NewUserRequest.model_validate(dump) + h = NewUserHandler(req) + h.handle() + self.data = h._result + + def _result_construct(self) -> None: + self._result = CreateUserAccountResult( + user_id=self.data.user_id, + profile_id=self.data.profile_id, + profile_nick=self._request.nick, + unique_nick=self._request.uniquenick, + cdkey_hash="", + version=3, + namespace_id=self._request.namespace_id, + partner_code=self._request.partner_code + ) # region d2g diff --git a/src/backends/protocols/gamespy/web_services/requests.py b/src/backends/protocols/gamespy/web_services/requests.py index 2c92ba29f..494b45644 100644 --- a/src/backends/protocols/gamespy/web_services/requests.py +++ b/src/backends/protocols/gamespy/web_services/requests.py @@ -123,7 +123,7 @@ class LoginUniqueNickRequest(AuthRequestBase): class CreateUserAccountRequest(AuthRequestBase): email: str - profile_nick: str + nick: str uniquenick: str password: str diff --git a/src/backends/routers/gamespy/webservices.py b/src/backends/routers/gamespy/web_services.py similarity index 88% rename from src/backends/routers/gamespy/webservices.py rename to src/backends/routers/gamespy/web_services.py index 5c0034b07..9d31c283e 100644 --- a/src/backends/routers/gamespy/webservices.py +++ b/src/backends/routers/gamespy/web_services.py @@ -1,10 +1,10 @@ from fastapi import APIRouter from backends.library.abstractions.contracts import RESPONSES_DEF, OKResponse -from backends.protocols.gamespy.web_services.handlers import CreateRecordHandler, GetMyRecordsHandler, LoginProfileHandler, LoginRemoteAuthHandler, LoginUniqueNickHandler, SearchForRecordsHandler +from backends.protocols.gamespy.web_services.handlers import CreateRecordHandler, CreateUserAccountHandler, GetMyRecordsHandler, LoginProfileHandler, LoginRemoteAuthHandler, LoginUniqueNickHandler, SearchForRecordsHandler from backends.protocols.gamespy.web_services.responses import CreateRecordResponse, GetMyRecordsResponse, LoginProfileResponse, LoginRemoteAuthRepsonse, LoginUniqueNickResponse, SearchForRecordsResponse from backends.urls import WEB_SERVICES -from backends.protocols.gamespy.web_services.requests import CreateRecordRequest, GetMyRecordsRequest, LoginProfileRequest, LoginRemoteAuthRequest, LoginUniqueNickRequest, SearchForRecordsRequest +from backends.protocols.gamespy.web_services.requests import CreateRecordRequest, CreateUserAccountRequest, GetMyRecordsRequest, LoginProfileRequest, LoginRemoteAuthRequest, LoginUniqueNickRequest, SearchForRecordsRequest router = APIRouter() @@ -68,7 +68,15 @@ def login_uniquenick_with_game_id(request: LoginUniqueNickRequest) -> LoginUniqu return login_uniquenick(request) +@router.post(f"{WEB_SERVICES}/CreateUserAccountHandler", responses=RESPONSES_DEF) +def create_user_account(request: CreateUserAccountRequest): + handler = CreateUserAccountHandler(request) + handler.handle() + return handler.response + # SAKE services + + @router.post(f"{WEB_SERVICES}/CreateRecordHandler", responses=RESPONSES_DEF) def create_record(request: CreateRecordRequest) -> CreateRecordResponse: handler = CreateRecordHandler(request) diff --git a/src/backends/routers/home.py b/src/backends/routers/home.py index b90ca2124..cbc05f80c 100644 --- a/src/backends/routers/home.py +++ b/src/backends/routers/home.py @@ -22,7 +22,7 @@ presence_search_player, query_report, server_browser, - webservices, + web_services, ) app = FastAPI() @@ -35,7 +35,7 @@ app.include_router(presence_search_player.router) app.include_router(query_report.router) app.include_router(server_browser.router) -app.include_router(webservices.router) +app.include_router(web_services.router) logger = LogManager.create("backend") diff --git a/src/frontends/gamespy/library/abstractions/client.py b/src/frontends/gamespy/library/abstractions/client.py index 581d40834..72b901c86 100644 --- a/src/frontends/gamespy/library/abstractions/client.py +++ b/src/frontends/gamespy/library/abstractions/client.py @@ -96,13 +96,14 @@ def send(self, response: "ResponseBase") -> None: buffer = sending_buffer else: raise UniSpyException("not supported buffer type") - self.log_network_sending(buffer) if self.crypto is not None: buffer = self.crypto.encrypt(buffer) self.connection.send(buffer) + self.log_network_sending(buffer) + def log_debug(self, message: str) -> None: self.logger.debug(f"{self._log_prefix}: {message}") diff --git a/src/frontends/gamespy/library/network/http_handler.py b/src/frontends/gamespy/library/network/http_handler.py index a88a0af4a..92fdf7d4e 100644 --- a/src/frontends/gamespy/library/network/http_handler.py +++ b/src/frontends/gamespy/library/network/http_handler.py @@ -15,10 +15,10 @@ def send(self, data: bytes) -> None: assert isinstance(data, bytes) self.handler.send_response(200) self.handler.send_header("Content-type", "text/xml") + self.handler.send_header("Content-Length", str(len(data))) self.handler.end_headers() self.handler.wfile.write(data) - class HttpHandler(BaseHTTPRequestHandler): conn: HttpConnection diff --git a/src/frontends/gamespy/protocols/web_services/applications/switcher.py b/src/frontends/gamespy/protocols/web_services/applications/switcher.py index 869852166..89264fad9 100644 --- a/src/frontends/gamespy/protocols/web_services/applications/switcher.py +++ b/src/frontends/gamespy/protocols/web_services/applications/switcher.py @@ -6,8 +6,8 @@ from frontends.gamespy.protocols.web_services.applications.client import Client from frontends.gamespy.protocols.web_services.aggregations.exceptions import WebException -from frontends.gamespy.protocols.web_services.modules.auth.contracts.requests import LoginProfileRequest, LoginProfileWithGameIdRequest, LoginRemoteAuthRequest, LoginRemoteAuthWithGameIdRequest, LoginUniqueNickRequest, LoginUniqueNickWithGameIdRequest -from frontends.gamespy.protocols.web_services.modules.auth.applications.handlers import LoginProfileHandler, LoginProfileWithGameIdHandler, LoginRemoteAuthHandler, LoginRemoteAuthWithGameIdHandler, LoginUniqueNickHandler, LoginUniqueNickWithGameIdHandler +from frontends.gamespy.protocols.web_services.modules.auth.contracts.requests import CreateUserAccountRequest, LoginProfileRequest, LoginProfileWithGameIdRequest, LoginRemoteAuthRequest, LoginRemoteAuthWithGameIdRequest, LoginUniqueNickRequest, LoginUniqueNickWithGameIdRequest +from frontends.gamespy.protocols.web_services.modules.auth.applications.handlers import CreateUserAccountHandler, LoginProfileHandler, LoginProfileWithGameIdHandler, LoginRemoteAuthHandler, LoginRemoteAuthWithGameIdHandler, LoginUniqueNickHandler, LoginUniqueNickWithGameIdHandler from frontends.gamespy.protocols.web_services.modules.direct2game.contracts.requests import GetPurchaseHistoryRequest, GetStoreAvailabilityRequest from frontends.gamespy.protocols.web_services.modules.direct2game.applications.handlers import GetPurchaseHistoryHandler, GetStoreAvailabilityHandler from frontends.gamespy.protocols.web_services.modules.sake.contracts.requests import CreateRecordRequest, GetMyRecordsRequest, SearchForRecordsRequest @@ -23,7 +23,10 @@ def __init__(self, client: ClientBase, raw_request: str) -> None: super().__init__(client, raw_request) def _process_raw_request(self) -> None: - name_node = ET.fromstring(self._raw_request)[0][0] + try: + name_node = ET.fromstring(self._raw_request)[0][0] + except Exception as e: + raise WebException(f"xml serialization failed: {str(e)}") if name_node is None: raise WebException("name node is missing from soap request") if name_node.tag is None: @@ -67,7 +70,8 @@ def _create_cmd_handlers(self, name: str, raw_request: str) -> CmdHandlerBase | return LoginUniqueNickHandler(self._client, LoginUniqueNickRequest(raw_request)) case "LoginUniqueNickWithGameId": return LoginUniqueNickWithGameIdHandler(self._client, LoginUniqueNickWithGameIdRequest(raw_request)) - + case "CreateUserAccount": + return CreateUserAccountHandler(self._client, CreateUserAccountRequest(raw_request)) # Direct2Game services case "GetStoreAvailability": diff --git a/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/requests.py b/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/requests.py index ad3e3aa8a..bb29de391 100644 --- a/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/requests.py +++ b/src/frontends/gamespy/protocols/web_services/modules/auth/contracts/requests.py @@ -147,7 +147,7 @@ def parse(self) -> None: class CreateUserAccountRequest(LoginRequestBase): email: str - profile_nick: str + nick: str uniquenick: str password: str @@ -158,17 +158,17 @@ def parse(self) -> None: raise AuthException("email is missing") self.email = email.text - profile_nick = self._content_element.find( + nick = self._content_element.find( f".//{{{NAMESPACE}}}profilenick") - if profile_nick is None or profile_nick.text is None: + if nick is None or nick.text is None: raise AuthException("password is missing") - self.profile_nick = profile_nick.text + self.nick = nick.text uniquenick = self._content_element.find( f".//{{{NAMESPACE}}}uniquenick") if uniquenick is None or uniquenick.text is None: raise AuthException("password is missing") - self.pasuniquenicksword = uniquenick.text + self.uniquenick = uniquenick.text password = self._content_element.find( f".//{{{NAMESPACE}}}password//{{{NAMESPACE}}}Value")