Weapon package
format
The BattleSpades Weapon Package (.bswp) is an open, versioned format for custom weapons and cosmetics. One file carries the voxel parts, hand-grip bones and attachment points, sight or scope, first- and third-person hold, keyframe animations and timed sounds. Anyone may build tools and clients that read or write it.
At a glance
- Format id
battlespades.weapon-package, current version 1.0.0 (semantic versioning), media typeapplication/vnd.battlespades.weapon-package+zip. - A ZIP (or an unpacked folder) with
manifest.jsonat the root and files undermodels/,sounds/,textures/andpreviews/. Every file is listed with its SHA-256 and size. - Packages are cosmetic. Damage, spread, ammunition, reload time and hitboxes always come from the server.
- Stock weapons can be exported as packages that reference the original assets instead of copying them, and reproduce stock hold, sight, muzzle flash and sound cues exactly.
- Nothing in a package runs code. Scripts are not part of the format.
Manifest example
Coordinates of bones and attachment points use the part’s KV6 voxel space: X across, Y toward the muzzle, Z down. Hold transforms use the game’s tool space: the same numbers as the original initial_position, arms_position_offset and view_model_size.
{
"$schema": "https://www.aosplay.net/schemas/bswp-1.schema.json",
"format": "battlespades.weapon-package",
"format_version": "1.0.0",
"kind": "weapon",
"id": "yourname.copperhead-rifle",
"version": "1.0.0",
"name": "Copperhead Rifle",
"authors": [
{
"name": "Your name"
}
],
"license": "CC-BY-4.0",
"target": {
"tool_id": 60,
"tool": "ASSAULT_RIFLE",
"base_template": "assaultrifle"
},
"compatibility": {
"protocol": 168,
"requires": [
"sounds.custom",
"skeleton.points"
],
"fallback": "retail"
},
"files": [
{
"path": "models/body.kv6",
"sha256": "…",
"bytes": 4872,
"media_type": "model/x-kv6"
},
{
"path": "sounds/bolt-rack.ogg",
"sha256": "…",
"bytes": 18211,
"media_type": "audio/ogg"
}
],
"models": [
{
"id": "body",
"file": "models/body.kv6",
"role": "body",
"views": [
"first_person",
"third_person"
],
"authored_offset": {
"first_person": [
0,
0,
0
],
"third_person": [
10,
-26,
4
]
},
"team_color": true
},
{
"id": "sight",
"ref": "retail:kv6/assaultRifle_sight.kv6",
"role": "sight",
"views": [
"sight"
]
}
],
"skeleton": {
"space": "kv6",
"points": [
{
"id": "grip.right",
"type": "hand_grip",
"hand": "right",
"part": "body",
"position": [
2,
15,
9.5
]
},
{
"id": "muzzle",
"type": "muzzle",
"part": "body",
"position": [
2,
51.5,
5
]
}
]
},
"sight": {
"mode": "retail",
"sight_position": [
0,
-0.07,
0
],
"zoom_factor": 1
},
"hold": {
"first_person": {
"initial_position": [
0,
0.2,
0
],
"initial_orientation": [
0,
0,
0
],
"arms_position_offset": [
-0.1,
-0.08,
-0.1
],
"view_model_size": 0.035
},
"third_person": {
"model_size": 0.04,
"initial_position": [
0,
0,
0
],
"initial_orientation": [
0,
0,
0
]
}
},
"animations": [
{
"id": "reload",
"event": "reload",
"duration": 0.9,
"base": "retail",
"tracks": [
{
"target": "weapon",
"property": "rotation",
"keys": [
{
"t": 0,
"value": [
0,
0,
0
]
},
{
"t": 0.3,
"value": [
-25,
0,
10
],
"ease": "ease_out"
},
{
"t": 0.9,
"value": [
0,
0,
0
]
}
]
}
],
"cues": [
{
"t": 0.45,
"sounds": [
"bolt-rack"
],
"gain": 0.9,
"pitch": [
-0.5,
0.5
]
}
]
}
],
"sounds": [
{
"id": "bolt-rack",
"file": "sounds/bolt-rack.ogg",
"duration_ms": 420,
"channels": 1
}
]
}Bones and attachment points
grip.right
Right hand grip (trigger hand).
grip.left
Left hand grip (support hand).
muzzle
Muzzle (flash, tracer origin).
sight.rear
Rear sight / eye line.
sight.front
Front sight post.
eject
Ejection port (casings).
socket.scope
Scope rail. Accepts scope.
socket.barrel
Barrel attachment. Accepts barrel.
socket.grip
Underbarrel grip. Accepts grip, underbarrel.
socket.magazine
Magazine well. Accepts magazine.
Any other id is allowed. Points record where they came from (authored, retail or estimated). Model parts can attach to a socket; scopes, suppressors and grips become their own KV6 parts.
Animation and sound timeline
Each clip is tied to a game event. Keyframe tracks add to the hold pose, and the original recoil or swing can be kept underneath. Cues play sound variants and emit effects at exact times after the event. Clip timing only changes how the weapon looks and sounds; the server still decides fire rate and reload time.
pullout
Tool raised (retail: 0.5 s, switch sound)
fire
Each accepted primary shot
fire_loop
Sustained automatic fire loop (loop: true)
fire_tail
Played once when the fire loop stops
dry_fire
Trigger pulled on an empty magazine
reload
One reload cycle (per shell for clip_reload weapons)
reload_end
The final reload cycle completed
zoom_in
Aim / scope in
zoom_out
Aim / scope out
melee
Melee swing (hit or miss)
melee_hit_block
Melee hit a block
melee_hit_player
Melee hit a player
throw_pin
Throwable: hold started
throw_release
Throwable: released
spin_loop
Barrel spin loop (minigun)
tool_loop_start
Sustained tool started
tool_loop
Sustained tool loop
tool_loop_stop
Sustained tool stopped
tool_extra
Tool-specific extra cue
inspect
Inspect / idle flourish (optional)
Cue options: sounds (variants), pick (cycle or random), gain 0–2 relative to the stock level, pitch as a random range in semitones, positional, and emit (muzzle_flash, eject_casing, tracer, camera_kick or custom.*). Custom events use the custom. prefix.
Sounds
- Ogg Vorbis/Opus or WAV, 8–96 kHz, mono or stereo. Use mono for weapon sounds: the game can only position mono sounds in the world.
- Up to 10 s per sound (20 s for loops) and 1024 KB. Community submissions allow 24 custom sounds of up to 512 KB each and 2 MB in total.
- Skin Studio measures peak and RMS level and suggests a gain toward about −18 dBFS RMS with peaks below −1 dBFS.
- Reviewers listen to every submitted sound before approval. Hate speech, copyrighted music and deafening or clipped audio are rejected.
Versioning and compatibility
- Readers accept every 1.x manifest. A newer minor version loads with a warning, and features the reader does not know are ignored.
- Unknown fields are always kept when a package is opened and saved again. Vendor data goes under
extensionswith a namespaced key. - New roles, events or capabilities are warnings, so only the unknown entry is skipped. A different major version is refused.
compatibility.requireslists capabilities (models.multipart, models.custom_geometry, skeleton.points, sight.iron, sight.overlay, animation.keyframes, sounds.custom, sounds.timeline, muzzle_flash.custom, colors.team). A client missing one usesfallback: stock visuals, or not equipping the item.- Older data upgrades through ordered migrations. The client’s previous
weapon-presentation.jsonsight and sound tuning already converts to packages.
Stock firearm defaults
Generated from the native client’s recovered retail tables (retail_tool_hold, muzzle flash tables and weapon catalogue sound slots). A new package for a weapon starts from these values.
| Tool | id | initial_position | arms offset | view size | model size | flash (view) | reload | fire sound |
|---|---|---|---|---|---|---|---|---|
| Smg | 7 | (0, 0, 0) | (0, 0, 0) | 0.05 | 0.065 | (0, 0.12, 0.5) ×0.5 | 1.25 s | smg_fire_loop |
| Minigun | 8 | (0, 0, 0) | (0, 0, 0) | 0.05 | 0.065 | (0, -0.18, 2) ×0.75 | 2 s | minigun_fire_single |
| Shotgun | 9 | (0, 0, 0) | (0, 0, 0) | 0.05 | 0.065 | (-0.05, 0.12, 0.8) ×1 | 0.5 s / shell | shotgunshoot |
| Shotgun2 | 10 | (0, 0.1, 0) | (0, -0.1, 0) | 0.05 | 0.065 | (-0.05, 0.12, 0.8) ×1 | 1 s / shell | shotgun_double_fire01 |
| Pistol | 17 | (0, 0, 0) | (0, 0, 0) | 0.05 | 0.065 | (-0.05, 0.12, 0.35) ×0.5 | 0.6 s | pistolshoot |
| Sniper | 18 | (0, 0.1, 0) | (0, -0.1, 0) | 0.05 | 0.065 | (0, 0.12, 0.9) ×0.5 | 2 s | semishoot |
| Sniper2 | 19 | (0, 0.2, 0.1) | (0, -0.2, -0.1) | 0.05 | 0.065 | (0, 0.12, 0.9) ×0.5 | 3 s | semi_weak_shoot |
| Tommygun | 35 | (0, 0.12, 0) | (0, -0.12, 0) | 0.05 | 0.04329 | (0, 0.35, 1.2) ×0.5 | 2 s | tommygun_fire_loop |
| Snub Pistol | 36 | (0, 0.12, 0) | (0, -0.12, 0) | 0.05 | 0.04329 | (0, 0.34, 0.5) ×0.5 | 0.75 s / shell | snub_fire |
| Classic Shotgun | 37 | (0, 0, 0) | (0, 0, 0) | 0.05 | 0.065 | (-0.05, 0.12, 0.8) ×1 | 0.5 s / shell | classic_shotgunshoot |
| Classic Smg | 38 | (0, 0, 0) | (0, 0, 0) | 0.05 | 0.065 | (0, 0.12, 0.5) ×0.5 | 1.25 s | classic_smg_fire_loop |
| Automatic Pistol | 53 | (-0.15, -0.05, 0.2) | (0, 0.05, -0.05) | 0.035 | 0.04 | (0, 0.12, 0.5) ×0.5 | 1 s | AoS_soundfx_PLAYER_marksman_wpn_AUTOPISTOL_fire_loop_001 |
| Assault Rifle | 60 | (0, 0.2, 0) | (-0.1, -0.08, -0.1) | 0.035 | 0.04 | (0, 0.12, 0.5) ×0.5 | 0.9 s | AoS_soundfx_PLAYER_commando_wpn_ASSAULT_RIFLE_burst_fire_001-005 |
| Light Machine Gun | 61 | (0, 0, 0.25) | (0, 0, 0) | 0.055 | 0.055 | (0, 0.12, 0.5) ×0.5 | 2 s | AoS_soundfx_PLAYER_medic_wpn_LMG_loop_fire_001-002 |
| Auto Shotgun | 62 | (0, 0.125, 0.13) | (0, -0.1, -0.2) | 0.05 | 0.05 | (-0.05, 0.12, 0.8) ×0.6 | 2.5 s | AoS_soundfx_PLAYER_specalist_wpn_AUTO_SHOTGUN_fire_001-005 |
Build with it
- Skin Studio Advanced edits multi-part KV6 models, places bones with a 3D gizmo, previews the first- and third-person hold, and records keyframes and sound cues on a timeline. It imports and exports
.bswp. - Validate your own tools against the JSON Schema. The reference TypeScript implementation (types, validator, migrations, reader and writer) is in
src/lib/creator/packageof the site repository, and the full specification isdocs/WEAPON_PACKAGE_FORMAT.md. - The native BattleSpades client does not load packages yet. The specification includes a loader checklist for client developers.