Strip all Phase references, TODO/FUTURE roadmap notes, and internal planning comments from the codebase. Run Prettier for consistent formatting across all Java files.
199 lines
7.0 KiB
Java
199 lines
7.0 KiB
Java
package com.tiedup.remake.events.system;
|
|
|
|
import com.tiedup.remake.core.SettingsAccessor;
|
|
import com.tiedup.remake.core.SystemMessageManager;
|
|
import com.tiedup.remake.core.TiedUpMod;
|
|
import com.tiedup.remake.dialogue.GagTalkManager;
|
|
import com.tiedup.remake.items.base.ItemGag;
|
|
import com.tiedup.remake.state.IBondageState;
|
|
import com.tiedup.remake.util.GagMaterial;
|
|
import com.tiedup.remake.util.KidnappedHelper;
|
|
import com.tiedup.remake.util.TiedUpUtils;
|
|
import com.tiedup.remake.v2.BodyRegionV2;
|
|
import com.tiedup.remake.v2.bondage.capability.V2EquipmentHelper;
|
|
import java.util.List;
|
|
import net.minecraft.network.chat.Component;
|
|
import net.minecraft.server.level.ServerPlayer;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraftforge.event.CommandEvent;
|
|
import net.minecraftforge.event.ServerChatEvent;
|
|
import net.minecraftforge.eventbus.api.EventPriority;
|
|
import net.minecraftforge.eventbus.api.SubscribeEvent;
|
|
import net.minecraftforge.fml.common.Mod;
|
|
|
|
/**
|
|
* ChatEventHandler - Intercepts chat messages to apply gag effects.
|
|
* Evolution: Implements proximity-based chat for gagged players.
|
|
*
|
|
*
|
|
* Security fix: Now blocks communication commands (/msg, /tell, etc.) when gagged
|
|
* to prevent gag bypass exploit
|
|
*/
|
|
@Mod.EventBusSubscriber(modid = TiedUpMod.MOD_ID)
|
|
public class ChatEventHandler {
|
|
|
|
/** List of communication commands that should be blocked when gagged */
|
|
private static final String[] BLOCKED_COMMANDS = {
|
|
"msg",
|
|
"tell",
|
|
"w",
|
|
"whisper",
|
|
"r",
|
|
"reply",
|
|
"me",
|
|
};
|
|
|
|
@SubscribeEvent
|
|
public static void onPlayerChat(ServerChatEvent event) {
|
|
ServerPlayer player = event.getPlayer();
|
|
IBondageState state = KidnappedHelper.getKidnappedState(player);
|
|
|
|
if (state != null && state.isGagged()) {
|
|
ItemStack gagStack = V2EquipmentHelper.getInRegion(
|
|
player,
|
|
BodyRegionV2.MOUTH
|
|
);
|
|
|
|
if (
|
|
!gagStack.isEmpty() &&
|
|
gagStack.getItem() instanceof ItemGag gagItem
|
|
) {
|
|
String originalMessage = event.getRawText();
|
|
GagMaterial material = gagItem.getGagMaterial();
|
|
|
|
// 1. Process the message through our GagTalkManager V2
|
|
Component muffledMessage = GagTalkManager.processGagMessage(
|
|
state,
|
|
gagStack,
|
|
originalMessage
|
|
);
|
|
|
|
// 2. Proximity Chat Logic
|
|
boolean useProximity =
|
|
SettingsAccessor.isGagTalkProximityEnabled(
|
|
player.level().getGameRules()
|
|
);
|
|
|
|
if (useProximity) {
|
|
// Cancel global and send to nearby
|
|
event.setCanceled(true);
|
|
|
|
Component finalChat = Component.literal("<")
|
|
.append(player.getDisplayName())
|
|
.append("> ")
|
|
.append(muffledMessage);
|
|
|
|
double range = material.getTalkRange();
|
|
|
|
List<ServerPlayer> nearbyPlayers =
|
|
TiedUpUtils.getPlayersAround(
|
|
player.level(),
|
|
player.blockPosition(),
|
|
range
|
|
);
|
|
|
|
int listeners = 0;
|
|
for (ServerPlayer other : nearbyPlayers) {
|
|
// Check if receiver has earplugs (they can't hear)
|
|
IBondageState receiverState =
|
|
KidnappedHelper.getKidnappedState(other);
|
|
if (
|
|
receiverState != null && receiverState.hasEarplugs()
|
|
) {
|
|
// Can't hear - skip this player
|
|
continue;
|
|
}
|
|
|
|
other.sendSystemMessage(finalChat);
|
|
if (other != player) listeners++;
|
|
}
|
|
|
|
if (listeners == 0) {
|
|
player.displayClientMessage(
|
|
Component.translatable(
|
|
"chat.tiedup.gag.no_one_heard"
|
|
).withStyle(
|
|
net.minecraft.ChatFormatting.ITALIC,
|
|
net.minecraft.ChatFormatting.GRAY
|
|
),
|
|
true
|
|
);
|
|
}
|
|
} else {
|
|
// Just replace message but keep it global
|
|
event.setMessage(muffledMessage);
|
|
}
|
|
|
|
TiedUpMod.LOGGER.debug(
|
|
"[Chat] {} muffled message processed (Proximity: {})",
|
|
player.getName().getString(),
|
|
useProximity
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Intercept commands to prevent gagged players from using communication commands.
|
|
* Blocks /msg, /tell, /w, /whisper, /r, /reply, /me when player is gagged.
|
|
*
|
|
* Security fix: Prevents gag bypass exploit via private messages
|
|
*/
|
|
@SubscribeEvent(priority = EventPriority.HIGHEST)
|
|
public static void onCommand(CommandEvent event) {
|
|
// Only check if sender is a ServerPlayer
|
|
if (
|
|
!(event
|
|
.getParseResults()
|
|
.getContext()
|
|
.getSource()
|
|
.getEntity() instanceof
|
|
ServerPlayer player)
|
|
) {
|
|
return;
|
|
}
|
|
|
|
// Check if player is gagged
|
|
IBondageState state = KidnappedHelper.getKidnappedState(player);
|
|
if (state == null || !state.isGagged()) {
|
|
return; // Not gagged, allow all commands
|
|
}
|
|
|
|
// Get the command name (first part of the command string)
|
|
String commandInput = event.getParseResults().getReader().getString();
|
|
if (commandInput.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
// Remove leading slash if present
|
|
String commandName = commandInput.startsWith("/")
|
|
? commandInput.substring(1)
|
|
: commandInput;
|
|
// Get only the first word (command name)
|
|
commandName = commandName.split(" ")[0].toLowerCase();
|
|
|
|
// Check if this is a blocked communication command
|
|
for (String blockedCmd : BLOCKED_COMMANDS) {
|
|
if (commandName.equals(blockedCmd)) {
|
|
// Block the command
|
|
event.setCanceled(true);
|
|
|
|
// Send muffled message to player
|
|
SystemMessageManager.sendToPlayer(
|
|
player,
|
|
SystemMessageManager.MessageCategory.ERROR,
|
|
"Mmmph! You can't use that command while gagged!"
|
|
);
|
|
|
|
TiedUpMod.LOGGER.debug(
|
|
"[Chat] Blocked command '{}' from gagged player {}",
|
|
commandName,
|
|
player.getName().getString()
|
|
);
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|