Skip to content

Commit f061267

Browse files
committed
Minecraft: noun
WIP work towards source transforms, featuring MinecraftDefinition
1 parent f68f21e commit f061267

20 files changed

+442
-220
lines changed

build.gradle.kts

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,23 +14,25 @@ repositories {
1414
minecraft.versions()
1515
}
1616

17-
val mcVer = "1.21.5"
17+
val mc by minecraft.creating {
18+
version = "1.21.5"
19+
}
1820

1921
dependencies {
20-
implementation(minecraft.of {
21-
client()
22-
version(mcVer)
23-
})
22+
println("adding dependencies")
23+
mc.transformer(fileTree("src/main/resources/transformers"))
24+
25+
implementation(mc.dependency)
2426

2527
compileOnly(cichlid.api("0.3.2"))
2628
cichlidRuntime(cichlid.runtime("0.3.2"))
2729
}
2830

2931
cichlid {
3032
runs {
31-
register("client") {
32-
version = mcVer
33-
}
33+
// register("client") {
34+
// version = mcVer
35+
// }
3436
// register("client 1.21.4") {
3537
// version = "1.21.4"
3638
// template = "client"

gradle.properties

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
org.gradle.jvmargs = -Xmx8G
22
org.gradle.caching=true
33
org.gradle.configuration-cache=true
4-
cichlid.use_project_cache=true
4+
#org.gradle.unsafe.isolated-projects=true
5+
cichlid.use_local_cache=true

gradle/wrapper/gradle-wrapper.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
distributionBase=GRADLE_USER_HOME
22
distributionPath=wrapper/dists
3-
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-rc-3-bin.zip
3+
distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip
44
networkTimeout=10000
55
validateDistributionUrl=true
66
zipStoreBase=GRADLE_USER_HOME

plugin/src/main/java/fish/cichlidmc/cichlid_gradle/CichlidGradlePlugin.java

Lines changed: 98 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,34 @@
33
import fish.cichlidmc.cichlid_gradle.cache.CichlidCache;
44
import fish.cichlidmc.cichlid_gradle.cache.mcmaven.McMavenConnectorFactory;
55
import fish.cichlidmc.cichlid_gradle.extension.CichlidExtension;
6+
import fish.cichlidmc.cichlid_gradle.extension.def.MinecraftDefinition;
7+
import fish.cichlidmc.cichlid_gradle.extension.def.MinecraftDefinitionImpl;
68
import fish.cichlidmc.cichlid_gradle.extension.dep.CichlidDepsExtension;
7-
import fish.cichlidmc.cichlid_gradle.extension.dep.MinecraftDepsExtension;
89
import fish.cichlidmc.cichlid_gradle.extension.repo.CichlidReposExtension;
910
import fish.cichlidmc.cichlid_gradle.extension.repo.MinecraftReposExtension;
1011
import fish.cichlidmc.cichlid_gradle.run.RunTaskGeneration;
12+
import fish.cichlidmc.cichlid_gradle.util.Utils;
13+
import org.gradle.api.Action;
14+
import org.gradle.api.NamedDomainObjectContainer;
1115
import org.gradle.api.NamedDomainObjectProvider;
1216
import org.gradle.api.Plugin;
1317
import org.gradle.api.Project;
18+
import org.gradle.api.artifacts.Configuration;
1419
import org.gradle.api.artifacts.ConfigurationContainer;
20+
import org.gradle.api.artifacts.Dependency;
1521
import org.gradle.api.artifacts.DependencyScopeConfiguration;
22+
import org.gradle.api.artifacts.ModuleIdentifier;
23+
import org.gradle.api.internal.artifacts.cache.ArtifactResolutionControl;
24+
import org.gradle.api.internal.artifacts.cache.ModuleResolutionControl;
25+
import org.gradle.api.internal.artifacts.configurations.ResolutionStrategyInternal;
26+
import org.gradle.api.internal.artifacts.ivyservice.resolutionstrategy.DefaultCachePolicy;
1627
import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
28+
import org.jetbrains.annotations.Nullable;
1729

1830
import javax.inject.Inject;
31+
import java.lang.reflect.Field;
32+
import java.util.List;
33+
import java.util.concurrent.TimeUnit;
1934

2035
public class CichlidGradlePlugin implements Plugin<Project> {
2136
public static final String NAME = "CichlidGradle";
@@ -36,16 +51,17 @@ public void apply(Project project) {
3651
CichlidReposExtension.setup(project);
3752
MinecraftReposExtension.setup(project);
3853
CichlidDepsExtension.setup(project);
39-
MinecraftDepsExtension.setup(project);
4054
RunTaskGeneration.setup(project);
55+
MinecraftDefinition.setup(project);
4156

42-
ConfigurationContainer configurations = project.getConfigurations();
43-
setupConfigurations(configurations);
57+
setupConfigurations(project);
4458

45-
McMavenConnectorFactory.inject(this.repositoryTransportFactory, CichlidCache.get(project));
59+
McMavenConnectorFactory.inject(this.repositoryTransportFactory, project);
4660
}
4761

48-
private static void setupConfigurations(ConfigurationContainer configurations) {
62+
private static void setupConfigurations(Project project) {
63+
ConfigurationContainer configurations = project.getConfigurations();
64+
4965
// configurations for Cichlid.
5066
// you need one configuration for declaring dependencies and another extending it for resolution
5167
NamedDomainObjectProvider<DependencyScopeConfiguration> cichlidRuntime = configurations.dependencyScope("cichlidRuntime");
@@ -54,5 +70,81 @@ private static void setupConfigurations(ConfigurationContainer configurations) {
5470

5571
// add Cichlid and its dependencies to the runtime classpath
5672
configurations.named("runtimeClasspath", runtime -> runtime.extendsFrom(cichlidRuntime.get()));
73+
74+
// minecraft dependencies are set as changing so the current transformers are always applied.
75+
// we need to set its cache duration to 0, but gradle API only allows you to do that per-configuration.
76+
// it's possible with some internals though.
77+
configurations.configureEach(CichlidGradlePlugin::disableMinecraftCaching);
78+
79+
// if we try to resolve transformers for the first time in the mcmaven, an error
80+
// is thrown because the current thread is already resolving a configuration,
81+
// so we need to resolve them right before they're actually needed.
82+
83+
NamedDomainObjectContainer<MinecraftDefinition> defs = MinecraftDefinition.getExtension(project);
84+
85+
// for each configuration, right before resolution:
86+
// - check if any dependencies are Minecraft
87+
// - if so, find the corresponding definition
88+
// - if it exists, resolve its transformers
89+
configurations.configureEach(configuration -> configuration.getIncoming().beforeResolve(resolvable -> {
90+
for (Dependency dep : resolvable.getDependencies()) {
91+
if (!isMinecraft(dep))
92+
continue;
93+
94+
String defName = dep.getVersion();
95+
if (defName == null)
96+
continue;
97+
98+
MinecraftDefinitionImpl def = (MinecraftDefinitionImpl) defs.findByName(defName);
99+
if (def == null)
100+
continue;
101+
102+
// call iterator to make sure they're actually resolved
103+
def.resolvableTransformers().getIncoming().getFiles().iterator();
104+
}
105+
}));
106+
}
107+
108+
private static void disableMinecraftCaching(Configuration configuration) {
109+
try {
110+
ResolutionStrategyInternal strategy = (ResolutionStrategyInternal) configuration.getResolutionStrategy();
111+
DefaultCachePolicy policy = (DefaultCachePolicy) strategy.getCachePolicy();
112+
113+
Field moduleCacheRulesField = DefaultCachePolicy.class.getDeclaredField("moduleCacheRules");
114+
moduleCacheRulesField.setAccessible(true);
115+
Field artifactCacheRulesField = DefaultCachePolicy.class.getDeclaredField("artifactCacheRules");
116+
artifactCacheRulesField.setAccessible(true);
117+
118+
List<Action<? super ModuleResolutionControl>> moduleCacheRules = Utils.get(moduleCacheRulesField, policy);
119+
List<Action<? super ArtifactResolutionControl>> artifactCacheRules = Utils.get(artifactCacheRulesField, policy);
120+
121+
// add to the ends of the lists to get final say.
122+
// gradle conveniently adds to the front.
123+
124+
moduleCacheRules.add(control -> {
125+
if (isMinecraft(control.getRequest().getModule())) {
126+
control.cacheFor(0, TimeUnit.SECONDS);
127+
}
128+
});
129+
artifactCacheRules.add(control -> {
130+
if (isMinecraft(control.getRequest().getId().getComponentIdentifier().getModuleIdentifier())) {
131+
control.cacheFor(0, TimeUnit.SECONDS);
132+
}
133+
});
134+
} catch (Throwable t) {
135+
throw new RuntimeException("Error accessing Gradle internals! You probably need to update Gradle, CichlidGradle, or both", t);
136+
}
137+
}
138+
139+
private static boolean isMinecraft(ModuleIdentifier id) {
140+
return isMinecraft(id.getGroup(), id.getName());
141+
}
142+
143+
private static boolean isMinecraft(Dependency dep) {
144+
return isMinecraft(dep.getGroup(), dep.getName());
145+
}
146+
147+
private static boolean isMinecraft(@Nullable String group, @Nullable String module) {
148+
return CichlidCache.MINECRAFT_GROUP.equals(group) && CichlidCache.MINECRAFT_MODULE.equals(module);
57149
}
58150
}

plugin/src/main/java/fish/cichlidmc/cichlid_gradle/cache/CichlidCache.java

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@
66
import fish.cichlidmc.pistonmetaparser.FullVersion;
77
import fish.cichlidmc.pistonmetaparser.manifest.Version;
88
import org.gradle.api.Project;
9+
import org.gradle.api.invocation.Gradle;
910

1011
import java.nio.file.Path;
1112
import java.util.Objects;
12-
import java.util.Set;
1313

1414
/**
1515
* Interface for Cichlid's global Minecraft cache. Holds no state, just interfaces with the filesystem.
@@ -37,13 +37,10 @@
3737
* </ul>
3838
*/
3939
public final class CichlidCache {
40-
// pattern is relative to root
4140
public static final String MINECRAFT_GROUP = "net.minecraft";
42-
public static final Set<String> MINECRAFT_MODULES = Set.of(
43-
"minecraft-client", "minecraft-server", "minecraft-merged", "minecraft-bundler"
44-
);
41+
public static final String MINECRAFT_MODULE = "minecraft";
4542

46-
public static final String PROJECT_CACHE_PROPERTY = "cichlid.use_project_cache";
43+
public static final String LOCAL_CACHE_PROPERTY = "cichlid.use_local_cache";
4744
public static final int FORMAT = 1;
4845
public static final String PATH = "cichlid-gradle-cache/v" + FORMAT;
4946

@@ -94,10 +91,11 @@ public static Path getPath(Project project) {
9491
}
9592

9693
private static Path getGradleCache(Project project) {
97-
if (Objects.equals(project.findProperty(PROJECT_CACHE_PROPERTY), "true")) {
98-
return project.file(".gradle").toPath();
94+
Gradle gradle = project.getGradle();
95+
if (Objects.equals(project.findProperty(LOCAL_CACHE_PROPERTY), "true")) {
96+
return gradle.getRootProject().file(".gradle").toPath();
9997
} else {
100-
return project.getGradle().getGradleUserHomeDir().toPath().resolve("caches");
98+
return gradle.getGradleUserHomeDir().toPath().resolve("caches");
10199
}
102100
}
103101
}
Lines changed: 47 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
package fish.cichlidmc.cichlid_gradle.cache.mcmaven;
22

33
import fish.cichlidmc.cichlid_gradle.cache.CichlidCache;
4+
import fish.cichlidmc.cichlid_gradle.extension.def.MinecraftDefinition;
5+
import org.gradle.api.InvalidUserDataException;
6+
import org.gradle.api.NamedDomainObjectContainer;
7+
import org.gradle.api.Project;
48
import org.gradle.api.internal.artifacts.repositories.transport.RepositoryTransportFactory;
59
import org.gradle.authentication.Authentication;
610
import org.gradle.internal.resource.connector.ResourceConnectorFactory;
@@ -9,21 +13,26 @@
913
import org.gradle.internal.resource.transfer.ExternalResourceConnector;
1014

1115
import java.lang.reflect.Field;
16+
import java.util.HashSet;
1217
import java.util.List;
1318
import java.util.Set;
1419

1520
public final class McMavenConnectorFactory implements ResourceConnectorFactory {
16-
public static final Set<String> PROTOCOLS = Set.of(MinecraftMaven.PROTOCOL);
21+
public final String protocol;
22+
private final ExternalResourceConnector connector;
1723

18-
private final CichlidCache cache;
19-
20-
public McMavenConnectorFactory(CichlidCache cache) {
21-
this.cache = cache;
24+
public McMavenConnectorFactory(String protocol, MinecraftMaven mcMaven) {
25+
this.protocol = protocol;
26+
this.connector = new DefaultExternalResourceConnector(
27+
new McMavenResourceAccessor(mcMaven),
28+
NoOpResourceLister.INSTANCE,
29+
NoOpUploader.INSTANCE
30+
);
2231
}
2332

2433
@Override
2534
public Set<String> getSupportedProtocols() {
26-
return PROTOCOLS;
35+
return Set.of(this.protocol);
2736
}
2837

2938
@Override
@@ -33,27 +42,45 @@ public Set<Class<? extends Authentication>> getSupportedAuthentication() {
3342

3443
@Override
3544
public ExternalResourceConnector createResourceConnector(ResourceConnectorSpecification connectionDetails) {
36-
return new DefaultExternalResourceConnector(
37-
new McMavenResourceAccessor(this.cache),
38-
NoOpResourceLister.INSTANCE,
39-
NoOpUploader.INSTANCE
40-
);
45+
return this.connector;
4146
}
4247

4348
// built-in implementations use ServiceLoader, but that only works for Gradle modules.
44-
public static void inject(RepositoryTransportFactory factory, CichlidCache cache) {
49+
// a new one is registered for each project anyway. Each has a unique protocol based on project path.
50+
public static void inject(RepositoryTransportFactory factory, Project project) {
51+
List<ResourceConnectorFactory> list = extractFactories(factory);
52+
Set<String> protocols = collectProtocols(list);
53+
54+
String protocol = MinecraftMaven.createProtocol(project);
55+
if (protocols.contains(protocol)) {
56+
throw new InvalidUserDataException("Multiple projects generate the URI protocol '" + protocol + "', including " + project.getPath());
57+
}
58+
59+
CichlidCache cache = CichlidCache.get(project);
60+
NamedDomainObjectContainer<MinecraftDefinition> defs = MinecraftDefinition.getExtension(project);
61+
MinecraftMaven mcMaven = new MinecraftMaven(defs, cache);
62+
63+
list.add(new McMavenConnectorFactory(protocol, mcMaven));
64+
}
65+
66+
@SuppressWarnings("unchecked")
67+
private static List<ResourceConnectorFactory> extractFactories(RepositoryTransportFactory factory) {
4568
try {
4669
Field registeredProtocols = RepositoryTransportFactory.class.getDeclaredField("registeredProtocols");
4770
registeredProtocols.setAccessible(true);
48-
@SuppressWarnings("unchecked")
49-
List<ResourceConnectorFactory> list = (List<ResourceConnectorFactory>) registeredProtocols.get(factory);
50-
// don't add if it already exists
51-
if (list.stream().anyMatch(f -> f instanceof McMavenConnectorFactory))
52-
return;
53-
54-
list.add(new McMavenConnectorFactory(cache));
55-
} catch (ReflectiveOperationException e) {
71+
return (List<ResourceConnectorFactory>) registeredProtocols.get(factory);
72+
} catch (ReflectiveOperationException | ClassCastException e) {
5673
throw new RuntimeException("Error accessing Gradle internals! You probably need to update Gradle, CichlidGradle, or both", e);
5774
}
5875
}
76+
77+
private static Set<String> collectProtocols(List<ResourceConnectorFactory> factories) {
78+
Set<String> protocols = new HashSet<>();
79+
for (ResourceConnectorFactory factory : factories) {
80+
if (factory instanceof McMavenConnectorFactory mcMavenFactory) {
81+
protocols.add(mcMavenFactory.protocol);
82+
}
83+
}
84+
return protocols;
85+
}
5986
}

plugin/src/main/java/fish/cichlidmc/cichlid_gradle/cache/mcmaven/McMavenResourceAccessor.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
package fish.cichlidmc.cichlid_gradle.cache.mcmaven;
22

3-
import fish.cichlidmc.cichlid_gradle.cache.CichlidCache;
43
import fish.cichlidmc.cichlid_gradle.util.FileUtils;
54
import org.gradle.api.resources.ResourceException;
65
import org.gradle.internal.hash.HashCode;
@@ -22,8 +21,8 @@
2221
public final class McMavenResourceAccessor implements ExternalResourceAccessor {
2322
private final MinecraftMaven mcMaven;
2423

25-
public McMavenResourceAccessor(CichlidCache cache) {
26-
this.mcMaven = new MinecraftMaven(cache);
24+
public McMavenResourceAccessor(MinecraftMaven mcMaven) {
25+
this.mcMaven = mcMaven;
2726
}
2827

2928
@Nullable

0 commit comments

Comments
 (0)