College Hockey Dynasty
Reference

Custom Data Packs

A Data Pack is a single JSON file that replaces the game's default fictional league with your own conferences, teams, and players. Import one from a local file or a hosted URL on the Data Packs screen in the main menu — importing a custom pack is a one-time Premium unlock. This page documents every field the app reads and the validation rules enforced on import — the exact schema, straight from the app's own code.

Don't want to hand-write JSON? The Data Pack Builder is a free browser tool that builds a valid pack for you — add conferences and teams, auto-generate legal rosters, and download a file ready to import. Nothing you enter leaves your browser.

1. Quick Start

The smallest valid pack is a JSON object with pack_name, version, conferences, teams, and players that satisfies these minimums:

JSON keys are snake_case. All IDs are UUID strings (standard 8-4-4-4-12 hex format). They just need to be unique within the pack and used consistently to link records together — a team's conference_id points at a conference's id, and a player's team_id points at a team's id.

2. Top-Level Structure

KeyTypeRequiredNotes
pack_nameStringYesDisplay name shown in the Data Packs list. Also seeds a stable ID for the pack itself (same name → same ID across installs/re-imports).
versionStringYesFree-form version label (e.g. "1.0"). Not validated against a format.
start_yearIntNoThe college hockey season the pack represents, as the year the season starts (e.g. 2025 for the 2025-26 season). A new game built from this pack begins in that season instead of the live calendar year. Omit to default to the current season.
conferencesArrayYesAt least 1.
teamsArrayYesAt least 4 across the whole pack.
playersArrayYesAt least 1 total; each team needs 12+ skaters and 2+ goalies.
pro_teamsArray of StringNoOverrides the 32 team names your players get drafted to in the Pro Draft — see §13. Omit to use the game's built-in fictional pro league.

3. Conferences

KeyTypeRequiredNotes
idUUIDYesUnique within the pack.
nameStringYesDisplay name.
exposure_ratingIntYes0–100. National TV reach / recruiting prestige — feeds recruiting exposure and NIL market visibility.
abbreviationStringNoShort code (e.g. "AHA"). Derived from name if omitted — see §9.
regionStringNoOne of northeast, midAtlantic, midwest, upperMidwest, greatLakes, atlanticSouth. Places the conference geographically for recruiting proximity and bracket siting. An unknown/omitted region is hashed deterministically so custom conferences still get a distinct recruiting territory.
is_independentBoolNoSee §7 Independents. Inferred from the name/abbreviation if omitted.
tournament_presetStringNoName of a built-in tournament format — see §10. Ignored if tournament is also present.
tournamentObjectNoFull inline tournament spec — see §10. Takes priority over tournament_preset.

4. Teams

KeyTypeRequiredNotes
idUUIDYesUnique within the pack. Also used as team_id on player records.
conference_idUUIDYesMust match a conference's id in the same pack.
nameStringYesFull program name.
mascotStringYesNickname (e.g. "Force", "Eagles").
primary_color / secondary_colorString (hex)Yes#RRGGBB hex colors, used for jerseys, logos, and UI accents.
prestigeIntYes0–100. Drives recruiting pull, NIL collective size, and revenue-share cap.
legacy_scoreIntYes0–100. Historical program strength — feeds donor/legacy financial mechanics.
facilities_ratingIntYes0–100. Facility quality tier — mapped internally to a facilities level.
home_rink_widthIntNoRaw rink width in feet. Bucketed to the nearest tier: < 90 → Standard (85 ft), 90–97 → Wide (92 ft), ≥ 98 → Olympic (100 ft). Omit for Standard.
abbreviationStringNoShort code. Derived from name if omitted — see §9.
logo_urlString (URL)NoRemote crest image URL, fetched once and cached. Omit to fall back to an initials/color avatar.
stateStringNoUS state or Canadian province the program calls home (e.g. "Colorado") — shown on the team-selection info sheet and team profile. Purely cosmetic; omit if you don't want it shown.
Starting revenue-share cap is prestige × 15,000 + 150,000 and starting NIL collective balance is prestige × 3,000 + 30,000 — both derived automatically. There's no way to set them directly.

5. Players

KeyTypeRequiredNotes
idUUIDYesUnique within the pack.
team_idUUIDYesMust match a team's id in the same pack.
first_name / last_nameStringYes
positionStringYesOne of C, LW, RW, LD, RD, G.
yearStringYesOne of Freshman, Sophomore, Junior, Senior. There is no "Graduate" year in the schema — author a grad player as a Senior; extra eligibility is granted only through in-game mechanics.
ageIntYes16–25.
archetypeStringYesMust match the player's position group — forwards (C/LW/RW) take sniper, playmaker, powerForward, twoWayForward, or grinder; defensemen (LD/RD) take offensiveDefenseman, defensiveDefenseman, or twoWayDefenseman; goalies (G) take butterfly, hybrid, or acrobatic. A mismatch isn't rejected by the validator but reads oddly on the profile.
overallIntYes40–99. Current skill rating.
potentialIntYes40–99. Development ceiling; should be ≥ overall (not enforced).
attributesObjectYesSix sub-ratings — skating, shooting, passing, physical, defending, goaltending — each 1–99. All six are required even for a position that doesn't use one of them (a forward still needs a low goaltending value like 110).
jerseyIntNo1–98. Outside that range, or omitted, is treated as unset — the app auto-assigns a collision-free number.
height_inches / weight_poundsIntNo
birth_dateStringNoISO yyyy-MM-dd.
hometownStringNo"City, ST" or "City, Country". Drives the profile map pin and recruiting-region assignment when present; procedurally generated when absent.
last_teamStringNoJunior/feeder club label shown on the profile (e.g. "Muskegon (USHL)").
statsObjectNoPrior-season real stat line — see §6.
Data packs do not author coaches or staff. Every coach and support-staff member — yours and every AI opponent's — is generated by the app at import time from each team's prestige and region. A pack only ever supplies conferences, teams, and players.
Data packs never receive synthetic pre-existing Pro Draft rights. The built-in league seeds a realistic slice of its best players with backdated draft rights on day one (see Pro Draft & Turning Pro) — a custom pack skips that step entirely, so every imported player starts undrafted regardless of overall. There's no schema field for it; a pack has no way to author it either.

6. The stats object — one prior season only

A pack can only carry a single prior-season stat line per player, and it's optional. When present, it's surfaced on the player's profile as their one season of pre-import history and seeds their career totals — it does not create multiple season rows.

KeyApplies toNotes
games_playedBoth
goals, assists, plus_minus, penalty_minutes, shots_on_goal, power_play_goals, shorthanded_goals, game_winning_goalsSkatersLeft at 0 for goalies.
wins, losses, ties, saves, goals_allowed, shutouts, minutes_playedGoaliesLeft at 0 for skaters.
team_nameBoth (optional)Attributes this stat line to a different team than the player's current team_id — use this for a transferred player whose real prior season was played elsewhere.

Every field defaults to 0 and every field is present on every player's stat object regardless of position. The season label shown on the profile is derived from the pack's start_year: with "start_year": 2025, a player's stats line is dated the 2024-25 season.

7. Independent Conferences

An independent grouping (teams that don't belong to any conference) schedules games and is ranked in the national index like any other conference, and crowns its own postseason champion, but grants no automatic National Tournament bid — its teams can only reach the tournament via an at-large bid.

Mark a conference as independent with "is_independent": true. If omitted, the app still infers independence when the conference's name contains "independent" (any case) or its resolved abbreviation is exactly "IND" — a custom-named independent grouping should set the flag explicitly to avoid surprises. Regardless of tournament shape, an independent conference can never grant an automatic bid — is_independent always wins over any grants_auto_bid you set.

Independent conferences default to a 5-team, top-3-bye, neutral-site single-elimination bracket with a consolation round unless overridden. The engine currently schedules an independent grouping the same way it schedules a real conference (games are drawn from within the group) — expect intra-group scheduling rather than bespoke non-conference scheduling.

8. Team Composition & Roster Rules

RuleValueEnforced how
Minimum skaters per team12Import fails validation below this.
Minimum goalies per team2Import fails validation below this.
Positional depth floorC: 4 · LW: 4 · RW: 4 · LD: 3 · RD: 3 · G: 2 (20 total)Not a hard import requirement — a thinner roster is silently backfilled with generated walk-ons so the lineup screen never shows blank slots.
Practical roster sizeGenerated teams target 34 players (20F/11D/3G); the app allows 22–38.Not something a pack must hit exactly — a thin pack gets backfilled, an over-38 pack isn't rejected.

In short: the validator only guarantees a team is viable to field a lineup (12 skaters + 2 goalies). Hitting the positional minimums exactly and aiming for a realistic ~34-man roster is a quality bar for your pack, not something the importer blocks on.

9. Abbreviation Derivation

If a team or conference omits abbreviation, the app derives one from name:

Supply an explicit abbreviation any time the derived one would be ambiguous (two teams deriving to the same code) or just isn't the code you want.

10. Conference Tournament Format

By default, a conference's postseason format falls back to a generic bracket sized to its team count. To model a real conference's actual format, author it one of two ways — inline wins over a named preset; either wins over the default.

Valid preset keys: big_ten, ccha, nchc, ecac, hockey_east, atlantic_hockey, uchc.

An inline spec looks like:

"tournament": {
  "field_size": 8,
  "bye_count": 0,
  "grants_auto_bid": true,
  "rounds": [
    { "label": "Quarterfinals", "wins_required": 2, "venue": "higher_seed_hosts", "seeding": "bracket_advance" },
    { "label": "Semifinals",    "wins_required": 1, "venue": "neutral_site",       "seeding": "reseed_by_standing" },
    { "label": "Championship",  "wins_required": 1, "venue": "neutral_site",       "seeding": "reseed_by_standing" }
  ]
}
KeyTypeRequiredDefault
field_sizeIntNonull/omitted = every conference team qualifies.
bye_countIntNo0
grants_auto_bidBoolNotrue (forced to false if the conference is independent).
roundsArrayYesOrdered first round → final.
rounds[].labelStringNoAuto-derived from bracket position ("Championship", "Semifinals", "Quarterfinals", "First Round", "Opening Round", or "Round N").
rounds[].wins_requiredIntNo1 (single game); 2 = best-of-three. Clamped to 1–4 on import regardless of what you author.
rounds[].venueStringNo"higher_seed_hosts" (default) or "neutral_site".
rounds[].seedingStringNo"bracket_advance" (fixed bracket, default) or "reseed_by_standing".

Whatever shape you author is validated against the conference's actual team count on import: if it doesn't cleanly fit, the round count is regenerated for the real field while preserving each round's authored flavor as closely as possible. A pack can never produce a broken bracket, even with a wrong team count.

11. Referential Integrity & Uniqueness

The importer runs these checks, in order, and reports every failure at once so you can fix a pack top-to-bottom in one pass:

A pack that fails validation is never partially imported — it's all-or-nothing. Malformed JSON that doesn't even reach the validator (a missing key, wrong type, corrupted syntax) is reported separately, naming the specific key/path Swift's decoder choked on.

On-disk storage is keyed by the pack's stable ID (derived from pack_name), not by a hand-slugged filename — two differently-named packs can never collide, and re-importing a pack with the same name always updates the same file rather than creating a duplicate.

12. Importing a Pack

From the Data Packs screen (main menu → Data Packs):

Either path decodes the JSON, runs the full validation pass above, and — only if it passes — saves a local copy and adds it to your pack list. You can then mark any imported pack as the default, which pre-selects it the next time you start a new game.

13. Pro Teams (Draft Destinations)

Every dynasty runs a fictionalized Pro Draft (see Pro Draft & Turning Pro) where your best college players get selected by one of 32 fictional pro teams. By default those 32 names come from the game's own built-in list (real city names, invented team names — no real league's trademark). A pack can supply pro_teams instead, so players drafted out of your custom league land on team names that fit your world:

"pro_teams": [
  "Boston Schooners", "Toronto Skyliners", "New York Statesmen", "..."
]

See Also