This guide is for contributors who want to add a new community mirror to HyPrism's default set — the mirrors auto-generated on first launch via MirrorLoaderService.GetDefaultMirrors().
For mirror schema reference, URL placeholders, version discovery methods, and
diffBasedBranchessemantics, see the User Mirrors Guide. This page only covers the contributor workflow.
Before adding a mirror to the defaults, ensure:
- The mirror is publicly available and reliable (reasonable uptime).
- The mirror operator has agreed to be included in HyPrism.
- You've verified that the mirror serves valid
.pwrfiles matching official Hytale patches. - You've determined which source type fits the mirror:
patternorjson-index(see User Mirrors Guide — Source types).
MirrorLoaderService.cs ← Generates defaults + loads *.mirror.json
↓ creates
JsonMirrorSource.cs ← Universal IVersionSource for all mirrors
↑ implements
IVersionSource.cs ← Unified interface (official + mirrors)
↑ used by
VersionService.cs ← Orchestrates all sources, caches results
Key files:
| File | Purpose |
|---|---|
Services/Game/Sources/MirrorLoaderService.cs |
Default mirror definitions (GetDefaultMirrors()), loading logic |
Services/Game/Sources/JsonMirrorSource.cs |
Handles both pattern and json-index sources |
Services/Game/Sources/IVersionSource.cs |
Interface + supporting types |
Models/MirrorMeta.cs |
Data model for .mirror.json schema |
You do not need to create a new C# class per mirror. All mirrors use JsonMirrorSource.
Determine:
| Question | Example |
|---|---|
| Mirror ID (unique, lowercase, no spaces) | mymirror |
| Display name | MyMirror |
| URL structure for full builds | https://cdn.example.com/{os}/{arch}/{branch}/0/{version}.pwr |
| URL structure for diff patches (if available) | https://cdn.example.com/{os}/{arch}/{branch}/{from}/{to}.pwr |
| How to discover available versions | JSON API / HTML autoindex / known static list |
| Does it use non-standard branch/OS names in URLs? | prerelease instead of pre-release, mac instead of darwin |
| Which branches have only diffs (no full builds)? | [] or ["pre-release"] |
| A URL to ping for availability checks | https://cdn.example.com/health |
Open Services/Game/Sources/MirrorLoaderService.cs and add a new entry to the list returned by GetDefaultMirrors().
For a pattern-based mirror:
// MyMirror — json-api based
new() {
SchemaVersion = 1,
Id = "mymirror",
Name = "MyMirror",
Description = "Community mirror hosted by MyMirror (cdn.example.com)",
Priority = 103, // Next available after existing mirrors
Enabled = true,
SourceType = "pattern",
Pattern = new MirrorPatternConfig
{
FullBuildUrl = "{base}/{os}/{arch}/{branch}/0/{version}.pwr",
DiffPatchUrl = "{base}/{os}/{arch}/{branch}/{from}/{to}.pwr",
BaseUrl = "https://cdn.example.com/hytale",
VersionDiscovery = new VersionDiscoveryConfig
{
Method = "json-api",
Url = "{base}/api/versions?branch={branch}&os={os}&arch={arch}",
JsonPath = "versions"
},
DiffBasedBranches = new List<string>()
},
SpeedTest = new MirrorSpeedTestConfig
{
PingUrl = "https://cdn.example.com/health"
},
Cache = new MirrorCacheConfig
{
IndexTtlMinutes = 30,
SpeedTestTtlMinutes = 60
}
}For a json-index-based mirror:
// MyIndexMirror — single API index
new() {
SchemaVersion = 1,
Id = "myindexmirror",
Name = "MyIndexMirror",
Description = "Community mirror hosted by MyIndexMirror (api.example.com)",
Priority = 104,
Enabled = true,
SourceType = "json-index",
JsonIndex = new MirrorJsonIndexConfig
{
ApiUrl = "https://api.example.com/index",
RootPath = "hytale",
Structure = "grouped", // or "flat"
PlatformMapping = new Dictionary<string, string>
{
["darwin"] = "mac" // only if the API uses non-standard names
},
FileNamePattern = new FileNamePatternConfig
{
Full = "v{version}-{os}-{arch}.pwr",
Diff = "v{from}~{to}-{os}-{arch}.pwr"
},
DiffBasedBranches = new List<string>()
},
SpeedTest = new MirrorSpeedTestConfig
{
PingUrl = "https://api.example.com/index"
},
Cache = new MirrorCacheConfig
{
IndexTtlMinutes = 30,
SpeedTestTtlMinutes = 60
}
}Existing default priorities:
| Mirror Type | Priority |
|---|---|
| HTML Autoindex style | 100 |
| JSON API style | 101 |
| JSON Index style | 102 |
Use the next available integer (e.g., 103). Lower = higher priority. Official Hytale is always 0.
If the mirror uses non-standard names in URLs or API keys:
// Branch name differs from internal "pre-release"
BranchMapping = new Dictionary<string, string>
{
["pre-release"] = "prerelease"
},
// OS name differs from internal "darwin"
OsMapping = new Dictionary<string, string>
{
["darwin"] = "macos"
},
// Platform key differs in json-index API
PlatformMapping = new Dictionary<string, string>
{
["darwin"] = "mac"
}# Must compile without errors
dotnet build
# Launch and verify your mirror appears in logs
dotnet run
# Look for: "Loaded mirror: MyMirror (mymirror) [priority=103, type=pattern]"Manual testing checklist:
- Launcher starts without errors
- Mirror appears in
MirrorLoaderlog output -
<data-dir>/Mirrors/mymirror.mirror.jsonis generated on fresh launch (deleteMirrors/folder first to test) - Version list loads for
releasebranch - Version list loads for
pre-releasebranch - Full build download URL resolves correctly (open in browser)
- Diff patch URL resolves correctly (if
diffPatchUrlis configured) - Generated JSON file is valid and uses UTF-8 (no
\uXXXXescapes for readable characters)
Update the following files:
| File | What to add |
|---|---|
Docs/English/User/Mirrors.md |
Add annotated example in "Built-in mirror examples" section |
Docs/Russian/User/Mirrors.md |
Same in Russian |
You don't need to update the schema reference — it's generic and covers all mirrors.
PR checklist:
-
MirrorLoaderService.cs— new entry inGetDefaultMirrors() -
dotnet buildpasses with 0 errors, 0 warnings - Tested with a fresh
Mirrors/folder (deleted and regenerated) - Mirror verified on at least one platform (Linux, Windows, or macOS)
- English user docs updated (
Docs/English/User/Mirrors.md) - Russian user docs updated (
Docs/Russian/User/Mirrors.md) - Mirror operator's permission acknowledged in PR description
If you leave this unset (null), it defaults to an empty list, which is usually correct. Only add branches here if the mirror genuinely has no full builds for that branch.
See User Mirrors Guide — diffBasedBranches for details.
The jsonPath field in VersionDiscoveryConfig supports exactly three formats:
| jsonPath | API response format |
|---|---|
"$root" |
[1, 2, 3] |
"versions" |
{"versions": [1, 2, 3]} |
"items[].version" |
{"items": [{"version": 1}, ...]} |
Other JSON path expressions are not supported. If the mirror's API doesn't match any of these, you'll need to extend JsonMirrorSource.ParseVersionsFromJson().
The regex in htmlPattern must be a valid .NET regex. Test it against the actual HTML response. Common issues:
- Double-escaped quotes in C# strings (use
@""verbatim strings) - Different HTML structure between nginx, Apache, and other web servers
MirrorLoaderService uses JavaScriptEncoder.UnsafeRelaxedJsonEscaping to avoid \uXXXX escapes in generated files. Don't change this — it keeps generated .mirror.json files human-readable.
If a new mirror requires capabilities not covered by the existing schema (e.g., authentication, custom protocol), you may need to:
- Add new fields to
MirrorMeta/MirrorPatternConfig/MirrorJsonIndexConfiginModels/MirrorMeta.cs - Handle them in
JsonMirrorSource.cs - Bump
SchemaVersionif the change is breaking - Update the user docs schema reference
- Ensure backward compatibility — old
.mirror.jsonfiles without new fields must still work (use nullable types with defaults)