Create features system with Flight, No Fall Damage

This commit is contained in:
Charlotte Som 2022-02-03 19:29:14 +00:00
parent ab0e18e2c8
commit ceac1fda7a
37 changed files with 930 additions and 57 deletions

View file

@ -1,7 +1,11 @@
import org.gradle.kotlin.dsl.accessors.runtime.extensionOf
import org.quiltmc.quiltmappings.loom.QuiltMappingsOnLoomPlugin
plugins {
id("fabric-loom")
val kotlinVersion: String by System.getProperties()
kotlin("jvm").version(kotlinVersion)
id("org.quiltmc.quilt-mappings-on-loom").version("3.1.1")
}
base {
@ -21,8 +25,25 @@ repositories {}
dependencies {
val minecraftVersion: String by project
minecraft("com.mojang:minecraft:$minecraftVersion")
val yarnMappings: String by project
mappings("net.fabricmc:yarn:$yarnMappings:v2")
val USE_QUILT_MAPPINGS = false
if (USE_QUILT_MAPPINGS) {
// Seems to crash on startup right now
val quiltMappings: String by project
mappings(loom.layered {
officialMojangMappings {
nameSyntheticMembers = false
}
val qm = extensionOf(project, "quiltMappings") as QuiltMappingsOnLoomPlugin.QuiltMappingsOnLoomExtension
addLayer(qm.mappings("org.quiltmc:quilt-mappings:${quiltMappings}:v2"))
})
} else {
val yarnMappings: String by project
mappings("net.fabricmc:yarn:$yarnMappings:v2")
}
val loaderVersion: String by project
modImplementation("net.fabricmc:fabric-loader:$loaderVersion")
val fabricVersion: String by project
@ -49,7 +70,11 @@ tasks {
}
withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile> {
kotlinOptions { jvmTarget = javaVersion.toString() }
kotlinOptions {
jvmTarget = javaVersion.toString()
freeCompilerArgs = freeCompilerArgs + listOf("-Xopt-in=kotlin.RequiresOptIn")
}
sourceCompatibility = javaVersion.toString()
targetCompatibility = javaVersion.toString()
}

View file

@ -4,6 +4,7 @@ org.gradle.warning.mode=all
# Check these on https://fabricmc.net/develop/
minecraftVersion=1.18.1
yarnMappings=1.18.1+build.22
quiltMappings=1.18.1+build.6
loaderVersion=0.12.12
# Fabric API
fabricVersion=0.46.1+1.18

View file

@ -1,6 +1,7 @@
pluginManagement {
repositories {
maven("https://maven.fabricmc.net") { name = "Fabric" }
maven("https://maven.quiltmc.org/repository/release") { name = "Quilt" }
mavenCentral()
gradlePluginPortal()
}
@ -10,4 +11,4 @@ pluginManagement {
val kotlinVersion: String by System.getProperties()
kotlin("jvm").version(kotlinVersion)
}
}
}

View file

@ -0,0 +1,22 @@
package codes.som.hibiscus.mixins;
import codes.som.hibiscus.HibiscusMod;
import codes.som.hibiscus.events.SendPacketEvent;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.network.ClientConnection;
import net.minecraft.network.Packet;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Redirect;
@Mixin(ClientPlayNetworkHandler.class)
public abstract class MixinClientPlayNetworkHandler {
@Redirect(method = "sendPacket", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/ClientConnection;send(Lnet/minecraft/network/Packet;)V"))
public void onSendPacket(ClientConnection clientConnection, Packet<?> packet) {
var event = new SendPacketEvent(packet);
HibiscusMod.bus().fire(event);
if (!event.isCancelled()) {
clientConnection.send(event.getPacket());
}
}
}

View file

@ -0,0 +1,38 @@
package codes.som.hibiscus.mixins;
import net.minecraft.client.network.ClientPlayerEntity;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(ClientPlayerEntity.class)
public interface MixinExtClientPlayerEntity {
@Accessor
double getLastX();
@Accessor
void setLastX(double lastX);
@Accessor
double getLastBaseY();
@Accessor
void setLastBaseY(double lastBaseY);
@Accessor
double getLastZ();
@Accessor
void setLastZ(double lastZ);
@Accessor
float getLastYaw();
@Accessor
void setLastYaw(float lastYaw);
@Accessor
float getLastPitch();
@Accessor
void setLastPitch(float lastPitch);
}

View file

@ -0,0 +1,13 @@
package codes.som.hibiscus.mixins;
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(PlayerMoveC2SPacket.class)
public interface MixinExtPlayerMoveC2SPacket {
@Accessor
@Mutable
void setOnGround(boolean onGround);
}

View file

@ -0,0 +1,13 @@
package codes.som.hibiscus.mixins;
import net.minecraft.network.packet.c2s.play.UpdatePlayerAbilitiesC2SPacket;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor;
@Mixin(UpdatePlayerAbilitiesC2SPacket.class)
public interface MixinExtUpdatePlayerAbilitiesC2SPacket {
@Accessor
@Mutable
void setFlying(boolean flying);
}

View file

@ -0,0 +1,45 @@
package codes.som.hibiscus.mixins;
import codes.som.hibiscus.HibiscusMod;
import codes.som.hibiscus.events.RenderOverlayEvent;
import codes.som.hibiscus.gui.ImGuiRenderer;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.RunArgs;
import net.minecraft.client.render.RenderTickCounter;
import org.spongepowered.asm.mixin.Final;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
@Mixin(MinecraftClient.class)
public abstract class MixinMinecraftClient {
@Shadow
private volatile boolean paused;
@Shadow
private float pausedTickDelta;
@Shadow
@Final
private RenderTickCounter renderTickCounter;
@Inject(method = "<init>", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gui/screen/SplashOverlay;init(Lnet/minecraft/client/MinecraftClient;)V", shift = At.Shift.BEFORE))
private void onFinishingStartup(RunArgs args, CallbackInfo ci) {
ImGuiRenderer.INSTANCE.setup();
}
@Inject(method = "render", at = @At(value = "FIELD", target = "Lnet/minecraft/client/MinecraftClient;skipGameRender:Z", shift = At.Shift.BEFORE))
private void onPreRenderEverything(boolean tick, CallbackInfo ci) {
float delta = this.paused ? this.pausedTickDelta : this.renderTickCounter.tickDelta;
ImGuiRenderer.INSTANCE.beginFrame(delta);
}
@Inject(method = "render", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/gl/Framebuffer;endWrite()V", shift = At.Shift.BEFORE))
private void onPostRenderEverything(boolean tick, CallbackInfo ci) {
float delta = this.paused ? this.pausedTickDelta : this.renderTickCounter.tickDelta;
HibiscusMod.bus().fire(new RenderOverlayEvent(delta));
ImGuiRenderer.INSTANCE.finishFrame(delta);
}
}

View file

@ -1,19 +1,26 @@
package site.hackery.unknit;
import com.mojang.authlib.*;
import com.mojang.authlib.exceptions.*;
import com.mojang.authlib.yggdrasil.*;
import com.mojang.util.*;
import net.minecraft.client.main.*;
import net.minecraft.client.util.*;
import org.spongepowered.asm.mixin.*;
import org.spongepowered.asm.mixin.injection.*;
import com.mojang.authlib.Agent;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.exceptions.AuthenticationException;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import com.mojang.authlib.yggdrasil.YggdrasilUserAuthentication;
import com.mojang.util.UUIDTypeAdapter;
import net.minecraft.client.main.Main;
import net.minecraft.client.util.Session;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.ModifyVariable;
import java.io.*;
import java.net.*;
import java.nio.file.*;
import java.util.*;
import java.io.IOException;
import java.net.Proxy;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
@Mixin(Main.class)
public abstract class MixinMinecraftMain {
@ -24,7 +31,7 @@ public abstract class MixinMinecraftMain {
}
}
@ModifyVariable(method = "main", at = @At("HEAD"), argsOnly = true)
@ModifyVariable(method = "main", at = @At("HEAD"), argsOnly = true, remap = false)
private static String[] setArgs(String[] args) {
List<String> arguments = new ArrayList<>(Arrays.asList(args));
replace(arguments, "--version", "1.18.1");

View file

@ -2,7 +2,9 @@ package codes.som.hibiscus
import codes.som.hibiscus.api.event.EventBus
import codes.som.hibiscus.events.KeyEvent
import codes.som.hibiscus.features.FeaturesRegistry
import codes.som.hibiscus.gui.ImGuiScreen
import codes.som.hibiscus.util.netmoving.NetworkMovingDispatcher
import net.fabricmc.api.ModInitializer
import org.lwjgl.glfw.GLFW
import org.lwjgl.glfw.GLFW.GLFW_KEY_RIGHT_SHIFT
@ -15,9 +17,15 @@ object HibiscusMod : ModInitializer {
@get:JvmName("bus")
val bus = EventBus()
val features = FeaturesRegistry()
override fun onInitialize() {
bus.register { key: KeyEvent ->
if (key.key != GLFW_KEY_RIGHT_SHIFT || key.action != GLFW.GLFW_PRESS)
bus.register(NetworkMovingDispatcher())
bus.register { event: KeyEvent ->
if (event.key != GLFW_KEY_RIGHT_SHIFT || event.action != GLFW.GLFW_PRESS)
return@register
if (mc.currentScreen != null)
return@register
mc.setScreen(ImGuiScreen)

View file

@ -1,5 +1,9 @@
package codes.som.hibiscus
import net.minecraft.client.MinecraftClient
import net.minecraft.client.network.ClientPlayerEntity
import net.minecraft.client.world.ClientWorld
val mc: MinecraftClient inline get() = MinecraftClient.getInstance()
val player: ClientPlayerEntity inline get() = mc.player!!
val world: ClientWorld inline get() = mc.world!!

View file

@ -0,0 +1,10 @@
package codes.som.hibiscus.api.event
abstract class Cancellable {
@get:JvmName("isCancelled")
var cancelled: Boolean = false
fun cancel() {
cancelled = true
}
}

View file

@ -0,0 +1,13 @@
@file:Suppress("NOTHING_TO_INLINE")
package codes.som.hibiscus.api.event
abstract class TypedListener<T : Event>(val eventType: Class<T>) : Listener<T>
inline fun <T : Event> EventBus.registerTyped(typedListener: TypedListener<T>) {
register(typedListener.eventType, typedListener)
}
inline fun <T : Event> EventBus.unregisterTyped(typedListener: TypedListener<T>) {
unregister(typedListener.eventType, typedListener)
}

View file

@ -0,0 +1,37 @@
package codes.som.hibiscus.api.feature
import codes.som.hibiscus.HibiscusMod
import codes.som.hibiscus.api.event.*
import codes.som.hibiscus.api.feature.values.ValueRegistry
abstract class Feature(val name: String, val category: FeatureCategory) {
private val listeners = mutableListOf<TypedListener<*>>()
protected inline fun <reified T : Event> on(listener: Listener<T>) =
on(object : TypedListener<T>(T::class.java), Listener<T> by listener {})
protected fun <T : Event> on(listener: TypedListener<T>) =
listeners.add(listener)
val values = ValueRegistry()
var enabled: Boolean = false
set(value) {
val hasChanged = value != field
if (hasChanged) {
field = value
if (value) {
onEnable()
listeners.forEach { HibiscusMod.bus.registerTyped(it) }
} else {
listeners.forEach { HibiscusMod.bus.unregisterTyped(it) }
onDisable()
}
}
}
open fun onEnable() {}
open fun onDisable() {}
// TODO: Module commands
}

View file

@ -0,0 +1,6 @@
package codes.som.hibiscus.api.feature
enum class FeatureCategory(val humanName: String) {
PLAYER("Player"),
MOVEMENT("Movement"),
}

View file

@ -0,0 +1,11 @@
package codes.som.hibiscus.api.feature.values
import codes.som.hibiscus.gui.ImGuiKt
class BooleanValue(name: String, value: Boolean) : RegisteredValue<Boolean>(name, value) {
override fun convertValueFromString(representation: String) = representation.toBoolean()
override fun drawUIControl() {
ImGuiKt.checkbox(name, ::value)
}
}

View file

@ -0,0 +1,23 @@
package codes.som.hibiscus.api.feature.values
import codes.som.hibiscus.gui.ImGuiKt
class FloatValue(
name: String, value: Float,
val min: Float = Float.MIN_VALUE, val max: Float = Float.MAX_VALUE
) : RegisteredValue<Float>(name, value) {
override fun valueChangeHook(orig: Float, new: Float) =
new.coerceIn(min, max)
override fun convertValueFromString(representation: String): Float {
return representation.toFloat()
}
override fun drawUIControl() {
if (min == Float.MIN_VALUE || max == Float.MAX_VALUE) {
ImGuiKt.dragFloat(name, ::value, 1f, min, max)
} else {
ImGuiKt.sliderFloat(name, ::value, min, max)
}
}
}

View file

@ -0,0 +1,23 @@
package codes.som.hibiscus.api.feature.values
import codes.som.hibiscus.gui.ImGuiKt
class IntegerValue(
name: String, value: Int,
val min: Int = Int.MIN_VALUE, val max: Int = Int.MAX_VALUE
) : RegisteredValue<Int>(name, value) {
override fun valueChangeHook(orig: Int, new: Int) =
new.coerceIn(min, max)
override fun convertValueFromString(representation: String): Int {
return representation.toInt()
}
override fun drawUIControl() {
if (min == Int.MIN_VALUE || max == Int.MAX_VALUE) {
ImGuiKt.dragInt(name, ::value, 1f, min.toFloat(), max.toFloat())
} else {
ImGuiKt.sliderInt(name, ::value, min, max)
}
}
}

View file

@ -0,0 +1,18 @@
package codes.som.hibiscus.api.feature.values
import codes.som.hibiscus.util.input.Keys
import org.lwjgl.glfw.GLFW
class KeyboardValue(name: String, value: Int = GLFW.GLFW_KEY_UNKNOWN) : RegisteredValue<Int>(name, value) {
override fun convertValueFromString(representation: String): Int {
return Keys.KEY_MAP[representation.toUpperCase()] ?: -1
}
override fun getValueAsString(): String {
return Keys.KEY_MAP.inverse()[value] ?: "UNKNOWN"
}
override fun drawUIControl() {
// TODO
}
}

View file

@ -0,0 +1,25 @@
package codes.som.hibiscus.api.feature.values
import kotlin.reflect.KProperty
abstract class RegisteredValue<T>(val name: String, value: T) {
var value: T = value
set(value) {
field = valueChangeHook(field, value)
}
open fun valueChangeHook(orig: T, new: T): T = new
open fun getValueAsString(): String = value.toString()
abstract fun convertValueFromString(representation: String): T
fun setValueFromString(representation: String) {
value = convertValueFromString(representation)
}
abstract fun drawUIControl()
operator fun getValue(thisRef: Any?, property: KProperty<*>): T = value
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
this.value = value
}
}

View file

@ -0,0 +1,21 @@
package codes.som.hibiscus.api.feature.values
import imgui.ImGui
import imgui.type.ImString
class StringValue(name: String, value: String) : RegisteredValue<String>(name, value) {
override fun convertValueFromString(representation: String) = representation
private val uiState = ImString(256)
private var lastValue = value
override fun drawUIControl() {
if (value != lastValue) {
uiState.set(value)
}
ImGui.inputText(name, uiState)
value = uiState.get()
lastValue = value
}
}

View file

@ -0,0 +1,29 @@
package codes.som.hibiscus.api.feature.values
import org.lwjgl.glfw.GLFW
class ValueRegistry {
private val registeredValues = mutableListOf<RegisteredValue<*>>()
fun register(value: RegisteredValue<*>) {
registeredValues.add(value)
}
fun exist() = registeredValues.isNotEmpty()
operator fun iterator() = registeredValues.iterator()
fun string(name: String, default: String) =
StringValue(name, default).also(this::register)
fun bool(name: String, default: Boolean) =
BooleanValue(name, default).also(this::register)
fun int(name: String, default: Int, min: Int = Int.MIN_VALUE, max: Int = Int.MAX_VALUE) =
IntegerValue(name, default, min, max).also(this::register)
fun key(name: String, default: Int = GLFW.GLFW_KEY_UNKNOWN) =
KeyboardValue(name, default).also(this::register)
fun float(name: String, default: Float, min: Float = Float.MIN_VALUE, max: Float = Float.MAX_VALUE) =
FloatValue(name, default, min, max).also(this::register)
}

View file

@ -0,0 +1,15 @@
package codes.som.hibiscus.events
import codes.som.hibiscus.api.event.Cancellable
import codes.som.hibiscus.api.event.Event
import net.minecraft.network.Packet
data class SendPacketEvent(var packet: Packet<*>) : Cancellable(), Event
class NetworkMovingEvent(
var x: Double,
var y: Double,
var z: Double,
var yaw: Float,
var pitch: Float,
var onGround: Boolean
) : Cancellable(), Event

View file

@ -0,0 +1,5 @@
package codes.som.hibiscus.events
import codes.som.hibiscus.api.event.Event
class RenderOverlayEvent(val delta: Float) : Event

View file

@ -0,0 +1,10 @@
package codes.som.hibiscus.features
import codes.som.hibiscus.api.feature.Feature
import codes.som.hibiscus.features.movement.Flight
import codes.som.hibiscus.features.player.NoFallDamage
val ALL_FEATURES: Array<() -> Feature> = arrayOf(
::NoFallDamage,
::Flight,
)

View file

@ -0,0 +1,7 @@
package codes.som.hibiscus.features
class FeaturesRegistry {
private val features = ALL_FEATURES.map { it() }
fun getAllFeatures() = features.asSequence()
}

View file

@ -0,0 +1,79 @@
package codes.som.hibiscus.features.movement
import codes.som.hibiscus.api.feature.Feature
import codes.som.hibiscus.api.feature.FeatureCategory
import codes.som.hibiscus.events.NetworkMovingEvent
import codes.som.hibiscus.events.PlayerTickEvent
import codes.som.hibiscus.events.SendPacketEvent
import codes.som.hibiscus.mc
import codes.som.hibiscus.mixins.MixinExtUpdatePlayerAbilitiesC2SPacket
import codes.som.hibiscus.player
import codes.som.hibiscus.util.ext.requireExtension
import codes.som.hibiscus.world
import net.minecraft.network.packet.c2s.play.ClientCommandC2SPacket
import net.minecraft.network.packet.c2s.play.UpdatePlayerAbilitiesC2SPacket
import net.minecraft.util.shape.VoxelShape
class Flight : Feature("Flight", FeatureCategory.MOVEMENT) {
private var flyKickCounter = 0
private var lockY = 0.0
init {
on { _: PlayerTickEvent ->
player.abilities.flying = true
}
val interceptServerboundAbilityPackets by values.bool("Intercept Serverbound Ability Packets", true)
val vanillaKickBypass by values.bool("Vanilla Kick Bypass", false)
on { event: SendPacketEvent ->
val (packet) = event
if (!interceptServerboundAbilityPackets)
return@on
if (packet is UpdatePlayerAbilitiesC2SPacket) {
requireExtension<MixinExtUpdatePlayerAbilitiesC2SPacket>(packet)
packet.setFlying(player.abilities.allowFlying)
}
if (packet is ClientCommandC2SPacket) {
if (packet.mode == ClientCommandC2SPacket.Mode.PRESS_SHIFT_KEY) {
event.cancel()
}
}
}
on { event: NetworkMovingEvent ->
if (!vanillaKickBypass)
return@on
flyKickCounter++
if (flyKickCounter >= 16) {
if (flyKickCounter == 16)
lockY = event.y - 0.03126
if (world.getBlockCollisions(
null,
player.boundingBox.offset(0.0, lockY - player.y, 0.0).expand(0.0625).stretch(0.0, -0.55, 0.0)
).all(VoxelShape::isEmpty)
) {
event.y = lockY
}
}
if (flyKickCounter >= 24)
flyKickCounter = 0
}
}
override fun onEnable() {
if (mc.player != null)
lockY = player.y - 0.03126
}
override fun onDisable() {
player.abilities.flying = false
}
}

View file

@ -0,0 +1,19 @@
package codes.som.hibiscus.features.player
import codes.som.hibiscus.api.feature.Feature
import codes.som.hibiscus.api.feature.FeatureCategory
import codes.som.hibiscus.events.SendPacketEvent
import codes.som.hibiscus.mixins.MixinExtPlayerMoveC2SPacket
import codes.som.hibiscus.util.ext.requireExtension
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket
class NoFallDamage : Feature("No Fall Damage", FeatureCategory.PLAYER) {
init {
on { (packet): SendPacketEvent ->
if (packet is PlayerMoveC2SPacket) {
requireExtension<MixinExtPlayerMoveC2SPacket>(packet)
packet.setOnGround(true)
}
}
}
}

View file

@ -0,0 +1,37 @@
package codes.som.hibiscus.gui
import imgui.ImGui
import imgui.type.ImBoolean
import kotlin.reflect.KMutableProperty
object ImGuiKt {
fun checkbox(label: String, prop: KMutableProperty<Boolean>) {
val imB = ImBoolean(prop.call())
ImGui.checkbox(label, imB)
prop.setter.call(imB.get())
}
fun dragFloat(label: String, prop: KMutableProperty<Float>, speed: Float, min: Float, max: Float) {
val arr = floatArrayOf(prop.call())
ImGui.dragFloat(label, arr, speed, min, max)
prop.setter.call(arr[0])
}
fun sliderFloat(label: String, prop: KMutableProperty<Float>, min: Float, max: Float) {
val arr = floatArrayOf(prop.call())
ImGui.sliderFloat(label, arr, min, max)
prop.setter.call(arr[0])
}
fun dragInt(label: String, prop: KMutableProperty<Int>, speed: Float, min: Float, max: Float) {
val arr = intArrayOf(prop.call())
ImGui.dragInt(label, arr, speed, min, max)
prop.setter.call(arr[0])
}
fun sliderInt(label: String, prop: KMutableProperty<Int>, min: Int, max: Int) {
val arr = intArrayOf(prop.call())
ImGui.sliderInt(label, arr, min, max)
prop.setter.call(arr[0])
}
}

View file

@ -0,0 +1,47 @@
package codes.som.hibiscus.gui
import codes.som.hibiscus.mc
import imgui.ImGui
import imgui.flag.ImGuiConfigFlags
import imgui.gl3.ImGuiImplGl3
import imgui.glfw.ImGuiImplGlfw
import org.lwjgl.glfw.GLFW
object ImGuiRenderer {
private val imGuiGlfw = ImGuiImplGlfw()
private val imGuiGl3 = ImGuiImplGl3()
fun setup() {
ImGui.createContext()
with(ImGui.getIO()) {
iniFilename = null // We don't want to save .ini file
addConfigFlags(ImGuiConfigFlags.NavEnableKeyboard) // Enable Keyboard Controls
addConfigFlags(ImGuiConfigFlags.DockingEnable) // Enable Docking
addConfigFlags(ImGuiConfigFlags.ViewportsEnable) // Enable Multi-Viewport / Platform Windows
configViewportsNoTaskBarIcon = true
}
applyHibiscusImGuiTheme()
imGuiGlfw.init(mc.window.handle, true)
imGuiGl3.init()
}
fun beginFrame(delta: Float) {
imGuiGlfw.newFrame()
ImGui.newFrame()
}
fun finishFrame(delta: Float) {
ImGui.render()
imGuiGl3.renderDrawData(ImGui.getDrawData())
if (ImGui.getIO().hasConfigFlags(ImGuiConfigFlags.ViewportsEnable)) {
val backupWindowPtr = GLFW.glfwGetCurrentContext()
ImGui.updatePlatformWindows()
ImGui.renderPlatformWindowsDefault()
GLFW.glfwMakeContextCurrent(backupWindowPtr)
}
}
}

View file

@ -1,47 +1,14 @@
package codes.som.hibiscus.gui
import codes.som.hibiscus.mc
import imgui.ImGui
import imgui.flag.ImGuiConfigFlags
import imgui.gl3.ImGuiImplGl3
import imgui.glfw.ImGuiImplGlfw
import net.minecraft.client.gui.screen.Screen
import net.minecraft.client.util.math.MatrixStack
import net.minecraft.text.Text
import org.lwjgl.glfw.GLFW
object ImGuiScreen : Screen(Text.of("hacker_menu")) {
private val imGuiGlfw = ImGuiImplGlfw()
private val imGuiGl3 = ImGuiImplGl3()
init {
ImGui.createContext()
applyHibiscusImGuiTheme()
imGuiGlfw.init(mc.window.handle, true)
imGuiGl3.init()
}
object ImGuiScreen : Screen(Text.of("hacker menu")) {
override fun render(matrices: MatrixStack?, mouseX: Int, mouseY: Int, delta: Float) {
super.render(matrices, mouseX, mouseY, delta)
imGuiGlfw.newFrame()
ImGui.newFrame()
ImGui.begin("hello")
ImGui.text("you just got hacked by didi @ digitalgangster")
ImGui.end()
ImGui.render()
imGuiGl3.renderDrawData(ImGui.getDrawData())
if (ImGui.getIO().hasConfigFlags(ImGuiConfigFlags.ViewportsEnable)) {
val backupWindowPtr = GLFW.glfwGetCurrentContext()
ImGui.updatePlatformWindows()
ImGui.renderPlatformWindowsDefault()
GLFW.glfwMakeContextCurrent(backupWindowPtr)
}
ModuleControlsUI.render(delta)
}
override fun shouldPause() = false

View file

@ -0,0 +1,58 @@
package codes.som.hibiscus.gui
import codes.som.hibiscus.HibiscusMod
import codes.som.hibiscus.api.feature.Feature
import codes.som.hibiscus.api.feature.FeatureCategory
import imgui.ImGui
import imgui.flag.ImGuiCond.FirstUseEver
import imgui.flag.ImGuiWindowFlags
import imgui.flag.ImGuiWindowFlags.AlwaysVerticalScrollbar
object ModuleControlsUI {
private val moduleValueWindows = mutableMapOf<Feature, BooleanArray>()
private fun categoryPanel(category: FeatureCategory, initialX: Float, initialY: Float) {
ImGui.setNextWindowPos(initialX, initialY, FirstUseEver)
ImGui.setNextWindowCollapsed(true, FirstUseEver)
ImGui.begin(category.humanName, AlwaysVerticalScrollbar)
for (feature in HibiscusMod.features.getAllFeatures().filter { it.category == category }) {
featureControls(feature)
}
ImGui.end()
}
private fun featureControls(feature: Feature) {
ImGuiKt.checkbox(feature.name, feature::enabled)
if (feature.values.exist()) {
val showValueWindow = moduleValueWindows.getOrPut(feature) { booleanArrayOf(false) }
ImGui.sameLine(ImGui.getWindowContentRegionMaxX() - ImGui.getWindowContentRegionMinX() - 10)
if (ImGui.radioButton("##${feature.name} values", showValueWindow[0])) {
showValueWindow[0] = !showValueWindow[0]
}
if (showValueWindow[0]) {
if (ImGui.begin("${feature.name}##Feature Controls", ImGuiWindowFlags.NoCollapse)) {
for (value in feature.values)
value.drawUIControl()
}
ImGui.end()
}
}
}
fun render(delta: Float) {
for ((index, category) in FeatureCategory.values().withIndex()) {
categoryPanel(
category,
ImGui.getMainViewport().posX + 10f,
ImGui.getMainViewport().posY + 10f + index * 20f
)
}
}
}

View file

@ -0,0 +1,14 @@
package codes.som.hibiscus.util.ext
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.contract
@OptIn(ExperimentalContracts::class)
inline fun <reified T : Any> requireExtension(obj: Any) {
contract {
returns() implies (obj is T)
}
if (obj !is T)
error("Missing mixin extension ${T::class.java.name} for ${obj.javaClass.name}")
}

View file

@ -0,0 +1,155 @@
package codes.som.hibiscus.util.input
import codes.som.hibiscus.mc
import com.google.common.collect.BiMap
import com.google.common.collect.ImmutableBiMap
import com.google.common.collect.ImmutableSortedSet
import net.minecraft.client.util.InputUtil
import org.lwjgl.glfw.GLFW
object Keys {
val KEY_MAP: BiMap<String, Int> = ImmutableBiMap.builder<String, Int>()
.put("SPACE", GLFW.GLFW_KEY_SPACE)
.put("APOSTROPHE", GLFW.GLFW_KEY_APOSTROPHE)
.put("COMMA", GLFW.GLFW_KEY_COMMA)
.put("MINUS", GLFW.GLFW_KEY_MINUS)
.put("PERIOD", GLFW.GLFW_KEY_PERIOD)
.put("SLASH", GLFW.GLFW_KEY_SLASH)
.put("0", GLFW.GLFW_KEY_0)
.put("1", GLFW.GLFW_KEY_1)
.put("2", GLFW.GLFW_KEY_2)
.put("3", GLFW.GLFW_KEY_3)
.put("4", GLFW.GLFW_KEY_4)
.put("5", GLFW.GLFW_KEY_5)
.put("6", GLFW.GLFW_KEY_6)
.put("7", GLFW.GLFW_KEY_7)
.put("8", GLFW.GLFW_KEY_8)
.put("9", GLFW.GLFW_KEY_9)
.put("SEMICOLON", GLFW.GLFW_KEY_SEMICOLON)
.put("EQUAL", GLFW.GLFW_KEY_EQUAL)
.put("A", GLFW.GLFW_KEY_A)
.put("B", GLFW.GLFW_KEY_B)
.put("C", GLFW.GLFW_KEY_C)
.put("D", GLFW.GLFW_KEY_D)
.put("E", GLFW.GLFW_KEY_E)
.put("F", GLFW.GLFW_KEY_F)
.put("G", GLFW.GLFW_KEY_G)
.put("H", GLFW.GLFW_KEY_H)
.put("I", GLFW.GLFW_KEY_I)
.put("J", GLFW.GLFW_KEY_J)
.put("K", GLFW.GLFW_KEY_K)
.put("L", GLFW.GLFW_KEY_L)
.put("M", GLFW.GLFW_KEY_M)
.put("N", GLFW.GLFW_KEY_N)
.put("O", GLFW.GLFW_KEY_O)
.put("P", GLFW.GLFW_KEY_P)
.put("Q", GLFW.GLFW_KEY_Q)
.put("R", GLFW.GLFW_KEY_R)
.put("S", GLFW.GLFW_KEY_S)
.put("T", GLFW.GLFW_KEY_T)
.put("U", GLFW.GLFW_KEY_U)
.put("V", GLFW.GLFW_KEY_V)
.put("W", GLFW.GLFW_KEY_W)
.put("X", GLFW.GLFW_KEY_X)
.put("Y", GLFW.GLFW_KEY_Y)
.put("Z", GLFW.GLFW_KEY_Z)
.put("LEFT_BRACKET", GLFW.GLFW_KEY_LEFT_BRACKET)
.put("BACKSLASH", GLFW.GLFW_KEY_BACKSLASH)
.put("RIGHT_BRACKET", GLFW.GLFW_KEY_RIGHT_BRACKET)
.put("GRAVE_ACCENT", GLFW.GLFW_KEY_GRAVE_ACCENT)
.put("WORLD_1", GLFW.GLFW_KEY_WORLD_1)
.put("WORLD_2", GLFW.GLFW_KEY_WORLD_2)
.put("ESCAPE", GLFW.GLFW_KEY_ESCAPE)
.put("ENTER", GLFW.GLFW_KEY_ENTER)
.put("TAB", GLFW.GLFW_KEY_TAB)
.put("BACKSPACE", GLFW.GLFW_KEY_BACKSPACE)
.put("INSERT", GLFW.GLFW_KEY_INSERT)
.put("DELETE", GLFW.GLFW_KEY_DELETE)
.put("RIGHT", GLFW.GLFW_KEY_RIGHT)
.put("LEFT", GLFW.GLFW_KEY_LEFT)
.put("DOWN", GLFW.GLFW_KEY_DOWN)
.put("UP", GLFW.GLFW_KEY_UP)
.put("PAGE_UP", GLFW.GLFW_KEY_PAGE_UP)
.put("PAGE_DOWN", GLFW.GLFW_KEY_PAGE_DOWN)
.put("HOME", GLFW.GLFW_KEY_HOME)
.put("END", GLFW.GLFW_KEY_END)
.put("CAPS_LOCK", GLFW.GLFW_KEY_CAPS_LOCK)
.put("SCROLL_LOCK", GLFW.GLFW_KEY_SCROLL_LOCK)
.put("NUM_LOCK", GLFW.GLFW_KEY_NUM_LOCK)
.put("PRINT_SCREEN", GLFW.GLFW_KEY_PRINT_SCREEN)
.put("PAUSE", GLFW.GLFW_KEY_PAUSE)
.put("F1", GLFW.GLFW_KEY_F1)
.put("F2", GLFW.GLFW_KEY_F2)
.put("F3", GLFW.GLFW_KEY_F3)
.put("F4", GLFW.GLFW_KEY_F4)
.put("F5", GLFW.GLFW_KEY_F5)
.put("F6", GLFW.GLFW_KEY_F6)
.put("F7", GLFW.GLFW_KEY_F7)
.put("F8", GLFW.GLFW_KEY_F8)
.put("F9", GLFW.GLFW_KEY_F9)
.put("F10", GLFW.GLFW_KEY_F10)
.put("F11", GLFW.GLFW_KEY_F11)
.put("F12", GLFW.GLFW_KEY_F12)
.put("F13", GLFW.GLFW_KEY_F13)
.put("F14", GLFW.GLFW_KEY_F14)
.put("F15", GLFW.GLFW_KEY_F15)
.put("F16", GLFW.GLFW_KEY_F16)
.put("F17", GLFW.GLFW_KEY_F17)
.put("F18", GLFW.GLFW_KEY_F18)
.put("F19", GLFW.GLFW_KEY_F19)
.put("F20", GLFW.GLFW_KEY_F20)
.put("F21", GLFW.GLFW_KEY_F21)
.put("F22", GLFW.GLFW_KEY_F22)
.put("F23", GLFW.GLFW_KEY_F23)
.put("F24", GLFW.GLFW_KEY_F24)
.put("F25", GLFW.GLFW_KEY_F25)
.put("KP_0", GLFW.GLFW_KEY_KP_0)
.put("KP_1", GLFW.GLFW_KEY_KP_1)
.put("KP_2", GLFW.GLFW_KEY_KP_2)
.put("KP_3", GLFW.GLFW_KEY_KP_3)
.put("KP_4", GLFW.GLFW_KEY_KP_4)
.put("KP_5", GLFW.GLFW_KEY_KP_5)
.put("KP_6", GLFW.GLFW_KEY_KP_6)
.put("KP_7", GLFW.GLFW_KEY_KP_7)
.put("KP_8", GLFW.GLFW_KEY_KP_8)
.put("KP_9", GLFW.GLFW_KEY_KP_9)
.put("KP_DECIMAL", GLFW.GLFW_KEY_KP_DECIMAL)
.put("KP_DIVIDE", GLFW.GLFW_KEY_KP_DIVIDE)
.put("KP_MULTIPLY", GLFW.GLFW_KEY_KP_MULTIPLY)
.put("KP_SUBTRACT", GLFW.GLFW_KEY_KP_SUBTRACT)
.put("KP_ADD", GLFW.GLFW_KEY_KP_ADD)
.put("KP_ENTER", GLFW.GLFW_KEY_KP_ENTER)
.put("KP_EQUAL", GLFW.GLFW_KEY_KP_EQUAL)
.put("LEFT_SHIFT", GLFW.GLFW_KEY_LEFT_SHIFT)
.put("LEFT_CONTROL", GLFW.GLFW_KEY_LEFT_CONTROL)
.put("LEFT_ALT", GLFW.GLFW_KEY_LEFT_ALT)
.put("LEFT_SUPER", GLFW.GLFW_KEY_LEFT_SUPER)
.put("RIGHT_SHIFT", GLFW.GLFW_KEY_RIGHT_SHIFT)
.put("RIGHT_CONTROL", GLFW.GLFW_KEY_RIGHT_CONTROL)
.put("RIGHT_ALT", GLFW.GLFW_KEY_RIGHT_ALT)
.put("RIGHT_SUPER", GLFW.GLFW_KEY_RIGHT_SUPER)
.put("MENU", GLFW.GLFW_KEY_MENU)
.put("NONE", GLFW.GLFW_KEY_UNKNOWN)
.build()
val KEY_PAD: Set<Int> = ImmutableSortedSet.of(
GLFW.GLFW_KEY_KP_0,
GLFW.GLFW_KEY_KP_1,
GLFW.GLFW_KEY_KP_2,
GLFW.GLFW_KEY_KP_3,
GLFW.GLFW_KEY_KP_4,
GLFW.GLFW_KEY_KP_5,
GLFW.GLFW_KEY_KP_6,
GLFW.GLFW_KEY_KP_7,
GLFW.GLFW_KEY_KP_8,
GLFW.GLFW_KEY_KP_9
)
fun pressed(key: Int): Boolean {
if (key == GLFW.GLFW_KEY_UNKNOWN)
return false
return InputUtil.isKeyPressed(mc.window.handle, key)
}
}

View file

@ -0,0 +1,64 @@
package codes.som.hibiscus.util.netmoving
import codes.som.hibiscus.HibiscusMod
import codes.som.hibiscus.api.event.TypedListener
import codes.som.hibiscus.events.NetworkMovingEvent
import codes.som.hibiscus.events.SendPacketEvent
import codes.som.hibiscus.mc
import codes.som.hibiscus.mixins.MixinExtClientPlayerEntity
import codes.som.hibiscus.util.ext.requireExtension
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket
class NetworkMovingDispatcher : TypedListener<SendPacketEvent>(SendPacketEvent::class.java) {
override fun on(event: SendPacketEvent) {
val packet = event.packet
if (packet is PlayerMoveC2SPacket) {
val player = mc.player!!
requireExtension<MixinExtClientPlayerEntity>(player)
val movingEvent = NetworkMovingEvent(
packet.getX(player.x), packet.getY(player.y), packet.getZ(player.z),
packet.getYaw(player.yaw), packet.getPitch(player.pitch), packet.isOnGround
)
HibiscusMod.bus.fire(movingEvent)
if (movingEvent.cancelled)
event.cancel()
val moving =
movingEvent.x != player.lastX || movingEvent.y != player.lastBaseY || movingEvent.z != player.lastZ
val rotating = movingEvent.yaw != player.lastYaw || movingEvent.pitch != player.lastPitch
player.lastX = movingEvent.x
player.lastBaseY = movingEvent.y
player.lastZ = movingEvent.z
player.lastYaw = movingEvent.yaw
player.lastPitch = movingEvent.pitch
event.packet = when {
moving && rotating -> PlayerMoveC2SPacket.Full(
movingEvent.x,
movingEvent.y,
movingEvent.z,
movingEvent.yaw,
movingEvent.pitch,
movingEvent.onGround
)
moving -> PlayerMoveC2SPacket.PositionAndOnGround(
movingEvent.x,
movingEvent.y,
movingEvent.z,
movingEvent.onGround
)
rotating -> PlayerMoveC2SPacket.LookAndOnGround(
movingEvent.yaw,
movingEvent.pitch,
movingEvent.onGround
)
else -> PlayerMoveC2SPacket.OnGroundOnly(movingEvent.onGround)
}
}
}
}

View file

@ -6,9 +6,12 @@
"defaultRequire": 1
},
"mixins": [
],
"client": [
"MixinClientPlayerEntity",
"MixinKeyboard"
"MixinClientPlayNetworkHandler",
"MixinExtClientPlayerEntity",
"MixinExtPlayerMoveC2SPacket",
"MixinExtUpdatePlayerAbilitiesC2SPacket",
"MixinKeyboard",
"MixinMinecraftClient"
]
}