이번에는 패치 다운로드를 UnityWebRequest(구 WWW)와 Coroutine 이용하여 다운로드하도록 구현해보겠습니다.
1. 패치 파일 업로드
실제 프로젝트에서는 보통은 CDN을 사용하는데, 저의 경우에는 간단하게 테스트하기 위해서는 XAMPP를 주로 사용합니다.
저의 경우에는 GitHub 리포에 올려두고 파일의 Raw 데이터 주소를 통해서 다운로드하도록 만들어두었습니다.
개인적으롤 사용 가능한 웹서버 및 CDN이 있으시다면 사용하셔도 무방합니다.
1.1 XAMPP
https://www.apachefriends.org/
XAMPP Installers and Downloads for Apache Friends
What is XAMPP? XAMPP is the most popular PHP development environment XAMPP is a completely free, easy to install Apache distribution containing MariaDB, PHP, and Perl. The XAMPP open source package has been set up to be incredibly easy to install and to us
www.apachefriends.org
1.2 GitHub
GitHub에 올라간 파일을 열어보면 우측 상단에 [Raw] 버튼을 누르면 원본 파일을 받을 수 있습니다.
저의 경우에는 아래와 같은 주소를 이용하고 있습니다.
2. 패치 리스트 다운로드
const string PATCH_LIST_FILENAME = "PatchList.json";
const string PATCHED_LIST_FILENAME = "PatchedList.json";
const string PATCH_BASE_URI = "https://raw.githubusercontent.com/PieceOfPaper/Unity_SimplePatchExample/main/PatchFiles";
const string SAVE_PATCH_PATH = "PatchFiles";
패치 경로 및 파일 이름 관련 정보들은 코드 내에서 계속해서 사용하기 때문에 const로 만들어두었습니다.
m_PatchList = null;
var uri = $"{PATCH_BASE_URI}/{m_Platform}/{PATCH_LIST_FILENAME}";
using (var request = UnityEngine.Networking.UnityWebRequest.Get(uri))
{
request.SetRequestHeader("Cache-Control", "max-age=0, no-cache, no-store");
request.SendWebRequest();
yield return request;
if (string.IsNullOrEmpty(request.error) == false)
{
Debug.LogError(request.error);
}
else
{
m_PatchList = JsonUtility.FromJson<PatchDataList>(request.downloadHandler.text);
}
}
간단하게 UnityWebRequest를 통해 다운로드하여 JsonUtility로 파싱 하는 코드입니다.
현재는 헤더로 Cache-Control만 추가했는데, 필요시에 아래 매뉴얼 링크 보시고 추가하시면 됩니다.
https://docs.unity3d.com/ScriptReference/Networking.UnityWebRequest.html
Unity - Scripting API: UnityWebRequest
UnityWebRequest handles the flow of HTTP communication with web servers. To download and upload data, use DownloadHandler and UploadHandler respectively. UnityWebRequest includes static utility functions that return UnityWebRequest instances configured for
docs.unity3d.com
https://docs.unity3d.com/ScriptReference/JsonUtility.html
Unity - Scripting API: JsonUtility
Success! Thank you for helping us improve the quality of Unity Documentation. Although we cannot accept all submissions, we do read each suggested change from our users and will make updates where applicable. Close
docs.unity3d.com
3. 패치한 필요한 데이터 리스트 만들기
public IEnumerable<PatchData> GetNeedPatchDatas()
{
//이미 패치된 데이터 불러오기
var pathedListPath = System.IO.Path.Combine(Application.persistentDataPath, SAVE_PATCH_PATH, PATCHED_LIST_FILENAME);
string jsonText = System.IO.File.Exists(pathedListPath) ? System.IO.File.ReadAllText(pathedListPath) : null;
var patchedDataList = string.IsNullOrEmpty(jsonText) ? null : JsonUtility.FromJson<PatchDataList>(jsonText);
if (patchedDataList == null) patchedDataList = new PatchDataList();
//패치가 필요한 데이터 리스트 만들기
List<PatchData> needPatchDataList = new List<PatchData>();
if (m_PatchList != null)
{
foreach (var patchData in m_PatchList.dataList)
{
var pathedData = patchedDataList.dataList.Find(m => m.fileName == patchData.fileName);
if (pathedData == null || pathedData.version != patchData.version)
{
needPatchDataList.Add(patchData);
}
}
}
return needPatchDataList;
}
서버로부터 받은 패치 리스트와 클라이언트에 저장된 이미 패치된 목록을 불러와 비교하여 값이 없거나 version이 다르면 리스트에 담도록 구현했습니다.
실제 프로젝트에서는 version을 어떻게 쓰느냐에 따라 다르긴 한데, 이 부분은 프로젝트 진행하실 때 맞게 구현하시면 됩니다.
4. 패치 파일 다운로드
//이미 패치된 데이터 불러오기
var pathedListPath = System.IO.Path.Combine(Application.persistentDataPath, SAVE_PATCH_PATH, PATCHED_LIST_FILENAME);
string jsonText = System.IO.File.Exists(pathedListPath) ? System.IO.File.ReadAllText(pathedListPath) : null;
m_PatchedList = string.IsNullOrEmpty(jsonText) ? null : JsonUtility.FromJson<PatchDataList>(jsonText);
if (m_PatchedList == null) m_PatchedList = new PatchDataList();
//패치 파일 다운로드
for (int i = 0; i < needPatchDataList.Count; i++)
{
var patchData = needPatchDataList[i];
var uri = $"{PATCH_BASE_URI}/{m_Platform}/{patchData.fileName}";
var savePath = System.IO.Path.Combine(Application.persistentDataPath, SAVE_PATCH_PATH, patchData.fileName);
using (var request = UnityEngine.Networking.UnityWebRequest.Get(uri))
{
request.downloadHandler = new UnityEngine.Networking.DownloadHandlerFile(savePath);
request.SetRequestHeader("Cache-Control", "max-age=0, no-cache, no-store");
request.SendWebRequest();
yield return request;
if (string.IsNullOrEmpty(request.error) == false)
{
Debug.LogError($"[PatchManager] DownloadPatchFiles Error - {request.error}");
}
else
{
//패치리스트 갱신
var pathedData = m_PatchedList.dataList.Find(m => m.fileName == patchData.fileName);
if (pathedData == null)
{
pathedData = new PatchData(patchData);
m_PatchedList.dataList.Add(pathedData);
}
else
{
pathedData.UpdateData(pathedData);
}
}
}
}
//패치리스트 저장
System.IO.File.WriteAllText(pathedListPath, JsonUtility.ToJson(m_PatchedList, false));
이전에 만들어둔 리스트를 이용해 UnityWebRequest로 다운로드 진행하는 코드입니다.
다운로드 진행 후에는 리스트를 갱신시켜주고 다운로드가 완료되면 다시 json으로 저장하도록 되어있습니다.
실제 프로젝트에서는 다운로드 도중 게임을 종료하는 상황을 대응하기 위해 다운로드 후에 패치 리스트를 갱신하는데, 패치파일이 많아지는 경우 패치 리스트를 갱신 후 파일에 저장하는 비용도 꽤 들기 때문에 이 부분에서는 조금 고민해보시길 바랍니다.
마무리
IEnumerator Start()
{
// 2. 패치 리스트 다운로드
yield return StartCoroutine(DownloadPatchListRoutine());
// 3. 패치한 필요한 데이터 리스트 만들기
var needPatchDatas = GetNeedPatchDatas();
// 4. 패치 파일 다운로드
yield return StartCoroutine(DownloadPatchFilesRoutine(needPatchDatas));
}
간단하게 패치 리스트를 만들고 패치 파일을 받는 코드를 만들어봤습니다.
위 코드처럼 저희가 구현한 함수들을 순차적으로 호출만 해주면 다운로드가 진행될 겁니다.
다음에 진행할 내용은 실제 게임에서 출력할 UI를 위해 파일 개수, 진행도, 파일크기 관련한 내용을 적어보겠습니다.
이번에도 긴 글 읽어주셔서 감사합니다.
https://github.com/PieceOfPaper/Unity_SimplePatchExample
GitHub - PieceOfPaper/Unity_SimplePatchExample: 그냥 심플하게 패치 받는 예제
그냥 심플하게 패치 받는 예제. Contribute to PieceOfPaper/Unity_SimplePatchExample development by creating an account on GitHub.
github.com
'Unity Tips' 카테고리의 다른 글
Unity에서 패치 다운로드 만들기 4편 - 다른 스레드 이용하여 만들기 (0) | 2022.05.08 |
---|---|
Unity에서 패치 다운로드 만들기 3편 - 패치 UI 만들기 (0) | 2022.05.07 |
Unity에서 패치 다운로드 만들기 1편 - 패치 리스트 만들기 (0) | 2022.05.07 |
Unity에서 Enum.Parse함수 GC 발생 줄이기 (0) | 2022.05.01 |
Unity에서 계층이 있는 데이터 저장 (JSON vs XML vs Scriptable Object) (0) | 2022.04.30 |