diff --git a/Supercell.GUT.Logic/Avatar/LogicClientAvatar.cs b/Supercell.GUT.Logic/Avatar/LogicClientAvatar.cs index c52523b..dad32ff 100644 --- a/Supercell.GUT.Logic/Avatar/LogicClientAvatar.cs +++ b/Supercell.GUT.Logic/Avatar/LogicClientAvatar.cs @@ -72,4 +72,9 @@ public class LogicClientAvatar : LogicBase checksumEncoder.WriteInt(this.TutorialFlags); } + + public void SetTutorialFlags(int tutorialFlags) + { + this.TutorialFlags |= tutorialFlags; + } } diff --git a/Supercell.GUT.Logic/Message/Account/CreateAvatarMessage.cs b/Supercell.GUT.Logic/Message/Account/CreateAvatarMessage.cs new file mode 100644 index 0000000..9bfc243 --- /dev/null +++ b/Supercell.GUT.Logic/Message/Account/CreateAvatarMessage.cs @@ -0,0 +1,46 @@ +using Supercell.GUT.Logic.Message.Attributes; +using Supercell.GUT.Titan.Logic.Message; + +namespace Supercell.GUT.Logic.Message.Account; + +[VersionedMessage(10200)] +public class CreateAvatarMessage : VersionedMessage +{ + public string? Name { get; set; } + + public CreateAvatarMessage() : base(0) + { + Name = null; + } + + public override void Encode() + { + base.Encode(); + + ByteStream.WriteString(Name); + } + + public override void Decode() + { + base.Decode(); + + Name = ByteStream.ReadString(); + } + + public override int GetMessageType() + { + return 10200; + } + + public override int GetServiceNodeType() + { + return 1; + } + + public override void Destruct() + { + base.Destruct(); + + Name = null; + } +} diff --git a/Supercell.GUT.Logic/Message/Account/KeepAliveMessage.cs b/Supercell.GUT.Logic/Message/Account/KeepAliveMessage.cs new file mode 100644 index 0000000..bf6867e --- /dev/null +++ b/Supercell.GUT.Logic/Message/Account/KeepAliveMessage.cs @@ -0,0 +1,33 @@ +using Supercell.GUT.Logic.Message.Attributes; +using Supercell.GUT.Titan.Logic.Message; + +namespace Supercell.GUT.Logic.Message.Account; + +[VersionedMessage(10108)] +public class KeepAliveMessage : VersionedMessage +{ + public KeepAliveMessage() : base(0) + { + ; + } + + public override void Encode() + { + base.Encode(); + } + + public override void Decode() + { + base.Decode(); + } + + public override int GetMessageType() + { + return 10108; + } + + public override int GetServiceNodeType() + { + return 1; + } +} diff --git a/Supercell.GUT.Logic/Message/Avatar/AddableFriendsMessage.cs b/Supercell.GUT.Logic/Message/Avatar/AddableFriendsMessage.cs new file mode 100644 index 0000000..03ef7fa --- /dev/null +++ b/Supercell.GUT.Logic/Message/Avatar/AddableFriendsMessage.cs @@ -0,0 +1,42 @@ +using Supercell.GUT.Logic.Message.Attributes; +using Supercell.GUT.Titan.Logic.Message; + +namespace Supercell.GUT.Logic.Message.Avatar; + +[VersionedMessage(20107)] +public class AddableFriendsMessage : VersionedMessage +{ + public AddableFriendsMessage() : base(0) + { + ; + } + + public override void Encode() + { + base.Encode(); + + this.ByteStream.WriteInt(-1); + } + + public override void Decode() + { + base.Decode(); + + this.ByteStream.ReadInt(); + } + + public override int GetMessageType() + { + return 20107; + } + + public override int GetServiceNodeType() + { + return 3; + } + + public override void Destruct() + { + base.Destruct(); + } +} diff --git a/Supercell.GUT.Logic/Message/Avatar/AskForAddableFriendsMessage.cs b/Supercell.GUT.Logic/Message/Avatar/AskForAddableFriendsMessage.cs new file mode 100644 index 0000000..7304eac --- /dev/null +++ b/Supercell.GUT.Logic/Message/Avatar/AskForAddableFriendsMessage.cs @@ -0,0 +1,62 @@ +using Supercell.GUT.Logic.Message.Attributes; +using Supercell.GUT.Titan.Logic.Message; +using Supercell.GUT.Titan.Logic.Util; + +namespace Supercell.GUT.Logic.Message.Avatar; + +[VersionedMessage(10503)] +public class AskForAddableFriendsMessage : VersionedMessage +{ + public LogicArrayList FacebookIds { get; set; } + public LogicArrayList GamecenterIds { get; set; } + + public AskForAddableFriendsMessage() : base(0) + { + this.FacebookIds = new LogicArrayList(); + this.GamecenterIds = new LogicArrayList(); + } + + public override void Destruct() + { + base.Destruct(); + + this.FacebookIds.Clear(); + this.GamecenterIds.Clear(); + } + + public override void Encode() + { + base.Encode(); + + int size = this.FacebookIds.Size(); + + this.ByteStream.WriteInt(size); + for (int i = 0; i < size; i++) + { + this.ByteStream.WriteString(this.FacebookIds[i]); + } + + size = this.GamecenterIds.Size(); + + this.ByteStream.WriteInt(size); + for (int i = 0; i < size; i++) + { + this.ByteStream.WriteString(this.GamecenterIds[i]); + } + } + + public override void Decode() + { + base.Decode(); + } + + public override int GetMessageType() + { + return 10503; + } + + public override int GetServiceNodeType() + { + return 3; + } +} diff --git a/Supercell.GUT.Logic/Message/Avatar/AskForFriendListMessage.cs b/Supercell.GUT.Logic/Message/Avatar/AskForFriendListMessage.cs new file mode 100644 index 0000000..c94d717 --- /dev/null +++ b/Supercell.GUT.Logic/Message/Avatar/AskForFriendListMessage.cs @@ -0,0 +1,23 @@ +using Supercell.GUT.Logic.Message.Attributes; +using Supercell.GUT.Titan.Logic.Message; + +namespace Supercell.GUT.Logic.Message.Avatar; + +[VersionedMessage(10504)] +public class AskForFriendListMessage : VersionedMessage +{ + public AskForFriendListMessage() : base(0) + { + ; + } + + public override int GetMessageType() + { + return 10504; + } + + public override int GetServiceNodeType() + { + return 3; + } +} diff --git a/Supercell.GUT.Logic/Message/Avatar/FriendListMessage.cs b/Supercell.GUT.Logic/Message/Avatar/FriendListMessage.cs new file mode 100644 index 0000000..1751b95 --- /dev/null +++ b/Supercell.GUT.Logic/Message/Avatar/FriendListMessage.cs @@ -0,0 +1,37 @@ +using Supercell.GUT.Logic.Message.Attributes; +using Supercell.GUT.Titan.Logic.Message; + +namespace Supercell.GUT.Logic.Message.Avatar; + +[VersionedMessage(20105)] +public class FriendListMessage : VersionedMessage +{ + public FriendListMessage() : base(0) + { + ; + } + + public override void Encode() + { + base.Encode(); + + this.ByteStream.WriteInt(-1); + } + + public override void Decode() + { + base.Decode(); + + this.ByteStream.ReadInt(); + } + + public override int GetMessageType() + { + return 20105; + } + + public override int GetServiceNodeType() + { + return 3; + } +} diff --git a/Supercell.GUT.Logic/Message/Avatar/TutorialProgressUpdateMessage.cs b/Supercell.GUT.Logic/Message/Avatar/TutorialProgressUpdateMessage.cs new file mode 100644 index 0000000..c4d89f9 --- /dev/null +++ b/Supercell.GUT.Logic/Message/Avatar/TutorialProgressUpdateMessage.cs @@ -0,0 +1,46 @@ +using Supercell.GUT.Logic.Message.Attributes; +using Supercell.GUT.Titan.Logic.Message; + +namespace Supercell.GUT.Logic.Message.Avatar; + +[VersionedMessage(10210)] +public class TutorialProgressUpdateMessage : VersionedMessage +{ + public int TutorialFlags { get; set; } + + public TutorialProgressUpdateMessage() : base(0) + { + TutorialFlags = 0; + } + + public override void Destruct() + { + base.Destruct(); + + TutorialFlags = 0; + } + + public override void Encode() + { + base.Encode(); + + ByteStream.WriteInt(TutorialFlags); + } + + public override void Decode() + { + base.Decode(); + + TutorialFlags = ByteStream.ReadInt(); + } + + public override int GetMessageType() + { + return 10210; + } + + public override int GetServiceNodeType() + { + return 3; + } +} diff --git a/Supercell.GUT.Server/Database/DatabaseManager.cs b/Supercell.GUT.Server/Database/DatabaseManager.cs new file mode 100644 index 0000000..0c452c8 --- /dev/null +++ b/Supercell.GUT.Server/Database/DatabaseManager.cs @@ -0,0 +1,54 @@ +using Microsoft.Extensions.Options; +using MongoDB.Driver; +using Supercell.GUT.Server.Database.Document; +using Supercell.GUT.Server.Database.Options; + +namespace Supercell.GUT.Server.Database; + +public class DatabaseManager +{ + private const string IdentifierCounterCollection = "t_id_counter"; + private static readonly FindOneAndUpdateOptions s_counterUpdateOptions = new() { ReturnDocument = ReturnDocument.After }; + + private readonly DatabaseOptions _options; + + private readonly MongoClient _client; + private readonly IMongoDatabase _database; + + private readonly IMongoCollection _idCounters; + + public DatabaseManager(IOptions options) + { + _options = options.Value; + + _client = new MongoClient(_options.MongoConnectionString); + _database = _client.GetDatabase(_options.DatabaseName); + + _idCounters = _database.GetCollection(IdentifierCounterCollection); + } + + public IMongoCollection GetCollection(string collectionName) where TDatabaseDocument : IDatabaseDocument + { + return _database.GetCollection(collectionName); + } + + public async Task GetNextLowIdAsync(string counterName) + { + var cursor = await _idCounters.FindAsync(doc => doc.CounterName == counterName); + if (!cursor.Any()) + { + await _idCounters.InsertOneAsync(new IdentifierCounterDocument + { + CounterName = counterName, + CurrentLowId = 0 + }); + } + + IdentifierCounterDocument? document = await _idCounters.FindOneAndUpdateAsync( + x => x.CounterName == counterName, + Builders.Update.Inc(nameof(IdentifierCounterDocument.CurrentLowId), 1), + s_counterUpdateOptions); + + return document.CurrentLowId; + } +} diff --git a/Supercell.GUT.Server/Database/Document/AccountDocument.cs b/Supercell.GUT.Server/Database/Document/AccountDocument.cs new file mode 100644 index 0000000..253d199 --- /dev/null +++ b/Supercell.GUT.Server/Database/Document/AccountDocument.cs @@ -0,0 +1,19 @@ +using Supercell.GUT.Titan.Logic.Math; + +namespace Supercell.GUT.Server.Database.Document; +public class AccountDocument : MongoSerializeableBase, IDatabaseDocument +{ + public LogicLong DocumentId { get; set; } + public string SessionKey { get; set; } = string.Empty; + + public AccountDocument() + { + DocumentId = new(); + } + + public AccountDocument(LogicLong id, string token) + { + DocumentId = id; + SessionKey = token; + } +} diff --git a/Supercell.GUT.Server/Database/Document/AvatarDocument.cs b/Supercell.GUT.Server/Database/Document/AvatarDocument.cs new file mode 100644 index 0000000..b535e12 --- /dev/null +++ b/Supercell.GUT.Server/Database/Document/AvatarDocument.cs @@ -0,0 +1,28 @@ +using Supercell.GUT.Logic.Avatar; +using Supercell.GUT.Titan.Logic.Math; + +namespace Supercell.GUT.Server.Database.Document; + +public class AvatarDocument : MongoSerializeableBase, IDatabaseDocument +{ + public LogicLong DocumentId { get; set; } + + public LogicClientAvatar LogicClientAvatar { get; set; } = new(); + + public int LastSaveTime { get; set; } + + public AvatarDocument() + { + DocumentId = new(); + } + + public AvatarDocument(LogicLong id) + { + DocumentId = id; + } + + public void SetId(LogicLong id) + { + LogicClientAvatar.Id = id; + } +} diff --git a/Supercell.GUT.Server/Database/Document/IDatabaseDocument.cs b/Supercell.GUT.Server/Database/Document/IDatabaseDocument.cs new file mode 100644 index 0000000..4f9ff9d --- /dev/null +++ b/Supercell.GUT.Server/Database/Document/IDatabaseDocument.cs @@ -0,0 +1,8 @@ +using Supercell.GUT.Titan.Logic.Math; + +namespace Supercell.GUT.Server.Database.Document; + +public interface IDatabaseDocument +{ + LogicLong DocumentId { get; set; } +} diff --git a/Supercell.GUT.Server/Database/Document/IdentifierCounterDocument.cs b/Supercell.GUT.Server/Database/Document/IdentifierCounterDocument.cs new file mode 100644 index 0000000..d4bcef3 --- /dev/null +++ b/Supercell.GUT.Server/Database/Document/IdentifierCounterDocument.cs @@ -0,0 +1,7 @@ +namespace Supercell.GUT.Server.Database.Document; + +internal class IdentifierCounterDocument : MongoSerializeableBase +{ + public string CounterName { get; set; } = string.Empty; + public int CurrentLowId { get; set; } +} diff --git a/Supercell.GUT.Server/Database/Document/MongoSerializeableBase.cs b/Supercell.GUT.Server/Database/Document/MongoSerializeableBase.cs new file mode 100644 index 0000000..58a0e3b --- /dev/null +++ b/Supercell.GUT.Server/Database/Document/MongoSerializeableBase.cs @@ -0,0 +1,13 @@ +using System.Text.Json.Serialization; +using MongoDB.Bson; +using MongoDB.Bson.Serialization.Attributes; + +namespace Supercell.GUT.Server.Database.Document; + +public abstract class MongoSerializeableBase +{ + [BsonId] + [BsonRepresentation(BsonType.ObjectId)] + [JsonIgnore] + public string? MongoId { get; set; } +} diff --git a/Supercell.GUT.Server/Database/Options/DatabaseOptions.cs b/Supercell.GUT.Server/Database/Options/DatabaseOptions.cs new file mode 100644 index 0000000..feaf33f --- /dev/null +++ b/Supercell.GUT.Server/Database/Options/DatabaseOptions.cs @@ -0,0 +1,7 @@ +namespace Supercell.GUT.Server.Database.Options; + +public class DatabaseOptions +{ + public required string MongoConnectionString { get; set; } + public required string DatabaseName { get; set; } +} diff --git a/Supercell.GUT.Server/Document/DocumentManager.cs b/Supercell.GUT.Server/Document/DocumentManager.cs new file mode 100644 index 0000000..a87a3db --- /dev/null +++ b/Supercell.GUT.Server/Document/DocumentManager.cs @@ -0,0 +1,86 @@ +using MongoDB.Driver; +using Supercell.GUT.Server.Database; +using Supercell.GUT.Server.Database.Document; +using Supercell.GUT.Server.Util; +using Supercell.GUT.Titan.Logic.Math; + +namespace Supercell.GUT.Server.Document; + +internal class DocumentManager +{ + private const string AccountCollectionName = "t_account_info"; + private const string AvatarCollectionName = "t_player_avatar"; + + private const string AccountIdCounterName = "AccountId"; + + private readonly DatabaseManager _databaseManager; + + public LogicLong Id { get; private set; } = new(); + + public AccountDocument? AccountDocument { get; private set; } + public AvatarDocument? AvatarDocument { get; private set; } + + public DocumentManager(DatabaseManager databaseManager) + { + _databaseManager = databaseManager; + } + + public async Task SaveAsync() + { + if (AccountDocument != null) + { + IMongoCollection accountCollection = _databaseManager.GetCollection(AccountCollectionName); + await accountCollection.ReplaceOneAsync(doc => doc.DocumentId.Equals(AccountDocument.DocumentId), AccountDocument); + } + + if (AvatarDocument != null) + { + AvatarDocument.LastSaveTime = TimeUtil.GetCurrentTimestamp(); + + IMongoCollection avatarCollection = _databaseManager.GetCollection(AvatarCollectionName); + await avatarCollection.ReplaceOneAsync(doc => doc.DocumentId.Equals(AvatarDocument.DocumentId), AvatarDocument); + } + } + + public async Task CreateAccount() + { + if (AccountDocument != null) throw new InvalidOperationException("DocumentManager::CreateAccount: called when AccountDocument already created!"); + + int lowId = await _databaseManager.GetNextLowIdAsync(AccountIdCounterName); + + AccountDocument = new(new(0, lowId), "ToDoRandomToken"); + IMongoCollection accountCollection = _databaseManager.GetCollection(AccountCollectionName); + + await accountCollection.InsertOneAsync(AccountDocument); + Id = AccountDocument.DocumentId; + } + + public async Task EnsureAccountDocument() + { + if (AccountDocument != null) return; + + IMongoCollection accountCollection = _databaseManager.GetCollection(AccountCollectionName); + AccountDocument = await (await accountCollection.FindAsync(document => document.DocumentId.Equals(Id))).SingleOrDefaultAsync(); + } + + public async Task EnsureAvatarDocument() + { + if (AvatarDocument != null) return; + + IMongoCollection avatarCollection = _databaseManager.GetCollection(AvatarCollectionName); + AvatarDocument = await (await avatarCollection.FindAsync(document => document.DocumentId.Equals(Id))).SingleOrDefaultAsync(); + + if (AvatarDocument == null) + { + AvatarDocument = new(Id); + await avatarCollection.InsertOneAsync(AvatarDocument); + } + } + + public void SetDocumentId(LogicLong id) + { + if (Id.LowerInt > 0) throw new InvalidOperationException("DocumentManager::SetDocumentId: trying to override Id."); + + Id = id; + } +} \ No newline at end of file diff --git a/Supercell.GUT.Server/Network/Connection/ClientConnection.cs b/Supercell.GUT.Server/Network/Connection/ClientConnection.cs index 60f34f5..592f08c 100644 --- a/Supercell.GUT.Server/Network/Connection/ClientConnection.cs +++ b/Supercell.GUT.Server/Network/Connection/ClientConnection.cs @@ -1,4 +1,5 @@ using Supercell.GUT.Logic; +using Supercell.GUT.Logic.Avatar; using Supercell.GUT.Server.Protocol; using Supercell.GUT.Titan.Logic.Message; @@ -11,6 +12,8 @@ internal class ClientConnection private readonly IConnectionListener _listener; private readonly MessageManager _messageManager; + public LogicClientAvatar? LogicClientAvatar { get; set; } + private readonly byte[] _receiveBuffer; private IProtocolEntity? _protocolEntity; private DateTime _lastKeepAliveTime; diff --git a/Supercell.GUT.Server/Program.cs b/Supercell.GUT.Server/Program.cs index 43a192b..ca1ab5f 100644 --- a/Supercell.GUT.Server/Program.cs +++ b/Supercell.GUT.Server/Program.cs @@ -2,7 +2,10 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Supercell.GUT.Logic.Message; +using Supercell.GUT.Server.Database; +using Supercell.GUT.Server.Database.Options; using Supercell.GUT.Server.Debugging; +using Supercell.GUT.Server.Document; using Supercell.GUT.Server.Network; using Supercell.GUT.Server.Network.Connection; using Supercell.GUT.Server.Network.Options; @@ -17,6 +20,7 @@ namespace Supercell.GUT.Server; internal static class Program { private const string GatewayOptionsSection = "Gateway"; + private const string DatabaseOptionsSection = "Database"; private static async Task Main(string[] args) { @@ -25,6 +29,7 @@ internal static class Program HostApplicationBuilder builder = Host.CreateApplicationBuilder(args); builder.Services.Configure(builder.Configuration.GetRequiredSection(GatewayOptionsSection)); + builder.Services.Configure(builder.Configuration.GetRequiredSection(DatabaseOptionsSection)); builder.Services.AddHandlers(); builder.Services.AddSingleton(); @@ -34,6 +39,8 @@ internal static class Program builder.Services.AddScoped(); builder.Services.AddSingleton(); builder.Services.AddScoped(); + builder.Services.AddScoped(); + builder.Services.AddSingleton(); builder.Services.AddHostedService(); diff --git a/Supercell.GUT.Server/Protocol/Handlers/AccountMessageHandler.cs b/Supercell.GUT.Server/Protocol/Handlers/AccountMessageHandler.cs index 70c60c6..1ac0d19 100644 --- a/Supercell.GUT.Server/Protocol/Handlers/AccountMessageHandler.cs +++ b/Supercell.GUT.Server/Protocol/Handlers/AccountMessageHandler.cs @@ -1,5 +1,7 @@ using Microsoft.Extensions.Logging; using Supercell.GUT.Logic.Message.Account; +using Supercell.GUT.Server.Database.Document; +using Supercell.GUT.Server.Document; using Supercell.GUT.Server.Network.Connection; using Supercell.GUT.Server.Protocol.Attributes; @@ -10,11 +12,19 @@ internal class AccountMessageHandler : MessageHandlerBase { private readonly ILogger _logger; private readonly ClientConnection _connection; + private readonly DocumentManager _documentManager; - public AccountMessageHandler(ClientConnection connection, ILogger logger) + public AccountMessageHandler(ClientConnection connection, ILogger logger, DocumentManager documentManager) { _logger = logger; _connection = connection; + _documentManager = documentManager; + } + + [MessageHandler(10108)] + public async Task OnKeepAlive(KeepAliveMessage keepAliveMessage) + { + await Task.CompletedTask; } [MessageHandler(10105)] @@ -33,32 +43,59 @@ internal class AccountMessageHandler : MessageHandlerBase [MessageHandler(10103)] public async Task OnCreateAccount(CreateAccountMessage createAccountMessage) { - _logger.LogInformation("Creating account! FacebookId: {fid} | GameCenterId: {gcid}", - createAccountMessage.FacebookId, - createAccountMessage.GameCenterId); + await _documentManager.CreateAccount(); + + AccountDocument? accountDocument = _documentManager.AccountDocument; + if (accountDocument == null) + { + await _connection.SendMessage(new CreateAccountFailedMessage() + { + ErrorCode = 1 + }); + + return; + } await _connection.SendMessage(new CreateAccountOkMessage() { - AccountIdHigherInt = 0, - AccountIdLowerInt = 1, - SessionKey = "telegram_is_@BL4D3_BR34D" + AccountIdHigherInt = accountDocument.DocumentId.HigherInt, + AccountIdLowerInt = accountDocument.DocumentId.LowerInt, + SessionKey = accountDocument.SessionKey }); } [MessageHandler(10102)] public async Task OnLoginUsingSession(LoginUsingSessionMessage loginUsingSessionMessage) { - _logger.LogInformation("Logging account! sessionkey: {sk} | GameCenterId: {gcid} | GameVersion: {cgv} | account: ({h}-{l})", - loginUsingSessionMessage.SessionKey, - loginUsingSessionMessage.GamecenterId, - loginUsingSessionMessage.ClientGameVersion, - loginUsingSessionMessage.AccountIdHigherInt, - loginUsingSessionMessage.AccountIdLowerInt); + if (_documentManager.Id.LowerInt == 0) + _documentManager.SetDocumentId(new (loginUsingSessionMessage.AccountIdHigherInt, loginUsingSessionMessage.AccountIdLowerInt)); + + await _documentManager.EnsureAccountDocument(); + + AccountDocument? accountDocument = _documentManager.AccountDocument; + if (accountDocument == null) + { + await _connection.SendMessage(new LoginFailedMessage() + { + ErrorCode = 2 + }); + + return; + } + else if (accountDocument.SessionKey != loginUsingSessionMessage.SessionKey) + { + await _connection.SendMessage(new LoginFailedMessage() + { + ErrorCode = 5 + }); + + return; + } await _connection.SendMessage(new LoginOkMessage() { - PrimaryAvatarIdHigherInt = loginUsingSessionMessage.AccountIdHigherInt, - PrimaryAvatarIdLowerInt = loginUsingSessionMessage.AccountIdLowerInt, + PrimaryAvatarIdHigherInt = accountDocument.DocumentId.HigherInt, + PrimaryAvatarIdLowerInt = accountDocument.DocumentId.LowerInt, Fingerprint = "fece25cfc941db1a5bb3e79d4e6a60c34659f49e" }); diff --git a/Supercell.GUT.Server/Protocol/Handlers/AvatarMessageHandler.cs b/Supercell.GUT.Server/Protocol/Handlers/AvatarMessageHandler.cs index b5fcdd9..a837036 100644 --- a/Supercell.GUT.Server/Protocol/Handlers/AvatarMessageHandler.cs +++ b/Supercell.GUT.Server/Protocol/Handlers/AvatarMessageHandler.cs @@ -1,9 +1,9 @@ using Microsoft.Extensions.Logging; using Supercell.GUT.Logic.Avatar; using Supercell.GUT.Logic.Message.Avatar; +using Supercell.GUT.Server.Document; using Supercell.GUT.Server.Network.Connection; using Supercell.GUT.Server.Protocol.Attributes; -using Supercell.GUT.Titan.Logic.Math; namespace Supercell.GUT.Server.Protocol.Handlers; @@ -12,31 +12,52 @@ internal class AvatarMessageHandler : MessageHandlerBase { private readonly ILogger _logger; private readonly ClientConnection _connection; + private readonly DocumentManager _documentManager; - public AvatarMessageHandler(ClientConnection connection, ILogger logger) + public AvatarMessageHandler(ClientConnection connection, ILogger logger, DocumentManager documentManager) { _logger = logger; _connection = connection; + _documentManager = documentManager; } [MessageHandler(10201)] public async Task OnSelectAvatar(SelectAvatarMessage selectAvatarMessage) { - _logger.LogInformation("selecting avatar! avatar: ({h}-{l})", - selectAvatarMessage.AvatarIdHigherInt, - selectAvatarMessage.AvatarIdLowerInt); + await _documentManager.EnsureAvatarDocument(); + LogicClientAvatar logicClientAvatar = _documentManager.AvatarDocument!.LogicClientAvatar; + + if (logicClientAvatar.Id.LowerInt == 0) + { + _documentManager.AvatarDocument.SetId(_documentManager.AvatarDocument.DocumentId); + await _documentManager.SaveAsync(); + } + + _connection.LogicClientAvatar = logicClientAvatar; await _connection.SendMessage(new AvatarDataMessage() { - LogicClientAvatar = new LogicClientAvatar() - { - Id = new LogicLong(selectAvatarMessage.AvatarIdHigherInt, selectAvatarMessage.AvatarIdLowerInt), - - Name = "BreadDEV", - AvatarCode = "a1", - - TutorialFlags = 999999 - } + LogicClientAvatar = _connection.LogicClientAvatar }); } + + [MessageHandler(10210)] + public async Task OnTutorialProgressUpdate(TutorialProgressUpdateMessage tutorialProgressUpdateMessage) + { + _connection.LogicClientAvatar!.SetTutorialFlags(tutorialProgressUpdateMessage.TutorialFlags); + + await _documentManager.SaveAsync(); + } + + [MessageHandler(10504)] + public async Task OnAskForFriendList(AskForFriendListMessage askForFriendListMessage) + { + await _connection.SendMessage(new FriendListMessage()); + } + + [MessageHandler(10503)] + public async Task OnAskForAddableFriends(AskForAddableFriendsMessage askForAddableFriendsMessage) + { + await _connection.SendMessage(new AddableFriendsMessage()); + } } diff --git a/Supercell.GUT.Server/Supercell.GUT.Server.csproj b/Supercell.GUT.Server/Supercell.GUT.Server.csproj index ba2b1a8..edb075f 100644 --- a/Supercell.GUT.Server/Supercell.GUT.Server.csproj +++ b/Supercell.GUT.Server/Supercell.GUT.Server.csproj @@ -9,6 +9,7 @@ + diff --git a/Supercell.GUT.Server/Util/TimeUtil.cs b/Supercell.GUT.Server/Util/TimeUtil.cs new file mode 100644 index 0000000..2cf97a7 --- /dev/null +++ b/Supercell.GUT.Server/Util/TimeUtil.cs @@ -0,0 +1,11 @@ +namespace Supercell.GUT.Server.Util; + +public static class TimeUtil +{ + private static readonly DateTime s_unixTime = new(1970, 1, 1); + + public static int GetCurrentTimestamp() + { + return (int)DateTime.UtcNow.Subtract(s_unixTime).TotalSeconds; + } +} diff --git a/Supercell.GUT.Server/appsettings.json b/Supercell.GUT.Server/appsettings.json index ceed59b..d0bb453 100644 --- a/Supercell.GUT.Server/appsettings.json +++ b/Supercell.GUT.Server/appsettings.json @@ -2,5 +2,9 @@ "Gateway": { "Host": "0.0.0.0", "Port": 9339 + }, + "Database": { + "MongoConnectionString": "mongodb://127.0.0.1:27017", + "DatabaseName": "gut" } } \ No newline at end of file diff --git a/Supercell.GUT.Titan/Logic/Math/LogicLong.cs b/Supercell.GUT.Titan/Logic/Math/LogicLong.cs index 6905aa2..19eda1d 100644 --- a/Supercell.GUT.Titan/Logic/Math/LogicLong.cs +++ b/Supercell.GUT.Titan/Logic/Math/LogicLong.cs @@ -36,4 +36,12 @@ public class LogicLong this.HigherInt = byteStream.ReadInt(); this.LowerInt = byteStream.ReadInt(); } + + public override bool Equals(object? obj) + { + if (obj is LogicLong logicLong) + return logicLong.HigherInt == this.HigherInt && logicLong.LowerInt == this.LowerInt; + + return false; + } }