Original demand
A customer service GM can add all the players in the game to be friends and chat. The specific functions are as follows:
* GM up and down
* Add game players as friends
* Delete Game players as friends
* GM sends chat messages
* Player Push Chat message
Additional qualification: A GM account can add multiple gamers as a friend, while a gamer can only be added by a GM account
Demand analysis
Because we are not in the game to cross-serve chat, cross-service friends this feature, and will not be supported in the future, so let GM in the game to create characters, and then add the game suit players to chat The program is not implemented. And GM isn't really a game character, and it doesn't have to be created inside the game.
The whole difficulty is how to get each game suit to access the various data sent by GM, how to push the player's data to GM.
Specific implementation
In order to implement the GM data on each server, we have adopted a simple solution: Put the GM data on our web server, the various game uniforms from the Web server to pick up the data.
This scheme is simple, the Web server and the game suit do not have long connection, directly with the HTTP GET and post method can take the data. The entire architecture is as follows:
GM1 - - - - - - - operation and Maintenance Chat Service - - - - - - - - Game Web Server - - - - - - Game server 1 - - - - - - - - Game Client 1 | | | | | | GM2 Game server 2 Game Client 2
Here customer service GM1 and GM2 both use the Web interface to chat with the game client.
Operation and maintenance Chat Service exists because:
* The creation of GM needs to be approved by operation and maintenance side.
* The game Web server can be whitelisted, only the IP of the OPS Chat Service can access the game's Web server
Only the game client and the game server is the TCP long connection, the other is the use of HTTP short connection to achieve.
The Web server uses Nginx, not nodejs. The Nginx scheme is quite mature and easy to deploy.
Since I am much more familiar with Python than Lua, I use UWSGI as an agent instead of writing it directly with Lua.
And the database is used by the Redis,redis set up a timed disk. Data format settings can refer to my premise of the article, basically is
gm:%d:name
In this format, key indicates what GMX's name is, and Val represents the name.
Implementation code
The configuration of Nginx and Uwsgi has been omitted. For privacy reasons, the relevant IP has been omitted, the code also has enough comments, no longer repeat:
#encoding: Utf-8"" "new features: * GM Registration * GM Online * GM Downline * Plus game players for friends * Delete Game friends * GM Push Chat information * Player Push chat Information---message data format for Utf-8 processed base64 encoding: Game clothing and GM sent over the base64 format, Note that the delimiter is not done base64 processing GS can only be pushed with the Get method, so the parameters are encapsulated in a similar urllib quote (UrlEncode). The OPS client also uses get a GM account to add multiple gamers as friends, while a gamer can only be added "" by a GM account . fromConfigImport* fromJsonImportDumps, loadsImportBase64ImportUrllibImportUrllib2ImportCopyImportRedismsg_separator =","#分割信息Max_recv_amount =Ten#每次消息10条吧Msg_max_len = -#消息不弄太长了Content_Type = [("Content-type","Text/html")]http_status = { $:"OK",404:"404 Not Found",}game_server_info_url ="Http://xxxxxyyyyy"Role_info_url ="http://xxyyy?uid=%s&hostnum=%s"Red = Redis. Strictredis (Host=redis. HOST, Port=redis. PORT, Db=redis. DB)#游戏服务器IP白名单if notGlobals (). Has_key ("Gserverip"): Gserverip = {} Res_data = Urllib2.urlopen (game_server_info_url) res = Res_data.read () res_list = Res.split ("\ n") forValinchRes_list:if notValContinue_, Port, IP, _ = Val.split (" ") Gserverip[ip] = PORTGGMIP = {"Xxxxyyyy":1,} defis_gm_account_exist(account_id):ifRed.get ("Gm_account:%s:name"% account_id):return1return0 defis_inter_server(hostnum):if(int (hostnum) >= +):return0return1 defcheck_is_int(account_id):Try: Int (account_id)except:returnhttp_status[ $], Content_Type, dumps ({"res":0,"errno": -1})return()#gm Client ensures the ID is unique defgm_create_account(env, params):account_id, account_name = params["Gm_account"], Urllib.unquote (params["Gm_name"]) Check_res = Check_is_int (account_id)ifCheck_res:returnCheck_resifIs_gm_account_exist (account_id):returnhttp_status[ $], Content_Type, dumps ({"res":0,"errno":1})#1 the role existsRed.set ("Gm_account:%s:name"% account_id, account_name) Red.sadd ("Gm_online_list", account_id)returnhttp_status[ $], Content_Type, dumps ({"res":1})#check param defgm_add_friend(env, params):var = gm_account, hostnum, usernum = params["Gm_account"], params["Host"], params["UID"] forNuminchVar:check_res = Check_is_int (num)ifCheck_res:returnCheck_resif notIs_gm_account_exist (Gm_account):returnhttp_status[ $], Content_Type, dumps ({"res":0,"errno":2})#2 The role doesn ' t existif(Red.get ("Gs_usernum:%s:friend"% usernum)):returnhttp_status[ $], Content_Type, dumps ({"res":0,"errno":3})#3 The Usernum has gotten a friend#内服计费没存数据, we're not dealing with it.if notIs_inter_server (hostnum): Http_res_data = Urllib2.urlopen (role_info_url% (Usernum, hostnum)) res = loads (HT Tp_res_data.read ())if(Type (res)! = Type ({}))or(Res.get ("Code",0) !=1):returnhttp_status[ $], Content_Type, dumps ({"res":0,"errno":4})#4 The uid doesn ' t existRed.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 'll be addedRed.hdel ("Remove_frd_list", Usernum)#信息残留returnhttp_status[ $], Content_Type, dumps ({"res":1}) defgm_remove_friend(env, params):account_id, uid = params["Gm_account"], params["UID"]if notIs_gm_account_exist (account_id):returnhttp_status[ $], Content_Type, dumps ({"res":0,"errno":2})#2 The role doesn ' t existifRed.get ("Gs_usernum:%s:friend"% uid)! = account_id:returnhttp_status[ $], Content_Type, dumps ({"res":0,"errno":5})# The Usernum have friend but isn ' t the GMif notRed.srem ("Gm_account:%s:friend"% account_id, UID):returnhttp_status[ $], Content_Type, dumps ({"res":0,"errno":4})# The Usernum is not a friend of the GMHostnum = Red.get ("Gs_usernum:%s:hostnum"% uid) Red.delete ("Gs_usernum:%s:hostnum"% uid)#合服考虑, if you take the GM, remove the player manually.Red.srem ("gs_hostnum:%s"% Hostnum, uid) red.delete ("Gs_usernum:%s:friend"% uid) Red.hset ("Remove_frd_list", UID, Hostnum)#uid的信息已经丢失, save the Hostnum information firstRed.srem ("Apply_frd_list", UID)#信息残留returnhttp_status[ $], Content_Type, dumps ({"res":1})#GM账号很少 defgm_online(env, params):account_id = params["Gm_account"]#可能客户端bug没发下线, just sadd.if notIs_gm_account_exist (account_id):returnhttp_status[ $], Content_Type, dumps ({"res":0,"errno":2})#2 The role doesn ' t existRed.sadd ("Gm_online_list", account_id)returnhttp_status[ $], Content_Type, dumps ({"res":1}) defgm_offline(env, params):account_id = params["Gm_account"]if notRed.srem ("Gm_online_list", account_id):returnhttp_status[ $], Content_Type, dumps ({"res":0})returnhttp_status[ $], Content_Type, dumps ({"res":1})#存在usernum上, Gs_msg and gm_msg. defgm_sendmsg(env, params):account_id, uid, msg = params["Gm_account"], params["UID"], Urllib.unquote (params["MSG"])#只能向好友发if notRed.sismember ("Gm_account:%s:friend"% account_id, UID):returnhttp_status[ $], Content_Type, dumps ({"res":0,"errno":4})# The Usernum is not a friend of the GMifRed.get ("Gs_usernum:%s:friend"% uid)! = account_id:returnhttp_status[ $], Content_Type, dumps ({"res":0,"errno":5})# The Usernum has friend but isn ' t the GM or doesn ' t hadRed.lpush ("GS_USERNUM:%S:MSG_FROM_GM"% UID, msg) Red.sadd ("Gm_newmsg_list", UID)#gs get msg from the This setreturnhttp_status[ $], Content_Type, dumps ({"res":1})#gm轮训所有的, he's got a server over there.#{gm_account:{"UID": Msg, "Uid2": MSG2}} defgm_receivemsg(env, params):User_set = Copy.copy (Red.smembers ("Gs_newmsg_list")) Msg_data = {} forUidinchUser_set:gm_account = Red.get ("Gs_usernum:%s:friend"% uid)if notGm_account:#理论上是不会ContinueMsg_list = pop_msg (UID,"Msg_from_gs") send_msg = Msg_separator.join (msg_list)if notSend_msg:Continueif notGm_accountinchMsg_data:msg_data[gm_account] = [] Msg_data[gm_account].append ({"UID": UID,"MSG": Send_msg})#red. Srem ("Gs_newmsg_list", UID)returnhttp_status[ $], 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)) forIinchXrange (msg_len): piece_msg = Red.rpop (Msg_key) msg_list.append (piece_msg)returnMsg_list#---------------------GS----------------------#apply and remove defget_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})} forUidinchApply_user_set:hostnum = Red.get ("Gs_usernum:%s:hostnum"% uid)ifHostnum! = Host:Continueaccount_id = Red.get ("Gs_usernum:%s:friend"% uid)if notACCOUNT_ID:#errorContinueAPPLY_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 = [] forUidinchDel_user_list:hostnum = Red.hget ("Remove_frd_list", UID)ifHostnum! = Host:ContinueRemove_list.append (UID) Red.hdel ("Remove_frd_list", UID)returnhttp_status[ $], 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"])if notRed.get ("Gs_usernum:%s:friend"% uid):returnhttp_status[ $], Content_Type, dumps ({"res":0,"errno":5})# The Usernum has friend but isn ' t the GM or doesn ' t hadRed.lpush ("Gs_usernum:%s:msg_from_gs"% UID, msg) Red.sadd ("Gs_newmsg_list", UID)#gm get msg from the This setreturnhttp_status[ $], Content_Type, dumps ({"res":1}) defgs_receivemsg(env, params):Host = params["Host"] User_set = Copy.copy (Red.smembers ("Gm_newmsg_list")) Total_msg_list = [] forUidinchUser_set:hostnum = Red.get ("Gs_usernum:%s:hostnum"% uid)ifHostnum! = Host:ContinueMsg_list = pop_msg (UID,"MSG_FROM_GM") user_msg = Msg_separator.join (msg_list)if notUser_msg:ContinueMsg_data = {"UID": UID,"MSG": User_msg,} total_msg_list.append (Msg_data)returnhttp_status[ $], 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") foraccount_idinchOnline_list:frd_set = Red.smembers ("Gm_account:%s:friend"% account_id) forUidinchFrd_set:ifRed.get ("Gs_usernum:%s:hostnum"% uid) = = Host:send_list.append (account_id)#只有这个服务器有gm的好友, only to notify Breakreturnhttp_status[ $], Content_Type, dumps ({"res":1,"Data": Base64.b64encode (Dumps (send_list))})#get: Action=create&gm_account&gm_name Create an account#get: Action=add&gm_account&host&uid Add Friends#get: action=del&gm_account&uid Delete Friends#get: Action=online&gm_account online#get: Action=offline&gm_account downline#get: action=send&gm_account&uid&msg Send Message#get: action=receive Rotation NewsGm_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):if notGgmip.get (env["REMOTE_ADDR"],0):returnhttp_status[ $], Content_Type,"%s has no access to the website"% env["REMOTE_ADDR"] Func = Gm_func.get (params["Action"],None)if notFuncreturnhttp_status[404], Content_Type,"Err action%s"% params["Action"]returnFunc (env, params)#get action=relation&host#get action=send&uid&msg#get action=receive&host#get action=online&hostGs_func = {"Relation": Get_frd_relation,"Send": Gs_sendmsg,"Receive": Gs_receivemsg,"Online": Get_online_list,} defhandle_gs_ticket(env, params):if notGserverip.get (env["REMOTE_ADDR"],0):returnhttp_status[ $], Content_Type,"%s has no access to the website"% env["REMOTE_ADDR"] Func = Gs_func.get (params["Action"],None)if notFuncreturnhttp_status[404], Content_Type,"Err action%s"% params["Action"]returnFunc (env, params)
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
The above describes the implementation of the game GM chat with Nginx+uwsgi+redis, including the contents of the content, I hope that the PHP tutorial interested in a friend helpful.