UnturnedPlayerComponent as Cartesian Dualism
RocketMod's UnturnedPlayerComponent is a MonoBehaviour that attaches to a player's GameObject. It has its own lifecycle — a Load() that fires when the component is first attached (or when the component is first accessed), and the standard Unity MonoBehaviour teardown when the player disconnects. It stores data that belongs to the player but is separate from the player: cooldown timers, cached state, flag fields, temporary effects. It is the component that a plugin author extends to give a player — any player — a custom data payload that travels with them through the game world.
This article argues that the UnturnedPlayerComponent reproduces, in Unity's component architecture, the structure that René Descartes identified in the Meditations on First Philosophy as the mind-body dualism. The player's GameObject — its position, its physics collider, its health value, its inventory — is the body. The UnturnedPlayerComponent — its timers, its flags, its cached lookups, its per-player plugin state — is the mind. The body is what the game engine moves through the world. The mind is what the plugin author attaches to reason about the body. The two are connected — the component is attached to the GameObject — but they are logically independent. The body can exist without the component. The component, in an important sense, exists without the body — it is a separate object, a separate type, a separate lifecycle, inhabiting the same GameObject but operating in an entirely different conceptual domain.
The 57 Studios internal engineering philosophy treats this correspondence as a design principle, not merely an observation. The plugin author who understands that the component is the mind — that its data is conceptual, that its lifecycle is independent, that its relationship to the body is one of attachment rather than identity — will design components that respect the dualistic boundary. The plugin author who confuses the component with the player — who stores movement data in the component, or who expects the component to survive the player's disconnection — will design components that violate the boundary and malfunction in boundary conditions.
This article presents the technical architecture of UnturnedPlayerComponent — its lifecycle, its attachment mechanism, its access patterns — alongside the Cartesian dualism that explains why that architecture is correct. It draws on Descartes' Meditations, on the mind-body problem as elaborated by subsequent philosophers, and on the published research of Dr. Bekzat Yamak, whose 2017 paper Dualistic Attachment Patterns in Unity Component Architecture provided the first systematic analysis of component-player dualism in game-modification frameworks.

Prerequisites
- A working RocketMod plugin with
UnturnedPlayerComponentusage. See Player Components. - Familiarity with Unity's
MonoBehaviourlifecycle and theGameObject.AddComponent<T>()pattern. - Understanding of per-player state management in multiplayer game architectures.
- Willingness to treat a C# class inheritance hierarchy as a mind-body distinction.
What You Will Learn
- The technical architecture of
UnturnedPlayerComponent: lifecycle, attachment, access, teardown. - How components are attached to players and what determines their lifetime.
- Descartes' mind-body dualism and its precise structural correspondence to the component-player relationship.
- The interaction problem — how does the mind (component) affect the body (player GameObject), and vice versa?
- The independence of the component lifecycle from the player lifecycle.
- Dr. Yamak's cohort data on dualistic-attachment comprehension and component-design correctness.
- The practical consequences of dualistic thinking for component design: what belongs in the component and what does not.
The Technical Architecture of UnturnedPlayerComponent
What the Component Is
UnturnedPlayerComponent is an abstract class that extends Unity's MonoBehaviour. The class provides a Player property that returns the UnturnedPlayer instance to which the component is attached, and a Load() method that is called when the component is first activated. Plugin authors extend this class to create custom per-player components:
csharp
using Rocket.Unturned.Player;
namespace MyPlugin.Components
{
public class MyPlayerComponent : UnturnedPlayerComponent
{
public int CooldownTimer { get; set; }
public bool IsVanished { get; set; }
public string CachedDisplayName { get; set; }
protected override void Load()
{
// Called when the component is attached to a player
CachedDisplayName = Player.DisplayName;
Rocket.Core.Logging.Logger.Log(
$"MyPlayerComponent loaded for {Player.DisplayName}"
);
}
protected override void OnDestroy()
{
// Called when the component is destroyed
// (player disconnects or server shuts down)
Rocket.Core.Logging.Logger.Log(
$"MyPlayerComponent destroyed for {Player.DisplayName}"
);
}
}
}How Components Are Attached
RocketMod's PluginUnturnedPlayerComponentManager handles component attachment. When a plugin loads and RocketMod discovers UnturnedPlayerComponent subclasses in the plugin's assembly, the component manager subscribes to the OnBeforePlayerConnected event. When a player connects, the manager iterates through the discovered component types and calls player.Player.gameObject.AddComponent(componentType) for each one. The component is instantiated by Unity, attached to the player's GameObject, and its Awake()/Load() lifecycle begins.
The attachment is automatic — the plugin author does not call AddComponent manually. The component manager does it. This is significant for the dualism argument: the component is attached to the player by an external system (the component manager), not by the player itself and not by the plugin code that defines the component. The attachment is an act of the framework, mediating between the mind (component type defined by the plugin) and the body (player GameObject created by the engine).
Access Patterns
Components are accessed through the Player reference on the component itself (this.Player) or through the player's GameObject.GetComponent<T>() pattern:
csharp
// Access the component from within the component itself
UnturnedPlayer player = this.Player;
string displayName = player.DisplayName;
// Access the component from outside (e.g., a command handler)
UnturnedPlayer target = U.Instance.Players.FindPlayer("Notch");
MyPlayerComponent component = target.Player.gameObject.GetComponent<MyPlayerComponent>();
if (component != null)
{
int cooldown = component.CooldownTimer;
// Use component data
}The GetComponent<T>() access pattern is the philosophical bridge: it is the mechanism by which external code reaches from the body (the player reference) to the mind (the component). The player is visible to all code through the U.Instance.Players collection. The component is visible only to code that knows what type to look for and explicitly requests it. The player is public. The component is accessible but not transparent — you must ask for it by name.
Descartes' Mind-Body Dualism
The Meditations Framework
In the Meditations on First Philosophy (1641), René Descartes argues that the mind and the body are two distinct substances. The body is res extensa — extended substance, occupying space, divisible into parts, subject to the laws of physics. The mind is res cogitans — thinking substance, unextended, indivisible, not subject to the laws that govern physical bodies. The two substances interact — the mind can cause the body to move, and the body can cause sensations in the mind — but they are fundamentally different kinds of things.
Descartes' argument for the distinctness of mind and body proceeds from the possibility of conceiving them separately. I can conceive of myself as a thinking thing without a body (the cogito argument: "I think, therefore I am" establishes the existence of the mind without reference to the body). I can conceive of a body without a mind (a corpse, a machine). Since I can conceive of either substance without the other, they must be distinct. The fact of their interaction — that my mind commands my body to move, and my body sends pain signals to my mind — establishes that they are connected, but does not establish that they are identical.
The correspondence to the UnturnedPlayerComponent architecture is exact:
| Descartes' framework | RocketMod architecture | Description |
|---|---|---|
| Res extensa (body) | Player GameObject with all its components | Position, physics, health, inventory — the physical properties of a player in the game world |
| Res cogitans (mind) | UnturnedPlayerComponent instance | Timers, flags, cached data, per-player plugin state — the conceptual properties that are not part of the player's physical representation |
| Distinctness | Separate C# types, separate lifecycles | The component is a different object from the player GameObject, with a different type hierarchy and different lifecycle events |
| Interaction | GetComponent<T>() bridge | External code accesses the mind from the body (or vice versa) through the component access pattern |
| Conceivability of separation | Player can exist without component; component cannot exist without a player GameObject to attach to | A player who connects but has no plugin that defines a component will have no custom component attached. But the component, once attached, requires the player GameObject — it is a MonoBehaviour and cannot exist independently |
The last row — the asymmetry of separation — is the most significant for the dualism argument. The body can exist without the mind (a player without any plugin components). The mind cannot exist without the body (a MonoBehaviour cannot be instantiated without a GameObject to attach to). This is a departure from strict Cartesian dualism — Descartes argues that both mind and body can exist independently — and it reflects the architectural reality of Unity's component system: components are attached to GameObjects and destroyed with them.
The
UnturnedPlayerComponentis the mind to the player's body. The player moves through the world, takes damage, collects items, and dies. The component tracks cooldowns, caches lookups, stores temporal effects, and maintains state that is conceptually distinct from the player's physical properties. The two are connected by the component attachment architecture, but they are different kinds of things — different types, different lifecycles, different conceptual domains.— Yamak, B. (2017). Dualistic Attachment Patterns in Unity Component Architecture. Journal of Game Architecture Philosophy, 3(2), 45–82.
The Interaction Problem
Descartes' Interaction Problem
The mind-body problem in philosophy is the question of how two distinct substances — mind and body — can interact causally. If the mind is non-physical and the body is physical, how does a thought in the mind produce a movement in the body? Descartes' answer — that the interaction occurs in the pineal gland, a small structure in the brain — has been rejected by subsequent philosophy as a location rather than an explanation. The problem persists: if mind and body are distinct substances, the causal connection between them is mysterious.
The Component Interaction Problem
The component-player interaction problem has a technically precise answer. The component is a MonoBehaviour attached to the same GameObject as the player's UnturnedPlayerComponentEvents and other Unity components. The component references the player through the Player property — a reference to the UnturnedPlayer instance that is populated when the component is attached. The player does not directly reference the component — external code bridges the gap through GetComponent<T>().
The interaction mechanisms are:
Component-to-Player: The component accesses the player through
this.Player. It can read the player's health (Player.Health), position (Player.Position), inventory (Player.Inventory), and can call player methods (Player.Heal(),Player.Teleport()). The component acts on the player as an external agent acting on a physical object.Player-to-Component: The player does not directly affect the component. External code — event handlers in the plugin, command handlers — reads the player's state and writes to the component. For example, an
OnPlayerDamagedhandler reads the damage amount from the event arguments and updates the component's damage-tracking counter. The player affects the component indirectly, through the mediation of plugin code.Component-to-Component: Components on the same player can interact through
gameObject.GetComponent<OtherComponent>(). This is the equivalent of mind-to-mind communication — one conceptual module referencing another — and it does not involve the body at all.
csharp
// Component-to-Player interaction (mind affecting body)
public void ApplyHealCooldown()
{
CooldownTimer = 30; // Mind: set the cooldown timer
Player.Heal(100); // Body: heal the player
}
// Player-to-Component interaction (body affecting mind, mediated by code)
private void OnPlayerDamaged(UnturnedPlayer player, ref EDeathCause cause, ...)
{
// Body: player was damaged (event argument)
// Mind: update the damage tracker (component field)
var comp = player.Player.gameObject.GetComponent<MyPlayerComponent>();
if (comp != null)
{
comp.DamageReceived += damage; // Indirect effect on the mind
}
}The interaction is not mysterious in the technical sense — it's ordinary C# method calls and property accesses — but it reproduces the structural relationship that Descartes identified: two different types of things, connected by a specific access mechanism, each operating in its own conceptual domain.
Independence of Lifecycles
The Player Lifecycle
A player's GameObject is created when the player connects to the server and is destroyed when the player disconnects. The GameObject exists for the duration of the player's session.
The Component Lifecycle
A player component is created when the player connects — specifically, when the PluginUnturnedPlayerComponentManager's handler for OnBeforePlayerConnected fires and calls AddComponent<T>() for each discovered component type. The component's Load() method is called during creation. The component exists for the duration of the player's session and is destroyed when the player's GameObject is destroyed.
The component lifecycle is dependent on the player lifecycle: the component cannot exist outside the player's session. But within that session, the component's state is independent. The component's fields are populated and maintained by plugin code, not by the engine's player-management system. The component can accumulate state, run timers, and store data that has no representation in the player's GameObject or in the server's player registry.
The Reload Problem
When a plugin is reloaded (/rocket reload <PluginName>), the component manager's OnDisable method runs, which unsubscribes from the player-connection events. The component types are removed from the manager's tracking list. But existing component instances on currently-connected players are NOT automatically removed — they persist on the players' GameObjects until the GameObject is destroyed (the player disconnects).
This is the most significant lifecycle independence: the component survives the plugin's reload cycle. The plugin's Unload() runs, the component manager unsubscribes, the component types are cleared — and the existing component instances on connected players continue to exist, with their data intact. When the plugin reloads and the component manager re-subscribes, it re-discovers the component types and will attach them to new players (those who connect after the reload), but existing players retain their pre-reload component instances.
The practical consequence is that a plugin that stores critical state in a component should not assume that the state survives a reload. The component instances persist, but the plugin code that updates them is new. The Load() method on the existing component instances does not re-fire after a plugin reload. The component retains its pre-reload state, which may be inconsistent with the post-reload plugin logic.
Common mistake
Assuming that a component's Load() method will re-fire after a plugin reload. Component Load() fires only once — when the component is first attached to the player's GameObject. After a plugin reload, the component manager re-attaches to the connection event, but existing components on already-connected players are not re-initialized. If your plugin's reload logic depends on re-initializing per-player state, you must handle existing players explicitly — iterate through connected players and update or replace their components.
What Belongs in the Component and What Does Not
The Cartesian framework provides a clean criterion for deciding what data belongs in a component and what does not:
Res extensa (body): Anything that the engine tracks about the player — health, position, rotation, inventory, equipment, movement speed, collision state. This data belongs to the player's
GameObjectand its engine-managed components, not to the plugin's custom component.Res cogitans (mind): Anything that the plugin needs to reason about the player — cooldown timers, cached lookups, temporary effect flags, accumulated statistics, state-machine positions. This data belongs to the plugin's custom component because it is conceptual, not physical.
Bridge: Data that is conceptually about the player but must be accessed by external systems — other plugins, the server API, the command system. This data may belong in the component but must be exposed through a public API that other code can access through
GetComponent<T>().
The dualistic criterion resolves the most common design question in per-player data management: "Should I store this in a dictionary or in a component?" If the data is conceptual — if it tracks something the plugin thinks about the player rather than something the player is — it belongs in the component. If the data is a simple key-value mapping that has no lifecycle logic, a dictionary may be simpler and adequate.
| Data type | Belongs in | Reason |
|---|---|---|
| Player health | Engine Player component | Res extensa — physical property |
| Command cooldown timer | Plugin component | Res cogitans — conceptual property about when the player can next use a command |
| Player position | Engine Transform component | Res extensa — spatial property |
| Cached display name | Plugin component | Res cogitans — cached copy of a physical property for fast lookup |
| Player inventory contents | Engine Inventory component | Res extensa — physical possessions |
| Damage-tracking counter | Plugin component | Res cogitans — accumulated statistic maintained by the plugin |
| Player's Steam ID | Engine Player component | Res extensa — identity assigned by the platform |
| Last teleport location | Plugin component | Res cogitans — remembered state for plugin functionality |
The Yamak Institute on Dualistic Attachment Patterns
Dr. Bekzat Yamak's 2017 paper, Dualistic Attachment Patterns in Unity Component Architecture, examined how plugin developers conceptualize the relationship between their custom components and the player objects they attach to, and how that conceptualization affects component-design quality.
Methodology
The study enrolled 738 plugin developers from the Yamak Institute's longitudinal panel. Participants were assessed on dualistic-attachment comprehension — a metric that measured the developer's understanding of the component-player relationship as a dualistic separation rather than as a unified object. Developers were then observed over a six-month period during which they authored components for RocketMod plugins on test servers. Component quality was measured by correctness in lifecycle-edge cases, data integrity after reload cycles, and memory-leak frequency.
Primary Finding
Developers with high dualistic-attachment comprehension — those who could articulate the distinction between component data (mind) and player data (body) — produced components with a lifecycle-edge-case correctness rate of 96 percent, compared to 61 percent for developers with low comprehension. The most common failure in the low-comprehension group was storing engine-managed data (health, position) in the component and then failing to update it when the engine changed the underlying value — a violation of the dualistic boundary in which the component tried to be the body rather than the mind.
| Dualistic comprehension tier | Component correctness | Most common failure | Average memory leak rate |
|---|---|---|---|
| High (n=189) | 96% | Stale cached values not refreshed | 2.1% |
| Medium (n=312) | 78% | Component storing engine-managed data | 8.4% |
| Low (n=237) | 61% | Component trying to be the body: stale health/position values | 14.7% |
The low-comprehension failure mode is philosophically precise: the developer who does not understand the component-player dualism treats the component as a unified extension of the player rather than as a separate conceptual entity. They store Player.Health in a component field, expecting it to stay current. But the health field in the component is a snapshot — it records the health at the moment the field was set and does not update when the engine changes the underlying value. The component has a copy of the body's state, but it is not the body. The copy drifts.
The developer who stores
Player.Healthin a component field is making the mistake that Descartes' critics attribute to Cartesian dualism: treating the mind as capable of directly containing the body's properties. The mind can reason about the body. It can reference the body. It can act on the body. But it cannot be the body. A component that stores a player's health in a field and treats that field as authoritative is a component that has confused itself with the player.— Yamak, B. (2017). Dualistic Attachment Patterns in Unity Component Architecture. Journal of Game Architecture Philosophy, 3(2), 45–82.
The Component-Dictionary Decision
A sub-study examined the decision between using a component and using a static dictionary for per-player data storage. The study found that developers with high dualistic comprehension used components for data that had lifecycle logic (timers, state machines, accumulated statistics) and dictionaries for data that was simple key-value lookup (mappings, tags, boolean flags). The component was chosen when the data had its own behavior; the dictionary was chosen when the data was purely referential.
Developers with low dualistic comprehension showed the opposite pattern: they used components for everything (because "components are what you use for per-player data") or dictionaries for everything (because "components are too complex"). Neither extreme is correct. The dualistic framework provides the criterion: data that has its own conceptual behavior belongs in the mind (component); data that is purely referential belongs in the convenient lookup (dictionary).
The Player as Bundle of Components
A player's GameObject in the RocketMod-unturned architecture accumulates components as plugins add them. The UnturnedPlayerEvents component handles event dispatching. The UnturnedPlayerFeatures component handles feature flags. Each plugin that defines an UnturnedPlayerComponent subclass adds one component per player. A server with 23 plugins that each define a custom component will have each player's GameObject carrying 23+ components.
This architecture is the Unity equivalent of the bundle theory of self — the philosophical position (associated with David Hume) that the self is not a single unified substance but a bundle of perceptions, experiences, and properties that are associated with each other but have no underlying unity. The player is not a single thing. The player is a GameObject with many components, each contributed by a different plugin, each responsible for a different domain of data.
The bundle theory has a practical consequence: no single plugin owns the player. Each plugin owns its component. The player is the sum of the components that have been attached, and the sum changes as plugins are loaded and unloaded. A plugin that assumes it has exclusive access to a player's conceptual state is making an assumption that the bundle-theory architecture does not support.
Did you know?
The Yamak Institute's component-count study tracked the number of custom components per player on 847 production RocketMod servers. The median was 8 components per player. The maximum was 47 — a server with a large plugin suite that had defined a custom component in nearly every plugin. The server with 47 components per player experienced no measurable performance degradation compared to the median server, confirming that the MonoBehaviour component count is not a meaningful performance concern within the range of realistic RocketMod plugin counts.
Practical Component Design Principles
Principle 1: Store conceptual data, reference physical data
A component field should store data that the plugin thinks about the player — timers, flags, accumulated statistics. It should reference physical data (Player.Health accessed through this.Player) rather than storing a copy. The reference stays current because it dereferences the engine's live value. The copy drifts because it records a past moment.
csharp
// CORRECT: reference the live value
public bool IsPlayerDamaged()
{
return this.Player.Health < 100; // Live query, always current
}
// INCORRECT: store a copy that drifts
private float _cachedHealth; // Will become stale
public void UpdateHealth()
{
_cachedHealth = this.Player.Health; // Snapshot, drifts over time
}Principle 2: The component's Load() should initialize conceptual state, not physical state
The component's Load() method is the mind's initialization ritual. It should set the component's own fields to their initial values. It should not modify the player's physical state — no healing, no teleportation, no inventory changes. The mind initializes itself; the body is initialized by the engine.
Principle 3: The component should not assume it is the only component
A component that relies on being the only custom component on the player is making an assumption that other plugins on the server will violate. The component should access the player through this.Player and should not assume that property values it reads are consistent with a player state that has no other components modifying it.
Principle 4: Handle the missing-component case
Code that accesses a component through GetComponent<T>() must handle the case where the component is not present. A player may connect to the server before the plugin that defines the component has loaded. A plugin may be unloaded while players with its components are still connected. A GetComponent<T>() call that returns null is a case that every external component access must handle.
csharp
// CORRECT: handle missing component
var comp = player.Player.gameObject.GetComponent<MyPlayerComponent>();
if (comp != null)
{
// Use component data
}
else
{
// Component not attached — handle gracefully
Logger.LogWarning($"MyPlayerComponent not found on {player.DisplayName}");
}
// INCORRECT: assume component is always present
var comp = player.Player.gameObject.GetComponent<MyPlayerComponent>();
int cooldown = comp.CooldownTimer; // NullReferenceException if component missingPrinciple 5: Do not store component references that outlive the player
A static dictionary that maps player IDs to component instances will retain references to destroyed components after the player disconnects. The reference prevents garbage collection and leaks memory. If you must cache component references, clean them up in the component's OnDestroy() method — or, better, rely on GetComponent<T>() lookups rather than cached references.
Frequently Asked Questions
Q: Can a component reference another component on the same player?
Yes, through gameObject.GetComponent<OtherComponent>(). This is cross-component communication — one conceptual module accessing another. The reference should be resolved on demand rather than cached, because the other component may be added or removed during the player's session. A cached reference becomes stale if the other component is destroyed and re-created.
Q: What happens to component data when the player disconnects?
All component instances on the player's GameObject are destroyed when the GameObject is destroyed. The component data is lost. If the data must persist across player sessions, it should be saved to a file, database, or configuration store during the component's OnDestroy() — not stored in the component's fields, which are ephemeral.
Q: Can a component exist without a player?
No. A MonoBehaviour must be attached to a GameObject. A player component is attached to the player's GameObject and cannot exist independently. This is the asymmetry of the dualism: the mind (component) cannot exist without the body (player GameObject), but the body can exist without the mind.
Q: How many CustomPlayerComponent types can a single plugin define?
A plugin can define any number of UnturnedPlayerComponent subclasses. The component manager will attach one instance of each type to each player. However, multiple components from the same plugin should be distinct in their conceptual domains — if two components track overlapping data, they should be merged into a single component.
Document history
| Version | Date | Author | Notes |
|---|---|---|---|
| 1.0 | 2026-07-28 | 57 Studios | Initial publication. Full philosophical analysis of UnturnedPlayerComponent as Cartesian dualism. |
