|
本節(jié)思路:
cmd命令按照如下流程: 當客戶端點擊準備按鈕,發(fā)送ready(client->server)命令到服務(wù)端,服務(wù)端更新該用戶的ready標識位(存于redis),計算房間人數(shù)是否已滿,以及房間所有用戶是否都已設(shè)置ready標識位,如是,則發(fā)送start(server->client)命令給所有用戶,開始游戲。 客戶端選擇好自己的出拳信息,發(fā)送guess(client->server)命令到服務(wù)端,服務(wù)端記錄該用戶出拳數(shù)據(jù),判斷房間內(nèi)所有人都以提交guess命令,則計算最后結(jié)果,發(fā)送result(server->client)命令給所有用戶。 客戶端接收到result命令后,重新進入ready流程。 如退出小程序,客戶端發(fā)送logout(client->server)命令到服務(wù)端,服務(wù)端從列表中刪除該用戶,重新發(fā)送init(server->client)命令到所有其他在線用戶,更新在線用戶列表。
命令流程如下:
效果示意圖: go服務(wù)端
package main
import (
"golang.org/x/net/websocket"
"fmt"
"log"
"net/http"
"github.com/go-redis/redis"
"encoding/json"
"strconv"
)
const max_room_num = 2
var (
JSON = websocket.JSON // codec for JSON
Message = websocket.Message // codec for string, []byte
ActiveClients = make(map[string]ClientConn) // map containing clients //在線websocket列表
User = make(map[string]string)
)
type ClientConn struct {
websocket *websocket.Conn
}
type UserMsg struct {
Room string
Cmd string
User string
AvatarUrl string
Content string
Uuid string
HandNum string
GuessNum string
}
type UserInfo struct {
User string
AvatarUrl string
Uuid string
}
type ReplyMsg struct {
Room string
Cmd string
Data string
}
type GuessResult struct {
Result string
CurrentNum int
HandRecord map[string]string
GuessRecord map[string]string
}
func echoHandler(ws *websocket.Conn) {
var err error
var userMsg UserMsg
for {
var data []byte
if err = websocket.Message.Receive(ws, &data); err != nil {
fmt.Println("can't receive")
break
}
err = json.Unmarshal(data, &userMsg)
fmt.Println(userMsg)
go wsHandler(ws,userMsg)
}
}
func wsHandler(ws *websocket.Conn,userMsg UserMsg) {
sockCli := ClientConn{ws}
var err error
redisClient := redis.NewClient(&redis.Options{
Addr: "localhost:6379",
Password: "", // no password set
DB: 0, // use default DB
})
//登錄
if userMsg.Cmd == "login" {
fmt.Println("login")
//判斷房間人數(shù)是否已滿
checkNumTmp := redisClient.SCard(userMsg.Room)
checkNum := checkNumTmp.Val()
if(checkNum < max_room_num) {
fmt.Println("checkNum success")
//socket用戶列表新增當前用戶websocket連接
ActiveClients[userMsg.Uuid] = sockCli
//用戶uuid保存到redis房間set集合內(nèi)
redisClient.SAdd("ROOM:"+userMsg.Room,userMsg.Uuid)
var me UserInfo
me.User = userMsg.User
me.AvatarUrl = userMsg.AvatarUrl
me.Uuid = userMsg.Uuid
//生成用戶信息json串
b, err := json.Marshal(me)
if err != nil {
fmt.Println("Encoding User Faild")
} else {
//保存用戶信息到redis
redisClient.Set("USER:"+me.Uuid,b,0)
//初始化用戶
initOnlineMsg(redisClient,userMsg)
}
} else {
var rm ReplyMsg
rm.Room = userMsg.Room
rm.Cmd = "loginFailed"
rm.Data = "登錄失敗,人數(shù)已滿"
sendMsg,err2 := json.Marshal(rm)
sendMsgStr := string(sendMsg)
fmt.Println(sendMsgStr)
if err2 != nil {
} else {
if err = websocket.Message.Send(ws, sendMsgStr); err != nil {
log.Println("Could not send UsersList to ", userMsg.User, err.Error())
}
}
}
//準備
} else if userMsg.Cmd == "ready" {
redisClient.Set("READY:"+userMsg.Uuid,"ready",0)
//從redis取房間內(nèi)的所有用戶uuid
roomSlice := redisClient.SMembers("ROOM:"+userMsg.Room)
//用戶uuid保存到一個go切片online
online := roomSlice.Val()
i := 0
//循環(huán)取在線用戶個人信息
if len(online) != 0 {
for _, na := range online {
if na != "" {
userJson := redisClient.Get("READY:"+na)
userJson2 := userJson.Val()
if userJson2 == "ready" {
i++
}
}
}
}
if i == len(online) && i == max_room_num {
var rm ReplyMsg
rm.Room = userMsg.Room
rm.Cmd = "start"
rm.Data = ""
broadcast(redisClient,userMsg,rm)
}
//退出
} else if userMsg.Cmd == "logout" {
fmt.Println("logout")
//socket用戶列表刪除該用戶websocket連接
delete(ActiveClients,userMsg.Uuid)
//從redis房間set集合內(nèi)刪除該用戶uuid
redisClient.SRem("ROOM:"+userMsg.Room,userMsg.Uuid)
//初始化用戶
initOnlineMsg(redisClient,userMsg)
//出拳
} else if userMsg.Cmd == "guess" {
var result string
fmt.Println("guess")
fmt.Println(userMsg.HandNum)
fmt.Println(userMsg.GuessNum)
myHandNum,_ := strconv.Atoi(userMsg.HandNum)
myGuessNum,_ := strconv.Atoi(userMsg.GuessNum)
redisClient.Set("HANDNUM:"+userMsg.Uuid,myHandNum,0)
redisClient.Set("GUESSNUM:"+userMsg.Uuid,myGuessNum,0)
//從redis取房間內(nèi)的所有用戶uuid
roomSlice := redisClient.SMembers("ROOM:"+userMsg.Room)
//用戶uuid保存到一個go切片online
online := roomSlice.Val()
i := 0
//循環(huán)取在線用戶
if len(online) != 0 {
for _, na := range online {
if na != "" {
handnumCmd := redisClient.Get("HANDNUM:"+na)
handnum := handnumCmd.Val()
if handnum != "" {
i++
}
}
}
}
//房間內(nèi)所有人都已提交,則計算最后結(jié)果
if i == len(online) && i == max_room_num {
var handRecordList map[string]string
handRecordList = make(map[string]string)
var guessRecordList map[string]string
guessRecordList = make(map[string]string)
//計算正確結(jié)果currentNum
currentNum := 0
//循環(huán)取在線用戶
if len(online) != 0 {
for _, na := range online {
if na != "" {
//取某用戶的出拳數(shù)據(jù),已用戶名為key,存入結(jié)果map
handnumCmd := redisClient.Get("HANDNUM:"+na)
handnum := handnumCmd.Val()
guessnumCmd := redisClient.Get("GUESSNUM:"+na)
guessnum := guessnumCmd.Val()
userJson := redisClient.Get("USER:"+na)
userJson2 := userJson.Val()
var user UserInfo
json.Unmarshal([]byte(userJson2), &user)
handRecordList[user.User] = handnum
guessRecordList[user.User] = guessnum
//計算結(jié)果
thandnum,_ := strconv.Atoi(handnum)
currentNum = currentNum + thandnum
}
}
}
//給各個用戶發(fā)送結(jié)果消息
if len(online) != 0 {
for _, na := range online {
if na != "" {
guessnumCmd := redisClient.Get("GUESSNUM:"+na)
guessnum := guessnumCmd.Val()
tguessnum ,_ := strconv.Atoi(guessnum)
if tguessnum == currentNum {
result = "1"
} else {
result = "0"
}
var guessResult GuessResult
guessResult.Result = result
guessResult.CurrentNum = currentNum
guessResult.HandRecord = handRecordList
guessResult.GuessRecord = guessRecordList
resultTmp,_ := json.Marshal(guessResult)
resultData := string(resultTmp)
//刪除用戶準備狀態(tài)
redisClient.Del("READY:"+na)
//刪除用戶猜拳數(shù)據(jù)
redisClient.Del("HANDNUM:"+na)
redisClient.Del("GUESSNUM:"+na)
var rm ReplyMsg
rm.Room = userMsg.Room
rm.Cmd = "result"
rm.Data = resultData
sendMsg,_ := json.Marshal(rm)
sendMsgStr := string(sendMsg)
if err = websocket.Message.Send(ActiveClients[na].websocket, sendMsgStr); err != nil {
log.Println("Could not send UsersList to ", "", err.Error())
}
}
}
}
}
//發(fā)消息
} else {
/*
//從redis取房間內(nèi)的所有用戶uuid
roomSlice := redisClient.SMembers(userMsg.Room)
//用戶uuid保存到一個go切片online
online := roomSlice.Val()
//循環(huán)給房間內(nèi)用戶發(fā)送消息
if len(online) != 0 {
for _, na := range online {
if na != "" {
//ActiveClients[na].websocket就是用戶對應(yīng)的websocket鏈接
if err = websocket.Message.Send(ActiveClients[na].websocket, userMsg.User+"說:"+userMsg.Content); err != nil {
log.Println("Could not send message to ", userMsg.User, err.Error())
}
}
}
}*/
}
}
//房間成員初始化,有人加入或者退出都要重新初始化,相當于聊天室的在線用戶列表的維護
func initOnlineMsg(redisClient *redis.Client,userMsg UserMsg) {
var err error
//從redis取房間內(nèi)的所有用戶uuid
roomSlice := redisClient.SMembers("ROOM:"+userMsg.Room)
//用戶uuid保存到一個go切片online
online := roomSlice.Val()
var onlineList []string
//循環(huán)取在線用戶個人信息
if len(online) != 0 {
for _, na := range online {
if na != "" {
userJson := redisClient.Get("USER:"+na)
userJson2 := userJson.Val()
onlineList = append(onlineList,userJson2)
}
}
}
fmt.Println("get online success")
//生成在線用戶信息json串
//c, err := json.Marshal(onlineList)
onlineListStr,err2 := json.Marshal(onlineList)
var rm ReplyMsg
rm.Room = userMsg.Room
rm.Cmd = "init"
rm.Data = string(onlineListStr)
sendMsg,err2 := json.Marshal(rm)
sendMsgStr := string(sendMsg)
fmt.Println("init")
if err2 != nil {
} else {
//給所有用戶發(fā)初始化消息
if len(online) != 0 {
for _, na := range online {
if na != "" {
if err = websocket.Message.Send(ActiveClients[na].websocket, sendMsgStr); err != nil {
log.Println("Could not send UsersList to ", "", err.Error())
}
}
}
}
//若房間人數(shù)滿,發(fā)送就緒消息
if len(online) >= max_room_num {
fmt.Println("full")
var rm ReplyMsg
rm.Room = userMsg.Room
rm.Cmd = "full"
rm.Data = ""
sendMsg,_ := json.Marshal(rm)
sendMsgStr := string(sendMsg)
for _, na := range online {
if na != "" {
if err = websocket.Message.Send(ActiveClients[na].websocket, sendMsgStr); err != nil {
log.Println("Could not send UsersList to ", "", err.Error())
}
}
}
}
}
}
//廣播消息
func broadcast(redisClient *redis.Client,userMsg UserMsg,rm ReplyMsg) {
var err error
//從redis取房間內(nèi)的所有用戶uuid
roomSlice := redisClient.SMembers("ROOM:"+userMsg.Room)
//用戶uuid保存到一個go切片online
online := roomSlice.Val()
sendMsg,err2 := json.Marshal(rm)
sendMsgStr := string(sendMsg)
fmt.Println("broadcast")
if err2 != nil {
} else {
//給所有用戶發(fā)消息
if len(online) != 0 {
for _, na := range online {
if na != "" {
if err = websocket.Message.Send(ActiveClients[na].websocket, sendMsgStr); err != nil {
log.Println("Could not send UsersList to ", "", err.Error())
}
}
}
}
}
}
func main() {
http.Handle("/echo", websocket.Handler(echoHandler))
http.Handle("/", http.FileServer(http.Dir(".")))
err := http.ListenAndServe(":8929", nil)
if err != nil {
panic("ListenAndServe: " + err.Error())
}
}
小程序代碼:
//app.js
App({
onLaunch: function () {
console.log("App生命周期函數(shù)——onLaunch函數(shù)");
},
checkSession:function(mysessionid) {
return new Promise(function(resolve, reject) {
wx.request({
url: 'https://xxx.xxxxx.com/check.php',
header: {
sessionid:mysessionid
},
success: function(res) {
console.log("檢查sessionid是否有效")
resolve(res.data)
},
fail: function(e) {
reject(e)
}
})
})
},
login:function() {
return new Promise(function(resolve, reject) {
wx.login({
success: function (res0) {
if (res0.code) {
wx.request({
url: 'https://xxx.xxxxx.com/login.php',
data: {
code: res0.code
},
header: {
'content-type': 'application/json'
},
success: function(res) {
console.log("取得新的sessionid")
console.log(res.data)
var mysessionid = res.data.k
wx.setStorageSync("mysessionid",mysessionid)
var myuuid = res.data.v
wx.setStorageSync("myuuid",myuuid)
resolve(mysessionid)
},
fail: function(e) {
reject(e)
}
})
}
}
})
})
},
getWxUserInfo:function() {
return new Promise(function(resolve, reject) {
wx.getUserInfo({
withCredentials: false,
success: function(res) {
console.log("取得新的userInfo")
var userInfo = res.userInfo
wx.setStorageSync("userInfo",userInfo)
console.log("setUserInfo")
resolve(userInfo)
}
})
})
},
getUserInfo:function() {
var that = this
return new Promise(function(resolve, reject) {
var mysessionid = wx.getStorageSync('mysessionid')
if(mysessionid) {
console.log("sessionid存在")
that.checkSession(mysessionid).then(function(sessionContent){
if(sessionContent == 0) {
console.log("sessionid無效-取userInfo存到本地")
that.login().then(function(){
that.getWxUserInfo().then(function(userInfo){
resolve(userInfo)
})
})
} else {
console.log("sessionid有效-直接取本地userInfo")
var userInfo = wx.getStorageSync("userInfo")
resolve(userInfo)
}
})
} else {
console.log("sessionid不存在,重新走登錄流程")
that.login().then(function(){
that.getWxUserInfo().then(function(userInfo){
resolve(userInfo)
})
})
}
})
},
globalData:{
userInfo:null,
onlineList:[],
onlineStatus:false,
myHandNum:0,
myGuessNum:0
}
})
page/index.js
//index.js
//獲取應(yīng)用實例
var app = getApp()
Page({
data: {
userInfo: {},
onlineList:{},
status:0,
statusStr:"等待中",
guessBoxStatus:"hideBox",
handList:['0','1','2','3','4','5'],
handStyleList:['primary','default','default','default','default','default'],
guessList:['0','1','2','3','4','5','6','7','8','9','10'],
guessStyleList:['primary','default','default','default','default','default','default','default','default','default','default'],
buttonList:['0','1','2'],
buttonStrList:['準備','開始','提交'],
buttonStyleList:['btnShow','btnHide','btnHide'],
buttonFuncList:['ready','start','guess']
},
onLoad: function () {
console.log("Page onLoad函數(shù)");
wx.playBackgroundAudio({
dataUrl: 'https://xxx.xxxxx.com/8585.mp3',
title: '古琴音效',
coverImgUrl: 'https://xxx.xxxxx.com/logo.png',
success: function() {
console.log("播放音效")
}
})
},
onHide: function() {
console.log('發(fā)送注銷消息')
var myuuid = wx.getStorageSync('myuuid')
var msg = new Object();
msg.Room = '1';
msg.Cmd = 'logout';
msg.Uuid = myuuid;
var str = JSON.stringify(msg)
wx.sendSocketMessage({
data:str
})
wx.closeSocket()
app.globalData.onlineStatus = false
},
onShow: function() {
var that = this
app.getUserInfo().then(function(userInfo){
that.setData({
userInfo:userInfo
})
that.wsHandler(userInfo)
that.initBox()
})
},
wsHandler: function(userInfo) {
var that = this
//websocket
wx.connectSocket({
url: 'wss://xx.xxxxx.com/echo'
})
wx.onSocketOpen(function(res) {
console.log('WebSocket連接已打開!')
var myuuid = wx.getStorageSync('myuuid')
var msg = new Object();
msg.Room = '1';
msg.Cmd = 'login';
msg.User = userInfo.nickName;
msg.AvatarUrl = userInfo.avatarUrl;
msg.Uuid = myuuid;
var str = JSON.stringify(msg)
wx.sendSocketMessage({
data:str
})
})
wx.onSocketMessage(function(res) {
var msg = JSON.parse(res.data)
if(msg.Cmd == 'init') {
var userList = JSON.parse(msg.Data)
app.globalData.onlineList = []
for(var i=0;i<userList.length;i++){
var user = JSON.parse(userList[i])
app.globalData.onlineList.push(user)
}
that.setData({
onlineList:app.globalData.onlineList,
status:0,
statusStr:'等待中'
})
}
if(msg.Cmd == 'full') {
that.setData({
status:1,
statusStr:'準備開始'
})
}
if(msg.Cmd == 'result') {
var result = JSON.parse(msg.Data)
var content = "總數(shù)為"+result.CurrentNum+"\n"
for (var value in result.HandRecord) {
content = content+value+"出拳:"+result.HandRecord[value]+"\n";
}
for (var value in result.GuessRecord) {
content = content+value+"猜拳:"+result.GuessRecord[value]+"\n";
}
if(result.Result == 1) {
content = "恭喜你,猜中啦\n" + content
wx.showModal({
content: content,
showCancel: false,
success: function (res) {
if (res.confirm) {
that.initBox()
}
}
});
}
if(result.Result == 0) {
content = "很遺憾,猜錯啦\n" + content
wx.showModal({
content: content,
showCancel: false,
success: function (res) {
if (res.confirm) {
that.initBox()
}
}
});
}
}
if(msg.Cmd == 'start') {
that.setData({
status:2,
statusStr:'游戲中',
guessBoxStatus:'showBox',
buttonStyleList:['btnHide','btnHide','btnShow'],
})
}
})
},
setHandNum: function(event) {
var that = this
console.log(event.target.dataset.handnum)
app.globalData.myHandNum = event.target.dataset.handnum
var myList = that.data.handStyleList
for(var i=0;i<myList.length;i++) {
if(i == event.target.dataset.handnum) {
myList[i] = 'primary'
} else {
myList[i] = 'default'
}
}
that.setData({
handStyleList:myList
})
},
setGuessNum: function(event) {
var that = this
console.log(event.target.dataset.guessnum)
app.globalData.myGuessNum = event.target.dataset.guessnum
var myList = that.data.guessStyleList
for(var i=0;i<myList.length;i++) {
if(i == event.target.dataset.guessnum) {
myList[i] = 'primary'
} else {
myList[i] = 'default'
}
}
that.setData({
guessStyleList:myList
})
},
guess: function() {
var that = this
var userInfo = that.data.userInfo
var myuuid = wx.getStorageSync('myuuid')
var msg = new Object();
msg.Room = '1';
msg.Cmd = 'guess';
msg.User = userInfo.nickName;
msg.AvatarUrl = userInfo.avatarUrl;
msg.Uuid = myuuid;
msg.HandNum = app.globalData.myHandNum
msg.GuessNum = app.globalData.myGuessNum
var str = JSON.stringify(msg)
wx.sendSocketMessage({
data:str
})
},
ready: function() {
var that = this
var userInfo = that.data.userInfo
var myuuid = wx.getStorageSync('myuuid')
var msg = new Object();
msg.Room = '1';
msg.Cmd = 'ready';
msg.User = userInfo.nickName;
msg.AvatarUrl = userInfo.avatarUrl;
msg.Uuid = myuuid;
var str = JSON.stringify(msg)
wx.sendSocketMessage({
data:str
})
that.setData({
status:1,
statusStr:'等待對手,準備開始',
buttonStyleList:['btnHide','btnHide','btnHide'],
})
},
start: function() {
var that = this
var userInfo = that.data.userInfo
var myuuid = wx.getStorageSync('myuuid')
var msg = new Object();
msg.Room = '1';
msg.Cmd = 'start';
msg.User = userInfo.nickName;
msg.AvatarUrl = userInfo.avatarUrl;
msg.Uuid = myuuid;
var str = JSON.stringify(msg)
wx.sendSocketMessage({
data:str
})
},
initBox: function() {
var that = this
that.setData({
status:0,
statusStr:"等待中",
guessBoxStatus:"hideBox",
handList:['0','1','2','3','4','5'],
handStyleList:['primary','default','default','default','default','default'],
guessList:['0','1','2','3','4','5','6','7','8','9','10'],
guessStyleList:['primary','default','default','default','default','default','default','default','default','default','default'],
buttonList:['0','1','2'],
buttonStrList:['準備','開始','提交'],
buttonStyleList:['btnShow','btnHide','btnHide'],
buttonFuncList:['ready','start','guess']
})
},
getAudioStatus: function() {
wx.getBackgroundAudioPlayerState({
success: function(res) {
var status = res.status
var dataUrl = res.dataUrl
var currentPosition = res.currentPosition
var duration = res.duration
var downloadPercent = res.downloadPercent
console.log("音樂狀態(tài)"+status)
console.log("音樂長度"+duration)
}
})
}
})
ps:播放音樂的功能,在開發(fā)工具可以看到,真機上沒有聽到聲音,暫時還沒找到解決辦法 check.php
<?php
$post_data = $_POST;
$header = get_all_headers();
$sessionid = $header['sessionid'];
$host = '127.0.0.1';
$port = '6379';
$timeout = 0;
$redis = new Redis();
$redis->connect($host, $port, $timeout);
$session_content = $redis->get("miniappsession:".$sessionid);
if($session_content)
{
echo $session_content;
} else {
echo 0;
}
/**
* 獲取自定義的header數(shù)據(jù)
*/
function get_all_headers(){
// 忽略獲取的header數(shù)據(jù)
$ignore = array('host','accept','content-length','content-type');
$headers = array();
foreach($_SERVER as $key=>$value){
if(substr($key, 0, 5)==='HTTP_'){
$key = substr($key, 5);
$key = str_replace('_', ' ', $key);
$key = str_replace(' ', '-', $key);
$key = strtolower($key);
if(!in_array($key, $ignore)){
$headers[$key] = $value;
}
}
}
return $headers;
}
login.php
<?php
$code = $_GET['code'];
define("APPID",'xxxxxxxxxxxxxxxxxxxxx');
define("SECRET",'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx');
$url = "https://api.weixin.qq.com/sns/jscode2session?appid=".APPID."&secret=".SECRET."&js_code=".$code."&grant_type=authorization_code";
$rs = curlGet($url);
$arr = json_decode($rs);
$str = randomFromDev(32);
$host = '127.0.0.1';
$port = '6379';
$timeout = 0;
$redis = new Redis();
$redis->connect($host, $port, $timeout);
$expires_time = 15*24*60*60;
$session_content = md5($arr->openid.$expires_time);
$redis->setex("miniappsession:".$str,$expires_time,$session_content);
$sessionObj['k'] = $str;
$sessionObj['v'] = $session_content;
echo json_encode($sessionObj);
function randomFromDev($len)
{
$fp = @fopen('/dev/urandom','rb');
$result = '';
if ($fp !== FALSE) {
$result .= @fread($fp, $len);
@fclose($fp);
}
else
{
trigger_error('Can not open /dev/urandom.');
}
$result = md5($result);
// convert from binary to string
//$result = base64_encode($result);
// remove none url chars
//$result = strtr($result, '+/', '-_');
// Remove = from the end
//$result = str_replace('=', ' ', $result);
return $result;
}
function curlGet($url, $method = 'get', $data = '')
{
$ch = curl_init();
$header = 'Accept-Charset: utf-8';
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_CUSTOMREQUEST, strtoupper($method));
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, $header);
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (compatible; MSIE 5.01; Windows NT 5.0)');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);
curl_setopt($ch, CURLOPT_AUTOREFERER, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$temp = curl_exec($ch);
return $temp;
}
|