How v2rayN Handles Profile Groups and Implements Server Switching
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 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:
- Parses the comma-separated
ChildItemsstring fromProtocolExtra - Retrieves full
ProfileItemobjects fromAppManager - Preserves the ordering defined by the group
- 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:
-
UI Selection: In
StatusBarViewModel.cs(lines 38-44), the status bar's server drop-down binds toStatusBarViewModel.SelectedServerusing ReactiveUI bindings. When a user selects a different server from theComboBox, the binding triggersServerSelectedChanged. -
Event Publication: The selection handler publishes
AppEvents.SetDefaultServerRequestedwith the selected server'sID(lines 30-45). This decouples the UI from configuration logic. -
Event Handling:
ProfilesViewModelsubscribes to this event (lines 68-72). Upon receiving the event, it invokesSetDefaultServer(string)to process the request. -
Configuration Update: The
SetDefaultServermethod (lines 303-325) validates the ID, loads the correspondingProfileItemviaAppManager, and updates the global configuration's_config.IndexIdthroughConfigHandler.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:
// 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:
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:
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
ProtocolExtrametadata containingGroupTypeand comma-separatedChildItems. - GroupProfileManager in
ServiceLib/Manager/GroupProfileManager.cshandles cycle detection viaHasCycle, child resolution viaGetChildProfileItemsByProtocolExtra, and descendant collection viaGetAllChildProfileItems. - Server switching follows a reactive pipeline:
StatusBarViewModeldetects selection changes, publishesSetDefaultServerRequested, andProfilesViewModelupdates_config.IndexIdthroughConfigHandlerbefore 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, 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.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →