class Client(object): def __init__(self, remote_port=8000, remote_address="127.0.0.1", local_port=9000, local_address="127.0.0.1", run_local=False, logging=False, callbacks={}, **kwargs): self.connection = Connection(ip=remote_address, port=remote_port, callbacks=callbacks, client_id=0) self.dataBuffer = [] self.logging = logging self.callbacks = callbacks def send(self, tag, data): """Communicates the given data to the server""" self.connection.send(tag, dumps(data)) def register(self, func, tag): """Decorator, must include a specific tag. Takes a function that will accept two arguments a tag, and the data itself""" self.callbacks[tag] = func return func def shutdown(self): self.connection.shutdown()
class Client(object): def __init__(self, remote_port=8000, remote_address="127.0.0.1", local_port=9000, local_address="127.0.0.1", run_local=False, logging=False, callbacks={}, **kwargs): self.connection = Connection(ip=remote_address, port=remote_port, callbacks=callbacks, client_id=0) self.dataBuffer = [] self.logging = logging self.callbacks = callbacks def send(self, tag, data): """Communicates the given data to the server""" self.connection.send(tag, dumps(data)) def register(self, func, tag): """Decorator, must include a specific tag. Takes a function that will accept two arguments a tag, and the data itself""" self.callbacks[tag] = func return func def shutdown(self): self.connection.shutdown()
class GameState(): user_tile_type: TileState turn: TileState winner: TileState _state: List[TileState] _board_size: int _listener: Callable multi_palyer_connection: Connection = None def __init__(self, board_size: int, listener: Callable, is_server: bool = None) -> None: self._board_size = board_size self._state = [TileState.EMPTY] * (board_size * board_size) self._listener = listener self.winner = None self.turn = TileState.X self.user_tile_type = TileState.X self.setup_multiplayer_connection(is_server) def setup_multiplayer_connection(self, is_server: bool = None): if is_server != None: self.multi_palyer_connection = Connection() if is_server: self.multi_palyer_connection.serve("localhost", 8080) self.multi_palyer_connection.wait_client_thread.join() else: self.multi_palyer_connection.connect("localhost", 8080) self.user_tile_type = TileState.O self.multi_palyer_connection.recive_listener = self.multiplayer_event def multiplayer_event(self, payload): if payload['packet'] == Packets.TILE_CHANGE: tile_type = payload['tile_type'] x = payload['x'] y = payload['y'] self.set(x, y, tile_type, from_network=True) def new_game(self): self._state = [TileState.EMPTY] * (self._board_size * self._board_size) self.winner = None self._listener() def _index(self, x, y): return y * self._board_size + x def get(self, x: int, y: int): index = self._index(x, y) return self._state[index] def set(self, x: int, y: int, state: TileState, /, from_network=False):
def setup_multiplayer_connection(self, is_server: bool = None): if is_server != None: self.multi_palyer_connection = Connection() if is_server: self.multi_palyer_connection.serve("localhost", 8080) self.multi_palyer_connection.wait_client_thread.join() else: self.multi_palyer_connection.connect("localhost", 8080) self.user_tile_type = TileState.O self.multi_palyer_connection.recive_listener = self.multiplayer_event
def __init__(self, remote_port=8000, remote_address="127.0.0.1", local_port=9000, local_address="127.0.0.1", run_local=False, logging=False, callbacks={}, **kwargs): self.connection = Connection(ip=remote_address, port=remote_port, callbacks=callbacks, client_id=0) self.dataBuffer = [] self.logging = logging self.callbacks = callbacks
def main(character, config): renderer = Renderer(config['Renderer']) conn = Connection(config['Network']) proxy = MessageProxy(conn) input_mgr = InputManager() res_mgr = ResourceManager(config['Game']) audio_mgr = AudioManager(config['Sound']) client = Client( character, renderer, proxy, input_mgr, res_mgr, audio_mgr, config) client.start()
def __init__(self, remote_port=8000, remote_address="127.0.0.1", local_port=9000, local_address="127.0.0.1", run_local=False, logging=False, callbacks={}, **kwargs): self.connection = Connection(ip=remote_address, port=remote_port, callbacks=callbacks, client_id=0) self.dataBuffer = [] self.logging = logging self.callbacks = callbacks
def login(self, character_name, password): # Connect self._conn = Connection() self._conn.connect() # Send login packet login_packet = OutgoingPacket() login_packet.add_u8(0x0A) login_packet.add_u8(0) login_packet.add_u8(0) login_packet.add_u16(0) login_packet.add_u8(0) login_packet.add_string(character_name) login_packet.add_string(password) self._conn.send_packet(login_packet) # Receive login response while True: packet = self._conn.recv_packet() if packet: break time.sleep(0.1) packet_type = packet.get_u8() if packet_type == 0x14: error_message = packet.get_string() print("login: could not login: \"{}\"".format(error_message)) self._conn.close() self._conn = None return False elif packet_type == 0x0A: self._parse_login_packet(packet) return True else: print("login: unknown packet type: {}".format(packet_type)) self._conn.close() self._conn = None return False
def __init__(self, id, port, ids, ports, on_request, on_release, logger=None): """ :param id: self id :param port: self port :param ids: ids of other processes :param ports: ports of other processes :param on_request: callback function :param on_release: callback function """ self.__number_of_processes = len(ids) self.ids = ids self.on_request = on_request self.on_release = on_release self.logger = logger self.connection = Connection(id, port, ids, ports, self.on_response, logger)
class API: """ Actually it is not pure RPC class. At least normally RPC layer doesn't suppose incoming messages that aren't answers for our outgoing requests. It means that in our case we must have some callback for them anyway (or maybe futures). Moreover some of our requests don't suppose answer (e.g. 'release'). Therefore I guess it would be strange to await every confirmation before sending next request during broadcast messaging. (Can consider current implementation of 'request' method as asynchronous remote calls :)) Anyway from the outside calls of the functions from this class looks exactly like RPC because they return only after receiving response (if it's supposed to be there). """ # constants __REQUEST = 0 __RELEASE = 1 __CONFIRM = 2 __PING = 3 # variables __lock = RLock() # we use this lock to achieve consistency inside some functions # where we suppose logic time to be the same during the whole execution of function clock = 0 # lamport clock __number_of_processes = 0 # exclude current __number_of_confirmations = 0 __last_request_id = 0 __last_confirmation_time = 0 def on_response(self, response): message, sender, sender_time, request_id = response self.__lock.acquire() self.clock = max(self.clock, sender_time) + 1 if message == self.__CONFIRM: if request_id != self.__last_request_id: # probably it's delayed confirmation for previous PING request # that finished with timeout # ignore it return self.__number_of_confirmations += 1 self.__last_confirmation_time = self.clock self.__lock.release() return self.__lock.release() if message == self.__REQUEST: self.on_request(sender, sender_time, request_id) elif message == self.__RELEASE: self.on_release(sender, sender_time) elif message == self.__PING: self.confirm(sender, request_id) def __init__(self, id, port, ids, ports, on_request, on_release, logger=None): """ :param id: self id :param port: self port :param ids: ids of other processes :param ports: ports of other processes :param on_request: callback function :param on_release: callback function """ self.__number_of_processes = len(ids) self.ids = ids self.on_request = on_request self.on_release = on_release self.logger = logger self.connection = Connection(id, port, ids, ports, self.on_response, logger) def current_time(self): """ :return: current lamport clock time """ return self.clock def request(self) -> int: """ send broadcast request with intention to acquire mutex returns when all confirmations received or after timeout :return: -1 if timeout, otherwise time of the last confirmation """ self.__lock.acquire() self.__number_of_confirmations = 0 self.__last_confirmation_time = 0 self.__last_request_id += 1 for _id in self.ids: self.clock += 1 self.connection.send(_id, self.__REQUEST, self.clock, self.__last_request_id) self.__lock.release() start = time() # await confirmations timeout = False while self.__number_of_confirmations != self.__number_of_processes: if time() - start >= 10 and not timeout: self.logger.error('await for confirmations more than 10s...') timeout = True return self.__last_confirmation_time def confirm(self, recipient_id, request_id): """ send confirmation :param recipient_id: id of process which requires confirmation """ self.__lock.acquire() self.clock += 1 # self.logger.warn("pong to {} with id: {}".format(recipient_id, request_id)) self.connection.send(recipient_id, self.__CONFIRM, self.clock, request_id) self.__lock.release() def release(self): """ send broadcast request to notify release of mutex :return logic time of event """ self.__lock.acquire() try: for _id in self.ids: self.clock += 1 self.connection.send(_id, self.__RELEASE, self.clock) return self.clock finally: self.__lock.release() def increment_clock(self): """ actually it isn't remote call, just increment local clock :return logic time of event """ self.__lock.acquire() try: self.clock += 1 return self.clock finally: self.__lock.release() def ping_all(self, timeout=1): self.__number_of_confirmations = 0 self.__last_confirmation_time = 0 self.__last_request_id += 1 for _id in self.ids: self.connection.send(_id, self.__PING, self.clock, self.__last_request_id) start = time() # await confirmations while self.__number_of_confirmations != self.__number_of_processes: if time() - start >= timeout: return False return True def tear_down(self): """ release socket """ self.connection.tear_down()
class Protocol(): def __init__(self, client): self._client = client self._conn = None def connected(self): return self._conn != None def login(self, character_name, password): # Connect self._conn = Connection() self._conn.connect() # Send login packet login_packet = OutgoingPacket() login_packet.add_u8(0x0A) login_packet.add_u8(0) login_packet.add_u8(0) login_packet.add_u16(0) login_packet.add_u8(0) login_packet.add_string(character_name) login_packet.add_string(password) self._conn.send_packet(login_packet) # Receive login response while True: packet = self._conn.recv_packet() if packet: break time.sleep(0.1) packet_type = packet.get_u8() if packet_type == 0x14: error_message = packet.get_string() print("login: could not login: \"{}\"".format(error_message)) self._conn.close() self._conn = None return False elif packet_type == 0x0A: self._parse_login_packet(packet) return True else: print("login: unknown packet type: {}".format(packet_type)) self._conn.close() self._conn = None return False def logout(self): # Gracefully logout logout_packet = OutgoingPacket() logout_packet.add_u8(0x14) self._conn.send_packet(logout_packet) # Just wait for the socket to close by the server try: while True: handle_packet() except: self._conn = None def disconnect(self): # Just close the socket self._conn.close() self._conn = None def move(self, direction): packet = OutgoingPacket() packet.add_u8(0x65 + direction) self._conn.send_packet(packet) def handle_packet(self): packet = self._conn.recv_packet() if not packet: return False packet_type = packet.get_u8() #print("parse_packet: skipping unknown packet type: 0x{:02x}".format(packet_type)) return True # Helpers def _parse_position(self, packet): x = packet.get_u16() y = packet.get_u16() z = packet.get_u8() return x, y, z def _parse_item(self, packet): item_type_id = packet.get_u16() # TODO(simon): parse count if stackable & parse subtype if multitype return Item(item_type_id) def _parse_creature(self, packet): creature_type = packet.get_u16() if creature_type == 0x0061: # New creature packet.get_u32() # creature ids to remove creature_id = packet.get_u32() creature_name = packet.get_string() elif creature_type == 0x0062: # TODO(simon): Handle known creature raise Exception() else: print("Unknown creature_type: {}".format(creature_type)) raise Exception() # Skip health, direction, outfit, 0xDC00 ?? and speed packet.skip(11) return Creature(creature_id, creature_name) def _parse_tile(self, packet): tile = Tile() tile.ground = self._parse_item(packet) for stackpos in range(10): if packet.peek_u16() >= 0xFF00: packet.get_u16() return tile elif packet.peek_u16() == 0x0061 or packet.peek_u16() == 0x0062: tile.creatures.append(self._parse_creature(packet)) else: tile.items.append(self._parse_item(packet)) raise Exception() def _parse_login_packet(self, packet): self._client.player_id = packet.get_u32() packet.skip(2) tmp = packet.get_u8() if tmp != 0x64: print("Expected full map (0x64) but got: {}".format(tmp)) raise Exception() self._client.player_position = self._parse_position(packet) x, y, z = self._client.player_position for x_ in range(x - 9, x + 9): for y_ in range(y - 7, y + 7): self._client.tiles[(x_, y_)] = self._parse_tile(packet)
''' The main entrance of the program ''' import time from logger import Logger from network import Connection from sonic import UltraSonic SONIC = UltraSonic() LOGGER = Logger() CONNECTION = Connection() def main(working_time): ''' main function to SmartBin to work :param working_time: :return: status ''' while working_time > 0: command, dist = 'measure', SONIC.middle_value() LOGGER.write(command, dist) # logger.write(CONNECTION.send_data(dist)) time.sleep(10) working_time -= 10 if __name__ == '__main__': main(40)