Kev*_*fer 3 python terminal multiplayer game-development
我写了一个非常简单的基于终端的扑克游戏(ascii art ftw),现在它是多人游戏,但你基本上必须通过一台计算机.是否有一种简单的(ish)方式使两个人可以从不同的机器连接并访问同一个游戏同时一起玩?只要我们有终端访问权限,它就不必是花哨的,也不需要是图形化的.
我不确定如何做到这一点,或者它是否实用,但只是想学习和探索一些选择.
aba*_*ert 12
这是一个非常模糊的问题,但我可以给你一些模糊的答案.
首先,您需要设计一个简单的协议.一个非常简单的基于行的协议应该可以正常工作:UTF-8文本,分隔消息的换行符,分隔参数的空格.例如,您可以拥有以下客户端 - >服务器消息:
JOIN name
SAY message with spaces
FOLD
RAISE amount
# ...
Run Code Online (Sandbox Code Playgroud)
...以及这些服务器 - >客户端消息:
OK
ERROR error message
JOINED player name with spaces
LEFT player
SAID player message with spaces
NEWHAND player player player player…
DEALT player face suit
ANTED player amount
CHECKED player
# ...
Run Code Online (Sandbox Code Playgroud)
这样的协议的好处是你可以用telnet或者手动输入nc,所以你甚至不需要客户端进行测试.
现在,您需要构建一个实现该协议的服务器,并将游戏逻辑构建到服务器中.
线程服务器可能是最简单的事情.然后主线开始游戏线程,花费大部分时间阻止Condition等待玩家行动.它还会阻塞accept,为每个连接启动一个新的客户端线程,这些连接大部分时间都会阻塞for line in self.sock.makefile():.Lock在客户端对象内添加一个允许其他线程安全地发送消息.然后你只需要一个带有锁定的客户端对象的集合,你就完成了.
因为我有一个类似设计的聊天服务器,让我调整一些比特来给你一个骨架.
首先,这是整个主线程:
lock = threading.Lock()
clients = []
game = Game()
ssock = socket.socket()
ssock.bind(('', 12345))
ssock.listen(5)
while True:
sock, addr = ssock.accept()
with lock:
clients.append(Client(addr, sock, len(clients))
Run Code Online (Sandbox Code Playgroud)
该Client对象是标准调度程序:
class Client(object):
def __init__(self, addr, sock, number):
self.sock = sock
self.name = '<{}> (not logged in)'.format(addr)
self.number = number
self.lock = threading.Lock()
self.thread = threading.Thread(target=self.serve)
self.thread.start()
def send(self, msg):
with self.lock:
self.sock.send(msg)
def run(self):
for line in self.sock.makefile():
args = line.rstrip().split()
cmd = args.pop().upper()
method = getattr(self, 'do_{}'.format(cmd), None)
if method is none:
self.write('ERROR unknown command {}\n'.format(cmd))
else:
try:
method(*args)
except Exception as e:
self.send('ERROR in {}: {}\n'.format(cmd, e))
else:
self.send('OK\n')
Run Code Online (Sandbox Code Playgroud)
您可能还需要一个broadcast功能:
def broadcast(msg):
with lock:
for client in clients:
client.send(msg)
Run Code Online (Sandbox Code Playgroud)
然后Client为每个命令编写方法.基本上,elif response == 'FOO'菜单代码中的do_FOO每print一个都成为一种方法,每一种方法都变成了一种方法broadcast,并且......就是这样.我稍后会展示一个更复杂的版本,但这里的大部分内容都是这样的:
def do_SAY(self, *msg):
broadcast('SAID {} {}'.format(self.number, ' '.join(msg)))
Run Code Online (Sandbox Code Playgroud)
最后,有一个Game对象.它运行在自己的线程上,就像每个线程一样Client.在大多数情况下,它的run方法与顺序非网络游戏中的逻辑相同.当然你必须打电话broadcast而不是print,但这很容易.唯一棘手的一点是你需要一点同步.
例如,在开始新手之前,你必须复制玩家列表(以及其他一些相关的游戏状态),以便其他线程可以修改它而不影响当前游戏,你还需要等到有足够的玩家所以一个人自己玩,你不会起手.所以:
def new_hand(self):
with self.condition:
while len(self.players) < 2:
self.condition.wait()
players = self.players
# all your existing sequential logic
Run Code Online (Sandbox Code Playgroud)
并且您需要join为客户端添加一个方法来从他们自己的线程调用:
def join(self, player):
with self.condition:
self.players.append(self)
self.condition.notify()
Run Code Online (Sandbox Code Playgroud)
所以,在Client对象中:
def do_JOIN(self, name):
self.name = name
game.join(self)
broadcast('JOINED {} {}'.format(self.number, self.name)
Run Code Online (Sandbox Code Playgroud)
让我们等待尽可能复杂的投注,只是为了看看在最坏的情况下它是多么容易.如果你想在不合适的情况下下注,你可以.每个人都可以看到你的赌注,如果情况发生变化,你就会承诺(例如,如果你打电话,那么你前面的那个人会加注,你就是他的新赌注).那么,这就是我们的工作:
def wait_for_bets(self, bettor):
with self.condition:
while self.bets[self.bettor] is None:
self.condition.wait()
bettor, bet = self.bettor, self.bets[self.bettor]
self.bets[self.bettor] = None
# handle the bet
Run Code Online (Sandbox Code Playgroud)
以下是Client提交投注的方式:
def bet(self, player, bet):
with self.condition:
self.bets[player] = bet
self.condition.notify()
Run Code Online (Sandbox Code Playgroud)
例如,在Client:
def do_FOLD(self):
game.bet(self, 'fold')
Run Code Online (Sandbox Code Playgroud)
显然有很多代码要编写.但重点是,除了上面已经显示的内容或已经存在于现有游戏中之外,没有任何复杂的内容.
| 归档时间: |
|
| 查看次数: |
3418 次 |
| 最近记录: |