Skip to content
Merged
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
11 changes: 10 additions & 1 deletion docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,16 @@ The Action consists of two parts
#### List of ActionTypes
- **JoinGame**, params={`agent_info`:AgentInfo(`<name>`, `<role>`)}: Used to register agent in a game with a given `<role>`.
- **QuitGame**, params={}: Used for termination of agent's interaction.
- **ResetGame**, params={`request_trajectory`:`bool` (default=`False`), `randomize_topology`=`bool` (default=`True`)}: Used for requesting reset of the game to its initial position. If `request_trajectory = True`, the coordinator will send back the complete trajectory of the previous run in the next message. If `randomize_topology`=`True`, the agent request topology to be changed in the next episode. NOTE: the topology is changed only if (i) the `use_dynamic_addresses` is set to `True` in the task configuration AND all active agents ask for the change.
- **ResetGame**, params={`request_trajectory`:`bool` (default=`False`), `seed`:`int` (default=`None`), `randomize_topology`:`bool` (default=`False`)}: Used for requesting reset of the game to its initial position. If `request_trajectory = True`, the coordinator will send back the complete trajectory of the previous run in the next message. The `seed` parameter allows setting a specific random seed for reproducibility. If `randomize_topology=True`, the agent requests the topology to be randomized in the next episode.

!!! note "Topology Change & Seed Dependency"
A topology change can only be requested if the agent also submits a specific `seed`. If `seed` is omitted or `None`, the `randomize_topology` flag is ignored.

!!! warning "Consensus Requirement"
In multi-agent games, all agents must agree on the `seed` and `randomize_topology` values. If agents request conflicting seeds or topology change flags, the game will shut down.

!!! note
The topology is changed only if (i) the `use_dynamic_addresses` is set to `True` in the task configuration AND (ii) all active agents ask for the change.
---
- **ScanNetwork**, params{`source_host`:`<IP>`, `target_network`:`<Network>`}: Scans the given `<Network>` from a specified source host. Discovers ALL hosts in a network that are accessible from `<IP>`. If successful, returns set of discovered `<IP>` objects.
- **FindServices**, params={`source_host`:`<IP>`, `target_host`:`<IP>`}: Used to discover ALL services running in the `target_host` if the host is accessible from `source_host`. If successful, returns a set of all discovered `<Service>` objects.
Expand Down
48 changes: 16 additions & 32 deletions netsecgame/game/coordinator.py
Original file line number Diff line number Diff line change
Expand Up @@ -487,7 +487,7 @@ async def _process_reset_game_action(self, agent_addr: tuple, reset_action: Acti
# - ONLY consider agents that submitted seed
# register if the agent wants to randomize the topology
if self._reset_seed_requests[agent_addr] is not None:
self._randomize_topology_requests[agent_addr] = reset_action.parameters.get("randomize_topology", True)
self._randomize_topology_requests[agent_addr] = reset_action.parameters.get("randomize_topology", False)
if all(self._reset_requests.values()):
# all agents want reset - reset the world
self.logger.debug(f"All agents requested reset, setting the event")
Expand Down Expand Up @@ -728,40 +728,24 @@ async def _reset_game(self) -> None:
self.logger.debug("\tExiting reset_game task.")
break
if len(self.agents) > 0:
# verify that all agents agreed on the seed (or sent None)
valid_seeding = False
valid_topology_change = False
non_none_seeds = [seed for seed in self._reset_seed_requests.values() if seed is not None]
if len(non_none_seeds) == 0: # no agent wants to change the seed
seed = None
valid_seeding = True
elif len(set(non_none_seeds)) == 1: # all agents agree on the seed
seed = non_none_seeds[0]
valid_seeding = True
else: # agents disagree on the seed
seed = None
# verify that all agents agreed on the topology change (or sent None)
valid_seed_agents = [agent for agent in self.agents if self._reset_seed_requests[agent] is not None]
valid_topology_requests = [self._randomize_topology_requests[agent] for agent in valid_seed_agents]
if len(set(valid_topology_requests)) == 1: # all valid agents agree on the topology change
valid_topology_change = True
topology_change = valid_topology_requests[0]
else: # agents disagree on the topology change
valid_topology_change = False
topology_change = None

if valid_seeding and valid_topology_change:
await self._handle_valid_reset(seed, topology_change)
self._reset_event.clear()
# notify all waiting agents
async with self._reset_done_condition:
self._reset_done_condition.notify_all()
elif not valid_seeding:
# 1. Validate Seeding (all non-None seeds must match)
unique_seeds = {seed for seed in self._reset_seed_requests.values() if seed is not None}
valid_seeding = len(unique_seeds) <= 1
seed = unique_seeds.pop() if len(unique_seeds) == 1 else None

# 2. Validate Topology Change (only for agents who provided a seed)
unique_topology_requests = {self._randomize_topology_requests[a] for a in self.agents if self._reset_seed_requests.get(a) is not None}
valid_topology_change = len(unique_topology_requests) <= 1
topology_change = unique_topology_requests.pop() if len(unique_topology_requests) == 1 else False

# 3. Handle Reset Actions
if not valid_seeding:
await self._handle_invalid_reset("Agents disagree on the seed. Undefined state. Stopping the game")
self._reset_event.clear()
elif not valid_topology_change:
await self._handle_invalid_reset("Agents disagree on the topology change. Undefined state. Stopping the game")
self._reset_event.clear()
else:
await self._handle_valid_reset(seed, topology_change)

self._reset_event.clear()
# notify all waiting agents
async with self._reset_done_condition:
Expand Down
Loading