From 2402122cf7ed25bb61d6872bbc007ed7419dc742 Mon Sep 17 00:00:00 2001 From: Ritabrata Das Date: Sun, 16 Feb 2025 11:14:35 +0530 Subject: Add hooks and user documentation --- docs/README.md | 365 ------------------------------------------------ docs/_coverpage.md | 17 +++ docs/api/README.md | 92 ++++++++++++ docs/api/_sidebar.md | 7 + docs/api/hooks.md | 110 +++++++++++++++ docs/api/objects.md | 272 ++++++++++++++++++++++++++++++++++++ docs/api/packets.md | 1 + docs/api/plugin.md | 76 ++++++++++ docs/index.html | 7 +- docs/user/README.md | 66 +++++++++ docs/user/_sidebar.md | 3 + docs/user/advanced.md | 90 ++++++++++++ docs/user/discord.md | 20 +++ docs/user/img/step1.png | Bin 0 -> 46232 bytes docs/user/img/step2.png | Bin 0 -> 38854 bytes docs/user/img/step3.png | Bin 0 -> 25723 bytes docs/user/img/step4.png | Bin 0 -> 40307 bytes docs/user/img/step5.png | Bin 0 -> 225097 bytes docs/user/img/step6.png | Bin 0 -> 47259 bytes 19 files changed, 760 insertions(+), 366 deletions(-) delete mode 100644 docs/README.md create mode 100644 docs/_coverpage.md create mode 100644 docs/api/README.md create mode 100644 docs/api/_sidebar.md create mode 100644 docs/api/hooks.md create mode 100644 docs/api/objects.md create mode 100644 docs/api/packets.md create mode 100644 docs/api/plugin.md create mode 100644 docs/user/README.md create mode 100644 docs/user/_sidebar.md create mode 100644 docs/user/advanced.md create mode 100644 docs/user/discord.md create mode 100644 docs/user/img/step1.png create mode 100644 docs/user/img/step2.png create mode 100644 docs/user/img/step3.png create mode 100644 docs/user/img/step4.png create mode 100644 docs/user/img/step5.png create mode 100644 docs/user/img/step6.png diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 9af29ef..0000000 --- a/docs/README.md +++ /dev/null @@ -1,365 +0,0 @@ -# Sakuya AC API Documentation - -Sakuya AC : The perfect and elegant YSFlight Proxy Software. It is written in Python. Uses -asyncio so that it doesn't lag. This documentation will guide you through creating your -own Plugin for Sakuya AC. - -## Getting Started - -To get started with Sakuya AC, you need to have Python 3.9 or higher installed on your system. - -### Basic Structure of a Plugin - -There are two ways to work with the proxy: -1. Hooks : These actually modify the incoming/outgoing packets from YSF Server, these are blocking -and can be used to modify the packets. -- Example : G Limiter, Chat Filter; As these need to modify the packets at that instant - -2. Commands : These are non essential and do not modify the incoming/outgong packets, you can send your -own packets to server/client. These are non blocking. -- Example : Fog color changer, Ban command; these do not need to modify any commands at that instant - -- All plugins are saved in plugins directory in the root of the project. - -### Simple Command Plugin - -```python -""" -This is an example test command! -""" - -from lib import YSchat -from time import sleep - -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_command('test', self.test) - self.plugin_manager.register_command('timer', self.timer) - - def test(self, full_message, player, message_to_client, message_to_server): - message_to_client.append(YSchat.message("Test command received")) - return True - - def timer(self, full_message, player, message_to_client, message_to_server): - sleep(5) - message_to_client.append(YSchat.message("Timer ended")) - return True -``` -- You start the plugin by giving it a description, -- You must have a global ENABLED variable, which is set by the user - if they wish to enable the plugin or not -- You must have a class Plugin, which has a register method - use ``register_command`` to register a command. - -- ``self.plugin_manager.register_command('command_name', self.function_name)`` -- now self.function_name must take the shown parameters - -### Simple Hooks Plugin - -```python -"""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 = 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): - 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 -``` -- Unlike the previous example, this uses a hook which modifies the packet from the server at that instant -- You must return True at the end of the function to indicate that the original packet will be sent - -- (In this case, the orginal packet is the flight data, not sending will cause the client to - not receive the flight data) -- returning False, means the orginal packet which triggered the hook will not be sent to the YSF server, -this is useful for chat filters etc. - -## Object Descriptions - -### ``Aircraft`` Class - -An aircraft class designed to hold information from airplane state and related packets within a flight simulation environment. This class manages aircraft properties such as position, attitude, -life, configuration etc. - -#### Attributes - -* `parent`: Reference to the parent object. -* `name` (*str*): Aircraft name (empty initially). -* `position` (*list[float]*): 3D position [x, y, z] (initially `[0, 0, 0]`). -* `attitude` (*list[float]*): Attitude angles (initially `[0, 0, 0]`). -* `initial_config` (*dict*): Initial configuration parameters (empty initially). -* `custom_config` (*dict*): Custom configuration parameters (empty initially). -* `life` (*int*): Current life/health (initially `-1`). -* `prev_life` (*int*): Previous life value (initially `-1`). -* `id` (*int*): Unique identifier (initially `-1`). -* `last_packet`: Last received packet (initially `None`). -* `damage_engine_warn_sent` (*bool*): Damage engine warning flag (initially `False`). -* `last_over_g_message` (*int*): Last over-G message timestamp (initially `0`). -* `just_repaired` (*bool*): Just repaired flag (initially `False`). - -#### Methods - -##### `reset(self)` - -**Description:** - -Resets all aircraft attributes to their initial defaults. - -**Parameters:** - -* `self`: The `Aircraft` instance. - -**Returns:** - -* `None` - -**Resets attributes:** `name`, `position`, `attitude`, `initial_config`, `custom_config`, `life`, `prev_life`, `id`, `last_packet`, `damage_engine_warn_sent`, `just_repaired`. - -##### `set_position(self, position: list)` - -**Description:** - -Sets the aircraft's 3D position. - -**Parameters:** - -* `self`: The `Aircraft` instance. -* `position` (*list[float]*): [x, y, z] coordinates. - -**Returns:** - -* `None` - -##### `set_attitude(self, attitude: list)` - -**Description:** - -Sets the aircraft's attitude. - -**Parameters:** - -* `self`: The `Aircraft` instance. -* `attitude` (*list[float]*): Attitude angles. - -**Returns:** - -* `None` - -##### `get_position(self)` - -**Description:** - -Returns the aircraft's 3D position. - -**Parameters:** - -* `self`: The `Aircraft` instance. - -**Returns:** - -* *list[float]*: [x, y, z] coordinates. - -##### `get_altitude(self)` - -**Description:** - -Returns the aircraft's altitude (Z-coordinate) in meters. - -**Parameters:** - -* `self`: The `Aircraft` instance. - -**Returns:** - -* *float*: Altitude in meters. - -##### `get_attitude(self)` - -**Description:** - -Returns the aircraft's attitude. - -**Parameters:** - -* `self`: The `Aircraft` instance. - -**Returns:** - -* *list[float]*: Attitude angles. - -##### `set_initial_config(self, config: dict)` - -**Description:** - -Sets the aircraft's initial configuration. - -**Parameters:** - -* `self`: The `Aircraft` instance. -* `config` (*dict*): Initial configuration key-value pairs. - -**Returns:** - -* `None` - -##### `get_initial_config_value(self, key: str)` - -**Description:** - -Retrieves a value from `initial_config`. - -**Parameters:** - -* `self`: The `Aircraft` instance. -* `key` (*str*): Configuration key. - -**Returns:** - -* *Any*: Configuration value or `None` if key not found. - -##### `set_custom_config_value(self, key: str, value)` - -**Description:** - -Sets a custom configuration value. - -**Parameters:** - -* `self`: The `Aircraft` instance. -* `key` (*str*): Configuration key. -* `value` (*Any*): Configuration value. - -**Returns:** - -* `None` - -##### `add_state(self, packet: FSNETCMD_AIRPLANESTATE)` - -**Description:** - -Updates aircraft state from an `FSNETCMD_AIRPLANESTATE` packet. - -**Parameters:** - -* `self`: The `Aircraft` instance. -* `packet` (*FSNETCMD_AIRPLANESTATE*): Airplane state packet. - -**Returns:** - -* *FSNETCMD_AIRPLANESTATE* or *None*: Input `packet` if processed, `None` if ID mismatch. - -**Functionality:** Updates `life`, `position`, `attitude` and stores the `last_packet`. - -##### `check_command(self, command: FSNETCMD_AIRCMD)` - -**Description:** - -Processes an `FSNETCMD_AIRCMD` packet for configuration commands. - -**Parameters:** - -* `self`: The `Aircraft` instance. -* `command` (*FSNETCMD_AIRCMD*): Air command packet. - -**Returns:** - -* `None` - -**Functionality:** Updates `initial_config` based on the command. Logs the command in debug. - -##### `set_afterburner(self, enabled: bool)` - -**Description:** - -Toggles the afterburner if available. - -**Parameters:** - -* `self`: The `Aircraft` instance. -* `enabled` (*bool*): `True` to enable, `False` to disable. - -**Returns:** - -* *FSNETCMD_AIRCMD* or *None*: Result of `FSNETCMD_AIRCMD.set_afterburner` if afterburner available, else `None`. - -**Functionality:** Checks for "AFTBURNR" in `initial_config` and sends command if available. - -### `Player` Class - -The `Player` class represents a connected client. It stores key information such as their username, alias, IP address, - and the `Aircraft` object they are currently piloting. - -#### Attributes - -* **`username`**: The player's username (string). Set via the `login` method. -* **`alias`**: The player's alias (string). Set via the `login` method. -* **`aircraft`**: An `Aircraft` object instance representing the aircraft the player is currently flying. Initially an empty `Aircraft` object and populated through `check_add_object` or `set_aircraft`. -* **`version`**: The client version (integer). Set via the `login` method. -* **`ip`**: The player's IP address (string). Set via the `set_ip` method. -* **`streamWriterObject`**: Object for handling network communication with the player's client. -* **`is_a_bot`**: A boolean flag indicating if the player is considered a bot. Initially `True`, and is intended to be set to `False` after a successful `LOGIN` packet is processed, to differentiate real players from initial bot-like states. - -#### Methods - -##### `set_aircraft(aircraft: Aircraft)` - -```python -set_aircraft(aircraft: Aircraft) -``` -Assigns a specific `Aircraft` object to this player, representing the aircraft they are currently flying. Useful when you need to manually set or update the player's aircraft. - -* **`aircraft`**: An `Aircraft` object instance. - -##### `login(packet: FSNETCMD_LOGON)` - -```python -login(packet: FSNETCMD_LOGON) -``` -Processes a login packet (`FSNETCMD_LOGON`) to extract and set the player's `username`, `alias`, and client `version`. This is typically called upon receiving a successful login packet from the client. - -* **`packet`**: An `FSNETCMD_LOGON` packet instance containing login details. - -##### `set_ip(ip)` - -```python -set_ip(ip) -``` -Sets the IP address associated with this player's connection. - -* **`ip`**: A string representing the player's IP address. - -##### `check_add_object(packet: FSNETCMD_ADDOBJECT)` - -```python -check_add_object(packet: FSNETCMD_ADDOBJECT) -``` -Checks if an `ADDOBJECT` packet (`FSNETCMD_ADDOBJECT`) pertains to this player based on the pilot's username in the packet. If it does, it initializes a new `Aircraft` object for the player using data from the packet, effectively setting the aircraft they are flying. Returns `True` if the aircraft was initialized, `False` otherwise. - -* **`packet`**: An `FSNETCMD_ADDOBJECT` packet instance containing aircraft creation details. -* **Returns**: `True` if the packet was for this player and the aircraft was initialized, `False` otherwise. - -##### `__str__()` - -```python -__str__() -``` -Returns a user-friendly string representation of the `Player` object. This string includes the player's `username`, the name of their `aircraft`, and its current `position`. Useful for logging and debugging purposes. - -* **Returns**: A descriptive string of the `Player` object. diff --git a/docs/_coverpage.md b/docs/_coverpage.md new file mode 100644 index 0000000..78ec250 --- /dev/null +++ b/docs/_coverpage.md @@ -0,0 +1,17 @@ +![logo](/favicon.ico) + +# Sakuya AC + +> The Perfect and Elegant YSFlight Proxy Software + +- Extensible, has a well documented plugin API +- Fast, uses asyncio so that it doesn't lag +- Completely free and open source lisenced under GPLv3 + +Contributors +- Skipper +- Biry + +[Github](https://github.com/the-indian-dev/sakuya-ac) +[API Documentation](/api/README.md) +[User Documentation](/user/README.md) diff --git a/docs/api/README.md b/docs/api/README.md new file mode 100644 index 0000000..736c0aa --- /dev/null +++ b/docs/api/README.md @@ -0,0 +1,92 @@ +# Sakuya AC API Documentation + +Sakuya AC : The perfect and elegant YSFlight Proxy Software. It is written in Python. Uses +asyncio so that it doesn't lag. This documentation will guide you through creating your +own Plugin for Sakuya AC. + +## Getting Started + +To get started with Sakuya AC, you need to have Python 3.9 or higher installed on your system. + +### Basic Structure of a Plugin + +There are two ways to work with the proxy: +1. Hooks : These actually modify the incoming/outgoing packets from YSF Server, these are blocking +and can be used to modify the packets. +- Example : G Limiter, Chat Filter; As these need to modify the packets at that instant + +2. Commands : These are non essential and do not modify the incoming/outgong packets, you can send your +own packets to server/client. These are non blocking. +- Example : Fog color changer, Ban command; these do not need to modify any commands at that instant + +- All plugins are saved in plugins directory in the root of the project. + +### Simple Command Plugin + +```python +""" +This is an example test command! +""" + +from lib import YSchat +from time import sleep + +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_command('test', self.test) + self.plugin_manager.register_command('timer', self.timer) + + def test(self, full_message, player, message_to_client, message_to_server): + message_to_client.append(YSchat.message("Test command received")) + return True + + def timer(self, full_message, player, message_to_client, message_to_server): + sleep(5) + message_to_client.append(YSchat.message("Timer ended")) + return True +``` +- You start the plugin by giving it a description, +- You must have a global ENABLED variable, which is set by the user + if they wish to enable the plugin or not +- You must have a class Plugin, which has a register method + use ``register_command`` to register a command. + -- ``self.plugin_manager.register_command('command_name', self.function_name)`` +- now self.function_name must take the shown parameters + +### Simple Hooks Plugin + +```python +"""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 = 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): + 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 +``` +- Unlike the previous example, this uses a hook which modifies the packet from the server at that instant +- You must return True at the end of the function to indicate that the original packet will be sent + -- (In this case, the orginal packet is the flight data, not sending will cause the client to + not receive the flight data) +- returning False, means the orginal packet which triggered the hook will not be sent to the YSF server, +this is useful for chat filters etc. diff --git a/docs/api/_sidebar.md b/docs/api/_sidebar.md new file mode 100644 index 0000000..a8f77cf --- /dev/null +++ b/docs/api/_sidebar.md @@ -0,0 +1,7 @@ +* [Home](/api/README.md) +* [Plugin Structure](/api/plugin.md) +* [Class Reference](/api/objects.md) + - [Aircraft](/api/objects.md#aircraft-class) + - [Player](/api/objects.md#player-class) +* [Packet Reference](/api/packets.md) +* [Hooks Reference](/api/hooks.md) diff --git a/docs/api/hooks.md b/docs/api/hooks.md new file mode 100644 index 0000000..373b42f --- /dev/null +++ b/docs/api/hooks.md @@ -0,0 +1,110 @@ +# Introduction + +Hooks are blocking functions that are called when a specific event occurs. +They are used to modify the behavior of the proxy. Every hook function +must return a `bool` value, `True` if the packet that triggered the hook +should be sent to the client/server or `False` if the packet should be +dropped and not sent. + +# Hook Structure + +In main `Plugin` Class, there must be a `register` method, under which +you must declare your hooks. +> eg. +> ```python +> def register(self, plugin_manager): +> self.plugin_manager = plugin_manager +> self.plugin_manager.register_hook('on_flight_data', self.on_receive) +>``` +> This will trigger the `on_receive` method of your plugin when the `on_flight_data` hook is triggered. + +# Hook Function Structure + +Every hook function must have the following structure: +```python +def on_receive(self, data, player, message_to_client, message_to_server) +``` +Where: +- `data` is the packet object that triggered the hook, It is a `bytes` object. +The data is sent without the header.(that is the size of the packet) + +- `player` is the `Player` object that triggered the hook. + +- `message_to_client` is the list that contains the packets that will be sent to the client. + you must append to it and return a value for the packet to be sent. + +- `message_to_server` is the list that contains the packets that will be sent to the server. + you must append to it and return a value for the packet to be sent. + +> You must return a `bool` value, `True` if the packet that triggered the hook should be +> sent to the client/server or `False` if the packet should be dropped and not sent. + +# Hook List + +## Client to Server Side + +| Hook | Packet Object | +|-------------------------|-----------------------| +| `on_login` | `FSNETCMD_LOGON` | +| `on_logout` | `FSNETCMD_LOGOFF` | +| `on_error` | `FSNETCMD_ERROR` | +| `on_load_field` | `FSNETCMD_LOADFIELD` | +| `on_add_object` | `FSNETCMD_ADDOBJECT` | +| `on_readback` | `FSNETCMD_READBACK` | +| `on_smoke_color` | `FSNETCMD_SMOKECOLOR` | +| `on_join_request` | `FSNETCMD_JOINREQUEST`| +| `on_join_approval` | `FSNETCMD_JOINAPPROVAL`| +| `on_reject_join_request`| `FSNETCMD_REJECTJOINREQ`| +| `on_flight_data` | `FSNETCMD_AIRPLANESTATE`| +| `on_unjoin` | `FSNETCMD_UNJOIN` | +| `on_remove_airplane` | `FSNETCMD_REMOVEAIRPLANE`| +| `on_request_test_airplane`| `FSNETCMD_REQUESTTESTAIRPLANE`| +| `on_kill_server` | `FSNETCMD_KILLSERVER` | +| `on_prepare_simulation` | `FSNETCMD_PREPARESIMULATION`| +| `on_test_packet` | `FSNETCMD_TESTPACKET` | +| `on_lockon` | `FSNETCMD_LOCKON` | +| `on_remove_ground` | `FSNETCMD_REMOVEGROUND`| +| `on_missile_launch` | `FSNETCMD_MISSILELAUNCH`| +| `on_get_damage` | `FSNETCMD_GETDAMAGE` | +| `on_weapon_config` | `FSNETCMD_WEAPONCONFIG`| +| `on_air_cmd` | `FSNETCMD_AIRCMD` | +| `on_chat` | `FSNETCMD_TEXTMESSAGE`| +| `on_environment` | `FSNETCMD_ENVIRONMENT`| +| `on_sky_color` | `FSNETCMD_SKYCOLOR` | +| `on_fog_color` | `FSNETCMD_FOGCOLOR` | +| `on_list` | `FSNETCMD_LIST` | + +
+ +## Server to Client Side + +| Hook | Packet Object | +|-------------------------------|-----------------------| +| `on_login_server` | `FSNETCMD_LOGON` | +| `on_logout_server` | `FSNETCMD_LOGOFF` | +| `on_error_server` | `FSNETCMD_ERROR` | +| `on_load_field_server` | `FSNETCMD_LOADFIELD` | +| `on_add_object_server` | `FSNETCMD_ADDOBJECT` | +| `on_readback_server` | `FSNETCMD_READBACK` | +| `on_smoke_color_server` | `FSNETCMD_SMOKECOLOR` | +| `on_join_request_server` | `FSNETCMD_JOINREQUEST`| +| `on_join_approval_server` | `FSNETCMD_JOINAPPROVAL`| +| `on_reject_join_request_server`| `FSNETCMD_REJECTJOINREQ`| +| `on_flight_data_server` | `FSNETCMD_AIRPLANESTATE`| +| `on_unjoin_server` | `FSNETCMD_UNJOIN` | +| `on_remove_airplane_server` | `FSNETCMD_REMOVEAIRPLANE`| +| `on_request_test_airplane_server`| `FSNETCMD_REQUESTTESTAIRPLANE`| +| `on_kill_server_server` | `FSNETCMD_KILLSERVER` | +| `on_prepare_simulation_server`| `FSNETCMD_PREPARESIMULATION`| +| `on_test_packet_server` | `FSNETCMD_TESTPACKET` | +| `on_lockon_server` | `FSNETCMD_LOCKON` | +| `on_remove_ground_server` | `FSNETCMD_REMOVEGROUND`| +| `on_missile_launch_server` | `FSNETCMD_MISSILELAUNCH`| +| `on_get_damage_server` | `FSNETCMD_GETDAMAGE` | +| `on_weapon_config_server` | `FSNETCMD_WEAPONCONFIG`| +| `on_air_cmd_server` | `FSNETCMD_AIRCMD` | +| `on_chat_server` | `FSNETCMD_TEXTMESSAGE`| +| `on_environment_server` | `FSNETCMD_ENVIRONMENT`| +| `on_sky_color_server` | `FSNETCMD_SKYCOLOR` | +| `on_fog_color_server` | `FSNETCMD_FOGCOLOR` | +| `on_list_server` | `FSNETCMD_LIST` | diff --git a/docs/api/objects.md b/docs/api/objects.md new file mode 100644 index 0000000..bdd5d49 --- /dev/null +++ b/docs/api/objects.md @@ -0,0 +1,272 @@ +## Object Descriptions + +### ``Aircraft`` Class + +An aircraft class designed to hold information from airplane state and related packets within a flight simulation environment. This class manages aircraft properties such as position, attitude, +life, configuration etc. + +#### Attributes + +* `parent`: Reference to the parent object. +* `name` (*str*): Aircraft name (empty initially). +* `position` (*list[float]*): 3D position [x, y, z] (initially `[0, 0, 0]`). +* `attitude` (*list[float]*): Attitude angles (initially `[0, 0, 0]`). +* `initial_config` (*dict*): Initial configuration parameters (empty initially). +* `custom_config` (*dict*): Custom configuration parameters (empty initially). +* `life` (*int*): Current life/health (initially `-1`). +* `prev_life` (*int*): Previous life value (initially `-1`). +* `id` (*int*): Unique identifier (initially `-1`). +* `last_packet`: Last received packet (initially `None`). +* `damage_engine_warn_sent` (*bool*): Damage engine warning flag (initially `False`). +* `last_over_g_message` (*int*): Last over-G message timestamp (initially `0`). +* `just_repaired` (*bool*): Just repaired flag (initially `False`). + +#### Methods + +##### `reset(self)` + +**Description:** + +Resets all aircraft attributes to their initial defaults. + +**Parameters:** + +* `self`: The `Aircraft` instance. + +**Returns:** + +* `None` + +**Resets attributes:** `name`, `position`, `attitude`, `initial_config`, `custom_config`, `life`, `prev_life`, `id`, `last_packet`, `damage_engine_warn_sent`, `just_repaired`. + +##### `set_position(self, position: list)` + +**Description:** + +Sets the aircraft's 3D position. + +**Parameters:** + +* `self`: The `Aircraft` instance. +* `position` (*list[float]*): [x, y, z] coordinates. + +**Returns:** + +* `None` + +##### `set_attitude(self, attitude: list)` + +**Description:** + +Sets the aircraft's attitude. + +**Parameters:** + +* `self`: The `Aircraft` instance. +* `attitude` (*list[float]*): Attitude angles. + +**Returns:** + +* `None` + +##### `get_position(self)` + +**Description:** + +Returns the aircraft's 3D position. + +**Parameters:** + +* `self`: The `Aircraft` instance. + +**Returns:** + +* *list[float]*: [x, y, z] coordinates. + +##### `get_altitude(self)` + +**Description:** + +Returns the aircraft's altitude (Z-coordinate) in meters. + +**Parameters:** + +* `self`: The `Aircraft` instance. + +**Returns:** + +* *float*: Altitude in meters. + +##### `get_attitude(self)` + +**Description:** + +Returns the aircraft's attitude. + +**Parameters:** + +* `self`: The `Aircraft` instance. + +**Returns:** + +* *list[float]*: Attitude angles. + +##### `set_initial_config(self, config: dict)` + +**Description:** + +Sets the aircraft's initial configuration. + +**Parameters:** + +* `self`: The `Aircraft` instance. +* `config` (*dict*): Initial configuration key-value pairs. + +**Returns:** + +* `None` + +##### `get_initial_config_value(self, key: str)` + +**Description:** + +Retrieves a value from `initial_config`. + +**Parameters:** + +* `self`: The `Aircraft` instance. +* `key` (*str*): Configuration key. + +**Returns:** + +* *Any*: Configuration value or `None` if key not found. + +##### `set_custom_config_value(self, key: str, value)` + +**Description:** + +Sets a custom configuration value. + +**Parameters:** + +* `self`: The `Aircraft` instance. +* `key` (*str*): Configuration key. +* `value` (*Any*): Configuration value. + +**Returns:** + +* `None` + +##### `add_state(self, packet: FSNETCMD_AIRPLANESTATE)` + +**Description:** + +Updates aircraft state from an `FSNETCMD_AIRPLANESTATE` packet. + +**Parameters:** + +* `self`: The `Aircraft` instance. +* `packet` (*FSNETCMD_AIRPLANESTATE*): Airplane state packet. + +**Returns:** + +* *FSNETCMD_AIRPLANESTATE* or *None*: Input `packet` if processed, `None` if ID mismatch. + +**Functionality:** Updates `life`, `position`, `attitude` and stores the `last_packet`. + +##### `check_command(self, command: FSNETCMD_AIRCMD)` + +**Description:** + +Processes an `FSNETCMD_AIRCMD` packet for configuration commands. + +**Parameters:** + +* `self`: The `Aircraft` instance. +* `command` (*FSNETCMD_AIRCMD*): Air command packet. + +**Returns:** + +* `None` + +**Functionality:** Updates `initial_config` based on the command. Logs the command in debug. + +##### `set_afterburner(self, enabled: bool)` + +**Description:** + +Toggles the afterburner if available. + +**Parameters:** + +* `self`: The `Aircraft` instance. +* `enabled` (*bool*): `True` to enable, `False` to disable. + +**Returns:** + +* *FSNETCMD_AIRCMD* or *None*: Result of `FSNETCMD_AIRCMD.set_afterburner` if afterburner available, else `None`. + +**Functionality:** Checks for "AFTBURNR" in `initial_config` and sends command if available. + +### `Player` Class + +The `Player` class represents a connected client. It stores key information such as their username, alias, IP address, + and the `Aircraft` object they are currently piloting. + +#### Attributes + +* **`username`**: The player's username (string). Set via the `login` method. +* **`alias`**: The player's alias (string). Set via the `login` method. +* **`aircraft`**: An `Aircraft` object instance representing the aircraft the player is currently flying. Initially an empty `Aircraft` object and populated through `check_add_object` or `set_aircraft`. +* **`version`**: The client version (integer). Set via the `login` method. +* **`ip`**: The player's IP address (string). Set via the `set_ip` method. +* **`streamWriterObject`**: Object for handling network communication with the player's client. +* **`is_a_bot`**: A boolean flag indicating if the player is considered a bot. Initially `True`, and is intended to be set to `False` after a successful `LOGIN` packet is processed, to differentiate real players from initial bot-like states. + +#### Methods + +##### `set_aircraft(aircraft: Aircraft)` + +```python +set_aircraft(aircraft: Aircraft) +``` +Assigns a specific `Aircraft` object to this player, representing the aircraft they are currently flying. Useful when you need to manually set or update the player's aircraft. + +* **`aircraft`**: An `Aircraft` object instance. + +##### `login(packet: FSNETCMD_LOGON)` + +```python +login(packet: FSNETCMD_LOGON) +``` +Processes a login packet (`FSNETCMD_LOGON`) to extract and set the player's `username`, `alias`, and client `version`. This is typically called upon receiving a successful login packet from the client. + +* **`packet`**: An `FSNETCMD_LOGON` packet instance containing login details. + +##### `set_ip(ip)` + +```python +set_ip(ip) +``` +Sets the IP address associated with this player's connection. + +* **`ip`**: A string representing the player's IP address. + +##### `check_add_object(packet: FSNETCMD_ADDOBJECT)` + +```python +check_add_object(packet: FSNETCMD_ADDOBJECT) +``` +Checks if an `ADDOBJECT` packet (`FSNETCMD_ADDOBJECT`) pertains to this player based on the pilot's username in the packet. If it does, it initializes a new `Aircraft` object for the player using data from the packet, effectively setting the aircraft they are flying. Returns `True` if the aircraft was initialized, `False` otherwise. + +* **`packet`**: An `FSNETCMD_ADDOBJECT` packet instance containing aircraft creation details. +* **Returns**: `True` if the packet was for this player and the aircraft was initialized, `False` otherwise. + +##### `__str__()` + +```python +__str__() +``` +Returns a user-friendly string representation of the `Player` object. This string includes the player's `username`, the name of their `aircraft`, and its current `position`. Useful for logging and debugging purposes. + +* **Returns**: A descriptive string of the `Player` object. diff --git a/docs/api/packets.md b/docs/api/packets.md new file mode 100644 index 0000000..f10c9a1 --- /dev/null +++ b/docs/api/packets.md @@ -0,0 +1 @@ +Incomplete! diff --git a/docs/api/plugin.md b/docs/api/plugin.md new file mode 100644 index 0000000..2d05ece --- /dev/null +++ b/docs/api/plugin.md @@ -0,0 +1,76 @@ +# Single File Plugins + +These plugins are contained in a single `.py` files, recomemded for small plugins. + +```python +""" +This is an example test command! +""" + +from lib import YSchat + +# ENABLED variable must be present in your plugin otherwise it will +# fail to load. This is used by user to change the state of the plugin + +ENABLED = True + +class Plugin: + def __init__(self): + # Intialise the plugin here + self.plugin_manager = None + + def register(self, plugin_manager): + # Here you declare functions of your plugin + # Bind to plugin manager + self.plugin_manager = plugin_manager + # Register your plugin commands + self.plugin_manager.register_command('test', self.test) + # Register your plugin hooks + self.plugin_manager.register_hook('on_flight_data', self.on_receive) + + # Command Function + def test(self, full_message, player, message_to_client, message_to_server): + message_to_client.append(YSchat.message("Test command received")) + return True + + # Hook Function + def on_receive(self, data, player, message_to_client, message_to_server): + print(f"Received flight data of {player.username}") + return True +``` + +# Multi File Plugins + +These plugins are contained in a directory with multiple files, recomemded for large plugins. + +```bash +plugin/ +├── Plugin.py +└── __init__.py +``` + +```python +# __init__.py +from .Plugin import Plugin +from .Plugin import ENABLED +``` + +```python +# Plugin.py +""" +This is a multi file plugin example +""" +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_flight_data) + + def on_flight_data(self, data, player,message_to_client, message_to_server): + print(f"Received flight data of {player.username}") + return True +``` diff --git a/docs/index.html b/docs/index.html index 551272b..f653dd2 100644 --- a/docs/index.html +++ b/docs/index.html @@ -15,9 +15,12 @@
+ + diff --git a/docs/user/README.md b/docs/user/README.md new file mode 100644 index 0000000..8626ae1 --- /dev/null +++ b/docs/user/README.md @@ -0,0 +1,66 @@ +## Introduction + +Sakuya AC has the following features: +1. Discord Chat sync +2. G Limiter +3. Plugin Support +etc. + +This guide will help you to install Sakuya AC on your server + +## Quick Start Guide + +1. Clone the git repository +```bash +git clone git@github.com:the-indian-dev/sakuya-ac +``` + +2. You will need Python 3.9 or above to run Sakuya AC +Please make sure you have correct version of Python + +3. Install the dependencies + +- For Windows +```bash +py -m pip install -r requirements.txt +``` +- For Debian, Ubuntu etc. +```bash +sudo apt install python3-aiohttp +``` +- For Arch Linux +```bash +sudo pacman -S python-aiohttp +``` + +4. Setup Configuration +- Open `config.py` in a text editor +- Start YSF Server and put its IP in `SERVER_HOST` and Port in `SERVER_PORT`. + The Proxy will run at the port given at `PROXY_PORT` + +5. Run the Proxy +```bash +python proxy.py +``` +You can now connect to the Proxy server at the port you specified in `PROXY_PORT` + +- Advanced Configuration is documented [here](/user/advanced.md) + +## Installing Plugins + +!> Plugins can be harmful to your computer, please install from trusted sources. + We are NOT responsible for installing plugins from untrusted sources. + +1. Download plugin from a trusted source +2. Put the plugin in the `plugins` directory + - If the plugin is a `.py` file, put it in the `plugins` directory + - If the plugin is a `.zip` file, extract it and put it in the `plugins` directory +3. Set the `ENABLED` variable in the plugin file to `True` +4. Run the proxy + +## Reporting Issues + +1. Please set logging level to `DEBUG` in `config.py` +2. Please provide the replay .yfs file for the issue +3. Provide steps to reproduce the bug +4. Start a new issue on our Github repository. diff --git a/docs/user/_sidebar.md b/docs/user/_sidebar.md new file mode 100644 index 0000000..fe12b7f --- /dev/null +++ b/docs/user/_sidebar.md @@ -0,0 +1,3 @@ +* [Getting Started](/user/README.md) +* [Advanced Configuration](/user/advanced.md) +* [Setting up Discord Chat Sync](/user/discord.md) diff --git a/docs/user/advanced.md b/docs/user/advanced.md new file mode 100644 index 0000000..aed5019 --- /dev/null +++ b/docs/user/advanced.md @@ -0,0 +1,90 @@ +# `config.py` Configuration + +The `config.py` file is the main configuration file for Sakuya AC. It contains the following configuration options: + +### `LOGGING_LEVEL` + +The logging level for the proxy. The logging level can be one of the following: +1. `DEBUG` : Logs all messages (Recomedded for debugging and reporting issues) +2. `INFO` : Logs only informational messages (Default, Recomended for normal use) +3. `WARNING` : Logs only warning messages +4. `CRTITICAL` : Logs only critical messages + +### `SERVER_HOST` + +The IP address of the YSF Server. The proxy will connect to this IP address. +For local server, use `"127.0.0.1"`, It takes string values, so make sure to +put quotaion marks around the IP address. + +### `SERVER_PORT` + +The port of the YSF Server. The proxy will connect to this port. The default is +`7915`, which is the default port for YSF Server. It takes integer values. Do not +put quotation marks around the port number. + +### `PROXY_PORT` + +The port on which the proxy will run. The default is `9000`. It takes integer values. + +### `WELCOME_MESSAGE` + +The welcome message that will be displayed when a client logs in to the YSFlight server. +It takes string values. You can use the following placeholders in the message: +- `{username}` : The username of the client +> eg. `Welcome {username} to the server!` +> This will display `Welcome Sakuya to the server!` when a client with username +> `Sakuya` logs in. + +### `PREFIX` + +This is the prefix for the chat commands in your server. The default is `/`. +It takes string values. Make sure to put quotation marks around the prefix. +> eg. If you set the prefix to `!`, then the command to change fog color would be `!fog` + +!> Messages starting with prefix will be treated as commands. Make sure to set a + prefix that is not used in normal chat messages. + +### `YSF_VERSION` + +The version for your YSFlight server. The default is `20150425`. It takes integer values. + +### `G_LIM` + +The G-Limiter value for your server. The default is `4`. It takes integer values. +The client will take damage if absolute value of their G force exceeds this value. + +- To change the interval at which the client takes G Force Damage, change the value of +`INTERVAL` in `plugins/over_g_damage.py`. The default is `0.2`, which means the client +takes damage every 0.2 seconds. + +### `HEALTH_HACK_MESSAGE` + +The message that will be displayed when a client tries to hack their health. It takes +string values. The username of the client is automatically appended to the end of the +message + +>eg. `HEALTH_HACK_MESSAGE = "Detected for Health Hack"` +> This will display `Detected for Health Hack by Sakuya` when a client tries to cheat. + +### `SMOKE_PLANE` + +If the plane should smoke when it has a life lower than `SMOKE_LIFE`. The default is `True`. + +### `SMOKE_LIFE` + +The life value below which the plane should start smoking. The default is `5`. It takes integer values. + +### `DISCORD_ENABLED` + +If the Discord Chat Sync should be enabled. The default is `False`. It takes boolean values. +You must `aiohttp` installed to use this feature. + +Setting up Discord Chat Sync is documented [here](/user/discord.md) + +### `DISCORD_TOKEN` + +The Discord Bot Token for the Discord Chat Sync. It takes string values. + +### `CHANNEL_ID` + +The Channel ID of the channel where the chat messages will be sent. It takes integer values. diff --git a/docs/user/discord.md b/docs/user/discord.md new file mode 100644 index 0000000..b6ea279 --- /dev/null +++ b/docs/user/discord.md @@ -0,0 +1,20 @@ +# Creating a Discord Bot account + +1. Go to [Discord Developer Portal](https://discord.com/developers/applications), + and login with your Discord account. +2. Click on `New Application` and give your bot a name. + ![Step 1](img/step1.png) +3. Go to the left sidebar and click on `Bot`. + ![Step 2](img/step3.png) +4. Click on `Reset Token` and copy the token. This your bot token for `DISCORD_TOKEN` in `config.py`. + ![Step 3](img/step4.png) +5. Scroll down to `Privileged Gateway Intents`. +6. Enable **Message Content** Intent. This is important! + ![Step 4](img/step5.png) + +# Getting Channel ID + +1. Go to your Discord server and right click on the channel where you want to sync the chat. +2. Click on `Copy ID` to copy the channel ID. + ![Step 5](img/step6.png) +3. Paste the channel ID in `CHANNEL_ID` in `config.py`. diff --git a/docs/user/img/step1.png b/docs/user/img/step1.png new file mode 100644 index 0000000..e7b2447 Binary files /dev/null and b/docs/user/img/step1.png differ diff --git a/docs/user/img/step2.png b/docs/user/img/step2.png new file mode 100644 index 0000000..3eee04e Binary files /dev/null and b/docs/user/img/step2.png differ diff --git a/docs/user/img/step3.png b/docs/user/img/step3.png new file mode 100644 index 0000000..165f711 Binary files /dev/null and b/docs/user/img/step3.png differ diff --git a/docs/user/img/step4.png b/docs/user/img/step4.png new file mode 100644 index 0000000..0fd4edd Binary files /dev/null and b/docs/user/img/step4.png differ diff --git a/docs/user/img/step5.png b/docs/user/img/step5.png new file mode 100644 index 0000000..69fdf8a Binary files /dev/null and b/docs/user/img/step5.png differ diff --git a/docs/user/img/step6.png b/docs/user/img/step6.png new file mode 100644 index 0000000..f7f847f Binary files /dev/null and b/docs/user/img/step6.png differ -- cgit v1.2.3