Skip to content

Commit 2a1f6f8

Browse files
committed
Adding Support for Serious Sam Classic (First and Second Encounter)
1 parent 553b6d5 commit 2a1f6f8

10 files changed

Lines changed: 287 additions & 0 deletions

File tree

docs/tests/protocols/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ Protocols Tests
88
test_halo1/index
99
test_flatout2/index
1010
test_battlefield2/index
11+
test_ssc/index
1112
test_source/index
1213
test_won/index
1314
test_fivem/index
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
.. _test_ssc:
2+
3+
test_ssc
4+
========
5+
6+
.. toctree::
7+
test_get_basic
8+
test_get_players
9+
test_get_status
10+
test_get_info
11+
test_get_rules
12+
test_get_teams
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
test_get_basic
2+
==============
3+
4+
Here are the results for the test method.
5+
6+
.. code-block:: json
7+
8+
{
9+
"gamename": "serioussam",
10+
"gamever": "1.05",
11+
"location": "DEU",
12+
"hostname": "Unnamed session",
13+
"hostport": "25600",
14+
"mapname": "Hatshepsut",
15+
"gametype": "Cooperative",
16+
"activemod": "",
17+
"numplayers": "1",
18+
"maxplayers": "8",
19+
"gamemode": "openplaying",
20+
"difficulty": "Normal",
21+
"friendlyfire": "1",
22+
"weaponsstay": "0",
23+
"ammostays": "0",
24+
"healthandarmorstays": "0",
25+
"allowhealth": "0",
26+
"allowarmor": "0",
27+
"infiniteammo": "1",
28+
"respawninplace": "1",
29+
"credits": "infinite",
30+
"password": "0",
31+
"vipplayers": "0",
32+
"player_0": "Serious Sam",
33+
"frags_0": "0",
34+
"ping_0": "3"
35+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
test_get_info
2+
=============
3+
4+
Here are the results for the test method.
5+
6+
.. code-block:: json
7+
8+
{
9+
"hostname": "Unnamed session",
10+
"hostport": "25600",
11+
"mapname": "Hatshepsut",
12+
"gametype": "Cooperative",
13+
"activemod": "",
14+
"numplayers": "1",
15+
"maxplayers": "8",
16+
"gamemode": "openplaying"
17+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
test_get_players
2+
================
3+
4+
Here are the results for the test method.
5+
6+
.. code-block:: json
7+
8+
[
9+
{
10+
"player": "Serious Sam",
11+
"frags": "0",
12+
"ping": "2"
13+
}
14+
]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
test_get_rules
2+
==============
3+
4+
Here are the results for the test method.
5+
6+
.. code-block:: json
7+
8+
{
9+
"difficulty": "Normal",
10+
"friendlyfire": "1",
11+
"weaponsstay": "0",
12+
"ammostays": "0",
13+
"healthandarmorstays": "0",
14+
"allowhealth": "0",
15+
"allowarmor": "0",
16+
"infiniteammo": "1",
17+
"respawninplace": "1",
18+
"credits": "infinite",
19+
"password": "0",
20+
"vipplayers": "0"
21+
}
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
test_get_status
2+
===============
3+
4+
Here are the results for the test method.
5+
6+
.. code-block:: json
7+
8+
{
9+
"info": {
10+
"gamename": "serioussam",
11+
"gamever": "1.05",
12+
"location": "DEU",
13+
"hostname": "Unnamed session",
14+
"hostport": "25600",
15+
"mapname": "Hatshepsut",
16+
"gametype": "Cooperative",
17+
"activemod": "",
18+
"numplayers": "1",
19+
"maxplayers": "8",
20+
"gamemode": "openplaying",
21+
"difficulty": "Normal",
22+
"friendlyfire": "1",
23+
"weaponsstay": "0",
24+
"ammostays": "0",
25+
"healthandarmorstays": "0",
26+
"allowhealth": "0",
27+
"allowarmor": "0",
28+
"infiniteammo": "1",
29+
"respawninplace": "1",
30+
"credits": "infinite",
31+
"password": "0",
32+
"vipplayers": "0"
33+
},
34+
"players": [
35+
{
36+
"player": "Serious Sam",
37+
"frags": "0",
38+
"ping": "2"
39+
}
40+
],
41+
"teams": []
42+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
test_get_teams
2+
==============
3+
4+
Here are the results for the test method.
5+
6+
.. code-block:: json
7+
8+
[]

opengsq/protocols/ssc.py

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
from __future__ import annotations
2+
3+
from opengsq.protocols.gamespy1 import GameSpy1
4+
5+
6+
class SSC(GameSpy1):
7+
"""Serious Sam Classic: The First Encounter Protocol"""
8+
9+
full_name = "Serious Sam Classic: The First Encounter"
10+
11+
def __init__(self, host: str, port: int = 25601, timeout: float = 5.0):
12+
"""
13+
Initialize the Serious Sam Classic protocol.
14+
15+
:param host: The hostname or IP address of the server.
16+
:param port: The port number of the server (default: 25601).
17+
:param timeout: The timeout for the connection in seconds (default: 5.0).
18+
"""
19+
super().__init__(host, port, timeout)
20+
21+
async def get_basic(self) -> dict[str, str]:
22+
"""
23+
Asynchronously retrieves comprehensive information about the game server.
24+
25+
For Serious Sam Classic, we return the full status information as the basic query.
26+
27+
:return: A dictionary containing comprehensive server information.
28+
"""
29+
# Get full status and flatten all information into one dict
30+
status = await self.get_status()
31+
32+
# Combine info with player information in a flattened format
33+
result = dict(status.info)
34+
35+
# Add player information as indexed fields
36+
for i, player in enumerate(status.players):
37+
for key, value in player.items():
38+
result[f"{key}_{i}"] = value
39+
40+
return result
41+
42+
async def get_status(self, xserverquery: bool = False):
43+
"""
44+
Asynchronously retrieves the status of the game server.
45+
46+
Serious Sam Classic doesn't support XServerQuery, so we always use the legacy format.
47+
48+
:param xserverquery: Ignored for Serious Sam Classic (always uses legacy format).
49+
:return: A Status object containing the status of the game server.
50+
"""
51+
# Always use legacy format for Serious Sam Classic (no xserverquery)
52+
return await super().get_status(xserverquery=False)
53+
54+
async def get_info(self, xserverquery: bool = False) -> dict[str, str]:
55+
"""
56+
Asynchronously retrieves the information of the current game running on the server.
57+
58+
:param xserverquery: Ignored for Serious Sam Classic (always uses legacy format).
59+
:return: A dictionary containing the information of the current game.
60+
"""
61+
return await super().get_info(xserverquery=False)
62+
63+
async def get_rules(self, xserverquery: bool = False) -> dict[str, str]:
64+
"""
65+
Asynchronously retrieves the rules of the current game running on the server.
66+
67+
:param xserverquery: Ignored for Serious Sam Classic (always uses legacy format).
68+
:return: A dictionary containing the rules of the current game.
69+
"""
70+
return await super().get_rules(xserverquery=False)
71+
72+
async def get_players(self, xserverquery: bool = False) -> list[dict[str, str]]:
73+
"""
74+
Asynchronously retrieves the information of each player on the server.
75+
76+
:param xserverquery: Ignored for Serious Sam Classic (always uses legacy format).
77+
:return: A list containing the information of each player.
78+
"""
79+
return await super().get_players(xserverquery=False)
80+
81+
82+
if __name__ == "__main__":
83+
import asyncio
84+
85+
async def main_async():
86+
ssc = SSC(host="172.29.100.29", port=25601, timeout=5.0)
87+
status = await ssc.get_status()
88+
print(status)
89+
90+
asyncio.run(main_async())
91+

tests/protocols/test_ssc.py

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import pytest
2+
from opengsq.protocols.ssc import SSC
3+
4+
from ..result_handler import ResultHandler
5+
6+
handler = ResultHandler(__file__)
7+
# handler.enable_save = True
8+
handler.delay_per_test = 1
9+
10+
test = SSC(host="172.29.100.29", port=25601)
11+
12+
13+
@pytest.mark.asyncio
14+
async def test_get_basic():
15+
result = await test.get_basic()
16+
await handler.save_result("test_get_basic", result)
17+
18+
19+
@pytest.mark.asyncio
20+
async def test_get_info():
21+
result = await test.get_info()
22+
await handler.save_result("test_get_info", result)
23+
24+
25+
@pytest.mark.asyncio
26+
async def test_get_rules():
27+
result = await test.get_rules()
28+
await handler.save_result("test_get_rules", result)
29+
30+
31+
@pytest.mark.asyncio
32+
async def test_get_players():
33+
result = await test.get_players()
34+
await handler.save_result("test_get_players", result)
35+
36+
37+
@pytest.mark.asyncio
38+
async def test_get_status():
39+
result = await test.get_status()
40+
await handler.save_result("test_get_status", result)
41+
42+
43+
@pytest.mark.asyncio
44+
async def test_get_teams():
45+
result = await test.get_teams()
46+
await handler.save_result("test_get_teams", result)

0 commit comments

Comments
 (0)