From 9d3f2aec02fb7c8436bd44661320874f4580325f Mon Sep 17 00:00:00 2001 From: Jan200101 Date: Wed, 19 Oct 2022 12:38:56 +0200 Subject: Initial Commit --- .gitignore | 4 +++ BerryClient/__init__.py | 1 + BerryClient/__main__.py | 13 ++++++++ BerryClient/addons/msg.py | 9 ++++++ BerryClient/addons/reload.py | 5 +++ BerryClient/commands.py | 50 +++++++++++++++++++++++++++++ BerryClient/core.py | 76 ++++++++++++++++++++++++++++++++++++++++++++ BerryClient/events.py | 66 ++++++++++++++++++++++++++++++++++++++ BerryClient/logging.py | 12 +++++++ LICENSE | 22 +++++++++++++ MANIFEST.in | 3 ++ README.md | 22 +++++++++++++ requirements.txt | 2 ++ setup.py | 47 +++++++++++++++++++++++++++ 14 files changed, 332 insertions(+) create mode 100644 .gitignore create mode 100644 BerryClient/__init__.py create mode 100644 BerryClient/__main__.py create mode 100644 BerryClient/addons/msg.py create mode 100644 BerryClient/addons/reload.py create mode 100644 BerryClient/commands.py create mode 100644 BerryClient/core.py create mode 100644 BerryClient/events.py create mode 100644 BerryClient/logging.py create mode 100644 LICENSE create mode 100644 MANIFEST.in create mode 100644 README.md create mode 100644 requirements.txt create mode 100755 setup.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d1c24fb --- /dev/null +++ b/.gitignore @@ -0,0 +1,4 @@ +env +dist +__pycache__ +*.egg-info diff --git a/BerryClient/__init__.py b/BerryClient/__init__.py new file mode 100644 index 0000000..3aa0d7b --- /dev/null +++ b/BerryClient/__init__.py @@ -0,0 +1 @@ +__version__ = "0.0.0" \ No newline at end of file diff --git a/BerryClient/__main__.py b/BerryClient/__main__.py new file mode 100644 index 0000000..05b6ce5 --- /dev/null +++ b/BerryClient/__main__.py @@ -0,0 +1,13 @@ +import click + +from BerryClient.core import BerryCore + +@click.group() +def main(): + pass + +@main.command() +def run(): + + ins = BerryCore() + ins.run() diff --git a/BerryClient/addons/msg.py b/BerryClient/addons/msg.py new file mode 100644 index 0000000..61edabd --- /dev/null +++ b/BerryClient/addons/msg.py @@ -0,0 +1,9 @@ +import logging + +from BerryClient.commands import command + +log = logging.getLogger(__name__) + +@command() +def msg(core, *content): + log.info(" ".join(content)) diff --git a/BerryClient/addons/reload.py b/BerryClient/addons/reload.py new file mode 100644 index 0000000..958bb2b --- /dev/null +++ b/BerryClient/addons/reload.py @@ -0,0 +1,5 @@ +from BerryClient.commands import command + +@command(name="reload") +def _reload(core): + core.reload() diff --git a/BerryClient/commands.py b/BerryClient/commands.py new file mode 100644 index 0000000..532c0a2 --- /dev/null +++ b/BerryClient/commands.py @@ -0,0 +1,50 @@ +import logging + +from BerryClient.events import on_chat_post + +log = logging.getLogger(__name__) + +PREFIX = "#" +COMMANDS = {} + +def command(name=None): + def decorator(func): + func_name = name or func.__name__ + + def wrapper(core, *args): + try: + retval = func(core, *args) + if retval: + core.postToChat(f"[{func_name}] {retval}") + except Exception as e: + log.exception(e) + + + description = None + if func.__doc__: + description = func.__doc__.strip().split("\n")[0] + + COMMANDS[func_name] = { + "func": wrapper, + "description": description + } + + return decorator + +@on_chat_post() +def process_post(core, post) -> bool: + if not post.message.startswith(PREFIX): + return False + + full_command = post.message[len(PREFIX):].split() + command = full_command[0] + args = full_command[1:] + + try: + COMMANDS[command]["func"](core, *args) + return True + except KeyError: + pass + + return False + diff --git a/BerryClient/core.py b/BerryClient/core.py new file mode 100644 index 0000000..5f59e1b --- /dev/null +++ b/BerryClient/core.py @@ -0,0 +1,76 @@ +import sys +import logging +from importlib import import_module, reload + +from mcpi import minecraft +from BerryClient.events import process_events +from BerryClient.logging import MinecraftChatHandler + +log = logging.getLogger() + +class BerryCore: + + ADDONS = [ + "BerryClient.addons.msg", + "BerryClient.addons.reload" + ] + ADDON_MODULES = {} + + def __init__(self, address: str = "localhost", port: int = 4711): + self.instance = minecraft.Minecraft.create(address, port) + self.running = False + + self.__setupLogging() + self.__loadAddons() + + def __setupLogging(self): + root_logger = logging.getLogger() + root_logger.setLevel(logging.DEBUG) + + formatter = logging.Formatter( + "[{levelname}][{name}]: {message}", style="{" + ) + + mc_handler = MinecraftChatHandler(self.instance) + mc_handler.setFormatter(formatter) + root_logger.addHandler(mc_handler) + + if not sys.stdout.closed: + stdout_handler = logging.StreamHandler(sys.stdout) + stdout_handler.setFormatter(formatter) + + root_logger.addHandler(stdout_handler) + + def __loadAddons(self): + for addon in self.ADDONS: + self.ADDON_MODULES[addon] = import_module(addon) + log.info(f"Loaded {addon}") + + def __reloadAddons(self): + for addon in self.ADDONS: + try: + self.ADDON_MODULES[addon] = reload(self.ADDON_MODULES[addon]) + log.info(f"Reloaded {addon}") + except KeyError: + self.ADDON_MODULES[addon] = import_module(addon) + log.info(f"Loaded {addon}") + + + def __getattr__(self, attr): + if self.instance: + return getattr(self.instance, attr) + + return AttributeError("No instance running") + + def reload(self): + self.__reloadAddons() + + def stop(self): + self.running = False + + def run(self): + self.running = True + + while self.running: + process_events(self) + diff --git a/BerryClient/events.py b/BerryClient/events.py new file mode 100644 index 0000000..c01b6ee --- /dev/null +++ b/BerryClient/events.py @@ -0,0 +1,66 @@ +import logging + +from mcpi.connection import RequestError +from mcpi.event import BlockEvent, ChatEvent + +log = logging.getLogger(__name__) + +BLOCK_CALLBACKS = [] +def on_block_hit(): + def decorator(func): + def wrapper(core: "BerryCore", event: BlockEvent) -> bool: + return func(core, event) + + BLOCK_CALLBACKS.append(wrapper) + + return decorator + +CHAT_CALLBACKS = [] +def on_chat_post(): + def decorator(func): + def wrapper(core: "BerryCore", event: ChatEvent) -> bool: + return func(core, event) + + CHAT_CALLBACKS.append(wrapper) + + return decorator + +PLAYER_MOVEMENT_CALLBACKS = [] +def on_player_movement(): + def decorator(func): + def wrapper(core: "BerryCore", playerId: int, old_pos, new_pos) -> bool: + return func(core, playerId, old_pos, new_pos) + + PLAYER_MOVEMENT_CALLBACKS.append(wrapper) + + return decorator + +PLAYER_POSITIONS = {} +def process_events(core: "BerryCore"): + + for hit in core.events.pollBlockHits(): + for callback in BLOCK_CALLBACKS: + if callback(block, hit): + break + + for post in core.events.pollChatPosts(): + for callback in CHAT_CALLBACKS: + if callback(core, post): + break + + OLD_POSITIONS = PLAYER_POSITIONS.copy() + try: + playerEntityIds = core.getPlayerEntityIds() + except RequestError: + playerEntityIds = [] + + for playerId in playerEntityIds: + PLAYER_POSITIONS[playerId] = core.entity.getPos(playerId) + + if (playerId in OLD_POSITIONS + and OLD_POSITIONS[playerId] != PLAYER_POSITIONS[playerId]): + + for callback in PLAYER_MOVEMENT_CALLBACKS: + if callback(core, playerId, + OLD_POSITIONS[playerId], PLAYER_POSITIONS[playerId]): + break diff --git a/BerryClient/logging.py b/BerryClient/logging.py new file mode 100644 index 0000000..60aad3d --- /dev/null +++ b/BerryClient/logging.py @@ -0,0 +1,12 @@ +import logging + +class MinecraftChatHandler(logging.Handler): + def __init__(self, mc, *args, **kwargs): + super().__init__(*args, **kwargs) + self.mc = mc + + def emit(self, record): + msg = self.format(record) + + for line in msg.split("\n"): + self.mc.postToChat(line) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1b5a336 --- /dev/null +++ b/LICENSE @@ -0,0 +1,22 @@ +MIT License + +Copyright (c) 2022 Jan Drögehoff + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..bb910eb --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,3 @@ +include README.md +include LICENSE +include requirements.txt diff --git a/README.md b/README.md new file mode 100644 index 0000000..a8c167c --- /dev/null +++ b/README.md @@ -0,0 +1,22 @@ +# unixreg + +## why +While working on porting a python application to Linux I came across many uses of winreg that could not easily be ported over. + +Instead of reworking the system I decided to implement a bandaid solution that deals with it transparently. + +## how to use +Update your winreg imports with this + +```py +try: + import winreg +except ImportError: + import unixreg as winreg +``` + +simply importing unixreg should be enough though, winreg is imported if found + +## license +this project is licensed under the [MIT License](LICENSE) +feel free to do whatever you want with it diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..b60bc31 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +mcpi +click diff --git a/setup.py b/setup.py new file mode 100755 index 0000000..3367110 --- /dev/null +++ b/setup.py @@ -0,0 +1,47 @@ +# -*- coding: utf-8 -*- +import setuptools +import re + +with open('requirements.txt') as f: + requirements = f.read().splitlines() + +version = None +with open('BerryClient/__init__.py') as f: + version = re.search(r'^__version__\s*=\s*[\'"]([^\'"]*)[\'"]', f.read(), re.MULTILINE).group(1) + +if version is None: + raise RuntimeError('version is not set') + +with open("README.md", "r") as fh: + long_description = fh.read() + + +setuptools.setup( + name="BerryClient", + version=version, + author="Jan Drögehoff", + author_email="jandroegehoff@gmail.com", + long_description=long_description, + long_description_content_type="text/markdown", + url="https://github.com/Jan200101/BerryClient", + packages=["BerryClient"], + license="MIT", + install_requires=requirements, + include_package_data=True, + classifiers=[ + "Development Status :: 3 - Alpha", + + "Programming Language :: Python :: 3", + + "License :: OSI Approved :: MIT License", + + "Operating System :: POSIX", + "Operating System :: POSIX :: Linux", + ], + python_requires=">=3.6", + entry_points={ + 'console_scripts': [ + 'BerryClient=BerryClient.__main__:main', + ], + }, +) -- cgit v1.2.3