Skip to content

Commit dbf7c1b

Browse files
Merge pull request #34 from Hornochs/add-toxikk
Add Toxikk LAN Support
2 parents 5522524 + 178cd02 commit dbf7c1b

File tree

8 files changed

+377
-0
lines changed

8 files changed

+377
-0
lines changed

docs/tests/protocols/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ Protocols Tests
77
test_gamespy4/index
88
test_teamspeak3/index
99
test_won/index
10+
test_toxikk/index
1011
test_gamespy1/index
1112
test_minecraft/index
1213
test_raknet/index
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
.. _test_toxikk:
2+
3+
test_toxikk
4+
===========
5+
6+
.. toctree::
7+
test_toxikk_status
Lines changed: 242 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,242 @@
1+
test_toxikk_status
2+
==================
3+
4+
Here are the results for the test method.
5+
6+
.. code-block:: json
7+
8+
{
9+
"name": "TOXIKK Server",
10+
"map": "BL-Foundation",
11+
"game_type": "cruzade.CRZBloodLust",
12+
"num_players": 1,
13+
"max_players": 8,
14+
"password_protected": false,
15+
"stats_enabled": true,
16+
"lan_mode": true,
17+
"players": [],
18+
"raw": {
19+
"hostaddress": "10.13.37.149",
20+
"hostport": 7777,
21+
"num_players": 1,
22+
"max_players": 8,
23+
"lan_mode": true,
24+
"uses_stats": true,
25+
"owner_id": "0110000101734c32",
26+
"owner_name": "TOXIKK Server",
27+
"localized_settings": [
28+
{
29+
"id": 32779,
30+
"value_index": 8,
31+
"advertisement_type": 1
32+
},
33+
{
34+
"id": 0,
35+
"value_index": 2,
36+
"advertisement_type": 1
37+
},
38+
{
39+
"id": 1,
40+
"value_index": 0,
41+
"advertisement_type": 1
42+
},
43+
{
44+
"id": 6,
45+
"value_index": 1,
46+
"advertisement_type": 1
47+
},
48+
{
49+
"id": 7,
50+
"value_index": 0,
51+
"advertisement_type": 1
52+
},
53+
{
54+
"id": 8,
55+
"value_index": 0,
56+
"advertisement_type": 1
57+
},
58+
{
59+
"id": 9,
60+
"value_index": 0,
61+
"advertisement_type": 1
62+
},
63+
{
64+
"id": 10,
65+
"value_index": 0,
66+
"advertisement_type": 1
67+
},
68+
{
69+
"id": 11,
70+
"value_index": 0,
71+
"advertisement_type": 1
72+
},
73+
{
74+
"id": 12,
75+
"value_index": 0,
76+
"advertisement_type": 1
77+
},
78+
{
79+
"id": 13,
80+
"value_index": 0,
81+
"advertisement_type": 1
82+
},
83+
{
84+
"id": 14,
85+
"value_index": 0,
86+
"advertisement_type": 1
87+
},
88+
{
89+
"id": 15,
90+
"value_index": 1,
91+
"advertisement_type": 1
92+
},
93+
{
94+
"id": 16,
95+
"value_index": 0,
96+
"advertisement_type": 1
97+
},
98+
{
99+
"id": 17,
100+
"value_index": 0,
101+
"advertisement_type": 1
102+
},
103+
{
104+
"id": 18,
105+
"value_index": 0,
106+
"advertisement_type": 1
107+
},
108+
{
109+
"id": 19,
110+
"value_index": 0,
111+
"advertisement_type": 1
112+
}
113+
],
114+
"settings_properties": [
115+
{
116+
"id": 1073741825,
117+
"data": "BL-Foundation",
118+
"advertisement_type": 2
119+
},
120+
{
121+
"id": 1073741826,
122+
"data": "cruzade.CRZBloodLust",
123+
"advertisement_type": 2
124+
},
125+
{
126+
"id": 268435704,
127+
"data": 20,
128+
"advertisement_type": 1
129+
},
130+
{
131+
"id": 268435705,
132+
"data": 10,
133+
"advertisement_type": 1
134+
},
135+
{
136+
"id": 268435703,
137+
"data": 7,
138+
"advertisement_type": 1
139+
},
140+
{
141+
"id": 1073741827,
142+
"data": "MY SERVER",
143+
"advertisement_type": 2
144+
},
145+
{
146+
"id": 268435717,
147+
"data": 0,
148+
"advertisement_type": 1
149+
},
150+
{
151+
"id": 1073741828,
152+
"data": "INSTAGIB\u001cNO STEALTH\u001cSpawn Protection Time",
153+
"advertisement_type": 2
154+
},
155+
{
156+
"id": 268435706,
157+
"data": 8,
158+
"advertisement_type": 1
159+
},
160+
{
161+
"id": 268435707,
162+
"data": 0,
163+
"advertisement_type": 0
164+
},
165+
{
166+
"id": 268435708,
167+
"data": 1,
168+
"advertisement_type": 1
169+
},
170+
{
171+
"id": 268435709,
172+
"data": 12,
173+
"advertisement_type": 1
174+
},
175+
{
176+
"id": 1073741829,
177+
"data": "76561197984599090",
178+
"advertisement_type": 2
179+
},
180+
{
181+
"id": 1073741830,
182+
"data": "",
183+
"advertisement_type": 2
184+
},
185+
{
186+
"id": 1073741831,
187+
"data": "",
188+
"advertisement_type": 2
189+
},
190+
{
191+
"id": 1073741832,
192+
"data": "GAMIE ?",
193+
"advertisement_type": 2
194+
},
195+
{
196+
"id": 1073741833,
197+
"data": "",
198+
"advertisement_type": 2
199+
},
200+
{
201+
"id": 1073741834,
202+
"data": "",
203+
"advertisement_type": 2
204+
},
205+
{
206+
"id": 1073741837,
207+
"data": "1.0000",
208+
"advertisement_type": 2
209+
},
210+
{
211+
"id": 1073741838,
212+
"data": "1",
213+
"advertisement_type": 2
214+
},
215+
{
216+
"id": 1073741839,
217+
"data": "1.2",
218+
"advertisement_type": 2
219+
},
220+
{
221+
"id": 1073741840,
222+
"data": "DefaultGameList",
223+
"advertisement_type": 2
224+
}
225+
],
226+
"map": "BL-Foundation",
227+
"gametype": "BloodLust",
228+
"frag_limit": 20,
229+
"time_limit": 10,
230+
"numbots": 7,
231+
"mutators": [
232+
"Instagib",
233+
"No Stealth",
234+
"Spawn Protection Time"
235+
],
236+
"bot_skill": "Experienced",
237+
"pure_server": 1,
238+
"password": 0,
239+
"vs_bots": "None",
240+
"force_respawn": 0
241+
}
242+
}

opengsq/protocols/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from opengsq.protocols.scum import Scum
2222
from opengsq.protocols.source import Source
2323
from opengsq.protocols.teamspeak3 import TeamSpeak3
24+
from opengsq.protocols.toxikk import Toxikk
2425
from opengsq.protocols.udk import UDK
2526
from opengsq.protocols.unreal2 import Unreal2
2627
from opengsq.protocols.ut3 import UT3

opengsq/protocols/toxikk.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
from opengsq.protocols.udk import UDK
2+
from opengsq.responses.toxikk.status import Status
3+
4+
class Toxikk(UDK):
5+
GAMEMODE_NAMES = {
6+
"cruzade.CRZBloodLust": "BloodLust",
7+
"cruzade.CRZTeamGame": "Squad Assault",
8+
"cruzade.CRZSquadSurvival": "Squad Survival",
9+
"cruzade.CRZCellCapture": "Cell Capture",
10+
"cruzade.CRZAreaDomination": "Area Domination",
11+
"cruzade.CRZArchRivals": "Arch Rivals"
12+
}
13+
14+
BOT_SKILL_NAMES = {
15+
0: "Novice",
16+
1: "Average",
17+
2: "Experienced",
18+
3: "Skilled",
19+
4: "Adept",
20+
5: "Masterful",
21+
6: "Inhuman",
22+
7: "Godlike"
23+
}
24+
25+
VS_BOTS_NAMES = {
26+
0: "None",
27+
1: "1:1",
28+
2: "3:2",
29+
3: "2:1"
30+
}
31+
32+
full_name = "Toxikk Protocol"
33+
34+
def __init__(self, host: str, port: int = 14001, timeout: float = 5.0):
35+
super().__init__(host, port, timeout)
36+
self.game_id = 0x4D5707DB
37+
self.packet_version = 7
38+
39+
def _parse_response(self, buffer: bytes) -> dict:
40+
base_response = super()._parse_response(buffer)
41+
toxikk_properties = {}
42+
43+
for prop in base_response['raw']['settings_properties']:
44+
prop_id = prop['id']
45+
if prop_id == 1073741825: # Map
46+
base_response['map'] = prop['data']
47+
toxikk_properties['map'] = prop['data']
48+
elif prop_id == 1073741826: # Game Type
49+
base_response['game_type'] = prop['data']
50+
toxikk_properties['gametype'] = self.GAMEMODE_NAMES.get(prop['data'], prop['data'])
51+
elif prop_id == 268435704: # Frag Limit
52+
toxikk_properties['frag_limit'] = prop['data']
53+
elif prop_id == 268435705: # Time Limit
54+
toxikk_properties['time_limit'] = prop['data']
55+
elif prop_id == 268435703: # Number of Bots
56+
toxikk_properties['numbots'] = prop['data']
57+
elif prop_id == 1073741828: # Mutators
58+
toxikk_properties['mutators'] = self._parse_mutators(prop['data'])
59+
60+
for setting in base_response['raw']['localized_settings']:
61+
setting_id = setting['id']
62+
value_index = setting['value_index']
63+
64+
if setting_id == 0:
65+
toxikk_properties['bot_skill'] = self.BOT_SKILL_NAMES.get(value_index)
66+
elif setting_id == 6:
67+
toxikk_properties['pure_server'] = value_index
68+
elif setting_id == 7:
69+
base_response['password_protected'] = value_index == 1
70+
toxikk_properties['password'] = value_index
71+
elif setting_id == 8:
72+
toxikk_properties['vs_bots'] = self.VS_BOTS_NAMES.get(value_index)
73+
elif setting_id == 10:
74+
toxikk_properties['force_respawn'] = value_index
75+
76+
base_response['raw'].update(toxikk_properties)
77+
return base_response
78+
79+
def _parse_mutators(self, mutator_value: any) -> list:
80+
if not mutator_value or not isinstance(mutator_value, str):
81+
return []
82+
return [m.title() for m in mutator_value.split('\x1c') if m]
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
from .status import Status

opengsq/responses/toxikk/status.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
from __future__ import annotations
2+
from dataclasses import dataclass, field
3+
from typing import List
4+
from opengsq.responses.udk.status import Status as UDKStatus
5+
6+
@dataclass
7+
class Status(UDKStatus):
8+
"""Toxikk-specific status response"""
9+
mutators: List[str] = field(default_factory=list)

0 commit comments

Comments
 (0)