Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Sign in / Register
Toggle navigation
S
srvpro2
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
nanahira
srvpro2
Commits
76b99077
Commit
76b99077
authored
Feb 13, 2026
by
nanahira
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
wip
parent
9a20f3ef
Changes
7
Hide whitespace changes
Inline
Side-by-side
Showing
7 changed files
with
72 additions
and
13 deletions
+72
-13
.dockerignore
.dockerignore
+1
-0
.gitignore
.gitignore
+1
-0
src/client/client-handler.ts
src/client/client-handler.ts
+7
-3
src/join-handlers/join-room.ts
src/join-handlers/join-room.ts
+2
-0
src/room/duel-record.ts
src/room/duel-record.ts
+5
-3
src/room/room.ts
src/room/room.ts
+50
-7
src/room/ygopro-resource-loader.ts
src/room/ygopro-resource-loader.ts
+6
-0
No files found.
.dockerignore
View file @
76b99077
...
...
@@ -42,3 +42,4 @@ Dockerfile
.dockerignore
/tests
/ssl
/ygopro
.gitignore
View file @
76b99077
...
...
@@ -37,3 +37,4 @@ lerna-debug.log*
/output
/config.yaml
/ssl
/ygopro
src/client/client-handler.ts
View file @
76b99077
...
...
@@ -16,6 +16,7 @@ import {
firstValueFrom
,
merge
,
map
,
take
,
}
from
'
rxjs
'
;
import
{
YGOProCtosDisconnect
}
from
'
../utility/ygopro-ctos-disconnect
'
;
...
...
@@ -52,8 +53,8 @@ export class ClientHandler {
YGOProCtosExternalAddress
,
YGOProCtosPlayerInfo
,
YGOProCtosJoinGame
,
].
some
((
allowed
)
=>
msg
instanceof
allowed
);
if
(
client
.
established
!
==
isPreHandshakeMsg
)
{
].
some
((
allowed
)
=>
(
msg
instanceof
allowed
)
as
boolean
);
if
(
client
.
established
=
==
isPreHandshakeMsg
)
{
// disallow any messages before handshake is complete, except for the ones needed for handshake
return
undefined
;
}
...
...
@@ -85,7 +86,7 @@ export class ClientHandler {
await
this
.
ctx
.
dispatch
(
msg
,
client
);
}
catch
(
e
)
{
this
.
logger
.
warn
(
`Error dispatching message
${
msg
.
constructor
.
name
}
from
${
client
.
loggingIp
()}
:
${(
e
as
Error
).
message
}
`
,
`Error dispatching message
${
msg
.
constructor
.
name
}
from
${
client
.
loggingIp
()}
:
${(
e
as
Error
).
stack
}
`
,
);
}
});
...
...
@@ -93,16 +94,19 @@ export class ClientHandler {
const
handshake$
=
forkJoin
([
client
.
receive$
.
pipe
(
filter
((
msg
)
=>
msg
instanceof
YGOProCtosPlayerInfo
),
take
(
1
),
takeUntil
(
client
.
disconnect$
),
),
client
.
receive$
.
pipe
(
filter
((
msg
)
=>
msg
instanceof
YGOProCtosJoinGame
),
take
(
1
),
takeUntil
(
client
.
disconnect$
),
),
]).
pipe
(
timeout
(
5000
),
takeUntil
(
client
.
disconnect$
));
firstValueFrom
(
handshake$
)
.
then
(()
=>
{
this
.
logger
.
debug
({
client
:
client
.
name
},
'
Handshake completed
'
);
client
.
established
=
true
;
})
.
catch
(()
=>
{
...
...
src/join-handlers/join-room.ts
View file @
76b99077
...
...
@@ -3,11 +3,13 @@ import { Context } from '../app';
import
{
RoomManager
}
from
'
../room/room-manager
'
;
export
class
JoinRoom
{
private
logger
=
this
.
ctx
.
createLogger
(
this
.
constructor
.
name
);
constructor
(
private
ctx
:
Context
)
{
this
.
ctx
.
middleware
(
YGOProCtosJoinGame
,
async
(
msg
,
client
,
next
)
=>
{
if
(
!
msg
.
pass
)
{
return
next
();
}
this
.
logger
.
debug
({
name
:
client
.
name
,
pass
:
msg
.
pass
},
'
Joining room
'
);
const
roomManager
=
this
.
ctx
.
get
(()
=>
RoomManager
);
const
room
=
await
roomManager
.
findOrCreateByName
(
msg
.
pass
);
return
room
.
join
(
client
);
...
...
src/room/duel-record.ts
View file @
76b99077
...
...
@@ -10,10 +10,12 @@ const REPLAY_ID_YRP2 = 0x32707279;
const
PRO_VERSION
=
0x1362
;
export
class
DuelRecord
{
seed
:
number
[];
players
:
{
name
:
string
;
deck
:
YGOProDeck
}[];
constructor
(
public
seed
:
number
[],
public
players
:
{
name
:
string
;
deck
:
YGOProDeck
}[],
)
{}
winPosition
?:
number
;
responses
:
Buffer
[];
responses
:
Buffer
[]
=
[]
;
toYrp
(
room
:
Room
)
{
const
isTag
=
room
.
isTag
;
...
...
src/room/room.ts
View file @
76b99077
...
...
@@ -27,6 +27,8 @@ import {
YGOProStocDuelEnd
,
YGOProStocChangeSide
,
YGOProStocWaitingSide
,
YGOProCtosTpResult
,
TurnPlayerResult
,
}
from
'
ygopro-msg-encode
'
;
import
{
DefaultHostInfoProvider
}
from
'
./default-hostinfo-provder
'
;
import
{
CardReaderFinalized
}
from
'
koishipro-core.js
'
;
...
...
@@ -45,11 +47,12 @@ import { OnRoomLeavePlayer } from './room-event/on-room-leave-player';
import
{
OnRoomLeaveObserver
}
from
'
./room-event/on-room-leave-observer
'
;
import
{
OnRoomMatchStart
}
from
'
./room-event/on-room-match-start
'
;
import
{
OnRoomGameStart
}
from
'
./room-event/on-room-game-start
'
;
// import { OnRoomDuelStart } from './room-event/on-room-duel-start'; // 备用事件,暂未使用
import
YGOProDeck
from
'
ygopro-deck-encode
'
;
import
{
checkDeck
,
checkChangeSide
}
from
'
../utility/check-deck
'
;
import
{
DuelRecord
}
from
'
./duel-record
'
;
import
{
last
}
from
'
rxjs
'
;
import
{
RoomEvent
}
from
'
./room-event/room-event
'
;
import
{
generateSeed
}
from
'
../utility/generate-seed
'
;
import
{
OnRoomDuelStart
}
from
'
./room-event/on-room-duel-start
'
;
export
type
RoomFinalizor
=
(
self
:
Room
)
=>
Awaitable
<
any
>
;
...
...
@@ -336,6 +339,7 @@ export class Room {
.
forEach
((
p
)
=>
p
.
send
(
new
YGOProStocDuelStart
()));
}
const
duelPos
=
this
.
getSwappedDuelPosByDuelPos
(
winMsg
.
player
!
);
this
.
isPosSwapped
=
false
;
await
Promise
.
all
(
this
.
allPlayers
.
map
((
p
)
=>
p
.
send
(
...
...
@@ -513,15 +517,21 @@ export class Room {
const
oldPos
=
client
.
pos
;
// 从当前位置的下一个位置开始循环查找空位
let
nextPos
=
(
oldPos
+
1
)
%
4
;
while
(
this
.
players
[
nextPos
])
{
nextPos
=
(
nextPos
+
1
)
%
4
;
}
// 移动到新位置
this
.
players
[
oldPos
]
=
undefined
;
this
.
players
[
firstEmptyPlayerSlot
]
=
client
;
client
.
pos
=
firstEmptyPlayerSlot
;
this
.
players
[
nextPos
]
=
client
;
client
.
pos
=
nextPos
;
// 发送 PlayerChange 给所有人
const
changeMsg
=
new
YGOProStocHsPlayerChange
().
fromPartial
({
playerPosition
:
oldPos
,
playerState
:
firstEmptyPlayerSlot
,
playerState
:
nextPos
,
});
this
.
allPlayers
.
forEach
((
p
)
=>
p
.
send
(
changeMsg
));
...
...
@@ -713,7 +723,7 @@ export class Room {
if
(
!
[
DuelStage
.
Finger
,
DuelStage
.
Siding
].
includes
(
this
.
duelStage
))
{
return
false
;
}
if
(
this
.
winnerPosition
s
.
length
===
0
)
{
if
(
this
.
duelRecord
s
.
length
===
0
)
{
this
.
allPlayers
.
forEach
((
p
)
=>
p
.
send
(
new
YGOProStocDuelStart
()));
const
displayCountDecks
=
[
0
,
1
].
map
(
(
p
)
=>
this
.
getDuelPosPlayers
(
p
)[
0
].
deck
!
,
...
...
@@ -754,7 +764,7 @@ export class Room {
}
// 触发事件
if
(
this
.
winnerPosition
s
.
length
===
0
)
{
if
(
this
.
duelRecord
s
.
length
===
0
)
{
// 触发比赛开始事件(第一局)
await
this
.
ctx
.
dispatch
(
new
OnRoomMatchStart
(
this
),
...
...
@@ -767,4 +777,37 @@ export class Room {
return
true
;
}
@
RoomMethod
()
private
async
onDuelStart
(
client
:
Client
,
msg
:
YGOProCtosTpResult
)
{
if
(
this
.
duelStage
!==
DuelStage
.
FirstGo
)
{
return
;
}
if
(
client
!==
this
.
firstgoPlayer
)
{
return
;
}
this
.
isPosSwapped
=
(
msg
.
res
===
TurnPlayerResult
.
FIRST
)
!==
(
this
.
getDuelPos
(
client
)
===
0
);
const
duelRecord
=
new
DuelRecord
(
generateSeed
(),
this
.
playingPlayers
.
map
((
p
)
=>
({
name
:
p
.
name
,
deck
:
p
.
deck
!
})),
);
if
(
this
.
isPosSwapped
)
{
this
.
playingPlayers
.
forEach
((
p
)
=>
{
duelRecord
.
players
[
this
.
getSwappedDuelPos
(
p
)]
=
{
name
:
p
.
name
,
deck
:
p
.
deck
!
,
};
});
}
this
.
duelRecords
.
push
(
duelRecord
);
await
this
.
ctx
.
dispatch
(
new
OnRoomDuelStart
(
this
),
this
.
getDuelPosPlayers
(
this
.
getSwappedDuelPos
(
0
))[
0
],
);
this
.
allPlayers
.
forEach
((
p
)
=>
p
.
sendChat
(
'
TODO: duel start
'
));
return
this
.
finalize
();
}
}
src/room/ygopro-resource-loader.ts
View file @
76b99077
...
...
@@ -13,7 +13,13 @@ export class YGOProResourceLoader {
]);
extraScriptPaths
=
loadPaths
(
this
.
ctx
.
getConfig
(
'
EXTRA_SCRIPT_PATH
'
));
private
logger
=
this
.
ctx
.
createLogger
(
this
.
constructor
.
name
);
async
getCardReader
()
{
this
.
logger
.
debug
(
{
ygoproPaths
:
this
.
ygoproPaths
,
sql
:
typeof
this
.
ctx
.
SQL
.
Database
},
'
Getting card reader
'
,
);
return
DirCardReader
(
this
.
ctx
.
SQL
,
...
this
.
ygoproPaths
);
}
...
...
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