├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src └── main │ ├── resources │ ├── assets │ │ └── curium │ │ │ └── icon.png │ ├── curium.accesswidener │ ├── fabric.mod.json │ └── curium.mixins.json │ └── java │ └── me │ └── katie │ └── curium │ ├── impl │ ├── stub │ │ ├── lwjgl │ │ │ └── GLFWErrorCallbackI.java │ │ └── resources │ │ │ └── NopReloadInstance.java │ ├── duck │ │ └── CuriumStateHolder.java │ ├── mixin │ │ ├── core │ │ │ ├── blaze3d │ │ │ │ ├── vertex │ │ │ │ │ ├── VertexBufferMixin.java │ │ │ │ │ ├── VertexConsumerMixin.java │ │ │ │ │ ├── TesselatorMixin.java │ │ │ │ │ └── BufferBuilderMixin.java │ │ │ │ ├── pipeline │ │ │ │ │ ├── MainTargetMixin.java │ │ │ │ │ └── RenderTargetMixin.java │ │ │ │ ├── audio │ │ │ │ │ ├── OggAudioStreamMixin.java │ │ │ │ │ ├── OpenAlUtilMixin.java │ │ │ │ │ ├── SoundBufferMixin.java │ │ │ │ │ ├── ChannelMixin.java │ │ │ │ │ ├── SoundBufferLibraryMixin.java │ │ │ │ │ ├── ListenerMixin.java │ │ │ │ │ └── LibraryMixin.java │ │ │ │ ├── systems │ │ │ │ │ ├── TimerQuery_FrameProfileMixin.java │ │ │ │ │ ├── TimerQueryMixin.java │ │ │ │ │ ├── TimerQuery_TimerQueryLazyLoaderMixin.java │ │ │ │ │ ├── RenderSystem_AutoStorageIndexBufferMixin.java │ │ │ │ │ └── RenderSystemMixin.java │ │ │ │ ├── platform │ │ │ │ │ ├── VideoModeMixin.java │ │ │ │ │ ├── ClipboardManagerMixin.java │ │ │ │ │ ├── TextureUtilMixin.java │ │ │ │ │ ├── GlUtilMixin.java │ │ │ │ │ ├── DebugMemoryUntrackerMixin.java │ │ │ │ │ ├── MonitorMixin.java │ │ │ │ │ ├── MacosUtilMixin.java │ │ │ │ │ ├── GlDebugMixin.java │ │ │ │ │ ├── WindowMixin.java │ │ │ │ │ ├── InputConstants_TypeMixin.java │ │ │ │ │ ├── MemoryTrackerMixin.java │ │ │ │ │ ├── ScreenManagerMixin.java │ │ │ │ │ ├── GLXMixin.java │ │ │ │ │ ├── InputConstantsMixin.java │ │ │ │ │ ├── GlStateManagerMixin.java │ │ │ │ │ └── NativeImageMixin.java │ │ │ │ ├── font │ │ │ │ │ └── TrueTypeGlyphProviderMixin.java │ │ │ │ ├── Blaze3DMixin.java │ │ │ │ └── shaders │ │ │ │ │ └── UniformMixin.java │ │ │ └── minecraft │ │ │ │ ├── KeyboardHandlerAccessor.java │ │ │ │ ├── MouseHandlerMixin.java │ │ │ │ ├── renderer │ │ │ │ ├── culling │ │ │ │ │ └── FrustumMixin.java │ │ │ │ ├── texture │ │ │ │ │ ├── TextureAtlasMixin.java │ │ │ │ │ ├── SpriteContentsMixin.java │ │ │ │ │ └── SpriteLoaderMixin.java │ │ │ │ └── VirtualScreenMixin.java │ │ │ │ ├── MouseHandlerAccessor.java │ │ │ │ ├── screen │ │ │ │ └── ReceivingLevelScreenMixin.java │ │ │ │ ├── font │ │ │ │ └── TrueTypeGlyphProviderBuilderMixin.java │ │ │ │ ├── MainMixin.java │ │ │ │ ├── KeyboardHandlerMixin.java │ │ │ │ ├── OptionsMixin.java │ │ │ │ └── MinecraftMixin.java │ │ ├── performance │ │ │ ├── ParticleEngineMixin.java │ │ │ ├── ModelManagerMixin.java │ │ │ ├── ReloadableResourceManagerMixin.java │ │ │ ├── GameRendererMixin.java │ │ │ └── MinecraftMixin.java │ │ └── reduce_threads │ │ │ ├── MainMixin.java │ │ │ ├── UtilMixin.java │ │ │ ├── SharedConstantsMixin.java │ │ │ ├── SoundEngineExecutorMixin.java │ │ │ └── ConnectionMixin.java │ ├── asm │ │ ├── mixin │ │ │ ├── annotations │ │ │ │ ├── Erase.java │ │ │ │ ├── OverwriteCtor.java │ │ │ │ ├── CustomTransformer.java │ │ │ │ └── StubClass.java │ │ │ ├── helper │ │ │ │ ├── CtorOverwriteHelper.java │ │ │ │ └── StubInternalHelper.java │ │ │ ├── ClassTransformer.java │ │ │ ├── transformers │ │ │ │ ├── StripLwjglLdcTransformer.java │ │ │ │ ├── EraseHandler.java │ │ │ │ ├── StubClassHandler.java │ │ │ │ ├── CustomTransformerHandler.java │ │ │ │ └── OverwriteCtorHandler.java │ │ │ └── CuriumMixinPlugin.java │ │ ├── CuriumASM.java │ │ └── util │ │ │ ├── ASMUtil.java │ │ │ └── KnotLoaderHacks.java │ ├── CuriumProperties.java │ ├── util │ │ ├── UnsafeUtil.java │ │ └── Util.java │ ├── api │ │ ├── FakeMouseImpl.java │ │ ├── CuriumImpl.java │ │ ├── TPSLimiterImpl.java │ │ └── FakeKeyboardImpl.java │ └── CuriumConstants.java │ ├── events │ ├── client │ │ ├── ClientStartedEvent.java │ │ └── TickEvent.java │ └── ClipboardEvent.java │ ├── input │ ├── InputAction.java │ ├── FakeKeyboard.java │ ├── FakeMouse.java │ ├── KeyboardKey.java │ └── InputModifiers.java │ ├── TPSLimiter.java │ └── Curium.java ├── settings.gradle ├── gradle.properties ├── README.md ├── LICENSE ├── .gitignore ├── gradlew.bat └── gradlew /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katietheqt/curium/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/assets/curium/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/katietheqt/curium/HEAD/src/main/resources/assets/curium/icon.png -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | 8 | gradlePluginPortal() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.4.2-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/stub/lwjgl/GLFWErrorCallbackI.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.stub.lwjgl; 2 | 3 | @FunctionalInterface 4 | @SuppressWarnings("unused") 5 | public interface GLFWErrorCallbackI { 6 | void invoke(int error, long description); 7 | } 8 | -------------------------------------------------------------------------------- /src/main/resources/curium.accesswidener: -------------------------------------------------------------------------------- 1 | accessWidener v1 named 2 | 3 | accessible method com/mojang/blaze3d/audio/Channel (I)V 4 | accessible class com/mojang/blaze3d/systems/TimerQuery$TimerQueryLazyLoader 5 | accessible class net/minecraft/client/Options$FieldAccess 6 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/duck/CuriumStateHolder.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.duck; 2 | 3 | import me.katie.curium.impl.api.CuriumImpl; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | public interface CuriumStateHolder { 7 | @NotNull CuriumImpl curium_getState(); 8 | } 9 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/vertex/VertexBufferMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.vertex; 2 | 3 | import com.mojang.blaze3d.vertex.VertexBuffer; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | 6 | @Mixin(VertexBuffer.class) 7 | public class VertexBufferMixin { 8 | 9 | } 10 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/pipeline/MainTargetMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.pipeline; 2 | 3 | import com.mojang.blaze3d.pipeline.MainTarget; 4 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | 7 | @Mixin(MainTarget.class) 8 | @StubClass 9 | public class MainTargetMixin { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/pipeline/RenderTargetMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.pipeline; 2 | 3 | import com.mojang.blaze3d.pipeline.RenderTarget; 4 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | 7 | @Mixin(RenderTarget.class) 8 | @StubClass 9 | public class RenderTargetMixin { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/audio/OggAudioStreamMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.audio; 2 | 3 | import com.mojang.blaze3d.audio.OggAudioStream; 4 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | 7 | @Mixin(OggAudioStream.class) 8 | @StubClass(isThrowing = true) 9 | public class OggAudioStreamMixin { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/performance/ParticleEngineMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.performance; 2 | 3 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 4 | import net.minecraft.client.particle.ParticleEngine; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | 7 | @Mixin(ParticleEngine.class) 8 | @StubClass(skip = { "", "" }) 9 | public class ParticleEngineMixin { 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/minecraft/KeyboardHandlerAccessor.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.minecraft; 2 | 3 | import net.minecraft.client.KeyboardHandler; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | @Mixin(KeyboardHandler.class) 8 | public interface KeyboardHandlerAccessor { 9 | @Invoker 10 | void callCharTyped(long l, int i, int j); 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/systems/TimerQuery_FrameProfileMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.systems; 2 | 3 | import com.mojang.blaze3d.systems.TimerQuery; 4 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | 7 | @Mixin(TimerQuery.FrameProfile.class) 8 | @StubClass(isThrowing = true) 9 | public class TimerQuery_FrameProfileMixin { 10 | } 11 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx1G 3 | 4 | # Fabric Properties 5 | # check these on https://modmuss50.me/fabric.html 6 | minecraft_version=1.20.1 7 | loader_version=0.14.21 8 | 9 | # Mod Properties 10 | mod_version=0.1 11 | maven_group=me.katie 12 | archives_base_name=curium 13 | 14 | # Dependencies 15 | # Commit slug of https://github.com/MeteorDevelopment/orbit 16 | orbit_commit_slug=d5638d -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/events/client/ClientStartedEvent.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.events.client; 2 | 3 | /** 4 | * Event fired when the game starts (tick loop). 5 | */ 6 | public class ClientStartedEvent { 7 | private static final ClientStartedEvent INSTANCE = new ClientStartedEvent(); 8 | 9 | private ClientStartedEvent() { 10 | 11 | } 12 | 13 | public static ClientStartedEvent get() { 14 | return INSTANCE; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/systems/TimerQueryMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.systems; 2 | 3 | import com.mojang.blaze3d.systems.TimerQuery; 4 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | 7 | @Mixin(TimerQuery.class) 8 | @StubClass( 9 | isThrowing = true, 10 | skip = "getInstance" 11 | ) 12 | public class TimerQueryMixin { 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/minecraft/MouseHandlerMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.minecraft; 2 | 3 | import net.minecraft.client.MouseHandler; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Overwrite; 6 | 7 | @Mixin(MouseHandler.class) 8 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 9 | public class MouseHandlerMixin { 10 | @Overwrite 11 | public void setup(long l2) { 12 | 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/input/InputAction.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.input; 2 | 3 | /** 4 | * What action to perform on a button (mouse or keyboard). 5 | */ 6 | public enum InputAction { 7 | /** 8 | * The button will be released. 9 | */ 10 | Release, 11 | 12 | /** 13 | * The button will be pressed. 14 | */ 15 | Press, 16 | 17 | /** 18 | * The button will be repeated (only applicable for keyboards). 19 | */ 20 | Repeat 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/asm/mixin/annotations/Erase.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.asm.mixin.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Removes methods or fields from a class entirely. 10 | */ 11 | @Retention(RetentionPolicy.CLASS) 12 | @Target(ElementType.TYPE) 13 | public @interface Erase { 14 | String[] methods() default {}; 15 | String[] fields() default {}; 16 | } 17 | -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "curium", 4 | "version": "${version}", 5 | "name": "Curium", 6 | "description": "Strips the renderer out of a Minecraft client", 7 | "authors": [], 8 | "contact": {}, 9 | "license": "MIT", 10 | "icon": "assets/curium/icon.png", 11 | "environment": "client", 12 | "entrypoints": {}, 13 | "mixins": [ 14 | "curium.mixins.json" 15 | ], 16 | "accessWidener": "curium.accesswidener", 17 | "depends": { 18 | "fabricloader": ">=0.14.21", 19 | "minecraft": "~1.20" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/minecraft/renderer/culling/FrustumMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.minecraft.renderer.culling; 2 | 3 | import net.minecraft.client.renderer.culling.Frustum; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Overwrite; 6 | 7 | @Mixin(Frustum.class) 8 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 9 | public class FrustumMixin { 10 | @Overwrite 11 | public Frustum offsetToFullyIncludeCameraCube(int i) { 12 | return (Frustum) (Object) this; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/minecraft/MouseHandlerAccessor.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.minecraft; 2 | 3 | import net.minecraft.client.MouseHandler; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.gen.Invoker; 6 | 7 | @Mixin(MouseHandler.class) 8 | public interface MouseHandlerAccessor { 9 | @Invoker 10 | void callOnPress(long l, int i, int j, int k); 11 | 12 | @Invoker 13 | void callOnScroll(long l, double d, double e); 14 | 15 | @Invoker 16 | void callOnMove(long l, double d, double e); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/TPSLimiter.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium; 2 | 3 | public interface TPSLimiter { 4 | /** 5 | * Sets the target tick time of the client (in milliseconds). The client will sleep to keep the tickrate below this 6 | * value (default is 50ms per tick). 7 | * 8 | * @param millis the duration to sleep (in milliseconds). Negative values remove the TPS limit 9 | */ 10 | void setTargetTickTime(long millis); 11 | 12 | /** 13 | * @return the target tick time 14 | * @see TPSLimiter#setTargetTickTime(long) 15 | */ 16 | long getTargetTickTime(); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/platform/VideoModeMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.platform; 2 | 3 | import com.mojang.blaze3d.platform.VideoMode; 4 | import me.katie.curium.impl.asm.mixin.annotations.Erase; 5 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | 8 | @Mixin(VideoMode.class) 9 | @StubClass(isThrowing = true) 10 | @Erase(methods = { 11 | "(Lorg/lwjgl/glfw/GLFWVidMode$Buffer;)V", 12 | "(Lorg/lwjgl/glfw/GLFWVidMode;)V" 13 | }) 14 | public class VideoModeMixin { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/performance/ModelManagerMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.performance; 2 | 3 | import net.minecraft.client.resources.model.ModelManager; 4 | import net.minecraft.world.level.block.state.BlockState; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Overwrite; 7 | 8 | @Mixin(ModelManager.class) 9 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 10 | public class ModelManagerMixin { 11 | @Overwrite 12 | public boolean requiresRender(BlockState blockState, BlockState blockState2) { 13 | return false; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/platform/ClipboardManagerMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.platform; 2 | 3 | import com.mojang.blaze3d.platform.ClipboardManager; 4 | import me.katie.curium.impl.asm.mixin.annotations.OverwriteCtor; 5 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | 8 | @Mixin(ClipboardManager.class) 9 | @StubClass( 10 | isThrowing = true, 11 | skip = "" 12 | ) 13 | public class ClipboardManagerMixin { 14 | @OverwriteCtor 15 | private void curium_overwriteCtor() { 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/systems/TimerQuery_TimerQueryLazyLoaderMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.systems; 2 | 3 | import com.mojang.blaze3d.systems.TimerQuery; 4 | import org.jetbrains.annotations.Nullable; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Overwrite; 7 | 8 | @Mixin(TimerQuery.TimerQueryLazyLoader.class) 9 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 10 | public class TimerQuery_TimerQueryLazyLoaderMixin { 11 | @Overwrite 12 | private static @Nullable TimerQuery instantiate() { 13 | return null; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/audio/OpenAlUtilMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.audio; 2 | 3 | import com.mojang.blaze3d.audio.OpenAlUtil; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Overwrite; 6 | 7 | @Mixin(OpenAlUtil.class) 8 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 9 | public class OpenAlUtilMixin { 10 | @Overwrite 11 | static boolean checkALError(String string) { 12 | return false; 13 | } 14 | 15 | @Overwrite 16 | static boolean checkALCError(long l, String string) { 17 | return false; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/events/client/TickEvent.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.events.client; 2 | 3 | public class TickEvent { 4 | public static class Pre extends TickEvent { 5 | private static final Pre INSTANCE = new Pre(); 6 | 7 | private Pre() { 8 | 9 | } 10 | 11 | public static Pre get() { 12 | return INSTANCE; 13 | } 14 | } 15 | 16 | public static class Post extends TickEvent { 17 | private static final Post INSTANCE = new Post(); 18 | 19 | private Post() { 20 | 21 | } 22 | 23 | public static Post get() { 24 | return INSTANCE; 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/reduce_threads/MainMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.reduce_threads; 2 | 3 | import net.minecraft.client.main.Main; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Redirect; 7 | 8 | @Mixin(Main.class) 9 | public class MainMixin { 10 | @Redirect( 11 | method = "main", 12 | at = @At( 13 | value = "INVOKE", 14 | target = "Lnet/minecraft/Util;startTimerHackThread()V" 15 | ) 16 | ) 17 | private static void curium_dontStartTimerHackThread() { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Curium 2 | ![Version](https://img.shields.io/badge/MC_Version-1.20.1-blue) 3 | 4 | A project to strip the rendering code out of a Minecraft client. 5 | 6 | ### Why? 7 | - Fabric loader updates near-instantly to snapshots and new releases 8 | - Reimplementations of the game are less accurate, and can therefore be detected server-side 9 | - Allows somewhat seamless porting of existing mods (renderer code may need to be edited) 10 | 11 | ### Goals 12 | - Minimal CPU and RAM usage 13 | - No dependency on LWJGL or GPU hardware 14 | - Ergonomic API for replaced features (e.g. keyboard or mouse spoofing) 15 | 16 | ### API 17 | Everything outside the `me.katie.curium.impl` package is part of the public API. -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/platform/TextureUtilMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.platform; 2 | 3 | import com.mojang.blaze3d.platform.TextureUtil; 4 | import me.katie.curium.impl.util.Util; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Overwrite; 7 | 8 | import java.io.InputStream; 9 | import java.nio.ByteBuffer; 10 | 11 | @Mixin(value = TextureUtil.class, remap = false) 12 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 13 | public class TextureUtilMixin { 14 | @Overwrite 15 | public static ByteBuffer readResource(InputStream inputStream) { 16 | return Util.stubbed(); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/vertex/VertexConsumerMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.vertex; 2 | 3 | import com.mojang.blaze3d.vertex.PoseStack; 4 | import com.mojang.blaze3d.vertex.VertexConsumer; 5 | import net.minecraft.client.renderer.block.model.BakedQuad; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Overwrite; 8 | 9 | @Mixin(VertexConsumer.class) 10 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 11 | public interface VertexConsumerMixin { 12 | @Overwrite 13 | default void putBulkData(PoseStack.Pose pose, BakedQuad bakedQuad, float[] fs, float f, float g, float h, int[] is, int i, boolean bl) { 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/asm/mixin/annotations/OverwriteCtor.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.asm.mixin.annotations; 2 | 3 | 4 | import java.lang.annotation.ElementType; 5 | import java.lang.annotation.Retention; 6 | import java.lang.annotation.RetentionPolicy; 7 | import java.lang.annotation.Target; 8 | 9 | /** 10 | * Annotation applied to a method within a mixin to indicate that the method should overwrite the body of a constructor 11 | * within the target. 12 | */ 13 | @Retention(RetentionPolicy.CLASS) 14 | @Target(ElementType.METHOD) 15 | public @interface OverwriteCtor { 16 | /** 17 | * The target descriptor of the superclass constructor to invoke. 18 | */ 19 | String superDesc() default "()V"; 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/font/TrueTypeGlyphProviderMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.font; 2 | 3 | import com.mojang.blaze3d.font.TrueTypeGlyphProvider; 4 | import me.katie.curium.impl.asm.mixin.annotations.Erase; 5 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | 8 | @Mixin(TrueTypeGlyphProvider.class) 9 | @StubClass(isThrowing = true) 10 | @Erase( 11 | methods = { 12 | "(Ljava/nio/ByteBuffer;Lorg/lwjgl/stb/STBTTFontinfo;FFFFLjava/lang/String;)V", 13 | }, 14 | fields = { 15 | "font" 16 | } 17 | ) 18 | public class TrueTypeGlyphProviderMixin { 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/platform/GlUtilMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.platform; 2 | 3 | import com.mojang.blaze3d.platform.GlUtil; 4 | import me.katie.curium.impl.util.Util; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Overwrite; 7 | 8 | import java.nio.Buffer; 9 | import java.nio.ByteBuffer; 10 | 11 | @Mixin(GlUtil.class) 12 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 13 | public class GlUtilMixin { 14 | @Overwrite 15 | public static ByteBuffer allocateMemory(int i) { 16 | return Util.stubbed(); 17 | } 18 | 19 | @Overwrite 20 | public static void freeMemory(Buffer buffer) { 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/asm/mixin/annotations/CustomTransformer.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.asm.mixin.annotations; 2 | 3 | import me.katie.curium.impl.asm.mixin.ClassTransformer; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | /** 11 | * Annotation applied to a mixin class that allows arbitrary class transformers to be applied to the target class. 12 | */ 13 | @Retention(RetentionPolicy.CLASS) 14 | @Target(ElementType.TYPE) 15 | public @interface CustomTransformer { 16 | /** 17 | * List of transformers to apply. 18 | */ 19 | Class[] value(); 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/stub/resources/NopReloadInstance.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.stub.resources; 2 | 3 | import net.minecraft.server.packs.resources.ReloadInstance; 4 | import org.jetbrains.annotations.NotNull; 5 | 6 | import java.util.concurrent.CompletableFuture; 7 | 8 | public class NopReloadInstance implements ReloadInstance { 9 | public static final ReloadInstance INSTANCE = new NopReloadInstance(); 10 | 11 | private NopReloadInstance() { 12 | 13 | } 14 | 15 | @Override 16 | public @NotNull CompletableFuture done() { 17 | return CompletableFuture.completedFuture(null); 18 | } 19 | 20 | @Override 21 | public float getActualProgress() { 22 | return 1.0f; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/systems/RenderSystem_AutoStorageIndexBufferMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.systems; 2 | 3 | import com.mojang.blaze3d.systems.RenderSystem; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Overwrite; 6 | 7 | @Mixin(RenderSystem.AutoStorageIndexBuffer.class) 8 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 9 | public class RenderSystem_AutoStorageIndexBufferMixin { 10 | @Overwrite 11 | public boolean hasStorage(int i) { 12 | return true; 13 | } 14 | 15 | @Overwrite 16 | public void bind(int i) { 17 | 18 | } 19 | 20 | @Overwrite 21 | private void ensureStorage(int i) { 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/input/FakeKeyboard.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.input; 2 | 3 | /** 4 | * Interface to fake inputs from a keyboard. 5 | */ 6 | public interface FakeKeyboard { 7 | /** 8 | * Simulates pressing, holding or releasing a key. 9 | * 10 | * @param key the keyboard key to press, hold or release 11 | * @param action the action to perform 12 | */ 13 | void update(KeyboardKey key, InputAction action); 14 | 15 | /** 16 | * Simulates typing a unicode codepoint (e.g. into a text box). 17 | * 18 | * @param codepoint the codepoint that was typed 19 | * @param modifiers the modifier keys to simulate holding 20 | */ 21 | void type(int codepoint, InputModifiers modifiers); 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/Blaze3DMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d; 2 | 3 | import com.mojang.blaze3d.Blaze3D; 4 | import me.katie.curium.impl.util.UnsafeUtil; 5 | import net.minecraft.Util; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Overwrite; 8 | 9 | @Mixin(Blaze3D.class) 10 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 11 | public class Blaze3DMixin { 12 | @Overwrite 13 | public static void youJustLostTheGame() { 14 | // Replacement for the LWJGL call. 15 | UnsafeUtil.UNSAFE.putInt(0L, 0); 16 | } 17 | 18 | @Overwrite 19 | public static double getTime() { 20 | return Util.getNanos() / 1_000_000_000d; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/asm/mixin/helper/CtorOverwriteHelper.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.asm.mixin.helper; 2 | 3 | import me.katie.curium.impl.asm.mixin.annotations.OverwriteCtor; 4 | 5 | /** 6 | * Helper class for {@link OverwriteCtor}. 7 | */ 8 | public class CtorOverwriteHelper { 9 | private CtorOverwriteHelper() { 10 | throw new UnsupportedOperationException("Cannot instantiate CtorOverwriteHelper"); 11 | } 12 | 13 | /** 14 | * Stub method that is translated to a call to the target super constructor by the annotation handler. 15 | */ 16 | @SuppressWarnings("unused") 17 | public static void super_(Object... args) { 18 | throw new IllegalStateException("super constructor was not transformed out"); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/audio/SoundBufferMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.audio; 2 | 3 | import com.mojang.blaze3d.audio.SoundBuffer; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Overwrite; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | 8 | import java.util.OptionalInt; 9 | 10 | @Mixin(SoundBuffer.class) 11 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 12 | public class SoundBufferMixin { 13 | @Shadow private boolean hasAlBuffer; 14 | 15 | @Overwrite 16 | public OptionalInt getAlBuffer() { 17 | return OptionalInt.empty(); 18 | } 19 | 20 | @Overwrite 21 | public void discardAlBuffer() { 22 | this.hasAlBuffer = false; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/minecraft/screen/ReceivingLevelScreenMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.minecraft.screen; 2 | 3 | import net.minecraft.client.gui.screens.ReceivingLevelScreen; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Redirect; 7 | 8 | @Mixin(ReceivingLevelScreen.class) 9 | public class ReceivingLevelScreenMixin { 10 | @Redirect( 11 | method = "tick", 12 | at = @At( 13 | value = "INVOKE", 14 | target = "Ljava/lang/System;currentTimeMillis()J" 15 | ) 16 | ) 17 | private long curium_skipReceivingLevelScreen() { 18 | return Long.MAX_VALUE; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/platform/DebugMemoryUntrackerMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.platform; 2 | 3 | import com.mojang.blaze3d.platform.DebugMemoryUntracker; 4 | import me.katie.curium.impl.asm.mixin.annotations.Erase; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Overwrite; 7 | 8 | import java.lang.invoke.MethodHandle; 9 | 10 | @Mixin(DebugMemoryUntracker.class) 11 | @Erase( 12 | methods = { "untrack(Lorg/lwjgl/system/Pointer;)V" } 13 | ) 14 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 15 | public class DebugMemoryUntrackerMixin { 16 | @Overwrite 17 | @SuppressWarnings("target") 18 | private static MethodHandle method_1408() { 19 | return null; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/reduce_threads/UtilMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.reduce_threads; 2 | 3 | import com.google.common.util.concurrent.MoreExecutors; 4 | import net.minecraft.Util; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Overwrite; 7 | 8 | import java.util.concurrent.ExecutorService; 9 | 10 | @Mixin(Util.class) 11 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 12 | public class UtilMixin { 13 | @Overwrite 14 | private static ExecutorService makeExecutor(String name) { 15 | return MoreExecutors.newDirectExecutorService(); 16 | } 17 | 18 | @Overwrite 19 | private static ExecutorService makeIoExecutor() { 20 | return MoreExecutors.newDirectExecutorService(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/platform/MonitorMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.platform; 2 | 3 | import com.mojang.blaze3d.platform.Monitor; 4 | import com.mojang.blaze3d.platform.VideoMode; 5 | import org.spongepowered.asm.mixin.*; 6 | 7 | import java.util.List; 8 | 9 | @Mixin(Monitor.class) 10 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 11 | public class MonitorMixin { 12 | @Mutable 13 | @Shadow @Final private List videoModes; 14 | 15 | @Shadow private VideoMode currentMode; 16 | 17 | @Overwrite 18 | public void refreshVideoModes() { 19 | VideoMode mode = new VideoMode(1920, 1080, 8, 8, 8, 60); 20 | 21 | this.videoModes = List.of(mode); 22 | this.currentMode = mode; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/minecraft/font/TrueTypeGlyphProviderBuilderMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.minecraft.font; 2 | 3 | import com.mojang.blaze3d.font.GlyphProvider; 4 | import net.minecraft.client.gui.font.providers.TrueTypeGlyphProviderDefinition; 5 | import net.minecraft.server.packs.resources.ResourceManager; 6 | import org.jetbrains.annotations.Nullable; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Overwrite; 9 | 10 | @Mixin(TrueTypeGlyphProviderDefinition.class) 11 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 12 | public class TrueTypeGlyphProviderBuilderMixin { 13 | @Overwrite 14 | private @Nullable GlyphProvider load(ResourceManager resourceManager) { 15 | return null; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/reduce_threads/SharedConstantsMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.reduce_threads; 2 | 3 | import net.minecraft.SharedConstants; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Overwrite; 6 | 7 | /** 8 | * DFU fails when running on a direct executor. 9 | *

10 | * Copied from LazyDFU, 11 | * licensed under MIT. 12 | */ 13 | @Mixin(SharedConstants.class) 14 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 15 | public class SharedConstantsMixin { 16 | @Overwrite 17 | public static void enableDataFixerOptimizations() { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/platform/MacosUtilMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.platform; 2 | 3 | import com.mojang.blaze3d.platform.MacosUtil; 4 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 5 | import net.minecraft.server.packs.resources.IoSupplier; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Overwrite; 8 | 9 | import java.io.InputStream; 10 | 11 | @Mixin(MacosUtil.class) 12 | @StubClass(isThrowing = true) 13 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 14 | public class MacosUtilMixin { 15 | @Overwrite 16 | public static void toggleFullscreen(long l) { 17 | 18 | } 19 | 20 | @Overwrite 21 | public static void loadIcon(IoSupplier ioSupplier) { 22 | 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/minecraft/MainMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.minecraft; 2 | 3 | import net.minecraft.client.main.Main; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Redirect; 7 | 8 | @Mixin(Main.class) 9 | public class MainMixin { 10 | @Redirect( 11 | method = "main", 12 | remap = false, 13 | at = @At( 14 | value = "INVOKE", 15 | target = "Ljava/lang/Thread;setName(Ljava/lang/String;)V", 16 | remap = false 17 | ) 18 | ) 19 | private static void curium_overwriteThreadName(Thread instance, String name) { 20 | instance.setName("Client Thread"); 21 | } 22 | } -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/platform/GlDebugMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.platform; 2 | 3 | import com.mojang.blaze3d.platform.GlDebug; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.Overwrite; 6 | 7 | import java.util.Collections; 8 | import java.util.List; 9 | 10 | @Mixin(GlDebug.class) 11 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 12 | public class GlDebugMixin { 13 | @Overwrite 14 | private static void printDebugLog(int i, int j, int k, int l, int m, long n, long o) { 15 | 16 | } 17 | 18 | @Overwrite 19 | public static List getLastOpenGlDebugMessages() { 20 | return Collections.emptyList(); 21 | } 22 | 23 | @Overwrite 24 | public static void enableDebugCallback(int i, boolean bl) { 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/platform/WindowMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.platform; 2 | 3 | import com.mojang.blaze3d.platform.DisplayData; 4 | import com.mojang.blaze3d.platform.ScreenManager; 5 | import com.mojang.blaze3d.platform.Window; 6 | import com.mojang.blaze3d.platform.WindowEventHandler; 7 | import me.katie.curium.impl.asm.mixin.annotations.OverwriteCtor; 8 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 9 | import org.jetbrains.annotations.Nullable; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | 12 | @Mixin(Window.class) 13 | @StubClass 14 | @SuppressWarnings({"overwrite"}) 15 | public class WindowMixin { 16 | @OverwriteCtor 17 | private void curium_overwriteCtor(WindowEventHandler windowEventHandler, ScreenManager screenManager, DisplayData displayData, @Nullable String string, String string2) { 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/vertex/TesselatorMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.vertex; 2 | 3 | import com.mojang.blaze3d.vertex.BufferBuilder; 4 | import com.mojang.blaze3d.vertex.Tesselator; 5 | import me.katie.curium.impl.asm.mixin.annotations.OverwriteCtor; 6 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 7 | import org.spongepowered.asm.mixin.*; 8 | 9 | @Mixin(Tesselator.class) 10 | @StubClass(skip = "") 11 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 12 | public class TesselatorMixin { 13 | @Mutable 14 | @Shadow @Final private BufferBuilder builder; 15 | 16 | @OverwriteCtor 17 | public void curium_overwriteCtor(int i) { 18 | this.builder = new BufferBuilder(-1); 19 | } 20 | 21 | @Overwrite 22 | public BufferBuilder getBuilder() { 23 | return this.builder; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/asm/mixin/annotations/StubClass.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.asm.mixin.annotations; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * Annotation that can be applied to a mixin to generate automatic method stubs. This occurs before the contents of the 10 | * mixin are applied, so mixin overwrites will still apply properly. 11 | */ 12 | @Retention(RetentionPolicy.CLASS) 13 | @Target(ElementType.TYPE) 14 | public @interface StubClass { 15 | String[] skip() default {}; 16 | 17 | /** 18 | * If set to {@literal true}, every method will be stubbed with an exception throw. Otherwise, only 19 | * {@literal void}-returning methods will be stubbed (to no-ops). 20 | */ 21 | boolean isThrowing() default false; 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/minecraft/renderer/texture/TextureAtlasMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.minecraft.renderer.texture; 2 | 3 | import net.minecraft.client.renderer.texture.TextureAtlas; 4 | import org.slf4j.Logger; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Redirect; 8 | 9 | @Mixin(TextureAtlas.class) 10 | public class TextureAtlasMixin { 11 | @Redirect( 12 | method = "upload", 13 | at = @At( 14 | value = "INVOKE", 15 | target = "Lorg/slf4j/Logger;info(Ljava/lang/String;[Ljava/lang/Object;)V", 16 | remap = false, 17 | ordinal = 0 18 | ) 19 | ) 20 | private void curium_removeUselessLogMessage(Logger instance, String message, Object[] objects) { 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/asm/CuriumASM.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.asm; 2 | 3 | import me.katie.curium.impl.CuriumConstants; 4 | import me.katie.curium.impl.asm.util.KnotLoaderHacks; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | 8 | /** 9 | * Because sometimes Mixins just aren't enough... 10 | */ 11 | public class CuriumASM { 12 | private CuriumASM() { 13 | throw new UnsupportedOperationException("Cannot instantiate CuriumASM"); 14 | } 15 | 16 | public static final Logger LOGGER = LoggerFactory.getLogger("Curium/ASM"); 17 | 18 | public static void init() { 19 | // Define stubbed LWJGL callback interfaces. 20 | KnotLoaderHacks.defineReplacerClasses( 21 | "org/lwjgl/glfw", 22 | CuriumConstants.PACKAGE + "/stub/lwjgl", 23 | new String[] { 24 | "GLFWErrorCallbackI" 25 | } 26 | ); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/minecraft/KeyboardHandlerMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.minecraft; 2 | 3 | import me.katie.curium.events.ClipboardEvent; 4 | import me.katie.curium.impl.api.CuriumImpl; 5 | import net.minecraft.client.KeyboardHandler; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Overwrite; 8 | 9 | @Mixin(KeyboardHandler.class) 10 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 11 | public class KeyboardHandlerMixin { 12 | @Overwrite 13 | public String getClipboard() { 14 | ClipboardEvent.Get event = CuriumImpl.get().getEventBus().post(ClipboardEvent.Get.get()); 15 | return event.contents; 16 | } 17 | 18 | @Overwrite 19 | public void setClipboard(String contents) { 20 | CuriumImpl.get().getEventBus().post(ClipboardEvent.Set.get(contents)); 21 | } 22 | 23 | @Overwrite 24 | public void setup(long l2) { 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/asm/mixin/ClassTransformer.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.asm.mixin; 2 | 3 | import org.objectweb.asm.tree.ClassNode; 4 | 5 | /** 6 | * Interface implemented by class transformers as part of the ASM pipeline. 7 | */ 8 | public interface ClassTransformer { 9 | /** 10 | * Transformation executed before mixins are applied to the target class. 11 | * 12 | * @param target the target class (changes are applied) 13 | * @param mixin a copy of the mixin (changes to this class are not applied) 14 | */ 15 | void preMixinTransform(ClassNode target, ClassNode mixin); 16 | 17 | /** 18 | * Transformation executed after mixins are applied to the target class. 19 | * 20 | * @param target the target class (changes are applied) 21 | * @param mixin a copy of the mixin (changes to this class are not applied) 22 | */ 23 | void postMixinTransform(ClassNode target, ClassNode mixin); 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/audio/ChannelMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.audio; 2 | 3 | import com.mojang.blaze3d.audio.Channel; 4 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 5 | import me.katie.curium.impl.util.Util; 6 | import org.jetbrains.annotations.Nullable; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Overwrite; 9 | 10 | @Mixin(Channel.class) 11 | @StubClass 12 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 13 | public class ChannelMixin { 14 | @Overwrite 15 | private int getState() { 16 | return 4116; 17 | } 18 | 19 | @Overwrite 20 | @SuppressWarnings("DataFlowIssue") // IntelliJ complains if the nullable is there 21 | public static @Nullable Channel create() { 22 | return new Channel(-1); 23 | } 24 | 25 | @Overwrite 26 | private int removeProcessedBuffers() { 27 | return Util.stubbed(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/platform/InputConstants_TypeMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.platform; 2 | 3 | import com.mojang.blaze3d.platform.InputConstants; 4 | import net.minecraft.network.chat.Component; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Overwrite; 7 | 8 | @Mixin(InputConstants.Type.class) 9 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 10 | public class InputConstants_TypeMixin { 11 | @Overwrite 12 | @SuppressWarnings("target") 13 | private static Component method_27450(Integer i, String s) { 14 | return Component.translatable(s); 15 | } 16 | 17 | @Overwrite 18 | @SuppressWarnings("target") 19 | private static Component method_27449(Integer i, String s) { 20 | return Component.translatable(s); 21 | } 22 | 23 | @Overwrite 24 | @SuppressWarnings("target") 25 | private static Component method_27447(Integer i, String s) { 26 | return Component.translatable(s); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/platform/MemoryTrackerMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.platform; 2 | 3 | import com.mojang.blaze3d.platform.MemoryTracker; 4 | import me.katie.curium.impl.asm.mixin.annotations.Erase; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Overwrite; 7 | 8 | import java.nio.ByteBuffer; 9 | 10 | @Mixin(MemoryTracker.class) 11 | @Erase(methods = "") 12 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 13 | public class MemoryTrackerMixin { 14 | @Overwrite 15 | public static ByteBuffer create(int size) { 16 | return ByteBuffer.allocate(size); 17 | } 18 | 19 | @Overwrite 20 | public static ByteBuffer resize(ByteBuffer buffer, int size) { 21 | ByteBuffer newBuffer = ByteBuffer.allocate(size); 22 | 23 | assert buffer.hasArray(); 24 | assert newBuffer.hasArray(); 25 | System.arraycopy(buffer.array(), 0, newBuffer.array(), 0, buffer.capacity()); 26 | 27 | return newBuffer; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/platform/ScreenManagerMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.platform; 2 | 3 | import com.mojang.blaze3d.platform.Monitor; 4 | import com.mojang.blaze3d.platform.MonitorCreator; 5 | import com.mojang.blaze3d.platform.ScreenManager; 6 | import com.mojang.blaze3d.platform.Window; 7 | import me.katie.curium.impl.asm.mixin.annotations.OverwriteCtor; 8 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 9 | import org.jetbrains.annotations.Nullable; 10 | import org.spongepowered.asm.mixin.*; 11 | 12 | @Mixin(ScreenManager.class) 13 | @StubClass 14 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 15 | public class ScreenManagerMixin { 16 | @OverwriteCtor 17 | private void curium_overwriteCtor(MonitorCreator monitorCreator) { 18 | 19 | } 20 | 21 | @Overwrite 22 | public @Nullable Monitor getMonitor(long l) { 23 | return null; 24 | } 25 | 26 | @Overwrite 27 | public @Nullable Monitor findBestMonitor(Window window) { 28 | return null; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/minecraft/OptionsMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.minecraft; 2 | 3 | import net.minecraft.client.Options; 4 | import org.objectweb.asm.Opcodes; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | import org.spongepowered.asm.mixin.injection.At; 8 | import org.spongepowered.asm.mixin.injection.Inject; 9 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 10 | 11 | @Mixin(Options.class) 12 | public class OptionsMixin { 13 | @Shadow public boolean onboardAccessibility; 14 | 15 | @Inject( 16 | method = "processOptions", 17 | at = @At( 18 | value = "FIELD", 19 | opcode = Opcodes.PUTFIELD, 20 | target = "Lnet/minecraft/client/Options;onboardAccessibility:Z", 21 | shift = At.Shift.AFTER 22 | ) 23 | ) 24 | private void curium_disableAccessibilityOnboarding(Options.FieldAccess fieldAccess, CallbackInfo ci) { 25 | this.onboardAccessibility = false; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/CuriumProperties.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl; 2 | 3 | /** 4 | * System properties for configuring Curium. 5 | */ 6 | public class CuriumProperties { 7 | private CuriumProperties() { 8 | throw new UnsupportedOperationException("Cannot instantiate CuriumConstants"); 9 | } 10 | 11 | public static final String PROPERTY_BASE = "curium"; 12 | 13 | /** 14 | * If set, the ASM package will pass output through a {@link org.objectweb.asm.util.CheckClassAdapter} before 15 | * returning to Mixin. 16 | */ 17 | public static boolean ASM_CHECK = System.getProperty(PROPERTY_BASE + ".asm.check") != null; 18 | 19 | /** 20 | * System property containing an IP or domain name to connect to. 21 | */ 22 | public static String SERVER_HOST = System.getProperty(PROPERTY_BASE + ".server.host"); 23 | 24 | /** 25 | * System property containing the port to connect to ({@link CuriumProperties#SERVER_HOST SERVER_HOST} must be set). 26 | */ 27 | public static String SERVER_PORT = System.getProperty(PROPERTY_BASE + ".server.port"); 28 | } 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2023 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/minecraft/renderer/VirtualScreenMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.minecraft.renderer; 2 | 3 | import com.mojang.blaze3d.platform.DisplayData; 4 | import com.mojang.blaze3d.platform.Window; 5 | import me.katie.curium.impl.asm.mixin.annotations.OverwriteCtor; 6 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 7 | import net.minecraft.client.Minecraft; 8 | import net.minecraft.client.renderer.VirtualScreen; 9 | import org.jetbrains.annotations.Nullable; 10 | import org.spongepowered.asm.mixin.*; 11 | 12 | @Mixin(VirtualScreen.class) 13 | @StubClass 14 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 15 | public class VirtualScreenMixin { 16 | @Mutable 17 | @Shadow @Final private Minecraft minecraft; 18 | 19 | @OverwriteCtor 20 | public void curium_overwriteCtor(Minecraft minecraft) { 21 | this.minecraft = minecraft; 22 | } 23 | 24 | @Overwrite 25 | public Window newWindow(DisplayData displayData, @Nullable String string, String string2) { 26 | // Window constructor is overwritten. 27 | return new Window(null, null, null, null, null); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/events/ClipboardEvent.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.events; 2 | 3 | import net.minecraft.client.KeyboardHandler; 4 | 5 | /** 6 | * Events fired when the game attempts to access the clipboard. 7 | */ 8 | public class ClipboardEvent { 9 | /** 10 | * Fired when a {@link KeyboardHandler#setClipboard(String)} call occurs. 11 | */ 12 | public static class Set extends ClipboardEvent { 13 | private static final Set INSTANCE = new Set(); 14 | public String contents; 15 | 16 | private Set() { 17 | 18 | } 19 | 20 | public static Set get(String contents) { 21 | INSTANCE.contents = contents; 22 | return INSTANCE; 23 | } 24 | } 25 | 26 | /** 27 | * Fired when a {@link KeyboardHandler#getClipboard()} call occurs. 28 | */ 29 | public static class Get extends ClipboardEvent { 30 | private static final Get INSTANCE = new Get(); 31 | public String contents; 32 | 33 | private Get() { 34 | 35 | } 36 | 37 | public static Get get() { 38 | INSTANCE.contents = null; 39 | return INSTANCE; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/Curium.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium; 2 | 3 | import me.katie.curium.input.FakeKeyboard; 4 | import me.katie.curium.input.FakeMouse; 5 | import me.katie.curium.impl.api.CuriumImpl; 6 | import meteordevelopment.orbit.IEventBus; 7 | import net.minecraft.client.Minecraft; 8 | import org.jetbrains.annotations.NotNull; 9 | 10 | /** 11 | * Curium-owned state of a {@link Minecraft} instance. 12 | */ 13 | public interface Curium { 14 | /** 15 | * Gets the current {@link CuriumImpl} instance. 16 | */ 17 | static @NotNull Curium get() { 18 | return CuriumImpl.get(); 19 | } 20 | 21 | /** 22 | * @return the client TPS limiter 23 | */ 24 | @NotNull TPSLimiter getTpsLimiter(); 25 | 26 | /** 27 | * @return a fake mouse for the client 28 | */ 29 | @NotNull FakeMouse getFakeMouse(); 30 | 31 | /** 32 | * @return a fake keyboard for the client 33 | */ 34 | @NotNull FakeKeyboard getFakeKeyboard(); 35 | 36 | /** 37 | * @return the Curium event bus 38 | */ 39 | @NotNull IEventBus getEventBus(); 40 | 41 | /** 42 | * Sets a flag to indicate that the client should exit on the next tick loop. 43 | */ 44 | void close(); 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/shaders/UniformMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.shaders; 2 | 3 | import com.mojang.blaze3d.shaders.Shader; 4 | import com.mojang.blaze3d.shaders.Uniform; 5 | import me.katie.curium.impl.asm.mixin.annotations.OverwriteCtor; 6 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 7 | import org.spongepowered.asm.mixin.Final; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Mutable; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | 12 | @Mixin(Uniform.class) 13 | @StubClass 14 | public abstract class UniformMixin { 15 | @Mutable 16 | @Shadow @Final private String name; 17 | 18 | @Mutable 19 | @Shadow @Final private int count; 20 | 21 | @Mutable 22 | @Shadow @Final private int type; 23 | 24 | @Mutable 25 | @Shadow @Final private Shader parent; 26 | 27 | @Shadow private int location; 28 | 29 | @Shadow protected abstract void markDirty(); 30 | 31 | @OverwriteCtor 32 | public void curium_overwriteCtor(String name, int type, int count, Shader parent) { 33 | this.name = name; 34 | this.count = count; 35 | this.type = type; 36 | this.parent = parent; 37 | this.location = -1; 38 | this.markDirty(); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/util/UnsafeUtil.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.util; 2 | 3 | import sun.misc.Unsafe; 4 | 5 | import java.lang.reflect.Field; 6 | import java.lang.reflect.Modifier; 7 | 8 | /** 9 | * Because sometimes ASM just isn't enough... 10 | */ 11 | public class UnsafeUtil { 12 | private UnsafeUtil() { 13 | throw new UnsupportedOperationException("Cannot instantiate UnsafeUtil"); 14 | } 15 | 16 | public static final Unsafe UNSAFE; 17 | 18 | static { 19 | // Reflect the Unsafe instance out of the static field in the Unsafe class. 20 | Field unsafeInstance = null; 21 | 22 | for (Field field: Unsafe.class.getDeclaredFields()) { 23 | if (Modifier.isStatic(field.getModifiers()) && field.getType() == Unsafe.class) { 24 | unsafeInstance = field; 25 | break; 26 | } 27 | } 28 | 29 | if (unsafeInstance == null) { 30 | throw new IllegalStateException("couldn't find Unsafe instance reference"); 31 | } 32 | 33 | try { 34 | unsafeInstance.setAccessible(true); 35 | UNSAFE = (Unsafe) unsafeInstance.get(null); 36 | } catch (IllegalAccessException e) { 37 | throw new IllegalStateException("failed to reflect unsafe instance", e); 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/minecraft/renderer/texture/SpriteContentsMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.minecraft.renderer.texture; 2 | 3 | import net.minecraft.client.renderer.texture.SpriteContents; 4 | import org.slf4j.Logger; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Redirect; 8 | 9 | @Mixin(SpriteContents.class) 10 | public class SpriteContentsMixin { 11 | @Redirect( 12 | method = "createAnimatedTexture", 13 | at = @At( 14 | value = "INVOKE", 15 | target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", 16 | remap = false 17 | ) 18 | ) 19 | private void curium_removeUselessLogMessage(Logger instance, String message, Object o1, Object o2) { 20 | 21 | } 22 | 23 | @Redirect( 24 | method = "createAnimatedTexture", 25 | at = @At( 26 | value = "INVOKE", 27 | target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;[Ljava/lang/Object;)V", 28 | remap = false 29 | ) 30 | ) 31 | private void curium_removeUselessLogMessage2(Logger instance, String message, Object[] objects) { 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/util/Util.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.util; 2 | 3 | import me.katie.curium.impl.asm.mixin.helper.StubInternalHelper; 4 | 5 | import java.util.concurrent.CompletableFuture; 6 | import java.util.function.Consumer; 7 | 8 | public class Util { 9 | private Util() { 10 | throw new UnsupportedOperationException("Cannot instantiate Util"); 11 | } 12 | 13 | public static T make(T initial, Consumer consumer) { 14 | consumer.accept(initial); 15 | return initial; 16 | } 17 | 18 | /** 19 | * Throws a descriptive error about a failed assertion due to a stubbed out method, including the stubbed method in 20 | * the method name. This uses a stack walker - do not wrap. 21 | */ 22 | @SuppressWarnings("UnusedReturnValue") 23 | public static T stubbed() { 24 | throw StubInternalHelper.getStubbedException(); 25 | } 26 | 27 | /** 28 | * Returns a future that fails with a descriptive error about a failed assertion due to a stubbed out method, 29 | * including the stubbed method in the method name. This uses a stack walker - do not wrap. 30 | */ 31 | public static CompletableFuture stubbedFuture() { 32 | RuntimeException ex = StubInternalHelper.getStubbedException(); 33 | return CompletableFuture.failedFuture(ex); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/audio/SoundBufferLibraryMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.audio; 2 | 3 | import com.mojang.blaze3d.audio.SoundBuffer; 4 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 5 | import me.katie.curium.impl.util.Util; 6 | import net.minecraft.client.resources.sounds.Sound; 7 | import net.minecraft.client.sounds.AudioStream; 8 | import net.minecraft.client.sounds.SoundBufferLibrary; 9 | import net.minecraft.resources.ResourceLocation; 10 | import org.spongepowered.asm.mixin.Mixin; 11 | import org.spongepowered.asm.mixin.Overwrite; 12 | 13 | import java.util.Collection; 14 | import java.util.concurrent.CompletableFuture; 15 | 16 | @Mixin(SoundBufferLibrary.class) 17 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 18 | @StubClass 19 | public class SoundBufferLibraryMixin { 20 | @Overwrite 21 | public CompletableFuture getCompleteBuffer(ResourceLocation resourceLocation) { 22 | return Util.stubbedFuture(); 23 | } 24 | 25 | @Overwrite 26 | public CompletableFuture getStream(ResourceLocation resourceLocation, boolean bl) { 27 | return Util.stubbedFuture(); 28 | } 29 | 30 | @Overwrite 31 | public CompletableFuture preload(Collection collection) { 32 | return CompletableFuture.completedFuture(null); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/api/FakeMouseImpl.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.api; 2 | 3 | import me.katie.curium.input.FakeMouse; 4 | import me.katie.curium.input.InputAction; 5 | import me.katie.curium.input.InputModifiers; 6 | import me.katie.curium.impl.mixin.core.minecraft.MouseHandlerAccessor; 7 | import net.minecraft.client.Minecraft; 8 | 9 | public class FakeMouseImpl implements FakeMouse { 10 | @Override 11 | public void move(long x, long y) { 12 | MouseHandlerAccessor accessor = (MouseHandlerAccessor) Minecraft.getInstance().mouseHandler; 13 | accessor.callOnMove(0, x, y); 14 | } 15 | 16 | @Override 17 | public void click(Button button, InputAction action, InputModifiers modifiers) { 18 | if (action != InputAction.Press && action != InputAction.Release) { 19 | throw new IllegalArgumentException("FakeMouse#click action must be press or release"); 20 | } 21 | 22 | MouseHandlerAccessor accessor = (MouseHandlerAccessor) Minecraft.getInstance().mouseHandler; 23 | accessor.callOnPress(0, button.ordinal(), action.ordinal(), modifiers.toRaw()); 24 | } 25 | 26 | @Override 27 | public void scroll(double xOffset, double yOffset) { 28 | MouseHandlerAccessor accessor = (MouseHandlerAccessor) Minecraft.getInstance().mouseHandler; 29 | accessor.callOnScroll(0, xOffset, yOffset); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/asm/mixin/transformers/StripLwjglLdcTransformer.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.asm.mixin.transformers; 2 | 3 | import me.katie.curium.impl.asm.mixin.ClassTransformer; 4 | import org.objectweb.asm.Opcodes; 5 | import org.objectweb.asm.Type; 6 | import org.objectweb.asm.tree.*; 7 | 8 | import java.util.Iterator; 9 | 10 | /** 11 | * Transformer that replaces {@code LDC} instructions referencing LWJGL classes with {@code ACONST_NULL} instructions. 12 | */ 13 | public class StripLwjglLdcTransformer implements ClassTransformer { 14 | @Override 15 | public void preMixinTransform(ClassNode target, ClassNode mixin) { 16 | 17 | } 18 | 19 | @Override 20 | public void postMixinTransform(ClassNode target, ClassNode mixin) { 21 | for (MethodNode method: target.methods) { 22 | for (Iterator it = method.instructions.iterator(); it.hasNext();) { 23 | AbstractInsnNode insn = it.next(); 24 | 25 | if (insn instanceof LdcInsnNode ldcInsn) { 26 | if (ldcInsn.cst instanceof Type type) { 27 | if (type.getInternalName().contains("lwjgl")) { 28 | method.instructions.insertBefore(insn, new InsnNode(Opcodes.ACONST_NULL)); 29 | it.remove(); 30 | } 31 | } 32 | } 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/reduce_threads/SoundEngineExecutorMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.reduce_threads; 2 | 3 | import me.katie.curium.impl.asm.mixin.annotations.Erase; 4 | import me.katie.curium.impl.util.Util; 5 | import net.minecraft.client.sounds.SoundEngineExecutor; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Overwrite; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Redirect; 11 | 12 | @Mixin(SoundEngineExecutor.class) 13 | @Erase( 14 | methods = { "createThread" } 15 | ) 16 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 17 | public class SoundEngineExecutorMixin { 18 | @Shadow private volatile boolean shutdown; 19 | 20 | @Redirect( 21 | method = "", 22 | at = @At( 23 | value = "INVOKE", 24 | target = "Lnet/minecraft/client/sounds/SoundEngineExecutor;createThread()Ljava/lang/Thread;" 25 | ) 26 | ) 27 | private Thread cerium_removeSoundEngineThread(SoundEngineExecutor instance) { 28 | this.shutdown = true; 29 | return null; 30 | } 31 | 32 | @Overwrite 33 | public void flush() { 34 | 35 | } 36 | 37 | @Overwrite 38 | public void waitForTasks() { 39 | 40 | } 41 | 42 | @Overwrite 43 | private void run() { 44 | Util.stubbed(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/asm/mixin/helper/StubInternalHelper.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.asm.mixin.helper; 2 | 3 | import me.katie.curium.impl.asm.util.ASMUtil; 4 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 5 | 6 | import java.lang.invoke.MethodType; 7 | 8 | /** 9 | * Internal helper to generate throws for {@link StubClass} in throwing mode. 10 | */ 11 | public class StubInternalHelper { 12 | private static final StackWalker STACK_WALKER = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE); 13 | 14 | private StubInternalHelper() { 15 | throw new UnsupportedOperationException("Cannot instantiate StubInternalHelper"); 16 | } 17 | 18 | public static RuntimeException getStubbedException() { 19 | String calledMethod = STACK_WALKER.walk(s -> 20 | s.skip(2) 21 | .findFirst() 22 | .map(f -> { 23 | MethodType type = f.getMethodType(); 24 | String desc = ASMUtil.methodTypeToAsmType(type).getDescriptor(); 25 | 26 | return String.format("%s;%s%s", f.getClassName(), f.getMethodName(), desc); 27 | }) 28 | .orElseThrow() 29 | ); 30 | 31 | return new IllegalStateException("call against a stubbed out method: " + calledMethod); 32 | } 33 | 34 | public static void stubbed() { 35 | throw getStubbedException(); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/input/FakeMouse.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.input; 2 | 3 | /** 4 | * Interface to fake inputs from a mouse. 5 | */ 6 | public interface FakeMouse { 7 | /** 8 | * Simulates a mouse movement. 9 | * 10 | * @param x the new cursor x-coordinate, relative to the left edge of the content area 11 | * @param y the new cursor y-coordinate, relative to the top edge of the content area 12 | */ 13 | void move(long x, long y); 14 | 15 | /** 16 | * Simulates pressing or releasing a mouse button. 17 | * 18 | * @param button the mouse button to press or release 19 | * @param action the action to perform 20 | * @param modifiers bitfield describing which modifiers keys were held down 21 | * @throws IllegalArgumentException if {@code action} is not {@link InputAction#Press Press} or {@link InputAction#Release Release}. 22 | */ 23 | void click(Button button, InputAction action, InputModifiers modifiers); 24 | 25 | /** 26 | * Simulates a mouse scroll. 27 | * 28 | * @param xOffset the scroll offset along the x-axis 29 | * @param yOffset the scroll offset along the y-axis 30 | */ 31 | void scroll(double xOffset, double yOffset); 32 | 33 | /** 34 | * Enum of possible mouse buttons. 35 | */ 36 | enum Button { 37 | Left, 38 | Middle, 39 | Right, 40 | Button4, 41 | Button5, 42 | Button6, 43 | Button7, 44 | Button8 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/audio/ListenerMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.audio; 2 | 3 | import com.mojang.blaze3d.audio.Listener; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Redirect; 7 | 8 | @Mixin(Listener.class) 9 | public class ListenerMixin { 10 | @Redirect( 11 | method = "setListenerPosition", 12 | at = @At( 13 | value = "INVOKE", 14 | target = "Lorg/lwjgl/openal/AL10;alListener3f(IFFF)V", 15 | remap = false 16 | ) 17 | ) 18 | private void curium_nopSetListenerPosition(int paramName, float value1, float value2, float value3) { 19 | 20 | } 21 | 22 | @Redirect( 23 | method = "setListenerOrientation", 24 | at = @At( 25 | value = "INVOKE", 26 | target = "Lorg/lwjgl/openal/AL10;alListenerfv(I[F)V", 27 | remap = false 28 | ) 29 | ) 30 | private void curium_nopSetListenerOrientation(int paramName, float[] values) { 31 | 32 | } 33 | 34 | @Redirect( 35 | method = "setGain", 36 | at = @At( 37 | value = "INVOKE", 38 | target = "Lorg/lwjgl/openal/AL10;alListenerf(IF)V", 39 | remap = false 40 | ) 41 | ) 42 | private void curium_nopSetGain(int paramName, float value) { 43 | 44 | } 45 | 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/asm/mixin/transformers/EraseHandler.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.asm.mixin.transformers; 2 | 3 | import me.katie.curium.impl.asm.mixin.ClassTransformer; 4 | import me.katie.curium.impl.asm.mixin.annotations.Erase; 5 | import org.objectweb.asm.tree.AnnotationNode; 6 | import org.objectweb.asm.tree.ClassNode; 7 | import org.spongepowered.asm.util.Annotations; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Handles the {@link Erase} annotation. 13 | */ 14 | public class EraseHandler implements ClassTransformer { 15 | @Override 16 | public void preMixinTransform(ClassNode target, ClassNode mixin) { 17 | // Fetch annotation. 18 | AnnotationNode annotation = Annotations.getInvisible(mixin, Erase.class); 19 | 20 | if (annotation == null) { 21 | return; 22 | } 23 | 24 | // Remove methods. 25 | List targetMethods = Annotations.getValue(annotation, "methods"); 26 | 27 | if (targetMethods != null && !targetMethods.isEmpty()) { 28 | target.methods.removeIf(m -> targetMethods.contains(m.name + m.desc) || targetMethods.contains(m.name)); 29 | } 30 | 31 | // Remove fields. 32 | List targetFields = Annotations.getValue(annotation, "fields"); 33 | 34 | if (targetFields != null && !targetFields.isEmpty()) { 35 | target.fields.removeIf(f -> targetFields.contains(f.name)); 36 | } 37 | } 38 | 39 | @Override 40 | public void postMixinTransform(ClassNode target, ClassNode mixin) { 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/CuriumConstants.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl; 2 | 3 | import com.google.common.collect.ImmutableList; 4 | import net.fabricmc.loader.api.FabricLoader; 5 | import net.fabricmc.loader.api.ModContainer; 6 | import net.fabricmc.loader.api.metadata.ModMetadata; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * Constants for Curium. 14 | */ 15 | public class CuriumConstants { 16 | 17 | private CuriumConstants() { 18 | throw new UnsupportedOperationException("Cannot instantiate CuriumConstants"); 19 | } 20 | 21 | public static final Logger LOGGER = LoggerFactory.getLogger("Curium"); 22 | 23 | /** 24 | * Sections of the "system report" portion of a crash report that will be skipped. 25 | */ 26 | public static final List OMITTED_SYSTEM_REPORT_SECTIONS = ImmutableList.of( 27 | /* "Backend library", */ "Window size", "GL Caps", "GL debug messages", "Using VBOs", "GPU Warnings", 28 | "Backend API", "Graphics mode" 29 | ); 30 | 31 | public static final String PACKAGE = CuriumConstants.class.getPackageName().replace('.', '/'); 32 | public static final String DESCRIPTION; 33 | public static final String USING_CURIUM = ""; 34 | 35 | static { 36 | ModContainer container = FabricLoader.getInstance().getModContainer("curium").orElseThrow(); 37 | ModMetadata metadata = container.getMetadata(); 38 | 39 | DESCRIPTION = ""; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/performance/ReloadableResourceManagerMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.performance; 2 | 3 | import me.katie.curium.impl.stub.resources.NopReloadInstance; 4 | import net.minecraft.server.packs.resources.PreparableReloadListener; 5 | import net.minecraft.server.packs.resources.ReloadInstance; 6 | import net.minecraft.server.packs.resources.ReloadableResourceManager; 7 | import net.minecraft.server.packs.resources.ResourceManager; 8 | import net.minecraft.util.Unit; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Redirect; 12 | 13 | import java.util.List; 14 | import java.util.concurrent.CompletableFuture; 15 | import java.util.concurrent.Executor; 16 | 17 | @Mixin(ReloadableResourceManager.class) 18 | public class ReloadableResourceManagerMixin { 19 | @Redirect( 20 | method = "createReload", 21 | at = @At( 22 | value = "INVOKE", 23 | target = "Lnet/minecraft/server/packs/resources/SimpleReloadInstance;create(Lnet/minecraft/server/packs/resources/ResourceManager;Ljava/util/List;Ljava/util/concurrent/Executor;Ljava/util/concurrent/Executor;Ljava/util/concurrent/CompletableFuture;Z)Lnet/minecraft/server/packs/resources/ReloadInstance;" 24 | ) 25 | ) 26 | private ReloadInstance create(ResourceManager resourceManager, List list, Executor executor, Executor executor2, CompletableFuture completableFuture, boolean bl) { 27 | return NopReloadInstance.INSTANCE; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/api/CuriumImpl.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.api; 2 | 3 | import me.katie.curium.input.FakeKeyboard; 4 | import me.katie.curium.input.FakeMouse; 5 | import me.katie.curium.TPSLimiter; 6 | import me.katie.curium.impl.duck.CuriumStateHolder; 7 | import me.katie.curium.Curium; 8 | import meteordevelopment.orbit.EventBus; 9 | import meteordevelopment.orbit.IEventBus; 10 | import net.minecraft.client.Minecraft; 11 | import org.jetbrains.annotations.NotNull; 12 | 13 | public class CuriumImpl implements Curium { 14 | public final TPSLimiterImpl tpsLimiter = new TPSLimiterImpl(); 15 | public final FakeMouseImpl fakeMouse = new FakeMouseImpl(); 16 | public final FakeKeyboardImpl fakeKeyboard = new FakeKeyboardImpl(); 17 | public final EventBus eventBus = new EventBus(); 18 | public boolean shouldClose = false; 19 | 20 | public CuriumImpl() { 21 | 22 | } 23 | 24 | public @NotNull static CuriumImpl get() { 25 | return ((CuriumStateHolder) Minecraft.getInstance()).curium_getState(); 26 | } 27 | 28 | @Override 29 | public @NotNull TPSLimiter getTpsLimiter() { 30 | return this.tpsLimiter; 31 | } 32 | 33 | @Override 34 | public @NotNull FakeMouse getFakeMouse() { 35 | return this.fakeMouse; 36 | } 37 | 38 | @Override 39 | public @NotNull FakeKeyboard getFakeKeyboard() { 40 | return this.fakeKeyboard; 41 | } 42 | 43 | @Override 44 | public @NotNull IEventBus getEventBus() { 45 | return this.eventBus; 46 | } 47 | 48 | @Override 49 | public void close() { 50 | this.shouldClose = true; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/minecraft/renderer/texture/SpriteLoaderMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.minecraft.renderer.texture; 2 | 3 | import net.minecraft.client.renderer.texture.SpriteLoader; 4 | import org.slf4j.Logger; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.injection.At; 7 | import org.spongepowered.asm.mixin.injection.Redirect; 8 | 9 | @Mixin(SpriteLoader.class) 10 | public class SpriteLoaderMixin { 11 | @Redirect( 12 | method = "stitch", 13 | at = @At( 14 | value = "INVOKE", 15 | target = "Lorg/slf4j/Logger;warn(Ljava/lang/String;[Ljava/lang/Object;)V", 16 | remap = false 17 | ) 18 | ) 19 | private void curium_removeUselessLogMessage(Logger instance, String message, Object[] objects) { 20 | 21 | } 22 | 23 | @Redirect( 24 | method = "loadSprite", 25 | at = @At( 26 | value = "INVOKE", 27 | target = "Lorg/slf4j/Logger;error(Ljava/lang/String;Ljava/lang/Object;Ljava/lang/Object;)V", 28 | remap = false 29 | ) 30 | ) 31 | private static void curium_removeUselessLogMessage2(Logger instance, String message, Object o1, Object o2) { 32 | 33 | } 34 | 35 | @Redirect( 36 | method = "loadSprite", 37 | at = @At( 38 | value = "INVOKE", 39 | target = "Lorg/slf4j/Logger;error(Ljava/lang/String;[Ljava/lang/Object;)V", 40 | remap = false 41 | ) 42 | ) 43 | private static void curium_removeUselessLogMessage3(Logger instance, String message, Object[] objects) { 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/api/TPSLimiterImpl.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.api; 2 | 3 | import me.katie.curium.TPSLimiter; 4 | import me.katie.curium.impl.CuriumConstants; 5 | import net.minecraft.Util; 6 | 7 | public class TPSLimiterImpl implements TPSLimiter { 8 | private long tickStartTime; 9 | private long targetTickTime = 50; // 20TPS 10 | private long laggyTicks = 0; 11 | 12 | /** 13 | * Hook that runs before a tick. 14 | */ 15 | public void preTick() { 16 | this.tickStartTime = Util.getMillis(); 17 | } 18 | 19 | /** 20 | * Hook that runs after a tick and sleeps if needed to meet the tick deadline. 21 | */ 22 | public void postTick() { 23 | if (this.targetTickTime < 0) { 24 | return; 25 | } 26 | 27 | long tickEndTime = Util.getMillis(); 28 | long tickTime = tickEndTime - this.tickStartTime; 29 | long remainingBudget = this.targetTickTime - tickTime; 30 | 31 | if (remainingBudget >= 0) { 32 | this.laggyTicks = 0; 33 | 34 | // We have time left before the next deadline, sleep. 35 | try { 36 | Thread.sleep(remainingBudget); 37 | } catch (InterruptedException ignored) { 38 | 39 | } 40 | } else { 41 | // Tick took longer than our time budget! 42 | this.laggyTicks++; 43 | 44 | if ((this.laggyTicks % 20) == 0) { 45 | CuriumConstants.LOGGER.warn("{} consecutive ticks exceeded {}ms time budget! (this tick: {}ms)", this.laggyTicks, this.targetTickTime, tickTime); 46 | } 47 | } 48 | } 49 | 50 | @Override 51 | public void setTargetTickTime(long millis) { 52 | this.targetTickTime = millis; 53 | } 54 | 55 | @Override 56 | public long getTargetTickTime() { 57 | return this.targetTickTime; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/reduce_threads/ConnectionMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.reduce_threads; 2 | 3 | import net.minecraft.network.Connection; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.ModifyArg; 7 | 8 | @Mixin(Connection.class) 9 | public class ConnectionMixin { 10 | @ModifyArg( 11 | // NETWORK_WORKER_GROUP 12 | method = "method_10767", 13 | remap = false, 14 | index = 0, 15 | at = @At( 16 | value = "INVOKE", 17 | target = "Lio/netty/channel/nio/NioEventLoopGroup;(ILjava/util/concurrent/ThreadFactory;)V", 18 | remap = false 19 | ) 20 | ) 21 | private static int curium_capNettyThreads(int nThreads) { 22 | return 1; 23 | } 24 | 25 | @ModifyArg( 26 | // NETWORK_EPOLL_WORKER_GROUP 27 | method = "method_10765", 28 | remap = false, 29 | index = 0, 30 | at = @At( 31 | value = "INVOKE", 32 | target = "Lio/netty/channel/epoll/EpollEventLoopGroup;(ILjava/util/concurrent/ThreadFactory;)V", 33 | remap = false 34 | ) 35 | ) 36 | private static int curium_capNettyThreads1(int nThreads) { 37 | return 1; 38 | } 39 | 40 | @ModifyArg( 41 | // LOCAL_WORKER_GROUP 42 | method = "method_10766", 43 | remap = false, 44 | index = 0, 45 | at = @At( 46 | value = "INVOKE", 47 | target = "Lio/netty/channel/DefaultEventLoopGroup;(ILjava/util/concurrent/ThreadFactory;)V", 48 | remap = false 49 | ) 50 | ) 51 | private static int curium_capNettyThreads2(int nThreads) { 52 | return 1; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/audio/LibraryMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.audio; 2 | 3 | import com.mojang.blaze3d.audio.Channel; 4 | import com.mojang.blaze3d.audio.Library; 5 | import me.katie.curium.impl.CuriumConstants; 6 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 7 | import me.katie.curium.impl.util.Util; 8 | import org.jetbrains.annotations.Nullable; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Overwrite; 11 | 12 | import java.util.Collections; 13 | import java.util.List; 14 | import java.util.OptionalLong; 15 | 16 | @Mixin(Library.class) 17 | @StubClass 18 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 19 | public class LibraryMixin { 20 | @Overwrite 21 | private int getChannelCount() { 22 | return 30; 23 | } 24 | 25 | @Overwrite 26 | public static @Nullable String getDefaultDeviceName() { 27 | return null; 28 | } 29 | 30 | @Overwrite 31 | public String getCurrentDeviceName() { 32 | return CuriumConstants.USING_CURIUM; 33 | } 34 | 35 | @Overwrite 36 | public synchronized boolean hasDefaultDeviceChanged() { 37 | return false; 38 | } 39 | 40 | @Overwrite 41 | private static long openDeviceOrFallback(@Nullable String string) { 42 | return Util.stubbed(); 43 | } 44 | 45 | @Overwrite 46 | private static OptionalLong tryOpenDevice(@Nullable String string) { 47 | return Util.stubbed(); 48 | } 49 | 50 | @Overwrite 51 | public @Nullable Channel acquireChannel(Library.Pool pool) { 52 | return new Channel(-1); 53 | } 54 | 55 | @Overwrite 56 | public String getDebugString() { 57 | return CuriumConstants.USING_CURIUM; 58 | } 59 | 60 | @Overwrite 61 | public List getAvailableSoundDevices() { 62 | return Collections.emptyList(); 63 | } 64 | 65 | @Overwrite 66 | public boolean isCurrentDeviceDisconnected() { 67 | return false; 68 | } 69 | 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/performance/GameRendererMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.performance; 2 | 3 | import net.minecraft.client.Minecraft; 4 | import net.minecraft.client.gui.GuiGraphics; 5 | import net.minecraft.client.gui.components.toasts.ToastComponent; 6 | import net.minecraft.client.gui.screens.Screen; 7 | import net.minecraft.client.renderer.GameRenderer; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.ModifyVariable; 11 | import org.spongepowered.asm.mixin.injection.Redirect; 12 | 13 | @Mixin(GameRenderer.class) 14 | public abstract class GameRendererMixin { 15 | @Redirect( 16 | method = "render", 17 | at = @At( 18 | value = "INVOKE", 19 | target = "Lnet/minecraft/client/Minecraft;isWindowActive()Z" 20 | ) 21 | ) 22 | private boolean cerium_noPause(Minecraft instance) { 23 | return true; 24 | } 25 | 26 | @ModifyVariable( 27 | method = "render", 28 | at = @At("HEAD"), 29 | argsOnly = true, 30 | index = 4 31 | ) 32 | private boolean cerium_skipMostRendering(boolean x) { 33 | return false; 34 | } 35 | 36 | @Redirect( 37 | method = "render", 38 | at = @At( 39 | value = "FIELD", 40 | target = "Lnet/minecraft/client/Minecraft;screen:Lnet/minecraft/client/gui/screens/Screen;" 41 | ) 42 | ) 43 | private Screen cerium_skipScreenRendering(Minecraft instance) { 44 | return null; 45 | } 46 | 47 | @Redirect( 48 | method = "render", 49 | at = @At( 50 | value = "INVOKE", 51 | target = "Lnet/minecraft/client/gui/components/toasts/ToastComponent;render(Lnet/minecraft/client/gui/GuiGraphics;)V" 52 | ) 53 | ) 54 | private void cerium_skipToastRendering(ToastComponent instance, GuiGraphics guiGraphics) { 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/performance/MinecraftMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.performance; 2 | 3 | import net.minecraft.client.Minecraft; 4 | import net.minecraft.client.gui.GuiGraphics; 5 | import net.minecraft.client.main.GameConfig; 6 | import net.minecraft.client.multiplayer.ClientLevel; 7 | import net.minecraft.client.renderer.GameRenderer; 8 | import net.minecraft.util.profiling.ProfileResults; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import org.spongepowered.asm.mixin.injection.Redirect; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | @Mixin(Minecraft.class) 16 | public class MinecraftMixin { 17 | @Inject( 18 | method = "", 19 | at = @At("TAIL") 20 | ) 21 | private void curium_onInit(GameConfig gameConfig, CallbackInfo ci) { 22 | System.gc(); 23 | } 24 | 25 | @Redirect( 26 | method = "runTick", 27 | at = @At( 28 | value = "INVOKE", 29 | target = "Lnet/minecraft/client/Minecraft;renderFpsMeter(Lnet/minecraft/client/gui/GuiGraphics;Lnet/minecraft/util/profiling/ProfileResults;)V" 30 | ) 31 | ) 32 | private void curium_neverRenderFpsMeter(Minecraft instance, GuiGraphics guiGraphics, ProfileResults profileResults) { 33 | 34 | } 35 | 36 | @Redirect( 37 | method = "tick", 38 | at = @At( 39 | value = "INVOKE", 40 | target = "Lnet/minecraft/client/multiplayer/ClientLevel;animateTick(III)V" 41 | ) 42 | ) 43 | private void curium_skipAnimateTick(ClientLevel instance, int i, int j, int k) { 44 | 45 | } 46 | 47 | @Redirect( 48 | method = "tick", 49 | at = @At( 50 | value = "INVOKE", 51 | target = "Lnet/minecraft/client/renderer/GameRenderer;tick()V" 52 | ) 53 | ) 54 | private void curium_neverTickGameRenderer(GameRenderer instance) { 55 | 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/platform/GLXMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.platform; 2 | 3 | import com.mojang.blaze3d.platform.GLX; 4 | import com.mojang.blaze3d.platform.Window; 5 | import me.katie.curium.impl.CuriumConstants; 6 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 7 | import me.katie.curium.impl.api.CuriumImpl; 8 | import me.katie.curium.impl.util.Util; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Overwrite; 11 | import org.spongepowered.asm.mixin.Shadow; 12 | import oshi.SystemInfo; 13 | import oshi.hardware.CentralProcessor; 14 | 15 | import java.util.Locale; 16 | import java.util.function.LongSupplier; 17 | 18 | @Mixin(value = GLX.class, remap = false) 19 | @StubClass 20 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 21 | public class GLXMixin { 22 | @Shadow private static String cpuInfo; 23 | 24 | @Overwrite 25 | public static String getOpenGLVersionString() { 26 | return CuriumConstants.DESCRIPTION; 27 | } 28 | 29 | @Overwrite 30 | public static int _getRefreshRate(Window window) { 31 | return 60; 32 | } 33 | 34 | @Overwrite 35 | public static String _getLWJGLVersion() { 36 | return CuriumConstants.DESCRIPTION; 37 | } 38 | 39 | @Overwrite 40 | public static LongSupplier _initGlfw() { 41 | return System::nanoTime; 42 | } 43 | 44 | @Overwrite 45 | public static boolean _shouldClose(Window window) { 46 | return CuriumImpl.get().shouldClose; 47 | } 48 | 49 | @Overwrite 50 | public static void _init(int i, boolean bl) { 51 | try { 52 | CentralProcessor processor = new SystemInfo().getHardware().getProcessor(); 53 | int coreCount = processor.getLogicalProcessorCount(); 54 | String processorId = processor.getProcessorIdentifier().getName(); 55 | 56 | cpuInfo = String.format(Locale.ROOT, "%dx %s", coreCount, processorId).replaceAll("\\s+", " "); 57 | } catch (Throwable ignored) { 58 | } 59 | } 60 | 61 | @Overwrite 62 | public static void _renderCrosshair(int i, boolean bl, boolean bl2, boolean bl3) { 63 | Util.stubbed(); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # User-specific stuff 2 | .idea/ 3 | 4 | *.iml 5 | *.ipr 6 | *.iws 7 | 8 | # IntelliJ 9 | out/ 10 | # mpeltonen/sbt-idea plugin 11 | .idea_modules/ 12 | 13 | # JIRA plugin 14 | atlassian-ide-plugin.xml 15 | 16 | # Compiled class file 17 | *.class 18 | 19 | # Log file 20 | *.log 21 | 22 | # BlueJ files 23 | *.ctxt 24 | 25 | # Package Files # 26 | *.jar 27 | *.war 28 | *.nar 29 | *.ear 30 | *.zip 31 | *.tar.gz 32 | *.rar 33 | 34 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 35 | hs_err_pid* 36 | 37 | *~ 38 | 39 | # temporary files which can be created if a process still has a handle open of a deleted file 40 | .fuse_hidden* 41 | 42 | # KDE directory preferences 43 | .directory 44 | 45 | # Linux trash folder which might appear on any partition or disk 46 | .Trash-* 47 | 48 | # .nfs files are created when an open file is removed but is still being accessed 49 | .nfs* 50 | 51 | # General 52 | .DS_Store 53 | .AppleDouble 54 | .LSOverride 55 | 56 | # Icon must end with two \r 57 | Icon 58 | 59 | # Thumbnails 60 | ._* 61 | 62 | # Files that might appear in the root of a volume 63 | .DocumentRevisions-V100 64 | .fseventsd 65 | .Spotlight-V100 66 | .TemporaryItems 67 | .Trashes 68 | .VolumeIcon.icns 69 | .com.apple.timemachine.donotpresent 70 | 71 | # Directories potentially created on remote AFP share 72 | .AppleDB 73 | .AppleDesktop 74 | Network Trash Folder 75 | Temporary Items 76 | .apdisk 77 | 78 | # Windows thumbnail cache files 79 | Thumbs.db 80 | Thumbs.db:encryptable 81 | ehthumbs.db 82 | ehthumbs_vista.db 83 | 84 | # Dump file 85 | *.stackdump 86 | 87 | # Folder config file 88 | [Dd]esktop.ini 89 | 90 | # Recycle Bin used on file shares 91 | $RECYCLE.BIN/ 92 | 93 | # Windows Installer files 94 | *.cab 95 | *.msi 96 | *.msix 97 | *.msm 98 | *.msp 99 | 100 | # Windows shortcuts 101 | *.lnk 102 | 103 | .gradle 104 | build/ 105 | 106 | # Ignore Gradle GUI config 107 | gradle-app.setting 108 | 109 | # Cache of project 110 | .gradletasknamecache 111 | 112 | **/build/ 113 | 114 | # Common working directory 115 | run/ 116 | 117 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 118 | !gradle-wrapper.jar 119 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/platform/InputConstantsMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.platform; 2 | 3 | import com.mojang.blaze3d.platform.InputConstants; 4 | import me.katie.curium.impl.api.CuriumImpl; 5 | import me.katie.curium.impl.asm.mixin.annotations.CustomTransformer; 6 | import me.katie.curium.impl.asm.mixin.annotations.Erase; 7 | import me.katie.curium.impl.asm.mixin.transformers.StripLwjglLdcTransformer; 8 | import me.katie.curium.impl.util.Util; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Overwrite; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Redirect; 13 | 14 | import java.lang.invoke.MethodHandle; 15 | import java.lang.invoke.MethodHandles; 16 | import java.lang.invoke.MethodType; 17 | 18 | @Mixin(InputConstants.class) 19 | @Erase(methods = { 20 | "setupKeyboardCallbacks(JLorg/lwjgl/glfw/GLFWKeyCallbackI;Lorg/lwjgl/glfw/GLFWCharModsCallbackI;)V", 21 | "setupMouseCallbacks(JLorg/lwjgl/glfw/GLFWCursorPosCallbackI;Lorg/lwjgl/glfw/GLFWMouseButtonCallbackI;Lorg/lwjgl/glfw/GLFWScrollCallbackI;Lorg/lwjgl/glfw/GLFWDropCallbackI;)V", 22 | }) 23 | @CustomTransformer(StripLwjglLdcTransformer.class) 24 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 25 | public class InputConstantsMixin { 26 | @Overwrite 27 | public static boolean isKeyDown(long window, int key) { 28 | return CuriumImpl.get().fakeKeyboard.isPressed(key); 29 | } 30 | 31 | @Overwrite 32 | public static void grabOrReleaseMouse(long l, int i, double d, double e) { 33 | 34 | } 35 | 36 | @Overwrite 37 | public static boolean isRawMouseInputSupported() { 38 | return false; 39 | } 40 | 41 | @Overwrite 42 | public static void updateRawMouseInput(long l, boolean bl) { 43 | Util.stubbed(); 44 | } 45 | 46 | // MCDev isn't happy with this injector, but it works. 47 | @SuppressWarnings({"MixinAnnotationTarget", "InvalidInjectorMethodSignature", "UnresolvedMixinReference"}) 48 | @Redirect( 49 | method = "", 50 | at = @At( 51 | value = "INVOKE", 52 | target = "Ljava/lang/invoke/MethodHandles$Lookup;findStatic(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/invoke/MethodType;)Ljava/lang/invoke/MethodHandle;", 53 | remap = false 54 | ) 55 | ) 56 | private static MethodHandle curium_dontFindGlfwMethodHandle(MethodHandles.Lookup instance, Class clazz, String name, MethodType type) throws NoSuchMethodException { 57 | throw new NoSuchMethodException(name); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/input/KeyboardKey.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.input; 2 | 3 | /** 4 | * Keys that can be pressed on a keyboard (see {@link FakeKeyboard}). 5 | */ 6 | public enum KeyboardKey { 7 | SPACE(32), 8 | APOSTROPHE(39), 9 | COMMA(44), 10 | MINUS(45), 11 | PERIOD(46), 12 | SLASH(47), 13 | NUM_0(48), 14 | NUM_1(49), 15 | NUM_2(50), 16 | NUM_3(51), 17 | NUM_4(52), 18 | NUM_5(53), 19 | NUM_6(54), 20 | NUM_7(55), 21 | NUM_8(56), 22 | NUM_9(57), 23 | SEMICOLON(59), 24 | EQUAL(61), 25 | A(65), 26 | B(66), 27 | C(67), 28 | D(68), 29 | E(69), 30 | F(70), 31 | G(71), 32 | H(72), 33 | I(73), 34 | J(74), 35 | K(75), 36 | L(76), 37 | M(77), 38 | N(78), 39 | O(79), 40 | P(80), 41 | Q(81), 42 | R(82), 43 | S(83), 44 | T(84), 45 | U(85), 46 | V(86), 47 | W(87), 48 | X(88), 49 | Y(89), 50 | Z(90), 51 | LEFT_BRACKET(91), 52 | BACKSLASH(92), 53 | RIGHT_BRACKET(93), 54 | GRAVE_ACCENT(96), 55 | WORLD_1(161), 56 | WORLD_2(162), 57 | ESCAPE(256), 58 | ENTER(257), 59 | TAB(258), 60 | BACKSPACE(259), 61 | INSERT(260), 62 | DELETE(261), 63 | RIGHT(262), 64 | LEFT(263), 65 | DOWN(264), 66 | UP(265), 67 | PAGE_UP(266), 68 | PAGE_DOWN(267), 69 | HOME(268), 70 | END(269), 71 | CAPS_LOCK(280), 72 | SCROLL_LOCK(281), 73 | NUM_LOCK(282), 74 | PRINT_SCREEN(283), 75 | PAUSE(284), 76 | F1(290), 77 | F2(291), 78 | F3(292), 79 | F4(293), 80 | F5(294), 81 | F6(295), 82 | F7(296), 83 | F8(297), 84 | F9(298), 85 | F10(299), 86 | F11(300), 87 | F12(301), 88 | F13(302), 89 | F14(303), 90 | F15(304), 91 | F16(305), 92 | F17(306), 93 | F18(307), 94 | F19(308), 95 | F20(309), 96 | F21(310), 97 | F22(311), 98 | F23(312), 99 | F24(313), 100 | F25(314), 101 | KP_0(320), 102 | KP_1(321), 103 | KP_2(322), 104 | KP_3(323), 105 | KP_4(324), 106 | KP_5(325), 107 | KP_6(326), 108 | KP_7(327), 109 | KP_8(328), 110 | KP_9(329), 111 | KP_DECIMAL(330), 112 | KP_DIVIDE(331), 113 | KP_MULTIPLY(332), 114 | KP_SUBTRACT(333), 115 | KP_ADD(334), 116 | KP_ENTER(335), 117 | KP_EQUAL(336), 118 | LEFT_SHIFT(340), 119 | LEFT_CONTROL(341), 120 | LEFT_ALT(342), 121 | LEFT_SUPER(343), 122 | RIGHT_SHIFT(344), 123 | RIGHT_CONTROL(345), 124 | RIGHT_ALT(346), 125 | RIGHT_SUPER(347), 126 | MENU(348); 127 | 128 | public final int lwjglIndex; 129 | 130 | KeyboardKey(int lwjglIndex) { 131 | this.lwjglIndex = lwjglIndex; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/api/FakeKeyboardImpl.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.api; 2 | 3 | import it.unimi.dsi.fastutil.ints.Int2ObjectMap; 4 | import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap; 5 | import me.katie.curium.input.FakeKeyboard; 6 | import me.katie.curium.input.InputAction; 7 | import me.katie.curium.input.InputModifiers; 8 | import me.katie.curium.input.KeyboardKey; 9 | import me.katie.curium.impl.mixin.core.minecraft.KeyboardHandlerAccessor; 10 | import net.minecraft.client.Minecraft; 11 | 12 | public class FakeKeyboardImpl implements FakeKeyboard { 13 | public final Int2ObjectMap keyStates = new Int2ObjectOpenHashMap<>(); 14 | private boolean capsLock = false; 15 | private boolean numLock = false; 16 | 17 | public boolean isPressed(KeyboardKey... keys) { 18 | for (KeyboardKey key: keys) { 19 | if (this.isPressed(key.lwjglIndex)) { 20 | return true; 21 | } 22 | } 23 | 24 | return false; 25 | } 26 | 27 | public boolean isPressed(int key) { 28 | return this.keyStates.getOrDefault(key, InputAction.Release) == InputAction.Press; 29 | } 30 | 31 | private int generateModifiers() { 32 | int flags = 0; 33 | 34 | if (this.isPressed(KeyboardKey.LEFT_SHIFT, KeyboardKey.RIGHT_SHIFT)) { 35 | flags |= InputModifiers.SHIFT; 36 | } 37 | 38 | if (this.isPressed(KeyboardKey.LEFT_CONTROL, KeyboardKey.RIGHT_CONTROL)) { 39 | flags |= InputModifiers.CONTROL; 40 | } 41 | 42 | if (this.isPressed(KeyboardKey.LEFT_ALT, KeyboardKey.RIGHT_ALT)) { 43 | flags |= InputModifiers.ALT; 44 | } 45 | 46 | if (this.isPressed(KeyboardKey.LEFT_SUPER, KeyboardKey.RIGHT_SUPER)) { 47 | flags |= InputModifiers.SUPER; 48 | } 49 | 50 | if (this.capsLock) { 51 | flags |= InputModifiers.CAPS_LOCK; 52 | } 53 | 54 | if (this.numLock) { 55 | flags |= InputModifiers.NUM_LOCK; 56 | } 57 | 58 | return flags; 59 | } 60 | 61 | public void update(KeyboardKey key, InputAction action) { 62 | if (action == InputAction.Press || action == InputAction.Release) { 63 | keyStates.put(key.lwjglIndex, action); 64 | } 65 | 66 | if (action == InputAction.Press || action == InputAction.Repeat) { 67 | if (key == KeyboardKey.CAPS_LOCK) { 68 | this.capsLock = !this.capsLock; 69 | } 70 | 71 | if (key == KeyboardKey.NUM_LOCK) { 72 | this.numLock = !this.numLock; 73 | } 74 | } 75 | 76 | int mods = this.generateModifiers(); 77 | Minecraft.getInstance().keyboardHandler.keyPress(0, key.lwjglIndex, -1, action.ordinal(), mods); 78 | } 79 | 80 | public void type(int codepoint, InputModifiers modifiers) { 81 | KeyboardHandlerAccessor accessor = (KeyboardHandlerAccessor) Minecraft.getInstance().keyboardHandler; 82 | accessor.callCharTyped(0, codepoint, modifiers.toRaw()); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/vertex/BufferBuilderMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.vertex; 2 | 3 | import com.mojang.blaze3d.vertex.BufferBuilder; 4 | import com.mojang.blaze3d.vertex.VertexFormat; 5 | import it.unimi.dsi.fastutil.ints.IntConsumer; 6 | import me.katie.curium.impl.asm.mixin.annotations.OverwriteCtor; 7 | import org.joml.Vector3f; 8 | import org.objectweb.asm.Opcodes; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.Overwrite; 11 | import org.spongepowered.asm.mixin.Shadow; 12 | import org.spongepowered.asm.mixin.injection.At; 13 | import org.spongepowered.asm.mixin.injection.Redirect; 14 | 15 | import java.nio.ByteBuffer; 16 | 17 | @Mixin(BufferBuilder.class) 18 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 19 | public class BufferBuilderMixin { 20 | @Shadow 21 | private ByteBuffer buffer; 22 | 23 | @Shadow private boolean building; 24 | 25 | @OverwriteCtor 26 | public void curium_overwriteCtor(int i) { 27 | this.buffer = null; 28 | this.building = false; 29 | } 30 | 31 | @Overwrite 32 | private void ensureCapacity(int i) { 33 | 34 | } 35 | 36 | @Redirect( 37 | method = { 38 | "restoreSortState", "begin" 39 | }, 40 | at = @At( 41 | value = "INVOKE", 42 | target = "Ljava/nio/ByteBuffer;rewind()Ljava/nio/ByteBuffer;" 43 | ) 44 | ) 45 | private ByteBuffer curium_rewindNop(ByteBuffer instance) { 46 | return instance; 47 | } 48 | 49 | 50 | @Redirect( 51 | method = "endVertex", 52 | at = @At( 53 | value = "INVOKE", 54 | target = "Ljava/nio/ByteBuffer;put(ILjava/nio/ByteBuffer;II)Ljava/nio/ByteBuffer;" 55 | ) 56 | ) 57 | private ByteBuffer curium_putNop(ByteBuffer instance, int index, ByteBuffer src, int offset, int length) { 58 | return instance; 59 | } 60 | 61 | @Redirect( 62 | method = "begin", 63 | at = @At( 64 | value = "FIELD", 65 | opcode = Opcodes.GETFIELD, 66 | target = "Lcom/mojang/blaze3d/vertex/BufferBuilder;building:Z" 67 | ) 68 | ) 69 | private boolean curium_neverThrowAlreadyBuilding(BufferBuilder instance) { 70 | return false; 71 | } 72 | 73 | @Overwrite 74 | private IntConsumer intConsumer(int i2, VertexFormat.IndexType indexType) { 75 | return _x -> {}; 76 | } 77 | 78 | @Overwrite 79 | private Vector3f[] makeQuadSortingPoints() { 80 | return new Vector3f[] {}; 81 | } 82 | 83 | @Overwrite 84 | public void putByte(int i, byte b) { 85 | 86 | } 87 | 88 | @Overwrite 89 | public void putShort(int i, short s) { 90 | 91 | } 92 | 93 | @Overwrite 94 | public void putFloat(int i, float f) { 95 | 96 | } 97 | 98 | @Overwrite 99 | public ByteBuffer bufferSlice(int i, int j) { 100 | return this.buffer; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/platform/GlStateManagerMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.platform; 2 | 3 | import com.mojang.blaze3d.platform.GlStateManager; 4 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 5 | import org.jetbrains.annotations.Nullable; 6 | import org.spongepowered.asm.mixin.Mixin; 7 | import org.spongepowered.asm.mixin.Overwrite; 8 | 9 | import java.nio.ByteBuffer; 10 | 11 | @Mixin(value = GlStateManager.class, remap = false) 12 | @StubClass 13 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 14 | public class GlStateManagerMixin { 15 | @Overwrite 16 | public static int glGetProgrami(int i, int j) { 17 | return 1337; 18 | } 19 | 20 | @Overwrite 21 | public static int glCreateShader(int i) { 22 | return 1337; 23 | } 24 | 25 | @Overwrite 26 | public static int glGetShaderi(int i, int j) { 27 | return 1337; 28 | } 29 | 30 | @Overwrite 31 | public static int glCreateProgram() { 32 | return 1337; 33 | } 34 | 35 | @Overwrite 36 | public static int _glGetUniformLocation(int i, CharSequence charSequence) { 37 | return 1337; 38 | } 39 | 40 | @Overwrite 41 | public static int _glGetAttribLocation(int i, CharSequence charSequence) { 42 | return 1337; 43 | } 44 | 45 | @Overwrite 46 | public static int _glGenBuffers() { 47 | return 1337; 48 | } 49 | 50 | @Overwrite 51 | public static int _glGenVertexArrays() { 52 | return 1337; 53 | } 54 | 55 | @Overwrite 56 | @Nullable 57 | @SuppressWarnings("DataFlowIssue") 58 | public static ByteBuffer _glMapBuffer(int i, int j) { 59 | return ByteBuffer.allocate(1024); 60 | } 61 | 62 | @Overwrite 63 | public static int glGenFramebuffers() { 64 | return 1337; 65 | } 66 | 67 | @Overwrite 68 | public static int glGenRenderbuffers() { 69 | return 1337; 70 | } 71 | 72 | @Overwrite 73 | public static int glCheckFramebufferStatus(int i) { 74 | return 1337; 75 | } 76 | 77 | @Overwrite 78 | public static int getBoundFramebuffer() { 79 | return 1337; 80 | } 81 | 82 | @Overwrite 83 | public static String glGetShaderInfoLog(int i, int j) { 84 | return ""; 85 | } 86 | 87 | @Overwrite 88 | public static String glGetProgramInfoLog(int i, int j) { 89 | return ""; 90 | } 91 | 92 | @Overwrite 93 | public static int _getTexLevelParameter(int i, int j, int k) { 94 | return 1337; 95 | } 96 | 97 | @Overwrite 98 | public static int _genTexture() { 99 | return 1337; 100 | } 101 | 102 | @Overwrite 103 | public static int _getActiveTexture() { 104 | return 1337; 105 | } 106 | 107 | @Overwrite 108 | public static int _getError() { 109 | return 0; 110 | } 111 | 112 | @Overwrite 113 | public static String _getString(int i) { 114 | return ""; 115 | } 116 | 117 | @Overwrite 118 | public static int _getInteger(int i) { 119 | return 1337; 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /src/main/resources/curium.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "me.katie.curium.impl.mixin", 5 | "compatibilityLevel": "JAVA_17", 6 | "plugin": "me.katie.curium.impl.asm.mixin.CuriumMixinPlugin", 7 | "mixins": [ 8 | "performance.ReloadableResourceManagerMixin" 9 | ], 10 | "client": [ 11 | "core.blaze3d.Blaze3DMixin", 12 | "core.blaze3d.audio.ChannelMixin", 13 | "core.blaze3d.audio.LibraryMixin", 14 | "core.blaze3d.audio.ListenerMixin", 15 | "core.blaze3d.audio.OggAudioStreamMixin", 16 | "core.blaze3d.audio.OpenAlUtilMixin", 17 | "core.blaze3d.audio.SoundBufferLibraryMixin", 18 | "core.blaze3d.audio.SoundBufferMixin", 19 | "core.blaze3d.font.TrueTypeGlyphProviderMixin", 20 | "core.blaze3d.pipeline.MainTargetMixin", 21 | "core.blaze3d.pipeline.RenderTargetMixin", 22 | "core.blaze3d.platform.ClipboardManagerMixin", 23 | "core.blaze3d.platform.DebugMemoryUntrackerMixin", 24 | "core.blaze3d.platform.GlDebugMixin", 25 | "core.blaze3d.platform.GlStateManagerMixin", 26 | "core.blaze3d.platform.GlUtilMixin", 27 | "core.blaze3d.platform.GLXMixin", 28 | "core.blaze3d.platform.InputConstants_TypeMixin", 29 | "core.blaze3d.platform.InputConstantsMixin", 30 | "core.blaze3d.platform.MacosUtilMixin", 31 | "core.blaze3d.platform.MemoryTrackerMixin", 32 | "core.blaze3d.platform.MonitorMixin", 33 | "core.blaze3d.platform.NativeImageMixin", 34 | "core.blaze3d.platform.ScreenManagerMixin", 35 | "core.blaze3d.platform.TextureUtilMixin", 36 | "core.blaze3d.platform.VideoModeMixin", 37 | "core.blaze3d.platform.WindowMixin", 38 | "core.blaze3d.shaders.UniformMixin", 39 | "core.blaze3d.systems.RenderSystem_AutoStorageIndexBufferMixin", 40 | "core.blaze3d.systems.RenderSystemMixin", 41 | "core.blaze3d.systems.TimerQuery_FrameProfileMixin", 42 | "core.blaze3d.systems.TimerQuery_TimerQueryLazyLoaderMixin", 43 | "core.blaze3d.systems.TimerQueryMixin", 44 | "core.blaze3d.vertex.BufferBuilderMixin", 45 | "core.blaze3d.vertex.TesselatorMixin", 46 | "core.blaze3d.vertex.VertexBufferMixin", 47 | "core.blaze3d.vertex.VertexConsumerMixin", 48 | "core.minecraft.KeyboardHandlerAccessor", 49 | "core.minecraft.KeyboardHandlerMixin", 50 | "core.minecraft.MainMixin", 51 | "core.minecraft.MinecraftMixin", 52 | "core.minecraft.MouseHandlerAccessor", 53 | "core.minecraft.MouseHandlerMixin", 54 | "core.minecraft.OptionsMixin", 55 | "core.minecraft.font.TrueTypeGlyphProviderBuilderMixin", 56 | "core.minecraft.renderer.VirtualScreenMixin", 57 | "core.minecraft.renderer.culling.FrustumMixin", 58 | "core.minecraft.renderer.texture.SpriteContentsMixin", 59 | "core.minecraft.renderer.texture.SpriteLoaderMixin", 60 | "core.minecraft.renderer.texture.TextureAtlasMixin", 61 | "core.minecraft.screen.ReceivingLevelScreenMixin", 62 | "performance.GameRendererMixin", 63 | "performance.MinecraftMixin", 64 | "performance.ModelManagerMixin", 65 | "performance.ParticleEngineMixin", 66 | "reduce_threads.ConnectionMixin", 67 | "reduce_threads.MainMixin", 68 | "reduce_threads.SharedConstantsMixin", 69 | "reduce_threads.SoundEngineExecutorMixin", 70 | "reduce_threads.UtilMixin" 71 | ], 72 | "injectors": { 73 | "defaultRequire": 1 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/asm/mixin/CuriumMixinPlugin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.asm.mixin; 2 | 3 | import me.katie.curium.impl.CuriumProperties; 4 | import me.katie.curium.impl.asm.CuriumASM; 5 | import me.katie.curium.impl.asm.mixin.transformers.CustomTransformerHandler; 6 | import me.katie.curium.impl.asm.mixin.transformers.EraseHandler; 7 | import me.katie.curium.impl.asm.mixin.transformers.OverwriteCtorHandler; 8 | import me.katie.curium.impl.asm.mixin.transformers.StubClassHandler; 9 | import me.katie.curium.impl.util.Util; 10 | import org.objectweb.asm.tree.ClassNode; 11 | import org.objectweb.asm.util.CheckClassAdapter; 12 | import org.spongepowered.asm.mixin.extensibility.IMixinConfigPlugin; 13 | import org.spongepowered.asm.mixin.extensibility.IMixinInfo; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.Set; 18 | 19 | /** 20 | * Bootstrapper for {@link CuriumASM} and class transformer applicator. 21 | */ 22 | public final class CuriumMixinPlugin implements IMixinConfigPlugin { 23 | private static final List TRANSFORMERS = Util.make(new ArrayList<>(), l -> { 24 | l.add(new EraseHandler()); 25 | l.add(new StubClassHandler()); 26 | l.add(new OverwriteCtorHandler()); 27 | l.add(new CustomTransformerHandler()); 28 | }); 29 | 30 | @Override 31 | public void onLoad(String mixinPackage) { 32 | try { 33 | CuriumASM.init(); 34 | } catch (Throwable e) { 35 | CuriumASM.LOGGER.error("CuriumASM initalization failed", e); 36 | } 37 | } 38 | 39 | @Override 40 | public String getRefMapperConfig() { 41 | return null; 42 | } 43 | 44 | @Override 45 | public boolean shouldApplyMixin(String targetClassName, String mixinClassName) { 46 | return true; 47 | } 48 | 49 | @Override 50 | public void acceptTargets(Set myTargets, Set otherTargets) { 51 | 52 | } 53 | 54 | @Override 55 | public List getMixins() { 56 | return null; 57 | } 58 | 59 | @Override 60 | public void preApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { 61 | ClassNode mixinClass = mixinInfo.getClassNode(0); 62 | 63 | for (ClassTransformer transformer: TRANSFORMERS) { 64 | transformer.preMixinTransform(targetClass, mixinClass); 65 | } 66 | 67 | if (CuriumProperties.ASM_CHECK) { 68 | CheckClassAdapter cca = new CheckClassAdapter(null, false); 69 | targetClass.accept(cca); 70 | } 71 | 72 | CuriumASM.LOGGER.debug("Applied pre-mixin transformers to {}", targetClassName); 73 | } 74 | 75 | @Override 76 | public void postApply(String targetClassName, ClassNode targetClass, String mixinClassName, IMixinInfo mixinInfo) { 77 | ClassNode mixinClass = mixinInfo.getClassNode(0); 78 | 79 | for (ClassTransformer transformer: TRANSFORMERS) { 80 | transformer.postMixinTransform(targetClass, mixinClass); 81 | } 82 | 83 | if (CuriumProperties.ASM_CHECK) { 84 | CheckClassAdapter cca = new CheckClassAdapter(null, false); 85 | targetClass.accept(cca); 86 | } 87 | 88 | CuriumASM.LOGGER.debug("Applied post-mixin transformers to {}", targetClassName); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/asm/util/ASMUtil.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.asm.util; 2 | 3 | import me.katie.curium.impl.asm.mixin.CuriumMixinPlugin; 4 | import org.objectweb.asm.ClassReader; 5 | import org.objectweb.asm.Opcodes; 6 | import org.objectweb.asm.Type; 7 | import org.objectweb.asm.tree.*; 8 | 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.lang.invoke.MethodType; 12 | 13 | /** 14 | * General helper methods for working with ASM. 15 | */ 16 | public class ASMUtil { 17 | private ASMUtil() { 18 | throw new UnsupportedOperationException("Cannot instantiate AsmHelper"); 19 | } 20 | 21 | /** 22 | * Generates a series of instructions to throw an {@link AssertionError} with a given message. 23 | */ 24 | public static InsnList generateFailedAssert(String reason) { 25 | StringBuilder sb = new StringBuilder("curium: assertion failed"); 26 | 27 | if (reason != null) { 28 | sb.append('('); 29 | sb.append(reason); 30 | sb.append(')'); 31 | } 32 | 33 | // Generate instructions to throw an AssertionError. 34 | InsnList insns = new InsnList(); 35 | 36 | insns.add(new TypeInsnNode(Opcodes.NEW, "java/lang/AssertionError")); 37 | insns.add(new InsnNode(Opcodes.DUP)); 38 | insns.add(new LdcInsnNode(sb.toString())); 39 | insns.add(new MethodInsnNode( 40 | Opcodes.INVOKESPECIAL, 41 | "java/lang/AssertionError", 42 | "", 43 | "(Ljava/lang/String;)V" 44 | )); 45 | insns.add(new InsnNode(Opcodes.ATHROW)); 46 | 47 | return insns; 48 | } 49 | 50 | /** 51 | * Gets an ASM {@link Type} from a JVM {@link MethodType}. 52 | */ 53 | public static Type methodTypeToAsmType(MethodType type) { 54 | Type returnType = Type.getType(type.returnType()); 55 | Class[] parameterTypes = type.parameterArray(); 56 | Type[] asmParameterTypes = new Type[parameterTypes.length]; 57 | 58 | for (int i = 0; i < parameterTypes.length; i++) { 59 | asmParameterTypes[i] = Type.getType(parameterTypes[i]); 60 | } 61 | 62 | return Type.getMethodType(returnType, asmParameterTypes); 63 | } 64 | 65 | /** 66 | * Gets the bytecode of a class from Knot (with resource). 67 | */ 68 | public static byte[] getClassBytes(String name) { 69 | try (InputStream is = CuriumMixinPlugin.class.getClassLoader().getResourceAsStream(name.replace('.', '/') + ".class")) { 70 | if (is == null) { 71 | throw new IllegalStateException("Failed to find class " + name + "on Knot"); 72 | } 73 | 74 | return is.readAllBytes(); 75 | } catch (IOException e) { 76 | throw new IllegalStateException("IO exception while reading " + name + " from Knot", e); 77 | } 78 | } 79 | 80 | /** 81 | * Gets the bytecode of a class from Knot (with resource) and converts it to an ASM class node. 82 | */ 83 | public static ClassNode getClassNode(String name) { 84 | byte[] buf = getClassBytes(name); 85 | ClassReader reader = new ClassReader(buf); 86 | ClassNode clazz = new ClassNode(); 87 | reader.accept(clazz, ClassReader.EXPAND_FRAMES); 88 | 89 | return clazz; 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/asm/mixin/transformers/StubClassHandler.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.asm.mixin.transformers; 2 | 3 | import me.katie.curium.impl.asm.util.ASMUtil; 4 | import me.katie.curium.impl.asm.CuriumASM; 5 | import me.katie.curium.impl.asm.mixin.ClassTransformer; 6 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 7 | import me.katie.curium.impl.asm.mixin.helper.StubInternalHelper; 8 | import org.objectweb.asm.Opcodes; 9 | import org.objectweb.asm.Type; 10 | import org.objectweb.asm.tree.*; 11 | import org.spongepowered.asm.util.Annotations; 12 | 13 | import java.util.ArrayList; 14 | import java.util.Iterator; 15 | import java.util.List; 16 | 17 | /** 18 | * Handles the {@link StubClass} annotation. 19 | */ 20 | public class StubClassHandler implements ClassTransformer { 21 | @Override 22 | public void preMixinTransform(ClassNode target, ClassNode mixin) { 23 | // Check annotation. 24 | AnnotationNode annotation = Annotations.getInvisible(mixin, StubClass.class); 25 | 26 | if (annotation == null) { 27 | return; 28 | } 29 | 30 | boolean isThrowing = Annotations.getValue(annotation, "isThrowing", Boolean.FALSE); 31 | List skippedMethods = Annotations.getValue(annotation, "skip", new ArrayList<>()); 32 | 33 | // Overwrite any void methods in the target with empty bodies. 34 | List newMethods = new ArrayList<>(); 35 | 36 | for (Iterator it = target.methods.iterator(); it.hasNext();) { 37 | MethodNode method = it.next(); 38 | 39 | Type desc = Type.getMethodType(method.desc); 40 | boolean doesApply = (!method.name.equals("") || isThrowing) && 41 | (isThrowing || desc.getReturnType() == Type.VOID_TYPE) && 42 | !skippedMethods.contains(method.name + method.desc) && 43 | !skippedMethods.contains(method.name) && 44 | (method.access & (Opcodes.ACC_NATIVE | Opcodes.ACC_ABSTRACT)) == 0; 45 | 46 | if (doesApply) { 47 | it.remove(); 48 | 49 | MethodNode fakeMethod = new MethodNode(); 50 | fakeMethod.name = method.name; 51 | fakeMethod.desc = method.desc; 52 | fakeMethod.access = method.access; 53 | fakeMethod.invisibleAnnotations = method.invisibleAnnotations; 54 | fakeMethod.visibleAnnotations = method.visibleAnnotations; 55 | fakeMethod.instructions = new InsnList(); 56 | 57 | if (isThrowing && !method.name.equals("")) { 58 | fakeMethod.instructions.add(new MethodInsnNode( 59 | Opcodes.INVOKESTATIC, 60 | Type.getInternalName(StubInternalHelper.class), 61 | "stubbed", 62 | "()V" 63 | )); 64 | 65 | fakeMethod.instructions.add(ASMUtil.generateFailedAssert("StubInternalHelper#stubbed didn't throw")); 66 | } else { 67 | fakeMethod.instructions.add(new InsnNode(Opcodes.RETURN)); 68 | } 69 | 70 | newMethods.add(fakeMethod); 71 | } 72 | } 73 | 74 | target.methods.addAll(newMethods); 75 | CuriumASM.LOGGER.debug("Stripped {} void method bodies from {}", newMethods.size(), target.name); 76 | } 77 | 78 | @Override 79 | public void postMixinTransform(ClassNode target, ClassNode mixin) { 80 | 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/platform/NativeImageMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.platform; 2 | 3 | import com.mojang.blaze3d.platform.NativeImage; 4 | import me.katie.curium.impl.asm.mixin.annotations.Erase; 5 | import me.katie.curium.impl.asm.mixin.annotations.OverwriteCtor; 6 | import me.katie.curium.impl.asm.mixin.annotations.StubClass; 7 | import org.jetbrains.annotations.Nullable; 8 | import org.spongepowered.asm.mixin.*; 9 | 10 | import java.io.InputStream; 11 | import java.nio.ByteBuffer; 12 | import java.util.function.IntUnaryOperator; 13 | 14 | @Mixin(NativeImage.class) 15 | @StubClass 16 | @Erase(methods = "writeToChannel(Ljava/nio/channels/WritableByteChannel;)Z") 17 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 18 | public class NativeImageMixin { 19 | @Mutable 20 | @Shadow @Final private NativeImage.Format format; 21 | 22 | @Mutable 23 | @Shadow @Final private int width; 24 | 25 | @Mutable 26 | @Shadow @Final private int height; 27 | 28 | @OverwriteCtor 29 | private void curium_overwriteCtor(NativeImage.Format format, int i, int j, boolean bl) { 30 | this.format = format; 31 | this.width = i; 32 | this.height = j; 33 | } 34 | 35 | @OverwriteCtor 36 | private void curium_overwriteCtor(NativeImage.Format format, int i, int j, boolean bl, long l) { 37 | this.format = format; 38 | this.width = i; 39 | this.height = j; 40 | } 41 | 42 | @Overwrite(remap = false) 43 | public String toString() { 44 | return "CuriumNativeImage"; 45 | } 46 | 47 | @Overwrite 48 | public static NativeImage read(InputStream inputStream) { 49 | return new NativeImage(-1, -1, false); 50 | } 51 | 52 | @Overwrite 53 | public static NativeImage read(@Nullable NativeImage.Format format, InputStream inputStream) { 54 | if (format == null) { 55 | format = NativeImage.Format.RGBA; 56 | } 57 | 58 | return new NativeImage(format, -1, -1, false); 59 | } 60 | 61 | @Overwrite 62 | public static NativeImage read(ByteBuffer byteBuffer) { 63 | return new NativeImage(-1, -1, false); 64 | } 65 | 66 | @Overwrite 67 | public static NativeImage read(byte[] bs) { 68 | return new NativeImage(-1, -1, false); 69 | } 70 | 71 | @Overwrite 72 | public static NativeImage read(@Nullable NativeImage.Format format, ByteBuffer byteBuffer) { 73 | if (format == null) { 74 | format = NativeImage.Format.RGBA; 75 | } 76 | 77 | return new NativeImage(format, -1, -1, false); 78 | } 79 | 80 | @Overwrite 81 | public int getPixelRGBA(int i, int j) { 82 | return 0; 83 | } 84 | 85 | @Overwrite 86 | public NativeImage mappedCopy(IntUnaryOperator intUnaryOperator) { 87 | return (NativeImage) (Object) this; 88 | } 89 | 90 | @Overwrite 91 | public int[] getPixelsRGBA() { 92 | return new int[] {}; 93 | } 94 | 95 | @Overwrite 96 | public byte getRedOrLuminance(int i, int j) { 97 | return 0; 98 | } 99 | 100 | @Overwrite 101 | public byte getGreenOrLuminance(int i, int j) { 102 | return 0; 103 | } 104 | 105 | @Overwrite 106 | public byte getBlueOrLuminance(int i, int j) { 107 | return 0; 108 | } 109 | 110 | @Overwrite 111 | public byte getLuminanceOrAlpha(int i, int j) { 112 | return 0; 113 | } 114 | 115 | @Deprecated 116 | @Overwrite 117 | public int[] makePixelArray() { 118 | return new int[] {}; 119 | } 120 | 121 | @Overwrite 122 | public byte[] asByteArray() { 123 | return new byte[] {}; 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/blaze3d/systems/RenderSystemMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.blaze3d.systems; 2 | 3 | import com.mojang.blaze3d.pipeline.RenderCall; 4 | import com.mojang.blaze3d.systems.RenderSystem; 5 | import me.katie.curium.impl.CuriumConstants; 6 | import me.katie.curium.impl.asm.mixin.annotations.Erase; 7 | import org.spongepowered.asm.mixin.Final; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Overwrite; 10 | import org.spongepowered.asm.mixin.Shadow; 11 | import org.spongepowered.asm.mixin.injection.At; 12 | import org.spongepowered.asm.mixin.injection.Redirect; 13 | 14 | import java.util.concurrent.ConcurrentLinkedQueue; 15 | 16 | @Mixin(value = RenderSystem.class, remap = false) 17 | @Erase(fields = { 18 | "isReplayingQueue", "gameThread", "renderThread", "MAX_SUPPORTED_TEXTURE_SIZE", "isInInit" 19 | }) 20 | @SuppressWarnings({"overwrite", "OverwriteAuthorRequired"}) 21 | public class RenderSystemMixin { 22 | @Shadow 23 | @Final 24 | private static ConcurrentLinkedQueue recordingQueue; 25 | 26 | @Redirect( 27 | method = "", 28 | at = @At( 29 | value = "FIELD", 30 | target = "Lcom/mojang/blaze3d/systems/RenderSystem;MAX_SUPPORTED_TEXTURE_SIZE:I" 31 | ) 32 | ) 33 | private static void curium_removeMaxSupportedTextureSizeField(int value) { 34 | 35 | } 36 | 37 | @Overwrite 38 | public static void initRenderThread() { 39 | 40 | } 41 | 42 | @Overwrite 43 | public static boolean isOnRenderThread() { 44 | return true; 45 | } 46 | 47 | @Overwrite 48 | public static boolean isOnRenderThreadOrInit() { 49 | return true; 50 | } 51 | 52 | @Overwrite 53 | public static void initGameThread(boolean bl) { 54 | 55 | } 56 | 57 | @Overwrite 58 | public static boolean isOnGameThread() { 59 | return true; 60 | } 61 | 62 | @Overwrite 63 | public static void assertInInitPhase() { 64 | 65 | } 66 | 67 | @Overwrite 68 | public static void assertOnGameThreadOrInit() { 69 | 70 | } 71 | 72 | @Overwrite 73 | public static void assertOnRenderThreadOrInit() { 74 | 75 | } 76 | 77 | @Overwrite 78 | public static void assertOnRenderThread() { 79 | 80 | } 81 | 82 | @Overwrite 83 | public static void assertOnGameThread() { 84 | 85 | } 86 | 87 | @Overwrite 88 | public static boolean isInInitPhase() { 89 | return true; 90 | } 91 | 92 | @Overwrite 93 | public static void flipFrame(long l) { 94 | 95 | } 96 | 97 | @Overwrite 98 | public static void replayQueue() { 99 | assert recordingQueue.isEmpty() : "curium: rendersystem call queue non-empty!"; 100 | } 101 | 102 | @Overwrite 103 | public static void limitDisplayFPS(int i) { 104 | 105 | } 106 | 107 | @Overwrite 108 | public static void beginInitialization() { 109 | 110 | } 111 | 112 | @Overwrite 113 | public static void finishInitialization() { 114 | 115 | } 116 | 117 | @Overwrite 118 | @Deprecated 119 | public static void runAsFancy(Runnable runnable) { 120 | runnable.run(); 121 | } 122 | 123 | @Overwrite 124 | public static String getBackendDescription() { 125 | return CuriumConstants.DESCRIPTION; 126 | } 127 | 128 | @Overwrite 129 | public static String getCapsString() { 130 | return CuriumConstants.USING_CURIUM; 131 | } 132 | 133 | @Overwrite 134 | public static int maxSupportedTextureSize() { 135 | return 1024; 136 | } 137 | 138 | @Overwrite 139 | public static void recordRenderCall(RenderCall renderCall) { 140 | renderCall.execute(); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/asm/util/KnotLoaderHacks.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.asm.util; 2 | 3 | import me.katie.curium.impl.asm.CuriumASM; 4 | import org.objectweb.asm.ClassWriter; 5 | import org.objectweb.asm.commons.ClassRemapper; 6 | import org.objectweb.asm.commons.Remapper; 7 | import org.objectweb.asm.tree.ClassNode; 8 | 9 | import java.lang.invoke.MethodHandle; 10 | import java.lang.invoke.MethodHandles; 11 | import java.lang.reflect.Method; 12 | import java.security.CodeSource; 13 | 14 | /** 15 | * Reflective hacks on Knot. 16 | */ 17 | public class KnotLoaderHacks { 18 | private KnotLoaderHacks() { 19 | throw new UnsupportedOperationException("Cannot instantiate KnotLoaderHacks"); 20 | } 21 | 22 | private static final ClassLoader KNOT; 23 | private static final MethodHandle KNOT_DEFINE_CLASS_FWD; 24 | 25 | static { 26 | // Find Knot (it should be our classloader). 27 | KNOT = KnotLoaderHacks.class.getClassLoader(); 28 | 29 | if (!KNOT.getClass().getName().contains("Knot")) { 30 | throw new IllegalStateException("Curium was not loaded by Knot!"); 31 | } 32 | 33 | // Reflect defineClassFwd method. 34 | try { 35 | Method method = KNOT.getClass().getDeclaredMethod("defineClassFwd", String.class, byte[].class, int.class, int.class, CodeSource.class); 36 | method.setAccessible(true); 37 | 38 | KNOT_DEFINE_CLASS_FWD = MethodHandles.lookup().unreflect(method); 39 | } catch (NoSuchMethodException | IllegalAccessException e) { 40 | throw new IllegalStateException("Knot missing defineClassFwd method", e); 41 | } 42 | } 43 | 44 | /** 45 | * Defines a classnode on the Knot classpath. 46 | */ 47 | public static Class defineClass(ClassNode clazz) { 48 | ClassWriter writer = new ClassWriter(0); 49 | clazz.accept(writer); 50 | byte[] b = writer.toByteArray(); 51 | 52 | try { 53 | return (Class) KNOT_DEFINE_CLASS_FWD.invoke(KNOT, clazz.name.replace('/', '.'), b, 0, b.length, null); 54 | } catch (Throwable e) { 55 | CuriumASM.LOGGER.error("Failed to define class on Knot", e); 56 | return null; 57 | } 58 | } 59 | 60 | /** 61 | * Defines multiple replacer classes where both packages are used as prefixes for each name in the {@code names} 62 | * parameter. 63 | */ 64 | public static void defineReplacerClasses(String targetPackage, String replacerPackage, String[] names) { 65 | for (String name: names) { 66 | defineReplacerClass(targetPackage + "/" + name, replacerPackage + "/" + name); 67 | } 68 | 69 | CuriumASM.LOGGER.debug("Defined {} classes (from package {} - replacing {})", names.length, replacerPackage, targetPackage); 70 | } 71 | 72 | /** 73 | * Defines a replacer class by loading it from the current classloader as a resource. 74 | */ 75 | public static Class defineReplacerClass(String targetName, String replacerName) { 76 | // Read class and remap it. 77 | ClassNode clazz = new ClassNode(); 78 | 79 | ClassRemapper remapper = new ClassRemapper(clazz, new Remapper() { 80 | @Override 81 | public String map(String internalName) { 82 | return this.mapType(internalName); 83 | } 84 | 85 | @Override 86 | public String mapType(String internalName) { 87 | if (internalName.equals(replacerName)) { 88 | return targetName; 89 | } 90 | 91 | return internalName; 92 | } 93 | }); 94 | 95 | ASMUtil.getClassNode(replacerName).accept(remapper); 96 | 97 | // Write back and convert to buffer. 98 | return KnotLoaderHacks.defineClass(clazz); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/asm/mixin/transformers/CustomTransformerHandler.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.asm.mixin.transformers; 2 | 3 | import me.katie.curium.impl.asm.mixin.CuriumMixinPlugin; 4 | import me.katie.curium.impl.asm.mixin.ClassTransformer; 5 | import me.katie.curium.impl.asm.mixin.annotations.CustomTransformer; 6 | import org.objectweb.asm.Type; 7 | import org.objectweb.asm.tree.AnnotationNode; 8 | import org.objectweb.asm.tree.ClassNode; 9 | import org.spongepowered.asm.util.Annotations; 10 | 11 | import java.lang.reflect.Constructor; 12 | import java.lang.reflect.InvocationTargetException; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | /** 17 | * Handles the {@link CustomTransformer} annotation. 18 | */ 19 | public class CustomTransformerHandler implements ClassTransformer { 20 | private static List getTransformers(ClassNode mixin) { 21 | // Check for annotation. 22 | AnnotationNode annotation = Annotations.getInvisible(mixin, CustomTransformer.class); 23 | 24 | if (annotation == null) { 25 | return null; 26 | } 27 | 28 | // Fetch transformers. 29 | List transformers = new ArrayList<>(); 30 | List transformerTypes = Annotations.getValue(annotation); 31 | 32 | for (Type transformerType: transformerTypes) { 33 | assert transformerType.getSort() == Type.OBJECT; 34 | String className = transformerType.getInternalName().replace('/', '.'); 35 | 36 | // Load transformer class. 37 | Class transformerClass; 38 | 39 | try { 40 | Class maybeTransformerClass = Class.forName(className, true, CuriumMixinPlugin.class.getClassLoader()); 41 | transformerClass = maybeTransformerClass.asSubclass(ClassTransformer.class); 42 | } catch (ClassNotFoundException e) { 43 | throw new IllegalStateException("@ClassTransformers transformer " + className + " could not be loaded", e); 44 | } catch (ClassCastException e) { 45 | throw new IllegalStateException("@ClassTransformers transformer " + className + " does not extend Transformer", e); 46 | } 47 | 48 | // Locate constructor. 49 | Constructor ctor; 50 | 51 | try { 52 | ctor = transformerClass.getConstructor(); 53 | ctor.setAccessible(true); 54 | } catch (NoSuchMethodException e) { 55 | throw new IllegalStateException("@ClassTransformers transformer " + className + " is missing no-args constructor", e); 56 | } 57 | 58 | // Construct instance. 59 | try { 60 | ClassTransformer transformer = ctor.newInstance(); 61 | transformers.add(transformer); 62 | } catch (InvocationTargetException e) { 63 | throw new RuntimeException("@ClassTransformers transformer constructor threw", e); 64 | } catch (InstantiationException e) { 65 | throw new IllegalStateException("@ClassTransformers transformer could not be constructed", e); 66 | } catch (IllegalAccessException e) { 67 | throw new IllegalStateException("illegal access to @ClassTransformers transformer constructor", e); 68 | } 69 | } 70 | 71 | return transformers; 72 | } 73 | 74 | @Override 75 | public void preMixinTransform(ClassNode target, ClassNode mixin) { 76 | List transformers = getTransformers(mixin); 77 | 78 | if (transformers == null) { 79 | return; 80 | } 81 | 82 | // Apply all located transformers. 83 | for (ClassTransformer transformer: transformers) { 84 | transformer.preMixinTransform(target, mixin); 85 | } 86 | } 87 | 88 | @Override 89 | public void postMixinTransform(ClassNode target, ClassNode mixin) { 90 | List transformers = getTransformers(mixin); 91 | 92 | if (transformers == null) { 93 | return; 94 | } 95 | 96 | // Apply all located transformers. 97 | for (ClassTransformer transformer: transformers) { 98 | transformer.postMixinTransform(target, mixin); 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/input/InputModifiers.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.input; 2 | 3 | /** 4 | * Ergonomic wrapper for setting input modifier bit flags. 5 | */ 6 | public class InputModifiers { 7 | /** If this flag is set one or more Shift keys are held down. */ 8 | public static final int SHIFT = 0x1; 9 | 10 | /** If this flag is set one or more Control keys are held down. */ 11 | public static final int CONTROL = 0x2; 12 | 13 | /** If this flag is set one or more Alt keys are held down. */ 14 | public static final int ALT = 0x4; 15 | 16 | /** If this flag is set one or more Super keys are held down. */ 17 | public static final int SUPER = 0x8; 18 | 19 | /** If this flag is set Caps Lock is enabled. */ 20 | public static final int CAPS_LOCK = 0x10; 21 | 22 | /** If this flag is set Num Lock is enabled. */ 23 | public static final int NUM_LOCK = 0x20; 24 | 25 | private final int flags; 26 | 27 | private InputModifiers(int flags) { 28 | this.flags = flags; 29 | } 30 | 31 | /** 32 | * @return the raw flags of this {@link InputModifiers} instance (as a bit field). 33 | */ 34 | public int toRaw() { 35 | return this.flags; 36 | } 37 | 38 | /** 39 | * @return an instance of {@link InputModifiers} with the given flags as the bits. 40 | */ 41 | public static InputModifiers fromRawFlags(int flags) { 42 | return new InputModifiers(flags); 43 | } 44 | 45 | /** 46 | * @return an instance of {@link InputModifiers} with no flags set. 47 | */ 48 | public static InputModifiers none() { 49 | return new InputModifiers(0); 50 | } 51 | 52 | /** 53 | * @return an instance of {@link InputModifiers} with the {@link InputModifiers#SHIFT SHIFT} flag set. 54 | */ 55 | public static InputModifiers shift() { 56 | return new InputModifiers(SHIFT); 57 | } 58 | 59 | /** 60 | * @return {@literal this} but with the {@link InputModifiers#SHIFT SHIFT} flag set. 61 | */ 62 | public InputModifiers withShift() { 63 | return new InputModifiers(this.flags | SHIFT); 64 | } 65 | 66 | /** 67 | * @return an instance of {@link InputModifiers} with the {@link InputModifiers#CONTROL CONTROL} flag set. 68 | */ 69 | public static InputModifiers control() { 70 | return new InputModifiers(CONTROL); 71 | } 72 | 73 | /** 74 | * @return {@literal this} but with the {@link InputModifiers#CONTROL CONTROL} flag set. 75 | */ 76 | public InputModifiers withControl() { 77 | return new InputModifiers(this.flags | CONTROL); 78 | } 79 | 80 | /** 81 | * @return an instance of {@link InputModifiers} with the {@link InputModifiers#ALT ALT} flag set. 82 | */ 83 | public static InputModifiers alt() { 84 | return new InputModifiers(ALT); 85 | } 86 | 87 | /** 88 | * @return {@literal this} but with the {@link InputModifiers#ALT ALT} flag set. 89 | */ 90 | public InputModifiers withAlt() { 91 | return new InputModifiers(this.flags | ALT); 92 | } 93 | 94 | /** 95 | * @return an instance of {@link InputModifiers} with the {@link InputModifiers#SUPER SUPER} flag set. 96 | */ 97 | public static InputModifiers super_() { 98 | return new InputModifiers(SUPER); 99 | } 100 | 101 | /** 102 | * @return {@literal this} but with the {@link InputModifiers#SUPER SUPER} flag set. 103 | */ 104 | public InputModifiers withSuper() { 105 | return new InputModifiers(this.flags | SUPER); 106 | } 107 | 108 | /** 109 | * @return an instance of {@link InputModifiers} with the {@link InputModifiers#CAPS_LOCK CAPS_LOCK} flag set. 110 | */ 111 | public static InputModifiers capsLock() { 112 | return new InputModifiers(CAPS_LOCK); 113 | } 114 | 115 | /** 116 | * @return {@literal this} but with the {@link InputModifiers#CAPS_LOCK CAPS_LOCK} flag set. 117 | */ 118 | public InputModifiers withCapsLock() { 119 | return new InputModifiers(this.flags | CAPS_LOCK); 120 | } 121 | 122 | /** 123 | * @return an instance of {@link InputModifiers} with the {@link InputModifiers#NUM_LOCK NUM_LOCK} flag set. 124 | */ 125 | public static InputModifiers numLock() { 126 | return new InputModifiers(NUM_LOCK); 127 | } 128 | 129 | /** 130 | * @return {@literal this} but with the {@link InputModifiers#NUM_LOCK NUM_LOCK} flag set. 131 | */ 132 | public InputModifiers withNumLock() { 133 | return new InputModifiers(this.flags | NUM_LOCK); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/asm/mixin/transformers/OverwriteCtorHandler.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.asm.mixin.transformers; 2 | 3 | import me.katie.curium.impl.asm.CuriumASM; 4 | import me.katie.curium.impl.asm.mixin.ClassTransformer; 5 | import me.katie.curium.impl.asm.mixin.annotations.OverwriteCtor; 6 | import me.katie.curium.impl.asm.mixin.helper.CtorOverwriteHelper; 7 | import org.objectweb.asm.Opcodes; 8 | import org.objectweb.asm.Type; 9 | import org.objectweb.asm.tree.*; 10 | import org.spongepowered.asm.util.Annotations; 11 | 12 | import java.util.ArrayList; 13 | import java.util.Iterator; 14 | 15 | /** 16 | * Handles the {@link OverwriteCtor} annotation. 17 | */ 18 | public class OverwriteCtorHandler implements ClassTransformer { 19 | private static final String HELPER_INTERNAL = Type.getInternalName(CtorOverwriteHelper.class); 20 | private static final String HELPER_SUPER_METHOD = "super_"; 21 | 22 | @Override 23 | public void preMixinTransform(ClassNode target, ClassNode mixin) { 24 | 25 | } 26 | 27 | @Override 28 | public void postMixinTransform(ClassNode target, ClassNode mixin) { 29 | // Relocate overwritten constructors from target class. 30 | for (MethodNode method: new ArrayList<>(target.methods)) { 31 | AnnotationNode annotation = Annotations.getInvisible(method, OverwriteCtor.class); 32 | 33 | if (annotation == null) { 34 | continue; 35 | } 36 | 37 | 38 | // Find the super call we're targeting. 39 | String targetDescString = Annotations.getValue(annotation, "superDesc", "()V"); 40 | Type targetDesc = Type.getMethodType(targetDescString); 41 | assert targetDesc.getReturnType() == Type.VOID_TYPE; 42 | 43 | // Overwrite super call. 44 | int foundCtorCount = 0; 45 | 46 | for (Iterator it = method.instructions.iterator(); it.hasNext(); ) { 47 | AbstractInsnNode insn = it.next(); 48 | 49 | if (insn instanceof MethodInsnNode methodInsn) { 50 | if (!methodInsn.owner.equals(HELPER_INTERNAL) || !methodInsn.name.equals(HELPER_SUPER_METHOD)) { 51 | continue; 52 | } 53 | 54 | // Inject bytecode to generate correct arguments. 55 | InsnList insns = new InsnList(); 56 | 57 | // Load uninitialized `this`. 58 | insns.add(new VarInsnNode(Opcodes.ALOAD, 0)); 59 | insns.add(new InsnNode(Opcodes.SWAP)); 60 | 61 | // Unwrap varargs array. 62 | Type[] argTypes = targetDesc.getArgumentTypes(); 63 | 64 | for (int i = 0; i < argTypes.length; i++) { 65 | Type argType = argTypes[i]; 66 | int opcode = argType.getOpcode(Opcodes.IALOAD); 67 | 68 | // - Duplicate varargs array on stack. 69 | // - Load constant index onto stack. 70 | // - Load from array. 71 | // - Cast to target class. 72 | // - Then swap array and item for next loop. 73 | insns.add(new InsnNode(Opcodes.DUP)); 74 | insns.add(new LdcInsnNode(i)); 75 | insns.add(new InsnNode(opcode)); 76 | 77 | if (argType.getSort() == Type.OBJECT || argType.getSort() == Type.ARRAY) { 78 | insns.add(new TypeInsnNode(Opcodes.CHECKCAST, argType.getInternalName())); 79 | } 80 | 81 | insns.add(new InsnNode(Opcodes.SWAP)); 82 | } 83 | 84 | // Pop the array. 85 | insns.add(new InsnNode(Opcodes.POP)); 86 | 87 | // Replace INVOKESTATIC with INVOKESPECIAL call. 88 | insns.add(new MethodInsnNode(Opcodes.INVOKESPECIAL, target.superName, "", targetDescString)); 89 | method.instructions.insertBefore(insn, insns); 90 | it.remove(); 91 | 92 | foundCtorCount++; 93 | } 94 | } 95 | 96 | if (foundCtorCount == 0) { 97 | if (targetDescString.equals("()V")) { 98 | // Can omit call in mixin if target descriptor is blank. 99 | InsnList insns = new InsnList(); 100 | insns.add(new VarInsnNode(Opcodes.ALOAD, 0)); 101 | insns.add(new MethodInsnNode( 102 | Opcodes.INVOKESPECIAL, 103 | target.superName, 104 | "", 105 | "()V" 106 | )); 107 | 108 | method.instructions.insert(insns); 109 | } else { 110 | throw new IllegalStateException(String.format( 111 | "missing super call in ctor overwrite for %s (from %s;%s%s)", 112 | target.name, mixin.name, method.name, method.desc 113 | )); 114 | } 115 | } else if (foundCtorCount == 1) { 116 | CuriumASM.LOGGER.debug("Overwrote constructor in {} with {};{}{}", target.name, mixin.name, method.name, method.desc); 117 | } else { 118 | throw new IllegalStateException(String.format( 119 | "found multiple super constructor calls in ctor overwrite for %s (from %s;%s%s)", 120 | target.name, mixin.name, method.name, method.desc 121 | )); 122 | } 123 | 124 | // Remove matching constructor from the target class. 125 | boolean foundTarget = false; 126 | 127 | for (Iterator it = target.methods.iterator(); it.hasNext();) { 128 | MethodNode targetCtor = it.next(); 129 | 130 | if (targetCtor.name.equals("") && targetCtor.desc.equals(method.desc)) { 131 | // Found the overwritten ctor, match the access. 132 | foundTarget = true; 133 | it.remove(); 134 | 135 | method.access = targetCtor.access; 136 | } 137 | } 138 | 139 | if (!foundTarget) { 140 | throw new IllegalStateException("tried to overwrite non-existent constructor in " + target.name + 141 | " (mixin " + mixin.name + ")"); 142 | } 143 | 144 | method.name = ""; 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # 4 | # Copyright © 2015-2021 the original authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | # 21 | # Gradle start up script for POSIX generated by Gradle. 22 | # 23 | # Important for running: 24 | # 25 | # (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is 26 | # noncompliant, but you have some other compliant shell such as ksh or 27 | # bash, then to run this script, type that shell name before the whole 28 | # command line, like: 29 | # 30 | # ksh Gradle 31 | # 32 | # Busybox and similar reduced shells will NOT work, because this script 33 | # requires all of these POSIX shell features: 34 | # * functions; 35 | # * expansions «$var», «${var}», «${var:-default}», «${var+SET}», 36 | # «${var#prefix}», «${var%suffix}», and «$( cmd )»; 37 | # * compound commands having a testable exit status, especially «case»; 38 | # * various built-in commands including «command», «set», and «ulimit». 39 | # 40 | # Important for patching: 41 | # 42 | # (2) This script targets any POSIX shell, so it avoids extensions provided 43 | # by Bash, Ksh, etc; in particular arrays are avoided. 44 | # 45 | # The "traditional" practice of packing multiple parameters into a 46 | # space-separated string is a well documented source of bugs and security 47 | # problems, so this is (mostly) avoided, by progressively accumulating 48 | # options in "$@", and eventually passing that to Java. 49 | # 50 | # Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, 51 | # and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; 52 | # see the in-line comments for details. 53 | # 54 | # There are tweaks for specific operating systems such as AIX, CygWin, 55 | # Darwin, MinGW, and NonStop. 56 | # 57 | # (3) This script is generated from the Groovy template 58 | # https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt 59 | # within the Gradle project. 60 | # 61 | # You can find Gradle at https://github.com/gradle/gradle/. 62 | # 63 | ############################################################################## 64 | 65 | # Attempt to set APP_HOME 66 | 67 | # Resolve links: $0 may be a link 68 | app_path=$0 69 | 70 | # Need this for daisy-chained symlinks. 71 | while 72 | APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path 73 | [ -h "$app_path" ] 74 | do 75 | ls=$( ls -ld "$app_path" ) 76 | link=${ls#*' -> '} 77 | case $link in #( 78 | /*) app_path=$link ;; #( 79 | *) app_path=$APP_HOME$link ;; 80 | esac 81 | done 82 | 83 | APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit 84 | 85 | APP_NAME="Gradle" 86 | APP_BASE_NAME=${0##*/} 87 | 88 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 89 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 90 | 91 | # Use the maximum available, or set MAX_FD != -1 to use that value. 92 | MAX_FD=maximum 93 | 94 | warn () { 95 | echo "$*" 96 | } >&2 97 | 98 | die () { 99 | echo 100 | echo "$*" 101 | echo 102 | exit 1 103 | } >&2 104 | 105 | # OS specific support (must be 'true' or 'false'). 106 | cygwin=false 107 | msys=false 108 | darwin=false 109 | nonstop=false 110 | case "$( uname )" in #( 111 | CYGWIN* ) cygwin=true ;; #( 112 | Darwin* ) darwin=true ;; #( 113 | MSYS* | MINGW* ) msys=true ;; #( 114 | NONSTOP* ) nonstop=true ;; 115 | esac 116 | 117 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 118 | 119 | 120 | # Determine the Java command to use to start the JVM. 121 | if [ -n "$JAVA_HOME" ] ; then 122 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 123 | # IBM's JDK on AIX uses strange locations for the executables 124 | JAVACMD=$JAVA_HOME/jre/sh/java 125 | else 126 | JAVACMD=$JAVA_HOME/bin/java 127 | fi 128 | if [ ! -x "$JAVACMD" ] ; then 129 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 130 | 131 | Please set the JAVA_HOME variable in your environment to match the 132 | location of your Java installation." 133 | fi 134 | else 135 | JAVACMD=java 136 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 137 | 138 | Please set the JAVA_HOME variable in your environment to match the 139 | location of your Java installation." 140 | fi 141 | 142 | # Increase the maximum file descriptors if we can. 143 | if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then 144 | case $MAX_FD in #( 145 | max*) 146 | MAX_FD=$( ulimit -H -n ) || 147 | warn "Could not query maximum file descriptor limit" 148 | esac 149 | case $MAX_FD in #( 150 | '' | soft) :;; #( 151 | *) 152 | ulimit -n "$MAX_FD" || 153 | warn "Could not set maximum file descriptor limit to $MAX_FD" 154 | esac 155 | fi 156 | 157 | # Collect all arguments for the java command, stacking in reverse order: 158 | # * args from the command line 159 | # * the main class name 160 | # * -classpath 161 | # * -D...appname settings 162 | # * --module-path (only if needed) 163 | # * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. 164 | 165 | # For Cygwin or MSYS, switch paths to Windows format before running java 166 | if "$cygwin" || "$msys" ; then 167 | APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) 168 | CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) 169 | 170 | JAVACMD=$( cygpath --unix "$JAVACMD" ) 171 | 172 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 173 | for arg do 174 | if 175 | case $arg in #( 176 | -*) false ;; # don't mess with options #( 177 | /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath 178 | [ -e "$t" ] ;; #( 179 | *) false ;; 180 | esac 181 | then 182 | arg=$( cygpath --path --ignore --mixed "$arg" ) 183 | fi 184 | # Roll the args list around exactly as many times as the number of 185 | # args, so each arg winds up back in the position where it started, but 186 | # possibly modified. 187 | # 188 | # NB: a `for` loop captures its iteration list before it begins, so 189 | # changing the positional parameters here affects neither the number of 190 | # iterations, nor the values presented in `arg`. 191 | shift # remove old arg 192 | set -- "$@" "$arg" # push replacement arg 193 | done 194 | fi 195 | 196 | # Collect all arguments for the java command; 197 | # * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of 198 | # shell script including quotes and variable substitutions, so put them in 199 | # double quotes to make sure that they get re-expanded; and 200 | # * put everything else in single quotes, so that it's not re-expanded. 201 | 202 | set -- \ 203 | "-Dorg.gradle.appname=$APP_BASE_NAME" \ 204 | -classpath "$CLASSPATH" \ 205 | org.gradle.wrapper.GradleWrapperMain \ 206 | "$@" 207 | 208 | # Use "xargs" to parse quoted args. 209 | # 210 | # With -n1 it outputs one arg per line, with the quotes and backslashes removed. 211 | # 212 | # In Bash we could simply go: 213 | # 214 | # readarray ARGS < <( xargs -n1 <<<"$var" ) && 215 | # set -- "${ARGS[@]}" "$@" 216 | # 217 | # but POSIX shell has neither arrays nor command substitution, so instead we 218 | # post-process each arg (as a line of input to sed) to backslash-escape any 219 | # character that might be a shell metacharacter, then use eval to reverse 220 | # that process (while maintaining the separation between arguments), and wrap 221 | # the whole thing up as a single "set" statement. 222 | # 223 | # This will of course break if any of these variables contains a newline or 224 | # an unmatched quote. 225 | # 226 | 227 | eval "set -- $( 228 | printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | 229 | xargs -n1 | 230 | sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | 231 | tr '\n' ' ' 232 | )" '"$@"' 233 | 234 | exec "$JAVACMD" "$@" 235 | -------------------------------------------------------------------------------- /src/main/java/me/katie/curium/impl/mixin/core/minecraft/MinecraftMixin.java: -------------------------------------------------------------------------------- 1 | package me.katie.curium.impl.mixin.core.minecraft; 2 | 3 | import com.mojang.realmsclient.client.RealmsClient; 4 | import me.katie.curium.events.client.ClientStartedEvent; 5 | import me.katie.curium.events.client.TickEvent; 6 | import me.katie.curium.impl.CuriumConstants; 7 | import me.katie.curium.impl.CuriumProperties; 8 | import me.katie.curium.impl.api.CuriumImpl; 9 | import me.katie.curium.impl.duck.CuriumStateHolder; 10 | import me.katie.curium.impl.util.Util; 11 | import net.minecraft.SystemReport; 12 | import net.minecraft.client.Minecraft; 13 | import net.minecraft.client.gui.screens.ConnectScreen; 14 | import net.minecraft.client.gui.screens.TitleScreen; 15 | import net.minecraft.client.gui.screens.multiplayer.JoinMultiplayerScreen; 16 | import net.minecraft.client.main.GameConfig; 17 | import net.minecraft.client.multiplayer.ServerData; 18 | import net.minecraft.client.multiplayer.resolver.ServerAddress; 19 | import net.minecraft.server.packs.resources.ReloadInstance; 20 | import net.minecraft.util.profiling.ProfilerFiller; 21 | import org.jetbrains.annotations.NotNull; 22 | import org.objectweb.asm.Opcodes; 23 | import org.slf4j.Logger; 24 | import org.spongepowered.asm.mixin.Mixin; 25 | import org.spongepowered.asm.mixin.Shadow; 26 | import org.spongepowered.asm.mixin.Unique; 27 | import org.spongepowered.asm.mixin.injection.At; 28 | import org.spongepowered.asm.mixin.injection.Inject; 29 | import org.spongepowered.asm.mixin.injection.Redirect; 30 | import org.spongepowered.asm.mixin.injection.Slice; 31 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 32 | 33 | import java.util.function.Supplier; 34 | 35 | @Mixin(Minecraft.class) 36 | public abstract class MinecraftMixin implements CuriumStateHolder { 37 | @Shadow public String fpsString; 38 | 39 | @Shadow private ProfilerFiller profiler; 40 | 41 | @Inject( 42 | method = "setInitialScreen", 43 | at = @At("HEAD"), 44 | cancellable = true 45 | ) 46 | private void curium_joinViaSystemProperty(RealmsClient realmsClient, ReloadInstance reloadInstance, GameConfig.QuickPlayData quickPlayData, CallbackInfo ci) { 47 | if (CuriumProperties.SERVER_HOST == null) { 48 | return; 49 | } 50 | 51 | ci.cancel(); 52 | 53 | // Parse port from system property. 54 | int port; 55 | 56 | try { 57 | port = Integer.parseInt(CuriumProperties.SERVER_PORT); 58 | } catch (NumberFormatException e) { 59 | throw new IllegalArgumentException("Curium server port was set to an invalid value", e); 60 | } 61 | 62 | // Join server by host and port. 63 | reloadInstance.done().thenRunAsync(() -> { 64 | JoinMultiplayerScreen screen = new JoinMultiplayerScreen(new TitleScreen()); 65 | ServerAddress address = new ServerAddress(CuriumProperties.SERVER_HOST, port); 66 | ServerData data = new ServerData("Curium Debug", CuriumProperties.SERVER_HOST, false); 67 | 68 | ConnectScreen.startConnecting(screen, (Minecraft) (Object) this, address, data, true); 69 | }, (Minecraft) (Object) this); 70 | } 71 | 72 | @Inject( 73 | method = "", 74 | at = @At("TAIL") 75 | ) 76 | private void curium_postInit(GameConfig gameConfig, CallbackInfo ci) { 77 | this.fpsString = CuriumConstants.USING_CURIUM; 78 | } 79 | 80 | @Redirect( 81 | method = "fillSystemReport", 82 | require = 0, 83 | at = @At( 84 | value = "INVOKE", 85 | target = "Lnet/minecraft/SystemReport;setDetail(Ljava/lang/String;Ljava/util/function/Supplier;)V" 86 | ) 87 | ) 88 | private static void curium_editSystemReport(SystemReport instance, String key, Supplier supplier) { 89 | // Removes useless sections from the crash report. 90 | if (!CuriumConstants.OMITTED_SYSTEM_REPORT_SECTIONS.contains(key)) { 91 | instance.setDetail(key, supplier); 92 | } 93 | } 94 | 95 | @Redirect( 96 | method = "", 97 | slice = @Slice( 98 | from = @At( 99 | value = "FIELD", 100 | opcode = Opcodes.PUTFIELD, 101 | target = "Lnet/minecraft/client/Minecraft;hotbarManager:Lnet/minecraft/client/HotbarManager;" 102 | ), 103 | to = @At( 104 | value = "INVOKE", 105 | target = "Lcom/mojang/blaze3d/systems/RenderSystem;initBackendSystem()Lnet/minecraft/util/TimeSource$NanoTimeSource;" 106 | ) 107 | ), 108 | at = @At( 109 | value = "INVOKE", 110 | target = "Lorg/slf4j/Logger;info(Ljava/lang/String;Ljava/lang/Object;)V", 111 | remap = false 112 | ) 113 | ) 114 | private void curium_removeBackendLibraryLog(Logger instance, String msg, Object o) { 115 | 116 | } 117 | 118 | @Redirect( 119 | method = "runTick", 120 | at = @At( 121 | value = "INVOKE", 122 | target = "Ljava/lang/Thread;yield()V" 123 | ) 124 | ) 125 | private void curium_removeThreadYield() { 126 | 127 | } 128 | 129 | @Inject( 130 | method = "run", 131 | at = @At("HEAD") 132 | ) 133 | private void curium_onGameRunning(CallbackInfo ci) { 134 | CuriumImpl.get().getEventBus().post(ClientStartedEvent.get()); 135 | } 136 | 137 | @Redirect( 138 | method = "", 139 | at = @At( 140 | value = "INVOKE", 141 | target = "Lorg/lwjgl/util/tinyfd/TinyFileDialogs;tinyfd_messageBox(Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Ljava/lang/CharSequence;Z)Z", 142 | remap = false 143 | ) 144 | ) 145 | private boolean curium_nopMessageBox(CharSequence aTitleEncoded, CharSequence aMessageEncoded, CharSequence aDialogTypeEncoded, CharSequence aIconTypeEncoded, boolean aTitle) { 146 | return Util.stubbed(); 147 | } 148 | 149 | @Redirect( 150 | method = "run", 151 | at = @At( 152 | value = "INVOKE", 153 | target = "Ljava/lang/Thread;setPriority(I)V", 154 | remap = false 155 | ) 156 | ) 157 | private void curium_nopSetPriority(Thread instance, int newPriority) { 158 | 159 | } 160 | 161 | @Redirect( 162 | method = "runTick", 163 | slice = @Slice( 164 | from = @At( 165 | value = "FIELD", 166 | opcode = Opcodes.PUTFIELD, 167 | target = "Lnet/minecraft/client/Minecraft;gpuUtilization:D" 168 | ) 169 | ), 170 | at = @At( 171 | value = "INVOKE", 172 | target = "Lnet/minecraft/Util;getMillis()J" 173 | ) 174 | ) 175 | private long curium_removeBusyLoop() { 176 | // This loop usually only executes a few times but because we don't sleep in this method it runs a *lot*. 177 | return Long.MIN_VALUE; 178 | } 179 | 180 | 181 | // State associated with this client. 182 | @Unique 183 | private final CuriumImpl curium_state = new CuriumImpl(); 184 | 185 | @Override 186 | @Unique 187 | public @NotNull CuriumImpl curium_getState() { 188 | return this.curium_state; 189 | } 190 | 191 | // These injectors cap the client tick rate to avoid what is effectively a busy-loop. 192 | @Inject( 193 | method = "run", 194 | at = @At( 195 | value = "INVOKE", 196 | target = "Lnet/minecraft/util/profiling/SingleTickProfiler;createTickProfiler(Ljava/lang/String;)Lnet/minecraft/util/profiling/SingleTickProfiler;", 197 | shift = At.Shift.BEFORE 198 | ) 199 | ) 200 | private void curium_preClientTick(CallbackInfo ci) { 201 | this.curium_state.tpsLimiter.preTick(); 202 | 203 | this.profiler.push("curium_pre"); 204 | CuriumImpl.get().getEventBus().post(TickEvent.Pre.get()); 205 | this.profiler.pop(); 206 | } 207 | 208 | @Inject( 209 | method = "run", 210 | at = @At( 211 | value = "INVOKE", 212 | target = "Lnet/minecraft/client/Minecraft;finishProfilers(ZLnet/minecraft/util/profiling/SingleTickProfiler;)V", 213 | shift = At.Shift.AFTER 214 | ) 215 | ) 216 | private void curium_postClientTick(CallbackInfo ci) { 217 | this.profiler.push("curium_post"); 218 | CuriumImpl.get().getEventBus().post(TickEvent.Post.get()); 219 | this.profiler.pop(); 220 | 221 | this.curium_state.tpsLimiter.postTick(); 222 | } 223 | } 224 | --------------------------------------------------------------------------------