Skip to content

Commit 3034a73

Browse files
Merge pull request #35 from Hornochs/add-renegade-x
Adding Renegade LAN Support
2 parents dbf7c1b + 05d5b00 commit 3034a73

File tree

8 files changed

+191
-0
lines changed

8 files changed

+191
-0
lines changed

docs/tests/protocols/index.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Protocols Tests
1212
test_minecraft/index
1313
test_raknet/index
1414
test_eos/index
15+
test_renegadex/index
1516
test_kaillera/index
1617
test_ase/index
1718
test_quake1/index
@@ -32,3 +33,4 @@ Protocols Tests
3233
test_vcmp/index
3334
test_satisfactory/index
3435
test_gamespy3/index
36+
test_renegadex/index
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.. _test_renegadex:
2+
3+
test_renegadex
4+
==============
5+
6+
.. toctree::
7+
test_renegadex_status
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
test_renegadex_status
2+
=====================
3+
4+
Here are the results for the test method.
5+
6+
.. code-block:: json
7+
8+
{
9+
"name": "Ich bin ein ziemlich langer Server der nicht weis wie lang das geht",
10+
"current_map": "CNC-Field",
11+
"port": 7777,
12+
"players": 0,
13+
"game_version": "Open Beta 5.85.815",
14+
"variables": {
15+
"player_limit": 64,
16+
"vehicle_limit": 20,
17+
"mine_limit": 24,
18+
"time_limit": 50,
19+
"passworded": false,
20+
"steam_required": false,
21+
"team_mode": 6,
22+
"spawn_crates": true,
23+
"game_type": 1,
24+
"ranked": false
25+
},
26+
"raw": {
27+
"Current Map": "CNC-Field",
28+
"Players": 0,
29+
"Port": 7777,
30+
"Name": "Ich bin ein ziemlich langer Server der nicht weis wie lang das geht",
31+
"IP": "10.13.37.149",
32+
"Game Version": "Open Beta 5.85.815",
33+
"Variables": {
34+
"Player Limit": 64,
35+
"Vehicle Limit": 20,
36+
"Mine Limit": 24,
37+
"Time Limit": 50,
38+
"bPassworded": false,
39+
"bSteamRequired": false,
40+
"Team Mode": 6,
41+
"bSpawnCrates": true,
42+
"Game Type": 1,
43+
"bRanked": false
44+
}
45+
}
46+
}

opengsq/protocols/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
from opengsq.protocols.quake2 import Quake2
1717
from opengsq.protocols.quake3 import Quake3
1818
from opengsq.protocols.raknet import RakNet
19+
from opengsq.protocols.renegadex import RenegadeX
1920
from opengsq.protocols.samp import Samp
2021
from opengsq.protocols.satisfactory import Satisfactory
2122
from opengsq.protocols.scum import Scum

opengsq/protocols/renegadex.py

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import json
2+
import asyncio
3+
from opengsq.protocol_base import ProtocolBase
4+
from opengsq.responses.renegadex import Status
5+
6+
class RenegadeX(ProtocolBase):
7+
full_name = "Renegade X Protocol"
8+
BROADCAST_PORT = 45542
9+
10+
def __init__(self, host: str, port: int = 7777, timeout: float = 5.0):
11+
super().__init__(host, port, timeout)
12+
13+
async def get_status(self) -> Status:
14+
loop = asyncio.get_running_loop()
15+
queue = asyncio.Queue()
16+
17+
class BroadcastProtocol(asyncio.DatagramProtocol):
18+
def __init__(self, queue, host):
19+
self.queue = queue
20+
self.target_host = host
21+
22+
def datagram_received(self, data, addr):
23+
if addr[0] == self.target_host:
24+
self.queue.put_nowait(data)
25+
26+
transport, _ = await loop.create_datagram_endpoint(
27+
lambda: BroadcastProtocol(queue, self._host),
28+
local_addr=('0.0.0.0', self.BROADCAST_PORT)
29+
)
30+
31+
try:
32+
complete_data = bytearray()
33+
while True:
34+
try:
35+
data = await asyncio.wait_for(queue.get(), timeout=self._timeout)
36+
complete_data.extend(data)
37+
38+
try:
39+
json_str = complete_data.decode('utf-8')
40+
server_info = json.loads(json_str)
41+
return Status.from_dict(server_info)
42+
except (UnicodeDecodeError, json.JSONDecodeError):
43+
continue
44+
45+
except asyncio.TimeoutError:
46+
raise TimeoutError("No broadcast received from the specified server")
47+
48+
finally:
49+
transport.close()
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .status import Status

opengsq/responses/renegadex/status.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
from dataclasses import dataclass
2+
from typing import Any
3+
4+
@dataclass
5+
class Variables:
6+
player_limit: int
7+
vehicle_limit: int
8+
mine_limit: int
9+
time_limit: int
10+
passworded: bool
11+
steam_required: bool
12+
team_mode: int
13+
spawn_crates: bool
14+
game_type: int
15+
ranked: bool
16+
17+
@classmethod
18+
def from_dict(cls, data: dict[str, Any]) -> 'Variables':
19+
return cls(
20+
player_limit=data["Player Limit"],
21+
vehicle_limit=data["Vehicle Limit"],
22+
mine_limit=data["Mine Limit"],
23+
time_limit=data["Time Limit"],
24+
passworded=data["bPassworded"],
25+
steam_required=data["bSteamRequired"],
26+
team_mode=data["Team Mode"],
27+
spawn_crates=data["bSpawnCrates"],
28+
game_type=data["Game Type"],
29+
ranked=data["bRanked"]
30+
)
31+
32+
@dataclass
33+
class Status:
34+
name: str
35+
current_map: str
36+
port: int
37+
players: int
38+
game_version: str
39+
variables: Variables
40+
raw: dict[str, Any]
41+
42+
@classmethod
43+
def from_dict(cls, data: dict[str, Any]) -> 'Status':
44+
return cls(
45+
name=data["Name"],
46+
current_map=data["Current Map"],
47+
port=data["Port"],
48+
players=data["Players"],
49+
game_version=data["Game Version"],
50+
variables=Variables.from_dict(data["Variables"]),
51+
raw=data
52+
)

tests/protocols/test_renegadex.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import pytest
2+
from opengsq.protocols.renegadex import RenegadeX
3+
from ..result_handler import ResultHandler
4+
5+
handler = ResultHandler(__file__)
6+
handler.enable_save = True
7+
8+
@pytest.mark.asyncio
9+
async def test_renegadex_status():
10+
rx = RenegadeX(host="10.13.37.149")
11+
result = await rx.get_status()
12+
13+
print("\nRenegade X Server Details:")
14+
print(f"Server Name: {result.name}")
15+
print(f"Current Map: {result.current_map}")
16+
print(f"Game Version: {result.game_version}")
17+
print(f"Players: {result.players}/{result.variables.player_limit}")
18+
print(f"Port: {result.port}")
19+
20+
print("\nServer Settings:")
21+
print(f"Vehicle Limit: {result.variables.vehicle_limit}")
22+
print(f"Mine Limit: {result.variables.mine_limit}")
23+
print(f"Time Limit: {result.variables.time_limit}")
24+
print(f"Team Mode: {result.variables.team_mode}")
25+
print(f"Game Type: {result.variables.game_type}")
26+
27+
print("\nServer Flags:")
28+
print(f"Password Protected: {'Yes' if result.variables.passworded else 'No'}")
29+
print(f"Steam Required: {'Yes' if result.variables.steam_required else 'No'}")
30+
print(f"Spawn Crates: {'Yes' if result.variables.spawn_crates else 'No'}")
31+
print(f"Ranked: {'Yes' if result.variables.ranked else 'No'}")
32+
33+
await handler.save_result("test_renegadex_status", result)

0 commit comments

Comments
 (0)