├── docs ├── version.json └── index.html ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── src └── main │ ├── resources │ ├── assets │ │ └── mcfdebugger │ │ │ ├── icon.png │ │ │ └── lang │ │ │ ├── zh_cn.json │ │ │ └── en_us.json │ ├── mcfdebugger.mixins.json │ └── fabric.mod.json │ └── java │ └── com │ └── hb │ └── mcfdebugger │ ├── mixinHelpers │ ├── HackedElement.java │ ├── SendSyntaxError.java │ ├── SendFunctionState.java │ ├── ReadCommandSource.java │ ├── SendCommandState.java │ └── FakeCommandFunctionCreate.java │ ├── config │ ├── ConfigHolder.java │ ├── LoadConfig.java │ └── ConfigManager.java │ ├── mixin │ ├── CommandSourceHook.java │ ├── SetCommandIndex.java │ ├── ExampleMixin.java │ ├── ServerLoad.java │ ├── regcmd.java │ ├── ServerCommandSourceHook.java │ ├── FunctionCommandHook.java │ ├── DebugHook.java │ ├── ErrorHook.java │ └── FunctionLoaderHook.java │ ├── PauseWaiter.java │ ├── SendCmdObj.java │ ├── SimpleCmdObj.java │ ├── DebugThread.java │ ├── commandHelpers │ ├── GetEntity.java │ └── ReadScoreboard.java │ ├── wsServer.java │ ├── McfDebugger.java │ ├── commands │ ├── DebuggerConfigCommand.java │ └── DebuggerCommand.java │ └── wsCommandParser.java ├── settings.gradle ├── .gitignore ├── gradle.properties ├── LICENSE ├── README_zh_cn.md ├── gradlew.bat ├── README.md ├── yarn.lock └── gradlew /docs/version.json: -------------------------------------------------------------------------------- 1 | { 2 | "now_version": "1.0.4", 3 | "now_version_id":4 4 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugeBlack/McfDebugger_Mod/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /src/main/resources/assets/mcfdebugger/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hugeBlack/McfDebugger_Mod/HEAD/src/main/resources/assets/mcfdebugger/icon.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Title 6 | 7 | 8 |

Doc Test!

9 | 10 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | maven { 4 | name = 'Fabric' 5 | url = 'https://maven.fabricmc.net/' 6 | } 7 | gradlePluginPortal() 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.1-bin.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /src/main/resources/assets/mcfdebugger/lang/zh_cn.json: -------------------------------------------------------------------------------- 1 | { 2 | "mcfdebugger.enabled": "函数调试器已启用.", 3 | "mcfdebugger.disabled": "函数调试器已禁用.", 4 | "mcfdebugger.set_port": "函数调试器的Websocket服务器端口已设置为%d.", 5 | "mcfdebugger.set_timeOut": "设置超时时间:%d秒.", 6 | "mcfdebugger.reload_command": "接收到调试器发来的重载指令." 7 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # gradle 2 | 3 | .gradle/ 4 | build/ 5 | out/ 6 | classes/ 7 | 8 | # eclipse 9 | 10 | *.launch 11 | 12 | # idea 13 | 14 | .idea/ 15 | *.iml 16 | *.ipr 17 | *.iws 18 | 19 | # vscode 20 | 21 | .settings/ 22 | .vscode/ 23 | bin/ 24 | .classpath 25 | .project 26 | 27 | # macos 28 | 29 | *.DS_Store 30 | 31 | # fabric 32 | 33 | run/ 34 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/mixinHelpers/HackedElement.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.mixinHelpers; 2 | 3 | import net.minecraft.server.function.CommandFunction; 4 | 5 | public abstract class HackedElement implements CommandFunction.Element { 6 | public String funNamespace; 7 | public String funPath; 8 | public int cmdIndex; 9 | public boolean isLastCmd; 10 | 11 | } 12 | -------------------------------------------------------------------------------- /src/main/resources/assets/mcfdebugger/lang/en_us.json: -------------------------------------------------------------------------------- 1 | { 2 | "mcfdebugger.enabled": "Function debugger has been enabled.", 3 | "mcfdebugger.disabled": "Function debugger has been disabled.", 4 | "mcfdebugger.set_port": "The Websocket server port of function debugger has been set to port:%d", 5 | "mcfdebugger.set_timeOut": "Set timeout:%d s", 6 | "mcfdebugger.reload_command": "Debugger sent a reload command." 7 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Done to increase the memory available to gradle. 2 | org.gradle.jvmargs=-Xmx1G 3 | 4 | # Fabric Properties 5 | # check these on https://fabricmc.net/versions.html 6 | minecraft_version=1.17.1 7 | yarn_mappings=1.17.1+build.35 8 | loader_version=0.11.6 9 | 10 | # Dependencies 11 | fabric_version=0.37.2+1.17 12 | 13 | # Mod Properties 14 | mod_version = 1.1.1 15 | maven_group = com.aigch.hugeblack 16 | archives_base_name = mcfdebugger 17 | 18 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/config/ConfigHolder.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.config; 2 | 3 | public class ConfigHolder { 4 | public static String debuggerMode="none"; 5 | public static boolean isThisModDebugging=false; 6 | public static boolean nonStopOnException = false; 7 | public static boolean executeCmdStopAtSuccess=false; 8 | public static void resetFeature(){ 9 | nonStopOnException = false; 10 | isThisModDebugging=false; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/mixin/CommandSourceHook.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.mixin; 2 | 3 | import net.minecraft.server.command.ServerCommandSource; 4 | import org.spongepowered.asm.mixin.Final; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | import org.spongepowered.asm.mixin.Shadow; 7 | 8 | @Mixin(ServerCommandSource.class) 9 | public class CommandSourceHook { 10 | @Shadow @Final int level; 11 | public int fakeGetLevel() { 12 | return this.level; 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/resources/mcfdebugger.mixins.json: -------------------------------------------------------------------------------- 1 | { 2 | "required": true, 3 | "minVersion": "0.8", 4 | "package": "com.hb.mcfdebugger.mixin", 5 | "compatibilityLevel": "JAVA_8", 6 | "mixins": [ 7 | "regcmd", 8 | "DebugHook", 9 | "SetCommandIndex", 10 | "FunctionLoaderHook", 11 | "ErrorHook", 12 | "ServerLoad", 13 | "FunctionCommandHook", 14 | "ServerCommandSourceHook", 15 | "CommandSourceHook" 16 | ], 17 | "client": [ 18 | "ExampleMixin" 19 | ], 20 | "injectors": { 21 | "defaultRequire": 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/mixin/SetCommandIndex.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.mixin; 2 | 3 | import com.hb.mcfdebugger.McfDebugger; 4 | import net.minecraft.server.function.CommandFunction; 5 | import org.spongepowered.asm.mixin.Mixin; 6 | 7 | @Mixin(CommandFunction.CommandElement.class) 8 | public abstract class SetCommandIndex { 9 | public String funNamespace= McfDebugger.nowCmd.funNamespace; 10 | public String funPath= McfDebugger.nowCmd.funPath; 11 | public int cmdIndex= McfDebugger.nowCmd.cmdIndex; 12 | public boolean isLastCmd=McfDebugger.nowIsLastCmd; 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/mixin/ExampleMixin.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.mixin; 2 | 3 | import net.minecraft.client.gui.screen.TitleScreen; 4 | import org.spongepowered.asm.mixin.Mixin; 5 | import org.spongepowered.asm.mixin.injection.At; 6 | import org.spongepowered.asm.mixin.injection.Inject; 7 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 8 | 9 | import static com.hb.mcfdebugger.McfDebugger.LOG; 10 | 11 | @Mixin(TitleScreen.class) 12 | public class ExampleMixin { 13 | @Inject(at = @At("HEAD"), method = "init()V") 14 | private void init(CallbackInfo info) { 15 | //LOG("This line is printed by an example mod mixin!"); 16 | } 17 | } -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/PauseWaiter.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger; 2 | 3 | import com.hb.mcfdebugger.config.ConfigHolder; 4 | 5 | public class PauseWaiter { 6 | public static void WaitForNext(){ 7 | DebugThread.nextStep = false; 8 | while (!DebugThread.nextStep) { 9 | try { 10 | if(DebugThread.wsserver.getConnections().size()<=0){ 11 | ConfigHolder.debuggerMode="none"; 12 | break; 13 | } 14 | Thread.sleep(100); 15 | } catch (InterruptedException e) { 16 | e.printStackTrace(); 17 | } 18 | } 19 | DebugThread.nextStep = false; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/mixinHelpers/SendSyntaxError.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.mixinHelpers; 2 | 3 | import com.hb.mcfdebugger.DebugThread; 4 | import com.hb.mcfdebugger.McfDebugger; 5 | 6 | import java.util.LinkedHashMap; 7 | import java.util.Map; 8 | 9 | public class SendSyntaxError { 10 | public static void send(IllegalArgumentException e){ 11 | Map errorMap = new LinkedHashMap<>(); 12 | errorMap.put("namespace",McfDebugger.nowCmd.funNamespace); 13 | errorMap.put("path",McfDebugger.nowCmd.funPath); 14 | errorMap.put("cmdIndex", String.valueOf(McfDebugger.nowCmd.cmdIndex)); 15 | errorMap.put("errorMsg", e.getMessage()); 16 | DebugThread.sendObjMsgToDebugger(errorMap,"functionSyntaxError"); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/mixin/ServerLoad.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.mixin; 2 | 3 | import com.hb.mcfdebugger.McfDebugger; 4 | import com.hb.mcfdebugger.config.LoadConfig; 5 | import net.minecraft.server.MinecraftServer; 6 | import net.minecraft.util.WorldSavePath; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.injection.At; 9 | import org.spongepowered.asm.mixin.injection.Inject; 10 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 11 | 12 | @Mixin(MinecraftServer.class) 13 | public class ServerLoad { 14 | @Inject(method = "loadWorld", at = @At("HEAD")) 15 | private void serverLoaded(CallbackInfo ci) 16 | { 17 | McfDebugger.mcServer=(MinecraftServer)(Object)this; 18 | LoadConfig.load(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/config/LoadConfig.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.config; 2 | 3 | import com.hb.mcfdebugger.McfDebugger; 4 | import net.minecraft.util.WorldSavePath; 5 | 6 | public class LoadConfig { 7 | public static void load(){ 8 | McfDebugger.configManager=ConfigManager.getConfigManager(); 9 | McfDebugger.configManager.passConfigFile(McfDebugger.mcServer.getSavePath(WorldSavePath.ROOT).resolve("mcfDebugger_config.json").toFile()); 10 | McfDebugger.configManager.setupConfig(); 11 | McfDebugger.configList=McfDebugger.configManager.loadConfig(); 12 | if(McfDebugger.configList.get("enable")==1){ 13 | McfDebugger.thread.restartWsServer(McfDebugger.configList.get("port"),McfDebugger.configList.get("timeOut")); 14 | }else{ 15 | McfDebugger.thread.stopWsServer(); 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/SendCmdObj.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger; 2 | 3 | import org.jetbrains.annotations.Nullable; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | 8 | public class SendCmdObj{ 9 | public String funNamespace; 10 | public String funPath; 11 | public String cmdContent; 12 | public Integer cmdIndex; 13 | public boolean pause; 14 | public String exception=""; 15 | public Map source=new HashMap<>(); 16 | public SendCmdObj(String funNamespace, String funPath, Integer cmdIndex, String cmdContent, Boolean pause, @Nullable Map source){ 17 | this.funNamespace=funNamespace; 18 | this.cmdContent=cmdContent; 19 | this.funPath=funPath; 20 | this.cmdIndex=cmdIndex; 21 | this.pause=pause; 22 | if(source!=null){this.source=source;} 23 | } 24 | public SimpleCmdObj toSimple(){ 25 | return new SimpleCmdObj(this.funNamespace,this.funPath,this.cmdIndex); 26 | } 27 | } -------------------------------------------------------------------------------- /src/main/resources/fabric.mod.json: -------------------------------------------------------------------------------- 1 | { 2 | "schemaVersion": 1, 3 | "id": "mcfdebugger", 4 | "version": "${version}", 5 | 6 | "name": "Function Debugger(Mod)", 7 | "description": "A mod that can be used to debug mcfunctions. Also install the vscode part to get full experience!", 8 | "authors": [ 9 | "Huge_Black" 10 | ], 11 | "contact": { 12 | "homepage": "https://space.bilibili.com/13687417", 13 | "sources": "https://github.com/hugeBlack/McfDebugger_Mod", 14 | "bilibili": "https://space.bilibili.com/13687417" 15 | }, 16 | 17 | "license": "CC 1.0", 18 | "icon": "assets/mcfdebugger/icon.png", 19 | 20 | "environment": "*", 21 | "entrypoints": { 22 | "main": [ 23 | "com.hb.mcfdebugger.McfDebugger" 24 | ] 25 | }, 26 | "mixins": [ 27 | "mcfdebugger.mixins.json" 28 | ], 29 | 30 | "depends": { 31 | "fabricloader": ">=0.11.3", 32 | "fabric": "*", 33 | "minecraft": "1.17.x", 34 | "java": ">=16" 35 | }, 36 | "suggests": { 37 | "another-mod": "*" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/SimpleCmdObj.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger; 2 | 3 | public class SimpleCmdObj { 4 | public String funNamespace; 5 | public String funPath; 6 | public Integer cmdIndex; 7 | public SimpleCmdObj(String funNamespace, String funPath, Integer cmdIndex){ 8 | this.funNamespace=funNamespace; 9 | this.funPath=funPath; 10 | this.cmdIndex=cmdIndex; 11 | } 12 | public SimpleCmdObj(){ 13 | this.funNamespace=""; 14 | this.funPath=""; 15 | this.cmdIndex=0; 16 | } 17 | public void clear(){ 18 | this.funNamespace=""; 19 | this.funPath=""; 20 | this.cmdIndex=0; 21 | } 22 | public boolean equals(SimpleCmdObj compareObj){ 23 | return (this.cmdIndex==compareObj.cmdIndex&&this.funPath.equals(compareObj.funPath)&&this.funNamespace.equals(compareObj.funNamespace)); 24 | } 25 | public boolean isNext(SimpleCmdObj compareObj){ 26 | return (this.cmdIndex==compareObj.cmdIndex+1&&this.funPath.equals(compareObj.funPath)&&this.funNamespace.equals(compareObj.funNamespace)); 27 | } 28 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020-2021 Huge_Black 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/mixin/regcmd.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.mixin; 2 | import com.hb.mcfdebugger.commands.*; 3 | import com.mojang.brigadier.CommandDispatcher; 4 | import net.minecraft.server.command.CommandManager; 5 | import net.minecraft.server.command.ServerCommandSource; 6 | import org.spongepowered.asm.mixin.Final; 7 | import org.spongepowered.asm.mixin.Mixin; 8 | import org.spongepowered.asm.mixin.Shadow; 9 | import org.spongepowered.asm.mixin.injection.At; 10 | import org.spongepowered.asm.mixin.injection.Inject; 11 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 12 | import net.minecraft.server.command.TestCommand; 13 | 14 | @Mixin(CommandManager.class) 15 | public abstract class regcmd 16 | { 17 | 18 | @Shadow @Final private CommandDispatcher dispatcher; 19 | 20 | @Inject(method = "", at = @At("RETURN")) 21 | private void onRegister(CommandManager.RegistrationEnvironment arg, CallbackInfo ci) { 22 | DebuggerConfigCommand.register(this.dispatcher); 23 | DebuggerCommand.register(this.dispatcher); 24 | TestCommand.register(this.dispatcher); 25 | 26 | } 27 | 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/DebugThread.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger; 2 | 3 | import java.io.IOException; 4 | import java.net.UnknownHostException; 5 | 6 | import static com.hb.mcfdebugger.McfDebugger.LOG; 7 | 8 | public class DebugThread extends Thread { 9 | public static Boolean nextStep = false; 10 | public static wsServer wsserver; 11 | 12 | @Override 13 | public void run() { 14 | super.run(); 15 | LOG("debugThreadStarted!"); 16 | 17 | 18 | } 19 | public void restartWsServer(int port ,int timeOut){ 20 | try { 21 | this.stopWsServer(); 22 | wsserver = wsServer.startWs(port); 23 | wsserver.setConnectionLostTimeout(timeOut); 24 | } catch (UnknownHostException e) { 25 | e.printStackTrace(); 26 | } 27 | } 28 | public void stopWsServer(){ 29 | if(wsserver!=null){ 30 | try { 31 | wsserver.stop(); 32 | } catch (IOException e) { 33 | e.printStackTrace(); 34 | } catch (InterruptedException e) { 35 | e.printStackTrace(); 36 | } 37 | } 38 | 39 | } 40 | public static void sendObjMsgToDebugger(Object obj, String msgType) { 41 | if(wsserver!=null){ 42 | wsServer.MsgObj msgObj = new wsServer.MsgObj(msgType, obj); 43 | wsserver.sendObj(msgObj); 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/mixinHelpers/SendFunctionState.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.mixinHelpers; 2 | 3 | import com.hb.mcfdebugger.DebugThread; 4 | import com.hb.mcfdebugger.McfDebugger; 5 | import net.minecraft.command.CommandSource; 6 | import net.minecraft.server.function.CommandFunction; 7 | import net.minecraft.server.function.FunctionLoader; 8 | import net.minecraft.util.Identifier; 9 | 10 | import java.util.LinkedList; 11 | import java.util.List; 12 | import java.util.Map; 13 | 14 | public class SendFunctionState { 15 | public static void send(FunctionLoader f) { 16 | Map funMap = f.getFunctions(); 17 | List a = funObjParse(funMap); 18 | DebugThread.sendObjMsgToDebugger(a, "functionList"); 19 | } 20 | 21 | public static List funObjParse(Map cmdMap) { 22 | 23 | List functionArray = new LinkedList<>(); 24 | for (Map.Entry entry : cmdMap.entrySet()) { 25 | McfDebugger.HbCmdObj cmdObj = new McfDebugger.HbCmdObj(); 26 | cmdObj.funName = String.valueOf(entry.getKey()); 27 | for (CommandFunction.Element singleCmd : entry.getValue().getElements()) { 28 | cmdObj.commands.add(singleCmd.toString()); 29 | } 30 | functionArray.add(cmdObj); 31 | } 32 | //McfDebugger.lastFunctionMap = cmdMap; 33 | return functionArray; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/mixinHelpers/ReadCommandSource.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.mixinHelpers; 2 | 3 | import com.hb.mcfdebugger.McfDebugger; 4 | import net.minecraft.server.command.ServerCommandSource; 5 | 6 | import java.lang.reflect.Method; 7 | import java.util.LinkedHashMap; 8 | import java.util.Map; 9 | 10 | public class ReadCommandSource { 11 | public static Map read(ServerCommandSource source){ 12 | Map sourceMap =new LinkedHashMap<>(); 13 | if(source.getEntity()!=null){ 14 | sourceMap.put("entityName",source.getEntity().getEntityName()); 15 | sourceMap.put("entityUuid",source.getEntity().getUuidAsString()); 16 | }else{ 17 | sourceMap.put("entityName","None(Server)"); 18 | sourceMap.put("entityUuid","None"); 19 | } 20 | sourceMap.put("pos" ,source.getPosition().toString()); 21 | sourceMap.put("rotation","("+source.getRotation().y+", "+source.getRotation().x+")"); 22 | sourceMap.put("world",source.getWorld().getRegistryKey().getValue().toString()); 23 | try { 24 | Method levelMethod = source.getClass().getDeclaredMethod("fakeGetLevel"); 25 | sourceMap.put("level", String.valueOf(levelMethod.invoke(source))); 26 | } catch (ReflectiveOperationException e) { 27 | sourceMap.put("level","Error"); 28 | } 29 | sourceMap.put("commandsLeftToMaxChainLength", String.valueOf(McfDebugger.commandsLeftToMaxChainLength)); 30 | return sourceMap; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/commandHelpers/GetEntity.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.commandHelpers; 2 | 3 | import com.hb.mcfdebugger.DebugThread; 4 | import com.mojang.brigadier.context.CommandContext; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | import net.minecraft.command.argument.EntityArgumentType; 7 | import net.minecraft.entity.Entity; 8 | import net.minecraft.server.command.ServerCommandSource; 9 | 10 | import java.util.*; 11 | 12 | public class GetEntity { 13 | public static void get(CommandContext cmd) throws CommandSyntaxException{ 14 | List entityList = new LinkedList<>(); 15 | Iterator var2 =EntityArgumentType.getOptionalEntities(cmd,"targets").iterator(); 16 | while(var2.hasNext()){ 17 | Entity entity =(Entity)var2.next(); 18 | SendEntityObj sendEntityObj = new SendEntityObj(); 19 | sendEntityObj.name=entity.getEntityName(); 20 | sendEntityObj.uuid=entity.getUuidAsString(); 21 | sendEntityObj.type=entity.getType().toString(); 22 | sendEntityObj.detail.put("tags",entity.getScoreboardTags()); 23 | sendEntityObj.detail.put("pos",entity.getPos().toString()); 24 | sendEntityObj.detail.put("rotation","("+entity.getRotationClient().y+", "+entity.getRotationClient().x+")"); 25 | entityList.add(sendEntityObj); 26 | } 27 | DebugThread.sendObjMsgToDebugger(entityList,"getEntityResult"); 28 | } 29 | public static class SendEntityObj{ 30 | public String name; 31 | public String uuid; 32 | public String type; 33 | public Map detail =new LinkedHashMap<>(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/mixin/ServerCommandSourceHook.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.mixin; 2 | 3 | import com.hb.mcfdebugger.DebugThread; 4 | import com.hb.mcfdebugger.McfDebugger; 5 | import com.hb.mcfdebugger.SendCmdObj; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import net.minecraft.server.command.ServerCommandSource; 8 | import net.minecraft.text.Text; 9 | import org.spongepowered.asm.mixin.Mixin; 10 | import org.spongepowered.asm.mixin.injection.At; 11 | import org.spongepowered.asm.mixin.injection.Inject; 12 | import com.hb.mcfdebugger.commands.DebuggerCommand; 13 | import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; 14 | 15 | @Mixin(ServerCommandSource.class) 16 | public class ServerCommandSourceHook { 17 | @Inject(method = "sendFeedback", at = @At("RETURN")) 18 | public void sendFeedback(Text message, boolean broadcastToOps, CallbackInfo ci) throws CommandSyntaxException { 19 | if (McfDebugger.lastCmdObj!=null && McfDebugger.lastCmdObj.toSimple().isNext(McfDebugger.nowLoudCmd)) { 20 | McfDebugger.hbPair send=new McfDebugger.hbPair("output",message.getString()); 21 | DebugThread.sendObjMsgToDebugger(send,"loudResult"); 22 | McfDebugger.nowLoudCmd.clear(); 23 | throw DebuggerCommand.LOGGER_HIT_EXCEPTION.create(); 24 | } 25 | if (McfDebugger.lastCmdObj!=null && McfDebugger.lastCmdObj.toSimple().isNext(McfDebugger.nowLogCmd)) { 26 | 27 | McfDebugger.hbPair send=new McfDebugger.hbPair("output",new McfDebugger.hbPair(message.getString(),McfDebugger.lastCmdObj)); 28 | DebugThread.sendObjMsgToDebugger(send,"logResult"); 29 | McfDebugger.nowLogCmd.clear(); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /README_zh_cn.md: -------------------------------------------------------------------------------- 1 | # Minecraft函数调试器 2 | 3 | ![McFD图标](https://i.loli.net/2021/02/17/3lkRqAjT5hNGorJ.png) 4 | 5 | 语言: 6 | [English](https://github.com/hugeBlack/McfDebugger_Mod/blob/master/README.md) \ 简体中文 7 | 8 | *(如果你只是想下载模组,请移步右边的[Release](https://github.com/hugeBlack/McfDebugger_Mod/releases)板块)* 9 | 10 | ## 简介 11 | 12 | 你还在为写函数的时候哪个指令没有执行而不知所措吗? 13 | 你还在为调试而各种/say吗 14 | 你还在为不知道哪个实体执行了function而一头雾水吗(跑 15 | 16 | Minecraft Function Debugger (Mod),简称 McFD,中文名「函数调试器(模组部分)」是一个能够为 Minecraft Java版的函数提供调试支持的Mod,你可以使用它像调试其他语言一样调试mcfunction 17 | 18 | 虽然您可以在有Websocket知识,Json读写能力的基础上仅使用本模组实现调试功能 19 | 20 | 我还是建议您务必配套使用 [对应的Vscode插件](https://github.com/hugeBlack/McfDebugger_Extension) 来获得最佳体验 21 | 22 | ## 修改 23 | 24 | * 对游戏性没有任何修改,主要对函数有关的方法进行了Overwrite,虽然已经尽可能地进行最少的修改,但可能对游戏的行为产生影响。 25 | * 本模组通过Websocket与Vscode插件通信 26 | 27 | ## 指令 28 | 29 | 配置指令: /mcfDebuggerConfig 需要4级权限 30 | 31 | * /mcfDebuggerConfig port <端口:整数> 立即修改端口并重启内置的Websocket服务器,默认1453 32 | * /mcfDebuggerConfig enable 立即启用/禁用内置的Websocket服务器,默认true 33 | * /mcfDebuggerConfig timeOut <秒:整数> 设置连接超时的时间,不宜太长,默认5 34 | * 每个世界不共享配置文件 35 | 36 | 调试器指令:/debuggerCmd 37 | 38 | * 用于实现调试器功能,没有明显效果,不会消耗MaxChainLength 39 | 40 | ## 使用 41 | 42 | 具体使用方式请移步 [Vscode插件](https://github.com/hugeBlack/McfDebugger_Extension) 部分. 43 | 44 | ## 免责声明 45 | 46 | VSCode插件部分只会读取你的数据包,因此你的数据包是安全的。然而,在某些极端情况下,模组部分可能会因为某些原因损坏你的存档,尽管经经过了些许测试,但是这种情况仍有可能发生。 47 | 并且,由于使用Webocket通信,你的设备可能被恶意入侵。 48 | 遗憾的是,我们并不能在这些情况中提供任何帮助。 49 | 所以请请随时备份您宝贵的存档并请自行配置防火墙来保证您的设备的安全。 50 | 51 | ## Q&A 52 | 53 | 1. Q:为什么使用Fabric而不是Forge之类的? 54 | A:因为Fabric光速支持快照等版本而FML更新慢。 55 | 2. Q:为什么游戏关闭不了? 56 | A:检查一下你的调试器VSCode部分是不是正在暂停状态,否则超时时间一过就会自动关闭,**切记不要去杀进程,可能会导致存档损坏** 57 | 3. Q:为什么提示文字显示不正常? 58 | A:请安装[Fabric-Api](https://www.curseforge.com/minecraft/mc-mods/fabric-api)! 59 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/mixin/FunctionCommandHook.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.mixin; 2 | 3 | import com.hb.mcfdebugger.McfDebugger; 4 | import net.minecraft.server.command.FunctionCommand; 5 | import net.minecraft.server.command.ServerCommandSource; 6 | import net.minecraft.server.function.CommandFunction; 7 | import net.minecraft.text.TranslatableText; 8 | import org.spongepowered.asm.mixin.Mixin; 9 | import org.spongepowered.asm.mixin.Overwrite; 10 | 11 | import java.util.Collection; 12 | import java.util.Iterator; 13 | 14 | @Mixin(FunctionCommand.class) 15 | public class FunctionCommandHook { 16 | @Overwrite 17 | private static int execute(ServerCommandSource source, Collection functions) { 18 | int i = 0; 19 | String cmdList = ""; 20 | CommandFunction commandFunction; 21 | McfDebugger.lastCallCmdObj = McfDebugger.lastCmdObj==null?null:McfDebugger.lastCmdObj; 22 | McfDebugger.tagFunctionLeft.add(functions.size()); 23 | for (Iterator var3 = functions.iterator(); var3.hasNext(); i += source.getServer().getCommandFunctionManager().execute(commandFunction, source.withSilent().withMaxLevel(2))) { 24 | 25 | commandFunction = (CommandFunction) var3.next(); 26 | //modified 27 | cmdList += commandFunction.getId().toString() + (var3.hasNext() ? "," : ""); 28 | if(!var3.hasNext()){ 29 | McfDebugger.hbPair p = new McfDebugger.hbPair(cmdList,McfDebugger.lastCallCmdObj); 30 | McfDebugger.stackList.add(p); 31 | } 32 | //modified 33 | } 34 | 35 | if (functions.size() == 1) { 36 | source.sendFeedback(new TranslatableText("commands.function.success.single", new Object[]{i, ((CommandFunction) functions.iterator().next()).getId()}), true); 37 | } else { 38 | source.sendFeedback(new TranslatableText("commands.function.success.multiple", new Object[]{i, functions.size()}), true); 39 | } 40 | //modified 41 | McfDebugger.lastCallCmdObj=null; 42 | McfDebugger.lastCmdObj = null; 43 | //modified 44 | return i; 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/wsServer.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger; 2 | import com.google.gson.Gson; 3 | import org.java_websocket.WebSocket; 4 | import org.java_websocket.handshake.ClientHandshake; 5 | import org.java_websocket.server.WebSocketServer; 6 | 7 | import java.net.InetSocketAddress; 8 | import java.net.UnknownHostException; 9 | import static com.hb.mcfdebugger.McfDebugger.LOG; 10 | 11 | public class wsServer extends WebSocketServer { 12 | 13 | public wsServer(int port) throws UnknownHostException { 14 | super(new InetSocketAddress(port)); 15 | LOG("websocket Server start at port:"+port); 16 | } 17 | 18 | @Override 19 | public void onOpen(WebSocket conn, ClientHandshake clientHandshake) { 20 | LOG("new connection ===" + conn.getRemoteSocketAddress().getAddress().getHostAddress()); 21 | } 22 | 23 | @Override 24 | public void onClose(WebSocket conn, int code, String reason, boolean remote) { 25 | 26 | } 27 | 28 | @Override 29 | public void onMessage(WebSocket conn, String message) { 30 | LOG("you have a new message: "+ message); 31 | //向客户端发送消息 32 | wsCommandParser.DebugHandler(message); 33 | //conn.send(message); 34 | } 35 | 36 | @Override 37 | public void onError(WebSocket conn, Exception e) { 38 | //e.printStackTrace(); 39 | if( conn != null ) { 40 | //some errors like port binding failed may not be assignable to a specific websocket 41 | } 42 | } 43 | 44 | @Override 45 | public void onStart() { 46 | LOG("Ws Started!"); 47 | } 48 | 49 | public static wsServer startWs(int port) throws UnknownHostException { 50 | wsServer s = new wsServer(port); 51 | s.start(); 52 | return s; 53 | } 54 | 55 | public void sendObj(MsgObj msgObj){ 56 | Gson gson=new Gson(); 57 | String backMsg=gson.toJson(msgObj); 58 | this.broadcast(backMsg); 59 | 60 | } 61 | public static class MsgObj{ 62 | public String msgType; 63 | public Object bodyObj; 64 | public MsgObj(String msgType,Object bodyObj){ 65 | this.msgType=msgType; 66 | this.bodyObj=bodyObj; 67 | } 68 | } 69 | } -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/McfDebugger.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger; 2 | 3 | import com.hb.mcfdebugger.config.ConfigHolder; 4 | import com.hb.mcfdebugger.config.ConfigManager; 5 | import net.fabricmc.api.ModInitializer; 6 | import net.minecraft.command.CommandSource; 7 | import net.minecraft.server.MinecraftServer; 8 | import net.minecraft.server.function.CommandFunction; 9 | import net.minecraft.server.function.FunctionLoader; 10 | import net.minecraft.util.Identifier; 11 | import net.minecraft.util.WorldSavePath; 12 | 13 | import java.util.*; 14 | 15 | public class McfDebugger implements ModInitializer { 16 | public static boolean isThisModDebugging=false; 17 | 18 | public static class HbCmdObj{ 19 | public String funName; 20 | public List commands; 21 | public HbCmdObj() { 22 | this.funName=""; 23 | this.commands=new LinkedList<>(); 24 | } 25 | } 26 | 27 | public static SimpleCmdObj nowCmd = new SimpleCmdObj(); 28 | public static boolean nowIsLastCmd=false; 29 | 30 | public static SendCmdObj lastCmdObj; 31 | public static MinecraftServer mcServer; 32 | 33 | public static int nowCommandCount=0; 34 | public static int commandsLeftToMaxChainLength=0; 35 | 36 | public static SimpleCmdObj nowMuteCmd = new SimpleCmdObj(); 37 | public static SimpleCmdObj nowLoudCmd = new SimpleCmdObj(); 38 | public static SimpleCmdObj nowLogCmd = new SimpleCmdObj(); 39 | 40 | public static List stackList= new LinkedList<>(); 41 | public static List tagFunctionLeft= new LinkedList<>(); 42 | public static SendCmdObj lastCallCmdObj=null; 43 | public static class hbPair{ 44 | public String key; 45 | public Object value; 46 | public hbPair(String key,Object value){ 47 | this.key=key; 48 | this.value=value; 49 | } 50 | } 51 | 52 | public static ConfigManager configManager=ConfigManager.getConfigManager(); 53 | public static LinkedHashMap configList=new LinkedHashMap<>(); 54 | 55 | public static DebugThread thread = new DebugThread(); 56 | 57 | @Override 58 | public void onInitialize() { 59 | LOG("Hello Fabric world!"); 60 | thread.start(); 61 | 62 | } 63 | public static void LOG(Object msgObj){ 64 | if(ConfigHolder.isThisModDebugging){ 65 | System.out.println(msgObj.toString()); 66 | } 67 | } 68 | 69 | public static boolean howeverStop = false; 70 | 71 | public static boolean stepOut = false; 72 | public static boolean stepIn = false; 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/mixin/DebugHook.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.mixin; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.ArrayDeque; 5 | import java.util.Deque; 6 | 7 | import com.hb.mcfdebugger.*; 8 | import com.hb.mcfdebugger.mixinHelpers.SendCommandState; 9 | import com.mojang.brigadier.ParseResults; 10 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 11 | import net.minecraft.server.command.ServerCommandSource; 12 | import net.minecraft.server.function.CommandFunction; 13 | import net.minecraft.server.function.CommandFunctionManager; 14 | 15 | import org.jetbrains.annotations.Nullable; 16 | import org.spongepowered.asm.mixin.Final; 17 | import org.spongepowered.asm.mixin.Mixin; 18 | import org.spongepowered.asm.mixin.Overwrite; 19 | import org.spongepowered.asm.mixin.Shadow; 20 | 21 | @Mixin(CommandFunction.CommandElement.class) 22 | public abstract class DebugHook implements CommandFunction.Element{ 23 | CommandFunction.Element element=(CommandFunction.Element)this; 24 | @Shadow @Final ParseResults parsed; 25 | @Shadow 26 | abstract int execute(CommandFunctionManager manager, ServerCommandSource source) throws CommandSyntaxException; 27 | @Overwrite 28 | public void execute (CommandFunctionManager manager, ServerCommandSource source, Deque entries, int maxChainLength, int depth, @Nullable CommandFunctionManager.Tracer tracer) throws CommandSyntaxException { 29 | SendCommandState.send(element,source); 30 | boolean isLastCmd=false; 31 | try { 32 | Field funNamespaceField = element.getClass().getDeclaredField("isLastCmd"); 33 | isLastCmd= (Boolean) funNamespaceField.get(element); 34 | } catch (ReflectiveOperationException e) { } 35 | if(isLastCmd) { 36 | int i =McfDebugger.tagFunctionLeft.size()-1; 37 | if(i>=0){ 38 | if(McfDebugger.tagFunctionLeft.get(i)<=1){ 39 | McfDebugger.stackList.remove(McfDebugger.stackList.size() - 1); 40 | McfDebugger.tagFunctionLeft.remove(i); 41 | }else{ 42 | McfDebugger.tagFunctionLeft.set(i,McfDebugger.tagFunctionLeft.get(i)-1); 43 | } 44 | } 45 | } 46 | //origin 47 | if (tracer != null) { 48 | String string = this.parsed.getReader().getString(); 49 | tracer.traceCommandStart(depth, string); 50 | int i = this.execute(manager, source); 51 | tracer.traceCommandEnd(depth, string, i); 52 | } else { 53 | this.execute(manager, source); 54 | } 55 | //origin 56 | 57 | } 58 | } -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/mixinHelpers/SendCommandState.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.mixinHelpers; 2 | 3 | import com.hb.mcfdebugger.*; 4 | import com.hb.mcfdebugger.config.ConfigHolder; 5 | import net.minecraft.command.argument.Vec3ArgumentType; 6 | import net.minecraft.entity.Entity; 7 | import net.minecraft.server.command.ServerCommandSource; 8 | import net.minecraft.server.function.CommandFunction; 9 | import net.minecraft.util.math.Vec2f; 10 | import net.minecraft.util.math.Vec3d; 11 | import net.minecraft.world.World; 12 | 13 | import java.lang.reflect.Field; 14 | import java.lang.reflect.Method; 15 | import java.util.LinkedHashMap; 16 | import java.util.Map; 17 | 18 | public class SendCommandState { 19 | public static void send(CommandFunction.Element element, ServerCommandSource source){ 20 | if(!ConfigHolder.debuggerMode.equals("none")) { 21 | String funNamespace = null; 22 | String funPath = null; 23 | Integer cmdIndex = -1; 24 | try { 25 | Field funNamespaceField = element.getClass().getDeclaredField("funNamespace"); 26 | funNamespace = (String) funNamespaceField.get(element); 27 | } catch (ReflectiveOperationException e) { 28 | } 29 | try { 30 | Field funPathField = element.getClass().getDeclaredField("funPath"); 31 | funPath = (String) funPathField.get(element); 32 | } catch (ReflectiveOperationException e) { 33 | } 34 | try { 35 | Field cmdIndexField = element.getClass().getDeclaredField("cmdIndex"); 36 | cmdIndex = (Integer) cmdIndexField.get(element); 37 | } catch (ReflectiveOperationException e) { 38 | } 39 | 40 | if(ConfigHolder.debuggerMode.equals("byStep")|| (cmdIndex==1 && McfDebugger.stepIn) ||wsCommandParser.isInPauseList(funNamespace,funPath,cmdIndex)){ 41 | McfDebugger.stepIn=false; 42 | SendCmdObj sendCmdObj = new SendCmdObj(funNamespace, funPath, cmdIndex, element.toString(),true,ReadCommandSource.read(source)); 43 | McfDebugger.lastCmdObj=sendCmdObj; 44 | DebugThread.sendObjMsgToDebugger(McfDebugger.stackList,"stackReport"); 45 | DebugThread.sendObjMsgToDebugger(sendCmdObj,"commandReport"); 46 | PauseWaiter.WaitForNext(); 47 | }else{ 48 | SendCmdObj sendCmdObj = new SendCmdObj(funNamespace, funPath, cmdIndex, element.toString(),false,null); 49 | McfDebugger.lastCmdObj=sendCmdObj; 50 | DebugThread.sendObjMsgToDebugger(sendCmdObj,"commandReport"); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/config/ConfigManager.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.config; 2 | import com.google.gson.Gson; 3 | 4 | import java.io.*; 5 | import java.util.Iterator; 6 | import java.util.LinkedHashMap; 7 | import java.util.Map; 8 | 9 | import static com.hb.mcfdebugger.McfDebugger.LOG; 10 | 11 | final public class ConfigManager { 12 | private static ConfigManager cm; 13 | private File configFile; 14 | private Gson gson; 15 | private LinkedHashMap configList; 16 | 17 | public static ConfigManager getConfigManager(){ 18 | if (cm == null){ 19 | cm = new ConfigManager(); 20 | } 21 | return cm; 22 | } 23 | 24 | private ConfigManager(){ 25 | initConfigList(); 26 | gson = new Gson(); 27 | } 28 | 29 | private void initConfigList(){ 30 | configList = setupRulesMap(); 31 | } 32 | 33 | public LinkedHashMap setupRulesMap(){ 34 | LinkedHashMap m = new LinkedHashMap<>(); 35 | m.put("port", 1453); 36 | m.put("timeOut", 5); 37 | m.put("enable", 1); 38 | return m; 39 | } 40 | 41 | public void passConfigFile(File f){ 42 | this.configFile = f; 43 | } 44 | 45 | public void setupConfig(){ 46 | loadConfig(); 47 | LOG("Config Loaded"); 48 | } 49 | 50 | public LinkedHashMap loadConfig(){ 51 | if (this.configFile.exists()){ 52 | try (FileReader reader = new FileReader(this.configFile)){ 53 | Iterator m =gson.fromJson(reader,new LinkedHashMap().getClass()).entrySet().iterator(); 54 | while(m.hasNext()){ 55 | Map.Entry entry = (Map.Entry)m.next(); 56 | this.configList.put(entry.getKey().toString(),((Double)entry.getValue()).intValue()); 57 | } 58 | } catch (IOException e) { 59 | LOG("Failed to parse config"); 60 | throw new RuntimeException("Could not parse config", e); 61 | } 62 | } else { 63 | initConfigList(); 64 | writeConfig(); 65 | } 66 | 67 | return configList; 68 | } 69 | 70 | public void writeConfig(){ 71 | File dir = configFile.getParentFile(); 72 | 73 | if (!dir.exists()) { 74 | if (!dir.mkdirs()) { 75 | LOG("Failed to create the parent directory"); 76 | throw new RuntimeException("Failed to create the parent directory"); 77 | } 78 | } else if (!dir.isDirectory()) { 79 | LOG("Failed to create config file"); 80 | throw new RuntimeException("The parent is not a directory"); 81 | } 82 | 83 | try (FileWriter writer = new FileWriter(configFile)) { 84 | gson.toJson(configList, writer); 85 | } catch (IOException e) { 86 | LOG("Failed to save config"); 87 | throw new RuntimeException("Could not save config file", e); 88 | } 89 | } 90 | 91 | 92 | 93 | } 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Minecraft Function Debugger 2 | 3 | ![McFD Banner](https://i.loli.net/2021/02/17/3lkRqAjT5hNGorJ.png) 4 | 5 | Languages: 6 | English \ [简体中文](https://github.com/hugeBlack/McfDebugger_Mod/blob/master/README_zh_cn.md) 7 | 8 | *( If you only want to download this mod, please see the [Release](https://github.com/hugeBlack/McfDebugger_Mod/releases) section on the right )* 9 | 10 | ## Intoduction 11 | 12 | Minecraft Function Debugger (Mod Part), McFD dor short, , McFD for short, is a vscode extension that provides support for debugging Minecraft functions. You can use it to debug mcfunctions like debugging other languages. 13 | 14 | Although you can use this mod only with sufficient knowledge of Websocket and Json, I still suggest that you use the [corresponding VSCode extension](https://github.com/hugeBlack/McfDebugger_Extension) to get full experience. 15 | 16 | ## Modification 17 | 18 | * It made no change to gaming features. However, it overwrites some of the methods of Function system. Although we tried our best to make as minior changes as possible, it may change the behaviour of the game in some aspect. 19 | * This mod uses Websocket to communicate with the VSCode extension. 20 | 21 | ## Commands 22 | 23 | ### Configuration command 24 | 25 | /mcfDebuggerConfig 26 | 27 | * /mcfDebuggerConfig port 28 | Change the port of Websocket Server inside and restart it (if enabled). 1453 by default. 29 | * /mcfDebuggerConfig enable 30 | Enable/disable the Websocket Server. True by default. 31 | * /mcfDebuggerConfig timeOut 32 | Set the time out. It should not be too large. 5 by default. 33 | * Configurations are not shared among worlds. 34 | * Require permission level 4. 35 | 36 | ### Debugger Command:/debuggerCmd 37 | 38 | * It is used by the debugger to implement certain features. It has no output and don't cost MaxChainLength. 39 | * Require permission level 2. 40 | 41 | ## Usage 42 | 43 | Please see the [Vscode Extension](https://github.com/hugeBlack/McfDebugger_Extension) part for usage. 44 | 45 | ## Disclaimer 46 | 47 | The VSCode Extension part reads your datapack only, so your datapack is safe definitely. However, the mod part may damage your save. Despite some tests, it could happen under extreme circumstances. 48 | 49 | Also, since it communicate with the game with Websocket your device may be attacked by ill-intentioned people. 50 | 51 | Unfortunately, we can't provide help in these cases. So I strongly recommend that you **should backup** your awesome world. AndP lease config your Firewall to defend your devide from potential attacks. 52 | 53 | ## Q&A 54 | 55 | 1. Q: Why using Fabric instead of Forge? 56 | A:Because Fabric supports Snapshots quickly while Forge don't. 57 | 2. Q: Why I can't stop my game? 58 | A:Check if the game is paused by a exception in VSCode. If it isn't, wait for the timeout and the game will quit automatically (that's why you should set a short timeout!). **NEVER shut down the game in that state with task manager or other means** because it may damage your save! 59 | 3. Q:Why translable texts were not displayed correctly? 60 | A:Please Install [Fabric-Api](https://www.curseforge.com/minecraft/mc-mods/fabric-api)! -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/mixin/ErrorHook.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.mixin; 2 | 3 | import com.hb.mcfdebugger.*; 4 | import com.hb.mcfdebugger.config.ConfigHolder; 5 | import com.hb.mcfdebugger.mixinHelpers.ReadCommandSource; 6 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 7 | import net.minecraft.server.command.ServerCommandSource; 8 | import net.minecraft.server.function.CommandFunction; 9 | import net.minecraft.server.function.CommandFunctionManager; 10 | import org.jetbrains.annotations.Nullable; 11 | import org.spongepowered.asm.mixin.Final; 12 | import org.spongepowered.asm.mixin.Mixin; 13 | import org.spongepowered.asm.mixin.Overwrite; 14 | import org.spongepowered.asm.mixin.Shadow; 15 | 16 | import java.lang.reflect.Field; 17 | import java.lang.reflect.Method; 18 | import java.util.ArrayDeque; 19 | import java.util.Deque; 20 | import java.util.LinkedHashMap; 21 | import java.util.Map; 22 | 23 | @Mixin(CommandFunctionManager.Entry.class) 24 | public class ErrorHook { 25 | @Shadow @Final ServerCommandSource source; 26 | @Shadow @Final int depth; 27 | @Shadow @Final CommandFunction.Element element; 28 | @Overwrite 29 | public void execute(CommandFunctionManager manager, Deque entries, int maxChainLength, @Nullable CommandFunctionManager.Tracer tracer) { 30 | try { 31 | this.element.execute(manager, this.source, entries, maxChainLength, this.depth, tracer); 32 | } catch (CommandSyntaxException var6) { 33 | if (tracer != null) { 34 | tracer.traceError(this.depth, var6.getRawMessage().getString()); 35 | } 36 | sendError(var6); 37 | } catch (Exception var7) { 38 | if (tracer != null) { 39 | tracer.traceError(this.depth, var7.getMessage()); 40 | } 41 | sendError(var7); 42 | } 43 | if (McfDebugger.lastCmdObj!=null && McfDebugger.lastCmdObj.toSimple().isNext(McfDebugger.nowMuteCmd)) { 44 | McfDebugger.nowMuteCmd.clear(); 45 | } 46 | } 47 | public void sendError(Exception exception) { 48 | if (!ConfigHolder.debuggerMode.equals("none")) { 49 | String exceptionMsg = exception.toString(); 50 | if (exceptionMsg != "") { 51 | if (McfDebugger.lastCmdObj!=null && !McfDebugger.lastCmdObj.toSimple().isNext(McfDebugger.nowMuteCmd)) { 52 | int mode=ConfigHolder.nonStopOnException?1:0; 53 | McfDebugger.lastCmdObj.exception=exceptionMsg; 54 | McfDebugger.lastCmdObj.pause=true; 55 | SendCmdObj sendCmdObj = new SendCmdObj(McfDebugger.lastCmdObj.funNamespace, McfDebugger.lastCmdObj.funPath, McfDebugger.lastCmdObj.cmdIndex, element.toString(),true, ReadCommandSource.read(source)); 56 | sendCmdObj.exception=exceptionMsg; 57 | if(mode==0||McfDebugger.howeverStop){ 58 | McfDebugger.howeverStop=false; 59 | DebugThread.sendObjMsgToDebugger(McfDebugger.stackList,"stackReport"); 60 | DebugThread.sendObjMsgToDebugger(sendCmdObj,"errorCommandReport"); 61 | PauseWaiter.WaitForNext(); 62 | }else if (mode==1){ 63 | DebugThread.sendObjMsgToDebugger(sendCmdObj,"nonStopErrorCommandReport"); 64 | } 65 | } 66 | } 67 | } 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/mixinHelpers/FakeCommandFunctionCreate.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.mixinHelpers; 2 | 3 | import com.google.common.collect.Lists; 4 | import com.hb.mcfdebugger.McfDebugger; 5 | import com.hb.mcfdebugger.SimpleCmdObj; 6 | import com.mojang.brigadier.CommandDispatcher; 7 | import com.mojang.brigadier.ParseResults; 8 | import com.mojang.brigadier.StringReader; 9 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 10 | import net.minecraft.server.command.CommandManager; 11 | import net.minecraft.server.command.ServerCommandSource; 12 | import net.minecraft.server.function.CommandFunction; 13 | import net.minecraft.util.Identifier; 14 | 15 | import java.util.LinkedHashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | public class FakeCommandFunctionCreate { 20 | public static CommandFunction create(Identifier id, CommandDispatcher commandDispatcher, ServerCommandSource serverCommandSource, List list) { 21 | List list2 = Lists.newArrayListWithCapacity(list.size()); 22 | 23 | for(int i = 0; i < list.size(); ++i) { 24 | int j = i + 1; 25 | //modified 26 | McfDebugger.nowCmd=new SimpleCmdObj(id.getNamespace(),id.getPath(),i); 27 | McfDebugger.nowIsLastCmd=(i == list.size()-1); 28 | //modified 29 | String string = ((String)list.get(i)).trim(); 30 | StringReader stringReader = new StringReader(string); 31 | if (stringReader.canRead() && (stringReader.peek() != '#'|| string.startsWith("#@"))) { 32 | if (stringReader.peek() == '/') { 33 | stringReader.skip(); 34 | if (stringReader.peek() == '/') { 35 | IllegalArgumentException e= new IllegalArgumentException("Unknown or invalid command '" + string + "' on line " + j + " (if you intended to make a comment, use '#' not '//')"); 36 | SendSyntaxError.send(e); 37 | throw e; 38 | } 39 | 40 | String string2 = stringReader.readUnquotedString(); 41 | IllegalArgumentException e = new IllegalArgumentException("Unknown or invalid command '" + string + "' on line " + j + " (did you mean '" + string2 + "'? Do not use a preceding forwards slash.)"); 42 | SendSyntaxError.send(e); 43 | throw e; 44 | } 45 | 46 | try { 47 | //modified 48 | ParseResults parseResults; 49 | 50 | if(!string.startsWith("#@")){ 51 | parseResults = commandDispatcher.parse(stringReader, serverCommandSource); 52 | }else{ 53 | parseResults = commandDispatcher.parse(new StringReader("debuggerCmd "+string.substring(2)), serverCommandSource); 54 | } if (parseResults.getReader().canRead()) { 55 | throw CommandManager.getException(parseResults); 56 | } 57 | 58 | list2.add(new CommandFunction.CommandElement(parseResults)); 59 | } catch (CommandSyntaxException var10) { 60 | IllegalArgumentException e = new IllegalArgumentException("Whilst parsing command on line " + j + ": " + var10.getMessage()); 61 | SendSyntaxError.send(e); 62 | throw e; 63 | } 64 | } 65 | } 66 | return new CommandFunction(id, (CommandFunction.Element[])list2.toArray(new CommandFunction.Element[0])); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/commandHelpers/ReadScoreboard.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.commandHelpers; 2 | import com.hb.mcfdebugger.DebugThread; 3 | import com.hb.mcfdebugger.McfDebugger; 4 | import com.mojang.brigadier.context.CommandContext; 5 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 6 | import net.minecraft.command.argument.ScoreHolderArgumentType; 7 | import net.minecraft.command.argument.ScoreboardObjectiveArgumentType; 8 | import net.minecraft.scoreboard.ScoreboardObjective; 9 | import net.minecraft.scoreboard.ScoreboardPlayerScore; 10 | import net.minecraft.scoreboard.ServerScoreboard; 11 | 12 | import java.util.*; 13 | 14 | public class ReadScoreboard { 15 | public static void parseAllScoreboard()throws Error{ 16 | 17 | Collection objectiveList = McfDebugger.mcServer.getScoreboard().getObjectives(); 18 | List sendObjectiveList=new LinkedList<>(); 19 | for(ScoreboardObjective nowObjective:objectiveList){ 20 | SendObjective sendObjective=new SendObjective(nowObjective); 21 | sendObjectiveList.add(sendObjective); 22 | } 23 | DebugThread.sendObjMsgToDebugger(sendObjectiveList,"scoreboardList"); 24 | } 25 | public static void parseScoreboardByEntity(CommandContext commandContext) throws CommandSyntaxException { 26 | ServerScoreboard objectives = McfDebugger.mcServer.getScoreboard(); 27 | Collection entities=ScoreHolderArgumentType.getScoreboardScoreHolders(commandContext, "targets"); 28 | entities.forEach((String entity)->{ 29 | Map playerScores=objectives.getPlayerObjectives(entity); 30 | PlayerScorePair playerScorePair=new PlayerScorePair(entity,playerScores); 31 | DebugThread.sendObjMsgToDebugger(playerScorePair,"getScoresResultByEntity"); 32 | 33 | }); 34 | } 35 | public static void parseScoreboardByObjective(CommandContext commandContext) throws CommandSyntaxException { 36 | ServerScoreboard objectives = McfDebugger.mcServer.getScoreboard(); 37 | ScoreboardObjective objective= ScoreboardObjectiveArgumentType.getObjective(commandContext,"objective"); 38 | Collection scores=objectives.getAllPlayerScores(objective); 39 | ObjectiveScorePair objectiveScorePair = new ObjectiveScorePair(objective.getName(),scores); 40 | DebugThread.sendObjMsgToDebugger(objectiveScorePair,"getScoresResultByObjective"); 41 | 42 | } 43 | 44 | public static class SendObjective{ 45 | public Map playerScores=new HashMap(); 46 | public String name; 47 | public String criterion; 48 | public String disPlayName; 49 | public SendObjective(ScoreboardObjective mcObjective){ 50 | this.name=mcObjective.getName(); 51 | this.criterion=mcObjective.getCriterion().getName(); 52 | this.disPlayName=mcObjective.getDisplayName().getString(); 53 | for(ScoreboardPlayerScore playerScore:mcObjective.getScoreboard().getAllPlayerScores(mcObjective)){ 54 | this.playerScores.put(playerScore.getPlayerName(),playerScore.getScore()); 55 | } 56 | } 57 | } 58 | public static class PlayerScorePair{ 59 | public String playerName; 60 | public Map playerScore=new LinkedHashMap<>(); 61 | public PlayerScorePair(String playerName,Map playerScore){ 62 | this.playerName=playerName; 63 | playerScore.forEach((ScoreboardObjective objective,ScoreboardPlayerScore score)->{ 64 | this.playerScore.put(objective.getName(),score.getScore()); 65 | }); 66 | } 67 | } 68 | public static class ObjectiveScorePair{ 69 | public String objectiveName; 70 | public Map objectiveScore=new LinkedHashMap<>(); 71 | public ObjectiveScorePair(String objectiveName,Collection playerScore){ 72 | this.objectiveName=objectiveName; 73 | playerScore.forEach((ScoreboardPlayerScore score)->{ 74 | this.objectiveScore.put(score.getPlayerName(),score.getScore()); 75 | }); 76 | } 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/commands/DebuggerConfigCommand.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.commands; 2 | 3 | import com.hb.mcfdebugger.DebugThread; 4 | import com.hb.mcfdebugger.McfDebugger; 5 | import com.mojang.brigadier.CommandDispatcher; 6 | import com.mojang.brigadier.arguments.BoolArgumentType; 7 | import com.mojang.brigadier.arguments.IntegerArgumentType; 8 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 9 | import com.mojang.brigadier.context.CommandContext; 10 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 11 | import net.minecraft.server.command.CommandManager; 12 | import net.minecraft.server.command.ServerCommandSource; 13 | import net.minecraft.text.TranslatableText; 14 | 15 | import static com.mojang.brigadier.arguments.IntegerArgumentType.*; 16 | 17 | public class DebuggerConfigCommand { 18 | public static final SimpleCommandExceptionType INVALID_ARGUMENT = new SimpleCommandExceptionType(new TranslatableText("mcfdebugger.invalidArgument")); 19 | 20 | public static void register(CommandDispatcher dispatcher) { 21 | LiteralArgumentBuilder literalArgumentBuilder = CommandManager.literal("mcfDebuggerConfig"). 22 | requires(source -> source.hasPermissionLevel(4)) 23 | .then(CommandManager.literal("enable") 24 | .then(CommandManager.argument("enable?", BoolArgumentType.bool()) 25 | .executes((CommandContext cmd) -> { 26 | boolean isEnable = BoolArgumentType.getBool(cmd, "enable?"); 27 | McfDebugger.configList.put("enable", isEnable ? 1 : 0); 28 | McfDebugger.configManager.writeConfig(); 29 | if (isEnable) { 30 | cmd.getSource().sendFeedback(new TranslatableText("mcfdebugger.enabled"), true); 31 | McfDebugger.thread.restartWsServer(McfDebugger.configList.get("port"), McfDebugger.configList.get("timeOut")); 32 | } else { 33 | cmd.getSource().sendFeedback(new TranslatableText("mcfdebugger.disabled"), true); 34 | McfDebugger.thread.stopWsServer(); 35 | 36 | } 37 | return 1; 38 | } 39 | ) 40 | ) 41 | ) 42 | .then(CommandManager.literal("port") 43 | .then(CommandManager.argument("port", integer()) 44 | .executes(cmd -> { 45 | if (IntegerArgumentType.getInteger(cmd, "port") < 65536 && IntegerArgumentType.getInteger(cmd, "port") > 0) { 46 | McfDebugger.configList.put("port", IntegerArgumentType.getInteger(cmd, "port")); 47 | McfDebugger.configManager.writeConfig(); 48 | cmd.getSource().sendFeedback(new TranslatableText("mcfdebugger.set_port", IntegerArgumentType.getInteger(cmd, "port")), true); 49 | McfDebugger.thread.restartWsServer(McfDebugger.configList.get("port"), McfDebugger.configList.get("timeOut")); 50 | return 1; 51 | } 52 | throw INVALID_ARGUMENT.create(); 53 | } 54 | ) 55 | ) 56 | ) 57 | .then(CommandManager.literal("timeOut") 58 | .then(CommandManager.argument("timeOut", integer()) 59 | .executes(cmd -> { 60 | if (IntegerArgumentType.getInteger(cmd, "timeOut") > 0) { 61 | McfDebugger.configList.put("enable", IntegerArgumentType.getInteger(cmd, "timeOut")); 62 | McfDebugger.configManager.writeConfig(); 63 | cmd.getSource().sendFeedback(new TranslatableText("mcfdebugger.set_time_out", IntegerArgumentType.getInteger(cmd, "timeOut")), true); 64 | DebugThread.wsserver.setConnectionLostTimeout(IntegerArgumentType.getInteger(cmd, "timeOut")); 65 | return 1; 66 | } 67 | throw INVALID_ARGUMENT.create(); 68 | } 69 | ) 70 | ) 71 | ); 72 | dispatcher.register(literalArgumentBuilder); 73 | } 74 | 75 | public static int getNum(int num) { 76 | return num; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@types/node@*": 6 | version "14.14.25" 7 | resolved "https://registry.yarnpkg.com/@types/node/-/node-14.14.25.tgz#15967a7b577ff81383f9b888aa6705d43fbbae93" 8 | integrity sha512-EPpXLOVqDvisVxtlbvzfyqSsFeQxltFbluZNRndIb8tr9KiBnYNLzrc1N3pyKUCww2RNrfHDViqDWWE1LCJQtQ== 9 | 10 | "@types/ws@^7.4.0": 11 | version "7.4.0" 12 | resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.0.tgz#499690ea08736e05a8186113dac37769ab251a0e" 13 | integrity sha512-Y29uQ3Uy+58bZrFLhX36hcI3Np37nqWE7ky5tjiDoy1GDZnIwVxS0CgF+s+1bXMzjKBFy+fqaRfb708iNzdinw== 14 | dependencies: 15 | "@types/node" "*" 16 | 17 | bufferutil@^4.0.1, bufferutil@^4.0.3: 18 | version "4.0.3" 19 | resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.3.tgz#66724b756bed23cd7c28c4d306d7994f9943cc6b" 20 | integrity sha512-yEYTwGndELGvfXsImMBLop58eaGW+YdONi1fNjTINSY98tmMmFijBG6WXgdkfuLNt4imzQNtIE+eBp1PVpMCSw== 21 | dependencies: 22 | node-gyp-build "^4.2.0" 23 | 24 | d@1, d@^1.0.1: 25 | version "1.0.1" 26 | resolved "https://registry.yarnpkg.com/d/-/d-1.0.1.tgz#8698095372d58dbee346ffd0c7093f99f8f9eb5a" 27 | integrity sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA== 28 | dependencies: 29 | es5-ext "^0.10.50" 30 | type "^1.0.1" 31 | 32 | debug@^2.2.0: 33 | version "2.6.9" 34 | resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" 35 | integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== 36 | dependencies: 37 | ms "2.0.0" 38 | 39 | es5-ext@^0.10.35, es5-ext@^0.10.50: 40 | version "0.10.53" 41 | resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" 42 | integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== 43 | dependencies: 44 | es6-iterator "~2.0.3" 45 | es6-symbol "~3.1.3" 46 | next-tick "~1.0.0" 47 | 48 | es6-iterator@~2.0.3: 49 | version "2.0.3" 50 | resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" 51 | integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= 52 | dependencies: 53 | d "1" 54 | es5-ext "^0.10.35" 55 | es6-symbol "^3.1.1" 56 | 57 | es6-symbol@^3.1.1, es6-symbol@~3.1.3: 58 | version "3.1.3" 59 | resolved "https://registry.yarnpkg.com/es6-symbol/-/es6-symbol-3.1.3.tgz#bad5d3c1bcdac28269f4cb331e431c78ac705d18" 60 | integrity sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA== 61 | dependencies: 62 | d "^1.0.1" 63 | ext "^1.1.2" 64 | 65 | ext@^1.1.2: 66 | version "1.4.0" 67 | resolved "https://registry.yarnpkg.com/ext/-/ext-1.4.0.tgz#89ae7a07158f79d35517882904324077e4379244" 68 | integrity sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A== 69 | dependencies: 70 | type "^2.0.0" 71 | 72 | is-typedarray@^1.0.0: 73 | version "1.0.0" 74 | resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" 75 | integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= 76 | 77 | ms@2.0.0: 78 | version "2.0.0" 79 | resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" 80 | integrity sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= 81 | 82 | next-tick@~1.0.0: 83 | version "1.0.0" 84 | resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" 85 | integrity sha1-yobR/ogoFpsBICCOPchCS524NCw= 86 | 87 | node-gyp-build@^4.2.0: 88 | version "4.2.3" 89 | resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.2.3.tgz#ce6277f853835f718829efb47db20f3e4d9c4739" 90 | integrity sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg== 91 | 92 | type@^1.0.1: 93 | version "1.2.0" 94 | resolved "https://registry.yarnpkg.com/type/-/type-1.2.0.tgz#848dd7698dafa3e54a6c479e759c4bc3f18847a0" 95 | integrity sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg== 96 | 97 | type@^2.0.0: 98 | version "2.1.0" 99 | resolved "https://registry.yarnpkg.com/type/-/type-2.1.0.tgz#9bdc22c648cf8cf86dd23d32336a41cfb6475e3f" 100 | integrity sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA== 101 | 102 | typedarray-to-buffer@^3.1.5: 103 | version "3.1.5" 104 | resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" 105 | integrity sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q== 106 | dependencies: 107 | is-typedarray "^1.0.0" 108 | 109 | utf-8-validate@^5.0.2, utf-8-validate@^5.0.4: 110 | version "5.0.4" 111 | resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.4.tgz#72a1735983ddf7a05a43a9c6b67c5ce1c910f9b8" 112 | integrity sha512-MEF05cPSq3AwJ2C7B7sHAA6i53vONoZbMGX8My5auEVm6W+dJ2Jd/TZPyGJ5CH42V2XtbI5FD28HeHeqlPzZ3Q== 113 | dependencies: 114 | node-gyp-build "^4.2.0" 115 | 116 | websocket@^1.0.33: 117 | version "1.0.33" 118 | resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.33.tgz#407f763fc58e74a3fa41ca3ae5d78d3f5e3b82a5" 119 | integrity sha512-XwNqM2rN5eh3G2CUQE3OHZj+0xfdH42+OFK6LdC2yqiC0YU8e5UK0nYre220T0IyyN031V/XOvtHvXozvJYFWA== 120 | dependencies: 121 | bufferutil "^4.0.1" 122 | debug "^2.2.0" 123 | es5-ext "^0.10.50" 124 | typedarray-to-buffer "^3.1.5" 125 | utf-8-validate "^5.0.2" 126 | yaeti "^0.0.6" 127 | 128 | ws@^7.4.3: 129 | version "7.4.3" 130 | resolved "https://registry.yarnpkg.com/ws/-/ws-7.4.3.tgz#1f9643de34a543b8edb124bdcbc457ae55a6e5cd" 131 | integrity sha512-hr6vCR76GsossIRsr8OLR9acVVm1jyfEWvhbNjtgPOrfvAlKzvyeg/P6r8RuDjRyrcQoPQT7K0DGEPc7Ae6jzA== 132 | 133 | yaeti@^0.0.6: 134 | version "0.0.6" 135 | resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" 136 | integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= 137 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MSYS* | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/commands/DebuggerCommand.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.commands; 2 | 3 | import com.hb.mcfdebugger.commandHelpers.GetEntity; 4 | import com.hb.mcfdebugger.McfDebugger; 5 | import com.hb.mcfdebugger.commandHelpers.ReadScoreboard; 6 | import com.hb.mcfdebugger.config.ConfigHolder; 7 | import com.mojang.brigadier.CommandDispatcher; 8 | import com.mojang.brigadier.builder.LiteralArgumentBuilder; 9 | import com.mojang.brigadier.context.CommandContext; 10 | import com.mojang.brigadier.exceptions.CommandSyntaxException; 11 | import com.mojang.brigadier.exceptions.SimpleCommandExceptionType; 12 | import net.minecraft.command.argument.*; 13 | import net.minecraft.server.command.CommandManager; 14 | import net.minecraft.server.command.ServerCommandSource; 15 | import net.minecraft.text.TranslatableText; 16 | import net.minecraft.util.math.BlockPos; 17 | import net.minecraft.util.math.Vec2f; 18 | import net.minecraft.world.chunk.Chunk; 19 | import net.minecraft.world.chunk.ChunkStatus; 20 | 21 | public class DebuggerCommand { 22 | public static final SimpleCommandExceptionType LOGGER_HIT_EXCEPTION = new SimpleCommandExceptionType(new TranslatableText("MCFDEBUGGER: Logger hit.")); 23 | public static final SimpleCommandExceptionType CONSOLE_LOG_HIT_EXCEPTION = new SimpleCommandExceptionType(new TranslatableText("MCFDEBUGGER: Logger hit.")); 24 | 25 | public static void register(CommandDispatcher dispatcher) { 26 | LiteralArgumentBuilder literalArgumentBuilder = CommandManager.literal("debuggerCmd"). 27 | requires(source -> source.hasPermissionLevel(2)) 28 | .then(CommandManager.literal("mute").executes((CommandContext cmd) -> { 29 | McfDebugger.nowCommandCount--; 30 | return DebuggerCommand.mute(cmd); 31 | })) 32 | .then(CommandManager.literal("getScoreboard") 33 | .then(CommandManager.literal("byEntity") 34 | .then(CommandManager.argument("targets", ScoreHolderArgumentType.scoreHolder()).suggests(ScoreHolderArgumentType.SUGGESTION_PROVIDER) 35 | .executes((CommandContext cmd) -> { 36 | McfDebugger.nowCommandCount--; 37 | return DebuggerCommand.execute_getScoreByEntity(cmd); 38 | 39 | } 40 | ) 41 | ) 42 | ) 43 | .then(CommandManager.literal("byObjective") 44 | .then(CommandManager.argument("objective", ScoreboardObjectiveArgumentType.scoreboardObjective()) 45 | .executes((CommandContext cmd) -> { 46 | McfDebugger.nowCommandCount--; 47 | return DebuggerCommand.execute_getScoreByObjective(cmd); 48 | } 49 | ) 50 | ) 51 | ) 52 | ) 53 | .then(CommandManager.literal("getEntity") 54 | .then(CommandManager.argument("targets", EntityArgumentType.entities()) 55 | .executes((CommandContext cmd) -> { 56 | McfDebugger.nowCommandCount--; 57 | return DebuggerCommand.getEntity(cmd); 58 | } 59 | ) 60 | ) 61 | 62 | ) 63 | .then(CommandManager.literal("loud") 64 | .executes((CommandContext cmd) -> { 65 | McfDebugger.nowCommandCount--; 66 | return DebuggerCommand.loud(cmd); 67 | } 68 | ) 69 | 70 | ) 71 | .then(CommandManager.literal("log") 72 | .executes((CommandContext cmd) -> { 73 | McfDebugger.nowCommandCount--; 74 | return DebuggerCommand.log(cmd); 75 | } 76 | ) 77 | 78 | ) 79 | ; 80 | dispatcher.register(literalArgumentBuilder); 81 | } 82 | 83 | public static int execute_getScoreByEntity(CommandContext cmd) throws CommandSyntaxException { 84 | if (!ConfigHolder.debuggerMode.equals("none")) { 85 | ReadScoreboard.parseScoreboardByEntity(cmd); 86 | McfDebugger.howeverStop=true; 87 | throw LOGGER_HIT_EXCEPTION.create(); 88 | } else { 89 | return 1; 90 | } 91 | 92 | } 93 | 94 | public static int execute_getScoreByObjective(CommandContext cmd) throws CommandSyntaxException { 95 | if (!ConfigHolder.debuggerMode.equals("none")) { 96 | ReadScoreboard.parseScoreboardByObjective(cmd); 97 | McfDebugger.howeverStop=true; 98 | throw LOGGER_HIT_EXCEPTION.create(); 99 | } else { 100 | return 1; 101 | } 102 | } 103 | 104 | public static int getEntity(CommandContext cmd) throws CommandSyntaxException { 105 | if (!ConfigHolder.debuggerMode.equals("none")) { 106 | GetEntity.get(cmd); 107 | McfDebugger.howeverStop=true; 108 | throw LOGGER_HIT_EXCEPTION.create(); 109 | } else { 110 | return 1; 111 | } 112 | } 113 | 114 | public static int loud(CommandContext cmd) throws CommandSyntaxException { 115 | if (!ConfigHolder.debuggerMode.equals("none")) { 116 | McfDebugger.nowLoudCmd=McfDebugger.lastCmdObj.toSimple(); 117 | } 118 | return 1; 119 | } 120 | 121 | public static int log(CommandContext cmd) throws CommandSyntaxException { 122 | if (!ConfigHolder.debuggerMode.equals("none")) { 123 | McfDebugger.nowLogCmd=McfDebugger.lastCmdObj.toSimple(); 124 | } 125 | return 1; 126 | } 127 | 128 | public static int mute(CommandContext cmd) throws CommandSyntaxException { 129 | if (!ConfigHolder.debuggerMode.equals("none")) { 130 | McfDebugger.nowMuteCmd=McfDebugger.lastCmdObj.toSimple(); 131 | } 132 | return 1; 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/mixin/FunctionLoaderHook.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger.mixin; 2 | 3 | import com.google.common.collect.ImmutableMap; 4 | import com.google.common.collect.Maps; 5 | import com.mojang.brigadier.CommandDispatcher; 6 | import com.mojang.datafixers.util.Pair; 7 | import net.minecraft.entity.Entity; 8 | import net.minecraft.resource.Resource; 9 | import net.minecraft.resource.ResourceManager; 10 | import net.minecraft.resource.ResourceReloader; 11 | import net.minecraft.server.MinecraftServer; 12 | import net.minecraft.server.command.CommandOutput; 13 | import net.minecraft.server.command.ServerCommandSource; 14 | import net.minecraft.server.function.CommandFunction; 15 | import net.minecraft.server.function.FunctionLoader; 16 | import net.minecraft.server.world.ServerWorld; 17 | import net.minecraft.tag.Tag; 18 | import net.minecraft.tag.TagGroup; 19 | import net.minecraft.tag.TagGroupLoader; 20 | import net.minecraft.text.LiteralText; 21 | import net.minecraft.util.Identifier; 22 | import net.minecraft.util.math.Vec2f; 23 | import net.minecraft.util.math.Vec3d; 24 | import net.minecraft.util.profiler.Profiler; 25 | import org.apache.commons.io.IOUtils; 26 | import org.apache.logging.log4j.LogManager; 27 | import org.apache.logging.log4j.Logger; 28 | import org.spongepowered.asm.mixin.Final; 29 | import org.spongepowered.asm.mixin.Mixin; 30 | import org.spongepowered.asm.mixin.Overwrite; 31 | import org.spongepowered.asm.mixin.Shadow; 32 | 33 | import java.io.IOException; 34 | import java.nio.charset.StandardCharsets; 35 | import java.util.*; 36 | import java.util.concurrent.CompletableFuture; 37 | import java.util.concurrent.CompletionException; 38 | import java.util.concurrent.Executor; 39 | import com.hb.mcfdebugger.mixinHelpers.FakeCommandFunctionCreate; 40 | 41 | @Mixin(FunctionLoader.class) 42 | public abstract class FunctionLoaderHook implements ResourceReloader { 43 | @Shadow @Final TagGroupLoader tagLoader; 44 | @Shadow abstract Optional get(Identifier id); 45 | private static final int PATH_PREFIX_LENGTH = "functions/".length(); 46 | private static final int PATH_SUFFIX_LENGTH = ".mcfunction".length(); 47 | @Shadow @Final CommandDispatcher commandDispatcher; 48 | private static final Logger LOGGER= LogManager.getLogger(); 49 | @Shadow volatile Map functions; 50 | @Shadow volatile TagGroup tags; 51 | @Shadow @Final int level; 52 | 53 | @Overwrite 54 | public CompletableFuture reload(ResourceReloader.Synchronizer synchronizer, ResourceManager manager, Profiler prepareProfiler, Profiler applyProfiler, Executor prepareExecutor, Executor applyExecutor) { 55 | CompletableFuture> completableFuture = CompletableFuture.supplyAsync(() -> { 56 | return this.tagLoader.loadTags(manager); 57 | }, prepareExecutor); 58 | CompletableFuture>> completableFuture2 = CompletableFuture.supplyAsync(() -> { 59 | return manager.findResources("functions", (string) -> { 60 | return string.endsWith(".mcfunction"); 61 | }); 62 | }, prepareExecutor).thenCompose((collection) -> { 63 | Map> map = Maps.newHashMap(); 64 | ServerCommandSource serverCommandSource = new ServerCommandSource(CommandOutput.DUMMY, Vec3d.ZERO, Vec2f.ZERO, (ServerWorld)null, this.level, "", LiteralText.EMPTY, (MinecraftServer)null, (Entity)null); 65 | Iterator var6 = collection.iterator(); 66 | 67 | while(var6.hasNext()) { 68 | Identifier identifier = (Identifier)var6.next(); 69 | String string = identifier.getPath(); 70 | Identifier identifier2 = new Identifier(identifier.getNamespace(), string.substring(PATH_PREFIX_LENGTH, string.length() - PATH_SUFFIX_LENGTH)); 71 | map.put(identifier2, CompletableFuture.supplyAsync(() -> { 72 | List list = readLines(manager, identifier); 73 | //modified 74 | return FakeCommandFunctionCreate.create(identifier2, this.commandDispatcher, serverCommandSource, list); 75 | }, prepareExecutor)); 76 | } 77 | 78 | CompletableFuture[] completableFutures = (CompletableFuture[])map.values().toArray(new CompletableFuture[0]); 79 | return CompletableFuture.allOf(completableFutures).handle((void_, throwable) -> { 80 | return map; 81 | }); 82 | }); 83 | CompletableFuture var10000 = completableFuture.thenCombine(completableFuture2, Pair::of); 84 | synchronizer.getClass(); 85 | return var10000.thenCompose(synchronizer::whenPrepared).thenAcceptAsync((pair) -> { 86 | Map> map = (Map)((Pair)pair).getSecond(); 87 | ImmutableMap.Builder builder = ImmutableMap.builder(); 88 | map.forEach((identifier, completableFutureA) -> { 89 | completableFutureA.handle((commandFunction, throwable) -> { 90 | if (throwable != null) { 91 | LOGGER.error("Failed to load function {}", identifier, throwable); 92 | } else { 93 | builder.put(identifier, commandFunction); 94 | } 95 | 96 | return null; 97 | }).join(); 98 | }); 99 | this.functions = builder.build(); 100 | this.tags = this.tagLoader.buildGroup((Map)((Pair)pair).getFirst()); 101 | }, applyExecutor); 102 | } 103 | private static List readLines(ResourceManager resourceManager, Identifier id) { 104 | try { 105 | Resource resource = resourceManager.getResource(id); 106 | Throwable var3 = null; 107 | 108 | List var4; 109 | try { 110 | var4 = IOUtils.readLines(resource.getInputStream(), StandardCharsets.UTF_8); 111 | } catch (Throwable var14) { 112 | var3 = var14; 113 | throw var14; 114 | } finally { 115 | if (resource != null) { 116 | if (var3 != null) { 117 | try { 118 | resource.close(); 119 | } catch (Throwable var13) { 120 | var3.addSuppressed(var13); 121 | } 122 | } else { 123 | resource.close(); 124 | } 125 | } 126 | 127 | } 128 | 129 | return var4; 130 | } catch (IOException var16) { 131 | throw new CompletionException(var16); 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/hb/mcfdebugger/wsCommandParser.java: -------------------------------------------------------------------------------- 1 | package com.hb.mcfdebugger; 2 | 3 | import com.google.gson.Gson; 4 | import com.google.gson.internal.LinkedTreeMap; 5 | import com.hb.mcfdebugger.commandHelpers.ReadScoreboard; 6 | import com.hb.mcfdebugger.config.ConfigHolder; 7 | import net.minecraft.entity.Entity; 8 | import net.minecraft.network.MessageType; 9 | import net.minecraft.server.command.ServerCommandSource; 10 | import net.minecraft.server.world.ServerWorld; 11 | import net.minecraft.text.TranslatableText; 12 | import net.minecraft.util.Util; 13 | import net.minecraft.util.math.Vec2f; 14 | import net.minecraft.util.math.Vec3d; 15 | import net.minecraft.world.CommandBlockExecutor; 16 | 17 | import java.util.ArrayList; 18 | import java.util.Arrays; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | public class wsCommandParser { 23 | public static void sendMsg(String Msg){ 24 | McfDebugger.mcServer.getPlayerManager().broadcastChatMessage(new TranslatableText(Msg), MessageType.SYSTEM, Util.NIL_UUID); 25 | 26 | } 27 | public static boolean isInPauseList(String funNamespace, String funPath, Integer cmdIndex){ 28 | if(wsCommandParser.pauseList==null){ 29 | return false; 30 | } 31 | for(LinkedTreeMap cmd:wsCommandParser.pauseList){ 32 | if(((Double)cmd.get("cmdIndex")).intValue()== cmdIndex && cmd.get("funNamespace").equals(funNamespace) && cmd.get("funPath").equals(funPath)){ 33 | return true; 34 | } 35 | } 36 | return false; 37 | } 38 | public static List> pauseList; 39 | public class command{ 40 | public String funNamespace; 41 | public String funPath; 42 | public int cmdIndex; 43 | } 44 | 45 | public static boolean inModeList(String mode){ 46 | List availableModes= Arrays.asList("byStep","none","normalDebug"); 47 | for(String nowMode:availableModes){ 48 | if(nowMode.equals(mode)){return true;} 49 | } 50 | return false; 51 | } 52 | public static class MsgObj{ 53 | public String msg; 54 | public String type; 55 | public MsgObj(String msg,String msgType){ 56 | this.msg=msg; 57 | this.type=msgType; 58 | } 59 | } 60 | public static void SendFeedBack(String msg,String msgType){ 61 | MsgObj a= new MsgObj(msg,msgType); 62 | DebugThread.sendObjMsgToDebugger(a,"cmdFeedback"); 63 | } 64 | public static void DebugHandler(String wsMessage){ 65 | 66 | Gson gson=new Gson(); 67 | Map commandObj = gson.fromJson(wsMessage,Map.class); 68 | String command=""; 69 | try{ 70 | command=commandObj.get("command").toString(); 71 | }catch(Error e){ 72 | SendFeedBack("Unknown command.","Error"); 73 | return; 74 | } 75 | switch (command){ 76 | case "next": 77 | DebugThread.nextStep=true; 78 | SendFeedBack("Function proceeded.","Success"); 79 | break; 80 | case "setMode": 81 | String mode=""; 82 | try{ 83 | mode=commandObj.get("mode").toString(); 84 | }catch(Error e){ 85 | SendFeedBack("Unknown mode.","Error"); 86 | break; 87 | } 88 | if(inModeList(mode)){ 89 | ConfigHolder.debuggerMode=mode; 90 | SendFeedBack("Mode switched.","Success"); 91 | }else{ 92 | SendFeedBack("Unknown mode.","Error"); 93 | } 94 | break; 95 | case "setPauseList": 96 | try{ 97 | pauseList=(List>) commandObj.get("pauseList"); 98 | SendFeedBack("pauseList set.","Success"); 99 | }catch (Error e){ 100 | SendFeedBack("pauseList error.","Error"); 101 | } 102 | break; 103 | case "getScoreboard": 104 | try{ 105 | ReadScoreboard.parseAllScoreboard(); 106 | }catch(Error e){ 107 | SendFeedBack("scoreboard error.","Error"); 108 | } 109 | 110 | break; 111 | case "reload": 112 | CommandBlockExecutor executor = new CommandBlockExecutor() { 113 | @Override 114 | public ServerWorld getWorld() { 115 | return McfDebugger.mcServer.getOverworld(); 116 | } 117 | 118 | @Override 119 | public void markDirty() { 120 | 121 | } 122 | 123 | @Override 124 | public Vec3d getPos() { 125 | return null; 126 | } 127 | 128 | @Override 129 | public ServerCommandSource getSource() { 130 | return new ServerCommandSource(this, new Vec3d(0,0,0), Vec2f.ZERO, this.getWorld(), 2, this.getCustomName().getString(), this.getCustomName(), this.getWorld().getServer(), (Entity)null); 131 | } 132 | }; 133 | executor.setCommand("reload"); 134 | executor.execute(McfDebugger.mcServer.getOverworld()); 135 | wsCommandParser.sendMsg("mcfdebugger.reload_command"); 136 | SendFeedBack("reloaded.","Success"); 137 | break; 138 | case "getVersion": 139 | DebugThread.sendObjMsgToDebugger("5","versionResult"); 140 | break; 141 | case "setFeatures": 142 | ConfigHolder.resetFeature(); 143 | ArrayList featureList = (ArrayList) commandObj.get("features"); 144 | for(Object featureObj:featureList){ 145 | String feature = featureObj.toString(); 146 | switch (feature){ 147 | case "--stopOnException": 148 | ConfigHolder.nonStopOnException=true; 149 | break; 150 | case "debugThisMod": 151 | ConfigHolder.isThisModDebugging=true; 152 | break; 153 | case "executeCmdStopAtSuccess": 154 | ConfigHolder.executeCmdStopAtSuccess=true; 155 | break; 156 | } 157 | } 158 | break; 159 | case "stepOut": 160 | McfDebugger.stepOut=true; 161 | SendFeedBack("step out!","Success"); 162 | break; 163 | case "stepIn": 164 | McfDebugger.stepIn=true; 165 | SendFeedBack("step in!","Success"); 166 | break; 167 | default: 168 | SendFeedBack("Unknown command.","Error"); 169 | } 170 | } 171 | } 172 | --------------------------------------------------------------------------------