Sta*_*kov 2 python udp tcp twisted server
我正在开发一个实时MMO游戏,并且有一个工作的TCP服务器(以及游戏客户端),但现在我正在考虑使用UDP来不断更新其他玩家的位置(从TCP拥塞控制中大大减少随机游戏填充!)我喜欢这些东西比我更聪明的人的帮助(我是python/twisted的新手,在其他地方找不到这个信息;))
目前,我的服务器接受一个简单的Twisted协议连接.例如.
''' TCP reciever '''
class TCPProtocol(Protocol):
def connectionMade(self):
#add to list of connected clients
factory.clients.append(self)
def dataReceived(self, data):
pass
#setup factory and TCP protocol class
factory = Factory()
factory.protocol = TCPProtocol
factory.clients = []
reactor.listenTCP(1959, factory)
Run Code Online (Sandbox Code Playgroud)
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ 请在下面的代码示例中启动我的内容:(在这里说"请帮忙!") 我是否错误地想到了这一点?任何指导都很棒,谢谢!
from twisted.internet.protocol import DatagramProtocol
from twisted.internet import reactor
KServerIP='127.0.0.1'
KServerPortTCP=8000
#KClientPortUDP=7055
''' TCP reciever '''
class TCPProtocol(Protocol):
def connectionMade(self):
#add to list of connected clients
factory.clients.append(self)
#set client variables (simplified)
self.pos_x=100
self.pos_y=100
#add to game room (to recieve updates)
mainGameRoom.clientsInRoom.append(self)
def dataReceived(self, data):
pass
#send custom byte message to client (I have special class to read it)
def sendMessageToClient(self, message, isUpdate):
''' @@@@@@@@@@@@@@@@@ HELP HERE PLEASE! @@@@@@@@@@@
if isUpdate and (CHECK IF CLIENT IS CONGESTED??? )
return (and send next update when not congested)'''
'''@@@@@@@@@@@@@@@@@ HELP HERE PLEASE! @@@@@@@@@@@ '''
#if not congested, write update!
msgLen = pack('!I', len(message.data))
self.transport.write(msgLen) #add length before message
self.transport.write(message.data)
#simplified version of my game room
#this room runs the game, and clients recieve pos updates for
#everyone in this room (up to 50 people)
dt_gameloop=1.0/60 #loop time difference
dt_sendClientUpdate=0.1 #update intervar
class GameRoom(object):
#room constants
room_w=1000
room_h=1000
def __init__(self):
super(GameRoom, self).__init__()
#schedule main game loop
l=task.LoopingCall(self.gameLoop)
l.start(dt_gameloop) # call every X seconds
#schedule users update loop
l=task.LoopingCall(self.sendAllClientsPosUpdate)
l.start(dt_sendClientUpdate) # call every X seconds
def gameLoop(self):
#game logic runs here (60 times a second), EG.
for anUpdateClient in self.clientsInRoom:
anUpdateClient.pos_x+=10
anUpdateClient.pos_y+=10
#send position update every 0.1 seconds,
#send all player positions to all clients
def sendAllClientsPosUpdate(self):
message = MessageWriter()
message.writeByte(MessageGameLoopUpdate) #message type
#create one byte message containing info for all players
message.writeInt(len(self.clientsInRoom)) #num items to read
for aClient in self.clientsInRoom:
message.writeInt(aClient.ID)
message.writeFloat( aCell.pos_x )#pos
message.writeFloat( aCell.pos_y )
#send message to all clients
for aClient in self.clientsInRoom:
aClient.sendMessageToClient(message, True)
#setup factory and TCP protocol class
factory = Factory()
factory.protocol = TCPProtocol
factory.clients = []
reactor.listenTCP(KServerPortTCP, factory)
#simple example, one game room
mainGameRoom=GameRoom()
print "Server started..."
reactor.run()
Run Code Online (Sandbox Code Playgroud)
你说的第一件事是你想"减少来自TCP的网络拥塞".这不是UDP的作用.UDP允许您解决拥塞控制问题,这实际上会增加网络拥塞.在您了解如何实施自己的拥塞控制算法之前,高延迟连接上的UDP流量只会导致数据包风暴,这会使您的服务器不堪重负并淹没用户的网络连接,使其无法使用.
在实时游戏中发送移动数据包的重要一点是,您始终希望确保在新位置已经可用时不要浪费时间"赶上"旧的移动数据包.在Twisted中,您可以使用生产者和消费者API在TCP连接上执行此操作,就像这样:
from zope.interface import implementer
from twisted.internet.protocol import Protocol
from twisted.internet.interfaces import IPullProducer
def serializePosition(position):
"... take a 'position', return some bytes ..."
@implementer(IPullProducer)
class MovementUpdater(Protocol, object):
def updatePosition(self, newPosition):
if newPosition != self.currentPosition:
self.currentPosition = newPosition
self.needToSendPosition()
waitingToSend = False
def needToSendPosition(self):
if not self.waitingToSend:
self.waitingToSend = True
self.transport.registerProducer(self, False)
def resumeProducing(self):
self.transport.write(serializePosition(self.currentPosition))
self.transport.unregisterProducer()
self.waitingToSend = False
def stopProducing(self):
"nothing to do here"
Run Code Online (Sandbox Code Playgroud)
每次游戏需要发送新位置时,它都可以调用updatePosition以更新玩家的当前位置. updatePosition首先更新当前位置,然后调用needToSendPosition哪个标记连接需要发送位置更新.这将协议注册为其传输的生成器,这将导致resumeProducing每次写入缓冲区空间可用时调用.一旦resumeProducing被呼叫,我们发送最新位置的任何内容 - 如果updatePosition在网络拥塞时调用500次,则一旦拥塞减轻,将仅发送一次更新.
这有点过于简单,因为每个transport只能有一个producer,并且您的游戏服务器可能会有很多不同的位置更新发送给客户端,因此您需要一个多路复用器来聚合来自多个客户端的所有位置更新,以及还有一些代码来订购消息,以便除了位置更新之外的其他东西仍然可以通过但位置更新具有优先权.
这似乎是额外的工作,如果你打算做UDP无论如何,但如果你打算做正确UDP,实际上从它那里得到任何好处,你将需要实现的东西很喜欢这个反正,所以这亿韩元"浪费了.