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
0
Issues
0
List
Boards
Labels
Service Desk
Milestones
Merge Requests
0
Merge Requests
0
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
love_飞影
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