MSW DefaultPlayer
Use the msw-general ModelBuilder to inspect/patch the DefaultPlayer model file, manage components, and configure movement / physics / HP / camera.
For costume / avatar equipment, see the
msw-avatarskill. Costumes apply not only to DefaultPlayer but to any entity, so they live in a separate skill.
DefaultPlayer overview
What is DefaultPlayer?
The player character model provided by default in the MapleStory Worlds Maker workspace.
- When any user enters a world, a player entity is created based on this model.
- The model ID to use is specified by the
PlayerUriproperty ofDefaultUserEnterLeaveLogic.
File location and structure
DefaultPlayer is made up of two .model files:
Important: both files are located in
./Global/. Custom script files are created under./RootDesk/MyDesk/.
DefaultPlayer is patched through ModelBuilder
DefaultPlayer is managed through the sibling msw-general/scripts/model/msw_model_builder.cjs, not raw JSON edits.
- Change a property value:
ModelBuilder.read("./Global/DefaultPlayer.model").value(...) - Add/remove a component:
component()/removeComponent() - Check the base component list:
ModelBuilder.snapshot("./Global/Player.model")
File structure detail
Player.model (base)
Components: the full list of default components on the player (MOD.Core.* native components)Properties: model property → component property link definitions (properties editable from the inspector)Values: empty (defaults are provided by the engine)
DefaultPlayer.model (override)
Components: only the components added on DefaultPlayer (e.g.script.PlayerHit,script.PlayerAttack)Values: the array of overridden setting values
DefaultPlayer default component list
Native components inherited from Player.model:
Script components added in DefaultPlayer.model:
Quick reference — key components
DefaultPlayer Values structure
Format of each entry in the Values array of DefaultPlayer.model:
TargetType rules
null: a model property defined in Player.model's Properties (linked through Properties to the actual component property)"MOD.Core.<ComponentName>": directly override a property on a specific native component"script.<ScriptName>": a property of a custom script component
Model properties (TargetType: null)
Mapped to actual component properties through the links defined in Player.model's Properties.
Direct component override (TargetType: specific component)
Values that directly override a component property rather than going through a model-property link:
Movement components per map mode
See
msw-general/references/platform.md§4 for the TileMapMode↔Body mapping table. Depending on the map mode, one of RigidbodyComponent / KinematicbodyComponent / SideviewbodyComponent is active.
Identifying a player (for script reference)
entity.PlayerComponent ~= nil→ whether the entity is a player_UserService.LocalPlayer→ my player entity (client-only)_UserService:GetUserEntityByUserId(userId)→ player entity for a specific user
Player entity runtime structure (root vs children)
A spawned player is not a single flat entity. At runtime the engine builds a small hierarchy under the player root, and avatar action selectors live on grandchild entities — not on the root. This affects how you look up components and how you trigger avatar poses.
Component → entity mapping
player:GetComponent("AvatarBodyActionSelectorComponent") returns nil and the LSP does not warn (the signature is Component, not nilable). The failure is only visible at runtime.
How to reach the selectors
AvatarRendererComponent:GetBodyEntity()andGetFaceEntity()are declared@ExecSpace("ClientOnly")— calling them from a server-side method returnsnil. UseGetFirstChildComponentByTypeName(name, recursive=true)as the cross-side fallback.
Triggering avatar poses — use StateComponent, never write the selector directly
A live player has a state machine running every tick: PlayerControllerComponent evaluates input/movement → StateComponent transitions (IDLE / MOVE / etc.) → AvatarStateAnimationComponent.ReceiveStateChangeEvent translates that into BodyActionStateChangeEvent → the selector's ActionState is rewritten.
This means writing AvatarBodyActionSelectorComponent.ActionState directly is a silent overwrite: the value applies for one frame, then the next state-machine tick maps the current StateComponent state back onto the selector and your write is gone. Logs print ActionState=Attack immediately after the assignment, but in play mode the pose flickers for one frame and disappears.
Correct entry point
State keys come from the player's ActionSheet. The DefaultPlayer ships with 11 UPPERCASE keys, each mapped to a default animation:
The state machine auto-returns to IDLE once the action finishes — no manual restore timer needed.
Casing pitfall: the string key passed to
ChangeStateis UPPERCASE ("ATTACK"). The enum value written toselector.ActionStatedirectly (NPC path — see below) isMapleAvatarBodyActionState.Attack— PascalCase, separate API surface, same underlying state. Wrong casing on the string side ("attack"/"Attack") misses the ActionSheet mapping silently — no warning.
When can you write the selector directly?
Only on entities without a running PlayerControllerComponent + StateComponent + AvatarStateAnimationComponent stack — e.g. NPCs / monsters that use the avatar renderer for visuals but have no input controller driving a state machine. On those, selector.ActionState = MapleAvatarBodyActionState.<Pose> sticks. On DefaultPlayer-shaped entities, route through StateComponent:ChangeState(...) instead.
Key services at a glance (for script reference)
How to modify DefaultPlayer
Changing property values (Values)
Load ./Global/DefaultPlayer.model with ModelBuilder.read(), then update values with value().
Example: set movement speed to 2.0
Note: both the model property (
TargetType: null,Name: "speed") and the direct component override (TargetType: "MOD.Core.MovementComponent",Name: "InputSpeed") can exist. Set both consistently throughvalue().
Example: jump force 1.5 + HP 2000
Adding a new Values entry
Use ModelBuilder.value(targetType, name, value, typeKey). The builder generates the ValueType descriptor; do not hand-write type strings.
Common typeKey values: bool, int, float, double, string, vector2, vector3, data_ref, collision_group.
Adding a component
Use component() on ./Global/DefaultPlayer.model.
Only add components that are not inherited from Player.model. For inherited native components (PlayerComponent, MovementComponent, CameraComponent, StateComponent, etc.), override values with value(...) instead of redeclaring the component. ModelBuilder warns (M040) when a component already inherited from BaseModelId: "player" is added locally.
Adding a custom script component:
Custom scripts (.mlua) must be created under
./RootDesk/MyDesk/. Write the script first, Makerrefresh, then add"script.<ScriptName>"with the builder.
Adding a non-inherited native component:
Removing a component
Use removeComponent() on DefaultPlayer.model. Related Values entries are removed by the builder.
Caution: components inherited from Player.model (the base) are not in DefaultPlayer.model's Components. Removing base components requires patching
Player.modelthroughModelBuilder, and is generally not recommended.
Pitfalls (Common Pitfalls)
Common pitfalls when adding to Components and changing Values in DefaultPlayer.model.
Component list pitfalls
Values change pitfall — only jumpForce / speed need extra care
Most Values entries are in the TargetType=null (alias) form and can be set through ModelBuilder.value(null, ...). Only the two fields below are exceptions — both an alias and a native entry (TargetType="MOD.Core.MovementComponent") exist at the same time:
On entity spawn, Values are applied in array order and both write to the same native field; the native entry appears later in the array, so the native value wins.
- Wrong: editing only the alias side (
jumpForce/speed) → overwritten by the later native entry and ignored - Right: edit both consistently to the same value, or edit only the native side (
JumpForce/InputSpeed)
Other alias-only entries (walkAcceleration, gravity, camera-related except cameraDeadZone, nameTag, damageSkinId, damageDelayPerAttack, triggerBody*, maxHp, etc.) can be modified through the alias as-is.
Hiding DefaultPlayer
DefaultPlayer's components are inherited from the base model, so they cannot be deleted. Disabling them via Enable=false is the only option.
Component keep/disable classification
Builder edit — add Enable=false to Values
Set the component values through ModelBuilder.value(). For components that don't yet have an Enable entry, the builder adds one.
After saving, Maker Refresh is required.
DefaultPlayer component extension patterns
- Non-avatar player: disable AvatarRendererComponent → add SpriteRendererComponent → set SpriteRUID.
- Collision setup: tweak ColliderType and CollisionGroup in TriggerComponent's Values.
- SpawnLocation: place a Special → SpawnLocation in the map (.map) file (revive position).
Workflows
Modifying basic player properties (movement speed, jump force, HP, etc.)
Adding a custom script to the player
Changing camera settings
Boundaries and caveats
In scope
- Inspect/patch the DefaultPlayer/Player model files through ModelBuilder
- Add/remove components through
component()/removeComponent() - Movement / physics / HP / camera settings through
value()
Out of scope
- Costume / avatar equipment →
msw-avatarskill - UI editor → .ui files under
./ui/(dedicated skill) - Map editing → .map files under
./map/(dedicated skill, including NPC/monster spawn) - General scripts / resources → each dedicated skill
Constraints
- Careful with Global/: DefaultPlayer.model and Player.model live in
./Global/. This folder is reserved for engine default templates, so creating new files here is not recommended. - Custom script location: new script files must be created under
./RootDesk/MyDesk/. - Map mode caveat: the active movement component differs depending on the map mode (MapleTile/RectTile/SideViewRectTile).
- ValueType correctness: when adding a Values entry, use
ModelBuilder.value()with an explicittypeKey; do not hand-writeValueType. - Maker Refresh: after adding/modifying scripts, Maker Refresh is required (.codeblock is auto-generated).


