Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ forge*changelog.txt

# Cubic Chunks generated files
cubicchunks.mixins.*.json
cubicchunks.dasm.json

# Dasm
.dasm.out
27 changes: 18 additions & 9 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
import io.github.opencubicchunks.gradle.FixAnnotationsFormatterStep
import io.github.opencubicchunks.gradle.GeneratePackageInfo
import org.gradle.api.tasks.testing.logging.TestExceptionFormat

buildscript {
dependencies {
Expand All @@ -18,6 +17,7 @@ plugins {
id("io.github.opencubicchunks.javaheaders").version("1.2.8")
id("io.github.opencubicchunks.gradle.mcGitVersion")
id("io.github.opencubicchunks.gradle.mixingen")
id("io.github.opencubicchunks.gradle.dasmgen")
}

version = mod_version
Expand Down Expand Up @@ -67,7 +67,16 @@ tasks.register("generatePackageInfo") {

tasks.register("genAll") {
group = "filegen"
dependsOn(generatePackageInfo, generateMixinConfigs)
dependsOn(generatePackageInfo, generateMixinConfigs, generateDasmConfigs)
}

dasmGen {
filePattern = "%s.dasm.json"
defaultMinVersion = "0.8"

config(sourceSets.main, "cubicchunks") {
it.packageName = "io.github.opencubicchunks.cubicchunks"
}
}

mixinGen {
Expand Down Expand Up @@ -229,10 +238,10 @@ neoForge {
enable()
// Configure which mod is being tested.
// This allows NeoForge to load the test/ classes and resources as belonging to the mod.
testedMod = mods.cubicchunks // <mod name> must match the name in the mods { } block.
// Configure which mods are loaded in the test environment, if the default (all declared mods) is not appropriate.
// This must contain testedMod, and can include other mods as well.
// loadedMods = [mods.<mod name >, mods.<mod name 2>]
testedMod = mods.cubicchunks // <mod name> must match the name in the mods { } block.
// Configure which mods are loaded in the test environment, if the default (all declared mods) is not appropriate.
// This must contain testedMod, and can include other mods as well.
// loadedMods = [mods.<mod name >, mods.<mod name 2>]
}
}

Expand Down Expand Up @@ -269,9 +278,9 @@ dependencies {
targetConfiguration = "testArchivesOutput"
}

libraries("io.github.notstirred:dasm:2.5.4") {
transitive = false
}
compileOnly("io.github.notstirred:dasm:3.2.0:api")
runtimeOnly(jarJar("io.github.notstirred:dasm-neoforge:3.2.0"))

libraries("io.github.opencubicchunks:regionlib:0.63.0-SNAPSHOT")
libraries("org.spongepowered:noise:2.0.0-SNAPSHOT")

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.github.opencubicchunks.gradle;

import java.io.IOException;
import java.io.UncheckedIOException;

import javax.annotation.Nonnull;

import org.gradle.api.Plugin;
import org.gradle.api.Project;
import org.gradle.api.Task;
import org.gradle.api.plugins.JavaPluginConvention;

public class DasmAutoGen implements Plugin<Project> {

@Override public void apply(@Nonnull Project target) {
DasmGenExtension dasmExtension = new DasmGenExtension();
target.getExtensions().add("dasmGen", dasmExtension);
Task generateDasmConfigs = target.getTasks().create("generateDasmConfigs");
generateDasmConfigs.setGroup("filegen");
generateDasmConfigs.doLast(task -> {
JavaPluginConvention convention = Utils.getJavaPluginConvention(target);
try {
dasmExtension.generateFiles(convention);
} catch (IOException e) {
throw new UncheckedIOException(e);
}
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
package io.github.opencubicchunks.gradle;

import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.TRUNCATE_EXISTING;
import static org.apache.tools.ant.util.StringUtils.removePrefix;
import static org.apache.tools.ant.util.StringUtils.removeSuffix;

import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import com.google.gson.stream.JsonWriter;
import org.gradle.api.Action;
import org.gradle.api.file.SourceDirectorySet;
import org.gradle.api.plugins.JavaPluginConvention;
import org.gradle.api.tasks.SourceSet;

@SuppressWarnings("unused")
public class DasmGenExtension {

Pattern pattern = Pattern.compile("(?<indent> *)@(Dasm|TransformFromClass).+? (interface|class|record) (?<name>\\w+)?", Pattern.DOTALL);

private final Map<SourceSet, Map<String, Action<DasmConfig>>> configsBySourceSet = new HashMap<>();

private String filePattern = "dasm.json";

private String defaultMinVersion;

DasmGenExtension() {}

public void setFilePattern(String pattern) {
this.filePattern = pattern;
}

public String getFilePattern() {
return this.filePattern;
}

public String getDefaultMinVersion() {
return defaultMinVersion;
}

public void setDefaultMinVersion(String defaultMinVersion) {
this.defaultMinVersion = defaultMinVersion;
}

public void config(SourceSet sourceSet, String name, Action<DasmConfig> configure) {
configsBySourceSet.computeIfAbsent(sourceSet, s -> new HashMap<>()).put(name, configure);
}

public static class DasmConfig {
private String packageName;
private String minVersion;

public String getPackageName() {
return packageName;
}

public void setPackageName(String packageName) {
this.packageName = packageName;
}

public String getMinVersion() {
return minVersion;
}

public void setMinVersion(String minVersion) {
this.minVersion = minVersion;
}
}

void generateFiles(JavaPluginConvention convention) throws IOException {
convention.getSourceSets().forEach(sourceSet -> {
Map<String, Action<DasmConfig>> configs = configsBySourceSet.get(sourceSet);
if (configs == null) {
return;
}

Set<File> resourcesSet = sourceSet.getResources().getSrcDirs();
Path resources;
try {
resources = resourcesSet.iterator().next().getCanonicalFile().toPath();
} catch (IOException e) {
throw new RuntimeException(e);
}
for (String name : configs.keySet()) {
DasmConfig config = new DasmConfig();
Action<DasmConfig> configure = configs.get(name);
if (defaultMinVersion != null) {
config.minVersion = defaultMinVersion;
}
configure.execute(config);
String fileName = String.format(filePattern, name);

Path path = resources.resolve(fileName);
try {
Files.createDirectories(resources);
try (JsonWriter writer = new JsonWriter(Files.newBufferedWriter(path, CREATE, TRUNCATE_EXISTING))) {
writer.setIndent(" ");
writer.beginObject();
if (config.packageName != null) {
writer.name("package").value(config.packageName);
}
if (config.minVersion != null) {
writer.name("minVersion").value(config.minVersion);
}

writeDasm(convention, sourceSet, name, config, writer);

writer.endObject();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
});
}

private void writeDasm(JavaPluginConvention convention, SourceSet sourceSet, String name, DasmConfig config, JsonWriter writer)
throws IOException {
Set<String> dasmTypes = getDasmClasses(config, sourceSet.getAllJava());

Set<Path> classesSet = new HashSet<>();
Set<Path> interfacesSet = new HashSet<>();

Path prefixPath = Paths.get(config.packageName.replace('.', '/'));

Function<String, String> transform = path -> removePrefix(path.replace(File.separatorChar, '.'), name + ".");

List<String> types = dasmTypes.stream().map(transform).sorted(Comparator.comparing(a -> a.toLowerCase(Locale.ROOT))).toList();

writer.name("dasm").beginArray();
for (String path : types) {
writer.value(path);
}
writer.endArray();
}

private Set<String> getDasmClasses(DasmConfig config, SourceDirectorySet allJava) throws IOException {
System.out.println("GetDasm Classes");
Set<Path> srcPaths = new HashSet<>();
for (File file : allJava.getSrcDirs()) {
Path toPath = file.getCanonicalFile().toPath();
System.out.println("GetDasm " + toPath);

srcPaths.add(toPath);
}

Set<String> dasmTypes = new HashSet<>();
for (File it : allJava) {
Path javaClass = it.getCanonicalFile().toPath();
System.out.println("Class: " + javaClass);
for (Path srcPath : srcPaths) {
if (javaClass.startsWith(srcPath)) {
String relative = srcPath.relativize(javaClass).toString();
if (relative.replace(File.separatorChar, '.').startsWith(config.packageName) && !relative.endsWith("package-info.java")) {
// This is an abomination. Ideally we'd parse .java files with a library
// This approach works fine for dasm on the outer class and its direct children, any other @Dasm annotation will be
// improperly parsed. (Also assumes our indentation is correct in all java files.)
Matcher matcher = pattern.matcher(Files.readString(javaClass));

String clazz = removeSuffix(relative, ".java");
while (matcher.find()) {
String indent = matcher.group("indent");
String name = matcher.group("name");

String path = indent.isEmpty() ? clazz : clazz + "$" + name;

dasmTypes.add(path);
}
}
}
}
}
return dasmTypes;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
implementation-class=io.github.opencubicchunks.gradle.DasmAutoGen
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import javax.annotation.Nullable;

import io.github.notstirred.dasm.api.annotations.Dasm;
import io.github.notstirred.dasm.api.annotations.selector.MethodSig;
import io.github.notstirred.dasm.api.annotations.selector.Ref;
import io.github.notstirred.dasm.api.annotations.transform.TransformFromMethod;
import io.github.opencubicchunks.cc_core.api.CubePos;
Expand Down Expand Up @@ -161,7 +160,7 @@ public boolean inRange(int x, int y, int z) {
&& Math.abs(z - this.viewCenterZ) <= this.cubeRadius;
}

@TransformFromMethod(owner = @Ref(ClientChunkCache.Storage.class), value = @MethodSig("getChunk(I)Lnet/minecraft/world/level/chunk/LevelChunk;"), visibility = PUBLIC)
@TransformFromMethod(owner = @Ref(ClientChunkCache.Storage.class), value = "getChunk(I)Lnet/minecraft/world/level/chunk/LevelChunk;", visibility = PUBLIC)
public native @Nullable LevelCube getChunk(int chunkIndex);

public void dumpChunks(String filePath) {
Expand Down
Loading