Clean repo for open source release
Remove build artifacts, dev tool configs, unused dependencies, and third-party source dumps. Add proper README, update .gitignore, clean up Makefile.
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
package com.tiedup.remake.network.personality;
|
||||
|
||||
/**
|
||||
* Discriminator for the unified NPC command packet.
|
||||
* Each type maps to what was previously a separate packet class.
|
||||
*/
|
||||
public enum NpcCommandType {
|
||||
GIVE_COMMAND,
|
||||
CANCEL_COMMAND,
|
||||
SELECT_JOB,
|
||||
CYCLE_FOLLOW_DISTANCE,
|
||||
TOGGLE_AUTO_REST
|
||||
}
|
||||
@@ -0,0 +1,140 @@
|
||||
package com.tiedup.remake.network.personality;
|
||||
|
||||
import com.tiedup.remake.core.SystemMessageManager;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
// Discipline now only triggers dialogue, no personality effects
|
||||
import com.tiedup.remake.dialogue.EntityDialogueManager;
|
||||
import com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory;
|
||||
import com.tiedup.remake.entities.EntityDamsel;
|
||||
import com.tiedup.remake.network.PacketRateLimiter;
|
||||
import com.tiedup.remake.personality.DisciplineType;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
/**
|
||||
* Packet sent from client to server to apply discipline to an NPC.
|
||||
* Used for verbal discipline actions (Praise, Scold, Threaten) from the DialogueScreen.
|
||||
*
|
||||
* Training System V2: Verbal discipline support
|
||||
*/
|
||||
public class PacketDisciplineAction {
|
||||
|
||||
/** Maximum range for discipline actions */
|
||||
private static final double MAX_RANGE = 32.0;
|
||||
|
||||
private final int entityId;
|
||||
private final String disciplineTypeName;
|
||||
|
||||
public PacketDisciplineAction(int entityId, DisciplineType type) {
|
||||
this.entityId = entityId;
|
||||
this.disciplineTypeName = type.name();
|
||||
}
|
||||
|
||||
private PacketDisciplineAction(int entityId, String disciplineTypeName) {
|
||||
this.entityId = entityId;
|
||||
this.disciplineTypeName = disciplineTypeName;
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeInt(entityId);
|
||||
buf.writeUtf(disciplineTypeName, 32);
|
||||
}
|
||||
|
||||
public static PacketDisciplineAction decode(FriendlyByteBuf buf) {
|
||||
return new PacketDisciplineAction(buf.readInt(), buf.readUtf(32));
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx
|
||||
.get()
|
||||
.enqueueWork(() -> {
|
||||
ServerPlayer sender = ctx.get().getSender();
|
||||
if (sender == null) return;
|
||||
handleServer(sender);
|
||||
});
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
|
||||
private void handleServer(ServerPlayer sender) {
|
||||
// MEDIUM FIX: Rate limiting to prevent discipline spam exploit
|
||||
if (!PacketRateLimiter.allowPacket(sender, "action")) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the target entity
|
||||
Entity entity = sender.level().getEntity(entityId);
|
||||
if (!(entity instanceof EntityDamsel damsel)) {
|
||||
TiedUpMod.LOGGER.warn(
|
||||
"[PacketDisciplineAction] Entity {} not found or not a Damsel",
|
||||
entityId
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Validate distance
|
||||
double distance = sender.distanceTo(damsel);
|
||||
if (distance > MAX_RANGE) {
|
||||
SystemMessageManager.sendToPlayer(
|
||||
sender,
|
||||
SystemMessageManager.MessageCategory.ERROR,
|
||||
"Target is too far away!"
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Parse discipline type
|
||||
DisciplineType type;
|
||||
try {
|
||||
type = DisciplineType.valueOf(disciplineTypeName);
|
||||
} catch (IllegalArgumentException e) {
|
||||
TiedUpMod.LOGGER.warn(
|
||||
"[PacketDisciplineAction] Unknown discipline type: {}",
|
||||
disciplineTypeName
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Only allow verbal discipline from this packet (Praise, Scold, Threaten)
|
||||
if (!type.isVerbal()) {
|
||||
TiedUpMod.LOGGER.warn(
|
||||
"[PacketDisciplineAction] Non-verbal discipline {} attempted via packet",
|
||||
type.name()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Show appropriate dialogue
|
||||
DialogueCategory category = switch (type) {
|
||||
case PRAISE -> DialogueCategory.PRAISE_RESPONSE;
|
||||
case SCOLD -> DialogueCategory.SCOLD_RESPONSE;
|
||||
case THREATEN -> DialogueCategory.THREATEN_RESPONSE;
|
||||
default -> DialogueCategory.SCOLD_RESPONSE;
|
||||
};
|
||||
EntityDialogueManager.talkTo(damsel, sender, category);
|
||||
|
||||
// Send feedback
|
||||
String npcName = damsel.getNpcName();
|
||||
String actionDesc = switch (type) {
|
||||
case PRAISE -> "praised";
|
||||
case SCOLD -> "scolded";
|
||||
case THREATEN -> "threatened";
|
||||
default -> "disciplined";
|
||||
};
|
||||
|
||||
SystemMessageManager.sendToPlayer(
|
||||
sender,
|
||||
SystemMessageManager.MessageCategory.INFO,
|
||||
"You " + actionDesc + " " + npcName + "."
|
||||
);
|
||||
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[PacketDisciplineAction] {} {} {}",
|
||||
sender.getName().getString(),
|
||||
actionDesc,
|
||||
npcName
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
package com.tiedup.remake.network.personality;
|
||||
|
||||
import com.tiedup.remake.core.SystemMessageManager;
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.dialogue.EntityDialogueManager;
|
||||
import com.tiedup.remake.dialogue.EntityDialogueManager.DialogueCategory;
|
||||
import com.tiedup.remake.entities.EntityDamsel;
|
||||
import com.tiedup.remake.items.ItemCommandWand;
|
||||
import com.tiedup.remake.items.ModItems;
|
||||
import com.tiedup.remake.items.base.ItemCollar;
|
||||
import com.tiedup.remake.network.ModNetwork;
|
||||
import com.tiedup.remake.network.PacketRateLimiter;
|
||||
import com.tiedup.remake.personality.JobExperience;
|
||||
import com.tiedup.remake.personality.NpcCommand;
|
||||
import com.tiedup.remake.personality.NpcNeeds;
|
||||
import com.tiedup.remake.personality.PersonalityState;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.InteractionHand;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
/**
|
||||
* Unified C2S packet for all NPC command wand actions.
|
||||
* Replaces PacketGiveCommand, PacketCancelCommand, PacketSelectJobCommand,
|
||||
* PacketCycleFollowDistance, and PacketToggleAutoRest.
|
||||
*/
|
||||
public class PacketNpcCommand {
|
||||
|
||||
private static final double MAX_COMMAND_RANGE = 32.0;
|
||||
|
||||
private final UUID entityUUID;
|
||||
private final NpcCommandType type;
|
||||
|
||||
// Optional fields depending on type
|
||||
private final String commandName; // GIVE_COMMAND, SELECT_JOB
|
||||
@Nullable
|
||||
private final BlockPos targetPos; // GIVE_COMMAND only
|
||||
private final boolean refreshScreen; // CYCLE_FOLLOW_DISTANCE, TOGGLE_AUTO_REST
|
||||
|
||||
// --- Constructors for each command type ---
|
||||
|
||||
public static PacketNpcCommand giveCommand(UUID entityUUID, NpcCommand command, @Nullable BlockPos targetPos) {
|
||||
return new PacketNpcCommand(entityUUID, NpcCommandType.GIVE_COMMAND, command.name(), targetPos, false);
|
||||
}
|
||||
|
||||
public static PacketNpcCommand cancelCommand(UUID entityUUID) {
|
||||
return new PacketNpcCommand(entityUUID, NpcCommandType.CANCEL_COMMAND, "", null, false);
|
||||
}
|
||||
|
||||
public static PacketNpcCommand selectJob(UUID entityUUID, NpcCommand job) {
|
||||
return new PacketNpcCommand(entityUUID, NpcCommandType.SELECT_JOB, job.name(), null, false);
|
||||
}
|
||||
|
||||
public static PacketNpcCommand cycleFollowDistance(UUID entityUUID, boolean refreshScreen) {
|
||||
return new PacketNpcCommand(entityUUID, NpcCommandType.CYCLE_FOLLOW_DISTANCE, "", null, refreshScreen);
|
||||
}
|
||||
|
||||
public static PacketNpcCommand toggleAutoRest(UUID entityUUID, boolean refreshScreen) {
|
||||
return new PacketNpcCommand(entityUUID, NpcCommandType.TOGGLE_AUTO_REST, "", null, refreshScreen);
|
||||
}
|
||||
|
||||
private PacketNpcCommand(UUID entityUUID, NpcCommandType type, String commandName,
|
||||
@Nullable BlockPos targetPos, boolean refreshScreen) {
|
||||
this.entityUUID = entityUUID;
|
||||
this.type = type;
|
||||
this.commandName = commandName;
|
||||
this.targetPos = targetPos;
|
||||
this.refreshScreen = refreshScreen;
|
||||
}
|
||||
|
||||
// --- Wire format ---
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(entityUUID);
|
||||
buf.writeEnum(type);
|
||||
switch (type) {
|
||||
case GIVE_COMMAND -> {
|
||||
buf.writeUtf(commandName);
|
||||
buf.writeBoolean(targetPos != null);
|
||||
if (targetPos != null) {
|
||||
buf.writeBlockPos(targetPos);
|
||||
}
|
||||
}
|
||||
case SELECT_JOB -> buf.writeUtf(commandName);
|
||||
case CYCLE_FOLLOW_DISTANCE, TOGGLE_AUTO_REST -> buf.writeBoolean(refreshScreen);
|
||||
case CANCEL_COMMAND -> {} // no extra data
|
||||
}
|
||||
}
|
||||
|
||||
public static PacketNpcCommand decode(FriendlyByteBuf buf) {
|
||||
UUID uuid = buf.readUUID();
|
||||
NpcCommandType type = buf.readEnum(NpcCommandType.class);
|
||||
String cmd = "";
|
||||
BlockPos pos = null;
|
||||
boolean refresh = false;
|
||||
|
||||
switch (type) {
|
||||
case GIVE_COMMAND -> {
|
||||
cmd = buf.readUtf(32);
|
||||
if (buf.readBoolean()) {
|
||||
pos = buf.readBlockPos();
|
||||
}
|
||||
}
|
||||
case SELECT_JOB -> cmd = buf.readUtf(32);
|
||||
case CYCLE_FOLLOW_DISTANCE, TOGGLE_AUTO_REST -> refresh = buf.readBoolean();
|
||||
case CANCEL_COMMAND -> {}
|
||||
}
|
||||
|
||||
return new PacketNpcCommand(uuid, type, cmd, pos, refresh);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx.get().enqueueWork(() -> {
|
||||
ServerPlayer sender = ctx.get().getSender();
|
||||
if (sender == null) return;
|
||||
handleServer(sender);
|
||||
});
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
|
||||
// --- Server handling ---
|
||||
|
||||
private void handleServer(ServerPlayer sender) {
|
||||
if (!PacketRateLimiter.allowPacket(sender, "action")) return;
|
||||
|
||||
Entity entity = ((net.minecraft.server.level.ServerLevel) sender.level()).getEntity(entityUUID);
|
||||
if (!(entity instanceof EntityDamsel damsel)) {
|
||||
TiedUpMod.LOGGER.warn("[PacketNpcCommand:{}] Entity {} not found or not a Damsel",
|
||||
type, entityUUID);
|
||||
return;
|
||||
}
|
||||
|
||||
double distance = sender.distanceTo(damsel);
|
||||
if (distance > MAX_COMMAND_RANGE) {
|
||||
SystemMessageManager.sendToPlayer(sender,
|
||||
SystemMessageManager.MessageCategory.ERROR, "Target is too far away!");
|
||||
return;
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case GIVE_COMMAND -> handleGiveCommand(sender, damsel);
|
||||
case CANCEL_COMMAND -> handleCancelCommand(sender, damsel);
|
||||
case SELECT_JOB -> handleSelectJob(sender, damsel);
|
||||
case CYCLE_FOLLOW_DISTANCE -> handleCycleFollowDistance(sender, damsel);
|
||||
case TOGGLE_AUTO_REST -> handleToggleAutoRest(sender, damsel);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleGiveCommand(ServerPlayer sender, EntityDamsel damsel) {
|
||||
NpcCommand command = NpcCommand.fromString(commandName);
|
||||
if (command == NpcCommand.NONE && !"NONE".equals(commandName)) {
|
||||
TiedUpMod.LOGGER.warn("[PacketNpcCommand:GIVE] Unknown command: {}", commandName);
|
||||
return;
|
||||
}
|
||||
|
||||
boolean success = damsel.giveCommand(sender, command, targetPos);
|
||||
String npcName = damsel.getNpcName();
|
||||
if (success) {
|
||||
EntityDialogueManager.talkTo(damsel, sender, DialogueCategory.COMMAND_ACCEPT);
|
||||
SystemMessageManager.sendToPlayer(sender,
|
||||
SystemMessageManager.MessageCategory.INFO,
|
||||
npcName + " accepted command: " + command.name());
|
||||
TiedUpMod.LOGGER.debug("[PacketNpcCommand:GIVE] {} gave command {} to {}",
|
||||
sender.getName().getString(), command.name(), npcName);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCancelCommand(ServerPlayer sender, EntityDamsel damsel) {
|
||||
if (!validateCollarOwnership(sender, damsel)) return;
|
||||
|
||||
damsel.cancelCommand();
|
||||
String npcName = damsel.getNpcName();
|
||||
SystemMessageManager.sendToPlayer(sender,
|
||||
SystemMessageManager.MessageCategory.INFO, npcName + "'s command cancelled.");
|
||||
TiedUpMod.LOGGER.debug("[PacketNpcCommand:CANCEL] {} cancelled command for {}",
|
||||
sender.getName().getString(), npcName);
|
||||
}
|
||||
|
||||
private void handleSelectJob(ServerPlayer sender, EntityDamsel damsel) {
|
||||
NpcCommand job = NpcCommand.fromString(commandName);
|
||||
if (!job.isWorkCommand()) {
|
||||
TiedUpMod.LOGGER.warn("[PacketNpcCommand:JOB] {} is not a work command", commandName);
|
||||
return;
|
||||
}
|
||||
|
||||
PersonalityState state = damsel.getPersonalityState();
|
||||
if (state == null) return;
|
||||
|
||||
String npcName = damsel.getNpcName();
|
||||
if (state.willObeyCommand(sender, job)) {
|
||||
ItemStack mainHand = sender.getItemInHand(InteractionHand.MAIN_HAND);
|
||||
ItemStack offHand = sender.getItemInHand(InteractionHand.OFF_HAND);
|
||||
|
||||
ItemStack wand = null;
|
||||
if (mainHand.getItem() == ModItems.COMMAND_WAND.get()) {
|
||||
wand = mainHand;
|
||||
} else if (offHand.getItem() == ModItems.COMMAND_WAND.get()) {
|
||||
wand = offHand;
|
||||
}
|
||||
|
||||
if (wand != null) {
|
||||
ItemCommandWand.enterSelectionMode(wand, damsel.getUUID(), job);
|
||||
EntityDialogueManager.talkTo(damsel, sender, DialogueCategory.COMMAND_ACCEPT);
|
||||
SystemMessageManager.sendToPlayer(sender,
|
||||
SystemMessageManager.MessageCategory.INFO,
|
||||
npcName + " is ready to " + job.name() + ". Click a chest to set work zone!");
|
||||
TiedUpMod.LOGGER.debug("[PacketNpcCommand:JOB] {} accepted job {} - wand in selection mode",
|
||||
npcName, job.name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void handleCycleFollowDistance(ServerPlayer sender, EntityDamsel damsel) {
|
||||
if (!validateCollarOwnership(sender, damsel)) return;
|
||||
|
||||
PersonalityState state = damsel.getPersonalityState();
|
||||
if (state == null) return;
|
||||
|
||||
NpcCommand.FollowDistance current = state.getFollowDistance();
|
||||
NpcCommand.FollowDistance next = switch (current) {
|
||||
case FAR -> NpcCommand.FollowDistance.CLOSE;
|
||||
case CLOSE -> NpcCommand.FollowDistance.HEEL;
|
||||
case HEEL -> NpcCommand.FollowDistance.FAR;
|
||||
};
|
||||
state.setFollowDistance(next);
|
||||
|
||||
TiedUpMod.LOGGER.debug("[PacketNpcCommand:FOLLOW] {} changed {} follow distance to {}",
|
||||
sender.getName().getString(), damsel.getNpcName(), next.name());
|
||||
|
||||
if (refreshScreen) {
|
||||
sendRefreshedScreen(sender, damsel, state);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleToggleAutoRest(ServerPlayer sender, EntityDamsel damsel) {
|
||||
if (!validateCollarOwnership(sender, damsel)) return;
|
||||
|
||||
PersonalityState state = damsel.getPersonalityState();
|
||||
if (state == null) return;
|
||||
|
||||
boolean newState = state.toggleAutoRest();
|
||||
TiedUpMod.LOGGER.debug("[PacketNpcCommand:REST] {} toggled {} auto-rest to {}",
|
||||
sender.getName().getString(), damsel.getNpcName(), newState ? "ON" : "OFF");
|
||||
SystemMessageManager.sendToPlayer(sender,
|
||||
SystemMessageManager.MessageCategory.INFO,
|
||||
"Auto-Rest: " + (newState ? "ON" : "OFF"));
|
||||
|
||||
if (refreshScreen) {
|
||||
sendRefreshedScreen(sender, damsel, state);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Shared helpers ---
|
||||
|
||||
private boolean validateCollarOwnership(ServerPlayer sender, EntityDamsel damsel) {
|
||||
if (!damsel.hasCollar()) {
|
||||
SystemMessageManager.sendToPlayer(sender,
|
||||
SystemMessageManager.MessageCategory.ERROR,
|
||||
damsel.getNpcName() + " is not wearing a collar!");
|
||||
return false;
|
||||
}
|
||||
|
||||
ItemStack collar = damsel.getEquipment(BodyRegionV2.NECK);
|
||||
if (!(collar.getItem() instanceof ItemCollar collarItem)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!collarItem.getOwners(collar).contains(sender.getUUID())) {
|
||||
SystemMessageManager.sendToPlayer(sender,
|
||||
SystemMessageManager.MessageCategory.ERROR,
|
||||
"You don't own " + damsel.getNpcName() + "'s collar!");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void sendRefreshedScreen(ServerPlayer sender, EntityDamsel damsel,
|
||||
PersonalityState state) {
|
||||
NpcNeeds needs = state.getNeeds();
|
||||
String homeType = state.getHomeType().name();
|
||||
|
||||
JobExperience jobExp = state.getJobExperience();
|
||||
NpcCommand activeCmd = state.getActiveCommand();
|
||||
String activeJobLevelName = "";
|
||||
int activeJobXp = 0;
|
||||
int activeJobXpMax = 10;
|
||||
if (activeCmd.isActiveJob()) {
|
||||
JobExperience.JobLevel level = jobExp.getJobLevel(activeCmd);
|
||||
activeJobLevelName = level.name();
|
||||
activeJobXp = jobExp.getExperience(activeCmd);
|
||||
activeJobXpMax = level.maxExp;
|
||||
}
|
||||
|
||||
ModNetwork.sendToPlayer(
|
||||
new PacketOpenCommandWandScreen(
|
||||
damsel.getUUID(), damsel.getNpcName(),
|
||||
state.getPersonality().name(), activeCmd.name(),
|
||||
needs.getHunger(), needs.getRest(), state.getMood(),
|
||||
state.getFollowDistance().name(), homeType,
|
||||
state.isAutoRestEnabled(), "", "",
|
||||
activeJobLevelName, activeJobXp, activeJobXpMax),
|
||||
sender);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
package com.tiedup.remake.network.personality;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
/**
|
||||
* Packet sent from server to client to open the Command Wand screen.
|
||||
* Contains all personality data needed for the GUI.
|
||||
*
|
||||
* Simplified: removed discovery, fear, relationship, secondaryTrait fields.
|
||||
*/
|
||||
public class PacketOpenCommandWandScreen {
|
||||
|
||||
private final java.util.UUID entityUUID;
|
||||
private final String npcName;
|
||||
private final String personalityTypeName;
|
||||
private final String activeCommandName;
|
||||
private final float hunger;
|
||||
private final float rest;
|
||||
private final float mood;
|
||||
private final String followDistanceMode;
|
||||
private final String homeType;
|
||||
private final boolean autoRestEnabled;
|
||||
private final String cellName;
|
||||
private final String cellQualityName;
|
||||
private final String activeJobLevelName;
|
||||
private final int activeJobXp;
|
||||
private final int activeJobXpMax;
|
||||
|
||||
/**
|
||||
* Constructor without cell info or job experience (defaults to "").
|
||||
*/
|
||||
public PacketOpenCommandWandScreen(
|
||||
java.util.UUID entityUUID,
|
||||
String npcName,
|
||||
String personalityTypeName,
|
||||
String activeCommandName,
|
||||
float hunger,
|
||||
float rest,
|
||||
float mood,
|
||||
String followDistanceMode,
|
||||
String homeType,
|
||||
boolean autoRestEnabled
|
||||
) {
|
||||
this(
|
||||
entityUUID,
|
||||
npcName,
|
||||
personalityTypeName,
|
||||
activeCommandName,
|
||||
hunger,
|
||||
rest,
|
||||
mood,
|
||||
followDistanceMode,
|
||||
homeType,
|
||||
autoRestEnabled,
|
||||
"",
|
||||
"",
|
||||
"",
|
||||
0,
|
||||
10
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor with cell info but no job experience.
|
||||
*/
|
||||
public PacketOpenCommandWandScreen(
|
||||
java.util.UUID entityUUID,
|
||||
String npcName,
|
||||
String personalityTypeName,
|
||||
String activeCommandName,
|
||||
float hunger,
|
||||
float rest,
|
||||
float mood,
|
||||
String followDistanceMode,
|
||||
String homeType,
|
||||
boolean autoRestEnabled,
|
||||
String cellName,
|
||||
String cellQualityName
|
||||
) {
|
||||
this(
|
||||
entityUUID,
|
||||
npcName,
|
||||
personalityTypeName,
|
||||
activeCommandName,
|
||||
hunger,
|
||||
rest,
|
||||
mood,
|
||||
followDistanceMode,
|
||||
homeType,
|
||||
autoRestEnabled,
|
||||
cellName,
|
||||
cellQualityName,
|
||||
"",
|
||||
0,
|
||||
10
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Full constructor with cell info and job experience.
|
||||
*/
|
||||
public PacketOpenCommandWandScreen(
|
||||
java.util.UUID entityUUID,
|
||||
String npcName,
|
||||
String personalityTypeName,
|
||||
String activeCommandName,
|
||||
float hunger,
|
||||
float rest,
|
||||
float mood,
|
||||
String followDistanceMode,
|
||||
String homeType,
|
||||
boolean autoRestEnabled,
|
||||
String cellName,
|
||||
String cellQualityName,
|
||||
String activeJobLevelName,
|
||||
int activeJobXp,
|
||||
int activeJobXpMax
|
||||
) {
|
||||
this.entityUUID = entityUUID;
|
||||
this.npcName = npcName;
|
||||
this.personalityTypeName = personalityTypeName;
|
||||
this.activeCommandName = activeCommandName;
|
||||
this.hunger = hunger;
|
||||
this.rest = rest;
|
||||
this.mood = mood;
|
||||
this.followDistanceMode = followDistanceMode;
|
||||
this.homeType = homeType;
|
||||
this.autoRestEnabled = autoRestEnabled;
|
||||
this.cellName = cellName;
|
||||
this.cellQualityName = cellQualityName;
|
||||
this.activeJobLevelName = activeJobLevelName;
|
||||
this.activeJobXp = activeJobXp;
|
||||
this.activeJobXpMax = activeJobXpMax;
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(entityUUID);
|
||||
buf.writeUtf(npcName, 64);
|
||||
buf.writeUtf(personalityTypeName, 32);
|
||||
buf.writeUtf(activeCommandName, 32);
|
||||
buf.writeFloat(hunger);
|
||||
buf.writeFloat(rest);
|
||||
buf.writeFloat(mood);
|
||||
buf.writeUtf(followDistanceMode, 16);
|
||||
buf.writeUtf(homeType, 16);
|
||||
buf.writeBoolean(autoRestEnabled);
|
||||
buf.writeUtf(cellName, 64);
|
||||
buf.writeUtf(cellQualityName, 16);
|
||||
buf.writeUtf(activeJobLevelName, 32);
|
||||
buf.writeInt(activeJobXp);
|
||||
buf.writeInt(activeJobXpMax);
|
||||
}
|
||||
|
||||
public static PacketOpenCommandWandScreen decode(FriendlyByteBuf buf) {
|
||||
return new PacketOpenCommandWandScreen(
|
||||
buf.readUUID(),
|
||||
buf.readUtf(64), // npcName
|
||||
buf.readUtf(32), // personalityTypeName
|
||||
buf.readUtf(32), // activeCommandName
|
||||
buf.readFloat(), // hunger
|
||||
buf.readFloat(), // rest
|
||||
buf.readFloat(), // mood
|
||||
buf.readUtf(16), // followDistanceMode
|
||||
buf.readUtf(16), // homeType
|
||||
buf.readBoolean(), // autoRestEnabled
|
||||
buf.readUtf(64), // cellName
|
||||
buf.readUtf(16), // cellQualityName
|
||||
buf.readUtf(32), // activeJobLevelName
|
||||
buf.readInt(), // activeJobXp
|
||||
buf.readInt() // activeJobXpMax
|
||||
);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx
|
||||
.get()
|
||||
.enqueueWork(() -> {
|
||||
if (FMLEnvironment.dist == Dist.CLIENT) {
|
||||
handleClient();
|
||||
}
|
||||
});
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
private void handleClient() {
|
||||
net.minecraft.client.Minecraft mc =
|
||||
net.minecraft.client.Minecraft.getInstance();
|
||||
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[PacketOpenCommandWandScreen] Opening screen for {} (entity {})",
|
||||
npcName,
|
||||
entityUUID
|
||||
);
|
||||
|
||||
mc.setScreen(
|
||||
new com.tiedup.remake.client.gui.screens.CommandWandScreen(
|
||||
entityUUID,
|
||||
npcName,
|
||||
personalityTypeName,
|
||||
activeCommandName,
|
||||
hunger,
|
||||
rest,
|
||||
mood,
|
||||
followDistanceMode,
|
||||
homeType,
|
||||
autoRestEnabled,
|
||||
cellName,
|
||||
cellQualityName,
|
||||
activeJobLevelName,
|
||||
activeJobXp,
|
||||
activeJobXpMax
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// --- Getters for GUI use ---
|
||||
|
||||
public java.util.UUID getEntityUUID() {
|
||||
return entityUUID;
|
||||
}
|
||||
|
||||
public String getNpcName() {
|
||||
return npcName;
|
||||
}
|
||||
|
||||
public String getPersonalityTypeName() {
|
||||
return personalityTypeName;
|
||||
}
|
||||
|
||||
public String getActiveCommandName() {
|
||||
return activeCommandName;
|
||||
}
|
||||
|
||||
public float getHunger() {
|
||||
return hunger;
|
||||
}
|
||||
|
||||
public float getRest() {
|
||||
return rest;
|
||||
}
|
||||
|
||||
public float getMood() {
|
||||
return mood;
|
||||
}
|
||||
|
||||
public String getFollowDistanceMode() {
|
||||
return followDistanceMode;
|
||||
}
|
||||
|
||||
public String getHomeType() {
|
||||
return homeType;
|
||||
}
|
||||
|
||||
public boolean isAutoRestEnabled() {
|
||||
return autoRestEnabled;
|
||||
}
|
||||
|
||||
public String getCellName() {
|
||||
return cellName;
|
||||
}
|
||||
|
||||
public String getCellQualityName() {
|
||||
return cellQualityName;
|
||||
}
|
||||
|
||||
public String getActiveJobLevelName() {
|
||||
return activeJobLevelName;
|
||||
}
|
||||
|
||||
public int getActiveJobXp() {
|
||||
return activeJobXp;
|
||||
}
|
||||
|
||||
public int getActiveJobXpMax() {
|
||||
return activeJobXpMax;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package com.tiedup.remake.network.personality;
|
||||
|
||||
import com.tiedup.remake.core.TiedUpMod;
|
||||
import com.tiedup.remake.v2.BodyRegionV2;
|
||||
import com.tiedup.remake.entities.EntityDamsel;
|
||||
import com.tiedup.remake.network.PacketRateLimiter;
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.server.level.ServerPlayer;
|
||||
import net.minecraft.world.entity.Entity;
|
||||
import net.minecraft.world.item.ItemStack;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
/**
|
||||
* Packet sent from client to server to request opening the NPC inventory screen.
|
||||
* Server responds with PacketOpenNpcInventory if validation passes.
|
||||
*
|
||||
* Personality System Phase J: NPC Inventory
|
||||
*/
|
||||
public class PacketRequestNpcInventory {
|
||||
|
||||
private final java.util.UUID entityUUID; // HIGH FIX: use UUID for persistence
|
||||
|
||||
/**
|
||||
* Create a request packet.
|
||||
*
|
||||
* @param entityUUID The NPC entity UUID
|
||||
*/
|
||||
public PacketRequestNpcInventory(java.util.UUID entityUUID) {
|
||||
this.entityUUID = entityUUID;
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeUUID(entityUUID); // HIGH FIX
|
||||
}
|
||||
|
||||
public static PacketRequestNpcInventory decode(FriendlyByteBuf buf) {
|
||||
return new PacketRequestNpcInventory(buf.readUUID());
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx
|
||||
.get()
|
||||
.enqueueWork(() -> {
|
||||
ServerPlayer player = ctx.get().getSender();
|
||||
if (player == null) return;
|
||||
if (!PacketRateLimiter.allowPacket(player, "ui")) return;
|
||||
|
||||
// HIGH FIX: lookup by UUID
|
||||
Entity entity = (
|
||||
(net.minecraft.server.level.ServerLevel) player.level()
|
||||
).getEntity(entityUUID);
|
||||
if (!(entity instanceof EntityDamsel damsel)) {
|
||||
TiedUpMod.LOGGER.warn(
|
||||
"[PacketRequestNpcInventory] Entity {} is not a damsel",
|
||||
entityUUID
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify player is nearby
|
||||
if (player.distanceTo(damsel) > 6.0) {
|
||||
TiedUpMod.LOGGER.warn(
|
||||
"[PacketRequestNpcInventory] Player {} too far from NPC",
|
||||
player.getName().getString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// Verify player is owner of collar (or NPC has no collar)
|
||||
if (damsel.hasCollar()) {
|
||||
ItemStack collar = damsel.getEquipment(BodyRegionV2.NECK);
|
||||
if (
|
||||
collar.getItem() instanceof
|
||||
com.tiedup.remake.items.base.ItemCollar collarItem
|
||||
) {
|
||||
if (!collarItem.isOwner(collar, player)) {
|
||||
TiedUpMod.LOGGER.warn(
|
||||
"[PacketRequestNpcInventory] Player {} is not owner of NPC collar",
|
||||
player.getName().getString()
|
||||
);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Open vanilla container via NetworkHooks
|
||||
TiedUpMod.LOGGER.debug(
|
||||
"[PacketRequestNpcInventory] Opening inventory for {} to {}",
|
||||
damsel.getNpcName(),
|
||||
player.getName().getString()
|
||||
);
|
||||
|
||||
net.minecraftforge.network.NetworkHooks.openScreen(
|
||||
player,
|
||||
damsel,
|
||||
buf -> {
|
||||
buf.writeInt(damsel.getId());
|
||||
buf.writeInt(damsel.getNpcInventorySize());
|
||||
buf.writeUtf(damsel.getNpcName(), 64);
|
||||
}
|
||||
);
|
||||
});
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.tiedup.remake.network.personality;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
import net.minecraft.ChatFormatting;
|
||||
import net.minecraft.network.FriendlyByteBuf;
|
||||
import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.sounds.SoundEvents;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
import net.minecraftforge.fml.loading.FMLEnvironment;
|
||||
import net.minecraftforge.network.NetworkEvent;
|
||||
|
||||
/**
|
||||
* Packet sent from server to client to alert an owner that someone
|
||||
* is trying to free their slave.
|
||||
*
|
||||
* Phase 11: Multiplayer protection system
|
||||
*/
|
||||
public class PacketSlaveBeingFreed {
|
||||
|
||||
private final String slaveName;
|
||||
private final String liberatorName;
|
||||
private final int x;
|
||||
private final int y;
|
||||
private final int z;
|
||||
|
||||
/**
|
||||
* Create alert packet.
|
||||
*
|
||||
* @param slaveName Name of the slave being freed
|
||||
* @param liberatorName Name of the player trying to free them
|
||||
* @param x X coordinate
|
||||
* @param y Y coordinate
|
||||
* @param z Z coordinate
|
||||
*/
|
||||
public PacketSlaveBeingFreed(
|
||||
String slaveName,
|
||||
String liberatorName,
|
||||
int x,
|
||||
int y,
|
||||
int z
|
||||
) {
|
||||
this.slaveName = slaveName;
|
||||
this.liberatorName = liberatorName;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
this.z = z;
|
||||
}
|
||||
|
||||
public void encode(FriendlyByteBuf buf) {
|
||||
buf.writeUtf(slaveName, 64);
|
||||
buf.writeUtf(liberatorName, 64);
|
||||
buf.writeInt(x);
|
||||
buf.writeInt(y);
|
||||
buf.writeInt(z);
|
||||
}
|
||||
|
||||
public static PacketSlaveBeingFreed decode(FriendlyByteBuf buf) {
|
||||
return new PacketSlaveBeingFreed(
|
||||
buf.readUtf(64),
|
||||
buf.readUtf(64),
|
||||
buf.readInt(),
|
||||
buf.readInt(),
|
||||
buf.readInt()
|
||||
);
|
||||
}
|
||||
|
||||
public void handle(Supplier<NetworkEvent.Context> ctx) {
|
||||
ctx
|
||||
.get()
|
||||
.enqueueWork(() -> {
|
||||
if (FMLEnvironment.dist == Dist.CLIENT) {
|
||||
handleClient();
|
||||
}
|
||||
});
|
||||
ctx.get().setPacketHandled(true);
|
||||
}
|
||||
|
||||
@OnlyIn(Dist.CLIENT)
|
||||
private void handleClient() {
|
||||
net.minecraft.client.Minecraft mc =
|
||||
net.minecraft.client.Minecraft.getInstance();
|
||||
|
||||
if (mc.player == null) return;
|
||||
|
||||
// Build alert message
|
||||
Component message = Component.literal("")
|
||||
.append(
|
||||
Component.literal("[WARNING] ").withStyle(
|
||||
ChatFormatting.RED,
|
||||
ChatFormatting.BOLD
|
||||
)
|
||||
)
|
||||
.append(
|
||||
Component.literal(liberatorName).withStyle(
|
||||
ChatFormatting.YELLOW
|
||||
)
|
||||
)
|
||||
.append(
|
||||
Component.literal(" is trying to free ").withStyle(
|
||||
ChatFormatting.RED
|
||||
)
|
||||
)
|
||||
.append(
|
||||
Component.literal(slaveName).withStyle(ChatFormatting.YELLOW)
|
||||
)
|
||||
.append(Component.literal(" at ").withStyle(ChatFormatting.RED))
|
||||
.append(
|
||||
Component.literal(
|
||||
"[" + x + ", " + y + ", " + z + "]"
|
||||
).withStyle(ChatFormatting.AQUA)
|
||||
)
|
||||
.append(Component.literal("!").withStyle(ChatFormatting.RED));
|
||||
|
||||
// Send to player chat
|
||||
mc.player.displayClientMessage(message, false);
|
||||
|
||||
// Play warning sound
|
||||
mc.player.playSound(SoundEvents.BELL_BLOCK, 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
// --- Getters ---
|
||||
|
||||
public String getSlaveName() {
|
||||
return slaveName;
|
||||
}
|
||||
|
||||
public String getLiberatorName() {
|
||||
return liberatorName;
|
||||
}
|
||||
|
||||
public int getX() {
|
||||
return x;
|
||||
}
|
||||
|
||||
public int getY() {
|
||||
return y;
|
||||
}
|
||||
|
||||
public int getZ() {
|
||||
return z;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user