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
EntityQuery
instances (wrapped inLazyRetry
for on‑demand initialization) to locate entities with specific component combinations (e.g.,CastleHeart
+TeamData
for 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
OnUpdate
loops 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,
LootSystem
usesDamageTrackingSystem
data to callLootEngine.SpawnLoot
for 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 activeEntity
instances that have bothCastleHeart
andTeamData
components.FindCastleHeartForUser(Entity user)
: Searches for the castle heart entity associated with a given user’sUser
component.
- 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 theEntity
of the castle that controls the given position, orEntity.Null
if 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 publishChatMessageEvent
s 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 theEntity
of a clan by its name, orEntity.Null
if 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
DamageEvent
s or similar hooks to incrementally accumulate damage per source entity. - Maintains an in‑memory map (
damageComponent
) keyed by target entity ID, storingDamageMap
instances 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
UnitDeathEvent
and calculates drop shares for each eligible player. - Uses
IItemDrop
implementations (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 toChatMessageEvent
to modify or filter chat messages, or toUnitDeathEvent
to 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.HeartQuery
only builds itsEntityQuery
the 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:
LootSystem
callsDamageTrackingSystem.GetDamageMap(...)
(another System) before spawning items viaItemAspect
orLootEngine
.CastleTerritorySystem
usesCastleHeartSystem.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.