aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRitabrata Das <[email protected]>2025-02-23 19:05:56 +0530
committerGitHub <[email protected]>2025-02-23 19:05:56 +0530
commitf9fd6a1810aa1e1c89ce7b875a3c26731f553b9c (patch)
tree2c0b8abe8a29c981e1c36c7e6bb5da8dc62606b0
parent86cc54fd0cf49ce05191620798b566fa9c49ff94 (diff)
parent30c7e24aa8de762a1b8c21476e9e8436db1b9826 (diff)
Merge pull request #9 from the-indian-dev/dev
Release 20250223 version
-rw-r--r--lib/PacketManager/packets/FSNETCMD_ADDOBJECT.py27
-rw-r--r--lib/PacketManager/packets/FSNETCMD_AIRPLANESTATE.py6
-rw-r--r--lib/PacketManager/packets/FSNETCMD_JOINREQUEST.py4
-rw-r--r--lib/Player.py1
-rw-r--r--lib/plugin_manager.py6
-rw-r--r--plugins/aircraft_replacer.py70
-rw-r--r--plugins/radar.py88
-rw-r--r--plugins/refuel.py74
-rw-r--r--proxy.py11
9 files changed, 251 insertions, 36 deletions
diff --git a/lib/PacketManager/packets/FSNETCMD_ADDOBJECT.py b/lib/PacketManager/packets/FSNETCMD_ADDOBJECT.py
index ef7f785..61e2b3f 100644
--- a/lib/PacketManager/packets/FSNETCMD_ADDOBJECT.py
+++ b/lib/PacketManager/packets/FSNETCMD_ADDOBJECT.py
@@ -46,7 +46,34 @@ class FSNETCMD_ADDOBJECT: #5
#There is an extra short, but it's just set to 0
if len(self.buffer) >= 176:
self.pilot = unpack("32s", self.buffer[124:156])[0].decode().strip('\x00')
+
+ def to_dict(self):
+ return {
+ "object_type": self.object_type,
+ "net_type": self.net_type,
+ "object_id": self.object_id,
+ "iff": self.iff,
+ "pos": self.pos,
+ "atti": self.atti,
+ "identifier": self.identifier,
+ "substrname": self.substrname,
+ "ysfid": self.ysfid,
+ "flags": self.flags,
+ "flags0": self.flags0,
+ "outside_radius": self.outsideRadius,
+ "aircraft_class": self.aircraft_class,
+ "aircraft_category": self.aircraft_category,
+ "pilot": self.pilot
+ }
+ def to_packet(self, with_size:bool=True):
+ return FSNETCMD_ADDOBJECT.encode(self.object_type, self.net_type,
+ self.object_id, self.iff, self.pos,
+ self.atti, self.identifier, self.substrname,
+ self.ysfid, self.flags, self.flags0,
+ self.outsideRadius, self.aircraft_class,
+ self.aircraft_category, self.pilot, with_size)
+
@staticmethod
def encode(object_type, net_type, object_id, iff, pos, atti, identifier, substrname, ysfid,
flags, flags0, outside_radius, aircraft_class=None, aircraft_category=None,
diff --git a/lib/PacketManager/packets/FSNETCMD_AIRPLANESTATE.py b/lib/PacketManager/packets/FSNETCMD_AIRPLANESTATE.py
index 3ebf8d6..9ebfc00 100644
--- a/lib/PacketManager/packets/FSNETCMD_AIRPLANESTATE.py
+++ b/lib/PacketManager/packets/FSNETCMD_AIRPLANESTATE.py
@@ -198,7 +198,7 @@ class FSNETCMD_AIRPLANESTATE: #11
packet = self.buffer[:56] + pack('h',flag) + self.buffer[58:]
return pack('I',len(packet)) + packet
else:
- flag = unpack('h',self.buffer[74:75])[0]
+ flag = unpack('h',self.buffer[74:76])[0]
flag &= ~((255<<8)|2)
flag |= (255<<8) | 2
@@ -213,7 +213,7 @@ class FSNETCMD_AIRPLANESTATE: #11
packet = self.buffer[:56] + pack('h',flag) + self.buffer[58:]
return pack('I',len(packet)) + packet
else:
- flag = unpack('h',self.buffer[74:75])[0]
+ flag = unpack('h',self.buffer[74:76])[0]
flag &= ~(8)
packet = self.buffer[:74] + pack('h',flag) + self.buffer[76:]
return pack('I',len(packet)) + packet
@@ -235,7 +235,7 @@ class FSNETCMD_AIRPLANESTATE: #11
buffer = pack("IfI", 11, remote_time, player_id)
buffer += pack("H", packet_version)
if packet_version == 4 or packet_version == 5:
- buffer += pack("fffhhhhhhhhh", *position, *atti, *velocity, *atti_velocity)
+ buffer += pack("3f9H", *position, *atti, *velocity, *atti_velocity)
buffer += pack("hhhh", smoke_oil, fuel, payload, 0)
buffer += pack("BB", flight_state, int(vgw*255))
buffer += pack("BB", int(spoiler*15)<<4 | int(landing_gear*15), int(flap*15)<<4 | int(brake*15))
diff --git a/lib/PacketManager/packets/FSNETCMD_JOINREQUEST.py b/lib/PacketManager/packets/FSNETCMD_JOINREQUEST.py
index 7796599..f24c94a 100644
--- a/lib/PacketManager/packets/FSNETCMD_JOINREQUEST.py
+++ b/lib/PacketManager/packets/FSNETCMD_JOINREQUEST.py
@@ -7,7 +7,7 @@ class FSNETCMD_JOINREQUEST: #8
"""
def __init__(self, buffer:bytes, should_decode:bool=True):
self.buffer = buffer
- self.iff = None
+ self.iff = 0 # The IFF sent is 1 subtracted from the selected value by the player
self.aircraft = None
self.start_pos = None #It's the STP name
self.fuel = None
@@ -26,4 +26,4 @@ class FSNETCMD_JOINREQUEST: #8
start_pos.encode(), 1, fuel, smoke)
if with_size:
return pack("I",len(buffer))+buffer
- return buffer \ No newline at end of file
+ return buffer
diff --git a/lib/Player.py b/lib/Player.py
index 7f54914..8e2250a 100644
--- a/lib/Player.py
+++ b/lib/Player.py
@@ -12,6 +12,7 @@ class Player:
self.ip = ""
self.streamWriterObject = streamWriterObject
self.is_a_bot = True # We check if they are still present after LOGIN packet, then they're not a bot
+ self.iff = 1
def set_aircraft(self, aircraft:Aircraft):
self.aircraft = aircraft
diff --git a/lib/plugin_manager.py b/lib/plugin_manager.py
index 07e28e6..52fb1f7 100644
--- a/lib/plugin_manager.py
+++ b/lib/plugin_manager.py
@@ -58,16 +58,16 @@ class PluginManager:
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
+ keep_original = True
if hook_name in self.hooks:
for callback in self.hooks[hook_name]:
keep = callback(data, *args, **kwargs)
if type(keep) == bool:
if keep == False:
- keep_orignal = False
+ keep_original = False
else:
warning(f"Return value of {callback} is not a boolean")
- return keep_orignal
+ return keep_original
def trigger_command(self, command:str, player, full_message:str, message_to_client:list, message_to_server:list):
"""Triggers the command"""
diff --git a/plugins/aircraft_replacer.py b/plugins/aircraft_replacer.py
new file mode 100644
index 0000000..f2db5a9
--- /dev/null
+++ b/plugins/aircraft_replacer.py
@@ -0,0 +1,70 @@
+"""This plugin will replace the aircraft of the player
+with the aircraft specified in the plugin. It is a template
+for creating plugins that modify the aircraft of the player."""
+from lib.PacketManager.packets import FSNETCMD_ADDOBJECT, FSNETCMD_JOINREQUEST, FSNETCMD_READBACK, FSNETCMD_AIRPLANESTATE
+from config import G_LIM
+import time
+
+# CONFIG
+ENABLED = False # Enable or disable the plugin
+
+class Plugin:
+ def __init__(self):
+ self.plugin_manager = None
+ self.first_add_object = False
+
+ def register(self, plugin_manager):
+ self.plugin_manager = plugin_manager
+ # self.plugin_manager.register_hook('on_flight_data', self.on_flight_data)
+ self.plugin_manager.register_hook('on_add_object_server', self.on_add_object_server)
+ # self.plugin_manager.register_hook('on_flight_data_server', self.on_flight_data_server)
+ self.plugin_manager.register_hook('on_join_request', self.on_join_request)
+
+
+ def on_add_object_server(self, data, player, messages_to_client, *args):
+ packet = FSNETCMD_ADDOBJECT(data)
+ #Swap the aircraft and position
+ if packet.pilot == player.username:
+ packet.identifier = "EUROFIGHTER_TYPHOON"
+ messages_to_client.append(packet.to_packet())
+ self.first_add_object = True
+ return False
+ return True
+
+ # def on_flight_data(self, data, player, messages_to_client, *args):
+ # packet = FSNETCMD_AIRPLANESTATE(data)
+ # if packet.player_id == player.aircraft.id:
+
+ # return True
+
+ # def on_flight_data_server(self, data, player, messages_to_client, *args):
+ # packet = FSNETCMD_AIRPLANESTATE(data)
+ # if packet.player_id == player.aircraft.id and self.first_add_object:
+ # packet.position = [0,2000.0,0]
+ # packet.velocity = [25,0,25]
+ # messages_to_client.append(packet.to_packet())
+
+ # self.first_add_object = False
+ # return False
+ # return True
+
+ def on_join_request(self, data, player, messages_to_client, message_to_server, *args):
+ packet = FSNETCMD_JOINREQUEST(data)
+ #Replace the aircraft:
+ packet.aircraft = "EUROFIGHTER_TYPHOON"
+ message_to_server.append(packet.to_packet())
+ return False
+
+ # 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 > INTERVAL:
+ # 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
diff --git a/plugins/radar.py b/plugins/radar.py
new file mode 100644
index 0000000..7636dc4
--- /dev/null
+++ b/plugins/radar.py
@@ -0,0 +1,88 @@
+"""
+This plugin allows you have to Radar features similar to that of
+original WW3 server.
+"""
+import math
+from lib.PacketManager.packets import FSNETCMD_AIRPLANESTATE
+from lib.YSchat import send
+import struct
+import traceback
+
+ENABLED = True
+RADIUS = 5000 # in meters, within this range planes with different IFF can see each other.
+
+flying_players = {}
+
+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_server', self.on_flight_data_server)
+ self.plugin_manager.register_hook('on_flight_data', self.on_flight_data)
+ self.plugin_manager.register_hook('on_unjoin', self.on_unjoin)
+ #self.plugin_manager.register_hook('on_join_request', self.on_join)
+
+ """
+ def on_join(self, data, player, message_to_client, message_to_server):
+ flying_players[player.aircraft.id] = [player, []]
+ return True
+ """
+
+ def on_flight_data(self, data, player, message_to_client, message_to_server):
+ try:
+ flying_players[player.aircraft.id][1] = FSNETCMD_AIRPLANESTATE(data).position
+ except KeyError:
+ return True
+ return True
+
+ def on_flight_data_server(self, data, player, message_to_client, message_to_server):
+ # TODO : This is very primitive, we need to implement a better way to keep track of players
+ try:
+ if player.aircraft.id not in flying_players and player.aircraft.id != -1:
+ flying_players[player.aircraft.id] = [player, []]
+ return True
+ elif player.aircraft.id == -1:
+ return True
+
+ decode = FSNETCMD_AIRPLANESTATE(data)
+
+ if len(flying_players[player.aircraft.id][1]) == 0:
+ return True
+ elif decode.player_id == player.aircraft.id:
+ return True
+ elif flying_players[decode.player_id][0].iff == player.iff:
+ return True
+ elif self.in_range(flying_players[player.aircraft.id][1], decode.position):
+ return True
+ else:
+ try:
+ # This is a really arbitary position thats hard to reach, better would
+ # be to randomise it for every time the server starts.
+ position_data = struct.pack("3f", 10e7, 10e7, 10e7)
+ if decode.packet_version == 4 or 5:
+ updated_data = data[:14] + position_data + data[26:]
+ else:
+ updated_data = data[:16] + position_data + data[28:]
+ except:
+ traceback.print_exc()
+
+ player.streamWriterObject.write(send(updated_data))
+ # await player.streamWriterObject.drain()
+ return False
+ except Exception as e:
+ if isinstance(e, KeyError):
+ return True
+ traceback.print_exc()
+
+ def on_unjoin(self, data, player, message_to_client, message_to_server):
+ if player.aircraft.id in flying_players:
+ flying_players.pop(player.aircraft.id)
+ return True
+
+ @staticmethod
+ def in_range(pos1, pos2):
+ # pos1 and pos2 are lists of [x,y,z]
+ dist = math.sqrt((pos1[0] - pos2[0])**2 + (pos1[1] - pos2[1])**2 + (pos1[2] - pos2[2])**2)
+ return dist <= RADIUS
diff --git a/plugins/refuel.py b/plugins/refuel.py
index 1b33031..ff96699 100644
--- a/plugins/refuel.py
+++ b/plugins/refuel.py
@@ -4,6 +4,7 @@ This plugin enables you to perform air to air refueling.
import math
from lib.PacketManager.packets import FSNETCMD_AIRCMD, FSNETCMD_AIRPLANESTATE
from lib import YSchat
+import asyncio
ENABLED = True
REFUEL_RADIUS = 500
@@ -23,6 +24,7 @@ class Plugin:
self.plugin_manager.register_command('refuel', self.refuel)
self.plugin_manager.register_command('refueler', self.refueler)
self.plugin_manager.register_hook('on_flight_data', self.on_flight_data)
+ self.plugin_manager.register_hook('on_unjoin', self.on_unjoin)
def refueler(self, full_message, player, message_to_client, message_to_server):
if player in refueling:
@@ -32,7 +34,7 @@ class Plugin:
a = YSchat.message("You are no more refueling!")
refuelers.pop(player)
else:
- a = YSchat.message(f"You are now refueling other players! To refuel the plane must be within {REFUEL_RADIUS}m")
+ a = YSchat.message(f"You are now refueling other players! To refuel the plane must be within {REFUEL_RADIUS}m and have same IFF")
refuelers[player] = [0, [], False]
message_to_client.append(a)
@@ -46,38 +48,19 @@ class Plugin:
a = YSchat.message("You are no more refueling!")
refueling.pop(player)
else:
- a = YSchat.message(f"You can now be refueled by other players! To refuel the plane must be within {REFUEL_RADIUS}m")
+ a = YSchat.message(f"You can now be refueled by other players! To refuel the plane must be within {REFUEL_RADIUS}m and have same IFF")
refueling[player] = [0, []]
message_to_client.append(a)
def on_flight_data(self, data, player, message_to_client, message_to_server):
- decode = FSNETCMD_AIRPLANESTATE(data)
- if player in refueling:
- for refueler in refuelers:
- refueling[player] = [decode.fuel, decode.position]
- if self.in_range(refuelers[refueler][1], decode.position):
- refuelers[refueler][2] = True
- max_lim = int((player.aircraft.initial_config['WEIGFUEL'])[:-2])
- if decode.fuel < max_lim:
- a = FSNETCMD_AIRCMD.set_command(player.aircraft.id, "INITFUEL", f"{decode.fuel+FUEL_RATE}kg", True)
- message_to_client.append(a)
- elif decode.fuel >= max_lim:
- a = YSchat.message("The plane is full!")
- refuelers[refueler][2] = False # No longer refueling
- message_to_client.append(a)
-
- if refuelers[refueler][0] < FUEL_RATE:
- a = YSchat.message("The refueler ran out of fuel!")
- message_to_client.append(a)
- else:
- refuelers[refueler][2] = False # No longer refueling
-
+ asyncio.create_task(self.refuel_logic(data, player))
+ return True
- elif player in refuelers:
- refuelers[player] = [decode.fuel, decode.position, refuelers[player][2]]
- if decode.fuel-FUEL_RATE > 0 and refuelers[player][2]:
- a = FSNETCMD_AIRCMD.set_command(player.aircraft.id, "INITFUEL", f"{decode.fuel-FUEL_RATE}kg", True)
- message_to_client.append(a)
+ def on_unjoin(self, data, player, message_to_client, message_to_server):
+ if player in refuelers:
+ refuelers.pop(player)
+ elif player in refueling:
+ refueling.pop(player)
return True
@staticmethod
@@ -91,3 +74,38 @@ class Plugin:
return True
else:
return False
+
+ async def refuel_logic(self, data, player):
+ decode = FSNETCMD_AIRPLANESTATE(data)
+ k = player.streamWriterObject
+ if player in refueling:
+ for refueler in refuelers:
+ if refueler.iff == player.iff:
+ refueling[player] = [decode.fuel, decode.position]
+ if self.in_range(refuelers[refueler][1], decode.position):
+ refuelers[refueler][2] = True
+ max_lim = int((player.aircraft.initial_config['WEIGFUEL'])[:-2])
+ if decode.fuel < max_lim:
+ #a = FSNETCMD_AIRCMD.set_command(player.aircraft.id, "INITFUEL", f"{decode.fuel+FUEL_RATE}kg", True)
+ #message_to_client.append(a)
+ k.write(FSNETCMD_AIRCMD.set_command(player.aircraft.id, "INITFUEL", f"{decode.fuel+FUEL_RATE}kg", True))
+ elif decode.fuel >= max_lim:
+ a = YSchat.message("The plane is full!")
+ refuelers[refueler][2] = False # No longer refueling
+ k.write(a)
+ #message_to_client.append(a)
+
+ if refuelers[refueler][0] < FUEL_RATE:
+ a = YSchat.message("The refueler ran out of fuel!")
+ k.write(a)
+ #message_to_client.append(a)
+ else:
+ refuelers[refueler][2] = False # No longer refueling
+
+
+ elif player in refuelers:
+ refuelers[player] = [decode.fuel, decode.position, refuelers[player][2]]
+ if decode.fuel-FUEL_RATE > 0 and refuelers[player][2]:
+ a = FSNETCMD_AIRCMD.set_command(player.aircraft.id, "INITFUEL", f"{decode.fuel-FUEL_RATE}kg", True)
+ #message_to_client.append(a)
+ k.write(a)
diff --git a/proxy.py b/proxy.py
index 9a60c0c..d043b32 100644
--- a/proxy.py
+++ b/proxy.py
@@ -130,6 +130,11 @@ async def handle_client(client_reader, client_writer):
writer.write(data)
continue
+ elif packet_type == "FSNETCMD_JOINREQUEST":
+ player.iff = FSNETCMD_JOINREQUEST(packet).iff + 1
+ if DISCORD_ENABLED:
+ asyncio.create_task(discord_send_message(CHANNEL_ID, f"{player.username} has taken off! 🛫"))
+
elif packet_type == "FSNETCMD_AIRPLANESTATE":
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)
@@ -144,6 +149,8 @@ async def handle_client(client_reader, client_writer):
player.aircraft.just_repaired = False
elif packet_type == "FSNETCMD_UNJOIN":
+ if DISCORD_ENABLED:
+ asyncio.create_task(discord_send_message(CHANNEL_ID, f"{player.username} has left the airplane! 🛬"))
player.aircraft.reset()
elif packet_type == "FSNETCMD_WEAPONCONFIG":
@@ -245,12 +252,16 @@ async def start_proxy():
info(f"Proxy server listening on port {PROXY_PORT}")
if DISCORD_ENABLED:
await asyncio.create_task(monitor_channel(CHANNEL_ID, CONNECTED_PLAYERS))
+ # await asyncio.create_task(set_bot_status_online())
async with server:
await server.serve_forever()
if __name__ == "__main__":
try:
+ if DISCORD_ENABLED:
+ asyncio.run(discord_send_message(CHANNEL_ID, "✅ Server has started."))
asyncio.run(start_proxy())
except KeyboardInterrupt:
+ asyncio.run(discord_send_message(CHANNEL_ID, "❌ Server has stopped."))
info("Goodbye!")