php方案 PHP 实现帧同步服务器 - 类王者荣耀的确定性帧同步逻辑(Lockstep)
直接说实话PHP不适合做帧同步服务器原因是PHP传统模式每次请求都重启没有常驻内存。 但用 Swoole 可以让PHP常驻内存完全可以做。---安装 composerrequireswoole/ide-helper# IDE提示# Swoole 需要编译安装或用Dockerdocker run--rm-p9501:9501phpswoole/swoole php server.php---完整帧同步服务器?php// server.phpuseSwoole\WebSocket\Server;useSwoole\WebSocket\Frame;useSwoole\Timer;$servernewServer(0.0.0.0,9501);// 全局游戏状态 $rooms[];// 房间列表$server-on(open,function(Server$server,$req)use($rooms){$fd$req-fd;$room$req-get[room]??default;$pid$req-get[pid]??$fd;// 玩家ID$rooms[$room][players][$fd][pid$pid,inputs[],// 该玩家缓存的输入readyfalse,];echo玩家$pid加入房间$room\n;// 人满开始2人房if(count($rooms[$room][players])2){startRoom($server,$room,$rooms);}});$server-on(message,function(Server$server,Frame$frame)use($rooms){$datajson_decode($frame-data,true);$fd$frame-fd;// 客户端上报本帧输入if($data[type]input){foreach($roomsas$room$state){if(isset($state[players][$fd])){// 缓存输入到对应帧$state[players][$fd][inputs][$data[frame]]$data[input];break;}}}});$server-on(close,function(Server$server,int$fd)use($rooms){foreach($roomsas$room$state){if(isset($state[players][$fd])){unset($state[players][$fd]);// 通知其他人broadcast($server,$state[players],json_encode([typeplayer_leave,fd$fd,]));}}});// 核心启动帧循环 functionstartRoom(Server$server,string$room,array$rooms):void{$rooms[$room][frame]0;$rooms[$room][running]true;$rooms[$room][startTime]microtime(true);broadcast($server,$rooms[$room][players],json_encode([typegame_start]));// 每66ms推进一帧15帧/秒王者是15帧Timer::tick(66,function(int$timerId)use($server,$room,$rooms){if(!($rooms[$room][running]??false)){Timer::clear($timerId);return;}$state$rooms[$room];$currentFrame$state[frame];// 收集所有玩家本帧输入没上报的用空输入$frameInputs[];foreach($state[players]as$fd$player){$frameInputs[][pid$player[pid],input$player[inputs][$currentFrame]??[x0,y0,skill0],];unset($state[players][$fd][inputs][$currentFrame]);// 清理已处理输入}// 广播本帧所有输入给所有客户端客户端自己跑逻辑broadcast($server,$state[players],json_encode([typeframe,frame$currentFrame,inputs$frameInputs,]));$state[frame];});}functionbroadcast(Server$server,array$players,string$msg):void{foreach($playersas$fd$_){if($server-isEstablished($fd)){$server-push($fd,$msg);}}}echo帧同步服务器启动: ws://0.0.0.0:9501\n;$server-start();---客户端浏览器测试scriptconstwsnewWebSocket(ws://localhost:9501?roomroom1pidplayer1);let localFrame0;// 每66ms上报本地输入setInterval((){ws.send(JSON.stringify({type:input,frame:localFrame,input:{x:Math.random()0.5?1:0,y:0,skill:0}}));localFrame;},66);ws.onmessage(e){constdataJSON.parse(e.data);if(data.typeframe){// 用服务器下发的输入跑本地逻辑所有客户端跑同样的逻辑 同步console.log(第${data.frame}帧,data.inputs);runGameLogic(data.frame,data.inputs);// 你的游戏逻辑}};functionrunGameLogic(frame,inputs){// 所有客户端拿到同样的inputs跑同样的确定性逻辑// 结果必然一致这就是帧同步的核心}/script---大白话解释 状态同步吃鸡/LOL 帧同步王者荣耀 服务器跑游戏逻辑 客户端跑游戏逻辑 把结果发给客户端 服务器只转发操作服务器压力大 服务器压力小 作弊难 作弊容易需校验 帧同步流程 第1帧 玩家A上报向右走 玩家B上报放技能 ↓ 服务器收集齐了 广播给所有人[A向右走,B放技能]↓ 所有客户端 各自用同样输入跑逻辑 → 结果完全一样 关键确定性 同样的输入同样的初始状态同样的结果 所以不能用random()要用种子随机数 不能用float直接比较要用定点数---防作弊生产必备// 客户端定期上报游戏状态哈希if($data[type]checksum){$state[checksums][$currentFrame][$fd]$data[hash];// 所有人都上报了比对if(count($state[checksums][$currentFrame])count($state[players])){$hashesarray_unique($state[checksums][$currentFrame]);if(count($hashes)1){broadcast($server,$state[players],json_encode([typedesync]));// 触发重新同步或踢出作弊者}}}---
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2416169.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!