From c3f8f23c1d5d0f366bec33881b688a29604149f8 Mon Sep 17 00:00:00 2001 From: bbldrizzy Date: Fri, 23 Oct 2026 08:36:42 +0100 Subject: [PATCH] fixed bugs, memory leaks, etc --- build-injector.ps1 | 6 +- injector/crates/loader/src/bridge.rs | 33 +++++++ injector/crates/loader/src/jni.rs | 25 +++++ injector/crates/loader/src/worker.rs | 14 ++- .../zprestige/prestige/client/Prestige.java | 35 +++++-- .../zprestige/prestige/combat/BotFilter.java | 2 +- .../combat/ExplosionDamageCalculator.java | 8 ++ .../prestige/combat/TargetTracker.java | 2 +- .../config/ClasspathResourcePack.java | 19 +++- .../prestige/data/OutlineCaptureConsumer.java | 2 +- .../prestige/event/ModuleStateSync.java | 23 +++-- .../prestige/gui/HudEditorScreen.java | 14 ++- .../hud/ModuleNotificationsElement.java | 7 +- .../zprestige/prestige/hud/TargetElement.java | 12 ++- .../zprestige/prestige/inject/Bootstrap.java | 29 ++++++ .../prestige/inject/NativeBridge.java | 3 + .../prestige/input/KeybindManager.java | 52 +++++----- .../prestige/inventory/InventoryUtil.java | 7 +- .../prestige/math/BlockChangeTracker.java | 8 +- .../prestige/math/LegacyParticlePhysics.java | 2 +- .../prestige/math/PopParticlePhysics.java | 2 +- .../mixin/AbstractBlockStateMixin.java | 2 +- .../AbstractClientPlayerEntityMixin.java | 2 +- .../zprestige/prestige/mixin/BlockMixin.java | 2 +- .../zprestige/prestige/mixin/CameraMixin.java | 16 ++- .../mixin/ChunkOcclusionDataBuilderMixin.java | 2 +- .../prestige/mixin/ClientConnectionMixin.java | 2 +- .../mixin/ClientPlayNetworkHandlerMixin.java | 7 +- .../zprestige/prestige/mixin/EntityMixin.java | 4 +- .../prestige/mixin/EntityRendererMixin.java | 5 +- .../prestige/mixin/FogRendererMixin.java | 4 +- .../prestige/mixin/GameRendererMixin.java | 15 +-- .../prestige/mixin/HeldItemRendererMixin.java | 2 +- .../prestige/mixin/InGameHudMixin.java | 6 +- .../prestige/mixin/ItemStackMixin.java | 4 +- .../mixin/LightmapTextureManagerMixin.java | 2 +- .../mixin/ModelCommandRendererMixin.java | 4 +- .../zprestige/prestige/mixin/MouseMixin.java | 2 +- .../prestige/mixin/ParticleManagerMixin.java | 2 +- .../prestige/mixin/PlayerListEntryMixin.java | 2 +- .../prestige/mixin/SkyRenderingMixin.java | 2 +- .../mixin/TextDisplayEntityRendererMixin.java | 2 +- .../prestige/mixin/TextVisitFactoryMixin.java | 2 +- .../prestige/mixin/WorldChunkMixin.java | 2 +- .../zprestige/prestige/mixin/WorldMixin.java | 4 +- .../prestige/module/combat/AimAssist.java | 9 +- .../module/combat/AnchorExploder.java | 2 +- .../prestige/module/combat/AnchorMacro.java | 6 +- .../prestige/module/combat/AnchorPlacer.java | 2 +- .../prestige/module/combat/AntiBot.java | 42 +++++--- .../prestige/module/combat/AutoAnchor.java | 2 +- .../prestige/module/combat/DoubleAnchor.java | 2 +- .../prestige/module/combat/SafeAnchor.java | 2 +- .../prestige/module/misc/AutoArmor.java | 4 + .../prestige/module/misc/AutoPickaxe.java | 2 +- .../prestige/module/misc/AutoPot.java | 2 +- .../prestige/module/misc/AutoText.java | 4 + .../prestige/module/misc/AutoTool.java | 2 +- .../prestige/module/misc/CartRefill.java | 6 +- .../prestige/module/misc/CartStealer.java | 4 + .../prestige/module/misc/ChestStealer.java | 4 + .../prestige/module/misc/Criticals.java | 19 +++- .../module/misc/CrystalOptimizer.java | 2 +- .../prestige/module/misc/EnemyFaker.java | 59 ++++++++--- .../prestige/module/misc/FastPlace.java | 4 + .../prestige/module/misc/Friends.java | 6 +- .../prestige/module/misc/KeyPearl.java | 2 +- .../prestige/module/misc/NameHider.java | 10 +- .../prestige/module/misc/Refill.java | 4 + .../prestige/module/misc/SelfDestruct.java | 59 +++++++++-- .../zprestige/prestige/module/misc/Teams.java | 5 + .../module/movement/AutoJumpReset.java | 4 + .../module/movement/AutoShortPearl.java | 7 +- .../prestige/module/movement/FakeLag.java | 24 ++++- .../prestige/module/movement/FastBridge.java | 12 +++ .../prestige/module/movement/Freecam.java | 4 +- .../prestige/module/movement/NoJumpDelay.java | 4 + .../prestige/module/movement/Parkour.java | 2 +- .../prestige/module/movement/ShortPearl.java | 6 +- .../prestige/module/movement/Velocity.java | 16 +-- .../prestige/module/movement/WTap.java | 8 +- .../module/spear/SpearMaceDoubleTap.java | 20 ++-- .../prestige/module/visual/Animations.java | 4 + .../module/visual/AntiResourcePack.java | 6 +- .../prestige/module/visual/FreeLook.java | 6 +- .../prestige/module/visual/HitEffect.java | 53 +++++++--- .../prestige/module/visual/ItemESP.java | 7 ++ .../prestige/module/visual/NameTags.java | 11 +++ .../prestige/module/visual/OreSimulation.java | 19 ++++ .../module/visual/PearlTrajectory.java | 7 ++ .../module/visual/ShieldRenderer.java | 9 ++ .../prestige/module/visual/Trajectories.java | 73 +++++++------- .../prestige/module/visual/TwoDESP.java | 5 +- .../prestige/network/BlockChangeListener.java | 9 ++ .../prestige/network/PacketSender.java | 4 +- .../prestige/network/SilentSlotManager.java | 18 ++-- .../prestige/network/TotemPopManager.java | 15 ++- .../prestige/reflect/FieldAccessor.java | 8 ++ .../prestige/reflect/MappedField.java | 4 + .../prestige/reflect/ReflectionBase.java | 21 ++-- .../prestige/render/BufferSource.java | 18 +++- .../prestige/render/DrawCommandQueue.java | 98 +++++++++++-------- .../prestige/render/DrawPipeline.java | 20 ++-- .../prestige/render/EspShaderManager.java | 12 ++- .../prestige/render/GlTextureHandle.java | 14 +++ .../zprestige/prestige/render/MsdfFont.java | 51 +++++----- .../prestige/render/Notification.java | 3 +- .../zprestige/prestige/render/PostShader.java | 23 +++-- .../zprestige/prestige/render/RenderCore.java | 50 +++++----- .../prestige/render/RenderEvents.java | 9 +- .../prestige/render/RoundedTextureShader.java | 3 +- .../prestige/render/ShaderCompiler.java | 22 +++-- .../render/SilentRotationManager.java | 5 +- .../render/StorageOutlineRenderer.java | 21 ++-- .../prestige/render/TextureLoader.java | 3 +- .../zprestige/prestige/social/MojangApi.java | 4 + .../zprestige/prestige/ui/ConfigsPage.java | 3 +- .../java/dev/zprestige/prestige/ui/Data.java | 1 + .../dev/zprestige/prestige/ui/DetailPage.java | 3 +- .../zprestige/prestige/ui/LocalConfigs.java | 10 +- .../zprestige/prestige/ui/WidgetsPage.java | 2 +- .../dev/zprestige/prestige/ui/mc/Entry.java | 1 + .../dev/zprestige/prestige/ui/mc/Overlay.java | 46 ++++++--- .../zprestige/prestige/world/BlockUtil.java | 5 +- 124 files changed, 1060 insertions(+), 420 deletions(-) diff --git a/build-injector.ps1 b/build-injector.ps1 index 9a9258d..a9f4f89 100644 --- a/build-injector.ps1 +++ b/build-injector.ps1 @@ -1,15 +1,13 @@ # Builds the Prestige payload jar (remapped for the production Fabric runtime), then the Rust injector and loader DLL, # and collects PrestigeInjector.exe + prestige_native.dll in dist\. -$ErrorActionPreference = 'Stop' +# native tools write progress/notes to stderr; failures are detected through $LASTEXITCODE instead +$ErrorActionPreference = 'Continue' Set-Location $PSScriptRoot if (Test-Path 'C:\Program Files\Eclipse Adoptium\jdk-21.0.12.101-hotspot') { $env:JAVA_HOME = 'C:\Program Files\Eclipse Adoptium\jdk-21.0.12.101-hotspot' } & .\gradlew.bat remapJar --console=plain if ($LASTEXITCODE -ne 0) { throw 'gradle remapJar failed' } -# cargo reports progress on stderr, which PowerShell would treat as an error under 'Stop' -$ErrorActionPreference = 'Continue' cargo build --release --manifest-path injector\Cargo.toml if ($LASTEXITCODE -ne 0) { throw 'cargo build failed' } -$ErrorActionPreference = 'Stop' New-Item -ItemType Directory -Force dist | Out-Null Copy-Item injector\target\release\PrestigeInjector.exe, injector\target\release\prestige_native.dll dist\ -Force Write-Host 'Done: dist\PrestigeInjector.exe + dist\prestige_native.dll' diff --git a/injector/crates/loader/src/bridge.rs b/injector/crates/loader/src/bridge.rs index 0160bee..c61e665 100644 --- a/injector/crates/loader/src/bridge.rs +++ b/injector/crates/loader/src/bridge.rs @@ -8,6 +8,8 @@ static JVMTI: AtomicPtr<*const *const c_void> = AtomicPtr::new(std::ptr::null_mu /// jclass whose bytes the ClassFileLoadHook should capture during the current RetransformClasses call (0 = none). static CAPTURE_TARGET: AtomicUsize = AtomicUsize::new(0); static CAPTURED: Mutex>> = Mutex::new(None); +/// Serialises classBytes calls: CAPTURE_TARGET holds a thread-local jclass reference, so only one thread may capture at a time. +static CAPTURE_LOCK: Mutex<()> = Mutex::new(()); /// Acquires the JVMTI capabilities and enables the class file load hook. Called once from the worker thread. pub unsafe fn init_jvmti(vm: VmPtr) -> Result<(), String> { @@ -56,6 +58,7 @@ pub unsafe extern "system" fn native_class_bytes(env: EnvPtr, _cls: JClass, targ if jvmti.is_null() { return std::ptr::null_mut(); } + let _serial = CAPTURE_LOCK.lock(); if let Ok(mut slot) = CAPTURED.lock() { *slot = None; } @@ -86,6 +89,9 @@ pub unsafe extern "system" fn native_redefine(env: EnvPtr, _cls: JClass, target: } let jni = Jni::new(env); let len = jni.get_array_length(bytes); + if len < 0 { + return -1; + } let mut buf = vec![0u8; len as usize]; jni.get_byte_array_region(bytes, 0, len, buf.as_mut_ptr()); let def = ClassDefinition { klass: target, class_byte_count: len, class_bytes: buf.as_ptr() }; @@ -104,3 +110,30 @@ pub unsafe extern "system" fn native_log(env: EnvPtr, _cls: JClass, message: JOb let text = Jni::new(env).get_string_utf(message); crate::log!("[java] {text}"); } + +extern "system" { + fn FreeLibraryAndExitThread(module: *mut c_void, exit_code: u32) -> !; +} + +/// void release(): stops the JVMTI hook and unloads this DLL after a short delay (self destruct). +/// Java must not call any native of NativeBridge afterwards. +pub unsafe extern "system" fn native_release(env: EnvPtr, cls: JClass) { + let jvmti = JVMTI.swap(std::ptr::null_mut(), Ordering::SeqCst); + if !jvmti.is_null() { + if let Err(e) = disable_class_file_load_hook(jvmti) { + crate::log!("release: {e}"); + } + } + // Late calls from Java then throw UnsatisfiedLinkError instead of jumping into the unmapped DLL. + let rc = Jni::new(env).unregister_natives(cls); + if rc != JNI_OK { + crate::log!("release: UnregisterNatives failed ({rc})"); + } + crate::log!("[java] released, unloading the loader"); + let module = crate::MODULE as usize; + // The thread below runs code from this DLL until FreeLibraryAndExitThread; the delay lets in-flight native calls return. + let _ = std::thread::Builder::new().name("prestige-unload".into()).spawn(move || { + std::thread::sleep(std::time::Duration::from_millis(2000)); + FreeLibraryAndExitThread(module as *mut c_void, 0); + }); +} diff --git a/injector/crates/loader/src/jni.rs b/injector/crates/loader/src/jni.rs index 8ee91f1..bb2ccb0 100644 --- a/injector/crates/loader/src/jni.rs +++ b/injector/crates/loader/src/jni.rs @@ -123,6 +123,9 @@ impl Jni { pub unsafe fn register_natives(&self, c: JClass, methods: &[NativeMethod]) -> JInt { f!(self, 215, unsafe extern "system" fn(EnvPtr, JClass, *const NativeMethod, JInt) -> JInt)(self.env, c, methods.as_ptr(), methods.len() as JInt) } + pub unsafe fn unregister_natives(&self, c: JClass) -> JInt { + f!(self, 216, unsafe extern "system" fn(EnvPtr, JClass) -> JInt)(self.env, c) + } pub unsafe fn exception_check(&self) -> bool { f!(self, 228, unsafe extern "system" fn(EnvPtr) -> JBool)(self.env) != 0 } @@ -168,6 +171,12 @@ pub unsafe fn attach_current_thread(vm: VmPtr) -> Result { Ok(env) } +/// Detaches the current thread (it must not hold Java frames); a thread attached from native code has to detach before it ends. +pub unsafe fn detach_current_thread(vm: VmPtr) { + let detach = std::mem::transmute::<*const c_void, unsafe extern "system" fn(VmPtr) -> JInt>(*(*vm).add(5)); + detach(vm); +} + pub unsafe fn get_jvmti(vm: VmPtr) -> Result { let get_env = std::mem::transmute::<*const c_void, unsafe extern "system" fn(VmPtr, *mut *mut c_void, JInt) -> JInt>(*(*vm).add(6)); let mut out: *mut c_void = std::ptr::null_mut(); @@ -253,3 +262,19 @@ pub unsafe fn redefine_class(j: JvmtiPtr, def: &ClassDefinition) -> JInt { let f = std::mem::transmute::<*const c_void, unsafe extern "system" fn(JvmtiPtr, JInt, *const ClassDefinition) -> JInt>(*(*j).add(86)); f(j, 1, def) } + +/// Disables the ClassFileLoadHook event and clears the callback table, so the JVM no longer calls into this DLL. +pub unsafe fn disable_class_file_load_hook(j: JvmtiPtr) -> Result<(), String> { + let set_mode = std::mem::transmute::<*const c_void, unsafe extern "C" fn(JvmtiPtr, JInt, JInt, JObject, ...) -> JInt>(*(*j).add(1)); + let rc = set_mode(j, 0, EVENT_CLASS_FILE_LOAD_HOOK, std::ptr::null_mut()); + if rc != 0 { + return Err(format!("SetEventNotificationMode(disable) failed ({rc})")); + } + let callbacks = [0usize; 40]; + let set_callbacks = std::mem::transmute::<*const c_void, unsafe extern "system" fn(JvmtiPtr, *const usize, JInt) -> JInt>(*(*j).add(121)); + let rc = set_callbacks(j, callbacks.as_ptr(), std::mem::size_of_val(&callbacks) as JInt); + if rc != 0 { + return Err(format!("SetEventCallbacks(clear) failed ({rc})")); + } + Ok(()) +} diff --git a/injector/crates/loader/src/worker.rs b/injector/crates/loader/src/worker.rs index 77b532c..c28901e 100644 --- a/injector/crates/loader/src/worker.rs +++ b/injector/crates/loader/src/worker.rs @@ -1,5 +1,5 @@ //! The loader worker: JVM discovery, payload installation into Fabric's Knot class loader, native registration, bootstrap. -use crate::bridge::{init_jvmti, native_class_bytes, native_log, native_redefine}; +use crate::bridge::{init_jvmti, native_class_bytes, native_log, native_redefine, native_release}; use crate::jni::*; use crate::log; use std::ffi::{c_char, c_void}; @@ -28,6 +28,13 @@ pub fn run() { unsafe fn install() -> Result<(), String> { let vm = find_vm()?; let env = attach_current_thread(vm)?; + struct Detach(VmPtr); + impl Drop for Detach { + fn drop(&mut self) { + unsafe { detach_current_thread(self.0) } + } + } + let _detach = Detach(vm); let jni = Jni::new(env); log!("attached to the JVM"); @@ -160,6 +167,11 @@ unsafe fn register_natives(jni: &Jni, loader: JObject) -> Result<(), String> { signature: c"(Ljava/lang/Class;[B)I".as_ptr(), fn_ptr: native_redefine as *const () as usize as *mut c_void, }, + NativeMethod { + name: c"release".as_ptr(), + signature: c"()V".as_ptr(), + fn_ptr: native_release as *const () as usize as *mut c_void, + }, NativeMethod { name: c"log".as_ptr(), signature: c"(Ljava/lang/String;)V".as_ptr(), diff --git a/src/main/java/dev/zprestige/prestige/client/Prestige.java b/src/main/java/dev/zprestige/prestige/client/Prestige.java index 5077a55..b90d100 100644 --- a/src/main/java/dev/zprestige/prestige/client/Prestige.java +++ b/src/main/java/dev/zprestige/prestige/client/Prestige.java @@ -40,6 +40,7 @@ import net.minecraft.client.gui.screen.ChatScreen; public class Prestige implements ModInitializer { private static final AtomicBoolean initialized = new AtomicBoolean(false); + private static final java.util.concurrent.atomic.AtomicInteger renderErrorCount = new java.util.concurrent.atomic.AtomicInteger(); public static EventBus eventBus; public static FontManager fontManager; public static ModuleManager moduleManager; @@ -95,23 +96,31 @@ public class Prestige implements ModInitializer { .register( (RenderCallback)(hudRenderer, hudContext) -> { if (!selfDestructed) { - if (MinecraftClient.getInstance().world != null && MinecraftClient.getInstance().player != null) { - if ((MinecraftAccess.mc.currentScreen == null || MinecraftAccess.mc.currentScreen instanceof ChatScreen) - && eventBus.hasListeners(Render2DEvent.class)) { - new Render2DEvent((hudRenderer), (hudContext)).post(); - } + try { + if (MinecraftClient.getInstance().world != null && MinecraftClient.getInstance().player != null) { + if ((MinecraftAccess.mc.currentScreen == null || MinecraftAccess.mc.currentScreen instanceof ChatScreen) + && eventBus.hasListeners(Render2DEvent.class)) { + new Render2DEvent((hudRenderer), (hudContext)).post(); + } - new FrameEvent((hudRenderer), (hudContext)).post(); - ShapeRenderer.flushDrawQueue(); + new FrameEvent((hudRenderer), (hudContext)).post(); + ShapeRenderer.flushDrawQueue(); + } + } catch (Throwable error) { + logRenderError(error); } } } ); RenderEvents.WORLD_RENDER.register((RenderCallback)(worldRenderer, worldContext) -> { if (!selfDestructed) { - if (MinecraftClient.getInstance().world != null && MinecraftClient.getInstance().player != null) { - Render2D.captureViewport(); - new Render3DEvent((worldRenderer), (worldContext)).post(); + try { + if (MinecraftClient.getInstance().world != null && MinecraftClient.getInstance().player != null) { + Render2D.captureViewport(); + new Render3DEvent((worldRenderer), (worldContext)).post(); + } + } catch (Throwable error) { + logRenderError(error); } } }); @@ -125,6 +134,12 @@ public class Prestige implements ModInitializer { } } + public static void logRenderError(Throwable error) { + if (renderErrorCount.getAndIncrement() < 5) { + error.printStackTrace(); + } + } + public static boolean needsWorldRender() { return EspShaderManager.enabled || eventBus != null && eventBus.hasListeners(Render3DEvent.class); } diff --git a/src/main/java/dev/zprestige/prestige/combat/BotFilter.java b/src/main/java/dev/zprestige/prestige/combat/BotFilter.java index d6554f2..e58369b 100644 --- a/src/main/java/dev/zprestige/prestige/combat/BotFilter.java +++ b/src/main/java/dev/zprestige/prestige/combat/BotFilter.java @@ -29,7 +29,7 @@ public class BotFilter implements MinecraftAccess { public void onClientTick(ClientTickEvent event) { this.validityCache.clear(); if (!this.validatedPlayers.isEmpty()) { - this.validatedPlayers.removeIf(player -> !((net.minecraft.entity.Entity)player).isAlive()); + this.validatedPlayers.removeIf(player -> !((net.minecraft.entity.Entity)player).isAlive() || ((net.minecraft.entity.Entity)player).isRemoved()); } } diff --git a/src/main/java/dev/zprestige/prestige/combat/ExplosionDamageCalculator.java b/src/main/java/dev/zprestige/prestige/combat/ExplosionDamageCalculator.java index 4dc1e01..6e5deda 100644 --- a/src/main/java/dev/zprestige/prestige/combat/ExplosionDamageCalculator.java +++ b/src/main/java/dev/zprestige/prestige/combat/ExplosionDamageCalculator.java @@ -73,6 +73,10 @@ public class ExplosionDamageCalculator implements MinecraftAccess { } public double getCrystalDamage(LivingEntity target, Vec3d crystalPos, boolean predictMotion, BlockPos obsidianPos, boolean ignoreTerrain) { + if (mc.world == null || mc.player == null || mc.getNetworkHandler() == null) { + return 0.0; + } + long worldTime = mc.world.getTime(); if (worldTime != this.cacheTime) { this.damageCache.clear(); @@ -305,6 +309,10 @@ public class ExplosionDamageCalculator implements MinecraftAccess { } private double getExposureWithOffset(Vec3d source, Entity entity, Vec3d boxOffset, RaycastContext context, BlockPos obsidianPos, boolean ignoreTerrain, BlockPos airPos, BlockPos placedBlockPos, BlockState placedBlockState) { + if (context == null) { + context = this.raycastContext = new RaycastContext(null, null, ShapeType.COLLIDER, FluidHandling.ANY, mc.player); + } + Box box = entity.getBoundingBox(); if (boxOffset != null) { box = box.offset(boxOffset.x, boxOffset.y, boxOffset.z); diff --git a/src/main/java/dev/zprestige/prestige/combat/TargetTracker.java b/src/main/java/dev/zprestige/prestige/combat/TargetTracker.java index 75e0f36..76c0d80 100644 --- a/src/main/java/dev/zprestige/prestige/combat/TargetTracker.java +++ b/src/main/java/dev/zprestige/prestige/combat/TargetTracker.java @@ -22,7 +22,7 @@ public class TargetTracker implements MinecraftAccess { @Subscribe public void onClientTick(ClientTickEvent event) { - if (this.target != null && (mc.world == null || !this.target.isAlive() || this.target.distanceTo(mc.player) > 15.0F || System.currentTimeMillis() - this.lastAttackTime > 5000L)) { + if (this.target != null && (mc.world == null || mc.player == null || !this.target.isAlive() || this.target.distanceTo(mc.player) > 15.0F || System.currentTimeMillis() - this.lastAttackTime > 5000L)) { this.target = null; } } diff --git a/src/main/java/dev/zprestige/prestige/config/ClasspathResourcePack.java b/src/main/java/dev/zprestige/prestige/config/ClasspathResourcePack.java index 278719a..c74f1d3 100644 --- a/src/main/java/dev/zprestige/prestige/config/ClasspathResourcePack.java +++ b/src/main/java/dev/zprestige/prestige/config/ClasspathResourcePack.java @@ -1,7 +1,9 @@ package dev.zprestige.prestige.config; import dev.zprestige.prestige.client.Prestige; +import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStream; import java.util.Optional; import java.util.Set; import net.minecraft.resource.InputSupplier; @@ -33,7 +35,7 @@ public class ClasspathResourcePack implements ResourcePack { @Override public Set getNamespaces(ResourceType type) { - return null; + return Set.of(); } @Nullable @@ -61,15 +63,24 @@ public class ClasspathResourcePack implements ResourcePack { public void close() { } + private static InputStream openOrThrow(String path) throws IOException { + InputStream stream = Prestige.class.getClassLoader().getResourceAsStream(path); + if (stream == null) { + throw new FileNotFoundException(path); + } + + return stream; + } + public static Resource createAssetResource(Identifier id) { - return new Resource(new ClasspathResourcePack(), () -> Prestige.class.getClassLoader().getResourceAsStream("assets/prestige/" + id.getPath())); + return new Resource(new ClasspathResourcePack(), () -> openOrThrow("assets/prestige/" + id.getPath())); } public static Resource createResource(String path) { - return new Resource(new ClasspathResourcePack(), () -> Prestige.class.getClassLoader().getResourceAsStream(path)); + return new Resource(new ClasspathResourcePack(), () -> openOrThrow(path)); } public static InputSupplier createInputSupplier(String path) { - return () -> Prestige.class.getClassLoader().getResourceAsStream(path); + return () -> openOrThrow(path); } } diff --git a/src/main/java/dev/zprestige/prestige/data/OutlineCaptureConsumer.java b/src/main/java/dev/zprestige/prestige/data/OutlineCaptureConsumer.java index 9109d18..c1662ec 100644 --- a/src/main/java/dev/zprestige/prestige/data/OutlineCaptureConsumer.java +++ b/src/main/java/dev/zprestige/prestige/data/OutlineCaptureConsumer.java @@ -24,7 +24,7 @@ public record OutlineCaptureConsumer(MeshBuilder writer) implements VertexConsum @Override public VertexConsumer lineWidth(float width) { - return null; + return this; } @Override diff --git a/src/main/java/dev/zprestige/prestige/event/ModuleStateSync.java b/src/main/java/dev/zprestige/prestige/event/ModuleStateSync.java index 9cf41b3..aa47fe9 100644 --- a/src/main/java/dev/zprestige/prestige/event/ModuleStateSync.java +++ b/src/main/java/dev/zprestige/prestige/event/ModuleStateSync.java @@ -14,7 +14,14 @@ public class ModuleStateSync { super(); this.hudDrawn = false; Prestige.eventBus.register(this); - RenderEvents.HUD_RENDER.register((RenderCallback)(renderer, context) -> this.hudDrawn = Prestige.hudEditorScreen.renderHud(renderer, context)); + RenderEvents.HUD_RENDER.register((RenderCallback)(renderer, context) -> { + try { + this.hudDrawn = Prestige.hudEditorScreen.renderHud(renderer, context); + } catch (Throwable error) { + this.hudDrawn = false; + Prestige.logRenderError(error); + } + }); } @Subscribe @@ -23,12 +30,16 @@ public class ModuleStateSync { for (Module module : (Iterable)(Prestige.moduleManager.getModules())) { boolean enabled = module.isEnabled(); - if (enabled && !module.getLastEnabled()) { - module.applyState(); - } + try { + if (enabled && !module.getLastEnabled()) { + module.applyState(); + } - if (!enabled && module.getLastEnabled()) { - module.applyState(); + if (!enabled && module.getLastEnabled()) { + module.applyState(); + } + } catch (Throwable error) { + error.printStackTrace(); } module.setLastEnabled(enabled); diff --git a/src/main/java/dev/zprestige/prestige/gui/HudEditorScreen.java b/src/main/java/dev/zprestige/prestige/gui/HudEditorScreen.java index 2dd7c3d..0d044cc 100644 --- a/src/main/java/dev/zprestige/prestige/gui/HudEditorScreen.java +++ b/src/main/java/dev/zprestige/prestige/gui/HudEditorScreen.java @@ -102,8 +102,12 @@ public class HudEditorScreen extends BaseScreen { this.lastFrameTime = System.currentTimeMillis(); RenderEvents.HUD_RENDER.register((RenderCallback)(renderBatch, renderMatrices) -> { if (MinecraftAccess.mc.currentScreen == this) { - Mouse mouse = MinecraftAccess.mc.mouse; - this.renderEditor(renderBatch, renderMatrices, (int)Math.round(mouse.getX() / MinecraftAccess.mc.getWindow().getScaleFactor()), (int)Math.round(mouse.getY() / MinecraftAccess.mc.getWindow().getScaleFactor()), Render2D.getTickDelta()); + try { + Mouse mouse = MinecraftAccess.mc.mouse; + this.renderEditor(renderBatch, renderMatrices, (int)Math.round(mouse.getX() / MinecraftAccess.mc.getWindow().getScaleFactor()), (int)Math.round(mouse.getY() / MinecraftAccess.mc.getWindow().getScaleFactor()), Render2D.getTickDelta()); + } catch (Throwable error) { + Prestige.logRenderError(error); // do not let an editor render error reach the game loop + } } }); } @@ -456,7 +460,11 @@ public class HudEditorScreen extends BaseScreen { hudElement.resolvePosition(); } - hudElement.render(batch, matrices, matrix); + try { + hudElement.render(batch, matrices, matrix); + } catch (Throwable error) { + Prestige.logRenderError(error); // one broken element must not take down the whole HUD + } renderedAny = true; } } diff --git a/src/main/java/dev/zprestige/prestige/hud/ModuleNotificationsElement.java b/src/main/java/dev/zprestige/prestige/hud/ModuleNotificationsElement.java index 79126bc..1efbf69 100644 --- a/src/main/java/dev/zprestige/prestige/hud/ModuleNotificationsElement.java +++ b/src/main/java/dev/zprestige/prestige/hud/ModuleNotificationsElement.java @@ -65,6 +65,9 @@ public class ModuleNotificationsElement extends HudElement { } this.notifications.add(new ModuleToast(title, message, type)); + while (this.notifications.size() > 32) { + this.notifications.remove(0); // toasts only expire while the HUD is drawn; keep the list bounded + } } @Override @@ -186,7 +189,9 @@ public class ModuleNotificationsElement extends HudElement { RoundedRectShader.drawRoundedRect(batch, cameraData, matrix, toastX, toastY, toastWidth, toastHeight, backgroundColor, 4.0F); GlTextureHandle typeIcon = this.getTypeIcon(toast.type); float iconX = rightAligned ? toastRight - 5.5F - 10.0F : toastX + 5.5F; - RoundedTextureShader.drawTexture(batch, cameraData, matrix, typeIcon, iconX, toastY + (toastHeight - 10.0F) / 2.0F, 10.0F, 10.0F, iconColor); + if (typeIcon != null) { + RoundedTextureShader.drawTexture(batch, cameraData, matrix, typeIcon, iconX, toastY + (toastHeight - 10.0F) / 2.0F, 10.0F, 10.0F, iconColor); + } float titleWidth = Prestige.fontManager.getDefaultFont().getTextWidth(toast.title) * 0.9F; float messageWidth = Prestige.fontManager.getDefaultFont().getTextWidth(toast.message) * 0.75F; float titleX = rightAligned ? iconX - 3.0F - titleWidth : iconX + 10.0F + 3.0F; diff --git a/src/main/java/dev/zprestige/prestige/hud/TargetElement.java b/src/main/java/dev/zprestige/prestige/hud/TargetElement.java index 35a5bf4..0cc9b60 100644 --- a/src/main/java/dev/zprestige/prestige/hud/TargetElement.java +++ b/src/main/java/dev/zprestige/prestige/hud/TargetElement.java @@ -74,7 +74,7 @@ public class TargetElement extends HudElement { this.frameTimeMs = System.currentTimeMillis() - this.lastFrameTime; this.lastFrameTime = System.currentTimeMillis(); PlayerEntity targetPlayer = mc.currentScreen instanceof HudEditorScreen ? mc.player : Prestige.targetTracker.getTarget(); - if (targetPlayer != null && (targetPlayer.getUuid() == null || mc.getNetworkHandler().getPlayerListEntry(targetPlayer.getUuid()) == null)) { + if (targetPlayer != null && (mc.getNetworkHandler() == null || targetPlayer.getUuid() == null || mc.getNetworkHandler().getPlayerListEntry(targetPlayer.getUuid()) == null)) { targetPlayer = null; } @@ -99,9 +99,17 @@ public class TargetElement extends HudElement { this.targetName = targetPlayer.getName().getString(); float rawHealth = targetPlayer.getHealth() + targetPlayer.getAbsorptionAmount(); + if (!Float.isFinite(rawHealth)) { + rawHealth = 0.0F; // NaN/Infinity health (server-controlled) would make floorToDecimals throw + } + this.health = (float)Math.ceil(rawHealth); - this.healthFraction = this.getHealthFraction(targetPlayer); + this.healthFraction = UiMath.clamp(rawHealth / 20.0F, 0.0F, 1.0F); this.distance = mc.player != null ? targetPlayer.distanceTo(mc.player) : 0.0F; + if (!Float.isFinite(this.distance)) { + this.distance = 0.0F; + } + if (this.targetUuid == null || !this.targetUuid.equals(targetPlayer.getUuid())) { this.targetUuid = targetPlayer.getUuid(); this.displayedHealth = this.health; diff --git a/src/main/java/dev/zprestige/prestige/inject/Bootstrap.java b/src/main/java/dev/zprestige/prestige/inject/Bootstrap.java index 28cf532..3da9d28 100644 --- a/src/main/java/dev/zprestige/prestige/inject/Bootstrap.java +++ b/src/main/java/dev/zprestige/prestige/inject/Bootstrap.java @@ -8,6 +8,7 @@ import dev.zprestige.prestige.ui.mc.Entry; import java.util.ArrayList; import java.util.List; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.function.ToIntBiFunction; import net.fabricmc.loader.api.FabricLoader; @@ -22,6 +23,8 @@ import net.minecraft.client.MinecraftClient; */ public final class Bootstrap { private static boolean started; + /** Class bytes as they were before the hooks, kept so self destruct can put the game classes back. */ + private static final Map, byte[]> ORIGINALS = new ConcurrentHashMap<>(); private Bootstrap() {} @@ -85,6 +88,7 @@ public final class Bootstrap { for (byte[] helper : result.helpers().values()) Definer.define(helper); int code = redefine.applyAsInt(type, result.target()); if (code != 0) throw new IllegalStateException("redefine returned JVMTI error " + code); + ORIGINALS.put(type, current); report.redefined++; report.moved += result.movedMethods(); } catch (Throwable t) { @@ -96,6 +100,31 @@ public final class Bootstrap { return report; } + /** True when this client was injected, i.e. hooks were applied through JVMTI and can be taken out again. */ + public static boolean isInjected() { + return !ORIGINALS.isEmpty(); + } + + /** + * Self destruct, part 1: redefines every hooked class back to its original bytes so no game code references the client any + * more. Returns the number of classes that could not be restored. + */ + public static int uninstall() { + int failed = 0; + for (Map.Entry, byte[]> entry : ORIGINALS.entrySet()) { + try { + int code = NativeBridge.redefine(entry.getKey(), entry.getValue()); + if (code != 0) throw new IllegalStateException("JVMTI error " + code); + ORIGINALS.remove(entry.getKey()); + } catch (Throwable t) { + failed++; + log("could not restore " + entry.getKey().getName() + ": " + t); + } + } + log("hooks removed" + (failed == 0 ? "" : ", " + failed + " classes could not be restored")); + return failed; + } + private static void initClient() { try { new Prestige().onInitialize(); diff --git a/src/main/java/dev/zprestige/prestige/inject/NativeBridge.java b/src/main/java/dev/zprestige/prestige/inject/NativeBridge.java index fb77435..d0725fd 100644 --- a/src/main/java/dev/zprestige/prestige/inject/NativeBridge.java +++ b/src/main/java/dev/zprestige/prestige/inject/NativeBridge.java @@ -10,6 +10,9 @@ public final class NativeBridge { /** Appends a line to prestige-native.log, which the injector tails to show progress. */ public static native void log(String message); + /** Disables the class file load hook and unloads prestige_native.dll shortly afterwards; nothing may call the natives after this. */ + public static native void release(); + /** JVMTI RedefineClasses for one class; returns the JVMTI error code, 0 on success. */ public static native int redefine(Class type, byte[] bytes); } diff --git a/src/main/java/dev/zprestige/prestige/input/KeybindManager.java b/src/main/java/dev/zprestige/prestige/input/KeybindManager.java index 922e45c..5577a4a 100644 --- a/src/main/java/dev/zprestige/prestige/input/KeybindManager.java +++ b/src/main/java/dev/zprestige/prestige/input/KeybindManager.java @@ -32,22 +32,26 @@ public class KeybindManager implements MinecraftAccess { } for (Module module : (Iterable)(Prestige.moduleManager.getModules())) { - int moduleKey = VirtualKeyMapper.toGlfwKey(module.getKey()); - if (module.getKeybind().isHoldMode()) { - if (module.isEnabled() && mc.currentScreen != null) { - module.toggle(); - } else if (event.getAction() == 0) { - if (module.isEnabled() && moduleKey == event.getKey()) { + try { + int moduleKey = VirtualKeyMapper.toGlfwKey(module.getKey()); + if (module.getKeybind().isHoldMode()) { + if (module.isEnabled() && mc.currentScreen != null) { + module.toggle(); + } else if (event.getAction() == 0) { + if (module.isEnabled() && moduleKey == event.getKey()) { + module.toggle(); + } + } else if (event.getAction() == 1 && mc.currentScreen == null && !module.isEnabled() && moduleKey == event.getKey()) { module.toggle(); } - } else if (event.getAction() == 1 && mc.currentScreen == null && !module.isEnabled() && moduleKey == event.getKey()) { + } else if (mc.currentScreen == null + && module.getKey() != -1 + && event.getAction() == 1 + && moduleKey == event.getKey()) { module.toggle(); } - } else if (mc.currentScreen == null - && module.getKey() != -1 - && event.getAction() == 1 - && moduleKey == event.getKey()) { - module.toggle(); + } catch (Throwable error) { + error.printStackTrace(); } } } @@ -73,20 +77,24 @@ public class KeybindManager implements MinecraftAccess { @Subscribe public void onMouseButton(MouseButtonEvent event) { - if (!Prestige.simulatedClickManager.isSimulated(event.getButton())) { + if (Prestige.simulatedClickManager != null && !Prestige.simulatedClickManager.isSimulated(event.getButton())) { for (Module module : (Iterable)(Prestige.moduleManager.getModules())) { - if (module.getKey() != -1 - && MOUSE_KEYCODE_TO_BUTTON.containsKey(module.getKey()) - && (Integer)MOUSE_KEYCODE_TO_BUTTON.get(module.getKey()) == event.getButton()) { - if (module.getKeybind().isHoldMode()) { - if (event.getAction() == 1 && mc.currentScreen == null && !module.isEnabled()) { - module.toggle(); - } else if (event.getAction() == 0 && module.isEnabled()) { + try { + if (module.getKey() != -1 + && MOUSE_KEYCODE_TO_BUTTON.containsKey(module.getKey()) + && (Integer)MOUSE_KEYCODE_TO_BUTTON.get(module.getKey()) == event.getButton()) { + if (module.getKeybind().isHoldMode()) { + if (event.getAction() == 1 && mc.currentScreen == null && !module.isEnabled()) { + module.toggle(); + } else if (event.getAction() == 0 && module.isEnabled()) { + module.toggle(); + } + } else if (mc.currentScreen == null && event.getAction() == 1) { module.toggle(); } - } else if (mc.currentScreen == null && event.getAction() == 1) { - module.toggle(); } + } catch (Throwable error) { + error.printStackTrace(); } } } diff --git a/src/main/java/dev/zprestige/prestige/inventory/InventoryUtil.java b/src/main/java/dev/zprestige/prestige/inventory/InventoryUtil.java index 2661532..39123a4 100644 --- a/src/main/java/dev/zprestige/prestige/inventory/InventoryUtil.java +++ b/src/main/java/dev/zprestige/prestige/inventory/InventoryUtil.java @@ -7,6 +7,7 @@ import java.util.List; import net.minecraft.block.Block; import net.minecraft.client.gui.screen.ingame.InventoryScreen; import net.minecraft.component.DataComponentTypes; +import net.minecraft.component.type.PotionContentsComponent; import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.item.Item; import net.minecraft.item.ItemStack; @@ -210,8 +211,12 @@ public class InventoryUtil implements MinecraftAccess { public static boolean hasEffect(ItemStack stack, RegistryEntry effect) { boolean found = false; + PotionContentsComponent contents = stack.get(DataComponentTypes.POTION_CONTENTS); + if (contents == null) { + return false; + } - for (StatusEffectInstance instance : stack.get(DataComponentTypes.POTION_CONTENTS).getEffects()) { + for (StatusEffectInstance instance : contents.getEffects()) { if (instance.getEffectType() == effect) { found = true; } diff --git a/src/main/java/dev/zprestige/prestige/math/BlockChangeTracker.java b/src/main/java/dev/zprestige/prestige/math/BlockChangeTracker.java index ba0035e..e5aa908 100644 --- a/src/main/java/dev/zprestige/prestige/math/BlockChangeTracker.java +++ b/src/main/java/dev/zprestige/prestige/math/BlockChangeTracker.java @@ -3,15 +3,15 @@ package dev.zprestige.prestige.math; import dev.zprestige.prestige.client.Prestige; import dev.zprestige.prestige.network.BlockChangeListener; import dev.zprestige.prestige.render.MinecraftAccess; -import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; import java.util.Map; import java.util.Map.Entry; import net.minecraft.util.math.BlockPos; public final class BlockChangeTracker implements MinecraftAccess { - public static final Map CLEARED_BLOCKS = new HashMap(); - public static final Map EXPLOSIONS = new HashMap(); - public static final Map PLACED_BLOCKS = new HashMap(); + public static final Map CLEARED_BLOCKS = new ConcurrentHashMap(); + public static final Map EXPLOSIONS = new ConcurrentHashMap(); + public static final Map PLACED_BLOCKS = new ConcurrentHashMap(); private static boolean listenerRegistered; private BlockChangeTracker() { diff --git a/src/main/java/dev/zprestige/prestige/math/LegacyParticlePhysics.java b/src/main/java/dev/zprestige/prestige/math/LegacyParticlePhysics.java index a5a3e5e..f25a837 100644 --- a/src/main/java/dev/zprestige/prestige/math/LegacyParticlePhysics.java +++ b/src/main/java/dev/zprestige/prestige/math/LegacyParticlePhysics.java @@ -8,7 +8,7 @@ import net.minecraft.util.math.Vec3d; public class LegacyParticlePhysics extends HitParticlePhysics { @Override public void updateMotion(HitParticle particle, boolean physicsEnabled) { - if (!physicsEnabled) { + if (!physicsEnabled || MinecraftAccess.mc.world == null) { this.applyLegacyMotion(particle); } else { if (!this.isPassable(MinecraftAccess.mc.world.getBlockState(BlockPos.ofFloored(particle.pos.x, particle.pos.y, particle.pos.z + particle.velocity.z)))) { diff --git a/src/main/java/dev/zprestige/prestige/math/PopParticlePhysics.java b/src/main/java/dev/zprestige/prestige/math/PopParticlePhysics.java index 72d69ca..8b57f5c 100644 --- a/src/main/java/dev/zprestige/prestige/math/PopParticlePhysics.java +++ b/src/main/java/dev/zprestige/prestige/math/PopParticlePhysics.java @@ -8,7 +8,7 @@ import net.minecraft.util.math.Vec3d; public class PopParticlePhysics extends HitParticlePhysics { @Override public void updateMotion(HitParticle particle, boolean physicsEnabled) { - if (!physicsEnabled) { + if (!physicsEnabled || MinecraftAccess.mc.world == null) { this.applyPopMotion(particle); } else { if (!this.isPassable(MinecraftAccess.mc.world.getBlockState(BlockPos.ofFloored(particle.pos.x, particle.pos.y, particle.pos.z + particle.velocity.z)))) { diff --git a/src/main/java/dev/zprestige/prestige/mixin/AbstractBlockStateMixin.java b/src/main/java/dev/zprestige/prestige/mixin/AbstractBlockStateMixin.java index 1848637..68becb7 100644 --- a/src/main/java/dev/zprestige/prestige/mixin/AbstractBlockStateMixin.java +++ b/src/main/java/dev/zprestige/prestige/mixin/AbstractBlockStateMixin.java @@ -19,7 +19,7 @@ public class AbstractBlockStateMixin { cancellable = true ) private void overrideAmbientOcclusionLightLevelAtReturn(BlockView blockView, BlockPos blockPos, InjectionCallback cir) { - if (HookBridge.hasAmbientOcclusionListeners(Prestige.eventBus, BlockVisibilityEvent.class)) { + if (Prestige.eventBus != null && HookBridge.hasAmbientOcclusionListeners(Prestige.eventBus, BlockVisibilityEvent.class)) { BlockVisibilityEvent event = HookBridge.createAmbientOcclusionEvent(null); if (HookBridge.postAmbientOcclusionEvent(event)) { cir.setReturnValue(1.0F); diff --git a/src/main/java/dev/zprestige/prestige/mixin/AbstractClientPlayerEntityMixin.java b/src/main/java/dev/zprestige/prestige/mixin/AbstractClientPlayerEntityMixin.java index 8b791a8..ab2a99d 100644 --- a/src/main/java/dev/zprestige/prestige/mixin/AbstractClientPlayerEntityMixin.java +++ b/src/main/java/dev/zprestige/prestige/mixin/AbstractClientPlayerEntityMixin.java @@ -18,7 +18,7 @@ public class AbstractClientPlayerEntityMixin { cancellable = true ) private void overridePlayerSkinAtGetSkinReturn(InjectionCallback ci) { - if (HookBridge.hasPlayerSkinListeners(Prestige.eventBus, SkinTexturesEvent.class)) { + if (Prestige.eventBus != null && HookBridge.hasPlayerSkinListeners(Prestige.eventBus, SkinTexturesEvent.class)) { SkinTexturesEvent event = HookBridge.createPlayerSkinEvent((SkinTextures)ci.getReturnValue(), ((AbstractClientPlayerEntity)(Object)this).getName().getString()); if (HookBridge.postPlayerSkinEvent(event)) { ci.setReturnValue(HookBridge.getPlayerSkinTextures(event)); diff --git a/src/main/java/dev/zprestige/prestige/mixin/BlockMixin.java b/src/main/java/dev/zprestige/prestige/mixin/BlockMixin.java index 679dd96..660b59b 100644 --- a/src/main/java/dev/zprestige/prestige/mixin/BlockMixin.java +++ b/src/main/java/dev/zprestige/prestige/mixin/BlockMixin.java @@ -19,7 +19,7 @@ public class BlockMixin { cancellable = true ) public static void overrideShouldDrawSideAtHead(BlockState state, BlockState otherState, Direction side, InjectionCallback cir) { - if (HookBridge.hasDrawSideListeners(Prestige.eventBus, BlockVisibilityEvent.class)) { + if (Prestige.eventBus != null && HookBridge.hasDrawSideListeners(Prestige.eventBus, BlockVisibilityEvent.class)) { BlockVisibilityEvent event = HookBridge.createDrawSideEvent(state.getBlock()); if (HookBridge.postDrawSideEvent(event)) { cir.setReturnValue(HookBridge.shouldDrawSide(event)); diff --git a/src/main/java/dev/zprestige/prestige/mixin/CameraMixin.java b/src/main/java/dev/zprestige/prestige/mixin/CameraMixin.java index 31ac256..88d32a3 100644 --- a/src/main/java/dev/zprestige/prestige/mixin/CameraMixin.java +++ b/src/main/java/dev/zprestige/prestige/mixin/CameraMixin.java @@ -18,12 +18,15 @@ public class CameraMixin { cancellable = true ) private void overrideCameraPosAtSetPosHead(double x, double y, double z, InjectionCallback ci) { - if (!CameraPositionEvent.applyingOverride && HookBridge.hasCameraPosListeners(Prestige.eventBus, CameraPositionEvent.class)) { + if (!CameraPositionEvent.applyingOverride && Prestige.eventBus != null && HookBridge.hasCameraPosListeners(Prestige.eventBus, CameraPositionEvent.class)) { CameraPositionEvent event = HookBridge.createCameraPosEvent(x, y, z); if (HookBridge.postCameraPosEvent(event)) { CameraPositionEvent.applyingOverride = true; - HookBridge.setCameraPos(HookBridge.createCameraPosAccessor((Camera)(Object)this), HookBridge.getCameraPosX(event), HookBridge.getCameraPosY(event), HookBridge.getCameraPosZ(event)); - CameraPositionEvent.applyingOverride = false; + try { + HookBridge.setCameraPos(HookBridge.createCameraPosAccessor((Camera)(Object)this), HookBridge.getCameraPosX(event), HookBridge.getCameraPosY(event), HookBridge.getCameraPosZ(event)); + } finally { + CameraPositionEvent.applyingOverride = false; + } ci.cancel(); } } @@ -39,8 +42,11 @@ public class CameraMixin { CameraRotationEvent event = HookBridge.createCameraRotationEvent(yaw, pitch); if (HookBridge.postCameraRotationEvent(event)) { CameraRotationEvent.applyingOverride = true; - HookBridge.setCameraRotation(HookBridge.createCameraRotationAccessor((Camera)(Object)this), HookBridge.getCameraYaw(event), HookBridge.getCameraPitch(event)); - CameraRotationEvent.applyingOverride = false; + try { + HookBridge.setCameraRotation(HookBridge.createCameraRotationAccessor((Camera)(Object)this), HookBridge.getCameraYaw(event), HookBridge.getCameraPitch(event)); + } finally { + CameraRotationEvent.applyingOverride = false; + } ci.cancel(); } } diff --git a/src/main/java/dev/zprestige/prestige/mixin/ChunkOcclusionDataBuilderMixin.java b/src/main/java/dev/zprestige/prestige/mixin/ChunkOcclusionDataBuilderMixin.java index dd5fa2f..df650cb 100644 --- a/src/main/java/dev/zprestige/prestige/mixin/ChunkOcclusionDataBuilderMixin.java +++ b/src/main/java/dev/zprestige/prestige/mixin/ChunkOcclusionDataBuilderMixin.java @@ -18,7 +18,7 @@ public class ChunkOcclusionDataBuilderMixin { cancellable = true ) private void skipChunkOcclusionAtMarkClosedHead(BlockPos pos, InjectionCallback ci) { - if (HookBridge.hasChunkOcclusionListeners(Prestige.eventBus, BlockVisibilityEvent.class)) { + if (Prestige.eventBus != null && HookBridge.hasChunkOcclusionListeners(Prestige.eventBus, BlockVisibilityEvent.class)) { if (HookBridge.postChunkOcclusionEvent(HookBridge.createChunkOcclusionEvent(null))) { ci.cancel(); } diff --git a/src/main/java/dev/zprestige/prestige/mixin/ClientConnectionMixin.java b/src/main/java/dev/zprestige/prestige/mixin/ClientConnectionMixin.java index d83a68e..13b2f26 100644 --- a/src/main/java/dev/zprestige/prestige/mixin/ClientConnectionMixin.java +++ b/src/main/java/dev/zprestige/prestige/mixin/ClientConnectionMixin.java @@ -20,7 +20,7 @@ public class ClientConnectionMixin { cancellable = true ) private static void postPacketReceiveEventAtHandlePacketHead(Packet packet, PacketListener listener, InjectionCallback ci) { - if (HookBridge.hasPacketReceiveListeners(Prestige.eventBus, PacketReceiveEvent.class)) { + if (Prestige.eventBus != null && HookBridge.hasPacketReceiveListeners(Prestige.eventBus, PacketReceiveEvent.class)) { PacketReceiveEvent event = HookBridge.createPacketReceiveEvent(packet); if (HookBridge.postPacketReceiveEvent(event)) { ci.cancel(); diff --git a/src/main/java/dev/zprestige/prestige/mixin/ClientPlayNetworkHandlerMixin.java b/src/main/java/dev/zprestige/prestige/mixin/ClientPlayNetworkHandlerMixin.java index aadac8b..bb1b7d6 100644 --- a/src/main/java/dev/zprestige/prestige/mixin/ClientPlayNetworkHandlerMixin.java +++ b/src/main/java/dev/zprestige/prestige/mixin/ClientPlayNetworkHandlerMixin.java @@ -34,7 +34,12 @@ public class ClientPlayNetworkHandlerMixin { target = {@CTarget("TAIL")} ) private void postChunkDataEventAtTail(ChunkDataS2CPacket packet, InjectionCallback info) { + if (this.world == null) { + return; + } WorldChunk chunk = this.world.getChunk(packet.getChunkX(), packet.getChunkZ()); - HookBridge.postChunkDataEvent(HookBridge.createChunkDataEvent(chunk)); + if (chunk != null) { + HookBridge.postChunkDataEvent(HookBridge.createChunkDataEvent(chunk)); + } } } diff --git a/src/main/java/dev/zprestige/prestige/mixin/EntityMixin.java b/src/main/java/dev/zprestige/prestige/mixin/EntityMixin.java index 1da5f50..ca4af2f 100644 --- a/src/main/java/dev/zprestige/prestige/mixin/EntityMixin.java +++ b/src/main/java/dev/zprestige/prestige/mixin/EntityMixin.java @@ -55,8 +55,8 @@ public abstract class EntityMixin { ) private void overrideBoundingBoxAtReturn(InjectionCallback cir) { Entity entity = (Entity)(Object)this; - if (entity != null) { - if (HookBridge.hasBoundingBoxListeners(Prestige.eventBus, BoundingBoxEvent.class)) { + if (entity != null && MinecraftClient.getInstance().player != null) { + if (Prestige.eventBus != null && HookBridge.hasBoundingBoxListeners(Prestige.eventBus, BoundingBoxEvent.class)) { Box box = (Box)cir.getReturnValue(); BoundingBoxEvent event = HookBridge.createBoundingBoxEvent(entity, box); if (HookBridge.postBoundingBoxEvent(event)) { diff --git a/src/main/java/dev/zprestige/prestige/mixin/EntityRendererMixin.java b/src/main/java/dev/zprestige/prestige/mixin/EntityRendererMixin.java index 03a88da..e3e060c 100644 --- a/src/main/java/dev/zprestige/prestige/mixin/EntityRendererMixin.java +++ b/src/main/java/dev/zprestige/prestige/mixin/EntityRendererMixin.java @@ -52,7 +52,7 @@ public abstract class EntityRendererMixin> 16 & 0xFF, skyPacked >> 8 & 0xFF, skyPacked & 0xFF); int hzPacked = state.sunriseAndSunsetColor; diff --git a/src/main/java/dev/zprestige/prestige/mixin/TextDisplayEntityRendererMixin.java b/src/main/java/dev/zprestige/prestige/mixin/TextDisplayEntityRendererMixin.java index 6ea84a4..9daf7e6 100644 --- a/src/main/java/dev/zprestige/prestige/mixin/TextDisplayEntityRendererMixin.java +++ b/src/main/java/dev/zprestige/prestige/mixin/TextDisplayEntityRendererMixin.java @@ -24,7 +24,7 @@ public class TextDisplayEntityRendererMixin { )} ) public void modifyLabelTextBeforeWrapLines(CArgs cArgs) { - if (HookBridge.hasTextDisplayLabelListeners(Prestige.eventBus, LabelTextEvent.class)) { + if (Prestige.eventBus != null && HookBridge.hasTextDisplayLabelListeners(Prestige.eventBus, LabelTextEvent.class)) { Text text = (Text)cArgs.get(0); String stringText = text.getString(); ClientWorld world = MinecraftClient.getInstance().world; diff --git a/src/main/java/dev/zprestige/prestige/mixin/TextVisitFactoryMixin.java b/src/main/java/dev/zprestige/prestige/mixin/TextVisitFactoryMixin.java index 74d52e8..caeaf18 100644 --- a/src/main/java/dev/zprestige/prestige/mixin/TextVisitFactoryMixin.java +++ b/src/main/java/dev/zprestige/prestige/mixin/TextVisitFactoryMixin.java @@ -22,7 +22,7 @@ public class TextVisitFactoryMixin { private static void replaceTextBeforeVisitFormatted(CArgs cArgs) { String text = (String)cArgs.get(0); if (text != null) { - if (!HookBridge.hasTextReplaceListeners(Prestige.eventBus, TextReplaceEvent.class)) { + if (Prestige.eventBus == null || !HookBridge.hasTextReplaceListeners(Prestige.eventBus, TextReplaceEvent.class)) { return; } diff --git a/src/main/java/dev/zprestige/prestige/mixin/WorldChunkMixin.java b/src/main/java/dev/zprestige/prestige/mixin/WorldChunkMixin.java index ad280a6..c166367 100644 --- a/src/main/java/dev/zprestige/prestige/mixin/WorldChunkMixin.java +++ b/src/main/java/dev/zprestige/prestige/mixin/WorldChunkMixin.java @@ -23,7 +23,7 @@ public class WorldChunkMixin { target = {@CTarget("TAIL")} ) private void postBlockUpdateEventAtSetBlockStateTail(BlockPos pos, BlockState state, int flags, InjectionCallback cir) { - if (HookBridge.hasBlockUpdateListeners(Prestige.eventBus, BlockStateUpdateEvent.class)) { + if (Prestige.eventBus != null && HookBridge.hasBlockUpdateListeners(Prestige.eventBus, BlockStateUpdateEvent.class)) { if (this.world.isClient()) { BlockStateUpdateEvent event = HookBridge.createBlockUpdateEvent(pos, state); HookBridge.postBlockUpdateEvent(event); diff --git a/src/main/java/dev/zprestige/prestige/mixin/WorldMixin.java b/src/main/java/dev/zprestige/prestige/mixin/WorldMixin.java index 590e99c..94b2f9c 100644 --- a/src/main/java/dev/zprestige/prestige/mixin/WorldMixin.java +++ b/src/main/java/dev/zprestige/prestige/mixin/WorldMixin.java @@ -18,7 +18,7 @@ public class WorldMixin { cancellable = true ) private void overrideRainGradientAtReturn(float delta, InjectionCallback cir) { - if (HookBridge.hasRainGradientListeners(Prestige.eventBus, RainGradientEvent.class)) { + if (Prestige.eventBus != null && HookBridge.hasRainGradientListeners(Prestige.eventBus, RainGradientEvent.class)) { if (HookBridge.postRainGradientEvent(HookBridge.createRainGradientEvent())) { cir.setReturnValue(0.0F); } @@ -31,7 +31,7 @@ public class WorldMixin { cancellable = true ) private void overrideTimeOfDayAtReturn(InjectionCallback cir) { - if (HookBridge.hasTimeOfDayListeners(Prestige.eventBus, TimeOfDayEvent.class)) { + if (Prestige.eventBus != null && HookBridge.hasTimeOfDayListeners(Prestige.eventBus, TimeOfDayEvent.class)) { TimeOfDayEvent event = HookBridge.createTimeOfDayEvent((Long)cir.getReturnValue()); if (HookBridge.postTimeOfDayEvent(event)) { cir.setReturnValue(HookBridge.getTimeOfDay(event)); diff --git a/src/main/java/dev/zprestige/prestige/module/combat/AimAssist.java b/src/main/java/dev/zprestige/prestige/module/combat/AimAssist.java index 8b891a4..655b9f4 100644 --- a/src/main/java/dev/zprestige/prestige/module/combat/AimAssist.java +++ b/src/main/java/dev/zprestige/prestige/module/combat/AimAssist.java @@ -25,6 +25,7 @@ import net.minecraft.item.Items; import net.minecraft.util.hit.EntityHitResult; import net.minecraft.util.hit.HitResult.Type; import net.minecraft.util.math.Box; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; public class AimAssist extends Module { @@ -170,13 +171,7 @@ public class AimAssist extends Module { float yawDelta = targetRotation.getYaw() - mc.player.getYaw(); float pitchDelta = targetRotation.getPitch() - mc.player.getPitch(); - while (yawDelta > 180.0F) { - yawDelta -= 360.0F; - } - - while (yawDelta < -180.0F) { - yawDelta += 360.0F; - } + yawDelta = MathHelper.wrapDegrees(yawDelta); if (!(this.fov.getValue() < 360.0F) || !(Math.abs(yawDelta) > this.fov.getValue() / 2.0F) && !(Math.abs(pitchDelta) > this.fov.getValue() / 2.0F)) { diff --git a/src/main/java/dev/zprestige/prestige/module/combat/AnchorExploder.java b/src/main/java/dev/zprestige/prestige/module/combat/AnchorExploder.java index 283627e..64d2a2c 100644 --- a/src/main/java/dev/zprestige/prestige/module/combat/AnchorExploder.java +++ b/src/main/java/dev/zprestige/prestige/module/combat/AnchorExploder.java @@ -377,7 +377,7 @@ public class AnchorExploder extends Module implements RotationProvider { public void onPacketReceive(PacketReceiveEvent event) { if (event.getPacket() instanceof BlockUpdateS2CPacket blockUpdate) { if (blockUpdate.getState() != null && blockUpdate.getState().getBlock() != Blocks.RESPAWN_ANCHOR) { - this.ownAnchors.remove(blockUpdate.getPos()); + mc.execute(() -> this.ownAnchors.remove(blockUpdate.getPos())); } } } diff --git a/src/main/java/dev/zprestige/prestige/module/combat/AnchorMacro.java b/src/main/java/dev/zprestige/prestige/module/combat/AnchorMacro.java index 9396478..8ab7bbe 100644 --- a/src/main/java/dev/zprestige/prestige/module/combat/AnchorMacro.java +++ b/src/main/java/dev/zprestige/prestige/module/combat/AnchorMacro.java @@ -305,8 +305,10 @@ public class AnchorMacro extends Module { return; } - this.bindPlacedAnchors.remove(blockUpdate.getPos()); - this.ownAnchors.remove(blockUpdate.getPos()); + mc.execute(() -> { + this.bindPlacedAnchors.remove(blockUpdate.getPos()); + this.ownAnchors.remove(blockUpdate.getPos()); + }); } } } diff --git a/src/main/java/dev/zprestige/prestige/module/combat/AnchorPlacer.java b/src/main/java/dev/zprestige/prestige/module/combat/AnchorPlacer.java index 7aae836..444d639 100644 --- a/src/main/java/dev/zprestige/prestige/module/combat/AnchorPlacer.java +++ b/src/main/java/dev/zprestige/prestige/module/combat/AnchorPlacer.java @@ -69,7 +69,7 @@ public class AnchorPlacer extends Module implements RotationProvider { @Subscribe public void onPacketReceive(PacketReceiveEvent event) { if (event.getPacket() instanceof BlockUpdateS2CPacket blockUpdate && blockUpdate.getState() != null && blockUpdate.getState().getBlock() != Blocks.RESPAWN_ANCHOR) { - this.placedAnchors.remove(blockUpdate.getPos()); + mc.execute(() -> this.placedAnchors.remove(blockUpdate.getPos())); } } diff --git a/src/main/java/dev/zprestige/prestige/module/combat/AntiBot.java b/src/main/java/dev/zprestige/prestige/module/combat/AntiBot.java index 4430e0c..ad4cc03 100644 --- a/src/main/java/dev/zprestige/prestige/module/combat/AntiBot.java +++ b/src/main/java/dev/zprestige/prestige/module/combat/AntiBot.java @@ -39,25 +39,37 @@ public class AntiBot extends Module { @Subscribe public void onPacketReceive(PacketReceiveEvent event) { - if (this.mode.isSelected("Packet") && event.getPacket() instanceof EntityPositionS2CPacket && mc.world != null) { - Entity entity = mc.world.getEntityById(((EntityPositionS2CPacket)event.getPacket()).entityId()); - if (entity == null) { - return; - } - - if (entity instanceof PlayerEntity player && !Prestige.botFilter.getValidatedPlayers().contains(entity)) { - int threshold = this.validationThreshold.getValue(); - int packetCount = (Integer)this.positionPacketCounts.getOrDefault(player, 0) + 1; - if (packetCount >= threshold) { - Prestige.botFilter.markValidated(player); - this.positionPacketCounts.remove(player); - } else { - this.positionPacketCounts.put(player, packetCount); + if (this.mode.isSelected("Packet") && event.getPacket() instanceof EntityPositionS2CPacket) { + int entityId = ((EntityPositionS2CPacket)event.getPacket()).entityId(); + mc.execute(() -> { + if (mc.world == null) { + return; } - } + + Entity entity = mc.world.getEntityById(entityId); + if (entity == null) { + return; + } + + if (entity instanceof PlayerEntity player && !Prestige.botFilter.getValidatedPlayers().contains(entity)) { + int threshold = this.validationThreshold.getValue(); + int packetCount = (Integer)this.positionPacketCounts.getOrDefault(player, 0) + 1; + if (packetCount >= threshold) { + Prestige.botFilter.markValidated(player); + this.positionPacketCounts.remove(player); + } else { + this.positionPacketCounts.put(player, packetCount); + } + } + }); } } + @Override + public void onDisable() { + this.positionPacketCounts.clear(); + } + @Subscribe public void onGameJoin(GameJoinEvent event) { this.positionPacketCounts.clear(); diff --git a/src/main/java/dev/zprestige/prestige/module/combat/AutoAnchor.java b/src/main/java/dev/zprestige/prestige/module/combat/AutoAnchor.java index c61a562..ea74e4f 100644 --- a/src/main/java/dev/zprestige/prestige/module/combat/AutoAnchor.java +++ b/src/main/java/dev/zprestige/prestige/module/combat/AutoAnchor.java @@ -198,7 +198,7 @@ public class AutoAnchor extends Module implements RotationProvider { if (event.getPacket() instanceof BlockUpdateS2CPacket blockUpdate) { this.blockUpdateCount++; if (blockUpdate.getState() != null && blockUpdate.getState().getBlock() != Blocks.RESPAWN_ANCHOR) { - this.placedAnchors.remove(blockUpdate.getPos()); + mc.execute(() -> this.placedAnchors.remove(blockUpdate.getPos())); } } } diff --git a/src/main/java/dev/zprestige/prestige/module/combat/DoubleAnchor.java b/src/main/java/dev/zprestige/prestige/module/combat/DoubleAnchor.java index c8ed262..3f3ca32 100644 --- a/src/main/java/dev/zprestige/prestige/module/combat/DoubleAnchor.java +++ b/src/main/java/dev/zprestige/prestige/module/combat/DoubleAnchor.java @@ -180,7 +180,7 @@ public class DoubleAnchor extends Module { return; } - BlockHitResult crosshairHit = (BlockHitResult)mc.crosshairTarget; + BlockHitResult crosshairHit = mc.crosshairTarget instanceof BlockHitResult blockCrosshair ? blockCrosshair : null; if (crosshairHit != null && mc.world.getBlockState(crosshairHit.getBlockPos()).isOf(Blocks.RESPAWN_ANCHOR) && mc.world.getBlockState(crosshairHit.getBlockPos()).get(Properties.CHARGES) != 0) { diff --git a/src/main/java/dev/zprestige/prestige/module/combat/SafeAnchor.java b/src/main/java/dev/zprestige/prestige/module/combat/SafeAnchor.java index c21b9d6..e7a3199 100644 --- a/src/main/java/dev/zprestige/prestige/module/combat/SafeAnchor.java +++ b/src/main/java/dev/zprestige/prestige/module/combat/SafeAnchor.java @@ -398,7 +398,7 @@ public class SafeAnchor extends Module implements RotationProvider { return; } - this.ownAnchors.remove(blockUpdate.getPos()); + mc.execute(() -> this.ownAnchors.remove(blockUpdate.getPos())); } } } diff --git a/src/main/java/dev/zprestige/prestige/module/misc/AutoArmor.java b/src/main/java/dev/zprestige/prestige/module/misc/AutoArmor.java index 958996d..4413ea5 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/AutoArmor.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/AutoArmor.java @@ -92,6 +92,10 @@ public class AutoArmor extends Module { } private boolean isSwapAllowed() { + if (mc.player.currentScreenHandler != mc.player.playerScreenHandler) { + return false; + } + return switch (this.mode.getValue()) { case "Inventory" -> mc.currentScreen instanceof InventoryScreen; case "Keybind" -> { diff --git a/src/main/java/dev/zprestige/prestige/module/misc/AutoPickaxe.java b/src/main/java/dev/zprestige/prestige/module/misc/AutoPickaxe.java index abe469a..e6d2c49 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/AutoPickaxe.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/AutoPickaxe.java @@ -32,7 +32,7 @@ public class AutoPickaxe extends Module { @Subscribe public void onFrame(FrameEvent event) { - if (mc.currentScreen == null && mc.isWindowFocused()) { + if (mc.player != null && mc.world != null && mc.currentScreen == null && mc.isWindowFocused()) { BlockPos belowPos = mc.player.getBlockPos().down(); boolean notMining = this.onlyWhenMining.getValue() && GLFW.glfwGetMouseButton(mc.getWindow().getHandle(), 0) != 1; HitResult hitResult = mc.crosshairTarget; diff --git a/src/main/java/dev/zprestige/prestige/module/misc/AutoPot.java b/src/main/java/dev/zprestige/prestige/module/misc/AutoPot.java index 838e2f7..0e3b85b 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/AutoPot.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/AutoPot.java @@ -31,7 +31,7 @@ public class AutoPot extends Module { @Subscribe public void onClientTick(ClientTickEvent event) { - if (mc.currentScreen == null && mc.isWindowFocused()) { + if (mc.player != null && mc.currentScreen == null && mc.isWindowFocused()) { boolean autoMode = this.mode.getValue().equals("Auto"); if (autoMode && mc.player.getHealth() > this.health.getValue()) { this.throwTimer.reset(); diff --git a/src/main/java/dev/zprestige/prestige/module/misc/AutoText.java b/src/main/java/dev/zprestige/prestige/module/misc/AutoText.java index fe9e9f6..2008747 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/AutoText.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/AutoText.java @@ -14,6 +14,10 @@ public class AutoText extends Module { @Override public void onEnable() { String message = this.text.getValue().toString(); + if (message.length() > 256) { + message = message.substring(0, 256); + } + if (message.startsWith("/")) { Objects.requireNonNull(mc.getNetworkHandler()).sendChatCommand(message.substring(1)); } else { diff --git a/src/main/java/dev/zprestige/prestige/module/misc/AutoTool.java b/src/main/java/dev/zprestige/prestige/module/misc/AutoTool.java index 0ee6d57..23f138b 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/AutoTool.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/AutoTool.java @@ -20,7 +20,7 @@ public class AutoTool extends Module { @Subscribe public void onSendMovementPackets(SendMovementPacketsEvent event) { - if (mc.crosshairTarget instanceof BlockHitResult blockHit) { + if (mc.player != null && mc.world != null && mc.crosshairTarget instanceof BlockHitResult blockHit) { BlockPos blockPos = blockHit.getBlockPos(); if (!mc.world.getBlockState(blockPos).isAir()) { int toolSlot = this.findBestToolSlot(blockPos); diff --git a/src/main/java/dev/zprestige/prestige/module/misc/CartRefill.java b/src/main/java/dev/zprestige/prestige/module/misc/CartRefill.java index 74f682d..408d8ba 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/CartRefill.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/CartRefill.java @@ -38,7 +38,7 @@ public class CartRefill extends Module { float delayMs = this.delay.getValue().intValue(); if (timer.hasElapsed(delayMs)) { if (this.isRefillAllowed()) { - int hotbarSlot = this.targetSlot.getValue() - 1; + int hotbarSlot = Math.max(0, Math.min(8, this.targetSlot.getValue() - 1)); if (mc.player.getInventory().getStack(hotbarSlot).isEmpty()) { int cartSlot = this.findTntMinecartSlot(); if (cartSlot != -1) { @@ -52,6 +52,10 @@ public class CartRefill extends Module { } private boolean isRefillAllowed() { + if (mc.player.currentScreenHandler != mc.player.playerScreenHandler) { + return false; + } + if (this.mode.getValue().equals("Always")) { return mc.currentScreen instanceof InventoryScreen; } else { diff --git a/src/main/java/dev/zprestige/prestige/module/misc/CartStealer.java b/src/main/java/dev/zprestige/prestige/module/misc/CartStealer.java index 47bd1f5..cf70a3f 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/CartStealer.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/CartStealer.java @@ -32,6 +32,10 @@ public class CartStealer extends Module { @Subscribe public void onFrame(FrameEvent event) { + if (mc.player == null || mc.interactionManager == null) { + return; + } + if (this.activateKey.getValue() == -1 || GLFW.glfwGetKey(mc.getWindow().getHandle(), this.activateKey.getValue()) == 1) { if (this.stealTimer.hasElapsedRandom(this.delay)) { if (mc.player.currentScreenHandler instanceof ShulkerBoxScreenHandler shulkerHandler) { diff --git a/src/main/java/dev/zprestige/prestige/module/misc/ChestStealer.java b/src/main/java/dev/zprestige/prestige/module/misc/ChestStealer.java index 18ac66b..1c6a4d7 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/ChestStealer.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/ChestStealer.java @@ -24,6 +24,10 @@ public class ChestStealer extends Module { @Subscribe public void onFrame(FrameEvent event) { + if (mc.player == null) { + return; + } + if (this.activateKey.getValue() != -1 && GLFW.glfwGetKey(mc.getWindow().getHandle(), this.activateKey.getValue()) == 1) { if (this.stealTimer.hasElapsed(this.delay.getValue())) { if (mc.player.currentScreenHandler instanceof GenericContainerScreenHandler containerHandler) { diff --git a/src/main/java/dev/zprestige/prestige/module/misc/Criticals.java b/src/main/java/dev/zprestige/prestige/module/misc/Criticals.java index 85c0695..d0f36f0 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/Criticals.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/Criticals.java @@ -71,12 +71,14 @@ public class Criticals extends Module { if (this.pendingTarget != null) { if (this.ticksUntilAttack <= 0) { this.replayingAttack = true; - if (mc.crosshairTarget.getType() == Type.ENTITY) { - PlayerInteractions.attackEntity(this.pendingTarget); + try { + if (mc.player != null && mc.crosshairTarget != null && mc.crosshairTarget.getType() == Type.ENTITY) { + PlayerInteractions.attackEntity(this.pendingTarget); + } + } finally { + this.replayingAttack = false; + this.pendingTarget = null; } - - this.replayingAttack = false; - this.pendingTarget = null; } else { this.ticksUntilAttack--; } @@ -92,6 +94,13 @@ public class Criticals extends Module { } } + @Override + public void onDisable() { + this.pendingTarget = null; + this.ticksUntilAttack = 0; + this.replayingAttack = false; + } + private void sendOffsetPosition(float yOffset) { PacketSender.sendPacket(new PositionAndOnGround(mc.player.getX(), mc.player.getY() + yOffset, mc.player.getZ(), false, mc.player.horizontalCollision)); } diff --git a/src/main/java/dev/zprestige/prestige/module/misc/CrystalOptimizer.java b/src/main/java/dev/zprestige/prestige/module/misc/CrystalOptimizer.java index a91049b..7777a0b 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/CrystalOptimizer.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/CrystalOptimizer.java @@ -23,7 +23,7 @@ public class CrystalOptimizer extends Module { @Subscribe public void onAttackEntity(AttackEntityEvent event) { Entity target = event.getTarget(); - if (target instanceof EndCrystalEntity) { + if (target instanceof EndCrystalEntity && mc.player != null) { if (mc.player.getStatusEffect(StatusEffects.WEAKNESS) != null) { return; } diff --git a/src/main/java/dev/zprestige/prestige/module/misc/EnemyFaker.java b/src/main/java/dev/zprestige/prestige/module/misc/EnemyFaker.java index 5ce0269..9d7786c 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/EnemyFaker.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/EnemyFaker.java @@ -44,6 +44,7 @@ public class EnemyFaker extends Module { && event.getPlayerName() != null && event.getSkinTextures() != null && !this.fakeName.getValue().isEmpty() + && mc.getNetworkHandler() != null && mc.getNetworkHandler().getPlayerListEntry(this.target.getUuid()) != null && mc.getNetworkHandler().getPlayerListEntry(this.target.getUuid()).getProfile() != null) { if (event.getPlayerName() @@ -86,7 +87,12 @@ public class EnemyFaker extends Module { @Subscribe public void onClientTick(ClientTickEvent event) { - for (PlayerEntity player : mc.world.getPlayers()) { + if (mc.world == null || mc.player == null) { + this.target = null; + return; + } + + for (PlayerEntity player : new ArrayList<>(mc.world.getPlayers())) { if (!player.equals(mc.player) && (player.isDead() || !player.isAlive())) { mc.world.removeEntity(player.getId(), RemovalReason.DISCARDED); } @@ -96,25 +102,50 @@ public class EnemyFaker extends Module { if (this.update.getValue()) { this.update.setValue(false); if (!this.fakeName.getValue().isEmpty()) { - UUID uuid = MojangApi.fetchUuid(this.fakeName.getValue().toString()); - if (uuid != null) { - GameProfile profile = MojangApi.fetchProfile(uuid); - CompletableFuture skinFuture = mc.getSkinProvider().fetchSkinTextures(profile); - skinFuture.thenAccept(skinResult -> { - if (((java.util.Optional)skinResult).isPresent()) { - this.fakeSkin = (SkinTextures)((java.util.Optional)skinResult).get(); - mc.player.sendMessage(Text.of("Fake skin updated to: " + this.fakeName.getValue()), false); - } else { - mc.player.sendMessage(Text.of("Failed to fetch skin for: " + this.fakeName.getValue()), false); - } - }); - } + String requestedName = this.fakeName.getValue().toString(); + // Mojang lookups block for several seconds on a bad connection, never do them on the game thread + Thread lookup = new Thread(() -> { + UUID uuid = MojangApi.fetchUuid(requestedName); + GameProfile profile = uuid == null ? null : MojangApi.fetchProfile(uuid); + if (profile != null) { + mc.execute(() -> this.applyFakeSkin(profile, requestedName)); + } + }, "Prestige-SkinLookup"); + lookup.setDaemon(true); + lookup.start(); } } } + private void applyFakeSkin(GameProfile profile, String requestedName) { + if (!this.isEnabled()) { + return; + } + + CompletableFuture skinFuture = mc.getSkinProvider().fetchSkinTextures(profile); + skinFuture.thenAccept(skinResult -> { + if (((java.util.Optional)skinResult).isPresent()) { + this.fakeSkin = (SkinTextures)((java.util.Optional)skinResult).get(); + if (mc.player != null) { + mc.player.sendMessage(Text.of("Fake skin updated to: " + requestedName), false); + } + } else if (mc.player != null) { + mc.player.sendMessage(Text.of("Failed to fetch skin for: " + requestedName), false); + } + }); + } + + @Override + public void onDisable() { + this.target = null; + this.fakeSkin = null; + } + public static PlayerEntity findClosestEnemy() { PlayerEntity closest = null; + if (mc.world == null || mc.player == null) { + return null; + } for (PlayerEntity player : new ArrayList<>(mc.world.getPlayers())) { if (!player.equals(mc.player) diff --git a/src/main/java/dev/zprestige/prestige/module/misc/FastPlace.java b/src/main/java/dev/zprestige/prestige/module/misc/FastPlace.java index 90bb9c7..69a8ef9 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/FastPlace.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/FastPlace.java @@ -27,6 +27,10 @@ public class FastPlace extends Module { @Subscribe public void onClientTick(ClientTickEvent event) { + if (mc.player == null) { + return; + } + if (!this.blocksOnly.getValue() || mc.player.getMainHandStack().getItem() instanceof BlockItem || mc.player.getOffHandStack().getItem() instanceof BlockItem) { diff --git a/src/main/java/dev/zprestige/prestige/module/misc/Friends.java b/src/main/java/dev/zprestige/prestige/module/misc/Friends.java index 5c1e245..5c4523e 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/Friends.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/Friends.java @@ -26,7 +26,7 @@ public class Friends extends Module { @Subscribe public void onTextReplace(TextReplaceEvent event) { - if (this.highlight.getValue()) { + if (this.highlight.getValue() && event.getText() != null) { for (Object friendName : Prestige.friendManager.getFriends()) { if (event.getText().contains((CharSequence)friendName)) { event.setTarget((String)friendName); @@ -71,7 +71,9 @@ public class Friends extends Module { if (hitResult.getType() == Type.ENTITY) { Entity targetEntity = ((EntityHitResult)hitResult).getEntity(); if (targetEntity instanceof PlayerEntity && Prestige.friendManager.isFriend(targetEntity.getName().getString())) { - mc.player.handSwinging = false; + if (mc.player != null) { + mc.player.handSwinging = false; + } event.cancel(); } } diff --git a/src/main/java/dev/zprestige/prestige/module/misc/KeyPearl.java b/src/main/java/dev/zprestige/prestige/module/misc/KeyPearl.java index f5b971c..42f72eb 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/KeyPearl.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/KeyPearl.java @@ -34,7 +34,7 @@ public class KeyPearl extends Module { @Subscribe public void onClientTick(ClientTickEvent event) { - if (mc.currentScreen == null && mc.isWindowFocused()) { + if (mc.player != null && mc.currentScreen == null && mc.isWindowFocused()) { if (this.actionTimer.hasElapsed(this.delay.getValue())) { if (this.pearlThrown) { InventoryUtil.selectSlot(this.previousSlot); diff --git a/src/main/java/dev/zprestige/prestige/module/misc/NameHider.java b/src/main/java/dev/zprestige/prestige/module/misc/NameHider.java index b5916fc..445a81a 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/NameHider.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/NameHider.java @@ -32,6 +32,10 @@ public class NameHider extends Module { @Subscribe public void onFrame(FrameEvent event) { + if (mc.player == null) { + return; + } + if (this.randomize.getValue()) { if (this.rerollTimer.hasElapsed(this.speed.getValue())) { this.replacementName = this.generateName(); @@ -47,7 +51,7 @@ public class NameHider extends Module { @Subscribe public void onTextReplace(TextReplaceEvent event) { String username = mc.getSession().getUsername(); - if (event.getText().contains(username)) { + if (username != null && !username.isEmpty() && event.getText() != null && event.getText().contains(username)) { event.setTarget(mc.getSession().getUsername()); event.setText(this.replacementName); event.cancel(); @@ -56,7 +60,9 @@ public class NameHider extends Module { @Override public void onEnable() { - this.replacementName = this.generateName(); + if (mc.player != null) { + this.replacementName = this.generateName(); + } } private String generateName() { diff --git a/src/main/java/dev/zprestige/prestige/module/misc/Refill.java b/src/main/java/dev/zprestige/prestige/module/misc/Refill.java index 07f8425..b18d2c2 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/Refill.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/Refill.java @@ -54,6 +54,10 @@ public class Refill extends Module { } private boolean isRefillAllowed() { + if (mc.player.currentScreenHandler != mc.player.playerScreenHandler) { + return false; + } + if (this.mode.getValue().equals("Always")) { return mc.currentScreen instanceof InventoryScreen; } else { diff --git a/src/main/java/dev/zprestige/prestige/module/misc/SelfDestruct.java b/src/main/java/dev/zprestige/prestige/module/misc/SelfDestruct.java index 11be976..40a5364 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/SelfDestruct.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/SelfDestruct.java @@ -2,8 +2,19 @@ package dev.zprestige.prestige.module.misc; import dev.zprestige.prestige.Module; import dev.zprestige.prestige.client.Prestige; +import dev.zprestige.prestige.inject.Bootstrap; +import dev.zprestige.prestige.inject.NativeBridge; +import dev.zprestige.prestige.ui.mc.Overlay; import java.util.ArrayList; +import java.util.List; +/** + * Removes the client from the running game. + * + * Stage 1 (this tick): stop all client logic, close the menu, switch every other module off. + * Stage 2 (next tick, after Module.toggle has returned): dispose the modules and empty the module list. + * Stage 3 (injected clients only): put the original bytes back into every hooked game class and let the native loader unload. + */ public class SelfDestruct extends Module { public SelfDestruct() { dev.zprestige.prestige.ui.bridge.Bootstrap.pre(this); @@ -12,18 +23,54 @@ public class SelfDestruct extends Module { @Override public void onEnable() { Prestige.selfDestructed = true; + Overlay.shutdown(); mc.setScreen(null); - for (Module module : (Iterable)(Prestige.moduleManager.getModules())) { - if (module.isEnabled()) { - module.toggle(); + List modules = new ArrayList<>(Prestige.moduleManager.getModules()); + for (Module module : modules) { + if (module == this) { + continue; + } + try { + if (module.isEnabled()) { + module.toggle(); + } + } catch (Throwable error) { + error.printStackTrace(); } - - module.dispose(); - module.getKeybind().setValue(-1); } + // Disposing this module now would null its own "Enabled" value while Module.toggle still has to read it. + mc.execute(() -> dispose(modules)); + } + + private void dispose(List modules) { + for (Module module : modules) { + try { + module.dispose(); + module.getKeybind().setValue(-1); + } catch (Throwable error) { + error.printStackTrace(); + } + } Prestige.moduleManager.setModules(new ArrayList()); + + if (Bootstrap.isInjected()) { + // one more tick so no hook of the current tick is still being entered when the original classes come back + mc.execute(() -> mc.execute(SelfDestruct::unhook)); + } + } + + private static void unhook() { + try { + Bootstrap.uninstall(); + NativeBridge.release(); + } catch (UnsatisfiedLinkError ignored) { + // loader already gone + } catch (Throwable error) { + // an Error escaping a main-thread task would take the whole game down + error.printStackTrace(); + } } @Override diff --git a/src/main/java/dev/zprestige/prestige/module/misc/Teams.java b/src/main/java/dev/zprestige/prestige/module/misc/Teams.java index 9059e75..0cfd4fe 100644 --- a/src/main/java/dev/zprestige/prestige/module/misc/Teams.java +++ b/src/main/java/dev/zprestige/prestige/module/misc/Teams.java @@ -36,6 +36,11 @@ public class Teams extends Module { @Subscribe public void onClientTick(ClientTickEvent event) { + if (mc.world == null || mc.player == null) { + teammates = new HashSet(); + return; + } + HashSet currentTeammates = new HashSet(); for (PlayerEntity player : mc.world.getPlayers()) { diff --git a/src/main/java/dev/zprestige/prestige/module/movement/AutoJumpReset.java b/src/main/java/dev/zprestige/prestige/module/movement/AutoJumpReset.java index 050e888..b56c62a 100644 --- a/src/main/java/dev/zprestige/prestige/module/movement/AutoJumpReset.java +++ b/src/main/java/dev/zprestige/prestige/module/movement/AutoJumpReset.java @@ -25,6 +25,10 @@ public class AutoJumpReset extends Module { @Subscribe public void onClientTick(ClientTickEvent event) { + if (mc.player == null) { + return; + } + DamageSource damageSource = mc.player.getRecentDamageSource(); if (mc.currentScreen == null && !mc.player.isBlocking() diff --git a/src/main/java/dev/zprestige/prestige/module/movement/AutoShortPearl.java b/src/main/java/dev/zprestige/prestige/module/movement/AutoShortPearl.java index 2bc1234..cd133c6 100644 --- a/src/main/java/dev/zprestige/prestige/module/movement/AutoShortPearl.java +++ b/src/main/java/dev/zprestige/prestige/module/movement/AutoShortPearl.java @@ -28,7 +28,7 @@ public class AutoShortPearl extends Module implements RotationProvider { @Subscribe public void onPacketReceive(PacketReceiveEvent event) { - if (event.getPacket() instanceof EntityDamageS2CPacket damagePacket) { + if (mc.world != null && mc.player != null && event.getPacket() instanceof EntityDamageS2CPacket damagePacket) { Entity damagedEntity = mc.world.getEntityById(damagePacket.entityId()); if (damagedEntity == null || damagedEntity != mc.player) { return; @@ -45,6 +45,10 @@ public class AutoShortPearl extends Module implements RotationProvider { @Override public Rotation provideRotation() { + if (mc.player == null) { + return null; + } + if (this.throwPending) { if (this.hitTimer.hasElapsed(this.delay.getValue()) && mc.isWindowFocused() && mc.currentScreen == null) { float throwPitch = 89.0F + RandomUtil.RANDOM.nextFloat() * 2.0F; @@ -69,6 +73,7 @@ public class AutoShortPearl extends Module implements RotationProvider { @Override public void onDisable() { + this.throwPending = false; Prestige.silentRotationManager.removeRotationProvider(this); } } diff --git a/src/main/java/dev/zprestige/prestige/module/movement/FakeLag.java b/src/main/java/dev/zprestige/prestige/module/movement/FakeLag.java index 1f4d526..c0217a0 100644 --- a/src/main/java/dev/zprestige/prestige/module/movement/FakeLag.java +++ b/src/main/java/dev/zprestige/prestige/module/movement/FakeLag.java @@ -11,6 +11,7 @@ import dev.zprestige.prestige.combat.TargetSelector; import dev.zprestige.prestige.event.PacketReceiveEvent; import dev.zprestige.prestige.event.PacketSendEvent; import dev.zprestige.prestige.event.FrameEvent; +import dev.zprestige.prestige.event.GameJoinEvent; import dev.zprestige.prestige.event.Render3DEvent; import dev.zprestige.prestige.math.UiMath; import dev.zprestige.prestige.module.combat.AutoElytra; @@ -137,6 +138,10 @@ public class FakeLag extends Module { @Subscribe public void onFrame(FrameEvent event) { this.rangeFlushed = false; + if (mc.player == null || mc.world == null) { + return; + } + if (this.mode.getValue().equals("Knockback") && this.knockbackTimer.hasElapsedRandom(this.choke)) { this.knockbackActive = false; this.flushPackets(); @@ -173,7 +178,7 @@ public class FakeLag extends Module { if (this.render.getValue()) { StopWatch activeTimer = this.knockbackActive ? this.knockbackTimer : this.chokeTimer; float chokeDuration = this.mode.getValue().equals("Manual") ? 1000.0F : (this.rangeFlushed ? this.rangeChoke.getRolled() : this.choke.getRolled()); - if (!(activeTimer.getElapsed() / chokeDuration / 2.0F < 0.1F)) { + if (chokeDuration > 0.0F && !(activeTimer.getElapsed() / chokeDuration / 2.0F < 0.1F)) { ShapeRenderer.drawGlowRing(event.context, event.renderer, (float)this.lastFlushPos.x, (float)this.lastFlushPos.y, (float)this.lastFlushPos.z, Math.min(0.5F, activeTimer.getElapsed() / chokeDuration / 2.0F), 0.005F, 0.1F, UiMath.withAlpha(UiMath.getAccentColor().brighter(), Math.min(1.0F, activeTimer.getElapsed() / chokeDuration)), UiMath.getAccentColor()); } } @@ -188,17 +193,28 @@ public class FakeLag extends Module { @Override public void onDisable() { this.flushPackets(); + // packets of an old world must never be replayed into a new one + this.heldPackets.clear(); + } + + @Subscribe + public void onGameJoin(GameJoinEvent event) { + this.heldPackets.clear(); + this.knockbackActive = false; } private void flushPackets() { if (mc.player != null && mc.world != null) { this.flushing = true; - while (mc.getNetworkHandler() != null && !this.heldPackets.isEmpty()) { - PacketSender.sendPacket((Packet)this.heldPackets.poll()); + try { + while (mc.getNetworkHandler() != null && !this.heldPackets.isEmpty()) { + PacketSender.sendPacket((Packet)this.heldPackets.poll()); + } + } finally { + this.flushing = false; } - this.flushing = false; this.chokeTimer.reset(); this.choke.reroll(); this.lastFlushPos = PositionUtil.getPlayerPos(); diff --git a/src/main/java/dev/zprestige/prestige/module/movement/FastBridge.java b/src/main/java/dev/zprestige/prestige/module/movement/FastBridge.java index 5cca6da..b38bae9 100644 --- a/src/main/java/dev/zprestige/prestige/module/movement/FastBridge.java +++ b/src/main/java/dev/zprestige/prestige/module/movement/FastBridge.java @@ -16,6 +16,10 @@ public class FastBridge extends Module { @Subscribe public void onClientTick(ClientTickEvent event) { + if (mc.player == null || mc.world == null) { + return; + } + boolean releaseSneak = !(mc.player.getMainHandStack().getItem() instanceof BlockItem) && !(mc.player.getOffHandStack().getItem() instanceof BlockItem); if (mc.player.getPitch() < 70.0F) { releaseSneak = true; @@ -38,4 +42,12 @@ public class FastBridge extends Module { this.sneakForced = true; } } + + @Override + public void onDisable() { + if (this.sneakForced) { + mc.options.sneakKey.setPressed(false); + this.sneakForced = false; + } + } } diff --git a/src/main/java/dev/zprestige/prestige/module/movement/Freecam.java b/src/main/java/dev/zprestige/prestige/module/movement/Freecam.java index df8d94e..15088fb 100644 --- a/src/main/java/dev/zprestige/prestige/module/movement/Freecam.java +++ b/src/main/java/dev/zprestige/prestige/module/movement/Freecam.java @@ -64,7 +64,7 @@ public class Freecam extends Module { @Subscribe public void onClientTick(ClientTickEvent event) { - if (mc.currentScreen == null) { + if (mc.player != null && mc.currentScreen == null) { mc.options.forwardKey.setPressed(false); mc.options.backKey.setPressed(false); mc.options.leftKey.setPressed(false); @@ -153,7 +153,7 @@ public class Freecam extends Module { @Subscribe public void onRender3D(Render3DEvent event) { - if (this.renderPrevious.getValue()) { + if (this.renderPrevious.getValue() && mc.player != null) { float bodyX = (float)this.frozenPos.x; float bodyY = (float)this.frozenPos.y; float bodyZ = (float)this.frozenPos.z; diff --git a/src/main/java/dev/zprestige/prestige/module/movement/NoJumpDelay.java b/src/main/java/dev/zprestige/prestige/module/movement/NoJumpDelay.java index 8c122ec..00b7ad4 100644 --- a/src/main/java/dev/zprestige/prestige/module/movement/NoJumpDelay.java +++ b/src/main/java/dev/zprestige/prestige/module/movement/NoJumpDelay.java @@ -19,6 +19,10 @@ public class NoJumpDelay extends Module { @Subscribe public void onClientTick(ClientTickEvent event) { + if (mc.player == null) { + return; + } + new JumpCooldownAccessor(mc.player).setJumpingCooldown(0); } } diff --git a/src/main/java/dev/zprestige/prestige/module/movement/Parkour.java b/src/main/java/dev/zprestige/prestige/module/movement/Parkour.java index e867eab..10fb07f 100644 --- a/src/main/java/dev/zprestige/prestige/module/movement/Parkour.java +++ b/src/main/java/dev/zprestige/prestige/module/movement/Parkour.java @@ -16,7 +16,7 @@ public class Parkour extends Module { @Subscribe public void onClientTick(ClientTickEvent event) { - if (mc.player.isOnGround() && !mc.options.jumpKey.isPressed()) { + if (mc.player != null && mc.world != null && mc.player.isOnGround() && !mc.options.jumpKey.isPressed()) { if (!mc.player.isSneaking() && !mc.options.sneakKey.isPressed()) { Box edgeBox = mc.player.getBoundingBox().offset(0.0, -0.5, 0.0) .expand(-this.distance.randomInRange() / 100.0, 0.0, -this.distance.randomInRange() / 100.0); diff --git a/src/main/java/dev/zprestige/prestige/module/movement/ShortPearl.java b/src/main/java/dev/zprestige/prestige/module/movement/ShortPearl.java index 72dabd6..151fa8a 100644 --- a/src/main/java/dev/zprestige/prestige/module/movement/ShortPearl.java +++ b/src/main/java/dev/zprestige/prestige/module/movement/ShortPearl.java @@ -44,6 +44,10 @@ public class ShortPearl extends Module implements RotationProvider { @Override public Rotation provideRotation() { + if (mc.player == null) { + return null; + } + if (!this.mode.getValue().equals("Silent")) { return null; } else if (this.silentThrowAge != -1) { @@ -73,7 +77,7 @@ public class ShortPearl extends Module implements RotationProvider { @Subscribe public void onFrame(FrameEvent event) { - if (this.mode.getValue().equals("Legit")) { + if (mc.player != null && this.mode.getValue().equals("Legit")) { if (this.stageTimer.hasElapsed(this.minDelay.getValue())) { switch (this.stage) { case 0: diff --git a/src/main/java/dev/zprestige/prestige/module/movement/Velocity.java b/src/main/java/dev/zprestige/prestige/module/movement/Velocity.java index 5e118b1..e6717f3 100644 --- a/src/main/java/dev/zprestige/prestige/module/movement/Velocity.java +++ b/src/main/java/dev/zprestige/prestige/module/movement/Velocity.java @@ -11,7 +11,7 @@ import dev.zprestige.prestige.event.PacketReceiveEvent; import dev.zprestige.prestige.event.ClientTickEvent; import dev.zprestige.prestige.event.GameJoinEvent; import dev.zprestige.prestige.render.RandomUtil; -import java.util.ArrayList; +import java.util.concurrent.CopyOnWriteArrayList; import java.util.Set; import net.minecraft.network.packet.s2c.play.ExplosionS2CPacket; @@ -24,7 +24,7 @@ public class Velocity extends Module { private BoolSetting ignoreExplosions; private RangeSetting vertical; private RangeSetting horizontal; - private ArrayList recentExplosions = new ArrayList(); + private CopyOnWriteArrayList recentExplosions = new CopyOnWriteArrayList(); @Override public Set getGamemodes() { @@ -33,11 +33,9 @@ public class Velocity extends Module { @Subscribe public void onClientTick(ClientTickEvent event) { - new ArrayList<>(this.recentExplosions).forEach(explosion -> { - if (mc.player != null && mc.player.isOnGround()) { - this.recentExplosions.remove(((net.minecraft.network.packet.s2c.play.ExplosionS2CPacket)explosion)); - } - }); + if (mc.player != null && mc.player.isOnGround()) { + this.recentExplosions.clear(); + } } @Subscribe @@ -57,6 +55,10 @@ public class Velocity extends Module { if (event.getPacket() instanceof ExplosionS2CPacket explosionPacket && explosionPacket.playerKnockback().isPresent() && explosionPacket.playerKnockback().get().lengthSquared() > 0.0) { + if (this.recentExplosions.size() >= 16) { + this.recentExplosions.remove(0); + } + this.recentExplosions.add(explosionPacket); } } diff --git a/src/main/java/dev/zprestige/prestige/module/movement/WTap.java b/src/main/java/dev/zprestige/prestige/module/movement/WTap.java index b32e2a6..d7459b3 100644 --- a/src/main/java/dev/zprestige/prestige/module/movement/WTap.java +++ b/src/main/java/dev/zprestige/prestige/module/movement/WTap.java @@ -37,7 +37,7 @@ public class WTap extends Module { public void onAttackEntity(AttackEntityEvent event) { if (!this.tapPending && !this.forwardReleased) { Entity target = event.getTarget(); - if (target != null) { + if (target != null && mc.player != null) { if (target instanceof PlayerEntity) { if (mc.options.forwardKey.isPressed() && mc.player.isSprinting() && RandomUtil.RANDOM.nextFloat() * 100.0F <= this.chance.getValue()) { this.reaction.reroll(); @@ -77,6 +77,12 @@ public class WTap extends Module { } } + @Override + public void onDisable() { + this.tapPending = false; + this.forwardReleased = false; + } + public boolean isTapPending() { return this.tapPending; } diff --git a/src/main/java/dev/zprestige/prestige/module/spear/SpearMaceDoubleTap.java b/src/main/java/dev/zprestige/prestige/module/spear/SpearMaceDoubleTap.java index bce6f57..eb55d96 100644 --- a/src/main/java/dev/zprestige/prestige/module/spear/SpearMaceDoubleTap.java +++ b/src/main/java/dev/zprestige/prestige/module/spear/SpearMaceDoubleTap.java @@ -51,13 +51,17 @@ public class SpearMaceDoubleTap extends Module { public void onPacketReceive(PacketReceiveEvent event) { if (event.getPacket() instanceof EntityStatusS2CPacket) { if (((EntityStatusS2CPacket)event.getPacket()).getStatus() == 2) { - int maceSlot = this.findBestMaceSlot(); - if (maceSlot != -1 && mc.player.fallDistance > this.triggerFallDistance.getValue().floatValue()) { - mc.player.stopUsingItem(); - InventoryUtil.selectSlot(maceSlot); - this.maceSwapTimer.reset(); - this.pendingMaceHit = true; - } + mc.execute(() -> { + if (mc.player != null && mc.world != null) { + int maceSlot = this.findBestMaceSlot(); + if (maceSlot != -1 && mc.player.fallDistance > this.triggerFallDistance.getValue().floatValue()) { + mc.player.stopUsingItem(); + InventoryUtil.selectSlot(maceSlot); + this.maceSwapTimer.reset(); + this.pendingMaceHit = true; + } + } + }); } } } @@ -108,7 +112,7 @@ public class SpearMaceDoubleTap extends Module { ItemStack stack = mc.player.getInventory().getStack(slot); if (stack != null) { float level = EnchantmentHelper.getLevel( - mc.world.getRegistryManager().getOptional(Enchantments.BREACH.getRegistryRef()).get().getEntry(Enchantments.DENSITY.getValue()).get(), stack + mc.world.getRegistryManager().getOptional(Enchantments.BREACH.getRegistryRef()).get().getEntry(Enchantments.BREACH.getValue()).get(), stack ); return (int)level; } else { diff --git a/src/main/java/dev/zprestige/prestige/module/visual/Animations.java b/src/main/java/dev/zprestige/prestige/module/visual/Animations.java index 5038afc..b38c184 100644 --- a/src/main/java/dev/zprestige/prestige/module/visual/Animations.java +++ b/src/main/java/dev/zprestige/prestige/module/visual/Animations.java @@ -25,6 +25,10 @@ public class Animations extends Module { @Subscribe public void onRender3D(Render3DEvent event) { + if (mc.player == null) { + return; + } + HeldItemRendererAccessor heldItemRenderer = new HeldItemRendererAccessor(mc.getEntityRenderDispatcher().getHeldItemRenderer()); if (heldItemRenderer.getMainHandEquipProgress() <= 1.0F) { heldItemRenderer.setMainHandEquipProgress(1.0F); diff --git a/src/main/java/dev/zprestige/prestige/module/visual/AntiResourcePack.java b/src/main/java/dev/zprestige/prestige/module/visual/AntiResourcePack.java index e1d74e4..35f3afe 100644 --- a/src/main/java/dev/zprestige/prestige/module/visual/AntiResourcePack.java +++ b/src/main/java/dev/zprestige/prestige/module/visual/AntiResourcePack.java @@ -15,10 +15,10 @@ public class AntiResourcePack extends Module { @Subscribe public void onPacketReceive(PacketReceiveEvent event) { - if (event.getPacket() instanceof ResourcePackSendS2CPacket) { + if (event.getPacket() instanceof ResourcePackSendS2CPacket packet) { event.cancel(); - PacketSender.sendPacket(new ResourcePackStatusC2SPacket(mc.player.getUuid(), Status.ACCEPTED)); - PacketSender.sendPacket(new ResourcePackStatusC2SPacket(mc.player.getUuid(), Status.SUCCESSFULLY_LOADED)); + PacketSender.sendPacket(new ResourcePackStatusC2SPacket(packet.id(), Status.ACCEPTED)); + PacketSender.sendPacket(new ResourcePackStatusC2SPacket(packet.id(), Status.SUCCESSFULLY_LOADED)); } } } diff --git a/src/main/java/dev/zprestige/prestige/module/visual/FreeLook.java b/src/main/java/dev/zprestige/prestige/module/visual/FreeLook.java index a6f61e0..0baefa6 100644 --- a/src/main/java/dev/zprestige/prestige/module/visual/FreeLook.java +++ b/src/main/java/dev/zprestige/prestige/module/visual/FreeLook.java @@ -26,6 +26,7 @@ public class FreeLook extends Module { public void onMouseUpdate(MouseUpdateEvent event) { this.cameraYaw = (float)(this.cameraYaw + event.getDeltaX() * 0.1F); this.cameraPitch = (float)(this.cameraPitch + event.getDeltaY() * 0.1F); + this.cameraPitch = Math.max(-90.0F, Math.min(90.0F, this.cameraPitch)); event.cancel(); } @@ -48,6 +49,9 @@ public class FreeLook extends Module { @Override public void onDisable() { - mc.options.setPerspective(this.previousPerspective); + if (this.previousPerspective != null) { + mc.options.setPerspective(this.previousPerspective); + this.previousPerspective = null; + } } } diff --git a/src/main/java/dev/zprestige/prestige/module/visual/HitEffect.java b/src/main/java/dev/zprestige/prestige/module/visual/HitEffect.java index 4c23a8c..ec23586 100644 --- a/src/main/java/dev/zprestige/prestige/module/visual/HitEffect.java +++ b/src/main/java/dev/zprestige/prestige/module/visual/HitEffect.java @@ -54,9 +54,17 @@ public class HitEffect extends Module { } } + @Override + public void onDisable() { + this.particles.clear(); + } + @Subscribe public void onAttackEntity(AttackEntityEvent event) { Entity target = event.getTarget(); + if (target == null) { + return; + } for (int particleIndex = 0; particleIndex < this.amount.getValue(); particleIndex++) { if (this.particles.size() <= this.lifespan.getValue() * 20) { @@ -76,6 +84,12 @@ public class HitEffect extends Module { @Subscribe public void onRender3D(Render3DEvent event) { + if (mc.player == null || mc.world == null) { + this.particles.clear(); + this.frameTimer.reset(); + return; + } + if (this.particles.isEmpty()) { this.frameTimer.reset(); } else { @@ -177,24 +191,31 @@ public class HitEffect extends Module { @Subscribe public void onPacketReceive(PacketReceiveEvent event) { + // Packets are handled on the network thread; particles are used on the main thread. if (this.totemPop.getValue()) { - if (event.getPacket() instanceof EntityStatusS2CPacket statusPacket && statusPacket.getStatus() == 35 && statusPacket.getEntity(mc.world) instanceof PlayerEntity player) { - for (int particleIndex = 0; particleIndex < this.amount.getValue() * 2; particleIndex++) { - if (this.particles.size() <= this.lifespan.getValue() * 20) { - this.particles - .add( - new HitParticle( - PositionUtil.getEntityPos(player), - new Vec3d( - player.getX() + (Math.random() - 0.5) * 0.5, - player.getY() + Math.random() * 1.0 + 0.5, - player.getZ() + (Math.random() - 0.5) * 0.5 - ), - AnimationMode.POP - ) - ); + if (event.getPacket() instanceof EntityStatusS2CPacket statusPacket && statusPacket.getStatus() == 35) { + mc.execute(() -> { + if (mc.world == null || !(statusPacket.getEntity(mc.world) instanceof PlayerEntity player)) { + return; } - } + + for (int particleIndex = 0; particleIndex < this.amount.getValue() * 2; particleIndex++) { + if (this.particles.size() <= this.lifespan.getValue() * 20) { + this.particles + .add( + new HitParticle( + PositionUtil.getEntityPos(player), + new Vec3d( + player.getX() + (Math.random() - 0.5) * 0.5, + player.getY() + Math.random() * 1.0 + 0.5, + player.getZ() + (Math.random() - 0.5) * 0.5 + ), + AnimationMode.POP + ) + ); + } + } + }); } } } diff --git a/src/main/java/dev/zprestige/prestige/module/visual/ItemESP.java b/src/main/java/dev/zprestige/prestige/module/visual/ItemESP.java index a7d5dc3..0460247 100644 --- a/src/main/java/dev/zprestige/prestige/module/visual/ItemESP.java +++ b/src/main/java/dev/zprestige/prestige/module/visual/ItemESP.java @@ -69,9 +69,16 @@ public class ItemESP extends Module { for (Entry entry : (Iterable)(entries)) { this.labels.put((ItemEntity)entry.getKey(), (ItemEspLabel)entry.getValue()); } + } else { + this.labels = new LinkedHashMap(); } } + @Override + public void onDisable() { + this.labels = new LinkedHashMap(); + } + @Subscribe public void onRender2D(Render2DEvent event) { if (mc.world != null && mc.player != null) { diff --git a/src/main/java/dev/zprestige/prestige/module/visual/NameTags.java b/src/main/java/dev/zprestige/prestige/module/visual/NameTags.java index e3e9589..7db7623 100644 --- a/src/main/java/dev/zprestige/prestige/module/visual/NameTags.java +++ b/src/main/java/dev/zprestige/prestige/module/visual/NameTags.java @@ -143,9 +143,20 @@ public class NameTags extends Module { for (Entry sortedEntry : (Iterable)(this.sortedEntries)) { this.displayEntries.put((PlayerEntity)sortedEntry.getKey(), (dev.zprestige.prestige.combat.NameTagEntry)sortedEntry.getValue()); } + } else { + this.entriesByPlayer.clear(); + this.sortedEntries.clear(); + this.displayEntries.clear(); } } + @Override + public void onDisable() { + this.entriesByPlayer.clear(); + this.sortedEntries.clear(); + this.displayEntries.clear(); + } + @Subscribe public void onRender2D(Render2DEvent event) { if (mc.player != null) { diff --git a/src/main/java/dev/zprestige/prestige/module/visual/OreSimulation.java b/src/main/java/dev/zprestige/prestige/module/visual/OreSimulation.java index 0ca1b85..71c5b6e 100644 --- a/src/main/java/dev/zprestige/prestige/module/visual/OreSimulation.java +++ b/src/main/java/dev/zprestige/prestige/module/visual/OreSimulation.java @@ -62,6 +62,7 @@ public class OreSimulation extends Module { private RegistryKey cachedDimension; private ClientWorld cachedWorld; private Frustum frustum; + private long lastPrune; @Subscribe public void onRender3D(Render3DEvent event) { @@ -69,6 +70,7 @@ public class OreSimulation extends Module { int centerChunkX = mc.player.getChunkPos().x; int centerChunkZ = mc.player.getChunkPos().z; int radius = this.chunks.getValue(); + this.pruneUnloadedChunks(); for (int ring = 0; ring <= radius; ring++) { for (int chunkX = -ring + centerChunkX; chunkX <= ring + centerChunkX; chunkX++) { @@ -82,6 +84,23 @@ public class OreSimulation extends Module { } } + // Drops simulated ores of chunks that are no longer loaded so the cache cannot grow without bound. + private void pruneUnloadedChunks() { + long now = System.currentTimeMillis(); + ClientWorld world = mc.world; + if (world == null || now - this.lastPrune < 5000L) { + return; + } + + this.lastPrune = now; + RegistryKey dimension = world.getRegistryKey(); + this.chunkOres.keySet().removeIf(key -> { + DimensionChunkKey chunkKey = (DimensionChunkKey)key; + return !chunkKey.dimension().equals(dimension) + || world.getChunk(ChunkPos.getPackedX(chunkKey.chunkPos()), ChunkPos.getPackedZ(chunkKey.chunkPos()), ChunkStatus.FULL, false) == null; + }); + } + private void renderChunk(Render3DEvent event, int chunkX, int chunkZ) { if (this.frustum == null || this.frustum.isVisible(new Box(chunkX << 4, -64.0, chunkZ << 4, (chunkX << 4) + 16, 320.0, (chunkZ << 4) + 16))) { ClientWorld world = mc.world; diff --git a/src/main/java/dev/zprestige/prestige/module/visual/PearlTrajectory.java b/src/main/java/dev/zprestige/prestige/module/visual/PearlTrajectory.java index 8ee39d5..b9ebdfc 100644 --- a/src/main/java/dev/zprestige/prestige/module/visual/PearlTrajectory.java +++ b/src/main/java/dev/zprestige/prestige/module/visual/PearlTrajectory.java @@ -69,6 +69,13 @@ public class PearlTrajectory extends Module { } } + @Override + public void onDisable() { + this.predictedPaths.clear(); + this.landingMarkers.clear(); + this.spawnTimes.clear(); + } + @Subscribe public void onRender3D(Render3DEvent event) { if (mc.world != null && mc.player != null) { diff --git a/src/main/java/dev/zprestige/prestige/module/visual/ShieldRenderer.java b/src/main/java/dev/zprestige/prestige/module/visual/ShieldRenderer.java index a4226c3..025ac9c 100644 --- a/src/main/java/dev/zprestige/prestige/module/visual/ShieldRenderer.java +++ b/src/main/java/dev/zprestige/prestige/module/visual/ShieldRenderer.java @@ -50,8 +50,16 @@ public class ShieldRenderer extends Module { @Subscribe public void onClientTick(ClientTickEvent event) { + if (mc.world == null) { + blockingTicks.clear(); + brokenTicksRemaining.clear(); + return; + } + + java.util.Set presentIds = new java.util.HashSet<>(); for (PlayerEntity player : mc.world.getPlayers()) { UUID uuid = player.getUuid(); + presentIds.add(uuid); if (player.isUsingItem() && player.getStackInHand(player.getActiveHand()).getItem() instanceof ShieldItem) { blockingTicks.put(uuid, (Integer)blockingTicks.getOrDefault(uuid, 0) + 1); } else { @@ -59,6 +67,7 @@ public class ShieldRenderer extends Module { } } + blockingTicks.keySet().retainAll(presentIds); brokenTicksRemaining.entrySet().removeIf(entry -> (Integer)((java.util.Map.Entry)entry).getValue() <= 1); brokenTicksRemaining.replaceAll((playerId, ticksLeft) -> ((java.lang.Integer)ticksLeft) > 1 ? ((java.lang.Integer)ticksLeft) - 1 : ((java.lang.Integer)ticksLeft)); } diff --git a/src/main/java/dev/zprestige/prestige/module/visual/Trajectories.java b/src/main/java/dev/zprestige/prestige/module/visual/Trajectories.java index 3d82f70..237ca88 100644 --- a/src/main/java/dev/zprestige/prestige/module/visual/Trajectories.java +++ b/src/main/java/dev/zprestige/prestige/module/visual/Trajectories.java @@ -61,7 +61,7 @@ public class Trajectories extends Module { @Subscribe public void onRender3D(Render3DEvent event) { - if (mc.player != null) { + if (mc.player != null && mc.world != null) { ItemStack heldStack = mc.player.getMainHandStack(); String mode = this.render.getValue().toString(); Vec3d playerPos = Render2D.getInterpolatedPos(mc.player); @@ -240,17 +240,19 @@ public class Trajectories extends Module { Vec3d renderPos = Render2D.getInterpolatedPos(mc.player); Vec3d originalPos = PositionUtil.getPlayerPos(); player.setPosition(renderPos.x, renderPos.y, renderPos.z); - TrajectoriesSimulatedPearl pearl = new TrajectoriesSimulatedPearl(mc.world, player, Items.ENDER_PEARL.getDefaultStack()); - pearl.setVelocity(player, this.smoothPitch, this.smoothYaw, 0.0F, 1.5F, 1.0F); - int steps = 0; + try { + TrajectoriesSimulatedPearl pearl = new TrajectoriesSimulatedPearl(mc.world, player, Items.ENDER_PEARL.getDefaultStack()); + pearl.setVelocity(player, this.smoothPitch, this.smoothYaw, 0.0F, 1.5F, 1.0F); + int steps = 0; - while (!pearl.landed && steps++ < 1000) { - points.add(PositionUtil.getEntityPos(pearl)); - pearl.tick(); + while (!pearl.landed && steps++ < 1000) { + points.add(PositionUtil.getEntityPos(pearl)); + pearl.tick(); + } + } finally { + player.setPosition(originalPos); } - player.setPosition(originalPos); - for (int pointIndex = 0; pointIndex < points.size() - 1; pointIndex++) { Vec3d point = (Vec3d)points.get(pointIndex); points.set(pointIndex, new Vec3d(point.x, point.y - 0.1F, point.z)); @@ -267,36 +269,41 @@ public class Trajectories extends Module { Vec3d originalPos = PositionUtil.getPlayerPos(); float originalPitch = player.getPitch(); float originalYaw = player.getYaw(); - player.setPosition(renderPos.x, renderPos.y, renderPos.z); - player.setPitch(this.smoothPitch); - player.setYaw(this.smoothYaw); - dev.zprestige.prestige.world.SimulatedBowArrow arrow = new dev.zprestige.prestige.world.SimulatedBowArrow(EntityType.ARROW, player, mc.world, arrowStack); - if (weapon instanceof CrossbowItem) { - Vec3d axis = player.getOppositeRotationVector(1.0F); - Quaternionf rotation = new Quaternionf().setAngleAxis((double)(angleOffset * (float) (Math.PI / 180.0)), axis.x, axis.y, axis.z); - Vector3f direction = player.getRotationVec(1.0F).toVector3f().rotate(rotation); - arrow.setVelocity(direction.x(), direction.y(), direction.z(), 3.15F, 1.0F); - } else { - arrow.setVelocity(player, this.smoothPitch, this.smoothYaw, 0.0F, BowItem.getPullProgress(mc.player.getItemUseTime()) * 3.0F, 1.0F); + boolean hitEntity; + try { + player.setPosition(renderPos.x, renderPos.y, renderPos.z); + player.setPitch(this.smoothPitch); + player.setYaw(this.smoothYaw); + dev.zprestige.prestige.world.SimulatedBowArrow arrow = new dev.zprestige.prestige.world.SimulatedBowArrow(EntityType.ARROW, player, mc.world, arrowStack); + if (weapon instanceof CrossbowItem) { + Vec3d axis = player.getOppositeRotationVector(1.0F); + Quaternionf rotation = new Quaternionf().setAngleAxis((double)(angleOffset * (float) (Math.PI / 180.0)), axis.x, axis.y, axis.z); + Vector3f direction = player.getRotationVec(1.0F).toVector3f().rotate(rotation); + arrow.setVelocity(direction.x(), direction.y(), direction.z(), 3.15F, 1.0F); + } else { + arrow.setVelocity(player, this.smoothPitch, this.smoothYaw, 0.0F, BowItem.getPullProgress(mc.player.getItemUseTime()) * 3.0F, 1.0F); + } + + arrow.inFlight = true; + int steps = 0; + + while (arrow.inFlight && steps++ < 1000) { + points.add(PositionUtil.getEntityPos(arrow)); + arrow.tick(); + } + + hitEntity = arrow.hitEntity; + } finally { + player.setPosition(originalPos); + player.setPitch(originalPitch); + player.setYaw(originalYaw); } - arrow.inFlight = true; - int steps = 0; - - while (arrow.inFlight && steps++ < 1000) { - points.add(PositionUtil.getEntityPos(arrow)); - arrow.tick(); - } - - player.setPosition(originalPos); - player.setPitch(originalPitch); - player.setYaw(originalYaw); - for (int pointIndex = 0; pointIndex < points.size() - 1; pointIndex++) { Vec3d point = (Vec3d)points.get(pointIndex); points.set(pointIndex, new Vec3d(point.x, point.y - 0.1F, point.z)); } - return new TrajectoryPath(points, arrow.hitEntity); + return new TrajectoryPath(points, hitEntity); } } diff --git a/src/main/java/dev/zprestige/prestige/module/visual/TwoDESP.java b/src/main/java/dev/zprestige/prestige/module/visual/TwoDESP.java index 5382006..d2fc559 100644 --- a/src/main/java/dev/zprestige/prestige/module/visual/TwoDESP.java +++ b/src/main/java/dev/zprestige/prestige/module/visual/TwoDESP.java @@ -109,7 +109,10 @@ public class TwoDESP extends Module { @Subscribe public void onClientTick(ClientTickEvent event) { - if (mc.world != null && mc.player != null) { + if (mc.world == null || mc.player == null) { + this.visibleTargets.clear(); + this.animatedBoxes.clear(); + } else { this.visibleTargets.clear(); int maxRange = this.maxDistance.getValue(); boolean includeInvisible = this.showInvisibles.getValue(); diff --git a/src/main/java/dev/zprestige/prestige/network/BlockChangeListener.java b/src/main/java/dev/zprestige/prestige/network/BlockChangeListener.java index 8dad335..210edde 100644 --- a/src/main/java/dev/zprestige/prestige/network/BlockChangeListener.java +++ b/src/main/java/dev/zprestige/prestige/network/BlockChangeListener.java @@ -18,7 +18,16 @@ import net.minecraft.util.math.Vec3d; public final class BlockChangeListener implements MinecraftAccess { @Subscribe public void onPacketReceive(PacketReceiveEvent event) { + // Packets arrive on the netty thread; BlockChangeTracker maps are plain HashMaps read on the client thread. long now = System.currentTimeMillis(); + if (mc.isOnThread()) { + handleReceive(event, now); + } else { + mc.execute(() -> handleReceive(event, now)); + } + } + + private static void handleReceive(PacketReceiveEvent event, long now) { if (event.getPacket() instanceof BlockUpdateS2CPacket blockUpdate) { if (blockUpdate.getState() != null && (blockUpdate.getState().isAir() || blockUpdate.getState().getBlock() == Blocks.FIRE)) { BlockChangeTracker.markCleared(blockUpdate.getPos(), now); diff --git a/src/main/java/dev/zprestige/prestige/network/PacketSender.java b/src/main/java/dev/zprestige/prestige/network/PacketSender.java index c3cdffd..680eac7 100644 --- a/src/main/java/dev/zprestige/prestige/network/PacketSender.java +++ b/src/main/java/dev/zprestige/prestige/network/PacketSender.java @@ -5,6 +5,8 @@ import net.minecraft.network.packet.Packet; public class PacketSender implements MinecraftAccess { public static void sendPacket(Packet packet) { - mc.getNetworkHandler().sendPacket(packet); + if (mc.getNetworkHandler() != null) { + mc.getNetworkHandler().sendPacket(packet); + } } } diff --git a/src/main/java/dev/zprestige/prestige/network/SilentSlotManager.java b/src/main/java/dev/zprestige/prestige/network/SilentSlotManager.java index fa05296..e9e6ba2 100644 --- a/src/main/java/dev/zprestige/prestige/network/SilentSlotManager.java +++ b/src/main/java/dev/zprestige/prestige/network/SilentSlotManager.java @@ -39,7 +39,7 @@ public class SilentSlotManager implements MinecraftAccess { if (event.getPacket() instanceof PlayerInteractItemC2SPacket || event.getPacket() instanceof PlayerInteractBlockC2SPacket || event.getPacket() instanceof PlayerInteractEntityC2SPacket - && InventoryUtil.isSword(mc.player.getMainHandStack())) { + && mc.player != null && InventoryUtil.isSword(mc.player.getMainHandStack())) { this.restoreSlot(); } } @@ -48,16 +48,22 @@ public class SilentSlotManager implements MinecraftAccess { public void spoofSlot(int slot) { this.spoofedSlot = slot; this.sendingOwnPacket = true; - PacketSender.sendPacket(new UpdateSelectedSlotC2SPacket(slot)); - this.sendingOwnPacket = false; + try { + PacketSender.sendPacket(new UpdateSelectedSlotC2SPacket(slot)); + } finally { + this.sendingOwnPacket = false; + } } public void restoreSlot() { if (this.spoofedSlot != -1) { this.sendingOwnPacket = true; - PacketSender.sendPacket(new UpdateSelectedSlotC2SPacket(InventoryUtil.getSelectedSlot())); - this.sendingOwnPacket = false; - this.spoofedSlot = -1; + try { + PacketSender.sendPacket(new UpdateSelectedSlotC2SPacket(InventoryUtil.getSelectedSlot())); + } finally { + this.sendingOwnPacket = false; + this.spoofedSlot = -1; + } } } diff --git a/src/main/java/dev/zprestige/prestige/network/TotemPopManager.java b/src/main/java/dev/zprestige/prestige/network/TotemPopManager.java index a9b9e23..b71b8db 100644 --- a/src/main/java/dev/zprestige/prestige/network/TotemPopManager.java +++ b/src/main/java/dev/zprestige/prestige/network/TotemPopManager.java @@ -27,15 +27,22 @@ public class TotemPopManager implements MinecraftAccess { @Subscribe public void onPacketReceive(PacketReceiveEvent event) { - if (event.getPacket() instanceof EntityStatusS2CPacket statusPacket && statusPacket.getStatus() == (int)TOTEM_POP_STATUS && statusPacket.getEntity(mc.world) instanceof PlayerEntity player) { - int pops = this.popCounts.getOrDefault(player.getUuid(), 0); - this.popCounts.put(player.getUuid(), ++pops); + if (event.getPacket() instanceof EntityStatusS2CPacket statusPacket && statusPacket.getStatus() == (int)TOTEM_POP_STATUS) { + // Packets arrive on the netty thread; touch the world and pop map on the client thread only. + mc.execute(() -> { + if (mc.world != null && statusPacket.getEntity(mc.world) instanceof PlayerEntity player) { + int pops = this.popCounts.getOrDefault(player.getUuid(), 0); + this.popCounts.put(player.getUuid(), ++pops); + } + }); } } @Subscribe public void onClientTick(ClientTickEvent event) { - if (!this.popCounts.isEmpty()) { + if (mc.world == null) { + this.popCounts.clear(); + } else if (!this.popCounts.isEmpty()) { this.presentPlayers.clear(); for (PlayerEntity player : mc.world.getPlayers()) { diff --git a/src/main/java/dev/zprestige/prestige/reflect/FieldAccessor.java b/src/main/java/dev/zprestige/prestige/reflect/FieldAccessor.java index 5757365..6b64bc4 100644 --- a/src/main/java/dev/zprestige/prestige/reflect/FieldAccessor.java +++ b/src/main/java/dev/zprestige/prestige/reflect/FieldAccessor.java @@ -55,6 +55,10 @@ public class FieldAccessor extends ReflectionBase { } public Object get(Object target) { + if (!this.isStaticField && target == null) { + throw new NullPointerException("target cannot be null for instance field " + this.field); + } + try { if (UNSAFE != null) { return this.unsafeGet(target); @@ -69,6 +73,10 @@ public class FieldAccessor extends ReflectionBase { } public void set(Object target, Object value) { + if (!this.isStaticField && target == null) { + throw new NullPointerException("target cannot be null for instance field " + this.field); + } + if (this.isStaticField) { if (UNSAFE != null) { this.unsafeSet(null, value); diff --git a/src/main/java/dev/zprestige/prestige/reflect/MappedField.java b/src/main/java/dev/zprestige/prestige/reflect/MappedField.java index 728f05b..e6b08d8 100644 --- a/src/main/java/dev/zprestige/prestige/reflect/MappedField.java +++ b/src/main/java/dev/zprestige/prestige/reflect/MappedField.java @@ -42,6 +42,10 @@ public final class MappedField { } private Object resolveTarget(Object target) { + if (!this.isStatic && target == null) { + throw new NullPointerException("target cannot be null for instance field " + this.field); + } + return this.isStatic ? this.staticBase : target; } diff --git a/src/main/java/dev/zprestige/prestige/reflect/ReflectionBase.java b/src/main/java/dev/zprestige/prestige/reflect/ReflectionBase.java index 78dd571..fc4e11a 100644 --- a/src/main/java/dev/zprestige/prestige/reflect/ReflectionBase.java +++ b/src/main/java/dev/zprestige/prestige/reflect/ReflectionBase.java @@ -19,21 +19,20 @@ public abstract class ReflectionBase { private static final Map RESOLVED_METHODS = new ConcurrentHashMap(); public static AccessibleObject forceAccessible(AccessibleObject member) { - if (UNSAFE == null) { + try { member.setAccessible(true); return member; - } else { + } catch (Throwable accessError) { + if (UNSAFE == null) { + throw accessError; + } + try { UNSAFE.putBoolean(member, 12L, true); - return member; - } catch (Throwable unsafeError) { - try { - member.setAccessible(true); - } catch (Throwable ignored) { - } - - return member; + } catch (Throwable ignored) { } + + return member; } } @@ -215,7 +214,7 @@ public abstract class ReflectionBase { char typeChar = descriptor.charAt(index); if (typeChar == 'L') { - Class elementClass = Class.forName(descriptor.substring(index + 1, descriptor.indexOf(59, index)).replace('/', '.')); + Class elementClass = Class.forName(descriptor.substring(index + 1, descriptor.indexOf(59, index)).replace('/', '.'), false, classLoader); return toArrayType(elementClass, arrayDepth); } else { return toArrayType(primitiveForDescriptor(Integer.valueOf(typeChar)), arrayDepth); diff --git a/src/main/java/dev/zprestige/prestige/render/BufferSource.java b/src/main/java/dev/zprestige/prestige/render/BufferSource.java index 050abf8..3c786de 100644 --- a/src/main/java/dev/zprestige/prestige/render/BufferSource.java +++ b/src/main/java/dev/zprestige/prestige/render/BufferSource.java @@ -10,8 +10,11 @@ import java.util.WeakHashMap; import java.util.concurrent.atomic.AtomicReference; import java.util.stream.Stream; import org.lwjgl.opengl.ARBVertexArrayObject; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class BufferSource { + private static final Logger LOGGER = LoggerFactory.getLogger(BufferSource.class); private final WeakHashMap buffersByMode = new WeakHashMap(); private final Set pendingModes = new LinkedHashSet(); public static final boolean ASSERTIONS_DISABLED = !BufferSource.class.desiredAssertionStatus(); @@ -53,6 +56,8 @@ public class BufferSource { MappedMeshBuilder builder = (MappedMeshBuilder)bufferData.activeBuilder().get(); if (!ASSERTIONS_DISABLED && builder == null) { throw new AssertionError(MISSING_BUILDER_MESSAGE); + } else if (builder == null) { + return; } else { int indexCount = builder.getIndexCount(); int vertexCount = builder.getVertexCount(); @@ -66,11 +71,18 @@ public class BufferSource { } public void flush(CameraMatrices context) { - for (DrawPipeline drawMode : (Iterable)(this.pendingModes)) { - this.draw(context, drawMode); + try { + for (DrawPipeline drawMode : (Iterable)(this.pendingModes)) { + try { + this.draw(context, drawMode); + } catch (Throwable throwable) { + LOGGER.error("Failed to draw buffered pipeline", throwable); + } + } + } finally { + this.pendingModes.clear(); } - this.pendingModes.clear(); this.buffersByMode.size(); } diff --git a/src/main/java/dev/zprestige/prestige/render/DrawCommandQueue.java b/src/main/java/dev/zprestige/prestige/render/DrawCommandQueue.java index 5f6d707..cae48bf 100644 --- a/src/main/java/dev/zprestige/prestige/render/DrawCommandQueue.java +++ b/src/main/java/dev/zprestige/prestige/render/DrawCommandQueue.java @@ -16,6 +16,7 @@ public class DrawCommandQueue { private boolean initialized = false; private final List commands = new ArrayList(); public float nextDepth = 0.0F; + private static final int MAX_QUEUED_COMMANDS = 200000; private void ensureInitialized() { if (!this.initialized) { @@ -29,6 +30,10 @@ public class DrawCommandQueue { } public void enqueue(DrawCommand command) { + if (this.commands.size() >= MAX_QUEUED_COMMANDS) { + this.clear(); + } + this.commands.add(new QueuedDraw(command, this.nextDepth)); this.nextDepth = Math.nextUp(this.nextDepth); } @@ -37,60 +42,69 @@ public class DrawCommandQueue { if (this.commands.isEmpty()) { this.nextDepth = 0.0F; } else { - this.ensureInitialized(); - int batchStart = 0; + boolean scissorEnabled = false; + try { + this.ensureInitialized(); + int batchStart = 0; - while (batchStart < this.commands.size()) { - DrawBatchKey key = ((QueuedDraw)this.commands.get(batchStart)).drawable().getBatchKey(); - int batchEnd = batchStart + 1; + while (batchStart < this.commands.size()) { + DrawBatchKey key = ((QueuedDraw)this.commands.get(batchStart)).drawable().getBatchKey(); + int batchEnd = batchStart + 1; - while (batchEnd < this.commands.size() && ((QueuedDraw)this.commands.get(batchEnd)).drawable().getBatchKey().equals(key)) { - batchEnd++; - } - - List batch = this.commands.subList(batchStart, batchEnd); - Vector4f scissor = key.scissorRect(); - if (scissor != null) { - GL11.glEnable(3089); - int framebufferHeight = RenderCore.getFramebufferHeight(); - float scale = RenderCore.getScaleFactor(); - GL11.glScissor((int)(scissor.x * scale), (int)(framebufferHeight - scissor.y * scale - scissor.w * scale), (int)(scissor.z * scale), (int)(scissor.w * scale)); - } - - DrawPipeline pipeline = key.drawMode(); - this.vertexArray - .setBindings(VertexArray.createBindings(this.vertexBuffer, pipeline.getVertexFormat())); - MappedMeshBuilder builder = new MappedMeshBuilder(pipeline.getVertexFormat(), this.vertexBuffer, this.indexBuffer); - - int indexCount; - int vertexCount; - try { - for (QueuedDraw entry : (Iterable)(batch)) { - entry.drawable().emitVertices(builder, entry.depth()); + while (batchEnd < this.commands.size() && ((QueuedDraw)this.commands.get(batchEnd)).drawable().getBatchKey().equals(key)) { + batchEnd++; } - vertexCount = builder.getVertexCount(); - indexCount = builder.getIndexCount(); - } catch (Throwable failure) { + List batch = this.commands.subList(batchStart, batchEnd); + Vector4f scissor = key.scissorRect(); + if (scissor != null) { + GL11.glEnable(3089); + scissorEnabled = true; + int framebufferHeight = RenderCore.getFramebufferHeight(); + float scale = RenderCore.getScaleFactor(); + GL11.glScissor((int)(scissor.x * scale), (int)(framebufferHeight - scissor.y * scale - scissor.w * scale), (int)(scissor.z * scale), (int)(scissor.w * scale)); + } + + DrawPipeline pipeline = key.drawMode(); + this.vertexArray + .setBindings(VertexArray.createBindings(this.vertexBuffer, pipeline.getVertexFormat())); + MappedMeshBuilder builder = new MappedMeshBuilder(pipeline.getVertexFormat(), this.vertexBuffer, this.indexBuffer); + + int indexCount; + int vertexCount; try { - builder.close(); - } catch (Throwable closeFailure) { - failure.addSuppressed(closeFailure); + for (QueuedDraw entry : (Iterable)(batch)) { + entry.drawable().emitVertices(builder, entry.depth()); + } + + vertexCount = builder.getVertexCount(); + indexCount = builder.getIndexCount(); + } catch (Throwable failure) { + try { + builder.close(); + } catch (Throwable closeFailure) { + failure.addSuppressed(closeFailure); + } + + throw failure; } - throw failure; - } + builder.close(); + pipeline.draw(context, this.vertexArray, this.indexBuffer, new MeshInfo(pipeline.getVertexFormat(), vertexCount, indexCount)); + if (scissor != null) { + GL11.glDisable(3089); + scissorEnabled = false; + } - builder.close(); - pipeline.draw(context, this.vertexArray, this.indexBuffer, new MeshInfo(pipeline.getVertexFormat(), vertexCount, indexCount)); - if (scissor != null) { + batchStart = batchEnd; + } + } finally { + if (scissorEnabled) { GL11.glDisable(3089); } - batchStart = batchEnd; + this.clear(); } - - this.clear(); } } diff --git a/src/main/java/dev/zprestige/prestige/render/DrawPipeline.java b/src/main/java/dev/zprestige/prestige/render/DrawPipeline.java index b33a5d8..5402c7e 100644 --- a/src/main/java/dev/zprestige/prestige/render/DrawPipeline.java +++ b/src/main/java/dev/zprestige/prestige/render/DrawPipeline.java @@ -49,17 +49,19 @@ public class DrawPipeline { } GlRenderPass pass = passBuilder.build(); - pass.begin(); - pass.uploadMatrices(context); + try { + pass.begin(); + pass.uploadMatrices(context); - for (RenderStateElement applyState : this.states) { - applyState.apply(pass); - } + for (RenderStateElement applyState : this.states) { + applyState.apply(pass); + } - pass.drawIndexed(vertexArray, indexBuffer, this.primitiveMode, drawInfo.indexCount()); - - for (RenderStateElement cleanupState : this.states) { - cleanupState.cleanup(); + pass.drawIndexed(vertexArray, indexBuffer, this.primitiveMode, drawInfo.indexCount()); + } finally { + for (RenderStateElement cleanupState : this.states) { + cleanupState.cleanup(); + } } } } diff --git a/src/main/java/dev/zprestige/prestige/render/EspShaderManager.java b/src/main/java/dev/zprestige/prestige/render/EspShaderManager.java index cf7f31a..9065c74 100644 --- a/src/main/java/dev/zprestige/prestige/render/EspShaderManager.java +++ b/src/main/java/dev/zprestige/prestige/render/EspShaderManager.java @@ -56,7 +56,7 @@ public class EspShaderManager implements MinecraftAccess { } break; } else { - entity = type.create(mc.world, null); + entity = createEntity(type); if (entity != null) { discardAfter = true; break; @@ -82,6 +82,14 @@ public class EspShaderManager implements MinecraftAccess { } } + private static Entity createEntity(EntityType type) { + try { + return type.create(mc.world, null); + } catch (Throwable ignored) { + return null; + } + } + private static Class getRendererModelClass(EntityRenderer renderer) { Class rendererClass = renderer.getClass(); Class cached = (Class)rendererModelCache.get(rendererClass); @@ -139,7 +147,7 @@ public class EspShaderManager implements MinecraftAccess { } break; } else { - entity = type.create(mc.world, null); + entity = createEntity(type); if (entity != null) { discardAfter = true; break; diff --git a/src/main/java/dev/zprestige/prestige/render/GlTextureHandle.java b/src/main/java/dev/zprestige/prestige/render/GlTextureHandle.java index 13dccfd..a675bf4 100644 --- a/src/main/java/dev/zprestige/prestige/render/GlTextureHandle.java +++ b/src/main/java/dev/zprestige/prestige/render/GlTextureHandle.java @@ -111,6 +111,20 @@ public class GlTextureHandle implements AutoCloseable { } } + @Override + public boolean equals(Object other) { + if (this == other) { + return true; + } else { + return other instanceof GlTextureHandle that && this.target == that.target && this.glId == that.glId; + } + } + + @Override + public int hashCode() { + return 31 * this.target + this.glId; + } + public static GlTextureHandle wrapMinecraftTexture(Identifier textureIdentifier) { return wrap(3553, ((GlTexture)MinecraftAccess.mc.getTextureManager().getTexture(textureIdentifier).getGlTexture()).getGlId()); } diff --git a/src/main/java/dev/zprestige/prestige/render/MsdfFont.java b/src/main/java/dev/zprestige/prestige/render/MsdfFont.java index 12a6a56..94b1572 100644 --- a/src/main/java/dev/zprestige/prestige/render/MsdfFont.java +++ b/src/main/java/dev/zprestige/prestige/render/MsdfFont.java @@ -85,36 +85,39 @@ public class MsdfFont { this.texelWidth = 1.0F / this.atlasWidth; this.texelHeight = 1.0F / this.atlasHeight; NativeImage image = NativeImage.read(imageStream); - this.atlasTexture = GlTextureHandle.create(3553); - this.atlasTexture.allocate(32856, image.getWidth(), image.getHeight()); - int width = image.getWidth(); - int height = image.getHeight(); - ByteBuffer pixels = BufferUtils.createByteBuffer(width * height * 4); - pixels.order(ByteOrder.LITTLE_ENDIAN); + try { + this.atlasTexture = GlTextureHandle.create(3553); + this.atlasTexture.allocate(32856, image.getWidth(), image.getHeight()); + int width = image.getWidth(); + int height = image.getHeight(); + ByteBuffer pixels = BufferUtils.createByteBuffer(width * height * 4); + pixels.order(ByteOrder.LITTLE_ENDIAN); - for (int row = 0; row < height; row++) { - int column = 0; + for (int row = 0; row < height; row++) { + int column = 0; - while (column < width) { - int argb = image.getColorArgb(column, row); + while (column < width) { + int argb = image.getColorArgb(column, row); - { - pixels.putInt(argb); - column++; + { + pixels.putInt(argb); + column++; + } } } - } - pixels.flip(); - GL11.glPixelStorei(3317, 1); - GL11.glPixelStorei(3314, 0); - GL11.glPixelStorei(3316, 0); - GL11.glPixelStorei(3315, 0); - this.atlasTexture.uploadRegion(0, 0, width, height, 6408, 5121, pixels); - GL11.glPixelStorei(3317, 4); - this.atlasTexture.setFilter(9729); - this.atlasTexture.setWrapMode(33071); - image.close(); + pixels.flip(); + GL11.glPixelStorei(3317, 1); + GL11.glPixelStorei(3314, 0); + GL11.glPixelStorei(3316, 0); + GL11.glPixelStorei(3315, 0); + this.atlasTexture.uploadRegion(0, 0, width, height, 6408, 5121, pixels); + GL11.glPixelStorei(3317, 4); + this.atlasTexture.setFilter(9729); + this.atlasTexture.setWrapMode(33071); + } finally { + image.close(); + } for (JsonElement glyphElement : json.getAsJsonArray("glyphs")) { MsdfGlyph glyph = MsdfGlyph.fromJson(glyphElement.getAsJsonObject()); diff --git a/src/main/java/dev/zprestige/prestige/render/Notification.java b/src/main/java/dev/zprestige/prestige/render/Notification.java index d813396..8c3d4ac 100644 --- a/src/main/java/dev/zprestige/prestige/render/Notification.java +++ b/src/main/java/dev/zprestige/prestige/render/Notification.java @@ -9,6 +9,7 @@ import java.awt.Color; import org.joml.Matrix4f; public class Notification { + private static final java.util.Map ICON_CACHE = new java.util.HashMap<>(); public final String title; public final String message; private final long durationMs; @@ -36,7 +37,7 @@ public class Notification { public float render(Matrix4f matrix, BufferSource buffers, CameraMatrices context, float posX, float posY, float width, float height, boolean visible) { if (this.iconTexture == null) { - this.iconTexture = TextureLoader.loadTexture(this.iconPath); + this.iconTexture = ICON_CACHE.computeIfAbsent(this.iconPath, TextureLoader::loadTexture); } posX += (width + 30.0F) * (1.0F - this.animation); diff --git a/src/main/java/dev/zprestige/prestige/render/PostShader.java b/src/main/java/dev/zprestige/prestige/render/PostShader.java index d723854..9a5b8bd 100644 --- a/src/main/java/dev/zprestige/prestige/render/PostShader.java +++ b/src/main/java/dev/zprestige/prestige/render/PostShader.java @@ -24,7 +24,7 @@ public abstract class PostShader extends ShaderBase { this.maskHeight = -1; this.initialized = false; this.maskFramebuffer = GlFramebuffer.create(); - this.pipelineCache = new java.util.HashMap(); // reconstructed: the ZKM ref was lost + this.pipelineCache = dev.zprestige.prestige.math.RenderCacheUtil.newLruCache(256); this.maskTexture = GlTextureHandle.create(3553); // reconstructed: colour attachment texture (GL_TEXTURE_2D), sized on first use in g() } @@ -47,17 +47,20 @@ public abstract class PostShader extends ShaderBase { GL11.glEnable(3042); GL11.glBlendFunc(770, 771); GL11.glDisable(2929); - this.compositePasses.render(); - if (depthWasEnabled) { - GL11.glEnable(2929); - } + try { + this.compositePasses.render(); + } finally { + if (depthWasEnabled) { + GL11.glEnable(2929); + } - if (cullWasEnabled) { - GL11.glEnable(2884); - } + if (cullWasEnabled) { + GL11.glEnable(2884); + } - if (!blendWasEnabled) { - GL11.glDisable(3042); + if (!blendWasEnabled) { + GL11.glDisable(3042); + } } this.maskFramebuffer.clearColor(0.0F, 0.0F, 0.0F, 0.0F); diff --git a/src/main/java/dev/zprestige/prestige/render/RenderCore.java b/src/main/java/dev/zprestige/prestige/render/RenderCore.java index f7f304a..fe95dc4 100644 --- a/src/main/java/dev/zprestige/prestige/render/RenderCore.java +++ b/src/main/java/dev/zprestige/prestige/render/RenderCore.java @@ -16,8 +16,11 @@ import net.minecraft.client.gl.GlBackend; import net.minecraft.client.texture.GlTexture; import net.minecraft.util.math.Vec3d; import org.joml.Vector3d; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public class RenderCore { + private static final Logger LOGGER = LoggerFactory.getLogger(RenderCore.class); public static final Cleaner CLEANER = Cleaner.create(); public static final BufferSource BUFFER_SOURCE = new BufferSource(); private static final List TASK_QUEUE = new ArrayList(); @@ -59,30 +62,25 @@ public class RenderCore { } } - // $VF: Could not create synchronized statement, marking monitor enters and exits - // Please report this to the Vineflower issue tracker, at https://github.com/Vineflower/vineflower/issues with a copy of the class file (if you have the rights to distribute it!) public static List drainTasks() { - synchronized (TASK_QUEUE){} // $VF: monitorenter - - try { + synchronized (TASK_QUEUE) { if (TASK_QUEUE.isEmpty()) { - List empty = List.of(); - // $VF: monitorexit - return empty; - } else { - ArrayList drained = new ArrayList(TASK_QUEUE); - TASK_QUEUE.clear(); - // $VF: monitorexit - return drained; + return List.of(); } - } finally { - // $VF: monitorexit + + ArrayList drained = new ArrayList(TASK_QUEUE); + TASK_QUEUE.clear(); + return drained; } } public static void runTasks(List tasks) { for (Runnable task : (Iterable)(tasks)) { - task.run(); + try { + task.run(); + } catch (Throwable throwable) { + LOGGER.error("Render thread task failed", throwable); + } } } @@ -90,8 +88,12 @@ public class RenderCore { Render2D.currentContext = context; Render2D.currentBuffers = BUFFER_SOURCE; worldContext = context; - ((RenderCallback)RenderEvents.WORLD_RENDER.invoker()).onRender(BUFFER_SOURCE, context); - BUFFER_SOURCE.flush(context); + try { + ((RenderCallback)RenderEvents.WORLD_RENDER.invoker()).onRender(BUFFER_SOURCE, context); + BUFFER_SOURCE.flush(context); + } catch (Throwable throwable) { + LOGGER.error("World render failed", throwable); + } } public static void renderHud(CameraMatrices context) { @@ -99,10 +101,14 @@ public class RenderCore { if (MinecraftAccess.mc.world != null && MinecraftAccess.mc.player != null) { Render2D.currentContext = context; Render2D.currentBuffers = BUFFER_SOURCE; - ((RenderCallback)RenderEvents.HUD_RENDER.invoker()).onRender(BUFFER_SOURCE, context); - BUFFER_SOURCE.flush(context); - ((RenderCallback)RenderEvents.AFTER_HUD_RENDER.invoker()).onRender(BUFFER_SOURCE, context); - BUFFER_SOURCE.flush(context); + try { + ((RenderCallback)RenderEvents.HUD_RENDER.invoker()).onRender(BUFFER_SOURCE, context); + BUFFER_SOURCE.flush(context); + ((RenderCallback)RenderEvents.AFTER_HUD_RENDER.invoker()).onRender(BUFFER_SOURCE, context); + BUFFER_SOURCE.flush(context); + } catch (Throwable throwable) { + LOGGER.error("HUD render failed", throwable); + } } } diff --git a/src/main/java/dev/zprestige/prestige/render/RenderEvents.java b/src/main/java/dev/zprestige/prestige/render/RenderEvents.java index 0ffe2bb..d7efcf3 100644 --- a/src/main/java/dev/zprestige/prestige/render/RenderEvents.java +++ b/src/main/java/dev/zprestige/prestige/render/RenderEvents.java @@ -3,8 +3,11 @@ package dev.zprestige.prestige.render; import net.fabricmc.fabric.api.event.Event; import net.fabricmc.fabric.api.event.EventFactory; import org.jetbrains.annotations.NotNull; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public interface RenderEvents { + Logger LOGGER = LoggerFactory.getLogger(RenderEvents.class); Event HUD_RENDER = createEvent(); Event WORLD_RENDER = createEvent(); Event AFTER_HUD_RENDER = createEvent(); @@ -13,7 +16,11 @@ public interface RenderEvents { private static Event createEvent() { return EventFactory.createArrayBacked(RenderCallback.class, listeners -> (buffers, context) -> { for (RenderCallback listener : (listeners)) { - listener.onRender(buffers, context); + try { + listener.onRender(buffers, context); + } catch (Throwable throwable) { + LOGGER.error("Render listener failed", throwable); + } } }); } diff --git a/src/main/java/dev/zprestige/prestige/render/RoundedTextureShader.java b/src/main/java/dev/zprestige/prestige/render/RoundedTextureShader.java index 3ac5a88..9d3dc21 100644 --- a/src/main/java/dev/zprestige/prestige/render/RoundedTextureShader.java +++ b/src/main/java/dev/zprestige/prestige/render/RoundedTextureShader.java @@ -2,6 +2,7 @@ package dev.zprestige.prestige.render; import dev.zprestige.prestige.data.BlendFuncState; import dev.zprestige.prestige.data.CameraMatrices; +import dev.zprestige.prestige.math.RenderCacheUtil; import dev.zprestige.prestige.math.TexturedDrawModeKey; import dev.zprestige.prestige.type.BlendMode; import dev.zprestige.prestige.type.GlPrimitive; @@ -18,7 +19,7 @@ import org.lwjgl.opengl.GL20; public class RoundedTextureShader extends ShaderBase { private static final RoundedTextureShader INSTANCE = new RoundedTextureShader(); - private static final Map DRAW_MODE_CACHE = new HashMap(); + private static final Map DRAW_MODE_CACHE = RenderCacheUtil.newLruCache(256); private final int tintModeUniform; private final int roundnessUniform; diff --git a/src/main/java/dev/zprestige/prestige/render/ShaderCompiler.java b/src/main/java/dev/zprestige/prestige/render/ShaderCompiler.java index 9a93ffc..f64a17d 100644 --- a/src/main/java/dev/zprestige/prestige/render/ShaderCompiler.java +++ b/src/main/java/dev/zprestige/prestige/render/ShaderCompiler.java @@ -10,7 +10,14 @@ public class ShaderCompiler { public static Shader loadProgram(String vertexPath, String fragmentPath, boolean spirv) { int vertexShader = compileShader(35633, vertexPath, spirv); - int fragmentShader = compileShader(35632, fragmentPath, spirv); + int fragmentShader; + try { + fragmentShader = compileShader(35632, fragmentPath, spirv); + } catch (RuntimeException exception) { + GL20.glDeleteShader(vertexShader); + throw exception; + } + int program = GL20.glCreateProgram(); GL20.glAttachShader(program, vertexShader); GL20.glAttachShader(program, fragmentShader); @@ -18,6 +25,9 @@ public class ShaderCompiler { if (GL20.glGetProgrami(program, 35714) == 0) { String linkLog = GL20.glGetProgramInfoLog(program); System.err.println(linkLog); + GL20.glDeleteProgram(program); + GL20.glDeleteShader(vertexShader); + GL20.glDeleteShader(fragmentShader); throw new RuntimeException("Program link failed. See error above"); } else { GL20.glDeleteShader(vertexShader); @@ -30,12 +40,10 @@ public class ShaderCompiler { if (spirv) { throw new UnsupportedOperationException("SPIR-V shader loading was removed for GL 3.3 compatibility. Provide GLSL source instead: " + path); } else { - int shader = GL20.glCreateShader(shaderType); - String source; try (InputStream stream = ShaderCompiler.class.getClassLoader().getResourceAsStream(path)) { - if (!ASSERTIONS_DISABLED && stream == null) { - throw new AssertionError(); + if (stream == null) { + throw new IOException("Shader resource not found: " + path); } source = new String(stream.readAllBytes(), StandardCharsets.UTF_8); @@ -43,12 +51,14 @@ public class ShaderCompiler { throw new RuntimeException(exception); } + int shader = GL20.glCreateShader(shaderType); GL20.glShaderSource(shader, source); GL20.glCompileShader(shader); if (GL20.glGetShaderi(shader, 35713) == 0) { String compileLog = GL20.glGetShaderInfoLog(shader); System.err.println(compileLog); - throw new RuntimeException("Shader compile failed. See logs above for error"); + GL20.glDeleteShader(shader); + throw new RuntimeException("Shader compile failed for " + path + ". See logs above for error"); } else { return shader; } diff --git a/src/main/java/dev/zprestige/prestige/render/SilentRotationManager.java b/src/main/java/dev/zprestige/prestige/render/SilentRotationManager.java index a547608..568201a 100644 --- a/src/main/java/dev/zprestige/prestige/render/SilentRotationManager.java +++ b/src/main/java/dev/zprestige/prestige/render/SilentRotationManager.java @@ -49,7 +49,10 @@ public class SilentRotationManager implements MinecraftAccess { } } - if (this.rotationProviders.isEmpty() && this.activeRotation == null && this.resyncRotation == null) { + if (mc.player == null) { + this.serverRotation = null; + this.lastSentRotation = null; + } else if (this.rotationProviders.isEmpty() && this.activeRotation == null && this.resyncRotation == null) { this.serverRotation = null; this.lastSentRotation = null; } else { diff --git a/src/main/java/dev/zprestige/prestige/render/StorageOutlineRenderer.java b/src/main/java/dev/zprestige/prestige/render/StorageOutlineRenderer.java index 2106648..b61d039 100644 --- a/src/main/java/dev/zprestige/prestige/render/StorageOutlineRenderer.java +++ b/src/main/java/dev/zprestige/prestige/render/StorageOutlineRenderer.java @@ -111,17 +111,20 @@ public class StorageOutlineRenderer implements MinecraftAccess { GL30.glEnable(3042); GL30.glBlendFunc(770, 771); GL30.glDisable(2929); - this.outlinePass.render(); - if (depthWasEnabled) { - GL30.glEnable(2929); - } + try { + this.outlinePass.render(); + } finally { + if (depthWasEnabled) { + GL30.glEnable(2929); + } - if (cullWasEnabled) { - GL30.glEnable(2884); - } + if (cullWasEnabled) { + GL30.glEnable(2884); + } - if (!blendWasEnabled) { - GL30.glDisable(3042); + if (!blendWasEnabled) { + GL30.glDisable(3042); + } } this.framebuffer.clearColor(0.0F, 0.0F, 0.0F, 0.0F); diff --git a/src/main/java/dev/zprestige/prestige/render/TextureLoader.java b/src/main/java/dev/zprestige/prestige/render/TextureLoader.java index d90ffa8..74fc266 100644 --- a/src/main/java/dev/zprestige/prestige/render/TextureLoader.java +++ b/src/main/java/dev/zprestige/prestige/render/TextureLoader.java @@ -46,6 +46,7 @@ public class TextureLoader { throw new IOException("Image buffer too small: " + pixels.remaining() + " bytes, expected " + expectedSize); } + int previousTexture = GL11.glGetInteger(32873); GlTextureHandle texture = GlTextureHandle.create(3553); texture.allocate(32856, width, height); GL15.glBindBuffer(35052, 0); @@ -72,7 +73,7 @@ public class TextureLoader { GL11.glTexParameterf(3553, 34046, Math.min(64.0F, MAX_ANISOTROPY)); } - GL11.glBindTexture(3553, 0); + GL11.glBindTexture(3553, previousTexture); result = texture; } finally { STBImage.stbi_image_free(pixels); diff --git a/src/main/java/dev/zprestige/prestige/social/MojangApi.java b/src/main/java/dev/zprestige/prestige/social/MojangApi.java index 2367d11..5aa0ed6 100644 --- a/src/main/java/dev/zprestige/prestige/social/MojangApi.java +++ b/src/main/java/dev/zprestige/prestige/social/MojangApi.java @@ -17,6 +17,8 @@ public class MojangApi { try { HttpURLConnection connection = (HttpURLConnection)new URL("https://api.mojang.com/users/profiles/minecraft/" + username).openConnection(); connection.setRequestMethod("GET"); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); if (connection.getResponseCode() != 200) { return null; } else { @@ -40,6 +42,8 @@ public class MojangApi { ) .openConnection(); connection.setRequestMethod("GET"); + connection.setConnectTimeout(5000); + connection.setReadTimeout(5000); if (connection.getResponseCode() != 200) { return null; } else { diff --git a/src/main/java/dev/zprestige/prestige/ui/ConfigsPage.java b/src/main/java/dev/zprestige/prestige/ui/ConfigsPage.java index 328b397..9ebb975 100644 --- a/src/main/java/dev/zprestige/prestige/ui/ConfigsPage.java +++ b/src/main/java/dev/zprestige/prestige/ui/ConfigsPage.java @@ -455,7 +455,8 @@ public final class ConfigsPage { /** Typed character. Return true if consumed. */ public static boolean typed(int codepoint) { - if (tab > 1 || !searchFocused || codepoint < 32) return false; + if (tab > 1 || !searchFocused || codepoint < 32 || !Character.isValidCodePoint(codepoint)) return false; + if (query.length() >= 64) return true; query += new String(Character.toChars(codepoint)); return true; } diff --git a/src/main/java/dev/zprestige/prestige/ui/Data.java b/src/main/java/dev/zprestige/prestige/ui/Data.java index 60090f5..659b2e3 100644 --- a/src/main/java/dev/zprestige/prestige/ui/Data.java +++ b/src/main/java/dev/zprestige/prestige/ui/Data.java @@ -64,6 +64,7 @@ public final class Data { } out.add(bind); for (String[] f : SETTINGS.getOrDefault(module, List.of())) { + if (f.length < 4) continue; // malformed settings.tsv row DetailPage.Setting s = new DetailPage.Setting(); s.name = f[2]; s.desc = f[3]; try { diff --git a/src/main/java/dev/zprestige/prestige/ui/DetailPage.java b/src/main/java/dev/zprestige/prestige/ui/DetailPage.java index fb9ffc8..d991936 100644 --- a/src/main/java/dev/zprestige/prestige/ui/DetailPage.java +++ b/src/main/java/dev/zprestige/prestige/ui/DetailPage.java @@ -165,6 +165,7 @@ public final class DetailPage { float v74 = (a3 + 17f) + 10f; fillRect(v73, v74 - 3f, 200f, 6f, 3f, new float[]{1f, 1f, 1f, 0.1f}); float span = s.rmax - s.rmin; + if (!(span > 0f)) span = 1f; // zero/NaN range would give NaN draw coordinates float a = Math.min(1f, (s.lo - s.rmin) / span); if (a < 0f) a = 0f; float bb = Math.min(1f, (s.hi - s.rmin) / span); if (bb < 0f) bb = 0f; float x1 = a * 200f + v73, x2 = bb * 200f + v73; @@ -192,7 +193,7 @@ public final class DetailPage { static void slider(float x, float y, float w, float h, float val, float min, float max, boolean hot, boolean drag) { float cy = h * 0.5f + y; fillRect(x, cy - 3f, w, 6f, 3f, new float[]{1f, 1f, 1f, 0.1f}); - float t = Math.min(1f, (val - min) / (max - min)); if (t < 0f) t = 0f; + float t = max > min ? Math.min(1f, (val - min) / (max - min)) : 0f; if (!(t >= 0f)) t = 0f; // max == min / NaN would give NaN draw coordinates float fw = t * w; fillRect(x, cy - 3f, fw, 6f, 3f, Theme.accent()); knob(fw + x, cy, hot, drag); diff --git a/src/main/java/dev/zprestige/prestige/ui/LocalConfigs.java b/src/main/java/dev/zprestige/prestige/ui/LocalConfigs.java index 0b50ee6..c317834 100644 --- a/src/main/java/dev/zprestige/prestige/ui/LocalConfigs.java +++ b/src/main/java/dev/zprestige/prestige/ui/LocalConfigs.java @@ -88,6 +88,10 @@ public final class LocalConfigs { Entry e = GSON.fromJson(Files.readString(p, StandardCharsets.UTF_8), Entry.class); if (e == null || e.name == null || e.name.isBlank()) continue; if (e.tags == null) e.tags = new ArrayList<>(); + e.tags.removeIf(java.util.Objects::isNull); + if (e.desc == null) e.desc = ""; + if (e.author == null) e.author = ""; + if (e.date == null) e.date = ""; ALL.add(e); } catch (Throwable t) { System.out.println("[prestige-ui] config " + p.getFileName() + ": " + t); } } @@ -174,7 +178,8 @@ public final class LocalConfigs { e.date = LocalDate.now().toString(); e.visibility = CreateConfigPage.visibility; for (int i = 0; i < CreateConfigPage.TAGS.length; i++) if (CreateConfigPage.tagSel[i]) e.tags.add(CreateConfigPage.TAGS[i]); - e.data = GSON.fromJson(snap, JsonObject.class); + try { e.data = GSON.fromJson(snap, JsonObject.class); } + catch (RuntimeException ex) { System.out.println("[prestige-ui] config snapshot: " + ex); WidgetsPage.toast("Could not create config"); return; } write(e); ALL.add(0, e); CreateConfigPage.name.setLength(0); @@ -210,7 +215,8 @@ public final class LocalConfigs { Entry e = find(c); String snap = snapshot(); if (e == null || snap == null) { WidgetsPage.toast("Could not update config"); return; } - e.data = GSON.fromJson(snap, JsonObject.class); + try { e.data = GSON.fromJson(snap, JsonObject.class); } + catch (RuntimeException ex) { System.out.println("[prestige-ui] config snapshot: " + ex); WidgetsPage.toast("Could not update config"); return; } e.date = LocalDate.now().toString(); write(e); rebuild(); diff --git a/src/main/java/dev/zprestige/prestige/ui/WidgetsPage.java b/src/main/java/dev/zprestige/prestige/ui/WidgetsPage.java index 61bd28d..92e4019 100644 --- a/src/main/java/dev/zprestige/prestige/ui/WidgetsPage.java +++ b/src/main/java/dev/zprestige/prestige/ui/WidgetsPage.java @@ -208,7 +208,7 @@ public final class WidgetsPage { popupFrame(x, y, h, anim, sc, yo); for (int i = 0; i < opts.length; i++) { float t = stag(anim, i), iy = (i * 28f + y) - yo + (1f - t) * 6f; - boolean on = sel[i], hov = hover == i; + boolean on = i < sel.length && sel[i], hov = hover == i; if (hov) hoverBar(x, iy, i, opts.length, t); float bx = x + 10f, by = (iy + 14f) - 6f, ca = chk[i]; fillRect(bx, by, 12f, 12f, 2f, a(lerp(Theme.e2B70(), Theme.accent(), ca), t)); diff --git a/src/main/java/dev/zprestige/prestige/ui/mc/Entry.java b/src/main/java/dev/zprestige/prestige/ui/mc/Entry.java index d5ed3eb..f8cf15a 100644 --- a/src/main/java/dev/zprestige/prestige/ui/mc/Entry.java +++ b/src/main/java/dev/zprestige/prestige/ui/mc/Entry.java @@ -31,6 +31,7 @@ public final class Entry implements ClientModInitializer { static boolean bridgeTried; void tick(MinecraftClient mc) { + if (dev.zprestige.prestige.client.Prestige.selfDestructed) return; if (!bridgeTried) { bridgeTried = true; dev.zprestige.prestige.ui.bridge.Bridge.bind(); } try { long h = mc.getWindow().getHandle(); diff --git a/src/main/java/dev/zprestige/prestige/ui/mc/Overlay.java b/src/main/java/dev/zprestige/prestige/ui/mc/Overlay.java index 8095f3c..15a5424 100644 --- a/src/main/java/dev/zprestige/prestige/ui/mc/Overlay.java +++ b/src/main/java/dev/zprestige/prestige/ui/mc/Overlay.java @@ -65,7 +65,7 @@ public final class Overlay { // ---- open / close (no Screen involved: the game keeps rendering and updating underneath) public static void open() { - if (visible) return; + if (visible || failed) return; MinecraftClient mc = MinecraftClient.getInstance(); loadPos(); wasGrabbed = mc.mouse.isCursorLocked(); @@ -119,34 +119,49 @@ public final class Overlay { } // ---- events from the MouseHandler / KeyboardHandler mixins (all return true when consumed) - public static boolean onMouseMove() { if (!visible) return false; pollCursor(); return true; } + /** Handlers must never throw into the game's input callbacks; an exception here would crash the client. */ + private static void inputError(Throwable t) { System.out.println("[prestige-ui] input " + t); } + + public static boolean onMouseMove() { if (!visible) return false; try { pollCursor(); } catch (Throwable t) { inputError(t); } return true; } public static boolean onMouseButton(int button, int action) { if (!visible) return false; - syncMouse(); - if (action == 1) Input.mouseDown(button); else if (action == 0) Input.mouseUp(button); - afterEvent(); + if (!inited || failed) return true; + try { + syncMouse(); + if (action == 1) Input.mouseDown(button); else if (action == 0) Input.mouseUp(button); + afterEvent(); + } catch (Throwable t) { inputError(t); } return true; } public static boolean onScroll(double dy) { if (!visible) return false; - syncMouse(); - Input.scroll(dy); + if (!inited || failed) return true; + try { + syncMouse(); + Input.scroll(dy); + } catch (Throwable t) { inputError(t); } return true; } public static boolean onKey(int key, int action) { if (!visible) return false; - if (key == 341 || key == 345) Input.ctrlDown = action != 0; - if (action == 1 || action == 2) { syncMouse(); Input.key(key); afterEvent(); } + if (!inited || failed) return true; + try { + if (key == 341 || key == 345) Input.ctrlDown = action != 0; + if (action == 1 || action == 2) { syncMouse(); Input.key(key); afterEvent(); } + } catch (Throwable t) { inputError(t); } return true; } public static boolean onChar(int cp) { if (!visible) return false; - Input.typed(cp); - afterEvent(); + if (!inited || failed) return true; + try { + Input.typed(cp); + afterEvent(); + } catch (Throwable t) { inputError(t); } return true; } @@ -267,6 +282,13 @@ public final class Overlay { javax.imageio.ImageIO.write(img, "png", new java.io.File(shotPath)); } + /** Self destruct: closes the menu and stops all further rendering and input handling. */ + public static void shutdown() { + try { close(); } catch (Throwable ignored) { } + visible = false; + failed = true; + } + public static void render() { if (!visible || failed) return; try { @@ -314,9 +336,9 @@ public final class Overlay { if (shotPath != null) { shoot(fbW, fbH); shotPath = null; } afterEvent(); } catch (Throwable t) { - failed = true; System.out.println("[prestige-ui] overlay failed: " + t); t.printStackTrace(System.out); + shutdown(); // failed = true and hide, otherwise the invisible menu keeps swallowing keyboard and mouse input } } } diff --git a/src/main/java/dev/zprestige/prestige/world/BlockUtil.java b/src/main/java/dev/zprestige/prestige/world/BlockUtil.java index 362baed..38d18d6 100644 --- a/src/main/java/dev/zprestige/prestige/world/BlockUtil.java +++ b/src/main/java/dev/zprestige/prestige/world/BlockUtil.java @@ -52,7 +52,7 @@ public class BlockUtil implements MinecraftAccess { } public static boolean isClickableHit(HitResult hitResult) { - return isClickableBlock(((BlockHitResult)hitResult).getBlockPos()); + return hitResult instanceof BlockHitResult blockHit && isClickableBlock(blockHit.getBlockPos()); } public static boolean isClickableBlockHit(BlockHitResult hitResult) { @@ -112,6 +112,7 @@ public class BlockUtil implements MinecraftAccess { } public static int getAnchorCharges(BlockPos pos) { - return getState(pos).get(Properties.CHARGES); + BlockState state = getState(pos); + return state.contains(Properties.CHARGES) ? state.get(Properties.CHARGES) : 0; } }