国产+高潮+在线,国产 av 仑乱内谢,www国产亚洲精品久久,51国产偷自视频区视频,成人午夜精品网站在线观看

實時戰(zhàn)斗游戲打飛機定時發(fā)送坐標問題

ketle

需求:
一個手機打飛機游戲,一個房間2個飛機,然后各自控制飛機位置,子彈自動發(fā)射,看誰先掛;

本來是下載了win版聊天室框架GatewayWorker來搞,正好都合適 ,有房間,有各種存儲,搞起來很順;
本來的流程是某房間 a飛機位置發(fā)生變化->服務(wù)器 服務(wù)器同時下發(fā)給a,b a飛機的位置;
現(xiàn)在因為這樣的方式不太好,比如延時,比如碰撞等都會產(chǎn)生問題;
所以改成:

a飛機位置發(fā)生變化->服務(wù)器 ,存儲位置
服務(wù)器定時(1/60 s)下發(fā)給a,b 各自的最新位置;
這樣的話碰撞什么的都可以在服務(wù)端計算,....

然后我現(xiàn)在的解決辦法(都是在群里各位大大給的,感謝)

a飛機位置發(fā)生變化->服務(wù)器 ,存儲位置到memcache;
新開一個worker去定時讀取memcache的內(nèi)容,凡是房間內(nèi)有2個人的,下發(fā)位置給他們;
相關(guān)代碼:

use \Workerman\Worker; 
use \GatewayWorker\Lib\Gateway;
use \GatewayWorker\Lib\Store; 
use \Workerman\Autoloader;
// 自動加載類
require_once __DIR__ . '/../../Workerman/Autoloader.php';
Autoloader::setRootPath(__DIR__);

$task = new Worker();
// 開啟多少個進程運行定時任務(wù),注意多進程并發(fā)問題
$task->count = 1;
$task->onWorkerStart = function($task)
{

    $time_interval = 0.0166;    
    $time_interval = 2.5;
    \Workerman\Lib\Timer::add($time_interval, function()
    {
        echo "task run\n";
        //Gateway:: 

        //Gateway::sendToClient(1, '3333');

        $all_room_key = "ALL_ROOM";
        $room_key_pre = "ROOM_";

        $store = Store::instance('room'); 
        $all_room = $store->get($all_room_key);

        if ( $all_room ) 
        {
            foreach ( $all_room as $kk => $vv )
            {
                $room_id = $kk;
                $room_info = $store->get($room_key_pre.$room_id);
                if ( count($room_info) > 1 ) 
                {
                    //{"d":{"y":68,"x":477,"uid":7,"timestamp":54.353},"cmd":"updatePosition"}
                    $time = explode ( " ", microtime () );
                    $timestamp = $time +$time ; 
                    foreach ( $room_info as $kk2 => $vv2 )
                    {                       
                        Gateway::sendToClient($kk, '{"d":{"y":'.$vv2.',"x":'.$vv2.',"uid":'.$vv2.',"timestamp":'.$timestamp.'},"cmd":"updatePosition"}');
                    }
                }
            }
        }

    });
};

然后這樣的話因為不是php內(nèi)存直接處理, 如果客戶端連接多,memcache壓力估計非常大,求各位大大...

10283 5 10
5個回答

walkor 打賞

使用memcache不合理
每個房間每1/60秒讀一次memcache,那么1000個房間每秒讀取memcache就要讀取60000次memcache,單臺memcache服務(wù)器是很難支持這么高的訪問量的。而且這樣的設(shè)計也不合理。

基于Workerman做
坐標從php內(nèi)存變量里面讀是最快的,實際上不推薦所有游戲都去用gatewayWorker,直接基于workerman來做更靈活,更穩(wěn)定,更高性能。例如下面的demo,是直接設(shè)置/讀取php變量,性能更高,更穩(wěn)定,擴展性更好。

<?php
use Workerman\Worker;
use Workerman\Lib\Timer;
require_once './Workerman/Autoloader.php';
// 初始化一個worker容器
$worker = new Worker('Text://0.0.0.0:6001');
// 全局對象,保存當前進程內(nèi)的房間數(shù)據(jù),每個房間兩個玩家(連接對象)
// 格式[room_id1=>, room_id2=>
$rooms = array();
// 全局變量。保存當前的連接對象,方便在任意函數(shù)中獲得當前連接對象
$current_connection = null;
// 固定為1
$worker->count = 1;
// tcp連接建立時,初始化坐標
$worker->onConnect = function($connection)
{
    $connection->x = 0;
    $connection->y = 0;
    // 發(fā)送當前連接的id
    $connection->send('{"type":"connection_id", "id":"'.$connection->id.'"}');
};
// 當有客戶端發(fā)來消息時執(zhí)行的回調(diào)函數(shù)
$worker->onMessage = function($connection, $data)
{
    // 全局保存當前對象,方便給當前連接發(fā)送數(shù)據(jù)
    global $current_connection;
    $current_connection = $connection;
    // 客戶端傳遞的數(shù)據(jù)格式類似 
    // {"mod":"Room", "act":"join", "args":{"room_id":13}}
    $data = json_decode($data, true);
    $class = $data;
    $method = $data;
    $callback = array($class, $method);
    // 執(zhí)行某個類的某個方法
    if(is_callable($callback))
    {
        call_user_func_array($callback, $data);
    }
    else
    {
        $connection->send('{"type":"err", "msg":"invalid packet"}');
    }
};
// 當有客戶端連接斷開時
$worker->onClose = function($connection)
{
    global $rooms;
    $room_id = isset($connection->room_id) ? $connection->room_id : null;
    // 還沒加入房間
    if($room_id === null)
    {
        return;
    }
    // 清理房間信息
    unset($rooms);
    // 如果有定時器,清理定時器
    $timer_id = isset($rooms) ? $rooms : 0;
    if($timer_id)
    {
        Timer::del($timer_id);
        unset($rooms);
    }
    if(empty($rooms)) {
        unset($rooms);
    }
    // 廣播退出事件
    Room::broadcast($room_id, array("type"=>"logout", "id"=>$connection->id));
};
// 運行所有的worker(其實當前只定義了一個)
Worker::runAll();

// 坐標類
class Location
{
    public static function update($x, $y)
    {
        global $current_connection;
        $current_connection->x = $x;
        $current_connection->y = $y;
    }
}

// 房間類
class Room
{
    // 加入房間
    // {"mod":"Room", "act":"join", "args":{"room_id":13}}
    public static function join($room_id)
    {
        global $rooms, $current_connection;
        $players = self::getPlayers($room_id);
        $player_count = count($players);
        // 房間已經(jīng)滿了
        if($player_count === 2)
        {
            return $current_connection->send('{"type":"err", "msg":"room full"}');
        }
        // 加入房間
        $rooms = $current_connection;
        // 用一個臨時屬性room_id存儲當前連接的房間號
        $current_connection->room_id = $room_id;
        // 已經(jīng)兩人,開始戰(zhàn)斗
        if($player_count+1 === 2)
        {
            // 發(fā)個包給客戶端,開始戰(zhàn)斗
            self::broadcastBeginFight($room_id);
            // 建立一個定時器發(fā)送當前房間($room_id)玩家的坐標
            $rooms = Timer::add(1, 'Room::broadcastLocation', array($room_id));
            var_export($rooms);
        }

    }
    // 廣播開始戰(zhàn)斗
    public static function broadcastBeginFight($room_id)
    {
        $data = array(
            'type' => 'begin_fight'
        );
        self::broadcast($room_id, $data);
    }
    // 廣播坐標
    public static function broadcastLocation($room_id)
    {
        list($player1_connection, $player2_connection) = array_values(self::getPlayers($room_id));
        $location_data = array(
            'type' => 'location',
            'data' => array(
                array($player1_connection->id, $player1_connection->x, $player1_connection->y),
                array($player2_connection->id, $player2_connection->x, $player2_connection->y)
            )
        );
        self::broadcast($room_id, $location_data);
    }
    // 向某個room廣播
    public static function broadcast($room_id, array $data)
    {
        $data_buffer = json_encode($data);
        foreach(self::getPlayers($room_id) as $connection)
        {
            $connection->send($data_buffer);
        }
    }
    // 獲得某個room的玩家連接對象
    protected static function getPlayers($room_id)
    {
        global $rooms;
        if(!isset($rooms))
        {
            return array();
        }
        $connections = $rooms;
        return $connections;
    }

}

以上代碼親測ok

多進程
上面的demo是單進程的,并且只能設(shè)置成單進程,目的是為了讓同一個房間的用戶都在一個進程里面,方便共享坐標等數(shù)據(jù)。

多進程的方法就是啟動多個上面的實例,每個實例一個端口,客戶端根據(jù)需要選擇連哪個服務(wù)器。
例如
1、每個實例作為一個區(qū),每個區(qū)多個房間。用戶選擇某個區(qū)的某個房間進入
2、也可以把端口號+roomid作為房間號。可以根據(jù)房間號得到端口號和實際的room_id

分布式部署
由于每個實例都是獨立的,完全可以部署在不同的服務(wù)器上,組成一個集群。
房間號規(guī)則可以為 ip+port+room_id

擴展閱讀
單個進程內(nèi)如果有阻塞操作,比如讀數(shù)據(jù)庫/redis等存儲,會導(dǎo)致進程阻塞,解決方法是建議一些任務(wù)進程,處理阻塞任務(wù),然后異步通知游戲進程
參考 http://wenda.workerman.net/?/question/358

  • 暫無評論
walkor 打賞

上面demo測試方法
demo使用的是Text協(xié)議,可以改成其它協(xié)議如websocket。
Text協(xié)議測試方法:

telnet 127.0.0.1 6001
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
{"type":"connection_id", "id":"2"}
{"mod":"Room", "act":"join", "args":{"room_id":13}}
{"type":"begin_fight"}
{"type":"location","data":}
{"type":"location","data":}
{"mod":"Location", "act":"update", "args":{"x":13, "y":56}}
{"type":"location","data":}
{"type":"location","data":}
{"type":"location","data":}
{"type":"location","data":}
{"type":"location","data":}
{"type":"location","data":}
{"type":"location","data":}
{"type":"location","data":}

說明:
加入房間的包
{"mod":"Room", "act":"join", "args":{"room_id":13}}
更新坐標的包
{"mod":"Location", "act":"update", "args":{"x":13, "y":56}}

  • 暫無評論
ketle

謝謝 walkor 等下我試試

  • 暫無評論
wk617070321

多房間棋牌游戲好像也可以這樣干

  • 暫無評論
muyu

舉報:加入房間下標是palyers,刪除客戶端下表是players,到時導(dǎo)致運行時刪不掉用戶會一直在!

  • walkor 2017-10-10

    已經(jīng)更正,感謝你的舉報

年代過于久遠,無法發(fā)表回答
??