@@ -99,6 +99,30 @@ public partial class DdonSqlDb : SqlDb
9999
100100 private readonly string SqlUpdatePartnerPawnId = "UPDATE \" ddon_character\" SET \" partner_pawn_id\" = @partner_pawn_id WHERE \" character_id\" = @character_id;" ;
101101
102+ private readonly string SqlSelectAllCompletedQuests =
103+ $ "SELECT { BuildQueryField ( CompletedQuestsFields ) } FROM \" ddon_completed_quests\" WHERE \" character_common_id\" = @character_common_id;";
104+
105+ private static readonly string [ ] StorageItemFieldsForCharacter = new [ ]
106+ {
107+ "item_uid" , "item_id" , "safety" , "color" , "plus_value" , "equip_points"
108+ } ;
109+
110+ private static readonly string [ ] CrestFieldsForCharacter = new [ ]
111+ {
112+ "character_common_id" , "item_uid" , "slot" , "crest_id" , "crest_amount"
113+ } ;
114+
115+ private static readonly string [ ] EquipmentLimitBreakFieldsForCharacter = new [ ]
116+ {
117+ "item_uid" , "effect_id" , "unk1" , "effect_type" , "unk0"
118+ } ;
119+
120+ private static readonly string SqlSelectStorageCrestDataByCharacter =
121+ $ "SELECT { BuildQueryField ( CrestFieldsForCharacter ) } FROM \" ddon_crests\" WHERE \" character_common_id\" = @character_common_id;";
122+
123+ private static readonly string SqlSelectStorageLimitBreakByCharacter =
124+ $ "SELECT { BuildQueryField ( EquipmentLimitBreakFieldsForCharacter ) } FROM \" ddon_equipment_limit_break\" WHERE \" character_id\" = @character_id;";
125+
102126 public override bool CreateCharacter ( Character character )
103127 {
104128 return ExecuteInTransaction ( conn =>
@@ -242,7 +266,7 @@ public override bool DeleteCharacter(uint characterId)
242266
243267 private void QueryCharacterData ( DbConnection conn , Character character )
244268 {
245- QueryCharacterCommonData ( conn , character ) ;
269+ QueryCharacterCommonData ( conn , character , character . CharacterId ) ;
246270
247271 // Shortcuts
248272 ExecuteReader ( conn , SqlSelectShortcuts ,
@@ -271,44 +295,78 @@ private void QueryCharacterData(DbConnection conn, Character character)
271295 character . Storage . AddStorage ( tuple . Item1 , tuple . Item2 ) ;
272296 }
273297 } ) ;
298+
299+ // Materialize storage item rows before running batch crest/limit-break queries.
300+ var storageRows = new List < ( StorageType StorageType , ushort Slot , uint ItemNum , Item Item ) > ( ) ;
274301 ExecuteReader ( conn , SqlSelectStorageItemsByCharacter ,
275- ( Action < DbCommand > ) ( command2 => { AddParameter ( command2 , "@character_id" , character . CharacterId ) ; } ) ,
276- reader2 =>
302+ ( Action < DbCommand > ) ( command => { AddParameter ( command , "@character_id" , character . CharacterId ) ; } ) ,
303+ reader =>
277304 {
278- while ( reader2 . Read ( ) )
305+ while ( reader . Read ( ) )
279306 {
280- StorageType storageType = ( StorageType ) GetByte ( reader2 , "storage_type" ) ;
281- ushort slot = GetUInt16 ( reader2 , "slot_no" ) ;
282- uint itemNum = GetUInt32 ( reader2 , "item_num" ) ;
283- Item item = new ( ) ;
307+ storageRows . Add ( (
308+ ( StorageType ) GetByte ( reader , "storage_type" ) ,
309+ GetUInt16 ( reader , "slot_no" ) ,
310+ GetUInt32 ( reader , "item_num" ) ,
311+ new Item
312+ {
313+ UId = GetString ( reader , "item_uid" ) ,
314+ ItemId = GetUInt32 ( reader , "item_id" ) ,
315+ SafetySetting = GetByte ( reader , "safety" ) ,
316+ Color = GetByte ( reader , "color" ) ,
317+ PlusValue = GetByte ( reader , "plus_value" ) ,
318+ EquipPoints = GetUInt32 ( reader , "equip_points" )
319+ }
320+ ) ) ;
321+ }
322+ } ) ;
284323
285- item . UId = GetString ( reader2 , "item_uid" ) ;
286- item . ItemId = GetUInt32 ( reader2 , "item_id" ) ;
287- item . SafetySetting = GetByte ( reader2 , "safety" ) ;
288- item . Color = GetByte ( reader2 , "color" ) ;
289- item . PlusValue = GetByte ( reader2 , "plus_value" ) ;
290- item . EquipPoints = GetUInt32 ( reader2 , "equip_points" ) ;
324+ if ( storageRows . Count > 0 )
325+ {
326+ var crestMap = new Dictionary < string , List < CDataEquipElementParam > > ( ) ;
327+ using DbConnection crestConn = OpenNewConnection ( ) ;
328+ ExecuteReader ( crestConn , SqlSelectStorageCrestDataByCharacter ,
329+ command => { AddParameter ( command , "@character_common_id" , character . CommonId ) ; } ,
330+ reader =>
331+ {
332+ while ( reader . Read ( ) )
333+ {
334+ string uid = GetString ( reader , "item_uid" ) ;
335+ if ( ! crestMap . TryGetValue ( uid , out var list ) )
336+ crestMap [ uid ] = list = new List < CDataEquipElementParam > ( ) ;
337+ list . Add ( ReadCrestData ( reader ) . ToCDataEquipElementParam ( ) ) ;
338+ }
339+ } ) ;
291340
292- using DbConnection connection = OpenNewConnection ( ) ;
293- ExecuteReader ( connection , SqlSelectAllCrestData ,
294- command4 =>
295- {
296- AddParameter ( command4 , "character_common_id" , character . CommonId ) ;
297- AddParameter ( command4 , "item_uid" , item . UId ) ;
298- } , reader4 =>
341+ var limitBreakMap = new Dictionary < string , List < CDataAddStatusParam > > ( ) ;
342+ using DbConnection limitBreakConn = OpenNewConnection ( ) ;
343+ ExecuteReader ( limitBreakConn , SqlSelectStorageLimitBreakByCharacter ,
344+ command => { AddParameter ( command , "@character_id" , character . CharacterId ) ; } ,
345+ reader =>
346+ {
347+ while ( reader . Read ( ) )
348+ {
349+ string uid = GetString ( reader , "item_uid" ) ;
350+ if ( ! limitBreakMap . TryGetValue ( uid , out var list ) )
351+ limitBreakMap [ uid ] = list = new List < CDataAddStatusParam > ( ) ;
352+ list . Add ( new CDataAddStatusParam
299353 {
300- while ( reader4 . Read ( ) )
301- {
302- Crest result = ReadCrestData ( reader4 ) ;
303- item . EquipElementParamList . Add ( result . ToCDataEquipElementParam ( ) ) ;
304- }
354+ EnhanceId = GetUInt16 ( reader , "effect_id" ) ,
355+ Unk1 = GetUInt16 ( reader , "unk1" ) ,
356+ EnhanceType = ( EquipEnhanceType ) GetByte ( reader , "effect_type" ) ,
357+ Unk0 = GetByte ( reader , "unk0" )
305358 } ) ;
359+ }
360+ } ) ;
306361
307- item . AddStatusParamList = GetEquipmentLimitBreakRecord ( item . UId , connection ) ;
308-
309- character . Storage . GetStorage ( storageType ) . SetItem ( item , itemNum , slot ) ;
310- }
311- } ) ;
362+ foreach ( var ( storageType , slot , itemNum , item ) in storageRows )
363+ {
364+ if ( crestMap . TryGetValue ( item . UId , out var crests ) )
365+ item . EquipElementParamList . AddRange ( crests ) ;
366+ item . AddStatusParamList = limitBreakMap . GetValueOrDefault ( item . UId , new List < CDataAddStatusParam > ( ) ) ;
367+ character . Storage . GetStorage ( storageType ) . SetItem ( item , itemNum , slot ) ;
368+ }
369+ }
312370
313371 // Wallet Points
314372 ExecuteReader ( conn , SqlSelectWalletPoints ,
@@ -350,29 +408,25 @@ private void QueryCharacterData(DbConnection conn, Character character)
350408 while ( reader . Read ( ) ) character . AbilityPresets . Add ( ReadAbilityPreset ( reader ) ) ;
351409 } ) ;
352410
353- // Quest Completion
354- foreach ( QuestType questType in Enum . GetValues < QuestType > ( ) )
355- ExecuteReader ( conn , SqlSelectCompletedQuestByType ,
356- command =>
357- {
358- AddParameter ( command , "@character_common_id" , character . CommonId ) ;
359- AddParameter ( command , "@quest_type" , ( uint ) questType ) ;
360- } , reader =>
411+ // Quest Completion - single query for all types, replaces per-QuestType loop.
412+ // QuestType is read directly from the stored column, identical end result to vanilla.
413+ ExecuteReader ( conn , SqlSelectAllCompletedQuests ,
414+ command => { AddParameter ( command , "@character_common_id" , character . CommonId ) ; } ,
415+ reader =>
416+ {
417+ while ( reader . Read ( ) )
361418 {
362- while ( reader . Read ( ) )
419+ CompletedQuest quest = new ( )
363420 {
364- CompletedQuest quest = new ( )
365- {
366- QuestId = ( QuestId ) GetUInt32 ( reader , "quest_id" ) ,
367- QuestType = questType ,
368- ClearCount = GetUInt32 ( reader , "clear_count" )
369- } ;
370-
371- character . CompletedQuests . TryAdd ( quest . QuestId , quest ) ;
372- }
373- } ) ;
421+ QuestId = ( QuestId ) GetUInt32 ( reader , "quest_id" ) ,
422+ QuestType = ( QuestType ) GetUInt32 ( reader , "quest_type" ) ,
423+ ClearCount = GetUInt32 ( reader , "clear_count" )
424+ } ;
425+ character . CompletedQuests . TryAdd ( quest . QuestId , quest ) ;
426+ }
427+ } ) ;
374428
375- //Clan membership
429+ // Clan membership
376430 character . ClanId = SelectClanMembershipByCharacterId ( character . CharacterId , conn ) ;
377431 character . ClanName = GetClanNameByClanId ( character . ClanId ) ;
378432
@@ -470,7 +524,7 @@ public override void CreateItems(DbConnection conn, Character character)
470524 continue ;
471525
472526 // Give starter weapon for all classes
473- // If creating a character for normal mode, Wwe are only interested in slot 1 and 2
527+ // If creating a character for normal mode, we are only interested in slot 1 and 2
474528 for ( byte i = 0 ; i < 2 ; i ++ )
475529 {
476530 Item item = jobEquipment . Value [ EquipType . Performance ] [ i ] ;
@@ -512,7 +566,7 @@ public override void CreateItems(DbConnection conn, Character character)
512566 }
513567 }
514568
515- //Helper function to add specific items to a storage
569+ // Helper function to add specific items to a storage
516570 public override void CreateListItems ( DbConnection conn , Character character , StorageType storageType , List < ( uint ItemId , uint Amount ) > itemList )
517571 {
518572 Storage itemType = character . Storage . GetAllStorages ( ) [ storageType ] ;
@@ -533,11 +587,7 @@ public override Storages SelectAllStoragesByCharacterId(uint characterId)
533587 /// <summary>
534588 /// TODO: Optimize connection handling here and avoid nested loops re-using a single connection which is not supported with Npgsql.
535589 /// Temporary workaround: Open a new connection for each nested read.
536- ///
537590 /// </summary>
538- /// <param name="connection"></param>
539- /// <param name="characterId"></param>
540- /// <returns></returns>
541591 public Storages SelectAllStoragesByCharacterId ( DbConnection connection , uint characterId )
542592 {
543593 Storages storages = new ( new Dictionary < StorageType , ushort > ( ) ) ;
@@ -644,8 +694,6 @@ private Character ReadAllCharacterData(DbDataReader reader)
644694 character . MatchingProfile . Comment = GetString ( reader , "comment" ) ;
645695 character . MatchingProfile . IsJoinParty = GetBoolean ( reader , "is_join_party" ) ;
646696
647-
648-
649697 character . FavWarpSlotNum = GetUInt32 ( reader , "fav_warp_slot_num" ) ;
650698
651699 character . MaxBazaarExhibits = GetUInt32 ( reader , "max_bazaar_exhibits" ) ;
0 commit comments