Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
N
Neos
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Locked Files
Issues
1
Issues
1
List
Boards
Labels
Service Desk
Milestones
Merge Requests
2
Merge Requests
2
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Security & Compliance
Security & Compliance
Dependency List
License Compliance
Packages
Packages
List
Container Registry
Analytics
Analytics
CI / CD
Code Review
Insights
Issues
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
MyCard
Neos
Commits
d31f77a7
Commit
d31f77a7
authored
Jul 10, 2023
by
Chunchi Che
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Feat/replay
parent
f15b790f
Changes
18
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
580 additions
and
25 deletions
+580
-25
neos-protobuf
neos-protobuf
+1
-1
neos.config.json
neos.config.json
+5
-1
neos.config.prod.json
neos.config.prod.json
+5
-1
src/api/ocgcore/idl/ocgcore.ts
src/api/ocgcore/idl/ocgcore.ts
+321
-5
src/api/ocgcore/ocgAdapter/protoDecl.ts
src/api/ocgcore/ocgAdapter/protoDecl.ts
+1
-0
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/mod.ts
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/mod.ts
+6
-0
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/sibylName.ts
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/sibylName.ts
+45
-0
src/middleware/socket.ts
src/middleware/socket.ts
+14
-1
src/service/duel/gameMsg.ts
src/service/duel/gameMsg.ts
+27
-0
src/service/duel/sibylName.ts
src/service/duel/sibylName.ts
+8
-0
src/service/duel/start.ts
src/service/duel/start.ts
+6
-1
src/stores/matStore/store.ts
src/stores/matStore/store.ts
+1
-0
src/stores/matStore/types.ts
src/stores/matStore/types.ts
+2
-0
src/ui/Login.tsx
src/ui/Login.tsx
+4
-0
src/ui/Neos.tsx
src/ui/Neos.tsx
+9
-0
src/ui/Replay/index.scss
src/ui/Replay/index.scss
+0
-0
src/ui/Replay/index.tsx
src/ui/Replay/index.tsx
+110
-0
src/ui/WaitRoom.tsx
src/ui/WaitRoom.tsx
+15
-15
No files found.
neos-protobuf
@
30f4ea7a
Subproject commit
2b7892f461be231b95ccc3c13512359177f3bb98
Subproject commit
30f4ea7acd79b9cb18a358548520ca939e22dc5f
neos.config.json
View file @
d31f77a7
...
...
@@ -10,6 +10,7 @@
"cardImgUrl"
:
"https://cdn02.moecube.com:444/images/ygopro-images-zh-CN"
,
"cardsDbUrl"
:
"https://cdn02.moecube.com:444/ygopro-database/zh-CN/cards.cdb"
,
"stringsUrl"
:
"https://cdn02.moecube.com:444/ygopro-database/zh-CN/strings.conf"
,
"replayUrl"
:
"replay.neos.moe"
,
"chainALL"
:
false
,
"streamInterval"
:
20
,
"startDelay"
:
1000
,
...
...
@@ -68,6 +69,9 @@
164
,
165
,
170
,
180
180
,
230
,
231
,
236
]
}
neos.config.prod.json
View file @
d31f77a7
...
...
@@ -10,6 +10,7 @@
"cardImgUrl"
:
"https://cdn02.moecube.com:444/images/ygopro-images-zh-CN"
,
"cardsDbUrl"
:
"https://cdn02.moecube.com:444/ygopro-database/zh-CN/cards.cdb"
,
"stringsUrl"
:
"https://cdn02.moecube.com:444/ygopro-database/zh-CN/strings.conf"
,
"replayUrl"
:
"replay.neos.moe"
,
"chainALL"
:
false
,
"streamInterval"
:
20
,
"startDelay"
:
1000
,
...
...
@@ -68,6 +69,9 @@
164
,
165
,
170
,
180
180
,
230
,
231
,
236
]
}
src/api/ocgcore/idl/ocgcore.ts
View file @
d31f77a7
This diff is collapsed.
Click to expand it.
src/api/ocgcore/ocgAdapter/protoDecl.ts
View file @
d31f77a7
...
...
@@ -69,3 +69,4 @@ export const MSG_FIELD_DISABLED = 56;
export
const
MSG_HAND_RES
=
133
;
export
const
MSG_SHUFFLE_HAND
=
33
;
export
const
MSG_SHUFFLE_EXTRA
=
39
;
export
const
MSG_SIBYL_NAME
=
235
;
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/mod.ts
View file @
d31f77a7
...
...
@@ -37,6 +37,7 @@ import MsgSelectTributeAdapter from "./selectTribute";
import
MsgSelectUnselectCardAdapter
from
"
./selectUnselectCard
"
;
import
MsgShuffleHandExtraAdapter
from
"
./shuffleHandExtra
"
;
import
MsgShuffleSetCard
from
"
./shuffleSetCard
"
;
import
MsgSibylNameAdapter
from
"
./sibylName
"
;
import
MsgSortCard
from
"
./sortCard
"
;
import
MsgStartAdapter
from
"
./start
"
;
import
MsgTossAdapter
from
"
./toss
"
;
...
...
@@ -273,6 +274,11 @@ export default class GameMsgAdapter implements StocAdapter {
break
;
}
case
GAME_MSG
.
MSG_SIBYL_NAME
:
{
gameMsg
.
sibyl_name
=
MsgSibylNameAdapter
(
gameData
);
break
;
}
default
:
{
gameMsg
.
unimplemented
=
new
ygopro
.
StocGameMessage
.
MsgUnimplemented
({
command
:
func
,
...
...
src/api/ocgcore/ocgAdapter/stoc/stocGameMsg/sibylName.ts
0 → 100644
View file @
d31f77a7
import
{
ygopro
}
from
"
@/api/ocgcore/idl/ocgcore
"
;
import
MsgSibylName
=
ygopro
.
StocGameMessage
.
MsgSibylName
;
const
LEN
=
100
;
/*
* Msg Sibyl Name
* @param - TODO
*
* @usage - Replay模式获取对战双方的昵称
* */
export
default
(
data
:
Uint8Array
)
=>
{
const
decoder
=
new
TextDecoder
(
"
utf-16
"
);
let
offset
=
0
;
const
name_0
=
cutString
(
decoder
.
decode
(
data
.
slice
(
offset
,
offset
+
LEN
)));
offset
+=
LEN
;
const
name_0_tag
=
cutString
(
decoder
.
decode
(
data
.
slice
(
offset
,
offset
+
LEN
))
);
offset
+=
LEN
;
const
name_0_c
=
cutString
(
decoder
.
decode
(
data
.
slice
(
offset
,
offset
+
LEN
)));
offset
+=
LEN
;
const
name_1
=
cutString
(
decoder
.
decode
(
data
.
slice
(
offset
,
offset
+
LEN
)));
offset
+=
LEN
;
const
name_1_tag
=
cutString
(
decoder
.
decode
(
data
.
slice
(
offset
,
offset
+
LEN
))
);
offset
+=
LEN
;
const
name_1_c
=
cutString
(
decoder
.
decode
(
data
.
slice
(
offset
,
offset
+
LEN
)));
return
new
MsgSibylName
({
name_0
,
name_0_tag
,
name_0_c
,
name_1
,
name_1_tag
,
name_1_c
,
});
};
function
cutString
(
str
:
string
):
string
{
const
end
=
str
.
indexOf
(
"
\
0
"
);
return
str
.
substring
(
0
,
end
);
}
src/middleware/socket.ts
View file @
d31f77a7
...
...
@@ -26,6 +26,11 @@ export interface socketAction {
player
:
string
;
passWd
:
string
;
};
isReplay
?:
boolean
;
// 是否是回放模式
replayInfo
?:
{
Url
:
string
;
// 提供回放服务的地址
data
:
ArrayBuffer
;
// 回放数据
};
// 通过长连接发送的数据
payload
?:
Uint8Array
;
}
...
...
@@ -36,12 +41,20 @@ let ws: WebSocketStream | null = null;
export
default
async
function
(
action
:
socketAction
)
{
switch
(
action
.
cmd
)
{
case
socketCmd
.
CONNECT
:
{
const
info
=
action
.
initInfo
;
const
{
initInfo
:
info
,
isReplay
,
replayInfo
}
=
action
;
if
(
info
)
{
ws
=
new
WebSocketStream
(
info
.
ip
,
(
conn
,
_event
)
=>
handleSocketOpen
(
conn
,
info
.
ip
,
info
.
player
,
info
.
passWd
)
);
await
ws
.
execute
(
handleSocketMessage
);
}
else
if
(
isReplay
&&
replayInfo
)
{
ws
=
new
WebSocketStream
(
replayInfo
.
Url
,
(
conn
,
_event
)
=>
{
console
.
info
(
"
replay websocket open.
"
);
conn
.
binaryType
=
"
arraybuffer
"
;
conn
.
send
(
replayInfo
.
data
);
});
await
ws
.
execute
(
handleSocketMessage
);
}
...
...
src/service/duel/gameMsg.ts
View file @
d31f77a7
import
{
ygopro
}
from
"
@/api
"
;
import
{
sleep
}
from
"
@/infra
"
;
import
{
matStore
}
from
"
@/stores
"
;
import
{
showWaiting
}
from
"
@/ui/Duel/Message
"
;
import
onAnnounce
from
"
./announce
"
;
...
...
@@ -40,6 +41,7 @@ import onMsgSet from "./set";
import
onMsgShuffleDeck
from
"
./shuffleDeck
"
;
import
onMsgShuffleHandExtra
from
"
./shuffleHandExtra
"
;
import
onMsgShuffleSetCard
from
"
./shuffleSetCard
"
;
import
onMsgSibylName
from
"
./sibylName
"
;
import
onMsgSortCard
from
"
./sortCard
"
;
import
onMsgSpSummoned
from
"
./spSummoned
"
;
import
onMsgSpSummoning
from
"
./spSummoning
"
;
...
...
@@ -69,6 +71,24 @@ const ActiveList = [
"
select_yes_no
"
,
];
const
ReplayIgnoreMsg
=
[
"
select_idle_cmd
"
,
"
select_place
"
,
"
select_card
"
,
"
select_chain
"
,
"
select_effect_yn
"
,
"
select_position
"
,
"
select_option
"
,
"
select_battle_cmd
"
,
"
select_unselect_card
"
,
"
select_yes_no
"
,
"
select_tribute
"
,
"
select_counter
"
,
"
rock_paper_scissors
"
,
"
sort_card
"
,
"
announce
"
,
];
let
animation
:
Promise
<
unknown
>
=
new
Promise
<
void
>
((
rs
)
=>
rs
());
export
default
async
function
handleGameMsg
(
pb
:
ygopro
.
YgoStocMsg
)
{
...
...
@@ -83,6 +103,8 @@ async function _handleGameMsg(pb: ygopro.YgoStocMsg) {
showWaiting
(
false
);
}
if
(
matStore
.
isReplay
&&
ReplayIgnoreMsg
.
includes
(
msg
.
gameMsg
))
return
;
switch
(
msg
.
gameMsg
)
{
case
"
start
"
:
{
await
onMsgStart
(
msg
.
start
);
...
...
@@ -345,6 +367,11 @@ async function _handleGameMsg(pb: ygopro.YgoStocMsg) {
break
;
}
case
"
sibyl_name
"
:
{
onMsgSibylName
(
msg
.
sibyl_name
);
break
;
}
case
"
unimplemented
"
:
{
onUnimplemented
(
msg
.
unimplemented
);
...
...
src/service/duel/sibylName.ts
0 → 100644
View file @
d31f77a7
import
{
ygopro
}
from
"
@/api
"
;
import
{
playerStore
}
from
"
@/stores
"
;
type
MsgSibylName
=
ygopro
.
StocGameMessage
.
MsgSibylName
;
export
default
(
sibylName
:
MsgSibylName
)
=>
{
playerStore
.
getMePlayer
().
name
=
sibylName
.
name_0
;
playerStore
.
getOpPlayer
().
name
=
sibylName
.
name_1
;
};
src/service/duel/start.ts
View file @
d31f77a7
...
...
@@ -7,6 +7,7 @@ import { fetchCard, ygopro } from "@/api";
import
{
useConfig
}
from
"
@/config
"
;
import
{
sleep
}
from
"
@/infra
"
;
import
{
cardStore
,
CardType
,
matStore
}
from
"
@/stores
"
;
import
{
replayStart
}
from
"
@/ui/Replay
"
;
const
TOKEN_SIZE
=
13
;
// 每人场上最多就只可能有13个token
export
default
async
(
start
:
ygopro
.
StocGameMessage
.
MsgStart
)
=>
{
...
...
@@ -72,7 +73,11 @@ export default async (start: ygopro.StocGameMessage.MsgStart) => {
// 设置自己的额外卡组,信息是在waitroom之中拿到的
cardStore
.
at
(
ygopro
.
CardZone
.
EXTRA
,
1
-
opponent
)
.
forEach
((
card
)
=>
(
card
.
code
=
myExtraDeckCodes
.
pop
()
!
));
.
forEach
((
card
)
=>
(
card
.
code
=
myExtraDeckCodes
.
pop
()
??
0
));
if
(
matStore
.
isReplay
)
{
replayStart
();
}
// 初始化完后,sleep 1s,让UI初始化完成,
// 否则在和AI对战时,由于后端给传给前端的`MSG`频率太高,会导致一些问题。
...
...
src/stores/matStore/store.ts
View file @
d31f77a7
...
...
@@ -80,6 +80,7 @@ export const matStore: MatState = proxy<MatState>({
enableM2
:
false
,
// 允许进入M2阶段
enableEp
:
false
,
// 允许回合结束
},
isReplay
:
false
,
unimplemented
:
0
,
handResults
:
{
me
:
0
,
...
...
src/stores/matStore/types.ts
View file @
d31f77a7
...
...
@@ -28,6 +28,8 @@ export interface MatState {
phase
:
PhaseState
;
isReplay
:
boolean
;
// 是否是回放模式
result
?:
{
isWin
:
boolean
;
reason
:
string
;
...
...
src/ui/Login.tsx
View file @
d31f77a7
...
...
@@ -9,6 +9,7 @@
import
"
../styles/core.scss
"
;
import
{
Input
}
from
"
antd
"
;
import
Link
from
"
antd/es/typography/Link
"
;
import
React
,
{
ChangeEvent
,
useEffect
,
useState
}
from
"
react
"
;
import
{
useNavigate
}
from
"
react-router-dom
"
;
...
...
@@ -88,6 +89,9 @@ export default function Login() {
<
a
href=
"https://doc.neos.moe"
>
Player Guide
</
a
>
<
span
className=
"fa fa-arrow-right"
></
span
>
</
p
>
<
p
>
<
Link
href=
"replay"
>
Clik here to play ygo replay
</
Link
>
</
p
>
</
div
>
<
div
className=
"sign-in__actions clearfix"
>
<
ul
>
...
...
src/ui/Neos.tsx
View file @
d31f77a7
...
...
@@ -7,6 +7,7 @@ const Login = React.lazy(() => import("./Login"));
const
WaitRoom
=
React
.
lazy
(()
=>
import
(
"
./WaitRoom
"
));
const
Mora
=
React
.
lazy
(()
=>
import
(
"
./Mora
"
));
const
NeosDuel
=
React
.
lazy
(()
=>
import
(
"
./Duel/Main
"
));
const
Replay
=
React
.
lazy
(()
=>
import
(
"
./Replay
"
));
export
default
function
()
{
return
(
...
...
@@ -28,6 +29,14 @@ export default function () {
</
Suspense
>
}
/>
<
Route
path=
"/replay"
element=
{
<
Suspense
fallback=
{
<
Loading
/>
}
>
<
Replay
/>
</
Suspense
>
}
/>
<
Route
path=
"/duel/:player/:passWd/:ip"
element=
{
...
...
src/ui/Replay/index.scss
0 → 100644
View file @
d31f77a7
src/ui/Replay/index.tsx
0 → 100644
View file @
d31f77a7
import
"
../../styles/core.scss
"
;
import
{
UploadOutlined
}
from
"
@ant-design/icons
"
;
import
{
Button
,
message
,
Modal
,
Upload
,
UploadProps
}
from
"
antd
"
;
import
React
,
{
useEffect
,
useState
}
from
"
react
"
;
import
{
useNavigate
}
from
"
react-router-dom
"
;
import
rustInit
from
"
rust-src
"
;
import
{
proxy
,
useSnapshot
}
from
"
valtio
"
;
import
{
initStrings
}
from
"
@/api
"
;
import
{
useConfig
}
from
"
@/config
"
;
import
socketMiddleWare
,
{
socketCmd
}
from
"
@/middleware/socket
"
;
import
sqliteMiddleWare
,
{
sqliteCmd
}
from
"
@/middleware/sqlite
"
;
import
{
matStore
}
from
"
@/stores
"
;
const
NeosConfig
=
useConfig
();
const
localStore
=
proxy
({
hasStart
:
false
,
});
const
ReplayModal
:
React
.
FC
=
()
=>
{
const
{
hasStart
}
=
useSnapshot
(
localStore
);
const
[
replay
,
setReplay
]
=
useState
<
null
|
ArrayBuffer
>
(
null
);
const
uploadProps
:
UploadProps
=
{
name
:
"
replay
"
,
onChange
(
info
)
{
info
.
file
.
status
=
"
done
"
;
},
beforeUpload
(
file
,
_
)
{
const
reader
=
new
FileReader
();
reader
.
readAsArrayBuffer
(
file
);
reader
.
onload
=
(
e
)
=>
setReplay
(
e
.
target
?.
result
as
ArrayBuffer
);
},
};
const
navigate
=
useNavigate
();
useEffect
(()
=>
{
if
(
hasStart
)
{
// 跳转
navigate
(
`/duel/neos/replay/
${
NeosConfig
.
replayUrl
}
`
);
}
},
[
hasStart
]);
return
(
<
Modal
title=
"选择回放"
open=
{
true
}
maskClosable=
{
false
}
onOk=
{
async
()
=>
{
if
(
replay
===
null
)
{
message
.
error
(
"
请先上传录像文件
"
);
}
else
{
// 标记为回放模式
matStore
.
isReplay
=
true
;
// 初始化wasm
const
url
=
import
.
meta
.
env
.
BASE_URL
===
"
/
"
?
undefined
:
new
URL
(
"
rust_src_bg.wasm
"
,
`${import.meta.env.BASE_URL}assets/`
);
await
rustInit
(
url
);
// 初始化额外卡组
// FIXME: 这样写应该不对,有空来修
window
.
myExtraDeckCodes
=
[];
// 初始化sqlite
await
sqliteMiddleWare
({
cmd
:
sqliteCmd
.
INIT
,
initInfo
:
{
dbUrl
:
NeosConfig
.
cardsDbUrl
},
});
// 初始化文案
await
initStrings
();
// 连接回放websocket服务
socketMiddleWare
({
cmd
:
socketCmd
.
CONNECT
,
isReplay
:
true
,
replayInfo
:
{
Url
:
NeosConfig
.
replayUrl
,
data
:
replay
,
},
});
}
}
}
onCancel=
{
()
=>
{
// 断开websocket连接
socketMiddleWare
({
cmd
:
socketCmd
.
DISCONNECT
});
// 回到初始界面
navigate
(
"
/
"
);
}
}
>
<
Upload
{
...
uploadProps
}
>
<
Button
icon=
{
<
UploadOutlined
/>
}
>
点击上传录像文件
</
Button
>
</
Upload
>
</
Modal
>
);
};
export
const
replayStart
=
()
=>
{
localStore
.
hasStart
=
true
;
};
export
default
ReplayModal
;
src/ui/WaitRoom.tsx
View file @
d31f77a7
...
...
@@ -59,15 +59,12 @@ const WaitRoom = () => {
useEffect
(()
=>
{
if
(
ip
&&
player
&&
player
.
length
!=
0
&&
passWd
&&
passWd
.
length
!=
0
)
{
const
init
=
async
()
=>
{
// 页面第一次渲染时,通过socket中间件向ygopro服务端请求建立长连接
socketMiddleWare
({
cmd
:
socketCmd
.
CONNECT
,
initInfo
:
{
ip
,
player
,
passWd
,
},
});
// 初始化wasm
const
url
=
import
.
meta
.
env
.
BASE_URL
===
"
/
"
?
undefined
:
new
URL
(
"
rust_src_bg.wasm
"
,
`
${
import
.
meta
.
env
.
BASE_URL
}
assets/`
);
await
rustInit
(
url
);
// 初始化sqlite
await
sqliteMiddleWare
({
...
...
@@ -78,12 +75,15 @@ const WaitRoom = () => {
// 初始化文案
await
initStrings
();
// 初始化wasm
const
url
=
import
.
meta
.
env
.
BASE_URL
===
"
/
"
?
undefined
:
new
URL
(
"
rust_src_bg.wasm
"
,
`
${
import
.
meta
.
env
.
BASE_URL
}
assets/`
);
await
rustInit
(
url
);
// 页面第一次渲染时,通过socket中间件向ygopro服务端请求建立长连接
socketMiddleWare
({
cmd
:
socketCmd
.
CONNECT
,
initInfo
:
{
ip
,
player
,
passWd
,
},
});
};
init
();
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment