游戏

游戏

21 人关注该话题

多个gatewayWorker实例间groupid能共享么 游戏

GatewayWorker walkor 回复了问题 • 2 人关注 • 1 个回复 • 83 次浏览 • 2018-04-21 16:00

游戏运行,遇到一个问题,不知道怎么解决 房间 游戏

Workerman Leon_ 回复了问题 • 4 人关注 • 4 个回复 • 419 次浏览 • 2018-03-10 21:14

每个房间怎么做各自的计时器 游戏 房间

Workerman muyu 回复了问题 • 2 人关注 • 1 个回复 • 241 次浏览 • 2017-12-07 18:25

打飞机定时发送坐标问题 游戏

Workerman muyu 回复了问题 • 9 人关注 • 5 个回复 • 2994 次浏览 • 2017-10-10 21:51

关于用GatewayWorker做一个游戏服务器的问题? 游戏

GatewayWorker stephen 回复了问题 • 2 人关注 • 1 个回复 • 646 次浏览 • 2017-01-02 17:49

关于尝试做一个棋牌类型服务器的一些问题。。 游戏 多进程 app

Workerman shanda0301 回复了问题 • 5 人关注 • 4 个回复 • 2101 次浏览 • 2015-11-05 18:24

关于workerman单进程的问题 游戏

Workerman ljfuyuan 回复了问题 • 3 人关注 • 2 个回复 • 765 次浏览 • 2015-10-16 13:31

关于MMORPG开发遇到的困惑 游戏

Workerman walkor 回复了问题 • 2 人关注 • 1 个回复 • 827 次浏览 • 2015-09-11 21:03

全局数据存储在哪里最合适? 游戏

Workerman walkor 回复了问题 • 3 人关注 • 1 个回复 • 909 次浏览 • 2015-09-07 21:04

关于计数器(Timer)的取消或注销问题 定时器 游戏

Workerman wytj0304 回复了问题 • 3 人关注 • 3 个回复 • 867 次浏览 • 2015-08-21 16:52

更多...
0

赞同来自:

**使用memcache不合理**
每个房间每1/60秒读一次memcache,那么1000个房间每秒读取memcache就要读取60000次memcache,单台memcache服务器是很难支持这么高的访问量的。而且这样的设计也不合理。

**基于Worke... 显示全部 »
**使用memcache不合理**
每个房间每1/60秒读一次memcache,那么1000个房间每秒读取memcache就要读取60000次memcache,单台memcache服务器是很难支持这么高的访问量的。而且这样的设计也不合理。

**基于Workerman做**
坐标从php内存变量里面读是最快的,实际上不推荐所有游戏都去用gatewayWorker,直接基于workerman来做更灵活,更稳定,更高性能。例如下面的demo,是直接设置/读取php变量,性能更高,更稳定,扩展性更好。
{{{
<?php
use Workerman\Worker;
use Workerman\Lib\Timer;
require_once './Workerman/Autoloader.php';
// 初始化一个worker容器
$worker = new Worker('Text://0.0.0.0:6001');
// 全局对象,保存当前进程内的房间数据,每个房间两个玩家(连接对象)
// 格式[room_id1=>[timer_id:xx, players:[connection1, connection2]], room_id2=>[timer_id:xx, players:[connection3, connection4], ..]
$rooms = array();
// 全局变量。保存当前的连接对象,方便在任意函数中获得当前连接对象
$current_connection = null;
// 固定为1
$worker->count = 1;
// tcp连接建立时,初始化坐标
$worker->onConnect = function($connection)
{
$connection->x = 0;
$connection->y = 0;
// 发送当前连接的id
$connection->send('{"type":"connection_id", "id":"'.$connection->id.'"}');
};
// 当有客户端发来消息时执行的回调函数
$worker->onMessage = function($connection, $data)
{
// 全局保存当前对象,方便给当前连接发送数据
global $current_connection;
$current_connection = $connection;
// 客户端传递的数据格式类似
// {"mod":"Room", "act":"join", "args":{"room_id":13}}
$data = json_decode($data, true);
$class = $data['mod'];
$method = $data['act'];
$callback = array($class, $method);
// 执行某个类的某个方法
if(is_callable($callback))
{
call_user_func_array($callback, $data['args']);
}
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[$room_id]['players'][$connection->id]);
// 如果有定时器,清理定时器
$timer_id = isset($rooms[$room_id]['timer_id']) ? $rooms[$room_id]['timer_id'] : 0;
if($timer_id)
{
Timer::del($timer_id);
unset($rooms[$connection->room_id]['timer_id']);
}
if(empty($rooms[$room_id]['players'])) {
unset($rooms[$room_id]);
}
// 广播退出事件
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);
// 房间已经满了
if($player_count === 2)
{
return $current_connection->send('{"type":"err", "msg":"room full"}');
}
// 加入房间
$rooms[$room_id]['players'][$current_connection->id] = $current_connection;
// 用一个临时属性room_id存储当前连接的房间号
$current_connection->room_id = $room_id;
// 已经两人,开始战斗
if($player_count+1 === 2)
{
// 发个包给客户端,开始战斗
self::broadcastBeginFight($room_id);
// 建立一个定时器发送当前房间($room_id)玩家的坐标
$rooms[$room_id]['timer_id'] = Timer::add(1, 'Room::broadcastLocation', array($room_id));
var_export($rooms[$room_id]['timer_id']);
}

}
// 广播开始战斗
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[$room_id]['players']))
{
return array();
}
$connections = $rooms[$room_id]['players'];
return $connections;
}

}
}}}

**以上代码亲测ok**

**多进程**
上面的demo是单进程的,并且只能设置成单进程,目的是为了让同一个房间的用户都在一个进程里面,方便共享坐标等数据。

多进程的方法就是启动多个上面的实例,每个实例一个端口,客户端根据需要选择连哪个服务器。
例如
1、每个实例作为一个区,每个区多个房间。用户选择某个区的某个房间进入
2、也可以把端口号+roomid作为房间号。可以根据房间号得到端口号和实际的room_id

**分布式部署**
由于每个实例都是独立的,完全可以部署在不同的服务器上,组成一个集群。
房间号规则可以为 ip+port+room_id

**扩展阅读**
单个进程内如果有阻塞操作,比如读数据库/redis等存储,会导致进程阻塞,解决方法是建议一些任务进程,处理阻塞任务,然后异步通知游戏进程
参考 http://wenda.workerman.net/?/question/358
更多...