From 1b6c07a6af4aea07b90a27a219dc22e4e7e2e92d Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sun, 3 Nov 2024 11:43:25 -0500 Subject: [PATCH 01/12] research: Quest Failures Add quest database interactions to transactions to prevent slowness when the database is being accessed by multiple users. --- Arrowgene.Ddon.Database/IDatabase.cs | 39 +++-- .../Sql/Core/DdonSqlCompletedQuests.cs | 95 +++++------ Arrowgene.Ddon.Database/Sql/Core/DdonSqlDb.cs | 28 ++++ .../Sql/Core/DdonSqlDbCharacter.cs | 2 +- .../Sql/Core/DdonSqlDbCharacterJobData.cs | 25 +-- .../Core/DdonSqlDbCharacterPlayPointData.cs | 37 ++--- .../Sql/Core/DdonSqlDbQuestReward.cs | 78 ++++----- .../Sql/Core/DdonSqlPriorityQuests.cs | 53 +++--- .../Sql/Core/DdonSqlQuestProgress.cs | 154 ++++++++---------- .../Characters/ExpManager.cs | 9 +- .../Characters/PlayPointManager.cs | 5 +- .../Characters/RewardManager.cs | 16 +- .../Handler/InstanceEnemyKillHandler.cs | 1 + .../Handler/QuestQuestProgressHandler.cs | 71 ++++---- .../Quests/QuestStateManager.cs | 75 +++++---- .../Database/DatabaseMigratorTest.cs | 36 ++-- 16 files changed, 343 insertions(+), 381 deletions(-) diff --git a/Arrowgene.Ddon.Database/IDatabase.cs b/Arrowgene.Ddon.Database/IDatabase.cs index 8bc51669a..5466d5979 100644 --- a/Arrowgene.Ddon.Database/IDatabase.cs +++ b/Arrowgene.Ddon.Database/IDatabase.cs @@ -143,7 +143,7 @@ CDataPawnSearchParameter searchParams // CharacterJobData bool ReplaceCharacterJobData(uint commonId, CDataCharacterJobData replacedCharacterJobData, DbConnection? connectionIn = null); - bool UpdateCharacterJobData(uint commonId, CDataCharacterJobData updatedCharacterJobData); + bool UpdateCharacterJobData(uint commonId, CDataCharacterJobData updatedCharacterJobData, DbConnection? connectionIn = null); // Wallet Points bool InsertWalletPoint(uint characterId, CDataWalletPoint walletPoint); @@ -414,37 +414,39 @@ uint excludedCharacterId ); // Rewards - bool InsertBoxRewardItems(uint commonId, QuestBoxRewards rewards); - bool DeleteBoxRewardItem(uint commonId, uint uniqId); - List SelectBoxRewardItems(uint commonId); + bool InsertBoxRewardItems(uint commonId, QuestBoxRewards rewards, DbConnection? connectionIn = null); + bool DeleteBoxRewardItem(uint commonId, uint uniqId, DbConnection? connectionIn = null); + List SelectBoxRewardItems(uint commonId, DbConnection? connectionIn = null); // Completed Quests - List GetCompletedQuestsByType(uint characterCommonId, QuestType questType); - CompletedQuest GetCompletedQuestsById(uint characterCommonId, QuestId questId); + List GetCompletedQuestsByType(uint characterCommonId, QuestType questType, DbConnection? connectionIn = null); + CompletedQuest GetCompletedQuestsById(uint characterCommonId, QuestId questId, DbConnection? connectionIn = null); bool InsertIfNotExistCompletedQuest( uint characterCommonId, QuestId questId, - QuestType questType + QuestType questType, + DbConnection? connectionIn = null ); bool ReplaceCompletedQuest( uint characterCommonId, QuestId questId, QuestType questType, - uint count = 1 + uint count = 1, + DbConnection? connectionIn = null ); // Quest Progress - bool InsertQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType, uint step); - bool UpdateQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType, uint step); - bool RemoveQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType); - List GetQuestProgressByType(uint characterCommonId, QuestType questType); - QuestProgress GetQuestProgressByScheduleId(uint characterCommonId, uint questScheduleId); + bool InsertQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType, uint step, DbConnection? connectionIn = null); + bool UpdateQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType, uint step, DbConnection? connectionIn = null); + bool RemoveQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType, DbConnection? connectionIn = null); + List GetQuestProgressByType(uint characterCommonId, QuestType questType, DbConnection? connectionIn = null); + QuestProgress GetQuestProgressByScheduleId(uint characterCommonId, uint questScheduleId, DbConnection? connectionIn = null); // Quest Priority - bool InsertPriorityQuest(uint characterCommonId, uint questScheduleId); - List GetPriorityQuestScheduleIds(uint characterCommonId); - bool DeletePriorityQuest(uint characterCommonId, uint questScheduleId); + bool InsertPriorityQuest(uint characterCommonId, uint questScheduleId, DbConnection? connectionIn = null); + List GetPriorityQuestScheduleIds(uint characterCommonId, DbConnection? connectionIn = null); + bool DeletePriorityQuest(uint characterCommonId, uint questScheduleId, DbConnection? connectionIn = null); // System mail long InsertSystemMailAttachment(SystemMailAttachment attachment); @@ -495,9 +497,10 @@ ushort addStat2 // Play points bool ReplaceCharacterPlayPointData( uint id, - CDataJobPlayPoint updatedCharacterPlayPointData + CDataJobPlayPoint updatedCharacterPlayPointData, + DbConnection? connectionIn = null ); - bool UpdateCharacterPlayPointData(uint id, CDataJobPlayPoint updatedCharacterPlayPointData); + bool UpdateCharacterPlayPointData(uint id, CDataJobPlayPoint updatedCharacterPlayPointData, DbConnection? connectionIn = null); // Stamps public bool InsertCharacterStampData(uint id, CharacterStampBonus stampData); diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlCompletedQuests.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlCompletedQuests.cs index 5a9802955..fae5a0347 100644 --- a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlCompletedQuests.cs +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlCompletedQuests.cs @@ -23,19 +23,12 @@ public abstract partial class DdonSqlDb : SqlDb GetCompletedQuestsByType(uint characterCommonId, QuestType questType) + public List GetCompletedQuestsByType(uint characterCommonId, QuestType questType, DbConnection? connectionIn = null) { - using TCon connection = OpenNewConnection(); - return GetCompletedQuestsByType(connection, characterCommonId, questType); - } - - public List GetCompletedQuestsByType(TCon connection, uint characterCommonId, QuestType questType) - { - List results = new List(); - - ExecuteInTransaction(conn => + return ExecuteQuerySafe>(connectionIn, (connection) => { - ExecuteReader(conn, SqlSelectCompletedQuestByType, + List results = new List(); + ExecuteReader(connection, SqlSelectCompletedQuestByType, command => { AddParameter(command, "@character_common_id", characterCommonId); AddParameter(command, "@quest_type", (uint)questType); @@ -45,90 +38,76 @@ public List GetCompletedQuestsByType(TCon connection, uint chara results.Add(new CompletedQuest() { - QuestId = (QuestId) GetUInt32(reader, "quest_id"), + QuestId = (QuestId)GetUInt32(reader, "quest_id"), QuestType = questType, ClearCount = GetUInt32(reader, "clear_count") }); } }); + return results; }); - - return results; } - public CompletedQuest GetCompletedQuestsById(uint characterCommonId, QuestId questId) + public CompletedQuest GetCompletedQuestsById(uint characterCommonId, QuestId questId, DbConnection? connectionIn = null) { - using TCon connection = OpenNewConnection(); - return GetCompletedQuestsById(connection, characterCommonId, questId); - } - - public CompletedQuest GetCompletedQuestsById(TCon connection, uint characterCommonId, QuestId questId) - { - CompletedQuest result = null; - - ExecuteInTransaction(conn => + return ExecuteQuerySafe(connectionIn, (connection) => { - ExecuteReader(conn, SqlSelectCompletedQuestById, + CompletedQuest result = null; + ExecuteReader(connection, SqlSelectCompletedQuestById, command => { AddParameter(command, "@character_common_id", characterCommonId); - AddParameter(command, "@quest_id", (uint) questId); + AddParameter(command, "@quest_id", (uint)questId); }, reader => { if (reader.Read()) { result = new CompletedQuest() { QuestId = questId, - QuestType = (QuestType) GetUInt32(reader, "quest_type"), + QuestType = (QuestType)GetUInt32(reader, "quest_type"), ClearCount = GetUInt32(reader, "clear_count") }; } }); + return result; }); - - return result; - } - - public bool InsertIfNotExistCompletedQuest(uint characterCommonId, QuestId questId, QuestType questType) - { - using TCon connection = OpenNewConnection(); - return InsertIfNotExistCompletedQuest(connection, characterCommonId, questId, questType); } - public bool InsertIfNotExistCompletedQuest(TCon connection, uint characterCommonId, QuestId questId, QuestType questType) + public bool InsertIfNotExistCompletedQuest(uint characterCommonId, QuestId questId, QuestType questType, DbConnection? connectionIn = null) { - return ExecuteNonQuery(connection, SqlInsertIfNotExistCompletedQuestId, command => + return ExecuteQuerySafe(connectionIn, (connection) => { - AddParameter(command, "character_common_id", characterCommonId); - AddParameter(command, "quest_id", (uint) questId); - AddParameter(command, "quest_type", (uint) questType); - AddParameter(command, "clear_count", 1); - }) == 1; + return ExecuteNonQuery(connection, SqlInsertIfNotExistCompletedQuestId, command => + { + AddParameter(command, "character_common_id", characterCommonId); + AddParameter(command, "quest_id", (uint)questId); + AddParameter(command, "quest_type", (uint)questType); + AddParameter(command, "clear_count", 1); + }) == 1; + }); } - public bool ReplaceCompletedQuest(uint characterCommonId, QuestId questId, QuestType questType, uint count = 1) + public bool ReplaceCompletedQuest(uint characterCommonId, QuestId questId, QuestType questType, uint count = 1, DbConnection? connectionIn = null) { - using TCon connection = OpenNewConnection(); - return ReplaceCompletedQuest(connection, characterCommonId, questId, questType, count); - } - - public bool ReplaceCompletedQuest(TCon connection, uint characterCommonId, QuestId questId, QuestType questType, uint count = 1) - { - if (!InsertIfNotExistCompletedQuest(connection, characterCommonId, questId, questType)) + + if (!InsertIfNotExistCompletedQuest(characterCommonId, questId, questType, connectionIn)) { - return UpdateCompletedQuest(connection, characterCommonId, questId, questType, count); + return UpdateCompletedQuest(characterCommonId, questId, questType, count, connectionIn); } return true; } - private bool UpdateCompletedQuest(TCon connection, uint characterCommonId, QuestId questId, QuestType questType, uint count = 1) + private bool UpdateCompletedQuest(uint characterCommonId, QuestId questId, QuestType questType, uint count = 1, DbConnection? connectionIn = null) { - return ExecuteNonQuery(connection, SqlUpdateCompletedQuestId, command => + return ExecuteQuerySafe(connectionIn, (connection) => { - AddParameter(command, "character_common_id", characterCommonId); - AddParameter(command, "quest_id", (uint)questId); - AddParameter(command, "quest_type", (uint)questType); - AddParameter(command, "clear_count", count); - }) == 1; + return ExecuteNonQuery(connection, SqlUpdateCompletedQuestId, command => + { + AddParameter(command, "character_common_id", characterCommonId); + AddParameter(command, "quest_id", (uint)questId); + AddParameter(command, "quest_type", (uint)questType); + AddParameter(command, "clear_count", count); + }) == 1; + }); } } } diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDb.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDb.cs index 46f5d33e7..278061d1d 100644 --- a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDb.cs +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDb.cs @@ -131,6 +131,34 @@ public void ExecuteReader(DbConnection conn, string sql, Action comma base.ExecuteReader((TCon) conn, sql, (command) => commandAction.Invoke((TCom) command), (reader) => readAction.Invoke((TReader) reader)); } + public T ExecuteQuerySafe(DbConnection? connectionIn, Func work) + { + bool isTransaction = connectionIn is not null; + TCon connection = (TCon)(connectionIn ?? OpenNewConnection()); + try + { + return work.Invoke(connection); + } + finally + { + if (!isTransaction) connection.Dispose(); + } + } + + public void ExecuteQuerySafe(DbConnection? connectionIn, Action work) + { + bool isTransaction = connectionIn is not null; + TCon connection = (TCon)(connectionIn ?? OpenNewConnection()); + try + { + work.Invoke(connection); + } + finally + { + if (!isTransaction) connection.Dispose(); + } + } + public bool MigrateDatabase(DatabaseMigrator migrator, uint toVersion) { uint currentVersion = GetMeta().DatabaseVersion; diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbCharacter.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbCharacter.cs index 9afbf3ece..97e2b8876 100644 --- a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbCharacter.cs +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbCharacter.cs @@ -476,7 +476,7 @@ private void StoreCharacterData(TCon conn, Character character) foreach(CDataJobPlayPoint playPoint in character.PlayPointList) { - ReplaceCharacterPlayPointData(conn, character.ContentCharacterId, playPoint); + ReplaceCharacterPlayPointData(character.ContentCharacterId, playPoint, conn); } ExecuteNonQuery(conn, SqlInsertCharacterStamp, (Action)(command => diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbCharacterJobData.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbCharacterJobData.cs index 3f8d24c48..1aca392ca 100644 --- a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbCharacterJobData.cs +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbCharacterJobData.cs @@ -39,22 +39,16 @@ public abstract partial class DdonSqlDb : SqlDb(connectionIn, (connection) => { Logger.Debug("Inserting character job data."); if (!InsertIfNotExistsCharacterJobData(connection, commonId, replacedCharacterJobData)) { Logger.Debug("Character job data already exists, replacing."); - return UpdateCharacterJobData(connection, commonId, replacedCharacterJobData); + return UpdateCharacterJobData(commonId, replacedCharacterJobData, connection); } return true; - } - finally - { - if (!isTransaction) connection.Dispose(); - } + }); } public bool InsertCharacterJobData(uint commonId, CDataCharacterJobData updatedCharacterJobData) @@ -79,15 +73,12 @@ public bool InsertIfNotExistsCharacterJobData(TCon connection, uint commonId, CD return ExecuteNonQuery(connection, SqlInsertIfNotExistsCharacterJobData, command => { AddParameter(command, commonId, updatedCharacterJobData); }) == 1; } - public bool UpdateCharacterJobData(uint commonId, CDataCharacterJobData updatedCharacterJobData) - { - using TCon connection = OpenNewConnection(); - return UpdateCharacterJobData(connection, commonId, updatedCharacterJobData); - } - - public bool UpdateCharacterJobData(TCon connection, uint commonId, CDataCharacterJobData updatedCharacterJobData) + public bool UpdateCharacterJobData(uint commonId, CDataCharacterJobData updatedCharacterJobData, DbConnection? connectionIn = null) { - return ExecuteNonQuery(connection, SqlUpdateCharacterJobData, command => { AddParameter(command, commonId, updatedCharacterJobData); }) == 1; + return ExecuteQuerySafe(connectionIn, (connection) => + { + return ExecuteNonQuery(connection, SqlUpdateCharacterJobData, command => { AddParameter(command, commonId, updatedCharacterJobData); }) == 1; + }); } private CDataCharacterJobData ReadCharacterJobData(DbDataReader reader) diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbCharacterPlayPointData.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbCharacterPlayPointData.cs index bbc73523a..66ca01ced 100644 --- a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbCharacterPlayPointData.cs +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbCharacterPlayPointData.cs @@ -1,6 +1,7 @@ using Arrowgene.Ddon.Shared.Entity.Structure; using Arrowgene.Ddon.Shared.Model; using System.Data.Common; +using System.Reflection.Metadata.Ecma335; namespace Arrowgene.Ddon.Database.Sql.Core { @@ -31,21 +32,18 @@ public abstract partial class DdonSqlDb : SqlDb(connectionIn, (connection) => { - Logger.Debug("Character playpoint data already exists, replacing."); - return UpdateCharacterPlayPointData(connection, id, replacedCharacterPlayPointData); - } - return true; + Logger.Debug("Inserting character playpoint data."); + if (!InsertIfNotExistsCharacterPlayPointData(connection, id, replacedCharacterPlayPointData)) + { + Logger.Debug("Character playpoint data already exists, replacing."); + return UpdateCharacterPlayPointData(id, replacedCharacterPlayPointData, connection); + } + return true; + }); } public bool InsertCharacterPlayPointData(uint id, CDataJobPlayPoint updatedCharacterPlayPointData) @@ -70,15 +68,12 @@ public bool InsertIfNotExistsCharacterPlayPointData(TCon connection, uint id, CD return ExecuteNonQuery(connection, SqlInsertIfNotExistsCharacterPlayPointData, command => { AddParameter(command, id, updatedCharacterPlayPointData); }) == 1; } - public bool UpdateCharacterPlayPointData(uint id, CDataJobPlayPoint updatedCharacterPlayPointData) + public bool UpdateCharacterPlayPointData(uint id, CDataJobPlayPoint updatedCharacterPlayPointData, DbConnection? connectionIn = null) { - using TCon connection = OpenNewConnection(); - return UpdateCharacterPlayPointData(connection, id, updatedCharacterPlayPointData); - } - - public bool UpdateCharacterPlayPointData(TCon connection, uint id, CDataJobPlayPoint updatedCharacterPlayPointData) - { - return ExecuteNonQuery(connection, SqlUpdateCharacterPlayPointData, command => { AddParameter(command, id, updatedCharacterPlayPointData); }) == 1; + return ExecuteQuerySafe(connectionIn, (connection) => + { + return ExecuteNonQuery(connection, SqlUpdateCharacterPlayPointData, command => { AddParameter(command, id, updatedCharacterPlayPointData); }) == 1; + }); } private void AddParameter(TCom command, uint id, CDataJobPlayPoint characterPlayPointData) diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbQuestReward.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbQuestReward.cs index 31446ec7e..54488cc2f 100644 --- a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbQuestReward.cs +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbQuestReward.cs @@ -26,46 +26,36 @@ public abstract partial class DdonSqlDb : SqlDb + return ExecuteQuerySafe(connectionIn, (connection) => { - AddParameter(command, "character_common_id", commonId); - AddParameter(command, "quest_schedule_id", rewards.QuestScheduleId); - AddParameter(command, "num_random_rewards", rewards.NumRandomRewards); - - int i; - for(i = 0; i < rewards.NumRandomRewards; i++) + return ExecuteNonQuery(connection, SqlInsertRewardBoxItems, command => { - AddParameter(command, $"random_reward{i}_index", rewards.RandomRewardIndices[i]); - } - - for (; i < MAX_RANDOM_REWARDS; i++) - { - AddParameter(command, $"random_reward{i}_index", 0); - } - }, out long autoIncrement) == 1; - } - - public List SelectBoxRewardItems(uint commonId) - { - using TCon connection = OpenNewConnection(); - return SelectBoxRewardItems(connection, commonId); + AddParameter(command, "character_common_id", commonId); + AddParameter(command, "quest_schedule_id", rewards.QuestScheduleId); + AddParameter(command, "num_random_rewards", rewards.NumRandomRewards); + + int i; + for (i = 0; i < rewards.NumRandomRewards; i++) + { + AddParameter(command, $"random_reward{i}_index", rewards.RandomRewardIndices[i]); + } + + for (; i < MAX_RANDOM_REWARDS; i++) + { + AddParameter(command, $"random_reward{i}_index", 0); + } + }, out long autoIncrement) == 1; + }); } - public List SelectBoxRewardItems(TCon conn, uint commonId) + public List SelectBoxRewardItems(uint commonId, DbConnection? connectionIn = null) { - List results = new List(); - - ExecuteInTransaction(conn => + return ExecuteQuerySafe>(connectionIn, (connection) => { - ExecuteReader(conn, SqlSelectRewardBoxItems, + List results = new List(); + ExecuteReader(connection, SqlSelectRewardBoxItems, command => { AddParameter(command, "@character_common_id", commonId); }, reader => { @@ -75,24 +65,20 @@ public List SelectBoxRewardItems(TCon conn, uint commonId) results.Add(result); } }); + return results; }); - - return results; - } - - public bool DeleteBoxRewardItem(uint commonId, uint uniqId) - { - using TCon connection = OpenNewConnection(); - return DeleteBoxRewardItem(connection, commonId, uniqId); } - public bool DeleteBoxRewardItem(TCon conn, uint commonId, uint uniqId) + public bool DeleteBoxRewardItem(uint commonId, uint uniqId, DbConnection? connectionIn = null) { - return ExecuteNonQuery(conn, SqlDeleteRewardBoxItem, command => + return ExecuteQuerySafe(connectionIn, (connection) => { - AddParameter(command, "@character_common_id", commonId); - AddParameter(command, "@uniq_reward_id", uniqId); - }) == 1; + return ExecuteNonQuery(connection, SqlDeleteRewardBoxItem, command => + { + AddParameter(command, "@character_common_id", commonId); + AddParameter(command, "@uniq_reward_id", uniqId); + }) == 1; + }); } private QuestBoxRewards ReadDatabaseQuestBoxReward(TReader reader) diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlPriorityQuests.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlPriorityQuests.cs index 1f1af624c..2ca49b4c3 100644 --- a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlPriorityQuests.cs +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlPriorityQuests.cs @@ -29,32 +29,24 @@ public abstract partial class DdonSqlDb : SqlDb + return ExecuteQuerySafe(connectionIn, (connection) => { - AddParameter(command, "character_common_id", characterCommonId); - AddParameter(command, "quest_schedule_id", questScheduleId); - }) == 1; + return ExecuteNonQuery(connection, SqlInsertIfNotExistPriorityQuestId, command => + { + AddParameter(command, "character_common_id", characterCommonId); + AddParameter(command, "quest_schedule_id", questScheduleId); + }) == 1; + }); } - public List GetPriorityQuestScheduleIds(uint characterCommonId) - { - using TCon connection = OpenNewConnection(); - return GetPriorityQuests(connection, characterCommonId); - } - public List GetPriorityQuests(TCon connection, uint characterCommonId) + public List GetPriorityQuestScheduleIds(uint characterCommonId, DbConnection? connectionIn = null) { - List results = new List(); - ExecuteInTransaction(conn => + return ExecuteQuerySafe>(connectionIn, (connection) => { - ExecuteReader(conn, SqlSelectPriorityQuests, + List results = new List(); + ExecuteReader(connection, SqlSelectPriorityQuests, command => { AddParameter(command, "@character_common_id", characterCommonId); }, reader => { @@ -63,23 +55,20 @@ public List GetPriorityQuests(TCon connection, uint characterCommonId) results.Add(GetUInt32(reader, "quest_schedule_id")); } }); + return results; }); - return results; } - public bool DeletePriorityQuest(uint characterCommonId, uint questScheduleId) + public bool DeletePriorityQuest(uint characterCommonId, uint questScheduleId, DbConnection? connectionIn = null) { - using TCon connection = OpenNewConnection(); - return DeletePriorityQuest(connection, characterCommonId, questScheduleId); - } - - public bool DeletePriorityQuest(TCon connection, uint characterCommonId, uint questScheduleId) - { - return ExecuteNonQuery(connection, SqlDeletePriorityQuest, command => + return ExecuteQuerySafe(connectionIn, (connection) => { - AddParameter(command, "@character_common_id", characterCommonId); - AddParameter(command, "quest_schedule_id", questScheduleId); - }) == 1; + return ExecuteNonQuery(connection, SqlDeletePriorityQuest, command => + { + AddParameter(command, "@character_common_id", characterCommonId); + AddParameter(command, "quest_schedule_id", questScheduleId); + }) == 1; + }); } } } diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlQuestProgress.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlQuestProgress.cs index 24aceba43..b500616d7 100644 --- a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlQuestProgress.cs +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlQuestProgress.cs @@ -22,44 +22,44 @@ public abstract partial class DdonSqlDb : SqlDb GetQuestProgressByType(uint characterCommonId, QuestType questType) + public List GetQuestProgressByType(uint characterCommonId, QuestType questType, DbConnection? connectionIn = null) { - using TCon connection = OpenNewConnection(); - if (questType == QuestType.All) + return ExecuteQuerySafe>(connectionIn, (connection) => { - return GetAllQuestProgress(connection, characterCommonId); - } - else - { - return GetQuestProgressByType(connection, characterCommonId, questType); - } + if (questType == QuestType.All) + { + return GetAllQuestProgress(connection, characterCommonId); + } + else + { + return GetQuestProgressByType(connection, characterCommonId, questType); + } + }); } - public List GetQuestProgressByType(TCon connection, uint characterCommonId, QuestType questType) + private List GetQuestProgressByType(TCon connection, uint characterCommonId, QuestType questType) { List results = new List(); - ExecuteInTransaction(conn => - { - ExecuteReader(conn, SqlSelectQuestProgressByType, - command => - { - AddParameter(command, "@character_common_id", characterCommonId); - AddParameter(command, "@quest_type", (uint)questType); - }, reader => + ExecuteReader(connection, SqlSelectQuestProgressByType, + command => + { + AddParameter(command, "@character_common_id", characterCommonId); + AddParameter(command, "@quest_type", (uint)questType); + }, reader => + { + while (reader.Read()) { - while (reader.Read()) - { - var result = ReadQuestProgress(reader); - results.Add(result); - } - }); - }); + var result = ReadQuestProgress(reader); + results.Add(result); + } + } + ); return results; } @@ -67,39 +67,27 @@ public List GetQuestProgressByType(TCon connection, uint characte private List GetAllQuestProgress(TCon connection, uint characterCommonId) { List results = new List(); - - ExecuteInTransaction(conn => - { - ExecuteReader(conn, SqlSelectAllQuestProgress, - command => - { - AddParameter(command, "@character_common_id", characterCommonId); - }, reader => + ExecuteReader(connection, SqlSelectAllQuestProgress, + command => + { + AddParameter(command, "@character_common_id", characterCommonId); + }, reader => + { + while (reader.Read()) { - while (reader.Read()) - { - var result = ReadQuestProgress(reader); - results.Add(result); - } - }); - }); - + var result = ReadQuestProgress(reader); + results.Add(result); + } + }); return results; } - - public QuestProgress GetQuestProgressByScheduleId(uint characterCommonId, uint questScheduleId) - { - using TCon connection = OpenNewConnection(); - return GetQuestProgressById(connection, characterCommonId, questScheduleId); - } - - public QuestProgress GetQuestProgressById(TCon connection, uint characterCommonId, uint questScheduleId) + public QuestProgress GetQuestProgressByScheduleId(uint characterCommonId, uint questScheduleId, DbConnection? connectionIn = null) { - QuestProgress result = null; - ExecuteInTransaction(connection => + return ExecuteQuerySafe(connectionIn, (connection) => { - ExecuteReader(connection, SqlSelectQuestProgressById, + QuestProgress result = null; + ExecuteReader(connection, SqlSelectQuestProgressByScheduleId, command => { AddParameter(command, "@character_common_id", characterCommonId); @@ -111,35 +99,29 @@ public QuestProgress GetQuestProgressById(TCon connection, uint characterCommonI { result = ReadQuestProgress(reader); } - }); + } + ); + return result; }); - return result; } - public bool RemoveQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType) + public bool RemoveQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType, DbConnection? connectionIn = null) { - using TCon connection = OpenNewConnection(); - return RemoveQuestProgress(connection, characterCommonId, questScheduleId, questType); - } - - public bool RemoveQuestProgress(TCon connection, uint characterCommonId, uint questScheduleId, QuestType questType) - { - return ExecuteNonQuery(connection, SqlDeleteQuestProgress, command => + return ExecuteQuerySafe(connectionIn, (connection) => { - AddParameter(command, "character_common_id", characterCommonId); - AddParameter(command, "quest_type", (uint)questType); - AddParameter(command, "quest_schedule_id", questScheduleId); - }) == 1; - } - - public bool InsertQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType, uint step) - { - using TCon connection = OpenNewConnection(); - return InsertQuestProgress(connection, characterCommonId, questScheduleId, questType, step); + return ExecuteNonQuery(connection, SqlDeleteQuestProgress, command => + { + AddParameter(command, "character_common_id", characterCommonId); + AddParameter(command, "quest_type", (uint)questType); + AddParameter(command, "quest_schedule_id", questScheduleId); + }) == 1; + }); } - public bool InsertQuestProgress(TCon connection, uint characterCommonId, uint questScheduleId, QuestType questType, uint step) + public bool InsertQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType, uint step, DbConnection? connectionIn = null) { + return ExecuteQuerySafe(connectionIn, (connection) => + { return ExecuteNonQuery(connection, SqlInsertQuestProgress, command => { AddParameter(command, "character_common_id", characterCommonId); @@ -147,23 +129,21 @@ public bool InsertQuestProgress(TCon connection, uint characterCommonId, uint qu AddParameter(command, "quest_type", (uint)questType); AddParameter(command, "step", (uint)step); }) == 1; + }); } - public bool UpdateQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType, uint step) - { - using TCon connection = OpenNewConnection(); - return UpdateQuestProgress(connection, characterCommonId, questScheduleId, questType, step); - } - - public bool UpdateQuestProgress(TCon connection, uint characterCommonId, uint questScheduleId, QuestType questType, uint step) + public bool UpdateQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType, uint step, DbConnection? connectionIn = null) { - return ExecuteNonQuery(connection, SqlUpdateQuestProgress, command => + return ExecuteQuerySafe(connectionIn, (connection) => { - AddParameter(command, "character_common_id", characterCommonId); - AddParameter(command, "quest_schedule_id", questScheduleId); - AddParameter(command, "quest_type", (uint)questType); - AddParameter(command, "step", (uint)step); - }) == 1; ; + return ExecuteNonQuery(connection, SqlUpdateQuestProgress, command => + { + AddParameter(command, "character_common_id", characterCommonId); + AddParameter(command, "quest_schedule_id", questScheduleId); + AddParameter(command, "quest_type", (uint)questType); + AddParameter(command, "step", (uint)step); + }) == 1; + }); } private QuestProgress ReadQuestProgress(TReader reader) diff --git a/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs b/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs index 5833a2af4..7d8f9e362 100644 --- a/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs @@ -14,6 +14,7 @@ using System.IO; using System.Text; using System.Buffers.Text; +using System.Data.Common; namespace Arrowgene.Ddon.GameServer.Characters { @@ -458,7 +459,7 @@ public static CDataCharacterJobData CalculateBaseStats(CharacterCommon Character return JobData; } - public void AddExp(GameClient client, CharacterCommon characterToAddExpTo, uint gainedExp, RewardSource rewardType, QuestType questType = QuestType.All) + public void AddExp(GameClient client, CharacterCommon characterToAddExpTo, uint gainedExp, RewardSource rewardType, QuestType questType = QuestType.All, DbConnection? connectionIn = null) { var lvCap = (client.GameMode == GameMode.Normal) ? _Server.Setting.GameLogicSetting.JobLevelMax @@ -564,11 +565,11 @@ public void AddExp(GameClient client, CharacterCommon characterToAddExpTo, uint } // PERSIST CHANGES IN DB - _Server.Database.UpdateCharacterJobData(characterToAddExpTo.CommonId, activeCharacterJobData); + _Server.Database.UpdateCharacterJobData(characterToAddExpTo.CommonId, activeCharacterJobData, connectionIn); } } - public void AddJp(GameClient client, CharacterCommon characterToJpExpTo, uint gainedJp, RewardSource rewardType, QuestType questType = QuestType.All) + public void AddJp(GameClient client, CharacterCommon characterToJpExpTo, uint gainedJp, RewardSource rewardType, QuestType questType = QuestType.All, DbConnection? connectionIn = null) { CDataCharacterJobData? activeCharacterJobData = characterToJpExpTo.ActiveCharacterJobData; activeCharacterJobData.JobPoint += gainedJp; @@ -594,7 +595,7 @@ public void AddJp(GameClient client, CharacterCommon characterToJpExpTo, uint ga } // PERSIST CHANGES IN DB - _Server.Database.UpdateCharacterJobData(characterToJpExpTo.CommonId, activeCharacterJobData); + _Server.Database.UpdateCharacterJobData(characterToJpExpTo.CommonId, activeCharacterJobData, connectionIn); } public void ResetExpData(GameClient client, CharacterCommon characterCommon) diff --git a/Arrowgene.Ddon.GameServer/Characters/PlayPointManager.cs b/Arrowgene.Ddon.GameServer/Characters/PlayPointManager.cs index 8f3d9dba4..3fa6d0aa1 100644 --- a/Arrowgene.Ddon.GameServer/Characters/PlayPointManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/PlayPointManager.cs @@ -4,6 +4,7 @@ using Arrowgene.Ddon.Shared.Model; using Arrowgene.Logging; using System; +using System.Data.Common; using System.Linq; namespace Arrowgene.Ddon.GameServer.Characters @@ -25,7 +26,7 @@ public PlayPointManager(DdonGameServer server) private readonly DdonGameServer _Server; - public void AddPlayPoint(GameClient client, uint gainedPoints, JobId? job = null, byte type = 1) + public void AddPlayPoint(GameClient client, uint gainedPoints, JobId? job = null, byte type = 1, DbConnection? connectionIn = null) { CDataJobPlayPoint? targetPlayPoint; if (job is null) @@ -56,7 +57,7 @@ public void AddPlayPoint(GameClient client, uint gainedPoints, JobId? job = null client.Send(ppNtc); - _Server.Database.UpdateCharacterPlayPointData(client.Character.CharacterId, targetPlayPoint); + _Server.Database.UpdateCharacterPlayPointData(client.Character.CharacterId, targetPlayPoint, connectionIn); } } diff --git a/Arrowgene.Ddon.GameServer/Characters/RewardManager.cs b/Arrowgene.Ddon.GameServer/Characters/RewardManager.cs index 1e631ec43..9dd05f679 100644 --- a/Arrowgene.Ddon.GameServer/Characters/RewardManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/RewardManager.cs @@ -4,6 +4,8 @@ using System.Collections.Generic; using Arrowgene.Ddon.Shared.Model.Quest; using Arrowgene.Ddon.Shared.Model; +using Arrowgene.Ddon.Database.Model; +using System.Data.Common; namespace Arrowgene.Ddon.GameServer.Characters { @@ -19,27 +21,27 @@ public RewardManager(DdonGameServer server) _Server = server; } - public bool AddQuestRewards(GameClient client, Quest quest) + public bool AddQuestRewards(GameClient client, Quest quest, DbConnection? connectionIn = null) { var rewards = quest.GenerateBoxRewards(); - var currentRewards = GetQuestBoxRewards(client); + var currentRewards = GetQuestBoxRewards(client, connectionIn); if (currentRewards.Count >= MAX_REWARD_BOX_RESULTS) { return false; } - return _Server.Database.InsertBoxRewardItems(client.Character.CommonId, rewards); + return _Server.Database.InsertBoxRewardItems(client.Character.CommonId, rewards, connectionIn); } - public List GetQuestBoxRewards(GameClient client) + public List GetQuestBoxRewards(GameClient client, DbConnection? connectionIn = null) { - return _Server.Database.SelectBoxRewardItems(client.Character.CommonId); + return _Server.Database.SelectBoxRewardItems(client.Character.CommonId, connectionIn); } - public bool DeleteQuestBoxReward(GameClient client, uint uniqId) + public bool DeleteQuestBoxReward(GameClient client, uint uniqId, DbConnection? connectionIn = null) { - return _Server.Database.DeleteBoxRewardItem(client.Character.CommonId, uniqId); + return _Server.Database.DeleteBoxRewardItem(client.Character.CommonId, uniqId, connectionIn); } } } diff --git a/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs b/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs index 0c249ca8f..d3e3c5cef 100644 --- a/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs @@ -179,6 +179,7 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne gainedExp = 0; } + // TODO: Add transaction? playerMember.QuestState.HandleEnemyHuntRequests(enemyKilled); S2CItemUpdateCharacterItemNtc updateCharacterItemNtc = new S2CItemUpdateCharacterItemNtc(); diff --git a/Arrowgene.Ddon.GameServer/Handler/QuestQuestProgressHandler.cs b/Arrowgene.Ddon.GameServer/Handler/QuestQuestProgressHandler.cs index 276b3f4f5..b6792ebf7 100644 --- a/Arrowgene.Ddon.GameServer/Handler/QuestQuestProgressHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/QuestQuestProgressHandler.cs @@ -7,6 +7,7 @@ using Arrowgene.Ddon.Shared.Model.Quest; using Arrowgene.Ddon.Shared.Network; using Arrowgene.Logging; +using System.Data.Common; namespace Arrowgene.Ddon.GameServer.Handler { @@ -51,45 +52,47 @@ public override void Handle(GameClient client, StructurePacket { - foreach (var memberClient in client.Party.Clients) + if (questProgressState == QuestProgressState.Accepted && quest.QuestType == QuestType.World) { - var questProgress = Server.Database.GetQuestProgressByScheduleId(memberClient.Character.CommonId, questScheduleId); - - if (questProgress != null) + foreach (var memberClient in client.Party.Clients) { - continue; + var questProgress = Server.Database.GetQuestProgressByScheduleId(memberClient.Character.CommonId, questScheduleId, connection); + if (questProgress != null) + { + continue; + } + + // Add a new world quest record for the player + if (!Server.Database.InsertQuestProgress(memberClient.Character.CommonId, questScheduleId, quest.QuestType, 0, connection)) + { + Logger.Error($"Failed to insert progress for the quest {quest.QuestId}"); + } } - - // Add a new world quest record for the player - if (!Server.Database.InsertQuestProgress(memberClient.Character.CommonId, questScheduleId, quest.QuestType, 0)) + } + else if (questProgressState == QuestProgressState.Accepted && + (quest.QuestType == QuestType.Tutorial || + quest.QuestType == QuestType.WildHunt || + quest.QuestType == QuestType.Light)) + { + // Add a new personal quest record for the player + if (!Server.Database.InsertQuestProgress(client.Character.CommonId, questScheduleId, quest.QuestType, 0, connection)) { Logger.Error($"Failed to insert progress for the quest {quest.QuestId}"); } } - } - else if (questProgressState == QuestProgressState.Accepted && ( - quest.QuestType == QuestType.Tutorial - || quest.QuestType == QuestType.WildHunt - || quest.QuestType == QuestType.Light)) - { - // Add a new personal quest record for the player - if (!Server.Database.InsertQuestProgress(client.Character.CommonId, questScheduleId, quest.QuestType, 0)) + + if (questProgressState == QuestProgressState.Checkpoint || questProgressState == QuestProgressState.Accepted) { - Logger.Error($"Failed to insert progress for the quest {quest.QuestId}"); + questStateManager.UpdateQuestProgress(questScheduleId, connection); } - } - - if (questProgressState == QuestProgressState.Checkpoint || questProgressState == QuestProgressState.Accepted) - { - questStateManager.UpdateQuestProgress(questScheduleId); - } - else if (questProgressState == QuestProgressState.Complete) - { - res.QuestProgressResult = 3; // ProcessEnd - CompleteQuest(quest, client, questStateManager); - } + else if (questProgressState == QuestProgressState.Complete) + { + res.QuestProgressResult = 3; // ProcessEnd + CompleteQuest(quest, client, questStateManager, connection); + } + }); if (res.QuestProcessState.Count > 0) { @@ -122,10 +125,10 @@ public override void Handle(GameClient client, StructurePacket comma public void CreateItems(DbConnection connection, Character character) { } public bool DeleteAccount(int accountId) { return true; } public int DeleteBazaarExhibition(ulong bazaarId) { return 1; } - public bool DeleteBoxRewardItem(uint commonId, uint uniqId) { return true; } + public bool DeleteBoxRewardItem(uint commonId, uint uniqId, DbConnection? connectionIn = null) { return true; } public bool DeleteCharacter(uint characterId) { return true; } public bool DeleteCommunicationShortcut(uint characterId, uint pageNo, uint buttonNo) { return true; } public bool DeleteConnection(int serverId, int accountId) { return true; } @@ -206,7 +206,7 @@ public void CreateItems(DbConnection connection, Character character) { } public bool DeleteEquippedCustomSkill(uint commonId, JobId job, byte slotNo) { return true; } public bool DeleteNormalSkillParam(uint commonId, JobId job, uint skillNo) { return true; } public bool DeletePawn(uint pawnId) { return true; } - public bool DeletePriorityQuest(uint characterCommonId, uint questScheduleId) { return true; } + public bool DeletePriorityQuest(uint characterCommonId, uint questScheduleId, DbConnection? connectionIn = null) { return true; } public bool DeleteReleasedWarpPoint(uint characterId, uint warpPointId) { return true; } public bool DeleteShortcut(uint characterId, uint pageNo, uint buttonNo) { return true; } public bool DeleteSpSkill(uint pawnId, JobId job, byte spSkillId) { return true; } @@ -220,15 +220,15 @@ public void DeleteAllEquipItems(uint commonId, DbConnection? connectionIn = null public void Execute(string sql) {} public void Execute(DbConnection conn, string sql) {} public List FetchCharacterBazaarExhibitions(uint characterId) { return new List(); } - public CompletedQuest GetCompletedQuestsById(uint characterCommonId, QuestId questId) { return new CompletedQuest(); } - public List GetCompletedQuestsByType(uint characterCommonId, QuestType questType) { return new List(); } + public CompletedQuest GetCompletedQuestsById(uint characterCommonId, QuestId questId, DbConnection? connectionIn = null) { return new CompletedQuest(); } + public List GetCompletedQuestsByType(uint characterCommonId, QuestType questType, DbConnection? connectionIn = null) { return new List(); } public bool CreateMeta(DatabaseMeta meta) { return true; } public DatabaseMeta GetMeta() { return new DatabaseMeta(); } - public List GetPriorityQuestScheduleIds(uint characterCommonId) { return new List(); } - public QuestProgress GetQuestProgressByScheduleId(uint characterCommonId, uint questScheduleId) { return new QuestProgress(); } - public List GetQuestProgressByType(uint characterCommonId, QuestType questType) { return new List(); } + public List GetPriorityQuestScheduleIds(uint characterCommonId, DbConnection? connectionIn = null) { return new List(); } + public QuestProgress GetQuestProgressByScheduleId(uint characterCommonId, uint questScheduleId, DbConnection? connectionIn = null) { return new QuestProgress(); } + public List GetQuestProgressByType(uint characterCommonId, QuestType questType, DbConnection? connectionIn = null) { return new List(); } public ulong InsertBazaarExhibition(BazaarExhibition exhibition) { return 1; } - public bool InsertBoxRewardItems(uint commonId, QuestBoxRewards rewards) { return true; } + public bool InsertBoxRewardItems(uint commonId, QuestBoxRewards rewards, DbConnection? connectionIn = null) { return true; } public bool InsertCommunicationShortcut(uint characterId, CDataCommunicationShortCut communicationShortcut) { return true; } public bool InsertConnection(Connection connection) { return true; } public int InsertContact(uint requestingCharacterId, uint requestedCharacterId, ContactListStatus status, ContactListType type, bool requesterFavorite, bool requestedFavorite) { return 1; } @@ -237,7 +237,7 @@ public void Execute(DbConnection conn, string sql) {} public bool InsertEquippedAbility(uint commonId, JobId equipptedToJob, byte slotNo, Ability ability) { return true; } public bool InsertEquippedCustomSkill(uint commonId, byte slotNo, CustomSkill skill) { return true; } public bool InsertGainExtendParam(uint commonId, CDataOrbGainExtendParam Param) { return true; } - public bool InsertIfNotExistCompletedQuest(uint characterCommonId, QuestId questId, QuestType questType) { return true; } + public bool InsertIfNotExistCompletedQuest(uint characterCommonId, QuestId questId, QuestType questType, DbConnection? connectionIn = null) { return true; } public bool InsertIfNotExistsDragonForceAugmentation(uint commonId, uint elementId, uint pageNo, uint groupNo, uint indexNo) { return true; } public bool InsertIfNotExistsNormalSkillParam(uint commonId, CDataNormalSkillParam normalSkillParam) { return true; } public bool InsertIfNotExistsPawnTrainingStatus(uint pawnId, JobId job, byte[] pawnTrainingStatus) { return true; } @@ -247,8 +247,8 @@ public void Execute(DbConnection conn, string sql) {} public bool InsertLearnedCustomSkill(uint commonId, CustomSkill skill) { return true; } public bool InsertNormalSkillParam(uint commonId, CDataNormalSkillParam normalSkillParam) { return true; } public bool InsertPawnTrainingStatus(uint pawnId, JobId job, byte[] pawnTrainingStatus) { return true; } - public bool InsertPriorityQuest(uint characterCommonId, uint questScheduleId) { return true; } - public bool InsertQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType, uint step) { return true; } + public bool InsertPriorityQuest(uint characterCommonId, uint questScheduleId, DbConnection? connectionIn = null) { return true; } + public bool InsertQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType, uint step, DbConnection? connectionIn = null) { return true; } public bool InsertReleasedWarpPoint(uint characterId, ReleasedWarpPoint ReleasedWarpPoint) { return true; } public bool InsertSecretAbilityUnlock(uint commonId, SecretAbility secretAbility) { return true; } public bool InsertShortcut(uint characterId, CDataShortCut shortcut) { return true; } @@ -257,7 +257,7 @@ public void Execute(DbConnection conn, string sql) {} public bool InsertStorage(uint characterId, StorageType storageType, Storage storage) { return true; } public bool InsertStorageItem(uint characterId, StorageType storageType, ushort slotNo, uint itemNum, Item item, DbConnection? connectionIn = null) { return true; } public bool InsertWalletPoint(uint characterId, CDataWalletPoint walletPoint) { return true; } - public bool RemoveQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType) { return true; } + public bool RemoveQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType, DbConnection? connectionIn = null) { return true; } public bool ReplaceCharacterJobData(uint commonId, CDataCharacterJobData replacedCharacterJobData, DbConnection? connectionIn = null) { return true; } public bool ReplaceCommunicationShortcut(uint characterId, CDataCommunicationShortCut communicationShortcut, DbConnection? connectionIn = null) { return true; } public bool ReplaceEquipItem(uint commonId, JobId job, EquipType equipType, byte equipSlot, string itemUId, DbConnection? connectionIn = null) { return true; } @@ -271,7 +271,7 @@ public void Execute(DbConnection conn, string sql) {} public bool ReplaceShortcut(uint characterId, CDataShortCut shortcut, DbConnection? connectionIn = null) { return true; } public bool ReplaceStorageItem(uint characterId, StorageType storageType, ushort slotNo, uint itemNum, Item item, DbConnection? connectionIn = null) { return true; } public bool ReplaceWalletPoint(uint characterId, CDataWalletPoint walletPoint) { return true; } - public bool ReplaceCompletedQuest(uint characterCommonId, QuestId questId, QuestType questType, uint count) { return true; } + public bool ReplaceCompletedQuest(uint characterCommonId, QuestId questId, QuestType questType, uint count, DbConnection? connectionIn = null) { return true; } public Account SelectAccountById(int accountId) { return new Account(); } public Account SelectAccountByLoginToken(string loginToken) { return new Account(); } public Account SelectAccountByName(string accountName) { return new Account(); } @@ -279,7 +279,7 @@ public void Execute(DbConnection conn, string sql) {} public List SelectActiveBazaarExhibitionsByItemIdsExcludingOwn(List itemIds, uint excludedCharacterId) { return new List(); } public List SelectAllUnlockedSecretAbilities(uint commonId) { return new List(); } public BazaarExhibition SelectBazaarExhibitionByBazaarId(ulong bazaarId) { return new BazaarExhibition(); } - public List SelectBoxRewardItems(uint commonId) { return new List(); } + public List SelectBoxRewardItems(uint commonId, DbConnection? connectionIn = null) { return new List(); } public Character SelectCharacter(uint characterId) { return new Character(); } public List SelectCharactersByAccountId(int accountId, GameMode gameMode) { return new List(); } public List SelectAllCharacters() { return new List(); } @@ -309,7 +309,7 @@ public void Execute(DbConnection conn, string sql) {} public bool UpdateCharacterArisenProfile(Character character) { return true; } public bool UpdateCharacterBaseInfo(Character character) { return true; } public bool UpdateCharacterCommonBaseInfo(CharacterCommon common, DbConnection? connectionIn = null) { return true; } - public bool UpdateCharacterJobData(uint commonId, CDataCharacterJobData updatedCharacterJobData) { return true; } + public bool UpdateCharacterJobData(uint commonId, CDataCharacterJobData updatedCharacterJobData, DbConnection? connectionIn = null) { return true; } public bool UpdateCharacterMatchingProfile(Character character) { return true; } public bool UpdateCommunicationShortcut(uint characterId, uint oldPageNo, uint oldButtonNo, CDataCommunicationShortCut updatedCommunicationShortcut) { return true; } public int UpdateContact(uint requestingCharacterId, uint requestedCharacterId, ContactListStatus status, ContactListType type, bool requesterFavorite, bool requestedFavorite) { return 1; } @@ -329,7 +329,7 @@ public void Execute(DbConnection conn, string sql) {} public bool InsertIfNotExistsPawnCraftProgress(CraftProgress craftProgress) { return true; } public bool UpdatePawnCraftProgress(CraftProgress craftProgress) { return true; } public bool DeletePawnCraftProgress(uint craftCharacterId, uint craftLeadPawnId) { return true; } - public bool UpdateQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType, uint step) { return true; } + public bool UpdateQuestProgress(uint characterCommonId, uint questScheduleId, QuestType questType, uint step, DbConnection? connectionIn = null) { return true; } public bool UpdateReleasedWarpPoint(uint characterId, ReleasedWarpPoint updatedReleasedWarpPoint) { return true; } public bool UpdateShortcut(uint characterId, uint oldPageNo, uint oldButtonNo, CDataShortCut updatedShortcut) { return true; } public bool UpdateStatusInfo(CharacterCommon character) { return true; } @@ -359,8 +359,8 @@ public bool UpdateRentalPawnSlot(uint characterId, uint num) public bool InsertAddStatus(string itemUid, uint characterId, byte isAddStat1, byte isAddStat2, ushort addStat1, ushort addStat2) { return true; } public List GetAddStatusByUID(string itemUid) { return new List(); } public bool UpdateAddStatus(string itemUid, uint characterId, byte isAddStat1, byte isAddStat2, ushort addStat1, ushort addStat2) { return true; } - public bool ReplaceCharacterPlayPointData(uint id, CDataJobPlayPoint updatedCharacterPlayPointData) { return true; } - public bool UpdateCharacterPlayPointData(uint id, CDataJobPlayPoint updatedCharacterPlayPointData) { return true; } + public bool ReplaceCharacterPlayPointData(uint id, CDataJobPlayPoint updatedCharacterPlayPointData, DbConnection? connectionIn = null) { return true; } + public bool UpdateCharacterPlayPointData(uint id, CDataJobPlayPoint updatedCharacterPlayPointData, DbConnection? connectionIn = null) { return true; } public bool InsertCharacterStampData(uint id, CharacterStampBonus stampData) { return true; } public bool UpdateCharacterStampData(uint id, CharacterStampBonus stampData) { return true; } public bool InsertCrest(uint characterCommonId, string itemUId, uint slot, uint crestId, uint crestAmount, DbConnection? connectionIn = null) { return true; } From ca045a2a65d1d1317b4c34665d5e008a92b72e6f Mon Sep 17 00:00:00 2001 From: Ryan Yappert Date: Fri, 8 Nov 2024 17:48:46 -0800 Subject: [PATCH 02/12] Packet queueing in InstanceEnemyKillHandler in preparation for moving to transactions. --- .../Characters/BitterblackMazeManager.cs | 21 +++---- .../Characters/ExpManager.cs | 24 +++++--- .../Characters/PlayPointManager.cs | 15 +++-- .../Handler/InstanceEnemyKillHandler.cs | 58 ++++++++++++------- Arrowgene.Ddon.GameServer/Party/PartyGroup.cs | 26 +++++++++ .../Quests/QuestStateManager.cs | 14 +++-- Arrowgene.Ddon.Server/Network/Client.cs | 9 ++- Arrowgene.Ddon.Server/Network/PacketQueue.cs | 33 +++++++++++ 8 files changed, 148 insertions(+), 52 deletions(-) create mode 100644 Arrowgene.Ddon.Server/Network/PacketQueue.cs diff --git a/Arrowgene.Ddon.GameServer/Characters/BitterblackMazeManager.cs b/Arrowgene.Ddon.GameServer/Characters/BitterblackMazeManager.cs index fc01151e7..5af5a4578 100644 --- a/Arrowgene.Ddon.GameServer/Characters/BitterblackMazeManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/BitterblackMazeManager.cs @@ -6,20 +6,11 @@ using Arrowgene.Ddon.Shared.Model; using Arrowgene.Ddon.Shared.Model.Appraisal; using Arrowgene.Ddon.Shared.Model.BattleContent; -using Arrowgene.Ddon.Shared.Model.Quest; using System; -using System.Collections; using System.Collections.Generic; using System.Data.Common; using System.Data.Entity.Core.Common.CommandTrees.ExpressionBuilder; -using System.Diagnostics; using System.Linq; -using System.Reflection.Metadata; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; -using System.Xml.Linq; -using YamlDotNet.Core; namespace Arrowgene.Ddon.GameServer.Characters { @@ -104,8 +95,10 @@ public static CDataBattleContentStatus GetUpdatedContentStatus(DdonGameServer se return contentStatus; } - public static void HandleTierClear(DdonGameServer server, GameClient client, Character character, StageId stageId) + public static PacketQueue HandleTierClear(DdonGameServer server, GameClient client, Character character, StageId stageId) { + PacketQueue packets = new(); + var progress = character.BbmProgress; var match = server.AssetRepository.BitterblackMazeAsset.Stages.Select(x => x.Value).Where(x => x.ContentId == progress.ContentId).FirstOrDefault(); @@ -118,7 +111,7 @@ public static void HandleTierClear(DdonGameServer server, GameClient client, Cha Unk0 = progress.ContentId, TierName = match.ContentName }; - client.Send(clearNtc); + client.Enqueue(clearNtc, packets); progress.ContentId = stageProgression.ConnectionList[0].Value; progress.Tier += 1; @@ -132,7 +125,7 @@ public static void HandleTierClear(DdonGameServer server, GameClient client, Cha ContentName = (match.ContentMode == BattleContentMode.Rotunda) ? "Bitterblack Maze Rotunda" : "Bitterblack Maze Abyss", ClearTime = (ulong) (DateTimeOffset.UtcNow.ToUnixTimeSeconds() - (long) progress.StartTime) }; - client.Send(clearNtc); + client.Enqueue(clearNtc, packets); progress.ContentId = 0; progress.Tier = 0; @@ -151,7 +144,9 @@ public static void HandleTierClear(DdonGameServer server, GameClient client, Cha // Update the situation information S2CBattleContentProgressNtc progressNtc = new S2CBattleContentProgressNtc(); progressNtc.BattleContentStatusList.Add(BitterblackMazeManager.GetUpdatedContentStatus(server, character)); - client.Send(progressNtc); + client.Enqueue(progressNtc, packets); + + return packets; } public static bool IsMazeReward(uint itemId) diff --git a/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs b/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs index 7d8f9e362..1f2c09440 100644 --- a/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs @@ -15,6 +15,7 @@ using System.Text; using System.Buffers.Text; using System.Data.Common; +using Arrowgene.Ddon.Shared.Network; namespace Arrowgene.Ddon.GameServer.Characters { @@ -459,8 +460,10 @@ public static CDataCharacterJobData CalculateBaseStats(CharacterCommon Character return JobData; } - public void AddExp(GameClient client, CharacterCommon characterToAddExpTo, uint gainedExp, RewardSource rewardType, QuestType questType = QuestType.All, DbConnection? connectionIn = null) + public PacketQueue AddExp(GameClient client, CharacterCommon characterToAddExpTo, uint gainedExp, RewardSource rewardType, QuestType questType = QuestType.All, DbConnection? connectionIn = null) { + PacketQueue packets = new(); + var lvCap = (client.GameMode == GameMode.Normal) ? _Server.Setting.GameLogicSetting.JobLevelMax : BitterblackMazeManager.LevelCap(client.Character.BbmProgress); @@ -484,7 +487,7 @@ public void AddExp(GameClient client, CharacterCommon characterToAddExpTo, uint expNtc.ExtraBonusExp = extraBonusExp; expNtc.TotalExp = activeCharacterJobData.Exp; expNtc.Type = (byte) rewardType; // TODO: Figure out - client.Send(expNtc); + client.Enqueue(expNtc, packets); } else if(characterToAddExpTo is Pawn) { @@ -495,7 +498,7 @@ public void AddExp(GameClient client, CharacterCommon characterToAddExpTo, uint expNtc.ExtraBonusExp = extraBonusExp; expNtc.TotalExp = activeCharacterJobData.Exp; expNtc.Type = (byte) rewardType; // TODO: Figure out - client.Send(expNtc); + client.Enqueue(expNtc, packets); } @@ -531,7 +534,7 @@ public void AddExp(GameClient client, CharacterCommon characterToAddExpTo, uint lvlNtc.AddJobPoint = addJobPoint; lvlNtc.TotalJobPoint = activeCharacterJobData.JobPoint; GameStructure.CDataCharacterLevelParam(lvlNtc.CharacterLevelParam, (Character) characterToAddExpTo); - client.Send(lvlNtc); + client.Enqueue(lvlNtc, packets); // Inform other party members S2CJobCharacterJobLevelUpMemberNtc lvlMemberNtc = new S2CJobCharacterJobLevelUpMemberNtc(); @@ -539,7 +542,7 @@ public void AddExp(GameClient client, CharacterCommon characterToAddExpTo, uint lvlMemberNtc.Job = characterToAddExpTo.Job; lvlMemberNtc.Level = activeCharacterJobData.Lv; GameStructure.CDataCharacterLevelParam(lvlMemberNtc.CharacterLevelParam, (Character) characterToAddExpTo); - client.Party.SendToAllExcept(lvlMemberNtc, client); + client.Party.EnqueueToAllExcept(lvlMemberNtc, packets, client); } else if(characterToAddExpTo is Pawn) { @@ -551,7 +554,7 @@ public void AddExp(GameClient client, CharacterCommon characterToAddExpTo, uint lvlNtc.AddJobPoint = addJobPoint; lvlNtc.TotalJobPoint = activeCharacterJobData.JobPoint; GameStructure.CDataCharacterLevelParam(lvlNtc.CharacterLevelParam, (Pawn) characterToAddExpTo); - client.Send(lvlNtc); + client.Enqueue(lvlNtc, packets); // Inform other party members S2CJobPawnJobLevelUpMemberNtc lvlMemberNtc = new S2CJobPawnJobLevelUpMemberNtc(); @@ -560,13 +563,20 @@ public void AddExp(GameClient client, CharacterCommon characterToAddExpTo, uint lvlMemberNtc.Job = characterToAddExpTo.Job; lvlMemberNtc.Level = activeCharacterJobData.Lv; GameStructure.CDataCharacterLevelParam(lvlMemberNtc.CharacterLevelParam, (Pawn) characterToAddExpTo); - client.Party.SendToAllExcept(lvlMemberNtc, client); + client.Party.EnqueueToAllExcept(lvlMemberNtc, packets, client); } } // PERSIST CHANGES IN DB _Server.Database.UpdateCharacterJobData(characterToAddExpTo.CommonId, activeCharacterJobData, connectionIn); } + + return packets; + } + + public void AddExpNtc(GameClient client, CharacterCommon characterToAddExpTo, uint gainedExp, RewardSource rewardType, QuestType questType = QuestType.All, DbConnection? connectionIn = null) + { + AddExp(client, characterToAddExpTo, gainedExp, rewardType, questType, connectionIn).Send(); } public void AddJp(GameClient client, CharacterCommon characterToJpExpTo, uint gainedJp, RewardSource rewardType, QuestType questType = QuestType.All, DbConnection? connectionIn = null) diff --git a/Arrowgene.Ddon.GameServer/Characters/PlayPointManager.cs b/Arrowgene.Ddon.GameServer/Characters/PlayPointManager.cs index 3fa6d0aa1..805329b46 100644 --- a/Arrowgene.Ddon.GameServer/Characters/PlayPointManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/PlayPointManager.cs @@ -26,7 +26,13 @@ public PlayPointManager(DdonGameServer server) private readonly DdonGameServer _Server; - public void AddPlayPoint(GameClient client, uint gainedPoints, JobId? job = null, byte type = 1, DbConnection? connectionIn = null) + public void AddPlayPointNtc(GameClient client, uint gainedPoints, JobId? job = null, byte type = 1, DbConnection? connectionIn = null) + { + var ntc = AddPlayPoint(client, gainedPoints, job, type, connectionIn); + client.Send(ntc); + } + + public S2CJobUpdatePlayPointNtc AddPlayPoint(GameClient client, uint gainedPoints, JobId? job = null, byte type = 1, DbConnection? connectionIn = null) { CDataJobPlayPoint? targetPlayPoint; if (job is null) @@ -40,7 +46,7 @@ public void AddPlayPoint(GameClient client, uint gainedPoints, JobId? job = null ?? throw new ResponseErrorException(ErrorCode.ERROR_CODE_JOB_VALUE_SHOP_INVALID_JOB); } - uint extraBonusPoints = (uint) (_Server.GpCourseManager.EnemyPlayPointBonus() * gainedPoints); + uint extraBonusPoints = (uint)(_Server.GpCourseManager.EnemyPlayPointBonus() * gainedPoints); if (targetPlayPoint != null && targetPlayPoint.PlayPoint.PlayPoint < PP_MAX) { uint clampedNew = Math.Min(targetPlayPoint.PlayPoint.PlayPoint + gainedPoints + extraBonusPoints, PP_MAX); @@ -54,11 +60,12 @@ public void AddPlayPoint(GameClient client, uint gainedPoints, JobId? job = null TotalPoint = targetPlayPoint.PlayPoint.PlayPoint, Type = type //Type == 1 (default) is "loud" and will show the UpdatePoint amount to the user, as both a chat log and floating text. }; - - client.Send(ppNtc); _Server.Database.UpdateCharacterPlayPointData(client.Character.CharacterId, targetPlayPoint, connectionIn); + return ppNtc; } + + return new(); } public void RemovePlayPoint(GameClient client, uint removedPoints, JobId? job = null, byte type = 0) diff --git a/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs b/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs index d3e3c5cef..c1e0a6d17 100644 --- a/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs @@ -6,7 +6,6 @@ using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Entity.Structure; using Arrowgene.Ddon.Shared.Model; -using Arrowgene.Ddon.Shared.Network; using Arrowgene.Logging; using System; using System.Collections.Generic; @@ -36,6 +35,8 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne CDataStageLayoutId layoutId = packet.LayoutId; StageId stageId = StageId.FromStageLayoutId(layoutId); + PacketQueue queuedPackets = new(); + // The training room uses special handling to produce enemies that don't exist in the QuestState or InstanceEnemyManager. // Return an empty response here to not break the rest of the handling. if (_ignoreKillsInStageIds.Contains(stageId.Id)) @@ -81,7 +82,8 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne EnemyInfo = enemyKilled.asCDataStageLayoutEnemyPresetEnemyInfoClient() } }; - client.Send(repopNtc); + client.Enqueue(repopNtc, queuedPackets); + //client.Send(repopNtc); } else { @@ -109,13 +111,15 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne LayoutId = packet.LayoutId, IsAreaBoss = IsAreaBoss && (client.GameMode == GameMode.Normal) }; - client.Party.SendToAll(groupDestroyedNtc); + client.Party.EnqueueToAll(groupDestroyedNtc, queuedPackets); + //client.Party.SendToAll(groupDestroyedNtc); if (IsAreaBoss && client.GameMode == GameMode.BitterblackMaze) { foreach (var memberClient in client.Party.Clients) { - BitterblackMazeManager.HandleTierClear(_gameServer, memberClient, memberClient.Character, stageId); + var ntcs = BitterblackMazeManager.HandleTierClear(_gameServer, memberClient, memberClient.Character, stageId); + queuedPackets.AddRange(ntcs); } } } @@ -125,6 +129,15 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne return new(); } + var dropItemNtc = new S2CInstancePopDropItemNtc() + { + LayoutId = packet.LayoutId, + SetId = packet.SetId, + MdlType = enemyKilled.DropsTable.MdlType, + PosX = packet.DropPosX, + PosY = packet.DropPosY, + PosZ = packet.DropPosZ + }; foreach (var partyMemberClient in client.Party.Clients) { // If the enemy is quest controlled, then either get from the quest loot drop, or the general one. @@ -141,25 +154,21 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne // If the roll was unlucky, there is a chance that no bag will show. if (instancedGatheringItems.Where(x => x.ItemNum > 0).Any()) { - partyMemberClient.Send(new S2CInstancePopDropItemNtc() - { - LayoutId = packet.LayoutId, - SetId = packet.SetId, - MdlType = enemyKilled.DropsTable.MdlType, - PosX = packet.DropPosX, - PosY = packet.DropPosY, - PosZ = packet.DropPosZ - }); + partyMemberClient.Enqueue(dropItemNtc, queuedPackets); + //partyMemberClient.Send(dropItemNtc); } } + + uint calcExp = _gameServer.ExpManager.GetAdjustedExp(client.GameMode, RewardSource.Enemy, client.Party, enemyKilled.GetDroppedExperience(), enemyKilled.Lv); + uint calcPP = enemyKilled.GetDroppedPlayPoints(); + foreach (PartyMember member in client.Party.Members) { if (member.JoinState != JoinState.On) continue; // Only fully joined members get rewards. - uint gainedExp = _gameServer.ExpManager.GetAdjustedExp(client.GameMode, RewardSource.Enemy, client.Party, enemyKilled.GetDroppedExperience(), enemyKilled.Lv); - - uint gainedPP = enemyKilled.GetDroppedPlayPoints(); + uint gainedExp = calcExp; + uint gainedPP = calcPP; GameClient memberClient; CharacterCommon memberCharacter; @@ -180,7 +189,8 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne } // TODO: Add transaction? - playerMember.QuestState.HandleEnemyHuntRequests(enemyKilled); + var huntPackets = playerMember.QuestState.HandleEnemyHuntRequests(enemyKilled); + queuedPackets.AddRange(huntPackets); S2CItemUpdateCharacterItemNtc updateCharacterItemNtc = new S2CItemUpdateCharacterItemNtc(); @@ -202,17 +212,20 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne if (updateCharacterItemNtc.UpdateItemList.Count != 0 || updateCharacterItemNtc.UpdateWalletList.Count != 0) { - memberClient.Send(updateCharacterItemNtc); + memberClient.Enqueue(updateCharacterItemNtc, queuedPackets); + //memberClient.Send(updateCharacterItemNtc); } if (gainedPP > 0) { - _gameServer.PPManager.AddPlayPoint(memberClient, gainedPP, type: 1); + var ntc = _gameServer.PPManager.AddPlayPoint(memberClient, gainedPP, type: 1); + memberClient.Enqueue(ntc, queuedPackets); } if (gainedExp > 0) { - _gameServer.ExpManager.AddExp(memberClient, memberCharacter, gainedExp, RewardSource.Enemy); + var ntcs = _gameServer.ExpManager.AddExp(memberClient, memberCharacter, gainedExp, RewardSource.Enemy); + queuedPackets.AddRange(ntcs); } } else if (member is PawnPartyMember pawnMember) @@ -236,7 +249,8 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne if (pawnExp > 0) { - _gameServer.ExpManager.AddExp(memberClient, memberCharacter, pawnExp, RewardSource.Enemy); + var ntcs = _gameServer.ExpManager.AddExp(memberClient, memberCharacter, pawnExp, RewardSource.Enemy); + queuedPackets.AddRange(ntcs); } } else @@ -245,6 +259,8 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne } } + queuedPackets.Send(); + // TODO: EnemyId and KillNum return new S2CInstanceEnemyKillRes() { diff --git a/Arrowgene.Ddon.GameServer/Party/PartyGroup.cs b/Arrowgene.Ddon.GameServer/Party/PartyGroup.cs index cb12f2d1b..d602e9268 100644 --- a/Arrowgene.Ddon.GameServer/Party/PartyGroup.cs +++ b/Arrowgene.Ddon.GameServer/Party/PartyGroup.cs @@ -2,6 +2,7 @@ using Arrowgene.Ddon.GameServer.Instance; using Arrowgene.Ddon.GameServer.Quests; using Arrowgene.Ddon.Server; +using Arrowgene.Ddon.Server.Network; using Arrowgene.Ddon.Shared; using Arrowgene.Ddon.Shared.Entity; using Arrowgene.Ddon.Shared.Entity.Structure; @@ -572,6 +573,16 @@ public PlayerPartyMember GetPlayerPartyMember(GameClient client) SendToAll(packet); } + public void EnqueueToAll(TResStruct res, PacketQueue queue) + where TResStruct : class, IPacketStructure, new() + { + StructurePacket packet = new StructurePacket(res); + foreach(GameClient client in Clients) + { + queue.Enqueue((client, packet)); + } + } + public void SendToAll(Packet packet) { foreach (GameClient client in Clients) @@ -586,6 +597,21 @@ public void SendToAll(Packet packet) SendToAllExcept(packet, exceptions); } + public void EnqueueToAllExcept(TResStruct res, PacketQueue queue, params GameClient[] exceptions) + where TResStruct : class, IPacketStructure, new() + { + StructurePacket packet = new StructurePacket(res); + foreach (GameClient client in Clients) + { + if (exceptions.Contains(client)) + { + continue; + } + queue.Enqueue((client, packet)); + } + } + + public void SendToAllExcept(Packet packet, params GameClient[] exceptions) { foreach (GameClient client in Clients) diff --git a/Arrowgene.Ddon.GameServer/Quests/QuestStateManager.cs b/Arrowgene.Ddon.GameServer/Quests/QuestStateManager.cs index 647eb30db..a2b7d58ef 100644 --- a/Arrowgene.Ddon.GameServer/Quests/QuestStateManager.cs +++ b/Arrowgene.Ddon.GameServer/Quests/QuestStateManager.cs @@ -1,4 +1,3 @@ -using Arrowgene.Ddon.Database.Model; using Arrowgene.Ddon.GameServer.Characters; using Arrowgene.Ddon.GameServer.Party; using Arrowgene.Ddon.Server; @@ -571,7 +570,7 @@ protected void SendWalletRewards(DdonGameServer server, GameClient client, Quest { if (expPoint.Type == ExpType.ExperiencePoints) { - server.ExpManager.AddExp(client, client.Character, expPoint.Reward, RewardSource.Quest, quest.QuestType, connectionIn); + server.ExpManager.AddExpNtc(client, client.Character, expPoint.Reward, RewardSource.Quest, quest.QuestType, connectionIn); } else if (expPoint.Type == ExpType.JobPoints) { @@ -579,7 +578,7 @@ protected void SendWalletRewards(DdonGameServer server, GameClient client, Quest } else if (expPoint.Type == ExpType.PlayPoints) { - server.PPManager.AddPlayPoint(client, expPoint.Reward, type: 1, connectionIn: connectionIn); + server.PPManager.AddPlayPointNtc(client, expPoint.Reward, type: 1, connectionIn: connectionIn); } } } @@ -865,13 +864,15 @@ public override void UpdatePriorityQuestList(GameClient requestingClient, DbConn throw new NotImplementedException(); } - public void HandleEnemyHuntRequests(Enemy enemy) + public PacketQueue HandleEnemyHuntRequests(Enemy enemy) { if (Member.Client.Character.GameMode != GameMode.Normal) { - return; + return new(); } + PacketQueue packets = new(); + lock (ActiveQuests) { foreach (var quest in ActiveQuests) @@ -907,10 +908,11 @@ public void HandleEnemyHuntRequests(Enemy enemy) Work03 = (int)huntRecord.AmountHunted }); - Member.Client.Send(ntc); + Member.Client.Enqueue(ntc, packets); } } } + return packets; } } } diff --git a/Arrowgene.Ddon.Server/Network/Client.cs b/Arrowgene.Ddon.Server/Network/Client.cs index f678f1437..0e4f415e7 100644 --- a/Arrowgene.Ddon.Server/Network/Client.cs +++ b/Arrowgene.Ddon.Server/Network/Client.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Arrowgene.Ddon.Shared.Entity; using Arrowgene.Ddon.Shared.Network; @@ -78,6 +78,13 @@ public List Receive(byte[] data) Send(packet); } + public void Enqueue(TResStruct res, PacketQueue queue) + where TResStruct : class, IPacketStructure, new() + { + StructurePacket packet = new StructurePacket(res); + queue.Enqueue((this, packet)); + } + public void Send(Packet packet) { if (!_challengeCompleted diff --git a/Arrowgene.Ddon.Server/Network/PacketQueue.cs b/Arrowgene.Ddon.Server/Network/PacketQueue.cs new file mode 100644 index 000000000..abedaea7b --- /dev/null +++ b/Arrowgene.Ddon.Server/Network/PacketQueue.cs @@ -0,0 +1,33 @@ +using Arrowgene.Ddon.Shared.Entity; +using Arrowgene.Ddon.Shared.Network; +using System.Collections.Generic; + +namespace Arrowgene.Ddon.Server.Network +{ + public class PacketQueue : Queue<(Client Client, Packet Packet)> + { + public PacketQueue() { } + public void Send() + { + foreach ((Client Client, Packet Packet) in this) + { + Client.Send(Packet); + } + } + + public void Enqueue(Client client, TResStruct res) + where TResStruct : class, IPacketStructure, new() + { + StructurePacket packet = new StructurePacket(res); + this.Enqueue((client, packet)); + } + + public void AddRange(IEnumerable<(Client Client, Packet Packet)> other) + { + foreach(var item in other) + { + this.Enqueue(item); + } + } + } +} From 219dcc7a930170d961937bb73869c3cc049d62c2 Mon Sep 17 00:00:00 2001 From: Ryan Yappert Date: Fri, 8 Nov 2024 21:14:20 -0800 Subject: [PATCH 03/12] Move InstanceEnemyKillHandler into a transaction so it should be much snappier. --- Arrowgene.Ddon.Database/IDatabase.cs | 14 +- .../Core/DdonSqlDbBitterBlackMazeProgress.cs | 33 +- .../Core/DdonSqlDbBitterBlackMazeRewards.cs | 27 +- .../Characters/BitterblackMazeManager.cs | 6 +- .../Characters/ExpManager.cs | 33 +- .../Handler/InstanceEnemyKillHandler.cs | 281 +++++++++--------- .../Quests/QuestStateManager.cs | 10 +- .../Database/DatabaseMigratorTest.cs | 6 +- 8 files changed, 202 insertions(+), 208 deletions(-) diff --git a/Arrowgene.Ddon.Database/IDatabase.cs b/Arrowgene.Ddon.Database/IDatabase.cs index 5466d5979..141bda8bf 100644 --- a/Arrowgene.Ddon.Database/IDatabase.cs +++ b/Arrowgene.Ddon.Database/IDatabase.cs @@ -538,23 +538,13 @@ bool InsertBBMProgress( bool killedDeath, ulong lastTicketTime ); - bool UpdateBBMProgress( - uint characterId, - ulong startTime, - uint contentId, - BattleContentMode contentMode, - uint tier, - bool killedDeath, - ulong lastTicketTime - ); - bool UpdateBBMProgress(uint characterId, BitterblackMazeProgress progress); + bool UpdateBBMProgress(uint characterId, BitterblackMazeProgress progress, DbConnection? connectionIn = null); BitterblackMazeProgress SelectBBMProgress(uint characterId); bool RemoveBBMProgress(uint characterId); // Bitterblack Maze Rewards bool InsertBBMRewards(uint characterId, uint goldMarks, uint silverMarks, uint redMarks); - bool UpdateBBMRewards(uint characterId, uint goldMarks, uint silverMarks, uint redMarks); - bool UpdateBBMRewards(uint characterId, BitterblackMazeRewards rewards); + bool UpdateBBMRewards(uint characterId, BitterblackMazeRewards rewards, DbConnection? connectionIn = null); bool RemoveBBMRewards(uint characterId); BitterblackMazeRewards SelectBBMRewards(uint characterId); diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbBitterBlackMazeProgress.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbBitterBlackMazeProgress.cs index 4d7e3de6d..352e63dc0 100644 --- a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbBitterBlackMazeProgress.cs +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbBitterBlackMazeProgress.cs @@ -43,29 +43,26 @@ public bool InsertBBMProgress(TCon connection, uint characterId, ulong startTime AddParameter(command, "last_ticket_time", lastTicketTime); }) == 1; } - public bool UpdateBBMProgress(uint characterId, BitterblackMazeProgress progress) + public bool UpdateBBMProgress(uint characterId, BitterblackMazeProgress progress, DbConnection? connectionIn = null) { - return UpdateBBMProgress(characterId, progress.StartTime, progress.ContentId, progress.ContentMode, progress.Tier, progress.KilledDeath, progress.LastTicketTime); + return UpdateBBMProgress(characterId, progress.StartTime, progress.ContentId, progress.ContentMode, progress.Tier, progress.KilledDeath, progress.LastTicketTime, connectionIn); } - public bool UpdateBBMProgress(uint characterId, ulong startTime, uint contentId, BattleContentMode contentMode, uint tier, bool killedDeath, ulong lastTicketTime) + public bool UpdateBBMProgress(uint characterId, ulong startTime, uint contentId, BattleContentMode contentMode, uint tier, bool killedDeath, ulong lastTicketTime, DbConnection? connectionIn = null) { - using TCon connection = OpenNewConnection(); - return UpdateBBMProgress(connection, characterId, startTime, contentId, contentMode, tier, killedDeath, lastTicketTime); - } - - public bool UpdateBBMProgress(TCon connection, uint characterId, ulong startTime, uint contentId, BattleContentMode contentMode, uint tier, bool killedDeath, ulong lastTicketTime) - { - return ExecuteNonQuery(connection, SqlUpdateBBMProgress, command => + return ExecuteQuerySafe(connectionIn, (connection) => { - AddParameter(command, "character_id", characterId); - AddParameter(command, "start_time", startTime); - AddParameter(command, "content_id", contentId); - AddParameter(command, "content_mode", (uint) contentMode); - AddParameter(command, "tier", tier); - AddParameter(command, "killed_death", killedDeath); - AddParameter(command, "last_ticket_time", lastTicketTime); - }) == 1; ; + return ExecuteNonQuery(connection, SqlUpdateBBMProgress, command => + { + AddParameter(command, "character_id", characterId); + AddParameter(command, "start_time", startTime); + AddParameter(command, "content_id", contentId); + AddParameter(command, "content_mode", (uint)contentMode); + AddParameter(command, "tier", tier); + AddParameter(command, "killed_death", killedDeath); + AddParameter(command, "last_ticket_time", lastTicketTime); + }) == 1; + }); } public bool RemoveBBMProgress(uint characterId) diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbBitterBlackMazeRewards.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbBitterBlackMazeRewards.cs index 56def1079..9d1824a11 100644 --- a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbBitterBlackMazeRewards.cs +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbBitterBlackMazeRewards.cs @@ -41,26 +41,23 @@ public bool InsertBBMRewards(TCon connection, uint characterId, uint goldMarks, }) == 1; } - public bool UpdateBBMRewards(uint characterId, BitterblackMazeRewards rewards) + public bool UpdateBBMRewards(uint characterId, BitterblackMazeRewards rewards, DbConnection? connectionIn = null) { - return UpdateBBMRewards(characterId, rewards.GoldMarks, rewards.SilverMarks, rewards.RedMarks); + return UpdateBBMRewards(characterId, rewards.GoldMarks, rewards.SilverMarks, rewards.RedMarks, connectionIn); } - public bool UpdateBBMRewards(uint characterId, uint goldMarks, uint silverMarks, uint redMarks) + public bool UpdateBBMRewards(uint characterId, uint goldMarks, uint silverMarks, uint redMarks, DbConnection? connectionIn = null) { - using TCon connection = OpenNewConnection(); - return UpdateBBMRewards(connection, characterId, goldMarks, silverMarks, redMarks); - } - - public bool UpdateBBMRewards(TCon connection, uint characterId, uint goldMarks, uint silverMarks, uint redMarks) - { - return ExecuteNonQuery(connection, SqlUpdateBBMRewards, command => + return ExecuteQuerySafe(connectionIn, (connection) => { - AddParameter(command, "character_id", characterId); - AddParameter(command, "gold_marks", goldMarks); - AddParameter(command, "silver_marks", silverMarks); - AddParameter(command, "red_marks", redMarks); - }) == 1; ; + return ExecuteNonQuery(connection, SqlUpdateBBMRewards, command => + { + AddParameter(command, "character_id", characterId); + AddParameter(command, "gold_marks", goldMarks); + AddParameter(command, "silver_marks", silverMarks); + AddParameter(command, "red_marks", redMarks); + }) == 1; + }); } public bool RemoveBBMRewards(uint characterId) diff --git a/Arrowgene.Ddon.GameServer/Characters/BitterblackMazeManager.cs b/Arrowgene.Ddon.GameServer/Characters/BitterblackMazeManager.cs index 5af5a4578..690a7242a 100644 --- a/Arrowgene.Ddon.GameServer/Characters/BitterblackMazeManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/BitterblackMazeManager.cs @@ -95,7 +95,7 @@ public static CDataBattleContentStatus GetUpdatedContentStatus(DdonGameServer se return contentStatus; } - public static PacketQueue HandleTierClear(DdonGameServer server, GameClient client, Character character, StageId stageId) + public static PacketQueue HandleTierClear(DdonGameServer server, GameClient client, Character character, StageId stageId, DbConnection? connectionIn = null) { PacketQueue packets = new(); @@ -130,7 +130,7 @@ public static PacketQueue HandleTierClear(DdonGameServer server, GameClient clie progress.ContentId = 0; progress.Tier = 0; } - server.Database.UpdateBBMProgress(character.CharacterId, progress); + server.Database.UpdateBBMProgress(character.CharacterId, progress, connectionIn); var rewards = server.Database.SelectBBMRewards(character.CharacterId); // TODO: handle BattleContentRewardBonus.Up (some sort of reward bonus) @@ -139,7 +139,7 @@ public static PacketQueue HandleTierClear(DdonGameServer server, GameClient clie rewards.GoldMarks += marks.Gold; rewards.SilverMarks += marks.Silver; rewards.RedMarks += marks.Red; - server.Database.UpdateBBMRewards(character.CharacterId, rewards); + server.Database.UpdateBBMRewards(character.CharacterId, rewards, connectionIn); // Update the situation information S2CBattleContentProgressNtc progressNtc = new S2CBattleContentProgressNtc(); diff --git a/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs b/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs index 1f2c09440..8ba100d0d 100644 --- a/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs @@ -1,21 +1,16 @@ #nullable enable -using System; -using System.Collections.Generic; -using Arrowgene.Ddon.Database; +using Arrowgene.Ddon.GameServer.Party; +using Arrowgene.Ddon.Server; +using Arrowgene.Ddon.Server.Network; using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Entity.Structure; using Arrowgene.Ddon.Shared.Model; -using Arrowgene.Ddon.Server; -using Arrowgene.Logging; -using Arrowgene.Ddon.Server.Network; -using System.Linq; using Arrowgene.Ddon.Shared.Model.Quest; -using Arrowgene.Ddon.GameServer.Party; -using System.IO; -using System.Text; -using System.Buffers.Text; +using Arrowgene.Logging; +using System; +using System.Collections.Generic; using System.Data.Common; -using Arrowgene.Ddon.Shared.Network; +using System.Linq; namespace Arrowgene.Ddon.GameServer.Characters { @@ -579,8 +574,9 @@ public void AddExpNtc(GameClient client, CharacterCommon characterToAddExpTo, ui AddExp(client, characterToAddExpTo, gainedExp, rewardType, questType, connectionIn).Send(); } - public void AddJp(GameClient client, CharacterCommon characterToJpExpTo, uint gainedJp, RewardSource rewardType, QuestType questType = QuestType.All, DbConnection? connectionIn = null) + public PacketQueue AddJp(GameClient client, CharacterCommon characterToJpExpTo, uint gainedJp, RewardSource rewardType, QuestType questType = QuestType.All, DbConnection? connectionIn = null) { + PacketQueue packets = new(); // Only ever has one packet, but has to exist because there are problems with returning interface types CDataCharacterJobData? activeCharacterJobData = characterToJpExpTo.ActiveCharacterJobData; activeCharacterJobData.JobPoint += gainedJp; @@ -591,7 +587,7 @@ public void AddJp(GameClient client, CharacterCommon characterToJpExpTo, uint ga jpNtc.AddJobPoint = gainedJp; jpNtc.ExtraBonusJobPoint = 0; jpNtc.TotalJobPoint = activeCharacterJobData.JobPoint; - client.Send(jpNtc); + client.Enqueue(jpNtc, packets); } else { @@ -601,11 +597,18 @@ public void AddJp(GameClient client, CharacterCommon characterToJpExpTo, uint ga jpNtc.AddJobPoint = gainedJp; jpNtc.ExtraBonusJobPoint = 0; jpNtc.TotalJobPoint = activeCharacterJobData.JobPoint; - client.Send(jpNtc); + client.Enqueue(jpNtc, packets); } // PERSIST CHANGES IN DB _Server.Database.UpdateCharacterJobData(characterToJpExpTo.CommonId, activeCharacterJobData, connectionIn); + + return packets; + } + + public void AddJpNtc(GameClient client, CharacterCommon characterToJpExpTo, uint gainedJp, RewardSource rewardType, QuestType questType = QuestType.All, DbConnection? connectionIn = null) + { + AddJp(client, characterToJpExpTo, gainedJp, rewardType, questType, connectionIn).Send(); } public void ResetExpData(GameClient client, CharacterCommon characterCommon) diff --git a/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs b/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs index c1e0a6d17..b13b41714 100644 --- a/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs @@ -78,7 +78,7 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne WaitSecond = enemyKilled.RepopWaitSecond, EnemyData = new CDataLayoutEnemyData() { - PositionIndex = (byte) packet.SetId, + PositionIndex = (byte)packet.SetId, EnemyInfo = enemyKilled.asCDataStageLayoutEnemyPresetEnemyInfoClient() } }; @@ -90,182 +90,185 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne enemyKilled.IsKilled = true; } - List group = client.Party.InstanceEnemyManager.GetInstancedEnemies(stageId); - bool groupDestroyed = group.Where(x => x.IsRequired).All(x => x.IsKilled); - if (groupDestroyed) + Server.Database.ExecuteInTransaction(connectionIn => { - bool IsAreaBoss = false; - foreach (var enemy in group) + List group = client.Party.InstanceEnemyManager.GetInstancedEnemies(stageId); + bool groupDestroyed = group.Where(x => x.IsRequired).All(x => x.IsKilled); + if (groupDestroyed) { - IsAreaBoss = IsAreaBoss || enemy.IsAreaBoss; - if (IsAreaBoss) + + bool IsAreaBoss = false; + foreach (var enemy in group) { - break; + IsAreaBoss = IsAreaBoss || enemy.IsAreaBoss; + if (IsAreaBoss) + { + break; + } } - } - // This is used for quests and things like key door monsters - S2CInstanceEnemyGroupDestroyNtc groupDestroyedNtc = new S2CInstanceEnemyGroupDestroyNtc() - { - LayoutId = packet.LayoutId, - IsAreaBoss = IsAreaBoss && (client.GameMode == GameMode.Normal) - }; - client.Party.EnqueueToAll(groupDestroyedNtc, queuedPackets); - //client.Party.SendToAll(groupDestroyedNtc); + // This is used for quests and things like key door monsters + S2CInstanceEnemyGroupDestroyNtc groupDestroyedNtc = new S2CInstanceEnemyGroupDestroyNtc() + { + LayoutId = packet.LayoutId, + IsAreaBoss = IsAreaBoss && (client.GameMode == GameMode.Normal) + }; + client.Party.EnqueueToAll(groupDestroyedNtc, queuedPackets); + //client.Party.SendToAll(groupDestroyedNtc); - if (IsAreaBoss && client.GameMode == GameMode.BitterblackMaze) - { - foreach (var memberClient in client.Party.Clients) + if (IsAreaBoss && client.GameMode == GameMode.BitterblackMaze) { - var ntcs = BitterblackMazeManager.HandleTierClear(_gameServer, memberClient, memberClient.Character, stageId); - queuedPackets.AddRange(ntcs); + foreach (var memberClient in client.Party.Clients) + { + var ntcs = BitterblackMazeManager.HandleTierClear(_gameServer, memberClient, memberClient.Character, stageId, connectionIn); + queuedPackets.AddRange(ntcs); + } } } - } - - if (packet.IsNoBattleReward) - { - return new(); - } - var dropItemNtc = new S2CInstancePopDropItemNtc() - { - LayoutId = packet.LayoutId, - SetId = packet.SetId, - MdlType = enemyKilled.DropsTable.MdlType, - PosX = packet.DropPosX, - PosY = packet.DropPosY, - PosZ = packet.DropPosZ - }; - foreach (var partyMemberClient in client.Party.Clients) - { - // If the enemy is quest controlled, then either get from the quest loot drop, or the general one. - List instancedGatheringItems = new List(); - - // Items from kill an enemy normally - instancedGatheringItems.AddRange(IsQuestControlled ? - partyMemberClient.InstanceQuestDropManager.GenerateEnemyLoot(quest, enemyKilled, packet.LayoutId, packet.SetId) : - partyMemberClient.InstanceDropItemManager.GetAssets(layoutId, (int)packet.SetId)); - - // Items for any server events which might be active - instancedGatheringItems.AddRange(partyMemberClient.InstanceEventDropItemManager.GenerateEventItems(partyMemberClient, enemyKilled, packet.LayoutId, packet.SetId)); - - // If the roll was unlucky, there is a chance that no bag will show. - if (instancedGatheringItems.Where(x => x.ItemNum > 0).Any()) + if (packet.IsNoBattleReward) { - partyMemberClient.Enqueue(dropItemNtc, queuedPackets); - //partyMemberClient.Send(dropItemNtc); + return; } - } - - uint calcExp = _gameServer.ExpManager.GetAdjustedExp(client.GameMode, RewardSource.Enemy, client.Party, enemyKilled.GetDroppedExperience(), enemyKilled.Lv); - uint calcPP = enemyKilled.GetDroppedPlayPoints(); - - foreach (PartyMember member in client.Party.Members) - { - if (member.JoinState != JoinState.On) continue; // Only fully joined members get rewards. - - uint gainedExp = calcExp; - uint gainedPP = calcPP; - - GameClient memberClient; - CharacterCommon memberCharacter; - if (member is PlayerPartyMember playerMember) + var dropItemNtc = new S2CInstancePopDropItemNtc() { - memberClient = playerMember.Client; - memberCharacter = memberClient.Character; - - if (memberCharacter.Stage.Id != stageId.Id) continue; // Only nearby allies get XP. - - if (memberClient.Character.ActiveCharacterPlayPointData.PlayPoint.ExpMode == ExpMode.Experience && !IsQuestControlled) - { - gainedPP = 0; - } - else if (!IsQuestControlled) - { - gainedExp = 0; - } + LayoutId = packet.LayoutId, + SetId = packet.SetId, + MdlType = enemyKilled.DropsTable.MdlType, + PosX = packet.DropPosX, + PosY = packet.DropPosY, + PosZ = packet.DropPosZ + }; + foreach (var partyMemberClient in client.Party.Clients) + { + // If the enemy is quest controlled, then either get from the quest loot drop, or the general one. + List instancedGatheringItems = new List(); - // TODO: Add transaction? - var huntPackets = playerMember.QuestState.HandleEnemyHuntRequests(enemyKilled); - queuedPackets.AddRange(huntPackets); + // Items from kill an enemy normally + instancedGatheringItems.AddRange(IsQuestControlled ? + partyMemberClient.InstanceQuestDropManager.GenerateEnemyLoot(quest, enemyKilled, packet.LayoutId, packet.SetId) : + partyMemberClient.InstanceDropItemManager.GetAssets(layoutId, (int)packet.SetId)); - S2CItemUpdateCharacterItemNtc updateCharacterItemNtc = new S2CItemUpdateCharacterItemNtc(); + // Items for any server events which might be active + instancedGatheringItems.AddRange(partyMemberClient.InstanceEventDropItemManager.GenerateEventItems(partyMemberClient, enemyKilled, packet.LayoutId, packet.SetId)); - if (enemyKilled.BloodOrbs > 0) + // If the roll was unlucky, there is a chance that no bag will show. + if (instancedGatheringItems.Where(x => x.ItemNum > 0).Any()) { - // Drop BO - uint gainedBo = enemyKilled.BloodOrbs; - uint bonusBo = (uint)(enemyKilled.BloodOrbs * _gameServer.GpCourseManager.EnemyBloodOrbBonus()); - CDataUpdateWalletPoint boUpdateWalletPoint = _gameServer.WalletManager.AddToWallet(memberClient.Character, WalletType.BloodOrbs, gainedBo + bonusBo, bonusBo); - updateCharacterItemNtc.UpdateWalletList.Add(boUpdateWalletPoint); + partyMemberClient.Enqueue(dropItemNtc, queuedPackets); + //partyMemberClient.Send(dropItemNtc); } + } - if (enemyKilled.HighOrbs > 0) - { - // Drop HO - uint gainedHo = enemyKilled.HighOrbs; - CDataUpdateWalletPoint hoUpdateWalletPoint = _gameServer.WalletManager.AddToWallet(memberClient.Character, WalletType.HighOrbs, gainedHo); - } - if (updateCharacterItemNtc.UpdateItemList.Count != 0 || updateCharacterItemNtc.UpdateWalletList.Count != 0) - { - memberClient.Enqueue(updateCharacterItemNtc, queuedPackets); - //memberClient.Send(updateCharacterItemNtc); - } + uint calcExp = _gameServer.ExpManager.GetAdjustedExp(client.GameMode, RewardSource.Enemy, client.Party, enemyKilled.GetDroppedExperience(), enemyKilled.Lv); + uint calcPP = enemyKilled.GetDroppedPlayPoints(); - if (gainedPP > 0) - { - var ntc = _gameServer.PPManager.AddPlayPoint(memberClient, gainedPP, type: 1); - memberClient.Enqueue(ntc, queuedPackets); - } - - if (gainedExp > 0) - { - var ntcs = _gameServer.ExpManager.AddExp(memberClient, memberCharacter, gainedExp, RewardSource.Enemy); - queuedPackets.AddRange(ntcs); - } - } - else if (member is PawnPartyMember pawnMember) + foreach (PartyMember member in client.Party.Members) { - Pawn pawn = pawnMember.Pawn; - memberClient = _gameServer.ClientLookup.GetClientByCharacterId(pawn.CharacterId); - memberCharacter = pawn; + if (member.JoinState != JoinState.On) continue; // Only fully joined members get rewards. - if (memberClient.Character.Stage.Id != stageId.Id || pawn.IsRented) + uint gainedExp = calcExp; + uint gainedPP = calcPP; + + GameClient memberClient; + CharacterCommon memberCharacter; + if (member is PlayerPartyMember playerMember) { - // Only nearby allies get XP - // and non-rented pawns - continue; + memberClient = playerMember.Client; + memberCharacter = memberClient.Character; + + if (memberCharacter.Stage.Id != stageId.Id) continue; // Only nearby allies get XP. + + if (memberClient.Character.ActiveCharacterPlayPointData.PlayPoint.ExpMode == ExpMode.Experience && !IsQuestControlled) + { + gainedPP = 0; + } + else if (!IsQuestControlled) + { + gainedExp = 0; + } + + var huntPackets = playerMember.QuestState.HandleEnemyHuntRequests(enemyKilled, connectionIn); + queuedPackets.AddRange(huntPackets); + + S2CItemUpdateCharacterItemNtc updateCharacterItemNtc = new S2CItemUpdateCharacterItemNtc(); + + if (enemyKilled.BloodOrbs > 0) + { + // Drop BO + uint gainedBo = enemyKilled.BloodOrbs; + uint bonusBo = (uint)(enemyKilled.BloodOrbs * _gameServer.GpCourseManager.EnemyBloodOrbBonus()); + CDataUpdateWalletPoint boUpdateWalletPoint = _gameServer.WalletManager.AddToWallet(memberClient.Character, WalletType.BloodOrbs, gainedBo + bonusBo, bonusBo, connectionIn: connectionIn); + updateCharacterItemNtc.UpdateWalletList.Add(boUpdateWalletPoint); + } + + if (enemyKilled.HighOrbs > 0) + { + // Drop HO + uint gainedHo = enemyKilled.HighOrbs; + CDataUpdateWalletPoint hoUpdateWalletPoint = _gameServer.WalletManager.AddToWallet(memberClient.Character, WalletType.HighOrbs, gainedHo, connectionIn: connectionIn); + } + + if (updateCharacterItemNtc.UpdateItemList.Count != 0 || updateCharacterItemNtc.UpdateWalletList.Count != 0) + { + memberClient.Enqueue(updateCharacterItemNtc, queuedPackets); + //memberClient.Send(updateCharacterItemNtc); + } + + if (gainedPP > 0) + { + var ntc = _gameServer.PPManager.AddPlayPoint(memberClient, gainedPP, type: 1, connectionIn:connectionIn); + memberClient.Enqueue(ntc, queuedPackets); + } + + if (gainedExp > 0) + { + var ntcs = _gameServer.ExpManager.AddExp(memberClient, memberCharacter, gainedExp, RewardSource.Enemy, connectionIn: connectionIn); + queuedPackets.AddRange(ntcs); + } } - - uint pawnExp = gainedExp; - if (_gameServer.ExpManager.RequiresPawnCatchup(client.GameMode, client.Party, pawn)) + else if (member is PawnPartyMember pawnMember) { - pawnExp = _gameServer.ExpManager.GetAdjustedPawnExp(client.GameMode, RewardSource.Enemy, client.Party, pawn, enemyKilled.GetDroppedExperience(), enemyKilled.Lv); + Pawn pawn = pawnMember.Pawn; + memberClient = _gameServer.ClientLookup.GetClientByCharacterId(pawn.CharacterId); + memberCharacter = pawn; + + if (memberClient.Character.Stage.Id != stageId.Id || pawn.IsRented) + { + // Only nearby allies get XP + // and non-rented pawns + continue; + } + + uint pawnExp = gainedExp; + if (_gameServer.ExpManager.RequiresPawnCatchup(client.GameMode, client.Party, pawn)) + { + pawnExp = _gameServer.ExpManager.GetAdjustedPawnExp(client.GameMode, RewardSource.Enemy, client.Party, pawn, enemyKilled.GetDroppedExperience(), enemyKilled.Lv); + } + + if (pawnExp > 0) + { + var ntcs = _gameServer.ExpManager.AddExp(memberClient, memberCharacter, pawnExp, RewardSource.Enemy, connectionIn: connectionIn); + queuedPackets.AddRange(ntcs); + } } - - if (pawnExp > 0) + else { - var ntcs = _gameServer.ExpManager.AddExp(memberClient, memberCharacter, pawnExp, RewardSource.Enemy); - queuedPackets.AddRange(ntcs); + throw new Exception("Unknown member type"); } } - else - { - throw new Exception("Unknown member type"); - } - } + }); queuedPackets.Send(); // TODO: EnemyId and KillNum return new S2CInstanceEnemyKillRes() { - EnemyId = enemyKilled.Id, - KillNum = 1 + EnemyId = packet.IsNoBattleReward ? 0u : enemyKilled.Id, + KillNum = packet.IsNoBattleReward ? 0u : 1u }; } diff --git a/Arrowgene.Ddon.GameServer/Quests/QuestStateManager.cs b/Arrowgene.Ddon.GameServer/Quests/QuestStateManager.cs index a2b7d58ef..0cbc944e9 100644 --- a/Arrowgene.Ddon.GameServer/Quests/QuestStateManager.cs +++ b/Arrowgene.Ddon.GameServer/Quests/QuestStateManager.cs @@ -864,7 +864,7 @@ public override void UpdatePriorityQuestList(GameClient requestingClient, DbConn throw new NotImplementedException(); } - public PacketQueue HandleEnemyHuntRequests(Enemy enemy) + public PacketQueue HandleEnemyHuntRequests(Enemy enemy, DbConnection? connectionIn = null) { if (Member.Client.Character.GameMode != GameMode.Normal) { @@ -881,6 +881,12 @@ public PacketQueue HandleEnemyHuntRequests(Enemy enemy) QuestState questState = quest.Value; QuestEnemyHuntRecord huntRecord = questState.UpdateHuntRequest(enemy); + if (Member.Client.Party.ExmInProgress && questObject.QuestType == QuestType.Light) + { + // The UI indicates that light quests cannot progress during EXMs. + continue; + } + if (huntRecord != null) { if (questObject.SaveWorkAsStep) @@ -890,7 +896,7 @@ public PacketQueue HandleEnemyHuntRequests(Enemy enemy) // Stage 0 -> Not accepted, should never occur. // Stage 1 -> Accepted, but no work done // Stage N+1 -> Accepted, but with N work done. - Server.Database.UpdateQuestProgress(Member.Client.Character.CommonId, questState.QuestScheduleId, questState.QuestType, huntRecord.AmountHunted + 1); + Server.Database.UpdateQuestProgress(Member.Client.Character.CommonId, questState.QuestScheduleId, questState.QuestType, huntRecord.AmountHunted + 1, connectionIn); } S2CQuestQuestProgressWorkSaveNtc ntc = new() diff --git a/Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs b/Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs index 97aee3d7b..f44d51e46 100644 --- a/Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs +++ b/Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs @@ -374,13 +374,11 @@ public bool UpdateRentalPawnSlot(uint characterId, uint num) public uint SelectBBMCharacterId(uint characterId, DbConnection? connectionIn = null) { return 0; } public uint SelectBBMNormalCharacterId(uint bbmCharacterId) { return 0; } public bool InsertBBMProgress(uint characterId, ulong startTime, uint contentId, BattleContentMode contentMode, uint tier, bool killedDeath, ulong lastTicketTime) { return true; } - public bool UpdateBBMProgress(uint characterId, ulong startTime, uint contentId, BattleContentMode contentMode, uint tier, bool killedDeath, ulong lastTicketTime) { return true; } - public bool UpdateBBMProgress(uint characterId, BitterblackMazeProgress progress) { return true; } + public bool UpdateBBMProgress(uint characterId, BitterblackMazeProgress progress, DbConnection? connectionIn = null) { return true; } public bool RemoveBBMProgress(uint characterId) { return true; } public BitterblackMazeProgress SelectBBMProgress(uint characterId) { return new BitterblackMazeProgress(); } public bool InsertBBMRewards(uint characterId, uint goldMarks, uint silverMarks, uint redMarks) { return true; } - public bool UpdateBBMRewards(uint characterId, BitterblackMazeRewards rewards) { return true; } - public bool UpdateBBMRewards(uint characterId, uint goldMarks, uint silverMarks, uint redMarks) { return true; } + public bool UpdateBBMRewards(uint characterId, BitterblackMazeRewards rewards, DbConnection? connectionIn = null) { return true; } public bool RemoveBBMRewards(uint characterId) { return true; } public BitterblackMazeRewards SelectBBMRewards(uint characterId) { return new BitterblackMazeRewards(); } public bool InsertBBMContentTreasure(uint characterId, BitterblackMazeTreasure treasure, DbConnection? connectionIn = null) { return true; } From af0b76bd5ec2580eb43c5250acf9b1319a1e8059 Mon Sep 17 00:00:00 2001 From: Ryan Yappert Date: Fri, 8 Nov 2024 21:17:58 -0800 Subject: [PATCH 04/12] Queue-ify a bunch of packet handling related to QuestProgressHandler so I/O is outside the DB lock. --- .../Sql/Core/DdonSqlQuestProgress.cs | 6 +-- .../Command/Commands/FinishQuestCommand.cs | 2 +- .../Handler/QuestCancelHandler.cs | 4 +- .../QuestCancelPriorityQuestHandler.cs | 2 +- .../QuestGetCycleContentsStateListHandler.cs | 46 ++++++---------- .../Handler/QuestQuestProgressHandler.cs | 25 ++++++--- .../Quests/QuestStateManager.cs | 52 ++++++++++++------- 7 files changed, 73 insertions(+), 64 deletions(-) diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlQuestProgress.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlQuestProgress.cs index b500616d7..e94083716 100644 --- a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlQuestProgress.cs +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlQuestProgress.cs @@ -20,11 +20,11 @@ public abstract partial class DdonSqlDb : SqlDb GetQuestProgressByType(uint characterCommonId, QuestType questType, DbConnection? connectionIn = null) diff --git a/Arrowgene.Ddon.GameServer/Chat/Command/Commands/FinishQuestCommand.cs b/Arrowgene.Ddon.GameServer/Chat/Command/Commands/FinishQuestCommand.cs index 3be859907..ae5f6bb91 100644 --- a/Arrowgene.Ddon.GameServer/Chat/Command/Commands/FinishQuestCommand.cs +++ b/Arrowgene.Ddon.GameServer/Chat/Command/Commands/FinishQuestCommand.cs @@ -70,7 +70,7 @@ public override void Execute(string[] command, GameClient client, ChatMessage me else { client.Party.QuestState.CompleteQuestProgress(quest.QuestScheduleId); - client.Party.QuestState.UpdatePriorityQuestList(client); + client.Party.QuestState.UpdatePriorityQuestList(client).Send(); client.Party.SendToAll(completeNtc); if (quest.ResetPlayerAfterQuest) diff --git a/Arrowgene.Ddon.GameServer/Handler/QuestCancelHandler.cs b/Arrowgene.Ddon.GameServer/Handler/QuestCancelHandler.cs index 309200987..e73b3cc25 100644 --- a/Arrowgene.Ddon.GameServer/Handler/QuestCancelHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/QuestCancelHandler.cs @@ -28,7 +28,7 @@ public override S2CQuestQuestCancelRes Handle(GameClient client, C2SQuestQuestCa if (isPriority && (client.Party.IsSolo || client.Party.Leader?.Client == client)) { - client.Party.QuestState.UpdatePriorityQuestList(client.Party.Leader.Client); + client.Party.QuestState.UpdatePriorityQuestList(client.Party.Leader.Client).Send(); } } else @@ -46,7 +46,7 @@ public override S2CQuestQuestCancelRes Handle(GameClient client, C2SQuestQuestCa if (isPriority) { - client.Party.QuestState.UpdatePriorityQuestList(client); + client.Party.QuestState.UpdatePriorityQuestList(client).Send(); } } } diff --git a/Arrowgene.Ddon.GameServer/Handler/QuestCancelPriorityQuestHandler.cs b/Arrowgene.Ddon.GameServer/Handler/QuestCancelPriorityQuestHandler.cs index 7ba39f415..0ff108d32 100644 --- a/Arrowgene.Ddon.GameServer/Handler/QuestCancelPriorityQuestHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/QuestCancelPriorityQuestHandler.cs @@ -28,7 +28,7 @@ public override S2CQuestCancelPriorityQuestRes Handle(GameClient client, C2SQues { Server.Database.DeletePriorityQuest(client.Character.CommonId, packet.QuestScheduleId); - client.Party.QuestState.UpdatePriorityQuestList(client); + client.Party.QuestState.UpdatePriorityQuestList(client).Send(); return new S2CQuestCancelPriorityQuestRes() { diff --git a/Arrowgene.Ddon.GameServer/Handler/QuestGetCycleContentsStateListHandler.cs b/Arrowgene.Ddon.GameServer/Handler/QuestGetCycleContentsStateListHandler.cs index 4fe9d486e..f6c854439 100644 --- a/Arrowgene.Ddon.GameServer/Handler/QuestGetCycleContentsStateListHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/QuestGetCycleContentsStateListHandler.cs @@ -1,16 +1,11 @@ -using Arrowgene.Buffers; using Arrowgene.Ddon.GameServer.Characters; using Arrowgene.Ddon.GameServer.Dump; using Arrowgene.Ddon.Server; -using Arrowgene.Ddon.Server.Network; using Arrowgene.Ddon.Shared.Entity; using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Entity.Structure; using Arrowgene.Ddon.Shared.Model.Quest; -using Arrowgene.Ddon.Shared.Network; using Arrowgene.Logging; -using System.Collections; -using System.Collections.Generic; using System.Linq; namespace Arrowgene.Ddon.GameServer.Handler @@ -45,17 +40,17 @@ public override S2CQuestGetCycleContentsStateListRes Handle(GameClient client, C var completedMsq = client.Character.CompletedQuests.Values.Where(x => x.QuestType == QuestType.Main); foreach (var msq in completedMsq) { - ntc.MainQuestIdList.Add(new CDataQuestId() { QuestId = (uint) msq.QuestId }); + ntc.MainQuestIdList.Add(new CDataQuestId() { QuestId = (uint)msq.QuestId }); } var completedTutorials = client.Character.CompletedQuests.Values.Where(x => x.QuestType == QuestType.Tutorial); foreach (var tut in completedTutorials) { - ntc.TutorialQuestIdList.Add(new CDataQuestId() { QuestId = (uint) tut.QuestId}); + ntc.TutorialQuestIdList.Add(new CDataQuestId() { QuestId = (uint)tut.QuestId }); } - var tutorialQuestInProgress = Server.Database.GetQuestProgressByType(client.Character.CommonId, QuestType.Tutorial); - foreach (var questProgress in tutorialQuestInProgress) + var allQuestsInProgress = Server.Database.GetQuestProgressByType(client.Character.CommonId, QuestType.All); + foreach (var questProgress in allQuestsInProgress) { if (!QuestManager.IsQuestEnabled(questProgress.QuestScheduleId)) { @@ -63,29 +58,22 @@ public override S2CQuestGetCycleContentsStateListRes Handle(GameClient client, C } var quest = QuestManager.GetQuestByScheduleId(questProgress.QuestScheduleId); - var tutorialQuest = quest.ToCDataTutorialQuestOrderList(questProgress.Step); - ntc.TutorialQuestOrderList.Add(tutorialQuest); - } - var wildHuntsInProgress = Server.Database.GetQuestProgressByType(client.Character.CommonId, QuestType.WildHunt); - foreach (var questProgress in wildHuntsInProgress) - { - if (!QuestManager.IsQuestEnabled(questProgress.QuestScheduleId)) + switch (questProgress.QuestType) { - continue; + case QuestType.Tutorial: + var tutorialQuest = quest.ToCDataTutorialQuestOrderList(questProgress.Step); + ntc.TutorialQuestOrderList.Add(tutorialQuest); + break; + case QuestType.WildHunt: + var mobHuntQuest = quest.ToCDataMobHuntQuestOrderList(questProgress.Step); + ntc.MobHuntQuestOrderList.Add(mobHuntQuest); + break; + case QuestType.Light: + var lightQuest = quest.ToCDataLightQuestOrderList(questProgress.Step); + ntc.LightQuestOrderList.Add(lightQuest); + break; } - - var quest = QuestManager.GetQuestByScheduleId(questProgress.QuestScheduleId); - var mobHuntQuest = quest.ToCDataMobHuntQuestOrderList(questProgress.Step); - ntc.MobHuntQuestOrderList.Add(mobHuntQuest); - } - - var lightQuestInProgress = Server.Database.GetQuestProgressByType(client.Character.CommonId, QuestType.Light); - foreach (var questProgress in lightQuestInProgress) - { - var quest = QuestManager.GetQuestByScheduleId(questProgress.QuestScheduleId); - var lightQuest = quest.ToCDataLightQuestOrderList(questProgress.Step); - ntc.LightQuestOrderList.Add(lightQuest); } if (client.Party != null) diff --git a/Arrowgene.Ddon.GameServer/Handler/QuestQuestProgressHandler.cs b/Arrowgene.Ddon.GameServer/Handler/QuestQuestProgressHandler.cs index b6792ebf7..6b9cb5b31 100644 --- a/Arrowgene.Ddon.GameServer/Handler/QuestQuestProgressHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/QuestQuestProgressHandler.cs @@ -2,6 +2,7 @@ using Arrowgene.Ddon.GameServer.Party; using Arrowgene.Ddon.GameServer.Quests; using Arrowgene.Ddon.Server; +using Arrowgene.Ddon.Server.Network; using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Entity.Structure; using Arrowgene.Ddon.Shared.Model.Quest; @@ -21,6 +22,8 @@ public QuestQuestProgressHandler(DdonGameServer server) : base(server) public override void Handle(GameClient client, StructurePacket packet) { + PacketQueue packets = new(); + QuestProgressState questProgressState = QuestProgressState.InProgress; S2CQuestQuestProgressRes res = new S2CQuestQuestProgressRes(); res.QuestScheduleId = packet.Structure.QuestScheduleId; @@ -90,7 +93,8 @@ public override void Handle(GameClient client, StructurePacket 0) { - client.Send(updateCharacterItemNtc); + client.Enqueue(updateCharacterItemNtc, packets); } foreach (var expPoint in quest.ExpRewards) { if (expPoint.Type == ExpType.ExperiencePoints) { - server.ExpManager.AddExpNtc(client, client.Character, expPoint.Reward, RewardSource.Quest, quest.QuestType, connectionIn); + var ntcs = server.ExpManager.AddExp(client, client.Character, expPoint.Reward, RewardSource.Quest, quest.QuestType, connectionIn); + packets.AddRange(ntcs); } else if (expPoint.Type == ExpType.JobPoints) { - server.ExpManager.AddJp(client, client.Character, expPoint.Reward, RewardSource.Quest, quest.QuestType, connectionIn); + var ntcs = server.ExpManager.AddJp(client, client.Character, expPoint.Reward, RewardSource.Quest, quest.QuestType, connectionIn); + packets.AddRange(ntcs); } else if (expPoint.Type == ExpType.PlayPoints) { - server.PPManager.AddPlayPointNtc(client, expPoint.Reward, type: 1, connectionIn: connectionIn); + var ntc = server.PPManager.AddPlayPoint(client, expPoint.Reward, type: 1, connectionIn: connectionIn); + client.Enqueue(ntc, packets); } } + + return packets; } public void ResetInstance() @@ -680,8 +687,9 @@ public override bool CompleteQuestProgress(uint questScheduleId, DbConnection? c return true; } - public override bool DistributeQuestRewards(uint questScheduleId, DbConnection? connectionIn = null) + public override PacketQueue DistributeQuestRewards(uint questScheduleId, DbConnection? connectionIn = null) { + PacketQueue packets = new(); Quest quest = GetQuest(questScheduleId); var questState = GetQuestState(quest); @@ -709,17 +717,20 @@ public override bool DistributeQuestRewards(uint questScheduleId, DbConnection? } // Check for Exp, Rift and Gold Rewards - SendWalletRewards(Server, memberClient, quest, connectionIn); + var ntcs = SendWalletRewards(Server, memberClient, quest, connectionIn); + packets.AddRange(ntcs); } - return true; + return packets; } - public override void UpdatePriorityQuestList(GameClient requestingClient, DbConnection? connectionIn = null) + public override PacketQueue UpdatePriorityQuestList(GameClient requestingClient, DbConnection? connectionIn = null) { + PacketQueue packets = new(); + if (Party.Leader is null || requestingClient != Party.Leader.Client) { - return; + return packets; } var leaderClient = Party.Leader.Client; @@ -737,7 +748,9 @@ public override void UpdatePriorityQuestList(GameClient requestingClient, DbConn var questState = questStateManager.GetQuestState(priorityQuestScheduleId); prioNtc.PriorityQuestList.Add(quest.ToCDataPriorityQuest(questState.Step)); } - Party.SendToAll(prioNtc); + Party.EnqueueToAll(prioNtc, packets); + + return packets; } public override bool UpdateQuestProgress( uint questScheduleId, DbConnection? connectionIn = null) @@ -782,7 +795,7 @@ public override bool UpdateQuestProgress(uint questScheduleId, DbConnection? con var questState = GetQuestState(questScheduleId); var quest = QuestManager.GetQuestByScheduleId(questScheduleId); - var result = Server.Database.GetQuestProgressByScheduleId(Member.Client.Character.CommonId, questState.QuestScheduleId); + var result = Server.Database.GetQuestProgressByScheduleId(Member.Client.Character.CommonId, questState.QuestScheduleId, connectionIn); if (result == null) { return false; @@ -793,7 +806,7 @@ public override bool UpdateQuestProgress(uint questScheduleId, DbConnection? con return false; } - Server.Database.UpdateQuestProgress(Member.Client.Character.CommonId, questState.QuestScheduleId, questState.QuestType, questState.Step + 1); + Server.Database.UpdateQuestProgress(Member.Client.Character.CommonId, questState.QuestScheduleId, questState.QuestType, questState.Step + 1, connectionIn); questState.Step += 1; @@ -845,21 +858,20 @@ public override bool CompleteQuestProgress(uint questScheduleId, DbConnection? c return true; } - public override bool DistributeQuestRewards(uint questScheduleId, DbConnection? connectionIn = null) + public override PacketQueue DistributeQuestRewards(uint questScheduleId, DbConnection? connectionIn = null) { Quest quest = GetQuest(questScheduleId); var questState = GetQuestState(quest); if (quest.HasRewards()) { - Server.RewardManager.AddQuestRewards(Member.Client, quest); + Server.RewardManager.AddQuestRewards(Member.Client, quest, connectionIn); } // Check for Exp, Rift and Gold Rewards - SendWalletRewards(Server, Member.Client, quest); - return true; + return SendWalletRewards(Server, Member.Client, quest, connectionIn); } - public override void UpdatePriorityQuestList(GameClient requestingClient, DbConnection? connectionIn = null) + public override PacketQueue UpdatePriorityQuestList(GameClient requestingClient, DbConnection? connectionIn = null) { throw new NotImplementedException(); } From d17dfa953e9003784840dc46a94fde55866d9028 Mon Sep 17 00:00:00 2001 From: Ryan Yappert Date: Fri, 1 Nov 2024 01:37:29 -0700 Subject: [PATCH 05/12] Overhaul contacts/friends list stuff. --- Arrowgene.Ddon.Database/IDatabase.cs | 1 + .../Sql/Core/DdonSqlDbContactList.cs | 64 +++++++++++++- .../Characters/ContactListManager.cs | 4 +- ...acterCommunityCharacterStatusGetHandler.cs | 49 +++++------ .../Handler/FriendApplyFriendListHandler.cs | 73 +++++---------- .../Handler/FriendGetFriendListHandler.cs | 66 ++++++-------- .../FriendRegisterFavoriteFriendHandler.cs | 37 +++----- .../Handler/FriendRemoveFriendHandler.cs | 88 +++++++------------ .../Entity/EntitySerializer.cs | 1 + .../C2SFriendGetFriendListReq.cs | 24 +++++ .../C2SFriendRegisterFavoriteFriendReq.cs | 12 +-- .../C2SFriendRemoveFriendReq.cs | 6 +- ...CharacterCommunityCharacterStatusGetRes.cs | 12 +-- .../S2CFriendGetFriendListRes.cs | 13 +-- .../Entity/Structure/CDataFriendInfo.cs | 18 ++-- .../Database/DatabaseMigratorTest.cs | 1 + 16 files changed, 242 insertions(+), 227 deletions(-) create mode 100644 Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SFriendGetFriendListReq.cs diff --git a/Arrowgene.Ddon.Database/IDatabase.cs b/Arrowgene.Ddon.Database/IDatabase.cs index 141bda8bf..9cc1a51de 100644 --- a/Arrowgene.Ddon.Database/IDatabase.cs +++ b/Arrowgene.Ddon.Database/IDatabase.cs @@ -382,6 +382,7 @@ bool requestedFavorite List SelectContactsByCharacterId(uint characterId); ContactListEntity SelectContactsByCharacterId(uint characterId1, uint characterId2); ContactListEntity SelectContactListById(uint id); + List<(ContactListEntity, CDataCharacterListElement)> SelectFullContactListByCharacterId(uint characterId, DbConnection? connectionIn = null); // Dragon Force Augmentation bool InsertIfNotExistsDragonForceAugmentation( diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbContactList.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbContactList.cs index 0370328b7..a39dd6354 100644 --- a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbContactList.cs +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbContactList.cs @@ -1,6 +1,9 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Data.Common; +using System.Security.Claims; +using Arrowgene.Ddon.Shared.Entity.Structure; using Arrowgene.Ddon.Shared.Model; +using Arrowgene.Ddon.Shared.Model.Clan; namespace Arrowgene.Ddon.Database.Sql.Core { @@ -31,6 +34,22 @@ public abstract partial class DdonSqlDb : SqlDb SelectFullContactListByCharacterId(uint characterId, DbConnection? connectionIn = null) + { + List<(ContactListEntity, CDataCharacterListElement)> list = new(); + + bool isTransaction = connectionIn is not null; + TCon connection = (TCon)(connectionIn ?? OpenNewConnection()); + try + { + ExecuteReader( + connection, + SqlSelectFullContactsByCharacterId, + command => + { + AddParameter(command, "@character_id", characterId); + }, + reader => + { + while (reader.Read()) + { + var contactListEntity = ReadContactListEntity(reader); + var characterListElement = new CDataCharacterListElement(); + + characterListElement.CommunityCharacterBaseInfo.CharacterId = GetUInt32(reader, "other_id"); + characterListElement.CommunityCharacterBaseInfo.CharacterName.FirstName = GetString(reader, "first_name"); + characterListElement.CommunityCharacterBaseInfo.CharacterName.LastName = GetString(reader, "last_name"); + characterListElement.CommunityCharacterBaseInfo.ClanName = GetStringNullable(reader, "clan_name") ?? string.Empty; + characterListElement.CurrentJobBaseInfo.Job = (JobId)GetByte(reader, "job"); + characterListElement.CurrentJobBaseInfo.Level = GetByte(reader, "lv"); + characterListElement.EntryJobBaseInfo = characterListElement.CurrentJobBaseInfo; + characterListElement.MatchingProfile = GetString(reader, "comment"); + + list.Add((contactListEntity, characterListElement)); + } + }); + } + finally + { + if (!isTransaction) connection.Dispose(); + } + + return list; + } + private ContactListEntity ReadContactListEntity(TReader reader) { ContactListEntity e = new ContactListEntity(); diff --git a/Arrowgene.Ddon.GameServer/Characters/ContactListManager.cs b/Arrowgene.Ddon.GameServer/Characters/ContactListManager.cs index 3bcc84720..acccc3130 100644 --- a/Arrowgene.Ddon.GameServer/Characters/ContactListManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/ContactListManager.cs @@ -59,13 +59,13 @@ public static CDataCharacterListElement CharacterToListEml(Character c) }; } - public static CDataFriendInfo CharacterToFriend(Character c, uint unFriendNo, bool isFavorite) + public static CDataFriendInfo CharacterToFriend(Character c, uint friendNo, bool isFavorite) { return new CDataFriendInfo() { IsFavorite = isFavorite, PendingStatus = 0x00, // TODO - UnFriendNo = unFriendNo, + FriendNo = friendNo, CharacterListElement = CharacterToListEml(c) }; diff --git a/Arrowgene.Ddon.GameServer/Handler/CharacterCommunityCharacterStatusGetHandler.cs b/Arrowgene.Ddon.GameServer/Handler/CharacterCommunityCharacterStatusGetHandler.cs index 89fe0f9c5..eb5d0a7b8 100644 --- a/Arrowgene.Ddon.GameServer/Handler/CharacterCommunityCharacterStatusGetHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/CharacterCommunityCharacterStatusGetHandler.cs @@ -1,60 +1,59 @@ -using System.Collections.Generic; -using Arrowgene.Ddon.GameServer.Characters; using Arrowgene.Ddon.Server; using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Entity.Structure; using Arrowgene.Ddon.Shared.Model; -using Arrowgene.Ddon.Shared.Network; using Arrowgene.Logging; +using System.Collections.Generic; namespace Arrowgene.Ddon.GameServer.Handler { - public class CharacterCommunityCharacterStatusGetHandler : GameStructurePacketHandler + public class CharacterCommunityCharacterStatusGetHandler : GameRequestPacketHandler { private static readonly ServerLogger Logger = LogProvider.Logger(typeof(CharacterCommunityCharacterStatusGetHandler)); - public CharacterCommunityCharacterStatusGetHandler(DdonGameServer server) : base(server) { } - // public override PacketId Id => PacketId.C2S_CHARACTER_COMMUNITY_CHARACTER_STATUS_GET_REQ; - - public override void Handle(GameClient client, StructurePacket packet) + public override S2CCharacterCommunityCharacterStatusGetRes Handle(GameClient client, C2SCharacterCommunityCharacterStatusGetReq request) { - // client.Send(InGameDump.Dump_65); - List updateCharacterList = new List(); List updateMatchingProfileList = new List(); - - List friends = Database.SelectContactsByCharacterId(client.Character.CharacterId); - - foreach (var f in friends) + + List<(ContactListEntity, CDataCharacterListElement)> friends = Database.SelectFullContactListByCharacterId(client.Character.CharacterId); + + foreach ((ContactListEntity contact, CDataCharacterListElement character) in friends) { - if (f.Type != ContactListType.FriendList || f.Status != ContactListStatus.Accepted) + if (contact.Type != ContactListType.FriendList || contact.Status != ContactListStatus.Accepted) { continue; } - Character otherCharacter = - ContactListManager.getCharWithOnlineStatus(Server,Database, f.GetOtherCharacterId(client.Character.CharacterId)); - updateCharacterList.Add(ContactListManager.CharacterToListEml(otherCharacter)); + + var matchClient = Server.ClientLookup.GetClientByCharacterId(character.CommunityCharacterBaseInfo.CharacterId); + if (matchClient != null) + { + character.OnlineStatus = matchClient.Character?.OnlineStatus ?? OnlineStatus.Offline; + character.ServerId = (ushort)Server.Id; + } + + updateCharacterList.Add(character); updateMatchingProfileList.Add(new CDataUpdateMatchingProfileInfo() { - CharacterId = otherCharacter.CharacterId, - Comment = otherCharacter.MatchingProfile.Comment + CharacterId = character.CommunityCharacterBaseInfo.CharacterId, + Comment = character.MatchingProfile, }); } - + client.Send(new S2CCharacterCommunityCharacterStatusUpdateNtc() { UpdateCharacterList = updateCharacterList, UpdateMatchingProfileList = updateMatchingProfileList }); - - client.Send(new S2CCharacterCommunityCharacterStatusGetRes() + + return new S2CCharacterCommunityCharacterStatusGetRes() { - Result = updateCharacterList.Count + 1 - }); + Result = (uint)(updateCharacterList.Count + 1) // ??? + }; } } } diff --git a/Arrowgene.Ddon.GameServer/Handler/FriendApplyFriendListHandler.cs b/Arrowgene.Ddon.GameServer/Handler/FriendApplyFriendListHandler.cs index 7ed21827c..11b5b7955 100644 --- a/Arrowgene.Ddon.GameServer/Handler/FriendApplyFriendListHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/FriendApplyFriendListHandler.cs @@ -1,14 +1,13 @@ -using Arrowgene.Ddon.GameServer.Characters; +using Arrowgene.Ddon.GameServer.Characters; using Arrowgene.Ddon.Server; using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Entity.Structure; using Arrowgene.Ddon.Shared.Model; -using Arrowgene.Ddon.Shared.Network; using Arrowgene.Logging; namespace Arrowgene.Ddon.GameServer.Handler { - public class FriendApplyFriendListHandler : GameStructurePacketHandler + public class FriendApplyFriendListHandler : GameRequestPacketHandler { private static readonly ServerLogger Logger = LogProvider.Logger(typeof(FriendApplyFriendListHandler)); @@ -17,70 +16,52 @@ public FriendApplyFriendListHandler(DdonGameServer server) : base(server) { } - public override void Handle(GameClient client, StructurePacket packet) + public override S2CFriendApplyFriendRes Handle(GameClient client, C2SFriendApplyFriendReq request) { - - ContactListEntity existingFriend = Server.Database.SelectContactsByCharacterId(packet.Structure.CharacterId, client.Character.CharacterId); + + ContactListEntity existingFriend = Server.Database.SelectContactsByCharacterId(request.CharacterId, client.Character.CharacterId); if (existingFriend != null) { - uint errorCode = (uint)ErrorCode.ERROR_CODE_FAIL; + ErrorCode errorCode = ErrorCode.ERROR_CODE_FAIL; if (existingFriend.Type == ContactListType.BlackList && client.Character.CharacterId == existingFriend.RequesterCharacterId) { - errorCode = (uint)ErrorCode.ERROR_CODE_FRIEND_TARGET_IN_BLACK_LIST; + errorCode = ErrorCode.ERROR_CODE_FRIEND_TARGET_IN_BLACK_LIST; } else if (existingFriend.Status == ContactListStatus.Accepted) { - errorCode = (uint)ErrorCode.ERROR_CODE_FRIEND_TARGET_ALREADY_FRIEND; + errorCode = ErrorCode.ERROR_CODE_FRIEND_TARGET_ALREADY_FRIEND; } else if (existingFriend.Status == ContactListStatus.PendingApproval && client.Character.CharacterId == existingFriend.RequesterCharacterId) { - errorCode = (uint)ErrorCode.ERROR_CODE_FRIEND_TARGET_ALREADY_APPLYING; + errorCode = ErrorCode.ERROR_CODE_FRIEND_TARGET_ALREADY_APPLYING; } else if (existingFriend.Status == ContactListStatus.PendingApproval && client.Character.CharacterId == existingFriend.RequestedCharacterId) { - errorCode = (uint)ErrorCode.ERROR_CODE_FRIEND_TARGET_ALREADY_APPROVING; + errorCode = ErrorCode.ERROR_CODE_FRIEND_TARGET_ALREADY_APPROVING; } - var res = new S2CFriendApplyFriendRes() - { - FriendInfo = new CDataFriendInfo(), - Error = errorCode - }; - Logger.Error(client, $"already in contact list: {packet.Structure.CharacterId} - friend invitation"); - client.Send(res); - return; - } - - Character requestedChar = ContactListManager.getCharWithOnlineStatus(Server, Database, packet.Structure.CharacterId); - if (requestedChar == null) - { - var res = new S2CFriendApplyFriendRes(); - Logger.Error(client, $"not found CharacterId:{packet.Structure.CharacterId} for friend invitation"); - res.Error = (uint)ErrorCode.ERROR_CODE_FRIEND_TARGET_PARAM_NOT_FOUND; - client.Send(res); - return; + + throw new ResponseErrorException(errorCode, $"already in contact list: {request.CharacterId} - friend invitation."); } + Character requestedChar = Server.ClientLookup.GetClientByCharacterId(request.CharacterId)?.Character + ?? ContactListManager.getCharWithOnlineStatus(Server, Database, request.CharacterId) + ?? throw new ResponseErrorException(ErrorCode.ERROR_CODE_FRIEND_TARGET_PARAM_NOT_FOUND, $"not found CharacterId:{request.CharacterId} for friend invitation."); + int id = Database.InsertContact(client.Character.CharacterId, requestedChar.CharacterId, ContactListStatus.PendingApproval, ContactListType.FriendList, false, false); - + if (id < 1) { - var res = new S2CFriendApplyFriendRes() - { - FriendInfo = new CDataFriendInfo() - }; - Logger.Error(client, $"Problem saving friend request"); - res.Error = (uint)ErrorCode.ERROR_CODE_FAIL; - client.Send(res); - return; + throw new ResponseErrorException(ErrorCode.ERROR_CODE_FAIL, $"Problem saving friend request."); } + CDataFriendInfo requester = ContactListManager.CharacterToFriend(client.Character, (uint)id, false); CDataFriendInfo requested = ContactListManager.CharacterToFriend(requestedChar, (uint)id, false); - requester.UnFriendNo = (uint)id; - requested.UnFriendNo = (uint)id; + requester.FriendNo = (uint)id; + requested.FriendNo = (uint)id; - GameClient requestedClient = Server.ClientLookup.GetClientByCharacterId(packet.Structure.CharacterId); + GameClient requestedClient = Server.ClientLookup.GetClientByCharacterId(request.CharacterId); if (requestedClient != null) { var ntc = new S2CFriendApplyFriendNtc() @@ -90,16 +71,10 @@ public override void Handle(GameClient client, StructurePacket + public class FriendGetFriendListHandler : GameRequestPacketHandler { private static readonly ServerLogger Logger = LogProvider.Logger(typeof(FriendGetFriendListHandler)); - public FriendGetFriendListHandler(DdonGameServer server) : base(server) { } - public override PacketId Id => PacketId.C2S_FRIEND_GET_FRIEND_LIST_REQ; - - public override void Handle(GameClient client, IPacket packet) + public override S2CFriendGetFriendListRes Handle(GameClient client, C2SFriendGetFriendListReq request) { - // client.Send(InGameDump.Dump_60); - List fList = new List(); - List applList = new List(); - List apprList = new List(); - List friends = Database.SelectContactsByCharacterId(client.Character.CharacterId); - - foreach (var f in friends) + + S2CFriendGetFriendListRes res = new(); + + List<(ContactListEntity, CDataCharacterListElement)> friends = Database.SelectFullContactListByCharacterId(client.Character.CharacterId); + + foreach ((ContactListEntity contact, CDataCharacterListElement character) in friends) { - if (f.Type != ContactListType.FriendList) + if (contact.Type != ContactListType.FriendList) { continue; } - Character otherCharacter = - Database.SelectCharacter(f.GetOtherCharacterId(client.Character.CharacterId)); - - if (f.Status == ContactListStatus.Accepted) + + if (contact.Status == ContactListStatus.Accepted) { - fList.Add(ContactListManager.CharacterToFriend(otherCharacter, f.Id, f.IsFavoriteForCharacter(client.Character.CharacterId))); - } - else if (f.Status == ContactListStatus.PendingApproval) + res.FriendInfoList.Add(new CDataFriendInfo() + { + CharacterListElement = character, + PendingStatus = 0, // TODO + IsFavorite = contact.IsFavoriteForCharacter(client.Character.CharacterId), + FriendNo = contact.Id, + }); + } + else if (contact.Status == ContactListStatus.PendingApproval) { - if (f.RequesterCharacterId == client.Character.CharacterId) + if (contact.RequesterCharacterId == client.Character.CharacterId) { - applList.Add(ContactListManager.CharacterToCommunityInfo(otherCharacter)); + res.ApplyingCharacterList.Add(character.CommunityCharacterBaseInfo); } - else if (f.RequestedCharacterId == client.Character.CharacterId) + else if (contact.RequestedCharacterId == client.Character.CharacterId) { - apprList.Add(ContactListManager.CharacterToCommunityInfo(otherCharacter)); + res.ApprovingCharacterList.Add(character.CommunityCharacterBaseInfo); } } } - - var result = new S2CFriendGetFriendListRes() - { - FriendInfoList = fList, - ApplyingCharacterList = applList, - ApprovingCharacterList = apprList, - Result = 0, - Error = 0, - - }; - - client.Send(result); + return res; } } } diff --git a/Arrowgene.Ddon.GameServer/Handler/FriendRegisterFavoriteFriendHandler.cs b/Arrowgene.Ddon.GameServer/Handler/FriendRegisterFavoriteFriendHandler.cs index 203d0020d..09b94070d 100644 --- a/Arrowgene.Ddon.GameServer/Handler/FriendRegisterFavoriteFriendHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/FriendRegisterFavoriteFriendHandler.cs @@ -1,4 +1,4 @@ -using Arrowgene.Ddon.Server; +using Arrowgene.Ddon.Server; using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Model; using Arrowgene.Ddon.Shared.Network; @@ -6,7 +6,7 @@ namespace Arrowgene.Ddon.GameServer.Handler { - public class FriendRegisterFavoriteFriendHandler : GameStructurePacketHandler + public class FriendRegisterFavoriteFriendHandler : GameRequestPacketHandler { private static readonly ServerLogger Logger = LogProvider.Logger(typeof(FriendRegisterFavoriteFriendHandler)); @@ -15,32 +15,19 @@ public FriendRegisterFavoriteFriendHandler(DdonGameServer server) : base(server) { } - public override void Handle(GameClient client, StructurePacket packet) + public override S2CFriendRegisterFavoriteFriendRes Handle(GameClient client, C2SFriendRegisterFavoriteFriendReq request) { - ContactListEntity r = Database.SelectContactListById(packet.Structure.unFriendNo); - if (r == null) - { - Logger.Error(client, $"ContactListEntity not found"); - client.Send( - new S2CFriendRegisterFavoriteFriendRes() - { - Result = -1, - Error = (uint)ErrorCode.ERROR_CODE_FRIEND_INVARID_FRIEND_NO - } - ); - return; - } - - r.SetFavoriteForCharacter(client.Character.CharacterId, packet.Structure.isFavorite); + ContactListEntity r = Database.SelectContactListById(request.FriendNo) + ?? throw new ResponseErrorException(ErrorCode.ERROR_CODE_FRIEND_INVARID_FRIEND_NO, "ContactListEntity not found"); + + r.SetFavoriteForCharacter(client.Character.CharacterId, request.IsFavorite); Database.UpdateContact(r.RequesterCharacterId, r.RequestedCharacterId, r.Status, r.Type, r.RequesterFavorite, r.RequestedFavorite); - - client.Send( - new S2CFriendRegisterFavoriteFriendRes() - { - Result = 1 - } - ); + + return new S2CFriendRegisterFavoriteFriendRes() + { + Result = 1 + }; } } } diff --git a/Arrowgene.Ddon.GameServer/Handler/FriendRemoveFriendHandler.cs b/Arrowgene.Ddon.GameServer/Handler/FriendRemoveFriendHandler.cs index 63d644613..e94f39dbb 100644 --- a/Arrowgene.Ddon.GameServer/Handler/FriendRemoveFriendHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/FriendRemoveFriendHandler.cs @@ -1,69 +1,45 @@ -using Arrowgene.Ddon.Server; +using Arrowgene.Ddon.Server; using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Model; -using Arrowgene.Ddon.Shared.Network; using Arrowgene.Logging; namespace Arrowgene.Ddon.GameServer.Handler { - public class FriendRemoveFriendHandler : GameStructurePacketHandler - { - private static readonly ServerLogger Logger = LogProvider.Logger(typeof(FriendRemoveFriendHandler)); + public class FriendRemoveFriendHandler : GameRequestPacketHandler + { + private static readonly ServerLogger Logger = LogProvider.Logger(typeof(FriendRemoveFriendHandler)); - public FriendRemoveFriendHandler(DdonGameServer server) : base(server) - { - } + public FriendRemoveFriendHandler(DdonGameServer server) : base(server) + { + } - public override void Handle(GameClient client, StructurePacket packet) - { + public override S2CFriendRemoveFriendRes Handle(GameClient client, C2SFriendRemoveFriendReq request) + { - ContactListEntity relationship = Database.SelectContactListById(packet.Structure.unFriendNo); - if (relationship == null) - { - Logger.Error(client, $"ContactListEntity not found"); - client.Send( - new S2CFriendRemoveFriendRes() - { - Result = -1, - Error = (uint)ErrorCode.ERROR_CODE_FRIEND_INVARID_FRIEND_NO - } - ); - return; - } + ContactListEntity relationship = Database.SelectContactListById(request.FriendNo) + ?? throw new ResponseErrorException(ErrorCode.ERROR_CODE_FRIEND_INVARID_FRIEND_NO, "ContactListEntity not found"); - if (Database.DeleteContactById(relationship.Id) < 1) - { - Logger.Error(client, $"Problem deleting contact"); - client.Send( - new S2CFriendRemoveFriendRes() - { - Result = -1, - Error = (uint)ErrorCode.ERROR_CODE_FRIEND_INVARID_FRIEND_NO - } - ); - return; - } - - client.Send( - new S2CFriendRemoveFriendRes() - { - Result = 1 - } - ); + if (Database.DeleteContactById(relationship.Id) < 1) + { + throw new ResponseErrorException(ErrorCode.ERROR_CODE_FRIEND_INVARID_FRIEND_NO, "Problem deleting contact"); + } - uint otherCharId = relationship.GetOtherCharacterId(client.Character.CharacterId); - - GameClient otherClient = Server.ClientLookup.GetClientByCharacterId(otherCharId); - if (otherClient != null) - { - otherClient.Send( - new S2CFriendRemoveFriendNtc() - { - CharacterId = client.Character.CharacterId - } - ); - } - } - } + uint otherCharId = relationship.GetOtherCharacterId(client.Character.CharacterId); + + GameClient otherClient = Server.ClientLookup.GetClientByCharacterId(otherCharId); + if (otherClient != null) + { + otherClient.Send(new S2CFriendRemoveFriendNtc() + { + CharacterId = client.Character.CharacterId + }); + } + + return new S2CFriendRemoveFriendRes() + { + Result = 1 + }; + } + } } diff --git a/Arrowgene.Ddon.Shared/Entity/EntitySerializer.cs b/Arrowgene.Ddon.Shared/Entity/EntitySerializer.cs index a18441773..e3f18bc1d 100644 --- a/Arrowgene.Ddon.Shared/Entity/EntitySerializer.cs +++ b/Arrowgene.Ddon.Shared/Entity/EntitySerializer.cs @@ -803,6 +803,7 @@ static EntitySerializer() Create(new C2SFriendRemoveFriendReq.Serializer()); Create(new C2SFriendRegisterFavoriteFriendReq.Serializer()); Create(new C2SFriendCancelFriendApplicationReq.Serializer()); + Create(new C2SFriendGetFriendListReq.Serializer()); Create(new C2SCharacterCommunityCharacterStatusGetReq.Serializer()); Create(new C2SBinarySaveSetCharacterBinSaveDataReq.Serializer()); diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SFriendGetFriendListReq.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SFriendGetFriendListReq.cs new file mode 100644 index 000000000..200d4bdbc --- /dev/null +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SFriendGetFriendListReq.cs @@ -0,0 +1,24 @@ +using Arrowgene.Buffers; +using Arrowgene.Ddon.Shared.Network; + +namespace Arrowgene.Ddon.Shared.Entity.PacketStructure +{ + public class C2SFriendGetFriendListReq : IPacketStructure + { + public PacketId Id => PacketId.C2S_FRIEND_GET_FRIEND_LIST_REQ; + + public class Serializer : PacketEntitySerializer + { + public override void Write(IBuffer buffer, C2SFriendGetFriendListReq obj) + { + } + + public override C2SFriendGetFriendListReq Read(IBuffer buffer) + { + C2SFriendGetFriendListReq obj = new C2SFriendGetFriendListReq(); + return obj; + } + } + + } +} diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SFriendRegisterFavoriteFriendReq.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SFriendRegisterFavoriteFriendReq.cs index 424dfce75..2bd8874aa 100644 --- a/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SFriendRegisterFavoriteFriendReq.cs +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SFriendRegisterFavoriteFriendReq.cs @@ -10,22 +10,22 @@ public class C2SFriendRegisterFavoriteFriendReq : IPacketStructure { public PacketId Id => PacketId.C2S_FRIEND_REGISTER_FAVORITE_FRIEND_REQ; - public UInt32 unFriendNo { get; set; } - public bool isFavorite { get; set; } + public uint FriendNo { get; set; } + public bool IsFavorite { get; set; } public class Serializer : PacketEntitySerializer { public override void Write(IBuffer buffer, C2SFriendRegisterFavoriteFriendReq obj) { - WriteUInt32(buffer, obj.unFriendNo); - WriteBool(buffer, obj.isFavorite); + WriteUInt32(buffer, obj.FriendNo); + WriteBool(buffer, obj.IsFavorite); } public override C2SFriendRegisterFavoriteFriendReq Read(IBuffer buffer) { C2SFriendRegisterFavoriteFriendReq obj = new C2SFriendRegisterFavoriteFriendReq(); - obj.unFriendNo = ReadUInt32(buffer); - obj.isFavorite = ReadBool(buffer); + obj.FriendNo = ReadUInt32(buffer); + obj.IsFavorite = ReadBool(buffer); return obj; } } diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SFriendRemoveFriendReq.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SFriendRemoveFriendReq.cs index bafc149a0..eb363f46c 100644 --- a/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SFriendRemoveFriendReq.cs +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/C2SFriendRemoveFriendReq.cs @@ -10,19 +10,19 @@ public class C2SFriendRemoveFriendReq : IPacketStructure { public PacketId Id => PacketId.C2S_FRIEND_REMOVE_FRIEND_REQ; - public UInt32 unFriendNo { get; set; } + public uint FriendNo { get; set; } public class Serializer : PacketEntitySerializer { public override void Write(IBuffer buffer, C2SFriendRemoveFriendReq obj) { - WriteUInt32(buffer, obj.unFriendNo); + WriteUInt32(buffer, obj.FriendNo); } public override C2SFriendRemoveFriendReq Read(IBuffer buffer) { C2SFriendRemoveFriendReq obj = new C2SFriendRemoveFriendReq(); - obj.unFriendNo = ReadUInt32(buffer); + obj.FriendNo = ReadUInt32(buffer); return obj; } } diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CCharacterCommunityCharacterStatusGetRes.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CCharacterCommunityCharacterStatusGetRes.cs index 6b49df646..9989b35f9 100644 --- a/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CCharacterCommunityCharacterStatusGetRes.cs +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CCharacterCommunityCharacterStatusGetRes.cs @@ -1,27 +1,23 @@ -using System; using Arrowgene.Buffers; -using Arrowgene.Ddon.Shared.Entity.Structure; using Arrowgene.Ddon.Shared.Network; namespace Arrowgene.Ddon.Shared.Entity.PacketStructure { - public class S2CCharacterCommunityCharacterStatusGetRes : IPacketStructure + public class S2CCharacterCommunityCharacterStatusGetRes : ServerResponse { - public PacketId Id => PacketId.S2C_CHARACTER_COMMUNITY_CHARACTER_STATUS_GET_RES; - public Int32 Result { get; set; } - + public override PacketId Id => PacketId.S2C_CHARACTER_COMMUNITY_CHARACTER_STATUS_GET_RES; public class Serializer : PacketEntitySerializer { public override void Write(IBuffer buffer, S2CCharacterCommunityCharacterStatusGetRes obj) { - WriteInt32(buffer, obj.Result); + WriteServerResponse(buffer, obj); } public override S2CCharacterCommunityCharacterStatusGetRes Read(IBuffer buffer) { S2CCharacterCommunityCharacterStatusGetRes obj = new S2CCharacterCommunityCharacterStatusGetRes(); - obj.Result = ReadInt32(buffer); + ReadServerResponse(buffer, obj); return obj; } diff --git a/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CFriendGetFriendListRes.cs b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CFriendGetFriendListRes.cs index 168766e1e..b1623d7a7 100644 --- a/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CFriendGetFriendListRes.cs +++ b/Arrowgene.Ddon.Shared/Entity/PacketStructure/S2CFriendGetFriendListRes.cs @@ -1,16 +1,19 @@ using Arrowgene.Buffers; -using Arrowgene.Ddon.Shared.Model; +using Arrowgene.Ddon.Shared.Entity.Structure; using Arrowgene.Ddon.Shared.Network; -using Arrowgene.Logging; -using System; using System.Collections.Generic; -using System.Net.Sockets; -using Arrowgene.Ddon.Shared.Entity.Structure; namespace Arrowgene.Ddon.Shared.Entity.PacketStructure { public class S2CFriendGetFriendListRes : ServerResponse { + public S2CFriendGetFriendListRes() + { + FriendInfoList = new(); + ApplyingCharacterList = new(); + ApprovingCharacterList = new(); + } + public override PacketId Id => PacketId.S2C_FRIEND_GET_FRIEND_LIST_RES; public List FriendInfoList { get; set; } diff --git a/Arrowgene.Ddon.Shared/Entity/Structure/CDataFriendInfo.cs b/Arrowgene.Ddon.Shared/Entity/Structure/CDataFriendInfo.cs index e1b9e888f..c3f23c262 100644 --- a/Arrowgene.Ddon.Shared/Entity/Structure/CDataFriendInfo.cs +++ b/Arrowgene.Ddon.Shared/Entity/Structure/CDataFriendInfo.cs @@ -1,14 +1,18 @@ -using System; using Arrowgene.Buffers; namespace Arrowgene.Ddon.Shared.Entity.Structure { public class CDataFriendInfo { - public CDataCharacterListElement CharacterListElement { get; set; } = new(); - public byte PendingStatus { get; set; } = 0; - public UInt32 UnFriendNo { get; set; } = 0; - public bool IsFavorite { get; set; } = false; + public CDataFriendInfo() + { + CharacterListElement = new(); + } + + public CDataCharacterListElement CharacterListElement { get; set; } + public byte PendingStatus { get; set; } + public uint FriendNo { get; set; } + public bool IsFavorite { get; set; } public class Serializer : EntitySerializer { @@ -16,7 +20,7 @@ public override void Write(IBuffer buffer, CDataFriendInfo obj) { WriteEntity(buffer, obj.CharacterListElement); WriteByte(buffer, obj.PendingStatus); - WriteUInt32(buffer, obj.UnFriendNo); + WriteUInt32(buffer, obj.FriendNo); WriteBool(buffer, obj.IsFavorite); } @@ -25,7 +29,7 @@ public override CDataFriendInfo Read(IBuffer buffer) CDataFriendInfo obj = new CDataFriendInfo(); obj.CharacterListElement = ReadEntity(buffer); obj.PendingStatus = ReadByte(buffer); - obj.UnFriendNo = ReadUInt32(buffer); + obj.FriendNo = ReadUInt32(buffer); obj.IsFavorite = ReadBool(buffer); return obj; } diff --git a/Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs b/Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs index f44d51e46..69cf636df 100644 --- a/Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs +++ b/Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs @@ -289,6 +289,7 @@ public void Execute(DbConnection conn, string sql) {} public ContactListEntity SelectContactListById(uint id) { return new ContactListEntity(); } public List SelectContactsByCharacterId(uint characterId) { return new List(); } public ContactListEntity SelectContactsByCharacterId(uint characterId1, uint characterId2) { return new ContactListEntity(); } + public List<(ContactListEntity, CDataCharacterListElement)> SelectFullContactListByCharacterId(uint characterId, DbConnection? connectionIn = null) { return new(); } public Item SelectStorageItemByUId(string uId, DbConnection? connectionIn = null) { return new Item(); } public List SelectNormalSkillParam(uint commonId, JobId job) { return new List(); } public CDataOrbGainExtendParam SelectOrbGainExtendParam(uint commonId) { return new CDataOrbGainExtendParam(); } From 5a0ea53618ef8c71ee8cd9408c68ad3e4af121e9 Mon Sep 17 00:00:00 2001 From: Ryan Yappert Date: Fri, 8 Nov 2024 01:36:11 -0800 Subject: [PATCH 06/12] Fix bad SQL query for contacts list. --- .../Sql/Core/DdonSqlDbContactList.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbContactList.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbContactList.cs index a39dd6354..b31a91b2f 100644 --- a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbContactList.cs +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbContactList.cs @@ -34,22 +34,22 @@ public abstract partial class DdonSqlDb : SqlDb Date: Wed, 30 Oct 2024 14:04:07 -0700 Subject: [PATCH 07/12] Add guards when unequipping items into a full storage box or equipment bag. --- .../Characters/EquipManager.cs | 38 ++++++++++++++++++- .../EquipChangeCharacterEquipHandler.cs | 29 +++++++------- ...EquipChangeCharacterStorageEquipHandler.cs | 19 ++++++---- .../Handler/EquipChangePawnEquipHandler.cs | 33 +++++++++------- .../EquipChangePawnStorageEquipHandler.cs | 37 ++++++++++-------- 5 files changed, 105 insertions(+), 51 deletions(-) diff --git a/Arrowgene.Ddon.GameServer/Characters/EquipManager.cs b/Arrowgene.Ddon.GameServer/Characters/EquipManager.cs index 3f4c39601..3dbb3291b 100644 --- a/Arrowgene.Ddon.GameServer/Characters/EquipManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/EquipManager.cs @@ -323,6 +323,40 @@ private bool CharacterHasEnsembleEquipped(DdonGameServer server, CharacterCommon return false; } + public bool CanMeetStorageRequirements(DdonGameServer server, GameClient client, CharacterCommon characterToEquipTo, List changeCharacterEquipList, List storageTypes) + { + int slotsRequired = 0; + int freeSlots = storageTypes.Sum(x => client.Character.Storage.GetStorage(x).EmptySlots()); + + // Check regular removals + slotsRequired += changeCharacterEquipList.Sum(x => x.EquipItemUId.Length == 0 ? 1 : 0); + + // Check removals caused by equipped an ensemble + foreach (var changeItem in changeCharacterEquipList) + { + var itemInfo = server.ItemManager.LookupInfoByUID(server, changeItem.EquipItemUId); + if (itemInfo.SubCategory == ItemSubCategory.EquipEnsemble) + { + foreach (EquipSlot slot in EnsembleSlots) + { + var equip = characterToEquipTo.EquipmentTemplate.GetEquipItem(characterToEquipTo.Job, changeItem.EquipType, slot); + if (equip == null && (byte)slot == changeItem.EquipCategory) + { + // Equipping an ensemble into an open body slot frees one storage space. + slotsRequired += -1; + } + else if (equip != null) + { + // Equipped an ensemble will force some other item to be removed, taking up one storage space. + slotsRequired += 1; + } + } + } + } + + return slotsRequired >= freeSlots; + } + public uint CalculateItemRank(DdonGameServer server, CharacterCommon characterCommon) { uint itemRank = 0; @@ -330,7 +364,7 @@ public uint CalculateItemRank(DdonGameServer server, CharacterCommon characterCo if (CharacterHasEnsembleEquipped(server, characterCommon)) { var mainHand = characterCommon.EquipmentTemplate.GetEquipItem(characterCommon.Job, EquipType.Performance, EquipSlot.WepMain); - itemRank = server.ItemManager.LookupInfoByItemID(server, mainHand.ItemId).Rank; + itemRank = mainHand is not null ? server.ItemManager.LookupInfoByItemID(server, mainHand.ItemId).Rank : 0u; foreach (EquipSlot slot in EnsembleSlots) { @@ -367,5 +401,7 @@ public uint CalculateItemRank(DdonGameServer server, CharacterCommon characterCo return itemRank > 0 ? itemRank : 1; } + + } } diff --git a/Arrowgene.Ddon.GameServer/Handler/EquipChangeCharacterEquipHandler.cs b/Arrowgene.Ddon.GameServer/Handler/EquipChangeCharacterEquipHandler.cs index 5f931e0c7..15d0fa44a 100644 --- a/Arrowgene.Ddon.GameServer/Handler/EquipChangeCharacterEquipHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/EquipChangeCharacterEquipHandler.cs @@ -1,16 +1,14 @@ -using System.Collections.Generic; using Arrowgene.Ddon.GameServer.Characters; using Arrowgene.Ddon.Server; using Arrowgene.Ddon.Server.Network; -using Arrowgene.Ddon.Shared.Entity; using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Model; -using Arrowgene.Ddon.Shared.Network; using Arrowgene.Logging; +using System.Collections.Generic; namespace Arrowgene.Ddon.GameServer.Handler { - public class EquipChangeCharacterEquipHandler : GameStructurePacketHandler + public class EquipChangeCharacterEquipHandler : GameRequestPacketHandler { private static readonly ServerLogger Logger = LogProvider.Logger(typeof(EquipChangeCharacterEquipHandler)); @@ -21,18 +19,23 @@ public EquipChangeCharacterEquipHandler(DdonGameServer server) : base(server) equipManager = server.EquipManager; } - public override void Handle(GameClient client, StructurePacket packet) + public override S2CEquipChangeCharacterEquipRes Handle(GameClient client, C2SEquipChangeCharacterEquipReq request) { (S2CItemUpdateCharacterItemNtc itemNtc, S2CEquipChangeCharacterEquipNtc equipNtc) equipResult = (null, null); + if (!Server.EquipManager.CanMeetStorageRequirements(Server, client, client.Character, request.ChangeCharacterEquipList, new List() { StorageType.ItemBagEquipment })) + { + throw new ResponseErrorException(ErrorCode.ERROR_CODE_ITEM_BAG_CAPACITY_OVER); + } + Server.Database.ExecuteInTransaction(connection => { equipResult = ((S2CItemUpdateCharacterItemNtc, S2CEquipChangeCharacterEquipNtc))equipManager.HandleChangeEquipList( - Server, client, - client.Character, - packet.Structure.ChangeCharacterEquipList, - ItemNoticeType.ChangeEquip, - new List() { StorageType.ItemBagEquipment }, + Server, client, + client.Character, + request.ChangeCharacterEquipList, + ItemNoticeType.ChangeEquip, + new List() { StorageType.ItemBagEquipment }, connection); }); @@ -43,11 +46,11 @@ public override void Handle(GameClient client, StructurePacket + public class EquipChangeCharacterStorageEquipHandler : GameRequestPacketHandler { private static readonly ServerLogger Logger = LogProvider.Logger(typeof(EquipChangeCharacterStorageEquipHandler)); @@ -19,10 +19,15 @@ public EquipChangeCharacterStorageEquipHandler(DdonGameServer server) : base(ser equipManager = server.EquipManager; } - public override void Handle(GameClient client, StructurePacket packet) + public override S2CEquipChangeCharacterStorageEquipRes Handle(GameClient client, C2SEquipChangeCharacterStorageEquipReq request) { (S2CItemUpdateCharacterItemNtc itemNtc, S2CEquipChangeCharacterEquipNtc equipNtc) equipResult = (null, null); + if (!Server.EquipManager.CanMeetStorageRequirements(Server, client, client.Character, request.ChangeCharacterEquipList, new List() { StorageType.StorageBoxNormal })) + { + throw new ResponseErrorException(ErrorCode.ERROR_CODE_ITEM_STORAGE_CAPACITY_OVER); + } + Server.Database.ExecuteInTransaction(connection => { equipResult = ((S2CItemUpdateCharacterItemNtc, S2CEquipChangeCharacterEquipNtc)) @@ -30,7 +35,7 @@ public override void Handle(GameClient client, StructurePacket + public class EquipChangePawnEquipHandler : GameRequestPacketHandler { private static readonly ServerLogger Logger = LogProvider.Logger(typeof(EquipChangePawnEquipHandler)); @@ -20,21 +19,27 @@ public EquipChangePawnEquipHandler(DdonGameServer server) : base(server) equipManager = server.EquipManager; } - public override void Handle(GameClient client, StructurePacket packet) + public override S2CEquipChangePawnEquipRes Handle(GameClient client, C2SEquipChangePawnEquipReq request) { (S2CItemUpdateCharacterItemNtc itemNtc, S2CEquipChangePawnEquipNtc equipNtc) equipResult = (null, null); - Pawn pawn = client.Character.Pawns.Where(pawn => pawn.PawnId == packet.Structure.PawnId).Single(); + Pawn pawn = client.Character.Pawns.Where(pawn => pawn.PawnId == request.PawnId).Single(); + + if (!Server.EquipManager.CanMeetStorageRequirements(Server, client, pawn, request.ChangeCharacterEquipList, new List() { StorageType.ItemBagEquipment })) + { + throw new ResponseErrorException(ErrorCode.ERROR_CODE_ITEM_BAG_CAPACITY_OVER); + } + Server.Database.ExecuteInTransaction(connection => { equipResult = ((S2CItemUpdateCharacterItemNtc, S2CEquipChangePawnEquipNtc)) equipManager.HandleChangeEquipList( - Server, - client, - pawn, - packet.Structure.ChangeCharacterEquipList, - ItemNoticeType.ChangePawnEquip, - new List() { StorageType.ItemBagEquipment }, + Server, + client, + pawn, + request.ChangeCharacterEquipList, + ItemNoticeType.ChangePawnEquip, + new List() { StorageType.ItemBagEquipment }, connection); }); @@ -43,12 +48,12 @@ public override void Handle(GameClient client, StructurePacket + public class EquipChangePawnStorageEquipHandler : GameRequestPacketHandler { private static readonly ServerLogger Logger = LogProvider.Logger(typeof(EquipChangePawnStorageEquipHandler)); @@ -20,20 +19,26 @@ public EquipChangePawnStorageEquipHandler(DdonGameServer server) : base(server) equipManager = server.EquipManager; } - public override void Handle(GameClient client, StructurePacket packet) + public override S2CEquipChangePawnStorageEquipRes Handle(GameClient client, C2SEquipChangePawnStorageEquipReq request) { (S2CItemUpdateCharacterItemNtc itemNtc, S2CEquipChangePawnEquipNtc equipNtc) equipResult = (null, null); - Pawn pawn = client.Character.Pawns.Where(pawn => pawn.PawnId == packet.Structure.PawnId).Single(); + Pawn pawn = client.Character.Pawns.Where(pawn => pawn.PawnId == request.PawnId).Single(); + + if (!Server.EquipManager.CanMeetStorageRequirements(Server, client, pawn, request.ChangeCharacterEquipList, new List() { StorageType.StorageBoxNormal })) + { + throw new ResponseErrorException(ErrorCode.ERROR_CODE_ITEM_STORAGE_CAPACITY_OVER); + } + Server.Database.ExecuteInTransaction(connection => { equipResult = ((S2CItemUpdateCharacterItemNtc, S2CEquipChangePawnEquipNtc)) equipManager.HandleChangeEquipList( - Server, - client, - pawn, - packet.Structure.ChangeCharacterEquipList, - ItemNoticeType.ChangeStoragePawnEquip, + Server, + client, + pawn, + request.ChangeCharacterEquipList, + ItemNoticeType.ChangeStoragePawnEquip, ItemManager.BoxStorageTypes, connection); }); @@ -41,14 +46,14 @@ public override void Handle(GameClient client, StructurePacket Date: Thu, 31 Oct 2024 17:08:58 -0700 Subject: [PATCH 08/12] Guards in PawnGetPartyPawnDataHandler (?), QuestGetSetQuestListHandler, and QuestStateManager. Better logging for ResponseErrorExceptions. --- .../Characters/EquipManager.cs | 8 +++- .../Handler/PawnGetPartyPawnDataHandler.cs | 7 ++-- .../Handler/QuestGetSetQuestListHandler.cs | 38 ++++++++----------- .../Quests/QuestStateManager.cs | 6 +++ .../Network/RequestPacketHandler.cs | 5 +++ 5 files changed, 37 insertions(+), 27 deletions(-) diff --git a/Arrowgene.Ddon.GameServer/Characters/EquipManager.cs b/Arrowgene.Ddon.GameServer/Characters/EquipManager.cs index 3dbb3291b..a4dfb6db3 100644 --- a/Arrowgene.Ddon.GameServer/Characters/EquipManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/EquipManager.cs @@ -334,6 +334,12 @@ public bool CanMeetStorageRequirements(DdonGameServer server, GameClient client, // Check removals caused by equipped an ensemble foreach (var changeItem in changeCharacterEquipList) { + // This is actually a removal, so skip it. + if (changeItem.EquipItemUId.Length == 0) + { + continue; + } + var itemInfo = server.ItemManager.LookupInfoByUID(server, changeItem.EquipItemUId); if (itemInfo.SubCategory == ItemSubCategory.EquipEnsemble) { @@ -354,7 +360,7 @@ public bool CanMeetStorageRequirements(DdonGameServer server, GameClient client, } } - return slotsRequired >= freeSlots; + return slotsRequired <= freeSlots; } public uint CalculateItemRank(DdonGameServer server, CharacterCommon characterCommon) diff --git a/Arrowgene.Ddon.GameServer/Handler/PawnGetPartyPawnDataHandler.cs b/Arrowgene.Ddon.GameServer/Handler/PawnGetPartyPawnDataHandler.cs index e0cc5a9dc..8f222d860 100644 --- a/Arrowgene.Ddon.GameServer/Handler/PawnGetPartyPawnDataHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/PawnGetPartyPawnDataHandler.cs @@ -24,13 +24,14 @@ public PawnGetPartyPawnDataHandler(DdonGameServer server) : base(server) public override S2CPawnGetPartyPawnDataRes Handle(GameClient client, C2SPawnGetPartyPawnDataReq packet) { // var owner = Server.CharacterManager.SelectCharacter(packet.Structure.CharacterId); - GameClient owner = this.Server.ClientLookup.GetClientByCharacterId(packet.CharacterId); + GameClient owner = this.Server.ClientLookup.GetClientByCharacterId(packet.CharacterId) + ?? throw new ResponseErrorException(ErrorCode.ERROR_CODE_CHARACTER_PARAM_NOT_FOUND); // TODO: Move this to a function or lookup class List pawns = owner.Character.Pawns.Concat(client.Character.RentedPawns).ToList(); Pawn pawn = pawns - .Where(pawn => pawn.PawnId == packet.PawnId) - .FirstOrDefault() ?? throw new ResponseErrorException(ErrorCode.ERROR_CODE_PAWN_NOT_FOUNDED); + .Find(pawn => pawn.PawnId == packet.PawnId) + ?? throw new ResponseErrorException(ErrorCode.ERROR_CODE_PAWN_NOT_FOUNDED); var res = new S2CPawnGetPartyPawnDataRes(); res.CharacterId = pawn.CharacterId; diff --git a/Arrowgene.Ddon.GameServer/Handler/QuestGetSetQuestListHandler.cs b/Arrowgene.Ddon.GameServer/Handler/QuestGetSetQuestListHandler.cs index 87403d3e7..01b48b511 100644 --- a/Arrowgene.Ddon.GameServer/Handler/QuestGetSetQuestListHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/QuestGetSetQuestListHandler.cs @@ -1,26 +1,15 @@ -using Arrowgene.Buffers; using Arrowgene.Ddon.GameServer.Characters; -using Arrowgene.Ddon.GameServer.Dump; -using Arrowgene.Ddon.GameServer.Party; using Arrowgene.Ddon.GameServer.Quests; using Arrowgene.Ddon.Server; -using Arrowgene.Ddon.Server.Network; -using Arrowgene.Ddon.Shared.Asset; using Arrowgene.Ddon.Shared.Entity.PacketStructure; using Arrowgene.Ddon.Shared.Entity.Structure; -using Arrowgene.Ddon.Shared.Model; using Arrowgene.Ddon.Shared.Model.Quest; -using Arrowgene.Ddon.Shared.Network; using Arrowgene.Logging; -using System.Collections; using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text.RegularExpressions; namespace Arrowgene.Ddon.GameServer.Handler { - public class QuestGetSetQuestListHandler : GameStructurePacketHandler + public class QuestGetSetQuestListHandler : GameRequestPacketHandler { private static readonly ServerLogger Logger = LogProvider.Logger(typeof(QuestGetQuestPartyBonusListHandler)); @@ -28,7 +17,7 @@ public QuestGetSetQuestListHandler(DdonGameServer server) : base(server) { } - public override void Handle(GameClient client, StructurePacket packet) + public override S2CQuestGetSetQuestListRes Handle(GameClient client, C2SQuestGetSetQuestListReq request) { // client.Send(GameFull.Dump_132); @@ -47,14 +36,14 @@ public override void Handle(GameClient client, StructurePacket : St where TReqStruct : class, IPacketStructure, new() where TResStruct : ServerResponse, new() { + private static readonly ServerLogger Logger = LogProvider.Logger(typeof(RequestPacketHandler)); + protected RequestPacketHandler(DdonServer server) : base(server) { } @@ -28,6 +31,8 @@ public sealed override void Handle(TClient client, StructurePacket r { response = new TResStruct(); response.Error = (uint) ex.ErrorCode; + var message = ex.Message.Length > 0 ? ("\n\tMessage: " + ex.Message) : ""; + Logger.Error(client, $"{ex.ErrorCode} thrown when handling {typeof(TReqStruct)}{message}."); client.Send(response); } catch (Exception) From d3a3eaa15ea65730c7c1f0e389fec1ba4064f469 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Fri, 8 Nov 2024 09:28:49 -0500 Subject: [PATCH 09/12] feat: Add new settings to be configured Added the following new configuration settings - ExpModifier - PpModifier - BoModifier - HoMOdifier - JpModifier - RewardBoxMax - QuestOrderMax --- .../Characters/ExpManager.cs | 10 +-- .../Characters/RewardManager.cs | 4 +- .../Handler/InstanceEnemyKillHandler.cs | 8 +-- .../QuestGetCycleContentsStateListHandler.cs | 2 + .../Quests/QuestStateManager.cs | 23 +++++- Arrowgene.Ddon.Server/GameLogicSetting.cs | 72 +++++++++++++++++++ 6 files changed, 104 insertions(+), 15 deletions(-) diff --git a/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs b/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs index 8ba100d0d..2a0808ffe 100644 --- a/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/ExpManager.cs @@ -509,7 +509,7 @@ public PacketQueue AddExp(GameClient client, CharacterCommon characterToAddExpTo if (client.GameMode == GameMode.Normal) { - addJobPoint += LEVEL_UP_JOB_POINTS_EARNED[targetLevel]; + addJobPoint += (uint)(LEVEL_UP_JOB_POINTS_EARNED[targetLevel] * _Server.Setting.GameLogicSetting.JpModifier); } } @@ -903,7 +903,7 @@ public uint GetAdjustedExp(GameMode gameMode, RewardSource source, PartyGroup pa { if (_Server.GpCourseManager.DisablePartyExpAdjustment()) { - return baseExpAmount; + return (uint)(baseExpAmount * _GameSettings.ExpModifier); } double multiplier = 1.0; @@ -914,7 +914,7 @@ public uint GetAdjustedExp(GameMode gameMode, RewardSource source, PartyGroup pa multiplier = Math.Min(partyRangeMultiplier, targetMultiplier); } - return (uint)(multiplier * baseExpAmount); + return (uint)(multiplier * baseExpAmount * _GameSettings.ExpModifier); } private uint GetMaxAllowedPartyRange() @@ -996,13 +996,13 @@ public uint GetAdjustedPawnExp(GameMode gameMode, RewardSource source, PartyGrou { if (!_GameSettings.EnablePawnCatchup || gameMode == GameMode.BitterblackMaze) { - return baseExpAmount; + return (uint)(baseExpAmount * _GameSettings.ExpModifier); } var targetMultiplier = CalculatePawnCatchupTargetLvMultiplier(gameMode, pawn, targetLv); var multiplier = _GameSettings.PawnCatchupMultiplier * targetMultiplier; - return (uint)(multiplier * baseExpAmount); + return (uint)(multiplier * baseExpAmount * _GameSettings.ExpModifier); } } } diff --git a/Arrowgene.Ddon.GameServer/Characters/RewardManager.cs b/Arrowgene.Ddon.GameServer/Characters/RewardManager.cs index 9dd05f679..606bed6e3 100644 --- a/Arrowgene.Ddon.GameServer/Characters/RewardManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/RewardManager.cs @@ -11,8 +11,6 @@ namespace Arrowgene.Ddon.GameServer.Characters { public class RewardManager { - private readonly int MAX_REWARD_BOX_RESULTS = 100; - private static readonly ServerLogger Logger = LogProvider.Logger(typeof(RewardManager)); private readonly DdonGameServer _Server; @@ -26,7 +24,7 @@ public bool AddQuestRewards(GameClient client, Quest quest, DbConnection? connec var rewards = quest.GenerateBoxRewards(); var currentRewards = GetQuestBoxRewards(client, connectionIn); - if (currentRewards.Count >= MAX_REWARD_BOX_RESULTS) + if (currentRewards.Count >= _Server.Setting.GameLogicSetting.RewardBoxMax) { return false; } diff --git a/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs b/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs index b13b41714..a6efe3ebd 100644 --- a/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/InstanceEnemyKillHandler.cs @@ -164,7 +164,7 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne uint calcExp = _gameServer.ExpManager.GetAdjustedExp(client.GameMode, RewardSource.Enemy, client.Party, enemyKilled.GetDroppedExperience(), enemyKilled.Lv); - uint calcPP = enemyKilled.GetDroppedPlayPoints(); + uint calcPP = (uint)(enemyKilled.GetDroppedPlayPoints() * _gameServer.Setting.GameLogicSetting.PpModifier); foreach (PartyMember member in client.Party.Members) { @@ -199,8 +199,8 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne if (enemyKilled.BloodOrbs > 0) { // Drop BO - uint gainedBo = enemyKilled.BloodOrbs; - uint bonusBo = (uint)(enemyKilled.BloodOrbs * _gameServer.GpCourseManager.EnemyBloodOrbBonus()); + uint gainedBo = (uint) (enemyKilled.BloodOrbs * _gameServer.Setting.GameLogicSetting.BoModifier); + uint bonusBo = (uint)(gainedBo * _gameServer.GpCourseManager.EnemyBloodOrbBonus()); CDataUpdateWalletPoint boUpdateWalletPoint = _gameServer.WalletManager.AddToWallet(memberClient.Character, WalletType.BloodOrbs, gainedBo + bonusBo, bonusBo, connectionIn: connectionIn); updateCharacterItemNtc.UpdateWalletList.Add(boUpdateWalletPoint); } @@ -208,7 +208,7 @@ public override S2CInstanceEnemyKillRes Handle(GameClient client, C2SInstanceEne if (enemyKilled.HighOrbs > 0) { // Drop HO - uint gainedHo = enemyKilled.HighOrbs; + uint gainedHo = (uint)(enemyKilled.HighOrbs * _gameServer.Setting.GameLogicSetting.HoModifier); CDataUpdateWalletPoint hoUpdateWalletPoint = _gameServer.WalletManager.AddToWallet(memberClient.Character, WalletType.HighOrbs, gainedHo, connectionIn: connectionIn); } diff --git a/Arrowgene.Ddon.GameServer/Handler/QuestGetCycleContentsStateListHandler.cs b/Arrowgene.Ddon.GameServer/Handler/QuestGetCycleContentsStateListHandler.cs index f6c854439..45a3c99a4 100644 --- a/Arrowgene.Ddon.GameServer/Handler/QuestGetCycleContentsStateListHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/QuestGetCycleContentsStateListHandler.cs @@ -35,6 +35,8 @@ public override S2CQuestGetCycleContentsStateListRes Handle(GameClient client, C ntc.WorldManageQuestOrderList = pcap.WorldManageQuestOrderList; // Recover paths + change vocation ntc.QuestDefine = pcap.QuestDefine; // Recover quest log data to be able to accept quests + ntc.QuestDefine.OrderMaxNum = Server.Setting.GameLogicSetting.QuestOrderMax; + ntc.QuestDefine.RewardBoxMaxNum = Server.Setting.GameLogicSetting.RewardBoxMax; // pcap.MainQuestIdList; (this will add back all missing functionality which depends on complete MSQ) var completedMsq = client.Character.CompletedQuests.Values.Where(x => x.QuestType == QuestType.Main); diff --git a/Arrowgene.Ddon.GameServer/Quests/QuestStateManager.cs b/Arrowgene.Ddon.GameServer/Quests/QuestStateManager.cs index bf08923d5..556715880 100644 --- a/Arrowgene.Ddon.GameServer/Quests/QuestStateManager.cs +++ b/Arrowgene.Ddon.GameServer/Quests/QuestStateManager.cs @@ -576,19 +576,36 @@ protected PacketQueue SendWalletRewards(DdonGameServer server, GameClient client foreach (var expPoint in quest.ExpRewards) { + uint amount = expPoint.Reward; + double modifier = 1.0; + switch (expPoint.Type) + { + case ExpType.ExperiencePoints: + modifier = server.Setting.GameLogicSetting.ExpModifier; + break; + case ExpType.JobPoints: + modifier = server.Setting.GameLogicSetting.JpModifier; + break; + case ExpType.PlayPoints: + modifier = server.Setting.GameLogicSetting.PpModifier; + break; + } + + amount = (uint)(amount * modifier); + if (expPoint.Type == ExpType.ExperiencePoints) { - var ntcs = server.ExpManager.AddExp(client, client.Character, expPoint.Reward, RewardSource.Quest, quest.QuestType, connectionIn); + var ntcs = server.ExpManager.AddExp(client, client.Character, amount, RewardSource.Quest, quest.QuestType, connectionIn); packets.AddRange(ntcs); } else if (expPoint.Type == ExpType.JobPoints) { - var ntcs = server.ExpManager.AddJp(client, client.Character, expPoint.Reward, RewardSource.Quest, quest.QuestType, connectionIn); + var ntcs = server.ExpManager.AddJp(client, client.Character, amount, RewardSource.Quest, quest.QuestType, connectionIn); packets.AddRange(ntcs); } else if (expPoint.Type == ExpType.PlayPoints) { - var ntc = server.PPManager.AddPlayPoint(client, expPoint.Reward, type: 1, connectionIn: connectionIn); + var ntc = server.PPManager.AddPlayPoint(client, amount, type: 1, connectionIn: connectionIn); client.Enqueue(ntc, packets); } } diff --git a/Arrowgene.Ddon.Server/GameLogicSetting.cs b/Arrowgene.Ddon.Server/GameLogicSetting.cs index 4a0aec73e..b3788e147 100644 --- a/Arrowgene.Ddon.Server/GameLogicSetting.cs +++ b/Arrowgene.Ddon.Server/GameLogicSetting.cs @@ -166,6 +166,42 @@ public class GameLogicSetting /// [DataMember(Order = 26)] public bool DisableExpCorrectionForMyPawn { get; set; } + /// + /// Global modifier for exp calculations to scale up or down. + /// + [DataMember(Order = 26)] public double ExpModifier { get; set; } + + /// + /// Global modifier for pp calculations to scale up or down. + /// + [DataMember(Order = 26)] public double PpModifier { get; set; } + + /// + /// Global modifier for BO calculations to scale up or down. + /// + [DataMember(Order = 26)] public double BoModifier { get; set; } + + /// + /// Global modifier for HO calculations to scale up or down. + /// + [DataMember(Order = 26)] public double HoModifier { get; set; } + + /// + /// Global modifier for JP calculations to scale up or down. + /// + [DataMember(Order = 26)] public double JpModifier { get; set; } + + /// + /// Configures the maximum amount of reward box slots. + /// + [DataMember(Order = 27)] public byte RewardBoxMax { get; set; } + + /// + /// Configures the maximum amount of quests that can be ordered at one time. + /// + [DataMember(Order = 27)] public byte QuestOrderMax { get; set; } + + /// /// Various URLs used by the client. /// Shared with the login server. @@ -249,6 +285,14 @@ public GameLogicSetting() DefaultMaxBazaarExhibits = 5; DefaultWarpFavorites = 3; + ExpModifier = 1.0; + PpModifier = 1.0; + BoModifier = 1.0; + HoModifier = 1.0; + JpModifier = 1.0; + RewardBoxMax = 100; + QuestOrderMax = 20; + string urlDomain = $"http://localhost:{52099}"; UrlManual = $"{urlDomain}/manual_nfb/"; UrlShopDetail = $"{urlDomain}/shop/ingame/stone/detail"; @@ -301,6 +345,14 @@ public GameLogicSetting(GameLogicSetting setting) DefaultMaxBazaarExhibits = setting.DefaultMaxBazaarExhibits; DefaultWarpFavorites = setting.DefaultWarpFavorites; + ExpModifier = setting.ExpModifier; + PpModifier = setting.PpModifier; + BoModifier = setting.BoModifier; + HoModifier = setting.HoModifier; + JpModifier = setting.JpModifier; + RewardBoxMax = setting.RewardBoxMax; + QuestOrderMax = setting.QuestOrderMax; + UrlManual = setting.UrlManual; UrlShopDetail = setting.UrlShopDetail; UrlShopCounterA = setting.UrlShopCounterA; @@ -375,6 +427,26 @@ void OnDeserialized(StreamingContext context) { PawnCatchupMultiplier = 1.0; } + if (ExpModifier < 0) + { + ExpModifier = 1.0; + } + if (PpModifier < 0) + { + PpModifier = 1.0; + } + if (BoModifier < 0) + { + BoModifier = 1.0; + } + if (HoModifier < 0) + { + HoModifier = 1.0; + } + if (JpModifier < 0) + { + JpModifier = 1.0; + } foreach (var walletMax in DefaultWalletLimits) { From 2aa64029b8feab9c8e73d8732755bc3e05ae3821 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sat, 9 Nov 2024 09:19:13 -0500 Subject: [PATCH 10/12] Fix issue with reward manager - Fix issue with indexing when rewards for disabled/non-existing quests are present. - Added rewards to transaction where possible. --- .../Handler/QuestGetRewardBoxItemHandler.cs | 36 +++++++++---------- .../Handler/QuestGetRewardBoxListHandler.cs | 1 + 2 files changed, 19 insertions(+), 18 deletions(-) diff --git a/Arrowgene.Ddon.GameServer/Handler/QuestGetRewardBoxItemHandler.cs b/Arrowgene.Ddon.GameServer/Handler/QuestGetRewardBoxItemHandler.cs index a10314b62..f7b1a569f 100644 --- a/Arrowgene.Ddon.GameServer/Handler/QuestGetRewardBoxItemHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/QuestGetRewardBoxItemHandler.cs @@ -63,34 +63,34 @@ public override S2CQuestGetRewardBoxItemRes Handle(GameClient client, C2SQuestGe } var distinctRewards = packet.GetRewardBoxItemList.Select(x => x.UID).Distinct().ToList(); - var slotCount = coalescedRewards.Sum(x => - distinctRewards.Contains(x.Key) - ? Server.ItemManager.PredictAddItemSlots(client.Character, StorageType.StorageBoxNormal, x.Value.ItemId, x.Value.Num) - : 0); + var slotCount = coalescedRewards.Sum(x => distinctRewards.Contains(x.Key) ? Server.ItemManager.PredictAddItemSlots(client.Character, StorageType.StorageBoxNormal, x.Value.ItemId, x.Value.Num) : 0); if (slotCount > client.Character.Storage.GetStorage(StorageType.StorageBoxNormal).EmptySlots()) { throw new ResponseErrorException(ErrorCode.ERROR_CODE_ITEM_STORAGE_OVERFLOW); } - foreach (var rewardUID in distinctRewards) + Server.Database.ExecuteInTransaction(connection => { - var reward = coalescedRewards[rewardUID]; - if (Server.ItemManager.IsItemWalletPoint(reward.ItemId)) + foreach (var rewardUID in distinctRewards) { - (WalletType walletType, uint amount) = Server.ItemManager.ItemToWalletPoint(reward.ItemId); - var result = Server.WalletManager.AddToWallet(client.Character, walletType, amount * reward.Num); - updateCharacterItemNtc.UpdateWalletList.Add(result); + var reward = coalescedRewards[rewardUID]; + if (Server.ItemManager.IsItemWalletPoint(reward.ItemId)) + { + (WalletType walletType, uint amount) = Server.ItemManager.ItemToWalletPoint(reward.ItemId); + var result = Server.WalletManager.AddToWallet(client.Character, walletType, amount * reward.Num, connectionIn: connection); + updateCharacterItemNtc.UpdateWalletList.Add(result); + } + else if (reward.Num > 0) + { + var result = Server.ItemManager.AddItem(Server, client.Character, false, reward.ItemId, reward.Num, connectionIn: connection); + updateCharacterItemNtc.UpdateItemList.AddRange(result); + } } - else if (reward.Num > 0) - { - var result = Server.ItemManager.AddItem(Server, client.Character, false, reward.ItemId, reward.Num); - updateCharacterItemNtc.UpdateItemList.AddRange(result); - } - } - client.Send(updateCharacterItemNtc); + Server.RewardManager.DeleteQuestBoxReward(client, questBoxReward.UniqRewardId, connectionIn: connection); + }); - Server.RewardManager.DeleteQuestBoxReward(client, questBoxReward.UniqRewardId); + client.Send(updateCharacterItemNtc); return new S2CQuestGetRewardBoxItemRes(); } diff --git a/Arrowgene.Ddon.GameServer/Handler/QuestGetRewardBoxListHandler.cs b/Arrowgene.Ddon.GameServer/Handler/QuestGetRewardBoxListHandler.cs index 869f63f86..5a02a9248 100644 --- a/Arrowgene.Ddon.GameServer/Handler/QuestGetRewardBoxListHandler.cs +++ b/Arrowgene.Ddon.GameServer/Handler/QuestGetRewardBoxListHandler.cs @@ -36,6 +36,7 @@ public override S2CQuestGetRewardBoxListRes Handle(GameClient client, C2SQuestGe var quest = QuestManager.GetQuestByScheduleId(boxReward.QuestScheduleId); if (quest == null) { + listNo += 1; Logger.Error($"Quest reward for QuestScheduleId={boxReward.QuestScheduleId}, but no definition of quest exists."); continue; } From 490d605646f928268fe819038378ff8b25b022bf Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sat, 9 Nov 2024 10:51:02 -0500 Subject: [PATCH 11/12] fix: Restrictions on /invite - Don't allow a player to be invited if they exist in a different game mode. - Restrict party size to 4 if gamemode is bitterblack maze. --- .../Chat/Command/Commands/PartyInviteCommand.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/Arrowgene.Ddon.GameServer/Chat/Command/Commands/PartyInviteCommand.cs b/Arrowgene.Ddon.GameServer/Chat/Command/Commands/PartyInviteCommand.cs index b0cefcb58..d43d8b5a8 100644 --- a/Arrowgene.Ddon.GameServer/Chat/Command/Commands/PartyInviteCommand.cs +++ b/Arrowgene.Ddon.GameServer/Chat/Command/Commands/PartyInviteCommand.cs @@ -2,6 +2,7 @@ using Arrowgene.Ddon.GameServer.Characters; using Arrowgene.Ddon.GameServer.Handler; using Arrowgene.Ddon.Shared.Entity.PacketStructure; +using Arrowgene.Ddon.Shared.Model; using Arrowgene.Ddon.Shared.Network; using System.Collections.Generic; using System.Linq; @@ -43,6 +44,12 @@ public override void Execute(string[] command, GameClient client, ChatMessage me return; } + if (client.GameMode == GameMode.BitterblackMaze && client.Party.Members.Count >= 4) + { + responses.Add(ChatResponse.CommandError(client, "This game mode only supports 4 players.")); + return; + } + if (!client.Party.GetPlayerPartyMember(client).IsLeader) { responses.Add(ChatResponse.CommandError(client, "Only the party leader can invite.")); @@ -112,6 +119,12 @@ public override void Execute(string[] command, GameClient client, ChatMessage me return; } + if (targetClient.GameMode != client.GameMode) + { + responses.Add(ChatResponse.CommandError(client, "You cannot invite players which are in different game modes.")); + return; + } + if (client.Party.Contains(targetClient.Character)) { responses.Add(ChatResponse.CommandError(client, "The party already contains that player.")); From e69797f77325dd7a84a386129b0bd84a602b8b71 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Sat, 9 Nov 2024 17:38:44 -0500 Subject: [PATCH 12/12] fix: Fix DB hang in BBM Fixed an issue where a transaction was not passed into some DB queries which causes a hang. --- Arrowgene.Ddon.Database/IDatabase.cs | 4 ++-- .../Core/DdonSqlDbBitterBlackMazeContentTreasure.cs | 11 ++--------- .../Sql/Core/DdonSqlDbBitterBlackMazeRewards.cs | 11 +++-------- .../Characters/BitterblackMazeManager.cs | 10 +++++----- Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs | 4 ++-- 5 files changed, 14 insertions(+), 26 deletions(-) diff --git a/Arrowgene.Ddon.Database/IDatabase.cs b/Arrowgene.Ddon.Database/IDatabase.cs index 9cc1a51de..2f67291bf 100644 --- a/Arrowgene.Ddon.Database/IDatabase.cs +++ b/Arrowgene.Ddon.Database/IDatabase.cs @@ -547,7 +547,7 @@ ulong lastTicketTime bool InsertBBMRewards(uint characterId, uint goldMarks, uint silverMarks, uint redMarks); bool UpdateBBMRewards(uint characterId, BitterblackMazeRewards rewards, DbConnection? connectionIn = null); bool RemoveBBMRewards(uint characterId); - BitterblackMazeRewards SelectBBMRewards(uint characterId); + BitterblackMazeRewards SelectBBMRewards(uint characterId, DbConnection? connectionIn = null); // Bitterblack Maze Treasure bool InsertBBMContentTreasure( @@ -564,7 +564,7 @@ bool InsertBBMContentTreasure( bool UpdateBBMContentTreasure(uint characterId, BitterblackMazeTreasure treasure); bool UpdateBBMContentTreasure(uint characterId, uint contentId, uint amount); bool RemoveBBMContentTreasure(uint characterId); - List SelectBBMContentTreasure(uint characterId); + List SelectBBMContentTreasure(uint characterId, DbConnection? connectionIn = null); // Clan bool CreateClan(CDataClanParam clanParam); diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbBitterBlackMazeContentTreasure.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbBitterBlackMazeContentTreasure.cs index a54081f0c..0579a16f7 100644 --- a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbBitterBlackMazeContentTreasure.cs +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbBitterBlackMazeContentTreasure.cs @@ -84,16 +84,10 @@ public bool RemoveBBMContentTreasure(TCon connection, uint characterId) }) == 1; } - public List SelectBBMContentTreasure(uint characterId) - { - using TCon connection = OpenNewConnection(); - return SelectBBMContentTreasure(connection, characterId); - } - - public List SelectBBMContentTreasure(TCon connection, uint characterId) + public List SelectBBMContentTreasure(uint characterId, DbConnection? connectionIn = null) { List results = new List(); - ExecuteInTransaction(connection => + ExecuteQuerySafe(connectionIn, (connection) => { ExecuteReader(connection, SqlSelectBBMContentTreasure, command => { @@ -109,7 +103,6 @@ public List SelectBBMContentTreasure(TCon connection, u } }); }); - return results; } } diff --git a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbBitterBlackMazeRewards.cs b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbBitterBlackMazeRewards.cs index 9d1824a11..989c16f15 100644 --- a/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbBitterBlackMazeRewards.cs +++ b/Arrowgene.Ddon.Database/Sql/Core/DdonSqlDbBitterBlackMazeRewards.cs @@ -74,16 +74,11 @@ public bool RemoveBBMRewards(TCon connection, uint characterId) }) == 1; } - public BitterblackMazeRewards SelectBBMRewards(uint characterId) - { - using TCon connection = OpenNewConnection(); - return SelectBBMRewards(connection, characterId); - } - - public BitterblackMazeRewards SelectBBMRewards(TCon connection, uint characterId) + public BitterblackMazeRewards SelectBBMRewards(uint characterId, DbConnection? connectionIn = null) { BitterblackMazeRewards result = null; - ExecuteInTransaction(connection => + + ExecuteQuerySafe(connectionIn, (connection) => { ExecuteReader(connection, SqlSelectBBMRewards, command => { diff --git a/Arrowgene.Ddon.GameServer/Characters/BitterblackMazeManager.cs b/Arrowgene.Ddon.GameServer/Characters/BitterblackMazeManager.cs index 690a7242a..7872570d6 100644 --- a/Arrowgene.Ddon.GameServer/Characters/BitterblackMazeManager.cs +++ b/Arrowgene.Ddon.GameServer/Characters/BitterblackMazeManager.cs @@ -48,14 +48,14 @@ public static uint LevelCap(BitterblackMazeProgress progress) return levelCap; } - public static CDataBattleContentStatus GetUpdatedContentStatus(DdonGameServer server, Character character) + public static CDataBattleContentStatus GetUpdatedContentStatus(DdonGameServer server, Character character, DbConnection? connectionIn = null) { var progress = character.BbmProgress; - var rewards = server.Database.SelectBBMRewards(character.CharacterId); + var rewards = server.Database.SelectBBMRewards(character.CharacterId, connectionIn); var availableRewards = new List(); - var trackedRewards = server.Database.SelectBBMContentTreasure(character.CharacterId); + var trackedRewards = server.Database.SelectBBMContentTreasure(character.CharacterId, connectionIn); foreach (var stage in server.AssetRepository.BitterblackMazeAsset.Stages) { var matches = trackedRewards.Select(x => x.ContentId == stage.Value.ContentId).ToList(); @@ -132,7 +132,7 @@ public static PacketQueue HandleTierClear(DdonGameServer server, GameClient clie } server.Database.UpdateBBMProgress(character.CharacterId, progress, connectionIn); - var rewards = server.Database.SelectBBMRewards(character.CharacterId); + var rewards = server.Database.SelectBBMRewards(character.CharacterId, connectionIn); // TODO: handle BattleContentRewardBonus.Up (some sort of reward bonus) // TODO: Is there a reason we wouldn't get a reward here? var marks = GetMarksForStage(server.AssetRepository.BitterblackMazeAsset, stageId); @@ -143,7 +143,7 @@ public static PacketQueue HandleTierClear(DdonGameServer server, GameClient clie // Update the situation information S2CBattleContentProgressNtc progressNtc = new S2CBattleContentProgressNtc(); - progressNtc.BattleContentStatusList.Add(BitterblackMazeManager.GetUpdatedContentStatus(server, character)); + progressNtc.BattleContentStatusList.Add(BitterblackMazeManager.GetUpdatedContentStatus(server, character, connectionIn)); client.Enqueue(progressNtc, packets); return packets; diff --git a/Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs b/Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs index 69cf636df..4990efe8b 100644 --- a/Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs +++ b/Arrowgene.Ddon.Test/Database/DatabaseMigratorTest.cs @@ -381,13 +381,13 @@ public bool UpdateRentalPawnSlot(uint characterId, uint num) public bool InsertBBMRewards(uint characterId, uint goldMarks, uint silverMarks, uint redMarks) { return true; } public bool UpdateBBMRewards(uint characterId, BitterblackMazeRewards rewards, DbConnection? connectionIn = null) { return true; } public bool RemoveBBMRewards(uint characterId) { return true; } - public BitterblackMazeRewards SelectBBMRewards(uint characterId) { return new BitterblackMazeRewards(); } + public BitterblackMazeRewards SelectBBMRewards(uint characterId, DbConnection? connectionIn = null) { return new BitterblackMazeRewards(); } public bool InsertBBMContentTreasure(uint characterId, BitterblackMazeTreasure treasure, DbConnection? connectionIn = null) { return true; } public bool InsertBBMContentTreasure(uint characterId, uint contentId, uint amount, DbConnection? connectionIn = null) { return true; } public bool UpdateBBMContentTreasure(uint characterId, BitterblackMazeTreasure treasure) { return true; } public bool UpdateBBMContentTreasure(uint characterId, uint contentId, uint amount) { return true; } public bool RemoveBBMContentTreasure(uint characterId) { return true; } - public List SelectBBMContentTreasure(uint characterId) { return new List(); } + public List SelectBBMContentTreasure(uint characterId, DbConnection? connectionIn = null) { return new List(); } public List SelectOfficialPawns() { return new List(); } public List SelectAllPlayerPawns(uint limit = 100) { return new List(); } public List SelectAllPlayerPawns(DbConnection connection, uint limit = 100) { return new List(); }