# How v2rayN Handles Profile Groups and Implements Server Switching

> Learn how v2rayN manages profile groups and server switching using ProfileItem, ProtocolExtra, and a reactive event pipeline for seamless global configuration updates and reloads.

- Repository: [2dust/v2rayN](https://github.com/2dust/v2rayN)
- Tags: internals
- Published: 2026-02-27

---

**v2rayN stores every server definition as a `ProfileItem`, implements profile groups through the `ProtocolExtra` property containing `GroupType` metadata and comma-separated child IDs, and executes server switching via a reactive event pipeline that updates the global configuration index and triggers an application reload.**

v2rayN manages proxy configurations through a sophisticated grouping system that allows users to organize servers hierarchically. The application stores each server or group as a `ProfileItem` object, with specialized logic in `GroupProfileManager` handling the validation and expansion of nested group structures. Understanding how v2rayN handles profile groups and implements server switching reveals a clean separation between UI interactions and core configuration management in the 2dust/v2rayN repository.

## Understanding v2rayN Profile Groups

### The ProfileItem Foundation

Every server definition in v2rayN is stored as a **`ProfileItem`**. This base structure contains connection parameters, protocol settings, and identification fields that uniquely define a single proxy endpoint or container.

### Group Metadata in ProtocolExtra

A profile group is technically a `ProfileItem` whose **`ProtocolExtra`** property contains specific metadata:
- **GroupType**: Identifies this item as a container rather than a concrete server
- **ChildItems**: A comma-separated string of profile IDs representing the group's members

This design allows v2rayN to treat groups and individual servers polymorphically while maintaining the ability to distinguish containers from endpoints.

## GroupProfileManager: Core Group Logic

The **`GroupProfileManager`** class in [`v2rayN/ServiceLib/Manager/GroupProfileManager.cs`](https://github.com/2dust/v2rayN/blob/main/v2rayN/ServiceLib/Manager/GroupProfileManager.cs) centralizes all group-related operations, implementing three critical responsibilities:

### Cyclic Reference Detection

To prevent infinite recursion during group expansion, the `HasCycle` method (lines 5-15) implements a depth-first traversal with a visited set and current path stack. If the method encounters an ID already present in the traversal stack, it reports a cyclic reference error, safeguarding the application against malformed group definitions.

### Child Profile Resolution

The `GetChildProfileItemsByProtocolExtra` method (lines 72-90) flattens a group into concrete server items. This method:
1. Parses the comma-separated `ChildItems` string from `ProtocolExtra`
2. Retrieves full `ProfileItem` objects from `AppManager`
3. Preserves the ordering defined by the group
4. Returns a flat `List<ProfileItem>` ready for core configuration

### Descendant Collection

For statistics and bulk operations, `GetAllChildProfileItems` (lines 46-68) performs a depth-first walk through nested group structures. The method recurses only when encountering child items where `ConfigType.IsGroupType()` returns true, ensuring complete traversal of arbitrarily deep hierarchies.

## The v2rayN Server Switching Pipeline

v2rayN implements server switching through a reactive event-driven pipeline spanning multiple ViewModels:

1. **UI Selection**: In [`StatusBarViewModel.cs`](https://github.com/2dust/v2rayN/blob/main/StatusBarViewModel.cs) (lines 38-44), the status bar's server drop-down binds to `StatusBarViewModel.SelectedServer` using ReactiveUI bindings. When a user selects a different server from the `ComboBox`, the binding triggers `ServerSelectedChanged`.

2. **Event Publication**: The selection handler publishes `AppEvents.SetDefaultServerRequested` with the selected server's `ID` (lines 30-45). This decouples the UI from configuration logic.

3. **Event Handling**: `ProfilesViewModel` subscribes to this event (lines 68-72). Upon receiving the event, it invokes `SetDefaultServer(string)` to process the request.

4. **Configuration Update**: The `SetDefaultServer` method (lines 303-325) validates the ID, loads the corresponding `ProfileItem` via `AppManager`, and updates the global configuration's `_config.IndexId` through `ConfigHandler.SetDefaultServerIndex`. After persisting the configuration, the method refreshes the UI and forces an application reload, causing the core process to restart with the new server selection.

## Group Selection and Expansion

When a user selects a **group** rather than an individual server, the pipeline remains identical through step 4. However, the downstream `CoreConfigHandler` calls `GroupProfileManager.GetChildProfileItems` to expand the group before constructing the core configuration.

This expansion:
- Validates the group structure using `HasCycle`
- Resolves all child profiles recursively
- Flattens the hierarchy into a concrete server list
- Passes the merged list to the core process

The core effectively "switches" to a group by receiving the expanded member list rather than a single server definition.

## Practical Implementation Examples

### Resolving Group Children Programmatically

When activating a group from code:

```csharp
// Inside CoreConfigHandler or components needing actual servers
var profile = await AppManager.Instance.GetProfileItem(selectedId);
if (profile.ConfigType.IsGroupType())
{
    // Expand the group recursively with cycle checking
    var (children, _) = await GroupProfileManager.GetChildProfileItems(profile);
    // children now contains a flat List<ProfileItem>
}

```

### Triggering Server Switches via Events

Components can programmatically switch servers using the same event pipeline as the UI:

```csharp
public async Task SwitchTo(string indexId)
{
    // Publish the event that ProfilesViewModel listens for
    AppEvents.SetDefaultServerRequested.Publish(indexId);
    // ProfilesViewModel handles config update and core restart
}

```

### Creating Groups in Code

To create a new profile group programmatically:

```csharp
var group = new ProfileItem
{
    IndexId = Guid.NewGuid().ToString(),
    ConfigType = EConfigType.Xray,  // Must be a group-compatible type
    Remarks = "Production Servers",
    ProtocolExtra = new ProtocolExtraItem
    {
        GroupType = "Xray",
        ChildItems = "server-id-1,server-id-2,server-id-3",  // Comma-separated IDs
        Filter = null  // Optional sub-filter for dynamic children
    }
};
await ConfigHandler.AddProfile(_config, group);

```

## Summary

- **ProfileItem objects** store both individual servers and groups, with groups distinguished by `ProtocolExtra` metadata containing `GroupType` and comma-separated `ChildItems`.
- **GroupProfileManager** in [`ServiceLib/Manager/GroupProfileManager.cs`](https://github.com/2dust/v2rayN/blob/main/ServiceLib/Manager/GroupProfileManager.cs) handles cycle detection via `HasCycle`, child resolution via `GetChildProfileItemsByProtocolExtra`, and descendant collection via `GetAllChildProfileItems`.
- **Server switching** follows a reactive pipeline: `StatusBarViewModel` detects selection changes, publishes `SetDefaultServerRequested`, and `ProfilesViewModel` updates `_config.IndexId` through `ConfigHandler` before reloading the application.
- **Group expansion** occurs downstream in `CoreConfigHandler`, which flattens selected groups into concrete server lists before core configuration generation.
- **Cyclic references** are detected early through depth-first traversal with path tracking, preventing infinite recursion in nested group structures.

## Frequently Asked Questions

### How does v2rayN distinguish between a server and a group?

v2rayN stores both as `ProfileItem` objects. A group is identified by its `ProtocolExtra` property containing a `GroupType` value and a `ChildItems` string with comma-separated member IDs. Individual servers lack this metadata. The `ConfigType.IsGroupType()` method provides runtime type checking to differentiate containers from concrete endpoints.

### What prevents circular group references in v2rayN?

The `GroupProfileManager.HasCycle` method implements cycle detection using depth-first search with a visited set and recursion stack. Located at lines 5-15 of [`GroupProfileManager.cs`](https://github.com/2dust/v2rayN/blob/main/GroupProfileManager.cs), this method tracks the current traversal path and reports an error if it encounters an ID already present in the stack, blocking self-referential or mutually recursive group definitions before they cause stack overflow errors.

### How does selecting a group differ from selecting a server in the UI?

The UI publishes the same `SetDefaultServerRequested` event for both selections. However, when `CoreConfigHandler` processes a group ID, it calls `GroupProfileManager.GetChildProfileItems` to expand the group into its constituent servers. The core receives the flattened list rather than a single endpoint, effectively treating the selection as a composite configuration.

### Where is the active server index stored in v2rayN?

The active server ID is stored in `_config.IndexId`, persisted through `ConfigHandler.SetDefaultServerIndex`. When `ProfilesViewModel.SetDefaultServer` (lines 303-325) executes, it updates this field, saves the configuration to disk, and triggers an application reload to initialize the core process with the new server or expanded group configuration.