transactions.zip Also looking forward to our marketplace, I do know that some development happened on the branch recently, hope to see it soon.
commandblock2
Posts
-
[GPLv3] transaction.js -
Typescript definition for minecraft and LiquidBounceUpdate for LiquidBounce v0.29.0:
What's new:- refined disclaimer in README.md
localStorage
in definition (no java.utils class definition yet)multiChoose
in manually maintained patch
-
[GPLv3] transaction.jsit was kinda weird, I never see any value starting maybe with -32768 or around, it's always that giant number, is it VFP or just everyone's using the same ac? Btw I even see some server like sending 300ish transactions per second and wtf is that.
-
Help with setting visibilityBy reading your post again I think I need to use ChoiceConfigurable. And I think that's definitely possible.
-
Help with setting visibilityAlso I am not sure about if we have to pass a object that implements all those functions to
Java.extend()
. But I think that's it for graaljs's type gymnastics, if you think this is necessary please open a issue at github. Ah just found that I forgot to use theReflectionUtils
xD. -
Help with setting visibility@Soulplexis It's done. Although not supposed to be implemented this way but it can be.
function __require(path) { if (path.startsWith("@embedded")) { return globalThis } if (path.startsWith("@minecraft-yarn-definitions/types/")) { return { [path.substring(path.lastIndexOf("/") + 1)]: Java.type(path .replaceAll("@minecraft-yarn-definitions/types/", "") .replaceAll("/", ".") ) } } return require(path); } var exports = {} "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); // imports /* eslint-disable unused-imports/no-unused-imports */ const _embedded_1 = __require("@embedded"); const ToggleableConfigurable_1 = __require("@minecraft-yarn-definitions/types/net/ccbluex/liquidbounce/config/types/ToggleableConfigurable"); /* eslint-enable unused-imports/no-unused-imports */ // DO NOT TOUCH ANYTHING ABOVE THIS LINE, also not sure why it didn't work const script = _embedded_1.registerScript.apply({ name: "example-script-api-hacking", version: "1.0.0", authors: ["commandblock2"] }); script.registerModule({ name: "example-typescript-module-script-api-hacking", description: "Ths is an minimal example module generated in ts but with a setting that's not supposed to be here", category: "Client", }, (mod) => { // Assuming you're in a JavaScript environment that supports Java.extend (like Nashorn or GraalVM) // @ts-expect-error const MyToggleableConfig = Java.extend(ToggleableConfigurable_1.ToggleableConfigurable, { // Implement abstract methods from ToggleableConfigurable and its parent classes // Required method implementations from EventListener interface parent: function () { return this.parent; // Return the parent passed in constructor }, children: function () { return []; // Return an array of child event listeners if any }, unregister: function () { // Implementation for unregistering this event listener }, // You can also override other methods like: enable: function () { // Custom enable logic // @ts-expect-error Java.super(this).enable(); // Call the parent method if needed }, disable: function () { // Custom disable logic // @ts-expect-error Java.super(this).disable(); // Call the parent method if needed } }); // Create an instance with required constructor parameters // constructor(parent: EventListener | null, name: string, enabled: boolean, aliases: string[]) const myConfig = new MyToggleableConfig(mod, "MyConfig", true, ["alias1", "alias2"]); const testBoolean = myConfig.boolean("testBoolean", false); _embedded_1.Client.displayChatMessage(testBoolean.toString()); // @ts-expect-error const field = mod.class.getDeclaredField("_values"); field.setAccessible(true); const valuesMap = field.get(mod); // @ts-expect-error valuesMap.put("MyConfig", mod.value(myConfig)); mod.on("enable", () => { _embedded_1.Client.displayChatMessage(`Hi, ${_embedded_1.mc.player}`); }); });
// imports /* eslint-disable unused-imports/no-unused-imports */ import { Setting, Vec3i, Vec3d, MathHelper, BlockPos, Hand, RotationAxis, mc, Client, RotationUtil, ItemUtil, NetworkUtil, InteractionUtil, BlockUtil, MovementUtil, ReflectionUtil, ParameterValidator, UnsafeThread, registerScript } from "@embedded"; import { Class } from "@minecraft-yarn-definitions/types/java/lang/Class"; import { ToggleableConfigurable } from "@minecraft-yarn-definitions/types/net/ccbluex/liquidbounce/config/types/ToggleableConfigurable"; import { ScriptModule } from "@minecraft-yarn-definitions/types/net/ccbluex/liquidbounce/script/bindings/features/ScriptModule"; /* eslint-enable unused-imports/no-unused-imports */ // DO NOT TOUCH ANYTHING ABOVE THIS LINE, also not sure why it didn't work const script = registerScript.apply({ name: "example-script-api-hacking", version: "1.0.0", authors: ["commandblock2"] }); script.registerModule({ name: "example-typescript-module-script-api-hacking", description: "Ths is an minimal example module generated in ts but with a setting that's not supposed to be here", category: "Client", }, (mod) => { // Assuming you're in a JavaScript environment that supports Java.extend (like Nashorn or GraalVM) // @ts-expect-error const MyToggleableConfig = Java.extend(ToggleableConfigurable, { // Implement abstract methods from ToggleableConfigurable and its parent classes // Required method implementations from EventListener interface parent: function () { return this.parent; // Return the parent passed in constructor }, children: function () { return []; // Return an array of child event listeners if any }, unregister: function () { // Implementation for unregistering this event listener }, // You can also override other methods like: enable: function () { // Custom enable logic // @ts-expect-error Java.super(this).enable(); // Call the parent method if needed }, disable: function () { // Custom disable logic // @ts-expect-error Java.super(this).disable(); // Call the parent method if needed } }); // Create an instance with required constructor parameters // constructor(parent: EventListener | null, name: string, enabled: boolean, aliases: string[]) const myConfig = new MyToggleableConfig(mod, "MyConfig", true, ["alias1", "alias2"]); const testBoolean = myConfig.boolean("testBoolean", false); Client.displayChatMessage(testBoolean.toString()); // @ts-expect-error const field = (mod.class as unknown as Class<ScriptModule>).getDeclaredField("_values"); field.setAccessible(true); const valuesMap = field.get(mod); // @ts-expect-error valuesMap.put("MyConfig", mod.value(myConfig)); mod.on("enable", () => { Client.displayChatMessage(`Hi, ${mc.player}`) }) })
-
Help with setting visibilityThis might be possible actually I think, if you put it in the lambda near mod.on that might even work, will try later
-
Help with setting visibilityBut on another thought, we could extend the value our self maybe and make the
ScriptToggleableConfigurable
our self within script? Maybe we could think about that.Also a bit of shameless self advertising, maybe try this typescript definition generator, could be a bit hard to setup and generate the definition yet.
-
Help with setting visibilityAfaik, it is not possible without significant change.
ScriptSetting
for now does not provide the togglableConfigurable value function yet. That's all they have right now.export class ScriptSetting extends Object { static INSTANCE: ScriptSetting; boolean(value: BooleanValue): Value<boolean>; choose(value: ChooseValue): ChooseListValue<NamedChoice>; float(value: FloatValue): RangedValue<number>; floatRange(value: FloatRangeValue): RangedValue<ClosedFloatingPointRange<number>>; int(value: IntValue): RangedValue<number>; intRange(value: IntRangeValue): RangedValue<number[]>; key(value: KeyValue): Value<InputUtil$Key>; text(value: TextView): Value<string>; textArray(value: TextArrayValue): Value<string[]>; }
even if you would construct a
ToggleableConfigurable
instance with the graal anonymous class/object syntax, theconstructor(parent: EventListener | null, name: string, enabled: boolean, aliases: string[])
would require aEventListener
, which is supplied bythis
in normal kotlin non-script modules. And thethis
in your script api corresponds to themod
in}, (mod) => {
in your last line given, which by design, is not possible to acquire.
However I do believe we could make a pr to the script api to make this possible, but I am not sure if a script is supposed to have such complex configuration. -
[GPLv3] transaction.jsfunction __require(path) { if (path.startsWith("@embedded")) { return globalThis } if (path.startsWith("@minecraft-yarn-definitions/types/")) { return { [path.substring(path.lastIndexOf("/") + 1)]: Java.type(path .replaceAll("@minecraft-yarn-definitions/types/", "") .replaceAll("/", ".") ) } } return require(path); } var exports = {} "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); // imports /* eslint-disable unused-imports/no-unused-imports */ const _embedded_1 = __require("@embedded"); const CommonPingS2CPacket_1 = __require("@minecraft-yarn-definitions/types/net/minecraft/network/packet/s2c/common/CommonPingS2CPacket"); // Global variables for storing transaction data const transactionQueue = []; const MAX_QUEUE_SIZE = 100; // Global statistics let totalTransactions = 0; let lastReportTime = 0; const REPORT_INTERVAL = 5000; // Report every 5 seconds const PARAMETER_FREQUENCY = {}; const script = _embedded_1.registerScript.apply({ name: "transactions", version: "1.0.0", authors: ["commandblock2"] }); script.registerModule({ name: "transactions", description: "Logs transactions packets for anticheat detection", category: "Client", settings: { reportInterval: _embedded_1.Setting.int({ name: "ReportInterval", default: 5, range: [1, 60], suffix: "seconds" }), showParameters: _embedded_1.Setting.boolean({ name: "ShowParameters", default: true }), logToChat: _embedded_1.Setting.boolean({ name: "LogToChat", default: true }), verbose: _embedded_1.Setting.boolean({ name: "Verbose", default: false }) } }, (mod) => { mod.on("enable", () => { // Reset statistics totalTransactions = 0; lastReportTime = Date.now(); // Clear the queue transactionQueue.length = 0; // Reset parameter frequency for (const key in PARAMETER_FREQUENCY) { delete PARAMETER_FREQUENCY[key]; } _embedded_1.Client.displayChatMessage("§a[Transactions] Monitoring started. Use .txstats to see statistics."); }); mod.on("disable", () => { _embedded_1.Client.displayChatMessage("§c[Transactions] Monitoring stopped."); }); mod.on("packet", (event) => { if (event.packet instanceof CommonPingS2CPacket_1.CommonPingS2CPacket) { const parameter = event.packet.getParameter(); const timestamp = Date.now(); if (mod.settings.verbose.getValue()) { _embedded_1.Client.displayChatMessage(`§a[Transactions] Received parameter: ${parameter}`); } // Add to queue transactionQueue.push({ timestamp, parameter }); // Keep queue size limited if (transactionQueue.length > MAX_QUEUE_SIZE) { transactionQueue.shift(); } // Update statistics totalTransactions++; PARAMETER_FREQUENCY[parameter] = (PARAMETER_FREQUENCY[parameter] || 0) + 1; // Check if it's time to report const currentTime = Date.now(); const reportIntervalMs = mod.settings.reportInterval.getValue() * 1000; if (mod.settings.logToChat.getValue() && currentTime - lastReportTime >= reportIntervalMs) { reportStatistics(); lastReportTime = currentTime; } } }); // Function to report statistics function reportStatistics() { if (transactionQueue.length === 0) { return; } const packetsPerSecond = calculatePacketsPerSecond(); _embedded_1.Client.displayChatMessage(`§e[Transactions] §fRate: §a${packetsPerSecond.toFixed(2)} §fpackets/sec | Total: §a${totalTransactions}`); if (mod.settings.showParameters.getValue()) { const topParameters = getTopParameters(8); if (topParameters.length > 0) { _embedded_1.Client.displayChatMessage(`§e[Transactions] §fTop parameters: ${topParameters.map(p => `§a${p.parameter}§f(${p.count})`).join(", ")}`); } } } // Calculate packets per second based on queue data function calculatePacketsPerSecond() { if (transactionQueue.length < 2) { return 0; } const oldestTimestamp = transactionQueue[0].timestamp; const newestTimestamp = transactionQueue[transactionQueue.length - 1].timestamp; const timeSpanSeconds = (newestTimestamp - oldestTimestamp) / 1000; return timeSpanSeconds > 0 ? transactionQueue.length / timeSpanSeconds : 0; } // Get top N most frequent parameters function getTopParameters(n) { return Object.entries(PARAMETER_FREQUENCY) .map(([parameter, count]) => ({ parameter: parseInt(parameter), count })) .sort((a, b) => b.count - a.count) .slice(0, n); } }); // Register a command to view transaction statistics script.registerCommand({ name: "txstats", aliases: ["transactionstats"], parameters: [{ name: "action", required: false, validate: _embedded_1.ParameterValidator.string }], onExecute(action) { if (action === "clear") { // Clear statistics totalTransactions = 0; transactionQueue.length = 0; for (const key in PARAMETER_FREQUENCY) { delete PARAMETER_FREQUENCY[key]; } _embedded_1.Client.displayChatMessage("§a[Transactions] Statistics cleared."); return; } if (transactionQueue.length === 0) { _embedded_1.Client.displayChatMessage("§c[Transactions] No transaction data available."); return; } // Display basic statistics const packetsPerSecond = calculatePacketsPerSecond(); _embedded_1.Client.displayChatMessage(`§e[Transactions] §fStatistics:`); _embedded_1.Client.displayChatMessage(`§fTotal transactions: §a${totalTransactions}`); _embedded_1.Client.displayChatMessage(`§fCurrent rate: §a${packetsPerSecond.toFixed(2)} §fpackets/sec`); // Display queue info _embedded_1.Client.displayChatMessage(`§fQueue size: §a${transactionQueue.length}/${MAX_QUEUE_SIZE}`); // Display top 5 parameters const topParameters = getTopParameters(5); if (topParameters.length > 0) { _embedded_1.Client.displayChatMessage(`§fTop 5 parameters:`); topParameters.forEach((p, index) => { _embedded_1.Client.displayChatMessage(`§f ${index + 1}. Parameter §a${p.parameter}§f: ${p.count} occurrences (${(p.count / totalTransactions * 100).toFixed(1)}%)`); }); } // Display time range if (transactionQueue.length >= 2) { const oldestTimestamp = transactionQueue[0].timestamp; const newestTimestamp = transactionQueue[transactionQueue.length - 1].timestamp; const timeSpanSeconds = (newestTimestamp - oldestTimestamp) / 1000; _embedded_1.Client.displayChatMessage(`§fTime span: §a${timeSpanSeconds.toFixed(2)} §fseconds`); } } }); // Helper functions for command execution function calculatePacketsPerSecond() { if (transactionQueue.length < 2) { return 0; } const oldestTimestamp = transactionQueue[0].timestamp; const newestTimestamp = transactionQueue[transactionQueue.length - 1].timestamp; const timeSpanSeconds = (newestTimestamp - oldestTimestamp) / 1000; return timeSpanSeconds > 0 ? transactionQueue.length / timeSpanSeconds : 0; } function getTopParameters(n) { return Object.entries(PARAMETER_FREQUENCY) .map(([parameter, count]) => ({ parameter: parseInt(parameter), count })) .sort((a, b) => b.count - a.count) .slice(0, n); }
Note that LiquidBounce cannot run typescript, at least as of now.
// imports /* eslint-disable unused-imports/no-unused-imports */ import { Setting, Vec3i, Vec3d, MathHelper, BlockPos, Hand, RotationAxis, mc, Client, RotationUtil, ItemUtil, NetworkUtil, InteractionUtil, BlockUtil, MovementUtil, ReflectionUtil, ParameterValidator, UnsafeThread, registerScript } from "@embedded"; import { CommonPingS2CPacket } from "@minecraft-yarn-definitions/types/net/minecraft/network/packet/s2c/common/CommonPingS2CPacket"; /* eslint-enable unused-imports/no-unused-imports */ // DO NOT TOUCH ANYTHING ABOVE THIS LINE // Define the TransactionEntry interface for storing packet data interface TransactionEntry { timestamp: number; parameter: number; } // Global variables for storing transaction data const transactionQueue: TransactionEntry[] = []; const MAX_QUEUE_SIZE = 100; // Global statistics let totalTransactions = 0; let lastReportTime = 0; const PARAMETER_FREQUENCY: { [key: number]: number } = {}; const script = registerScript.apply({ name: "transactions", version: "1.0.0", authors: ["commandblock2"] }); script.registerModule({ name: "transactions", description: "Logs transactions packets for anticheat detection", category: "Client", settings: { reportInterval: Setting.int({ name: "ReportInterval", default: 5, range: [1, 60], suffix: "seconds" }), showParameters: Setting.boolean({ name: "ShowParameters", default: true }), logToChat: Setting.boolean({ name: "LogToChat", default: true }), verbose: Setting.boolean({ name: "Verbose", default: false }) } }, (mod) => { mod.on("enable", () => { // Reset statistics totalTransactions = 0; lastReportTime = Date.now(); // Clear the queue transactionQueue.length = 0; // Reset parameter frequency for (const key in PARAMETER_FREQUENCY) { delete PARAMETER_FREQUENCY[key]; } Client.displayChatMessage("§a[Transactions] Monitoring started. Use .txstats to see statistics."); }); mod.on("disable", () => { Client.displayChatMessage("§c[Transactions] Monitoring stopped."); }); mod.on("packet", (event) => { if (event.packet instanceof CommonPingS2CPacket) { const parameter = event.packet.getParameter(); const timestamp = Date.now(); if (mod.settings.verbose.getValue()) { Client.displayChatMessage(`§a[Transactions] Received parameter: ${parameter}`); } // Add to queue transactionQueue.push({ timestamp, parameter }); // Keep queue size limited if (transactionQueue.length > MAX_QUEUE_SIZE) { transactionQueue.shift(); } // Update statistics totalTransactions++; PARAMETER_FREQUENCY[parameter] = (PARAMETER_FREQUENCY[parameter] || 0) + 1; // Check if it's time to report const currentTime = Date.now(); const reportIntervalMs = mod.settings.reportInterval.getValue() * 1000; if (mod.settings.logToChat.getValue() && currentTime - lastReportTime >= reportIntervalMs) { reportStatistics(); lastReportTime = currentTime; } } }); // Function to report statistics function reportStatistics() { if (transactionQueue.length === 0) { return; } const packetsPerSecond = calculatePacketsPerSecond(); Client.displayChatMessage(`§e[Transactions] §fRate: §a${packetsPerSecond.toFixed(2)} §fpackets/sec | Total: §a${totalTransactions}`); if (mod.settings.showParameters.getValue()) { const topParameters = getTopParameters(8); if (topParameters.length > 0) { Client.displayChatMessage(`§e[Transactions] §fTop parameters: ${topParameters.map(p => `§a${p.parameter}§f(${p.count})`).join(", ")}`); } } } // Calculate packets per second based on queue data function calculatePacketsPerSecond(): number { if (transactionQueue.length < 2) { return 0; } const oldestTimestamp = transactionQueue[0].timestamp; const newestTimestamp = transactionQueue[transactionQueue.length - 1].timestamp; const timeSpanSeconds = (newestTimestamp - oldestTimestamp) / 1000; return timeSpanSeconds > 0 ? transactionQueue.length / timeSpanSeconds : 0; } // Get top N most frequent parameters function getTopParameters(n: number): { parameter: number, count: number }[] { return Object.entries(PARAMETER_FREQUENCY) .map(([parameter, count]) => ({ parameter: parseInt(parameter), count })) .sort((a, b) => b.count - a.count) .slice(0, n); } }); // Register a command to view transaction statistics script.registerCommand({ name: "txstats", aliases: ["transactionstats"], parameters: [{ name: "action", required: false, validate: ParameterValidator.string }], onExecute(action: string) { if (action === "clear") { // Clear statistics totalTransactions = 0; transactionQueue.length = 0; for (const key in PARAMETER_FREQUENCY) { delete PARAMETER_FREQUENCY[key]; } Client.displayChatMessage("§a[Transactions] Statistics cleared."); return; } if (transactionQueue.length === 0) { Client.displayChatMessage("§c[Transactions] No transaction data available."); return; } // Display basic statistics const packetsPerSecond = calculatePacketsPerSecond(); Client.displayChatMessage(`§e[Transactions] §fStatistics:`); Client.displayChatMessage(`§fTotal transactions: §a${totalTransactions}`); Client.displayChatMessage(`§fCurrent rate: §a${packetsPerSecond.toFixed(2)} §fpackets/sec`); // Display queue info Client.displayChatMessage(`§fQueue size: §a${transactionQueue.length}/${MAX_QUEUE_SIZE}`); // Display top 5 parameters const topParameters = getTopParameters(5); if (topParameters.length > 0) { Client.displayChatMessage(`§fTop 5 parameters:`); topParameters.forEach((p, index) => { Client.displayChatMessage(`§f ${index + 1}. Parameter §a${p.parameter}§f: ${p.count} occurrences (${(p.count / totalTransactions * 100).toFixed(1)}%)`); }); } // Display time range if (transactionQueue.length >= 2) { const oldestTimestamp = transactionQueue[0].timestamp; const newestTimestamp = transactionQueue[transactionQueue.length - 1].timestamp; const timeSpanSeconds = (newestTimestamp - oldestTimestamp) / 1000; Client.displayChatMessage(`§fTime span: §a${timeSpanSeconds.toFixed(2)} §fseconds`); } } }); // Helper functions for command execution function calculatePacketsPerSecond(): number { if (transactionQueue.length < 2) { return 0; } const oldestTimestamp = transactionQueue[0].timestamp; const newestTimestamp = transactionQueue[transactionQueue.length - 1].timestamp; const timeSpanSeconds = (newestTimestamp - oldestTimestamp) / 1000; return timeSpanSeconds > 0 ? transactionQueue.length / timeSpanSeconds : 0; } function getTopParameters(n: number): { parameter: number, count: number }[] { return Object.entries(PARAMETER_FREQUENCY) .map(([parameter, count]) => ({ parameter: parseInt(parameter), count })) .sort((a, b) => b.count - a.count) .slice(0, n); }
-
where is the liquidbounce folder in prism launcher
-
[Script] PearlBlockThrow ?This is a somewhat faithful transplation of the original source code, which would not work on 1.8.x, this should desync your inventory too.
function __require(path) { if (path.startsWith("@embedded")) { return globalThis } if (path.startsWith("@minecraft-yarn-definitions/types/")) { return { [path.substring(path.lastIndexOf("/") + 1)]: Java.type(path .replaceAll("@minecraft-yarn-definitions/types/", "") .replaceAll("/", ".") ) } } return require(path); } var exports = {} "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); // imports /* eslint-disable unused-imports/no-unused-imports */ const _embedded_1 = __require("@embedded"); /* eslint-enable unused-imports/no-unused-imports */ // DO NOT TOUCH ANYTHING ABOVE THIS LINE, also not sure why it didn't work const PlayerInteractItemC2SPacket_1 = __require("@minecraft-yarn-definitions/types/net/minecraft/network/packet/c2s/play/PlayerInteractItemC2SPacket"); const UpdateSelectedSlotC2SPacket_1 = __require("@minecraft-yarn-definitions/types/net/minecraft/network/packet/c2s/play/UpdateSelectedSlotC2SPacket"); const UpdateSelectedSlotS2CPacket_1 = __require("@minecraft-yarn-definitions/types/net/minecraft/network/packet/s2c/play/UpdateSelectedSlotS2CPacket"); const Items_1 = __require("@minecraft-yarn-definitions/types/net/minecraft/item/Items"); const Hand_1 = __require("@minecraft-yarn-definitions/types/net/minecraft/util/Hand"); const script = _embedded_1.registerScript.apply({ name: "no-pearl-use", version: "1.0.0", authors: ["commandblock2"] }); script.registerModule({ name: "no-pearl-use", description: "cancels ender pearl use", category: "Player", }, (mod) => { var slot = 0; mod.on("enable", () => { var _a, _b; slot = (_b = (_a = _embedded_1.mc.player) === null || _a === void 0 ? void 0 : _a.inventory.selectedSlot) !== null && _b !== void 0 ? _b : 0; }); mod.on("packet", (event) => { var _a; if (event.packet instanceof UpdateSelectedSlotS2CPacket_1.UpdateSelectedSlotS2CPacket) { slot = event.packet.slot(); } if (event.packet instanceof UpdateSelectedSlotC2SPacket_1.UpdateSelectedSlotC2SPacket) { slot = event.packet.selectedSlot; } if (event.packet instanceof PlayerInteractItemC2SPacket_1.PlayerInteractItemC2SPacket && ((_a = _embedded_1.mc.player) === null || _a === void 0 ? void 0 : _a.inventory.main[slot].item) == Items_1.Items.ENDER_PEARL) { // use reflection to set the hand to offhand const field = event.packet.class.getDeclaredField("hand"); field.setAccessible(true); field.set(event.packet, Hand_1.Hand.OFF_HAND); _embedded_1.Client.displayChatMessage(`§cPearl use changed to hand ${event.packet.hand}`); } }); });
original typescript code
// imports /* eslint-disable unused-imports/no-unused-imports */ import { Setting, Vec3i, Vec3d, MathHelper, BlockPos, Hand, RotationAxis, mc, Client, RotationUtil, ItemUtil, NetworkUtil, InteractionUtil, BlockUtil, MovementUtil, ReflectionUtil, ParameterValidator, UnsafeThread, registerScript } from "@embedded"; /* eslint-enable unused-imports/no-unused-imports */ // DO NOT TOUCH ANYTHING ABOVE THIS LINE, also not sure why it didn't work import { PlayerInteractItemC2SPacket } from "@minecraft-yarn-definitions/types/net/minecraft/network/packet/c2s/play/PlayerInteractItemC2SPacket"; import { UpdateSelectedSlotC2SPacket } from "@minecraft-yarn-definitions/types/net/minecraft/network/packet/c2s/play/UpdateSelectedSlotC2SPacket"; import { UpdateSelectedSlotS2CPacket } from "@minecraft-yarn-definitions/types/net/minecraft/network/packet/s2c/play/UpdateSelectedSlotS2CPacket"; import { Items } from "@minecraft-yarn-definitions/types/net/minecraft/item/Items"; import { Hand as MCHand } from "@minecraft-yarn-definitions/types/net/minecraft/util/Hand"; const script = registerScript.apply({ name: "no-pearl-use", version: "1.0.0", authors: ["commandblock2"] }); script.registerModule({ name: "no-pearl-use", description: "cancels ender pearl use", category: "Player", }, (mod) => { var slot = 0 mod.on("enable", () => { slot = mc.player?.inventory.selectedSlot ?? 0; }) mod.on("packet", (event) => { if (event.packet instanceof UpdateSelectedSlotS2CPacket) { slot = event.packet.slot(); } if (event.packet instanceof UpdateSelectedSlotC2SPacket) { slot = event.packet.selectedSlot; } if (event.packet instanceof PlayerInteractItemC2SPacket && mc.player?.inventory.main[slot].item == Items.ENDER_PEARL ) { // use reflection to set the hand to offhand const field = (event.packet as any).class.getDeclaredField("hand"); field.setAccessible(true); field.set(event.packet, MCHand.OFF_HAND); Client.displayChatMessage(`§cPearl use changed to hand ${event.packet.hand}`); } }) })
This will indeed cancel the usage of the pearl on 1.8.x protocols
function __require(path) { if (path.startsWith("@embedded")) { return globalThis } if (path.startsWith("@minecraft-yarn-definitions/types/")) { return { [path.substring(path.lastIndexOf("/") + 1)]: Java.type(path .replaceAll("@minecraft-yarn-definitions/types/", "") .replaceAll("/", ".") ) } } return require(path); } var exports = {} "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); // imports /* eslint-disable unused-imports/no-unused-imports */ const _embedded_1 = __require("@embedded"); /* eslint-enable unused-imports/no-unused-imports */ // DO NOT TOUCH ANYTHING ABOVE THIS LINE, also not sure why it didn't work const PlayerInteractItemC2SPacket_1 = __require("@minecraft-yarn-definitions/types/net/minecraft/network/packet/c2s/play/PlayerInteractItemC2SPacket"); const UpdateSelectedSlotC2SPacket_1 = __require("@minecraft-yarn-definitions/types/net/minecraft/network/packet/c2s/play/UpdateSelectedSlotC2SPacket"); const UpdateSelectedSlotS2CPacket_1 = __require("@minecraft-yarn-definitions/types/net/minecraft/network/packet/s2c/play/UpdateSelectedSlotS2CPacket"); const Items_1 = __require("@minecraft-yarn-definitions/types/net/minecraft/item/Items"); const script = _embedded_1.registerScript.apply({ name: "no-pearl-use", version: "1.0.0", authors: ["commandblock2"] }); script.registerModule({ name: "no-pearl-use", description: "cancels ender pearl use", category: "Player", }, (mod) => { var slot = 0; mod.on("enable", () => { var _a, _b; slot = (_b = (_a = _embedded_1.mc.player) === null || _a === void 0 ? void 0 : _a.inventory.selectedSlot) !== null && _b !== void 0 ? _b : 0; }); mod.on("packet", (event) => { var _a; if (event.packet instanceof UpdateSelectedSlotS2CPacket_1.UpdateSelectedSlotS2CPacket) { slot = event.packet.slot(); } if (event.packet instanceof UpdateSelectedSlotC2SPacket_1.UpdateSelectedSlotC2SPacket) { slot = event.packet.selectedSlot; } if (event.packet instanceof PlayerInteractItemC2SPacket_1.PlayerInteractItemC2SPacket && ((_a = _embedded_1.mc.player) === null || _a === void 0 ? void 0 : _a.inventory.main[slot].item) == Items_1.Items.ENDER_PEARL) { event.cancelEvent(); } }); });
This will desync your inventory with the server anyway, but it is true that no end pearl will be thrown if you have one in your hand (unless u have a pearl in server side but not client side). Can be used as a example.
Later is the original ts file.// imports /* eslint-disable unused-imports/no-unused-imports */ import { Setting, Vec3i, Vec3d, MathHelper, BlockPos, Hand, RotationAxis, mc, Client, RotationUtil, ItemUtil, NetworkUtil, InteractionUtil, BlockUtil, MovementUtil, ReflectionUtil, ParameterValidator, UnsafeThread, registerScript } from "@embedded"; /* eslint-enable unused-imports/no-unused-imports */ // DO NOT TOUCH ANYTHING ABOVE THIS LINE, also not sure why it didn't work import { PlayerInteractItemC2SPacket } from "@minecraft-yarn-definitions/types/net/minecraft/network/packet/c2s/play/PlayerInteractItemC2SPacket"; import { UpdateSelectedSlotC2SPacket } from "@minecraft-yarn-definitions/types/net/minecraft/network/packet/c2s/play/UpdateSelectedSlotC2SPacket"; import { UpdateSelectedSlotS2CPacket } from "@minecraft-yarn-definitions/types/net/minecraft/network/packet/s2c/play/UpdateSelectedSlotS2CPacket"; import { Items } from "@minecraft-yarn-definitions/types/net/minecraft/item/Items"; const script = registerScript.apply({ name: "no-pearl-use", version: "1.0.0", authors: ["commandblock2"] }); script.registerModule({ name: "no-pearl-use", description: "cancels ender pearl use", category: "Player", }, (mod) => { var slot = 0 mod.on("enable", () => { slot = mc.player?.inventory.selectedSlot ?? 0; }) mod.on("packet", (event) => { if (event.packet instanceof UpdateSelectedSlotS2CPacket) { slot = event.packet.slot(); } if (event.packet instanceof UpdateSelectedSlotC2SPacket) { slot = event.packet.selectedSlot; } if (event.packet instanceof PlayerInteractItemC2SPacket && mc.player?.inventory.main[slot].item == Items.ENDER_PEARL ) { event.cancelEvent(); } }) })
-
who can write the script? noSlotChanger in the client LB for NextgenTrying with matrix on loyisa.cn with noslow mode set to None indeed will move your slot to other items. However just like I assumed previously simply ignoring the packet just won't do, you can never eat the food, even if you immediately send back a slot change. You might need something else to make a nolow mode for matrix. If you need to verify yourself, here is your script anyway.
function __require(path) { if (path.startsWith("@embedded")) { return globalThis } if (path.startsWith("@minecraft-yarn-definitions/types/")) { return { [path.substring(path.lastIndexOf("/") + 1)]: Java.type(path .replaceAll("@minecraft-yarn-definitions/types/", "") .replaceAll("/", ".") ) } } return require(path); } var exports = {} "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); // imports /* eslint-disable unused-imports/no-unused-imports */ const _embedded_1 = __require("@embedded"); /* eslint-enable unused-imports/no-unused-imports */ // DO NOT TOUCH ANYTHING ABOVE THIS LINE, also not sure why it didn't work const UpdateSelectedSlotC2SPacket_1 = __require("@minecraft-yarn-definitions/types/net/minecraft/network/packet/c2s/play/UpdateSelectedSlotC2SPacket"); const UpdateSelectedSlotS2CPacket_1 = __require("@minecraft-yarn-definitions/types/net/minecraft/network/packet/s2c/play/UpdateSelectedSlotS2CPacket"); const script = _embedded_1.registerScript.apply({ name: "anti slot changing", version: "1.0.0", authors: ["commandblock2"] }); script.registerModule({ name: "anti-slot-changing", description: "like no rotate reset but for slots", category: "Exploit", settings: { resyncServerSide: _embedded_1.Setting.boolean({ name: "resync server side", default: true }) }, }, (mod) => { mod.on("packet", (event) => { var _a, _b, _c; if (event.packet instanceof UpdateSelectedSlotS2CPacket_1.UpdateSelectedSlotS2CPacket) { event.cancelEvent(); if (!mod.settings.resyncServerSide.get()) return; (_a = _embedded_1.mc.getNetworkHandler()) === null || _a === void 0 ? void 0 : _a.sendPacket(new UpdateSelectedSlotC2SPacket_1.UpdateSelectedSlotC2SPacket((_c = (_b = _embedded_1.mc.player) === null || _b === void 0 ? void 0 : _b.inventory.selectedSlot) !== null && _c !== void 0 ? _c : 0)); } }); });
-
Vectorized VelocityChanges the velocity direction or strength based on the incoming packet or minecraft player's facing direction. Usually not useful unless you need to be flying sideways or your facing direction.
Released js for LiquidBounce Nextgen 0.28.1, Licensed under GPLv3
function __require(path) { if (path.startsWith("@embedded")) { return globalThis } if (path.startsWith("@minecraft-yarn-definitions/types/")) { return { [path.substring(path.lastIndexOf("/") + 1)]: Java.type(path .replaceAll("@minecraft-yarn-definitions/types/", "") .replaceAll("/", ".") ) } } return require(path); } var exports = {} "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); // imports /* eslint-disable unused-imports/no-unused-imports */ const _embedded_1 = __require("@embedded"); const EntityVelocityUpdateS2CPacket_1 = __require("@minecraft-yarn-definitions/types/net/minecraft/network/packet/s2c/play/EntityVelocityUpdateS2CPacket"); /* eslint-enable unused-imports/no-unused-imports */ // DO NOT TOUCH ANYTHING ABOVE THIS LINE, also not sure why it didn't work const script = _embedded_1.registerScript.apply({ name: "vectorized-velocity-xz", version: "1.0.0", authors: ["commandblock2"] }); script.registerModule({ name: "vectorized-velocity-xz", description: "Port of the old script for legacy, for those anticheat that doesn't have proper velocity detection", category: "Combat", settings: { offsetBasedOnPlayerOrIncomingVelocity: _embedded_1.Setting.choose({ name: "OffsetBasedOnPlayerOrIncomingVelocity", choices: ["player", "incoming"], default: "player" }), offset: _embedded_1.Setting.float({ name: "AngleOffset", default: 0, range: [-180, 180] }), amplitude: _embedded_1.Setting.float({ name: "Amplitude", default: 1, range: [-100, 100] }) } }, (mod) => { mod.on("packet", (event) => { if (!_embedded_1.mc.player) return; const packet = event.packet; if (packet instanceof EntityVelocityUpdateS2CPacket_1.EntityVelocityUpdateS2CPacket && packet.entityId == _embedded_1.mc.player.id) { const yaw = mod.settings.offset.get() + (mod.settings.offsetBasedOnPlayerOrIncomingVelocity.getValue() == "player" ? -_embedded_1.mc.player.yaw : (Math.atan2(packet.getVelocityX(), packet.getVelocityZ()) * 180 / Math.PI)); const velocity = Math.sqrt(packet.getVelocityX() * packet.getVelocityX() + packet.getVelocityZ() * packet.getVelocityZ()) * 8000 * mod.settings.amplitude.get(); // graaljs traps here: // 1. it eats error here when we have loss in precision and does not update the value // 2. packet.velocityX and packet.getVelocityX() is very different (public double getVelocityX() { return (double)this.velocityX / 8000.0; }) // and when reading velocityX graaljs calls getVelocityX() and when writing velocityX it directly writes that packet.velocityX = Math.round(velocity * Math.sin(yaw / 180 * Math.PI)); packet.velocityZ = Math.round(velocity * Math.cos(yaw / 180 * Math.PI)); } }); });
original typescript script, won't run directly in LiquidBounce, for more info, see https://github.com/commandblock2/minecraft-LBNG-types
// imports /* eslint-disable unused-imports/no-unused-imports */ import { Setting, Vec3i, Vec3d, MathHelper, BlockPos, Hand, RotationAxis, mc, Client, RotationUtil, ItemUtil, NetworkUtil, InteractionUtil, BlockUtil, MovementUtil, ReflectionUtil, ParameterValidator, UnsafeThread, registerScript } from "@embedded"; import { EntityVelocityUpdateS2CPacket } from "@minecraft-yarn-definitions/types/net/minecraft/network/packet/s2c/play/EntityVelocityUpdateS2CPacket"; /* eslint-enable unused-imports/no-unused-imports */ // DO NOT TOUCH ANYTHING ABOVE THIS LINE, also not sure why it didn't work const script = registerScript.apply({ name: "vectorized-velocity-xz", version: "1.0.0", authors: ["commandblock2"] }); script.registerModule({ name: "vectorized-velocity-xz", description: "Port of the old script for legacy, for those anticheat that doesn't have proper velocity detection", category: "Combat", settings: { offsetBasedOnPlayerOrIncomingVelocity: Setting.choose({ name: "OffsetBasedOnPlayerOrIncomingVelocity", choices: ["player", "incoming"], default: "player" }), offset: Setting.float({ name: "AngleOffset", default: 0, range: [-180, 180] }), amplitude: Setting.float({ name: "Amplitude", default: 1, range: [-100, 100] }) } }, (mod) => { mod.on("packet", (event) => { if (!mc.player) return; const packet = event.packet if (packet instanceof EntityVelocityUpdateS2CPacket && packet.entityId == mc.player.id) { const yaw = mod.settings.offset.get() + (mod.settings.offsetBasedOnPlayerOrIncomingVelocity.getValue() == "player" ? -mc.player.yaw : (Math.atan2(packet.getVelocityX(), packet.getVelocityZ()) * 180 / Math.PI)); const velocity = Math.sqrt(packet.getVelocityX() * packet.getVelocityX() + packet.getVelocityZ() * packet.getVelocityZ()) * 8000 * mod.settings.amplitude.get(); // graaljs traps here: // 1. it eats error here when we have loss in precision and does not update the value // 2. packet.velocityX and packet.getVelocityX() is very different (public double getVelocityX() { return (double)this.velocityX / 8000.0; }) // and when reading velocityX graaljs calls getVelocityX() and when writing velocityX it directly writes that packet.velocityX = Math.round(velocity * Math.sin(yaw / 180 * Math.PI)) packet.velocityZ = Math.round(velocity * Math.cos(yaw / 180 * Math.PI)) } }) })
-
who can write the script? noSlotChanger in the client LB for NextgenThis might be more than just 'preventing the server side anti-cheat to change the hot bar slot' since you mentioned that you are using noslow while eating. Can you please post what anti cheat (might be using
UpdateSelectedSlotS2CPacket
to sync your hotbar) and what noslow modes are you using? (grim noslow modes switches your hotbar server side)internal class NoSlowSharedGrim2364MC18(override val parent: ChoiceConfigurable<*>) : Choice("Grim2364-1.8") { @Suppress("unused") private val onNetworkTick = handler<PlayerNetworkMovementTickEvent> { event -> if (player.isUsingItem && event.state == EventState.PRE) { // Switch slots so grim exempts noslow... // Introduced with https://github.com/GrimAnticheat/Grim/issues/874 untracked { val slot = player.inventory.selectedSlot network.sendPacket(UpdateSelectedSlotC2SPacket(slot % 8 + 1)) network.sendPacket(UpdateSelectedSlotC2SPacket(slot % 7 + 2)) network.sendPacket(UpdateSelectedSlotC2SPacket(slot)) } } } }
Even if you refuse to accept any
UpdateSelectedSlotS2CPacket
and looks as if your hotbar is not switching, there could be a desync and you won't be able to eat food. Would appreciate if you could provide more information of what you are tying to do. -
who can write the script? noSlotChanger in the client LB for Nextgenencoded a jar into base64 and embed in a script, not crazy at all xD.
-
WIP Windows10 Fluent Design ThemeFeatures (maybe xD)
- reveal effect from https://github.com/d2phap/fluent-reveal-effect
- absolutely no rounded corner
- use platform dependent font
Hack
and not packaging it into the theme xD - in-consistent effect application (not on buttons like Bind)
- not polished at all
- other gui not changed at all
- horrible performance
- no instruction to install at all
Jokes aside this is indeed a WIP theme and don't expect it be perfect. To contribute to this theme, prs and issues are welcomed, https://github.com/commandblock2/GoodOldFluentDesign
- reveal effect from https://github.com/d2phap/fluent-reveal-effect
-
Typescript definition for minecraft and LiquidBounceUpdate: subsequent updates will be available on https://github.com/commandblock2/minecraft-LBNG-types
and the generated script was reverted to a state where it was not as readable as the following example (for correctness) but still largely remained the same.
Now it can kinda generate a working script, but the ts file has a few squiggles.
template.ts
// imports import { Setting, Vec3i, Vec3d, MathHelper, BlockPos, Hand, RotationAxis, mc, Client, RotationUtil, ItemUtil, NetworkUtil, InteractionUtil, BlockUtil, MovementUtil, ReflectionUtil, ParameterValidator, UnsafeThread, registerScript } from "@embedded"; import { ScriptModule } from "@minecraft-yarn-definitions/types/net/ccbluex/liquidbounce/script/bindings/features/ScriptModule"; import { Matrix2d } from "@minecraft-yarn-definitions/types/org/joml/Matrix2d"; const script = registerScript.apply({ name: "template", version: "1.0.0", authors: ["commandblock2"] }); script.registerModule({ // @ts-ignore name: "example-from-template", // @ts-ignore description: "Ths is an example module generated in ts", // @ts-ignore category: "Client" }, (mod: ScriptModule) => { mod.on("enable", () => { Client.displayChatMessage(`${mc.player}`) Client.displayChatMessage(`${new Vec3i(1, 2, 3)}`) Client.displayChatMessage(`${new Matrix2d(1.2, 1.3, 1.4, 15)}`) Client.displayChatMessage("enabled") }) mod.on("disable", () => Client.displayChatMessage("disabled")) })
compiled template.js
function __require(path) { if (path.startsWith("@embedded")) { return { _embedded: globalThis } } if (path.startsWith("@minecraft-yarn-definitions/types/")) { return { [path.substring(path.lastIndexOf("/") + 1)]: Java.type(path .replaceAll("@minecraft-yarn-definitions/types/", "") .replaceAll("/", ".") ) } } return require(path); } var exports = {} "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const { _embedded } = __require("@embedded"); const { Matrix2d } = __require("@minecraft-yarn-definitions/types/org/joml/Matrix2d"); const script = _embedded.registerScript.apply({ name: "template", version: "1.0.0", authors: ["commandblock2"] }); script.registerModule({ // @ts-ignore name: "example-from-template", // @ts-ignore description: "Ths is an example module generated in ts", // @ts-ignore category: "Client" }, (mod) => { mod.on("enable", () => { _embedded.Client.displayChatMessage(`${_embedded.mc.player}`); _embedded.Client.displayChatMessage(`${new Vec3i(1, 2, 3)}`); _embedded.Client.displayChatMessage(`${new Matrix2d(1.2, 1.3, 1.4, 15)}`); _embedded.Client.displayChatMessage("enabled"); }); mod.on("disable", () => _embedded.Client.displayChatMessage("disabled")); });
-
Typescript definition for minecraft and LiquidBounceUpdate: now the script will also print the embedded context made available by the script api
// Import required Java classes // type: array /** @type any[]*/ var globalEntries = Object.entries(this); const System = Java.type("java.lang.System"); const URLClassLoader = Java.type("java.net.URLClassLoader"); const File = Java.type("java.io.File"); const URL = Java.type("java.net.URL"); const Thread = Java.type("java.lang.Thread"); const Paths = Java.type("java.nio.file.Paths"); const Map = Java.type("java.util.HashMap"); const ArrayList = Java.type("java.util.ArrayList"); const JvmClassMappingKt = Java.type("kotlin.jvm.JvmClassMappingKt"); const Class = Java.type("java.lang.Class"); // Function to create a URLClassLoader from a JAR path function createClassLoaderFromJar(jarPath) { try { // Create File object for the JAR const jarFile = new File(jarPath); // Convert File to URL const jarUrl = jarFile.toURI().toURL(); // Create URLClassLoader with the system class loader as parent return new URLClassLoader( [jarUrl], Thread.currentThread().getContextClassLoader() ); } catch (e) { console.error("Error creating ClassLoader:", e); throw e; } } // Function to load a class from a given ClassLoader function loadClassFromJar(classLoader, className) { try { return classLoader.loadClass(className); } catch (e) { console.error(`Error loading class ${className}:`, e); throw e; } } const script = registerScript({ name: "for-repl", version: "1.0.0", authors: ["commandblock2"], }); script.registerModule( { name: "for-repl", category: "Client", description: "Sausage", settings: { path: Setting.text({ name: "Path", default: "/mnt/old-linux/home/commandblock2/git_repo/MultiMC5/build/instances/LBNG-Production/minecraft/LiquidBounce/scripts", }), packageName: Setting.text({ name: "NPMPackageName", default: "minecraft-yarn-definitions", }), }, }, (mod) => { mod.on("enable", (event) => { const loader = createClassLoaderFromJar( mod.settings.path.value + "/ts-generator-1.0.0.jar" ); const NPMGen = loadClassFromJar( loader, "me.commandblock2.tsGenerator.NPMPackageGenerator" ); const TsGen = loadClassFromJar( loader, "me.ntrrgc.tsGenerator.TypeScriptGenerator" ); const VoidType = loadClassFromJar( loader, "me.ntrrgc.tsGenerator.VoidType" ); const NULL = VoidType.getEnumConstants()[0]; const javaClasses = globalEntries .filter((entry) => entry[1] != undefined) .map((entry) => (entry[1] instanceof Class ? entry[1] : entry[1].class)) .filter((entry) => entry != undefined); const kotlinClasses = javaClasses.map((entry) => JvmClassMappingKt.getKotlinClass(entry) ); const classes = new ArrayList(kotlinClasses); try { const generated = new TsGen( classes, new Map(), new ArrayList(), new ArrayList(), "number", NULL ); const npmPack = new NPMGen(generated, mod.settings.packageName.value); npmPack.writePackageTo( Paths.get(mod.settings.path.value + "/types-gen") ); const embeddedDefinition = ` // imports ${javaClasses .map((clazz) => { return `import { ${clazz.simpleName} } from "@${mod.settings.packageName.value}/types/${clazz.name.replaceAll(".", "/")}";`; }) .join("\n")} // definitions for objects ${globalEntries .filter((entry) => entry[1] != undefined) .filter((entry) => !(entry[1] instanceof Class)) .filter((entry) => entry[1].class != undefined) .map((entry) => `export var ${entry[0]}: ${entry[1].class.simpleName};`) .join("\n\n")} `; console.log(embeddedDefinition) } catch (e) { e.printStackTrace(); console.error(e); throw e; } // ReflectionUtil.invokeMethod(mc.player, "getName") // event.context.drawGuiTexture(RenderLayer.getGuiTextured, CLOSE_TEXTURE, 0, 0, 16, 16, -1); }); } );
-
Typescript definition for minecraft and LiquidBounceFollowing
repo
meansdemo
https://github.com/commandblock2/minecraft-LBNG-types.
minecraft-LBNG-types
DISCLAIMER: Use this at your own risk!!! This is an unofficial project not endorsed by Mojang or fabric developers. This repo does NOT
- contains any generated minecraft type definitions by itself
- redistribute the game itself
- guarentee the correctness of the generated definition
This repo contains
- instruction and scripts for creating typescript definitions
- for Minecraft (with mods*)
- for LiquidBounce NextGen
- for everything inside of the jvm
- typescript definitions for embedded context of LiquidBounce NextGen scriptAPI
- a set of manually maintained patches to make the script api work properly
- a set of examples LiquidBounce NextGen script using typescript
- a compiler script that will compile all the .ts files into .js files that could run in graaljs (LiquidBounce NextGen runtime) with watch mode
- some prompt files to use on LLMs, specifically for claude + continue.dev vscode extension, at .continue/prompts.
Note: the mods are only limited to those presented at the development environment of LiquidBounce NextGen.
When writing your script in typescript, expect inconsistencies with script API in js, but please report them to this repo if you can
Instruction (subject to change)
Adjust the order flexibly to your needs.
Generating the typescript definitions
-
Setup development environment for Liquidbounce NextGen
-
Clone LBNG, run
git checkout 451cb31e9bf093fe07f9b28202bc2471921ea13d
(for version 0.29.0 release) and launch with gradle./gradlew runClient
without intellij idea(recommened because of the sheer amount of memory it takes to generate the definition). -
Place the
LBNG-script/ts-defgen.js
in your script folder for LiquidBounce -
Build or download the latest released ts-generator jar from github, place it in your script folder as well.
-
Do a
.script reload
in your client, this should load thets-defgen.js
-
Run the
.ts-defgen
command -
See messages from chat and wait for around a few minute or more or less depending on your setup, this may take a while and also nearly 7GB of additional RAM (other than your intellij idea plus the what Minecraft needs in it's original state, causes OOM for me a lot of times xD).
Now you can find a
types-gen
folder in your script folder, this contains the generated typescript definitions.. ├── ts-defgen.js ├── ts-generator-1.1.1.jar └── types-gen └── minecraft-yarn-definitions ├── package.json ├── tsconfig.json └── types ├── ai ├── com ├── _COROUTINE ├── de ├── io ├── it ├── java ├── javax ├── jdk ├── joptsimple ├── kotlin ├── kotlinx ├── net ├── okhttp3 ├── okio ├── org ├── oshi └── sun ├── ... other scripts.js
Writing scripts with TypeScript Support
- Run
npm install
in this directory. - copy the generated folder
types-gen
togenerated-modules
folder in the root of your project. - Run the script
apply-patch
withnpm run apply-patches
- Run
npm install file:./generated-modules/types-gen/minecraft-yarn-definitions/ --no-save
, no-save for now, not sure if I should do this. - Open the
template.ts
file and try start writing your script, you should see TypeScript type hints for all the classes that are available. vscode will automatically generate working imports, but you should not touch the import statement with@embedded
namespace. - Run the script
compile
with npm like step 4 ornpm run watch
- Corresponding javascript file is generated in the
dist
directory, you can link this dist directory to your scripts directory in LB.
Contribution and TODOs
If you know how to better organize this project (architecture), please feel free to submit a PR.
If you find errors on generated definitions about Minecraft classes or LiquidBounce classes, please file your tick at ts-generator.
If you find a un-intended behavior in the
ts-defgen.js
, compile script or manually maintained definitions(TODO), please file an issue here.License
This project is licensed under the GNU General Public license, see LICENSE for more information.