feat: WS sends selected_game_id on connect, gear indicator auto-adapts to game mapping (Forza/AC/iRacing)
This commit is contained in:
@@ -84,7 +84,12 @@ async def scene_render(request: Request, scene_id: str):
|
||||
async def websocket_endpoint(ws: WebSocket):
|
||||
cid = await ws_manager.connect(ws)
|
||||
try:
|
||||
await ws.send_json({"type": "connected", "client_id": cid})
|
||||
cfg = get_config()
|
||||
await ws.send_json({
|
||||
"type": "connected",
|
||||
"client_id": cid,
|
||||
"selected_game_id": cfg.get("selected_game_id"),
|
||||
})
|
||||
while True:
|
||||
msg = await ws.receive_json()
|
||||
if msg.get("type") == "ping":
|
||||
|
||||
@@ -46,6 +46,60 @@ html,body{width:100%;height:100%;overflow:hidden;background:#000;
|
||||
</div></div></div>
|
||||
<script>
|
||||
const M={aspect_ratio:"1:1",render_mode:"contain",gear_style:"auto"};
|
||||
let GAME_ID='';
|
||||
|
||||
// Forza: 0=R, 1-8=1-8 (no neutral in UDP)
|
||||
// AC/F1: -1=R, 0=N, 1-8=1-8
|
||||
// iRacing: 0=N, 1=R, 2=1st... (offset +2)
|
||||
function gearLabel(g){
|
||||
if(!g&&g!==0)return'-';
|
||||
const style=M.gear_style||'auto';
|
||||
const isForza=GAME_ID&&(GAME_ID.includes('forza'));
|
||||
const isIRacing=GAME_ID&&GAME_ID.includes('iracing');
|
||||
|
||||
if(style==='forza'||isForza){
|
||||
if(g===0)return'R';
|
||||
if(g>=1&&g<=8)return String(g);
|
||||
return String(g);
|
||||
}
|
||||
if(style==='ac'||(!isForza&&!isIRacing)){
|
||||
if(g===-1)return'R';
|
||||
if(g===0)return'N';
|
||||
if(g>=1&&g<=8)return String(g);
|
||||
return String(g);
|
||||
}
|
||||
if(isIRacing){
|
||||
if(g===0)return'N';
|
||||
if(g===1)return'R';
|
||||
if(g>=2&&g<=9)return String(g-1);
|
||||
return String(g);
|
||||
}
|
||||
// fallback
|
||||
if(g===0)return'N';
|
||||
if(g>0)return String(g);
|
||||
return String(g);
|
||||
}
|
||||
|
||||
function gearBarIndex(g){
|
||||
if(!g&&g!==0)return-1;
|
||||
const isForza=GAME_ID&&GAME_ID.includes('forza');
|
||||
const isIRacing=GAME_ID&&GAME_ID.includes('iracing');
|
||||
|
||||
if(isForza){
|
||||
if(g===0)return 0; // R at index 0
|
||||
if(g>=1&&g<=8)return g+1; // 1->2
|
||||
}
|
||||
if(isIRacing){
|
||||
if(g===0)return 1; // N at index 1
|
||||
if(g===1)return 0; // R at index 0
|
||||
if(g>=2&&g<=9)return g; // 2->2(index for '1')
|
||||
}
|
||||
// AC/F1/default
|
||||
if(g===-1)return 0; // R
|
||||
if(g===0)return 1; // N
|
||||
if(g>=1&&g<=8)return g+1; // 1->2
|
||||
return -1;
|
||||
}
|
||||
(function(){
|
||||
const box=document.getElementById('box'),gearTxt=document.getElementById('gear-txt'),
|
||||
gearEl=document.getElementById('gear-el'),barRow=document.getElementById('bar-row');
|
||||
@@ -91,21 +145,8 @@ const M={aspect_ratio:"1:1",render_mode:"contain",gear_style:"auto"};
|
||||
|
||||
function updateGear(g){
|
||||
if(g===lastGear)return;lastGear=g;
|
||||
|
||||
// Determine mapping based on actual gear value
|
||||
let label,activeIndex;
|
||||
if(g<0){// AC style: negative is reverse
|
||||
label='R';activeIndex=0;
|
||||
}else if(g===0){
|
||||
// ambiguous: could be R (Forza) or N (AC)
|
||||
// Show R by default for Forza, but check gear_style
|
||||
label='R';activeIndex=0;
|
||||
}else{
|
||||
// Forward gear: g is 1-based in Forza, but some games might use 0-based
|
||||
label=String(g);
|
||||
if(g>=1&&g<=8)activeIndex=g+1; // gear 1 -> index 2
|
||||
else activeIndex=-1;
|
||||
}
|
||||
const label=gearLabel(g);
|
||||
const activeIndex=gearBarIndex(g);
|
||||
|
||||
gearTxt.textContent=label;
|
||||
gearEl.className='gear-circle active';
|
||||
@@ -132,7 +173,9 @@ const M={aspect_ratio:"1:1",render_mode:"contain",gear_style:"auto"};
|
||||
|
||||
function connect(){
|
||||
ws=new WebSocket(wsUrl);ws.onopen=()=>{if(rt){clearTimeout(rt);rt=null}};
|
||||
ws.onmessage=(e)=>{try{const m=JSON.parse(e.data);if(m.type==='telemetry')updateGear(m.data.gear||0)}catch(e){}};
|
||||
ws.onmessage=(e)=>{try{const m=JSON.parse(e.data);
|
||||
if(m.type==='connected'){GAME_ID=m.selected_game_id||'';console.log('[Gear] game:',GAME_ID)}
|
||||
if(m.type==='telemetry')updateGear(m.data.gear||0)}catch(e){}};
|
||||
ws.onclose=()=>{rt=setTimeout(connect,2000)};
|
||||
}
|
||||
|
||||
|
||||
@@ -136,7 +136,8 @@ const PageDashboard = {
|
||||
},
|
||||
|
||||
_openDashboard(themeId) {
|
||||
const url = `${location.origin}/dashboard/${themeId}`;
|
||||
const gameParam = this._selectedGameId ? `?game=${this._selectedGameId}` : '';
|
||||
const url = `${location.origin}/dashboard/${themeId}${gameParam}`;
|
||||
window.open(url, '_blank');
|
||||
navigator.clipboard.writeText(url).then(() => {
|
||||
Toast.show('链接已复制,可在局域网设备访问', 'success');
|
||||
|
||||
Reference in New Issue
Block a user