view pytouhou/network.py @ 612:73f134f84c7f

Request a RGB888 context, since SDL2’s default of RGB332 sucks. On X11/GLX, it will select the first config available, that is the best one, while on EGL it will iterate over them to select the one closest to what the application requested. Of course, anything lower than RGB888 looks bad and we really don’t want that.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Thu, 26 Mar 2015 20:20:37 +0100
parents 711c75115675
children d1f0bb0b7a17
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(object):
    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.warn('Message not received in time, dropping frame.')