Added a lot of commands. Rotation to player still crashes the client.
This commit is contained in:
@@ -8,7 +8,7 @@ loader_version=0.15.1
|
|||||||
# Mod Properties
|
# Mod Properties
|
||||||
mod_version=0.1.0
|
mod_version=0.1.0
|
||||||
maven_group=com.example
|
maven_group=com.example
|
||||||
archives_base_name=addon-template
|
archives_base_name=meteor-butler
|
||||||
|
|
||||||
# Dependencies
|
# Dependencies
|
||||||
|
|
||||||
|
|||||||
@@ -1,43 +0,0 @@
|
|||||||
package com.example.addon;
|
|
||||||
|
|
||||||
import com.example.addon.commands.CommandExample;
|
|
||||||
import com.example.addon.hud.HudExample;
|
|
||||||
import com.example.addon.modules.ModuleExample;
|
|
||||||
import com.mojang.logging.LogUtils;
|
|
||||||
import meteordevelopment.meteorclient.addons.MeteorAddon;
|
|
||||||
import meteordevelopment.meteorclient.commands.Commands;
|
|
||||||
import meteordevelopment.meteorclient.systems.hud.Hud;
|
|
||||||
import meteordevelopment.meteorclient.systems.hud.HudGroup;
|
|
||||||
import meteordevelopment.meteorclient.systems.modules.Category;
|
|
||||||
import meteordevelopment.meteorclient.systems.modules.Modules;
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
|
|
||||||
public class Addon extends MeteorAddon {
|
|
||||||
public static final Logger LOG = LogUtils.getLogger();
|
|
||||||
public static final Category CATEGORY = new Category("Example");
|
|
||||||
public static final HudGroup HUD_GROUP = new HudGroup("Example");
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onInitialize() {
|
|
||||||
LOG.info("Initializing Meteor Addon Template");
|
|
||||||
|
|
||||||
// Modules
|
|
||||||
Modules.get().add(new ModuleExample());
|
|
||||||
|
|
||||||
// Commands
|
|
||||||
Commands.add(new CommandExample());
|
|
||||||
|
|
||||||
// HUD
|
|
||||||
Hud.get().register(HudExample.INFO);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onRegisterCategories() {
|
|
||||||
Modules.registerCategory(CATEGORY);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String getPackage() {
|
|
||||||
return "com.example.addon";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
package com.example.addon.commands;
|
|
||||||
|
|
||||||
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
|
||||||
import meteordevelopment.meteorclient.commands.Command;
|
|
||||||
import net.minecraft.command.CommandSource;
|
|
||||||
|
|
||||||
import static com.mojang.brigadier.Command.SINGLE_SUCCESS;
|
|
||||||
|
|
||||||
public class CommandExample extends Command {
|
|
||||||
public CommandExample() {
|
|
||||||
super("example", "Sends a message.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void build(LiteralArgumentBuilder<CommandSource> builder) {
|
|
||||||
builder.executes(context -> {
|
|
||||||
info("hi");
|
|
||||||
return SINGLE_SUCCESS;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
package com.example.addon.hud;
|
|
||||||
|
|
||||||
import com.example.addon.Addon;
|
|
||||||
import meteordevelopment.meteorclient.systems.hud.HudElement;
|
|
||||||
import meteordevelopment.meteorclient.systems.hud.HudElementInfo;
|
|
||||||
import meteordevelopment.meteorclient.systems.hud.HudRenderer;
|
|
||||||
import meteordevelopment.meteorclient.utils.render.color.Color;
|
|
||||||
|
|
||||||
public class HudExample extends HudElement {
|
|
||||||
public static final HudElementInfo<HudExample> INFO = new HudElementInfo<>(Addon.HUD_GROUP, "example", "HUD element example.", HudExample::new);
|
|
||||||
|
|
||||||
public HudExample() {
|
|
||||||
super(INFO);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void render(HudRenderer renderer) {
|
|
||||||
setSize(renderer.textWidth("Example element", true), renderer.textHeight(true));
|
|
||||||
|
|
||||||
renderer.text("Example element", x, y, Color.WHITE, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
package com.example.addon.modules;
|
|
||||||
|
|
||||||
import com.example.addon.Addon;
|
|
||||||
import meteordevelopment.meteorclient.systems.modules.Module;
|
|
||||||
|
|
||||||
public class ModuleExample extends Module {
|
|
||||||
public ModuleExample() {
|
|
||||||
super(Addon.CATEGORY, "example", "An example module in a custom category.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
38
src/main/java/me/trouper/butler/Addon.java
Normal file
38
src/main/java/me/trouper/butler/Addon.java
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package me.trouper.butler;
|
||||||
|
|
||||||
|
import com.mojang.logging.LogUtils;
|
||||||
|
import me.trouper.butler.commands.SwarmManager;
|
||||||
|
import me.trouper.butler.modules.SwarmPlusMaster;
|
||||||
|
import me.trouper.butler.modules.SwarmPlusWorker;
|
||||||
|
import meteordevelopment.meteorclient.addons.MeteorAddon;
|
||||||
|
import meteordevelopment.meteorclient.commands.Commands;
|
||||||
|
import meteordevelopment.meteorclient.systems.modules.Category;
|
||||||
|
import meteordevelopment.meteorclient.systems.modules.Modules;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
|
||||||
|
public class Addon extends MeteorAddon {
|
||||||
|
public static final Logger LOG = LogUtils.getLogger();
|
||||||
|
public static final Category CATEGORY = new Category("Butler");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onInitialize() {
|
||||||
|
LOG.info("Initializing Butler Addon for Meteor");
|
||||||
|
|
||||||
|
// Modules
|
||||||
|
Modules.get().add(new SwarmPlusMaster());
|
||||||
|
Modules.get().add(new SwarmPlusWorker());
|
||||||
|
|
||||||
|
// Commands
|
||||||
|
Commands.add(new SwarmManager());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRegisterCategories() {
|
||||||
|
Modules.registerCategory(CATEGORY);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPackage() {
|
||||||
|
return "me.trouper.addon";
|
||||||
|
}
|
||||||
|
}
|
||||||
290
src/main/java/me/trouper/butler/commands/SwarmManager.java
Normal file
290
src/main/java/me/trouper/butler/commands/SwarmManager.java
Normal file
@@ -0,0 +1,290 @@
|
|||||||
|
package me.trouper.butler.commands;
|
||||||
|
|
||||||
|
import com.mojang.brigadier.arguments.ArgumentType;
|
||||||
|
import com.mojang.brigadier.arguments.DoubleArgumentType;
|
||||||
|
import com.mojang.brigadier.arguments.IntegerArgumentType;
|
||||||
|
import com.mojang.brigadier.arguments.StringArgumentType;
|
||||||
|
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
|
||||||
|
import me.trouper.butler.modules.SwarmPlusMaster;
|
||||||
|
import me.trouper.butler.server.Connection;
|
||||||
|
import me.trouper.butler.utils.MathUtils;
|
||||||
|
import meteordevelopment.meteorclient.MeteorClient;
|
||||||
|
import meteordevelopment.meteorclient.commands.Command;
|
||||||
|
import meteordevelopment.meteorclient.commands.arguments.ModuleArgumentType;
|
||||||
|
import meteordevelopment.meteorclient.commands.arguments.PlayerArgumentType;
|
||||||
|
import meteordevelopment.meteorclient.commands.arguments.SettingArgumentType;
|
||||||
|
import meteordevelopment.meteorclient.commands.arguments.SettingValueArgumentType;
|
||||||
|
import meteordevelopment.meteorclient.pathing.PathManagers;
|
||||||
|
import meteordevelopment.meteorclient.settings.Setting;
|
||||||
|
import meteordevelopment.meteorclient.systems.modules.Module;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.command.CommandSource;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
|
||||||
|
import java.awt.geom.Point2D;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
import static com.mojang.brigadier.Command.SINGLE_SUCCESS;
|
||||||
|
|
||||||
|
public class SwarmManager extends Command {
|
||||||
|
public SwarmManager() {
|
||||||
|
super("manager", "Sends a message.");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void build(LiteralArgumentBuilder<CommandSource> builder) {
|
||||||
|
builder.then(literal("chat")
|
||||||
|
.then(argument("command", StringArgumentType.greedyString())
|
||||||
|
.executes(context -> {
|
||||||
|
String exec = context.getArgument("command", String.class);
|
||||||
|
if (SwarmPlusMaster.swarmServer == null) {
|
||||||
|
error("SwarmPlusMaster module is disabled. Start a swarm server to send commands to it!");
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}
|
||||||
|
SwarmPlusMaster.swarmServer.broadcast("[CHAT] " + exec);
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
.then(literal("list").executes(context -> {
|
||||||
|
List<Connection> connections = SwarmPlusMaster.swarmServer.getConnections().stream().toList();
|
||||||
|
StringBuilder connectionList = new StringBuilder();
|
||||||
|
int pointer = 0;
|
||||||
|
for (Connection connection : connections) {
|
||||||
|
pointer++;
|
||||||
|
connectionList.append("\n%s: %s on %s".formatted(pointer, connection.getClientSideName(),connection.getAddress()));
|
||||||
|
}
|
||||||
|
info("Swarm connections: " + connectionList.toString());
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}))
|
||||||
|
.then(literal("kick").then(argument("target",StringArgumentType.string())
|
||||||
|
.executes(context -> {
|
||||||
|
String target = context.getArgument("target", String.class);
|
||||||
|
if (SwarmPlusMaster.swarmServer == null) {
|
||||||
|
error("SwarmPlusMaster module is disabled. Start a swarm server to send commands to it!");
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}
|
||||||
|
//info("Looping %s connections".formatted(SwarmPlusMaster.swarmServer.connectionCount()));
|
||||||
|
|
||||||
|
for (Connection connection : SwarmPlusMaster.swarmServer.getConnections()) {
|
||||||
|
//info("Looping connections: %s user %s".formatted(connection.getAddress(),connection.getClientSideName()));
|
||||||
|
if (connection.getClientSideName().equals(target)) connection.disconnect();
|
||||||
|
//info("Disconnected %s".formatted(target),"DO YOU NEED AN ARGUMENT AGAIN?????");
|
||||||
|
}
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
.then(literal("toggle")
|
||||||
|
.then(argument("module", ModuleArgumentType.create())
|
||||||
|
.executes(context -> {
|
||||||
|
if (SwarmPlusMaster.swarmServer == null) {
|
||||||
|
error("SwarmPlusMaster module is disabled. Start a swarm server to send commands to it!");
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}
|
||||||
|
Module m = ModuleArgumentType.get(context);
|
||||||
|
SwarmPlusMaster.swarmServer.broadcast("[METEOR] toggle " + m.name);
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}).then(literal("on")
|
||||||
|
.executes(context -> {
|
||||||
|
if (SwarmPlusMaster.swarmServer == null) {
|
||||||
|
error("SwarmPlusMaster module is disabled. Start a swarm server to send commands to it!");
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}
|
||||||
|
Module m = ModuleArgumentType.get(context);
|
||||||
|
SwarmPlusMaster.swarmServer.broadcast("[METEOR] toggle " + m.name + " on");
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}))
|
||||||
|
.then(literal("off")
|
||||||
|
.executes(context -> {
|
||||||
|
if (SwarmPlusMaster.swarmServer == null) {
|
||||||
|
error("SwarmPlusMaster module is disabled. Start a swarm server to send commands to it!");
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}
|
||||||
|
Module m = ModuleArgumentType.get(context);
|
||||||
|
SwarmPlusMaster.swarmServer.broadcast("[METEOR] toggle " + m.name + " off");
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then(literal("settings")
|
||||||
|
.then(argument("module", ModuleArgumentType.create())
|
||||||
|
.then(argument("setting", SettingArgumentType.create())
|
||||||
|
.then(argument("value", SettingValueArgumentType.create())
|
||||||
|
.executes(context -> {
|
||||||
|
if (SwarmPlusMaster.swarmServer == null) {
|
||||||
|
error("SwarmPlusMaster module is disabled. Start a swarm server to send commands to it!");
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}
|
||||||
|
Module module = ModuleArgumentType.get(context);
|
||||||
|
Setting<?> setting = SettingArgumentType.get(context);
|
||||||
|
String value = SettingValueArgumentType.get(context);
|
||||||
|
|
||||||
|
SwarmPlusMaster.swarmServer.broadcast("[METEOR] settings %s %s %s".formatted(module.name,setting.name,value));
|
||||||
|
ModuleArgumentType.get(context).info("Setting %s changed in %s to %s for all swarm members.", module.title, setting.title, value);
|
||||||
|
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then(literal("spread")
|
||||||
|
.then(argument("radius", IntegerArgumentType.integer(1))
|
||||||
|
.executes(context -> {
|
||||||
|
if (MeteorClient.mc.player == null) {
|
||||||
|
info("How did we get here?");
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}
|
||||||
|
int rad = context.getArgument("radius",Integer.class);
|
||||||
|
int n = SwarmPlusMaster.swarmServer.connectionCount();
|
||||||
|
Point2D.Double[] distribution = MathUtils.distributePoints(MeteorClient.mc.player.getX(),MeteorClient.mc.player.getZ(),rad,n);
|
||||||
|
int index = 0;
|
||||||
|
for (Connection connection : SwarmPlusMaster.swarmServer.getConnections()) {
|
||||||
|
int x = (int) Math.round(distribution[index].x);
|
||||||
|
int z = (int) Math.round(distribution[index].y);
|
||||||
|
connection.sendMessage("[BARITONE] gotoxz %s %s".formatted(x,z));
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
SwarmManager.this.info("Bots moving to a circle with radius (highlight)%s(default).",rad);
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
.then(literal("here")
|
||||||
|
.executes(context -> {
|
||||||
|
int roundX = (int) Math.round(MeteorClient.mc.player.getX());
|
||||||
|
int roundY = (int) Math.round(MeteorClient.mc.player.getY());
|
||||||
|
int roundZ = (int) Math.round(MeteorClient.mc.player.getZ());
|
||||||
|
SwarmPlusMaster.swarmServer.broadcast("[BARITONE] gotoxyz %s %s %s".formatted(
|
||||||
|
roundX,
|
||||||
|
roundY,
|
||||||
|
roundZ
|
||||||
|
));
|
||||||
|
SwarmManager.this.info("Pathing (highlight)all bots(default) to the host.");
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
})
|
||||||
|
.then(argument("target",StringArgumentType.string())
|
||||||
|
.executes(context -> {
|
||||||
|
String target = StringArgumentType.getString(context,"target");
|
||||||
|
int roundX = (int) Math.round(MeteorClient.mc.player.getX());
|
||||||
|
int roundY = (int) Math.round(MeteorClient.mc.player.getY());
|
||||||
|
int roundZ = (int) Math.round(MeteorClient.mc.player.getZ());
|
||||||
|
for (Connection connection : SwarmPlusMaster.swarmServer.getConnections().stream().toList()) {
|
||||||
|
if (!connection.getClientSideName().equalsIgnoreCase(target)) continue;
|
||||||
|
connection.sendMessage("[BARITONE] gotoxyz %s %s %s".formatted(
|
||||||
|
roundX,
|
||||||
|
roundY,
|
||||||
|
roundZ
|
||||||
|
));
|
||||||
|
SwarmManager.this.info("Pathing (highlight)%s(default) to the host.",target);
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}
|
||||||
|
SwarmManager.this.error("Could not find a connection with the name (highlight)%s",target);
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
.then(literal("goto")
|
||||||
|
.then(argument("y", IntegerArgumentType.integer()).executes(context -> {
|
||||||
|
SwarmPlusMaster.swarmServer.broadcast("[BARITONE] gotoy %s".formatted(
|
||||||
|
IntegerArgumentType.getInteger(context,"y")
|
||||||
|
));
|
||||||
|
SwarmManager.this.info("Pathing all bots to (highlight)%s",
|
||||||
|
IntegerArgumentType.getInteger(context,"y")
|
||||||
|
);
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}))
|
||||||
|
.then(argument("x",IntegerArgumentType.integer())
|
||||||
|
.then(argument("z",IntegerArgumentType.integer())
|
||||||
|
.executes(context -> {
|
||||||
|
SwarmPlusMaster.swarmServer.broadcast("[BARITONE] gotoxz %s %s".formatted(
|
||||||
|
IntegerArgumentType.getInteger(context,"x"),
|
||||||
|
IntegerArgumentType.getInteger(context,"z")
|
||||||
|
));
|
||||||
|
SwarmManager.this.info("Pathing all bots to (highlight)%s %s",
|
||||||
|
IntegerArgumentType.getInteger(context,"x"),
|
||||||
|
IntegerArgumentType.getInteger(context,"z"));
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
})))
|
||||||
|
.then(argument("x",IntegerArgumentType.integer())
|
||||||
|
.then(argument("y",IntegerArgumentType.integer())
|
||||||
|
.then(argument("z",IntegerArgumentType.integer())
|
||||||
|
.executes(context -> {
|
||||||
|
SwarmPlusMaster.swarmServer.broadcast("[BARITONE] gotoxyz %s %s %s".formatted(
|
||||||
|
IntegerArgumentType.getInteger(context,"x"),
|
||||||
|
IntegerArgumentType.getInteger(context,"y"),
|
||||||
|
IntegerArgumentType.getInteger(context,"z")
|
||||||
|
));
|
||||||
|
SwarmManager.this.info("Pathing all bots to (highlight)%s %s %s",
|
||||||
|
IntegerArgumentType.getInteger(context,"x"),
|
||||||
|
IntegerArgumentType.getInteger(context,"y"),
|
||||||
|
IntegerArgumentType.getInteger(context,"z")
|
||||||
|
);
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}))))
|
||||||
|
)
|
||||||
|
.then(literal("rotate")
|
||||||
|
.then(literal("absolute")
|
||||||
|
.then(argument("pitch", DoubleArgumentType.doubleArg(0,360))
|
||||||
|
.then(argument("yaw", DoubleArgumentType.doubleArg(0,360))
|
||||||
|
.executes(context -> {
|
||||||
|
SwarmPlusMaster.swarmServer.broadcast("[LOOK] absolute %s %s".formatted(
|
||||||
|
context.getArgument("pitch",double.class),
|
||||||
|
context.getArgument("yaw",double.class)));
|
||||||
|
SwarmManager.this.info("Bots now facing (highlight)%s %s",
|
||||||
|
context.getArgument("pitch",double.class),
|
||||||
|
context.getArgument("yaw",double.class));
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then(literal("player")
|
||||||
|
.then(argument("target",PlayerArgumentType.create())
|
||||||
|
.executes(context -> {
|
||||||
|
SwarmPlusMaster.swarmServer.broadcast("[LOOK] player %s".formatted(context.getArgument("target",String.class)));
|
||||||
|
SwarmManager.this.info("Bots now targeting (highlight)%s",context.getArgument("target",String.class));
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.then(literal("follow")
|
||||||
|
.then(argument("player", PlayerArgumentType.create())
|
||||||
|
.executes(context -> {
|
||||||
|
PlayerEntity pe = PlayerArgumentType.get(context);
|
||||||
|
SwarmPlusMaster.swarmServer.broadcast("[BARITONE] follow %s".formatted(pe.getName().getString()));
|
||||||
|
SwarmManager.this.info("Bots now following (highlight)%s(default).",pe.getName().getString());
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}))
|
||||||
|
)
|
||||||
|
.then(literal("closegame").executes(context -> {
|
||||||
|
SwarmPlusMaster.swarmServer.broadcast("[CLOSEGAME]");
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}))
|
||||||
|
.then(literal("stop").executes(context -> {
|
||||||
|
SwarmPlusMaster.swarmServer.broadcast("[STOP]");
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}))
|
||||||
|
.then(literal("raw")
|
||||||
|
.then(literal("all")
|
||||||
|
.then(argument("packet",StringArgumentType.greedyString())
|
||||||
|
.executes(context -> {
|
||||||
|
SwarmPlusMaster.swarmServer.broadcast(StringArgumentType.getString(context,"packet"));
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
})))
|
||||||
|
.then(literal("dm")
|
||||||
|
.then(argument("target",StringArgumentType.string())
|
||||||
|
.then(argument("packet",StringArgumentType.greedyString())
|
||||||
|
.executes(context -> {
|
||||||
|
String target = StringArgumentType.getString(context,"target");
|
||||||
|
for (Connection connection : SwarmPlusMaster.swarmServer.getConnections().stream().toList()) {
|
||||||
|
if (!connection.getClientSideName().equalsIgnoreCase(target)) continue;
|
||||||
|
connection.sendMessage(StringArgumentType.getString(context,"packet"));
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
}
|
||||||
|
return SINGLE_SUCCESS;
|
||||||
|
})
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
76
src/main/java/me/trouper/butler/modules/SwarmPlusMaster.java
Normal file
76
src/main/java/me/trouper/butler/modules/SwarmPlusMaster.java
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package me.trouper.butler.modules;
|
||||||
|
|
||||||
|
import me.trouper.butler.Addon;
|
||||||
|
import me.trouper.butler.server.Address;
|
||||||
|
import me.trouper.butler.server.Server;
|
||||||
|
import meteordevelopment.meteorclient.settings.*;
|
||||||
|
import meteordevelopment.meteorclient.systems.modules.Module;
|
||||||
|
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
public class SwarmPlusMaster extends Module {
|
||||||
|
|
||||||
|
public static Server swarmServer = null;
|
||||||
|
|
||||||
|
private final SettingGroup general = settings.getDefaultGroup();
|
||||||
|
|
||||||
|
public SwarmPlusMaster() {
|
||||||
|
super(Addon.CATEGORY, "swarm-plus-host", "(Host/Master) Control multiple instances of meteor through one account, but better.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Setting<String> ip = general.add(new StringSetting.Builder()
|
||||||
|
.name("address")
|
||||||
|
.defaultValue("localhost")
|
||||||
|
.build());
|
||||||
|
|
||||||
|
private final Setting<Integer> port = general.add(new IntSetting.Builder()
|
||||||
|
.name("port")
|
||||||
|
.defaultValue(25561)
|
||||||
|
.max(65535)
|
||||||
|
.min(0)
|
||||||
|
.build());
|
||||||
|
|
||||||
|
private final Setting<Boolean> verbose = general.add(new BoolSetting.Builder()
|
||||||
|
.name("verbose")
|
||||||
|
.defaultValue(false)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivate() {
|
||||||
|
swarmServer = new Server(new Address(ip.get(),port.get())) {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHandShake(Address address, String clientSideName) {
|
||||||
|
super.onHandShake(address, clientSideName);
|
||||||
|
SwarmPlusMaster.this.info("%s connected on %s".formatted(clientSideName,address));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public synchronized boolean removeConnection(Address address) {
|
||||||
|
SwarmPlusMaster.this.info("Client disconnecting: " + address);
|
||||||
|
return super.removeConnection(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void info(String str, Object... args) {
|
||||||
|
super.info(str, args);
|
||||||
|
if (verbose.get()) SwarmPlusMaster.this.info(str.formatted(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void error(String str, Object... args) {
|
||||||
|
super.error(str, args);
|
||||||
|
if (verbose.get()) SwarmPlusMaster.this.info("Error: " + str.formatted(args));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
info("Started a new server on IP %s with port %s".formatted(ip,port));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeactivate() {
|
||||||
|
swarmServer.disconnect();
|
||||||
|
info("Closed the swarm server.","THERE YOU GO METEOR HERE IS YOUR ARG NOW DON'T CRASH");
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
160
src/main/java/me/trouper/butler/modules/SwarmPlusWorker.java
Normal file
160
src/main/java/me/trouper/butler/modules/SwarmPlusWorker.java
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
package me.trouper.butler.modules;
|
||||||
|
|
||||||
|
import me.trouper.butler.Addon;
|
||||||
|
import me.trouper.butler.server.Address;
|
||||||
|
import me.trouper.butler.server.Client;
|
||||||
|
import me.trouper.butler.server.Response;
|
||||||
|
import me.trouper.butler.utils.MathUtils;
|
||||||
|
import me.trouper.butler.utils.Text;
|
||||||
|
import meteordevelopment.meteorclient.settings.*;
|
||||||
|
import meteordevelopment.meteorclient.systems.config.Config;
|
||||||
|
import meteordevelopment.meteorclient.systems.modules.Module;
|
||||||
|
import meteordevelopment.meteorclient.utils.player.ChatUtils;
|
||||||
|
import net.minecraft.client.MinecraftClient;
|
||||||
|
import net.minecraft.entity.Entity;
|
||||||
|
import net.minecraft.entity.player.PlayerEntity;
|
||||||
|
import net.minecraft.util.math.Vec3d;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Objects;
|
||||||
|
|
||||||
|
public class SwarmPlusWorker extends Module {
|
||||||
|
|
||||||
|
private Client client = null;
|
||||||
|
|
||||||
|
private final SettingGroup general = settings.getDefaultGroup();
|
||||||
|
|
||||||
|
public SwarmPlusWorker() {
|
||||||
|
super(Addon.CATEGORY, "swarm-plus-worker", "(Worker/Client) Control multiple instances of meteor through one account, but better.");
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Setting<String> ip = general.add(new StringSetting.Builder()
|
||||||
|
.name("address")
|
||||||
|
.defaultValue("localhost")
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
private final Setting<Integer> port = general.add(new IntSetting.Builder()
|
||||||
|
.name("port")
|
||||||
|
.defaultValue(25561)
|
||||||
|
.max(65535)
|
||||||
|
.min(0)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
private final Setting<Boolean> verbose = general.add(new BoolSetting.Builder()
|
||||||
|
.name("verbose")
|
||||||
|
.defaultValue(false)
|
||||||
|
.build()
|
||||||
|
);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivate() {
|
||||||
|
client = new Client(new Address(ip.get(),port.get())) {
|
||||||
|
@Override
|
||||||
|
protected void info(String str, Object... args) {
|
||||||
|
super.info(str, args);
|
||||||
|
String full = str.formatted(args);
|
||||||
|
if (verbose.get()) SwarmPlusWorker.this.info(full);
|
||||||
|
String packetType = Text.getPacketType(full);
|
||||||
|
String packetArgs = Text.getPacketArgs(full);
|
||||||
|
if (packetType == null) packetType = "ERR";
|
||||||
|
switch (packetType) {
|
||||||
|
case "CHAT" -> {
|
||||||
|
if (packetArgs == null) {
|
||||||
|
SwarmPlusWorker.this.error("Chat message returned null. (highlight)%s",full);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
SwarmPlusWorker.this.info("Received chat command. Sending (highlight)%s".formatted(packetArgs));
|
||||||
|
if (MinecraftClient.getInstance().player == null) return;
|
||||||
|
ChatUtils.sendPlayerMsg(packetArgs);
|
||||||
|
}
|
||||||
|
case "METEOR" -> {
|
||||||
|
SwarmPlusWorker.this.info("Received meteor command (highlight)%s(default).".formatted(packetArgs));
|
||||||
|
if (MinecraftClient.getInstance().player == null) return;
|
||||||
|
ChatUtils.sendPlayerMsg(Config.get().prefix.get() + packetArgs);
|
||||||
|
}
|
||||||
|
case "LOOK" -> {
|
||||||
|
String[] largs = packetArgs.split(" ");
|
||||||
|
switch (largs[0]) {
|
||||||
|
case "player" -> {
|
||||||
|
String target = largs[1];
|
||||||
|
for (Entity entity : mc.player.clientWorld.getEntities()) {
|
||||||
|
if (!(entity instanceof PlayerEntity)) continue;
|
||||||
|
if (!entity.getName().getString().equalsIgnoreCase(target)) continue;
|
||||||
|
Vec3d vec = entity.getEyePos().subtract(mc.player.getEyePos()).normalize();
|
||||||
|
float[] rot = MathUtils.toPolar(vec.x,vec.y,vec.z);
|
||||||
|
mc.player.setPitch(rot[0]);
|
||||||
|
mc.player.setYaw(rot[1]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "absolute" -> {
|
||||||
|
float pitch = Float.parseFloat(largs[1]);
|
||||||
|
float yaw = Float.parseFloat(largs[2]);
|
||||||
|
mc.player.setPitch(pitch);
|
||||||
|
mc.player.setYaw(yaw);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "STOP" -> {
|
||||||
|
SwarmPlusWorker.this.info("Received Stop Command");
|
||||||
|
ChatUtils.sendPlayerMsg("#stop");
|
||||||
|
}
|
||||||
|
case "BARITONE" -> {
|
||||||
|
String[] bargs = packetArgs.split(" ");
|
||||||
|
if (verbose.get()) SwarmPlusWorker.this.info("Received command addressed to baritone. Full: > (highlight)%s(default) < Arguments: > (highlight)%s(default) <".formatted(full, Arrays.stream(bargs).toList().toString()));
|
||||||
|
switch (bargs[0]) {
|
||||||
|
case "gotoxyz" -> {
|
||||||
|
int x = Integer.parseInt(bargs[1]);
|
||||||
|
int y = Integer.parseInt(bargs[2]);
|
||||||
|
int z = Integer.parseInt(bargs[3]);
|
||||||
|
if (verbose.get()) SwarmPlusWorker.this.info("Received baritone command (highlight)gotoxyz %s %s %s".formatted(x,y,z));
|
||||||
|
ChatUtils.sendPlayerMsg("#goto %s %s %s".formatted(x,y,z));
|
||||||
|
}
|
||||||
|
case "gotoxz" -> {
|
||||||
|
int x = Integer.parseInt(bargs[1]);
|
||||||
|
int z = Integer.parseInt(bargs[2]);
|
||||||
|
if (verbose.get()) SwarmPlusWorker.this.info("Received baritone command (highlight)gotoxz %s %s".formatted(x,z));
|
||||||
|
ChatUtils.sendPlayerMsg("#goto %s %s".formatted(x,z));
|
||||||
|
}
|
||||||
|
case "gotoy" -> {
|
||||||
|
int y = Integer.parseInt(bargs[1]);
|
||||||
|
if (verbose.get()) SwarmPlusWorker.this.info("Received baritone command (highlight)gotoy %s".formatted(y));
|
||||||
|
ChatUtils.sendPlayerMsg("#goto %s".formatted(y));
|
||||||
|
}
|
||||||
|
case "follow" -> {
|
||||||
|
String player = bargs[1];
|
||||||
|
if (verbose.get()) SwarmPlusWorker.this.info("Received baritone command (highlight)follow %s".formatted(player));
|
||||||
|
ChatUtils.sendPlayerMsg("#follow player %s".formatted(player));
|
||||||
|
SwarmPlusWorker.this.info("Following (highlight)%s",player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case "CLOSEGAME" -> {
|
||||||
|
SwarmPlusWorker.this.info("Close game call from host!");
|
||||||
|
System.exit(0);
|
||||||
|
}
|
||||||
|
default -> {
|
||||||
|
SwarmPlusWorker.this.error("An error occurred when receiving a packet from the host. (highlight)%s",full);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void error(String str, Object... args) {
|
||||||
|
super.error(str, args);
|
||||||
|
if (verbose.get()) SwarmPlusWorker.this.info("Error: " + str.formatted(args));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
client.sendToServer(new Response(Response.Method.TO_SERVER, Response.Type.HANDSHAKE,mc.getSession().getUsername()));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeactivate() {
|
||||||
|
client.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
27
src/main/java/me/trouper/butler/server/Address.java
Normal file
27
src/main/java/me/trouper/butler/server/Address.java
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package me.trouper.butler.server;
|
||||||
|
|
||||||
|
import java.net.Socket;
|
||||||
|
|
||||||
|
public record Address(String ip, int port) {
|
||||||
|
|
||||||
|
public Address(String ip, int port) {
|
||||||
|
this.ip = "127.0.0.1".equals(ip) ? "localhost" : ip;
|
||||||
|
this.port = port;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Address(Socket socket) {
|
||||||
|
this(socket.getInetAddress().getHostAddress(), socket.getPort());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return ip + ":" + port;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof Address ad))
|
||||||
|
return false;
|
||||||
|
return ad.ip.equals(this.ip) && ad.port == this.port;
|
||||||
|
}
|
||||||
|
}
|
||||||
120
src/main/java/me/trouper/butler/server/Client.java
Normal file
120
src/main/java/me/trouper/butler/server/Client.java
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package me.trouper.butler.server;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.UUID;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
|
||||||
|
public class Client extends ConnectionThread {
|
||||||
|
|
||||||
|
private final Address serverAddress;
|
||||||
|
private Address serverSideAddress;
|
||||||
|
private final ConcurrentLinkedQueue<String> queuedMessages;
|
||||||
|
private Socket socket;
|
||||||
|
private final UUID id;
|
||||||
|
|
||||||
|
public Client(Address connectingTo) {
|
||||||
|
this.serverAddress = connectingTo;
|
||||||
|
this.id = UUID.randomUUID();
|
||||||
|
this.setName("[CLIENT:%s]".formatted(id));
|
||||||
|
this.queuedMessages = new ConcurrentLinkedQueue<>();
|
||||||
|
|
||||||
|
try {
|
||||||
|
info("connecting to server %s ...", connectingTo);
|
||||||
|
socket = new Socket(connectingTo.ip(), connectingTo.port());
|
||||||
|
start();
|
||||||
|
info("server connected! (%s)", connectingTo);
|
||||||
|
new Thread(this::upload, "[CLIENT-UPLOADER:%s]".formatted(id)).start();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
error("cannot connect to server %s: %s", connectingTo, ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
DataInputStream dis = new DataInputStream(socket.getInputStream());
|
||||||
|
|
||||||
|
while (!isInterrupted()) {
|
||||||
|
onReceiveMessage(dis.readUTF());
|
||||||
|
}
|
||||||
|
|
||||||
|
dis.close();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
error("cannot receive data from server: %s", serverAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void upload() {
|
||||||
|
try {
|
||||||
|
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
|
||||||
|
while (!isInterrupted()) {
|
||||||
|
if (!queuedMessages.isEmpty()) {
|
||||||
|
String msg = queuedMessages.poll();
|
||||||
|
info(msg);
|
||||||
|
dos.writeUTF(msg);
|
||||||
|
dos.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dos.close();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
error("error transmitting message: %s", ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void onReceiveMessage(String message) {
|
||||||
|
if (!Response.isResponsePattern(message)) {
|
||||||
|
info("message received: %s", message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Response res = Response.parse(message);
|
||||||
|
if (res.getMethod() == Response.Method.TO_CLIENT && res.getType() == Response.Type.HANDSHAKE)
|
||||||
|
this.handeShake(res);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
error("Response parse failure: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void handeShake(Response res) {
|
||||||
|
String serverSideIp = (String) res.getArgs()[0];
|
||||||
|
String serverSidePort = (String) res.getArgs()[1];
|
||||||
|
this.serverSideAddress = new Address(serverSideIp, Integer.parseInt(serverSidePort));
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void sendToServer(String str) {
|
||||||
|
if (str != null)
|
||||||
|
queuedMessages.add(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void sendToServer(Response res) {
|
||||||
|
sendToServer(res.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect() {
|
||||||
|
sendToServer(new Response(Response.Method.TO_SERVER, Response.Type.DEAD_FISH, "disconnected manually"));
|
||||||
|
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
socket = null;
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
error("server connection closed");
|
||||||
|
}
|
||||||
|
interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UUID getUniqueId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Address getServerSideAddress() {
|
||||||
|
return serverSideAddress;
|
||||||
|
}
|
||||||
|
}
|
||||||
120
src/main/java/me/trouper/butler/server/Connection.java
Normal file
120
src/main/java/me/trouper/butler/server/Connection.java
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
package me.trouper.butler.server;
|
||||||
|
|
||||||
|
import java.io.DataInputStream;
|
||||||
|
import java.io.DataOutputStream;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
|
||||||
|
public class Connection extends ConnectionThread {
|
||||||
|
|
||||||
|
private final ConcurrentLinkedQueue<String> queuedMessages;
|
||||||
|
private final Socket socket;
|
||||||
|
private final Server host;
|
||||||
|
private String clientSideName;
|
||||||
|
|
||||||
|
public Connection(Server host, Socket socket) {
|
||||||
|
this.socket = socket;
|
||||||
|
this.host = host;
|
||||||
|
this.queuedMessages = new ConcurrentLinkedQueue<>();
|
||||||
|
this.setName("[CONNECTION:%s]".formatted(getAddress()));
|
||||||
|
start();
|
||||||
|
new Thread(this::receive, "[CONNECTION-RECEIVER:%s]".formatted(getAddress())).start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
|
||||||
|
while (!isInterrupted()) {
|
||||||
|
if (!queuedMessages.isEmpty()) {
|
||||||
|
String msg = queuedMessages.poll();
|
||||||
|
info(msg);
|
||||||
|
dos.writeUTF(msg);
|
||||||
|
dos.flush();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dos.close();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
error("error transmitting message: %s", ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void receive() {
|
||||||
|
try {
|
||||||
|
DataInputStream dis = new DataInputStream(socket.getInputStream());
|
||||||
|
|
||||||
|
while (!isInterrupted()) {
|
||||||
|
onReceiveMessage(dis.readUTF());
|
||||||
|
}
|
||||||
|
|
||||||
|
dis.close();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
error("cannot receive data from client: %s", getAddress());
|
||||||
|
}
|
||||||
|
deadFish();
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void onReceiveMessage(String message) {
|
||||||
|
if (!Response.isResponsePattern(message)) {
|
||||||
|
host.info("(%s) message received: %s", getAddress(), message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
Response res = Response.parse(message);
|
||||||
|
if (res.getMethod() != Response.Method.TO_SERVER) return;
|
||||||
|
if (res.getType() == Response.Type.DEAD_FISH)
|
||||||
|
this.deadFish();
|
||||||
|
if (res.getType() == Response.Type.HANDSHAKE) {
|
||||||
|
this.clientSideName = (String) res.getArgs()[0];
|
||||||
|
host.onHandShake(getAddress(), clientSideName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
host.error("Response parse failure: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void deadFish() {
|
||||||
|
host.info("disconnecting %s ...", this.getAddress());
|
||||||
|
host.removeConnection(getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect() {
|
||||||
|
info("disconnecting from %s ...", getAddress());
|
||||||
|
queuedMessages.clear();
|
||||||
|
try {
|
||||||
|
socket.close();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
error("connection closed");
|
||||||
|
}
|
||||||
|
interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendMessage(String str) {
|
||||||
|
if (str != null)
|
||||||
|
queuedMessages.add(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void sendMessage(Response res) {
|
||||||
|
sendMessage(res.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
public Address getAddress() {
|
||||||
|
return new Address(socket);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof Connection conn))
|
||||||
|
return false;
|
||||||
|
return this.getAddress().equals(conn.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getClientSideName() {
|
||||||
|
return clientSideName;
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/main/java/me/trouper/butler/server/ConnectionThread.java
Normal file
12
src/main/java/me/trouper/butler/server/ConnectionThread.java
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package me.trouper.butler.server;
|
||||||
|
|
||||||
|
public class ConnectionThread extends Thread {
|
||||||
|
|
||||||
|
protected void error(String str, Object... args) {
|
||||||
|
System.err.println(getName() + " Error: " + str.formatted(args));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void info(String str, Object... args) {
|
||||||
|
System.out.println(getName() + " Info: " + str.formatted(args));
|
||||||
|
}
|
||||||
|
}
|
||||||
111
src/main/java/me/trouper/butler/server/Response.java
Normal file
111
src/main/java/me/trouper/butler/server/Response.java
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
package me.trouper.butler.server;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
public class Response {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final Method method;
|
||||||
|
private final Type type;
|
||||||
|
private final Object[] args;
|
||||||
|
|
||||||
|
public Response(Method method, Type type, Object... args) {
|
||||||
|
this.method = method;
|
||||||
|
this.type = type;
|
||||||
|
this.args = args;
|
||||||
|
this.id = "%s:%s(%s)".formatted(method.id, type.id, Arrays.toString(args).replaceAll("(^\\[)|(]$)", ""));
|
||||||
|
}
|
||||||
|
|
||||||
|
public Method getMethod() {
|
||||||
|
return method;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type getType() {
|
||||||
|
return type;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Object[] getArgs() {
|
||||||
|
return args;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object obj) {
|
||||||
|
if (!(obj instanceof Response res))
|
||||||
|
return false;
|
||||||
|
return res.id.equals(this.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Response parse(String responseString) throws IllegalArgumentException {
|
||||||
|
try {
|
||||||
|
String[] split = responseString.split(":");
|
||||||
|
String body = split[1];
|
||||||
|
String methodStr = split[0].trim();
|
||||||
|
String typeStr = body.replaceFirst("\\(.*\\)", "").trim();
|
||||||
|
String argsStr = body.replaceFirst("^.*\\(", "").replaceAll("\\)$", "").trim();
|
||||||
|
Object[] args = argsStr.split("\s*,\s*");
|
||||||
|
|
||||||
|
Method method = null;
|
||||||
|
Type type = null;
|
||||||
|
|
||||||
|
for (var v : Method.values()) {
|
||||||
|
if (v.id.equals(methodStr)) {
|
||||||
|
method = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (var v : Type.values()) {
|
||||||
|
if (v.id.equals(typeStr)) {
|
||||||
|
type = v;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method == null || type == null)
|
||||||
|
throw new IllegalArgumentException("method or type cannot be null! (provided: %s, %s)".formatted(methodStr, typeStr));
|
||||||
|
|
||||||
|
return new Response(method, type, args);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
throw new IllegalArgumentException("response syntax is invalid: " + ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isResponsePattern(String str) {
|
||||||
|
return str.matches(".+:.+\\((.+,?)\\)");
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Method {
|
||||||
|
TO_SERVER("improperC2S"),
|
||||||
|
TO_CLIENT("improperS2C");
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
Method(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Type {
|
||||||
|
HANDSHAKE("handshake"),
|
||||||
|
DEAD_FISH("dead_fish");
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
|
||||||
|
Type(String id) {
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
108
src/main/java/me/trouper/butler/server/Server.java
Normal file
108
src/main/java/me/trouper/butler/server/Server.java
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
package me.trouper.butler.server;
|
||||||
|
|
||||||
|
import java.net.ServerSocket;
|
||||||
|
import java.net.Socket;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
|
|
||||||
|
public class Server extends ConnectionThread {
|
||||||
|
|
||||||
|
private final ConcurrentLinkedQueue<Connection> connections = new ConcurrentLinkedQueue<>();
|
||||||
|
private ServerSocket serverSocket;
|
||||||
|
private final Address address;
|
||||||
|
|
||||||
|
public Server(Address address) {
|
||||||
|
this.address = address;
|
||||||
|
this.setName("[SERVER:%s]".formatted(address.port()));
|
||||||
|
|
||||||
|
try {
|
||||||
|
info("starting server...");
|
||||||
|
this.serverSocket = new ServerSocket(address.port());
|
||||||
|
start();
|
||||||
|
info("server has started on %s:%s", serverSocket.getInetAddress().getHostAddress(), address.port());
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
error("error starting server: %s", ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
while (!isInterrupted()) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
Socket socket = serverSocket.accept();
|
||||||
|
this.onClientConnect(socket);
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
error("cannot handle client connect event: %s", ex.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected synchronized void onClientConnect(Socket socket) {
|
||||||
|
Connection conn = new Connection(this, socket);
|
||||||
|
connections.add(conn);
|
||||||
|
Address connAddress = conn.getAddress();
|
||||||
|
conn.sendMessage(new Response(Response.Method.TO_CLIENT, Response.Type.HANDSHAKE, connAddress.ip(), connAddress.port()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void broadcast(String str) {
|
||||||
|
for (var conn : connections)
|
||||||
|
conn.sendMessage(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized Connection getConnectionOfAddress(Address address) {
|
||||||
|
for (var conn : connections)
|
||||||
|
if (conn.getAddress().equals(address))
|
||||||
|
return conn;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void dmConnection(Address address, String message) {
|
||||||
|
Connection con = getConnectionOfAddress(address);
|
||||||
|
if (con != null) con.sendMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized void dmConnection(Client client, String message) {
|
||||||
|
if (client != null && client.getServerSideAddress() != null) dmConnection(client.getServerSideAddress(),message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean removeConnection(Address address) {
|
||||||
|
for (var conn : connections)
|
||||||
|
if (conn.getAddress().equals(address))
|
||||||
|
return connections.remove(conn);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void disconnect() {
|
||||||
|
interrupt();
|
||||||
|
info("stopping server...");
|
||||||
|
|
||||||
|
for (var conn : connections)
|
||||||
|
conn.disconnect();
|
||||||
|
connections.clear();
|
||||||
|
|
||||||
|
try {
|
||||||
|
serverSocket.close();
|
||||||
|
}
|
||||||
|
catch (Exception ex) {
|
||||||
|
error("server closed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int connectionCount() {
|
||||||
|
return connections.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getPort() {
|
||||||
|
return address.port();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onHandShake(Address address, String clientSideName) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public ConcurrentLinkedQueue<Connection> getConnections() {
|
||||||
|
return connections;
|
||||||
|
}
|
||||||
|
}
|
||||||
35
src/main/java/me/trouper/butler/utils/CipherUtils.java
Normal file
35
src/main/java/me/trouper/butler/utils/CipherUtils.java
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
package me.trouper.butler.utils;
|
||||||
|
|
||||||
|
import javax.crypto.Cipher;
|
||||||
|
import javax.crypto.spec.SecretKeySpec;
|
||||||
|
import java.util.Base64;
|
||||||
|
|
||||||
|
public class CipherUtils {
|
||||||
|
private static final String secretKey = "GG8T885O4Yd/86OMVFdL0w=="; // 16, 24, or 32 bytes
|
||||||
|
private static final String algorithm = "AES";
|
||||||
|
public static String encrypt(String strToEncrypt) {
|
||||||
|
try {
|
||||||
|
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), algorithm);
|
||||||
|
Cipher cipher = Cipher.getInstance(algorithm);
|
||||||
|
cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec);
|
||||||
|
byte[] encryptedBytes = cipher.doFinal(strToEncrypt.getBytes());
|
||||||
|
return Base64.getEncoder().encodeToString(encryptedBytes);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String decrypt(String strToDecrypt) {
|
||||||
|
try {
|
||||||
|
SecretKeySpec secretKeySpec = new SecretKeySpec(secretKey.getBytes(), algorithm);
|
||||||
|
Cipher cipher = Cipher.getInstance(algorithm);
|
||||||
|
cipher.init(Cipher.DECRYPT_MODE, secretKeySpec);
|
||||||
|
byte[] decryptedBytes = cipher.doFinal(Base64.getDecoder().decode(strToDecrypt));
|
||||||
|
return new String(decryptedBytes);
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
40
src/main/java/me/trouper/butler/utils/MathUtils.java
Normal file
40
src/main/java/me/trouper/butler/utils/MathUtils.java
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package me.trouper.butler.utils;
|
||||||
|
|
||||||
|
import java.awt.geom.Point2D;
|
||||||
|
|
||||||
|
public class MathUtils {
|
||||||
|
|
||||||
|
// WARNING! This class contains SIN!!!!!
|
||||||
|
|
||||||
|
public static Point2D.Double[] distributePoints(double centerX, double centerZ, double radius, int n) {
|
||||||
|
Point2D.Double[] points = new Point2D.Double[n];
|
||||||
|
double angleIncrement = 2 * Math.PI / n;
|
||||||
|
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
double angle = i * angleIncrement;
|
||||||
|
double x = centerX + radius * Math.cos(angle);
|
||||||
|
double z = centerZ + radius * Math.sin(angle);
|
||||||
|
points[i] = new Point2D.Double(x, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float[] toPolar(double x, double y, double z) {
|
||||||
|
double pi2 = 2 * Math.PI;
|
||||||
|
float pitch, yaw;
|
||||||
|
|
||||||
|
if (x == 0 && z == 0) {
|
||||||
|
pitch = y > 0 ? -90 : 90;
|
||||||
|
return new float[] { pitch, 0.0F };
|
||||||
|
}
|
||||||
|
|
||||||
|
double theta = Math.atan2(-x, z);
|
||||||
|
yaw = (float)Math.toDegrees((theta + pi2) % pi2);
|
||||||
|
|
||||||
|
double xz = Math.sqrt(x * x + z * z);
|
||||||
|
pitch = (float)Math.toDegrees(Math.atan(-y / xz));
|
||||||
|
|
||||||
|
return new float[] { pitch, yaw };
|
||||||
|
}
|
||||||
|
}
|
||||||
30
src/main/java/me/trouper/butler/utils/Text.java
Normal file
30
src/main/java/me/trouper/butler/utils/Text.java
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
package me.trouper.butler.utils;
|
||||||
|
|
||||||
|
import java.util.regex.Matcher;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
public class Text {
|
||||||
|
|
||||||
|
public static String getPacketType(String input) {
|
||||||
|
Pattern pattern = Pattern.compile("\\[(.*?)\\]");
|
||||||
|
Matcher matcher = pattern.matcher(input);
|
||||||
|
|
||||||
|
if (matcher.find()) {
|
||||||
|
return matcher.group(1);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPacketArgs(String input) {
|
||||||
|
Pattern pattern = Pattern.compile("\\[\\s*(.*?)\\s*\\]");
|
||||||
|
Matcher matcher = pattern.matcher(input);
|
||||||
|
|
||||||
|
if (matcher.find()) {
|
||||||
|
int endIndex = matcher.end();
|
||||||
|
return input.substring(endIndex).trim();
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,11 +1,12 @@
|
|||||||
{
|
{
|
||||||
"schemaVersion": 1,
|
"schemaVersion": 1,
|
||||||
"id": "addon-template",
|
"id": "butler-addon",
|
||||||
"version": "${version}",
|
"version": "${version}",
|
||||||
"name": "Addon Template",
|
"name": "Butler Addon",
|
||||||
"description": "An addon template for the Meteor addons.",
|
"description": "Added functionality to the swarm module.",
|
||||||
"authors": [
|
"authors": [
|
||||||
"seasnail"
|
"obvWolf",
|
||||||
|
"ImproperIssues"
|
||||||
],
|
],
|
||||||
"contact": {
|
"contact": {
|
||||||
"repo": "https://github.com/MeteorDevelopment/meteor-addon-template"
|
"repo": "https://github.com/MeteorDevelopment/meteor-addon-template"
|
||||||
@@ -14,7 +15,7 @@
|
|||||||
"environment": "client",
|
"environment": "client",
|
||||||
"entrypoints": {
|
"entrypoints": {
|
||||||
"meteor": [
|
"meteor": [
|
||||||
"com.example.addon.Addon"
|
"me.trouper.butler.Addon"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mixins": [
|
"mixins": [
|
||||||
|
|||||||
Reference in New Issue
Block a user