고객님의 문의에 답변하는 직원은 고객 여러분의 가족 중 한 사람일 수 있습니다.
고객의 언어폭력(비하, 조롱, 욕설, 협박, 성희롱 등)으로부터 직원을 보호하기 위해
관련 법에 따라 수사기관에 필요한 조치를 요구할 수 있으며, 형법에 의해 처벌 대상이 될 수 있습니다.
커뮤니티 이용 정책에 위배되는 게시물을 작성할 경우, 별도 안내 없이 게시물 삭제 또는 커뮤니티 이용이 제한될 수 있습니다.
문의 응대 : 평일 오전 10시 ~ 오후 6시
문의를 남기실 경우 다음 항목을 작성해 주세요.
정보가 부족하거나 응대시간 외 문의하는 경우 확인 및 답변이 지연될 수 있습니다.
- 뒤끝 SDK 버전 :
- 프로젝트명 : 쿠노이치 키우기
- 스테이터스 코드 :
- 에러 코드 :
- 에러 메시지 :
안녕하세요.
기존에 차트를 한번에 요청해서 받아왔는데, 콜백 수가 초과되는 현상이 있어서 하나씩 가져오도록 코드를 변경했습니다.
변경 후에 안정화는 되었지만 로딩 속도가 많이 느린 것 같아 문의를 남깁니다.
혹시 개선할 수 있는 방안이 있을까요?
using System.Collections;
using System.Collections.Generic;
using System;
using UnityEngine;
using LitJson;
using BackEnd;
using TheBackend;
using System.IO;
using System.Linq;
using GameUtils;
using System.Text;
using UnityEngine.SceneManagement;
[Serializable]
public class ChartInfoData
{
public string keyName = string.Empty;
public string chartName = string.Empty;
public string chartExplain = string.Empty;
public string old = string.Empty;
public bool isChartUpload = false;
public bool isLiveChart = false;
public string chartVersion = string.Empty;
#region new
public int selectedChartFileId = 0;
#endregion
#region old
public string uuid = string.Empty;
public string inDate = string.Empty;
#endregion
public ChartInfoData(JsonData jsonData)
{
this.chartName = jsonData.ContainsKey("chartName") ? jsonData["chartName"].ToString() : string.Empty;
this.chartExplain = jsonData.ContainsKey("chartExplain") ? jsonData["chartExplain"].ToString() : string.Empty;
this.old = jsonData.ContainsKey("old") ? jsonData["old"].ToString() : string.Empty;
string[] chartNameCuts = chartName.Split('_');
if (chartNameCuts.Length == 2)
{
this.keyName = chartNameCuts[0];
if (chartNameCuts[1].Equals("Live"))
this.isLiveChart = true;
else if (chartNameCuts[1].Equals("Dev"))
this.isLiveChart = false;
}
else if (chartNameCuts.Length == 3)
{
this.keyName = chartNameCuts[0];
if (chartNameCuts[1].Equals("Live"))
this.isLiveChart = true;
else if (chartNameCuts[1].Equals("Dev"))
this.isLiveChart = false;
chartVersion = chartNameCuts[2];
}
else
{
this.keyName = chartName;
}
int outNum = 0;
if (int.TryParse(jsonData["selectedChartFileId"].ToString(), out outNum))
{
this.isChartUpload = true;
this.selectedChartFileId = outNum;
}
else
{
this.isChartUpload = false;
this.selectedChartFileId = 0;
this.uuid = jsonData.ContainsKey("uuid") ? jsonData["uuid"].ToString() : string.Empty;
this.inDate = jsonData.ContainsKey("inDate") ? jsonData["inDate"].ToString() : string.Empty;
}
}
}
public class CChartManager : Singleton
{
// 차트정보 데이터
List chartInfos = new();
Dictionary<int, ChartInfoData> dicChartInfoDatas = new();
// 차트 데이터
Dictionary<string, JsonData> dicChartJsonDatas = new();
public Action LoadComplete = null;
public Action LoadAllComplete = null;
int curLoadChartCount = 0;
public int CurLoadChartCount { get { return curLoadChartCount; } }
// === ADD: 이번 세션의 목표 개수를 고정(중간 변동 방지) ===
private int expectedLoadCount = 0;
public int MaxLoadChartCount { get { return expectedLoadCount; } }
public CStageData stageData;
public CMonsterData monsterData;
public CGoodsData goodsData;
public CConstData constData;
public CCharLevelData charLevelData;
public CStringData stringData;
public CCharStatData charStatData;
public CStatData statData;
public CCharStatBonusData charStatBonusData;
public CSummonData summonData;
public CEquipmentData equipmentData;
public CHiddenEquipSubStatData hiddenEquipSubStatData;
public CHiddenEquipData hiddenEquipData;
public CHiddenEquipmentLevelData hiddenEquipmentLevelData;
public CAdBuffData adbuffData;
public CBreathingStatData breathingStatData;
public CBreathingStatBonusData breathingStatBonusData;
public CIntentionStatData intentionStatData;
public CIntentionAwakenStatData intentionAwakenStatData;
public CIntentionAwakenLockData intentionAwakenLockData;
public CSkillData skillData;
public CPromoStatData promoStatData;
public CPromoMissionData promoMissionData;
public CArtifactData artifactData;
public CStageCollectionData stageCollectionData;
public CLevelCollectionData levelCollectionData;
public CDPromoData promoData;
public CDGiantData giantData;
public CPromoTitleData promoTitleData;
public CDEquipData equipDungeonData;
public CCostumeData costumeData;
public CMissionData missionData;
public CShopData shopData;
public CDBreathData breathData;
public CDSkillData skillDungeonData;
public CDGoldExpData goldExpData;
public CDIntentionData intentionDungeonData;
public CEventAttendanceData eventAttendanceData;
public CEventMissionData eventMissionData;
public CEventPassMissionData eventPassMissionData;
public CPassData passData;
public CGuideQuestData guideQuestData;
public CDForsakenData forsakenData;
public CSurpriseShopData surpriseShopData;
public bool isChartLoadComplete = false;
// === ADD: 경합/중복 방지용 세션 및 완료 집합 ===
private int loadSessionId = 0; // 로드 세션 토큰(증가시 지난 콜백 무효화)
private HashSet<int> completedFileIds = new(); // 완료된 selectedChartFileId 모음
private bool isLoadingCharts = false; // 한 세션의 마감 제어
public void LoadAllChartData()
{
chartInfos.Clear();
dicChartInfoDatas.Clear();
dicChartJsonDatas.Clear();
// === ADD: 세션/상태 초기화 ===
loadSessionId++; // 새 세션 시작
expectedLoadCount = 0; // 목표 개수는 GetChartList 이후 고정
curLoadChartCount = 0;
completedFileIds.Clear();
isLoadingCharts = true;
isChartLoadComplete = false;
GetChartList();
}
void GetChartList()
{
chartInfos.Clear();
dicChartInfoDatas.Clear();
bool isLiveBuild = !CGameManager.Instance.isDevelopBuild;
int foldid = 2194;
if (isLiveBuild)
{
foldid = 2442;
}
// 화이트리스트 체크 (동작 동일)
SendQueue.Enqueue(Backend.Chart.GetChartContents, "201241", (callback) =>
{
if (callback.IsSuccess())
{
JsonData jsonData = callback.FlattenRows();
for (int i = 0; i < jsonData.Count; i++)
{
JsonData jData = jsonData[i];
string Nickname = GameUtils.GameUtils.TryParse<string>("Nickname", jData);
if (CGameManager.Instance.nickName.Equals(Nickname))
{
CGameManager.Instance.isWhiteList = true;
break;
}
}
}
else
CGameManager.Instance.isWhiteList = false;
});
Backend.Chart.GetChartListByFolder(foldid, (callback) =>
{
if (callback.IsSuccess())
{
JsonData json = callback.FlattenRows();
for (int i = 0; i < json.Count; i++)
{
JsonData chartData = json[i];
ChartInfoData chartInfoData = new ChartInfoData(chartData);
if (chartInfoData == null || chartInfoData.selectedChartFileId == 0)
continue;
if (isLiveBuild == chartInfoData.isLiveChart)
{
int selectedChartFileId = chartInfoData.selectedChartFileId;
if (dicChartInfoDatas.ContainsKey(selectedChartFileId) == false)
dicChartInfoDatas.Add(selectedChartFileId, chartInfoData);
}
chartInfos.Add(chartInfoData);
}
// === ADD: 이번 세션의 목표 개수 고정 ===
expectedLoadCount = dicChartInfoDatas.Count;
SetChartDataToLocal();
}
else
{
ShowErr(callback);
}
});
}
void CheckWhiteList(string selectedChartFileId, Action complete)
{
SendQueue.Enqueue(Backend.Chart.GetChartContents, selectedChartFileId, (callback) =>
{
if (callback.IsSuccess())
{
JsonData jsonData = callback.FlattenRows();
for (int i = 0; i < jsonData.Count; i++)
{
JsonData jData = jsonData[i];
string Nickname = GameUtils.GameUtils.TryParse<string>("Nickname", jData);
if (CGameManager.Instance.nickName.Equals(Nickname))
{
CGameManager.Instance.isWhiteList = true;
break;
}
}
}
else
CGameManager.Instance.isWhiteList = false;
if (complete != null)
complete();
complete = null;
});
}
void SetChartDataToLocal()
{
StopAllCoroutines();
StartCoroutine(CoSetChartDataToLocal());
}
void HandleLoadComplete()
{
LoadComplete?.Invoke();
}
void LoadFinish()
{
StartCoroutine(DelayLoadScene());
}
IEnumerator DelayLoadScene()
{
float timer = 0f;
while (timer < 1f)
{
timer += Time.unscaledDeltaTime;
yield return null;
}
PlayerPrefs.Save();
LoadAllComplete?.Invoke();
LoadComplete = null;
LoadAllComplete = null;
}
void ShowErr(BackendReturnObject _callback)
{
if (_callback == null)
ShowErr();
else
{
string context = string.Format("statusCode : {0}\nerrCode : {1}\nmsg : {2}", _callback.GetStatusCode(), _callback.GetErrorCode(), _callback.GetMessage());
ShowErr(context);
}
}
void ShowErr(string context = null)
{
PlayerPrefs.DeleteAll();
if (string.IsNullOrEmpty(context))
context = CStringManager.Instance.GetStringText("Msg_NetworkDisconnect");
CBackendManager.Instance.BackendError.ShowErrPopup(context);
}
public void removeChartData(string _chartKey)
{
if (dicChartJsonDatas.ContainsKey(_chartKey))
{
dicChartJsonDatas.Remove(_chartKey);
}
}
public JsonData GetChartJsonData(string _chartKey)
{
if (dicChartJsonDatas.ContainsKey(_chartKey))
return dicChartJsonDatas[_chartKey];
if (!isChartLoadComplete)
{
return null;
}
string chartContent = Backend.Chart.GetLocalChartData(_chartKey);
if (string.IsNullOrEmpty(chartContent))
{
ShowErr(string.Format("error : {0}", _chartKey));
return null;
}
JsonData jsonData = JsonMapper.ToObject(chartContent);
if (jsonData.ContainsKey("rows"))
jsonData = jsonData["rows"];
if (jsonData != null)
dicChartJsonDatas.Add(_chartKey, jsonData);
return jsonData;
}
IEnumerator CoSetChartDataToLocal()
{
// === ADD: 세션 토큰 캡처 (이 코루틴은 이 세션에만 유효) ===
int session = loadSessionId;
foreach (var item in dicChartInfoDatas.ToList())
{
var chartInfoData = item.Value;
string chartKey = chartInfoData.keyName;
int selectedChartFileId = chartInfoData.selectedChartFileId;
// === ADD: 동일 파일 중복 증가 방지 ===
if (completedFileIds.Contains(selectedChartFileId))
{
yield return null;
continue;
}
int lastFileID = PlayerPrefs.GetInt(chartKey, 0);
int curFileID = selectedChartFileId;
// === ADD: 공통 완료 처리(세션/중복 체크 포함) ===
Action<bool> onDone = (bool success) =>
{
// 지난 세션에서 온 콜백이면 무시
if (session != loadSessionId) return;
if (!success) { ShowErr(); return; }
// 중복 완료 방지: 새로 추가된 경우에만 카운트 증가
if (!completedFileIds.Add(selectedChartFileId)) return;
curLoadChartCount += 1;
// 방어적 클램프 (이상 증가 방지)
if (curLoadChartCount > expectedLoadCount)
curLoadChartCount = expectedLoadCount;
HandleLoadComplete();
// 모두 끝났다면 한 번만 마무리
if (curLoadChartCount >= expectedLoadCount && isLoadingCharts)
{
isLoadingCharts = false;
isChartLoadComplete = true;
LoadFinish();
}
};
if (lastFileID != curFileID)
{
SendQueue.Enqueue(Backend.Chart.GetOneChartAndSaveV2, selectedChartFileId.ToString(), chartKey, callback =>
{
if (callback.IsSuccess())
{
PlayerPrefs.SetInt(chartKey, selectedChartFileId);
onDone(true);
}
else onDone(false);
});
}
else
{
string chartData = Backend.Chart.GetLocalChartData(chartKey);
if (string.IsNullOrEmpty(chartData))
{
SendQueue.Enqueue(Backend.Chart.GetOneChartAndSaveV2, selectedChartFileId.ToString(), chartKey, callback =>
{
if (callback.IsSuccess())
{
PlayerPrefs.SetInt(chartKey, selectedChartFileId);
onDone(true);
}
else onDone(false);
});
}
else
{
// 로컬에 이미 존재 → 즉시 완료 처리
onDone(true);
}
}
yield return null;
}
// === ADD: 세션/목표 기반 대기(중간에 세션 바뀌면 즉시 빠져나감) ===
while (session == loadSessionId && curLoadChartCount < expectedLoadCount)
yield return null;
if (session == loadSessionId && isLoadingCharts)
{
isLoadingCharts = false;
isChartLoadComplete = true;
LoadFinish();
}
yield return null;
}
}