Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
F
fc-minigame
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
陈冲
fc-minigame
Commits
96495239
Commit
96495239
authored
Jun 17, 2026
by
董政锦
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
fix: 打印websocket日志,把 avatar 作为用户标识(因为接口的userid/wechat_id经常返回没有);
parent
d65da20b
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
189 additions
and
45 deletions
+189
-45
useAdminGameSocket.ts
frontend-large/src/composables/useAdminGameSocket.ts
+5
-2
Rank1View.vue
frontend-large/src/pages/game1/views/Rank1View.vue
+15
-3
Game.vue
frontend-large/src/pages/game2/Game.vue
+26
-7
Rank1View.vue
frontend-large/src/pages/game2/views/Rank1View.vue
+29
-8
Game3.vue
frontend-large/src/pages/game3/Game3.vue
+4
-0
Rank1View.vue
frontend-large/src/pages/game3/views/Rank1View.vue
+2
-0
Game4.vue
frontend-large/src/pages/game4/Game4.vue
+32
-7
Rank1View.vue
frontend-large/src/pages/game4/views/Rank1View.vue
+29
-5
Game5.vue
frontend-large/src/pages/game5/Game5.vue
+21
-9
Rank1View.vue
frontend-large/src/pages/game5/views/Rank1View.vue
+14
-3
Rank1View.vue
frontend-large/src/pages/game6/views/Rank1View.vue
+12
-1
No files found.
frontend-large/src/composables/useAdminGameSocket.ts
View file @
96495239
...
@@ -58,6 +58,7 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) {
...
@@ -58,6 +58,7 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) {
validuser
:
CryptoJS
.
AES
.
encrypt
(
token
,
passphrase
).
toString
(),
validuser
:
CryptoJS
.
AES
.
encrypt
(
token
,
passphrase
).
toString
(),
},
},
onConnect
:
()
=>
{
onConnect
:
()
=>
{
console
.
log
(
'[useAdminGameSocket] WebSocket 连接成功,gameId:'
,
options
.
gameId
);
if
(
options
.
onConnect
)
{
if
(
options
.
onConnect
)
{
if
(
!
options
.
onConnect
())
{
if
(
!
options
.
onConnect
())
{
return
;
return
;
...
@@ -66,14 +67,16 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) {
...
@@ -66,14 +67,16 @@ export function useAdminGameSocket(options: AdminGameSocketOptions) {
window
.
setTimeout
(
requestRoomState
,
200
)
window
.
setTimeout
(
requestRoomState
,
200
)
},
},
onDisconnect
:
(
_reason
:
string
)
=>
{
onDisconnect
:
(
_reason
:
string
)
=>
{
console
.
log
(
'[useAdminGameSocket] WebSocket 断开连接,reason:'
,
_reason
,
'gameId:'
,
options
.
gameId
);
},
},
onError
:
(
error
:
any
)
=>
{
onError
:
(
error
:
any
)
=>
{
// console.error('socket.io connect_error:', error)
console
.
error
(
'[useAdminGameSocket] WebSocket 错误:'
,
error
,
'gameId:'
,
options
.
gameId
);
},
},
})
})
offSocketMessage
=
onSocketMessage
(
async
(
evt
,
payload
:
SocketPayload
)
=>
{
offSocketMessage
=
onSocketMessage
(
async
(
evt
,
payload
:
SocketPayload
)
=>
{
console
.
log
(
'[useAdminGameSocket] 收到 WebSocket 消息 - evt:'
,
evt
,
'payload:'
,
JSON
.
stringify
(
payload
),
'gameId:'
,
options
.
gameId
);
const
{
msg
,
state
,
data
}
=
payload
const
{
msg
,
state
,
data
}
=
payload
if
(
state
===
0
)
{
if
(
state
===
0
)
{
if
(
evt
==
'login_result'
)
{
if
(
evt
==
'login_result'
)
{
...
...
frontend-large/src/pages/game1/views/Rank1View.vue
View file @
96495239
...
@@ -62,12 +62,24 @@ const emptyPlayer = (index: number): Player => ({
...
@@ -62,12 +62,24 @@ const emptyPlayer = (index: number): Player => ({
const
list
=
ref
<
Player
[]
>
(
Array
.
from
({
length
:
RANK_LIMIT
},
(
_
,
index
)
=>
emptyPlayer
(
index
)));
const
list
=
ref
<
Player
[]
>
(
Array
.
from
({
length
:
RANK_LIMIT
},
(
_
,
index
)
=>
emptyPlayer
(
index
)));
const
playerKey
=
(
player
:
Player
,
index
=
0
)
=>
{
const
playerKey
=
(
player
:
Player
,
index
=
0
)
=>
{
// 优先使用 userid/wechat_id
if
(
player
.
userid
||
player
.
wechat_id
)
{
if
(
player
.
userid
||
player
.
wechat_id
)
{
return
player
.
userid
||
player
.
wechat_id
||
`empty-
${
index
}
`
;
const
id
=
player
.
userid
||
player
.
wechat_id
;
if
(
!
String
(
id
).
startsWith
(
'empty-'
))
{
return
String
(
id
);
}
}
if
(
player
.
nickname
!==
'-'
)
{
return
`player-
${
player
.
nickname
}
-
${
player
.
avatar
||
''
}
`
;
}
}
// 如果有头像且昵称有效,用 avatar 作为唯一标识
if
(
player
.
avatar
&&
player
.
nickname
&&
player
.
nickname
!==
'-'
&&
player
.
nickname
!==
'虚位以待'
)
{
return
`avt:
${
player
.
avatar
}
`
;
}
// 如果昵称有效但没有头像,用昵称作为标识
if
(
player
.
nickname
&&
player
.
nickname
!==
'-'
&&
player
.
nickname
!==
'虚位以待'
)
{
return
`name:
${
player
.
nickname
}
`
;
}
return
`empty-
${
index
}
`
;
return
`empty-
${
index
}
`
;
};
};
...
...
frontend-large/src/pages/game2/Game.vue
View file @
96495239
...
@@ -84,14 +84,23 @@ const updateRankPlayers = (data: any) => {
...
@@ -84,14 +84,23 @@ const updateRankPlayers = (data: any) => {
console
.
log
(
'[game2] updateRankPlayers 更新后 rankPlayers:'
,
JSON
.
stringify
(
rankPlayers
.
value
.
map
(
p
=>
({
nickname
:
p
.
nickname
,
userid
:
p
.
userid
}))))
console
.
log
(
'[game2] updateRankPlayers 更新后 rankPlayers:'
,
JSON
.
stringify
(
rankPlayers
.
value
.
map
(
p
=>
({
nickname
:
p
.
nickname
,
userid
:
p
.
userid
}))))
}
}
const
playerToRankPlayer
=
(
player
:
any
,
index
:
number
):
Player
=>
({
const
playerToRankPlayer
=
(
player
:
any
,
index
:
number
):
Player
=>
{
// WebSocket 数据可能没有 userid/wechat_id,用 avatar 作为唯一标识
const
hasId
=
player
?.
userid
||
player
?.
wechat_id
||
player
?.
openid
||
player
?.
user_id
;
const
avatar
=
player
?.
avatar
||
''
;
const
nickname
=
player
?.
nickname
||
'虚位以待'
;
const
isRealPlayer
=
nickname
&&
nickname
!==
'-'
&&
nickname
!==
'虚位以待'
;
const
wechat_id
=
hasId
||
(
isRealPlayer
&&
avatar
?
`avt:
${
avatar
}
`
:
`empty-
${
index
}
`
);
return
{
telephone
:
player
?.
telephone
??
''
,
telephone
:
player
?.
telephone
??
''
,
userid
:
player
?.
userid
??
''
,
userid
:
hasId
||
(
isRealPlayer
&&
avatar
?
`avt:
${
avatar
}
`
:
''
)
,
wechat_id
:
player
?.
wechat_id
||
`empty-
${
index
}
`
,
wechat_id
,
nickname
:
player
?.
nickname
||
'虚位以待'
,
nickname
,
avatar
:
player
?.
avatar
||
''
,
avatar
,
score
:
player
?.
score
??
'-'
,
score
:
player
?.
score
??
'-'
,
})
};
}
const
seedRankPlayersFromLoading
=
()
=>
{
const
seedRankPlayersFromLoading
=
()
=>
{
rankPlayers
.
value
=
Array
.
from
(
rankPlayers
.
value
=
Array
.
from
(
...
@@ -138,7 +147,11 @@ const gotoRank1WithIntro = async () => {
...
@@ -138,7 +147,11 @@ const gotoRank1WithIntro = async () => {
const
gameId
=
'game2'
const
gameId
=
'game2'
const
{
startGame
,
createRoom
,
backRoom
,
closeRoom
}
=
useAdminGameSocket
({
const
{
startGame
,
createRoom
,
backRoom
,
closeRoom
}
=
useAdminGameSocket
({
gameId
,
gameId
,
onAny
:
(
evt
,
payload
)
=>
{
console
.
log
(
"[Game2 WebSocket] 🔥 onAny 触发 - event:"
,
evt
,
"payload:"
,
JSON
.
stringify
(
payload
));
},
onRoomState
:
(
data
:
any
)
=>
{
onRoomState
:
(
data
:
any
)
=>
{
console
.
log
(
"[Game2] onRoomState 触发,data:"
,
JSON
.
stringify
(
data
));
if
(
Array
.
isArray
(
data
?.
list
))
{
if
(
Array
.
isArray
(
data
?.
list
))
{
renderPlayers
(
data
)
renderPlayers
(
data
)
}
}
...
@@ -154,7 +167,9 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
...
@@ -154,7 +167,9 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
gotoLoading
()
gotoLoading
()
break
break
case
2
:
case
2
:
console
.
log
(
"[Game2] status=2,准备进入 rank1,loadingPlayers:"
,
loadingPlayers
.
value
.
length
);
seedRankPlayersFromLoading
()
seedRankPlayersFromLoading
()
console
.
log
(
"[Game2] seedRankPlayersFromLoading 后,rankPlayers:"
,
rankPlayers
.
value
.
length
,
JSON
.
stringify
(
rankPlayers
.
value
.
slice
(
0
,
3
)));
screen
.
value
=
'rank1'
screen
.
value
=
'rank1'
break
break
case
3
:
case
3
:
...
@@ -163,11 +178,15 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
...
@@ -163,11 +178,15 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
}
}
},
},
onRoomJoin
:
(
data
)
=>
{
onRoomJoin
:
(
data
)
=>
{
console
.
log
(
"[Game2] onRoomJoin 触发,data:"
,
JSON
.
stringify
(
data
));
renderPlayers
(
data
)
renderPlayers
(
data
)
gotoLoading
()
gotoLoading
()
},
},
onGameStart
:
gotoRank1WithIntro
,
onGameStart
:
gotoRank1WithIntro
,
onRoomRank
:
updateRankPlayers
,
onRoomRank
:
(
data
)
=>
{
console
.
log
(
"[Game2] onRoomRank 触发,data:"
,
JSON
.
stringify
(
data
));
updateRankPlayers
(
data
);
},
onRelogin
:
async
()
=>
{
onRelogin
:
async
()
=>
{
$remove_socket_storage
();
$remove_socket_storage
();
await
router
.
replace
(
'/'
)
await
router
.
replace
(
'/'
)
...
...
frontend-large/src/pages/game2/views/Rank1View.vue
View file @
96495239
...
@@ -94,9 +94,18 @@ const preloadHorseSprites = () => {
...
@@ -94,9 +94,18 @@ const preloadHorseSprites = () => {
});
});
};
};
const
playerKey
=
(
player
:
Player
,
index
=
0
)
=>
(
const
playerKey
=
(
player
:
Player
,
index
=
0
)
=>
{
String
(
player
.
userid
||
player
.
wechat_id
||
player
.
telephone
||
`
${
player
.
nickname
}
-
${
index
}
`
)
// 优先使用 userid/wechat_id,没有则用 avatar 作为唯一标识
);
const
id
=
player
.
userid
||
player
.
wechat_id
||
player
.
telephone
;
if
(
id
&&
!
String
(
id
).
startsWith
(
'empty-'
))
return
String
(
id
);
// 如果有头像且昵称有效,用 avatar 作为唯一标识
if
(
player
.
avatar
&&
player
.
nickname
&&
player
.
nickname
!==
'-'
&&
player
.
nickname
!==
'虚位以待'
)
{
return
`avt:
${
player
.
avatar
}
`
;
}
return
`empty-
${
index
}
`
;
};
const
hashKey
=
(
key
:
string
)
=>
Array
.
from
(
key
).
reduce
(
const
hashKey
=
(
key
:
string
)
=>
Array
.
from
(
key
).
reduce
(
(
hash
,
character
)
=>
((
hash
<<
5
)
-
hash
+
character
.
charCodeAt
(
0
))
|
0
,
(
hash
,
character
)
=>
((
hash
<<
5
)
-
hash
+
character
.
charCodeAt
(
0
))
|
0
,
...
@@ -113,11 +122,23 @@ const createPaceProfile = (key: string) => {
...
@@ -113,11 +122,23 @@ const createPaceProfile = (key: string) => {
};
};
};
};
const
isRealPlayer
=
(
player
:
Player
)
=>
(
const
isRealPlayer
=
(
player
:
Player
)
=>
{
Boolean
(
player
)
if
(
!
player
)
return
false
;
&&
!
String
(
player
.
wechat_id
||
''
).
startsWith
(
'empty-'
)
&&
player
.
nickname
!==
'-'
// 检查 wechat_id 是否为空占位
);
if
(
String
(
player
.
wechat_id
||
''
).
startsWith
(
'empty-'
))
return
false
;
// 检查昵称是否有效
if
(
!
player
.
nickname
||
player
.
nickname
===
'-'
||
player
.
nickname
===
'虚位以待'
)
return
false
;
// 如果有 userid/wechat_id/telephone,认为是真实玩家
if
(
player
.
userid
||
player
.
wechat_id
||
player
.
telephone
)
return
true
;
// 如果有头像,也认为是真实玩家(WebSocket 数据可能没有 ID 字段)
if
(
player
.
avatar
)
return
true
;
return
false
;
};
const
targetXForRacer
=
(
racer
:
Racer
,
rankIndex
:
number
)
=>
{
const
targetXForRacer
=
(
racer
:
Racer
,
rankIndex
:
number
)
=>
{
...
...
frontend-large/src/pages/game3/Game3.vue
View file @
96495239
...
@@ -128,6 +128,7 @@ const gotoRank1WithIntro = async () => {
...
@@ -128,6 +128,7 @@ const gotoRank1WithIntro = async () => {
}
}
function
handleRoomState
(
data
:
any
)
{
function
handleRoomState
(
data
:
any
)
{
console
.
log
(
"[Game3] onRoomState 触发,data:"
,
JSON
.
stringify
(
data
));
if
(
Array
.
isArray
(
data
?.
list
))
{
if
(
Array
.
isArray
(
data
?.
list
))
{
renderPlayers
(
data
)
renderPlayers
(
data
)
}
}
...
@@ -141,6 +142,7 @@ function handleRoomState(data: any) {
...
@@ -141,6 +142,7 @@ function handleRoomState(data: any) {
gotoLoading
()
gotoLoading
()
break
break
case
2
:
case
2
:
console
.
log
(
"[Game3] status=2,准备进入 rank1,loadingPlayers:"
,
loadingPlayers
.
value
.
length
);
// 重连/中途进房时游戏已在运行,直接进入赛马画面
// 重连/中途进房时游戏已在运行,直接进入赛马画面
if
(
isPlayingStartAnimation
)
break
;
if
(
isPlayingStartAnimation
)
break
;
isPlayingStartAnimation
=
true
;
isPlayingStartAnimation
=
true
;
...
@@ -161,6 +163,8 @@ const gameId='game3'
...
@@ -161,6 +163,8 @@ const gameId='game3'
const
{
startGame
,
createRoom
,
backRoom
,
closeRoom
}
=
useAdminGameSocket
({
const
{
startGame
,
createRoom
,
backRoom
,
closeRoom
}
=
useAdminGameSocket
({
gameId
,
gameId
,
onAny
:
(
evt
,
payload
)
=>
{
onAny
:
(
evt
,
payload
)
=>
{
console
.
log
(
"[Game3 WebSocket] 🔥 onAny 触发 - event:"
,
evt
,
"payload:"
,
JSON
.
stringify
(
payload
));
// 兜底处理所有非标准事件,解析玩家实时分数(对齐 game4)
// 兜底处理所有非标准事件,解析玩家实时分数(对齐 game4)
const
data
=
payload
?.
data
??
payload
;
const
data
=
payload
?.
data
??
payload
;
...
...
frontend-large/src/pages/game3/views/Rank1View.vue
View file @
96495239
...
@@ -92,8 +92,10 @@ const emptyPlayer = (index: number): Player => ({
...
@@ -92,8 +92,10 @@ const emptyPlayer = (index: number): Player => ({
const
list
=
ref
<
Player
[]
>
(
Array
.
from
({
length
:
RANK_LIMIT
},
(
_
,
index
)
=>
emptyPlayer
(
index
)));
const
list
=
ref
<
Player
[]
>
(
Array
.
from
({
length
:
RANK_LIMIT
},
(
_
,
index
)
=>
emptyPlayer
(
index
)));
const
updateRankList
=
(
rankList
:
Player
[])
=>
{
const
updateRankList
=
(
rankList
:
Player
[])
=>
{
console
.
log
(
'[game3 Rank1View] updateRankList 被调用,rankList:'
,
rankList
?.
length
,
JSON
.
stringify
(
rankList
?.
slice
(
0
,
3
)));
const
next
=
Array
.
from
({
length
:
RANK_LIMIT
},
(
_
,
index
)
=>
rankList
[
index
]
??
emptyPlayer
(
index
));
const
next
=
Array
.
from
({
length
:
RANK_LIMIT
},
(
_
,
index
)
=>
rankList
[
index
]
??
emptyPlayer
(
index
));
list
.
value
=
next
;
list
.
value
=
next
;
console
.
log
(
'[game3 Rank1View] updateRankList 后,list:'
,
list
.
value
.
length
,
'有效玩家数:'
,
list
.
value
.
filter
(
p
=>
getPlayerKey
(
p
).
length
>
0
&&
!
getPlayerKey
(
p
).
startsWith
(
'empty-'
)).
length
);
};
};
// ========== 瞬时加速脉冲(对齐 game4 detectScoreChanges) ==========
// ========== 瞬时加速脉冲(对齐 game4 detectScoreChanges) ==========
...
...
frontend-large/src/pages/game4/Game4.vue
View file @
96495239
...
@@ -47,12 +47,14 @@ const confirmRefresh = (event: BeforeUnloadEvent) => {
...
@@ -47,12 +47,14 @@ const confirmRefresh = (event: BeforeUnloadEvent) => {
}
}
const
renderPlayers
=
(
data
:
any
)
=>
{
const
renderPlayers
=
(
data
:
any
)
=>
{
console
.
log
(
"[Game4] renderPlayers 被调用,data:"
,
JSON
.
stringify
(
data
));
const
players
=
Array
.
isArray
(
data
?.
list
)
?
data
.
list
:
[]
const
players
=
Array
.
isArray
(
data
?.
list
)
?
data
.
list
:
[]
playerCount
.
value
=
Number
(
data
?.
count
??
players
.
length
)
playerCount
.
value
=
Number
(
data
?.
count
??
players
.
length
)
loadingPlayers
.
value
=
Array
.
from
(
loadingPlayers
.
value
=
Array
.
from
(
{
length
:
PLAYER_LIMIT
},
{
length
:
PLAYER_LIMIT
},
(
_
,
index
)
=>
players
[
index
]
??
emptyLoadingPlayer
(),
(
_
,
index
)
=>
players
[
index
]
??
emptyLoadingPlayer
(),
)
)
console
.
log
(
"[Game4] renderPlayers 完成,loadingPlayers:"
,
loadingPlayers
.
value
.
length
,
"有效玩家:"
,
loadingPlayers
.
value
.
filter
(
p
=>
p
.
nickname
!==
'虚位以待'
).
map
(
p
=>
({
nickname
:
p
.
nickname
,
userid
:
p
.
userid
,
wechat_id
:
p
.
wechat_id
,
avatar
:
p
.
avatar
})));
}
}
const
updateRankPlayers
=
(
data
:
any
)
=>
{
const
updateRankPlayers
=
(
data
:
any
)
=>
{
...
@@ -64,28 +66,43 @@ const updateRankPlayers = (data: any) => {
...
@@ -64,28 +66,43 @@ const updateRankPlayers = (data: any) => {
)
)
}
}
const
playerToRankPlayer
=
(
player
:
any
,
index
:
number
):
Player
=>
({
const
playerToRankPlayer
=
(
player
:
any
,
index
:
number
):
Player
=>
{
// WebSocket 数据可能没有 userid/wechat_id,用 avatar 作为唯一标识
const
hasId
=
player
?.
userid
||
player
?.
wechat_id
||
player
?.
openid
||
player
?.
user_id
;
const
avatar
=
player
?.
avatar
||
''
;
const
nickname
=
player
?.
nickname
||
'虚位以待'
;
const
isRealPlayer
=
nickname
&&
nickname
!==
'-'
&&
nickname
!==
'虚位以待'
;
const
wechat_id
=
hasId
||
(
isRealPlayer
&&
avatar
?
`avt:
${
avatar
}
`
:
`empty-
${
index
}
`
);
return
{
telephone
:
player
?.
telephone
??
''
,
telephone
:
player
?.
telephone
??
''
,
userid
:
player
?.
userid
??
''
,
userid
:
hasId
||
(
isRealPlayer
&&
avatar
?
`avt:
${
avatar
}
`
:
''
)
,
wechat_id
:
player
?.
wechat_id
||
`empty-
${
index
}
`
,
wechat_id
,
nickname
:
player
?.
nickname
||
'虚位以待'
,
nickname
,
avatar
:
player
?.
avatar
||
''
,
avatar
,
score
:
player
?.
score
??
'-'
,
score
:
player
?.
score
??
'-'
,
})
};
}
const
seedRankPlayersFromLoading
=
()
=>
{
const
seedRankPlayersFromLoading
=
()
=>
{
console
.
log
(
"[Game4] seedRankPlayersFromLoading 开始,loadingPlayers:"
,
loadingPlayers
.
value
.
length
,
JSON
.
stringify
(
loadingPlayers
.
value
.
slice
(
0
,
3
)));
rankPlayers
.
value
=
Array
.
from
(
rankPlayers
.
value
=
Array
.
from
(
{
length
:
RANK_LIMIT
},
{
length
:
RANK_LIMIT
},
(
_
,
index
)
=>
{
(
_
,
index
)
=>
{
const
player
=
loadingPlayers
.
value
[
index
]
const
player
=
loadingPlayers
.
value
[
index
]
if
(
!
player
?.
userid
&&
!
player
?.
wechat_id
)
{
if
(
!
player
?.
userid
&&
!
player
?.
wechat_id
)
{
console
.
log
(
"[Game4] seedRankPlayersFromLoading index:"
,
index
,
"无 userid/wechat_id,判定为空占位"
);
return
emptyRankPlayer
(
index
)
return
emptyRankPlayer
(
index
)
}
}
console
.
log
(
"[Game4] seedRankPlayersFromLoading index:"
,
index
,
"有玩家数据:"
,
JSON
.
stringify
(
player
));
return
playerToRankPlayer
(
player
,
index
)
return
playerToRankPlayer
(
player
,
index
)
},
},
)
)
console
.
log
(
"[Game4] seedRankPlayersFromLoading 完成,rankPlayers:"
,
rankPlayers
.
value
.
length
,
JSON
.
stringify
(
rankPlayers
.
value
.
slice
(
0
,
3
)));
}
}
const
resetRoundState
=
()
=>
{
const
resetRoundState
=
()
=>
{
...
@@ -122,6 +139,8 @@ const gameId = 'game4'
...
@@ -122,6 +139,8 @@ const gameId = 'game4'
const
{
startGame
,
createRoom
,
backRoom
,
closeRoom
}
=
useAdminGameSocket
({
const
{
startGame
,
createRoom
,
backRoom
,
closeRoom
}
=
useAdminGameSocket
({
gameId
,
gameId
,
onAny
:
(
evt
,
payload
)
=>
{
onAny
:
(
evt
,
payload
)
=>
{
console
.
log
(
"[Game4 WebSocket] 🔥 onAny 触发 - event:"
,
evt
,
"payload:"
,
payload
);
const
data
=
payload
?.
data
??
payload
;
const
data
=
payload
?.
data
??
payload
;
// 调试日志:打印 WebSocket 原始数据
// 调试日志:打印 WebSocket 原始数据
...
@@ -133,7 +152,10 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
...
@@ -133,7 +152,10 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
rawPayload
:
payload
,
rawPayload
:
payload
,
});
});
if
(
!
Array
.
isArray
(
data
))
return
;
if
(
!
Array
.
isArray
(
data
))
{
console
.
log
(
"[Game4 WebSocket] ⚠️ data 不是数组,跳过处理"
);
return
;
}
const
boosts
:
Record
<
string
,
number
>
=
{};
const
boosts
:
Record
<
string
,
number
>
=
{};
let
hasBoost
=
false
;
let
hasBoost
=
false
;
...
@@ -158,6 +180,7 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
...
@@ -158,6 +180,7 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
}
}
},
},
onRoomState
:
(
data
:
any
)
=>
{
onRoomState
:
(
data
:
any
)
=>
{
console
.
log
(
"[Game4] onRoomState 触发,data:"
,
JSON
.
stringify
(
data
));
if
(
Array
.
isArray
(
data
?.
list
))
{
if
(
Array
.
isArray
(
data
?.
list
))
{
renderPlayers
(
data
)
renderPlayers
(
data
)
}
}
...
@@ -173,7 +196,9 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
...
@@ -173,7 +196,9 @@ const { startGame, createRoom, backRoom, closeRoom } = useAdminGameSocket({
gotoLoading
()
gotoLoading
()
break
break
case
2
:
case
2
:
console
.
log
(
"[Game4] status=2,准备进入 rank1,loadingPlayers:"
,
loadingPlayers
.
value
.
length
);
seedRankPlayersFromLoading
()
seedRankPlayersFromLoading
()
console
.
log
(
"[Game4] seedRankPlayersFromLoading 后,rankPlayers:"
,
rankPlayers
.
value
.
length
,
JSON
.
stringify
(
rankPlayers
.
value
.
slice
(
0
,
3
)));
screen
.
value
=
'rank1'
screen
.
value
=
'rank1'
break
break
case
3
:
case
3
:
...
...
frontend-large/src/pages/game4/views/Rank1View.vue
View file @
96495239
...
@@ -165,18 +165,34 @@ const gameStarted = ref(false);
...
@@ -165,18 +165,34 @@ const gameStarted = ref(false);
const
LANE_POSITIONS
=
[
0
,
108
,
228
,
338
,
448
];
const
LANE_POSITIONS
=
[
0
,
108
,
228
,
338
,
448
];
const
updateHorses
=
(
players
:
Player
[])
=>
{
const
updateHorses
=
(
players
:
Player
[])
=>
{
const
validPlayers
=
players
.
filter
(
console
.
log
(
"[Rank1View] updateHorses 被调用,players:"
,
players
?.
length
,
JSON
.
stringify
(
players
?.
slice
(
0
,
3
)));
(
p
)
=>
p
.
nickname
!==
"虚位以待"
&&
!
p
.
wechat_id
?.
startsWith
(
"empty-"
),
);
const
validPlayers
=
players
.
filter
((
p
)
=>
{
// 昵称必须是真实玩家
if
(
!
p
.
nickname
||
p
.
nickname
===
"虚位以待"
||
p
.
nickname
===
"-"
)
return
false
;
// wechat_id 不能是空占位
if
(
p
.
wechat_id
&&
String
(
p
.
wechat_id
).
startsWith
(
"empty-"
))
return
false
;
// 有 userid/wechat_id 或者有 avatar,都认为是真实玩家
if
(
p
.
userid
||
p
.
wechat_id
||
p
.
avatar
)
return
true
;
return
false
;
});
if
(
validPlayers
.
length
===
0
)
return
;
console
.
log
(
"[Rank1View] 有效玩家数:"
,
validPlayers
.
length
,
validPlayers
.
map
(
p
=>
({
nickname
:
p
.
nickname
,
userid
:
p
.
userid
,
wechat_id
:
p
.
wechat_id
,
avatar
:
p
.
avatar
})));
if
(
validPlayers
.
length
===
0
)
{
console
.
log
(
"[Rank1View] ⚠️ 无有效玩家,不生成马匹"
);
return
;
}
// 保留已存在的 xPercent 和 boost,避免新玩家列表覆盖进度
// 保留已存在的 xPercent 和 boost,避免新玩家列表覆盖进度
const
existingMap
=
new
Map
(
horses
.
value
.
map
((
h
)
=>
[
h
.
id
,
h
]));
const
existingMap
=
new
Map
(
horses
.
value
.
map
((
h
)
=>
[
h
.
id
,
h
]));
horses
.
value
=
validPlayers
.
map
((
player
,
index
)
=>
{
horses
.
value
=
validPlayers
.
map
((
player
,
index
)
=>
{
// WebSocket 数据无 userid/wechat_id,用 avatar 作为稳定标识
// WebSocket 数据无 userid/wechat_id,用 avatar 作为稳定标识
const
id
=
player
.
userid
||
player
.
wechat_id
||
player
.
avatar
||
`horse-
${
index
}
`
;
const
id
=
player
.
userid
||
player
.
wechat_id
||
(
player
.
avatar
?
`avt:
${
player
.
avatar
}
`
:
`horse-
${
index
}
`
)
;
const
existing
=
existingMap
.
get
(
id
);
const
existing
=
existingMap
.
get
(
id
);
const
horseType
=
HORSE_TYPES
[
index
%
HORSE_TYPES
.
length
]
!
;
const
horseType
=
HORSE_TYPES
[
index
%
HORSE_TYPES
.
length
]
!
;
return
{
return
{
...
@@ -192,6 +208,8 @@ const updateHorses = (players: Player[]) => {
...
@@ -192,6 +208,8 @@ const updateHorses = (players: Player[]) => {
frameTimer
:
existing
?.
frameTimer
??
0
,
frameTimer
:
existing
?.
frameTimer
??
0
,
};
};
});
});
console
.
log
(
"[Rank1View] ✅ 生成马匹数:"
,
horses
.
value
.
length
,
horses
.
value
.
map
(
h
=>
({
id
:
h
.
id
,
nickname
:
h
.
nickname
,
startY
:
h
.
startY
})));
};
};
const
mockAvatars
=
[
const
mockAvatars
=
[
...
@@ -221,6 +239,7 @@ const mockHorses = () => {
...
@@ -221,6 +239,7 @@ const mockHorses = () => {
watch
(
watch
(
()
=>
props
.
rankList
,
()
=>
props
.
rankList
,
(
players
)
=>
{
(
players
)
=>
{
console
.
log
(
"[Rank1View] watch rankList 触发,players:"
,
players
?.
length
,
JSON
.
stringify
(
players
?.
slice
(
0
,
2
)));
if
(
players
&&
players
.
length
>
0
)
{
if
(
players
&&
players
.
length
>
0
)
{
updateHorses
(
players
);
updateHorses
(
players
);
// 检测积分增长 → 触发瞬时加速脉冲
// 检测积分增长 → 触发瞬时加速脉冲
...
@@ -409,6 +428,9 @@ const stopGame = () => {
...
@@ -409,6 +428,9 @@ const stopGame = () => {
};
};
const
startIntro
=
async
()
=>
{
const
startIntro
=
async
()
=>
{
console
.
log
(
"[Rank1View] startIntro 开始,当前 rankList:"
,
props
.
rankList
?.
length
,
JSON
.
stringify
(
props
.
rankList
?.
slice
(
0
,
2
)));
console
.
log
(
"[Rank1View] 当前 horses 数量:"
,
horses
.
value
.
length
);
// 本地模拟已关闭,如需开启请取消以下注释
// 本地模拟已关闭,如需开启请取消以下注释
// if ($is_local_mode()) {
// if ($is_local_mode()) {
// mockHorses();
// mockHorses();
...
@@ -424,6 +446,8 @@ const startIntro = async () => {
...
@@ -424,6 +446,8 @@ const startIntro = async () => {
const
countdownPromise
=
designStage
.
value
?.
startCountdown
(
3
,
60
);
const
countdownPromise
=
designStage
.
value
?.
startCountdown
(
3
,
60
);
await
new
Promise
((
r
)
=>
setTimeout
(
r
,
3500
));
await
new
Promise
((
r
)
=>
setTimeout
(
r
,
3500
));
console
.
log
(
"[Rank1View] 倒计时结束,准备启动游戏,当前 horses:"
,
horses
.
value
.
length
,
horses
.
value
.
map
(
h
=>
h
.
nickname
));
gameStarted
.
value
=
true
;
gameStarted
.
value
=
true
;
isGameRunning
.
value
=
true
;
isGameRunning
.
value
=
true
;
...
...
frontend-large/src/pages/game5/Game5.vue
View file @
96495239
...
@@ -79,14 +79,23 @@ const updateRankPlayers = (data: any) => {
...
@@ -79,14 +79,23 @@ const updateRankPlayers = (data: any) => {
)
)
}
}
const
playerToRankPlayer
=
(
player
:
any
,
index
:
number
):
Player
=>
({
const
playerToRankPlayer
=
(
player
:
any
,
index
:
number
):
Player
=>
{
// WebSocket 数据可能没有 userid/wechat_id,用 avatar 作为唯一标识
const
hasId
=
player
?.
userid
||
player
?.
wechat_id
||
player
?.
openid
||
player
?.
user_id
;
const
avatar
=
player
?.
avatar
||
''
;
const
nickname
=
player
?.
nickname
||
'虚位以待'
;
const
isRealPlayer
=
nickname
&&
nickname
!==
'-'
&&
nickname
!==
'虚位以待'
;
const
wechat_id
=
hasId
||
(
isRealPlayer
&&
avatar
?
`avt:
${
avatar
}
`
:
`empty-
${
index
}
`
);
return
{
telephone
:
player
?.
telephone
??
''
,
telephone
:
player
?.
telephone
??
''
,
userid
:
player
?.
userid
??
''
,
userid
:
hasId
||
(
isRealPlayer
&&
avatar
?
`avt:
${
avatar
}
`
:
''
)
,
wechat_id
:
player
?.
wechat_id
||
`empty-
${
index
}
`
,
wechat_id
,
nickname
:
player
?.
nickname
||
'虚位以待'
,
nickname
,
avatar
:
player
?.
avatar
||
''
,
avatar
,
score
:
player
?.
score
??
'-'
,
score
:
player
?.
score
??
'-'
,
})
};
}
const
seedRankPlayersFromLoading
=
()
=>
{
const
seedRankPlayersFromLoading
=
()
=>
{
rankPlayers
.
value
=
Array
.
from
(
rankPlayers
.
value
=
Array
.
from
(
...
@@ -158,18 +167,21 @@ function handleRoomState(data: any) {
...
@@ -158,18 +167,21 @@ function handleRoomState(data: any) {
const
{
startGame
,
createRoom
,
closeRoom
,
backRoom
}
=
useAdminGameSocket
({
const
{
startGame
,
createRoom
,
closeRoom
,
backRoom
}
=
useAdminGameSocket
({
gameId
:
'game5'
,
gameId
:
'game5'
,
onAny
:
(
evt
,
payload
)
=>
{
console
.
log
(
"[Game5 WebSocket] 🔥 onAny 触发 - event:"
,
evt
,
"payload:"
,
JSON
.
stringify
(
payload
));
},
onRoomState
:
handleRoomState
,
onRoomState
:
handleRoomState
,
onRoomJoin
:
(
data
)
=>
{
onRoomJoin
:
(
data
)
=>
{
console
.
log
(
'[
WebSocket] 玩家加入房间
- 数据:'
,
JSON
.
stringify
(
data
))
console
.
log
(
'[
Game5] onRoomJoin 触发
- 数据:'
,
JSON
.
stringify
(
data
))
renderPlayers
(
data
)
renderPlayers
(
data
)
gotoLoading
()
gotoLoading
()
},
},
onGameStart
:
()
=>
{
onGameStart
:
()
=>
{
console
.
log
(
'[
WebSocket
] 游戏开始!'
)
console
.
log
(
'[
Game5
] 游戏开始!'
)
gotoRank1WithIntro
()
gotoRank1WithIntro
()
},
},
onRoomRank
:
(
data
)
=>
{
onRoomRank
:
(
data
)
=>
{
console
.
log
(
'[
WebSocket] 排名更新
- 数据:'
,
JSON
.
stringify
(
data
))
console
.
log
(
'[
Game5] onRoomRank 触发
- 数据:'
,
JSON
.
stringify
(
data
))
updateRankPlayers
(
data
)
updateRankPlayers
(
data
)
},
},
onRelogin
:
async
()
=>
{
onRelogin
:
async
()
=>
{
...
...
frontend-large/src/pages/game5/views/Rank1View.vue
View file @
96495239
...
@@ -75,8 +75,18 @@ const emptyPlayer = (index: number): Player => ({
...
@@ -75,8 +75,18 @@ const emptyPlayer = (index: number): Player => ({
const
list
=
ref
<
Player
[]
>
(
const
list
=
ref
<
Player
[]
>
(
Array
.
from
({
length
:
RANK_LIMIT
},
(
_
,
index
)
=>
emptyPlayer
(
index
)),
Array
.
from
({
length
:
RANK_LIMIT
},
(
_
,
index
)
=>
emptyPlayer
(
index
)),
);
);
const
playerKey
=
(
player
:
Player
,
index
=
0
)
=>
const
playerKey
=
(
player
:
Player
,
index
=
0
)
=>
{
player
.
userid
||
player
.
wechat_id
||
`empty-
${
index
}
`
;
// 优先使用 userid/wechat_id,没有则用 avatar 作为唯一标识
const
id
=
player
.
userid
||
player
.
wechat_id
;
if
(
id
&&
!
String
(
id
).
startsWith
(
'empty-'
))
return
String
(
id
);
// 如果有头像且昵称有效,用 avatar 作为唯一标识
if
(
player
.
avatar
&&
player
.
nickname
&&
player
.
nickname
!==
'-'
&&
player
.
nickname
!==
'虚位以待'
)
{
return
`avt:
${
player
.
avatar
}
`
;
}
return
`empty-
${
index
}
`
;
};
// ========== 排名更新 ==========
// ========== 排名更新 ==========
const
updateRankList
=
(
rankList
:
Player
[])
=>
{
const
updateRankList
=
(
rankList
:
Player
[])
=>
{
...
@@ -109,8 +119,9 @@ const updateRankList = (rankList: Player[]) => {
...
@@ -109,8 +119,9 @@ const updateRankList = (rankList: Player[]) => {
watch
(
watch
(
()
=>
props
.
rankList
,
()
=>
props
.
rankList
,
(
rankList
)
=>
{
(
rankList
)
=>
{
console
.
log
(
'[Game5 Rank1View] watch rankList 触发,rankList:'
,
rankList
?.
length
,
JSON
.
stringify
(
rankList
?.
slice
(
0
,
3
)));
if
(
rankList
&&
rankList
.
length
>
0
)
{
if
(
rankList
&&
rankList
.
length
>
0
)
{
console
.
log
(
'[
Rank1View] 排名列表更新 - 数据:'
,
JSON
.
stringify
(
rankList
.
map
(
p
=>
({
nickname
:
p
.
nickname
,
score
:
p
.
score
}))))
console
.
log
(
'[
Game5 Rank1View] 排名列表更新 - 数据:'
,
JSON
.
stringify
(
rankList
.
map
(
p
=>
({
nickname
:
p
.
nickname
,
score
:
p
.
score
,
userid
:
p
.
userid
,
wechat_id
:
p
.
wechat_id
}))))
updateRankList
(
rankList
);
updateRankList
(
rankList
);
}
}
...
...
frontend-large/src/pages/game6/views/Rank1View.vue
View file @
96495239
...
@@ -75,7 +75,18 @@ const emptyPlayer = (index: number): Player => ({
...
@@ -75,7 +75,18 @@ const emptyPlayer = (index: number): Player => ({
});
});
const
list
=
ref
<
Player
[]
>
(
Array
.
from
({
length
:
RANK_LIMIT
},
(
_
,
index
)
=>
emptyPlayer
(
index
)));
const
list
=
ref
<
Player
[]
>
(
Array
.
from
({
length
:
RANK_LIMIT
},
(
_
,
index
)
=>
emptyPlayer
(
index
)));
const
playerKey
=
(
player
:
Player
,
index
=
0
)
=>
player
.
userid
||
player
.
wechat_id
||
`empty-
${
index
}
`
;
const
playerKey
=
(
player
:
Player
,
index
=
0
)
=>
{
// 优先使用 userid/wechat_id,没有则用 avatar 作为唯一标识
const
id
=
player
.
userid
||
player
.
wechat_id
;
if
(
id
&&
!
String
(
id
).
startsWith
(
'empty-'
))
return
String
(
id
);
// 如果有头像且昵称有效,用 avatar 作为唯一标识
if
(
player
.
avatar
&&
player
.
nickname
&&
player
.
nickname
!==
'-'
&&
player
.
nickname
!==
'虚位以待'
)
{
return
`avt:
${
player
.
avatar
}
`
;
}
return
`empty-
${
index
}
`
;
};
const
mockRankStep
=
ref
(
0
);
const
mockRankStep
=
ref
(
0
);
type
MoleKind
=
"land"
|
"mole"
|
"rabbit"
;
type
MoleKind
=
"land"
|
"mole"
|
"rabbit"
;
type
MolePhase
=
"land"
|
"show"
|
"hide"
;
type
MolePhase
=
"land"
|
"show"
|
"hide"
;
...
...
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