Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 21 additions & 0 deletions assets/chiv2-server-browser-api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -554,10 +554,27 @@ components:
current_map:
type: string
description: The current map being played on the server
map_rotation:
$ref: '#/components/schemas/MapRotation'
mods:
type: array
items:
$ref: '#/components/schemas/Mod'
MapRotation:
type: object
description: The map rotation for the server
required:
- maps
properties:
maps:
type: array
items:
type: string
description: An ordered list of maps in the rotation.
index:
type: integer
format: int32
description: The index of the current map
Mod:
type: object
required:
Expand Down Expand Up @@ -623,6 +640,8 @@ components:
current_map:
type: string
description: The current map being played on the server
map_rotation:
$ref: '#/components/schemas/MapRotation'
player_count:
type: integer
format: int32
Expand Down Expand Up @@ -663,6 +682,8 @@ components:
current_map:
type: string
description: The current map being played on the server
map_rotation:
$ref: '#/components/schemas/MapRotation'
player_count:
type: integer
format: int32
Expand Down
28 changes: 27 additions & 1 deletion src/server_browser_backend/models/base_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,19 @@ def from_json(json: dict):
)


@dataclass(frozen=True)
class MapRotation:
maps: List[str]
index: Optional[int] = None

@staticmethod
def from_json(json: dict):
return MapRotation(
get_or(json, "maps", list),
get_or_optional(json, "index", int),
)


@dataclass(frozen=True)
class ServerRegistrationRequest:
ports: Chivalry2Ports
Expand All @@ -34,6 +47,7 @@ class ServerRegistrationRequest:
max_players: int
mods: List[Mod]
local_ip_address: Optional[str]
map_rotation: Optional[MapRotation]

@staticmethod
def from_json(json: dict):
Expand All @@ -49,6 +63,7 @@ def from_json(json: dict):
get_or(json, "max_players", int),
list(map(Mod.from_json, mod_objs)),
get_or_optional(json, "local_ip_address", str),
MapRotation.from_json(json["map_rotation"]) if "map_rotation" in json else None,
)


Expand All @@ -67,6 +82,7 @@ class Server:
max_players: int
is_verified: bool
mods: List[Mod]
map_rotation: Optional[MapRotation]

@staticmethod
def create_after_registration(
Expand All @@ -86,6 +102,7 @@ def create_after_registration(
registration.max_players,
False,
registration.mods,
registration.map_rotation,
)

@staticmethod
Expand All @@ -106,6 +123,7 @@ def from_json(json: dict):
get_or(json, "max_players", int),
False,
list(map(Mod.from_json, mod_objs)),
MapRotation.from_json(json["map_rotation"]) if "map_rotation" in json else None,
)

def with_heartbeat(self, heartbeat_time: float):
Expand All @@ -123,6 +141,7 @@ def with_heartbeat(self, heartbeat_time: float):
self.max_players,
self.is_verified,
self.mods,
self.map_rotation,
)

def with_update(self, update_request: UpdateRegisteredServer) -> Server:
Expand All @@ -140,6 +159,7 @@ def with_update(self, update_request: UpdateRegisteredServer) -> Server:
update_request.max_players,
self.is_verified,
update_request.mods if update_request.mods is not None else self.mods,
update_request.map_rotation if update_request.map_rotation is not None else self.map_rotation,
)

def unverified(self) -> Server:
Expand All @@ -157,6 +177,7 @@ def unverified(self) -> Server:
self.max_players,
False,
self.mods,
self.map_rotation,
)

def verified(self) -> Server:
Expand All @@ -174,6 +195,7 @@ def verified(self) -> Server:
self.max_players,
True,
self.mods,
self.map_rotation,
)


Expand All @@ -190,6 +212,7 @@ class ServerResponse:
max_players: int
is_verified: bool
mods: List[Mod]
map_rotation: Optional[MapRotation] = None

@staticmethod
def from_server(server: Server) -> ServerResponse:
Expand All @@ -205,6 +228,7 @@ def from_server(server: Server) -> ServerResponse:
server.max_players,
server.is_verified,
server.mods,
server.map_rotation,
)


Expand All @@ -215,6 +239,7 @@ class UpdateRegisteredServer:
player_count: int
max_players: int
mods: List[Mod] | None
map_rotation: Optional[MapRotation] = None

@staticmethod
def from_json(json: dict):
Expand All @@ -226,7 +251,8 @@ def from_json(json: dict):
get_or(json, "current_map", str),
get_or(json, "player_count", int),
get_or(json, "max_players", int),
mod_objs
mod_objs,
MapRotation.from_json(json["map_rotation"]) if "map_rotation" in json else None,
)


Expand Down
99 changes: 99 additions & 0 deletions src/test/server_browser_backend/routes/test_api_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,105 @@ def test_delete(client: FlaskClient):
assert response_json["status"] == "deleted"
assert not shared.server_list.exists(id)

test_map_rotation = {
"maps": ["Map 1", "Map 2", "Map 3"],
"index": 0
}

test_server_with_rotation_json = {
"name": "Test Server",
"description": "Test Description",
"ports": {"game": 7777, "ping": 7778, "a2s": 27015},
"player_count": 0,
"max_players": 64,
"current_map": "Map 1",
"password_protected": False,
"mods": [],
"map_rotation": test_map_rotation
}

def test_register_with_map_rotation(client: FlaskClient):
prepare_test_state()

response = client.post("/api/v1/servers", json=test_server_with_rotation_json)
assert response.status_code == 201

response_json = response.get_json()
assert "server" in response_json
assert "map_rotation" in response_json["server"]
assert response_json["server"]["map_rotation"]["maps"] == test_map_rotation["maps"]
assert response_json["server"]["map_rotation"]["index"] == test_map_rotation["index"]

def test_update_map_rotation(client: FlaskClient):
prepare_test_state()

# Register
reg_response = client.post("/api/v1/servers", json=test_server_with_rotation_json)
reg_json = reg_response.get_json()
server_id = reg_json["server"]["unique_id"]
key = reg_json["key"]

# Update map rotation
new_rotation = {
"maps": ["New Map 1", "New Map 2"],
"index": 1
}

update_json = {
"current_map": "New Map 2",
"player_count": 10,
"max_players": 64,
"map_rotation": new_rotation
}

update_response = client.put(
f"/api/v1/servers/{server_id}",
headers={shared.KEY_HEADER: key},
json=update_json
)

assert update_response.status_code == 200
update_response_json = update_response.get_json()
assert update_response_json["server"]["map_rotation"]["maps"] == new_rotation["maps"]
assert update_response_json["server"]["map_rotation"]["index"] == new_rotation["index"]

# Verify in server list
list_response = client.get("/api/v1/servers")
assert list_response.status_code == 200
list_json = list_response.get_json()

server_in_list = next(s for s in list_json["servers"] if s["unique_id"] == server_id)
assert server_in_list["map_rotation"]["maps"] == new_rotation["maps"]
assert server_in_list["map_rotation"]["index"] == new_rotation["index"]

def test_update_keep_map_rotation(client: FlaskClient):
prepare_test_state()

# Register
reg_response = client.post("/api/v1/servers", json=test_server_with_rotation_json)
reg_json = reg_response.get_json()
server_id = reg_json["server"]["unique_id"]
key = reg_json["key"]

# Update without map rotation
update_json = {
"current_map": "Map 1",
"player_count": 5,
"max_players": 64
}

update_response = client.put(
f"/api/v1/servers/{server_id}",
headers={shared.KEY_HEADER: key},
json=update_json
)

assert update_response.status_code == 200
update_response_json = update_response.get_json()
# Should still have the original map rotation
assert update_response_json["server"]["map_rotation"]["maps"] == test_map_rotation["maps"]
assert update_response_json["server"]["map_rotation"]["index"] == test_map_rotation["index"]


def test_delete_nonexistant_server(client: FlaskClient):
prepare_test_state()
Expand Down