Skip to content
Merged

Ff #3

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.1.2
1.1.1
10 changes: 10 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,24 @@ dependencies {
modImplementation "net.fabricmc:fabric-loader:${project.loader_version}"
modImplementation "net.fabricmc.fabric-api:fabric-api:${project.fabric_version}"

// Quilt Loader and Quilted Fabric API (for Quilt compatibility)
modImplementation("org.quiltmc:quilt-loader:0.22.0") // Use latest compatible version
modImplementation("org.quiltmc.quilted-fabric-api:quilted-fabric-api:${project.fabric_version}")

// Optional: JSON parsing
implementation "com.google.code.gson:gson:2.10.1"
}


processResources {
inputs.property "version", project.version
filesMatching("fabric.mod.json") {
expand "version": inputs.properties["version"]
}
// Copy quilt.mod.json to the output jar
from("src/main/resources/quilt.mod.json") {
into ""
}
}

tasks.withType(JavaCompile).configureEach {
Expand All @@ -67,6 +76,7 @@ jar {
}
}


publishing {
publications {
create("mavenJava", MavenPublication) {
Expand Down
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ loader_version=0.16.14
loom_version=1.11-SNAPSHOT

# Mod Properties
mod_version=1.0.0
mod_version=1.0.2
maven_group=com.example
archives_base_name=CMDMaker

Expand Down
272 changes: 215 additions & 57 deletions src/main/java/com/example/ExampleMod.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,64 +19,132 @@ public class ExampleMod implements ModInitializer {
public static final String MOD_ID = "modid";
public static final Logger LOGGER = LoggerFactory.getLogger(MOD_ID);

private static final Path CONFIG_PATH = Paths.get("config", MOD_ID + "_aliases.json");
private static final Path CONFIG_PATH = Paths.get("config", "CommandMaker", "aliases.json");
private static final Map<String, String> aliases = new HashMap<>();

// Store per-player custom variables: player UUID -> (varName -> value)
private static final Map<UUID, Map<String, String>> playerVariables = new HashMap<>();
@Override
public void onInitialize() {
ensureConfigExists();
loadAliases();
CommandRegistrationCallback.EVENT.register((dispatcher, registryAccess, environment) -> {
registerAddCommand(dispatcher);
registerAliases(dispatcher);
registerSetCmdVariable(dispatcher);
});
LOGGER.info("Alias mod initialized!");
}

// Ensure config folder and aliases.json exist, create with example if not
private void ensureConfigExists() {
try {
Path folder = CONFIG_PATH.getParent();
if (!Files.exists(folder)) {
Files.createDirectories(folder);
}
if (!Files.exists(CONFIG_PATH)) {
List<String> lines = new ArrayList<>();
lines.add("# CommandMaker Aliases Config");
lines.add("# Each entry is an alias and its command target.");
lines.add("# You can use variables like ${player}, ${x}, ${y}, ${z} in the command.");
lines.add("# You can use argument variables like ${1}, ${2}, ... for user-supplied arguments.");
lines.add("# To run multiple commands, use a JSON array as the value for the alias.");
lines.add("# Example:");
lines.add("# teleport: 'tp ${player} 0 100 0'");
lines.add("# greet: 'say Hello, ${player}!'");
lines.add("# kit: ['give ${player} diamond_sword 1', 'give ${player} diamond_helmet 1']");
lines.add("# giveme: 'give ${player} ${1} ${2}'");
lines.add("{");
lines.add("}");
Files.write(CONFIG_PATH, lines, StandardOpenOption.CREATE_NEW);
}
} catch (Exception e) {
LOGGER.error("Failed to create config folder or file", e);
}
}

// Register /addcommand (reload|add|del) ...
private void registerAddCommand(CommandDispatcher<ServerCommandSource> dispatcher) {
dispatcher.register(
LiteralArgumentBuilder.<ServerCommandSource>literal("addcommand")
.then(net.minecraft.server.command.CommandManager.literal("reload")
.executes(ctx -> {
loadAliases();
registerAliases(dispatcher);
ctx.getSource().sendFeedback(() -> net.minecraft.text.Text.literal("Aliases reloaded."), false);
return 1;
})
.then(
net.minecraft.server.command.CommandManager.literal("reload")
.executes(ctx -> {
loadAliases();
// Remove all old aliases from dispatcher before re-registering
for (String alias : new HashSet<>(aliases.keySet())) {
dispatcher.getRoot().getChildren().removeIf(node -> node.getName().equals(alias));
}
registerAliases(dispatcher);
ctx.getSource().sendFeedback(() -> net.minecraft.text.Text.literal("Aliases reloaded."), false);
return 1;
})
)
.then(net.minecraft.server.command.CommandManager.literal("add")
.then(net.minecraft.server.command.CommandManager.argument("alias", StringArgumentType.word())
.then(net.minecraft.server.command.CommandManager.argument("target", StringArgumentType.greedyString())
.executes(ctx -> {
String alias = StringArgumentType.getString(ctx, "alias");
String target = StringArgumentType.getString(ctx, "target");
aliases.put(alias, target);
saveAliases();
registerAlias(dispatcher, alias, target);
ctx.getSource().sendFeedback(() -> net.minecraft.text.Text.literal("Alias /" + alias + " -> " + target + " added."), false);
return 1;
})
.then(
net.minecraft.server.command.CommandManager.literal("add")
.then(
net.minecraft.server.command.CommandManager.argument("alias", StringArgumentType.word())
.then(
net.minecraft.server.command.CommandManager.argument("target", StringArgumentType.greedyString())
.executes(ctx -> {
String alias = StringArgumentType.getString(ctx, "alias");
String target = StringArgumentType.getString(ctx, "target");
aliases.put(alias, target);
saveAliases();
registerAlias(dispatcher, alias, target);
ctx.getSource().sendFeedback(() -> net.minecraft.text.Text.literal("Alias /" + alias + " -> " + target + " added."), false);
return 1;
})
)
)
)
.then(
net.minecraft.server.command.CommandManager.literal("del")
.then(
net.minecraft.server.command.CommandManager.argument("alias", StringArgumentType.word())
.executes(ctx -> {
String alias = StringArgumentType.getString(ctx, "alias");
if (aliases.remove(alias) != null) {
saveAliases();
dispatcher.getRoot().getChildren().removeIf(node -> node.getName().equals(alias));
ctx.getSource().sendFeedback(() -> net.minecraft.text.Text.literal("Alias /" + alias + " removed."), false);
} else {
ctx.getSource().sendFeedback(() -> net.minecraft.text.Text.literal("Alias /" + alias + " not found."), false);
}
return 1;
})
)
)
)
.then(net.minecraft.server.command.CommandManager.literal("del")
.then(net.minecraft.server.command.CommandManager.argument("alias", StringArgumentType.word())
);
}

// Register /setcmdvariable <variable> <value> command
private void registerSetCmdVariable(CommandDispatcher<ServerCommandSource> dispatcher) {
dispatcher.register(
net.minecraft.server.command.CommandManager.literal("setcmdvariable")
.then(net.minecraft.server.command.CommandManager.argument("variable", StringArgumentType.word())
.then(net.minecraft.server.command.CommandManager.argument("value", StringArgumentType.greedyString())
.executes(ctx -> {
String alias = StringArgumentType.getString(ctx, "alias");
if (aliases.remove(alias) != null) {
saveAliases();
// Remove the command node from dispatcher
dispatcher.getRoot().getChildren().removeIf(node -> node.getName().equals(alias));
ctx.getSource().sendFeedback(() -> net.minecraft.text.Text.literal("Alias /" + alias + " removed."), false);
} else {
ctx.getSource().sendFeedback(() -> net.minecraft.text.Text.literal("Alias /" + alias + " not found."), false);
String var = StringArgumentType.getString(ctx, "variable");
String value = StringArgumentType.getString(ctx, "value");
ServerCommandSource source = ctx.getSource();
UUID uuid = null;
try {
uuid = source.getPlayer().getUuid();
} catch (Exception e) {
source.sendFeedback(() -> net.minecraft.text.Text.literal("Only players can set variables."), false);
return 0;
}
playerVariables.computeIfAbsent(uuid, k -> new HashMap<>()).put(var, value);
source.sendFeedback(() -> net.minecraft.text.Text.literal("Set variable ${" + var + "} = " + value), false);
return 1;
})
})
)
)
);
}


private void registerAliases(CommandDispatcher<ServerCommandSource> dispatcher) {
for (Map.Entry<String, String> entry : aliases.entrySet()) {
registerAlias(dispatcher, entry.getKey(), entry.getValue());
Expand All @@ -88,34 +156,112 @@ private void registerAlias(CommandDispatcher<ServerCommandSource> dispatcher, St
dispatcher.getRoot().getChildren().removeIf(node -> node.getName().equals(alias));
dispatcher.register(
net.minecraft.server.command.CommandManager.literal(alias)
.executes(ctx -> {
ServerCommandSource source = ctx.getSource();
CommandDispatcher<ServerCommandSource> cmdDispatcher = source.getServer().getCommandManager().getDispatcher();
ParseResults<ServerCommandSource> parsed = cmdDispatcher.parse(target, source);
return cmdDispatcher.execute(parsed);
})
.then(net.minecraft.server.command.CommandManager.argument("args", StringArgumentType.greedyString())
.executes(ctx -> {
String args = StringArgumentType.getString(ctx, "args");
String full = target + " " + args;
ServerCommandSource source = ctx.getSource();
CommandDispatcher<ServerCommandSource> cmdDispatcher = source.getServer().getCommandManager().getDispatcher();
ParseResults<ServerCommandSource> parsed = cmdDispatcher.parse(full, source);
return cmdDispatcher.execute(parsed);
})
.executes(ctx -> executeAlias(alias, ctx))
)
.executes(ctx -> executeAlias(alias, ctx))
);
}

// Execute an alias, supporting multiple commands and argument substitution
private int executeAlias(String alias, CommandContext<ServerCommandSource> ctx) {
ServerCommandSource source = ctx.getSource();
String raw = aliases.get(alias);
List<String> commands = new ArrayList<>();
try {
JsonElement el = JsonParser.parseString(raw);
if (el.isJsonArray()) {
for (JsonElement e : el.getAsJsonArray()) {
commands.add(e.getAsString());
}
} else if (el.isJsonPrimitive()) {
commands.add(el.getAsString());
}
} catch (Exception e) {
LOGGER.error("Failed to parse alias commands for " + alias, e);
return 0;
}
// Parse arguments
String args = "";
try { args = StringArgumentType.getString(ctx, "args"); } catch (Exception ignored) {}
String[] splitArgs = args.isEmpty() ? new String[0] : args.split("\\s+");
int result = 1;
for (String cmd : commands) {
String substituted = substituteVariablesWithArgs(cmd, ctx, splitArgs);
CommandDispatcher<ServerCommandSource> cmdDispatcher = source.getServer().getCommandManager().getDispatcher();
ParseResults<ServerCommandSource> parsed = cmdDispatcher.parse(substituted, source);
try {
result = cmdDispatcher.execute(parsed);
} catch (com.mojang.brigadier.exceptions.CommandSyntaxException e) {
LOGGER.error("Command execution failed for alias: " + alias, e);
}
}
return result;
}

// Substitute variables in the command string, including argument placeholders
private String substituteVariablesWithArgs(String command, CommandContext<ServerCommandSource> ctx, String[] splitArgs) {
ServerCommandSource source = ctx.getSource();
Map<String, String> vars = new HashMap<>();
try {
vars.put("player", source.getName());
vars.put("x", String.valueOf(source.getPosition().x));
vars.put("y", String.valueOf(source.getPosition().y));
vars.put("z", String.valueOf(source.getPosition().z));
UUID uuid = source.getPlayer() != null ? source.getPlayer().getUuid() : null;
if (uuid != null && playerVariables.containsKey(uuid)) {
vars.putAll(playerVariables.get(uuid));
}
} catch (Exception ignored) {}
// Add argument variables ${1}, ${2}, ...
for (int i = 0; i < splitArgs.length; i++) {
vars.put(String.valueOf(i + 1), splitArgs[i]);
}
for (Map.Entry<String, String> entry : vars.entrySet()) {
command = command.replace("${" + entry.getKey() + "}", entry.getValue());
}
return command;
}

// Substitute variables in the command string, e.g. ${player}, ${x}, etc. and custom variables
private String substituteVariables(String command, CommandContext<ServerCommandSource> ctx) {
ServerCommandSource source = ctx.getSource();
Map<String, String> vars = new HashMap<>();
try {
vars.put("player", source.getName());
vars.put("x", String.valueOf(source.getPosition().x));
vars.put("y", String.valueOf(source.getPosition().y));
vars.put("z", String.valueOf(source.getPosition().z));
// Add custom variables for this player
UUID uuid = source.getPlayer() != null ? source.getPlayer().getUuid() : null;
if (uuid != null && playerVariables.containsKey(uuid)) {
vars.putAll(playerVariables.get(uuid));
}
} catch (Exception ignored) {}
for (Map.Entry<String, String> entry : vars.entrySet()) {
command = command.replace("${" + entry.getKey() + "}", entry.getValue());
}
return command;
}

private void loadAliases() {
aliases.clear();
try {
if (Files.exists(CONFIG_PATH)) {
String json = Files.readString(CONFIG_PATH);
JsonObject obj = JsonParser.parseString(json).getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
aliases.put(entry.getKey(), entry.getValue().getAsString());
}
if (!Files.exists(CONFIG_PATH)) {
ensureConfigExists();
}
List<String> lines = Files.readAllLines(CONFIG_PATH);
StringBuilder jsonBuilder = new StringBuilder();
for (String line : lines) {
if (line.trim().startsWith("#")) continue;
jsonBuilder.append(line).append("\n");
}
String json = jsonBuilder.toString().trim();
if (json.isEmpty() || json.equals("{}")) return;
JsonObject obj = JsonParser.parseString(json).getAsJsonObject();
for (Map.Entry<String, JsonElement> entry : obj.entrySet()) {
// Store as stringified JSON for each alias (could be string or array)
aliases.put(entry.getKey(), entry.getValue().toString());
}
} catch (Exception e) {
LOGGER.error("Failed to load aliases config", e);
Expand All @@ -125,13 +271,25 @@ private void loadAliases() {
private void saveAliases() {
try {
Files.createDirectories(CONFIG_PATH.getParent());
List<String> lines = new ArrayList<>();
lines.add("# CommandMaker Aliases Config");
lines.add("# Each entry is an alias and its command target.");
lines.add("# You can use variables like ${player}, ${x}, ${y}, ${z} in the command.");
lines.add("# You can use argument variables like ${1}, ${2}, ... for user-supplied arguments.");
lines.add("# To run multiple commands, use a JSON array as the value for the alias.");
lines.add("# Example:");
lines.add("# teleport: 'tp ${player} 0 100 0'");
lines.add("# greet: 'say Hello, ${player}!'");
lines.add("# kit: ['give ${player} diamond_sword 1', 'give ${player} diamond_helmet 1']");
lines.add("# giveme: 'give ${player} ${1} ${2}'");
JsonObject obj = new JsonObject();
for (Map.Entry<String, String> entry : aliases.entrySet()) {
obj.addProperty(entry.getKey(), entry.getValue());
JsonElement el = JsonParser.parseString(entry.getValue());
obj.add(entry.getKey(), el);
}
Files.writeString(CONFIG_PATH, obj.toString(), StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
lines.add(obj.toString());
Files.write(CONFIG_PATH, lines, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);
} catch (Exception e) {
LOGGER.error("Failed to save aliases config", e);
}
}
}
}}
4 changes: 2 additions & 2 deletions src/main/resources/fabric.mod.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@
}
],
"depends": {
"fabricloader": ">=0.16.14",
"minecraft": "1.21.7",
"fabricloader": ">=0.15.0",
"minecraft": ">=1.20 <=1.21.8",
"java": ">=21",
"fabric-api": "*"
},
Expand Down
Loading
Loading