Overview
com.111percent.scripting.percent-local-save
Feature
데이터 형식을 내부 스토리지에 Json 형식으로 저장할 수 있는 기능을 제공하는 패키지입니다.
이게 뭔가요?
데이터 객체를 Json 직렬화를 통해 물리디스크에 저장할 수 있는 패키지입니다.
내부 로직에 의해 쉽고 빠르게 저장 및 불러오기가 가능합니다.
또한 호환되는 패키지가 있는경우 해당 패키지와 함께 사용할 수 있는 기능을 갖추고 있습니다.
왜 필요한가요?
기존 Percent Save 패키지보다 더 빠르고 쉽게 저장할 수 있습니다
- 기존 패키지 보다 약 1.48배 더 빠르게 저장되며 약 1.32배 더 빠르게 데이터를 불러옵니다.
- String 1280 클래스 타입으로 비교한 값 입니다.
- 직렬화된 데이터가 더 많을수록, 저장 또는 로드 비중이 높을수록 성능이 더욱 향상됩니다.
- 연속적으로 데이터를 저장 또는 로드할 때 Stream 할당 오버헤드를 제거하였습니다.
- String 10964 기준 약 2.1배, 연속적으로 저장 또는 불러올 수록 성능이 비약적으로 향상.
- 기존 패키지 또는 JsonConverter 형식보다 50% 이상 메모리할당을 방지합니다.
- 기존 IDataStorage 인터페이스를 삭제하고 원형 타입 기본형을 저장합니다.
- Unity 구조체 원형 타입을 지원합니다.
- JsonConverter 내부 로직 및 JsonSerializerSetting 구성이 변경 및 추가되었습니다.
- DataStorage를 사용하지 않아도 저장 및 불러오기에 대한 인터페이스를 지원합니다.
- 내부 기능중 JsonSerialize 직렬화 및 역직렬화 기능을 지원합니다.
- 네이티브 바이너리 형식으로 인해 기존 파일보다 더 적은 용량으로 저장합니다.
호환되는 패키지 목록
어떻게 사용하나요?
지원하는 유니티 직렬화 타입
아래 나열되는 목록은 UnityEngine 라이브러리의 구조체 형식을 지원하는 타입입니다.
별도로 구조체를 생성하지 않고 직렬화가 가능합니다.
- Vector2, Vector2Int
- Vector3, Vector3Int
- Vector4
- Quaternion
- Rect, RectInt
- Color, Color32
직렬화 하는 방법은 PercentJsonConverter.Serialize, Deserialize 함수로 가능하며 코드 구현부는 아래 샘플코드를 참조해주세요.
유니티 플레이어 환경 기능 지원
- 이 패키지는 Project Settings > Editor > EnterPlayer Mode Options 기능을 지원합니다.
- 이 패키지는 Project Settings > Player > Optimization > Strip Engine Code 기능을 지원합니다.
예시
1. 일회성 또는 환경설정 등 자주 저장 또는 불러오지 않는 데이터 관리 방법
DataStorageService 정적 클래스를 사용하여 자주 사용하지 않는 데이터를 저장 또는 불러오는 방법에 대한 샘플 코드입니다.
아래 예시에서는 struct GameEnvironment 이라는 게임 환경에 대한 구조체를 선언하고 저장 및 불러오는 코드를 설명합니다.
using Percent.Scripting.LocalSave;
// 코드 설명을 위한 게임 환경설정입니다.
// 필드 및 프로퍼티 상관없이 저장할 수 있습니다.
// 만약 저장을 원하지 않는 데이터의 경우 [JsonIgnore] 속성을 사용해주세요
public struct GameEnvironment
{
public string UserName;
public float SoundLevel;
public int GraphicQuality;
// ...
}
public void InitializeEnvironment()
{
// 불러오거나 저장할 파일의 위치입니다.
var fileName = $"{Application.persistentDataPath}/MyGameDirectory/MyGameEnvironment.percent";
// 게임 설정값을 불러옵니다.
// 만약 존재하지 않다면 default 또는 null 형식을 반환합니다.
var result = DataStorageService.Load<GameEnvironment>(
fileName: fileName, // 불러올 파일의 주소.
cryptoSettings: CryptoSettings.None); // 암호화 유무. CryptoSettings.None 또는 default 인 경우 암호화를 사용하지 않습니다.
var errCode = result.Error;
var metadata = result.Metadata;
// 불러온 데이터를 검사합니다.
if (errCode == ErrorCode.EmptyValue || // 파일의 값이 비어있는 경우
EqualityComparer<GameEnvironment>.Default.Equals(env, default)) // 불러오는데 성공하였으나 값이 비어있는 경우
{
// 설정하고자 하는 기본 게임 환경설정 값을 입력합니다.
env = new GameEnvironment()
{
UserName = "Percent Guest",
SoundLevel = 1,
GraphicQuality = 3,
// ...
};
// 기본 설정값을 물리디스크에 저장합니다.
DataStorageService.Save(fileName, env, usedEncrypt);
}
}
2. 게임 내 저장 및 불러오기가 빈번히 일어나는 데이터 관리 방법
DataStorageFactory 클래스를 사용하여 DataStorage<>를 만들고 사용하는 방법에 대한 샘플 코드입니다.
- DataStorage 객체가 생성되는 경우 해당 파일의 권한은 생성된 DataStorage 가 관리합니다.
- 파일 입력 및 출력을 외부에서 접근할 수 없습니다. 반드시 생성된 객체에서 Save(), Load() 함수를 사용하여 접근해주세요.
- 외부에서 접근하고자 한다면 생성할 때 FileStream 커스텀 객체를 넣고 적절한 권한을 부여해주세요. (DataStorageFactory.Create(value, fileStream)
아래 예시에서는
- class MyGameUserData 클래스를 선언하고
- DataStorageFactory 클래스를 사용하여 DataStorage
객체를 생성 - DataStorage
객체를 사용하는 방법에 대해 설명합니다.
using Percent.Scripting.LocalSave;
// 코드 설명을 위한 유저 데이터 더미 클래스입니다.
// 필드 및 프로퍼티 상관없이 저장할 수 있습니다.
// 만약 저장을 원하지 않는 데이터의 경우 [JsonIgnore] 속성을 사용해주세요
public class MyGameUserData
{
public Vector3 Position { get; set; }
public int Level;
public int[] ItemCodes;
}
public void SaveUserData()
{
// 스토리지를 생성합니다.
// null 으로 생성하는 경우 데이터가 빈 객체를 생성합니다.
// name 및 directory 매개변수가 빈 경우 아래 값으로 설정합니다.
// - name : typeof(MyGameUserData).Name
// - directory : Application.persistentDataPath}
// ! 이후 데이터 파일 관리를 위해 디렉토리를 직접 설정하는 것을 권장합니다.
var storage = DataStorageFactory.Create<MyGameUserData>(
value: null,
crypto = CryptoSettings.None); // 암호화 사용 유무입니다.
// 암호화 사용 시 권장하는 버전은 V2 입니다. (CryptoSettings.V2)
// 암호화를 사용하지 않는 경우 CryptoSettings.None 또는 default를 사용해주세요.
// 사용자 정의 키값을 사용하려는 경우 CryptoSettings 항목을 확인해주세요.
// 데이터를 불러옵니다.
// 기본적으로 DataStorage.Load 함수는 **메타데이터의 Crc값을 검사합니다.** 불필요한 경우 매개변수에 false를 설정해주세요(checkCrc: false)
var errCode = storage.Load(usedEncrypt);
if (errCode != ErrorCode.Success)
{
// 파일이 삭제되거나 외부에서 변조 또는 훼손되는 경우 불러오기에 실패할 수 있습니다.
// errCode 변수에서 제공하는 값을 확인하여 각 에러별로 개발자가 대처할 수 있도록 합니다.
// ErrorCode.EmptyData
// 파일에 저장된 데이터가 없는 경우압니다.
// ErrorCode.NotFound_Crc
// 파일이 존재하나 메타데이터가 없는 경우입니다.
// 레거시 파일을 불러올 때 발생하며, 내부에 값이 손상되어있지 않다면 정상적으로 값을 불러옵니다. (내부에서 레거시 호환 처리를 진행)
// ErrorCode.NotMatched_Crc
// 파일이 존재하나 손상된 데이터입니다. (외부에서 임의적으로 변조했을 확률이 높습니다)
// ErrorCode.Failed_Load
// 데이터가 형식을 벗어날 만큼 손상되거나 읽을 수 없는 경우입니다.
// 이 경우 메타데이터 값이 있다면(Crc32 != 0) 파일이 외부에서부터 손상되었을 가능성이 매우 높습니다.
// ErrorCode.Unknown
// 알 수 없는 오류입니다.
}
// 데이터가 비어있다면 새 데이터를 삽입합니다.
storage.Value ??= new MyGameUserData();
// 다른 작업을 진행 ..
// 데이터를 저장합니다.
storage.Save(usedEncrypt);
}
3. 서버 통신 등 임시적으로 데이터 형식의 직렬화 데이터가 필요한 경우
PercentJsonConverter 클래스를 사용하여 데이터의 직렬화 및 역직렬화에 대한 샘플 코드입니다.
Caution
참고 : DataStorage 에서 호출하는 Save(), Load() 함수는 base64 형식을 거치지 않으므로 PercentJsonConverter 클래스 내 직렬화 및 역직렬화의 반환값이 다릅니다.
아래 예시에서는 MyGameData 라는 객체를 직렬화 및 역직렬화 하는 방법에 대해 설명합니다.
using Percent.Scripting.Json.Converters;
// 직렬화 할 데이터 객체입니다.
// 형식에 상관없이 가능합니다.
var data = MyGameData(); // 임시 데이터를 가져옵니다.
// 데이터를 직렬화 합니다.
// 암호화가 필요한 경우 CryptoSettings 값을 변경해주세요.
var str = PercentJsonConverter.Serialize(data, CryptoSettings.None);
// 문자열을 역직렬화 합니다.
var value = PercentJsonConverter.Deserialize<MyGameUserData>(str, CryptoSettings.None);
4. 스토리지 할당 해제
DataStorageManager 클래스를 사용하여 DataStorage 할당을 해제합니다.
- 파일을 삭제하지 않습니다.
var storage = // 할당 해제를 원하는 스토리지
var beforeSave = // 해제하기 전 저장할 것인가?
DataStorageManager.Release(storage, beforeSave);
5. 스토리지 파일을 삭제
var storage = // 삭제하려는 스토리지
// 스토리지가 이미 닫혀있다면 (Release) DataStorageManager 클래스로 삭제할 수 없습니다.
// 할당 해제된 스토리지를 제거하려면 수동으로 제거해주세요 (File.Delete)
if (storage.IsClose)
{
// 파일이 삭제된 경우 true 값을 반환합니다.
// 파일이 이미 존재하지 않는 경우에도 동일합니다.
DataStorageManager.Delete(storage);
}
6. 생성된 스토리지를 찾거나 가져오는 방법
DataStorageManager 클래스를 통해 기존에 생성되어있는 스토리지를 가져옵니다.
DataStorage 객체가 생성될 때 자동으로 매니저 객체에 저장되어 스토리지의 상태를 관리할 수 있습니다.
아래 예시에서는
- MyGameUserData 형식을 선언
- DataStorageFactory 에서 DataStorage
객체를 생성 - DataStorageManager 클래스를 사용하여 스토리지를 반환하는 방법에 대해 설명합니다.
using Percent.Scripting.LocalSave;
// 임시 스토리지를 생성합니다.
// 이름을 설정하는 경우, 확장자가 없다면 .percent 확장자가 추가됩니다. (DataStorage.DefaultExtension)
// ex) CustomName.percent
var storage = DataStorageFactory.Create<MyGameUserData>(null, "CustomName");
// 작업을 진행합니다 ..
// 이름으로 스토리지를 찾습니다.
var findStorage = DataStorageManager.GetStorage<MyGameUserData>("CustomName");
// 조건문을 사용하여 스토리지를 찾습니다.
var findPredicateStorage = DataStorageManager.GetStorage(x => x.Name == "CustomName");
// 여러개의 스토리지를 찾습니다.
// 확장자가 .percent 인 모든 스토리지를 찾습니다.
var finds = DataStorageManager.GetStorages(x => x.Extension == ".percent");
암호화 & 복호화
CryptoSettings 구조체를 사용하여 기존처럼 암호화 및 복호화 기능을 사용할 수 있습니다.
- 아래 코드는 기본적으로 제공하는 구조체를 사용하는 예시입니다.
var storage = DataStorageFactory.Create<MyGameUserData>(value, "CustomName", CryptoSettings.Default);
- 아래 코드는 기본적으로 제공하는 키값을 사용하여 최신 버전의 암호화 기능을 사용하는 예시입니다.
Important
AesCryptoService.Version.V2 방식을 사용하는 것을 권장합니다.
var crypto = new CryptoSettings(true, AesCryptoService.PublicKey, AesCryptoService.Version.V2);
var storage = DataStorageFactory.Create<MyGameUserData>(value, "CustomName", crypto);
CRCProvider
직렬화된 CRC 값 구하기
- 값 또는 문자열 등 Crc값을 구할 수 있는 유틸리티 입니다.
- 아래 예시에서는 DataStorage Metadata에서 사용되는 직렬화된 Crc 값을 구합니다.
// 기존 스토리지 이름으로 저장된 crc32값의 문자열을 가져옵니다.
var value = // 무결성 검사를 진행할 값 또는 문자열
// 해당 값을 직렬화한 후 Crc값을 계산합니다.
var crc = CrcProvider.GetCrc32WithSerialize(value);
바이트 또는 스트림 등 객체에 대한 CRC 값 구하기
byte[] value = ...;
var crc = CrcProvider.GetCrc32(value);
Stream stream = ...;
var crc = CrcProvider.GetCrc32WithStream(value);
// Unity Burst 형식으로, 큰 파일인 경우 빠르게 Crc32값을 구하는 방법
fixed (byte* pBytes = value)
{
var crc = CrcProvider.GetCrc32WithBurst(pBytes, value.Length);
}