大家好,我是查理~Facebook更名meta,顿时点燃了元界的概念。今天我将用Python实现一个简单的迷你虚拟世界。代码简洁易懂。不仅可以学习Python知识,还可以通过实践来理解Metaverse的概念。还等什么,现在就开始吧!迷你元界什么是元界?不同的人有不同的理解和理解,但最常见的是:元宇宙是一个接入点,每个人都可以成为其中的一个元素,相互影响。那么我们的元宇宙有哪些功能呢?首先,必须有一个可以访问的函数。然后可以相互交换信息。比如a给b发消息,b可以给a发消息,同时广播消息,即成员可以私信和群聊。另外,元界成员可以收到元界动态,比如有新人加入,有人离开等,玩腻了可以离开元界。最终的效果是这样的:可能很难直接去思考如何设计一个接入点。从另一个角度来看,接入点实际上是一台服务器。只要我们在线,就无时无刻不在和服务器打交道。然后选择最简单的TCP服务器。TCP服务器的核心是维护套接字(socket)的状态,向其发送或获取信息。Python的socket库提供了很多方便的方法可以帮助我们构建。核心代码如下:importsocketsocket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)socket.bind((ip,port))socket.listen()data=socket.recv(1024)...创建一个socket,让它监听一个本机拥有的ip和端口,然后读取socket发送过来的数据。如何搭建客户端客户端是用户连接元界的工具。在这里,它是一个可以连接到服务器的工具。服务端是一个TCP服务器,客户端自然需要使用能够连接到TCP服务器的方法。Python也已经为我们准备好了,几行代码就可以搞定,核心代码如下:=client.recv(1024)...代码和服务器很相似,只是连接到一个服务器的ip和端口。如何构建业务逻辑,首先需要服务端对访问用户进行管理。然后当收到一条用户消息时,判断是转发给其他用户,广播还是回复。这样就需要构造一个消息格式来表示用户消息的类型或目的。我们使用@username的格式来区分消息是发送给特定用户还是发送给某个群组。另外,为了完成注册功能,还需要定义另外一种设置用户名的命令格式。我们可以使用name:username的格式作为设置用户名的命令。构建有了初步设计,就可以进一步构建我们的代码了。服务器服务器需要同时响应多个连接,包括新建连接、消息和连接断开等,为了不阻塞服务器,我们使用非阻塞连接。连接上后,存储连接,然后用select工具等待有消息的连接。simpletcp[1]已经实现了这个功能,只需要稍作改动即可。把接收到的消息,建立链接,关闭链接做成一个回调方法,这样就可以对外写业务逻辑了。核心业务核心代码如下:#创建服务器链接self._socket=socket.socket(socket.AF_INET,socket.SOCK_STREAM)self._socket.setblocking(0)self._socket.bind((self.ip,self.port))self._socket.listen(self._max_connections)#storeestablishedconnectionsreaders=[self._socket]#storeclientipandportIPs=dict()#exitflag用于关闭服务器self._stop=False#Servermainloopwhilereadersandnotself._stop:#使用select从已建立的链接中选择一些有新消息的reads,_,err=select.select(readers,[],readers)forsockinread:ifsockisself._socket:#建立新连接#获取socket和新连接的ip和端口client_socket,client_ip=self._socket.accept()#设置连接为非阻塞client_socket.setblocking(0)#添加到监听队列readers.append(client_socket)#存储ip信息IPs[client_socket]=client_ip#调用连接建立回调函数self.onCreateConn(self,client_socket,client_ip)else:#收到一条新消息try:#获取消息data=sock.recv(self.recv_bytes)exceptsocket.errorase:ife.errno==errno.ECONNRESET:#表示连接已经退出data=Noneelse:raiseeifdata:#调用回调函数self收到消息时.onReceiveMsg(self,sock,IPs[sock],data)else:#连接退出时,移除监听队列readers.remove(sock)sock.close()#调用连接关闭回调函数self.onCloseConn(self,sock,IPs[sock])#Handlethewronglinkforsockinerr:#移除监听队列readers.remove(sock)sock.close()#调用链接关闭回调函数self.onCloseConn(self,sock,IPS[sock])首先使用socket建立服务器连接,和原来的socket核心代码一样,连接设置为非阻塞的,这样可以同时监听多个连接同时通过select而不会阻塞服务器。关于select,可以看这里[2]在主循环中,过滤掉有消息的链接,判断是建立链接还是发送消息,调用不同的回调函数,最后处理异常事件处理现在通过回调功能,可以写业务,看中间的代码。这一段是建立链接时的处理:defonCreateConn(server,sock,ip):cid=f'{ip[0]}_{ip[1]}'clients[cid]={'cid':cid,'sock':sock,'name':None}sock.send("你已经连接到元界了,告诉我你的代号,输入格式是name:lily。".encode('utf-8'))先计算客户端id,即cid,是由ip和port组成的字典。Clients是一个字典,以cid为key,存放cid、link、name。建立链接后,向链接发送问候语,并要求其设置自己的名称,然后接收消息的回调函数比较复杂,主要是处理的情况比较多:defonReceiveMsg(server,sock,ip,data):cid=f'{ip[0]}_{ip[1]}'data=data.decode('utf-8')print(f"接收到的数据:{data}")_from=clients[cid]ifdata.startswith('name:'):#setthenamename=data[5:].strip()ifnotname:sock.send(f"不能设置空名,否则别人无法找到你".encode('utf-8'))elifnotcheckname(name,cid):sock.send(f"名字{name}已经被使用,请换一个试试。".encode('utf-8'))else:ifnot_from['name']:sock.send(f"{name}很高兴见到你现在你可以享受元节了".encode('utf-8'))msg??=f"新成员{name}加入了元宇宙,和他聊天吧。".encode('utf-8')sendMsg(msg,_from)else:sock.send(f"改名完成".encode('utf-8'))msg??=f"{_from['name']}把名字改成{name},和TA聊聊".encode('utf-8')sendMsg(msg,_from)_from['name']=nameelif'@'indata:#privatemessagetargets=re.findall(r'@(.+?)',data)print(targets)msg??=f"{_from['name']}:{data}".encode('utf-8')sendMsg(msg,_from,targets)else:#群消息msg=f"{_from['name']}:{data}".encode('utf-8')sendMsg(msg,_from)代码分为两部分,if前面是处理接收到的消息,和将字节转换为字符串;if开始处理具体如果你收到以name:开头的消息,说明你需要设置用户名,包括权重判断,给其他成员发送消息。如果收到的消息中有@,说明你是发私信,先提取需要发送的用户。然后将消息发送给相应的用户。如果没有特殊标记,则表示群发。sendMsg用于发送消息,接收三个参数。第一个是message,第二个是sender,第三个是receivername数组,链接的时候需要处理关闭回调函数:defonCloseConn(server,sock,ip):cid=f'{ip[0]}_{ip[1]}'name=clients[cid]['name']ifname:msg=f"{name}从元节中消失".encode('utf-8')sendMsg(msg,clients[cid])delclients[cid]当收到断开链接消息时,合成消息,将其发送给其他用户,然后从客户端缓存中删除客户端。客户端需要解决两个问题,第一个是处理接收到的消息,第二个是允许用户输入。我们将以线程的形式接收消息,并将用户输入作为主循环。接收消息,先看接收消息的代码:defreceive(client):whileTrue:try:s_info=client.recv(1024)#接受来自服务器的消息,如果不是s_info:print(f"{bcolors.WARNING}serverlinkBreak{bcolors.ENDC}")breakprint(f"{bcolors.OKCYAN}newmessage:{bcolors.ENDC}\n",bcolors.OKGREEN+s_info.decode('utf-8')+bcolors.ENDC)exceptException:print(f"{bcolors.WARNING}服务器链接断开{bcolors.ENDC}")breakifclose:break这是线程中使用的代码,接收客户端链接作为参数并从从链接获取信息继续。如果没有消息,recv方法将阻塞直到新消息到达。收到消息后,将消息写入控制台。bcolors提供了一些颜色标记来以不同的颜色显示消息。close是一个全局标志,如果客户端需要退出,它会被设置为True,让线程结束输入处理我们来看看输入控制程序:whileTrue:passvalue=input("")value=value.strip()ifvalue==':start':ifthread:print(f"{bcolors.OKBLUE}你已经在metaverse{bcolors.ENDC}")else:client=createClient(ip,5000)thread=线程(目标=接收,args=(客户端,))thread.start()print(f"{bcolors.OKBLUE}你已经进入元节{bcolors.ENDC}")elifvalue==':quit'orvalue==':stop':ifthread:client.close()close=Trueprint(f"{bcolors.OKBLUE}isexiting...{bcolors.ENDC}")thread.join()print(f"{bcolors.OKBLUE}Metaverse已退出{bcolors.ENDC}")thread=Noneifvalue==':quit':print(f"{bcolors.OKBLUE}exitprogram{bcolors.ENDC}")breakpasselifvalue==':help':help()else:ifclient:#聊天模式client.send(value.encode('utf-8'))else:print(f'{bcolors.WARNING}尚未连接元界,请输入:开始连接{bcolors.ENDC}')client.close()主要它是对不同命令的响应。例如:start表示需要建立链接,:quit表示退出等命令前加:以区别于一般消息。编码后就可以启动了。最终代码由三部分组成。第一部分是服务端核心代码,存放在simpletcp.py中。第二部分是服务端业务代码,存放在metaServer.py中。第三部分是客户端代码,存放在metaClient.py中。另外还需要一些辅助处理,比如发送消息的sendMsg方法,颜色处理方法等,具体可以下载本文源码。进入代码目录,启动命令行,执行pythonmetaServer.py,输入命令start:然后打开命令行,执行pythonmetaClient.py,输入命令:start,就可以访问Metaverse了:设置自己的name:如果有新成员加入,会有消息提醒,还可以玩一些互动:怎么样,这样就形成了一个元宇宙,让其他小伙伴加入试试看。综上所述,Metaverse是现在很火的一个概念,但它仍然建立在现有技术的基础上。元宇宙为人们提供了生活在虚拟魔法世界中的想象空间。其实自从有了互联网,我们就逐渐生活在了Metaverse中。今天我们使用基础的TCP技术搭建自己的元界聊天室。虽然功能与想象中的元界相去甚远,但主要功能已经成型。如果大家有兴趣,可以在此基础上增加更多好玩的功能,比如好友、群组、消息记录等,让这个元宇宙变得更好玩的同时,也能获得更深入的了解。
