Commit 6ed0ff83 authored by Chunchi Che's avatar Chunchi Che

Merge branch 'feat/408_room_only' into 'main'

Feat/408 room only

See merge request !411
parents a7d6c103 2017af9b
Pipeline #30314 passed with stages
in 10 minutes and 4 seconds
{ {
"version": 4961, "version": 4961,
"servers": [{ "servers": [
"name": "koishi", {
"ip": "koishi.momobako.com", "name": "koishi",
"port": "7211" "ip": "koishi.momobako.com",
}, "port": "7211"
{ },
"name": "mycard-athletic", {
"ip": "tiramisu.moecube.com", "name": "mycard-athletic",
"port": "8912" "ip": "tiramisu.moecube.com",
}, "port": "8912"
{ },
"name": "mycard-custom", {
"ip": "tiramisu.moecube.com", "name": "mycard-custom",
"port": "7912" "ip": "tiramisu.moecube.com",
}, "port": "7912"
{ },
"name": "pre-release", {
"ip": "koishi.momobako.com", "name": "pre-release",
"port": "889" "ip": "koishi.momobako.com",
} "port": "889"
], },
"assetsPath": "/neos-assets", {
"releaseImgUrl": "https://cdn02.moecube.com:444/images/ygopro-images-zh-CN", "name": "408",
"preReleaseImgUrl": "https://cdn02.moecube.com:444/ygopro-super-pre/data/pics", "ip": "koishi.momobako.com",
"releaseDbUrl": "https://cdn02.moecube.com:444/ygopro-database/zh-CN/cards.cdb", "port": "1408"
"preReleaseDbUrl": "https://cdn02.moecube.com:444/ygopro-super-pre/data/test-release.cdb", }
"preReleaseConfig": "https://cdn02.moecube.com:444/ygopro-super-pre/data/test-release-v2.json", ],
"stringsUrl": "https://cdn02.moecube.com:444/ygopro-database/zh-CN/strings.conf", "assetsPath": "/neos-assets",
"lflistUrl": "https://cdn02.moecube.com:444/ygopro-database/zh-CN/lflist.conf", "releaseResource": {
"replayUrl": "replay.neos.moe", "img": "https://cdn02.moecube.com:444/images/ygopro-images-zh-CN",
"loginUrl": "https://accounts.moecube.com/signin", "cdb": "https://cdn02.moecube.com:444/ygopro-database/zh-CN/cards.cdb",
"logoutUrl": "https://accounts.moecube.com/signout", "lflist": "https://cdn02.moecube.com:444/ygopro-database/zh-CN/lflist.conf"
"profileUrl": "https://accounts.moecube.com/profiles", },
"athleticWatchUrl": "wss://tiramisu.moecube.com:8923", "preReleaseResource": {
"entertainWatchUrl": "wss://tiramisu.moecube.com:7923", "img": "https://cdn02.moecube.com:444/ygopro-super-pre/data/pics",
"userApi": "https://sapi.moecube.com:444/accounts/users/{username}.json", "cdb": "https://cdn02.moecube.com:444/ygopro-super-pre/data/test-release.cdb",
"lflist": "https://cdn02.moecube.com:444/ygopro-database/zh-CN/lflist.conf",
"config": "https://cdn02.moecube.com:444/ygopro-super-pre/data/test-release-v2.json"
},
"env408Resource": {
"lflist": "https://cdn02.moecube.com:444/cn-database/env408-zh-CN/expansions/lflist.conf"
},
"stringsUrl": "https://cdn02.moecube.com:444/ygopro-database/zh-CN/strings.conf",
"replayUrl": "replay.neos.moe",
"loginUrl": "https://accounts.moecube.com/signin",
"logoutUrl": "https://accounts.moecube.com/signout",
"profileUrl": "https://accounts.moecube.com/profiles",
"athleticWatchUrl": "wss://tiramisu.moecube.com:8923",
"entertainWatchUrl": "wss://tiramisu.moecube.com:7923",
"userApi": "https://sapi.moecube.com:444/accounts/users/{username}.json",
"mdproServer": "https://rarnu.xyz:38443", "mdproServer": "https://rarnu.xyz:38443",
"agentServer": "https://sapi.moecube.com:444/neos-ai-agent", "agentServer": "https://sapi.moecube.com:444/neos-ai-agent",
"streamInterval": 20, "startDelay": 1000,
"startDelay": 1000, "ui": {
"ui": { "hint": {
"hint": { "maxCount": 1
"maxCount": 1 }
} }
},
"unimplementedWhiteList": [
1,
6,
7,
34,
54,
55,
56,
60,
61,
62,
63,
64,
65,
70,
71,
72,
73,
74,
75,
76,
80,
81,
83,
93,
95,
96,
97,
101,
102,
110,
111,
112,
113,
114,
120,
121,
122,
123,
130,
131,
132,
133,
160,
161,
163,
164,
165,
170,
180,
230,
231,
236
]
} }
{ {
"version": 4961, "version": 4961,
"servers": [{ "servers": [
"name": "koishi", {
"ip": "koishi.momobako.com", "name": "koishi",
"port": "7211" "ip": "koishi.momobako.com",
}, "port": "7211"
{ },
"name": "mycard-athletic", {
"ip": "tiramisu.moecube.com", "name": "mycard-athletic",
"port": "8912" "ip": "tiramisu.moecube.com",
}, "port": "8912"
{ },
"name": "mycard-custom", {
"ip": "tiramisu.moecube.com", "name": "mycard-custom",
"port": "7912" "ip": "tiramisu.moecube.com",
}, "port": "7912"
{ },
"name": "pre-release", {
"ip": "koishi.momobako.com", "name": "pre-release",
"port": "889" "ip": "koishi.momobako.com",
} "port": "889"
], },
"assetsPath": "/neos-assets", {
"releaseImgUrl": "https://cdn02.moecube.com:444/images/ygopro-images-zh-CN", "name": "408",
"preReleaseImgUrl": "https://cdn02.moecube.com:444/ygopro-super-pre/data/pics", "ip": "koishi.momobako.com",
"releaseDbUrl": "https://cdn02.moecube.com:444/ygopro-database/zh-CN/cards.cdb", "port": "1408"
"preReleaseDbUrl": "https://cdn02.moecube.com:444/ygopro-super-pre/data/test-release.cdb", }
"preReleaseConfig": "https://cdn02.moecube.com:444/ygopro-super-pre/data/test-release-v2.json", ],
"stringsUrl": "https://cdn02.moecube.com:444/ygopro-database/zh-CN/strings.conf", "assetsPath": "/neos-assets",
"lflistUrl": "https://cdn02.moecube.com:444/ygopro-database/zh-CN/lflist.conf", "releaseResource": {
"replayUrl": "replay.neos.moe", "img": "https://cdn02.moecube.com:444/images/ygopro-images-zh-CN",
"loginUrl": "https://accounts.moecube.com/signin", "cdb": "https://cdn02.moecube.com:444/ygopro-database/zh-CN/cards.cdb",
"logoutUrl": "https://accounts.moecube.com/signout", "lflist": "https://cdn02.moecube.com:444/ygopro-database/zh-CN/lflist.conf"
"profileUrl": "https://accounts.moecube.com/profiles", },
"athleticWatchUrl": "wss://tiramisu.moecube.com:8923", "preReleaseResource": {
"entertainWatchUrl": "wss://tiramisu.moecube.com:7923", "img": "https://cdn02.moecube.com:444/ygopro-super-pre/data/pics",
"userApi": "https://sapi.moecube.com:444/accounts/users/{username}.json", "cdb": "https://cdn02.moecube.com:444/ygopro-super-pre/data/test-release.cdb",
"lflist": "https://cdn02.moecube.com:444/ygopro-database/zh-CN/lflist.conf",
"config": "https://cdn02.moecube.com:444/ygopro-super-pre/data/test-release-v2.json"
},
"env408Resource": {
"lflist": "https://cdn02.moecube.com:444/cn-database/env408-zh-CN/expansions/lflist.conf"
},
"stringsUrl": "https://cdn02.moecube.com:444/ygopro-database/zh-CN/strings.conf",
"replayUrl": "replay.neos.moe",
"loginUrl": "https://accounts.moecube.com/signin",
"logoutUrl": "https://accounts.moecube.com/signout",
"profileUrl": "https://accounts.moecube.com/profiles",
"athleticWatchUrl": "wss://tiramisu.moecube.com:8923",
"entertainWatchUrl": "wss://tiramisu.moecube.com:7923",
"userApi": "https://sapi.moecube.com:444/accounts/users/{username}.json",
"mdproServer": "https://rarnu.xyz:38443", "mdproServer": "https://rarnu.xyz:38443",
"agentServer": "https://sapi.moecube.com:444/neos-ai-agent", "agentServer": "https://sapi.moecube.com:444/neos-ai-agent",
"streamInterval": 20, "startDelay": 1000,
"startDelay": 1000, "ui": {
"ui": { "hint": {
"hint": { "maxCount": 1
"maxCount": 1 }
} }
},
"unimplementedWhiteList": [
1,
6,
7,
34,
54,
55,
56,
60,
61,
62,
63,
64,
65,
70,
71,
72,
73,
74,
75,
76,
80,
81,
83,
93,
95,
96,
97,
101,
102,
110,
111,
112,
113,
114,
120,
121,
122,
123,
130,
131,
132,
133,
160,
161,
163,
164,
165,
170,
180,
230,
231,
236
]
} }
...@@ -4,7 +4,7 @@ import { FtsParams } from "@/middleware/sqlite/fts"; ...@@ -4,7 +4,7 @@ import { FtsParams } from "@/middleware/sqlite/fts";
import { isSuperReleaseCard } from "./superPreRelease"; import { isSuperReleaseCard } from "./superPreRelease";
const NeosConfig = useConfig(); const { assetsPath, releaseResource, preReleaseResource } = useConfig();
export interface CardMeta { export interface CardMeta {
id: number; id: number;
...@@ -80,33 +80,31 @@ export function getCardStr(meta: CardMeta, idx: number): string | undefined { ...@@ -80,33 +80,31 @@ export function getCardStr(meta: CardMeta, idx: number): string | undefined {
export function getCardImgUrl(code: number, back = false) { export function getCardImgUrl(code: number, back = false) {
const ASSETS_BASE = const ASSETS_BASE =
import.meta.env.BASE_URL === "/" import.meta.env.BASE_URL === "/"
? NeosConfig.assetsPath ? assetsPath
: `${import.meta.env.BASE_URL}${NeosConfig.assetsPath}`; : `${import.meta.env.BASE_URL}${assetsPath}`;
if (back || code === 0) { if (back || code === 0) {
return `${ASSETS_BASE}/card_back.jpg`; return `${ASSETS_BASE}/card_back.jpg`;
} } else if (isSuperReleaseCard(code)) {
return `${preReleaseResource.img}/${code}.jpg`;
} else {
// Define translations for different languages (I18N)
const language = localStorage.getItem("language");
let imgUrl;
// Define translations for different languages (I18N) switch (language) {
const language = localStorage.getItem("language"); case "en":
let imgUrl; case "br":
case "pt":
case "fr":
case "es":
imgUrl = releaseResource.img.replace("zh-CN", "en-US");
break;
default:
imgUrl = releaseResource.img;
break;
}
/* End of definition (I18N) */
switch (language) {
case "en":
case "br":
case "pt":
case "fr":
case "es":
imgUrl = NeosConfig.releaseImgUrl.replace("zh-CN", "en-US");
break;
default:
imgUrl = NeosConfig.releaseImgUrl;
break;
}
/* End of definition (I18N) */
if (isSuperReleaseCard(code)) {
return `${NeosConfig.preReleaseImgUrl}/${code}.jpg`;
} else {
return `${imgUrl}/${code}.jpg`; return `${imgUrl}/${code}.jpg`;
} }
} }
import { isNil } from "lodash-es"; import { isNil } from "lodash-es";
import { useConfig } from "@/config";
import { CardMeta } from "./cards"; import { CardMeta } from "./cards";
const { lflistUrl } = useConfig();
class Forbidden { class Forbidden {
private data: Map<number, number> = new Map<number, number>(); private data: Map<number, number> = new Map<number, number>();
public time: string = "?"; public time: string = "?";
public async init(): Promise<void> { public async init(lflist: string): Promise<void> {
const text = await (await fetch(lflistUrl)).text(); const text = await (await fetch(lflist)).text();
const { time, forbiddens } = this.extractForbiddensFromText(text); const { time, forbiddens } = this.extractForbiddensFromText(text);
this.time = time; this.time = time;
this.setForbiddens(forbiddens); this.setForbiddens(forbiddens);
...@@ -43,7 +40,7 @@ class Forbidden { ...@@ -43,7 +40,7 @@ class Forbidden {
function parseCardInfo( function parseCardInfo(
input: string, input: string,
): { cardId: number; limitCount: number } | null { ): { cardId: number; limitCount: number } | null {
const match = input.match(/^(\d+)\s+(\d+)\s+--/); const match = input.match(/^(\d+)\s+(\d+)/);
if (match) { if (match) {
const cardId = parseInt(match[1]); const cardId = parseInt(match[1]);
const limitCount = parseInt(match[2]); const limitCount = parseInt(match[2]);
...@@ -85,3 +82,4 @@ class Forbidden { ...@@ -85,3 +82,4 @@ class Forbidden {
} }
export const forbidden = new Forbidden(); export const forbidden = new Forbidden();
export const forbidden_408 = new Forbidden();
import { useConfig } from "@/config"; import { useConfig } from "@/config";
const { preReleaseConfig } = useConfig(); const {
preReleaseResource: { config },
} = useConfig();
interface SuperPreInfo { interface SuperPreInfo {
/* only use id currently, other fields see: /* only use id currently, other fields see:
...@@ -12,7 +14,7 @@ interface SuperPreInfo { ...@@ -12,7 +14,7 @@ interface SuperPreInfo {
let superPreList: SuperPreInfo[] = []; let superPreList: SuperPreInfo[] = [];
export async function initSuperPrerelease() { export async function initSuperPrerelease() {
const json = await (await fetch(preReleaseConfig)).text(); const json = await (await fetch(config)).text();
const list: SuperPreInfo[] = JSON.parse(json); const list: SuperPreInfo[] = JSON.parse(json);
superPreList = list; superPreList = list;
} }
......
...@@ -8,10 +8,13 @@ export class WebSocketStream { ...@@ -8,10 +8,13 @@ export class WebSocketStream {
public ws: WebSocket; public ws: WebSocket;
stream: ReadableStream; stream: ReadableStream;
constructor(ip: string, onWsOpen?: (ws: WebSocket, ev: Event) => any) { constructor(
ip: string,
onWsOpen?: (conn: WebSocketStream, ev: Event) => any,
) {
this.ws = new WebSocket("wss://" + ip); this.ws = new WebSocket("wss://" + ip);
if (onWsOpen) { if (onWsOpen) {
this.ws.onopen = (e) => onWsOpen(this.ws, e); this.ws.onopen = (e) => onWsOpen(this, e);
} }
this.ws.onerror = (e) => { this.ws.onerror = (e) => {
if (e instanceof ErrorEvent) { if (e instanceof ErrorEvent) {
...@@ -69,7 +72,6 @@ export class WebSocketStream { ...@@ -69,7 +72,6 @@ export class WebSocketStream {
// but now it seems that we don't need wait any more, // but now it seems that we don't need wait any more,
// so comment the following line and check if it's ok without it. // so comment the following line and check if it's ok without it.
// //
// await sleep(useConfig().streamInterval);
await onMessage(value); await onMessage(value);
} else { } else {
console.warn("value from ReadableStream is undefined!"); console.warn("value from ReadableStream is undefined!");
......
...@@ -13,11 +13,13 @@ export function initSocket(initInfo: { ...@@ -13,11 +13,13 @@ export function initSocket(initInfo: {
ip: string; ip: string;
player: string; player: string;
passWd: string; passWd: string;
customOnConnected?: (conn: WebSocketStream) => void;
}): WebSocketStream { }): WebSocketStream {
const { ip, player, passWd } = initInfo; const { ip, player, passWd, customOnConnected } = initInfo;
return new WebSocketStream(ip, (conn, _event) => return new WebSocketStream(ip, (conn, _event) => {
handleSocketOpen(conn, ip, player, passWd), handleSocketOpen(conn, ip, player, passWd);
); customOnConnected && customOnConnected(conn);
});
} }
export function initReplaySocket(replayInfo: { export function initReplaySocket(replayInfo: {
...@@ -27,8 +29,8 @@ export function initReplaySocket(replayInfo: { ...@@ -27,8 +29,8 @@ export function initReplaySocket(replayInfo: {
const { url, data } = replayInfo; const { url, data } = replayInfo;
return new WebSocketStream(url, (conn, _event) => { return new WebSocketStream(url, (conn, _event) => {
console.info("replay websocket open."); console.info("replay websocket open.");
conn.binaryType = "arraybuffer"; conn.ws.binaryType = "arraybuffer";
conn.send(data); conn.ws.send(data);
}); });
} }
......
import { ygopro } from "@/api"; import { ygopro } from "@/api";
import { useConfig } from "@/config";
import { Container } from "@/container"; import { Container } from "@/container";
const NeosConfig = useConfig(); const UNIMPLEMENTED_WHITE_LIST = [
1, 6, 7, 34, 54, 55, 56, 60, 61, 62, 63, 64, 65, 70, 71, 72, 73, 74, 75, 76,
80, 81, 83, 93, 95, 96, 97, 101, 102, 110, 111, 112, 113, 114, 120, 121, 122,
123, 130, 131, 132, 133, 160, 161, 163, 164, 165, 170, 180, 230, 231, 236,
];
export default ( export default (
container: Container, container: Container,
unimplemented: ygopro.StocGameMessage.MsgUnimplemented, unimplemented: ygopro.StocGameMessage.MsgUnimplemented,
) => { ) => {
if (!NeosConfig.unimplementedWhiteList.includes(unimplemented.command)) { if (!UNIMPLEMENTED_WHITE_LIST.includes(unimplemented.command)) {
container.context.matStore.unimplemented = unimplemented.command; container.context.matStore.unimplemented = unimplemented.command;
} }
}; };
...@@ -4,6 +4,7 @@ ...@@ -4,6 +4,7 @@
* */ * */
import { sendJoinGame, sendPlayerInfo } from "@/api"; import { sendJoinGame, sendPlayerInfo } from "@/api";
import { useConfig } from "@/config"; import { useConfig } from "@/config";
import { WebSocketStream } from "@/infra";
const NeosConfig = useConfig(); const NeosConfig = useConfig();
/* /*
...@@ -12,17 +13,17 @@ const NeosConfig = useConfig(); ...@@ -12,17 +13,17 @@ const NeosConfig = useConfig();
* *
* */ * */
export default function handleSocketOpen( export default function handleSocketOpen(
ws: WebSocket | undefined, conn: WebSocketStream | undefined,
_ip: string, _ip: string,
player: string, player: string,
passWd: string, passWd: string,
) { ) {
console.log("WebSocket opened."); console.log("WebSocket opened.");
if (ws && ws.readyState === 1) { if (conn && conn.ws.readyState === 1) {
ws.binaryType = "arraybuffer"; conn.ws.binaryType = "arraybuffer";
sendPlayerInfo(ws, player); sendPlayerInfo(conn.ws, player);
sendJoinGame(ws, NeosConfig.version, passWd); sendJoinGame(conn.ws, NeosConfig.version, passWd);
} }
} }
...@@ -26,6 +26,7 @@ import { ...@@ -26,6 +26,7 @@ import {
DeckZone, DeckZone,
Loading, Loading,
ScrollableArea, ScrollableArea,
Select,
} from "@/ui/Shared"; } from "@/ui/Shared";
import { Type } from "@/ui/Shared/DeckZone"; import { Type } from "@/ui/Shared/DeckZone";
...@@ -41,6 +42,9 @@ import { ...@@ -41,6 +42,9 @@ import {
iDeckToEditingDeck, iDeckToEditingDeck,
} from "./utils"; } from "./utils";
const ENV_OCG = 0;
const ENV_408 = 1;
export const loader: LoaderFunction = async () => { export const loader: LoaderFunction = async () => {
// 必须先加载卡组,不然页面会崩溃 // 必须先加载卡组,不然页面会崩溃
if (!initStore.decks) { if (!initStore.decks) {
...@@ -182,6 +186,9 @@ export const DeckEditor: React.FC<{ ...@@ -182,6 +186,9 @@ export const DeckEditor: React.FC<{
}> = ({ deck, onClear, onReset, onSave, onShuffle, onSort }) => { }> = ({ deck, onClear, onReset, onSave, onShuffle, onSort }) => {
const snapEditDeck = useSnapshot(editDeckStore); const snapEditDeck = useSnapshot(editDeckStore);
const [deckName, setDeckName] = useState(editDeckStore.deckName); const [deckName, setDeckName] = useState(editDeckStore.deckName);
const [env, setEnv] = useState(ENV_OCG);
const handleEnvChange = (value: any) => setEnv(value);
useEffect(() => { useEffect(() => {
iDeckToEditingDeck(deck).then(editDeckStore.set); iDeckToEditingDeck(deck).then(editDeckStore.set);
...@@ -249,6 +256,21 @@ export const DeckEditor: React.FC<{ ...@@ -249,6 +256,21 @@ export const DeckEditor: React.FC<{
value={deckName} value={deckName}
/> />
<Space style={{ marginRight: "0.4rem" }} size={5}> <Space style={{ marginRight: "0.4rem" }} size={5}>
<Select
title={i18n("Environment")}
value={env}
options={[
{
value: ENV_OCG,
label: "OCG",
},
{
value: ENV_408,
label: "408",
},
]}
onChange={handleEnvChange}
/>
<Button <Button
type="text" type="text"
size="small" size="small"
...@@ -313,6 +335,7 @@ export const DeckEditor: React.FC<{ ...@@ -313,6 +335,7 @@ export const DeckEditor: React.FC<{
editDeckStore.add(type, card); editDeckStore.add(type, card);
} }
}} }}
is408={env === ENV_408}
/> />
))} ))}
</ScrollableArea> </ScrollableArea>
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
}, },
"BuildDeck": { "BuildDeck": {
"EnterTheDeckName": "请输入卡组名字", "EnterTheDeckName": "请输入卡组名字",
"Environment": "环境",
"Shuffle": "打乱", "Shuffle": "打乱",
"Sort": "排序", "Sort": "排序",
"Clear": "清空", "Clear": "清空",
...@@ -165,6 +166,7 @@ ...@@ -165,6 +166,7 @@
"Server": "服务器", "Server": "服务器",
"KoishiServer": "Koishi服", "KoishiServer": "Koishi服",
"UltraPreemptiveServer": "超先行服", "UltraPreemptiveServer": "超先行服",
"408": "408环境",
"PlayerNickname": "玩家昵称", "PlayerNickname": "玩家昵称",
"RoomPasswordOptional": "房间密码(可选)", "RoomPasswordOptional": "房间密码(可选)",
"JoinRoom": "加入房间", "JoinRoom": "加入房间",
......
...@@ -42,6 +42,7 @@ ...@@ -42,6 +42,7 @@
}, },
"BuildDeck": { "BuildDeck": {
"EnterTheDeckName": "Deck name", "EnterTheDeckName": "Deck name",
"Environment": "Environment",
"Shuffle": "Shuffle", "Shuffle": "Shuffle",
"Sort": "Sort", "Sort": "Sort",
"Clear": "Clear", "Clear": "Clear",
...@@ -165,6 +166,7 @@ ...@@ -165,6 +166,7 @@
"Server": "Server", "Server": "Server",
"KoishiServer": "Koishi Server", "KoishiServer": "Koishi Server",
"UltraPreemptiveServer": "Ultra Preemptive Server", "UltraPreemptiveServer": "Ultra Preemptive Server",
"408": "408 Environment",
"PlayerNickname": "Player Nickname", "PlayerNickname": "Player Nickname",
"RoomPasswordOptional": "Room Password (optional)", "RoomPasswordOptional": "Room Password (optional)",
"JoinRoom": "Join the Room" "JoinRoom": "Join the Room"
......
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
}, },
"BuildDeck": { "BuildDeck": {
"EnterTheDeckName": "デッキ名を入力", "EnterTheDeckName": "デッキ名を入力",
"Environment": "環境",
"Shuffle": "シャッフル", "Shuffle": "シャッフル",
"Sort": "ソート", "Sort": "ソート",
"Clear": "クリア", "Clear": "クリア",
...@@ -163,6 +164,7 @@ ...@@ -163,6 +164,7 @@
"Server": "サーバー", "Server": "サーバー",
"KoishiServer": "コイシサーバー", "KoishiServer": "コイシサーバー",
"UltraPreemptiveServer": "超先行サーバー", "UltraPreemptiveServer": "超先行サーバー",
"408": "408環境",
"PlayerNickname": "プレイヤーニックネーム", "PlayerNickname": "プレイヤーニックネーム",
"RoomPasswordOptional": "ルームパスワード(任意)", "RoomPasswordOptional": "ルームパスワード(任意)",
"JoinRoom": "ルームに参加" "JoinRoom": "ルームに参加"
......
...@@ -3,6 +3,7 @@ import rustInit from "rust-src"; ...@@ -3,6 +3,7 @@ import rustInit from "rust-src";
import { import {
CookieKeys, CookieKeys,
forbidden, forbidden,
forbidden_408,
getCookie, getCookie,
initStrings, initStrings,
initSuperPrerelease, initSuperPrerelease,
...@@ -12,7 +13,7 @@ import { useConfig } from "@/config"; ...@@ -12,7 +13,7 @@ import { useConfig } from "@/config";
import { useEnv } from "@/hook"; import { useEnv } from "@/hook";
import sqliteMiddleWare, { sqliteCmd } from "@/middleware/sqlite"; import sqliteMiddleWare, { sqliteCmd } from "@/middleware/sqlite";
import { accountStore, deckStore, initStore, type User } from "@/stores"; import { accountStore, deckStore, initStore, type User } from "@/stores";
const { releaseDbUrl, preReleaseDbUrl } = useConfig(); const { releaseResource, preReleaseResource, env408Resource } = useConfig();
const { BASE_URL } = useEnv(); const { BASE_URL } = useEnv();
/** 加载ygodb */ /** 加载ygodb */
...@@ -24,7 +25,11 @@ export const initSqlite = async () => { ...@@ -24,7 +25,11 @@ export const initSqlite = async () => {
sqlite.progress = 0.01; sqlite.progress = 0.01;
await sqliteMiddleWare({ await sqliteMiddleWare({
cmd: sqliteCmd.INIT, cmd: sqliteCmd.INIT,
initInfo: { releaseDbUrl, preReleaseDbUrl, progressCallback }, initInfo: {
releaseDbUrl: releaseResource.cdb,
preReleaseDbUrl: preReleaseResource.cdb,
progressCallback,
},
}); });
sqlite.progress = 1; sqlite.progress = 1;
} }
...@@ -51,7 +56,8 @@ export const initWASM = async () => { ...@@ -51,7 +56,8 @@ export const initWASM = async () => {
/** 加载禁限卡表 */ /** 加载禁限卡表 */
export const initForbidden = async () => { export const initForbidden = async () => {
if (!initStore.forbidden) { if (!initStore.forbidden) {
await forbidden.init(); await forbidden.init(releaseResource.lflist);
await forbidden_408.init(env408Resource.lflist);
initStore.forbidden = true; initStore.forbidden = true;
} }
}; };
......
...@@ -4,7 +4,9 @@ import { useTranslation } from "react-i18next"; ...@@ -4,7 +4,9 @@ import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import { proxy, useSnapshot } from "valtio"; import { proxy, useSnapshot } from "valtio";
import { sendChat } from "@/api";
import { useConfig } from "@/config"; import { useConfig } from "@/config";
import { WebSocketStream } from "@/infra";
import { accountStore, roomStore } from "@/stores"; import { accountStore, roomStore } from "@/stores";
import { Select } from "@/ui/Shared"; import { Select } from "@/ui/Shared";
...@@ -16,6 +18,7 @@ const serverConfig = NeosConfig.servers; ...@@ -16,6 +18,7 @@ const serverConfig = NeosConfig.servers;
const KOISHI_INDEX = 0; const KOISHI_INDEX = 0;
const PRERELEASE_INDEX = 3; const PRERELEASE_INDEX = 3;
const ENV_408 = 4;
const { const {
defaults: { defaultPlayer, defaultPassword }, defaults: { defaultPlayer, defaultPassword },
...@@ -54,6 +57,16 @@ export const MatchModal: React.FC = ({}) => { ...@@ -54,6 +57,16 @@ export const MatchModal: React.FC = ({}) => {
const handlePasswdChange = (event: ChangeEvent<HTMLInputElement>) => { const handlePasswdChange = (event: ChangeEvent<HTMLInputElement>) => {
setPasswd(event.target.value); setPasswd(event.target.value);
}; };
const send408Hint = (conn: WebSocketStream) => {
setTimeout(
() =>
sendChat(
conn,
"由于技术原因,408环境卡池内可用卡牌暂无法直接标出,某些卡片实际使用的是旧效果,例如混沌之黑魔术师、多尔·多拉、死之卡组破坏病毒...",
),
1000,
);
};
const handleSubmit = async () => { const handleSubmit = async () => {
setConfirmLoading(true); setConfirmLoading(true);
...@@ -62,6 +75,7 @@ export const MatchModal: React.FC = ({}) => { ...@@ -62,6 +75,7 @@ export const MatchModal: React.FC = ({}) => {
ip: genServerAddress(serverId), ip: genServerAddress(serverId),
passWd: passwd, passWd: passwd,
enableKuriboh, enableKuriboh,
customOnConnected: serverId === ENV_408 ? send408Hint : undefined,
}); });
}; };
...@@ -115,6 +129,10 @@ export const MatchModal: React.FC = ({}) => { ...@@ -115,6 +129,10 @@ export const MatchModal: React.FC = ({}) => {
value: PRERELEASE_INDEX, value: PRERELEASE_INDEX,
label: i18n("UltraPreemptiveServer"), label: i18n("UltraPreemptiveServer"),
}, },
{
value: ENV_408,
label: i18n("408"),
},
]} ]}
onChange={handleServerChange} onChange={handleServerChange}
/> />
......
...@@ -3,6 +3,7 @@ import rustInit from "rust-src"; ...@@ -3,6 +3,7 @@ import rustInit from "rust-src";
import { initStrings, initSuperPrerelease } from "@/api"; import { initStrings, initSuperPrerelease } from "@/api";
import { useConfig } from "@/config"; import { useConfig } from "@/config";
import { getUIContainer, initUIContainer } from "@/container/compat"; import { getUIContainer, initUIContainer } from "@/container/compat";
import { WebSocketStream } from "@/infra";
import { initReplaySocket, initSocket } from "@/middleware/socket"; import { initReplaySocket, initSocket } from "@/middleware/socket";
import { import {
pollSocketLooper, pollSocketLooper,
...@@ -21,6 +22,7 @@ export const connectSrvpro = async (params: { ...@@ -21,6 +22,7 @@ export const connectSrvpro = async (params: {
enableKuriboh?: boolean; enableKuriboh?: boolean;
replay?: boolean; replay?: boolean;
replayData?: ArrayBuffer; replayData?: ArrayBuffer;
customOnConnected?: (conn: WebSocketStream) => void;
}) => { }) => {
// 初始化wasm // 初始化wasm
const url = const url =
......
import React, { memo, useRef, useState } from "react"; import React, { memo, useRef, useState } from "react";
import { useDrag } from "react-dnd"; import { useDrag } from "react-dnd";
import { CardMeta, forbidden } from "@/api"; import { CardMeta, forbidden, forbidden_408 } from "@/api";
import { useConfig } from "@/config"; import { useConfig } from "@/config";
import { Type } from "../DeckZone"; import { Type } from "../DeckZone";
...@@ -22,48 +22,51 @@ export const DeckCard: React.FC<{ ...@@ -22,48 +22,51 @@ export const DeckCard: React.FC<{
onMouseUp?: (event: DeckCardMouseUpEvent) => void; onMouseUp?: (event: DeckCardMouseUpEvent) => void;
onMouseEnter?: () => void; onMouseEnter?: () => void;
onDoubleClick?: (card: CardMeta) => void; onDoubleClick?: (card: CardMeta) => void;
}> = memo(({ value, source, onMouseUp, onMouseEnter, onDoubleClick }) => { is408?: boolean;
const ref = useRef<HTMLDivElement>(null); }> = memo(
const [{ isDragging }, drag] = useDrag({ ({ value, source, onMouseUp, onMouseEnter, onDoubleClick, is408 }) => {
type: "Card", const ref = useRef<HTMLDivElement>(null);
item: { value, source }, const [{ isDragging }, drag] = useDrag({
collect: (monitor) => ({ type: "Card",
isDragging: monitor.isDragging(), item: { value, source },
}), collect: (monitor) => ({
}); isDragging: monitor.isDragging(),
drag(ref); }),
const [showText, setShowText] = useState(true); });
const limitCnt = forbidden.get(value); drag(ref);
const [showText, setShowText] = useState(true);
const limitCnt = is408 ? forbidden_408.get(value) : forbidden.get(value);
return ( return (
<div <div
className={styles.card} className={styles.card}
ref={ref} ref={ref}
style={{ opacity: isDragging && source !== "search" ? 0 : 1 }} style={{ opacity: isDragging && source !== "search" ? 0 : 1 }}
onMouseUp={(event) => onMouseUp={(event) =>
onMouseUp?.({ onMouseUp?.({
event, event,
card: value, card: value,
}) })
} }
onMouseEnter={onMouseEnter} onMouseEnter={onMouseEnter}
onDoubleClick={() => onDoubleClick?.(value)} onDoubleClick={() => onDoubleClick?.(value)}
onContextMenu={(e) => { onContextMenu={(e) => {
e.preventDefault(); e.preventDefault();
}} }}
> >
{showText && <div className={styles.cardname}>{value.text.name}</div>} {showText && <div className={styles.cardname}>{value.text.name}</div>}
<YgoCard <YgoCard
className={styles.cardcover} className={styles.cardcover}
code={value.id} code={value.id}
onLoad={() => setShowText(false)} onLoad={() => setShowText(false)}
/>
{limitCnt !== undefined && (
<img
className={styles.cardlimit}
src={`${assetsPath}/Limit0${limitCnt}.png`}
/> />
)} {limitCnt !== undefined && (
</div> <img
); className={styles.cardlimit}
}); src={`${assetsPath}/Limit0${limitCnt}.png`}
/>
)}
</div>
);
},
);
...@@ -26,6 +26,7 @@ export const DeckZone: React.FC<{ ...@@ -26,6 +26,7 @@ export const DeckZone: React.FC<{
) => void; ) => void;
onElementMouseUp: (event: DeckCardMouseUpEvent) => void; onElementMouseUp: (event: DeckCardMouseUpEvent) => void;
onDoubleClick?: (card: CardMeta) => void; onDoubleClick?: (card: CardMeta) => void;
is408?: boolean;
}> = ({ }> = ({
type, type,
cards, cards,
...@@ -33,6 +34,7 @@ export const DeckZone: React.FC<{ ...@@ -33,6 +34,7 @@ export const DeckZone: React.FC<{
onChange, onChange,
onElementMouseUp: onElementMouseUp, onElementMouseUp: onElementMouseUp,
onDoubleClick, onDoubleClick,
is408,
}) => { }) => {
const { message } = App.useApp(); const { message } = App.useApp();
const [allowToDrop, setAllowToDrop] = useState(false); const [allowToDrop, setAllowToDrop] = useState(false);
...@@ -74,6 +76,7 @@ export const DeckZone: React.FC<{ ...@@ -74,6 +76,7 @@ export const DeckZone: React.FC<{
source={type} source={type}
onMouseUp={onElementMouseUp} onMouseUp={onElementMouseUp}
onDoubleClick={onDoubleClick} onDoubleClick={onDoubleClick}
is408={is408}
/> />
))} ))}
<div className={styles["editing-zone-name"]}> <div className={styles["editing-zone-name"]}>
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
display: flex; display: flex;
align-items: center; align-items: center;
.prefix { .prefix {
height: 40px; height: 2rem;
line-height: 40px; line-height: 2rem;
padding: 0 1rem; padding: 0 1rem;
border-radius: 6px; border-radius: 6px;
border-right: none; border-right: none;
......
...@@ -10,7 +10,7 @@ export const Select: React.FC< ...@@ -10,7 +10,7 @@ export const Select: React.FC<
{title && <span className={styles.prefix}>{title}</span>} {title && <span className={styles.prefix}>{title}</span>}
<AntdSelect <AntdSelect
className={classNames(styles.select, className)} className={classNames(styles.select, className)}
size="large" size="middle"
dropdownStyle={{ dropdownStyle={{
backdropFilter: "blur(20px)", backdropFilter: "blur(20px)",
...dropdownStyle, ...dropdownStyle,
......
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