diff options
| author | Ritabrata Das <[email protected]> | 2025-02-13 06:40:27 +0530 |
|---|---|---|
| committer | GitHub <[email protected]> | 2025-02-13 06:40:27 +0530 |
| commit | 7fb77e2f0f715713950878f4369d420481ab5170 (patch) | |
| tree | 35c85d9099f255be12aa1a2daa40215b41a9bcb0 | |
| parent | acca268ffafd26bf87fa6b14f228bdd54cf47233 (diff) | |
| parent | ab9065592f811dcb78c6f1f7fe927d3deadf3695 (diff) | |
Merge pull request #3 from Skipper-is/plugin_manager
Initial setup of plugin manager
| -rw-r--r-- | lib/Aircraft.py | 1 | ||||
| -rw-r--r-- | lib/plugin_manager.py | 46 | ||||
| -rw-r--r-- | plugins/allcaps.py | 23 | ||||
| -rw-r--r-- | plugins/discolights.py | 22 | ||||
| -rw-r--r-- | plugins/over_g_damage.py | 28 | ||||
| -rw-r--r-- | proxy.py | 54 |
6 files changed, 142 insertions, 32 deletions
diff --git a/lib/Aircraft.py b/lib/Aircraft.py index ecbecff..dd2bbed 100644 --- a/lib/Aircraft.py +++ b/lib/Aircraft.py @@ -17,6 +17,7 @@ class Aircraft: self.id = -1 self.last_packet = None self.damage_engine_warn_sent = False + self.last_over_g_message = 0 self.just_repaired = False def reset(self): diff --git a/lib/plugin_manager.py b/lib/plugin_manager.py new file mode 100644 index 0000000..113c47a --- /dev/null +++ b/lib/plugin_manager.py @@ -0,0 +1,46 @@ +import importlib +import os +import sys +from logging import info + +PLUGIN_DIR = os.path.join(os.path.dirname(__file__), '../plugins') +sys.path.append(PLUGIN_DIR) + +class PluginManager: + def __init__(self): + self.plugins = {} + self.hooks= {} + self.load_plugins() + + def load_plugins(self): + for plugin in os.listdir(PLUGIN_DIR): + if plugin.endswith('.py') and not plugin.startswith('__'): + plugin_name = plugin[:-3] + plugin_module = importlib.import_module(plugin_name) + if hasattr(plugin_module, 'Plugin'): + if plugin_module.ENABLED: + plugin_instance = plugin_module.Plugin() + self.register_plugin(plugin_instance) + info(f"Loaded plugin {plugin_name}") + self.plugins[plugin_name] = plugin_instance + + def register_plugin(self, plugin): + """Registers the plugin with the plugin manager""" + plugin.register(self) + + def register_hook(self, hook_name, callback): + """Registers the hook with the plugin manager""" + if hook_name not in self.hooks: + self.hooks[hook_name] = [] + self.hooks[hook_name].append(callback) + + def triggar_hook(self, hook_name, data, *args, **kwargs): + """Triggars the callbacks for the hook. + If a callback returns False, then the original data is set to None + and not forwarded to the destination. This is useful for modifying + packets.""" + keep_orignal = True + if hook_name in self.hooks: + for callback in self.hooks[hook_name]: + keep_orignal = callback(data, *args, **kwargs) + return keep_orignal diff --git a/plugins/allcaps.py b/plugins/allcaps.py new file mode 100644 index 0000000..83b5aec --- /dev/null +++ b/plugins/allcaps.py @@ -0,0 +1,23 @@ +"""This plugin will convert all chat messages to all caps. +It can be enabled here by changing the value of ENABLED to True.""" +from lib.PacketManager.packets import FSNETCMD_TEXTMESSAGE +from logging import info +ENABLED = False + +class Plugin: + def __init__(self): + self.plugin_manager = None + + def register(self, plugin_manager): + self.plugin_manager = plugin_manager + self.plugin_manager.register_hook('on_chat', self.on_chat) + + def on_chat(self, data, player,message_to_client, message_to_server): + if ENABLED: + message = FSNETCMD_TEXTMESSAGE(data, should_decode=True) + message.message = message.message.upper() + message = FSNETCMD_TEXTMESSAGE.encode(f"({message.user}){message.message}", with_size=True) + message_to_server.append(message) + + + return False diff --git a/plugins/discolights.py b/plugins/discolights.py new file mode 100644 index 0000000..db441e7 --- /dev/null +++ b/plugins/discolights.py @@ -0,0 +1,22 @@ +"""This plugin will flash the lights/fog colour whenever a flight status update +is sent +It can be enabled here by changing the value of ENABLED to True.""" +from lib.PacketManager.packets import FSNETCMD_SKYCOLOR, FSNETCMD_FOGCOLOR +from random import randint +ENABLED = False + +class Plugin: + def __init__(self): + self.plugin_manager = None + + def register(self, plugin_manager): + self.plugin_manager = plugin_manager + self.plugin_manager.register_hook('on_flight_data', self.on_receive) + + def on_receive(self, data, player, messages_to_client, *args): + if ENABLED: + sky_colour_packet = FSNETCMD_SKYCOLOR.encode(randint(0, 255), randint(0, 255), randint(0, 255), True) + fog_colour_packet = FSNETCMD_FOGCOLOR.encode(randint(0, 255), randint(0, 255), randint(0, 255), True) + messages_to_client.append(sky_colour_packet) + messages_to_client.append(fog_colour_packet) + return True diff --git a/plugins/over_g_damage.py b/plugins/over_g_damage.py new file mode 100644 index 0000000..aa44ca8 --- /dev/null +++ b/plugins/over_g_damage.py @@ -0,0 +1,28 @@ +"""This plugin will cause damage to the aircraft if +it exceeds the g-force limit set in the config file.""" +from lib.PacketManager.packets import FSNETCMD_GETDAMAGE, FSNETCMD_TEXTMESSAGE +from config import G_LIM +import time +ENABLED = True + +class Plugin: + def __init__(self): + self.plugin_manager = None + + def register(self, plugin_manager): + self.plugin_manager = plugin_manager + self.plugin_manager.register_hook('on_flight_data', self.on_receive) + + def on_receive(self, data, player, messages_to_client, *args): + if ENABLED: + if abs(player.aircraft.last_packet.g_value)> G_LIM: + if time.time() - player.aircraft.last_over_g_message > 1: # Only send every second. + player.aircraft.last_over_g_message = time.time() + damage_packet = FSNETCMD_GETDAMAGE.encode(player.aircraft.id, + 1, 1, + player.aircraft.id, + 1, 11,0, True) + warning_message = FSNETCMD_TEXTMESSAGE.encode(f"You are exceeding the G Limit for the aircraft!, gValue = {player.aircraft.last_packet.g_value}!",True) + messages_to_client.append(damage_packet) + messages_to_client.append(warning_message) + return True @@ -7,6 +7,7 @@ import asyncio from struct import unpack, pack from lib.parseFlightData import parseFlightData from lib import YSchat, YSplayer, YSendFlight, YSundead, YSviaversion, Player, Aircraft +from lib.plugin_manager import PluginManager from lib.PacketManager.PacketManager import PacketManager from lib.PacketManager.packets import * import logging @@ -55,6 +56,9 @@ info("Perfect and Elegant Proxy for your YSFlight Server") info("Lisenced under GPLv3") info("Press CTRL+C to stop the proxy") +#Load the plugins +plugin_manager = PluginManager() + # Handle client connections async def handle_client(client_reader, client_writer): message_to_client = [] @@ -76,7 +80,8 @@ async def handle_client(client_reader, client_writer): while True: try: - #Test if there are any unsent messages to the client or server from other processes. + #Test if there are any unsent messages to the client or + # server from other processes. if len(message_to_client) > 0: client_writer.write(message_to_client.pop(0)) await client_writer.drain() @@ -114,34 +119,23 @@ async def handle_client(client_reader, client_writer): try: if packet_type == "FSNETCMD_LOGON": + keep_message = plugin_manager.triggar_hook('on_login', packet, player, message_to_client, message_to_server) + if not keep_message: + data = None player.login(FSNETCMD_LOGON(packet)) if player.version != YSF_VERSION and VIA_VERSION: info(f"ViaVersion enabled : Porting {player.username} from {player.version} to {YSF_VERSION}") message_to_client.append(YSchat.message(f"Porting you to YSFlight {YSF_VERSION}, This is currently Experimental")) message_to_client.append(YSchat.message(f"Please report any bugs to the server admin or join with the correct version")) - data = YSviaversion.genViaVersion(player.username, YSF_VERSION) + data = YSviaversion.genViaVersion(player.username, YSF_VERSION) #TODO: Refactor using the FSNETCMD packet. writer.write(data) continue elif packet_type == "FSNETCMD_AIRPLANESTATE": - packet = player.aircraft.add_state(FSNETCMD_AIRPLANESTATE(packet)) - - # Disco lights - # Just for fun remove in production - #skycolorPacket = FSNETCMD_SKYCOLOR.encode(randint(0,255), randint(0,255), randint(0,255), True) - #fogcolorpacket = FSNETCMD_FOGCOLOR.encode(randint(0,255), randint(0,255), randint(0,255), True) - #message_to_server.append(skycolorPacket) - #message_to_client.append(skycolorPacket) - #message_to_server.append(fogcolorpacket) - #message_to_client.append(fogcolorpacket) - - if abs(player.aircraft.last_packet.g_value) > G_LIM: - debug("G Value exceeded : ", player.aircraft.last_packet.g_value) - # We make a packet which damages the aircraft using the same pilot ID, using a gun - damageData = FSNETCMD_GETDAMAGE.encode(player.aircraft.id, 1, 1, player.aircraft.id, 1, 11, 0, True) - warnMsg = YSchat.message(f"You are exceeding the G Limit for the aircraft!, gValue = {player.aircraft.last_packet.g_value}!") - message_to_client.append(damageData) - message_to_client.append(warnMsg) + packet = player.aircraft.add_state(FSNETCMD_AIRPLANESTATE(packet)) #TODO: Do we want to convert all this to plugins? Probably not, but there is duplicated functionality + keep_message = plugin_manager.triggar_hook('on_flight_data', packet, player, message_to_client, message_to_server) + if not keep_message: + data = None if player.aircraft.life == -1: #Uninitialised player.aircraft.prev_life = player.aircraft.life @@ -174,16 +168,11 @@ async def handle_client(client_reader, client_writer): elif packet_type == "FSNETCMD_TEXTMESSAGE": msg = FSNETCMD_TEXTMESSAGE(packet) + keep_message = plugin_manager.triggar_hook('on_chat', packet, player, message_to_client, message_to_server) + if not keep_message: + data = None finalMsg = (f"{player.username} : {msg.message}") - # TODO: Remove in production - # skyColor = FSNETCMD_SKYCOLOR.encode(136, 34, 34, True) # Top Gradient - # fogColor = FSNETCMD_FOGCOLOR.encode(237, 156, 21, True) # Bottom Gradient - # fogColor = FSNETCMD_FOGCOLOR.encode(136, 34, 34, True) # Dark Orange - fogColor = FSNETCMD_FOGCOLOR.encode(120, 30, 30, True) # Magenta Tint - # message_to_client.append(skyColor) - message_to_client.append(fogColor) - # message_to_server.append(skyColor) - message_to_server.append(fogColor) + if DISCORD_ENABLED: # Make it non blocking! @@ -222,9 +211,10 @@ async def handle_client(client_reader, client_writer): pck = FSNETCMD_ENVIRONMENT.setTime(packet, True, False) data = pack("I", len(pck)) + pck - # Forward the packet to the other endpoint - writer.write(data) - await writer.drain() + # Forward the packet to the other endpoint if the data packet still exists. + if data: + writer.write(data) + await writer.drain() except (asyncio.CancelledError, ConnectionResetError, BrokenPipeError) as e: if e == BrokenPipeError or ConnectionResetError or asyncio.CancelledError: info(f"Connection closed by {player.username} : {player.ip}") |
