1+ using System ;
2+ using System . Collections . Generic ;
3+ using System . IO ;
4+ using System . Runtime . CompilerServices ;
5+ using UnityEngine ;
6+
7+ namespace VirtueSky . DataStorage
8+ {
9+ public static class GameData
10+ {
11+ private static bool isInitialized ;
12+ private static int profile ;
13+ private static Dictionary < string , byte [ ] > datas = new ( ) ;
14+ private const int INIT_SIZE = 64 ;
15+
16+ public static event Action OnSaveEvent ;
17+
18+ #region Internal Stuff
19+
20+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
21+ public static void Init ( )
22+ {
23+ if ( isInitialized ) return ;
24+ isInitialized = true ;
25+ Load ( ) ;
26+ }
27+
28+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
29+ private static byte [ ] Serialize < T > ( T data )
30+ {
31+ return SerializeAdapter . ToBinary ( data ) ;
32+ }
33+
34+
35+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
36+ private static T Deserialize < T > ( byte [ ] bytes )
37+ {
38+ return SerializeAdapter . FromBinary < T > ( bytes ) ;
39+ }
40+
41+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
42+ private static void RequireNullCheck ( )
43+ {
44+ if ( datas == null ) Load ( ) ;
45+ if ( datas == null ) throw new NullReferenceException ( ) ;
46+ }
47+
48+ private static string GetPath => GetDataPath ( $ "data_{ profile } .dat") ;
49+
50+ static string GetDataPath ( string name )
51+ {
52+ var persistentDataPath = GetPersistentDataPath ( ) ;
53+ if ( ! Directory . Exists ( persistentDataPath ) )
54+ {
55+ Directory . CreateDirectory ( persistentDataPath ) ;
56+ }
57+
58+ return Path . Combine ( persistentDataPath , name ) ;
59+ }
60+
61+ public static string GetPersistentDataPath ( )
62+ {
63+ #if UNITY_EDITOR
64+ return Path . Combine ( Directory . GetParent ( Application . dataPath ) . FullName , "TempDataStorage" ) ;
65+ #else
66+ return Application . persistentDataPath ;
67+ #endif
68+ }
69+
70+ #endregion
71+
72+ #region Public API
73+
74+ public static bool IsInitialized => isInitialized ;
75+
76+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
77+ public static void ChangeProfile ( int profile )
78+ {
79+ if ( GameData . profile == profile ) return ;
80+
81+ Save ( ) ;
82+ GameData . profile = profile ;
83+ Load ( ) ;
84+ }
85+
86+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
87+ public static bool VerifyProfile ( int profile )
88+ {
89+ return GameData . profile == profile ;
90+ }
91+
92+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
93+ public static void Save ( )
94+ {
95+ OnSaveEvent ? . Invoke ( ) ;
96+
97+ byte [ ] bytes = Serialize ( datas ) ;
98+ File . WriteAllBytes ( GetPath , bytes ) ;
99+ }
100+
101+
102+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
103+ public static async void SaveAsync ( )
104+ {
105+ OnSaveEvent ? . Invoke ( ) ;
106+
107+ byte [ ] bytes = Serialize ( datas ) ;
108+ await File . WriteAllBytesAsync ( GetPath , bytes ) ;
109+ }
110+
111+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
112+ public static void Load ( )
113+ {
114+ if ( ! File . Exists ( GetPath ) )
115+ {
116+ var stream = File . Create ( GetPath ) ;
117+ stream . Close ( ) ;
118+ }
119+
120+ byte [ ] bytes = File . ReadAllBytes ( GetPath ) ;
121+ if ( bytes . Length == 0 )
122+ {
123+ datas . Clear ( ) ;
124+ return ;
125+ }
126+
127+ datas = Deserialize < Dictionary < string , byte [ ] > > ( bytes ) ?? new Dictionary < string , byte [ ] > ( INIT_SIZE ) ;
128+ }
129+
130+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
131+ public static async void LoadAsync ( )
132+ {
133+ if ( ! File . Exists ( GetPath ) )
134+ {
135+ var stream = File . Create ( GetPath ) ;
136+ stream . Close ( ) ;
137+ }
138+
139+ byte [ ] bytes = await File . ReadAllBytesAsync ( GetPath ) ;
140+ if ( bytes . Length == 0 )
141+ {
142+ datas . Clear ( ) ;
143+ return ;
144+ }
145+
146+ datas = Deserialize < Dictionary < string , byte [ ] > > ( bytes ) ?? new Dictionary < string , byte [ ] > ( INIT_SIZE ) ;
147+ }
148+
149+ /// <summary>
150+ ///
151+ /// </summary>
152+ /// <param name="key"></param>
153+ /// <param name="default">If value of <paramref name="key"/> can not be found or empty! will return the default value of data type!</param>
154+ /// <typeparam name="T"></typeparam>
155+ /// <returns></returns>
156+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
157+ public static T Get < T > ( string key , T @default = default )
158+ {
159+ RequireNullCheck ( ) ;
160+
161+ datas . TryGetValue ( key , out byte [ ] value ) ;
162+ if ( value == null || value . Length == 0 ) return @default ;
163+
164+ return Deserialize < T > ( value ) ;
165+ }
166+
167+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
168+ public static bool TryGet < T > ( string key , out T data )
169+ {
170+ RequireNullCheck ( ) ;
171+
172+ bool hasKey ;
173+ if ( datas . TryGetValue ( key , out byte [ ] value ) )
174+ {
175+ data = Deserialize < T > ( value ) ;
176+ hasKey = true ;
177+ }
178+ else
179+ {
180+ data = default ;
181+ hasKey = false ;
182+ }
183+
184+ return hasKey ;
185+ }
186+
187+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
188+ public static void Set < T > ( string key , T data )
189+ {
190+ RequireNullCheck ( ) ;
191+ byte [ ] bytes = Serialize ( data ) ;
192+ if ( datas . TryAdd ( key , bytes ) ) return ;
193+ datas [ key ] = bytes ;
194+ }
195+
196+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
197+ public static bool HasKey ( string key ) => datas . ContainsKey ( key ) ;
198+
199+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
200+ public static void DeleteKey ( string key ) => datas . Remove ( key ) ;
201+
202+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
203+ public static void DeleteAll ( ) => datas . Clear ( ) ;
204+
205+ public static void DeleteFileData ( )
206+ {
207+ if ( File . Exists ( GetPath ) )
208+ {
209+ File . Delete ( GetPath ) ;
210+ }
211+ }
212+
213+ /// <summary>
214+ /// Get raw byte[] of all data of profile
215+ /// </summary>
216+ /// <returns></returns>
217+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
218+ public static byte [ ] Backup ( )
219+ {
220+ return SerializeAdapter . ToBinary ( datas ) ;
221+ }
222+
223+ /// <summary>
224+ /// Load from byte[]
225+ /// </summary>
226+ /// <param name="bytes"></param>
227+ [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
228+ public static void Restore ( byte [ ] bytes )
229+ {
230+ datas = SerializeAdapter . FromBinary < Dictionary < string , byte [ ] > > ( bytes ) ;
231+ }
232+
233+ #endregion
234+ }
235+ }
0 commit comments