-
Notifications
You must be signed in to change notification settings - Fork 0
/
DB.js
433 lines (382 loc) · 21.2 KB
/
DB.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
// @ts-check
"use strict";
export class データベース操作 {
static 初期化() {
console.log(セーブデータ.データベースバージョン.取得());
const バージョン = セーブデータ.データベースバージョン.取得() ?? 0;
データベース操作.#バージョン = バージョン;
const リクエスト = indexedDB.open(データベース操作.#データベース名, バージョン);
リクエスト.addEventListener("upgradeneeded", データベース操作.#データベースをアップグレード);
リクエスト.addEventListener("success", データベース操作.#データベースを登録);
}
static 新規プレイヤー登録(内容, 軌跡内容) {
データベース操作.#新規データ群作成(
{ ...データベース操作.#保管庫群.プレイヤー, ...データベース操作.#保管庫群.場所 },
データベース操作.#保管庫名.プレイヤー一覧,
内容,
new Map([[データベース操作.#保管庫群.プレイヤー.プレイヤー軌跡, 軌跡内容]]),
true
);
}
static プレイヤーを保存(内容) {
データベース操作.#データの更新または新規追加(データベース操作.#保管庫名.プレイヤー一覧, 内容);
}
static プレイヤーを読み込む(名前, コールバック) {
console.trace(名前);
const 要求 = データベース操作.#データベース.transaction(データベース操作.#保管庫名.プレイヤー一覧, "readonly")
.objectStore(データベース操作.#保管庫名.プレイヤー一覧)
.get(名前);
要求.addEventListener("success", コールバック);
}
static プレイヤーを削除(名前) {
データベース操作.#データベース.transaction(データベース操作.#保管庫名.プレイヤー一覧, "readwrite")
.objectStore(データベース操作.#保管庫名.プレイヤー一覧)
.delete(名前);
// TODO 保管庫群削除
}
static 場所別キャラクターを更新する(場所名, コールバック) {
データベース操作.#逐次取得と更新(`${場所名}${データベース操作.#保管庫群.場所.キャラクター一覧.接尾辞}`, コールバック);
}
static 場所別ログを読み込む(場所名, コールバック) {
データベース操作.#全取得(`${ 場所名 } ${ データベース操作.#保管庫群.場所.ログ.接尾辞 } `, コールバック);
}
static 場所別キャラクター一覧から削除(場所名, 削除キャラクターの場所別IDのリスト) {
const 対象 = `${ 場所名 } ${ データベース操作.#保管庫群.場所.キャラクター一覧.接尾辞 } `;
const 保管庫 = データベース操作.#データベース.transaction(対象, "readwrite")
.objectStore(対象);
for (const 削除キャラクターの場所別ID of 削除キャラクターの場所別IDのリスト) {
保管庫.delete(削除キャラクターの場所別ID);
}
}
static 場所別キャラクターの登録または更新(場所名, 追加キャラクター, 追加キャラクターの場所別ID) {
const 対象 = `${ 場所名 } ${ データベース操作.#保管庫群.場所.キャラクター一覧.接尾辞 } `;
データベース操作.#データベース.transaction(対象, "readwrite")
.objectStore(対象)
// 追加キャラクターの場所別IDがundefinedなら登録、それ以外なら更新になる
.put(追加キャラクター, 追加キャラクターの場所別ID);
}
static 何でも屋の依頼を読み込む(コールバック) {
データベース操作.#全取得(データベース操作.#保管庫名.何でも屋依頼一覧, コールバック);
}
static 何でも屋の依頼を更新(何でも屋の依頼) {
データベース操作.#データの更新または新規追加(データベース操作.#保管庫名.何でも屋依頼一覧, 何でも屋の依頼);
}
static 習得錬金レシピを保存(錬金レシピ, プレイヤーID) {
データベース操作.#データの更新または新規追加(`${ プレイヤーID } ${ データベース操作.#保管庫群.プレイヤー.錬金レシピ.接尾辞 } `, 錬金レシピ);
}
static アイテム図鑑を読み込む(プレイヤーID, コールバック) {
データベース操作.#全取得(`${ プレイヤーID } ${ データベース操作.#保管庫群.プレイヤー.アイテム図鑑.接尾辞 } `, コールバック);
}
static ジョブマスターを読み込む(プレイヤーID, コールバック) {
データベース操作.#全取得(`${ プレイヤーID } ${ データベース操作.#保管庫群.プレイヤー.ジョブマス.接尾辞 } `, コールバック);
}
static プロフィールを読み込む(プレイヤーID, コールバック) {
データベース操作.#全取得(`${ プレイヤーID } ${ データベース操作.#保管庫群.プレイヤー.プロフィール.接尾辞 } `, コールバック);
}
static プロフィールを更新(プレイヤーID, データリスト) {
const 保管庫名 = `${ プレイヤーID } ${ データベース操作.#保管庫群.プレイヤー.プロフィール.接尾辞 } `;
const 保管庫 = データベース操作.#データベース.transaction(保管庫名, "readwrite")
.objectStore(保管庫名);
for (const データ of データリスト) {
保管庫.put(データ);
}
}
static 新規クエスト登録(内容) {
データベース操作.#新規データ群作成({ ...データベース操作.#保管庫群.場所, ...データベース操作.#保管庫群.クエスト }, データベース操作.#保管庫名.クエスト一覧, 内容);
}
static クエスト保存() {
}
static チャットを書き込んでから読み込む(チャット, 場所名, コールバック) {
データベース操作.#ログを追加(`${ 場所名 } ${ データベース操作.#保管庫群.場所.ログ.接尾辞 } `, チャット, コールバック);
}
static 手紙送信ログを読み込む(プレイヤーID, コールバック) {
データベース操作.#全取得(`${ プレイヤーID } ${ データベース操作.#保管庫群.プレイヤー.手紙送信ログ.接尾辞 } `, コールバック);
}
static 手紙を書き込んでから読み込む(プレイヤーID, 送信先プレイヤー名, チャット, コールバック, プレイヤー非存在時コールバック) {
データベース操作.プレイヤーを読み込む(送信先プレイヤー名, (データベースイベント) => {
if (データベースイベント.target.result === undefined) {
プレイヤー非存在時コールバック();
return;
}
// TODO 連続で手紙を送ることはできません。しばらくしてから送ってください
const 書き込みチャット = [チャット];
データベース操作.#ログを追加(`${ データベースイベント.target.result._ID } ${ データベース操作.#保管庫群.プレイヤー.手紙受信ログ.接尾辞 } `, 書き込みチャット);
データベース操作.#ログを追加(`${ プレイヤーID } ${ データベース操作.#保管庫群.プレイヤー.手紙送信ログ.接尾辞 } `, 書き込みチャット, コールバック);
});
}
static 手紙受信ログを読み込む(プレイヤーID, コールバック) {
データベース操作.#全取得(`${ プレイヤーID } ${ データベース操作.#保管庫群.プレイヤー.手紙受信ログ.接尾辞 } `, コールバック);
}
static 言葉を教える(プレイヤーID, 内容) {
データベース操作.#ログを追加(`${ プレイヤーID } ${ データベース操作.#保管庫群.プレイヤー.ホームはなす.接尾辞 } `, [内容]);
}
static 話す言葉を取得(プレイヤーID, コールバック) {
データベース操作.#全取得(`${ プレイヤーID } ${ データベース操作.#保管庫群.プレイヤー.ホームはなす.接尾辞 } `, コールバック);
}
static アイテムを送る(アイテム名, 送信プレイヤーID, 受信プレイヤーID) {
データベース操作.#倉庫間を移動(アイテム名, データベース操作.#保管庫群.プレイヤー.アイテム倉庫.接尾辞, 送信プレイヤーID, 受信プレイヤーID);
}
static アイテムを入手(アイテム名, プレイヤーID) {
データベース操作.#新規データ追加(`${ プレイヤーID } ${ データベース操作.#保管庫群.プレイヤー.アイテム倉庫.接尾辞 } `, アイテム名);
}
static アイテムを破棄(アイテム名, プレイヤーID) {
データベース操作.倉庫内の最初のキーを取得(アイテム名, プレイヤーID, (データベースイベント) => {
データベースイベント.target.source.objectStore.delete(データベースイベント.target.result);
});
}
static 倉庫内の最初のキーを取得(アイテム名, プレイヤーID, コールバック) {
const 対象 = `${ プレイヤーID } ${ データベース操作.#保管庫群.プレイヤー.アイテム倉庫.接尾辞 } `;
データベース操作.#データベース.transaction(対象, "readwrite")
.objectStore(対象)
.index("名前")
.getKey(アイテム名)
.addEventListener("success", コールバック);
}
static 倉庫内のアイテムを入れ替える(引き出すアイテム名, 預けるアイテム名, プレイヤーID) {
const 対象 = `${ プレイヤーID } ${ データベース操作.#保管庫群.プレイヤー.アイテム倉庫.接尾辞 } `;
データベース操作.#データベース.transaction(対象, "readwrite")
.objectStore(対象)
.index("名前")
.put(預けるアイテム名, 引き出すアイテム名);
}
static モンスターを送る(アイテム名, 送信プレイヤーID, 受信プレイヤーID) {
データベース操作.#倉庫間を移動(アイテム名, データベース操作.#保管庫群.プレイヤー.モンスター倉庫.接尾辞, 送信プレイヤーID, 受信プレイヤーID);
}
static 画像からモンスター倉庫IDを取得() {
}
static プレイヤー軌跡を追加(内容, プレイヤーID) {
データベース操作.#ログを追加(`${ プレイヤーID } ${ データベース操作.#保管庫名.プレイヤー軌跡.接尾辞 } `, 内容);
}
static ニュースを追加(内容) {
データベース操作.#ログを追加(データベース操作.#保管庫名.ニュース, 内容);
}
static エラータイプ = Object.freeze({
既に存在する: "ConstraintError"
});
static #データベースをアップグレード(データベースイベント) {
const データベース = データベースイベント.target.result;
if (データベースイベント.oldVersion < 1) {
データベース.createObjectStore(データベース操作.#保管庫名.プレイヤー一覧, { keyPath: "_名前", autoIncrement: true });
データベース.createObjectStore(データベース操作.#保管庫名.ギルド一覧, { keyPath: "_名前", autoIncrement: true });
const クエスト一覧 = データベース.createObjectStore(データベース操作.#保管庫名.クエスト一覧, { autoIncrement: true });
クエスト一覧.createIndex("名前", "名前", { unique: true });
クエスト一覧.createIndex("最終更新日時", "最終更新日時", { unique: true });
データベース.createObjectStore(データベース操作.#保管庫名.ニュース, { autoIncrement: true });
const 依頼一覧 = データベース.createObjectStore(データベース操作.#保管庫名.何でも屋依頼一覧, { keyPath: "_ID" });
for (let i = 0; i < 何でも屋の依頼数; i += 1) {
依頼一覧.add(何でも屋の依頼.ダミーデータ取得(i));
}
データベース.createObjectStore(データベース操作.#保管庫名.更新連打, { autoIncrement: true });
for (const 場所名 of 場所.全場所名()) {
データベース操作.#新規保管庫作成(データベース, データベース操作.#保管庫群.場所, null, 場所名)
}
}
セーブデータ.データベースバージョン.保存(データベース操作.#バージョン);
}
static #データベースを登録(データベースイベント) {
データベース操作.#データベース = データベースイベント.target.result;
}
static #倉庫間を移動(名前, 倉庫接尾辞, 送信プレイヤーID, 受信プレイヤーID) {
const
送信プレイヤー倉庫 = `${ 送信プレイヤーID } ${ 倉庫接尾辞 } `,
受信プレイヤー倉庫 = `${ 受信プレイヤーID } ${ 倉庫接尾辞 } `,
要求 = データベース操作.#データベース.transaction([送信プレイヤー倉庫, 受信プレイヤー倉庫], "readwrite");
要求.objectStore(送信プレイヤー倉庫)
.remove(データベース操作.#倉庫IDを取得(名前, 倉庫接尾辞, 送信プレイヤーID, 要求));
要求.objectStore(受信プレイヤー倉庫)
.add(アイテム名);
}
static #倉庫IDを取得(名前, 倉庫接尾辞, プレイヤーID, 要求 = データベース操作.#データベース.transaction([`${ プレイヤーID } ${ 倉庫接尾辞 } `], "readonly")) {
要求.objectStore(プレイヤーID)
.index("_名前")
.get(名前);
}
static #新規データ群作成(新規保管庫情報リスト, 保管庫名, 内容, 保管庫デフォルトアイテム, IDを保存する = false) {
データベース操作.#データベース?.close();
const
リクエスト = indexedDB.open(データベース操作.#データベース名, ++データベース操作.#バージョン);
リクエスト.addEventListener("upgradeneeded", () => {
const ID = データベース操作.#新規保管庫作成(リクエスト.result, 新規保管庫情報リスト, 保管庫デフォルトアイテム);
if (IDを保存する)
内容.ID = ID;
});
リクエスト.addEventListener("success", () => {
データベース操作.#新規データ追加(保管庫名, 内容);
});
}
static #新規保管庫作成(データベース, 新規保管庫情報リスト, 保管庫デフォルトアイテム, 名前 = データベース操作.#ID生成()) {
for (const 新規保管庫情報 of Object.values(新規保管庫情報リスト)) {
if (typeof 新規保管庫情報.接尾辞 !== "string") {
throw new Error("接尾辞は文字列にしてください");
}
const 新規保管庫 = データベース.createObjectStore(`${ 名前 } ${ 新規保管庫情報.接尾辞 } `, { autoIncrement: true }),
デフォルトアイテム = 保管庫デフォルトアイテム?.get(新規保管庫情報);
if (デフォルトアイテム)
新規保管庫.add(デフォルトアイテム);
if (!新規保管庫情報.索引リスト)
continue;
for (const 索引 of 新規保管庫情報.索引リスト)
新規保管庫.createIndex(索引.名前 ?? "名前", 索引.名前 ?? 空文字列, 索引.固有 ? { unique: true } : null);
}
セーブデータ.データベースバージョン.保存(データベース操作.#バージョン);
データベース操作.#データベース = データベース;
return 名前;
}
static #新規データ追加(保管庫名, 内容) {
データベース操作.#データベース.transaction(保管庫名, "readwrite")
.objectStore(保管庫名)
.add(内容);
}
static #データの更新または新規追加(保管庫名, 内容) {
データベース操作.#データベース.transaction(保管庫名, "readwrite")
.objectStore(保管庫名)
.put(内容);
}
static #逐次取得と更新(保管庫名, コールバック) {
データベース操作.#データベース.transaction(保管庫名, "readwrite")
.objectStore(保管庫名)
.openCursor()
.addEventListener("success", コールバック);
}
static #全取得(保管庫名, コールバック) {
データベース操作.#データベース.transaction(保管庫名, "readonly")
.objectStore(保管庫名)
.getAll()
.addEventListener("success", コールバック);
}
static #ID生成() {
return btoa(String.fromCharCode(...データベース操作.#数字をUint8Arrayに(データベース操作.#バージョン - 1)));
}
static #数字をUint8Arrayに(数字) {
if (typeof 数字 !== "number" || 数字 < 0) {
throw new TypeError("0以上の数字にしてください");
}
数字 = Math.floor(数字);
const 配列 = [];
let 添え字 = 0;
while (数字 !== 0) {
// new Uint8Array 時に自動的に%256されるので問題ない
配列[添え字++] = 数字;
数字 >>= 8;
}
return new Uint8Array(配列);
}
static #ログを追加(保管庫名, 内容リスト, コールバック) {
const
保管庫 = データベース操作.#データベース.transaction(保管庫名, "readwrite")
.objectStore(保管庫名);
for (const 内容 of 内容リスト) {
保管庫.add(内容);
}
データベース操作.#ログ最大数制限(保管庫, コールバック);
}
static #ログ最大数制限(保管庫, コールバック) {
保管庫.count().addEventListener("success", (イベント) => {
const
要求 = イベント.target,
保管庫 = 要求.source,
超過ログ件数 = イベント.target.result - 最大ログ保存件数;
if (超過ログ件数 <= 0) {
if (コールバック) {
保管庫.getAll().addEventListener("success", コールバック);
}
return;
}
保管庫.openKeyCursor().addEventListener("success", (イベント) => {
保管庫.delete(IDBKeyRange.upperBound(イベント.target.result.key + 超過ログ件数, true));
if (コールバック) {
保管庫.getAll().addEventListener("success", コールバック);
}
});
});
}
static #データベース;
static #バージョン;
static #データベース名 = Object.freeze("@ぼっちぃー");
static #保管庫名 = Object.freeze({
プレイヤー一覧: "プレイヤー一覧",
ログイン中プレイヤー一覧: "ログイン中プレイヤー一覧",
ギルド一覧: "ギルド一覧",
クエスト一覧: "クエスト一覧",
何でも屋依頼一覧: "依頼一覧",
ニュース: "ニュース",
更新連打: "更新連打"
});
static #保管庫群 = Object.freeze({
// 基本的には
// (一度登録したらなくならないもの || ログ) ? 大文字 : 小文字
// base64の後に付加するので3文字までなら大丈夫なはず
// それに気を付ければダブらなければ何でも大丈夫
プレイヤー: Object.freeze({
ホーム通知: Object.freeze({
接尾辞: "a" // Alert
}),
錬金レシピ: Object.freeze({
接尾辞: "A" // Alchemy
}),
戦闘報酬: Object.freeze({
接尾辞: "b" // Battle
}),
アイテム倉庫: Object.freeze({
接尾辞: "i", // Item Depot
索引リスト: [{ 名前: undefined }]
}),
アイテム図鑑: Object.freeze({
接尾辞: "I", // Item Book
索引リスト: [{ 名前: "名前", 固有: true }]
}),
プロフィール: Object.freeze({
接尾辞: "j" // Jikoshokai
}),
ジョブマス: Object.freeze({
接尾辞: "J", // Job Master
索引リスト: [{ 名前: "名前", 固有: true }]
}),
入金: Object.freeze({
接尾辞: "k", // "KANE"
索引リスト: [{ 名前: "名前", 固有: true }]
}),
プレイヤー軌跡: Object.freeze({
接尾辞: "K" // Kiseki
}),
/* 場所.ログ L */
手紙送信ログ: Object.freeze({
接尾辞: "S" // Letter Sending Log
}),
モンスター倉庫: Object.freeze({
接尾辞: "m", // Monster Depot
索引リスト: [{ 名前: "名前" }, { 名前: "画像" }]
}),
モンスターブック: Object.freeze({
接尾辞: "M" // Monster Book
}),
/* 場所.キャラクター一覧 p */
手紙受信ログ: Object.freeze({
接尾辞: "R" // Letter Receiving Log
}),
すくしょ: Object.freeze({
接尾辞: "s"
}),
ホームはなす: Object.freeze({
接尾辞: "t" // Talk
})
}),
クエスト: Object.freeze({
/* 場所.ログ L */
/* 場所.キャラクター一覧 p */
}),
ギルド: Object.freeze({
/* 場所.ログ L */
/* 場所.キャラクター一覧 p */
}),
場所: Object.freeze({
ログ: Object.freeze({
接尾辞: "L" // Log
}),
キャラクター一覧: Object.freeze({
接尾辞: "p", // Player
索引リスト: [{ 名前: "名前", 固有: true }]
})
})
});
}