Skip to content

Add the cserverseed command #698

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: fabric
Choose a base branch
from
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ public static void registerCommands(CommandDispatcher<FabricClientCommandSource>
RelogCommand.register(dispatcher);
RenderCommand.register(dispatcher);
ReplyCommand.register(dispatcher);
ServerSeedCommand.register(dispatcher);
ShrugCommand.register(dispatcher);
SignSearchCommand.register(dispatcher);
SnakeCommand.register(dispatcher);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package net.earthcomputer.clientcommands.command;

import com.google.common.hash.HashCode;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import com.seedfinding.mccore.rand.seed.WorldSeed;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.network.chat.Component;
import net.minecraft.network.chat.ComponentUtils;
import org.apache.commons.lang3.StringUtils;

import java.util.List;

import static com.mojang.brigadier.arguments.LongArgumentType.*;
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*;

public class ServerSeedCommand {

private static final SimpleCommandExceptionType IMPOSSIBLE_SEED_COMBINATION_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.cserverseed.hashedSeed.impossibleSeedCombination"));
private static final SimpleCommandExceptionType ENCHANTMENT_SEED_NOT_SENT_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.cserverseed.enchantmentSeedNotSent"));

public static void register(CommandDispatcher<FabricClientCommandSource> dispatcher) {
dispatcher.register(literal("cserverseed")
.then(literal("enchantmentSeed")
.executes(ctx -> enchantmentSeed(ctx.getSource())))
.then(literal("hashedSeed")
.executes(ctx -> hashedSeed(ctx.getSource()))
.then(argument("structureSeed", longArg())
.executes(ctx -> fromStructureSeed(ctx.getSource(), getLong(ctx, "structureSeed"))))));
}

private static int enchantmentSeed(FabricClientCommandSource source) throws CommandSyntaxException {
int seed = source.getPlayer().getEnchantmentSeed();
if (seed == 0) {
throw ENCHANTMENT_SEED_NOT_SENT_EXCEPTION.create();
}
// The enchantment seed is a Java int, which is 32 bits.
// However, the server sends the enchantment seed as short to the client.
// This causes the 16 higher bits to be ignored, leaving only the 16 lower bits.
seed &= 0x0000ffff;
source.sendFeedback(Component.translatable("commands.cserverseed.enchantmentSeed", ComponentUtils.copyOnClickText(StringUtils.leftPad(Integer.toBinaryString(seed), 16, '0'))));
return seed;
}

private static int hashedSeed(FabricClientCommandSource source) {
long hashedSeed = source.getWorld().getBiomeManager().biomeZoomSeed;
HashCode seedHash = HashCode.fromLong(hashedSeed);
source.sendFeedback(Component.translatable("commands.cserverseed.hashedSeed", ComponentUtils.copyOnClickText(seedHash.toString()), ComponentUtils.copyOnClickText(String.valueOf(seedHash.asLong()))));
return (int) hashedSeed;
}

private static int fromStructureSeed(FabricClientCommandSource source, long structureSeed) throws CommandSyntaxException {
long hashedSeed = source.getWorld().getBiomeManager().biomeZoomSeed;
List<Long> seeds = WorldSeed.fromHash(structureSeed, hashedSeed);
if (seeds.isEmpty()) {
throw IMPOSSIBLE_SEED_COMBINATION_EXCEPTION.create();
}
// Collision is extremely unlikely, so just call `getFirst`
long worldSeed = seeds.getFirst();
source.sendFeedback(Component.translatable("commands.cserverseed.hashedSeed.fromStructureSeed", ComponentUtils.copyOnClickText(String.valueOf(worldSeed))));
return (int) worldSeed;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package net.earthcomputer.clientcommands.mixin.commands.serverseed;

import com.llamalad7.mixinextras.sugar.Local;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.network.protocol.game.ClientboundContainerSetDataPacket;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.EnchantmentMenu;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

@Mixin(ClientPacketListener.class)
public class ClientPacketListenerMixin {
@Inject(method = "handleContainerSetData", at = @At(value = "INVOKE", target = "Lnet/minecraft/world/inventory/AbstractContainerMenu;setData(II)V", shift = At.Shift.AFTER))
private void onContainerData(ClientboundContainerSetDataPacket packet, CallbackInfo ci, @Local Player player) {
if (player.containerMenu instanceof EnchantmentMenu menu) {
player.enchantmentSeed = menu.getEnchantmentSeed();
}
}
}
6 changes: 6 additions & 0 deletions src/main/resources/assets/clientcommands/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,12 @@
"commands.creply.messageTooLong": "Your reply was too long (maximum: %s, given: %s)",
"commands.creply.noTargetFound": "Could not find a target to reply to",

"commands.cserverseed.enchantmentSeed": "Lower 16 bits of the enchantment seed: %s",
"commands.cserverseed.enchantmentSeedNotSent": "The server has not sent the partial enchantment seed yet!",
"commands.cserverseed.hashedSeed": "First 8 bytes of the hashed world seed: %s (decimal: %s)",
"commands.cserverseed.hashedSeed.fromStructureSeed": "Found world seed: %s",
"commands.cserverseed.hashedSeed.impossibleSeedCombination": "Impossible seed combination!",

"commands.csignsearch.starting": "Searching signs",

"commands.csnap.airborne": "You cannot snap while airborne",
Expand Down
4 changes: 4 additions & 0 deletions src/main/resources/clientcommands.aw
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ accessible method net/minecraft/world/entity/projectile/FishingHook canHitEntity
accessible method net/minecraft/world/entity/player/Inventory addResource (ILnet/minecraft/world/item/ItemStack;)I
accessible method net/minecraft/world/entity/player/Inventory hasRemainingSpaceForItem (Lnet/minecraft/world/item/ItemStack;Lnet/minecraft/world/item/ItemStack;)Z

# cserverseed
accessible field net/minecraft/world/entity/player/Player enchantmentSeed I
accessible field net/minecraft/world/level/biome/BiomeManager biomeZoomSeed J

# chat
accessible method net/minecraft/client/Minecraft openChatScreen (Ljava/lang/String;)V

Expand Down
1 change: 1 addition & 0 deletions src/main/resources/mixins.clientcommands.json
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@
"commands.generic.CommandSuggestionsMixin",
"commands.glow.LivingEntityRenderStateMixin",
"commands.reply.ClientPacketListenerMixin",
"commands.serverseed.ClientPacketListenerMixin",
"commands.snap.MinecraftMixin",
"dataqueryhandler.ClientPacketListenerMixin",
"events.ClientPacketListenerMixin",
Expand Down
Loading