一、首先呢, 让我们的客户端发起请求
ws = new WebSocket("ws://192.168.199.204:7000/ws/101/"+
xxtea.encode(JSON.stringify({openid:Global.openid,third_key:Global.third_key}))
);
这时服务器接收这个连接请求
# 目录: app/channels/application_cable/connection.rb
def connect
self.current_user = valid_myid
end
valid_myid方法主要做下面几件事
1. 解密获取openid 和 third_key
2. 验证用户是否存在, 若存在发送welcome, 不存在拒绝连接
此时连接正常建立了.
二、订阅
接下来客户端会收到welcome消息, 然后向服务器发送订阅请求
ws.onmessage = function (event) {
const d = JSON.parse(event.data);
switch (d.type) {
case "welcome" :
// 发送订阅请求, 请求订阅WxDsqChannel这个频道
ws.send(wsFunction.cmd_subscribe());
break;
//其他case.....
}
}
服务器接收订阅请求
# 目录: app/channels/wx_dsq_channel.rb
class WxDsqChannel < WxGameCable::Channel
def subscribed
connection.gtype = GTYPE
_subscribed # 这个是父类的方法
end
end
# 目录 : app/channels/application_cable/wx_game_cable.rb
module WxGameCable
class Channel < ActionCable::Channel::Base
def _subscribed
# 给一条沟通的道路
stream_from("ps_#{gtype}_#{current_user.id}")
# 将本人信息存到redis中
WxDB.set_user_info(pkey,current_user)
end
end
end
订阅成功后, 服务器会发送confirm_subscription消息
ws.onmessage = function (event) {
const d = JSON.parse(event.data);
switch (d.type) {
case "confirm_subscription" :
// 订阅之后,发送start命令
ws.send(wsFunction.cmd_message("start",""));
break;
}
}
三、 匹配
# 目录: app/channels/wx_dsq_channel.rb
def start
_matching # 还是调用父类方法
end
# 目录 : app/channels/application_cable/wx_game_cable.rb
def _matching
opponent = WxDB.get_opponent(gtype)
if opponent == 0
#队列中无人,将自己加入队列中,创建房间
WxDB.add_opponent(gtype,cu.id)
room = WxDB.create_room(cu.id,gtype)
if room
WxDB.set_user_room_id(pkey,room['r_id'])
end
else
r_id = WxDB.get_user_room_id(pkey(opponent)) # 获取对手的房间号
room_key = rkey(r_id)
# 将房间设置成匹配成功状态
room = _set_room_to_matched(room_key, cu.id)
if room
m_uid, s_uid = room['m_uid'].to_i, room['s_uid'].to_i
WxDB.set_user_roomid_and_camp(pkey(m_uid),room['r_id'],B_BLUE)
WxDB.set_user_roomid_and_camp(pkey(s_uid),room['r_id'],B_RED)
players = {:m_user => WxDB.get_user_info(pkey(m_uid)), :s_user => WxDB.get_user_info(pkey(s_uid)) }
hash = {cmd: 'matched',players:players,ts: Time.mts}.merge(room)
# 发送匹配成功命令,将双方的信息和房间信息下发至客户端
WxJob.perform_later Cmd.delay_time_run,"ps_#{gtype}_#{m_uid}", hash
WxJob.perform_later Cmd.delay_time_run,"ps_#{gtype}_#{s_uid}", hash
end
end
end
接下来就是客户端接收匹配成功matched命令
ws.onmessage = function (event) {
const d = JSON.parse(event.data);
switch (d.type) {
case "welcome" :
...;
case "confirm_subscription" :
...;
case "ping" :
...;
default :
switch (d.message.cmd) {
case "matched" :
self.do_matched(d);// 绘制头像, 昵称 等等操作
ws.send(wsFunction.cmd_message("ready",""));// 发送准备命令
break;
}
}
}
四、准备
# 目录: app/channels/wx_dsq_channel.rb
def ready(msg)
_ready(msg)
end
# 目录 : app/channels/application_cable/wx_game_cable.rb
def _ready(msg)
room = _my_room_info
room_key = rkey(room['r_id'])
if room['status'].to_i == R_MATCH_SUCC
stream_from(room_key)
num = $wxgame.hincrby(room_key, "readied_num", 1)
# 都准备好了
if num == 2
_set_room_to_gaming(room_key)
# 发送开始命令
WxJob.perform_later Cmd.delay_time_run,room_key, {cmd: "started", c_camp: room['c_camp']}
check_time(room_key,room) # 检测超时时间, 由子类来实现
end
end
end
然后游戏就开始了...
接下来需要TODO的还有游戏逻辑,超时判断,结束游戏,再来一局,发起认输等等....
那么问题来了, 如何新建一个见缝插针channel 或 扫雷channel呢?
五、其他...
需要注意的一点:
- 发送消息要使用 Job.perform_later, 源码中有Provides behavior for enqueuing and retrying jobs.的提示, 发送消息会有丢失的情况, 可以用Job的 retry功能.
require 'active_job/arguments'
module ActiveJob
# Provides behavior for enqueuing and retrying jobs. 注意这里
module Enqueuing
extend ActiveSupport::Concern
# Includes the +perform_later+ method for job initialization.
module ClassMethods
# Push a job onto the queue. The arguments must be legal JSON types
# (string, int, float, nil, true, false, hash or array) or
# GlobalID::Identification instances. Arbitrary Ruby objects
# are not supported.
#
# Returns an instance of the job class queued with arguments available in
# Job#arguments.
def perform_later(*args)
job_or_instantiate(*args).enqueue
end
protected
def job_or_instantiate(*args)
args.first.is_a?(self) ? args.first : new(*args)
end
end
# ...
def retry_job(options={})
enqueue options
end
参考资料:
rails 参考资料: https://ruby-china.github.io/rails-guides/v5.0/action_cable_overview.html
redis命令参考官网文档:https://redis.io/commands
cocos creator网络文档: http://docs.cocos.com/creator/manual/zh/scripting/network.html