Use nginx+uwsgi+redis to implement game GM chat function
Original requirement
A customer service GM can add all players in the game server as friends and can chat. The specific functions are as follows:
* GM online and offline
* Add gamers as friends
* Delete game players as friends
* GM sends chat message
* Player push chat messages
Additional limitations: One GM account can add multiple game players as friends, but one game player can only be added by one GM account
Requirement analysis
Because our game does not have such functions as cross-server chat and cross-server friends, and It will not be supported in the future, so the solution of letting the GM create a character in the game and then add players from each game server to chat is impossible to implement. Moreover, the GM is not actually a game character and does not need to be created in the game.
The whole difficulty is how to allow each game server to access various data sent by the GM, and how to push player data to the GM.
Specific implementation
In order to realize the transfer of GM data to various servers, we adopted a simple solution: put the GM data on our web server, and each game server will get the data from the web server regularly.
This solution is very simple. There is no need for a long connection between the web server and the game server. You can directly use the get and post methods of http to get the data. The entire structure is as follows:
<code><span>GM1</span><span>-</span><span>-</span><span>-</span><span>-</span><span>-</span><span>-</span><span>-</span><span>运维聊天服</span><span>-</span><span>-</span><span>-</span><span>-</span><span>-</span><span>-</span><span>-</span><span>-</span><span>游戏web服务器</span><span>-</span><span>-</span><span>-</span><span>-</span><span>-</span><span>-</span><span>游戏服务器1</span><span>-</span><span>-</span><span>-</span><span>-</span><span>-</span><span>-</span><span>-</span><span>-</span><span>游戏客户端1</span><span>|</span><span>|</span><span>|</span><span>|</span><span>|</span><span>|</span><span>GM2</span><span>游戏服务器2</span><span>游戏客户端2</span></code>
Here customer service GM1 and GM2 both use the web interface to chat with the game client.
The operation and maintenance chat server exists because:
* The creation of GM requires approval from the operation and maintenance side. .
* The game's web server can be whitelisted and only the IP of the operation and maintenance chat server can access the game's web server
In the picture above, only the game client and the game server use tcp long connections, and the others are implemented using http short connections.
The web server uses nginx instead of nodejs. The nginx solution is quite mature and easy to deploy.
Since I am much more familiar with python than lua, I used uwsgi as the proxy instead of writing directly in lua.
The database uses redis, and redis has set up scheduled saves. For the data format setting, please refer to the article I mentioned before. It is basically in the format of gm:%d:name
. The key represents the name of gmx, and the val represents the name.
Implementation code
The configuration of nginx and uwsgi has been omitted. For privacy reasons, the relevant IP has been omitted, and there are sufficient comments in the code, so I won’t go into details:
<code><span>#encoding: utf-8</span><span>""" 新功能: * GM注册 * GM上线 * GM下线 * 加游戏玩家为好友 * 删除游戏好友 * GM推聊天信息 * 玩家推聊天信息 --- 消息数据格式为utf-8处理后的base64编码:游戏服和GM发过来的都是base64格式,要注意分隔符没做base64处理 GS只能用get方式推送消息,所以参数用类似于urllib quote(urlencode)进行了封装。运维客户端也用get 一个GM账号能添加多个游戏玩家为好友,而一个游戏玩家只能被一个GM账号添加 """</span><span>from</span> config <span>import</span> * <span>from</span> json <span>import</span> dumps, loads <span>import</span> base64 <span>import</span> urllib <span>import</span> urllib2 <span>import</span> copy <span>import</span> redis MSG_SEPARATOR = <span>","</span><span>#分割信息</span> MAX_RECV_AMOUNT = <span>10</span><span>#每次消息10条吧</span> MSG_MAX_LEN = <span>500</span><span>#消息不弄太长了</span>CONTENT_TYPE = [(<span>"Content-Type"</span>,<span>"text/html"</span>)] HTTP_STATUS = { <span>200</span>: <span>"200 OK"</span>, <span>404</span>: <span>"404 Not Found"</span>, } GAME_SERVER_INFO_URL = <span>"http://xxxxxyyyyy"</span>ROLE_INFO_URL = <span>"http://xxyyy?uid=%s&hostnum=%s"</span>red = redis.StrictRedis(host=REDIS.HOST, port=REDIS.PORT, db=REDIS.DB) <span>#游戏服务器IP白名单</span><span>if</span><span>not</span> globals().has_key(<span>"gServerIP"</span>): gServerIP = {} res_data = urllib2.urlopen(GAME_SERVER_INFO_URL) res = res_data.read() res_list = res.split(<span>"\n"</span>) <span>for</span> val <span>in</span> res_list: <span>if</span><span>not</span> val: <span>continue</span> _, port, ip, _ = val.split(<span>" "</span>) gServerIP[ip] = port gGMIP = { <span>"xxxxyyyy"</span> : <span>1</span>, } <span><span>def</span><span>is_gm_account_exist</span><span>(account_id)</span>:</span><span>if</span> red.get(<span>"gm_account:%s:name"</span> % account_id): <span>return</span><span>1</span><span>return</span><span>0</span><span><span>def</span><span>is_inter_server</span><span>(hostnum)</span>:</span><span>if</span> ( int(hostnum) >= <span>1000</span> ): <span>return</span><span>0</span><span>return</span><span>1</span><span><span>def</span><span>check_is_int</span><span>(account_id)</span>:</span><span>try</span>: int(account_id) <span>except</span>: <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>0</span>, <span>"errno"</span>: -<span>1</span>}) <span>return</span> () <span>#gm client ensures the id is unique</span><span><span>def</span><span>gm_create_account</span><span>(env, params)</span>:</span> account_id, account_name = params[<span>"gm_account"</span>], urllib.unquote(params[<span>"gm_name"</span>]) check_res = check_is_int(account_id) <span>if</span> check_res: <span>return</span> check_res <span>if</span> is_gm_account_exist(account_id): <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>0</span>, <span>"errno"</span>: <span>1</span>}) <span>#1 the role exists</span> red.set(<span>"gm_account:%s:name"</span> % account_id, account_name) red.sadd(<span>"gm_online_list"</span>, account_id) <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>1</span>}) <span>#check param</span><span><span>def</span><span>gm_add_friend</span><span>(env, params)</span>:</span> var = gm_account, hostnum, usernum = params[<span>"gm_account"</span>], params[<span>"host"</span>], params[<span>"uid"</span>] <span>for</span> num <span>in</span> var: check_res = check_is_int(num) <span>if</span> check_res: <span>return</span> check_res <span>if</span><span>not</span> is_gm_account_exist(gm_account): <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>0</span>, <span>"errno"</span>: <span>2</span>}) <span>#2 the role doesn't exist</span><span>if</span> ( red.get(<span>"gs_usernum:%s:friend"</span> % usernum) ): <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>0</span>, <span>"errno"</span>: <span>3</span>}) <span>#3 the usernum has gotten a friend</span><span>#内服计费没存数据,就不处理了</span><span>if</span><span>not</span> is_inter_server(hostnum): http_res_data = urllib2.urlopen(ROLE_INFO_URL % (usernum, hostnum)) res = loads(http_res_data.read()) <span>if</span> (type(res) != type({})) <span>or</span> (res.get(<span>"code"</span>, <span>0</span>) != <span>1</span>): <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>0</span>, <span>"errno"</span>: <span>4</span>}) <span>#4 the uid doesn't exist</span> red.sadd(<span>"gm_account:%s:friend"</span> % gm_account, usernum) <span>#两边都处理下</span> red.sadd(<span>"gs_hostnum:%s"</span> % hostnum, usernum) <span>#记录该服务器的所有玩家</span> red.set(<span>"gs_usernum:%s:hostnum"</span> % usernum, hostnum) <span>#该玩家的信息</span> red.set(<span>"gs_usernum:%s:friend"</span> % usernum, gm_account) <span>#一个玩家只能被一个gm添加为好友</span> red.sadd(<span>"apply_frd_list"</span>, usernum) <span>#usernum will be added </span> red.hdel(<span>"remove_frd_list"</span>, usernum) <span>#信息残留</span><span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>1</span>}) <span><span>def</span><span>gm_remove_friend</span><span>(env, params)</span>:</span> account_id, uid = params[<span>"gm_account"</span>], params[<span>"uid"</span>] <span>if</span><span>not</span> is_gm_account_exist(account_id): <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>0</span>, <span>"errno"</span>: <span>2</span>}) <span>#2 the role doesn't exist</span><span>if</span> red.get(<span>"gs_usernum:%s:friend"</span> % uid) != account_id: <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>0</span>, <span>"errno"</span>: <span>5</span>}) <span># the usernum has friend but isn't the gm</span><span>if</span><span>not</span> red.srem(<span>"gm_account:%s:friend"</span> % account_id, uid): <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>0</span>, <span>"errno"</span>: <span>4</span>}) <span># the usernum is not a friend of the gm</span> hostnum = red.get(<span>"gs_usernum:%s:hostnum"</span> % uid) red.delete(<span>"gs_usernum:%s:hostnum"</span> % uid) <span>#合服考虑,如果合服了GM手动删除这个玩家吧</span> red.srem(<span>"gs_hostnum:%s"</span> % hostnum, uid) red.delete(<span>"gs_usernum:%s:friend"</span> % uid) red.hset(<span>"remove_frd_list"</span>, uid, hostnum) <span>#uid的信息已经丢失,先额外保存下hostnum信息</span> red.srem(<span>"apply_frd_list"</span>, uid) <span>#信息残留</span><span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>1</span>}) <span>#GM账号很少</span><span><span>def</span><span>gm_online</span><span>(env, params)</span>:</span> account_id = params[<span>"gm_account"</span>] <span>#可能客户端bug没发下线,直接sadd吧</span><span>if</span><span>not</span> is_gm_account_exist(account_id): <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>0</span>, <span>"errno"</span>: <span>2</span>}) <span>#2 the role doesn't exist</span> red.sadd(<span>"gm_online_list"</span>, account_id) <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>1</span>}) <span><span>def</span><span>gm_offline</span><span>(env, params)</span>:</span> account_id = params[<span>"gm_account"</span>] <span>if</span><span>not</span> red.srem(<span>"gm_online_list"</span>, account_id): <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>0</span>}) <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>1</span>}) <span>#存在usernum上,gs_msg和gm_msg</span><span><span>def</span><span>gm_sendmsg</span><span>(env, params)</span>:</span> account_id, uid, msg = params[<span>"gm_account"</span>], params[<span>"uid"</span>], urllib.unquote(params[<span>"msg"</span>]) <span>#只能向好友发</span><span>if</span><span>not</span> red.sismember(<span>"gm_account:%s:friend"</span> % account_id, uid): <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>0</span>, <span>"errno"</span>: <span>4</span>}) <span># the usernum is not a friend of the gm</span><span>if</span> red.get(<span>"gs_usernum:%s:friend"</span> % uid) != account_id: <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>0</span>, <span>"errno"</span>: <span>5</span>}) <span># the usernum has friend but isn't the gm or doesn't have</span> red.lpush(<span>"gs_usernum:%s:msg_from_gm"</span> % uid, msg) red.sadd(<span>"gm_newmsg_list"</span>, uid) <span>#gs get msg from this set</span><span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>1</span>}) <span>#gm轮训所有的,他那边还有个服务器...</span><span>#{gm_account:{"uid": msg, "uid2": msg2}}</span><span><span>def</span><span>gm_receivemsg</span><span>(env, params)</span>:</span> user_set = copy.copy(red.smembers(<span>"gs_newmsg_list"</span>)) msg_data = {} <span>for</span> uid <span>in</span> user_set: gm_account = red.get(<span>"gs_usernum:%s:friend"</span> % uid) <span>if</span><span>not</span> gm_account: <span>#理论上是不会</span><span>continue</span> msg_list = pop_msg(uid, <span>"msg_from_gs"</span>) send_msg = MSG_SEPARATOR.join(msg_list) <span>if</span><span>not</span> send_msg: <span>continue</span><span>if</span><span>not</span> gm_account <span>in</span> msg_data: msg_data[gm_account] = [] msg_data[gm_account].append({<span>"uid"</span> : uid, <span>"msg"</span> : send_msg}) <span>#red.srem("gs_newmsg_list", uid)</span><span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>1</span>, <span>"data"</span>: base64.b64encode(dumps(msg_data))}) <span><span>def</span><span>pop_msg</span><span>(uid, msg_type)</span>:</span> msg_list = [] msg_key = <span>"gs_usernum:%s:%s"</span> % (uid, msg_type) msg_len = min(MAX_RECV_AMOUNT, red.llen(msg_key)) <span>for</span> i <span>in</span> xrange(msg_len): piece_msg = red.rpop(msg_key) msg_list.append(piece_msg) <span>return</span> msg_list <span>#---------------------GS----------------------</span><span>#apply and remove</span><span><span>def</span><span>get_frd_relation</span><span>(env, params)</span>:</span> host = params[<span>"host"</span>] apply_user_set = copy.copy(red.smembers(<span>"apply_frd_list"</span>)) apply_data = {} <span>#{"res":1 "data":base64({uid: gm_account})}</span><span>for</span> uid <span>in</span> apply_user_set: hostnum = red.get(<span>"gs_usernum:%s:hostnum"</span> % uid) <span>if</span> hostnum != host: <span>continue</span> account_id = red.get(<span>"gs_usernum:%s:friend"</span> % uid) <span>if</span><span>not</span> account_id: <span>#error </span><span>continue</span> apply_data[uid] = [account_id, red.get(<span>"gm_account:%s:name"</span> % account_id)] red.srem(<span>"apply_frd_list"</span>, uid) del_user_list = red.hkeys(<span>"remove_frd_list"</span>) remove_list = [] <span>for</span> uid <span>in</span> del_user_list: hostnum = red.hget(<span>"remove_frd_list"</span>, uid) <span>if</span> hostnum != host: <span>continue</span> remove_list.append(uid) red.hdel(<span>"remove_frd_list"</span>, uid) <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>1</span>, <span>"apply_data"</span>: base64.b64encode(dumps(apply_data)), <span>"remove_data"</span>: base64.b64encode(dumps(remove_list))}) <span><span>def</span><span>gs_sendmsg</span><span>(env, params)</span>:</span> uid, msg = params[<span>"uid"</span>], urllib.unquote(params[<span>"msg"</span>]) <span>if</span><span>not</span> red.get(<span>"gs_usernum:%s:friend"</span> % uid): <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>0</span>, <span>"errno"</span>: <span>5</span>}) <span># the usernum has friend but isn't the gm or doesn't have</span> red.lpush(<span>"gs_usernum:%s:msg_from_gs"</span> % uid, msg) red.sadd(<span>"gs_newmsg_list"</span>, uid) <span>#gm get msg from this set</span><span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>1</span>}) <span><span>def</span><span>gs_receivemsg</span><span>(env, params)</span>:</span> host = params[<span>"host"</span>] user_set = copy.copy(red.smembers(<span>"gm_newmsg_list"</span>)) total_msg_list = [] <span>for</span> uid <span>in</span> user_set: hostnum = red.get(<span>"gs_usernum:%s:hostnum"</span> % uid) <span>if</span> hostnum != host: <span>continue</span> msg_list = pop_msg(uid, <span>"msg_from_gm"</span>) user_msg = MSG_SEPARATOR.join(msg_list) <span>if</span><span>not</span> user_msg: <span>continue</span> msg_data = { <span>"uid"</span> : uid, <span>"msg"</span> : user_msg, } total_msg_list.append(msg_data) <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>1</span>, <span>"data"</span>: base64.b64encode(dumps(total_msg_list))}) <span><span>def</span><span>get_online_list</span><span>(env, params)</span>:</span> host = params[<span>"host"</span>] send_list = [] online_list = red.smembers(<span>"gm_online_list"</span>) <span>for</span> account_id <span>in</span> online_list: frd_set = red.smembers(<span>"gm_account:%s:friend"</span> % account_id) <span>for</span> uid <span>in</span> frd_set: <span>if</span> red.get(<span>"gs_usernum:%s:hostnum"</span> % uid) == host: send_list.append(account_id) <span>#只有这个服务器有gm的好友,才通知</span><span>break</span><span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, dumps({<span>"res"</span>: <span>1</span>, <span>"data"</span>: base64.b64encode(dumps(send_list))}) <span>#get: action=create&gm_account&gm_name 创建账号</span><span>#get: action=add&gm_account&host&uid 添加好友</span><span>#get: action=del&gm_account&uid 删除好友</span><span>#get: action=online&gm_account上线</span><span>#get: action=offline&gm_account 下线</span><span>#get: action=send&gm_account&uid&msg 发送消息</span><span>#get: action=receive 轮训消息</span> GM_FUNC = { <span>"create"</span> : gm_create_account, <span>"add"</span> : gm_add_friend, <span>"del"</span> : gm_remove_friend, <span>"online"</span> : gm_online, <span>"offline"</span> : gm_offline, <span>"send"</span> : gm_sendmsg, <span>"receive"</span> : gm_receivemsg, } <span><span>def</span><span>handle_gm_ticket</span><span>(env, params)</span>:</span><span>if</span><span>not</span> gGMIP.get(env[<span>"REMOTE_ADDR"</span>], <span>0</span>): <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, <span>"%s has no access to the website"</span> % env[<span>"REMOTE_ADDR"</span>] func = GM_FUNC.get(params[<span>"action"</span>], <span>None</span>) <span>if</span><span>not</span> func: <span>return</span> HTTP_STATUS[<span>404</span>], CONTENT_TYPE, <span>"err action %s"</span> % params[<span>"action"</span>] <span>return</span> func(env, params) <span>#get action=relation&host</span><span>#get action=send&uid&msg</span><span>#get action=receive&host</span><span>#get action=online&host </span> GS_FUNC = { <span>"relation"</span> : get_frd_relation, <span>"send"</span> : gs_sendmsg, <span>"receive"</span> : gs_receivemsg, <span>"online"</span> : get_online_list, } <span><span>def</span><span>handle_gs_ticket</span><span>(env, params)</span>:</span><span>if</span><span>not</span> gServerIP.get(env[<span>"REMOTE_ADDR"</span>], <span>0</span>): <span>return</span> HTTP_STATUS[<span>200</span>], CONTENT_TYPE, <span>"%s has no access to the website"</span> % env[<span>"REMOTE_ADDR"</span>] func = GS_FUNC.get(params[<span>"action"</span>], <span>None</span>) <span>if</span><span>not</span> func: <span>return</span> HTTP_STATUS[<span>404</span>], CONTENT_TYPE, <span>"err action %s"</span> % params[<span>"action"</span>] <span>return</span> func(env, params) </code>
Copyright Statement: This article is an original article by the blogger and may not be reproduced without the permission of the blogger.
The above introduces the use of nginx+uwsgi+redis to implement the game GM chat function, including various aspects. I hope it will be helpful to friends who are interested in PHP tutorials.

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics











The usage of return in C language is: 1. For functions whose return value type is void, you can use the return statement to end the execution of the function early; 2. For functions whose return value type is not void, the function of the return statement is to end the execution of the function. The result is returned to the caller; 3. End the execution of the function early. Inside the function, we can use the return statement to end the execution of the function early, even if the function does not return a value.

Source code: publicclassReturnFinallyDemo{publicstaticvoidmain(String[]args){System.out.println(case1());}publicstaticintcase1(){intx;try{x=1;returnx;}finally{x=3;}}}#Output The output of the above code can simply conclude: return is executed before finally. Let's take a look at what happens at the bytecode level. The following intercepts part of the bytecode of the case1 method, and compares the source code to annotate the meaning of each instruction in

Vue3.2 setup syntax sugar is a compile-time syntax sugar that uses the combined API in a single file component (SFC) to solve the cumbersome setup in Vue3.0. The declared variables, functions, and content introduced by import are exposed through return, so that they can be used in Vue3.0. Problems in use 1. There is no need to return declared variables, functions and content introduced by import during use. You can use syntactic sugar //import the content introduced import{getToday}from'./utils'//variable constmsg='Hello !'//function func

Usage of return in JavaScript requires specific code examples In JavaScript, the return statement is used to specify the value returned from a function. Not only can it be used to end the execution of a function, it can also return a value to the place where the function was called. The return statement has the following common uses: Return a value The return statement can be used to return a value to the place where the function is called. Here is a simple example: functionadd(a,b){

JavaScript functions provide two interfaces to interact with the outside world. The parameters serve as the entrance to receive external information; the return value serves as the outlet to feed back the operation results to the outside world. The following article will take you to understand the JavaScript function return value and briefly analyze the usage of the return statement. I hope it will be helpful to you!

How to use return in JavaScript requires specific code examples. In JavaScript, return is a very important keyword. It is usually used to return a value in a function or end the execution of a function. The return statement is used to return a value to the caller of the function and terminate the execution of the function. The return statement can be used anywhere in a function and can return any JavaScript data type, including numbers, strings, booleans,

Python return value return usage is that when the function executes the return statement, execution will stop immediately and the specified value will be returned to the place where the function was called. Detailed usage: 1. Return a single value; 2. Return multiple values; 3. Return a null value; 4. End the execution of the function early.

What currency is MSG? MSG is a cryptocurrency, whose full name is MessageCoin. It is a digital currency based on blockchain technology and has the characteristics of decentralization, anonymity and security. MSG's mission is to promote secure and efficient peer-to-peer communication and value transfer on a global scale. Is MSG Coin worth investing in? Investing in MSG Coin requires comprehensive consideration of multiple factors, including market prospects, technical strength and team background. The market potential of MSG coins has attracted much attention. As the cryptocurrency industry develops rapidly, global demand for decentralized communications and payments continues to rise. As a digital currency focusing on communication and value transfer, MSG has a wide range of application prospects. The technical strength of MSG coin is also one of the important factors that attracts users.
