您好,欢迎来到三六零分类信息网!老站,搜索引擎当天收录,欢迎发信息

用nginx+uwsgi+redis实现游戏GM聊天功能

2024/2/25 21:33:25发布18次查看
原始需求
一个客服gm能够加所有游戏服内的玩家为好友,并能进行聊天。具体功能如下:
* gm上、下线
* 加游戏玩家为好友
* 删游戏玩家为好友
* gm发送聊天消息
* 玩家推送聊天消息
额外限定:一个gm账号能够添加多个游戏玩家为好友,而一个游戏玩家只能被一个gm账号添加
需求分析
因为我们游戏内并没有跨服聊天、跨服好友这种功能,而且以后也不会支持,所以让gm在游戏里面创建角色,然后加各个游戏服的玩家进行聊天的方案是无法实施的。 而且gm实际上并不是一个游戏角色,也不用在游戏内创建。
整个的难点是,如何让各个游戏服访问到gm发过来的各种数据,如何将玩家的数据推送给gm。
具体实现
为了实现gm的数据在各个服务器传递,我们采用了一种简单的方案:将gm的数据放在我们的web服务器上,各个游戏服定时从web服务器去拿数据。
这种方案很简单,web服务器与游戏服不用长连接,直接用http的get和post方法就可以拿数据了。整个架构如下:
gm1-------运维聊天服--------游戏web服务器------游戏服务器1--------游戏客户端1||||||gm2游戏服务器2游戏客户端2
这里客服gm1和gm2都用的web界面与游戏客户端聊天。
运维聊天服存在是因为:
* gm的创建需要运维那边的审批。。
* 游戏的web服务器可以进行白名单审查,只有运维聊天服的ip可以访问游戏的web服务器
上图中只有游戏客户端与游戏服务器是采用的tcp长连接,其他都是使用http短连接来实现。
web服务器采用的是nginx,而不是nodejs。nginx方案挺成熟的,而且部署也很容易。
由于,我对python的熟悉程度比lua程度高很多,所以用了uwsgi来做代理,而不是直接用lua来写。
而数据库则采用的redis,redis设置了定时存盘了。数据格式设置可参考我之前提的文章,基本都是
gm:%d:name这种格式,key表示gmx的名字是什么,val表示名字。
实现代码
nginx和uwsgi的配置已略去。因为隐私原因,相关ip已略去,代码里面也有足够的注释,不再赘述:
#encoding: utf-8新功能:* gm注册* gm上线* gm下线* 加游戏玩家为好友* 删除游戏好友* gm推聊天信息* 玩家推聊天信息---消息数据格式为utf-8处理后的base64编码:游戏服和gm发过来的都是base64格式,要注意分隔符没做base64处理gs只能用get方式推送消息,所以参数用类似于urllib quote(urlencode)进行了封装。运维客户端也用get一个gm账号能添加多个游戏玩家为好友,而一个游戏玩家只能被一个gm账号添加from config import *from json import dumps, loadsimport base64import urllibimport urllib2import copyimport redismsg_separator = ,#分割信息max_recv_amount = 10#每次消息10条吧msg_max_len = 500#消息不弄太长了content_type = [(content-type,text/html)]http_status = { 200: 200 ok, 404: 404 not found,}game_server_info_url = http://xxxxxyyyyyrole_info_url = http://xxyyy?uid=%s&hostnum=%sred = redis.strictredis(host=redis.host, port=redis.port, db=redis.db)#游戏服务器ip白名单ifnot globals().has_key(gserverip): gserverip = {} res_data = urllib2.urlopen(game_server_info_url) res = res_data.read() res_list = res.split(\n) for val in res_list: ifnot val: continue _, port, ip, _ = val.split( ) gserverip[ip] = portggmip = { xxxxyyyy : 1,}defis_gm_account_exist(account_id):if red.get(gm_account:%s:name % account_id): return1return0defis_inter_server(hostnum):if ( int(hostnum) >= 1000 ): return0return1defcheck_is_int(account_id):try: int(account_id) except: return http_status[200], content_type, dumps({res: 0, errno: -1}) return ()#gm client ensures the id is uniquedefgm_create_account(env, params): account_id, account_name = params[gm_account], urllib.unquote(params[gm_name]) check_res = check_is_int(account_id) if check_res: return check_res if is_gm_account_exist(account_id): return http_status[200], content_type, dumps({res: 0, errno: 1}) #1 the role exists red.set(gm_account:%s:name % account_id, account_name) red.sadd(gm_online_list, account_id) return http_status[200], content_type, dumps({res: 1})#check paramdefgm_add_friend(env, params): var = gm_account, hostnum, usernum = params[gm_account], params[host], params[uid] for num in var: check_res = check_is_int(num) if check_res: return check_res ifnot is_gm_account_exist(gm_account): return http_status[200], content_type, dumps({res: 0, errno: 2}) #2 the role doesn't existif ( red.get(gs_usernum:%s:friend % usernum) ): return http_status[200], content_type, dumps({res: 0, errno: 3}) #3 the usernum has gotten a friend#内服计费没存数据,就不处理了ifnot is_inter_server(hostnum): http_res_data = urllib2.urlopen(role_info_url % (usernum, hostnum)) res = loads(http_res_data.read()) if (type(res) != type({})) or (res.get(code, 0) != 1): return http_status[200], content_type, dumps({res: 0, errno: 4}) #4 the uid doesn't exist red.sadd(gm_account:%s:friend % gm_account, usernum) #两边都处理下 red.sadd(gs_hostnum:%s % hostnum, usernum) #记录该服务器的所有玩家 red.set(gs_usernum:%s:hostnum % usernum, hostnum) #该玩家的信息 red.set(gs_usernum:%s:friend % usernum, gm_account) #一个玩家只能被一个gm添加为好友 red.sadd(apply_frd_list, usernum) #usernum will be added red.hdel(remove_frd_list, usernum) #信息残留return http_status[200], content_type, dumps({res: 1}) defgm_remove_friend(env, params): account_id, uid = params[gm_account], params[uid] ifnot is_gm_account_exist(account_id): return http_status[200], content_type, dumps({res: 0, errno: 2}) #2 the role doesn't existif red.get(gs_usernum:%s:friend % uid) != account_id: return http_status[200], content_type, dumps({res: 0, errno: 5}) # the usernum has friend but isn't the gmifnot red.srem(gm_account:%s:friend % account_id, uid): return http_status[200], content_type, dumps({res: 0, errno: 4}) # the usernum is not a friend of the gm hostnum = red.get(gs_usernum:%s:hostnum % uid) red.delete(gs_usernum:%s:hostnum % uid) #合服考虑,如果合服了gm手动删除这个玩家吧 red.srem(gs_hostnum:%s % hostnum, uid) red.delete(gs_usernum:%s:friend % uid) red.hset(remove_frd_list, uid, hostnum) #uid的信息已经丢失,先额外保存下hostnum信息 red.srem(apply_frd_list, uid) #信息残留return http_status[200], content_type, dumps({res: 1}) #gm账号很少defgm_online(env, params): account_id = params[gm_account] #可能客户端bug没发下线,直接sadd吧ifnot is_gm_account_exist(account_id): return http_status[200], content_type, dumps({res: 0, errno: 2}) #2 the role doesn't exist red.sadd(gm_online_list, account_id) return http_status[200], content_type, dumps({res: 1})defgm_offline(env, params): account_id = params[gm_account] ifnot red.srem(gm_online_list, account_id): return http_status[200], content_type, dumps({res: 0}) return http_status[200], content_type, dumps({res: 1})#存在usernum上,gs_msg和gm_msgdefgm_sendmsg(env, params): account_id, uid, msg = params[gm_account], params[uid], urllib.unquote(params[msg]) #只能向好友发ifnot red.sismember(gm_account:%s:friend % account_id, uid): return http_status[200], content_type, dumps({res: 0, errno: 4}) # the usernum is not a friend of the gmif red.get(gs_usernum:%s:friend % uid) != account_id: return http_status[200], content_type, dumps({res: 0, errno: 5}) # the usernum has friend but isn't the gm or doesn't have red.lpush(gs_usernum:%s:msg_from_gm % uid, msg) red.sadd(gm_newmsg_list, uid) #gs get msg from this setreturn http_status[200], content_type, dumps({res: 1})#gm轮训所有的,他那边还有个服务器...#{gm_account:{uid: msg, uid2: msg2}}defgm_receivemsg(env, params): user_set = copy.copy(red.smembers(gs_newmsg_list)) msg_data = {} for uid in user_set: gm_account = red.get(gs_usernum:%s:friend % uid) ifnot gm_account: #理论上是不会continue msg_list = pop_msg(uid, msg_from_gs) send_msg = msg_separator.join(msg_list) ifnot send_msg: continueifnot gm_account in msg_data: msg_data[gm_account] = [] msg_data[gm_account].append({uid : uid, msg : send_msg}) #red.srem(gs_newmsg_list, uid)return http_status[200], content_type, dumps({res: 1, data: base64.b64encode(dumps(msg_data))})defpop_msg(uid, msg_type): msg_list = [] msg_key = gs_usernum:%s:%s % (uid, msg_type) msg_len = min(max_recv_amount, red.llen(msg_key)) for i in xrange(msg_len): piece_msg = red.rpop(msg_key) msg_list.append(piece_msg) return msg_list#---------------------gs----------------------#apply and removedefget_frd_relation(env, params): host = params[host] apply_user_set = copy.copy(red.smembers(apply_frd_list)) apply_data = {} #{res:1 data:base64({uid: gm_account})}for uid in apply_user_set: hostnum = red.get(gs_usernum:%s:hostnum % uid) if hostnum != host: continue account_id = red.get(gs_usernum:%s:friend % uid) ifnot account_id: #error continue apply_data[uid] = [account_id, red.get(gm_account:%s:name % account_id)] red.srem(apply_frd_list, uid) del_user_list = red.hkeys(remove_frd_list) remove_list = [] for uid in del_user_list: hostnum = red.hget(remove_frd_list, uid) if hostnum != host: continue remove_list.append(uid) red.hdel(remove_frd_list, uid) return http_status[200], content_type, dumps({res: 1, apply_data: base64.b64encode(dumps(apply_data)), remove_data: base64.b64encode(dumps(remove_list))}) defgs_sendmsg(env, params): uid, msg = params[uid], urllib.unquote(params[msg]) ifnot red.get(gs_usernum:%s:friend % uid): return http_status[200], content_type, dumps({res: 0, errno: 5}) # the usernum has friend but isn't the gm or doesn't have red.lpush(gs_usernum:%s:msg_from_gs % uid, msg) red.sadd(gs_newmsg_list, uid) #gm get msg from this setreturn http_status[200], content_type, dumps({res: 1})defgs_receivemsg(env, params): host = params[host] user_set = copy.copy(red.smembers(gm_newmsg_list)) total_msg_list = [] for uid in user_set: hostnum = red.get(gs_usernum:%s:hostnum % uid) if hostnum != host: continue msg_list = pop_msg(uid, msg_from_gm) user_msg = msg_separator.join(msg_list) ifnot user_msg: continue msg_data = { uid : uid, msg : user_msg, } total_msg_list.append(msg_data) return http_status[200], content_type, dumps({res: 1, data: base64.b64encode(dumps(total_msg_list))})defget_online_list(env, params): host = params[host] send_list = [] online_list = red.smembers(gm_online_list) for account_id in online_list: frd_set = red.smembers(gm_account:%s:friend % account_id) for uid in frd_set: if red.get(gs_usernum:%s:hostnum % uid) == host: send_list.append(account_id) #只有这个服务器有gm的好友,才通知breakreturn http_status[200], content_type, dumps({res: 1, data: base64.b64encode(dumps(send_list))})#get: action=create&gm_account&gm_name 创建账号#get: action=add&gm_account&host&uid 添加好友#get: action=del&gm_account&uid 删除好友#get: action=online&gm_account上线#get: action=offline&gm_account 下线#get: action=send&gm_account&uid&msg 发送消息#get: action=receive 轮训消息gm_func = { create : gm_create_account, add : gm_add_friend, del : gm_remove_friend, online : gm_online, offline : gm_offline, send : gm_sendmsg, receive : gm_receivemsg,}defhandle_gm_ticket(env, params):ifnot ggmip.get(env[remote_addr], 0): return http_status[200], content_type, %s has no access to the website % env[remote_addr] func = gm_func.get(params[action], none) ifnot func: return http_status[404], content_type, err action %s % params[action] return func(env, params)#get action=relation&host#get action=send&uid&msg#get action=receive&host#get action=online&host gs_func = { relation : get_frd_relation, send : gs_sendmsg, receive : gs_receivemsg, online : get_online_list,}defhandle_gs_ticket(env, params):ifnot gserverip.get(env[remote_addr], 0): return http_status[200], content_type, %s has no access to the website % env[remote_addr] func = gs_func.get(params[action], none) ifnot func: return http_status[404], content_type, err action %s % params[action] return func(env, params)
版权声明:本文为博主原创文章,未经博主允许不得转载。
以上就介绍了用nginx+uwsgi+redis实现游戏gm聊天功能,包括了方面的内容,希望对php教程有兴趣的朋友有所帮助。
该用户其它信息

VIP推荐

免费发布信息,免费发布B2B信息网站平台 - 三六零分类信息网 沪ICP备09012988号-2
企业名录 Product