Config support

can prob be improved but that will be done later :trollface:
This commit is contained in:
chipmunk 2023-03-11 21:56:16 -05:00
parent 2023ae3cd1
commit 88dc82eb1e
17 changed files with 738 additions and 664 deletions

View file

@ -1,21 +1,64 @@
package land.chipmunk.chipmunkmod; package land.chipmunk.chipmunkmod;
import net.fabricmc.api.ModInitializer; import net.fabricmc.api.ModInitializer;
import org.slf4j.Logger; import java.io.InputStream;
import org.slf4j.LoggerFactory; import java.io.FileInputStream;
import java.io.InputStreamReader;
public class ChipmunkMod implements ModInitializer { import java.io.BufferedReader;
// This logger is used to write text to the console and the log file. import java.io.BufferedWriter;
// It is considered best practice to use your mod id as the logger's name. import java.io.File;
// That way, it's clear which mod wrote info, warnings, and errors. import java.io.FileWriter;
public static final Logger LOGGER = LoggerFactory.getLogger("chipmunkmod"); import java.io.IOException;
import org.slf4j.Logger;
@Override import org.slf4j.LoggerFactory;
public void onInitialize () { import com.google.gson.Gson;
// This code runs as soon as Minecraft is in a mod-load-ready state.
// However, some things (like resources) may still be uninitialized. public class ChipmunkMod implements ModInitializer {
// Proceed with mild caution. // This logger is used to write text to the console and the log file.
// It is considered best practice to use your mod id as the logger's name.
LOGGER.info("Hello Fabric world!"); // That way, it's clear which mod wrote info, warnings, and errors.
} public static final Logger LOGGER = LoggerFactory.getLogger("chipmunkmod");
} public static Configuration CONFIG;
@Override
public void onInitialize () {
// This code runs as soon as Minecraft is in a mod-load-ready state.
// However, some things (like resources) may still be uninitialized.
// Proceed with mild caution.
try {
new File("config").mkdirs(); // TODO: Clean this up
CONFIG = loadConfig();
} catch (IOException exception) {
throw new RuntimeException("Could not load the config", exception);
}
LOGGER.info("Hello Fabric world!");
}
public static Configuration loadConfig () throws IOException {
final Gson gson = new Gson();
final File file = new File("config/chipmunkmod.json");
if (!file.exists()) {
InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("default_config.json");
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
final StringBuilder sb = new StringBuilder();
while (reader.ready()) sb.append((char) reader.read());
final String defaultConfig = sb.toString();
// Write the default config
BufferedWriter configWriter = new BufferedWriter(new FileWriter(file));
configWriter.write(defaultConfig);
configWriter.close();
return gson.fromJson(defaultConfig, Configuration.class);
}
InputStream is = new FileInputStream(file);
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
return gson.fromJson(reader, Configuration.class);
}
}

View file

@ -0,0 +1,17 @@
package land.chipmunk.chipmunkmod;
import land.chipmunk.chipmunkmod.data.BlockArea;
import net.minecraft.util.math.BlockPos;
public class Configuration {
public CommandManager commands = new CommandManager();
public CommandCore core = new CommandCore();
public class CommandManager {
public String prefix = ".";
}
public class CommandCore {
public BlockArea relativeArea = new BlockArea(new BlockPos(0, 0, 0), new BlockPos(15, 0, 15));
}
}

View file

@ -1,73 +1,74 @@
package land.chipmunk.chipmunkmod.command; package land.chipmunk.chipmunkmod.command;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.arguments.ArgumentType; import com.mojang.brigadier.arguments.ArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder; import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.builder.RequiredArgumentBuilder; import com.mojang.brigadier.builder.RequiredArgumentBuilder;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.command.CommandException; import net.minecraft.command.CommandException;
import net.minecraft.text.ClickEvent; import net.minecraft.text.ClickEvent;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.text.Texts; import net.minecraft.text.Texts;
import net.minecraft.text.MutableText; import net.minecraft.text.MutableText;
import net.minecraft.util.Formatting; import net.minecraft.util.Formatting;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import land.chipmunk.chipmunkmod.commands.*; import land.chipmunk.chipmunkmod.ChipmunkMod;
import land.chipmunk.chipmunkmod.commands.*;
public class CommandManager {
public static CommandDispatcher<FabricClientCommandSource> dispatcher = new CommandDispatcher(); public class CommandManager {
public static String prefix = "."; public static CommandDispatcher<FabricClientCommandSource> dispatcher = new CommandDispatcher();
public static String prefix = ChipmunkMod.CONFIG.commands.prefix;
public static void executeCommand (String command) {
final MinecraftClient client = MinecraftClient.getInstance(); public static void executeCommand (String command) {
final MinecraftClient client = MinecraftClient.getInstance();
final FabricClientCommandSource commandSource = (FabricClientCommandSource) client.getNetworkHandler().getCommandSource();
final FabricClientCommandSource commandSource = (FabricClientCommandSource) client.getNetworkHandler().getCommandSource();
try {
dispatcher.execute(command, commandSource); try {
} catch (CommandSyntaxException e) { dispatcher.execute(command, commandSource);
commandSource.sendError(Texts.toText(e.getRawMessage())); } catch (CommandSyntaxException e) {
final Text context = getContext(e); commandSource.sendError(Texts.toText(e.getRawMessage()));
if (context != null) commandSource.sendError(context); final Text context = getContext(e);
} catch (CommandException e) { if (context != null) commandSource.sendError(context);
commandSource.sendError(e.getTextMessage()); } catch (CommandException e) {
} catch (RuntimeException e) { commandSource.sendError(e.getTextMessage());
commandSource.sendError(Text.of(e.getMessage())); } catch (RuntimeException e) {
} commandSource.sendError(Text.of(e.getMessage()));
} }
}
public static Text getContext (CommandSyntaxException exception) {
final int _cursor = exception.getCursor(); public static Text getContext (CommandSyntaxException exception) {
final String input = exception.getInput(); final int _cursor = exception.getCursor();
final String input = exception.getInput();
if (input == null || _cursor < 0) {
return null; if (input == null || _cursor < 0) {
} return null;
final MutableText text = Text.literal("") }
.formatted(Formatting.GRAY); final MutableText text = Text.literal("")
text.setStyle(text.getStyle().withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, prefix + input))); .formatted(Formatting.GRAY);
text.setStyle(text.getStyle().withClickEvent(new ClickEvent(ClickEvent.Action.SUGGEST_COMMAND, prefix + input)));
final int cursor = Math.min(input.length(), _cursor);
final int cursor = Math.min(input.length(), _cursor);
if (cursor > CommandSyntaxException.CONTEXT_AMOUNT) {
text.append(Text.literal("...")); if (cursor > CommandSyntaxException.CONTEXT_AMOUNT) {
} text.append(Text.literal("..."));
}
text
.append(Text.literal(input.substring(Math.max(0, cursor - CommandSyntaxException.CONTEXT_AMOUNT), cursor))) text
.append(Text.literal(input.substring(cursor)).formatted(Formatting.RED, Formatting.UNDERLINE)) .append(Text.literal(input.substring(Math.max(0, cursor - CommandSyntaxException.CONTEXT_AMOUNT), cursor)))
.append(Text.translatable("command.context.here").formatted(Formatting.RED, Formatting.ITALIC)); .append(Text.literal(input.substring(cursor)).formatted(Formatting.RED, Formatting.UNDERLINE))
.append(Text.translatable("command.context.here").formatted(Formatting.RED, Formatting.ITALIC));
return text;
} return text;
}
public static LiteralArgumentBuilder<FabricClientCommandSource> literal (String name) { return LiteralArgumentBuilder.<FabricClientCommandSource>literal(name); }
public static <T> RequiredArgumentBuilder<FabricClientCommandSource, T> argument (String name, ArgumentType<T> type) { return RequiredArgumentBuilder.<FabricClientCommandSource, T>argument(name, type); } public static LiteralArgumentBuilder<FabricClientCommandSource> literal (String name) { return LiteralArgumentBuilder.<FabricClientCommandSource>literal(name); }
public static <T> RequiredArgumentBuilder<FabricClientCommandSource, T> argument (String name, ArgumentType<T> type) { return RequiredArgumentBuilder.<FabricClientCommandSource, T>argument(name, type); }
static {
TestCommand.register(dispatcher); static {
CoreCommand.register(dispatcher); TestCommand.register(dispatcher);
UsernameCommand.register(dispatcher); CoreCommand.register(dispatcher);
} UsernameCommand.register(dispatcher);
}
} }

View file

@ -1,79 +1,79 @@
package land.chipmunk.chipmunkmod.commands; package land.chipmunk.chipmunkmod.commands;
import com.mojang.brigadier.Command; import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import static land.chipmunk.chipmunkmod.command.CommandManager.literal; import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
import static land.chipmunk.chipmunkmod.command.CommandManager.argument; import static land.chipmunk.chipmunkmod.command.CommandManager.argument;
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
import static com.mojang.brigadier.arguments.StringArgumentType.getString; import static com.mojang.brigadier.arguments.StringArgumentType.getString;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import land.chipmunk.chipmunkmod.modules.CommandCore; import land.chipmunk.chipmunkmod.modules.CommandCore;
public class CoreCommand { public class CoreCommand {
public static void register (CommandDispatcher<FabricClientCommandSource> dispatcher) { public static void register (CommandDispatcher<FabricClientCommandSource> dispatcher) {
dispatcher.register( dispatcher.register(
literal("core") literal("core")
.then( .then(
literal("run") literal("run")
.then( .then(
argument("command", greedyString()) argument("command", greedyString())
.executes(c -> run(c)) .executes(c -> run(c))
) )
) )
.then( .then(
literal("runTracked") literal("runTracked")
.then( .then(
argument("command", greedyString()) argument("command", greedyString())
.executes(c -> runTracked(c)) .executes(c -> runTracked(c))
) )
) )
.then(literal("refill").executes(c -> refill(c))) .then(literal("refill").executes(c -> refill(c)))
.then(literal("move").executes(c -> move(c))) .then(literal("move").executes(c -> move(c)))
); );
} }
public static int run (CommandContext<FabricClientCommandSource> context) { public static int run (CommandContext<FabricClientCommandSource> context) {
CommandCore.INSTANCE.run(getString(context, "command")); CommandCore.INSTANCE.run(getString(context, "command"));
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
} }
public static int runTracked (CommandContext<FabricClientCommandSource> context) { public static int runTracked (CommandContext<FabricClientCommandSource> context) {
final FabricClientCommandSource source = context.getSource(); final FabricClientCommandSource source = context.getSource();
final String command = getString(context, "command"); final String command = getString(context, "command");
final CompletableFuture<NbtCompound> future = CommandCore.INSTANCE.runTracked(command); final CompletableFuture<NbtCompound> future = CommandCore.INSTANCE.runTracked(command);
future.thenApply(tag -> { future.thenApply(tag -> {
try { try {
final String output = tag.getString("LastOutput"); final String output = tag.getString("LastOutput");
if (output != null) source.sendFeedback(Text.Serializer.fromJson(output)); if (output != null) source.sendFeedback(Text.Serializer.fromJson(output));
} catch (Exception ignored) { } catch (Exception ignored) {
} }
return tag; return tag;
}); });
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
} }
public static int refill (CommandContext<FabricClientCommandSource> context) { public static int refill (CommandContext<FabricClientCommandSource> context) {
CommandCore.INSTANCE.refill(); CommandCore.INSTANCE.refill();
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
} }
public static int move (CommandContext<FabricClientCommandSource> context) { public static int move (CommandContext<FabricClientCommandSource> context) {
final FabricClientCommandSource source = context.getSource(); final FabricClientCommandSource source = context.getSource();
CommandCore.INSTANCE.move(source.getClient().player.getPos()); CommandCore.INSTANCE.move(source.getClient().player.getPos());
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
} }
} }

View file

@ -1,24 +1,24 @@
package land.chipmunk.chipmunkmod.commands; package land.chipmunk.chipmunkmod.commands;
import com.mojang.brigadier.Command; import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import static land.chipmunk.chipmunkmod.command.CommandManager.literal; import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.text.Text; import net.minecraft.text.Text;
public class TestCommand { public class TestCommand {
public static void register (CommandDispatcher<FabricClientCommandSource> dispatcher) { public static void register (CommandDispatcher<FabricClientCommandSource> dispatcher) {
dispatcher.register( dispatcher.register(
literal("test") literal("test")
.executes(c -> helloWorld(c)) .executes(c -> helloWorld(c))
); );
} }
public static int helloWorld (CommandContext<FabricClientCommandSource> context) { public static int helloWorld (CommandContext<FabricClientCommandSource> context) {
final FabricClientCommandSource source = context.getSource(); final FabricClientCommandSource source = context.getSource();
source.sendFeedback(Text.literal("Hello, world!")); source.sendFeedback(Text.literal("Hello, world!"));
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
} }
} }

View file

@ -1,67 +1,67 @@
package land.chipmunk.chipmunkmod.commands; package land.chipmunk.chipmunkmod.commands;
import com.mojang.brigadier.Command; import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.context.CommandContext; import com.mojang.brigadier.context.CommandContext;
import static com.mojang.brigadier.arguments.StringArgumentType.greedyString; import static com.mojang.brigadier.arguments.StringArgumentType.greedyString;
import static com.mojang.brigadier.arguments.StringArgumentType.getString; import static com.mojang.brigadier.arguments.StringArgumentType.getString;
import static land.chipmunk.chipmunkmod.command.CommandManager.literal; import static land.chipmunk.chipmunkmod.command.CommandManager.literal;
import static land.chipmunk.chipmunkmod.command.CommandManager.argument; import static land.chipmunk.chipmunkmod.command.CommandManager.argument;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.screen.ConnectScreen; import net.minecraft.client.gui.screen.ConnectScreen;
import net.minecraft.client.gui.screen.TitleScreen; import net.minecraft.client.gui.screen.TitleScreen;
import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen; import net.minecraft.client.gui.screen.multiplayer.MultiplayerScreen;
import net.minecraft.client.network.ServerInfo; import net.minecraft.client.network.ServerInfo;
import net.minecraft.client.network.ServerAddress; import net.minecraft.client.network.ServerAddress;
import net.minecraft.client.util.Session; import net.minecraft.client.util.Session;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import java.util.Optional; import java.util.Optional;
import land.chipmunk.chipmunkmod.mixin.MinecraftClientAccessor; import land.chipmunk.chipmunkmod.mixin.MinecraftClientAccessor;
public class UsernameCommand { public class UsernameCommand {
private static final Session ORIGINAL_SESSION = ((MinecraftClientAccessor) MinecraftClient.getInstance()).session(); private static final Session ORIGINAL_SESSION = ((MinecraftClientAccessor) MinecraftClient.getInstance()).session();
private static final SimpleCommandExceptionType USERNAME_TOO_LONG = new SimpleCommandExceptionType(Text.translatable("The specified username is longer than 16 characters")); private static final SimpleCommandExceptionType USERNAME_TOO_LONG = new SimpleCommandExceptionType(Text.translatable("The specified username is longer than 16 characters"));
public static void register (CommandDispatcher<FabricClientCommandSource> dispatcher) { public static void register (CommandDispatcher<FabricClientCommandSource> dispatcher) {
dispatcher.register( dispatcher.register(
literal("username") literal("username")
.then( .then(
literal("set") literal("set")
.then( .then(
argument("username", greedyString()) argument("username", greedyString())
.executes(c -> updateUsername(c)) .executes(c -> updateUsername(c))
) )
) )
.then( .then(
literal("revert") literal("revert")
.executes(c -> updateSession(c, ORIGINAL_SESSION)) .executes(c -> updateSession(c, ORIGINAL_SESSION))
) )
); );
} }
public static int updateUsername (CommandContext<FabricClientCommandSource> context) throws CommandSyntaxException { public static int updateUsername (CommandContext<FabricClientCommandSource> context) throws CommandSyntaxException {
final String username = getString(context, "username"); final String username = getString(context, "username");
if (username.length() > 16) throw USERNAME_TOO_LONG.create(); if (username.length() > 16) throw USERNAME_TOO_LONG.create();
final Session session = new Session(username, "", "", Optional.empty(), Optional.empty(), Session.AccountType.MOJANG); final Session session = new Session(username, "", "", Optional.empty(), Optional.empty(), Session.AccountType.MOJANG);
return updateSession(context, session); return updateSession(context, session);
} }
public static int updateSession (CommandContext<FabricClientCommandSource> context, Session session) throws CommandSyntaxException { public static int updateSession (CommandContext<FabricClientCommandSource> context, Session session) throws CommandSyntaxException {
final FabricClientCommandSource source = context.getSource(); final FabricClientCommandSource source = context.getSource();
final MinecraftClient client = source.getClient(); final MinecraftClient client = source.getClient();
((MinecraftClientAccessor) client).session(session); ((MinecraftClientAccessor) client).session(session);
// TODO: Put this in a separate class // TODO: Put this in a separate class
final ServerInfo info = client.getCurrentServerEntry(); final ServerInfo info = client.getCurrentServerEntry();
client.world.disconnect(); client.world.disconnect();
client.disconnect(); client.disconnect();
ConnectScreen.connect(new MultiplayerScreen(new TitleScreen()), client, ServerAddress.parse(info.address), info); ConnectScreen.connect(new MultiplayerScreen(new TitleScreen()), client, ServerAddress.parse(info.address), info);
return Command.SINGLE_SUCCESS; return Command.SINGLE_SUCCESS;
} }
} }

View file

@ -1,13 +1,13 @@
package land.chipmunk.chipmunkmod.data; package land.chipmunk.chipmunkmod.data;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Data; import lombok.Data;
// ? Am I reinventing the wheel here? // ? Am I reinventing the wheel here?
@AllArgsConstructor @AllArgsConstructor
@Data @Data
public class BlockArea { public class BlockArea {
private BlockPos start; private BlockPos start;
private BlockPos end; private BlockPos end;
} }

View file

@ -1,50 +1,50 @@
package land.chipmunk.chipmunkmod.mixin; package land.chipmunk.chipmunkmod.mixin;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import com.mojang.brigadier.suggestion.Suggestions; import com.mojang.brigadier.suggestion.Suggestions;
import net.minecraft.client.gui.widget.TextFieldWidget; import net.minecraft.client.gui.widget.TextFieldWidget;
import com.mojang.brigadier.CommandDispatcher; import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.StringReader; import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource; import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import land.chipmunk.chipmunkmod.command.CommandManager; import land.chipmunk.chipmunkmod.command.CommandManager;
@Mixin(net.minecraft.client.gui.screen.ChatInputSuggestor.class) @Mixin(net.minecraft.client.gui.screen.ChatInputSuggestor.class)
public class ChatInputSuggestorMixin { public class ChatInputSuggestorMixin {
@Shadow @Shadow
CompletableFuture<Suggestions> pendingSuggestions; CompletableFuture<Suggestions> pendingSuggestions;
@Shadow @Shadow
public void show (boolean narrateFirstSuggestion) {} public void show (boolean narrateFirstSuggestion) {}
@Shadow @Shadow
final TextFieldWidget textField; final TextFieldWidget textField;
public ChatInputSuggestorMixin () { public ChatInputSuggestorMixin () {
textField = null; textField = null;
} }
@Inject(at = @At("TAIL"), method = "refresh()V") @Inject(at = @At("TAIL"), method = "refresh()V")
public void onRefresh (CallbackInfo ci) { public void onRefresh (CallbackInfo ci) {
final String text = this.textField.getText(); final String text = this.textField.getText();
final int cursor = this.textField.getCursor(); final int cursor = this.textField.getCursor();
if (cursor < CommandManager.prefix.length() || !text.startsWith(CommandManager.prefix)) return; if (cursor < CommandManager.prefix.length() || !text.startsWith(CommandManager.prefix)) return;
final StringReader reader = new StringReader(text); final StringReader reader = new StringReader(text);
reader.setCursor(CommandManager.prefix.length()); // Skip the prefix reader.setCursor(CommandManager.prefix.length()); // Skip the prefix
final CommandDispatcher<FabricClientCommandSource> dispatcher = CommandManager.dispatcher; final CommandDispatcher<FabricClientCommandSource> dispatcher = CommandManager.dispatcher;
final MinecraftClient client = MinecraftClient.getInstance(); final MinecraftClient client = MinecraftClient.getInstance();
final FabricClientCommandSource commandSource = (FabricClientCommandSource) client.getNetworkHandler().getCommandSource(); final FabricClientCommandSource commandSource = (FabricClientCommandSource) client.getNetworkHandler().getCommandSource();
pendingSuggestions = dispatcher.getCompletionSuggestions(dispatcher.parse(reader, commandSource), cursor); pendingSuggestions = dispatcher.getCompletionSuggestions(dispatcher.parse(reader, commandSource), cursor);
show(true); show(true);
} }
} }

View file

@ -1,22 +1,22 @@
package land.chipmunk.chipmunkmod.mixin; package land.chipmunk.chipmunkmod.mixin;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable; import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import land.chipmunk.chipmunkmod.command.CommandManager; import land.chipmunk.chipmunkmod.command.CommandManager;
@Mixin(net.minecraft.client.gui.screen.ChatScreen.class) @Mixin(net.minecraft.client.gui.screen.ChatScreen.class)
public class ChatScreenMixin { public class ChatScreenMixin {
@Inject(at = @At("HEAD"), method = "sendMessage", cancellable = true) @Inject(at = @At("HEAD"), method = "sendMessage", cancellable = true)
public void sendMessage(String chatText, boolean addToHistory, CallbackInfoReturnable<Boolean> cir) { public void sendMessage(String chatText, boolean addToHistory, CallbackInfoReturnable<Boolean> cir) {
if (chatText.startsWith(CommandManager.prefix)) { if (chatText.startsWith(CommandManager.prefix)) {
CommandManager.executeCommand(chatText.substring(CommandManager.prefix.length())); CommandManager.executeCommand(chatText.substring(CommandManager.prefix.length()));
if (addToHistory) MinecraftClient.getInstance().inGameHud.getChatHud().addToMessageHistory(chatText); if (addToHistory) MinecraftClient.getInstance().inGameHud.getChatHud().addToMessageHistory(chatText);
cir.setReturnValue(true); cir.setReturnValue(true);
} }
} }
} }

View file

@ -1,20 +1,20 @@
package land.chipmunk.chipmunkmod.mixin; package land.chipmunk.chipmunkmod.mixin;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import land.chipmunk.chipmunkmod.modules.CommandCore; import land.chipmunk.chipmunkmod.modules.CommandCore;
import land.chipmunk.chipmunkmod.modules.SelfCare; import land.chipmunk.chipmunkmod.modules.SelfCare;
@Mixin(net.minecraft.network.ClientConnection.class) @Mixin(net.minecraft.network.ClientConnection.class)
public class ClientConnectionMixin { public class ClientConnectionMixin {
@Inject(at = @At("HEAD"), method = "disconnect", cancellable = true) @Inject(at = @At("HEAD"), method = "disconnect", cancellable = true)
public void disconnect (Text disconnectReason, CallbackInfo ci) { public void disconnect (Text disconnectReason, CallbackInfo ci) {
if (disconnectReason == ClientPlayNetworkHandlerAccessor.chatValidationFailedText()) { if (disconnectReason == ClientPlayNetworkHandlerAccessor.chatValidationFailedText()) {
ci.cancel(); ci.cancel();
} }
} }
} }

View file

@ -1,11 +1,11 @@
package land.chipmunk.chipmunkmod.mixin; package land.chipmunk.chipmunkmod.mixin;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.text.Text; import net.minecraft.text.Text;
@Mixin(net.minecraft.client.network.ClientPlayNetworkHandler.class) @Mixin(net.minecraft.client.network.ClientPlayNetworkHandler.class)
public interface ClientPlayNetworkHandlerAccessor { public interface ClientPlayNetworkHandlerAccessor {
@Accessor("CHAT_VALIDATION_FAILED_TEXT") @Accessor("CHAT_VALIDATION_FAILED_TEXT")
public static Text chatValidationFailedText () { throw new AssertionError(); } public static Text chatValidationFailedText () { throw new AssertionError(); }
} }

View file

@ -1,16 +1,16 @@
package land.chipmunk.chipmunkmod.mixin; package land.chipmunk.chipmunkmod.mixin;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket; import net.minecraft.network.packet.s2c.play.GameJoinS2CPacket;
import land.chipmunk.chipmunkmod.modules.SelfCare; import land.chipmunk.chipmunkmod.modules.SelfCare;
@Mixin(net.minecraft.client.network.ClientPlayNetworkHandler.class) @Mixin(net.minecraft.client.network.ClientPlayNetworkHandler.class)
public class ClientPlayNetworkHandlerMixin { public class ClientPlayNetworkHandlerMixin {
@Inject(method = "onGameJoin", at = @At("TAIL")) @Inject(method = "onGameJoin", at = @At("TAIL"))
private void onGameJoin (GameJoinS2CPacket packet, CallbackInfo ci) { private void onGameJoin (GameJoinS2CPacket packet, CallbackInfo ci) {
SelfCare.INSTANCE.init(); SelfCare.INSTANCE.init();
} }
} }

View file

@ -1,34 +1,34 @@
package land.chipmunk.chipmunkmod.mixin; package land.chipmunk.chipmunkmod.mixin;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow; import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity;
import net.minecraft.entity.MovementType; import net.minecraft.entity.MovementType;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec2f; import net.minecraft.util.math.Vec2f;
import land.chipmunk.chipmunkmod.modules.CommandCore; import land.chipmunk.chipmunkmod.modules.CommandCore;
@Mixin(ClientPlayerEntity.class) @Mixin(ClientPlayerEntity.class)
public class ClientPlayerEntityMixin { public class ClientPlayerEntityMixin {
private static MinecraftClient CLIENT = MinecraftClient.getInstance(); private static MinecraftClient CLIENT = MinecraftClient.getInstance();
@Inject(at = @At("HEAD"), method = "move") @Inject(at = @At("HEAD"), method = "move")
public void move (MovementType type, Vec3d relPos, CallbackInfo ci) { public void move (MovementType type, Vec3d relPos, CallbackInfo ci) {
if ((ClientPlayerEntity) (Object) this != CLIENT.player) return; if ((ClientPlayerEntity) (Object) this != CLIENT.player) return;
final Vec3d position = ((ClientPlayerEntity) (Object) this).getPos().add(relPos); final Vec3d position = ((ClientPlayerEntity) (Object) this).getPos().add(relPos);
final ClientWorld world = CLIENT.getNetworkHandler().getWorld(); final ClientWorld world = CLIENT.getNetworkHandler().getWorld();
final BlockPos origin = CommandCore.INSTANCE.origin(); final BlockPos origin = CommandCore.INSTANCE.origin();
if (origin == null) { CommandCore.INSTANCE.move(position); return; } if (origin == null) { CommandCore.INSTANCE.move(position); return; }
final int distance = (int) Math.sqrt(new Vec2f(origin.getX() / 16, origin.getZ() / 16).distanceSquared(new Vec2f((int) position.getX() / 16, (int) position.getZ() / 16))); final int distance = (int) Math.sqrt(new Vec2f(origin.getX() / 16, origin.getZ() / 16).distanceSquared(new Vec2f((int) position.getX() / 16, (int) position.getZ() / 16)));
if (distance > world.getSimulationDistance()) CommandCore.INSTANCE.move(position); if (distance > world.getSimulationDistance()) CommandCore.INSTANCE.move(position);
} }
} }

View file

@ -1,16 +1,16 @@
package land.chipmunk.chipmunkmod.mixin; package land.chipmunk.chipmunkmod.mixin;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Mutable; import org.spongepowered.asm.mixin.Mutable;
import org.spongepowered.asm.mixin.gen.Accessor; import org.spongepowered.asm.mixin.gen.Accessor;
import net.minecraft.client.util.Session; import net.minecraft.client.util.Session;
@Mixin(net.minecraft.client.MinecraftClient.class) @Mixin(net.minecraft.client.MinecraftClient.class)
public interface MinecraftClientAccessor { public interface MinecraftClientAccessor {
@Accessor("session") @Accessor("session")
Session session (); Session session ();
@Mutable @Mutable
@Accessor("session") @Accessor("session")
void session (Session session); void session (Session session);
} }

View file

@ -1,147 +1,148 @@
package land.chipmunk.chipmunkmod.modules; package land.chipmunk.chipmunkmod.modules;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.network.ClientConnection; import net.minecraft.network.ClientConnection;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.network.packet.c2s.play.UpdateCommandBlockC2SPacket; import net.minecraft.network.packet.c2s.play.UpdateCommandBlockC2SPacket;
import net.minecraft.block.entity.CommandBlockBlockEntity; import net.minecraft.block.entity.CommandBlockBlockEntity;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import java.util.List; import java.util.List;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import java.util.function.Consumer; import java.util.function.Consumer;
import land.chipmunk.chipmunkmod.data.BlockArea; import land.chipmunk.chipmunkmod.ChipmunkMod;
import land.chipmunk.chipmunkmod.data.BlockArea;
public class CommandCore {
private MinecraftClient client; public class CommandCore {
@Getter @Setter private boolean ready = false; private MinecraftClient client;
@Getter @Setter private BlockPos origin; @Getter @Setter private boolean ready = false;
// TODO: Make it configurable @Getter @Setter private BlockPos origin;
@Getter private final BlockArea relativeArea = new BlockArea(new BlockPos(0, 0, 0), new BlockPos(15, 0, 15)); @Getter private final BlockArea relativeArea;
@Getter @Setter private BlockPos currentBlockRelative; @Getter @Setter private BlockPos currentBlockRelative;
public static CommandCore INSTANCE = new CommandCore(MinecraftClient.getInstance()); public static CommandCore INSTANCE = new CommandCore(MinecraftClient.getInstance(), ChipmunkMod.CONFIG.core.relativeArea);
public CommandCore (MinecraftClient client) { public CommandCore (MinecraftClient client, BlockArea relativeArea) {
this.client = client; this.client = client;
} this.relativeArea = relativeArea;
}
public void move (Vec3d position) {
if (!ready) { public void move (Vec3d position) {
ready = true; if (!ready) {
// for (Listener listener : listeners) listener.ready(); ready = true;
} // for (Listener listener : listeners) listener.ready();
}
origin = new BlockPos(
((int) position.getX() / 16) * 16, origin = new BlockPos(
0, // TODO: Use the actual bottom of the world instead of hardcoding to 0 ((int) position.getX() / 16) * 16,
((int) position.getZ() / 16) * 16 0, // TODO: Use the actual bottom of the world instead of hardcoding to 0
); ((int) position.getZ() / 16) * 16
);
if (currentBlockRelative == null) currentBlockRelative = new BlockPos(relativeArea.start());
refill(); if (currentBlockRelative == null) currentBlockRelative = new BlockPos(relativeArea.start());
} refill();
}
public void refill () {
// final PositionManager position = client.position(); public void refill () {
final BlockPos relStart = relativeArea.start(); // final PositionManager position = client.position();
final BlockPos relEnd = relativeArea.end(); final BlockPos relStart = relativeArea.start();
final BlockPos relEnd = relativeArea.end();
final String command = String.format(
"fill %s %s %s %s %s %s command_block", final String command = String.format(
relStart.getX() + origin.getX(), "fill %s %s %s %s %s %s command_block",
relStart.getY() + origin.getY(), relStart.getX() + origin.getX(),
relStart.getZ() + origin.getZ(), relStart.getY() + origin.getY(),
relStart.getZ() + origin.getZ(),
relEnd.getX() + origin.getX(),
relEnd.getY() + origin.getY(), relEnd.getX() + origin.getX(),
relEnd.getZ() + origin.getZ() relEnd.getY() + origin.getY(),
); relEnd.getZ() + origin.getZ()
);
client.getNetworkHandler().sendChatCommand(command);
} client.getNetworkHandler().sendChatCommand(command);
}
public void incrementCurrentBlock () {
final BlockPos start = relativeArea.start(); public void incrementCurrentBlock () {
final BlockPos end = relativeArea.end(); final BlockPos start = relativeArea.start();
final BlockPos end = relativeArea.end();
int x = currentBlockRelative.getX();
int y = currentBlockRelative.getY(); int x = currentBlockRelative.getX();
int z = currentBlockRelative.getZ(); int y = currentBlockRelative.getY();
int z = currentBlockRelative.getZ();
x++;
x++;
if (x > end.getX()) {
x = start.getX(); if (x > end.getX()) {
z++; x = start.getX();
} z++;
}
if (z > end.getZ()) {
z = start.getZ(); if (z > end.getZ()) {
y++; z = start.getZ();
} y++;
}
if (y > end.getY()) {
x = start.getX(); if (y > end.getY()) {
y = start.getY(); x = start.getX();
z = start.getZ(); y = start.getY();
} z = start.getZ();
}
currentBlockRelative = new BlockPos(x, y, z);
} currentBlockRelative = new BlockPos(x, y, z);
}
public BlockPos currentBlockAbsolute () {
return currentBlockRelative.add(origin); public BlockPos currentBlockAbsolute () {
} return currentBlockRelative.add(origin);
}
public void run (String command) {
final ClientConnection connection = client.getNetworkHandler().getConnection(); public void run (String command) {
final BlockPos currentBlock = currentBlockAbsolute(); final ClientConnection connection = client.getNetworkHandler().getConnection();
final BlockPos currentBlock = currentBlockAbsolute();
// TODO: Support using repeating command blocks (on kaboom-like servers) (because less packets)
connection.send(new UpdateCommandBlockC2SPacket(currentBlock, "", CommandBlockBlockEntity.Type.REDSTONE, false, false, false)); // TODO: Support using repeating command blocks (on kaboom-like servers) (because less packets)
connection.send(new UpdateCommandBlockC2SPacket(currentBlock, command, CommandBlockBlockEntity.Type.REDSTONE, false, false, true)); connection.send(new UpdateCommandBlockC2SPacket(currentBlock, "", CommandBlockBlockEntity.Type.REDSTONE, false, false, false));
connection.send(new UpdateCommandBlockC2SPacket(currentBlock, command, CommandBlockBlockEntity.Type.REDSTONE, false, false, true));
incrementCurrentBlock();
} incrementCurrentBlock();
}
public CompletableFuture<NbtCompound> runTracked (String command) {
final ClientConnection connection = client.getNetworkHandler().getConnection(); public CompletableFuture<NbtCompound> runTracked (String command) {
final BlockPos currentBlock = currentBlockAbsolute(); final ClientConnection connection = client.getNetworkHandler().getConnection();
final BlockPos currentBlock = currentBlockAbsolute();
// TODO: Support using repeating command blocks (on kaboom-like servers) (because less packets)
connection.send(new UpdateCommandBlockC2SPacket(currentBlock, "", CommandBlockBlockEntity.Type.SEQUENCE, false, false, false)); // TODO: Support using repeating command blocks (on kaboom-like servers) (because less packets)
connection.send(new UpdateCommandBlockC2SPacket(currentBlock, command, CommandBlockBlockEntity.Type.REDSTONE, true, false, true)); connection.send(new UpdateCommandBlockC2SPacket(currentBlock, "", CommandBlockBlockEntity.Type.SEQUENCE, false, false, false));
connection.send(new UpdateCommandBlockC2SPacket(currentBlock, command, CommandBlockBlockEntity.Type.REDSTONE, true, false, true));
incrementCurrentBlock();
incrementCurrentBlock();
CompletableFuture<NbtCompound> future = new CompletableFuture<NbtCompound>();
CompletableFuture<NbtCompound> future = new CompletableFuture<NbtCompound>();
final Timer timer = new Timer();
final Timer timer = new Timer();
final TimerTask queryTask = new TimerTask() {
public void run () { final TimerTask queryTask = new TimerTask() {
client.getNetworkHandler().getDataQueryHandler().queryBlockNbt(currentBlock, public void run () {
tag -> { future.complete(tag); }); client.getNetworkHandler().getDataQueryHandler().queryBlockNbt(currentBlock,
tag -> { future.complete(tag); });
timer.cancel(); // ? Is this necesary?
timer.purge(); timer.cancel(); // ? Is this necesary?
} timer.purge();
}; }
};
timer.schedule(queryTask, 50);
timer.schedule(queryTask, 50);
return future;
} return future;
}
public void cleanup () {
origin = null; public void cleanup () {
currentBlockRelative = null; origin = null;
ready = false; currentBlockRelative = null;
} ready = false;
} }
}

View file

@ -1,72 +1,72 @@
package land.chipmunk.chipmunkmod.modules; package land.chipmunk.chipmunkmod.modules;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.network.ClientPlayNetworkHandler; import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.client.network.ClientPlayerEntity; import net.minecraft.client.network.ClientPlayerEntity;
import com.mojang.brigadier.tree.CommandNode; import com.mojang.brigadier.tree.CommandNode;
import com.mojang.brigadier.tree.LiteralCommandNode; import com.mojang.brigadier.tree.LiteralCommandNode;
import lombok.Getter; import lombok.Getter;
import java.util.Timer; import java.util.Timer;
import java.util.TimerTask; import java.util.TimerTask;
public class SelfCare { public class SelfCare {
private final MinecraftClient client; private final MinecraftClient client;
@Getter private long interval; @Getter private long interval;
private Timer timer = null; private Timer timer = null;
public static final SelfCare INSTANCE = new SelfCare(MinecraftClient.getInstance(), 70L); public static final SelfCare INSTANCE = new SelfCare(MinecraftClient.getInstance(), 70L);
public SelfCare (MinecraftClient client, long interval) { public SelfCare (MinecraftClient client, long interval) {
this.client = client; this.client = client;
this.interval = interval; this.interval = interval;
} }
public void init () { public void init () {
final TimerTask task = new TimerTask() { final TimerTask task = new TimerTask() {
public void run () { public void run () {
tick(); tick();
} }
}; };
if (timer != null) cleanup(); if (timer != null) cleanup();
timer = new Timer(); timer = new Timer();
timer.schedule(task, interval, interval); timer.schedule(task, interval, interval);
} }
public void cleanup () { public void cleanup () {
if (timer == null) return; if (timer == null) return;
timer.cancel(); timer.cancel();
timer.purge(); timer.purge();
timer = null; timer = null;
} }
public void tick () { public void tick () {
final ClientPlayerEntity player = client.player; final ClientPlayerEntity player = client.player;
final ClientPlayNetworkHandler networkHandler = client.getNetworkHandler(); final ClientPlayNetworkHandler networkHandler = client.getNetworkHandler();
if (networkHandler == null) { if (networkHandler == null) {
cleanup(); cleanup();
return; return;
} }
if (!player.hasPermissionLevel(2)) { if (serverHasCommand("op")) networkHandler.sendChatCommand("op @s[type=player]"); } if (!player.hasPermissionLevel(2)) { if (serverHasCommand("op")) networkHandler.sendChatCommand("op @s[type=player]"); }
else if (!client.player.isCreative()) networkHandler.sendChatCommand("gamemode creative"); else if (!client.player.isCreative()) networkHandler.sendChatCommand("gamemode creative");
} }
// TODO: Move this into a separate class related to server info gathering (and yes, I plan on making this d y n a m i c and require little to no configuration for most servers) // TODO: Move this into a separate class related to server info gathering (and yes, I plan on making this d y n a m i c and require little to no configuration for most servers)
private boolean serverHasCommand (String name) { private boolean serverHasCommand (String name) {
final ClientPlayNetworkHandler networkHandler = client.getNetworkHandler(); final ClientPlayNetworkHandler networkHandler = client.getNetworkHandler();
for (CommandNode node : networkHandler.getCommandDispatcher().getRoot().getChildren()) { for (CommandNode node : networkHandler.getCommandDispatcher().getRoot().getChildren()) {
if (!(node instanceof LiteralCommandNode)) continue; if (!(node instanceof LiteralCommandNode)) continue;
final LiteralCommandNode literal = (LiteralCommandNode) node; final LiteralCommandNode literal = (LiteralCommandNode) node;
if (literal.getLiteral().equals(name)) return true; if (literal.getLiteral().equals(name)) return true;
} }
return false; return false;
} }
} }

View file

@ -0,0 +1,12 @@
{
"commands": {
"prefix": "."
},
"core": {
"relativeArea": {
"start": { "x": 0, "y": 0, "z": 0 },
"end": { "x": 15, "y": 0, "z": 15 }
}
}
}