Skip to content

Commit dbbf9d8

Browse files
committed
Add initial support for Hyxin mods!
1 parent afaa109 commit dbbf9d8

14 files changed

Lines changed: 232 additions & 16 deletions

File tree

dev/src/main/groovy/com/fox2code/hypertale/dev/HypertaleIntellijIDEASupport.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@
3333

3434
final class HypertaleIntellijIDEASupport {
3535
private static final String[] ideaAddDictionary = new String[]{
36-
"hypixel", "hytale", "earlyplugins", "hypertale", "clinit", "mixin", "mixins",
37-
"Hypixel's", "Hytale's", "Hypertale's", "Mixin's"
36+
"hypixel", "hytale", "earlyplugins", "hypertale", "clinit", "mixin", "mixins", "hyxin",
37+
"Hypixel's", "Hytale's", "Hypertale's", "Mixin's", "Hyxin's"
3838
};
3939

4040
static void installIdeaDictionaryOnIDEASync(Project target, Iterable<String> words) throws IOException {

gradle.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ org.gradle.parallel=true
44
org.gradle.jvmargs=-Xmx1024m -XX:-UseGCOverheadLimit -Dfile.encoding=UTF-8
55
org.gradle.java.installations.auto-download=true
66
# Hypertale properties
7-
hypertale.version=1.1.1
7+
hypertale.version=1.2.0-dev
88
hypertale.init=1
99

1010
# https://plugins.gradle.org/plugin/net.ltgt.errorprone
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2026 Fox2Code
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package com.build_9.hyxin;
25+
26+
import com.fox2code.hypertale.launcher.EarlyLogger;
27+
28+
/**
29+
* Stub of Hyxin's {@code Constants} class, used to help improve compatibility with Hyxin mods!
30+
*/
31+
public class Constants {
32+
public static void log(String message) {
33+
EarlyLogger.logRaw("Hyxin: " + message);
34+
}
35+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2026 Fox2Code
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package com.build_9.hyxin;
25+
26+
import com.fox2code.hypertale.loader.HypertaleModGatherer;
27+
import org.objectweb.asm.ClassReader;
28+
29+
import java.io.FileNotFoundException;
30+
import java.io.IOException;
31+
import java.io.InputStream;
32+
33+
/**
34+
* Stub of Hyxin's {@code LaunchEnvironment} class, used to help improve compatibility with Hyxin mods!
35+
*/
36+
public final class LaunchEnvironment {
37+
private static final LaunchEnvironment INSTANCE = new LaunchEnvironment();
38+
39+
private LaunchEnvironment() {}
40+
41+
public static LaunchEnvironment get() {
42+
return INSTANCE;
43+
}
44+
45+
public static void create(ClassLoader systemLoader, ClassLoader earlyPluginLoader) {
46+
throw new IllegalStateException("Cannot call create() on Hypertale!");
47+
}
48+
49+
public void captureRuntimeLoader(ClassLoader loader) {
50+
throw new IllegalStateException("Cannot call captureRuntimeLoader() on Hypertale!");
51+
}
52+
53+
public ClassLoader getSystemLoader() {
54+
return HypertaleModGatherer.class.getClassLoader();
55+
}
56+
57+
public ClassLoader getEarlyPluginLoader() {
58+
return HypertaleModGatherer.class.getClassLoader();
59+
}
60+
61+
public ClassLoader getRuntimeLoader() {
62+
return HypertaleModGatherer.class.getClassLoader();
63+
}
64+
65+
public ClassLoader findLoaderForClass(String resourceName) throws ClassNotFoundException {
66+
try {
67+
return this.findLoaderFor(resourceName.replace(".", "/").concat(".class"));
68+
} catch (IOException var3) {
69+
throw new ClassNotFoundException("Could not find class '" + resourceName + "'.", var3);
70+
}
71+
}
72+
73+
public ClassLoader findLoaderFor(String resourceName) throws IOException {
74+
ClassLoader mainClassLoader = HypertaleModGatherer.class.getClassLoader();
75+
if (mainClassLoader != null && mainClassLoader.getResource(resourceName) != null) {
76+
return mainClassLoader;
77+
} else {
78+
throw new FileNotFoundException("Could not find resource '" + resourceName + "' on any class loader.");
79+
}
80+
}
81+
82+
public InputStream findResourceStream(String resourceName) throws IOException {
83+
return this.findLoaderFor(resourceName).getResourceAsStream(resourceName);
84+
}
85+
86+
public ClassReader getClassReader(String name) throws IOException, ClassNotFoundException {
87+
String fileName = name.replace(".", "/").concat(".class");
88+
try (InputStream inputStream = this.findResourceStream(fileName)) {
89+
return new ClassReader(inputStream);
90+
} catch (RuntimeException var3) {
91+
throw new ClassNotFoundException("Could not find class '" + fileName + "'.");
92+
}
93+
}
94+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2026 Fox2Code
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
package com.build_9.hyxin.mixin;
25+
26+
import org.spongepowered.asm.mixin.MixinEnvironment;
27+
import org.spongepowered.asm.mixin.transformer.IMixinTransformer;
28+
29+
/**
30+
* Stub of Hyxin's {@code MixinService} class, used to help improve compatibility with Hyxin mods!
31+
*/
32+
public class MixinService {
33+
public static IMixinTransformer transformer;
34+
35+
public static void changePhase(MixinEnvironment.Phase phase) {}
36+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/**
2+
* Stub package to improve compatibility with Hyxin mods.
3+
*/
4+
package com.build_9.hyxin.mixin;
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/**
2+
* Stub package to improve compatibility with Hyxin mods.
3+
*/
4+
package com.build_9.hyxin;

launcher/src/main/java/com/fox2code/hypertale/launcher/Main.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ static void main(String[] args) throws IOException, InterruptedException {
6161
HypertaleAgent.tryLoadEarlyAgent();
6262
System.setProperty("rellatsnI.tnega.yddubetyb.ten", HypertaleAgent.class.getName());
6363
if (Boolean.getBoolean("hypertale.gradleInit")) {
64+
System.setProperty("mixin.hotSwap", "true");
6465
System.setProperty("hypertale.initMethod", "gradle");
6566
} else if (!Boolean.getBoolean("hypertale.useInitWrapper")) {
6667
System.setProperty("hypertale.initMethod", "direct");

launcher/src/main/java/com/fox2code/hypertale/launcher/PatchHelper.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import com.fox2code.hypertale.patcher.mixin.MixinLoader;
2929
import org.objectweb.asm.ClassReader;
3030
import org.objectweb.asm.tree.ClassNode;
31+
import org.spongepowered.tools.agent.MixinAgent;
3132

3233
import java.lang.instrument.ClassFileTransformer;
3334
import java.lang.instrument.Instrumentation;
@@ -72,6 +73,7 @@ public byte[] transform(ClassLoader loader, String className, Class<?> classBein
7273
return classWriter.toByteArray();
7374
}
7475
});
76+
MixinAgent.init(instrumentation);
7577
} else if (initCLSetClassTransformer != null) {
7678
EarlyLogger.log("Using late transformer for patching!");
7779
if (useMixins) {

launcher/src/main/java/com/fox2code/hypertale/loader/HypertaleModGatherer.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,19 @@ public final class HypertaleModGatherer {
4545
private final int modHash;
4646
private final List<ClassPathModCandidate> classPathManifests;
4747
private final boolean usingMixins;
48+
private final boolean hasHyxin;
4849

4950
private HypertaleModGatherer(List<File> hypertaleMods, List<File> mods, List<File> libraries,
5051
File modSyncBootstrap, int modHash, List<ClassPathModCandidate> classPathManifests,
51-
boolean usingMixins) {
52+
boolean usingMixins, boolean hasHyxin) {
5253
this.hypertaleMods = hypertaleMods;
5354
this.mods = mods;
5455
this.libraries = libraries;
5556
this.modSyncBootstrap = modSyncBootstrap;
5657
this.modHash = modHash;
5758
this.classPathManifests = classPathManifests;
5859
this.usingMixins = usingMixins;
60+
this.hasHyxin = hasHyxin;
5961
}
6062

6163
public List<File> getHypertaleMods() {
@@ -92,7 +94,7 @@ public static HypertaleModGatherer gatherModsDev() {
9294

9395
public static HypertaleModGatherer gatherMods(String[] args) {
9496
// TODO: Process launch arguments
95-
boolean[] useMixins = new boolean[]{false};
97+
boolean[] useMixins = new boolean[]{false, false};
9698
ArrayList<File> mods = new ArrayList<>();
9799
ArrayList<File> hypertaleMods = new ArrayList<>();
98100
ArrayList<File> libraries = new ArrayList<>();
@@ -111,7 +113,8 @@ public static HypertaleModGatherer gatherMods(String[] args) {
111113
return new HypertaleModGatherer(Collections.unmodifiableList(hypertaleMods),
112114
Collections.unmodifiableList(mods), Collections.unmodifiableList(libraries),
113115
modSyncBootstrap, Arrays.hashCode(fileSizes),
114-
Collections.unmodifiableList(gatherClassPathMods(useMixins)), useMixins[0]);
116+
Collections.unmodifiableList(gatherClassPathMods(useMixins)),
117+
useMixins[0], useMixins[1]);
115118
}
116119

117120
private static List<ClassPathModCandidate> gatherClassPathMods(boolean[] useMixins) {
@@ -140,7 +143,7 @@ private static List<ClassPathModCandidate> gatherClassPathMods(boolean[] useMixi
140143
try (InputStream inputStream = url.openStream()) {
141144
byte[] manifestData = IOUtils.readAllBytes(inputStream);
142145
String modInfo = new String(manifestData, StandardCharsets.UTF_8);
143-
if (modInfo.contains("\"HypertaleMixinConfig\"")) {
146+
if (modInfo.contains("\"HypertaleMixinConfig\"") || modInfo.contains("\"Hyxin\"")) {
144147
useMixins[0] = true;
145148
}
146149
}
@@ -178,6 +181,11 @@ private static void appendMods(ArrayList<File> hypertaleMods, ArrayList<File> mo
178181
"com/fox2code/hypertale/init/Main.class") != null) {
179182
continue; // <- Do not consider HypertaleInit as a mod!
180183
}
184+
if (zipFile.getEntry(
185+
"com/build_9/hyxin/mixin/MixinService.class") != null) {
186+
useMixins[1] = true;
187+
continue; // <- We already implement Hyxin APIs
188+
}
181189
ZipEntry manifestEntry;
182190
if ((manifestEntry = zipFile.getEntry("manifest.json")) != null) {
183191
String modInfo;
@@ -186,7 +194,7 @@ private static void appendMods(ArrayList<File> hypertaleMods, ArrayList<File> mo
186194
} catch (RuntimeException _) {
187195
modInfo = "";
188196
}
189-
if (modInfo.contains("\"HypertaleMixinConfig\"")) {
197+
if (modInfo.contains("\"HypertaleMixinConfig\"") || modInfo.contains("\"Hyxin\"")) {
190198
useMixins[0] = true;
191199
}
192200
if (modInfo.contains("\"Hypertale")) {

0 commit comments

Comments
 (0)