コード例 #1
0
class ReplicationManager(GameObjectBase):
    """
    Manages GameObjects that are marked for replication.
    """
    def __init__(self):
        super().__init__()
        self.__replicators = {}
        self.__client = None
        self.__server = None

    def add_object(self, replication_id, gob):
        """Adds a GameObject to be replicated from server to connected clients."""
        self.__replicators[replication_id] = gob

    def __compile_replication_data(self):
        """
        Compiles and returns the dictionary with the data for all the register replicators.

        The server calls this function after all the game objects have been updated. The compiled data is sent by
        the server to all the connected clients.
        """
        replication_data = {}
        for rep_id, gob in self.__replicators.items():
            replication_data[rep_id] = {}
            for rep_prop in gob.get_replicated_props():
                replication_data[rep_id][rep_prop.name] = rep_prop.packer.pack(
                    getattr(gob, rep_prop.getter))
        return replication_data

    def __apply_replication_data(self, game_state):
        """
        Applies the data received by the client to the registered game objects.

        The client calls this function at the end of each game update to ensure that all the replicated game objects
        are in sync with their primary replicas existing on the server.
        """
        for replication_id, replication_data in game_state.items():
            gob = self.__replicators.get(replication_id)
            if gob:
                for rep_prop in gob.get_replicated_props():
                    new_value = replication_data.get(rep_prop.name)
                    if new_value:
                        setattr(gob, rep_prop.setter,
                                rep_prop.packer.unpack(new_value))

    def start_replication(self):
        """Starts replication."""
        # HACK ALERT! #
        if self.__find_local_server():
            # Found a local server running -> connect to it
            self.__client = Client(("localhost", 54879))
            self.__client.connect("Player2")
            logging.debug("Started client")
        else:
            # No local server running -> run one
            self.__server = Server()
            self.__server.start()
            logging.debug("Started server")
        # HACK ALERT! #

    def update(self, delta):
        """Updates the replication manager."""
        if self.__client:
            if self.__client.tick():
                if self.__client.state == ClientState.PLAYING:
                    self.__apply_replication_data(
                        self.__client.get_game_state())
                    self.__client.reset_game_state()
            else:
                self.__client = None

        elif self.__server:
            self.__server.propagate_game_state(
                self.__compile_replication_data())
            self.__server.tick()

    def propagate_input(self, inputs):
        """Sets the list of input actions collected by the game in the current frame. These are sent to the server."""
        if self.__client:
            self.__client.set_inputs(inputs)

    @classmethod
    def __find_local_server(cls):
        """Tries to connect to a local server. Returns True if successful, False otherwise."""
        client = Client(("localhost", 54879))
        client.connect("Player2")

        found_server = False
        try:
            client.tick()
            found_server = True
        except BrokenPipeError:
            pass
        finally:
            client.stop()

        return found_server