Skip to content

Commit

Permalink
Merge remote-tracking branch 'upstream/fabric' into connectfour
Browse files Browse the repository at this point in the history
  • Loading branch information
RealRTTV committed Nov 30, 2024
2 parents a086e1b + 22f4579 commit 2f490ba
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ public static void registerCommands(CommandDispatcher<FabricClientCommandSource>
PosCommand.register(dispatcher);
RelogCommand.register(dispatcher);
RenderCommand.register(dispatcher);
ReplyCommand.register(dispatcher);
ShrugCommand.register(dispatcher);
SignSearchCommand.register(dispatcher);
SnakeCommand.register(dispatcher);
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/net/earthcomputer/clientcommands/Configs.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package net.earthcomputer.clientcommands;

import dev.xpple.betterconfig.api.Config;
import net.earthcomputer.clientcommands.command.ReplyCommand;
import net.earthcomputer.clientcommands.features.ChorusManipulation;
import net.earthcomputer.clientcommands.features.EnchantmentCracker;
import net.earthcomputer.clientcommands.features.FishingCracker;
Expand Down Expand Up @@ -176,4 +177,10 @@ public enum PacketDumpMethod {

@Config
public static int maximumPacketFieldDepth = 10;

@Config(temporary = true, setter = @Config.Setter("setMinimumReplyDelaySeconds"))
public static float minimumReplyDelaySeconds = 0.5f;
public static void setMinimumReplyDelaySeconds(float minimumReplyDelaySeconds) {
Configs.minimumReplyDelaySeconds = Math.clamp(minimumReplyDelaySeconds, 0.0f, ReplyCommand.MAXIMUM_REPLY_DELAY_SECONDS);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package net.earthcomputer.clientcommands.command;

import com.mojang.brigadier.Command;
import com.mojang.brigadier.CommandDispatcher;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.Dynamic2CommandExceptionType;
import com.mojang.brigadier.exceptions.SimpleCommandExceptionType;
import net.earthcomputer.clientcommands.Configs;
import net.fabricmc.fabric.api.client.command.v2.FabricClientCommandSource;
import net.minecraft.SharedConstants;
import net.minecraft.network.chat.Component;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.List;

import static dev.xpple.clientarguments.arguments.CMessageArgument.*;
import static net.fabricmc.fabric.api.client.command.v2.ClientCommandManager.*;

public class ReplyCommand {
public static final float MAXIMUM_REPLY_DELAY_SECONDS = 300.0f;

private static final SimpleCommandExceptionType NO_TARGET_FOUND_EXCEPTION = new SimpleCommandExceptionType(Component.translatable("commands.creply.noTargetFound"));
private static final Dynamic2CommandExceptionType MESSAGE_TOO_LONG_EXCEPTION = new Dynamic2CommandExceptionType((a, b) -> Component.translatable("commands.creply.messageTooLong", a, b));

private static final List<ReplyCandidate> replyCandidates = new ArrayList<>();

@Nullable
public static String getCurrentTarget() {
long now = System.currentTimeMillis();

for (int i = 0; i < replyCandidates.size(); i++) {
ReplyCandidate candidate = replyCandidates.get(i);
if (now - candidate.timestampMs > MAXIMUM_REPLY_DELAY_SECONDS * 1_000.0f) {
replyCandidates.remove(i--);
} else {
// list is ordered and `now - candidate.timestampMs` will only get smaller and smaller, so the cmp above will never change
break;
}
}

for (int i = replyCandidates.size() - 1; i >= 0; i--) {
ReplyCandidate candidate = replyCandidates.get(i);
if (now - candidate.timestampMs >= Configs.minimumReplyDelaySeconds * 1_000.0f) {
return candidate.username;
}
}

return null;
}

public static void addReplyCandidate(String username, long timestamp) {
replyCandidates.add(new ReplyCandidate(username, timestamp));
}

public static void register(CommandDispatcher<FabricClientCommandSource> dispatcher) {
var command = dispatcher.register(literal("creply")
.then(argument("message", message())
.executes(ctx -> reply(ctx.getSource(), getMessage(ctx, "message")))));
dispatcher.register(literal("cr").redirect(command));
}

public static int reply(FabricClientCommandSource source, Component message) throws CommandSyntaxException {
String target = ReplyCommand.getCurrentTarget();
if (target == null) {
throw NO_TARGET_FOUND_EXCEPTION.create();
}

String text = message.getString();
String command = String.format("w %s %s", target, text);

if (command.length() > SharedConstants.MAX_CHAT_LENGTH) {
throw MESSAGE_TOO_LONG_EXCEPTION.create(SharedConstants.MAX_CHAT_LENGTH - (command.length() - text.length()), text.length());
}

source.getClient().getConnection().sendCommand(command);

return Command.SINGLE_SUCCESS;
}

private record ReplyCandidate(String username, long timestampMs) {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package net.earthcomputer.clientcommands.mixin.commands.reply;

import net.earthcomputer.clientcommands.command.ReplyCommand;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.multiplayer.PlayerInfo;
import net.minecraft.network.chat.ChatType;
import net.minecraft.network.protocol.game.ClientboundPlayerChatPacket;
import org.jetbrains.annotations.Nullable;
import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.Shadow;
import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;

import java.util.UUID;

@Mixin(ClientPacketListener.class)
public abstract class ClientPacketListenerMixin {
@Shadow public abstract @Nullable PlayerInfo getPlayerInfo(UUID uniqueId);

@Inject(method = "handlePlayerChat", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/protocol/PacketUtils;ensureRunningOnSameThread(Lnet/minecraft/network/protocol/Packet;Lnet/minecraft/network/PacketListener;Lnet/minecraft/util/thread/BlockableEventLoop;)V", shift = At.Shift.AFTER))
private void onHandlePlayerChat(ClientboundPlayerChatPacket packet, CallbackInfo ci) {
if (packet.chatType().chatType().is(ChatType.MSG_COMMAND_INCOMING) || packet.chatType().chatType().is(ChatType.MSG_COMMAND_OUTGOING)) {
PlayerInfo info = getPlayerInfo(packet.sender());
if (info != null) {
ReplyCommand.addReplyCandidate(info.getProfile().getName(), System.currentTimeMillis());
}
}
}
}
3 changes: 3 additions & 0 deletions src/main/resources/assets/clientcommands/lang/en_us.json
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,9 @@

"commands.crender.entities.success": "Entity rendering rules have been updated",

"commands.creply.noTargetFound": "Could not find a target to reply to",
"commands.creply.messageTooLong": "Your reply was too long (maximum: %d, given: %d)",

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

"commands.csnap.airborne": "You cannot snap while airborne",
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 @@ -66,6 +66,7 @@
"commands.alias.ClientSuggestionProviderMixin",
"commands.enchant.MultiPlayerGameModeMixin",
"commands.findblock.ClientLevelMixin",
"commands.reply.ClientPacketListenerMixin",
"commands.generic.CommandSuggestionsMixin",
"commands.glow.LivingEntityRenderStateMixin",
"commands.snap.MinecraftMixin",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ public ReferencesSet findMethodReferences(String owner, String name, String desc
// we don't know whether the method is virtual or not yet, so find the non-virtual method first and then check
// if it's non-virtual
ReferencesSet nonVirtualReferences = resolveNonVirtualMethod(classInfo, new NameAndDesc(name, desc), index::get);
if (nonVirtualReferences != null && (nonVirtualReferences.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC)) != 0) {
if (nonVirtualReferences != null && ((nonVirtualReferences.access & (Opcodes.ACC_PRIVATE | Opcodes.ACC_STATIC)) != 0 || "<init>".equals(name))) {
return nonVirtualReferences;
}

Expand Down

0 comments on commit 2f490ba

Please sign in to comment.