Systems Overview
The RisingV.Core.Systems namespace contains a collection of “System” classes that encapsulate core game mechanics and domain-specific logic within the RisingV framework. Each System interacts directly with Unity’s Entity Component System (ECS) or hooks into RisingV’s event architecture to manage in‑game functionality such as castle ownership, territorial control, chat handling, clan management, damage tracking, and loot distribution. By centralizing these workflows, Systems ensure consistent, maintainable behavior across plugins and support hot‑reload without losing runtime state.
API Documentation
For full details on available classes and methods in RisingV.Core.Systems, refer to the official API reference:
Role and Responsibilities
Encapsulation of Domain Logic
Each System class groups related functionality around a specific game domain. For example:- CastleHeartSystem locates and queries “Castle Heart” entities (the central building in a player’s castle).
- CastleTerritorySystem translates world‑space positions into grid coordinates and determines which castle (and thus which clan) controls a particular region.
- ChatSystem intercepts low‑level network chat events, decodes message payloads, and publishes them as RisingV events.
- ClanSystem manages clan creation, membership, and leadership roles by manipulating ECS components on player entities.
- DamageTrackingSystem tracks and aggregates damage dealt by players to targets, enabling percent‑based loot distribution or stat calculations.
- LootSystem listens for unit death events, consults damage tracking data, and spawns item drops based on configured loot tables and player contributions.
Integration with ECS & EventBridge
- Most Systems inherit from
Shared.Systems.System, which provides convenience methods for scheduling updates and subscribing to RisingV’s in‑game events (EventBridge). - Systems often create
EntityQueryinstances (wrapped inLazyRetryfor on‑demand initialization) to locate entities with specific component combinations (e.g.,CastleHeart+TeamDatafor castle ownership). - By responding to events instead of patching game methods directly, Systems decouple game integration from mod logic. This allows multiple plugins to rely on the same core System APIs and ensures consistent behavior even if underlying game code changes.
- Most Systems inherit from
Stateful Management & Hot‑Reload
- Many Systems maintain runtime state (cached queries, dictionaries of tracked damage, etc.).
- When a plugin or server hot‑reloads, System constructors and
OnUpdateloops re‑initialize or resume operation seamlessly—no manual intervention needed to rebuild queries or re‑attach handlers. - Underlying data (e.g., active damage maps, registered clan relationships) may persist in shared databases or in ECS components, ensuring minimal disruption across reloads.
Interoperability with Engines & Aspects
- Systems often delegate specific tasks to Engines (e.g.,
LootEngine,DamageEngine,DeathEngine) and Aspects (e.g.,EntityAspect,BuffAspect) to perform high‑level operations such as spawning loot, applying buffs, or writing to persistent databases. - For example,
LootSystemusesDamageTrackingSystemdata to callLootEngine.SpawnLootfor each eligible player, ensuring correct distribution of drops.
- Systems often delegate specific tasks to Engines (e.g.,
Core System Classes
Below is a summary of each System class provided by RisingV.Core.Systems, along with its primary purpose and typical usage. Click the class name to view its API reference.
CastleHeartSystem
- Namespace:
RisingV.Core.Systems - Purpose: Locates all “Castle Heart” entities and provides utilities to query which castle belongs to which player or clan.
- Key Methods:
GetAllCastleHearts(): Returns an array of all activeEntityinstances that have bothCastleHeartandTeamDatacomponents.FindCastleHeartForUser(Entity user): Searches for the castle heart entity associated with a given user’sUsercomponent.
- Usage:
// Determine if a user owns a castle if (CastleHeartSystem.FindCastleHeartForUser(playerUserEntity, out var heartEntity)) { // 'heartEntity' is the Castle Heart owned by the player } - API Reference: CastleHeartSystem
CastleTerritorySystem
- Namespace:
RisingV.Core.Systems - Purpose: Converts world positions into a grid coordinate system and determines which castle (and thus which clan) controls a specific tile. This supports territory‑based gameplay (e.g., claiming land, region‑specific rules).
- Key Methods:
GetGridPosition(Vector3 worldPos): Computes the discrete grid tile for a world position.GetCastleOwnerAtPos(float3 worldPos): Returns theEntityof the castle that controls the given position, orEntity.Nullif unoccupied.
- Usage:
// Check castle ownership for a location float3 worldPosition = new float3(10f, 0f, -5f); Entity owningCastle = CastleTerritorySystem.GetCastleOwnerAtPos(worldPosition); if (owningCastle != Entity.Null) { // The returned Entity is the Castle Heart controlling that tile } - API Reference: CastleTerritorySystem
ChatSystem
- Namespace:
RisingV.Core.Systems - Purpose: Intercepts and processes raw network chat events from Stunlock’s game code, decodes message payloads, and exposes a clean API for sending or receiving chat messages via RisingV’s
EventBridge. - Key Features:
- Registers handlers for incoming chat packets (
ChatMessageEventQuery) to publishChatMessageEvents to plugin subscribers. - Provides
SendChat(Entity sourceEntity, string message)to programmatically broadcast messages from a specific player or system.
- Registers handlers for incoming chat packets (
- Usage:
// Subscribe to chat messages EventBridge.Subscribe<ChatMessageEvent>(evt => { Logger.LogInfo($"Received chat from {evt.SourceEntity}: {evt.MessageText}"); }); // Send a system announcement ChatSystem.SendChat(systemEntity, "[Server] Welcome to the Realm!"); - API Reference: ChatSystem
ClanSystem
- Namespace:
RisingV.Core.Systems - Purpose: Manages all clan‑related state, including creation, membership, leadership roles, and deletion. Encapsulates common clan operations by reading and writing ECS components on user entities.
- Key Methods:
GetByName(string clanName): Returns theEntityof a clan by its name, orEntity.Nullif not found.GetClanLeader(Entity clanEntity): Returns the user entity of the clan leader for a specified clan.GetClanMembersAsPlayers(Entity clanEntity): Returns a list of all players in the specified clan, including their user entities.IsClanLeader(User user): Checks if a user holds the Leader role.
- Usage:
// Create a new clan for the player Entity clanEntity = ClanSystem.GetByName("VampireLegion"); var leader = ClanSystem.GetClanLeader(clanEntity); var members = ClanSystem.GetClanMembersAsPlayers(clanEntity); // Check if a user is the clan leader bool isLeader = ClanSystem.IsClanLeader(playerUser); - API Reference: ClanSystem
DamageTrackingSystem
- Namespace:
RisingV.Core.Systems - Purpose: Aggregates all damage dealt by players (or other entities) to a given target. This fine‑grained tracking enables percent‑based reward distribution (e.g., loot sharing) and advanced stat calculations.
- Key Features:
- Listens for
DamageEvents or similar hooks to incrementally accumulate damage per source entity. - Maintains an in‑memory map (
damageComponent) keyed by target entity ID, storingDamageMapinstances that record totals, percentages, and level information. - Exposes methods to retrieve or reset damage data for a given target.
- Listens for
- Usage:
// During a death event, query top damage contributor IReadOnlyDictionary<NetworkId, DamageMap> damageMap = DamageTrackingSystem.GetDamageMap(targetEntity); var sortedContributors = damageMap.OrderByDescending(kvp => kvp.Value.TotalDamage); var topContributor = sortedContributors.First().Key; - API Reference: DamageTrackingSystem
LootSystem
- Namespace:
RisingV.Core.Systems - Purpose: Coordinates item drop logic when entities die. By consulting
DamageTrackingSystem, it fairly distributes loot according to each contributor’s damage percentage, and then interacts with RisingV’s item‐spawning APIs to physically drop items in the world. - Key Features:
- Subscribes to
UnitDeathEventand calculates drop shares for each eligible player. - Uses
IItemDropimplementations (configured through item definitions or drop tables) to determine exactly what to spawn for each contributor. - Safely handles errors in drop operations and logs failures to aid debugging.
- Subscribes to
- Usage:
// Automated via event subscription; no explicit call needed unless customizing behavior // Example: Extend drop logic with a special event EventBridge.Subscribe<UnitDeathEvent>(evt => { if (FactionAspect.GetFaction(evt.Victim).Equals(Faction.Boss)) { var itemsToDrop = LootSystem.GenerateItemDrops(_lootTables[evt.Victim.GetHashCode()]); var itemsDropped = LootSystem.DropItemsNearby(evt.Victim, itemsToDrop); // or var itemsDropped = LootSystem.GenerateAndDropItems(evt.Victim, itemsToDrop); } }); - API Reference: LootSystem
Integration & Usage
Registration
- Systems are typically registered during plugin initialization via
SystemManager. For example inPlugin.OnInitialize():SystemManager.AddSystem<ChatSystem>(this); SystemManager.AddSystem<ClanSystem>(this); SystemManager.AddSystem<DamageTrackingSystem>(this); SystemManager.AddSystem<LootSystem>(this); SystemManager.AddSystem<CastleHeartSystem>(this); SystemManager.AddSystem<CastleTerritorySystem>(this); - After
Ready(), all systems are active: queries are built, event subscriptions are live, and ECS interactions can proceed normally.
- Systems are typically registered during plugin initialization via
Event Subscriptions
- Many Systems publish or consume events via
EventBridge. Plugin authors can hook into these events for custom logic. For example, subscribe toChatMessageEventto modify or filter chat messages, or toUnitDeathEventto award achievements.
- Many Systems publish or consume events via
ECS Queries
- Systems use
LazyRetry<EntityQuery>wrappers to defer query creation until entities and components are guaranteed to exist. This avoids null references during server start or plugin reload. - For example,
CastleHeartSystem.HeartQueryonly builds itsEntityQuerythe first time it’s needed, and retries if initial creation fails.
- Systems use
Hot‑Reload Support
- Because Systems rely on shared ECS components and event subscriptions, a hot‑reload (triggered by BepInEx or plugin manager) seamlessly reconnects Systems:
- Queries are lazily reinitialized.
- Cached damage data or clan state stored in persistent components remains intact.
- Event subscriptions resume automatically once
Reload()completes.
- Because Systems rely on shared ECS components and event subscriptions, a hot‑reload (triggered by BepInEx or plugin manager) seamlessly reconnects Systems:
Cross‑System Collaboration
- Systems often delegate tasks to Engines or Aspects. For example:
LootSystemcallsDamageTrackingSystem.GetDamageMap(...)(another System) before spawning items viaItemAspectorLootEngine.CastleTerritorySystemusesCastleHeartSystem.FindCastleHeartForUser(...)to resolve ownership.
- This layered approach promotes single responsibility:
- Hooks publish raw events.
- Systems process and validate event data.
- Engines/Aspects execute high‑level logic (e.g., database updates, world modifications).
- Systems often delegate tasks to Engines or Aspects. For example:
Summary
The RisingV.Core.Systems layer is a critical part of the RisingV architecture, responsible for:
- Encapsulating core gameplay mechanics (chat, clans, castles, damage, loot).
- Processing in‑game events and coordinating with Engines/Aspects for persistent effects.
- Abstracting direct ECS interactions and network packet handling behind well-defined APIs.
- Ensuring consistent behavior across plugins and supporting hot‑reload without losing runtime state.
By leveraging these System classes, plugin authors can focus on feature development—knowing that RisingV.Core.Systems will reliably manage underlying game interactions and maintain a cohesive, event‑driven workflow.