init addon
Some checks failed
build / build (push) Has been cancelled

This commit is contained in:
Shrecknt 2025-07-12 12:27:33 -07:00
parent 3ab4c6667a
commit 8993d9c5a8
13 changed files with 186 additions and 109 deletions

View file

@ -1,9 +1,5 @@
# Sun Addon Template
# Sun LiveOverflow Addon
## Setup
Utilities for the LiveOverflow CTF server.
For setup instructions please see the [fabric wiki page](https://fabricmc.net/wiki/tutorial:setup) that relates to the IDE that you are using.
## License
This template is available under the CC0 license. Feel free to learn from it and incorporate it in your own projects.
Only works with [Sun Client](https://git.shrecked.dev/Shrecknt/sun).

View file

@ -10,8 +10,8 @@ loader_version=0.16.14
# Mod Properties
mod_version=1.0.0
maven_group=com.example
archives_base_name=sun-addon-template
maven_group=dev.shrecked.ctf
archives_base_name=ctf
# Dependencies
sun_version=1.2.1

View file

@ -1,30 +0,0 @@
package com.example;
import com.mojang.logging.LogUtils;
import dev.shrecked.sun.SunMod;
import dev.shrecked.sun.components.SunAddonInitializer;
import dev.shrecked.sun.components.command.CommandRegistry;
import dev.shrecked.sun.components.module.ModuleRegistry;
import org.slf4j.Logger;
import java.lang.invoke.MethodHandles;
public class ExampleAddon implements SunAddonInitializer {
public static final Logger LOGGER = LogUtils.getLogger();
@Override
public void onInitialize() {
SunMod.NORBIT.registerLambdaFactory("com.example", (lookupInMethod, klass) -> (MethodHandles.Lookup) lookupInMethod.invoke(null, klass, MethodHandles.lookup()));
LOGGER.info("Sun example addon initialized!");
}
@Override
public void loadModules() {
ModuleRegistry.reflectModulesFrom("com.example.modules");
}
@Override
public void loadCommands() {
CommandRegistry.reflectCommandsFrom("com.example.commands");
}
}

View file

@ -1,22 +0,0 @@
package com.example.commands;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import dev.shrecked.sun.components.annotation.SunCommand;
import dev.shrecked.sun.components.command.Command;
import dev.shrecked.sun.util.ChatUtil;
import net.minecraft.command.CommandSource;
@SunCommand(name = "example-command", alias = {"ex"})
public class ExampleCommand extends Command {
@Override
public void build(LiteralArgumentBuilder<CommandSource> builder) {
builder.then(literal("echo")
.then(argument("text", StringArgumentType.greedyString()).executes(context -> {
String text = context.getArgument("text", String.class);
ChatUtil.info(String.format("echo! %s", text));
return SINGLE_SUCCESS;
}))
).build();
}
}

View file

@ -1,39 +0,0 @@
package com.example.modules;
import com.example.ExampleAddon;
import dev.shrecked.sun.components.annotation.SunModule;
import dev.shrecked.sun.components.event.Events;
import dev.shrecked.sun.components.module.Category;
import dev.shrecked.sun.components.module.Module;
import dev.shrecked.sun.components.setting.settings.StringSetting;
import meteordevelopment.orbit.EventHandler;
import net.minecraft.client.MinecraftClient;
import java.awt.*;
@SunModule(key = "example-module", category = Category.World.class)
public class ExampleModule extends Module {
private final StringSetting text = config.add("text", new StringSetting("Example text display!"));
@Override
protected void onEnable() {
ExampleAddon.LOGGER.info("enabled example module!");
}
@Override
protected void onDisable() {
ExampleAddon.LOGGER.info("disabled example module!");
}
@EventHandler
private void onHudRender(Events.HudRender event) {
event.context().drawText(
MinecraftClient.getInstance().textRenderer,
this.text.getValue(),
5,
5,
Color.RED.getRGB(),
true
);
}
}

View file

@ -0,0 +1,40 @@
package dev.shrecked.ctf;
import com.mojang.logging.LogUtils;
import dev.shrecked.sun.SunMod;
import dev.shrecked.sun.components.SunAddonInitializer;
import dev.shrecked.sun.components.annotation.SunCategory;
import dev.shrecked.sun.components.command.CommandRegistry;
import dev.shrecked.sun.components.module.Category;
import dev.shrecked.sun.components.module.ModuleRegistry;
import org.slf4j.Logger;
import java.lang.invoke.MethodHandles;
public class CtfAddon implements SunAddonInitializer {
public static final Logger LOGGER = LogUtils.getLogger();
@Override
public void onInitialize() {
SunMod.NORBIT.registerLambdaFactory("dev.shrecked.ctf", (lookupInMethod, klass) -> (MethodHandles.Lookup) lookupInMethod.invoke(null, klass, MethodHandles.lookup()));
LOGGER.info("Sun example addon initialized!");
}
@Override
public void loadCategories() {
Category.add(CtfCategory.class);
}
@Override
public void loadModules() {
ModuleRegistry.reflectModulesFrom("dev.shrecked.ctf.modules");
}
@Override
public void loadCommands() {
CommandRegistry.reflectCommandsFrom("dev.shrecked.ctf.commands");
}
@SunCategory(key = "ctf")
public static class CtfCategory extends Category {}
}

View file

@ -0,0 +1,25 @@
package dev.shrecked.ctf.mixin;
import com.llamalad7.mixinextras.injector.wrapoperation.Operation;
import com.llamalad7.mixinextras.injector.wrapoperation.WrapOperation;
import dev.shrecked.ctf.modules.RoboWalk;
import dev.shrecked.sun.components.module.ModuleRegistry;
import dev.shrecked.sun.modules.EventlessMove;
import net.minecraft.client.network.ClientPlayNetworkHandler;
import net.minecraft.network.packet.Packet;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
@Mixin(EventlessMove.class)
public class EventlessMoveMixin {
/**
* FIXME: The position generated by {@code from.lerp(to, 0.499)} ({@code ordinal = 1}) is rounded, so this movement is too far and generates a movement event.
*/
@WrapOperation(method = "onPlayerMove", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/network/ClientPlayNetworkHandler;sendPacket(Lnet/minecraft/network/packet/Packet;)V", ordinal = 4))
private void onPlayerMove(ClientPlayNetworkHandler instance, Packet<?> packet, Operation<Void> original) {
RoboWalk roboWalk = ModuleRegistry.fromClass(RoboWalk.class);
if (roboWalk.isEnabled()) roboWalk.forceDisableHandler = true;
original.call(instance, packet);
if (roboWalk.isEnabled()) roboWalk.forceDisableHandler = false;
}
}

View file

@ -0,0 +1,41 @@
package dev.shrecked.ctf.modules;
import dev.shrecked.ctf.CtfAddon;
import dev.shrecked.sun.components.annotation.SunModule;
import dev.shrecked.sun.components.event.Events;
import dev.shrecked.sun.components.module.Module;
import meteordevelopment.orbit.EventHandler;
import net.minecraft.network.packet.s2c.play.GameStateChangeS2CPacket;
import net.minecraft.network.packet.s2c.play.WorldBorderInitializeS2CPacket;
import net.minecraft.network.packet.s2c.play.WorldBorderSizeChangedS2CPacket;
import net.minecraft.world.border.WorldBorder;
@SunModule(key = "game-state-spoofer", category = CtfAddon.CtfCategory.class)
public class GameStateSpoofer extends Module {
@EventHandler
private void onInboundPacket(Events.InboundPacket event) {
if (event.packet instanceof WorldBorderSizeChangedS2CPacket) {
event.packet = new WorldBorderSizeChangedS2CPacket(worldBorder());
}
if (event.packet instanceof WorldBorderInitializeS2CPacket) {
event.packet = new WorldBorderInitializeS2CPacket(worldBorder());
}
if (event.packet instanceof GameStateChangeS2CPacket packet) {
GameStateChangeS2CPacket.Reason reason = packet.getReason();
if (reason == GameStateChangeS2CPacket.GAME_MODE_CHANGED
|| reason == GameStateChangeS2CPacket.GAME_WON
|| reason == GameStateChangeS2CPacket.DEMO_MESSAGE_SHOWN) {
event.cancel();
}
}
}
private static WorldBorder worldBorder() {
WorldBorder worldBorder = new WorldBorder();
worldBorder.setCenter(0, 0);
worldBorder.setSize(Double.MAX_VALUE);
return worldBorder;
}
}

View file

@ -0,0 +1,54 @@
package dev.shrecked.ctf.modules;
import dev.shrecked.ctf.CtfAddon;
import dev.shrecked.sun.components.annotation.SunModule;
import dev.shrecked.sun.components.event.Events;
import dev.shrecked.sun.components.module.Module;
import dev.shrecked.sun.util.ChatUtil;
import meteordevelopment.orbit.EventHandler;
import meteordevelopment.orbit.EventPriority;
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket;
import net.minecraft.network.packet.c2s.play.VehicleMoveC2SPacket;
import net.minecraft.util.math.Vec3d;
@SunModule(key = "robo-walk", category = CtfAddon.CtfCategory.class)
public class RoboWalk extends Module {
public boolean forceDisableHandler = false;
@EventHandler(priority = EventPriority.LOWEST)
private void onOutboundPacket(Events.OutboundPacket event) {
if (forceDisableHandler) return;
if (event.packet instanceof PlayerMoveC2SPacket packet && packet.changesPosition()) {
double newX = round(packet.getX(0f));
double newY = packet.getY(0f);
double newZ = round(packet.getZ(0f));
PlayerMoveC2SPacket newPacket;
if (packet instanceof PlayerMoveC2SPacket.PositionAndOnGround) {
newPacket = new PlayerMoveC2SPacket.PositionAndOnGround(newX, newY, newZ, packet.isOnGround(), packet.horizontalCollision());
} else if (packet instanceof PlayerMoveC2SPacket.Full) {
newPacket = new PlayerMoveC2SPacket.Full(newX, newY, newZ, packet.getYaw(0f), packet.getPitch(0f), packet.isOnGround(), packet.horizontalCollision());
} else {
throw new RuntimeException("Unhandled movement packet type '" + packet.getClass().getSimpleName() + "'");
}
event.packet = newPacket;
}
if (event.packet instanceof VehicleMoveC2SPacket(Vec3d position, float yaw, float pitch, boolean onGround)) {
Vec3d fixedPosition = new Vec3d(
round(position.getX()),
position.getY(),
round(position.getZ())
);
event.packet = new VehicleMoveC2SPacket(fixedPosition, yaw, pitch, onGround);
}
}
private static double round(double value) {
double rounded = (double) (Math.round(value * 100)) / 100;
double res = Math.nextAfter(rounded, rounded + Math.signum(value));
long check = ((long) (res * 1000)) % 10;
if (check != 0) ChatUtil.error("Bad round " + value);
return res;
}
}

View file

Before

Width:  |  Height:  |  Size: 453 B

After

Width:  |  Height:  |  Size: 453 B

View file

@ -1,4 +1,5 @@
{
"sun.modules.example-module": "Example Module",
"sun.modules.example-module.setting.text": "Example Text Config Option"
"sun.category.ctf": "LiveOverflow CTF",
"sun.modules.game-state-spoofer": "Game State Spoof",
"sun.modules.robo-walk": "Robo-Walk"
}

View file

@ -0,0 +1,8 @@
{
"required": true,
"package": "dev.shrecked.ctf.mixin",
"compatibilityLevel": "JAVA_21",
"client": [
"EventlessMoveMixin"
]
}

View file

@ -1,29 +1,32 @@
{
"schemaVersion": 1,
"id": "sun-addon-template",
"id": "ctf",
"version": "${version}",
"name": "Sun Addon Template",
"description": "A template addon for Sun client",
"name": "Sun LiveOverflow Addon",
"description": "Sun client addon for the LiveOverflow CTF server",
"authors": [
"Me!"
"Shrecknt"
],
"contact": {
"homepage": "https://shrecked.dev/",
"sources": "https://git.ssi.fyi/Shrecknt/sun-addon-template"
},
"license": "CC0-1.0",
"icon": "assets/sun-addon-template/icon.png",
"icon": "assets/ctf/icon.png",
"environment": "*",
"entrypoints": {
"sun-addon": [
"com.example.ExampleAddon"
"dev.shrecked.ctf.CtfAddon"
]
},
"mixins": [
"ctf.mixins.json"
],
"depends": {
"fabricloader": ">=0.15.0",
"minecraft": ">=1.21.1",
"java": ">=21",
"fabric-api": "*",
"sun": ">=1.1.0"
"sun": ">=1.2.1"
}
}