Commit 68164fb4 authored by nanahira's avatar nanahira

refa structure

parent 19dbe06b
HOST: "::" HOST: "::"
PORT: "7911" PORT: "7911"
REDIS_URL: ""
LOG_LEVEL: info LOG_LEVEL: info
WS_PORT: "0" WS_PORT: "0"
SSL_PATH: "" SSL_PATH: ""
...@@ -9,3 +10,15 @@ TRUSTED_PROXIES: 127.0.0.0/8,::1/128 ...@@ -9,3 +10,15 @@ TRUSTED_PROXIES: 127.0.0.0/8,::1/128
NO_CONNECT_COUNT_LIMIT: "" NO_CONNECT_COUNT_LIMIT: ""
ALT_VERSIONS: "" ALT_VERSIONS: ""
USE_PROXY: "" USE_PROXY: ""
YGOPRO_PATH: ./ygopro
EXTRA_SCRIPT_PATH: ""
HOSTINFO_LFLIST: "0"
HOSTINFO_RULE: "0"
HOSTINFO_MODE: "0"
HOSTINFO_DUEL_RULE: "5"
HOSTINFO_NO_CHECK_DECK: "0"
HOSTINFO_NO_SHUFFLE_DECK: "0"
HOSTINFO_START_LP: "8000"
HOSTINFO_START_HAND: "5"
HOSTINFO_DRAW_COUNT: "1"
HOSTINFO_TIME_LIMIT: "240"
...@@ -11,11 +11,12 @@ ...@@ -11,11 +11,12 @@
"dependencies": { "dependencies": {
"aragami": "^1.2.10", "aragami": "^1.2.10",
"axios": "^1.13.5", "axios": "^1.13.5",
"better-lock": "^3.2.0",
"http-proxy-agent": "^7.0.2", "http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6", "https-proxy-agent": "^7.0.6",
"ipaddr.js": "^2.3.0", "ipaddr.js": "^2.3.0",
"koishipro-core.js": "^1.3.1", "koishipro-core.js": "^1.3.1",
"nfkit": "^1.0.24", "nfkit": "^1.0.27",
"p-queue": "6.6.2", "p-queue": "6.6.2",
"pino": "^10.3.1", "pino": "^10.3.1",
"pino-pretty": "^13.1.3", "pino-pretty": "^13.1.3",
...@@ -2310,6 +2311,12 @@ ...@@ -2310,6 +2311,12 @@
"typed-reflector": "^1.0.12" "typed-reflector": "^1.0.12"
} }
}, },
"node_modules/aragami/node_modules/better-lock": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/better-lock/-/better-lock-2.0.3.tgz",
"integrity": "sha512-3bCaToLrmEXZcIOOVWgi1STvp3/6EpoZAmlWBeuX2MvDB0Ql2ctl/vQ0CbhQIJYQiptdGypllP3ez+TeEmdnKQ==",
"license": "MIT"
},
"node_modules/aragami/node_modules/lru-cache": { "node_modules/aragami/node_modules/lru-cache": {
"version": "7.18.3", "version": "7.18.3",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz",
...@@ -2511,9 +2518,9 @@ ...@@ -2511,9 +2518,9 @@
} }
}, },
"node_modules/better-lock": { "node_modules/better-lock": {
"version": "2.0.3", "version": "3.2.0",
"resolved": "https://registry.npmjs.org/better-lock/-/better-lock-2.0.3.tgz", "resolved": "https://registry.npmjs.org/better-lock/-/better-lock-3.2.0.tgz",
"integrity": "sha512-3bCaToLrmEXZcIOOVWgi1STvp3/6EpoZAmlWBeuX2MvDB0Ql2ctl/vQ0CbhQIJYQiptdGypllP3ez+TeEmdnKQ==", "integrity": "sha512-Wm6j7yKyEjXBCIhjRTVOEScyxOtwFNCuGnQ63bRIwialPQSGcltmMc319t4AWuI8xr/INSfAs7I6x9oN+o2fqA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/brace-expansion": { "node_modules/brace-expansion": {
...@@ -5373,9 +5380,9 @@ ...@@ -5373,9 +5380,9 @@
"license": "MIT" "license": "MIT"
}, },
"node_modules/nfkit": { "node_modules/nfkit": {
"version": "1.0.24", "version": "1.0.27",
"resolved": "https://registry.npmjs.org/nfkit/-/nfkit-1.0.24.tgz", "resolved": "https://registry.npmjs.org/nfkit/-/nfkit-1.0.27.tgz",
"integrity": "sha512-Av73fUWuEU2rF8Lyng/Y8YIDUDHjeDHqInyKsS0Me+hWlW0TvqIhmK/DMpFS7aXxBf+Fp0btecIVoxXYUmAlnQ==", "integrity": "sha512-xgsVsp1aHgrkvxWeTb6Zv55kji9Hq3KcFTZqaIDtQmBBgDwcRtmC2o2/BTQ4ZYjzhaopX+uJCFacfRn39xfL2w==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/node-int64": { "node_modules/node-int64": {
......
...@@ -58,11 +58,12 @@ ...@@ -58,11 +58,12 @@
"dependencies": { "dependencies": {
"aragami": "^1.2.10", "aragami": "^1.2.10",
"axios": "^1.13.5", "axios": "^1.13.5",
"better-lock": "^3.2.0",
"http-proxy-agent": "^7.0.2", "http-proxy-agent": "^7.0.2",
"https-proxy-agent": "^7.0.6", "https-proxy-agent": "^7.0.6",
"ipaddr.js": "^2.3.0", "ipaddr.js": "^2.3.0",
"koishipro-core.js": "^1.3.1", "koishipro-core.js": "^1.3.1",
"nfkit": "^1.0.24", "nfkit": "^1.0.27",
"p-queue": "6.6.2", "p-queue": "6.6.2",
"pino": "^10.3.1", "pino": "^10.3.1",
"pino-pretty": "^13.1.3", "pino-pretty": "^13.1.3",
......
import { createAppContext } from 'nfkit'; import { AppContextState, createAppContext } from 'nfkit';
import { ConfigService } from './services/config'; import { ConfigService } from './services/config';
import { Logger } from './services/logger'; import { Logger } from './services/logger';
import { Emitter } from './services/emitter'; import { Emitter } from './services/emitter';
import { SSLFinder } from './services/ssl-finder';
import { ClientHandler } from './client/client-handler';
import { IpResolver } from './services/ip-resolver';
import { HttpClient } from './services/http-client'; import { HttpClient } from './services/http-client';
import { Chnroute } from './services/chnroute';
import { I18nService } from './services/i18n';
import { YGOProCtosJoinGame } from 'ygopro-msg-encode';
import { TcpServer } from './transport/tcp/server';
import { WsServer } from './transport/ws/server';
import { ClientVersionCheck } from './services/client-version-check';
import { AragamiService } from './services/aragami'; import { AragamiService } from './services/aragami';
import { RoomManager } from './room/room-manager'; import { TransportModule } from './client/transport-module';
import { RoomEventRegister } from './room/room-event-register'; import { JoinHandlerModule } from './join-handlers/join-handler-module';
import { DefaultHostInfoProvider } from './room/default-hostinfo-provder'; import { RoomModule } from './room/room-module';
import { YGOProResourceLoader } from './services/ygopro-resource-loader'; import { SqljsFactory, SqljsLoader } from './services/sqljs';
const core = createAppContext() const core = createAppContext()
.provide(ConfigService, { .provide(ConfigService, {
...@@ -26,31 +17,17 @@ const core = createAppContext() ...@@ -26,31 +17,17 @@ const core = createAppContext()
.provide(Emitter, { merge: ['dispatch', 'middleware', 'removeMiddleware'] }) .provide(Emitter, { merge: ['dispatch', 'middleware', 'removeMiddleware'] })
.provide(HttpClient, { merge: ['http'] }) .provide(HttpClient, { merge: ['http'] })
.provide(AragamiService, { merge: ['aragami'] }) .provide(AragamiService, { merge: ['aragami'] })
.provide(SqljsLoader, {
useFactory: SqljsFactory,
merge: ['SQL'],
})
.define(); .define();
export type Context = typeof core; export type Context = typeof core;
export type ContextState = AppContextState<Context>;
export const app = core export const app = core
.provide(SSLFinder) .use(TransportModule)
.provide(IpResolver) .use(RoomModule)
.provide(Chnroute) .use(JoinHandlerModule)
.provide(I18nService)
.provide(ClientHandler)
.provide(TcpServer)
.provide(WsServer)
.provide(ClientVersionCheck)
.provide(DefaultHostInfoProvider)
.provide(YGOProResourceLoader)
.provide(RoomManager)
.provide(RoomEventRegister)
.define(); .define();
app.middleware(YGOProCtosJoinGame, async (msg, client, _next) => {
await client.sendChat(`Welcome ${client.name_vpass || client.name}!`);
await client.sendChat(`Your IP: ${client.ip}`);
await client.sendChat(`Your physical IP: ${client.physicalIp()}`);
await client.sendChat(`Your pass: ${msg.pass}`);
return client.die(
'This server is for testing purposes only. Please use an official server to play the game.',
);
});
...@@ -6,8 +6,8 @@ import { ...@@ -6,8 +6,8 @@ import {
} from 'ygopro-msg-encode'; } from 'ygopro-msg-encode';
import { Context } from '../app'; import { Context } from '../app';
import { Client } from './client'; import { Client } from './client';
import { IpResolver } from '../services/ip-resolver'; import { IpResolver } from './ip-resolver';
import { WsClient } from '../transport/ws/client'; import { WsClient } from './transport/ws/client';
import { import {
forkJoin, forkJoin,
filter, filter,
......
...@@ -12,10 +12,11 @@ import { ...@@ -12,10 +12,11 @@ import {
YGOProStocHsPlayerEnter, YGOProStocHsPlayerEnter,
YGOProStocHsPlayerChange, YGOProStocHsPlayerChange,
PlayerChangeState, PlayerChangeState,
NetPlayerType,
} from 'ygopro-msg-encode'; } from 'ygopro-msg-encode';
import { YGOProProtoPipe } from '../utility/ygopro-proto-pipe'; import { YGOProProtoPipe } from '../utility/ygopro-proto-pipe';
import { I18nService } from '../services/i18n'; import { I18nService } from './i18n';
import { Chnroute } from '../services/chnroute'; import { Chnroute } from './chnroute';
import YGOProDeck from 'ygopro-deck-encode'; import YGOProDeck from 'ygopro-deck-encode';
import PQueue from 'p-queue'; import PQueue from 'p-queue';
...@@ -93,12 +94,18 @@ export abstract class Client { ...@@ -93,12 +94,18 @@ export abstract class Client {
}); });
} }
async sendChat(msg: string, type = ChatColor.BABYBLUE) { async sendChat(msg: string, type: number = ChatColor.BABYBLUE) {
return this.send( return this.send(
new YGOProStocChat().fromPartial({ new YGOProStocChat().fromPartial({
msg: await this.ctx msg:
.get(() => I18nService) type <= NetPlayerType.OBSERVER
.translate(this.ctx.get(() => Chnroute).getLocale(this.ip), msg), ? msg
: await this.ctx
.get(() => I18nService)
.translate(
this.ctx.get(() => Chnroute).getLocale(this.ip),
msg,
),
player_type: type, player_type: type,
}), }),
); );
......
import { Context } from '../app'; import { Context } from '../app';
import { Client } from '../client/client'; import { Client } from './client';
import * as ipaddr from 'ipaddr.js'; import * as ipaddr from 'ipaddr.js';
import { convertStringArray } from '../utility/convert-string-array'; import { convertStringArray } from '../utility/convert-string-array';
......
import { createAppContext } from 'nfkit';
import { ContextState } from '../app';
import { TcpServer } from './transport/tcp/server';
import { WsServer } from './transport/ws/server';
import { ClientHandler } from './client-handler';
import { Chnroute } from './chnroute';
import { I18nService } from './i18n';
import { IpResolver } from './ip-resolver';
import { SSLFinder } from './ssl-finder';
export const TransportModule = createAppContext<ContextState>()
.provide(SSLFinder)
.provide(IpResolver)
.provide(Chnroute)
.provide(I18nService)
.provide(ClientHandler)
.provide(TcpServer)
.provide(WsServer);
import { Socket } from 'node:net'; import { Socket } from 'node:net';
import { Observable, fromEvent, merge } from 'rxjs'; import { Observable, fromEvent, merge } from 'rxjs';
import { map, take } from 'rxjs/operators'; import { map, take } from 'rxjs/operators';
import { Context } from '../../app'; import { Context } from '../../../app';
import { Client } from '../../client/client'; import { Client } from '../../client';
export class TcpClient extends Client { export class TcpClient extends Client {
constructor( constructor(
......
import { Server as NetServer, Socket, createServer } from 'node:net'; import { Server as NetServer, Socket, createServer } from 'node:net';
import { Context } from '../../app'; import { Context } from '../../../app';
import { ClientHandler } from '../../client/client-handler'; import { ClientHandler } from '../../client-handler';
import { TcpClient } from './client'; import { TcpClient } from './client';
export class TcpServer { export class TcpServer {
......
...@@ -3,8 +3,8 @@ import { Socket } from 'node:net'; ...@@ -3,8 +3,8 @@ import { Socket } from 'node:net';
import { Observable, fromEvent, merge } from 'rxjs'; import { Observable, fromEvent, merge } from 'rxjs';
import { map, take } from 'rxjs/operators'; import { map, take } from 'rxjs/operators';
import WebSocket, { RawData } from 'ws'; import WebSocket, { RawData } from 'ws';
import { Context } from '../../app'; import { Context } from '../../../app';
import { Client } from '../../client/client'; import { Client } from '../../client';
export class WsClient extends Client { export class WsClient extends Client {
constructor( constructor(
......
import { IncomingMessage, createServer as createHttpServer } from 'node:http'; import { IncomingMessage, createServer as createHttpServer } from 'node:http';
import { createServer as createHttpsServer } from 'node:https'; import { createServer as createHttpsServer } from 'node:https';
import { Server as WebSocketServer } from 'ws'; import { Server as WebSocketServer } from 'ws';
import { Context } from '../../app'; import { Context } from '../../../app';
import { ClientHandler } from '../../client/client-handler'; import { ClientHandler } from '../../client-handler';
import { SSLFinder } from '../../services/ssl-finder'; import { SSLFinder } from '../../ssl-finder';
import { WsClient } from './client'; import { WsClient } from './client';
import { WebSocket } from 'ws'; import { WebSocket } from 'ws';
import { IpResolver } from '../../services/ip-resolver'; import { IpResolver } from '../../ip-resolver';
export class WsServer { export class WsServer {
private wss?: WebSocketServer; private wss?: WebSocketServer;
......
...@@ -10,6 +10,7 @@ export type HostinfoOptions = { ...@@ -10,6 +10,7 @@ export type HostinfoOptions = {
export const defaultConfig = { export const defaultConfig = {
HOST: '::', HOST: '::',
PORT: '7911', PORT: '7911',
REDIS_URL: '',
LOG_LEVEL: 'info', LOG_LEVEL: 'info',
WS_PORT: '0', WS_PORT: '0',
SSL_PATH: '', SSL_PATH: '',
......
...@@ -7,6 +7,7 @@ export const TRANSLATIONS = { ...@@ -7,6 +7,7 @@ export const TRANSLATIONS = {
'Your client version is not fully supported. Please rejoin to enable temporary compatibility mode. For the best experience, we recommend updating your game to the latest version.', 'Your client version is not fully supported. Please rejoin to enable temporary compatibility mode. For the best experience, we recommend updating your game to the latest version.',
version_polyfilled: version_polyfilled:
'Temporary compatibility mode has been enabled for your version. We recommend updating your game to avoid potential compatibility issues in the future.', 'Temporary compatibility mode has been enabled for your version. We recommend updating your game to avoid potential compatibility issues in the future.',
blank_room_name: 'Blank room name is unallowed, please fill in something.',
}, },
'zh-CN': { 'zh-CN': {
update_required: '请更新你的客户端版本', update_required: '请更新你的客户端版本',
...@@ -15,5 +16,6 @@ export const TRANSLATIONS = { ...@@ -15,5 +16,6 @@ export const TRANSLATIONS = {
'当前客户端版本暂未完全支持。请重新加入以启用临时兼容模式。为获得更佳体验,建议尽快更新游戏版本。', '当前客户端版本暂未完全支持。请重新加入以启用临时兼容模式。为获得更佳体验,建议尽快更新游戏版本。',
version_polyfilled: version_polyfilled:
'已为当前版本启用临时兼容模式。建议尽快更新游戏,以避免后续兼容性问题。', '已为当前版本启用临时兼容模式。建议尽快更新游戏,以避免后续兼容性问题。',
blank_room_name: '房间名不能为空,请在主机密码处填写房间名',
}, },
}; };
import { YGOProCtosJoinGame } from 'ygopro-msg-encode';
import { Context } from '../app';
export class JoinFallback {
constructor(private ctx: Context) {
this.ctx.middleware(YGOProCtosJoinGame, async (msg, client, next) => {
return client.die('#{blank_room_name}');
});
}
}
import { createAppContext } from 'nfkit';
import { ContextState } from '../app';
import { ClientVersionCheck } from './client-version-check';
import { JoinRoom } from './join-room';
export const JoinHandlerModule = createAppContext<ContextState>()
.provide(ClientVersionCheck)
.provide(JoinRoom)
.define();
import { YGOProCtosJoinGame } from 'ygopro-msg-encode';
import { Context } from '../app';
import { RoomManager } from '../room/room-manager';
export class JoinRoom {
constructor(private ctx: Context) {
this.ctx.middleware(YGOProCtosJoinGame, async (msg, client, next) => {
if (!msg.pass) {
return next();
}
const roomManager = this.ctx.get(() => RoomManager);
const room = await roomManager.findOrCreateByName(msg.pass);
return room.join(client);
});
}
}
import { RoomEvent } from "./room-event"; import { RoomEvent } from './room-event';
export class OnRoomJoin extends RoomEvent { } export class OnRoomJoin extends RoomEvent {}
import { RoomEvent } from "./room-event"; import { RoomEvent } from './room-event';
export class OnRoomLeave extends RoomEvent { } export class OnRoomLeave extends RoomEvent {}
import { Room } from "../room"; import { Room } from '../room';
export class RoomEvent { export class RoomEvent {
constructor(public room: Room) {} constructor(public room: Room) {}
......
import { Context } from '../app'; import { Context } from '../app';
import { Room, RoomFinalizor } from './room'; import { Room, RoomFinalizor } from './room';
import BetterLock from 'better-lock';
export class RoomManager { export class RoomManager {
constructor(private ctx: Context) {} constructor(private ctx: Context) {}
...@@ -24,11 +25,13 @@ export class RoomManager { ...@@ -24,11 +25,13 @@ export class RoomManager {
return Array.from(this.rooms.values()); return Array.from(this.rooms.values());
} }
private roomCreateLock = new BetterLock();
async findOrCreateByName(name: string) { async findOrCreateByName(name: string) {
const existing = this.findByName(name); const existing = this.findByName(name);
if (existing) return existing; if (existing) return existing;
return this.ctx.aragami.lock(`room_create:${name}`, async () => { return this.roomCreateLock.acquire(`room_create:${name}`, async () => {
const existing = this.findByName(name); const existing = this.findByName(name);
if (existing) return existing; if (existing) return existing;
......
import { createAppContext } from 'nfkit';
import { ContextState } from '../app';
import { YGOProResourceLoader } from './ygopro-resource-loader';
import { DefaultHostInfoProvider } from './default-hostinfo-provder';
import { RoomEventRegister } from './room-event-register';
import { RoomManager } from './room-manager';
export const RoomModule = createAppContext<ContextState>()
.provide(DefaultHostInfoProvider)
.provide(YGOProResourceLoader)
.provide(RoomManager)
.provide(RoomEventRegister);
...@@ -16,10 +16,12 @@ import { ...@@ -16,10 +16,12 @@ import {
YGOProStocDeckCount_DeckInfo, YGOProStocDeckCount_DeckInfo,
YGOProStocSelectTp, YGOProStocSelectTp,
YGOProStocSelectHand, YGOProStocSelectHand,
ChatColor,
YGOProCtosChat,
} from 'ygopro-msg-encode'; } from 'ygopro-msg-encode';
import { DefaultHostInfoProvider } from './default-hostinfo-provder'; import { DefaultHostInfoProvider } from './default-hostinfo-provder';
import { CardReaderFinalized } from 'koishipro-core.js'; import { CardReaderFinalized } from 'koishipro-core.js';
import { YGOProResourceLoader } from '../services/ygopro-resource-loader'; import { YGOProResourceLoader } from './ygopro-resource-loader';
import { blankLFList } from '../utility/blank-lflist'; import { blankLFList } from '../utility/blank-lflist';
import { Client } from '../client/client'; import { Client } from '../client/client';
import { RoomMethod } from '../utility/decorators'; import { RoomMethod } from '../utility/decorators';
...@@ -163,12 +165,15 @@ export class Room { ...@@ -163,12 +165,15 @@ export class Room {
return this.playingPlayers.filter((p) => !teammates.has(p)); return this.playingPlayers.filter((p) => !teammates.has(p));
} }
private get teamOffsetBit() {
return this.isTag ? 1 : 0;
}
getDuelPos(client: Client) { getDuelPos(client: Client) {
if (client.pos === NetPlayerType.OBSERVER) { if (client.pos === NetPlayerType.OBSERVER) {
return -1; return -1;
} }
const teamOffsetBit = this.isTag ? 1 : 0; return (client.pos & (0x1 << this.teamOffsetBit)) >>> this.teamOffsetBit;
return (client.pos & (0x1 << teamOffsetBit)) >>> teamOffsetBit;
} }
getPosPlayers(duelPos: number) { getPosPlayers(duelPos: number) {
...@@ -178,6 +183,14 @@ export class Room { ...@@ -178,6 +183,14 @@ export class Room {
return this.playingPlayers.filter((p) => this.getDuelPos(p) === duelPos); return this.playingPlayers.filter((p) => this.getDuelPos(p) === duelPos);
} }
isPosSwapped = false;
getSwappedPos(client: Client) {
if (client.pos === NetPlayerType.OBSERVER || !this.isPosSwapped) {
return client.pos;
}
return client.pos ^ (0x1 << this.teamOffsetBit);
}
async join(client: Client) { async join(client: Client) {
client.roomName = this.name; client.roomName = this.name;
client.isHost = !this.allPlayers.length; client.isHost = !this.allPlayers.length;
...@@ -221,6 +234,7 @@ export class Room { ...@@ -221,6 +234,7 @@ export class Room {
} else { } else {
await this.ctx.dispatch(new OnRoomJoinObserver(this), client); await this.ctx.dispatch(new OnRoomJoinObserver(this), client);
} }
return undefined;
} }
duelStage = DuelStage.Begin; duelStage = DuelStage.Begin;
...@@ -432,6 +446,15 @@ export class Room { ...@@ -432,6 +446,15 @@ export class Room {
this.allPlayers.forEach((p) => p.send(changeMsg)); this.allPlayers.forEach((p) => p.send(changeMsg));
} }
@RoomMethod()
private async onChat(client: Client, msg: YGOProCtosChat) {
return this.sendChat(msg.msg, this.getSwappedPos(client));
}
async sendChat(msg: string, type: number = ChatColor.BABYBLUE) {
return Promise.all(this.allPlayers.map((p) => p.sendChat(msg, type)));
}
duelCount = 0; duelCount = 0;
firstgoPlayer?: Client; firstgoPlayer?: Client;
......
import initSqlJs, { SqlJsStatic } from 'sql.js';
import { Context } from '../app'; import { Context } from '../app';
import { loadPaths } from '../utility/load-path'; import { loadPaths } from '../utility/load-path';
import { DirCardReader, searchYGOProResource } from 'koishipro-core.js'; import { DirCardReader, searchYGOProResource } from 'koishipro-core.js';
import { YGOProLFList, YGOProLFListItem } from 'ygopro-lflist-encode'; import { YGOProLFList } from 'ygopro-lflist-encode';
import path from 'node:path'; import path from 'node:path';
export class YGOProResourceLoader { export class YGOProResourceLoader {
...@@ -14,14 +13,8 @@ export class YGOProResourceLoader { ...@@ -14,14 +13,8 @@ export class YGOProResourceLoader {
]); ]);
extraScriptPaths = loadPaths(this.ctx.getConfig('EXTRA_SCRIPT_PATH')); extraScriptPaths = loadPaths(this.ctx.getConfig('EXTRA_SCRIPT_PATH'));
private SQL!: SqlJsStatic;
async init() {
this.SQL = await initSqlJs();
}
async getCardReader() { async getCardReader() {
return DirCardReader(this.SQL, ...this.ygoproPaths); return DirCardReader(this.ctx.SQL, ...this.ygoproPaths);
} }
async *getLFLists() { async *getLFLists() {
......
import { Aragami } from 'aragami'; import { Aragami } from 'aragami';
import { AppContext } from 'nfkit'; import { AppContext } from 'nfkit';
import { ConfigService } from './config';
export class AragamiService { export class AragamiService {
constructor(private ctx: AppContext) {} constructor(private ctx: AppContext) {}
aragami = new Aragami(); private redisUrl = this.ctx.get(ConfigService).getConfig('REDIS_URL');
aragami = new Aragami({
redis: this.redisUrl ? { uri: this.redisUrl } : undefined,
});
} }
import { AppContext } from 'nfkit';
import type { SqlJsStatic } from 'sql.js';
import initSqlJs from 'sql.js';
export class SqljsLoader {
constructor(private ctx: AppContext) {}
SQL!: SqlJsStatic;
setSqlJs(SQL: SqlJsStatic) {
this.SQL = SQL;
return this;
}
}
export const SqljsFactory = async (ctx: AppContext) => {
const SQL = await initSqlJs();
return new SqljsLoader(ctx).setSqlJs(SQL);
};
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment