Thank you for your interest in contributing! This guide covers the architecture, build process, and conventions specific to HyperProtect-Mixin.
- Java 21+ (source and target compatibility)
- Gradle (wrapper included)
- Hytale Server (Early Access) with
--accept-early-pluginsflag - Hyxin early plugin loader (v0.0.11+)
- A consumer mod for testing hooks (e.g., HyperFactions)
HyperProtect-Mixin is a Hyxin-based early plugin that injects protection checkpoints into the Hytale server via bytecode mixins.
Early Plugin Lifecycle → Hyxin Mixin Injection → Bridge Initialization
↓
Consumer Mod (e.g., HyperFactions) → Registers Hook Objects at Slot Indices
↓
Server Event Occurs → Mixin Interceptor Fires → Bridge Reads Hook → Verdict Returned
| Component | Location | Purpose |
|---|---|---|
ProtectionBridge |
bridge/ |
AtomicReferenceArray<Object>(24) stored in System.getProperties() — the cross-classloader bridge |
HookSlot |
bridge/ |
Eagerly-cached MethodHandle wrapper for hook implementations |
FaultReporter |
bridge/ |
Sampled error reporter (logs first + every 100th to prevent flooding) |
ChatFormatter |
msg/ |
&-code message formatter with hex color support |
| Interceptors | intercept/ |
Mixin classes organized by category (building, items, combat, etc.) |
- No compile-time dependency — consumers use
System.getProperties().get("hyperprotect.bridge")to access the bridge - Reflection-only contracts — hook methods are resolved via
MethodHandleat runtime - One hook per slot — last
bridge.set(index, ...)wins - Fail-open — errors and missing hooks always allow the action
HyperProtect-Mixin builds a plain JAR (no shadow/relocation — mixin JARs don't bundle dependencies):
./gradlew jarOutput: build/libs/HyperProtect-Mixin-<version>.jar
Dependencies (all compileOnly):
HytaleServer.jar— resolved fromlibs/Hyxin-0.0.11-all.jar— resolved fromlibs/org.jetbrains:annotations:24.1.0
- Copy the built JAR to
earlyplugins/(NOTmods/) - Ensure Hyxin is also in
earlyplugins/ - Add
--accept-early-pluginsto your server start script - Start the server and check logs for:
HyperProtect-Mixin loaded!Protection hooks: block-break, block-place, explosion, ...
- Verify system properties are set:
hyperprotect.bridge.active=truehyperprotect.intercept.*properties for each interceptor
- Test with a consumer mod (e.g., HyperFactions) to verify hooks fire correctly
In ProtectionBridge.java, add a new constant:
public static final int my_hook = 23; // Next available indexIf the array size needs to increase, update SLOT_COUNT.
Create a new class in the appropriate intercept/ subdirectory:
@Mixin(TargetClass.class)
public class MyHookInterceptor {
@Unique private static final FaultReporter FAULTS = new FaultReporter("MyHookInterceptor");
@Unique private static volatile HookSlot cachedSlot;
@Unique private static final MethodType EVALUATE_TYPE = MethodType.methodType(
int.class, UUID.class, String.class, int.class, int.class, int.class);
@Unique private static final MethodType FETCH_REASON_TYPE = MethodType.methodType(
String.class, UUID.class, String.class, int.class, int.class, int.class);
static {
System.setProperty("hyperprotect.intercept.my_hook", "true");
}
// ... resolveSlot(), formatReason(), and @Inject method
}Add the class to hyperprotect.mixin.json:
"com.hyperprotect.mixin.intercept.category.MyHookInterceptor"- Add the hook to
docs/hooks.mdwith method signatures and usage - Add the slot to
docs/getting-started.mdslot table - Add an example to
docs/examples.md - Add the system property to
docs/feature-detection.md
- Use
@Uniquefor all static fields (avoids mixin class conflicts) - Use volatile
HookSlotcaching with identity check onimpl() - Always wrap hook invocations in try/catch with
FAULTS.report(t)— fail-open - Set
InteractionState.Failed+ci.cancel()for denied block interactions - Set system property in
static {}initializer for feature detection
- Java 21 features — records, sealed classes, pattern matching where appropriate
@NotNull/@Nullableannotations on public API methodsConcurrentHashMapfor shared mutable state- No raw types — always use generics
- Conventional naming —
evaluateXfor primary methods,fetchXDenyReasonfor deny reason methods
Follow Conventional Commits:
feat:— new hook, interceptor, or bridge featurefix:— bug fixdocs:— documentation changesrefactor:— code restructuring without behavior changechore:— build, dependency, or tooling updates
Examples:
feat: add hammer block cycling interceptor
fix: resolve race condition in HookSlot caching
docs: add respawn hook documentation
refactor: extract common interceptor pattern to base utility
main— stable releasesfeat/*— new hooks and featuresfix/*— bug fixes
- Bugs: Use the Bug Report template
- Feature Requests: Use the Feature Request template
- Questions: Join the HyperSystems Discord