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