Mercurial > touhou
view pytouhou/network.py @ 792:11bc22bad1bf default tip
python: Replace the image crate with png
We weren’t using any of its features anyway, so the png crate is exactly what
we need, without the many heavy dependencies of image.
https://github.com/image-rs/image-png/pull/670 will eventually make it even
faster to build.
| author | Link Mauve <linkmauve@linkmauve.fr> |
|---|---|
| date | Sat, 17 Jan 2026 22:22:25 +0100 |
| parents | d18c0bf11138 |
| children |
line wrap: on
line source
import socket from struct import Struct from select import select from time import time from pytouhou.utils.helpers import get_logger logger = get_logger(__name__) MSG_STRUCT = Struct('!HHH') class Network: def __init__(self, port=8080, dest=None, selected_player=0): self.frame = 0 self.keystate = 0 self.old_keystate = 0 self.remote_keystate = 0 self.selected_player = selected_player self.remote_addr = dest self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM, socket.IPPROTO_UDP) self.sock.bind(('', port)) def read_message(self): message = None start_time = time() delta = 1./60. rlist, _, _ = select([self.sock], [], [], delta) while rlist: msg, addr = rlist[0].recvfrom(MSG_STRUCT.size) # Check whether the message comes from the right address if self.frame == 0 or addr == self.remote_addr: self.remote_addr = addr frame, keystate, old_keystate = MSG_STRUCT.unpack(msg) # Check for well-formedness if frame in (self.frame, self.frame + 1): message = (frame, keystate, old_keystate) else: logger.error('Mismatch, got a message from %s, waiting for %s.', self.remote_addr, addr) # If no valid message has been read, wait for one as long as possible # else, read as much as we can without blocking. delta = 0 if message else max(0, 1./60. - (time() - start_time)) rlist, _, _ = select(rlist, [], [], delta) return message def send_message(self): if self.remote_addr is not None: self.sock.sendto(MSG_STRUCT.pack(self.frame, self.keystate, self.old_keystate), self.remote_addr) def run_game_iter(self, game, keystate, other_keystate): keystates = [other_keystate, other_keystate] keystates[self.selected_player] = keystate game.run_iter(keystates) def run_iter(self, game, keystate): if game.frame % 3 == 0: # Phase 1: Update game with old data self.run_game_iter(game, self.keystate, self.remote_keystate) elif game.frame % 3 == 1: # Phase 2: Update data, send new data, update game with old data self.old_keystate, self.keystate = self.keystate, keystate self.frame = game.frame // 3 self.send_message() self.run_game_iter(game, self.old_keystate, self.remote_keystate) elif game.frame % 3 == 2: # Phase 3: Send new data, get remote data, update game with new data self.send_message() # Follow one valid update message = self.read_message() if message: frame, keystate, old_keystate = message if frame == self.frame: self.remote_keystate = keystate elif frame == self.frame + 1: self.remote_keystate = old_keystate else: raise Exception #TODO self.run_game_iter(game, self.keystate, self.remote_keystate) elif game.frame > 2: logger.warning('Message not received in time, dropping frame.')
