diff --git a/controller_example.py b/controller_example.py index 7556a91..c62cc6c 100755 --- a/controller_example.py +++ b/controller_example.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python2 # -*- coding: utf-8 -*- """ @@ -18,6 +18,8 @@ import pygame +DEBUG = True + # define some constants E_UID = pygame.USEREVENT + 1 E_DOWNLOAD = pygame.USEREVENT + 2 @@ -29,7 +31,7 @@ class ReceiverThread(Thread): """ This thread will listen on a UDP port for packets from the game. """ - def __init__(self, host='0.0.0.0', port=11337): + def __init__(self, host='0.0.0.0', port=1338): """ Creates the socket and binds it to the given host and port. """ @@ -48,24 +50,26 @@ def run(self): logging.warning('example warning') logging.error('example error') logging.critical('example critical') - #print(datetime.now(), '<<< {}'.format(data)) if data.startswith('/uid/'): - #print(datetime.now(), '### uid', data[5:], 'received') e = pygame.event.Event(E_UID, {'uid': int(data[5:])}) pygame.event.post(e) + if DEBUG: logging.info('uid received: {}'.format(data[5:])) elif data.startswith('/download/'): e = pygame.event.Event(E_DOWNLOAD, {'url': str(data[10:])}) pygame.event.post(e) + if DEBUG: logging.info('download of {} triggered'.format(data[10:])) elif data.startswith('/play/'): e = pygame.event.Event(E_PLAY, {'filename': str(data[6:])}) pygame.event.post(e) + if DEBUG: logging.info('playback of {} triggered'.format(data[6:])) elif data.startswith('/rumble/'): - e = pygame.event.Event(E_RUMBLE, {'duration': float(data[8:].replace(',', '.'))}) + e = pygame.event.Event(E_RUMBLE, {'duration': int(data[8:])}) pygame.event.post(e) + if DEBUG: logging.info('request rumble for {}ms'.format(data[8:])) class Controller(object): - def __init__(self, game_host='127.0.0.1', game_port=1338, host='0.0.0.0', port=11337): + def __init__(self, game_host='127.0.0.1', game_port=1338, host='0.0.0.0', port=1338): self.game_host = game_host # Host of Mate Light self.game_port = game_port # Port of Mate Light self.host = host # Host of ReceiverThread @@ -98,41 +102,47 @@ def __init__(self, game_host='127.0.0.1', game_port=1338, host='0.0.0.0', port=1 self.rumble_active = False self.uid = None - self._receiver = ReceiverThread(host, port) + self._receiver = ReceiverThread(self.host, self.port) self._receiver.setDaemon(True) self._receiver.start() def ping(self): if self.uid: + if DEBUG: logging.info('sending ping') msg = '/controller/{}/ping/{}'.format(self.uid, self._receiver.port) self.sock.sendto(msg.encode('utf-8'), (self.game_host, self.game_port)) - #print(datetime.now(), '>>>', msg) def send_keys(self): # alternative states creation: [1 if k else 0 for k in self.keys] states = '/controller/{}/states/{}'.format(self.uid, ''.join([str(k) for k in self.keys])) + if DEBUG: logging.info('sending states {}'.format(''.join([str(k) for k in self.keys]))) self.sock.sendto(states.encode('utf-8'), (self.game_host, self.game_port)) - #print(datetime.now(), '>>>' + states) self.timeout = time.time() def send_message(self, msg): + if DEBUG: logging.info('sending of messages not yet implemented') pass def disconnect(self): + if DEBUG: logging.info('disconnecting from game') msg = '/controller/{}/kthxbye'.format(self.uid) self.sock.sendto(msg.encode('utf-8'), (self.game_host, self.game_port)) def connect(self): + if DEBUG: logging.info('connecting to game') msg = '/controller/new/{}'.format(self.port) self.sock.sendto(msg.encode('utf-8'), (self.game_host, self.game_port)) def rumble(self, duration): + if DEBUG: logging.info('rumble not yet implemented') pass def download_sound(self, url): + if DEBUG: logging.info('downloading of media files not yet implemented') pass def play_sound(self, filename): + if DEBUG: logging.info('playing media files not yet implemented') pass def handle_inputs(self): @@ -147,11 +157,9 @@ def handle_inputs(self): elif event.type == pygame.MOUSEBUTTONUP: pygame.event.post(pygame.event.Event(pygame.QUIT)) elif event.type == E_UID: - #print(datetime.now(), '### UID event received', event.uid) self.uid = event.uid if self.uid is not None: - #print(datetime.now(), '### UID set. checking other events') if event.type == E_DOWNLOAD: self.download_sound(event.url) elif event.type == E_PLAY: @@ -162,22 +170,19 @@ def handle_inputs(self): try: button = self.mapping[event.key] if event.type == pygame.KEYDOWN: - #print('{} | {}'.format(event.key, button)) self.keys[button] = 1 elif event.type == pygame.KEYUP: - #print('{} | {}'.format(event.key, button)) self.keys[button] = 0 self.send_keys() except KeyError: break else: - #print(datetime.now(), '### UID not set. connecting to game.') self.connect() time.sleep(1) if __name__ == '__main__': - ctlr = Controller('127.0.0.1', 1338, '0.0.0.0', 11337) + ctlr = Controller('127.0.0.1', 1338, '0.0.0.0', 1338) try: while True: ctlr.handle_inputs() diff --git a/emulator.py b/emulator.py index 27d2e64..4ed51f4 100755 --- a/emulator.py +++ b/emulator.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python2.7 +#!/usr/bin/env python2 # -*- coding: utf-8 -*- """ @@ -7,13 +7,27 @@ This little program emulates the awesome Mate Light, just in case you're not on c-base space station but want to send something to it. + +Usage: + emulator.py [-w=] [-h=] [--host=] [--port=] [--dot=] + emulator.py --help + emulator.py --version + +Options: + --help Show this screen. + --version Show version. + -w= Width in pixels [default: 40]. + -h= Height in pixels [default: 16]. + --host= Bind to IP address [default: 127.0.0.1]. + --port= Bind to Port [default: 1337]. + --dot= Size of dots in pixels [default: 10]. """ __author__ = 'Ricardo Band' __copyright__ = 'Copyright 2014, Ricardo Band' __credits__ = ['Ricardo Band'] __license__ = 'MIT' -__version__ = '0.2.0' +__version__ = '0.3.0' __maintainer__ = 'Ricardo Band' __email__ = 'me@xengi.de' __status__ = 'Development' @@ -22,27 +36,27 @@ import socket import pygame +from docopt import docopt class Emu(object): """ The Emulator is a simple pygame game. """ - def __init__(self, width=40, height=16, ip='127.0.0.1', port=1337): + def __init__(self, width=40, height=16, ip='127.0.0.1', port=1337, dotsize=10): """ Creates a screen with the given size, generates the matrix for the Mate bottles and binds the socket for incoming frames. """ self.width = width self.height = height + self.dotsize = dotsize pygame.init() - # one mate bottle is 10x10px - self.screen = pygame.display.set_mode([self.width * 10, self.height * 10]) + self.screen = pygame.display.set_mode([self.width * self.dotsize, self.height * self.dotsize]) pygame.display.set_caption("Mate Light Emu") self.clock = pygame.time.Clock() self.matrix = [] for c in range(self.width * self.height * 3): - # fill matrix with black color self.matrix.append(0) self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) @@ -71,7 +85,7 @@ def update(self): if pixel < pixels: pygame.draw.circle(self.screen, (self.matrix[pixel], self.matrix[pixel + 1], self.matrix[pixel + 2]), - (x * 10 + 5, y * 10 + 5), 5, 0) + (x * self.dotsize + self.dotsize / 2, y * self.dotsize + self.dotsize / 2), self.dotsize / 2, 0) def render(self): """ @@ -101,5 +115,6 @@ def gameloop(self): if __name__ == '__main__': - EMU = Emu(40, 16, '127.0.0.1', 1337) + ARGS = docopt(__doc__, version=__version__) + EMU = Emu(int(ARGS['-w']), int(ARGS['-h']), ARGS['--host'], int(ARGS['--port']), int(ARGS['--dot'])) EMU.gameloop() diff --git a/game_example.py b/game_example.py index a9e172e..b42d819 100755 --- a/game_example.py +++ b/game_example.py @@ -13,7 +13,7 @@ __copyright__ = 'Copyright 2014, Ricardo Band' __credits__ = ['Ricardo Band'] __license__ = 'MIT' -__version__ = '0.2.0' +__version__ = '0.3.0' __maintainer__ = 'Ricardo Band' __email__ = 'me@xengi.de' __status__ = 'Development' @@ -21,7 +21,7 @@ from datetime import datetime import pymlgame -from pymlgame.locals import * +from pymlgame.locals import WHITE, BLUE, GREEN, CYAN, MAGENTA, YELLOW, RED, BLACK, E_NEWCTLR, E_DISCONNECT, E_KEYDOWN, E_KEYUP, E_PING from pymlgame.screen import Screen from pymlgame.clock import Clock from pymlgame.surface import Surface diff --git a/icons_android.xcf b/icons_android.xcf new file mode 100644 index 0000000..7acc6e7 Binary files /dev/null and b/icons_android.xcf differ diff --git a/pymlgame/__init__.py b/pymlgame/__init__.py index a268b2d..2c4c9fc 100644 --- a/pymlgame/__init__.py +++ b/pymlgame/__init__.py @@ -1,31 +1,35 @@ # -*- coding: utf-8 -*- +""" +PyMLGame +""" + __author__ = 'Ricardo Band' __copyright__ = 'Copyright 2014, Ricardo Band' __credits__ = ['Ricardo Band'] __license__ = 'MIT' -__version__ = '0.2.0' +__version__ = '0.3.0' __maintainer__ = 'Ricardo Band' __email__ = 'me@xengi.de' __status__ = 'Development' -# from pymlgame.locals import * -# from pymlgame.screen import Screen -# from pymlgame.clock import Clock -# from pymlgame.surface import Surface +from pymlgame.locals import * +from pymlgame.screen import Screen +from pymlgame.clock import Clock +from pymlgame.surface import Surface from pymlgame.controller import Controller -_ctlr = Controller() +CONTROLLER = Controller() def init(host='0.0.0.0', port=1338): """ - Initialize pymlgame. This creates a controller thread that listens for game controllers and events. + Initialize PyMLGame. This creates a controller thread that listens for game controllers and events. """ - _ctlr.host = host - _ctlr.port = port - _ctlr.setDaemon(True) # because it's a deamon it will exit together with the main thread - _ctlr.start() + CONTROLLER.host = host + CONTROLLER.port = port + CONTROLLER.setDaemon(True) # because it's a deamon it will exit together with the main thread + CONTROLLER.start() def get_events(maximum=10): @@ -35,10 +39,10 @@ def get_events(maximum=10): events = [] for ev in range(0, maximum): try: - if _ctlr.queue.empty(): + if CONTROLLER.queue.empty(): break else: - events.append(_ctlr.queue.get_nowait()) + events.append(CONTROLLER.queue.get_nowait()) except NameError: not_initialized() events = False @@ -50,11 +54,11 @@ def get_event(): """ Get the next event in the queue if there is one. """ - if not _ctlr.queue.empty(): - return _ctlr.queue.get_nowait() + if not CONTROLLER.queue.empty(): + return CONTROLLER.queue.get_nowait() else: return False def not_initialized(): - print('pymlgame is not initialized correctly. Use pymlgame.init() first.') + print('PyMLGame is not initialized correctly. Use pymlgame.init() first.') diff --git a/pymlgame/clock.py b/pymlgame/clock.py index e4a1b25..50ac3bf 100644 --- a/pymlgame/clock.py +++ b/pymlgame/clock.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -pymlgame - Clock +PyMLGame - Clock """ import time @@ -21,5 +21,5 @@ def tick(self): """ Let the Clock tick. """ - #TODO: I think this is not the correct way. Should think about this again.. - time.sleep(1.0/self.fps) \ No newline at end of file + #TODO: I think this is not the correct way. I should think about this again.. + time.sleep(1.0/self.fps) diff --git a/pymlgame/controller.py b/pymlgame/controller.py index 2104d90..7cf6597 100644 --- a/pymlgame/controller.py +++ b/pymlgame/controller.py @@ -1,16 +1,17 @@ # -*- coding: utf-8 -*- """ -pymlgame - Controller +PyMLGame - Controller """ +from uuid import uuid4 import time from datetime import datetime import socket from queue import Queue from threading import Thread -from pymlgame.locals import * +from pymlgame.locals import E_NEWCTLR, E_DISCONNECT, E_PING, E_KEYUP, E_KEYDOWN, E_MESSAGE, E_RUMBLE from pymlgame.event import Event @@ -19,8 +20,6 @@ class Controller(Thread): A controller can be a game controller attached to the system or any other input that can trigger the controller functions like a smartphone app. """ - _next_uid = 0 - def __init__(self, host='0.0.0.0', port=1338): """ Creates a controller deamon @@ -37,29 +36,25 @@ def _new_controller(self, addr, port): """ Get an uid for your controller. """ - #print(datetime.now(), '### new controller at', addr, ':', port) for uid, controller in self.controllers.items(): if controller[0] == addr: - #print(datetime.now(), '### duplicate address. not adding this one.') # duplicate address. sending the uid again - #print(datetime.now(), '>>> /uid/{}'.format(uid), addr, port) + #print('/uid/{} => {}:{}'.format(uid, addr, port)) self.sock.sendto('/uid/{}'.format(uid).encode('utf-8'), (addr, port)) return False # get an uid and add the controller to the game - uid = self._next_uid - self._next_uid += 1 + uid = str(uuid4()) self.controllers[uid] = [addr, port, '00000000000000', time.time()] # tell the controller about it - #print(datetime.now(), '>>> /uid/{}'.format(uid), addr, port) + #print('/uid/{} => {}:{}'.format(uid, addr, port)) self.sock.sendto('/uid/{}'.format(uid).encode('utf-8'), (addr, port)) # create event for pymlgame e = Event(uid, E_NEWCTLR) self.queue.put_nowait(e) - #print(datetime.now(), '### controller added with uid', uid) return uid def _del_controller(self, uid): @@ -68,7 +63,6 @@ def _del_controller(self, uid): """ try: self.controllers.pop(uid) - #print(datetime.now(), '### controller', uid, 'deleted') e = Event(uid, E_DISCONNECT) self.queue.put_nowait(e) except KeyError: @@ -97,13 +91,11 @@ def _update_states(self, uid, states): """ #TODO: use try and catch all exceptions # test if uid exists - #print(datetime.now(), '### Checking states', states, 'for controller', uid) if self.controllers[uid]: # test if states have correct lenght if len(states) == 14: old_states = self.controllers[uid][2] if old_states != states: - #print(datetime.now(), '### checking old states', old_states, 'against new states', states) for key in range(14): if int(old_states[key]) > int(states[key]): e = Event(uid, E_KEYUP, key) @@ -111,6 +103,7 @@ def _update_states(self, uid, states): elif int(old_states[key]) < int(states[key]): e = Event(uid, E_KEYDOWN, key) self.queue.put_nowait(e) + self.controllers[uid][2] = states self.controllers[uid][3] = time.time() def _got_message(self, uid, text): @@ -135,16 +128,14 @@ def send(self, uid, event, payload): addr = self.controllers[uid][0] port = self.controllers[uid][1] if event == E_MESSAGE: - #print(datetime.now(), '>>> /message/{}'.format(payload), addr, port) + #print('/message/{} => {}:{}'.format(payload, addr, port)) return sock.sendto('/message/{}'.format(payload).encode('utf-8'), (addr, port)) elif event == E_RUMBLE: - #print(datetime.now(), '>>> /rumble/{}'.format(payload), addr, port) + #print('/rumble/{} => {}:{}'.format(payload, addr, port)) return sock.sendto('/rumble/{}'.format(payload).encode('utf-8'), (addr, port)) else: - #print(datetime.now(), '### Unknown event type.') pass else: - #print(datetime.now(), '### This UID ({}) doesn\'t exist.'.format(uid)) pass return False @@ -156,7 +147,6 @@ def run(self): data, sender = self.sock.recvfrom(1024) addr = sender[0] msg = data.decode('utf-8') - #print(datetime.now(), '<<<', msg) if msg.startswith('/controller/'): try: uid = msg.split('/')[2] @@ -164,7 +154,6 @@ def run(self): port = int(msg.split('/')[3]) self._new_controller(addr, port) else: - uid = int(uid) cmd = msg.split('/')[3] if cmd == 'ping': port = msg.split('/')[3] @@ -176,17 +165,15 @@ def run(self): self._update_states(uid, states) elif cmd == 'text': # /controller//text/ - text = msg[12 + len(str(uid)) + 6:] + text = msg[12 + len(uid) + 6:] self._got_message(uid, text) except IndexError or KeyError: - #print(datetime.now(), '### Error in coitus protocol.') pass else: - #print(datetime.now(), '### This thing doesn\'t fit:', msg) pass # find unused controllers and delete them ctlrs = self.controllers.items() for uid, state in ctlrs: if state[3] < time.time() - 60: - self.controllers.pop(uid) \ No newline at end of file + self.controllers.pop(uid) diff --git a/pymlgame/event.py b/pymlgame/event.py index 7d36b4c..dd8edf4 100644 --- a/pymlgame/event.py +++ b/pymlgame/event.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -pymlgame - Event +PyMLGame - Event """ from pymlgame.locals import E_KEYDOWN, E_KEYUP @@ -11,7 +11,7 @@ class Event(object): def __init__(self, uid, type, data=None): self.uid = uid self.type = type - if type == E_KEYDOWN or E_KEYUP: + if type == E_KEYDOWN or type == E_KEYUP: self.button = data else: - self.data = data \ No newline at end of file + self.data = data diff --git a/pymlgame/locals.py b/pymlgame/locals.py index 4550791..59d2161 100644 --- a/pymlgame/locals.py +++ b/pymlgame/locals.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- """ -pymlgame - Locals +PyMLGame - Locals """ # event types @@ -52,4 +52,4 @@ GREY3 = (178, 178, 178) GREY2 = (204, 204, 204) GREY1 = (229, 229, 229) -WHITE = (255, 255, 255) \ No newline at end of file +WHITE = (255, 255, 255) diff --git a/pymlgame/screen.py b/pymlgame/screen.py index 612d415..90d5edd 100644 --- a/pymlgame/screen.py +++ b/pymlgame/screen.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- """ -pymlgame - Screen +PyMLGame - Screen """ import socket -from pymlgame.locals import * +from pymlgame.locals import BLACK from pymlgame.surface import Surface diff --git a/pymlgame/surface.py b/pymlgame/surface.py index c5faee4..9f499f0 100644 --- a/pymlgame/surface.py +++ b/pymlgame/surface.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- """ -pymlgame - Surface +PyMLGame - Surface """ import math -from pymlgame.locals import * +from pymlgame.locals import BLACK class Surface(object): @@ -111,4 +111,4 @@ def replace_color(self, before, after): for x in range(self.width): for y in range(self.height): if self.matrix[x][y] == before: - self.matrix[x][y] = after \ No newline at end of file + self.matrix[x][y] = after diff --git a/setup.py b/setup.py index 3315bec..279c5b7 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,7 @@ __copyright__ = 'Copyright 2014, Ricardo Band' __credits__ = ['Ricardo Band'] __license__ = 'MIT' -__version__ = '0.2.0' +__version__ = '0.3.0' __maintainer__ = 'Ricardo Band' __email__ = 'me@xengi.de' __status__ = 'Development' @@ -25,16 +25,16 @@ def read(fname): return open(os.path.join(os.path.dirname(__file__), fname)).read() -setup(name='pymlgame', +setup(name='PyMLGame', version=__version__, author=__author__, author_email=__email__, maintainer=__maintainer__, maintainer_email=__email__, - url='http://github.com/c-base/pymlgame', - description='pymlgame is an abstraction layer to easily build games for Mate Light inspired by pygame.', + url='http://github.com/PyMLGame/pymlgame', + description='PyMLGame is an abstraction layer to easily build games for Mate Light inspired by PyGame.', long_description=read('README.md'), - download_url='https://github.com/c-base/pymlgame/archive/master.zip', + download_url='https://github.com/PyMLGame/pymlgame/archive/master.zip', classifiers=['Development Status :: 4 - Beta', 'Environment :: Console', 'Intended Audience :: Developers',