Merge branch 'dev' of https://git.fantabos.co/bscv into dev

This commit is contained in:
frelodev 2024-01-10 23:35:23 +01:00
commit 85f8adb577
20 changed files with 611 additions and 98 deletions

3
.gitignore vendored
View file

@ -18,6 +18,9 @@ build
.gradle
gradle.properties
# ??? gradle again ???
.factorypath
# other
eclipse
run

View file

@ -8,6 +8,7 @@ import ftbsc.bscv.api.IModule;
import ftbsc.bscv.patches.CommandsPatch.CommandsBuiltEvent;
import ftbsc.bscv.system.Friends;
import ftbsc.bscv.system.ModManager;
import ftbsc.bscv.system.Ruler;
import net.minecraft.client.gui.screen.IngameMenuScreen;
import net.minecraft.client.gui.widget.button.Button;
import net.minecraft.command.CommandSource;
@ -42,6 +43,9 @@ public class Boscovicino implements ICommons {
private static Friends friends;
public static Friends friends() { return Boscovicino.friends; }
@SuppressWarnings("unused") // it just needs to exist to be used by player
private static Ruler ruler;
public Boscovicino() {
FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onSetupComplete);
@ -53,6 +57,8 @@ public class Boscovicino implements ICommons {
Boscovicino.modManager.finish();
Boscovicino.ruler = new Ruler();
Boscovicino.spec = cfg.build();
ForgeConfigSpec.Builder friendSpec = new ForgeConfigSpec.Builder();

View file

@ -0,0 +1,57 @@
package ftbsc.bscv.commands;
import com.google.auto.service.AutoService;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import ftbsc.bscv.api.ILoadable;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands;
import static ftbsc.bscv.Boscovicino.log;
@AutoService(ILoadable.class)
public class BlockSearch extends AbstractCommand {
@Override
public String getName() { return "block"; }
public LiteralArgumentBuilder<CommandSource> register(LiteralArgumentBuilder<CommandSource> builder) {
return builder
.then(
Commands.literal("search")
.then(
Commands.argument("id", IntegerArgumentType.integer(0))
.executes( ctx -> {
int block_id = ctx.getArgument("id", Integer.class);
int block_number = block_id >> 4;
int block_meta = block_id & 0b1111;
BlockState state = Block.stateById(block_id);
log("block #[%d:%d]::%d >> %s", block_number, block_meta, block_id, state.toString());
return 1;
})
)
.then(
Commands.argument("number", IntegerArgumentType.integer(0))
.then(
Commands.argument("meta", IntegerArgumentType.integer(0))
.executes( ctx -> {
int block_number = ctx.getArgument("number", Integer.class);
int block_meta = ctx.getArgument("meta", Integer.class);
int block_id = (block_number << 4) | block_meta;
BlockState state = Block.stateById(block_id);
log("block #[%d:%d]::%d >> %s", block_number, block_meta, block_id, state.toString());
return 1;
})
)
)
)
.executes(ctx -> {
log("no block specified");
return 0;
});
}
}

View file

@ -4,6 +4,7 @@ import com.google.auto.service.AutoService;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import ftbsc.bscv.api.ILoadable;
import net.minecraft.block.BlockState;
import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands;
import net.minecraft.util.Direction;
@ -18,17 +19,18 @@ public class Cursor extends AbstractCommand {
public LiteralArgumentBuilder<CommandSource> register(LiteralArgumentBuilder<CommandSource> builder) {
return builder
.then(
Commands.literal("pos")
Commands.literal("info")
.executes(ctx -> {
switch (MC.hitResult.getType()) {
case BLOCK:
BlockRayTraceResult result = (BlockRayTraceResult) MC.hitResult;
Direction dir = result.getDirection();
BlockPos pos =result.getBlockPos();
log("Block @ %s (%s)", pos.toString(), dir.toString());
BlockPos pos = result.getBlockPos();
BlockState state = MC.level.getBlockState(pos);
log("Block @ %s (%s): %s", pos.toString(), dir.toString(), state.toString());
return 1;
case ENTITY:
log("Entity @ %s", MC.hitResult.getLocation().toString());
log("Entity @ %s (TODO!)", MC.hitResult.getLocation().toString());
return 1;
default:
case MISS:

View file

@ -1,6 +1,7 @@
package ftbsc.bscv.commands;
import com.google.auto.service.AutoService;
import com.mojang.brigadier.arguments.IntegerArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import ftbsc.bscv.api.ILoadable;
@ -8,11 +9,15 @@ import ftbsc.bscv.tools.Inventory;
import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.Item;
import static ftbsc.bscv.Boscovicino.log;
@AutoService(ILoadable.class)
public class Item extends AbstractCommand {
public class ItemCommand extends AbstractCommand {
@Override
public String getName() { return "item"; }
public LiteralArgumentBuilder<CommandSource> register(LiteralArgumentBuilder<CommandSource> builder) {
return builder
@ -32,6 +37,17 @@ public class Item extends AbstractCommand {
return 1;
})
)
.then(
Commands.literal("search")
.then(
Commands.argument("id", IntegerArgumentType.integer(0))
.executes(ctx -> {
int item_id = ctx.getArgument("id", Integer.class);
log("item #[%d] >> %s", item_id, Item.byId(item_id).toString());
return 1;
})
)
)
.executes(ctx -> {
Slot slot = Inventory.hotbar(MC.player).get(MC.player.inventory.selected);
if (!slot.hasItem()) return 0;

View file

@ -9,7 +9,7 @@ import ftbsc.bscv.api.IModule;
import net.minecraft.command.CommandSource;
import net.minecraft.command.Commands;
import static ftbsc.bscv.Boscovicino.log;
import java.util.stream.Collectors;
@AutoService(ILoadable.class)
public class ModCommands extends AbstractCommand {
@ -43,8 +43,11 @@ public class ModCommands extends AbstractCommand {
})
)
.executes(ctx -> {
log("no args specified");
return 0;
String mods = Boscovicino.modManager.mods.stream()
.map(x -> x.getName())
.collect(Collectors.joining(","));
Boscovicino.log("[ %s ]", mods);
return 1;
});
}

View file

@ -1,6 +1,7 @@
package ftbsc.bscv.modules;
import ftbsc.bscv.Boscovicino;
import ftbsc.bscv.tools.Keybind;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.client.util.InputMappings;
import net.minecraftforge.client.event.InputEvent;
@ -51,7 +52,7 @@ public abstract class QuickModule extends AbstractModule {
public QuickModule() {
super();
this.keybind = new KeyBinding(key_name(this.getName()), this.getDefaultKey(), key_category());
this.keybind = new KeyBinding(Keybind.name(this.getName()), this.getDefaultKey(), Keybind.category());
ClientRegistry.registerKeyBinding(this.keybind);
// register a separate subclass on the hook, so that it's always listening
@ -76,12 +77,4 @@ public abstract class QuickModule extends AbstractModule {
// );
}
private static String key_name(String name) {
return String.format("key.%s.%s", Boscovicino.MOD_ID, name);
}
private static String key_category() {
return String.format("key.category.%s", Boscovicino.MOD_ID);
}
}

View file

@ -52,7 +52,7 @@ public class Aura extends QuickModule {
this.reach = Setting.Decimal.builder()
.min(0.)
.fallback(5.)
.fallback(4.)
.name("reach")
.comment("aura attack reach")
.build(this);

View file

@ -4,6 +4,7 @@ import com.mojang.blaze3d.matrix.MatrixStack;
import com.mojang.blaze3d.systems.RenderSystem;
import ftbsc.bscv.modules.AbstractModule;
import ftbsc.bscv.tools.Inventory;
import ftbsc.bscv.tools.Setting;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.inventory.container.Slot;
@ -48,38 +49,6 @@ public class Highlighter extends AbstractModule {
this.pattern = Pattern.compile(this.query.get());
}
private List<String> enchantments(ItemStack stack) {
final ListNBT tags;
if (Items.ENCHANTED_BOOK.equals(stack.getItem())) {
tags = EnchantedBookItem.getEnchantments(stack); // special case to also search book enchants
} else {
tags = stack.getEnchantmentTags();
}
List<String> out = new ArrayList<>();
for (int i = 0; i < tags.size(); i++) {
CompoundNBT tag = tags.getCompound(i);
out.add(String.format("%s %s", tag.getString("id"), tag.getInt("lvl")));
}
return out;
}
private boolean matches(ItemStack stack) {
if (stack.isEmpty()) return false;
String displayName = stack.getDisplayName().getString();
if (this.pattern.matcher(displayName).find()) return true;
if (Items.ENCHANTED_BOOK.equals(stack.getItem()) || stack.isEnchanted()) {
for (String ench : this.enchantments(stack)) {
if (this.pattern.matcher(ench).find()) return true;
}
}
return false;
}
@SubscribeEvent
public void onGuiContainerDraw(GuiContainerEvent.DrawBackground event) {
MatrixStack matrix = event.getMatrixStack();
@ -91,7 +60,7 @@ public class Highlighter extends AbstractModule {
for (Slot slot : screen.getMenu().slots) {
ItemStack stack = slot.getItem();
if (this.matches(stack)) {
if (Inventory.matchItem(this.pattern, stack)) {
GuiUtils.drawGradientRect(
matrix.last().pose(), 0,
slot.x, slot.y, slot.x + 16, slot.y + 16,

View file

@ -40,24 +40,24 @@ public class VanillaFlight extends QuickModule {
this.force = Setting.Bool.builder()
.name("force")
.comment("force enable flight on user")
.fallback(false)
.fallback(true)
.build(this);
this.drift = Setting.Bool.builder()
.name("drift")
.comment("gradually reduce momentum")
.fallback(true)
.fallback(false)
.build(this);
this.speed = Setting.Decimal.builder()
.min(0.)
.fallback(0.05)
.fallback(0.1)
.name("speed")
.comment("flight speed to set")
.build(this);
this.antikick = Setting.Switch.builder(AntikickMode.class)
.fallback(AntikickMode.NONE)
.fallback(AntikickMode.PACKET)
.name("antikick")
.comment("prevent vanilla flight kick by descending")
.build(this);

View file

@ -1,11 +1,14 @@
package ftbsc.bscv.modules.self;
import com.google.auto.service.AutoService;
import ftbsc.bscv.Boscovicino;
import ftbsc.bscv.api.ILoadable;
import ftbsc.bscv.modules.AbstractModule;
import ftbsc.bscv.tools.Inventory;
import ftbsc.bscv.tools.Setting;
import net.minecraft.block.BlockState;
import net.minecraft.enchantment.Enchantments;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockRayTraceResult;
@ -19,6 +22,7 @@ import java.util.List;
public class AutoTool extends AbstractModule {
public final ForgeConfigSpec.ConfigValue<Integer> limit;
public final ForgeConfigSpec.ConfigValue<Boolean> prefer_looting;
public AutoTool() {
super();
@ -28,6 +32,12 @@ public class AutoTool extends AbstractModule {
.comment("durability limit for tools, set to 0 to destroy them")
.fallback(1)
.build(this);
this.prefer_looting = Setting.Bool.builder()
.name("prefer-looting")
.comment("when picking best weapon, prefer looting over slight more DPS")
.fallback(true)
.build(this);
}
private boolean itemIsTooDamaged(ItemStack item) {
@ -47,6 +57,12 @@ public class AutoTool extends AbstractModule {
}
double damage = Inventory.itemDPS(item);
int looting = Inventory.getEnchLevel(item, Enchantments.MOB_LOOTING);
if (this.prefer_looting.get() && looting > 0) {
damage += 0.1 * looting;
}
if (damage > current_damage) {
current_slot = i;
current_damage = damage;

View file

@ -0,0 +1,79 @@
package ftbsc.bscv.modules.self;
import java.util.ArrayList;
import java.util.List;
import com.google.auto.service.AutoService;
import ftbsc.bscv.api.ILoadable;
import ftbsc.bscv.modules.AbstractModule;
import ftbsc.bscv.tools.Inventory;
import ftbsc.bscv.tools.Setting;
import net.minecraft.client.gui.screen.inventory.ContainerScreen;
import net.minecraft.command.arguments.ItemArgument;
import net.minecraft.command.arguments.ItemInput;
import net.minecraft.inventory.container.ClickType;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.Item;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.event.TickEvent.ClientTickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
@AutoService(ILoadable.class)
public class ContainerCleaner extends AbstractModule {
public final ForgeConfigSpec.ConfigValue<Integer> cooldown;
public final ForgeConfigSpec.ConfigValue<Boolean> limit;
public final ForgeConfigSpec.ConfigValue<Boolean> all;
public final ForgeConfigSpec.ConfigValue<List<? extends Integer>> blacklist;
private int counter;
public ContainerCleaner() {
super();
this.counter = 0;
this.cooldown = Setting.Number.builder()
.fallback(0)
.name("cooldown")
.comment("ticks before throwing next item")
.build(this);
this.limit = Setting.Bool.builder()
.fallback(true)
.name("limit")
.comment("limit to one action per tick")
.build(this);
this.all = Setting.Bool.builder()
.fallback(true)
.name("all")
.comment("throw whole stacks instead of single items")
.build(this);
this.blacklist = new Setting.Many<ItemInput, Integer>(ItemArgument.item(), ItemInput.class)
.writer(x -> Item.getId(x.getItem()))
.fallback(new ArrayList<Integer>())
.name("blacklist")
.comment("items to throw away")
.build(this);
}
@SubscribeEvent
public void onTick(ClientTickEvent event) {
if (MC.screen == null) return;
if (!(MC.screen instanceof ContainerScreen)) return;
if (this.counter > 0) {
this.counter -= 1;
return;
}
ContainerScreen<?> screen = (ContainerScreen<?>) MC.screen;
for (Slot slot : screen.getMenu().slots) {
if (this.blacklist.get().contains(Item.getId(slot.getItem().getItem()))) {
int button = this.all.get() ? 1 : 0;
Inventory.clickSlot(screen.getMenu().containerId, slot.index, button, ClickType.THROW);
this.counter = this.cooldown.get();
if (this.limit.get()) return; // only throw one item per tick
}
}
}
}

View file

@ -0,0 +1,39 @@
package ftbsc.bscv.modules.self;
import com.google.auto.service.AutoService;
import ftbsc.bscv.api.ILoadable;
import ftbsc.bscv.modules.AbstractModule;
import ftbsc.bscv.tools.Inventory;
import ftbsc.bscv.tools.Setting;
import net.minecraft.client.gui.screen.inventory.InventoryScreen;
import net.minecraft.inventory.container.ClickType;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.event.TickEvent.ClientTickEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
@AutoService(ILoadable.class)
public class FastCraft extends AbstractModule {
public final ForgeConfigSpec.ConfigValue<Boolean> drop;
public FastCraft() {
super();
this.drop = Setting.Bool.builder()
.fallback(false)
.name("drop")
.comment("throw cradted items away instead of moving them back in inventory")
.build(this);
}
@SubscribeEvent
public void onTick(ClientTickEvent event) {
if (MC.screen == null) return;
if (!(MC.screen instanceof InventoryScreen)) return;
InventoryScreen inventory = (InventoryScreen) MC.screen;
if (inventory.getMenu().slots.get(0).hasItem()) {
// TODO can we throw them all? like this it throws one by one
Inventory.clickSlot(0, this.drop.get() ? ClickType.THROW : ClickType.QUICK_MOVE);
}
}
}

View file

@ -0,0 +1,72 @@
package ftbsc.bscv.modules.self;
import com.google.auto.service.AutoService;
import ftbsc.bscv.api.ILoadable;
import ftbsc.bscv.modules.AbstractModule;
import ftbsc.bscv.tools.Setting;
import net.minecraftforge.client.event.RenderHandEvent;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.eventbus.api.SubscribeEvent;
@AutoService(ILoadable.class)
public class HandChanger extends AbstractModule {
public final ForgeConfigSpec.ConfigValue<Double> main;
public final ForgeConfigSpec.ConfigValue<Double> off;
public final ForgeConfigSpec.ConfigValue<Boolean> cancel_main;
public final ForgeConfigSpec.ConfigValue<Boolean> cancel_off;
public HandChanger() {
super();
this.main = Setting.Decimal.builder()
.min(0.)
.max(1.)
.name("main")
.comment("height of main hand")
.fallback(1.)
.build(this);
this.off = Setting.Decimal.builder()
.min(0.)
.max(1.)
.name("off")
.comment("height of off hand")
.fallback(1.)
.build(this);
this.cancel_main = Setting.Bool.builder()
.name("cancel-main")
.comment("completely prevent main hand rendering")
.fallback(false)
.build(this);
this.cancel_off = Setting.Bool.builder()
.name("cancel-off")
.comment("completely prevent off hand rendering")
.fallback(false)
.build(this);
}
@SubscribeEvent
public void onRenderHands(RenderHandEvent event) {
switch (event.getHand()) {
case MAIN_HAND:
if (this.cancel_main.get()) {
event.setCanceled(true);
} else if (this.main.get() != 1.0) {
MC.gameRenderer.itemInHandRenderer.mainHandHeight = this.main.get().floatValue(); // ACCESSTRANSFORMER public net.minecraft.client.renderer.FirstPersonRenderer field_187469_f
MC.gameRenderer.itemInHandRenderer.oMainHandHeight = this.main.get().floatValue(); // ACCESSTRANSFORMER public net.minecraft.client.renderer.FirstPersonRenderer field_187469_f
}
break;
case OFF_HAND:
if (this.cancel_off.get()) {
event.setCanceled(true);
} else if (this.off.get() != 1.0) {
MC.gameRenderer.itemInHandRenderer.offHandHeight = this.off.get().floatValue(); // ACCESSTRANSFORMER public net.minecraft.client.renderer.FirstPersonRenderer field_187470_g
MC.gameRenderer.itemInHandRenderer.oOffHandHeight = this.off.get().floatValue(); // ACCESSTRANSFORMER public net.minecraft.client.renderer.FirstPersonRenderer field_187470_g
}
break;
}
}
}

View file

@ -1,4 +1,4 @@
package ftbsc.bscv.modules.hud;
package ftbsc.bscv.modules.self;
import com.google.auto.service.AutoService;

View file

@ -0,0 +1,85 @@
package ftbsc.bscv.system;
import ftbsc.bscv.ICommons;
import ftbsc.bscv.tools.Keybind;
import net.minecraft.client.settings.KeyBinding;
import net.minecraft.client.util.InputMappings;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.RayTraceResult.Type;
import net.minecraftforge.client.event.InputEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.client.registry.ClientRegistry;
import static ftbsc.bscv.Boscovicino.log;
public class Ruler implements ICommons {
public final KeyBinding keybind;
public Ruler() {
super();
this.keybind = new KeyBinding(Keybind.name("Ruler"), InputMappings.UNKNOWN.getValue(), Keybind.category());
ClientRegistry.registerKeyBinding(this.keybind);
// register a separate subclass on the hook, so that it's always listening
MinecraftForge.EVENT_BUS.register(new ToggleHook(this.keybind));
// dispatcher.register(
// Commands.literal(this.name.toLowerCase())
// .then(
// Commands.literal("bind")
// .then(
// Commands.argument("key", StringArgumentType.word())
// .executes( ctx -> {
// this.keybind.setKey(
// InputMappings.getKey( // TODO it's not this easy!
// StringArgumentType.getString(ctx, "key")
// )
// );
// return 1;
// })
// )
// )
// );
}
public static void measure() {
RayTraceResult aim = MC.player.pick(1024, 0, false); // will 1024 be enough?
double distance = Math.sqrt(aim.distanceTo(MC.player));
if (aim.getType() == Type.BLOCK) {
log("distance: %.1fm", distance);
}
}
// TODO can this be made an util or a global event listener?
private class ToggleHook {
private final KeyBinding key;
private boolean debounce;
// TODO all examples show isPressed() to get a debounced value
// but it seems to be missing? making my own debounce for now
protected ToggleHook(KeyBinding key) {
this.key = key;
this.debounce = false;
}
private void onInput() {
if (this.debounce) {
if (!this.key.isDown()) {
this.debounce = false;
}
} else {
if (this.key.isDown()) {
Ruler.measure();
this.debounce = true;
}
}
}
@SubscribeEvent
public void onKeyPress(InputEvent.KeyInputEvent event) { this.onInput(); }
@SubscribeEvent
public void onKeyPress(InputEvent.MouseInputEvent event) { this.onInput(); }
}
}

View file

@ -7,13 +7,19 @@ import net.minecraft.enchantment.Enchantments;
import net.minecraft.entity.ai.attributes.AttributeModifier;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.inventory.EquipmentSlotType;
import net.minecraft.inventory.container.ClickType;
import net.minecraft.inventory.container.Slot;
import net.minecraft.item.EnchantedBookItem;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.nbt.INBT;
import net.minecraft.nbt.ListNBT;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;
public class Inventory implements ICommons {
@ -68,4 +74,44 @@ public class Inventory implements ICommons {
return damage / (1. + speed);
}
public static List<String> itemEnchantments(ItemStack stack) {
final ListNBT tags;
if (Items.ENCHANTED_BOOK.equals(stack.getItem())) {
tags = EnchantedBookItem.getEnchantments(stack); // special case to also search book enchants
} else {
tags = stack.getEnchantmentTags();
}
List<String> out = new ArrayList<>();
for (int i = 0; i < tags.size(); i++) {
CompoundNBT tag = tags.getCompound(i);
out.add(String.format("%s %s", tag.getString("id"), tag.getInt("lvl")));
}
return out;
}
public static void clickSlot(int slot_index, ClickType click) { clickSlot(0, slot_index, 0, click); }
public static void clickSlot(Slot slot, int button, ClickType click) { clickSlot(0, slot.index, button, click); }
public static void clickSlot(int container, int slot_index, ClickType click) { clickSlot(container, slot_index, 0, click); }
public static void clickSlot(int container, int slot_index, int button, ClickType click) {
MC.gameMode.handleInventoryMouseClick(container, slot_index, button, click, MC.player);
}
public static boolean matchItem(Pattern pattern, ItemStack stack) {
if (stack.isEmpty()) return false;
String displayName = stack.getDisplayName().getString();
if (pattern.matcher(displayName).find()) return true;
if (Items.ENCHANTED_BOOK.equals(stack.getItem()) || stack.isEnchanted()) {
for (String ench : itemEnchantments(stack)) {
if (pattern.matcher(ench).find()) return true;
}
}
return false;
}
}

View file

@ -0,0 +1,13 @@
package ftbsc.bscv.tools;
import ftbsc.bscv.Boscovicino;
public class Keybind {
public static String name(String name) {
return String.format("key.%s.%s", Boscovicino.MOD_ID, name);
}
public static String category() {
return String.format("key.category.%s", Boscovicino.MOD_ID);
}
}

View file

@ -1,23 +1,28 @@
package ftbsc.bscv.tools;
import com.google.common.collect.Lists;
import com.mojang.brigadier.arguments.*;
import ftbsc.bscv.api.IModule;
import net.minecraft.command.Commands;
import net.minecraftforge.common.ForgeConfigSpec;
import net.minecraftforge.server.command.EnumArgument;
import java.io.Serializable;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import static ftbsc.bscv.Boscovicino.log;
public abstract class Setting<T> {
public abstract class Setting<T, I> {
protected Optional<String> name;
protected Optional<String> comment;
protected Optional<T> fallback;
protected Optional<Consumer<T>> callback;
protected Optional<Consumer<I>> callback;
Setting() {
this.name = Optional.empty();
@ -26,29 +31,35 @@ public abstract class Setting<T> {
this.callback = Optional.empty();
}
public Setting<T> name(String name) {
public Setting<T, I> name(String name) {
this.name = Optional.of(name);
return this;
}
public Setting<T> comment(String comment) {
public Setting<T, I> comment(String comment) {
this.comment = Optional.of(comment);
return this;
}
public Setting<T> fallback(T fallback) {
public Setting<T, I> fallback(T fallback) {
this.fallback = Optional.of(fallback);
return this;
}
public Setting<T> callback(Consumer<T> callback) {
public Setting<T, I> callback(Consumer<I> callback) {
this.callback = Optional.of(callback);
return this;
}
abstract ForgeConfigSpec.ConfigValue<T> value(ForgeConfigSpec.Builder builder);
abstract ArgumentType<T> argument();
abstract ArgumentType<I> argument();
public abstract ForgeConfigSpec.ConfigValue<T> build(IModule module);
private static abstract class ValueSetting<T> extends Setting<T, T> {
abstract Class<T> clazz();
@ -84,10 +95,11 @@ public abstract class Setting<T> {
);
return conf;
}
}
public static class Bool extends Setting<Boolean> {
public static class Bool extends ValueSetting<Boolean> {
public static Bool builder() { return new Bool(); }
public Class<Boolean> clazz() { return Boolean.class; }
@ -101,7 +113,7 @@ public abstract class Setting<T> {
}
public static class Decimal extends Setting<Double> {
public static class Decimal extends ValueSetting<Double> {
protected Optional<Double> min;
protected Optional<Double> max;
@ -133,7 +145,7 @@ public abstract class Setting<T> {
}
public static class Number extends Setting<Integer> {
public static class Number extends ValueSetting<Integer> {
protected Optional<Integer> min;
protected Optional<Integer> max;
@ -165,7 +177,7 @@ public abstract class Setting<T> {
}
public static class Str extends Setting<String> {
public static class Str extends ValueSetting<String> {
public static Str builder() { return new Str(); }
protected boolean greedy = false;
@ -190,7 +202,7 @@ public abstract class Setting<T> {
}
public static class Switch<T extends Enum<T>> extends Setting<T> {
public static class Switch<T extends Enum<T>> extends ValueSetting<T> {
private final Class<T> enumClazz;
public static<T extends Enum<T>> Switch<T> builder(Class<T> type) { return new Switch<T>(type); }
@ -214,4 +226,102 @@ public abstract class Setting<T> {
}
}
public static class Many<T, S extends Serializable> extends Setting<List<? extends S>, T> {
private Predicate<Object> validator;
private Function<T, S> writer;
private final ArgumentType<T> argument_type;
private final Class<T> clazz;
// TODO can we infer clazz from the argument type without needing the second argument???
public Many(ArgumentType<T> argument, Class<T> clazz) {
super();
this.validator = x -> true;
this.writer = x -> (S) x; // TODO this works ootb if it's just the same type but crashes at runtime for everything else
this.argument_type = argument;
this.clazz = clazz;
}
public static<T, S extends Serializable> Many<T, S> builder(ArgumentType<T> argument, Class<T> clazz) { return new Many<T, S>(argument, clazz); }
public Many<T, S> validator(Predicate<Object> validator) {
this.validator = validator;
return this;
}
public Many<T, S> writer(Function<T, S> writer) {
this.writer = writer;
return this;
}
public ArgumentType<T> argument() {
return this.argument_type;
}
public ForgeConfigSpec.ConfigValue<List<? extends S>> value(ForgeConfigSpec.Builder builder) {
return builder
.comment(this.comment.get())
.defineList(
this.name.get(),
this.fallback.get(),
this.validator
);
}
public ForgeConfigSpec.ConfigValue<List<? extends S>> build(IModule module) {
ForgeConfigSpec.ConfigValue<List<? extends S>> conf = this.value(module.getConfigBuilder());
String optName = this.name.get();
ArgumentType<T> arg = this.argument();
Consumer<T> cb = this.callback.isPresent() ? this.callback.get() : null;
module.getDispatcher().register(
Commands.literal(module.getName().toLowerCase())
.then(
Commands.literal(optName)
.then(
Commands.literal("add")
.then(
Commands.argument(optName, arg)
.executes( ctx -> {
T value = ctx.getArgument(optName, this.clazz);
if (cb != null) {
cb.accept(value);
}
List<S> botch = Lists.newArrayList(conf.get());
botch.add(this.writer.apply(value));
conf.set(botch);
conf.save();
log(String.format("> %s -++> %s <", String.join(".", conf.getPath()), conf.get().toString()));
return 1;
})
)
)
.then(
Commands.literal("remove")
.then(
Commands.argument(optName, arg)
.executes( ctx -> {
T value = ctx.getArgument(optName, clazz);
if (cb != null) {
cb.accept(value);
}
List<S> botch = Lists.newArrayList(conf.get());
boolean removed = botch.remove(this.writer.apply(value));
conf.set(botch);
conf.save();
log(String.format("> %s -%s> %s <", String.join(".", conf.getPath()), removed ? "//" : "--", conf.get().toString()));
return 1;
})
)
)
.executes(ctx -> {
log(String.format("> %s: %s <", optName, conf.get().toString()));
return 1;
})
)
);
return conf;
}
}
}

View file

@ -5,3 +5,7 @@ public net.minecraft.network.play.client.CPlayerPacket field_149474_g # onGround
public net.minecraft.network.play.client.CPlayerPacket field_149477_b # y
public net.minecraft.client.multiplayer.PlayerController func_78750_j()V # ensureHasSentCarriedItem()
public net.minecraft.client.Minecraft field_71467_ac # rightClickDelay
public net.minecraft.client.renderer.FirstPersonRenderer field_187469_f # mainHandHeight
public net.minecraft.client.renderer.FirstPersonRenderer field_187470_g # oMainHandHeight
public net.minecraft.client.renderer.FirstPersonRenderer field_187471_h # offHandHeight
public net.minecraft.client.renderer.FirstPersonRenderer field_187472_i # oOffHandHeight