Merge branch 'master' of https://github.com/JessSystemV/BaseBandRewrite
This commit is contained in:
commit
8e9a7715de
20 changed files with 279 additions and 99 deletions
|
@ -4,6 +4,7 @@ import com.baseband.client.configuration.Updater;
|
|||
import com.baseband.client.event.EventManager;
|
||||
import com.baseband.client.event.FMLEventHandler;
|
||||
import com.baseband.client.module.Feature;
|
||||
import com.baseband.client.module.client.Client;
|
||||
import com.baseband.client.module.render.*;
|
||||
import com.baseband.client.util.config.KeyBind;
|
||||
import com.baseband.client.util.ingame.ChatUtil;
|
||||
|
@ -47,7 +48,19 @@ public class BaseBand {
|
|||
}
|
||||
|
||||
public static void notify(String text) {
|
||||
HUD.notifs.add(new HUD.Notification(text));
|
||||
Client c = getFeature(Client.class);
|
||||
if(isFeatureEnabled(HUD.class) && c.notificationDest != Client.NotificationDest.Chat) {
|
||||
HUD.notifs.add(new HUD.Notification(text));
|
||||
if(c.notificationDest == Client.NotificationDest.Both)
|
||||
ChatUtil.print(text);
|
||||
} else
|
||||
ChatUtil.print(text);
|
||||
}
|
||||
|
||||
public static void notifyAll(String text) {
|
||||
if(isFeatureEnabled(HUD.class)) {
|
||||
HUD.notifs.add(new HUD.Notification(text));
|
||||
}
|
||||
ChatUtil.print(text);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
package com.baseband.client.configuration.annotation;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Target(ElementType.FIELD)
|
||||
public @interface Description {
|
||||
String value();
|
||||
}
|
|
@ -1,14 +1,17 @@
|
|||
package com.baseband.client.gui.lib.component;
|
||||
|
||||
import com.baseband.client.BaseBand;
|
||||
import com.baseband.client.configuration.annotation.AnyGate;
|
||||
import com.baseband.client.configuration.annotation.Config;
|
||||
import com.baseband.client.configuration.annotation.Gate;
|
||||
import com.baseband.client.gui.lib.GUIManager;
|
||||
import com.baseband.client.gui.lib.RenderException;
|
||||
import com.baseband.client.util.misc.Marker;
|
||||
import com.baseband.client.util.render.TextSplitter;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.gui.ScaledResolution;
|
||||
import org.lwjgl.util.Point;
|
||||
import org.lwjgl.util.Rectangle;
|
||||
|
||||
|
@ -31,6 +34,7 @@ public abstract class Component {
|
|||
final FontRenderer fontRenderer = Minecraft.getMinecraft().fontRenderer;
|
||||
public final ArrayList<Component> subComponents = new ArrayList<>();
|
||||
public String text = "";
|
||||
public String hover = null;
|
||||
public boolean green = false;
|
||||
|
||||
@Config("GUI/Expanded")
|
||||
|
@ -65,14 +69,7 @@ public abstract class Component {
|
|||
Gui.drawRect(x + 2 + sub * 8, y.get(), x + 2 + sub * 8 + 1, y.get() + (isLastInList ? 5 + (sub == 0 ? subSizes() : 0) : size() + subSizes()), GUIManager.frameColor);
|
||||
// this is the - part of the |-
|
||||
Gui.drawRect(x + 2 + sub * 8, y.get() + 4, x + 5 + sub * 8 + 1, y.get() + 4 + 1, GUIManager.frameColor);
|
||||
String text = this.text;
|
||||
if (fontRenderer.getStringWidth(text) > 190 - (sub * 8)) {
|
||||
while (fontRenderer.getStringWidth(text) > 185 - (sub * 8)) {
|
||||
text = text.substring(0, text.length() - 1);
|
||||
}
|
||||
text += "...";
|
||||
}
|
||||
fontRenderer.drawString(text, x + 8 + sub * 8, y.get(), green ? GUIManager.fontColorOn : GUIManager.fontColorOff);
|
||||
fontRenderer.drawString(TextSplitter.limit(text, 190 - (sub * 8)), x + 8 + sub * 8, y.get(), green ? GUIManager.fontColorOn : GUIManager.fontColorOff);
|
||||
draw(x + 8 + sub * 8, y.get());
|
||||
y.addAndGet(size());
|
||||
if (subComponentsShown) {
|
||||
|
@ -142,7 +139,19 @@ public abstract class Component {
|
|||
|
||||
public void onConfirm(boolean result) { }
|
||||
|
||||
public void hover(int x, int y) {}
|
||||
public void hover(int x, int y) {
|
||||
if(hover != null) {
|
||||
ScaledResolution sr = new ScaledResolution(BaseBand.mc);
|
||||
int bsx = 200;
|
||||
String hover = TextSplitter.breakText(this.hover, 200);
|
||||
int bsy = TextSplitter.getStringHeight(hover);
|
||||
int bx = sr.getScaledWidth() - 200 - 5;
|
||||
int by = sr.getScaledHeight() - bsy - 5;
|
||||
|
||||
Gui.drawRect(bx - 2, by - 2, bx + bsx + 2, by + bsy + 2, 0x80000000);
|
||||
TextSplitter.renderSplit(hover, bx, by, 0xffffffff, true);
|
||||
}
|
||||
}
|
||||
|
||||
public Component gate(AnyGate gate) {
|
||||
if(gate == null)
|
||||
|
@ -150,4 +159,9 @@ public abstract class Component {
|
|||
this.visibilityGate = gate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Component hover(String hover) {
|
||||
this.hover = hover;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,6 +46,7 @@ public class IntSlider extends Component {
|
|||
|
||||
@Override
|
||||
public void hover(int x, int y) {
|
||||
super.hover(x, y);
|
||||
if(countdown > 0)
|
||||
return;
|
||||
|
||||
|
|
|
@ -46,6 +46,7 @@ public class Slider extends Component {
|
|||
|
||||
@Override
|
||||
public void hover(int x, int y) {
|
||||
super.hover(x, y);
|
||||
if(countdown > 0)
|
||||
return;
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ public class StringButton extends Button {
|
|||
protected final String name;
|
||||
protected final ConfigHandle handle;
|
||||
protected final String field;
|
||||
protected String underlyingHover;
|
||||
|
||||
public StringButton(String name, ConfigHandle handle, String field) {
|
||||
super(name + ": --UNINIT", new ClickEvent() {
|
||||
|
@ -27,6 +28,14 @@ public class StringButton extends Button {
|
|||
|
||||
@Override
|
||||
public synchronized void update() {
|
||||
text = name + ": " + handle.getContent().getString(field);
|
||||
String string = handle.getContent().getString(field);
|
||||
text = name + ": " + string;
|
||||
hover = underlyingHover + "\nValue: " + string;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Component hover(String hover) {
|
||||
underlyingHover = hover;
|
||||
return super.hover(hover);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -79,12 +79,12 @@ public abstract class Feature extends ToggleButton implements SetCommand {
|
|||
if(enabled) {
|
||||
BaseBand.eventManager.subscribe(this);
|
||||
MinecraftForge.EVENT_BUS.register(this);
|
||||
HUD.notify("§l" + this + "§a enabled§r.");
|
||||
BaseBand.notify("§l" + this + "§a enabled§r.");
|
||||
onEnable();
|
||||
} else {
|
||||
BaseBand.eventManager.unsubscribe(this);
|
||||
MinecraftForge.EVENT_BUS.unregister(this);
|
||||
HUD.notify("§l" + this + "§c disabled§r.");
|
||||
BaseBand.notify("§l" + this + "§c disabled§r.");
|
||||
onDisable();
|
||||
}
|
||||
}
|
||||
|
@ -107,11 +107,13 @@ public abstract class Feature extends ToggleButton implements SetCommand {
|
|||
Field[] fields = getClass().getFields();
|
||||
for (Field f : fields) {
|
||||
Config config = f.getDeclaredAnnotation(Config.class);
|
||||
Description descriptionAnnotation = f.getDeclaredAnnotation(Description.class);
|
||||
String description = descriptionAnnotation == null ? null : descriptionAnnotation.value();
|
||||
AnyGate gate = AnyGate.get(f, this);
|
||||
if (config != null) {
|
||||
if(Button.ClickEvent.class.isAssignableFrom(f.getType())) {
|
||||
try {
|
||||
subComponents.add(new Button(config.value(), ((Button.ClickEvent) f.get(this))).gate(gate));
|
||||
subComponents.add(new Button(config.value(), ((Button.ClickEvent) f.get(this))).gate(gate).hover(description));
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
@ -127,7 +129,7 @@ public abstract class Feature extends ToggleButton implements SetCommand {
|
|||
String[] r = range.value().split("\\.\\.");
|
||||
int min = Integer.parseInt(r[0]);
|
||||
int max = Integer.parseInt(r[1]);
|
||||
subComponents.add(new IntSlider(config.value(), settings, config.value(), Object::toString, max - min, min).gate(gate));
|
||||
subComponents.add(new IntSlider(config.value(), settings, config.value(), Object::toString, max - min, min).gate(gate).hover(description));
|
||||
} else {
|
||||
throw new RuntimeException("No range specified for slider");
|
||||
}
|
||||
|
@ -138,7 +140,7 @@ public abstract class Feature extends ToggleButton implements SetCommand {
|
|||
String[] r = range.value().split("\\.\\.");
|
||||
float min = Float.parseFloat(r[0]);
|
||||
float max = Float.parseFloat(r[1]);
|
||||
subComponents.add(new Slider(config.value(), settings, config.value(), n -> String.valueOf(Math.round(n * 100) / 100f), max - min, min).gate(gate));
|
||||
subComponents.add(new Slider(config.value(), settings, config.value(), n -> String.valueOf(Math.round(n * 100) / 100f), max - min, min).gate(gate).hover(description));
|
||||
} else {
|
||||
throw new RuntimeException("No range specified for slider");
|
||||
}
|
||||
|
@ -155,7 +157,7 @@ public abstract class Feature extends ToggleButton implements SetCommand {
|
|||
}
|
||||
}
|
||||
if(f.getType().isEnum()) {
|
||||
subComponents.add(new EnumButton((Class<? extends Enum<?>>) f.getType(), config.value(), settings, config.value()).gate(gate));
|
||||
subComponents.add(new EnumButton((Class<? extends Enum<?>>) f.getType(), config.value(), settings, config.value()).gate(gate).hover(description));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package com.baseband.client.module.chat;
|
||||
|
||||
import com.baseband.client.configuration.annotation.Config;
|
||||
import com.baseband.client.configuration.annotation.Gate;
|
||||
import com.baseband.client.configuration.annotation.MultiGate;
|
||||
import com.baseband.client.configuration.annotation.Range;
|
||||
import com.baseband.client.configuration.annotation.*;
|
||||
import com.baseband.client.event.events.PacketEvent;
|
||||
import com.baseband.client.gui.lib.component.Button;
|
||||
import com.baseband.client.BaseBand;
|
||||
|
@ -34,7 +31,7 @@ public class ChatCrypt extends Feature {
|
|||
}
|
||||
|
||||
public enum NotifMode {
|
||||
Client,
|
||||
Notification,
|
||||
Chat,
|
||||
Both,
|
||||
}
|
||||
|
@ -45,36 +42,51 @@ public class ChatCrypt extends Feature {
|
|||
}
|
||||
|
||||
@Config("Notification mode")
|
||||
@Description("Where to put received messages (if you select Notification, you can still receive messages with chat hidden/disabled).")
|
||||
public NotifMode mode = NotifMode.Chat;
|
||||
|
||||
@Config("Send")
|
||||
@Description("Encrypt sent messages")
|
||||
public boolean send;
|
||||
|
||||
@Config("Use SBE algorithm (preferred)")
|
||||
@Config("Use SBE algorithm")
|
||||
@Description("Trypt will look more random and has 256 possible encodings for the same message, meaning repetition is harder to see.\n" +
|
||||
"SBE is shorter.")
|
||||
@Marker(1)
|
||||
public boolean useSBE = true;
|
||||
public boolean useSBE = false;
|
||||
@Marker(2)
|
||||
public boolean useTrypt = false;
|
||||
|
||||
@Config("(But Trypt looks more random)")
|
||||
@Marker(-1)
|
||||
@Gate(-1)
|
||||
public boolean _trypt_info = true;
|
||||
|
||||
@Config("Seed")
|
||||
@Range("-4096..4096")
|
||||
public int seed = 256;
|
||||
@Description("(Must be a number or will be hashed into one.)\n" +
|
||||
"PRNG seed to make results less predictable.\n" +
|
||||
"On SBE: Necessary for decreasing the likelihood of undigested bytes.\n" +
|
||||
"On Trypt: Necessary for scrambling byte order and decreasing the likelihood of undigested bytes.")
|
||||
public String sSeed = "94278";
|
||||
|
||||
@Config("Box Size")
|
||||
@Description("The size of the SBE random data box.")
|
||||
@Range("256..4096")
|
||||
@Gate(1)
|
||||
public int boxSize = 256;
|
||||
|
||||
@Config("Scramble (slow)")
|
||||
@Description("Trypt allows for further injection of randomness by taking message bytes and injecting them into the PRNGs.\n" +
|
||||
"Effectively increases entropy for same-length-different-content messages. Otherwise not very necessary.")
|
||||
@Gate(2)
|
||||
@Range("0..64")
|
||||
public int scramble = 0;
|
||||
|
||||
@Config("Allow CC:keep")
|
||||
@Description("Allows players in your ChatCrypt bubble to send CC:keep to synchronize everyone's Trypt instance. \n" +
|
||||
"This resets Trypt, making the first messages very slightly less random.")
|
||||
@Gate(2)
|
||||
public boolean allowCCKeep = true;
|
||||
|
||||
@Config("Keep Trypt instance")
|
||||
@Description("Keeping the Trypt instance between messages allows for significantly more secure transmission of data by making use of its stream capability.\n" +
|
||||
"§lWarning§r: Others using the same key will have to be synchronized, meaning whenever someone joins in, everyone must reset their Trypt instance." +
|
||||
"High ping might also break it.")
|
||||
@Gate(2)
|
||||
@Marker(3)
|
||||
public boolean keepTrypt = false;
|
||||
|
@ -83,7 +95,7 @@ public class ChatCrypt extends Feature {
|
|||
@MultiGate(and = {2, 3})
|
||||
public Button.ClickEvent resetTrypt = btn -> {
|
||||
trypt = null;
|
||||
BaseBand.notify("§c§lChat>§a Trypt instance reset.");
|
||||
BaseBand.notifyAll("§c§lChat>§a Trypt instance reset.");
|
||||
};
|
||||
|
||||
@Config("Password")
|
||||
|
@ -94,9 +106,9 @@ public class ChatCrypt extends Feature {
|
|||
this.send = !this.send;
|
||||
handle.poll("Send");
|
||||
if(send)
|
||||
BaseBand.notify("Sent messages §a§lwill§r be encrypted");
|
||||
BaseBand.notifyAll("Sent messages §a§lwill§r be encrypted");
|
||||
else
|
||||
BaseBand.notify("Sent messages §cwill §lnot§r be encrypted");
|
||||
BaseBand.notifyAll("Sent messages §cwill §lnot§r be encrypted");
|
||||
}, this);
|
||||
|
||||
private Trypt trypt;
|
||||
|
@ -111,9 +123,16 @@ public class ChatCrypt extends Feature {
|
|||
}
|
||||
}
|
||||
|
||||
int seed = -1;
|
||||
|
||||
@Override
|
||||
public void onEveryTick() {
|
||||
useTrypt = !useSBE;
|
||||
try {
|
||||
seed = Integer.parseInt(sSeed);
|
||||
} catch (NumberFormatException e) {
|
||||
seed = sSeed.hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
String sentOriginal = null;
|
||||
|
@ -156,7 +175,7 @@ public class ChatCrypt extends Feature {
|
|||
GlobalUtil.LOGGER.debug("Successfully kept Trypt key up-to-date.");
|
||||
}
|
||||
else {
|
||||
BaseBand.notify("§d§lChat>§c Unable to keep Trypt key up-to-date. Disabled keep." + (allowCCKeep ? " (Enable and sync by sending CC:keep)" : ""));
|
||||
BaseBand.notifyAll("§d§lChat>§c Unable to keep Trypt key up-to-date. Disabled keep." + (allowCCKeep ? " (Enable and sync by sending CC:keep)" : ""));
|
||||
keepTrypt = false;
|
||||
}
|
||||
}
|
||||
|
@ -171,7 +190,7 @@ public class ChatCrypt extends Feature {
|
|||
}
|
||||
|
||||
switch (mode) {
|
||||
case Client:
|
||||
case Notification:
|
||||
e.setCancelled(true);
|
||||
case Both:
|
||||
HUD.notify("§dChat> §r" + username + ": " + message, 15000);
|
||||
|
@ -216,7 +235,7 @@ public class ChatCrypt extends Feature {
|
|||
}
|
||||
else {
|
||||
if(!keepTrypt || trypt == null)
|
||||
trypt = new Trypt(seed, Hasher.sha512hex(password).getBytes(StandardCharsets.UTF_8));
|
||||
trypt = new Trypt(seed, Hasher.sha512hex(password).getBytes(StandardCharsets.UTF_8), scramble);
|
||||
return armorBytes(trypt.encryptChunk(value.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
}
|
||||
|
@ -229,7 +248,7 @@ public class ChatCrypt extends Feature {
|
|||
}
|
||||
else {
|
||||
if(!keepTrypt || trypt == null)
|
||||
trypt = new Trypt(seed, Hasher.sha512hex(password).getBytes(StandardCharsets.UTF_8));
|
||||
trypt = new Trypt(seed, Hasher.sha512hex(password).getBytes(StandardCharsets.UTF_8), scramble);
|
||||
return new String(trypt.decryptChunk(encrypted), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
@ -239,7 +258,7 @@ public class ChatCrypt extends Feature {
|
|||
SBE sbe = new SBE(Hasher.sha512hex(password).getBytes(StandardCharsets.UTF_8), boxSize, seed);
|
||||
return armorBytes(sbe.transform(value.getBytes(StandardCharsets.UTF_8)));
|
||||
} else {
|
||||
return armorBytes(new Trypt(seed, Hasher.sha512hex(password).getBytes(StandardCharsets.UTF_8)).encryptChunk(value.getBytes(StandardCharsets.UTF_8)));
|
||||
return armorBytes(new Trypt(seed, Hasher.sha512hex(password).getBytes(StandardCharsets.UTF_8), 0).encryptChunk(value.getBytes(StandardCharsets.UTF_8)));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -249,7 +268,7 @@ public class ChatCrypt extends Feature {
|
|||
return new String(sbe.transform(encrypted), StandardCharsets.US_ASCII);
|
||||
}
|
||||
else {
|
||||
return new String(new Trypt(seed, Hasher.sha512hex(password).getBytes(StandardCharsets.UTF_8)).decryptChunk(encrypted), StandardCharsets.UTF_8);
|
||||
return new String(new Trypt(seed, Hasher.sha512hex(password).getBytes(StandardCharsets.UTF_8), 0).decryptChunk(encrypted), StandardCharsets.UTF_8);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.baseband.client.module.chat;
|
||||
|
||||
import com.baseband.client.configuration.annotation.Config;
|
||||
import com.baseband.client.configuration.annotation.Description;
|
||||
import com.baseband.client.configuration.annotation.Gate;
|
||||
import com.baseband.client.event.events.PacketEvent;
|
||||
import com.baseband.client.gui.lib.component.Button;
|
||||
|
@ -45,6 +46,7 @@ public class ExtraChat extends Feature {
|
|||
public boolean infinitechat;
|
||||
|
||||
@Config("DM regex")
|
||||
@Description("ADVANCED!!\nThe RegEx used to distinguish DMs from normal messages.")
|
||||
public String dmRegex = "^((To|From|You whisper to) \\w+|\\w+ whispers to you): .*$";
|
||||
|
||||
@Config("Remove DMs now")
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.baseband.client.module.client;
|
||||
|
||||
import com.baseband.client.configuration.annotation.Config;
|
||||
import com.baseband.client.configuration.annotation.Description;
|
||||
import com.baseband.client.event.events.PacketEvent;
|
||||
import com.baseband.client.event.events.PlayerDestroyEvent;
|
||||
import com.baseband.client.gui.GuiTheme;
|
||||
|
@ -20,6 +21,10 @@ public class Client extends Feature {
|
|||
public static EntityLivingBase entityTarget = null;
|
||||
public static EntityLivingBase playerTarget = null;
|
||||
|
||||
@Config("Notification target")
|
||||
@Description("By default, notifications try to go to the HUD and fall back to Chat. You can also set them to go to chat only, or both.")
|
||||
public NotificationDest notificationDest = NotificationDest.HUD;
|
||||
|
||||
@Config("Prefix")
|
||||
public String prefix = "&";
|
||||
|
||||
|
@ -31,6 +36,7 @@ public class Client extends Feature {
|
|||
};
|
||||
|
||||
@Config("ScreenshotUpload")
|
||||
@Description("Uploads screenshots to imgur automatically.")
|
||||
public boolean screenshotUpload;
|
||||
|
||||
@Config("Theme")
|
||||
|
@ -40,7 +46,6 @@ public class Client extends Feature {
|
|||
public KeyBind clearTargetsBind = new KeyBind(null, () -> clearTargets.click(null), this);
|
||||
|
||||
|
||||
|
||||
public GuiTheme.ITheme getTheme() {
|
||||
return theme;
|
||||
}
|
||||
|
@ -117,4 +122,10 @@ public class Client extends Feature {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum NotificationDest {
|
||||
HUD,
|
||||
Chat,
|
||||
Both,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.baseband.client.module.client;
|
||||
|
||||
import com.baseband.client.configuration.annotation.Config;
|
||||
import com.baseband.client.configuration.annotation.Description;
|
||||
import com.baseband.client.configuration.annotation.Gate;
|
||||
import com.baseband.client.configuration.annotation.Range;
|
||||
import com.baseband.client.event.events.PlayerDestroyEvent;
|
||||
|
@ -20,12 +21,16 @@ public class Timer extends Feature {
|
|||
@Range("0.05..7.5")
|
||||
public float m = 0.5f;
|
||||
@Config("Adjust to server")
|
||||
@Description("Adjusts the client's TPS to the server's. Enable either Fasten or Slowdown for this to do anything.\n" +
|
||||
"The client TPS is §lALSO§r still going to be adjusted by the Multiplier.")
|
||||
@Marker(1)
|
||||
public boolean adjust = false;
|
||||
@Config("Fasten")
|
||||
@Description("Allows client to run faster than 20TPS if the server runs at that speed. Might get you flagged.")
|
||||
@Gate(1)
|
||||
public boolean fasten = false;
|
||||
@Config("Slowdown")
|
||||
@Description("Allows client to run slower than 20TPS if the server runs at that speed. This usually doesn't cause any lag-backs, and even prevents them in very low TPS.")
|
||||
@Gate(1)
|
||||
public boolean slowdown = true;
|
||||
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
package com.baseband.client.module.combat;
|
||||
|
||||
import com.baseband.client.configuration.annotation.Config;
|
||||
import com.baseband.client.configuration.annotation.Gate;
|
||||
import com.baseband.client.configuration.annotation.MultiGate;
|
||||
import com.baseband.client.configuration.annotation.Range;
|
||||
import com.baseband.client.configuration.annotation.*;
|
||||
import com.baseband.client.gui.GuiBBIngame;
|
||||
import com.baseband.client.module.Feature;
|
||||
import com.baseband.client.module.category.Combat;
|
||||
|
@ -60,11 +57,17 @@ public class AutoTotem extends Feature {
|
|||
public boolean renderTots = true;
|
||||
|
||||
@Config("Mode")
|
||||
@Description("The mode of AutoTotem:\n" +
|
||||
"Single waits until there is no totem, then replaces it.\n" +
|
||||
"Hypermove constantly moves totems using clicks (only works on unstacked totems). Won't let you use the inventory normally.\n" +
|
||||
"Hyperswap constantly moves totems using swaps (works on any stack size and lets you use inventory). AntiCheat might not like it.\n" +
|
||||
"Stack, which is like TTC's AutoTotem, moves totems using swaps and is optimized for larger stacks.")
|
||||
public Mode mode = Mode.Hypermove;
|
||||
|
||||
/// CONFIG FOR SINGLE
|
||||
|
||||
@Config("Use swap instead of click")
|
||||
@Description("Sometimes, this might be faster or more reliable.")
|
||||
@Gate(M_SINGLE)
|
||||
@Marker(M_SINGLESTACK)
|
||||
public boolean preferSwap = false;
|
||||
|
@ -72,6 +75,7 @@ public class AutoTotem extends Feature {
|
|||
/// CONFIG FOR HYPERSWITCH
|
||||
|
||||
@Config("Speed (ticks/switch)")
|
||||
@Description("Amount of ticks until the next switch. 1 = Every tick, 2 = Every two ticks, etc.")
|
||||
@Range("1..10")
|
||||
@Gate(M_HYPERSWITCH)
|
||||
public int hCooldownAmount = 4 ;
|
||||
|
@ -79,20 +83,25 @@ public class AutoTotem extends Feature {
|
|||
/// CONFIG FOR ^+HYPERSWAP
|
||||
|
||||
@Config("Hotbar slots to use")
|
||||
@Description("How many Hotbar slots may be occupied by switching totems. More is better, but more space intensive.")
|
||||
@Range("1..4")
|
||||
@Gate(M_HYPERSWAP)
|
||||
public int hotbarSlots = 2;
|
||||
|
||||
@Config("Replenish rate (ticks)")
|
||||
@Range("3..20")
|
||||
@Description("How often to check if a totem from HotBar is missing.")
|
||||
@Range("1..20")
|
||||
@Gate(M_HYPERSWAP)
|
||||
public int replenishRate = 7;
|
||||
|
||||
@Config("Redownload inventory")
|
||||
@Description("Use a malformed click packet to force the server to send the inventory every time we the replenish check runs. " +
|
||||
"Might get you kicked at high replenish rates.")
|
||||
@Gate(M_HYPERSWAP)
|
||||
public boolean redownload = true;
|
||||
|
||||
@Config("Stacked totems")
|
||||
@Description("Hyperswap can work with stacked totems, in which case replenishing a slot happens at a set amount instead of 0.")
|
||||
@Gate(M_HYPERSWAP)
|
||||
@Marker(M_HYPERSWAP_STACK)
|
||||
public boolean hsStacked = false;
|
||||
|
@ -100,11 +109,13 @@ public class AutoTotem extends Feature {
|
|||
/// CONFIG FOR STACK
|
||||
|
||||
@Config("Prep count")
|
||||
@Description("When to prepare for a switch by moving new totems into the HotBar.")
|
||||
@Range("0..15")
|
||||
@Gate(M_STACK)
|
||||
public int prepCount = 3;
|
||||
|
||||
@Config("Switch count")
|
||||
@Description("When to perform the switch.")
|
||||
@Range("0..15")
|
||||
@MultiGate(or = {M_STACK, M_HYPERSWAP_STACK})
|
||||
public int switchCount = 4;
|
||||
|
@ -112,6 +123,7 @@ public class AutoTotem extends Feature {
|
|||
/// MULTI
|
||||
|
||||
@Config("Cooldown (ms)")
|
||||
@Description("How long to wait after a switch (to avoid AntiCheat).")
|
||||
@Range("0..1000")
|
||||
@MultiGate(or = {M_STACK, M_SINGLESTACK})
|
||||
public int sCooldownAmount = 200;
|
||||
|
@ -261,7 +273,6 @@ public class AutoTotem extends Feature {
|
|||
private boolean replenishHyperswap() {
|
||||
// sideswapping to replenish hotbar when needed
|
||||
if(replenishCooldown == 0) {
|
||||
replenishCooldown = replenishRate;
|
||||
Container container = mc.player.inventoryContainer;
|
||||
ArrayList<Integer> toFix = new ArrayList<>();
|
||||
for (int i = 0; i < hotbarSlots; i++) {
|
||||
|
@ -273,6 +284,7 @@ public class AutoTotem extends Feature {
|
|||
if (toFix.isEmpty()) {
|
||||
if(redownload) {
|
||||
InventoryUtils.redownload();
|
||||
replenishCooldown = replenishRate;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.baseband.client.module.movement;
|
|||
|
||||
import com.baseband.client.BaseBand;
|
||||
import com.baseband.client.configuration.annotation.Config;
|
||||
import com.baseband.client.configuration.annotation.Description;
|
||||
import com.baseband.client.configuration.annotation.Gate;
|
||||
import com.baseband.client.configuration.annotation.Range;
|
||||
import com.baseband.client.module.Feature;
|
||||
|
@ -25,10 +26,13 @@ public class ElytraBot extends Feature {
|
|||
boolean isCommandMode = true;
|
||||
|
||||
@Config("Target")
|
||||
@Description("Either a player name or a block position to which the bot should fly.")
|
||||
@Gate(1)
|
||||
public String target = "Edit or &ElytraBot [name]";
|
||||
|
||||
@Config("Y offset on players")
|
||||
@Description("Players might stand on the ground, or you might find yourself being annoyed by your own bots interfering with your vision.\n" +
|
||||
"This amount of blocks will be added to the y-position of any player being followed.")
|
||||
@Range("0..4")
|
||||
public float yOffset = 3;
|
||||
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
package com.baseband.client.module.movement;
|
||||
|
||||
import com.baseband.client.configuration.annotation.Config;
|
||||
import com.baseband.client.configuration.annotation.Gate;
|
||||
import com.baseband.client.configuration.annotation.MultiGate;
|
||||
import com.baseband.client.configuration.annotation.Range;
|
||||
import com.baseband.client.Setup;
|
||||
import com.baseband.client.configuration.annotation.*;
|
||||
import com.baseband.client.event.events.MoveEvent;
|
||||
import com.baseband.client.event.events.PacketEvent;
|
||||
import com.baseband.client.module.Feature;
|
||||
|
@ -29,6 +27,7 @@ public class ElytraFly extends Feature {
|
|||
}
|
||||
|
||||
@Config("Mixin (recommended)")
|
||||
@Description("Use the client's mixin for ElytraFly control, which is more accurate but can theoretically be broken by other clients.")
|
||||
@Marker(2)
|
||||
public boolean mixinMode = true;
|
||||
|
||||
|
@ -37,36 +36,48 @@ public class ElytraFly extends Feature {
|
|||
public boolean sounds;
|
||||
|
||||
@Config("AntiKick")
|
||||
@Description("Whether to attempt an evasion of FlightKick. §lNot required for vanilla servers.")
|
||||
@Gate(2)
|
||||
public boolean antiKick;
|
||||
|
||||
@Config("HSpeed")
|
||||
@Description("Horizontal speed in blocks per half second (10t).")
|
||||
@Range("1..50")
|
||||
public int hSpeed = 10;
|
||||
|
||||
@Config("VSpeed")
|
||||
@Description("Vertical speed in blocks per half second (10t).")
|
||||
@Range("1..50")
|
||||
public int vSpeed = 10;
|
||||
|
||||
@Config("Auto Takeoff")
|
||||
@Description("Advanced automatic takeoff functionality. Takes off when enabling the module or when jumping.\n" +
|
||||
"With correct settings, this should only require a single normal-height jump at normal speed to take off.\n" +
|
||||
"With okay ping, this can also take off in 1x2 tunnels.")
|
||||
@Marker(1)
|
||||
public boolean autoTakeoff = false;
|
||||
|
||||
@Config("Post-Takeoff Motion")
|
||||
@Description("Applies some amount of motion to the player after takeoff is complete, just in case the server resets the elytra again due to bad ping.\n" +
|
||||
"This pretty much guarantees success after the second attempt even on bad ping.")
|
||||
@Range("0..3")
|
||||
@Gate(1)
|
||||
public float takeoffMotion = 0.3f;
|
||||
|
||||
@Config("Takeoff Ticks")
|
||||
@Description("How often to attempt a takeoff. This usually doesn't affect success, but lower numbers might trigger AntiCheats, " +
|
||||
"and higher ones might be more forgiving to bad Gate settings.")
|
||||
@Range("1..40")
|
||||
@Gate(1)
|
||||
public int takeoffTicks = 1;
|
||||
|
||||
@Config("Takeoff Gate Mode")
|
||||
@Description("Advanced: How the begin of a fall should be detected. Packet is almost always the better option and can't be misconfigured. Only try changing if something doesn't work.")
|
||||
@Gate(1)
|
||||
public GateMode gateMode = GateMode.Packet;
|
||||
|
||||
@Config("Takeoff Gate Motion")
|
||||
@Description("How much downward motion the player must have to trigger the Gate.")
|
||||
@Range("0..0.5")
|
||||
@MultiGate(and = {1, 2})
|
||||
public float takeoffGateMotion = 0.1f;
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.baseband.client.module.render;
|
||||
|
||||
import com.baseband.client.configuration.annotation.Config;
|
||||
import com.baseband.client.configuration.annotation.Description;
|
||||
import com.baseband.client.configuration.annotation.Gate;
|
||||
import com.baseband.client.gui.GuiRewrite;
|
||||
import com.baseband.client.module.Feature;
|
||||
|
@ -23,9 +24,11 @@ public class ClickGUI extends Feature {
|
|||
public boolean breakWindows = true;
|
||||
|
||||
@Config("Mouse Fix")
|
||||
@Description("(LEGACY) Draws a white dot on the screen where the cursor is, in case your system is weird and hides it. This has a near-zero chance of ever being necessary.")
|
||||
public boolean mouseFix;
|
||||
|
||||
@Config("Save expanded features")
|
||||
@Description("Whether and for how long to save whether each module is expanded or not.")
|
||||
public SaveExpandedMode saveExpanded = SaveExpandedMode.Always;
|
||||
|
||||
@Override
|
||||
|
|
|
@ -8,6 +8,7 @@ import com.baseband.client.module.Feature;
|
|||
import com.baseband.client.module.category.Render;
|
||||
import com.baseband.client.module.client.Client;
|
||||
import com.baseband.client.util.misc.Marker;
|
||||
import com.baseband.client.util.render.TextSplitter;
|
||||
import net.minecraft.client.Minecraft;
|
||||
import net.minecraft.client.gui.Gui;
|
||||
import net.minecraft.client.gui.ScaledResolution;
|
||||
|
@ -21,6 +22,8 @@ import java.util.Comparator;
|
|||
import java.util.Date;
|
||||
|
||||
import static com.baseband.client.BaseBand.features;
|
||||
import static com.baseband.client.util.render.TextSplitter.getStringWidth;
|
||||
import static com.baseband.client.util.render.TextSplitter.renderSplit;
|
||||
|
||||
@Render
|
||||
public class HUD extends Feature {
|
||||
|
@ -67,34 +70,42 @@ public class HUD extends Feature {
|
|||
|
||||
@Config("Notifications")
|
||||
@Marker(1)
|
||||
@Gate(Integer.MIN_VALUE)
|
||||
public boolean notifications = true;
|
||||
|
||||
@Config("Notification location")
|
||||
@Description("Where to display notifications. Left and Right display the most recent at the top, Center displays the most recent at the bottom.")
|
||||
@Gate(1)
|
||||
public NotificationLocation nLocation = NotificationLocation.Center;
|
||||
|
||||
@Config("Advanced")
|
||||
@Description("Show rendering controls for notifications.")
|
||||
@Gate(1)
|
||||
@Marker(2)
|
||||
public boolean advancedNotifs = false;
|
||||
|
||||
@Config("Notification size X")
|
||||
@Description("How wide a notification should be. The width in pixels is this multiplied by 100.")
|
||||
@Range("2..6")
|
||||
@MultiGate(and = {1, 2})
|
||||
@Gate(2)
|
||||
public int nSize = 2;
|
||||
|
||||
@Config("Notification size Y")
|
||||
@Description("How much height a notification should have. This is auto-adjusted for multi-line notifications.\n" +
|
||||
"1 is one line of text only, 2 is half a line of padding on both sides (for a total of 2 lines), 3 is one line of padding, etc.")
|
||||
@Range("1..4")
|
||||
@MultiGate(and = {1, 2})
|
||||
@Gate(2)
|
||||
public int nVSize = 1;
|
||||
|
||||
@Config("Notification spacing")
|
||||
@Description("How many pixels of space to leave between notifications.")
|
||||
@Range("0..20")
|
||||
@MultiGate(and = {1, 2})
|
||||
@Gate(2)
|
||||
public int nVSpace = 1;
|
||||
|
||||
@Config("Test!")
|
||||
@Gate(1)
|
||||
@Description("Click to test your notification rendering settings.")
|
||||
@Gate(2)
|
||||
public Button.ClickEvent testBtn = btn -> notify(btn.text + " " + System.currentTimeMillis());
|
||||
|
||||
@Override
|
||||
|
@ -116,6 +127,7 @@ public class HUD extends Feature {
|
|||
@SubscribeEvent
|
||||
public void text(RenderGameOverlayEvent.Text e) {
|
||||
ScaledResolution sr = new ScaledResolution(mc);
|
||||
TextSplitter.init(mc.fontRenderer);
|
||||
|
||||
mc.fontRenderer.drawStringWithShadow("§lBaseBand§r - \"" + BaseBand.buildString + "\"", 2,2, BaseBand.getFeature(Client.class).getTheme().getGreenColor());
|
||||
int y = 11;
|
||||
|
@ -148,26 +160,15 @@ public class HUD extends Feature {
|
|||
Notification notif = notifs[i];
|
||||
int localYSize = ySize;
|
||||
|
||||
String text = notif.text;
|
||||
while(getStringWidth(text) > xSize - textOffset * 2) {
|
||||
StringBuilder nextLine = new StringBuilder();
|
||||
while(getStringWidth(text) > xSize - textOffset * 2) {
|
||||
int h = text.lastIndexOf(' ');
|
||||
if(h == -1)
|
||||
h = text.length() - 1;
|
||||
nextLine.insert(0, text.substring(h));
|
||||
text = text.substring(0, h);
|
||||
}
|
||||
text += "\n" + nextLine;
|
||||
localYSize += mc.fontRenderer.FONT_HEIGHT;
|
||||
}
|
||||
String text = TextSplitter.breakText(notif.text, xSize - textOffset * 2);
|
||||
localYSize -= TextSplitter.getStringHeight(text) - mc.fontRenderer.FONT_HEIGHT;
|
||||
|
||||
int textboxYSize = localYSize;
|
||||
localYSize = notif.opacity(localYSize, 1);
|
||||
|
||||
drawSizedBox(x, y, xSize, localYSize, 0x202040 + (notif.opacity(0x80, 1) << 24), isCenter == 1);
|
||||
GlStateManager.enableBlend();
|
||||
drawStringL(text, x + textOffset, y + textOffset - isCenter * textboxYSize, 0xffffff + (Math.max(8, notif.opacity(0xff, 2)) << 24), notif.opacity(2) == 1.0);
|
||||
renderSplit(text, x + textOffset, y + textOffset - isCenter * textboxYSize, 0xffffff + (Math.max(8, notif.opacity(0xff, 2)) << 24), notif.opacity(2) == 1.0);
|
||||
GlStateManager.disableBlend();
|
||||
y += (localYSize + nVSpace) * dir;
|
||||
}
|
||||
|
@ -178,25 +179,8 @@ public class HUD extends Feature {
|
|||
Gui.drawRect(x, y, x + sx, y + sy * (backwards ? -1 : 1), color);
|
||||
}
|
||||
|
||||
private int getStringWidth(String text) {
|
||||
int w = 0;
|
||||
for (String s : text.split("\n")) {
|
||||
int width = mc.fontRenderer.getStringWidth(s);
|
||||
if(width > w)
|
||||
w = width;
|
||||
}
|
||||
return w;
|
||||
}
|
||||
|
||||
private void drawString(String s, int x, int y, int color, boolean shadow) {
|
||||
drawStringL(s, x - getStringWidth(s), y, color, shadow);
|
||||
}
|
||||
|
||||
private void drawStringL(String s, int x, int y, int color, boolean shadow) {
|
||||
String[] split = s.split("\n");
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
mc.fontRenderer.drawString(split[i], (float) x, (float) y + mc.fontRenderer.FONT_HEIGHT * i, color, shadow);
|
||||
}
|
||||
private void drawStringCentered(String s, int x, int y, int color, boolean shadow) {
|
||||
renderSplit(s, x - getStringWidth(s), y, color, shadow);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package com.baseband.client.module.world;
|
||||
|
||||
import com.baseband.client.configuration.annotation.Config;
|
||||
import com.baseband.client.configuration.annotation.Description;
|
||||
import com.baseband.client.configuration.annotation.Range;
|
||||
import com.baseband.client.event.events.PacketEvent;
|
||||
import com.baseband.client.module.Feature;
|
||||
|
@ -8,6 +9,7 @@ import com.baseband.client.module.category.World;
|
|||
import net.minecraft.network.play.client.CPacketKeepAlive;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.Objects;
|
||||
import java.util.Queue;
|
||||
|
||||
@World
|
||||
|
@ -16,7 +18,9 @@ public class Ping extends Feature {
|
|||
Queue<PacketHolder> queue = new LinkedList<>();
|
||||
|
||||
@Config("Seconds")
|
||||
@Range("1..100")
|
||||
@Description("How many seconds to add to your ping.\n" +
|
||||
"§lWarning§r: You might get timed out on high settings (Time-out happens at 30 seconds including real ping).")
|
||||
@Range("0..28")
|
||||
int seconds;
|
||||
|
||||
@Override
|
||||
|
@ -28,7 +32,7 @@ public class Ping extends Feature {
|
|||
public void onTick() {
|
||||
if (mc.player != null && !queue.isEmpty() && queue.peek().passed()) {
|
||||
text = "Ping "+ (enabled ? " §7[" + queue.size() +"/" + seconds + "]" : "");
|
||||
mc.player.connection.sendPacket(queue.poll().getPacket());
|
||||
mc.player.connection.sendPacket(Objects.requireNonNull(queue.poll()).getPacket());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package com.baseband.client.module.world;
|
||||
|
||||
import com.baseband.client.BaseBand;
|
||||
import com.baseband.client.module.Feature;
|
||||
import com.baseband.client.module.category.World;
|
||||
import com.baseband.client.module.render.HUD;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
|
||||
@World
|
||||
|
@ -13,13 +13,13 @@ public class Selection extends Feature {
|
|||
public static void select(BlockPos b) {
|
||||
if(begin != null) {
|
||||
end = b;
|
||||
HUD.notify("Position 2: " + b.getX() + " " + b.getY() + " " + b.getZ());
|
||||
BaseBand.notify("Position 2: " + b.getX() + " " + b.getY() + " " + b.getZ());
|
||||
return;
|
||||
}
|
||||
if(end != null) {
|
||||
begin = b;
|
||||
HUD.notify("Selection reset.");
|
||||
HUD.notify("Position 1: " + b.getX() + " " + b.getY() + " " + b.getZ());
|
||||
BaseBand.notify("Selection reset.");
|
||||
BaseBand.notify("Position 1: " + b.getX() + " " + b.getY() + " " + b.getZ());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,15 +5,19 @@ import java.util.ArrayList;
|
|||
import java.util.Random;
|
||||
|
||||
public class Trypt {
|
||||
final SecureRandom sRand = new SecureRandom();
|
||||
final Random cRandE;
|
||||
final Random cRandD;
|
||||
final byte[] key;
|
||||
private final SecureRandom sRand = new SecureRandom();
|
||||
private final Random cRandE;
|
||||
private final Random cRandD;
|
||||
private final long seed;
|
||||
private final byte[] key;
|
||||
private final int scramble;
|
||||
|
||||
public Trypt(long seed, byte[] key) {
|
||||
public Trypt(long seed, byte[] key, int scramble) {
|
||||
cRandE = new Random(seed);
|
||||
cRandD = new Random(seed);
|
||||
this.seed = seed;
|
||||
this.key = key;
|
||||
this.scramble = scramble;
|
||||
}
|
||||
|
||||
public byte[] encryptChunk(byte[] input) {
|
||||
|
@ -39,6 +43,10 @@ public class Trypt {
|
|||
salt = (byte) (salt ^ (newLocation << cRandE.nextInt(8)));
|
||||
}
|
||||
|
||||
for (byte b = 0; b < input[cRandE.nextInt(input.length)]; b++) {
|
||||
cRandE.nextLong();
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
|
||||
|
@ -57,6 +65,10 @@ public class Trypt {
|
|||
salt = (byte) (salt ^ (newLocation << cRandD.nextInt(8)));
|
||||
}
|
||||
|
||||
for (byte b = 0; b < output[cRandD.nextInt(output.length)]; b++) {
|
||||
cRandD.nextLong();
|
||||
}
|
||||
|
||||
return output;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
package com.baseband.client.util.render;
|
||||
|
||||
import com.baseband.client.BaseBand;
|
||||
import net.minecraft.client.gui.FontRenderer;
|
||||
|
||||
public class TextSplitter {
|
||||
|
||||
public static FontRenderer fontRenderer = BaseBand.mc.fontRenderer;
|
||||
|
||||
public static void init(FontRenderer fr) {
|
||||
fontRenderer = fr;
|
||||
}
|
||||
|
||||
public static String limit(String text, int width) {
|
||||
if (fontRenderer.getStringWidth(text) > width) {
|
||||
while (fontRenderer.getStringWidth(text) > width - 5) {
|
||||
text = text.substring(0, text.length() - 1);
|
||||
}
|
||||
text += "...";
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
public static String breakText(String text, int width) {
|
||||
while(getStringWidth(text) > width) {
|
||||
StringBuilder nextLine = new StringBuilder();
|
||||
while(getStringWidth(text) > width) {
|
||||
int h = text.lastIndexOf(' ');
|
||||
if(h == -1)
|
||||
h = text.length() - 1;
|
||||
nextLine.insert(0, text.substring(h));
|
||||
text = text.substring(0, h);
|
||||
}
|
||||
text += "\n" + nextLine;
|
||||
}
|
||||
return text;
|
||||
}
|
||||
|
||||
public static int getStringHeight(String text) {
|
||||
return text.split("\n").length * fontRenderer.FONT_HEIGHT;
|
||||
}
|
||||
|
||||
public static void renderSplit(String text, int x, int y, int color, boolean shadow) {
|
||||
String[] split = text.split("\n");
|
||||
StringBuilder codes = new StringBuilder();
|
||||
for (int i = 0; i < split.length; i++) {
|
||||
fontRenderer.drawString(codes + split[i], (float) x, (float) y + fontRenderer.FONT_HEIGHT * i, color, shadow);
|
||||
codes.append(split[i].replaceAll("[^§]*?(§\\w)?[^§]*", "$1"));
|
||||
}
|
||||
}
|
||||
|
||||
public static int getStringWidth(String text) {
|
||||
int w = 0;
|
||||
for (String s : text.split("\n")) {
|
||||
int width = fontRenderer.getStringWidth(s);
|
||||
if(width > w)
|
||||
w = width;
|
||||
}
|
||||
return w;
|
||||
}
|
||||
}
|
Loading…
Add table
Reference in a new issue