diff --git a/gradle.properties b/gradle.properties index 6ef13b5..4980d3c 100755 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ group=net.gensokyoreimagined.nitori -version=1.3-SNAPSHOT +version=1.4-SNAPSHOT description=Converting patches into mixins, for the Ignite Framework org.gradle.parallel=true diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/MixinIdentifier.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/MixinIdentifier.java new file mode 100644 index 0000000..069f6ad --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/alloc/MixinIdentifier.java @@ -0,0 +1,34 @@ +package net.gensokyoreimagined.nitori.mixin.alloc; + +import net.minecraft.resources.ResourceLocation; +import org.spongepowered.asm.mixin.Final; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Overwrite; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.Unique; + +@Mixin(ResourceLocation.class) +public class MixinIdentifier { + + @Shadow @Final + private String namespace; + + @Shadow @Final + private String path; + + @Unique + private String nitori$cachedString = null; + + /** + * @author ishland + * @reason cache toString + */ + @Overwrite + public String toString() { + if (this.nitori$cachedString != null) return this.nitori$cachedString; + final String s = this.namespace + ":" + this.path; + this.nitori$cachedString = s; + return s; + } + +} \ No newline at end of file diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/MixinMob.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/micro_opts/MixinMob.java similarity index 97% rename from src/main/java/net/gensokyoreimagined/nitori/mixin/MixinMob.java rename to src/main/java/net/gensokyoreimagined/nitori/mixin/entity/micro_opts/MixinMob.java index 3a638b9..2862a5e 100644 --- a/src/main/java/net/gensokyoreimagined/nitori/mixin/MixinMob.java +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/entity/micro_opts/MixinMob.java @@ -12,7 +12,7 @@ // // You should have received a copy of the GNU General Public License // along with this program. If not, see . -package net.gensokyoreimagined.nitori.mixin; +package net.gensokyoreimagined.nitori.mixin.entity.micro_opts; import net.minecraft.core.BlockPos; import net.minecraft.world.entity.EntityType; diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/virtual_thread/DirectVirtualThreadService.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/virtual_thread/DirectVirtualThreadService.java new file mode 100644 index 0000000..cfd6fdf --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/virtual_thread/DirectVirtualThreadService.java @@ -0,0 +1,50 @@ +// Gale - virtual thread support + +package net.gensokyoreimagined.nitori.mixin.virtual_thread; + +import org.jetbrains.annotations.NotNull; + +import java.util.concurrent.ThreadFactory; + +/** + * An implementation of {@link VirtualThreadService} that can create virtual threads directly. + * + * @author Martijn Muijsers + */ +final class DirectVirtualThreadService extends VirtualThreadService { + + private DirectVirtualThreadService() { + super(); + } + + @Override + public @NotNull ThreadFactory createFactory() { + // Disabled until Minecraft requires servers to have a Java version that can read class files compiled with functionality from Java 19+ on preview / Java 21+ on stable + throw new UnsupportedOperationException(); +// return Thread.ofVirtual().factory(); + } + + @Override + public @NotNull Thread start(@NotNull Runnable task) { + // Disabled until Minecraft requires servers to have a Java version that can read class files compiled with functionality from Java 19+ on preview / Java 21+ on stable + throw new UnsupportedOperationException(); +// Objects.requireNonNull(task, "The task to start a virtual thread cannot be null"); +// return Thread.ofVirtual().start(task); + } + + /** + * @return A functional {@link DirectVirtualThreadService}. + * @throws Throwable If creating virtual threads directly is not supported by the current runtime. + * This could be any {@link Throwable}, including an {@link Exception} or an {@link Error}. + */ + static @NotNull DirectVirtualThreadService create() throws Throwable { + // Disabled until Minecraft requires servers to have a Java version that can read class files compiled with functionality from Java 19+ on preview / Java 21+ on stable + throw new UnsupportedOperationException(); +// var service = new DirectVirtualThreadService(); +// // Run some tests to verify +// service.runTest(); +// // If we end up here, it works +// return service; + } + +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/virtual_thread/ReflectionVirtualThreadService.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/virtual_thread/ReflectionVirtualThreadService.java new file mode 100644 index 0000000..a5b0cf6 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/virtual_thread/ReflectionVirtualThreadService.java @@ -0,0 +1,76 @@ +// Gale - virtual thread support + +package net.gensokyoreimagined.nitori.mixin.virtual_thread; + +import org.jetbrains.annotations.NotNull; + +import java.lang.reflect.Method; +import java.util.Objects; +import java.util.concurrent.ThreadFactory; + +/** + * An implementation of {@link VirtualThreadService} that can create virtual threads using Java reflection. + * + * @author Martijn Muijsers + */ +final class ReflectionVirtualThreadService extends VirtualThreadService { + + /** + * The {@link Thread}#ofVirtual() method. + */ + private final @NotNull Method Thread_ofVirtual_method; + + /** + * The {@link Thread}.Builder#factory() method. + */ + private final @NotNull Method Thread_Builder_factory_method; + + /** + * The {@link Thread}.Builder#start(Runnable) method. + */ + private final @NotNull Method Thread_Builder_start_method; + + private ReflectionVirtualThreadService() throws Throwable { + this.Thread_ofVirtual_method = Objects.requireNonNull(Thread.class.getMethod("ofVirtual")); + // The Thread.Builder class + var Thread_Builder_class = Objects.requireNonNull(Class.forName("java.lang.Thread$Builder")); + this.Thread_Builder_factory_method = Objects.requireNonNull(Thread_Builder_class.getMethod("factory")); + this.Thread_Builder_start_method = Objects.requireNonNull(Thread_Builder_class.getMethod("start", Runnable.class)); + } + + @Override + public @NotNull ThreadFactory createFactory() { + try { + return (ThreadFactory) this.Thread_Builder_factory_method.invoke(this.Thread_ofVirtual_method.invoke(null)); + } catch (Exception e) { + // This should not be possible because it was tested in create() + throw new RuntimeException(e); + } + } + + @Override + public @NotNull Thread start(@NotNull Runnable task) { + Objects.requireNonNull(task, "The task to start a virtual thread cannot be null"); + try { + return (Thread) this.Thread_Builder_start_method.invoke(this.Thread_ofVirtual_method.invoke(null), task); + } catch (Exception e) { + // This should not be possible because it was tested in create() + throw new RuntimeException(e); + } + } + + /** + * @return A functional {@link ReflectionVirtualThreadService}. + * @throws Throwable If creating virtual threads via reflection is not supported by the current runtime. + * This could be any {@link Throwable}, including an {@link Exception} or an {@link Error}. + */ + static @NotNull ReflectionVirtualThreadService create() throws Throwable { + // This will already throw something if the reflection fails + var service = new ReflectionVirtualThreadService(); + // Run some tests to verify + service.runTest(); + // If we end up here, it works + return service; + } + +} diff --git a/src/main/java/net/gensokyoreimagined/nitori/mixin/virtual_thread/VirtualThreadService.java b/src/main/java/net/gensokyoreimagined/nitori/mixin/virtual_thread/VirtualThreadService.java new file mode 100644 index 0000000..903f803 --- /dev/null +++ b/src/main/java/net/gensokyoreimagined/nitori/mixin/virtual_thread/VirtualThreadService.java @@ -0,0 +1,101 @@ +// Gale - virtual thread support + +package net.gensokyoreimagined.nitori.mixin.virtual_thread; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.concurrent.ThreadFactory; + +/** + * An abstract service to create virtual threads. + * + * @author Martijn Muijsers + */ +public sealed abstract class VirtualThreadService permits ReflectionVirtualThreadService, DirectVirtualThreadService { + + /** + * @return A {@link ThreadFactory} that produces virtual threads. + */ + public abstract @NotNull ThreadFactory createFactory(); + + /** + * @param task The runnable for the thread to execute. + * @return A virtual thread that has been started with the given task. + */ + public abstract @NotNull Thread start(Runnable task); + + /** + * Runs a test on the {@link #createFactory} and {@link #start} methods, + * which certainly throws some {@link Throwable} if something goes wrong. + */ + protected void runTest() throws Throwable { + // This will definitely throw something if it doesn't work + try { + this.start(() -> {}).join(); + } catch (InterruptedException ignored) {} // Except InterruptedException, we don't care about that one + try { + var thread = this.createFactory().newThread(() -> {}); + thread.start(); + thread.join(); + } catch (InterruptedException ignored) {} // Except InterruptedException, we don't care about that one + // If we end up here, it works + } + + private static boolean initialized = false; + + /** + * The {@link VirtualThreadService} for the current runtime, + * or null if virtual threads are not supported, or if not {@link #initialized} yet. + */ + private static @Nullable VirtualThreadService implementation; + + /** + * @return Whether virtual threads are supported on the current runtime. + */ + public static boolean isSupported() { + return get() != null; + } + + /** + * @return The {@link VirtualThreadService} for the current runtime, + * or null if virtual threads are not {@linkplain #isSupported() supported}. + * + * This method is thread-safe only after the first time it has been fully run. + */ + public static @Nullable VirtualThreadService get() { + if (!initialized) { + initialized = true; + try { + implementation = DirectVirtualThreadService.create(); + } catch (Throwable ignored) { + try { + implementation = ReflectionVirtualThreadService.create(); + } catch (Throwable ignored2) {} + } + } + return implementation; + } + + /** + * The minimum major version of Java that is known to support using virtual threads + * (although possibly behind a feature preview flag). + */ + public static final int minimumJavaMajorVersionWithFeaturePreview = 19; + + /** + * The minimum major version of Java that is known to support using virtual threads + * even without any feature preview flags. + */ + public static final int minimumJavaMajorVersionWithoutFeaturePreview = 21; + + public static int getJavaMajorVersion() { + var version = System.getProperty("java.version"); + if (version.startsWith("1.")) { + return version.charAt(2) - '0'; + } + int dotIndex = version.indexOf("."); + return Integer.parseInt(dotIndex == -1 ? version : version.substring(0, dotIndex)); + } + +} diff --git a/src/main/resources/ignite.mod.json b/src/main/resources/ignite.mod.json index f3c5000..c1b1897 100755 --- a/src/main/resources/ignite.mod.json +++ b/src/main/resources/ignite.mod.json @@ -1,6 +1,6 @@ { "id": "Nitori", - "version": "1.3-SNAPSHOT", + "version": "1.4-SNAPSHOT", "mixins": [ "mixins.core.json" ] diff --git a/src/main/resources/mixins.core.json b/src/main/resources/mixins.core.json index 94f354a..da5967f 100755 --- a/src/main/resources/mixins.core.json +++ b/src/main/resources/mixins.core.json @@ -16,7 +16,7 @@ "MixinGameRules", "MixinIteratorSafeOrderedReferenceSet", "MixinLevelStorageAccess", - "MixinMob", + "entity.micro_opts.MixinMob", "MixinNoiseBasedChunkGenerator", "MixinPlayer", "MixinPlayerList", @@ -31,6 +31,7 @@ "alloc.composter.ComposterMixin$ComposterBlockDummyInventoryMixin", "alloc.composter.ComposterMixin$ComposterBlockFullComposterInventoryMixin", "alloc.biome_temprature_leak.Biome_threadLocalMixin", + "alloc.MixinIdentifier", "cached_hashcode.BlockNeighborGroupMixin", "collections.attributes.AttributeContainerMixin", "collections.block_entity_tickers.WorldChunkMixin",