Skip to content

Commit 0d64f45

Browse files
committed
Port the bulk of the fabric adapter
1 parent 21b0a74 commit 0d64f45

35 files changed

+2640
-240
lines changed

LICENSE

Lines changed: 674 additions & 121 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 3 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,7 @@
1-
# Legacy Fabric Template Mod
1+
# Astraea Spark
22

3-
- [Quick start guide](#quick-start-guide)
4-
- [Introduction to the folder structure](#introduction-to-the-folder-structure)
5-
- [Creating your mod](#creating-your-mod)
6-
- [Useful gradle commands](#useful-gradle-commands)
7-
- [More info](#more-info)
8-
- [License](#license)
9-
10-
## Quick start guide
11-
12-
### Introduction to the folder structure
13-
14-
**Build files:**
15-
16-
| File | Description |
17-
|-----------------------| -------------------------------------------------------- |
18-
| `build.gradle.kts` | Configures the compilation process. |
19-
| `gradle.properties` | Contains properties for Minecraft, fabric, and your mod. |
20-
| `settings.gradle.kts` | Configures the plugin repositories. |
21-
22-
**Fabric files:**
23-
24-
These files are located at `src/main/resources`.
25-
26-
| File | Description | Additional information |
27-
|----------------------------| ---------------------------------------- | --------------------------------------------------------------------------------------------------------------------- |
28-
| `fabric.mod.json` | Contains metadata about your mod. | [wiki:fabric_mod_json_spec](https://fabricmc.net/wiki/documentation:fabric_mod_json_spec) |
29-
| `template-mod.mixins.json` | Contains a list of all your mixin files. | [wiki:mixin_registration](https://fabricmc.net/wiki/tutorial:mixin_registration) |
30-
| `assets/template/icon.png` | The icon of your mod. | [wiki:fabric_mod_json_spec#icon](https://fabricmc.net/wiki/documentation:fabric_mod_json_spec?s[]=icon#custom_fields) |
31-
32-
33-
### Creating your mod
34-
35-
First of you must replace all occurrences of `template-mod` with the id of your mod.
36-
37-
If your mod doesn't use mixins you can safely remove the mixin entry in your `fabric.mod.json` as well as delete any `*.mixin.json` files.
38-
39-
This template has the legacy fabric api included in its build script, more info about the api can be found at it's [github repo](https://github.com/Legacy-Fabric/fabric).
40-
If you know what you are doing you can also safely remove the api from the build script as it isn't required.
41-
42-
### Useful gradle commands
43-
44-
```sh
45-
# Compile your mod
46-
./gradlew build
47-
48-
# Remove old build files
49-
./gradlew clean
50-
51-
# Generate Minecraft sources
52-
./gradlew genSourcesWithVineflower
53-
54-
# Launch a modded Minecraft client
55-
./gradlew runClient
56-
57-
# Kill gradle if it's doing stupid things
58-
./gradlew --stop
59-
```
60-
61-
## More info
62-
63-
Additional tutorials and tips can be found in the [wiki](https://github.com/Legacy-Fabric/fabric-example-mod/wiki).
64-
65-
For more detailed setup instructions please see the [fabric wiki](https://fabricmc.net/wiki/tutorial:setup).
66-
67-
If you are new to fabric or Minecraft modding in general then [this wiki page](https://fabricmc.net/wiki/tutorial:primer) may help you.
3+
A Legacy Fabric adapter for the Spark performance profiler.
684

695
## License
706

71-
This template is available under the CC0 license. Feel free to learn from it and incorporate it in your own projects.
7+
Astraea Spark is a fork of [spark-fabric](https://github.com/lucko/spark/tree/master/spark-fabric) licensed under the [GNU GPLv3 license](./LICENSE).

build.gradle.kts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,10 @@ base {
1010
}
1111

1212
repositories {
13+
maven {
14+
name = "Lucko Maven"
15+
url = uri("https://nexus.lucko.me/repository/maven-snapshots/")
16+
}
1317
// Add repositories to retrieve artifacts from in here.
1418
// You should only use this when depending on other mods because
1519
// Loom adds the essential maven repositories to download Minecraft and libraries from automatically.
@@ -40,10 +44,16 @@ dependencies {
4044
// As of September 2024, available only for intermediary generation 1.
4145
modImplementation("net.legacyfabric.legacy-fabric-api:legacy-fabric-api:${properties["fabric_version"] as String}")
4246

47+
implementation("me.lucko:spark-common:1.10.119-SNAPSHOT")
48+
4349
// You can retrieve a specific api module using this notation.
4450
// modImplementation(legacy.apiModule("legacy-fabric-item-groups-v1", properties["fabric_version"] as String))
4551
}
4652

53+
loom {
54+
accessWidenerPath = file("src/main/resources/astraea-spark.accesswidener")
55+
}
56+
4757
tasks {
4858
processResources {
4959
val properties = mapOf(

src/main/java/com/example/template/TemplateMod.java

Lines changed: 0 additions & 14 deletions
This file was deleted.

src/main/java/com/example/template/mixin/ExampleMixin.java

Lines changed: 0 additions & 15 deletions
This file was deleted.
Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
/*
2+
* This file is part of spark.
3+
*
4+
* Copyright (c) lucko (Luck) <[email protected]>
5+
* Copyright (c) contributors
6+
*
7+
* This program is free software: you can redistribute it and/or modify
8+
* it under the terms of the GNU General Public License as published by
9+
* the Free Software Foundation, either version 3 of the License, or
10+
* (at your option) any later version.
11+
*
12+
* This program is distributed in the hope that it will be useful,
13+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15+
* GNU General Public License for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* along with this program. If not, see <http://www.gnu.org/licenses/>.
19+
*/
20+
21+
package io.github.astraea.spark;
22+
23+
import com.google.common.collect.ImmutableMap;
24+
import io.github.astraea.spark.smap.MixinUtils;
25+
import io.github.astraea.spark.smap.SourceMap;
26+
import io.github.astraea.spark.smap.SourceMapProvider;
27+
import me.lucko.spark.common.sampler.source.ClassSourceLookup;
28+
import me.lucko.spark.common.util.classfinder.ClassFinder;
29+
import net.fabricmc.loader.api.FabricLoader;
30+
import net.fabricmc.loader.api.ModContainer;
31+
import org.objectweb.asm.Type;
32+
import org.spongepowered.asm.mixin.FabricUtil;
33+
import org.spongepowered.asm.mixin.extensibility.IMixinConfig;
34+
import org.spongepowered.asm.mixin.transformer.Config;
35+
import org.spongepowered.asm.mixin.transformer.meta.MixinMerged;
36+
37+
import java.lang.reflect.Method;
38+
import java.net.URI;
39+
import java.nio.file.Path;
40+
import java.util.Collection;
41+
import java.util.Map;
42+
43+
public class LegacyFabricClassSourceLookup extends ClassSourceLookup.ByCodeSource {
44+
private final ClassFinder classFinder;
45+
private final SourceMapProvider smapProvider;
46+
private final Path modsDirectory;
47+
private final Map<String, String> pathToModMap;
48+
49+
public LegacyFabricClassSourceLookup(ClassFinder classFinder) {
50+
this.classFinder = classFinder;
51+
this.smapProvider = new SourceMapProvider();
52+
53+
FabricLoader loader = FabricLoader.getInstance();
54+
this.modsDirectory = loader.getGameDir().resolve("mods").toAbsolutePath().normalize();
55+
this.pathToModMap = constructPathToModIdMap(loader.getAllMods());
56+
}
57+
58+
@Override
59+
public String identifyFile(Path path) {
60+
String id = this.pathToModMap.get(path.toAbsolutePath().normalize().toString());
61+
if (id != null) {
62+
return id;
63+
}
64+
65+
if (!path.startsWith(this.modsDirectory)) {
66+
return null;
67+
}
68+
69+
return super.identifyFileName(this.modsDirectory.relativize(path).toString());
70+
}
71+
72+
@Override
73+
public String identify(MethodCall methodCall) throws Exception {
74+
String className = methodCall.getClassName();
75+
String methodName = methodCall.getMethodName();
76+
String methodDesc = methodCall.getMethodDescriptor();
77+
78+
if (className.equals("native") || methodName.equals("<init>") || methodName.equals("<clinit>")) {
79+
return null;
80+
}
81+
82+
Class<?> clazz = this.classFinder.findClass(className);
83+
if (clazz == null) {
84+
return null;
85+
}
86+
87+
Class<?>[] params = getParameterTypesForMethodDesc(methodDesc);
88+
Method reflectMethod = clazz.getDeclaredMethod(methodName, params);
89+
90+
MixinMerged mixinMarker = reflectMethod.getDeclaredAnnotation(MixinMerged.class);
91+
if (mixinMarker == null) {
92+
return null;
93+
}
94+
95+
return modIdFromMixinClass(mixinMarker.mixin());
96+
}
97+
98+
@Override
99+
public String identify(MethodCallByLine methodCall) throws Exception {
100+
String className = methodCall.getClassName();
101+
String methodName = methodCall.getMethodName();
102+
int lineNumber = methodCall.getLineNumber();
103+
104+
if (className.equals("native") || methodName.equals("<init>") || methodName.equals("<clinit>")) {
105+
return null;
106+
}
107+
108+
SourceMap smap = this.smapProvider.getSourceMap(className);
109+
if (smap == null) {
110+
return null;
111+
}
112+
113+
int[] inputLineInfo = smap.getReverseLineMapping().get(lineNumber);
114+
if (inputLineInfo == null || inputLineInfo.length == 0) {
115+
return null;
116+
}
117+
118+
for (int fileInfoIds : inputLineInfo) {
119+
SourceMap.FileInfo inputFileInfo = smap.getFileInfo().get(fileInfoIds);
120+
if (inputFileInfo == null) {
121+
continue;
122+
}
123+
124+
String path = inputFileInfo.path;
125+
if (path.endsWith(".java")) {
126+
path = path.substring(0, path.length() - 5);
127+
}
128+
129+
String possibleMixinClassName = path.replace('/', '.');
130+
if (possibleMixinClassName.equals(className)) {
131+
continue;
132+
}
133+
134+
return modIdFromMixinClass(possibleMixinClassName);
135+
}
136+
137+
return null;
138+
}
139+
140+
private static String modIdFromMixinClass(String mixinClassName) {
141+
for (Config config : MixinUtils.getMixinConfigs().values()) {
142+
IMixinConfig mixinConfig = config.getConfig();
143+
String mixinPackage = mixinConfig.getMixinPackage();
144+
if (!mixinPackage.isEmpty() && mixinClassName.startsWith(mixinPackage)) {
145+
return mixinConfig.getDecoration(FabricUtil.KEY_MOD_ID);
146+
}
147+
}
148+
return null;
149+
}
150+
151+
private Class<?>[] getParameterTypesForMethodDesc(String methodDesc) {
152+
Type methodType = Type.getMethodType(methodDesc);
153+
Class<?>[] params = new Class[methodType.getArgumentTypes().length];
154+
Type[] argumentTypes = methodType.getArgumentTypes();
155+
156+
for (int i = 0, argumentTypesLength = argumentTypes.length; i < argumentTypesLength; i++) {
157+
Type argumentType = argumentTypes[i];
158+
params[i] = getClassFromType(argumentType);
159+
}
160+
161+
return params;
162+
}
163+
164+
private Class<?> getClassFromType(Type type) {
165+
switch (type.getSort()) {
166+
case Type.VOID: return void.class;
167+
case Type.BOOLEAN: return boolean.class;
168+
case Type.CHAR: return char.class;
169+
case Type.BYTE: return byte.class;
170+
case Type.SHORT: return short.class;
171+
case Type.INT: return int.class;
172+
case Type.FLOAT: return float.class;
173+
case Type.LONG: return long.class;
174+
case Type.DOUBLE: return double.class;
175+
case Type.ARRAY: {
176+
final Class<?> classFromType = getClassFromType(type.getElementType());
177+
Class<?> result = classFromType;
178+
if (classFromType != null) {
179+
for (int i = 0; i < type.getDimensions(); i++) {
180+
result = result.arrayType();
181+
}
182+
}
183+
return result;
184+
}
185+
case Type.OBJECT: return this.classFinder.findClass(type.getClassName());
186+
default: return null;
187+
}
188+
}
189+
190+
private static Map<String, String> constructPathToModIdMap(Collection<ModContainer> mods) {
191+
ImmutableMap.Builder<String, String> builder = ImmutableMap.builder();
192+
for (ModContainer mod : mods) {
193+
String modId = mod.getMetadata().getId();
194+
if (modId.equals("java")) {
195+
continue;
196+
}
197+
198+
for (Path path : mod.getRootPaths()) {
199+
URI uri = path.toUri();
200+
if (uri.getScheme().equals("jar") && path.toString().equals("/")) { // ZipFileSystem
201+
String zipFilePath = path.getFileSystem().toString();
202+
builder.put(zipFilePath, modId);
203+
} else {
204+
builder.put(path.toAbsolutePath().normalize().toString(), modId);
205+
}
206+
207+
}
208+
}
209+
return builder.build();
210+
}
211+
}

0 commit comments

Comments
 (0)