From 6c753b802c0746d01ea5ed420e1733749731fd8d Mon Sep 17 00:00:00 2001 From: Guntram Blohm Date: Wed, 26 Dec 2018 00:24:11 +0100 Subject: [PATCH 1/3] Fix crash when a keybinding with a category not known to MC gets added --- .../mixin/hook/client/MixinKeyBinding.java | 21 +++++++++++++++++++ src/main/resources/mixins.rift.hooks.json | 3 ++- 2 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 src/main/java/org/dimdev/rift/mixin/hook/client/MixinKeyBinding.java diff --git a/src/main/java/org/dimdev/rift/mixin/hook/client/MixinKeyBinding.java b/src/main/java/org/dimdev/rift/mixin/hook/client/MixinKeyBinding.java new file mode 100644 index 0000000..2ad24ce --- /dev/null +++ b/src/main/java/org/dimdev/rift/mixin/hook/client/MixinKeyBinding.java @@ -0,0 +1,21 @@ +package org.dimdev.rift.mixin.hook.client; + +import java.util.Map; +import net.minecraft.client.settings.KeyBinding; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.Shadow; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(KeyBinding.class) +public class MixinKeyBinding { + @Shadow private static Map CATEGORY_ORDER; + @Inject(method = "", at = @At("RETURN")) + private void insertCategory(String description, + int keycode, String category, CallbackInfo ci) { + if (CATEGORY_ORDER.get(category)==null) { + CATEGORY_ORDER.put(category, CATEGORY_ORDER.size()); + } + } +} diff --git a/src/main/resources/mixins.rift.hooks.json b/src/main/resources/mixins.rift.hooks.json index 45db5f0..5feb5c7 100644 --- a/src/main/resources/mixins.rift.hooks.json +++ b/src/main/resources/mixins.rift.hooks.json @@ -48,6 +48,7 @@ "client.MixinRenderManager", "client.MixinEntityPlayerSP", "client.MixinGameSettings", - "client.MixinGuiIngame" + "client.MixinGuiIngame", + "client.MixinKeyBinding" ] } From c44fe05317e425ecd0cae88c23a7c40675248d33 Mon Sep 17 00:00:00 2001 From: Guntram Blohm Date: Thu, 27 Dec 2018 19:52:43 +0100 Subject: [PATCH 2/3] Add a new interface for local commands, which are handled within the client and never see the server, and merge local and server completion suggestions for the client gui. --- .../listener/client/LocalCommandAdder.java | 8 +++ .../rift/mixin/hook/client/MixinGuiChat.java | 57 +++++++++++++++++ .../mixin/hook/client/MixinLocalCommand.java | 25 ++++++++ .../dimdev/rift/util/LocalCommandManager.java | 64 +++++++++++++++++++ src/main/resources/mixins.rift.hooks.json | 2 + 5 files changed, 156 insertions(+) create mode 100644 src/main/java/org/dimdev/rift/listener/client/LocalCommandAdder.java create mode 100644 src/main/java/org/dimdev/rift/mixin/hook/client/MixinGuiChat.java create mode 100644 src/main/java/org/dimdev/rift/mixin/hook/client/MixinLocalCommand.java create mode 100644 src/main/java/org/dimdev/rift/util/LocalCommandManager.java diff --git a/src/main/java/org/dimdev/rift/listener/client/LocalCommandAdder.java b/src/main/java/org/dimdev/rift/listener/client/LocalCommandAdder.java new file mode 100644 index 0000000..63d9385 --- /dev/null +++ b/src/main/java/org/dimdev/rift/listener/client/LocalCommandAdder.java @@ -0,0 +1,8 @@ +package org.dimdev.rift.listener.client; + +import com.mojang.brigadier.CommandDispatcher; +import net.minecraft.command.CommandSource; + +public interface LocalCommandAdder { + void registerLocalCommands(CommandDispatcher dispatcher); +} diff --git a/src/main/java/org/dimdev/rift/mixin/hook/client/MixinGuiChat.java b/src/main/java/org/dimdev/rift/mixin/hook/client/MixinGuiChat.java new file mode 100644 index 0000000..19a4fa9 --- /dev/null +++ b/src/main/java/org/dimdev/rift/mixin/hook/client/MixinGuiChat.java @@ -0,0 +1,57 @@ +package org.dimdev.rift.mixin.hook.client; + +import com.mojang.brigadier.suggestion.Suggestion; +import net.minecraft.client.gui.GuiChat; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Redirect; +import com.mojang.brigadier.suggestion.Suggestions; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CompletableFuture; +import net.minecraft.client.gui.GuiTextField; +import org.dimdev.rift.util.LocalCommandManager; +import org.spongepowered.asm.mixin.Shadow; + +@Mixin(GuiChat.class) +public class MixinGuiChat { + + @Shadow GuiTextField inputField; + + @Redirect(method="showSuggestions", + at=@At(value="INVOKE", + target="Ljava/util/concurrent/CompletableFuture;join()Ljava/lang/Object;", + remap=false) + ) + + // We need to declare this to return Object, not Suggestions, because the + // redirected method is type polymorphic and thus returns Object, + // not Suggestions, in its byte code definition. + + public Object gotServerSideSuggestions(CompletableFuture pendingSuggestions) { + Suggestions server=pendingSuggestions.join(); +// System.out.println("gSSS called, server is " + server); + Suggestions local=LocalCommandManager.getSuggestions(inputField.getText()); +// System.out.println("local is " + local); +// System.out.println("---------------------------"); + + if (local.isEmpty()) { + return server; + } + + if (server.isEmpty()) { + return local; + } + + if (!local.getRange().equals(server.getRange())) { + System.err.println("something wrong with ranges"); + return server; + } + + List results=new ArrayList<>(); + results.addAll(server.getList()); + results.addAll(local.getList()); + Suggestions result=new Suggestions(server.getRange(), results); + return result; + } +} diff --git a/src/main/java/org/dimdev/rift/mixin/hook/client/MixinLocalCommand.java b/src/main/java/org/dimdev/rift/mixin/hook/client/MixinLocalCommand.java new file mode 100644 index 0000000..0cb6753 --- /dev/null +++ b/src/main/java/org/dimdev/rift/mixin/hook/client/MixinLocalCommand.java @@ -0,0 +1,25 @@ +package org.dimdev.rift.mixin.hook.client; + +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import net.minecraft.client.entity.EntityPlayerSP; +import org.dimdev.rift.util.LocalCommandManager; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; + +@Mixin(EntityPlayerSP.class) +public class MixinLocalCommand { + @Inject(method="sendChatMessage", at=@At("HEAD"), cancellable=true) + private void handleLocalCommand(String message, CallbackInfo callbackInfo) { + if (message.startsWith("/")) { + try { + LocalCommandManager.dispatchLocalCommand(message.substring(1)); + callbackInfo.cancel(); + } catch (CommandSyntaxException ex) { + // Don't do anything, it wasn't intended to be our command. + // Not cancelling callbackInfo will make MC send the command to the server. + } + } + } +} diff --git a/src/main/java/org/dimdev/rift/util/LocalCommandManager.java b/src/main/java/org/dimdev/rift/util/LocalCommandManager.java new file mode 100644 index 0000000..e134cd1 --- /dev/null +++ b/src/main/java/org/dimdev/rift/util/LocalCommandManager.java @@ -0,0 +1,64 @@ +/* + * To change this license header, choose License Headers in Project Properties. + * To change this template file, choose Tools | Templates + * and open the template in the editor. + */ +package org.dimdev.rift.util; + +import com.mojang.brigadier.CommandDispatcher; +import com.mojang.brigadier.ParseResults; +import com.mojang.brigadier.StringReader; +import com.mojang.brigadier.exceptions.CommandSyntaxException; +import com.mojang.brigadier.suggestion.Suggestions; +import java.util.concurrent.CompletableFuture; +import net.minecraft.command.CommandSource; +import org.dimdev.rift.listener.client.LocalCommandAdder; +import org.dimdev.riftloader.RiftLoader; + +/** + * + * @author gbl + */ +public class LocalCommandManager { + + private static LocalCommandManager instance; + private CommandDispatcher dispatcher; + private CompletableFuture suggestions; + + private LocalCommandManager() { + } + + public static LocalCommandManager getInstance() { + if (instance == null) { + instance=new LocalCommandManager(); + instance.dispatcher=new CommandDispatcher<>(); + + for (LocalCommandAdder localCommandAdder: RiftLoader.instance.getListeners(LocalCommandAdder.class)) { + localCommandAdder.registerLocalCommands(instance.dispatcher); + } + } + return instance; + } + + public static void dispatchLocalCommand(String s) throws CommandSyntaxException { + getInstance().dispatcher.execute(s, null); + } + + private Suggestions getSuggestionsFor(String command) { + // Don't just pass command; pass a stringreader that skips over the '/', + // or Suggestions.range won't match the server version. + StringReader reader=new StringReader(command); + reader.skip(); + ParseResults parse = dispatcher.parse(reader, null); + // We are losing the advantage of using a separate thread here, + // but the server commands, which imply a network exchange, + // need it much more than we do. + suggestions = dispatcher.getCompletionSuggestions(parse); + Suggestions result = suggestions.join(); + return result; + } + + public static Suggestions getSuggestions(String s) { + return getInstance().getSuggestionsFor(s); + } +} diff --git a/src/main/resources/mixins.rift.hooks.json b/src/main/resources/mixins.rift.hooks.json index 5feb5c7..0288e99 100644 --- a/src/main/resources/mixins.rift.hooks.json +++ b/src/main/resources/mixins.rift.hooks.json @@ -41,6 +41,8 @@ "MixinItemTool" ], "client": [ + "client.MixinGuiChat", + "client.MixinLocalCommand", "client.MixinMinecraft", "client.MixinModelBakery", "client.MixinNetHandlerPlayClient", From 39f9b58080d82dde6d2bbe60e0ab6d34cb504637 Mon Sep 17 00:00:00 2001 From: Guntram Blohm Date: Wed, 16 Jan 2019 13:58:51 +0100 Subject: [PATCH 3/3] Fix crash when pressing TAB in an empty input field --- .../org/dimdev/rift/mixin/hook/client/MixinGuiChat.java | 7 ++----- .../java/org/dimdev/rift/util/LocalCommandManager.java | 3 +++ 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/main/java/org/dimdev/rift/mixin/hook/client/MixinGuiChat.java b/src/main/java/org/dimdev/rift/mixin/hook/client/MixinGuiChat.java index 19a4fa9..fdf1f88 100644 --- a/src/main/java/org/dimdev/rift/mixin/hook/client/MixinGuiChat.java +++ b/src/main/java/org/dimdev/rift/mixin/hook/client/MixinGuiChat.java @@ -30,12 +30,9 @@ public class MixinGuiChat { public Object gotServerSideSuggestions(CompletableFuture pendingSuggestions) { Suggestions server=pendingSuggestions.join(); -// System.out.println("gSSS called, server is " + server); Suggestions local=LocalCommandManager.getSuggestions(inputField.getText()); -// System.out.println("local is " + local); -// System.out.println("---------------------------"); - - if (local.isEmpty()) { + + if (local==null || local.isEmpty()) { return server; } diff --git a/src/main/java/org/dimdev/rift/util/LocalCommandManager.java b/src/main/java/org/dimdev/rift/util/LocalCommandManager.java index e134cd1..71550bd 100644 --- a/src/main/java/org/dimdev/rift/util/LocalCommandManager.java +++ b/src/main/java/org/dimdev/rift/util/LocalCommandManager.java @@ -45,6 +45,9 @@ public static void dispatchLocalCommand(String s) throws CommandSyntaxException } private Suggestions getSuggestionsFor(String command) { + if (!command.startsWith("/")) { + return null; + } // Don't just pass command; pass a stringreader that skips over the '/', // or Suggestions.range won't match the server version. StringReader reader=new StringReader(command);