Implement Profile Regions

This commit is contained in:
Kam committed 2025-02-19 18:15:05 -05:00
1 parent db330d0038
commit cf6e6dd655
14 files changed
+486 -283

No files matched your search

@@ -2,72 +2,90 @@ package kamkeel.npcs.command.profile;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.util.ChatComponentTranslation;
import java.lang.reflect.Method;
import java.util.Map.Entry;
import static kamkeel.npcs.util.ColorUtil.sendError;
import static kamkeel.npcs.util.ColorUtil.sendMessage;
import noppes.npcs.CustomNpcsPermissions;
public class CommandHelpProfile extends CommandProfileBase {
private CommandProfile parent;
private CommandProfile parent;
public CommandHelpProfile(CommandProfile parent){
this.parent = parent;
}
public CommandHelpProfile(CommandProfile parent){
this.parent = parent;
}
@Override
public String getCommandName() {
return "help";
}
@Override
public String getCommandName() {
return "help";
}
@Override
public String getDescription() {
return "help [command]";
}
@Override
public String getDescription() {
return "help [command]";
}
@Override
public void processCommand(ICommandSender sender, String[] args) throws CommandException {
if(args.length == 0){
sendMessage(sender, "\u00A78------ \u00A7aProfile Commands \u00A78------");
for(Entry<String, CommandProfileBase> entry : parent.map.entrySet()){
sendMessage(sender, "\u00A77> " + "\u00A7e" + entry.getKey() + "\u00A78: \u00A77" + entry.getValue().getCommandUsage(sender));
}
return;
}
@Override
public void processCommand(ICommandSender sender, String[] args) throws CommandException {
if(args.length == 0){
sendMessage(sender, "\u00A78------ \u00A7aProfile Commands \u00A78------");
for(Entry<String, CommandProfileBase> entry : parent.map.entrySet()){
sendMessage(sender, "\u00A77> " + "\u00A7e" + entry.getKey() + "\u00A78: \u00A77"
+ entry.getValue().getCommandUsage(sender));
}
return;
}
CommandProfileBase command = parent.getCommand(args);
if(command == null){
sendError(sender, "Unknown command " + args[0]);
return;
}
if(command == null){
sendError(sender, "Unknown command " + args[0]);
return;
}
if(command.subcommands.isEmpty()){
sender.addChatMessage(new ChatComponentTranslation(command.getCommandUsage(sender)));
return;
}
if(command.subcommands.isEmpty()){
sender.addChatMessage(new ChatComponentTranslation(command.getCommandUsage(sender)));
return;
}
Method m = null;
if(args.length > 1){
m = command.subcommands.get(args[1].toLowerCase());
}
if(m == null){
sendMessage(sender, "\u00A78------ \u00A7a" + command.getCommandName().toUpperCase() + " SubCommands \u00A78------");
sendMessage(sender, "\u00A77Usage: \u00A76" + command.getUsage());
for(Entry<String, Method> entry : command.subcommands.entrySet()){
sender.addChatMessage(new ChatComponentTranslation("\u00A77> " + "\u00A7e" + entry.getKey() + "\u00A78: \u00A77" + entry.getValue().getAnnotation(SubCommand.class).desc()));
}
sender.addChatMessage(new ChatComponentTranslation("\u00A78Permission:\u00A77 " + CommandProfile.getCommandPermission(command.getCommandName())));
}
else{
sendMessage(sender, "\u00A78------ \u00A7b" + command.getCommandName().toUpperCase() + "." + args[1].toUpperCase() + " Command \u00A78------");
SubCommand sc = m.getAnnotation(SubCommand.class);
sender.addChatMessage(new ChatComponentTranslation("\u00A77" + sc.desc()));
if(!sc.usage().isEmpty())
sender.addChatMessage(new ChatComponentTranslation("\u00A77Usage: \u00A76" + sc.usage()));
sender.addChatMessage(new ChatComponentTranslation("\u00A78Permission:\u00A77 " + getSubCommandPermission(args[1])));
}
}
Method m = null;
if(args.length > 1){
m = command.subcommands.get(args[1].toLowerCase());
}
if(m == null){
sendMessage(sender, "\u00A78------ \u00A7a" + command.getCommandName().toUpperCase()
+ " SubCommands \u00A78------");
sendMessage(sender, "\u00A77Usage: \u00A76" + command.getUsage());
for(Entry<String, Method> entry : command.subcommands.entrySet()){
SubCommand sc = entry.getValue().getAnnotation(SubCommand.class);
// For players, only list subcommands if they have permission; for console, show all.
if(!(sender instanceof EntityPlayer) || command.canSendCommand(sender, sc, entry.getKey())){
sender.addChatMessage(new ChatComponentTranslation("\u00A77> "
+ "\u00A7e" + entry.getKey()
+ "\u00A78: \u00A77" + sc.desc()));
}
}
// Show permission node only if sender is console or has permission.
if(!(sender instanceof EntityPlayer) ||
CustomNpcsPermissions.hasPermission((EntityPlayer)sender, CustomNpcsPermissions.PROFILE_ADMIN)){
sender.addChatMessage(new ChatComponentTranslation("\u00A78Permission:\u00A77 "
+ CommandProfile.getCommandPermission(command.getCommandName())));
}
}
else{
sendMessage(sender, "\u00A78------ \u00A7b" + command.getCommandName().toUpperCase()
+ "." + args[1].toUpperCase() + " Command \u00A78------");
SubCommand sc = m.getAnnotation(SubCommand.class);
sender.addChatMessage(new ChatComponentTranslation("\u00A77" + sc.desc()));
if(!sc.usage().isEmpty()){
sender.addChatMessage(new ChatComponentTranslation("\u00A77Usage: \u00A76" + sc.usage()));
}
if(!(sender instanceof EntityPlayer) ||
CustomNpcsPermissions.hasPermission((EntityPlayer)sender, CustomNpcsPermissions.PROFILE_ADMIN)){
sender.addChatMessage(new ChatComponentTranslation("\u00A78Permission:\u00A77 "
+ getSubCommandPermission(args[1])));
}
}
}
}
@@ -26,6 +26,7 @@ public class CommandProfile extends CommandBase {
registerCommand(new CommandProfileCreate());
registerCommand(new CommandProfileRemove());
registerCommand(new CommandProfileAdmin());
registerCommand(new CommandProfileRegion());
registerCommand(new CommandProfileList());
registerCommand(new CommandProfileRename());
}
@@ -3,11 +3,11 @@ package kamkeel.npcs.command.profile;
import kamkeel.npcs.controllers.ProfileController;
import kamkeel.npcs.controllers.data.Profile;
import kamkeel.npcs.controllers.data.ProfileOperation;
import kamkeel.npcs.controllers.data.EnumProfileOperation;
import kamkeel.npcs.controllers.data.Slot;
import noppes.npcs.LogWriter;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import java.io.File;
import java.util.Map;
import java.util.UUID;
@@ -51,26 +51,15 @@ public class CommandProfileAdmin extends CommandProfileBase {
}
boolean temporary = (args.length >= 5 && args[4].equalsIgnoreCase("temp"));
ProfileOperation result = ProfileController.cloneSlot(sourcePlayer, sourceSlot, destinationSlot, temporary);
switch(result) {
case SUCCESS:
sendResult(sender, "Successfully cloned slot %d from %s to slot %d for %s.", sourceSlot, sourcePlayer, destinationSlot, destinationPlayer);
break;
case NEW_SLOT_CREATED:
sendResult(sender, "Successfully cloned slot %d from %s to NEW slot %d for %s.", sourceSlot, sourcePlayer, destinationSlot, destinationPlayer);
break;
case LOCKED:
sendError(sender, "Profile is locked. Please try again later.");
break;
case VERIFICATION_FAILED:
sendError(sender, "Profile verification failed. Change not permitted.");
break;
case PLAYER_NOT_FOUND:
sendError(sender, "Player profile not found.");
break;
case ERROR:
default:
sendError(sender, "Error cloning slot.");
break;
if(result.getResult() == EnumProfileOperation.SUCCESS) {
sendResult(sender, "Successfully cloned slot %d from %s to slot %d for %s.", sourceSlot, sourcePlayer, destinationSlot, destinationPlayer);
} else if(result.getResult() == EnumProfileOperation.LOCKED) {
// Detail message for admins – note: using result.getMessage() for further details.
sendError(sender, "Clone failed for %s: Profile is locked. Details: %s", sourcePlayer, result.getMessage());
} else if(result.getResult() == EnumProfileOperation.ERROR) {
sendError(sender, "Error cloning slot for %s: %s", sourcePlayer, result.getMessage());
} else {
sendError(sender, "Unexpected error cloning slot: %s", result.getMessage());
}
}
@@ -92,20 +81,17 @@ public class CommandProfileAdmin extends CommandProfileBase {
return;
}
ProfileOperation result = ProfileController.removeSlot(targetPlayer, slotId);
switch(result) {
case SUCCESS:
sendResult(sender, "Successfully removed slot %d from %s.", slotId, targetPlayer);
break;
case LOCKED:
sendError(sender, "Profile for %s is locked. Please try again later.", targetPlayer);
break;
default:
sendError(sender, "Error removing slot %d from %s.", slotId, targetPlayer);
break;
if(result.getResult() == EnumProfileOperation.SUCCESS) {
sendResult(sender, "Successfully removed slot %d from %s.", slotId, targetPlayer);
} else if(result.getResult() == EnumProfileOperation.LOCKED) {
sendError(sender, "Remove failed for %s: Profile is locked. Details: %s", targetPlayer, result.getMessage());
} else if(result.getResult() == EnumProfileOperation.ERROR) {
sendError(sender, "Error removing slot %d from %s: %s", slotId, targetPlayer, result.getMessage());
} else {
sendError(sender, "Unexpected error during removal: %s", result.getMessage());
}
}
// Existing change subcommand update:
@SubCommand(
desc = "Change a specified player's active slot.",
usage = "<targetPlayer> <newSlotId>"
@@ -124,28 +110,17 @@ public class CommandProfileAdmin extends CommandProfileBase {
return;
}
ProfileOperation result = ProfileController.changeSlot(targetPlayer, newSlotId);
switch(result) {
case SUCCESS:
sendResult(sender, "Successfully changed %s's active slot to %d.", targetPlayer, newSlotId);
break;
case NEW_SLOT_CREATED:
sendResult(sender, "Switched %s to NEW slot %d (new profile created).", targetPlayer, newSlotId);
break;
case ALREADY_ACTIVE:
sendError(sender, "Slot %d is already active for %s.", newSlotId, targetPlayer);
break;
case LOCKED:
sendError(sender, "Profile for %s is locked. Please try again later.", targetPlayer);
break;
case VERIFICATION_FAILED:
sendError(sender, "Profile verification failed for %s.", targetPlayer);
break;
default:
sendError(sender, "Error changing active slot for %s.", targetPlayer);
if(result.getResult() == EnumProfileOperation.SUCCESS) {
sendResult(sender, "Successfully changed %s's active slot to %d.", targetPlayer, newSlotId);
} else if(result.getResult() == EnumProfileOperation.LOCKED) {
sendError(sender, "Change failed for %s: Profile is locked. Details: %s", targetPlayer, result.getMessage());
} else if(result.getResult() == EnumProfileOperation.ERROR) {
sendError(sender, "Error changing active slot for %s: %s", targetPlayer, result.getMessage());
} else {
sendError(sender, "Unexpected error during change: %s", result.getMessage());
}
}
// New admin create subcommand:
@SubCommand(
desc = "Create a new slot for a specified player's profile.",
usage = "<targetPlayer>"
@@ -158,25 +133,18 @@ public class CommandProfileAdmin extends CommandProfileBase {
String targetPlayer = args[0];
Profile profile = ProfileController.getProfile(targetPlayer);
if(profile == null) {
sendError(sender, "Profile not found for player: " + targetPlayer);
sendError(sender, "Profile not found for player: %s", targetPlayer);
return;
}
ProfileOperation result = ProfileController.createSlotInternal(profile);
switch(result) {
case NEW_SLOT_CREATED:
sendResult(sender, "New slot created successfully for %s.", targetPlayer);
break;
case MAX_SLOTS:
sendError(sender, "Player %s has reached maximum allowed slots.", targetPlayer);
break;
case LOCKED:
sendError(sender, "Profile for %s is locked. Please try again later.", targetPlayer);
break;
case VERIFICATION_FAILED:
sendError(sender, "Profile verification failed for %s.", targetPlayer);
break;
default:
sendError(sender, "Error creating new slot for %s.", targetPlayer);
if(result.getResult() == EnumProfileOperation.SUCCESS) {
sendResult(sender, "New slot created successfully for %s.", targetPlayer);
} else if(result.getResult() == EnumProfileOperation.LOCKED) {
sendError(sender, "Create failed for %s: Profile is locked. Details: %s", targetPlayer, result.getMessage());
} else if(result.getResult() == EnumProfileOperation.ERROR) {
sendError(sender, "Error creating new slot for %s: %s", targetPlayer, result.getMessage());
} else {
sendError(sender, "Unexpected error during slot creation: %s", result.getMessage());
}
}
@@ -192,7 +160,7 @@ public class CommandProfileAdmin extends CommandProfileBase {
String targetPlayer = args[0];
Profile profile = ProfileController.getProfile(targetPlayer);
if(profile == null) {
sendError(sender, "Profile not found for player: " + targetPlayer);
sendError(sender, "Profile not found for player: %s", targetPlayer);
return;
}
sendMessage(sender, "Profile Slots for %s:", targetPlayer);
@@ -236,7 +204,7 @@ public class CommandProfileAdmin extends CommandProfileBase {
}
Profile profile = ProfileController.getProfile(targetPlayer);
if(profile == null) {
sendError(sender, "Profile not found for player: " + targetPlayer);
sendError(sender, "Profile not found for player: %s", targetPlayer);
return;
}
if(profile.locked) {
@@ -268,7 +236,7 @@ public class CommandProfileAdmin extends CommandProfileBase {
return;
}
String targetPlayer = args[0];
String backupFileName = args[1] += ".dat";
String backupFileName = args[1] + ".dat";
File backupDir = new File(ProfileController.getBackupDir(), ProfileController.getProfile(targetPlayer).player.getUniqueID().toString());
File backupFile = new File(backupDir, backupFileName);
if(!backupFile.exists()){
@@ -2,11 +2,11 @@ package kamkeel.npcs.command.profile;
import kamkeel.npcs.controllers.ProfileController;
import kamkeel.npcs.controllers.data.ProfileOperation;
import kamkeel.npcs.controllers.data.EnumProfileOperation;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.player.EntityPlayer;
import static kamkeel.npcs.util.ColorUtil.sendError;
import static kamkeel.npcs.util.ColorUtil.sendResult;
import static kamkeel.npcs.util.ColorUtil.*;
public class CommandProfileChange extends CommandProfileBase {
@@ -49,24 +49,14 @@ public class CommandProfileChange extends CommandProfileBase {
}
EntityPlayer player = (EntityPlayer)sender;
ProfileOperation result = ProfileController.changeSlot(player, slotId);
switch(result) {
case SUCCESS:
sendResult(sender, "Successfully changed profile slot to %d.", slotId);
break;
case NEW_SLOT_CREATED:
sendResult(sender, "Switched to new slot %d (new profile created).", slotId);
break;
case ALREADY_ACTIVE:
sendError(sender, "Slot %d is already active.", slotId);
break;
case LOCKED:
sendError(sender, "Profile is locked. Please try again later.");
break;
case VERIFICATION_FAILED:
sendError(sender, "Profile verification failed. Change not permitted.");
break;
default:
sendError(sender, "Error occurred while changing your profile slot.");
if(result.getResult() == EnumProfileOperation.SUCCESS) {
sendResult(sender, "Successfully changed profile slot to %d.", slotId);
} else if(result.getResult() == EnumProfileOperation.LOCKED) {
sendError(sender, "Profile is locked. Details: %s", result.getMessage());
} else if(result.getResult() == EnumProfileOperation.ERROR) {
sendError(sender, "Error changing your profile slot: %s", result.getMessage());
} else {
sendError(sender, "Unexpected error: %s", result.getMessage());
}
}
}
@@ -1,13 +1,12 @@
package kamkeel.npcs.command.profile;
import kamkeel.npcs.controllers.ProfileController;
import kamkeel.npcs.controllers.data.Profile;
import kamkeel.npcs.controllers.data.ProfileOperation;
import kamkeel.npcs.controllers.data.EnumProfileOperation;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.player.EntityPlayer;
import static kamkeel.npcs.util.ColorUtil.sendError;
import static kamkeel.npcs.util.ColorUtil.sendResult;
import static kamkeel.npcs.util.ColorUtil.*;
public class CommandProfileCreate extends CommandProfileBase {
@@ -38,27 +37,19 @@ public class CommandProfileCreate extends CommandProfileBase {
return;
}
EntityPlayer player = (EntityPlayer)sender;
Profile profile = ProfileController.getProfile(player);
if(profile == null) {
if(ProfileController.getProfile(player) == null) {
sendError(sender, "Profile not found.");
return;
}
ProfileOperation result = ProfileController.createSlotInternal(profile);
switch(result) {
case NEW_SLOT_CREATED:
sendResult(sender, "New profile slot created successfully.");
break;
case MAX_SLOTS:
sendError(sender, "You have reached the maximum allowed slots.");
break;
case LOCKED:
sendError(sender, "Profile is locked. Please try again later.");
break;
case VERIFICATION_FAILED:
sendError(sender, "Profile verification failed. Creation not permitted.");
break;
default:
sendError(sender, "Error occurred while creating a new profile slot.");
ProfileOperation result = ProfileController.createSlotInternal(ProfileController.getProfile(player));
if(result.getResult() == EnumProfileOperation.SUCCESS) {
sendResult(sender, "New profile slot created successfully.");
} else if(result.getResult() == EnumProfileOperation.LOCKED) {
sendError(sender, "Profile is locked. Details: %s", result.getMessage());
} else if(result.getResult() == EnumProfileOperation.ERROR) {
sendError(sender, "Error creating new profile slot: %s", result.getMessage());
} else {
sendError(sender, "Unexpected error: %s", result.getMessage());
}
}
}
@@ -0,0 +1,160 @@
package kamkeel.npcs.command.profile;
import noppes.npcs.config.ConfigMain;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import java.util.ArrayList;
import java.util.List;
import static kamkeel.npcs.util.ColorUtil.sendError;
import static kamkeel.npcs.util.ColorUtil.sendMessage;
import static kamkeel.npcs.util.ColorUtil.sendResult;
public class CommandProfileRegion extends CommandProfileBase {
@Override
public String getCommandName() {
return "region";
}
@Override
public String getDescription() {
return "Manage profile region switching and restricted regions";
}
@Override
public String getUsage() {
return "<subcommand>";
}
@SubCommand(
desc = "Enable region-based profile switching",
usage = ""
)
public void enable(ICommandSender sender, String[] args) throws CommandException {
ConfigMain.RegionProfileSwitching = true;
ConfigMain.RegionProfileSwitchingProperty.set(true);
if(ConfigMain.config.hasChanged()){
ConfigMain.config.save();
}
sendResult(sender, "Profile region switching enabled.");
}
@SubCommand(
desc = "Disable region-based profile switching",
usage = ""
)
public void disable(ICommandSender sender, String[] args) throws CommandException {
ConfigMain.RegionProfileSwitching = false;
ConfigMain.RegionProfileSwitchingProperty.set(false);
if(ConfigMain.config.hasChanged()){
ConfigMain.config.save();
}
sendResult(sender, "Profile region switching disabled.");
}
@SubCommand(
desc = "Add a new region. Format: DIM, X1, Y1, Z1, X2, Y2, Z2",
usage = "<dim> <x1> <y1> <z1> <x2> <y2> <z2>"
)
public void add(ICommandSender sender, String[] args) throws CommandException {
if(args.length < 1){
sendError(sender, "Usage: /profile region add <DIM, X1, Y1, Z1, X2, Y2, Z2>");
return;
}
// Combine args in case the region data was split by spaces
String regionStr = String.join(" ", args).trim();
String[] parts = regionStr.split(",");
if(parts.length != 7){
sendError(sender, "Invalid region format. Must contain 7 integers separated by commas.");
return;
}
List<Integer> regionList = new ArrayList<>();
try {
for(String part : parts){
regionList.add(Integer.parseInt(part.trim()));
}
} catch(NumberFormatException e){
sendError(sender, "Invalid number format in region data.");
return;
}
ConfigMain.RestrictedProfileRegions.add(regionList);
// Update config property value
List<String> regionStrings = new ArrayList<>();
for(List<Integer> region : ConfigMain.RestrictedProfileRegions) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < region.size(); i++) {
if(i > 0) sb.append(", ");
sb.append(region.get(i));
}
regionStrings.add(sb.toString());
}
ConfigMain.RestrictedProfileRegionsProperty.set(regionStrings.toArray(new String[0]));
if(ConfigMain.config.hasChanged()){
ConfigMain.config.save();
}
sendResult(sender, "Region added successfully.");
}
@SubCommand(
desc = "Remove a region by its index (1-based).",
usage = "<index>"
)
public void remove(ICommandSender sender, String[] args) throws CommandException {
if(args.length < 1){
sendError(sender, "Usage: /profile region remove <index>");
return;
}
int index;
try {
index = Integer.parseInt(args[0]);
} catch(NumberFormatException e){
sendError(sender, "Index must be a number.");
return;
}
index = index - 1; // Convert to 0-based index
if(index < 0 || index >= ConfigMain.RestrictedProfileRegions.size()){
sendError(sender, "Invalid index. Must be between 1 and " + ConfigMain.RestrictedProfileRegions.size());
return;
}
ConfigMain.RestrictedProfileRegions.remove(index);
// Update config property value
List<String> regionStrings = new ArrayList<>();
for(List<Integer> region : ConfigMain.RestrictedProfileRegions) {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < region.size(); i++) {
if(i > 0) sb.append(", ");
sb.append(region.get(i));
}
regionStrings.add(sb.toString());
}
ConfigMain.RestrictedProfileRegionsProperty.set(regionStrings.toArray(new String[0]));
if(ConfigMain.config.hasChanged()){
ConfigMain.config.save();
}
sendResult(sender, "Region removed successfully.");
}
@SubCommand(
desc = "List all restricted profile regions.",
usage = ""
)
public void list(ICommandSender sender, String[] args) throws CommandException {
if(ConfigMain.RestrictedProfileRegions.isEmpty()){
sendMessage(sender, "No restricted profile regions configured.");
return;
}
sendMessage(sender, "Restricted Profile Regions:");
int i = 1;
for(List<Integer> region : ConfigMain.RestrictedProfileRegions) {
StringBuilder sb = new StringBuilder();
sb.append(i).append(": ");
for (int j = 0; j < region.size(); j++) {
if(j > 0) sb.append(", ");
sb.append(region.get(j));
}
sendMessage(sender, sb.toString());
i++;
}
}
}
@@ -2,11 +2,11 @@ package kamkeel.npcs.command.profile;
import kamkeel.npcs.controllers.ProfileController;
import kamkeel.npcs.controllers.data.ProfileOperation;
import kamkeel.npcs.controllers.data.EnumProfileOperation;
import net.minecraft.command.CommandException;
import net.minecraft.command.ICommandSender;
import net.minecraft.entity.player.EntityPlayer;
import static kamkeel.npcs.util.ColorUtil.sendError;
import static kamkeel.npcs.util.ColorUtil.sendResult;
import static kamkeel.npcs.util.ColorUtil.*;
public class CommandProfileRemove extends CommandProfileBase {
@@ -49,18 +49,14 @@ public class CommandProfileRemove extends CommandProfileBase {
}
EntityPlayer player = (EntityPlayer)sender;
ProfileOperation result = ProfileController.removeSlot(player, slotId);
switch(result) {
case SUCCESS:
sendResult(sender, "Successfully removed slot %d from your profile.", slotId);
break;
case LOCKED:
sendError(sender, "Profile is locked. Please try again later.");
break;
case ERROR:
sendError(sender, "Error removing slot. Ensure the slot exists and is not your active slot.");
break;
default:
sendError(sender, "Operation could not be completed.");
if(result.getResult() == EnumProfileOperation.SUCCESS) {
sendResult(sender, "Successfully removed slot %d from your profile.", slotId);
} else if(result.getResult() == EnumProfileOperation.LOCKED) {
sendError(sender, "Profile is locked. Details: %s", result.getMessage());
} else if(result.getResult() == EnumProfileOperation.ERROR) {
sendError(sender, "Error removing slot: %s", result.getMessage());
} else {
sendError(sender, "Unexpected error: %s", result.getMessage());
}
}
}
@@ -21,8 +21,41 @@ import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.*;
import static noppes.npcs.CustomNpcsPermissions.PROFILE_REGION_BYPASS;
import static noppes.npcs.CustomNpcsPermissions.hasPermission;
public class ProfileController {
// ------------------- Message Constants -------------------
// General messages
private static final String MSG_PLAYER_NOT_FOUND = "Player not found.";
// Clone Slot messages
private static final String MSG_PROFILE_LOCKED_CLONE = "Profile is locked; cannot clone slot.";
private static final String MSG_SOURCE_SLOT_NOT_EXIST = "Source slot does not exist.";
private static final String MSG_CANNOT_CLONE_CURRENT = "Cannot clone to the current active slot.";
private static final String MSG_INVALID_DEST_SLOT = "Invalid destination slot id.";
private static final String MSG_CLONE_SUCCESS = "Slot cloned successfully.";
// Remove Slot messages
private static final String MSG_PROFILE_LOCKED_REMOVE = "Profile is locked; cannot remove slot.";
private static final String MSG_CANNOT_REMOVE_ACTIVE = "Cannot remove the currently active slot.";
private static final String MSG_SLOT_NOT_EXIST = "Slot does not exist.";
private static final String MSG_REMOVE_SUCCESS = "Slot removed successfully.";
// Create Slot messages
private static final String MSG_PROFILE_LOCKED_CREATE = "Profile is locked; cannot create slot.";
private static final String MSG_MAX_SLOTS_REACHED = "Maximum allowed slots reached.";
private static final String MSG_NEW_SLOT_CREATED = "New slot created successfully.";
// Change Slot messages
private static final String MSG_PROFILE_LOCKED_CHANGE = "Profile is locked; cannot change slot.";
private static final String MSG_SLOT_ALREADY_ACTIVE = "Slot is already active.";
private static final String MSG_REGION_NOT_ALLOWED = "Profile switching not allowed from your current location.";
private static final String MSG_CHANGE_SUCCESS = "Slot changed successfully.";
// ------------------- End Constants -------------------
public static HashMap<String, IProfileData> profileTypes;
public static HashMap<UUID, Profile> activeProfiles;
public static String profile_directory = "profiles";
@@ -175,7 +208,6 @@ public class ProfileController {
// Now enforce backup amount limit.
File[] backups = backupDir.listFiles((dir, name) -> name.endsWith(".dat"));
if(backups != null && backups.length > ConfigMain.ProfileBackupAmount) {
// Sort backups by last modified ascending (oldest first)
Arrays.sort(backups, Comparator.comparingLong(File::lastModified));
for(int i = 0; i < backups.length - ConfigMain.ProfileBackupAmount; i++){
backups[i].delete();
@@ -291,7 +323,6 @@ public class ProfileController {
}
// ---------- Internal Slot Operation Helpers ----------
// Returns the next available temporary (negative) slot id (starting at -1)
private static int getNextAvailableTempSlot(Profile profile) {
int id = -1;
while(profile.slots.containsKey(id)) {
@@ -301,120 +332,127 @@ public class ProfileController {
}
// Clone a slot within a Profile.
// If temporary==true, destinationSlotId is computed automatically.
private static ProfileOperation cloneSlotInternal(Profile profile, int sourceSlotId, int destinationSlotId, boolean temporary) {
if(profile.locked) {
LogWriter.error("Profile is locked; cannot clone slot.");
return ProfileOperation.LOCKED;
return ProfileOperation.locked(MSG_PROFILE_LOCKED_CLONE);
}
// Run verification checks on all IProfileData in priority order.
if(profile.player != null) {
List<IProfileData> dataList = new ArrayList<>(profileTypes.values());
dataList.sort(Comparator.comparingInt(IProfileData::getSwitchPriority));
for(IProfileData pd : dataList) {
if(!pd.verifySwitch(profile.player)) {
LogWriter.error("Verification check failed for profile type: " + pd.getTagName());
return ProfileOperation.VERIFICATION_FAILED;
if (pd.verifySwitch(profile.player).getResult() != EnumProfileOperation.SUCCESS) {
return pd.verifySwitch(profile.player);
}
}
}
if(!profile.slots.containsKey(sourceSlotId)) {
LogWriter.error("Source slot " + sourceSlotId + " does not exist.");
return ProfileOperation.ERROR;
return ProfileOperation.error(MSG_SOURCE_SLOT_NOT_EXIST);
}
if(destinationSlotId == profile.currentID) {
LogWriter.error("Cannot clone to the current active slot (" + profile.currentID + ").");
return ProfileOperation.ERROR;
return ProfileOperation.error(MSG_CANNOT_CLONE_CURRENT);
}
if(temporary) {
destinationSlotId = getNextAvailableTempSlot(profile);
} else {
if(destinationSlotId <= 0) {
LogWriter.error("Invalid destination slot id for a non-temporary clone.");
return ProfileOperation.ERROR;
return ProfileOperation.error(MSG_INVALID_DEST_SLOT);
}
}
Slot sourceSlot = profile.slots.get(sourceSlotId);
NBTTagCompound clonedData = (NBTTagCompound) sourceSlot.getCompound().copy();
Slot clonedSlot = new Slot(destinationSlotId, "Cloned Slot " + destinationSlotId, clonedData, System.currentTimeMillis(), temporary);
profile.slots.put(destinationSlotId, clonedSlot);
return ProfileOperation.SUCCESS;
return ProfileOperation.success(MSG_CLONE_SUCCESS);
}
// Remove a slot from a Profile.
private static ProfileOperation removeSlotInternal(Profile profile, int slotId) {
if(profile.locked) {
LogWriter.error("Profile is locked; cannot remove slot.");
return ProfileOperation.LOCKED;
return ProfileOperation.locked(MSG_PROFILE_LOCKED_REMOVE);
}
if(slotId == profile.currentID) {
LogWriter.error("Cannot remove the currently active slot (" + profile.currentID + ").");
return ProfileOperation.ERROR;
return ProfileOperation.error(MSG_CANNOT_REMOVE_ACTIVE);
}
if(!profile.slots.containsKey(slotId)) {
LogWriter.error("Slot " + slotId + " does not exist.");
return ProfileOperation.ERROR;
return ProfileOperation.error(MSG_SLOT_NOT_EXIST);
}
profile.slots.remove(slotId);
return ProfileOperation.SUCCESS;
return ProfileOperation.success(MSG_REMOVE_SUCCESS);
}
public static ProfileOperation createSlotInternal(Profile profile) {
if (profile.locked) {
LogWriter.error("Profile is locked; cannot create slot.");
return ProfileOperation.LOCKED;
return ProfileOperation.locked(MSG_PROFILE_LOCKED_CREATE);
}
int newSlotId = 0;
while (profile.slots.containsKey(newSlotId)) {
newSlotId++;
}
// Check if the player is allowed to create a new slot.
if (!allowSlotPermission(profile.player)) {
LogWriter.error("Player has reached maximum allowed slots.");
return ProfileOperation.MAX_SLOTS;
return ProfileOperation.error(MSG_MAX_SLOTS_REACHED);
}
// Run verification checks from all IProfileData in order of priority.
if (profile.player != null) {
List<IProfileData> dataList = new ArrayList<>(profileTypes.values());
dataList.sort(Comparator.comparingInt(IProfileData::getSwitchPriority));
for (IProfileData pd : dataList) {
if (!pd.verifySwitch(profile.player)) {
LogWriter.error("Verification check failed for profile type: " + pd.getTagName());
return ProfileOperation.VERIFICATION_FAILED;
if (pd.verifySwitch(profile.player).getResult() != EnumProfileOperation.SUCCESS) {
return pd.verifySwitch(profile.player);
}
}
}
// Create and add the new slot.
Slot newSlot = new Slot(newSlotId, "Slot " + newSlotId);
newSlot.setCompound(new NBTTagCompound());
profile.slots.put(newSlotId, newSlot);
return ProfileOperation.NEW_SLOT_CREATED;
return ProfileOperation.success(MSG_NEW_SLOT_CREATED);
}
// Change the active slot in a profile without automatically creating a new one.
// If the target slot does not exist, the operation fails.
private static ProfileOperation changeSlotInternal(Profile profile, int newSlotId) {
if (profile.locked) {
LogWriter.error("Profile is locked; cannot change slot.");
return ProfileOperation.LOCKED;
return ProfileOperation.locked(MSG_PROFILE_LOCKED_CHANGE);
}
if (profile.currentID == newSlotId) {
LogWriter.error("Slot " + newSlotId + " is already active.");
return ProfileOperation.ALREADY_ACTIVE;
return ProfileOperation.error(MSG_SLOT_ALREADY_ACTIVE);
}
if (!profile.slots.containsKey(newSlotId)) {
LogWriter.error("Slot " + newSlotId + " does not exist.");
return ProfileOperation.ERROR;
return ProfileOperation.error(MSG_SLOT_NOT_EXIST);
}
// Region-based check: if enabled, ensure the player is within an allowed region.
if (ConfigMain.RegionProfileSwitching && profile.player != null) {
boolean allowed = hasPermission(profile.player, PROFILE_REGION_BYPASS);
if(!allowed){
int playerDim = profile.player.dimension;
int playerX = (int) profile.player.posX;
int playerY = (int) profile.player.posY;
int playerZ = (int) profile.player.posZ;
for (List<Integer> region : ConfigMain.RestrictedProfileRegions) {
if(region.size() == 7) {
int dim = region.get(0);
int x1 = Math.min(region.get(1), region.get(4));
int y1 = Math.min(region.get(2), region.get(5));
int z1 = Math.min(region.get(3), region.get(6));
int x2 = Math.max(region.get(1), region.get(4));
int y2 = Math.max(region.get(2), region.get(5));
int z2 = Math.max(region.get(3), region.get(6));
if(playerDim == dim &&
playerX >= x1 && playerX <= x2 &&
playerY >= y1 && playerY <= y2 &&
playerZ >= z1 && playerZ <= z2) {
allowed = true;
break;
}
}
}
}
if (!allowed) {
return ProfileOperation.error(MSG_REGION_NOT_ALLOWED);
}
}
// Run verification checks on all IProfileData in priority order.
if (profile.player != null) {
List<IProfileData> dataList = new ArrayList<>(profileTypes.values());
dataList.sort(Comparator.comparingInt(IProfileData::getSwitchPriority));
for (IProfileData pd : dataList) {
if (!pd.verifySwitch(profile.player)) {
LogWriter.error("Verification check failed for profile type: " + pd.getTagName());
return ProfileOperation.VERIFICATION_FAILED;
if (pd.verifySwitch(profile.player).getResult() != EnumProfileOperation.SUCCESS) {
return pd.verifySwitch(profile.player);
}
}
}
@@ -424,31 +462,32 @@ public class ProfileController {
profile.currentID = newSlotId;
if (profile.player != null) {
loadSlotData(profile.player);
}
if (profile.player != null) {
PlayerData pdata = PlayerData.get(profile.player);
pdata.profileSlot = newSlotId;
pdata.save();
}
return ProfileOperation.SUCCESS;
return ProfileOperation.success(MSG_CHANGE_SUCCESS);
}
// ---------- Public Operations (Overloaded for EntityPlayer, UUID, or username) ----------
// ----- Clone Slot -----
public static ProfileOperation cloneSlot(EntityPlayer player, int sourceSlotId, int destinationSlotId, boolean temporary) {
Profile profile = getProfile(player);
if(profile == null) return ProfileOperation.PLAYER_NOT_FOUND;
if(profile == null)
return ProfileOperation.error(MSG_PLAYER_NOT_FOUND);
ProfileOperation result = cloneSlotInternal(profile, sourceSlotId, destinationSlotId, temporary);
if(result == ProfileOperation.SUCCESS && player != null)
if(result.getResult() == EnumProfileOperation.SUCCESS && player != null) {
save(player, profile);
}
return result;
}
public static ProfileOperation cloneSlot(UUID uuid, int sourceSlotId, int destinationSlotId, boolean temporary) {
Profile profile = getProfile(uuid);
if(profile == null) return ProfileOperation.PLAYER_NOT_FOUND;
if(profile == null)
return ProfileOperation.error(MSG_PLAYER_NOT_FOUND);
ProfileOperation result = cloneSlotInternal(profile, sourceSlotId, destinationSlotId, temporary);
if(result == ProfileOperation.SUCCESS) {
if(result.getResult() == EnumProfileOperation.SUCCESS) {
if(profile.player != null)
save(profile.player, profile);
else
@@ -459,9 +498,10 @@ public class ProfileController {
public static ProfileOperation cloneSlot(String username, int sourceSlotId, int destinationSlotId, boolean temporary) {
Profile profile = getProfile(username);
if(profile == null) return ProfileOperation.PLAYER_NOT_FOUND;
if(profile == null)
return ProfileOperation.error(MSG_PLAYER_NOT_FOUND);
ProfileOperation result = cloneSlotInternal(profile, sourceSlotId, destinationSlotId, temporary);
if(result == ProfileOperation.SUCCESS) {
if(result.getResult() == EnumProfileOperation.SUCCESS) {
if(profile.player != null)
save(profile.player, profile);
else {
@@ -476,18 +516,21 @@ public class ProfileController {
// ----- Remove Slot -----
public static ProfileOperation removeSlot(EntityPlayer player, int slotId) {
Profile profile = getProfile(player);
if(profile == null) return ProfileOperation.PLAYER_NOT_FOUND;
if(profile == null)
return ProfileOperation.error(MSG_PLAYER_NOT_FOUND);
ProfileOperation result = removeSlotInternal(profile, slotId);
if(result == ProfileOperation.SUCCESS && player != null)
if(result.getResult() == EnumProfileOperation.SUCCESS && player != null) {
save(player, profile);
}
return result;
}
public static ProfileOperation removeSlot(UUID uuid, int slotId) {
Profile profile = getProfile(uuid);
if(profile == null) return ProfileOperation.PLAYER_NOT_FOUND;
if(profile == null)
return ProfileOperation.error(MSG_PLAYER_NOT_FOUND);
ProfileOperation result = removeSlotInternal(profile, slotId);
if(result == ProfileOperation.SUCCESS) {
if(result.getResult() == EnumProfileOperation.SUCCESS) {
if(profile.player != null)
save(profile.player, profile);
else
@@ -498,9 +541,10 @@ public class ProfileController {
public static ProfileOperation removeSlot(String username, int slotId) {
Profile profile = getProfile(username);
if(profile == null) return ProfileOperation.PLAYER_NOT_FOUND;
if(profile == null)
return ProfileOperation.error(MSG_PLAYER_NOT_FOUND);
ProfileOperation result = removeSlotInternal(profile, slotId);
if(result == ProfileOperation.SUCCESS) {
if(result.getResult() == EnumProfileOperation.SUCCESS) {
if(profile.player != null)
save(profile.player, profile);
else {
@@ -515,18 +559,20 @@ public class ProfileController {
// ----- Change Slot -----
public static ProfileOperation changeSlot(EntityPlayer player, int newSlotId) {
Profile profile = getProfile(player);
if(profile == null) return ProfileOperation.PLAYER_NOT_FOUND;
if(profile == null)
return ProfileOperation.error(MSG_PLAYER_NOT_FOUND);
ProfileOperation result = changeSlotInternal(profile, newSlotId);
if((result == ProfileOperation.SUCCESS || result == ProfileOperation.NEW_SLOT_CREATED) && player != null)
if(result.getResult() == EnumProfileOperation.SUCCESS && player != null)
save(player, profile);
return result;
}
public static ProfileOperation changeSlot(UUID uuid, int newSlotId) {
Profile profile = getProfile(uuid);
if(profile == null) return ProfileOperation.PLAYER_NOT_FOUND;
if(profile == null)
return ProfileOperation.error(MSG_PLAYER_NOT_FOUND);
ProfileOperation result = changeSlotInternal(profile, newSlotId);
if(result == ProfileOperation.SUCCESS || result == ProfileOperation.NEW_SLOT_CREATED) {
if(result.getResult() == EnumProfileOperation.SUCCESS) {
if(profile.player != null)
save(profile.player, profile);
else
@@ -537,9 +583,10 @@ public class ProfileController {
public static ProfileOperation changeSlot(String username, int newSlotId) {
Profile profile = getProfile(username);
if(profile == null) return ProfileOperation.PLAYER_NOT_FOUND;
if(profile == null)
return ProfileOperation.error(MSG_PLAYER_NOT_FOUND);
ProfileOperation result = changeSlotInternal(profile, newSlotId);
if(result == ProfileOperation.SUCCESS || result == ProfileOperation.NEW_SLOT_CREATED) {
if(result.getResult() == EnumProfileOperation.SUCCESS) {
if(profile.player != null)
save(profile.player, profile);
else {
@@ -557,7 +604,6 @@ public class ProfileController {
return;
Profile profile = activeProfiles.get(player.getUniqueID());
if(profile.locked) {
LogWriter.error("Profile is locked; cannot save slot data.");
return;
}
NBTTagCompound dataCompound;
@@ -634,7 +680,6 @@ public class ProfileController {
return infoList;
}
public static boolean allowSlotPermission(EntityPlayer player) {
Profile profile = ProfileController.getProfile(player);
int currentSlots = profile.slots.size();
@@ -659,28 +704,6 @@ public class ProfileController {
return currentSlots < highestAllowed;
}
public void sendChatAlert(ProfileOperation operation, EntityPlayerMP player){
switch (operation){
case SUCCESS:
ChatAlertPacket.sendChatAlert(player, "quest.newquest", ": ", "");
break;
case ERROR:
break;
case LOCKED:
break;
case PLAYER_NOT_FOUND:
break;
case NEW_SLOT_CREATED:
break;
case ALREADY_ACTIVE:
break;
case VERIFICATION_FAILED:
break;
case MAX_SLOTS:
break;
}
}
public static EntityPlayerMP getPlayer(String username){
return MinecraftServer.getServer().getConfigurationManager().func_152612_a(username);
}
@@ -4,6 +4,7 @@ import kamkeel.npcs.controllers.SyncController;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.nbt.NBTTagCompound;
import noppes.npcs.controllers.PartyController;
import noppes.npcs.controllers.data.PlayerData;
import java.util.ArrayList;
import java.util.List;
@@ -48,8 +49,12 @@ public class CNPCData implements IProfileData {
}
@Override
public boolean verifySwitch(EntityPlayer player) {
return true;
public ProfileOperation verifySwitch(EntityPlayer player) {
PlayerData playerData = PlayerData.get(player);
if(playerData.partyUUID != null)
return ProfileOperation.error("Cannot switch while in Party");
return ProfileOperation.success("");
}
@Override
@@ -0,0 +1,7 @@
package kamkeel.npcs.controllers.data;
public enum EnumProfileOperation {
SUCCESS,
ERROR,
LOCKED
}
@@ -38,7 +38,7 @@ public interface IProfileData {
* @param player The player switching profiles.
* @return true if allowed, false if not.
*/
public boolean verifySwitch(EntityPlayer player);
public ProfileOperation verifySwitch(EntityPlayer player);
/**
* NEW: Builds and returns a list of informational strings about this profile data,
@@ -1,13 +1,31 @@
package kamkeel.npcs.controllers.data;
public enum ProfileOperation {
SUCCESS,
ERROR,
LOCKED,
PLAYER_NOT_FOUND,
NEW_SLOT_CREATED,
ALREADY_ACTIVE,
VERIFICATION_FAILED,
MAX_SLOTS
}
public class ProfileOperation {
private final EnumProfileOperation result;
private final String message;
public ProfileOperation(EnumProfileOperation result, String message) {
this.result = result;
this.message = message;
}
public EnumProfileOperation getResult() {
return result;
}
public String getMessage() {
return message;
}
public static ProfileOperation success(String message) {
return new ProfileOperation(EnumProfileOperation.SUCCESS, message);
}
public static ProfileOperation error(String message) {
return new ProfileOperation(EnumProfileOperation.ERROR, message);
}
public static ProfileOperation locked(String message) {
return new ProfileOperation(EnumProfileOperation.LOCKED, message);
}
}
@@ -94,6 +94,7 @@ public class CustomNpcsPermissions{
public static final Permission PROFILE_DELETE = new Permission("customnpcs.profile.delete");
public static final Permission PROFILE_RENAME = new Permission("customnpcs.profile.rename");
public static final Permission PROFILE_CHANGE = new Permission("customnpcs.profile.change");
public static final Permission PROFILE_REGION_BYPASS = new Permission("customnpcs.profile.region-bypass");
public static final Permission PROFILE_MAX = new Permission("customnpcs.profile.max.*");
public static CustomNpcsPermissions Instance;
@@ -8,6 +8,8 @@ import noppes.npcs.config.legacy.LegacyConfig;
import org.apache.logging.log4j.Level;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class ConfigMain
{
@@ -87,6 +89,11 @@ public class ConfigMain
public static boolean AllowProfileBackups = true;
public static int ProfileBackupAmount = 5;
public static Property RegionProfileSwitchingProperty;
public static boolean RegionProfileSwitching = false;
public static Property RestrictedProfileRegionsProperty;
public static List<List<Integer>> RestrictedProfileRegions = new ArrayList<>();
/**
* General NPC Properties
@@ -230,6 +237,24 @@ public class ConfigMain
AllowProfileBackups = config.get(PROFILES, "Enable Profile Backups", true, "Will create backups of profile changes").getBoolean(true);
ProfileBackupAmount = config.get(PROFILES, "Number of Backups", 5, "How many backups per player to save").getInt(5);
RegionProfileSwitchingProperty = config.get(PROFILES, "Region Profile Switching", false, "If true, only allows profile switching in certain regions.");
RegionProfileSwitching = RegionProfileSwitchingProperty.getBoolean(false);
RestrictedProfileRegionsProperty = config.get(PROFILES, "Restricted Profile Regions", new String[]{
"0, 100, 64, 100, 200, 80, 200",
"1, 50, 60, 50, 150, 75, 150"
}, "List of restricted regions where profile switching is enabled. Format: DIM, X1, Y1, Z1, X2, Y2, Z2");
RestrictedProfileRegions.clear();
for (String region : RestrictedProfileRegionsProperty.getStringList()) {
String[] parts = region.split(", ");
List<Integer> regionList = new ArrayList<>();
for (String part : parts) {
regionList.add(Integer.parseInt(part));
}
RestrictedProfileRegions.add(regionList);
}
// Convert to Legacy
if(CustomNpcs.legacyExist){
EnableUpdateChecker = LegacyConfig.EnableUpdateChecker;