diff --git a/airmash/client.py b/airmash/client.py index 0c92631..1231ffe 100644 --- a/airmash/client.py +++ b/airmash/client.py @@ -20,7 +20,9 @@ def __init__(self, enable_debug=False, enable_chat=True): def on(self, key, handler=None): def decorate(handler): - self._handlers[key] = handler + keys = [key] if isinstance(key, str) else key + for k in keys: + self._handlers[k] = handler if handler is None: return decorate @@ -116,6 +118,10 @@ def process_message(self, message): self.send(cmd) self.connected = True + + Mob.current_clock = message.clock + Player.current_clock = message.clock + return self._call_handler(message) if message.command == 'SERVER_MESSAGE': @@ -176,7 +182,8 @@ def process_message(self, message): if message.command == 'PLAYER_FIRE': for projectile in message.projectiles: self._debug_print(packets.DEBUG_ACTION, u"New projectile of type {}".format(projectile.type)) - self.projectiles[projectile.id] = Mob(projectile.id, self.players[message.id], projectile) + self.projectiles[projectile.id] = Mob( + projectile.id, self.players[message.id], projectile, clock=message.clock) return self._call_handler(message) if message.command == 'EVENT_STEALTH': @@ -193,9 +200,11 @@ def process_message(self, message): self._debug_print(packets.DEBUG_ACTION, u"{} uses repel. {} self.players and {} self.projectiles repelled.".format(self.players[message.id].name, len(message.players), len(message.mobs))) for projectile in message.mobs: if projectile.id in self.projectiles: - self.projectiles[projectile.id].update(projectile, new_owner=self.players[message.id]) + self.projectiles[projectile.id].update( + projectile, new_owner=self.players[message.id], clock=message.clock) else: - self.projectiles[projectile.id] = Mob(projectile.id, self.players[message.id], projectile) + self.projectiles[projectile.id] = Mob( + projectile.id, self.players[message.id], projectile, clock=message.clock) return self._call_handler(message) return self._call_handler(message) @@ -208,7 +217,7 @@ def process_message(self, message): if message.command == 'PLAYER_LEAVE': self._debug_print(packets.DEBUG_ACTION, u"{} left".format(self.players[message.id].name)) - self.players[message.id].online = False + self.players[message.id].active = False return self._call_handler(message) if message.command == 'PLAYER_NEW': @@ -242,7 +251,7 @@ def process_message(self, message): self.projectiles[message.id].update(message) else: self._debug_print(packets.DEBUG_INFO, u"Mob type of {} does not exist? {}".format(message.type, message.id)) - self.projectiles[message.id] = Mob(message.id, None, message) + self.projectiles[message.id] = Mob(message.id, None, message, clock=message.clock) return self._call_handler(message) if message.command in ['MOB_DESPAWN', 'MOB_DESPAWN_COORDS']: diff --git a/airmash/mob.py b/airmash/mob.py index 516ac47..c192e49 100644 --- a/airmash/mob.py +++ b/airmash/mob.py @@ -1,12 +1,23 @@ class Mob(): - def __init__(self, id, owner, data={}): - self.online = True + current_clock = 0 + + def __init__(self, id, owner, data={}, clock=0): + self.clock = clock + Mob.current_clock = clock + self.active = True self.id = id self.owner = owner - self.active = True self._handlers = {} self.update(data) + @property + def age(self): + return (Mob.current_clock - self.clock) / 1e6 + + def __str__(self): + owner = self.owner.name if self.owner else 'Unknown' + return f'id:{self.id} age:{self.age:.1f} pos:({self.posX:.1f}, {self.posY:.1f})' + def despawn(self): self.active = False self._handle_change('despawn', True, False) @@ -20,6 +31,8 @@ def update(self, data, new_owner=None): old = self.__dict__.copy() + self.clock = self._get_default(data, 'clock', 0) + Mob.current_clock = self.clock self.type = self._get_default(data, 'type', 0) self.posX = self._get_default(data, 'posX', 0) self.posY = self._get_default(data, 'posY', 0) diff --git a/airmash/player.py b/airmash/player.py index cdbbb0a..09537f1 100644 --- a/airmash/player.py +++ b/airmash/player.py @@ -4,8 +4,11 @@ def ks(player, k, a, b): print("[{}] {}: {}".format(player.name, k, a)) class Player(): + + current_clock = 0 + def __init__(self, id, data={}): - self.online = True + self.active = True self.id = id self._handlers = {} self.update(data) @@ -14,11 +17,18 @@ def __init__(self, id, data={}): #self._handlers['upgrades'] = ks #self._handlers['keystate'] = ks + @property + def age(self): + return (Player.current_clock - self.clock) / 1e6 + + def __str__(self): + return f'{self.name} id:{self.id} age:{self.age:.1f} pos:({self.posX:.1f}, {self.posY:.1f})' def update(self, data): old = self.__dict__.copy() self.clock = self._get_default(data, 'clock', 0) + Player.current_clock = self.clock self.status = self._get_default(data, 'status', 0) self.level = self._get_default(data, 'level', 0) self.name = self._get_default(data, 'name', "") diff --git a/gui-client.py b/gui-client.py new file mode 100644 index 0000000..c64da0a --- /dev/null +++ b/gui-client.py @@ -0,0 +1,101 @@ +import os +import sys +from threading import Thread +from time import sleep + +import pygame + +from airmash.client import Client + +client = Client(enable_debug=False, enable_chat=False) + + +@client.on('PLAYER_HIT') +def on_hit(client, message): + for player in message.players: + if player.id == client.player.id: + print("Uh oh! I've been hit!") + + +@client.on(['MOB_DESPAWN', 'MOB_DESPAWN_COORDS', 'MOB_UPDATE', 'MOB_UPDATE_STATIONARY']) +def md(client, message): + print(message) + + +kwargs = dict( + name='z', + flag='GB', + region='eu', + room='ffa1', + enable_trace=False +) + +t = Thread(target=client.connect, kwargs=kwargs) +t.start() + +size = width, height = 900, 1000 +black = 0, 0, 0 +white = 255, 255, 255 +red = 255, 0, 0 +green = 0, 255, 0 +blue = 0, 0, 255 + +player_size = 50 +projectile_size = 20 + +os.environ['SDL_VIDEO_WINDOW_POS'] = "0,20" +pygame.init() +font = pygame.font.SysFont('', 20) +clock = pygame.time.Clock() +screen = pygame.display.set_mode(size) + +keys = { + pygame.K_w: 'UP', + pygame.K_s: 'DOWN', + pygame.K_a: 'LEFT', + pygame.K_d: 'RIGHT', + pygame.K_SPACE: 'FIRE', + pygame.K_LSHIFT: 'SPECIAL', +} + +while not client.connected: + sleep(0.1) + + +def draw(items, colour, size=player_size): + for item in items: + if not item.active: + continue + x = item.posX - me.posX + (width / 2.0) + y = item.posY - me.posY + (height / 2.0) + if 0 < x < width and 0 < y < height: + pygame.draw.rect(screen, colour, (x, y, size, size)) + textsurface = font.render(str(item), True, white) + screen.blit(textsurface, (x, y)) + + +while 1: + + for event in pygame.event.get(): + if event.type == pygame.QUIT: + sys.exit() + + elif event.type == pygame.KEYDOWN: + d = keys.get(event.key) + if d: + client.key(d, True) + + elif event.type == pygame.KEYUP: + d = keys.get(event.key) + if d: + client.key(d, False) + + screen.fill(black) + + me = client.player + draw([me], blue) + draw(client.players.values(), green) + draw(client.projectiles.values(), red, size=20) + + pygame.display.flip() + clock.tick(30)